diff options
author | Mauro Sardara <msardara@cisco.com> | 2019-10-07 14:37:42 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@fd.io> | 2019-10-07 14:37:42 +0000 |
commit | 7896701a177d66f376172ab43df4b0c1d5d867a3 (patch) | |
tree | a89986dcceb1d5b6faa7ae529b1d4a1e9f4d6d85 /ctrl/facemgr/src | |
parent | 108c55669102931acc9bd99ca9918379722732b8 (diff) | |
parent | 6b84ec54083da9911f5ad4816d0eb4f4745afad4 (diff) |
Merge "[HICN-298] Release new hICN app for Android"
Diffstat (limited to 'ctrl/facemgr/src')
51 files changed, 7139 insertions, 2414 deletions
diff --git a/ctrl/facemgr/src/CMakeLists.txt b/ctrl/facemgr/src/CMakeLists.txt index e7dbda5c1..f99e57e07 100644 --- a/ctrl/facemgr/src/CMakeLists.txt +++ b/ctrl/facemgr/src/CMakeLists.txt @@ -20,40 +20,22 @@ set(COMPILER_DEFINITIONS ) set(HEADER_FILES -#cache.h common.h error.h - event.h - face.h - face_cache.h - face_rules.h + facelet.h interface.h - interface_map.h - interface_ops_map.h util/hash.h - util/ip_address.h - util/log.h util/map.h - util/policy.h util/set.h - util/token.h - util/types.h - ) set(SOURCE_FILES -# cache.c + api.c + cfg.c error.c - event.c - face.c - face_cache.c - face_rules.c + facelet.c interface.c - interface_map.c - interface_ops_map.c - facemgr.c util/log.c - util/policy.c ) set(INCLUDE_DIRS @@ -61,51 +43,82 @@ set(INCLUDE_DIRS ../includes/ ${CONFIG_INCLUDE_DIR} ${LIBEVENT_INCLUDE_DIR} + ${HICN_INCLUDE_DIRS} ${LIBHICNCTRL_INCLUDE_DIRS} ) set(LIBRARIES ${CONFIG_LIBRARY} ${LIBEVENT_LIBRARY} + ${HICN_LIBRARIES} ${LIBHICNCTRL_LIBRARIES} ) + add_subdirectory(interfaces) -if (ANDROID_API) - build_library(${FACE_MGR} +if(WITH_THREAD) + set(LIBRARIES + ${LIBRARIES} + "pthread" + "event_pthreads" + ) + set(COMPILER_DEFINITIONS + ${COMPILER_DEFINITIONS} + "-DWITH_THREAD" + ) +endif() + +if(WITH_EXAMPLE_DUMMY) + set(COMPILER_DEFINITIONS + ${COMPILER_DEFINITIONS} + "-DWITH_EXAMPLE_DUMMY" + ) +endif() + +if(WITH_EXAMPLE_UPDOWN) + set(COMPILER_DEFINITIONS + ${COMPILER_DEFINITIONS} + "-DWITH_EXAMPLE_UPDOWN" + ) +endif() + +if (${CMAKE_SYSTEM_NAME} STREQUAL "Android") + build_library(${LIBFACEMGR} STATIC SOURCES ${SOURCE_FILES} INSTALL_HEADERS ${TO_INSTALL_HEADER_FILES} DEPENDS ${DEPENDENCIES} - COMPONENT ${FACE_MGR} + LINK_LIBRARIES ${LIBRARIES} + COMPONENT ${FACEMGR} INCLUDE_DIRS ${INCLUDE_DIRS} INSTALL_ROOT_DIR hicn DEFINITIONS ${COMPILER_DEFINITIONS} ) else () - build_library(${FACE_MGR} - STATIC NO_DEV + build_library(${LIBFACEMGR} + SHARED STATIC NO_DEV SOURCES ${SOURCE_FILES} INSTALL_HEADERS ${TO_INSTALL_HEADER_FILES} DEPENDS ${DEPENDENCIES} - COMPONENT ${FACE_MGR} + LINK_LIBRARIES ${LIBRARIES} + COMPONENT ${FACEMGR} INCLUDE_DIRS ${INCLUDE_DIRS} INSTALL_ROOT_DIR hicn DEFINITIONS ${COMPILER_DEFINITIONS} ) endif () -if(NOT ANDROID_API AND NOT COMPILE_FOR_IOS) +if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Android" AND NOT COMPILE_FOR_IOS) list(APPEND DAEMON_SRC main.c ) - build_executable(${FACE_MGR} + build_executable(${FACEMGR} SOURCES ${DAEMON_SRC} - LINK_LIBRARIES ${FACE_MGR}.static ${LIBRARIES} - DEPENDS ${FACE_MGR}.static - COMPONENT ${FACE_MGR} + LINK_LIBRARIES ${LIBFACEMGR}.static ${LIBRARIES} + DEPENDS ${LIBFACEMGR}.static + COMPONENT ${FACEMGR} INCLUDE_DIRS ${INCLUDE_DIRS} DEFINITIONS ${COMPILER_DEFINITIONS} ) diff --git a/ctrl/facemgr/src/api.c b/ctrl/facemgr/src/api.c new file mode 100644 index 000000000..f630792c4 --- /dev/null +++ b/ctrl/facemgr/src/api.c @@ -0,0 +1,1421 @@ +/* + * 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 facemgr.c + * \brief Implementation of Face manager library interface + */ + +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#include <hicn/facemgr/api.h> +#include <hicn/facemgr/cfg.h> +#include <hicn/util/log.h> + +#ifdef __APPLE__ +#include "interfaces/network_framework/network_framework.h" +#endif /* __APPLE__ */ + +#ifdef __linux__ +#include "interfaces/bonjour/bonjour.h" +#endif /* __linux__ */ + +#ifdef __ANDROID__ +#include <hicn/android_utility/android_utility.h> +#endif /* __ANDROID__ */ + +#include <hicn/ctrl/face.h> +#include "common.h" +#include "facelet.h" +#include "interface.h" +#include "util/map.h" +#include "util/set.h" + +#define RAND_NAME_LEN 5 + +#define DEFAULT_PORT 9695 + +typedef struct { + interface_t * interface; + void * event; +} interface_map_data_t; + +TYPEDEF_SET_H(facelet_cache, facelet_t *); +TYPEDEF_SET(facelet_cache, facelet_t *, facelet_cmp, facelet_snprintf); + +TYPEDEF_MAP_H(interface_map, const char *, interface_map_data_t *); +TYPEDEF_MAP(interface_map, const char *, interface_map_data_t *, strcmp, string_snprintf, generic_snprintf); + +int int_cmp(int x, int y) { return x - y; } + +TYPEDEF_MAP_H(bonjour_map, netdevice_t *, interface_t *); +TYPEDEF_MAP(bonjour_map, netdevice_t *, interface_t *, netdevice_cmp, generic_snprintf, generic_snprintf); + +/* TODO automatically register interfaces */ + +#ifdef __APPLE__ +extern interface_ops_t network_framework_ops; +#endif +#ifdef __linux__ +extern interface_ops_t netlink_ops; +extern interface_ops_t bonjour_ops; +#endif +#ifdef __ANDROID__ +extern interface_ops_t android_utility_ops; +#endif /* __ANDROID__ */ +#ifdef WITH_EXAMPLE_DUMMY +extern interface_ops_t dummy_ops; +#endif +#ifdef WITH_EXAMPLE_UPDOWN +extern interface_ops_t updown_ops; +#endif +extern interface_ops_t hicn_light_ops; + + +int +facemgr_overlay_snprintf(char * s, size_t size, const facemgr_overlay_t * overlay) +{ + return -1; +} + +struct facemgr_s { + /**************************************************/ + /* Configuration parameters (exposed through API) */ + + facemgr_cfg_t * cfg; + +#ifdef __ANDROID__ + /* + * Those two pointers are needed to call java functions from the face + * manager. + */ + JavaVM *jvm; +#endif /* __ANDROID__ */ + + /* Event loop support */ + void * loop; + void * (*loop_register_fd)(void * loop, int fd, void * cb, void * cb_args); + int (*loop_unregister_event)(void * loop, void * event); + + /****************************/ + /* Internal data structures */ + + /* Map of interfaces index by name */ + interface_map_t interface_map; + + /* Faces under construction */ + facelet_cache_t facelet_cache; + + /********************************************************/ + /* Interfaces - Those should be fully replaced by a map */ + + interface_t * hl; + +#ifdef __ANDROID__ + interface_t * au; /* android_utility */ +#endif /* __ANDROID__ */ + +#ifdef __APPLE__ + interface_t * nf; /* network_framework */ +#endif /* __APPLE__ */ + +#ifdef __linux__ + interface_t * nl; /* netlink */ + + /* + * We maintain a map of dynamically created bonjour interfaces, one for each + * found netdevice + */ + bonjour_map_t bonjour_map; +#endif /* __linux__ */ + +#ifdef WITH_EXAMPLE_DUMMY + interface_t * dummy; +#endif +#ifdef WITH_EXAMPLE_UPDOWN + interface_t * updown; +#endif +}; + +int +facemgr_initialize(facemgr_t * facemgr) +{ + int rc; + + rc = interface_map_initialize(&facemgr->interface_map); + if (rc < 0) + goto ERR_INTERFACE_MAP; + + rc = facelet_cache_initialize(&facemgr->facelet_cache); + if (rc < 0) + goto ERR_FACE_CACHE_PENDING; + +#ifdef __linux__ + rc = bonjour_map_initialize(&facemgr->bonjour_map); + if (rc < 0) + goto ERR_BJ; +#endif /* __linux */ + + facemgr->cfg = facemgr_cfg_create(); + if (!facemgr->cfg) + goto ERR_CFG; + + return 0; + +ERR_CFG: +#ifdef __linux__ + bonjour_map_finalize(&facemgr->bonjour_map); +ERR_BJ: +#endif /* __linux__ */ + facelet_cache_finalize(&facemgr->facelet_cache); +ERR_FACE_CACHE_PENDING: + interface_map_finalize(&facemgr->interface_map); +ERR_INTERFACE_MAP: + return -1; +} + +int +facemgr_finalize(facemgr_t * facemgr) +{ + int rc; + + /* TODO Free all interfaces: pass free to map */ + + rc = interface_map_finalize(&facemgr->interface_map); + if (rc < 0) + goto ERR; + + rc = facelet_cache_finalize(&facemgr->facelet_cache); + if (rc < 0) + goto ERR; + +#ifdef __linux__ + rc = bonjour_map_finalize(&facemgr->bonjour_map); + if (rc < 0) + goto ERR; +#endif /* __linux__ */ + + return 0; + +ERR: + return -1; +} + +AUTOGENERATE_CREATE_FREE(facemgr); + +int +facemgr_set_config(facemgr_t * facemgr, facemgr_cfg_t * cfg) +{ + if (facemgr->cfg) { + facemgr_cfg_free(facemgr->cfg); + } + facemgr->cfg = cfg; + return 0; +} + +int facemgr_reset_config(facemgr_t * facemgr) +{ + assert(facemgr->cfg); + facemgr_cfg_free(facemgr->cfg); + facemgr->cfg = facemgr_cfg_create(); + if (!facemgr->cfg) + return -1; + return 0; +} + +facemgr_t * +facemgr_create_with_config(facemgr_cfg_t * cfg) +{ + facemgr_t * facemgr = facemgr_create(); + if (!facemgr) + return NULL; + int rc = facemgr_set_config(facemgr, cfg); + if (rc < 0) { + free(facemgr); + return NULL; + } + return facemgr; +} + +int facemgr_on_event(facemgr_t * facemgr, facelet_t * facelet); + +int +facemgr_create_interface(facemgr_t * facemgr, const char * name, const char * type, void * cfg, interface_t ** pinterface) +{ + int fd, rc; + void * event = NULL; + char rand_name[RAND_NAME_LEN+1]; + interface_t * interface; + + if (!name) { + /* + * We can manipulate the name on the stack as it will be strdup'ed by + * interface_create + */ + rand_str(rand_name, RAND_NAME_LEN); + name = rand_name; + } + + INFO("Creating interface %s [%s]...", name, type); + interface = interface_create(name, type); + if (!interface) { + ERROR("Error creating interface %s [%s]", name, type); + goto ERR_CREATE; + } + interface_set_callback(interface, facemgr_on_event, facemgr); + + fd = interface_initialize(interface, cfg); + if (fd < 0) + goto ERR_INIT; + if (fd != 0) { + event = facemgr->loop_register_fd(facemgr->loop, fd, interface->ops->callback, interface); + if (event == NULL) + goto ERR_FD; + } + + interface_map_data_t * interface_map_data = malloc(sizeof(interface_map_data_t)); + if (!interface_map_data) + goto ERR_MAP_DATA; + + + *interface_map_data = (interface_map_data_t) { + .interface = interface, + .event = event, + }; + + rc = interface_map_add(&facemgr->interface_map, interface->name, interface_map_data); + if (rc < 0) + goto ERR_MAP_ADD; + + DEBUG("Interface %s created successfully.", name); + if (pinterface) + *pinterface = interface; + return 0; + +ERR_MAP_ADD: + free(interface_map_data); +ERR_MAP_DATA: + if (fd > 0) + facemgr->loop_unregister_event(facemgr->loop, interface_map_data->event); +ERR_FD: + interface_finalize(interface); +ERR_INIT: + interface_free(interface); +ERR_CREATE: + if (pinterface) + *pinterface = NULL; + return -1; +} + +int +facemgr_delete_interface(facemgr_t * facemgr, interface_t * interface) +{ + int rc; + + interface_map_data_t * interface_map_data = NULL; + + DEBUG("Removing interface %s\n", interface->name); + rc = interface_map_remove(&facemgr->interface_map, interface->name, &interface_map_data); + if (rc < 0) + return -1; + + if (!interface_map_data) + return -1; + + free(interface_map_data); + + rc = facemgr->loop_unregister_event(facemgr->loop, interface_map_data->event); + if (rc < 0) + return -1; + + + interface_finalize(interface); + interface_free(interface); + + return 0; +} + +#ifdef __linux__ +int facemgr_query_bonjour(facemgr_t * facemgr, netdevice_t * netdevice) +{ + interface_t * bj = NULL; + + int rc = bonjour_map_get(&facemgr->bonjour_map, netdevice, &bj); + if (rc < 0) + return rc; + + if (!bj) { + /* Create a new bonjour interface */ + bonjour_cfg_t bj_cfg = { + .netdevice = *netdevice, + }; + rc = facemgr_create_interface(facemgr, NULL, "bonjour", &bj_cfg, &bj); + if (rc < 0) { + ERROR("Error creating 'Bonjour' interface for '%s'\n", netdevice->name); + return -1; + } + } + + DEBUG("sending event to bonjour interface"); + + /* Send an event to the interface (GET ?) */ + return interface_on_event(bj, NULL); +} +#endif /* __linux__ */ + +#ifdef __ANDROID__ +int facemgr_query_android_utility(facemgr_t * facemgr, netdevice_t netdevice) +{ + /* Send an event to the interface */ + facelet_t * facelet = facelet_create(); + if (!facelet) + goto ERR_MALLOC; + + int rc = facelet_set_netdevice(facelet, netdevice); + if (rc < 0) + goto ERR_ND; + + rc = interface_on_event(facemgr->au, facelet); + if (rc < 0) + goto ERR_EVENT; + + return 0; + +ERR_EVENT: +ERR_ND: + facelet_free(facelet); +ERR_MALLOC: + return -1; +} +#endif /* __ANDROID__ */ + + +/** + * \brief Performs a cache lookup to find matching facelets + * \param [in] facelet_cache - Facelet cache on which to perform lookup + * \param [in] facelet - Facelet to lookup + * \param [out] cached_facelet - Pointer used to return a newly allocated + * facelet array corresponding to the result of the cache lookup. + * \return The number of entries in the array in case of success (positive + * value), or -1 in case of error. + */ +int +facelet_cache_lookup(const facelet_cache_t * facelet_cache, facelet_t * facelet, + facelet_t ***cached_facelets) +{ + /* + * If the facelet is uniquely identified by its key, it is used to perform + * an efficient lookup directly... + */ + if (facelet_has_key(facelet)) { + facelet_t * found = NULL; + if (facelet_cache_get(facelet_cache, facelet, &found) < 0) { + ERROR("[facelet_cache_lookup] Error during cache lookup"); + return -1; + } + if (!found) + return 0; + *cached_facelets = malloc(sizeof(facelet_t*)); + *cached_facelets[0] = found; + return 1; + } + + /* ...otherwise, we iterate over the facelet + * cache to find matching elements. + */ + facelet_t ** facelet_array; + int n = facelet_cache_get_array(facelet_cache, &facelet_array); + if (n < 0) { + ERROR("[facelet_cache_lookup] Error during cache match"); + return -1; + } + *cached_facelets = malloc(n * sizeof(facelet_t*)); + + DEBUG("cache match n = %d", n); + + int num_match = 0; + for (unsigned i = 0; i < n; i++) { + char buf[128]; + facelet_snprintf(buf, 128, facelet_array[i]); + DEBUG("- facelet_array[%d] %s", i, buf); + facelet_snprintf(buf, 128, facelet); + DEBUG(" facelet %s", buf); + + DEBUG("match ?"); + if (!facelet_match(facelet_array[i], facelet)) { + DEBUG("no match"); + continue; + } + DEBUG("match!"); + (*cached_facelets)[num_match++] = facelet_array[i]; + } + free(facelet_array); + DEBUG("return nummatch=%d", num_match); + return num_match; +} + + +/** + * \brief Checks whether the facelet satisfies face creation rules + * \param [in] facemgr - Pointer to the face manager instance + * \param [in] facelet - Pointer to the facelet to process + * \return 0 in case of success, -2 if we don't have enough information to + * decide, -3 if the face does not satisfy rules, and -1 in case of error + */ +int +facemgr_facelet_satisfy_rules(facemgr_t * facemgr, facelet_t * facelet) +{ + /* As key, netdevice and family should always be present */ + netdevice_t netdevice = NETDEVICE_EMPTY; + int rc = facelet_get_netdevice(facelet, &netdevice); + if (rc < 0) { + ERROR("[facemgr_facelet_satisfy_rules] Error retrieving netdevice from facelet"); + return -1; + } + + int family = AF_UNSPEC; + if (facelet_has_family(facelet)) { + if (facelet_get_family(facelet, &family) < 0) { + ERROR("[facemgr_facelet_satisfy_rules] Error retrieving family from facelet"); + return -1; + } + } + + netdevice_type_t netdevice_type = NETDEVICE_TYPE_UNDEFINED; +#ifdef __ANDROID__ + /* + * In addition to netdevice, netdevice_type should be present to correctly + * apply rules + */ + rc = facelet_get_netdevice_type(facelet, &netdevice_type); + if (rc < 0) { + ERROR("[facemgr_facelet_satisfy_rules] Error retrieving netdevice_type from facelet"); + return -2; + } +#endif /* __ANDROID__ */ + + /* Ignore */ + bool ignore; + if (facemgr_cfg_get_ignore(facemgr->cfg, &netdevice, netdevice_type, + &ignore) < 0) + return -1; + if (ignore) { + DEBUG("Ignored interface '%s/%s'...", netdevice.name, + netdevice_type_str[netdevice_type]); + return -3; + } + + /* IPv4 */ + bool ipv4; + if (facemgr_cfg_get_ipv4(facemgr->cfg, &netdevice, netdevice_type, + &ipv4) < 0) + return -1; + if (!ipv4) { + DEBUG("Ignored IPv4..."); + return -3; + } + + /* IPv6 */ + bool ipv6; + if (facemgr_cfg_get_ipv6(facemgr->cfg, &netdevice, netdevice_type, + &ipv6) < 0) + return -1; + if (!ipv6) { + DEBUG("Ignored IPv6..."); + return -3; + } + + return 0; +} + +#ifdef __ANDROID__ +/** + * \brief Complements facelet information through Android Utility interface + * \return 0 if request was successful, -1 in case of error, and -2 if the + * interface is not applicable + * + * This function returnds _after_ completion. + */ +int +facemgr_complement_facelet_au(facemgr_t * facemgr, facelet_t * facelet) +{ + + if (facelet_has_netdevice_type(facelet)) + return -2; + + if (facelet_is_au_done(facelet)) + return -2; + + netdevice_t netdevice = NETDEVICE_EMPTY; + int rc = facelet_get_netdevice(facelet, &netdevice); + if (rc < 0) { + ERROR("[facemgr_complement_facelet_bj] Error retrieving netdevice from facelet"); + return -1; + } + + DEBUG("Querying android utility..."); + facelet_set_au_done(facelet); + + /* /!\ Synchronous code here /!\ */ + if (facemgr_query_android_utility(facemgr, netdevice) < 0) + return -1; + return 0; +} +#endif /* __ANDROID__ */ + +#ifdef __linux__ +/** + * \brief Complements facelet information through Bonjour interface. + * \return 0 if request was successful, -1 in case of error, and -2 if the + * interface is not applicable + * + * This function returnds _before_ completion as bonjour querying is + * asynchronous. + */ +int +facemgr_complement_facelet_bj(facemgr_t * facemgr, facelet_t * facelet) +{ + netdevice_t netdevice = NETDEVICE_EMPTY; + int rc = facelet_get_netdevice(facelet, &netdevice); + if (rc < 0) { + ERROR("[facemgr_complement_facelet_bj] Error retrieving netdevice from facelet"); + return -1; + } + + netdevice_type_t netdevice_type = NETDEVICE_TYPE_UNDEFINED; +#ifdef __ANDROID__ + /* + * In addition to netdevice, netdevice_type should be present to correctly + * apply rules + */ + rc = facelet_get_netdevice_type(facelet, &netdevice_type); + if (rc < 0) { + ERROR("[facemgr_complement_facelet_bj] Error retrieving netdevice_type from facelet"); + return -2; + } +#endif /* __ANDROID__ */ + + bool discovery; + if (facemgr_cfg_get_discovery(facemgr->cfg, &netdevice, netdevice_type, + &discovery) < 0) + return -2; + + DEBUG("Discovery: %s", discovery ? "ON" : "OFF"); + + if (!discovery) + return -2; + + facemgr_face_type_t face_type = FACEMGR_FACE_TYPE_UNDEFINED; + if (facelet_get_face_type(facelet, &face_type) < 0) { + ERROR("[facemgr_complement_facelet_bj] Error retrieving face type from facelet"); + return -1; + } + + bool discovery_needed = (face_type.layer == FACE_TYPE_LAYER_4) && + ((!facelet_has_remote_addr(facelet)) || (!facelet_has_remote_port(facelet))); + + DEBUG("Discovery needed: %s", discovery ? "ON" : "OFF"); + + if (!discovery_needed) { + return -2; + } + + if (!facelet_has_local_addr(facelet)) { + DEBUG("No discovery possible without local address"); + return -2; + } + + if (facelet_is_bj_done(facelet)) { + DEBUG("Bonjour already queried"); + return -2; + } + + facelet_set_bj_done(facelet); + return facemgr_query_bonjour(facemgr, &netdevice); +} +#endif /* __linux__ */ + +/** + * \brief Complements facelet information through Manual settings. + * \return 0 if request was successful, -1 in case of error, and -2 if the + * interface is not applicable + * + * This function returnds _before_ completion as bonjour querying is + * asynchronous. + */ +int +facemgr_complement_facelet_manual(facemgr_t * facemgr, facelet_t * facelet) +{ + + netdevice_t netdevice = NETDEVICE_EMPTY; + int rc = facelet_get_netdevice(facelet, &netdevice); + if (rc < 0) { + ERROR("[facemgr_complement_facelet_manual] Error retrieving netdevice from facelet"); + return -1; + } + + netdevice_type_t netdevice_type = NETDEVICE_TYPE_UNDEFINED; +#ifdef __ANDROID__ + /* + * In addition to netdevice, netdevice_type should be present to correctly + * apply rules + */ + rc = facelet_get_netdevice_type(facelet, &netdevice_type); + if (rc < 0) { + ERROR("[facemgr_complement_facelet_manual] Error retrieving netdevice_type from facelet"); + return -2; + } +#endif /* __ANDROID__ */ + + int family = AF_UNSPEC; + if (facelet_has_family(facelet)) { + if (facelet_get_family(facelet, &family) < 0) { + ERROR("[facemgr_complement_facelet_manual] Error retrieving family from facelet"); + return -1; + } + } + + /* Do not query manual is there is a change to go through bonjour */ + bool discovery; + if (facemgr_cfg_get_discovery(facemgr->cfg, &netdevice, netdevice_type, + &discovery) < 0) + return -2; + + facemgr_face_type_t face_type = FACEMGR_FACE_TYPE_UNDEFINED; + if (facelet_get_face_type(facelet, &face_type) < 0) { + ERROR("[facemgr_complement_facelet_manual] Error retrieving face type from facelet"); + return -1; + } + + bool discovery_needed = (face_type.layer == FACE_TYPE_LAYER_4) && + ((!facelet_has_remote_addr(facelet)) || (!facelet_has_remote_port(facelet))); + + if (!discovery_needed) { + DEBUG("manual settings not considered as no discovery is needed"); + return -2; + } + + if (discovery && !facelet_is_bj_done(facelet)) { + DEBUG("manual settings not considered as discovery is enabled and Bonjour has not yet been done"); + return -2; + } + + DEBUG("Applying manual settings..."); + /* + * Manual overlay specification (remote addr/port) + * We never override a result we have obtained through bonjour + */ + if (!facelet_has_remote_addr(facelet)) { + ip_address_t remote_addr; + if (facemgr_cfg_get_overlay_remote_addr(facemgr->cfg, + &netdevice, netdevice_type, family, &remote_addr) < 0) { + ERROR("[facemgr_complement_facelet_manual] Error getting remote addr information from cfg"); + return -1; + } + if (ip_address_empty(&remote_addr)) { + ERROR("[facemgr_complement_facelet_manual] Got empty remote addr information from cfg"); + } else { + DEBUG(" - remote address"); + facelet_set_remote_addr(facelet, remote_addr); + } + } + + if (!facelet_has_remote_port(facelet)) { + uint16_t remote_port; + int rc = facemgr_cfg_get_overlay_remote_port(facemgr->cfg, + &netdevice, netdevice_type, family, &remote_port); + if (rc < 0) { + ERROR("[facemgr_complement_facelet_manual] Error getting remote port information from cfg"); + return -1; + } + DEBUG(" - remote port"); + facelet_set_remote_port(facelet, remote_port); + } + + /* + * Complementing local addr/port XXX this should be done somewhere + * else : manual settings have the lowest priority + * + * Local IP address is specific as it allows to override the source + * address just before creating the face... we would need to check + * whether this is an address that belong to us... it might be used + * to arbitrate amongst several IP addresses instead... + */ + ip_address_t local_addr; + if (facemgr_cfg_get_overlay_local_addr(facemgr->cfg, &netdevice, + netdevice_type, family, &local_addr) < 0) { + ERROR("[facemgr_complement_facelet_manual] Error getting local addr information from cfg"); + return -1; + } + if (ip_address_empty(&local_addr)) { + ERROR("[facemgr_complement_facelet_manual] Got empty local addr information from cfg"); + } else { + DEBUG(" - local addres"); + facelet_set_local_addr(facelet, local_addr); + } + + /* Sets a default local port, so far nobody sets it */ + uint16_t local_port; + if (facemgr_cfg_get_overlay_local_port(facemgr->cfg, + &netdevice, netdevice_type, family, &local_port) < 0) { + ERROR("[facemgr_complement_facelet_manual] Error getting local port information from cfg"); + return -1; + } + DEBUG(" - local port"); + facelet_set_local_port(facelet, local_port); + return 0; +} + +int +facemgr_complement_facelet(facemgr_t * facemgr, facelet_t * facelet) +{ + int rc; + + if (!facelet_has_key(facelet)) + return -2; + +#ifdef __ANDROID__ + rc = facemgr_complement_facelet_au(facemgr, facelet); + if (rc != -2) + return rc; +#endif /* __ANDROID__ */ + + /* We continue only if the current call was not applicable. In the current + * setting we have no interface that can be requested in parallel, and no + * need to. This might evolve in future releases. + */ + +#ifdef __linux__ + rc = facemgr_complement_facelet_bj(facemgr, facelet); + if (rc != -2) + return rc; +#endif /* __linux__ */ + + DEBUG("Complement manual"); + + rc = facemgr_complement_facelet_manual(facemgr, facelet); + if (rc != -2) + return rc; + + INFO("[facemgr_complement_facelet] No more interfaces to query... incomplete face"); + return 0; +} + +/** + * \brief Process facelet CREATE event + * \param [in] facemgr - Pointer to the face manager instance + * \param [in] facelet - Pointer to the facelet event to process + * \return 0 if everything went correctly, or -1 in case of error. + */ +int +facemgr_process_create(facemgr_t * facemgr, facelet_t * facelet) +{ + /* + * We create an interface locally, which does not means it should not exist + * remotely. Once such codepath is enabled, the two facelets will have been + * merged and we need to handle an eventual update on our side. + * + * In the same way, we need to check for the equivalence of face types etc. + */ + int rc; + + if (facelet_cache_add(&facemgr->facelet_cache, facelet) < 0) { + ERROR("[facemgr_process_create] Error adding facelet to cache"); + return -1; + } + DEBUG("Facelet added to cache"); + + /* + * If the facelet does not satisfy filters, we do not lose any information + * but do not take any action to complement the face + */ + rc = facemgr_facelet_satisfy_rules(facemgr, facelet); + if (rc == -3) { + /* Does not satisfy rules */ + return 0; + } + + // FIXME: we should complement a part of the facelet, so that we don't + // necessarily keep this information if we get more locally. Or at least we + // should remember that. + if (rc == -2) { + /* + * We don't have equivalent for linux right now, heuristic is only used + * at the end... might change. + */ +#ifdef __ANDROID__ + /* Priority is given to information that complements a face */ + if (facemgr_complement_facelet_au(facemgr, facelet) < 0) { + ERROR("[facemgr_process_create] Error while attempting to complement face for fields required by rule application"); + return -1; + } + return 0; +#endif /* __ANDROID__ */ + } + if (rc < 0) + return -1; + +// netdevice_t netdevice = NETDEVICE_EMPTY; +// if (facelet_get_netdevice(facelet, &netdevice) < 0) { +// ERROR("[facemgr_process_create] Error retrieving netdevice from facelet"); +// return -1; +// } +// +// netdevice_type_t netdevice_type = NETDEVICE_TYPE_UNDEFINED; +//#ifdef __ANDROID__ +// /* +// * In addition to netdevice, netdevice_type should be present to correctly +// * apply rules +// */ +// if (facelet_get_netdevice_type(facelet, &netdevice_type) < 0) { +// ERROR("[facemgr_process_create] Error retrieving netdevice_type from facelet"); +// return -2; +// } +//#endif /* __ANDROID__ */ + + + char facelet_s[MAXSZ_FACELET]; + facelet_snprintf(facelet_s, MAXSZ_FACELET, facelet); + DEBUG("---[ FACELET CREATE : %s ] ---", facelet_s); + + /* Do we have enough information about the facelet ? */ + if (!facelet_validate_face(facelet)) { + if (facemgr_complement_facelet(facemgr, facelet) < 0) { + ERROR("[facemgr_process_create] Error while attempting to complement face for fields required by face creation"); + return -1; + } + // we should not stop after complement_manual but create a face if + // possible... so we add a second validation + } + + if (!facelet_validate_face(facelet)) + return 0; + + /* + * Is the forwarder connected, and has the facelet cache already sync'ed the + * remote faces ? + */ + // TODO + + /* + * Actually create the face on the forwarder + * + * FIXME Currently hicn-light is hardcoded + */ + if (interface_on_event(facemgr->hl, facelet) < 0) + return -1; + facelet_set_status(facelet, FACELET_STATUS_CLEAN); + return 0; +} + +/** + * \brief Process facelet GET event + * \param [in] facemgr - Pointer to the face manager instance + * \param [in] facelet - Pointer to the facelet event to process + * \return 0 if everything went correctly, or -1 in case of error. + */ +int +facemgr_process_get(facemgr_t * facemgr, facelet_t * facelet) +{ + facelet_set_status(facelet, FACELET_STATUS_CLEAN); + if (facelet_has_netdevice(facelet)) { + netdevice_t netdevice; + if (facelet_get_netdevice(facelet, &netdevice) < 0) + return -1; + if (!IS_VALID_NETDEVICE(netdevice)) + return 0; + return facelet_cache_add(&facemgr->facelet_cache, facelet); + } + return 0; +} + +/** + * \brief Process facelet UPDATE event + * \param [in] facemgr - Pointer to the face manager instance + * \param [in] facelet - Pointer to the facelet event to process + * \return 0 if everything went correctly, or -1 in case of error. + */ +int +facemgr_process_update(facemgr_t * facemgr, facelet_t * facelet) +{ + /* This is the most complex operation since we have the same problems as in + * CREATE + the need to manage changes... + * + * This might eventually trigger a face deletion... + */ + + /* + * Update in local does not mean the face should not be created remotely as + * it might be the first time we have enough information to create it + */ + + char facelet_s[MAXSZ_FACELET]; + facelet_snprintf(facelet_s, MAXSZ_FACELET, facelet); + DEBUG("---[ FACELET UPDATE : %s ] ---", facelet_s); + + /* Sets face type */ + if (!facelet_has_face_type(facelet)) { + + /* As key, netdevice and family should always be present */ + netdevice_t netdevice = NETDEVICE_EMPTY; + int rc = facelet_get_netdevice(facelet, &netdevice); + if (rc < 0) { + ERROR("[facemgr_facelet_satisfy_rules] Error retrieving netdevice from facelet"); + return -1; + } + + netdevice_type_t netdevice_type = NETDEVICE_TYPE_UNDEFINED; +#ifdef __ANDROID__ + /* + * In addition to netdevice, netdevice_type should be present to correctly + * apply rules + */ + rc = facelet_get_netdevice_type(facelet, &netdevice_type); + if (rc < 0) { + ERROR("[facemgr_facelet_satisfy_rules] Error retrieving netdevice_type from facelet"); + return -2; + } +#endif /* __ANDROID__ */ + + facemgr_face_type_t face_type = FACEMGR_FACE_TYPE_UNDEFINED; + if (facemgr_cfg_get_face_type(facemgr->cfg, &netdevice, netdevice_type, &face_type) < 0) + return rc; + facelet_set_face_type(facelet, face_type); + } + + /* Process GET/UDPATE... */ + switch(facelet_get_status(facelet)) { + case FACELET_STATUS_UNDEFINED: + ERROR("[facemgr_process_update] Unexpected facelet status"); + return -1; + + case FACELET_STATUS_DELETED: + case FACELET_STATUS_NEW: + /* + * If the remote action should be a CREATE, then we need to check + * whether we have enough information about the face... + */ + if (!facelet_validate_face(facelet)) { + if (facemgr_complement_facelet(facemgr, facelet) < 0) { + ERROR("[facemgr_process_update] Error while attempting to complement face for fields required by face creation"); + return -1; + } + } + if (!facelet_validate_face(facelet)) + return 0; + + facelet_set_event(facelet, FACELET_EVENT_CREATE); + interface_on_event(facemgr->hl, facelet); + + /* This works assuming the call to hicn-light is blocking */ + facelet_set_status(facelet, FACELET_STATUS_CLEAN); + break; + + case FACELET_STATUS_CLEAN: + /* Nothing to do */ + break; + + case FACELET_STATUS_DIRTY: + /* + * For now we assume only local changes, and proceed to try and + * update the hICN forwarder. + * + * In case of update, the face exists which means we should already + * have enough information + */ + if (!facelet_validate_face(facelet)) { + if (facemgr_complement_facelet(facemgr, facelet) < 0) { + ERROR("[facemgr_process_create] Error while attempting to complement face for fields required by face creation"); + return -1; + } + } + + if (!facelet_validate_face(facelet)) + return 0; + + facelet_set_event(facelet, FACELET_EVENT_UPDATE); + if (interface_on_event(facemgr->hl, facelet) < 0) + return -1; + + /* This works assuming the call to hicn-light is blocking and we + * have proceeded to all udpates */ + facelet_set_status(facelet, FACELET_STATUS_CLEAN); + break; + + case FACELET_STATUS_CONFLICT: + ERROR("[facemgr_process_update] Conflict resolution (not) yet implemented"); + return -1; + case FACELET_STATUS_N: + ERROR("[facemgr_process_update] Facelet in error"); + return -1; + } + return 0; +} + +/** + * \brief Process facelet DELETE event + * \param [in] facemgr - Pointer to the face manager instance + * \param [in] facelet - Pointer to the facelet event to process + * \return 0 if everything went correctly, or -1 in case of error. + */ +int +facemgr_process_delete(facemgr_t * facemgr, facelet_t * facelet) +{ + if (interface_on_event(facemgr->hl, facelet) < 0) + return -1; + + facelet_set_status(facelet, FACELET_STATUS_DELETED); + //facelet_set_bj_done(facelet, false); + + return 0; +} + +/** + * \brief Process incoming events from interfaces + * + * Implementation notes: + * - Any event or timeout due to an interface triggers either a local cache + * update, as well a face operations needed to resync the state. + */ +int +facemgr_on_event(facemgr_t * facemgr, facelet_t * facelet_in) +{ + int ret = 0; + assert(facelet_in); + + char facelet_s[MAXSZ_FACELET]; + facelet_snprintf(facelet_s, MAXSZ_FACELET, facelet_in); + DEBUG("----------------------------------"); + DEBUG("EVENT %s\n", facelet_s); + + facelet_t ** cached_facelets = NULL; + int n = facelet_cache_lookup(&facemgr->facelet_cache, facelet_in, &cached_facelets); + if (n < 0) { + ERROR("[facemgr_on_event] Error during cache lookup"); + goto ERR; + } + DEBUG("num matches n=%d", n); + if (n == 0) { + /* This is a new facelet... we expect a CREATE event. */ + switch(facelet_get_event(facelet_in)) { + case FACELET_EVENT_CREATE: + if (facemgr_process_create(facemgr, facelet_in) < 0) { + ERROR("[facemgr_process_cached_facelet] Error processing CREATE event"); + goto ERR; + } + break; + + case FACELET_EVENT_GET: + /* Insert new facelet in cached */ + if (facemgr_process_get(facemgr, facelet_in) < 0) { + ERROR("[facemgr_process_cached_facelet] Error processing GET event"); + goto ERR; + } + break; + + case FACELET_EVENT_UPDATE: + /* Might be because we previously ignored the facelet... */ + //ERROR("[facemgr_on_event] Unexpected UPDATE... face does not exist"); + //goto ERR; + INFO("Ignored UPDATE for non-existing face"); + break; + + case FACELET_EVENT_DELETE: + ERROR("[facemgr_on_event] Unexpected DELETE... face does not exist"); + goto ERR; + + case FACELET_EVENT_UNDEFINED: + ERROR("[facemgr_on_event] Unexpected UNDEFINED event."); + goto ERR; + + default: /* XXX Some events should be deprecated */ + ERROR("[facemgr_on_event] Deprecated event"); + goto ERR; + } + goto DUMP_CACHE; + } + + /* + * From now on, it should not make any difference whether we have one or + * more facelet. + */ + for (unsigned i = 0; i < n; i ++) { + /* + * We merge each cached facelet with incoming one, and perform state + * reconciliation by sending appropriate updates to the forwarder + */ + facelet_t * facelet = cached_facelets[i]; + DEBUG("... match #%d", i); + switch(facelet_get_event(facelet_in)) { + case FACELET_EVENT_CREATE: + // FIXME, this might occur if the facemgr restarts and we try to + // re-create existing faces + ERROR("[facemgr_on_event] CREATE event for a face that already exists..."); + ret = -1; + continue; + + case FACELET_EVENT_GET: /* should be an INFORM message */ + // FIXME, this might occur if the forwarder restarts and we + // resync faces... + ERROR("[facemgr_on_event] GET event for a face that already exists..."); + ret = -1; + continue; + + case FACELET_EVENT_UPDATE: + { + DEBUG("FACELET_EVENT_UPDATE"); + char buf[128]; + facelet_snprintf(buf, 128, facelet_in); + DEBUG("MERGE %s", buf); + facelet_snprintf(buf, 128, facelet); + DEBUG(" ON %s", buf); + } + if (facelet_merge(facelet, facelet_in) < 0) { + ERROR("[facemgr_on_event] Error merging facelets"); + continue; + } + if (facemgr_process_update(facemgr, facelet) < 0) { + ERROR("[facemgr_on_event] Error processing UPDATE event"); + ret = -1; + } + continue; + + case FACELET_EVENT_DELETE: + if (facelet_merge(facelet, facelet_in) < 0) { + ERROR("[facemgr_on_event] Error merging facelets"); + continue; + } + if (facemgr_process_delete(facemgr, facelet) < 0) { + ERROR("[facemgr_on_event] Error processing DELETE event"); + ret = -1; + } + continue; + + default: /* XXX Some events should be deprecated */ + ERROR("[facemgr_on_event] Deprecated event"); + ret = -1; + } + + } + free(cached_facelets); + goto DUMP_CACHE; + +ERR: + ret = -1; + +DUMP_CACHE: + DEBUG(" <CACHE>"); + facelet_cache_dump(&facemgr->facelet_cache); + DEBUG(" </CACHE>"); + DEBUG("</EVENT ret=%d>", ret); + DEBUG("----------------------------------"); + return ret; +} + +int +facemgr_bootstrap(facemgr_t * facemgr) +{ + int rc; + + DEBUG("Registering interfaces..."); + rc = interface_register(&hicn_light_ops); + if (rc < 0) { + ERROR("Could not register interfaces"); + goto ERR_REGISTER; + } + +#ifdef __APPLE__ + rc = interface_register(&network_framework_ops); + if (rc < 0) + goto ERR_REGISTER; +#endif /* __APPLE__ */ + +#ifdef __linux__ + rc = interface_register(&netlink_ops); + if (rc < 0) + goto ERR_REGISTER; + rc = interface_register(&bonjour_ops); + if (rc < 0) + goto ERR_REGISTER; +#endif /* __linux__ */ + +#ifdef __ANDROID__ + rc = interface_register(&android_utility_ops); + if (rc < 0) + goto ERR_REGISTER; +#endif /* __ANDROID__ */ + +#ifdef WITH_EXAMPLE_DUMMY + rc = interface_register(&dummy_ops); + if (rc < 0) + goto ERR_REGISTER; +#endif + +#ifdef WITH_EXAMPLE_UPDOWN + rc = interface_register(&updown_ops); + if (rc < 0) + goto ERR_REGISTER; +#endif + + rc = facemgr_create_interface(facemgr, "hl", "hicn_light", NULL, &facemgr->hl); + if (rc < 0) { + ERROR("Error creating 'hICN forwarder (hicn-light)' interface\n"); + goto ERR_HL_CREATE; + } + +#ifdef __APPLE__ + network_framework_cfg_t nf_cfg = { + .rules = &facemgr->rules, + }; + rc = facemgr_create_interface(facemgr, "nf", "network_framework", &nf_cfg, &facemgr->nf); + if (rc < 0) { + ERROR("Error creating 'Apple Network Framework' interface\n"); + goto ERR_NF_CREATE; + } +#endif /* __APPLE__ */ + +#ifdef __linux__ + rc = facemgr_create_interface(facemgr, "nl", "netlink", NULL, &facemgr->nl); + if (rc < 0) { + ERROR("Error creating 'Netlink' interface\n"); + goto ERR_NL_CREATE; + } +#endif /* __linux__ */ + +#ifdef __ANDROID__ + android_utility_cfg_t au_cfg = { + .jvm = facemgr->jvm, + }; + rc = facemgr_create_interface(facemgr, "au", "android_utility", &au_cfg, &facemgr->au); + if (rc < 0) { + ERROR("Error creating 'Android Utility' interface\n"); + goto ERR_AU_CREATE; + } +#endif /* __ANDROID__ */ + +#ifdef WITH_EXAMPLE_DUMMY + rc = facemgr_create_interface(facemgr, "dummy0", "dummy", NULL, &facemgr->dummy); + if (rc < 0) { + ERROR("Error creating 'dummy' interface\n"); + goto ERR_DUMMY_CREATE; + } +#endif + +#ifdef WITH_EXAMPLE_UPDOWN + rc = facemgr_create_interface(facemgr, "updown0", "updown", NULL, &facemgr->updown); + if (rc < 0) { + ERROR("Error creating 'updown' interface\n"); + goto ERR_UPDOWN_CREATE; + } +#endif + + DEBUG("Facemgr successfully initialized..."); + + return 0; + + /* FIXME facemgr_delete_interface */ +#ifdef WITH_EXAMPLE_UPDOWN + interface_free(facemgr->updown); +ERR_UPDOWN_CREATE: +#endif +#ifdef WITH_EXAMPLE_DUMMY + interface_free(facemgr->dummy); +ERR_DUMMY_CREATE: +#endif +#ifdef __ANDROID__ + interface_free(facemgr->au); +ERR_AU_CREATE: +#endif /* __ANDROID__ */ +#ifdef __linux__ + interface_free(facemgr->nl); +ERR_NL_CREATE: +#endif /* __linux__ */ +#ifdef __APPLE__ + interface_free(facemgr->nf); +ERR_NF_CREATE: +#endif /* __APPLE__ */ + interface_free(facemgr->hl); +ERR_HL_CREATE: +ERR_REGISTER: + return -1; +} + +void facemgr_stop(facemgr_t * facemgr) +{ + // FIXME we should iterate on interface map + +#ifdef __APPLE__ + facemgr_delete_interface(facemgr, facemgr->nf); +#endif /* __APPLE__ */ + + +#ifdef __linux__ + facemgr_delete_interface(facemgr, facemgr->nl); + + /* Delete all bonjour interfaces */ + interface_t ** bonjour_array;// = NULL; // NOTE: would allow avoiding tests + int n = bonjour_map_get_value_array(&facemgr->bonjour_map, &bonjour_array); + if (n > 0) { + netdevice_t ** netdevice_array; // = NULL; + int m = bonjour_map_get_key_array(&facemgr->bonjour_map, &netdevice_array); + if (m > 0) { + assert(m == n); + for (int i = 0; i < n; i++) { /* Fail silently */ + DEBUG("Deleting bonjour interface associated to %s (%p)\n", + netdevice_array[i]->name, bonjour_array[i]); + facemgr_delete_interface(facemgr, bonjour_array[i]); + } + free(netdevice_array); + } + free(bonjour_array); + } +#endif /* __linux__ */ + +#ifdef __ANDROID__ + facemgr_delete_interface(facemgr, facemgr->au); +#endif /* __ANDROID__ */ + + facemgr_delete_interface(facemgr, facemgr->hl); + +#ifdef WITH_EXAMPLE_DUMMY + facemgr_delete_interface(facemgr, facemgr->dummy); +#endif + +#ifdef WITH_EXAMPLE_UPDOWN + facemgr_delete_interface(facemgr, facemgr->updown); +#endif +} + +#ifdef __ANDROID__ +void facemgr_set_jvm(facemgr_t * facemgr, JavaVM *jvm) +{ + facemgr->jvm = jvm; +} +#endif /* __ANDROID__ */ + +void facemgr_set_event_loop_handler(facemgr_t * facemgr, void * loop, void * loop_register_fd, void * loop_unregister_event) +{ + facemgr->loop = loop; + facemgr->loop_register_fd = loop_register_fd; + facemgr->loop_unregister_event = loop_unregister_event; +} + +void facemgr_list_faces(facemgr_t * facemgr, facemgr_list_faces_cb_t cb, void * user_data) +{ + //face_cache_iter(&facemgr->face_cache, cb, user_data); + facelet_cache_dump(&facemgr->facelet_cache); +} diff --git a/ctrl/facemgr/src/cfg.c b/ctrl/facemgr/src/cfg.c new file mode 100644 index 000000000..6b04208bb --- /dev/null +++ b/ctrl/facemgr/src/cfg.c @@ -0,0 +1,991 @@ +/** + * \file cfg.c + * \brief Implementation of Face manager configuration + */ + +#include <assert.h> +#include <hicn/ctrl.h> // HICN_DEFAULT_PORT +#include <hicn/facemgr/cfg.h> +#include <hicn/policy.h> +#include <hicn/util/ip_address.h> +#include "util/set.h" + +/* Overlay */ + +typedef struct { + bool is_local_port; + uint16_t local_port; + bool is_local_addr; + ip_address_t local_addr; + bool is_remote_port; + uint16_t remote_port; + bool is_remote_addr; + ip_address_t remote_addr; +} facemgr_cfg_overlay_t; + +int +facemgr_cfg_overlay_initialize(facemgr_cfg_overlay_t * overlay) +{ + overlay->is_local_port = false; + overlay->local_port = 0; + overlay->is_local_addr = false; + overlay->local_addr = IP_ADDRESS_EMPTY; + + overlay->is_remote_port = false; + overlay->remote_port = 0; + overlay->is_remote_addr = false; + overlay->remote_addr = IP_ADDRESS_EMPTY; + + return 0; +} + +int +facemgr_cfg_overlay_finalize(facemgr_cfg_overlay_t * overlay) +{ + return 0; +} + +facemgr_cfg_overlay_t * facemgr_cfg_overlay_create() +{ + facemgr_cfg_overlay_t * overlay = malloc(sizeof(facemgr_cfg_overlay_t)); + if (!overlay) + return NULL; + + int rc = facemgr_cfg_overlay_initialize(overlay); + if (rc < 0) { + free(overlay); + return NULL; + } + + return overlay; +} + +void facemgr_cfg_overlay_free(facemgr_cfg_overlay_t * overlay) +{ + facemgr_cfg_overlay_finalize(overlay); + free(overlay); +} + +typedef struct { + facemgr_cfg_overlay_t * v4; + facemgr_cfg_overlay_t * v6; +} facemgr_cfg_overlays_t; + +typedef struct { + const char * interface_name; + netdevice_type_t interface_type; +} facemgr_cfg_match_t; + + +typedef struct { + /* Interface specific */ + bool is_face_type; // default is auto + facemgr_face_type_t face_type; + + /* This should be defaut for the global settings */ + bool is_ignore; + bool ignore; + bool is_discovery; + bool discovery; + bool is_ipv4; + bool ipv4; + bool is_ipv6; + bool ipv6; + + facemgr_cfg_overlays_t overlays; // fallback unless discovery is disabled +} facemgr_cfg_override_t; + +struct facemgr_cfg_rule_s { + facemgr_cfg_match_t match; + facemgr_cfg_override_t override; +}; + +int +facemgr_cfg_override_initialize(facemgr_cfg_override_t * override) +{ + override->is_face_type = false; + override->face_type = FACEMGR_FACE_TYPE_UNDEFINED; + + override->is_ignore = false; + override->ignore = false; + + override->is_discovery = false; + override->discovery = false; + + override->overlays.v4 = NULL; + override->overlays.v6 = NULL; + + return 0; +} + +int +facemgr_cfg_override_finalize(facemgr_cfg_override_t * override) +{ + if (override->overlays.v4) { + facemgr_cfg_overlay_free(override->overlays.v4); + override->overlays.v4 = NULL; + } + if (override->overlays.v6) { + facemgr_cfg_overlay_free(override->overlays.v6); + override->overlays.v6 = NULL; + } + + return 0; +} + + +/* Rule */ +facemgr_cfg_rule_t * facemgr_cfg_rule_create() +{ + facemgr_cfg_rule_t * rule = malloc(sizeof(facemgr_cfg_rule_t)); + if (!rule) + return NULL; + + int rc = facemgr_cfg_rule_initialize(rule); + if (rc < 0) + return NULL; + + return rule; +} + +void facemgr_cfg_rule_free(facemgr_cfg_rule_t * rule) +{ + facemgr_cfg_rule_finalize(rule); + free(rule); +} + +int +facemgr_cfg_rule_initialize(facemgr_cfg_rule_t * rule) +{ + rule->match.interface_name = NULL; + rule->match.interface_type = NETDEVICE_TYPE_UNDEFINED; + + int rc = facemgr_cfg_override_initialize(&rule->override); + if (rc < 0) + return -1; + + return 0; +} + +int +facemgr_cfg_rule_finalize(facemgr_cfg_rule_t * rule) +{ + if (rule->match.interface_name) { + free((void*)rule->match.interface_name); + rule->match.interface_name = NULL; + } + return facemgr_cfg_override_finalize(&rule->override); +} + +void +facemgr_cfg_rule_dump(facemgr_cfg_rule_t * rule) +{ + DEBUG(" <rule>"); + DEBUG(" <match interface_name=%s interface_type=%s>", + rule->match.interface_name, + netdevice_type_str[rule->match.interface_type]); + DEBUG(" <override>"); + if (rule->override.is_face_type) { + DEBUG(" <face_type>%d</face_type>", rule->override.face_type); + } + if (rule->override.is_ignore) { + DEBUG(" <ignore>%d</ignore>", rule->override.ignore); + } + if (rule->override.is_discovery) { + DEBUG(" <discovery>%d</discovery>", rule->override.discovery); + } + if (rule->override.is_ipv4) { + DEBUG(" <ipv4>%d</ipv4>", rule->override.ipv4); + } + if (rule->override.is_ipv6) { + DEBUG(" <ipv6>%d</ipv6>", rule->override.ipv6); + } + DEBUG(" <overlays>"); + if (rule->override.overlays.v4) { + DEBUG(" <ipv4>"); + if (rule->override.overlays.v4->is_local_addr) { + char buf[MAXSZ_IP_ADDRESS]; + ip_address_snprintf(buf, MAXSZ_IP_ADDRESS, + &rule->override.overlays.v4->local_addr, AF_INET); + DEBUG(" <local_addr>%s</local_addr>", buf); + } + if (rule->override.overlays.v4->is_local_port) { + DEBUG(" <local_port>%d</local_port>", + rule->override.overlays.v4->local_port); + } + if (rule->override.overlays.v4->is_remote_addr) { + char buf[MAXSZ_IP_ADDRESS]; + ip_address_snprintf(buf, MAXSZ_IP_ADDRESS, + &rule->override.overlays.v4->remote_addr, AF_INET); + DEBUG(" <remote_addr>%s</remote_addr>", buf); + } + if (rule->override.overlays.v4->is_remote_port) { + DEBUG(" <remote_port>%d</remote_port>", + rule->override.overlays.v4->remote_port); + } + DEBUG(" </ipv4>"); + } + if (rule->override.overlays.v6) { + DEBUG(" <ipv6>"); + if (rule->override.overlays.v6->is_local_addr) { + char buf[MAXSZ_IP_ADDRESS]; + ip_address_snprintf(buf, MAXSZ_IP_ADDRESS, + &rule->override.overlays.v6->local_addr, AF_INET6); + DEBUG(" <local_addr>%s</local_addr>", buf); + } + if (rule->override.overlays.v6->is_local_port) { + DEBUG(" <local_port>%d</local_port>", + rule->override.overlays.v6->local_port); + } + if (rule->override.overlays.v6->is_remote_addr) { + char buf[MAXSZ_IP_ADDRESS]; + ip_address_snprintf(buf, MAXSZ_IP_ADDRESS, + &rule->override.overlays.v6->remote_addr, AF_INET6); + DEBUG(" <remote_addr>%s</remote_addr>", buf); + } + if (rule->override.overlays.v6->is_remote_port) { + DEBUG(" <remote_port>%d</remote_port>", + rule->override.overlays.v6->remote_port); + } + DEBUG(" </ipv6>"); + } + DEBUG(" </overlays>"); + DEBUG(" </override>"); + DEBUG(" </rule>"); +} + +int +facemgr_cfg_rule_set_match(facemgr_cfg_rule_t * rule, const char * interface_name, + netdevice_type_t interface_type) +{ + rule->match.interface_name = interface_name ? strdup(interface_name) : NULL; + rule->match.interface_type = interface_type; + return 0; +} + +int +facemgr_cfg_rule_set_face_type(facemgr_cfg_rule_t * rule, facemgr_face_type_t * face_type) +{ + rule->override.is_face_type = true; + rule->override.face_type = *face_type; + return 0; +} + +int +facemgr_cfg_rule_unset_face_type(facemgr_cfg_rule_t * rule) +{ + rule->override.is_face_type = false; + rule->override.face_type = FACEMGR_FACE_TYPE_UNDEFINED; /* optional */ + return 0; +} + +int +facemgr_cfg_rule_set_discovery(facemgr_cfg_rule_t * rule, bool status) +{ + rule->override.is_discovery = true; + rule->override.discovery = status; + return 0; +} + +int +facemgr_cfg_rule_unset_discovery(facemgr_cfg_rule_t * rule) +{ + rule->override.is_discovery = false; + return 0; +} + +int +facemgr_cfg_rule_set_ignore(facemgr_cfg_rule_t * rule, bool status) +{ + rule->override.is_ignore = true; + rule->override.ignore = status; + return 0; +} + +int +facemgr_cfg_rule_unset_ignore(facemgr_cfg_rule_t * rule) +{ + rule->override.is_ignore = false; + return 0; +} + +int +facemgr_cfg_rule_set_ipv4(facemgr_cfg_rule_t * rule, bool status) +{ + rule->override.is_ipv4 = true; + rule->override.ipv4 = status; + return 0; +} + +int +facemgr_cfg_rule_unset_ipv4(facemgr_cfg_rule_t * rule) +{ + rule->override.is_ipv4 = false; + return 0; +} + +int +facemgr_cfg_rule_set_ipv6(facemgr_cfg_rule_t * rule, bool status) +{ + rule->override.is_ipv6 = true; + rule->override.ipv6 = status; + return 0; +} + +int +facemgr_cfg_rule_unset_ipv6(facemgr_cfg_rule_t * rule) +{ + rule->override.is_ipv6 = false; + return 0; +} + +int +facemgr_cfg_rule_set_overlay(facemgr_cfg_rule_t * rule, int family, + ip_address_t * local_addr, uint16_t local_port, + ip_address_t * remote_addr, uint16_t remote_port) { + if ((family != AF_INET) && (family != AF_INET6)) + return -1; + + facemgr_cfg_overlay_t * overlay = facemgr_cfg_overlay_create(); + if (local_addr) { + overlay->is_local_addr = true; + overlay->local_addr = *local_addr; + } + if (IS_VALID_PORT(local_port)) { + overlay->is_local_port = true; + overlay->local_port = local_port; + } + if (remote_addr) { + overlay->is_remote_addr = true; + overlay->remote_addr = *remote_addr; + } + if (IS_VALID_PORT(remote_port)) { + overlay->is_remote_port = true; + overlay->remote_port = remote_port; + } + + switch(family) { + case AF_INET: + rule->override.overlays.v4 = overlay; + break; + + case AF_INET6: + rule->override.overlays.v6 = overlay; + break; + + default: + return -1; + } + + return 0; +} + +int +facemgr_rule_unset_overlay(facemgr_cfg_rule_t * rule, int family) +{ + if ((family != AF_INET) && (family != AF_INET6) && (family != AF_UNSPEC)) + return -1; + + if ((family == AF_UNSPEC) || (family == AF_INET)) { + if (rule->override.overlays.v4) { + facemgr_cfg_overlay_free(rule->override.overlays.v4); + rule->override.overlays.v4 = NULL; + } + } + if ((family == AF_UNSPEC) || (family == AF_INET6)) { + if (rule->override.overlays.v6) { + facemgr_cfg_overlay_free(rule->override.overlays.v6); + rule->override.overlays.v6 = NULL; + } + } + return 0; +} + +int +facemgr_cfg_rule_cmp(const facemgr_cfg_rule_t * r1, const facemgr_cfg_rule_t * r2) +{ + /* + * We implement a lexicographic order on the tuple (interface_name, + * interface_type) + */ + + /* We need to handle NULL cases out of strcmp */ + if (!r1->match.interface_name) { + if (r2->match.interface_name) + return 1; + else + goto BOTH_NULL; + } else { + if (!r2->match.interface_name) + return -1; + } + + + /* Only if both are non-NULL, we proceed to strcmp */ + int rc = strcmp(r1->match.interface_name, r2->match.interface_name); + if (rc != 0) + return rc; + +BOTH_NULL: + return r1->match.interface_type - r2->match.interface_type; +} + +/* General */ + +TYPEDEF_SET_H(facemgr_cfg_rule_set, facemgr_cfg_rule_t *); +TYPEDEF_SET(facemgr_cfg_rule_set, facemgr_cfg_rule_t *, facemgr_cfg_rule_cmp, generic_snprintf); + +struct facemgr_cfg_s { + facemgr_cfg_override_t global; + facemgr_cfg_rule_set_t * rule_set; + //log_cfg_t log; +}; + +facemgr_cfg_t * facemgr_cfg_create() +{ + facemgr_cfg_t * cfg = malloc(sizeof(facemgr_cfg_t)); + if (!cfg) + return NULL; + + int rc = facemgr_cfg_initialize(cfg); + if (rc < 0) { + free(cfg); + return NULL; + } + + return cfg; +} + +void facemgr_cfg_free(facemgr_cfg_t * cfg) +{ + facemgr_cfg_finalize(cfg); + free(cfg); +} + +int +facemgr_cfg_initialize(facemgr_cfg_t * cfg) +{ + int rc = facemgr_cfg_override_initialize(&cfg->global); + if (rc < 0) + goto ERR_OVERRIDE; + + cfg->rule_set = facemgr_cfg_rule_set_create(); + if (!cfg->rule_set) + goto ERR_RULE_SET; + + return 0; + +ERR_RULE_SET: + facemgr_cfg_override_finalize(&cfg->global); +ERR_OVERRIDE: + return -1; +} + +int +facemgr_cfg_finalize(facemgr_cfg_t * cfg) +{ + /* TODO Free all rules */ + facemgr_cfg_rule_set_free(cfg->rule_set); + return facemgr_cfg_override_finalize(&cfg->global); +} + +void facemgr_cfg_dump(facemgr_cfg_t * cfg) +{ + return; /* NOT IMPLEMENTED */ +} + +/* General */ +int +facemgr_cfg_set_face_type(facemgr_cfg_t * cfg, facemgr_face_type_t * face_type) +{ + cfg->global.is_face_type = true; + cfg->global.face_type = *face_type; + return 0; +} + +int +facemgr_cfg_unset_face_type(facemgr_cfg_t * cfg) +{ + cfg->global.is_face_type = false; + cfg->global.face_type = FACEMGR_FACE_TYPE_UNDEFINED; /* optional */ + return 0; +} + +int +facemgr_cfg_set_discovery(facemgr_cfg_t * cfg, bool status) +{ + cfg->global.is_discovery = true; + cfg->global.discovery = status; + return 0; +} + +int +facemgr_cfg_unset_discovery(facemgr_cfg_t * cfg) +{ + cfg->global.is_discovery = false; + return 0; +} + +int +facemgr_cfg_set_overlay(facemgr_cfg_t * cfg, int family, + ip_address_t * local_addr, uint16_t local_port, + ip_address_t * remote_addr, uint16_t remote_port) +{ + if ((family != AF_INET) && (family != AF_INET6)) + return -1; + + facemgr_cfg_overlay_t * overlay = facemgr_cfg_overlay_create(); + if (local_addr) { + overlay->is_local_addr = true; + overlay->local_addr = *local_addr; + } + if (IS_VALID_PORT(local_port)) { + overlay->is_local_port = true; + overlay->local_port = local_port; + } + if (remote_addr) { + overlay->is_remote_addr = true; + overlay->remote_addr = *remote_addr; + } + if (IS_VALID_PORT(remote_port)) { + overlay->is_remote_port = true; + overlay->remote_port = remote_port; + } + + DEBUG("facemgr_cfg_set_overlay"); + + switch(family) { + case AF_INET: + cfg->global.overlays.v4 = overlay; + break; + + case AF_INET6: + cfg->global.overlays.v6 = overlay; + break; + + default: + return -1; + } + + DEBUG("<global>"); + DEBUG(" <overlay>"); + if (overlay) { + DEBUG(" <ipv4>"); + if (overlay->is_local_addr) { + char buf[MAXSZ_IP_ADDRESS]; + ip_address_snprintf(buf, MAXSZ_IP_ADDRESS, + &overlay->local_addr, AF_INET); + DEBUG(" <local_addr>%s</local_addr>", buf); + } + if (overlay->is_local_port) { + DEBUG(" <local_port>%d</local_port>", + overlay->local_port); + } + if (overlay->is_remote_addr) { + char buf[MAXSZ_IP_ADDRESS]; + ip_address_snprintf(buf, MAXSZ_IP_ADDRESS, + &overlay->remote_addr, AF_INET); + DEBUG(" <remote_addr>%s</remote_addr>", buf); + } + if (overlay->is_remote_port) { + DEBUG(" <remote_port>%d</remote_port>", + overlay->remote_port); + } + DEBUG(" </ipv4>"); + } + DEBUG(" </overlay>"); + DEBUG("</global>"); + + return 0; +} + +int +facemgr_cfg_unset_overlay(facemgr_cfg_t * cfg, int family) +{ + if ((family != AF_INET) && (family != AF_INET6) && (family != AF_UNSPEC)) + return -1; + + if ((family == AF_UNSPEC) || (family == AF_INET)) { + if (cfg->global.overlays.v4) { + facemgr_cfg_overlay_free(cfg->global.overlays.v4); + cfg->global.overlays.v4 = NULL; + } + } + if ((family == AF_UNSPEC) || (family == AF_INET6)) { + if (cfg->global.overlays.v6) { + facemgr_cfg_overlay_free(cfg->global.overlays.v6); + cfg->global.overlays.v6 = NULL; + } + } + return 0; +} + +int +facemgr_cfg_add_rule(facemgr_cfg_t * cfg, facemgr_cfg_rule_t * rule) +{ + facemgr_cfg_rule_dump(rule); + return facemgr_cfg_rule_set_add(cfg->rule_set, rule); +} + +int +facemgr_cfg_del_rule(facemgr_cfg_t * cfg, facemgr_cfg_rule_t * rule) +{ + return facemgr_cfg_rule_set_remove(cfg->rule_set, rule, NULL); +} + +int facemgr_cfg_get_rule(const facemgr_cfg_t * cfg, const char * interface_name, + netdevice_type_t interface_type, facemgr_cfg_rule_t ** rule) { + facemgr_cfg_rule_t rule_search = { + .match = { + .interface_name = interface_name, + .interface_type = interface_type, + }, + }; + return facemgr_cfg_rule_set_get(cfg->rule_set, &rule_search, rule); +} + +/* Query API */ + +/* + * Check whether there are override rules for the given netdevice + * + * TODO: + * - until we have proper indexes we loop through the whole structure + */ +int +facemgr_cfg_get_override(const facemgr_cfg_t * cfg, + const netdevice_t * netdevice, netdevice_type_t netdevice_type, + facemgr_cfg_override_t ** override) +{ + if (!netdevice) { + *override = NULL; + return 0; + } + + facemgr_cfg_rule_t **rule_array; + int rc = facemgr_cfg_rule_set_get_array(cfg->rule_set, &rule_array); + if (rc < 0) { + ERROR("facemgr_cfg_rule_set_get_array failed"); + return rc; + } + for (unsigned i = 0; i < rc; i++) { + const char * interface_name = rule_array[i]->match.interface_name; + /* Check match for interface name */ + if (interface_name && (strcmp(interface_name, netdevice->name) != 0)) + continue; + /* Check match for interface type */ + if (rule_array[i]->match.interface_type != NETDEVICE_TYPE_UNDEFINED) { +#ifdef __ANDROID__ + if (netdevice_type != rule_array[i]->match.interface_type) + continue; +#else + ERROR("Match on interface type is currently not implemented"); + goto ERR_ARRAY; +#endif /* __ANDROID__ */ + } + /* Found match... do we have an override for face_type */ + DEBUG("override found nd=%s, ndt=%s", rule_array[i]->match.interface_name, netdevice_type_str[rule_array[i]->match.interface_type]); + *override = &rule_array[i]->override; + goto FOUND; + } + + DEBUG("override not found"); + *override = NULL; + +FOUND: + free(rule_array); + return 0; + +#ifndef __ANDROID__ +ERR_ARRAY: + free(rule_array); + return -1; +#endif /* __ANDROID__ */ +} + +int +facemgr_cfg_get_face_type(const facemgr_cfg_t * cfg, + const netdevice_t * netdevice, netdevice_type_t netdevice_type, + facemgr_face_type_t * face_type) +{ + facemgr_cfg_override_t * override; + int rc = facemgr_cfg_get_override(cfg, netdevice, netdevice_type, + &override); + if (rc < 0) { + ERROR("get override failed"); + return rc; + } + + if ((override) && (override->is_face_type)) { + *face_type = override->face_type; + return 0; + } + + *face_type = cfg->global.is_face_type + ? cfg->global.face_type + : FACEMGR_FACE_TYPE_DEFAULT; + + return 0; +} + +int +facemgr_cfg_get_discovery(const facemgr_cfg_t * cfg, + const netdevice_t * netdevice, netdevice_type_t netdevice_type, + bool * discovery) +{ + facemgr_cfg_override_t * override; + int rc = facemgr_cfg_get_override(cfg, netdevice, netdevice_type, + &override); + if (rc < 0) + return rc; + + if ((override) && (override->is_discovery)) { + *discovery = override->discovery; + return 0; + } + + *discovery = cfg->global.is_discovery + ? cfg->global.discovery + : FACEMGR_CFG_DEFAULT_DISCOVERY; + return 0; +} + +int +facemgr_cfg_get_ipv4(const facemgr_cfg_t * cfg, + const netdevice_t * netdevice, netdevice_type_t netdevice_type, + bool * ipv4) +{ + facemgr_cfg_override_t * override; + int rc = facemgr_cfg_get_override(cfg, netdevice, netdevice_type, + &override); + if (rc < 0) + return rc; + + if ((override) && (override->is_ipv4)) { + *ipv4 = override->ipv4; + return 0; + } + + *ipv4 = cfg->global.is_ipv4 + ? cfg->global.ipv4 + : FACEMGR_CFG_DEFAULT_IPV4; + return 0; +} + +int +facemgr_cfg_get_ipv6(const facemgr_cfg_t * cfg, + const netdevice_t * netdevice, netdevice_type_t netdevice_type, + bool * ipv6) +{ + facemgr_cfg_override_t * override; + int rc = facemgr_cfg_get_override(cfg, netdevice, netdevice_type, + &override); + if (rc < 0) + return rc; + + if ((override) && (override->is_ipv6)) { + *ipv6 = override->ipv6; + return 0; + } + + *ipv6 = cfg->global.is_ipv6 + ? cfg->global.ipv6 + : FACEMGR_CFG_DEFAULT_IPV6; + return 0; +} + +int +facemgr_cfg_get_ignore(const facemgr_cfg_t * cfg, + const netdevice_t * netdevice, netdevice_type_t netdevice_type, + bool * ignore) +{ + facemgr_cfg_override_t * override; + int rc = facemgr_cfg_get_override(cfg, netdevice, netdevice_type, + &override); + if (rc < 0) + return rc; + + if ((override) && (override->is_ignore)) { + *ignore = override->ignore; + return 0; + } + + assert (!cfg->global.is_ignore); + + *ignore = (netdevice && (netdevice->name[0] != '\0') && strcmp(netdevice->name, "lo") == 0); + + return 0; +} + +int +facemgr_cfg_get_overlay_local_addr(const facemgr_cfg_t * cfg, + const netdevice_t * netdevice, netdevice_type_t netdevice_type, + int family, ip_address_t * addr) +{ + facemgr_cfg_override_t * override; + int rc = facemgr_cfg_get_override(cfg, netdevice, netdevice_type, + &override); + if (rc < 0) + return rc; + + switch (family) { + case AF_INET: + if ((override) && (override->overlays.v4) && (override->overlays.v4->is_local_addr)) { + *addr = override->overlays.v4->local_addr; + return 0; + } + if ((cfg->global.overlays.v4) && (cfg->global.overlays.v4->is_local_addr)) { + *addr = cfg->global.overlays.v4->local_addr; + return 0; + } + break; + case AF_INET6: + if ((override) && (override->overlays.v6) && (override->overlays.v6->is_local_addr)) { + *addr = override->overlays.v6->local_addr; + return 0; + } + if ((cfg->global.overlays.v6) && (cfg->global.overlays.v6->is_local_addr)) { + *addr = cfg->global.overlays.v6->local_addr; + return 0; + } + break; + case AF_UNSPEC: + break; + default: + return -1; + } + + *addr = IP_ADDRESS_EMPTY; + return 0; +} + +int +facemgr_cfg_get_overlay_local_port(const facemgr_cfg_t * cfg, + const netdevice_t * netdevice, netdevice_type_t netdevice_type, + int family, u16 * port) +{ + facemgr_cfg_override_t * override; + int rc = facemgr_cfg_get_override(cfg, netdevice, netdevice_type, + &override); + if (rc < 0) + return rc; + + switch (family) { + case AF_INET: + if ((override) && (override->overlays.v4) && (override->overlays.v4->is_local_port)) { + *port = override->overlays.v4->local_port; + return 0; + } + if ((cfg->global.overlays.v4) && (cfg->global.overlays.v4->is_local_port)) { + *port = cfg->global.overlays.v4->local_port; + return 0; + } + break; + case AF_INET6: + if ((override) && (override->overlays.v6) && (override->overlays.v6->is_local_port)) { + *port = override->overlays.v6->local_port; + return 0; + } + if ((cfg->global.overlays.v6) && (cfg->global.overlays.v6->is_local_port)) { + *port = cfg->global.overlays.v6->local_port; + return 0; + } + break; + case AF_UNSPEC: + break; + default: + return -1; + } + + *port = HICN_DEFAULT_PORT; + return 0; +} + +int +facemgr_cfg_get_overlay_remote_addr(const facemgr_cfg_t * cfg, + const netdevice_t * netdevice, netdevice_type_t netdevice_type, + int family, ip_address_t * addr) +{ + facemgr_cfg_override_t * override; + int rc = facemgr_cfg_get_override(cfg, netdevice, netdevice_type, + &override); + if (rc < 0) + return rc; + + switch (family) { + case AF_INET: + if ((override) && (override->overlays.v4) && (override->overlays.v4->is_remote_addr)) { + DEBUG("remote addr v4 from override"); + *addr = override->overlays.v4->remote_addr; + return 0; + } + if ((cfg->global.overlays.v4) && (cfg->global.overlays.v4->is_remote_addr)) { + DEBUG("remote addr v4 from global"); + *addr = cfg->global.overlays.v4->remote_addr; + return 0; + } + break; + case AF_INET6: + if ((override) && (override->overlays.v6) && (override->overlays.v6->is_remote_addr)) { + DEBUG("remote addr v6 from override"); + *addr = override->overlays.v6->remote_addr; + return 0; + } + if ((cfg->global.overlays.v6) && (cfg->global.overlays.v6->is_remote_addr)) { + DEBUG("remote addr v6 from global"); + *addr = cfg->global.overlays.v6->remote_addr; + return 0; + } + break; + case AF_UNSPEC: + break; + default: + return -1; + } + + DEBUG("remote addr empty"); + *addr = IP_ADDRESS_EMPTY; + return 0; +} + +int +facemgr_cfg_get_overlay_remote_port(const facemgr_cfg_t * cfg, + const netdevice_t * netdevice, netdevice_type_t netdevice_type, + int family, u16 * port) +{ + facemgr_cfg_override_t * override; + int rc = facemgr_cfg_get_override(cfg, netdevice, netdevice_type, + &override); + if (rc < 0) + return rc; + + switch (family) { + case AF_INET: + if ((override) && (override->overlays.v4) && (override->overlays.v4->is_remote_port)) { + *port = override->overlays.v4->remote_port; + return 0; + } + if ((cfg->global.overlays.v4) && (cfg->global.overlays.v4->is_remote_port)) { + *port = cfg->global.overlays.v4->remote_port; + return 0; + } + break; + case AF_INET6: + if ((override) && (override->overlays.v6) && (override->overlays.v6->is_remote_port)) { + *port = override->overlays.v6->remote_port; + return 0; + } + if ((cfg->global.overlays.v6) && (cfg->global.overlays.v6->is_remote_port)) { + *port = cfg->global.overlays.v6->remote_port; + return 0; + } + break; + case AF_UNSPEC: + break; + default: + return -1; + } + + *port = HICN_DEFAULT_PORT; + return 0; +} diff --git a/ctrl/facemgr/src/common.h b/ctrl/facemgr/src/common.h index a73964b6d..9d6e8ca87 100644 --- a/ctrl/facemgr/src/common.h +++ b/ctrl/facemgr/src/common.h @@ -22,18 +22,12 @@ #include <stdbool.h> #include <stdint.h> +#include <stdlib.h> -#include "util/types.h" -#include "util/ip_address.h" -#include "util/token.h" // XXX debug +#include <hicn/util/ip_address.h> //#define DEBUG -/* Return value conventions */ -#define FACEMGR_SUCCESS 0 -#define FACEMGR_FAILURE -1 -#define FACEMGR_IS_ERROR(rc) (rc < 0) - /* Useful types and macros for comparisons */ typedef int(*cmp_t)(const void * x, const void * y); @@ -43,18 +37,35 @@ typedef int(*cmp_t)(const void * x, const void * y); #define INDENT(n, fmt) "%*s" fmt, n, "" #define printfi(n, fmt, ...) printf(INDENT(n*4, fmt), ##__VA_ARGS__) +#define _unused(x) ((void)(x)) + +/* Random strings */ + +static inline +void rand_str(char *dest, size_t length) { + char charset[] = "0123456789" + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + while (length-- > 0) { + size_t index = (double) rand() / RAND_MAX * (sizeof charset - 1); + *dest++ = charset[index]; + } + *dest = '\0'; +} + /* Boilerplate code */ #define NO_INITIALIZE(NAME) \ int \ NAME ## _initialize(NAME ## _t * obj) { \ - return FACEMGR_SUCCESS; \ + return 0; \ } #define NO_FINALIZE(NAME) \ int \ NAME ## _finalize(NAME ## _t * obj) { \ - return FACEMGR_SUCCESS; \ + return 0; \ } #define AUTOGENERATE_CREATE_FREE(NAME) \ @@ -66,7 +77,7 @@ NAME ## _create() \ if (!obj) \ goto ERR_MALLOC; \ \ - if (FACEMGR_IS_ERROR(NAME ## _initialize(obj))) \ + if (NAME ## _initialize(obj) < 0) \ goto ERR_INIT; \ \ return obj; \ @@ -80,7 +91,7 @@ ERR_MALLOC: \ void \ NAME ## _free(NAME ## _t * obj) \ { \ - if (FACEMGR_IS_ERROR(NAME ## _finalize(obj))) \ + if (NAME ## _finalize(obj) < 0) \ (void)0; /* XXX */ \ free(obj); \ } \ diff --git a/ctrl/facemgr/src/event.c b/ctrl/facemgr/src/event.c deleted file mode 100644 index 446c51c22..000000000 --- a/ctrl/facemgr/src/event.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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 event.h - * \brief Implementatino of face events - */ - -#include "common.h" -#include "event.h" -#include "interface.h" -#include "util/token.h" - -const char * event_type_str[] = { -#define _(x) [EVENT_TYPE_ ## x] = STRINGIZE(x), -foreach_event_type -#undef _ -}; - -int -event_raise(event_type_t type, const face_t * face, const interface_t * interface) -{ - event_t event = { .type = type, .face = face }; - if (interface->callback) - interface->callback(interface->callback_data, &event); - return FACEMGR_SUCCESS; -} diff --git a/ctrl/facemgr/src/event.h b/ctrl/facemgr/src/event.h deleted file mode 100644 index 53295d009..000000000 --- a/ctrl/facemgr/src/event.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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 event.h - * \brief Face event - */ -#ifndef FACEMGR_EVENT_H -#define FACEMGR_EVENT_H - -#include "face.h" -#include "interface.h" - -#define foreach_event_type \ - _(UNDEFINED) \ - _(CREATE) \ - _(UPDATE) \ - _(DELETE) \ - _(SET_PARAMS) \ - _(SET_UP) \ - _(SET_DOWN) \ - _(SET_TAGS) \ - _(CLEAR_TAGS) \ - _(ADD_TAG) \ - _(REMOVE_TAG) \ - _(N) - -#define MAXSZ_EVENT_TYPE_ 10 -#define MAXSZ_EVENT_TYPE MAXSZ_EVENT_TYPE_ + 1 - -typedef enum { -#define _(x) EVENT_TYPE_ ## x, -foreach_event_type -#undef _ -} event_type_t; - -extern const char * event_type_str[]; - -typedef struct event_s { - event_type_t type; - const face_t * face; /* + bitfield for face fields ? */ -} event_t; - -int -event_raise(event_type_t type, const face_t * face, const interface_t * interface); - -#endif /* FACEMGR_EVENT_H */ diff --git a/ctrl/facemgr/src/face.c b/ctrl/facemgr/src/face.c deleted file mode 100644 index 270a6fa9f..000000000 --- a/ctrl/facemgr/src/face.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * 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 face.c - * \brief Implementation of face abstraction - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "face.h" -#include "util/hash.h" -#include "util/token.h" - -#ifndef bzero -#define bzero(b,len) (memset((b), '\0', (len)), (void) 0) -#endif - -#define member_size(type, member) sizeof(((type *)0)->member) - - -/* Netdevice */ - -const char * netdevice_type_str[] = { -#define _(x) [NETDEVICE_TYPE_ ## x] = STRINGIZE(x), -foreach_netdevice_type -#undef _ -}; - - -/* Face state */ - -const char * face_state_str[] = { -#define _(x) [FACE_STATE_ ## x] = STRINGIZE(x), -foreach_face_state -#undef _ -}; - - -/* Face type */ - -const char * face_type_str[] = { -#define _(x) [FACE_TYPE_ ## x] = STRINGIZE(x), -foreach_face_type -#undef _ -}; - - -/* Face */ - -int -face_initialize(face_t * face) -{ - bzero(face, sizeof(face_t)); /* 0'ed for hash */ - return 1; -} - -int -face_initialize_udp(face_t * face, const ip_address_t * local_addr, - u16 local_port, const ip_address_t * remote_addr, u16 remote_port, - int family) -{ - if (!local_addr) - return -1; - - *face = (face_t) { - .type = FACE_TYPE_UDP, - .params.tunnel = { - .family = family, - .local_addr = *local_addr, - .local_port = local_port, - .remote_addr = remote_addr ? *remote_addr : IP_ADDRESS_EMPTY, - .remote_port = remote_port, - }, - }; - return 1; -} - -int -face_initialize_udp_sa(face_t * face, const struct sockaddr * local_addr, - const struct sockaddr * remote_addr) -{ - if (!local_addr) - return -1; - - if (remote_addr && (local_addr->sa_family != remote_addr->sa_family)) - return -1; - - switch (local_addr->sa_family) { - case AF_INET: - { - struct sockaddr_in *lsai = (struct sockaddr_in *)local_addr; - struct sockaddr_in *rsai = (struct sockaddr_in *)remote_addr; - *face = (face_t) { - .type = FACE_TYPE_UDP, - .params.tunnel = { - .family = AF_INET, - .local_addr.v4.as_inaddr = lsai->sin_addr, - .local_port = lsai ? ntohs(lsai->sin_port) : 0, - .remote_addr = IP_ADDRESS_EMPTY, - .remote_port = rsai ? ntohs(rsai->sin_port) : 0, - }, - }; - if (rsai) - face->params.tunnel.remote_addr.v4.as_inaddr = rsai->sin_addr; - } - break; - case AF_INET6: - { - struct sockaddr_in6 *lsai = (struct sockaddr_in6 *)local_addr; - struct sockaddr_in6 *rsai = (struct sockaddr_in6 *)remote_addr; - *face = (face_t) { - .type = FACE_TYPE_UDP, - .params.tunnel = { - .family = AF_INET6, - .local_addr.v6.as_in6addr = lsai->sin6_addr, - .local_port = lsai ? ntohs(lsai->sin6_port) : 0, - .remote_addr = IP_ADDRESS_EMPTY, - .remote_port = rsai ? ntohs(rsai->sin6_port) : 0, - }, - }; - if (rsai) - face->params.tunnel.remote_addr.v6.as_in6addr = rsai->sin6_addr; - } - break; - default: - return -1; - } - return 1; -} - -face_t * face_create() -{ - face_t * face = calloc(1, sizeof(face_t)); /* 0'ed for hash */ - return face; -} - -face_t * face_create_udp(const ip_address_t * local_addr, u16 local_port, - const ip_address_t * remote_addr, u16 remote_port, int family) -{ - face_t * face = face_create(); - if (face_initialize_udp(face, local_addr, local_port, remote_addr, remote_port, family) < 0) - goto ERR_INIT; - return face; - -ERR_INIT: - free(face); - return NULL; -} - -face_t * face_create_udp_sa(const struct sockaddr * local_addr, - const struct sockaddr * remote_addr) -{ - face_t * face = face_create(); - if (face_initialize_udp_sa(face, local_addr, remote_addr) < 0) - goto ERR_INIT; - return face; - -ERR_INIT: - free(face); - return NULL; -} - -void face_free(face_t * face) -{ - free(face); -} - -#define face_param_cmp(f1, f2, face_param_type) \ - memcmp(&f1->type, &f2->type, \ - member_size(face_params_t, face_param_type)); - -/** - * \brief Compare two faces - * \param [in] f1 - First face - * \param [in] f2 - Second face - * \return whether faces are equal, ie both their types are parameters are - * equal. - * - * NOTE: this function implements a partial order. - */ -int -face_cmp(const face_t * f1, const face_t * f2) -{ - if (f1->type != f2->type) - return false; - - switch(f1->type) { - case FACE_TYPE_HICN: - return face_param_cmp(f1, f2, hicn); - case FACE_TYPE_TCP: - case FACE_TYPE_UDP: - return face_param_cmp(f1, f2, tunnel); - default: - return false; - } -} - -hash_t -face_hash(const face_t * face) -{ - /* Assuming the unused part of the struct is set to zero */ - return hash_struct(face); -} - -/* /!\ Please update constants in header file upon changes */ -size_t -face_snprintf(char * s, size_t size, const face_t * face) -{ - switch(face->type) { - case FACE_TYPE_HICN: - return 0; // XXX Not implemented - case FACE_TYPE_TCP: - case FACE_TYPE_UDP: - { - char local[MAXSZ_IP_ADDRESS]; - char remote[MAXSZ_IP_ADDRESS]; - char tags[MAXSZ_POLICY_TAGS]; - - ip_address_snprintf(local, MAXSZ_IP_ADDRESS, - &face->params.tunnel.local_addr, - face->params.tunnel.family); - ip_address_snprintf(remote, MAXSZ_IP_ADDRESS, - &face->params.tunnel.remote_addr, - face->params.tunnel.family); - policy_tags_snprintf(tags, MAXSZ_POLICY_TAGS, face->tags); - - return snprintf(s, size, "%s [%s:%d -> %s:%d] [%s]", - face_type_str[face->type], - local, - face->params.tunnel.local_port, - remote, - face->params.tunnel.remote_port, - tags); - } - break; - default: - return 0; - } - -} - -int -face_set_tags(face_t * face, policy_tags_t tags) -{ - face->tags = tags; - return 1; -} diff --git a/ctrl/facemgr/src/face.h b/ctrl/facemgr/src/face.h deleted file mode 100644 index 8b553f685..000000000 --- a/ctrl/facemgr/src/face.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - * 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 face.h - * \brief Face abstraction - */ -#ifndef HICN_FACE_H -#define HICN_FACE_H - -#ifndef SPACES -#define SPACES(x) x -#endif -#ifndef SPACE -#define SPACE 1 -#endif -#ifndef NULLTERM -#define NULLTERM 1 -#endif - -#include "util/ip_address.h" -#include "util/policy.h" -#include "util/types.h" - - -/* Netdevice type */ - -#include <net/if.h> // IFNAMSIZ - -#define foreach_netdevice_type \ - _(UNDEFINED) \ - _(WIRED) \ - _(WIFI) \ - _(CELLULAR) \ - _(VPN) \ - _(N) - -#define MAXSZ_NETDEVICE_TYPE_ 9 -#define MAXSZ_NETDEVICE_TYPE MAXSZ_NETDEVICE_TYPE_ + NULLTERM - -typedef enum { -#define _(x) NETDEVICE_TYPE_ ## x, -foreach_netdevice_type -#undef _ -} netdevice_type_t; - -extern const char * netdevice_type_str[]; - - -/* Netdevice */ - -typedef struct { - u32 index; - char name[IFNAMSIZ]; -} netdevice_t; - -#define NETDEVICE_UNDEFINED_INDEX 0 - -/* Face state */ - -#define foreach_face_state \ - _(UNDEFINED) \ - _(PENDING_UP) \ - _(UP) \ - _(PENDING_DOWN) \ - _(DOWN) \ - _(ERROR) \ - _(N) - -#define MAXSZ_FACE_STATE_ 12 -#define MAXSZ_FACE_STATE MAXSZ_FACE_STATE_ + 1 - -typedef enum { -#define _(x) FACE_STATE_ ## x, -foreach_face_state -#undef _ -} face_state_t; - -extern const char * face_state_str[]; - - -/* Face type */ - -#define foreach_face_type \ - _(UNDEFINED) \ - _(HICN) \ - _(HICN_LISTENER) \ - _(TCP) \ - _(TCP_LISTENER) \ - _(UDP) \ - _(UDP_LISTENER) \ - _(N) - -#define MAXSZ_FACE_TYPE_ 13 -#define MAXSZ_FACE_TYPE MAXSZ_FACE_TYPE_ + 1 - -typedef enum { -#define _(x) FACE_TYPE_ ## x, -foreach_face_type -#undef _ -} face_type_t; - -extern const char * face_type_str[]; - -#define MAXSZ_FACE_ MAXSZ_FACE_TYPE_ + 2 * MAXSZ_IP_ADDRESS_ + 2 * MAXSZ_PORT_ + 9 + MAXSZ_POLICY_TAGS_ -#define MAXSZ_FACE MAXSZ_FACE_ + 1 - -/* Face */ - -typedef union { - int family; /* To access family independently of face type */ - struct { - int family; - netdevice_t netdevice; - ip_address_t local_addr; - ip_address_t remote_addr; - } hicn; - struct { - int family; - ip_address_t local_addr; - u16 local_port; - ip_address_t remote_addr; - u16 remote_port; - } tunnel; -} face_params_t; - -typedef struct { - face_type_t type; - face_params_t params; - face_state_t admin_state; - face_state_t state; -#ifdef WITH_POLICY - policy_tags_t tags; /**< \see policy_tag_t */ -#endif /* WITH_POLICY */ -} face_t; - -int face_initialize(face_t * face); -int face_initialize_udp(face_t * face, const ip_address_t * local_addr, - u16 local_port, const ip_address_t * remote_addr, u16 remote_port, - int family); -int face_initialize_udp_sa(face_t * face, - const struct sockaddr * local_addr, const struct sockaddr * remote_addr); - -face_t * face_create(); -face_t * face_create_udp(const ip_address_t * local_addr, u16 local_port, - const ip_address_t * remote_addr, u16 remote_port, int family); -face_t * face_create_udp_sa(const struct sockaddr * local_addr, - const struct sockaddr * remote_addr); - -int face_finalize(face_t * face); - -void face_free(face_t * face); - -typedef int (*face_cmp_t)(const face_t * f1, const face_t * f2); - -int face_cmp(const face_t * f1, const face_t * f2); -hash_t face_hash(const face_t * face); - -size_t -face_snprintf(char * s, size_t size, const face_t * face); - -int face_set_tags(face_t * face, policy_tags_t tags); - -#endif /* HICN_FACE_H */ - diff --git a/ctrl/facemgr/src/face_cache.h b/ctrl/facemgr/src/face_cache.h deleted file mode 100644 index caefb7e22..000000000 --- a/ctrl/facemgr/src/face_cache.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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. - */ - -#ifndef FACE_CACHE_H -#define FACE_CACHE_H - -#include "face.h" -#include "util/set.h" - -TYPEDEF_SET_H(face_cache, face_t *); - -#endif /* FACE_CACHE_H */ diff --git a/ctrl/facemgr/src/face_rules.c b/ctrl/facemgr/src/face_rules.c deleted file mode 100644 index ddefc15f9..000000000 --- a/ctrl/facemgr/src/face_rules.c +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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. - */ - -#include "face_rules.h" - -#include <string.h> -#include "util/policy.h" - -TYPEDEF_MAP(face_rules, const char *, policy_tags_t, strcmp, string_snprintf, policy_tags_snprintf); diff --git a/ctrl/facemgr/src/facelet.c b/ctrl/facemgr/src/facelet.c new file mode 100644 index 000000000..8a3074d2a --- /dev/null +++ b/ctrl/facemgr/src/facelet.c @@ -0,0 +1,1008 @@ +/* + * 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 facelet.c + * \brief Implementation of facelet + */ + +#include <assert.h> +#include <stdbool.h> +#include <hicn/ctrl/face.h> +#include <hicn/facemgr/cfg.h> +#include <hicn/util/log.h> + +#include "facelet.h" + +const char * face_type_layer_str[] = { +#define _(x) [FACE_TYPE_LAYER_ ## x] = STRINGIZE(x), + foreach_face_type_layer +#undef _ +}; + +const char * face_type_encap_str[] = { +#define _(x) [FACE_TYPE_ENCAP_ ## x] = STRINGIZE(x), + foreach_face_type_encap +#undef _ +}; + +#define FACEMGR_FACE_TYPE_STR(x) \ + face_type_layer_str[x.layer], face_type_encap_str[x.encap] + + +const char * facelet_status_str[] = { +#define _(x) [FACELET_STATUS_ ## x] = STRINGIZE(x), + foreach_facelet_status +#undef _ +}; + +/* Facelet attribute status */ + + +const char * facelet_attr_status_str[] = { +#define _(x, str) [FACELET_ATTR_STATUS_ ## x] = STRINGIZE(x), + foreach_facelet_attr_status +#undef _ +}; + +const char * facelet_attr_status_str_short[] = { +#define _(x, str) [FACELET_ATTR_STATUS_ ## x] = STRINGIZE(str), + foreach_facelet_attr_status +#undef _ +}; + + +/* Facelet */ + +struct facelet_s { +#define _(TYPE, NAME) TYPE NAME; + foreach_facelet_attr +#undef _ +#define _(TYPE, NAME) facelet_attr_status_t NAME ## _status; + foreach_facelet_attr +#undef _ + + facelet_status_t status; + facelet_event_t event; + + /* Joins */ + bool bj_done; + bool au_done; + int num_pending; +}; + +const char * facelet_event_str[] = { +#define _(x) [FACELET_EVENT_ ## x] = STRINGIZE(x), +foreach_facelet_event +#undef _ +}; + +facelet_t * +facelet_create() +{ + facelet_t * facelet = calloc(1, sizeof(facelet_t)); + if (!facelet) + goto ERR_MALLOC; + + facelet->netdevice_status = FACELET_ATTR_STATUS_UNSET; + facelet->netdevice_type_status = FACELET_ATTR_STATUS_UNSET; + facelet->family_status = FACELET_ATTR_STATUS_UNSET; + facelet->local_addr_status = FACELET_ATTR_STATUS_UNSET; + facelet->local_port_status = FACELET_ATTR_STATUS_UNSET; + facelet->remote_addr_status = FACELET_ATTR_STATUS_UNSET; + facelet->remote_port_status = FACELET_ATTR_STATUS_UNSET; + facelet->admin_state_status = FACELET_ATTR_STATUS_UNSET; + facelet->state_status = FACELET_ATTR_STATUS_UNSET; + facelet->face_type_status = FACELET_ATTR_STATUS_UNSET; + + facelet->status = FACELET_STATUS_NEW; + + facelet->bj_done = false; + facelet->au_done = false; + facelet->num_pending = 0; + + facelet->event = FACELET_EVENT_UNDEFINED; + + return facelet; + +ERR_MALLOC: + return NULL; +} + +facelet_t * +facelet_create_from_netdevice(netdevice_t * netdevice) +{ + facelet_t * facelet = facelet_create(); + if (!facelet) + goto ERR_FACELET; + + int rc = facelet_set_netdevice(facelet, *netdevice); + if (rc < 0) + goto ERR_NETDEV; + + return facelet; + +ERR_NETDEV: + facelet_free(facelet); +ERR_FACELET: + return NULL; +} + +/** + * \brief Validate whether the facelet has all required fields to construct a + * face of the given type + * \param [in) facelet - Pointer to the facelet to verify + * \return 0 in case of success, -1 otherwise + */ +int +facelet_validate_face(const facelet_t * facelet) +{ + if (!facelet_has_face_type(facelet)) + return false; + switch(facelet->face_type.layer) { + case FACE_TYPE_LAYER_4: + if (!facelet_has_remote_port(facelet)) + return false; + if (!facelet_has_remote_addr(facelet)) + return false; + case FACE_TYPE_LAYER_3: + if (!facelet_has_local_addr(facelet)) + return false; + if (!facelet_has_netdevice(facelet)) + return false; + return true; + + default: + return false; /* Error */ + } + // FIXME Not implemented + return 0; +} + + +netdevice_type_t +netdevice_type_from_face_tags(const face_t * face) +{ + policy_tags_t tags = face->tags; + if (policy_tags_has(tags, POLICY_TAG_WIRED)) + return NETDEVICE_TYPE_WIRED; + else if (policy_tags_has(tags, POLICY_TAG_WIFI)) + return NETDEVICE_TYPE_WIFI; + else if (policy_tags_has(tags, POLICY_TAG_CELLULAR)) + return NETDEVICE_TYPE_CELLULAR; + return NETDEVICE_TYPE_UNDEFINED; +} + +facelet_t * +facelet_create_from_face(face_t * face) +{ + facelet_t * facelet = malloc(sizeof(facelet_t)); + if (!facelet) + goto ERR_MALLOC; + + /* Go through the face attributes to update the local representation */ + + /* Attribute : netdevice */ + /* NOTE index is not set */ + if (IS_VALID_NETDEVICE(face->netdevice)) { + facelet->netdevice = face->netdevice; + facelet->netdevice_status = FACELET_ATTR_STATUS_CLEAN; + } else { + facelet->netdevice_status = FACELET_ATTR_STATUS_UNSET; + } + + /* Attribute : netdevice_type */ + facelet->netdevice_type = netdevice_type_from_face_tags(face); + if (facelet->netdevice_type != NETDEVICE_TYPE_UNDEFINED) { + facelet->netdevice_type_status = FACELET_ATTR_STATUS_CLEAN; + } else { + facelet->netdevice = NETDEVICE_EMPTY; + facelet->netdevice_type_status = FACELET_ATTR_STATUS_UNSET; + } + + /* Attribute : family */ + if (IS_VALID_FAMILY(face->family)) { + facelet->family = face->family; + facelet->family_status = FACELET_ATTR_STATUS_CLEAN; + + /* Attribute : local_addr */ + if (ip_address_cmp(&face->local_addr, &IP_ADDRESS_EMPTY, face->family) != 0) { + facelet->local_addr = face->local_addr; + facelet->local_addr_status = FACELET_ATTR_STATUS_CLEAN; + } else { + facelet->local_addr_status = FACELET_ATTR_STATUS_UNSET; + } + + /* Attribute : local_port */ + if (IS_VALID_PORT(face->local_port)) { + facelet->local_port = face->local_port; + facelet->local_port_status = FACELET_ATTR_STATUS_CLEAN; + } else { + facelet->local_port_status = FACELET_ATTR_STATUS_UNSET; + } + + /* Attribute : remote_addr */ + if (ip_address_cmp(&face->remote_addr, &IP_ADDRESS_EMPTY, face->family) != 0) { + facelet->remote_addr = face->remote_addr; + facelet->remote_addr_status = FACELET_ATTR_STATUS_CLEAN; + } else { + facelet->remote_addr_status = FACELET_ATTR_STATUS_UNSET; + } + + /* Attribute : remote_port */ + if (IS_VALID_PORT(face->remote_port)) { + facelet->remote_port = face->remote_port; + facelet->remote_port_status = FACELET_ATTR_STATUS_CLEAN; + } else { + facelet->remote_port_status = FACELET_ATTR_STATUS_UNSET; + } + + } else { + facelet->family_status = FACELET_ATTR_STATUS_UNSET; + facelet->local_addr_status = FACELET_ATTR_STATUS_UNSET; + facelet->local_port_status = FACELET_ATTR_STATUS_UNSET; + facelet->remote_addr_status = FACELET_ATTR_STATUS_UNSET; + facelet->remote_port_status = FACELET_ATTR_STATUS_UNSET; + } + + /* Attribute : admin_state */ + if ((face->admin_state == FACE_STATE_UP) || + (face->admin_state == FACE_STATE_DOWN)) { + facelet->admin_state = face->admin_state; + facelet->admin_state_status = FACELET_ATTR_STATUS_CLEAN; + } else { + facelet->admin_state_status = FACELET_ATTR_STATUS_UNSET; + } + + /* Attribute : state */ + if ((face->state == FACE_STATE_UP) || + (face->state == FACE_STATE_DOWN)) { + facelet->state = face->state; + facelet->state_status = FACELET_ATTR_STATUS_CLEAN; + } else { + facelet->state_status = FACELET_ATTR_STATUS_UNSET; + } + + /* Attribute : face_type */ + if ((face->type != FACE_TYPE_UNDEFINED) && (face->type != FACE_TYPE_N)) { + switch(face->type) { + case FACE_TYPE_UDP: + facelet->face_type = FACEMGR_FACE_TYPE_OVERLAY_UDP; + break; + case FACE_TYPE_TCP: + facelet->face_type = FACEMGR_FACE_TYPE_OVERLAY_TCP; + break; + case FACE_TYPE_HICN: + facelet->face_type = FACEMGR_FACE_TYPE_NATIVE_TCP; + break; + default: + ERROR("[facelet_create_from_face] Face type not (yet) implemented"); + goto ERR_FACE; + } + facelet->face_type_status = FACELET_ATTR_STATUS_CLEAN; + } else { + facelet->face_type_status = FACELET_ATTR_STATUS_UNSET; + } + + /* Status */ + facelet->status = FACELET_STATUS_CLEAN; + + /* TODO Consistency check between face type and found attributes */ + if (facelet_validate_face(facelet) < 0) + goto ERR_FACE; + + facelet->bj_done = false; + facelet->au_done = false; + facelet->num_pending = 0; + + facelet->event = FACELET_EVENT_UNDEFINED; + + return facelet; + +ERR_FACE: + free(facelet); +ERR_MALLOC: + return NULL; +} + + +void +facelet_free(facelet_t * facelet) +{ + free(facelet); +} + +facelet_t * +facelet_dup(const facelet_t * current_facelet) +{ + facelet_t * facelet = facelet_create(); + if (!facelet) + goto ERR_CREATE; + +#define _(TYPE, NAME) facelet-> NAME = current_facelet-> NAME; + foreach_facelet_attr +#undef _ +#define _(TYPE, NAME) facelet-> NAME ## _status = current_facelet-> NAME ## _status; + foreach_facelet_attr +#undef _ + + facelet->status = current_facelet->status; + facelet->event = current_facelet->event; + + facelet->bj_done = current_facelet->bj_done; + facelet->au_done = current_facelet->au_done; + facelet->num_pending = current_facelet->num_pending; + + return facelet; + +ERR_CREATE: + return NULL; +} + +int +facelet_cmp(const facelet_t * f1, const facelet_t * f2) +{ + /* + * Under the assumption we only create a face per physical interface, a + * facelet is uniquely identified by its netdevice attribute, and address + * family if any. + * + * This function is mostly used for lookups into the cache, and the face + * thus needs to have a netdevice associated, and optionally, an address + * family. + * + * For other situations, the `facelet_match` function is more appropriate. + */ + + if ((f1->netdevice_status != FACELET_ATTR_STATUS_UNSET) && + (f2->netdevice_status != FACELET_ATTR_STATUS_UNSET)) { + int rc = netdevice_cmp(&f1->netdevice, &f2->netdevice); + if (rc != 0) + return rc; + + } else { + /* Both unset : we might have the face without netdevice due to hicn + * light not returning it currently, but we cannot skip it in the match + * otherwise we cannot distinguish with other faces except matching on + * other fields which might unfortunately not be determined yet... + */ + return (f1->netdevice_status == FACELET_ATTR_STATUS_UNSET) ? -1 : 1; + } + + assert(f1->family_status != FACELET_ATTR_STATUS_UNSET); + assert(f2->family_status != FACELET_ATTR_STATUS_UNSET); + + if ((f1->family == AF_UNSPEC) || (f2->family == AF_UNSPEC)) + return 0; + int diff = f1->family - f2->family; + return (diff > 0) ? 1 : + (diff < 0) ? -1 : 0; +} + +/* + * If the match has a field set, then the facelet only matches iif it has the + * same field set, and both values are equal + */ +#define MATCH_ATTRIBUTE(TYPE, NAME) \ +do { \ + if (facelet_match->NAME ## _status == FACELET_ATTR_STATUS_CLEAN) { \ + if (facelet_has_ ## NAME(facelet_match)) { \ + TYPE NAME; \ + TYPE NAME ## _match; \ + if (!facelet_has_ ## NAME(facelet)) \ + return false; \ + if (facelet_get_ ## NAME (facelet, & NAME) < 0) \ + return false; \ + if (facelet_get_ ## NAME (facelet_match, & NAME ## _match) < 0) \ + return false; \ + if (memcmp(& NAME, & NAME ## _match, sizeof(NAME)) != 0) \ + return false; \ + } \ + } \ +} while(0) + +/* facelet_match is the incoming one */ +bool +facelet_match(const facelet_t * facelet, const facelet_t * facelet_match) +{ +#define _(TYPE, NAME) MATCH_ATTRIBUTE(TYPE, NAME); + foreach_facelet_attr +#undef _ + return true; +} + +bool facelet_has_key(const facelet_t * facelet) { + return (facelet_has_netdevice(facelet) && facelet_has_family(facelet)); +} + +/* + * Implementation note: + * - facelet_set_* is equivalent to merge with a CLEAN remote attribute + */ +#define FACELET_ACCESSORS(TYPE, NAME) \ +bool \ +facelet_has_ ## NAME(const facelet_t * facelet) \ +{ \ + assert(facelet); \ + assert(facelet->NAME ## _status != FACELET_ATTR_STATUS_UNDEFINED); \ + assert(facelet->NAME ## _status != FACELET_ATTR_STATUS_N); \ + return ((facelet-> NAME ## _status != FACELET_ATTR_STATUS_UNSET)); \ +} \ + \ +facelet_attr_status_t \ +facelet_get_ ## NAME ## _status(const facelet_t * facelet) \ +{ \ + return (facelet->NAME ## _status); \ +} \ + \ +int \ +facelet_get_ ## NAME(const facelet_t * facelet, TYPE * NAME) \ +{ \ + assert(facelet); \ + if (!facelet_has_ ## NAME(facelet)) \ + return -1; \ + *NAME = facelet-> NAME; \ + return 0; \ +} \ + \ +int \ +facelet_set_local_ ## NAME(facelet_t * facelet, TYPE NAME) \ +{ \ + assert(facelet); \ + switch(facelet->NAME ## _status) { \ + case FACELET_ATTR_STATUS_UNSET: \ + case FACELET_ATTR_STATUS_CLEAN: \ + case FACELET_ATTR_STATUS_DIRTY: \ + case FACELET_ATTR_STATUS_PENDING: \ + facelet-> NAME = NAME; \ + facelet->NAME ## _status = FACELET_ATTR_STATUS_DIRTY; \ + if (facelet->status == FACELET_STATUS_CLEAN) \ + facelet->status = FACELET_STATUS_DIRTY; \ + break; \ + case FACELET_ATTR_STATUS_CONFLICT: \ + break; \ + case FACELET_ATTR_STATUS_UNDEFINED: \ + case FACELET_ATTR_STATUS_N: \ + ERROR("Unexpected attribute status value"); \ + return -1; \ + } \ + return 0; \ +} \ + \ +int \ +facelet_set_remote_ ## NAME(facelet_t * facelet, TYPE NAME) \ +{ \ + assert(facelet); \ + switch(facelet->NAME ## _status) { \ + case FACELET_ATTR_STATUS_UNSET: \ + facelet-> NAME = NAME; \ + facelet->NAME ## _status = FACELET_ATTR_STATUS_CLEAN; \ + break; \ + case FACELET_ATTR_STATUS_CLEAN: \ + facelet->NAME = NAME; \ + break; \ + case FACELET_ATTR_STATUS_DIRTY: \ + ERROR("Discarded remote value for status reasons"); \ + break; \ + case FACELET_ATTR_STATUS_PENDING: \ + ERROR("Received remote value on pending attribute"); \ + facelet->NAME ## _status = FACELET_ATTR_STATUS_CONFLICT; \ + if (facelet->status != FACELET_STATUS_CONFLICT) \ + facelet->status = FACELET_STATUS_CONFLICT; \ + break; \ + case FACELET_ATTR_STATUS_CONFLICT: \ + return -1; \ + case FACELET_ATTR_STATUS_UNDEFINED: \ + case FACELET_ATTR_STATUS_N: \ + ERROR("Unexpected attribute status value"); \ + return -1; \ + } \ + return 0; \ +} \ + \ +int \ +facelet_set_ ## NAME(facelet_t * facelet, TYPE NAME) \ +{ \ + return facelet_set_local_ ## NAME(facelet, NAME); \ +} \ + \ + +#define _(TYPE, NAME) FACELET_ACCESSORS(TYPE, NAME) +foreach_facelet_attr +#undef _ + +/* + * This function is called for every facelet attribute. It is responsible for + * comparing both the current and new value, and set the attribute and facelet + * status appropriately. + */ + +// FIXME CLEAN for key fields, dirty for fields to update. + +#define MERGE_ATTRIBUTE(TYPE, NAME) \ +do { \ + switch(facelet_to_merge->NAME ## _status) { \ + case FACELET_ATTR_STATUS_UNDEFINED: \ + case FACELET_ATTR_STATUS_N: \ + case FACELET_ATTR_STATUS_PENDING: \ + case FACELET_ATTR_STATUS_CONFLICT: \ + ERROR("Unexpected facelet attribute status"); \ + return -1; \ + case FACELET_ATTR_STATUS_UNSET: \ + break; \ + case FACELET_ATTR_STATUS_CLEAN: \ + case FACELET_ATTR_STATUS_DIRTY: \ + facelet_set_ ## NAME(facelet, facelet_to_merge-> NAME); \ + break; \ + } \ +} while (0) + +int facelet_merge(facelet_t * facelet, const facelet_t * facelet_to_merge) +{ + assert(facelet && facelet_to_merge); +#define _(TYPE, NAME) MERGE_ATTRIBUTE(TYPE, NAME); + foreach_facelet_attr +#undef _ + facelet->event = facelet_to_merge->event; + return 0; +} + +#define MERGE_ATTRIBUTE_REMOTE(TYPE, NAME) \ +do { \ + switch(facelet_to_merge->NAME ## _status) { \ + case FACELET_ATTR_STATUS_UNDEFINED: \ + case FACELET_ATTR_STATUS_N: \ + case FACELET_ATTR_STATUS_DIRTY: \ + case FACELET_ATTR_STATUS_PENDING: \ + case FACELET_ATTR_STATUS_CONFLICT: \ + ERROR("Unexpected facelet attribute status"); \ + return -1; \ + case FACELET_ATTR_STATUS_UNSET: \ + break; \ + case FACELET_ATTR_STATUS_CLEAN: \ + facelet_set_ ## NAME(facelet, facelet_to_merge-> NAME); \ + break; \ + \ + } \ +} while (0) + +int facelet_merge_remote(facelet_t * facelet, const facelet_t * facelet_to_merge) +{ + assert(facelet && facelet_to_merge); +#define _(TYPE, NAME) MERGE_ATTRIBUTE_REMOTE(TYPE, NAME); + foreach_facelet_attr +#undef _ + facelet->event = facelet_to_merge->event; + return 0; +} + +int +facelet_get_face(const facelet_t * facelet, face_t ** pface) +{ + assert(pface); + + /* Facelet has all the required information to create a face */ + if (facelet_validate_face(facelet) < 0) + return 0; + + face_t * face = face_create(); + if (!face) + goto ERR_CREATE; + + assert(facelet_has_netdevice(facelet)); + face->netdevice = facelet->netdevice; + + /* Face type */ + switch(facelet->face_type.layer) { + case FACE_TYPE_LAYER_4: + switch(facelet->face_type.encap) { + case FACE_TYPE_ENCAP_UDP: + face->type = FACE_TYPE_UDP; + break; + case FACE_TYPE_ENCAP_TCP: + face->type = FACE_TYPE_TCP; + break; + case FACE_TYPE_ENCAP_UNDEFINED: + case FACE_TYPE_ENCAP_N: + ERROR("[facelet_get_face] Unsupported face encapsulation"); + goto ERR; + } + + if (facelet_get_family(facelet, &face->family) < 0) + goto ERR; + if (facelet_get_local_addr(facelet, &face->local_addr) < 0) + goto ERR; + if (facelet_get_local_port(facelet, &face->local_port) < 0) + goto ERR; + if (facelet_get_remote_addr(facelet, &face->remote_addr) < 0) + goto ERR; + if (facelet_get_remote_port(facelet, &face->remote_port) < 0) + goto ERR; + break; + + case FACE_TYPE_LAYER_3: + ERROR("{facelet_get_face] hICN face not (yet) implemented"); + goto ERR; + + case FACE_TYPE_LAYER_UNDEFINED: + case FACE_TYPE_LAYER_N: + ERROR("[facelet_get_face] Unsupported face type"); + goto ERR; + } + + if (facelet_has_admin_state(facelet)) { + if (facelet_get_admin_state(facelet, &face->admin_state) < 0) + goto ERR; + } else { + face->admin_state = FACE_STATE_UP; + } + + if (facelet_has_state(facelet)) { + if (facelet_get_state(facelet, &face->state) < 0) + goto ERR; + } else { + face->state = FACE_STATE_UP; + } + + /* Tags */ + + /* - based on netdevice type */ + policy_tags_t tags = POLICY_TAGS_EMPTY; + if (facelet_has_netdevice_type(facelet)) { + netdevice_type_t netdevice_type; + if (facelet_get_netdevice_type(facelet, &netdevice_type) < 0) { + ERROR("error getting netdevice_type"); + goto ERR; + } + + + switch(netdevice_type) { + case NETDEVICE_TYPE_UNDEFINED: + case NETDEVICE_TYPE_LOOPBACK: + break; + case NETDEVICE_TYPE_WIRED: + policy_tags_add(&tags, POLICY_TAG_WIRED); + break; + case NETDEVICE_TYPE_WIFI: + policy_tags_add(&tags, POLICY_TAG_WIFI); + break; + case NETDEVICE_TYPE_CELLULAR: + policy_tags_add(&tags, POLICY_TAG_CELLULAR); + break; + default: + goto ERR; + } + } +#ifdef __linux__ +#ifndef __ANDROID__ + else { + /* + * Heuristics to determine face type based on name, until a better + * solution is found + */ + if (strncmp(facelet->netdevice.name, "eth", 3) == 0) { + policy_tags_add(&tags, POLICY_TAG_WIRED); + goto DONE; + } + if (strncmp(facelet->netdevice.name, "en", 2) == 0) { + policy_tags_add(&tags, POLICY_TAG_WIRED); + goto DONE; + } + if (strncmp(facelet->netdevice.name, "wl", 2) == 0) { + /* wlan* wlp* wlx* */ + policy_tags_add(&tags, POLICY_TAG_WIFI); + goto DONE; + } + +DONE: + ; + } +#endif /* ! __ANDROID__ */ +#endif /* __linux__ */ + face->tags = tags; + + *pface = face; + + return 0; + +ERR: + free(face); +ERR_CREATE: + *pface = NULL; + return -1; +} + +facelet_status_t +facelet_get_status(const facelet_t * facelet) +{ + return facelet->status; +} + +#define SET_ATTR_STATUS_CLEAN(TYPE, NAME) \ +do { \ + if (facelet->NAME ## _status == FACELET_ATTR_STATUS_DIRTY) \ + facelet->NAME ## _status = FACELET_ATTR_STATUS_CLEAN; \ +} while (0) + +void +facelet_set_status(facelet_t * facelet, facelet_status_t status) +{ + if (status == FACELET_STATUS_CLEAN) { +#define _(TYPE, NAME) SET_ATTR_STATUS_CLEAN(TYPE, NAME); + foreach_facelet_attr +#undef _ + } + facelet->status = status; +} + +int +facelet_add_pending(facelet_t * facelet) +{ + assert(facelet); + facelet->num_pending++; + return 0; +} + +int +facelet_remove_pending(facelet_t * facelet) +{ + assert(facelet); + if (facelet->num_pending == 0) + return -1; + facelet->num_pending--; + return 0; +} + +bool +facelet_has_pending(const facelet_t * facelet) +{ + assert(facelet); + DEBUG("num pending=%d\n", facelet->num_pending); + return (facelet->num_pending > 0); +} + +void +facelet_set_bj_done(facelet_t * facelet) +{ + facelet->bj_done = true; +} + +bool +facelet_is_bj_done(const facelet_t * facelet) +{ + return facelet->bj_done; +} + +void +facelet_set_au_done(facelet_t * facelet) +{ + facelet->au_done = true; +} + +bool +facelet_is_au_done(const facelet_t * facelet) +{ + return facelet->au_done; +} + +facelet_event_t +facelet_get_event(const facelet_t * facelet) +{ + return facelet->event; +} + +void +facelet_set_event(facelet_t * facelet, facelet_event_t event) +{ + facelet->event = event; +} + +int +facelet_raise_event(facelet_t * facelet, const interface_t * interface) +{ + if (interface->callback) + interface->callback(interface->callback_data, facelet); + return 0; +} + +int +facelet_snprintf(char * s, size_t size, facelet_t * facelet) +{ + char * cur = s; + int rc; + + assert(facelet); + + /* Header + key attributes (netdevice + family) */ + rc = snprintf(cur, s + size - cur, "<Facelet %s (%s) [%d]", + // FIXME, better than the event would be the action to be performed next + facelet_event_str[facelet->event], + (facelet->family == AF_INET) ? "AF_INET" : + (facelet->family == AF_INET6) ? "AF_INET6" : + (facelet->family == AF_UNSPEC) ? "AF_UNSPEC" : + "unknown", + facelet->num_pending); + if (rc < 0) + return rc; + cur += rc; + if (size != 0 && cur >= s + size) + return cur - s; + + /* Netdevice */ + if (facelet_has_netdevice(facelet)) { + rc = snprintf(cur, s + size - cur, " netdevice=%s", + facelet->netdevice.name[0] ? facelet->netdevice.name : "*"); + if (rc < 0) + return rc; + cur += rc; + if (size != 0 && cur >= s + size) + return cur - s; + + rc = snprintf(cur, s + size - cur, "/%d", facelet->netdevice.index); + if (rc < 0) + return rc; + cur += rc; + if (size != 0 && cur >= s + size) + return cur - s; + + } else { + rc = snprintf(cur, s + size - cur, " netdevice=*/*"); + if (rc < 0) + return rc; + cur += rc; + if (size != 0 && cur >= s + size) + return cur - s; + } + + /* Netdevice type */ + if (facelet_has_netdevice_type(facelet)) { + rc = snprintf(cur, s + size - cur, " type=%s", + netdevice_type_str[facelet->netdevice_type]); + if (rc < 0) + return rc; + cur += rc; + if (size != 0 && cur >= s + size) + return cur - s; + } +#ifdef __linux__ +#ifndef __ANDROID__ + else { + /* + * Heuristics to determine face type based on name, until a better + * solution is found + */ + if ((strncmp(facelet->netdevice.name, "eth", 3) == 0) || + (strncmp(facelet->netdevice.name, "en", 2) == 0)) { + rc = snprintf(cur, s + size - cur, " [type=WIRED]"); + goto HEURISTIC_DONE; + } + if (strncmp(facelet->netdevice.name, "wl", 2) == 0) { + /* wlan* wlp* wlx* */ + rc = snprintf(cur, s + size - cur, " [type=WIFI]"); + goto HEURISTIC_DONE; + } + goto HEURISTIC_END; + +HEURISTIC_DONE: + if (rc < 0) + return rc; + cur += rc; + if (size != 0 && cur >= s + size) + return cur - s; +HEURISTIC_END: + ; + } +#endif /* ! __ANDROID__ */ +#endif /* __linux__ */ + + /* Local ip address */ + if (facelet_has_local_addr(facelet)) { + rc = snprintf(cur, s + size - cur, " local_addr="); + if (rc < 0) + return rc; + cur += rc; + if (size != 0 && cur >= s + size) + return cur - s; + + rc = ip_address_snprintf(cur, s + size - cur, &facelet->local_addr, + facelet->family); + if (rc < 0) + return rc; + cur += rc; + if (size != 0 && cur >= s + size) + return cur - s; + } + + /* Local port */ + if (facelet_has_local_port(facelet)) { + rc = snprintf(cur, s + size - cur, " local_port=%d", + facelet->local_port); + if (rc < 0) + return rc; + cur += rc; + if (size != 0 && cur >= s + size) + return cur - s; + } + + /* Remote ip address */ + if (facelet_has_remote_addr(facelet)) { + rc = snprintf(cur, s + size - cur, " remote_addr="); + if (rc < 0) + return rc; + cur += rc; + if (size != 0 && cur >= s + size) + return cur - s; + + rc = ip_address_snprintf(cur, s + size - cur, &facelet->remote_addr, + facelet->family); + if (rc < 0) + return rc; + cur += rc; + if (size != 0 && cur >= s + size) + return cur - s; + } + + /* Remote port */ + if (facelet_has_remote_port(facelet)) { + rc = snprintf(cur, s + size - cur, " remote_port=%d", + facelet->remote_port); + if (rc < 0) + return rc; + cur += rc; + if (size != 0 && cur >= s + size) + return cur - s; + } + + /* Admin state */ + if (facelet_has_admin_state(facelet)) { + rc = snprintf(cur, s + size - cur, " admin_state=%s", + face_state_str[facelet->admin_state]); + if (rc < 0) + return rc; + cur += rc; + if (size != 0 && cur >= s + size) + return cur - s; + } + + /* State */ + if (facelet_has_state(facelet)) { + rc = snprintf(cur, s + size - cur, " state=%s", + face_state_str[facelet->state]); + if (rc < 0) + return rc; + cur += rc; + if (size != 0 && cur >= s + size) + return cur - s; + } + + if (facelet_has_face_type(facelet)) { + rc = snprintf(cur, s + size - cur, " face_type=IP%s/%s", + FACEMGR_FACE_TYPE_STR(facelet->face_type)); + if (rc < 0) + return rc; + cur += rc; + if (size != 0 && cur >= s + size) + return cur - s; + } + + rc = snprintf(cur, s + size - cur, ">"); + if (rc < 0) + return rc; + cur += rc; + if (size != 0 && cur >= s + size) + return cur - s; + + return cur - s; +} diff --git a/ctrl/facemgr/src/facelet.h b/ctrl/facemgr/src/facelet.h new file mode 100644 index 000000000..fecee8641 --- /dev/null +++ b/ctrl/facemgr/src/facelet.h @@ -0,0 +1,198 @@ +/* + * 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 facelet.h + * \brief Facelet + * + * A facelet consists in partial information and annotations collected towards + * the contruction of the final face that will be sent to the forwarder. + * + * It might also consist in a pattern allowing the deletion of a group of face + * for instance. + */ +#ifndef FACEMGR_FACELET_H +#define FACEMGR_FACELET_H + +#include <stdbool.h> + +#include <hicn/ctrl/face.h> +#include <hicn/facemgr.h> + +#include "interface.h" + +/* NOTE: Any test should be sufficient */ +#define IS_VALID_NETDEVICE(netdevice) ((netdevice.index != 0) && (netdevice.name[0] != '\0')) + +typedef struct facelet_s facelet_t; + +/* Facelet status */ +#define foreach_facelet_status \ + _(UNDEFINED) \ + _(NEW) \ + _(CLEAN) \ + _(DIRTY) \ + _(CONFLICT) \ + _(DELETED) \ + _(N) + +typedef enum { +#define _(x) FACELET_STATUS_ ## x, + foreach_facelet_status +#undef _ +} facelet_status_t; + +extern const char * facelet_status_str[]; + +/* Facelet attribute status */ + +/* + * We expect an attribute in the cache to be able to take any value but + * UNDEFINED and N, which facelet events should either be UNSET or CLEAN + */ +#define foreach_facelet_attr_status \ + _(UNDEFINED, '?') \ + _(UNSET, 'X') \ + _(CLEAN, ' ') \ + _(DIRTY, '*') \ + _(PENDING, 'P') \ + _(CONFLICT, '!') \ + _(N, '-') + +typedef enum { +#define _(x, y) FACELET_ATTR_STATUS_ ## x, + foreach_facelet_attr_status +#undef _ +} facelet_attr_status_t; + +extern const char * facelet_attr_status_str[]; +extern const char * facelet_attr_status_str_short[]; + +/* Facelet attribute */ + +#define foreach_facelet_attr \ + _(netdevice_type_t, netdevice_type) \ + _(netdevice_t, netdevice) \ + _(int, family) \ + _(ip_address_t, local_addr) \ + _(u16, local_port) \ + _(ip_address_t, remote_addr) \ + _(u16, remote_port) \ + _(face_state_t, admin_state) \ + _(face_state_t, state) \ + _(facemgr_face_type_t, face_type) + +#define foreach_facelet_event \ + _(UNDEFINED) \ + _(GET) \ + _(CREATE) \ + _(UPDATE) \ + _(DELETE) \ + _(SET_PARAMS) \ + _(SET_UP) \ + _(SET_DOWN) \ + _(SET_TAGS) \ + _(CLEAR_TAGS) \ + _(ADD_TAG) \ + _(REMOVE_TAG) \ + _(N) + +#define MAXSZ_EVENT__ 10 +#define MAXSZ_EVENT_ MAXSZ_EVENT_ + 1 + +/** + * \brief Enumeration of the possible types of event + */ +typedef enum { +#define _(x) FACELET_EVENT_ ## x, +foreach_facelet_event +#undef _ +} facelet_event_t; + +extern const char * facelet_event_str[]; + +/** + * \brief Create a facelet. + */ +facelet_t * facelet_create(); + +facelet_t * facelet_create_from_netdevice(netdevice_t * netdevice); + +int facelet_validate_face(const facelet_t * facelet); + +facelet_t * facelet_create_from_face(face_t * face); + +void facelet_free(facelet_t * facelet); + +facelet_t * facelet_dup(const facelet_t * current_facelet); + +int facelet_cmp(const facelet_t * f1, const facelet_t * f2); + +/* NOTE: only clean attributes are matched */ +bool facelet_match(const facelet_t * facelet, const facelet_t * facelet_match); + +/** + * \brief Returns whether the specified facelet has all key attributes defined. + * + * Key attributes are netdevice and family. If both are present, this allows to + * uniquely identify a facelet, otherwise it is a 'wildcard' facelet + * specification and might match several facelets. + */ +bool facelet_has_key(const facelet_t * facelet); + + +#define FACELET_ACCESSORS_H(TYPE, NAME) \ +bool facelet_has_ ## NAME(const facelet_t * facelet); \ +facelet_attr_status_t facelet_get_ ## NAME ## _status(const facelet_t * facelet);\ +int facelet_get_ ## NAME(const facelet_t * facelet, TYPE * NAME); \ +int facelet_set_ ## NAME(facelet_t * facelet, TYPE NAME); + +#define _(TYPE, NAME) FACELET_ACCESSORS_H(TYPE, NAME) +foreach_facelet_attr +#undef _ + +int facelet_get_face(const facelet_t * facelet, face_t ** pface); + +int facelet_merge(facelet_t * facelet, const facelet_t * facelet_to_merge); + +facelet_status_t facelet_get_status(const facelet_t * facelet); +void facelet_set_status(facelet_t * facelet, facelet_status_t status); + +int facelet_add_pending(facelet_t * facelet); +int facelet_remove_pending(facelet_t * facelet); +bool facelet_has_pending(const facelet_t * facelet); + +void facelet_set_bj_done(facelet_t * facelet); +bool facelet_is_bj_done(const facelet_t * facelet); +void facelet_set_au_done(facelet_t * facelet); +bool facelet_is_au_done(const facelet_t * facelet); + +facelet_event_t facelet_get_event(const facelet_t * facelet); +void facelet_set_event(facelet_t * facelet, facelet_event_t event); + +/** + * \brief Create and raises an event to the face manager + * \param [in] event_type - Type of the event to create + * \param [in] facelet - Facelet to communicate with the event + * \param [in] interface - Interface that raised the event (or NULL if it was + * created but the face manager itself, or is a joined event) + */ +int facelet_raise_event(facelet_t * facelet, const interface_t * interface); + +int facelet_snprintf(char * buf, size_t size, facelet_t * facelet); + +#define MAXSZ_FACELET 1024 + +#endif /* FACEMGR_FACELET_H */ diff --git a/ctrl/facemgr/src/facemgr.c b/ctrl/facemgr/src/facemgr.c deleted file mode 100644 index 41e30de56..000000000 --- a/ctrl/facemgr/src/facemgr.c +++ /dev/null @@ -1,298 +0,0 @@ -/* - * 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 facemgr.c - * \brief Implementation of Face manager library interface - */ - -#include <stdio.h> - -#include "common.h" -#include "event.h" -#include "facemgr.h" -#include "interface.h" -#include "util/log.h" - -#ifdef __APPLE__ -extern interface_ops_t network_framework_ops; -#endif -#ifdef __linux__ -extern interface_ops_t netlink_ops; -#endif -#if 0 -extern interface_ops_t dummy_ops; -#endif -extern interface_ops_t hicn_light_ops; - -int -facemgr_initialize(facemgr_t * facemgr) -{ - int rc; - - rc = interface_map_initialize(&facemgr->interface_map); - if (FACEMGR_IS_ERROR(rc)) - goto ERR_INTERFACE_MAP; - - rc = face_cache_initialize(&facemgr->face_cache); - if (FACEMGR_IS_ERROR(rc)) - goto ERR_FACE_SET; - - rc = face_rules_initialize(&facemgr->rules); - if (FACEMGR_IS_ERROR(rc)) - goto ERR_FACE_SET; - - return FACEMGR_SUCCESS; - -ERR_FACE_SET: - interface_map_finalize(&facemgr->interface_map); - -ERR_INTERFACE_MAP: - return FACEMGR_FAILURE; -} - -int -facemgr_finalize(facemgr_t * facemgr) -{ - int rc; - - /* XXX Free all interfaces: pass free to map */ - rc = interface_map_finalize(&facemgr->interface_map); - if (FACEMGR_IS_ERROR(rc)) - goto ERR; - - rc = face_cache_finalize(&facemgr->face_cache); - if (FACEMGR_IS_ERROR(rc)) - goto ERR; - - rc = face_rules_finalize(&facemgr->rules); - if (FACEMGR_IS_ERROR(rc)) - goto ERR; - - return FACEMGR_SUCCESS; - -ERR: - return FACEMGR_FAILURE; -} - -AUTOGENERATE_CREATE_FREE(facemgr); - -int -facemgr_on_event(facemgr_t * facemgr, event_t * event) -{ - int rc; - char face_s[MAXSZ_FACE]; - face_t * cached_face; - - if (!event->face) { - printf("Event with empty face\n"); - return -1; - } - - face_t face = *event->face; - - /* Complement unbound UDP faces */ - switch(face.type) { - case FACE_TYPE_TCP: - case FACE_TYPE_UDP: - switch (face.params.tunnel.family) { - case AF_INET: - if ((ip_address_empty(&face.params.tunnel.remote_addr)) && - (!ip_address_empty(&facemgr->overlay_v4_remote_addr))) - face.params.tunnel.remote_addr = facemgr->overlay_v4_remote_addr; - if ((face.params.tunnel.local_port == 0) && (facemgr->overlay_v4_local_port != 0)) - face.params.tunnel.local_port = facemgr->overlay_v4_local_port; - if ((face.params.tunnel.remote_port == 0) && (facemgr->overlay_v4_remote_port != 0)) - face.params.tunnel.remote_port = facemgr->overlay_v4_remote_port; - break; - case AF_INET6: - if ((ip_address_empty(&face.params.tunnel.remote_addr)) && - (!ip_address_empty(&facemgr->overlay_v6_remote_addr))) - face.params.tunnel.remote_addr = facemgr->overlay_v6_remote_addr; - if ((face.params.tunnel.local_port == 0) && (facemgr->overlay_v6_local_port != 0)) - face.params.tunnel.local_port = facemgr->overlay_v6_local_port; - if ((face.params.tunnel.remote_port == 0) && (facemgr->overlay_v6_remote_port != 0)) - face.params.tunnel.remote_port = facemgr->overlay_v6_remote_port; - default: - break; - } - break; - default: - break; - } - - face_snprintf(face_s, MAXSZ_FACE, &face); - - /* TODO Here, we need to filter events based on our cache, and update the cache - * based on our actions if they are successful */ - - switch(event->type) { - case EVENT_TYPE_CREATE: - rc = face_cache_get(&facemgr->face_cache, &face, &cached_face); - if (!FACEMGR_IS_ERROR(rc)) { - DEBUG("Face found in cache"); - goto IGNORE_EVENT; - } - rc = face_cache_add(&facemgr->face_cache, &face); - if (FACEMGR_IS_ERROR(rc)) - WARN("Failed to add face to cache"); - break; - case EVENT_TYPE_DELETE: - rc = face_cache_remove(&facemgr->face_cache, &face, &cached_face); - if (FACEMGR_IS_ERROR(rc)) - WARN("Face not found in cache"); - break; - case EVENT_TYPE_SET_UP: - case EVENT_TYPE_SET_DOWN: - /* TODO We need a return code to update the cache */ - break; - default: - printf("Not implemented!\n"); - break; - } - - /* Process event */ - printf("[ FACE %s ] %s\n", event_type_str[event->type], face_s); - /* Hardcoded hicn-light */ - rc = interface_on_event(facemgr->hl, event); - if (FACEMGR_IS_ERROR(rc)) - goto ERR; - -IGNORE_EVENT: - return FACEMGR_SUCCESS; - -ERR: - return FACEMGR_FAILURE; -} - -#ifdef __linux__ -void interface_callback(evutil_socket_t fd, short what, void * arg) { - interface_t * interface = (interface_t *)arg; - interface->ops->callback(interface); -} -#endif /* __linux__ */ - -int -facemgr_create_interface(facemgr_t * facemgr, const char * name, const char * type, interface_t ** interface) -{ - int fd, rc; - - INFO("Creating interface %s [%s]...\n", name, type); - *interface = interface_create(name, type); - if (!*interface) { - ERROR("Error creating interface %s [%s]\n", name, type); - return -1; - } - interface_set_callback(*interface, facemgr_on_event, facemgr); - - fd = interface_initialize(*interface, &facemgr->rules); - if (fd < 0) - return -2; - if (fd != 0) { -#ifdef __linux__ - evutil_make_socket_nonblocking(fd); - struct event * event = event_new(facemgr->loop, fd, EV_READ | EV_PERSIST, interface_callback, *interface); - if (!event) { - return -3; - } - - if (event_add(event, NULL) < 0) { - return -4; - } -#else - ERROR("Not implemented\n"); - return FACEMGR_FAILURE; -#endif /* __linux__ */ - } - - rc = interface_map_add(&facemgr->interface_map, (*interface)->name, *interface); - if (FACEMGR_IS_ERROR(rc)) - return -5; - - DEBUG("Interface created successfully.\n"); - return FACEMGR_SUCCESS; -} - -int -facemgr_bootstrap(facemgr_t * facemgr) -{ - int rc; - - DEBUG("Registering interfaces..."); - rc = interface_register(&hicn_light_ops); - if (FACEMGR_IS_ERROR(rc)) { - ERROR("Could not register interfaces"); - goto ERR_REGISTER; - } - -#ifdef __APPLE__ - rc = interface_register(&network_framework_ops); - if (FACEMGR_IS_ERROR(rc)) - goto ERR_REGISTER; -#endif /* __APPLE__ */ - -#ifdef __linux__ - rc = interface_register(&netlink_ops); - if (FACEMGR_IS_ERROR(rc)) - goto ERR_REGISTER; -#endif /* __linux__ */ - -#if 0 - rc = interface_register(&dummy_ops); - if (FACEMGR_IS_ERROR(rc)) - goto ERR_REGISTER; -#endif - - rc = facemgr_create_interface(facemgr, "hl", "hicn_light", &facemgr->hl); - if (rc < 0) { - ERROR("Error creating 'hICN forwarder (hicn-light)' interface\n"); - goto ERR_HL_CREATE; - } - -#ifdef __APPLE__ - rc = facemgr_create_interface(facemgr, "nf", "network_framework", &facemgr->nf); - if (rc < 0) { - ERROR("Error creating 'Apple Network Framework' interface\n"); - goto ERR_NF_CREATE; - } -#endif /* __APPLE__ */ - -#ifdef __linux__ - rc = facemgr_create_interface(facemgr, "nl", "netlink", &facemgr->nl); - if (rc < 0) { - ERROR("Error creating 'Netlink' interface\n"); - goto ERR_NF_CREATE; - } -#endif /* __linux__ */ - -#if 0 - rc = facemgr_create_interface(facemgr, "dummy", "dummy", &facemgr->dummy); - if (rc < 0) { - ERROR("Error creating 'Dummy' interface\n"); - goto ERR_NF_CREATE; - } -#endif - - DEBUG("Facemgr successfully initialized..."); - - return FACEMGR_SUCCESS; - -ERR_NF_CREATE: - interface_free(facemgr->hl); -ERR_HL_CREATE: - //interface_map_remove(&facemgr->interface_map, data->nf->name); -ERR_REGISTER: - return FACEMGR_FAILURE; -} diff --git a/ctrl/facemgr/src/facemgr.h b/ctrl/facemgr/src/facemgr.h deleted file mode 100644 index 6505a1bd8..000000000 --- a/ctrl/facemgr/src/facemgr.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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 facemgr.h - * \brief Face manager library interface - */ -#ifndef FACEMGR_H -#define FACEMGR_H - -#include <string.h> -#include "common.h" -#include "face.h" -#include "face_cache.h" -#include "face_rules.h" -#include "interface.h" -#include "interface_map.h" -#include "util/ip_address.h" -#include "util/map.h" -#include "util/policy.h" -#ifndef __APPLE__ -#include <event2/event.h> -#endif /* __APPLE__ */ - -/* - * \brief Face manager context - */ -typedef struct { -#ifndef APPLE - /* Event loop */ - struct event_base * loop; -#endif /* APPLE */ - - interface_map_t interface_map; - interface_t * hl; - -#ifdef __APPLE__ - interface_t * nf; -#endif /* __APPLE__ */ - -#ifdef __linux__ - interface_t * nl; -#endif /* __linux__ */ - -#if 0 - interface_t * dummy; -#endif - - /* Overlay management */ - uint16_t overlay_v4_local_port; - ip_address_t overlay_v4_remote_addr; - uint16_t overlay_v4_remote_port; - uint16_t overlay_v6_local_port; - ip_address_t overlay_v6_remote_addr; - uint16_t overlay_v6_remote_port; - - face_rules_t rules; - face_cache_t face_cache; -} facemgr_t; - -AUTOGENERATE_DEFS(facemgr); - -int facemgr_bootstrap(facemgr_t * facemgr); - -#endif /* FACEMGR_H */ diff --git a/ctrl/facemgr/src/interface.c b/ctrl/facemgr/src/interface.c index af9f666a7..3e6bc0854 100644 --- a/ctrl/facemgr/src/interface.c +++ b/ctrl/facemgr/src/interface.c @@ -20,12 +20,13 @@ #include <stdlib.h> #include <string.h> -#include "event.h" -#include "face_rules.h" +#include "facelet.h" #include "interface.h" -#include "interface_ops_map.h" #include "util/map.h" +TYPEDEF_MAP_H(interface_ops_map, const char *, const interface_ops_t *); +TYPEDEF_MAP(interface_ops_map, const char *, const interface_ops_t *, strcmp, string_snprintf, generic_snprintf); + static interface_ops_map_t * interface_ops_map = NULL; int @@ -34,19 +35,19 @@ interface_register(const interface_ops_t * ops) if (!interface_ops_map) { interface_ops_map = interface_ops_map_create(); if (!interface_ops_map) - return FACEMGR_FAILURE; + return -1; } interface_ops_map_add(interface_ops_map, ops->type, ops); - return FACEMGR_SUCCESS; + return 0; } interface_t * interface_create(const char * name, const char * type) { - interface_ops_t * ops; + const interface_ops_t * ops = NULL; int rc = interface_ops_map_get(interface_ops_map, type, &ops); - if (FACEMGR_IS_ERROR(rc)) { + if (rc < 0) { printf("Interface type not found %s\n", type); return NULL; } @@ -80,25 +81,25 @@ _interface_set_callback(interface_t * interface, callback_t callback, void * cal } int -interface_initialize(interface_t * interface, struct face_rules_s * rules) +interface_initialize(interface_t * interface, void * cfg) { if (!interface->ops->initialize) - return FACEMGR_FAILURE; - return interface->ops->initialize(interface, rules, &interface->data); + return -1; + return interface->ops->initialize(interface, cfg); } int interface_finalize(interface_t * interface) { if (!interface->ops->finalize) - return FACEMGR_FAILURE; + return -1; return interface->ops->finalize(interface); } int -interface_on_event(interface_t * interface, const event_t * event) +interface_on_event(interface_t * interface, const facelet_t * facelet) { if (!interface->ops->on_event) - return FACEMGR_FAILURE; - return interface->ops->on_event(interface, event); + return -1; + return interface->ops->on_event(interface, facelet); } diff --git a/ctrl/facemgr/src/interface.h b/ctrl/facemgr/src/interface.h index f38313182..331312bde 100644 --- a/ctrl/facemgr/src/interface.h +++ b/ctrl/facemgr/src/interface.h @@ -30,27 +30,30 @@ #include <stdbool.h> -struct event_s; -typedef int (*callback_t)(struct event_s * event, void * callback_data); +struct facelet_s; +typedef int (*callback_t)(struct facelet_s * facelet, void * callback_data); struct interface_s; -struct face_rules_s; /** * \brief Interface operations */ typedef struct { + /** The type given to the interfaces */ char * type; - bool is_singleton; - int (*initialize)(struct interface_s * interface, struct face_rules_s * rules, void ** pdata); + /* Constructor */ + int (*initialize)(struct interface_s * interface, void * cfg); + /* Destructor */ int (*finalize)(struct interface_s * interface); + /* Callback upon file descriptor event (iif previously registered) */ int (*callback)(struct interface_s * interface); - int (*on_event)(struct interface_s * interface, const struct event_s * event); + /* Callback upon face events coming from the face manager */ + int (*on_event)(struct interface_s * interface, const struct facelet_s * facelet); } interface_ops_t; typedef struct interface_s { char * name; - interface_ops_t * ops; + const interface_ops_t * ops; callback_t callback; void * callback_data; void * data; @@ -90,9 +93,9 @@ void _interface_set_callback(interface_t * interface, callback_t callback, void #define interface_set_callback(interface, callback, callback_data) \ _interface_set_callback(interface, (callback_t)callback, (void*)callback_data) -int interface_initialize(interface_t * interface, struct face_rules_s * rules); +int interface_initialize(interface_t * interface, void * cfg); int interface_finalize(interface_t * interface); -int interface_on_event(interface_t * interface, const struct event_s * event); +int interface_on_event(interface_t * interface, const struct facelet_s * facelet); #endif /* FACEMGR_INTERFACE_H */ diff --git a/ctrl/facemgr/src/interface_map.c b/ctrl/facemgr/src/interface_map.c deleted file mode 100644 index 9f7c20cab..000000000 --- a/ctrl/facemgr/src/interface_map.c +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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. - */ - -#include "interface_map.h" - -#include <string.h> -#include "interface.h" - -TYPEDEF_MAP(interface_map, const char *, interface_t *, strcmp, string_snprintf, generic_snprintf); diff --git a/ctrl/facemgr/src/interface_map.h b/ctrl/facemgr/src/interface_map.h deleted file mode 100644 index 5930b3001..000000000 --- a/ctrl/facemgr/src/interface_map.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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. - */ - -#ifndef INTERFACE_MAP_H -#define INTERFACE_MAP_H - -#include "interface.h" -#include "util/map.h" - -TYPEDEF_MAP_H(interface_map, const char *, interface_t *); - -#endif /* INTERFACE_MAP_H */ diff --git a/ctrl/facemgr/src/interface_ops_map.c b/ctrl/facemgr/src/interface_ops_map.c deleted file mode 100644 index 373f5d22f..000000000 --- a/ctrl/facemgr/src/interface_ops_map.c +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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. - */ - -#include "interface_ops_map.h" - -#include <string.h> -#include "util/map.h" - -TYPEDEF_MAP(interface_ops_map, const char *, interface_ops_t *, strcmp, string_snprintf, generic_snprintf); diff --git a/ctrl/facemgr/src/interface_ops_map.h b/ctrl/facemgr/src/interface_ops_map.h deleted file mode 100644 index 2d590390a..000000000 --- a/ctrl/facemgr/src/interface_ops_map.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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. - */ - -#ifndef INTERFACE_OPS_MAP_H -#define INTERFACE_OPS_MAP_H - -#include "interface.h" -#include "util/map.h" - -TYPEDEF_MAP_H(interface_ops_map, const char *, interface_ops_t *); - -#endif /* INTERFACE_OPS_MAP_H */ diff --git a/ctrl/facemgr/src/interfaces/CMakeLists.txt b/ctrl/facemgr/src/interfaces/CMakeLists.txt index e5a26177a..8d079612a 100644 --- a/ctrl/facemgr/src/interfaces/CMakeLists.txt +++ b/ctrl/facemgr/src/interfaces/CMakeLists.txt @@ -24,12 +24,21 @@ endif() if(LINUX) add_subdirectory(netlink) +add_subdirectory(bonjour) endif() -if(false) +if(ANDROID) +add_subdirectory(android_utility) +endif() + +if(WITH_EXAMPLE_DUMMY) add_subdirectory(dummy) endif() +if(WITH_EXAMPLE_UPDOWN) +add_subdirectory(updown) +endif() + set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) set(INCLUDE_DIRS ${INCLUDE_DIRS} PARENT_SCOPE) diff --git a/ctrl/facemgr/src/interfaces/android_utility/CMakeLists.txt b/ctrl/facemgr/src/interfaces/android_utility/CMakeLists.txt new file mode 100644 index 000000000..0ebe87745 --- /dev/null +++ b/ctrl/facemgr/src/interfaces/android_utility/CMakeLists.txt @@ -0,0 +1,27 @@ +# 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. + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/android_utility.c +) + +list(APPEND INCLUDE_DIRS +) + +list(APPEND LIBRARIES +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) +set(INCLUDE_DIRS ${INCLUDE_DIRS} PARENT_SCOPE) +set(LIBRARIES ${LIBRARIES} PARENT_SCOPE) diff --git a/ctrl/facemgr/src/interfaces/android_utility/android_utility.c b/ctrl/facemgr/src/interfaces/android_utility/android_utility.c new file mode 100644 index 000000000..bb612507f --- /dev/null +++ b/ctrl/facemgr/src/interfaces/android_utility/android_utility.c @@ -0,0 +1,138 @@ +/* + * 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 interfaces/android_utility/android_utility.c + * \brief Implementation of Android utility. + */ + +#include <assert.h> + +#include <hicn/facemgr.h> +#include <hicn/ctrl/face.h> +#include <hicn/util/log.h> +#include "../../common.h" +#include "../../facelet.h" +#include "../../interface.h" +#include <hicn/android_utility/android_utility.h> + +#define FACEMGR_ANDROID_UTILITY_CLASS "com/cisco/hicn/forwarder/supportlibrary/AndroidUtility" + +#define AU_INTERFACE_TYPE_UNDEFINED 0 +#define AU_INTERFACE_TYPE_WIRED 1 +#define AU_INTERFACE_TYPE_WIFI 2 +#define AU_INTERFACE_TYPE_CELLULAR 3 +#define AU_INTERFACE_TYPE_LOOPBACK 4 /* not supported yet */ + +#define ERR_STR_JAVA "Java VM parameters are required in the interface configuration." + +typedef struct { + android_utility_cfg_t cfg; +} au_data_t; + +int au_initialize(interface_t * interface, void * cfg) +{ + au_data_t * data = malloc(sizeof(au_data_t)); + if (!data) + return -1; + interface->data = data; + + if (!cfg) + goto ERR_CFG; + + data->cfg = * (android_utility_cfg_t *) cfg; + + if (!data->cfg.jvm) + goto ERR_CFG; + + return 0; + +ERR_CFG: + fprintf(stderr, ERR_STR_JAVA); + return -1; +} + +int au_finalize(interface_t * interface) +{ + /* Nothing to do */ + return 0; +} + +int au_on_event(interface_t * interface, const facelet_t * facelet) +{ + /* + * This function is responsible to annotate every face we receive with the + * correct interface type, based on the value returned by the Android + * utility shipped with the Android forwarder. + */ + DEBUG("Android utility received request"); + au_data_t * data = (au_data_t*)interface->data; + + netdevice_t netdevice = NETDEVICE_EMPTY; + int rc = facelet_get_netdevice(facelet, &netdevice); + if (rc < 0) + return -1; + DEBUG("[au_on_event] netdevice=%s", netdevice.name); + + JNIEnv *env; + JavaVM *jvm = data->cfg.jvm; + (*jvm)->AttachCurrentThread(jvm, &env, NULL); + jclass cls = (*env)->FindClass(env, FACEMGR_ANDROID_UTILITY_CLASS); + jmethodID getNetworkType = (*env)->GetStaticMethodID(env, cls, + "getNetworkType", "(Ljava/lang/String;)I"); + jint interface_type = (*env)->CallStaticIntMethod(env, cls, getNetworkType, + (*env)->NewStringUTF(env, netdevice.name)); + + DEBUG("Processing results for interface %s", netdevice.name); + + netdevice_type_t netdevice_type = AU_INTERFACE_TYPE_UNDEFINED; + switch(interface_type) { + case AU_INTERFACE_TYPE_UNDEFINED: + break; + case AU_INTERFACE_TYPE_WIRED: + netdevice_type = NETDEVICE_TYPE_WIRED; + break; + case AU_INTERFACE_TYPE_WIFI: + netdevice_type = NETDEVICE_TYPE_WIFI; + break; + case AU_INTERFACE_TYPE_CELLULAR: + netdevice_type = NETDEVICE_TYPE_CELLULAR; + break; + case AU_INTERFACE_TYPE_LOOPBACK: + netdevice_type = NETDEVICE_TYPE_LOOPBACK; + break; + default: + return -1; + } + + facelet_t * facelet_new = facelet_create(); + facelet_set_netdevice(facelet_new, netdevice); + facelet_set_status(facelet_new, FACELET_STATUS_CLEAN); + facelet_set_netdevice_type(facelet_new, netdevice_type); + + DEBUG("sending AU udpate"); + facelet_set_event(facelet_new, FACELET_EVENT_UPDATE); + facelet_raise_event(facelet_new, interface); + + return 0; +} + +const interface_ops_t android_utility_ops = { + .type = "android_utility", + .initialize = au_initialize, + .finalize = au_finalize, + .callback = NULL, + .on_event = au_on_event, +}; diff --git a/ctrl/facemgr/src/interfaces/bonjour/CMakeLists.txt b/ctrl/facemgr/src/interfaces/bonjour/CMakeLists.txt new file mode 100644 index 000000000..8a0ddc888 --- /dev/null +++ b/ctrl/facemgr/src/interfaces/bonjour/CMakeLists.txt @@ -0,0 +1,32 @@ +# 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. + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/bonjour.h + ${CMAKE_CURRENT_SOURCE_DIR}/mdns/mdns.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/bonjour.c +) + +list(APPEND LIBRARIES +) + +list(APPEND INCLUDE_DIRS +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) +set(INCLUDE_DIRS ${INCLUDE_DIRS} PARENT_SCOPE) +set(LIBRARIES ${LIBRARIES} PARENT_SCOPE) diff --git a/ctrl/facemgr/src/interfaces/bonjour/bonjour.c b/ctrl/facemgr/src/interfaces/bonjour/bonjour.c new file mode 100644 index 000000000..d7b27b995 --- /dev/null +++ b/ctrl/facemgr/src/interfaces/bonjour/bonjour.c @@ -0,0 +1,408 @@ +/* + * 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 interfaces/bonjour/bonjour.c + * \brief Implementation of Bonjour interface + * + * TODO: + * - concurrent queries + * - interface binding + */ + +#include <hicn/facemgr.h> +#include <hicn/util/log.h> + +#include "../../common.h" +#include "../../facelet.h" +#include "../../interface.h" +#include "../../util/map.h" +#include "mdns/mdns.h" + +#include "bonjour.h" + +#define DEFAULT_BUFFER_SIZE 2048 +#define SERVICE_STRING_SIZE 256 + +#define DEFAULT_SERVICE_NAME "hicn" +#define DEFAULT_SERVICE_PROTOCOL "udp" +#define DEFAULT_SERVICE_DOMAIN "local" + +typedef struct { + bonjour_cfg_t cfg; + int sock; + size_t buffer_size; + void* buffer; + + /* The face being resolved, non-NULL values indicate interface is busy... */ + face_t * face; +} bj_data_t; + +int bj_initialize(interface_t * interface, void * cfg) +{ + bj_data_t * data = malloc(sizeof(bj_data_t)); + if (!data) + goto ERR_MALLOC; + interface->data = data; + + if (cfg) { +#ifndef __linux__ + if (cfg->netdevice) + WARN("Binding to interface is (currently) only supported on Linux"); +#endif /* ! __linux__ */ + data->cfg = * (bonjour_cfg_t *) cfg; + } else { + memset(&data->cfg, 0, sizeof(bonjour_cfg_t)); + } + + if (!data->cfg.service_name) + data->cfg.service_name = DEFAULT_SERVICE_NAME; + + if (!data->cfg.service_protocol) + data->cfg.service_protocol = DEFAULT_SERVICE_PROTOCOL; + + if (!data->cfg.service_domain) + data->cfg.service_domain = DEFAULT_SERVICE_DOMAIN; + + data->sock = mdns_socket_open_ipv4(); + if (data->sock < 0) { + printf("Failed to open socket: %s\n", strerror(errno)); + goto ERR_SOCK; + } + + /* Netdevice configuration */ +#ifdef __linux__ +#ifndef __ANDROID__ + if (IS_VALID_NETDEVICE(data->cfg.netdevice)) { + int rc = setsockopt(data->sock, SOL_SOCKET, SO_BINDTODEVICE, + &data->cfg.netdevice.name, strlen(data->cfg.netdevice.name) + 1); + if (rc == -1) { + ERROR("setsockopt"); + goto ERR_SOCK_OPT; + } + } +#endif +#endif /* __linux__ */ + + data->buffer_size = DEFAULT_BUFFER_SIZE; + data->buffer = malloc(data->buffer_size); + if (!data->buffer) + goto ERR_BUFFER; + +#ifdef _WIN32 + WORD versionWanted = MAKEWORD(1, 1); + WSADATA wsaData; + WSAStartup(versionWanted, &wsaData); +#endif + + return data->sock; + +ERR_BUFFER: +#ifndef __ANDROID__ +ERR_SOCK_OPT: +#endif + mdns_socket_close(data->sock); +#ifdef _WIN32 + WSACleanup(); +#endif +ERR_SOCK: + free(data); +ERR_MALLOC: + return -1; +} + +/* + * We reuse the callback to be triggered upon external events + * TODO: move to a cleaner interface architecture later... + */ +int bj_on_event(interface_t * interface, const facelet_t * facelet) +{ + bj_data_t * data = (bj_data_t*)interface->data; + + /* + printf("Sending DNS-SD discovery\n"); + if (mdns_discovery_send(sock)) { + printf("Failed to send DNS-DS discovery: %s\n", strerror(errno)); + goto quit; + } + + printf("Reading DNS-SD replies\n"); + for (int i = 0; i < 10; ++i) { + records = mdns_discovery_recv(sock, buffer, capacity, callback, + user_data); + sleep(1); + } + */ + + DEBUG("Sending mDNS query"); + char service_string[SERVICE_STRING_SIZE]; + + int rc = snprintf(service_string, SERVICE_STRING_SIZE, "_%s._%s.%s.", + data->cfg.service_name, data->cfg.service_protocol, + data->cfg.service_domain); + if (rc < 0) + ; // error + else if (rc >= SERVICE_STRING_SIZE) + ; //truncated + + if (mdns_query_send(data->sock, MDNS_RECORDTYPE_PTR, + service_string, + strlen(service_string), + data->buffer, data->buffer_size)) { + printf("Failed to send mDNS query: %s\n", strerror(errno)); + return -1; + } + return 0; +} + +static char addrbuffer[64]; +static char namebuffer[256]; +static mdns_record_txt_t txtbuffer[128]; + +static mdns_string_t +ipv4_address_to_string(char* buffer, size_t capacity, const struct sockaddr_in* addr) { + char host[NI_MAXHOST] = {0}; + char service[NI_MAXSERV] = {0}; + int ret = getnameinfo((const struct sockaddr*)addr, sizeof(struct sockaddr_in), + host, NI_MAXHOST, service, NI_MAXSERV, + NI_NUMERICSERV | NI_NUMERICHOST); + int len = 0; + if (ret == 0) { + if (addr->sin_port != 0) + len = snprintf(buffer, capacity, "%s:%s", host, service); + else + len = snprintf(buffer, capacity, "%s", host); + } + if (len >= (int)capacity) + len = (int)capacity - 1; + mdns_string_t str = {buffer, len}; + return str; +} + +static mdns_string_t +ipv6_address_to_string(char* buffer, size_t capacity, const struct sockaddr_in6* addr) { + char host[NI_MAXHOST] = {0}; + char service[NI_MAXSERV] = {0}; + int ret = getnameinfo((const struct sockaddr*)addr, sizeof(struct sockaddr_in6), + host, NI_MAXHOST, service, NI_MAXSERV, + NI_NUMERICSERV | NI_NUMERICHOST); + int len = 0; + if (ret == 0) { + if (addr->sin6_port != 0) + len = snprintf(buffer, capacity, "[%s]:%s", host, service); + else + len = snprintf(buffer, capacity, "%s", host); + } + if (len >= (int)capacity) + len = (int)capacity - 1; + mdns_string_t str = {buffer, len}; + return str; +} + +static mdns_string_t +ip_address_to_string(char* buffer, size_t capacity, const struct sockaddr* addr) { + if (addr->sa_family == AF_INET6) + return ipv6_address_to_string(buffer, capacity, (const struct sockaddr_in6*)addr); + return ipv4_address_to_string(buffer, capacity, (const struct sockaddr_in*)addr); +} + +int +ip_address_set_sockaddr(ip_address_t * ip_address, struct sockaddr * sa) +{ + switch(sa->sa_family) { + case AF_INET: + ip_address->v4.as_inaddr = ((struct sockaddr_in *)sa)->sin_addr; + break; + case AF_INET6: + ip_address->v6.as_in6addr = ((struct sockaddr_in6 *)sa)->sin6_addr; + break; + default: + return -1; + } + + return 0; +} + +static int +callback(const struct sockaddr* from, mdns_entry_type_t entry, uint16_t type, + uint16_t rclass, uint32_t ttl, const void* data, size_t size, size_t + offset, size_t length, void* user_data) +{ + interface_t * interface = (interface_t*)user_data; + bj_data_t * bj_data = (bj_data_t *)interface->data; + + struct sockaddr_storage addr; + + mdns_string_t fromaddrstr = ip_address_to_string(addrbuffer, sizeof(addrbuffer), from); + const char* entrytype = (entry == MDNS_ENTRYTYPE_ANSWER) ? "answer" : + ((entry == MDNS_ENTRYTYPE_AUTHORITY) ? "authority" : "additional"); + + switch(type) { + case MDNS_RECORDTYPE_A: + { + ip_address_t ip_address; + mdns_record_parse_a(data, size, offset, length, (struct sockaddr_in*)&addr); + ip_address_set_sockaddr(&ip_address, (struct sockaddr *)&addr); + + mdns_string_t addrstr = ipv4_address_to_string(namebuffer, sizeof(namebuffer), (struct sockaddr_in *)&addr); + DEBUG("%.*s : %s A %.*s", + MDNS_STRING_FORMAT(fromaddrstr), entrytype, + MDNS_STRING_FORMAT(addrstr)); + + facelet_t * facelet = facelet_create(); + facelet_set_netdevice(facelet, bj_data->cfg.netdevice); + facelet_set_family(facelet, AF_INET); + facelet_set_remote_addr(facelet, ip_address); + //facelet_set_remote_port(facelet, ((struct sockaddr_in*)&addr)->sin_port); + + facelet_set_event(facelet, FACELET_EVENT_UPDATE); + facelet_raise_event(facelet, interface); + break; + } + + case MDNS_RECORDTYPE_AAAA: + { + ip_address_t ip_address; + mdns_record_parse_aaaa(data, size, offset, length, (struct sockaddr_in6*)&addr); + ip_address_set_sockaddr(&ip_address, (struct sockaddr *)&addr); + + mdns_string_t addrstr = ipv6_address_to_string(namebuffer, + sizeof(namebuffer), (struct sockaddr_in6*)&addr); + DEBUG("%.*s : %s AAAA %.*s", + MDNS_STRING_FORMAT(fromaddrstr), entrytype, + MDNS_STRING_FORMAT(addrstr)); + + facelet_t * facelet = facelet_create(); + facelet_set_netdevice(facelet, bj_data->cfg.netdevice); + facelet_set_family(facelet, AF_INET6); + facelet_set_remote_addr(facelet, ip_address); + //facelet_set_remote_port(facelet, ((struct sockaddr_in6*)&addr)->sin6_port); + + facelet_set_event(facelet, FACELET_EVENT_UPDATE); + facelet_raise_event(facelet, interface); + break; + } + + case MDNS_RECORDTYPE_SRV: /* same port for both v4 and v6 */ + { + mdns_record_srv_t srv = mdns_record_parse_srv(data, size, offset, length, + namebuffer, sizeof(namebuffer)); + + DEBUG("%.*s : %s SRV %.*s priority %d weight %d port %d", + MDNS_STRING_FORMAT(fromaddrstr), entrytype, + MDNS_STRING_FORMAT(srv.name), srv.priority, srv.weight, srv.port); + + /* We raise both v4 and v6 + * + * Unless we choose whether we query A and/or AAAA, this might leave + * us with an unused pending facelet, eg. we might not have an IPv6 + * but we raise an IPv6 bonjour event... + */ + + facelet_t * facelet = facelet_create(); + facelet_set_netdevice(facelet, bj_data->cfg.netdevice); + facelet_set_family(facelet, AF_INET); + facelet_set_remote_port(facelet, srv.port); + + facelet_set_event(facelet, FACELET_EVENT_UPDATE); + facelet_raise_event(facelet, interface); + + facelet = facelet_create(); + facelet_set_netdevice(facelet, bj_data->cfg.netdevice); + facelet_set_family(facelet, AF_INET6); + facelet_set_remote_port(facelet, srv.port); + + facelet_set_event(facelet, FACELET_EVENT_UPDATE); + facelet_raise_event(facelet, interface); + break; + } + + case MDNS_RECORDTYPE_PTR: + { + mdns_string_t namestr = mdns_record_parse_ptr(data, size, offset, length, + namebuffer, sizeof(namebuffer)); + DEBUG("%.*s : %s PTR %.*s type %u rclass 0x%x ttl %u length %d", + MDNS_STRING_FORMAT(fromaddrstr), entrytype, + MDNS_STRING_FORMAT(namestr), type, rclass, ttl, (int)length); + break; + } + + case MDNS_RECORDTYPE_TXT: + { + size_t parsed = mdns_record_parse_txt(data, size, offset, length, + txtbuffer, sizeof(txtbuffer) / sizeof(mdns_record_txt_t)); + for (size_t itxt = 0; itxt < parsed; ++itxt) { + if (txtbuffer[itxt].value.length) { + DEBUG("%.*s : %s TXT %.*s = %.*s", + MDNS_STRING_FORMAT(fromaddrstr), entrytype, + MDNS_STRING_FORMAT(txtbuffer[itxt].key), + MDNS_STRING_FORMAT(txtbuffer[itxt].value)); + } + else { + DEBUG("%.*s : %s TXT %.*s", + MDNS_STRING_FORMAT(fromaddrstr), entrytype, + MDNS_STRING_FORMAT(txtbuffer[itxt].key)); + } + } + break; + } + + default: + /* Silently ignore the received record */ + DEBUG("%.*s : %s type %u rclass 0x%x ttl %u length %d", + MDNS_STRING_FORMAT(fromaddrstr), entrytype, + type, rclass, ttl, (int)length); + return 0; + } + return 0; + +} + +/* + * The fact we use a single fd does not allow us to get user_data associated to + * the query. + */ +int bj_callback(interface_t * interface) +{ + bj_data_t * data = (bj_data_t*)interface->data; + DEBUG("Got an mDNS reply"); + /* size_t records = */ mdns_query_recv(data->sock, data->buffer, data->buffer_size, callback, interface, 1); + + return 0; +} + +int bj_finalize(interface_t * interface) +{ + bj_data_t * data = (bj_data_t*)interface->data; + + free(data->buffer); + mdns_socket_close(data->sock); + +#ifdef _WIN32 + WSACleanup(); +#endif + + return 0; + +} + +const interface_ops_t bonjour_ops = { + .type = "bonjour", + .initialize = bj_initialize, + .on_event = bj_on_event, + .callback = bj_callback, + .finalize = bj_finalize, + // .on_event = NULL, +}; diff --git a/ctrl/facemgr/src/face_rules.h b/ctrl/facemgr/src/interfaces/bonjour/bonjour.h index 28f5391a0..fe053079d 100644 --- a/ctrl/facemgr/src/face_rules.h +++ b/ctrl/facemgr/src/interfaces/bonjour/bonjour.h @@ -13,19 +13,23 @@ * limitations under the License. */ -#ifndef FACE_RULES_H -#define FACE_RULES_H - -#include "util/map.h" -#include "util/policy.h" - -/* - * Face creation rules +/** + * \file interfaces/bonjour/bonjour.h + * \brief Bonjour interface * - * For now, face creations rules are very simple and consist in a map between - * the physical interface name, and the associated list of tags that will - * preempt those assigned by the system. + * NOTES: + * - shall we support multiple service names, or instanciate multiple instances + * of the interface ? + * - interface list ? + * - ideally we should register here events that will trigger bonjour + * queries... */ -TYPEDEF_MAP_H(face_rules, const char *, policy_tags_t); -#endif /* FACE_RULES_H */ +#include <hicn/ctrl/face.h> /* netdevice_t */ + +typedef struct { + netdevice_t netdevice; + char * service_name; + char * service_protocol; + char * service_domain; +} bonjour_cfg_t; diff --git a/ctrl/facemgr/src/interfaces/bonjour/mdns/LICENSE b/ctrl/facemgr/src/interfaces/bonjour/mdns/LICENSE new file mode 100644 index 000000000..cf1ab25da --- /dev/null +++ b/ctrl/facemgr/src/interfaces/bonjour/mdns/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to <http://unlicense.org> diff --git a/ctrl/facemgr/src/interfaces/bonjour/mdns/README.md b/ctrl/facemgr/src/interfaces/bonjour/mdns/README.md new file mode 100644 index 000000000..1bee49c08 --- /dev/null +++ b/ctrl/facemgr/src/interfaces/bonjour/mdns/README.md @@ -0,0 +1,9 @@ +# Public domain mDNS/DNS-SD library in C + +This library provides a cross-platform mDNS and DNS-DS library in C. The latest source code is always available at + +https://github.com/mjansson/mdns + +This library is put in the public domain; you can redistribute it and/or modify it without any restrictions. + +Created by Mattias Jansson ([@maniccoder](https://twitter.com/maniccoder)) diff --git a/ctrl/facemgr/src/interfaces/bonjour/mdns/mdns.c b/ctrl/facemgr/src/interfaces/bonjour/mdns/mdns.c new file mode 100644 index 000000000..a8e97e8e0 --- /dev/null +++ b/ctrl/facemgr/src/interfaces/bonjour/mdns/mdns.c @@ -0,0 +1,192 @@ + +#ifdef _WIN32 +# define _CRT_SECURE_NO_WARNINGS 1 +#endif + +#include "mdns.h" + +#include <stdio.h> +#include <errno.h> + +#ifdef _WIN32 +# define sleep(x) Sleep(x * 1000) +#else +# include <netdb.h> +#endif + +static char addrbuffer[64]; +static char namebuffer[256]; +static mdns_record_txt_t txtbuffer[128]; + +static mdns_string_t +ipv4_address_to_string(char* buffer, size_t capacity, const struct sockaddr_in* addr) { + char host[NI_MAXHOST] = {0}; + char service[NI_MAXSERV] = {0}; + int ret = getnameinfo((const struct sockaddr*)addr, sizeof(struct sockaddr_in), + host, NI_MAXHOST, service, NI_MAXSERV, + NI_NUMERICSERV | NI_NUMERICHOST); + int len = 0; + if (ret == 0) { + if (addr->sin_port != 0) + len = snprintf(buffer, capacity, "%s:%s", host, service); + else + len = snprintf(buffer, capacity, "%s", host); + } + if (len >= (int)capacity) + len = (int)capacity - 1; + mdns_string_t str = {buffer, len}; + return str; +} + +static mdns_string_t +ipv6_address_to_string(char* buffer, size_t capacity, const struct sockaddr_in6* addr) { + char host[NI_MAXHOST] = {0}; + char service[NI_MAXSERV] = {0}; + int ret = getnameinfo((const struct sockaddr*)addr, sizeof(struct sockaddr_in6), + host, NI_MAXHOST, service, NI_MAXSERV, + NI_NUMERICSERV | NI_NUMERICHOST); + int len = 0; + if (ret == 0) { + if (addr->sin6_port != 0) + len = snprintf(buffer, capacity, "[%s]:%s", host, service); + else + len = snprintf(buffer, capacity, "%s", host); + } + if (len >= (int)capacity) + len = (int)capacity - 1; + mdns_string_t str = {buffer, len}; + return str; +} + +static mdns_string_t +ip_address_to_string(char* buffer, size_t capacity, const struct sockaddr* addr) { + if (addr->sa_family == AF_INET6) + return ipv6_address_to_string(buffer, capacity, (const struct sockaddr_in6*)addr); + return ipv4_address_to_string(buffer, capacity, (const struct sockaddr_in*)addr); +} + +static int +callback(const struct sockaddr* from, + mdns_entry_type_t entry, uint16_t type, + uint16_t rclass, uint32_t ttl, + const void* data, size_t size, size_t offset, size_t length, + void* user_data) { + mdns_string_t fromaddrstr = ip_address_to_string(addrbuffer, sizeof(addrbuffer), from); + const char* entrytype = (entry == MDNS_ENTRYTYPE_ANSWER) ? "answer" : + ((entry == MDNS_ENTRYTYPE_AUTHORITY) ? "authority" : "additional"); + if (type == MDNS_RECORDTYPE_PTR) { + mdns_string_t namestr = mdns_record_parse_ptr(data, size, offset, length, + namebuffer, sizeof(namebuffer)); + INFO("%.*s : %s PTR %.*s type %u rclass 0x%x ttl %u length %d\n", + MDNS_STRING_FORMAT(fromaddrstr), entrytype, + MDNS_STRING_FORMAT(namestr), type, rclass, ttl, (int)length); + } + else if (type == MDNS_RECORDTYPE_SRV) { + mdns_record_srv_t srv = mdns_record_parse_srv(data, size, offset, length, + namebuffer, sizeof(namebuffer)); + INFO("%.*s : %s SRV %.*s priority %d weight %d port %d\n", + MDNS_STRING_FORMAT(fromaddrstr), entrytype, + MDNS_STRING_FORMAT(srv.name), srv.priority, srv.weight, srv.port); + } + else if (type == MDNS_RECORDTYPE_A) { + struct sockaddr_in addr; + mdns_record_parse_a(data, size, offset, length, &addr); + mdns_string_t addrstr = ipv4_address_to_string(namebuffer, sizeof(namebuffer), &addr); + INFO("%.*s : %s A %.*s\n", + MDNS_STRING_FORMAT(fromaddrstr), entrytype, + MDNS_STRING_FORMAT(addrstr)); + } + else if (type == MDNS_RECORDTYPE_AAAA) { + struct sockaddr_in6 addr; + mdns_record_parse_aaaa(data, size, offset, length, &addr); + mdns_string_t addrstr = ipv6_address_to_string(namebuffer, sizeof(namebuffer), &addr); + INFO("%.*s : %s AAAA %.*s\n", + MDNS_STRING_FORMAT(fromaddrstr), entrytype, + MDNS_STRING_FORMAT(addrstr)); + } + else if (type == MDNS_RECORDTYPE_TXT) { + size_t parsed = mdns_record_parse_txt(data, size, offset, length, + txtbuffer, sizeof(txtbuffer) / sizeof(mdns_record_txt_t)); + for (size_t itxt = 0; itxt < parsed; ++itxt) { + if (txtbuffer[itxt].value.length) { + INFO("%.*s : %s TXT %.*s = %.*s\n", + MDNS_STRING_FORMAT(fromaddrstr), entrytype, + MDNS_STRING_FORMAT(txtbuffer[itxt].key), + MDNS_STRING_FORMAT(txtbuffer[itxt].value)); + } + else { + INFO("%.*s : %s TXT %.*s\n", + MDNS_STRING_FORMAT(fromaddrstr), entrytype, + MDNS_STRING_FORMAT(txtbuffer[itxt].key)); + } + } + } + else { + INFO("%.*s : %s type %u rclass 0x%x ttl %u length %d\n", + MDNS_STRING_FORMAT(fromaddrstr), entrytype, + type, rclass, ttl, (int)length); + } + return 0; +} + +int +main() { + size_t capacity = 2048; + void* buffer = 0; + void* user_data = 0; + size_t records; + +#ifdef _WIN32 + WORD versionWanted = MAKEWORD(1, 1); + WSADATA wsaData; + WSAStartup(versionWanted, &wsaData); +#endif + + int sock = mdns_socket_open_ipv4(); + if (sock < 0) { + INFO("Failed to open socket: %s\n", strerror(errno)); + return -1; + } + INFO("Opened IPv4 socket for mDNS/DNS-SD\n"); + buffer = malloc(capacity); +/* + INFO("Sending DNS-SD discovery\n"); + if (mdns_discovery_send(sock)) { + INFO("Failed to send DNS-DS discovery: %s\n", strerror(errno)); + goto quit; + } + + INFO("Reading DNS-SD replies\n"); + for (int i = 0; i < 10; ++i) { + records = mdns_discovery_recv(sock, buffer, capacity, callback, + user_data); + sleep(1); + } + */ + + INFO("Sending mDNS query\n"); + if (mdns_query_send(sock, MDNS_RECORDTYPE_PTR, + MDNS_STRING_CONST("_hicn._udp.local."), + buffer, capacity)) { + INFO("Failed to send mDNS query: %s\n", strerror(errno)); + goto quit; + } + + INFO("Reading mDNS replies\n"); + for (int i = 0; i < 10; ++i) { + records = mdns_query_recv(sock, buffer, capacity, callback, user_data, 1); + sleep(1); + } + +quit: + free(buffer); + + mdns_socket_close(sock); + INFO("Closed socket\n"); + +#ifdef _WIN32 + WSACleanup(); +#endif + + return 0; +} diff --git a/ctrl/facemgr/src/interfaces/bonjour/mdns/mdns.h b/ctrl/facemgr/src/interfaces/bonjour/mdns/mdns.h new file mode 100644 index 000000000..ff04b5d72 --- /dev/null +++ b/ctrl/facemgr/src/interfaces/bonjour/mdns/mdns.h @@ -0,0 +1,879 @@ +/* mdns.h - mDNS/DNS-SD library - Public Domain - 2017 Mattias Jansson + * + * This library provides a cross-platform mDNS and DNS-SD library in C. + * The implementation is based on RFC 6762 and RFC 6763. + * + * The latest source code is always available at + * + * https://github.com/mjansson/mdns + * + * This library is put in the public domain; you can redistribute it and/or modify it without any restrictions. + * + */ + +#pragma once + +#include <stdint.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#include <fcntl.h> +#ifdef _WIN32 +#include <Winsock2.h> +#include <Ws2tcpip.h> +#define strncasecmp _strnicmp +#else +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#endif + +#define MDNS_INVALID_POS ((size_t)-1) + +#define MDNS_STRING_CONST(s) (s), (sizeof((s))-1) +#define MDNS_STRING_FORMAT(s) (int)((s).length), s.str + +enum mdns_record_type { + MDNS_RECORDTYPE_IGNORE = 0, + //Address + MDNS_RECORDTYPE_A = 1, + //Domain Name pointer + MDNS_RECORDTYPE_PTR = 12, + //Arbitrary text string + MDNS_RECORDTYPE_TXT = 16, + //IP6 Address [Thomson] + MDNS_RECORDTYPE_AAAA = 28, + //Server Selection [RFC2782] + MDNS_RECORDTYPE_SRV = 33 +}; + +enum mdns_entry_type { + MDNS_ENTRYTYPE_ANSWER = 1, + MDNS_ENTRYTYPE_AUTHORITY = 2, + MDNS_ENTRYTYPE_ADDITIONAL = 3 +}; + +enum mdns_class { + MDNS_CLASS_IN = 1 +}; + +typedef enum mdns_record_type mdns_record_type_t; +typedef enum mdns_entry_type mdns_entry_type_t; +typedef enum mdns_class mdns_class_t; + +typedef int (* mdns_record_callback_fn)(const struct sockaddr* from, + mdns_entry_type_t entry, uint16_t type, + uint16_t rclass, uint32_t ttl, + const void* data, size_t size, size_t offset, size_t length, + void* user_data); + +typedef struct mdns_string_t mdns_string_t; +typedef struct mdns_string_pair_t mdns_string_pair_t; +typedef struct mdns_record_srv_t mdns_record_srv_t; +typedef struct mdns_record_txt_t mdns_record_txt_t; + +struct mdns_string_t { + const char* str; + size_t length; +}; + +struct mdns_string_pair_t { + size_t offset; + size_t length; + int ref; +}; + +struct mdns_record_srv_t { + uint16_t priority; + uint16_t weight; + uint16_t port; + mdns_string_t name; +}; + +struct mdns_record_txt_t { + mdns_string_t key; + mdns_string_t value; +}; + +static int +mdns_socket_open_ipv4(void); + +static int +mdns_socket_setup_ipv4(int sock); + +#if 0 +static int +mdns_socket_open_ipv6(void); + + + +static int +mdns_socket_setup_ipv6(int sock); +#endif + +static void +mdns_socket_close(int sock); + +#if 0 +static int +mdns_discovery_send(int sock); + + +static size_t +mdns_discovery_recv(int sock, void* buffer, size_t capacity, + mdns_record_callback_fn callback, void* user_data); +#endif + +static int +mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t length, + void* buffer, size_t capacity); + +static size_t +mdns_query_recv(int sock, void* buffer, size_t capacity, + mdns_record_callback_fn callback, void* user_data, + uint8_t one_shot); + +static mdns_string_t +mdns_string_extract(const void* buffer, size_t size, size_t* offset, + char* str, size_t capacity); + +static int +mdns_string_skip(const void* buffer, size_t size, size_t* offset); + +#if 0 +static int +mdns_string_equal(const void* buffer_lhs, size_t size_lhs, size_t* ofs_lhs, + const void* buffer_rhs, size_t size_rhs, size_t* ofs_rhs); +#endif + +static void* +mdns_string_make(void* data, size_t capacity, const char* name, size_t length); + +static mdns_string_t +mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t length, + char* strbuffer, size_t capacity); + +static mdns_record_srv_t +mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t length, + char* strbuffer, size_t capacity); + +static struct sockaddr_in* +mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t length, + struct sockaddr_in* addr); + +static struct sockaddr_in6* +mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t length, + struct sockaddr_in6* addr); + +static size_t +mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t length, + mdns_record_txt_t* records, size_t capacity); + +// Implementations + +static int +mdns_socket_open_ipv4(void) { + int sock = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock < 0) + return -1; + if (mdns_socket_setup_ipv4(sock)) { + mdns_socket_close(sock); + return -1; + } + return sock; +} + +static int +mdns_socket_setup_ipv4(int sock) { + struct sockaddr_in saddr; + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = INADDR_ANY; +#ifdef __APPLE__ + saddr.sin_len = sizeof(saddr); +#endif + + if (bind(sock, (struct sockaddr*)&saddr, sizeof(saddr))) + return -1; + +#ifdef _WIN32 + unsigned long param = 1; + ioctlsocket(sock, FIONBIO, ¶m); +#else + const int flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, flags | O_NONBLOCK); +#endif + + unsigned char ttl = 1; + unsigned char loopback = 1; + struct ip_mreq req; + + setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, (const char*)&ttl, sizeof(ttl)); + setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, (const char*)&loopback, sizeof(loopback)); + + memset(&req, 0, sizeof(req)); + req.imr_multiaddr.s_addr = htonl((((uint32_t)224U) << 24U) | ((uint32_t)251U)); + req.imr_interface.s_addr = INADDR_ANY; + if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&req, sizeof(req))) + return -1; + + return 0; +} + +#if 0 +static int +mdns_socket_open_ipv6(void) { + int sock = (int)socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (sock < 0) + return -1; + if (mdns_socket_setup_ipv6(sock)) { + mdns_socket_close(sock); + return -1; + } + return sock; +} + + +static int +mdns_socket_setup_ipv6(int sock) { + struct sockaddr_in6 saddr; + memset(&saddr, 0, sizeof(saddr)); + saddr.sin6_family = AF_INET6; + saddr.sin6_addr = in6addr_any; +#ifdef __APPLE__ + saddr.sin6_len = sizeof(saddr); +#endif + + if (bind(sock, (struct sockaddr*)&saddr, sizeof(saddr))) + return -1; + +#ifdef _WIN32 + unsigned long param = 1; + ioctlsocket(sock, FIONBIO, ¶m); +#else + const int flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, flags | O_NONBLOCK); +#endif + + int hops = 1; + unsigned int loopback = 1; + struct ipv6_mreq req; + + setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char*)&hops, sizeof(hops)); + setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const char*)&loopback, sizeof(loopback)); + + memset(&req, 0, sizeof(req)); + req.ipv6mr_multiaddr.s6_addr[0] = 0xFF; + req.ipv6mr_multiaddr.s6_addr[1] = 0x02; + req.ipv6mr_multiaddr.s6_addr[15] = 0xFB; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char*)&req, sizeof(req))) + return -1; + + return 0; +} +#endif + +static void +mdns_socket_close(int sock) { +#ifdef _WIN32 + closesocket(sock); +#else + close(sock); +#endif +} + +static int +mdns_is_string_ref(uint8_t val) { + return (0xC0 == (val & 0xC0)); +} + +static mdns_string_pair_t +mdns_get_next_substring(const void* rawdata, size_t size, size_t offset) { + const uint8_t* buffer = rawdata; + mdns_string_pair_t pair = {MDNS_INVALID_POS, 0, 0}; + if (!buffer[offset]) { + pair.offset = offset; + return pair; + } + if (mdns_is_string_ref(buffer[offset])) { + if (size < offset + 2) + return pair; + + offset = (((size_t)(0x3f & buffer[offset]) << 8) | (size_t)buffer[offset + 1]); + if (offset >= size) + return pair; + + pair.ref = 1; + } + + size_t length = (size_t)buffer[offset++]; + if (size < offset + length) + return pair; + + pair.offset = offset; + pair.length = length; + + return pair; +} + +static int +mdns_string_skip(const void* buffer, size_t size, size_t* offset) { + size_t cur = *offset; + mdns_string_pair_t substr; + do { + substr = mdns_get_next_substring(buffer, size, cur); + if (substr.offset == MDNS_INVALID_POS) + return 0; + if (substr.ref) { + *offset = cur + 2; + return 1; + } + cur = substr.offset + substr.length; + } + while (substr.length); + + *offset = cur + 1; + return 1; +} + +#if 0 +static int +mdns_string_equal(const void* buffer_lhs, size_t size_lhs, size_t* ofs_lhs, + const void* buffer_rhs, size_t size_rhs, size_t* ofs_rhs) { + size_t lhs_cur = *ofs_lhs; + size_t rhs_cur = *ofs_rhs; + size_t lhs_end = MDNS_INVALID_POS; + size_t rhs_end = MDNS_INVALID_POS; + mdns_string_pair_t lhs_substr; + mdns_string_pair_t rhs_substr; + do { + lhs_substr = mdns_get_next_substring(buffer_lhs, size_lhs, lhs_cur); + rhs_substr = mdns_get_next_substring(buffer_rhs, size_rhs, rhs_cur); + if ((lhs_substr.offset == MDNS_INVALID_POS) || (rhs_substr.offset == MDNS_INVALID_POS)) + return 0; + if (lhs_substr.length != rhs_substr.length) + return 0; + if (strncasecmp((const char*)buffer_rhs + rhs_substr.offset, + (const char*)buffer_lhs + lhs_substr.offset, rhs_substr.length)) + return 0; + if (lhs_substr.ref && (lhs_end == MDNS_INVALID_POS)) + lhs_end = lhs_cur + 2; + if (rhs_substr.ref && (rhs_end == MDNS_INVALID_POS)) + rhs_end = rhs_cur + 2; + lhs_cur = lhs_substr.offset + lhs_substr.length; + rhs_cur = rhs_substr.offset + rhs_substr.length; + } + while (lhs_substr.length); + + if (lhs_end == MDNS_INVALID_POS) + lhs_end = lhs_cur + 1; + *ofs_lhs = lhs_end; + + if (rhs_end == MDNS_INVALID_POS) + rhs_end = rhs_cur + 1; + *ofs_rhs = rhs_end; + + return 1; +} +#endif + +static mdns_string_t +mdns_string_extract(const void* buffer, size_t size, size_t* offset, + char* str, size_t capacity) { + size_t cur = *offset; + size_t end = MDNS_INVALID_POS; + mdns_string_pair_t substr; + mdns_string_t result = {str, 0}; + char* dst = str; + size_t remain = capacity; + do { + substr = mdns_get_next_substring(buffer, size, cur); + if (substr.offset == MDNS_INVALID_POS) + return result; + if (substr.ref && (end == MDNS_INVALID_POS)) + end = cur + 2; + if (substr.length) { + size_t to_copy = (substr.length < remain) ? substr.length : remain; + memcpy(dst, (const char*)buffer + substr.offset, to_copy); + dst += to_copy; + remain -= to_copy; + if (remain) { + *dst++ = '.'; + --remain; + } + } + cur = substr.offset + substr.length; + } + while (substr.length); + + if (end == MDNS_INVALID_POS) + end = cur + 1; + *offset = end; + + result.length = capacity - remain; + return result; +} + +static size_t +mdns_string_find(const char* str, size_t length, char c, size_t offset) { + const void* found; + if (offset >= length) + return MDNS_INVALID_POS; + found = memchr(str + offset, c, length - offset); + if (found) + return (size_t)((const char*)found - str); + return MDNS_INVALID_POS; +} + +static void* +mdns_string_make(void* data, size_t capacity, const char* name, size_t length) { + size_t pos = 0; + size_t last_pos = 0; + size_t remain = capacity; + unsigned char* dest = data; + while ((last_pos < length) && ((pos = mdns_string_find(name, length, '.', last_pos)) != MDNS_INVALID_POS)) { + size_t sublength = pos - last_pos; + if (sublength < remain) { + *dest = (unsigned char)sublength; + memcpy(dest + 1, name + last_pos, sublength); + dest += sublength + 1; + remain -= sublength + 1; + } + else { + return 0; + } + last_pos = pos + 1; + } + if (last_pos < length) { + size_t sublength = length - last_pos; + if (sublength < capacity) { + *dest = (unsigned char)sublength; + memcpy(dest + 1, name + last_pos, sublength); + dest += sublength + 1; + remain -= sublength + 1; + } + else { + return 0; + } + } + if (!remain) + return 0; + *dest++ = 0; + return dest; +} + +static size_t +mdns_records_parse(const struct sockaddr* from, const void* buffer, size_t size, size_t* offset, + mdns_entry_type_t type, size_t records, mdns_record_callback_fn callback, + void* user_data) { + size_t parsed = 0; + int do_callback = 1; + for (size_t i = 0; i < records; ++i) { + mdns_string_skip(buffer, size, offset); + const uint16_t* data = (const uint16_t*)((const char*)buffer + (*offset)); + + uint16_t rtype = ntohs(*data++); + uint16_t rclass = ntohs(*data++); + uint32_t ttl = ntohs(*(const uint32_t*)(const void*)data); data += 2; + uint16_t length = ntohs(*data++); + + *offset += 10; + + if (do_callback) { + ++parsed; + if (callback(from, type, rtype, rclass, ttl, buffer, size, *offset, length, + user_data)) + do_callback = 0; + } + + *offset += length; + } + return parsed; +} + +static const uint8_t mdns_services_query[] = { + // Transaction ID + 0x00, 0x00, + // Flags + 0x00, 0x00, + // 1 question + 0x00, 0x01, + // No answer, authority or additional RRs + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + // _services._dns-sd._udp.local. + 0x09, '_', 's', 'e', 'r', 'v', 'i', 'c', 'e', 's', + 0x07, '_', 'd', 'n', 's', '-', 's', 'd', + 0x04, '_', 'u', 'd', 'p', + 0x05, 'l', 'o', 'c', 'a', 'l', + 0x00, + // PTR record + 0x00, MDNS_RECORDTYPE_PTR, + // QU (unicast response) and class IN + 0x80, MDNS_CLASS_IN +}; + +#if 0 +static int +mdns_discovery_send(int sock) { + struct sockaddr_storage addr_storage; + struct sockaddr_in addr; + struct sockaddr_in6 addr6; + struct sockaddr* saddr = (struct sockaddr*)&addr_storage; + socklen_t saddrlen = sizeof(struct sockaddr_storage); + if (getsockname(sock, saddr, &saddrlen)) + return -1; + if (saddr->sa_family == AF_INET6) { + memset(&addr6, 0, sizeof(struct sockaddr_in6)); + addr6.sin6_family = AF_INET6; +#ifdef __APPLE__ + addr6.sin6_len = sizeof(struct sockaddr_in6); +#endif + addr6.sin6_addr.s6_addr[0] = 0xFF; + addr6.sin6_addr.s6_addr[1] = 0x02; + addr6.sin6_addr.s6_addr[15] = 0xFB; + addr6.sin6_port = htons((unsigned short)5353); + saddr = (struct sockaddr*)&addr6; + saddrlen = sizeof(struct sockaddr_in6); + } + else { + memset(&addr, 0, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; +#ifdef __APPLE__ + addr.sin_len = sizeof(struct sockaddr_in); +#endif + addr.sin_addr.s_addr = htonl((((uint32_t)224U) << 24U) | ((uint32_t)251U)); + addr.sin_port = htons((unsigned short)5353); + saddr = (struct sockaddr*)&addr; + saddrlen = sizeof(struct sockaddr_in); + } + + if (sendto(sock, mdns_services_query, sizeof(mdns_services_query), 0, + saddr, saddrlen) < 0) + return -1; + return 0; +} + +static size_t +mdns_discovery_recv(int sock, void* buffer, size_t capacity, + mdns_record_callback_fn callback, void* user_data) { + struct sockaddr_in6 addr; + struct sockaddr* saddr = (struct sockaddr*)&addr; + memset(&addr, 0, sizeof(addr)); + saddr->sa_family = AF_INET; +#ifdef __APPLE__ + saddr->sa_len = sizeof(addr); +#endif + socklen_t addrlen = sizeof(addr); + int ret = recvfrom(sock, buffer, capacity, 0, + saddr, &addrlen); + if (ret <= 0) + return 0; + + size_t data_size = (size_t)ret; + size_t records = 0; + uint16_t* data = (uint16_t*)buffer; + + uint16_t transaction_id = ntohs(*data++); + uint16_t flags = ntohs(*data++); + uint16_t questions = ntohs(*data++); + uint16_t answer_rrs = ntohs(*data++); + uint16_t authority_rrs = ntohs(*data++); + uint16_t additional_rrs = ntohs(*data++); + + if (transaction_id || (flags != 0x8400)) + return 0; //Not a reply to our question + + if (questions > 1) + return 0; + + int i; + for (i = 0; i < questions; ++i) { + size_t ofs = (size_t)((char*)data - (char*)buffer); + size_t verify_ofs = 12; + //Verify it's our question, _services._dns-sd._udp.local. + if (!mdns_string_equal(buffer, data_size, &ofs, + mdns_services_query, sizeof(mdns_services_query), &verify_ofs)) + return 0; + data = (uint16_t*)((char*)buffer + ofs); + + uint16_t type = ntohs(*data++); + uint16_t rclass = ntohs(*data++); + + //Make sure we get a reply based on our PTR question for class IN + if ((type != MDNS_RECORDTYPE_PTR) || ((rclass & 0x7FFF) != MDNS_CLASS_IN)) + return 0; + } + + int do_callback = 1; + for (i = 0; i < answer_rrs; ++i) { + size_t ofs = (size_t)((char*)data - (char*)buffer); + size_t verify_ofs = 12; + //Verify it's an answer to our question, _services._dns-sd._udp.local. + int is_answer = mdns_string_equal(buffer, data_size, &ofs, + mdns_services_query, sizeof(mdns_services_query), &verify_ofs); + data = (uint16_t*)((char*)buffer + ofs); + + uint16_t type = ntohs(*data++); + uint16_t rclass = ntohs(*data++); + uint32_t ttl = ntohl(*(uint32_t*)(void*)data); data += 2; + uint16_t length = ntohs(*data++); + + if (is_answer && do_callback) { + ++records; + if (callback(saddr, MDNS_ENTRYTYPE_ANSWER, type, rclass, ttl, buffer, + data_size, (size_t)((char*)data - (char*)buffer), length, + user_data)) + do_callback = 0; + } + data = (void*)((char*)data + length); + } + + size_t offset = (size_t)((char*)data - (char*)buffer); + records += mdns_records_parse(saddr, buffer, data_size, &offset, + MDNS_ENTRYTYPE_AUTHORITY, authority_rrs, + callback, user_data); + records += mdns_records_parse(saddr, buffer, data_size, &offset, + MDNS_ENTRYTYPE_ADDITIONAL, additional_rrs, + callback, user_data); + + return records; +} +#endif + +static uint16_t mdns_transaction_id = 0; + +static int +mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t length, + void* buffer, size_t capacity) { + if (capacity < (17 + length)) + return -1; + + uint16_t* data = buffer; + //Transaction ID + *data++ = htons(++mdns_transaction_id); + //Flags + *data++ = 0; + //Questions + *data++ = htons(1); + //No answer, authority or additional RRs + *data++ = 0; + *data++ = 0; + *data++ = 0; + //Name string + data = mdns_string_make(data, capacity - 17, name, length); + if (!data) + return -1; + //Record type + *data++ = htons(type); + //! Unicast response, class IN + *data++ = htons(0x8000U | MDNS_CLASS_IN); + + struct sockaddr_storage addr_storage; + struct sockaddr_in addr; + struct sockaddr_in6 addr6; + struct sockaddr* saddr = (struct sockaddr*)&addr_storage; + socklen_t saddrlen = sizeof(struct sockaddr_storage); + if (getsockname(sock, saddr, &saddrlen)) + return -1; + if (saddr->sa_family == AF_INET6) { + memset(&addr6, 0, sizeof(struct sockaddr_in6)); + addr6.sin6_family = AF_INET6; +#ifdef __APPLE__ + addr6.sin6_len = sizeof(struct sockaddr_in6); +#endif + addr6.sin6_addr.s6_addr[0] = 0xFF; + addr6.sin6_addr.s6_addr[1] = 0x02; + addr6.sin6_addr.s6_addr[15] = 0xFB; + addr6.sin6_port = htons((unsigned short)5353); + saddr = (struct sockaddr*)&addr6; + saddrlen = sizeof(struct sockaddr_in6); + } + else { + memset(&addr, 0, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; +#ifdef __APPLE__ + addr.sin_len = sizeof(struct sockaddr_in); +#endif + addr.sin_addr.s_addr = htonl((((uint32_t)224U) << 24U) | ((uint32_t)251U)); + addr.sin_port = htons((unsigned short)5353); + saddr = (struct sockaddr*)&addr; + saddrlen = sizeof(struct sockaddr_in); + } + + if (sendto(sock, buffer, (char*)data - (char*)buffer, 0, + saddr, saddrlen) < 0) + return -1; + return 0; +} + +static size_t +mdns_query_recv(int sock, void* buffer, size_t capacity, + mdns_record_callback_fn callback, void* user_data, + uint8_t one_shot) { + struct sockaddr_in6 addr; + struct sockaddr* saddr = (struct sockaddr*)&addr; + memset(&addr, 0, sizeof(addr)); + saddr->sa_family = AF_INET; +#ifdef __APPLE__ + saddr->sa_len = sizeof(addr); +#endif + socklen_t addrlen = sizeof(addr); + int ret = recvfrom(sock, buffer, capacity, 0, + saddr, &addrlen); + if (ret <= 0) + return 0; + + size_t data_size = (size_t)ret; + uint16_t* data = (uint16_t*)buffer; + + uint16_t transaction_id = ntohs(*data++); + ++data;// uint16_t flags = ntohs(*data++); + uint16_t questions = ntohs(*data++); + uint16_t answer_rrs = ntohs(*data++); + uint16_t authority_rrs = ntohs(*data++); + uint16_t additional_rrs = ntohs(*data++); + + if (one_shot && transaction_id != mdns_transaction_id)// || (flags != 0x8400)) + return 0; //Not a reply to our last question + + if (questions > 1) + return 0; + + //Skip questions part + int i; + for (i = 0; i < questions; ++i) { + size_t ofs = (size_t)((char*)data - (char*)buffer); + if (!mdns_string_skip(buffer, data_size, &ofs)) + return 0; + data = (void*)((char*)buffer + ofs); + ++data; + ++data; + } + + size_t records = 0; + size_t offset = (size_t)((char*)data - (char*)buffer); + records += mdns_records_parse(saddr, buffer, data_size, &offset, + MDNS_ENTRYTYPE_ANSWER, answer_rrs, + callback, user_data); + records += mdns_records_parse(saddr, buffer, data_size, &offset, + MDNS_ENTRYTYPE_AUTHORITY, authority_rrs, + callback, user_data); + records += mdns_records_parse(saddr, buffer, data_size, &offset, + MDNS_ENTRYTYPE_ADDITIONAL, additional_rrs, + callback, user_data); + return records; +} + +static mdns_string_t +mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t length, + char* strbuffer, size_t capacity) { + //PTR record is just a string + if ((size >= offset + length) && (length >= 2)) + return mdns_string_extract(buffer, size, &offset, strbuffer, capacity); + mdns_string_t empty = {0, 0}; + return empty; +} + +static mdns_record_srv_t +mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t length, + char* strbuffer, size_t capacity) { + mdns_record_srv_t srv; + memset(&srv, 0, sizeof(mdns_record_srv_t)); + // Read the priority, weight, port number and the discovery name + // SRV record format (http://www.ietf.org/rfc/rfc2782.txt): + // 2 bytes network-order unsigned priority + // 2 bytes network-order unsigned weight + // 2 bytes network-order unsigned port + // string: discovery (domain) name, minimum 2 bytes when compressed + if ((size >= offset + length) && (length >= 8)) { + const uint16_t* recorddata = (const uint16_t*)((const char*)buffer + offset); + srv.priority = ntohs(*recorddata++); + srv.weight = ntohs(*recorddata++); + srv.port = ntohs(*recorddata++); + offset += 6; + srv.name = mdns_string_extract(buffer, size, &offset, strbuffer, capacity); + } + return srv; +} + +static struct sockaddr_in* +mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t length, + struct sockaddr_in* addr) { + memset(addr, 0, sizeof(struct sockaddr_in)); + addr->sin_family = AF_INET; +#ifdef __APPLE__ + addr->sin_len = sizeof(struct sockaddr_in); +#endif + if ((size >= offset + length) && (length == 4)) + addr->sin_addr.s_addr = *(const uint32_t*)((const char*)buffer + offset); + return addr; +} + +static struct sockaddr_in6* +mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t length, + struct sockaddr_in6* addr) { + memset(addr, 0, sizeof(struct sockaddr_in6)); + addr->sin6_family = AF_INET6; +#ifdef __APPLE__ + addr->sin6_len = sizeof(struct sockaddr_in6); +#endif + if ((size >= offset + length) && (length == 16)) + addr->sin6_addr = *(const struct in6_addr*)((const char*)buffer + offset); + return addr; +} + +static size_t +mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t length, + mdns_record_txt_t* records, size_t capacity) { + size_t parsed = 0; + const char* strdata; + size_t separator, sublength; + size_t end = offset + length; + + if (size < end) + end = size; + + while ((offset < end) && (parsed < capacity)) { + strdata = (const char*)buffer + offset; + sublength = *(const unsigned char*)strdata; + + ++strdata; + offset += sublength + 1; + + separator = 0; + for (size_t c = 0; c < sublength; ++c) { + //DNS-SD TXT record keys MUST be printable US-ASCII, [0x20, 0x7E] + if ((strdata[c] < 0x20) || (strdata[c] > 0x7E)) + break; + if (strdata[c] == '=') { + separator = c; + break; + } + } + + if (!separator) + continue; + + if (separator < sublength) { + records[parsed].key.str = strdata; + records[parsed].key.length = separator; + records[parsed].value.str = strdata + separator + 1; + records[parsed].value.length = sublength - (separator + 1); + } + else { + records[parsed].key.str = strdata; + records[parsed].key.length = sublength; + } + + ++parsed; + } + + return parsed; +} + +#ifdef _WIN32 +#undef strncasecmp +#endif diff --git a/ctrl/facemgr/src/interfaces/dummy/CMakeLists.txt b/ctrl/facemgr/src/interfaces/dummy/CMakeLists.txt index 1af3b4b2a..05276bc5a 100644 --- a/ctrl/facemgr/src/interfaces/dummy/CMakeLists.txt +++ b/ctrl/facemgr/src/interfaces/dummy/CMakeLists.txt @@ -12,6 +12,7 @@ # limitations under the License. list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/dummy.h ) list(APPEND SOURCE_FILES diff --git a/ctrl/facemgr/src/interfaces/dummy/dummy.c b/ctrl/facemgr/src/interfaces/dummy/dummy.c index b0c558388..6a21792a2 100644 --- a/ctrl/facemgr/src/interfaces/dummy/dummy.c +++ b/ctrl/facemgr/src/interfaces/dummy/dummy.c @@ -19,31 +19,101 @@ */ #include <stdlib.h> +#include <unistd.h> // close + +#include <hicn/facemgr.h> -#include "../../interface.h" #include "../../common.h" -#include "../../event.h" -#include "../../face.h" -#include "../../facemgr.h" +#include "../../facelet.h" +#include "../../interface.h" + +#include "dummy.h" #define DEFAULT_PORT 9695 -int dummy_initialize(interface_t * interface, face_rules_t * rules, void **pdata) { - ip_address_t local = IPV4_LOOPBACK; - ip_address_t remote = IPV4_LOOPBACK; - face_t * face = face_create_udp(&local, DEFAULT_PORT, &remote, DEFAULT_PORT, AF_INET); - event_raise(EVENT_TYPE_CREATE, face, interface); - return FACEMGR_SUCCESS; +#define UNUSED(x) ((void)x) + +/* + * Internal data + */ +typedef struct { + /* The configuration data will likely be allocated on the stack (or should + * be freed) by the caller, we recommend to make a copy of this data. + * This copy can further be altered with default values. + */ + dummy_cfg_t cfg; + + /* ... */ + + int fd; /* Sample internal data: file descriptor */ +} dummy_data_t; + +int dummy_initialize(interface_t * interface, void * cfg) +{ + dummy_data_t * data = malloc(sizeof(dummy_data_t)); + if (!data) + goto ERR_MALLOC; + interface->data = data; + + /* Use default values for unspecified configuration parameters */ + if (cfg) { + data->cfg = *(dummy_cfg_t *)cfg; + } else { + memset(&data->cfg, 0, sizeof(data->cfg)); + } + + /* ... */ + + data->fd = 0; + + /* ... */ + + /* + * We should return a negative value in case of error, and a positive value + * otherwise: + * - a file descriptor (>0) will be added to the event loop; or + * - 0 if we don't use any file descriptor + */ + return data->fd; + +ERR_MALLOC: + return -1; +} + +int dummy_finalize(interface_t * interface) +{ + dummy_data_t * data = (dummy_data_t*)interface->data; + + if (data->fd > 0) + close(data->fd); + + return 0; } -int dummy_finalize(interface_t * interface) { - return FACEMGR_SUCCESS; +int dummy_callback(interface_t * interface) +{ + dummy_data_t * data = (dummy_data_t*)interface->data; + UNUSED(data); + + /* ... */ + + return 0; +} + +int dummy_on_event(interface_t * interface, const facelet_t * facelet) +{ + dummy_data_t * data = (dummy_data_t*)interface->data; + UNUSED(data); + + /* ... */ + + return 0; } interface_ops_t dummy_ops = { .type = "dummy", - .is_singleton = true, .initialize = dummy_initialize, .finalize = dummy_finalize, - .on_event = NULL, + .callback = dummy_callback, + .on_event = dummy_on_event, }; diff --git a/ctrl/facemgr/src/netdevice.c b/ctrl/facemgr/src/interfaces/dummy/dummy.h index 817b0e47b..22fe5d1a6 100644 --- a/ctrl/facemgr/src/netdevice.c +++ b/ctrl/facemgr/src/interfaces/dummy/dummy.h @@ -14,15 +14,21 @@ */ /** - * \file netdevice.c - * \brief Implementation of Netdevice abstraction + * \file dummy.h + * \brief Dummy interface + * + * This class serves as a template to implement new face maanger interfaces, and + * can be used for test purposes. */ -#include "common.h" -#include "netdevice.h" +#ifndef FACEMGR_INTERFACE_DUMMY_H +#define FACEMGR_INTERFACE_DUMMY_H + +/* + * Configuration data + */ +typedef struct { + /* ... */ +} dummy_cfg_t; -const char * netdevice_type_str[] = { -#define _(x) [NETDEVICE_TYPE_ ## x] = STRINGIZE(x), -foreach_netdevice_type -#undef _ -} +#endif /* FACEMGR_INTERFACE_DUMMY_H */ diff --git a/ctrl/facemgr/src/interfaces/hicn_light/hicn_light.c b/ctrl/facemgr/src/interfaces/hicn_light/hicn_light.c index 85694573d..e42c6c6ae 100644 --- a/ctrl/facemgr/src/interfaces/hicn_light/hicn_light.c +++ b/ctrl/facemgr/src/interfaces/hicn_light/hicn_light.c @@ -18,27 +18,70 @@ * \brief hICN light interface */ #include <stdbool.h> -#include <stdlib.h> // arc4random [random, rand] #include <stdio.h> // snprintf #include <time.h> // time #include <hicn/ctrl.h> +#include <hicn/facemgr.h> +#include <hicn/util/ip_address.h> +#include <hicn/util/log.h> -#include "../../facemgr.h" +#include "../../facelet.h" #include "../../interface.h" -#include "../../util/ip_address.h" -#include "../../util/log.h" #include "../../util/map.h" -#include "../../event.h" #define DEFAULT_ROUTE_COST 0 +typedef enum { + HL_STATE_UNDEFINED, + HL_STATE_FACES_SENT, + HL_STATE_DONE, +} hl_state_t; + typedef struct { hc_sock_t * s; - bool busy; + hl_state_t state; } hl_data_t; -int hl_initialize(interface_t * interface, face_rules_t * rules, void ** pdata) +int hl_process_state(interface_t * interface) +{ + hl_data_t * data = (hl_data_t *)interface->data; + + hc_data_t * faces; +#if 0 + char buf[MAXSZ_FACE]; +#endif + + switch(data->state) + { + case HL_STATE_UNDEFINED: + if (hc_face_list(data->s, &faces) < 0) { + /* Blocking call */ + printf("Could not retrieve face list\n"); + return -1; + } + foreach_face(f, faces) { +#if 0 + hc_face_snprintf(buf, MAXSZ_FACE, f); + printf("Face: %s\n", buf); +#endif + facelet_t * facelet = facelet_create_from_face(&f->face); + facelet_set_event(facelet, FACELET_EVENT_GET); + facelet_raise_event(facelet, interface); + } + break; + + case HL_STATE_FACES_SENT: + break; + + default: /* HL_STATE_DONE never called */ + break; + } + + return 0; +} + +int hl_initialize(interface_t * interface, void * cfg) { hl_data_t * data = malloc(sizeof(hl_data_t)); if (!data) { @@ -57,51 +100,52 @@ int hl_initialize(interface_t * interface, face_rules_t * rules, void ** pdata) goto ERR_CONNECT; } - data->busy = false; + data->state = HL_STATE_UNDEFINED; + + interface->data = data; - *pdata = data; + hl_process_state(interface); - return FACEMGR_SUCCESS; + return 0; ERR_CONNECT: hc_sock_free(data->s); ERR_SOCK: free(data); ERR_MALLOC: - return FACEMGR_FAILURE; + return -1; } int hl_finalize(interface_t * interface) { //hc_data_t * data = interface->data; //hc_sock_close(data->s); - return FACEMGR_SUCCESS; + return 0; } -int hl_on_event(interface_t * interface, const event_t * event) +int hl_on_event(interface_t * interface, const facelet_t * facelet) { - hc_face_t face; + hc_face_t hc_face; hc_route_t route; int rc; hl_data_t * data = (hl_data_t *)interface->data; - /* XXX We need a queue or a socket pool to process concurrent events */ - if (data->busy) { - ERROR("[hicn_light] Busy !"); - return FACEMGR_FAILURE; - } + face_t * face = NULL; + if (facelet_get_face(facelet, &face) < 0) + return -1; + + switch(facelet_get_event(facelet)) { - switch(event->type) { - case EVENT_TYPE_CREATE: + case FACELET_EVENT_CREATE: /* Create face */ - face.face = *event->face; - rc = hc_face_create(data->s, &face); + hc_face.face = *face; + rc = hc_face_create(data->s, &hc_face); if (rc < 0) { ERROR("Failed to create face\n"); goto ERR; } - DEBUG("Created face id=%d\n", face.id); + INFO("Created face id=%d\n", hc_face.id); #if 0 /* Add default route v4 */ @@ -134,88 +178,84 @@ int hl_on_event(interface_t * interface, const event_t * event) } #endif -#if 1 - /* We add routes based on face tags */ - - if (policy_tags_has(event->face->tags, POLICY_TAG_TRUSTED)) { - route = (hc_route_t) { - .face_id = face.id, - .family = AF_INET6, - .len = 16, - .cost = DEFAULT_ROUTE_COST, - }; - if (ip_address_pton("b001::", &route.remote_addr) < 0) { - ERROR("Failed to convert prefix"); - goto ERR; - } - if (hc_route_create(data->s, &route) < 0) { - ERROR("Failed to create hICN/IPv6 route"); - goto ERR; - } + /* Adding default route */ + route = (hc_route_t) { + .face_id = hc_face.id, + .family = AF_INET6, + .len = 0, + .cost = DEFAULT_ROUTE_COST, + }; + if (ip_address_pton("::", &route.remote_addr) < 0) { + ERROR("Failed to convert prefix"); + goto ERR; + } + if (hc_route_create(data->s, &route) < 0) { + ERROR("Failed to create hICN/IPv6 route"); + goto ERR; + } - route = (hc_route_t) { - .face_id = face.id, - .family = AF_INET6, - .len = 16, - .cost = DEFAULT_ROUTE_COST, - }; - if (ip_address_pton("d001::", &route.remote_addr) < 0) { - ERROR("Failed to convert prefix"); + break; + + case FACELET_EVENT_DELETE: + /* Removing a face should also remove associated routes */ + /* Create face */ + hc_face.face = *face; + rc = hc_face_delete(data->s, &hc_face); + if (rc < 0) { + ERROR("Failed to delete face\n"); + goto ERR; + } + INFO("Deleted face id=%d\n", hc_face.id); + break; + + case FACELET_EVENT_UPDATE: + /* Currently, only admin_state is supported */ + if (facelet_get_admin_state_status(facelet) == FACELET_ATTR_STATUS_DIRTY) { + hc_face.face = *face; + hc_face_t * face_found; + rc = hc_face_get(data->s, &hc_face, &face_found); + if (rc < 0) { + ERROR("Failed to find face\n"); goto ERR; } - if (hc_route_create(data->s, &route) < 0) { - ERROR("Failed to create hICN/IPv6 route"); + if (!face_found) { + ERROR("Face to update has not been found"); goto ERR; } + char conn_id_or_name[NAME_LEN]; + snprintf(conn_id_or_name, NAME_LEN, "%d", face_found->id); + free(face_found); + printf("Face id = %d\n", face_found->id); - } else { - - route = (hc_route_t) { - .face_id = face.id, - .family = AF_INET6, - .len = 16, - .cost = DEFAULT_ROUTE_COST, - }; - if (ip_address_pton("c001::", &route.remote_addr) < 0) { - ERROR("Failed to convert prefix"); + face_state_t admin_state; + if (facelet_get_admin_state(facelet, &admin_state) < 0) { + ERROR("Failed to retrieve facelet admin state"); goto ERR; } - if (hc_route_create(data->s, &route) < 0) { - ERROR("Failed to create hICN/IPv6 route"); + + printf("Setting admin state"); + if (hc_connection_set_admin_state(data->s, conn_id_or_name, admin_state) < 0) { + ERROR("Failed to update admin state"); goto ERR; } + INFO("Admin state updated"); } -#endif - - break; - - case EVENT_TYPE_DELETE: - /* Removing a face should also remove associated routes */ - /* Create face */ - face.face = *event->face; - rc = hc_face_delete(data->s, &face); - if (rc < 0) { - ERROR("Failed to delete face\n"); - goto ERR; - } - INFO("Deleted face id=%d\n", face.id); break; default: - ERROR("Unknown event %s\n", event_type_str[event->type]); + ERROR("Unknown event %s\n", facelet_event_str[facelet_get_event(facelet)]); /* Unsupported events */ goto ERR; } - return FACEMGR_SUCCESS; + return 0; ERR: - return FACEMGR_FAILURE; + return -1; } const interface_ops_t hicn_light_ops = { .type = "hicn_light", - .is_singleton = false, .initialize = hl_initialize, .finalize = hl_finalize, .on_event = hl_on_event, diff --git a/ctrl/facemgr/src/interfaces/netlink/netlink.c b/ctrl/facemgr/src/interfaces/netlink/netlink.c index 5bf0baf9f..08cbe4d3b 100644 --- a/ctrl/facemgr/src/interfaces/netlink/netlink.c +++ b/ctrl/facemgr/src/interfaces/netlink/netlink.c @@ -18,34 +18,120 @@ * \brief Netlink interface */ +#include <assert.h> #include <linux/rtnetlink.h> +#include <net/if_arp.h> // ARPHRD_LOOPBACK #include <sys/types.h> // getpid #include <unistd.h> // getpid -#include "../../event.h" -#include "../../facemgr.h" +#include <hicn/facemgr.h> +#include <hicn/util/ip_address.h> +#include <hicn/util/log.h> + +#include "../../common.h" +#include "../../facelet.h" #include "../../interface.h" +typedef enum { + NL_STATE_UNDEFINED, + NL_STATE_LINK_SENT, + NL_STATE_ADDR_SENT, + NL_STATE_DONE, +} nl_state_t; + /* Internal data storage */ typedef struct { int fd; + nl_state_t state; } nl_data_t; -// little helper to parsing message using netlink macroses -void parseRtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) +static inline void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len, + unsigned short flags) { + unsigned short type; + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); + while (RTA_OK(rta, len)) { + type = rta->rta_type & ~flags; + if (type <= max) + tb[type] = rta; + rta = RTA_NEXT(rta, len); + } +} + +int nl_process_state(interface_t * interface) +{ + nl_data_t * data = (nl_data_t*)interface->data; + int rc; + + switch(data->state) { + case NL_STATE_UNDEFINED: + { + struct { + struct nlmsghdr header; + struct rtgenmsg payload; + } msg2 = { + .header = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)), + .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP, + .nlmsg_type = RTM_GETLINK, + .nlmsg_pid = getpid(), + .nlmsg_seq = 3, + }, + .payload = { + .rtgen_family = AF_PACKET, + } + }; + + rc = send(data->fd, &msg2, msg2.header.nlmsg_len, 0); + if (rc < 0) + printf("E: Error sending netlink query\n"); + + data->state = NL_STATE_LINK_SENT; + break; + } + + case NL_STATE_LINK_SENT: + { + /* Issue a first query to receive static state */ + struct { + struct nlmsghdr header; + struct ifaddrmsg payload; + } msg = { + .header = { + .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)), + .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP, + .nlmsg_type = RTM_GETADDR, + .nlmsg_pid = getpid(), + .nlmsg_seq = 7, + }, + .payload = { + .ifa_family = AF_INET, + } + }; + + rc = send(data->fd, &msg, msg.header.nlmsg_len, 0); + if (rc < 0) + printf("E: Error sending netlink query\n"); + + data->state = NL_STATE_ADDR_SENT; + break; + } - while (RTA_OK(rta, len)) { // while not end of the message - if (rta->rta_type <= max) { - tb[rta->rta_type] = rta; // read attr + case NL_STATE_ADDR_SENT: + { + data->state = NL_STATE_DONE; + break; } - rta = RTA_NEXT(rta,len); // get next attr + + default: /* NL_STATE_DONE never called */ + break; } -} + return 0; +} -int nl_initialize(interface_t * interface, face_rules_t * rules, void ** pdata) +int nl_initialize(interface_t * interface, void * cfg) { nl_data_t * data = malloc(sizeof(nl_data_t)); if (!data) @@ -57,31 +143,194 @@ int nl_initialize(interface_t * interface, face_rules_t * rules, void ** pdata) goto ERR_SOCKET; } + data->state = NL_STATE_UNDEFINED; + struct sockaddr_nl local; // local addr struct memset(&local, 0, sizeof(local)); local.nl_family = AF_NETLINK; // set protocol family - local.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE; // set groups we interested in + // NOTE: RTNLGRP_LINK replaces obsolete RTMGRP_LINK, etc + local.nl_groups = 0 + | RTMGRP_LINK + | RTMGRP_IPV4_IFADDR + | RTMGRP_IPV6_IFADDR +#if 0 + | RTMGRP_IPV4_ROUTE; + | RTMGRP_IPV6_ROUTE; +#endif + ; local.nl_pid = getpid(); // set out id using current process id - if (bind(data->fd, (struct sockaddr*)&local, sizeof(local)) < 0) { // bind socket printf("Failed to bind netlink socket: %s\n", (char*)strerror(errno)); goto ERR_BIND; } - /* Issue a first query to receive static state */ + interface->data = data; + nl_process_state(interface); - *pdata = data; - return data->fd; // FACEMGR_SUCCESS; + return data->fd; // 0; ERR_BIND: close(data->fd); ERR_SOCKET: free(data); ERR_MALLOC: - *pdata = NULL; - return FACEMGR_FAILURE; + return -1; +} + +int parse_link(struct nlmsghdr * h, facelet_t ** facelet, + char * interface_name, size_t interface_name_size, + bool * up, bool * running) +{ + struct ifinfomsg *ifi; // structure for network interface info + struct rtattr *tb[IFLA_MAX + 1]; + + assert(facelet); + + ifi = (struct ifinfomsg*) NLMSG_DATA(h); // get information about changed network interface + parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(h), 1<<15); + + if (interface_name) { + assert(tb[IFLA_IFNAME]); + snprintf(interface_name, interface_name_size, "%s", (char*)RTA_DATA(tb[IFLA_IFNAME])); + } + + if (up) + *up = ifi->ifi_flags & IFF_UP; + if (running) + *running = ifi->ifi_flags & IFF_RUNNING; + + + *facelet = facelet_create(); + netdevice_t * netdevice = netdevice_create_from_name(interface_name); + if (!netdevice) + goto ERROR; + int rc = facelet_set_netdevice(*facelet, *netdevice); + if (rc < 0) + goto ERROR; + +// FIXME Tags +#if 0 + /* This is the only opportunity to identify a loopback interface on both + * linux _and_ android, as NetworkCapabilities does not have a flag for + * LOOPBACK... */ + if (ifi->ifi_type==ARPHRD_LOOPBACK) { + DEBUG("loopback"); + } + +#ifdef IFLA_WIRELESS + /* + * This signals a wirless event, but it typically occurs _after_ a face is + * created... we might need to update an existing face by setting a tag... + * or find a way to exploit this flag before actually creating the face... + */ + if (tb[IFLA_WIRELESS]) + DEBUG("wireless!!!"); +#else +#warning "IFLA_WIRELESS not supported on this platform" +#endif /* IFLA_WIRELESS */ + +#endif + + // TODO + // - ifi_change + // - IFLA_PROTINFO + + return 0; + +ERROR: + facelet_free(*facelet); + *facelet = NULL; + + return -1; +} + +int parse_addr(struct nlmsghdr * h, facelet_t ** facelet, + char * interface_name, size_t interface_name_size, + char * interface_address, size_t interface_address_size) +{ + ip_address_t local_addr = IP_ADDRESS_EMPTY; + struct ifaddrmsg *ifa; // structure for network interface data + struct rtattr *tba[IFA_MAX+1]; + + assert(facelet); + + ifa = (struct ifaddrmsg*)NLMSG_DATA(h); // get data from the network interface + + parse_rtattr(tba, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(h), 0); + + /* FIXME + * + * IFA_LOCAL ok for v4, not there for v6 + * + * IFA_ADDRESS seems to work for both but with the following precaution + * + * IFA_ADDRESS is prefix address, rather than local interface address. + * It makes no difference for normally configured broadcast interfaces, + * but for point-to-point IFA_ADDRESS is DESTINATION address, + * local address is supplied in IFA_LOCAL attribute. + */ + if (!tba[IFA_ADDRESS]) { + ERROR("[netlink.parse_addr] No local address"); + return -1; + } + + switch(ifa->ifa_family) { + case AF_INET: + local_addr.v4.as_inaddr = *(struct in_addr*)RTA_DATA(tba[IFA_ADDRESS]); + break; + case AF_INET6: + local_addr.v6.as_in6addr = *(struct in6_addr*)RTA_DATA(tba[IFA_ADDRESS]); + break; + default: + return 0; + } + +#if 0 /* Not working for v6 */ + if (interface_name) { + assert(tba[IFLA_IFNAME]); + snprintf(interface_name, interface_name_size, "%s", (char*)RTA_DATA(tba[IFLA_IFNAME])); + } +#endif + + /* See comment in parse_link */ + if (interface_address) { + assert(tba[IFA_ADDRESS]); + ip_address_snprintf(interface_address, interface_address_size, &local_addr, ifa->ifa_family); + } + + netdevice_t * netdevice = netdevice_create_from_index(ifa->ifa_index); + if (!netdevice) { + ERROR("[netlink.parse_addr] error creating netdevice '%s'", interface_name); + goto ERROR; + } + + if (interface_name) { + snprintf(interface_name, interface_name_size, "%s", netdevice->name); + } + + *facelet = facelet_create(); + if (facelet_set_netdevice(*facelet, *netdevice) < 0) { + ERROR("[netlink.parse_addr] error setting netdevice"); + goto ERROR; + } + if (facelet_set_family(*facelet, ifa->ifa_family) < 0) { + ERROR("[netlink.parse_addr] error setting family"); + goto ERROR; + } + if (facelet_set_local_addr(*facelet, local_addr) < 0) { + ERROR("[netlink.parse_addr] error setting local address"); + goto ERROR; + } + + return 0; + +ERROR: + facelet_free(*facelet); + *facelet = NULL; + + return -1; } int nl_callback(interface_t * interface) @@ -97,13 +346,12 @@ int nl_callback(interface_t * interface) iov.iov_len = sizeof(buf); // set size // initialize protocol message header - struct msghdr msg; - { - msg.msg_name = &local; // local address - msg.msg_namelen = sizeof(local); // address size - msg.msg_iov = &iov; // io vector - msg.msg_iovlen = 1; // io size - } + struct msghdr msg = { + .msg_name = &local, // local address + .msg_namelen = sizeof(local), // address size + .msg_iov = &iov, // io vector + .msg_iovlen = 1, // io size + }; ssize_t status = recvmsg(data->fd, &msg, 0); @@ -115,12 +363,12 @@ int nl_callback(interface_t * interface) */ printf("Failed to read netlink: %s", (char*)strerror(errno)); - return FACEMGR_FAILURE; + return -1; } if (msg.msg_namelen != sizeof(local)) { // check message length, just in case printf("Invalid length of the sender address struct\n"); - return FACEMGR_FAILURE; + return -1; } // message parser @@ -129,103 +377,122 @@ int nl_callback(interface_t * interface) for (h = (struct nlmsghdr*)buf; status >= (ssize_t)sizeof(*h); ) { // read all messagess headers int len = h->nlmsg_len; int l = len - sizeof(*h); - char *ifName = NULL; if ((l < 0) || (len > status)) { printf("Invalid message length: %i\n", len); continue; } - // now we can check message type - if ((h->nlmsg_type == RTM_NEWROUTE) || (h->nlmsg_type == RTM_DELROUTE)) { // some changes in routing table - printf("Routing table was changed\n"); - } else { // in other case we need to go deeper - char *ifUpp; - char *ifRunn; - struct ifinfomsg *ifi; // structure for network interface info - struct rtattr *tb[IFLA_MAX + 1]; - - ifi = (struct ifinfomsg*) NLMSG_DATA(h); // get information about changed network interface - - parseRtattr(tb, IFLA_MAX, IFLA_RTA(ifi), h->nlmsg_len); // get attributes - - if (tb[IFLA_IFNAME]) { // validation - ifName = (char*)RTA_DATA(tb[IFLA_IFNAME]); // get network interface name - } - - if (ifi->ifi_flags & IFF_UP) { // get UP flag of the network interface - ifUpp = (char*)"UP"; - } else { - ifUpp = (char*)"DOWN"; - } + switch(h->nlmsg_type) { +#if 0 + case RTM_NEWROUTE: + case RTM_DELROUTE: + DEBUG("Routing table was changed"); + break; +#endif + + case RTM_DELADDR: + { + facelet_t * facelet = NULL; + char interface_name[IFNAMSIZ]; + char interface_address[MAXSZ_IP_ADDRESS] = {0}; + + if (parse_addr(h, &facelet, interface_name, IFNAMSIZ, + interface_address, MAXSZ_IP_ADDRESS) < 0) { + ERROR("Error parsing address message"); + break; + } - if (ifi->ifi_flags & IFF_RUNNING) { // get RUNNING flag of the network interface - ifRunn = (char*)"RUNNING"; - } else { - ifRunn = (char*)"NOT RUNNING"; + DEBUG("Interface %s: address was removed", interface_name); + if (facelet) { + facelet_set_event(facelet, FACELET_EVENT_DELETE); + facelet_raise_event(facelet, interface); + } + break; } - char ifAddress[256] = {0}; // network addr - struct ifaddrmsg *ifa; // structure for network interface data - struct rtattr *tba[IFA_MAX+1]; + case RTM_NEWADDR: + { + facelet_t * facelet = NULL; + char interface_name[IFNAMSIZ]; + char interface_address[MAXSZ_IP_ADDRESS] = {0}; - ifa = (struct ifaddrmsg*)NLMSG_DATA(h); // get data from the network interface + if (parse_addr(h, &facelet, interface_name, IFNAMSIZ, + interface_address, MAXSZ_IP_ADDRESS) < 0) { + ERROR("Error parsing address message"); + break; + } - parseRtattr(tba, IFA_MAX, IFA_RTA(ifa), h->nlmsg_len); + DEBUG("Interface %s: new address was assigned: %s", interface_name, interface_address); - if (tba[IFA_LOCAL]) { - inet_ntop(AF_INET, RTA_DATA(tba[IFA_LOCAL]), ifAddress, sizeof(ifAddress)); // get IP addr + if (facelet) { + facelet_set_event(facelet, FACELET_EVENT_UPDATE); + facelet_raise_event(facelet, interface); + } + break; } - face_t * face; - - if (tba[IFA_LOCAL]) { - ip_address_t local_addr = IP_ADDRESS_EMPTY; - switch(ifa->ifa_family) { - case AF_INET: - local_addr.v4.as_inaddr = *(struct in_addr*)RTA_DATA(tba[IFA_LOCAL]); - break; - case AF_INET6: - local_addr.v6.as_in6addr = *(struct in6_addr*)RTA_DATA(tba[IFA_LOCAL]); - break; - default: - continue; + case RTM_DELLINK: + { + facelet_t * facelet = NULL; + char interface_name[IFNAMSIZ]; + if (parse_link(h, &facelet, interface_name, IFNAMSIZ, + NULL, NULL) < 0) { + ERROR("Error parsing link message"); + break; + } + if (facelet) { + facelet_set_event(facelet, FACELET_EVENT_DELETE); + facelet_raise_event(facelet, interface); } - face = face_create_udp(&local_addr, 0, &IP_ADDRESS_EMPTY, 0, ifa->ifa_family); - } else { - face = NULL; + + DEBUG("Network interface %s was removed", interface_name); + break; } - switch (h->nlmsg_type) { - case RTM_DELADDR: - // DOES NOT SEEM TO BE TRIGGERED - printf("Interface %s: address was removed\n", ifName); - if (face) - event_raise(EVENT_TYPE_DELETE, face, interface); - break; + case RTM_NEWLINK: + { + facelet_t * facelet = NULL; + char interface_name[IFNAMSIZ]; + bool up, running; - case RTM_DELLINK: - printf("Network interface %s was removed\n", ifName); + if (parse_link(h, &facelet, interface_name, IFNAMSIZ, &up, &running) < 0) { + ERROR("Error parsing link message"); break; + } - case RTM_NEWLINK: - printf("New network interface %s, state: %s %s\n", ifName, ifUpp, ifRunn); - // UP RUNNING - // UP NOT RUNNING - // DOWN NOT RUNNING - if (!(ifi->ifi_flags & IFF_UP) || (!(ifi->ifi_flags & IFF_RUNNING))) { - if(face) - event_raise(EVENT_TYPE_DELETE, face, interface); + // UP RUNNING + // UP NOT RUNNING + // DOWN NOT RUNNING + DEBUG("New network interface %s, state: %s %s", interface_name, + up ? "UP" : "DOWN", + running ? "RUNNING" : "NOT_RUNNING"); + + if (facelet && up && running) { + facelet_set_event(facelet, FACELET_EVENT_CREATE); + facelet_t * facelet6 = facelet_dup(facelet); + if (!facelet6) { + ERROR("Could not duplicate face for v6"); + break; } - break; - case RTM_NEWADDR: - printf("Interface %s: new address was assigned: %s\n", ifName, ifAddress); - printf("NEW FACE\n"); - if (face) - event_raise(EVENT_TYPE_CREATE, face, interface); - break; + facelet_set_family(facelet, AF_INET); + facelet_raise_event(facelet, interface); + + facelet_set_family(facelet6, AF_INET6); + facelet_raise_event(facelet6, interface); + } + break; } + + case NLMSG_ERROR: + break; + case NLMSG_DONE: + nl_process_state(interface); + break; + default: + break; + } status -= NLMSG_ALIGN(len); // align offsets by the message length, this is important @@ -233,20 +500,19 @@ int nl_callback(interface_t * interface) h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); // get next message } - return FACEMGR_SUCCESS; + return 0; } int nl_finalize(interface_t * interface) { nl_data_t * data = (nl_data_t*)interface->data; close(data->fd); - return FACEMGR_SUCCESS; + return 0; } const interface_ops_t netlink_ops = { .type = "netlink", - .is_singleton = true, .initialize = nl_initialize, .callback = nl_callback, .finalize = nl_finalize, diff --git a/ctrl/facemgr/src/interfaces/network_framework/CMakeLists.txt b/ctrl/facemgr/src/interfaces/network_framework/CMakeLists.txt index ca6659342..e8b0144b1 100644 --- a/ctrl/facemgr/src/interfaces/network_framework/CMakeLists.txt +++ b/ctrl/facemgr/src/interfaces/network_framework/CMakeLists.txt @@ -17,6 +17,7 @@ if (NOT NETWORK_LIBRARY) endif() list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/network_framework.h ) list(APPEND SOURCE_FILES diff --git a/ctrl/facemgr/src/interfaces/network_framework/network_framework.c b/ctrl/facemgr/src/interfaces/network_framework/network_framework.c index 8a33129b4..4a9d24f61 100644 --- a/ctrl/facemgr/src/interfaces/network_framework/network_framework.c +++ b/ctrl/facemgr/src/interfaces/network_framework/network_framework.c @@ -24,14 +24,17 @@ #include <Network/Network.h> #include <err.h> +#include <hicn/facemgr.h> +#include <hicn/util/token.h> +#include <hicn/util/log.h> + #include "../../common.h" -#include "../../event.h" -#include "../../face.h" -#include "../../facemgr.h" +#include <hicn/ctrl/face.h> +#include "../../facelet.h" #include "../../interface.h" #include "../../util/map.h" -#include "../../util/token.h" -#include "../../util/log.h" + +#include "network_framework.h" /* * Bonjour service discovery for hICN forwarder @@ -107,7 +110,7 @@ cmp_iface(const nw_interface_t iface1, const nw_interface_t iface2) //TYPEDEF_MAP(map_cnx, nw_interface_t, nw_connection_t, cmp_iface); typedef struct { - face_rules_t * rules; /**< Face creation rules */ + network_framework_cfg_t cfg; nw_path_monitor_t pm; /**< Main path monitor */ // map_cnx_t map_cnx; /**< Map: interface -> connection for face status */ } nf_data_t; @@ -209,97 +212,90 @@ dump_connection(nw_connection_t connection, int indent) nw_release(path); } -face_t * -face_create_from_connection(nw_connection_t connection, face_rules_t * rules) +facelet_t * +facelet_create_from_connection(nw_connection_t connection) { - face_t * face; - struct sockaddr_in * sin; - struct sockaddr_in6 * sin6; + facelet_t * facelet; + ip_address_t local_addr, remote_addr; + uint16_t remote_port; nw_path_t path = nw_connection_copy_current_path(connection); nw_endpoint_t local = nw_path_copy_effective_local_endpoint(path); nw_endpoint_t remote = nw_path_copy_effective_remote_endpoint(path); __block nw_interface_t interface; - const struct sockaddr * local_addr = nw_endpoint_get_address(local); - const struct sockaddr * remote_addr = nw_endpoint_get_address(remote); + const struct sockaddr * local_sa = nw_endpoint_get_address(local); + const struct sockaddr * remote_sa = nw_endpoint_get_address(remote); - assert (local_addr->sa_family == remote_addr->sa_family); - switch(local_addr->sa_family) { + assert (local_sa->sa_family == remote_sa->sa_family); + switch(local_sa->sa_family) { case AF_INET: - sin = (struct sockaddr_in *)local_addr; - sin->sin_port = htons(DEFAULT_PORT); - sin = (struct sockaddr_in *)remote_addr; - sin->sin_port = htons(DEFAULT_PORT); + local_addr.v4.as_inaddr = ((struct sockaddr_in *)local_sa)->sin_addr; + remote_addr.v4.as_inaddr = ((struct sockaddr_in *)remote_sa)->sin_addr; + remote_port = ((struct sockaddr_in *)remote_sa)->sin_port; break; case AF_INET6: - sin6 = (struct sockaddr_in6 *)local_addr; - sin6->sin6_port = htons(DEFAULT_PORT); - sin6 = (struct sockaddr_in6 *)remote_addr; - sin6->sin6_port = htons(DEFAULT_PORT); + local_addr.v6.as_in6addr = ((struct sockaddr_in6 *)local_sa)->sin6_addr; + remote_addr.v6.as_in6addr = ((struct sockaddr_in6 *)remote_sa)->sin6_addr; + remote_port = ((struct sockaddr_in6 *)remote_sa)->sin6_port; break; default: - ERROR("Unsupported address family: %d\n", local_addr->sa_family); + ERROR("Unsupported address family: %d\n", local_sa->sa_family); return NULL; } - face = face_create_udp_sa(local_addr, remote_addr); /* Retrieving path interface type (a single one expected */ nw_path_enumerate_interfaces(path, (nw_path_enumerate_interfaces_block_t)^(nw_interface_t path_interface) { interface = path_interface; return false; }); - nw_interface_type_t type = nw_interface_get_type(interface); - const char * name = nw_interface_get_name(interface); - - policy_tags_t tags = POLICY_TAGS_EMPTY; - if (rules) { - if (!FACEMGR_IS_ERROR(face_rules_get(rules, name, &tags))) - goto SET_TAGS; + const char * name = nw_interface_get_name(interface); + netdevice_t netdevice; + snprintf(netdevice.name, IFNAMSIZ, "%s", name); + netdevice_update_index(&netdevice); - char tags[MAXSZ_POLICY_TAGS]; - policy_tags_snprintf(tags, MAXSZ_POLICY_TAGS, face->tags); - } + netdevice_type_t netdevice_type; + nw_interface_type_t type = nw_interface_get_type(interface); switch(type) { case INTERFACE_TYPE_OTHER: - policy_tags_add(&tags, POLICY_TAG_WIFI); - policy_tags_add(&tags, POLICY_TAG_TRUSTED); + netdevice_type = NETDEVICE_TYPE_UNDEFINED; break; case INTERFACE_TYPE_WIFI: - // XXX disambuiguate on interface name for now. - policy_tags_add(&tags, POLICY_TAG_WIFI); - policy_tags_add(&tags, POLICY_TAG_TRUSTED); + netdevice_type = NETDEVICE_TYPE_WIFI; break; case INTERFACE_TYPE_CELLULAR: - policy_tags_add(&tags, POLICY_TAG_CELLULAR); + netdevice_type = NETDEVICE_TYPE_CELLULAR; break; case INTERFACE_TYPE_WIRED: - /* Both VPN and USB WiFi are not well detected on MacOS. For USB - * WiFi, we currently have no solution. For VPN, until we have - * proper support of AnyC APIs, we need to have heuristics to - * determine VPN interfaces. */ - policy_tags_add(&tags, POLICY_TAG_WIRED); - policy_tags_add(&tags, POLICY_TAG_TRUSTED); + netdevice_type = NETDEVICE_TYPE_WIRED; break; case INTERFACE_TYPE_LOOPBACK: - tags = POLICY_TAGS_EMPTY; + netdevice_type = NETDEVICE_TYPE_LOOPBACK; break; default: break; } -SET_TAGS: - face_set_tags(face, tags); - nw_release(local); nw_release(remote); nw_release(path); - return face; + facelet = facelet_create(); + if (!facelet) + return NULL; + + facelet_set_netdevice(facelet, netdevice); + facelet_set_netdevice_type(facelet, netdevice_type); + facelet_set_family(facelet, local_sa->sa_family); + facelet_set_local_addr(facelet, local_addr); + facelet_set_remote_addr(facelet, remote_addr); + facelet_set_remote_port(facelet, remote_port); + + return facelet; } void @@ -340,9 +336,11 @@ on_connection_state_event(interface_t * interface, nw_interface_t iface, dump_connection(cnx, 1); }); #endif - nf_data_t * data = (nf_data_t*)interface->data; - face_t * face = face_create_from_connection(cnx, data->rules); - event_raise(EVENT_TYPE_CREATE, face, interface); + facelet_t * facelet = facelet_create_from_connection(cnx); + if (!facelet) + return; + facelet_set_event(facelet, FACELET_EVENT_CREATE); + facelet_raise_event(facelet, interface); break; } case nw_connection_state_failed: @@ -482,14 +480,11 @@ void on_interface_event(interface_t * interface, nw_interface_t iface) * This is the first time we have a connection with address and port * and thus the full identification of an hICN face */ - nf_data_t * data = (nf_data_t*)interface->data; - face_t * face = face_create_from_connection(connection, data->rules); - //event_raise(value ? EVENT_TYPE_SET_UP : EVENT_TYPE_SET_DOWN, face, interface); - if(value) { - event_raise(EVENT_TYPE_CREATE, face, interface); - } else { - event_raise(EVENT_TYPE_DELETE, face, interface); - } + facelet_t * facelet = facelet_create_from_connection(connection); + if (!facelet) + return; + facelet_set_event(facelet, value ? FACELET_EVENT_CREATE : FACELET_EVENT_DELETE); + facelet_raise_event(facelet, interface); }); @@ -528,13 +523,16 @@ void on_path_event(interface_t * interface, nw_path_t path) } -int nf_initialize(interface_t * interface, face_rules_t * rules, void ** pdata) +int nf_initialize(interface_t * interface, void * cfg) { nf_data_t * data = malloc(sizeof(nf_data_t)); if (!data) goto ERR_MALLOC; - data->rules = rules; + if (cfg) + data->cfg = * (network_framework_cfg_t *)cfg; + else + data->cfg.rules = NULL; data->pm = nw_path_monitor_create(); if (!data->pm) @@ -552,14 +550,13 @@ int nf_initialize(interface_t * interface, face_rules_t * rules, void ** pdata) DEBUG("Starting network path monitor"); nw_path_monitor_start(data->pm); - *pdata = data; - return FACEMGR_SUCCESS; + interface->data = data; + return 0; ERR_PM: free(data); ERR_MALLOC: - *pdata = NULL; - return FACEMGR_FAILURE; + return -1; } int nf_finalize(interface_t * interface) @@ -569,12 +566,11 @@ int nf_finalize(interface_t * interface) nw_path_monitor_cancel(data->pm); data->pm = NULL; } - return FACEMGR_SUCCESS; + return 0; } const interface_ops_t network_framework_ops = { .type = "network_framework", - .is_singleton = true, .initialize = nf_initialize, .finalize = nf_finalize, .on_event = NULL, diff --git a/ctrl/facemgr/src/face_cache.c b/ctrl/facemgr/src/interfaces/network_framework/network_framework.h index bee36af30..edb35e904 100644 --- a/ctrl/facemgr/src/face_cache.c +++ b/ctrl/facemgr/src/interfaces/network_framework/network_framework.h @@ -13,9 +13,10 @@ * limitations under the License. */ -#include "face_cache.h" - -#include "face.h" -#include "util/set.h" +/** + * \file network_framework.h + * \brief Network framework interface + */ -TYPEDEF_SET(face_cache, face_t *, face_cmp, face_snprintf); +typedef struct { +} network_framework_cfg_t; diff --git a/ctrl/facemgr/src/interfaces/updown/CMakeLists.txt b/ctrl/facemgr/src/interfaces/updown/CMakeLists.txt new file mode 100644 index 000000000..e5fd2167e --- /dev/null +++ b/ctrl/facemgr/src/interfaces/updown/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. + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/updown.h +) + +list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/updown.c +) + +list(APPEND INCLUDE_DIRS +) + +list(APPEND LIBRARIES +) + +set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) +set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) +set(INCLUDE_DIRS ${INCLUDE_DIRS} PARENT_SCOPE) +set(LIBRARIES ${LIBRARIES} PARENT_SCOPE) diff --git a/ctrl/facemgr/src/interfaces/updown/updown.c b/ctrl/facemgr/src/interfaces/updown/updown.c new file mode 100644 index 000000000..f5a813cd4 --- /dev/null +++ b/ctrl/facemgr/src/interfaces/updown/updown.c @@ -0,0 +1,138 @@ +/* + * 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 updown.c + * \brief Implementation of Example updown interface + */ + +#include <assert.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <hicn/facemgr.h> + +#include "../../common.h" +#include "../../facelet.h" +#include "../../interface.h" + +/** + * \brief Default unix socket path (the leading \0 means using the abstract + * namespace instead of the filesystem). + */ +#define UNIX_PATH "\0updownsrv" + +typedef struct { + int fd; /* Unix client socket */ +} updown_data_t; + +int updown_initialize(interface_t * interface, void * cfg) +{ + struct sockaddr_un addr; + char * socket_path = UNIX_PATH; + + updown_data_t * data = malloc(sizeof(updown_data_t)); + if (!data) + goto ERR_MALLOC; + interface->data = data; + + data->fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (data->fd == -1) { + perror("socket error"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + if (*socket_path == '\0') { + *addr.sun_path = '\0'; + strncpy(addr.sun_path+1, socket_path+1, sizeof(addr.sun_path)-2); + } else { + strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1); + } + + if (connect(data->fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { + perror("connect error"); + return -1; + } + + return data->fd; + +ERR_MALLOC: + return -1; +} + +int updown_finalize(interface_t * interface) +{ + updown_data_t * data = (updown_data_t*)interface->data; + + if (data->fd > 0) + close(data->fd); + + return 0; +} + +int updown_callback(interface_t * interface) +{ + updown_data_t * data = (updown_data_t*)interface->data; + char buf[100]; + int rc; + + rc = read(data->fd, buf, sizeof(buf)); + if (rc < 0) + return -1; + + /* + * If the process is paused (eg. in a debugger, we might have more than one + * read. + * XXX how big is the buffer + * XXX shall we drain the queue if it exceeds buffer size ? + */ + //assert(rc == 1); + + /* Raise facelet update event */ + facelet_t * facelet = facelet_create(); + facelet_set_netdevice_type(facelet, NETDEVICE_TYPE_WIFI); //CELLULAR); + facelet_set_status(facelet, FACELET_STATUS_CLEAN); + switch(buf[0]) { + case '\0': + facelet_set_admin_state(facelet, FACE_STATE_DOWN); + break; + case '\1': + facelet_set_admin_state(facelet, FACE_STATE_UP); + break; + break; + default: + ERROR("Invalid data received from updown server. Ignoring..."); + facelet_free(facelet); + return -1; + } + + facelet_set_event(facelet, FACELET_EVENT_UPDATE); + + facelet_raise_event(facelet, interface); + + return 0; +} + +interface_ops_t updown_ops = { + .type = "updown", + .initialize = updown_initialize, + .finalize = updown_finalize, + .callback = updown_callback, +}; diff --git a/ctrl/facemgr/src/main.c b/ctrl/facemgr/src/main.c index 6a80d806b..2a336db8f 100644 --- a/ctrl/facemgr/src/main.c +++ b/ctrl/facemgr/src/main.c @@ -18,8 +18,21 @@ * \brief Face manager daemon entry point */ +#ifdef WITH_THREAD +#ifndef __linux__ +#error "Not implemented" +#endif /* __linux__ */ +#include <pthread.h> +#endif /* WITH_THREAD */ + +#ifndef __APPLE__ +#include <event2/event.h> +#include <event2/thread.h> +#endif /* __APPLE__ */ + #include <getopt.h> #include <limits.h> +#include <signal.h> #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -27,20 +40,41 @@ #include <libconfig.h> -#include "util/log.h" -#include "util/policy.h" - #ifdef __APPLE__ #include <Dispatch/Dispatch.h> #else -// Note: we might want to use libevent on Apple too #include <event2/event.h> #endif -#include "facemgr.h" +#include <hicn/facemgr.h> +#include <hicn/policy.h> + +#include <hicn/util/ip_address.h> +#include <hicn/util/log.h> + +#include <hicn/facemgr/cfg.h> #define FACEMGR_TIMEOUT 3 +static struct event_base * loop; + +void facemgr_signal_handler(int signal) { + fprintf(stderr, "Received ^C... quitting !\n"); + exit(0); +#if 0 + return; + + // FIXME + + /* should be atomic */ + // FIXME Don't use loop in a static variable as we should not need it if all + // events are properly unregistered... +#endif +#ifdef __linux__ + event_base_loopbreak(loop); +#endif /* __linux__ */ + loop = NULL; +} static struct option long_options[] = { @@ -101,106 +135,305 @@ int parse_cmdline(int argc, char ** argv, facemgr_options_t * opts) return 0; } -int parse_config_file(const char * cfgpath, facemgr_t * facemgr) +int +parse_config_global(facemgr_cfg_t * cfg, config_setting_t * setting) { - /* Reading configuration file */ - config_t cfg; - config_setting_t *setting; + /* - face_type */ + + const char *face_type_str; + facemgr_face_type_t face_type; + if (config_setting_lookup_string(setting, "face_type", &face_type_str)) { + if (strcasecmp(face_type_str, "auto") == 0) { + face_type = FACEMGR_FACE_TYPE_DEFAULT; + } else + if (strcasecmp(face_type_str, "native-udp") == 0) { + face_type = FACEMGR_FACE_TYPE_NATIVE_UDP; + } else + if (strcasecmp(face_type_str, "native-tcp") == 0) { + face_type = FACEMGR_FACE_TYPE_NATIVE_TCP; + } else + if (strcasecmp(face_type_str, "overlay-udp") == 0) { + face_type = FACEMGR_FACE_TYPE_OVERLAY_UDP; + } else + if (strcasecmp(face_type_str, "overlay-tcp") == 0) { + face_type = FACEMGR_FACE_TYPE_OVERLAY_TCP; + } else { + ERROR("Invalid face type in section 'global'"); + return -1; + } - config_init(&cfg); + int rc = facemgr_cfg_set_face_type(cfg, &face_type); + if (rc < 0) + goto ERR; + } - /* Read the file. If there is an error, report it and exit. */ - if(!config_read_file(&cfg, cfgpath)) - goto ERR_FILE; + /* - disable_discovery */ - setting = config_lookup(&cfg, "log"); - if (setting) { - const char *log_level_str; - if (config_setting_lookup_string(setting, "log_level", &log_level_str)) { - if (strcmp(log_level_str, "FATAL") == 0) { - log_conf.log_level = LOG_FATAL; - } else - if (strcmp(log_level_str, "ERROR") == 0) { - log_conf.log_level = LOG_ERROR; - } else - if (strcmp(log_level_str, "WARN") == 0) { - log_conf.log_level = LOG_WARN; - } else - if (strcmp(log_level_str, "INFO") == 0) { - log_conf.log_level = LOG_INFO; - } else - if (strcmp(log_level_str, "DEBUG") == 0) { - log_conf.log_level = LOG_DEBUG; - } else - if (strcmp(log_level_str, "TRACE") == 0) { - log_conf.log_level = LOG_TRACE; - } else { - printf("Ignored unknown log level\n"); - } - } + int disable_discovery; + if (config_setting_lookup_bool(setting, "disable_discovery", + &disable_discovery)) { + int rc = facemgr_cfg_set_discovery(cfg, !disable_discovery); + if (rc < 0) + goto ERR; } - setting = config_lookup(&cfg, "faces.overlay.ipv4"); - if (setting) { - const char * ip_address; - int local_port, remote_port; - if (config_setting_lookup_int(setting, "local_port", &local_port)) { - if ((local_port < 0) || (local_port > MAX_PORT)) + /* - disable_ipv4 */ + + int disable_ipv4; + if (config_setting_lookup_bool(setting, "disable_ipv4", + &disable_ipv4)) { + INFO("Ignored setting 'disable_ipv4' in section 'global' (not implemented)."); +#if 0 + int rc = facemgr_cfg_set_ipv4(cfg, !disable_ipv4); + if (rc < 0) + goto ERR; +#endif + } + + /* - disable ipv6 */ + + int disable_ipv6; + if (config_setting_lookup_bool(setting, "disable_ipv6", + &disable_ipv6)) { + INFO("Ignored setting 'disable_ipv6' in section 'global': (not implemented)."); +#if 0 + int rc = facemgr_cfg_set_ipv6(cfg, !disable_ipv6); + if (rc < 0) + goto ERR; +#endif + } + + /* - overlay */ + config_setting_t *overlay = config_setting_get_member(setting, "overlay"); + if (overlay) { + + /* ipv4 */ + config_setting_t *overlay_v4 = config_setting_get_member(overlay, "ipv4"); + if (overlay_v4) { + const char * local_addr_str, * remote_addr_str; + ip_address_t local_addr, remote_addr; + ip_address_t * local_addr_p = NULL; + ip_address_t * remote_addr_p = NULL; + int local_port = 0; + int remote_port = 0; + + if (config_setting_lookup_string(overlay_v4, "local_addr", &local_addr_str)) { + ip_address_pton(local_addr_str, &local_addr); + local_addr_p = &local_addr; + } + + if (config_setting_lookup_int(overlay_v4, "local_port", &local_port)) { + if (!IS_VALID_PORT(local_port)) + goto ERR; + } + + if (config_setting_lookup_string(overlay_v4, "remote_addr", &remote_addr_str)) { + ip_address_pton(remote_addr_str, &remote_addr); + remote_addr_p = &remote_addr; + } + + if (config_setting_lookup_int(overlay_v4, "remote_port", &remote_port)) { + if (!IS_VALID_PORT(remote_port)) + goto ERR; + } + int rc = facemgr_cfg_set_overlay(cfg, AF_INET, + local_addr_p, local_port, + remote_addr_p, remote_port); + if (rc < 0) goto ERR; - facemgr->overlay_v4_local_port = (uint16_t)local_port; } - if (config_setting_lookup_int(setting, "remote_port", &remote_port)) { - if ((remote_port < 0) || (remote_port > MAX_PORT)) + /* ipv6 */ + config_setting_t *overlay_v6 = config_setting_get_member(overlay, "ipv6"); + if (overlay_v6) { + const char * local_addr_str, * remote_addr_str; + ip_address_t local_addr, remote_addr; + ip_address_t * local_addr_p = NULL; + ip_address_t * remote_addr_p = NULL; + int local_port = 0; + int remote_port = 0; + + if (config_setting_lookup_string(overlay_v6, "local_addr", &local_addr_str)) { + ip_address_pton(local_addr_str, &local_addr); + local_addr_p = &local_addr; + } + + if (config_setting_lookup_int(overlay_v6, "local_port", &local_port)) { + if (!IS_VALID_PORT(local_port)) + goto ERR; + } + + if (config_setting_lookup_string(overlay_v6, "remote_addr", &remote_addr_str)) { + ip_address_pton(remote_addr_str, &remote_addr); + remote_addr_p = &remote_addr; + } + + if (config_setting_lookup_int(overlay_v6, "remote_port", &remote_port)) { + if (!IS_VALID_PORT(remote_port)) + goto ERR; + } + int rc = facemgr_cfg_set_overlay(cfg, AF_INET6, + local_addr_p, local_port, + remote_addr_p, remote_port); + if (rc < 0) goto ERR; - facemgr->overlay_v4_remote_port = (uint16_t)remote_port; } - if (config_setting_lookup_string(setting, "remote_addr", &ip_address)) { - ip_address_pton(ip_address, &facemgr->overlay_v4_remote_addr); - printf("got v4 remote addr\n"); + } /* overlay */ + + return 0; + +ERR: + return -1; +} + +int +parse_config_rules(facemgr_cfg_t * cfg, config_setting_t * setting) +{ + /* List of match-override tuples */ + facemgr_cfg_rule_t * rule; + + int count = config_setting_length(setting); + for (unsigned i = 0; i < count; ++i) { + config_setting_t * rule_setting = config_setting_get_elem(setting, i); + + /* Sanity check */ + + config_setting_t * match_setting = config_setting_get_member(rule_setting, "match"); + if (!match_setting) { + ERROR("Missing match section in rule #%d", i); + goto ERR_CHECK; } - } - setting = config_lookup(&cfg, "faces.overlay.ipv6"); - if (setting) { - const char * ip_address; - int local_port, remote_port; - if (config_setting_lookup_int(setting, "local_port", &local_port)) { - if ((local_port < 0) || (local_port > MAX_PORT)) + config_setting_t * override_setting = config_setting_get_member(rule_setting, "override"); + if (!override_setting) { + ERROR("Missing override section in rule #%d", i); + goto ERR_CHECK; + } + + rule = facemgr_cfg_rule_create(); + if (!rule) + goto ERR_RULE; + + /* Parse match */ + + const char * interface_name = NULL; + config_setting_lookup_string(match_setting, "interface_name", &interface_name); + + const char * interface_type_str; + netdevice_type_t interface_type = NETDEVICE_TYPE_UNDEFINED; + if (config_setting_lookup_string(match_setting, "interface_type", &interface_type_str)) { + if (strcasecmp(interface_type_str, "wired") == 0) { + interface_type = NETDEVICE_TYPE_WIRED; + } else + if (strcasecmp(interface_type_str, "wifi") == 0) { + interface_type = NETDEVICE_TYPE_WIFI; + } else + if (strcasecmp(interface_type_str, "cellular") == 0) { + interface_type = NETDEVICE_TYPE_CELLULAR; + } else { + ERROR("Unknown interface type in rule #%d", i); goto ERR; - facemgr->overlay_v6_local_port = (uint16_t)local_port; + } } - if (config_setting_lookup_int(setting, "remote_port", &remote_port)) { - if ((remote_port < 0) || (remote_port > MAX_PORT)) + if ((!interface_name) && (interface_type == NETDEVICE_TYPE_UNDEFINED)) { + ERROR("Empty match section in rule #%d", i); + goto ERR; + } + + /* Associate match to rule */ + + int rc = facemgr_cfg_rule_set_match(rule, interface_name, interface_type); + if (rc < 0) + goto ERR; + + /* Parse override */ + + /* - face_type */ + + const char *face_type_str; + facemgr_face_type_t face_type; + if (config_setting_lookup_string(override_setting, "face_type", &face_type_str)) { + if (strcasecmp(face_type_str, "auto")) { + /* We currently hardcode different behaviours based on the OS */ +#ifdef __ANDROID__ + face_type = FACEMGR_FACE_TYPE_OVERLAY_UDP; +#else + face_type = FACEMGR_FACE_TYPE_NATIVE_TCP; +#endif + } else + if (strcasecmp(face_type_str, "native-udp") == 0) { + face_type = FACEMGR_FACE_TYPE_NATIVE_UDP; + } else + if (strcasecmp(face_type_str, "native-tcp") == 0) { + face_type = FACEMGR_FACE_TYPE_NATIVE_TCP; + } else + if (strcasecmp(face_type_str, "overlay-udp") == 0) { + face_type = FACEMGR_FACE_TYPE_OVERLAY_UDP; + } else + if (strcasecmp(face_type_str, "overlay-tcp") == 0) { + face_type = FACEMGR_FACE_TYPE_OVERLAY_TCP; + } else { + ERROR("Invalid face type in section 'global'"); + return -1; + } + + int rc = facemgr_cfg_rule_set_face_type(rule, &face_type); + if (rc < 0) goto ERR; - facemgr->overlay_v6_remote_port = (uint16_t)remote_port; } - if (config_setting_lookup_string(setting, "remote_addr", &ip_address)) - ip_address_pton(ip_address, &facemgr->overlay_v6_remote_addr); - } + /* - disable_discovery */ - setting = config_lookup(&cfg, "faces.rules"); - if (setting) { - int count = config_setting_length(setting); - for(unsigned i = 0; i < count; ++i) { - const char *interface_name; - policy_tags_t tags = POLICY_TAGS_EMPTY; + int disable_discovery; + if (config_setting_lookup_bool(override_setting, "disable_discovery", + &disable_discovery)) { + int rc = facemgr_cfg_rule_set_discovery(rule, !disable_discovery); + if (rc < 0) + goto ERR; + } - config_setting_t *rule = config_setting_get_elem(setting, i); + /* - disable_ipv4 */ - /* Interface name */ - if(!(config_setting_lookup_string(rule, "name", &interface_name))) - continue; + int disable_ipv4; + if (config_setting_lookup_bool(override_setting, "disable_ipv4", + &disable_ipv4)) { + INFO("Ignored setting 'disable_ipv4' in rule #%d (not implemented).", i); +#if 0 + int rc = facemgr_cfg_rule_set_ipv4(rule, !disable_ipv4); + if (rc < 0) + goto ERR; +#endif + } - /* Associated tags */ - config_setting_t *tag_settings = config_setting_get_member(rule, "tags"); - if (!tag_settings) + /* - disable ipv6 */ + + int disable_ipv6; + if (config_setting_lookup_bool(override_setting, "disable_ipv6", + &disable_ipv6)) { + INFO("Ignored setting 'disable_ipv6' in rule #%d (not implemented).", i); +#if 0 + int rc = facemgr_cfg_rule_set_ipv6(rule, !disable_ipv6); + if (rc < 0) goto ERR; +#endif + } + /* - ignore */ + int ignore; + if (config_setting_lookup_bool(override_setting, "ignore", &ignore)) { + int rc = facemgr_cfg_rule_set_ignore(rule, !!ignore); + if (rc < 0) + goto ERR; + } + /* - tags */ + config_setting_t *tag_settings = config_setting_get_member(override_setting, "tags"); + if (tag_settings) { + INFO("Ignored setting 'tags' in rule #%d (not implemented).", i); +#if 0 + policy_tags_t tags = POLICY_TAGS_EMPTY; for (unsigned j = 0; j < config_setting_length(tag_settings); j++) { const char * tag_str = config_setting_get_string_elem(tag_settings, j); policy_tag_t tag = policy_tag_from_str(tag_str); @@ -209,41 +442,267 @@ int parse_config_file(const char * cfgpath, facemgr_t * facemgr) policy_tags_add(&tags, tag); } - /* debug */ + int rc = facemgr_cfg_rule_set_tags(rule, tags); + if (rc < 0) + goto ERR; + +#if 0 char tags_str[MAXSZ_POLICY_TAGS]; policy_tags_snprintf(tags_str, MAXSZ_POLICY_TAGS, tags); - printf("Rule #%d interface_name=%s, tags=%s\n", i, interface_name, tags_str); - face_rules_add(&facemgr->rules, strdup(interface_name), tags); + DEBUG("Added tags tags=%s", tags_str); +#endif +#endif } + + /* - overlay */ + config_setting_t *overlay = config_setting_get_member(override_setting, "overlay"); + if (overlay) { + + /* ipv4 */ + config_setting_t *overlay_v4 = config_setting_get_member(overlay, "ipv4"); + if (overlay_v4) { + const char * local_addr_str, * remote_addr_str; + ip_address_t local_addr, remote_addr; + ip_address_t * local_addr_p = NULL; + ip_address_t * remote_addr_p = NULL; + int local_port = 0; + int remote_port = 0; + + if (config_setting_lookup_string(overlay_v4, "local_addr", &local_addr_str)) { + ip_address_pton(local_addr_str, &local_addr); + local_addr_p = &local_addr; + } + + if (config_setting_lookup_int(overlay_v4, "local_port", &local_port)) { + if (!IS_VALID_PORT(local_port)) + goto ERR; + } + + if (config_setting_lookup_string(overlay_v4, "remote_addr", &remote_addr_str)) { + ip_address_pton(remote_addr_str, &remote_addr); + remote_addr_p = &remote_addr; + } + + if (config_setting_lookup_int(overlay_v4, "remote_port", &remote_port)) { + if (!IS_VALID_PORT(remote_port)) + goto ERR; + } + int rc = facemgr_cfg_rule_set_overlay(rule, AF_INET, + local_addr_p, local_port, + remote_addr_p, remote_port); + if (rc < 0) + goto ERR; + } + + /* ipv6 */ + config_setting_t *overlay_v6 = config_setting_get_member(overlay, "ipv6"); + if (overlay_v6) { + const char * local_addr_str, * remote_addr_str; + ip_address_t local_addr, remote_addr; + ip_address_t * local_addr_p = NULL; + ip_address_t * remote_addr_p = NULL; + int local_port = 0; + int remote_port = 0; + + if (config_setting_lookup_string(overlay_v6, "local_addr", &local_addr_str)) { + ip_address_pton(local_addr_str, &local_addr); + local_addr_p = &local_addr; + } + + if (config_setting_lookup_int(overlay_v6, "local_port", &local_port)) { + if (!IS_VALID_PORT(local_port)) + goto ERR; + } + + if (config_setting_lookup_string(overlay_v6, "remote_addr", &remote_addr_str)) { + ip_address_pton(remote_addr_str, &remote_addr); + remote_addr_p = &remote_addr; + } + + if (config_setting_lookup_int(overlay_v6, "remote_port", &remote_port)) { + if (!IS_VALID_PORT(remote_port)) + goto ERR; + } + int rc = facemgr_cfg_rule_set_overlay(rule, AF_INET6, + local_addr_p, local_port, + remote_addr_p, remote_port); + if (rc < 0) + goto ERR; + } + + } /* overlay */ + + /* Add newly created rule */ + + rc = facemgr_cfg_add_rule(cfg, rule); + if (rc < 0) + goto ERR; } + return 0; + +ERR: + facemgr_cfg_rule_free(rule); +ERR_RULE: +ERR_CHECK: + return -1; +} - config_destroy(&cfg); +/* Currently not using facemgr_cfg_t */ +int +parse_config_log(facemgr_cfg_t * cfg, config_setting_t * setting) +{ + const char *log_level_str; + if (config_setting_lookup_string(setting, "log_level", &log_level_str)) { + if (strcasecmp(log_level_str, "FATAL") == 0) { + log_conf.log_level = LOG_FATAL; + } else + if (strcasecmp(log_level_str, "ERROR") == 0) { + log_conf.log_level = LOG_ERROR; + } else + if (strcasecmp(log_level_str, "WARN") == 0) { + log_conf.log_level = LOG_WARN; + } else + if (strcasecmp(log_level_str, "INFO") == 0) { + log_conf.log_level = LOG_INFO; + } else + if (strcasecmp(log_level_str, "DEBUG") == 0) { + log_conf.log_level = LOG_DEBUG; + } else + if (strcasecmp(log_level_str, "TRACE") == 0) { + log_conf.log_level = LOG_TRACE; + } else { + ERROR("Invalid log level in section 'log'"); + return -1; + } + } + return 0; +} + +int +parse_config_file(const char * cfgpath, facemgr_cfg_t * cfg) +{ + /* Reading configuration file */ + config_t cfgfile; + config_setting_t *setting; + + config_init(&cfgfile); + + /* Read the file. If there is an error, report it and exit. */ + if(!config_read_file(&cfgfile, cfgpath)) + goto ERR_FILE; + + setting = config_lookup(&cfgfile, "global"); + if (setting) { + int rc = parse_config_global(cfg, setting); + if (rc < 0) + goto ERR_PARSE; + } + + setting = config_lookup(&cfgfile, "rules"); + if (setting) { + int rc = parse_config_rules(cfg, setting); + if (rc < 0) + goto ERR_PARSE; + } + + setting = config_lookup(&cfgfile, "log"); + if (setting) { + int rc = parse_config_log(cfg, setting); + if (rc < 0) + goto ERR_PARSE; + } + + config_destroy(&cfgfile); return 0; ERR_FILE: - printf("Could not read configuration file %s\n", cfgpath); - fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), - config_error_line(&cfg), config_error_text(&cfg)); - config_destroy(&cfg); + ERROR("Could not read configuration file %s", cfgpath); + fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfgfile), + config_error_line(&cfgfile), config_error_text(&cfgfile)); + config_destroy(&cfgfile); exit(EXIT_FAILURE); return -1; -ERR: - fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfg), - config_error_line(&cfg), config_error_text(&cfg)); - config_destroy(&cfg); +ERR_PARSE: + fprintf(stderr, "%s:%d - %s\n", config_error_file(&cfgfile), + config_error_line(&cfgfile), config_error_text(&cfgfile)); + config_destroy(&cfgfile); return -1; } -#ifndef APPLE -void dummy_handler(int fd, short event, void *arg) { } -#endif /* APPLE */ +#ifdef __linux__ +typedef struct { + void (*cb)(void *, ...); + void * args; +} cb_wrapper_args_t; + +void cb_wrapper(evutil_socket_t fd, short what, void * arg) { + cb_wrapper_args_t * cb_wrapper_args = arg; + cb_wrapper_args->cb(cb_wrapper_args->args); +} -int main(int argc, char **argv) +struct event * +loop_register_fd(struct event_base * loop, int fd, void * cb, void * cb_args) { - facemgr_t * facemgr = facemgr_create(); - if (!facemgr) - goto ERR_FACEMGR; + // TODO: not freed + cb_wrapper_args_t * cb_wrapper_args = malloc(sizeof(cb_wrapper_args_t)); + *cb_wrapper_args = (cb_wrapper_args_t) { + .cb = cb, + .args = cb_args, + }; + + evutil_make_socket_nonblocking(fd); + struct event * event = event_new(loop, fd, EV_READ | EV_PERSIST, cb_wrapper, cb_wrapper_args); + if (!event) + goto ERR_EVENT_NEW; + + if (event_add(event, NULL) < 0) + goto ERR_EVENT_ADD; + + return event; + +ERR_EVENT_ADD: + event_free(event); +ERR_EVENT_NEW: + return NULL; +} + +int +loop_unregister_event(struct event_base * loop, struct event * event) +{ + if (!event) + return 0; + + event_del(event); + event_free(event); + + return 0; +} + + +void * start_dispatch(void * loop_ptr) +{ + struct event_base * loop = (struct event_base *) loop_ptr; + event_base_dispatch(loop); + + return NULL; +} + +#endif /* __linux__ */ + +int main(int argc, char ** argv) +{ + facemgr_cfg_t * cfg = NULL; + facemgr_t * facemgr; +#ifdef WITH_THREAD + pthread_t facemgr_thread; +#endif /* WITH_THREAD */ + + struct sigaction sigIntHandler; + sigIntHandler.sa_handler = facemgr_signal_handler; + sigemptyset(&sigIntHandler.sa_mask); + sigIntHandler.sa_flags = 0; + sigaction(SIGINT, &sigIntHandler, NULL); char cfgfile[PATH_MAX]; @@ -252,7 +711,7 @@ int main(int argc, char **argv) /* Commandline */ facemgr_options_t cmdline_opts = {0}; if (parse_cmdline(argc, argv, &cmdline_opts) < 0) { - ERROR("Error parsing commandline\n"); + ERROR("Error parsing commandline"); goto ERR_CMDLINE; } @@ -260,7 +719,7 @@ int main(int argc, char **argv) //facemgr_options_t cfgfile_opts; if (cmdline_opts.cfgfile) { - if (strcmp(cmdline_opts.cfgfile, "none") == 0) + if (strcasecmp(cmdline_opts.cfgfile, "none") == 0) goto NO_CFGFILE; if (!realpath(cmdline_opts.cfgfile, (char*)&cfgfile)) @@ -274,76 +733,118 @@ int main(int argc, char **argv) if (probe_cfgfile(cfgfile) < 0) goto NO_CFGFILE; - printf("Using configuration file %s\n", cfgfile); - PARSE_CFGFILE: - if (parse_config_file(cfgfile, facemgr) < 0) { - ERROR("Error parsing configuration file %s\n", cfgfile); + DEBUG("Using configuration file %s", cfgfile); + cfg = facemgr_cfg_create(); + if (!cfg) + goto ERR_FACEMGR_CFG; + + if (parse_config_file(cfgfile, cfg) < 0) { + ERROR("Error parsing configuration file %s", cfgfile); goto ERR_PARSE; } + facemgr = facemgr_create_with_config(cfg); + if (!facemgr) + goto ERR_FACEMGR_CONFIG; + + goto MAIN_LOOP; + NO_CFGFILE: + facemgr = facemgr_create(); + if (!facemgr) + goto ERR_FACEMGR; + +MAIN_LOOP: + + /* Main loop */ + + +#ifdef WITH_THREAD + evthread_use_pthreads(); +#endif /* WITH_THREAD */ + #ifdef __linux__ - facemgr->loop = event_base_new(); - if (!facemgr->loop) - fatal("Could not create an event base"); - - /* Main loop - * - * To avoid the loop to exit when empty, we might either rely on an option - * introduced from versions 2.1.x: - * event_base_loop(loop->base, EVLOOP_NO_EXIT_ON_EMPTY); - * or use this workaround: - * http://archives.seul.org/libevent/users/Sep-2012/msg00056.html - * - * TODO: - * - HUP should interrupt the main loop - */ - { - struct event *ev; - struct timeval tv; - tv.tv_sec = FACEMGR_TIMEOUT; - tv.tv_usec = 0; - - ev = event_new(facemgr->loop, fileno(stdin), EV_TIMEOUT | EV_PERSIST, dummy_handler, NULL); - event_add(ev, &tv); - } + /* Event loop */ + loop = event_base_new(); + if (!loop) + goto ERR_EVENT; + + facemgr_set_event_loop_handler(facemgr, loop, loop_register_fd, loop_unregister_event); #endif /* __linux__ */ - DEBUG("Bootstrap...\n"); +#ifdef __ANDROID__ + facemgr_set_jvm(facemgr, NULL, NULL); // FIXME +#endif /* __ ANDROID__ */ + + DEBUG("Bootstrap..."); + if (facemgr_bootstrap(facemgr) < 0 ) goto ERR_BOOTSTRAP; #ifdef __linux__ event_set_log_callback(NULL); - event_base_dispatch(facemgr->loop); - event_base_free(facemgr->loop); +#ifdef WITH_THREAD + if (pthread_create(&facemgr_thread, NULL, start_dispatch, loop)) { + fprintf(stderr, "Error creating thread\n"); + return EXIT_FAILURE; + } +#else + event_base_dispatch(loop); +#endif /* WITH_THREAD */ + #endif /* __linux__ */ #ifdef __APPLE__ /* Main loop */ - facemgr->loop = NULL; dispatch_main(); #endif /* __APPLE__ */ - /* Clean up */ - //interface_delete_all(); +#ifdef __linux__ +#ifdef WITH_THREAD + for(;;) { + facemgr_list_faces(facemgr, NULL, NULL); + sleep(5); + } +#endif /* WITH_THREAD */ +#endif /* __linux__ */ + + facemgr_stop(facemgr); +#ifdef __linux__ +#ifdef WITH_THREAD + DEBUG("Waiting for loop to terminate..."); + if(pthread_join(facemgr_thread, NULL)) { + fprintf(stderr, "Error joining thread\n"); + return EXIT_FAILURE; + } + DEBUG("Loop terminated !"); +#endif /* WITH_THREAD */ +#endif /* __linux__ */ facemgr_free(facemgr); return EXIT_SUCCESS; ERR_BOOTSTRAP: +#ifdef __linux__ +ERR_EVENT: +#endif /* __linux__ */ + + facemgr_free(facemgr); +ERR_FACEMGR_CONFIG: + if (cfg) + facemgr_cfg_free(cfg); +ERR_FACEMGR: +ERR_FACEMGR_CFG: + ERR_PARSE: ERR_PATH: ERR_CMDLINE: - facemgr_free(facemgr); -ERR_FACEMGR: return EXIT_FAILURE; -} +} diff --git a/ctrl/facemgr/src/netdevice.h b/ctrl/facemgr/src/netdevice.h deleted file mode 100644 index b64ad0f9a..000000000 --- a/ctrl/facemgr/src/netdevice.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 netdevice.h - * \brief Netdevice abstraction - */ -#ifndef FACEMGR_NETDEVICE_H -#define FACEMGR_NETDEVICE_H - -#include <net/if.h> // IFNAMSIZ - -#include "common.h" - -#define foreach_netdevice_type \ - _(UNDEFINED) \ - _(WIRED) \ - _(WIFI) \ - _(CELLULAR) \ - _(VPN) \ - _(N) - -#define MAXSZ_NETDEVICE_TYPE 9 -#define MAXSZ_NETDEVICE_TYPE_ MAXSZ_NETDEVICE_TYPE - -typedef enum { -#define _(x) x, -foreach_netdevice_type -#undef _ -} netdevice_type_t; - -extern const char * netdevice_type_str[]; - - -typedef struct { - u32 index; - char name[IFNAMSIZ]; -} netdevice_t; - -#endif /* FACEMGR_NETDEVICE_H */ diff --git a/ctrl/facemgr/src/util/ip_address.h b/ctrl/facemgr/src/util/ip_address.h deleted file mode 100644 index 243ce048b..000000000 --- a/ctrl/facemgr/src/util/ip_address.h +++ /dev/null @@ -1,328 +0,0 @@ -/* - * 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 ip_address.h - * \brief IP address type support - */ -#ifndef UTIL_IP_ADDRESS_H -#define UTIL_IP_ADDRESS_H - -#include <arpa/inet.h> // inet_ntop -#ifdef __APPLE__ -#include <libkern/OSByteOrder.h> -#define __bswap_constant_32(x) OSSwapInt32(x) -#include <machine/endian.h> -#else -#include <endian.h> -#ifdef __ANDROID__ -#include <byteswap.h> -#endif -#endif -#include <errno.h> -#include <netdb.h> // struct addrinfo -#include <netinet/in.h> // INET*_ADDRSTRLEN, IN*ADDR_LOOPBACK -#include <stdlib.h> -#include <stdio.h> // snprintf -#include <string.h> // memset - -#include "types.h" - -#define bytes_to_bits(x) (x * 8) -#define IPV6_ADDR_LEN 16 /* bytes */ -#define IPV4_ADDR_LEN 4 /* bytes */ -#define IPV6_ADDR_LEN_BITS bytes_to_bits(IPV6_ADDR_LEN) -#define IPV4_ADDR_LEN_BITS bytes_to_bits(IPV4_ADDR_LEN) - -#define IP_MAX_ADDR_LEN IPV6_ADDR_LEN - -#define DUMMY_PORT 1234 - -typedef union { - union { - struct in_addr as_inaddr; - u8 as_u8[4]; - u16 as_u16[2]; - u32 as_u32; - } v4; - union { - struct in6_addr as_in6addr; - u8 as_u8[16]; - u16 as_u16[8]; - u32 as_u32[4]; - u64 as_u64[2]; - } v6; - u8 buffer[IP_MAX_ADDR_LEN]; - u8 as_u8[IP_MAX_ADDR_LEN]; - u16 as_u16[IP_MAX_ADDR_LEN >> 1]; - u32 as_u32[IP_MAX_ADDR_LEN >> 2]; - u64 as_u64[IP_MAX_ADDR_LEN >> 3]; -} ip_address_t; - -#define MAXSZ_IP4_ADDRESS_ INET_ADDRSTRLEN - 1 -#define MAXSZ_IP6_ADDRESS_ INET6_ADDRSTRLEN - 1 -#define MAXSZ_IP_ADDRESS_ MAXSZ_IP6_ADDRESS_ -#define MAXSZ_IP4_ADDRESS MAXSZ_IP4_ADDRESS_ + 1 -#define MAXSZ_IP6_ADDRESS MAXSZ_IP6_ADDRESS_ + 1 -#define MAXSZ_IP_ADDRESS MAXSZ_IP_ADDRESS_ + 1 - -typedef struct { - int family; - ip_address_t address; - u8 len; -} ip_prefix_t; - -#define MAXSZ_PREFIX_ MAXSZ_IP_ADDRESS_ + 1 + 3 -#define MAXSZ_PREFIX MAXSZ_PREFIX_ + 1 - -/* No htonl() with const */ -static const ip_address_t IPV4_LOOPBACK = { -#if __BYTE_ORDER == __LITTLE_ENDIAN -#ifdef __ANDROID__ - .v4.as_inaddr.s_addr = bswap_32(INADDR_LOOPBACK), -#else - .v4.as_inaddr.s_addr = __bswap_constant_32(INADDR_LOOPBACK), -#endif -#else - .v4.as_inaddr.s_addr = INADDR_LOOPBACK, -#endif -}; - -static const ip_address_t IPV6_LOOPBACK = { - .v6.as_in6addr = IN6ADDR_LOOPBACK_INIT, -}; - -static const ip_address_t IPV4_ANY = { - .v4.as_inaddr.s_addr = INADDR_ANY, -}; - -static const ip_address_t IPV6_ANY = { - .v6.as_in6addr = IN6ADDR_ANY_INIT, -}; - -#define IP_ANY(family) (family == AF_INET) ? IPV4_ANY : IPV6_ANY - -static const ip_address_t IP_ADDRESS_EMPTY = { - .as_u64 = { 0 }, -}; - - -#define MAX_PORT 1 << (8 * sizeof(u16)) -#define IS_VALID_PORT(x) ((x > 0) && (x < MAX_PORT)) - -#define MAXSZ_PORT_ 5 -#define MAXSZ_PORT MAXSZ_PORT_ + 1 - -#define IS_VALID_FAMILY(x) ((x == AF_INET) || (x == AF_INET6)) - -static inline -int -ip_address_get_family (const char * ip_address) -{ - struct addrinfo hint, *res = NULL; - int rc; - - memset (&hint, '\0', sizeof hint); - - hint.ai_family = PF_UNSPEC; - hint.ai_flags = AI_NUMERICHOST; - - rc = getaddrinfo (ip_address, NULL, &hint, &res); - if (rc) - { - return -1; - } - rc = res->ai_family; - freeaddrinfo (res); - return rc; -} - -static inline -int -ip_address_len (const ip_address_t * ip_address, int family) -{ - return (family == AF_INET6) ? IPV6_ADDR_LEN : - (family == AF_INET) ? IPV4_ADDR_LEN : 0; -} - -static inline -int -ip_address_ntop (const ip_address_t * ip_address, char *dst, const size_t len, - int family) -{ - const char * s = inet_ntop (family, ip_address->buffer, dst, len); - return (s ? 1 : -1); -} - -/* - * Parse ip addresses in presentation format - */ -static inline -int -ip_address_pton (const char *ip_address_str, ip_address_t * ip_address) -{ - int pton_fd; - char *addr = strdup (ip_address_str); - int family; - - - family = ip_address_get_family (addr); - - switch (family) - { - case AF_INET6: - pton_fd = inet_pton (AF_INET6, addr, &ip_address->buffer); - break; - case AF_INET: - pton_fd = inet_pton (AF_INET, addr, &ip_address->buffer); - break; - default: - goto ERR; - } - - // 0 = not in presentation format - // < 0 = other error (use perror) - if (pton_fd <= 0) - { - goto ERR; - } - - return 1; -ERR: - free (addr); - return -1; -} - - - -static inline -int -ip_address_snprintf(char * s, size_t size, const ip_address_t * ip_address, int family) -{ - size_t len = family == AF_INET ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN; - const char * rc = inet_ntop (family, ip_address->buffer, s, len); - return rc ? strlen(rc) : -1; -} - - -static inline -int -ip_address_to_sockaddr(const ip_address_t * ip_address, - struct sockaddr *sockaddr_address, int family) -{ - struct sockaddr_in6 *tmp6 = (struct sockaddr_in6 *) sockaddr_address; - struct sockaddr_in *tmp4 = (struct sockaddr_in *) sockaddr_address; - - switch (family) - { - case AF_INET6: - tmp6->sin6_family = AF_INET6; - tmp6->sin6_port = DUMMY_PORT; - tmp6->sin6_scope_id = 0; - memcpy (&tmp6->sin6_addr, ip_address->buffer, IPV6_ADDR_LEN); - break; - case AF_INET: - tmp4->sin_family = AF_INET; - tmp4->sin_port = DUMMY_PORT; - memcpy (&tmp4->sin_addr, ip_address->buffer, IPV4_ADDR_LEN); - break; - default: - return -1; - } - - return 1; -} - -static inline -int -ip_address_cmp(const ip_address_t * ip1, const ip_address_t * ip2, int family) -{ - return memcmp(ip1, ip2, ip_address_len(ip1, family)); -} - -static inline -int -ip_address_empty(const ip_address_t * ip) -{ - return (memcmp(ip, &IP_ADDRESS_EMPTY, sizeof(IP_ADDRESS_EMPTY)) == 0); -} - -/* Parse IP Prefixes in presentation format (in bits, separated by a slash) */ -static inline -int -ip_prefix_pton (const char *ip_address_str, ip_prefix_t * ip_prefix) -{ - int pton_fd; - char *p; - char *eptr; - char *addr = strdup (ip_address_str); - - p = strchr (addr, '/'); - if (!p) - { - ip_prefix->len = 0; // until we get the ip address family - } - else - { - ip_prefix->len = strtoul (p + 1, &eptr, 10); - *p = 0; - } - - ip_prefix->family = ip_address_get_family (addr); - - switch (ip_prefix->family) - { - case AF_INET6: - if (ip_prefix->len > IPV6_ADDR_LEN_BITS) - goto ERR; - pton_fd = inet_pton (AF_INET6, addr, &ip_prefix->address.buffer); - break; - case AF_INET: - if (ip_prefix->len > IPV4_ADDR_LEN_BITS) - goto ERR; - pton_fd = inet_pton (AF_INET, addr, &ip_prefix->address.buffer); - break; - default: - goto ERR; - } - - // 0 = not in presentation format - // < 0 = other error (use perror) - if (pton_fd <= 0) - { - goto ERR; - } - - return 1; -ERR: - free (addr); - return -1; -} - -static inline -int -ip_prefix_ntop (const ip_prefix_t * ip_prefix, char *dst, size_t size) -{ - char ip_s[MAXSZ_IP_ADDRESS]; - const char * s = inet_ntop (ip_prefix->family, ip_prefix->address.buffer, ip_s, MAXSZ_IP_ADDRESS); - if (!s) - return -1; - size_t n = snprintf(dst, size, "%s/%d", ip_s, ip_prefix->len); - - return (n > 0 ? 1 : -1); -} - - -#endif /* UTIL_IP_ADDRESS_H */ diff --git a/ctrl/facemgr/src/util/log.c b/ctrl/facemgr/src/util/log.c index 54943cf45..c1fc999ad 100644 --- a/ctrl/facemgr/src/util/log.c +++ b/ctrl/facemgr/src/util/log.c @@ -13,12 +13,16 @@ * limitations under the License. */ -#include "log.h" +#include <hicn/util/log.h> #include <stdarg.h> #include <stdlib.h> #include <stdio.h> +#ifdef __ANDROID__ +#include <android/log.h> +#endif + log_conf_t log_conf = DEFAULT_LOG_CONF; #define FMT_DATETIME "%02d-%02d-%04d %02d:%02d:%02d" @@ -43,48 +47,96 @@ static char *timestamp(void) } void _log_va(int level, const char *fmt, va_list ap) -{ - char *prefix; - FILE *f = log_conf.log_file ? log_conf.log_file : stdout; +{ #if 0 if (!conf.log_system) return; #endif + char *prefix; + +#ifdef __ANDROID__ + int prio = -1; if (level > log_conf.log_level) return; switch (level) { case LOG_FATAL: + prio = ANDROID_LOG_FATAL; prefix = "FATAL: "; break; case LOG_ERROR: + prio = ANDROID_LOG_ERROR; prefix = "ERROR: "; break; case LOG_WARN: + prio = ANDROID_LOG_WARN; prefix = "WARNING: "; break; case LOG_INFO: + prio = ANDROID_LOG_INFO; prefix = ""; break; case LOG_DEBUG: + prio = ANDROID_LOG_DEBUG; prefix = "DEBUG: "; break; case LOG_TRACE: + prio = ANDROID_LOG_DEBUG; prefix = "TRACE: "; break; default: + prio = ANDROID_LOG_INFO; prefix = ""; break; } + if (log_conf.log_file) { + FILE *f = log_conf.log_file; + fprintf(f, "%s %s", timestamp(), prefix); + vfprintf(f, fmt, ap); + fprintf(f, "\n"); + } else { + __android_log_vprint(ANDROID_LOG_INFO, "HICN FACEMGR", fmt, ap); + } + +#else + + if (level > log_conf.log_level) + return; + + switch (level) { + case LOG_FATAL: + prefix = "FATAL: "; + break; + case LOG_ERROR: + prefix = "ERROR: "; + break; + case LOG_WARN: + prefix = "WARNING: "; + break; + case LOG_INFO: + prefix = ""; + break; + case LOG_DEBUG: + prefix = "DEBUG: "; + break; + case LOG_TRACE: + prefix = "TRACE: "; + break; + default: + prefix = ""; + break; + } + FILE *f = log_conf.log_file ? log_conf.log_file : stdout; fprintf(f, "%s %s", timestamp(), prefix); vfprintf(f, fmt, ap); fprintf(f, "\n"); #ifdef DEBUG fflush(f); #endif +#endif } void _log(int level, const char *fmt, ...) diff --git a/ctrl/facemgr/src/util/log.h b/ctrl/facemgr/src/util/log.h deleted file mode 100644 index f1cafba47..000000000 --- a/ctrl/facemgr/src/util/log.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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. - */ - -#ifndef UTIL_LOG_H -#define UTIL_LOG_H - -#include <stdarg.h> // va_* -#include <stdio.h> // FILE -#include <time.h> // time, localtime - -#define LOG_FATAL 0 -#define LOG_ERROR 1 -#define LOG_WARN 2 -#define LOG_INFO 3 -#define LOG_DEBUG 4 -#define LOG_TRACE 5 - -typedef struct { - int log_level; - int debug; - FILE * log_file; -} log_conf_t; - -#define DEFAULT_LOG_CONF { \ - .log_level = LOG_DEBUG, \ - .debug = 0, \ - .log_file = NULL, \ -}; - -extern log_conf_t log_conf; - -#define WITH_DEBUG(BLOCK) \ - if (log_conf.log_level >= LOG_DEBUG) \ - BLOCK - -#define FATAL(fmt, ...) (_log(LOG_FATAL, fmt, ##__VA_ARGS__ )) -#define ERROR(fmt, ...) (_log(LOG_ERROR, fmt, ##__VA_ARGS__ )) -#define WARN(fmt, ...) (_log(LOG_WARN, fmt, ##__VA_ARGS__ )) -#define INFO(fmt, ...) (_log(LOG_INFO, fmt, ##__VA_ARGS__ )) -#define DEBUG(fmt, ...) (_log(LOG_DEBUG, fmt, ##__VA_ARGS__ )) -#define TRACE(fmt, ...) (_log(LOG_TRACE, fmt, ##__VA_ARGS__ )) - -void _log_va(int level, const char *fmt, va_list ap); - -void _log(int level, const char *fmt, ...); - -void fatal(char *fmt, ...); - -#ifdef HAVE_BACKTRACE -#include <execinfo.h> -void print_trace(void); -#endif - -#endif // UTIL_LOG_H diff --git a/ctrl/facemgr/src/util/map.h b/ctrl/facemgr/src/util/map.h index 2694de2a7..b6773f209 100644 --- a/ctrl/facemgr/src/util/map.h +++ b/ctrl/facemgr/src/util/map.h @@ -31,6 +31,10 @@ typedef struct { VAL_T value; \ } NAME ## _pair_t; \ \ +NAME ## _pair_t * NAME ## _pair_create(KEY_T key, VAL_T value); \ + \ +void NAME ## _pair_free(NAME ## _pair_t * pair); \ + \ int NAME ## _pair_cmp(const NAME ## _pair_t * p1, const NAME ## _pair_t * p2); \ \ TYPEDEF_SET_H(NAME ## _pair_set, NAME ## _pair_t *) \ @@ -47,7 +51,7 @@ NAME ## _t * NAME ## _create(); \ void NAME ## _free(NAME ## _t * map); \ \ -int NAME ## _add(NAME ## _t * map, KEY_T key, const VAL_T value); \ +int NAME ## _add(NAME ## _t * map, KEY_T key, VAL_T value); \ \ int NAME ## _remove(NAME ## _t * map, KEY_T key, VAL_T * value); \ \ @@ -60,6 +64,24 @@ void NAME ## _dump(NAME ## _t * map); #define TYPEDEF_MAP(NAME, KEY_T, VAL_T, CMP, KEY_SNPRINTF, VALUE_SNPRINTF) \ \ +NAME ## _pair_t * NAME ## _pair_create(KEY_T key, VAL_T value) \ +{ \ + /* Create pair */ \ + NAME ## _pair_t * pair = malloc(sizeof(NAME ## _pair_t)); \ + if (!pair) \ + return NULL; \ + \ + pair->key = key; \ + pair->value = value; \ + \ + return pair; \ +} \ + \ +void NAME ## _pair_free(NAME ## _pair_t * pair) \ +{ \ + free(pair); \ +} \ + \ int \ NAME ## _pair_cmp(const NAME ## _pair_t * p1, const NAME ## _pair_t * p2) \ { \ @@ -72,7 +94,7 @@ NAME ## _pair_snprintf(char * buf, size_t size, const NAME ## _pair_t * pair) { rc = KEY_SNPRINTF(buf, BUFSIZE/2, (KEY_T)pair->key); \ if (rc < 0) \ return rc; \ - rc = VALUE_SNPRINTF(buf+rc, BUFSIZE/2, (VAL_T)pair->value); \ + rc = VALUE_SNPRINTF(buf+rc, BUFSIZE/2, (VAL_T)pair->value); \ return rc; \ } \ \ @@ -93,57 +115,98 @@ NAME ## _finalize(NAME ## _t * map) AUTOGENERATE_CREATE_FREE(NAME) \ \ int \ -NAME ## _add(NAME ## _t * map, KEY_T key, const VAL_T value) \ +NAME ## _add(NAME ## _t * map, KEY_T key, VAL_T value) \ { \ int rc; \ + NAME ## _pair_t * found = NULL; \ \ - /* Create pair */ \ - NAME ## _pair_t * pair = malloc(sizeof(NAME ## _pair_t)); \ + NAME ## _pair_t * pair = NAME ## _pair_create(key, value); \ if (!pair) \ - return FACEMGR_FAILURE; \ - \ - pair->key = key; \ - pair->value = (VAL_T)value; \ + return -1; \ \ - rc = NAME ## _pair_set_get(&map->pair_set, pair, NULL); \ - if (!FACEMGR_IS_ERROR(rc)) { \ - free(pair); \ + rc = NAME ## _pair_set_get(&map->pair_set, pair, &found); \ + if (rc < 0) \ + return -1; \ + if (found) { \ + NAME ## _pair_free(pair); \ return ERR_MAP_EXISTS; \ } \ \ rc = NAME ## _pair_set_add(&map->pair_set, pair); \ - if (FACEMGR_IS_ERROR(rc)) { \ - free(pair); \ - return FACEMGR_FAILURE; \ + if (rc < 0) { \ + NAME ## _pair_free(pair); \ + return -1; \ } \ - return FACEMGR_SUCCESS; \ + return 0; \ } \ \ int \ NAME ## _remove(NAME ## _t * map, KEY_T key, VAL_T * value) \ { \ - NAME ## _pair_t * found, search = { .key = key }; \ + NAME ## _pair_t * found = NULL; \ + NAME ## _pair_t search = { .key = key }; \ int rc = NAME ## _pair_set_remove(&map->pair_set, &search, &found); \ - if (FACEMGR_IS_ERROR(rc)) \ + if (rc < 0) \ return ERR_MAP_NOT_FOUND; \ - *value = found->value; \ - return FACEMGR_SUCCESS; \ + if (value) \ + *value = found->value; \ + NAME ## _pair_free(found); \ + return 0; \ } \ \ int \ NAME ## _get(NAME ## _t * map, KEY_T key, VAL_T * value) \ { \ - NAME ## _pair_t * found, search = { .key = key }; \ + NAME ## _pair_t * found = NULL, search = { .key = key }; \ int rc = NAME ## _pair_set_get(&map->pair_set, &search, &found); \ - if (FACEMGR_IS_ERROR(rc)) \ - return ERR_MAP_NOT_FOUND; \ - *value = found->value; \ - return FACEMGR_SUCCESS; \ + if (rc < 0) \ + return -1; \ + if (found) \ + *value = found->value; \ + return 0; \ } \ \ void \ NAME ## _dump(NAME ## _t * map) { \ NAME ## _pair_set_dump(&map->pair_set); \ +} \ + \ +int \ +NAME ## _get_key_array(NAME ## _t * map, KEY_T **array) { \ + NAME ## _pair_t ** pair_array; \ + int n = NAME ## _pair_set_get_array(&map->pair_set, &pair_array); \ + if (n < 0) \ + return -1; \ + /* Allocate result array */ \ + array = malloc(n * sizeof(KEY_T)); \ + if (!array) { \ + free(pair_array); \ + return -1; \ + } \ + /* Copy keys */ \ + for (int i = 0; i < n; i++) \ + array[i] = &pair_array[i]->key; \ + free(pair_array); \ + return 0; \ +} \ + \ +int \ +NAME ## _get_value_array(NAME ## _t * map, VAL_T **array) { \ + NAME ## _pair_t ** pair_array; \ + int n = NAME ## _pair_set_get_array(&map->pair_set, &pair_array); \ + if (n < 0) \ + return -1; \ + /* Allocate result array */ \ + array = malloc(n * sizeof(VAL_T)); \ + if (!array) { \ + free(pair_array); \ + return -1; \ + } \ + /* Copy values */ \ + for (int i = 0; i < n; i++) \ + array[i] = &pair_array[i]->value; \ + free(pair_array); \ + return 0; \ } #endif /* UTIL_MAP_H */ diff --git a/ctrl/facemgr/src/util/policy.c b/ctrl/facemgr/src/util/policy.c deleted file mode 100644 index 6c8651ee3..000000000 --- a/ctrl/facemgr/src/util/policy.c +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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 policy.h - * \brief Implementation of policy description - */ - -#include <stdio.h> -#include "policy.h" - -const char * policy_tag_str[] = { - #define _(x, y) [POLICY_TAG_ ## x] = STRINGIZE(x), - foreach_policy_tag - #undef _ -}; - -const char policy_tag_short_str[] = { - #define _(x, y) [POLICY_TAG_ ## x] = y, - foreach_policy_tag - #undef _ -}; - -const char * policy_state_str[] = { - #define _(x) [POLICY_STATE_ ## x] = STRINGIZE(x), - foreach_policy_state - #undef _ -}; - -int -policy_tag_state_snprintf(char * s, size_t size, const policy_tag_state_t * tag_state) -{ - char *cur = s; - int rc; - - if (tag_state->disabled > 1) - return -1; - - rc = snprintf(cur, s + size - cur, "%s%s", (tag_state->disabled == 1) ? "!" : "", policy_state_str[tag_state->state]); - if (rc < 0) - return rc; - cur += rc; - if (size != 0 && cur >= s + size) - return cur - s; - - return cur - s; -} diff --git a/ctrl/facemgr/src/util/policy.h b/ctrl/facemgr/src/util/policy.h deleted file mode 100644 index e20af6560..000000000 --- a/ctrl/facemgr/src/util/policy.h +++ /dev/null @@ -1,234 +0,0 @@ -/* - * 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 policy.h - * \brief Policy description - */ -#ifndef HICN_POLICY_H -#define HICN_POLICY_H - -#include <netinet/in.h> // INET*_ADDRSTRLEN -#include <string.h> // strcasecmp -#include "token.h" - -/* POLICY TAG */ - -#define foreach_policy_tag \ - /* Interface type */ \ - _(WIRED, 'E') \ - _(WIFI, 'W') \ - _(CELLULAR, 'C') \ - /* QoS */ \ - _(BEST_EFFORT, 'b') \ - _(REALTIME, 'r') \ - _(MULTIPATH, 'M') \ - /* Security */ \ - _(TRUSTED, 'T') - -typedef enum { -#define _(x, y) POLICY_TAG_ ## x, -foreach_policy_tag -#undef _ - POLICY_TAG_N -} policy_tag_t; - -#define MAXSZ_POLICY_TAG_ 11 -#define MAXSZ_POLICY_TAG MAXSZ_POLICY_TAG_ + 1 - -extern const char * policy_tag_str[]; -extern const char policy_tag_short_str[]; - -static inline -policy_tag_t -policy_tag_from_str(const char * str) -{ -#define _(x, y) if (strcasecmp(str, policy_tag_str[POLICY_TAG_ ## x] ) == 0) { return POLICY_TAG_ ## x; } else -foreach_policy_tag -#undef _ - return POLICY_TAG_N; -} - -/* POLICY_TAGS */ - -typedef int policy_tags_t; - -static inline -void policy_tags_add(policy_tags_t * tags, policy_tag_t tag) -{ - *tags |= (1 << tag); -} - -static inline -void policy_tags_remove(policy_tags_t * tags, policy_tag_t tag) -{ - *tags &= ~(1 << tag); -} - -static inline -int policy_tags_has(policy_tags_t tags, policy_tag_t tag) -{ - return tags & (1 << tag); -} - -#define POLICY_TAGS_EMPTY 0 - -static inline -int -policy_tags_snprintf(char * s, size_t size, policy_tags_t tags) -{ -#define _(x, y) s[POLICY_TAG_ ## x] = policy_tags_has(tags, POLICY_TAG_ ## x) ? y : '.'; -foreach_policy_tag -#undef _ - s[POLICY_TAG_N] = '\0'; - return POLICY_TAG_N + 1; -} - -#define MAXSZ_POLICY_TAGS_ POLICY_TAG_N -#define MAXSZ_POLICY_TAGS MAXSZ_POLICY_TAGS_ + 1 - -/* POLICY STATE */ - -/* TODO vs. weight */ - -#define foreach_policy_state \ - _(NEUTRAL) \ - _(REQUIRE) \ - _(PREFER) \ - _(AVOID) \ - _(PROHIBIT) \ - _(N) - -typedef enum { -#define _(x) POLICY_STATE_ ## x, -foreach_policy_state -#undef _ -} policy_state_t; - -#define MAXSZ_POLICY_STATE_ 8 -#define MAXSZ_POLICY_STATE MAXSZ_POLICY_STATE_ + 1 - -extern const char * policy_state_str[]; - - -/* POLICY TAG STATE */ - -typedef struct { - policy_state_t state; - uint8_t disabled; -} policy_tag_state_t; - -#define MAXSZ_POLICY_TAG_STATE_ 8 -#define MAXSZ_POLICY_TAG_STATE MAXSZ_POLICY_TAG_STATE_ + 1 - -int policy_tag_state_snprintf(char * s, size_t size, const policy_tag_state_t * tag_state); - - -/* INTERFACE STATS */ - -typedef struct { - float throughput; - float latency; - float loss_rate; -} interface_stats_t; - -#define INTERFACE_STATS_NONE { \ - .throughput = 0, \ - .latency = 0, \ - .loss_rate = 0, \ -} - - -/* POLICY STATS */ - -typedef struct { - interface_stats_t wired; - interface_stats_t wifi; - interface_stats_t cellular; - interface_stats_t all; -} policy_stats_t; - -#define POLICY_STATS_NONE { \ - .wired = INTERFACE_STATS_NONE, \ - .wifi = INTERFACE_STATS_NONE, \ - .cellular = INTERFACE_STATS_NONE, \ - .all = INTERFACE_STATS_NONE, \ -} - -typedef struct { - uint32_t num_packets; - uint32_t num_bytes; - uint32_t num_losses; - uint32_t latency_idle; -} interface_counters_t; - -#define INTERFACE_COUNTERS_NONE { \ - .num_packets = 0, \ - .num_bytes = 0, \ - .num_losses = 0, \ - .latency_idle = 0, \ -} - -typedef struct { - interface_counters_t wired; - interface_counters_t wifi; - interface_counters_t cellular; - interface_counters_t all; - uint64_t last_update; -} policy_counters_t; - -#define POLICY_COUNTERS_NONE (policy_counters_t) { \ - .wired = INTERFACE_COUNTERS_NONE, \ - .wifi = INTERFACE_COUNTERS_NONE, \ - .cellular = INTERFACE_COUNTERS_NONE, \ - .all = INTERFACE_COUNTERS_NONE, \ - .last_update = 0, \ -} - -/* POLICY */ - -#define APP_NAME_LEN 128 - -typedef struct { - char app_name[APP_NAME_LEN]; - policy_tag_state_t tags[POLICY_TAG_N]; - policy_stats_t stats; -} policy_t; - -static const policy_t POLICY_NONE = { - .app_name = { 0 }, - .tags = { -#define _(x, y) [POLICY_TAG_ ## x] = { POLICY_STATE_NEUTRAL, 0 }, -foreach_policy_tag -#undef _ - }, - .stats = POLICY_STATS_NONE, -}; - - -/* POLICY DESCRIPTION */ - -#define PFX_STRLEN 4 /* eg. /128 */ - -typedef struct { - int family; - union { - char ipv4_prefix[INET_ADDRSTRLEN + PFX_STRLEN]; - char ipv6_prefix[INET6_ADDRSTRLEN + PFX_STRLEN]; - }; - policy_t policy; -} policy_description_t; - -#endif /* HICN_POLICY_H */ diff --git a/ctrl/facemgr/src/util/set.h b/ctrl/facemgr/src/util/set.h index 47a6eeaff..61df209ab 100644 --- a/ctrl/facemgr/src/util/set.h +++ b/ctrl/facemgr/src/util/set.h @@ -16,15 +16,27 @@ #ifndef UTIL_SET_H #define UTIL_SET_H +#include <hicn/util/log.h> #include <search.h> #include <string.h> -#include "token.h" +//#if !defined(__ANDROID__) && !defined(__APPLE__) +//#include <threads.h> +//#else +#define thread_local _Thread_local +//#endif /* ! __ANDROID__ */ #include "../common.h" #define ERR_SET_EXISTS -2 #define ERR_SET_NOT_FOUND -3 -#define BUFSIZE 80 +/* FIXME: buffer overflow when this is too small... investigate */ +#define BUFSIZE 1024 + +static inline +int +int_snprintf(char * buf, size_t size, int value) { + return snprintf(buf, size, "%d", value); +} static inline int @@ -34,7 +46,7 @@ string_snprintf(char * buf, size_t size, const char * s) { static inline int -generic_snprintf(char * buf, size_t size, void * value) { +generic_snprintf(char * buf, size_t size, const void * value) { return snprintf(buf, BUFSIZE, "%p", value); } @@ -57,7 +69,9 @@ int NAME ## _add(NAME ## _t * set, const T element); \ \ int NAME ## _remove(NAME ## _t * set, const T search, T * element); \ \ -int NAME ## _get(NAME ## _t * set, const T search, T * element); \ +int NAME ## _get(const NAME ## _t * set, const T search, T * element); \ + \ +int NAME ## _get_array(const NAME ## _t * set, T ** element); \ \ void NAME ## _dump(NAME ## _t * set); @@ -70,7 +84,7 @@ NAME ## _initialize(NAME ## _t * set) \ { \ set->root = NULL; \ set->size = 0; \ - return FACEMGR_SUCCESS; \ + return 0; \ } \ \ NO_FINALIZE(NAME); \ @@ -79,49 +93,88 @@ AUTOGENERATE_CREATE_FREE(NAME); \ int \ NAME ## _add(NAME ## _t * set, const T element) \ { \ - return tsearch(element, &set->root, (cmp_t)CMP) \ - ? FACEMGR_SUCCESS : FACEMGR_FAILURE; \ + void * ptr = tsearch(element, &set->root, (cmp_t)CMP); \ + if (!ptr) \ + return -1; \ + set->size++; \ + return 0; \ } \ \ int \ NAME ## _remove(NAME ## _t * set, const T search, T * element) \ { \ - T * found = tdelete(search, &set->root, (cmp_t)CMP); \ - if (found && element) \ + T * found = tfind(search, &set->root, (cmp_t)CMP); \ + if (!found) \ + return ERR_SET_NOT_FOUND; \ + if (element) \ *element = *found; \ - return found ? FACEMGR_SUCCESS : ERR_SET_NOT_FOUND; \ + tdelete(search, &set->root, (cmp_t)CMP); \ + set->size--; \ + return 0; \ } \ \ int \ -NAME ## _get(NAME ## _t * set, const T search, T * element) \ +NAME ## _get(const NAME ## _t * set, const T search, T * element) \ { \ T * found = tfind(search, &set->root, (cmp_t)CMP); \ - if (found && element) \ - *element = *found; \ - return found ? FACEMGR_SUCCESS : ERR_SET_NOT_FOUND; \ + if (element) \ + *element = found ? *found : NULL; \ + return 0; \ } \ \ -void \ -__ ## NAME ## _dump_node(const void *nodep, const VISIT which, const int depth) \ +static void \ +NAME ## _dump_node(const void *nodep, const VISIT which, \ + const int depth) \ { \ char buf[BUFSIZE]; \ switch (which) { \ case preorder: \ - break; \ - case postorder: \ - break; \ case endorder: \ break; \ + case postorder: \ case leaf: \ SNPRINTF(buf, BUFSIZE, *(T*)nodep); \ - printf("%s\n", buf); \ + INFO("%s", buf); \ break; \ } \ } \ \ void \ NAME ## _dump(NAME ## _t * set) { \ - twalk(set->root, __ ## NAME ## _dump_node); \ + twalk(set->root, NAME ## _dump_node); \ } \ + \ +thread_local \ +T * NAME ## _array_pos = NULL; \ + \ +static void \ +NAME ## _add_node_to_array(const void *nodep, const VISIT which, \ + const int depth) \ +{ \ + if (!NAME ## _array_pos) \ + return; \ + switch (which) { \ + case preorder: \ + case endorder: \ + break; \ + case postorder: \ + case leaf: \ + *NAME ## _array_pos = *(T*)nodep; \ + NAME ## _array_pos++; \ + break; \ + } \ +} \ + \ +int \ +NAME ## _get_array(const NAME ## _t * set, T ** element) \ +{ \ + *element = malloc(set->size * sizeof(T)); \ + if (!*element) \ + return -1; \ + NAME ## _array_pos = *element; \ + twalk(set->root, NAME ## _add_node_to_array); \ + NAME ## _array_pos = NULL; \ + return set->size; \ +} #endif /* UTIL_SET_H */ diff --git a/ctrl/facemgr/src/util/token.h b/ctrl/facemgr/src/util/token.h deleted file mode 100644 index 43e0a77b2..000000000 --- a/ctrl/facemgr/src/util/token.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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. - */ - -/* Token concatenation */ - -/* - * Concatenate preprocessor tokens A and B without expanding macro definitions - * (however, if invoked from a macro, macro arguments are expanded). - */ -#define PPCAT_NX(A, B) A ## B - -/* - * Concatenate preprocessor tokens A and B after macro-expanding them. - */ -#define PPCAT(A, B) PPCAT_NX(A, B) - -/* Token stringification */ - -/* - * Turn A into a string literal without expanding macro definitions - * (however, if invoked from a macro, macro arguments are expanded). - */ -#define STRINGIZE_NX(A) #A - -/* - * Turn A into a string literal after macro-expanding it. - */ -#define STRINGIZE(A) STRINGIZE_NX(A) diff --git a/ctrl/facemgr/src/util/types.h b/ctrl/facemgr/src/util/types.h deleted file mode 100644 index 10a0bdca0..000000000 --- a/ctrl/facemgr/src/util/types.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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. - */ - -#ifndef UTIL_TYPES -#define UTIL_TYPES - -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; - -/* Helper for avoiding warnings about type-punning */ -#define UNION_CAST(x, destType) \ - (((union {__typeof__(x) a; destType b;})x).b) - -typedef unsigned int hash_t; - -typedef int (*cmp_t)(const void *, const void *); - -/* Enums */ - -#define IS_VALID_ENUM_TYPE(NAME, x) ((x > NAME ## _UNDEFINED) && (x < NAME ## _N)) - -#endif /* UTIL_TYPES */ |