Commit e816161c authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

Don't attempt atomic route changes under Linux.

The netlink API doesn't natively support atomic route changes.  We used
to attempt to install the new route before removing the old route, but that
would fail in mysterious ways on non-multipath kernels, leading to "stuck"
routes.  Avoid the pain, just take the native approach.
parent d74185a4
...@@ -864,43 +864,27 @@ kernel_route(int operation, const unsigned char *dest, unsigned short plen, ...@@ -864,43 +864,27 @@ kernel_route(int operation, const unsigned char *dest, unsigned short plen,
ipv4 = v4mapped(gate); ipv4 = v4mapped(gate);
if(operation == ROUTE_MODIFY) { if(operation == ROUTE_MODIFY) {
int added;
if(newmetric == metric && memcmp(newgate, gate, 16) == 0 && if(newmetric == metric && memcmp(newgate, gate, 16) == 0 &&
newifindex == ifindex) newifindex == ifindex)
return 0; return 0;
/* It is better to add the new route before removing the old /* It would be better to add the new route before removing the
one, to avoid losing packets. However, if the old and new old one, to avoid losing packets. However, this causes
priorities are equal, this only works if the kernel supports problems with non-multipath kernels, which sometimes
ECMP. So we first try the "right" order, and fall back on silently fail the request, causing "stuck" routes. Let's
the "wrong" order if it fails with EEXIST. */ stick with the naive approach, and hope that the window is
rc = kernel_route(ROUTE_ADD, dest, plen, small enough to be negligible. */
newgate, newifindex, newmetric,
NULL, 0, 0);
if(rc < 0) {
if(errno != EEXIST)
return rc;
added = 0;
} else {
added = 1;
}
kernel_route(ROUTE_FLUSH, dest, plen, kernel_route(ROUTE_FLUSH, dest, plen,
gate, ifindex, metric, gate, ifindex, metric,
NULL, 0, 0); NULL, 0, 0);
if(!added) {
rc = kernel_route(ROUTE_ADD, dest, plen, rc = kernel_route(ROUTE_ADD, dest, plen,
newgate, newifindex, newmetric, newgate, newifindex, newmetric,
NULL, 0, 0); NULL, 0, 0);
if(rc < 0) { if(rc < 0) {
if(errno == EEXIST) if(errno == EEXIST)
rc = 1; rc = 1;
/* In principle, we should try to re-install the flushed /* Should we try to re-install the flushed route on failure?
route on failure to preserve. However, this should Error handling is hard. */
hopefully not matter much in practice. */
} }
}
return rc; return rc;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment