mirror of
https://github.com/systemd/systemd
synced 2026-03-13 16:44:48 +01:00
Compare commits
No commits in common. "45b1fc3a88b2f5102ecabfabdf0ee4f175aecd64" and "1ceaad69378272c64da4ecaab0d59ebb7a92ca0a" have entirely different histories.
45b1fc3a88
...
1ceaad6937
21
README
21
README
@ -35,10 +35,6 @@ LICENSE:
|
||||
REQUIREMENTS:
|
||||
Linux kernel >= 3.13
|
||||
Linux kernel >= 4.2 for unified cgroup hierarchy support
|
||||
Linux kernel >= 4.10 for cgroup-bpf egress and ingress hooks
|
||||
Linux kernel >= 4.15 for cgroup-bpf device hook
|
||||
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
|
||||
|
||||
Kernel Config Options:
|
||||
@ -99,20 +95,8 @@ REQUIREMENTS:
|
||||
Required for CPUQuota= in resource control unit settings
|
||||
CONFIG_CFS_BANDWIDTH
|
||||
|
||||
Required for IPAddressDeny=, IPAddressAllow=, IPIngressFilterPath=,
|
||||
IPEgressFilterPath= in resource control unit settings
|
||||
Required for IPAddressDeny= and IPAddressAllow= in resource control
|
||||
unit settings
|
||||
CONFIG_BPF
|
||||
CONFIG_BPF_SYSCALL
|
||||
CONFIG_BPF_JIT
|
||||
CONFIG_HAVE_EBPF_JIT
|
||||
CONFIG_CGROUP_BPF
|
||||
|
||||
Required for SocketBind{Allow|Deny}= in resource control unit settings
|
||||
CONFIG_BPF
|
||||
CONFIG_BPF_SYSCALL
|
||||
CONFIG_BPF_JIT
|
||||
CONFIG_HAVE_EBPF_JIT
|
||||
CONFIG_CGROUP_BPF
|
||||
|
||||
For UEFI systems:
|
||||
@ -170,7 +154,6 @@ REQUIREMENTS:
|
||||
libcryptsetup (optional), >= 2.3.0 required for signed Verity images support
|
||||
libaudit (optional)
|
||||
libacl (optional)
|
||||
libbpf >= 0.2.0 (optional)
|
||||
libfdisk >= 2.33 (from util-linux) (optional)
|
||||
libselinux (optional)
|
||||
liblzma (optional)
|
||||
@ -195,8 +178,6 @@ REQUIREMENTS:
|
||||
meson >= 0.46 (>= 0.49 is required to build position-independent executables)
|
||||
ninja
|
||||
gcc, awk, sed, grep, m4, and similar tools
|
||||
clang >= 10.0, llvm >= 10.0 (optional, required to build BPF programs
|
||||
from source code in C)
|
||||
|
||||
During runtime, you need the following additional
|
||||
dependencies:
|
||||
|
||||
@ -2482,10 +2482,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
readonly s ManagedOOMPreference = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly a(ss) BPFProgram = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly a(iqq) SocketBindAllow = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly a(iqq) SocketBindDeny = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as Environment = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
@ -3022,10 +3018,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
|
||||
<!--property BPFProgram is not documented!-->
|
||||
|
||||
<!--property SocketBindAllow is not documented!-->
|
||||
|
||||
<!--property SocketBindDeny is not documented!-->
|
||||
|
||||
<!--property EnvironmentFiles is not documented!-->
|
||||
|
||||
<!--property PassEnvironment is not documented!-->
|
||||
@ -3586,10 +3578,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindAllow"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
|
||||
@ -4277,10 +4265,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
||||
readonly s ManagedOOMPreference = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly a(ss) BPFProgram = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly a(iqq) SocketBindAllow = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly a(iqq) SocketBindDeny = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as Environment = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
@ -4843,10 +4827,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
||||
|
||||
<!--property BPFProgram is not documented!-->
|
||||
|
||||
<!--property SocketBindAllow is not documented!-->
|
||||
|
||||
<!--property SocketBindDeny is not documented!-->
|
||||
|
||||
<!--property EnvironmentFiles is not documented!-->
|
||||
|
||||
<!--property PassEnvironment is not documented!-->
|
||||
@ -5403,10 +5383,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindAllow"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
|
||||
@ -5996,10 +5972,6 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
||||
readonly s ManagedOOMPreference = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly a(ss) BPFProgram = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly a(iqq) SocketBindAllow = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly a(iqq) SocketBindDeny = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as Environment = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
@ -6490,10 +6462,6 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
||||
|
||||
<!--property BPFProgram is not documented!-->
|
||||
|
||||
<!--property SocketBindAllow is not documented!-->
|
||||
|
||||
<!--property SocketBindDeny is not documented!-->
|
||||
|
||||
<!--property EnvironmentFiles is not documented!-->
|
||||
|
||||
<!--property PassEnvironment is not documented!-->
|
||||
@ -6968,10 +6936,6 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindAllow"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
|
||||
@ -7682,10 +7646,6 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
||||
readonly s ManagedOOMPreference = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly a(ss) BPFProgram = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly a(iqq) SocketBindAllow = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly a(iqq) SocketBindDeny = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as Environment = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
@ -8162,10 +8122,6 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
||||
|
||||
<!--property BPFProgram is not documented!-->
|
||||
|
||||
<!--property SocketBindAllow is not documented!-->
|
||||
|
||||
<!--property SocketBindDeny is not documented!-->
|
||||
|
||||
<!--property EnvironmentFiles is not documented!-->
|
||||
|
||||
<!--property PassEnvironment is not documented!-->
|
||||
@ -8626,10 +8582,6 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindAllow"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
|
||||
@ -9193,10 +9145,6 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
|
||||
readonly s ManagedOOMPreference = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly a(ss) BPFProgram = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly a(iqq) SocketBindAllow = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly a(iqq) SocketBindDeny = [...];
|
||||
};
|
||||
interface org.freedesktop.DBus.Peer { ... };
|
||||
interface org.freedesktop.DBus.Introspectable { ... };
|
||||
@ -9337,10 +9285,6 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
|
||||
|
||||
<!--property BPFProgram is not documented!-->
|
||||
|
||||
<!--property SocketBindAllow is not documented!-->
|
||||
|
||||
<!--property SocketBindDeny is not documented!-->
|
||||
|
||||
<!--Autogenerated cross-references for systemd.directives, do not edit-->
|
||||
|
||||
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
|
||||
@ -9485,10 +9429,6 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindAllow"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
|
||||
|
||||
<!--End of Autogenerated section-->
|
||||
|
||||
<refsect2>
|
||||
@ -9652,10 +9592,6 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
|
||||
readonly s ManagedOOMPreference = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly a(ss) BPFProgram = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly a(iqq) SocketBindAllow = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly a(iqq) SocketBindDeny = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s KillMode = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
@ -9812,10 +9748,6 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
|
||||
|
||||
<!--property BPFProgram is not documented!-->
|
||||
|
||||
<!--property SocketBindAllow is not documented!-->
|
||||
|
||||
<!--property SocketBindDeny is not documented!-->
|
||||
|
||||
<!--property KillMode is not documented!-->
|
||||
|
||||
<!--property KillSignal is not documented!-->
|
||||
@ -9986,10 +9918,6 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindAllow"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="SocketBindDeny"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="KillMode"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="KillSignal"/>
|
||||
|
||||
@ -762,76 +762,6 @@ BPFProgram=bind6:/sys/fs/bpf/sock-addr-hook
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>SocketBindAllow=<replaceable>bind-rule</replaceable></varname></term>
|
||||
<term><varname>SocketBindDeny=<replaceable>bind-rule</replaceable></varname></term>
|
||||
|
||||
<listitem>
|
||||
<para>Allow or deny binding a socket address to a socket by matching it with the <replaceable>bind-rule</replaceable> and
|
||||
applying a corresponding action if there is a match.</para>
|
||||
|
||||
<para><replaceable>bind-rule</replaceable> describes socket properties such as <replaceable>address-family</replaceable>
|
||||
and <replaceable>ip-ports</replaceable>.</para>
|
||||
|
||||
<para><replaceable>bind-rule</replaceable> := [<replaceable>address-family</replaceable><constant>:</constant>]<replaceable>ip-ports</replaceable></para>
|
||||
|
||||
<para><replaceable>address-family</replaceable> := { <constant>IPv4</constant> | <constant>IPv6</constant> }</para>
|
||||
|
||||
<para><replaceable>ip-ports</replaceable> := { <replaceable>ip-port</replaceable> | <replaceable>ip-port-range</replaceable> |
|
||||
<constant>any</constant> }</para>
|
||||
|
||||
<para>An optional <replaceable>address-family</replaceable> expects <constant>IPv4</constant> or <constant>IPv6</constant> values.
|
||||
If not specified, a rule will be matched for both IPv4 and IPv6 addresses and applied depending on other socket fields, e.g.
|
||||
<replaceable>ip-port</replaceable>.</para>
|
||||
|
||||
<para><replaceable>ip-port</replaceable> value must lie within 1…65535 interval inclusively, i.e.
|
||||
dynamic port <constant>0</constant> is not allowed. A range of sequential ports is described by
|
||||
<replaceable>ip-port-range</replaceable> := <replaceable>ip-port-low</replaceable><constant>-</constant><replaceable>ip-port-high</replaceable>,
|
||||
where <replaceable>ip-port-low</replaceable> is smaller than or equal to <replaceable>ip-port-high</replaceable>
|
||||
and both are within 1…65535 inclusively. A special value <constant>any</constant>
|
||||
should be used to apply a rule to any port with a positive value.</para>
|
||||
|
||||
<para>To allow multiple rules assign <varname>SocketBindAllow=</varname> or <varname>SocketBindDeny=</varname> multiple times.
|
||||
To clear the existing assignments pass an empty <varname>SocketBindAllow=</varname> or <varname>SocketBindDeny=</varname>
|
||||
assignment.</para>
|
||||
|
||||
<para>For each of <varname>SocketBindAllow=</varname> and <varname>SocketBindDeny=</varname>, maximum allowed number of assignments is
|
||||
<constant>128</constant>.</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para>Binding to a socket is allowed when a socket address matches an entry in the
|
||||
<varname>SocketBindAllow=</varname> list.</para></listitem>
|
||||
|
||||
<listitem><para>Otherwise, binding is denied when the socket address matches an entry in the
|
||||
<varname>SocketBindDeny=</varname> list.</para></listitem>
|
||||
|
||||
<listitem><para>Otherwise, binding is allowed.</para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>The feature is implemented with <constant>cgroup/bind4</constant> and <constant>cgroup/bind6</constant> cgroup-bpf hooks.</para>
|
||||
<para>Examples:<programlisting>…
|
||||
# Allow binding IPv6 socket addresses with a port greater than or equal to 10000.
|
||||
[Service]
|
||||
SocketBindAllow=IPv6:10000-65535
|
||||
SocketBindDeny=any
|
||||
…
|
||||
# Allow binding IPv4 and IPv6 socket addresses with 1234 and 4321 ports.
|
||||
[Service]
|
||||
SocketBindAllow=1234
|
||||
SocketBindAllow=4321
|
||||
SocketBindDeny=any
|
||||
…
|
||||
# Deny binding IPv6 socket addresses.
|
||||
[Service]
|
||||
SocketBindDeny=IPv6:any
|
||||
…
|
||||
# Deny binding IPv4 and IPv6 socket addresses.
|
||||
[Service]
|
||||
SocketBindDeny=any
|
||||
…</programlisting></para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>DeviceAllow=</varname></term>
|
||||
|
||||
|
||||
23
meson.build
23
meson.build
@ -937,25 +937,6 @@ if not libcap.found()
|
||||
libcap = cc.find_library('cap')
|
||||
endif
|
||||
|
||||
want_bpf_framework = get_option('bpf-framework')
|
||||
bpf_framework_required = want_bpf_framework == 'true'
|
||||
|
||||
libbpf = dependency('libbpf', required : bpf_framework_required, version : '>= 0.2')
|
||||
conf.set10('HAVE_LIBBPF', libbpf.found())
|
||||
|
||||
if want_bpf_framework == 'false'
|
||||
conf.set10('BPF_FRAMEWORK', 0)
|
||||
else
|
||||
clang = find_program('clang', required : bpf_framework_required)
|
||||
llvm_strip = find_program('llvm-strip', required : bpf_framework_required)
|
||||
bpftool = find_program('bpftool', required : bpf_framework_required)
|
||||
bpf_arches = ['x86_64']
|
||||
deps_found = libbpf.found() and clang.found() and llvm_strip.found() and bpftool.found()
|
||||
# Can build BPF program from source code in restricted C
|
||||
conf.set10('BPF_FRAMEWORK',
|
||||
bpf_arches.contains(host_machine.cpu_family()) and deps_found)
|
||||
endif
|
||||
|
||||
libmount = dependency('mount',
|
||||
version : fuzzer_build ? '>= 0' : '>= 2.30')
|
||||
|
||||
@ -1623,7 +1604,6 @@ conf.set10('ENABLE_EFI', have)
|
||||
|
||||
############################################################
|
||||
|
||||
build_bpf_skel_py = find_program('tools/build-bpf-skel.py')
|
||||
generate_gperfs = find_program('tools/generate-gperfs.py')
|
||||
make_autosuspend_rules_py = find_program('tools/make-autosuspend-rules.py')
|
||||
make_directive_index_py = find_program('tools/make-directive-index.py')
|
||||
@ -1716,7 +1696,6 @@ install_libsystemd_static = static_library(
|
||||
libxz,
|
||||
libzstd,
|
||||
liblz4,
|
||||
libbpf,
|
||||
libcap,
|
||||
libblkid,
|
||||
libmount,
|
||||
@ -3787,7 +3766,6 @@ foreach tuple : [
|
||||
['elfutils'],
|
||||
['gcrypt'],
|
||||
['gnutls'],
|
||||
['libbpf'],
|
||||
['libcryptsetup'],
|
||||
['libcurl'],
|
||||
['libfdisk'],
|
||||
@ -3814,7 +3792,6 @@ foreach tuple : [
|
||||
# components
|
||||
['backlight'],
|
||||
['binfmt'],
|
||||
['bpf-framework', conf.get('BPF_FRAMEWORK') == 1],
|
||||
['coredump'],
|
||||
['environment.d'],
|
||||
['efi'],
|
||||
|
||||
@ -403,6 +403,3 @@ option('kernel-install', type: 'boolean', value: 'true',
|
||||
description : 'install kernel-install and associated files')
|
||||
option('analyze', type: 'boolean', value: 'true',
|
||||
description : 'install systemd-analyze')
|
||||
|
||||
option('bpf-framework', type : 'combo', choices : ['auto', 'true', 'false'],
|
||||
description: 'build BPF programs from source code in restricted C')
|
||||
|
||||
@ -2164,7 +2164,6 @@ static const char *const cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
|
||||
[CGROUP_CONTROLLER_BPF_FIREWALL] = "bpf-firewall",
|
||||
[CGROUP_CONTROLLER_BPF_DEVICES] = "bpf-devices",
|
||||
[CGROUP_CONTROLLER_BPF_FOREIGN] = "bpf-foreign",
|
||||
[CGROUP_CONTROLLER_BPF_SOCKET_BIND] = "bpf-socket-bind",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);
|
||||
|
||||
@ -31,7 +31,6 @@ typedef enum CGroupController {
|
||||
CGROUP_CONTROLLER_BPF_FIREWALL,
|
||||
CGROUP_CONTROLLER_BPF_DEVICES,
|
||||
CGROUP_CONTROLLER_BPF_FOREIGN,
|
||||
CGROUP_CONTROLLER_BPF_SOCKET_BIND,
|
||||
|
||||
_CGROUP_CONTROLLER_MAX,
|
||||
_CGROUP_CONTROLLER_INVALID = -EINVAL,
|
||||
@ -52,7 +51,6 @@ typedef enum CGroupMask {
|
||||
CGROUP_MASK_BPF_FIREWALL = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FIREWALL),
|
||||
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),
|
||||
|
||||
/* 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 +59,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_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
|
||||
} CGroupMask;
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1+
|
||||
|
||||
if conf.get('BPF_FRAMEWORK') == 1
|
||||
socket_bind_skel_h = custom_target(
|
||||
'socket-bind.skel.h',
|
||||
input : 'socket-bind.bpf.c',
|
||||
output : 'socket-bind.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
|
||||
@ -1,47 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
/* The SPDX header above is actually correct in claiming this was
|
||||
* LGPL-2.1-or-later, because it is. Since the kernel doesn't consider that
|
||||
* compatible with GPL we will claim this to be GPL however, which should be
|
||||
* fine given that LGPL-2.1-or-later downgrades to GPL if needed.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* Bind rule is matched with socket fields accessible to cgroup/bind{4,6} hook
|
||||
* through bpf_sock_addr struct.
|
||||
* address_family is expected to be one of AF_UNSPEC, AF_INET or AF_INET6.
|
||||
* Matching by family is bypassed for rules with AF_UNSPEC set, which makes the
|
||||
* rest of a rule applicable for both IPv4 and IPv6 addresses.
|
||||
* If matching by family is either successful or bypassed, a rule and a socket
|
||||
* are matched by ports.
|
||||
* nr_ports and port_min fields specify a set of ports to match a user port
|
||||
* with.
|
||||
* If nr_ports is 0, maching by port is bypassed, making that rule applicable
|
||||
* for all possible ports, e.g. [1, 65535] range. Thus a rule with
|
||||
* address_family and nr_ports equal to AF_UNSPEC and 0 correspondingly forms
|
||||
* 'allow any' or 'deny any' cases.
|
||||
* For positive nr_ports, a user_port lying in a range from port_min to
|
||||
* port_min + nr_ports exclusively is considered to be a match. nr_ports
|
||||
* equalling to 1 forms a rule for a single port.
|
||||
* Ports are in host order.
|
||||
*
|
||||
* Examples:
|
||||
* AF_UNSPEC, 1, 7777: match IPv4 and IPv6 addresses with 7777 user port;
|
||||
*
|
||||
* AF_INET, 1023, 1: match IPv4 addresses with user port in [1, 1023]
|
||||
* range inclusively;
|
||||
*
|
||||
* AF_INET6, 0, 0: match IPv6 addresses;
|
||||
*
|
||||
* AF_UNSPEC, 0, 0: match IPv4 and IPv6 addresses.
|
||||
*/
|
||||
|
||||
struct socket_bind_rule {
|
||||
__u32 address_family;
|
||||
__u16 nr_ports;
|
||||
__u16 port_min;
|
||||
};
|
||||
|
||||
#define SOCKET_BIND_MAX_RULES 128
|
||||
@ -1,103 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
/* The SPDX header above is actually correct in claiming this was
|
||||
* LGPL-2.1-or-later, because it is. Since the kernel doesn't consider that
|
||||
* compatible with GPL we will claim this to be GPL however, which should be
|
||||
* fine given that LGPL-2.1-or-later downgrades to GPL if needed.
|
||||
*/
|
||||
|
||||
#include "socket-bind-api.bpf.h"
|
||||
/* <linux/types.h> must precede <bpf/bpf_helpers.h> due to
|
||||
* <bpf/bpf_helpers.h> does not depend from type header by design.
|
||||
*/
|
||||
#include <linux/types.h>
|
||||
#include <bpf/bpf_endian.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/*
|
||||
* max_entries is set from user space with bpf_map__resize helper.
|
||||
*/
|
||||
struct socket_bind_map_t {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__type(key, __u32);
|
||||
__type(value, struct socket_bind_rule);
|
||||
};
|
||||
|
||||
enum socket_bind_action {
|
||||
SOCKET_BIND_DENY = 0,
|
||||
SOCKET_BIND_ALLOW = 1,
|
||||
};
|
||||
|
||||
struct socket_bind_map_t sd_bind_allow SEC(".maps");
|
||||
struct socket_bind_map_t sd_bind_deny SEC(".maps");
|
||||
|
||||
static __always_inline bool match_af(
|
||||
__u8 address_family, const struct socket_bind_rule *r) {
|
||||
return r->address_family == AF_UNSPEC || address_family == r->address_family;
|
||||
}
|
||||
|
||||
static __always_inline bool match_user_port(
|
||||
__u16 port, const struct socket_bind_rule *r) {
|
||||
return r->nr_ports == 0 ||
|
||||
(port >= r->port_min && port < r->port_min + (__u32) r->nr_ports);
|
||||
}
|
||||
|
||||
static __always_inline bool match(
|
||||
__u8 address_family,
|
||||
__u16 port,
|
||||
const struct socket_bind_rule *r) {
|
||||
return match_af(address_family, r) && match_user_port(port, r);
|
||||
}
|
||||
|
||||
static __always_inline bool match_rules(
|
||||
struct bpf_sock_addr *ctx,
|
||||
struct socket_bind_map_t *rules) {
|
||||
volatile __u32 user_port = ctx->user_port;
|
||||
__u16 port = (__u16)bpf_ntohs(user_port);
|
||||
|
||||
for (__u32 i = 0; i < SOCKET_BIND_MAX_RULES; ++i) {
|
||||
const __u32 key = i;
|
||||
const struct socket_bind_rule *rule = bpf_map_lookup_elem(rules, &key);
|
||||
|
||||
/* Lookup returns NULL if iterator is advanced past the last
|
||||
* element put in the map. */
|
||||
if (!rule)
|
||||
break;
|
||||
|
||||
if (match(ctx->user_family, port, rule))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static __always_inline int bind_socket(struct bpf_sock_addr *ctx) {
|
||||
if (match_rules(ctx, &sd_bind_allow))
|
||||
return SOCKET_BIND_ALLOW;
|
||||
|
||||
if (match_rules(ctx, &sd_bind_deny))
|
||||
return SOCKET_BIND_DENY;
|
||||
|
||||
return SOCKET_BIND_ALLOW;
|
||||
}
|
||||
|
||||
SEC("cgroup/bind4")
|
||||
int sd_bind4(struct bpf_sock_addr *ctx) {
|
||||
if (ctx->user_family != AF_INET || ctx->family != AF_INET)
|
||||
return SOCKET_BIND_ALLOW;
|
||||
|
||||
return bind_socket(ctx);
|
||||
}
|
||||
|
||||
SEC("cgroup/bind6")
|
||||
int sd_bind6(struct bpf_sock_addr *ctx) {
|
||||
if (ctx->user_family != AF_INET6 || ctx->family != AF_INET6)
|
||||
return SOCKET_BIND_ALLOW;
|
||||
|
||||
return bind_socket(ctx);
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
@ -25,7 +25,6 @@
|
||||
#include "percent-util.h"
|
||||
#include "process-util.h"
|
||||
#include "procfs-util.h"
|
||||
#include "socket-bind.h"
|
||||
#include "special.h"
|
||||
#include "stat-util.h"
|
||||
#include "stdio-util.h"
|
||||
@ -201,18 +200,6 @@ void cgroup_context_remove_bpf_foreign_program(CGroupContext *c, CGroupBPFForeig
|
||||
free(p);
|
||||
}
|
||||
|
||||
void cgroup_context_remove_socket_bind(CGroupSocketBindItem **head) {
|
||||
CGroupSocketBindItem *h;
|
||||
|
||||
assert(head);
|
||||
|
||||
while (*head) {
|
||||
h = *head;
|
||||
LIST_REMOVE(socket_bind_items, *head, h);
|
||||
free(h);
|
||||
}
|
||||
}
|
||||
|
||||
void cgroup_context_done(CGroupContext *c) {
|
||||
assert(c);
|
||||
|
||||
@ -234,9 +221,6 @@ void cgroup_context_done(CGroupContext *c) {
|
||||
while (c->device_allow)
|
||||
cgroup_context_free_device_allow(c, c->device_allow);
|
||||
|
||||
cgroup_context_remove_socket_bind(&c->socket_bind_allow);
|
||||
cgroup_context_remove_socket_bind(&c->socket_bind_deny);
|
||||
|
||||
c->ip_address_allow = ip_address_access_free_all(c->ip_address_allow);
|
||||
c->ip_address_deny = ip_address_access_free_all(c->ip_address_deny);
|
||||
|
||||
@ -392,7 +376,6 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
|
||||
CGroupBPFForeignProgram *p;
|
||||
CGroupDeviceAllow *a;
|
||||
CGroupContext *c;
|
||||
CGroupSocketBindItem *bi;
|
||||
IPAddressAccessItem *iaai;
|
||||
char **path;
|
||||
char q[FORMAT_TIMESPAN_MAX];
|
||||
@ -579,34 +562,6 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
|
||||
LIST_FOREACH(programs, p, c->bpf_foreign_programs)
|
||||
fprintf(f, "%sBPFProgram: %s:%s",
|
||||
prefix, bpf_cgroup_attach_type_to_string(p->attach_type), p->bpffs_path);
|
||||
|
||||
if (c->socket_bind_allow) {
|
||||
fprintf(f, "%sSocketBindAllow:", prefix);
|
||||
LIST_FOREACH(socket_bind_items, bi, c->socket_bind_allow)
|
||||
cgroup_context_dump_socket_bind_item(bi, f);
|
||||
fputc('\n', f);
|
||||
}
|
||||
|
||||
if (c->socket_bind_deny) {
|
||||
fprintf(f, "%sSocketBindDeny:", prefix);
|
||||
LIST_FOREACH(socket_bind_items, bi, c->socket_bind_deny)
|
||||
cgroup_context_dump_socket_bind_item(bi, f);
|
||||
fputc('\n', f);
|
||||
}
|
||||
}
|
||||
|
||||
void cgroup_context_dump_socket_bind_item(const CGroupSocketBindItem *item, FILE *f) {
|
||||
const char *family = item->address_family == AF_INET ? "IPv4:" :
|
||||
item->address_family == AF_INET6 ? "IPv6:" : "";
|
||||
|
||||
if (item->nr_ports == 0)
|
||||
fprintf(f, " %sany", family);
|
||||
else if (item->nr_ports == 1)
|
||||
fprintf(f, " %s%" PRIu16, family, item->port_min);
|
||||
else {
|
||||
uint16_t port_max = item->port_min + item->nr_ports - 1;
|
||||
fprintf(f, " %s%" PRIu16 "-%" PRIu16, family, item->port_min, port_max);
|
||||
}
|
||||
}
|
||||
|
||||
int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode) {
|
||||
@ -1100,12 +1055,6 @@ static void cgroup_apply_firewall(Unit *u) {
|
||||
(void) bpf_firewall_install(u);
|
||||
}
|
||||
|
||||
static void cgroup_apply_socket_bind(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
(void) socket_bind_install(u);
|
||||
}
|
||||
|
||||
static int cgroup_apply_devices(Unit *u) {
|
||||
_cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
|
||||
const char *path;
|
||||
@ -1534,9 +1483,6 @@ static void cgroup_context_apply(
|
||||
|
||||
if (apply_mask & CGROUP_MASK_BPF_FOREIGN)
|
||||
cgroup_apply_bpf_foreign_program(u);
|
||||
|
||||
if (apply_mask & CGROUP_MASK_BPF_SOCKET_BIND)
|
||||
cgroup_apply_socket_bind(u);
|
||||
}
|
||||
|
||||
static bool unit_get_needs_bpf_firewall(Unit *u) {
|
||||
@ -1580,17 +1526,6 @@ static bool unit_get_needs_bpf_foreign_program(Unit *u) {
|
||||
return !LIST_IS_EMPTY(c->bpf_foreign_programs);
|
||||
}
|
||||
|
||||
static bool unit_get_needs_socket_bind(Unit *u) {
|
||||
CGroupContext *c;
|
||||
assert(u);
|
||||
|
||||
c = unit_get_cgroup_context(u);
|
||||
if (!c)
|
||||
return false;
|
||||
|
||||
return c->socket_bind_allow != NULL || c->socket_bind_deny != NULL;
|
||||
}
|
||||
|
||||
static CGroupMask unit_get_cgroup_mask(Unit *u) {
|
||||
CGroupMask mask = 0;
|
||||
CGroupContext *c;
|
||||
@ -1645,9 +1580,6 @@ static CGroupMask unit_get_bpf_mask(Unit *u) {
|
||||
if (unit_get_needs_bpf_foreign_program(u))
|
||||
mask |= CGROUP_MASK_BPF_FOREIGN;
|
||||
|
||||
if (unit_get_needs_socket_bind(u))
|
||||
mask |= CGROUP_MASK_BPF_SOCKET_BIND;
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
@ -3131,11 +3063,6 @@ static int cg_bpf_mask_supported(CGroupMask *ret) {
|
||||
if (r > 0)
|
||||
mask |= CGROUP_MASK_BPF_FOREIGN;
|
||||
|
||||
/* BPF-based bind{4|6} hooks */
|
||||
r = socket_bind_supported();
|
||||
if (r > 0)
|
||||
mask |= CGROUP_MASK_BPF_SOCKET_BIND;
|
||||
|
||||
*ret = mask;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -32,7 +32,6 @@ typedef struct CGroupIODeviceLatency CGroupIODeviceLatency;
|
||||
typedef struct CGroupBlockIODeviceWeight CGroupBlockIODeviceWeight;
|
||||
typedef struct CGroupBlockIODeviceBandwidth CGroupBlockIODeviceBandwidth;
|
||||
typedef struct CGroupBPFForeignProgram CGroupBPFForeignProgram;
|
||||
typedef struct CGroupSocketBindItem CGroupSocketBindItem;
|
||||
|
||||
typedef enum CGroupDevicePolicy {
|
||||
/* When devices listed, will allow those, plus built-in ones, if none are listed will allow
|
||||
@ -102,13 +101,6 @@ struct CGroupBPFForeignProgram {
|
||||
char *bpffs_path;
|
||||
};
|
||||
|
||||
struct CGroupSocketBindItem {
|
||||
LIST_FIELDS(CGroupSocketBindItem, socket_bind_items);
|
||||
int address_family;
|
||||
uint16_t nr_ports;
|
||||
uint16_t port_min;
|
||||
};
|
||||
|
||||
struct CGroupContext {
|
||||
bool cpu_accounting;
|
||||
bool io_accounting;
|
||||
@ -173,9 +165,6 @@ struct CGroupContext {
|
||||
CGroupDevicePolicy device_policy;
|
||||
LIST_HEAD(CGroupDeviceAllow, device_allow);
|
||||
|
||||
LIST_HEAD(CGroupSocketBindItem, socket_bind_allow);
|
||||
LIST_HEAD(CGroupSocketBindItem, socket_bind_deny);
|
||||
|
||||
/* Common */
|
||||
TasksMax tasks_max;
|
||||
|
||||
@ -214,7 +203,6 @@ usec_t cgroup_cpu_adjust_period(usec_t period, usec_t quota, usec_t resolution,
|
||||
void cgroup_context_init(CGroupContext *c);
|
||||
void cgroup_context_done(CGroupContext *c);
|
||||
void cgroup_context_dump(Unit *u, FILE* f, const char *prefix);
|
||||
void cgroup_context_dump_socket_bind_item(const CGroupSocketBindItem *item, FILE *f);
|
||||
|
||||
void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a);
|
||||
void cgroup_context_free_io_device_weight(CGroupContext *c, CGroupIODeviceWeight *w);
|
||||
@ -223,7 +211,6 @@ void cgroup_context_free_io_device_latency(CGroupContext *c, CGroupIODeviceLaten
|
||||
void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w);
|
||||
void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b);
|
||||
void cgroup_context_remove_bpf_foreign_program(CGroupContext *c, CGroupBPFForeignProgram *p);
|
||||
void cgroup_context_remove_socket_bind(CGroupSocketBindItem **head);
|
||||
|
||||
int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode);
|
||||
int cgroup_add_bpf_foreign_program(CGroupContext *c, uint32_t attach_type, const char *path);
|
||||
|
||||
@ -16,7 +16,6 @@
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "limits-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "percent-util.h"
|
||||
|
||||
@ -376,32 +375,6 @@ static int property_get_bpf_foreign_program(
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int property_get_socket_bind(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
CGroupSocketBindItem **items = userdata, *i;
|
||||
int r;
|
||||
|
||||
assert(items);
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "(iqq)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
LIST_FOREACH(socket_bind_items, i, *items) {
|
||||
r = sd_bus_message_append(reply, "(iqq)", i->address_family, i->nr_ports, i->port_min);
|
||||
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),
|
||||
@ -454,8 +427,6 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
|
||||
SD_BUS_PROPERTY("ManagedOOMMemoryPressureLimit", "u", NULL, offsetof(CGroupContext, moom_mem_pressure_limit), 0),
|
||||
SD_BUS_PROPERTY("ManagedOOMPreference", "s", property_get_managed_oom_preference, offsetof(CGroupContext, moom_preference), 0),
|
||||
SD_BUS_PROPERTY("BPFProgram", "a(ss)", property_get_bpf_foreign_program, 0, 0),
|
||||
SD_BUS_PROPERTY("SocketBindAllow", "a(iqq)", property_get_socket_bind, offsetof(CGroupContext, socket_bind_allow), 0),
|
||||
SD_BUS_PROPERTY("SocketBindDeny", "a(iqq)", property_get_socket_bind, offsetof(CGroupContext, socket_bind_deny), 0),
|
||||
SD_BUS_VTABLE_END
|
||||
};
|
||||
|
||||
@ -1878,89 +1849,6 @@ int bus_cgroup_set_property(
|
||||
|
||||
return 1;
|
||||
}
|
||||
if (STR_IN_SET(name, "SocketBindAllow", "SocketBindDeny")) {
|
||||
CGroupSocketBindItem **list;
|
||||
uint16_t nr_ports, port_min;
|
||||
size_t n = 0;
|
||||
int family;
|
||||
|
||||
list = streq(name, "SocketBindAllow") ? &c->socket_bind_allow : &c->socket_bind_deny;
|
||||
|
||||
r = sd_bus_message_enter_container(message, 'a', "(iqq)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
while ((r = sd_bus_message_read(message, "(iqq)", &family, &nr_ports, &port_min)) > 0) {
|
||||
|
||||
if (!IN_SET(family, AF_UNSPEC, AF_INET, AF_INET6))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= expects INET or INET6 family, if specified.", name);
|
||||
|
||||
if (port_min + (uint32_t) nr_ports > (1 << 16))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= expects maximum port value lesser than 65536.", name);
|
||||
|
||||
if (port_min == 0 && nr_ports != 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= expects port range starting with positive value.", name);
|
||||
|
||||
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
||||
_cleanup_free_ CGroupSocketBindItem *item = NULL;
|
||||
|
||||
item = new(CGroupSocketBindItem, 1);
|
||||
if (!item)
|
||||
return log_oom();
|
||||
|
||||
*item = (CGroupSocketBindItem) {
|
||||
.address_family = family,
|
||||
.nr_ports = nr_ports,
|
||||
.port_min = port_min
|
||||
};
|
||||
|
||||
LIST_PREPEND(socket_bind_items, *list, TAKE_PTR(item));
|
||||
}
|
||||
n++;
|
||||
}
|
||||
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 *buf = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
CGroupSocketBindItem *item;
|
||||
size_t size = 0;
|
||||
|
||||
if (n == 0)
|
||||
cgroup_context_remove_socket_bind(list);
|
||||
else {
|
||||
if ((u->manager->cgroup_supported & CGROUP_MASK_BPF_SOCKET_BIND) == 0)
|
||||
log_full(LOG_DEBUG,
|
||||
"Unit %s configures source compiled BPF programs "
|
||||
"but the local system does not support that.\n"
|
||||
"Starting this unit will fail!", u->id);
|
||||
}
|
||||
|
||||
f = open_memstream_unlocked(&buf, &size);
|
||||
if (!f)
|
||||
return -ENOMEM;
|
||||
|
||||
fprintf(f, "%s:", name);
|
||||
|
||||
LIST_FOREACH(socket_bind_items, item, *list)
|
||||
cgroup_context_dump_socket_bind_item(item, f);
|
||||
|
||||
fputc('\n', f);
|
||||
|
||||
r = fflush_and_check(f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
unit_write_setting(u, flags, name, buf);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@ -235,9 +235,7 @@ $1.ManagedOOMMemoryPressure, config_parse_managed_oom_mode,
|
||||
$1.ManagedOOMMemoryPressureLimit, config_parse_managed_oom_mem_pressure_limit, 0, offsetof($1, cgroup_context.moom_mem_pressure_limit)
|
||||
$1.ManagedOOMPreference, config_parse_managed_oom_preference, 0, offsetof($1, cgroup_context.moom_preference)
|
||||
$1.NetClass, config_parse_warn_compat, DISABLED_LEGACY, 0
|
||||
$1.BPFProgram, config_parse_bpf_foreign_program, 0, offsetof($1, cgroup_context)
|
||||
$1.SocketBindAllow, config_parse_cgroup_socket_bind, 0, offsetof($1, cgroup_context.socket_bind_allow)
|
||||
$1.SocketBindDeny, config_parse_cgroup_socket_bind, 0, offsetof($1, cgroup_context.socket_bind_deny)'
|
||||
$1.BPFProgram, config_parse_bpf_foreign_program, 0, offsetof($1, cgroup_context)'
|
||||
)m4_dnl
|
||||
Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description)
|
||||
Unit.Documentation, config_parse_documentation, 0, offsetof(Unit, documentation)
|
||||
|
||||
@ -55,7 +55,6 @@
|
||||
#endif
|
||||
#include "securebits-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "socket-bind.h"
|
||||
#include "socket-netlink.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
@ -5658,73 +5657,6 @@ int config_parse_bpf_foreign_program(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_cgroup_socket_bind(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
_cleanup_free_ CGroupSocketBindItem *item = NULL;
|
||||
const char *address_family = NULL, *user_port;
|
||||
uint16_t nr_ports = 0, port_min = 0;
|
||||
CGroupSocketBindItem **head = data;
|
||||
_cleanup_free_ char *word = NULL;
|
||||
int af = AF_UNSPEC, r;
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
cgroup_context_remove_socket_bind(head);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = extract_first_word(&rvalue, &word, ":", 0);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
|
||||
if (rvalue)
|
||||
address_family = word;
|
||||
|
||||
if (address_family) {
|
||||
if (streq(address_family, "IPv4"))
|
||||
af = AF_INET;
|
||||
else if (streq(address_family, "IPv6"))
|
||||
af = AF_INET6;
|
||||
else
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Only IPv4 or IPv6 protocols are supported, ignoring");
|
||||
}
|
||||
|
||||
user_port = rvalue ?: word;
|
||||
if (!streq(user_port, "any")) {
|
||||
uint16_t port_max;
|
||||
|
||||
r = parse_ip_port_range(user_port, &port_min, &port_max);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Invalid port or port range, ignoring: %m");
|
||||
|
||||
nr_ports = 1 + port_max - port_min;
|
||||
}
|
||||
|
||||
item = new(CGroupSocketBindItem, 1);
|
||||
if (!item)
|
||||
return log_oom();
|
||||
*item = (CGroupSocketBindItem) {
|
||||
.address_family = af,
|
||||
.nr_ports = nr_ports,
|
||||
.port_min = port_min,
|
||||
};
|
||||
|
||||
LIST_PREPEND(socket_bind_items, *head, TAKE_PTR(item));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int merge_by_names(Unit **u, Set *names, const char *id) {
|
||||
char *k;
|
||||
int r;
|
||||
|
||||
@ -141,7 +141,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_mount_images);
|
||||
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);
|
||||
|
||||
/* gperf prototypes */
|
||||
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
|
||||
|
||||
@ -105,8 +105,6 @@ libcore_sources = '''
|
||||
slice.h
|
||||
smack-setup.c
|
||||
smack-setup.h
|
||||
socket-bind.c
|
||||
socket-bind.h
|
||||
socket.c
|
||||
socket.h
|
||||
swap.c
|
||||
@ -125,11 +123,6 @@ libcore_sources = '''
|
||||
unit.h
|
||||
'''.split()
|
||||
|
||||
subdir('bpf/socket_bind')
|
||||
if conf.get('BPF_FRAMEWORK') == 1
|
||||
libcore_sources += [socket_bind_skel_h]
|
||||
endif
|
||||
|
||||
load_fragment_gperf_gperf = custom_target(
|
||||
'load-fragment-gperf.gperf',
|
||||
input : 'load-fragment-gperf.gperf.m4',
|
||||
@ -159,7 +152,6 @@ libcore = static_library(
|
||||
include_directories : includes,
|
||||
dependencies : [versiondep,
|
||||
threads,
|
||||
libbpf,
|
||||
librt,
|
||||
libseccomp,
|
||||
libpam,
|
||||
|
||||
@ -1,238 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#if BPF_FRAMEWORK
|
||||
#include <bpf/bpf.h>
|
||||
#endif
|
||||
|
||||
#include "fd-util.h"
|
||||
#include "socket-bind.h"
|
||||
|
||||
#if BPF_FRAMEWORK
|
||||
/* libbpf, clang, llvm and bpftool compile time dependencies are satisfied */
|
||||
#include "bpf-link.h"
|
||||
#include "bpf/socket_bind/socket-bind.skel.h"
|
||||
#include "bpf/socket_bind/socket-bind-api.bpf.h"
|
||||
|
||||
static struct socket_bind_bpf *socket_bind_bpf_free(struct socket_bind_bpf *obj) {
|
||||
/* socket_bind_bpf__destroy handles object == NULL case */
|
||||
(void) socket_bind_bpf__destroy(obj);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(struct socket_bind_bpf *, socket_bind_bpf_free);
|
||||
|
||||
static int update_rules_map(
|
||||
int map_fd, CGroupSocketBindItem *head) {
|
||||
CGroupSocketBindItem *item;
|
||||
uint32_t i = 0;
|
||||
|
||||
assert(map_fd >= 0);
|
||||
|
||||
LIST_FOREACH(socket_bind_items, item, head) {
|
||||
const uint32_t key = i++;
|
||||
struct socket_bind_rule val = {
|
||||
.address_family = (uint32_t) item->address_family,
|
||||
.nr_ports = item->nr_ports,
|
||||
.port_min = item->port_min,
|
||||
};
|
||||
|
||||
if (bpf_map_update_elem(map_fd, &key, &val, BPF_ANY) != 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int prepare_socket_bind_bpf(
|
||||
Unit *u, CGroupSocketBindItem *allow, CGroupSocketBindItem *deny, struct socket_bind_bpf **ret_obj) {
|
||||
_cleanup_(socket_bind_bpf_freep) struct socket_bind_bpf *obj = 0;
|
||||
uint32_t allow_count = 0, deny_count = 0;
|
||||
int allow_map_fd, deny_map_fd, r;
|
||||
CGroupSocketBindItem *item;
|
||||
|
||||
assert(ret_obj);
|
||||
|
||||
LIST_FOREACH(socket_bind_items, item, allow)
|
||||
allow_count++;
|
||||
|
||||
LIST_FOREACH(socket_bind_items, item, deny)
|
||||
deny_count++;
|
||||
|
||||
if (allow_count > SOCKET_BIND_MAX_RULES)
|
||||
return log_unit_error_errno(u, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Maximum number of socket bind rules=%u is exceeded", SOCKET_BIND_MAX_RULES);
|
||||
|
||||
if (deny_count > SOCKET_BIND_MAX_RULES)
|
||||
return log_unit_error_errno(u, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Maximum number of socket bind rules=%u is exceeded", SOCKET_BIND_MAX_RULES);
|
||||
|
||||
obj = socket_bind_bpf__open();
|
||||
if (!obj)
|
||||
return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOMEM), "Failed to open BPF object");
|
||||
|
||||
if (bpf_map__resize(obj->maps.sd_bind_allow, MAX(allow_count, 1u)) != 0)
|
||||
return log_unit_error_errno(u, errno,
|
||||
"Failed to resize BPF map '%s': %m", bpf_map__name(obj->maps.sd_bind_allow));
|
||||
|
||||
if (bpf_map__resize(obj->maps.sd_bind_deny, MAX(deny_count, 1u)) != 0)
|
||||
return log_unit_error_errno(u, errno,
|
||||
"Failed to resize BPF map '%s': %m", bpf_map__name(obj->maps.sd_bind_deny));
|
||||
|
||||
if (socket_bind_bpf__load(obj) != 0)
|
||||
return log_unit_error_errno(u, errno, "Failed to load BPF object");
|
||||
|
||||
allow_map_fd = bpf_map__fd(obj->maps.sd_bind_allow);
|
||||
assert(allow_map_fd >= 0);
|
||||
|
||||
r = update_rules_map(allow_map_fd, allow);
|
||||
if (r < 0)
|
||||
return log_unit_error_errno(
|
||||
u, r, "Failed to put socket bind allow rules into BPF map '%s'",
|
||||
bpf_map__name(obj->maps.sd_bind_allow));
|
||||
|
||||
deny_map_fd = bpf_map__fd(obj->maps.sd_bind_deny);
|
||||
assert(deny_map_fd >= 0);
|
||||
|
||||
r = update_rules_map(deny_map_fd, deny);
|
||||
if (r < 0)
|
||||
return log_unit_error_errno(
|
||||
u, r, "Failed to put socket bind deny rules into BPF map '%s'",
|
||||
bpf_map__name(obj->maps.sd_bind_deny));
|
||||
|
||||
*ret_obj = TAKE_PTR(obj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socket_bind_supported(void) {
|
||||
_cleanup_(socket_bind_bpf_freep) struct socket_bind_bpf *obj = NULL;
|
||||
|
||||
int r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Can't determine whether the unified hierarchy is used: %m");
|
||||
|
||||
if (r == 0) {
|
||||
log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Not running with unified cgroup hierarchy, BPF is not supported");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!bpf_probe_prog_type(BPF_PROG_TYPE_CGROUP_SOCK_ADDR, /*ifindex=*/0)) {
|
||||
log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"BPF program type cgroup_sock_addr is not supported");
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = prepare_socket_bind_bpf(/*unit=*/NULL, /*allow_rules=*/NULL, /*deny_rules=*/NULL, &obj);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "BPF based socket_bind is not supported: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return can_link_bpf_program(obj->progs.sd_bind4);
|
||||
}
|
||||
|
||||
int socket_bind_add_initial_link_fd(Unit *u, int fd) {
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
if (!u->initial_socket_bind_link_fds) {
|
||||
u->initial_socket_bind_link_fds = fdset_new();
|
||||
if (!u->initial_socket_bind_link_fds)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
r = fdset_put(u->initial_socket_bind_link_fds, fd);
|
||||
if (r < 0)
|
||||
return log_unit_error_errno(u, r, "Failed to put socket-bind BPF link fd %d to initial fdset", fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int socket_bind_install_impl(Unit *u) {
|
||||
_cleanup_(bpf_link_freep) struct bpf_link *ipv4 = NULL, *ipv6 = NULL;
|
||||
_cleanup_(socket_bind_bpf_freep) struct socket_bind_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->socket_bind_allow && !cc->socket_bind_deny)
|
||||
return 0;
|
||||
|
||||
r = prepare_socket_bind_bpf(u, cc->socket_bind_allow, cc->socket_bind_deny, &obj);
|
||||
if (r < 0)
|
||||
return log_unit_error_errno(u, r, "Failed to load BPF object: %m");
|
||||
|
||||
cgroup_fd = open(cgroup_path, O_RDONLY | O_CLOEXEC, 0);
|
||||
if (cgroup_fd < 0)
|
||||
return log_unit_error_errno(
|
||||
u, errno, "Failed to open cgroup=%s for reading", cgroup_path);
|
||||
|
||||
ipv4 = bpf_program__attach_cgroup(obj->progs.sd_bind4, cgroup_fd);
|
||||
r = libbpf_get_error(ipv4);
|
||||
if (r != 0)
|
||||
return log_unit_error_errno(u, r, "Failed to link '%s' cgroup-bpf program",
|
||||
bpf_program__name(obj->progs.sd_bind4));
|
||||
|
||||
ipv6 = bpf_program__attach_cgroup(obj->progs.sd_bind6, cgroup_fd);
|
||||
r = libbpf_get_error(ipv6);
|
||||
if (r != 0)
|
||||
return log_unit_error_errno(u, r, "Failed to link '%s' cgroup-bpf program",
|
||||
bpf_program__name(obj->progs.sd_bind6));
|
||||
|
||||
u->ipv4_socket_bind_link = TAKE_PTR(ipv4);
|
||||
u->ipv6_socket_bind_link = TAKE_PTR(ipv6);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socket_bind_install(Unit *u) {
|
||||
int r = socket_bind_install_impl(u);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
|
||||
fdset_close(u->initial_socket_bind_link_fds);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int serialize_socket_bind(Unit *u, FILE *f, FDSet *fds) {
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
r = serialize_bpf_link(f, fds, "ipv4-socket-bind-bpf-link", u->ipv4_socket_bind_link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return serialize_bpf_link(f, fds, "ipv6-socket-bind-bpf-link", u->ipv6_socket_bind_link);
|
||||
}
|
||||
|
||||
#else /* ! BPF_FRAMEWORK */
|
||||
int socket_bind_supported(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socket_bind_add_initial_link_fd(Unit *u, int fd) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socket_bind_install(Unit *u) {
|
||||
log_unit_debug(u, "Failed to install socket bind: BPF framework is not supported");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int serialize_socket_bind(Unit *u, FILE *f, FDSet *fds) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@ -1,15 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include "fdset.h"
|
||||
#include "unit.h"
|
||||
|
||||
int socket_bind_supported(void);
|
||||
|
||||
/* Add BPF link fd created before daemon-reload or daemon-reexec.
|
||||
* FDs will be closed at the end of socket_bind_install. */
|
||||
int socket_bind_add_initial_link_fd(Unit *u, int fd);
|
||||
|
||||
int socket_bind_install(Unit *u);
|
||||
|
||||
int serialize_socket_bind(Unit *u, FILE *f, FDSet *fds);
|
||||
@ -31,6 +31,7 @@
|
||||
#NUMAMask=
|
||||
#RuntimeWatchdogSec=0
|
||||
#RebootWatchdogSec=10min
|
||||
#ShutdownWatchdogSec=10min
|
||||
#KExecWatchdogSec=0
|
||||
#WatchdogDevice=
|
||||
#CapabilityBoundingSet=
|
||||
|
||||
@ -7,7 +7,6 @@
|
||||
#include "format-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "serialize.h"
|
||||
#include "socket-bind.h"
|
||||
#include "string-table.h"
|
||||
#include "unit-serialize.h"
|
||||
#include "user-util.h"
|
||||
@ -152,8 +151,6 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
|
||||
(void) serialize_cgroup_mask(f, "cgroup-enabled-mask", u->cgroup_enabled_mask);
|
||||
(void) serialize_cgroup_mask(f, "cgroup-invalidated-mask", u->cgroup_invalidated_mask);
|
||||
|
||||
(void) serialize_socket_bind(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))
|
||||
@ -365,23 +362,6 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
|
||||
else if (MATCH_DESERIALIZE_IMMEDIATE("cgroup-invalidated-mask", l, v, cg_mask_from_string, u->cgroup_invalidated_mask))
|
||||
continue;
|
||||
|
||||
else if (STR_IN_SET(l, "ipv4-socket-bind-bpf-link-fd", "ipv6-socket-bind-bpf-link-fd")) {
|
||||
int fd;
|
||||
|
||||
if (safe_atoi(v, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
|
||||
log_unit_debug(u, "Failed to parse %s value: %s, ignoring.", l, v);
|
||||
else {
|
||||
if (fdset_remove(fds, fd) < 0) {
|
||||
log_unit_debug(u, "Failed to remove %s value=%d from fdset", l, fd);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
(void) socket_bind_add_initial_link_fd(u, fd);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
else if (streq(l, "ref-uid")) {
|
||||
uid_t uid;
|
||||
|
||||
|
||||
@ -41,7 +41,6 @@
|
||||
#include "rm-rf.h"
|
||||
#include "set.h"
|
||||
#include "signal-util.h"
|
||||
#include "socket-bind.h"
|
||||
#include "sparse-endian.h"
|
||||
#include "special.h"
|
||||
#include "specifier.h"
|
||||
@ -57,9 +56,6 @@
|
||||
#include "unit.h"
|
||||
#include "user-util.h"
|
||||
#include "virt.h"
|
||||
#if BPF_FRAMEWORK
|
||||
#include "bpf-link.h"
|
||||
#endif
|
||||
|
||||
/* Thresholds for logging at INFO level about resource consumption */
|
||||
#define MENTIONWORTHY_CPU_NSEC (1 * NSEC_PER_SEC)
|
||||
@ -667,13 +663,6 @@ Unit* unit_free(Unit *u) {
|
||||
if (u->on_console)
|
||||
manager_unref_console(u->manager);
|
||||
|
||||
|
||||
fdset_free(u->initial_socket_bind_link_fds);
|
||||
#if BPF_FRAMEWORK
|
||||
bpf_link_free(u->ipv4_socket_bind_link);
|
||||
bpf_link_free(u->ipv6_socket_bind_link);
|
||||
#endif
|
||||
|
||||
unit_release_cgroup(u);
|
||||
|
||||
if (!MANAGER_IS_RELOADING(u->manager))
|
||||
|
||||
@ -309,15 +309,6 @@ typedef struct Unit {
|
||||
* attached to unit cgroup by provided program fd and attach type. */
|
||||
Hashmap *bpf_foreign_by_key;
|
||||
|
||||
FDSet *initial_socket_bind_link_fds;
|
||||
#if BPF_FRAMEWORK
|
||||
/* BPF links to BPF programs attached to cgroup/bind{4|6} hooks and
|
||||
* responsible for allowing or denying a unit to bind(2) to a socket
|
||||
* address. */
|
||||
struct bpf_link *ipv4_socket_bind_link;
|
||||
struct bpf_link *ipv6_socket_bind_link;
|
||||
#endif
|
||||
|
||||
uint64_t ip_accounting_extra[_CGROUP_IP_ACCOUNTING_METRIC_MAX];
|
||||
|
||||
/* Low-priority event source which is used to remove watched PIDs that have gone away, and subscribe to any new
|
||||
|
||||
@ -1,38 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include "bpf-link.h"
|
||||
#include "serialize.h"
|
||||
|
||||
bool can_link_bpf_program(struct bpf_program *prog) {
|
||||
_cleanup_(bpf_link_freep) struct bpf_link *link = NULL;
|
||||
|
||||
assert(prog);
|
||||
|
||||
/* Pass invalid cgroup fd intentionally. */
|
||||
link = bpf_program__attach_cgroup(prog, /*cgroup_fd=*/-1);
|
||||
|
||||
/* EBADF indicates that bpf_link is supported by kernel. */
|
||||
return libbpf_get_error(link) == -EBADF;
|
||||
}
|
||||
|
||||
int serialize_bpf_link(FILE *f, FDSet *fds, const char *key, struct bpf_link *link) {
|
||||
int fd;
|
||||
|
||||
assert(key);
|
||||
|
||||
if (!link)
|
||||
return -ENOENT;
|
||||
|
||||
if (libbpf_get_error(link) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
fd = bpf_link__fd(link);
|
||||
return serialize_fd(f, fds, key, fd);
|
||||
}
|
||||
|
||||
struct bpf_link *bpf_link_free(struct bpf_link *link) {
|
||||
/* bpf_link__destroy handles link == NULL case */
|
||||
(void) bpf_link__destroy(link);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bpf/libbpf.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "fdset.h"
|
||||
#include "macro.h"
|
||||
|
||||
bool can_link_bpf_program(struct bpf_program *prog);
|
||||
|
||||
int serialize_bpf_link(FILE *f, FDSet *fds, const char *key, struct bpf_link *link);
|
||||
|
||||
struct bpf_link *bpf_link_free(struct bpf_link *p);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(struct bpf_link *, bpf_link_free);
|
||||
@ -862,57 +862,6 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (STR_IN_SET(field, "SocketBindAllow",
|
||||
"SocketBindDeny")) {
|
||||
if (isempty(eq))
|
||||
r = sd_bus_message_append(m, "(sv)", field, "a(iqq)", 0);
|
||||
else {
|
||||
const char *address_family, *user_port;
|
||||
_cleanup_free_ char *word = NULL;
|
||||
int family = AF_UNSPEC;
|
||||
|
||||
r = extract_first_word(&eq, &word, ":", 0);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse %s: %m", field);
|
||||
|
||||
address_family = eq ? word : NULL;
|
||||
if (address_family) {
|
||||
if (!STR_IN_SET(address_family, "IPv4", "IPv6"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Only IPv4 and IPv6 protocols are supported");
|
||||
|
||||
if (streq(address_family, "IPv4"))
|
||||
family = AF_INET;
|
||||
else
|
||||
family = AF_INET6;
|
||||
}
|
||||
|
||||
user_port = eq ? eq : word;
|
||||
if (streq(user_port, "any")) {
|
||||
r = sd_bus_message_append(m, "(sv)", field, "a(iqq)", 1, family, 0, 0);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
} else {
|
||||
uint16_t port_min, port_max;
|
||||
|
||||
r = parse_ip_port_range(user_port, &port_min, &port_max);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Invalid port or port range: %s", user_port);
|
||||
|
||||
r = sd_bus_message_append(
|
||||
m, "(sv)", field, "a(iqq)", 1, family, port_max - port_min + 1, port_min);
|
||||
}
|
||||
}
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -319,13 +319,6 @@ if conf.get('HAVE_LIBIPTC') == 1
|
||||
shared_sources += files('firewall-util-iptables.c')
|
||||
endif
|
||||
|
||||
if conf.get('HAVE_LIBBPF') == 1
|
||||
shared_sources += files('''
|
||||
bpf-link.c
|
||||
bpf-link.h
|
||||
'''.split())
|
||||
endif
|
||||
|
||||
if conf.get('HAVE_KMOD') == 1
|
||||
shared_sources += files('module-util.c')
|
||||
endif
|
||||
@ -385,7 +378,6 @@ libshared_name = 'systemd-shared-@0@'.format(meson.project_version())
|
||||
libshared_deps = [threads,
|
||||
libacl,
|
||||
libblkid,
|
||||
libbpf,
|
||||
libcap,
|
||||
libcrypt,
|
||||
libgcrypt,
|
||||
|
||||
@ -1712,34 +1712,6 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
return 1;
|
||||
} else if (STR_IN_SET(name, "SocketBindAllow", "SocketBindDeny")) {
|
||||
uint16_t nr_ports, port_min;
|
||||
const char *family;
|
||||
int af;
|
||||
|
||||
r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(iqq)");
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
while ((r = sd_bus_message_read(m, "(iqq)", &af, &nr_ports, &port_min)) > 0) {
|
||||
family = af == AF_INET ? "IPv4:" : af == AF_INET6 ? "IPv6:" : "";
|
||||
if (nr_ports == 0)
|
||||
bus_print_property_valuef(name, expected_value, value, "%sany", family);
|
||||
else if (nr_ports == 1)
|
||||
bus_print_property_valuef(
|
||||
name, expected_value, value, "%s%hu", family, port_min);
|
||||
else
|
||||
bus_print_property_valuef(
|
||||
name, expected_value, value, "%s%hu-%hu", family, port_min,
|
||||
(uint16_t) (port_min + nr_ports - 1));
|
||||
}
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
r = sd_bus_message_exit_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@ -611,12 +611,3 @@ tests += [
|
||||
[libudev],
|
||||
[threads]],
|
||||
]
|
||||
|
||||
tests += [
|
||||
[['src/test/test-socket-bind.c'],
|
||||
[libcore,
|
||||
libshared],
|
||||
[libbpf],
|
||||
core_includes,
|
||||
'BPF_FRAMEWORK'],
|
||||
]
|
||||
|
||||
@ -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");
|
||||
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");
|
||||
|
||||
@ -1,151 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "load-fragment.h"
|
||||
#include "manager.h"
|
||||
#include "process-util.h"
|
||||
#include "rlimit-util.h"
|
||||
#include "rm-rf.h"
|
||||
#include "service.h"
|
||||
#include "socket-bind.h"
|
||||
#include "strv.h"
|
||||
#include "tests.h"
|
||||
#include "unit.h"
|
||||
#include "virt.h"
|
||||
|
||||
static int find_netcat_executable(char **ret_path) {
|
||||
char **candidates = STRV_MAKE("ncat", "nc", "netcat"), **c;
|
||||
int r = 0;
|
||||
|
||||
STRV_FOREACH(c, candidates) {
|
||||
r = find_executable(*c, ret_path);
|
||||
if (r == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int test_socket_bind(
|
||||
Manager *m,
|
||||
const char *unit_name,
|
||||
const char *netcat_path,
|
||||
const char *port,
|
||||
char **allow_rules,
|
||||
char **deny_rules) {
|
||||
_cleanup_free_ char *exec_start = NULL;
|
||||
_cleanup_(unit_freep) Unit *u = NULL;
|
||||
CGroupSocketBindItem *bi;
|
||||
CGroupContext *cc = NULL;
|
||||
char **rule;
|
||||
int cld_code, r;
|
||||
|
||||
assert_se(u = unit_new(m, sizeof(Service)));
|
||||
assert_se(unit_add_name(u, unit_name) == 0);
|
||||
assert_se(cc = unit_get_cgroup_context(u));
|
||||
|
||||
STRV_FOREACH(rule, allow_rules) {
|
||||
r = config_parse_cgroup_socket_bind(
|
||||
u->id, "filename", 1, "Service", 1, "SocketBindAllow", 0,
|
||||
*rule, &cc->socket_bind_allow, u);
|
||||
if (r < 0)
|
||||
return log_unit_error_errno(u, r, "Failed to parse SocketBindAllow: %m");
|
||||
}
|
||||
|
||||
fprintf(stderr, "SocketBindAllow:");
|
||||
LIST_FOREACH(socket_bind_items, bi, cc->socket_bind_allow)
|
||||
cgroup_context_dump_socket_bind_item(bi, stderr);
|
||||
fputc('\n', stderr);
|
||||
|
||||
STRV_FOREACH(rule, deny_rules) {
|
||||
r = config_parse_cgroup_socket_bind(
|
||||
u->id, "filename", 1, "Service", 1, "SocketBindDeny", 0,
|
||||
*rule, &cc->socket_bind_deny, u);
|
||||
if (r < 0)
|
||||
return log_unit_error_errno(u, r, "Failed to parse SocketBindDeny: %m");
|
||||
}
|
||||
|
||||
fprintf(stderr, "SocketBindDeny:");
|
||||
LIST_FOREACH(socket_bind_items, bi, cc->socket_bind_deny)
|
||||
cgroup_context_dump_socket_bind_item(bi, stderr);
|
||||
fputc('\n', stderr);
|
||||
|
||||
exec_start = strjoin("-timeout --preserve-status -sSIGTERM 1s ", netcat_path, " -l ", port, " -vv");
|
||||
assert_se(exec_start != NULL);
|
||||
|
||||
r = config_parse_exec(u->id, "filename", 1, "Service", 1, "ExecStart",
|
||||
SERVICE_EXEC_START, exec_start, SERVICE(u)->exec_command, u);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse ExecStart");
|
||||
|
||||
SERVICE(u)->type = SERVICE_ONESHOT;
|
||||
u->load_state = UNIT_LOADED;
|
||||
|
||||
r = unit_start(u);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Unit start failed %m");
|
||||
|
||||
while (!IN_SET(SERVICE(u)->state, SERVICE_DEAD, SERVICE_FAILED)) {
|
||||
r = sd_event_run(m->event, UINT64_MAX);
|
||||
if (r < 0)
|
||||
return log_error_errno(errno, "Event run failed %m");
|
||||
}
|
||||
|
||||
cld_code = SERVICE(u)->exec_command[SERVICE_EXEC_START]->exec_status.code;
|
||||
if (cld_code != CLD_EXITED)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "ExecStart didn't exited, code='%s'", sigchld_code_to_string(cld_code));
|
||||
|
||||
if (SERVICE(u)->state != SERVICE_DEAD)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Service is not dead");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
_cleanup_free_ char *unit_dir = NULL, *netcat_path = NULL;
|
||||
_cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
|
||||
_cleanup_(manager_freep) Manager *m = NULL;
|
||||
struct rlimit rl;
|
||||
int r;
|
||||
|
||||
test_setup_logging(LOG_DEBUG);
|
||||
|
||||
if (detect_container() > 0)
|
||||
return log_tests_skipped("test-bpf fails inside LXC and Docker containers: https://github.com/systemd/systemd/issues/9666");
|
||||
|
||||
if (getuid() != 0)
|
||||
return log_tests_skipped("not running as root");
|
||||
|
||||
assert_se(getrlimit(RLIMIT_MEMLOCK, &rl) >= 0);
|
||||
rl.rlim_cur = rl.rlim_max = MAX(rl.rlim_max, CAN_MEMLOCK_SIZE);
|
||||
(void) setrlimit_closest(RLIMIT_MEMLOCK, &rl);
|
||||
|
||||
if (!can_memlock())
|
||||
return log_tests_skipped("Can't use mlock(), skipping.");
|
||||
|
||||
r = socket_bind_supported();
|
||||
if (r <= 0)
|
||||
return log_tests_skipped("socket-bind is not supported, skipping.");
|
||||
|
||||
if (find_netcat_executable(&netcat_path) != 0)
|
||||
return log_tests_skipped("Can not find netcat executable, skipping.");
|
||||
|
||||
r = enter_cgroup_subroot(NULL);
|
||||
if (r == -ENOMEDIUM)
|
||||
return log_tests_skipped("cgroupfs not available");
|
||||
|
||||
assert_se(get_testdata_dir("units", &unit_dir) >= 0);
|
||||
assert_se(set_unit_path(unit_dir) >= 0);
|
||||
assert_se(runtime_dir = setup_fake_runtime_dir());
|
||||
|
||||
assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0);
|
||||
assert_se(manager_startup(m, NULL, NULL) >= 0);
|
||||
|
||||
assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "2000", STRV_MAKE("2000"), STRV_MAKE("any")) >= 0);
|
||||
assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "2000", STRV_MAKE("IPv6:2001-2002"), STRV_MAKE("any")) >= 0);
|
||||
assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "6666", STRV_MAKE("IPv4:6666", "6667"), STRV_MAKE("any")) >= 0);
|
||||
assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "6666", STRV_MAKE("6667", "6668", ""), STRV_MAKE("any")) >= 0);
|
||||
assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "7777", STRV_MAKE_EMPTY, STRV_MAKE_EMPTY) >= 0);
|
||||
assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "8888", STRV_MAKE("any"), STRV_MAKE("any")) >= 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -162,8 +162,6 @@ SetCredential=
|
||||
Slice=
|
||||
SloppyOptions=
|
||||
SmackProcessLabel=
|
||||
SocketBindAllow=
|
||||
SocketBindDeny=
|
||||
StandardError=
|
||||
StandardInput=
|
||||
StandardInputData=
|
||||
|
||||
@ -52,8 +52,6 @@ RuntimeMaxSec=
|
||||
SendSIGHUP=
|
||||
SendSIGKILL=
|
||||
Slice=
|
||||
SocketBindAllow=
|
||||
SocketBindDeny=
|
||||
StartupBlockIOWeight=
|
||||
StartupCPUShares=
|
||||
StartupCPUWeight=
|
||||
|
||||
@ -88,8 +88,6 @@ RequiresMountsFor=
|
||||
RequiresOverridable=
|
||||
Requisite=
|
||||
RequisiteOverridable=
|
||||
SocketBindAllow=
|
||||
SocketBindDeny=
|
||||
SourcePath=
|
||||
StartLimitAction=
|
||||
StartLimitBurst=
|
||||
|
||||
@ -45,8 +45,6 @@ MemoryMin=
|
||||
MemorySwapMax=
|
||||
NetClass=
|
||||
Slice=
|
||||
SocketBindAllow=
|
||||
SocketBindDeny=
|
||||
StartupBlockIOWeight=
|
||||
StartupCPUShares=
|
||||
StartupCPUWeight=
|
||||
|
||||
@ -204,8 +204,6 @@ SmackLabel=
|
||||
SmackLabelIPIn=
|
||||
SmackLabelIPOut=
|
||||
SmackProcessLabel=
|
||||
SocketBindAllow=
|
||||
SocketBindDeny=
|
||||
SocketGroup=
|
||||
SocketMode=
|
||||
SocketProtocol=
|
||||
|
||||
@ -158,8 +158,6 @@ SendSIGKILL=
|
||||
SetCredential=
|
||||
Slice=
|
||||
SmackProcessLabel=
|
||||
SocketBindAllow=
|
||||
SocketBindDeny=
|
||||
StandardError=
|
||||
StandardInput=
|
||||
StandardInputData=
|
||||
|
||||
@ -1,123 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: LGPL-2.1+
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
def clang_arch_flag(arch):
|
||||
return '-D__{}__'.format(arch)
|
||||
|
||||
|
||||
def target_triplet():
|
||||
gcc_exec = 'gcc'
|
||||
|
||||
try:
|
||||
return subprocess.check_output([gcc_exec, '-dumpmachine'],
|
||||
universal_newlines=True).strip()
|
||||
except subprocess.CalledProcessError as e:
|
||||
logging.error('Failed to get target triplet: {}'.format(e))
|
||||
except FileNotFoundError:
|
||||
logging.error('gcc not installed')
|
||||
return None
|
||||
|
||||
|
||||
def clang_compile(clang_exec, clang_flags, src_c, out_file, target_arch,
|
||||
target_triplet):
|
||||
clang_args = [clang_exec, *clang_flags, target_arch, '-I.']
|
||||
|
||||
if target_triplet:
|
||||
clang_args += [
|
||||
'-isystem',
|
||||
'/usr/include/{}'.format(target_triplet)]
|
||||
|
||||
clang_args += [
|
||||
'-idirafter',
|
||||
'/usr/local/include',
|
||||
'-idirafter',
|
||||
'/usr/include']
|
||||
|
||||
clang_args += [src_c, '-o', out_file]
|
||||
|
||||
logging.debug('{}'.format(' '.join(clang_args)))
|
||||
subprocess.check_call(clang_args)
|
||||
|
||||
|
||||
def llvm_strip(llvm_strip_exec, in_file):
|
||||
llvm_strip_args = [llvm_strip_exec, '-g', in_file]
|
||||
|
||||
logging.debug('Stripping useless DWARF info:')
|
||||
logging.debug('{}'.format(' '.join(llvm_strip_args)))
|
||||
|
||||
subprocess.check_call(llvm_strip_args)
|
||||
|
||||
|
||||
def gen_bpf_skeleton(bpftool_exec, in_file, out_fd):
|
||||
bpftool_args = [bpftool_exec, 'g', 's', in_file]
|
||||
|
||||
logging.debug('Generating BPF skeleton:')
|
||||
logging.debug('{}'.format(' '.join(bpftool_args)))
|
||||
subprocess.check_call(bpftool_args, stdout=out_fd)
|
||||
|
||||
|
||||
def bpf_build(args):
|
||||
clang_flags = [
|
||||
'-Wno-compare-distinct-pointer-types',
|
||||
'-O2',
|
||||
'-target',
|
||||
'bpf',
|
||||
'-g',
|
||||
'-c',
|
||||
]
|
||||
|
||||
clang_out_path = pathlib.Path(args.bpf_src_c).with_suffix('.o')
|
||||
with clang_out_path.open(mode='w') as clang_out, \
|
||||
open(args.bpf_skel_h, mode='w') as bpf_skel_h:
|
||||
clang_compile(clang_exec=args.clang_exec,
|
||||
clang_flags=clang_flags,
|
||||
src_c=args.bpf_src_c,
|
||||
out_file=clang_out.name,
|
||||
target_arch=clang_arch_flag(args.arch),
|
||||
target_triplet=target_triplet())
|
||||
|
||||
llvm_strip(llvm_strip_exec=args.llvm_strip_exec, in_file=clang_out.name)
|
||||
|
||||
gen_bpf_skeleton(bpftool_exec=args.bpftool_exec,
|
||||
in_file=clang_out.name,
|
||||
out_fd=bpf_skel_h)
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument(
|
||||
'bpf_src_c',
|
||||
help='Path to *.c source of BPF program in systemd source tree \
|
||||
relative to the work directory')
|
||||
|
||||
parser.add_argument(
|
||||
'bpf_skel_h',
|
||||
help='Path to C header file')
|
||||
|
||||
parser.add_argument(
|
||||
'--clang_exec',
|
||||
help='Path to clang exec')
|
||||
|
||||
parser.add_argument(
|
||||
'--llvm_strip_exec',
|
||||
help='Path to llvm-strip exec')
|
||||
|
||||
parser.add_argument(
|
||||
'--bpftool_exec',
|
||||
help='Path to bpftool exec')
|
||||
|
||||
parser.add_argument(
|
||||
'--arch',
|
||||
help='Target CPU architecture',
|
||||
default='x86_64')
|
||||
|
||||
args = parser.parse_args();
|
||||
|
||||
logging.basicConfig(stream=sys.stderr, level=logging.WARNING)
|
||||
bpf_build(args)
|
||||
Loading…
x
Reference in New Issue
Block a user