From 9ee09e36a02efcfa4e2cfa88ca49117fdfb6a6f7 Mon Sep 17 00:00:00 2001 From: Jeremy Lakeman Date: Tue, 30 Apr 2013 16:35:45 +0930 Subject: [PATCH] Replace old routing engine with new link state engine --- commandline.c | 50 --- constants.h | 3 - mdp_client.c | 3 - overlay.c | 3 - overlay_address.h | 2 - overlay_advertise.c | 238 ---------- overlay_interface.c | 12 +- overlay_mdp.c | 8 - overlay_packetformats.c | 35 +- overlay_queue.c | 27 +- overlay_route.c | 959 ---------------------------------------- route_link.c | 17 + serval.h | 52 +-- sourcefiles.mk | 2 - tests/dnaprotocol | 17 - tests/routing | 8 +- 16 files changed, 57 insertions(+), 1379 deletions(-) delete mode 100644 overlay_advertise.c delete mode 100644 overlay_route.c diff --git a/commandline.c b/commandline.c index 4bc0489d..92b7d601 100644 --- a/commandline.c +++ b/commandline.c @@ -2137,54 +2137,6 @@ int app_crypt_test(const struct cli_parsed *parsed, void *context) return 0; } -int app_node_info(const struct cli_parsed *parsed, void *context) -{ - if (config.debug.verbose) - DEBUG_cli_parsed(parsed); - const char *sid; - cli_arg(parsed, "sid", &sid, NULL, ""); - - overlay_mdp_frame mdp; - bzero(&mdp,sizeof(mdp)); - - mdp.packetTypeAndFlags=MDP_NODEINFO; - - /* get SID or SID prefix - XXX - Doesn't correctly handle odd-lengthed SID prefixes (ignores last digit). - The matching code in overlay_route.c also has a similar problem with the last - digit of an odd-length prefix being ignored. */ - int i; - mdp.nodeinfo.sid_prefix_length=0; - for(i = 0; (i != SID_SIZE)&&sid[i<<1]&&sid[(i<<1)+1]; i++) { - mdp.nodeinfo.sid[mdp.nodeinfo.sid_prefix_length] = hexvalue(sid[i<<1]) << 4; - mdp.nodeinfo.sid[mdp.nodeinfo.sid_prefix_length++] |= hexvalue(sid[(i<<1)+1]); - } - mdp.nodeinfo.sid_prefix_length*=2; - - int result=overlay_mdp_send(&mdp,MDP_AWAITREPLY,5000); - if (result) { - if (mdp.packetTypeAndFlags==MDP_ERROR) - { - overlay_mdp_client_done(); - return WHYF(" MDP Server error #%d: '%s'",mdp.error.error,mdp.error.message); - } - else { - overlay_mdp_client_done(); - return WHYF("Could not get information about node."); - } - } - - cli_printf("record"); cli_delim(":"); - cli_printf("%s",mdp.nodeinfo.foundP?"found":"noresult"); cli_delim(":"); - cli_printf("%s", alloca_tohex_sid(mdp.nodeinfo.sid)); cli_delim(":"); - cli_printf("%s",mdp.nodeinfo.localP?"self":"peer"); cli_delim(":"); - cli_printf("%s",mdp.nodeinfo.neighbourP?"direct":"indirect"); cli_delim(":"); - cli_printf("%d",mdp.nodeinfo.score); cli_delim(":"); - cli_printf("%d",mdp.nodeinfo.interface_number); cli_delim("\n"); - - return 0; -} - int app_route_print(const struct cli_parsed *parsed, void *context) { if (config.debug.verbose) @@ -2454,8 +2406,6 @@ struct cli_schema command_line_options[]={ "Print the routing table"}, {app_network_scan, {"scan","[
]",NULL}, 0, "Scan the network for serval peers. If no argument is supplied, all local addresses will be scanned."}, - {app_node_info,{"node","info","",NULL}, 0, - "Return routing information about a SID"}, {app_count_peers,{"peer","count",NULL}, 0, "Return a count of routable peers on the network"}, {app_dna_lookup,{"dna","lookup","","[]",NULL}, 0, diff --git a/constants.h b/constants.h index d2871f98..6e9ea62b 100644 --- a/constants.h +++ b/constants.h @@ -81,7 +81,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. /* All of these types should be considered deprecated. Processing code should migrate to well known MDP port numbers */ /* Overlay mesh packet codes */ -#define OF_TYPE_SELFANNOUNCE_ACK 0x20 /* BATMAN style "I saw your announcment" frames */ #define OF_TYPE_DATA 0x30 /* Ordinary data frame. Upto MTU bytes of payload. 32 bit channel/port indicator for each end. @@ -91,7 +90,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 1 byte channel/port indicator for each end */ #define OF_TYPE_RHIZOME_ADVERT 0x50 /* Advertisment of file availability via Rhizome */ #define OF_TYPE_PLEASEEXPLAIN 0x60 /* Request for resolution of an abbreviated address */ -#define OF_TYPE_NODEANNOUNCE 0x70 #define PAYLOAD_FLAG_SENDER_SAME (1<<0) #define PAYLOAD_FLAG_TO_BROADCAST (1<<1) @@ -160,7 +158,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #define MDP_GETADDRS 5 #define MDP_ADDRLIST 6 #define MDP_ROUTING_TABLE 7 -#define MDP_NODEINFO 8 #define MDP_GOODBYE 9 #define MDP_SCAN 10 diff --git a/mdp_client.c b/mdp_client.c index 31195a7c..e58c11ea 100644 --- a/mdp_client.c +++ b/mdp_client.c @@ -304,9 +304,6 @@ int overlay_mdp_relevant_bytes(overlay_mdp_frame *mdp) len=(&mdp->error.message[0]-(char *)mdp) + strlen(mdp->error.message)+1; if (mdp->error.error) INFOF("mdp return/error code: %d:%s",mdp->error.error,mdp->error.message); break; - case MDP_NODEINFO: - len=(&mdp->raw[0] - (char *)mdp) + sizeof(overlay_mdp_nodeinfo); - break; default: return WHY("Illegal MDP frame type."); } diff --git a/overlay.c b/overlay.c index ca4c7dc6..181e3ab4 100644 --- a/overlay.c +++ b/overlay.c @@ -149,9 +149,6 @@ schedule(&_sched_##X); } /* Periodically check for new interfaces */ SCHEDULE(overlay_interface_discover, 1, 100); - /* Periodically update route table. */ - SCHEDULE(overlay_route_tick, 100, 100); - /* Periodically advertise bundles */ SCHEDULE(overlay_rhizome_advertise, 1000, 10000); diff --git a/overlay_address.h b/overlay_address.h index 9d3ba1f2..4c5e028f 100644 --- a/overlay_address.h +++ b/overlay_address.h @@ -78,8 +78,6 @@ struct subscriber{ time_ms_t last_stun_request; time_ms_t last_probe; time_ms_t last_probe_response; - time_ms_t last_rx; - time_ms_t last_acked; time_ms_t last_tx; // public signing key details for remote peers diff --git a/overlay_advertise.c b/overlay_advertise.c deleted file mode 100644 index cc345603..00000000 --- a/overlay_advertise.c +++ /dev/null @@ -1,238 +0,0 @@ -/* -Serval Distributed Numbering Architecture (DNA) -Copyright (C) 2010 Paul Gardner-Stephen - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include "serval.h" -#include "conf.h" -#include "str.h" -#include "overlay_address.h" -#include "overlay_buffer.h" -#include "overlay_packet.h" - -/* List of prioritised advertisements */ -#define OVERLAY_MAX_ADVERTISEMENT_REQUESTS 16 -overlay_node *oad_requests[OVERLAY_MAX_ADVERTISEMENT_REQUESTS]; -int oad_request_count=0; - -/* Where we are up to in the node list for round-robin advertising */ -int oad_bin=0; -int oad_slot=0; - -/* Which round of the node list we are up to. - This is used for reducing the advertisement rate for stable nodes. - Initially this will just mean advertising higher-scoring nodes - less often. - - Our goal is to advertise all nodes often enough to maintain connectivity, - without wasting any packets. - - Basically high-scoring nodes can be advertised less often than low-scoring - nodes. - - Let's advertise nodes <100 every round, <200 every 2 rounds, and >=200 - every 4th round. -*/ -int oad_round=0; - -/* Request that this node be advertised as a matter of priority */ -int overlay_route_please_advertise(overlay_node *n) -{ - if (oad_request_countsend_full=0; - - if (overlay_address_append(NULL,state->payload,subscriber) || - ob_append_byte(state->payload,score) || - ob_append_byte(state->payload,gateways)){ - // stop if we run out of space, remember where we should start next time. - state->next_advertisement=subscriber; - ob_rewind(state->payload); - return 1; - } - ob_checkpoint(state->payload); - return 0; -} - -int add_advertisement(struct subscriber *subscriber, void *context){ - struct advertisement_state *state=context; - - if (subscriber->reachable==REACHABLE_SELF && subscriber != my_subscriber) - return advertise(state, subscriber, 255, 1); - - if (subscriber->node){ - overlay_node *n=subscriber->node; - - if ((subscriber->reachable&REACHABLE) && (!(subscriber->reachable&REACHABLE_ASSUMED)) - && n->best_link_score>0 && n->observations[n->best_observation].gateways_en_route < 64){ - - return advertise(state, subscriber, n->best_link_score -1, - n->observations[n->best_observation].gateways_en_route +1); - } - } - - return 0; -} - -int overlay_route_queue_advertisements(overlay_interface *interface) -{ - /* Construct a route advertisement frame and append it to e. - - Work out available space in packet for advertisments, and fit the - highest scoring nodes from the current portion in. - - Each advertisement consists of an address prefix followed by score. - We will use 6 bytes of prefix to make it reasonably hard to generate - collisions, including by birthday paradox (good for networks upto about - 20million nodes), and one byte each for score gateways_en_route. - - XXX - We need to send full addresses sometimes so that receiver can - resolve them. Either that or we need to start supporting the PLEASEEXPLAIN - packets, which is probably a better solution. - - The receiver will discount the score based on their measured reliability - for packets to arrive from us; we just repeat what discounted score - we have remembered. - - Hacking the frame together this way is less flexible, but much faster - than messing about with malloc() and setting address fields. - - The src,dst and nexthop can each be encoded with a single byte. - Thus using a fixed 1-byte RFS field we are limited to RFS<0xfa, - which gives us 30 available advertisement slots per packet. - */ - - if (!my_subscriber) - return WHY("Cannot advertise because I don't know who I am"); - struct overlay_frame *frame=malloc(sizeof(struct overlay_frame)); - bzero(frame,sizeof(struct overlay_frame)); - frame->type=OF_TYPE_NODEANNOUNCE; - frame->source = my_subscriber; - frame->ttl=1; - frame->queue=OQ_MESH_MANAGEMENT; - frame->destination_resolved=1; - frame->recvaddr=interface->broadcast_address; - frame->interface=interface; - frame->payload = ob_new(); - ob_limitsize(frame->payload, 400); - - struct advertisement_state state={.payload = frame->payload,}; - - // TODO high priority advertisements first.... - /* - while (slots>0&&oad_request_count) { - oad_request_count--; - ob_append_bytes(e,oad_requests[oad_request_count]->subscriber->sid,6); - ob_append_byte(e,oad_requests[oad_request_count]->best_link_score); - ob_append_byte(e,oad_requests[oad_request_count] - ->observations[oad_requests[oad_request_count] - ->best_observation].gateways_en_route); - slots--; - slots_used++; - } -*/ - ob_checkpoint(frame->payload); - // append announcements starting from the last node we couldn't advertise last time - enum_subscribers(interface->next_advert, add_advertisement, &state); - - // if we didn't start at the beginning and still have space, start again from the beginning - if (interface->next_advert && !state.next_advertisement && ob_remaining(frame->payload) > 0){ - enum_subscribers(NULL, add_advertisement, &state); - } - - interface->next_advert=state.next_advertisement; - ob_limitsize(frame->payload, ob_position(frame->payload)); - - if (overlay_payload_enqueue(frame)){ - op_free(frame); - return -1; - } - return 0; -} - -/* Pull out the advertisements and update our routing table accordingly. - Because we are using a non-standard abbreviation scheme, we have to extract - and search for the nodes ourselves. - - Also, we need to discount the scores based on the score of the sender. - We can either do this once now (more computationally efficient), or have - a rather complicated scheme whereby we attempt to trace through the list - of nodes from here to there. That seems silly, and is agains't the BATMAN - approach of each node just knowing single-hop information. - */ -int overlay_route_saw_advertisements(int i, struct overlay_frame *f, struct decode_context *context, time_ms_t now) -{ - IN(); - struct subscriber *previous=context->previous; - // minimum record length is (address code, 3 byte sid, score, gateways) - while(ob_remaining(f->payload)>0) - { - struct subscriber *subscriber; - context->invalid_addresses=0; - - if (overlay_address_parse(context, f->payload, &subscriber)){ - WHY("Failed to parse address"); - break; - } - - int score=ob_get(f->payload); - int gateways_en_route=ob_get(f->payload); - - // stop if hit end of payload - if (score<0 || gateways_en_route<0){ - WHY("Unexpected end of payload"); - break; - } - - // skip if we can't parse the subscriber id - if (context->invalid_addresses || !subscriber) - continue; - - /* Don't record routes to ourselves */ - if (subscriber->reachable==REACHABLE_SELF) { - if (config.debug.overlayrouting) - DEBUGF("Ignore announcement about me (%s)", alloca_tohex_sid(subscriber->sid)); - continue; - } - - /* File it */ - overlay_route_record_link(now, subscriber, f->source, - i, - /* time range that this advertisement covers. - XXX - Make it up for now. */ - now-2500,now, - score,gateways_en_route); - - } - // restore the previous subscriber id for parsing the next header - context->previous=previous; - RETURN(0); - OUT(); -} diff --git a/overlay_interface.c b/overlay_interface.c index 4c7d24f1..e5e9742f 100644 --- a/overlay_interface.c +++ b/overlay_interface.c @@ -357,7 +357,6 @@ overlay_interface_init(const char *name, struct in_addr src_addr, struct in_addr This will ultimately get tuned by the bandwidth and other properties of the interface */ interface->mtu=1200; interface->state=INTERFACE_STATE_DOWN; - interface->last_tick_ms= -1; // not ticked yet interface->alarm.poll.fd=0; // How often do we announce ourselves on this interface? @@ -707,11 +706,10 @@ static void overlay_interface_poll(struct sched_ent *alarm) if (alarm->poll.revents==0){ time_ms_t now = gettime_ms(); - if (interface->state==INTERFACE_STATE_UP && interface->tick_ms>0 && now >= interface->last_tick_ms+interface->tick_ms){ - // tick the interface - overlay_route_queue_advertisements(interface); - interface->last_tick_ms=now; - alarm->alarm=interface->last_tick_ms+interface->tick_ms; + if (interface->state==INTERFACE_STATE_UP && interface->tick_ms>0){ + if (now >= interface->last_tx+interface->tick_ms) + overlay_send_tick_packet(interface); + alarm->alarm=interface->last_tx+interface->tick_ms; }else{ alarm->alarm=-1; } @@ -766,6 +764,8 @@ overlay_broadcast_ensemble(overlay_interface *interface, struct sockaddr_in *recipientaddr, unsigned char *bytes,int len) { + interface->last_tx = gettime_ms(); + if (config.debug.packettx) { DEBUGF("Sending this packet via interface %s (len=%d)",interface->name,len); diff --git a/overlay_mdp.c b/overlay_mdp.c index 584d9b1f..5db2b48b 100644 --- a/overlay_mdp.c +++ b/overlay_mdp.c @@ -914,14 +914,6 @@ void overlay_mdp_poll(struct sched_ent *alarm) overlay_mdp_releasebindings(recvaddr_un,recvaddrlen); return; - /* Deprecated. We can replace with a more generic dump of the routing table */ - case MDP_NODEINFO: - if (config.debug.mdprequests) DEBUG("MDP_NODEINFO"); - - if (!overlay_route_node_info(&mdp->nodeinfo)) - overlay_mdp_reply(mdp_named.poll.fd,recvaddr_un,recvaddrlen,mdp); - return; - case MDP_ROUTING_TABLE: { struct routing_state state={ diff --git a/overlay_packetformats.c b/overlay_packetformats.c index 42f16df3..29583cea 100644 --- a/overlay_packetformats.c +++ b/overlay_packetformats.c @@ -74,17 +74,6 @@ int process_incoming_frame(time_ms_t now, struct overlay_interface *interface, s int id = (interface - overlay_interfaces); switch(f->type) { - case OF_TYPE_SELFANNOUNCE_ACK: - if (config.debug.overlayframes) - DEBUG("Processing OF_TYPE_SELFANNOUNCE_ACK"); - overlay_route_saw_selfannounce_ack(f,now); - break; - case OF_TYPE_NODEANNOUNCE: - if (config.debug.overlayframes) - DEBUG("Processing OF_TYPE_NODEANNOUNCE"); - overlay_route_saw_advertisements(id,f,context,now); - break; - // data frames case OF_TYPE_RHIZOME_ADVERT: if (config.debug.overlayframes) @@ -103,7 +92,8 @@ int process_incoming_frame(time_ms_t now, struct overlay_interface *interface, s process_explain(f); break; default: - RETURN(WHYF("Support for f->type=0x%x not implemented",f->type)); + if (config.debug.verbose && config.debug.overlayframes) + DEBUGF("Overlay type f->type=0x%x not supported", f->type); } RETURN(0); OUT(); @@ -112,8 +102,11 @@ int process_incoming_frame(time_ms_t now, struct overlay_interface *interface, s // duplicate the frame and queue it int overlay_forward_payload(struct overlay_frame *f){ IN(); - if (f->ttl == 0) + if (f->ttl == 0){ + if (config.debug.overlayframes) + DEBUGF("NOT FORWARDING, due to ttl=0"); RETURN(0); + } if (config.debug.overlayframes) DEBUGF("Forwarding payload for %s, ttl=%u", @@ -148,7 +141,6 @@ int parseMdpPacketHeader(struct decode_context *context, struct overlay_frame *f IN(); int process=1; int forward=2; - time_ms_t now = gettime_ms(); int flags = ob_get(buffer); if (flags<0) @@ -218,7 +210,7 @@ int parseMdpPacketHeader(struct decode_context *context, struct overlay_frame *f if (frame->ttl == 0) { forward = 0; if (config.debug.overlayframes) - DEBUGF("Don't forward when TTL expired"); + DEBUGF("NOT FORWARDING, due to ttl=0"); } if (flags & PAYLOAD_FLAG_LEGACY_TYPE){ @@ -231,9 +223,6 @@ int parseMdpPacketHeader(struct decode_context *context, struct overlay_frame *f frame->modifiers=flags; - if (frame->source) - frame->source->last_rx = now; - // if we can't understand one of the addresses, skip processing the payload if ((forward||process)&&context->invalid_addresses){ if (config.debug.overlayframes) @@ -271,8 +260,6 @@ int parseEnvelopeHeader(struct decode_context *context, struct overlay_interface RETURN(1); } - context->sender->last_rx = now; - // TODO probe unicast links when we detect an address change. // if this is a dummy announcement for a node that isn't in our routing table @@ -298,14 +285,6 @@ int parseEnvelopeHeader(struct decode_context *context, struct overlay_interface if (addr && (context->sender->last_probe==0 || now - context->sender->last_probe > interface->tick_ms*10)) overlay_send_probe(context->sender, *addr, interface, OQ_MESH_MANAGEMENT); - if ((!(packet_flags&PACKET_UNICAST)) && context->sender->last_acked + interface->tick_ms <= now){ - overlay_route_ack_selfannounce(interface, - context->sender->last_acked>now - 3*interface->tick_ms?context->sender->last_acked:now, - now,sender_interface,context->sender); - - context->sender->last_acked = now; - } - link_received_packet(context->sender, interface, sender_interface, sender_seq, packet_flags & PACKET_UNICAST); } diff --git a/overlay_queue.c b/overlay_queue.c index 49e2376b..7d54f101 100644 --- a/overlay_queue.c +++ b/overlay_queue.c @@ -524,19 +524,15 @@ overlay_fill_send_packet(struct outgoing_packet *packet, time_ms_t now) { } if(packet->buffer){ - if (ob_position(packet->buffer) > packet->header_length){ - - if (config.debug.packetconstruction) - ob_dump(packet->buffer,"assembled packet"); + if (config.debug.packetconstruction) + ob_dump(packet->buffer,"assembled packet"); - if (overlay_broadcast_ensemble(packet->interface, &packet->dest, ob_ptr(packet->buffer), ob_position(packet->buffer))){ - // sendto failed. We probably don't have a valid route - if (packet->unicast_subscriber){ - set_reachable(packet->unicast_subscriber, REACHABLE_NONE); - } + if (overlay_broadcast_ensemble(packet->interface, &packet->dest, ob_ptr(packet->buffer), ob_position(packet->buffer))){ + // sendto failed. We probably don't have a valid route + if (packet->unicast_subscriber){ + set_reachable(packet->unicast_subscriber, REACHABLE_NONE); } - }else - WARN("No payloads were sent?"); + } ob_free(packet->buffer); RETURN(1); } @@ -551,3 +547,12 @@ static void overlay_send_packet(struct sched_ent *alarm){ overlay_fill_send_packet(&packet, gettime_ms()); } + +int overlay_send_tick_packet(struct overlay_interface *interface){ + struct outgoing_packet packet; + bzero(&packet, sizeof(struct outgoing_packet)); + overlay_init_packet(&packet, NULL, 0, interface, interface->broadcast_address); + + overlay_fill_send_packet(&packet, gettime_ms()); + return 0; +} diff --git a/overlay_route.c b/overlay_route.c deleted file mode 100644 index bde0bb03..00000000 --- a/overlay_route.c +++ /dev/null @@ -1,959 +0,0 @@ -/* -Serval Distributed Numbering Architecture (DNA) -Copyright (C) 2010 Paul Gardner-Stephen - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -#include "serval.h" -#include "conf.h" -#include "str.h" -#include "strbuf.h" -#include "overlay_buffer.h" -#include "overlay_address.h" -#include "overlay_packet.h" - -/* - Here we implement the actual routing algorithm which is heavily based on BATMAN. - - The fundamental difference is that we want to allow the mesh to grow beyond the - size that could ordinarily be accomodated by the available bandwidth. Some - explanation follows. - - BATMAN operates by having nodes periodically send "hello" or originator messages, - either with a limited distribution or with a sufficiently high TTL to spread - over the whole network. - - The latter results in a super-linear bandwidth requirement as the network grows - in size. - - What we wish to do is to implement the BATMAN concept, but using link-local traffic - only. To do this we need to change the high-TTL originator frames into something - equivalent, but that does not get automatic network-wide distribution. - - What seems possible is to implement the BATMAN approach for link-local neighbours, - and then have each node periodically announce the link-score to the peers that - they know about, whether link-local or more distant. If the number of reported - peers is left unconstrained, super-linear bandwidth consumption will still occur. - - However, if the number of peers that each node announces is limited, then bandwidth - will be capped at a constant factor (which can be chosen based on the bandwidth - available). The trade-off being that each node will only be able to see some number - of "nearest" peers based on the available bandwidth. - - This seems an entirely reasonable outcome, and at least on the surface would appear - to solve our problem of wanting to allow a global-scale mesh, even if only local - connectivity is possible, in contrast to existing mesh protocols that will not allow - any connectivity once the number of nodes grows beyond a certain point. - - Remaining challenges that we have to think through are how to add a hierarchical - element to the mesh that might allow us to route traffic beyond a nodes' - neighbourhood of peers. - - There is some hope to extend the effective range beyond the immediate neighbourhood - to some degree by rotating the peers that a node reports on, so that a larger total - set of nodes becomes known to the mesh, in return for less frequent updates on their - link scores and optimal routes. - - This actually makes some logical sense, as the general direction in which to route - a frame to a distant node is less likely to change more slowly than for nearer nodes. - So we will attempt this. - - With some careful thought, this statistical announcement of peers also serves to allow - long-range but very low bandwidth links, e.g., satellite or dial-up, as well as long-shot - WiFi where bandwidth is less constrained. - - Questions arise as to the possibility of introducing routing loops through the use of - stale information. So we will certainly need to have some idea of the freshness of - routing data. - - Finally, all this works only for bidirectional links. We will need to think about how - to handle mono-directional links. BATMAN does this well, but I don't have the documentation - here at 36,000 feet to digest it and think about how to incorporate it. - - Having landed and thought about this a bit more, what we will do is send link-local - announcements which each direct neighbour Y will listen to and build up an estimated - probability of a packet sent by X reaching them. This information will be - periodically broadcast as the interface ticks, and not forwarded beyond link-local, - this preventing super-scalar traffic growth. When X hears that Y's P(X,Y) from - such a neighbour reception notice X can record P(X,Y) as its link score to Y. This - deals with asymmetric delivery probabilities for link-local neighbours. - - So how do we efficiently distribute P(X,Y) to our second-degree neighbours, which - we shall call Z? We will assume that P(X,Z) = P(X,Y)*P(Y,Z). Thus X needs to get - Y's set of P(Y,a) values. This is easy to arrange if X and Y are bidirectionally - link-local, as Y can periodically broadcast this information, and X can cache it. - This process will eventually build up the entire set P(X,b), where b are all nodes - on the mesh. However, it assumes that every link is bidirectional. What if X can - send directly to Y, but Y cannot send directly to X, i.e., P(X,Y)~1, P(Y,X)~0? - Provided that there is some path P(Y,m)*P(m,X) >0, then Y will eventually learn - about it. If Y knows that P(X,Y)>0, then it knows that X is a link-local neighbour - monodirectionally, and thus should endeavour to tell X about its direct neighbours. - This is fairly easy to arrange, and we will try this approach. - - So overall, this results in traffic at each node which is O(n^2+n*m) where n is the - number of direct neighbours and m is the total number of nodes reachable on the - mesh. As we can limit the number of nodes reachable on the mesh by having nodes - only advertise their k highest scoring nodes, we can effectively limit the traffic - to approximately linear with respect to reachable node count, but quadratic with - respect to the number of immediate neighbours. This seems a reasonable outcome. - - Related to this we need to continue thinking about how to handle intermittant links in a more - formal sense, including getting an idea of when nodes might reappear. - - Turning to the practical side of things, we need to keep track of reachability scores for - nodes via each of our immediate neighbours. Recognising the statistical nature of - the announcments, we probably want to keep track of some that have ceased to be neighbours - in case they become neighbours again. - - Probably it makes more sense to have a list of known nodes and the most recent and - highest scoring nodes by which we may reach them, complete with the sequence numbers of last - observation that they are based upon, and possibly more information down the track to - support intermittant links. - -*/ - - -struct overlay_neighbour_observation { - /* Sequence numbers are handled as ranges because the tick - rate can vary between interfaces, and we want to be able to - estimate the reliability of links to nodes that may have - several available interfaces. - We don't want sequence numbers to wrap too often, but we - would also like to support fairly fast ticking interfaces, - e.g., for gigabit type links. So lets go with 1ms granularity. */ - unsigned int s1; - unsigned int s2; - time_ms_t time_ms; - unsigned char sender_interface; - unsigned char valid; -}; - -struct overlay_neighbour { - time_ms_t last_observation_time_ms; - time_ms_t last_metric_update; - int most_recent_observation_id; - struct overlay_neighbour_observation observations[OVERLAY_MAX_OBSERVATIONS]; - overlay_node *node; - - /* Scores of visibility from each of the neighbours interfaces. - This is so that the sender knows which interface to use to reach us. - */ - unsigned char scores[OVERLAY_MAX_INTERFACES]; -}; - -/* We need to keep track of which nodes are our direct neighbours. - This means we need to keep an eye on how recently we received DIRECT announcements - from nodes, and keep a list of the most recent ones. The challenge is to keep the - list ordered without having to do copies or have nasty linked-list structures that - require lots of random memory reads to resolve. - - The simplest approach is to maintain a cache of neighbours and practise random - replacement. It is however succecptible to cache flushing attacks by adversaries, so - we will need something smarter in the long term. -*/ -#define overlay_max_neighbours 128 -int overlay_neighbour_count=0; -struct overlay_neighbour overlay_neighbours[overlay_max_neighbours]; - -int overlay_route_recalc_node_metrics(overlay_node *n, time_ms_t now); -int overlay_route_recalc_neighbour_metrics(struct overlay_neighbour *n, time_ms_t now); -struct overlay_neighbour *overlay_route_get_neighbour_structure(overlay_node *node, int createP); - - -overlay_node *get_node(struct subscriber *subscriber, int create){ - if (!subscriber) - return NULL; - - // we don't want to track routing info for ourselves. - if (subscriber->reachable==REACHABLE_SELF) - return NULL; - - if ((!subscriber->node) && create){ - subscriber->node = (overlay_node *)malloc(sizeof(overlay_node)); - memset(subscriber->node,0,sizeof(overlay_node)); - subscriber->node->subscriber = subscriber; - // if we're taking over routing calculations, make sure we invalidate any other calculations first - set_reachable(subscriber, REACHABLE_NONE); - // This info message is used by tests; don't alter or remove it. - INFOF("ADD OVERLAY NODE sid=%s", alloca_tohex_sid(subscriber->sid)); - } - - return subscriber->node; -} - -int overlay_route_ack_selfannounce(overlay_interface *recv_interface, - unsigned int s1,unsigned int s2, - int interface, - struct subscriber *subscriber) -{ - /* Acknowledge the receipt of a self-announcement of an immediate neighbour. - We could acknowledge immediately, but that requires the transmission of an - extra packet with all the overhead that entails. However, there is no real - need to send the ack out immediately. It should be entirely reasonable to - send the ack out with the next interface tick. - - So we can craft the ack and submit it to the queue. As the next-hop will get - determined at TX time, this will ensure that we send the packet out on the - right interface to reach the originator of the self-assessment. - - So all we need to do is craft the payload and put it onto the queue for - OVERLAY_MESH_MANAGEMENT messages. - - Also, we should check for older such frames on the queue and drop them. - - There is one caveat to the above: until the first selfannounce gets returned, - we don't have an open route. Thus we need to just make sure that the ack - goes out broadcast if we don't know about a return path. Once the return path - starts getting built, it should be fine. - - */ - - /* XXX Allocate overlay_frame structure and populate it */ - struct overlay_frame *out=NULL; - out=calloc(sizeof(struct overlay_frame),1); - if (!out) return WHY("calloc() failed to allocate an overlay frame"); - - out->type=OF_TYPE_SELFANNOUNCE_ACK; - out->modifiers=0; - out->ttl=6; /* maximum time to live for an ack taking an indirect route back - to the originator. If it were 1, then we would not be able to - handle mono-directional links (which WiFi is notorious for). - XXX 6 is quite an arbitrary selection however. */ - - /* Set destination of ack to source of observed frame */ - out->destination = subscriber; - /* set source to ourselves */ - out->source = my_subscriber; - - /* Set the time in the ack. Use the last sequence number we have seen - from this neighbour, as that may be helpful information for that neighbour - down the track. My policy is to communicate that information which should - be helpful for forming and maintaining the health of the mesh, as that way - each node can in potentially implement a different mesh routing protocol, - without breaking the wire protocol. This makes over-the-air software updates - much safer. - - Combining of adjacent observation reports may mean that the most recent - observation is not the last one in the list, also the wrapping of the sequence - numbers means we can't just take the highest-numbered sequence number. - So we need to take the observation which was most recently received. - */ - out->payload=ob_new(); - - /* XXX - we should merge contiguous observation reports so that packet loss - on the return path doesn't count against the link. */ - ob_append_ui32(out->payload,s1); - ob_append_ui32(out->payload,s2); - ob_append_byte(out->payload,interface); - - /* Add to queue. Keep broadcast status that we have assigned here if required to - get ack back to sender before we have a route. */ - out->queue=OQ_MESH_MANAGEMENT; - if (overlay_payload_enqueue(out)) - { - op_free(out); - return WHY("overlay_payload_enqueue(self-announce ack) failed"); - } - - /* XXX Remove any stale versions (or should we just freshen, and forget making - a new one, since it would be more efficient). */ - - return 0; -} - -int overlay_route_make_neighbour(overlay_node *n) -{ - if (!n) return WHY("n is NULL"); - - /* If it is already a neighbour, then return */ - if (n->neighbour_id) return 0; - - /* It isn't yet a neighbour, so find or free a neighbour slot */ - /* slot 0 is reserved, so skip it */ - if (!overlay_neighbour_count) overlay_neighbour_count=1; - if (overlay_neighbour_countneighbour_id=overlay_neighbour_count++; - } else { - /* Evict an old neighbour */ - int nid=1+random()%(overlay_max_neighbours-1); - if (overlay_neighbours[nid].node) overlay_neighbours[nid].node->neighbour_id=0; - n->neighbour_id=nid; - } - bzero(&overlay_neighbours[n->neighbour_id],sizeof(struct overlay_neighbour)); - overlay_neighbours[n->neighbour_id].node=n; - - return 0; -} - -struct overlay_neighbour *overlay_route_get_neighbour_structure(overlay_node *node, int createP) -{ - if (!node) - return NULL; - - /* Check if node is already a neighbour, or if not, make it one */ - if (!node->neighbour_id){ - if (!createP) - return NULL; - - if (overlay_route_make_neighbour(node)) - { WHY("overlay_route_make_neighbour() failed"); return NULL; } - } - - /* Get neighbour structure */ - return &overlay_neighbours[node->neighbour_id]; -} - -int overlay_route_node_can_hear_me(struct subscriber *subscriber, int sender_interface, - unsigned int s1,unsigned int s2, - time_ms_t now) -{ - /* 1. Find (or create) node entry for the node. - 2. Replace oldest observation with this observation. - 3. Update score of how reliably we can hear this node */ - - /* Get neighbour structure */ - - struct overlay_neighbour *neh=overlay_route_get_neighbour_structure(get_node(subscriber, 1),1 /* create if necessary */); - if (!neh) - return WHY("Unable to create neighbour structure"); - - int obs_index=neh->most_recent_observation_id; - int merge=0; - - /* See if this observation is contiguous with a previous one, if so, merge. - This not only reduces the number of observation slots we need, but dramatically speeds up - the scanning of recent observations when re-calculating observation scores. */ - while (neh->observations[obs_index].valid && neh->observations[obs_index].s2 >= s1 - 1) { - if (neh->observations[obs_index].sender_interface == sender_interface) { - if (config.debug.overlayrouting) - DEBUGF("merging observation into slot #%d s1=%u s2=%u", obs_index, neh->observations[obs_index].s1, neh->observations[obs_index].s2); - s1 = neh->observations[obs_index].s1; - merge=1; - break; - } - if (--obs_index < 0) - obs_index = OVERLAY_MAX_OBSERVATIONS - 1; - } - if (!merge) { - /* Replace oldest observation with this one */ - obs_index = neh->most_recent_observation_id + 1; - if (obs_index >= OVERLAY_MAX_OBSERVATIONS) - obs_index = 0; - } - - if (config.debug.overlayrouting) - DEBUGF("assign observation slot #%d: s1=%u s2=%u time_ms=%lld", obs_index, s1, s2, (long long)now); - neh->observations[obs_index].s1=s1; - neh->observations[obs_index].s2=s2; - neh->observations[obs_index].sender_interface=sender_interface; - neh->observations[obs_index].time_ms=now; - neh->observations[obs_index].valid=1; - - neh->most_recent_observation_id=obs_index; - neh->last_observation_time_ms=now; - /* force updating of stats for neighbour if we have added an observation */ - neh->last_metric_update=0; - - /* Update reachability metrics for node */ - if (overlay_route_recalc_neighbour_metrics(neh,now)) - return -1; - - if (config.debug.overlayroutemonitor) overlay_route_dump(); - return 0; -} - -/* XXX Think about scheduling this node's score for readvertising? */ -int overlay_route_recalc_node_metrics(overlay_node *n, time_ms_t now) -{ - int o; - int best_score=0; - int best_observation=-1; - int reachable = REACHABLE_NONE; - - overlay_interface *interface=NULL; - struct subscriber *next_hop=NULL; - - // TODO assumption timeout... - if (n->subscriber->reachable&REACHABLE_ASSUMED){ - reachable=n->subscriber->reachable; - interface=n->subscriber->interface; - } - - if (n->neighbour_id) - { - /* Node is also a direct neighbour, so check score that way */ - if (n->neighbour_id>overlay_max_neighbours||n->neighbour_id<0) - return WHY("n->neighbour_id is invalid."); - - struct overlay_neighbour *neighbour=&overlay_neighbours[n->neighbour_id]; - - int i; - for(i=0;iscores[i]>best_score) - { - best_score=neighbour->scores[i]; - best_observation=-1; - reachable=REACHABLE_BROADCAST; - interface = &overlay_interfaces[i]; - // if we've probed this unicast link, preserve the status - if ((n->subscriber->reachable&REACHABLE_UNICAST) && !(n->subscriber->reachable&REACHABLE_ASSUMED)) - reachable|=REACHABLE_UNICAST; - } - } - } - - if (best_score<=0){ - for(o=0;oobservations[o].observed_score && n->observations[o].sender->reachable&REACHABLE - && !(n->observations[o].sender->reachable&REACHABLE_ASSUMED)) - { - int discounted_score=n->observations[o].observed_score; - discounted_score-=(now-n->observations[o].rx_time)/1000; - if (discounted_score<0) discounted_score=0; - n->observations[o].corrected_score=discounted_score; - if (discounted_score>best_score) { - best_score=discounted_score; - best_observation=o; - reachable=REACHABLE_INDIRECT; - next_hop=n->observations[o].sender; - } - } - } - } - - /* Think about scheduling this node's score for readvertising if its score - has changed a lot? - Really what we probably want is to advertise when the score goes up, since - if it goes down, we probably don't need to say anything at all. - */ - - int diff=best_score - n->best_link_score; - if (diff>0) { - overlay_route_please_advertise(n); - if (config.debug.overlayroutemonitor) overlay_route_dump(); - } - int old_best = n->best_link_score; - - /* Remember new reachability information */ - switch (reachable){ - case REACHABLE_INDIRECT: - n->subscriber->next_hop = next_hop; - break; - case REACHABLE_BROADCAST: - case REACHABLE_BROADCAST|REACHABLE_UNICAST: - n->subscriber->interface = interface; - break; - } - n->best_link_score=best_score; - n->best_observation=best_observation; - set_reachable(n->subscriber, reachable); - - if (old_best && !best_score){ - INFOF("PEER UNREACHABLE, sid=%s", alloca_tohex_sid(n->subscriber->sid)); - overlay_send_probe(n->subscriber, n->subscriber->address, n->subscriber->interface, OQ_MESH_MANAGEMENT); - - }else if(best_score && !old_best){ - INFOF("PEER REACHABLE, sid=%s", alloca_tohex_sid(n->subscriber->sid)); - /* Make sure node is advertised soon */ - overlay_route_please_advertise(n); - } - - return 0; -} - -/* Recalculate node reachability metric, but only for directly connected nodes, - i.e., link-local neighbours. - - The scores should be calculated separately for each interface we can - hear the node on, so that this information can get back to the sender so that - they know the best interface to use when trying to talk to us. - - For now we will calculate a weighted sum of recent reachability over some fixed - length time interval. - The sequence numbers are all based on a milli-second clock. - - For mobile mesh networks we need this metric to be very fast adapting to new - paths, but to have a memory of older paths in case they are still useful. - - We thus combined equally a measure of very recent reachability (in last 10 - interface ticks perhaps?) with a measure of longer-term reachability (last - 200 seconds perhaps?). Also, if no recent observations, then we further - limit the score. -*/ -int overlay_route_recalc_neighbour_metrics(struct overlay_neighbour *n, time_ms_t now) -{ - int i; - time_ms_t most_recent_observation=0; - IN(); - - if (!n->node) - RETURN(WHY("Neighbour is not a node")); - - if (config.debug.overlayrouting) - DEBUGF("Updating neighbour metrics for %s", alloca_tohex_sid(n->node->subscriber->sid)); - - /* At most one update per half second */ - if (n->last_metric_update == 0) { - if (config.debug.overlayrouting) - DEBUG("last update was never"); - } else { - time_ms_t ago = now - n->last_metric_update; - if (ago < 500) { - if (config.debug.overlayrouting) - DEBUGF("last update was %lldms ago -- skipping", (long long)ago); - RETURN (0); - } - if (config.debug.overlayrouting) - DEBUGF("last update was %lldms ago", (long long)ago); - } - n->last_metric_update = now; - - /* Somewhere to remember how many milliseconds we have seen */ - int ms_observed_5sec[OVERLAY_MAX_INTERFACES]; - int ms_observed_200sec[OVERLAY_MAX_INTERFACES]; - for(i=0;iobservations[i].sender_interface; - overlay_interface *interface = &overlay_interfaces[interface_number]; - - if (!n->observations[i].valid || - n->observations[i].sender_interface>=OVERLAY_MAX_INTERFACES || - interface->state!=INTERFACE_STATE_UP) - continue; - - int long_interval=interface->tick_ms * 400; - int short_interval=interface->tick_ms * 10; - - /* Work out the interval covered by the observation. - The times are represented as lowest 32 bits of a 64-bit - millisecond clock. This introduces modulo problems, - however by using 32-bit modulo arithmatic here, we avoid - most of them. */ - unsigned int interval=n->observations[i].s2-n->observations[i].s1; - - /* Check the observation age, and ignore if too old */ - time_ms_t obs_age = now - n->observations[i].time_ms; - if (config.debug.overlayrouting) - DEBUGF("tallying obs: %lldms old, %ums long", obs_age,interval); - - /* Ignore very large intervals (>1hour) as being likely to be erroneous. - (or perhaps a clock wrap due to the modulo arithmatic) - - One tick per hour should be well and truly slow enough to do - 50KB per 12 hours, which is the minimum traffic charge rate - on an expensive BGAN satellite link. - */ - if (interval>=3600000 || obs_age>long_interval) - continue; - - if (config.debug.overlayrouting) - DEBUGF("adding %dms (interface %d '%s')", - interval,interface_number, - overlay_interfaces[interface_number].name); - - ms_observed_200sec[interface_number]+=interval; - if (obs_age<=short_interval){ - ms_observed_5sec[interface_number]+=(interval>short_interval?short_interval:interval); - } - - if (n->observations[i].time_ms>most_recent_observation) most_recent_observation=n->observations[i].time_ms; - } - - /* From the sum of observations calculate the metrics. - We want the score to climb quickly and then plateu. - */ - - int scoreChanged=0; - - for(i=0;itick_ms * 400; - int short_interval=interface->tick_ms * 10; - - int score; - if (ms_observed_200sec[i]>long_interval) ms_observed_200sec[i]=long_interval; - if (ms_observed_5sec[i]>short_interval) ms_observed_5sec[i]=short_interval; - if (ms_observed_200sec[i]==0) { - // Not observed at all - score=0; - } else { - int contrib_200=ms_observed_200sec[i]/(long_interval/128); - int contrib_5=ms_observed_5sec[i]/(short_interval/128); - - if (contrib_5<1) - score=contrib_200/2; - else - score=contrib_5+contrib_200; - - /* Deal with invalid sequence number ranges */ - if (score<1) score=1; - if (score>255) score=255; - } - - if (n->scores[i]!=score){ - scoreChanged=1; - n->scores[i]=score; - } - if ((config.debug.overlayrouting)&&score) - DEBUGF("Neighbour score on interface #%d = %d (observations for %dms)",i,score,ms_observed_200sec[i]); - } - if (scoreChanged) - overlay_route_recalc_node_metrics(n->node, now); - - RETURN(0); - OUT(); -} - -/* - Self-announcement acks bounce back to the self-announcer from immediate neighbours - who report the link score they have calculated based on listening to self-announces - from that peer. By acking them these scores then get to the originator, who then - has a score for the link to their neighbour, which is measuring the correct - direction of the link. - - Frames consist of 32bit timestamp in seconds followed by zero or more entries - of the format: - - 8bits - link score - 8bits - interface number - - this is followed by a 00 byte to indicate the end. - - That way we don't waste lots of bytes on single-interface nodes. - (But I am sure we can do better). - - These link scores should get stored in our node list as compared to our neighbour list, - with the node itself listed as the nexthop that the score is associated with. -*/ -int overlay_route_saw_selfannounce_ack(struct overlay_frame *f,long long now) -{ - IN(); - if (config.debug.overlayrouting) - DEBUGF("processing selfannounce ack (payload length=%d)",f->payload->sizeLimit); - - if (f->payload->sizeLimit<9) - RETURN(WHY("selfannounce ack packet too short")); - - unsigned int s1=ob_get_ui32(f->payload); - unsigned int s2=ob_get_ui32(f->payload); - int iface=ob_get(f->payload); - - // Call something like the following for each link - overlay_route_node_can_hear_me(f->source,iface,s1,s2,now); - - RETURN(0); - OUT(); -} - -/* if to and via are the same, then this is evidence that we can get to the - node directly. */ -int overlay_route_record_link(time_ms_t now, struct subscriber *to, - struct subscriber *via,int sender_interface, - unsigned int s1,unsigned int s2,int score, - int gateways_en_route) -{ - IN(); - if (config.debug.overlayrouting) - DEBUGF("to=%s, via=%s, sender_interface=%d, s1=%d, s2=%d score=%d gateways_en_route=%d", - alloca_tohex_sid(to->sid), alloca_tohex_sid(via->sid), sender_interface, s1, s2, - score, gateways_en_route - ); - - if (sender_interface>OVERLAY_MAX_INTERFACES || score == 0) { - if (config.debug.overlayrouting) - DEBUG("invalid report"); - RETURN(0); - } - - overlay_node *n = get_node(to,1); - if (!n) - RETURN(WHY("Could not create entry for node")); - - int slot = -1; - int i; - for (i = 0; i < OVERLAY_MAX_OBSERVATIONS; ++i) { - /* Take note of where we can find space for a fresh observation */ - if (slot == -1 && n->observations[i].observed_score == 0) - slot = i; - /* If the intermediate host ("via") address and interface numbers match, then overwrite old - observation with new one */ - if (n->observations[i].sender == via) { - slot = i; - break; - } - } - /* If in doubt, replace a random slot. - XXX - we should probably replace the lowest scoring slot instead, but random will work well - enough for now. */ - if (slot == -1) { - slot = random() % OVERLAY_MAX_OBSERVATIONS; - if (config.debug.overlayrouting) - DEBUGF("allocate observation slot=%d", slot); - } else { - if (config.debug.overlayrouting) - DEBUGF("overwrite observation slot=%d (sender=%s interface=%u observed_score=%u rx_time=%lld)", - slot, - n->observations[slot].sender?alloca_tohex_sid(n->observations[slot].sender->sid):"[None]", - n->observations[slot].interface, - n->observations[slot].observed_score, - n->observations[slot].rx_time - ); - } - - n->observations[slot].observed_score=0; - n->observations[slot].gateways_en_route=gateways_en_route; - n->observations[slot].rx_time=now; - n->observations[slot].sender = via; - n->observations[slot].observed_score=score; - n->observations[slot].interface=sender_interface; - - /* Remember that we have seen an observation for this node. - XXX - This should actually be set to the time that the last first-hand - observation of the node was made, so that stale information doesn't build - false belief of reachability. - This is why the timestamp field is supplied, which is just copied from the - original selfannouncement ack. We just have to register it against our - local time to interpret it (XXX which comes with some risks related to - clock-skew, but we will deal with those in due course). - */ - n->last_observation_time_ms=now; - if (s2>n->last_first_hand_observation_time_millisec) - n->last_first_hand_observation_time_millisec=s2; - - overlay_route_recalc_node_metrics(n,now); - - if (config.debug.overlayroutemonitor) - overlay_route_dump(); - - RETURN(0); - OUT(); -} - -int node_dump(struct subscriber *subscriber, void *context){ - strbuf *b=context; - overlay_node *node = subscriber->node; - int o; - - if (node){ - - strbuf_sprintf(*b," %s* : %d :", alloca_tohex(subscriber->sid, 7), - node->best_link_score); - for(o=0;oobservations[o].observed_score) - { - overlay_node_observation *ob=&node->observations[o]; - if (ob->corrected_score) - strbuf_sprintf(*b," %d/%d via %s*", - ob->corrected_score,ob->gateways_en_route, - alloca_tohex(ob->sender->sid,7)); - } - } - strbuf_sprintf(*b,"\n"); - } - return 0; -} - -int overlay_route_dump() -{ - int n,i; - time_ms_t now = gettime_ms(); - strbuf b = strbuf_alloca(8192); - - strbuf_sprintf(b,"Overlay Local Identities\n------------------------\n"); - int cn,in,kp; - for(cn=0;cncontext_count;cn++) - for(in=0;incontexts[cn]->identity_count;in++) - for(kp=0;kpcontexts[cn]->identities[in]->keypair_count;kp++) - if (keyring->contexts[cn]->identities[in]->keypairs[kp]->type - ==KEYTYPE_CRYPTOBOX) - { - for(i=0;icontexts[cn]->identities[in] - ->keypairs[kp]->public_key[i]); - strbuf_sprintf(b,"\n"); - } - DEBUG(strbuf_str(b)); - - strbuf_reset(b); - strbuf_sprintf(b,"\nOverlay Neighbour Table\n------------------------\n"); - for(n=0;nsubscriber->sid, 7), - (long long)(now - overlay_neighbours[n].last_observation_time_ms)); - for(i=0;i0 && overlay_neighbours[neighbour_id].node) - if (overlay_route_recalc_neighbour_metrics(&overlay_neighbours[neighbour_id],now)) - WHY("overlay_route_recalc_neighbour_metrics() failed"); - - return 0; -} - -/* Updating the route score to get to a node it trickier, as they might not be a - neighbour. Even if they are a neighbour, all we have to go on is the node's - observations. - From these we can work out a discounted score based on their age. - - XXX This is where the discounting should be modified for nodes that are - updated less often as they exhibit score stability. Actually, for the - most part we can tolerate these without any special action, as their high - scores will keep them reachable for longer anyway. -*/ -int overlay_route_tick_node(struct subscriber *subscriber, void *context) -{ - if (subscriber->node) - overlay_route_recalc_node_metrics(subscriber->node, gettime_ms()); - return 0; -} - -void overlay_route_tick(struct sched_ent *alarm) -{ - int n; - time_ms_t now = gettime_ms(); - - /* Go through some of neighbour list */ - for (n=0;nalarm = gettime_ms()+5000; - alarm->deadline = alarm->alarm+100; - schedule(alarm); - return; -} - -int overlay_route_node_info(overlay_mdp_nodeinfo *node_info) -{ - time_ms_t now = gettime_ms(); - - if (0) - DEBUGF("Looking for node %s* (prefix len=0x%x)", - alloca_tohex(node_info->sid, node_info->sid_prefix_length), - node_info->sid_prefix_length - ); - - node_info->foundP=0; - - /* check if it is a local identity */ - int cn,in,kp; - for(cn=0;cncontext_count;cn++) - for(in=0;incontexts[cn]->identity_count;in++) - for(kp=0;kpcontexts[cn]->identities[in]->keypair_count;kp++) - if (keyring->contexts[cn]->identities[in]->keypairs[kp]->type - ==KEYTYPE_CRYPTOBOX) - { - if (!memcmp(&node_info->sid[0], - &keyring->contexts[cn]->identities[in] - ->keypairs[kp]->public_key[0], - node_info->sid_prefix_length/2)) - { - node_info->foundP=1; - node_info->localP=1; - node_info->neighbourP=0; - node_info->time_since_last_observation = 0; - node_info->score=256; - node_info->interface_number=-1; - bcopy(&keyring->contexts[cn]->identities[in] - ->keypairs[kp]->public_key[0], - &node_info->sid[0],SID_SIZE); - - return 0; - } - } - - struct subscriber *subscriber = find_subscriber(node_info->sid, node_info->sid_prefix_length/2, 0); - if (subscriber && subscriber->node){ - overlay_node *node = subscriber->node; - - node_info->foundP=1; - node_info->localP=0; - node_info->score=-1; - node_info->interface_number=-1; - bcopy(subscriber->sid, - node_info->sid,SID_SIZE); - - if (subscriber->node->neighbour_id){ - int n = subscriber->node->neighbour_id; - node_info->neighbourP=1; - node_info->time_since_last_observation = now - overlay_neighbours[n].last_observation_time_ms; - - int i; - for(i=0;inode_info->score) - { - node_info->score=overlay_neighbours[n].scores[i]; - node_info->interface_number=i; - } - - }else{ - node_info->neighbourP=0; - node_info->time_since_last_observation = -1; - int o; - for(o=0;oobservations[o].observed_score) - { - overlay_node_observation *ob - =&node->observations[o]; - if (ob->corrected_score>node_info->score) { - node_info->score=ob->corrected_score; - } - if (node_info->time_since_last_observation == -1 || now - ob->rx_time < node_info->time_since_last_observation) - node_info->time_since_last_observation = now - ob->rx_time; - } - } - } - - return 0; -} diff --git a/route_link.c b/route_link.c index 17cfac94..81163fb2 100644 --- a/route_link.c +++ b/route_link.c @@ -225,6 +225,9 @@ static void update_path_score(struct neighbour *neighbour, struct link *link){ static int find_best_link(struct subscriber *subscriber) { + if (subscriber->reachable==REACHABLE_SELF) + return 0; + struct link_state *state = get_link_state(subscriber); if (state->route_version == route_version) return 0; @@ -280,6 +283,16 @@ next: state->route_version = route_version; state->calculating = 0; + if (next_hop == NULL){ + if (!(subscriber->reachable & REACHABLE_ASSUMED)) + subscriber->reachable = REACHABLE_NONE; + } else if (next_hop == subscriber){ + subscriber->reachable = REACHABLE_BROADCAST | (subscriber->reachable & REACHABLE_UNICAST); + } else { + subscriber->reachable = REACHABLE_INDIRECT; + } + subscriber->next_hop = next_hop; + return 0; } @@ -496,6 +509,10 @@ struct neighbour_link * get_neighbour_link(struct neighbour *neighbour, struct o // track stats for receiving packets from this neighbour int link_received_packet(struct subscriber *subscriber, struct overlay_interface *interface, int sender_interface, int sender_seq, int unicast) { + // TODO better handling of unicast routes + if (unicast) + return 0; + struct neighbour *neighbour = get_neighbour(subscriber, 1); struct neighbour_link *link=get_neighbour_link(neighbour, interface, sender_interface); time_ms_t now = gettime_ms(); diff --git a/serval.h b/serval.h index 7fd7d590..de246d2e 100644 --- a/serval.h +++ b/serval.h @@ -418,12 +418,12 @@ typedef struct overlay_interface { unsigned tick_ms; /* milliseconds per tick */ unsigned int uartbps; // set serial port speed (which might be different from link speed) int ctsrts; // enabled hardware flow control if non-zero - + + // time last packet was sent on this interface + time_ms_t last_tx; + struct subscriber *next_advert; - /* The time of the last tick on this interface in milli seconds */ - time_ms_t last_tick_ms; - /* sequence number of last packet sent on this interface. Used to allow NACKs that can request retransmission of recent packets. */ @@ -535,53 +535,12 @@ int rfs_length(int l); int rfs_encode(int l,unsigned char *b); int rfs_decode(unsigned char *b,int *offset); -typedef struct overlay_node_observation { - unsigned char observed_score; /* serves as validty check also */ - unsigned char corrected_score; - unsigned char gateways_en_route; - unsigned char RESERVED; /* for alignment */ - unsigned char interface; - time_ms_t rx_time; - struct subscriber *sender; -} overlay_node_observation; - - -typedef struct overlay_node { - struct subscriber *subscriber; - int neighbour_id; /* 0=not a neighbour */ - int most_recent_observation_id; - int best_link_score; - int best_observation; - unsigned int last_first_hand_observation_time_millisec; - time_ms_t last_observation_time_ms; - /* When did we last advertise this node on each interface, and what score - did we advertise? */ - time_ms_t most_recent_advertisment_ms[OVERLAY_MAX_INTERFACES]; - unsigned char most_recent_advertised_score[OVERLAY_MAX_INTERFACES]; - overlay_node_observation observations[OVERLAY_MAX_OBSERVATIONS]; -} overlay_node; - -int overlay_route_saw_selfannounce_ack(struct overlay_frame *f, time_ms_t now); -int overlay_route_ack_selfannounce(overlay_interface *recv_interface, - unsigned int s1,unsigned int s2, - int interface, - struct subscriber *subscriber); -overlay_node *overlay_route_find_node(const unsigned char *sid,int prefixLen,int createP); - int overlayServerMode(); int overlay_payload_enqueue(struct overlay_frame *p); int overlay_queue_remaining(int queue); int overlay_queue_schedule_next(time_ms_t next_allowed_packet); -int overlay_route_record_link( time_ms_t now, struct subscriber *to, - struct subscriber *via,int sender_interface, - unsigned int s1,unsigned int s2,int score,int gateways_en_route); -int overlay_route_dump(); -int overlay_route_queue_advertisements(overlay_interface *interface); -int ovleray_route_please_advertise(overlay_node *n); - -int overlay_route_saw_advertisements(int i, struct overlay_frame *f, struct decode_context *context, time_ms_t now); +int overlay_send_tick_packet(struct overlay_interface *interface); int overlay_rhizome_saw_advertisements(int i, struct overlay_frame *f, time_ms_t now); -int overlay_route_please_advertise(overlay_node *n); int rhizome_server_get_fds(struct pollfd *fds,int *fdcount,int fdmax); int rhizome_saw_voice_traffic(); int overlay_saw_mdp_containing_frame(struct overlay_frame *f, time_ms_t now); @@ -800,7 +759,6 @@ void overlay_packetradio_poll(struct sched_ent *alarm); int overlay_packetradio_setup_port(overlay_interface *interface); int overlay_packetradio_tx_packet(struct overlay_frame *frame); void overlay_dummy_poll(struct sched_ent *alarm); -void overlay_route_tick(struct sched_ent *alarm); void server_config_reload(struct sched_ent *alarm); void server_shutdown_check(struct sched_ent *alarm); void overlay_mdp_poll(struct sched_ent *alarm); diff --git a/sourcefiles.mk b/sourcefiles.mk index 67e440b8..00c34a07 100644 --- a/sourcefiles.mk +++ b/sourcefiles.mk @@ -27,7 +27,6 @@ SERVAL_SOURCES = $(SERVAL_BASE)audiodevices.c \ $(SERVAL_BASE)monitor-cli.c \ $(SERVAL_BASE)net.c \ $(SERVAL_BASE)overlay.c \ - $(SERVAL_BASE)overlay_advertise.c \ $(SERVAL_BASE)overlay_address.c \ $(SERVAL_BASE)overlay_buffer.c \ $(SERVAL_BASE)overlay_interface.c \ @@ -39,7 +38,6 @@ SERVAL_SOURCES = $(SERVAL_BASE)audiodevices.c \ $(SERVAL_BASE)overlay_olsr.c \ $(SERVAL_BASE)overlay_packetformats.c \ $(SERVAL_BASE)overlay_payload.c \ - $(SERVAL_BASE)overlay_route.c \ $(SERVAL_BASE)packetformats.c \ $(SERVAL_BASE)performance_timing.c \ $(SERVAL_BASE)randombytes.c \ diff --git a/tests/dnaprotocol b/tests/dnaprotocol index 1df26c51..c4a1aa0d 100755 --- a/tests/dnaprotocol +++ b/tests/dnaprotocol @@ -21,28 +21,12 @@ source "${0%/*}/../testframework.sh" source "${0%/*}/../testdefs.sh" -instances_reach_each_other() { - local I J - for I; do - for J; do - [ $I = $J ] && continue - local logvar=LOG${I#+} - local sidvar=SID${J#+} - if ! grep "PEER REACHABLE, sid=${!sidvar}" "${!logvar}"; then - return 1 - fi - done - done - return 0 -} - setup() { setup_servald assert_no_servald_processes foreach_instance +A +B create_single_identity configure_servald_server() { set_server_vars; } start_servald_instances +A +B - wait_until --sleep=0.25 instances_reach_each_other +A +B set_instance +A } @@ -146,7 +130,6 @@ EOF set dna.helper.argv.1 "$instance_name" } start_servald_instances +A +B +C +D - wait_until --sleep=0.25 instances_reach_each_other +A +B +C +D set_instance +D } diff --git a/tests/routing b/tests/routing index 4b5bfda1..8fc4144d 100755 --- a/tests/routing +++ b/tests/routing @@ -44,6 +44,7 @@ start_routing_instance() { set log.console.show_time on \ set debug.mdprequests yes \ set debug.linkstate yes \ + set debug.verbose yes \ set rhizome.enable no start_servald_server wait_until interface_up @@ -330,8 +331,11 @@ test_circle() { executeOk_servald route print assertStdoutGrep --matches=1 "^$SIDC:INDIRECT :" stop_servald_server +B - foreach_instance +A +C +D +E +F +G +H \ - wait_until --timeout=20 instance_offline +B + foreach_instance +A +C \ + wait_until --timeout=10 instance_offline +B + foreach_instance +A +C \ + wait_until --timeout=10 has_seen_instances +A +C + set_instance +A executeOk_servald mdp ping --timeout=3 $SIDC 1 tfw_cat --stdout --stderr executeOk_servald route print