1
0
mirror of https://github.com/systemd/systemd synced 2026-03-25 16:25:04 +01:00

Compare commits

...

15 Commits

Author SHA1 Message Date
Lennart Poettering
dc131951b5
Merge pull request #18385 from kinvolk/mauricio/restrict-network-interfaces
Add RestrictNetworkInterfaces=
2021-08-20 03:41:11 +02:00
Daan De Meyer
6c35ea5ef0 udev: Add support for configuring nic coalescing settings
These are configured via the corresponding ethtool ioctl.
2021-08-20 00:32:28 +01:00
Luca Boccassi
a622c58993
Merge pull request #20486 from DaanDeMeyer/sd-bus-eproto
sd-bus: Return detailed (sd-buscntr) error from bus_container_connect_socket().
2021-08-19 23:32:34 +01:00
Daan De Meyer
e4cdadf3bd sd-bus: Improve (sd-buscntr) error logging
We're only doing one thing in the child process which is connecting
to the D-Bus socket so let's mention that in the error message when
something goes wrong instead of having a generic error message.
2021-08-19 15:47:27 +01:00
Daan De Meyer
405a028e65 sd-bus: Return detailed (sd-buscntr) error from bus_container_connect_socket()
Previously, when the connect() call in (sd-buscntr) failed, we returned
-EPROTO without ever reading the actual errno from the error pipe. To fix
the issue, delay checking the process exit status until after we've read
and processed any error from the error pipe.
2021-08-19 15:47:24 +01:00
Mauricio Vásquez
43689840a2 README: add requirements for RestrictNetworkInterfaces=
Signed-off-by: Mauricio Vásquez <mauricio@kinvolk.io>
2021-08-19 07:25:01 -05:00
Mauricio Vásquez
7e959a73af systemctl: show RestrictNetworkInterfaces= in systemctl show
Signed-off-by: Mauricio Vásquez <mauricio@kinvolk.io>
2021-08-19 07:25:01 -05:00
Mauricio Vásquez
00d6fceeb3 tests: add integration test for RestrictNetworkInterfaces=
Signed-off-by: Mauricio Vásquez <mauricio@kinvolk.io>
2021-08-19 07:25:01 -05:00
Mauricio Vásquez
2ce150f5ec src/test: add restrict network interfaces to test-cgroup-mask
Signed-off-by: Mauricio Vásquez <mauricio@kinvolk.io>
2021-08-18 15:55:54 -05:00
Mauricio Vásquez
795ccb03e0 man: add RestrictNetworkInterfaces= documentation
Signed-off-by: Mauricio Vásquez <mauricio@kinvolk.io>
2021-08-18 15:55:54 -05:00
Mauricio Vásquez
57585d5999 Document RestrictNetworkInterfaces dbus properties
Signed-off-by: Mauricio Vásquez <mauricio@kinvolk.io>
2021-08-18 15:55:53 -05:00
Mauricio Vásquez
a59cb62cf2 core: add D-bus properties for RestrictNetworkInterfaces=
Signed-off-by: Mauricio Vásquez <mauricio@kinvolk.io>
2021-08-18 15:55:53 -05:00
Mauricio Vásquez
4f0c25c794 core: add load fragment implementation for RestrictNetworkInterfaces=
Signed-off-by: Mauricio Vásquez <mauricio@kinvolk.io>
2021-08-18 15:55:53 -05:00
Mauricio Vásquez
6f50d4f7d6 core: implement RestrictNetworkInterfaces=
This commit introduces all the logic to load and attach the BPF
programs to restrict network interfaces when a unit specifying it is
loaded.

Signed-off-by: Mauricio Vásquez <mauricio@kinvolk.io>
2021-08-18 15:55:53 -05:00
Mauricio Vásquez
dc83b840d3 core: add RestrictNetworkInterfaces= BPF program source code
The code is composed by two BPF_PROG_TYPE_CGROUP_SKB programs that
are loaded in the cgroup inet ingress and egress hooks
(BPF_CGROUP_INET_{INGRESS|EGRESS}).

The decision to let a packet pass or not is based on a map that contains
the indexes of the interfaces.

Signed-off-by: Mauricio Vásquez <mauricio@kinvolk.io>
2021-08-18 15:55:53 -05:00
47 changed files with 1165 additions and 59 deletions

4
README
View File

@ -40,6 +40,7 @@ REQUIREMENTS:
Linux kernel >= 4.17 for cgroup-bpf socket address hooks
Linux kernel >= 5.3 for bounded-loops in BPF program
Linux kernel >= 5.4 for signed Verity images support
Linux kernel >= 5.7 for BPF links
Kernel Config Options:
CONFIG_DEVTMPFS
@ -108,7 +109,8 @@ REQUIREMENTS:
CONFIG_HAVE_EBPF_JIT
CONFIG_CGROUP_BPF
Required for SocketBind{Allow|Deny}= in resource control unit settings
Required for SocketBind{Allow|Deny}=, RestrictNetworkInterfaces= in
resource control unit settings
CONFIG_BPF
CONFIG_BPF_SYSCALL
CONFIG_BPF_JIT

View File

@ -2528,6 +2528,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
readonly a(iiqq) SocketBindAllow = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(iiqq) SocketBindDeny = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly (bas) RestrictNetworkInterfaces = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as Environment = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@ -3070,6 +3072,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<!--property SocketBindDeny is not documented!-->
<!--property RestrictNetworkInterfaces is not documented!-->
<!--property EnvironmentFiles is not documented!-->
<!--property PassEnvironment is not documented!-->
@ -3638,6 +3642,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
<variablelist class="dbus-property" generated="True" extra-ref="RestrictNetworkInterfaces"/>
<variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
@ -4340,6 +4346,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
readonly a(iiqq) SocketBindAllow = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(iiqq) SocketBindDeny = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly (bas) RestrictNetworkInterfaces = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as Environment = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@ -4910,6 +4918,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
<!--property SocketBindDeny is not documented!-->
<!--property RestrictNetworkInterfaces is not documented!-->
<!--property EnvironmentFiles is not documented!-->
<!--property PassEnvironment is not documented!-->
@ -5476,6 +5486,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
<variablelist class="dbus-property" generated="True" extra-ref="RestrictNetworkInterfaces"/>
<variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
@ -6075,6 +6087,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
readonly a(iiqq) SocketBindAllow = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(iiqq) SocketBindDeny = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly (bas) RestrictNetworkInterfaces = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as Environment = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@ -6573,6 +6587,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
<!--property SocketBindDeny is not documented!-->
<!--property RestrictNetworkInterfaces is not documented!-->
<!--property EnvironmentFiles is not documented!-->
<!--property PassEnvironment is not documented!-->
@ -7057,6 +7073,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
<variablelist class="dbus-property" generated="True" extra-ref="RestrictNetworkInterfaces"/>
<variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
@ -7777,6 +7795,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
readonly a(iiqq) SocketBindAllow = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(iiqq) SocketBindDeny = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly (bas) RestrictNetworkInterfaces = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as Environment = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@ -8261,6 +8281,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
<!--property SocketBindDeny is not documented!-->
<!--property RestrictNetworkInterfaces is not documented!-->
<!--property EnvironmentFiles is not documented!-->
<!--property PassEnvironment is not documented!-->
@ -8731,6 +8753,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
<variablelist class="dbus-property" generated="True" extra-ref="RestrictNetworkInterfaces"/>
<variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
@ -9304,6 +9328,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
readonly a(iiqq) SocketBindAllow = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(iiqq) SocketBindDeny = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly (bas) RestrictNetworkInterfaces = ...;
};
interface org.freedesktop.DBus.Peer { ... };
interface org.freedesktop.DBus.Introspectable { ... };
@ -9448,6 +9474,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
<!--property SocketBindDeny is not documented!-->
<!--property RestrictNetworkInterfaces is not documented!-->
<!--Autogenerated cross-references for systemd.directives, do not edit-->
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
@ -9598,6 +9626,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
<variablelist class="dbus-property" generated="True" extra-ref="RestrictNetworkInterfaces"/>
<!--End of Autogenerated section-->
<refsect2>
@ -9767,6 +9797,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
readonly a(iiqq) SocketBindAllow = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly a(iiqq) SocketBindDeny = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly (bas) RestrictNetworkInterfaces = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s KillMode = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@ -9927,6 +9959,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
<!--property SocketBindDeny is not documented!-->
<!--property RestrictNetworkInterfaces is not documented!-->
<!--property KillMode is not documented!-->
<!--property KillSignal is not documented!-->
@ -10103,6 +10137,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
<variablelist class="dbus-property" generated="True" extra-ref="RestrictNetworkInterfaces"/>
<variablelist class="dbus-property" generated="True" extra-ref="KillMode"/>
<variablelist class="dbus-property" generated="True" extra-ref="KillSignal"/>

View File

@ -773,6 +773,77 @@
accept. An unsigned integer in the range 1…65535. Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>UseAdaptiveRxCoalesce=</varname></term>
<term><varname>UseAdaptiveTxCoalesce=</varname></term>
<listitem>
<para>Boolean properties that, when set, enable/disable adaptive Rx/Tx coalescing if the hardware
supports it. When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>RxCoalesceSec=</varname></term>
<term><varname>RxCoalesceIrqSec=</varname></term>
<term><varname>RxCoalesceLowSec=</varname></term>
<term><varname>RxCoalesceHighSec=</varname></term>
<term><varname>TxCoalesceSec=</varname></term>
<term><varname>TxCoalesceIrqSec=</varname></term>
<term><varname>TxCoalesceLowSec=</varname></term>
<term><varname>TxCoalesceHighSec=</varname></term>
<listitem>
<para>These properties configure the delay before Rx/Tx interrupts are generated after a packet is
sent/received. The <literal>Irq</literal> properties come into effect when the host is servicing an
IRQ. The <literal>Low</literal> and <literal>High</literal> properties come into effect when the
packet rate drops below the low packet rate threshold or exceeds the high packet rate threshold
respectively if adaptive Rx/Tx coalescing is enabled. When unset, the kernel's defaults will be
used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>RxMaxCoalescedFrames=</varname></term>
<term><varname>RxMaxCoalescedIrqFrames=</varname></term>
<term><varname>RxMaxCoalescedLowFrames=</varname></term>
<term><varname>RxMaxCoalescedHighFrames=</varname></term>
<term><varname>TxMaxCoalescedFrames=</varname></term>
<term><varname>TxMaxCoalescedIrqFrames=</varname></term>
<term><varname>TxMaxCoalescedLowFrames=</varname></term>
<term><varname>TxMaxCoalescedHighFrames=</varname></term>
<listitem>
<para>These properties configure the maximum number of frames that are sent/received before a Rx/Tx
interrupt is generated. The <literal>Irq</literal> properties come into effect when the host is
servicing an IRQ. The <literal>Low</literal> and <literal>High</literal> properties come into
effect when the packet rate drops below the low packet rate threshold or exceeds the high packet
rate threshold respectively if adaptive Rx/Tx coalescing is enabled. When unset, the kernel's
defaults will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>CoalescePacketRateLow=</varname></term>
<term><varname>CoalescePacketRateHigh=</varname></term>
<listitem>
<para>These properties configure the low and high packet rate (expressed in packets per second)
threshold respectively and are used to determine when the corresponding coalescing settings for low
and high packet rates come into effect if adaptive Rx/Tx coalescing is enabled. If unset, the
kernel's defaults will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>CoalescePacketRateSampleIntervalSec=</varname></term>
<listitem>
<para>Configures how often to sample the packet rate used for adaptive Rx/Tx coalescing. This
property cannot be zero. This lowest time granularity supported by this property is seconds.
Partial seconds will be rounded up before being passed to the kernel. If unset, the kernel's
default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>StatisticsBlockCoalesceSec=</varname></term>
<listitem>
<para>How long to delay driver in-memory statistics block updates. If the driver does not have an
in-memory statistic block, this property is ignored. This property cannot be zero. If unset, the
kernel's default will be used.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@ -855,6 +855,52 @@ SocketBindDeny=any
</listitem>
</varlistentry>
<varlistentry>
<term><varname>RestrictNetworkInterfaces=</varname></term>
<listitem>
<para>Takes a list of space-separated network interface names. This option restricts the network
interfaces that processes of this unit can use. By default processes can only use the network interfaces
listed (allow-list). If the first character of the rule is <literal>~</literal>, the effect is inverted:
the processes can only use network interfaces not listed (deny-list).
</para>
<para>This option can appear multiple times, in which case the network interface names are merged. If the
empty string is assigned the set is reset, all prior assigments will have not effect.
</para>
<para>If you specify both types of this option (i.e. allow-listing and deny-listing), the first encountered
will take precedence and will dictate the default action (allow vs deny). Then the next occurrences of this
option will add or delete the listed network interface names from the set, depending of its type and the
default action.
</para>
<para>The loopback interface ("lo") is not treated in any special way, you have to configure it explicitly
in the unit file.
</para>
<para>Example 1: allow-list
<programlisting>
RestrictNetworkInterfaces=eth1
RestrictNetworkInterfaces=eth2</programlisting>
Programs in the unit will be only able to use the eth1 and eth2 network
interfaces.
</para>
<para>Example 2: deny-list
<programlisting>
RestrictNetworkInterfaces=~eth1 eth2</programlisting>
Programs in the unit will be able to use any network interface but eth1 and eth2.
</para>
<para>Example 3: mixed
<programlisting>
RestrictNetworkInterfaces=eth1 eth2
RestrictNetworkInterfaces=~eth1</programlisting>
Programs in the unit will be only able to use the eth2 network interface.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>DeviceAllow=</varname></term>

View File

@ -2214,6 +2214,7 @@ static const char *const cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
[CGROUP_CONTROLLER_BPF_DEVICES] = "bpf-devices",
[CGROUP_CONTROLLER_BPF_FOREIGN] = "bpf-foreign",
[CGROUP_CONTROLLER_BPF_SOCKET_BIND] = "bpf-socket-bind",
[CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES] = "bpf-restrict-network-interfaces",
};
DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);

View File

@ -32,6 +32,7 @@ typedef enum CGroupController {
CGROUP_CONTROLLER_BPF_DEVICES,
CGROUP_CONTROLLER_BPF_FOREIGN,
CGROUP_CONTROLLER_BPF_SOCKET_BIND,
CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES,
_CGROUP_CONTROLLER_MAX,
_CGROUP_CONTROLLER_INVALID = -EINVAL,
@ -53,6 +54,7 @@ typedef enum CGroupMask {
CGROUP_MASK_BPF_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_DEVICES),
CGROUP_MASK_BPF_FOREIGN = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FOREIGN),
CGROUP_MASK_BPF_SOCKET_BIND = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_SOCKET_BIND),
CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES),
/* All real cgroup v1 controllers */
CGROUP_MASK_V1 = CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT|CGROUP_MASK_BLKIO|CGROUP_MASK_MEMORY|CGROUP_MASK_DEVICES|CGROUP_MASK_PIDS,
@ -61,7 +63,7 @@ typedef enum CGroupMask {
CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS,
/* All cgroup v2 BPF pseudo-controllers */
CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN|CGROUP_MASK_BPF_SOCKET_BIND,
CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN|CGROUP_MASK_BPF_SOCKET_BIND|CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES,
_CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
} CGroupMask;

View File

@ -0,0 +1,14 @@
# SPDX-License-Identifier: LGPL-2.1+
if conf.get('BPF_FRAMEWORK') == 1
restrict_ifaces_skel_h = custom_target(
'restrict-ifaces.skel.h',
input : 'restrict-ifaces.bpf.c',
output : 'restrict-ifaces.skel.h',
command : [build_bpf_skel_py,
'--clang_exec', clang.path(),
'--llvm_strip_exec', llvm_strip.path(),
'--bpftool_exec', bpftool.path(),
'--arch', host_machine.cpu_family(),
'@INPUT@', '@OUTPUT@'])
endif

View File

@ -0,0 +1,52 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/* <linux/bpf.h> must precede <bpf/bpf_helpers.h> due to integer types
* in bpf helpers signatures.
*/
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
const volatile __u8 is_allow_list = 0;
/* Map containing the network interfaces indexes.
* The interpretation of the map depends on the value of is_allow_list.
*/
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, __u32);
__type(value, __u8);
} sd_restrictif SEC(".maps");
#define DROP 0
#define PASS 1
static inline int restrict_network_interfaces_impl(const struct __sk_buff *sk) {
__u32 zero = 0, ifindex;
__u8 *lookup_result;
ifindex = sk->ifindex;
lookup_result = bpf_map_lookup_elem(&sd_restrictif, &ifindex);
if (is_allow_list) {
/* allow-list: let the packet pass if iface in the list */
if (lookup_result)
return PASS;
} else {
/* deny-list: let the packet pass if iface *not* in the list */
if (!lookup_result)
return PASS;
}
return DROP;
}
SEC("cgroup_skb/egress")
int sd_restrictif_e(const struct __sk_buff *sk) {
return restrict_network_interfaces_impl(sk);
}
SEC("cgroup_skb/ingress")
int sd_restrictif_i(const struct __sk_buff *sk) {
return restrict_network_interfaces_impl(sk);
}
static const char _license[] SEC("license") = "LGPL-2.1-or-later";

View File

@ -28,6 +28,7 @@
#include "percent-util.h"
#include "process-util.h"
#include "procfs-util.h"
#include "restrict-ifaces.h"
#include "special.h"
#include "stat-util.h"
#include "stdio-util.h"
@ -246,6 +247,8 @@ void cgroup_context_done(CGroupContext *c) {
while (c->bpf_foreign_programs)
cgroup_context_remove_bpf_foreign_program(c, c->bpf_foreign_programs);
c->restrict_network_interfaces = set_free(c->restrict_network_interfaces);
cpu_set_reset(&c->cpuset_cpus);
cpu_set_reset(&c->cpuset_mems);
}
@ -583,6 +586,12 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
cgroup_context_dump_socket_bind_item(bi, f);
fputc('\n', f);
}
if (c->restrict_network_interfaces) {
char *iface;
SET_FOREACH(iface, c->restrict_network_interfaces)
fprintf(f, "%sRestrictNetworkInterfaces: %s\n", prefix, iface);
}
}
void cgroup_context_dump_socket_bind_item(const CGroupSocketBindItem *item, FILE *f) {
@ -1097,6 +1106,12 @@ static void cgroup_apply_socket_bind(Unit *u) {
(void) bpf_socket_bind_install(u);
}
static void cgroup_apply_restrict_network_interfaces(Unit *u) {
assert(u);
(void) restrict_network_interfaces_install(u);
}
static int cgroup_apply_devices(Unit *u) {
_cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
const char *path;
@ -1527,6 +1542,9 @@ static void cgroup_context_apply(
if (apply_mask & CGROUP_MASK_BPF_SOCKET_BIND)
cgroup_apply_socket_bind(u);
if (apply_mask & CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES)
cgroup_apply_restrict_network_interfaces(u);
}
static bool unit_get_needs_bpf_firewall(Unit *u) {
@ -1580,6 +1598,17 @@ static bool unit_get_needs_socket_bind(Unit *u) {
return c->socket_bind_allow || c->socket_bind_deny;
}
static bool unit_get_needs_restrict_network_interfaces(Unit *u) {
CGroupContext *c;
assert(u);
c = unit_get_cgroup_context(u);
if (!c)
return false;
return !set_isempty(c->restrict_network_interfaces);
}
static CGroupMask unit_get_cgroup_mask(Unit *u) {
CGroupMask mask = 0;
CGroupContext *c;
@ -1635,6 +1664,9 @@ static CGroupMask unit_get_bpf_mask(Unit *u) {
if (unit_get_needs_socket_bind(u))
mask |= CGROUP_MASK_BPF_SOCKET_BIND;
if (unit_get_needs_restrict_network_interfaces(u))
mask |= CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES;
return mask;
}
@ -3132,6 +3164,11 @@ static int cg_bpf_mask_supported(CGroupMask *ret) {
if (r > 0)
mask |= CGROUP_MASK_BPF_SOCKET_BIND;
/* BPF-based cgroup_skb/{egress|ingress} hooks */
r = restrict_network_interfaces_supported();
if (r > 0)
mask |= CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES;
*ret = mask;
return 0;
}

View File

@ -160,6 +160,9 @@ struct CGroupContext {
char **ip_filters_egress;
LIST_HEAD(CGroupBPFForeignProgram, bpf_foreign_programs);
Set *restrict_network_interfaces;
bool restrict_network_interfaces_is_allow_list;
/* For legacy hierarchies */
uint64_t cpu_shares;
uint64_t startup_cpu_shares;

View File

@ -20,6 +20,7 @@
#include "parse-util.h"
#include "path-util.h"
#include "percent-util.h"
#include "socket-util.h"
BUS_DEFINE_PROPERTY_GET(bus_property_get_tasks_max, "t", TasksMax, tasks_max_resolve);
@ -403,6 +404,47 @@ static int property_get_socket_bind(
return sd_bus_message_close_container(reply);
}
static int property_get_restrict_network_interfaces(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
int r;
CGroupContext *c = userdata;
char *iface;
assert(bus);
assert(reply);
assert(c);
r = sd_bus_message_open_container(reply, 'r', "bas");
if (r < 0)
return r;
r = sd_bus_message_append(reply, "b", c->restrict_network_interfaces_is_allow_list);
if (r < 0)
return r;
r = sd_bus_message_open_container(reply, 'a', "s");
if (r < 0)
return r;
SET_FOREACH(iface, c->restrict_network_interfaces) {
r = sd_bus_message_append(reply, "s", iface);
if (r < 0)
return r;
}
r = sd_bus_message_close_container(reply);
if (r < 0)
return r;
return sd_bus_message_close_container(reply);
}
const sd_bus_vtable bus_cgroup_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Delegate", "b", bus_property_get_bool, offsetof(CGroupContext, delegate), 0),
@ -457,6 +499,7 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
SD_BUS_PROPERTY("BPFProgram", "a(ss)", property_get_bpf_foreign_program, 0, 0),
SD_BUS_PROPERTY("SocketBindAllow", "a(iiqq)", property_get_socket_bind, offsetof(CGroupContext, socket_bind_allow), 0),
SD_BUS_PROPERTY("SocketBindDeny", "a(iiqq)", property_get_socket_bind, offsetof(CGroupContext, socket_bind_deny), 0),
SD_BUS_PROPERTY("RestrictNetworkInterfaces", "(bas)", property_get_restrict_network_interfaces, 0, 0),
SD_BUS_VTABLE_END
};
@ -1963,6 +2006,64 @@ int bus_cgroup_set_property(
return 1;
}
if (streq(name, "RestrictNetworkInterfaces")) {
int is_allow_list;
_cleanup_strv_free_ char **l = NULL;
r = sd_bus_message_enter_container(message, 'r', "bas");
if (r < 0)
return r;
r = sd_bus_message_read(message, "b", &is_allow_list);
if (r < 0)
return r;
r = sd_bus_message_read_strv(message, &l);
if (r < 0)
return r;
r = sd_bus_message_exit_container(message);
if (r < 0)
return r;
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
_cleanup_free_ char *joined = NULL;
char **s;
if (strv_isempty(l)) {
c->restrict_network_interfaces_is_allow_list = false;
c->restrict_network_interfaces = set_free(c->restrict_network_interfaces);
unit_write_settingf(u, flags, name, "%s=", name);
return 1;
}
if (set_isempty(c->restrict_network_interfaces))
c->restrict_network_interfaces_is_allow_list = is_allow_list;
STRV_FOREACH(s, l) {
if (!ifname_valid(*s)) {
log_full(LOG_WARNING, "Invalid interface name, ignoring: %s", *s);
continue;
}
if (c->restrict_network_interfaces_is_allow_list != (bool) is_allow_list)
free(set_remove(c->restrict_network_interfaces, *s));
else {
r = set_put_strdup(&c->restrict_network_interfaces, *s);
if (r < 0)
return log_oom();
}
}
joined = strv_join(l, " ");
if (!joined)
return -ENOMEM;
unit_write_settingf(u, flags, name, "%s=%s%s", name, is_allow_list ? "" : "~", joined);
}
return 1;
}
if (streq(name, "DisableControllers") || (u->transient && u->load_state == UNIT_STUB))
return bus_cgroup_set_transient_property(u, c, name, message, flags, error);

View File

@ -233,6 +233,7 @@
{{type}}.BPFProgram, config_parse_bpf_foreign_program, 0, offsetof({{type}}, cgroup_context)
{{type}}.SocketBindAllow, config_parse_cgroup_socket_bind, 0, offsetof({{type}}, cgroup_context.socket_bind_allow)
{{type}}.SocketBindDeny, config_parse_cgroup_socket_bind, 0, offsetof({{type}}, cgroup_context.socket_bind_deny)
{{type}}.RestrictNetworkInterfaces, config_parse_restrict_network_interfaces, 0, offsetof({{type}}, cgroup_context)
{%- endmacro -%}
%{

View File

@ -5711,6 +5711,72 @@ int config_parse_cgroup_socket_bind(
return 0;
}
int config_parse_restrict_network_interfaces(
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) {
CGroupContext *c = data;
bool is_allow_rule = true;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
if (isempty(rvalue)) {
/* Empty assignment resets the list */
c->restrict_network_interfaces = set_free(c->restrict_network_interfaces);
return 0;
}
if (rvalue[0] == '~') {
is_allow_rule = false;
rvalue++;
}
if (set_isempty(c->restrict_network_interfaces))
/* Only initialize this when creating the set */
c->restrict_network_interfaces_is_allow_list = is_allow_rule;
for (const char *p = rvalue;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
if (r == 0)
break;
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Trailing garbage in %s, ignoring: %s", lvalue, rvalue);
break;
}
if (!ifname_valid(word)) {
log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid interface name, ignoring: %s", word);
continue;
}
if (c->restrict_network_interfaces_is_allow_list != is_allow_rule)
free(set_remove(c->restrict_network_interfaces, word));
else {
r = set_put_strdup(&c->restrict_network_interfaces, word);
if (r < 0)
return log_oom();
}
}
return 0;
}
static int merge_by_names(Unit **u, Set *names, const char *id) {
char *k;
int r;

View File

@ -141,6 +141,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_socket_timestamping);
CONFIG_PARSER_PROTOTYPE(config_parse_extension_images);
CONFIG_PARSER_PROTOTYPE(config_parse_bpf_foreign_program);
CONFIG_PARSER_PROTOTYPE(config_parse_cgroup_socket_bind);
CONFIG_PARSER_PROTOTYPE(config_parse_restrict_network_interfaces);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);

View File

@ -97,6 +97,8 @@ libcore_sources = '''
namespace.h
path.c
path.h
restrict-ifaces.c
restrict-ifaces.h
scope.c
scope.h
selinux-access.c
@ -136,6 +138,11 @@ if conf.get('BPF_FRAMEWORK') == 1
libcore_sources += [socket_bind_skel_h]
endif
subdir('bpf/restrict_ifaces')
if conf.get('BPF_FRAMEWORK') == 1
libcore_sources += [restrict_ifaces_skel_h]
endif
load_fragment_gperf_gperf = custom_target(
'load-fragment-gperf.gperf',
input : 'load-fragment-gperf.gperf.in',

205
src/core/restrict-ifaces.c Normal file
View File

@ -0,0 +1,205 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "fd-util.h"
#include "restrict-ifaces.h"
#include "netlink-util.h"
#if BPF_FRAMEWORK
/* libbpf, clang and llc compile time dependencies are satisfied */
#include "bpf-dlopen.h"
#include "bpf-link.h"
#include "bpf/restrict_ifaces/restrict-ifaces.skel.h"
static struct restrict_ifaces_bpf *restrict_ifaces_bpf_free(struct restrict_ifaces_bpf *obj) {
restrict_ifaces_bpf__destroy(obj);
return NULL;
}
DEFINE_TRIVIAL_CLEANUP_FUNC(struct restrict_ifaces_bpf *, restrict_ifaces_bpf_free);
static int prepare_restrict_ifaces_bpf(Unit* u, bool is_allow_list,
const Set *restrict_network_interfaces,
struct restrict_ifaces_bpf **ret_object) {
_cleanup_(restrict_ifaces_bpf_freep) struct restrict_ifaces_bpf *obj = NULL;
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
char *iface;
int r, map_fd;
assert(ret_object);
obj = restrict_ifaces_bpf__open();
if (!obj)
return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOMEM), "Failed to open BPF object");
r = sym_bpf_map__resize(obj->maps.sd_restrictif, MAX(set_size(restrict_network_interfaces), 1u));
if (r != 0)
return log_unit_error_errno(u, r,
"Failed to resize BPF map '%s': %m",
sym_bpf_map__name(obj->maps.sd_restrictif));
obj->rodata->is_allow_list = is_allow_list;
r = restrict_ifaces_bpf__load(obj);
if (r != 0)
return log_unit_error_errno(u, r, "Failed to load BPF object: %m");
map_fd = sym_bpf_map__fd(obj->maps.sd_restrictif);
SET_FOREACH(iface, restrict_network_interfaces) {
uint8_t dummy = 0;
int ifindex;
ifindex = rtnl_resolve_interface(&rtnl, iface);
if (ifindex < 0) {
log_unit_warning_errno(u, ifindex, "Couldn't find index of network interface: %m. Ignoring '%s'", iface);
continue;
}
if (sym_bpf_map_update_elem(map_fd, &ifindex, &dummy, BPF_ANY))
return log_unit_error_errno(u, errno, "Failed to update BPF map '%s' fd: %m", sym_bpf_map__name(obj->maps.sd_restrictif));
}
*ret_object = TAKE_PTR(obj);
return 0;
}
int restrict_network_interfaces_supported(void) {
_cleanup_(restrict_ifaces_bpf_freep) struct restrict_ifaces_bpf *obj = NULL;
int r;
static int supported = -1;
if (supported >= 0)
return supported;
r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
if (r < 0) {
log_warning_errno(r, "Can't determine whether the unified hierarchy is used: %m");
supported = 0;
return supported;
}
if (r == 0) {
log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Not running with unified cgroup hierarchy, BPF is not supported");
supported = 0;
return supported;
}
if (dlopen_bpf() < 0)
return false;
if (!sym_bpf_probe_prog_type(BPF_PROG_TYPE_CGROUP_SKB, /*ifindex=*/0)) {
log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"BPF program type cgroup_skb is not supported");
supported = 0;
return supported;
}
r = prepare_restrict_ifaces_bpf(NULL, true, NULL, &obj);
if (r < 0)
return log_debug_errno(r, "Failed to load BPF object: %m");
supported = bpf_can_link_program(obj->progs.sd_restrictif_i);
return supported;
}
static int restrict_network_interfaces_install_impl(Unit *u) {
_cleanup_(bpf_link_freep) struct bpf_link *egress_link = NULL, *ingress_link = NULL;
_cleanup_(restrict_ifaces_bpf_freep) struct restrict_ifaces_bpf *obj = NULL;
_cleanup_free_ char *cgroup_path = NULL;
_cleanup_close_ int cgroup_fd = -1;
CGroupContext *cc;
int r;
cc = unit_get_cgroup_context(u);
if (!cc)
return 0;
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &cgroup_path);
if (r < 0)
return log_unit_error_errno(u, r, "Failed to get cgroup path: %m");
if (!cc->restrict_network_interfaces)
return 0;
r = prepare_restrict_ifaces_bpf(u,
cc->restrict_network_interfaces_is_allow_list,
cc->restrict_network_interfaces,
&obj);
if (r < 0)
return r;
cgroup_fd = open(cgroup_path, O_RDONLY | O_CLOEXEC | O_DIRECTORY, 0);
if (cgroup_fd < 0)
return -errno;
ingress_link = sym_bpf_program__attach_cgroup(obj->progs.sd_restrictif_i, cgroup_fd);
r = sym_libbpf_get_error(ingress_link);
if (r != 0)
return log_unit_error_errno(u, r, "Failed to create ingress cgroup link: %m");
egress_link = sym_bpf_program__attach_cgroup(obj->progs.sd_restrictif_e, cgroup_fd);
r = sym_libbpf_get_error(egress_link);
if (r != 0)
return log_unit_error_errno(u, r, "Failed to create egress cgroup link: %m");
u->restrict_ifaces_ingress_bpf_link = TAKE_PTR(ingress_link);
u->restrict_ifaces_egress_bpf_link = TAKE_PTR(egress_link);
return 0;
}
int restrict_network_interfaces_install(Unit *u) {
int r = restrict_network_interfaces_install_impl(u);
fdset_close(u->initial_restric_ifaces_link_fds);
return r;
}
int serialize_restrict_network_interfaces(Unit *u, FILE *f, FDSet *fds) {
int r;
assert(u);
r = bpf_serialize_link(f, fds, "restrict-ifaces-bpf-fd", u->restrict_ifaces_ingress_bpf_link);
if (r < 0)
return r;
return bpf_serialize_link(f, fds, "restrict-ifaces-bpf-fd", u->restrict_ifaces_egress_bpf_link);
}
int restrict_network_interfaces_add_initial_link_fd(Unit *u, int fd) {
int r;
assert(u);
if (!u->initial_restric_ifaces_link_fds) {
u->initial_restric_ifaces_link_fds = fdset_new();
if (!u->initial_restric_ifaces_link_fds)
return log_oom();
}
r = fdset_put(u->initial_restric_ifaces_link_fds, fd);
if (r < 0)
return log_unit_error_errno(u, r, "Failed to put restrict-ifaces-bpf-fd %d to restored fdset: %m", fd);
return 0;
}
#else /* ! BPF_FRAMEWORK */
int restrict_network_interfaces_supported(void) {
return 0;
}
int restrict_network_interfaces_install(Unit *u) {
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP),
"Failed to install RestrictInterfaces: BPF programs built from source code are not supported: %m");
}
int serialize_restrict_network_interfaces(Unit *u, FILE *f, FDSet *fds) {
return 0;
}
int restrict_network_interfaces_add_initial_link_fd(Unit *u, int fd) {
return 0;
}
#endif

View File

@ -0,0 +1,16 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include "fdset.h"
#include "unit.h"
typedef struct Unit Unit;
int restrict_network_interfaces_supported(void);
int restrict_network_interfaces_install(Unit *u);
int serialize_restrict_network_interfaces(Unit *u, FILE *f, FDSet *fds);
/* Add BPF link fd created before daemon-reload or daemon-reexec.
* FDs will be closed at the end of restrict_network_interfaces_install. */
int restrict_network_interfaces_add_initial_link_fd(Unit *u, int fd);

View File

@ -7,6 +7,7 @@
#include "fileio.h"
#include "format-util.h"
#include "parse-util.h"
#include "restrict-ifaces.h"
#include "serialize.h"
#include "string-table.h"
#include "unit-serialize.h"
@ -173,6 +174,8 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool switching_root) {
(void) bpf_program_serialize_attachment_set(f, fds, "ip-bpf-custom-ingress-installed", u->ip_bpf_custom_ingress_installed);
(void) bpf_program_serialize_attachment_set(f, fds, "ip-bpf-custom-egress-installed", u->ip_bpf_custom_egress_installed);
(void) serialize_restrict_network_interfaces(u, f, fds);
if (uid_is_valid(u->ref_uid))
(void) serialize_item_format(f, "ref-uid", UID_FMT, u->ref_uid);
if (gid_is_valid(u->ref_gid))
@ -413,6 +416,21 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
(void) bpf_program_deserialize_attachment_set(v, fds, &u->ip_bpf_custom_egress_installed);
continue;
} else if (streq(l, "restrict-ifaces-bpf-fd")) {
int fd;
if (safe_atoi(v, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) {
log_unit_debug(u, "Failed to parse restrict-ifaces-bpf-fd value: %s", v);
continue;
}
if (fdset_remove(fds, fd) < 0) {
log_unit_debug(u, "Failed to remove restrict-ifaces-bpf-fd %d from fdset", fd);
continue;
}
(void) restrict_network_interfaces_add_initial_link_fd(u, fd);
continue;
} else if (streq(l, "ref-uid")) {
uid_t uid;

View File

@ -766,6 +766,12 @@ Unit* unit_free(Unit *u) {
bpf_program_unref(u->bpf_device_control_installed);
#if BPF_FRAMEWORK
bpf_link_free(u->restrict_ifaces_ingress_bpf_link);
bpf_link_free(u->restrict_ifaces_egress_bpf_link);
#endif
fdset_free(u->initial_restric_ifaces_link_fds);
condition_free_list(u->conditions);
condition_free_list(u->asserts);

View File

@ -335,6 +335,12 @@ typedef struct Unit {
struct bpf_link *ipv6_socket_bind_link;
#endif
FDSet *initial_restric_ifaces_link_fds;
#if BPF_FRAMEWORK
struct bpf_link *restrict_ifaces_ingress_bpf_link;
struct bpf_link *restrict_ifaces_egress_bpf_link;
#endif
/* Low-priority event source which is used to remove watched PIDs that have gone away, and subscribe to any new
* ones which might have appeared. */
sd_event_source *rewatch_pids_event_source;

View File

@ -75,8 +75,7 @@ int bus_container_connect_socket(sd_bus *b) {
r = wait_for_terminate_and_check("(sd-buscntrns)", child, 0);
if (r < 0)
return r;
if (r != EXIT_SUCCESS)
return -EPROTO;
bool nonzero_exit_status = r != EXIT_SUCCESS;
n = read(pair[0], &error_buf, sizeof(error_buf));
if (n < 0)
@ -95,8 +94,11 @@ int bus_container_connect_socket(sd_bus *b) {
return 1;
if (error_buf > 0)
return log_debug_errno(error_buf, "Got error from (sd-buscntr): %m");
return log_debug_errno(error_buf, "(sd-buscntr) failed to connect to D-Bus socket: %m");
}
if (nonzero_exit_status)
return -EPROTO;
return bus_socket_start_auth(b);
}

View File

@ -1384,7 +1384,8 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
if (STR_IN_SET(field, "RestrictAddressFamilies",
"SystemCallFilter",
"SystemCallLog")) {
"SystemCallLog",
"RestrictNetworkInterfaces")) {
int allow_list = 1;
const char *p = eq;

View File

@ -14,6 +14,7 @@
#include "memory-util.h"
#include "socket-util.h"
#include "string-table.h"
#include "strv.h"
#include "strxcpyx.h"
static const char* const duplex_table[_DUP_MAX] = {
@ -1101,3 +1102,207 @@ int config_parse_wol(
return 0;
}
int config_parse_coalesce_u32(
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) {
u32_opt *dst = data;
uint32_t k;
int r;
if (isempty(rvalue)) {
dst->value = 0;
dst->set = false;
return 0;
}
r = safe_atou32(rvalue, &k);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to parse %s=, ignoring: %s", lvalue, rvalue);
return 0;
}
dst->value = k;
dst->set = true;
return 0;
}
int config_parse_coalesce_sec(
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) {
u32_opt *dst = data;
usec_t usec;
int r;
if (isempty(rvalue)) {
dst->value = 0;
dst->set = false;
return 0;
}
r = parse_sec(rvalue, &usec);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
"Failed to parse coalesce setting value, ignoring: %s", rvalue);
return 0;
}
if (usec > UINT32_MAX) {
log_syntax(unit, LOG_WARNING, filename, line, 0,
"Too large %s= value, ignoring: %s", lvalue, rvalue);
return 0;
}
if (STR_IN_SET(lvalue, "StatisticsBlockCoalesceSec", "CoalescePacketRateSampleIntervalSec") && usec < 1) {
log_syntax(unit, LOG_WARNING, filename, line, 0,
"Invalid %s= value, ignoring: %s", lvalue, rvalue);
return 0;
}
dst->value = (uint32_t) usec;
dst->set = true;
return 0;
}
int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const netdev_coalesce_param *coalesce) {
struct ethtool_coalesce ecmd = {
.cmd = ETHTOOL_GCOALESCE,
};
struct ifreq ifr = {
.ifr_data = (void*) &ecmd,
};
bool need_update = false;
int r;
assert(ethtool_fd);
assert(ifname);
assert(coalesce);
if (coalesce->use_adaptive_rx_coalesce < 0 &&
coalesce->use_adaptive_tx_coalesce < 0 &&
!coalesce->rx_coalesce_usecs.set &&
!coalesce->rx_max_coalesced_frames.set &&
!coalesce->rx_coalesce_usecs_irq.set &&
!coalesce->rx_max_coalesced_frames_irq.set &&
!coalesce->tx_coalesce_usecs.set &&
!coalesce->tx_max_coalesced_frames.set &&
!coalesce->tx_coalesce_usecs_irq.set &&
!coalesce->tx_max_coalesced_frames_irq.set &&
!coalesce->stats_block_coalesce_usecs.set &&
!coalesce->pkt_rate_low.set &&
!coalesce->rx_coalesce_usecs_low.set &&
!coalesce->rx_max_coalesced_frames_low.set &&
!coalesce->tx_coalesce_usecs_low.set &&
!coalesce->tx_max_coalesced_frames_low.set &&
!coalesce->pkt_rate_high.set &&
!coalesce->rx_coalesce_usecs_high.set &&
!coalesce->rx_max_coalesced_frames_high.set &&
!coalesce->tx_coalesce_usecs_high.set &&
!coalesce->tx_max_coalesced_frames_high.set &&
!coalesce->rate_sample_interval.set)
return 0;
r = ethtool_connect(ethtool_fd);
if (r < 0)
return r;
strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
if (r < 0)
return -errno;
if (coalesce->use_adaptive_rx_coalesce >= 0)
UPDATE(ecmd.use_adaptive_rx_coalesce, (uint32_t) coalesce->use_adaptive_rx_coalesce, need_update);
if (coalesce->use_adaptive_tx_coalesce >= 0)
UPDATE(ecmd.use_adaptive_tx_coalesce, (uint32_t) coalesce->use_adaptive_tx_coalesce, need_update);
if (coalesce->rx_coalesce_usecs.set)
UPDATE(ecmd.rx_coalesce_usecs, coalesce->rx_coalesce_usecs.value, need_update);
if (coalesce->rx_max_coalesced_frames.set)
UPDATE(ecmd.rx_max_coalesced_frames, coalesce->rx_max_coalesced_frames.value, need_update);
if (coalesce->rx_coalesce_usecs_irq.set)
UPDATE(ecmd.rx_coalesce_usecs_irq, coalesce->rx_coalesce_usecs_irq.value, need_update);
if (coalesce->rx_max_coalesced_frames_irq.set)
UPDATE(ecmd.rx_max_coalesced_frames_irq, coalesce->rx_max_coalesced_frames_irq.value, need_update);
if (coalesce->tx_coalesce_usecs.set)
UPDATE(ecmd.tx_coalesce_usecs, coalesce->tx_coalesce_usecs.value, need_update);
if (coalesce->tx_max_coalesced_frames.set)
UPDATE(ecmd.tx_max_coalesced_frames, coalesce->tx_max_coalesced_frames.value, need_update);
if (coalesce->tx_coalesce_usecs_irq.set)
UPDATE(ecmd.tx_coalesce_usecs_irq, coalesce->tx_coalesce_usecs_irq.value, need_update);
if (coalesce->tx_max_coalesced_frames_irq.set)
UPDATE(ecmd.tx_max_coalesced_frames_irq, coalesce->tx_max_coalesced_frames_irq.value, need_update);
if (coalesce->stats_block_coalesce_usecs.set)
UPDATE(ecmd.stats_block_coalesce_usecs, coalesce->stats_block_coalesce_usecs.value, need_update);
if (coalesce->pkt_rate_low.set)
UPDATE(ecmd.pkt_rate_low, coalesce->pkt_rate_low.value, need_update);
if (coalesce->rx_coalesce_usecs_low.set)
UPDATE(ecmd.rx_coalesce_usecs_low, coalesce->rx_coalesce_usecs_low.value, need_update);
if (coalesce->rx_max_coalesced_frames_low.set)
UPDATE(ecmd.rx_max_coalesced_frames_low, coalesce->rx_max_coalesced_frames_low.value, need_update);
if (coalesce->tx_coalesce_usecs_low.set)
UPDATE(ecmd.tx_coalesce_usecs_low, coalesce->tx_coalesce_usecs_low.value, need_update);
if (coalesce->tx_max_coalesced_frames_low.set)
UPDATE(ecmd.tx_max_coalesced_frames_low, coalesce->tx_max_coalesced_frames_low.value, need_update);
if (coalesce->pkt_rate_high.set)
UPDATE(ecmd.pkt_rate_high, coalesce->pkt_rate_high.value, need_update);
if (coalesce->rx_coalesce_usecs_high.set)
UPDATE(ecmd.rx_coalesce_usecs_high, coalesce->rx_coalesce_usecs_high.value, need_update);
if (coalesce->rx_max_coalesced_frames_high.set)
UPDATE(ecmd.rx_max_coalesced_frames_high, coalesce->rx_max_coalesced_frames_high.value, need_update);
if (coalesce->tx_coalesce_usecs_high.set)
UPDATE(ecmd.tx_coalesce_usecs_high, coalesce->tx_coalesce_usecs_high.value, need_update);
if (coalesce->tx_max_coalesced_frames_high.set)
UPDATE(ecmd.tx_max_coalesced_frames_high, coalesce->tx_max_coalesced_frames_high.value, need_update);
if (coalesce->rate_sample_interval.set)
UPDATE(ecmd.rate_sample_interval, DIV_ROUND_UP(coalesce->rate_sample_interval.value, USEC_PER_SEC), need_update);
if (!need_update)
return 0;
ecmd.cmd = ETHTOOL_SCOALESCE;
r = ioctl(*ethtool_fd, SIOCETHTOOL, &ifr);
if (r < 0)
return -errno;
return 0;
}

View File

@ -76,6 +76,31 @@ typedef struct netdev_ring_param {
u32_opt tx;
} netdev_ring_param;
typedef struct netdev_coalesce_param {
u32_opt rx_coalesce_usecs;
u32_opt rx_max_coalesced_frames;
u32_opt rx_coalesce_usecs_irq;
u32_opt rx_max_coalesced_frames_irq;
u32_opt tx_coalesce_usecs;
u32_opt tx_max_coalesced_frames;
u32_opt tx_coalesce_usecs_irq;
u32_opt tx_max_coalesced_frames_irq;
u32_opt stats_block_coalesce_usecs;
int use_adaptive_rx_coalesce;
int use_adaptive_tx_coalesce;
u32_opt pkt_rate_low;
u32_opt rx_coalesce_usecs_low;
u32_opt rx_max_coalesced_frames_low;
u32_opt tx_coalesce_usecs_low;
u32_opt tx_max_coalesced_frames_low;
u32_opt pkt_rate_high;
u32_opt rx_coalesce_usecs_high;
u32_opt rx_max_coalesced_frames_high;
u32_opt tx_coalesce_usecs_high;
u32_opt tx_max_coalesced_frames_high;
u32_opt rate_sample_interval;
} netdev_coalesce_param;
int ethtool_get_driver(int *ethtool_fd, const char *ifname, char **ret);
int ethtool_get_link_info(int *ethtool_fd, const char *ifname,
int *ret_autonegotiation, uint64_t *ret_speed,
@ -89,6 +114,7 @@ int ethtool_set_glinksettings(int *ethtool_fd, const char *ifname,
uint64_t speed, Duplex duplex, NetDevPort port);
int ethtool_set_channels(int *ethtool_fd, const char *ifname, const netdev_channels *channels);
int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg);
int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const netdev_coalesce_param *coalesce);
const char *duplex_to_string(Duplex d) _const_;
Duplex duplex_from_string(const char *d) _pure_;
@ -106,3 +132,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_wol);
CONFIG_PARSER_PROTOTYPE(config_parse_port);
CONFIG_PARSER_PROTOTYPE(config_parse_advertise);
CONFIG_PARSER_PROTOTYPE(config_parse_ring_buffer_or_channel);
CONFIG_PARSER_PROTOTYPE(config_parse_coalesce_u32);
CONFIG_PARSER_PROTOTYPE(config_parse_coalesce_sec);
CONFIG_PARSER_PROTOTYPE(config_parse_nic_coalesce_setting);

View File

@ -1023,7 +1023,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
return 1;
} else if (STR_IN_SET(name, "SystemCallFilter", "SystemCallLog", "RestrictAddressFamilies")) {
} else if (STR_IN_SET(name, "SystemCallFilter", "SystemCallLog", "RestrictAddressFamilies", "RestrictNetworkInterfaces")) {
_cleanup_strv_free_ char **l = NULL;
int allow_list;

View File

@ -140,7 +140,7 @@ static void test_cg_mask_to_string_one(CGroupMask mask, const char *t) {
static void test_cg_mask_to_string(void) {
test_cg_mask_to_string_one(0, NULL);
test_cg_mask_to_string_one(_CGROUP_MASK_ALL, "cpu cpuacct cpuset io blkio memory devices pids bpf-firewall bpf-devices bpf-foreign bpf-socket-bind");
test_cg_mask_to_string_one(_CGROUP_MASK_ALL, "cpu cpuacct cpuset io blkio memory devices pids bpf-firewall bpf-devices bpf-foreign bpf-socket-bind bpf-restrict-network-interfaces");
test_cg_mask_to_string_one(CGROUP_MASK_CPU, "cpu");
test_cg_mask_to_string_one(CGROUP_MASK_CPUACCT, "cpuacct");
test_cg_mask_to_string_one(CGROUP_MASK_CPUSET, "cpuset");

View File

@ -21,54 +21,76 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
Match.MACAddress, config_parse_hwaddrs, 0, offsetof(LinkConfig, match.mac)
Match.PermanentMACAddress, config_parse_hwaddrs, 0, offsetof(LinkConfig, match.permanent_mac)
Match.OriginalName, config_parse_match_ifnames, 0, offsetof(LinkConfig, match.ifname)
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.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)
Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(LinkConfig, conditions)
Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(LinkConfig, conditions)
Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(LinkConfig, conditions)
Link.Description, config_parse_string, 0, offsetof(LinkConfig, description)
Link.MACAddressPolicy, config_parse_mac_address_policy, 0, offsetof(LinkConfig, mac_address_policy)
Link.MACAddress, config_parse_hwaddr, 0, offsetof(LinkConfig, mac)
Link.NamePolicy, config_parse_name_policy, 0, offsetof(LinkConfig, name_policy)
Link.Name, config_parse_ifname, 0, offsetof(LinkConfig, name)
Link.AlternativeName, config_parse_ifnames, IFNAME_VALID_ALTERNATIVE, offsetof(LinkConfig, alternative_names)
Link.AlternativeNamesPolicy, config_parse_alternative_names_policy, 0, offsetof(LinkConfig, alternative_names_policy)
Link.Alias, config_parse_ifalias, 0, offsetof(LinkConfig, alias)
Link.TransmitQueues, config_parse_rx_tx_queues, 0, offsetof(LinkConfig, txqueues)
Link.ReceiveQueues, config_parse_rx_tx_queues, 0, offsetof(LinkConfig, rxqueues)
Link.TransmitQueueLength, config_parse_txqueuelen, 0, offsetof(LinkConfig, txqueuelen)
Link.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(LinkConfig, mtu)
Link.BitsPerSecond, config_parse_si_uint64, 0, offsetof(LinkConfig, speed)
Link.Duplex, config_parse_duplex, 0, offsetof(LinkConfig, duplex)
Link.AutoNegotiation, config_parse_tristate, 0, offsetof(LinkConfig, autonegotiation)
Link.WakeOnLan, config_parse_wol, 0, offsetof(LinkConfig, wol)
Link.Port, config_parse_port, 0, offsetof(LinkConfig, port)
Link.ReceiveChecksumOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_RX])
Link.TransmitChecksumOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_TX])
Link.GenericSegmentationOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_GSO])
Link.TCPSegmentationOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_TSO])
Link.TCP6SegmentationOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_TSO6])
Link.UDPSegmentationOffload, config_parse_warn_compat, DISABLED_LEGACY, 0
Link.GenericReceiveOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_GRO])
Link.LargeReceiveOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_LRO])
Link.RxChannels, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, channels.rx)
Link.TxChannels, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, channels.tx)
Link.OtherChannels, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, channels.other)
Link.CombinedChannels, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, channels.combined)
Link.Advertise, config_parse_advertise, 0, offsetof(LinkConfig, advertise)
Link.RxBufferSize, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, ring.rx)
Link.RxMiniBufferSize, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, ring.rx_mini)
Link.RxJumboBufferSize, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, ring.rx_jumbo)
Link.TxBufferSize, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, ring.tx)
Link.RxFlowControl, config_parse_tristate, 0, offsetof(LinkConfig, rx_flow_control)
Link.TxFlowControl, config_parse_tristate, 0, offsetof(LinkConfig, tx_flow_control)
Link.AutoNegotiationFlowControl, config_parse_tristate, 0, offsetof(LinkConfig, autoneg_flow_control)
Link.GenericSegmentOffloadMaxBytes, config_parse_iec_size, 0, offsetof(LinkConfig, gso_max_size)
Link.GenericSegmentOffloadMaxSegments, config_parse_uint32, 0, offsetof(LinkConfig, gso_max_segments)
Match.MACAddress, config_parse_hwaddrs, 0, offsetof(LinkConfig, match.mac)
Match.PermanentMACAddress, config_parse_hwaddrs, 0, offsetof(LinkConfig, match.permanent_mac)
Match.OriginalName, config_parse_match_ifnames, 0, offsetof(LinkConfig, match.ifname)
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.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)
Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(LinkConfig, conditions)
Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(LinkConfig, conditions)
Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(LinkConfig, conditions)
Link.Description, config_parse_string, 0, offsetof(LinkConfig, description)
Link.MACAddressPolicy, config_parse_mac_address_policy, 0, offsetof(LinkConfig, mac_address_policy)
Link.MACAddress, config_parse_hwaddr, 0, offsetof(LinkConfig, mac)
Link.NamePolicy, config_parse_name_policy, 0, offsetof(LinkConfig, name_policy)
Link.Name, config_parse_ifname, 0, offsetof(LinkConfig, name)
Link.AlternativeName, config_parse_ifnames, IFNAME_VALID_ALTERNATIVE, offsetof(LinkConfig, alternative_names)
Link.AlternativeNamesPolicy, config_parse_alternative_names_policy, 0, offsetof(LinkConfig, alternative_names_policy)
Link.Alias, config_parse_ifalias, 0, offsetof(LinkConfig, alias)
Link.TransmitQueues, config_parse_rx_tx_queues, 0, offsetof(LinkConfig, txqueues)
Link.ReceiveQueues, config_parse_rx_tx_queues, 0, offsetof(LinkConfig, rxqueues)
Link.TransmitQueueLength, config_parse_txqueuelen, 0, offsetof(LinkConfig, txqueuelen)
Link.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(LinkConfig, mtu)
Link.BitsPerSecond, config_parse_si_uint64, 0, offsetof(LinkConfig, speed)
Link.Duplex, config_parse_duplex, 0, offsetof(LinkConfig, duplex)
Link.AutoNegotiation, config_parse_tristate, 0, offsetof(LinkConfig, autonegotiation)
Link.WakeOnLan, config_parse_wol, 0, offsetof(LinkConfig, wol)
Link.Port, config_parse_port, 0, offsetof(LinkConfig, port)
Link.ReceiveChecksumOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_RX])
Link.TransmitChecksumOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_TX])
Link.GenericSegmentationOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_GSO])
Link.TCPSegmentationOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_TSO])
Link.TCP6SegmentationOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_TSO6])
Link.UDPSegmentationOffload, config_parse_warn_compat, DISABLED_LEGACY, 0
Link.GenericReceiveOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_GRO])
Link.LargeReceiveOffload, config_parse_tristate, 0, offsetof(LinkConfig, features[NET_DEV_FEAT_LRO])
Link.RxChannels, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, channels.rx)
Link.TxChannels, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, channels.tx)
Link.OtherChannels, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, channels.other)
Link.CombinedChannels, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, channels.combined)
Link.Advertise, config_parse_advertise, 0, offsetof(LinkConfig, advertise)
Link.RxBufferSize, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, ring.rx)
Link.RxMiniBufferSize, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, ring.rx_mini)
Link.RxJumboBufferSize, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, ring.rx_jumbo)
Link.TxBufferSize, config_parse_ring_buffer_or_channel, 0, offsetof(LinkConfig, ring.tx)
Link.RxFlowControl, config_parse_tristate, 0, offsetof(LinkConfig, rx_flow_control)
Link.TxFlowControl, config_parse_tristate, 0, offsetof(LinkConfig, tx_flow_control)
Link.AutoNegotiationFlowControl, config_parse_tristate, 0, offsetof(LinkConfig, autoneg_flow_control)
Link.GenericSegmentOffloadMaxBytes, config_parse_iec_size, 0, offsetof(LinkConfig, gso_max_size)
Link.GenericSegmentOffloadMaxSegments, config_parse_uint32, 0, offsetof(LinkConfig, gso_max_segments)
Link.RxCoalesceSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.rx_coalesce_usecs)
Link.RxMaxCoalescedFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.rx_max_coalesced_frames)
Link.RxCoalesceIrqSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.rx_coalesce_usecs_irq)
Link.RxMaxCoalescedIrqFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.rx_max_coalesced_frames_irq)
Link.TxCoalesceSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.tx_coalesce_usecs)
Link.TxMaxCoalescedFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.tx_max_coalesced_frames)
Link.TxCoalesceIrqSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.tx_coalesce_usecs_irq)
Link.TxMaxCoalescedIrqFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.tx_max_coalesced_frames_irq)
Link.StatisticsBlockCoalesceSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.stats_block_coalesce_usecs)
Link.UseAdaptiveRxCoalesce, config_parse_tristate, 0, offsetof(LinkConfig, coalesce.use_adaptive_rx_coalesce)
Link.UseAdaptiveTxCoalesce, config_parse_tristate, 0, offsetof(LinkConfig, coalesce.use_adaptive_tx_coalesce)
Link.CoalescePacketRateLow, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.pkt_rate_low)
Link.RxCoalesceLowSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.rx_coalesce_usecs_low)
Link.RxMaxCoalescedLowFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.rx_max_coalesced_frames_low)
Link.TxCoalesceLowSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.tx_coalesce_usecs_low)
Link.TxMaxCoalescedLowFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.tx_max_coalesced_frames_low)
Link.CoalescePacketRateHigh, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.pkt_rate_high)
Link.RxCoalesceHighSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.rx_coalesce_usecs_high)
Link.RxMaxCoalescedHighFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.rx_max_coalesced_frames_high)
Link.TxCoalesceHighSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.tx_coalesce_usecs_high)
Link.TxMaxCoalescedHighFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.tx_max_coalesced_frames_high)
Link.CoalescePacketRateSampleIntervalSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.rate_sample_interval)

View File

@ -353,6 +353,10 @@ static int link_config_apply_ethtool_settings(int *ethtool_fd, const LinkConfig
if (r < 0)
log_device_warning_errno(device, r, "Could not set flow control, ignoring: %m");
r = ethtool_set_nic_coalesce_settings(ethtool_fd, name, &config->coalesce);
if (r < 0)
log_device_warning_errno(device, r, "Could not set coalesce settings, ignoring: %m");
return 0;
}

View File

@ -64,6 +64,7 @@ struct LinkConfig {
int rx_flow_control;
int tx_flow_control;
int autoneg_flow_control;
netdev_coalesce_param coalesce;
LIST_FIELDS(LinkConfig, links);
};

View File

@ -0,0 +1 @@
../TEST-01-BASIC/Makefile

View File

@ -0,0 +1,9 @@
#!/usr/bin/env bash
TEST_NO_NSPAWN=1
set -e
TEST_DESCRIPTION="test RestrictNetworkInterfaces="
. $TEST_BASE_DIR/test-functions
do_test "$@" 62

View File

@ -51,3 +51,25 @@ TxFlowControl=
AutoNegotiationFlowControl=
GenericSegmentOffloadMaxBytes=
GenericSegmentOffloadMaxSegments=
RxCoalesceSec=
RxMaxCoalescedFrames=
RxCoalesceIrqSec=
RxMaxCoalescedIrqFrames=
TxCoalesceSec=
TxMaxCoalescedFrames=
TxCoalesceIrqSec=
TxMaxCoalescedIrqFrames=
StatisticsBlockCoalesceSec=
UseAdaptiveRxCoalesce=
UseAdaptiveTxCoalesce=
CoalescePacketRateLow=
RxCoalesceLowSec=
RxMaxCoalescedLowFrames=
TxCoalesceLowSec=
TxMaxCoalescedLowFrames=
CoalescePacketRateHigh=
RxCoalesceHighSec=
RxMaxCoalescedHighFrames=
TxCoalesceHighSec=
TxMaxCoalescedHighFrames=
CoalescePacketRateSampleIntervalSec=

View File

@ -886,6 +886,7 @@ RemoveIPC=
ReserveVT=
RestrictAddressFamilies=
RestrictNamespaces=
RestrictNetworkInterfaces=
RestrictRealtime=
RestrictSUIDSGID=
RuntimeDirectory=

View File

@ -144,6 +144,7 @@ RemoveIPC=
RestartKillSignal=
RestrictAddressFamilies=
RestrictNamespaces=
RestrictNetworkInterfaces=
RestrictRealtime=
RestrictSUIDSGID=
RootDirectory=

View File

@ -48,6 +48,7 @@ MemoryMin=
MemorySwapMax=
NetClass=
RestartKillSignal=
RestrictNetworkInterfaces=
RuntimeMaxSec=
SendSIGHUP=
SendSIGKILL=

View File

@ -275,6 +275,7 @@ RestartPreventExitStatus=
RestartSec=
RestrictAddressFamilies=
RestrictNamespaces=
RestrictNetworkInterfaces=
RestrictRealtime=
RestrictSUIDSGID=
RootDirectory=

View File

@ -44,6 +44,7 @@ MemoryMax=
MemoryMin=
MemorySwapMax=
NetClass=
RestrictNetworkInterfaces=
Slice=
SocketBindAllow=
SocketBindDeny=

View File

@ -180,6 +180,7 @@ RemoveOnStop=
RestartKillSignal=
RestrictAddressFamilies=
RestrictNamespaces=
RestrictNetworkInterfaces=
RestrictRealtime=
RestrictSUIDSGID=
ReusePort=

View File

@ -141,6 +141,7 @@ RemoveIPC=
RestartKillSignal=
RestrictAddressFamilies=
RestrictNamespaces=
RestrictNetworkInterfaces=
RestrictRealtime=
RestrictSUIDSGID=
RootDirectory=

View File

@ -672,6 +672,7 @@ setup_basic_environment() {
has_user_dbus_socket && install_user_dbus
setup_selinux
strip_binaries
instmods veth
install_depmod_files
generate_module_dependencies
if get_bool "$IS_BUILT_WITH_ASAN"; then

View File

@ -0,0 +1,8 @@
[Unit]
Description=TEST-62-RESTRICT-IFACES-all-pings-work
[Service]
ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.1'
ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.5'
ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.9'
RestrictNetworkInterfaces=
Type=oneshot

View File

@ -0,0 +1,9 @@
[Unit]
Description=TEST-62-RESTRICT-IFACES-allow-list
[Service]
ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.1'
ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.5'
ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.9'
RestrictNetworkInterfaces=veth0
RestrictNetworkInterfaces=veth1
Type=oneshot

View File

@ -0,0 +1,9 @@
[Unit]
Description=TEST-62-RESTRICT-IFACES-deny-list
[Service]
ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.1'
ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.5'
ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.9'
RestrictNetworkInterfaces=~veth0
RestrictNetworkInterfaces=~veth1
Type=oneshot

View File

@ -0,0 +1,9 @@
[Unit]
Description=TEST-62-RESTRICT-IFACES-empty-assigment
[Service]
ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.1'
ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.5'
ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.9'
RestrictNetworkInterfaces=veth0
RestrictNetworkInterfaces=
Type=oneshot

View File

@ -0,0 +1,10 @@
[Unit]
Description=TEST-62-RESTRICT-IFACES-invert-assigment
[Service]
ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.1'
ExecStart=/bin/sh -c 'ping -c 1 -W 0.2 192.168.113.5'
ExecStart=/bin/sh -c '! ping -c 1 -W 0.2 192.168.113.9'
RestrictNetworkInterfaces=veth0
RestrictNetworkInterfaces=veth0 veth1
RestrictNetworkInterfaces=~veth0
Type=oneshot

View File

@ -0,0 +1,6 @@
Description=TEST-62-RESTRICT-IFACES
[Service]
ExecStartPre=rm -f /failed /testok
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
Type=oneshot

60
test/units/testsuite-62.sh Executable file
View File

@ -0,0 +1,60 @@
#!/usr/bin/env bash
set -ex
set -o pipefail
setup() {
systemd-analyze log-level debug
systemd-analyze log-target console
for i in `seq 0 3`;
do
ip netns del ns${i} || true
ip link del veth${i} || true
ip netns add ns${i}
ip link add veth${i} type veth peer name veth${i}_
ip link set veth${i}_ netns ns${i}
ip -n ns${i} link set dev veth${i}_ up
ip -n ns${i} link set dev lo up
ip -n ns${i} addr add "192.168.113."$((4*i+1))/30 dev veth${i}_
ip link set dev veth${i} up
ip addr add "192.168.113."$((4*i+2))/30 dev veth${i}
done
}
teardown() {
set +e
for i in `seq 0 3`;
do
ip netns del ns${i}
ip link del veth${i}
done
systemd-analyze log-level info
}
KERNEL_VERSION="$(uname -r)"
KERNEL_MAJOR="${KERNEL_VERSION%%.*}"
KERNEL_MINOR="${KERNEL_VERSION#$KERNEL_MAJOR.}"
KERNEL_MINOR="${KERNEL_MINOR%%.*}"
MAJOR_REQUIRED=5
MINOR_REQUIRED=7
if [[ "$KERNEL_MAJOR" -lt $MAJOR_REQUIRED || ("$KERNEL_MAJOR" -eq $MAJOR_REQUIRED && "$KERNEL_MINOR" -lt $MINOR_REQUIRED) ]]; then
echo "kernel is not 5.7+" >>/skipped
exit 0
fi
trap teardown EXIT
setup
systemctl start --wait testsuite-62-1.service
systemctl start --wait testsuite-62-2.service
systemctl start --wait testsuite-62-3.service
systemctl start --wait testsuite-62-4.service
systemctl start --wait testsuite-62-5.service
echo OK > /testok
exit 0