mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-02-20 17:33:08 +00:00
Replace old routing engine with new link state engine
This commit is contained in:
parent
c6a9564dc3
commit
9ee09e36a0
@ -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","[<address>]",NULL}, 0,
|
||||
"Scan the network for serval peers. If no argument is supplied, all local addresses will be scanned."},
|
||||
{app_node_info,{"node","info","<sid>",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","<did>","[<timeout>]",NULL}, 0,
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.");
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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_count<OVERLAY_MAX_ADVERTISEMENT_REQUESTS)
|
||||
{
|
||||
oad_requests[oad_request_count++]=n;
|
||||
return 0;
|
||||
}
|
||||
else return 1;
|
||||
}
|
||||
|
||||
struct advertisement_state{
|
||||
struct overlay_buffer *payload;
|
||||
struct subscriber *next_advertisement;
|
||||
};
|
||||
|
||||
int advertise(struct advertisement_state *state, struct subscriber *subscriber, char score, char gateways){
|
||||
// never send the full sid in an advertisement
|
||||
subscriber->send_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();
|
||||
}
|
@ -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);
|
||||
|
@ -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={
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
959
overlay_route.c
959
overlay_route.c
@ -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_count<overlay_max_neighbours) {
|
||||
/* Use next free neighbour slot */
|
||||
n->neighbour_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;i<overlay_interface_count;i++)
|
||||
{
|
||||
if (overlay_interfaces[i].state==INTERFACE_STATE_UP &&
|
||||
neighbour->scores[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;o<OVERLAY_MAX_OBSERVATIONS;o++)
|
||||
{
|
||||
// only count observations from neighbours that we *know* we have a 2 way path to
|
||||
if (n->observations[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;i<OVERLAY_MAX_INTERFACES;i++) {
|
||||
ms_observed_5sec[i]=0;
|
||||
ms_observed_200sec[i]=0;
|
||||
}
|
||||
|
||||
/* XXX This simple accumulation scheme does not weed out duplicates, nor weight for recency of
|
||||
communication.
|
||||
Also, we might like to take into account the interface we received
|
||||
the announcements on. */
|
||||
for(i=0;i<OVERLAY_MAX_OBSERVATIONS;i++) {
|
||||
int interface_number=n->observations[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;i<OVERLAY_MAX_INTERFACES;i++) {
|
||||
overlay_interface *interface = &overlay_interfaces[i];
|
||||
int long_interval=interface->tick_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;o<OVERLAY_MAX_OBSERVATIONS;o++)
|
||||
{
|
||||
if (node->observations[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;cn<keyring->context_count;cn++)
|
||||
for(in=0;in<keyring->contexts[cn]->identity_count;in++)
|
||||
for(kp=0;kp<keyring->contexts[cn]->identities[in]->keypair_count;kp++)
|
||||
if (keyring->contexts[cn]->identities[in]->keypairs[kp]->type
|
||||
==KEYTYPE_CRYPTOBOX)
|
||||
{
|
||||
for(i=0;i<SID_SIZE;i++)
|
||||
strbuf_sprintf(b,"%02x",keyring->contexts[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;n<overlay_neighbour_count;n++)
|
||||
if (overlay_neighbours[n].node)
|
||||
{
|
||||
strbuf_sprintf(b," %s* : %lldms ago :",
|
||||
alloca_tohex(overlay_neighbours[n].node->subscriber->sid, 7),
|
||||
(long long)(now - overlay_neighbours[n].last_observation_time_ms));
|
||||
for(i=0;i<OVERLAY_MAX_INTERFACES;i++)
|
||||
if (overlay_neighbours[n].scores[i])
|
||||
strbuf_sprintf(b," %d(via #%d)",
|
||||
overlay_neighbours[n].scores[i],i);
|
||||
strbuf_sprintf(b,"\n");
|
||||
}
|
||||
DEBUG(strbuf_str(b));
|
||||
|
||||
strbuf_reset(b);
|
||||
strbuf_sprintf(b,"Overlay Mesh Route Table\n------------------------\n");
|
||||
|
||||
enum_subscribers(NULL, node_dump, &b);
|
||||
|
||||
DEBUG(strbuf_str(b));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Ticking neighbours is easy; we just pretend we have heard from them again,
|
||||
and recalculate the score that way, which already includes a mechanism for
|
||||
taking into account the age of the most recent observation */
|
||||
int overlay_route_tick_neighbour(int neighbour_id, time_ms_t now)
|
||||
{
|
||||
if (neighbour_id>0 && 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;n<overlay_max_neighbours;n++)
|
||||
overlay_route_tick_neighbour(n,now);
|
||||
|
||||
/* Go through the node list */
|
||||
enum_subscribers(NULL, overlay_route_tick_node, NULL);
|
||||
|
||||
/* Update callback interval based on how much work we have to do */
|
||||
alarm->alarm = 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;cn<keyring->context_count;cn++)
|
||||
for(in=0;in<keyring->contexts[cn]->identity_count;in++)
|
||||
for(kp=0;kp<keyring->contexts[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;i<OVERLAY_MAX_INTERFACES;i++)
|
||||
if (overlay_neighbours[n].scores[i]>node_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;o<OVERLAY_MAX_OBSERVATIONS;o++)
|
||||
if (node->observations[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;
|
||||
}
|
17
route_link.c
17
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();
|
||||
|
52
serval.h
52
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);
|
||||
|
@ -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 \
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user