1
0
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.

38 changed files with 5 additions and 1364 deletions

21
README
View File

@ -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:

View File

@ -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"/>

View File

@ -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>

View File

@ -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'],

View File

@ -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')

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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";

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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,

View File

@ -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

View File

@ -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);

View File

@ -31,6 +31,7 @@
#NUMAMask=
#RuntimeWatchdogSec=0
#RebootWatchdogSec=10min
#ShutdownWatchdogSec=10min
#KExecWatchdogSec=0
#WatchdogDevice=
#CapabilityBoundingSet=

View File

@ -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;

View File

@ -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))

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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,

View File

@ -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;
}

View File

@ -611,12 +611,3 @@ tests += [
[libudev],
[threads]],
]
tests += [
[['src/test/test-socket-bind.c'],
[libcore,
libshared],
[libbpf],
core_includes,
'BPF_FRAMEWORK'],
]

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");
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

@ -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;
}

View File

@ -162,8 +162,6 @@ SetCredential=
Slice=
SloppyOptions=
SmackProcessLabel=
SocketBindAllow=
SocketBindDeny=
StandardError=
StandardInput=
StandardInputData=

View File

@ -52,8 +52,6 @@ RuntimeMaxSec=
SendSIGHUP=
SendSIGKILL=
Slice=
SocketBindAllow=
SocketBindDeny=
StartupBlockIOWeight=
StartupCPUShares=
StartupCPUWeight=

View File

@ -88,8 +88,6 @@ RequiresMountsFor=
RequiresOverridable=
Requisite=
RequisiteOverridable=
SocketBindAllow=
SocketBindDeny=
SourcePath=
StartLimitAction=
StartLimitBurst=

View File

@ -45,8 +45,6 @@ MemoryMin=
MemorySwapMax=
NetClass=
Slice=
SocketBindAllow=
SocketBindDeny=
StartupBlockIOWeight=
StartupCPUShares=
StartupCPUWeight=

View File

@ -204,8 +204,6 @@ SmackLabel=
SmackLabelIPIn=
SmackLabelIPOut=
SmackProcessLabel=
SocketBindAllow=
SocketBindDeny=
SocketGroup=
SocketMode=
SocketProtocol=

View File

@ -158,8 +158,6 @@ SendSIGKILL=
SetCredential=
Slice=
SmackProcessLabel=
SocketBindAllow=
SocketBindDeny=
StandardError=
StandardInput=
StandardInputData=

View File

@ -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)