diff options
Diffstat (limited to 'hicn-light/src/socket/ops_linux.c')
-rwxr-xr-x | hicn-light/src/socket/ops_linux.c | 1723 |
1 files changed, 1723 insertions, 0 deletions
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, +}; |