mirror of
https://github.com/systemd/systemd
synced 2026-03-13 08:34:47 +01:00
Compare commits
18 Commits
1ceaad6937
...
45b1fc3a88
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45b1fc3a88 | ||
|
|
862e01d70f | ||
|
|
7dc1707aab | ||
|
|
c308025875 | ||
|
|
6359811021 | ||
|
|
18ef723ef6 | ||
|
|
dcf4781caf | ||
|
|
28b76fc82a | ||
|
|
3d027d4d60 | ||
|
|
8dd210ab66 | ||
|
|
a8e5eb1788 | ||
|
|
91ce91c76c | ||
|
|
b18e9fc167 | ||
|
|
43b3f0fb00 | ||
|
|
09fc220c52 | ||
|
|
7d861e1263 | ||
|
|
cf4f9a57f2 | ||
|
|
58a33faf80 |
21
README
21
README
@ -35,6 +35,10 @@ LICENSE:
|
|||||||
REQUIREMENTS:
|
REQUIREMENTS:
|
||||||
Linux kernel >= 3.13
|
Linux kernel >= 3.13
|
||||||
Linux kernel >= 4.2 for unified cgroup hierarchy support
|
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
|
Linux kernel >= 5.4 for signed Verity images support
|
||||||
|
|
||||||
Kernel Config Options:
|
Kernel Config Options:
|
||||||
@ -95,8 +99,20 @@ REQUIREMENTS:
|
|||||||
Required for CPUQuota= in resource control unit settings
|
Required for CPUQuota= in resource control unit settings
|
||||||
CONFIG_CFS_BANDWIDTH
|
CONFIG_CFS_BANDWIDTH
|
||||||
|
|
||||||
Required for IPAddressDeny= and IPAddressAllow= in resource control
|
Required for IPAddressDeny=, IPAddressAllow=, IPIngressFilterPath=,
|
||||||
|
IPEgressFilterPath= in resource control unit settings
|
||||||
unit settings
|
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
|
CONFIG_CGROUP_BPF
|
||||||
|
|
||||||
For UEFI systems:
|
For UEFI systems:
|
||||||
@ -154,6 +170,7 @@ REQUIREMENTS:
|
|||||||
libcryptsetup (optional), >= 2.3.0 required for signed Verity images support
|
libcryptsetup (optional), >= 2.3.0 required for signed Verity images support
|
||||||
libaudit (optional)
|
libaudit (optional)
|
||||||
libacl (optional)
|
libacl (optional)
|
||||||
|
libbpf >= 0.2.0 (optional)
|
||||||
libfdisk >= 2.33 (from util-linux) (optional)
|
libfdisk >= 2.33 (from util-linux) (optional)
|
||||||
libselinux (optional)
|
libselinux (optional)
|
||||||
liblzma (optional)
|
liblzma (optional)
|
||||||
@ -178,6 +195,8 @@ REQUIREMENTS:
|
|||||||
meson >= 0.46 (>= 0.49 is required to build position-independent executables)
|
meson >= 0.46 (>= 0.49 is required to build position-independent executables)
|
||||||
ninja
|
ninja
|
||||||
gcc, awk, sed, grep, m4, and similar tools
|
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
|
During runtime, you need the following additional
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
@ -2482,6 +2482,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
|||||||
readonly s ManagedOOMPreference = '...';
|
readonly s ManagedOOMPreference = '...';
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly a(ss) BPFProgram = [...];
|
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")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||||
readonly as Environment = ['...', ...];
|
readonly as Environment = ['...', ...];
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||||
@ -3018,6 +3022,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
|||||||
|
|
||||||
<!--property BPFProgram is not documented!-->
|
<!--property BPFProgram is not documented!-->
|
||||||
|
|
||||||
|
<!--property SocketBindAllow is not documented!-->
|
||||||
|
|
||||||
|
<!--property SocketBindDeny is not documented!-->
|
||||||
|
|
||||||
<!--property EnvironmentFiles is not documented!-->
|
<!--property EnvironmentFiles is not documented!-->
|
||||||
|
|
||||||
<!--property PassEnvironment is not documented!-->
|
<!--property PassEnvironment is not documented!-->
|
||||||
@ -3578,6 +3586,10 @@ 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="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="Environment"/>
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
|
||||||
@ -4265,6 +4277,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
|||||||
readonly s ManagedOOMPreference = '...';
|
readonly s ManagedOOMPreference = '...';
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly a(ss) BPFProgram = [...];
|
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")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||||
readonly as Environment = ['...', ...];
|
readonly as Environment = ['...', ...];
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||||
@ -4827,6 +4843,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
|
|||||||
|
|
||||||
<!--property BPFProgram is not documented!-->
|
<!--property BPFProgram is not documented!-->
|
||||||
|
|
||||||
|
<!--property SocketBindAllow is not documented!-->
|
||||||
|
|
||||||
|
<!--property SocketBindDeny is not documented!-->
|
||||||
|
|
||||||
<!--property EnvironmentFiles is not documented!-->
|
<!--property EnvironmentFiles is not documented!-->
|
||||||
|
|
||||||
<!--property PassEnvironment is not documented!-->
|
<!--property PassEnvironment is not documented!-->
|
||||||
@ -5383,6 +5403,10 @@ 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="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="Environment"/>
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
|
||||||
@ -5972,6 +5996,10 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
|||||||
readonly s ManagedOOMPreference = '...';
|
readonly s ManagedOOMPreference = '...';
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly a(ss) BPFProgram = [...];
|
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")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||||
readonly as Environment = ['...', ...];
|
readonly as Environment = ['...', ...];
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||||
@ -6462,6 +6490,10 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
|||||||
|
|
||||||
<!--property BPFProgram is not documented!-->
|
<!--property BPFProgram is not documented!-->
|
||||||
|
|
||||||
|
<!--property SocketBindAllow is not documented!-->
|
||||||
|
|
||||||
|
<!--property SocketBindDeny is not documented!-->
|
||||||
|
|
||||||
<!--property EnvironmentFiles is not documented!-->
|
<!--property EnvironmentFiles is not documented!-->
|
||||||
|
|
||||||
<!--property PassEnvironment is not documented!-->
|
<!--property PassEnvironment is not documented!-->
|
||||||
@ -6936,6 +6968,10 @@ node /org/freedesktop/systemd1/unit/home_2emount {
|
|||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
|
<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="Environment"/>
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
|
||||||
@ -7646,6 +7682,10 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
|||||||
readonly s ManagedOOMPreference = '...';
|
readonly s ManagedOOMPreference = '...';
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly a(ss) BPFProgram = [...];
|
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")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||||
readonly as Environment = ['...', ...];
|
readonly as Environment = ['...', ...];
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||||
@ -8122,6 +8162,10 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
|
|||||||
|
|
||||||
<!--property BPFProgram is not documented!-->
|
<!--property BPFProgram is not documented!-->
|
||||||
|
|
||||||
|
<!--property SocketBindAllow is not documented!-->
|
||||||
|
|
||||||
|
<!--property SocketBindDeny is not documented!-->
|
||||||
|
|
||||||
<!--property EnvironmentFiles is not documented!-->
|
<!--property EnvironmentFiles is not documented!-->
|
||||||
|
|
||||||
<!--property PassEnvironment is not documented!-->
|
<!--property PassEnvironment is not documented!-->
|
||||||
@ -8582,6 +8626,10 @@ 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="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="Environment"/>
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
|
||||||
@ -9145,6 +9193,10 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
|
|||||||
readonly s ManagedOOMPreference = '...';
|
readonly s ManagedOOMPreference = '...';
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly a(ss) BPFProgram = [...];
|
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.Peer { ... };
|
||||||
interface org.freedesktop.DBus.Introspectable { ... };
|
interface org.freedesktop.DBus.Introspectable { ... };
|
||||||
@ -9285,6 +9337,10 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
|
|||||||
|
|
||||||
<!--property BPFProgram is not documented!-->
|
<!--property BPFProgram is not documented!-->
|
||||||
|
|
||||||
|
<!--property SocketBindAllow is not documented!-->
|
||||||
|
|
||||||
|
<!--property SocketBindDeny is not documented!-->
|
||||||
|
|
||||||
<!--Autogenerated cross-references for systemd.directives, do not edit-->
|
<!--Autogenerated cross-references for systemd.directives, do not edit-->
|
||||||
|
|
||||||
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
|
<variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
|
||||||
@ -9429,6 +9485,10 @@ node /org/freedesktop/systemd1/unit/system_2eslice {
|
|||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="BPFProgram"/>
|
<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-->
|
<!--End of Autogenerated section-->
|
||||||
|
|
||||||
<refsect2>
|
<refsect2>
|
||||||
@ -9592,6 +9652,10 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
|
|||||||
readonly s ManagedOOMPreference = '...';
|
readonly s ManagedOOMPreference = '...';
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||||
readonly a(ss) BPFProgram = [...];
|
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")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||||
readonly s KillMode = '...';
|
readonly s KillMode = '...';
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||||
@ -9748,6 +9812,10 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope {
|
|||||||
|
|
||||||
<!--property BPFProgram is not documented!-->
|
<!--property BPFProgram is not documented!-->
|
||||||
|
|
||||||
|
<!--property SocketBindAllow is not documented!-->
|
||||||
|
|
||||||
|
<!--property SocketBindDeny is not documented!-->
|
||||||
|
|
||||||
<!--property KillMode is not documented!-->
|
<!--property KillMode is not documented!-->
|
||||||
|
|
||||||
<!--property KillSignal is not documented!-->
|
<!--property KillSignal is not documented!-->
|
||||||
@ -9918,6 +9986,10 @@ 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="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="KillMode"/>
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="KillSignal"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="KillSignal"/>
|
||||||
|
|||||||
@ -762,6 +762,76 @@ BPFProgram=bind6:/sys/fs/bpf/sock-addr-hook
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
<varlistentry>
|
||||||
<term><varname>DeviceAllow=</varname></term>
|
<term><varname>DeviceAllow=</varname></term>
|
||||||
|
|
||||||
|
|||||||
23
meson.build
23
meson.build
@ -937,6 +937,25 @@ if not libcap.found()
|
|||||||
libcap = cc.find_library('cap')
|
libcap = cc.find_library('cap')
|
||||||
endif
|
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',
|
libmount = dependency('mount',
|
||||||
version : fuzzer_build ? '>= 0' : '>= 2.30')
|
version : fuzzer_build ? '>= 0' : '>= 2.30')
|
||||||
|
|
||||||
@ -1604,6 +1623,7 @@ conf.set10('ENABLE_EFI', have)
|
|||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
|
|
||||||
|
build_bpf_skel_py = find_program('tools/build-bpf-skel.py')
|
||||||
generate_gperfs = find_program('tools/generate-gperfs.py')
|
generate_gperfs = find_program('tools/generate-gperfs.py')
|
||||||
make_autosuspend_rules_py = find_program('tools/make-autosuspend-rules.py')
|
make_autosuspend_rules_py = find_program('tools/make-autosuspend-rules.py')
|
||||||
make_directive_index_py = find_program('tools/make-directive-index.py')
|
make_directive_index_py = find_program('tools/make-directive-index.py')
|
||||||
@ -1696,6 +1716,7 @@ install_libsystemd_static = static_library(
|
|||||||
libxz,
|
libxz,
|
||||||
libzstd,
|
libzstd,
|
||||||
liblz4,
|
liblz4,
|
||||||
|
libbpf,
|
||||||
libcap,
|
libcap,
|
||||||
libblkid,
|
libblkid,
|
||||||
libmount,
|
libmount,
|
||||||
@ -3766,6 +3787,7 @@ foreach tuple : [
|
|||||||
['elfutils'],
|
['elfutils'],
|
||||||
['gcrypt'],
|
['gcrypt'],
|
||||||
['gnutls'],
|
['gnutls'],
|
||||||
|
['libbpf'],
|
||||||
['libcryptsetup'],
|
['libcryptsetup'],
|
||||||
['libcurl'],
|
['libcurl'],
|
||||||
['libfdisk'],
|
['libfdisk'],
|
||||||
@ -3792,6 +3814,7 @@ foreach tuple : [
|
|||||||
# components
|
# components
|
||||||
['backlight'],
|
['backlight'],
|
||||||
['binfmt'],
|
['binfmt'],
|
||||||
|
['bpf-framework', conf.get('BPF_FRAMEWORK') == 1],
|
||||||
['coredump'],
|
['coredump'],
|
||||||
['environment.d'],
|
['environment.d'],
|
||||||
['efi'],
|
['efi'],
|
||||||
|
|||||||
@ -403,3 +403,6 @@ option('kernel-install', type: 'boolean', value: 'true',
|
|||||||
description : 'install kernel-install and associated files')
|
description : 'install kernel-install and associated files')
|
||||||
option('analyze', type: 'boolean', value: 'true',
|
option('analyze', type: 'boolean', value: 'true',
|
||||||
description : 'install systemd-analyze')
|
description : 'install systemd-analyze')
|
||||||
|
|
||||||
|
option('bpf-framework', type : 'combo', choices : ['auto', 'true', 'false'],
|
||||||
|
description: 'build BPF programs from source code in restricted C')
|
||||||
|
|||||||
@ -2164,6 +2164,7 @@ static const char *const cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
|
|||||||
[CGROUP_CONTROLLER_BPF_FIREWALL] = "bpf-firewall",
|
[CGROUP_CONTROLLER_BPF_FIREWALL] = "bpf-firewall",
|
||||||
[CGROUP_CONTROLLER_BPF_DEVICES] = "bpf-devices",
|
[CGROUP_CONTROLLER_BPF_DEVICES] = "bpf-devices",
|
||||||
[CGROUP_CONTROLLER_BPF_FOREIGN] = "bpf-foreign",
|
[CGROUP_CONTROLLER_BPF_FOREIGN] = "bpf-foreign",
|
||||||
|
[CGROUP_CONTROLLER_BPF_SOCKET_BIND] = "bpf-socket-bind",
|
||||||
};
|
};
|
||||||
|
|
||||||
DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);
|
DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);
|
||||||
|
|||||||
@ -31,6 +31,7 @@ typedef enum CGroupController {
|
|||||||
CGROUP_CONTROLLER_BPF_FIREWALL,
|
CGROUP_CONTROLLER_BPF_FIREWALL,
|
||||||
CGROUP_CONTROLLER_BPF_DEVICES,
|
CGROUP_CONTROLLER_BPF_DEVICES,
|
||||||
CGROUP_CONTROLLER_BPF_FOREIGN,
|
CGROUP_CONTROLLER_BPF_FOREIGN,
|
||||||
|
CGROUP_CONTROLLER_BPF_SOCKET_BIND,
|
||||||
|
|
||||||
_CGROUP_CONTROLLER_MAX,
|
_CGROUP_CONTROLLER_MAX,
|
||||||
_CGROUP_CONTROLLER_INVALID = -EINVAL,
|
_CGROUP_CONTROLLER_INVALID = -EINVAL,
|
||||||
@ -51,6 +52,7 @@ typedef enum CGroupMask {
|
|||||||
CGROUP_MASK_BPF_FIREWALL = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FIREWALL),
|
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_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_DEVICES),
|
||||||
CGROUP_MASK_BPF_FOREIGN = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FOREIGN),
|
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 */
|
/* 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,
|
CGROUP_MASK_V1 = CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT|CGROUP_MASK_BLKIO|CGROUP_MASK_MEMORY|CGROUP_MASK_DEVICES|CGROUP_MASK_PIDS,
|
||||||
@ -59,7 +61,7 @@ typedef enum CGroupMask {
|
|||||||
CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS,
|
CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS,
|
||||||
|
|
||||||
/* All cgroup v2 BPF pseudo-controllers */
|
/* All cgroup v2 BPF pseudo-controllers */
|
||||||
CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN,
|
CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN|CGROUP_MASK_BPF_SOCKET_BIND,
|
||||||
|
|
||||||
_CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
|
_CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1
|
||||||
} CGroupMask;
|
} CGroupMask;
|
||||||
|
|||||||
14
src/core/bpf/socket_bind/meson.build
Normal file
14
src/core/bpf/socket_bind/meson.build
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# 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
|
||||||
47
src/core/bpf/socket_bind/socket-bind-api.bpf.h
Normal file
47
src/core/bpf/socket_bind/socket-bind-api.bpf.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/* 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
|
||||||
103
src/core/bpf/socket_bind/socket-bind.bpf.c
Normal file
103
src/core/bpf/socket_bind/socket-bind.bpf.c
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/* 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,6 +25,7 @@
|
|||||||
#include "percent-util.h"
|
#include "percent-util.h"
|
||||||
#include "process-util.h"
|
#include "process-util.h"
|
||||||
#include "procfs-util.h"
|
#include "procfs-util.h"
|
||||||
|
#include "socket-bind.h"
|
||||||
#include "special.h"
|
#include "special.h"
|
||||||
#include "stat-util.h"
|
#include "stat-util.h"
|
||||||
#include "stdio-util.h"
|
#include "stdio-util.h"
|
||||||
@ -200,6 +201,18 @@ void cgroup_context_remove_bpf_foreign_program(CGroupContext *c, CGroupBPFForeig
|
|||||||
free(p);
|
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) {
|
void cgroup_context_done(CGroupContext *c) {
|
||||||
assert(c);
|
assert(c);
|
||||||
|
|
||||||
@ -221,6 +234,9 @@ void cgroup_context_done(CGroupContext *c) {
|
|||||||
while (c->device_allow)
|
while (c->device_allow)
|
||||||
cgroup_context_free_device_allow(c, 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_allow = ip_address_access_free_all(c->ip_address_allow);
|
||||||
c->ip_address_deny = ip_address_access_free_all(c->ip_address_deny);
|
c->ip_address_deny = ip_address_access_free_all(c->ip_address_deny);
|
||||||
|
|
||||||
@ -376,6 +392,7 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
|
|||||||
CGroupBPFForeignProgram *p;
|
CGroupBPFForeignProgram *p;
|
||||||
CGroupDeviceAllow *a;
|
CGroupDeviceAllow *a;
|
||||||
CGroupContext *c;
|
CGroupContext *c;
|
||||||
|
CGroupSocketBindItem *bi;
|
||||||
IPAddressAccessItem *iaai;
|
IPAddressAccessItem *iaai;
|
||||||
char **path;
|
char **path;
|
||||||
char q[FORMAT_TIMESPAN_MAX];
|
char q[FORMAT_TIMESPAN_MAX];
|
||||||
@ -562,6 +579,34 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) {
|
|||||||
LIST_FOREACH(programs, p, c->bpf_foreign_programs)
|
LIST_FOREACH(programs, p, c->bpf_foreign_programs)
|
||||||
fprintf(f, "%sBPFProgram: %s:%s",
|
fprintf(f, "%sBPFProgram: %s:%s",
|
||||||
prefix, bpf_cgroup_attach_type_to_string(p->attach_type), p->bpffs_path);
|
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) {
|
int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode) {
|
||||||
@ -1055,6 +1100,12 @@ static void cgroup_apply_firewall(Unit *u) {
|
|||||||
(void) bpf_firewall_install(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) {
|
static int cgroup_apply_devices(Unit *u) {
|
||||||
_cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
|
_cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
|
||||||
const char *path;
|
const char *path;
|
||||||
@ -1483,6 +1534,9 @@ static void cgroup_context_apply(
|
|||||||
|
|
||||||
if (apply_mask & CGROUP_MASK_BPF_FOREIGN)
|
if (apply_mask & CGROUP_MASK_BPF_FOREIGN)
|
||||||
cgroup_apply_bpf_foreign_program(u);
|
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) {
|
static bool unit_get_needs_bpf_firewall(Unit *u) {
|
||||||
@ -1526,6 +1580,17 @@ static bool unit_get_needs_bpf_foreign_program(Unit *u) {
|
|||||||
return !LIST_IS_EMPTY(c->bpf_foreign_programs);
|
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) {
|
static CGroupMask unit_get_cgroup_mask(Unit *u) {
|
||||||
CGroupMask mask = 0;
|
CGroupMask mask = 0;
|
||||||
CGroupContext *c;
|
CGroupContext *c;
|
||||||
@ -1580,6 +1645,9 @@ static CGroupMask unit_get_bpf_mask(Unit *u) {
|
|||||||
if (unit_get_needs_bpf_foreign_program(u))
|
if (unit_get_needs_bpf_foreign_program(u))
|
||||||
mask |= CGROUP_MASK_BPF_FOREIGN;
|
mask |= CGROUP_MASK_BPF_FOREIGN;
|
||||||
|
|
||||||
|
if (unit_get_needs_socket_bind(u))
|
||||||
|
mask |= CGROUP_MASK_BPF_SOCKET_BIND;
|
||||||
|
|
||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3063,6 +3131,11 @@ static int cg_bpf_mask_supported(CGroupMask *ret) {
|
|||||||
if (r > 0)
|
if (r > 0)
|
||||||
mask |= CGROUP_MASK_BPF_FOREIGN;
|
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;
|
*ret = mask;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,6 +32,7 @@ typedef struct CGroupIODeviceLatency CGroupIODeviceLatency;
|
|||||||
typedef struct CGroupBlockIODeviceWeight CGroupBlockIODeviceWeight;
|
typedef struct CGroupBlockIODeviceWeight CGroupBlockIODeviceWeight;
|
||||||
typedef struct CGroupBlockIODeviceBandwidth CGroupBlockIODeviceBandwidth;
|
typedef struct CGroupBlockIODeviceBandwidth CGroupBlockIODeviceBandwidth;
|
||||||
typedef struct CGroupBPFForeignProgram CGroupBPFForeignProgram;
|
typedef struct CGroupBPFForeignProgram CGroupBPFForeignProgram;
|
||||||
|
typedef struct CGroupSocketBindItem CGroupSocketBindItem;
|
||||||
|
|
||||||
typedef enum CGroupDevicePolicy {
|
typedef enum CGroupDevicePolicy {
|
||||||
/* When devices listed, will allow those, plus built-in ones, if none are listed will allow
|
/* When devices listed, will allow those, plus built-in ones, if none are listed will allow
|
||||||
@ -101,6 +102,13 @@ struct CGroupBPFForeignProgram {
|
|||||||
char *bpffs_path;
|
char *bpffs_path;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CGroupSocketBindItem {
|
||||||
|
LIST_FIELDS(CGroupSocketBindItem, socket_bind_items);
|
||||||
|
int address_family;
|
||||||
|
uint16_t nr_ports;
|
||||||
|
uint16_t port_min;
|
||||||
|
};
|
||||||
|
|
||||||
struct CGroupContext {
|
struct CGroupContext {
|
||||||
bool cpu_accounting;
|
bool cpu_accounting;
|
||||||
bool io_accounting;
|
bool io_accounting;
|
||||||
@ -165,6 +173,9 @@ struct CGroupContext {
|
|||||||
CGroupDevicePolicy device_policy;
|
CGroupDevicePolicy device_policy;
|
||||||
LIST_HEAD(CGroupDeviceAllow, device_allow);
|
LIST_HEAD(CGroupDeviceAllow, device_allow);
|
||||||
|
|
||||||
|
LIST_HEAD(CGroupSocketBindItem, socket_bind_allow);
|
||||||
|
LIST_HEAD(CGroupSocketBindItem, socket_bind_deny);
|
||||||
|
|
||||||
/* Common */
|
/* Common */
|
||||||
TasksMax tasks_max;
|
TasksMax tasks_max;
|
||||||
|
|
||||||
@ -203,6 +214,7 @@ usec_t cgroup_cpu_adjust_period(usec_t period, usec_t quota, usec_t resolution,
|
|||||||
void cgroup_context_init(CGroupContext *c);
|
void cgroup_context_init(CGroupContext *c);
|
||||||
void cgroup_context_done(CGroupContext *c);
|
void cgroup_context_done(CGroupContext *c);
|
||||||
void cgroup_context_dump(Unit *u, FILE* f, const char *prefix);
|
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_device_allow(CGroupContext *c, CGroupDeviceAllow *a);
|
||||||
void cgroup_context_free_io_device_weight(CGroupContext *c, CGroupIODeviceWeight *w);
|
void cgroup_context_free_io_device_weight(CGroupContext *c, CGroupIODeviceWeight *w);
|
||||||
@ -211,6 +223,7 @@ 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_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w);
|
||||||
void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b);
|
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_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_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);
|
int cgroup_add_bpf_foreign_program(CGroupContext *c, uint32_t attach_type, const char *path);
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "fileio.h"
|
#include "fileio.h"
|
||||||
#include "limits-util.h"
|
#include "limits-util.h"
|
||||||
|
#include "parse-util.h"
|
||||||
#include "path-util.h"
|
#include "path-util.h"
|
||||||
#include "percent-util.h"
|
#include "percent-util.h"
|
||||||
|
|
||||||
@ -375,6 +376,32 @@ static int property_get_bpf_foreign_program(
|
|||||||
return sd_bus_message_close_container(reply);
|
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[] = {
|
const sd_bus_vtable bus_cgroup_vtable[] = {
|
||||||
SD_BUS_VTABLE_START(0),
|
SD_BUS_VTABLE_START(0),
|
||||||
SD_BUS_PROPERTY("Delegate", "b", bus_property_get_bool, offsetof(CGroupContext, delegate), 0),
|
SD_BUS_PROPERTY("Delegate", "b", bus_property_get_bool, offsetof(CGroupContext, delegate), 0),
|
||||||
@ -427,6 +454,8 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
|
|||||||
SD_BUS_PROPERTY("ManagedOOMMemoryPressureLimit", "u", NULL, offsetof(CGroupContext, moom_mem_pressure_limit), 0),
|
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("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("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
|
SD_BUS_VTABLE_END
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1849,6 +1878,89 @@ int bus_cgroup_set_property(
|
|||||||
|
|
||||||
return 1;
|
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))
|
if (streq(name, "DisableControllers") || (u->transient && u->load_state == UNIT_STUB))
|
||||||
return bus_cgroup_set_transient_property(u, c, name, message, flags, error);
|
return bus_cgroup_set_transient_property(u, c, name, message, flags, error);
|
||||||
|
|||||||
@ -235,7 +235,9 @@ $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.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.ManagedOOMPreference, config_parse_managed_oom_preference, 0, offsetof($1, cgroup_context.moom_preference)
|
||||||
$1.NetClass, config_parse_warn_compat, DISABLED_LEGACY, 0
|
$1.NetClass, config_parse_warn_compat, DISABLED_LEGACY, 0
|
||||||
$1.BPFProgram, config_parse_bpf_foreign_program, 0, offsetof($1, cgroup_context)'
|
$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)'
|
||||||
)m4_dnl
|
)m4_dnl
|
||||||
Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description)
|
Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description)
|
||||||
Unit.Documentation, config_parse_documentation, 0, offsetof(Unit, documentation)
|
Unit.Documentation, config_parse_documentation, 0, offsetof(Unit, documentation)
|
||||||
|
|||||||
@ -55,6 +55,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#include "securebits-util.h"
|
#include "securebits-util.h"
|
||||||
#include "signal-util.h"
|
#include "signal-util.h"
|
||||||
|
#include "socket-bind.h"
|
||||||
#include "socket-netlink.h"
|
#include "socket-netlink.h"
|
||||||
#include "stat-util.h"
|
#include "stat-util.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
@ -5657,6 +5658,73 @@ int config_parse_bpf_foreign_program(
|
|||||||
return 0;
|
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) {
|
static int merge_by_names(Unit **u, Set *names, const char *id) {
|
||||||
char *k;
|
char *k;
|
||||||
int r;
|
int r;
|
||||||
|
|||||||
@ -141,6 +141,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_mount_images);
|
|||||||
CONFIG_PARSER_PROTOTYPE(config_parse_socket_timestamping);
|
CONFIG_PARSER_PROTOTYPE(config_parse_socket_timestamping);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_extension_images);
|
CONFIG_PARSER_PROTOTYPE(config_parse_extension_images);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_bpf_foreign_program);
|
CONFIG_PARSER_PROTOTYPE(config_parse_bpf_foreign_program);
|
||||||
|
CONFIG_PARSER_PROTOTYPE(config_parse_cgroup_socket_bind);
|
||||||
|
|
||||||
/* gperf prototypes */
|
/* gperf prototypes */
|
||||||
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
|
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
|
||||||
|
|||||||
@ -105,6 +105,8 @@ libcore_sources = '''
|
|||||||
slice.h
|
slice.h
|
||||||
smack-setup.c
|
smack-setup.c
|
||||||
smack-setup.h
|
smack-setup.h
|
||||||
|
socket-bind.c
|
||||||
|
socket-bind.h
|
||||||
socket.c
|
socket.c
|
||||||
socket.h
|
socket.h
|
||||||
swap.c
|
swap.c
|
||||||
@ -123,6 +125,11 @@ libcore_sources = '''
|
|||||||
unit.h
|
unit.h
|
||||||
'''.split()
|
'''.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 = custom_target(
|
||||||
'load-fragment-gperf.gperf',
|
'load-fragment-gperf.gperf',
|
||||||
input : 'load-fragment-gperf.gperf.m4',
|
input : 'load-fragment-gperf.gperf.m4',
|
||||||
@ -152,6 +159,7 @@ libcore = static_library(
|
|||||||
include_directories : includes,
|
include_directories : includes,
|
||||||
dependencies : [versiondep,
|
dependencies : [versiondep,
|
||||||
threads,
|
threads,
|
||||||
|
libbpf,
|
||||||
librt,
|
librt,
|
||||||
libseccomp,
|
libseccomp,
|
||||||
libpam,
|
libpam,
|
||||||
|
|||||||
238
src/core/socket-bind.c
Normal file
238
src/core/socket-bind.c
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
/* 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
|
||||||
15
src/core/socket-bind.h
Normal file
15
src/core/socket-bind.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/* 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,7 +31,6 @@
|
|||||||
#NUMAMask=
|
#NUMAMask=
|
||||||
#RuntimeWatchdogSec=0
|
#RuntimeWatchdogSec=0
|
||||||
#RebootWatchdogSec=10min
|
#RebootWatchdogSec=10min
|
||||||
#ShutdownWatchdogSec=10min
|
|
||||||
#KExecWatchdogSec=0
|
#KExecWatchdogSec=0
|
||||||
#WatchdogDevice=
|
#WatchdogDevice=
|
||||||
#CapabilityBoundingSet=
|
#CapabilityBoundingSet=
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
#include "format-util.h"
|
#include "format-util.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
#include "serialize.h"
|
#include "serialize.h"
|
||||||
|
#include "socket-bind.h"
|
||||||
#include "string-table.h"
|
#include "string-table.h"
|
||||||
#include "unit-serialize.h"
|
#include "unit-serialize.h"
|
||||||
#include "user-util.h"
|
#include "user-util.h"
|
||||||
@ -151,6 +152,8 @@ 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-enabled-mask", u->cgroup_enabled_mask);
|
||||||
(void) serialize_cgroup_mask(f, "cgroup-invalidated-mask", u->cgroup_invalidated_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))
|
if (uid_is_valid(u->ref_uid))
|
||||||
(void) serialize_item_format(f, "ref-uid", UID_FMT, u->ref_uid);
|
(void) serialize_item_format(f, "ref-uid", UID_FMT, u->ref_uid);
|
||||||
if (gid_is_valid(u->ref_gid))
|
if (gid_is_valid(u->ref_gid))
|
||||||
@ -362,6 +365,23 @@ 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))
|
else if (MATCH_DESERIALIZE_IMMEDIATE("cgroup-invalidated-mask", l, v, cg_mask_from_string, u->cgroup_invalidated_mask))
|
||||||
continue;
|
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")) {
|
else if (streq(l, "ref-uid")) {
|
||||||
uid_t uid;
|
uid_t uid;
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,7 @@
|
|||||||
#include "rm-rf.h"
|
#include "rm-rf.h"
|
||||||
#include "set.h"
|
#include "set.h"
|
||||||
#include "signal-util.h"
|
#include "signal-util.h"
|
||||||
|
#include "socket-bind.h"
|
||||||
#include "sparse-endian.h"
|
#include "sparse-endian.h"
|
||||||
#include "special.h"
|
#include "special.h"
|
||||||
#include "specifier.h"
|
#include "specifier.h"
|
||||||
@ -56,6 +57,9 @@
|
|||||||
#include "unit.h"
|
#include "unit.h"
|
||||||
#include "user-util.h"
|
#include "user-util.h"
|
||||||
#include "virt.h"
|
#include "virt.h"
|
||||||
|
#if BPF_FRAMEWORK
|
||||||
|
#include "bpf-link.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Thresholds for logging at INFO level about resource consumption */
|
/* Thresholds for logging at INFO level about resource consumption */
|
||||||
#define MENTIONWORTHY_CPU_NSEC (1 * NSEC_PER_SEC)
|
#define MENTIONWORTHY_CPU_NSEC (1 * NSEC_PER_SEC)
|
||||||
@ -663,6 +667,13 @@ Unit* unit_free(Unit *u) {
|
|||||||
if (u->on_console)
|
if (u->on_console)
|
||||||
manager_unref_console(u->manager);
|
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);
|
unit_release_cgroup(u);
|
||||||
|
|
||||||
if (!MANAGER_IS_RELOADING(u->manager))
|
if (!MANAGER_IS_RELOADING(u->manager))
|
||||||
|
|||||||
@ -309,6 +309,15 @@ typedef struct Unit {
|
|||||||
* attached to unit cgroup by provided program fd and attach type. */
|
* attached to unit cgroup by provided program fd and attach type. */
|
||||||
Hashmap *bpf_foreign_by_key;
|
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];
|
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
|
/* Low-priority event source which is used to remove watched PIDs that have gone away, and subscribe to any new
|
||||||
|
|||||||
38
src/shared/bpf-link.c
Normal file
38
src/shared/bpf-link.c
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/* 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;
|
||||||
|
}
|
||||||
16
src/shared/bpf-link.h
Normal file
16
src/shared/bpf-link.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/* 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,6 +862,57 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
|
|||||||
return 1;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -319,6 +319,13 @@ if conf.get('HAVE_LIBIPTC') == 1
|
|||||||
shared_sources += files('firewall-util-iptables.c')
|
shared_sources += files('firewall-util-iptables.c')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if conf.get('HAVE_LIBBPF') == 1
|
||||||
|
shared_sources += files('''
|
||||||
|
bpf-link.c
|
||||||
|
bpf-link.h
|
||||||
|
'''.split())
|
||||||
|
endif
|
||||||
|
|
||||||
if conf.get('HAVE_KMOD') == 1
|
if conf.get('HAVE_KMOD') == 1
|
||||||
shared_sources += files('module-util.c')
|
shared_sources += files('module-util.c')
|
||||||
endif
|
endif
|
||||||
@ -378,6 +385,7 @@ libshared_name = 'systemd-shared-@0@'.format(meson.project_version())
|
|||||||
libshared_deps = [threads,
|
libshared_deps = [threads,
|
||||||
libacl,
|
libacl,
|
||||||
libblkid,
|
libblkid,
|
||||||
|
libbpf,
|
||||||
libcap,
|
libcap,
|
||||||
libcrypt,
|
libcrypt,
|
||||||
libgcrypt,
|
libgcrypt,
|
||||||
|
|||||||
@ -1712,6 +1712,34 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return bus_log_parse_error(r);
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -611,3 +611,12 @@ tests += [
|
|||||||
[libudev],
|
[libudev],
|
||||||
[threads]],
|
[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) {
|
static void test_cg_mask_to_string(void) {
|
||||||
test_cg_mask_to_string_one(0, NULL);
|
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");
|
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_CPU, "cpu");
|
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_CPUACCT, "cpuacct");
|
||||||
test_cg_mask_to_string_one(CGROUP_MASK_CPUSET, "cpuset");
|
test_cg_mask_to_string_one(CGROUP_MASK_CPUSET, "cpuset");
|
||||||
|
|||||||
151
src/test/test-socket-bind.c
Normal file
151
src/test/test-socket-bind.c
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
/* 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,6 +162,8 @@ SetCredential=
|
|||||||
Slice=
|
Slice=
|
||||||
SloppyOptions=
|
SloppyOptions=
|
||||||
SmackProcessLabel=
|
SmackProcessLabel=
|
||||||
|
SocketBindAllow=
|
||||||
|
SocketBindDeny=
|
||||||
StandardError=
|
StandardError=
|
||||||
StandardInput=
|
StandardInput=
|
||||||
StandardInputData=
|
StandardInputData=
|
||||||
|
|||||||
@ -52,6 +52,8 @@ RuntimeMaxSec=
|
|||||||
SendSIGHUP=
|
SendSIGHUP=
|
||||||
SendSIGKILL=
|
SendSIGKILL=
|
||||||
Slice=
|
Slice=
|
||||||
|
SocketBindAllow=
|
||||||
|
SocketBindDeny=
|
||||||
StartupBlockIOWeight=
|
StartupBlockIOWeight=
|
||||||
StartupCPUShares=
|
StartupCPUShares=
|
||||||
StartupCPUWeight=
|
StartupCPUWeight=
|
||||||
|
|||||||
@ -88,6 +88,8 @@ RequiresMountsFor=
|
|||||||
RequiresOverridable=
|
RequiresOverridable=
|
||||||
Requisite=
|
Requisite=
|
||||||
RequisiteOverridable=
|
RequisiteOverridable=
|
||||||
|
SocketBindAllow=
|
||||||
|
SocketBindDeny=
|
||||||
SourcePath=
|
SourcePath=
|
||||||
StartLimitAction=
|
StartLimitAction=
|
||||||
StartLimitBurst=
|
StartLimitBurst=
|
||||||
|
|||||||
@ -45,6 +45,8 @@ MemoryMin=
|
|||||||
MemorySwapMax=
|
MemorySwapMax=
|
||||||
NetClass=
|
NetClass=
|
||||||
Slice=
|
Slice=
|
||||||
|
SocketBindAllow=
|
||||||
|
SocketBindDeny=
|
||||||
StartupBlockIOWeight=
|
StartupBlockIOWeight=
|
||||||
StartupCPUShares=
|
StartupCPUShares=
|
||||||
StartupCPUWeight=
|
StartupCPUWeight=
|
||||||
|
|||||||
@ -204,6 +204,8 @@ SmackLabel=
|
|||||||
SmackLabelIPIn=
|
SmackLabelIPIn=
|
||||||
SmackLabelIPOut=
|
SmackLabelIPOut=
|
||||||
SmackProcessLabel=
|
SmackProcessLabel=
|
||||||
|
SocketBindAllow=
|
||||||
|
SocketBindDeny=
|
||||||
SocketGroup=
|
SocketGroup=
|
||||||
SocketMode=
|
SocketMode=
|
||||||
SocketProtocol=
|
SocketProtocol=
|
||||||
|
|||||||
@ -158,6 +158,8 @@ SendSIGKILL=
|
|||||||
SetCredential=
|
SetCredential=
|
||||||
Slice=
|
Slice=
|
||||||
SmackProcessLabel=
|
SmackProcessLabel=
|
||||||
|
SocketBindAllow=
|
||||||
|
SocketBindDeny=
|
||||||
StandardError=
|
StandardError=
|
||||||
StandardInput=
|
StandardInput=
|
||||||
StandardInputData=
|
StandardInputData=
|
||||||
|
|||||||
123
tools/build-bpf-skel.py
Executable file
123
tools/build-bpf-skel.py
Executable file
@ -0,0 +1,123 @@
|
|||||||
|
#!/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