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)
--show-gateway
--show-gateway IPv6-target
- If an IPv6 target address is passed as argument, the IPv6 route for this
- host is reported.
+ For IPv6 this queries the route towards ::/128, or the specified IPv6
+ target address if passed as argument.
+ For IPv4 on Linux, Windows, MacOS and BSD it looks for a 0.0.0.0/0 route.
+ If there are more specific routes, the result will not always be matching
+ the route of the IPv4 packets to the VPN gateway.
Advanced Expert Options
* continue;
* }
*/
+
+ if (h->nlmsg_type == NLMSG_DONE)
+ {
+ ret = 0;
+ goto out;
+ }
+
if (h->nlmsg_type == NLMSG_ERROR)
{
err = (struct nlmsgerr *)NLMSG_DATA(h);
ret = 0;
if (cb)
{
- ret = cb(h, arg_cb);
+ int r = cb(h, arg_cb);
+ if (r <= 0)
+ {
+ ret = r;
+ }
}
}
else
if (cb)
{
- ret = cb(h, arg_cb);
- goto out;
+ int r = cb(h, arg_cb);
+ if (r <= 0)
+ {
+ ret = r;
+ goto out;
+ }
}
else
{
int addr_size;
inet_address_t gw;
char iface[IFNAMSIZ];
+ bool default_only;
} route_res_t;
static int
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)
{
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: