summaryrefslogtreecommitdiffstats
path: root/ctrl/facemgr/src/facelet.c
diff options
context:
space:
mode:
Diffstat (limited to 'ctrl/facemgr/src/facelet.c')
-rw-r--r--ctrl/facemgr/src/facelet.c1008
1 files changed, 1008 insertions, 0 deletions
diff --git a/ctrl/facemgr/src/facelet.c b/ctrl/facemgr/src/facelet.c
new file mode 100644
index 000000000..8a3074d2a
--- /dev/null
+++ b/ctrl/facemgr/src/facelet.c
@@ -0,0 +1,1008 @@
+/*
+ * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file facelet.c
+ * \brief Implementation of facelet
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <hicn/ctrl/face.h>
+#include <hicn/facemgr/cfg.h>
+#include <hicn/util/log.h>
+
+#include "facelet.h"
+
+const char * face_type_layer_str[] = {
+#define _(x) [FACE_TYPE_LAYER_ ## x] = STRINGIZE(x),
+ foreach_face_type_layer
+#undef _
+};
+
+const char * face_type_encap_str[] = {
+#define _(x) [FACE_TYPE_ENCAP_ ## x] = STRINGIZE(x),
+ foreach_face_type_encap
+#undef _
+};
+
+#define FACEMGR_FACE_TYPE_STR(x) \
+ face_type_layer_str[x.layer], face_type_encap_str[x.encap]
+
+
+const char * facelet_status_str[] = {
+#define _(x) [FACELET_STATUS_ ## x] = STRINGIZE(x),
+ foreach_facelet_status
+#undef _
+};
+
+/* Facelet attribute status */
+
+
+const char * facelet_attr_status_str[] = {
+#define _(x, str) [FACELET_ATTR_STATUS_ ## x] = STRINGIZE(x),
+ foreach_facelet_attr_status
+#undef _
+};
+
+const char * facelet_attr_status_str_short[] = {
+#define _(x, str) [FACELET_ATTR_STATUS_ ## x] = STRINGIZE(str),
+ foreach_facelet_attr_status
+#undef _
+};
+
+
+/* Facelet */
+
+struct facelet_s {
+#define _(TYPE, NAME) TYPE NAME;
+ foreach_facelet_attr
+#undef _
+#define _(TYPE, NAME) facelet_attr_status_t NAME ## _status;
+ foreach_facelet_attr
+#undef _
+
+ facelet_status_t status;
+ facelet_event_t event;
+
+ /* Joins */
+ bool bj_done;
+ bool au_done;
+ int num_pending;
+};
+
+const char * facelet_event_str[] = {
+#define _(x) [FACELET_EVENT_ ## x] = STRINGIZE(x),
+foreach_facelet_event
+#undef _
+};
+
+facelet_t *
+facelet_create()
+{
+ facelet_t * facelet = calloc(1, sizeof(facelet_t));
+ if (!facelet)
+ goto ERR_MALLOC;
+
+ facelet->netdevice_status = FACELET_ATTR_STATUS_UNSET;
+ facelet->netdevice_type_status = FACELET_ATTR_STATUS_UNSET;
+ facelet->family_status = FACELET_ATTR_STATUS_UNSET;
+ facelet->local_addr_status = FACELET_ATTR_STATUS_UNSET;
+ facelet->local_port_status = FACELET_ATTR_STATUS_UNSET;
+ facelet->remote_addr_status = FACELET_ATTR_STATUS_UNSET;
+ facelet->remote_port_status = FACELET_ATTR_STATUS_UNSET;
+ facelet->admin_state_status = FACELET_ATTR_STATUS_UNSET;
+ facelet->state_status = FACELET_ATTR_STATUS_UNSET;
+ facelet->face_type_status = FACELET_ATTR_STATUS_UNSET;
+
+ facelet->status = FACELET_STATUS_NEW;
+
+ facelet->bj_done = false;
+ facelet->au_done = false;
+ facelet->num_pending = 0;
+
+ facelet->event = FACELET_EVENT_UNDEFINED;
+
+ return facelet;
+
+ERR_MALLOC:
+ return NULL;
+}
+
+facelet_t *
+facelet_create_from_netdevice(netdevice_t * netdevice)
+{
+ facelet_t * facelet = facelet_create();
+ if (!facelet)
+ goto ERR_FACELET;
+
+ int rc = facelet_set_netdevice(facelet, *netdevice);
+ if (rc < 0)
+ goto ERR_NETDEV;
+
+ return facelet;
+
+ERR_NETDEV:
+ facelet_free(facelet);
+ERR_FACELET:
+ return NULL;
+}
+
+/**
+ * \brief Validate whether the facelet has all required fields to construct a
+ * face of the given type
+ * \param [in) facelet - Pointer to the facelet to verify
+ * \return 0 in case of success, -1 otherwise
+ */
+int
+facelet_validate_face(const facelet_t * facelet)
+{
+ if (!facelet_has_face_type(facelet))
+ return false;
+ switch(facelet->face_type.layer) {
+ case FACE_TYPE_LAYER_4:
+ if (!facelet_has_remote_port(facelet))
+ return false;
+ if (!facelet_has_remote_addr(facelet))
+ return false;
+ case FACE_TYPE_LAYER_3:
+ if (!facelet_has_local_addr(facelet))
+ return false;
+ if (!facelet_has_netdevice(facelet))
+ return false;
+ return true;
+
+ default:
+ return false; /* Error */
+ }
+ // FIXME Not implemented
+ return 0;
+}
+
+
+netdevice_type_t
+netdevice_type_from_face_tags(const face_t * face)
+{
+ policy_tags_t tags = face->tags;
+ if (policy_tags_has(tags, POLICY_TAG_WIRED))
+ return NETDEVICE_TYPE_WIRED;
+ else if (policy_tags_has(tags, POLICY_TAG_WIFI))
+ return NETDEVICE_TYPE_WIFI;
+ else if (policy_tags_has(tags, POLICY_TAG_CELLULAR))
+ return NETDEVICE_TYPE_CELLULAR;
+ return NETDEVICE_TYPE_UNDEFINED;
+}
+
+facelet_t *
+facelet_create_from_face(face_t * face)
+{
+ facelet_t * facelet = malloc(sizeof(facelet_t));
+ if (!facelet)
+ goto ERR_MALLOC;
+
+ /* Go through the face attributes to update the local representation */
+
+ /* Attribute : netdevice */
+ /* NOTE index is not set */
+ if (IS_VALID_NETDEVICE(face->netdevice)) {
+ facelet->netdevice = face->netdevice;
+ facelet->netdevice_status = FACELET_ATTR_STATUS_CLEAN;
+ } else {
+ facelet->netdevice_status = FACELET_ATTR_STATUS_UNSET;
+ }
+
+ /* Attribute : netdevice_type */
+ facelet->netdevice_type = netdevice_type_from_face_tags(face);
+ if (facelet->netdevice_type != NETDEVICE_TYPE_UNDEFINED) {
+ facelet->netdevice_type_status = FACELET_ATTR_STATUS_CLEAN;
+ } else {
+ facelet->netdevice = NETDEVICE_EMPTY;
+ facelet->netdevice_type_status = FACELET_ATTR_STATUS_UNSET;
+ }
+
+ /* Attribute : family */
+ if (IS_VALID_FAMILY(face->family)) {
+ facelet->family = face->family;
+ facelet->family_status = FACELET_ATTR_STATUS_CLEAN;
+
+ /* Attribute : local_addr */
+ if (ip_address_cmp(&face->local_addr, &IP_ADDRESS_EMPTY, face->family) != 0) {
+ facelet->local_addr = face->local_addr;
+ facelet->local_addr_status = FACELET_ATTR_STATUS_CLEAN;
+ } else {
+ facelet->local_addr_status = FACELET_ATTR_STATUS_UNSET;
+ }
+
+ /* Attribute : local_port */
+ if (IS_VALID_PORT(face->local_port)) {
+ facelet->local_port = face->local_port;
+ facelet->local_port_status = FACELET_ATTR_STATUS_CLEAN;
+ } else {
+ facelet->local_port_status = FACELET_ATTR_STATUS_UNSET;
+ }
+
+ /* Attribute : remote_addr */
+ if (ip_address_cmp(&face->remote_addr, &IP_ADDRESS_EMPTY, face->family) != 0) {
+ facelet->remote_addr = face->remote_addr;
+ facelet->remote_addr_status = FACELET_ATTR_STATUS_CLEAN;
+ } else {
+ facelet->remote_addr_status = FACELET_ATTR_STATUS_UNSET;
+ }
+
+ /* Attribute : remote_port */
+ if (IS_VALID_PORT(face->remote_port)) {
+ facelet->remote_port = face->remote_port;
+ facelet->remote_port_status = FACELET_ATTR_STATUS_CLEAN;
+ } else {
+ facelet->remote_port_status = FACELET_ATTR_STATUS_UNSET;
+ }
+
+ } else {
+ facelet->family_status = FACELET_ATTR_STATUS_UNSET;
+ facelet->local_addr_status = FACELET_ATTR_STATUS_UNSET;
+ facelet->local_port_status = FACELET_ATTR_STATUS_UNSET;
+ facelet->remote_addr_status = FACELET_ATTR_STATUS_UNSET;
+ facelet->remote_port_status = FACELET_ATTR_STATUS_UNSET;
+ }
+
+ /* Attribute : admin_state */
+ if ((face->admin_state == FACE_STATE_UP) ||
+ (face->admin_state == FACE_STATE_DOWN)) {
+ facelet->admin_state = face->admin_state;
+ facelet->admin_state_status = FACELET_ATTR_STATUS_CLEAN;
+ } else {
+ facelet->admin_state_status = FACELET_ATTR_STATUS_UNSET;
+ }
+
+ /* Attribute : state */
+ if ((face->state == FACE_STATE_UP) ||
+ (face->state == FACE_STATE_DOWN)) {
+ facelet->state = face->state;
+ facelet->state_status = FACELET_ATTR_STATUS_CLEAN;
+ } else {
+ facelet->state_status = FACELET_ATTR_STATUS_UNSET;
+ }
+
+ /* Attribute : face_type */
+ if ((face->type != FACE_TYPE_UNDEFINED) && (face->type != FACE_TYPE_N)) {
+ switch(face->type) {
+ case FACE_TYPE_UDP:
+ facelet->face_type = FACEMGR_FACE_TYPE_OVERLAY_UDP;
+ break;
+ case FACE_TYPE_TCP:
+ facelet->face_type = FACEMGR_FACE_TYPE_OVERLAY_TCP;
+ break;
+ case FACE_TYPE_HICN:
+ facelet->face_type = FACEMGR_FACE_TYPE_NATIVE_TCP;
+ break;
+ default:
+ ERROR("[facelet_create_from_face] Face type not (yet) implemented");
+ goto ERR_FACE;
+ }
+ facelet->face_type_status = FACELET_ATTR_STATUS_CLEAN;
+ } else {
+ facelet->face_type_status = FACELET_ATTR_STATUS_UNSET;
+ }
+
+ /* Status */
+ facelet->status = FACELET_STATUS_CLEAN;
+
+ /* TODO Consistency check between face type and found attributes */
+ if (facelet_validate_face(facelet) < 0)
+ goto ERR_FACE;
+
+ facelet->bj_done = false;
+ facelet->au_done = false;
+ facelet->num_pending = 0;
+
+ facelet->event = FACELET_EVENT_UNDEFINED;
+
+ return facelet;
+
+ERR_FACE:
+ free(facelet);
+ERR_MALLOC:
+ return NULL;
+}
+
+
+void
+facelet_free(facelet_t * facelet)
+{
+ free(facelet);
+}
+
+facelet_t *
+facelet_dup(const facelet_t * current_facelet)
+{
+ facelet_t * facelet = facelet_create();
+ if (!facelet)
+ goto ERR_CREATE;
+
+#define _(TYPE, NAME) facelet-> NAME = current_facelet-> NAME;
+ foreach_facelet_attr
+#undef _
+#define _(TYPE, NAME) facelet-> NAME ## _status = current_facelet-> NAME ## _status;
+ foreach_facelet_attr
+#undef _
+
+ facelet->status = current_facelet->status;
+ facelet->event = current_facelet->event;
+
+ facelet->bj_done = current_facelet->bj_done;
+ facelet->au_done = current_facelet->au_done;
+ facelet->num_pending = current_facelet->num_pending;
+
+ return facelet;
+
+ERR_CREATE:
+ return NULL;
+}
+
+int
+facelet_cmp(const facelet_t * f1, const facelet_t * f2)
+{
+ /*
+ * Under the assumption we only create a face per physical interface, a
+ * facelet is uniquely identified by its netdevice attribute, and address
+ * family if any.
+ *
+ * This function is mostly used for lookups into the cache, and the face
+ * thus needs to have a netdevice associated, and optionally, an address
+ * family.
+ *
+ * For other situations, the `facelet_match` function is more appropriate.
+ */
+
+ if ((f1->netdevice_status != FACELET_ATTR_STATUS_UNSET) &&
+ (f2->netdevice_status != FACELET_ATTR_STATUS_UNSET)) {
+ int rc = netdevice_cmp(&f1->netdevice, &f2->netdevice);
+ if (rc != 0)
+ return rc;
+
+ } else {
+ /* Both unset : we might have the face without netdevice due to hicn
+ * light not returning it currently, but we cannot skip it in the match
+ * otherwise we cannot distinguish with other faces except matching on
+ * other fields which might unfortunately not be determined yet...
+ */
+ return (f1->netdevice_status == FACELET_ATTR_STATUS_UNSET) ? -1 : 1;
+ }
+
+ assert(f1->family_status != FACELET_ATTR_STATUS_UNSET);
+ assert(f2->family_status != FACELET_ATTR_STATUS_UNSET);
+
+ if ((f1->family == AF_UNSPEC) || (f2->family == AF_UNSPEC))
+ return 0;
+ int diff = f1->family - f2->family;
+ return (diff > 0) ? 1 :
+ (diff < 0) ? -1 : 0;
+}
+
+/*
+ * If the match has a field set, then the facelet only matches iif it has the
+ * same field set, and both values are equal
+ */
+#define MATCH_ATTRIBUTE(TYPE, NAME) \
+do { \
+ if (facelet_match->NAME ## _status == FACELET_ATTR_STATUS_CLEAN) { \
+ if (facelet_has_ ## NAME(facelet_match)) { \
+ TYPE NAME; \
+ TYPE NAME ## _match; \
+ if (!facelet_has_ ## NAME(facelet)) \
+ return false; \
+ if (facelet_get_ ## NAME (facelet, & NAME) < 0) \
+ return false; \
+ if (facelet_get_ ## NAME (facelet_match, & NAME ## _match) < 0) \
+ return false; \
+ if (memcmp(& NAME, & NAME ## _match, sizeof(NAME)) != 0) \
+ return false; \
+ } \
+ } \
+} while(0)
+
+/* facelet_match is the incoming one */
+bool
+facelet_match(const facelet_t * facelet, const facelet_t * facelet_match)
+{
+#define _(TYPE, NAME) MATCH_ATTRIBUTE(TYPE, NAME);
+ foreach_facelet_attr
+#undef _
+ return true;
+}
+
+bool facelet_has_key(const facelet_t * facelet) {
+ return (facelet_has_netdevice(facelet) && facelet_has_family(facelet));
+}
+
+/*
+ * Implementation note:
+ * - facelet_set_* is equivalent to merge with a CLEAN remote attribute
+ */
+#define FACELET_ACCESSORS(TYPE, NAME) \
+bool \
+facelet_has_ ## NAME(const facelet_t * facelet) \
+{ \
+ assert(facelet); \
+ assert(facelet->NAME ## _status != FACELET_ATTR_STATUS_UNDEFINED); \
+ assert(facelet->NAME ## _status != FACELET_ATTR_STATUS_N); \
+ return ((facelet-> NAME ## _status != FACELET_ATTR_STATUS_UNSET)); \
+} \
+ \
+facelet_attr_status_t \
+facelet_get_ ## NAME ## _status(const facelet_t * facelet) \
+{ \
+ return (facelet->NAME ## _status); \
+} \
+ \
+int \
+facelet_get_ ## NAME(const facelet_t * facelet, TYPE * NAME) \
+{ \
+ assert(facelet); \
+ if (!facelet_has_ ## NAME(facelet)) \
+ return -1; \
+ *NAME = facelet-> NAME; \
+ return 0; \
+} \
+ \
+int \
+facelet_set_local_ ## NAME(facelet_t * facelet, TYPE NAME) \
+{ \
+ assert(facelet); \
+ switch(facelet->NAME ## _status) { \
+ case FACELET_ATTR_STATUS_UNSET: \
+ case FACELET_ATTR_STATUS_CLEAN: \
+ case FACELET_ATTR_STATUS_DIRTY: \
+ case FACELET_ATTR_STATUS_PENDING: \
+ facelet-> NAME = NAME; \
+ facelet->NAME ## _status = FACELET_ATTR_STATUS_DIRTY; \
+ if (facelet->status == FACELET_STATUS_CLEAN) \
+ facelet->status = FACELET_STATUS_DIRTY; \
+ break; \
+ case FACELET_ATTR_STATUS_CONFLICT: \
+ break; \
+ case FACELET_ATTR_STATUS_UNDEFINED: \
+ case FACELET_ATTR_STATUS_N: \
+ ERROR("Unexpected attribute status value"); \
+ return -1; \
+ } \
+ return 0; \
+} \
+ \
+int \
+facelet_set_remote_ ## NAME(facelet_t * facelet, TYPE NAME) \
+{ \
+ assert(facelet); \
+ switch(facelet->NAME ## _status) { \
+ case FACELET_ATTR_STATUS_UNSET: \
+ facelet-> NAME = NAME; \
+ facelet->NAME ## _status = FACELET_ATTR_STATUS_CLEAN; \
+ break; \
+ case FACELET_ATTR_STATUS_CLEAN: \
+ facelet->NAME = NAME; \
+ break; \
+ case FACELET_ATTR_STATUS_DIRTY: \
+ ERROR("Discarded remote value for status reasons"); \
+ break; \
+ case FACELET_ATTR_STATUS_PENDING: \
+ ERROR("Received remote value on pending attribute"); \
+ facelet->NAME ## _status = FACELET_ATTR_STATUS_CONFLICT; \
+ if (facelet->status != FACELET_STATUS_CONFLICT) \
+ facelet->status = FACELET_STATUS_CONFLICT; \
+ break; \
+ case FACELET_ATTR_STATUS_CONFLICT: \
+ return -1; \
+ case FACELET_ATTR_STATUS_UNDEFINED: \
+ case FACELET_ATTR_STATUS_N: \
+ ERROR("Unexpected attribute status value"); \
+ return -1; \
+ } \
+ return 0; \
+} \
+ \
+int \
+facelet_set_ ## NAME(facelet_t * facelet, TYPE NAME) \
+{ \
+ return facelet_set_local_ ## NAME(facelet, NAME); \
+} \
+ \
+
+#define _(TYPE, NAME) FACELET_ACCESSORS(TYPE, NAME)
+foreach_facelet_attr
+#undef _
+
+/*
+ * This function is called for every facelet attribute. It is responsible for
+ * comparing both the current and new value, and set the attribute and facelet
+ * status appropriately.
+ */
+
+// FIXME CLEAN for key fields, dirty for fields to update.
+
+#define MERGE_ATTRIBUTE(TYPE, NAME) \
+do { \
+ switch(facelet_to_merge->NAME ## _status) { \
+ case FACELET_ATTR_STATUS_UNDEFINED: \
+ case FACELET_ATTR_STATUS_N: \
+ case FACELET_ATTR_STATUS_PENDING: \
+ case FACELET_ATTR_STATUS_CONFLICT: \
+ ERROR("Unexpected facelet attribute status"); \
+ return -1; \
+ case FACELET_ATTR_STATUS_UNSET: \
+ break; \
+ case FACELET_ATTR_STATUS_CLEAN: \
+ case FACELET_ATTR_STATUS_DIRTY: \
+ facelet_set_ ## NAME(facelet, facelet_to_merge-> NAME); \
+ break; \
+ } \
+} while (0)
+
+int facelet_merge(facelet_t * facelet, const facelet_t * facelet_to_merge)
+{
+ assert(facelet && facelet_to_merge);
+#define _(TYPE, NAME) MERGE_ATTRIBUTE(TYPE, NAME);
+ foreach_facelet_attr
+#undef _
+ facelet->event = facelet_to_merge->event;
+ return 0;
+}
+
+#define MERGE_ATTRIBUTE_REMOTE(TYPE, NAME) \
+do { \
+ switch(facelet_to_merge->NAME ## _status) { \
+ case FACELET_ATTR_STATUS_UNDEFINED: \
+ case FACELET_ATTR_STATUS_N: \
+ case FACELET_ATTR_STATUS_DIRTY: \
+ case FACELET_ATTR_STATUS_PENDING: \
+ case FACELET_ATTR_STATUS_CONFLICT: \
+ ERROR("Unexpected facelet attribute status"); \
+ return -1; \
+ case FACELET_ATTR_STATUS_UNSET: \
+ break; \
+ case FACELET_ATTR_STATUS_CLEAN: \
+ facelet_set_ ## NAME(facelet, facelet_to_merge-> NAME); \
+ break; \
+ \
+ } \
+} while (0)
+
+int facelet_merge_remote(facelet_t * facelet, const facelet_t * facelet_to_merge)
+{
+ assert(facelet && facelet_to_merge);
+#define _(TYPE, NAME) MERGE_ATTRIBUTE_REMOTE(TYPE, NAME);
+ foreach_facelet_attr
+#undef _
+ facelet->event = facelet_to_merge->event;
+ return 0;
+}
+
+int
+facelet_get_face(const facelet_t * facelet, face_t ** pface)
+{
+ assert(pface);
+
+ /* Facelet has all the required information to create a face */
+ if (facelet_validate_face(facelet) < 0)
+ return 0;
+
+ face_t * face = face_create();
+ if (!face)
+ goto ERR_CREATE;
+
+ assert(facelet_has_netdevice(facelet));
+ face->netdevice = facelet->netdevice;
+
+ /* Face type */
+ switch(facelet->face_type.layer) {
+ case FACE_TYPE_LAYER_4:
+ switch(facelet->face_type.encap) {
+ case FACE_TYPE_ENCAP_UDP:
+ face->type = FACE_TYPE_UDP;
+ break;
+ case FACE_TYPE_ENCAP_TCP:
+ face->type = FACE_TYPE_TCP;
+ break;
+ case FACE_TYPE_ENCAP_UNDEFINED:
+ case FACE_TYPE_ENCAP_N:
+ ERROR("[facelet_get_face] Unsupported face encapsulation");
+ goto ERR;
+ }
+
+ if (facelet_get_family(facelet, &face->family) < 0)
+ goto ERR;
+ if (facelet_get_local_addr(facelet, &face->local_addr) < 0)
+ goto ERR;
+ if (facelet_get_local_port(facelet, &face->local_port) < 0)
+ goto ERR;
+ if (facelet_get_remote_addr(facelet, &face->remote_addr) < 0)
+ goto ERR;
+ if (facelet_get_remote_port(facelet, &face->remote_port) < 0)
+ goto ERR;
+ break;
+
+ case FACE_TYPE_LAYER_3:
+ ERROR("{facelet_get_face] hICN face not (yet) implemented");
+ goto ERR;
+
+ case FACE_TYPE_LAYER_UNDEFINED:
+ case FACE_TYPE_LAYER_N:
+ ERROR("[facelet_get_face] Unsupported face type");
+ goto ERR;
+ }
+
+ if (facelet_has_admin_state(facelet)) {
+ if (facelet_get_admin_state(facelet, &face->admin_state) < 0)
+ goto ERR;
+ } else {
+ face->admin_state = FACE_STATE_UP;
+ }
+
+ if (facelet_has_state(facelet)) {
+ if (facelet_get_state(facelet, &face->state) < 0)
+ goto ERR;
+ } else {
+ face->state = FACE_STATE_UP;
+ }
+
+ /* Tags */
+
+ /* - based on netdevice type */
+ policy_tags_t tags = POLICY_TAGS_EMPTY;
+ if (facelet_has_netdevice_type(facelet)) {
+ netdevice_type_t netdevice_type;
+ if (facelet_get_netdevice_type(facelet, &netdevice_type) < 0) {
+ ERROR("error getting netdevice_type");
+ goto ERR;
+ }
+
+
+ switch(netdevice_type) {
+ case NETDEVICE_TYPE_UNDEFINED:
+ case NETDEVICE_TYPE_LOOPBACK:
+ break;
+ case NETDEVICE_TYPE_WIRED:
+ policy_tags_add(&tags, POLICY_TAG_WIRED);
+ break;
+ case NETDEVICE_TYPE_WIFI:
+ policy_tags_add(&tags, POLICY_TAG_WIFI);
+ break;
+ case NETDEVICE_TYPE_CELLULAR:
+ policy_tags_add(&tags, POLICY_TAG_CELLULAR);
+ break;
+ default:
+ goto ERR;
+ }
+ }
+#ifdef __linux__
+#ifndef __ANDROID__
+ else {
+ /*
+ * Heuristics to determine face type based on name, until a better
+ * solution is found
+ */
+ if (strncmp(facelet->netdevice.name, "eth", 3) == 0) {
+ policy_tags_add(&tags, POLICY_TAG_WIRED);
+ goto DONE;
+ }
+ if (strncmp(facelet->netdevice.name, "en", 2) == 0) {
+ policy_tags_add(&tags, POLICY_TAG_WIRED);
+ goto DONE;
+ }
+ if (strncmp(facelet->netdevice.name, "wl", 2) == 0) {
+ /* wlan* wlp* wlx* */
+ policy_tags_add(&tags, POLICY_TAG_WIFI);
+ goto DONE;
+ }
+
+DONE:
+ ;
+ }
+#endif /* ! __ANDROID__ */
+#endif /* __linux__ */
+ face->tags = tags;
+
+ *pface = face;
+
+ return 0;
+
+ERR:
+ free(face);
+ERR_CREATE:
+ *pface = NULL;
+ return -1;
+}
+
+facelet_status_t
+facelet_get_status(const facelet_t * facelet)
+{
+ return facelet->status;
+}
+
+#define SET_ATTR_STATUS_CLEAN(TYPE, NAME) \
+do { \
+ if (facelet->NAME ## _status == FACELET_ATTR_STATUS_DIRTY) \
+ facelet->NAME ## _status = FACELET_ATTR_STATUS_CLEAN; \
+} while (0)
+
+void
+facelet_set_status(facelet_t * facelet, facelet_status_t status)
+{
+ if (status == FACELET_STATUS_CLEAN) {
+#define _(TYPE, NAME) SET_ATTR_STATUS_CLEAN(TYPE, NAME);
+ foreach_facelet_attr
+#undef _
+ }
+ facelet->status = status;
+}
+
+int
+facelet_add_pending(facelet_t * facelet)
+{
+ assert(facelet);
+ facelet->num_pending++;
+ return 0;
+}
+
+int
+facelet_remove_pending(facelet_t * facelet)
+{
+ assert(facelet);
+ if (facelet->num_pending == 0)
+ return -1;
+ facelet->num_pending--;
+ return 0;
+}
+
+bool
+facelet_has_pending(const facelet_t * facelet)
+{
+ assert(facelet);
+ DEBUG("num pending=%d\n", facelet->num_pending);
+ return (facelet->num_pending > 0);
+}
+
+void
+facelet_set_bj_done(facelet_t * facelet)
+{
+ facelet->bj_done = true;
+}
+
+bool
+facelet_is_bj_done(const facelet_t * facelet)
+{
+ return facelet->bj_done;
+}
+
+void
+facelet_set_au_done(facelet_t * facelet)
+{
+ facelet->au_done = true;
+}
+
+bool
+facelet_is_au_done(const facelet_t * facelet)
+{
+ return facelet->au_done;
+}
+
+facelet_event_t
+facelet_get_event(const facelet_t * facelet)
+{
+ return facelet->event;
+}
+
+void
+facelet_set_event(facelet_t * facelet, facelet_event_t event)
+{
+ facelet->event = event;
+}
+
+int
+facelet_raise_event(facelet_t * facelet, const interface_t * interface)
+{
+ if (interface->callback)
+ interface->callback(interface->callback_data, facelet);
+ return 0;
+}
+
+int
+facelet_snprintf(char * s, size_t size, facelet_t * facelet)
+{
+ char * cur = s;
+ int rc;
+
+ assert(facelet);
+
+ /* Header + key attributes (netdevice + family) */
+ rc = snprintf(cur, s + size - cur, "<Facelet %s (%s) [%d]",
+ // FIXME, better than the event would be the action to be performed next
+ facelet_event_str[facelet->event],
+ (facelet->family == AF_INET) ? "AF_INET" :
+ (facelet->family == AF_INET6) ? "AF_INET6" :
+ (facelet->family == AF_UNSPEC) ? "AF_UNSPEC" :
+ "unknown",
+ facelet->num_pending);
+ if (rc < 0)
+ return rc;
+ cur += rc;
+ if (size != 0 && cur >= s + size)
+ return cur - s;
+
+ /* Netdevice */
+ if (facelet_has_netdevice(facelet)) {
+ rc = snprintf(cur, s + size - cur, " netdevice=%s",
+ facelet->netdevice.name[0] ? facelet->netdevice.name : "*");
+ if (rc < 0)
+ return rc;
+ cur += rc;
+ if (size != 0 && cur >= s + size)
+ return cur - s;
+
+ rc = snprintf(cur, s + size - cur, "/%d", facelet->netdevice.index);
+ if (rc < 0)
+ return rc;
+ cur += rc;
+ if (size != 0 && cur >= s + size)
+ return cur - s;
+
+ } else {
+ rc = snprintf(cur, s + size - cur, " netdevice=*/*");
+ if (rc < 0)
+ return rc;
+ cur += rc;
+ if (size != 0 && cur >= s + size)
+ return cur - s;
+ }
+
+ /* Netdevice type */
+ if (facelet_has_netdevice_type(facelet)) {
+ rc = snprintf(cur, s + size - cur, " type=%s",
+ netdevice_type_str[facelet->netdevice_type]);
+ if (rc < 0)
+ return rc;
+ cur += rc;
+ if (size != 0 && cur >= s + size)
+ return cur - s;
+ }
+#ifdef __linux__
+#ifndef __ANDROID__
+ else {
+ /*
+ * Heuristics to determine face type based on name, until a better
+ * solution is found
+ */
+ if ((strncmp(facelet->netdevice.name, "eth", 3) == 0) ||
+ (strncmp(facelet->netdevice.name, "en", 2) == 0)) {
+ rc = snprintf(cur, s + size - cur, " [type=WIRED]");
+ goto HEURISTIC_DONE;
+ }
+ if (strncmp(facelet->netdevice.name, "wl", 2) == 0) {
+ /* wlan* wlp* wlx* */
+ rc = snprintf(cur, s + size - cur, " [type=WIFI]");
+ goto HEURISTIC_DONE;
+ }
+ goto HEURISTIC_END;
+
+HEURISTIC_DONE:
+ if (rc < 0)
+ return rc;
+ cur += rc;
+ if (size != 0 && cur >= s + size)
+ return cur - s;
+HEURISTIC_END:
+ ;
+ }
+#endif /* ! __ANDROID__ */
+#endif /* __linux__ */
+
+ /* Local ip address */
+ if (facelet_has_local_addr(facelet)) {
+ rc = snprintf(cur, s + size - cur, " local_addr=");
+ if (rc < 0)
+ return rc;
+ cur += rc;
+ if (size != 0 && cur >= s + size)
+ return cur - s;
+
+ rc = ip_address_snprintf(cur, s + size - cur, &facelet->local_addr,
+ facelet->family);
+ if (rc < 0)
+ return rc;
+ cur += rc;
+ if (size != 0 && cur >= s + size)
+ return cur - s;
+ }
+
+ /* Local port */
+ if (facelet_has_local_port(facelet)) {
+ rc = snprintf(cur, s + size - cur, " local_port=%d",
+ facelet->local_port);
+ if (rc < 0)
+ return rc;
+ cur += rc;
+ if (size != 0 && cur >= s + size)
+ return cur - s;
+ }
+
+ /* Remote ip address */
+ if (facelet_has_remote_addr(facelet)) {
+ rc = snprintf(cur, s + size - cur, " remote_addr=");
+ if (rc < 0)
+ return rc;
+ cur += rc;
+ if (size != 0 && cur >= s + size)
+ return cur - s;
+
+ rc = ip_address_snprintf(cur, s + size - cur, &facelet->remote_addr,
+ facelet->family);
+ if (rc < 0)
+ return rc;
+ cur += rc;
+ if (size != 0 && cur >= s + size)
+ return cur - s;
+ }
+
+ /* Remote port */
+ if (facelet_has_remote_port(facelet)) {
+ rc = snprintf(cur, s + size - cur, " remote_port=%d",
+ facelet->remote_port);
+ if (rc < 0)
+ return rc;
+ cur += rc;
+ if (size != 0 && cur >= s + size)
+ return cur - s;
+ }
+
+ /* Admin state */
+ if (facelet_has_admin_state(facelet)) {
+ rc = snprintf(cur, s + size - cur, " admin_state=%s",
+ face_state_str[facelet->admin_state]);
+ if (rc < 0)
+ return rc;
+ cur += rc;
+ if (size != 0 && cur >= s + size)
+ return cur - s;
+ }
+
+ /* State */
+ if (facelet_has_state(facelet)) {
+ rc = snprintf(cur, s + size - cur, " state=%s",
+ face_state_str[facelet->state]);
+ if (rc < 0)
+ return rc;
+ cur += rc;
+ if (size != 0 && cur >= s + size)
+ return cur - s;
+ }
+
+ if (facelet_has_face_type(facelet)) {
+ rc = snprintf(cur, s + size - cur, " face_type=IP%s/%s",
+ FACEMGR_FACE_TYPE_STR(facelet->face_type));
+ if (rc < 0)
+ return rc;
+ cur += rc;
+ if (size != 0 && cur >= s + size)
+ return cur - s;
+ }
+
+ rc = snprintf(cur, s + size - cur, ">");
+ if (rc < 0)
+ return rc;
+ cur += rc;
+ if (size != 0 && cur >= s + size)
+ return cur - s;
+
+ return cur - s;
+}