diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml
index 26a5dbbd1e..066793c6e6 100644
--- a/man/systemd.netdev.xml
+++ b/man/systemd.netdev.xml
@@ -171,7 +171,10 @@
WireGuard Secure Network Tunnel.
netdevsim
- A simulator. This simulated networking device is used for testing various networking APIs and at this time is particularly focused on testing hardware offloading related interfaces.
+ A simulator. This simulated networking device is used for testing various networking APIs and at this time is particularly focused on testing hardware offloading related interfaces.
+
+ nlmon
+ A Netlink monitor device. Use an nlmon device when you want to monitor system Netlink messages.
fou
Foo-over-UDP tunneling.
diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c
index 7fd3efbd35..35ca843569 100644
--- a/src/libsystemd/sd-netlink/netlink-types.c
+++ b/src/libsystemd/sd-netlink/netlink-types.c
@@ -357,6 +357,7 @@ static const char* const nl_union_link_info_data_table[] = {
[NL_UNION_LINK_INFO_DATA_NETDEVSIM] = "netdevsim",
[NL_UNION_LINK_INFO_DATA_CAN] = "can",
[NL_UNION_LINK_INFO_DATA_MACSEC] = "macsec",
+ [NL_UNION_LINK_INFO_DATA_NLMON] = "nlmon",
};
DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData);
diff --git a/src/libsystemd/sd-netlink/netlink-types.h b/src/libsystemd/sd-netlink/netlink-types.h
index 8585280463..fed43ae43c 100644
--- a/src/libsystemd/sd-netlink/netlink-types.h
+++ b/src/libsystemd/sd-netlink/netlink-types.h
@@ -82,6 +82,7 @@ typedef enum NLUnionLinkInfoData {
NL_UNION_LINK_INFO_DATA_NETDEVSIM,
NL_UNION_LINK_INFO_DATA_CAN,
NL_UNION_LINK_INFO_DATA_MACSEC,
+ NL_UNION_LINK_INFO_DATA_NLMON,
_NL_UNION_LINK_INFO_DATA_MAX,
_NL_UNION_LINK_INFO_DATA_INVALID = -1
} NLUnionLinkInfoData;
diff --git a/src/network/meson.build b/src/network/meson.build
index 0bcf7f4a81..feeb98cb07 100644
--- a/src/network/meson.build
+++ b/src/network/meson.build
@@ -13,6 +13,8 @@ sources = files('''
netdev/macvlan.h
netdev/netdev.c
netdev/netdev.h
+ netdev/nlmon.c
+ netdev/nlmon.h
netdev/tunnel.c
netdev/tunnel.h
netdev/tuntap.c
diff --git a/src/network/netdev/bond.c b/src/network/netdev/bond.c
index 240cfe34e2..a85c9e33ec 100644
--- a/src/network/netdev/bond.c
+++ b/src/network/netdev/bond.c
@@ -592,4 +592,5 @@ const NetDevVTable bond_vtable = {
.sections = "Match\0NetDev\0Bond\0",
.fill_message_create = netdev_bond_fill_message_create,
.create_type = NETDEV_CREATE_MASTER,
+ .generate_mac = true,
};
diff --git a/src/network/netdev/dummy.c b/src/network/netdev/dummy.c
index aded1c589f..091a54ff39 100644
--- a/src/network/netdev/dummy.c
+++ b/src/network/netdev/dummy.c
@@ -6,4 +6,5 @@ const NetDevVTable dummy_vtable = {
.object_size = sizeof(Dummy),
.sections = "Match\0NetDev\0",
.create_type = NETDEV_CREATE_INDEPENDENT,
+ .generate_mac = true,
};
diff --git a/src/network/netdev/geneve.c b/src/network/netdev/geneve.c
index 3dc8f083cc..611f9b2f6d 100644
--- a/src/network/netdev/geneve.c
+++ b/src/network/netdev/geneve.c
@@ -355,4 +355,5 @@ const NetDevVTable geneve_vtable = {
.create = netdev_geneve_create,
.create_type = NETDEV_CREATE_INDEPENDENT,
.config_verify = netdev_geneve_verify,
+ .generate_mac = true,
};
diff --git a/src/network/netdev/ipvlan.c b/src/network/netdev/ipvlan.c
index f48cb0231a..45dfe17777 100644
--- a/src/network/netdev/ipvlan.c
+++ b/src/network/netdev/ipvlan.c
@@ -77,6 +77,7 @@ const NetDevVTable ipvlan_vtable = {
.sections = "Match\0NetDev\0IPVLAN\0",
.fill_message_create = netdev_ipvlan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
+ .generate_mac = true,
};
const NetDevVTable ipvtap_vtable = {
@@ -85,6 +86,7 @@ const NetDevVTable ipvtap_vtable = {
.sections = "Match\0NetDev\0IPVTAP\0",
.fill_message_create = netdev_ipvlan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
+ .generate_mac = true,
};
IPVlanMode link_get_ipvlan_mode(Link *link) {
diff --git a/src/network/netdev/macsec.c b/src/network/netdev/macsec.c
index c82a6fd0ac..740d885339 100644
--- a/src/network/netdev/macsec.c
+++ b/src/network/netdev/macsec.c
@@ -1246,4 +1246,5 @@ const NetDevVTable macsec_vtable = {
.done = macsec_done,
.create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_macsec_verify,
+ .generate_mac = true,
};
diff --git a/src/network/netdev/macvlan.c b/src/network/netdev/macvlan.c
index 871f0201a8..e13c3af9fd 100644
--- a/src/network/netdev/macvlan.c
+++ b/src/network/netdev/macvlan.c
@@ -61,6 +61,7 @@ const NetDevVTable macvtap_vtable = {
.sections = "Match\0NetDev\0MACVTAP\0",
.fill_message_create = netdev_macvlan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
+ .generate_mac = true,
};
const NetDevVTable macvlan_vtable = {
@@ -69,4 +70,5 @@ const NetDevVTable macvlan_vtable = {
.sections = "Match\0NetDev\0MACVLAN\0",
.fill_message_create = netdev_macvlan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
+ .generate_mac = true,
};
diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c
index 3968ab5495..5429237603 100644
--- a/src/network/netdev/netdev.c
+++ b/src/network/netdev/netdev.c
@@ -19,6 +19,7 @@
#include "netdev/macvlan.h"
#include "netdev/netdev.h"
#include "netdev/netdevsim.h"
+#include "netdev/nlmon.h"
#include "netdev/tunnel.h"
#include "netdev/tuntap.h"
#include "netdev/vcan.h"
@@ -70,6 +71,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_ERSPAN] = &erspan_vtable,
[NETDEV_KIND_L2TP] = &l2tptnl_vtable,
[NETDEV_KIND_MACSEC] = &macsec_vtable,
+ [NETDEV_KIND_NLMON] = &nlmon_vtable,
};
static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
@@ -104,6 +106,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_ERSPAN] = "erspan",
[NETDEV_KIND_L2TP] = "l2tp",
[NETDEV_KIND_MACSEC] = "macsec",
+ [NETDEV_KIND_NLMON] = "nlmon",
};
DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
@@ -734,7 +737,7 @@ int netdev_load_one(Manager *manager, const char *filename) {
if (!netdev->filename)
return log_oom();
- if (!netdev->mac && !IN_SET(netdev->kind, NETDEV_KIND_VLAN, NETDEV_KIND_BRIDGE)) {
+ if (!netdev->mac && NETDEV_VTABLE(netdev)->generate_mac) {
r = netdev_get_mac(netdev->ifname, &netdev->mac);
if (r < 0)
return log_netdev_error_errno(netdev, r,
diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h
index dcf072ce94..57fabbef40 100644
--- a/src/network/netdev/netdev.h
+++ b/src/network/netdev/netdev.h
@@ -49,6 +49,7 @@ typedef enum NetDevKind {
NETDEV_KIND_ERSPAN,
NETDEV_KIND_L2TP,
NETDEV_KIND_MACSEC,
+ NETDEV_KIND_NLMON,
_NETDEV_KIND_MAX,
_NETDEV_KIND_TUNNEL, /* Used by config_parse_stacked_netdev() */
_NETDEV_KIND_INVALID = -1
@@ -131,6 +132,9 @@ typedef struct NetDevVTable {
/* verify that compulsory configuration options were specified */
int (*config_verify)(NetDev *netdev, const char *filename);
+
+ /* Generate MAC address or not When MACAddress= is not specified. */
+ bool generate_mac;
} NetDevVTable;
extern const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX];
diff --git a/src/network/netdev/netdevsim.c b/src/network/netdev/netdevsim.c
index 8caba67600..3b9d875183 100644
--- a/src/network/netdev/netdevsim.c
+++ b/src/network/netdev/netdevsim.c
@@ -7,4 +7,5 @@ const NetDevVTable netdevsim_vtable = {
.object_size = sizeof(NetDevSim),
.sections = "Match\0NetDev\0",
.create_type = NETDEV_CREATE_INDEPENDENT,
+ .generate_mac = true,
};
diff --git a/src/network/netdev/nlmon.c b/src/network/netdev/nlmon.c
new file mode 100644
index 0000000000..c58a6b51ca
--- /dev/null
+++ b/src/network/netdev/nlmon.c
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "netdev/nlmon.h"
+
+static int netdev_nlmon_verify(NetDev *netdev, const char *filename) {
+ assert(netdev);
+ assert(filename);
+
+ if (netdev->mac) {
+ log_netdev_warning(netdev, "%s: MACAddress= is not supported. Ignoring", filename);
+ netdev->mac = mfree(netdev->mac);
+ }
+
+ return 0;
+}
+
+const NetDevVTable nlmon_vtable = {
+ .object_size = sizeof(NLMon),
+ .sections = "Match\0NetDev\0",
+ .create_type = NETDEV_CREATE_INDEPENDENT,
+ .config_verify = netdev_nlmon_verify,
+};
diff --git a/src/network/netdev/nlmon.h b/src/network/netdev/nlmon.h
new file mode 100644
index 0000000000..590b62d863
--- /dev/null
+++ b/src/network/netdev/nlmon.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+typedef struct NLMon NLMon;
+
+#include "netdev/netdev.h"
+
+struct NLMon {
+ NetDev meta;
+};
+
+DEFINE_NETDEV_CAST(NLMON, NLMon);
+
+extern const NetDevVTable nlmon_vtable;
diff --git a/src/network/netdev/tunnel.c b/src/network/netdev/tunnel.c
index e69a009c31..1572836b69 100644
--- a/src/network/netdev/tunnel.c
+++ b/src/network/netdev/tunnel.c
@@ -778,6 +778,7 @@ const NetDevVTable ipip_vtable = {
.fill_message_create = netdev_ipip_sit_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_tunnel_verify,
+ .generate_mac = true,
};
const NetDevVTable sit_vtable = {
@@ -787,6 +788,7 @@ const NetDevVTable sit_vtable = {
.fill_message_create = netdev_ipip_sit_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_tunnel_verify,
+ .generate_mac = true,
};
const NetDevVTable vti_vtable = {
@@ -796,6 +798,7 @@ const NetDevVTable vti_vtable = {
.fill_message_create = netdev_vti_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_tunnel_verify,
+ .generate_mac = true,
};
const NetDevVTable vti6_vtable = {
@@ -805,6 +808,7 @@ const NetDevVTable vti6_vtable = {
.fill_message_create = netdev_vti_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_tunnel_verify,
+ .generate_mac = true,
};
const NetDevVTable gre_vtable = {
@@ -814,6 +818,7 @@ const NetDevVTable gre_vtable = {
.fill_message_create = netdev_gre_erspan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_tunnel_verify,
+ .generate_mac = true,
};
const NetDevVTable gretap_vtable = {
@@ -823,6 +828,7 @@ const NetDevVTable gretap_vtable = {
.fill_message_create = netdev_gre_erspan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_tunnel_verify,
+ .generate_mac = true,
};
const NetDevVTable ip6gre_vtable = {
@@ -832,6 +838,7 @@ const NetDevVTable ip6gre_vtable = {
.fill_message_create = netdev_ip6gre_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_tunnel_verify,
+ .generate_mac = true,
};
const NetDevVTable ip6gretap_vtable = {
@@ -841,6 +848,7 @@ const NetDevVTable ip6gretap_vtable = {
.fill_message_create = netdev_ip6gre_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_tunnel_verify,
+ .generate_mac = true,
};
const NetDevVTable ip6tnl_vtable = {
@@ -850,6 +858,7 @@ const NetDevVTable ip6tnl_vtable = {
.fill_message_create = netdev_ip6tnl_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_tunnel_verify,
+ .generate_mac = true,
};
const NetDevVTable erspan_vtable = {
@@ -859,4 +868,5 @@ const NetDevVTable erspan_vtable = {
.fill_message_create = netdev_gre_erspan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_tunnel_verify,
+ .generate_mac = true,
};
diff --git a/src/network/netdev/vcan.c b/src/network/netdev/vcan.c
index 574d1cad31..f6a4dbb098 100644
--- a/src/network/netdev/vcan.c
+++ b/src/network/netdev/vcan.c
@@ -6,4 +6,5 @@ const NetDevVTable vcan_vtable = {
.object_size = sizeof(VCan),
.sections = "Match\0NetDev\0",
.create_type = NETDEV_CREATE_INDEPENDENT,
+ .generate_mac = true,
};
diff --git a/src/network/netdev/veth.c b/src/network/netdev/veth.c
index a0a0b8e39e..e996b49b0a 100644
--- a/src/network/netdev/veth.c
+++ b/src/network/netdev/veth.c
@@ -92,4 +92,5 @@ const NetDevVTable veth_vtable = {
.fill_message_create = netdev_veth_fill_message_create,
.create_type = NETDEV_CREATE_INDEPENDENT,
.config_verify = netdev_veth_verify,
+ .generate_mac = true,
};
diff --git a/src/network/netdev/vrf.c b/src/network/netdev/vrf.c
index 5efab537cc..596a91453c 100644
--- a/src/network/netdev/vrf.c
+++ b/src/network/netdev/vrf.c
@@ -30,4 +30,5 @@ const NetDevVTable vrf_vtable = {
.sections = "Match\0NetDev\0VRF\0",
.fill_message_create = netdev_vrf_fill_message_create,
.create_type = NETDEV_CREATE_MASTER,
+ .generate_mac = true,
};
diff --git a/src/network/netdev/vxcan.c b/src/network/netdev/vxcan.c
index c5dd5df51c..79f47690ad 100644
--- a/src/network/netdev/vxcan.c
+++ b/src/network/netdev/vxcan.c
@@ -70,4 +70,5 @@ const NetDevVTable vxcan_vtable = {
.fill_message_create = netdev_vxcan_fill_message_create,
.create_type = NETDEV_CREATE_INDEPENDENT,
.config_verify = netdev_vxcan_verify,
+ .generate_mac = true,
};
diff --git a/src/network/netdev/vxlan.c b/src/network/netdev/vxlan.c
index e24537083b..bba58e8aaf 100644
--- a/src/network/netdev/vxlan.c
+++ b/src/network/netdev/vxlan.c
@@ -380,4 +380,5 @@ const NetDevVTable vxlan_vtable = {
.fill_message_create = netdev_vxlan_fill_message_create,
.create_type = NETDEV_CREATE_STACKED,
.config_verify = netdev_vxlan_verify,
+ .generate_mac = true,
};
diff --git a/src/network/netdev/wireguard.c b/src/network/netdev/wireguard.c
index 91fcee1401..154ca3b7a4 100644
--- a/src/network/netdev/wireguard.c
+++ b/src/network/netdev/wireguard.c
@@ -978,4 +978,5 @@ const NetDevVTable wireguard_vtable = {
.done = wireguard_done,
.create_type = NETDEV_CREATE_INDEPENDENT,
.config_verify = wireguard_verify,
+ .generate_mac = true,
};
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 924cc8ce6a..9486329b9a 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -132,7 +132,7 @@ bool link_ipv4ll_enabled(Link *link, AddressFamilyBoolean mask) {
if (!link->network)
return false;
- if (STRPTR_IN_SET(link->kind, "vrf", "wireguard", "ipip", "gre", "ip6gre", "ip6tnl", "sit", "vti", "vti6", "can", "vcan", "vxcan"))
+ if (STRPTR_IN_SET(link->kind, "vrf", "wireguard", "ipip", "gre", "ip6gre", "ip6tnl", "sit", "vti", "vti6", "can", "vcan", "vxcan", "nlmon"))
return false;
/* L3 or L3S mode do not support ARP. */
@@ -142,6 +142,9 @@ bool link_ipv4ll_enabled(Link *link, AddressFamilyBoolean mask) {
if (link->network->bond)
return false;
+ if (link->network->bond)
+ return false;
+
return link->network->link_local & mask;
}
@@ -157,7 +160,7 @@ static bool link_ipv6ll_enabled(Link *link) {
if (!link->network)
return false;
- if (STRPTR_IN_SET(link->kind, "vrf", "wireguard", "ipip", "gre", "sit", "vti", "can", "vcan", "vxcan"))
+ if (STRPTR_IN_SET(link->kind, "vrf", "wireguard", "ipip", "gre", "sit", "vti", "can", "vcan", "vxcan", "nlmon"))
return false;
if (link->network->bond)
diff --git a/test/test-network/conf/25-nlmon.netdev b/test/test-network/conf/25-nlmon.netdev
new file mode 100644
index 0000000000..2ae6b12425
--- /dev/null
+++ b/test/test-network/conf/25-nlmon.netdev
@@ -0,0 +1,3 @@
+[NetDev]
+Kind=nlmon
+Name=nlmon99
diff --git a/test/test-network/conf/25-nlmon.network b/test/test-network/conf/25-nlmon.network
new file mode 100644
index 0000000000..8247f65307
--- /dev/null
+++ b/test/test-network/conf/25-nlmon.network
@@ -0,0 +1,6 @@
+[Match]
+Name=nlmon99
+
+[Network]
+LinkLocalAddressing=yes
+IPv6AcceptRA=no
diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py
index c0d7c9cbe0..cd99e5787a 100755
--- a/test/test-network/systemd-networkd-tests.py
+++ b/test/test-network/systemd-networkd-tests.py
@@ -274,6 +274,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'isataptun99',
'macvlan99',
'macvtap99',
+ 'nlmon99',
'sittun96',
'sittun97',
'sittun98',
@@ -344,6 +345,8 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
'25-macsec.key',
'25-macsec.netdev',
'25-macsec.network',
+ '25-nlmon.netdev',
+ '25-nlmon.network',
'25-sit-tunnel-local-any.netdev',
'25-sit-tunnel-remote-any.netdev',
'25-sit-tunnel.netdev',
@@ -1004,6 +1007,11 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
self.assertRegex(output, 'RXSC: 8c16456c83a90002, state on')
self.assertRegex(output, '0: PN [0-9]*, state off, key 02030400000000000000000000000000')
+ def test_nlmon(self):
+ self.copy_unit_to_networkd_unit_path('25-nlmon.netdev', '25-nlmon.network')
+ self.start_networkd()
+
+ self.wait_online(['nlmon99:carrier'])
class NetworkdL2TPTests(unittest.TestCase, Utilities):