diff --git a/man/systemd.link.xml b/man/systemd.link.xml
index 55ca597c97..1eaf8e14cb 100644
--- a/man/systemd.link.xml
+++ b/man/systemd.link.xml
@@ -178,6 +178,20 @@
+
+ Kind=
+
+ A whitespace-separated list of shell-style globs matching the device kind, as exposed by
+ networkctl status INTERFACE or
+ ip -d link show INTERFACE. If the list is
+ prefixed with a "!", the test is inverted. Some valid values are bond,
+ bridge, gre, tun,
+ veth. Valid kinds are given by netlink's IFLA_INFO_KIND
+ attribute, so this is not comprehensive.
+
+
+
+
Property=
diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 52d017bb78..3e8d9affd9 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -85,6 +85,7 @@
+
diff --git a/src/libsystemd/sd-netlink/netlink-util.c b/src/libsystemd/sd-netlink/netlink-util.c
index 8b4ed44790..ce2c4f3b54 100644
--- a/src/libsystemd/sd-netlink/netlink-util.c
+++ b/src/libsystemd/sd-netlink/netlink-util.c
@@ -366,11 +366,13 @@ int rtnl_get_link_info(
int ifindex,
unsigned short *ret_iftype,
unsigned *ret_flags,
+ char **ret_kind,
struct hw_addr_data *ret_hw_addr,
struct hw_addr_data *ret_permanent_hw_addr) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
struct hw_addr_data addr = HW_ADDR_NULL, perm_addr = HW_ADDR_NULL;
+ _cleanup_free_ char *kind = NULL;
unsigned short iftype;
unsigned flags;
int r;
@@ -409,6 +411,19 @@ int rtnl_get_link_info(
return r;
}
+ if (ret_kind) {
+ r = sd_netlink_message_enter_container(reply, IFLA_LINKINFO);
+ if (r >= 0) {
+ r = sd_netlink_message_read_string_strdup(reply, IFLA_INFO_KIND, &kind);
+ if (r < 0 && r != -ENODATA)
+ return r;
+
+ r = sd_netlink_message_exit_container(reply);
+ if (r < 0)
+ return r;
+ }
+ }
+
if (ret_hw_addr) {
r = netlink_message_read_hw_addr(reply, IFLA_ADDRESS, &addr);
if (r < 0 && r != -ENODATA)
@@ -425,6 +440,8 @@ int rtnl_get_link_info(
*ret_iftype = iftype;
if (ret_flags)
*ret_flags = flags;
+ if (ret_kind)
+ *ret_kind = TAKE_PTR(kind);
if (ret_hw_addr)
*ret_hw_addr = addr;
if (ret_permanent_hw_addr)
diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h
index 097483c435..fee450cdc2 100644
--- a/src/libsystemd/sd-netlink/netlink-util.h
+++ b/src/libsystemd/sd-netlink/netlink-util.h
@@ -94,6 +94,7 @@ int rtnl_get_link_info(
int ifindex,
unsigned short *ret_iftype,
unsigned *ret_flags,
+ char **ret_kind,
struct hw_addr_data *ret_hw_addr,
struct hw_addr_data *ret_permanent_hw_addr);
diff --git a/src/network/networkctl.c b/src/network/networkctl.c
index 20e176e854..8fe9844c4f 100644
--- a/src/network/networkctl.c
+++ b/src/network/networkctl.c
@@ -263,7 +263,7 @@ typedef struct VxLanInfo {
typedef struct LinkInfo {
char name[IFNAMSIZ+1];
- char netdev_kind[NETDEV_KIND_MAX];
+ char *netdev_kind;
sd_device *sd_device;
int ifindex;
unsigned short iftype;
@@ -364,6 +364,7 @@ static int link_info_compare(const LinkInfo *a, const LinkInfo *b) {
static LinkInfo* link_info_array_free(LinkInfo *array) {
for (unsigned i = 0; array && array[i].needs_freeing; i++) {
sd_device_unref(array[i].sd_device);
+ free(array[i].netdev_kind);
free(array[i].ssid);
free(array[i].qdisc);
strv_free(array[i].alternative_names);
@@ -374,7 +375,6 @@ static LinkInfo* link_info_array_free(LinkInfo *array) {
DEFINE_TRIVIAL_CLEANUP_FUNC(LinkInfo*, link_info_array_free);
static int decode_netdev(sd_netlink_message *m, LinkInfo *info) {
- const char *received_kind;
int r;
assert(m);
@@ -384,15 +384,17 @@ static int decode_netdev(sd_netlink_message *m, LinkInfo *info) {
if (r < 0)
return r;
- r = sd_netlink_message_read_string(m, IFLA_INFO_KIND, &received_kind);
- if (r < 0)
+ r = sd_netlink_message_read_string_strdup(m, IFLA_INFO_KIND, &info->netdev_kind);
+ if (r < 0) {
+ (void) sd_netlink_message_exit_container(m);
return r;
+ }
r = sd_netlink_message_enter_container(m, IFLA_INFO_DATA);
if (r < 0)
return r;
- if (streq(received_kind, "bridge")) {
+ if (streq(info->netdev_kind, "bridge")) {
(void) sd_netlink_message_read_u32(m, IFLA_BR_FORWARD_DELAY, &info->forward_delay);
(void) sd_netlink_message_read_u32(m, IFLA_BR_HELLO_TIME, &info->hello_time);
(void) sd_netlink_message_read_u32(m, IFLA_BR_MAX_AGE, &info->max_age);
@@ -402,12 +404,12 @@ static int decode_netdev(sd_netlink_message *m, LinkInfo *info) {
(void) sd_netlink_message_read_u16(m, IFLA_BR_PRIORITY, &info->priority);
(void) sd_netlink_message_read_u8(m, IFLA_BR_MCAST_IGMP_VERSION, &info->mcast_igmp_version);
(void) sd_netlink_message_read_u8(m, IFLA_BRPORT_STATE, &info->port_state);
- } if (streq(received_kind, "bond")) {
+ } if (streq(info->netdev_kind, "bond")) {
(void) sd_netlink_message_read_u8(m, IFLA_BOND_MODE, &info->mode);
(void) sd_netlink_message_read_u32(m, IFLA_BOND_MIIMON, &info->miimon);
(void) sd_netlink_message_read_u32(m, IFLA_BOND_DOWNDELAY, &info->downdelay);
(void) sd_netlink_message_read_u32(m, IFLA_BOND_UPDELAY, &info->updelay);
- } else if (streq(received_kind, "vxlan")) {
+ } else if (streq(info->netdev_kind, "vxlan")) {
(void) sd_netlink_message_read_u32(m, IFLA_VXLAN_ID, &info->vxlan_info.vni);
r = sd_netlink_message_read_in_addr(m, IFLA_VXLAN_GROUP, &info->vxlan_info.group.in);
@@ -437,12 +439,12 @@ static int decode_netdev(sd_netlink_message *m, LinkInfo *info) {
(void) sd_netlink_message_read_u8(m, IFLA_VXLAN_L2MISS, &info->vxlan_info.l2miss);
(void) sd_netlink_message_read_u8(m, IFLA_VXLAN_TOS, &info->vxlan_info.tos);
(void) sd_netlink_message_read_u8(m, IFLA_VXLAN_TTL, &info->vxlan_info.ttl);
- } else if (streq(received_kind, "vlan"))
+ } else if (streq(info->netdev_kind, "vlan"))
(void) sd_netlink_message_read_u16(m, IFLA_VLAN_ID, &info->vlan_id);
- else if (STR_IN_SET(received_kind, "ipip", "sit")) {
+ else if (STR_IN_SET(info->netdev_kind, "ipip", "sit")) {
(void) sd_netlink_message_read_in_addr(m, IFLA_IPTUN_LOCAL, &info->local.in);
(void) sd_netlink_message_read_in_addr(m, IFLA_IPTUN_REMOTE, &info->remote.in);
- } else if (streq(received_kind, "geneve")) {
+ } else if (streq(info->netdev_kind, "geneve")) {
(void) sd_netlink_message_read_u32(m, IFLA_GENEVE_ID, &info->vni);
r = sd_netlink_message_read_in_addr(m, IFLA_GENEVE_REMOTE, &info->remote.in);
@@ -460,27 +462,25 @@ static int decode_netdev(sd_netlink_message *m, LinkInfo *info) {
(void) sd_netlink_message_read_u8(m, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, &info->csum6_rx);
(void) sd_netlink_message_read_u16(m, IFLA_GENEVE_PORT, &info->tunnel_port);
(void) sd_netlink_message_read_u32(m, IFLA_GENEVE_LABEL, &info->label);
- } else if (STR_IN_SET(received_kind, "gre", "gretap", "erspan")) {
+ } else if (STR_IN_SET(info->netdev_kind, "gre", "gretap", "erspan")) {
(void) sd_netlink_message_read_in_addr(m, IFLA_GRE_LOCAL, &info->local.in);
(void) sd_netlink_message_read_in_addr(m, IFLA_GRE_REMOTE, &info->remote.in);
- } else if (STR_IN_SET(received_kind, "ip6gre", "ip6gretap", "ip6erspan")) {
+ } else if (STR_IN_SET(info->netdev_kind, "ip6gre", "ip6gretap", "ip6erspan")) {
(void) sd_netlink_message_read_in6_addr(m, IFLA_GRE_LOCAL, &info->local.in6);
(void) sd_netlink_message_read_in6_addr(m, IFLA_GRE_REMOTE, &info->remote.in6);
- } else if (streq(received_kind, "vti")) {
+ } else if (streq(info->netdev_kind, "vti")) {
(void) sd_netlink_message_read_in_addr(m, IFLA_VTI_LOCAL, &info->local.in);
(void) sd_netlink_message_read_in_addr(m, IFLA_VTI_REMOTE, &info->remote.in);
- } else if (streq(received_kind, "vti6")) {
+ } else if (streq(info->netdev_kind, "vti6")) {
(void) sd_netlink_message_read_in6_addr(m, IFLA_VTI_LOCAL, &info->local.in6);
(void) sd_netlink_message_read_in6_addr(m, IFLA_VTI_REMOTE, &info->remote.in6);
- } else if (STR_IN_SET(received_kind, "macvlan", "macvtap"))
+ } else if (STR_IN_SET(info->netdev_kind, "macvlan", "macvtap"))
(void) sd_netlink_message_read_u32(m, IFLA_MACVLAN_MODE, &info->macvlan_mode);
- else if (streq(received_kind, "ipvlan")) {
+ else if (streq(info->netdev_kind, "ipvlan")) {
(void) sd_netlink_message_read_u16(m, IFLA_IPVLAN_MODE, &info->ipvlan_mode);
(void) sd_netlink_message_read_u16(m, IFLA_IPVLAN_FLAGS, &info->ipvlan_flags);
}
- strncpy(info->netdev_kind, received_kind, IFNAMSIZ);
-
(void) sd_netlink_message_exit_container(m);
(void) sd_netlink_message_exit_container(m);
@@ -1644,6 +1644,9 @@ static int link_status_one(
TABLE_STRING, "Type:",
TABLE_STRING, strna(t),
TABLE_EMPTY,
+ TABLE_STRING, "Kind:",
+ TABLE_STRING, strna(info->netdev_kind),
+ TABLE_EMPTY,
TABLE_STRING, "State:");
if (r < 0)
return table_log_add_error(r);
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 6fdf800782..d26027f0bd 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -1168,6 +1168,7 @@ static int link_get_network(Link *link, Network **ret) {
&link->permanent_hw_addr,
link->driver,
link->iftype,
+ link->kind,
link->ifname,
link->alternative_names,
link->wlan_iftype,
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index c3de7d7239..edce61996a 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -52,6 +52,7 @@ Match.PermanentMACAddress, config_parse_hw_addrs,
Match.Path, config_parse_match_strv, 0, offsetof(Network, match.path)
Match.Driver, config_parse_match_strv, 0, offsetof(Network, match.driver)
Match.Type, config_parse_match_strv, 0, offsetof(Network, match.iftype)
+Match.Kind, config_parse_match_strv, 0, offsetof(Network, match.kind)
Match.WLANInterfaceType, config_parse_match_strv, 0, offsetof(Network, match.wlan_iftype)
Match.SSID, config_parse_match_strv, 0, offsetof(Network, match.ssid)
Match.BSSID, config_parse_ether_addrs, 0, offsetof(Network, match.bssid)
diff --git a/src/shared/net-condition.c b/src/shared/net-condition.c
index 006676d973..b96cb60064 100644
--- a/src/shared/net-condition.c
+++ b/src/shared/net-condition.c
@@ -22,6 +22,7 @@ void net_match_clear(NetMatch *match) {
match->path = strv_free(match->path);
match->driver = strv_free(match->driver);
match->iftype = strv_free(match->iftype);
+ match->kind = strv_free(match->kind);
match->ifname = strv_free(match->ifname);
match->property = strv_free(match->property);
match->wlan_iftype = strv_free(match->wlan_iftype);
@@ -38,6 +39,7 @@ bool net_match_is_empty(const NetMatch *match) {
strv_isempty(match->path) &&
strv_isempty(match->driver) &&
strv_isempty(match->iftype) &&
+ strv_isempty(match->kind) &&
strv_isempty(match->ifname) &&
strv_isempty(match->property) &&
strv_isempty(match->wlan_iftype) &&
@@ -126,6 +128,7 @@ int net_match_config(
const struct hw_addr_data *permanent_hw_addr,
const char *driver,
unsigned short iftype,
+ const char *kind,
const char *ifname,
char * const *alternative_names,
enum nl80211_iftype wlan_iftype,
@@ -160,6 +163,9 @@ int net_match_config(
if (!net_condition_test_strv(match->iftype, iftype_str))
return false;
+ if (!net_condition_test_strv(match->kind, kind))
+ return false;
+
if (!net_condition_test_ifname(match->ifname, ifname, alternative_names))
return false;
diff --git a/src/shared/net-condition.h b/src/shared/net-condition.h
index e767439335..0884d43f46 100644
--- a/src/shared/net-condition.h
+++ b/src/shared/net-condition.h
@@ -15,7 +15,8 @@ typedef struct NetMatch {
Set *permanent_hw_addr;
char **path;
char **driver;
- char **iftype;
+ char **iftype; /* udev's DEVTYPE field or ARPHRD_XXX, e.g. ether, wlan. */
+ char **kind; /* IFLA_INFO_KIND attribute, e.g. gre, gretap, erspan. */
char **ifname;
char **property;
char **wlan_iftype;
@@ -33,6 +34,7 @@ int net_match_config(
const struct hw_addr_data *permanent_hw_addr,
const char *driver,
unsigned short iftype,
+ const char *kind,
const char *ifname,
char * const *alternative_names,
enum nl80211_iftype wlan_iftype,
diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf
index 17b3697077..96280148c7 100644
--- a/src/udev/net/link-config-gperf.gperf
+++ b/src/udev/net/link-config-gperf.gperf
@@ -28,6 +28,7 @@ Match.OriginalName, config_parse_match_ifnames,
Match.Path, config_parse_match_strv, 0, offsetof(LinkConfig, match.path)
Match.Driver, config_parse_match_strv, 0, offsetof(LinkConfig, match.driver)
Match.Type, config_parse_match_strv, 0, offsetof(LinkConfig, match.iftype)
+Match.Kind, config_parse_match_strv, 0, offsetof(LinkConfig, match.kind)
Match.Property, config_parse_match_property, 0, offsetof(LinkConfig, match.property)
Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(LinkConfig, conditions)
Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(LinkConfig, conditions)
diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c
index 29e960acc0..9b51025c6a 100644
--- a/src/udev/net/link-config.c
+++ b/src/udev/net/link-config.c
@@ -361,6 +361,7 @@ Link *link_free(Link *link) {
return NULL;
sd_device_unref(link->device);
+ free(link->kind);
free(link->driver);
return mfree(link);
}
@@ -402,7 +403,8 @@ int link_new(LinkConfigContext *ctx, sd_netlink **rtnl, sd_device *device, Link
if (r < 0)
log_link_debug_errno(link, r, "Failed to get \"addr_assign_type\" attribute, ignoring: %m");
- r = rtnl_get_link_info(rtnl, link->ifindex, &link->iftype, &link->flags, &link->hw_addr, &link->permanent_hw_addr);
+ r = rtnl_get_link_info(rtnl, link->ifindex, &link->iftype, &link->flags,
+ &link->kind, &link->hw_addr, &link->permanent_hw_addr);
if (r < 0)
return r;
@@ -439,6 +441,7 @@ int link_get_config(LinkConfigContext *ctx, Link *link) {
&link->permanent_hw_addr,
link->driver,
link->iftype,
+ link->kind,
link->ifname,
/* alternative_names = */ NULL,
/* wlan_iftype = */ 0,
diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h
index 90cb438e4b..ea9f560f45 100644
--- a/src/udev/net/link-config.h
+++ b/src/udev/net/link-config.h
@@ -32,6 +32,7 @@ typedef struct Link {
sd_device *device;
sd_device_action_t action;
+ char *kind;
char *driver;
uint16_t iftype;
uint32_t flags;
diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c
index aa7d229816..85f89fadd8 100644
--- a/src/udev/udev-event.c
+++ b/src/udev/udev-event.c
@@ -900,7 +900,7 @@ static int rename_netif(UdevEvent *event) {
return 0;
}
- r = rtnl_get_link_info(&event->rtnl, ifindex, NULL, &flags, NULL, NULL);
+ r = rtnl_get_link_info(&event->rtnl, ifindex, NULL, &flags, NULL, NULL, NULL);
if (r < 0)
return log_device_warning_errno(dev, r, "Failed to get link flags: %m");
diff --git a/test/fuzz/fuzz-link-parser/directives.link b/test/fuzz/fuzz-link-parser/directives.link
index eae76749eb..d6c6cc6f2e 100644
--- a/test/fuzz/fuzz-link-parser/directives.link
+++ b/test/fuzz/fuzz-link-parser/directives.link
@@ -5,6 +5,7 @@ OriginalName=
Path=
Driver=
Type=
+Kind=
Property=
Host=
Virtualization=
diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network
index 10a40d2664..ef302dca7b 100644
--- a/test/fuzz/fuzz-network-parser/directives.network
+++ b/test/fuzz/fuzz-network-parser/directives.network
@@ -17,6 +17,7 @@ MulticastRouter=
[Match]
KernelVersion=
Type=
+Kind=
Driver=
Architecture=
Firmware=