From b35c1aca4340e22c52920378328b1eb840ab8aab Mon Sep 17 00:00:00 2001 From: Jeremy Lakeman Date: Tue, 29 Jan 2013 11:57:13 +1100 Subject: [PATCH] Specify that unicast traffic should use unicast UDP packets --- conf_schema.h | 6 +++-- overlay_address.h | 1 + overlay_interface.c | 5 +++- overlay_link.c | 16 ++++++------ overlay_packetformats.c | 12 +++++---- overlay_queue.c | 15 +++++++++++ overlay_route.c | 4 +++ serval.h | 9 ++++--- tests/routing | 56 +++++++++++++++++++++++++++++++++++++---- 9 files changed, 100 insertions(+), 24 deletions(-) diff --git a/conf_schema.h b/conf_schema.h index 79e57aec..5958655c 100644 --- a/conf_schema.h +++ b/conf_schema.h @@ -334,13 +334,15 @@ ATOM(struct pattern_list, match, PATTERN_LIST_EMPTY, cf_opt_pattern_list, STRING(256, dummy, "", cf_opt_str_nonempty,, "Path of dummy file, absolute or relative to server.dummy_interface_dir") ATOM(struct in_addr, dummy_address, (struct in_addr){htonl(0x7F000001)}, cf_opt_in_addr,, "Dummy interface address") ATOM(struct in_addr, dummy_netmask, (struct in_addr){htonl(0xFFFFFF00)}, cf_opt_in_addr,, "Dummy interface netmask") -ATOM(int, dummy_filter_broadcasts, 0, cf_opt_int_boolean,, "If true, drop all incoming broadcast packets") +ATOM(char, dummy_filter_broadcasts, 0, cf_opt_char_boolean,, "If true, drop all incoming broadcast packets") +ATOM(char, dummy_filter_unicasts, 0, cf_opt_char_boolean,, "If true, drop all incoming unicast packets") ATOM(short, type, OVERLAY_INTERFACE_WIFI, cf_opt_interface_type,, "Type of network interface") ATOM(uint16_t, port, RHIZOME_HTTP_PORT, cf_opt_uint16_nonzero,, "Port number for network interface") ATOM(int, packet_interval, -1, cf_opt_int,, "Minimum interval between packets in microseconds") ATOM(int, mdp_tick_ms, -1, cf_opt_int32_nonneg,, "Override MDP tick interval for this interface") ATOM(char, send_broadcasts, 1, cf_opt_char_boolean,, "If false, don't send any broadcast packets") -ATOM(int, default_route, 0, cf_opt_int_boolean,, "If true, use this interface as a default route") +ATOM(char, default_route, 0, cf_opt_char_boolean,, "If true, use this interface as a default route") +ATOM(char, prefer_unicast, 0, cf_opt_char_boolean,, "If true, send unicast data as unicast IP packets if available") END_STRUCT ARRAY(interface_list, SORTED NO_DUPLICATES) diff --git a/overlay_address.h b/overlay_address.h index 7fbf5616..d00670d6 100644 --- a/overlay_address.h +++ b/overlay_address.h @@ -72,6 +72,7 @@ struct subscriber{ struct sockaddr_in address; 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; diff --git a/overlay_interface.c b/overlay_interface.c index fb4b63c4..cebe417f 100644 --- a/overlay_interface.c +++ b/overlay_interface.c @@ -397,6 +397,8 @@ overlay_interface_init(const char *name, struct in_addr src_addr, struct in_addr return WHYF("Invalid tick interval %d specified for interface %s", interface->tick_ms, name); limit_init(&interface->transfer_limit, packet_interval); + + interface->prefer_unicast = ifconfig->prefer_unicast; if (ifconfig->dummy[0]) { interface->fileP = 1; @@ -421,6 +423,7 @@ overlay_interface_init(const char *name, struct in_addr src_addr, struct in_addr interface->broadcast_address.sin_port = htons(PORT_DNA); interface->broadcast_address.sin_addr.s_addr = interface->address.sin_addr.s_addr | ~interface->netmask.s_addr; interface->drop_broadcasts = ifconfig->dummy_filter_broadcasts; + interface->drop_unicasts = ifconfig->dummy_filter_unicasts; /* Seek to end of file as initial reading point */ interface->recv_offset = lseek(interface->alarm.poll.fd,0,SEEK_END); @@ -586,7 +589,7 @@ void overlay_dummy_poll(struct sched_ent *alarm) if (config.debug.packetrx) DEBUG_packet_visualise("Read from dummy interface", packet.payload, packet.payload_length); - if (memcmp(&packet.dst_addr, &interface->address, sizeof(packet.dst_addr))==0 || + if (((!interface->drop_unicasts) && memcmp(&packet.dst_addr, &interface->address, sizeof(packet.dst_addr))==0) || ((!interface->drop_broadcasts) && memcmp(&packet.dst_addr, &interface->broadcast_address, sizeof(packet.dst_addr))==0)){ diff --git a/overlay_link.c b/overlay_link.c index 43e12612..fd399ccb 100644 --- a/overlay_link.c +++ b/overlay_link.c @@ -225,18 +225,20 @@ overlay_mdp_service_probe(overlay_mdp_frame *mdp) } struct subscriber *peer = find_subscriber(mdp->out.src.sid, SID_SIZE, 0); + if (peer->reachable == REACHABLE_SELF) + RETURN(0); + struct probe_contents probe; bcopy(&mdp->out.payload, &probe, sizeof(struct probe_contents)); if (probe.addr.sin_family!=AF_INET) RETURN(WHY("Unsupported address family")); - if (peer->reachable == REACHABLE_NONE || peer->reachable == REACHABLE_INDIRECT || (peer->reachable & REACHABLE_ASSUMED)){ - peer->interface = &overlay_interfaces[probe.interface]; - peer->address.sin_family = AF_INET; - peer->address.sin_addr = probe.addr.sin_addr; - peer->address.sin_port = probe.addr.sin_port; - set_reachable(peer, REACHABLE_UNICAST); - } + peer->last_probe_response = gettime_ms(); + peer->interface = &overlay_interfaces[probe.interface]; + peer->address.sin_family = AF_INET; + peer->address.sin_addr = probe.addr.sin_addr; + peer->address.sin_port = probe.addr.sin_port; + set_reachable(peer, REACHABLE_UNICAST | (peer->reachable & REACHABLE_DIRECT)); RETURN(0); } diff --git a/overlay_packetformats.c b/overlay_packetformats.c index 6f407939..426fbe2d 100644 --- a/overlay_packetformats.c +++ b/overlay_packetformats.c @@ -235,23 +235,24 @@ int packetOkOverlay(struct overlay_interface *interface,unsigned char *packet, s // TODO probe unicast links when we detect an address change. - // always update the IP address we heard them from, even if we don't need to use it right now - context.sender->address = f.recvaddr; - // if this is a dummy announcement for a node that isn't in our routing table if (context.sender->reachable == REACHABLE_NONE) { context.sender->interface = interface; + context.sender->address = f.recvaddr; + context.sender->last_probe = 0; // assume for the moment, that we can reply with the same packet type if (packet_flags&PACKET_UNICAST){ - /* Note the probe payload must be queued before any SID/SAS request so we can force the packet to have a full sid */ - overlay_send_probe(context.sender, f.recvaddr, interface, OQ_MESH_MANAGEMENT); set_reachable(context.sender, REACHABLE_UNICAST|REACHABLE_ASSUMED); }else{ set_reachable(context.sender, REACHABLE_BROADCAST|REACHABLE_ASSUMED); } } + /* Note the probe payload must be queued before any SID/SAS request so we can force the packet to have a full sid */ + if (context.sender->last_probe==0 || now - context.sender->last_probe > 5000) + overlay_send_probe(context.sender, f.recvaddr, 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, @@ -259,6 +260,7 @@ int packetOkOverlay(struct overlay_interface *interface,unsigned char *packet, s context.sender->last_acked = now; } + } if (packet_flags & PACKET_UNICAST) diff --git a/overlay_queue.c b/overlay_queue.c index 558d777c..5ee6d2aa 100644 --- a/overlay_queue.c +++ b/overlay_queue.c @@ -165,6 +165,10 @@ int overlay_payload_enqueue(struct overlay_frame *p) if (p->queue>=OQ_MAX) return WHY("Invalid queue specified"); + /* queue a unicast probe if we haven't for a while. */ + if (p->destination && (p->destination->last_probe==0 || gettime_ms() - p->destination->last_probe > 5000)) + overlay_send_probe(p->destination, p->destination->address, p->destination->interface, OQ_MESH_MANAGEMENT); + overlay_txqueue *queue = &overlay_tx[p->queue]; if (config.debug.packettx) @@ -346,6 +350,17 @@ overlay_stuff_packet(struct outgoing_packet *packet, overlay_txqueue *queue, tim frame->interface = frame->next_hop->interface; + // if both broadcast and unicast are available, pick on based on interface preference + if ((r&(REACHABLE_UNICAST|REACHABLE_BROADCAST))==(REACHABLE_UNICAST|REACHABLE_BROADCAST)){ + if (frame->interface->prefer_unicast){ + r=REACHABLE_UNICAST; + // used by tests + if (config.debug.overlayframes) + DEBUGF("Choosing to send via unicast for %s", alloca_tohex_sid(frame->destination->sid)); + }else + r=REACHABLE_BROADCAST; + } + if(r&REACHABLE_UNICAST){ frame->recvaddr = frame->next_hop->address; frame->unicast = 1; diff --git a/overlay_route.c b/overlay_route.c index b33aa26c..f52a3f2e 100644 --- a/overlay_route.c +++ b/overlay_route.c @@ -411,6 +411,9 @@ int overlay_route_recalc_node_metrics(overlay_node *n, time_ms_t now) 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; } } } @@ -455,6 +458,7 @@ int overlay_route_recalc_node_metrics(overlay_node *n, time_ms_t now) n->subscriber->next_hop = next_hop; break; case REACHABLE_BROADCAST: + case REACHABLE_BROADCAST|REACHABLE_UNICAST: n->subscriber->interface = interface; break; } diff --git a/serval.h b/serval.h index 6aa46d45..09778f87 100644 --- a/serval.h +++ b/serval.h @@ -354,7 +354,8 @@ typedef struct overlay_interface { char name[256]; int recv_offset; int fileP; // dummyP - int drop_broadcasts; + char drop_broadcasts; + char drop_unicasts; int port; int type; /* Number of milli-seconds per tick for this interface, which is basically related to the @@ -371,11 +372,11 @@ typedef struct overlay_interface { unsigned tick_ms; /* milliseconds per tick */ struct subscriber *next_advert; - int send_broadcasts; + char send_broadcasts; + char prefer_unicast; + /* The time of the last tick on this interface in milli seconds */ time_ms_t last_tick_ms; - /* How many times have we abbreviated our address since we last announced it in full? */ - int ticks_since_sent_full_address; /* sequence number of last packet sent on this interface. Used to allow NACKs that can request retransmission of recent packets. diff --git a/tests/routing b/tests/routing index 657540d2..0f78eb36 100755 --- a/tests/routing +++ b/tests/routing @@ -75,7 +75,7 @@ test_single_link() { executeOk_servald mdp ping $SIDB 1 tfw_cat --stdout --stderr executeOk_servald route print - assertStdoutGrep --matches=1 "^$SIDB:BROADCAST :" + assertStdoutGrep --matches=1 "^$SIDB:BROADCAST UNICAST :" } setup_multiple_nodes() { @@ -98,9 +98,9 @@ test_multiple_nodes() { executeOk_servald mdp ping $SIDD 1 tfw_cat --stdout --stderr executeOk_servald route print - assertStdoutGrep --matches=1 "^$SIDB:BROADCAST :" - assertStdoutGrep --matches=1 "^$SIDC:BROADCAST :" - assertStdoutGrep --matches=1 "^$SIDD:BROADCAST :" + assertStdoutGrep --matches=1 "^$SIDB:BROADCAST " + assertStdoutGrep --matches=1 "^$SIDC:BROADCAST " + assertStdoutGrep --matches=1 "^$SIDD:BROADCAST " } setup_scan() { @@ -134,6 +134,52 @@ test_scan() { assertStdoutGrep --matches=1 "^$SIDB:UNICAST :" } +setup_broadcast_only() { + setup_servald + assert_no_servald_processes + foreach_instance +A +B create_single_identity + foreach_instance +A +B add_interface 1 + foreach_instance +A +B \ + executeOk_servald config \ + set interfaces.1.dummy_filter_unicasts 1 + foreach_instance +A +B start_routing_instance +} + +doc_broadcast_only="Broadcast packets only" +test_broadcast_only() { + foreach_instance +A +B \ + wait_until has_seen_instances +A +B + set_instance +A + executeOk_servald mdp ping $SIDB 1 + tfw_cat --stdout --stderr + executeOk_servald route print + assertStdoutGrep --matches=1 "^$SIDB:BROADCAST :" +} + +setup_prefer_unicast() { + setup_servald + assert_no_servald_processes + foreach_instance +A +B create_single_identity + foreach_instance +A +B add_interface 1 + foreach_instance +A +B \ + executeOk_servald config \ + set interfaces.1.prefer_unicast 1 \ + set debug.overlayframes 1 + foreach_instance +A +B start_routing_instance +} + +doc_prefer_unicast="Prefer unicast packets" +test_prefer_unicast() { + foreach_instance +A +B \ + wait_until has_seen_instances +A +B + set_instance +A + executeOk_servald mdp ping $SIDB 1 + tfw_cat --stdout --stderr + executeOk_servald route print + assertStdoutGrep --matches=1 "^$SIDB:BROADCAST UNICAST :" + assertGrep "$instance_servald_log" 'Choosing to send via unicast' +} + setup_multihop_linear() { setup_servald assert_no_servald_processes @@ -152,7 +198,7 @@ test_multihop_linear() { executeOk_servald mdp ping $SIDD 1 tfw_cat --stdout --stderr executeOk_servald route print - assertStdoutGrep --matches=1 "^$SIDB:BROADCAST :" + assertStdoutGrep --matches=1 "^$SIDB:BROADCAST UNICAST :" assertStdoutGrep --matches=1 "^$SIDC:INDIRECT :" assertStdoutGrep --matches=1 "^$SIDD:INDIRECT :" }