Merge branch 'master' into mpr_rework
[oonf.git] / src-plugins / nhdp / nhdp / nhdp_domain.c
index 5681b6e..defcaf6 100644 (file)
@@ -67,10 +67,13 @@ static void _apply_mpr(struct nhdp_domain *domain,
     const char *mpr_name, uint8_t willingness);
 static void _remove_mpr(struct nhdp_domain *);
 
-static void _cb_update_everyone_mpr(void);
+static void _cb_update_everyone_mpr(struct nhdp_domain *);
 
-static void _recalculate_neighbor_metric(struct nhdp_domain *domain,
+static bool _recalculate_neighbor_metrics(struct nhdp_domain *domain);
+static bool _recalculate_neighbor_metric(struct nhdp_domain *domain,
         struct nhdp_neighbor *neigh);
+static bool _recalculate_mpr_set(struct nhdp_domain *domain);
+
 static const char *_link_to_string(struct nhdp_metric_str *, uint32_t);
 static const char *_path_to_string(struct nhdp_metric_str *, uint32_t, uint8_t);
 static const char *_int_to_string(struct nhdp_metric_str *, struct nhdp_link *);
@@ -460,85 +463,43 @@ nhdp_domain_process_metric_2hoptlv(struct nhdp_domain *domain,
 }
 
 /**
- * Neighborhood changed in terms of metrics or connectivity.
  * This will trigger a MPR set recalculation.
+ * @param force_change force a MPR recalculation and dijkstra trigger
+ *    even if the existing neighbor set didn't change
  */
 void
-nhdp_domain_neighborhood_changed(void) {
+nhdp_domain_recalculate_mpr(bool force_change) {
   struct nhdp_domain_listener *listener;
   struct nhdp_domain *domain;
-  struct nhdp_neighbor *neigh;
-  
-  list_for_each_element(&_domain_list, domain, _node) {
-    list_for_each_element(nhdp_db_get_neigh_list(), neigh, _global_node) {
-      _recalculate_neighbor_metric(domain, neigh);
-    }
-
-    if (domain->mpr->update_mpr != NULL) {
-      domain->mpr->update_mpr();
-    }
-  }
-
-  // TODO: flooding mpr ?
-  // (Why do we need to consider flooding MPRs here?)
-
-  list_for_each_element(&_domain_listener_list, listener, _node) {
-    if (listener->update) {
-      listener->update(NULL);
-    }
-  }
 
-  /* check if we still have routing MPR selectors */
-  OONF_DEBUG(LOG_NHDP, "Checking if we still have routing MPR selectors");
-  _node_is_selected_as_mpr = false;
+  bool changed_metric[NHDP_MAXIMUM_DOMAINS];
+  bool changed_mpr[NHDP_MAXIMUM_DOMAINS];
 
-  list_for_each_element(&_domain_list, domain, _node) {
-    list_for_each_element(nhdp_db_get_neigh_list(), neigh, _global_node) {
-      if (nhdp_domain_get_neighbordata(domain, neigh)->local_is_mpr) {
-        _node_is_selected_as_mpr = true;
-        return;
-      }
-    }
-  }
-}
-
-/**
- * One neighbor changed in terms of metrics or connectivity.
- * This will trigger a MPR set recalculation.
- * @param neigh neighbor where the changed happened
- */
-void
-nhdp_domain_neighbor_changed(struct nhdp_neighbor *neigh) {
-  struct nhdp_domain_listener *listener;
-  struct nhdp_domain *domain;
+  memset(changed_metric, 0, sizeof(changed_metric));
+  memset(changed_mpr, 0, sizeof(changed_mpr));
   
-
+  OONF_DEBUG(LOG_NHDP, "Recalculating MPR set?");
   list_for_each_element(&_domain_list, domain, _node) {
-    _recalculate_neighbor_metric(domain, neigh);
+    /* recalculate the neighbor metrics and see if they changed */
+    changed_metric[domain->index] = _recalculate_neighbor_metrics(domain);
 
-    if (domain->mpr->update_mpr != NULL) {
-      domain->mpr->update_mpr();
-    }
-  }
+    if (changed_metric[domain->index]) {
+      /* recalculate routing MPRs */
+      changed_mpr[domain->index] = _recalculate_mpr_set(domain);
 
-  // TODO: flooding mpr ?
-  // (Why do we need to consider flooding MPRs here?)
-
-  list_for_each_element(&_domain_listener_list, listener, _node) {
-    if (listener->update) {
-      listener->update(neigh);
+      if (domain->ext == _flooding_domain.ext && domain->mpr->update_mpr) {
+        /* recalculating flooding MPR */
+        domain->mpr->update_mpr(&_flooding_domain);
+      }
     }
-  }
 
-  /* check if we still have routing MPR selectors */
-  OONF_DEBUG(LOG_NHDP, "Checking if we still have routing MPR selectors");
-  _node_is_selected_as_mpr = false;
-
-  list_for_each_element(&_domain_list, domain, _node) {
-    list_for_each_element(nhdp_db_get_neigh_list(), neigh, _global_node) {
-      if (nhdp_domain_get_neighbordata(domain, neigh)->local_is_mpr) {
-        _node_is_selected_as_mpr = true;
-        return;
+    list_for_each_element(&_domain_listener_list, listener, _node) {
+      /* trigger domain listeners */
+      if ((force_change || changed_metric[domain->index]) && listener->metric_update) {
+        listener->metric_update(domain);
+      }
+      if ((force_change || changed_mpr[domain->index]) && listener->mpr_update) {
+        listener->mpr_update(domain);
       }
     }
   }
@@ -587,7 +548,7 @@ nhdp_domain_process_mprtypes_tlv(
 
 /**
  * Process an in MPR tlv for a NHDP link
- * @param mprtypes list of extenstions for MPR
+ * @param mprtypes list of extensions for MPR
  * @param mprtypes_size length of mprtypes array
  * @param neigh NHDP neighbor
  * @param tlv MPR tlv context
@@ -633,13 +594,23 @@ nhdp_domain_process_mpr_tlv(uint8_t *mprtypes, size_t mprtypes_size,
     OONF_DEBUG(LOG_NHDP_R, "Routing MPR for neighbor in domain %u: %s",
         domain->ext, nhdp_domain_get_neighbordata(domain, neigh)->local_is_mpr ? "true" : "false");
   }
+
+  _node_is_selected_as_mpr = false;
+  list_for_each_element(&_domain_list, domain, _node) {
+    list_for_each_element(nhdp_db_get_neigh_list(), neigh, _global_node) {
+      if (nhdp_domain_get_neighbordata(domain, neigh)->local_is_mpr) {
+        _node_is_selected_as_mpr = true;
+        return;
+      }
+    }
+  }
 }
 
 /**
  * Process an in Willingness tlv and put values into
  * temporary storage in MPR handler object. Call
  * nhdp_domain_store_willingness to permanently store them later.
- * @param mprtypes list of extenstions for MPR
+ * @param mprtypes list of extensions for MPR
  * @param mprtypes_size length of mprtypes array
  * @param tlv Willingness tlv context
  */
@@ -839,6 +810,13 @@ nhdp_domain_set_flooding_mpr(const char *mpr_name, uint8_t willingness) {
   _apply_mpr(&_flooding_domain, mpr_name, willingness);
 }
 
+/**
+ * @return the virtual flooding domain
+ */
+const struct nhdp_domain *
+nhdp_domain_get_flooding_domain(void) {
+  return &_flooding_domain;
+}
 /**
  * Sets the incoming metric of a link. This is the only function external
  * code should use to commit the calculated metric values to the nhdp db.
@@ -866,8 +844,7 @@ nhdp_domain_set_incoming_metric(struct nhdp_domain_metric *metric,
 }
 
 /**
- * get list of nhdp domains
- * @return domain list
+ * @return list of domains
  */
 struct list_entity *
 nhdp_domain_get_list(void) {
@@ -875,8 +852,7 @@ nhdp_domain_get_list(void) {
 }
 
 /**
- * get list of nhdp domain listeners for metric/mpr changes
- * @return listener list
+ * @return list of event listeners for domain metric/mpr triggers
  */
 struct list_entity *
 nhdp_domain_get_listener_list(void) {
@@ -884,35 +860,89 @@ nhdp_domain_get_listener_list(void) {
 }
 
 /**
- * get current NHDP flooding domain
- * @return flooding domain
+ * Recalculate the neighbor metrics of a NHDP domain
+ * @param domain NHDP domain
+ * @return if neighbor metric or two-hop link metric changed
  */
-const struct nhdp_domain *
-nhdp_domain_get_flooding(void) {
-  return &_flooding_domain;
+static bool
+_recalculate_neighbor_metrics(struct nhdp_domain *domain) {
+  struct nhdp_neighbor *neigh;
+  bool changed;
+
+  /* recalculate the neighbor metrics and see if they changed */
+  changed = false;
+  list_for_each_element(nhdp_db_get_neigh_list(), neigh, _global_node) {
+    changed |= _recalculate_neighbor_metric(domain, neigh);
+  }
+
+  if (changed) {
+    OONF_DEBUG(LOG_NHDP, "Domain ext %u metric changed", domain->ext);
+  }
+  return changed;
+}
+
+/**
+ * Recalculate the MPR set of a NHDP domain
+ * @param domain nhdp domain
+ * @return true if the MPR set changed
+ */
+static bool
+_recalculate_mpr_set(struct nhdp_domain *domain) {
+  struct nhdp_neighbor *neigh;
+  struct nhdp_neighbor_domaindata *neighdata;
+
+  if (!domain->mpr->update_mpr) {
+    return false;
+  }
+
+  /* remember old MPR set */
+  list_for_each_element(nhdp_db_get_neigh_list(), neigh, _global_node) {
+    neighdata = nhdp_domain_get_neighbordata(domain, neigh);
+    neighdata->_neigh_was_mpr = neighdata->neigh_is_mpr;
+  }
+
+  /* update MPR set */
+  domain->mpr->update_mpr(domain);
+
+  /* check for changes */
+  list_for_each_element(nhdp_db_get_neigh_list(), neigh, _global_node) {
+    neighdata = nhdp_domain_get_neighbordata(domain, neigh);
+    if (neighdata->_neigh_was_mpr != neighdata->neigh_is_mpr) {
+      OONF_DEBUG(LOG_NHDP, "Domain ext %u MPR set changed", domain->ext);
+      return true;
+    }
+  }
+  return false;
 }
 
 /**
  * Recalculate the 'best link/metric' values of a neighbor
+ * and check for two-hop outgoing link metric changes
  * @param domain NHDP domain
  * @param neigh NHDP neighbor
+ * @param neighdata NHDP neighbor domaindata
+ * @return true neighbor metric or the two-hop link metrics changed
  */
-static void
+static bool
 _recalculate_neighbor_metric(
     struct nhdp_domain *domain,
     struct nhdp_neighbor *neigh) {
   struct nhdp_link *lnk;
   struct nhdp_link_domaindata *linkdata;
+  struct nhdp_l2hop *l2hop;
+  struct nhdp_l2hop_domaindata *l2hopdata;
   struct nhdp_neighbor_domaindata *neighdata;
-  struct nhdp_metric oldmetric;
+  uint32_t old_outgoing;
+  bool changed;
 #ifdef OONF_LOG_DEBUG_INFO
   struct netaddr_str nbuf;
 #endif
 
   neighdata = nhdp_domain_get_neighbordata(domain, neigh);
+  changed = false;
 
-  /* copy old metric value */
-  memcpy(&oldmetric, &neighdata->metric, sizeof(oldmetric));
+    /* copy old metric value */
+  old_outgoing = neighdata->metric.out;
 
   /* reset metric */
   neighdata->metric.in = RFC7181_METRIC_INFINITE;
@@ -927,6 +957,10 @@ _recalculate_neighbor_metric(
 
   /* get best metric */
   list_for_each_element(&neigh->_links, lnk, _neigh_node) {
+    if (lnk->status != NHDP_LINK_SYMMETRIC) {
+      continue;
+    }
+
     linkdata = nhdp_domain_get_linkdata(domain, lnk);
     if (linkdata->metric.out < neighdata->metric.out) {
       OONF_DEBUG(LOG_NHDP, "Link on if %s has better outgoing metric: %u",
@@ -942,6 +976,14 @@ _recalculate_neighbor_metric(
               linkdata->metric.in);
       neighdata->metric.in = linkdata->metric.in;
     }
+
+    /* check for changes in outgoing 2-hop metrics */
+    avl_for_each_element(&lnk->_2hop, l2hop, _link_node) {
+      l2hopdata = nhdp_domain_get_l2hopdata(domain, l2hop);
+
+      changed |= l2hopdata->metric.out != l2hopdata->_last_used_outgoing_metric;
+      l2hopdata->_last_used_outgoing_metric = l2hopdata->metric.out;
+    }
   }
 
   if (neighdata->best_link != NULL) {
@@ -950,11 +992,7 @@ _recalculate_neighbor_metric(
     neighdata->best_link_ifindex =
         nhdp_interface_get_if_listener(neighdata->best_link->local_if)->data->index;
   }
-
-  if (memcmp(&oldmetric, &neighdata->metric, sizeof(oldmetric)) != 0) {
-    /* mark metric as updated */
-    domain->neighbor_metric_changed = true;
-  }
+  return changed || neighdata->metric.out != old_outgoing;
 }
 
 /**
@@ -1177,23 +1215,19 @@ _remove_mpr(struct nhdp_domain *domain) {
 }
 
 static void
-_cb_update_everyone_mpr(void) {
+_cb_update_everyone_mpr(struct nhdp_domain *domain) {
   struct nhdp_neighbor *neigh;
-  struct nhdp_domain *domain;
   struct nhdp_neighbor_domaindata *domaindata;
 
   list_for_each_element(nhdp_db_get_neigh_list(), neigh, _global_node) {
-    if (_flooding_domain.mpr == &_everyone_mprs) {
+    if (&_flooding_domain == domain) {
       neigh->neigh_is_flooding_mpr =
           neigh->flooding_willingness > RFC7181_WILLINGNESS_NEVER;
     }
-
-    list_for_each_element(nhdp_domain_get_list(), domain, _node) {
-      if (domain->mpr == &_everyone_mprs) {
-        domaindata = nhdp_domain_get_neighbordata(domain, neigh);
-        domaindata->neigh_is_mpr =
-            domaindata->willingness > RFC7181_WILLINGNESS_NEVER;
-      }
+    else if (domain->mpr == &_everyone_mprs) {
+      domaindata = nhdp_domain_get_neighbordata(domain, neigh);
+      domaindata->neigh_is_mpr =
+          domaindata->willingness > RFC7181_WILLINGNESS_NEVER;
     }
   }
 }