aboutsummaryrefslogtreecommitdiffstats
path: root/ctrl/facemgr/src/interfaces
diff options
context:
space:
mode:
authorJordan Augé <jordan.auge+fdio@cisco.com>2019-07-26 23:20:30 +0200
committerMauro Sardara <msardara@cisco.com>2019-07-29 17:13:35 +0200
commit0a1c6b5565e20167d1f1f33a5a8b597f420b18b0 (patch)
tree98c5da8f231fbd3dc2ce6502040e29c8333d9ffc /ctrl/facemgr/src/interfaces
parent05ca0aa8f612ee48fb66d4dbebe596b7f1e03181 (diff)
[HICN-252] Add per-application policy framework to hicn-light forwarder
Change-Id: I0531cd7a7de179581295ae34766c81cd9cf3e172 Signed-off-by: Jordan Augé <jordan.auge+fdio@cisco.com> Signed-off-by: Mauro Sardara <msardara@cisco.com> Co-authored-by: Mauro Sardara <msardara@cisco.com>
Diffstat (limited to 'ctrl/facemgr/src/interfaces')
-rw-r--r--ctrl/facemgr/src/interfaces/CMakeLists.txt36
-rw-r--r--ctrl/facemgr/src/interfaces/dummy/CMakeLists.txt30
-rw-r--r--ctrl/facemgr/src/interfaces/dummy/dummy.c49
-rw-r--r--ctrl/facemgr/src/interfaces/hicn_light/CMakeLists.txt33
-rw-r--r--ctrl/facemgr/src/interfaces/hicn_light/hicn_light.c222
-rw-r--r--ctrl/facemgr/src/interfaces/netlink/CMakeLists.txt30
-rw-r--r--ctrl/facemgr/src/interfaces/netlink/netlink.c254
-rw-r--r--ctrl/facemgr/src/interfaces/network_framework/CMakeLists.txt36
-rw-r--r--ctrl/facemgr/src/interfaces/network_framework/network_framework.c581
9 files changed, 1271 insertions, 0 deletions
diff --git a/ctrl/facemgr/src/interfaces/CMakeLists.txt b/ctrl/facemgr/src/interfaces/CMakeLists.txt
new file mode 100644
index 000000000..e5a26177a
--- /dev/null
+++ b/ctrl/facemgr/src/interfaces/CMakeLists.txt
@@ -0,0 +1,36 @@
+# 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)
+list(APPEND SOURCE_FILES)
+list(APPEND INCLUDE_DIRS)
+list(APPEND LIBRARIES)
+
+add_subdirectory(hicn_light)
+
+if(APPLE)
+add_subdirectory(network_framework)
+endif()
+
+if(LINUX)
+add_subdirectory(netlink)
+endif()
+
+if(false)
+add_subdirectory(dummy)
+endif()
+
+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/dummy/CMakeLists.txt b/ctrl/facemgr/src/interfaces/dummy/CMakeLists.txt
new file mode 100644
index 000000000..1af3b4b2a
--- /dev/null
+++ b/ctrl/facemgr/src/interfaces/dummy/CMakeLists.txt
@@ -0,0 +1,30 @@
+# 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
+)
+
+list(APPEND SOURCE_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/dummy.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/dummy/dummy.c b/ctrl/facemgr/src/interfaces/dummy/dummy.c
new file mode 100644
index 000000000..b0c558388
--- /dev/null
+++ b/ctrl/facemgr/src/interfaces/dummy/dummy.c
@@ -0,0 +1,49 @@
+/*
+ * 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.c
+ * \brief Implementation of Dummy interface
+ */
+
+#include <stdlib.h>
+
+#include "../../interface.h"
+#include "../../common.h"
+#include "../../event.h"
+#include "../../face.h"
+#include "../../facemgr.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;
+}
+
+int dummy_finalize(interface_t * interface) {
+ return FACEMGR_SUCCESS;
+}
+
+interface_ops_t dummy_ops = {
+ .type = "dummy",
+ .is_singleton = true,
+ .initialize = dummy_initialize,
+ .finalize = dummy_finalize,
+ .on_event = NULL,
+};
diff --git a/ctrl/facemgr/src/interfaces/hicn_light/CMakeLists.txt b/ctrl/facemgr/src/interfaces/hicn_light/CMakeLists.txt
new file mode 100644
index 000000000..ef839a69c
--- /dev/null
+++ b/ctrl/facemgr/src/interfaces/hicn_light/CMakeLists.txt
@@ -0,0 +1,33 @@
+# 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
+)
+
+list(APPEND SOURCE_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light.c
+)
+
+list(APPEND LIBRARIES
+ ${HICNCTRL_LIBRARIES}
+)
+
+
+list(APPEND INCLUDE_DIRS
+ ${HICNCTRL_INCLUDE_DIR}
+)
+
+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/hicn_light/hicn_light.c b/ctrl/facemgr/src/interfaces/hicn_light/hicn_light.c
new file mode 100644
index 000000000..85694573d
--- /dev/null
+++ b/ctrl/facemgr/src/interfaces/hicn_light/hicn_light.c
@@ -0,0 +1,222 @@
+/*
+ * 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/hicn_light/hicn_light.c
+ * \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 "../../facemgr.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 struct {
+ hc_sock_t * s;
+ bool busy;
+} hl_data_t;
+
+int hl_initialize(interface_t * interface, face_rules_t * rules, void ** pdata)
+{
+ hl_data_t * data = malloc(sizeof(hl_data_t));
+ if (!data) {
+ ERROR("[hicn_light] Out of memory!");
+ goto ERR_MALLOC;
+ }
+
+ data->s = hc_sock_create();
+ if (data->s <= 0) {
+ ERROR("[hicn_light] Could not create control socket");
+ goto ERR_SOCK;
+ }
+
+ if (hc_sock_connect(data->s) < 0) {
+ ERROR("[hicn_light] Could not connect control socket");
+ goto ERR_CONNECT;
+ }
+
+ data->busy = false;
+
+ *pdata = data;
+
+ return FACEMGR_SUCCESS;
+
+ERR_CONNECT:
+ hc_sock_free(data->s);
+ERR_SOCK:
+ free(data);
+ERR_MALLOC:
+ return FACEMGR_FAILURE;
+}
+
+int hl_finalize(interface_t * interface)
+{
+ //hc_data_t * data = interface->data;
+ //hc_sock_close(data->s);
+ return FACEMGR_SUCCESS;
+}
+
+int hl_on_event(interface_t * interface, const event_t * event)
+{
+ hc_face_t 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;
+ }
+
+ switch(event->type) {
+ case EVENT_TYPE_CREATE:
+
+ /* Create face */
+ face.face = *event->face;
+ rc = hc_face_create(data->s, &face);
+ if (rc < 0) {
+ ERROR("Failed to create face\n");
+ goto ERR;
+ }
+ DEBUG("Created face id=%d\n", face.id);
+
+#if 0
+ /* Add default route v4 */
+ route = (hc_route_t) {
+ .face_id = face.id,
+ .family = AF_INET,
+ .remote_addr = IPV4_ANY,
+ .len = 0,
+ .cost = DEFAULT_ROUTE_COST,
+
+ };
+ if (hc_route_create(data->s, &route) < 0) {
+ ERROR("Failed to create default hICN/IPv4 route");
+ goto ERR;
+ }
+ INFO("Successfully created default hICN/IPv4 route.");
+#endif
+
+#if 0
+ route = (hc_route_t) {
+ .face_id = face.id,
+ .family = AF_INET6,
+ .remote_addr = IPV6_ANY,
+ .len = 0,
+ .cost = DEFAULT_ROUTE_COST,
+ };
+ if (hc_route_create(data->s, &route) < 0) {
+ ERROR("Failed to create default hICN/IPv6 route");
+ goto ERR;
+ }
+#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;
+ }
+
+ 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");
+ goto ERR;
+ }
+ if (hc_route_create(data->s, &route) < 0) {
+ ERROR("Failed to create hICN/IPv6 route");
+ goto ERR;
+ }
+
+ } 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");
+ goto ERR;
+ }
+ if (hc_route_create(data->s, &route) < 0) {
+ ERROR("Failed to create hICN/IPv6 route");
+ goto ERR;
+ }
+ }
+#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]);
+ /* Unsupported events */
+ goto ERR;
+ }
+
+ return FACEMGR_SUCCESS;
+
+ERR:
+ return FACEMGR_FAILURE;
+}
+
+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/CMakeLists.txt b/ctrl/facemgr/src/interfaces/netlink/CMakeLists.txt
new file mode 100644
index 000000000..7f44d87fe
--- /dev/null
+++ b/ctrl/facemgr/src/interfaces/netlink/CMakeLists.txt
@@ -0,0 +1,30 @@
+# 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
+)
+
+list(APPEND SOURCE_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/netlink.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/netlink/netlink.c b/ctrl/facemgr/src/interfaces/netlink/netlink.c
new file mode 100644
index 000000000..5bf0baf9f
--- /dev/null
+++ b/ctrl/facemgr/src/interfaces/netlink/netlink.c
@@ -0,0 +1,254 @@
+/*
+ * 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/netlink/netlink.c
+ * \brief Netlink interface
+ */
+
+#include <linux/rtnetlink.h>
+#include <sys/types.h> // getpid
+#include <unistd.h> // getpid
+
+#include "../../event.h"
+#include "../../facemgr.h"
+#include "../../interface.h"
+
+/* Internal data storage */
+typedef struct {
+ int fd;
+} nl_data_t;
+
+// little helper to parsing message using netlink macroses
+void parseRtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+
+ while (RTA_OK(rta, len)) { // while not end of the message
+ if (rta->rta_type <= max) {
+ tb[rta->rta_type] = rta; // read attr
+ }
+ rta = RTA_NEXT(rta,len); // get next attr
+ }
+}
+
+
+int nl_initialize(interface_t * interface, face_rules_t * rules, void ** pdata)
+{
+ nl_data_t * data = malloc(sizeof(nl_data_t));
+ if (!data)
+ goto ERR_MALLOC;
+
+ data->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (data->fd < 0) {
+ printf("Failed to create netlink socket: %s\n", (char*)strerror(errno));
+ goto ERR_SOCKET;
+ }
+
+ 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
+ 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 */
+
+
+ *pdata = data;
+ return data->fd; // FACEMGR_SUCCESS;
+
+ERR_BIND:
+ close(data->fd);
+ERR_SOCKET:
+ free(data);
+ERR_MALLOC:
+ *pdata = NULL;
+ return FACEMGR_FAILURE;
+}
+
+int nl_callback(interface_t * interface)
+{
+ nl_data_t * data = (nl_data_t*)interface->data;
+
+ struct sockaddr_nl local; // local addr struct
+ memset(&local, 0, sizeof(local));
+
+ char buf[8192]; // message buffer
+ struct iovec iov; // message structure
+ iov.iov_base = buf; // set message buffer as io
+ 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
+ }
+
+ ssize_t status = recvmsg(data->fd, &msg, 0);
+
+ // check status
+ if (status < 0) {
+/*
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+*/
+
+ printf("Failed to read netlink: %s", (char*)strerror(errno));
+ return FACEMGR_FAILURE;
+ }
+
+ if (msg.msg_namelen != sizeof(local)) { // check message length, just in case
+ printf("Invalid length of the sender address struct\n");
+ return FACEMGR_FAILURE;
+ }
+
+ // message parser
+ struct nlmsghdr *h;
+
+ 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";
+ }
+
+ if (ifi->ifi_flags & IFF_RUNNING) { // get RUNNING flag of the network interface
+ ifRunn = (char*)"RUNNING";
+ } else {
+ ifRunn = (char*)"NOT RUNNING";
+ }
+
+ char ifAddress[256] = {0}; // network addr
+ struct ifaddrmsg *ifa; // structure for network interface data
+ struct rtattr *tba[IFA_MAX+1];
+
+ ifa = (struct ifaddrmsg*)NLMSG_DATA(h); // get data from the network interface
+
+ parseRtattr(tba, IFA_MAX, IFA_RTA(ifa), h->nlmsg_len);
+
+ if (tba[IFA_LOCAL]) {
+ inet_ntop(AF_INET, RTA_DATA(tba[IFA_LOCAL]), ifAddress, sizeof(ifAddress)); // get IP addr
+ }
+
+ 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;
+ }
+ face = face_create_udp(&local_addr, 0, &IP_ADDRESS_EMPTY, 0, ifa->ifa_family);
+ } else {
+ face = NULL;
+ }
+
+ 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_DELLINK:
+ printf("Network interface %s was removed\n", ifName);
+ 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);
+ }
+ 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;
+ }
+ }
+
+ status -= NLMSG_ALIGN(len); // align offsets by the message length, this is important
+
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); // get next message
+ }
+
+ return FACEMGR_SUCCESS;
+}
+
+int nl_finalize(interface_t * interface)
+{
+ nl_data_t * data = (nl_data_t*)interface->data;
+ close(data->fd);
+ return FACEMGR_SUCCESS;
+
+}
+
+const interface_ops_t netlink_ops = {
+ .type = "netlink",
+ .is_singleton = true,
+ .initialize = nl_initialize,
+ .callback = nl_callback,
+ .finalize = nl_finalize,
+ .on_event = NULL,
+};
diff --git a/ctrl/facemgr/src/interfaces/network_framework/CMakeLists.txt b/ctrl/facemgr/src/interfaces/network_framework/CMakeLists.txt
new file mode 100644
index 000000000..ca6659342
--- /dev/null
+++ b/ctrl/facemgr/src/interfaces/network_framework/CMakeLists.txt
@@ -0,0 +1,36 @@
+# 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.
+
+find_library(NETWORK_LIBRARY Network)
+if (NOT NETWORK_LIBRARY)
+ message(FATAL_ERROR "NetworkFramework not found")
+endif()
+
+list(APPEND HEADER_FILES
+)
+
+list(APPEND SOURCE_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/network_framework.c
+)
+
+list(APPEND INCLUDE_DIRS
+)
+
+list(APPEND LIBRARIES
+ ${NETWORK_LIBRARY}
+)
+
+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/network_framework/network_framework.c b/ctrl/facemgr/src/interfaces/network_framework/network_framework.c
new file mode 100644
index 000000000..8a33129b4
--- /dev/null
+++ b/ctrl/facemgr/src/interfaces/network_framework/network_framework.c
@@ -0,0 +1,581 @@
+/*
+ * 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.c
+ * \brief Implementation of Network framework interface
+ */
+
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <Network/Network.h>
+#include <err.h>
+
+#include "../../common.h"
+#include "../../event.h"
+#include "../../face.h"
+#include "../../facemgr.h"
+#include "../../interface.h"
+#include "../../util/map.h"
+#include "../../util/token.h"
+#include "../../util/log.h"
+
+/*
+ * Bonjour service discovery for hICN forwarder
+ *
+ * Possible values for BONJOUR_PROTOCOL:
+ * udp (default) : avoid potential handshake during connection setup.
+ * tcp
+ *
+ * Service advertisement / discovery on MacOSX
+ *
+ * dns-sd -R hicn _hicn._tcp local 9695
+ * dns-sd -R hicn _hicn._udp local 9695
+ *
+ * dns-sd -B _hicn._tcp local
+ * dns-sd -B _hicn._udp local
+ *
+ * Service discovery on Linux (various useful commandline arguments):
+ *
+ * avahi-browse -pt _hicn._udp
+ * avahi-browse -rp _hicn._tcp
+ */
+
+#define BONJOUR_PROTOCOL udp
+#define BONJOUR_SERVICE_DOMAIN "local"
+#define BONJOUR_SERVICE_NAME "hicn"
+
+/* Generated variables */
+#define BONJOUR_SERVICE_TYPE "_hicn._" STRINGIZE(BONJOUR_PROTOCOL)
+#define BONJOUR_PROTOCOL_NAME STRINGIZE(BONJOUR_PROTOCOL)
+#define nw_parameters_create_fn PPCAT(nw_parameters_create_secure_, BONJOUR_PROTOCOL)
+
+#define DEFAULT_PORT 9695
+
+typedef enum {
+ INTERFACE_TYPE_OTHER,
+ INTERFACE_TYPE_WIFI,
+ INTERFACE_TYPE_CELLULAR,
+ INTERFACE_TYPE_WIRED,
+ INTERFACE_TYPE_LOOPBACK,
+} _nw_interface_type_t;
+
+const char * interface_type_str[] = {
+ "OTHER", "WIFI", "CELLULAR", "WIRED", "LOOPBACK",
+};
+
+#if 0
+typedef enum {
+ PATH_STATUS_INVALID,
+ PATH_STATUS_SATISTIED,
+ PATH_STATUS_UNSATISFIED,
+ PATH_STATUS_SATISFIABLE,
+} _nw_path_status_t;
+#endif
+
+const char * path_status_str[] = {
+ "INVALID", "SATISFIED", "UNSATISFIED", "SATISFIABLE",
+};
+
+const char * endpoint_type_str[] = {
+ "INVALID", "ADDRESS", "HOST", "BONJOUR",
+};
+
+const char * connection_state_str[] = {
+ "INVALID", "WAITING", "PREPARING", "READY", "FAILED", "CANCELLED",
+};
+
+int
+cmp_iface(const nw_interface_t iface1, const nw_interface_t iface2)
+{
+ return INT_CMP(nw_interface_get_index(iface1), nw_interface_get_index(iface2));
+}
+
+//TYPEDEF_MAP(map_cnx, nw_interface_t, nw_connection_t, cmp_iface);
+
+typedef struct {
+ face_rules_t * rules; /**< Face creation rules */
+ nw_path_monitor_t pm; /**< Main path monitor */
+// map_cnx_t map_cnx; /**< Map: interface -> connection for face status */
+} nf_data_t;
+
+void
+dump_interface(nw_interface_t interface, int indent)
+{
+ uint32_t index = nw_interface_get_index(interface);
+ const char * name = nw_interface_get_name(interface);
+ nw_interface_type_t type = nw_interface_get_type(interface);
+
+ printfi(indent+1, "%d: %s [%s]\n", index, name, interface_type_str[type]);
+}
+
+void
+dump_endpoint(nw_endpoint_t endpoint, int indent)
+{
+ if (!endpoint) {
+ printfi(indent, "N/A\n");
+ return;
+ }
+
+ nw_endpoint_type_t endpoint_type = nw_endpoint_get_type(endpoint);
+ const char * hostname = nw_endpoint_get_hostname(endpoint);
+ short port = nw_endpoint_get_port(endpoint);
+ const struct sockaddr * address = nw_endpoint_get_address(endpoint);
+
+ printfi(indent, "Type: %s\n", endpoint_type_str[endpoint_type]);
+ printfi(indent, "Hostname: %s\n", hostname);
+ printfi(indent, "Port: %d\n", port);
+
+ if (address) {
+ char *s = NULL;
+ switch(address->sa_family) {
+ case AF_INET: {
+ struct sockaddr_in *addr_in = (struct sockaddr_in *)address;
+ s = malloc(INET_ADDRSTRLEN);
+ inet_ntop(AF_INET, &(addr_in->sin_addr), s, INET_ADDRSTRLEN);
+ break;
+ }
+ case AF_INET6: {
+ struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)address;
+ s = malloc(INET6_ADDRSTRLEN);
+ inet_ntop(AF_INET6, &(addr_in6->sin6_addr), s, INET6_ADDRSTRLEN);
+ break;
+ }
+ default:
+ break;
+ }
+ printfi(indent, "IP address: %s\n", s);
+ free(s);
+ }
+}
+
+void
+dump_path(nw_path_t path, int indent)
+{
+ /* nw_path_enumerate_interfaces : not interesting */
+ nw_path_status_t path_status = nw_path_get_status(path);
+ printfi(indent, "Status: %s\n", path_status_str[path_status]);
+ printfi(indent, "Expensive: %s\n", nw_path_is_expensive(path) ? "true" : "false");
+ printfi(indent, "IPv4 enabled: %s\n", nw_path_has_ipv4(path) ? "true" : "false");
+ printfi(indent, "IPv6 enabled: %s\n", nw_path_has_ipv6(path) ? "true" : "false");
+ printfi(indent, "DNS: %s\n", nw_path_has_dns(path) ? "true" : "false");
+ printfi(indent, "Interfaces:\n");
+ nw_path_enumerate_interfaces(path, (nw_path_enumerate_interfaces_block_t)^(nw_interface_t interface) {
+ dump_interface(interface, indent+1);
+ return true;
+ });
+
+ nw_endpoint_t local = nw_path_copy_effective_local_endpoint(path);
+ printfi(indent, "Effective local endpoint:\n");
+ dump_endpoint(local, indent+1);
+ nw_release(local);
+
+ nw_endpoint_t remote = nw_path_copy_effective_remote_endpoint(path);
+ printfi(indent, "Effective remote endpoint:\n");
+ dump_endpoint(remote, indent+1);
+ nw_release(remote);
+}
+
+void
+dump_connection(nw_connection_t connection, int indent)
+{
+ nw_endpoint_t remote = nw_connection_copy_endpoint(connection);
+ nw_path_t path = nw_connection_copy_current_path(connection);
+
+ printfi(indent, "Remote endpoint:\n");
+ dump_endpoint(remote, indent+1);
+ printfi(indent, "Path:\n");
+ dump_path(path, indent+1);
+
+ /*
+ nw_connection_copy_protocol_metadata();
+ nw_connection_get_maximum_datagram_size();
+ */
+
+ nw_release(remote);
+ nw_release(path);
+}
+
+face_t *
+face_create_from_connection(nw_connection_t connection, face_rules_t * rules)
+{
+ face_t * face;
+ struct sockaddr_in * sin;
+ struct sockaddr_in6 * sin6;
+
+ 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);
+
+ assert (local_addr->sa_family == remote_addr->sa_family);
+ switch(local_addr->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);
+ 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);
+ break;
+ default:
+ ERROR("Unsupported address family: %d\n", local_addr->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;
+
+ char tags[MAXSZ_POLICY_TAGS];
+ policy_tags_snprintf(tags, MAXSZ_POLICY_TAGS, face->tags);
+ }
+
+ switch(type) {
+ case INTERFACE_TYPE_OTHER:
+ policy_tags_add(&tags, POLICY_TAG_WIFI);
+ policy_tags_add(&tags, POLICY_TAG_TRUSTED);
+ 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);
+ break;
+ case INTERFACE_TYPE_CELLULAR:
+ policy_tags_add(&tags, POLICY_TAG_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);
+ break;
+ case INTERFACE_TYPE_LOOPBACK:
+ tags = POLICY_TAGS_EMPTY;
+ break;
+ default:
+ break;
+
+ }
+
+SET_TAGS:
+ face_set_tags(face, tags);
+
+ nw_release(local);
+ nw_release(remote);
+ nw_release(path);
+
+ return face;
+}
+
+void
+on_connection_state_event(interface_t * interface, nw_interface_t iface,
+ nw_connection_t cnx, nw_connection_state_t state, nw_error_t error)
+{
+#if 0
+ DEBUG("Connection [new state = %s]:\n", connection_state_str[state]);
+ nw_path_t path = nw_connection_copy_current_path(cnx);
+ nw_path_enumerate_interfaces(path, (nw_path_enumerate_interfaces_block_t)^(nw_interface_t interface) {
+ const char * name = nw_interface_get_name(interface);
+ printf("NAME=%s\n", name);
+ return true;
+ });
+#endif
+
+ /* We should get enough information to create the face and set if up
+ * asap */
+
+ nw_endpoint_t remote = nw_connection_copy_endpoint(cnx);
+ errno = error ? nw_error_get_error_code(error) : 0;
+
+ switch(state) {
+ case nw_connection_state_waiting:
+ warn("connect to %s port %u (%s) failed, is waiting",
+ nw_endpoint_get_hostname(remote),
+ nw_endpoint_get_port(remote),
+ BONJOUR_PROTOCOL_NAME);
+ break;
+
+ case nw_connection_state_preparing:
+ break;
+
+ case nw_connection_state_ready:
+ {
+#if 0
+ WITH_DEBUG({
+ 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);
+ break;
+ }
+ case nw_connection_state_failed:
+ /* Can we fail with bonjour, or are we always waiting ? */
+ warn("connect to %s port %u (%s) failed",
+ nw_endpoint_get_hostname(remote),
+ nw_endpoint_get_port(remote),
+ BONJOUR_PROTOCOL_NAME);
+ break;
+
+ case nw_connection_state_cancelled:
+ // Release the primary reference on the connection
+ // that was taken at creation time
+ nw_release(cnx);
+ break;
+
+ default: /* nw_connection_state_invalid */
+ /* Should never be called */
+ break;
+
+ }
+
+ nw_release(remote);
+
+}
+
+void
+on_connection_path_event(interface_t * interface, nw_interface_t iface,
+ nw_connection_t cnx, nw_path_t path)
+{
+#if 0
+ DEBUG("Connection [path changed]:\n");
+ WITH_DEBUG({
+ //dump_connection(cnx, 1);
+ });
+#endif
+ /* redundant *//*
+ DEBUG(1, "Path:\n");
+ dump_path(path, 2);
+ */
+}
+
+/**
+ * Enumerate main path interfaces
+ *
+ * We need to create specific dummy connections for each newly discovered
+ * interface
+ *
+ * Currently we only use Bonjour/TCP for remote hICN discovery and connection
+ * path monitoring.
+ */
+void on_interface_event(interface_t * interface, nw_interface_t iface)
+{
+ /* We can create an hICN face on this interface that will be down until
+ * connected
+ * It is however possible to have two default gateways on the same
+ * interface, or more, or even zero. Somehow we need a strategy, timers, etc
+ * to properly do the job.
+ *
+ * We have to determine:
+ * - how many faces to build
+ * - the face type : hICN, tunnel (TCP/UDP)
+ * - the underlying protocol : v4, v6
+ *
+ * This depends on the configuration, end host and network capabilities.
+ *
+ * We can rely on several types of discovery:
+ * - DHCP
+ * - Bonjour
+ * - ...
+ *
+ * So far:
+ * - bonjour discovery attempt, we expect to discover one hICN interface
+ * (how bonjour works with more than one is unclear), after a certain
+ * time, if none is discovered, we cannot do any tunnel face.
+ */
+
+ nw_endpoint_t endpoint;
+
+ endpoint = nw_endpoint_create_bonjour_service(
+ BONJOUR_SERVICE_NAME,
+ BONJOUR_SERVICE_TYPE,
+ BONJOUR_SERVICE_DOMAIN);
+
+ if (!endpoint)
+ goto ERR;
+
+ /* nw_parameters_create_secure_{udp,tcp} */
+ nw_parameters_t parameters = nw_parameters_create_fn(
+ NW_PARAMETERS_DISABLE_PROTOCOL, /* no (d)tls */
+ NW_PARAMETERS_DEFAULT_CONFIGURATION /* default udp/tcp */);
+
+ if (!parameters)
+ goto ERR;
+
+ nw_parameters_require_interface(parameters, iface);
+ nw_parameters_set_reuse_local_address(parameters, true);
+
+ nw_connection_t connection = nw_connection_create(endpoint, parameters);
+ if (!connection)
+ goto ERR;
+
+ nw_release(endpoint);
+ nw_release(parameters);
+
+ /* Remember not to recreate connection */
+ // XXX TODO
+
+ /* Setup connection handlers */
+
+ nw_connection_set_state_changed_handler(connection, ^(nw_connection_state_t state, nw_error_t error) {
+ on_connection_state_event(interface, iface, connection, state, error);
+ });
+
+ nw_connection_set_path_changed_handler(connection, ^(nw_path_t path) {
+ on_connection_path_event(interface, iface, connection, path);
+ });
+
+ nw_connection_set_better_path_available_handler(connection, ^(bool value) {
+#if 0
+ DEBUG("Connection [better path = %s]\n", (value ? "true" : "false"));
+ WITH_DEBUG({
+ dump_connection(connection, 1);
+ });
+#endif
+ });
+
+ nw_connection_set_viability_changed_handler(connection, ^(bool value) {
+#if 0
+ DEBUG("Connection [viable = %s]\n", (value ? "true" : "false"));
+ WITH_DEBUG({
+ //dump_connection(connection, 1);
+ });
+#endif
+
+ /*
+ * 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);
+ }
+
+ });
+
+ nw_connection_start(connection);
+
+ nw_connection_set_queue(connection, dispatch_get_main_queue());
+ nw_retain(connection); // Hold a reference until cancelled
+
+#if 0
+ DEBUG("Created Bonjour cnx on interface:\n");
+ WITH_DEBUG({
+ dump_interface(iface, 1);
+ });
+#endif
+
+ERR:
+ return;
+}
+
+void on_path_event(interface_t * interface, nw_path_t path)
+{
+ /* Simplification: we handle path event only once.
+ * Ideally, test whether we discover new interfaces or not
+ */
+#if 0
+ DEBUG("Path [event]:\n");
+ WITH_DEBUG({
+ dump_path(path, 1);
+ });
+#endif
+
+ nw_path_enumerate_interfaces(path, (nw_path_enumerate_interfaces_block_t)^(nw_interface_t iface) {
+ on_interface_event(interface, iface);
+ return true;
+ });
+
+}
+
+int nf_initialize(interface_t * interface, face_rules_t * rules, void ** pdata)
+{
+ nf_data_t * data = malloc(sizeof(nf_data_t));
+ if (!data)
+ goto ERR_MALLOC;
+
+ data->rules = rules;
+
+ data->pm = nw_path_monitor_create();
+ if (!data->pm)
+ goto ERR_PM;
+
+ nw_path_monitor_set_queue(data->pm, dispatch_get_main_queue());
+ nw_path_monitor_set_cancel_handler(data->pm, ^() { });
+ nw_path_monitor_set_update_handler(data->pm, ^(nw_path_t path) {
+ on_path_event(interface, path);
+ });
+
+ // XXX NEEDED ?
+ nw_retain(data->pm);
+
+ DEBUG("Starting network path monitor");
+ nw_path_monitor_start(data->pm);
+
+ *pdata = data;
+ return FACEMGR_SUCCESS;
+
+ERR_PM:
+ free(data);
+ERR_MALLOC:
+ *pdata = NULL;
+ return FACEMGR_FAILURE;
+}
+
+int nf_finalize(interface_t * interface)
+{
+ nf_data_t * data = (nf_data_t*)interface->data;
+ if (data->pm) {
+ nw_path_monitor_cancel(data->pm);
+ data->pm = NULL;
+ }
+ return FACEMGR_SUCCESS;
+}
+
+const interface_ops_t network_framework_ops = {
+ .type = "network_framework",
+ .is_singleton = true,
+ .initialize = nf_initialize,
+ .finalize = nf_finalize,
+ .on_event = NULL,
+};