Merge pull request #34331 from yuwata/network-netdev-cleanups
network/netdev: several cleanups for attaching/detaching netdev, and setting/getting ifindex from netdev
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/l2tp.h>
|
||||
#include <linux/genetlink.h>
|
||||
|
||||
@@ -752,28 +753,40 @@ static void l2tp_tunnel_init(NetDev *netdev) {
|
||||
t->udp6_csum_tx = true;
|
||||
}
|
||||
|
||||
static int l2tp_session_verify(L2tpSession *session) {
|
||||
NetDev *netdev;
|
||||
#define log_session(session, fmt, ...) \
|
||||
({ \
|
||||
const L2tpSession *_session = (session); \
|
||||
log_section_warning_errno( \
|
||||
_session ? _session->section : NULL, \
|
||||
SYNTHETIC_ERRNO(EINVAL), \
|
||||
fmt " Ignoring [L2TPSession] section.", \
|
||||
##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
static int l2tp_session_verify(L2tpSession *session, Set **names) {
|
||||
int r;
|
||||
|
||||
assert(session);
|
||||
assert(session->tunnel);
|
||||
|
||||
netdev = NETDEV(session->tunnel);
|
||||
assert(names);
|
||||
|
||||
if (section_is_invalid(session->section))
|
||||
return -EINVAL;
|
||||
|
||||
if (!session->name)
|
||||
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: L2TP session without name configured. "
|
||||
"Ignoring [L2TPSession] section from line %u",
|
||||
session->section->filename, session->section->line);
|
||||
return log_session(session, "L2TP session without name configured.");
|
||||
|
||||
if (session->session_id == 0 || session->peer_session_id == 0)
|
||||
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: L2TP session without session IDs configured. "
|
||||
"Ignoring [L2TPSession] section from line %u",
|
||||
session->section->filename, session->section->line);
|
||||
return log_session(session, "L2TP session without session IDs configured.");
|
||||
|
||||
if (streq(session->name, NETDEV(session->tunnel)->ifname))
|
||||
return log_session(session, "L2TP session name %s cannot be the same as the netdev name.", session->name);
|
||||
|
||||
r = set_ensure_put(names, &string_hash_ops, session->name);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
if (r == 0)
|
||||
return log_session(session, "L2TP session name %s is duplicated.", session->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -785,27 +798,100 @@ static int netdev_l2tp_tunnel_verify(NetDev *netdev, const char *filename) {
|
||||
L2tpSession *session;
|
||||
|
||||
if (!IN_SET(t->family, AF_INET, AF_INET6))
|
||||
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: L2TP tunnel with invalid address family configured. Ignoring",
|
||||
filename);
|
||||
return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: L2TP tunnel with invalid address family configured. Ignoring",
|
||||
filename);
|
||||
|
||||
if (!in_addr_is_set(t->family, &t->remote))
|
||||
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: L2TP tunnel without a remote address configured. Ignoring",
|
||||
filename);
|
||||
return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: L2TP tunnel without a remote address configured. Ignoring",
|
||||
filename);
|
||||
|
||||
if (t->tunnel_id == 0 || t->peer_tunnel_id == 0)
|
||||
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: L2TP tunnel without tunnel IDs configured. Ignoring",
|
||||
filename);
|
||||
return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"%s: L2TP tunnel without tunnel IDs configured. Ignoring",
|
||||
filename);
|
||||
|
||||
_cleanup_set_free_ Set *names = NULL;
|
||||
ORDERED_HASHMAP_FOREACH(session, t->sessions_by_section)
|
||||
if (l2tp_session_verify(session) < 0)
|
||||
if (l2tp_session_verify(session, &names) < 0)
|
||||
l2tp_session_free(session);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netdev_l2tp_tunnel_attach(NetDev *netdev) {
|
||||
L2tpTunnel *t = L2TP(netdev);
|
||||
L2tpSession *session;
|
||||
int r;
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(session, t->sessions_by_section) {
|
||||
assert(session->name);
|
||||
|
||||
r = netdev_attach_name(netdev, session->name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void netdev_l2tp_tunnel_detach(NetDev *netdev) {
|
||||
L2tpTunnel *t = L2TP(netdev);
|
||||
L2tpSession *session;
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(session, t->sessions_by_section)
|
||||
netdev_detach_name(netdev, session->name);
|
||||
}
|
||||
|
||||
static int netdev_l2tp_tunnel_set_ifindex(NetDev *netdev, const char *name, int ifindex) {
|
||||
L2tpTunnel *t = L2TP(netdev);
|
||||
L2tpSession *session;
|
||||
bool found = false;
|
||||
|
||||
assert(name);
|
||||
assert(ifindex > 0);
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(session, t->sessions_by_section)
|
||||
if (streq(session->name, name)) {
|
||||
if (session->ifindex == ifindex)
|
||||
return 0; /* already set. */
|
||||
if (session->ifindex > 0 && session->ifindex != ifindex)
|
||||
return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EEXIST),
|
||||
"Could not set ifindex %i for session %s, already set to %i.",
|
||||
ifindex, session->name, session->ifindex);
|
||||
|
||||
session->ifindex = ifindex;
|
||||
log_netdev_debug(netdev, "Session %s gained ifindex %i.", session->name, session->ifindex);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received netlink message with unexpected interface name %s (ifindex=%i).",
|
||||
name, ifindex);
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(session, t->sessions_by_section)
|
||||
if (session->ifindex <= 0)
|
||||
return 0; /* This session is not ready yet. */
|
||||
|
||||
return netdev_enter_ready(netdev);
|
||||
}
|
||||
|
||||
static int netdev_l2tp_tunnel_get_ifindex(NetDev *netdev, const char *name) {
|
||||
L2tpTunnel *t = L2TP(netdev);
|
||||
L2tpSession *session;
|
||||
|
||||
assert(name);
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(session, t->sessions_by_section)
|
||||
if (streq(session->name, name))
|
||||
return session->ifindex;
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void l2tp_tunnel_done(NetDev *netdev) {
|
||||
L2tpTunnel *t = L2TP(netdev);
|
||||
|
||||
@@ -822,4 +908,10 @@ const NetDevVTable l2tptnl_vtable = {
|
||||
.create_type = NETDEV_CREATE_INDEPENDENT,
|
||||
.is_ready_to_create = netdev_l2tp_is_ready_to_create,
|
||||
.config_verify = netdev_l2tp_tunnel_verify,
|
||||
.attach = netdev_l2tp_tunnel_attach,
|
||||
.detach = netdev_l2tp_tunnel_detach,
|
||||
.set_ifindex = netdev_l2tp_tunnel_set_ifindex,
|
||||
.get_ifindex = netdev_l2tp_tunnel_get_ifindex,
|
||||
.iftype = ARPHRD_ETHER,
|
||||
.skip_netdev_kind_check = true,
|
||||
};
|
||||
|
||||
@@ -37,6 +37,7 @@ typedef struct L2tpSession {
|
||||
ConfigSection *section;
|
||||
|
||||
char *name;
|
||||
int ifindex;
|
||||
|
||||
uint32_t session_id;
|
||||
uint32_t peer_session_id;
|
||||
|
||||
+185
-81
@@ -191,14 +191,40 @@ static bool netdev_is_stacked(NetDev *netdev) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void netdev_detach_from_manager(NetDev *netdev) {
|
||||
if (netdev->ifname && netdev->manager)
|
||||
hashmap_remove(netdev->manager->netdevs, netdev->ifname);
|
||||
NetDev* netdev_detach_name(NetDev *netdev, const char *name) {
|
||||
assert(netdev);
|
||||
|
||||
if (!netdev->manager || !name)
|
||||
return NULL; /* Already detached or not attached yet. */
|
||||
|
||||
return hashmap_remove_value(netdev->manager->netdevs, name, netdev);
|
||||
}
|
||||
|
||||
static NetDev *netdev_free(NetDev *netdev) {
|
||||
static NetDev* netdev_detach_impl(NetDev *netdev) {
|
||||
assert(netdev);
|
||||
|
||||
if (netdev->state != _NETDEV_STATE_INVALID &&
|
||||
NETDEV_VTABLE(netdev) &&
|
||||
NETDEV_VTABLE(netdev)->detach)
|
||||
NETDEV_VTABLE(netdev)->detach(netdev);
|
||||
|
||||
NetDev *n = netdev_detach_name(netdev, netdev->ifname);
|
||||
|
||||
netdev->manager = NULL;
|
||||
return n; /* Return NULL when it is not attached yet, or already detached. */
|
||||
}
|
||||
|
||||
void netdev_detach(NetDev *netdev) {
|
||||
assert(netdev);
|
||||
|
||||
netdev_unref(netdev_detach_impl(netdev));
|
||||
}
|
||||
|
||||
static NetDev* netdev_free(NetDev *netdev) {
|
||||
assert(netdev);
|
||||
|
||||
netdev_detach_impl(netdev);
|
||||
|
||||
/* Invoke the per-kind done() destructor, but only if the state field is initialized. We conditionalize that
|
||||
* because we parse .netdev files twice: once to determine the kind (with a short, minimal NetDev structure
|
||||
* allocation, with no room for per-kind fields), and once to read the kind's properties (with a full,
|
||||
@@ -211,8 +237,6 @@ static NetDev *netdev_free(NetDev *netdev) {
|
||||
NETDEV_VTABLE(netdev)->done)
|
||||
NETDEV_VTABLE(netdev)->done(netdev);
|
||||
|
||||
netdev_detach_from_manager(netdev);
|
||||
|
||||
condition_free_list(netdev->conditions);
|
||||
free(netdev->filename);
|
||||
strv_free(netdev->dropins);
|
||||
@@ -245,9 +269,52 @@ void netdev_drop(NetDev *netdev) {
|
||||
|
||||
log_netdev_debug(netdev, "netdev removed");
|
||||
|
||||
netdev_detach_from_manager(netdev);
|
||||
netdev_unref(netdev);
|
||||
return;
|
||||
netdev_detach(netdev);
|
||||
}
|
||||
|
||||
int netdev_attach_name(NetDev *netdev, const char *name) {
|
||||
int r;
|
||||
|
||||
assert(netdev);
|
||||
assert(netdev->manager);
|
||||
assert(name);
|
||||
|
||||
r = hashmap_ensure_put(&netdev->manager->netdevs, &string_hash_ops, name, netdev);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r == -EEXIST) {
|
||||
NetDev *n = hashmap_get(netdev->manager->netdevs, name);
|
||||
|
||||
assert(n);
|
||||
if (!streq(netdev->filename, n->filename))
|
||||
log_netdev_warning_errno(netdev, r,
|
||||
"Device \"%s\" was already configured by \"%s\", ignoring %s.",
|
||||
name, n->filename, netdev->filename);
|
||||
|
||||
return -EEXIST;
|
||||
}
|
||||
assert(r > 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netdev_attach(NetDev *netdev) {
|
||||
int r;
|
||||
|
||||
assert(netdev);
|
||||
assert(netdev->ifname);
|
||||
|
||||
r = netdev_attach_name(netdev, netdev->ifname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (NETDEV_VTABLE(netdev)->attach) {
|
||||
r = NETDEV_VTABLE(netdev)->attach(netdev);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int netdev_get(Manager *manager, const char *name, NetDev **ret) {
|
||||
@@ -266,11 +333,51 @@ int netdev_get(Manager *manager, const char *name, NetDev **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void link_assign_netdev(Link *link) {
|
||||
_unused_ _cleanup_(netdev_unrefp) NetDev *old = NULL;
|
||||
NetDev *netdev;
|
||||
|
||||
assert(link);
|
||||
assert(link->manager);
|
||||
assert(link->ifname);
|
||||
|
||||
old = TAKE_PTR(link->netdev);
|
||||
|
||||
if (netdev_get(link->manager, link->ifname, &netdev) < 0)
|
||||
return;
|
||||
|
||||
int ifindex = NETDEV_VTABLE(netdev)->get_ifindex ?
|
||||
NETDEV_VTABLE(netdev)->get_ifindex(netdev, link->ifname) :
|
||||
netdev->ifindex;
|
||||
if (ifindex != link->ifindex)
|
||||
return;
|
||||
|
||||
if (NETDEV_VTABLE(netdev)->iftype != link->iftype)
|
||||
return;
|
||||
|
||||
if (!NETDEV_VTABLE(netdev)->skip_netdev_kind_check) {
|
||||
const char *kind;
|
||||
|
||||
if (netdev->kind == NETDEV_KIND_TAP)
|
||||
kind = "tun"; /* the kernel does not distinguish between tun and tap */
|
||||
else
|
||||
kind = netdev_kind_to_string(netdev->kind);
|
||||
|
||||
if (!streq_ptr(kind, link->kind))
|
||||
return;
|
||||
}
|
||||
|
||||
link->netdev = netdev_ref(netdev);
|
||||
|
||||
if (netdev != old)
|
||||
log_link_debug(link, "Found matching .netdev file: %s", netdev->filename);
|
||||
}
|
||||
|
||||
void netdev_enter_failed(NetDev *netdev) {
|
||||
netdev->state = NETDEV_STATE_FAILED;
|
||||
}
|
||||
|
||||
static int netdev_enter_ready(NetDev *netdev) {
|
||||
int netdev_enter_ready(NetDev *netdev) {
|
||||
assert(netdev);
|
||||
assert(netdev->ifname);
|
||||
|
||||
@@ -309,96 +416,108 @@ static int netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev
|
||||
return 1;
|
||||
}
|
||||
|
||||
int netdev_set_ifindex_internal(NetDev *netdev, int ifindex) {
|
||||
assert(netdev);
|
||||
assert(ifindex > 0);
|
||||
|
||||
if (netdev->ifindex == ifindex)
|
||||
return 0; /* Already set. */
|
||||
|
||||
if (netdev->ifindex > 0 && netdev->ifindex != ifindex)
|
||||
return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EEXIST),
|
||||
"Could not set ifindex to %i, already set to %i.",
|
||||
ifindex, netdev->ifindex);
|
||||
|
||||
netdev->ifindex = ifindex;
|
||||
log_netdev_debug(netdev, "Gained index %i.", ifindex);
|
||||
return 1; /* set new ifindex. */
|
||||
}
|
||||
|
||||
static int netdev_set_ifindex_impl(NetDev *netdev, const char *name, int ifindex) {
|
||||
int r;
|
||||
|
||||
assert(netdev);
|
||||
assert(name);
|
||||
assert(ifindex > 0);
|
||||
|
||||
if (NETDEV_VTABLE(netdev)->set_ifindex)
|
||||
return NETDEV_VTABLE(netdev)->set_ifindex(netdev, name, ifindex);
|
||||
|
||||
if (!streq(netdev->ifname, name))
|
||||
return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received netlink message with unexpected interface name %s (ifindex=%i).",
|
||||
name, ifindex);
|
||||
|
||||
r = netdev_set_ifindex_internal(netdev, ifindex);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
return netdev_enter_ready(netdev);
|
||||
}
|
||||
|
||||
int netdev_set_ifindex(NetDev *netdev, sd_netlink_message *message) {
|
||||
uint16_t type;
|
||||
const char *kind;
|
||||
const char *received_kind;
|
||||
const char *received_name;
|
||||
int r, ifindex;
|
||||
int r, ifindex, family;
|
||||
|
||||
assert(netdev);
|
||||
assert(message);
|
||||
|
||||
r = sd_netlink_message_get_type(message, &type);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Could not get rtnl message type: %m");
|
||||
return log_netdev_warning_errno(netdev, r, "Could not get rtnl message type: %m");
|
||||
|
||||
if (type != RTM_NEWLINK)
|
||||
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), "Cannot set ifindex from unexpected rtnl message type.");
|
||||
return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL), "Cannot set ifindex from unexpected rtnl message type.");
|
||||
|
||||
r = sd_rtnl_message_get_family(message, &family);
|
||||
if (r < 0)
|
||||
return log_netdev_warning_errno(netdev, r, "Failed to get family from received rtnl message: %m");
|
||||
|
||||
if (family != AF_UNSPEC)
|
||||
return 0; /* IFLA_LINKINFO is only contained in the message with AF_UNSPEC. */
|
||||
|
||||
r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
|
||||
if (r < 0) {
|
||||
log_netdev_error_errno(netdev, r, "Could not get ifindex: %m");
|
||||
netdev_enter_failed(netdev);
|
||||
return r;
|
||||
} else if (ifindex <= 0) {
|
||||
log_netdev_error(netdev, "Got invalid ifindex: %d", ifindex);
|
||||
netdev_enter_failed(netdev);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (netdev->ifindex > 0) {
|
||||
if (netdev->ifindex != ifindex) {
|
||||
log_netdev_error(netdev, "Could not set ifindex to %d, already set to %d",
|
||||
ifindex, netdev->ifindex);
|
||||
netdev_enter_failed(netdev);
|
||||
return -EEXIST;
|
||||
} else
|
||||
/* ifindex already set to the same for this netdev */
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_netdev_warning_errno(netdev, r, "Could not get ifindex: %m");
|
||||
if (ifindex <= 0)
|
||||
return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL), "Got invalid ifindex: %d", ifindex);
|
||||
|
||||
r = sd_netlink_message_read_string(message, IFLA_IFNAME, &received_name);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Could not get IFNAME: %m");
|
||||
|
||||
if (!streq(netdev->ifname, received_name)) {
|
||||
log_netdev_error(netdev, "Received newlink with wrong IFNAME %s", received_name);
|
||||
netdev_enter_failed(netdev);
|
||||
return -EINVAL;
|
||||
}
|
||||
return log_netdev_warning_errno(netdev, r, "Could not get IFNAME: %m");
|
||||
|
||||
if (!NETDEV_VTABLE(netdev)->skip_netdev_kind_check) {
|
||||
|
||||
r = sd_netlink_message_enter_container(message, IFLA_LINKINFO);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Could not get LINKINFO: %m");
|
||||
return log_netdev_warning_errno(netdev, r, "Could not get LINKINFO: %m");
|
||||
|
||||
r = sd_netlink_message_read_string(message, IFLA_INFO_KIND, &received_kind);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Could not get KIND: %m");
|
||||
return log_netdev_warning_errno(netdev, r, "Could not get KIND: %m");
|
||||
|
||||
r = sd_netlink_message_exit_container(message);
|
||||
if (r < 0)
|
||||
return log_netdev_error_errno(netdev, r, "Could not exit container: %m");
|
||||
return log_netdev_warning_errno(netdev, r, "Could not exit container: %m");
|
||||
|
||||
if (netdev->kind == NETDEV_KIND_TAP)
|
||||
/* the kernel does not distinguish between tun and tap */
|
||||
kind = "tun";
|
||||
else {
|
||||
else
|
||||
kind = netdev_kind_to_string(netdev->kind);
|
||||
if (!kind) {
|
||||
log_netdev_error(netdev, "Could not get kind");
|
||||
netdev_enter_failed(netdev);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
if (!kind)
|
||||
return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL), "Could not get netdev kind.");
|
||||
|
||||
if (!streq(kind, received_kind)) {
|
||||
log_netdev_error(netdev, "Received newlink with wrong KIND %s, expected %s",
|
||||
received_kind, kind);
|
||||
netdev_enter_failed(netdev);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!streq(kind, received_kind))
|
||||
return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received newlink with wrong KIND %s, expected %s",
|
||||
received_kind, kind);
|
||||
}
|
||||
|
||||
netdev->ifindex = ifindex;
|
||||
|
||||
log_netdev_debug(netdev, "netdev has index %d", netdev->ifindex);
|
||||
|
||||
netdev_enter_ready(netdev);
|
||||
|
||||
return 0;
|
||||
return netdev_set_ifindex_impl(netdev, received_name, ifindex);
|
||||
}
|
||||
|
||||
#define HASH_KEY SD_ID128_MAKE(52,e1,45,bd,00,6f,29,96,21,c6,30,6d,83,71,04,48)
|
||||
@@ -850,24 +969,9 @@ int netdev_load_one(Manager *manager, const char *filename) {
|
||||
if (!netdev->filename)
|
||||
return log_oom();
|
||||
|
||||
r = hashmap_ensure_put(&netdev->manager->netdevs, &string_hash_ops, netdev->ifname, netdev);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r == -EEXIST) {
|
||||
NetDev *n = hashmap_get(netdev->manager->netdevs, netdev->ifname);
|
||||
|
||||
assert(n);
|
||||
if (!streq(netdev->filename, n->filename))
|
||||
log_netdev_warning_errno(netdev, r,
|
||||
"Device was already configured by \"%s\", ignoring %s.",
|
||||
n->filename, netdev->filename);
|
||||
|
||||
/* Clear ifname before netdev_free() is called. Otherwise, the NetDev object 'n' is
|
||||
* removed from the hashmap 'manager->netdevs'. */
|
||||
netdev->ifname = mfree(netdev->ifname);
|
||||
return -EEXIST;
|
||||
}
|
||||
assert(r > 0);
|
||||
r = netdev_attach(netdev);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
log_netdev_debug(netdev, "loaded \"%s\"", netdev_kind_to_string(netdev->kind));
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "conf-parser.h"
|
||||
#include "ether-addr-util.h"
|
||||
#include "hash-funcs.h"
|
||||
#include "list.h"
|
||||
#include "log-link.h"
|
||||
#include "networkd-link.h"
|
||||
@@ -168,6 +169,16 @@ typedef struct NetDevVTable {
|
||||
/* verify that compulsory configuration options were specified */
|
||||
int (*config_verify)(NetDev *netdev, const char *filename);
|
||||
|
||||
/* attach/detach additional interfaces, e.g. veth peer or L2TP sessions. */
|
||||
int (*attach)(NetDev *netdev);
|
||||
void (*detach)(NetDev *netdev);
|
||||
|
||||
/* set ifindex of the created interface. */
|
||||
int (*set_ifindex)(NetDev *netdev, const char *name, int ifindex);
|
||||
|
||||
/* get ifindex of the netdev. */
|
||||
int (*get_ifindex)(NetDev *netdev, const char *name);
|
||||
|
||||
/* expected iftype, e.g. ARPHRD_ETHER. */
|
||||
uint16_t iftype;
|
||||
|
||||
@@ -195,10 +206,16 @@ extern const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX];
|
||||
/* For casting the various netdev kinds into a netdev */
|
||||
#define NETDEV(n) (&(n)->meta)
|
||||
|
||||
int netdev_attach_name(NetDev *netdev, const char *name);
|
||||
NetDev* netdev_detach_name(NetDev *netdev, const char *name);
|
||||
void netdev_detach(NetDev *netdev);
|
||||
int netdev_set_ifindex_internal(NetDev *netdev, int ifindex);
|
||||
|
||||
int netdev_load(Manager *manager, bool reload);
|
||||
int netdev_load_one(Manager *manager, const char *filename);
|
||||
void netdev_drop(NetDev *netdev);
|
||||
void netdev_enter_failed(NetDev *netdev);
|
||||
int netdev_enter_ready(NetDev *netdev);
|
||||
|
||||
NetDev *netdev_unref(NetDev *netdev);
|
||||
NetDev *netdev_ref(NetDev *netdev);
|
||||
@@ -207,6 +224,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(NetDev*, netdev_unref);
|
||||
|
||||
bool netdev_is_managed(NetDev *netdev);
|
||||
int netdev_get(Manager *manager, const char *name, NetDev **ret);
|
||||
void link_assign_netdev(Link *link);
|
||||
int netdev_set_ifindex(NetDev *netdev, sd_netlink_message *newlink);
|
||||
int netdev_generate_hw_addr(NetDev *netdev, Link *link, const char *name,
|
||||
const struct hw_addr_data *hw_addr, struct hw_addr_data *ret);
|
||||
|
||||
@@ -62,9 +62,75 @@ static int netdev_veth_verify(NetDev *netdev, const char *filename) {
|
||||
"Veth NetDev without peer name configured in %s. Ignoring",
|
||||
filename);
|
||||
|
||||
if (streq(v->ifname_peer, netdev->ifname))
|
||||
return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Veth peer name cannot be the same as the main interface name.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netdev_veth_attach(NetDev *netdev) {
|
||||
Veth *v = VETH(netdev);
|
||||
|
||||
assert(v->ifname_peer);
|
||||
return netdev_attach_name(netdev, v->ifname_peer);
|
||||
}
|
||||
|
||||
static void netdev_veth_detach(NetDev *netdev) {
|
||||
Veth *v = VETH(netdev);
|
||||
|
||||
netdev_detach_name(netdev, v->ifname_peer);
|
||||
}
|
||||
|
||||
static int netdev_veth_set_ifindex(NetDev *netdev, const char *name, int ifindex) {
|
||||
Veth *v = VETH(netdev);
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
assert(ifindex > 0);
|
||||
|
||||
if (streq(netdev->ifname, name)) {
|
||||
r = netdev_set_ifindex_internal(netdev, ifindex);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
} else if (streq(v->ifname_peer, name)) {
|
||||
if (v->ifindex_peer == ifindex)
|
||||
return 0; /* already set. */
|
||||
|
||||
if (v->ifindex_peer > 0 && v->ifindex_peer != ifindex)
|
||||
return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EEXIST),
|
||||
"Could not set ifindex %i for peer %s, already set to %i.",
|
||||
ifindex, v->ifname_peer, v->ifindex_peer);
|
||||
|
||||
v->ifindex_peer = ifindex;
|
||||
log_netdev_debug(netdev, "Peer interface %s gained index %i.", v->ifname_peer, ifindex);
|
||||
|
||||
} else
|
||||
return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received netlink message with unexpected interface name %s (index=%i).",
|
||||
name, ifindex);
|
||||
|
||||
if (netdev->ifindex > 0 && v->ifindex_peer > 0)
|
||||
return netdev_enter_ready(netdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netdev_veth_get_ifindex(NetDev *netdev, const char *name) {
|
||||
Veth *v = VETH(netdev);
|
||||
|
||||
assert(name);
|
||||
|
||||
if (streq(netdev->ifname, name))
|
||||
return netdev->ifindex;
|
||||
|
||||
if (streq(v->ifname_peer, name))
|
||||
return v->ifindex_peer;
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void veth_done(NetDev *netdev) {
|
||||
Veth *v = VETH(netdev);
|
||||
|
||||
@@ -78,6 +144,10 @@ const NetDevVTable veth_vtable = {
|
||||
.fill_message_create = netdev_veth_fill_message_create,
|
||||
.create_type = NETDEV_CREATE_INDEPENDENT,
|
||||
.config_verify = netdev_veth_verify,
|
||||
.attach = netdev_veth_attach,
|
||||
.detach = netdev_veth_detach,
|
||||
.set_ifindex = netdev_veth_set_ifindex,
|
||||
.get_ifindex = netdev_veth_get_ifindex,
|
||||
.iftype = ARPHRD_ETHER,
|
||||
.generate_mac = true,
|
||||
};
|
||||
|
||||
@@ -10,6 +10,7 @@ struct Veth {
|
||||
|
||||
char *ifname_peer;
|
||||
struct hw_addr_data hw_addr_peer;
|
||||
int ifindex_peer;
|
||||
};
|
||||
|
||||
DEFINE_NETDEV_CAST(VETH, Veth);
|
||||
|
||||
@@ -38,9 +38,74 @@ static int netdev_vxcan_verify(NetDev *netdev, const char *filename) {
|
||||
return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"VxCan NetDev without peer name configured in %s. Ignoring", filename);
|
||||
|
||||
if (streq(v->ifname_peer, netdev->ifname))
|
||||
return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"VxCan peer name cannot be the same as the main interface name.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netdev_vxcan_attach(NetDev *netdev) {
|
||||
VxCan *v = VXCAN(netdev);
|
||||
assert(v->ifname_peer);
|
||||
|
||||
return netdev_attach_name(netdev, v->ifname_peer);
|
||||
}
|
||||
|
||||
static void netdev_vxcan_detach(NetDev *netdev) {
|
||||
VxCan *v = VXCAN(netdev);
|
||||
|
||||
netdev_detach_name(netdev, v->ifname_peer);
|
||||
}
|
||||
|
||||
static int netdev_vxcan_set_ifindex(NetDev *netdev, const char *name, int ifindex) {
|
||||
VxCan *v = VXCAN(netdev);
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
assert(ifindex > 0);
|
||||
|
||||
if (streq(netdev->ifname, name)) {
|
||||
r = netdev_set_ifindex_internal(netdev, ifindex);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
} else if (streq(v->ifname_peer, name)) {
|
||||
if (v->ifindex_peer == ifindex)
|
||||
return 0; /* already set */
|
||||
if (v->ifindex_peer > 0 && v->ifindex_peer != ifindex)
|
||||
return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EEXIST),
|
||||
"Could not set ifindex %i for peer %s, already set to %i.",
|
||||
ifindex, v->ifname_peer, v->ifindex_peer);
|
||||
|
||||
v->ifindex_peer = ifindex;
|
||||
log_netdev_debug(netdev, "Peer interface %s gained index %i.", v->ifname_peer, ifindex);
|
||||
|
||||
} else
|
||||
return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received netlink message with unexpected interface name %s (ifindex=%i).",
|
||||
name, ifindex);
|
||||
|
||||
if (netdev->ifindex > 0 && v->ifindex_peer > 0)
|
||||
return netdev_enter_ready(netdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int netdev_vxcan_get_ifindex(NetDev *netdev, const char *name) {
|
||||
VxCan *v = VXCAN(netdev);
|
||||
|
||||
assert(name);
|
||||
|
||||
if (streq(netdev->ifname, name))
|
||||
return netdev->ifindex;
|
||||
|
||||
if (streq(v->ifname_peer, name))
|
||||
return v->ifindex_peer;
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void vxcan_done(NetDev *netdev) {
|
||||
VxCan *v = VXCAN(netdev);
|
||||
|
||||
@@ -54,5 +119,9 @@ const NetDevVTable vxcan_vtable = {
|
||||
.fill_message_create = netdev_vxcan_fill_message_create,
|
||||
.create_type = NETDEV_CREATE_INDEPENDENT,
|
||||
.config_verify = netdev_vxcan_verify,
|
||||
.attach = netdev_vxcan_attach,
|
||||
.detach = netdev_vxcan_detach,
|
||||
.set_ifindex = netdev_vxcan_set_ifindex,
|
||||
.get_ifindex = netdev_vxcan_get_ifindex,
|
||||
.iftype = ARPHRD_CAN,
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ struct VxCan {
|
||||
NetDev meta;
|
||||
|
||||
char *ifname_peer;
|
||||
int ifindex_peer;
|
||||
};
|
||||
|
||||
DEFINE_NETDEV_CAST(VXCAN, VxCan);
|
||||
|
||||
@@ -1307,18 +1307,15 @@ static int link_get_network(Link *link, Network **ret) {
|
||||
|
||||
int link_reconfigure_impl(Link *link, bool force) {
|
||||
Network *network = NULL;
|
||||
NetDev *netdev = NULL;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
link_assign_netdev(link);
|
||||
|
||||
if (IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_LINGER))
|
||||
return 0;
|
||||
|
||||
r = netdev_get(link->manager, link->ifname, &netdev);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return r;
|
||||
|
||||
r = link_get_network(link, &network);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return r;
|
||||
@@ -1383,9 +1380,6 @@ int link_reconfigure_impl(Link *link, bool force) {
|
||||
link_free_engines(link);
|
||||
link->network = network_unref(link->network);
|
||||
|
||||
netdev_unref(link->netdev);
|
||||
link->netdev = netdev_ref(netdev);
|
||||
|
||||
if (!network) {
|
||||
link_set_state(link, LINK_STATE_UNMANAGED);
|
||||
return 0;
|
||||
@@ -2810,6 +2804,7 @@ int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Man
|
||||
r = netdev_set_ifindex(netdev, message);
|
||||
if (r < 0) {
|
||||
log_netdev_warning_errno(netdev, r, "Could not process new link message for netdev, ignoring: %m");
|
||||
netdev_enter_failed(netdev);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -633,7 +633,11 @@ Manager* manager_free(Manager *m) {
|
||||
m->dhcp_pd_subnet_ids = set_free(m->dhcp_pd_subnet_ids);
|
||||
m->networks = ordered_hashmap_free_with_destructor(m->networks, network_unref);
|
||||
|
||||
m->netdevs = hashmap_free_with_destructor(m->netdevs, netdev_unref);
|
||||
/* The same object may be registered with multiple names, and netdev_detach() may drop multiple
|
||||
* entries. Hence, hashmap_free_with_destructor() cannot be used. */
|
||||
for (NetDev *n; (n = hashmap_first(m->netdevs)); )
|
||||
netdev_detach(n);
|
||||
m->netdevs = hashmap_free(m->netdevs);
|
||||
|
||||
m->tuntap_fds_by_name = hashmap_free(m->tuntap_fds_by_name);
|
||||
|
||||
|
||||
@@ -1008,19 +1008,15 @@ static int static_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request
|
||||
}
|
||||
|
||||
static int link_request_wireguard_routes(Link *link, bool only_ipv4) {
|
||||
NetDev *netdev;
|
||||
Route *route;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
if (!streq_ptr(link->kind, "wireguard"))
|
||||
if (!link->netdev || link->netdev->kind != NETDEV_KIND_WIREGUARD)
|
||||
return 0;
|
||||
|
||||
if (netdev_get(link->manager, link->ifname, &netdev) < 0)
|
||||
return 0;
|
||||
|
||||
Wireguard *w = WIREGUARD(netdev);
|
||||
Wireguard *w = WIREGUARD(link->netdev);
|
||||
|
||||
SET_FOREACH(route, w->routes) {
|
||||
if (only_ipv4 && route->family != AF_INET)
|
||||
|
||||
@@ -623,6 +623,12 @@ static int link_save(Link *link) {
|
||||
"IPV6_ADDRESS_STATE=%s\n",
|
||||
admin_state, oper_state, carrier_state, address_state, ipv4_address_state, ipv6_address_state);
|
||||
|
||||
if (link->netdev) {
|
||||
r = serialize_config_files(f, "NETDEV", link->netdev->filename, link->netdev->dropins);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (link->network) {
|
||||
const char *online_state, *captive_portal;
|
||||
bool space = false;
|
||||
@@ -650,12 +656,6 @@ static int link_save(Link *link) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (link->netdev) {
|
||||
r = serialize_config_files(f, "NETDEV", link->netdev->filename, link->netdev->dropins);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/************************************************************/
|
||||
|
||||
fputs("DNS=", f);
|
||||
|
||||
Reference in New Issue
Block a user