aboutsummaryrefslogtreecommitdiffstats
path: root/ctrl/facemgr/src/interfaces
diff options
context:
space:
mode:
authorJordan Augé <jordan.auge+fdio@cisco.com>2019-10-07 09:52:33 +0200
committerJordan Augé <jordan.auge+fdio@cisco.com>2019-10-07 15:55:42 +0200
commit6b84ec54083da9911f5ad4816d0eb4f4745afad4 (patch)
treee4296ebb218fff02dc0bbea73ce1c8d12aba7bcc /ctrl/facemgr/src/interfaces
parent85a791ac2cdd35d79c00141e748b4c68fbdafb0d (diff)
[HICN-298] Release new hICN app for Android
Change-Id: I43adc62fadf00690b687078d739788dffdc5e566 Signed-off-by: Jordan Augé <jordan.auge+fdio@cisco.com>
Diffstat (limited to 'ctrl/facemgr/src/interfaces')
-rw-r--r--ctrl/facemgr/src/interfaces/CMakeLists.txt11
-rw-r--r--ctrl/facemgr/src/interfaces/android_utility/CMakeLists.txt27
-rw-r--r--ctrl/facemgr/src/interfaces/android_utility/android_utility.c138
-rw-r--r--ctrl/facemgr/src/interfaces/bonjour/CMakeLists.txt32
-rw-r--r--ctrl/facemgr/src/interfaces/bonjour/bonjour.c408
-rw-r--r--ctrl/facemgr/src/interfaces/bonjour/bonjour.h35
-rw-r--r--ctrl/facemgr/src/interfaces/bonjour/mdns/LICENSE24
-rw-r--r--ctrl/facemgr/src/interfaces/bonjour/mdns/README.md9
-rw-r--r--ctrl/facemgr/src/interfaces/bonjour/mdns/mdns.c192
-rw-r--r--ctrl/facemgr/src/interfaces/bonjour/mdns/mdns.h879
-rw-r--r--ctrl/facemgr/src/interfaces/dummy/CMakeLists.txt1
-rw-r--r--ctrl/facemgr/src/interfaces/dummy/dummy.c98
-rw-r--r--ctrl/facemgr/src/interfaces/dummy/dummy.h34
-rw-r--r--ctrl/facemgr/src/interfaces/hicn_light/hicn_light.c204
-rw-r--r--ctrl/facemgr/src/interfaces/netlink/netlink.c472
-rw-r--r--ctrl/facemgr/src/interfaces/network_framework/CMakeLists.txt1
-rw-r--r--ctrl/facemgr/src/interfaces/network_framework/network_framework.c138
-rw-r--r--ctrl/facemgr/src/interfaces/network_framework/network_framework.h22
-rw-r--r--ctrl/facemgr/src/interfaces/updown/CMakeLists.txt31
-rw-r--r--ctrl/facemgr/src/interfaces/updown/updown.c138
20 files changed, 2623 insertions, 271 deletions
diff --git a/ctrl/facemgr/src/interfaces/CMakeLists.txt b/ctrl/facemgr/src/interfaces/CMakeLists.txt
index e5a26177a..8d079612a 100644
--- a/ctrl/facemgr/src/interfaces/CMakeLists.txt
+++ b/ctrl/facemgr/src/interfaces/CMakeLists.txt
@@ -24,12 +24,21 @@ endif()
if(LINUX)
add_subdirectory(netlink)
+add_subdirectory(bonjour)
endif()
-if(false)
+if(ANDROID)
+add_subdirectory(android_utility)
+endif()
+
+if(WITH_EXAMPLE_DUMMY)
add_subdirectory(dummy)
endif()
+if(WITH_EXAMPLE_UPDOWN)
+add_subdirectory(updown)
+endif()
+
set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE)
set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
set(INCLUDE_DIRS ${INCLUDE_DIRS} PARENT_SCOPE)
diff --git a/ctrl/facemgr/src/interfaces/android_utility/CMakeLists.txt b/ctrl/facemgr/src/interfaces/android_utility/CMakeLists.txt
new file mode 100644
index 000000000..0ebe87745
--- /dev/null
+++ b/ctrl/facemgr/src/interfaces/android_utility/CMakeLists.txt
@@ -0,0 +1,27 @@
+# Copyright (c) 2017-2019 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+list(APPEND SOURCE_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/android_utility.c
+)
+
+list(APPEND INCLUDE_DIRS
+)
+
+list(APPEND LIBRARIES
+)
+
+set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE)
+set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
+set(INCLUDE_DIRS ${INCLUDE_DIRS} PARENT_SCOPE)
+set(LIBRARIES ${LIBRARIES} PARENT_SCOPE)
diff --git a/ctrl/facemgr/src/interfaces/android_utility/android_utility.c b/ctrl/facemgr/src/interfaces/android_utility/android_utility.c
new file mode 100644
index 000000000..bb612507f
--- /dev/null
+++ b/ctrl/facemgr/src/interfaces/android_utility/android_utility.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file interfaces/android_utility/android_utility.c
+ * \brief Implementation of Android utility.
+ */
+
+#include <assert.h>
+
+#include <hicn/facemgr.h>
+#include <hicn/ctrl/face.h>
+#include <hicn/util/log.h>
+#include "../../common.h"
+#include "../../facelet.h"
+#include "../../interface.h"
+#include <hicn/android_utility/android_utility.h>
+
+#define FACEMGR_ANDROID_UTILITY_CLASS "com/cisco/hicn/forwarder/supportlibrary/AndroidUtility"
+
+#define AU_INTERFACE_TYPE_UNDEFINED 0
+#define AU_INTERFACE_TYPE_WIRED 1
+#define AU_INTERFACE_TYPE_WIFI 2
+#define AU_INTERFACE_TYPE_CELLULAR 3
+#define AU_INTERFACE_TYPE_LOOPBACK 4 /* not supported yet */
+
+#define ERR_STR_JAVA "Java VM parameters are required in the interface configuration."
+
+typedef struct {
+ android_utility_cfg_t cfg;
+} au_data_t;
+
+int au_initialize(interface_t * interface, void * cfg)
+{
+ au_data_t * data = malloc(sizeof(au_data_t));
+ if (!data)
+ return -1;
+ interface->data = data;
+
+ if (!cfg)
+ goto ERR_CFG;
+
+ data->cfg = * (android_utility_cfg_t *) cfg;
+
+ if (!data->cfg.jvm)
+ goto ERR_CFG;
+
+ return 0;
+
+ERR_CFG:
+ fprintf(stderr, ERR_STR_JAVA);
+ return -1;
+}
+
+int au_finalize(interface_t * interface)
+{
+ /* Nothing to do */
+ return 0;
+}
+
+int au_on_event(interface_t * interface, const facelet_t * facelet)
+{
+ /*
+ * This function is responsible to annotate every face we receive with the
+ * correct interface type, based on the value returned by the Android
+ * utility shipped with the Android forwarder.
+ */
+ DEBUG("Android utility received request");
+ au_data_t * data = (au_data_t*)interface->data;
+
+ netdevice_t netdevice = NETDEVICE_EMPTY;
+ int rc = facelet_get_netdevice(facelet, &netdevice);
+ if (rc < 0)
+ return -1;
+ DEBUG("[au_on_event] netdevice=%s", netdevice.name);
+
+ JNIEnv *env;
+ JavaVM *jvm = data->cfg.jvm;
+ (*jvm)->AttachCurrentThread(jvm, &env, NULL);
+ jclass cls = (*env)->FindClass(env, FACEMGR_ANDROID_UTILITY_CLASS);
+ jmethodID getNetworkType = (*env)->GetStaticMethodID(env, cls,
+ "getNetworkType", "(Ljava/lang/String;)I");
+ jint interface_type = (*env)->CallStaticIntMethod(env, cls, getNetworkType,
+ (*env)->NewStringUTF(env, netdevice.name));
+
+ DEBUG("Processing results for interface %s", netdevice.name);
+
+ netdevice_type_t netdevice_type = AU_INTERFACE_TYPE_UNDEFINED;
+ switch(interface_type) {
+ case AU_INTERFACE_TYPE_UNDEFINED:
+ break;
+ case AU_INTERFACE_TYPE_WIRED:
+ netdevice_type = NETDEVICE_TYPE_WIRED;
+ break;
+ case AU_INTERFACE_TYPE_WIFI:
+ netdevice_type = NETDEVICE_TYPE_WIFI;
+ break;
+ case AU_INTERFACE_TYPE_CELLULAR:
+ netdevice_type = NETDEVICE_TYPE_CELLULAR;
+ break;
+ case AU_INTERFACE_TYPE_LOOPBACK:
+ netdevice_type = NETDEVICE_TYPE_LOOPBACK;
+ break;
+ default:
+ return -1;
+ }
+
+ facelet_t * facelet_new = facelet_create();
+ facelet_set_netdevice(facelet_new, netdevice);
+ facelet_set_status(facelet_new, FACELET_STATUS_CLEAN);
+ facelet_set_netdevice_type(facelet_new, netdevice_type);
+
+ DEBUG("sending AU udpate");
+ facelet_set_event(facelet_new, FACELET_EVENT_UPDATE);
+ facelet_raise_event(facelet_new, interface);
+
+ return 0;
+}
+
+const interface_ops_t android_utility_ops = {
+ .type = "android_utility",
+ .initialize = au_initialize,
+ .finalize = au_finalize,
+ .callback = NULL,
+ .on_event = au_on_event,
+};
diff --git a/ctrl/facemgr/src/interfaces/bonjour/CMakeLists.txt b/ctrl/facemgr/src/interfaces/bonjour/CMakeLists.txt
new file mode 100644
index 000000000..8a0ddc888
--- /dev/null
+++ b/ctrl/facemgr/src/interfaces/bonjour/CMakeLists.txt
@@ -0,0 +1,32 @@
+# Copyright (c) 2017-2019 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+list(APPEND HEADER_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/bonjour.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/mdns/mdns.h
+)
+
+list(APPEND SOURCE_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/bonjour.c
+)
+
+list(APPEND LIBRARIES
+)
+
+list(APPEND INCLUDE_DIRS
+)
+
+set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE)
+set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE)
+set(INCLUDE_DIRS ${INCLUDE_DIRS} PARENT_SCOPE)
+set(LIBRARIES ${LIBRARIES} PARENT_SCOPE)
diff --git a/ctrl/facemgr/src/interfaces/bonjour/bonjour.c b/ctrl/facemgr/src/interfaces/bonjour/bonjour.c
new file mode 100644
index 000000000..d7b27b995
--- /dev/null
+++ b/ctrl/facemgr/src/interfaces/bonjour/bonjour.c
@@ -0,0 +1,408 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file interfaces/bonjour/bonjour.c
+ * \brief Implementation of Bonjour interface
+ *
+ * TODO:
+ * - concurrent queries
+ * - interface binding
+ */
+
+#include <hicn/facemgr.h>
+#include <hicn/util/log.h>
+
+#include "../../common.h"
+#include "../../facelet.h"
+#include "../../interface.h"
+#include "../../util/map.h"
+#include "mdns/mdns.h"
+
+#include "bonjour.h"
+
+#define DEFAULT_BUFFER_SIZE 2048
+#define SERVICE_STRING_SIZE 256
+
+#define DEFAULT_SERVICE_NAME "hicn"
+#define DEFAULT_SERVICE_PROTOCOL "udp"
+#define DEFAULT_SERVICE_DOMAIN "local"
+
+typedef struct {
+ bonjour_cfg_t cfg;
+ int sock;
+ size_t buffer_size;
+ void* buffer;
+
+ /* The face being resolved, non-NULL values indicate interface is busy... */
+ face_t * face;
+} bj_data_t;
+
+int bj_initialize(interface_t * interface, void * cfg)
+{
+ bj_data_t * data = malloc(sizeof(bj_data_t));
+ if (!data)
+ goto ERR_MALLOC;
+ interface->data = data;
+
+ if (cfg) {
+#ifndef __linux__
+ if (cfg->netdevice)
+ WARN("Binding to interface is (currently) only supported on Linux");
+#endif /* ! __linux__ */
+ data->cfg = * (bonjour_cfg_t *) cfg;
+ } else {
+ memset(&data->cfg, 0, sizeof(bonjour_cfg_t));
+ }
+
+ if (!data->cfg.service_name)
+ data->cfg.service_name = DEFAULT_SERVICE_NAME;
+
+ if (!data->cfg.service_protocol)
+ data->cfg.service_protocol = DEFAULT_SERVICE_PROTOCOL;
+
+ if (!data->cfg.service_domain)
+ data->cfg.service_domain = DEFAULT_SERVICE_DOMAIN;
+
+ data->sock = mdns_socket_open_ipv4();
+ if (data->sock < 0) {
+ printf("Failed to open socket: %s\n", strerror(errno));
+ goto ERR_SOCK;
+ }
+
+ /* Netdevice configuration */
+#ifdef __linux__
+#ifndef __ANDROID__
+ if (IS_VALID_NETDEVICE(data->cfg.netdevice)) {
+ int rc = setsockopt(data->sock, SOL_SOCKET, SO_BINDTODEVICE,
+ &data->cfg.netdevice.name, strlen(data->cfg.netdevice.name) + 1);
+ if (rc == -1) {
+ ERROR("setsockopt");
+ goto ERR_SOCK_OPT;
+ }
+ }
+#endif
+#endif /* __linux__ */
+
+ data->buffer_size = DEFAULT_BUFFER_SIZE;
+ data->buffer = malloc(data->buffer_size);
+ if (!data->buffer)
+ goto ERR_BUFFER;
+
+#ifdef _WIN32
+ WORD versionWanted = MAKEWORD(1, 1);
+ WSADATA wsaData;
+ WSAStartup(versionWanted, &wsaData);
+#endif
+
+ return data->sock;
+
+ERR_BUFFER:
+#ifndef __ANDROID__
+ERR_SOCK_OPT:
+#endif
+ mdns_socket_close(data->sock);
+#ifdef _WIN32
+ WSACleanup();
+#endif
+ERR_SOCK:
+ free(data);
+ERR_MALLOC:
+ return -1;
+}
+
+/*
+ * We reuse the callback to be triggered upon external events
+ * TODO: move to a cleaner interface architecture later...
+ */
+int bj_on_event(interface_t * interface, const facelet_t * facelet)
+{
+ bj_data_t * data = (bj_data_t*)interface->data;
+
+ /*
+ printf("Sending DNS-SD discovery\n");
+ if (mdns_discovery_send(sock)) {
+ printf("Failed to send DNS-DS discovery: %s\n", strerror(errno));
+ goto quit;
+ }
+
+ printf("Reading DNS-SD replies\n");
+ for (int i = 0; i < 10; ++i) {
+ records = mdns_discovery_recv(sock, buffer, capacity, callback,
+ user_data);
+ sleep(1);
+ }
+ */
+
+ DEBUG("Sending mDNS query");
+ char service_string[SERVICE_STRING_SIZE];
+
+ int rc = snprintf(service_string, SERVICE_STRING_SIZE, "_%s._%s.%s.",
+ data->cfg.service_name, data->cfg.service_protocol,
+ data->cfg.service_domain);
+ if (rc < 0)
+ ; // error
+ else if (rc >= SERVICE_STRING_SIZE)
+ ; //truncated
+
+ if (mdns_query_send(data->sock, MDNS_RECORDTYPE_PTR,
+ service_string,
+ strlen(service_string),
+ data->buffer, data->buffer_size)) {
+ printf("Failed to send mDNS query: %s\n", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static char addrbuffer[64];
+static char namebuffer[256];
+static mdns_record_txt_t txtbuffer[128];
+
+static mdns_string_t
+ipv4_address_to_string(char* buffer, size_t capacity, const struct sockaddr_in* addr) {
+ char host[NI_MAXHOST] = {0};
+ char service[NI_MAXSERV] = {0};
+ int ret = getnameinfo((const struct sockaddr*)addr, sizeof(struct sockaddr_in),
+ host, NI_MAXHOST, service, NI_MAXSERV,
+ NI_NUMERICSERV | NI_NUMERICHOST);
+ int len = 0;
+ if (ret == 0) {
+ if (addr->sin_port != 0)
+ len = snprintf(buffer, capacity, "%s:%s", host, service);
+ else
+ len = snprintf(buffer, capacity, "%s", host);
+ }
+ if (len >= (int)capacity)
+ len = (int)capacity - 1;
+ mdns_string_t str = {buffer, len};
+ return str;
+}
+
+static mdns_string_t
+ipv6_address_to_string(char* buffer, size_t capacity, const struct sockaddr_in6* addr) {
+ char host[NI_MAXHOST] = {0};
+ char service[NI_MAXSERV] = {0};
+ int ret = getnameinfo((const struct sockaddr*)addr, sizeof(struct sockaddr_in6),
+ host, NI_MAXHOST, service, NI_MAXSERV,
+ NI_NUMERICSERV | NI_NUMERICHOST);
+ int len = 0;
+ if (ret == 0) {
+ if (addr->sin6_port != 0)
+ len = snprintf(buffer, capacity, "[%s]:%s", host, service);
+ else
+ len = snprintf(buffer, capacity, "%s", host);
+ }
+ if (len >= (int)capacity)
+ len = (int)capacity - 1;
+ mdns_string_t str = {buffer, len};
+ return str;
+}
+
+static mdns_string_t
+ip_address_to_string(char* buffer, size_t capacity, const struct sockaddr* addr) {
+ if (addr->sa_family == AF_INET6)
+ return ipv6_address_to_string(buffer, capacity, (const struct sockaddr_in6*)addr);
+ return ipv4_address_to_string(buffer, capacity, (const struct sockaddr_in*)addr);
+}
+
+int
+ip_address_set_sockaddr(ip_address_t * ip_address, struct sockaddr * sa)
+{
+ switch(sa->sa_family) {
+ case AF_INET:
+ ip_address->v4.as_inaddr = ((struct sockaddr_in *)sa)->sin_addr;
+ break;
+ case AF_INET6:
+ ip_address->v6.as_in6addr = ((struct sockaddr_in6 *)sa)->sin6_addr;
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+callback(const struct sockaddr* from, mdns_entry_type_t entry, uint16_t type,
+ uint16_t rclass, uint32_t ttl, const void* data, size_t size, size_t
+ offset, size_t length, void* user_data)
+{
+ interface_t * interface = (interface_t*)user_data;
+ bj_data_t * bj_data = (bj_data_t *)interface->data;
+
+ struct sockaddr_storage addr;
+
+ mdns_string_t fromaddrstr = ip_address_to_string(addrbuffer, sizeof(addrbuffer), from);
+ const char* entrytype = (entry == MDNS_ENTRYTYPE_ANSWER) ? "answer" :
+ ((entry == MDNS_ENTRYTYPE_AUTHORITY) ? "authority" : "additional");
+
+ switch(type) {
+ case MDNS_RECORDTYPE_A:
+ {
+ ip_address_t ip_address;
+ mdns_record_parse_a(data, size, offset, length, (struct sockaddr_in*)&addr);
+ ip_address_set_sockaddr(&ip_address, (struct sockaddr *)&addr);
+
+ mdns_string_t addrstr = ipv4_address_to_string(namebuffer, sizeof(namebuffer), (struct sockaddr_in *)&addr);
+ DEBUG("%.*s : %s A %.*s",
+ MDNS_STRING_FORMAT(fromaddrstr), entrytype,
+ MDNS_STRING_FORMAT(addrstr));
+
+ facelet_t * facelet = facelet_create();
+ facelet_set_netdevice(facelet, bj_data->cfg.netdevice);
+ facelet_set_family(facelet, AF_INET);
+ facelet_set_remote_addr(facelet, ip_address);
+ //facelet_set_remote_port(facelet, ((struct sockaddr_in*)&addr)->sin_port);
+
+ facelet_set_event(facelet, FACELET_EVENT_UPDATE);
+ facelet_raise_event(facelet, interface);
+ break;
+ }
+
+ case MDNS_RECORDTYPE_AAAA:
+ {
+ ip_address_t ip_address;
+ mdns_record_parse_aaaa(data, size, offset, length, (struct sockaddr_in6*)&addr);
+ ip_address_set_sockaddr(&ip_address, (struct sockaddr *)&addr);
+
+ mdns_string_t addrstr = ipv6_address_to_string(namebuffer,
+ sizeof(namebuffer), (struct sockaddr_in6*)&addr);
+ DEBUG("%.*s : %s AAAA %.*s",
+ MDNS_STRING_FORMAT(fromaddrstr), entrytype,
+ MDNS_STRING_FORMAT(addrstr));
+
+ facelet_t * facelet = facelet_create();
+ facelet_set_netdevice(facelet, bj_data->cfg.netdevice);
+ facelet_set_family(facelet, AF_INET6);
+ facelet_set_remote_addr(facelet, ip_address);
+ //facelet_set_remote_port(facelet, ((struct sockaddr_in6*)&addr)->sin6_port);
+
+ facelet_set_event(facelet, FACELET_EVENT_UPDATE);
+ facelet_raise_event(facelet, interface);
+ break;
+ }
+
+ case MDNS_RECORDTYPE_SRV: /* same port for both v4 and v6 */
+ {
+ mdns_record_srv_t srv = mdns_record_parse_srv(data, size, offset, length,
+ namebuffer, sizeof(namebuffer));
+
+ DEBUG("%.*s : %s SRV %.*s priority %d weight %d port %d",
+ MDNS_STRING_FORMAT(fromaddrstr), entrytype,
+ MDNS_STRING_FORMAT(srv.name), srv.priority, srv.weight, srv.port);
+
+ /* We raise both v4 and v6
+ *
+ * Unless we choose whether we query A and/or AAAA, this might leave
+ * us with an unused pending facelet, eg. we might not have an IPv6
+ * but we raise an IPv6 bonjour event...
+ */
+
+ facelet_t * facelet = facelet_create();
+ facelet_set_netdevice(facelet, bj_data->cfg.netdevice);
+ facelet_set_family(facelet, AF_INET);
+ facelet_set_remote_port(facelet, srv.port);
+
+ facelet_set_event(facelet, FACELET_EVENT_UPDATE);
+ facelet_raise_event(facelet, interface);
+
+ facelet = facelet_create();
+ facelet_set_netdevice(facelet, bj_data->cfg.netdevice);
+ facelet_set_family(facelet, AF_INET6);
+ facelet_set_remote_port(facelet, srv.port);
+
+ facelet_set_event(facelet, FACELET_EVENT_UPDATE);
+ facelet_raise_event(facelet, interface);
+ break;
+ }
+
+ case MDNS_RECORDTYPE_PTR:
+ {
+ mdns_string_t namestr = mdns_record_parse_ptr(data, size, offset, length,
+ namebuffer, sizeof(namebuffer));
+ DEBUG("%.*s : %s PTR %.*s type %u rclass 0x%x ttl %u length %d",
+ MDNS_STRING_FORMAT(fromaddrstr), entrytype,
+ MDNS_STRING_FORMAT(namestr), type, rclass, ttl, (int)length);
+ break;
+ }
+
+ case MDNS_RECORDTYPE_TXT:
+ {
+ size_t parsed = mdns_record_parse_txt(data, size, offset, length,
+ txtbuffer, sizeof(txtbuffer) / sizeof(mdns_record_txt_t));
+ for (size_t itxt = 0; itxt < parsed; ++itxt) {
+ if (txtbuffer[itxt].value.length) {
+ DEBUG("%.*s : %s TXT %.*s = %.*s",
+ MDNS_STRING_FORMAT(fromaddrstr), entrytype,
+ MDNS_STRING_FORMAT(txtbuffer[itxt].key),
+ MDNS_STRING_FORMAT(txtbuffer[itxt].value));
+ }
+ else {
+ DEBUG("%.*s : %s TXT %.*s",
+ MDNS_STRING_FORMAT(fromaddrstr), entrytype,
+ MDNS_STRING_FORMAT(txtbuffer[itxt].key));
+ }
+ }
+ break;
+ }
+
+ default:
+ /* Silently ignore the received record */
+ DEBUG("%.*s : %s type %u rclass 0x%x ttl %u length %d",
+ MDNS_STRING_FORMAT(fromaddrstr), entrytype,
+ type, rclass, ttl, (int)length);
+ return 0;
+ }
+ return 0;
+
+}
+
+/*
+ * The fact we use a single fd does not allow us to get user_data associated to
+ * the query.
+ */
+int bj_callback(interface_t * interface)
+{
+ bj_data_t * data = (bj_data_t*)interface->data;
+ DEBUG("Got an mDNS reply");
+ /* size_t records = */ mdns_query_recv(data->sock, data->buffer, data->buffer_size, callback, interface, 1);
+
+ return 0;
+}
+
+int bj_finalize(interface_t * interface)
+{
+ bj_data_t * data = (bj_data_t*)interface->data;
+
+ free(data->buffer);
+ mdns_socket_close(data->sock);
+
+#ifdef _WIN32
+ WSACleanup();
+#endif
+
+ return 0;
+
+}
+
+const interface_ops_t bonjour_ops = {
+ .type = "bonjour",
+ .initialize = bj_initialize,
+ .on_event = bj_on_event,
+ .callback = bj_callback,
+ .finalize = bj_finalize,
+ // .on_event = NULL,
+};
diff --git a/ctrl/facemgr/src/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 <hicn/ctrl/face.h> /* netdevice_t */
+
+typedef struct {
+ netdevice_t netdevice;
+ char * service_name;
+ char * service_protocol;
+ char * service_domain;
+} bonjour_cfg_t;
diff --git a/ctrl/facemgr/src/interfaces/bonjour/mdns/LICENSE b/ctrl/facemgr/src/interfaces/bonjour/mdns/LICENSE
new file mode 100644
index 000000000..cf1ab25da
--- /dev/null
+++ b/ctrl/facemgr/src/interfaces/bonjour/mdns/LICENSE
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org>
diff --git a/ctrl/facemgr/src/interfaces/bonjour/mdns/README.md b/ctrl/facemgr/src/interfaces/bonjour/mdns/README.md
new file mode 100644
index 000000000..1bee49c08
--- /dev/null
+++ b/ctrl/facemgr/src/interfaces/bonjour/mdns/README.md
@@ -0,0 +1,9 @@
+# Public domain mDNS/DNS-SD library in C
+
+This library provides a cross-platform mDNS and DNS-DS library in C. The latest source code is always available at
+
+https://github.com/mjansson/mdns
+
+This library is put in the public domain; you can redistribute it and/or modify it without any restrictions.
+
+Created by Mattias Jansson ([@maniccoder](https://twitter.com/maniccoder))
diff --git a/ctrl/facemgr/src/interfaces/bonjour/mdns/mdns.c b/ctrl/facemgr/src/interfaces/bonjour/mdns/mdns.c
new file mode 100644
index 000000000..a8e97e8e0
--- /dev/null
+++ b/ctrl/facemgr/src/interfaces/bonjour/mdns/mdns.c
@@ -0,0 +1,192 @@
+
+#ifdef _WIN32
+# define _CRT_SECURE_NO_WARNINGS 1
+#endif
+
+#include "mdns.h"
+
+#include <stdio.h>
+#include <errno.h>
+
+#ifdef _WIN32
+# define sleep(x) Sleep(x * 1000)
+#else
+# include <netdb.h>
+#endif
+
+static char addrbuffer[64];
+static char namebuffer[256];
+static mdns_record_txt_t txtbuffer[128];
+
+static mdns_string_t
+ipv4_address_to_string(char* buffer, size_t capacity, const struct sockaddr_in* addr) {
+ char host[NI_MAXHOST] = {0};
+ char service[NI_MAXSERV] = {0};
+ int ret = getnameinfo((const struct sockaddr*)addr, sizeof(struct sockaddr_in),
+ host, NI_MAXHOST, service, NI_MAXSERV,
+ NI_NUMERICSERV | NI_NUMERICHOST);
+ int len = 0;
+ if (ret == 0) {
+ if (addr->sin_port != 0)
+ len = snprintf(buffer, capacity, "%s:%s", host, service);
+ else
+ len = snprintf(buffer, capacity, "%s", host);
+ }
+ if (len >= (int)capacity)
+ len = (int)capacity - 1;
+ mdns_string_t str = {buffer, len};
+ return str;
+}
+
+static mdns_string_t
+ipv6_address_to_string(char* buffer, size_t capacity, const struct sockaddr_in6* addr) {
+ char host[NI_MAXHOST] = {0};
+ char service[NI_MAXSERV] = {0};
+ int ret = getnameinfo((const struct sockaddr*)addr, sizeof(struct sockaddr_in6),
+ host, NI_MAXHOST, service, NI_MAXSERV,
+ NI_NUMERICSERV | NI_NUMERICHOST);
+ int len = 0;
+ if (ret == 0) {
+ if (addr->sin6_port != 0)
+ len = snprintf(buffer, capacity, "[%s]:%s", host, service);
+ else
+ len = snprintf(buffer, capacity, "%s", host);
+ }
+ if (len >= (int)capacity)
+ len = (int)capacity - 1;
+ mdns_string_t str = {buffer, len};
+ return str;
+}
+
+static mdns_string_t
+ip_address_to_string(char* buffer, size_t capacity, const struct sockaddr* addr) {
+ if (addr->sa_family == AF_INET6)
+ return ipv6_address_to_string(buffer, capacity, (const struct sockaddr_in6*)addr);
+ return ipv4_address_to_string(buffer, capacity, (const struct sockaddr_in*)addr);
+}
+
+static int
+callback(const struct sockaddr* from,
+ mdns_entry_type_t entry, uint16_t type,
+ uint16_t rclass, uint32_t ttl,
+ const void* data, size_t size, size_t offset, size_t length,
+ void* user_data) {
+ mdns_string_t fromaddrstr = ip_address_to_string(addrbuffer, sizeof(addrbuffer), from);
+ const char* entrytype = (entry == MDNS_ENTRYTYPE_ANSWER) ? "answer" :
+ ((entry == MDNS_ENTRYTYPE_AUTHORITY) ? "authority" : "additional");
+ if (type == MDNS_RECORDTYPE_PTR) {
+ mdns_string_t namestr = mdns_record_parse_ptr(data, size, offset, length,
+ namebuffer, sizeof(namebuffer));
+ INFO("%.*s : %s PTR %.*s type %u rclass 0x%x ttl %u length %d\n",
+ MDNS_STRING_FORMAT(fromaddrstr), entrytype,
+ MDNS_STRING_FORMAT(namestr), type, rclass, ttl, (int)length);
+ }
+ else if (type == MDNS_RECORDTYPE_SRV) {
+ mdns_record_srv_t srv = mdns_record_parse_srv(data, size, offset, length,
+ namebuffer, sizeof(namebuffer));
+ INFO("%.*s : %s SRV %.*s priority %d weight %d port %d\n",
+ MDNS_STRING_FORMAT(fromaddrstr), entrytype,
+ MDNS_STRING_FORMAT(srv.name), srv.priority, srv.weight, srv.port);
+ }
+ else if (type == MDNS_RECORDTYPE_A) {
+ struct sockaddr_in addr;
+ mdns_record_parse_a(data, size, offset, length, &addr);
+ mdns_string_t addrstr = ipv4_address_to_string(namebuffer, sizeof(namebuffer), &addr);
+ INFO("%.*s : %s A %.*s\n",
+ MDNS_STRING_FORMAT(fromaddrstr), entrytype,
+ MDNS_STRING_FORMAT(addrstr));
+ }
+ else if (type == MDNS_RECORDTYPE_AAAA) {
+ struct sockaddr_in6 addr;
+ mdns_record_parse_aaaa(data, size, offset, length, &addr);
+ mdns_string_t addrstr = ipv6_address_to_string(namebuffer, sizeof(namebuffer), &addr);
+ INFO("%.*s : %s AAAA %.*s\n",
+ MDNS_STRING_FORMAT(fromaddrstr), entrytype,
+ MDNS_STRING_FORMAT(addrstr));
+ }
+ else if (type == MDNS_RECORDTYPE_TXT) {
+ size_t parsed = mdns_record_parse_txt(data, size, offset, length,
+ txtbuffer, sizeof(txtbuffer) / sizeof(mdns_record_txt_t));
+ for (size_t itxt = 0; itxt < parsed; ++itxt) {
+ if (txtbuffer[itxt].value.length) {
+ INFO("%.*s : %s TXT %.*s = %.*s\n",
+ MDNS_STRING_FORMAT(fromaddrstr), entrytype,
+ MDNS_STRING_FORMAT(txtbuffer[itxt].key),
+ MDNS_STRING_FORMAT(txtbuffer[itxt].value));
+ }
+ else {
+ INFO("%.*s : %s TXT %.*s\n",
+ MDNS_STRING_FORMAT(fromaddrstr), entrytype,
+ MDNS_STRING_FORMAT(txtbuffer[itxt].key));
+ }
+ }
+ }
+ else {
+ INFO("%.*s : %s type %u rclass 0x%x ttl %u length %d\n",
+ MDNS_STRING_FORMAT(fromaddrstr), entrytype,
+ type, rclass, ttl, (int)length);
+ }
+ return 0;
+}
+
+int
+main() {
+ size_t capacity = 2048;
+ void* buffer = 0;
+ void* user_data = 0;
+ size_t records;
+
+#ifdef _WIN32
+ WORD versionWanted = MAKEWORD(1, 1);
+ WSADATA wsaData;
+ WSAStartup(versionWanted, &wsaData);
+#endif
+
+ int sock = mdns_socket_open_ipv4();
+ if (sock < 0) {
+ INFO("Failed to open socket: %s\n", strerror(errno));
+ return -1;
+ }
+ INFO("Opened IPv4 socket for mDNS/DNS-SD\n");
+ buffer = malloc(capacity);
+/*
+ INFO("Sending DNS-SD discovery\n");
+ if (mdns_discovery_send(sock)) {
+ INFO("Failed to send DNS-DS discovery: %s\n", strerror(errno));
+ goto quit;
+ }
+
+ INFO("Reading DNS-SD replies\n");
+ for (int i = 0; i < 10; ++i) {
+ records = mdns_discovery_recv(sock, buffer, capacity, callback,
+ user_data);
+ sleep(1);
+ }
+ */
+
+ INFO("Sending mDNS query\n");
+ if (mdns_query_send(sock, MDNS_RECORDTYPE_PTR,
+ MDNS_STRING_CONST("_hicn._udp.local."),
+ buffer, capacity)) {
+ INFO("Failed to send mDNS query: %s\n", strerror(errno));
+ goto quit;
+ }
+
+ INFO("Reading mDNS replies\n");
+ for (int i = 0; i < 10; ++i) {
+ records = mdns_query_recv(sock, buffer, capacity, callback, user_data, 1);
+ sleep(1);
+ }
+
+quit:
+ free(buffer);
+
+ mdns_socket_close(sock);
+ INFO("Closed socket\n");
+
+#ifdef _WIN32
+ WSACleanup();
+#endif
+
+ return 0;
+}
diff --git a/ctrl/facemgr/src/interfaces/bonjour/mdns/mdns.h b/ctrl/facemgr/src/interfaces/bonjour/mdns/mdns.h
new file mode 100644
index 000000000..ff04b5d72
--- /dev/null
+++ b/ctrl/facemgr/src/interfaces/bonjour/mdns/mdns.h
@@ -0,0 +1,879 @@
+/* mdns.h - mDNS/DNS-SD library - Public Domain - 2017 Mattias Jansson
+ *
+ * This library provides a cross-platform mDNS and DNS-SD library in C.
+ * The implementation is based on RFC 6762 and RFC 6763.
+ *
+ * The latest source code is always available at
+ *
+ * https://github.com/mjansson/mdns
+ *
+ * This library is put in the public domain; you can redistribute it and/or modify it without any restrictions.
+ *
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <fcntl.h>
+#ifdef _WIN32
+#include <Winsock2.h>
+#include <Ws2tcpip.h>
+#define strncasecmp _strnicmp
+#else
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif
+
+#define MDNS_INVALID_POS ((size_t)-1)
+
+#define MDNS_STRING_CONST(s) (s), (sizeof((s))-1)
+#define MDNS_STRING_FORMAT(s) (int)((s).length), s.str
+
+enum mdns_record_type {
+ MDNS_RECORDTYPE_IGNORE = 0,
+ //Address
+ MDNS_RECORDTYPE_A = 1,
+ //Domain Name pointer
+ MDNS_RECORDTYPE_PTR = 12,
+ //Arbitrary text string
+ MDNS_RECORDTYPE_TXT = 16,
+ //IP6 Address [Thomson]
+ MDNS_RECORDTYPE_AAAA = 28,
+ //Server Selection [RFC2782]
+ MDNS_RECORDTYPE_SRV = 33
+};
+
+enum mdns_entry_type {
+ MDNS_ENTRYTYPE_ANSWER = 1,
+ MDNS_ENTRYTYPE_AUTHORITY = 2,
+ MDNS_ENTRYTYPE_ADDITIONAL = 3
+};
+
+enum mdns_class {
+ MDNS_CLASS_IN = 1
+};
+
+typedef enum mdns_record_type mdns_record_type_t;
+typedef enum mdns_entry_type mdns_entry_type_t;
+typedef enum mdns_class mdns_class_t;
+
+typedef int (* mdns_record_callback_fn)(const struct sockaddr* from,
+ mdns_entry_type_t entry, uint16_t type,
+ uint16_t rclass, uint32_t ttl,
+ const void* data, size_t size, size_t offset, size_t length,
+ void* user_data);
+
+typedef struct mdns_string_t mdns_string_t;
+typedef struct mdns_string_pair_t mdns_string_pair_t;
+typedef struct mdns_record_srv_t mdns_record_srv_t;
+typedef struct mdns_record_txt_t mdns_record_txt_t;
+
+struct mdns_string_t {
+ const char* str;
+ size_t length;
+};
+
+struct mdns_string_pair_t {
+ size_t offset;
+ size_t length;
+ int ref;
+};
+
+struct mdns_record_srv_t {
+ uint16_t priority;
+ uint16_t weight;
+ uint16_t port;
+ mdns_string_t name;
+};
+
+struct mdns_record_txt_t {
+ mdns_string_t key;
+ mdns_string_t value;
+};
+
+static int
+mdns_socket_open_ipv4(void);
+
+static int
+mdns_socket_setup_ipv4(int sock);
+
+#if 0
+static int
+mdns_socket_open_ipv6(void);
+
+
+
+static int
+mdns_socket_setup_ipv6(int sock);
+#endif
+
+static void
+mdns_socket_close(int sock);
+
+#if 0
+static int
+mdns_discovery_send(int sock);
+
+
+static size_t
+mdns_discovery_recv(int sock, void* buffer, size_t capacity,
+ mdns_record_callback_fn callback, void* user_data);
+#endif
+
+static int
+mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t length,
+ void* buffer, size_t capacity);
+
+static size_t
+mdns_query_recv(int sock, void* buffer, size_t capacity,
+ mdns_record_callback_fn callback, void* user_data,
+ uint8_t one_shot);
+
+static mdns_string_t
+mdns_string_extract(const void* buffer, size_t size, size_t* offset,
+ char* str, size_t capacity);
+
+static int
+mdns_string_skip(const void* buffer, size_t size, size_t* offset);
+
+#if 0
+static int
+mdns_string_equal(const void* buffer_lhs, size_t size_lhs, size_t* ofs_lhs,
+ const void* buffer_rhs, size_t size_rhs, size_t* ofs_rhs);
+#endif
+
+static void*
+mdns_string_make(void* data, size_t capacity, const char* name, size_t length);
+
+static mdns_string_t
+mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t length,
+ char* strbuffer, size_t capacity);
+
+static mdns_record_srv_t
+mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t length,
+ char* strbuffer, size_t capacity);
+
+static struct sockaddr_in*
+mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t length,
+ struct sockaddr_in* addr);
+
+static struct sockaddr_in6*
+mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t length,
+ struct sockaddr_in6* addr);
+
+static size_t
+mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t length,
+ mdns_record_txt_t* records, size_t capacity);
+
+// Implementations
+
+static int
+mdns_socket_open_ipv4(void) {
+ int sock = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock < 0)
+ return -1;
+ if (mdns_socket_setup_ipv4(sock)) {
+ mdns_socket_close(sock);
+ return -1;
+ }
+ return sock;
+}
+
+static int
+mdns_socket_setup_ipv4(int sock) {
+ struct sockaddr_in saddr;
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = INADDR_ANY;
+#ifdef __APPLE__
+ saddr.sin_len = sizeof(saddr);
+#endif
+
+ if (bind(sock, (struct sockaddr*)&saddr, sizeof(saddr)))
+ return -1;
+
+#ifdef _WIN32
+ unsigned long param = 1;
+ ioctlsocket(sock, FIONBIO, &param);
+#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, &param);
+#else
+ const int flags = fcntl(sock, F_GETFL, 0);
+ fcntl(sock, F_SETFL, flags | O_NONBLOCK);
+#endif
+
+ int hops = 1;
+ unsigned int loopback = 1;
+ struct ipv6_mreq req;
+
+ setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char*)&hops, sizeof(hops));
+ setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const char*)&loopback, sizeof(loopback));
+
+ memset(&req, 0, sizeof(req));
+ req.ipv6mr_multiaddr.s6_addr[0] = 0xFF;
+ req.ipv6mr_multiaddr.s6_addr[1] = 0x02;
+ req.ipv6mr_multiaddr.s6_addr[15] = 0xFB;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char*)&req, sizeof(req)))
+ return -1;
+
+ return 0;
+}
+#endif
+
+static void
+mdns_socket_close(int sock) {
+#ifdef _WIN32
+ closesocket(sock);
+#else
+ close(sock);
+#endif
+}
+
+static int
+mdns_is_string_ref(uint8_t val) {
+ return (0xC0 == (val & 0xC0));
+}
+
+static mdns_string_pair_t
+mdns_get_next_substring(const void* rawdata, size_t size, size_t offset) {
+ const uint8_t* buffer = rawdata;
+ mdns_string_pair_t pair = {MDNS_INVALID_POS, 0, 0};
+ if (!buffer[offset]) {
+ pair.offset = offset;
+ return pair;
+ }
+ if (mdns_is_string_ref(buffer[offset])) {
+ if (size < offset + 2)
+ return pair;
+
+ offset = (((size_t)(0x3f & buffer[offset]) << 8) | (size_t)buffer[offset + 1]);
+ if (offset >= size)
+ return pair;
+
+ pair.ref = 1;
+ }
+
+ size_t length = (size_t)buffer[offset++];
+ if (size < offset + length)
+ return pair;
+
+ pair.offset = offset;
+ pair.length = length;
+
+ return pair;
+}
+
+static int
+mdns_string_skip(const void* buffer, size_t size, size_t* offset) {
+ size_t cur = *offset;
+ mdns_string_pair_t substr;
+ do {
+ substr = mdns_get_next_substring(buffer, size, cur);
+ if (substr.offset == MDNS_INVALID_POS)
+ return 0;
+ if (substr.ref) {
+ *offset = cur + 2;
+ return 1;
+ }
+ cur = substr.offset + substr.length;
+ }
+ while (substr.length);
+
+ *offset = cur + 1;
+ return 1;
+}
+
+#if 0
+static int
+mdns_string_equal(const void* buffer_lhs, size_t size_lhs, size_t* ofs_lhs,
+ const void* buffer_rhs, size_t size_rhs, size_t* ofs_rhs) {
+ size_t lhs_cur = *ofs_lhs;
+ size_t rhs_cur = *ofs_rhs;
+ size_t lhs_end = MDNS_INVALID_POS;
+ size_t rhs_end = MDNS_INVALID_POS;
+ mdns_string_pair_t lhs_substr;
+ mdns_string_pair_t rhs_substr;
+ do {
+ lhs_substr = mdns_get_next_substring(buffer_lhs, size_lhs, lhs_cur);
+ rhs_substr = mdns_get_next_substring(buffer_rhs, size_rhs, rhs_cur);
+ if ((lhs_substr.offset == MDNS_INVALID_POS) || (rhs_substr.offset == MDNS_INVALID_POS))
+ return 0;
+ if (lhs_substr.length != rhs_substr.length)
+ return 0;
+ if (strncasecmp((const char*)buffer_rhs + rhs_substr.offset,
+ (const char*)buffer_lhs + lhs_substr.offset, rhs_substr.length))
+ return 0;
+ if (lhs_substr.ref && (lhs_end == MDNS_INVALID_POS))
+ lhs_end = lhs_cur + 2;
+ if (rhs_substr.ref && (rhs_end == MDNS_INVALID_POS))
+ rhs_end = rhs_cur + 2;
+ lhs_cur = lhs_substr.offset + lhs_substr.length;
+ rhs_cur = rhs_substr.offset + rhs_substr.length;
+ }
+ while (lhs_substr.length);
+
+ if (lhs_end == MDNS_INVALID_POS)
+ lhs_end = lhs_cur + 1;
+ *ofs_lhs = lhs_end;
+
+ if (rhs_end == MDNS_INVALID_POS)
+ rhs_end = rhs_cur + 1;
+ *ofs_rhs = rhs_end;
+
+ return 1;
+}
+#endif
+
+static mdns_string_t
+mdns_string_extract(const void* buffer, size_t size, size_t* offset,
+ char* str, size_t capacity) {
+ size_t cur = *offset;
+ size_t end = MDNS_INVALID_POS;
+ mdns_string_pair_t substr;
+ mdns_string_t result = {str, 0};
+ char* dst = str;
+ size_t remain = capacity;
+ do {
+ substr = mdns_get_next_substring(buffer, size, cur);
+ if (substr.offset == MDNS_INVALID_POS)
+ return result;
+ if (substr.ref && (end == MDNS_INVALID_POS))
+ end = cur + 2;
+ if (substr.length) {
+ size_t to_copy = (substr.length < remain) ? substr.length : remain;
+ memcpy(dst, (const char*)buffer + substr.offset, to_copy);
+ dst += to_copy;
+ remain -= to_copy;
+ if (remain) {
+ *dst++ = '.';
+ --remain;
+ }
+ }
+ cur = substr.offset + substr.length;
+ }
+ while (substr.length);
+
+ if (end == MDNS_INVALID_POS)
+ end = cur + 1;
+ *offset = end;
+
+ result.length = capacity - remain;
+ return result;
+}
+
+static size_t
+mdns_string_find(const char* str, size_t length, char c, size_t offset) {
+ const void* found;
+ if (offset >= length)
+ return MDNS_INVALID_POS;
+ found = memchr(str + offset, c, length - offset);
+ if (found)
+ return (size_t)((const char*)found - str);
+ return MDNS_INVALID_POS;
+}
+
+static void*
+mdns_string_make(void* data, size_t capacity, const char* name, size_t length) {
+ size_t pos = 0;
+ size_t last_pos = 0;
+ size_t remain = capacity;
+ unsigned char* dest = data;
+ while ((last_pos < length) && ((pos = mdns_string_find(name, length, '.', last_pos)) != MDNS_INVALID_POS)) {
+ size_t sublength = pos - last_pos;
+ if (sublength < remain) {
+ *dest = (unsigned char)sublength;
+ memcpy(dest + 1, name + last_pos, sublength);
+ dest += sublength + 1;
+ remain -= sublength + 1;
+ }
+ else {
+ return 0;
+ }
+ last_pos = pos + 1;
+ }
+ if (last_pos < length) {
+ size_t sublength = length - last_pos;
+ if (sublength < capacity) {
+ *dest = (unsigned char)sublength;
+ memcpy(dest + 1, name + last_pos, sublength);
+ dest += sublength + 1;
+ remain -= sublength + 1;
+ }
+ else {
+ return 0;
+ }
+ }
+ if (!remain)
+ return 0;
+ *dest++ = 0;
+ return dest;
+}
+
+static size_t
+mdns_records_parse(const struct sockaddr* from, const void* buffer, size_t size, size_t* offset,
+ mdns_entry_type_t type, size_t records, mdns_record_callback_fn callback,
+ void* user_data) {
+ size_t parsed = 0;
+ int do_callback = 1;
+ for (size_t i = 0; i < records; ++i) {
+ mdns_string_skip(buffer, size, offset);
+ const uint16_t* data = (const uint16_t*)((const char*)buffer + (*offset));
+
+ uint16_t rtype = ntohs(*data++);
+ uint16_t rclass = ntohs(*data++);
+ uint32_t ttl = ntohs(*(const uint32_t*)(const void*)data); data += 2;
+ uint16_t length = ntohs(*data++);
+
+ *offset += 10;
+
+ if (do_callback) {
+ ++parsed;
+ if (callback(from, type, rtype, rclass, ttl, buffer, size, *offset, length,
+ user_data))
+ do_callback = 0;
+ }
+
+ *offset += length;
+ }
+ return parsed;
+}
+
+static const uint8_t mdns_services_query[] = {
+ // Transaction ID
+ 0x00, 0x00,
+ // Flags
+ 0x00, 0x00,
+ // 1 question
+ 0x00, 0x01,
+ // No answer, authority or additional RRs
+ 0x00, 0x00,
+ 0x00, 0x00,
+ 0x00, 0x00,
+ // _services._dns-sd._udp.local.
+ 0x09, '_', 's', 'e', 'r', 'v', 'i', 'c', 'e', 's',
+ 0x07, '_', 'd', 'n', 's', '-', 's', 'd',
+ 0x04, '_', 'u', 'd', 'p',
+ 0x05, 'l', 'o', 'c', 'a', 'l',
+ 0x00,
+ // PTR record
+ 0x00, MDNS_RECORDTYPE_PTR,
+ // QU (unicast response) and class IN
+ 0x80, MDNS_CLASS_IN
+};
+
+#if 0
+static int
+mdns_discovery_send(int sock) {
+ struct sockaddr_storage addr_storage;
+ struct sockaddr_in addr;
+ struct sockaddr_in6 addr6;
+ struct sockaddr* saddr = (struct sockaddr*)&addr_storage;
+ socklen_t saddrlen = sizeof(struct sockaddr_storage);
+ if (getsockname(sock, saddr, &saddrlen))
+ return -1;
+ if (saddr->sa_family == AF_INET6) {
+ memset(&addr6, 0, sizeof(struct sockaddr_in6));
+ addr6.sin6_family = AF_INET6;
+#ifdef __APPLE__
+ addr6.sin6_len = sizeof(struct sockaddr_in6);
+#endif
+ addr6.sin6_addr.s6_addr[0] = 0xFF;
+ addr6.sin6_addr.s6_addr[1] = 0x02;
+ addr6.sin6_addr.s6_addr[15] = 0xFB;
+ addr6.sin6_port = htons((unsigned short)5353);
+ saddr = (struct sockaddr*)&addr6;
+ saddrlen = sizeof(struct sockaddr_in6);
+ }
+ else {
+ memset(&addr, 0, sizeof(struct sockaddr_in));
+ addr.sin_family = AF_INET;
+#ifdef __APPLE__
+ addr.sin_len = sizeof(struct sockaddr_in);
+#endif
+ addr.sin_addr.s_addr = htonl((((uint32_t)224U) << 24U) | ((uint32_t)251U));
+ addr.sin_port = htons((unsigned short)5353);
+ saddr = (struct sockaddr*)&addr;
+ saddrlen = sizeof(struct sockaddr_in);
+ }
+
+ if (sendto(sock, mdns_services_query, sizeof(mdns_services_query), 0,
+ saddr, saddrlen) < 0)
+ return -1;
+ return 0;
+}
+
+static size_t
+mdns_discovery_recv(int sock, void* buffer, size_t capacity,
+ mdns_record_callback_fn callback, void* user_data) {
+ struct sockaddr_in6 addr;
+ struct sockaddr* saddr = (struct sockaddr*)&addr;
+ memset(&addr, 0, sizeof(addr));
+ saddr->sa_family = AF_INET;
+#ifdef __APPLE__
+ saddr->sa_len = sizeof(addr);
+#endif
+ socklen_t addrlen = sizeof(addr);
+ int ret = recvfrom(sock, buffer, capacity, 0,
+ saddr, &addrlen);
+ if (ret <= 0)
+ return 0;
+
+ size_t data_size = (size_t)ret;
+ size_t records = 0;
+ uint16_t* data = (uint16_t*)buffer;
+
+ uint16_t transaction_id = ntohs(*data++);
+ uint16_t flags = ntohs(*data++);
+ uint16_t questions = ntohs(*data++);
+ uint16_t answer_rrs = ntohs(*data++);
+ uint16_t authority_rrs = ntohs(*data++);
+ uint16_t additional_rrs = ntohs(*data++);
+
+ if (transaction_id || (flags != 0x8400))
+ return 0; //Not a reply to our question
+
+ if (questions > 1)
+ return 0;
+
+ int i;
+ for (i = 0; i < questions; ++i) {
+ size_t ofs = (size_t)((char*)data - (char*)buffer);
+ size_t verify_ofs = 12;
+ //Verify it's our question, _services._dns-sd._udp.local.
+ if (!mdns_string_equal(buffer, data_size, &ofs,
+ mdns_services_query, sizeof(mdns_services_query), &verify_ofs))
+ return 0;
+ data = (uint16_t*)((char*)buffer + ofs);
+
+ uint16_t type = ntohs(*data++);
+ uint16_t rclass = ntohs(*data++);
+
+ //Make sure we get a reply based on our PTR question for class IN
+ if ((type != MDNS_RECORDTYPE_PTR) || ((rclass & 0x7FFF) != MDNS_CLASS_IN))
+ return 0;
+ }
+
+ int do_callback = 1;
+ for (i = 0; i < answer_rrs; ++i) {
+ size_t ofs = (size_t)((char*)data - (char*)buffer);
+ size_t verify_ofs = 12;
+ //Verify it's an answer to our question, _services._dns-sd._udp.local.
+ int is_answer = mdns_string_equal(buffer, data_size, &ofs,
+ mdns_services_query, sizeof(mdns_services_query), &verify_ofs);
+ data = (uint16_t*)((char*)buffer + ofs);
+
+ uint16_t type = ntohs(*data++);
+ uint16_t rclass = ntohs(*data++);
+ uint32_t ttl = ntohl(*(uint32_t*)(void*)data); data += 2;
+ uint16_t length = ntohs(*data++);
+
+ if (is_answer && do_callback) {
+ ++records;
+ if (callback(saddr, MDNS_ENTRYTYPE_ANSWER, type, rclass, ttl, buffer,
+ data_size, (size_t)((char*)data - (char*)buffer), length,
+ user_data))
+ do_callback = 0;
+ }
+ data = (void*)((char*)data + length);
+ }
+
+ size_t offset = (size_t)((char*)data - (char*)buffer);
+ records += mdns_records_parse(saddr, buffer, data_size, &offset,
+ MDNS_ENTRYTYPE_AUTHORITY, authority_rrs,
+ callback, user_data);
+ records += mdns_records_parse(saddr, buffer, data_size, &offset,
+ MDNS_ENTRYTYPE_ADDITIONAL, additional_rrs,
+ callback, user_data);
+
+ return records;
+}
+#endif
+
+static uint16_t mdns_transaction_id = 0;
+
+static int
+mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t length,
+ void* buffer, size_t capacity) {
+ if (capacity < (17 + length))
+ return -1;
+
+ uint16_t* data = buffer;
+ //Transaction ID
+ *data++ = htons(++mdns_transaction_id);
+ //Flags
+ *data++ = 0;
+ //Questions
+ *data++ = htons(1);
+ //No answer, authority or additional RRs
+ *data++ = 0;
+ *data++ = 0;
+ *data++ = 0;
+ //Name string
+ data = mdns_string_make(data, capacity - 17, name, length);
+ if (!data)
+ return -1;
+ //Record type
+ *data++ = htons(type);
+ //! Unicast response, class IN
+ *data++ = htons(0x8000U | MDNS_CLASS_IN);
+
+ struct sockaddr_storage addr_storage;
+ struct sockaddr_in addr;
+ struct sockaddr_in6 addr6;
+ struct sockaddr* saddr = (struct sockaddr*)&addr_storage;
+ socklen_t saddrlen = sizeof(struct sockaddr_storage);
+ if (getsockname(sock, saddr, &saddrlen))
+ return -1;
+ if (saddr->sa_family == AF_INET6) {
+ memset(&addr6, 0, sizeof(struct sockaddr_in6));
+ addr6.sin6_family = AF_INET6;
+#ifdef __APPLE__
+ addr6.sin6_len = sizeof(struct sockaddr_in6);
+#endif
+ addr6.sin6_addr.s6_addr[0] = 0xFF;
+ addr6.sin6_addr.s6_addr[1] = 0x02;
+ addr6.sin6_addr.s6_addr[15] = 0xFB;
+ addr6.sin6_port = htons((unsigned short)5353);
+ saddr = (struct sockaddr*)&addr6;
+ saddrlen = sizeof(struct sockaddr_in6);
+ }
+ else {
+ memset(&addr, 0, sizeof(struct sockaddr_in));
+ addr.sin_family = AF_INET;
+#ifdef __APPLE__
+ addr.sin_len = sizeof(struct sockaddr_in);
+#endif
+ addr.sin_addr.s_addr = htonl((((uint32_t)224U) << 24U) | ((uint32_t)251U));
+ addr.sin_port = htons((unsigned short)5353);
+ saddr = (struct sockaddr*)&addr;
+ saddrlen = sizeof(struct sockaddr_in);
+ }
+
+ if (sendto(sock, buffer, (char*)data - (char*)buffer, 0,
+ saddr, saddrlen) < 0)
+ return -1;
+ return 0;
+}
+
+static size_t
+mdns_query_recv(int sock, void* buffer, size_t capacity,
+ mdns_record_callback_fn callback, void* user_data,
+ uint8_t one_shot) {
+ struct sockaddr_in6 addr;
+ struct sockaddr* saddr = (struct sockaddr*)&addr;
+ memset(&addr, 0, sizeof(addr));
+ saddr->sa_family = AF_INET;
+#ifdef __APPLE__
+ saddr->sa_len = sizeof(addr);
+#endif
+ socklen_t addrlen = sizeof(addr);
+ int ret = recvfrom(sock, buffer, capacity, 0,
+ saddr, &addrlen);
+ if (ret <= 0)
+ return 0;
+
+ size_t data_size = (size_t)ret;
+ uint16_t* data = (uint16_t*)buffer;
+
+ uint16_t transaction_id = ntohs(*data++);
+ ++data;// uint16_t flags = ntohs(*data++);
+ uint16_t questions = ntohs(*data++);
+ uint16_t answer_rrs = ntohs(*data++);
+ uint16_t authority_rrs = ntohs(*data++);
+ uint16_t additional_rrs = ntohs(*data++);
+
+ if (one_shot && transaction_id != mdns_transaction_id)// || (flags != 0x8400))
+ return 0; //Not a reply to our last question
+
+ if (questions > 1)
+ return 0;
+
+ //Skip questions part
+ int i;
+ for (i = 0; i < questions; ++i) {
+ size_t ofs = (size_t)((char*)data - (char*)buffer);
+ if (!mdns_string_skip(buffer, data_size, &ofs))
+ return 0;
+ data = (void*)((char*)buffer + ofs);
+ ++data;
+ ++data;
+ }
+
+ size_t records = 0;
+ size_t offset = (size_t)((char*)data - (char*)buffer);
+ records += mdns_records_parse(saddr, buffer, data_size, &offset,
+ MDNS_ENTRYTYPE_ANSWER, answer_rrs,
+ callback, user_data);
+ records += mdns_records_parse(saddr, buffer, data_size, &offset,
+ MDNS_ENTRYTYPE_AUTHORITY, authority_rrs,
+ callback, user_data);
+ records += mdns_records_parse(saddr, buffer, data_size, &offset,
+ MDNS_ENTRYTYPE_ADDITIONAL, additional_rrs,
+ callback, user_data);
+ return records;
+}
+
+static mdns_string_t
+mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t length,
+ char* strbuffer, size_t capacity) {
+ //PTR record is just a string
+ if ((size >= offset + length) && (length >= 2))
+ return mdns_string_extract(buffer, size, &offset, strbuffer, capacity);
+ mdns_string_t empty = {0, 0};
+ return empty;
+}
+
+static mdns_record_srv_t
+mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t length,
+ char* strbuffer, size_t capacity) {
+ mdns_record_srv_t srv;
+ memset(&srv, 0, sizeof(mdns_record_srv_t));
+ // Read the priority, weight, port number and the discovery name
+ // SRV record format (http://www.ietf.org/rfc/rfc2782.txt):
+ // 2 bytes network-order unsigned priority
+ // 2 bytes network-order unsigned weight
+ // 2 bytes network-order unsigned port
+ // string: discovery (domain) name, minimum 2 bytes when compressed
+ if ((size >= offset + length) && (length >= 8)) {
+ const uint16_t* recorddata = (const uint16_t*)((const char*)buffer + offset);
+ srv.priority = ntohs(*recorddata++);
+ srv.weight = ntohs(*recorddata++);
+ srv.port = ntohs(*recorddata++);
+ offset += 6;
+ srv.name = mdns_string_extract(buffer, size, &offset, strbuffer, capacity);
+ }
+ return srv;
+}
+
+static struct sockaddr_in*
+mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t length,
+ struct sockaddr_in* addr) {
+ memset(addr, 0, sizeof(struct sockaddr_in));
+ addr->sin_family = AF_INET;
+#ifdef __APPLE__
+ addr->sin_len = sizeof(struct sockaddr_in);
+#endif
+ if ((size >= offset + length) && (length == 4))
+ addr->sin_addr.s_addr = *(const uint32_t*)((const char*)buffer + offset);
+ return addr;
+}
+
+static struct sockaddr_in6*
+mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t length,
+ struct sockaddr_in6* addr) {
+ memset(addr, 0, sizeof(struct sockaddr_in6));
+ addr->sin6_family = AF_INET6;
+#ifdef __APPLE__
+ addr->sin6_len = sizeof(struct sockaddr_in6);
+#endif
+ if ((size >= offset + length) && (length == 16))
+ addr->sin6_addr = *(const struct in6_addr*)((const char*)buffer + offset);
+ return addr;
+}
+
+static size_t
+mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t length,
+ mdns_record_txt_t* records, size_t capacity) {
+ size_t parsed = 0;
+ const char* strdata;
+ size_t separator, sublength;
+ size_t end = offset + length;
+
+ if (size < end)
+ end = size;
+
+ while ((offset < end) && (parsed < capacity)) {
+ strdata = (const char*)buffer + offset;
+ sublength = *(const unsigned char*)strdata;
+
+ ++strdata;
+ offset += sublength + 1;
+
+ separator = 0;
+ for (size_t c = 0; c < sublength; ++c) {
+ //DNS-SD TXT record keys MUST be printable US-ASCII, [0x20, 0x7E]
+ if ((strdata[c] < 0x20) || (strdata[c] > 0x7E))
+ break;
+ if (strdata[c] == '=') {
+ separator = c;
+ break;
+ }
+ }
+
+ if (!separator)
+ continue;
+
+ if (separator < sublength) {
+ records[parsed].key.str = strdata;
+ records[parsed].key.length = separator;
+ records[parsed].value.str = strdata + separator + 1;
+ records[parsed].value.length = sublength - (separator + 1);
+ }
+ else {
+ records[parsed].key.str = strdata;
+ records[parsed].key.length = sublength;
+ }
+
+ ++parsed;
+ }
+
+ return parsed;
+}
+
+#ifdef _WIN32
+#undef strncasecmp
+#endif
diff --git a/ctrl/facemgr/src/interfaces/dummy/CMakeLists.txt b/ctrl/facemgr/src/interfaces/dummy/CMakeLists.txt
index 1af3b4b2a..05276bc5a 100644
--- a/ctrl/facemgr/src/interfaces/dummy/CMakeLists.txt
+++ b/ctrl/facemgr/src/interfaces/dummy/CMakeLists.txt
@@ -12,6 +12,7 @@
# limitations under the License.
list(APPEND HEADER_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/dummy.h
)
list(APPEND SOURCE_FILES
diff --git a/ctrl/facemgr/src/interfaces/dummy/dummy.c b/ctrl/facemgr/src/interfaces/dummy/dummy.c
index b0c558388..6a21792a2 100644
--- a/ctrl/facemgr/src/interfaces/dummy/dummy.c
+++ b/ctrl/facemgr/src/interfaces/dummy/dummy.c
@@ -19,31 +19,101 @@
*/
#include <stdlib.h>
+#include <unistd.h> // close
+
+#include <hicn/facemgr.h>
-#include "../../interface.h"
#include "../../common.h"
-#include "../../event.h"
-#include "../../face.h"
-#include "../../facemgr.h"
+#include "../../facelet.h"
+#include "../../interface.h"
+
+#include "dummy.h"
#define DEFAULT_PORT 9695
-int dummy_initialize(interface_t * interface, face_rules_t * rules, void **pdata) {
- ip_address_t local = IPV4_LOOPBACK;
- ip_address_t remote = IPV4_LOOPBACK;
- face_t * face = face_create_udp(&local, DEFAULT_PORT, &remote, DEFAULT_PORT, AF_INET);
- event_raise(EVENT_TYPE_CREATE, face, interface);
- return FACEMGR_SUCCESS;
+#define UNUSED(x) ((void)x)
+
+/*
+ * Internal data
+ */
+typedef struct {
+ /* The configuration data will likely be allocated on the stack (or should
+ * be freed) by the caller, we recommend to make a copy of this data.
+ * This copy can further be altered with default values.
+ */
+ dummy_cfg_t cfg;
+
+ /* ... */
+
+ int fd; /* Sample internal data: file descriptor */
+} dummy_data_t;
+
+int dummy_initialize(interface_t * interface, void * cfg)
+{
+ dummy_data_t * data = malloc(sizeof(dummy_data_t));
+ if (!data)
+ goto ERR_MALLOC;
+ interface->data = data;
+
+ /* Use default values for unspecified configuration parameters */
+ if (cfg) {
+ data->cfg = *(dummy_cfg_t *)cfg;
+ } else {
+ memset(&data->cfg, 0, sizeof(data->cfg));
+ }
+
+ /* ... */
+
+ data->fd = 0;
+
+ /* ... */
+
+ /*
+ * We should return a negative value in case of error, and a positive value
+ * otherwise:
+ * - a file descriptor (>0) will be added to the event loop; or
+ * - 0 if we don't use any file descriptor
+ */
+ return data->fd;
+
+ERR_MALLOC:
+ return -1;
+}
+
+int dummy_finalize(interface_t * interface)
+{
+ dummy_data_t * data = (dummy_data_t*)interface->data;
+
+ if (data->fd > 0)
+ close(data->fd);
+
+ return 0;
}
-int dummy_finalize(interface_t * interface) {
- return FACEMGR_SUCCESS;
+int dummy_callback(interface_t * interface)
+{
+ dummy_data_t * data = (dummy_data_t*)interface->data;
+ UNUSED(data);
+
+ /* ... */
+
+ return 0;
+}
+
+int dummy_on_event(interface_t * interface, const facelet_t * facelet)
+{
+ dummy_data_t * data = (dummy_data_t*)interface->data;
+ UNUSED(data);
+
+ /* ... */
+
+ return 0;
}
interface_ops_t dummy_ops = {
.type = "dummy",
- .is_singleton = true,
.initialize = dummy_initialize,
.finalize = dummy_finalize,
- .on_event = NULL,
+ .callback = dummy_callback,
+ .on_event = dummy_on_event,
};
diff --git a/ctrl/facemgr/src/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 <stdbool.h>
-#include <stdlib.h> // arc4random [random, rand]
#include <stdio.h> // snprintf
#include <time.h> // time
#include <hicn/ctrl.h>
+#include <hicn/facemgr.h>
+#include <hicn/util/ip_address.h>
+#include <hicn/util/log.h>
-#include "../../facemgr.h"
+#include "../../facelet.h"
#include "../../interface.h"
-#include "../../util/ip_address.h"
-#include "../../util/log.h"
#include "../../util/map.h"
-#include "../../event.h"
#define DEFAULT_ROUTE_COST 0
+typedef enum {
+ HL_STATE_UNDEFINED,
+ HL_STATE_FACES_SENT,
+ HL_STATE_DONE,
+} hl_state_t;
+
typedef struct {
hc_sock_t * s;
- bool busy;
+ hl_state_t state;
} hl_data_t;
-int hl_initialize(interface_t * interface, face_rules_t * rules, void ** pdata)
+int hl_process_state(interface_t * interface)
+{
+ hl_data_t * data = (hl_data_t *)interface->data;
+
+ hc_data_t * faces;
+#if 0
+ char buf[MAXSZ_FACE];
+#endif
+
+ switch(data->state)
+ {
+ case HL_STATE_UNDEFINED:
+ if (hc_face_list(data->s, &faces) < 0) {
+ /* Blocking call */
+ printf("Could not retrieve face list\n");
+ return -1;
+ }
+ foreach_face(f, faces) {
+#if 0
+ hc_face_snprintf(buf, MAXSZ_FACE, f);
+ printf("Face: %s\n", buf);
+#endif
+ facelet_t * facelet = facelet_create_from_face(&f->face);
+ facelet_set_event(facelet, FACELET_EVENT_GET);
+ facelet_raise_event(facelet, interface);
+ }
+ break;
+
+ case HL_STATE_FACES_SENT:
+ break;
+
+ default: /* HL_STATE_DONE never called */
+ break;
+ }
+
+ return 0;
+}
+
+int hl_initialize(interface_t * interface, void * cfg)
{
hl_data_t * data = malloc(sizeof(hl_data_t));
if (!data) {
@@ -57,51 +100,52 @@ int hl_initialize(interface_t * interface, face_rules_t * rules, void ** pdata)
goto ERR_CONNECT;
}
- data->busy = false;
+ data->state = HL_STATE_UNDEFINED;
+
+ interface->data = data;
- *pdata = data;
+ hl_process_state(interface);
- return FACEMGR_SUCCESS;
+ return 0;
ERR_CONNECT:
hc_sock_free(data->s);
ERR_SOCK:
free(data);
ERR_MALLOC:
- return FACEMGR_FAILURE;
+ return -1;
}
int hl_finalize(interface_t * interface)
{
//hc_data_t * data = interface->data;
//hc_sock_close(data->s);
- return FACEMGR_SUCCESS;
+ return 0;
}
-int hl_on_event(interface_t * interface, const event_t * event)
+int hl_on_event(interface_t * interface, const facelet_t * facelet)
{
- hc_face_t face;
+ hc_face_t hc_face;
hc_route_t route;
int rc;
hl_data_t * data = (hl_data_t *)interface->data;
- /* XXX We need a queue or a socket pool to process concurrent events */
- if (data->busy) {
- ERROR("[hicn_light] Busy !");
- return FACEMGR_FAILURE;
- }
+ face_t * face = NULL;
+ if (facelet_get_face(facelet, &face) < 0)
+ return -1;
+
+ switch(facelet_get_event(facelet)) {
- switch(event->type) {
- case EVENT_TYPE_CREATE:
+ case FACELET_EVENT_CREATE:
/* Create face */
- face.face = *event->face;
- rc = hc_face_create(data->s, &face);
+ hc_face.face = *face;
+ rc = hc_face_create(data->s, &hc_face);
if (rc < 0) {
ERROR("Failed to create face\n");
goto ERR;
}
- DEBUG("Created face id=%d\n", face.id);
+ INFO("Created face id=%d\n", hc_face.id);
#if 0
/* Add default route v4 */
@@ -134,88 +178,84 @@ int hl_on_event(interface_t * interface, const event_t * event)
}
#endif
-#if 1
- /* We add routes based on face tags */
-
- if (policy_tags_has(event->face->tags, POLICY_TAG_TRUSTED)) {
- route = (hc_route_t) {
- .face_id = face.id,
- .family = AF_INET6,
- .len = 16,
- .cost = DEFAULT_ROUTE_COST,
- };
- if (ip_address_pton("b001::", &route.remote_addr) < 0) {
- ERROR("Failed to convert prefix");
- goto ERR;
- }
- if (hc_route_create(data->s, &route) < 0) {
- ERROR("Failed to create hICN/IPv6 route");
- goto ERR;
- }
+ /* Adding default route */
+ route = (hc_route_t) {
+ .face_id = hc_face.id,
+ .family = AF_INET6,
+ .len = 0,
+ .cost = DEFAULT_ROUTE_COST,
+ };
+ if (ip_address_pton("::", &route.remote_addr) < 0) {
+ ERROR("Failed to convert prefix");
+ goto ERR;
+ }
+ if (hc_route_create(data->s, &route) < 0) {
+ ERROR("Failed to create hICN/IPv6 route");
+ goto ERR;
+ }
- route = (hc_route_t) {
- .face_id = face.id,
- .family = AF_INET6,
- .len = 16,
- .cost = DEFAULT_ROUTE_COST,
- };
- if (ip_address_pton("d001::", &route.remote_addr) < 0) {
- ERROR("Failed to convert prefix");
+ break;
+
+ case FACELET_EVENT_DELETE:
+ /* Removing a face should also remove associated routes */
+ /* Create face */
+ hc_face.face = *face;
+ rc = hc_face_delete(data->s, &hc_face);
+ if (rc < 0) {
+ ERROR("Failed to delete face\n");
+ goto ERR;
+ }
+ INFO("Deleted face id=%d\n", hc_face.id);
+ break;
+
+ case FACELET_EVENT_UPDATE:
+ /* Currently, only admin_state is supported */
+ if (facelet_get_admin_state_status(facelet) == FACELET_ATTR_STATUS_DIRTY) {
+ hc_face.face = *face;
+ hc_face_t * face_found;
+ rc = hc_face_get(data->s, &hc_face, &face_found);
+ if (rc < 0) {
+ ERROR("Failed to find face\n");
goto ERR;
}
- if (hc_route_create(data->s, &route) < 0) {
- ERROR("Failed to create hICN/IPv6 route");
+ if (!face_found) {
+ ERROR("Face to update has not been found");
goto ERR;
}
+ char conn_id_or_name[NAME_LEN];
+ snprintf(conn_id_or_name, NAME_LEN, "%d", face_found->id);
+ free(face_found);
+ printf("Face id = %d\n", face_found->id);
- } else {
-
- route = (hc_route_t) {
- .face_id = face.id,
- .family = AF_INET6,
- .len = 16,
- .cost = DEFAULT_ROUTE_COST,
- };
- if (ip_address_pton("c001::", &route.remote_addr) < 0) {
- ERROR("Failed to convert prefix");
+ face_state_t admin_state;
+ if (facelet_get_admin_state(facelet, &admin_state) < 0) {
+ ERROR("Failed to retrieve facelet admin state");
goto ERR;
}
- if (hc_route_create(data->s, &route) < 0) {
- ERROR("Failed to create hICN/IPv6 route");
+
+ printf("Setting admin state");
+ if (hc_connection_set_admin_state(data->s, conn_id_or_name, admin_state) < 0) {
+ ERROR("Failed to update admin state");
goto ERR;
}
+ INFO("Admin state updated");
}
-#endif
-
- break;
-
- case EVENT_TYPE_DELETE:
- /* Removing a face should also remove associated routes */
- /* Create face */
- face.face = *event->face;
- rc = hc_face_delete(data->s, &face);
- if (rc < 0) {
- ERROR("Failed to delete face\n");
- goto ERR;
- }
- INFO("Deleted face id=%d\n", face.id);
break;
default:
- ERROR("Unknown event %s\n", event_type_str[event->type]);
+ ERROR("Unknown event %s\n", facelet_event_str[facelet_get_event(facelet)]);
/* Unsupported events */
goto ERR;
}
- return FACEMGR_SUCCESS;
+ return 0;
ERR:
- return FACEMGR_FAILURE;
+ return -1;
}
const interface_ops_t hicn_light_ops = {
.type = "hicn_light",
- .is_singleton = false,
.initialize = hl_initialize,
.finalize = hl_finalize,
.on_event = hl_on_event,
diff --git a/ctrl/facemgr/src/interfaces/netlink/netlink.c b/ctrl/facemgr/src/interfaces/netlink/netlink.c
index 5bf0baf9f..08cbe4d3b 100644
--- a/ctrl/facemgr/src/interfaces/netlink/netlink.c
+++ b/ctrl/facemgr/src/interfaces/netlink/netlink.c
@@ -18,34 +18,120 @@
* \brief Netlink interface
*/
+#include <assert.h>
#include <linux/rtnetlink.h>
+#include <net/if_arp.h> // ARPHRD_LOOPBACK
#include <sys/types.h> // getpid
#include <unistd.h> // getpid
-#include "../../event.h"
-#include "../../facemgr.h"
+#include <hicn/facemgr.h>
+#include <hicn/util/ip_address.h>
+#include <hicn/util/log.h>
+
+#include "../../common.h"
+#include "../../facelet.h"
#include "../../interface.h"
+typedef enum {
+ NL_STATE_UNDEFINED,
+ NL_STATE_LINK_SENT,
+ NL_STATE_ADDR_SENT,
+ NL_STATE_DONE,
+} nl_state_t;
+
/* Internal data storage */
typedef struct {
int fd;
+ nl_state_t state;
} nl_data_t;
-// little helper to parsing message using netlink macroses
-void parseRtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+static inline void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len,
+ unsigned short flags)
{
+ unsigned short type;
+
memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+ while (RTA_OK(rta, len)) {
+ type = rta->rta_type & ~flags;
+ if (type <= max)
+ tb[type] = rta;
+ rta = RTA_NEXT(rta, len);
+ }
+}
+
+int nl_process_state(interface_t * interface)
+{
+ nl_data_t * data = (nl_data_t*)interface->data;
+ int rc;
+
+ switch(data->state) {
+ case NL_STATE_UNDEFINED:
+ {
+ struct {
+ struct nlmsghdr header;
+ struct rtgenmsg payload;
+ } msg2 = {
+ .header = {
+ .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
+ .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
+ .nlmsg_type = RTM_GETLINK,
+ .nlmsg_pid = getpid(),
+ .nlmsg_seq = 3,
+ },
+ .payload = {
+ .rtgen_family = AF_PACKET,
+ }
+ };
+
+ rc = send(data->fd, &msg2, msg2.header.nlmsg_len, 0);
+ if (rc < 0)
+ printf("E: Error sending netlink query\n");
+
+ data->state = NL_STATE_LINK_SENT;
+ break;
+ }
+
+ case NL_STATE_LINK_SENT:
+ {
+ /* Issue a first query to receive static state */
+ struct {
+ struct nlmsghdr header;
+ struct ifaddrmsg payload;
+ } msg = {
+ .header = {
+ .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
+ .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
+ .nlmsg_type = RTM_GETADDR,
+ .nlmsg_pid = getpid(),
+ .nlmsg_seq = 7,
+ },
+ .payload = {
+ .ifa_family = AF_INET,
+ }
+ };
+
+ rc = send(data->fd, &msg, msg.header.nlmsg_len, 0);
+ if (rc < 0)
+ printf("E: Error sending netlink query\n");
+
+ data->state = NL_STATE_ADDR_SENT;
+ break;
+ }
- while (RTA_OK(rta, len)) { // while not end of the message
- if (rta->rta_type <= max) {
- tb[rta->rta_type] = rta; // read attr
+ case NL_STATE_ADDR_SENT:
+ {
+ data->state = NL_STATE_DONE;
+ break;
}
- rta = RTA_NEXT(rta,len); // get next attr
+
+ default: /* NL_STATE_DONE never called */
+ break;
}
-}
+ return 0;
+}
-int nl_initialize(interface_t * interface, face_rules_t * rules, void ** pdata)
+int nl_initialize(interface_t * interface, void * cfg)
{
nl_data_t * data = malloc(sizeof(nl_data_t));
if (!data)
@@ -57,31 +143,194 @@ int nl_initialize(interface_t * interface, face_rules_t * rules, void ** pdata)
goto ERR_SOCKET;
}
+ data->state = NL_STATE_UNDEFINED;
+
struct sockaddr_nl local; // local addr struct
memset(&local, 0, sizeof(local));
local.nl_family = AF_NETLINK; // set protocol family
- local.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE; // set groups we interested in
+ // NOTE: RTNLGRP_LINK replaces obsolete RTMGRP_LINK, etc
+ local.nl_groups = 0
+ | RTMGRP_LINK
+ | RTMGRP_IPV4_IFADDR
+ | RTMGRP_IPV6_IFADDR
+#if 0
+ | RTMGRP_IPV4_ROUTE;
+ | RTMGRP_IPV6_ROUTE;
+#endif
+ ;
local.nl_pid = getpid(); // set out id using current process id
-
if (bind(data->fd, (struct sockaddr*)&local, sizeof(local)) < 0) { // bind socket
printf("Failed to bind netlink socket: %s\n", (char*)strerror(errno));
goto ERR_BIND;
}
- /* Issue a first query to receive static state */
+ interface->data = data;
+ nl_process_state(interface);
- *pdata = data;
- return data->fd; // FACEMGR_SUCCESS;
+ return data->fd; // 0;
ERR_BIND:
close(data->fd);
ERR_SOCKET:
free(data);
ERR_MALLOC:
- *pdata = NULL;
- return FACEMGR_FAILURE;
+ return -1;
+}
+
+int parse_link(struct nlmsghdr * h, facelet_t ** facelet,
+ char * interface_name, size_t interface_name_size,
+ bool * up, bool * running)
+{
+ struct ifinfomsg *ifi; // structure for network interface info
+ struct rtattr *tb[IFLA_MAX + 1];
+
+ assert(facelet);
+
+ ifi = (struct ifinfomsg*) NLMSG_DATA(h); // get information about changed network interface
+ parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(h), 1<<15);
+
+ if (interface_name) {
+ assert(tb[IFLA_IFNAME]);
+ snprintf(interface_name, interface_name_size, "%s", (char*)RTA_DATA(tb[IFLA_IFNAME]));
+ }
+
+ if (up)
+ *up = ifi->ifi_flags & IFF_UP;
+ if (running)
+ *running = ifi->ifi_flags & IFF_RUNNING;
+
+
+ *facelet = facelet_create();
+ netdevice_t * netdevice = netdevice_create_from_name(interface_name);
+ if (!netdevice)
+ goto ERROR;
+ int rc = facelet_set_netdevice(*facelet, *netdevice);
+ if (rc < 0)
+ goto ERROR;
+
+// FIXME Tags
+#if 0
+ /* This is the only opportunity to identify a loopback interface on both
+ * linux _and_ android, as NetworkCapabilities does not have a flag for
+ * LOOPBACK... */
+ if (ifi->ifi_type==ARPHRD_LOOPBACK) {
+ DEBUG("loopback");
+ }
+
+#ifdef IFLA_WIRELESS
+ /*
+ * This signals a wirless event, but it typically occurs _after_ a face is
+ * created... we might need to update an existing face by setting a tag...
+ * or find a way to exploit this flag before actually creating the face...
+ */
+ if (tb[IFLA_WIRELESS])
+ DEBUG("wireless!!!");
+#else
+#warning "IFLA_WIRELESS not supported on this platform"
+#endif /* IFLA_WIRELESS */
+
+#endif
+
+ // TODO
+ // - ifi_change
+ // - IFLA_PROTINFO
+
+ return 0;
+
+ERROR:
+ facelet_free(*facelet);
+ *facelet = NULL;
+
+ return -1;
+}
+
+int parse_addr(struct nlmsghdr * h, facelet_t ** facelet,
+ char * interface_name, size_t interface_name_size,
+ char * interface_address, size_t interface_address_size)
+{
+ ip_address_t local_addr = IP_ADDRESS_EMPTY;
+ struct ifaddrmsg *ifa; // structure for network interface data
+ struct rtattr *tba[IFA_MAX+1];
+
+ assert(facelet);
+
+ ifa = (struct ifaddrmsg*)NLMSG_DATA(h); // get data from the network interface
+
+ parse_rtattr(tba, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(h), 0);
+
+ /* FIXME
+ *
+ * IFA_LOCAL ok for v4, not there for v6
+ *
+ * IFA_ADDRESS seems to work for both but with the following precaution
+ *
+ * IFA_ADDRESS is prefix address, rather than local interface address.
+ * It makes no difference for normally configured broadcast interfaces,
+ * but for point-to-point IFA_ADDRESS is DESTINATION address,
+ * local address is supplied in IFA_LOCAL attribute.
+ */
+ if (!tba[IFA_ADDRESS]) {
+ ERROR("[netlink.parse_addr] No local address");
+ return -1;
+ }
+
+ switch(ifa->ifa_family) {
+ case AF_INET:
+ local_addr.v4.as_inaddr = *(struct in_addr*)RTA_DATA(tba[IFA_ADDRESS]);
+ break;
+ case AF_INET6:
+ local_addr.v6.as_in6addr = *(struct in6_addr*)RTA_DATA(tba[IFA_ADDRESS]);
+ break;
+ default:
+ return 0;
+ }
+
+#if 0 /* Not working for v6 */
+ if (interface_name) {
+ assert(tba[IFLA_IFNAME]);
+ snprintf(interface_name, interface_name_size, "%s", (char*)RTA_DATA(tba[IFLA_IFNAME]));
+ }
+#endif
+
+ /* See comment in parse_link */
+ if (interface_address) {
+ assert(tba[IFA_ADDRESS]);
+ ip_address_snprintf(interface_address, interface_address_size, &local_addr, ifa->ifa_family);
+ }
+
+ netdevice_t * netdevice = netdevice_create_from_index(ifa->ifa_index);
+ if (!netdevice) {
+ ERROR("[netlink.parse_addr] error creating netdevice '%s'", interface_name);
+ goto ERROR;
+ }
+
+ if (interface_name) {
+ snprintf(interface_name, interface_name_size, "%s", netdevice->name);
+ }
+
+ *facelet = facelet_create();
+ if (facelet_set_netdevice(*facelet, *netdevice) < 0) {
+ ERROR("[netlink.parse_addr] error setting netdevice");
+ goto ERROR;
+ }
+ if (facelet_set_family(*facelet, ifa->ifa_family) < 0) {
+ ERROR("[netlink.parse_addr] error setting family");
+ goto ERROR;
+ }
+ if (facelet_set_local_addr(*facelet, local_addr) < 0) {
+ ERROR("[netlink.parse_addr] error setting local address");
+ goto ERROR;
+ }
+
+ return 0;
+
+ERROR:
+ facelet_free(*facelet);
+ *facelet = NULL;
+
+ return -1;
}
int nl_callback(interface_t * interface)
@@ -97,13 +346,12 @@ int nl_callback(interface_t * interface)
iov.iov_len = sizeof(buf); // set size
// initialize protocol message header
- struct msghdr msg;
- {
- msg.msg_name = &local; // local address
- msg.msg_namelen = sizeof(local); // address size
- msg.msg_iov = &iov; // io vector
- msg.msg_iovlen = 1; // io size
- }
+ struct msghdr msg = {
+ .msg_name = &local, // local address
+ .msg_namelen = sizeof(local), // address size
+ .msg_iov = &iov, // io vector
+ .msg_iovlen = 1, // io size
+ };
ssize_t status = recvmsg(data->fd, &msg, 0);
@@ -115,12 +363,12 @@ int nl_callback(interface_t * interface)
*/
printf("Failed to read netlink: %s", (char*)strerror(errno));
- return FACEMGR_FAILURE;
+ return -1;
}
if (msg.msg_namelen != sizeof(local)) { // check message length, just in case
printf("Invalid length of the sender address struct\n");
- return FACEMGR_FAILURE;
+ return -1;
}
// message parser
@@ -129,103 +377,122 @@ int nl_callback(interface_t * interface)
for (h = (struct nlmsghdr*)buf; status >= (ssize_t)sizeof(*h); ) { // read all messagess headers
int len = h->nlmsg_len;
int l = len - sizeof(*h);
- char *ifName = NULL;
if ((l < 0) || (len > status)) {
printf("Invalid message length: %i\n", len);
continue;
}
- // now we can check message type
- if ((h->nlmsg_type == RTM_NEWROUTE) || (h->nlmsg_type == RTM_DELROUTE)) { // some changes in routing table
- printf("Routing table was changed\n");
- } else { // in other case we need to go deeper
- char *ifUpp;
- char *ifRunn;
- struct ifinfomsg *ifi; // structure for network interface info
- struct rtattr *tb[IFLA_MAX + 1];
-
- ifi = (struct ifinfomsg*) NLMSG_DATA(h); // get information about changed network interface
-
- parseRtattr(tb, IFLA_MAX, IFLA_RTA(ifi), h->nlmsg_len); // get attributes
-
- if (tb[IFLA_IFNAME]) { // validation
- ifName = (char*)RTA_DATA(tb[IFLA_IFNAME]); // get network interface name
- }
-
- if (ifi->ifi_flags & IFF_UP) { // get UP flag of the network interface
- ifUpp = (char*)"UP";
- } else {
- ifUpp = (char*)"DOWN";
- }
+ switch(h->nlmsg_type) {
+#if 0
+ case RTM_NEWROUTE:
+ case RTM_DELROUTE:
+ DEBUG("Routing table was changed");
+ break;
+#endif
+
+ case RTM_DELADDR:
+ {
+ facelet_t * facelet = NULL;
+ char interface_name[IFNAMSIZ];
+ char interface_address[MAXSZ_IP_ADDRESS] = {0};
+
+ if (parse_addr(h, &facelet, interface_name, IFNAMSIZ,
+ interface_address, MAXSZ_IP_ADDRESS) < 0) {
+ ERROR("Error parsing address message");
+ break;
+ }
- if (ifi->ifi_flags & IFF_RUNNING) { // get RUNNING flag of the network interface
- ifRunn = (char*)"RUNNING";
- } else {
- ifRunn = (char*)"NOT RUNNING";
+ DEBUG("Interface %s: address was removed", interface_name);
+ if (facelet) {
+ facelet_set_event(facelet, FACELET_EVENT_DELETE);
+ facelet_raise_event(facelet, interface);
+ }
+ break;
}
- char ifAddress[256] = {0}; // network addr
- struct ifaddrmsg *ifa; // structure for network interface data
- struct rtattr *tba[IFA_MAX+1];
+ case RTM_NEWADDR:
+ {
+ facelet_t * facelet = NULL;
+ char interface_name[IFNAMSIZ];
+ char interface_address[MAXSZ_IP_ADDRESS] = {0};
- ifa = (struct ifaddrmsg*)NLMSG_DATA(h); // get data from the network interface
+ if (parse_addr(h, &facelet, interface_name, IFNAMSIZ,
+ interface_address, MAXSZ_IP_ADDRESS) < 0) {
+ ERROR("Error parsing address message");
+ break;
+ }
- parseRtattr(tba, IFA_MAX, IFA_RTA(ifa), h->nlmsg_len);
+ DEBUG("Interface %s: new address was assigned: %s", interface_name, interface_address);
- if (tba[IFA_LOCAL]) {
- inet_ntop(AF_INET, RTA_DATA(tba[IFA_LOCAL]), ifAddress, sizeof(ifAddress)); // get IP addr
+ if (facelet) {
+ facelet_set_event(facelet, FACELET_EVENT_UPDATE);
+ facelet_raise_event(facelet, interface);
+ }
+ break;
}
- face_t * face;
-
- if (tba[IFA_LOCAL]) {
- ip_address_t local_addr = IP_ADDRESS_EMPTY;
- switch(ifa->ifa_family) {
- case AF_INET:
- local_addr.v4.as_inaddr = *(struct in_addr*)RTA_DATA(tba[IFA_LOCAL]);
- break;
- case AF_INET6:
- local_addr.v6.as_in6addr = *(struct in6_addr*)RTA_DATA(tba[IFA_LOCAL]);
- break;
- default:
- continue;
+ case RTM_DELLINK:
+ {
+ facelet_t * facelet = NULL;
+ char interface_name[IFNAMSIZ];
+ if (parse_link(h, &facelet, interface_name, IFNAMSIZ,
+ NULL, NULL) < 0) {
+ ERROR("Error parsing link message");
+ break;
+ }
+ if (facelet) {
+ facelet_set_event(facelet, FACELET_EVENT_DELETE);
+ facelet_raise_event(facelet, interface);
}
- face = face_create_udp(&local_addr, 0, &IP_ADDRESS_EMPTY, 0, ifa->ifa_family);
- } else {
- face = NULL;
+
+ DEBUG("Network interface %s was removed", interface_name);
+ break;
}
- switch (h->nlmsg_type) {
- case RTM_DELADDR:
- // DOES NOT SEEM TO BE TRIGGERED
- printf("Interface %s: address was removed\n", ifName);
- if (face)
- event_raise(EVENT_TYPE_DELETE, face, interface);
- break;
+ case RTM_NEWLINK:
+ {
+ facelet_t * facelet = NULL;
+ char interface_name[IFNAMSIZ];
+ bool up, running;
- case RTM_DELLINK:
- printf("Network interface %s was removed\n", ifName);
+ if (parse_link(h, &facelet, interface_name, IFNAMSIZ, &up, &running) < 0) {
+ ERROR("Error parsing link message");
break;
+ }
- case RTM_NEWLINK:
- printf("New network interface %s, state: %s %s\n", ifName, ifUpp, ifRunn);
- // UP RUNNING
- // UP NOT RUNNING
- // DOWN NOT RUNNING
- if (!(ifi->ifi_flags & IFF_UP) || (!(ifi->ifi_flags & IFF_RUNNING))) {
- if(face)
- event_raise(EVENT_TYPE_DELETE, face, interface);
+ // UP RUNNING
+ // UP NOT RUNNING
+ // DOWN NOT RUNNING
+ DEBUG("New network interface %s, state: %s %s", interface_name,
+ up ? "UP" : "DOWN",
+ running ? "RUNNING" : "NOT_RUNNING");
+
+ if (facelet && up && running) {
+ facelet_set_event(facelet, FACELET_EVENT_CREATE);
+ facelet_t * facelet6 = facelet_dup(facelet);
+ if (!facelet6) {
+ ERROR("Could not duplicate face for v6");
+ break;
}
- break;
- case RTM_NEWADDR:
- printf("Interface %s: new address was assigned: %s\n", ifName, ifAddress);
- printf("NEW FACE\n");
- if (face)
- event_raise(EVENT_TYPE_CREATE, face, interface);
- break;
+ facelet_set_family(facelet, AF_INET);
+ facelet_raise_event(facelet, interface);
+
+ facelet_set_family(facelet6, AF_INET6);
+ facelet_raise_event(facelet6, interface);
+ }
+ break;
}
+
+ case NLMSG_ERROR:
+ break;
+ case NLMSG_DONE:
+ nl_process_state(interface);
+ break;
+ default:
+ break;
+
}
status -= NLMSG_ALIGN(len); // align offsets by the message length, this is important
@@ -233,20 +500,19 @@ int nl_callback(interface_t * interface)
h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); // get next message
}
- return FACEMGR_SUCCESS;
+ return 0;
}
int nl_finalize(interface_t * interface)
{
nl_data_t * data = (nl_data_t*)interface->data;
close(data->fd);
- return FACEMGR_SUCCESS;
+ return 0;
}
const interface_ops_t netlink_ops = {
.type = "netlink",
- .is_singleton = true,
.initialize = nl_initialize,
.callback = nl_callback,
.finalize = nl_finalize,
diff --git a/ctrl/facemgr/src/interfaces/network_framework/CMakeLists.txt b/ctrl/facemgr/src/interfaces/network_framework/CMakeLists.txt
index ca6659342..e8b0144b1 100644
--- a/ctrl/facemgr/src/interfaces/network_framework/CMakeLists.txt
+++ b/ctrl/facemgr/src/interfaces/network_framework/CMakeLists.txt
@@ -17,6 +17,7 @@ if (NOT NETWORK_LIBRARY)
endif()
list(APPEND HEADER_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/network_framework.h
)
list(APPEND SOURCE_FILES
diff --git a/ctrl/facemgr/src/interfaces/network_framework/network_framework.c b/ctrl/facemgr/src/interfaces/network_framework/network_framework.c
index 8a33129b4..4a9d24f61 100644
--- a/ctrl/facemgr/src/interfaces/network_framework/network_framework.c
+++ b/ctrl/facemgr/src/interfaces/network_framework/network_framework.c
@@ -24,14 +24,17 @@
#include <Network/Network.h>
#include <err.h>
+#include <hicn/facemgr.h>
+#include <hicn/util/token.h>
+#include <hicn/util/log.h>
+
#include "../../common.h"
-#include "../../event.h"
-#include "../../face.h"
-#include "../../facemgr.h"
+#include <hicn/ctrl/face.h>
+#include "../../facelet.h"
#include "../../interface.h"
#include "../../util/map.h"
-#include "../../util/token.h"
-#include "../../util/log.h"
+
+#include "network_framework.h"
/*
* Bonjour service discovery for hICN forwarder
@@ -107,7 +110,7 @@ cmp_iface(const nw_interface_t iface1, const nw_interface_t iface2)
//TYPEDEF_MAP(map_cnx, nw_interface_t, nw_connection_t, cmp_iface);
typedef struct {
- face_rules_t * rules; /**< Face creation rules */
+ network_framework_cfg_t cfg;
nw_path_monitor_t pm; /**< Main path monitor */
// map_cnx_t map_cnx; /**< Map: interface -> connection for face status */
} nf_data_t;
@@ -209,97 +212,90 @@ dump_connection(nw_connection_t connection, int indent)
nw_release(path);
}
-face_t *
-face_create_from_connection(nw_connection_t connection, face_rules_t * rules)
+facelet_t *
+facelet_create_from_connection(nw_connection_t connection)
{
- face_t * face;
- struct sockaddr_in * sin;
- struct sockaddr_in6 * sin6;
+ facelet_t * facelet;
+ ip_address_t local_addr, remote_addr;
+ uint16_t remote_port;
nw_path_t path = nw_connection_copy_current_path(connection);
nw_endpoint_t local = nw_path_copy_effective_local_endpoint(path);
nw_endpoint_t remote = nw_path_copy_effective_remote_endpoint(path);
__block nw_interface_t interface;
- const struct sockaddr * local_addr = nw_endpoint_get_address(local);
- const struct sockaddr * remote_addr = nw_endpoint_get_address(remote);
+ const struct sockaddr * local_sa = nw_endpoint_get_address(local);
+ const struct sockaddr * remote_sa = nw_endpoint_get_address(remote);
- assert (local_addr->sa_family == remote_addr->sa_family);
- switch(local_addr->sa_family) {
+ assert (local_sa->sa_family == remote_sa->sa_family);
+ switch(local_sa->sa_family) {
case AF_INET:
- sin = (struct sockaddr_in *)local_addr;
- sin->sin_port = htons(DEFAULT_PORT);
- sin = (struct sockaddr_in *)remote_addr;
- sin->sin_port = htons(DEFAULT_PORT);
+ local_addr.v4.as_inaddr = ((struct sockaddr_in *)local_sa)->sin_addr;
+ remote_addr.v4.as_inaddr = ((struct sockaddr_in *)remote_sa)->sin_addr;
+ remote_port = ((struct sockaddr_in *)remote_sa)->sin_port;
break;
case AF_INET6:
- sin6 = (struct sockaddr_in6 *)local_addr;
- sin6->sin6_port = htons(DEFAULT_PORT);
- sin6 = (struct sockaddr_in6 *)remote_addr;
- sin6->sin6_port = htons(DEFAULT_PORT);
+ local_addr.v6.as_in6addr = ((struct sockaddr_in6 *)local_sa)->sin6_addr;
+ remote_addr.v6.as_in6addr = ((struct sockaddr_in6 *)remote_sa)->sin6_addr;
+ remote_port = ((struct sockaddr_in6 *)remote_sa)->sin6_port;
break;
default:
- ERROR("Unsupported address family: %d\n", local_addr->sa_family);
+ ERROR("Unsupported address family: %d\n", local_sa->sa_family);
return NULL;
}
- face = face_create_udp_sa(local_addr, remote_addr);
/* Retrieving path interface type (a single one expected */
nw_path_enumerate_interfaces(path, (nw_path_enumerate_interfaces_block_t)^(nw_interface_t path_interface) {
interface = path_interface;
return false;
});
- nw_interface_type_t type = nw_interface_get_type(interface);
- const char * name = nw_interface_get_name(interface);
-
- policy_tags_t tags = POLICY_TAGS_EMPTY;
- if (rules) {
- if (!FACEMGR_IS_ERROR(face_rules_get(rules, name, &tags)))
- goto SET_TAGS;
+ const char * name = nw_interface_get_name(interface);
+ netdevice_t netdevice;
+ snprintf(netdevice.name, IFNAMSIZ, "%s", name);
+ netdevice_update_index(&netdevice);
- char tags[MAXSZ_POLICY_TAGS];
- policy_tags_snprintf(tags, MAXSZ_POLICY_TAGS, face->tags);
- }
+ netdevice_type_t netdevice_type;
+ nw_interface_type_t type = nw_interface_get_type(interface);
switch(type) {
case INTERFACE_TYPE_OTHER:
- policy_tags_add(&tags, POLICY_TAG_WIFI);
- policy_tags_add(&tags, POLICY_TAG_TRUSTED);
+ netdevice_type = NETDEVICE_TYPE_UNDEFINED;
break;
case INTERFACE_TYPE_WIFI:
- // XXX disambuiguate on interface name for now.
- policy_tags_add(&tags, POLICY_TAG_WIFI);
- policy_tags_add(&tags, POLICY_TAG_TRUSTED);
+ netdevice_type = NETDEVICE_TYPE_WIFI;
break;
case INTERFACE_TYPE_CELLULAR:
- policy_tags_add(&tags, POLICY_TAG_CELLULAR);
+ netdevice_type = NETDEVICE_TYPE_CELLULAR;
break;
case INTERFACE_TYPE_WIRED:
- /* Both VPN and USB WiFi are not well detected on MacOS. For USB
- * WiFi, we currently have no solution. For VPN, until we have
- * proper support of AnyC APIs, we need to have heuristics to
- * determine VPN interfaces. */
- policy_tags_add(&tags, POLICY_TAG_WIRED);
- policy_tags_add(&tags, POLICY_TAG_TRUSTED);
+ netdevice_type = NETDEVICE_TYPE_WIRED;
break;
case INTERFACE_TYPE_LOOPBACK:
- tags = POLICY_TAGS_EMPTY;
+ netdevice_type = NETDEVICE_TYPE_LOOPBACK;
break;
default:
break;
}
-SET_TAGS:
- face_set_tags(face, tags);
-
nw_release(local);
nw_release(remote);
nw_release(path);
- return face;
+ facelet = facelet_create();
+ if (!facelet)
+ return NULL;
+
+ facelet_set_netdevice(facelet, netdevice);
+ facelet_set_netdevice_type(facelet, netdevice_type);
+ facelet_set_family(facelet, local_sa->sa_family);
+ facelet_set_local_addr(facelet, local_addr);
+ facelet_set_remote_addr(facelet, remote_addr);
+ facelet_set_remote_port(facelet, remote_port);
+
+ return facelet;
}
void
@@ -340,9 +336,11 @@ on_connection_state_event(interface_t * interface, nw_interface_t iface,
dump_connection(cnx, 1);
});
#endif
- nf_data_t * data = (nf_data_t*)interface->data;
- face_t * face = face_create_from_connection(cnx, data->rules);
- event_raise(EVENT_TYPE_CREATE, face, interface);
+ facelet_t * facelet = facelet_create_from_connection(cnx);
+ if (!facelet)
+ return;
+ facelet_set_event(facelet, FACELET_EVENT_CREATE);
+ facelet_raise_event(facelet, interface);
break;
}
case nw_connection_state_failed:
@@ -482,14 +480,11 @@ void on_interface_event(interface_t * interface, nw_interface_t iface)
* This is the first time we have a connection with address and port
* and thus the full identification of an hICN face
*/
- nf_data_t * data = (nf_data_t*)interface->data;
- face_t * face = face_create_from_connection(connection, data->rules);
- //event_raise(value ? EVENT_TYPE_SET_UP : EVENT_TYPE_SET_DOWN, face, interface);
- if(value) {
- event_raise(EVENT_TYPE_CREATE, face, interface);
- } else {
- event_raise(EVENT_TYPE_DELETE, face, interface);
- }
+ facelet_t * facelet = facelet_create_from_connection(connection);
+ if (!facelet)
+ return;
+ facelet_set_event(facelet, value ? FACELET_EVENT_CREATE : FACELET_EVENT_DELETE);
+ facelet_raise_event(facelet, interface);
});
@@ -528,13 +523,16 @@ void on_path_event(interface_t * interface, nw_path_t path)
}
-int nf_initialize(interface_t * interface, face_rules_t * rules, void ** pdata)
+int nf_initialize(interface_t * interface, void * cfg)
{
nf_data_t * data = malloc(sizeof(nf_data_t));
if (!data)
goto ERR_MALLOC;
- data->rules = rules;
+ if (cfg)
+ data->cfg = * (network_framework_cfg_t *)cfg;
+ else
+ data->cfg.rules = NULL;
data->pm = nw_path_monitor_create();
if (!data->pm)
@@ -552,14 +550,13 @@ int nf_initialize(interface_t * interface, face_rules_t * rules, void ** pdata)
DEBUG("Starting network path monitor");
nw_path_monitor_start(data->pm);
- *pdata = data;
- return FACEMGR_SUCCESS;
+ interface->data = data;
+ return 0;
ERR_PM:
free(data);
ERR_MALLOC:
- *pdata = NULL;
- return FACEMGR_FAILURE;
+ return -1;
}
int nf_finalize(interface_t * interface)
@@ -569,12 +566,11 @@ int nf_finalize(interface_t * interface)
nw_path_monitor_cancel(data->pm);
data->pm = NULL;
}
- return FACEMGR_SUCCESS;
+ return 0;
}
const interface_ops_t network_framework_ops = {
.type = "network_framework",
- .is_singleton = true,
.initialize = nf_initialize,
.finalize = nf_finalize,
.on_event = NULL,
diff --git a/ctrl/facemgr/src/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 <assert.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <hicn/facemgr.h>
+
+#include "../../common.h"
+#include "../../facelet.h"
+#include "../../interface.h"
+
+/**
+ * \brief Default unix socket path (the leading \0 means using the abstract
+ * namespace instead of the filesystem).
+ */
+#define UNIX_PATH "\0updownsrv"
+
+typedef struct {
+ int fd; /* Unix client socket */
+} updown_data_t;
+
+int updown_initialize(interface_t * interface, void * cfg)
+{
+ struct sockaddr_un addr;
+ char * socket_path = UNIX_PATH;
+
+ updown_data_t * data = malloc(sizeof(updown_data_t));
+ if (!data)
+ goto ERR_MALLOC;
+ interface->data = data;
+
+ data->fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (data->fd == -1) {
+ perror("socket error");
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ if (*socket_path == '\0') {
+ *addr.sun_path = '\0';
+ strncpy(addr.sun_path+1, socket_path+1, sizeof(addr.sun_path)-2);
+ } else {
+ strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);
+ }
+
+ if (connect(data->fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
+ perror("connect error");
+ return -1;
+ }
+
+ return data->fd;
+
+ERR_MALLOC:
+ return -1;
+}
+
+int updown_finalize(interface_t * interface)
+{
+ updown_data_t * data = (updown_data_t*)interface->data;
+
+ if (data->fd > 0)
+ close(data->fd);
+
+ return 0;
+}
+
+int updown_callback(interface_t * interface)
+{
+ updown_data_t * data = (updown_data_t*)interface->data;
+ char buf[100];
+ int rc;
+
+ rc = read(data->fd, buf, sizeof(buf));
+ if (rc < 0)
+ return -1;
+
+ /*
+ * If the process is paused (eg. in a debugger, we might have more than one
+ * read.
+ * XXX how big is the buffer
+ * XXX shall we drain the queue if it exceeds buffer size ?
+ */
+ //assert(rc == 1);
+
+ /* Raise facelet update event */
+ facelet_t * facelet = facelet_create();
+ facelet_set_netdevice_type(facelet, NETDEVICE_TYPE_WIFI); //CELLULAR);
+ facelet_set_status(facelet, FACELET_STATUS_CLEAN);
+ switch(buf[0]) {
+ case '\0':
+ facelet_set_admin_state(facelet, FACE_STATE_DOWN);
+ break;
+ case '\1':
+ facelet_set_admin_state(facelet, FACE_STATE_UP);
+ break;
+ break;
+ default:
+ ERROR("Invalid data received from updown server. Ignoring...");
+ facelet_free(facelet);
+ return -1;
+ }
+
+ facelet_set_event(facelet, FACELET_EVENT_UPDATE);
+
+ facelet_raise_event(facelet, interface);
+
+ return 0;
+}
+
+interface_ops_t updown_ops = {
+ .type = "updown",
+ .initialize = updown_initialize,
+ .finalize = updown_finalize,
+ .callback = updown_callback,
+};