--- /dev/null
+#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;
+}
}
/* 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;
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]);
}
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)
}
/* 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 */
}
/* 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);
* 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);