aboutsummaryrefslogtreecommitdiffstats
path: root/hicn-light/src/socket
diff options
context:
space:
mode:
Diffstat (limited to 'hicn-light/src/socket')
-rwxr-xr-xhicn-light/src/socket/CMakeLists.txt31
-rwxr-xr-xhicn-light/src/socket/api.c604
-rwxr-xr-xhicn-light/src/socket/api.h216
-rwxr-xr-xhicn-light/src/socket/error.c7
-rwxr-xr-xhicn-light/src/socket/error.h46
-rwxr-xr-xhicn-light/src/socket/ops.h54
-rwxr-xr-xhicn-light/src/socket/ops_linux.c1723
7 files changed, 2681 insertions, 0 deletions
diff --git a/hicn-light/src/socket/CMakeLists.txt b/hicn-light/src/socket/CMakeLists.txt
new file mode 100755
index 000000000..6ea94dcfa
--- /dev/null
+++ b/hicn-light/src/socket/CMakeLists.txt
@@ -0,0 +1,31 @@
+# Copyright (c) 2017-2019 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
+
+if (UNIX AND NOT APPLE AND NOT ANDROID_API)
+ list(APPEND HEADER_FILES
+ socket/api.h
+ socket/error.h
+ socket/ops.h
+ )
+
+ list(APPEND SOURCE_FILES
+ socket/api.c
+ socket/error.c
+ socket/ops_linux.c
+ )
+endif()
+
+set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE)
+set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) \ No newline at end of file
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;
+}
diff --git a/hicn-light/src/socket/api.h b/hicn-light/src/socket/api.h
new file mode 100755
index 000000000..e1516ebe1
--- /dev/null
+++ b/hicn-light/src/socket/api.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file hicn_face.h
+ * @brief hICN socket library
+ *
+ * This module provides an interface to managing so-called hICN sockets,
+ * realizing punting of interest and data packets using a TUN device.
+ */
+
+#ifndef HICN_SOCKET_API_H
+#define HICN_SOCKET_API_H
+
+#include <stdint.h> // uint*_t
+#include <stdlib.h>
+
+#include <hicn/hicn.h>
+#include "error.h"
+
+#define BUFSIZE 4096
+#define MAX_CONNECTIONS \
+ 255 // We currently limit the number of connections we can establish
+#define IF_NAMESIZE 16
+
+/* hICN socket helper */
+
+/** hICN configuration options */
+typedef struct {
+ // uint32_t interval;
+
+ /* Identifier used to name hICN TUN interfaces (should be unique) */
+ char *identifier;
+ // hicn_format_t format;
+
+} hicn_conf_t;
+
+/**
+ * hICN adjacency
+ */
+typedef struct {
+ char *local_ip_address;
+ char *gateway_ip_address;
+} hicn_adjacency_t;
+
+#define EMPTY_HICN_ADJACENCY \
+ (hicn_adjacency_t) { 0, 0 }
+
+/* hICN socket operations */
+
+typedef struct {
+ uint8_t pkbuf[BUFSIZE];
+ uint32_t rb_pkbuf_r;
+ uint32_t rb_pkbuf_w;
+} hicn_buffer_t;
+
+typedef enum { HS_UNSPEC, HS_LISTENER, HS_CONNECTION } hicn_socket_type_t;
+
+typedef struct hicn_socket_s {
+ hicn_socket_type_t type;
+ int fd;
+
+ /* Implementation specific state follows */
+ char tun_name[IF_NAMESIZE];
+ uint32_t tun_id;
+
+ hicn_buffer_t buffer;
+ void (*cb)(struct hicn_socket_s *, void *, uint8_t *, size_t);
+ void *cb_data;
+
+ union {
+ struct {
+ ip_address_t tun_ip_address;
+ uint32_t interface_id;
+
+ /* ID of the corresponding table : avoid default values of 0, 32766 and
+ * 32767 */
+ uint8_t table_id;
+ } connection;
+ };
+} hicn_socket_t;
+
+/**
+ * hICN global state
+ */
+typedef struct {
+ /* Configuration data */
+ hicn_conf_t *conf;
+
+ // We need state associate to each FD, to know what type of socket it is and
+ // its state.
+ void *socket_root; /**< A tree of socket indexed by their fd */
+
+} hicn_socket_helper_t;
+
+/**
+ * Create an hICN instance.
+ *
+ * This is used to configure the state of an hICN router consistently between
+ * a listener and the different connections. It also regroups all the state
+ * related to hICN functionalities.
+ *
+ * @return A pointer to an hICN instance.
+ */
+hicn_socket_helper_t *hicn_create();
+
+void hicn_destroy();
+
+/**
+ * Retrieve hICN configuration.
+ *
+ * Gets the current configuration of an hICN instance for information purposes,
+ * or later update it.
+ *
+ * TODO
+ * - We might want to prevent configuration updates while the hICN instance is
+ * running. Define running...
+ *
+ * @param [in] hicn Pointer to hICN instance.
+ * @return Pointer to an hICN configuration data structure.
+ *
+ * @see hicn_set_conf
+ */
+hicn_conf_t *hicn_get_conf(hicn_socket_helper_t *hicn);
+
+/**
+ * Update hICN configuration.
+ *
+ * @param [in] hicn Pointer to an hICN instance.
+ * @param [in] hicn_conf Pointer to an hICN configuration data structure.
+ * @return 0 in case of success, -1 otherwise.
+ *
+ * @see hicn_get_conf
+ */
+int hicn_set_conf(hicn_socket_helper_t *hicn, hicn_conf_t *hicn_conf);
+
+/**
+ * Release hICN state.
+ *
+ * @param [in] hicn Pointer to an hICN instance.
+ */
+void hicn_free(hicn_socket_helper_t *hicn);
+
+// FIXME doc
+int hicn_get_local_address(const ip_address_t *remote_address,
+ ip_address_t *local_address);
+
+/* hICN socket */
+
+/**
+ * Create an hICN socket.
+ *
+ * An hICN socket abstracts the underlying implementation and allows hICN
+ * packets to be sent and received independently of the underlying
+ * implementation.
+ *
+ * It is possible to further specialize the socket in a listener socket, and a
+ * connection socket.
+ *
+ * @param [in] hicn Pointer to an hICN instance.
+ * @param [in] identifier Unique identifier for this socket, used to named the
+ * TUN device
+ * @param [in] local_ip_address IP address used locally by the socket (or NULL
+ * for letting the library decide automatically).
+ * @return File descriptor (>0) in case of success, -1 otherwise.
+ *
+ * @see hicn_listen
+ * @see hicn_bind
+ */
+int hicn_socket(hicn_socket_helper_t *hicn, const char *identifier,
+ const char *local_ip_address);
+
+/**
+ * Packet punting.
+ *
+ * Note that we cannot listen on a socket that is already bound.
+ *
+ * @param [in] hicn Pointer to an hICN instance.
+ * @param [in] fd File descriptor identifying the hICN socket.
+ * @param [in] prefix Prefix (IPv4 or IPv6) to be bound to hICN in
+ * RFC-compliant presentation format.
+ * @return 0 in case of success, -1 otherwise.
+ *
+ * @see hicn_socket
+ */
+int hicn_listen(hicn_socket_helper_t *hicn, int fd, const char *prefix);
+
+/**
+ * Packet forwarding
+ * @param [in] hicn Pointer to an hICN instance.
+ * @param [in] fd File descriptor identifying the hICN socket.
+ * @param [in] prefix Prefix (IPv4 or IPv6) to be bound to hICN in
+ * RFC-compliant presentation format.
+ * @return 0 in case of success, -1 otherwise.
+ *
+ * XXX adjacency does not perform any copy heresofar
+ *
+ * @see hicn_socket
+ */
+int hicn_bind(hicn_socket_helper_t *hicn, int fd,
+ const char *remote_ip_address);
+
+#endif /* HICN_SOCKET_API_H */
diff --git a/hicn-light/src/socket/error.c b/hicn-light/src/socket/error.c
new file mode 100755
index 000000000..3dafec8cf
--- /dev/null
+++ b/hicn-light/src/socket/error.c
@@ -0,0 +1,7 @@
+#include "error.h"
+
+const char* HICN_SOCKET_ERROR_STRING[] = {
+#define _(a, b, c) [b] = c,
+ foreach_hicn_socket_error
+#undef _
+};
diff --git a/hicn-light/src/socket/error.h b/hicn-light/src/socket/error.h
new file mode 100755
index 000000000..8195efd84
--- /dev/null
+++ b/hicn-light/src/socket/error.h
@@ -0,0 +1,46 @@
+#ifndef HICN_SOCKET_ERROR_H
+#define HICN_SOCKET_ERROR_H
+
+// FIXME remove unused errors
+#define foreach_hicn_socket_error \
+ _(NONE, 0, "OK") \
+ _(UNSPEC, 1, "unspecified error") \
+ _(NOT_HICN, 2, "not a hICN paclet") \
+ _(UNKNOWN_ADDRESS, 10, "unknown address") \
+ _(INVALID_PARAMETER, 20, "invalid parameter") \
+ _(INVALID_IP_ADDRESS, 21, "invalid IP address") \
+ _(CORRUPTED_PACKET, 22, "corrupted packet") \
+ _(UNEXPECTED, 98, "unexpected error") \
+ _(NOT_IMPLEMENTED, 99, "not implemented") \
+ _(SOCKET_LOCAL_NULL_ADDRESS, 101, "empty local address") \
+ _(SOCKET_LOCAL_REPR, 102, "cannot represent local address") \
+ _(SOCKET_LOCAL_HEURISTIC, 103, "error finding local address") \
+ _(SOCKET_LOCAL_SET_TUN_IP, 104, "cannot set local IP to TUN") \
+ _(BIND_SOCKET_NOT_FOUND, 301, "bind: socket not found") \
+ _(BIND_SOCKET_ALREADY_BOUND, 302, "bind: socket already bound") \
+ _(BIND_REMOTE_INTERFACE, 303, "bind: no interface towards gateway") \
+ _(BIND_REMOTE_NETMASK, 304, "bind: no local IP with netmask < 128") \
+ _(BIND_REMOTE_REPR, 305, "bind: error representing local IP") \
+ _(BIND_REMOTE_LOCAL_NULL_ADDR, 306, "bind: could not set local endpoint") \
+ _(BIND_REMOTE_LOCAL_REPR, 307, "bind: error representing remote IP") \
+ _(BIND_REMOTE_LOCAL_HEURISTIC, 308, "bind: could not apply heuristic") \
+ _(BIND_REMOTE_LOCAL_SET_TUN_IP, 309, "bind: error setting local IP to TUN") \
+ _(BIND_NDP, 310, "bind: could not enable NDP proxy") \
+ _(BIND_NEIGH_PROXY, 311, "bind: could not neighbour") \
+ _(BIND_REPR, 312, "bind: error represeting IP") \
+ _(BIND_LO, 313, "bind: could not remove local route") \
+ _(BIND_RULE, 314, "bind: could not add rule") \
+ _(BIND_ROUTE, 315, "bind: could not add output route")
+
+typedef enum {
+#define _(a, b, c) HICN_SOCKET_ERROR_##a = (-b),
+ foreach_hicn_socket_error
+#undef _
+ HICN_SOCKET_N_ERROR,
+} hicn_socket_error_t;
+
+extern const char *HICN_SOCKET_ERROR_STRING[];
+
+#define hicn_socket_strerror(errno) (char *)(HICN_SOCKET_ERROR_STRING[-errno])
+
+#endif /* HICN_SOCKET_ERROR_H */
diff --git a/hicn-light/src/socket/ops.h b/hicn-light/src/socket/ops.h
new file mode 100755
index 000000000..249caf87a
--- /dev/null
+++ b/hicn-light/src/socket/ops.h
@@ -0,0 +1,54 @@
+#ifndef HICN_SOCKET_OPS_H
+#define HICN_SOCKET_OPS_H
+
+#include <hicn/hicn.h>
+#include <stdint.h>
+
+typedef struct {
+ char *arch;
+ int (*tun_create)(char *name);
+ int (*get_tun_name)(const char *prefix, const char *identifier,
+ char *tun_name);
+ int (*enable_v6_forwarding)(char *interface_name);
+ int (*enable_v4_forwarding)();
+ int (*enable_ndp_proxy)();
+
+ uint32_t (*get_ifid)(const char *ifname);
+ int (*get_output_ifid)(const char *ip_address, uint8_t address_family,
+ uint32_t *interface_id);
+ int (*get_ip_addr)(uint32_t interface_id, uint8_t address_family,
+ ip_address_t *ip_address);
+ int (*set_ip_addr)(uint32_t interface_id, ip_address_t *ip_address);
+ int (*up_if)(uint32_t interface_id);
+ int (*add_in_route_table)(const ip_address_t *prefix,
+ const uint32_t interface_id,
+ const uint8_t table_id);
+ int (*add_in_route_table_s)(const char *prefix, const uint32_t interface_id,
+ const uint8_t table_id);
+ int (*add_in_route_s)(const char *prefix, const uint32_t interface_id);
+ int (*add_out_route)(const char *gateway, const uint8_t address_family,
+ const uint8_t table_id, int default_route);
+ int (*del_out_route)(const char *gateway, const uint8_t address_family,
+ const uint8_t table_id);
+ int (*del_lo_route)(const ip_address_t *ip_address);
+ int (*add_rule)(const char *interface_name, const uint8_t address_family,
+ const uint8_t table_id);
+ int (*del_rule)(const char *interface_name, const uint8_t address_family,
+ const uint8_t table_id);
+ int (*add_neigh_proxy)(const ip_address_t *ip_address,
+ const uint32_t interface_id);
+ int (*add_prio_rule)(const ip_address_t *ip_address,
+ const uint8_t address_family, const uint32_t priority,
+ const uint8_t table_id);
+ int (*add_lo_prio_rule)(const ip_address_t *ip_address,
+ const uint8_t address_family,
+ const uint32_t priority);
+ int (*del_prio_rule)(const ip_address_t *ip_address,
+ const uint8_t address_family, const uint32_t priority,
+ const uint8_t table_id);
+ int (*del_lo_prio_rule)(const ip_address_t *ip_address,
+ const uint8_t address_family,
+ const uint32_t priority);
+} hicn_socket_ops_t;
+
+#endif /* HICN_SOCKET_OPS_H */
diff --git a/hicn-light/src/socket/ops_linux.c b/hicn-light/src/socket/ops_linux.c
new file mode 100755
index 000000000..d085f0d3d
--- /dev/null
+++ b/hicn-light/src/socket/ops_linux.c
@@ -0,0 +1,1723 @@
+#include <sys/ioctl.h> // ioctl
+#include <sys/socket.h> // needed by linux/if.h
+//#include <linux/if.h>
+#include <errno.h>
+#include <fcntl.h> // ''
+#include <linux/if_tun.h>
+#include <linux/limits.h> // PATH_MAX
+#include <stdio.h> // fprintf
+#include <string.h> // memset
+#include <sys/stat.h> // open
+#include <sys/uio.h> // writev
+#include <unistd.h> // close
+
+#include "error.h"
+#include "ops.h"
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
+
+/******************************************************************************
+ * netlink.h
+ ******************************************************************************/
+
+#ifndef HICN_NETLINK_H
+#define HICN_NETLINK_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+// DEPRECATED|/* Socket */
+// DEPRECATED|int _nl_get_socket();
+// DEPRECATED|int _nl_send(int s, uint8_t * buffer, size_t len);
+// DEPRECATED|size_t _nl_receive(uint8_t * buffer, size_t len);
+// DEPRECATED|
+// DEPRECATED|/* Netlink packet format */
+// DEPRECATED|int _nl_header(int request, uint8_t * buffer, size_t len, uint32_t
+// flags); DEPRECATED|int _nl_payload_rule(uint8_t table_id, uint8_t * buffer,
+// size_t len); DEPRECATED|int _nl_payload_link(uint32_t ifindex, uint8_t *
+// buffer, size_t len); DEPRECATED|int _nl_payload_route(uint8_t table_id,
+// uint8_t dst_len, uint8_t * buffer, size_t len); DEPRECATED| DEPRECATED|int
+// _nl_parse(uint8_t * buffer, size_t len); DEPRECATED|int _nl_parse_ret(uint8_t
+// * buffer, size_t len); DEPRECATED|int _nl_parse_link_ifid(uint8_t * buffer,
+// size_t len, uint32_t * interface_id); DEPRECATED|int
+// _nl_parse_link_ip_addr(uint8_t * buffer, size_t len, struct in6_addr * addr);
+
+/* Public interface */
+
+/**
+ * Get the interface ID of an interface by its name
+ *
+ * @return 32-bit interface identifier in case of success, or 0.
+ *
+ * @see if_nametoindex
+ *
+ */
+uint32_t _nl_get_ifid(const char *ifname);
+
+/**
+ * Retrieve the output interface corresponding to the specified IP address.
+ *
+ * @param [in] addr IP(v6) address in presentation form.
+ * @param [out] Identifier of the corresponding output interface.
+ * @return int 0 in case of success, -1 otherwise
+ */
+int _nl_get_output_ifid(const char *ip_address, uint8_t address_family,
+ uint32_t *interface_id);
+
+/**
+ * Retrieve the first IP address of an interface (identified by its id) which
+ * has a netmask < 128.
+ *
+ * @param [in] s File descriptor of the netlink socket (deprecated).
+ * @param [in] interface_id Identifier of the interface for which to retrieve
+ * the IP address.
+ * @param [out] addr IP(v6) address in binary form.
+ * @return int 0 in case of success, -1 otherwise
+ *
+ * @see getifaddrs
+ */
+int _nl_get_ip_addr(uint32_t interface_id, uint8_t address_family,
+ ip_address_t *ip_address);
+
+int _nl_set_ip_addr(uint32_t interface_id, ip_address_t *ip_address);
+
+int _nl_up_if(uint32_t interface_id);
+
+int _nl_add_in_route_table(const ip_address_t *prefix,
+ const uint32_t interface_id, const uint8_t table_id);
+int _nl_add_in_route_table_s(const char *prefix, const uint32_t interface_id,
+ const uint8_t table_id);
+int _nl_add_in_route_s(const char *prefix, const uint32_t interface_id);
+
+int _nl_add_out_route(const char *gateway, const uint8_t address_family,
+ const uint8_t table_idi, int default_route);
+int _nl_del_out_route(const char *gateway, const uint8_t address_family,
+ const uint8_t table_id);
+
+int _nl_del_lo_route(const ip_address_t *ip_address);
+
+int _nl_add_rule(const char *interface_name, const uint8_t address_family,
+ const uint8_t table_id);
+int _nl_del_rule(const char *interface_name, const uint8_t address_family,
+ const uint8_t table_id);
+
+int _nl_add_neigh_proxy(const ip_address_t *ip_address,
+ const uint32_t interface_id);
+
+int _nl_add_prio_rule(const ip_address_t *ip_address,
+ const uint8_t address_family, const uint32_t priority,
+ const uint8_t table_id);
+int _nl_add_lo_prio_rule(const ip_address_t *ip_address,
+ const uint8_t address_family, const uint32_t priority);
+int _nl_del_prio_rule(const ip_address_t *ip_address,
+ const uint8_t address_family, const uint32_t priority,
+ const uint8_t table_id);
+int _nl_del_lo_prio_rule(const ip_address_t *ip_address,
+ const uint8_t address_family, const uint32_t priority);
+
+#endif /* HICN_NETLINK_H */
+
+/******************************************************************************
+ * netlink.c
+ ******************************************************************************/
+
+/*
+ * This module offers an interface to the Netlink API appropriate for
+ * implementing punting as required by hICN (1).
+ *
+ * More specifically, it consists of the following functionalities:
+ * - LINK
+ . map interface name to ID
+ . set and interface up
+ * - ADDR
+ . get and set ip addresses on a given interface ID
+ * - ROUTE
+ . get output interface id towards IP (ip route get IP > interface_id)
+ . add input route (ip route add PREFIX dev INTERFACE) for punting
+ interests . add output route (ip route add default GATEWAY table TABLE) for
+ routing interests (2, 3) . delete local route towards IP (ip route del IP table
+ local) for ???
+ /!\ could this be avoided by removing the local attribute in the
+ netlink call ?
+ * - RULE
+ * . add output rule (ip rule add iif interface table TABLE) for routing
+ interests (2, 3)
+ * - ND PROXY
+ * . enable NDP proxy functionality for IP on interface ID (ip -6 neigh add
+ proxy IP dev INTERFACE)
+ * for allowing the TUN to be reachable on the reverse data path
+ *
+ * Implementation notes:
+ * (1) We have not been using the libnl library because it requires
+ * manipulating too many function and data structures for a simple purpose.
+ * Currently, many parts of the code are somehow repetitive, but this might
+ * be improved by a proper API in a future release.
+ * (2) allows load balancing over different interfaces = multihoming. Please
+ * note that it is not possible to have load balancing over two faces using
+ * the same output interface as we are using the underlying IP network !
+ * This might be mitigated with the use of SR however.
+ * (3) The implementation of punting heavily uses the policy routing
+ * functionalities, as we need to hook through a TUN into user space a
+ * whole prefix used as a destination (for interests) or source (for data
+ * packets). We thus combine the use of rules to assign routing table IDs,
+ * and routes inside those tables. As there is no easy way to allocate
+ * which routing tables we use, we made the choice to index them by the ID
+ * of the interface, assuming there is no external conflict. This might be
+ * improved in the future.
+ *
+ * This hICN implementation uses TUNs in two different ways:
+ * - a main TUN interface, which receives all punted interests,
+ * demultiplex them before assigning them an input face (eventually
+ * dynamically creating it);
+ * - a set of output TUN interfaces, aka faces, used for routing of
+ * interests, and for receiving the corresponding data packets on the way
+ * back. Punting of data packets if based of their destination IP, which
+ * is the IP of the physical output interface used for the interest, which
+ * is unique (cf (2)).
+ *
+ * The corresponding routing tables IDs are :
+ * MAIN_TUN_ID -> used for punting of data packets
+ * OUTPUT_TUN_ID_i -> used for routing of interests towards next hop
+ * (bypassing local IP routing table)
+ *
+ * Note that punting of interests is done just through a route, and routing
+ * of data packets is done just through the regular IP routing table on the
+ * note after the address translation done in the forwarder.
+ *
+ * - Forging netlink packets
+ *
+ * A previous implementation used function calls with pointers to populate
+ * the various header parts in a buffer in order to build a netlink packet.
+ * A newer implementation uses nested structs and iovecs to build the whole
+ * packet in a single write call. This should allow a simpler evolution
+ * towards a cleaner API.
+ */
+
+#include <arpa/inet.h> // inet_pton
+#include <errno.h> // errno
+#include <linux/fib_rules.h> // fib_rule_hdr, FRA_*
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h> // IFF_UP
+#include <netinet/in.h> // in6addr
+#include <stdio.h> // perror
+#include <string.h>
+#include <sys/socket.h> // ''
+#include <sys/types.h> // socket
+#include <unistd.h> // read
+
+#include <sys/socket.h> // ''
+#include <sys/types.h> // send, recv
+
+//#include "../../hicn.h"
+//#include "../../hicn_util.h" // ARRAY_SIZE, hicn_packet_dump_iov
+
+#define BUFSIZE 4096
+#define FLAGS_CREATE NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK
+// ??
+#define FLAGS_CREATE_MATCH \
+ NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | NLM_F_MATCH
+
+// XXX putting ACK poses a prolem for the value received by get_if_id.
+#define FLAGS_GET NLM_F_REQUEST
+#define FLAGS_GET_ROOT (NLM_F_REQUEST | NLM_F_ROOT)
+
+#define FLAGS_LIST NLM_F_REQUEST | NLM_F_DUMP
+
+#define IF_NAMESIZE 16
+#define FR_ACT_TO_TBL 1
+#define NLMSG_BOTTOM(nlmsg) \
+ ((struct rtattr *)(((void *)(nlmsg)) + NLMSG_ALIGN((nlmsg)->nlmsg_len)))
+
+int seq = 1;
+
+static inline size_t iov_length(const struct iovec *iov,
+ unsigned long nr_segs) {
+ unsigned long seg;
+ size_t ret = 0;
+
+ for (seg = 0; seg < nr_segs; seg++) ret += iov[seg].iov_len;
+ return ret;
+}
+
+typedef struct {
+ struct nlmsghdr hdr;
+ struct nlmsgerr payload;
+} nl_err_hdr_t;
+
+/* Low level : nl header */
+
+int _nl_get_socket() { return socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); }
+
+int _nl_header(int request, uint8_t *buffer, size_t len, uint32_t flags) {
+ struct nlmsghdr *nl = (struct nlmsghdr *)buffer;
+
+ nl->nlmsg_len = 0; // NLMSG_LENGTH(sizeof(struct ifinfomsg));
+ nl->nlmsg_type = request;
+ nl->nlmsg_flags = flags;
+ nl->nlmsg_seq = seq++; //
+ nl->nlmsg_pid = 0; // getpid();
+
+ return 0;
+}
+
+/* Low level : nl protocols */
+
+/* Low level : attributes */
+
+int addAttr(struct nlmsghdr *nl, int maxlen, int type, void *data,
+ int attr_len) {
+ struct rtattr *rta;
+ int len = RTA_LENGTH(attr_len);
+
+ if (NLMSG_ALIGN(nl->nlmsg_len) + len > maxlen) {
+ exit(EXIT_FAILURE);
+ }
+
+ rta = (struct rtattr *)((char *)nl + NLMSG_ALIGN(nl->nlmsg_len));
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), data, attr_len);
+ nl->nlmsg_len = NLMSG_ALIGN(nl->nlmsg_len) + len;
+ return 0;
+}
+
+int _nl_payload_rule(uint8_t table_id, uint8_t address_family, uint8_t *buffer,
+ size_t len) {
+ struct nlmsghdr *nl = (struct nlmsghdr *)buffer;
+ struct fib_rule_hdr *frh = (struct fib_rule_hdr *)(NLMSG_DATA(buffer));
+
+ memset(frh, 0, sizeof(struct fib_rule_hdr));
+ frh->family = address_family;
+ frh->table = table_id;
+ frh->action = FR_ACT_TO_TBL,
+ frh->flags = NLM_F_REPLACE; // 0
+ frh->tos = 0;
+
+ nl->nlmsg_len += NLMSG_LENGTH(sizeof(struct fib_rule_hdr));
+
+ return 0;
+}
+
+int _nl_payload_link(uint32_t ifindex, uint8_t *buffer, size_t len) {
+ struct nlmsghdr *nl = (struct nlmsghdr *)buffer;
+ struct ifinfomsg *ifi = (struct ifinfomsg *)(NLMSG_DATA(buffer));
+
+ memset(ifi, 0, sizeof(struct ifinfomsg));
+ ifi->ifi_family = AF_UNSPEC;
+ // ifi->ifi_type = 0;
+ ifi->ifi_index =
+ ifindex; // new interface, could be specified since linux 3.7
+ ifi->ifi_flags = 0;
+ // ifi->ifi_change = 0xffffffff;
+
+ nl->nlmsg_len += NLMSG_LENGTH(sizeof(struct ifinfomsg));
+
+ return 0;
+}
+
+int _nl_payload_addr(uint32_t ifindex, uint8_t *buffer, size_t len) {
+ struct nlmsghdr *nl = (struct nlmsghdr *)buffer;
+ struct ifaddrmsg *addr = (struct ifaddrmsg *)(NLMSG_DATA(buffer));
+
+ memset(addr, 0, sizeof(struct ifaddrmsg));
+ addr->ifa_family = AF_UNSPEC; // INET6;
+ /*
+ addr->ifa_prefixlen = 128;
+ addr->ifa_flags = 0;
+ addr->ifa_scope = RT_SCOPE_LINK; //IFA_ADDRESS;
+ addr->ifa_index = ifindex;
+ */
+
+ nl->nlmsg_len += NLMSG_LENGTH(sizeof(struct ifaddrmsg)) - 4;
+
+ return 0;
+}
+
+int _nl_payload_route(uint8_t table_id, uint8_t addr_family, uint8_t dst_len,
+ uint8_t *buffer, size_t len) {
+ struct nlmsghdr *nl = (struct nlmsghdr *)buffer;
+ struct rtmsg *raddr = (struct rtmsg *)(NLMSG_DATA(buffer));
+
+ raddr->rtm_family = addr_family;
+ raddr->rtm_dst_len = dst_len;
+ raddr->rtm_src_len = 0;
+ raddr->rtm_tos = 0;
+
+ raddr->rtm_table = table_id;
+ raddr->rtm_protocol = RTPROT_BOOT;
+ raddr->rtm_scope = RT_SCOPE_UNIVERSE;
+ raddr->rtm_type = RTN_UNICAST;
+
+ raddr->rtm_flags = 0;
+
+ nl->nlmsg_len += NLMSG_LENGTH(sizeof(struct rtmsg));
+
+ return 0;
+}
+
+uint32_t _nl_get_ifid(const char *interface_name) {
+ char buffer[BUFSIZE];
+ struct nlmsghdr *hdr = (struct nlmsghdr *)buffer;
+ size_t n;
+ int fd;
+ size_t len = interface_name ? strlen(interface_name) + 1 : 0;
+ uint8_t padding[RTA_ALIGNTO] = {0, 0, 0, 0};
+
+ if (len == 0) {
+ goto ERR_IF;
+ }
+
+ struct {
+ struct nlmsghdr hdr;
+ struct ifinfomsg payload;
+ } msg = {//.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+ .hdr.nlmsg_type = RTM_GETLINK,
+ .hdr.nlmsg_flags = FLAGS_GET,
+ .payload.ifi_family = AF_UNSPEC,
+ .payload.ifi_index = 0};
+ struct rtattr a_ifname = {RTA_LENGTH(strlen(interface_name) + 1),
+ IFLA_IFNAME};
+
+ struct iovec iov[] = {{&msg, sizeof(msg)},
+ {&a_ifname, sizeof(a_ifname)},
+ {(char *)interface_name, len},
+ {padding, RTA_SPACE(len) - RTA_LENGTH(len)}};
+ msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov));
+
+ fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (fd < 0) {
+ goto ERR_SOCKET;
+ }
+ n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov));
+ if (n == -1) {
+ goto ERR_SEND;
+ }
+ n = recv(fd, buffer, BUFSIZE, 0);
+ if (n == -1) {
+ goto ERR_RECV;
+ }
+
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr);
+ if (err->error < 0) {
+ errno = -err->error;
+ goto ERR_NL;
+ }
+ return 0; /* Unexpected */
+ }
+
+ for (; NLMSG_OK(hdr, n); hdr = NLMSG_NEXT(hdr, n)) {
+ struct ifinfomsg *payload = (struct ifinfomsg *)NLMSG_DATA(hdr);
+ return payload->ifi_index;
+ }
+ return 0;
+
+ERR_NL:
+ERR_RECV:
+ERR_SEND:
+ERR_SOCKET:
+ERR_IF:
+ return 0;
+}
+
+int _nl_get_output_ifid(const char *ip_address, uint8_t family_address,
+ uint32_t *interface_id) {
+ int rc;
+
+ char buffer[BUFSIZE];
+ struct nlmsghdr *hdr = (struct nlmsghdr *)buffer;
+ size_t n;
+ int fd;
+
+ fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (fd < 0) {
+ goto ERR;
+ }
+
+ if (family_address == AF_INET6) {
+ struct in6_addr addr; // V6SPECIFIC
+
+ struct {
+ struct nlmsghdr hdr;
+ struct rtmsg payload;
+ } msg = {
+ .hdr.nlmsg_type = RTM_GETROUTE,
+ .hdr.nlmsg_flags = NLM_F_REQUEST,
+ .hdr.nlmsg_seq = seq++,
+ .payload.rtm_family = AF_INET6,
+ .payload.rtm_dst_len = IPV6_ADDR_LEN_BITS,
+ .payload.rtm_src_len = 0,
+ .payload.rtm_tos = 0,
+ .payload.rtm_table = RT_TABLE_UNSPEC,
+ .payload.rtm_protocol = RTPROT_UNSPEC,
+ .payload.rtm_scope = RT_SCOPE_UNIVERSE,
+ .payload.rtm_type = RTN_UNSPEC,
+ .payload.rtm_flags = 0 // RTM_F_NOTIFY in 'ip route get'
+ };
+
+ /* Convert the IP address to binary form */
+ rc = inet_pton(AF_INET6, ip_address, &addr);
+ if (rc <= 0) {
+ goto ERR;
+ }
+
+ /* Set attribute = length/type/value */
+ struct rtattr a_dst = {RTA_LENGTH(16), RTA_DST};
+ struct iovec iov[] = {
+ {&msg, sizeof(msg)},
+ {&a_dst, sizeof(a_dst)}, // attribute
+ {&addr, sizeof(addr)} // value
+ };
+ msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov));
+
+ n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov));
+ if (n == -1) {
+ goto ERR;
+ }
+ } else if (family_address == AF_INET) {
+ struct in_addr addr;
+
+ struct {
+ struct nlmsghdr hdr;
+ struct rtmsg payload;
+ } msg = {
+ .hdr.nlmsg_type = RTM_GETROUTE,
+ .hdr.nlmsg_flags = NLM_F_REQUEST,
+ .hdr.nlmsg_seq = seq++,
+ .payload.rtm_family = AF_INET,
+ .payload.rtm_dst_len = IPV4_ADDR_LEN_BITS,
+ .payload.rtm_src_len = 0,
+ .payload.rtm_tos = 0,
+ .payload.rtm_table = RT_TABLE_UNSPEC,
+ .payload.rtm_protocol = RTPROT_UNSPEC,
+ .payload.rtm_scope = RT_SCOPE_UNIVERSE,
+ .payload.rtm_type = RTN_UNSPEC,
+ .payload.rtm_flags = 0 // RTM_F_NOTIFY in 'ip route get'
+ };
+
+ /* Convert the IP address to binary form */
+ rc = inet_pton(AF_INET, ip_address, &addr);
+ if (rc <= 0) {
+ goto ERR;
+ }
+
+ /* Set attribute = length/type/value */
+ struct rtattr a_dst = {RTA_LENGTH(4), RTA_DST};
+ struct iovec iov[] = {
+ {&msg, sizeof(msg)},
+ {&a_dst, sizeof(a_dst)}, // attribute
+ {&addr, sizeof(addr)} // value
+ };
+ msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov));
+
+ n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov));
+ if (n == -1) {
+ goto ERR;
+ }
+ } else {
+ goto ERR;
+ }
+
+ n = recv(fd, buffer, BUFSIZE, 0);
+ if (n == -1) {
+ goto ERR;
+ }
+
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr);
+ if (err->error < 0) {
+ errno = -err->error;
+ goto ERR;
+ }
+ return HICN_SOCKET_ERROR_UNEXPECTED; /* Unexpected */
+ }
+
+ for (; NLMSG_OK(hdr, n); hdr = NLMSG_NEXT(hdr, n)) {
+ struct rtmsg *rtm = (struct rtmsg *)NLMSG_DATA(hdr);
+ int attrlen = RTM_PAYLOAD(hdr);
+ struct rtattr *rta;
+ for (rta = RTM_RTA(rtm); RTA_OK(rta, attrlen);
+ rta = RTA_NEXT(rta, attrlen)) {
+ if (rta->rta_type == RTA_OIF) {
+ *interface_id = *(uint32_t *)RTA_DATA(rta);
+ return HICN_SOCKET_ERROR_NONE;
+ }
+ }
+ }
+
+ return HICN_SOCKET_ERROR_NONE;
+
+ERR:
+ return HICN_SOCKET_ERROR_UNSPEC;
+}
+
+int _nl_get_ip_addr(uint32_t interface_id, uint8_t address_family,
+ ip_address_t *ip_address) {
+ char buffer[BUFSIZE];
+ struct nlmsghdr *hdr = (struct nlmsghdr *)buffer;
+ size_t n;
+ int fd;
+
+ struct {
+ struct nlmsghdr hdr;
+ struct ifaddrmsg payload;
+ } msg = {.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
+ .hdr.nlmsg_type = RTM_GETADDR,
+ .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT, // | NLM_F_MATCH,
+ .payload.ifa_family = address_family,
+ .payload.ifa_index = 0};
+
+ fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (fd < 0) {
+ goto ERR_SOCKET;
+ }
+
+ n = send(fd, &msg, sizeof(msg), 0);
+ if (n == -1) {
+ goto ERR_SEND;
+ }
+ n = recv(fd, buffer, BUFSIZE, 0);
+ if (n == -1) {
+ goto ERR_RECV;
+ }
+
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr);
+ if (err->error < 0) {
+ errno = -err->error;
+ goto ERR_NL;
+ }
+ return -99; /* Unexpected */
+ }
+
+ for (; NLMSG_OK(hdr, n); hdr = NLMSG_NEXT(hdr, n)) {
+ struct ifaddrmsg *payload = (struct ifaddrmsg *)NLMSG_DATA(hdr);
+
+ if (address_family == AF_INET6) {
+ if ((payload->ifa_index == interface_id) &&
+ (payload->ifa_prefixlen < IPV6_ADDR_LEN * 8)) {
+ printf("got ip address\n");
+ memcpy(ip_address->buffer, RTA_DATA(payload + 1), IPV6_ADDR_LEN);
+ ip_address->family = AF_INET6;
+ ip_address->prefix_len = IPV6_ADDR_LEN_BITS;
+ printf("returning %d\n", HICN_SOCKET_ERROR_NONE);
+ return HICN_SOCKET_ERROR_NONE;
+ }
+ } else if (address_family == AF_INET) {
+ if ((payload->ifa_index == interface_id) &&
+ (payload->ifa_prefixlen < IPV4_ADDR_LEN * 8)) {
+ printf("got ip address\n");
+ memcpy(ip_address->buffer, RTA_DATA(payload + 1), IPV4_ADDR_LEN);
+ ip_address->family = AF_INET;
+ ip_address->prefix_len = IPV4_ADDR_LEN_BITS;
+ printf("returning %d\n", HICN_SOCKET_ERROR_NONE);
+ return HICN_SOCKET_ERROR_NONE;
+ }
+ } else {
+ return -99;
+ }
+ }
+
+ERR_NL:
+ERR_RECV:
+ERR_SEND:
+ERR_SOCKET:
+ printf("error getting ip address\n");
+ return HICN_SOCKET_ERROR_UNSPEC;
+}
+
+int _nl_set_ip_addr(uint32_t interface_id, ip_address_t *ip_address) {
+ char buffer[BUFSIZE];
+ struct nlmsghdr *hdr = (struct nlmsghdr *)buffer;
+ size_t n;
+ int fd;
+
+ struct {
+ struct nlmsghdr hdr;
+ struct ifaddrmsg payload;
+ } msg = {
+ .hdr.nlmsg_type = RTM_NEWADDR,
+ .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_MATCH | NLM_F_ATOMIC,
+ .hdr.nlmsg_seq = seq++,
+ .payload.ifa_family = ip_address->family,
+ .payload.ifa_prefixlen = ip_address->prefix_len,
+ .payload.ifa_flags = 0,
+ .payload.ifa_scope = RT_SCOPE_UNIVERSE,
+ .payload.ifa_index = interface_id};
+
+ /* Set attributes = length/type/value */
+ struct rtattr ifa_address = {RTA_LENGTH(ip_address_len(ip_address)),
+ IFA_ADDRESS};
+ // XXX maybe the reason why we have a local route ?
+ // struct rtattr ifa_local = { RTA_LENGTH(ip_address_len(ip_address)),
+ // IFA_LOCAL };
+ struct iovec iov[] = {
+ {&msg, sizeof(msg)},
+ {&ifa_address, sizeof(ifa_address)},
+ {(void *)&ip_address->buffer, sizeof(ip_address->buffer)},
+ // { &ifa_local, sizeof(ifa_local) },
+ // { (void*)&ip_address->buffer, sizeof(ip_address->buffer) },
+ };
+ msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov));
+
+ fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (fd < 0) {
+ goto ERR_SOCKET;
+ }
+
+ // hicn_packet_dump_iov(iov, ARRAY_SIZE(iov));
+
+ n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov));
+ if (n == -1) {
+ goto ERR_SEND;
+ }
+ n = recv(fd, buffer, BUFSIZE, 0);
+ if (n == -1) {
+ goto ERR_RECV;
+ }
+
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr);
+ if (err->error < 0) {
+ errno = -err->error;
+ goto ERR_NL;
+ }
+ }
+
+ return 0;
+
+ERR_NL:
+ERR_RECV:
+ERR_SEND:
+ERR_SOCKET:
+ return -1;
+}
+
+int _nl_up_if(uint32_t interface_id) {
+ char buffer[BUFSIZE];
+ struct nlmsghdr *hdr = (struct nlmsghdr *)buffer;
+ size_t n;
+ int fd;
+
+ fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (fd < 0) {
+ goto ERR_SOCKET;
+ }
+
+ struct {
+ struct nlmsghdr hdr;
+ struct ifinfomsg payload;
+ } msg = {
+ .hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+ .hdr.nlmsg_type = RTM_NEWLINK,
+ .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
+ .payload.ifi_family = AF_UNSPEC,
+ .payload.ifi_index = interface_id,
+ .payload.ifi_flags = IFF_UP,
+ .payload.ifi_change = IFF_UP // 0xffffffff
+ };
+
+ n = send(fd, &msg, sizeof(msg), 0);
+ if (n == -1) {
+ goto ERR_SEND;
+ }
+ n = recv(fd, buffer, BUFSIZE, 0);
+ if (n == -1) {
+ goto ERR_RECV;
+ }
+
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr);
+ if (err->error < 0) {
+ errno = -err->error;
+ goto ERR_NL;
+ }
+ return 0;
+ }
+
+ERR_NL:
+ERR_RECV:
+ERR_SEND:
+ERR_SOCKET:
+ return -1;
+}
+
+struct route_info {
+ char *dst_addr;
+ char *src_addr;
+ char *gateway;
+ char ifName[IF_NAMESIZE];
+};
+
+/*
+ * ip -6 route add PREFIX dev INTERFACE_NAME
+ */
+#if 0
+int _nl_add_in_route(const char * prefix, const uint32_t interface_id)
+{
+ char buffer[BUFSIZE];
+ struct nlmsghdr * hdr = (struct nlmsghdr *)buffer;
+ size_t n;
+ int fd;
+
+ int pton_fd;
+ unsigned char dst[sizeof(struct in6_addr)];
+ char * p;
+ char * eptr;
+ char addr[strlen(prefix)];
+ uint32_t dst_len;
+
+ strncpy(addr, prefix, strlen(prefix));
+
+ p = strchr(addr, '/');
+ if (!p) {
+ dst_len = IPV6_ADDR_LEN;
+ } else {
+ dst_len = strtoul(p + 1, &eptr, 10);
+ if (dst_len > IPV6_ADDR_LEN * 8) {
+ printf("E: Netmask > IPV6_ADDR_LEN");
+ return -1;
+ }
+ *p = 0;
+ }
+
+ pton_fd = inet_pton(AF_INET6, addr, dst);
+ if (pton_fd <= 0) {
+ if (pton_fd == 0)
+ ;//ERROR("Not in presentation format");
+ else
+ perror("inet_pton");
+ return -2;
+ }
+
+ _nl_header(RTM_NEWROUTE, (uint8_t *)buffer, BUFSIZE, FLAGS_CREATE_MATCH);
+ _nl_payload_route(RT_TABLE_MAIN, dst_len, (uint8_t *)buffer, BUFSIZE);
+
+ addAttr(hdr, BUFSIZE, RTA_DST, dst, IPV6_ADDR_LEN);
+ addAttr(hdr, BUFSIZE, RTA_OIF, (void*)&interface_id, sizeof(uint32_t));
+
+ fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (fd < 0) {
+ goto ERR_SOCKET;
+ }
+
+ n = send(fd, buffer, hdr->nlmsg_len, 0);
+ if (n == -1) {
+ goto ERR_SEND;
+ }
+ n = recv(fd, buffer, BUFSIZE, 0);
+ if (n == -1) {
+ goto ERR_RECV;
+ }
+
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr * err = (struct nlmsgerr *)NLMSG_DATA(hdr);
+ if (err->error < 0) {
+ errno = -err->error;
+ goto ERR_NL;
+ }
+ return 0;
+ }
+
+ERR_NL:
+ERR_RECV:
+ERR_SEND:
+ERR_SOCKET:
+ return -1;
+
+}
+#endif
+
+/*
+ * ip -6 route add local default via GATEWAY_IP table TABLE_ID
+ */
+int _nl_add_out_route(const char *gateway, uint8_t address_family,
+ const uint8_t table_id, int default_route) {
+ char buffer[BUFSIZE];
+ struct nlmsghdr *hdr = (struct nlmsghdr *)buffer;
+ size_t n;
+ int fd;
+
+ int pton_fd;
+
+ if (address_family == AF_INET) {
+ struct in_addr gw;
+
+ pton_fd = inet_pton(AF_INET, gateway, (struct in_addr *)&gw);
+ if (pton_fd < 0) {
+ return -1;
+ }
+
+ _nl_header(RTM_NEWROUTE, (uint8_t *)buffer, BUFSIZE,
+ NLM_F_REQUEST | NLM_F_ACK | NLM_F_MATCH | NLM_F_ATOMIC);
+ _nl_payload_route(table_id, address_family, 0, (uint8_t *)buffer, BUFSIZE);
+
+ /* gw */
+ addAttr(hdr, BUFSIZE, RTA_GATEWAY, &gw, sizeof(gw));
+
+ } else if (address_family == AF_INET6) {
+ struct in6_addr gw;
+
+ pton_fd = inet_pton(AF_INET6, gateway, (struct in6_addr *)&gw);
+ if (pton_fd < 0) {
+ return -1;
+ }
+
+ _nl_header(RTM_NEWROUTE, (uint8_t *)buffer, BUFSIZE,
+ NLM_F_REQUEST | NLM_F_ACK | NLM_F_MATCH | NLM_F_ATOMIC);
+ _nl_payload_route(table_id, address_family, 0, (uint8_t *)buffer, BUFSIZE);
+
+ /* gw */
+ addAttr(hdr, BUFSIZE, RTA_GATEWAY, &gw, sizeof(gw));
+ if (default_route != -1) {
+ addAttr(hdr, BUFSIZE, RTA_OIF, &default_route, sizeof(default_route));
+ }
+
+ } else {
+ return -1;
+ }
+
+ // For more than 255 tables
+ // addAttr(msg, BUFSIZE, RTA_TABLE, &table_id, sizeof(uint32_t));
+
+ fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (fd < 0) {
+ goto ERR_SOCKET;
+ }
+
+ n = send(fd, buffer, hdr->nlmsg_len, 0);
+ if (n == -1) {
+ goto ERR_SEND;
+ }
+ n = recv(fd, buffer, BUFSIZE, 0);
+ if (n == -1) {
+ goto ERR_RECV;
+ }
+
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr);
+ if (err->error < 0) {
+ errno = -err->error;
+ goto ERR_NL;
+ }
+ return 0;
+ }
+
+ERR_NL:
+ERR_RECV:
+ERR_SEND:
+ERR_SOCKET:
+ return -1;
+}
+
+/*
+ * ip -6 route del local default via GATEWAY_IP table TABLE_ID
+ */
+int _nl_del_out_route(const char *gateway, const uint8_t address_family,
+ const uint8_t table_id) {
+ char buffer[BUFSIZE];
+ struct nlmsghdr *hdr = (struct nlmsghdr *)buffer;
+ size_t n;
+ int fd;
+
+ int pton_fd;
+
+ if (address_family == AF_INET) {
+ struct in_addr gw;
+
+ pton_fd = inet_pton(AF_INET, gateway, (struct in_addr *)&gw);
+ if (pton_fd < 0) {
+ return -1;
+ }
+
+ _nl_header(RTM_DELROUTE, (uint8_t *)buffer, BUFSIZE,
+ NLM_F_REQUEST | NLM_F_ACK | NLM_F_MATCH | NLM_F_ATOMIC);
+ _nl_payload_route(table_id, address_family, 0, (uint8_t *)buffer, BUFSIZE);
+
+ /* gw */
+ addAttr(hdr, BUFSIZE, RTA_GATEWAY, &gw, sizeof(gw));
+
+ } else if (address_family == AF_INET6) {
+ struct in6_addr gw;
+
+ pton_fd = inet_pton(AF_INET6, gateway, (struct in6_addr *)&gw);
+ if (pton_fd < 0) {
+ return -1;
+ }
+
+ _nl_header(RTM_DELROUTE, (uint8_t *)buffer, BUFSIZE,
+ NLM_F_REQUEST | NLM_F_ACK | NLM_F_MATCH | NLM_F_ATOMIC);
+ _nl_payload_route(table_id, address_family, 0, (uint8_t *)buffer, BUFSIZE);
+
+ /* gw */
+ addAttr(hdr, BUFSIZE, RTA_GATEWAY, &gw, sizeof(gw));
+
+ } else {
+ return -1;
+ }
+
+ // For more than 255 tables
+ // addAttr(msg, BUFSIZE, RTA_TABLE, &table_id, sizeof(uint32_t));
+
+ fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (fd < 0) {
+ goto ERR_SOCKET;
+ }
+
+ n = send(fd, buffer, hdr->nlmsg_len, 0);
+ if (n == -1) {
+ goto ERR_SEND;
+ }
+ n = recv(fd, buffer, BUFSIZE, 0);
+ if (n == -1) {
+ goto ERR_RECV;
+ }
+
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr);
+ if (err->error < 0) {
+ errno = -err->error;
+ goto ERR_NL;
+ }
+ return 0;
+ }
+
+ERR_NL:
+ERR_RECV:
+ERR_SEND:
+ERR_SOCKET:
+ return -1;
+}
+
+/*
+ * ip route del 1:2::2 dev lo table local
+ *
+ */
+int _nl_del_lo_route(const ip_address_t *ip_address) {
+ char buffer[BUFSIZE];
+ struct nlmsghdr *hdr = (struct nlmsghdr *)buffer;
+ size_t n;
+ int fd;
+
+ struct {
+ struct nlmsghdr hdr;
+ struct rtmsg payload;
+ } msg = {
+ .hdr.nlmsg_type = RTM_DELROUTE,
+ .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
+ .hdr.nlmsg_seq = seq++,
+ .payload.rtm_family = ip_address->family,
+ .payload.rtm_dst_len = ip_address->prefix_len,
+ .payload.rtm_src_len = 0,
+ .payload.rtm_tos = 0,
+ .payload.rtm_table = RT_TABLE_LOCAL,
+ .payload.rtm_protocol = RTPROT_UNSPEC,
+ .payload.rtm_scope = RT_SCOPE_UNIVERSE,
+ .payload.rtm_type = RTN_UNSPEC,
+ .payload.rtm_flags = 0 // RTM_F_NOTIFY in 'ip route get'
+ };
+
+ /* Set attribute = length/type/value */
+ uint32_t one = 1;
+ struct rtattr a_dst = {RTA_LENGTH(ip_address_len(ip_address)), RTA_DST};
+ struct rtattr a_ifid_lo = {RTA_LENGTH(sizeof(uint32_t)), RTA_OIF};
+ struct iovec iov[] = {
+ {&msg, sizeof(msg)},
+ /* Ip address */
+ {&a_dst, sizeof(a_dst)},
+ {(void *)&ip_address->buffer, ip_address_len(ip_address)},
+ /* Interface id */
+ {&a_ifid_lo, sizeof(a_ifid_lo)},
+ {&one, sizeof(one)}};
+ msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov));
+
+ fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (fd < 0) {
+ goto ERR;
+ }
+
+ n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov));
+ if (n == -1) {
+ goto ERR;
+ }
+ n = recv(fd, buffer, BUFSIZE, 0);
+ if (n == -1) {
+ goto ERR;
+ }
+
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr);
+ if (err->error < 0) {
+ errno = -err->error;
+ goto ERR;
+ }
+ return 0;
+ }
+
+ return HICN_SOCKET_ERROR_NONE;
+ERR:
+ return HICN_SOCKET_ERROR_UNSPEC;
+}
+
+/*
+ * ip -6 rule add iif INTERFACE_NAME lookup TABLE_ID
+ */
+int _nl_add_rule(const char *interface_name, uint8_t address_family,
+ const uint8_t table_id) {
+ char buffer[BUFSIZE];
+ struct nlmsghdr *hdr = (struct nlmsghdr *)buffer;
+ size_t n;
+ int fd;
+
+ _nl_header(RTM_NEWRULE, (uint8_t *)buffer, BUFSIZE, FLAGS_CREATE);
+ _nl_payload_rule(table_id, address_family, (uint8_t *)buffer, BUFSIZE);
+
+ /* XXX iif */
+ addAttr(hdr, BUFSIZE, FRA_IIFNAME, (void *)interface_name,
+ strlen(interface_name));
+ // attr1 = addNestedAttr(hdr, IFLA_LINKINFO);
+ // endNestedAttr(hdr, attr1);
+
+ fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (fd < 0) {
+ goto ERR_SOCKET;
+ }
+
+ n = send(fd, buffer, hdr->nlmsg_len, 0);
+ if (n == -1) {
+ goto ERR_SEND;
+ }
+ n = recv(fd, buffer, BUFSIZE, 0);
+ if (n == -1) {
+ goto ERR_RECV;
+ }
+
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr);
+ if (err->error < 0) {
+ errno = -err->error;
+ goto ERR_NL;
+ }
+ return 0;
+ }
+
+ERR_NL:
+ERR_RECV:
+ERR_SEND:
+ERR_SOCKET:
+ return -1;
+}
+
+/*
+ * ip -6 rule del iif INTERFACE_NAME //lookup TABLE_ID
+ */
+int _nl_del_rule(const char *interface_name, uint8_t address_family,
+ const uint8_t table_id) {
+ char buffer[BUFSIZE];
+ struct nlmsghdr *hdr = (struct nlmsghdr *)buffer;
+ size_t n;
+ int fd;
+
+ _nl_header(RTM_DELRULE, (uint8_t *)buffer, BUFSIZE, FLAGS_CREATE);
+ _nl_payload_rule(table_id, address_family, (uint8_t *)buffer, BUFSIZE);
+
+ /* XXX iif */
+ addAttr(hdr, BUFSIZE, FRA_IIFNAME, (void *)interface_name,
+ strlen(interface_name));
+
+ fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (fd < 0) {
+ goto ERR_SOCKET;
+ }
+
+ n = send(fd, buffer, hdr->nlmsg_len, 0);
+ if (n == -1) {
+ goto ERR_SEND;
+ }
+
+ n = recv(fd, buffer, BUFSIZE, 0);
+ if (n == -1) {
+ goto ERR_RECV;
+ }
+
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr);
+ if (err->error < 0) {
+ errno = -err->error;
+ goto ERR_NL;
+ }
+ return 0;
+ }
+
+ERR_NL:
+ERR_RECV:
+ERR_SEND:
+ERR_SOCKET:
+ return -1;
+}
+
+/*
+ * ip -6 neigh add proxy 1:2::2 dev hicnc-cons-eth0 2>&1 | grep nei
+ *
+ */
+int _nl_add_neigh_proxy(const ip_address_t *ip_address,
+ const uint32_t interface_id) {
+ /* Buffer for holding the response, with appropriate casting on the header */
+ char buffer[BUFSIZE];
+ struct nlmsghdr *hdr = (struct nlmsghdr *)buffer;
+
+ /* Used for send and receive operations on netlink socket */
+ int fd;
+ size_t n;
+
+ /* Packet header */
+ struct {
+ struct nlmsghdr hdr;
+ struct ndmsg payload;
+ } msg = {
+ .hdr.nlmsg_type = RTM_NEWNEIGH,
+ .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | NLM_F_EXCL,
+ .hdr.nlmsg_seq = seq++,
+ .payload.ndm_family = ip_address->family,
+ .payload.ndm_ifindex = interface_id,
+ .payload.ndm_state = NUD_PERMANENT,
+ .payload.ndm_flags = NTF_PROXY,
+ };
+
+ /* Message attributes = length/type/value */
+ struct rtattr a_dst = {RTA_LENGTH(ip_address_len(ip_address)), NDA_DST};
+
+ /* Iovec describing the packets */
+ struct iovec iov[] = {
+ {&msg, sizeof(msg)},
+ /* Ip address */
+ {&a_dst, sizeof(a_dst)},
+ {(void *)&ip_address->buffer, sizeof(ip_address->buffer)},
+ };
+ msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov));
+
+ /* Open netlink socket */
+ fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (fd < 0) {
+ goto ERR;
+ }
+
+ /* Send packet */
+ n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov));
+ if (n == -1) {
+ goto ERR;
+ }
+
+ /* Receive answer */
+ n = recv(fd, buffer, BUFSIZE, 0);
+ if (n == -1) {
+ goto ERR;
+ }
+
+ /* Parse answer */
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr);
+ if (err->error < 0) {
+ errno = -err->error;
+ goto ERR;
+ }
+ }
+
+ return HICN_SOCKET_ERROR_NONE;
+ERR:
+ return HICN_SOCKET_ERROR_UNSPEC;
+}
+
+/* ip -6 route add 0:1::/64 dev hicn-if0 table 100 */
+/* ip -6 route add 0:2::/64 dev hicn-if1 table 100 */
+int _nl_add_in_route_table(const ip_address_t *prefix,
+ const uint32_t interface_id,
+ const uint8_t table_id) {
+ /* Buffer for holding the response, with appropriate casting on the header */
+ char buffer[BUFSIZE];
+ struct nlmsghdr *hdr = (struct nlmsghdr *)buffer;
+
+ /* Used for send and receive operations on netlink socket */
+ int fd;
+ size_t n;
+
+ /* Packet header */
+ struct {
+ struct nlmsghdr hdr;
+ struct rtmsg payload;
+ } msg = {
+ .hdr.nlmsg_type = RTM_NEWROUTE,
+ .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | NLM_F_EXCL,
+ .hdr.nlmsg_seq = seq++,
+ .payload.rtm_family = prefix->family,
+ .payload.rtm_dst_len = prefix->prefix_len, // XXX ? XXX dst_len,
+ .payload.rtm_src_len = 0,
+ .payload.rtm_tos = 0,
+ .payload.rtm_table = table_id, /* RT_TABLE_MAIN, etc. */
+ .payload.rtm_protocol = RTPROT_BOOT,
+ .payload.rtm_scope =
+ prefix->family == AF_INET6 ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK,
+ .payload.rtm_type = RTN_UNICAST,
+ .payload.rtm_flags = 0,
+ };
+
+ /* Message attributes = length/type/value */
+ // XXX This could be put directly inside the iovec maybe ? XXX
+ struct rtattr a_dst = {RTA_LENGTH(ip_address_len(prefix)), RTA_DST};
+ struct rtattr a_oif = {RTA_LENGTH(sizeof(uint32_t)), RTA_OIF};
+
+ /* Iovec describing the packets */
+ struct iovec iov[] = {
+ {&msg, sizeof(msg)},
+ /* Destination prefix / ip address */
+ {&a_dst, sizeof(a_dst)},
+ {(void *)&prefix->buffer, ip_address_len(prefix)},
+ /* Output interface */
+ {&a_oif, sizeof(a_oif)},
+ {(void *)&interface_id, sizeof(uint32_t)},
+ };
+ msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov));
+
+ /* Open netlink socket */
+ fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (fd < 0) {
+ goto ERR;
+ }
+
+ /* Send packet */
+ n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov));
+ if (n == -1) {
+ goto ERR;
+ }
+
+ /* Receive answer */
+ n = recv(fd, buffer, BUFSIZE, 0);
+ if (n == -1) {
+ goto ERR;
+ }
+
+ /* Parse answer */
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr);
+ if (err->error < 0) {
+ errno = -err->error;
+ goto ERR;
+ }
+ }
+
+ return HICN_SOCKET_ERROR_NONE;
+ERR:
+ return HICN_SOCKET_ERROR_UNSPEC;
+}
+
+/* Additional helper functions */
+
+int _nl_add_in_route_table_s(const char *prefix, const uint32_t interface_id,
+ const uint8_t table_id) {
+ int rc;
+ ip_address_t ip_address;
+
+ rc = hicn_ip_pton(prefix, &ip_address);
+ if (rc < 0) {
+ return rc;
+ }
+
+ return _nl_add_in_route_table(&ip_address, interface_id, table_id);
+}
+
+int _nl_add_in_route_s(const char *prefix, const uint32_t interface_id) {
+ return _nl_add_in_route_table_s(prefix, interface_id, RT_TABLE_MAIN);
+}
+
+////////* ip -6 rule add from all prio 10 table local */
+/* ip -6 rule add from b001::/16 prio 0 table 100 */
+int _nl_add_prio_rule(const ip_address_t *ip_address, uint8_t address_family,
+ const uint32_t priority, const uint8_t table_id) {
+ /* Buffer for holding the response, with appropriate casting on the header */
+ char buffer[BUFSIZE];
+ struct nlmsghdr *hdr = (struct nlmsghdr *)buffer;
+
+ /* Used for send and receive operations on netlink socket */
+ int fd;
+ size_t n;
+
+ /* Packet header */
+ struct {
+ struct nlmsghdr hdr;
+ struct fib_rule_hdr payload;
+ } msg = {
+ .hdr.nlmsg_type = RTM_NEWRULE,
+ .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | NLM_F_EXCL,
+ .hdr.nlmsg_seq = seq++,
+ .payload.family = address_family,
+ //.payload.dst_len = ,
+ .payload.src_len = ip_address ? ip_address->prefix_len : 0,
+ .payload.tos = 0,
+ .payload.table = table_id,
+ .payload.action = FR_ACT_TO_TBL,
+ .payload.flags = NLM_F_REPLACE, // 0
+ };
+
+ /* Open netlink socket */
+ fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (fd < 0) {
+ goto ERR;
+ }
+
+ if (ip_address) {
+ /* Message attributes = length/type/value */
+ struct rtattr a_src = {RTA_LENGTH(ip_address_len(ip_address)), FRA_SRC};
+ struct rtattr a_prio = {RTA_LENGTH(sizeof(uint32_t)), FRA_PRIORITY};
+
+ /* Iovec describing the packets */
+ struct iovec iov[] = {
+ {&msg, sizeof(msg)},
+ /* Source prefix / ip_address */
+ {&a_src, sizeof(a_src)},
+ {(void *)&ip_address->buffer, ip_address_len(ip_address)},
+ /* Priority */
+ {&a_prio, sizeof(a_prio)},
+ {(void *)&priority, sizeof(uint32_t)},
+ };
+ msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov));
+
+ /* Send packet */
+ n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov));
+ if (n == -1) {
+ goto ERR;
+ }
+ } else {
+ struct rtattr a_prio = {RTA_LENGTH(sizeof(uint32_t)), FRA_PRIORITY};
+
+ /* Iovec describing the packets */
+ struct iovec iov[] = {
+ {&msg, sizeof(msg)},
+ /* Priority */
+ {&a_prio, sizeof(a_prio)},
+ {(void *)&priority, sizeof(uint32_t)},
+ };
+ msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov));
+
+ /* Send packet */
+ n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov));
+ if (n == -1) {
+ goto ERR;
+ }
+ }
+
+ /* Receive answer */
+ n = recv(fd, buffer, BUFSIZE, 0);
+ if (n == -1) {
+ goto ERR;
+ }
+
+ /* Parse answer */
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr);
+ if (err->error < 0) {
+ errno = -err->error;
+ goto ERR;
+ }
+ }
+
+ return HICN_SOCKET_ERROR_NONE;
+ERR:
+ return HICN_SOCKET_ERROR_UNSPEC;
+}
+
+int _nl_add_lo_prio_rule(const ip_address_t *ip_address, uint8_t address_family,
+ const uint32_t priority) {
+ return _nl_add_prio_rule(ip_address, address_family, priority,
+ RT_TABLE_LOCAL);
+}
+
+/* ip -6 rule del from all prio 0 table local */
+int _nl_del_prio_rule(const ip_address_t *ip_address, uint8_t address_family,
+ const uint32_t priority, const uint8_t table_id) {
+ /* Buffer for holding the response, with appropriate casting on the header */
+ char buffer[BUFSIZE];
+ struct nlmsghdr *hdr = (struct nlmsghdr *)buffer;
+
+ /* Used for send and receive operations on netlink socket */
+ int fd;
+ size_t n;
+
+ /* Packet header */
+ struct {
+ struct nlmsghdr hdr;
+ struct fib_rule_hdr payload;
+ } msg = {
+ .hdr.nlmsg_type = RTM_DELRULE,
+ .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK | NLM_F_EXCL,
+ .hdr.nlmsg_seq = seq++,
+ .payload.family = address_family,
+ //.payload.dst_len = ,
+ .payload.src_len = ip_address ? ip_address->prefix_len : 0,
+ .payload.tos = 0,
+ .payload.table = table_id,
+ .payload.action = FR_ACT_TO_TBL,
+ .payload.flags = NLM_F_REPLACE, // 0
+ };
+
+ /* Open netlink socket */
+ fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (fd < 0) {
+ goto ERR;
+ }
+
+ /* Message attributes = length/type/value */
+ if (ip_address) {
+ struct rtattr a_src = {RTA_LENGTH(ip_address_len(ip_address)), FRA_SRC};
+ struct rtattr a_prio = {RTA_LENGTH(sizeof(uint32_t)), FRA_PRIORITY};
+
+ /* Iovec describing the packets */
+ struct iovec iov[] = {
+ {&msg, sizeof(msg)},
+ /* Source prefix / ip_address */
+ {&a_src, sizeof(a_src)},
+ {(void *)&ip_address->buffer, ip_address_len(ip_address)},
+ /* Priority */
+ {&a_prio, sizeof(a_prio)},
+ {(void *)&priority, sizeof(uint32_t)},
+ };
+ msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov));
+
+ /* Send packet */
+ n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov));
+ if (n == -1) {
+ goto ERR;
+ }
+
+ } else {
+ struct rtattr a_prio = {RTA_LENGTH(sizeof(uint32_t)), FRA_PRIORITY};
+
+ /* Iovec describing the packets */
+ struct iovec iov[] = {
+ {&msg, sizeof(msg)},
+ /* Priority */
+ {&a_prio, sizeof(a_prio)},
+ {(void *)&priority, sizeof(uint32_t)},
+ };
+ msg.hdr.nlmsg_len = iov_length(iov, ARRAY_SIZE(iov));
+
+ /* Send packet */
+ n = writev(fd, (struct iovec *)&iov, ARRAY_SIZE(iov));
+ if (n == -1) {
+ goto ERR;
+ }
+ }
+
+ /* Receive answer */
+ n = recv(fd, buffer, BUFSIZE, 0);
+ if (n == -1) {
+ goto ERR;
+ }
+
+ /* Parse answer */
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(hdr);
+ if (err->error < 0 &&
+ err->error != -2) { //-2 is not such file or directory
+ errno = -err->error;
+ goto ERR;
+ }
+ }
+
+ return HICN_SOCKET_ERROR_NONE;
+ERR:
+ return HICN_SOCKET_ERROR_UNSPEC;
+}
+
+int _nl_del_lo_prio_rule(const ip_address_t *ip_address, uint8_t address_family,
+ const uint32_t priority) {
+ return _nl_del_prio_rule(ip_address, address_family, priority,
+ RT_TABLE_LOCAL);
+}
+
+/******************************************************************************/
+
+// #include <net/if.h>
+// duplicate declarations, in the meantime
+#define IF_NAMESIZE 16
+
+//#define WITH_TUN_PI 1
+
+#ifdef WITH_TUN_PI
+#define TUN_FLAGS IFF_TUN
+#else
+#define TUN_FLAGS IFF_TUN | IFF_NO_PI
+#endif
+
+/*
+ * Taken from Kernel Documentation/networking/tuntap.txt
+ */
+
+int tun_alloc(char *dev, int flags) {
+ struct ifreq ifr;
+ int fd, err;
+ char *clonedev = "/dev/net/tun";
+
+ /* Arguments taken by the function:
+ *
+ * char *dev: the name of an interface (or '\0'). MUST have enough
+ * space to hold the interface name if '\0' is passed
+ * int flags: interface flags (eg, IFF_TUN etc.)
+ */
+
+ /* open the clone device */
+ if ((fd = open(clonedev, O_RDWR)) < 0) {
+ return fd;
+ }
+
+ /* preparation of the struct ifr, of type "struct ifreq" */
+ memset(&ifr, 0, sizeof(ifr));
+
+ ifr.ifr_flags = flags;
+
+ if (*dev) {
+ /* if a device name was specified, put it in the structure; otherwise,
+ * the kernel will try to allocate the "next" device of the
+ * specified type */
+ strncpy(ifr.ifr_name, dev, IF_NAMESIZE - 1);
+ }
+
+ /* try to create the device */
+ if ((err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0) {
+ close(fd);
+ return err;
+ }
+
+ /* if the operation was successful, write back the name of the
+ * interface to the variable "dev", so the caller can know
+ * it. Note that the caller MUST reserve space in *dev (see calling
+ * code below) */
+ strcpy(dev, ifr.ifr_name);
+
+ /* this is the special file descriptor that the caller will use to talk
+ * with the virtual interface */
+ return fd;
+}
+
+int linux_get_tun_name(const char *prefix, const char *identifier,
+ char *tun_name) {
+ snprintf(tun_name, IF_NAMESIZE, "%s-%s", prefix,
+ identifier ? identifier : "main");
+ return 0;
+}
+
+int linux_tun_enable_offload(int fd) {
+ unsigned int offload = 0, tso4 = 1, tso6 = 1, ecn = 1, ufo = 1, csum = 1;
+
+ /* Check if our kernel supports TUNSETOFFLOAD */
+ if (ioctl(fd, TUNSETOFFLOAD, 0) != 0 && errno == EINVAL) {
+ goto ERR_TUN;
+ }
+
+ if (csum) {
+ offload |= TUN_F_CSUM;
+ if (tso4) offload |= TUN_F_TSO4;
+ if (tso6) offload |= TUN_F_TSO6;
+ if ((tso4 || tso6) && ecn) offload |= TUN_F_TSO_ECN;
+ if (ufo) offload |= TUN_F_UFO;
+ }
+
+ if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) {
+ offload &= ~TUN_F_UFO;
+ if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) {
+ fprintf(stderr, "TUNSETOFFLOAD ioctl() failed: %s\n", strerror(errno));
+ }
+ }
+
+ return 0;
+
+ERR_TUN:
+ return -1;
+}
+
+int linux_tun_create(char *name) {
+ int fd, rc;
+
+ fd = tun_alloc(name, TUN_FLAGS);
+ if (fd < 0) {
+ // ERROR("Error connecting to tun/tap interface %s!", name);
+ errno = -2;
+ goto ERR_TUN;
+ }
+
+ rc = linux_tun_enable_offload(fd);
+ if (rc < 0) {
+ // WARN("Could not enable hardware offload on TUN device");
+ } else {
+ // INFO("Enabled hardware offload on TUN device");
+ }
+
+ return fd;
+
+ERR_TUN:
+ return -1;
+}
+
+/*
+ *
+ * interface name can be NULL for all interfaces
+ */
+int linux_enable_proc(char *path) {
+ int ret = 0;
+ int fd;
+
+ fd = open(path, O_WRONLY);
+ if (fd < 0) {
+ return -1;
+ }
+
+ if (write(fd, "1", 1) != 1) {
+ ret = -2;
+ }
+
+ close(fd);
+ return ret;
+}
+
+int linux_enable_v4_forwarding() {
+ return linux_enable_proc("/proc/sys/net/ipv4/ip_forward");
+}
+
+int linux_enable_v6_forwarding(char *interface_name) {
+ char path[PATH_MAX];
+ snprintf(path, PATH_MAX, "/proc/sys/net/ipv6/conf/%s/forwarding",
+ (interface_name) ? interface_name : "all");
+
+ return linux_enable_proc(path);
+}
+
+int linux_enable_ndp_proxy() {
+ return linux_enable_proc("/proc/sys/net/ipv6/conf/all/proxy_ndp");
+}
+
+const hicn_socket_ops_t ops = {
+ .arch = "linux",
+ .get_tun_name = linux_get_tun_name,
+ .tun_create = linux_tun_create,
+ .enable_v4_forwarding = linux_enable_v4_forwarding,
+ .enable_v6_forwarding = linux_enable_v6_forwarding,
+ .enable_ndp_proxy = linux_enable_ndp_proxy,
+ .get_ifid = _nl_get_ifid,
+ .get_output_ifid = _nl_get_output_ifid,
+ .get_ip_addr = _nl_get_ip_addr,
+ .set_ip_addr = _nl_set_ip_addr,
+ .up_if = _nl_up_if,
+ .add_in_route_table = _nl_add_in_route_table,
+ .add_in_route_table_s = _nl_add_in_route_table_s,
+ .add_in_route_s = _nl_add_in_route_s,
+ .add_out_route = _nl_add_out_route,
+ .del_out_route = _nl_del_out_route,
+ .del_lo_route = _nl_del_lo_route,
+ .add_rule = _nl_add_rule,
+ .del_rule = _nl_del_rule,
+ .add_neigh_proxy = _nl_add_neigh_proxy,
+ .add_prio_rule = _nl_add_prio_rule,
+ .add_lo_prio_rule = _nl_add_lo_prio_rule,
+ .del_prio_rule = _nl_del_prio_rule,
+ .del_lo_prio_rule = _nl_del_lo_prio_rule,
+};