From 15ad172a847fa667c57a4594ef4158405db9a984 Mon Sep 17 00:00:00 2001 From: Angelo Mantellini Date: Tue, 31 Mar 2020 17:50:43 +0200 Subject: [HICN-554] hicn-light refactoring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I36f2d393741d4502ce14d3791158e43e3e9cd4cf Signed-off-by: Jordan Augé --- hicn-light/src/hicn/core/fib_entry.c | 503 +++++++++++++++++++++++++++++++++++ 1 file changed, 503 insertions(+) create mode 100644 hicn-light/src/hicn/core/fib_entry.c (limited to 'hicn-light/src/hicn/core/fib_entry.c') diff --git a/hicn-light/src/hicn/core/fib_entry.c b/hicn-light/src/hicn/core/fib_entry.c new file mode 100644 index 000000000..5c6e28d5b --- /dev/null +++ b/hicn-light/src/hicn/core/fib_entry.c @@ -0,0 +1,503 @@ +/* + * 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. + */ + +#include + +#include +#include +//#include +#include +#include +#include + +#ifdef WITH_MAPME +#include +#endif /* WITH_MAPME */ + +#ifdef WITH_POLICY +#include +#include + +#ifdef WITH_MAPME +#include +#endif /* WITH_MAPME */ + +#endif /* WITH_POLICY */ + +#ifdef WITH_PREFIX_STATS +#include +#endif /* WITH_PREFIX_STATS */ + + +fib_entry_t * +fib_entry_create(Name *name, strategy_type_t strategy_type, + strategy_options_t * strategy_options, const forwarder_t * forwarder) +{ + + assert(name); + assert(forwarder); + + fib_entry_t * entry = malloc(sizeof(fib_entry_t)); + if (!entry) + goto ERR_MALLOC; + + entry->name = name_Acquire(name); + + // XXX TODO strategy type might be undefined. We need a default strategy + // specified somewhere in the configuration. + fib_entry_set_strategy(entry, strategy_type, strategy_options); + +#ifdef WITH_MAPME + entry->user_data = NULL; + entry->user_data_release = NULL; +#endif /* WITH_MAPME */ + + entry->forwarder = forwarder; + +#ifdef WITH_POLICY + entry->policy = POLICY_NONE; +#endif /* WITH_POLICY */ + +#ifdef WITH_PREFIX_STATS + entry->prefix_stats = PREFIX_STATS_EMPTY; + entry->prefix_counters = PREFIX_COUNTERS_EMPTY; +#endif /* WITH_PREFIX_STATS */ + + return entry; + +ERR_MALLOC: + return NULL; +} + +void +fib_entry_free(fib_entry_t * entry) +{ + assert(entry); + + name_Release(&entry->name); +#ifdef WITH_MAPME + if (entry->user_data) + entry->user_data_release(&entry->user_data); +#endif /* WITH_MAPME */ + free(entry); +} + +// XXX TODO DUPLICATE +void +fib_entry_set_strategy(fib_entry_t * entry, strategy_type_t strategy_type, + strategy_options_t * strategy_options) +{ + if (STRATEGY_TYPE_VALID(strategy_type)) { + entry->strategy.type = strategy_type; + if (strategy_options) + entry->strategy.options = *strategy_options; + strategy_vft[strategy_type]->initialize(&entry->strategy); + } +} + +#ifdef WITH_POLICY + +nexthops_t * +fib_entry_filter_nexthops(fib_entry_t * entry, nexthops_t * nexthops, + unsigned ingress_id, bool prefer_local) +{ + assert(entry); + assert(nexthops); + + /* Filter out ingress, down & administrative down faces */ + const connection_table_t * table = forwarder_get_connection_table(entry->forwarder); + connection_t * conn; + unsigned nexthop, i; + uint_fast32_t flags; + + policy_t policy = fib_entry_get_policy(entry); + + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, nexthop == ingress_id); + nexthops_disable_if(nexthops, i, + (connection_get_admin_state(conn) == FACE_STATE_DOWN)); + nexthops_disable_if(nexthops, i, + (connection_get_state(conn) == FACE_STATE_DOWN)); + }); + + if (prefer_local) { + /* Backup flags */ + flags = nexthops->flags; + + /* Filter local */ + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, (!connection_is_local(conn))); + }); + + /* Local faces have priority */ + if (nexthops_get_curlen(nexthops) > 0) + return nexthops; + + nexthops->flags = flags; + } + + /* Filter out local */ + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, (connection_is_local(conn))); + + /* Policy filtering : next hops */ + nexthops_disable_if(nexthops, i, + (policy.tags[POLICY_TAG_WIRED].state == POLICY_STATE_REQUIRE) && + (!connection_has_tag(conn, POLICY_TAG_WIRED))); + nexthops_disable_if(nexthops, i, + (policy.tags[POLICY_TAG_WIRED].state == POLICY_STATE_PROHIBIT) && + (connection_has_tag(conn, POLICY_TAG_WIRED))); + nexthops_disable_if(nexthops, i, + (policy.tags[POLICY_TAG_WIFI].state == POLICY_STATE_REQUIRE) && + (!connection_has_tag(conn, POLICY_TAG_WIFI))); + nexthops_disable_if(nexthops, i, + (policy.tags[POLICY_TAG_WIFI].state == POLICY_STATE_PROHIBIT) && + (connection_has_tag(conn, POLICY_TAG_WIFI))); + nexthops_disable_if(nexthops, i, + (policy.tags[POLICY_TAG_CELLULAR].state == POLICY_STATE_REQUIRE) && + (!connection_has_tag(conn, POLICY_TAG_CELLULAR))); + nexthops_disable_if(nexthops, i, + (policy.tags[POLICY_TAG_CELLULAR].state == POLICY_STATE_PROHIBIT) && + (connection_has_tag(conn, POLICY_TAG_CELLULAR))); + nexthops_disable_if(nexthops, i, + (policy.tags[POLICY_TAG_TRUSTED].state == POLICY_STATE_REQUIRE) && + (!connection_has_tag(conn, POLICY_TAG_TRUSTED))); + nexthops_disable_if(nexthops, i, + (policy.tags[POLICY_TAG_TRUSTED].state == POLICY_STATE_PROHIBIT) && + (connection_has_tag(conn, POLICY_TAG_TRUSTED))); + }); + + if (nexthops_get_curlen(nexthops) == 0) + return nexthops; + + /* We have at least one matching next hop, implement heuristic */ + + /* + * As VPN connections might trigger duplicate uses of one interface, we start + * by filtering out interfaces based on trust status. + */ + flags = nexthops->flags; + + if ((policy.tags[POLICY_TAG_TRUSTED].state == POLICY_STATE_REQUIRE) || + (policy.tags[POLICY_TAG_TRUSTED].state == POLICY_STATE_PREFER)) { + + /* Try to filter out NON TRUSTED faces */ + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, + (!connection_has_tag(conn, POLICY_TAG_TRUSTED))); + }); + + if ((nexthops_get_curlen(nexthops) == 0) && (policy.tags[POLICY_TAG_TRUSTED].state == POLICY_STATE_REQUIRE)) { + return nexthops; + } + + } else { + /* Try to filter out TRUSTED faces */ + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, + (connection_has_tag(conn, POLICY_TAG_TRUSTED))); + }); + } + + if (nexthops_get_curlen(nexthops) == 0) + nexthops->flags = flags; + + /* Other preferences */ + if (policy.tags[POLICY_TAG_WIRED].state == POLICY_STATE_AVOID) { + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, + connection_has_tag(conn, POLICY_TAG_WIRED)); + }); + if (nexthops_get_curlen(nexthops) == 0) + nexthops->flags = flags; + } + if (policy.tags[POLICY_TAG_WIFI].state == POLICY_STATE_AVOID) { + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, + connection_has_tag(conn, POLICY_TAG_WIFI)); + }); + if (nexthops_get_curlen(nexthops) == 0) + nexthops->flags = flags; + } + if (policy.tags[POLICY_TAG_CELLULAR].state == POLICY_STATE_AVOID) { + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, + connection_has_tag(conn, POLICY_TAG_CELLULAR)); + }); + if (nexthops_get_curlen(nexthops) == 0) + nexthops->flags = flags; + } + + if (policy.tags[POLICY_TAG_WIRED].state == POLICY_STATE_PREFER) { + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, + !connection_has_tag(conn, POLICY_TAG_WIRED)); + }); + if (nexthops_get_curlen(nexthops) == 0) + nexthops->flags = flags; + } + if (policy.tags[POLICY_TAG_WIFI].state == POLICY_STATE_PREFER) { + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, + !connection_has_tag(conn, POLICY_TAG_WIFI)); + }); + if (nexthops_get_curlen(nexthops) == 0) + nexthops->flags = flags; + } + if (policy.tags[POLICY_TAG_CELLULAR].state == POLICY_STATE_PREFER) { + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, + !connection_has_tag(conn, POLICY_TAG_CELLULAR)); + }); + if (nexthops_get_curlen(nexthops) == 0) + nexthops->flags = flags; + } + + /* Priority */ + uint32_t max_priority = 0; + nexthops_foreach(nexthops, nexthop, { + conn = connection_table_at(table, nexthop); + uint32_t priority = connection_get_priority(conn); + if (priority > max_priority) + max_priority = priority; + }); + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, + connection_get_priority(conn) < max_priority); + }); + + return nexthops; +} + +/* + * Update available next hops following policy update. + * + * The last nexthop parameter is only used if needed, otherwise the pointer to + * fib entry is returned to avoid an useless copy + */ +nexthops_t * +fib_entry_get_available_nexthops(fib_entry_t * entry, unsigned ingress_id, nexthops_t * new_nexthops) +{ + connection_table_t * table = forwarder_get_connection_table(entry->forwarder); + + /* + * Give absolute preference to local faces, with no policy, unless + * ingress_id == ~0, which means we are searching faces on which to + * advertise our prefix + */ + if (ingress_id == ~0) { + assert(new_nexthops); + /* We create a nexthop structure based on connections */ + // XXX This should be done close to where it is needed + connection_t * connection; + connection_table_foreach(table, connection, { + new_nexthops->elts[nexthops_get_len(new_nexthops)] = connection_table_get_connection_id(table, connection); + nexthops_inc(new_nexthops); + }); + + return fib_entry_filter_nexthops(entry, new_nexthops, ingress_id, false); + } + + return fib_entry_filter_nexthops(entry, fib_entry_get_nexthops(entry), ingress_id, true); +} + +policy_t +fib_entry_get_policy(const fib_entry_t * entry) +{ + return entry->policy; +} + +void +fib_entry_set_policy(fib_entry_t * entry, policy_t policy) +{ + entry->policy = policy; + +#ifdef WITH_MAPME + /* + * Skip entries that do not correspond to a producer ( / have a locally + * served prefix / have no local connection as next hop) + */ + if (!fib_entry_has_local_nexthop(entry)) + return; + mapme_t * mapme = forwarder_get_mapme(entry->forwarder); + mapme_send_to_all_nexthops(mapme, entry); +#endif /* WITH_MAPME */ +} + +#endif /* WITH_POLICY */ + +void +fib_entry_nexthops_add(fib_entry_t * entry, unsigned nexthop) +{ + nexthops_add(fib_entry_get_nexthops(entry), nexthop); + // XXX TODO + strategy_vft[entry->strategy.type]->add_nexthop(&entry->strategy, nexthop, NULL); +} + +void +fib_entry_nexthops_remove(fib_entry_t * entry, unsigned nexthop) +{ + nexthops_remove(fib_entry_get_nexthops(entry), nexthop); + // XXX TODO + strategy_vft[entry->strategy.type]->remove_nexthop(&entry->strategy, nexthop, NULL); +} + +const nexthops_t * +fib_entry_get_nexthops_from_strategy(fib_entry_t * entry, + const msgbuf_t * msgbuf, bool is_retransmission) +{ + assert(entry); + assert(msgbuf); + + const prefix_stats_mgr_t * mgr = forwarder_get_prefix_stats_mgr(entry->forwarder); + assert(mgr); + + /* Filtering */ + nexthops_t * nexthops = fib_entry_get_available_nexthops(entry, + msgbuf_get_connection_id(msgbuf), NULL); + if (nexthops_get_curlen(nexthops) == 0) + return nexthops; + +#ifdef WITH_PREFIX_STATS + /* + * Update statistics about loss rates. We only detect losses upon + * retransmissions, and assume for the computation that the candidate set of + * output faces is the same as previously (i.e. does not take into account + * event such as face up/down, policy update, etc. Otherwise we would need to + * know what was the previous choice ! + */ + if (is_retransmission) + prefix_stats_on_retransmission(mgr, &entry->prefix_counters, nexthops); +#endif /* WITH_PREFIX_STATS */ + + /* + * NOTE: We might want to call a forwarding strategy even with no nexthop to + * take a fallback decision. + */ + if (nexthops_get_curlen(nexthops) == 0) + return nexthops; + +#ifdef WITH_POLICY + /* + * If multipath is disabled, we don't offer much choice to the forwarding + * strategy, but still go through it for accounting purposes. + */ + policy_t policy = fib_entry_get_policy(entry); + if ((policy.tags[POLICY_TAG_MULTIPATH].state == POLICY_STATE_PROHIBIT) || + (policy.tags[POLICY_TAG_MULTIPATH].state != POLICY_STATE_AVOID)) { + nexthops_select_one(nexthops); + } +#endif /* WITH_POLICY */ + + return strategy_vft[entry->strategy.type]->lookup_nexthops(&entry->strategy, + nexthops, msgbuf); +} + +void +fib_entry_on_data(fib_entry_t * entry, + const nexthops_t * nexthops, const msgbuf_t * msgbuf, + Ticks pitEntryCreation, Ticks objReception) +{ + assert(entry); + assert(nexthops); + assert(msgbuf); + +#ifdef WITH_PREFIX_STATS + const prefix_stats_mgr_t * mgr = forwarder_get_prefix_stats_mgr(entry->forwarder); + Ticks rtt = objReception - pitEntryCreation; + prefix_stats_on_data(mgr, &entry->prefix_stats, &entry->prefix_counters, + nexthops, msgbuf, rtt); +#endif /* WITH_PREFIX_STATS */ + + strategy_vft[entry->strategy.type]->on_data(&entry->strategy, nexthops, msgbuf, pitEntryCreation, objReception); +} + +void +fib_entry_on_timeout(fib_entry_t * entry, const nexthops_t * nexthops) +{ + assert(entry); + assert(nexthops); + +#ifdef WITH_PREFIX_STATS + const prefix_stats_mgr_t * mgr = forwarder_get_prefix_stats_mgr(entry->forwarder); + prefix_stats_on_timeout(mgr, &entry->prefix_counters, nexthops); +#endif /* WITH_PREFIX_STATS */ + + strategy_vft[entry->strategy.type]->on_timeout(&entry->strategy, nexthops); +} + +Name * +fib_entry_get_prefix(const fib_entry_t * entry) +{ + assert(entry); + + return entry->name; +} + + +/* + * Return true if we have at least one local connection as next hop + */ +bool +fib_entry_has_local_nexthop(const fib_entry_t * entry) +{ + connection_table_t * table = forwarder_get_connection_table(entry->forwarder); + + unsigned nexthop; + nexthops_foreach(fib_entry_get_nexthops(entry), nexthop, { + const connection_t * conn = connection_table_at(table, nexthop); + /* Ignore non-local connections */ + if (!connection_is_local(conn)) + continue; + return true; + }); + return false; +} + +#ifdef WITH_MAPME + +void * +fib_entry_get_user_data(const fib_entry_t * entry) +{ + assert(entry); + + return entry->user_data; +} + +void +fib_entry_set_user_data(fib_entry_t * entry, const void * user_data, + void (*user_data_release)(void **)) +{ + assert(entry); + assert(user_data); + assert(user_data_release); + + entry->user_data = (void *)user_data; + entry->user_data_release = user_data_release; +} + +#endif /* WITH_MAPME */ -- cgit 1.2.3-korg