Test peer table and update selection functions. Seem to work.
authorJennifer Richards <jennifer@painless-security.com>
Sat, 16 Jul 2016 05:14:08 +0000 (01:14 -0400)
committerJennifer Richards <jennifer@painless-security.com>
Sat, 16 Jul 2016 05:14:08 +0000 (01:14 -0400)
Makefile.am
include/trp_internal.h
trp/test/ptbl_test.c [new file with mode: 0644]
trp/trps.c

index 89d5976..c6c5222 100644 (file)
@@ -1,6 +1,6 @@
 DISTCHECK_CONFIGURE_FLAGS = \
        --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
-bin_PROGRAMS= tr/trust_router tr/trpc tid/example/tidc tid/example/tids common/dh_test/tr_dh_test common/mq_test/mq_test common/mq_test/thread_test trp/msgtst trp/test/rtbl_test
+bin_PROGRAMS= tr/trust_router tr/trpc tid/example/tidc tid/example/tids common/dh_test/tr_dh_test common/mq_test/mq_test common/mq_test/thread_test trp/msgtst trp/test/rtbl_test trp/test/ptbl_test
 AM_CPPFLAGS=-I$(srcdir)/include $(GLIB_CFLAGS)
 AM_CFLAGS = -Wall -Werror=missing-prototypes -Werror -Wno-parentheses $(GLIB_CFLAGS)
 SUBDIRS = gsscon 
@@ -89,6 +89,14 @@ trp/trp_rtable.c
 
 trp_test_rtbl_test_LDADD =  $(GLIB_LIBS)
 
+trp_test_ptbl_test_SOURCES = trp/test/ptbl_test.c \
+$(common_srcs) \
+$(trp_srcs) \
+$(tid_srcs) \
+common/tr_mq.c
+
+trp_test_ptbl_test_LDADD = gsscon/libgsscon.la  $(GLIB_LIBS)
+
 tid_example_tidc_SOURCES = tid/example/tidc_main.c 
 
 #tid_example_tidc_LDADD = gsscon/libgsscon.la libtr_tid.la $(GLIB_LIBS)
index 4d16124..151139b 100644 (file)
@@ -166,5 +166,6 @@ TRP_RENTRY *trps_get_selected_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME
 TR_NAME *trps_get_next_hop(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm);
 TRP_RC trps_sweep_routes(TRPS_INSTANCE *trps);
 TRP_RC trps_add_peer(TRPS_INSTANCE *trps, TRP_PEER *peer);
+TRP_RENTRY **trps_select_updates_for_peer(TALLOC_CTX *memctx, TRPS_INSTANCE *trps, TR_NAME *peer_gssname, size_t *n_update);
 
 #endif /* TRP_INTERNAL_H */
diff --git a/trp/test/ptbl_test.c b/trp/test/ptbl_test.c
new file mode 100644 (file)
index 0000000..15d6d15
--- /dev/null
@@ -0,0 +1,255 @@
+#include <stdio.h>
+#include <talloc.h>
+#include <assert.h>
+
+#include <trp_internal.h>
+#include <trp_ptable.h>
+
+struct peer_entry {
+  char *server;
+  unsigned int port;
+  unsigned int linkcost;
+};
+
+static struct peer_entry peer_data[]={
+  {"peer0", 10000, 0x0001},
+  {"peer1", 15000, 0x0002},
+  {"peer2", 20000, 0x0004},
+  {"peer3", 25000, 0x0008},
+  {"peer4", 30000, 0x0010}
+};
+static size_t n_peers=sizeof(peer_data)/sizeof(peer_data[0]);
+
+static void populate_ptable(TRPS_INSTANCE *trps)
+{
+  TRP_PEER *new_peer;
+  int i;
+
+  for (i=0; i<n_peers; i++) {
+    new_peer=trp_peer_new(NULL);
+    assert(new_peer!=NULL);
+    trp_peer_set_server(new_peer, peer_data[i].server);
+    assert(trp_peer_get_server(new_peer)!=NULL);
+    trp_peer_set_port(new_peer, peer_data[i].port);
+    trp_peer_set_linkcost(new_peer, peer_data[i].linkcost);
+    assert(trps_add_peer(trps, new_peer)==TRP_SUCCESS);
+  }
+}
+
+static struct peer_entry *find_peer_entry(char *server)
+{
+  int i;
+  for (i=0; i<n_peers; i++) {
+    if (0==strcmp(server, peer_data[i].server)) {
+      return (peer_data+i);
+    }
+  }
+  return NULL;
+}
+
+static void verify_ptable(TRPS_INSTANCE *trps)
+{
+  struct peer_entry *peer_entry=NULL;
+  TRP_PEER *peer;
+  char *s;
+  TR_NAME *gssname;
+
+  peer=trps->ptable->head;
+  while (peer!=NULL) {
+    peer_entry=find_peer_entry(trp_peer_get_server(peer));
+    assert(peer_entry!=NULL);
+    assert(!strcmp(trp_peer_get_server(peer), peer_entry->server));
+    assert(trp_peer_get_port(peer)==peer_entry->port);
+    assert(trp_peer_get_linkcost(peer)==peer_entry->linkcost);
+    assert(0<asprintf(&s, "trustrouter@%s", peer_entry->server));
+    gssname=tr_new_name(s);
+    free(s);
+    assert(gssname!=NULL);
+    assert(!tr_name_cmp(trp_peer_get_gssname(peer), gssname));
+    tr_free_name(gssname);
+    peer=peer->next;
+  }
+}
+
+struct route_data {
+  char *apc;
+  char *realm;
+  char *peer;
+  unsigned int metric;
+  char *trust_router;
+  char *next_hop;
+  int selected;
+  unsigned int interval;
+  int verified; /* for testing */
+};
+static struct route_data route_table[]={
+  {"apc0", "realm0", "", 0, "tr.r0.apc0", "", 1, 60, 0},
+  {"apc0", "realm1", "", 0, "tr.r1.apc0", "", 1, 60, 0},
+  {"apc0", "realm0", "trustrouter@peer0", 1, "tr.r0.apc0", "trustrouter@peer0", 0, 60, 0},
+  {"apc0", "realm1", "trustrouter@peer0", 0, "tr.r1.apc0", "trustrouter@peer0", 0, 60, 0},
+  {"apc0", "realm2", "trustrouter@peer0", 0, "tr.r2.apc0", "trustrouter@peer0", 1, 60, 0},
+  {"apc0", "realm3", "trustrouter@peer0", 1, "tr.r3.apc0", "trustrouter@peer0", 0, 60, 0},
+  {"apc0", "realm4", "trustrouter@peer0", 2, "tr.r4.apc0", "trustrouter@peer0", 0, 60, 0},
+  {"apc0", "realm0", "trustrouter@peer1", 0, "tr.r0.apc0", "trustrouter@peer1", 0, 60, 0},
+  {"apc0", "realm1", "trustrouter@peer1", 1, "tr.r1.apc0", "trustrouter@peer1", 0, 60, 0},
+  {"apc0", "realm2", "trustrouter@peer1", 1, "tr.r2.apc0", "trustrouter@peer1", 0, 60, 0},
+  {"apc0", "realm3", "trustrouter@peer1", 0, "tr.r3.apc0", "trustrouter@peer1", 1, 60, 0},
+  {"apc0", "realm4", "trustrouter@peer1", 2, "tr.r4.apc0", "trustrouter@peer1", 0, 60, 0},
+  {"apc0", "realm0", "trustrouter@peer2", 0, "tr.r0.apc0", "trustrouter@peer2", 0, 60, 0},
+  {"apc0", "realm1", "trustrouter@peer2", 2, "tr.r1.apc0", "trustrouter@peer2", 0, 60, 0},
+  {"apc0", "realm2", "trustrouter@peer2", 2, "tr.r2.apc0", "trustrouter@peer2", 0, 60, 0},
+  {"apc0", "realm3", "trustrouter@peer2", 1, "tr.r3.apc0", "trustrouter@peer2", 0, 60, 0},
+  {"apc0", "realm4", "trustrouter@peer2", 0, "tr.r4.apc0", "trustrouter@peer2", 1, 60, 0},
+};
+static size_t n_routes=sizeof(route_table)/sizeof(route_table[0]);
+
+/* These are the correct updates to select from the above route table for each peer.
+ * The rule is: send selected route unless it is through that peer, otherwise send
+ * the best (lowest metric) alternative route. 
+ *
+ * In a few cases there are multiple valid options (when a two non-selected routes
+ * exist). If these tests are failing, it may be that the trps code is selecting another
+ * valid option, so check that. Probably ought to tweak metrics to avoid that ambiguity. */
+static struct route_data update_table[][10]={
+  { /* peer0 */
+    {"apc0", "realm0", "", 0, "tr.r0.apc0", "", 1, 60, 0},
+    {"apc0", "realm1", "", 0, "tr.r1.apc0", "", 1, 60, 0},
+    {"apc0", "realm2", "trustrouter@peer1", 1, "tr.r2.apc0", "trustrouter@peer1", 0, 60, 0},
+    {"apc0", "realm3", "trustrouter@peer1", 0, "tr.r3.apc0", "trustrouter@peer1", 1, 60, 0},
+    {"apc0", "realm4", "trustrouter@peer2", 0, "tr.r4.apc0", "trustrouter@peer2", 1, 60, 0},
+    {NULL}
+  },
+  { /* peer1 */
+    {"apc0", "realm0", "", 0, "tr.r0.apc0", "", 1, 60, 0},
+    {"apc0", "realm1", "", 0, "tr.r1.apc0", "", 1, 60, 0},
+    {"apc0", "realm2", "trustrouter@peer0", 0, "tr.r2.apc0", "trustrouter@peer0", 1, 60, 0},
+    {"apc0", "realm3", "trustrouter@peer2", 1, "tr.r3.apc0", "trustrouter@peer2", 0, 60, 0},
+    {"apc0", "realm4", "trustrouter@peer2", 0, "tr.r4.apc0", "trustrouter@peer2", 1, 60, 0},
+    {NULL}
+  },
+  { /* peer2 */
+    {"apc0", "realm0", "", 0, "tr.r0.apc0", "", 1, 60, 0},
+    {"apc0", "realm1", "", 0, "tr.r1.apc0", "", 1, 60, 0},
+    {"apc0", "realm2", "trustrouter@peer0", 0, "tr.r2.apc0", "trustrouter@peer0", 1, 60, 0},
+    {"apc0", "realm3", "trustrouter@peer1", 0, "tr.r3.apc0", "trustrouter@peer1", 1, 60, 0},
+    {"apc0", "realm4", "trustrouter@peer1", 2, "tr.r4.apc0", "trustrouter@peer1", 0, 60, 0},
+    {NULL}
+  },
+  { /* peer3 */
+    {"apc0", "realm0", "", 0, "tr.r0.apc0", "", 1, 60, 0},
+    {"apc0", "realm1", "", 0, "tr.r1.apc0", "", 1, 60, 0},
+    {"apc0", "realm2", "trustrouter@peer0", 0, "tr.r2.apc0", "trustrouter@peer0", 1, 60, 0},
+    {"apc0", "realm3", "trustrouter@peer1", 0, "tr.r3.apc0", "trustrouter@peer1", 1, 60, 0},
+    {"apc0", "realm4", "trustrouter@peer2", 0, "tr.r4.apc0", "trustrouter@peer2", 1, 60, 0},
+    {NULL}
+  },
+  { /* peer4 */
+    {"apc0", "realm0", "", 0, "tr.r0.apc0", "", 1, 60, 0},
+    {"apc0", "realm1", "", 0, "tr.r1.apc0", "", 1, 60, 0},
+    {"apc0", "realm2", "trustrouter@peer0", 0, "tr.r2.apc0", "trustrouter@peer0", 1, 60, 0},
+    {"apc0", "realm3", "trustrouter@peer1", 0, "tr.r3.apc0", "trustrouter@peer1", 1, 60, 0},
+    {"apc0", "realm4", "trustrouter@peer2", 0, "tr.r4.apc0", "trustrouter@peer2", 1, 60, 0},
+    {NULL}
+  }
+};
+
+static void populate_rtable(TRPS_INSTANCE *trps)
+{
+  int i;
+  TRP_RENTRY *new;
+
+  for (i=0; i<n_routes; i++) {
+    new=trp_rentry_new(NULL);
+    assert(new!=NULL);
+    trp_rentry_set_apc(new, tr_new_name(route_table[i].apc));
+    trp_rentry_set_realm(new, tr_new_name(route_table[i].realm));
+    trp_rentry_set_peer(new, tr_new_name(route_table[i].peer));
+    trp_rentry_set_metric(new, route_table[i].metric);
+    trp_rentry_set_trust_router(new, tr_new_name(route_table[i].trust_router));
+    trp_rentry_set_next_hop(new, tr_new_name(route_table[i].next_hop));
+    trp_rentry_set_selected(new, route_table[i].selected);
+    trp_rentry_set_interval(new, route_table[i].interval);
+    /* do not set expiry */
+    trp_rtable_add(trps->rtable, new);
+    new=NULL;
+  }
+}
+
+static void verify_update(TRP_RENTRY **updates, size_t n_updates, struct route_data *expected)
+{
+  int ii,jj;
+  int found;
+
+  for(jj=0; jj<n_updates; jj++) {
+    found=0;
+    for (ii=0; expected[ii].apc!=NULL; ii++) {
+      if ((0==strcmp(expected[ii].apc, updates[jj]->apc->buf))
+         &&(0==strcmp(expected[ii].realm, updates[jj]->realm->buf))
+         &&(0==strcmp(expected[ii].peer, updates[jj]->peer->buf))
+         &&(expected[ii].metric==updates[jj]->metric)
+         &&(0==strcmp(expected[ii].trust_router, updates[jj]->trust_router->buf))
+         &&(0==strcmp(expected[ii].next_hop, updates[jj]->next_hop->buf))
+         &&(expected[ii].selected==updates[jj]->selected)
+         &&(expected[ii].interval==updates[jj]->interval)) {
+        assert(expected[ii].verified==0); /* should only encounter each entry once */
+        expected[ii].verified=1;
+        found=1;
+        continue;
+      }
+    }
+    if (!found) {
+      printf("missing:\n%s\n", trp_rentry_to_str(NULL,updates[jj], " | "));
+      assert(0);
+    }
+  }
+  for(ii=0; expected[ii].apc!=NULL; ii++)
+    assert(expected[ii].verified==1);
+}
+
+static void verify_update_selection(TRPS_INSTANCE *trps)
+{
+  int ii;
+  TRP_RENTRY **updates=NULL;
+  size_t n_updates;
+  TR_NAME *gssname=NULL;
+  char *s;
+
+  for (ii=0; ii<n_peers; ii++) {
+    assert(0<asprintf(&s, "trustrouter@%s", peer_data[ii].server));
+    assert(NULL!=(gssname=tr_new_name(s)));
+    free(s);
+    updates=trps_select_updates_for_peer(NULL, trps, gssname, &n_updates);
+    tr_free_name(gssname);
+    verify_update(updates, n_updates, update_table[ii]);
+    talloc_free(updates);
+  }
+}
+
+int main(void)
+{
+  TALLOC_CTX *main_ctx=talloc_new(NULL);
+  TRPS_INSTANCE *trps;
+  char *s;
+
+  trps=trps_new(main_ctx);
+
+  printf("\nPopulating peer table...\n");
+  populate_ptable(trps);
+
+  printf("\nVerifying peer table...\n");
+  verify_ptable(trps);
+
+  printf("\nPopulating route table...\n");
+  populate_rtable(trps);
+  s=trp_rtable_to_str(main_ctx, trps->rtable, " | ", NULL);
+  printf("Route Table:\n%s---\n", s);
+
+  printf("\nVerifying route update selection...\n");
+  verify_update_selection(trps);
+
+  printf("\nDone\n\n");
+  talloc_report_full(main_ctx, stderr);
+  talloc_free(main_ctx);
+  talloc_report_full(NULL, stderr);
+  return 0;
+}
index 4c2f86d..4596807 100644 (file)
@@ -562,7 +562,7 @@ static TRP_RC trps_handle_update(TRPS_INSTANCE *trps, TRP_UPD *upd)
 }
 
 /* choose the best route to comm/realm, optionally excluding routes to a particular peer */
-static TRP_RENTRY *trps_find_best_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TRP_PEER *exclude_peer)
+static TRP_RENTRY *trps_find_best_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *exclude_peer)
 {
   TRP_RENTRY **entry=NULL;
   TRP_RENTRY *best=NULL;
@@ -575,7 +575,7 @@ static TRP_RENTRY *trps_find_best_route(TRPS_INSTANCE *trps, TR_NAME *comm, TR_N
   for (kk=0; kk<n_entry; kk++) {
     if (trp_rentry_get_metric(entry[kk]) < min_metric) {
       if ((exclude_peer==NULL) || (0!=tr_name_cmp(trp_rentry_get_peer(entry[kk]),
-                                                  trp_peer_get_gssname(exclude_peer)))) {
+                                                  exclude_peer))) {
         kk_min=kk;
         min_metric=trp_rentry_get_metric(entry[kk]);
       }
@@ -602,21 +602,21 @@ static TRP_RC trps_update_active_routes(TRPS_INSTANCE *trps)
   for (ii=0; ii<n_apc; ii++) {
     realm=trp_rtable_get_apc_realms(trps->rtable, apc[ii], &n_realm);
     for (jj=0; jj<n_realm; jj++) {
-      best_route=trps_find_best_route(trps, apc[ii], realm[jj]);
+      best_route=trps_find_best_route(trps, apc[ii], realm[jj], NULL);
       if (best_route==NULL)
-        min_metric=TRP_METRIC_INFINITY;
+        best_metric=TRP_METRIC_INFINITY;
       else
-        min_metric=trp_rentry_get_metric(best_route);
+        best_metric=trp_rentry_get_metric(best_route);
 
-      cur_route=trps_get_selected_route(trps, apc[ii], realm[jj], NULL);
+      cur_route=trps_get_selected_route(trps, apc[ii], realm[jj]);
       if (cur_route!=NULL) {
         cur_metric=trp_rentry_get_metric(cur_route);
-        if ((min_metric < cur_metric) && (trp_metric_is_finite(min_metric))) {
+        if ((best_metric < cur_metric) && (trp_metric_is_finite(best_metric))) {
           trp_rentry_set_selected(cur_route, 0);
           trp_rentry_set_selected(best_route, 1);
         } else if (!trp_metric_is_finite(cur_metric)) /* rejects infinite or invalid metrics */
           trp_rentry_set_selected(cur_route, 0);
-      } else if (trp_metric_is_finite(min_metric))
+      } else if (trp_metric_is_finite(best_metric))
         trp_rentry_set_selected(best_route, 1);
     }
     if (realm!=NULL)
@@ -703,16 +703,16 @@ TRP_RC trps_sweep_routes(TRPS_INSTANCE *trps)
 }
 
 /* select the correct route to comm/realm to be announced to peer */
-static TRP_RENTRY trps_select_realm_update(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME realm, TRP_PEER *peer)
+static TRP_RENTRY *trps_select_realm_update(TRPS_INSTANCE *trps, TR_NAME *comm, TR_NAME *realm, TR_NAME *peer_gssname)
 {
   TRP_RENTRY *route;
 
   /* Take the currently selected route unless it is through the peer we're sending the update to.
    * I.e., enforce the split horizon rule. */
   route=trp_rtable_get_selected_entry(trps->rtable, comm, realm);
-  if (0==tr_name_cmp(trp_peer_get_gssname(peer), trp_rentry_get_peer(route))) {
+  if (0==tr_name_cmp(peer_gssname, trp_rentry_get_peer(route))) {
     /* the selected entry goes through the peer we're reporting to, choose an alternate */
-    route=trps_find_best_route(trps, comm, realm, peer);
+    route=trps_find_best_route(trps, comm, realm, peer_gssname);
     if (!trp_metric_is_finite(trp_rentry_get_metric(route)))
       route=NULL; /* don't advertise a retracted route */
   }
@@ -722,7 +722,7 @@ static TRP_RENTRY trps_select_realm_update(TRPS_INSTANCE *trps, TR_NAME *comm, T
 /* returns an array of pointers to updates (*not* an array of updates). Returns number of entries
  * via n_update parameter. (The allocated space will generally be larger than required, see note in
  * the code.) */
-TRP_RENTRY **trps_select_updates_for_peer(TALLOC_CTX *memctx, TRPS_INSTANCE *trps, TRP_PEER *peer, size_t *n_update)
+TRP_RENTRY **trps_select_updates_for_peer(TALLOC_CTX *memctx, TRPS_INSTANCE *trps, TR_NAME *peer_gssname, size_t *n_update)
 {
   size_t n_apc=0;
   TR_NAME **apc=trp_rtable_get_apcs(trps->rtable, &n_apc);
@@ -737,19 +737,18 @@ TRP_RENTRY **trps_select_updates_for_peer(TALLOC_CTX *memctx, TRPS_INSTANCE *trp
    * with space for every route table entry to be returned. This is guaranteed to be large
    * enough. If the routing table gets very large, this may be wasteful, but that seems
    * unlikely to be significant in the near future. */
-  result=talloc_array(memctx, TRP_RENTRY, trp_rtable_size(trps->rtable));
+  result=talloc_array(memctx, TRP_RENTRY *, trp_rtable_size(trps->rtable));
   if (result==NULL) {
     talloc_free(apc);
     return NULL;
   }
   
   for (ii=0; ii<n_apc; ii++) {
-    realm=trp_rtable_get_apc_realms(trps->rtable, apc[ii], &n_realm) {
-      for (jj=0; jj<n_realm; jj++) {
-        best=trps_select_realm_update(trps, apc[ii], realm[jj], peer);
-        if (best!=NULL)
-          result[n_used++]=best;
-      }
+    realm=trp_rtable_get_apc_realms(trps->rtable, apc[ii], &n_realm);
+    for (jj=0; jj<n_realm; jj++) {
+      best=trps_select_realm_update(trps, apc[ii], realm[jj], peer_gssname);
+      if (best!=NULL)
+        result[n_used++]=best;
     }
     if (realm!=NULL)
       talloc_free(realm);