Skip to content

Commit

Permalink
Recalculate and resend MTU probes if they are too large for the system.
Browse files Browse the repository at this point in the history
Currently, if a MTU probe is sent and gets rejected by the system
because it is too large (i.e. send() returns EMSGSIZE), the MTU
discovery algorithm is not aware of it and still behaves as if the probe
was actually sent.

This patch makes the MTU discovery algorithm recalculate and send a new
probe when this happens, so that the probe "slot" does not go to waste.
  • Loading branch information
dechamps committed Jan 2, 2015
1 parent f89319f commit 06345f8
Showing 1 changed file with 35 additions and 27 deletions.
62 changes: 35 additions & 27 deletions src/net_packet.c
Original file line number Diff line number Diff line change
Expand Up @@ -991,33 +991,41 @@ static void try_mtu(node_t *n) {
if(n->mtuprobes == 0)
n->maxmtu = choose_initial_maxmtu(n);

/* Decreasing the number of probes per cycle might make the algorithm react faster to lost packets,
but it will typically increase convergence time in the no-loss case. */
const length_t probes_per_cycle = 8;

/* This magic value was determined using math simulations.
It will result in a 1329-byte first probe, followed (if there was a reply) by a 1407-byte probe.
Since 1407 is just below the range of tinc MTUs over typical networks,
this fine-tuning allows tinc to cover a lot of ground very quickly.
This fine-tuning is only valid for maxmtu = MTU; if maxmtu is smaller,
then it's better to use a multiplier of 1. Indeed, this leads to an interesting scenario
if choose_initial_maxmtu() returns the actual MTU value - it will get confirmed with one single probe. */
const float multiplier = (n->maxmtu == MTU) ? 0.97 : 1;

const float cycle_position = probes_per_cycle - (n->mtuprobes % probes_per_cycle) - 1;
const length_t minmtu = MAX(n->minmtu, 512);
const float interval = n->maxmtu - minmtu;

/* The core of the discovery algorithm is this exponential.
It produces very large probes early in the cycle, and then it very quickly decreases the probe size.
This reflects the fact that in the most difficult cases, we don't get any feedback for probes that
are too large, and therefore we need to concentrate on small offsets so that we can quickly converge
on the precise MTU as we are approaching it.
The last probe of the cycle is always 1 byte in size - this is to make sure we'll get at least one
reply per cycle so that we can make progress. */
const length_t offset = powf(interval, multiplier * cycle_position / (probes_per_cycle - 1));

send_udp_probe_packet(n, minmtu + offset);
for (;;) {
/* Decreasing the number of probes per cycle might make the algorithm react faster to lost packets,
but it will typically increase convergence time in the no-loss case. */
const length_t probes_per_cycle = 8;

/* This magic value was determined using math simulations.
It will result in a 1329-byte first probe, followed (if there was a reply) by a 1407-byte probe.
Since 1407 is just below the range of tinc MTUs over typical networks,
this fine-tuning allows tinc to cover a lot of ground very quickly.
This fine-tuning is only valid for maxmtu = MTU; if maxmtu is smaller,
then it's better to use a multiplier of 1. Indeed, this leads to an interesting scenario
if choose_initial_maxmtu() returns the actual MTU value - it will get confirmed with one single probe. */
const float multiplier = (n->maxmtu == MTU) ? 0.97 : 1;

const float cycle_position = probes_per_cycle - (n->mtuprobes % probes_per_cycle) - 1;
const length_t minmtu = MAX(n->minmtu, 512);
const float interval = n->maxmtu - minmtu;

/* The core of the discovery algorithm is this exponential.
It produces very large probes early in the cycle, and then it very quickly decreases the probe size.
This reflects the fact that in the most difficult cases, we don't get any feedback for probes that
are too large, and therefore we need to concentrate on small offsets so that we can quickly converge
on the precise MTU as we are approaching it.
The last probe of the cycle is always 1 byte in size - this is to make sure we'll get at least one
reply per cycle so that we can make progress. */
const length_t offset = powf(interval, multiplier * cycle_position / (probes_per_cycle - 1));

length_t maxmtu = n->maxmtu;
send_udp_probe_packet(n, minmtu + offset);
/* If maxmtu changed, it means the probe was rejected by the system because it was too large.
In that case, we recalculate with the new maxmtu and try again. */
if(n->mtuprobes < 0 || maxmtu == n->maxmtu)
break;
}

if(n->mtuprobes >= 0)
n->mtuprobes++;
}
Expand Down

0 comments on commit 06345f8

Please sign in to comment.