From 6b84ec54083da9911f5ad4816d0eb4f4745afad4 Mon Sep 17 00:00:00 2001 From: Jordan Augé Date: Mon, 7 Oct 2019 09:52:33 +0200 Subject: [HICN-298] Release new hICN app for Android MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I43adc62fadf00690b687078d739788dffdc5e566 Signed-off-by: Jordan Augé --- ctrl/facemgr/src/interfaces/CMakeLists.txt | 11 +- .../src/interfaces/android_utility/CMakeLists.txt | 27 + .../interfaces/android_utility/android_utility.c | 138 ++++ ctrl/facemgr/src/interfaces/bonjour/CMakeLists.txt | 32 + ctrl/facemgr/src/interfaces/bonjour/bonjour.c | 408 ++++++++++ ctrl/facemgr/src/interfaces/bonjour/bonjour.h | 35 + ctrl/facemgr/src/interfaces/bonjour/mdns/LICENSE | 24 + ctrl/facemgr/src/interfaces/bonjour/mdns/README.md | 9 + ctrl/facemgr/src/interfaces/bonjour/mdns/mdns.c | 192 +++++ ctrl/facemgr/src/interfaces/bonjour/mdns/mdns.h | 879 +++++++++++++++++++++ ctrl/facemgr/src/interfaces/dummy/CMakeLists.txt | 1 + ctrl/facemgr/src/interfaces/dummy/dummy.c | 98 ++- ctrl/facemgr/src/interfaces/dummy/dummy.h | 34 + .../facemgr/src/interfaces/hicn_light/hicn_light.c | 204 +++-- ctrl/facemgr/src/interfaces/netlink/netlink.c | 472 ++++++++--- .../interfaces/network_framework/CMakeLists.txt | 1 + .../network_framework/network_framework.c | 138 ++-- .../network_framework/network_framework.h | 22 + ctrl/facemgr/src/interfaces/updown/CMakeLists.txt | 31 + ctrl/facemgr/src/interfaces/updown/updown.c | 138 ++++ 20 files changed, 2623 insertions(+), 271 deletions(-) create mode 100644 ctrl/facemgr/src/interfaces/android_utility/CMakeLists.txt create mode 100644 ctrl/facemgr/src/interfaces/android_utility/android_utility.c create mode 100644 ctrl/facemgr/src/interfaces/bonjour/CMakeLists.txt create mode 100644 ctrl/facemgr/src/interfaces/bonjour/bonjour.c create mode 100644 ctrl/facemgr/src/interfaces/bonjour/bonjour.h create mode 100644 ctrl/facemgr/src/interfaces/bonjour/mdns/LICENSE create mode 100644 ctrl/facemgr/src/interfaces/bonjour/mdns/README.md create mode 100644 ctrl/facemgr/src/interfaces/bonjour/mdns/mdns.c create mode 100644 ctrl/facemgr/src/interfaces/bonjour/mdns/mdns.h create mode 100644 ctrl/facemgr/src/interfaces/dummy/dummy.h create mode 100644 ctrl/facemgr/src/interfaces/network_framework/network_framework.h create mode 100644 ctrl/facemgr/src/interfaces/updown/CMakeLists.txt create mode 100644 ctrl/facemgr/src/interfaces/updown/updown.c (limited to 'ctrl/facemgr/src/interfaces') 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 + +#include +#include +#include +#include "../../common.h" +#include "../../facelet.h" +#include "../../interface.h" +#include + +#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 +#include + +#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/interfaces/bonjour/bonjour.h b/ctrl/facemgr/src/interfaces/bonjour/bonjour.h new file mode 100644 index 000000000..fe053079d --- /dev/null +++ b/ctrl/facemgr/src/interfaces/bonjour/bonjour.h @@ -0,0 +1,35 @@ +/* + * 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.h + * \brief Bonjour interface + * + * 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... + */ + +#include /* 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 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 +#include + +#ifdef _WIN32 +# define sleep(x) Sleep(x * 1000) +#else +# include +#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 +#include +#include +#include + +#include +#ifdef _WIN32 +#include +#include +#define strncasecmp _strnicmp +#else +#include +#include +#include +#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 +#include // close + +#include -#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/interfaces/dummy/dummy.h b/ctrl/facemgr/src/interfaces/dummy/dummy.h new file mode 100644 index 000000000..22fe5d1a6 --- /dev/null +++ b/ctrl/facemgr/src/interfaces/dummy/dummy.h @@ -0,0 +1,34 @@ +/* + * 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 dummy.h + * \brief Dummy interface + * + * This class serves as a template to implement new face maanger interfaces, and + * can be used for test purposes. + */ + +#ifndef FACEMGR_INTERFACE_DUMMY_H +#define FACEMGR_INTERFACE_DUMMY_H + +/* + * Configuration data + */ +typedef struct { + /* ... */ +} dummy_cfg_t; + +#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 -#include // arc4random [random, rand] #include // snprintf #include // time #include +#include +#include +#include -#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 #include +#include // ARPHRD_LOOPBACK #include // getpid #include // getpid -#include "../../event.h" -#include "../../facemgr.h" +#include +#include +#include + +#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 #include +#include +#include +#include + #include "../../common.h" -#include "../../event.h" -#include "../../face.h" -#include "../../facemgr.h" +#include +#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/interfaces/network_framework/network_framework.h b/ctrl/facemgr/src/interfaces/network_framework/network_framework.h new file mode 100644 index 000000000..edb35e904 --- /dev/null +++ b/ctrl/facemgr/src/interfaces/network_framework/network_framework.h @@ -0,0 +1,22 @@ +/* + * 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 network_framework.h + * \brief Network framework interface + */ + +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 +#include +#include +#include +#include +#include + +#include + +#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, +}; -- cgit 1.2.3-korg