From 9a50d8a9ef95a8ac3fd681d171abb4ab525fa3ac Mon Sep 17 00:00:00 2001 From: Jeremy Lakeman Date: Fri, 9 Aug 2013 16:19:45 +0930 Subject: [PATCH] Route mulit-hop via unicast links --- overlay_address.c | 6 ++-- overlay_packet.h | 2 ++ overlay_packetformats.c | 3 ++ overlay_queue.c | 65 ++++++----------------------------- route_link.c | 75 +++++++++++++++++++++++++++++------------ serval.h | 2 +- tests/directory_service | 4 +-- tests/routing | 63 ++++++++++++++++++++++------------ 8 files changed, 118 insertions(+), 102 deletions(-) diff --git a/overlay_address.c b/overlay_address.c index e2ebef73..6f61f0f3 100644 --- a/overlay_address.c +++ b/overlay_address.c @@ -262,11 +262,11 @@ static int add_explain_response(struct subscriber *subscriber, void *context){ // if one of our identities is unknown, // the header of this packet must include our full sid. if (subscriber->reachable==REACHABLE_SELF){ - subscriber->send_full=1; if (subscriber==my_subscriber){ response->please_explain->source_full=1; return 0; } + subscriber->send_full=1; } // add the whole subscriber id to the payload, stop if we run out of space @@ -399,11 +399,11 @@ int send_please_explain(struct decode_context *context, struct subscriber *sourc if (!context->sender) frame->source_full=1; - if (destination && (destination->reachable & REACHABLE)){ + if (destination){ frame->ttl = PAYLOAD_TTL_DEFAULT; // MAX? frame->destination = destination; + frame->source_full=1; }else{ - DEBUGF("Need to send explanation to destination that isn't routable"); // send both a broadcast & unicast response out the same interface this packet arrived on. frame->ttl=1;// how will this work with olsr?? if (context->interface){ diff --git a/overlay_packet.h b/overlay_packet.h index ff8116c8..b1d00bcc 100644 --- a/overlay_packet.h +++ b/overlay_packet.h @@ -51,6 +51,8 @@ struct overlay_frame { time_ms_t delay_until; struct packet_destination destinations[MAX_PACKET_DESTINATIONS]; int destination_count; + int transmit_count; + // each payload gets a sequence number that is reused on retransmission int32_t mdp_sequence; diff --git a/overlay_packetformats.c b/overlay_packetformats.c index 721f6f85..513fdefe 100644 --- a/overlay_packetformats.c +++ b/overlay_packetformats.c @@ -303,6 +303,9 @@ int parseEnvelopeHeader(struct decode_context *context, struct overlay_interface RETURN(1); } + if (context->packet_version > context->sender->max_packet_version) + context->sender->max_packet_version=context->packet_version; + if (interface->point_to_point && interface->other_device!=context->sender){ INFOF("Established point to point link with %s on %s", alloca_tohex_sid(context->sender->sid), interface->name); context->point_to_point_device = context->interface->other_device = context->sender; diff --git a/overlay_queue.c b/overlay_queue.c index 00b6dfa3..dbc74357 100644 --- a/overlay_queue.c +++ b/overlay_queue.c @@ -152,19 +152,6 @@ int overlay_payload_enqueue(struct overlay_frame *p) if (!p) return WHY("Cannot queue NULL"); - do{ - if (p->destination_count>0) - break; - if (!p->destination) - break; - if (p->destination->reachable&REACHABLE) - break; - if (directory_service&&directory_service->reachable&REACHABLE) - break; - return WHYF("Cannot send %x packet, destination %s is unreachable", p->type, - alloca_tohex_sid(p->destination->sid)); - } while(0); - if (p->queue>=OQ_MAX) return WHY("Invalid queue specified"); @@ -199,7 +186,7 @@ int overlay_payload_enqueue(struct overlay_frame *p) // hook to allow for flooding via olsr olsr_send(p); - link_add_broadcast_destinations(p); + link_add_destinations(p); // just drop it now if (p->destination_count == 0){ @@ -311,15 +298,9 @@ overlay_calc_queue_time(overlay_txqueue *queue, struct overlay_frame *frame){ return 0; } }else{ - // ignore payload alarm if the destination is currently unreachable if (!frame->destination){ return 0; } - if (!(frame->destination->reachable&REACHABLE)){ - if (!directory_service || !(directory_service->reachable&REACHABLE)){ - return 0; - } - } } if (next_allowed_packet < frame->delay_until) @@ -366,38 +347,12 @@ overlay_stuff_packet(struct outgoing_packet *packet, overlay_txqueue *queue, tim if (packet->buffer && ob_limit(frame->payload) > ob_remaining(packet->buffer)) goto skip; - if (frame->destination_count==0){ - frame->next_hop = frame->destination; + if (frame->destination_count==0 && frame->destination){ + link_add_destinations(frame); - if (frame->next_hop){ - // Where do we need to route this payload next? - int r = frame->next_hop->reachable; - - // first, should we try to bounce this payload off the directory service? - if (r==REACHABLE_NONE && - directory_service && - frame->next_hop!=directory_service){ - frame->next_hop=directory_service; - r=directory_service->reachable; - } - - // do we need to route via a neighbour? - if (r&REACHABLE_INDIRECT){ - frame->next_hop = frame->next_hop->next_hop; - r = frame->next_hop->reachable; - } - - if (!(r&REACHABLE_DIRECT)){ - goto skip; - } - - frame->destinations[frame->destination_count++].destination=add_destination_ref(frame->next_hop->destination); - - // degrade packet version if required to reach the destination - if (frame->packet_version > frame->next_hop->max_packet_version) - frame->packet_version = frame->next_hop->max_packet_version; - - } + // degrade packet version if required to reach the destination + if (frame->packet_version > frame->next_hop->max_packet_version) + frame->packet_version = frame->next_hop->max_packet_version; } int destination_index=-1; @@ -485,7 +440,8 @@ overlay_stuff_packet(struct outgoing_packet *packet, overlay_txqueue *queue, tim } frame->destinations[destination_index].delay_until = now+200; - + frame->transmit_count++; + if (config.debug.overlayframes){ DEBUGF("Appended payload %p, %d type %x len %d for %s via %s", frame, frame->mdp_sequence, @@ -495,8 +451,9 @@ overlay_stuff_packet(struct outgoing_packet *packet, overlay_txqueue *queue, tim } // dont retransmit if we aren't sending sequence numbers, or we've been asked not to - if (frame->packet_version<1 || frame->resend<=0 || - frame->enqueued_at + queue->latencyTarget < frame->destinations[destination_index].delay_until){ + if (frame->packet_version<1 || frame->resend<=0 || packet->seq==-1){ + if (config.debug.overlayframes) + DEBUGF("Not waiting for retransmission (%d, %d, %d)", frame->packet_version, frame->resend, packet->seq); remove_destination(frame, destination_index); if (frame->destination_count==0){ frame = overlay_queue_remove(queue, frame); diff --git a/route_link.c b/route_link.c index 269dd914..1a2d8e1a 100644 --- a/route_link.c +++ b/route_link.c @@ -777,7 +777,7 @@ static int link_send_neighbours() struct link_out *out = n->out_links; while(out){ - if (out->destination->unicast){ + if (out->destination->tick_ms>0 && out->destination->unicast){ if (out->destination->last_tx + out->destination->tick_ms < now) overlay_send_tick_packet(out->destination); if (out->destination->last_tx + out->destination->tick_ms < link_send_alarm.alarm) @@ -871,31 +871,63 @@ struct link_in * get_neighbour_link(struct neighbour *neighbour, struct overlay_ return link; } -int link_add_broadcast_destinations(struct overlay_frame *frame) +int link_add_destinations(struct overlay_frame *frame) { - char added_interface[OVERLAY_MAX_INTERFACES]; - bzero(added_interface, sizeof(added_interface)); - - struct neighbour *neighbour = neighbours; - for(;neighbour;neighbour = neighbour->_next){ - if (neighbour->subscriber->reachable&REACHABLE_DIRECT){ - struct network_destination *dest = neighbour->subscriber->destination; - // TODO set packet version per destination - if (frame->packet_version > neighbour->subscriber->max_packet_version) - frame->packet_version = neighbour->subscriber->max_packet_version; - - if (!dest->unicast){ - // make sure we only add broadcast interfaces once - int id = dest->interface - overlay_interfaces; - if (added_interface[id]){ - continue; + if (frame->destination){ + frame->next_hop = frame->destination; + + // if the destination is unreachable, but we have a reachable directory service + // forward it through the directory service + if (frame->next_hop->reachable==REACHABLE_NONE + && directory_service + && frame->next_hop!=directory_service + && directory_service->reachable&REACHABLE) + frame->next_hop = directory_service; + + if (frame->next_hop->reachable==REACHABLE_NONE){ + // if the destination is a neighbour, add all probable destinations + struct neighbour *n = get_neighbour(frame->destination, 0); + if (n){ + struct link_out *out = n->out_links; + while(out){ + frame->destinations[frame->destination_count++].destination = add_destination_ref(out->destination); + out = out->_next; } } - - frame->destinations[frame->destination_count++].destination=add_destination_ref(dest); + } + + if ((frame->next_hop->reachable&REACHABLE)==REACHABLE_INDIRECT) + frame->next_hop = frame->next_hop->next_hop; + + if (frame->next_hop->reachable&REACHABLE_DIRECT){ + if (frame->destination_count < MAX_PACKET_DESTINATIONS) + frame->destinations[frame->destination_count++].destination=add_destination_ref(frame->next_hop->destination); + } + }else{ + char added_interface[OVERLAY_MAX_INTERFACES]; + bzero(added_interface, sizeof(added_interface)); + + struct neighbour *neighbour = neighbours; + for(;neighbour;neighbour = neighbour->_next){ + if (neighbour->subscriber->reachable&REACHABLE_DIRECT){ + struct network_destination *dest = neighbour->subscriber->destination; + // TODO set packet version per destination + if (frame->packet_version > neighbour->subscriber->max_packet_version) + frame->packet_version = neighbour->subscriber->max_packet_version; + + if (!dest->unicast){ + // make sure we only add broadcast interfaces once + int id = dest->interface - overlay_interfaces; + if (added_interface[id]){ + continue; + } + } + + if (frame->destination_count < MAX_PACKET_DESTINATIONS) + frame->destinations[frame->destination_count++].destination=add_destination_ref(dest); + } } } - return 0; } @@ -977,6 +1009,7 @@ static struct link_out *create_out_link(struct neighbour *neighbour, overlay_int else ret->destination = add_destination_ref(interface->destination); ret->timeout = gettime_ms()+ret->destination->tick_ms*2; + update_alarm(gettime_ms()+5); } return ret; } diff --git a/serval.h b/serval.h index e3a44b95..9a55ee15 100644 --- a/serval.h +++ b/serval.h @@ -853,7 +853,7 @@ int link_state_legacy_ack(struct overlay_frame *frame, time_ms_t now); int link_state_ack_soon(struct subscriber *sender); int link_state_should_forward_broadcast(struct subscriber *transmitter); int link_unicast_ack(struct subscriber *subscriber, struct overlay_interface *interface, struct sockaddr_in addr); -int link_add_broadcast_destinations(struct overlay_frame *frame); +int link_add_destinations(struct overlay_frame *frame); int generate_nonce(unsigned char *nonce,int bytes); diff --git a/tests/directory_service b/tests/directory_service index ba35b30f..824c5bcc 100755 --- a/tests/directory_service +++ b/tests/directory_service @@ -145,13 +145,13 @@ test_routing() { wait_until is_published $SIDC set_instance +B executeOk_servald route print - assertStdoutGrep --matches=1 "^$SIDA:UNICAST :" + assertStdoutGrep --matches=1 "^$SIDA:UNICAST:" executeOk_servald dna lookup "$DIDC" assertStdoutLineCount '==' 1 assertStdoutGrep --matches=1 "^sid://$SIDC/local/$DIDC:$DIDC:$NAMEC\$" set_instance +C executeOk_servald route print - assertStdoutGrep --matches=1 "^$SIDA:UNICAST :" + assertStdoutGrep --matches=1 "^$SIDA:UNICAST:" executeOk_servald dna lookup "$DIDB" assertStdoutLineCount '==' 1 assertStdoutGrep --matches=1 "^sid://$SIDB/local/$DIDB:$DIDB:$NAMEB\$" diff --git a/tests/routing b/tests/routing index e792c447..c747da6c 100755 --- a/tests/routing +++ b/tests/routing @@ -18,13 +18,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# Routing conditions that are known to not be working or covered by tests; -# - No support for multi-hop paths involving any unicast links -# - unicast only links need to be shared in the routing table -# - routing table needs to be shared to unicast only peers -# - unicast IP information should be shared among unicast peers -# source "${0%/*}/../testframework.sh" source "${0%/*}/../testdefs.sh" @@ -241,29 +234,33 @@ doc_scan="Network scan with isolated clients" setup_scan() { setup_servald assert_no_servald_processes - foreach_instance +A +B create_single_identity - foreach_instance +A +B add_interface 1 - set_instance +B - executeOk_servald config \ - set interfaces.1.dummy_address 127.0.1.11 - foreach_instance +A +B \ + foreach_instance +A +B +C create_single_identity + foreach_instance +A +B +C add_interface 1 + foreach_instance +A +B +C \ executeOk_servald config \ set interfaces.1.drop_broadcasts 100 - foreach_instance +A +B start_routing_instance + foreach_instance +A +B +C start_routing_instance } test_scan() { set_instance +A executeOk_servald scan wait_until scan_completed - wait_until has_seen_instances +B - executeOk_servald mdp ping --timeout=3 $SIDB 1 - tfw_cat --stdout --stderr + wait_until --timeout=10 has_seen_instances +B +C executeOk_servald route print assertStdoutGrep --matches=1 "^$SIDB:UNICAST:" + assertStdoutGrep --matches=1 "^$SIDC:UNICAST:" + executeOk_servald mdp ping --timeout=3 $SIDB 1 + tfw_cat --stdout --stderr + set_instance +B + executeOk_servald route print + assertStdoutGrep --matches=1 "^$SIDA:UNICAST:" + assertStdoutGrep --matches=1 "^$SIDC:INDIRECT:" + executeOk_servald mdp ping --timeout=3 $SIDC 1 + tfw_cat --stdout --stderr } scan_completed() { - grep "Scan completed" $LOG||return1 + grep "Scan completed" $instance_servald_log || return 1 return 0 } @@ -275,7 +272,6 @@ setup_single_filter() { foreach_instance +A +B add_interface 1 set_instance +B executeOk_servald config \ - set interfaces.1.dummy_address 127.0.1.11 \ set interfaces.1.drop_broadcasts 100 foreach_instance +A +B start_routing_instance } @@ -360,6 +356,31 @@ test_multihop_linear() { assertStdoutGrep --matches=1 "^6:$SIDA\$" } +doc_unicast_route="Route across unicast links" +setup_unicast_route() { + setup_servald + assert_no_servald_processes + foreach_instance +A +B +C +D create_single_identity + foreach_instance +A +B add_interface 1 + foreach_instance +B +C add_interface 2 + foreach_instance +C +D add_interface 3 + set_instance +A + executeOk_servald config \ + set interfaces.1.drop_broadcasts 100 + set_instance +C + executeOk_servald config \ + set interfaces.2.drop_broadcasts 100 \ + set interfaces.3.drop_broadcasts 100 + foreach_instance +A +B +C +D start_routing_instance +} +test_unicast_route() { + wait_until --timeout=20 path_exists +A +B +C +D + wait_until --timeout=5 path_exists +D +C +B +A + set_instance +A + executeOk_servald mdp ping --timeout=3 $SIDD 1 + tfw_cat --stdout --stderr +} + setup_offline() { setup_servald assert_no_servald_processes @@ -640,10 +661,10 @@ test_crowded_mess() { foreach_instance +A +H \ wait_until has_seen_instances +A +H set_instance +A - executeOk_servald mdp ping --timeout=3 $SIDH 1 - tfw_cat --stdout --stderr executeOk_servald route print assertStdoutGrep --matches=1 "^$SIDH:INDIRECT:" + executeOk_servald mdp ping --timeout=3 $SIDH 1 + tfw_cat --stdout --stderr } runTests "$@"