/* * Copyright (c) 2021-2022 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. */ /** * * Example: * @code * <#example#> * @endcode */ #ifndef _WIN32 #include #include #endif #include #include #include #include #include #include #include #include //#include #ifdef WITH_MAPME #include #endif /* WITH_MAPME */ #include //the listener list #include #include #include //#include #include #include #include #include #include "commands.h" #include "configuration.h" #define ETHERTYPE 0x0801 #define DEFAULT_COST 1 #define DEFAULT_PORT 1234 #define make_ack(msg) \ do { \ ((msg_header_t *)msg)->header.message_type = ACK_LIGHT; \ ((msg_header_t *)msg)->header.length = 0; \ } while (0) #define make_nack(msg) \ do { \ ((msg_header_t *)msg)->header.message_type = NACK_LIGHT; \ ((msg_header_t *)msg)->header.length = 0; \ } while (0) #define msg_malloc_list(msg, COMMAND_ID, N, seq_number) \ do { \ msg = calloc(1, sizeof((msg)->header) + N * sizeof((msg)->payload)); \ (msg)->header.message_type = RESPONSE_LIGHT; \ (msg)->header.command_id = (COMMAND_ID); \ (msg)->header.length = (uint16_t)(N); \ (msg)->header.seq_num = (seq_number); \ } while (0); // conn_id = UINT_MAX when symbolic_name is not found static inline unsigned _symbolic_to_conn_id(forwarder_t *forwarder, const char *symbolic_or_connid, bool allow_self, unsigned ingress_id) { unsigned conn_id; const connection_table_t *table = forwarder_get_connection_table(forwarder); if (allow_self && strcmp(symbolic_or_connid, "SELF") == 0) { conn_id = ingress_id; } else if (is_number(symbolic_or_connid, SYMBOLIC_NAME_LEN)) { // case for conn_id as input // XXX type issue ! XXX No check, see man unsigned id = atoi(symbolic_or_connid); if (id < 0) return CONNECTION_ID_UNDEFINED; conn_id = id; if (!connection_table_validate_id(table, conn_id)) { ERROR("ConnID not found, check list connections"); conn_id = CONNECTION_ID_UNDEFINED; } } else { // case for symbolic as input: check if symbolic name can be resolved conn_id = (unsigned int)connection_table_get_id_by_name(table, symbolic_or_connid); if (connection_id_is_valid(conn_id)) { DEBUG("Resolved symbolic name '%s' to conn_id %u", symbolic_or_connid, conn_id); } else { WARN("Symbolic name '%s' could not be resolved", symbolic_or_connid); } } return conn_id; } #define symbolic_to_conn_id(forwarder, symbolic) \ _symbolic_to_conn_id(forwarder, symbolic, false, 0) #define symbolic_to_conn_id_self(forwarder, symbolic, ingress_id) \ _symbolic_to_conn_id(forwarder, symbolic, true, ingress_id) connection_t *getConnectionBySymbolicOrId(forwarder_t *forwarder, const char *symbolic_or_connid) { connection_table_t *table = forwarder_get_connection_table(forwarder); unsigned conn_id = symbolic_to_conn_id(forwarder, symbolic_or_connid); if (!connection_id_is_valid(conn_id)) return NULL; /* conn_id is assumed validated here */ return connection_table_at(table, conn_id); } /* Listener */ uint8_t *configuration_on_listener_add(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { INFO("CMD: listener add (ingress=%d)", ingress_id); assert(forwarder); assert(packet); *reply_size = sizeof(msg_header_t); msg_listener_add_t *msg = (msg_listener_add_t *)packet; cmd_listener_add_t *control = &msg->payload; switch (control->type) { case FACE_TYPE_UDP_LISTENER: case FACE_TYPE_TCP_LISTENER: case FACE_TYPE_HICN_LISTENER: break; default: ERROR("Wrong listener type"); goto NACK; } listener_table_t *table = forwarder_get_listener_table(forwarder); assert(table); /* Verify that the listener DOES NOT exist */ listener_t *listener = listener_table_get_by_name(table, control->symbolic); if (listener) { ERROR("Listener with name=%s already exists", control->symbolic); goto NACK; } address_t address; memset(&address, 0, sizeof(address_t)); if (address_from_ip_port(&address, control->family, &control->address, control->port) < 0) { WARN( "Unsupported address type for HICN (ingress id %u): " "must be either IPV4 or IPV6", ingress_id); goto NACK; } if (!face_type_is_defined(control->type)) { WARN("[configuration_on_listener_add] Invalid listener type"); goto NACK; } listener = listener_table_get_by_address(table, control->type, &address); if (listener) { char addr_str[NI_MAXHOST]; int port; address_to_string(&address, addr_str, &port); ERROR("Listener for address=%s, type=%s already exists", addr_str, face_type_str(control->type)); goto NACK; } // NOTE: interface_name is expected NULL for hICN listener listener = listener_create(control->type, &address, control->interface_name, control->symbolic, forwarder); if (!listener) goto NACK; make_ack(msg); return (uint8_t *)msg; NACK: make_nack(msg); return (uint8_t *)msg; } unsigned symbolic_to_listener_id(forwarder_t *forwarder, const char *symbolic_or_listener_id) { unsigned listener_id; const listener_table_t *table = forwarder_get_listener_table(forwarder); if (is_number(symbolic_or_listener_id, SYMBOLIC_NAME_LEN)) { // XXX type issue ! XXX No check, see man unsigned id = atoi(symbolic_or_listener_id); if (id < 0) return LISTENER_ID_UNDEFINED; listener_id = id; if (!listener_table_validate_id(table, listener_id)) { ERROR("Listener ID %d not found", id); listener_id = LISTENER_ID_UNDEFINED; } } else { // case for symbolic as input: check if symbolic name can be resolved listener_id = (unsigned int)listener_table_get_id_by_name( table, symbolic_or_listener_id); if (listener_id_is_valid(listener_id)) { DEBUG("Resolved symbolic name '%s' to conn_id %u", symbolic_or_listener_id, listener_id); } else { WARN("Symbolic name '%s' could not be resolved", symbolic_or_listener_id); } } return listener_id; } uint8_t *configuration_on_listener_remove(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { INFO("CMD: listener remove (ingress=%d)", ingress_id); assert(forwarder); assert(packet); *reply_size = sizeof(msg_header_t); msg_listener_remove_t *msg = (msg_listener_remove_t *)packet; cmd_listener_remove_t *control = &msg->payload; unsigned listener_id = symbolic_to_listener_id(forwarder, control->symbolicOrListenerid); if (!listener_id_is_valid(listener_id)) { ERROR("Invalid listener id=%u", listener_id); goto NACK; } listener_table_t *listener_table = forwarder_get_listener_table(forwarder); listener_t *listener = listener_table_get_by_id(listener_table, listener_id); if (!listener) { ERROR("Listener ID not found, check list listeners"); goto NACK; } // Do not remove listener if it is the one curretly used to send the command connection_table_t *conn_table = forwarder_get_connection_table(forwarder); connection_t *curr_connection = connection_table_get_by_id(conn_table, ingress_id); const address_pair_t *pair = connection_get_pair(curr_connection); if (address_equals(listener_get_address(listener), address_pair_get_local(pair))) { ERROR("Cannot remove current listener"); goto NACK; } connection_table_t *table = forwarder_get_connection_table(forwarder); connection_t *connection; connection_table_foreach(table, connection, { const address_pair_t *pair = connection_get_pair(connection); if (!address_equals(listener_get_address(listener), address_pair_get_local(pair))) continue; unsigned conn_id = (unsigned)connection_table_get_connection_id(table, connection); /* Remove connection from the FIB */ // XXX TODO get entries, raise notifications... // XXX isn't it possible to implement this in the forwarder ????? forwarder_remove_connection_id_from_routes(forwarder, conn_id); /* Remove connection */ connection_table_remove_by_id(table, conn_id); }); /* Remove listener */ listener_table_remove_by_id(listener_table, listener_id); listener_finalize(listener); WITH_DEBUG(listener_table_print_by_key(listener_table);) make_ack(msg); return (uint8_t *)msg; NACK: make_nack(msg); return (uint8_t *)msg; } // TODO(eloparco): Unused forwarder param static inline void fill_listener_command(const listener_t *listener, cmd_listener_list_item_t *cmd) { assert(listener); assert(cmd); struct sockaddr_in *sin; struct sockaddr_in6 *sin6; const address_t *addr = listener_get_address(listener); cmd->id = (uint32_t)listener_get_id(listener); cmd->type = (uint8_t)listener_get_type(listener); switch (addr->as_ss.ss_family) { case AF_INET: sin = (struct sockaddr_in *)addr; cmd->family = (uint8_t)AF_INET; cmd->local_addr.v4.as_inaddr = sin->sin_addr; cmd->local_port = sin->sin_port; break; case AF_INET6: sin6 = (struct sockaddr_in6 *)addr; cmd->family = (uint8_t)AF_INET6; cmd->local_addr.v6.as_in6addr = sin6->sin6_addr; cmd->local_port = sin6->sin6_port; break; default: break; } const char *name = listener_get_name(listener); snprintf(cmd->name, SYMBOLIC_NAME_LEN, "%s", name); const char *interface_name = listener_get_interface_name(listener); snprintf(cmd->interface_name, SYMBOLIC_NAME_LEN, "%s", interface_name); } uint8_t *configuration_on_listener_list(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { INFO("CMD: listener list (ingress=%d)", ingress_id); assert(forwarder); assert(packet); listener_table_t *table = forwarder_get_listener_table(forwarder); size_t n = listener_table_len(table); msg_listener_list_t *msg_received = (msg_listener_list_t *)packet; uint8_t command_id = msg_received->header.command_id; uint32_t seq_num = msg_received->header.seq_num; msg_listener_list_reply_t *msg = NULL; msg_malloc_list(msg, command_id, n, seq_num); if (!msg) goto NACK; cmd_listener_list_item_t *payload = &msg->payload; listener_table_foreach(table, listener, { fill_listener_command(listener, payload); payload++; }); *reply_size = sizeof(msg->header) + n * sizeof(msg->payload); return (uint8_t *)msg; NACK: *reply_size = sizeof(msg_header_t); make_nack(msg); return (uint8_t *)msg; } /* Connection */ uint8_t *configuration_on_connection_add(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { INFO("CMD: connection add (ingress=%d)", ingress_id); assert(forwarder); assert(packet); *reply_size = sizeof(msg_header_t); msg_connection_add_t *msg = (msg_connection_add_t *)packet; cmd_connection_add_t *control = &msg->payload; switch (control->type) { case FACE_TYPE_UDP: case FACE_TYPE_TCP: case FACE_TYPE_HICN: break; default: goto NACK; } if (!face_type_is_defined(control->type)) goto NACK; connection_table_t *table = forwarder_get_connection_table(forwarder); char *symbolic_name = control->symbolic; // Generate connection name if not specified if (symbolic_name[0] == '\0') { int rc = connection_table_get_random_name(table, symbolic_name); if (rc < 0) { ERROR("Unable to generate new connection name"); goto NACK; } } else { if (connection_table_get_by_name(table, symbolic_name)) { ERROR("Connection symbolic name already exists"); goto NACK; } } address_pair_t pair; if (address_pair_from_ip_port(&pair, control->family, &control->local_ip, control->local_port, &control->remote_ip, control->remote_port) < 0) goto NACK; if (forwarder_add_connection(forwarder, symbolic_name, control->type, &pair, control->tags, control->priority, control->admin_state) < 0) goto NACK; make_ack(msg); return (uint8_t *)msg; NACK: make_nack(msg); return (uint8_t *)msg; } /** * Add an IP-based tunnel. * * The call can fail if the symbolic name is a duplicate. It could also fail if * there's an problem creating the local side of the tunnel (i.e. the local * socket address is not usable). * * @return true Tunnel added * @return false Tunnel not added (an error) */ uint8_t *configuration_on_connection_remove(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { INFO("CMD: connection remove (ingress=%d)", ingress_id); assert(forwarder); assert(packet); *reply_size = sizeof(msg_header_t); msg_connection_remove_t *msg = (msg_connection_remove_t *)packet; cmd_connection_remove_t *control = &msg->payload; unsigned conn_id = symbolic_to_conn_id_self( forwarder, control->symbolic_or_connid, ingress_id); if (!connection_id_is_valid(conn_id)) { ERROR("Invalid connection id=%u", conn_id); goto NACK; } if (strcmp(control->symbolic_or_connid, "SELF") != 0 && conn_id == ingress_id) { ERROR("Cannot remove current connection"); goto NACK; } /* * * Don't close the fd for SELF otherwise it won't be possible * to send the reply back. The connection is finalized later in * _forwarder_finalize_connection_if_self */ bool finalize = (strcmp(control->symbolic_or_connid, "SELF") != 0); if (forwarder_remove_connection(forwarder, conn_id, finalize) < 0) goto NACK; WITH_DEBUG({ connection_table_t *table = forwarder_get_connection_table(forwarder); connection_table_print_by_pair(table); }) make_ack(msg); return (uint8_t *)msg; NACK: make_nack(msg); return (uint8_t *)msg; } static inline void tolower_str(char *str) { char *p = str; for (; *p; p++) *p = tolower(*p); } // TODO(eloparco): Forwarder param not used static inline void fill_connections_command(const connection_t *connection, cmd_connection_list_item_t *cmd) { assert(connection); assert(cmd); struct sockaddr_in *sin; struct sockaddr_in6 *sin6; const address_pair_t *pair = connection_get_pair(connection); cmd->id = connection_get_id(connection), cmd->state = (uint8_t)connection_get_state(connection), cmd->admin_state = (uint8_t)connection_get_admin_state(connection), cmd->type = (uint8_t)connection_get_type(connection), #ifdef WITH_POLICY cmd->priority = connection_get_priority(connection), cmd->tags = (uint8_t)connection_get_tags(connection), #endif /* WITH_POLICY */ snprintf(cmd->name, SYMBOLIC_NAME_LEN, "%s", connection_get_name(connection)); tolower_str(cmd->name); snprintf(cmd->interface_name, SYMBOLIC_NAME_LEN, "%s", connection_get_interface_name(connection)); switch (pair->local.as_ss.ss_family) { case AF_INET: cmd->family = (uint8_t)AF_INET; sin = (struct sockaddr_in *)(&pair->local); cmd->local_port = sin->sin_port; cmd->local_addr.v4.as_inaddr = sin->sin_addr; sin = (struct sockaddr_in *)(&pair->remote); cmd->remote_port = sin->sin_port; cmd->remote_addr.v4.as_inaddr = sin->sin_addr; break; case AF_INET6: cmd->family = (uint8_t)AF_INET6; sin6 = (struct sockaddr_in6 *)(&pair->local); cmd->local_port = sin6->sin6_port; cmd->local_addr.v6.as_in6addr = sin6->sin6_addr; sin6 = (struct sockaddr_in6 *)(&pair->remote); cmd->remote_port = sin6->sin6_port; cmd->remote_addr.v6.as_in6addr = sin6->sin6_addr; break; default: break; } } static inline void fill_connection_stats_command(connection_t *connection, cmd_stats_list_item_t *cmd) { assert(connection && cmd); cmd->id = connection->id; cmd->stats = connection->stats; } uint8_t *configuration_on_connection_list(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { INFO("CMD: connection list (ingress=%d)", ingress_id); assert(forwarder); assert(packet); connection_table_t *table = forwarder_get_connection_table(forwarder); // -1 since current connection (i.e. the one used to send // the command) is not considered size_t n = connection_table_len(table) - 1; msg_connection_list_t *msg_received = (msg_connection_list_t *)packet; uint8_t command_id = msg_received->header.command_id; uint32_t seq_num = msg_received->header.seq_num; msg_connection_list_reply_t *msg = NULL; msg_malloc_list(msg, command_id, n, seq_num); if (!msg) goto NACK; cmd_connection_list_item_t *payload = &msg->payload; connection_table_foreach_new(table, connection, { if (connection->id == ingress_id) continue; fill_connections_command(connection, payload); payload++; }); *reply_size = sizeof(msg->header) + n * sizeof(msg->payload); return (uint8_t *)msg; NACK: *reply_size = sizeof(msg_header_t); make_nack(msg); return (uint8_t *)msg; } #if 0 uint8_t *configuration_on_connection_set_admin_state(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { assert(forwarder); assert(packet); msg_connection_set_admin_state_t *msg = (msg_connection_set_admin_state_t *)packet; cmd_connection_set_admin_state_t *control = &msg->payload; if ((control->admin_state != FACE_STATE_UP) && (control->admin_state != FACE_STATE_DOWN)) goto NACK; connection_t *conn = getConnectionBySymbolicOrId(forwarder, control->symbolic_or_connid); if (!conn) goto NACK; connection_set_admin_state(conn, control->admin_state); /* Hook: connection event */ forwarder_on_connection_event(forwarder, conn, control->admin_state == FACE_STATE_UP ? CONNECTION_EVENT_SET_UP : CONNECTION_EVENT_SET_DOWN); make_ack(msg); return (uint8_t *)msg; NACK: make_nack(msg); return (uint8_t *)msg; } #endif uint8_t *configuration_on_connection_update(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { assert(forwarder); assert(packet); #ifdef WITH_POLICY msg_connection_update_t *msg = (msg_connection_update_t *)packet; cmd_connection_update_t *control = &msg->payload; connection_t *conn = getConnectionBySymbolicOrId(forwarder, control->symbolic_or_connid); if (!conn) goto NACK; connection_set_tags(conn, control->tags); connection_set_admin_state(conn, control->admin_state); if (control->priority > 0) connection_set_priority(conn, control->priority); make_ack(msg); return (uint8_t *)msg; NACK: #endif /* WITH_POLICY */ make_nack(msg); return (uint8_t *)msg; } #if 0 uint8_t *configuration_on_connection_set_priority(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { assert(forwarder); assert(packet); #ifdef WITH_POLICY msg_connection_set_priority_t *msg = (msg_connection_set_priority_t *)packet; cmd_connection_set_priority_t *control = &msg->payload; connection_t *conn = getConnectionBySymbolicOrId(forwarder, control->symbolic_or_connid); if (!conn) goto NACK; connection_set_priority(conn, control->priority); /* Hook: connection event */ forwarder_on_connection_event(forwarder, conn, CONNECTION_EVENT_PRIORITY_CHANGED); make_ack(msg); return (uint8_t *)msg; NACK: #endif /* WITH_POLICY */ make_nack(msg); return (uint8_t *)msg; } uint8_t *configuration_on_connection_set_tags(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { assert(forwarder); assert(packet); #ifdef WITH_POLICY msg_connection_set_tags_t *msg = (msg_connection_set_tags_t *)packet; cmd_connection_set_tags_t *control = &msg->payload; connection_t *conn = getConnectionBySymbolicOrId(forwarder, control->symbolic_or_connid); if (!conn) goto NACK; connection_set_tags(conn, control->tags); /* Hook: connection event */ forwarder_on_connection_event(forwarder, conn, CONNECTION_EVENT_TAGS_CHANGED); make_ack(msg); return (uint8_t *)msg; NACK: #endif /* WITH_POLICY */ make_nack(msg); return (uint8_t *)msg; } #endif /* Route */ uint8_t *configuration_on_route_add(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { INFO("CMD: route add (ingress=%d)", ingress_id); assert(forwarder); assert(packet); *reply_size = sizeof(msg_header_t); msg_route_add_t *msg = (msg_route_add_t *)packet; cmd_route_add_t *control = &msg->payload; unsigned conn_id = symbolic_to_conn_id_self( forwarder, control->symbolic_or_connid, ingress_id); if (!connection_id_is_valid(conn_id)) goto NACK; hicn_ip_prefix_t prefix = {.family = control->family, .address = control->address, .len = control->len}; if (!forwarder_add_or_update_route(forwarder, &prefix, conn_id)) goto NACK; make_ack(msg); return (uint8_t *)msg; NACK: make_nack(msg); return (uint8_t *)msg; } uint8_t *configuration_on_route_remove(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { INFO("CMD: route remove (ingress=%d)", ingress_id); assert(forwarder); assert(packet); *reply_size = sizeof(msg_header_t); msg_route_remove_t *msg = (msg_route_remove_t *)packet; cmd_route_remove_t *control = &msg->payload; unsigned conn_id = symbolic_to_conn_id(forwarder, control->symbolic_or_connid); if (!connection_id_is_valid(conn_id)) goto NACK; hicn_ip_prefix_t prefix = {.family = control->family, .address = control->address, .len = control->len}; if (!forwarder_remove_route(forwarder, &prefix, conn_id)) goto NACK; make_ack(msg); return (uint8_t *)msg; NACK: make_nack(msg); return (uint8_t *)msg; } static inline off_t fill_route_command(const fib_entry_t *entry, cmd_route_list_item_t *cmd) { const nexthops_t *nexthops = fib_entry_get_nexthops(entry); assert(nexthops_get_len(nexthops) == nexthops_get_curlen(nexthops)); size_t num_nexthops = nexthops_get_len(nexthops); off_t pos = 0; if (num_nexthops == 0) return 0; const hicn_prefix_t *prefix = fib_entry_get_prefix(entry); const hicn_ip_address_t *address = hicn_prefix_get_ip_address(prefix); int family = hicn_ip_address_get_family(address); nexthops_foreach(nexthops, nexthop, { cmd->family = family; cmd->remote_addr = *address; cmd->face_id = nexthop; cmd->len = hicn_prefix_get_len(prefix); cmd->cost = DEFAULT_COST; pos++; cmd++; }); return pos; } uint8_t *configuration_on_route_list(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { INFO("CMD: route list (ingress=%d)", ingress_id); assert(forwarder); assert(packet); msg_route_list_t *msg_received = (msg_route_list_t *)packet; uint8_t command_id = msg_received->header.command_id; uint32_t seq_num = msg_received->header.seq_num; const fib_t *fib = forwarder_get_fib(forwarder); /* * Two step approach to precompute the number of entries to allocate * * NOTE: we might have routes with no or multiple next hops. */ size_t n = 0; fib_foreach_entry(fib, entry, { const nexthops_t *nexthops = fib_entry_get_nexthops(entry); assert(nexthops_get_len(nexthops) == nexthops_get_curlen(nexthops)); n += nexthops_get_len(nexthops); }); msg_route_list_reply_t *msg = NULL; msg_malloc_list(msg, command_id, n, seq_num); if (!msg) goto NACK; cmd_route_list_item_t *payload = &msg->payload; off_t pos = 0; fib_foreach_entry(fib, entry, { pos += fill_route_command(entry, payload + pos); }); *reply_size = sizeof(msg->header) + n * sizeof(msg->payload); return (uint8_t *)msg; NACK: *reply_size = sizeof(msg_header_t); make_nack(msg); return (uint8_t *)msg; } /* Cache */ uint8_t *configuration_on_cache_set_store(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { INFO("CMD: cache set store (ingress=%d)", ingress_id); assert(forwarder); assert(packet); *reply_size = sizeof(msg_header_t); msg_cache_set_store_t *msg = (msg_cache_set_store_t *)packet; cmd_cache_set_store_t *control = &msg->payload; if ((control->activate != 0) && (control->activate != 1)) goto NACK; bool value = (bool)control->activate; forwarder_cs_set_store(forwarder, value); assert(forwarder_cs_get_store(forwarder) == value); make_ack(msg); return (uint8_t *)msg; NACK: make_nack(msg); return (uint8_t *)msg; } uint8_t *configuration_on_cache_set_serve(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { INFO("CMD: cache set serve (ingress=%d)", ingress_id); assert(forwarder); assert(packet); *reply_size = sizeof(msg_header_t); msg_cache_set_serve_t *msg = (msg_cache_set_serve_t *)packet; cmd_cache_set_serve_t *control = &msg->payload; if ((control->activate != 0) && (control->activate != 1)) goto NACK; bool value = (bool)control->activate; forwarder_cs_set_serve(forwarder, value); assert(forwarder_cs_get_serve(forwarder) == value); make_ack(msg); return (uint8_t *)msg; NACK: make_nack(msg); return (uint8_t *)msg; } uint8_t *configuration_on_cache_clear(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { INFO("CMD: cache clear (ingress=%d)", ingress_id); assert(forwarder); assert(packet); *reply_size = sizeof(msg_header_t); msg_cache_clear_t *msg = (msg_cache_clear_t *)packet; forwarder_cs_clear(forwarder); make_ack(msg); return (uint8_t *)msg; } uint8_t *configuration_on_cache_list(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { INFO("CMD: cache list (ingress=%d)", ingress_id); assert(forwarder); assert(packet); msg_cache_list_t *msg_received = (msg_cache_list_t *)packet; uint32_t seq_num = msg_received->header.seq_num; msg_cache_list_reply_t *msg = malloc(sizeof(*msg)); *msg = (msg_cache_list_reply_t){ .header = {.message_type = RESPONSE_LIGHT, .length = 1, .seq_num = seq_num}, .payload = { .store_in_cs = forwarder_cs_get_store(forwarder), .serve_from_cs = forwarder_cs_get_serve(forwarder), .cs_size = (unsigned int)forwarder_cs_get_size(forwarder), .num_stale_entries = (unsigned int)forwarder_cs_get_num_stale_entries(forwarder)}}; *reply_size = sizeof(*msg); return (uint8_t *)msg; } /* Strategy */ uint8_t *configuration_on_strategy_set(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { INFO("CMD: strategy set (ingress=%d)", ingress_id); assert(forwarder); assert(packet); *reply_size = sizeof(msg_header_t); msg_strategy_set_t *msg = (msg_strategy_set_t *)packet; cmd_strategy_set_t *control = &msg->payload; char prefix_s[MAXSZ_IP_PREFIX]; hicn_ip_prefix_t prefix = { .family = control->family, .address = control->address, .len = control->len, }; int rc = hicn_ip_prefix_snprintf(prefix_s, MAXSZ_IP_PREFIX, &prefix); assert(rc < MAXSZ_IP_PREFIX); if (rc < 0) goto NACK; strategy_type_t strategy = control->type; configuration_t *config = forwarder_get_configuration(forwarder); strategy_type_t existingFwdStrategy = configuration_get_strategy(config, prefix_s); strategy_options_t *options = NULL; // XXX check control->family hicn_prefix_t name_prefix = HICN_PREFIX_EMPTY; hicn_prefix_create_from_ip_address_len(&control->address, control->len, &name_prefix); // The strategy is not present in the hash table // or has to be updated or to be restarted if (existingFwdStrategy == STRATEGY_TYPE_UNDEFINED || strategy != existingFwdStrategy || (strategy == existingFwdStrategy && strategy == STRATEGY_TYPE_BESTPATH)) { configuration_set_strategy(config, prefix_s, strategy); forwarder_set_strategy(forwarder, &name_prefix, strategy, options); } else { WITH_WARN({ char buf[MAXSZ_HICN_PREFIX]; hicn_prefix_snprintf(buf, MAXSZ_HICN_PREFIX, &name_prefix); WARN("Strategy for prefix %s not updated", buf); }) } make_ack(msg); return (uint8_t *)msg; NACK: make_nack(msg); return (uint8_t *)msg; } uint8_t *configuration_on_strategy_add_local_prefix(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { INFO("CMD: strategy add local prefix (ingress=%d)", ingress_id); assert(forwarder); assert(packet); *reply_size = sizeof(msg_header_t); msg_strategy_add_local_prefix_t *msg = (msg_strategy_add_local_prefix_t *)packet; cmd_strategy_add_local_prefix_t *control = &msg->payload; char prefix_s[MAXSZ_IP_PREFIX]; hicn_ip_prefix_t prefix = { .family = control->family, .address = control->address, .len = control->len, }; int rc = hicn_ip_prefix_snprintf(prefix_s, MAXSZ_IP_PREFIX, &prefix); assert(rc < MAXSZ_IP_PREFIX); if (rc < 0) goto NACK; strategy_type_t strategy = control->type; configuration_t *config = forwarder_get_configuration(forwarder); strategy_type_t existingFwdStrategy = configuration_get_strategy(config, prefix_s); if (strategy != existingFwdStrategy) goto NACK; if (strategy != STRATEGY_TYPE_BESTPATH && strategy != STRATEGY_TYPE_REPLICATION) goto NACK; hicn_prefix_t name_prefix = HICN_PREFIX_EMPTY; hicn_prefix_create_from_ip_address_len(&control->address, control->len, &name_prefix); strategy_options_t options; hicn_prefix_t local_prefix = HICN_PREFIX_EMPTY; hicn_prefix_create_from_ip_address_len(&control->address, control->len, &local_prefix); // for the moment bestpath and replication are the same but we distinguish // the two in case they will diverge in the future if (strategy == STRATEGY_TYPE_BESTPATH) { options.bestpath.local_prefixes = create_local_prefixes(); local_prefixes_add_prefix(options.bestpath.local_prefixes, &local_prefix); } else { options.replication.local_prefixes = create_local_prefixes(); local_prefixes_add_prefix(options.replication.local_prefixes, &local_prefix); } forwarder_add_strategy_options(forwarder, &name_prefix, strategy, &options); free_local_prefixes(options.bestpath.local_prefixes); make_ack(msg); return (uint8_t *)msg; NACK: make_nack(msg); return (uint8_t *)msg; } /* Statistics */ uint8_t *configuration_on_stats_get(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { assert(forwarder && packet); INFO("CMD: stats get (ingress=%d)", ingress_id); msg_stats_get_t *msg_received = (msg_stats_get_t *)packet; uint32_t seq_num = msg_received->header.seq_num; msg_stats_get_reply_t *msg = malloc(sizeof(*msg)); *msg = (msg_stats_get_reply_t){ .header = {.message_type = RESPONSE_LIGHT, .length = 1, .seq_num = seq_num}, .payload = {.forwarder = forwarder_get_stats(forwarder), .pkt_cache = pkt_cache_get_stats(forwarder_get_pkt_cache(forwarder))} }; *reply_size = sizeof(*msg); return (uint8_t *)msg; } uint8_t *configuration_on_stats_list(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { assert(forwarder && packet); INFO("CMD: stats list (ingress=%d)", ingress_id); connection_table_t *table = forwarder_get_connection_table(forwarder); // -1 since current connection (i.e. the one used to send // the command) is not considered size_t n = connection_table_len(table) - 1; msg_stats_list_t *msg_received = (msg_stats_list_t *)packet; uint8_t command_id = msg_received->header.command_id; uint32_t seq_num = msg_received->header.seq_num; msg_stats_list_reply_t *msg = NULL; msg_malloc_list(msg, command_id, n, seq_num); if (!msg) goto NACK; cmd_stats_list_item_t *payload = &msg->payload; connection_t *connection; connection_table_foreach(table, connection, { if (connection->id == ingress_id) continue; fill_connection_stats_command(connection, payload); payload++; }); *reply_size = sizeof(msg->header) + n * sizeof(msg->payload); return (uint8_t *)msg; NACK: *reply_size = sizeof(msg_header_t); make_nack(msg); return (uint8_t *)msg; } /* WLDR */ uint8_t *configuration_on_wldr_set(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { assert(forwarder); assert(packet); msg_wldr_set_t *msg = (msg_wldr_set_t *)packet; cmd_wldr_set_t *control = &msg->payload; if ((control->activate != 0) && (control->activate != 1)) goto NACK; bool value = (bool)control->activate; unsigned conn_id = symbolic_to_conn_id(forwarder, control->symbolic_or_connid); if (!connection_id_is_valid(conn_id)) goto NACK; connection_table_t *table = forwarder_get_connection_table(forwarder); connection_t *conn = connection_table_at(table, conn_id); if (value) connection_wldr_enable(conn, value); make_ack(msg); return (uint8_t *)msg; NACK: make_nack(msg); return (uint8_t *)msg; } /* Punting */ uint8_t *configuration_on_punting_add(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { // #if !defined(__APPLE__) && !defined(_WIN32) && defined(PUNTING) msg_punting_add_t *msg = (msg_punting_add_t *)packet; #if !defined(__APPLE__) && !defined(_WIN32) && defined(PUNTING) cmd_punting_add_t *control = &msg->payload; if (ip_address_empty(&control->address)) goto NACK; /* This is for hICN listeners only */ // XXX add check ! // comments: // EncapType: I use the Hicn encap since the punting is available only for // Hicn listeners LocalAddress: The only listern for which we need punting // rules is the main one, which has no address // so I create a fake empty address. This need to be consistent // with the address set at creation time address_t fakeaddr; memset(&fakeaddr, 0, sizeof(address_t)); fakeaddr = ADDRESS_ANY(control->family, DEFAULT_PORT); listener_table_t *table = forwarder_get_listener_table(forwarder); listener_t *listener = listener_table_get_by_address(table, FACE_TYPE_HICN, &fakeaddr); if (!listener) { ERROR("the main listener does not exist"); goto NACK; } hicn_ip_prefix_t prefix = {.family = control->family, .address = control->address, .len = control->len}; char prefix_s[MAXSZ_IP_PREFIX]; int rc = hicn_ip_prefix_snprintf(prefix_s, MAXSZ_IP_PREFIX, &prefix); assert(rc < MAXSZ_IP_PREFIX); if (rc < 0) goto NACK; if (listener_punt(listener, prefix_s) < 0) { ERROR("error while adding the punting rule"); goto NACK; } make_ack(msg); return (uint8_t *)msg; NACK: #endif make_nack(msg); return (uint8_t *)msg; } /* MAP-Me */ #ifdef WITH_MAPME uint8_t *configuration_on_mapme_enable(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { INFO("CMD: mapme enable (ingress=%d)", ingress_id); assert(forwarder); assert(packet); *reply_size = sizeof(msg_header_t); msg_mapme_enable_t *msg = (msg_mapme_enable_t *)packet; cmd_mapme_enable_t *control = &msg->payload; if ((control->activate != 0) && (control->activate != 1)) goto NACK; bool value = (bool)control->activate; INFO("MAP-Me SET enable: %s", value ? "on" : "off"); mapme_t *mapme = forwarder_get_mapme(forwarder); if (!mapme) goto NACK; mapme_set_enable(mapme, value); make_ack(msg); return (uint8_t *)msg; NACK: make_nack(msg); return (uint8_t *)msg; } uint8_t *configuration_on_mapme_set_discovery(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { INFO("CMD: mapme discovery (ingress=%d)", ingress_id); assert(forwarder); assert(packet); *reply_size = sizeof(msg_header_t); msg_mapme_set_discovery_t *msg = (msg_mapme_set_discovery_t *)packet; cmd_mapme_set_discovery_t *control = &msg->payload; if ((control->activate != 0) && (control->activate != 1)) goto NACK; bool value = (bool)control->activate; INFO("MAP-Me SET discovery: %s", value ? "on" : "off"); mapme_t *mapme = forwarder_get_mapme(forwarder); if (!mapme) goto NACK; mapme_set_discovery(mapme, value); make_ack(msg); return (uint8_t *)msg; NACK: make_nack(msg); return (uint8_t *)msg; } uint8_t *configuration_on_mapme_set_timescale(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { INFO("CMD: mapme timescale (ingress=%d)", ingress_id); assert(forwarder); assert(packet); *reply_size = sizeof(msg_header_t); msg_mapme_set_timescale_t *msg = (msg_mapme_set_timescale_t *)packet; cmd_mapme_set_timescale_t *control = &msg->payload; INFO("MAP-Me SET timescale: %u", control->timePeriod); mapme_t *mapme = forwarder_get_mapme(forwarder); if (!mapme) goto NACK; mapme_set_timescale(mapme, control->timePeriod); make_ack(msg); return (uint8_t *)msg; NACK: make_nack(msg); return (uint8_t *)msg; } uint8_t *configuration_on_mapme_set_retx(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { INFO("CMD: mapme retransmission (ingress=%d)", ingress_id); assert(forwarder); assert(packet); *reply_size = sizeof(msg_header_t); msg_mapme_set_retx_t *msg = (msg_mapme_set_retx_t *)packet; cmd_mapme_set_retx_t *control = &msg->payload; INFO("MAP-Me SET retx: %u", control->timePeriod); mapme_t *mapme = forwarder_get_mapme(forwarder); if (!mapme) goto NACK; mapme_set_retransmision(mapme, control->timePeriod); make_ack(msg); return (uint8_t *)msg; NACK: make_nack(msg); return (uint8_t *)msg; } uint8_t *configuration_on_mapme_send_update(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { assert(forwarder); assert(packet); INFO("CMD: mapme send update (ingress=%d)", ingress_id); msg_mapme_send_update_t *msg = (msg_mapme_send_update_t *)packet; *reply_size = sizeof(msg_header_t); fib_t *fib = forwarder_get_fib(forwarder); if (!fib) goto NACK; mapme_t *mapme = forwarder_get_mapme(forwarder); /* * The command triggers a mapme update for all prefixes produced on this * face * */ fib_foreach_entry(fib, entry, { const nexthops_t *nexthops = fib_entry_get_nexthops(entry); nexthops_foreach(nexthops, nexthop, { if (nexthop != ingress_id) continue; /* This entry points to the producer face */ mapme_set_all_adjacencies(mapme, entry); break; }); }); make_ack(msg); return (uint8_t *)msg; NACK: make_nack(msg); return (uint8_t *)msg; } #endif /* WITH_MAPME */ /* Policy */ uint8_t *configuration_on_policy_add(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { assert(forwarder); assert(packet); #ifdef WITH_POLICY msg_policy_add_t *msg = (msg_policy_add_t *)packet; cmd_policy_add_t *control = &msg->payload; hicn_ip_prefix_t prefix = {.family = control->family, .address = control->address, .len = control->len}; if (!forwarder_add_or_update_policy(forwarder, &prefix, &control->policy)) goto NACK; make_ack(msg); return (uint8_t *)msg; NACK: #endif /* WITH_POLICY */ make_nack(msg); return (uint8_t *)msg; } uint8_t *configuration_on_policy_remove(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { assert(forwarder); assert(packet); #ifdef WITH_POLICY msg_policy_remove_t *msg = (msg_policy_remove_t *)packet; cmd_policy_remove_t *control = &msg->payload; hicn_ip_prefix_t prefix = {.family = control->family, .address = control->address, .len = control->len}; if (!forwarder_remove_policy(forwarder, &prefix)) goto NACK; make_ack(msg); return (uint8_t *)msg; NACK: #endif /* WITH_POLICY */ make_nack(msg); return (uint8_t *)msg; } static inline void fill_policy_command(const fib_entry_t *entry, cmd_policy_list_item_t *cmd) { const hicn_prefix_t *prefix = fib_entry_get_prefix(entry); const hicn_ip_address_t *ip_address = hicn_prefix_get_ip_address(prefix); cmd->remote_addr = *ip_address; cmd->family = hicn_ip_address_get_family(ip_address); cmd->len = hicn_prefix_get_len(prefix); hicn_policy_t policy = fib_entry_get_policy(entry); _hicn_policy_t _policy = { .stats = { .wired = {.throughput = htonf(policy.stats.wired.throughput), .latency = htonf(policy.stats.wired.latency), .loss_rate = htonf(policy.stats.wired.loss_rate)}, .wifi = {.throughput = htonf(policy.stats.wifi.throughput), .latency = htonf(policy.stats.wifi.latency), .loss_rate = htonf(policy.stats.wifi.loss_rate)}, .cellular = {.throughput = htonf(policy.stats.cellular.throughput), .latency = htonf(policy.stats.cellular.latency), .loss_rate = htonf(policy.stats.cellular.loss_rate)}, .all = {.throughput = htonf(policy.stats.all.throughput), .latency = htonf(policy.stats.all.latency), .loss_rate = htonf(policy.stats.all.loss_rate)}}}; for (unsigned i = 0; i < POLICY_TAG_N; i++) { _policy.tags[i] = (_policy_tag_state_t){ .state = policy.tags[i].state, .disabled = policy.tags[i].disabled, }; } memcpy(_policy.app_name, policy.app_name, APP_NAME_LEN); memcpy(cmd->policy, &_policy, sizeof(_policy)); } uint8_t *configuration_on_policy_list(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { assert(forwarder); assert(packet); const fib_t *fib = forwarder_get_fib(forwarder); assert(fib); size_t n = fib_get_size(fib); #ifdef WITH_POLICY msg_policy_list_t *msg_received = (msg_policy_list_t *)packet; uint8_t command_id = msg_received->header.command_id; uint32_t seq_num = msg_received->header.seq_num; msg_policy_list_reply_t *msg = NULL; msg_malloc_list(msg, command_id, n, seq_num); if (!msg) goto NACK; cmd_policy_list_item_t *payload = &msg->payload; fib_foreach_entry(fib, entry, { fill_policy_command(entry, payload); payload++; }); return (uint8_t *)msg; #endif /* WITH_POLICY */ NACK: make_nack(msg); return (uint8_t *)msg; } /* Subscription */ uint8_t *configuration_on_subscription_add(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { INFO("CMD: subscription add (ingress=%d)", ingress_id); assert(forwarder); assert(packet); *reply_size = sizeof(msg_header_t); msg_subscription_add_t *msg = (msg_subscription_add_t *)packet; cmd_subscription_add_t *control = &msg->payload; hc_topics_t topics = control->topics; subscription_table_t *subscriptions = forwarder_get_subscriptions(forwarder); assert(subscriptions); int ret = subscription_table_add_topics_for_connection(subscriptions, topics, ingress_id); if (ret < 0) goto NACK; WITH_DEBUG(subscription_table_print(subscriptions);) make_ack(msg); return (uint8_t *)msg; NACK: make_nack(msg); return (uint8_t *)msg; } uint8_t *configuration_on_subscription_remove(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { INFO("CMD: subscription remove (ingress=%d)", ingress_id); assert(forwarder); assert(packet); *reply_size = sizeof(msg_header_t); msg_subscription_add_t *msg = (msg_subscription_add_t *)packet; cmd_subscription_add_t *control = &msg->payload; hc_topics_t topics = control->topics; subscription_table_t *subscriptions = forwarder_get_subscriptions(forwarder); assert(subscriptions); subscription_table_remove_topics_for_connection(subscriptions, topics, ingress_id); WITH_DEBUG(subscription_table_print(subscriptions);) make_ack(msg); return (uint8_t *)msg; } uint8_t *configuration_on_active_interface_update(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { msg_active_interface_update_t *msg = (msg_active_interface_update_t *)packet; make_nack(msg); return (uint8_t *)msg; } uint8_t *command_process(forwarder_t *forwarder, uint8_t *packet, unsigned ingress_id, size_t *reply_size) { uint8_t *reply = NULL; /* * For most commands, the packet will simply be transformed into an ack. * For list commands, a new message will be allocated, and the return value * might eventually be NULL in case of an error. That is why the free the * reply at the end in these circumstances. * * XXX rework this part. */ command_type_t command_type = ((msg_header_t *)packet)->header.command_id; switch (command_type) { #define _(l, u) \ case COMMAND_TYPE_##u: \ reply = configuration_on_##l(forwarder, packet, ingress_id, reply_size); \ assert(reply); \ break; foreach_command_type #undef _ case COMMAND_TYPE_UNDEFINED : case COMMAND_TYPE_N : ERROR("Unexpected command type"); reply = packet; make_nack(reply); if (reply_size) *reply_size = sizeof(msg_header_t); break; } return reply; } ssize_t command_process_msgbuf(forwarder_t *forwarder, msgbuf_t *msgbuf) { assert(forwarder); assert(msgbuf); uint8_t *packet = msgbuf_get_packet(msgbuf); unsigned ingress_id = msgbuf_get_connection_id(msgbuf); uint8_t *reply = NULL; size_t reply_size = 0; reply = command_process(forwarder, packet, ingress_id, &reply_size); if (connection_id_is_valid(msgbuf->connection_id)) { connection_table_t *table = forwarder_get_connection_table(forwarder); const connection_t *connection = connection_table_at(table, ingress_id); connection_send_packet(connection, reply, reply_size); } /* Free allocated replies */ if (reply != packet) free(reply); return msgbuf_get_len(msgbuf); } void commands_notify(const forwarder_t *forwarder, hc_topic_t topic, uint8_t *msg, size_t size) { // Retrieve subscribed connections subscription_table_t *subscriptions = forwarder_get_subscriptions(forwarder); unsigned *subscribed_conn_ids = subscription_table_get_connections_for_topic(subscriptions, topic); // Send notification to subscribed connections const connection_table_t *table = forwarder_get_connection_table(forwarder); for (int i = 0; i < vector_len(subscribed_conn_ids); i++) { const connection_t *conn = connection_table_at(table, subscribed_conn_ids[i]); connection_send_packet(conn, msg, size); } } void commands_notify_connection(const forwarder_t *forwarder, connection_event_t event, const connection_t *connection) { #if 0 uint8_t command_id; switch (event) { case CONNECTION_EVENT_CREATE: command_id = COMMAND_TYPE_CONNECTION_ADD; break; case CONNECTION_EVENT_DELETE: command_id = COMMAND_TYPE_CONNECTION_REMOVE; break; case CONNECTION_EVENT_UPDATE: case CONNECTION_EVENT_SET_UP: case CONNECTION_EVENT_SET_DOWN: case CONNECTION_EVENT_PRIORITY_CHANGED: case CONNECTION_EVENT_TAGS_CHANGED: command_id = COMMAND_TYPE_CONNECTION_UPDATE; break; case CONNECTION_EVENT_UNDEFINED: case CONNECTION_EVENT_N: default: return; } #endif msg_connection_notify_t msg = {.header = { .message_type = NOTIFICATION_LIGHT, .command_id = OBJECT_TYPE_CONNECTION, .length = 1, .seq_num = 0, }}; fill_connections_command(connection, &msg.payload); commands_notify(forwarder, TOPIC_CONNECTION, (uint8_t *)&msg, sizeof(msg)); } void commands_notify_route(const forwarder_t *forwarder, const fib_entry_t *entry) { const nexthops_t *nexthops = fib_entry_get_nexthops(entry); size_t n = nexthops_get_len(nexthops); msg_route_notify_t *msg = NULL; msg_malloc_list(msg, OBJECT_TYPE_ROUTE, n, 0); if (!msg) return; fill_route_command(entry, &msg->payload); commands_notify(forwarder, TOPIC_ROUTE, (uint8_t *)&msg, sizeof(msg)); } void commands_notify_active_interface_update(const forwarder_t *forwarder, hicn_ip_prefix_t *prefix, netdevice_flags_t flags) { struct { cmd_header_t header; hc_active_interface_t payload; } msg = {.header = { .message_type = NOTIFICATION_LIGHT, .command_id = OBJECT_TYPE_ACTIVE_INTERFACE, .length = 1, .seq_num = 0, }, .payload = {.prefix = *prefix, .interface_types = flags}}; INFO("Notify active interface"); commands_notify(forwarder, TOPIC_ACTIVE_INTERFACE, (uint8_t *)&msg, sizeof(msg)); }