1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-12-23 17:34:00 +03:00

Merge pull request #13735 from ssahani/ip-nexthop

network: introduce ip nexthop routing
This commit is contained in:
Yu Watanabe 2019-10-14 23:25:23 +09:00 committed by GitHub
commit 3adf85b72f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1038 additions and 12 deletions

View File

@ -1150,6 +1150,29 @@
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[NextHop] Section Options</title>
<para>The <literal>[NextHop]</literal> section accepts the
following keys. Specify several <literal>[NextHop]</literal>
sections to configure several nexthop. Nexthop is used to manipulate entries in the kernel's nexthop
tables.</para>
<variablelist class='network-directives'>
<varlistentry>
<term><varname>Gateway=</varname></term>
<listitem>
<para>As in the <literal>[Network]</literal> section. This is mandatory.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Id=</varname></term>
<listitem>
<para>The id of the nexthop (an unsigned integer). If unspecified or '0' then automatically chosen by kernel.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>

56
src/basic/linux/nexthop.h Normal file
View File

@ -0,0 +1,56 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#ifndef _LINUX_NEXTHOP_H
#define _LINUX_NEXTHOP_H
#include <linux/types.h>
struct nhmsg {
unsigned char nh_family;
unsigned char nh_scope; /* return only */
unsigned char nh_protocol; /* Routing protocol that installed nh */
unsigned char resvd;
unsigned int nh_flags; /* RTNH_F flags */
};
/* entry in a nexthop group */
struct nexthop_grp {
__u32 id; /* nexthop id - must exist */
__u8 weight; /* weight of this nexthop */
__u8 resvd1;
__u16 resvd2;
};
enum {
NEXTHOP_GRP_TYPE_MPATH, /* default type if not specified */
__NEXTHOP_GRP_TYPE_MAX,
};
#define NEXTHOP_GRP_TYPE_MAX (__NEXTHOP_GRP_TYPE_MAX - 1)
enum {
NHA_UNSPEC,
NHA_ID, /* u32; id for nexthop. id == 0 means auto-assign */
NHA_GROUP, /* array of nexthop_grp */
NHA_GROUP_TYPE, /* u16 one of NEXTHOP_GRP_TYPE */
/* if NHA_GROUP attribute is added, no other attributes can be set */
NHA_BLACKHOLE, /* flag; nexthop used to blackhole packets */
/* if NHA_BLACKHOLE is added, OIF, GATEWAY, ENCAP can not be set */
NHA_OIF, /* u32; nexthop device */
NHA_GATEWAY, /* be32 (IPv4) or in6_addr (IPv6) gw address */
NHA_ENCAP_TYPE, /* u16; lwt encap type */
NHA_ENCAP, /* lwt encap data */
/* NHA_OIF can be appended to dump request to return only
* nexthops using given device
*/
NHA_GROUPS, /* flag; only return nexthop groups in dump */
NHA_MASTER, /* u32; only return nexthops with given master dev */
__NHA_MAX,
};
#define NHA_MAX (__NHA_MAX - 1)
#endif

View File

@ -157,6 +157,13 @@ enum {
RTM_GETCHAIN,
#define RTM_GETCHAIN RTM_GETCHAIN
RTM_NEWNEXTHOP = 104,
#define RTM_NEWNEXTHOP RTM_NEWNEXTHOP
RTM_DELNEXTHOP,
#define RTM_DELNEXTHOP RTM_DELNEXTHOP
RTM_GETNEXTHOP,
#define RTM_GETNEXTHOP RTM_GETNEXTHOP
__RTM_MAX,
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
};
@ -165,7 +172,7 @@ enum {
#define RTM_NR_FAMILIES (RTM_NR_MSGTYPES >> 2)
#define RTM_FAM(cmd) (((cmd) - RTM_BASE) >> 2)
/*
/*
Generic structure for encapsulation of optional route information.
It is reminiscent of sockaddr, but with sa_family replaced
with attribute type.
@ -205,7 +212,7 @@ struct rtmsg {
unsigned char rtm_table; /* Routing table id */
unsigned char rtm_protocol; /* Routing protocol; see below */
unsigned char rtm_scope; /* See below */
unsigned char rtm_scope; /* See below */
unsigned char rtm_type; /* See below */
unsigned rtm_flags;
@ -342,6 +349,7 @@ enum rtattr_type_t {
RTA_IP_PROTO,
RTA_SPORT,
RTA_DPORT,
RTA_NH_ID,
__RTA_MAX
};
@ -515,7 +523,7 @@ struct ifinfomsg {
};
/********************************************************************
* prefix information
* prefix information
****/
struct prefixmsg {
@ -529,7 +537,7 @@ struct prefixmsg {
unsigned char prefix_pad3;
};
enum
enum
{
PREFIX_UNSPEC,
PREFIX_ADDRESS,
@ -704,6 +712,8 @@ enum rtnetlink_groups {
#define RTNLGRP_IPV4_MROUTE_R RTNLGRP_IPV4_MROUTE_R
RTNLGRP_IPV6_MROUTE_R,
#define RTNLGRP_IPV6_MROUTE_R RTNLGRP_IPV6_MROUTE_R
RTNLGRP_NEXTHOP,
#define RTNLGRP_NEXTHOP RTNLGRP_NEXTHOP
__RTNLGRP_MAX
};
#define RTNLGRP_MAX (__RTNLGRP_MAX - 1)

View File

@ -88,7 +88,8 @@ int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) {
assert_return(m, -EINVAL);
assert_return(m->hdr, -EINVAL);
assert_return(IN_SET(m->hdr->nlmsg_type, RTM_GETLINK, RTM_GETADDR, RTM_GETROUTE, RTM_GETNEIGH, RTM_GETRULE, RTM_GETADDRLABEL), -EINVAL);
assert_return(IN_SET(m->hdr->nlmsg_type, RTM_GETLINK, RTM_GETADDR, RTM_GETROUTE, RTM_GETNEIGH,
RTM_GETRULE, RTM_GETADDRLABEL, RTM_GETNEXTHOP), -EINVAL);
SET_FLAG(m->hdr->nlmsg_flags, NLM_F_DUMP, dump);

View File

@ -18,6 +18,7 @@
#include <linux/if_link.h>
#include <linux/if_macsec.h>
#include <linux/if_tunnel.h>
#include <linux/nexthop.h>
#include <linux/l2tp.h>
#include <linux/veth.h>
#include <linux/wireguard.h>
@ -716,6 +717,17 @@ static const NLTypeSystem rtnl_routing_policy_rule_type_system = {
.types = rtnl_routing_policy_rule_types,
};
static const NLType rtnl_nexthop_types[] = {
[NHA_ID] = { .type = NETLINK_TYPE_U32 },
[NHA_OIF] = { .type = NETLINK_TYPE_U32 },
[NHA_GATEWAY] = { .type = NETLINK_TYPE_IN_ADDR },
};
static const NLTypeSystem rtnl_nexthop_type_system = {
.count = ELEMENTSOF(rtnl_nexthop_types),
.types = rtnl_nexthop_types,
};
static const NLType rtnl_types[] = {
[NLMSG_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 },
[NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = sizeof(struct nlmsgerr) },
@ -738,6 +750,9 @@ static const NLType rtnl_types[] = {
[RTM_NEWRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) },
[RTM_DELRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) },
[RTM_GETRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) },
[RTM_NEWNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
[RTM_DELNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
[RTM_GETNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
};
const NLTypeSystem rtnl_type_system_root = {

View File

@ -1,6 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include <linux/rtnetlink.h>
#include "sd-netlink.h"
#include "in-addr-util.h"
@ -19,6 +21,10 @@ static inline bool rtnl_message_type_is_route(uint16_t type) {
return IN_SET(type, RTM_NEWROUTE, RTM_GETROUTE, RTM_DELROUTE);
}
static inline bool rtnl_message_type_is_nexthop(uint16_t type) {
return IN_SET(type, RTM_NEWNEXTHOP, RTM_GETNEXTHOP, RTM_DELNEXTHOP);
}
static inline bool rtnl_message_type_is_link(uint16_t type) {
return IN_SET(type, RTM_NEWLINK, RTM_SETLINK, RTM_GETLINK, RTM_DELLINK);
}

View File

@ -2,6 +2,7 @@
#include <netinet/in.h>
#include <linux/if_addrlabel.h>
#include <linux/nexthop.h>
#include <stdbool.h>
#include <unistd.h>
@ -285,6 +286,70 @@ int sd_rtnl_message_new_route(sd_netlink *rtnl, sd_netlink_message **ret,
return 0;
}
int sd_rtnl_message_new_nexthop(sd_netlink *rtnl, sd_netlink_message **ret,
uint16_t nhmsg_type, int nh_family,
unsigned char nh_protocol) {
struct nhmsg *nhm;
int r;
assert_return(rtnl_message_type_is_nexthop(nhmsg_type), -EINVAL);
assert_return((nhmsg_type == RTM_GETNEXTHOP && nh_family == AF_UNSPEC) ||
IN_SET(nh_family, AF_INET, AF_INET6), -EINVAL);
assert_return(ret, -EINVAL);
r = message_new(rtnl, ret, nhmsg_type);
if (r < 0)
return r;
if (nhmsg_type == RTM_NEWNEXTHOP)
(*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND;
nhm = NLMSG_DATA((*ret)->hdr);
nhm->nh_family = nh_family;
nhm->nh_scope = RT_SCOPE_UNIVERSE;
nhm->nh_protocol = nh_protocol;
return 0;
}
int sd_rtnl_message_nexthop_set_flags(sd_netlink_message *m, uint8_t flags) {
struct nhmsg *nhm;
assert_return(m, -EINVAL);
assert_return(m->hdr, -EINVAL);
assert_return(rtnl_message_type_is_nexthop(m->hdr->nlmsg_type), -EINVAL);
nhm = NLMSG_DATA(m->hdr);
nhm->nh_flags |= flags;
return 0;
}
int sd_rtnl_message_nexthop_set_family(sd_netlink_message *m, uint8_t family) {
struct nhmsg *nhm;
assert_return(m, -EINVAL);
assert_return(m->hdr, -EINVAL);
nhm = NLMSG_DATA(m->hdr);
nhm->nh_family = family;
return 0;
}
int sd_rtnl_message_nexthop_get_family(sd_netlink_message *m, uint8_t *family) {
struct nhmsg *nhm;
assert_return(m, -EINVAL);
assert_return(m->hdr, -EINVAL);
nhm = NLMSG_DATA(m->hdr);
*family = nhm->nh_family ;
return 0;
}
int sd_rtnl_message_neigh_set_flags(sd_netlink_message *m, uint8_t flags) {
struct ndmsg *ndm;
@ -713,6 +778,14 @@ int sd_rtnl_message_get_family(sd_netlink_message *m, int *family) {
*family = rtm->rtm_family;
return 0;
} else if (rtnl_message_type_is_nexthop(m->hdr->nlmsg_type)) {
struct nhmsg *nhm;
nhm = NLMSG_DATA(m->hdr);
*family = nhm->nh_family;
return 0;
}

View File

@ -896,6 +896,13 @@ int sd_netlink_add_match(
if (r < 0)
return r;
break;
case RTM_NEWNEXTHOP:
case RTM_DELNEXTHOP:
r = socket_broadcast_group_ref(rtnl, RTNLGRP_NEXTHOP);
if (r < 0)
return r;
break;
default:
return -EOPNOTSUPP;
}

View File

@ -93,6 +93,8 @@ sources = files('''
networkd-network-bus.h
networkd-network.c
networkd-network.h
networkd-nexthop.c
networkd-nexthop.h
networkd-route.c
networkd-route.h
networkd-routing-policy-rule.c

View File

@ -672,6 +672,9 @@ static Link *link_free(Link *link) {
link->routes = set_free_with_destructor(link->routes, route_free);
link->routes_foreign = set_free_with_destructor(link->routes_foreign, route_free);
link->nexthops = set_free_with_destructor(link->nexthops, nexthop_free);
link->nexthops_foreign = set_free_with_destructor(link->nexthops_foreign, nexthop_free);
link->neighbors = set_free_with_destructor(link->neighbors, neighbor_free);
link->neighbors_foreign = set_free_with_destructor(link->neighbors_foreign, neighbor_free);
@ -903,6 +906,58 @@ static int link_request_set_routing_policy_rule(Link *link) {
return 0;
}
static int nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(link);
assert(link->nexthop_messages > 0);
assert(IN_SET(link->state, LINK_STATE_CONFIGURING,
LINK_STATE_FAILED, LINK_STATE_LINGER));
link->nexthop_messages--;
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 1;
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
log_link_warning_errno(link, r, "Could not set nexthop: %m");
link_enter_failed(link);
return 1;
}
if (link->nexthop_messages == 0) {
log_link_debug(link, "Nexthop set");
link->static_nexthops_configured = true;
link_check_ready(link);
}
return 1;
}
int link_request_set_nexthop(Link *link) {
NextHop *nh;
int r;
LIST_FOREACH(nexthops, nh, link->network->static_nexthops) {
r = nexthop_configure(nh, link, nexthop_handler);
if (r < 0)
return log_link_warning_errno(link, r, "Could not set nexthop: %m");
if (r > 0)
link->nexthop_messages++;
}
if (link->nexthop_messages == 0) {
link->static_nexthops_configured = true;
link_check_ready(link);
} else {
log_link_debug(link, "Setting nexthop");
link_set_state(link, LINK_STATE_CONFIGURING);
}
return 1;
}
static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
@ -948,6 +1003,7 @@ int link_request_set_routes(Link *link) {
assert(link->state != _LINK_STATE_INVALID);
link->static_routes_configured = false;
link->static_routes_ready = false;
if (!link_has_carrier(link) && !link->network->configure_without_carrier)
/* During configuring addresses, the link lost its carrier. As networkd is dropping
@ -1017,6 +1073,17 @@ void link_check_ready(Link *link) {
if (!link->static_routes_configured)
return;
if (!link->static_routes_ready) {
link->static_routes_ready = true;
r = link_request_set_nexthop(link);
if (r < 0)
link_enter_failed(link);
return;
}
if (!link->static_nexthops_configured)
return;
if (!link->routing_policy_rules_configured)
return;
@ -1134,6 +1201,8 @@ static int link_request_set_addresses(Link *link) {
link->addresses_ready = false;
link->neighbors_configured = false;
link->static_routes_configured = false;
link->static_routes_ready = false;
link->static_nexthops_configured = false;
link->routing_policy_rules_configured = false;
r = link_set_bridge_fdb(link);

View File

@ -69,6 +69,7 @@ typedef struct Link {
unsigned address_label_messages;
unsigned neighbor_messages;
unsigned route_messages;
unsigned nexthop_messages;
unsigned routing_policy_rule_messages;
unsigned routing_policy_rule_remove_messages;
unsigned enslaving;
@ -79,9 +80,8 @@ typedef struct Link {
Set *neighbors_foreign;
Set *routes;
Set *routes_foreign;
bool addresses_configured;
bool addresses_ready;
Set *nexthops;
Set *nexthops_foreign;
sd_dhcp_client *dhcp_client;
sd_dhcp_lease *dhcp_lease, *dhcp_lease_old;
@ -100,8 +100,12 @@ typedef struct Link {
sd_ipv4ll *ipv4ll;
bool ipv4ll_address:1;
bool addresses_configured:1;
bool addresses_ready:1;
bool neighbors_configured:1;
bool static_routes_configured:1;
bool static_routes_ready:1;
bool static_nexthops_configured:1;
bool routing_policy_rules_configured:1;
bool setting_mtu:1;
@ -198,6 +202,7 @@ uint32_t link_get_vrf_table(Link *link);
uint32_t link_get_dhcp_route_table(Link *link);
uint32_t link_get_ipv6_accept_ra_route_table(Link *link);
int link_request_set_routes(Link *link);
int link_request_set_nexthop(Link *link);
#define ADDRESS_FMT_VAL(address) \
be32toh((address).s_addr) >> 24, \

View File

@ -5,6 +5,7 @@
#include <unistd.h>
#include <linux/if.h>
#include <linux/fib_rules.h>
#include <linux/nexthop.h>
#include "sd-daemon.h"
#include "sd-netlink.h"
@ -1153,6 +1154,118 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi
return 1;
}
int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
_cleanup_(nexthop_freep) NextHop *tmp = NULL;
_cleanup_free_ char *gateway = NULL;
NextHop *nexthop = NULL;
Manager *m = userdata;
Link *link = NULL;
uint16_t type;
int r;
assert(rtnl);
assert(message);
assert(m);
if (sd_netlink_message_is_error(message)) {
r = sd_netlink_message_get_errno(message);
if (r < 0)
log_warning_errno(r, "rtnl: failed to receive rule message, ignoring: %m");
return 0;
}
r = sd_netlink_message_get_type(message, &type);
if (r < 0) {
log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
return 0;
} else if (!IN_SET(type, RTM_NEWNEXTHOP, RTM_DELNEXTHOP)) {
log_warning("rtnl: received unexpected message type %u when processing nexthop, ignoring.", type);
return 0;
}
r = nexthop_new(&tmp);
if (r < 0)
return log_oom();
r = sd_rtnl_message_get_family(message, &tmp->family);
if (r < 0) {
log_warning_errno(r, "rtnl: could not get nexthop family, ignoring: %m");
return 0;
} else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
log_debug("rtnl: received nexthop message with invalid family %d, ignoring.", tmp->family);
return 0;
}
switch (tmp->family) {
case AF_INET:
r = sd_netlink_message_read_in_addr(message, NHA_GATEWAY, &tmp->gw.in);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
return 0;
}
break;
case AF_INET6:
r = sd_netlink_message_read_in6_addr(message, NHA_GATEWAY, &tmp->gw.in6);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
return 0;
}
break;
default:
assert_not_reached("Received rule message with unsupported address family");
}
r = sd_netlink_message_read_u32(message, NHA_ID, &tmp->id);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get NHA_ID attribute, ignoring: %m");
return 0;
}
r = sd_netlink_message_read_u32(message, NHA_OIF, &tmp->oif);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m");
return 0;
}
r = link_get(m, tmp->oif, &link);
if (r < 0 || !link) {
if (!m->enumerating)
log_warning("rtnl: received nexthop message for link (%d) we do not know about, ignoring", tmp->oif);
return 0;
}
(void) nexthop_get(link, tmp, &nexthop);
if (DEBUG_LOGGING)
(void) in_addr_to_string(tmp->family, &tmp->gw, &gateway);
switch (type) {
case RTM_NEWNEXTHOP:
if (!nexthop) {
log_debug("Remembering foreign nexthop: %s, oif: %d, id: %d", gateway, tmp->oif, tmp->id);
r = nexthop_add_foreign(link, tmp, &nexthop);
if (r < 0) {
log_warning_errno(r, "Could not remember foreign nexthop, ignoring: %m");
return 0;
}
}
break;
case RTM_DELNEXTHOP:
log_debug("Forgetting foreign nexthop: %s, oif: %d, id: %d", gateway, tmp->oif, tmp->id);
nexthop_free(nexthop);
break;
default:
assert_not_reached("Received invalid RTNL message type");
}
return 1;
}
static int systemd_netlink_fd(void) {
int n, fd, rtnl_fd = -EINVAL;
@ -1253,6 +1366,14 @@ static int manager_connect_rtnl(Manager *m) {
if (r < 0)
return r;
r = sd_netlink_add_match(m->rtnl, NULL, RTM_NEWNEXTHOP, &manager_rtnl_process_nexthop, NULL, m, "network-rtnl_process_nexthop");
if (r < 0)
return r;
r = sd_netlink_add_match(m->rtnl, NULL, RTM_DELNEXTHOP, &manager_rtnl_process_nexthop, NULL, m, "network-rtnl_process_nexthop");
if (r < 0)
return r;
return 0;
}
@ -1931,6 +2052,47 @@ int manager_rtnl_enumerate_rules(Manager *m) {
return r;
}
int manager_rtnl_enumerate_nexthop(Manager *m) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
sd_netlink_message *nexthop;
int r;
assert(m);
assert(m->rtnl);
r = sd_rtnl_message_new_nexthop(m->rtnl, &req, RTM_GETNEXTHOP, 0, 0);
if (r < 0)
return r;
r = sd_netlink_message_request_dump(req, true);
if (r < 0)
return r;
r = sd_netlink_call(m->rtnl, req, 0, &reply);
if (r < 0) {
if (r == -EOPNOTSUPP) {
log_debug("Nexthop are not supported by the kernel. Ignoring.");
return 0;
}
return r;
}
for (nexthop = reply; nexthop; nexthop = sd_netlink_message_next(nexthop)) {
int k;
m->enumerating = true;
k = manager_rtnl_process_nexthop(m->rtnl, nexthop, m);
if (k < 0)
r = k;
m->enumerating = false;
}
return r;
}
int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) {
AddressPool *p;
int r;

View File

@ -83,11 +83,13 @@ int manager_rtnl_enumerate_addresses(Manager *m);
int manager_rtnl_enumerate_neighbors(Manager *m);
int manager_rtnl_enumerate_routes(Manager *m);
int manager_rtnl_enumerate_rules(Manager *m);
int manager_rtnl_enumerate_nexthop(Manager *m);
int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, void *userdata);
int manager_rtnl_process_neighbor(sd_netlink *nl, sd_netlink_message *message, void *userdata);
int manager_rtnl_process_route(sd_netlink *nl, sd_netlink_message *message, void *userdata);
int manager_rtnl_process_rule(sd_netlink *nl, sd_netlink_message *message, void *userdata);
int manager_rtnl_process_nexthop(sd_netlink *nl, sd_netlink_message *message, void *userdata);
void manager_dirty(Manager *m);

View File

@ -141,6 +141,8 @@ Route.InitialAdvertisedReceiveWindow, config_parse_tcp_window,
Route.QuickAck, config_parse_quickack, 0, 0
Route.FastOpenNoCookie, config_parse_fast_open_no_cookie, 0, 0
Route.TTLPropagate, config_parse_route_ttl_propagate, 0, 0
NextHop.Id, config_parse_nexthop_id, 0, 0
NextHop.Gateway, config_parse_nexthop_gateway, 0, 0
DHCPv4.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns)
DHCPv4.RoutesToDNS, config_parse_bool, 0, offsetof(Network, dhcp_routes_to_dns)

View File

@ -151,6 +151,7 @@ int network_verify(Network *network) {
AddressLabel *label, *label_next;
Prefix *prefix, *prefix_next;
RoutingPolicyRule *rule, *rule_next;
NextHop *nexthop, *nextnop_next;
assert(network);
assert(network->filename);
@ -282,6 +283,10 @@ int network_verify(Network *network) {
if (route_section_verify(route, network) < 0)
route_free(route);
LIST_FOREACH_SAFE(nexthops, nexthop, nextnop_next, network->static_nexthops)
if (nexthop_section_verify(nexthop) < 0)
nexthop_free(nexthop);
LIST_FOREACH_SAFE(static_fdb_entries, fdb, fdb_next, network->static_fdb_entries)
if (section_is_invalid(fdb->section))
fdb_entry_free(fdb);
@ -453,8 +458,9 @@ int network_load_one(Manager *manager, const char *filename) {
"IPv6AddressLabel\0"
"RoutingPolicyRule\0"
"Route\0"
"DHCP\0"
"DHCPv4\0" /* compat */
"NextHop\0"
"DHCP\0" /* compat */
"DHCPv4\0"
"DHCPv6\0"
"DHCPServer\0"
"IPv6AcceptRA\0"
@ -525,8 +531,9 @@ static Network *network_free(Network *network) {
FdbEntry *fdb_entry;
Neighbor *neighbor;
AddressLabel *label;
Prefix *prefix;
Address *address;
NextHop *nexthop;
Prefix *prefix;
Route *route;
if (!network)
@ -573,6 +580,9 @@ static Network *network_free(Network *network) {
while ((route = network->static_routes))
route_free(route);
while ((nexthop = network->static_nexthops))
nexthop_free(nexthop);
while ((address = network->static_addresses))
address_free(address);
@ -596,6 +606,7 @@ static Network *network_free(Network *network) {
hashmap_free(network->addresses_by_section);
hashmap_free(network->routes_by_section);
hashmap_free(network->nexthops_by_section);
hashmap_free(network->fdb_entries_by_section);
hashmap_free(network->neighbors_by_section);
hashmap_free(network->address_labels_by_section);

View File

@ -19,6 +19,7 @@
#include "networkd-lldp-rx.h"
#include "networkd-lldp-tx.h"
#include "networkd-neighbor.h"
#include "networkd-nexthop.h"
#include "networkd-radv.h"
#include "networkd-route.h"
#include "networkd-routing-policy-rule.h"
@ -228,6 +229,7 @@ struct Network {
LIST_HEAD(Address, static_addresses);
LIST_HEAD(Route, static_routes);
LIST_HEAD(NextHop, static_nexthops);
LIST_HEAD(FdbEntry, static_fdb_entries);
LIST_HEAD(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses);
LIST_HEAD(Neighbor, neighbors);
@ -238,6 +240,7 @@ struct Network {
unsigned n_static_addresses;
unsigned n_static_routes;
unsigned n_static_nexthops;
unsigned n_static_fdb_entries;
unsigned n_ipv6_proxy_ndp_addresses;
unsigned n_neighbors;
@ -248,6 +251,7 @@ struct Network {
Hashmap *addresses_by_section;
Hashmap *routes_by_section;
Hashmap *nexthops_by_section;
Hashmap *fdb_entries_by_section;
Hashmap *neighbors_by_section;
Hashmap *address_labels_by_section;

View File

@ -0,0 +1,473 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2019 VMware, Inc.
*/
#include <linux/nexthop.h>
#include "alloc-util.h"
#include "conf-parser.h"
#include "in-addr-util.h"
#include "netlink-util.h"
#include "networkd-manager.h"
#include "networkd-nexthop.h"
#include "parse-util.h"
#include "set.h"
#include "string-util.h"
#include "util.h"
int nexthop_new(NextHop **ret) {
_cleanup_(nexthop_freep) NextHop *nexthop = NULL;
nexthop = new(NextHop, 1);
if (!nexthop)
return -ENOMEM;
*nexthop = (NextHop) {
.family = AF_UNSPEC,
};
*ret = TAKE_PTR(nexthop);
return 0;
}
static int nexthop_new_static(Network *network, const char *filename, unsigned section_line, NextHop **ret) {
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
_cleanup_(nexthop_freep) NextHop *nexthop = NULL;
int r;
assert(network);
assert(ret);
assert(!!filename == (section_line > 0));
if (filename) {
r = network_config_section_new(filename, section_line, &n);
if (r < 0)
return r;
nexthop = hashmap_get(network->nexthops_by_section, n);
if (nexthop) {
*ret = TAKE_PTR(nexthop);
return 0;
}
}
r = nexthop_new(&nexthop);
if (r < 0)
return r;
nexthop->protocol = RTPROT_STATIC;
nexthop->network = network;
LIST_PREPEND(nexthops, network->static_nexthops, nexthop);
network->n_static_nexthops++;
if (filename) {
nexthop->section = TAKE_PTR(n);
r = hashmap_ensure_allocated(&network->nexthops_by_section, &network_config_hash_ops);
if (r < 0)
return r;
r = hashmap_put(network->nexthops_by_section, nexthop->section, nexthop);
if (r < 0)
return r;
}
*ret = TAKE_PTR(nexthop);
return 0;
}
void nexthop_free(NextHop *nexthop) {
if (!nexthop)
return;
if (nexthop->network) {
LIST_REMOVE(nexthops, nexthop->network->static_nexthops, nexthop);
assert(nexthop->network->n_static_nexthops > 0);
nexthop->network->n_static_nexthops--;
if (nexthop->section)
hashmap_remove(nexthop->network->nexthops_by_section, nexthop->section);
}
network_config_section_free(nexthop->section);
if (nexthop->link) {
set_remove(nexthop->link->nexthops, nexthop);
set_remove(nexthop->link->nexthops_foreign, nexthop);
}
free(nexthop);
}
static void nexthop_hash_func(const NextHop *nexthop, struct siphash *state) {
assert(nexthop);
siphash24_compress(&nexthop->id, sizeof(nexthop->id), state);
siphash24_compress(&nexthop->oif, sizeof(nexthop->oif), state);
siphash24_compress(&nexthop->family, sizeof(nexthop->family), state);
switch (nexthop->family) {
case AF_INET:
case AF_INET6:
siphash24_compress(&nexthop->gw, FAMILY_ADDRESS_SIZE(nexthop->family), state);
break;
default:
/* treat any other address family as AF_UNSPEC */
break;
}
}
static int nexthop_compare_func(const NextHop *a, const NextHop *b) {
int r;
r = CMP(a->id, b->id);
if (r != 0)
return r;
r = CMP(a->oif, b->oif);
if (r != 0)
return r;
r = CMP(a->family, b->family);
if (r != 0)
return r;
switch (a->family) {
case AF_INET:
case AF_INET6:
r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
if (r != 0)
return r;
return 0;
default:
/* treat any other address family as AF_UNSPEC */
return 0;
}
}
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
nexthop_hash_ops,
NextHop,
nexthop_hash_func,
nexthop_compare_func,
nexthop_free);
bool nexthop_equal(NextHop *r1, NextHop *r2) {
if (r1 == r2)
return true;
if (!r1 || !r2)
return false;
return nexthop_compare_func(r1, r2) == 0;
}
int nexthop_get(Link *link, NextHop *in, NextHop **ret) {
NextHop *existing;
assert(link);
assert(in);
existing = set_get(link->nexthops, in);
if (existing) {
if (ret)
*ret = existing;
return 1;
}
existing = set_get(link->nexthops_foreign, in);
if (existing) {
if (ret)
*ret = existing;
return 0;
}
return -ENOENT;
}
static int nexthop_add_internal(Link *link, Set **nexthops, NextHop *in, NextHop **ret) {
_cleanup_(nexthop_freep) NextHop *nexthop = NULL;
int r;
assert(link);
assert(nexthops);
assert(in);
r = nexthop_new(&nexthop);
if (r < 0)
return r;
nexthop->id = in->id;
nexthop->oif = in->oif;
nexthop->family = in->family;
nexthop->gw = in->gw;
r = set_ensure_allocated(nexthops, &nexthop_hash_ops);
if (r < 0)
return r;
r = set_put(*nexthops, nexthop);
if (r < 0)
return r;
if (r == 0)
return -EEXIST;
nexthop->link = link;
if (ret)
*ret = nexthop;
nexthop = NULL;
return 0;
}
int nexthop_add_foreign(Link *link, NextHop *in, NextHop **ret) {
return nexthop_add_internal(link, &link->nexthops_foreign, in, ret);
}
int nexthop_add(Link *link, NextHop *in, NextHop **ret) {
NextHop *nexthop;
int r;
r = nexthop_get(link, in, &nexthop);
if (r == -ENOENT) {
/* NextHop does not exist, create a new one */
r = nexthop_add_internal(link, &link->nexthops, in, &nexthop);
if (r < 0)
return r;
} else if (r == 0) {
/* Take over a foreign nexthop */
r = set_ensure_allocated(&link->nexthops, &nexthop_hash_ops);
if (r < 0)
return r;
r = set_put(link->nexthops, nexthop);
if (r < 0)
return r;
set_remove(link->nexthops_foreign, nexthop);
} else if (r == 1) {
/* NextHop exists, do nothing */
;
} else
return r;
if (ret)
*ret = nexthop;
return 0;
}
static int nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(m);
assert(link);
assert(link->ifname);
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 1;
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -ESRCH)
log_link_warning_errno(link, r, "Could not drop nexthop: %m");
return 1;
}
int nexthop_remove(NextHop *nexthop, 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(IN_SET(nexthop->family, AF_INET, AF_INET6));
r = sd_rtnl_message_new_nexthop(link->manager->rtnl, &req,
RTM_DELNEXTHOP, nexthop->family,
nexthop->protocol);
if (r < 0)
return log_link_error_errno(link, r, "Could not create RTM_DELNEXTHOP message: %m");
if (DEBUG_LOGGING) {
_cleanup_free_ char *gw = NULL;
if (!in_addr_is_null(nexthop->family, &nexthop->gw))
(void) in_addr_to_string(nexthop->family, &nexthop->gw, &gw);
log_link_debug(link, "Removing nexthop: gw: %s", strna(gw));
}
if (in_addr_is_null(nexthop->family, &nexthop->gw) == 0) {
r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, nexthop->family, &nexthop->gw);
if (r < 0)
return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
}
r = netlink_call_async(link->manager->rtnl, NULL, req,
callback ?: nexthop_remove_handler,
link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
link_ref(link);
return 0;
}
int nexthop_configure(
NextHop *nexthop,
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(IN_SET(nexthop->family, AF_INET, AF_INET6));
assert(callback);
if (DEBUG_LOGGING) {
_cleanup_free_ char *gw = NULL;
if (!in_addr_is_null(nexthop->family, &nexthop->gw))
(void) in_addr_to_string(nexthop->family, &nexthop->gw, &gw);
log_link_debug(link, "Configuring nexthop: gw: %s", strna(gw));
}
r = sd_rtnl_message_new_nexthop(link->manager->rtnl, &req,
RTM_NEWNEXTHOP, nexthop->family,
nexthop->protocol);
if (r < 0)
return log_link_error_errno(link, r, "Could not create RTM_NEWNEXTHOP message: %m");
r = sd_netlink_message_append_u32(req, NHA_ID, nexthop->id);
if (r < 0)
return log_link_error_errno(link, r, "Could not append NHA_ID attribute: %m");
r = sd_netlink_message_append_u32(req, NHA_OIF, link->ifindex);
if (r < 0)
return log_link_error_errno(link, r, "Could not append NHA_OIF attribute: %m");
if (in_addr_is_null(nexthop->family, &nexthop->gw) == 0) {
r = netlink_message_append_in_addr_union(req, NHA_GATEWAY, nexthop->family, &nexthop->gw);
if (r < 0)
return log_link_error_errno(link, r, "Could not append NHA_GATEWAY attribute: %m");
r = sd_rtnl_message_nexthop_set_family(req, nexthop->family);
if (r < 0)
return log_link_error_errno(link, r, "Could not set nexthop family: %m");
}
r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
link_ref(link);
r = nexthop_add(link, nexthop, &nexthop);
if (r < 0)
return log_link_error_errno(link, r, "Could not add nexthop: %m");
return 1;
}
int nexthop_section_verify(NextHop *nh) {
if (section_is_invalid(nh->section))
return -EINVAL;
if (in_addr_is_null(nh->family, &nh->gw) < 0)
return -EINVAL;
return 0;
}
int config_parse_nexthop_id(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL;
Network *network = userdata;
int r;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
assert(data);
r = nexthop_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
r = safe_atou32(rvalue, &n->id);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Could not parse nexthop id \"%s\", ignoring assignment: %m", rvalue);
return 0;
}
TAKE_PTR(n);
return 0;
}
int config_parse_nexthop_gateway(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL;
Network *network = userdata;
int r;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
assert(data);
r = nexthop_new_static(network, filename, section_line, &n);
if (r < 0)
return r;
r = in_addr_from_string_auto(rvalue, &n->family, &n->gw);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
return 0;
}
TAKE_PTR(n);
return 0;
}

View File

@ -0,0 +1,50 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2019 VMware, Inc.
*/
#pragma once
#include "conf-parser.h"
#include "macro.h"
typedef struct NextHop NextHop;
typedef struct NetworkConfigSection NetworkConfigSection;
#include "networkd-network.h"
#include "networkd-util.h"
struct NextHop {
Network *network;
NetworkConfigSection *section;
Link *link;
unsigned char protocol;
int family;
uint32_t oif;
uint32_t id;
union in_addr_union gw;
LIST_FIELDS(NextHop, nexthops);
};
extern const struct hash_ops nexthop_hash_ops;
int nexthop_new(NextHop **ret);
void nexthop_free(NextHop *nexthop);
int nexthop_configure(NextHop *nexthop, Link *link, link_netlink_message_handler_t callback);
int nexthop_remove(NextHop *nexthop, Link *link, link_netlink_message_handler_t callback);
int nexthop_get(Link *link, NextHop *in, NextHop **ret);
int nexthop_add(Link *link, NextHop *in, NextHop **ret);
int nexthop_add_foreign(Link *link, NextHop *in, NextHop **ret);
bool nexthop_equal(NextHop *r1, NextHop *r2);
int nexthop_section_verify(NextHop *nexthop);
DEFINE_NETWORK_SECTION_FUNCTIONS(NextHop, nexthop_free);
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_id);
CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_gateway);

View File

@ -107,6 +107,10 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return log_error_errno(r, "Could not enumerate rules: %m");
r = manager_rtnl_enumerate_nexthop(m);
if (r < 0)
return log_error_errno(r, "Could not enumerate nexthop: %m");
r = manager_start(m);
if (r < 0)
return log_error_errno(r, "Could not start manager: %m");

View File

@ -167,6 +167,12 @@ int sd_rtnl_message_route_get_dst_prefixlen(sd_netlink_message *m, unsigned char
int sd_rtnl_message_route_get_src_prefixlen(sd_netlink_message *m, unsigned char *src_len);
int sd_rtnl_message_route_get_type(sd_netlink_message *m, unsigned char *type);
int sd_rtnl_message_new_nexthop(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nhmsg_type, int nh_family, unsigned char nh_protocol);
int sd_rtnl_message_nexthop_set_flags(sd_netlink_message *m, uint8_t flags);
int sd_rtnl_message_nexthop_set_family(sd_netlink_message *m, uint8_t family);
int sd_rtnl_message_nexthop_get_family(sd_netlink_message *m, uint8_t *family);
int sd_rtnl_message_neigh_set_flags(sd_netlink_message *m, uint8_t flags);
int sd_rtnl_message_neigh_set_state(sd_netlink_message *m, uint16_t state);
int sd_rtnl_message_neigh_get_family(sd_netlink_message *m, int *family);

View File

@ -255,3 +255,6 @@ MaxLeaseTimeSec=
DefaultLeaseTimeSec=
EmitTimezone=
DNS=
[NextHop]
Id=
Gateway=

View File

@ -0,0 +1,11 @@
[Match]
Name=veth99
[Network]
IPv6AcceptRA=no
Address=192.168.5.10/24
Gateway=192.168.5.1
[NextHop]
Id=1
Gateway=192.168.5.1

View File

@ -0,0 +1,7 @@
[Match]
Name=veth-peer
[Network]
IPv6AcceptRA=no
Address=2600::1/0
Address=192.168.5.1/24

View File

@ -113,6 +113,16 @@ def expectedFailureIfLinkFileFieldIsNotSet():
return f
def expectedFailureIfNexthopIsNotAvailable():
def f(func):
rc = call('ip nexthop list')
if rc == 0:
return func
else:
return unittest.expectedFailure(func)
return f
def setUpModule():
global running_units
@ -1402,7 +1412,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
'dummy99',
'gretun97',
'ip6gretun97',
'test1'
'test1',
'veth99',
]
units = [
@ -1426,6 +1437,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
'25-neighbor-ipv6.network',
'25-neighbor-ip-dummy.network',
'25-neighbor-ip.network',
'25-nexthop.network',
'25-link-local-addressing-no.network',
'25-link-local-addressing-yes.network',
'25-link-section-unmanaged.network',
@ -1435,6 +1447,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
'25-gateway-next-static.network',
'25-sysctl-disable-ipv6.network',
'25-sysctl.network',
'25-veth-peer.network',
'25-veth.netdev',
'26-link-local-addressing-ipv6.network',
'configure-without-carrier.network',
'routing-policy-rule-dummy98.network',
@ -1974,6 +1988,16 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98')
self.assertNotRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98')
@expectedFailureIfNexthopIsNotAvailable()
def test_nexthop(self):
copy_unit_to_networkd_unit_path('25-nexthop.network', '25-veth.netdev', '25-veth-peer.network')
start_networkd()
self.wait_online(['veth99:routable', 'veth-peer:routable'])
output = check_output('ip nexthop list dev veth99')
print(output)
self.assertRegex(output, '192.168.5.1')
class NetworkdStateFileTests(unittest.TestCase, Utilities):
links = [
'dummy98',