Merge pull request #17271 from yuwata/network-route-improve-multipath-route-support
network: improve multipath route support
This commit is contained in:
commit
45a536980c
|
@ -1282,9 +1282,10 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
|
|||
<varlistentry>
|
||||
<term><varname>Gateway=</varname></term>
|
||||
<listitem>
|
||||
<para>Takes the gateway address or special value <literal>_dhcp</literal>. If
|
||||
<literal>_dhcp</literal>, then the gateway address provided by DHCP (or in the IPv6 case,
|
||||
provided by IPv6 RA) is used.</para>
|
||||
<para>Takes the gateway address or the special values <literal>_dhcp4</literal> and
|
||||
<literal>_dhcp6</literal>. If <literal>_dhcp4</literal> or <literal>_dhcp6</literal> is
|
||||
set, then the gateway address provided by DHCP (or in the IPv6 case, provided by IPv6 RA)
|
||||
is used.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
|
||||
/*
|
||||
* Linux INET6 implementation
|
||||
*
|
||||
* Authors:
|
||||
* Pedro Roque <roque@di.fc.ul.pt>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_LINUX_IPV6_ROUTE_H
|
||||
#define _UAPI_LINUX_IPV6_ROUTE_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/in6.h> /* For struct in6_addr. */
|
||||
|
||||
#define RTF_DEFAULT 0x00010000 /* default - learned via ND */
|
||||
#define RTF_ALLONLINK 0x00020000 /* (deprecated and will be removed)
|
||||
fallback, no routers on link */
|
||||
#define RTF_ADDRCONF 0x00040000 /* addrconf route - RA */
|
||||
#define RTF_PREFIX_RT 0x00080000 /* A prefix only route - RA */
|
||||
#define RTF_ANYCAST 0x00100000 /* Anycast */
|
||||
|
||||
#define RTF_NONEXTHOP 0x00200000 /* route with no nexthop */
|
||||
#define RTF_EXPIRES 0x00400000
|
||||
|
||||
#define RTF_ROUTEINFO 0x00800000 /* route information - RA */
|
||||
|
||||
#define RTF_CACHE 0x01000000 /* read-only: can not be set by user */
|
||||
#define RTF_FLOW 0x02000000 /* flow significant route */
|
||||
#define RTF_POLICY 0x04000000 /* policy route */
|
||||
|
||||
#define RTF_PREF(pref) ((pref) << 27)
|
||||
#define RTF_PREF_MASK 0x18000000
|
||||
|
||||
#define RTF_PCPU 0x40000000 /* read-only: can not be set by user */
|
||||
#define RTF_LOCAL 0x80000000
|
||||
|
||||
|
||||
struct in6_rtmsg {
|
||||
struct in6_addr rtmsg_dst;
|
||||
struct in6_addr rtmsg_src;
|
||||
struct in6_addr rtmsg_gateway;
|
||||
__u32 rtmsg_type;
|
||||
__u16 rtmsg_dst_len;
|
||||
__u16 rtmsg_src_len;
|
||||
__u32 rtmsg_metric;
|
||||
unsigned long rtmsg_info;
|
||||
__u32 rtmsg_flags;
|
||||
int rtmsg_ifindex;
|
||||
};
|
||||
|
||||
#define RTMSG_NEWDEVICE 0x11
|
||||
#define RTMSG_DELDEVICE 0x12
|
||||
#define RTMSG_NEWROUTE 0x21
|
||||
#define RTMSG_DELROUTE 0x22
|
||||
|
||||
#define IP6_RT_PRIO_USER 1024
|
||||
#define IP6_RT_PRIO_ADDRCONF 256
|
||||
|
||||
#endif /* _UAPI_LINUX_IPV6_ROUTE_H */
|
|
@ -91,9 +91,11 @@ basic_sources = files('''
|
|||
limits-util.h
|
||||
linux/btrfs.h
|
||||
linux/btrfs_tree.h
|
||||
linux/can/netlink.h
|
||||
linux/can/vxcan.h
|
||||
linux/fib_rules.h
|
||||
linux/fou.h
|
||||
linux/hdlc/ioctl.h
|
||||
linux/if.h
|
||||
linux/if_addr.h
|
||||
linux/if_arp.h
|
||||
|
@ -106,6 +108,7 @@ basic_sources = files('''
|
|||
linux/if_tunnel.h
|
||||
linux/in.h
|
||||
linux/in6.h
|
||||
linux/ipv6_route.h
|
||||
linux/l2tp.h
|
||||
linux/libc-compat.h
|
||||
linux/netdevice.h
|
||||
|
|
|
@ -694,6 +694,30 @@ int sd_netlink_message_read(sd_netlink_message *m, unsigned short type, size_t s
|
|||
return r;
|
||||
}
|
||||
|
||||
int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data) {
|
||||
void *attr_data, *data;
|
||||
int r;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
|
||||
r = netlink_message_read_internal(m, type, &attr_data, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_data) {
|
||||
data = memdup(attr_data, r);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret_data = data;
|
||||
}
|
||||
|
||||
if (ret_size)
|
||||
*ret_size = r;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsigned short type, char **data) {
|
||||
void *attr_data;
|
||||
char *str;
|
||||
|
|
|
@ -716,7 +716,7 @@ static const NLType rtnl_route_types[] = {
|
|||
[RTA_TABLE] = { .type = NETLINK_TYPE_U32 },
|
||||
[RTA_MARK] = { .type = NETLINK_TYPE_U32 },
|
||||
[RTA_MFC_STATS] = { .type = NETLINK_TYPE_U64 },
|
||||
[RTA_VIA] = { .type = NETLINK_TYPE_U32 },
|
||||
[RTA_VIA] = { /* See struct rtvia */ },
|
||||
[RTA_NEWDST] = { .type = NETLINK_TYPE_U32 },
|
||||
[RTA_PREF] = { .type = NETLINK_TYPE_U8 },
|
||||
[RTA_EXPIRES] = { .type = NETLINK_TYPE_U32 },
|
||||
|
|
|
@ -368,3 +368,80 @@ int rtattr_append_attribute(struct rtattr **rta, unsigned short type, const void
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtattr_read_nexthop(const struct rtnexthop *rtnh, size_t size, int family, OrderedSet **ret) {
|
||||
_cleanup_ordered_set_free_free_ OrderedSet *set = NULL;
|
||||
int r;
|
||||
|
||||
assert(rtnh);
|
||||
assert(IN_SET(family, AF_INET, AF_INET6));
|
||||
|
||||
if (size < sizeof(struct rtnexthop))
|
||||
return -EBADMSG;
|
||||
|
||||
for (; size >= sizeof(struct rtnexthop); ) {
|
||||
_cleanup_free_ MultipathRoute *m = NULL;
|
||||
|
||||
if (NLMSG_ALIGN(rtnh->rtnh_len) > size)
|
||||
return -EBADMSG;
|
||||
|
||||
if (rtnh->rtnh_len < sizeof(struct rtnexthop))
|
||||
return -EBADMSG;
|
||||
|
||||
m = new(MultipathRoute, 1);
|
||||
if (!m)
|
||||
return -ENOMEM;
|
||||
|
||||
*m = (MultipathRoute) {
|
||||
.ifindex = rtnh->rtnh_ifindex,
|
||||
.weight = rtnh->rtnh_hops == 0 ? 0 : rtnh->rtnh_hops + 1,
|
||||
};
|
||||
|
||||
if (rtnh->rtnh_len > sizeof(struct rtnexthop)) {
|
||||
size_t len = rtnh->rtnh_len - sizeof(struct rtnexthop);
|
||||
|
||||
for (struct rtattr *attr = RTNH_DATA(rtnh); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
|
||||
if (attr->rta_type == RTA_GATEWAY) {
|
||||
if (attr->rta_len != RTA_LENGTH(FAMILY_ADDRESS_SIZE(family)))
|
||||
return -EBADMSG;
|
||||
|
||||
m->gateway.family = family;
|
||||
memcpy(&m->gateway.address, RTA_DATA(attr), FAMILY_ADDRESS_SIZE(family));
|
||||
break;
|
||||
} else if (attr->rta_type == RTA_VIA) {
|
||||
uint16_t gw_family;
|
||||
|
||||
if (family != AF_INET)
|
||||
return -EINVAL;
|
||||
|
||||
if (attr->rta_len < RTA_LENGTH(sizeof(uint16_t)))
|
||||
return -EBADMSG;
|
||||
|
||||
gw_family = *(uint16_t *) RTA_DATA(attr);
|
||||
|
||||
if (gw_family != AF_INET6)
|
||||
return -EBADMSG;
|
||||
|
||||
if (attr->rta_len != RTA_LENGTH(FAMILY_ADDRESS_SIZE(gw_family) + sizeof(gw_family)))
|
||||
return -EBADMSG;
|
||||
|
||||
memcpy(&m->gateway, RTA_DATA(attr), FAMILY_ADDRESS_SIZE(gw_family) + sizeof(gw_family));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r = ordered_set_ensure_put(&set, NULL, m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
TAKE_PTR(m);
|
||||
|
||||
size -= NLMSG_ALIGN(rtnh->rtnh_len);
|
||||
rtnh = RTNH_NEXT(rtnh);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(set);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -6,9 +6,22 @@
|
|||
#include "sd-netlink.h"
|
||||
|
||||
#include "in-addr-util.h"
|
||||
#include "ordered-set.h"
|
||||
#include "socket-util.h"
|
||||
#include "util.h"
|
||||
|
||||
/* See struct rtvia in rtnetlink.h */
|
||||
typedef struct RouteVia {
|
||||
uint16_t family;
|
||||
union in_addr_union address;
|
||||
} _packed_ RouteVia;
|
||||
|
||||
typedef struct MultipathRoute {
|
||||
RouteVia gateway;
|
||||
int ifindex;
|
||||
uint32_t weight;
|
||||
} MultipathRoute;
|
||||
|
||||
int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret);
|
||||
uint32_t rtnl_message_get_serial(sd_netlink_message *m);
|
||||
void rtnl_message_seal(sd_netlink_message *m);
|
||||
|
@ -94,3 +107,5 @@ int netlink_message_read_in_addr_union(sd_netlink_message *m, unsigned short typ
|
|||
|
||||
void rtattr_append_attribute_internal(struct rtattr *rta, unsigned short type, const void *data, size_t data_length);
|
||||
int rtattr_append_attribute(struct rtattr **rta, unsigned short type, const void *data, size_t data_length);
|
||||
|
||||
int rtattr_read_nexthop(const struct rtnexthop *rtnh, size_t size, int family, OrderedSet **ret);
|
||||
|
|
|
@ -83,3 +83,10 @@ CONFIG_PARSER_PROTOTYPE(config_parse_lifetime);
|
|||
CONFIG_PARSER_PROTOTYPE(config_parse_address_flags);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_address_scope);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_duplicate_address_detection);
|
||||
|
||||
#define IPV4_ADDRESS_FMT_STR "%u.%u.%u.%u"
|
||||
#define IPV4_ADDRESS_FMT_VAL(address) \
|
||||
be32toh((address).s_addr) >> 24, \
|
||||
(be32toh((address).s_addr) >> 16) & 0xFFu, \
|
||||
(be32toh((address).s_addr) >> 8) & 0xFFu, \
|
||||
be32toh((address).s_addr) & 0xFFu
|
||||
|
|
|
@ -38,7 +38,7 @@ static int dhcp4_release_old_lease(Link *link) {
|
|||
link_dirty(link);
|
||||
|
||||
SET_FOREACH(route, link->dhcp_routes_old) {
|
||||
k = route_remove(route, link, NULL);
|
||||
k = route_remove(route, NULL, link, NULL);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
}
|
||||
|
@ -312,6 +312,7 @@ static int link_set_dhcp_routes(Link *link) {
|
|||
|
||||
route->family = AF_INET;
|
||||
route->protocol = RTPROT_DHCP;
|
||||
route->gw_family = AF_INET;
|
||||
assert_se(sd_dhcp_route_get_gateway(static_routes[i], &route->gw.in) >= 0);
|
||||
assert_se(sd_dhcp_route_get_destination(static_routes[i], &route->dst.in) >= 0);
|
||||
assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen) >= 0);
|
||||
|
@ -374,6 +375,7 @@ static int link_set_dhcp_routes(Link *link) {
|
|||
return log_link_error_errno(link, r, "Could not allocate route: %m");
|
||||
|
||||
route->family = AF_INET;
|
||||
route->gw_family = AF_INET;
|
||||
route->gw.in = router[0];
|
||||
route->prefsrc.in = address;
|
||||
route->protocol = RTPROT_DHCP;
|
||||
|
@ -392,6 +394,7 @@ static int link_set_dhcp_routes(Link *link) {
|
|||
if (rt->family != AF_INET)
|
||||
continue;
|
||||
|
||||
rt->gw_family = AF_INET;
|
||||
rt->gw.in = router[0];
|
||||
|
||||
r = dhcp_route_configure(rt, link);
|
||||
|
@ -512,7 +515,7 @@ static int dhcp4_remove_all(Link *link) {
|
|||
assert(link);
|
||||
|
||||
SET_FOREACH(route, link->dhcp_routes) {
|
||||
k = route_remove(route, link, dhcp4_remove_route_handler);
|
||||
k = route_remove(route, NULL, link, dhcp4_remove_route_handler);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
else
|
||||
|
@ -788,20 +791,20 @@ static int dhcp4_update_address(Link *link, bool announce) {
|
|||
if (r > 0 && !in4_addr_is_null(&router[0]))
|
||||
log_struct(LOG_INFO,
|
||||
LOG_LINK_INTERFACE(link),
|
||||
LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
|
||||
ADDRESS_FMT_VAL(address),
|
||||
LOG_LINK_MESSAGE(link, "DHCPv4 address "IPV4_ADDRESS_FMT_STR"/%u via "IPV4_ADDRESS_FMT_STR,
|
||||
IPV4_ADDRESS_FMT_VAL(address),
|
||||
prefixlen,
|
||||
ADDRESS_FMT_VAL(router[0])),
|
||||
"ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
|
||||
IPV4_ADDRESS_FMT_VAL(router[0])),
|
||||
"ADDRESS="IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(address),
|
||||
"PREFIXLEN=%u", prefixlen,
|
||||
"GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(router[0]));
|
||||
"GATEWAY="IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(router[0]));
|
||||
else
|
||||
log_struct(LOG_INFO,
|
||||
LOG_LINK_INTERFACE(link),
|
||||
LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u",
|
||||
ADDRESS_FMT_VAL(address),
|
||||
LOG_LINK_MESSAGE(link, "DHCPv4 address "IPV4_ADDRESS_FMT_STR"/%u",
|
||||
IPV4_ADDRESS_FMT_VAL(address),
|
||||
prefixlen),
|
||||
"ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
|
||||
"ADDRESS="IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(address),
|
||||
"PREFIXLEN=%u", prefixlen);
|
||||
}
|
||||
|
||||
|
@ -952,8 +955,8 @@ static int dhcp_server_is_deny_listed(Link *link, sd_dhcp_client *client) {
|
|||
if (set_contains(link->network->dhcp_deny_listed_ip, UINT32_TO_PTR(addr.s_addr))) {
|
||||
log_struct(LOG_DEBUG,
|
||||
LOG_LINK_INTERFACE(link),
|
||||
LOG_LINK_MESSAGE(link, "DHCPv4 IP '%u.%u.%u.%u' found in deny-listed IP addresses, ignoring offer",
|
||||
ADDRESS_FMT_VAL(addr)));
|
||||
LOG_LINK_MESSAGE(link, "DHCPv4 server IP address "IPV4_ADDRESS_FMT_STR" found in deny-list, ignoring offer",
|
||||
IPV4_ADDRESS_FMT_VAL(addr)));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -980,8 +983,8 @@ static int dhcp_server_is_allow_listed(Link *link, sd_dhcp_client *client) {
|
|||
if (set_contains(link->network->dhcp_allow_listed_ip, UINT32_TO_PTR(addr.s_addr))) {
|
||||
log_struct(LOG_DEBUG,
|
||||
LOG_LINK_INTERFACE(link),
|
||||
LOG_LINK_MESSAGE(link, "DHCPv4 IP '%u.%u.%u.%u' found in allow-listed IP addresses, accepting offer",
|
||||
ADDRESS_FMT_VAL(addr)));
|
||||
LOG_LINK_MESSAGE(link, "DHCPv4 server IP address "IPV4_ADDRESS_FMT_STR" found in allow-list, accepting offer",
|
||||
IPV4_ADDRESS_FMT_VAL(addr)));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -164,7 +164,7 @@ static int dhcp6_pd_remove_old(Link *link, bool force) {
|
|||
link_dirty(link);
|
||||
|
||||
SET_FOREACH(route, link->dhcp6_pd_routes_old) {
|
||||
k = route_remove(route, link, NULL);
|
||||
k = route_remove(route, NULL, link, NULL);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
|
||||
|
@ -208,7 +208,7 @@ int dhcp6_pd_remove(Link *link) {
|
|||
link_dirty(link);
|
||||
|
||||
SET_FOREACH(route, link->dhcp6_pd_routes) {
|
||||
k = route_remove(route, link, NULL);
|
||||
k = route_remove(route, NULL, link, NULL);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
|
||||
|
@ -704,7 +704,7 @@ static int dhcp6_remove_old(Link *link, bool force) {
|
|||
link_dirty(link);
|
||||
|
||||
SET_FOREACH(route, link->dhcp6_routes_old) {
|
||||
k = route_remove(route, link, NULL);
|
||||
k = route_remove(route, NULL, link, NULL);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
}
|
||||
|
@ -740,7 +740,7 @@ static int dhcp6_remove(Link *link) {
|
|||
link_dirty(link);
|
||||
|
||||
SET_FOREACH(route, link->dhcp6_routes) {
|
||||
k = route_remove(route, link, NULL);
|
||||
k = route_remove(route, NULL, link, NULL);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ static int ipv4ll_address_lost(Link *link) {
|
|||
if (r < 0)
|
||||
return 0;
|
||||
|
||||
log_link_debug(link, "IPv4 link-local release %u.%u.%u.%u", ADDRESS_FMT_VAL(addr));
|
||||
log_link_debug(link, "IPv4 link-local release "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(addr));
|
||||
|
||||
r = address_new(&address);
|
||||
if (r < 0)
|
||||
|
@ -79,8 +79,8 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {
|
|||
else if (r < 0)
|
||||
return r;
|
||||
|
||||
log_link_debug(link, "IPv4 link-local claim %u.%u.%u.%u",
|
||||
ADDRESS_FMT_VAL(address));
|
||||
log_link_debug(link, "IPv4 link-local claim "IPV4_ADDRESS_FMT_STR,
|
||||
IPV4_ADDRESS_FMT_VAL(address));
|
||||
|
||||
r = address_new(&ll_addr);
|
||||
if (r < 0)
|
||||
|
|
|
@ -243,9 +243,3 @@ int log_link_message_full_errno(Link *link, sd_netlink_message *m, int level, in
|
|||
#define log_link_message_notice_errno(link, m, err, msg) log_link_message_full_errno(link, m, LOG_NOTICE, err, msg)
|
||||
#define log_link_message_info_errno(link, m, err, msg) log_link_message_full_errno(link, m, LOG_INFO, err, msg)
|
||||
#define log_link_message_debug_errno(link, m, err, msg) log_link_message_full_errno(link, m, LOG_DEBUG, err, msg)
|
||||
|
||||
#define ADDRESS_FMT_VAL(address) \
|
||||
be32toh((address).s_addr) >> 24, \
|
||||
(be32toh((address).s_addr) >> 16) & 0xFFu, \
|
||||
(be32toh((address).s_addr) >> 8) & 0xFFu, \
|
||||
be32toh((address).s_addr) & 0xFFu
|
||||
|
|
|
@ -886,6 +886,9 @@ void manager_free(Manager *m) {
|
|||
m->rules_foreign = set_free(m->rules_foreign);
|
||||
set_free(m->rules_saved);
|
||||
|
||||
m->routes = set_free(m->routes);
|
||||
m->routes_foreign = set_free(m->routes_foreign);
|
||||
|
||||
sd_netlink_unref(m->rtnl);
|
||||
sd_netlink_unref(m->genl);
|
||||
sd_resolve_unref(m->resolve);
|
||||
|
|
|
@ -61,6 +61,10 @@ struct Manager {
|
|||
Set *rules_foreign;
|
||||
Set *rules_saved;
|
||||
|
||||
/* Manager stores routes without RTA_OIF attribute. */
|
||||
Set *routes;
|
||||
Set *routes_foreign;
|
||||
|
||||
/* For link speed meter*/
|
||||
bool use_speed_meter;
|
||||
sd_event_source *speed_meter_event_source;
|
||||
|
|
|
@ -163,7 +163,7 @@ static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool
|
|||
|
||||
SET_FOREACH(nr, link->ndisc_routes)
|
||||
if (nr->marked && IN6_ARE_ADDR_EQUAL(&nr->router, router)) {
|
||||
k = route_remove(nr->route, link, NULL);
|
||||
k = route_remove(nr->route, NULL, link, NULL);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
}
|
||||
|
@ -513,6 +513,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
|
|||
route->priority = link->network->dhcp6_route_metric;
|
||||
route->protocol = RTPROT_RA;
|
||||
route->pref = preference;
|
||||
route->gw_family = AF_INET6;
|
||||
route->gw = gateway;
|
||||
route->lifetime = time_now + lifetime * USEC_PER_SEC;
|
||||
route->mtu = mtu;
|
||||
|
@ -530,6 +531,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
|
|||
continue;
|
||||
|
||||
route_gw->gw = gateway;
|
||||
route_gw->gw_family = AF_INET6;
|
||||
|
||||
r = ndisc_route_configure(route_gw, link, rt);
|
||||
if (r < 0)
|
||||
|
@ -839,6 +841,7 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
|
|||
route->protocol = RTPROT_RA;
|
||||
route->pref = preference;
|
||||
route->gw.in6 = gateway;
|
||||
route->gw_family = AF_INET6;
|
||||
route->dst_prefixlen = prefixlen;
|
||||
route->lifetime = time_now + lifetime * USEC_PER_SEC;
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <linux/icmpv6.h>
|
||||
#include <linux/ipv6_route.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "netlink-util.h"
|
||||
|
@ -21,11 +22,11 @@
|
|||
|
||||
#define ROUTES_DEFAULT_MAX_PER_FAMILY 4096U
|
||||
|
||||
static uint32_t link_get_vrf_table(Link *link) {
|
||||
static uint32_t link_get_vrf_table(const Link *link) {
|
||||
return link->network->vrf ? VRF(link->network->vrf)->table : RT_TABLE_MAIN;
|
||||
}
|
||||
|
||||
uint32_t link_get_dhcp_route_table(Link *link) {
|
||||
uint32_t link_get_dhcp_route_table(const Link *link) {
|
||||
/* When the interface is part of an VRF use the VRFs routing table, unless
|
||||
* another table is explicitly specified. */
|
||||
if (link->network->dhcp_route_table_set)
|
||||
|
@ -33,7 +34,7 @@ uint32_t link_get_dhcp_route_table(Link *link) {
|
|||
return link_get_vrf_table(link);
|
||||
}
|
||||
|
||||
uint32_t link_get_ipv6_accept_ra_route_table(Link *link) {
|
||||
uint32_t link_get_ipv6_accept_ra_route_table(const Link *link) {
|
||||
if (link->network->ipv6_accept_ra_route_table_set)
|
||||
return link->network->ipv6_accept_ra_route_table;
|
||||
return link_get_vrf_table(link);
|
||||
|
@ -271,6 +272,11 @@ Route *route_free(Route *route) {
|
|||
free(set_remove(route->link->ndisc_routes, n));
|
||||
}
|
||||
|
||||
if (route->manager) {
|
||||
set_remove(route->manager->routes, route);
|
||||
set_remove(route->manager->routes_foreign, route);
|
||||
}
|
||||
|
||||
ordered_set_free_free(route->multipath_routes);
|
||||
|
||||
sd_event_source_unref(route->expire);
|
||||
|
@ -292,7 +298,10 @@ void route_hash_func(const Route *route, struct siphash *state) {
|
|||
siphash24_compress(&route->src_prefixlen, sizeof(route->src_prefixlen), state);
|
||||
siphash24_compress(&route->src, FAMILY_ADDRESS_SIZE(route->family), state);
|
||||
|
||||
siphash24_compress(&route->gw, FAMILY_ADDRESS_SIZE(route->family), state);
|
||||
siphash24_compress(&route->gw_family, sizeof(route->gw_family), state);
|
||||
if (IN_SET(route->gw_family, AF_INET, AF_INET6))
|
||||
siphash24_compress(&route->gw, FAMILY_ADDRESS_SIZE(route->gw_family), state);
|
||||
|
||||
|
||||
siphash24_compress(&route->prefsrc, FAMILY_ADDRESS_SIZE(route->family), state);
|
||||
|
||||
|
@ -339,10 +348,16 @@ int route_compare_func(const Route *a, const Route *b) {
|
|||
if (r != 0)
|
||||
return r;
|
||||
|
||||
r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
|
||||
r = CMP(a->gw_family, b->gw_family);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
if (IN_SET(a->gw_family, AF_INET, AF_INET6)) {
|
||||
r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
|
||||
if (r != 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = memcmp(&a->prefsrc, &b->prefsrc, FAMILY_ADDRESS_SIZE(a->family));
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
@ -393,7 +408,7 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
|
|||
route_compare_func,
|
||||
route_free);
|
||||
|
||||
static bool route_equal(Route *r1, Route *r2) {
|
||||
static bool route_equal(const Route *r1, const Route *r2) {
|
||||
if (r1 == r2)
|
||||
return true;
|
||||
|
||||
|
@ -403,59 +418,89 @@ static bool route_equal(Route *r1, Route *r2) {
|
|||
return route_compare_func(r1, r2) == 0;
|
||||
}
|
||||
|
||||
static int route_get(Link *link, Route *in, Route **ret) {
|
||||
|
||||
static int route_get(const Manager *manager, const Link *link, const Route *in, Route **ret) {
|
||||
Route *existing;
|
||||
|
||||
assert(link);
|
||||
assert(manager || link);
|
||||
assert(in);
|
||||
|
||||
existing = set_get(link->routes, in);
|
||||
if (existing) {
|
||||
if (ret)
|
||||
*ret = existing;
|
||||
return 1;
|
||||
}
|
||||
if (link) {
|
||||
existing = set_get(link->routes, in);
|
||||
if (existing) {
|
||||
if (ret)
|
||||
*ret = existing;
|
||||
return 1;
|
||||
}
|
||||
|
||||
existing = set_get(link->routes_foreign, in);
|
||||
if (existing) {
|
||||
if (ret)
|
||||
*ret = existing;
|
||||
return 0;
|
||||
existing = set_get(link->routes_foreign, in);
|
||||
if (existing) {
|
||||
if (ret)
|
||||
*ret = existing;
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
existing = set_get(manager->routes, in);
|
||||
if (existing) {
|
||||
if (ret)
|
||||
*ret = existing;
|
||||
return 1;
|
||||
}
|
||||
|
||||
existing = set_get(manager->routes_foreign, in);
|
||||
if (existing) {
|
||||
if (ret)
|
||||
*ret = existing;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int route_add_internal(Link *link, Set **routes, Route *in, Route **ret) {
|
||||
static void route_copy(Route *dest, const Route *src, const MultipathRoute *m) {
|
||||
assert(dest);
|
||||
assert(src);
|
||||
|
||||
dest->family = src->family;
|
||||
dest->src = src->src;
|
||||
dest->src_prefixlen = src->src_prefixlen;
|
||||
dest->dst = src->dst;
|
||||
dest->dst_prefixlen = src->dst_prefixlen;
|
||||
dest->prefsrc = src->prefsrc;
|
||||
dest->scope = src->scope;
|
||||
dest->protocol = src->protocol;
|
||||
dest->type = src->type;
|
||||
dest->tos = src->tos;
|
||||
dest->priority = src->priority;
|
||||
dest->table = src->table;
|
||||
dest->initcwnd = src->initcwnd;
|
||||
dest->initrwnd = src->initrwnd;
|
||||
dest->lifetime = src->lifetime;
|
||||
|
||||
if (m) {
|
||||
dest->gw_family = m->gateway.family;
|
||||
dest->gw = m->gateway.address;
|
||||
dest->gw_weight = m->weight;
|
||||
} else {
|
||||
dest->gw_family = src->gw_family;
|
||||
dest->gw = src->gw;
|
||||
}
|
||||
}
|
||||
|
||||
static int route_add_internal(Manager *manager, Link *link, Set **routes, const Route *in, const MultipathRoute *m, Route **ret) {
|
||||
_cleanup_(route_freep) Route *route = NULL;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(manager || link);
|
||||
assert(routes);
|
||||
assert(in);
|
||||
assert(!m || (link && (m->ifindex == 0 || m->ifindex == link->ifindex)));
|
||||
|
||||
r = route_new(&route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
route->family = in->family;
|
||||
route->src = in->src;
|
||||
route->src_prefixlen = in->src_prefixlen;
|
||||
route->dst = in->dst;
|
||||
route->dst_prefixlen = in->dst_prefixlen;
|
||||
route->gw = in->gw;
|
||||
route->prefsrc = in->prefsrc;
|
||||
route->scope = in->scope;
|
||||
route->protocol = in->protocol;
|
||||
route->type = in->type;
|
||||
route->tos = in->tos;
|
||||
route->priority = in->priority;
|
||||
route->table = in->table;
|
||||
route->initcwnd = in->initcwnd;
|
||||
route->initrwnd = in->initrwnd;
|
||||
route->lifetime = in->lifetime;
|
||||
route_copy(route, in, m);
|
||||
|
||||
r = set_ensure_put(routes, &route_hash_ops, route);
|
||||
if (r < 0)
|
||||
|
@ -464,6 +509,7 @@ static int route_add_internal(Link *link, Set **routes, Route *in, Route **ret)
|
|||
return -EEXIST;
|
||||
|
||||
route->link = link;
|
||||
route->manager = manager;
|
||||
|
||||
if (ret)
|
||||
*ret = route;
|
||||
|
@ -473,28 +519,39 @@ static int route_add_internal(Link *link, Set **routes, Route *in, Route **ret)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int route_add_foreign(Link *link, Route *in, Route **ret) {
|
||||
return route_add_internal(link, &link->routes_foreign, in, ret);
|
||||
static int route_add_foreign(Manager *manager, Link *link, const Route *in, Route **ret) {
|
||||
assert(manager || link);
|
||||
return route_add_internal(manager, link, link ? &link->routes_foreign : &manager->routes_foreign, in, NULL, ret);
|
||||
}
|
||||
|
||||
static int route_add(Link *link, Route *in, Route **ret) {
|
||||
|
||||
static int route_add(Manager *manager, Link *link, const Route *in, const MultipathRoute *m, Route **ret) {
|
||||
Route *route;
|
||||
int r;
|
||||
|
||||
r = route_get(link, in, &route);
|
||||
assert(manager || link);
|
||||
assert(in);
|
||||
|
||||
r = route_get(manager, link, in, &route);
|
||||
if (r == -ENOENT) {
|
||||
/* Route does not exist, create a new one */
|
||||
r = route_add_internal(link, &link->routes, in, &route);
|
||||
r = route_add_internal(manager, link, link ? &link->routes : &manager->routes, in, m, &route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else if (r == 0) {
|
||||
/* Take over a foreign route */
|
||||
r = set_ensure_put(&link->routes, &route_hash_ops, route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (link) {
|
||||
r = set_ensure_put(&link->routes, &route_hash_ops, route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
set_remove(link->routes_foreign, route);
|
||||
set_remove(link->routes_foreign, route);
|
||||
} else {
|
||||
r = set_ensure_put(&manager->routes, &route_hash_ops, route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
set_remove(manager->routes_foreign, route);
|
||||
}
|
||||
} else if (r == 1) {
|
||||
/* Route exists, do nothing */
|
||||
;
|
||||
|
@ -511,10 +568,9 @@ static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *l
|
|||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(link);
|
||||
assert(link->ifname);
|
||||
|
||||
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
|
||||
/* Note that link may be NULL. */
|
||||
if (link && IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
|
||||
return 1;
|
||||
|
||||
r = sd_netlink_message_get_errno(m);
|
||||
|
@ -524,19 +580,22 @@ static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *l
|
|||
return 1;
|
||||
}
|
||||
|
||||
int route_remove(Route *route, Link *link,
|
||||
link_netlink_message_handler_t callback) {
|
||||
int route_remove(
|
||||
const Route *route,
|
||||
Manager *manager,
|
||||
Link *link,
|
||||
link_netlink_message_handler_t callback) {
|
||||
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
assert(link->manager->rtnl);
|
||||
assert(link->ifindex > 0);
|
||||
assert(link || manager);
|
||||
assert(IN_SET(route->family, AF_INET, AF_INET6));
|
||||
|
||||
r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
|
||||
if (!manager)
|
||||
manager = link->manager;
|
||||
|
||||
r = sd_rtnl_message_new_route(manager->rtnl, &req,
|
||||
RTM_DELROUTE, route->family,
|
||||
route->protocol);
|
||||
if (r < 0)
|
||||
|
@ -552,8 +611,8 @@ int route_remove(Route *route, Link *link,
|
|||
}
|
||||
if (!in_addr_is_null(route->family, &route->src))
|
||||
(void) in_addr_to_string(route->family, &route->src, &src);
|
||||
if (!in_addr_is_null(route->family, &route->gw))
|
||||
(void) in_addr_to_string(route->family, &route->gw, &gw);
|
||||
if (!in_addr_is_null(route->gw_family, &route->gw))
|
||||
(void) in_addr_to_string(route->gw_family, &route->gw, &gw);
|
||||
if (!in_addr_is_null(route->family, &route->prefsrc))
|
||||
(void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
|
||||
|
||||
|
@ -565,10 +624,21 @@ int route_remove(Route *route, Link *link,
|
|||
strna(route_type_to_string(route->type)));
|
||||
}
|
||||
|
||||
if (in_addr_is_null(route->family, &route->gw) == 0) {
|
||||
r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->family, &route->gw);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
|
||||
if (in_addr_is_null(route->gw_family, &route->gw) == 0) {
|
||||
if (route->gw_family == route->family) {
|
||||
r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->gw_family, &route->gw);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
|
||||
} else {
|
||||
RouteVia rtvia = {
|
||||
.family = route->gw_family,
|
||||
.address = route->gw,
|
||||
};
|
||||
|
||||
r = sd_netlink_message_append_data(req, RTA_VIA, &rtvia, sizeof(rtvia));
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not append RTA_VIA attribute: %m");
|
||||
}
|
||||
}
|
||||
|
||||
if (route->dst_prefixlen) {
|
||||
|
@ -617,12 +687,13 @@ int route_remove(Route *route, Link *link,
|
|||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
|
||||
|
||||
link_ref(link);
|
||||
if (link)
|
||||
link_ref(link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool link_is_static_route_configured(Link *link, Route *route) {
|
||||
static bool link_is_static_route_configured(const Link *link, const Route *route) {
|
||||
Route *net_route;
|
||||
|
||||
assert(link);
|
||||
|
@ -667,9 +738,9 @@ int link_drop_foreign_routes(Link *link) {
|
|||
continue;
|
||||
|
||||
if (link_is_static_route_configured(link, route))
|
||||
k = route_add(link, route, NULL);
|
||||
k = route_add(NULL, link, route, NULL, NULL);
|
||||
else
|
||||
k = route_remove(route, link, NULL);
|
||||
k = route_remove(route, NULL, link, NULL);
|
||||
if (k < 0 && r >= 0)
|
||||
r = k;
|
||||
}
|
||||
|
@ -688,7 +759,7 @@ int link_drop_routes(Link *link) {
|
|||
if (route->protocol == RTPROT_KERNEL)
|
||||
continue;
|
||||
|
||||
k = route_remove(route, link, NULL);
|
||||
k = route_remove(route, NULL, link, NULL);
|
||||
if (k < 0 && r >= 0)
|
||||
r = k;
|
||||
}
|
||||
|
@ -702,16 +773,57 @@ static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdat
|
|||
|
||||
assert(route);
|
||||
|
||||
r = route_remove(route, route->link, NULL);
|
||||
if (r < 0)
|
||||
r = route_remove(route, route->manager, route->link, NULL);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(route->link, r, "Could not remove route: %m");
|
||||
else
|
||||
route_free(route);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int append_nexthop_one(Route *route, MultipathRoute *m, struct rtattr **rta, size_t offset) {
|
||||
static int route_add_and_setup_timer(Link *link, const Route *route, const MultipathRoute *m, Route **ret) {
|
||||
_cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
|
||||
Route *nr;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(route);
|
||||
|
||||
if (IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW))
|
||||
r = route_add(link->manager, NULL, route, NULL, &nr);
|
||||
else if (!m || m->ifindex == 0 || m->ifindex == link->ifindex)
|
||||
r = route_add(NULL, link, route, m, &nr);
|
||||
else {
|
||||
Link *link_gw;
|
||||
|
||||
r = link_get(link->manager, m->ifindex, &link_gw);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Failed to get link with ifindex %d: %m", m->ifindex);
|
||||
|
||||
r = route_add(NULL, link_gw, route, m, &nr);
|
||||
}
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not add route: %m");
|
||||
|
||||
/* TODO: drop expiration handling once it can be pushed into the kernel */
|
||||
if (nr->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) {
|
||||
r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(),
|
||||
nr->lifetime, 0, route_expire_handler, nr);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not arm expiration timer: %m");
|
||||
}
|
||||
|
||||
sd_event_source_unref(nr->expire);
|
||||
nr->expire = TAKE_PTR(expire);
|
||||
|
||||
if (ret)
|
||||
*ret = nr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int append_nexthop_one(const Route *route, const MultipathRoute *m, struct rtattr **rta, size_t offset) {
|
||||
struct rtnexthop *rtnh;
|
||||
struct rtattr *new_rta;
|
||||
int r;
|
||||
|
@ -756,7 +868,7 @@ clear:
|
|||
return r;
|
||||
}
|
||||
|
||||
static int append_nexthops(Route *route, sd_netlink_message *req) {
|
||||
static int append_nexthops(const Route *route, sd_netlink_message *req) {
|
||||
_cleanup_free_ struct rtattr *rta = NULL;
|
||||
struct rtnexthop *rtnh;
|
||||
MultipathRoute *m;
|
||||
|
@ -793,13 +905,13 @@ static int append_nexthops(Route *route, sd_netlink_message *req) {
|
|||
}
|
||||
|
||||
int route_configure(
|
||||
Route *route,
|
||||
const Route *route,
|
||||
Link *link,
|
||||
link_netlink_message_handler_t callback,
|
||||
Route **ret) {
|
||||
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
|
||||
_cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
|
||||
unsigned flags;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
@ -809,7 +921,7 @@ int route_configure(
|
|||
assert(IN_SET(route->family, AF_INET, AF_INET6));
|
||||
assert(callback);
|
||||
|
||||
if (route_get(link, route, NULL) <= 0 &&
|
||||
if (route_get(link->manager, link, route, NULL) <= 0 &&
|
||||
set_size(link->routes) >= routes_max())
|
||||
return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG),
|
||||
"Too many routes are configured, refusing: %m");
|
||||
|
@ -824,8 +936,8 @@ int route_configure(
|
|||
}
|
||||
if (!in_addr_is_null(route->family, &route->src))
|
||||
(void) in_addr_to_string(route->family, &route->src, &src);
|
||||
if (!in_addr_is_null(route->family, &route->gw))
|
||||
(void) in_addr_to_string(route->family, &route->gw, &gw);
|
||||
if (!in_addr_is_null(route->gw_family, &route->gw))
|
||||
(void) in_addr_to_string(route->gw_family, &route->gw, &gw);
|
||||
if (!in_addr_is_null(route->family, &route->prefsrc))
|
||||
(void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
|
||||
|
||||
|
@ -843,14 +955,21 @@ int route_configure(
|
|||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not create RTM_NEWROUTE message: %m");
|
||||
|
||||
if (in_addr_is_null(route->family, &route->gw) == 0) {
|
||||
r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->family, &route->gw);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
|
||||
if (in_addr_is_null(route->gw_family, &route->gw) == 0) {
|
||||
if (route->gw_family == route->family) {
|
||||
r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->gw_family, &route->gw);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
|
||||
} else {
|
||||
RouteVia rtvia = {
|
||||
.family = route->gw_family,
|
||||
.address = route->gw,
|
||||
};
|
||||
|
||||
r = sd_rtnl_message_route_set_family(req, route->family);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not set route family: %m");
|
||||
r = sd_netlink_message_append_data(req, RTA_VIA, &rtvia, sizeof(rtvia));
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not append RTA_VIA attribute: %m");
|
||||
}
|
||||
}
|
||||
|
||||
if (route->dst_prefixlen > 0) {
|
||||
|
@ -883,10 +1002,11 @@ int route_configure(
|
|||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not set scope: %m");
|
||||
|
||||
flags = route->flags;
|
||||
if (route->gateway_onlink >= 0)
|
||||
SET_FLAG(route->flags, RTNH_F_ONLINK, route->gateway_onlink);
|
||||
SET_FLAG(flags, RTNH_F_ONLINK, route->gateway_onlink);
|
||||
|
||||
r = sd_rtnl_message_route_set_flags(req, route->flags);
|
||||
r = sd_rtnl_message_route_set_flags(req, flags);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not set flags: %m");
|
||||
|
||||
|
@ -987,24 +1107,27 @@ int route_configure(
|
|||
|
||||
link_ref(link);
|
||||
|
||||
r = route_add(link, route, &route);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not add route: %m");
|
||||
if (ordered_set_isempty(route->multipath_routes)) {
|
||||
Route *nr;
|
||||
|
||||
/* TODO: drop expiration handling once it can be pushed into the kernel */
|
||||
if (route->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) {
|
||||
r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(),
|
||||
route->lifetime, 0, route_expire_handler, route);
|
||||
r = route_add_and_setup_timer(link, route, NULL, &nr);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not arm expiration timer: %m");
|
||||
return r;
|
||||
|
||||
if (ret)
|
||||
*ret = nr;
|
||||
} else {
|
||||
MultipathRoute *m;
|
||||
|
||||
assert(!ret);
|
||||
|
||||
ORDERED_SET_FOREACH(m, route->multipath_routes) {
|
||||
r = route_add_and_setup_timer(link, route, m, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
sd_event_source_unref(route->expire);
|
||||
route->expire = TAKE_PTR(expire);
|
||||
|
||||
if (ret)
|
||||
*ret = route;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -1070,7 +1193,7 @@ int link_set_routes(Link *link) {
|
|||
if (rt->gateway_from_dhcp)
|
||||
continue;
|
||||
|
||||
if ((in_addr_is_null(rt->family, &rt->gw) && ordered_set_isempty(rt->multipath_routes)) != (phase == PHASE_NON_GATEWAY))
|
||||
if ((in_addr_is_null(rt->gw_family, &rt->gw) && ordered_set_isempty(rt->multipath_routes)) != (phase == PHASE_NON_GATEWAY))
|
||||
continue;
|
||||
|
||||
r = route_configure(rt, link, route_handler, NULL);
|
||||
|
@ -1091,13 +1214,105 @@ int link_set_routes(Link *link) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
|
||||
_cleanup_(route_freep) Route *tmp = NULL;
|
||||
static int process_route_one(Manager *manager, Link *link, uint16_t type, const Route *tmp, const MultipathRoute *m) {
|
||||
_cleanup_(route_freep) Route *nr = NULL;
|
||||
Route *route = NULL;
|
||||
int r;
|
||||
|
||||
assert(manager);
|
||||
assert(tmp);
|
||||
assert(IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE));
|
||||
|
||||
if (m) {
|
||||
if (link)
|
||||
return log_link_warning_errno(link, SYNTHETIC_ERRNO(EINVAL),
|
||||
"rtnl: received route contains both RTA_OIF and RTA_MULTIPATH, ignoring.");
|
||||
|
||||
if (m->ifindex <= 0)
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"rtnl: received multipath route with invalid ifindex, ignoring.");
|
||||
|
||||
r = link_get(manager, m->ifindex, &link);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "rtnl: received multipath route for link (%d) we do not know, ignoring: %m", m->ifindex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = route_new(&nr);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
route_copy(nr, tmp, m);
|
||||
|
||||
tmp = nr;
|
||||
}
|
||||
|
||||
(void) route_get(manager, link, tmp, &route);
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
_cleanup_free_ char *buf_dst = NULL, *buf_dst_prefixlen = NULL,
|
||||
*buf_src = NULL, *buf_gw = NULL, *buf_prefsrc = NULL;
|
||||
char buf_scope[ROUTE_SCOPE_STR_MAX], buf_table[ROUTE_TABLE_STR_MAX],
|
||||
buf_protocol[ROUTE_PROTOCOL_STR_MAX];
|
||||
|
||||
if (!in_addr_is_null(tmp->family, &tmp->dst)) {
|
||||
(void) in_addr_to_string(tmp->family, &tmp->dst, &buf_dst);
|
||||
(void) asprintf(&buf_dst_prefixlen, "/%u", tmp->dst_prefixlen);
|
||||
}
|
||||
if (!in_addr_is_null(tmp->family, &tmp->src))
|
||||
(void) in_addr_to_string(tmp->family, &tmp->src, &buf_src);
|
||||
if (!in_addr_is_null(tmp->gw_family, &tmp->gw))
|
||||
(void) in_addr_to_string(tmp->gw_family, &tmp->gw, &buf_gw);
|
||||
if (!in_addr_is_null(tmp->family, &tmp->prefsrc))
|
||||
(void) in_addr_to_string(tmp->family, &tmp->prefsrc, &buf_prefsrc);
|
||||
|
||||
log_link_debug(link,
|
||||
"%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
|
||||
(!route && !manager->manage_foreign_routes) ? "Ignoring received foreign" :
|
||||
type == RTM_DELROUTE ? "Forgetting" :
|
||||
route ? "Received remembered" : "Remembering",
|
||||
strna(buf_dst), strempty(buf_dst_prefixlen),
|
||||
strna(buf_src), strna(buf_gw), strna(buf_prefsrc),
|
||||
format_route_scope(tmp->scope, buf_scope, sizeof buf_scope),
|
||||
format_route_table(tmp->table, buf_table, sizeof buf_table),
|
||||
format_route_protocol(tmp->protocol, buf_protocol, sizeof buf_protocol),
|
||||
strna(route_type_to_string(tmp->type)));
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case RTM_NEWROUTE:
|
||||
if (!route && manager->manage_foreign_routes) {
|
||||
/* A route appeared that we did not request */
|
||||
r = route_add_foreign(manager, link, tmp, NULL);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case RTM_DELROUTE:
|
||||
route_free(route);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Received route message with invalid RTNL message type");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
|
||||
_cleanup_ordered_set_free_free_ OrderedSet *multipath_routes = NULL;
|
||||
_cleanup_(route_freep) Route *tmp = NULL;
|
||||
_cleanup_free_ void *rta_multipath = NULL;
|
||||
Link *link = NULL;
|
||||
uint32_t ifindex;
|
||||
uint16_t type;
|
||||
unsigned char table;
|
||||
RouteVia via;
|
||||
size_t rta_len;
|
||||
int r;
|
||||
|
||||
assert(rtnl);
|
||||
|
@ -1122,24 +1337,23 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
|
|||
}
|
||||
|
||||
r = sd_netlink_message_read_u32(message, RTA_OIF, &ifindex);
|
||||
if (r == -ENODATA) {
|
||||
log_debug("rtnl: received route message without ifindex, ignoring");
|
||||
return 0;
|
||||
} else if (r < 0) {
|
||||
if (r < 0 && r != -ENODATA) {
|
||||
log_warning_errno(r, "rtnl: could not get ifindex from route message, ignoring: %m");
|
||||
return 0;
|
||||
} else if (ifindex <= 0) {
|
||||
log_warning("rtnl: received route message with invalid ifindex %d, ignoring.", ifindex);
|
||||
return 0;
|
||||
}
|
||||
} else if (r >= 0) {
|
||||
if (ifindex <= 0) {
|
||||
log_warning("rtnl: received route message with invalid ifindex %d, ignoring.", ifindex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = link_get(m, ifindex, &link);
|
||||
if (r < 0 || !link) {
|
||||
/* when enumerating we might be out of sync, but we will
|
||||
* get the route again, so just ignore it */
|
||||
if (!m->enumerating)
|
||||
log_warning("rtnl: received route message for link (%d) we do not know about, ignoring", ifindex);
|
||||
return 0;
|
||||
r = link_get(m, ifindex, &link);
|
||||
if (r < 0 || !link) {
|
||||
/* when enumerating we might be out of sync, but we will
|
||||
* get the route again, so just ignore it */
|
||||
if (!m->enumerating)
|
||||
log_warning("rtnl: received route message for link (%d) we do not know about, ignoring", ifindex);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
r = route_new(&tmp);
|
||||
|
@ -1173,6 +1387,16 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
|
|||
if (r < 0 && r != -ENODATA) {
|
||||
log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
|
||||
return 0;
|
||||
} else if (r >= 0)
|
||||
tmp->gw_family = AF_INET;
|
||||
|
||||
r = sd_netlink_message_read(message, RTA_VIA, sizeof(via), &via);
|
||||
if (r < 0 && r != -ENODATA) {
|
||||
log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
|
||||
return 0;
|
||||
} else if (r >= 0) {
|
||||
tmp->gw_family = via.family;
|
||||
tmp->gw = via.address;
|
||||
}
|
||||
|
||||
r = sd_netlink_message_read_in_addr(message, RTA_SRC, &tmp->src.in);
|
||||
|
@ -1200,7 +1424,8 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
|
|||
if (r < 0 && r != -ENODATA) {
|
||||
log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
} else if (r >= 0)
|
||||
tmp->gw_family = AF_INET6;
|
||||
|
||||
r = sd_netlink_message_read_in6_addr(message, RTA_SRC, &tmp->src.in6);
|
||||
if (r < 0 && r != -ENODATA) {
|
||||
|
@ -1289,63 +1514,34 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
|
|||
}
|
||||
}
|
||||
|
||||
(void) route_get(link, tmp, &route);
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
_cleanup_free_ char *buf_dst = NULL, *buf_dst_prefixlen = NULL,
|
||||
*buf_src = NULL, *buf_gw = NULL, *buf_prefsrc = NULL;
|
||||
char buf_scope[ROUTE_SCOPE_STR_MAX], buf_table[ROUTE_TABLE_STR_MAX],
|
||||
buf_protocol[ROUTE_PROTOCOL_STR_MAX];
|
||||
|
||||
if (!in_addr_is_null(tmp->family, &tmp->dst)) {
|
||||
(void) in_addr_to_string(tmp->family, &tmp->dst, &buf_dst);
|
||||
(void) asprintf(&buf_dst_prefixlen, "/%u", tmp->dst_prefixlen);
|
||||
r = sd_netlink_message_read_data(message, RTA_MULTIPATH, &rta_len, &rta_multipath);
|
||||
if (r < 0 && r != -ENODATA) {
|
||||
log_link_warning_errno(link, r, "rtnl: failed to read RTA_MULTIPATH attribute, ignoring: %m");
|
||||
return 0;
|
||||
} else if (r >= 0) {
|
||||
r = rtattr_read_nexthop(rta_multipath, rta_len, tmp->family, &multipath_routes);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "rtnl: failed to parse RTA_MULTIPATH attribute, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
if (!in_addr_is_null(tmp->family, &tmp->src))
|
||||
(void) in_addr_to_string(tmp->family, &tmp->src, &buf_src);
|
||||
if (!in_addr_is_null(tmp->family, &tmp->gw))
|
||||
(void) in_addr_to_string(tmp->family, &tmp->gw, &buf_gw);
|
||||
if (!in_addr_is_null(tmp->family, &tmp->prefsrc))
|
||||
(void) in_addr_to_string(tmp->family, &tmp->prefsrc, &buf_prefsrc);
|
||||
|
||||
log_link_debug(link,
|
||||
"%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
|
||||
(!route && !link->manager->manage_foreign_routes) ? "Ignoring received foreign" :
|
||||
type == RTM_DELROUTE ? "Forgetting" :
|
||||
route ? "Received remembered" : "Remembering",
|
||||
strna(buf_dst), strempty(buf_dst_prefixlen),
|
||||
strna(buf_src), strna(buf_gw), strna(buf_prefsrc),
|
||||
format_route_scope(tmp->scope, buf_scope, sizeof buf_scope),
|
||||
format_route_table(tmp->table, buf_table, sizeof buf_table),
|
||||
format_route_protocol(tmp->protocol, buf_protocol, sizeof buf_protocol),
|
||||
strna(route_type_to_string(tmp->type)));
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case RTM_NEWROUTE:
|
||||
if (!route && link->manager->manage_foreign_routes) {
|
||||
/* A route appeared that we did not request */
|
||||
r = route_add_foreign(link, tmp, &route);
|
||||
if (r < 0) {
|
||||
log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
if (ordered_set_isempty(multipath_routes))
|
||||
(void) process_route_one(m, link, type, tmp, NULL);
|
||||
else {
|
||||
MultipathRoute *mr;
|
||||
|
||||
ORDERED_SET_FOREACH(mr, multipath_routes) {
|
||||
r = process_route_one(m, link, type, tmp, mr);
|
||||
if (r < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case RTM_DELROUTE:
|
||||
route_free(route);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Received route message with invalid RTNL message type");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int link_serialize_routes(Link *link, FILE *f) {
|
||||
int link_serialize_routes(const Link *link, FILE *f) {
|
||||
bool space = false;
|
||||
Route *route;
|
||||
|
||||
|
@ -1376,11 +1572,9 @@ int link_deserialize_routes(Link *link, const char *routes) {
|
|||
assert(link);
|
||||
|
||||
for (const char *p = routes;; ) {
|
||||
_cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
|
||||
_cleanup_(route_freep) Route *tmp = NULL;
|
||||
_cleanup_free_ char *route_str = NULL;
|
||||
char *prefixlen_str;
|
||||
Route *route;
|
||||
|
||||
r = extract_first_word(&p, &route_str, NULL, 0);
|
||||
if (r < 0)
|
||||
|
@ -1419,20 +1613,9 @@ int link_deserialize_routes(Link *link, const char *routes) {
|
|||
continue;
|
||||
}
|
||||
|
||||
r = route_add(link, tmp, &route);
|
||||
r = route_add_and_setup_timer(link, tmp, NULL, NULL);
|
||||
if (r < 0)
|
||||
return log_link_debug_errno(link, r, "Failed to add route: %m");
|
||||
|
||||
if (route->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) {
|
||||
r = sd_event_add_time(link->manager->event, &expire,
|
||||
clock_boottime_or_monotonic(),
|
||||
route->lifetime, 0, route_expire_handler, route);
|
||||
if (r < 0)
|
||||
log_link_debug_errno(link, r, "Could not arm route expiration handler: %m");
|
||||
}
|
||||
|
||||
sd_event_source_unref(route->expire);
|
||||
route->expire = TAKE_PTR(expire);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1537,23 +1720,43 @@ int config_parse_gateway(
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
n->gateway_from_dhcp = false;
|
||||
n->gw_family = AF_UNSPEC;
|
||||
n->gw = IN_ADDR_NULL;
|
||||
TAKE_PTR(n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (streq(rvalue, "_dhcp")) {
|
||||
n->gateway_from_dhcp = true;
|
||||
TAKE_PTR(n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (streq(rvalue, "_dhcp4")) {
|
||||
n->gw_family = AF_INET;
|
||||
n->gateway_from_dhcp = true;
|
||||
TAKE_PTR(n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (streq(rvalue, "_dhcp6")) {
|
||||
n->gw_family = AF_INET6;
|
||||
n->gateway_from_dhcp = true;
|
||||
TAKE_PTR(n);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (n->family == AF_UNSPEC)
|
||||
r = in_addr_from_string_auto(rvalue, &n->family, &n->gw);
|
||||
else
|
||||
r = in_addr_from_string(n->family, rvalue, &n->gw);
|
||||
r = in_addr_from_string_auto(rvalue, &n->gw_family, &n->gw);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
n->gateway_from_dhcp = false;
|
||||
TAKE_PTR(n);
|
||||
return 0;
|
||||
}
|
||||
|
@ -2175,15 +2378,32 @@ static int route_section_verify(Route *route, Network *network) {
|
|||
if (section_is_invalid(route->section))
|
||||
return -EINVAL;
|
||||
|
||||
if (route->family == AF_UNSPEC)
|
||||
route->family = route->gw_family;
|
||||
|
||||
if (route->family == AF_UNSPEC) {
|
||||
assert(route->section);
|
||||
|
||||
if (route->gateway_from_dhcp) {
|
||||
log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
|
||||
"Please use \"_dhcp4\" or \"_dhcp6\" instead. Assuming \"_dhcp4\".",
|
||||
route->section->filename, route->section->line);
|
||||
|
||||
route->family = AF_INET;
|
||||
route->gw_family = AF_INET;
|
||||
} else
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: Route section without Gateway=, Destination=, Source=, "
|
||||
"or PreferredSource= field configured. "
|
||||
"Ignoring [Route] section from line %u.",
|
||||
route->section->filename, route->section->line);
|
||||
}
|
||||
|
||||
if (route->family == AF_INET6 && route->gw_family == AF_INET)
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: Route section without Gateway=, Destination=, Source=, "
|
||||
"or PreferredSource= field configured. "
|
||||
"%s: IPv4 gateway is configured for IPv6 route. "
|
||||
"Ignoring [Route] section from line %u.",
|
||||
route->section->filename, route->section->line);
|
||||
}
|
||||
|
||||
if (!route->table_set && network->vrf) {
|
||||
route->table = VRF(network->vrf)->table;
|
||||
|
@ -2200,8 +2420,16 @@ static int route_section_verify(Route *route, Network *network) {
|
|||
route->scope = RT_SCOPE_LINK;
|
||||
}
|
||||
|
||||
if (route->scope != RT_SCOPE_UNIVERSE && route->family == AF_INET6) {
|
||||
log_warning("%s: Scope= is specified for IPv6 route. It will be ignored.", route->section->filename);
|
||||
route->scope = RT_SCOPE_UNIVERSE;
|
||||
}
|
||||
|
||||
if (route->family == AF_INET6 && route->priority == 0)
|
||||
route->priority = IP6_RT_PRIO_USER;
|
||||
|
||||
if (ordered_hashmap_isempty(network->addresses_by_section) &&
|
||||
in_addr_is_null(route->family, &route->gw) == 0 &&
|
||||
in_addr_is_null(route->gw_family, &route->gw) == 0 &&
|
||||
route->gateway_onlink < 0) {
|
||||
log_warning("%s: Gateway= without static address configured. "
|
||||
"Enabling GatewayOnLink= option.",
|
||||
|
|
|
@ -15,24 +15,16 @@
|
|||
typedef struct Manager Manager;
|
||||
typedef struct Network Network;
|
||||
|
||||
typedef struct MultipathRouteVia {
|
||||
uint16_t family;
|
||||
union in_addr_union address;
|
||||
} _packed_ MultipathRouteVia;
|
||||
|
||||
typedef struct MultipathRoute {
|
||||
MultipathRouteVia gateway;
|
||||
int ifindex;
|
||||
uint32_t weight;
|
||||
} MultipathRoute;
|
||||
|
||||
typedef struct Route {
|
||||
Network *network;
|
||||
NetworkConfigSection *section;
|
||||
|
||||
Link *link;
|
||||
Manager *manager;
|
||||
|
||||
int family;
|
||||
int gw_family;
|
||||
uint32_t gw_weight;
|
||||
int quickack;
|
||||
int fast_open_no_cookie;
|
||||
int ttl_propagate;
|
||||
|
@ -73,17 +65,17 @@ int route_new(Route **ret);
|
|||
Route *route_free(Route *route);
|
||||
DEFINE_NETWORK_SECTION_FUNCTIONS(Route, route_free);
|
||||
|
||||
int route_configure(Route *route, Link *link, link_netlink_message_handler_t callback, Route **ret);
|
||||
int route_remove(Route *route, Link *link, link_netlink_message_handler_t callback);
|
||||
int route_configure(const Route *route, Link *link, link_netlink_message_handler_t callback, Route **ret);
|
||||
int route_remove(const Route *route, Manager *manager, Link *link, link_netlink_message_handler_t callback);
|
||||
|
||||
int link_set_routes(Link *link);
|
||||
int link_drop_routes(Link *link);
|
||||
int link_drop_foreign_routes(Link *link);
|
||||
int link_serialize_routes(Link *link, FILE *f);
|
||||
int link_serialize_routes(const Link *link, FILE *f);
|
||||
int link_deserialize_routes(Link *link, const char *routes);
|
||||
|
||||
uint32_t link_get_dhcp_route_table(Link *link);
|
||||
uint32_t link_get_ipv6_accept_ra_route_table(Link *link);
|
||||
uint32_t link_get_dhcp_route_table(const Link *link);
|
||||
uint32_t link_get_ipv6_accept_ra_route_table(const Link *link);
|
||||
|
||||
int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m);
|
||||
|
||||
|
|
|
@ -142,11 +142,40 @@ int local_addresses(sd_netlink *context, int ifindex, int af, struct local_addre
|
|||
return (int) n_list;
|
||||
}
|
||||
|
||||
static int add_local_gateway(
|
||||
struct local_address **list,
|
||||
size_t *n_list,
|
||||
size_t *n_allocated,
|
||||
int af,
|
||||
int ifindex,
|
||||
uint32_t metric,
|
||||
const RouteVia *via) {
|
||||
|
||||
assert(list);
|
||||
assert(n_list);
|
||||
assert(n_allocated);
|
||||
assert(via);
|
||||
|
||||
if (af != AF_UNSPEC && af != via->family)
|
||||
return 0;
|
||||
|
||||
if (!GREEDY_REALLOC(*list, *n_allocated, *n_list + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
(*list)[(*n_list)++] = (struct local_address) {
|
||||
.ifindex = ifindex,
|
||||
.metric = metric,
|
||||
.family = via->family,
|
||||
.address = via->address,
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int local_gateways(sd_netlink *context, int ifindex, int af, struct local_address **ret) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
|
||||
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
|
||||
_cleanup_free_ struct local_address *list = NULL;
|
||||
sd_netlink_message *m = NULL;
|
||||
size_t n_list = 0, n_allocated = 0;
|
||||
int r;
|
||||
|
||||
|
@ -172,12 +201,16 @@ int local_gateways(sd_netlink *context, int ifindex, int af, struct local_addres
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (m = reply; m; m = sd_netlink_message_next(m)) {
|
||||
struct local_address *a;
|
||||
for (sd_netlink_message *m = reply; m; m = sd_netlink_message_next(m)) {
|
||||
_cleanup_ordered_set_free_free_ OrderedSet *multipath_routes = NULL;
|
||||
_cleanup_free_ void *rta_multipath = NULL;
|
||||
union in_addr_union gateway;
|
||||
uint16_t type;
|
||||
unsigned char dst_len, src_len, table;
|
||||
uint32_t ifi;
|
||||
uint32_t ifi, metric = 0;
|
||||
size_t rta_len;
|
||||
int family;
|
||||
RouteVia via;
|
||||
|
||||
r = sd_netlink_message_get_errno(m);
|
||||
if (r < 0)
|
||||
|
@ -208,48 +241,72 @@ int local_gateways(sd_netlink *context, int ifindex, int af, struct local_addres
|
|||
if (table != RT_TABLE_MAIN)
|
||||
continue;
|
||||
|
||||
r = sd_netlink_message_read_u32(m, RTA_OIF, &ifi);
|
||||
if (r == -ENODATA) /* Not all routes have an RTA_OIF attribute (for example nexthop ones) */
|
||||
continue;
|
||||
if (r < 0)
|
||||
r = sd_netlink_message_read_u32(m, RTA_PRIORITY, &metric);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
return r;
|
||||
if (ifindex > 0 && (int) ifi != ifindex)
|
||||
continue;
|
||||
|
||||
r = sd_rtnl_message_route_get_family(m, &family);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (af != AF_UNSPEC && af != family)
|
||||
if (!IN_SET(family, AF_INET, AF_INET6))
|
||||
continue;
|
||||
|
||||
if (!GREEDY_REALLOC0(list, n_allocated, n_list + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
a = list + n_list;
|
||||
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
r = sd_netlink_message_read_in_addr(m, RTA_GATEWAY, &a->address.in);
|
||||
if (r < 0)
|
||||
r = sd_netlink_message_read_u32(m, RTA_OIF, &ifi);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
return r;
|
||||
if (r >= 0) {
|
||||
if (ifi <= 0)
|
||||
return -EINVAL;
|
||||
if (ifindex > 0 && (int) ifi != ifindex)
|
||||
continue;
|
||||
|
||||
break;
|
||||
case AF_INET6:
|
||||
r = sd_netlink_message_read_in6_addr(m, RTA_GATEWAY, &a->address.in6);
|
||||
if (r < 0)
|
||||
r = netlink_message_read_in_addr_union(m, RTA_GATEWAY, family, &gateway);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
return r;
|
||||
if (r >= 0) {
|
||||
via.family = family;
|
||||
via.address = gateway;
|
||||
r = add_local_gateway(&list, &n_list, &n_allocated, af, ifi, metric, &via);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (family != AF_INET)
|
||||
continue;
|
||||
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
r = sd_netlink_message_read(m, RTA_VIA, sizeof(via), &via);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
return r;
|
||||
if (r >= 0) {
|
||||
r = add_local_gateway(&list, &n_list, &n_allocated, af, ifi, metric, &via);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
sd_netlink_message_read_u32(m, RTA_PRIORITY, &a->metric);
|
||||
r = sd_netlink_message_read_data(m, RTA_MULTIPATH, &rta_len, &rta_multipath);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
return r;
|
||||
if (r >= 0) {
|
||||
MultipathRoute *mr;
|
||||
|
||||
a->ifindex = ifi;
|
||||
a->family = family;
|
||||
r = rtattr_read_nexthop(rta_multipath, rta_len, family, &multipath_routes);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
n_list++;
|
||||
ORDERED_SET_FOREACH(mr, multipath_routes) {
|
||||
if (ifindex > 0 && mr->ifindex != ifindex)
|
||||
continue;
|
||||
|
||||
r = add_local_gateway(&list, &n_list, &n_allocated, af, ifi, metric, &mr->gateway);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typesafe_qsort(list, n_list, address_compare);
|
||||
|
|
|
@ -103,6 +103,7 @@ int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned shor
|
|||
int sd_netlink_message_close_container(sd_netlink_message *m);
|
||||
|
||||
int sd_netlink_message_read(sd_netlink_message *m, unsigned short type, size_t size, void *data);
|
||||
int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data);
|
||||
int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsigned short type, char **data);
|
||||
int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data);
|
||||
int sd_netlink_message_read_strv(sd_netlink_message *m, unsigned short container_type, unsigned short type_id, char ***ret);
|
||||
|
|
|
@ -10,14 +10,11 @@ IPv4LLRoute=yes
|
|||
|
||||
[Route]
|
||||
Destination=2001:1234:5:8fff:ff:ff:ff:ff/128
|
||||
Scope=link
|
||||
|
||||
[Route]
|
||||
Destination=2001:1234:5:9fff:ff:ff:ff:ff/128
|
||||
Scope=link
|
||||
|
||||
[Route]
|
||||
Destination=::/0
|
||||
Gateway=2001:1234:5:8fff:ff:ff:ff:ff
|
||||
|
||||
[Route]
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
[Match]
|
||||
Name=dummy98
|
||||
|
||||
[Network]
|
||||
IPv6AcceptRA=no
|
||||
Address=2001:1234:5:8f63::1/128
|
||||
Address=149.10.124.58/28
|
||||
|
||||
[Route]
|
||||
Destination=2001:1234:5:8fff:ff:ff:ff:ff/128
|
||||
|
||||
[Route]
|
||||
Destination=149.10.124.66
|
||||
Gateway=2001:1234:5:8fff:ff:ff:ff:ff
|
|
@ -146,6 +146,20 @@ def expectedFailureIfNexthopIsNotAvailable():
|
|||
|
||||
return f
|
||||
|
||||
def expectedFailureIfRTA_VIAIsNotSupported():
|
||||
def f(func):
|
||||
call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
|
||||
call('ip link set up dev dummy98', stderr=subprocess.DEVNULL)
|
||||
call('ip route add 2001:1234:5:8fff:ff:ff:ff:fe/128 dev dummy98', stderr=subprocess.DEVNULL)
|
||||
rc = call('ip route add 10.10.10.10 via inet6 2001:1234:5:8fff:ff:ff:ff:fe dev dummy98', stderr=subprocess.DEVNULL)
|
||||
call('ip link del dummy98', stderr=subprocess.DEVNULL)
|
||||
if rc == 0:
|
||||
return func
|
||||
else:
|
||||
return unittest.expectedFailure(func)
|
||||
|
||||
return f
|
||||
|
||||
def expectedFailureIfAlternativeNameIsNotAvailable():
|
||||
def f(func):
|
||||
call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
|
||||
|
@ -1760,6 +1774,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
|
|||
'25-prefix-route-without-vrf.network',
|
||||
'25-route-ipv6-src.network',
|
||||
'25-route-static.network',
|
||||
'25-route-via-ipv6.network',
|
||||
'25-route-vrf.network',
|
||||
'25-gateway-static.network',
|
||||
'25-gateway-next-static.network',
|
||||
|
@ -2130,10 +2145,11 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
|
|||
self.assertRegex(output, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
|
||||
self.assertRegex(output, '2001:1234:5:8f63::1 proto kernel')
|
||||
|
||||
print('### ip -6 route show dev dummy98 default')
|
||||
output = check_output('ip -6 route show dev dummy98 default')
|
||||
print('### ip -6 route show default')
|
||||
output = check_output('ip -6 route show default')
|
||||
print(output)
|
||||
self.assertRegex(output, 'default via 2001:1234:5:8fff:ff:ff:ff:ff proto static metric 1024 pref medium')
|
||||
self.assertRegex(output, 'default')
|
||||
self.assertRegex(output, 'via 2001:1234:5:8fff:ff:ff:ff:ff')
|
||||
|
||||
print('### ip -4 route show dev dummy98')
|
||||
output = check_output('ip -4 route show dev dummy98')
|
||||
|
@ -2198,6 +2214,27 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
|
|||
self.assertRegex(output, 'via 2001:1234:5:8fff:ff:ff:ff:ff dev dummy98')
|
||||
self.assertRegex(output, 'via 2001:1234:5:9fff:ff:ff:ff:ff dev dummy98')
|
||||
|
||||
@expectedFailureIfRTA_VIAIsNotSupported()
|
||||
def test_route_via_ipv6(self):
|
||||
copy_unit_to_networkd_unit_path('25-route-via-ipv6.network', '12-dummy.netdev')
|
||||
start_networkd()
|
||||
self.wait_online(['dummy98:routable'])
|
||||
|
||||
output = check_output(*networkctl_cmd, '-n', '0', 'status', 'dummy98', env=env)
|
||||
print(output)
|
||||
|
||||
print('### ip -6 route show dev dummy98')
|
||||
output = check_output('ip -6 route show dev dummy98')
|
||||
print(output)
|
||||
self.assertRegex(output, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
|
||||
self.assertRegex(output, '2001:1234:5:8f63::1 proto kernel')
|
||||
|
||||
print('### ip -4 route show dev dummy98')
|
||||
output = check_output('ip -4 route show dev dummy98')
|
||||
print(output)
|
||||
self.assertRegex(output, '149.10.124.48/28 proto kernel scope link src 149.10.124.58')
|
||||
self.assertRegex(output, '149.10.124.66 via inet6 2001:1234:5:8fff:ff:ff:ff:ff proto static')
|
||||
|
||||
@expectedFailureIfModuleIsNotAvailable('vrf')
|
||||
def test_route_vrf(self):
|
||||
copy_unit_to_networkd_unit_path('25-route-vrf.network', '12-dummy.netdev',
|
||||
|
@ -2419,9 +2456,10 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
|
|||
output = check_output('ip -4 route show dev dummy98')
|
||||
print(output)
|
||||
self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
|
||||
output = check_output('ip -6 route show dev dummy98')
|
||||
output = check_output('ip -6 route show default')
|
||||
print(output)
|
||||
self.assertRegex(output, 'default via 2607:5300:203:39ff:ff:ff:ff:ff proto static')
|
||||
self.assertRegex(output, 'default')
|
||||
self.assertRegex(output, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
|
||||
|
||||
check_output('ip link del dummy98')
|
||||
|
||||
|
@ -2442,9 +2480,9 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
|
|||
output = check_output('ip -4 route show dev dummy98')
|
||||
print(output)
|
||||
self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
|
||||
output = check_output('ip -6 route show dev dummy98')
|
||||
output = check_output('ip -6 route show default')
|
||||
print(output)
|
||||
self.assertRegex(output, 'default via 2607:5300:203:39ff:ff:ff:ff:ff proto static')
|
||||
self.assertRegex(output, 'via 2607:5300:203:39ff:ff:ff:ff:ff')
|
||||
|
||||
def test_bind_carrier(self):
|
||||
check_output('ip link add dummy98 type dummy')
|
||||
|
|
Loading…
Reference in New Issue