diff options
Diffstat (limited to 'hicn-light/src/socket')
-rwxr-xr-x | hicn-light/src/socket/CMakeLists.txt | 31 | ||||
-rwxr-xr-x | hicn-light/src/socket/api.c | 604 | ||||
-rwxr-xr-x | hicn-light/src/socket/api.h | 216 | ||||
-rwxr-xr-x | hicn-light/src/socket/error.c | 7 | ||||
-rwxr-xr-x | hicn-light/src/socket/error.h | 46 | ||||
-rwxr-xr-x | hicn-light/src/socket/ops.h | 54 | ||||
-rwxr-xr-x | hicn-light/src/socket/ops_linux.c | 1723 |
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, +}; |