X-Git-Url: https://tinc-vpn.org/git/browse?a=blobdiff_plain;f=src%2Fgraph.c;h=a774eacc2ab86e62d9090826b4795daacad98ec3;hb=f6e87ab476a0faf8b124ecaaa27f967d825e6457;hp=297832cda5bb8f3eeabd8cf241c3eeacfc8601f0;hpb=5a28aa7b8b0ab6237c2eab5f8b11253ea3ec5a05;p=tinc diff --git a/src/graph.c b/src/graph.c index 297832cd..a774eacc 100644 --- a/src/graph.c +++ b/src/graph.c @@ -1,6 +1,6 @@ /* graph.c -- graph algorithms - Copyright (C) 2001-2011 Guus Sliepen , + Copyright (C) 2001-2017 Guus Sliepen , 2001-2005 Ivo Timmermans This program is free software; you can redistribute it and/or modify @@ -44,22 +44,21 @@ #include "system.h" -#include "avl_tree.h" -#include "config.h" #include "connection.h" #include "device.h" #include "edge.h" #include "graph.h" +#include "list.h" #include "logger.h" +#include "names.h" #include "netutl.h" #include "node.h" -#include "process.h" #include "protocol.h" +#include "script.h" #include "subnet.h" #include "utils.h" #include "xalloc.h" - -static bool graph_changed = true; +#include "graph.h" /* Implementation of Kruskal's algorithm. Running time: O(EN) @@ -67,40 +66,23 @@ static bool graph_changed = true; */ static void mst_kruskal(void) { - avl_node_t *node, *next; - edge_t *e; - node_t *n; - connection_t *c; - int nodes = 0; - int safe_edges = 0; - bool skipped; - /* Clear MST status on connections */ - for(node = connection_tree->head; node; node = node->next) { - c = node->data; + for list_each(connection_t, c, connection_list) { c->status.mst = false; } - /* Do we have something to do at all? */ - - if(!edge_weight_tree->head) - return; - - ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Running Kruskal's algorithm:"); + logger(DEBUG_SCARY_THINGS, LOG_DEBUG, "Running Kruskal's algorithm:"); /* Clear visited status on nodes */ - for(node = node_tree->head; node; node = node->next) { - n = node->data; + for splay_each(node_t, n, node_tree) { n->status.visited = false; - nodes++; } /* Starting point */ - for(node = edge_weight_tree->head; node; node = node->next) { - e = node->data; + for splay_each(edge_t, e, edge_weight_tree) { if(e->from->status.reachable) { e->from->status.visited = true; break; @@ -109,11 +91,10 @@ static void mst_kruskal(void) { /* Add safe edges */ - for(skipped = false, node = edge_weight_tree->head; node; node = next) { - next = node->next; - e = node->data; + bool skipped = false; - if(!e->reverse || e->from->status.visited == e->to->status.visited) { + for splay_each(edge_t, e, edge_weight_tree) { + if(!e->reverse || (e->from->status.visited == e->to->status.visited)) { skipped = true; continue; } @@ -121,26 +102,21 @@ static void mst_kruskal(void) { e->from->status.visited = true; e->to->status.visited = true; - if(e->connection) + if(e->connection) { e->connection->status.mst = true; + } - if(e->reverse->connection) + if(e->reverse->connection) { e->reverse->connection->status.mst = true; + } - safe_edges++; - - ifdebug(SCARY_THINGS) logger(LOG_DEBUG, " Adding edge %s - %s weight %d", e->from->name, - e->to->name, e->weight); + logger(DEBUG_SCARY_THINGS, LOG_DEBUG, " Adding edge %s - %s weight %d", e->from->name, e->to->name, e->weight); if(skipped) { skipped = false; next = edge_weight_tree->head; - continue; } } - - ifdebug(SCARY_THINGS) logger(LOG_DEBUG, "Done, counted %d nodes and %d safe edges.", nodes, - safe_edges); } /* Implementation of a simple breadth-first search algorithm. @@ -148,25 +124,14 @@ static void mst_kruskal(void) { */ static void sssp_bfs(void) { - avl_node_t *node, *next, *to; - edge_t *e; - node_t *n; - list_t *todo_list; - list_node_t *from, *todonext; - bool indirect; - char *name; - char *address, *port; - char *envp[7]; - int i; - - todo_list = list_alloc(NULL); + list_t *todo_list = list_alloc(NULL); /* Clear visited status on nodes */ - for(node = node_tree->head; node; node = node->next) { - n = node->data; + for splay_each(node_t, n, node_tree) { n->status.visited = false; n->status.indirect = true; + n->distance = -1; } /* Begin with myself */ @@ -176,26 +141,30 @@ static void sssp_bfs(void) { myself->nexthop = myself; myself->prevedge = NULL; myself->via = myself; + myself->distance = 0; list_insert_head(todo_list, myself); /* Loop while todo_list is filled */ - for(from = todo_list->head; from; from = todonext) { /* "from" is the node from which we start */ - n = from->data; + for list_each(node_t, n, todo_list) { /* "n" is the node from which we start */ + logger(DEBUG_SCARY_THINGS, LOG_DEBUG, " Examining edges from %s", n->name); - for(to = n->edge_tree->head; to; to = to->next) { /* "to" is the edge connected to "from" */ - e = to->data; + if(n->distance < 0) { + abort(); + } - if(!e->reverse) + for splay_each(edge_t, e, n->edge_tree) { /* "e" is the edge connected to "from" */ + if(!e->reverse || e->to == myself) { continue; + } /* Situation: - / - / + / + / ----->(n)---e-->(e->to) - \ - \ + \ + \ Where e is an edge, (n) and (e->to) are nodes. n->address is set to the e->address of the edge left of n to n. @@ -206,91 +175,137 @@ static void sssp_bfs(void) { of nodes behind it. */ - indirect = n->status.indirect || e->options & OPTION_INDIRECT; + bool indirect = n->status.indirect || e->options & OPTION_INDIRECT; if(e->to->status.visited - && (!e->to->status.indirect || indirect)) + && (!e->to->status.indirect || indirect) + && (e->to->distance != n->distance + 1 || e->weight >= e->to->prevedge->weight)) { continue; + } + + // Only update nexthop if it doesn't increase the path length + + if(!e->to->status.visited || (e->to->distance == n->distance + 1 && e->weight >= e->to->prevedge->weight)) { + e->to->nexthop = (n->nexthop == myself) ? e->to : n->nexthop; + } e->to->status.visited = true; e->to->status.indirect = indirect; - e->to->nexthop = (n->nexthop == myself) ? e->to : n->nexthop; e->to->prevedge = e; e->to->via = indirect ? n->via : e->to; e->to->options = e->options; + e->to->distance = n->distance + 1; - if(e->to->address.sa.sa_family == AF_UNSPEC && e->address.sa.sa_family != AF_UNKNOWN) + if(!e->to->status.reachable || (e->to->address.sa.sa_family == AF_UNSPEC && e->address.sa.sa_family != AF_UNKNOWN)) { update_node_udp(e->to, &e->address); + } list_insert_tail(todo_list, e->to); } - todonext = from->next; - list_delete_node(todo_list, from); + next = node->next; /* Because the list_insert_tail() above could have added something extra for us! */ + list_delete_node(todo_list, node); } list_free(todo_list); +} +static void check_reachability(void) { /* Check reachability status. */ - for(node = node_tree->head; node; node = next) { - next = node->next; - n = node->data; + int reachable_count = 0; + int became_reachable_count = 0; + int became_unreachable_count = 0; + for splay_each(node_t, n, node_tree) { if(n->status.visited != n->status.reachable) { n->status.reachable = !n->status.reachable; + n->last_state_change = now.tv_sec; if(n->status.reachable) { - ifdebug(TRAFFIC) logger(LOG_DEBUG, "Node %s (%s) became reachable", - n->name, n->hostname); + logger(DEBUG_TRAFFIC, LOG_DEBUG, "Node %s (%s) became reachable", + n->name, n->hostname); + + if(n != myself) { + became_reachable_count++; + } } else { - ifdebug(TRAFFIC) logger(LOG_DEBUG, "Node %s (%s) became unreachable", - n->name, n->hostname); + logger(DEBUG_TRAFFIC, LOG_DEBUG, "Node %s (%s) became unreachable", + n->name, n->hostname); + + if(n != myself) { + became_unreachable_count++; + } + } + + if(experimental && OPTION_VERSION(n->options) >= 2) { + n->status.sptps = true; } /* TODO: only clear status.validkey if node is unreachable? */ n->status.validkey = false; + + if(n->status.sptps) { + sptps_stop(&n->sptps); + n->status.waitingforkey = false; + } + n->last_req_key = 0; + n->status.udp_confirmed = false; n->maxmtu = MTU; + n->maxrecentlen = 0; n->minmtu = 0; n->mtuprobes = 0; - if(n->mtuevent) { - event_del(n->mtuevent); - n->mtuevent = NULL; - } + timeout_del(&n->udp_ping_timeout); + + char *name; + char *address; + char *port; - xasprintf(&envp[0], "NETNAME=%s", netname ? : ""); - xasprintf(&envp[1], "DEVICE=%s", device ? : ""); - xasprintf(&envp[2], "INTERFACE=%s", iface ? : ""); - xasprintf(&envp[3], "NODE=%s", n->name); + environment_t env; + environment_init(&env); + environment_add(&env, "NODE=%s", n->name); sockaddr2str(&n->address, &address, &port); - xasprintf(&envp[4], "REMOTEADDRESS=%s", address); - xasprintf(&envp[5], "REMOTEPORT=%s", port); - envp[6] = NULL; + environment_add(&env, "REMOTEADDRESS=%s", address); + environment_add(&env, "REMOTEPORT=%s", port); - execute_script(n->status.reachable ? "host-up" : "host-down", envp); + execute_script(n->status.reachable ? "host-up" : "host-down", &env); - xasprintf(&name, - n->status.reachable ? "hosts/%s-up" : "hosts/%s-down", - n->name); - execute_script(name, envp); + xasprintf(&name, n->status.reachable ? "hosts/%s-up" : "hosts/%s-down", n->name); + execute_script(name, &env); free(name); free(address); free(port); - - for(i = 0; i < 6; i++) - free(envp[i]); + environment_exit(&env); subnet_update(n, NULL, n->status.reachable); - if(!n->status.reachable) + if(!n->status.reachable) { update_node_udp(n, NULL); - else if(n->connection) - send_ans_key(n); + memset(&n->status, 0, sizeof(n->status)); + n->options = 0; + } else if(n->connection) { + // Speed up UDP probing by sending our key. + if(!n->status.sptps) { + send_ans_key(n); + } + } + } + + if(n->status.reachable && n != myself) { + reachable_count++; + } + } + + if(device_standby) { + if(reachable_count == 0 && became_unreachable_count > 0) { + device_disable(); + } else if(reachable_count > 0 && reachable_count == became_reachable_count) { + device_enable(); } } } @@ -298,69 +313,6 @@ static void sssp_bfs(void) { void graph(void) { subnet_cache_flush(); sssp_bfs(); + check_reachability(); mst_kruskal(); - graph_changed = true; -} - - - -/* Dump nodes and edges to a graphviz file. - - The file can be converted to an image with - dot -Tpng graph_filename -o image_filename.png -Gconcentrate=true -*/ - -void dump_graph(void) { - avl_node_t *node; - node_t *n; - edge_t *e; - char *filename = NULL, *tmpname = NULL; - FILE *file; - - if(!graph_changed || !get_config_string(lookup_config(config_tree, "GraphDumpFile"), &filename)) - return; - - graph_changed = false; - - ifdebug(PROTOCOL) logger(LOG_NOTICE, "Dumping graph"); - - if(filename[0] == '|') { - file = popen(filename + 1, "w"); - } else { - xasprintf(&tmpname, "%s.new", filename); - file = fopen(tmpname, "w"); - } - - if(!file) { - logger(LOG_ERR, "Unable to open graph dump file %s: %s", filename, strerror(errno)); - free(tmpname); - return; - } - - fprintf(file, "digraph {\n"); - - /* dump all nodes first */ - for(node = node_tree->head; node; node = node->next) { - n = node->data; - fprintf(file, " %s [label = \"%s\"];\n", n->name, n->name); - } - - /* now dump all edges */ - for(node = edge_weight_tree->head; node; node = node->next) { - e = node->data; - fprintf(file, " %s -> %s;\n", e->from->name, e->to->name); - } - - fprintf(file, "}\n"); - - if(filename[0] == '|') { - pclose(file); - } else { - fclose(file); -#ifdef HAVE_MINGW - unlink(filename); -#endif - rename(tmpname, filename); - free(tmpname); - } }