aboutsummaryrefslogtreecommitdiffstats
path: root/hicn-light/src/hicn/strategies/best_path.c
diff options
context:
space:
mode:
Diffstat (limited to 'hicn-light/src/hicn/strategies/best_path.c')
-rw-r--r--hicn-light/src/hicn/strategies/best_path.c353
1 files changed, 353 insertions, 0 deletions
diff --git a/hicn-light/src/hicn/strategies/best_path.c b/hicn-light/src/hicn/strategies/best_path.c
new file mode 100644
index 000000000..9223cc8ac
--- /dev/null
+++ b/hicn-light/src/hicn/strategies/best_path.c
@@ -0,0 +1,353 @@
+/*
+ * 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.
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <hicn/hicn-light/config.h>
+
+#include <hicn/core/forwarder.h>
+#include <hicn/core/nexthops.h>
+#include <hicn/core/strategy.h>
+#include <hicn/core/strategy_vft.h>
+
+#include "best_path.h"
+
+#define MAX_NEXTHOP_COST 100
+#define MAX_RTT_ALLOWED 300.0 // ms
+#define MAX_LOSSES_ALLOWED 0.4 // 40%
+
+#define MAX_PROBING_DURATION 5000 // ticks (= ms)
+#define PROBES_WAINTING_TIME 500 // ticks (= ms)
+#define MAX_PROBES 50
+
+/* Shorthand */
+#define nexthop_state_t strategy_bestpath_nexthop_state_t
+#define strategy_state_t strategy_bestpath_state_t
+#define nexthop_state(nexthops, i) (&nexthops->state[i].bestpath)
+
+// nexthop state functions
+
+static const nexthop_state_t NEXTHOP_STATE_INIT = {
+ .sent_probes = 0,
+ .recv_probes = 0,
+ .rtt_sum = 0,
+};
+
+static inline unsigned int get_sent_probes(nexthop_state_t *state) {
+ return state->sent_probes;
+}
+
+static inline unsigned int inc_sent_probes(nexthop_state_t *state) {
+ state->sent_probes++;
+ return state->sent_probes;
+}
+
+static inline void add_rtt_sample(nexthop_state_t *state, Ticks rtt) {
+ state->recv_probes++;
+ state->rtt_sum += rtt;
+}
+
+static inline unsigned int get_nexthop_cost(nexthop_state_t *state) {
+ if (state->recv_probes == 0)
+ return 100; // we have no info for this nexthop, return max cost
+
+ assert(state->recv_probes <= state->sent_probes);
+
+ double rtt = (double)state->rtt_sum / (double)state->recv_probes;
+ double delay_cost = rtt / MAX_RTT_ALLOWED;
+ if (delay_cost > 1) delay_cost = 1;
+
+ double loss_rate = (double)(state->sent_probes - state->recv_probes) /
+ (double)state->sent_probes;
+ double loss_cost = loss_rate / MAX_LOSSES_ALLOWED;
+ if (loss_cost > 1) loss_cost = 1;
+
+ double total_cost = delay_cost * 0.5 + loss_cost * 0.5;
+ return round(total_cost * 100);
+}
+
+// options functions
+static void bestpath_update_remote_node(strategy_entry_t *entry,
+ nexthops_t *nexthops) {
+ strategy_state_t *state = &entry->state.bestpath;
+ strategy_bestpath_options_t *options = &entry->options.bestpath;
+ off_t offset = nexthops_find(nexthops, state->best_nexthop);
+
+ /* Backup flags and cur_len: because our code is called from
+ * strategy_on_data / check_stop_probing / stop_probing
+ * which does not expect the nexthop flags to be modified.
+ */
+ uint_fast32_t flags = nexthops->flags;
+ size_t cur_len = nexthops_get_curlen(nexthops);
+
+ nexthops_select(nexthops, offset);
+ update_remote_node_paths(nexthops, entry->forwarder, options->local_prefixes);
+
+ /* Restore flags & curlen */
+ nexthops->flags = flags;
+ nexthops->cur_elts = cur_len;
+}
+
+// probing functions
+
+static void start_probing(strategy_entry_t *entry) {
+ strategy_state_t *state = &entry->state.bestpath;
+ if (state->probing_state == PROBING_OFF) {
+ state->probing_state = PROBING_ON;
+ state->probing_time = ticks_now();
+ }
+}
+
+static void stop_probing(strategy_entry_t *entry, nexthops_t *nexthops) {
+ strategy_state_t *state = &entry->state.bestpath;
+ nexthop_t best_nexthop;
+ best_nexthop = state->best_nexthop;
+ unsigned int min_cost = ~0;
+ unsigned current_nexthop_cost = ~0;
+
+ nexthops_enumerate(nexthops, i, nexthop, {
+ unsigned int cost = get_nexthop_cost(nexthop_state(nexthops, i));
+ if (cost < min_cost) {
+ min_cost = cost;
+ best_nexthop = nexthop;
+ }
+ if (nexthop == state->best_nexthop) current_nexthop_cost = cost;
+
+ nexthops->state[i].bestpath = NEXTHOP_STATE_INIT;
+ });
+
+ if (best_nexthop != state->best_nexthop) {
+ if (current_nexthop_cost > min_cost) {
+ // update best face
+ state->best_nexthop = best_nexthop;
+ }
+ }
+
+ // always update remote node
+ bestpath_update_remote_node(entry, nexthops);
+
+ state->probing_state = PROBING_OFF;
+ delete_all_probes(state->pg);
+}
+
+static void check_stop_probing(strategy_entry_t *entry, nexthops_t *nexthops) {
+ strategy_state_t *state = &entry->state.bestpath;
+ if (state->probing_state == PROBING_OFF) return;
+
+ if (state->probing_state == PROBING_ON) {
+ Ticks probing_duration = ticks_now() - state->probing_time;
+ if (probing_duration >= MAX_PROBING_DURATION) {
+ state->probing_state = PROBING_ENDING;
+ state->probing_time = ticks_now();
+ }
+ return;
+ }
+
+ if (state->probing_state == SENT_MAX_PROBES) {
+ state->probing_state = PROBING_ENDING;
+ state->probing_time = ticks_now();
+ return;
+ }
+
+ if (state->probing_state == PROBING_ENDING) {
+ Ticks ending_duration = ticks_now() - state->probing_time;
+ if (ending_duration >= PROBES_WAINTING_TIME) stop_probing(entry, nexthops);
+ }
+}
+
+static void send_probes(strategy_entry_t *entry, nexthops_t *nexthops,
+ const msgbuf_t *msgbuf) {
+ strategy_state_t *state = &entry->state.bestpath;
+
+ bool sent_max_probes = false;
+ nexthops_enumerate(nexthops, i, nexthop, {
+ if (get_sent_probes(nexthop_state(nexthops, i)) < MAX_PROBES) {
+ int res = generate_probe(state->pg, msgbuf, entry->forwarder, nexthop);
+ if (res >= 0) inc_sent_probes(nexthop_state(nexthops, i));
+ } else {
+ sent_max_probes = true;
+ }
+ });
+
+ if (sent_max_probes) {
+ state->probing_state = SENT_MAX_PROBES;
+ check_stop_probing(entry, nexthops);
+ }
+}
+
+static void init_strategy_state(strategy_state_t *state) {
+ state->best_nexthop = ~0;
+ state->probing_state = PROBING_OFF;
+ state->pg = create_probe_generator();
+}
+
+// strategy functions
+static int strategy_bestpath_initialize(strategy_entry_t *entry,
+ const void *forwarder) {
+ if (entry->forwarder == NULL) {
+ srand((unsigned int)time(NULL));
+ entry->forwarder = forwarder;
+ init_strategy_state(&entry->state.bestpath);
+ } else {
+ strategy_state_t *state = &entry->state.bestpath;
+ if (!state->pg) {
+ // the previous strategy was a different one
+ init_strategy_state(state);
+ } else {
+ // all set, start probing
+ start_probing(entry);
+ }
+ }
+ return 0;
+}
+
+static int strategy_bestpath_finalize(strategy_entry_t *entry) {
+ strategy_state_t *state = &entry->state.bestpath;
+ free_local_prefixes(entry->options.bestpath.local_prefixes);
+ destroy_probe_generator(state->pg);
+ return 0;
+}
+
+static int strategy_bestpath_add_nexthop(strategy_entry_t *entry,
+ nexthops_t *nexthops, off_t offset) {
+ // reset the strategy state
+ nexthops->state[offset].bestpath = NEXTHOP_STATE_INIT;
+ return 0;
+}
+
+static int strategy_bestpath_remove_nexthop(strategy_entry_t *entry,
+ nexthops_t *nexthops,
+ off_t offset) {
+ /* Nothing to do */
+ return 0;
+}
+
+static nexthops_t *strategy_bestpath_lookup_nexthops(strategy_entry_t *entry,
+ nexthops_t *nexthops,
+ const msgbuf_t *msgbuf) {
+ size_t nexthops_len = nexthops_get_curlen(nexthops);
+ if (nexthops_len == 0) {
+ // nexthops is empty, return
+ return nexthops;
+ }
+
+ strategy_state_t *state = &entry->state.bestpath;
+ off_t best_nexthop_offset = nexthops_find(nexthops, state->best_nexthop);
+
+ // TODO explain the purpose of this test
+ if (nexthops_len == 1) {
+ nexthop_t nh = nexthops_get_one(nexthops);
+ if (state->best_nexthop != nh) {
+ state->best_nexthop = nh;
+ bestpath_update_remote_node(entry, nexthops);
+ }
+ return nexthops;
+ }
+
+ if (state->best_nexthop == ~0 || best_nexthop_offset == INVALID_NEXTHOP) {
+ state->best_nexthop = nexthops_get_one(nexthops);
+ best_nexthop_offset = nexthops_find(nexthops, state->best_nexthop);
+ bestpath_update_remote_node(entry, nexthops);
+ // we have probe only in case the number of face is > 1
+ start_probing(entry);
+ // bestpath_update_remote_node sets the nexthops. in case of probing we want
+ // to send the packets on all faces, so we reset the nexthops here
+ nexthops_reset(nexthops);
+ }
+
+ if (state->probing_state == PROBING_ON) {
+ // send a probe for each interest received
+ send_probes(entry, nexthops, msgbuf);
+
+ uint32_t suffix = hicn_name_get_suffix(msgbuf_get_name(msgbuf));
+ if (suffix >= MIN_PROBE_SUFFIX && suffix <= MAX_PROBE_SUFFIX) {
+ // this packet is a probe from the transport, so register it
+ Ticks time = get_probe_send_time(state->pg, suffix);
+ if (time == 0) {
+ // a probe with the same seq number is not pending, send the packet
+ // the stats for this probe will be collected by the transport
+ register_probe(state->pg, suffix);
+ } else {
+ // this probe is already pending. avoid duplicates and drop it
+ nexthops->flags = ~0;
+ nexthops->cur_elts = 0;
+ }
+ }
+ } else {
+ // we are not probing anymore. if in probing ending state (wait for probes
+ // to come back) keep replicating traffic, otherwise and on best path
+ if (state->probing_state != PROBING_ENDING)
+ nexthops_select(nexthops, best_nexthop_offset);
+ }
+
+ // in case we are still probing send all interest on all paths
+ // so do not select any next hop.
+ // XXX in this transition phase should we replicate also at the
+ // server side?
+ return nexthops;
+}
+
+static int strategy_bestpath_on_data(strategy_entry_t *entry,
+ nexthops_t *nexthops,
+ const nexthops_t *data_nexthops,
+ const msgbuf_t *msgbuf,
+ Ticks pitEntryCreation,
+ Ticks objReception) {
+ strategy_state_t *state = &entry->state.bestpath;
+ if (state->probing_state == PROBING_OFF) return 0;
+
+ uint32_t seq = hicn_name_get_suffix(msgbuf_get_name(msgbuf));
+ if (seq >= MIN_PROBE_SUFFIX && seq <= MAX_PROBE_SUFFIX) {
+ if (pitEntryCreation != 0) {
+ // this is not a probe sent by the forwader. do not use it in the probing
+ // statisitcs but remove it from the map if it exists
+ delete_probe(state->pg, seq);
+ return 0;
+ }
+
+ Ticks send_time = get_probe_send_time(state->pg, seq);
+ if (send_time != 0) {
+ Ticks rtt = ticks_now() - send_time;
+ delete_probe(state->pg, seq);
+ nexthops_enumerate(data_nexthops, i, nexthop, {
+ off_t pos = nexthops_find(nexthops, nexthop);
+ add_rtt_sample(nexthop_state(nexthops, pos), rtt);
+ });
+ }
+ }
+
+ check_stop_probing(entry, nexthops);
+
+ return 0;
+}
+
+static int strategy_bestpath_on_timeout(strategy_entry_t *entry,
+ nexthops_t *nexthops,
+ const nexthops_t *timeout_nexthops) {
+ /* Nothing to do */
+ return 0;
+}
+
+#undef nexthop_state_t
+#undef strategy_state_t
+
+DECLARE_STRATEGY(bestpath);
+
+#undef nexthop_state_t
+#undef strategy_state_t