aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVladislav Grishenko2020-09-08 17:36:25 +0500
committerGert Doering2020-09-10 11:22:05 +0200
commit7a9fefb6e2ea28967a09956eeb041b687ca0e335 (patch)
tree7df03aa503b2ac324df6467bae70dd6d2842797d /src
parent43e70c78c34b90b15b6bfdac9c1911853defca10 (diff)
downloadopenvpn-7a9fefb6e2ea28967a09956eeb041b687ca0e335.zip
openvpn-7a9fefb6e2ea28967a09956eeb041b687ca0e335.tar.gz
Fix best gateway selection over netlink
Netlink route request with NLM_F_DUMP flag set means to return all entries matching criteria passed in message content - matching supplied family & dst address in our case. So, gateway from the first ipv4 route was always used. On kernels earlier than 2.6.38 default routes are the last ones, so arbitrary host/net route w/o gateway is likely be returned as first, causing gateway to be invalid or empty. After refactoring in 2.6.38 kernel default routes are on top, so the problem with older kernels was hidden. Fix this behavior by selecting first 0.0.0.0/0 if dst was not set or empty. For IPv6, no behavior is changed - request ::/128 route, so just clarify the sizes via netlink route api. Tested on 5.4.0, 4.1.51, 2.6.36 and 2.6.22 kernels. Signed-off-by: Vladislav Grishenko <themiron@yandex-team.ru> Acked-by: Antonio Quartulli <a@unstable.cc> Message-Id: <20200908123625.23179-1-themiron@yandex-team.ru> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg20900.html Signed-off-by: Gert Doering <gert@greenie.muc.de> (cherry picked from commit 505d5ad8fadcdc56bae07f4b95c05acd93a47c24)
Diffstat (limited to 'src')
-rw-r--r--src/openvpn/networking_sitnl.c44
1 files changed, 40 insertions, 4 deletions
diff --git a/src/openvpn/networking_sitnl.c b/src/openvpn/networking_sitnl.c
index 713a213..2bc70a5 100644
--- a/src/openvpn/networking_sitnl.c
+++ b/src/openvpn/networking_sitnl.c
@@ -345,6 +345,13 @@ sitnl_send(struct nlmsghdr *payload, pid_t peer, unsigned int groups,
* continue;
* }
*/
+
+ if (h->nlmsg_type == NLMSG_DONE)
+ {
+ ret = 0;
+ goto out;
+ }
+
if (h->nlmsg_type == NLMSG_ERROR)
{
err = (struct nlmsgerr *)NLMSG_DATA(h);
@@ -360,7 +367,11 @@ sitnl_send(struct nlmsghdr *payload, pid_t peer, unsigned int groups,
ret = 0;
if (cb)
{
- ret = cb(h, arg_cb);
+ int r = cb(h, arg_cb);
+ if (r <= 0)
+ {
+ ret = r;
+ }
}
}
else
@@ -375,8 +386,12 @@ sitnl_send(struct nlmsghdr *payload, pid_t peer, unsigned int groups,
if (cb)
{
- ret = cb(h, arg_cb);
- goto out;
+ int r = cb(h, arg_cb);
+ if (r <= 0)
+ {
+ ret = r;
+ goto out;
+ }
}
else
{
@@ -410,6 +425,7 @@ typedef struct {
int addr_size;
inet_address_t gw;
char iface[IFNAMSIZ];
+ bool default_only;
} route_res_t;
static int
@@ -421,6 +437,12 @@ sitnl_route_save(struct nlmsghdr *n, void *arg)
int len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*r));
unsigned int ifindex = 0;
+ /* filter-out non-zero dst prefixes */
+ if (res->default_only && r->rtm_dst_len != 0)
+ {
+ return 1;
+ }
+
while (RTA_OK(rta, len))
{
switch (rta->rta_type)
@@ -477,11 +499,25 @@ sitnl_route_best_gw(sa_family_t af_family, const inet_address_t *dst,
{
case AF_INET:
res.addr_size = sizeof(in_addr_t);
- req.n.nlmsg_flags |= NLM_F_DUMP;
+ /*
+ * kernel can't return 0.0.0.0/8 host route, dump all
+ * the routes and filter for 0.0.0.0/0 in cb()
+ */
+ if (!dst || !dst->ipv4)
+ {
+ req.n.nlmsg_flags |= NLM_F_DUMP;
+ res.default_only = true;
+ }
+ else
+ {
+ req.r.rtm_dst_len = 32;
+ }
break;
case AF_INET6:
res.addr_size = sizeof(struct in6_addr);
+ /* kernel can return ::/128 host route */
+ req.r.rtm_dst_len = 128;
break;
default: