/**
 * Look through the gateway list and select the best gateway
 * depending on the distance to this router
 */
static void gw_default_choose_gateway(void) {
    uint64_t cost_ipv4_threshold = UINT64_MAX;
    uint64_t cost_ipv6_threshold = UINT64_MAX;
    bool eval_cost_ipv4_threshold = false;
    bool eval_cost_ipv6_threshold = false;
    struct gateway_entry *inet_ipv4 = NULL;
    struct gateway_entry *inet_ipv6 = NULL;
    uint64_t cost_ipv4 = UINT64_MAX;
    uint64_t cost_ipv6 = UINT64_MAX;
    struct gateway_entry *gw;
    struct tc_entry *tc;
    bool dual;

    if (olsr_cnf->smart_gw_thresh) {
        /* determine the path cost thresholds */

        gw = olsr_get_ipv4_inet_gateway();
        if (gw) {
            tc = olsr_lookup_tc_entry(&gw->originator);
            if (tc) {
                uint64_t cost = gw_default_weigh_costs(tc->path_cost, gw->uplink, gw->downlink);
                cost_ipv4_threshold = gw_default_calc_threshold(cost);
                eval_cost_ipv4_threshold = true;
            }
        }
        gw = olsr_get_ipv6_inet_gateway();
        if (gw) {
            tc = olsr_lookup_tc_entry(&gw->originator);
            if (tc) {
                uint64_t cost = gw_default_weigh_costs(tc->path_cost, gw->uplink, gw->downlink);
                cost_ipv6_threshold = gw_default_calc_threshold(cost);
                eval_cost_ipv6_threshold = true;
            }
        }
    }

    OLSR_FOR_ALL_GATEWAY_ENTRIES(gw) {
        uint64_t path_cost;
        tc = olsr_lookup_tc_entry(&gw->originator);

        if (!tc) {
            /* gateways should not exist without tc entry */
            continue;
        }

        if (tc->path_cost == ROUTE_COST_BROKEN) {
            /* do not consider nodes with an infinite ETX */
            continue;
        }

        if (!gw->uplink || !gw->downlink) {
            /* do not consider nodes without bandwidth or with a uni-directional link */
            continue;
        }

        /* determine the path cost */
        path_cost = gw_default_weigh_costs(tc->path_cost, gw->uplink, gw->downlink);

        if (!gw_def_finished_ipv4 && gw->ipv4 && gw->ipv4nat == olsr_cnf->smart_gw_allow_nat && path_cost < cost_ipv4
                && (!eval_cost_ipv4_threshold || (path_cost < cost_ipv4_threshold))) {
            inet_ipv4 = gw;
            cost_ipv4 = path_cost;
        }
        if (!gw_def_finished_ipv6 && gw->ipv6 && path_cost < cost_ipv6
                && (!eval_cost_ipv6_threshold || (path_cost < cost_ipv6_threshold))) {
            inet_ipv6 = gw;
            cost_ipv6 = path_cost;
        }
    }
    OLSR_FOR_ALL_GATEWAY_ENTRIES_END(gw)

    /* determine if we found an IPv4 and IPv6 gateway */
    gw_def_finished_ipv4 |= (inet_ipv4 != NULL);
    gw_def_finished_ipv6 |= (inet_ipv6 != NULL);

    /* determine if we are dealing with a dual stack gateway */
    dual = (inet_ipv4 == inet_ipv6) && (inet_ipv4 != NULL);

    if (inet_ipv4) {
        /* we are dealing with an IPv4 or dual stack gateway */
        olsr_set_inet_gateway(&inet_ipv4->originator, true, dual);
    }
    if (inet_ipv6 && !dual) {
        /* we are dealing with an IPv6-only gateway */
        olsr_set_inet_gateway(&inet_ipv6->originator, false, true);
    }

    if ((olsr_cnf->smart_gw_thresh == 0) && gw_def_finished_ipv4 && gw_def_finished_ipv6) {
        /* stop looking for a better gateway */
        olsr_stop_timer(gw_def_timer);
        gw_def_timer = NULL;
    }
}
/**
 * Look through the gateway list and select the best gateway
 * depending on the distance to this router
 */
static void gw_default_choose_gateway(void) {
  uint64_t cost_ipv4_threshold = UINT64_MAX;
  uint64_t cost_ipv6_threshold = UINT64_MAX;
  bool cost_ipv4_threshold_valid = false;
  bool cost_ipv6_threshold_valid = false;
  struct gateway_entry *chosen_gw_ipv4 = NULL;
  struct gateway_entry *chosen_gw_ipv6 = NULL;
  uint64_t chosen_gw_ipv4_costs = UINT64_MAX;
  uint64_t chosen_gw_ipv6_costs = UINT64_MAX;
  struct gateway_entry *gw;
  bool dual = false;

  if (olsr_cnf->smart_gw_thresh) {
    /* determine the path cost thresholds */

    uint64_t cost = gw_default_getcosts(olsr_get_inet_gateway(false));
    if (cost != UINT64_MAX) {
      cost_ipv4_threshold = gw_default_calc_threshold(cost);
      cost_ipv4_threshold_valid = true;
    }

    cost = gw_default_getcosts(olsr_get_inet_gateway(true));
    if (cost != UINT64_MAX) {
      cost_ipv6_threshold = gw_default_calc_threshold(cost);
      cost_ipv6_threshold_valid = true;
    }
  }

  OLSR_FOR_ALL_GATEWAY_ENTRIES(gw) {
    uint64_t gw_cost = gw_default_getcosts(gw);

    if (gw_cost == UINT64_MAX) {
      /* never select a node with infinite costs */
      continue;
    }

    if (gw_def_choose_new_ipv4_gw) {
      bool gw_eligible_v4 = gw->ipv4
          /* && (olsr_cnf->ip_version == AF_INET || olsr_cnf->use_niit) *//* contained in gw_def_choose_new_ipv4_gw */
          && (olsr_cnf->smart_gw_allow_nat || !gw->ipv4nat);
      if (gw_eligible_v4 && gw_cost < chosen_gw_ipv4_costs
          && (!cost_ipv4_threshold_valid || (gw_cost < cost_ipv4_threshold))) {
        chosen_gw_ipv4 = gw;
        chosen_gw_ipv4_costs = gw_cost;
      }
    }

    if (gw_def_choose_new_ipv6_gw) {
      bool gw_eligible_v6 = gw->ipv6
          /* && olsr_cnf->ip_version == AF_INET6 *//* contained in gw_def_choose_new_ipv6_gw */;
      if (gw_eligible_v6 && gw_cost < chosen_gw_ipv6_costs
          && (!cost_ipv6_threshold_valid || (gw_cost < cost_ipv6_threshold))) {
        chosen_gw_ipv6 = gw;
        chosen_gw_ipv6_costs = gw_cost;
      }
    }
  } OLSR_FOR_ALL_GATEWAY_ENTRIES_END(gw)

  /* determine if we should keep looking for IPv4 and/or IPv6 gateways */
  gw_def_choose_new_ipv4_gw = gw_def_choose_new_ipv4_gw && (chosen_gw_ipv4 == NULL);
  gw_def_choose_new_ipv6_gw = gw_def_choose_new_ipv6_gw && (chosen_gw_ipv6 == NULL);

  /* determine if we are dealing with a dual stack gateway */
  dual = chosen_gw_ipv4 && (chosen_gw_ipv4 == chosen_gw_ipv6);

  if (chosen_gw_ipv4) {
    /* we are dealing with an IPv4 or dual stack gateway */
    olsr_set_inet_gateway(&chosen_gw_ipv4->originator, chosen_gw_ipv4_costs, true, dual);
  }
  if (chosen_gw_ipv6 && !dual) {
    /* we are dealing with an IPv6-only gateway */
    olsr_set_inet_gateway(&chosen_gw_ipv6->originator, chosen_gw_ipv6_costs, false, true);
  }

  if ((olsr_cnf->smart_gw_thresh == 0) && !gw_def_choose_new_ipv4_gw && !gw_def_choose_new_ipv6_gw) {
    /* stop looking for a better gateway */
    olsr_stop_timer(gw_def_timer);
    gw_def_timer = NULL;
  }
}