mirror of
https://github.com/systemd/systemd
synced 2026-03-06 13:15:06 +01:00
Compare commits
41 Commits
a2be8be2cf
...
e4dde4e87d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4dde4e87d | ||
|
|
a8af734e75 | ||
|
|
4dd465cb4e | ||
|
|
84a1ff9457 | ||
|
|
ac5644635d | ||
|
|
221019166f | ||
|
|
199f75205b | ||
|
|
91174d2803 | ||
|
|
24ee0f9d16 | ||
|
|
ecfbbf098c | ||
|
|
b43dfb6e6e | ||
|
|
5777c6130b | ||
|
|
8bc63a0b13 | ||
|
|
60e4fb4240 | ||
|
|
d39079fcaa | ||
|
|
468695c8cd | ||
|
|
536970d4f9 | ||
|
|
efda832d4f | ||
|
|
39ede7cc37 | ||
|
|
b6fad30665 | ||
|
|
e2054217d5 | ||
|
|
7d9ec60990 | ||
|
|
ce6b138c75 | ||
|
|
de31bbc6b1 | ||
|
|
bc5a9b82d5 | ||
|
|
715a70e721 | ||
|
|
4df42cd99d | ||
|
|
99c41c0de4 | ||
|
|
347ea16797 | ||
|
|
6f00fd9b7c | ||
|
|
bcd1a2bb5c | ||
|
|
761cf19d7b | ||
|
|
f51343d0af | ||
|
|
3122097217 | ||
|
|
47ed20e1e0 | ||
|
|
805d67c565 | ||
|
|
f2bd032044 | ||
|
|
adce225a10 | ||
|
|
dcdd9030ba | ||
|
|
25e4608b8b | ||
|
|
c4fea19abb |
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -1 +1,2 @@
|
||||
*.[ch] whitespace=tab-in-indent,trailing-space
|
||||
test/dmidecode-dumps/*.bin binary
|
||||
|
||||
2
TODO
2
TODO
@ -297,7 +297,7 @@ Features:
|
||||
initrd had set.
|
||||
|
||||
* sd-event: add native support for P_ALL waitid() watching, then move PID 1 to
|
||||
it fo reaping assigned but unknown children. This needs to some special care
|
||||
it for reaping assigned but unknown children. This needs to some special care
|
||||
to operate somewhat sensibly in light of priorities: P_ALL will return
|
||||
arbitrary processes, regardless of the priority we want to watch them with,
|
||||
hence on each event loop iteration check all processes which we shall watch
|
||||
|
||||
@ -515,7 +515,7 @@
|
||||
<varlistentry>
|
||||
<term><option>--tasks-max=</option><replaceable>TASKS</replaceable></term>
|
||||
|
||||
<listitem><para>Takes a non-zero unsigned integer as argument. Configures the maximum numer of tasks
|
||||
<listitem><para>Takes a non-zero unsigned integer as argument. Configures the maximum number of tasks
|
||||
(i.e. threads, where each process is at least one thread) the user may have at any given time. This
|
||||
limit applies to all tasks forked off the user's sessions, even if they change user identity via
|
||||
<citerefentry project='man-pages'><refentrytitle>su</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
<?xml version='1.0'?> <!--*-nxml-*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
|
||||
<!ENTITY % entities SYSTEM "custom-entities.ent" >
|
||||
%entities;
|
||||
]>
|
||||
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
|
||||
|
||||
<refentry id="hostname">
|
||||
@ -26,23 +29,65 @@
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para>The <filename>/etc/hostname</filename> file configures the
|
||||
name of the local system that is set during boot using the
|
||||
<citerefentry><refentrytitle>sethostname</refentrytitle><manvolnum>2</manvolnum></citerefentry>
|
||||
system call. It should contain a single newline-terminated
|
||||
hostname string. Comments (lines starting with a `#') are ignored.
|
||||
The hostname may be a free-form string up to 64 characters in length;
|
||||
however, it is recommended that it consists only of 7-bit ASCII lower-case
|
||||
characters and no spaces or dots, and limits itself to the format allowed
|
||||
for DNS domain name labels, even though this is not a strict
|
||||
requirement.</para>
|
||||
<para>The <filename>/etc/hostname</filename> file configures the name of the local system. Unless
|
||||
overridden as described in the next section,
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> will set this
|
||||
hostname during boot using the
|
||||
<citerefentry><refentrytitle>sethostname</refentrytitle><manvolnum>2</manvolnum></citerefentry> system
|
||||
call.</para>
|
||||
|
||||
<para>The file should contain a single newline-terminated hostname string. Comments (lines starting with
|
||||
a <literal>#</literal>) are ignored. The hostname should be composed of up to 64 7-bit ASCII lower-case
|
||||
alphanumeric characters or hyphens forming a valid DNS domain name. It is recommended that this name
|
||||
contains only a single label, i.e. without any dots. Invalid characters will be filtered out in an
|
||||
attempt to make the name valid, but obviously it is recommended to use a valid name and not rely on this
|
||||
filtering.</para>
|
||||
|
||||
<para>You may use
|
||||
<citerefentry><refentrytitle>hostnamectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
to change the value of this file during runtime from the command
|
||||
line. Use
|
||||
<citerefentry><refentrytitle>systemd-firstboot</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
to initialize it on mounted (but not booted) system images.</para>
|
||||
<citerefentry><refentrytitle>hostnamectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> to change
|
||||
the value of this file during runtime from the command line. Use
|
||||
<citerefentry><refentrytitle>systemd-firstboot</refentrytitle><manvolnum>1</manvolnum></citerefentry> to
|
||||
initialize it on mounted (but not booted) system images.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Hostname semantics</title>
|
||||
|
||||
<para><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> and the
|
||||
associated tools will obtain the hostname in the following ways:</para>
|
||||
<itemizedlist>
|
||||
<listitem><para>If the kernel commandline parameter <varname>systemd.hostname=</varname> specifies a
|
||||
valid hostname,
|
||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> will use it
|
||||
to set the hostname during early boot, see
|
||||
<citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>Otherwise, the "static" hostname specified by <filename>/etc/hostname</filename> as
|
||||
described above will be used.</para></listitem>
|
||||
|
||||
<listitem><para>Otherwise, a transient hostname may be set during runtime, for example based on
|
||||
information in a DHCP lease, see
|
||||
<citerefentry><refentrytitle>systemd-hostnamed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
|
||||
Both <ulink url="https://developer.gnome.org/NetworkManager/stable/">NetworkManager</ulink> and
|
||||
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
allow this. Note that
|
||||
<citerefentry><refentrytitle>systemd-hostnamed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
gives higher priority to the static hostname, so the transient hostname will only be used if the static
|
||||
hostname is not configured.</para></listitem>
|
||||
|
||||
<listitem><para>Otherwise, a fallback hostname configured at compilation time will be used
|
||||
(<literal>&FALLBACK_HOSTNAME;</literal>).</para></listitem>
|
||||
|
||||
<!-- what about the "linux" fallback fallback? -->
|
||||
</itemizedlist>
|
||||
|
||||
<para>Effectively, the static hostname has higher priority than a transient hostname, which has higher
|
||||
priority than the fallback hostname. Transient hostnames are equivalent, so setting a new transient
|
||||
hostname causes the previous transient hostname to be forgotten. The hostname specified on the kernel
|
||||
command line is like a transient hostname, with the exception that it has higher priority when the
|
||||
machine boots. Also note that those are the semantics implemented by systemd tools, but other programs
|
||||
may also set the hostname.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
||||
@ -32,33 +32,23 @@
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><command>hostnamectl</command> may be used to query and
|
||||
change the system hostname and related settings.</para>
|
||||
<para><command>hostnamectl</command> may be used to query and change the system hostname and related
|
||||
settings.</para>
|
||||
|
||||
<para>This tool distinguishes three different hostnames: the
|
||||
high-level "pretty" hostname which might include all kinds of
|
||||
special characters (e.g. "Lennart's Laptop"), the static hostname
|
||||
which is used to initialize the kernel hostname at boot (e.g.
|
||||
"lennarts-laptop"), and the transient hostname which is a fallback
|
||||
value received from network configuration. If a static hostname is
|
||||
set, and is valid (something other than localhost), then the
|
||||
transient hostname is not used.</para>
|
||||
<para><citerefentry><refentrytitle>systemd-hostnamed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
and this tool distinguish three different hostnames: the high-level "pretty" hostname which might include
|
||||
all kinds of special characters (e.g. "Lennart's Laptop"), the "static" hostname which is the
|
||||
user-configured hostname (e.g. "lennarts-laptop"), and the transient hostname which is a fallback value
|
||||
received from network configuration (e.g. "node12345678"). If a static hostname is set to a valid value,
|
||||
then the transient hostname is not used.</para>
|
||||
|
||||
<para>Note that the pretty hostname has little restrictions on the characters and length used, while the static and
|
||||
transient hostnames are limited to the usually accepted characters of Internet domain names, and 64 characters at
|
||||
maximum (the latter being a Linux limitation).</para>
|
||||
|
||||
<para>The static hostname is stored in
|
||||
<filename>/etc/hostname</filename>, see
|
||||
<citerefentry><refentrytitle>hostname</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
for more information. The pretty hostname, chassis type, and icon
|
||||
name are stored in <filename>/etc/machine-info</filename>, see
|
||||
<citerefentry><refentrytitle>machine-info</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
|
||||
|
||||
<para>Use
|
||||
<citerefentry><refentrytitle>systemd-firstboot</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
to initialize the system hostname for mounted (but not booted)
|
||||
system images.</para>
|
||||
<citerefentry><refentrytitle>systemd-firstboot</refentrytitle><manvolnum>1</manvolnum></citerefentry> to
|
||||
initialize the system hostname for mounted (but not booted) system images.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
||||
@ -94,10 +94,11 @@
|
||||
<varlistentry id='colors'>
|
||||
<term><varname>$SYSTEMD_COLORS</varname></term>
|
||||
|
||||
<listitem><para>The value must be a boolean. Controls whether colorized output should be
|
||||
generated. This can be specified to override the decision that <command>systemd</command> makes based
|
||||
on <varname>$TERM</varname> and what the console is connected to.</para>
|
||||
</listitem>
|
||||
<listitem><para>Takes a boolean argument. When true, <command>systemd</command> and related utilities
|
||||
will use colors in their output, otherwise the output will be monochrome. Additionally, the variable can
|
||||
take one of the following special values: <literal>16</literal>, <literal>256</literal> to restrict the use
|
||||
of colors to the base 16 or 256 ANSI colors, respectively. This can be specified to override the automatic
|
||||
decision based on <varname>$TERM</varname> and what the console is connected to.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<!-- This is not documented on purpose, because it is not clear if $NO_COLOR will become supported
|
||||
|
||||
@ -62,6 +62,9 @@ node /org/freedesktop/hostname1 {
|
||||
readonly s Hostname = '...';
|
||||
readonly s StaticHostname = '...';
|
||||
readonly s PrettyHostname = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly s FallbackHostname = '...';
|
||||
readonly s HostnameSource = '...';
|
||||
readonly s IconName = '...';
|
||||
readonly s Chassis = '...';
|
||||
readonly s Deployment = '...';
|
||||
@ -113,6 +116,10 @@ node /org/freedesktop/hostname1 {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="PrettyHostname"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="FallbackHostname"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="HostnameSource"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="IconName"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="Chassis"/>
|
||||
@ -144,55 +151,61 @@ node /org/freedesktop/hostname1 {
|
||||
<refsect1>
|
||||
<title>Semantics</title>
|
||||
|
||||
<para>The <emphasis>static (configured) hostname</emphasis> is the one configured in
|
||||
<filename>/etc/hostname</filename>. It is chosen by the local user. It is not always in sync with the
|
||||
current hostname as returned by the
|
||||
<para>The <varname>StaticHostname</varname> property exposes the "static" hostname configured in
|
||||
<filename>/etc/hostname</filename>. It is not always in sync with the current hostname as returned by the
|
||||
<citerefentry project="man-pages"><refentrytitle>gethostname</refentrytitle><manvolnum>3</manvolnum></citerefentry>
|
||||
system call. If no hostname is configured this property will be the empty string. Setting this property
|
||||
to the empty string will remove <filename>/etc/hostname</filename>. This property should be an
|
||||
internet-style hostname, 7-bit lowercase ASCII, no special chars/spaces.</para>
|
||||
system call. If no static hostname is configured this property will be the empty string.</para>
|
||||
|
||||
<para>The <emphasis>transient (dynamic) hostname</emphasis> is the one configured via the kernel's
|
||||
<para>When <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> or
|
||||
<citerefentry><refentrytitle>systemd-hostnamed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
set the hostname, this static hostname <emphasis>has the highest priority</emphasis>.</para>
|
||||
|
||||
<para>The <varname>Hostname</varname> property exposes the actual hostname configured in the kernel via
|
||||
<citerefentry project="man-pages"><refentrytitle>sethostname</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
|
||||
It can be different from the static hostname if DHCP or mDNS have been configured to change the name
|
||||
based on network information. <!-- FIXME: it's not DHCP that configures this... -->
|
||||
This property is never empty. If no hostname is set this will default to
|
||||
<literal>&FALLBACK_HOSTNAME;</literal> (configurable at compilation time). Setting this property to the
|
||||
empty string will reset the dynamic hostname to the static hostname. If no static hostname is
|
||||
configured the dynamic hostname will be reset to <literal>&FALLBACK_HOSTNAME;</literal>. This property
|
||||
should be an internet-style hostname, 7-bit lowercase ASCII, no special chars/spaces.</para>
|
||||
It can be different from the static hostname. This property is never empty.</para>
|
||||
|
||||
<para>The <emphasis>pretty hostname</emphasis> is a free-form UTF-8 hostname for presentation to the
|
||||
user. User interfaces should ensure that the pretty hostname and the static hostname stay in sync.
|
||||
I.e. when the former is <literal>Lennart’s Computer</literal> the latter should be
|
||||
<literal>lennarts-computer</literal>. If no pretty hostname is set this setting will be the empty
|
||||
string. Applications should then find a suitable fallback, such as the dynamic hostname.</para>
|
||||
<para>The <varname>PrettyHostname</varname> property exposes the <emphasis>pretty hostname</emphasis>
|
||||
which is a free-form UTF-8 hostname for presentation to the user. User interfaces should ensure that the
|
||||
pretty hostname and the static hostname stay in sync. E.g. when the former is <literal>Lennart’s
|
||||
Computer</literal> the latter should be <literal>lennarts-computer</literal>. If no pretty hostname is
|
||||
set this setting will be the empty string. Applications should then find a suitable fallback, such as the
|
||||
dynamic hostname.</para>
|
||||
|
||||
<para>The <emphasis>icon name</emphasis> is a name following the XDG icon naming spec. If not set,
|
||||
information such as the chassis type (see below) is used to find a suitable fallback icon name
|
||||
(i.e. <literal>computer-laptop</literal> vs. <literal>computer-desktop</literal> is picked based on the
|
||||
chassis information). If no such data is available, the empty string is returned. In that case an application
|
||||
should fall back to a replacement icon, for example <literal>computer</literal>. If this property is set
|
||||
to the empty string, the automatic fallback name selection is enabled again.</para>
|
||||
<para>The <varname>FallbackHostname</varname> property exposes the fallback hostname (configured at
|
||||
compilation time).</para>
|
||||
|
||||
<para>The <emphasis>chassis type</emphasis> should be one of the currently defined chassis types:
|
||||
<literal>desktop</literal>, <literal>laptop</literal>, <literal>server</literal>,
|
||||
<literal>tablet</literal>, <literal>handset</literal>, as well as the special chassis types
|
||||
<literal>vm</literal> and <literal>container</literal> for virtualized systems. Note that in most cases
|
||||
the chassis type will be determined automatically from DMI/SMBIOS/ACPI firmware information. Writing to
|
||||
this setting is hence useful only to override misdetected chassis types, or to configure the chassis type if
|
||||
it could not be auto-detected. Set this property to the empty string to reenable the automatic detection of
|
||||
the chassis type from firmware information.</para>
|
||||
<para>The <varname>HostnameSource</varname> property exposes the origin of the currently configured
|
||||
hostname. One of <literal>static</literal> (set from <filename>/etc/hostname</filename>),
|
||||
<literal>transient</literal> (a non-permanent hostname from an external source),
|
||||
<literal>fallback</literal> (the compiled-in fallback value).</para>
|
||||
|
||||
<para>The <varname>IconName</varname> property exposes the <emphasis>icon name</emphasis> following the
|
||||
XDG icon naming spec. If not set, information such as the chassis type (see below) is used to find a
|
||||
suitable fallback icon name (i.e. <literal>computer-laptop</literal>
|
||||
vs. <literal>computer-desktop</literal> is picked based on the chassis information). If no such data is
|
||||
available, the empty string is returned. In that case an application should fall back to a replacement
|
||||
icon, for example <literal>computer</literal>. If this property is set to the empty string, the automatic
|
||||
fallback name selection is enabled again.</para>
|
||||
|
||||
<para>The <varname>Chassis</varname> property exposes a <emphasis>chassis type</emphasis>, one of the
|
||||
currently defined chassis types: <literal>desktop</literal>, <literal>laptop</literal>,
|
||||
<literal>server</literal>, <literal>tablet</literal>, <literal>handset</literal>, as well as the special
|
||||
chassis types <literal>vm</literal> and <literal>container</literal> for virtualized systems. Note that
|
||||
in most cases the chassis type will be determined automatically from DMI/SMBIOS/ACPI firmware
|
||||
information. Writing to this setting is hence useful only to override misdetected chassis types, or to
|
||||
configure the chassis type if it could not be auto-detected. Set this property to the empty string to
|
||||
reenable the automatic detection of the chassis type from firmware information.</para>
|
||||
|
||||
<para>Note that <filename>systemd-hostnamed</filename> starts only on request and terminates after a
|
||||
short idle period. This effectively means that <function>PropertyChanged</function> messages are not sent
|
||||
out for changes made directly on the files (as in: administrator edits the files with vi). This is
|
||||
the intended behavior: manual configuration changes should require manual reloading.</para>
|
||||
|
||||
<para>The transient (dynamic) hostname maps directly to the kernel hostname. This hostname should be
|
||||
assumed to be highly dynamic, and hence should be watched directly, without depending on
|
||||
<function>PropertyChanged</function> messages from <filename>systemd-hostnamed</filename>. To accomplish
|
||||
this, open <filename>/proc/sys/kernel/hostname</filename> and
|
||||
<para>The transient (dynamic) hostname exposed by the <varname>Hostname</varname> property maps directly
|
||||
to the kernel hostname. This hostname should be assumed to be highly dynamic, and hence should be watched
|
||||
directly, without depending on <function>PropertyChanged</function> messages from
|
||||
<filename>systemd-hostnamed</filename>. To accomplish this, open
|
||||
<filename>/proc/sys/kernel/hostname</filename> and
|
||||
<citerefentry project="man-pages"><refentrytitle>poll</refentrytitle><manvolnum>3</manvolnum></citerefentry>
|
||||
for <constant>SIGHUP</constant> which is triggered by the kernel every time the hostname changes. Again:
|
||||
this is special for the transient (dynamic) hostname, and does not apply to the configured (fixed)
|
||||
@ -206,15 +219,17 @@ node /org/freedesktop/hostname1 {
|
||||
for that. For more information on these files and syscalls see the respective man pages.</para>
|
||||
|
||||
<refsect2>
|
||||
<title>Methods and Properties</title>
|
||||
<title>Methods</title>
|
||||
|
||||
<para><function>SetHostname()</function> sets the transient (dynamic) hostname which is exposed by the
|
||||
<varname>Hostname</varname> property. If empty, the transient hostname is set to the static hostname.
|
||||
</para>
|
||||
<para><function>SetHostname()</function> sets the transient (dynamic) hostname, which is used if no
|
||||
static hostname is set. This value must be an internet-style hostname, 7-bit lowercase ASCII, no
|
||||
special chars/spaces. An empty string will unset the transient hostname.</para>
|
||||
|
||||
<para><function>SetStaticHostname()</function> sets the static hostname which is exposed by the
|
||||
<varname>StaticHostname</varname> property. If empty, the built-in default of
|
||||
<literal>&FALLBACK_HOSTNAME;</literal> is used.</para>
|
||||
<varname>StaticHostname</varname> property. When called with an empty argument, the static
|
||||
configuration in <filename>/etc/hostname</filename> is removed. Since the static hostname has the
|
||||
highest priority, calling this function usually affects also the <varname>Hostname</varname> property
|
||||
and the effective hostname configured in the kernel.</para>
|
||||
|
||||
<para><function>SetPrettyHostname()</function> sets the pretty hostname which is exposed by the
|
||||
<varname>PrettyHostname</varname> property.</para>
|
||||
@ -287,10 +302,6 @@ node /org/freedesktop/hostname1 {
|
||||
with <citerefentry><refentrytitle>nss-myhostname</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
|
||||
</para>
|
||||
|
||||
<para>A client that wants to change the local hostname for DHCP/mDNS should invoke
|
||||
<code>SetHostname("newname", false)</code> as soon as the name is available and afterwards reset it via
|
||||
<code>SetHostname("")</code>.</para>
|
||||
|
||||
<para>Here are some recommendations to follow when generating a static (internet) hostname from a pretty
|
||||
name:
|
||||
<itemizedlist>
|
||||
|
||||
@ -199,7 +199,7 @@
|
||||
specified as the special string <literal>.host</literal> the connection is made to the local system. This
|
||||
is useful to connect to the local system bus as specific user, e.g. <literal>foobar@.host</literal> to
|
||||
connect to the local system bus as local user <literal>foobar</literal>. If the <literal>@</literal>
|
||||
syntax is used either the left-hand side or the right-hand side may be ommited (but not both) in which
|
||||
syntax is used either the left-hand side or the right-hand side may be omitted (but not both) in which
|
||||
case the local user name or <literal>.host</literal> is implied. If the <literal>@</literal> syntax is
|
||||
not used the connection is always made as root user. See
|
||||
<citerefentry><refentrytitle>sd_bus_set_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>
|
||||
|
||||
@ -51,9 +51,15 @@
|
||||
</listitem>
|
||||
</itemizedlist></para>
|
||||
|
||||
<para>The static hostname is stored in <filename>/etc/hostname</filename>, see
|
||||
<citerefentry><refentrytitle>hostname</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more
|
||||
information. The pretty hostname, chassis type, and icon name are stored in
|
||||
<filename>/etc/machine-info</filename>, see
|
||||
<citerefentry><refentrytitle>machine-info</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
|
||||
|
||||
<para>The tool
|
||||
<citerefentry><refentrytitle>hostnamectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
is a command line client to this service.</para>
|
||||
<citerefentry><refentrytitle>hostnamectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> is a
|
||||
command line client to this service.</para>
|
||||
|
||||
<para>See
|
||||
<citerefentry><refentrytitle>org.freedesktop.hostname1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
|
||||
@ -51,7 +51,7 @@
|
||||
system is made (which is useful to connect to a specific user's user bus: <literal>--user
|
||||
--machine=lennart@.host</literal>). If the <literal>@</literal> syntax is not used, the connection is
|
||||
made as root user. If the <literal>@</literal> syntax is used either the left hand side or the right hand
|
||||
side may be ommitted (but not both) in which case the local user name and <literal>.host</literal> are
|
||||
side may be omitted (but not both) in which case the local user name and <literal>.host</literal> are
|
||||
implied.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
27
po/sv.po
27
po/sv.po
@ -5,13 +5,14 @@
|
||||
# Andreas Henriksson <andreas@fatal.se>, 2016.
|
||||
# Josef Andersson <l10nl18nsweja@gmail.com>, 2015, 2017.
|
||||
# Göran Uddeborg <goeran@uddeborg.se>, 2020.
|
||||
# Luna Jernberg <bittin@reimu.nl>, 2020.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: systemd master\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-08-19 18:02+0200\n"
|
||||
"PO-Revision-Date: 2020-08-27 02:46+0000\n"
|
||||
"Last-Translator: Göran Uddeborg <goeran@uddeborg.se>\n"
|
||||
"PO-Revision-Date: 2020-12-16 12:36+0000\n"
|
||||
"Last-Translator: Luna Jernberg <bittin@reimu.nl>\n"
|
||||
"Language-Team: Swedish <https://translate.fedoraproject.org/projects/systemd/"
|
||||
"master/sv/>\n"
|
||||
"Language: sv\n"
|
||||
@ -19,7 +20,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 4.2.1\n"
|
||||
"X-Generator: Weblate 4.3.2\n"
|
||||
|
||||
#: src/core/org.freedesktop.systemd1.policy.in:22
|
||||
msgid "Send passphrase back to system"
|
||||
@ -685,10 +686,8 @@ msgid "Set NTP servers"
|
||||
msgstr ""
|
||||
|
||||
#: src/network/org.freedesktop.network1.policy:23
|
||||
#, fuzzy
|
||||
#| msgid "Authentication is required to set the system time."
|
||||
msgid "Authentication is required to set NTP servers."
|
||||
msgstr "Autentisering krävs för ställa in systemtiden."
|
||||
msgstr "Autentisering krävs för ställa in NTP-servrar."
|
||||
|
||||
#: src/network/org.freedesktop.network1.policy:33
|
||||
#: src/resolve/org.freedesktop.resolve1.policy:44
|
||||
@ -697,10 +696,8 @@ msgstr ""
|
||||
|
||||
#: src/network/org.freedesktop.network1.policy:34
|
||||
#: src/resolve/org.freedesktop.resolve1.policy:45
|
||||
#, fuzzy
|
||||
#| msgid "Authentication is required to set the system time."
|
||||
msgid "Authentication is required to set DNS servers."
|
||||
msgstr "Autentisering krävs för ställa in systemtiden."
|
||||
msgstr "Autentisering krävs för ställa in DNS-servrar."
|
||||
|
||||
#: src/network/org.freedesktop.network1.policy:44
|
||||
#: src/resolve/org.freedesktop.resolve1.policy:55
|
||||
@ -729,7 +726,7 @@ msgstr "Autentisering krävs för att ställa in lokalt värdnamn."
|
||||
#: src/network/org.freedesktop.network1.policy:66
|
||||
#: src/resolve/org.freedesktop.resolve1.policy:77
|
||||
msgid "Enable/disable LLMNR"
|
||||
msgstr ""
|
||||
msgstr "Aktivera/inaktivera LLMNR"
|
||||
|
||||
#: src/network/org.freedesktop.network1.policy:67
|
||||
#: src/resolve/org.freedesktop.resolve1.policy:78
|
||||
@ -741,7 +738,7 @@ msgstr "Autentisering krävs för att försätta systemet i viloläge."
|
||||
#: src/network/org.freedesktop.network1.policy:77
|
||||
#: src/resolve/org.freedesktop.resolve1.policy:88
|
||||
msgid "Enable/disable multicast DNS"
|
||||
msgstr ""
|
||||
msgstr "Aktivera/inaktivera multicast-DNS"
|
||||
|
||||
#: src/network/org.freedesktop.network1.policy:78
|
||||
#: src/resolve/org.freedesktop.resolve1.policy:89
|
||||
@ -753,7 +750,7 @@ msgstr "Autentisering krävs för att logga in på den lokala värden"
|
||||
#: src/network/org.freedesktop.network1.policy:88
|
||||
#: src/resolve/org.freedesktop.resolve1.policy:99
|
||||
msgid "Enable/disable DNS over TLS"
|
||||
msgstr ""
|
||||
msgstr "Aktivera/inaktivera DNS över TLS"
|
||||
|
||||
#: src/network/org.freedesktop.network1.policy:89
|
||||
#: src/resolve/org.freedesktop.resolve1.policy:100
|
||||
@ -765,7 +762,7 @@ msgstr "Autentisering krävs för att ställa in lokalt värdnamn."
|
||||
#: src/network/org.freedesktop.network1.policy:99
|
||||
#: src/resolve/org.freedesktop.resolve1.policy:110
|
||||
msgid "Enable/disable DNSSEC"
|
||||
msgstr ""
|
||||
msgstr "Aktivera/inaktivera DNSSEC"
|
||||
|
||||
#: src/network/org.freedesktop.network1.policy:100
|
||||
#: src/resolve/org.freedesktop.resolve1.policy:111
|
||||
@ -880,7 +877,7 @@ msgstr "Autentisering krävs för att hämta ner en VM eller behållaravbildning
|
||||
|
||||
#: src/resolve/org.freedesktop.resolve1.policy:22
|
||||
msgid "Register a DNS-SD service"
|
||||
msgstr ""
|
||||
msgstr "Registrera en DNS-SD-tjänst"
|
||||
|
||||
#: src/resolve/org.freedesktop.resolve1.policy:23
|
||||
#, fuzzy
|
||||
@ -890,7 +887,7 @@ msgstr "Autentisering krävs för att ställa in ett väggmeddelande"
|
||||
|
||||
#: src/resolve/org.freedesktop.resolve1.policy:33
|
||||
msgid "Unregister a DNS-SD service"
|
||||
msgstr ""
|
||||
msgstr "Avregistrera en DNS-SD-tjänst"
|
||||
|
||||
#: src/resolve/org.freedesktop.resolve1.policy:34
|
||||
#, fuzzy
|
||||
|
||||
8
rules.d/70-memory.rules
Normal file
8
rules.d/70-memory.rules
Normal file
@ -0,0 +1,8 @@
|
||||
# do not edit this file, it will be overwritten on update
|
||||
|
||||
ACTION=="remove", GOTO="memory_end"
|
||||
SUBSYSTEM!="dmi", GOTO="memory_end"
|
||||
|
||||
IMPORT{program}="dmi_memory_id"
|
||||
|
||||
LABEL="memory_end"
|
||||
@ -17,6 +17,7 @@ rules = files('''
|
||||
60-serial.rules
|
||||
70-joystick.rules
|
||||
70-mouse.rules
|
||||
70-memory.rules
|
||||
70-touchpad.rules
|
||||
75-net-description.rules
|
||||
75-probe_mtd.rules
|
||||
|
||||
@ -7,28 +7,10 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "hostname-util.h"
|
||||
#include "macro.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
bool hostname_is_set(void) {
|
||||
struct utsname u;
|
||||
|
||||
assert_se(uname(&u) >= 0);
|
||||
|
||||
if (isempty(u.nodename))
|
||||
return false;
|
||||
|
||||
/* This is the built-in kernel default hostname */
|
||||
if (streq(u.nodename, "(none)"))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
char* gethostname_malloc(void) {
|
||||
struct utsname u;
|
||||
const char *s;
|
||||
@ -208,105 +190,3 @@ bool is_localhost(const char *hostname) {
|
||||
endswith_no_case(hostname, ".localhost.localdomain") ||
|
||||
endswith_no_case(hostname, ".localhost.localdomain.");
|
||||
}
|
||||
|
||||
int sethostname_idempotent(const char *s) {
|
||||
char buf[HOST_NAME_MAX + 1] = {};
|
||||
|
||||
assert(s);
|
||||
|
||||
if (gethostname(buf, sizeof(buf)) < 0)
|
||||
return -errno;
|
||||
|
||||
if (streq(buf, s))
|
||||
return 0;
|
||||
|
||||
if (sethostname(s, strlen(s)) < 0)
|
||||
return -errno;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int shorten_overlong(const char *s, char **ret) {
|
||||
char *h, *p;
|
||||
|
||||
/* Shorten an overlong name to HOST_NAME_MAX or to the first dot,
|
||||
* whatever comes earlier. */
|
||||
|
||||
assert(s);
|
||||
|
||||
h = strdup(s);
|
||||
if (!h)
|
||||
return -ENOMEM;
|
||||
|
||||
if (hostname_is_valid(h, 0)) {
|
||||
*ret = h;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = strchr(h, '.');
|
||||
if (p)
|
||||
*p = 0;
|
||||
|
||||
strshorten(h, HOST_NAME_MAX);
|
||||
|
||||
if (!hostname_is_valid(h, 0)) {
|
||||
free(h);
|
||||
return -EDOM;
|
||||
}
|
||||
|
||||
*ret = h;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int read_etc_hostname_stream(FILE *f, char **ret) {
|
||||
int r;
|
||||
|
||||
assert(f);
|
||||
assert(ret);
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
char *p;
|
||||
|
||||
r = read_line(f, LONG_LINE_MAX, &line);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) /* EOF without any hostname? the file is empty, let's treat that exactly like no file at all: ENOENT */
|
||||
return -ENOENT;
|
||||
|
||||
p = strstrip(line);
|
||||
|
||||
/* File may have empty lines or comments, ignore them */
|
||||
if (!IN_SET(*p, '\0', '#')) {
|
||||
char *copy;
|
||||
|
||||
hostname_cleanup(p); /* normalize the hostname */
|
||||
|
||||
if (!hostname_is_valid(p, VALID_HOSTNAME_TRAILING_DOT)) /* check that the hostname we return is valid */
|
||||
return -EBADMSG;
|
||||
|
||||
copy = strdup(p);
|
||||
if (!copy)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = copy;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int read_etc_hostname(const char *path, char **ret) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (!path)
|
||||
path = "/etc/hostname";
|
||||
|
||||
f = fopen(path, "re");
|
||||
if (!f)
|
||||
return -errno;
|
||||
|
||||
return read_etc_hostname_stream(f, ret);
|
||||
|
||||
}
|
||||
|
||||
@ -7,8 +7,6 @@
|
||||
#include "macro.h"
|
||||
#include "strv.h"
|
||||
|
||||
bool hostname_is_set(void);
|
||||
|
||||
char* gethostname_malloc(void);
|
||||
char* gethostname_short_malloc(void);
|
||||
int gethostname_strict(char **ret);
|
||||
@ -29,10 +27,3 @@ static inline bool is_gateway_hostname(const char *hostname) {
|
||||
/* This tries to identify the valid syntaxes for the our synthetic "gateway" host. */
|
||||
return STRCASE_IN_SET(hostname, "_gateway", "_gateway.");
|
||||
}
|
||||
|
||||
int sethostname_idempotent(const char *s);
|
||||
|
||||
int shorten_overlong(const char *s, char **ret);
|
||||
|
||||
int read_etc_hostname_stream(FILE *f, char **ret);
|
||||
int read_etc_hostname(const char *path, char **ret);
|
||||
|
||||
1869
src/basic/linux/netfilter/nf_tables.h
Normal file
1869
src/basic/linux/netfilter/nf_tables.h
Normal file
File diff suppressed because it is too large
Load Diff
81
src/basic/linux/netfilter/nfnetlink.h
Normal file
81
src/basic/linux/netfilter/nfnetlink.h
Normal file
@ -0,0 +1,81 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
#ifndef _NFNETLINK_H
|
||||
#define _NFNETLINK_H
|
||||
#include <linux/types.h>
|
||||
#include <linux/netfilter/nfnetlink_compat.h>
|
||||
|
||||
enum nfnetlink_groups {
|
||||
NFNLGRP_NONE,
|
||||
#define NFNLGRP_NONE NFNLGRP_NONE
|
||||
NFNLGRP_CONNTRACK_NEW,
|
||||
#define NFNLGRP_CONNTRACK_NEW NFNLGRP_CONNTRACK_NEW
|
||||
NFNLGRP_CONNTRACK_UPDATE,
|
||||
#define NFNLGRP_CONNTRACK_UPDATE NFNLGRP_CONNTRACK_UPDATE
|
||||
NFNLGRP_CONNTRACK_DESTROY,
|
||||
#define NFNLGRP_CONNTRACK_DESTROY NFNLGRP_CONNTRACK_DESTROY
|
||||
NFNLGRP_CONNTRACK_EXP_NEW,
|
||||
#define NFNLGRP_CONNTRACK_EXP_NEW NFNLGRP_CONNTRACK_EXP_NEW
|
||||
NFNLGRP_CONNTRACK_EXP_UPDATE,
|
||||
#define NFNLGRP_CONNTRACK_EXP_UPDATE NFNLGRP_CONNTRACK_EXP_UPDATE
|
||||
NFNLGRP_CONNTRACK_EXP_DESTROY,
|
||||
#define NFNLGRP_CONNTRACK_EXP_DESTROY NFNLGRP_CONNTRACK_EXP_DESTROY
|
||||
NFNLGRP_NFTABLES,
|
||||
#define NFNLGRP_NFTABLES NFNLGRP_NFTABLES
|
||||
NFNLGRP_ACCT_QUOTA,
|
||||
#define NFNLGRP_ACCT_QUOTA NFNLGRP_ACCT_QUOTA
|
||||
NFNLGRP_NFTRACE,
|
||||
#define NFNLGRP_NFTRACE NFNLGRP_NFTRACE
|
||||
__NFNLGRP_MAX,
|
||||
};
|
||||
#define NFNLGRP_MAX (__NFNLGRP_MAX - 1)
|
||||
|
||||
/* General form of address family dependent message.
|
||||
*/
|
||||
struct nfgenmsg {
|
||||
__u8 nfgen_family; /* AF_xxx */
|
||||
__u8 version; /* nfnetlink version */
|
||||
__be16 res_id; /* resource id */
|
||||
};
|
||||
|
||||
#define NFNETLINK_V0 0
|
||||
|
||||
/* netfilter netlink message types are split in two pieces:
|
||||
* 8 bit subsystem, 8bit operation.
|
||||
*/
|
||||
|
||||
#define NFNL_SUBSYS_ID(x) ((x & 0xff00) >> 8)
|
||||
#define NFNL_MSG_TYPE(x) (x & 0x00ff)
|
||||
|
||||
/* No enum here, otherwise __stringify() trick of MODULE_ALIAS_NFNL_SUBSYS()
|
||||
* won't work anymore */
|
||||
#define NFNL_SUBSYS_NONE 0
|
||||
#define NFNL_SUBSYS_CTNETLINK 1
|
||||
#define NFNL_SUBSYS_CTNETLINK_EXP 2
|
||||
#define NFNL_SUBSYS_QUEUE 3
|
||||
#define NFNL_SUBSYS_ULOG 4
|
||||
#define NFNL_SUBSYS_OSF 5
|
||||
#define NFNL_SUBSYS_IPSET 6
|
||||
#define NFNL_SUBSYS_ACCT 7
|
||||
#define NFNL_SUBSYS_CTNETLINK_TIMEOUT 8
|
||||
#define NFNL_SUBSYS_CTHELPER 9
|
||||
#define NFNL_SUBSYS_NFTABLES 10
|
||||
#define NFNL_SUBSYS_NFT_COMPAT 11
|
||||
#define NFNL_SUBSYS_COUNT 12
|
||||
|
||||
/* Reserved control nfnetlink messages */
|
||||
#define NFNL_MSG_BATCH_BEGIN NLMSG_MIN_TYPE
|
||||
#define NFNL_MSG_BATCH_END NLMSG_MIN_TYPE+1
|
||||
|
||||
/**
|
||||
* enum nfnl_batch_attributes - nfnetlink batch netlink attributes
|
||||
*
|
||||
* @NFNL_BATCH_GENID: generation ID for this changeset (NLA_U32)
|
||||
*/
|
||||
enum nfnl_batch_attributes {
|
||||
NFNL_BATCH_UNSPEC,
|
||||
NFNL_BATCH_GENID,
|
||||
__NFNL_BATCH_MAX
|
||||
};
|
||||
#define NFNL_BATCH_MAX (__NFNL_BATCH_MAX - 1)
|
||||
|
||||
#endif /* _NFNETLINK_H */
|
||||
@ -404,7 +404,7 @@ static int write_to_console(
|
||||
if (show_location) {
|
||||
const char *lon = "", *loff = "";
|
||||
if (log_get_show_color()) {
|
||||
lon = ANSI_HIGHLIGHT_YELLOW4;
|
||||
lon = ansi_highlight_yellow4();
|
||||
loff = ANSI_NORMAL;
|
||||
}
|
||||
|
||||
|
||||
@ -78,6 +78,7 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
|
||||
_DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope)
|
||||
|
||||
#define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,)
|
||||
#define DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,)
|
||||
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static)
|
||||
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static)
|
||||
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static)
|
||||
|
||||
@ -47,7 +47,7 @@ static volatile unsigned cached_columns = 0;
|
||||
static volatile unsigned cached_lines = 0;
|
||||
|
||||
static volatile int cached_on_tty = -1;
|
||||
static volatile int cached_colors_enabled = -1;
|
||||
static volatile int cached_color_mode = _COLOR_INVALID;
|
||||
static volatile int cached_underline_enabled = -1;
|
||||
|
||||
int chvt(int vt) {
|
||||
@ -164,8 +164,7 @@ int ask_char(char *ret, const char *replies, const char *fmt, ...) {
|
||||
char c;
|
||||
bool need_nl = true;
|
||||
|
||||
if (colors_enabled())
|
||||
fputs(ANSI_HIGHLIGHT, stdout);
|
||||
fputs(ansi_highlight(), stdout);
|
||||
|
||||
putchar('\r');
|
||||
|
||||
@ -173,8 +172,7 @@ int ask_char(char *ret, const char *replies, const char *fmt, ...) {
|
||||
vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (colors_enabled())
|
||||
fputs(ANSI_NORMAL, stdout);
|
||||
fputs(ansi_normal(), stdout);
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
@ -213,15 +211,13 @@ int ask_string(char **ret, const char *text, ...) {
|
||||
assert(ret);
|
||||
assert(text);
|
||||
|
||||
if (colors_enabled())
|
||||
fputs(ANSI_HIGHLIGHT, stdout);
|
||||
fputs(ansi_highlight(), stdout);
|
||||
|
||||
va_start(ap, text);
|
||||
vprintf(text, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (colors_enabled())
|
||||
fputs(ANSI_NORMAL, stdout);
|
||||
fputs(ansi_normal(), stdout);
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
@ -867,7 +863,7 @@ void reset_terminal_feature_caches(void) {
|
||||
cached_columns = 0;
|
||||
cached_lines = 0;
|
||||
|
||||
cached_colors_enabled = -1;
|
||||
cached_color_mode = _COLOR_INVALID;
|
||||
cached_underline_enabled = -1;
|
||||
cached_on_tty = -1;
|
||||
}
|
||||
@ -1206,38 +1202,57 @@ bool terminal_is_dumb(void) {
|
||||
return getenv_terminal_is_dumb();
|
||||
}
|
||||
|
||||
bool colors_enabled(void) {
|
||||
static ColorMode parse_systemd_colors(void) {
|
||||
const char *e;
|
||||
int r;
|
||||
|
||||
/* Returns true if colors are considered supported on our stdout. For that we check $SYSTEMD_COLORS first
|
||||
* (which is the explicit way to turn colors on/off). If that didn't work we turn colors off unless we are on a
|
||||
* TTY. And if we are on a TTY we turn it off if $TERM is set to "dumb". There's one special tweak though: if
|
||||
* we are PID 1 then we do not check whether we are connected to a TTY, because we don't keep /dev/console open
|
||||
* continuously due to fear of SAK, and hence things are a bit weird. */
|
||||
e = getenv("SYSTEMD_COLORS");
|
||||
if (!e)
|
||||
return _COLOR_INVALID;
|
||||
if (streq(e, "16"))
|
||||
return COLOR_16;
|
||||
if (streq(e, "256"))
|
||||
return COLOR_256;
|
||||
r = parse_boolean(e);
|
||||
if (r >= 0)
|
||||
return r > 0 ? COLOR_ON : COLOR_OFF;
|
||||
return _COLOR_INVALID;
|
||||
}
|
||||
|
||||
if (cached_colors_enabled < 0) {
|
||||
int val;
|
||||
ColorMode get_color_mode(void) {
|
||||
|
||||
val = getenv_bool("SYSTEMD_COLORS");
|
||||
if (val >= 0)
|
||||
cached_colors_enabled = val;
|
||||
/* Returns the mode used to choose output colors. The possible modes are COLOR_OFF for no colors,
|
||||
* COLOR_16 for only the base 16 ANSI colors, COLOR_256 for more colors and COLOR_ON for unrestricted
|
||||
* color output. For that we check $SYSTEMD_COLORS first (which is the explicit way to
|
||||
* change the mode). If that didn't work we turn colors off unless we are on a TTY. And if we are on a TTY
|
||||
* we turn it off if $TERM is set to "dumb". There's one special tweak though: if we are PID 1 then we do not
|
||||
* check whether we are connected to a TTY, because we don't keep /dev/console open continuously due to fear
|
||||
* of SAK, and hence things are a bit weird. */
|
||||
ColorMode m;
|
||||
|
||||
if (cached_color_mode < 0) {
|
||||
m = parse_systemd_colors();
|
||||
if (m >= 0)
|
||||
cached_color_mode = m;
|
||||
else if (getenv("NO_COLOR"))
|
||||
/* We only check for the presence of the variable; value is ignored. */
|
||||
cached_colors_enabled = false;
|
||||
cached_color_mode = COLOR_OFF;
|
||||
|
||||
else if (getpid_cached() == 1)
|
||||
/* PID1 outputs to the console without holding it open all the time */
|
||||
cached_colors_enabled = !getenv_terminal_is_dumb();
|
||||
/* PID1 outputs to the console without holding it open all the time.
|
||||
* Also note the Linux console can only handle 16 colors.
|
||||
*/
|
||||
cached_color_mode = getenv_terminal_is_dumb() ? COLOR_OFF : COLOR_16;
|
||||
else
|
||||
cached_colors_enabled = !terminal_is_dumb();
|
||||
cached_color_mode = terminal_is_dumb() ? COLOR_OFF : COLOR_256;
|
||||
}
|
||||
|
||||
return cached_colors_enabled;
|
||||
return cached_color_mode;
|
||||
}
|
||||
|
||||
bool dev_console_colors_enabled(void) {
|
||||
_cleanup_free_ char *s = NULL;
|
||||
int b;
|
||||
ColorMode m;
|
||||
|
||||
/* Returns true if we assume that color is supported on /dev/console.
|
||||
*
|
||||
@ -1246,9 +1261,9 @@ bool dev_console_colors_enabled(void) {
|
||||
* line. If we find $TERM set we assume color if it's not set to "dumb", similarly to how regular
|
||||
* colors_enabled() operates. */
|
||||
|
||||
b = getenv_bool("SYSTEMD_COLORS");
|
||||
if (b >= 0)
|
||||
return b;
|
||||
m = parse_systemd_colors();
|
||||
if (m >= 0)
|
||||
return m;
|
||||
|
||||
if (getenv("NO_COLOR"))
|
||||
return false;
|
||||
@ -1353,7 +1368,7 @@ void get_log_colors(int priority, const char **on, const char **off, const char
|
||||
|
||||
if (priority <= LOG_ERR) {
|
||||
if (on)
|
||||
*on = ANSI_HIGHLIGHT_RED;
|
||||
*on = ansi_highlight_red();
|
||||
if (off)
|
||||
*off = ANSI_NORMAL;
|
||||
if (highlight)
|
||||
@ -1361,7 +1376,7 @@ void get_log_colors(int priority, const char **on, const char **off, const char
|
||||
|
||||
} else if (priority <= LOG_WARNING) {
|
||||
if (on)
|
||||
*on = ANSI_HIGHLIGHT_YELLOW;
|
||||
*on = ansi_highlight_yellow();
|
||||
if (off)
|
||||
*off = ANSI_NORMAL;
|
||||
if (highlight)
|
||||
@ -1373,14 +1388,14 @@ void get_log_colors(int priority, const char **on, const char **off, const char
|
||||
if (off)
|
||||
*off = ANSI_NORMAL;
|
||||
if (highlight)
|
||||
*highlight = ANSI_HIGHLIGHT_RED;
|
||||
*highlight = ansi_highlight_red();
|
||||
|
||||
} else if (priority >= LOG_DEBUG) {
|
||||
if (on)
|
||||
*on = ANSI_GREY;
|
||||
*on = ansi_grey();
|
||||
if (off)
|
||||
*off = ANSI_NORMAL;
|
||||
if (highlight)
|
||||
*highlight = ANSI_HIGHLIGHT_RED;
|
||||
*highlight = ansi_highlight_red();
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,6 +61,10 @@
|
||||
#define ANSI_HIGHLIGHT "\x1B[0;1;39m"
|
||||
#define ANSI_HIGHLIGHT_UNDERLINE "\x1B[0;1;4m"
|
||||
|
||||
/* Fallback colors: 256 -> 16 */
|
||||
#define ANSI_HIGHLIGHT_GREY_FALLBACK "\x1B[0;1;90m"
|
||||
#define ANSI_HIGHLIGHT_YELLOW_FALLBACK "\x1B[0;1;33m"
|
||||
|
||||
/* Reset/clear ANSI styles */
|
||||
#define ANSI_NORMAL "\x1B[0m"
|
||||
|
||||
@ -93,6 +97,23 @@ typedef enum AcquireTerminalFlags {
|
||||
ACQUIRE_TERMINAL_PERMISSIVE = 1 << 2,
|
||||
} AcquireTerminalFlags;
|
||||
|
||||
/* Limits the use of ANSI colors to a subset. */
|
||||
typedef enum ColorMode {
|
||||
/* No colors, monochrome output. */
|
||||
COLOR_OFF = 0,
|
||||
|
||||
/* All colors, no restrictions. */
|
||||
COLOR_ON = 1,
|
||||
|
||||
/* Only the base 16 colors. */
|
||||
COLOR_16 = 16,
|
||||
|
||||
/* Only 256 colors. */
|
||||
COLOR_256 = 256,
|
||||
|
||||
_COLOR_INVALID = -1,
|
||||
} ColorMode;
|
||||
|
||||
int acquire_terminal(const char *name, AcquireTerminalFlags flags, usec_t timeout);
|
||||
int release_terminal(void);
|
||||
|
||||
@ -127,19 +148,44 @@ void reset_terminal_feature_caches(void);
|
||||
|
||||
bool on_tty(void);
|
||||
bool terminal_is_dumb(void);
|
||||
bool colors_enabled(void);
|
||||
ColorMode get_color_mode(void);
|
||||
bool underline_enabled(void);
|
||||
bool dev_console_colors_enabled(void);
|
||||
|
||||
static inline bool colors_enabled(void) {
|
||||
|
||||
/* Returns true if colors are considered supported on our stdout. */
|
||||
return get_color_mode() != COLOR_OFF;
|
||||
}
|
||||
|
||||
#define DEFINE_ANSI_FUNC(name, NAME) \
|
||||
static inline const char *ansi_##name(void) { \
|
||||
return colors_enabled() ? ANSI_##NAME : ""; \
|
||||
}
|
||||
|
||||
#define DEFINE_ANSI_FUNC_UNDERLINE(name, NAME, REPLACEMENT) \
|
||||
static inline const char *ansi_##name(void) { \
|
||||
return underline_enabled() ? ANSI_##NAME : \
|
||||
colors_enabled() ? ANSI_##REPLACEMENT : ""; \
|
||||
#define DEFINE_ANSI_FUNC_256(name, NAME, FALLBACK) \
|
||||
static inline const char *ansi_##name(void) { \
|
||||
switch (get_color_mode()) { \
|
||||
case COLOR_OFF: return ""; \
|
||||
case COLOR_16: return ANSI_##FALLBACK; \
|
||||
default : return ANSI_##NAME; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define DEFINE_ANSI_FUNC_UNDERLINE(name, NAME) \
|
||||
static inline const char *ansi_##name(void) { \
|
||||
return underline_enabled() ? ANSI_##NAME ANSI_UNDERLINE : \
|
||||
colors_enabled() ? ANSI_##NAME : ""; \
|
||||
}
|
||||
|
||||
|
||||
#define DEFINE_ANSI_FUNC_UNDERLINE_256(name, NAME, FALLBACK) \
|
||||
static inline const char *ansi_##name(void) { \
|
||||
switch (get_color_mode()) { \
|
||||
case COLOR_OFF: return ""; \
|
||||
case COLOR_16: return underline_enabled() ? ANSI_##FALLBACK ANSI_UNDERLINE : ANSI_##FALLBACK; \
|
||||
default : return underline_enabled() ? ANSI_##NAME ANSI_UNDERLINE: ANSI_##NAME; \
|
||||
} \
|
||||
}
|
||||
|
||||
DEFINE_ANSI_FUNC(normal, NORMAL);
|
||||
@ -152,7 +198,7 @@ DEFINE_ANSI_FUNC(blue, BLUE);
|
||||
DEFINE_ANSI_FUNC(magenta, MAGENTA);
|
||||
DEFINE_ANSI_FUNC(cyan, CYAN);
|
||||
DEFINE_ANSI_FUNC(white, WHITE);
|
||||
DEFINE_ANSI_FUNC(grey, GREY);
|
||||
DEFINE_ANSI_FUNC_256(grey, GREY, BRIGHT_BLACK);
|
||||
|
||||
DEFINE_ANSI_FUNC(bright_black, BRIGHT_BLACK);
|
||||
DEFINE_ANSI_FUNC(bright_red, BRIGHT_RED);
|
||||
@ -163,29 +209,30 @@ DEFINE_ANSI_FUNC(bright_magenta, BRIGHT_MAGENTA);
|
||||
DEFINE_ANSI_FUNC(bright_cyan, BRIGHT_CYAN);
|
||||
DEFINE_ANSI_FUNC(bright_white, BRIGHT_WHITE);
|
||||
|
||||
DEFINE_ANSI_FUNC(highlight_black, HIGHLIGHT_BLACK);
|
||||
DEFINE_ANSI_FUNC(highlight_red, HIGHLIGHT_RED);
|
||||
DEFINE_ANSI_FUNC(highlight_green, HIGHLIGHT_GREEN);
|
||||
DEFINE_ANSI_FUNC(highlight_yellow, HIGHLIGHT_YELLOW);
|
||||
DEFINE_ANSI_FUNC(highlight_blue, HIGHLIGHT_BLUE);
|
||||
DEFINE_ANSI_FUNC(highlight_magenta, HIGHLIGHT_MAGENTA);
|
||||
DEFINE_ANSI_FUNC(highlight_cyan, HIGHLIGHT_CYAN);
|
||||
DEFINE_ANSI_FUNC(highlight_grey, HIGHLIGHT_GREY);
|
||||
DEFINE_ANSI_FUNC(highlight_white, HIGHLIGHT_WHITE);
|
||||
DEFINE_ANSI_FUNC(highlight_black, HIGHLIGHT_BLACK);
|
||||
DEFINE_ANSI_FUNC(highlight_red, HIGHLIGHT_RED);
|
||||
DEFINE_ANSI_FUNC(highlight_green, HIGHLIGHT_GREEN);
|
||||
DEFINE_ANSI_FUNC_256(highlight_yellow, HIGHLIGHT_YELLOW, HIGHLIGHT_YELLOW_FALLBACK);
|
||||
DEFINE_ANSI_FUNC_256(highlight_yellow4, HIGHLIGHT_YELLOW4, HIGHLIGHT_YELLOW_FALLBACK);
|
||||
DEFINE_ANSI_FUNC(highlight_blue, HIGHLIGHT_BLUE);
|
||||
DEFINE_ANSI_FUNC(highlight_magenta, HIGHLIGHT_MAGENTA);
|
||||
DEFINE_ANSI_FUNC(highlight_cyan, HIGHLIGHT_CYAN);
|
||||
DEFINE_ANSI_FUNC_256(highlight_grey, HIGHLIGHT_GREY, HIGHLIGHT_GREY_FALLBACK);
|
||||
DEFINE_ANSI_FUNC(highlight_white, HIGHLIGHT_WHITE);
|
||||
|
||||
static inline const char* _ansi_highlight_yellow(void) {
|
||||
return colors_enabled() ? _ANSI_HIGHLIGHT_YELLOW : "";
|
||||
}
|
||||
|
||||
DEFINE_ANSI_FUNC_UNDERLINE(underline, UNDERLINE, NORMAL);
|
||||
DEFINE_ANSI_FUNC_UNDERLINE(highlight_underline, HIGHLIGHT_UNDERLINE, HIGHLIGHT);
|
||||
DEFINE_ANSI_FUNC_UNDERLINE(grey_underline, GREY_UNDERLINE, GREY);
|
||||
DEFINE_ANSI_FUNC_UNDERLINE(highlight_red_underline, HIGHLIGHT_RED_UNDERLINE, HIGHLIGHT_RED);
|
||||
DEFINE_ANSI_FUNC_UNDERLINE(highlight_green_underline, HIGHLIGHT_GREEN_UNDERLINE, HIGHLIGHT_GREEN);
|
||||
DEFINE_ANSI_FUNC_UNDERLINE(highlight_yellow_underline, HIGHLIGHT_YELLOW_UNDERLINE, HIGHLIGHT_YELLOW);
|
||||
DEFINE_ANSI_FUNC_UNDERLINE(highlight_blue_underline, HIGHLIGHT_BLUE_UNDERLINE, HIGHLIGHT_BLUE);
|
||||
DEFINE_ANSI_FUNC_UNDERLINE(highlight_magenta_underline, HIGHLIGHT_MAGENTA_UNDERLINE, HIGHLIGHT_MAGENTA);
|
||||
DEFINE_ANSI_FUNC_UNDERLINE(highlight_grey_underline, HIGHLIGHT_GREY_UNDERLINE, HIGHLIGHT_GREY);
|
||||
DEFINE_ANSI_FUNC_UNDERLINE(underline, NORMAL);
|
||||
DEFINE_ANSI_FUNC_UNDERLINE(highlight_underline, HIGHLIGHT);
|
||||
DEFINE_ANSI_FUNC_UNDERLINE_256(grey_underline, GREY, BRIGHT_BLACK);
|
||||
DEFINE_ANSI_FUNC_UNDERLINE(highlight_red_underline, HIGHLIGHT_RED);
|
||||
DEFINE_ANSI_FUNC_UNDERLINE(highlight_green_underline, HIGHLIGHT_GREEN);
|
||||
DEFINE_ANSI_FUNC_UNDERLINE_256(highlight_yellow_underline, HIGHLIGHT_YELLOW, HIGHLIGHT_YELLOW_FALLBACK);
|
||||
DEFINE_ANSI_FUNC_UNDERLINE(highlight_blue_underline, HIGHLIGHT_BLUE);
|
||||
DEFINE_ANSI_FUNC_UNDERLINE(highlight_magenta_underline, HIGHLIGHT_MAGENTA);
|
||||
DEFINE_ANSI_FUNC_UNDERLINE_256(highlight_grey_underline, HIGHLIGHT_GREY, HIGHLIGHT_GREY_FALLBACK);
|
||||
|
||||
int get_ctty_devnr(pid_t pid, dev_t *d);
|
||||
int get_ctty(pid_t, dev_t *_devnr, char **r);
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
#include "hexdecoct.h"
|
||||
#include "io-util.h"
|
||||
#include "ioprio.h"
|
||||
#include "journal-util.h"
|
||||
#include "journal-file.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "namespace.h"
|
||||
#include "parse-util.h"
|
||||
|
||||
@ -1,62 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "fileio.h"
|
||||
#include "hostname-setup.h"
|
||||
#include "hostname-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "proc-cmdline.h"
|
||||
#include "string-util.h"
|
||||
#include "util.h"
|
||||
|
||||
int hostname_setup(void) {
|
||||
_cleanup_free_ char *b = NULL;
|
||||
const char *hn = NULL;
|
||||
bool enoent = false;
|
||||
int r;
|
||||
|
||||
r = proc_cmdline_get_key("systemd.hostname", 0, &b);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to retrieve system hostname from kernel command line, ignoring: %m");
|
||||
else if (r > 0) {
|
||||
if (hostname_is_valid(b, VALID_HOSTNAME_TRAILING_DOT))
|
||||
hn = b;
|
||||
else {
|
||||
log_warning("Hostname specified on kernel command line is invalid, ignoring: %s", b);
|
||||
b = mfree(b);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hn) {
|
||||
r = read_etc_hostname(NULL, &b);
|
||||
if (r == -ENOENT)
|
||||
enoent = true;
|
||||
else if (r < 0)
|
||||
log_warning_errno(r, "Failed to read configured hostname, ignoring: %m");
|
||||
else
|
||||
hn = b;
|
||||
}
|
||||
|
||||
if (isempty(hn)) {
|
||||
/* Don't override the hostname if it is already set and not explicitly configured */
|
||||
if (hostname_is_set())
|
||||
return 0;
|
||||
|
||||
if (enoent)
|
||||
log_info("No hostname configured.");
|
||||
|
||||
hn = FALLBACK_HOSTNAME;
|
||||
}
|
||||
|
||||
r = sethostname_idempotent(hn);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to set hostname to <%s>: %m", hn);
|
||||
|
||||
log_info("Set hostname to <%s>.", hn);
|
||||
return 0;
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
int hostname_setup(void);
|
||||
@ -38,7 +38,7 @@
|
||||
#include "io-util.h"
|
||||
#include "ioprio.h"
|
||||
#include "ip-protocol-list.h"
|
||||
#include "journal-util.h"
|
||||
#include "journal-file.h"
|
||||
#include "limits-util.h"
|
||||
#include "load-fragment.h"
|
||||
#include "log.h"
|
||||
|
||||
@ -2064,7 +2064,7 @@ static int initialize_runtime(
|
||||
}
|
||||
|
||||
status_welcome();
|
||||
hostname_setup();
|
||||
(void) hostname_setup(true);
|
||||
/* Force transient machine-id on first boot. */
|
||||
machine_id_setup(NULL, first_boot, arg_machine_id, NULL);
|
||||
(void) loopback_setup();
|
||||
|
||||
@ -76,8 +76,6 @@ libcore_sources = '''
|
||||
execute.h
|
||||
generator-setup.c
|
||||
generator-setup.h
|
||||
hostname-setup.c
|
||||
hostname-setup.h
|
||||
ima-setup.c
|
||||
ima-setup.h
|
||||
ip-address-access.c
|
||||
|
||||
@ -901,7 +901,7 @@ static int mount_procfs(const MountEntry *m, const NamespaceInfo *ns_info) {
|
||||
* mount. Hence let's gracefully fallback to a classic, unrestricted version. */
|
||||
r = mount_nofollow_verbose(LOG_DEBUG, "proc", entry_path, "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
|
||||
if (r == -EPERM) {
|
||||
/* When we do not have enough priviledge to mount /proc, fallback to use existing /proc. */
|
||||
/* When we do not have enough privileges to mount /proc, fallback to use existing /proc. */
|
||||
|
||||
if (n > 0)
|
||||
/* /proc or some of sub-mounts are umounted in the above. Refuse incomplete tree.
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fuzz.h"
|
||||
#include "hostname-util.h"
|
||||
#include "hostname-setup.h"
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
@ -123,7 +123,7 @@ fuzzers += [
|
||||
[libshared],
|
||||
[]],
|
||||
|
||||
[['src/fuzz/fuzz-hostname-util.c'],
|
||||
[['src/fuzz/fuzz-hostname-setup.c'],
|
||||
[libshared],
|
||||
[]],
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "bus-common-errors.h"
|
||||
#include "bus-get-properties.h"
|
||||
#include "bus-log-control-api.h"
|
||||
#include "bus-polkit.h"
|
||||
#include "def.h"
|
||||
@ -16,6 +17,7 @@
|
||||
#include "env-util.h"
|
||||
#include "fileio-label.h"
|
||||
#include "fileio.h"
|
||||
#include "hostname-setup.h"
|
||||
#include "hostname-util.h"
|
||||
#include "id128-util.h"
|
||||
#include "main-func.h"
|
||||
@ -29,6 +31,7 @@
|
||||
#include "service-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-table.h"
|
||||
#include "strv.h"
|
||||
#include "user-util.h"
|
||||
#include "util.h"
|
||||
@ -58,6 +61,8 @@ enum {
|
||||
typedef struct Context {
|
||||
char *data[_PROP_MAX];
|
||||
|
||||
HostnameSource hostname_source;
|
||||
|
||||
struct stat etc_hostname_stat;
|
||||
struct stat etc_os_release_stat;
|
||||
struct stat etc_machine_info_stat;
|
||||
@ -66,11 +71,9 @@ typedef struct Context {
|
||||
} Context;
|
||||
|
||||
static void context_reset(Context *c, uint64_t mask) {
|
||||
int p;
|
||||
|
||||
assert(c);
|
||||
|
||||
for (p = 0; p < _PROP_MAX; p++) {
|
||||
for (int p = 0; p < _PROP_MAX; p++) {
|
||||
if (!FLAGS_SET(mask, UINT64_C(1) << p))
|
||||
continue;
|
||||
|
||||
@ -312,63 +315,63 @@ static char* context_fallback_icon_name(Context *c) {
|
||||
return strdup("computer");
|
||||
}
|
||||
|
||||
static bool hostname_is_useful(const char *hn) {
|
||||
return !isempty(hn) && !is_localhost(hn);
|
||||
}
|
||||
|
||||
static int context_update_kernel_hostname(
|
||||
Context *c,
|
||||
const char *transient_hn) {
|
||||
|
||||
const char *static_hn, *hn;
|
||||
struct utsname u;
|
||||
const char *hn;
|
||||
HostnameSource hns;
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
|
||||
if (!transient_hn) {
|
||||
/* If no transient hostname is passed in, then let's check what is currently set. */
|
||||
assert_se(uname(&u) >= 0);
|
||||
transient_hn =
|
||||
isempty(u.nodename) || streq(u.nodename, "(none)") ? NULL : u.nodename;
|
||||
}
|
||||
|
||||
static_hn = c->data[PROP_STATIC_HOSTNAME];
|
||||
|
||||
/* /etc/hostname with something other than "localhost"
|
||||
* has the highest preference ... */
|
||||
if (hostname_is_useful(static_hn))
|
||||
hn = static_hn;
|
||||
/* /etc/hostname has the highest preference ... */
|
||||
if (c->data[PROP_STATIC_HOSTNAME]) {
|
||||
hn = c->data[PROP_STATIC_HOSTNAME];
|
||||
hns = HOSTNAME_STATIC;
|
||||
|
||||
/* ... the transient hostname, (ie: DHCP) comes next ... */
|
||||
else if (!isempty(transient_hn))
|
||||
} else if (transient_hn) {
|
||||
hn = transient_hn;
|
||||
|
||||
/* ... fallback to static "localhost.*" ignored above ... */
|
||||
else if (!isempty(static_hn))
|
||||
hn = static_hn;
|
||||
hns = HOSTNAME_TRANSIENT;
|
||||
|
||||
/* ... and the ultimate fallback */
|
||||
else
|
||||
} else {
|
||||
hn = FALLBACK_HOSTNAME;
|
||||
hns = HOSTNAME_FALLBACK;
|
||||
}
|
||||
|
||||
if (sethostname_idempotent(hn) < 0)
|
||||
return -errno;
|
||||
r = sethostname_idempotent(hn);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set hostname: %m");
|
||||
|
||||
if (c->hostname_source != hns) {
|
||||
c->hostname_source = hns;
|
||||
r = 1;
|
||||
}
|
||||
|
||||
(void) nscd_flush_cache(STRV_MAKE("hosts"));
|
||||
|
||||
return 0;
|
||||
if (r == 0)
|
||||
log_debug("Hostname was already set to <%s>.", hn);
|
||||
else {
|
||||
log_info("Hostname set to <%s> (%s)", hn, hostname_source_to_string(hns));
|
||||
|
||||
hostname_update_source_hint(hn, hns);
|
||||
}
|
||||
|
||||
return r; /* 0 if no change, 1 if something was done */
|
||||
}
|
||||
|
||||
static int context_write_data_static_hostname(Context *c) {
|
||||
assert(c);
|
||||
|
||||
if (isempty(c->data[PROP_STATIC_HOSTNAME])) {
|
||||
|
||||
if (unlink("/etc/hostname") < 0)
|
||||
return errno == ENOENT ? 0 : -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return write_string_file_atomic_label("/etc/hostname", c->data[PROP_STATIC_HOSTNAME]);
|
||||
}
|
||||
|
||||
@ -383,7 +386,7 @@ static int context_write_data_machine_info(Context *c) {
|
||||
};
|
||||
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
int r, p;
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
|
||||
@ -391,7 +394,7 @@ static int context_write_data_machine_info(Context *c) {
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return r;
|
||||
|
||||
for (p = PROP_PRETTY_HOSTNAME; p <= PROP_LOCATION; p++) {
|
||||
for (int p = PROP_PRETTY_HOSTNAME; p <= PROP_LOCATION; p++) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
char **u;
|
||||
|
||||
@ -461,6 +464,52 @@ static int property_get_static_hostname(
|
||||
return sd_bus_message_append(reply, "s", c->data[PROP_STATIC_HOSTNAME]);
|
||||
}
|
||||
|
||||
static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_fallback_hostname, "s", FALLBACK_HOSTNAME);
|
||||
|
||||
static int property_get_hostname_source(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
Context *c = userdata;
|
||||
int r;
|
||||
assert(c);
|
||||
|
||||
context_read_etc_hostname(c);
|
||||
|
||||
if (c->hostname_source < 0) {
|
||||
char hostname[HOST_NAME_MAX + 1] = {};
|
||||
_cleanup_free_ char *fallback = NULL;
|
||||
|
||||
(void) get_hostname_filtered(hostname);
|
||||
|
||||
if (streq_ptr(hostname, c->data[PROP_STATIC_HOSTNAME]))
|
||||
c->hostname_source = HOSTNAME_STATIC;
|
||||
|
||||
else {
|
||||
/* If the hostname was not set by us, try to figure out where it came from. If we set
|
||||
* it to the fallback hostname, the file will tell us. We compare the string because
|
||||
* it is possible that the hostname was set by an older version that had a different
|
||||
* fallback, in the initramfs or before we reexecuted. */
|
||||
|
||||
r = read_one_line_file("/run/systemd/fallback-hostname", &fallback);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
log_warning_errno(r, "Failed to read /run/systemd/fallback-hostname, ignoring: %m");
|
||||
|
||||
if (streq_ptr(fallback, hostname))
|
||||
c->hostname_source = HOSTNAME_FALLBACK;
|
||||
else
|
||||
c->hostname_source = HOSTNAME_TRANSIENT;
|
||||
}
|
||||
}
|
||||
|
||||
return sd_bus_message_append(reply, "s", hostname_source_to_string(c->hostname_source));
|
||||
}
|
||||
|
||||
static int property_get_machine_info_field(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
@ -579,7 +628,6 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *
|
||||
Context *c = userdata;
|
||||
const char *name;
|
||||
int interactive, r;
|
||||
struct utsname u;
|
||||
|
||||
assert(m);
|
||||
assert(c);
|
||||
@ -588,20 +636,15 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
context_read_etc_hostname(c);
|
||||
name = empty_to_null(name);
|
||||
|
||||
if (isempty(name))
|
||||
name = c->data[PROP_STATIC_HOSTNAME];
|
||||
|
||||
if (isempty(name))
|
||||
name = FALLBACK_HOSTNAME;
|
||||
/* We always go through with the procedure below without comparing to the current hostname, because
|
||||
* we might want to adjust hostname source information even if the actual hostname is unchanged. */
|
||||
|
||||
if (!hostname_is_valid(name, 0))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name);
|
||||
|
||||
assert_se(uname(&u) >= 0);
|
||||
if (streq_ptr(name, u.nodename))
|
||||
return sd_bus_reply_method_return(m, NULL);
|
||||
context_read_etc_hostname(c);
|
||||
|
||||
r = bus_verify_polkit_async(
|
||||
m,
|
||||
@ -618,14 +661,12 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *
|
||||
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
|
||||
|
||||
r = context_update_kernel_hostname(c, name);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to set hostname: %m");
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
|
||||
}
|
||||
|
||||
log_info("Changed hostname to '%s'", name);
|
||||
|
||||
(void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "Hostname", NULL);
|
||||
else if (r > 0)
|
||||
(void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
|
||||
"/org/freedesktop/hostname1", "org.freedesktop.hostname1",
|
||||
"Hostname", "HostnameSource", NULL);
|
||||
|
||||
return sd_bus_reply_method_return(m, NULL);
|
||||
}
|
||||
@ -650,7 +691,7 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_
|
||||
if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME]))
|
||||
return sd_bus_reply_method_return(m, NULL);
|
||||
|
||||
if (!isempty(name) && !hostname_is_valid(name, 0))
|
||||
if (name && !hostname_is_valid(name, 0))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid static hostname '%s'", name);
|
||||
|
||||
r = bus_verify_polkit_async(
|
||||
@ -671,21 +712,21 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = context_update_kernel_hostname(c, NULL);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to set hostname: %m");
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
|
||||
}
|
||||
|
||||
r = context_write_data_static_hostname(c);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to write static hostname: %m");
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %m");
|
||||
}
|
||||
|
||||
log_info("Changed static hostname to '%s'", strna(c->data[PROP_STATIC_HOSTNAME]));
|
||||
r = context_update_kernel_hostname(c, NULL);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to set hostname: %m");
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
|
||||
}
|
||||
|
||||
(void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "StaticHostname", NULL);
|
||||
(void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m),
|
||||
"/org/freedesktop/hostname1", "org.freedesktop.hostname1",
|
||||
"StaticHostname", "Hostname", "HostnameSource", NULL);
|
||||
|
||||
return sd_bus_reply_method_return(m, NULL);
|
||||
}
|
||||
@ -850,6 +891,8 @@ static const sd_bus_vtable hostname_vtable[] = {
|
||||
SD_BUS_PROPERTY("Hostname", "s", property_get_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("StaticHostname", "s", property_get_static_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("PrettyHostname", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_PRETTY_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("FallbackHostname", "s", property_get_fallback_hostname, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("HostnameSource", "s", property_get_hostname_source, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("IconName", "s", property_get_icon_name, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("Chassis", "s", property_get_chassis, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("Deployment", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_DEPLOYMENT, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
@ -960,7 +1003,9 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **ret) {
|
||||
}
|
||||
|
||||
static int run(int argc, char *argv[]) {
|
||||
_cleanup_(context_destroy) Context context = {};
|
||||
_cleanup_(context_destroy) Context context = {
|
||||
.hostname_source = _HOSTNAME_INVALID, /* appropriate value will be set later */
|
||||
};
|
||||
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
int r;
|
||||
|
||||
@ -1521,6 +1521,44 @@ int journal_file_find_data_object(
|
||||
ret, ret_offset);
|
||||
}
|
||||
|
||||
bool journal_field_valid(const char *p, size_t l, bool allow_protected) {
|
||||
const char *a;
|
||||
|
||||
/* We kinda enforce POSIX syntax recommendations for
|
||||
environment variables here, but make a couple of additional
|
||||
requirements.
|
||||
|
||||
http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */
|
||||
|
||||
if (l == (size_t) -1)
|
||||
l = strlen(p);
|
||||
|
||||
/* No empty field names */
|
||||
if (l <= 0)
|
||||
return false;
|
||||
|
||||
/* Don't allow names longer than 64 chars */
|
||||
if (l > 64)
|
||||
return false;
|
||||
|
||||
/* Variables starting with an underscore are protected */
|
||||
if (!allow_protected && p[0] == '_')
|
||||
return false;
|
||||
|
||||
/* Don't allow digits as first character */
|
||||
if (p[0] >= '0' && p[0] <= '9')
|
||||
return false;
|
||||
|
||||
/* Only allow A-Z0-9 and '_' */
|
||||
for (a = p; a < p + l; a++)
|
||||
if ((*a < 'A' || *a > 'Z') &&
|
||||
(*a < '0' || *a > '9') &&
|
||||
*a != '_')
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int journal_file_append_field(
|
||||
JournalFile *f,
|
||||
const void *field, uint64_t size,
|
||||
@ -1534,6 +1572,9 @@ static int journal_file_append_field(
|
||||
assert(f);
|
||||
assert(field && size > 0);
|
||||
|
||||
if (!journal_field_valid(field, size, true))
|
||||
return -EBADMSG;
|
||||
|
||||
hash = journal_file_hash_data(f, field, size);
|
||||
|
||||
r = journal_file_find_field_object_with_hash(f, field, size, hash, &o, &p);
|
||||
@ -3711,7 +3752,7 @@ int journal_file_dispose(int dir_fd, const char *fname) {
|
||||
|
||||
assert(fname);
|
||||
|
||||
/* Renames a journal file to *.journal~, i.e. to mark it as corruped or otherwise uncleanly shutdown. Note that
|
||||
/* Renames a journal file to *.journal~, i.e. to mark it as corrupted or otherwise uncleanly shutdown. Note that
|
||||
* this is done without looking into the file or changing any of its contents. The idea is that this is called
|
||||
* whenever something is suspicious and we want to move the file away and make clear that it is not accessed
|
||||
* for writing anymore. */
|
||||
|
||||
@ -271,3 +271,5 @@ static inline bool JOURNAL_FILE_COMPRESS(JournalFile *f) {
|
||||
}
|
||||
|
||||
uint64_t journal_file_hash_data(JournalFile *f, const void *data, size_t sz);
|
||||
|
||||
bool journal_field_valid(const char *p, size_t l, bool allow_protected);
|
||||
|
||||
@ -41,7 +41,9 @@ static void draw_progress(uint64_t p, usec_t *last_usec) {
|
||||
|
||||
fputs("\r", stdout);
|
||||
if (colors_enabled())
|
||||
fputs("\x1B[?25l" ANSI_HIGHLIGHT_GREEN, stdout);
|
||||
fputs("\x1B[?25l", stdout);
|
||||
|
||||
fputs(ansi_highlight_green(), stdout);
|
||||
|
||||
for (i = 0; i < j; i++)
|
||||
fputs("\xe2\x96\x88", stdout);
|
||||
|
||||
@ -759,7 +759,7 @@ static usec_t client_compute_request_timeout(usec_t now, uint64_t attempt) {
|
||||
* Note that while the default T1/T2 initial times do have random 'fuzz' applied,
|
||||
* the RFC sec 4.4.5 does not mention adding any fuzz to retries. */
|
||||
static usec_t client_compute_reacquisition_timeout(usec_t now, usec_t expire) {
|
||||
return MAX(usec_sub_unsigned(expire, now) / 2, 60 * USEC_PER_SEC);
|
||||
return now + MAX(usec_sub_unsigned(expire, now) / 2, 60 * USEC_PER_SEC);
|
||||
}
|
||||
|
||||
static int cmp_uint8(const uint8_t *a, const uint8_t *b) {
|
||||
|
||||
@ -81,6 +81,7 @@ libsystemd_sources = files('''
|
||||
sd-netlink/netlink-types.h
|
||||
sd-netlink/netlink-util.c
|
||||
sd-netlink/netlink-util.h
|
||||
sd-netlink/nfnl-message.c
|
||||
sd-netlink/rtnl-message.c
|
||||
sd-netlink/sd-netlink.c
|
||||
sd-network/network-util.c
|
||||
|
||||
@ -112,68 +112,74 @@ static void test_login(void) {
|
||||
|
||||
if (session) {
|
||||
r = sd_session_is_active(session);
|
||||
assert_se(r >= 0);
|
||||
log_info("sd_session_is_active(\"%s\") → %s", session, yes_no(r));
|
||||
if (r == -ENXIO)
|
||||
log_notice("sd_session_is_active() failed with ENXIO, it seems logind is not running.");
|
||||
else {
|
||||
/* All those tests will fail with ENXIO, so let's skip them. */
|
||||
|
||||
r = sd_session_is_remote(session);
|
||||
assert_se(r >= 0);
|
||||
log_info("sd_session_is_remote(\"%s\") → %s", session, yes_no(r));
|
||||
assert_se(r >= 0);
|
||||
log_info("sd_session_is_active(\"%s\") → %s", session, yes_no(r));
|
||||
|
||||
r = sd_session_get_state(session, &state);
|
||||
assert_se(r == 0);
|
||||
log_info("sd_session_get_state(\"%s\") → \"%s\"", session, state);
|
||||
r = sd_session_is_remote(session);
|
||||
assert_se(r >= 0);
|
||||
log_info("sd_session_is_remote(\"%s\") → %s", session, yes_no(r));
|
||||
|
||||
assert_se(sd_session_get_uid(session, &u) >= 0);
|
||||
log_info("sd_session_get_uid(\"%s\") → "UID_FMT, session, u);
|
||||
assert_se(u == u2);
|
||||
r = sd_session_get_state(session, &state);
|
||||
assert_se(r == 0);
|
||||
log_info("sd_session_get_state(\"%s\") → \"%s\"", session, state);
|
||||
|
||||
assert_se(sd_session_get_type(session, &type) >= 0);
|
||||
log_info("sd_session_get_type(\"%s\") → \"%s\"", session, type);
|
||||
assert_se(sd_session_get_uid(session, &u) >= 0);
|
||||
log_info("sd_session_get_uid(\"%s\") → "UID_FMT, session, u);
|
||||
assert_se(u == u2);
|
||||
|
||||
assert_se(sd_session_get_class(session, &class) >= 0);
|
||||
log_info("sd_session_get_class(\"%s\") → \"%s\"", session, class);
|
||||
assert_se(sd_session_get_type(session, &type) >= 0);
|
||||
log_info("sd_session_get_type(\"%s\") → \"%s\"", session, type);
|
||||
|
||||
r = sd_session_get_display(session, &display);
|
||||
assert_se(IN_SET(r, 0, -ENODATA));
|
||||
log_info("sd_session_get_display(\"%s\") → \"%s\"", session, strna(display));
|
||||
assert_se(sd_session_get_class(session, &class) >= 0);
|
||||
log_info("sd_session_get_class(\"%s\") → \"%s\"", session, class);
|
||||
|
||||
r = sd_session_get_remote_user(session, &remote_user);
|
||||
assert_se(IN_SET(r, 0, -ENODATA));
|
||||
log_info("sd_session_get_remote_user(\"%s\") → \"%s\"",
|
||||
session, strna(remote_user));
|
||||
r = sd_session_get_display(session, &display);
|
||||
assert_se(IN_SET(r, 0, -ENODATA));
|
||||
log_info("sd_session_get_display(\"%s\") → \"%s\"", session, strna(display));
|
||||
|
||||
r = sd_session_get_remote_host(session, &remote_host);
|
||||
assert_se(IN_SET(r, 0, -ENODATA));
|
||||
log_info("sd_session_get_remote_host(\"%s\") → \"%s\"",
|
||||
session, strna(remote_host));
|
||||
r = sd_session_get_remote_user(session, &remote_user);
|
||||
assert_se(IN_SET(r, 0, -ENODATA));
|
||||
log_info("sd_session_get_remote_user(\"%s\") → \"%s\"",
|
||||
session, strna(remote_user));
|
||||
|
||||
r = sd_session_get_seat(session, &seat);
|
||||
if (r >= 0) {
|
||||
assert_se(seat);
|
||||
r = sd_session_get_remote_host(session, &remote_host);
|
||||
assert_se(IN_SET(r, 0, -ENODATA));
|
||||
log_info("sd_session_get_remote_host(\"%s\") → \"%s\"",
|
||||
session, strna(remote_host));
|
||||
|
||||
log_info("sd_session_get_seat(\"%s\") → \"%s\"", session, seat);
|
||||
r = sd_session_get_seat(session, &seat);
|
||||
if (r >= 0) {
|
||||
assert_se(seat);
|
||||
|
||||
log_info("sd_session_get_seat(\"%s\") → \"%s\"", session, seat);
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
r = sd_seat_can_multi_session(seat);
|
||||
r = sd_seat_can_multi_session(seat);
|
||||
#pragma GCC diagnostic pop
|
||||
assert_se(r == 1);
|
||||
log_info("sd_session_can_multi_seat(\"%s\") → %s", seat, yes_no(r));
|
||||
assert_se(r == 1);
|
||||
log_info("sd_session_can_multi_seat(\"%s\") → %s", seat, yes_no(r));
|
||||
|
||||
r = sd_seat_can_tty(seat);
|
||||
assert_se(r >= 0);
|
||||
log_info("sd_session_can_tty(\"%s\") → %s", seat, yes_no(r));
|
||||
r = sd_seat_can_tty(seat);
|
||||
assert_se(r >= 0);
|
||||
log_info("sd_session_can_tty(\"%s\") → %s", seat, yes_no(r));
|
||||
|
||||
r = sd_seat_can_graphical(seat);
|
||||
assert_se(r >= 0);
|
||||
log_info("sd_session_can_graphical(\"%s\") → %s", seat, yes_no(r));
|
||||
} else {
|
||||
log_info_errno(r, "sd_session_get_seat(\"%s\"): %m", session);
|
||||
assert_se(r == -ENODATA);
|
||||
r = sd_seat_can_graphical(seat);
|
||||
assert_se(r >= 0);
|
||||
log_info("sd_session_can_graphical(\"%s\") → %s", seat, yes_no(r));
|
||||
} else {
|
||||
log_info_errno(r, "sd_session_get_seat(\"%s\"): %m", session);
|
||||
assert_se(r == -ENODATA);
|
||||
}
|
||||
|
||||
assert_se(sd_uid_get_state(u, &state2) == 0);
|
||||
log_info("sd_uid_get_state("UID_FMT", …) → %s", u, state2);
|
||||
}
|
||||
|
||||
assert_se(sd_uid_get_state(u, &state2) == 0);
|
||||
log_info("sd_uid_get_state("UID_FMT", …) → %s", u, state2);
|
||||
}
|
||||
|
||||
if (seat) {
|
||||
@ -214,7 +220,7 @@ static void test_login(void) {
|
||||
assert_se(sd_get_seats(NULL) == r);
|
||||
|
||||
r = sd_seat_get_active(NULL, &t, NULL);
|
||||
assert_se(IN_SET(r, 0, -ENODATA));
|
||||
assert_se(IN_SET(r, 0, -ENODATA, -ENXIO));
|
||||
log_info("sd_seat_get_active(NULL, …) (active session on current seat) → %s / \"%s\"", e(r), strnull(t));
|
||||
free(t);
|
||||
|
||||
|
||||
@ -139,6 +139,7 @@ int socket_bind(sd_netlink *nl);
|
||||
int socket_broadcast_group_ref(sd_netlink *nl, unsigned group);
|
||||
int socket_broadcast_group_unref(sd_netlink *nl, unsigned group);
|
||||
int socket_write_message(sd_netlink *nl, sd_netlink_message *m);
|
||||
int socket_writev_message(sd_netlink *nl, sd_netlink_message *m[], size_t msgcount);
|
||||
int socket_read_message(sd_netlink *nl);
|
||||
|
||||
int rtnl_rqueue_make_room(sd_netlink *rtnl);
|
||||
|
||||
@ -238,6 +238,31 @@ int socket_write_message(sd_netlink *nl, sd_netlink_message *m) {
|
||||
return k;
|
||||
}
|
||||
|
||||
int socket_writev_message(sd_netlink *nl, sd_netlink_message *m[], size_t msgcount) {
|
||||
_cleanup_free_ struct iovec *iovs = NULL;
|
||||
ssize_t k;
|
||||
size_t i;
|
||||
|
||||
assert(nl);
|
||||
assert(msgcount);
|
||||
|
||||
iovs = new0(struct iovec, msgcount);
|
||||
if (!iovs)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < msgcount; i++) {
|
||||
assert(m[i]->hdr != NULL);
|
||||
assert(m[i]->hdr->nlmsg_len > 0);
|
||||
iovs[i] = IOVEC_MAKE(m[i]->hdr, m[i]->hdr->nlmsg_len);
|
||||
}
|
||||
|
||||
k = writev(nl->fd, iovs, msgcount);
|
||||
if (k < 0)
|
||||
return -errno;
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
static int socket_recv_message(int fd, struct iovec *iov, uint32_t *ret_mcast_group, bool peek) {
|
||||
union sockaddr_union sender;
|
||||
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct nl_pktinfo))) control;
|
||||
|
||||
@ -19,6 +19,8 @@
|
||||
#include <linux/if_macsec.h>
|
||||
#include <linux/if_tunnel.h>
|
||||
#include <linux/l2tp.h>
|
||||
#include <linux/netfilter/nf_tables.h>
|
||||
#include <linux/netfilter/nfnetlink.h>
|
||||
#include <linux/nexthop.h>
|
||||
#include <linux/nl80211.h>
|
||||
#include <linux/pkt_sched.h>
|
||||
@ -1312,6 +1314,243 @@ static const NLType genl_families[] = {
|
||||
[SD_GENL_NL80211] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_nl80211_cmds_type_system },
|
||||
};
|
||||
|
||||
static const NLType nfnl_nft_table_types[] = {
|
||||
[NFTA_TABLE_NAME] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
|
||||
[NFTA_TABLE_FLAGS] = { .type = NETLINK_TYPE_U32 },
|
||||
};
|
||||
|
||||
static const NLTypeSystem nfnl_nft_table_type_system = {
|
||||
.count = ELEMENTSOF(nfnl_nft_table_types),
|
||||
.types = nfnl_nft_table_types,
|
||||
};
|
||||
|
||||
static const NLType nfnl_nft_chain_hook_types[] = {
|
||||
[NFTA_HOOK_HOOKNUM] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_HOOK_PRIORITY] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_HOOK_DEV] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
|
||||
};
|
||||
|
||||
static const NLTypeSystem nfnl_nft_chain_hook_type_system = {
|
||||
.count = ELEMENTSOF(nfnl_nft_chain_hook_types),
|
||||
.types = nfnl_nft_chain_hook_types,
|
||||
};
|
||||
|
||||
static const NLType nfnl_nft_chain_types[] = {
|
||||
[NFTA_CHAIN_TABLE] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
|
||||
[NFTA_CHAIN_NAME] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
|
||||
[NFTA_CHAIN_HOOK] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_chain_hook_type_system },
|
||||
[NFTA_CHAIN_TYPE] = { .type = NETLINK_TYPE_STRING, .size = 16 },
|
||||
[NFTA_CHAIN_FLAGS] = { .type = NETLINK_TYPE_U32 },
|
||||
};
|
||||
|
||||
static const NLTypeSystem nfnl_nft_chain_type_system = {
|
||||
.count = ELEMENTSOF(nfnl_nft_chain_types),
|
||||
.types = nfnl_nft_chain_types,
|
||||
};
|
||||
|
||||
static const NLType nfnl_nft_expr_meta_types[] = {
|
||||
[NFTA_META_DREG] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_META_KEY] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_META_SREG] = { .type = NETLINK_TYPE_U32 },
|
||||
};
|
||||
|
||||
static const NLType nfnl_nft_expr_payload_types[] = {
|
||||
[NFTA_PAYLOAD_DREG] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_PAYLOAD_BASE] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_PAYLOAD_OFFSET] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_PAYLOAD_LEN] = { .type = NETLINK_TYPE_U32 },
|
||||
};
|
||||
|
||||
static const NLType nfnl_nft_expr_nat_types[] = {
|
||||
[NFTA_NAT_TYPE] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_NAT_FAMILY] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_NAT_REG_ADDR_MIN] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_NAT_REG_ADDR_MAX] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_NAT_REG_PROTO_MIN] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_NAT_REG_PROTO_MAX] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_NAT_FLAGS] = { .type = NETLINK_TYPE_U32 },
|
||||
};
|
||||
|
||||
static const NLType nfnl_nft_data_types[] = {
|
||||
[NFTA_DATA_VALUE] = { .type = NETLINK_TYPE_BINARY },
|
||||
};
|
||||
|
||||
static const NLTypeSystem nfnl_nft_data_type_system = {
|
||||
.count = ELEMENTSOF(nfnl_nft_data_types),
|
||||
.types = nfnl_nft_data_types,
|
||||
};
|
||||
|
||||
static const NLType nfnl_nft_expr_bitwise_types[] = {
|
||||
[NFTA_BITWISE_SREG] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_BITWISE_DREG] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_BITWISE_LEN] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_BITWISE_MASK] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
|
||||
[NFTA_BITWISE_XOR] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
|
||||
};
|
||||
|
||||
static const NLType nfnl_nft_expr_cmp_types[] = {
|
||||
[NFTA_CMP_SREG] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_CMP_OP] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_CMP_DATA] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
|
||||
};
|
||||
|
||||
static const NLType nfnl_nft_expr_fib_types[] = {
|
||||
[NFTA_FIB_DREG] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_FIB_RESULT] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_FIB_FLAGS] = { .type = NETLINK_TYPE_U32 },
|
||||
};
|
||||
|
||||
static const NLType nfnl_nft_expr_lookup_types[] = {
|
||||
[NFTA_LOOKUP_SET] = { .type = NETLINK_TYPE_STRING },
|
||||
[NFTA_LOOKUP_SREG] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_LOOKUP_DREG] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_LOOKUP_FLAGS] = { .type = NETLINK_TYPE_U32 },
|
||||
};
|
||||
|
||||
static const NLType nfnl_nft_expr_masq_types[] = {
|
||||
[NFTA_MASQ_FLAGS] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_MASQ_REG_PROTO_MIN] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_MASQ_REG_PROTO_MAX] = { .type = NETLINK_TYPE_U32 },
|
||||
};
|
||||
|
||||
static const NLTypeSystem nfnl_expr_data_type_systems[] = {
|
||||
[NL_UNION_NFT_EXPR_DATA_BITWISE] = { .count = ELEMENTSOF(nfnl_nft_expr_bitwise_types),
|
||||
.types = nfnl_nft_expr_bitwise_types },
|
||||
[NL_UNION_NFT_EXPR_DATA_CMP] = { .count = ELEMENTSOF(nfnl_nft_expr_cmp_types),
|
||||
.types = nfnl_nft_expr_cmp_types },
|
||||
[NL_UNION_NFT_EXPR_DATA_FIB] = { .count = ELEMENTSOF(nfnl_nft_expr_fib_types),
|
||||
.types = nfnl_nft_expr_fib_types },
|
||||
[NL_UNION_NFT_EXPR_DATA_LOOKUP] = { .count = ELEMENTSOF(nfnl_nft_expr_lookup_types),
|
||||
.types = nfnl_nft_expr_lookup_types },
|
||||
[NL_UNION_NFT_EXPR_DATA_MASQ] = { .count = ELEMENTSOF(nfnl_nft_expr_masq_types),
|
||||
.types = nfnl_nft_expr_masq_types },
|
||||
[NL_UNION_NFT_EXPR_DATA_META] = { .count = ELEMENTSOF(nfnl_nft_expr_meta_types),
|
||||
.types = nfnl_nft_expr_meta_types },
|
||||
[NL_UNION_NFT_EXPR_DATA_NAT] = { .count = ELEMENTSOF(nfnl_nft_expr_nat_types),
|
||||
.types = nfnl_nft_expr_nat_types },
|
||||
[NL_UNION_NFT_EXPR_DATA_PAYLOAD] = { .count = ELEMENTSOF(nfnl_nft_expr_payload_types),
|
||||
.types = nfnl_nft_expr_payload_types },
|
||||
};
|
||||
|
||||
static const char* const nl_union_nft_expr_data_table[] = {
|
||||
[NL_UNION_NFT_EXPR_DATA_BITWISE] = "bitwise",
|
||||
[NL_UNION_NFT_EXPR_DATA_CMP] = "cmp",
|
||||
[NL_UNION_NFT_EXPR_DATA_LOOKUP] = "lookup",
|
||||
[NL_UNION_NFT_EXPR_DATA_META] = "meta",
|
||||
[NL_UNION_NFT_EXPR_DATA_FIB] = "fib",
|
||||
[NL_UNION_NFT_EXPR_DATA_MASQ] = "masq",
|
||||
[NL_UNION_NFT_EXPR_DATA_NAT] = "nat",
|
||||
[NL_UNION_NFT_EXPR_DATA_PAYLOAD] = "payload",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(nl_union_nft_expr_data, NLUnionNFTExprData);
|
||||
|
||||
static const NLTypeSystemUnion nfnl_nft_data_expr_type_system_union = {
|
||||
.num = _NL_UNION_NFT_EXPR_DATA_MAX,
|
||||
.lookup = nl_union_nft_expr_data_from_string,
|
||||
.type_systems = nfnl_expr_data_type_systems,
|
||||
.match_type = NL_MATCH_SIBLING,
|
||||
.match = NFTA_EXPR_NAME,
|
||||
};
|
||||
|
||||
static const NLType nfnl_nft_rule_expr_types[] = {
|
||||
[NFTA_EXPR_NAME] = { .type = NETLINK_TYPE_STRING, .size = 16 },
|
||||
[NFTA_EXPR_DATA] = { .type = NETLINK_TYPE_UNION,
|
||||
.type_system_union = &nfnl_nft_data_expr_type_system_union },
|
||||
};
|
||||
|
||||
static const NLTypeSystem nfnl_nft_rule_expr_type_system = {
|
||||
.count = ELEMENTSOF(nfnl_nft_rule_expr_types),
|
||||
.types = nfnl_nft_rule_expr_types,
|
||||
};
|
||||
|
||||
static const NLType nfnl_nft_rule_types[] = {
|
||||
[NFTA_RULE_TABLE] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
|
||||
[NFTA_RULE_CHAIN] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
|
||||
[NFTA_RULE_EXPRESSIONS] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_rule_expr_type_system }
|
||||
};
|
||||
|
||||
static const NLTypeSystem nfnl_nft_rule_type_system = {
|
||||
.count = ELEMENTSOF(nfnl_nft_rule_types),
|
||||
.types = nfnl_nft_rule_types,
|
||||
};
|
||||
|
||||
static const NLType nfnl_nft_set_types[] = {
|
||||
[NFTA_SET_TABLE] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
|
||||
[NFTA_SET_NAME] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
|
||||
[NFTA_SET_FLAGS] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_SET_KEY_TYPE] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_SET_KEY_LEN] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_SET_DATA_TYPE] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_SET_DATA_LEN] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_SET_POLICY] = { .type = NETLINK_TYPE_U32 },
|
||||
[NFTA_SET_ID] = { .type = NETLINK_TYPE_U32 },
|
||||
};
|
||||
|
||||
static const NLTypeSystem nfnl_nft_set_type_system = {
|
||||
.count = ELEMENTSOF(nfnl_nft_set_types),
|
||||
.types = nfnl_nft_set_types,
|
||||
};
|
||||
|
||||
static const NLType nfnl_nft_setelem_types[] = {
|
||||
[NFTA_SET_ELEM_KEY] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
|
||||
[NFTA_SET_ELEM_DATA] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_data_type_system },
|
||||
[NFTA_SET_ELEM_FLAGS] = { .type = NETLINK_TYPE_U32 },
|
||||
};
|
||||
|
||||
static const NLTypeSystem nfnl_nft_setelem_type_system = {
|
||||
.count = ELEMENTSOF(nfnl_nft_setelem_types),
|
||||
.types = nfnl_nft_setelem_types,
|
||||
};
|
||||
|
||||
static const NLType nfnl_nft_setelem_list_types[] = {
|
||||
[NFTA_SET_ELEM_LIST_TABLE] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
|
||||
[NFTA_SET_ELEM_LIST_SET] = { .type = NETLINK_TYPE_STRING, .size = NFT_TABLE_MAXNAMELEN - 1 },
|
||||
[NFTA_SET_ELEM_LIST_ELEMENTS] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_setelem_type_system },
|
||||
};
|
||||
|
||||
static const NLTypeSystem nfnl_nft_setelem_list_type_system = {
|
||||
.count = ELEMENTSOF(nfnl_nft_setelem_list_types),
|
||||
.types = nfnl_nft_setelem_list_types,
|
||||
};
|
||||
|
||||
static const NLType nfnl_nft_msg_types [] = {
|
||||
[NFT_MSG_DELTABLE] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_table_type_system, .size = sizeof(struct nfgenmsg) },
|
||||
[NFT_MSG_NEWTABLE] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_table_type_system, .size = sizeof(struct nfgenmsg) },
|
||||
[NFT_MSG_NEWCHAIN] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_chain_type_system, .size = sizeof(struct nfgenmsg) },
|
||||
[NFT_MSG_NEWRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_rule_type_system, .size = sizeof(struct nfgenmsg) },
|
||||
[NFT_MSG_NEWSET] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_set_type_system, .size = sizeof(struct nfgenmsg) },
|
||||
[NFT_MSG_NEWSETELEM] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_setelem_list_type_system, .size = sizeof(struct nfgenmsg) },
|
||||
[NFT_MSG_DELSETELEM] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_setelem_list_type_system, .size = sizeof(struct nfgenmsg) },
|
||||
};
|
||||
|
||||
static const NLTypeSystem nfnl_nft_msg_type_system = {
|
||||
.count = ELEMENTSOF(nfnl_nft_msg_types),
|
||||
.types = nfnl_nft_msg_types,
|
||||
};
|
||||
|
||||
static const NLType nfnl_msg_batch_types [] = {
|
||||
[NFNL_BATCH_GENID] = { .type = NETLINK_TYPE_U32 }
|
||||
};
|
||||
|
||||
static const NLTypeSystem nfnl_msg_batch_type_system = {
|
||||
.count = ELEMENTSOF(nfnl_msg_batch_types),
|
||||
.types = nfnl_msg_batch_types,
|
||||
};
|
||||
|
||||
static const NLType nfnl_types[] = {
|
||||
[NLMSG_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 },
|
||||
[NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &error_type_system, .size = sizeof(struct nlmsgerr) },
|
||||
[NFNL_MSG_BATCH_BEGIN] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_msg_batch_type_system, .size = sizeof(struct nfgenmsg) },
|
||||
[NFNL_MSG_BATCH_END] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_msg_batch_type_system, .size = sizeof(struct nfgenmsg) },
|
||||
[NFNL_SUBSYS_NFTABLES] = { .type = NETLINK_TYPE_NESTED, .type_system = &nfnl_nft_msg_type_system, .size = sizeof(struct nfgenmsg) },
|
||||
};
|
||||
|
||||
const NLTypeSystem nfnl_type_system_root = {
|
||||
.count = ELEMENTSOF(nfnl_types),
|
||||
.types = nfnl_types,
|
||||
};
|
||||
|
||||
/* Mainly used when sending message */
|
||||
const NLTypeSystem genl_family_type_system_root = {
|
||||
.count = ELEMENTSOF(genl_families),
|
||||
@ -1368,6 +1607,8 @@ const NLTypeSystem *type_system_get_root(int protocol) {
|
||||
switch (protocol) {
|
||||
case NETLINK_GENERIC:
|
||||
return &genl_type_system_root;
|
||||
case NETLINK_NETFILTER:
|
||||
return &nfnl_type_system_root;
|
||||
default: /* NETLINK_ROUTE: */
|
||||
return &rtnl_type_system_root;
|
||||
}
|
||||
@ -1378,9 +1619,12 @@ int type_system_root_get_type(sd_netlink *nl, const NLType **ret, uint16_t type)
|
||||
const NLType *nl_type;
|
||||
int r;
|
||||
|
||||
if (!nl || nl->protocol != NETLINK_GENERIC)
|
||||
if (!nl)
|
||||
return type_system_get_type(&rtnl_type_system_root, ret, type);
|
||||
|
||||
if (nl->protocol != NETLINK_GENERIC)
|
||||
return type_system_get_type(type_system_get_root(nl->protocol), ret, type);
|
||||
|
||||
r = nlmsg_type_to_genl_family(nl, type, &family);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -21,6 +21,7 @@ enum {
|
||||
NETLINK_TYPE_NESTED, /* NLA_NESTED */
|
||||
NETLINK_TYPE_UNION,
|
||||
NETLINK_TYPE_SOCKADDR,
|
||||
NETLINK_TYPE_BINARY,
|
||||
};
|
||||
|
||||
typedef enum NLMatchType {
|
||||
@ -117,3 +118,19 @@ typedef enum NLUnionTCAOptionData {
|
||||
|
||||
const char *nl_union_tca_option_data_to_string(NLUnionTCAOptionData p) _const_;
|
||||
NLUnionTCAOptionData nl_union_tca_option_data_from_string(const char *p) _pure_;
|
||||
|
||||
typedef enum NLUnionNFTExprData {
|
||||
NL_UNION_NFT_EXPR_DATA_BITWISE,
|
||||
NL_UNION_NFT_EXPR_DATA_CMP,
|
||||
NL_UNION_NFT_EXPR_DATA_FIB,
|
||||
NL_UNION_NFT_EXPR_DATA_LOOKUP,
|
||||
NL_UNION_NFT_EXPR_DATA_PAYLOAD,
|
||||
NL_UNION_NFT_EXPR_DATA_MASQ,
|
||||
NL_UNION_NFT_EXPR_DATA_META,
|
||||
NL_UNION_NFT_EXPR_DATA_NAT,
|
||||
_NL_UNION_NFT_EXPR_DATA_MAX,
|
||||
_NL_UNION_NFT_EXPR_DATA_INVALID = -1,
|
||||
} NLUnionNFTExprData;
|
||||
|
||||
const char *nl_union_nft_expr_data_to_string(NLUnionNFTExprData p) _const_;
|
||||
NLUnionNFTExprData nl_union_nft_expr_data_from_string(const char *p) _pure_;
|
||||
|
||||
318
src/libsystemd/sd-netlink/nfnl-message.c
Normal file
318
src/libsystemd/sd-netlink/nfnl-message.c
Normal file
@ -0,0 +1,318 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <linux/if_addrlabel.h>
|
||||
#include <linux/netfilter/nfnetlink.h>
|
||||
#include <linux/netfilter/nf_tables.h>
|
||||
#include <linux/nexthop.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sd-netlink.h"
|
||||
|
||||
#include "format-util.h"
|
||||
#include "netlink-internal.h"
|
||||
#include "netlink-types.h"
|
||||
#include "netlink-util.h"
|
||||
#include "socket-util.h"
|
||||
#include "util.h"
|
||||
|
||||
static int nft_message_new(sd_netlink *nfnl, sd_netlink_message **ret, int family, uint16_t type, uint16_t flags) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
struct nfgenmsg *nfh;
|
||||
const NLType *nl_type;
|
||||
size_t size;
|
||||
int r;
|
||||
|
||||
assert_return(nfnl, -EINVAL);
|
||||
|
||||
r = type_system_root_get_type(nfnl, &nl_type, NFNL_SUBSYS_NFTABLES);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (type_get_type(nl_type) != NETLINK_TYPE_NESTED)
|
||||
return -EINVAL;
|
||||
|
||||
r = message_new_empty(nfnl, &m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
size = NLMSG_SPACE(type_get_size(nl_type));
|
||||
|
||||
assert(size >= sizeof(struct nlmsghdr));
|
||||
m->hdr = malloc0(size);
|
||||
if (!m->hdr)
|
||||
return -ENOMEM;
|
||||
|
||||
m->hdr->nlmsg_flags = NLM_F_REQUEST | flags;
|
||||
|
||||
type_get_type_system(nl_type, &m->containers[0].type_system);
|
||||
|
||||
r = type_system_get_type_system(m->containers[0].type_system,
|
||||
&m->containers[0].type_system,
|
||||
type);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
m->hdr->nlmsg_len = size;
|
||||
m->hdr->nlmsg_type = NFNL_SUBSYS_NFTABLES << 8 | type;
|
||||
|
||||
nfh = NLMSG_DATA(m->hdr);
|
||||
nfh->nfgen_family = family;
|
||||
nfh->version = NFNETLINK_V0;
|
||||
nfh->res_id = nfnl->serial;
|
||||
|
||||
*ret = TAKE_PTR(m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_nfnl_message_batch(sd_netlink *nfnl, sd_netlink_message **ret, int v) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
struct nfgenmsg *nfh;
|
||||
int r;
|
||||
|
||||
r = message_new(nfnl, &m, v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
nfh = NLMSG_DATA(m->hdr);
|
||||
nfh->nfgen_family = AF_UNSPEC;
|
||||
nfh->version = NFNETLINK_V0;
|
||||
nfh->res_id = NFNL_SUBSYS_NFTABLES;
|
||||
|
||||
*ret = TAKE_PTR(m);
|
||||
return r;
|
||||
}
|
||||
|
||||
int sd_nfnl_message_batch_begin(sd_netlink *nfnl, sd_netlink_message **ret) {
|
||||
return sd_nfnl_message_batch(nfnl, ret, NFNL_MSG_BATCH_BEGIN);
|
||||
}
|
||||
|
||||
int sd_nfnl_message_batch_end(sd_netlink *nfnl, sd_netlink_message **ret) {
|
||||
return sd_nfnl_message_batch(nfnl, ret, NFNL_MSG_BATCH_END);
|
||||
}
|
||||
|
||||
int sd_nfnl_nft_message_new_basechain(sd_netlink *nfnl, sd_netlink_message **ret,
|
||||
int family,
|
||||
const char *table, const char *chain,
|
||||
const char *type,
|
||||
uint8_t hook, int prio) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
int r;
|
||||
|
||||
r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWCHAIN, NLM_F_CREATE | NLM_F_ACK);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_string(m, NFTA_CHAIN_TABLE, table);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_string(m, NFTA_CHAIN_NAME, chain);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_string(m, NFTA_CHAIN_TYPE, type);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_open_container(m, NFTA_CHAIN_HOOK);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_u32(m, NFTA_HOOK_HOOKNUM, htobe32(hook));
|
||||
if (r < 0)
|
||||
goto cancel;
|
||||
|
||||
r = sd_netlink_message_append_u32(m, NFTA_HOOK_PRIORITY, htobe32(prio));
|
||||
if (r < 0)
|
||||
goto cancel;
|
||||
|
||||
r = sd_netlink_message_close_container(m);
|
||||
if (r < 0)
|
||||
goto cancel;
|
||||
|
||||
*ret = TAKE_PTR(m);
|
||||
return 0;
|
||||
cancel:
|
||||
sd_netlink_message_cancel_array(m);
|
||||
return r;
|
||||
}
|
||||
|
||||
int sd_nfnl_nft_message_del_table(sd_netlink *nfnl, sd_netlink_message **ret,
|
||||
int family, const char *table) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
int r;
|
||||
|
||||
r = nft_message_new(nfnl, &m, family, NFT_MSG_DELTABLE, NLM_F_CREATE | NLM_F_ACK);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_string(m, NFTA_TABLE_NAME, table);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(m);
|
||||
return r;
|
||||
}
|
||||
|
||||
int sd_nfnl_nft_message_new_table(sd_netlink *nfnl, sd_netlink_message **ret,
|
||||
int family, const char *table, uint16_t flags) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
int r;
|
||||
|
||||
r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWTABLE, NLM_F_CREATE | flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_string(m, NFTA_TABLE_NAME, table);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(m);
|
||||
return r;
|
||||
}
|
||||
|
||||
int sd_nfnl_nft_message_new_rule(sd_netlink *nfnl, sd_netlink_message **ret,
|
||||
int family, const char *table, const char *chain) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
int r;
|
||||
|
||||
r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWRULE, NLM_F_CREATE | NLM_F_ACK);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_string(m, NFTA_RULE_TABLE, table);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_string(m, NFTA_RULE_CHAIN, chain);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(m);
|
||||
return r;
|
||||
}
|
||||
|
||||
int sd_nfnl_nft_message_new_set(sd_netlink *nfnl, sd_netlink_message **ret,
|
||||
int family, const char *table, const char *set_name,
|
||||
uint32_t set_id, uint32_t klen) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
int r;
|
||||
|
||||
r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWSET, NLM_F_CREATE | NLM_F_ACK);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_string(m, NFTA_SET_TABLE, table);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_string(m, NFTA_SET_NAME, set_name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_u32(m, NFTA_SET_ID, ++set_id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_u32(m, NFTA_SET_KEY_LEN, htobe32(klen));
|
||||
if (r < 0)
|
||||
return r;
|
||||
*ret = TAKE_PTR(m);
|
||||
return r;
|
||||
}
|
||||
|
||||
int sd_nfnl_nft_message_new_setelems_begin(sd_netlink *nfnl, sd_netlink_message **ret,
|
||||
int family, const char *table, const char *set_name) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
int r;
|
||||
|
||||
r = nft_message_new(nfnl, &m, family, NFT_MSG_NEWSETELEM, NLM_F_CREATE | NLM_F_ACK);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_string(m, NFTA_SET_ELEM_LIST_TABLE, table);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_string(m, NFTA_SET_ELEM_LIST_SET, set_name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
*ret = TAKE_PTR(m);
|
||||
return r;
|
||||
}
|
||||
|
||||
int sd_nfnl_nft_message_del_setelems_begin(sd_netlink *nfnl, sd_netlink_message **ret,
|
||||
int family, const char *table, const char *set_name) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
int r;
|
||||
|
||||
r = nft_message_new(nfnl, &m, family, NFT_MSG_DELSETELEM, NLM_F_ACK);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_string(m, NFTA_SET_ELEM_LIST_TABLE, table);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_string(m, NFTA_SET_ELEM_LIST_SET, set_name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_open_container(m, NFTA_SET_ELEM_LIST_ELEMENTS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
*ret = TAKE_PTR(m);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int sd_nfnl_add_data(sd_netlink_message *m, uint16_t attr, const void *data, uint32_t dlen) {
|
||||
int r = sd_netlink_message_open_container(m, attr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_data(m, NFTA_DATA_VALUE, data, dlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_netlink_message_close_container(m); /* attr */
|
||||
}
|
||||
|
||||
int sd_nfnl_nft_message_add_setelem(sd_netlink_message *m,
|
||||
uint32_t num,
|
||||
const void *key, uint32_t klen,
|
||||
const void *data, uint32_t dlen) {
|
||||
int r;
|
||||
|
||||
r = sd_netlink_message_open_array(m, num);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_nfnl_add_data(m, NFTA_SET_ELEM_KEY, key, klen);
|
||||
if (r < 0)
|
||||
goto cancel;
|
||||
|
||||
if (data) {
|
||||
r = sd_nfnl_add_data(m, NFTA_SET_ELEM_DATA, data, dlen);
|
||||
if (r < 0)
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
return r;
|
||||
cancel:
|
||||
sd_netlink_message_cancel_array(m);
|
||||
return r;
|
||||
}
|
||||
|
||||
int sd_nfnl_nft_message_add_setelem_end(sd_netlink_message *m) {
|
||||
return sd_netlink_message_close_container(m); /* NFTA_SET_ELEM_LIST_ELEMENTS */
|
||||
}
|
||||
|
||||
int sd_nfnl_socket_open(sd_netlink **ret) {
|
||||
return netlink_open_family(ret, NETLINK_NETFILTER);
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <linux/fib_rules.h>
|
||||
#include <linux/if_addrlabel.h>
|
||||
#include <linux/if_bridge.h>
|
||||
#include <linux/nexthop.h>
|
||||
@ -197,7 +198,7 @@ int sd_rtnl_message_route_get_scope(const sd_netlink_message *m, unsigned char *
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_rtnl_message_route_get_tos(const sd_netlink_message *m, unsigned char *tos) {
|
||||
int sd_rtnl_message_route_get_tos(const sd_netlink_message *m, uint8_t *tos) {
|
||||
struct rtmsg *rtm;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
@ -848,7 +849,7 @@ int sd_rtnl_message_addrlabel_get_prefixlen(const sd_netlink_message *m, unsigne
|
||||
}
|
||||
|
||||
int sd_rtnl_message_new_routing_policy_rule(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int ifal_family) {
|
||||
struct rtmsg *rtm;
|
||||
struct fib_rule_hdr *frh;
|
||||
int r;
|
||||
|
||||
assert_return(rtnl_message_type_is_routing_policy_rule(nlmsg_type), -EINVAL);
|
||||
@ -861,177 +862,175 @@ int sd_rtnl_message_new_routing_policy_rule(sd_netlink *rtnl, sd_netlink_message
|
||||
if (nlmsg_type == RTM_NEWRULE)
|
||||
(*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
|
||||
|
||||
rtm = NLMSG_DATA((*ret)->hdr);
|
||||
rtm->rtm_family = ifal_family;
|
||||
rtm->rtm_protocol = RTPROT_BOOT;
|
||||
rtm->rtm_scope = RT_SCOPE_UNIVERSE;
|
||||
rtm->rtm_type = RTN_UNICAST;
|
||||
frh = NLMSG_DATA((*ret)->hdr);
|
||||
frh->family = ifal_family;
|
||||
frh->action = FR_ACT_TO_TBL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_rtnl_message_routing_policy_rule_set_tos(sd_netlink_message *m, unsigned char tos) {
|
||||
struct rtmsg *routing_policy_rule;
|
||||
int sd_rtnl_message_routing_policy_rule_set_tos(sd_netlink_message *m, uint8_t tos) {
|
||||
struct fib_rule_hdr *frh;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
assert_return(m->hdr, -EINVAL);
|
||||
assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
|
||||
|
||||
routing_policy_rule = NLMSG_DATA(m->hdr);
|
||||
frh = NLMSG_DATA(m->hdr);
|
||||
|
||||
routing_policy_rule->rtm_tos = tos;
|
||||
frh->tos = tos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_rtnl_message_routing_policy_rule_get_tos(const sd_netlink_message *m, unsigned char *tos) {
|
||||
struct rtmsg *routing_policy_rule;
|
||||
int sd_rtnl_message_routing_policy_rule_get_tos(const sd_netlink_message *m, uint8_t *tos) {
|
||||
struct fib_rule_hdr *frh;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
assert_return(m->hdr, -EINVAL);
|
||||
assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
|
||||
|
||||
routing_policy_rule = NLMSG_DATA(m->hdr);
|
||||
frh = NLMSG_DATA(m->hdr);
|
||||
|
||||
*tos = routing_policy_rule->rtm_tos;
|
||||
*tos = frh->tos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_rtnl_message_routing_policy_rule_set_table(sd_netlink_message *m, unsigned char table) {
|
||||
struct rtmsg *routing_policy_rule;
|
||||
int sd_rtnl_message_routing_policy_rule_set_table(sd_netlink_message *m, uint8_t table) {
|
||||
struct fib_rule_hdr *frh;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
assert_return(m->hdr, -EINVAL);
|
||||
assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
|
||||
|
||||
routing_policy_rule = NLMSG_DATA(m->hdr);
|
||||
frh = NLMSG_DATA(m->hdr);
|
||||
|
||||
routing_policy_rule->rtm_table = table;
|
||||
frh->table = table;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_rtnl_message_routing_policy_rule_get_table(const sd_netlink_message *m, unsigned char *table) {
|
||||
struct rtmsg *routing_policy_rule;
|
||||
int sd_rtnl_message_routing_policy_rule_get_table(const sd_netlink_message *m, uint8_t *table) {
|
||||
struct fib_rule_hdr *frh;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
assert_return(m->hdr, -EINVAL);
|
||||
assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
|
||||
|
||||
routing_policy_rule = NLMSG_DATA(m->hdr);
|
||||
frh = NLMSG_DATA(m->hdr);
|
||||
|
||||
*table = routing_policy_rule->rtm_table;
|
||||
*table = frh->table;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_rtnl_message_routing_policy_rule_set_flags(sd_netlink_message *m, unsigned flags) {
|
||||
struct rtmsg *routing_policy_rule;
|
||||
int sd_rtnl_message_routing_policy_rule_set_flags(sd_netlink_message *m, uint32_t flags) {
|
||||
struct fib_rule_hdr *frh;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
assert_return(m->hdr, -EINVAL);
|
||||
assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
|
||||
|
||||
routing_policy_rule = NLMSG_DATA(m->hdr);
|
||||
routing_policy_rule->rtm_flags |= flags;
|
||||
frh = NLMSG_DATA(m->hdr);
|
||||
frh->flags |= flags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_rtnl_message_routing_policy_rule_get_flags(const sd_netlink_message *m, unsigned *flags) {
|
||||
struct rtmsg *routing_policy_rule;
|
||||
int sd_rtnl_message_routing_policy_rule_get_flags(const sd_netlink_message *m, uint32_t *flags) {
|
||||
struct fib_rule_hdr *frh;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
assert_return(m->hdr, -EINVAL);
|
||||
assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
|
||||
|
||||
routing_policy_rule = NLMSG_DATA(m->hdr);
|
||||
*flags = routing_policy_rule->rtm_flags;
|
||||
frh = NLMSG_DATA(m->hdr);
|
||||
*flags = frh->flags;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_rtnl_message_routing_policy_rule_set_rtm_type(sd_netlink_message *m, unsigned char type) {
|
||||
struct rtmsg *routing_policy_rule;
|
||||
int sd_rtnl_message_routing_policy_rule_set_fib_type(sd_netlink_message *m, uint8_t type) {
|
||||
struct fib_rule_hdr *frh;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
assert_return(m->hdr, -EINVAL);
|
||||
assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
|
||||
|
||||
routing_policy_rule = NLMSG_DATA(m->hdr);
|
||||
frh = NLMSG_DATA(m->hdr);
|
||||
|
||||
routing_policy_rule->rtm_type = type;
|
||||
frh->action = type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_rtnl_message_routing_policy_rule_get_rtm_type(const sd_netlink_message *m, unsigned char *type) {
|
||||
struct rtmsg *routing_policy_rule;
|
||||
int sd_rtnl_message_routing_policy_rule_get_fib_type(const sd_netlink_message *m, uint8_t *type) {
|
||||
struct fib_rule_hdr *frh;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
assert_return(m->hdr, -EINVAL);
|
||||
assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
|
||||
|
||||
routing_policy_rule = NLMSG_DATA(m->hdr);
|
||||
frh = NLMSG_DATA(m->hdr);
|
||||
|
||||
*type = routing_policy_rule->rtm_type;
|
||||
*type = frh->action;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(sd_netlink_message *m, unsigned char len) {
|
||||
struct rtmsg *routing_policy_rule;
|
||||
int sd_rtnl_message_routing_policy_rule_set_fib_dst_prefixlen(sd_netlink_message *m, uint8_t len) {
|
||||
struct fib_rule_hdr *frh;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
assert_return(m->hdr, -EINVAL);
|
||||
assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
|
||||
|
||||
routing_policy_rule = NLMSG_DATA(m->hdr);
|
||||
frh = NLMSG_DATA(m->hdr);
|
||||
|
||||
routing_policy_rule->rtm_dst_len = len;
|
||||
frh->dst_len = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(const sd_netlink_message *m, unsigned char *len) {
|
||||
struct rtmsg *routing_policy_rule;
|
||||
int sd_rtnl_message_routing_policy_rule_get_fib_dst_prefixlen(const sd_netlink_message *m, uint8_t *len) {
|
||||
struct fib_rule_hdr *frh;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
assert_return(m->hdr, -EINVAL);
|
||||
assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
|
||||
|
||||
routing_policy_rule = NLMSG_DATA(m->hdr);
|
||||
frh = NLMSG_DATA(m->hdr);
|
||||
|
||||
*len = routing_policy_rule->rtm_dst_len;
|
||||
*len = frh->dst_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_rtnl_message_routing_policy_rule_set_rtm_src_prefixlen(sd_netlink_message *m, unsigned char len) {
|
||||
struct rtmsg *routing_policy_rule;
|
||||
int sd_rtnl_message_routing_policy_rule_set_fib_src_prefixlen(sd_netlink_message *m, uint8_t len) {
|
||||
struct fib_rule_hdr *frh;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
assert_return(m->hdr, -EINVAL);
|
||||
assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
|
||||
|
||||
routing_policy_rule = NLMSG_DATA(m->hdr);
|
||||
frh = NLMSG_DATA(m->hdr);
|
||||
|
||||
routing_policy_rule->rtm_src_len = len;
|
||||
frh->src_len = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(const sd_netlink_message *m, unsigned char *len) {
|
||||
struct rtmsg *routing_policy_rule;
|
||||
int sd_rtnl_message_routing_policy_rule_get_fib_src_prefixlen(const sd_netlink_message *m, uint8_t *len) {
|
||||
struct fib_rule_hdr *frh;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
assert_return(m->hdr, -EINVAL);
|
||||
assert_return(rtnl_message_type_is_routing_policy_rule(m->hdr->nlmsg_type), -EINVAL);
|
||||
|
||||
routing_policy_rule = NLMSG_DATA(m->hdr);
|
||||
frh = NLMSG_DATA(m->hdr);
|
||||
|
||||
*len = routing_policy_rule->rtm_src_len;
|
||||
*len = frh->src_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -226,6 +226,41 @@ int sd_netlink_send(sd_netlink *nl,
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sd_netlink_sendv(sd_netlink *nl,
|
||||
sd_netlink_message *messages[],
|
||||
size_t msgcount,
|
||||
uint32_t **ret_serial) {
|
||||
_cleanup_free_ uint32_t *serials = NULL;
|
||||
unsigned i;
|
||||
int r;
|
||||
|
||||
assert_return(nl, -EINVAL);
|
||||
assert_return(!rtnl_pid_changed(nl), -ECHILD);
|
||||
assert_return(messages, -EINVAL);
|
||||
|
||||
if (ret_serial) {
|
||||
serials = new0(uint32_t, msgcount);
|
||||
if (!serials)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < msgcount; i++) {
|
||||
assert_return(!messages[i]->sealed, -EPERM);
|
||||
rtnl_seal_message(nl, messages[i]);
|
||||
if (serials)
|
||||
serials[i] = rtnl_message_get_serial(messages[i]);
|
||||
}
|
||||
|
||||
r = socket_writev_message(nl, messages, msgcount);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_serial)
|
||||
*ret_serial = TAKE_PTR(serials);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int rtnl_rqueue_make_room(sd_netlink *rtnl) {
|
||||
assert(rtnl);
|
||||
|
||||
@ -586,21 +621,15 @@ int sd_netlink_call_async(
|
||||
return k;
|
||||
}
|
||||
|
||||
int sd_netlink_call(sd_netlink *rtnl,
|
||||
sd_netlink_message *message,
|
||||
uint64_t usec,
|
||||
sd_netlink_message **ret) {
|
||||
int sd_netlink_read(sd_netlink *rtnl,
|
||||
uint32_t serial,
|
||||
uint64_t usec,
|
||||
sd_netlink_message **ret) {
|
||||
usec_t timeout;
|
||||
uint32_t serial;
|
||||
int r;
|
||||
|
||||
assert_return(rtnl, -EINVAL);
|
||||
assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
|
||||
assert_return(message, -EINVAL);
|
||||
|
||||
r = sd_netlink_send(rtnl, message, &serial);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
timeout = calc_elapse(usec);
|
||||
|
||||
@ -670,6 +699,24 @@ int sd_netlink_call(sd_netlink *rtnl,
|
||||
}
|
||||
}
|
||||
|
||||
int sd_netlink_call(sd_netlink *rtnl,
|
||||
sd_netlink_message *message,
|
||||
uint64_t usec,
|
||||
sd_netlink_message **ret) {
|
||||
uint32_t serial;
|
||||
int r;
|
||||
|
||||
assert_return(rtnl, -EINVAL);
|
||||
assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
|
||||
assert_return(message, -EINVAL);
|
||||
|
||||
r = sd_netlink_send(rtnl, message, &serial);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_netlink_read(rtnl, serial, usec, ret);
|
||||
}
|
||||
|
||||
int sd_netlink_get_events(const sd_netlink *rtnl) {
|
||||
assert_return(rtnl, -EINVAL);
|
||||
assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
|
||||
|
||||
@ -282,7 +282,7 @@ static int address_set_masquerade(Address *address, bool add) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = fw_add_masquerade(add, AF_INET, &masked, address->prefixlen);
|
||||
r = fw_add_masquerade(&address->link->manager->fw_ctx, add, AF_INET, &masked, address->prefixlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include "escape.h"
|
||||
#include "alloc-util.h"
|
||||
#include "dhcp-client-internal.h"
|
||||
#include "hostname-setup.h"
|
||||
#include "hostname-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "network-internal.h"
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
#include "dns-domain.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "firewall-util.h"
|
||||
#include "local-addresses.h"
|
||||
#include "netlink-util.h"
|
||||
#include "network-internal.h"
|
||||
@ -912,6 +913,8 @@ void manager_free(Manager *m) {
|
||||
|
||||
safe_close(m->ethtool_fd);
|
||||
|
||||
m->fw_ctx = fw_ctx_free(m->fw_ctx);
|
||||
|
||||
free(m);
|
||||
}
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#include "sd-resolve.h"
|
||||
|
||||
#include "dhcp-identifier.h"
|
||||
#include "firewall-util.h"
|
||||
#include "hashmap.h"
|
||||
#include "networkd-link.h"
|
||||
#include "networkd-network.h"
|
||||
@ -74,6 +75,8 @@ struct Manager {
|
||||
|
||||
bool dhcp4_prefix_root_cannot_set_table:1;
|
||||
bool bridge_mdb_on_master_not_supported:1;
|
||||
|
||||
FirewallContext *fw_ctx;
|
||||
};
|
||||
|
||||
int manager_new(Manager **ret);
|
||||
|
||||
@ -820,7 +820,7 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
|
||||
|
||||
static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
|
||||
_cleanup_(route_freep) Route *route = NULL;
|
||||
struct in6_addr gateway;
|
||||
union in_addr_union gateway;
|
||||
uint32_t lifetime;
|
||||
unsigned preference, prefixlen;
|
||||
usec_t time_now;
|
||||
@ -835,10 +835,20 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
|
||||
if (lifetime == 0)
|
||||
return 0;
|
||||
|
||||
r = sd_ndisc_router_get_address(rt, &gateway);
|
||||
r = sd_ndisc_router_get_address(rt, &gateway.in6);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m");
|
||||
|
||||
if (link_has_ipv6_address(link, &gateway.in6) == 0) {
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
(void) in_addr_to_string(AF_INET6, &gateway, &buf);
|
||||
log_link_debug(link, "Advertised route gateway, %s, is local to the link, ignoring route", strnull(buf));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Failed to get route prefix length: %m");
|
||||
@ -860,7 +870,7 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
|
||||
route->priority = link->network->dhcp6_route_metric;
|
||||
route->protocol = RTPROT_RA;
|
||||
route->pref = preference;
|
||||
route->gw.in6 = gateway;
|
||||
route->gw.in6 = gateway.in6;
|
||||
route->gw_family = AF_INET6;
|
||||
route->dst_prefixlen = prefixlen;
|
||||
route->lifetime = time_now + lifetime * USEC_PER_SEC;
|
||||
|
||||
@ -380,7 +380,7 @@ static int routing_policy_rule_set_netlink_message(RoutingPolicyRule *rule, sd_n
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not append FRA_SRC attribute: %m");
|
||||
|
||||
r = sd_rtnl_message_routing_policy_rule_set_rtm_src_prefixlen(m, rule->from_prefixlen);
|
||||
r = sd_rtnl_message_routing_policy_rule_set_fib_src_prefixlen(m, rule->from_prefixlen);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not set source prefix length: %m");
|
||||
}
|
||||
@ -390,7 +390,7 @@ static int routing_policy_rule_set_netlink_message(RoutingPolicyRule *rule, sd_n
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not append FRA_DST attribute: %m");
|
||||
|
||||
r = sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(m, rule->to_prefixlen);
|
||||
r = sd_rtnl_message_routing_policy_rule_set_fib_dst_prefixlen(m, rule->to_prefixlen);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "Could not set destination prefix length: %m");
|
||||
}
|
||||
@ -749,7 +749,7 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Man
|
||||
log_warning_errno(r, "rtnl: could not get FRA_SRC attribute, ignoring: %m");
|
||||
return 0;
|
||||
} else if (r >= 0) {
|
||||
r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &tmp->from_prefixlen);
|
||||
r = sd_rtnl_message_routing_policy_rule_get_fib_src_prefixlen(message, &tmp->from_prefixlen);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "rtnl: received rule message without valid source prefix length, ignoring: %m");
|
||||
return 0;
|
||||
@ -761,7 +761,7 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Man
|
||||
log_warning_errno(r, "rtnl: could not get FRA_DST attribute, ignoring: %m");
|
||||
return 0;
|
||||
} else if (r >= 0) {
|
||||
r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &tmp->to_prefixlen);
|
||||
r = sd_rtnl_message_routing_policy_rule_get_fib_dst_prefixlen(message, &tmp->to_prefixlen);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "rtnl: received rule message without valid destination prefix length, ignoring: %m");
|
||||
return 0;
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
|
||||
#include "capability-util.h"
|
||||
#include "daemon-util.h"
|
||||
#include "firewall-util.h"
|
||||
#include "main-func.h"
|
||||
#include "mkdir.h"
|
||||
#include "networkd-conf.h"
|
||||
@ -92,6 +93,10 @@ static int run(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = fw_ctx_new(&m->fw_ctx);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Could not initialize firewall, IPMasquerade= option not available: %m");
|
||||
|
||||
r = manager_start(m);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not start manager: %m");
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
#include "alloc-util.h"
|
||||
#include "dhcp-lease-internal.h"
|
||||
#include "ether-addr-util.h"
|
||||
#include "hostname-util.h"
|
||||
#include "hostname-setup.h"
|
||||
#include "network-internal.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "string-util.h"
|
||||
|
||||
@ -82,7 +82,7 @@ void expose_port_free_all(ExposePort *p) {
|
||||
}
|
||||
}
|
||||
|
||||
int expose_port_flush(ExposePort* l, union in_addr_union *exposed) {
|
||||
int expose_port_flush(FirewallContext **fw_ctx, ExposePort* l, union in_addr_union *exposed) {
|
||||
ExposePort *p;
|
||||
int r, af = AF_INET;
|
||||
|
||||
@ -97,7 +97,8 @@ int expose_port_flush(ExposePort* l, union in_addr_union *exposed) {
|
||||
log_debug("Lost IP address.");
|
||||
|
||||
LIST_FOREACH(ports, p, l) {
|
||||
r = fw_add_local_dnat(false,
|
||||
r = fw_add_local_dnat(fw_ctx,
|
||||
false,
|
||||
af,
|
||||
p->protocol,
|
||||
p->host_port,
|
||||
@ -112,7 +113,7 @@ int expose_port_flush(ExposePort* l, union in_addr_union *exposed) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *exposed) {
|
||||
int expose_port_execute(sd_netlink *rtnl, FirewallContext **fw_ctx, ExposePort *l, union in_addr_union *exposed) {
|
||||
_cleanup_free_ struct local_address *addresses = NULL;
|
||||
union in_addr_union new_exposed;
|
||||
ExposePort *p;
|
||||
@ -136,7 +137,7 @@ int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *ex
|
||||
addresses[0].scope < RT_SCOPE_LINK;
|
||||
|
||||
if (!add)
|
||||
return expose_port_flush(l, exposed);
|
||||
return expose_port_flush(fw_ctx, l, exposed);
|
||||
|
||||
new_exposed = addresses[0].address;
|
||||
if (in_addr_equal(af, exposed, &new_exposed))
|
||||
@ -150,7 +151,8 @@ int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *ex
|
||||
|
||||
LIST_FOREACH(ports, p, l) {
|
||||
|
||||
r = fw_add_local_dnat(true,
|
||||
r = fw_add_local_dnat(fw_ctx,
|
||||
true,
|
||||
af,
|
||||
p->protocol,
|
||||
p->host_port,
|
||||
@ -188,7 +190,7 @@ int expose_port_watch_rtnl(
|
||||
sd_event *event,
|
||||
int recv_fd,
|
||||
sd_netlink_message_handler_t handler,
|
||||
union in_addr_union *exposed,
|
||||
void *userdata,
|
||||
sd_netlink **ret) {
|
||||
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
|
||||
int fd, r;
|
||||
@ -207,11 +209,11 @@ int expose_port_watch_rtnl(
|
||||
return log_error_errno(r, "Failed to create rtnl object: %m");
|
||||
}
|
||||
|
||||
r = sd_netlink_add_match(rtnl, NULL, RTM_NEWADDR, handler, NULL, exposed, "nspawn-NEWADDR");
|
||||
r = sd_netlink_add_match(rtnl, NULL, RTM_NEWADDR, handler, NULL, userdata, "nspawn-NEWADDR");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to subscribe to RTM_NEWADDR messages: %m");
|
||||
|
||||
r = sd_netlink_add_match(rtnl, NULL, RTM_DELADDR, handler, NULL, exposed, "nspawn-DELADDR");
|
||||
r = sd_netlink_add_match(rtnl, NULL, RTM_DELADDR, handler, NULL, userdata, "nspawn-DELADDR");
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to subscribe to RTM_DELADDR messages: %m");
|
||||
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "firewall-util.h"
|
||||
|
||||
#include "sd-event.h"
|
||||
#include "sd-netlink.h"
|
||||
|
||||
@ -19,8 +21,8 @@ typedef struct ExposePort {
|
||||
void expose_port_free_all(ExposePort *p);
|
||||
int expose_port_parse(ExposePort **l, const char *s);
|
||||
|
||||
int expose_port_watch_rtnl(sd_event *event, int recv_fd, sd_netlink_message_handler_t handler, union in_addr_union *exposed, sd_netlink **ret);
|
||||
int expose_port_watch_rtnl(sd_event *event, int recv_fd, sd_netlink_message_handler_t handler, void *userdata, sd_netlink **ret);
|
||||
int expose_port_send_rtnl(int send_fd);
|
||||
|
||||
int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *exposed);
|
||||
int expose_port_flush(ExposePort* l, union in_addr_union *exposed);
|
||||
int expose_port_execute(sd_netlink *rtnl, FirewallContext **fw_ctx, ExposePort *l, union in_addr_union *exposed);
|
||||
int expose_port_flush(FirewallContext **fw_ctx, ExposePort* l, union in_addr_union *exposed);
|
||||
|
||||
@ -46,6 +46,7 @@
|
||||
#include "fs-util.h"
|
||||
#include "gpt.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "hostname-setup.h"
|
||||
#include "hostname-util.h"
|
||||
#include "id128-util.h"
|
||||
#include "io-util.h"
|
||||
@ -1770,11 +1771,6 @@ static int verify_arguments(void) {
|
||||
if (arg_expose_ports && !arg_private_network)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot use --port= without private networking.");
|
||||
|
||||
#if ! HAVE_LIBIPTC
|
||||
if (arg_expose_ports)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--port= is not supported, compiled without libiptc support.");
|
||||
#endif
|
||||
|
||||
if (arg_caps_ambient) {
|
||||
if (arg_caps_ambient == (uint64_t)-1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "AmbientCapability= does not support the value all.");
|
||||
@ -2474,14 +2470,19 @@ static int setup_kmsg(int kmsg_socket) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ExposeArgs {
|
||||
union in_addr_union address;
|
||||
struct FirewallContext *fw_ctx;
|
||||
};
|
||||
|
||||
static int on_address_change(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
|
||||
union in_addr_union *exposed = userdata;
|
||||
struct ExposeArgs *args = userdata;
|
||||
|
||||
assert(rtnl);
|
||||
assert(m);
|
||||
assert(exposed);
|
||||
assert(args);
|
||||
|
||||
expose_port_execute(rtnl, arg_expose_ports, exposed);
|
||||
expose_port_execute(rtnl, &args->fw_ctx, arg_expose_ports, &args->address);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -4466,7 +4467,7 @@ static int run_container(
|
||||
bool secondary,
|
||||
FDSet *fds,
|
||||
char veth_name[IFNAMSIZ], bool *veth_created,
|
||||
union in_addr_union *exposed,
|
||||
struct ExposeArgs *expose_args,
|
||||
int *master, pid_t *pid, int *ret) {
|
||||
|
||||
static const struct sigaction sa = {
|
||||
@ -4895,11 +4896,11 @@ static int run_container(
|
||||
(void) sd_event_add_signal(event, NULL, SIGCHLD, on_sigchld, PID_TO_PTR(*pid));
|
||||
|
||||
if (arg_expose_ports) {
|
||||
r = expose_port_watch_rtnl(event, rtnl_socket_pair[0], on_address_change, exposed, &rtnl);
|
||||
r = expose_port_watch_rtnl(event, rtnl_socket_pair[0], on_address_change, expose_args, &rtnl);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) expose_port_execute(rtnl, arg_expose_ports, exposed);
|
||||
(void) expose_port_execute(rtnl, &expose_args->fw_ctx, arg_expose_ports, &expose_args->address);
|
||||
}
|
||||
|
||||
rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
|
||||
@ -5026,7 +5027,7 @@ static int run_container(
|
||||
return 0; /* finito */
|
||||
}
|
||||
|
||||
expose_port_flush(arg_expose_ports, exposed);
|
||||
expose_port_flush(&expose_args->fw_ctx, arg_expose_ports, &expose_args->address);
|
||||
|
||||
(void) remove_veth_links(veth_name, arg_network_veth_extra);
|
||||
*veth_created = false;
|
||||
@ -5155,12 +5156,13 @@ static int run(int argc, char *argv[]) {
|
||||
_cleanup_fdset_free_ FDSet *fds = NULL;
|
||||
int r, n_fd_passed, ret = EXIT_SUCCESS;
|
||||
char veth_name[IFNAMSIZ] = "";
|
||||
union in_addr_union exposed = {};
|
||||
struct ExposeArgs expose_args = {};
|
||||
_cleanup_(release_lock_file) LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT;
|
||||
char tmprootdir[] = "/tmp/nspawn-root-XXXXXX";
|
||||
_cleanup_(loop_device_unrefp) LoopDevice *loop = NULL;
|
||||
_cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
|
||||
_cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
|
||||
_cleanup_(fw_ctx_freep) FirewallContext *fw_ctx = NULL;
|
||||
pid_t pid = 0;
|
||||
|
||||
log_parse_environment();
|
||||
@ -5517,12 +5519,20 @@ static int run(int argc, char *argv[]) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (arg_expose_ports) {
|
||||
r = fw_ctx_new(&fw_ctx);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Cannot expose configured ports, firewall initialization failed: %m");
|
||||
goto finish;
|
||||
}
|
||||
expose_args.fw_ctx = fw_ctx;
|
||||
}
|
||||
for (;;) {
|
||||
r = run_container(dissected_image,
|
||||
secondary,
|
||||
fds,
|
||||
veth_name, &veth_created,
|
||||
&exposed, &master,
|
||||
&expose_args, &master,
|
||||
&pid, &ret);
|
||||
if (r <= 0)
|
||||
break;
|
||||
@ -5572,7 +5582,7 @@ finish:
|
||||
(void) rm_rf(p, REMOVE_ROOT);
|
||||
}
|
||||
|
||||
expose_port_flush(arg_expose_ports, &exposed);
|
||||
expose_port_flush(&fw_ctx, arg_expose_ports, &expose_args.address);
|
||||
|
||||
if (veth_created)
|
||||
(void) remove_veth_links(veth_name, arg_network_veth_extra);
|
||||
|
||||
@ -71,6 +71,7 @@ struct DnsPacket {
|
||||
union in_addr_union sender, destination;
|
||||
uint16_t sender_port, destination_port;
|
||||
uint32_t ttl;
|
||||
usec_t timestamp; /* CLOCK_BOOTTIME (or CLOCK_MONOTONIC if the former doesn't exist) */
|
||||
|
||||
/* For support of truncated packets */
|
||||
DnsPacket *more;
|
||||
|
||||
@ -377,6 +377,7 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use
|
||||
s->read_packet->family = s->peer.sa.sa_family;
|
||||
s->read_packet->ttl = s->ttl;
|
||||
s->read_packet->ifindex = s->ifindex;
|
||||
s->read_packet->timestamp = now(clock_boottime_or_monotonic());
|
||||
|
||||
if (s->read_packet->family == AF_INET) {
|
||||
s->read_packet->sender.in = s->peer.in.sin_addr;
|
||||
|
||||
@ -890,7 +890,6 @@ static int dns_transaction_fix_rcode(DnsTransaction *t) {
|
||||
}
|
||||
|
||||
void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
|
||||
usec_t ts;
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
@ -973,8 +972,6 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
|
||||
}
|
||||
}
|
||||
|
||||
assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0);
|
||||
|
||||
switch (t->scope->protocol) {
|
||||
|
||||
case DNS_PROTOCOL_DNS:
|
||||
@ -1039,7 +1036,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) {
|
||||
|
||||
case DNS_PROTOCOL_LLMNR:
|
||||
case DNS_PROTOCOL_MDNS:
|
||||
dns_scope_packet_received(t->scope, ts - t->start_usec);
|
||||
dns_scope_packet_received(t->scope, p->timestamp - t->start_usec);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@ -854,6 +854,8 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
|
||||
} else
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
p->timestamp = now(clock_boottime_or_monotonic());
|
||||
|
||||
CMSG_FOREACH(cmsg, &mh) {
|
||||
|
||||
if (cmsg->cmsg_level == IPPROTO_IPV6) {
|
||||
|
||||
@ -481,7 +481,7 @@ int ask_password_tty(
|
||||
|
||||
if (!(flags & ASK_PASSWORD_SILENT) && !(flags & ASK_PASSWORD_ECHO)) {
|
||||
if (use_color)
|
||||
(void) loop_write(ttyfd, ANSI_GREY, STRLEN(ANSI_GREY), false);
|
||||
(void) loop_write(ttyfd, ansi_grey(), strlen(ansi_grey()), false);
|
||||
(void) loop_write(ttyfd, PRESS_TAB, strlen(PRESS_TAB), false);
|
||||
press_tab_visible = true;
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
#include "fsck-util.h"
|
||||
#include "gpt.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "hostname-util.h"
|
||||
#include "hostname-setup.h"
|
||||
#include "id128-util.h"
|
||||
#include "mkdir.h"
|
||||
#include "mount-util.h"
|
||||
|
||||
350
src/shared/firewall-util-iptables.c
Normal file
350
src/shared/firewall-util-iptables.c
Normal file
@ -0,0 +1,350 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
/* Temporary work-around for broken glibc vs. linux kernel header definitions
|
||||
* This is already fixed upstream, remove this when distributions have updated.
|
||||
*/
|
||||
#define _NET_IF_H 1
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <endian.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <net/if.h>
|
||||
#ifndef IFNAMSIZ
|
||||
#define IFNAMSIZ 16
|
||||
#endif
|
||||
#include <linux/if.h>
|
||||
#include <linux/netfilter_ipv4/ip_tables.h>
|
||||
#include <linux/netfilter/nf_nat.h>
|
||||
#include <linux/netfilter/xt_addrtype.h>
|
||||
#include <libiptc/libiptc.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "firewall-util.h"
|
||||
#include "firewall-util-private.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "macro.h"
|
||||
#include "socket-util.h"
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(struct xtc_handle*, iptc_free);
|
||||
|
||||
static int entry_fill_basics(
|
||||
struct ipt_entry *entry,
|
||||
int protocol,
|
||||
const char *in_interface,
|
||||
const union in_addr_union *source,
|
||||
unsigned source_prefixlen,
|
||||
const char *out_interface,
|
||||
const union in_addr_union *destination,
|
||||
unsigned destination_prefixlen) {
|
||||
|
||||
assert(entry);
|
||||
|
||||
if (out_interface && !ifname_valid(out_interface))
|
||||
return -EINVAL;
|
||||
if (in_interface && !ifname_valid(in_interface))
|
||||
return -EINVAL;
|
||||
|
||||
entry->ip.proto = protocol;
|
||||
|
||||
if (in_interface) {
|
||||
size_t l;
|
||||
|
||||
l = strlen(in_interface);
|
||||
assert(l < sizeof entry->ip.iniface);
|
||||
assert(l < sizeof entry->ip.iniface_mask);
|
||||
|
||||
strcpy(entry->ip.iniface, in_interface);
|
||||
memset(entry->ip.iniface_mask, 0xFF, l + 1);
|
||||
}
|
||||
if (source) {
|
||||
entry->ip.src = source->in;
|
||||
in4_addr_prefixlen_to_netmask(&entry->ip.smsk, source_prefixlen);
|
||||
}
|
||||
|
||||
if (out_interface) {
|
||||
size_t l = strlen(out_interface);
|
||||
assert(l < sizeof entry->ip.outiface);
|
||||
assert(l < sizeof entry->ip.outiface_mask);
|
||||
|
||||
strcpy(entry->ip.outiface, out_interface);
|
||||
memset(entry->ip.outiface_mask, 0xFF, l + 1);
|
||||
}
|
||||
if (destination) {
|
||||
entry->ip.dst = destination->in;
|
||||
in4_addr_prefixlen_to_netmask(&entry->ip.dmsk, destination_prefixlen);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fw_iptables_add_masquerade(
|
||||
bool add,
|
||||
int af,
|
||||
const union in_addr_union *source,
|
||||
unsigned source_prefixlen) {
|
||||
|
||||
static const xt_chainlabel chain = "POSTROUTING";
|
||||
_cleanup_(iptc_freep) struct xtc_handle *h = NULL;
|
||||
struct ipt_entry *entry, *mask;
|
||||
struct ipt_entry_target *t;
|
||||
size_t sz;
|
||||
struct nf_nat_ipv4_multi_range_compat *mr;
|
||||
int r, protocol = 0;
|
||||
const char *out_interface = NULL;
|
||||
const union in_addr_union *destination = NULL;
|
||||
unsigned destination_prefixlen = 0;
|
||||
|
||||
if (af != AF_INET)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!source || source_prefixlen == 0)
|
||||
return -EINVAL;
|
||||
|
||||
h = iptc_init("nat");
|
||||
if (!h)
|
||||
return -errno;
|
||||
|
||||
sz = XT_ALIGN(sizeof(struct ipt_entry)) +
|
||||
XT_ALIGN(sizeof(struct ipt_entry_target)) +
|
||||
XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
|
||||
|
||||
/* Put together the entry we want to add or remove */
|
||||
entry = alloca0(sz);
|
||||
entry->next_offset = sz;
|
||||
entry->target_offset = XT_ALIGN(sizeof(struct ipt_entry));
|
||||
r = entry_fill_basics(entry, protocol, NULL, source, source_prefixlen, out_interface, destination, destination_prefixlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Fill in target part */
|
||||
t = ipt_get_target(entry);
|
||||
t->u.target_size =
|
||||
XT_ALIGN(sizeof(struct ipt_entry_target)) +
|
||||
XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
|
||||
strncpy(t->u.user.name, "MASQUERADE", sizeof(t->u.user.name));
|
||||
mr = (struct nf_nat_ipv4_multi_range_compat*) t->data;
|
||||
mr->rangesize = 1;
|
||||
|
||||
/* Create a search mask entry */
|
||||
mask = alloca(sz);
|
||||
memset(mask, 0xFF, sz);
|
||||
|
||||
if (add) {
|
||||
if (iptc_check_entry(chain, entry, (unsigned char*) mask, h))
|
||||
return 0;
|
||||
if (errno != ENOENT) /* if other error than not existing yet, fail */
|
||||
return -errno;
|
||||
|
||||
if (!iptc_insert_entry(chain, entry, 0, h))
|
||||
return -errno;
|
||||
} else {
|
||||
if (!iptc_delete_entry(chain, entry, (unsigned char*) mask, h)) {
|
||||
if (errno == ENOENT) /* if it's already gone, all is good! */
|
||||
return 0;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
if (!iptc_commit(h))
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fw_iptables_add_local_dnat(
|
||||
bool add,
|
||||
int af,
|
||||
int protocol,
|
||||
uint16_t local_port,
|
||||
const union in_addr_union *remote,
|
||||
uint16_t remote_port,
|
||||
const union in_addr_union *previous_remote) {
|
||||
|
||||
static const xt_chainlabel chain_pre = "PREROUTING", chain_output = "OUTPUT";
|
||||
_cleanup_(iptc_freep) struct xtc_handle *h = NULL;
|
||||
struct ipt_entry *entry, *mask;
|
||||
struct ipt_entry_target *t;
|
||||
struct ipt_entry_match *m;
|
||||
struct xt_addrtype_info_v1 *at;
|
||||
struct nf_nat_ipv4_multi_range_compat *mr;
|
||||
size_t sz, msz;
|
||||
int r;
|
||||
const char *in_interface = NULL;
|
||||
const union in_addr_union *source = NULL;
|
||||
unsigned source_prefixlen = 0;
|
||||
const union in_addr_union *destination = NULL;
|
||||
unsigned destination_prefixlen = 0;
|
||||
|
||||
assert(add || !previous_remote);
|
||||
|
||||
if (af != AF_INET)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!IN_SET(protocol, IPPROTO_TCP, IPPROTO_UDP))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (local_port <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (remote_port <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
h = iptc_init("nat");
|
||||
if (!h)
|
||||
return -errno;
|
||||
|
||||
sz = XT_ALIGN(sizeof(struct ipt_entry)) +
|
||||
XT_ALIGN(sizeof(struct ipt_entry_match)) +
|
||||
XT_ALIGN(sizeof(struct xt_addrtype_info_v1)) +
|
||||
XT_ALIGN(sizeof(struct ipt_entry_target)) +
|
||||
XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
|
||||
|
||||
if (protocol == IPPROTO_TCP)
|
||||
msz = XT_ALIGN(sizeof(struct ipt_entry_match)) +
|
||||
XT_ALIGN(sizeof(struct xt_tcp));
|
||||
else
|
||||
msz = XT_ALIGN(sizeof(struct ipt_entry_match)) +
|
||||
XT_ALIGN(sizeof(struct xt_udp));
|
||||
|
||||
sz += msz;
|
||||
|
||||
/* Fill in basic part */
|
||||
entry = alloca0(sz);
|
||||
entry->next_offset = sz;
|
||||
entry->target_offset =
|
||||
XT_ALIGN(sizeof(struct ipt_entry)) +
|
||||
XT_ALIGN(sizeof(struct ipt_entry_match)) +
|
||||
XT_ALIGN(sizeof(struct xt_addrtype_info_v1)) +
|
||||
msz;
|
||||
r = entry_fill_basics(entry, protocol, in_interface, source, source_prefixlen, NULL, destination, destination_prefixlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Fill in first match */
|
||||
m = (struct ipt_entry_match*) ((uint8_t*) entry + XT_ALIGN(sizeof(struct ipt_entry)));
|
||||
m->u.match_size = msz;
|
||||
if (protocol == IPPROTO_TCP) {
|
||||
struct xt_tcp *tcp;
|
||||
|
||||
strncpy(m->u.user.name, "tcp", sizeof(m->u.user.name));
|
||||
tcp = (struct xt_tcp*) m->data;
|
||||
tcp->dpts[0] = tcp->dpts[1] = local_port;
|
||||
tcp->spts[0] = 0;
|
||||
tcp->spts[1] = 0xFFFF;
|
||||
|
||||
} else {
|
||||
struct xt_udp *udp;
|
||||
|
||||
strncpy(m->u.user.name, "udp", sizeof(m->u.user.name));
|
||||
udp = (struct xt_udp*) m->data;
|
||||
udp->dpts[0] = udp->dpts[1] = local_port;
|
||||
udp->spts[0] = 0;
|
||||
udp->spts[1] = 0xFFFF;
|
||||
}
|
||||
|
||||
/* Fill in second match */
|
||||
m = (struct ipt_entry_match*) ((uint8_t*) entry + XT_ALIGN(sizeof(struct ipt_entry)) + msz);
|
||||
m->u.match_size =
|
||||
XT_ALIGN(sizeof(struct ipt_entry_match)) +
|
||||
XT_ALIGN(sizeof(struct xt_addrtype_info_v1));
|
||||
strncpy(m->u.user.name, "addrtype", sizeof(m->u.user.name));
|
||||
m->u.user.revision = 1;
|
||||
at = (struct xt_addrtype_info_v1*) m->data;
|
||||
at->dest = XT_ADDRTYPE_LOCAL;
|
||||
|
||||
/* Fill in target part */
|
||||
t = ipt_get_target(entry);
|
||||
t->u.target_size =
|
||||
XT_ALIGN(sizeof(struct ipt_entry_target)) +
|
||||
XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
|
||||
strncpy(t->u.user.name, "DNAT", sizeof(t->u.user.name));
|
||||
mr = (struct nf_nat_ipv4_multi_range_compat*) t->data;
|
||||
mr->rangesize = 1;
|
||||
mr->range[0].flags = NF_NAT_RANGE_PROTO_SPECIFIED|NF_NAT_RANGE_MAP_IPS;
|
||||
mr->range[0].min_ip = mr->range[0].max_ip = remote->in.s_addr;
|
||||
if (protocol == IPPROTO_TCP)
|
||||
mr->range[0].min.tcp.port = mr->range[0].max.tcp.port = htobe16(remote_port);
|
||||
else
|
||||
mr->range[0].min.udp.port = mr->range[0].max.udp.port = htobe16(remote_port);
|
||||
|
||||
mask = alloca0(sz);
|
||||
memset(mask, 0xFF, sz);
|
||||
|
||||
if (add) {
|
||||
/* Add the PREROUTING rule, if it is missing so far */
|
||||
if (!iptc_check_entry(chain_pre, entry, (unsigned char*) mask, h)) {
|
||||
if (errno != ENOENT)
|
||||
return -EINVAL;
|
||||
|
||||
if (!iptc_insert_entry(chain_pre, entry, 0, h))
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* If a previous remote is set, remove its entry */
|
||||
if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) {
|
||||
mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr;
|
||||
|
||||
if (!iptc_delete_entry(chain_pre, entry, (unsigned char*) mask, h)) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
mr->range[0].min_ip = mr->range[0].max_ip = remote->in.s_addr;
|
||||
}
|
||||
|
||||
/* Add the OUTPUT rule, if it is missing so far */
|
||||
if (!in_interface) {
|
||||
|
||||
/* Don't apply onto loopback addresses */
|
||||
if (!destination) {
|
||||
entry->ip.dst.s_addr = htobe32(0x7F000000);
|
||||
entry->ip.dmsk.s_addr = htobe32(0xFF000000);
|
||||
entry->ip.invflags = IPT_INV_DSTIP;
|
||||
}
|
||||
|
||||
if (!iptc_check_entry(chain_output, entry, (unsigned char*) mask, h)) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
if (!iptc_insert_entry(chain_output, entry, 0, h))
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* If a previous remote is set, remove its entry */
|
||||
if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) {
|
||||
mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr;
|
||||
|
||||
if (!iptc_delete_entry(chain_output, entry, (unsigned char*) mask, h)) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!iptc_delete_entry(chain_pre, entry, (unsigned char*) mask, h)) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (!in_interface) {
|
||||
if (!destination) {
|
||||
entry->ip.dst.s_addr = htobe32(0x7F000000);
|
||||
entry->ip.dmsk.s_addr = htobe32(0xFF000000);
|
||||
entry->ip.invflags = IPT_INV_DSTIP;
|
||||
}
|
||||
|
||||
if (!iptc_delete_entry(chain_output, entry, (unsigned char*) mask, h)) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!iptc_commit(h))
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
958
src/shared/firewall-util-nft.c
Normal file
958
src/shared/firewall-util-nft.c
Normal file
@ -0,0 +1,958 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <endian.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <linux/netfilter/nf_tables.h>
|
||||
#include <linux/netfilter/nf_nat.h>
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <netinet/ip.h>
|
||||
|
||||
#include "sd-netlink.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "firewall-util.h"
|
||||
#include "firewall-util-private.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "macro.h"
|
||||
#include "socket-util.h"
|
||||
#include "time-util.h"
|
||||
|
||||
#define NFT_SYSTEMD_DNAT_MAP_NAME "map_port_ipport"
|
||||
#define NFT_SYSTEMD_TABLE_NAME "io.systemd.nat"
|
||||
#define NFT_SYSTEMD_MASQ_SET_NAME "masq_saddr"
|
||||
|
||||
#define NFNL_DEFAULT_TIMEOUT_USECS (1ULL * USEC_PER_SEC)
|
||||
|
||||
#define UDP_DPORT_OFFSET 2
|
||||
|
||||
static int nfnl_netlink_sendv(sd_netlink *nfnl,
|
||||
sd_netlink_message *messages[],
|
||||
size_t msgcount) {
|
||||
_cleanup_free_ uint32_t *serial = NULL;
|
||||
size_t i;
|
||||
int r;
|
||||
|
||||
assert(msgcount > 0);
|
||||
|
||||
r = sd_netlink_sendv(nfnl, messages, msgcount, &serial);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = 0;
|
||||
for (i = 1; i < msgcount - 1; i++) {
|
||||
int tmp;
|
||||
|
||||
/* If message is an error, this returns embedded errno */
|
||||
tmp = sd_netlink_read(nfnl, serial[i], NFNL_DEFAULT_TIMEOUT_USECS, NULL);
|
||||
if (tmp < 0 && r == 0)
|
||||
r = tmp;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int nfnl_add_open_expr_container(sd_netlink_message *m, const char *name) {
|
||||
int r;
|
||||
|
||||
r = sd_netlink_message_open_array(m, NFTA_LIST_ELEM);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_string(m, NFTA_EXPR_NAME, name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_netlink_message_open_container_union(m, NFTA_EXPR_DATA, name);
|
||||
}
|
||||
|
||||
static int nfnl_add_expr_fib(sd_netlink_message *m, uint32_t nft_fib_flags,
|
||||
enum nft_fib_result result,
|
||||
enum nft_registers dreg) {
|
||||
int r;
|
||||
|
||||
r = nfnl_add_open_expr_container(m, "fib");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_u32(m, NFTA_FIB_FLAGS, htobe32(nft_fib_flags));
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_netlink_message_append_u32(m, NFTA_FIB_RESULT, htobe32(result));
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_netlink_message_append_u32(m, NFTA_FIB_DREG, htobe32(dreg));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
|
||||
}
|
||||
|
||||
static int nfnl_add_expr_meta(sd_netlink_message *m, enum nft_meta_keys key,
|
||||
enum nft_registers dreg) {
|
||||
int r;
|
||||
|
||||
r = nfnl_add_open_expr_container(m, "meta");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_u32(m, NFTA_META_KEY, htobe32(key));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_u32(m, NFTA_META_DREG, htobe32(dreg));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
|
||||
}
|
||||
|
||||
static int nfnl_add_expr_payload(sd_netlink_message *m, enum nft_payload_bases pb,
|
||||
uint32_t offset, uint32_t len, enum nft_registers dreg) {
|
||||
int r;
|
||||
|
||||
r = nfnl_add_open_expr_container(m, "payload");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_u32(m, NFTA_PAYLOAD_DREG, htobe32(dreg));
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_netlink_message_append_u32(m, NFTA_PAYLOAD_BASE, htobe32(pb));
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_netlink_message_append_u32(m, NFTA_PAYLOAD_OFFSET, htobe32(offset));
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_netlink_message_append_u32(m, NFTA_PAYLOAD_LEN, htobe32(len));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
|
||||
if (r < 0)
|
||||
return r;
|
||||
return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
|
||||
}
|
||||
|
||||
static int nfnl_add_expr_lookup_set_data(sd_netlink_message *m, const char *set_name,
|
||||
enum nft_registers sreg) {
|
||||
int r;
|
||||
|
||||
r = nfnl_add_open_expr_container(m, "lookup");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_string(m, NFTA_LOOKUP_SET, set_name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_netlink_message_append_u32(m, NFTA_LOOKUP_SREG, htobe32(sreg));
|
||||
}
|
||||
|
||||
static int nfnl_add_expr_lookup_set(sd_netlink_message *m, const char *set_name,
|
||||
enum nft_registers sreg) {
|
||||
int r;
|
||||
|
||||
r = nfnl_add_expr_lookup_set_data(m, set_name, sreg);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
|
||||
if (r < 0)
|
||||
return r;
|
||||
return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
|
||||
}
|
||||
|
||||
static int nfnl_add_expr_lookup_map(sd_netlink_message *m, const char *set_name,
|
||||
enum nft_registers sreg, enum nft_registers dreg) {
|
||||
int r;
|
||||
|
||||
r = nfnl_add_expr_lookup_set_data(m, set_name, sreg);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_u32(m, NFTA_LOOKUP_DREG, htobe32(dreg));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
|
||||
}
|
||||
|
||||
static int nfnl_add_expr_data(sd_netlink_message *m, int attr, const void *data, uint32_t dlen) {
|
||||
int r;
|
||||
|
||||
r = sd_netlink_message_open_container(m, attr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_netlink_message_append_data(m, NFTA_DATA_VALUE, data, dlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_netlink_message_close_container(m); /* attr */
|
||||
}
|
||||
|
||||
static int nfnl_add_expr_cmp_data(sd_netlink_message *m, const void *data, uint32_t dlen) {
|
||||
return nfnl_add_expr_data(m, NFTA_CMP_DATA, data, dlen);
|
||||
}
|
||||
|
||||
static int nfnl_add_expr_cmp(sd_netlink_message *m, enum nft_cmp_ops cmp_op,
|
||||
enum nft_registers sreg, const void *data, uint32_t dlen) {
|
||||
int r;
|
||||
|
||||
r = nfnl_add_open_expr_container(m, "cmp");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_u32(m, NFTA_CMP_OP, htobe32(cmp_op));
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_netlink_message_append_u32(m, NFTA_CMP_SREG, htobe32(sreg));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = nfnl_add_expr_cmp_data(m, data, dlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
|
||||
if (r < 0)
|
||||
return r;
|
||||
return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
|
||||
}
|
||||
|
||||
static int nfnl_add_expr_bitwise(sd_netlink_message *m,
|
||||
enum nft_registers sreg,
|
||||
enum nft_registers dreg,
|
||||
const void *and,
|
||||
const void *xor, uint32_t len) {
|
||||
int r;
|
||||
|
||||
r = nfnl_add_open_expr_container(m, "bitwise");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_u32(m, NFTA_BITWISE_SREG, htobe32(sreg));
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_netlink_message_append_u32(m, NFTA_BITWISE_DREG, htobe32(dreg));
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_netlink_message_append_u32(m, NFTA_BITWISE_LEN, htobe32(len));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = nfnl_add_expr_data(m, NFTA_BITWISE_MASK, and, len);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = nfnl_add_expr_data(m, NFTA_BITWISE_XOR, xor, len);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_close_container(m); /* NFTA_EXPR_DATA */
|
||||
if (r < 0)
|
||||
return r;
|
||||
return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
|
||||
}
|
||||
|
||||
static int nfnl_add_expr_dnat(sd_netlink_message *m,
|
||||
int family,
|
||||
enum nft_registers areg,
|
||||
enum nft_registers preg) {
|
||||
int r;
|
||||
|
||||
r = nfnl_add_open_expr_container(m, "nat");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_u32(m, NFTA_NAT_TYPE, htobe32(NFT_NAT_DNAT));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_u32(m, NFTA_NAT_FAMILY, htobe32(family));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_u32(m, NFTA_NAT_REG_ADDR_MIN, htobe32(areg));
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_netlink_message_append_u32(m, NFTA_NAT_REG_PROTO_MIN, htobe32(preg));
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_netlink_message_close_container(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_netlink_message_close_container(m);
|
||||
}
|
||||
|
||||
static int nfnl_add_expr_masq(sd_netlink_message *m) {
|
||||
int r;
|
||||
|
||||
r = sd_netlink_message_open_array(m, NFTA_LIST_ELEM);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_string(m, NFTA_EXPR_NAME, "masq");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_netlink_message_close_container(m); /* NFTA_LIST_ELEM */
|
||||
}
|
||||
|
||||
/* -t nat -A POSTROUTING -p protocol -s source/pflen -o out_interface -d destionation/pflen -j MASQUERADE */
|
||||
static int sd_nfnl_message_new_masq_rule(sd_netlink *nfnl, sd_netlink_message **ret, int family,
|
||||
const char *chain) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
int r;
|
||||
|
||||
r = sd_nfnl_nft_message_new_rule(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, chain);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_open_container(m, NFTA_RULE_EXPRESSIONS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* 1st statement: ip saddr @masq_saddr. Place iph->saddr in reg1. */
|
||||
r = nfnl_add_expr_payload(m, NFT_PAYLOAD_NETWORK_HEADER, offsetof(struct iphdr, saddr),
|
||||
sizeof(uint32_t), NFT_REG32_01);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* 1st statement: use reg1 content to make lookup in @masq_saddr set. */
|
||||
r = nfnl_add_expr_lookup_set(m, NFT_SYSTEMD_MASQ_SET_NAME, NFT_REG32_01);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* 2nd statement: masq. Only executed by kernel if the previous lookup was successful. */
|
||||
r = nfnl_add_expr_masq(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_close_container(m); /* NFTA_RULE_EXPRESSIONS */
|
||||
if (r < 0)
|
||||
return r;
|
||||
*ret = TAKE_PTR(m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -t nat -A PREROUTING -p protocol --dport local_port -i in_interface -s source/pflen -d destionation/pflen -j DNAT --to-destination remote_addr:remote_port */
|
||||
static int sd_nfnl_message_new_dnat_rule_pre(sd_netlink *nfnl, sd_netlink_message **ret, int family,
|
||||
const char *chain) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
enum nft_registers proto_reg;
|
||||
uint32_t local = RTN_LOCAL;
|
||||
int r;
|
||||
|
||||
r = sd_nfnl_nft_message_new_rule(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, chain);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_open_container(m, NFTA_RULE_EXPRESSIONS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* 1st statement: fib daddr type local */
|
||||
r = nfnl_add_expr_fib(m, NFTA_FIB_F_DADDR, NFT_FIB_RESULT_ADDRTYPE, NFT_REG32_01);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* 1st statement (cont.): compare RTN_LOCAL */
|
||||
r = nfnl_add_expr_cmp(m, NFT_CMP_EQ, NFT_REG32_01, &local, sizeof(local));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* 2nd statement: lookup local port in map, fetch address:dport to map to */
|
||||
r = nfnl_add_expr_meta(m, NFT_META_L4PROTO, NFT_REG32_01);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = nfnl_add_expr_payload(m, NFT_PAYLOAD_TRANSPORT_HEADER, UDP_DPORT_OFFSET,
|
||||
sizeof(uint16_t), NFT_REG32_02);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* 3rd statement: lookup 'l4proto . dport', e.g. 'tcp . 22' as key and
|
||||
* store address and port for the dnat mapping in REG1/REG2.
|
||||
*/
|
||||
r = nfnl_add_expr_lookup_map(m, NFT_SYSTEMD_DNAT_MAP_NAME, NFT_REG32_01, NFT_REG32_01);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
proto_reg = NFT_REG32_02;
|
||||
r = nfnl_add_expr_dnat(m, family, NFT_REG32_01, proto_reg);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_close_container(m); /* NFTA_RULE_EXPRESSIONS */
|
||||
if (r < 0)
|
||||
return r;
|
||||
*ret = TAKE_PTR(m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_nfnl_message_new_dnat_rule_out(sd_netlink *nfnl, sd_netlink_message **ret,
|
||||
int family, const char *chain) {
|
||||
static const uint32_t zero, one = 1;
|
||||
|
||||
uint32_t lonet = htobe32(0x7F000000), lomask = htobe32(0xff000000);
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
enum nft_registers proto_reg;
|
||||
int r;
|
||||
|
||||
r = sd_nfnl_nft_message_new_rule(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, chain);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_open_container(m, NFTA_RULE_EXPRESSIONS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* 1st statement: exclude 127.0.0.1/8: ip daddr != 127.0.0.1/8 */
|
||||
r = nfnl_add_expr_payload(m, NFT_PAYLOAD_NETWORK_HEADER, offsetof(struct iphdr, daddr),
|
||||
sizeof(uint32_t), NFT_REG32_01);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* 1st statement (cont.): bitops/prefix */
|
||||
r = nfnl_add_expr_bitwise(m, NFT_REG32_01, NFT_REG32_01, &lomask, &zero, sizeof(lomask));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* 1st statement (cont.): compare reg1 with 127/8 */
|
||||
r = nfnl_add_expr_cmp(m, NFT_CMP_NEQ, NFT_REG32_01, &lonet, sizeof(lonet));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* 2nd statement: meta oif lo */
|
||||
r = nfnl_add_expr_meta(m, NFT_META_OIF, NFT_REG32_01);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* 2nd statement (cont.): compare to lo ifindex (1) */
|
||||
r = nfnl_add_expr_cmp(m, NFT_CMP_EQ, NFT_REG32_01, &one, sizeof(one));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* 3rd statement: meta l4proto . th dport dnat ip . port to map @map_port_ipport */
|
||||
r = nfnl_add_expr_meta(m, NFT_META_L4PROTO, NFT_REG32_01);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* 3rd statement (cont): store the port number in reg2 */
|
||||
r = nfnl_add_expr_payload(m, NFT_PAYLOAD_TRANSPORT_HEADER, UDP_DPORT_OFFSET,
|
||||
sizeof(uint16_t), NFT_REG32_02);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* 3rd statement (cont): use reg1 and reg2 and retrieve
|
||||
* the new destination ip and port number.
|
||||
*
|
||||
* reg1 and reg2 are clobbered and will then contain the new
|
||||
* address/port number.
|
||||
*/
|
||||
r = nfnl_add_expr_lookup_map(m, NFT_SYSTEMD_DNAT_MAP_NAME, NFT_REG32_01, NFT_REG32_01);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* 4th statement: dnat connection to address/port retrieved by the
|
||||
* preceeding expression. */
|
||||
proto_reg = NFT_REG32_02;
|
||||
r = nfnl_add_expr_dnat(m, family, NFT_REG32_01, proto_reg);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_close_container(m); /* NFTA_RULE_EXPRESSIONS */
|
||||
if (r < 0)
|
||||
return r;
|
||||
*ret = TAKE_PTR(m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nft_new_set(struct sd_netlink *nfnl,
|
||||
sd_netlink_message **ret,
|
||||
int family, const char *set_name,
|
||||
uint32_t set_id,
|
||||
uint32_t flags, uint32_t type, uint32_t klen) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
int r;
|
||||
|
||||
r = sd_nfnl_nft_message_new_set(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, set_name, set_id, klen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (flags != 0) {
|
||||
r = sd_netlink_message_append_u32(m, NFTA_SET_FLAGS, htobe32(flags));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_netlink_message_append_u32(m, NFTA_SET_KEY_TYPE, htobe32(type));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(m);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int nft_new_map(struct sd_netlink *nfnl,
|
||||
sd_netlink_message **ret,
|
||||
int family, const char *set_name, uint32_t set_id,
|
||||
uint32_t flags, uint32_t type, uint32_t klen, uint32_t dtype, uint32_t dlen) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
int r;
|
||||
|
||||
r = nft_new_set(nfnl, &m, family, set_name, set_id, flags | NFT_SET_MAP, type, klen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_u32(m, NFTA_SET_DATA_TYPE, htobe32(dtype));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_u32(m, NFTA_SET_DATA_LEN, htobe32(dlen));
|
||||
if (r < 0)
|
||||
return r;
|
||||
*ret = TAKE_PTR(m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nft_add_element(sd_netlink *nfnl, sd_netlink_message **ret,
|
||||
int family, const char *set_name,
|
||||
const void *key, uint32_t klen,
|
||||
const void *data, uint32_t dlen) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
int r;
|
||||
|
||||
/*
|
||||
* Ideally there would be an API that provides:
|
||||
*
|
||||
* 1) a init function to add the main ruleset skeleton
|
||||
* 2) a function that populates the sets with all known address/port pairs to s/dnat for
|
||||
* 3) a function that can remove address/port pairs again.
|
||||
*
|
||||
* At this time, the existing API is used which is built on a
|
||||
* 'add/delete a rule' paradigm.
|
||||
*
|
||||
* This replicated here and each element gets added to the set
|
||||
* one-by-one.
|
||||
*/
|
||||
r = sd_nfnl_nft_message_new_setelems_begin(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, set_name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_nfnl_nft_message_add_setelem(m, 0, key, klen, data, dlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* could theoretically append more set elements to add here */
|
||||
r = sd_nfnl_nft_message_add_setelem_end(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
*ret = TAKE_PTR(m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nft_del_element(sd_netlink *nfnl,
|
||||
sd_netlink_message **ret, int family, const char *set_name,
|
||||
const void *key, uint32_t klen,
|
||||
const void *data, uint32_t dlen) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
|
||||
int r;
|
||||
|
||||
r = sd_nfnl_nft_message_del_setelems_begin(nfnl, &m, family, NFT_SYSTEMD_TABLE_NAME, set_name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_nfnl_nft_message_add_setelem(m, 0, key, klen, data, dlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_nfnl_nft_message_add_setelem_end(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
*ret = TAKE_PTR(m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is needed so 'nft' userspace tool can properly format the contents
|
||||
* of the set/map when someone uses 'nft' to inspect their content.
|
||||
*
|
||||
* The values cannot be changed, they are part of the nft tool type identifier ABI.
|
||||
*/
|
||||
#define TYPE_BITS 6
|
||||
|
||||
enum nft_key_types {
|
||||
TYPE_IPADDR = 7,
|
||||
TYPE_IP6ADDR = 8,
|
||||
TYPE_INET_PROTOCOL = 12,
|
||||
TYPE_INET_SERVICE = 13,
|
||||
};
|
||||
|
||||
static uint32_t concat_types2(enum nft_key_types a, enum nft_key_types b) {
|
||||
uint32_t type = (uint32_t)a;
|
||||
|
||||
type <<= TYPE_BITS;
|
||||
type |= (uint32_t)b;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
/* enough space to hold netlink messages for table skeleton */
|
||||
#define NFT_INIT_MSGS 16
|
||||
static int fw_nftables_init_family(sd_netlink *nfnl, int family) {
|
||||
sd_netlink_message *batch[NFT_INIT_MSGS] = {};
|
||||
size_t ip_type_size = sizeof(uint32_t);
|
||||
int ip_type = TYPE_IPADDR, r;
|
||||
size_t msgcnt = 0, i;
|
||||
uint32_t set_id = 0;
|
||||
|
||||
r = sd_nfnl_message_batch_begin(nfnl, &batch[msgcnt]);
|
||||
if (r < 0)
|
||||
goto out_unref;
|
||||
|
||||
msgcnt++;
|
||||
assert(msgcnt < NFT_INIT_MSGS);
|
||||
/* Set F_EXCL so table add fails if the table already exists. */
|
||||
r = sd_nfnl_nft_message_new_table(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_TABLE_NAME, NLM_F_EXCL | NLM_F_ACK);
|
||||
if (r < 0)
|
||||
goto out_unref;
|
||||
|
||||
msgcnt++;
|
||||
assert(msgcnt < NFT_INIT_MSGS);
|
||||
|
||||
r = sd_nfnl_nft_message_new_basechain(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_TABLE_NAME,
|
||||
"prerouting", "nat",
|
||||
NF_INET_PRE_ROUTING, NF_IP_PRI_NAT_DST + 1);
|
||||
if (r < 0)
|
||||
goto out_unref;
|
||||
|
||||
msgcnt++;
|
||||
assert(msgcnt < NFT_INIT_MSGS);
|
||||
r = sd_nfnl_nft_message_new_basechain(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_TABLE_NAME,
|
||||
"output", "nat",
|
||||
NF_INET_LOCAL_OUT, NF_IP_PRI_NAT_DST + 1);
|
||||
if (r < 0)
|
||||
goto out_unref;
|
||||
|
||||
msgcnt++;
|
||||
assert(msgcnt < NFT_INIT_MSGS);
|
||||
r = sd_nfnl_nft_message_new_basechain(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_TABLE_NAME,
|
||||
"postrouting", "nat",
|
||||
NF_INET_POST_ROUTING, NF_IP_PRI_NAT_SRC + 1);
|
||||
if (r < 0)
|
||||
goto out_unref;
|
||||
|
||||
msgcnt++;
|
||||
assert(msgcnt < NFT_INIT_MSGS);
|
||||
/* set to store ip address ranges we should masquerade for */
|
||||
r = nft_new_set(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_MASQ_SET_NAME, ++set_id, NFT_SET_INTERVAL, ip_type, ip_type_size);
|
||||
if (r < 0)
|
||||
goto out_unref;
|
||||
|
||||
/*
|
||||
* map to store ip address:port pair to dnat to. elements in concatenation
|
||||
* are rounded up to 4 bytes.
|
||||
*
|
||||
* Example: ip protocol . tcp daddr is sizeof(uint32_t) + sizeof(uint32_t), not
|
||||
* sizeof(uint8_t) + sizeof(uint16_t).
|
||||
*/
|
||||
msgcnt++;
|
||||
assert(msgcnt < NFT_INIT_MSGS);
|
||||
r = nft_new_map(nfnl, &batch[msgcnt], family, NFT_SYSTEMD_DNAT_MAP_NAME, ++set_id, 0,
|
||||
concat_types2(TYPE_INET_PROTOCOL, TYPE_INET_SERVICE), sizeof(uint32_t) * 2,
|
||||
concat_types2(ip_type, TYPE_INET_SERVICE), ip_type_size + sizeof(uint32_t));
|
||||
if (r < 0)
|
||||
goto out_unref;
|
||||
|
||||
msgcnt++;
|
||||
assert(msgcnt < NFT_INIT_MSGS);
|
||||
r = sd_nfnl_message_new_dnat_rule_pre(nfnl, &batch[msgcnt], family, "prerouting");
|
||||
if (r < 0)
|
||||
goto out_unref;
|
||||
|
||||
msgcnt++;
|
||||
assert(msgcnt < NFT_INIT_MSGS);
|
||||
r = sd_nfnl_message_new_dnat_rule_out(nfnl, &batch[msgcnt], family, "output");
|
||||
if (r < 0)
|
||||
goto out_unref;
|
||||
|
||||
msgcnt++;
|
||||
r = sd_nfnl_message_new_masq_rule(nfnl, &batch[msgcnt], family, "postrouting");
|
||||
if (r < 0)
|
||||
goto out_unref;
|
||||
|
||||
msgcnt++;
|
||||
assert(msgcnt < NFT_INIT_MSGS);
|
||||
r = sd_nfnl_message_batch_end(nfnl, &batch[msgcnt]);
|
||||
if (r < 0)
|
||||
goto out_unref;
|
||||
|
||||
msgcnt++;
|
||||
assert(msgcnt <= NFT_INIT_MSGS);
|
||||
r = nfnl_netlink_sendv(nfnl, batch, msgcnt);
|
||||
if (r == -EEXIST)
|
||||
r = 0;
|
||||
|
||||
out_unref:
|
||||
for (i = 0; i < msgcnt; i++)
|
||||
sd_netlink_message_unref(batch[i]);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int fw_nftables_init(FirewallContext *ctx) {
|
||||
_cleanup_(sd_netlink_unrefp) sd_netlink *nfnl = NULL;
|
||||
int r;
|
||||
|
||||
r = sd_nfnl_socket_open(&nfnl);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = fw_nftables_init_family(nfnl, AF_INET);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
ctx->nfnl = TAKE_PTR(nfnl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fw_nftables_exit(FirewallContext *ctx) {
|
||||
ctx->nfnl = sd_netlink_unref(ctx->nfnl);
|
||||
}
|
||||
|
||||
static int nft_message_add_setelem_iprange(sd_netlink_message *m,
|
||||
const union in_addr_union *source,
|
||||
unsigned int prefixlen) {
|
||||
uint32_t mask, start, end;
|
||||
unsigned int nplen;
|
||||
int r;
|
||||
|
||||
assert(prefixlen <= 32);
|
||||
nplen = 32 - prefixlen;
|
||||
|
||||
mask = (1U << nplen) - 1U;
|
||||
mask = htobe32(~mask);
|
||||
start = source->in.s_addr & mask;
|
||||
|
||||
r = sd_nfnl_nft_message_add_setelem(m, 0, &start, sizeof(start), NULL, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_nfnl_nft_message_add_setelem_end(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
end = be32toh(start) + (1U << nplen);
|
||||
if (end < be32toh(start))
|
||||
end = 0U;
|
||||
end = htobe32(end);
|
||||
|
||||
r = sd_nfnl_nft_message_add_setelem(m, 1, &end, sizeof(end), NULL, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_message_append_u32(m, NFTA_SET_ELEM_FLAGS, htobe32(NFT_SET_ELEM_INTERVAL_END));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_nfnl_nft_message_add_setelem_end(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* When someone runs 'nft flush ruleset' in the same net namespace
|
||||
* this will also tear down the systemd nat table.
|
||||
*
|
||||
* Unlike iptables -t nat -F (which will remove all rules added by the
|
||||
* systemd iptables backend, iptables has builtin chains that cannot be
|
||||
* deleted -- the next add operation will 'just work'.
|
||||
*
|
||||
* In the nftables case, everything gets removed. The next add operation
|
||||
* will yield -ENOENT.
|
||||
*
|
||||
* If we see -ENOENT on add, replay the inital table setup.
|
||||
* If that works, re-do the add operation.
|
||||
*
|
||||
* Note that this doesn't protect against external sabotage such as a
|
||||
* 'while true; nft flush ruleset;done'. There is nothing that could be
|
||||
* done about that short of extending the kernel to allow tables to be
|
||||
* owned by stystemd-networkd and making them non-deleteable except by
|
||||
* the 'owning process'.
|
||||
*/
|
||||
static int fw_nftables_recreate_table(sd_netlink *nfnl, int af, sd_netlink_message **old, size_t size) {
|
||||
int r = fw_nftables_init_family(nfnl, af);
|
||||
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
while (size > 0) {
|
||||
size_t i = --size;
|
||||
|
||||
old[i] = sd_netlink_message_unref(old[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define NFT_MASQ_MSGS 3
|
||||
|
||||
int fw_nftables_add_masquerade(
|
||||
FirewallContext *ctx,
|
||||
bool add,
|
||||
int af,
|
||||
const union in_addr_union *source,
|
||||
unsigned int source_prefixlen) {
|
||||
sd_netlink_message *transaction[NFT_MASQ_MSGS] = {};
|
||||
bool retry = true;
|
||||
size_t tsize;
|
||||
int r;
|
||||
|
||||
if (!source || source_prefixlen == 0)
|
||||
return -EINVAL;
|
||||
|
||||
again:
|
||||
r = sd_nfnl_message_batch_begin(ctx->nfnl, &transaction[0]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
tsize = 1;
|
||||
if (add)
|
||||
r = sd_nfnl_nft_message_new_setelems_begin(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_TABLE_NAME, NFT_SYSTEMD_MASQ_SET_NAME);
|
||||
else
|
||||
r = sd_nfnl_nft_message_del_setelems_begin(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_TABLE_NAME, NFT_SYSTEMD_MASQ_SET_NAME);
|
||||
|
||||
if (r < 0)
|
||||
goto out_unref;
|
||||
|
||||
r = nft_message_add_setelem_iprange(transaction[tsize], source, source_prefixlen);
|
||||
if (r < 0)
|
||||
goto out_unref;
|
||||
|
||||
++tsize;
|
||||
assert(tsize < NFT_MASQ_MSGS);
|
||||
r = sd_nfnl_message_batch_end(ctx->nfnl, &transaction[tsize]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
++tsize;
|
||||
r = nfnl_netlink_sendv(ctx->nfnl, transaction, tsize);
|
||||
|
||||
if (retry && r == -ENOENT) {
|
||||
int tmp = fw_nftables_recreate_table(ctx->nfnl, af, transaction, tsize);
|
||||
if (tmp == 0) {
|
||||
retry = false;
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
out_unref:
|
||||
while (tsize > 0)
|
||||
sd_netlink_message_unref(transaction[--tsize]);
|
||||
return r < 0 ? r : 0;
|
||||
}
|
||||
|
||||
#define NFT_DNAT_MSGS 4
|
||||
|
||||
int fw_nftables_add_local_dnat(
|
||||
FirewallContext *ctx,
|
||||
bool add,
|
||||
int af,
|
||||
int protocol,
|
||||
uint16_t local_port,
|
||||
const union in_addr_union *remote,
|
||||
uint16_t remote_port,
|
||||
const union in_addr_union *previous_remote) {
|
||||
uint32_t data[2], key[2];
|
||||
sd_netlink_message *transaction[NFT_DNAT_MSGS] = {};
|
||||
bool retry = true;
|
||||
size_t tsize;
|
||||
int r;
|
||||
|
||||
assert(add || !previous_remote);
|
||||
|
||||
if (af != AF_INET)
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
if (!IN_SET(protocol, IPPROTO_TCP, IPPROTO_UDP))
|
||||
return -EPROTONOSUPPORT;
|
||||
|
||||
if (local_port <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
again:
|
||||
key[0] = protocol;
|
||||
key[1] = htobe16(local_port);
|
||||
|
||||
if (!remote)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (remote_port <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
data[1] = htobe16(remote_port);
|
||||
|
||||
r = sd_nfnl_message_batch_begin(ctx->nfnl, &transaction[0]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
tsize = 1;
|
||||
/* If a previous remote is set, remove its entry */
|
||||
if (add && previous_remote && previous_remote->in.s_addr != remote->in.s_addr) {
|
||||
data[0] = previous_remote->in.s_addr;
|
||||
|
||||
r = nft_del_element(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, sizeof(data));
|
||||
if (r < 0)
|
||||
goto out_unref;
|
||||
|
||||
tsize++;
|
||||
}
|
||||
|
||||
data[0] = remote->in.s_addr;
|
||||
|
||||
assert(tsize < NFT_DNAT_MSGS);
|
||||
if (add)
|
||||
nft_add_element(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, sizeof(data));
|
||||
else
|
||||
nft_del_element(ctx->nfnl, &transaction[tsize], af, NFT_SYSTEMD_DNAT_MAP_NAME, key, sizeof(key), data, sizeof(data));
|
||||
|
||||
tsize++;
|
||||
assert(tsize < NFT_DNAT_MSGS);
|
||||
|
||||
r = sd_nfnl_message_batch_end(ctx->nfnl, &transaction[tsize]);
|
||||
if (r < 0)
|
||||
goto out_unref;
|
||||
|
||||
tsize++;
|
||||
assert(tsize <= NFT_DNAT_MSGS);
|
||||
r = nfnl_netlink_sendv(ctx->nfnl, transaction, tsize);
|
||||
|
||||
if (retry && r == -ENOENT) {
|
||||
int tmp = fw_nftables_recreate_table(ctx->nfnl, af, transaction, tsize);
|
||||
|
||||
if (tmp == 0) {
|
||||
retry = false;
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
out_unref:
|
||||
while (tsize > 0)
|
||||
sd_netlink_message_unref(transaction[--tsize]);
|
||||
return r < 0 ? r : 0;
|
||||
}
|
||||
59
src/shared/firewall-util-private.h
Normal file
59
src/shared/firewall-util-private.h
Normal file
@ -0,0 +1,59 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "in-addr-util.h"
|
||||
#include "sd-netlink.h"
|
||||
|
||||
enum FirewallBackend {
|
||||
FW_BACKEND_NONE,
|
||||
#if HAVE_LIBIPTC
|
||||
FW_BACKEND_IPTABLES,
|
||||
#endif
|
||||
FW_BACKEND_NFTABLES,
|
||||
};
|
||||
|
||||
struct FirewallContext {
|
||||
enum FirewallBackend firewall_backend;
|
||||
sd_netlink *nfnl;
|
||||
};
|
||||
|
||||
int fw_nftables_init(FirewallContext *ctx);
|
||||
void fw_nftables_exit(FirewallContext *ctx);
|
||||
|
||||
int fw_nftables_add_masquerade(
|
||||
FirewallContext *ctx,
|
||||
bool add,
|
||||
int af,
|
||||
const union in_addr_union *source,
|
||||
unsigned source_prefixlen);
|
||||
|
||||
int fw_nftables_add_local_dnat(
|
||||
FirewallContext *ctx,
|
||||
bool add,
|
||||
int af,
|
||||
int protocol,
|
||||
uint16_t local_port,
|
||||
const union in_addr_union *remote,
|
||||
uint16_t remote_port,
|
||||
const union in_addr_union *previous_remote);
|
||||
|
||||
#if HAVE_LIBIPTC
|
||||
|
||||
int fw_iptables_add_masquerade(
|
||||
bool add,
|
||||
int af,
|
||||
const union in_addr_union *source,
|
||||
unsigned source_prefixlen);
|
||||
|
||||
int fw_iptables_add_local_dnat(
|
||||
bool add,
|
||||
int af,
|
||||
int protocol,
|
||||
uint16_t local_port,
|
||||
const union in_addr_union *remote,
|
||||
uint16_t remote_port,
|
||||
const union in_addr_union *previous_remote);
|
||||
#endif
|
||||
@ -1,156 +1,86 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
/* Temporary work-around for broken glibc vs. linux kernel header definitions
|
||||
* This is already fixed upstream, remove this when distributions have updated.
|
||||
*/
|
||||
#define _NET_IF_H 1
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <endian.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <net/if.h>
|
||||
#ifndef IFNAMSIZ
|
||||
#define IFNAMSIZ 16
|
||||
#endif
|
||||
#include <linux/if.h>
|
||||
#include <linux/netfilter_ipv4/ip_tables.h>
|
||||
#include <linux/netfilter/nf_nat.h>
|
||||
#include <linux/netfilter/xt_addrtype.h>
|
||||
#include <libiptc/libiptc.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "firewall-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "macro.h"
|
||||
#include "socket-util.h"
|
||||
#include "firewall-util-private.h"
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(struct xtc_handle*, iptc_free);
|
||||
static enum FirewallBackend firewall_backend_probe(FirewallContext *ctx) {
|
||||
if (fw_nftables_init(ctx) == 0)
|
||||
return FW_BACKEND_NFTABLES;
|
||||
#if HAVE_LIBIPTC
|
||||
return FW_BACKEND_IPTABLES;
|
||||
#else
|
||||
return FW_BACKEND_NONE;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int entry_fill_basics(
|
||||
struct ipt_entry *entry,
|
||||
int protocol,
|
||||
const char *in_interface,
|
||||
const union in_addr_union *source,
|
||||
unsigned source_prefixlen,
|
||||
const char *out_interface,
|
||||
const union in_addr_union *destination,
|
||||
unsigned destination_prefixlen) {
|
||||
int fw_ctx_new(FirewallContext **ret) {
|
||||
_cleanup_free_ FirewallContext *ctx = NULL;
|
||||
|
||||
assert(entry);
|
||||
|
||||
if (out_interface && !ifname_valid(out_interface))
|
||||
return -EINVAL;
|
||||
if (in_interface && !ifname_valid(in_interface))
|
||||
return -EINVAL;
|
||||
|
||||
entry->ip.proto = protocol;
|
||||
|
||||
if (in_interface) {
|
||||
size_t l;
|
||||
|
||||
l = strlen(in_interface);
|
||||
assert(l < sizeof entry->ip.iniface);
|
||||
assert(l < sizeof entry->ip.iniface_mask);
|
||||
|
||||
strcpy(entry->ip.iniface, in_interface);
|
||||
memset(entry->ip.iniface_mask, 0xFF, l + 1);
|
||||
}
|
||||
if (source) {
|
||||
entry->ip.src = source->in;
|
||||
in4_addr_prefixlen_to_netmask(&entry->ip.smsk, source_prefixlen);
|
||||
}
|
||||
|
||||
if (out_interface) {
|
||||
size_t l = strlen(out_interface);
|
||||
assert(l < sizeof entry->ip.outiface);
|
||||
assert(l < sizeof entry->ip.outiface_mask);
|
||||
|
||||
strcpy(entry->ip.outiface, out_interface);
|
||||
memset(entry->ip.outiface_mask, 0xFF, l + 1);
|
||||
}
|
||||
if (destination) {
|
||||
entry->ip.dst = destination->in;
|
||||
in4_addr_prefixlen_to_netmask(&entry->ip.dmsk, destination_prefixlen);
|
||||
}
|
||||
ctx = new0(FirewallContext, 1);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
/* could probe here. However, this means that we will load
|
||||
* iptable_nat or nf_tables, both will enable connection tracking.
|
||||
*
|
||||
* Alternative would be to probe here but only call
|
||||
* fw_ctx_new when nspawn/networkd know they will call
|
||||
* fw_add_masquerade/local_dnat later anyway.
|
||||
*/
|
||||
*ret = TAKE_PTR(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
FirewallContext *fw_ctx_free(FirewallContext *ctx) {
|
||||
if (!ctx)
|
||||
return NULL;
|
||||
|
||||
if (ctx->firewall_backend == FW_BACKEND_NFTABLES)
|
||||
fw_nftables_exit(ctx);
|
||||
|
||||
return mfree(ctx);
|
||||
}
|
||||
|
||||
int fw_add_masquerade(
|
||||
FirewallContext **fw_ctx,
|
||||
bool add,
|
||||
int af,
|
||||
const union in_addr_union *source,
|
||||
unsigned source_prefixlen) {
|
||||
FirewallContext *ctx;
|
||||
int r;
|
||||
|
||||
static const xt_chainlabel chain = "POSTROUTING";
|
||||
_cleanup_(iptc_freep) struct xtc_handle *h = NULL;
|
||||
struct ipt_entry *entry, *mask;
|
||||
struct ipt_entry_target *t;
|
||||
size_t sz;
|
||||
struct nf_nat_ipv4_multi_range_compat *mr;
|
||||
int r, protocol = 0;
|
||||
const char *out_interface = NULL;
|
||||
const union in_addr_union *destination = NULL;
|
||||
unsigned destination_prefixlen = 0;
|
||||
|
||||
if (af != AF_INET)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
h = iptc_init("nat");
|
||||
if (!h)
|
||||
return -errno;
|
||||
|
||||
sz = XT_ALIGN(sizeof(struct ipt_entry)) +
|
||||
XT_ALIGN(sizeof(struct ipt_entry_target)) +
|
||||
XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
|
||||
|
||||
/* Put together the entry we want to add or remove */
|
||||
entry = alloca0(sz);
|
||||
entry->next_offset = sz;
|
||||
entry->target_offset = XT_ALIGN(sizeof(struct ipt_entry));
|
||||
r = entry_fill_basics(entry, protocol, NULL, source, source_prefixlen, out_interface, destination, destination_prefixlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Fill in target part */
|
||||
t = ipt_get_target(entry);
|
||||
t->u.target_size =
|
||||
XT_ALIGN(sizeof(struct ipt_entry_target)) +
|
||||
XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
|
||||
strncpy(t->u.user.name, "MASQUERADE", sizeof(t->u.user.name));
|
||||
mr = (struct nf_nat_ipv4_multi_range_compat*) t->data;
|
||||
mr->rangesize = 1;
|
||||
|
||||
/* Create a search mask entry */
|
||||
mask = alloca(sz);
|
||||
memset(mask, 0xFF, sz);
|
||||
|
||||
if (add) {
|
||||
if (iptc_check_entry(chain, entry, (unsigned char*) mask, h))
|
||||
return 0;
|
||||
if (errno != ENOENT) /* if other error than not existing yet, fail */
|
||||
return -errno;
|
||||
|
||||
if (!iptc_insert_entry(chain, entry, 0, h))
|
||||
return -errno;
|
||||
} else {
|
||||
if (!iptc_delete_entry(chain, entry, (unsigned char*) mask, h)) {
|
||||
if (errno == ENOENT) /* if it's already gone, all is good! */
|
||||
return 0;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
if (!*fw_ctx) {
|
||||
r = fw_ctx_new(fw_ctx);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!iptc_commit(h))
|
||||
return -errno;
|
||||
ctx = *fw_ctx;
|
||||
if (ctx->firewall_backend == FW_BACKEND_NONE)
|
||||
ctx->firewall_backend = firewall_backend_probe(ctx);
|
||||
|
||||
return 0;
|
||||
switch (ctx->firewall_backend) {
|
||||
case FW_BACKEND_NONE:
|
||||
return -EOPNOTSUPP;
|
||||
#if HAVE_LIBIPTC
|
||||
case FW_BACKEND_IPTABLES:
|
||||
return fw_iptables_add_masquerade(add, af, source, source_prefixlen);
|
||||
#endif
|
||||
case FW_BACKEND_NFTABLES:
|
||||
return fw_nftables_add_masquerade(ctx, add, af, source, source_prefixlen);
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int fw_add_local_dnat(
|
||||
FirewallContext **fw_ctx,
|
||||
bool add,
|
||||
int af,
|
||||
int protocol,
|
||||
@ -158,189 +88,28 @@ int fw_add_local_dnat(
|
||||
const union in_addr_union *remote,
|
||||
uint16_t remote_port,
|
||||
const union in_addr_union *previous_remote) {
|
||||
FirewallContext *ctx;
|
||||
|
||||
static const xt_chainlabel chain_pre = "PREROUTING", chain_output = "OUTPUT";
|
||||
_cleanup_(iptc_freep) struct xtc_handle *h = NULL;
|
||||
struct ipt_entry *entry, *mask;
|
||||
struct ipt_entry_target *t;
|
||||
struct ipt_entry_match *m;
|
||||
struct xt_addrtype_info_v1 *at;
|
||||
struct nf_nat_ipv4_multi_range_compat *mr;
|
||||
size_t sz, msz;
|
||||
int r;
|
||||
const char *in_interface = NULL;
|
||||
const union in_addr_union *source = NULL;
|
||||
unsigned source_prefixlen = 0;
|
||||
const union in_addr_union *destination = NULL;
|
||||
unsigned destination_prefixlen = 0;
|
||||
|
||||
assert(add || !previous_remote);
|
||||
|
||||
if (af != AF_INET)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!IN_SET(protocol, IPPROTO_TCP, IPPROTO_UDP))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (local_port <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (remote_port <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
h = iptc_init("nat");
|
||||
if (!h)
|
||||
return -errno;
|
||||
|
||||
sz = XT_ALIGN(sizeof(struct ipt_entry)) +
|
||||
XT_ALIGN(sizeof(struct ipt_entry_match)) +
|
||||
XT_ALIGN(sizeof(struct xt_addrtype_info_v1)) +
|
||||
XT_ALIGN(sizeof(struct ipt_entry_target)) +
|
||||
XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
|
||||
|
||||
if (protocol == IPPROTO_TCP)
|
||||
msz = XT_ALIGN(sizeof(struct ipt_entry_match)) +
|
||||
XT_ALIGN(sizeof(struct xt_tcp));
|
||||
else
|
||||
msz = XT_ALIGN(sizeof(struct ipt_entry_match)) +
|
||||
XT_ALIGN(sizeof(struct xt_udp));
|
||||
|
||||
sz += msz;
|
||||
|
||||
/* Fill in basic part */
|
||||
entry = alloca0(sz);
|
||||
entry->next_offset = sz;
|
||||
entry->target_offset =
|
||||
XT_ALIGN(sizeof(struct ipt_entry)) +
|
||||
XT_ALIGN(sizeof(struct ipt_entry_match)) +
|
||||
XT_ALIGN(sizeof(struct xt_addrtype_info_v1)) +
|
||||
msz;
|
||||
r = entry_fill_basics(entry, protocol, in_interface, source, source_prefixlen, NULL, destination, destination_prefixlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Fill in first match */
|
||||
m = (struct ipt_entry_match*) ((uint8_t*) entry + XT_ALIGN(sizeof(struct ipt_entry)));
|
||||
m->u.match_size = msz;
|
||||
if (protocol == IPPROTO_TCP) {
|
||||
struct xt_tcp *tcp;
|
||||
|
||||
strncpy(m->u.user.name, "tcp", sizeof(m->u.user.name));
|
||||
tcp = (struct xt_tcp*) m->data;
|
||||
tcp->dpts[0] = tcp->dpts[1] = local_port;
|
||||
tcp->spts[0] = 0;
|
||||
tcp->spts[1] = 0xFFFF;
|
||||
|
||||
} else {
|
||||
struct xt_udp *udp;
|
||||
|
||||
strncpy(m->u.user.name, "udp", sizeof(m->u.user.name));
|
||||
udp = (struct xt_udp*) m->data;
|
||||
udp->dpts[0] = udp->dpts[1] = local_port;
|
||||
udp->spts[0] = 0;
|
||||
udp->spts[1] = 0xFFFF;
|
||||
if (!*fw_ctx) {
|
||||
int ret = fw_ctx_new(fw_ctx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Fill in second match */
|
||||
m = (struct ipt_entry_match*) ((uint8_t*) entry + XT_ALIGN(sizeof(struct ipt_entry)) + msz);
|
||||
m->u.match_size =
|
||||
XT_ALIGN(sizeof(struct ipt_entry_match)) +
|
||||
XT_ALIGN(sizeof(struct xt_addrtype_info_v1));
|
||||
strncpy(m->u.user.name, "addrtype", sizeof(m->u.user.name));
|
||||
m->u.user.revision = 1;
|
||||
at = (struct xt_addrtype_info_v1*) m->data;
|
||||
at->dest = XT_ADDRTYPE_LOCAL;
|
||||
ctx = *fw_ctx;
|
||||
if (ctx->firewall_backend == FW_BACKEND_NONE)
|
||||
ctx->firewall_backend = firewall_backend_probe(ctx);
|
||||
|
||||
/* Fill in target part */
|
||||
t = ipt_get_target(entry);
|
||||
t->u.target_size =
|
||||
XT_ALIGN(sizeof(struct ipt_entry_target)) +
|
||||
XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat));
|
||||
strncpy(t->u.user.name, "DNAT", sizeof(t->u.user.name));
|
||||
mr = (struct nf_nat_ipv4_multi_range_compat*) t->data;
|
||||
mr->rangesize = 1;
|
||||
mr->range[0].flags = NF_NAT_RANGE_PROTO_SPECIFIED|NF_NAT_RANGE_MAP_IPS;
|
||||
mr->range[0].min_ip = mr->range[0].max_ip = remote->in.s_addr;
|
||||
if (protocol == IPPROTO_TCP)
|
||||
mr->range[0].min.tcp.port = mr->range[0].max.tcp.port = htobe16(remote_port);
|
||||
else
|
||||
mr->range[0].min.udp.port = mr->range[0].max.udp.port = htobe16(remote_port);
|
||||
|
||||
mask = alloca0(sz);
|
||||
memset(mask, 0xFF, sz);
|
||||
|
||||
if (add) {
|
||||
/* Add the PREROUTING rule, if it is missing so far */
|
||||
if (!iptc_check_entry(chain_pre, entry, (unsigned char*) mask, h)) {
|
||||
if (errno != ENOENT)
|
||||
return -EINVAL;
|
||||
|
||||
if (!iptc_insert_entry(chain_pre, entry, 0, h))
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* If a previous remote is set, remove its entry */
|
||||
if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) {
|
||||
mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr;
|
||||
|
||||
if (!iptc_delete_entry(chain_pre, entry, (unsigned char*) mask, h)) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
mr->range[0].min_ip = mr->range[0].max_ip = remote->in.s_addr;
|
||||
}
|
||||
|
||||
/* Add the OUTPUT rule, if it is missing so far */
|
||||
if (!in_interface) {
|
||||
|
||||
/* Don't apply onto loopback addresses */
|
||||
if (!destination) {
|
||||
entry->ip.dst.s_addr = htobe32(0x7F000000);
|
||||
entry->ip.dmsk.s_addr = htobe32(0xFF000000);
|
||||
entry->ip.invflags = IPT_INV_DSTIP;
|
||||
}
|
||||
|
||||
if (!iptc_check_entry(chain_output, entry, (unsigned char*) mask, h)) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
if (!iptc_insert_entry(chain_output, entry, 0, h))
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* If a previous remote is set, remove its entry */
|
||||
if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) {
|
||||
mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr;
|
||||
|
||||
if (!iptc_delete_entry(chain_output, entry, (unsigned char*) mask, h)) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!iptc_delete_entry(chain_pre, entry, (unsigned char*) mask, h)) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (!in_interface) {
|
||||
if (!destination) {
|
||||
entry->ip.dst.s_addr = htobe32(0x7F000000);
|
||||
entry->ip.dmsk.s_addr = htobe32(0xFF000000);
|
||||
entry->ip.invflags = IPT_INV_DSTIP;
|
||||
}
|
||||
|
||||
if (!iptc_delete_entry(chain_output, entry, (unsigned char*) mask, h)) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
switch (ctx->firewall_backend) {
|
||||
case FW_BACKEND_NONE:
|
||||
return -EOPNOTSUPP;
|
||||
case FW_BACKEND_NFTABLES:
|
||||
return fw_nftables_add_local_dnat(ctx, add, af, protocol, local_port, remote, remote_port, previous_remote);
|
||||
#if HAVE_LIBIPTC
|
||||
case FW_BACKEND_IPTABLES:
|
||||
return fw_iptables_add_local_dnat(add, af, protocol, local_port, remote, remote_port, previous_remote);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!iptc_commit(h))
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
@ -6,15 +6,22 @@
|
||||
|
||||
#include "in-addr-util.h"
|
||||
|
||||
#if HAVE_LIBIPTC
|
||||
typedef struct FirewallContext FirewallContext;
|
||||
|
||||
int fw_ctx_new(FirewallContext **ret);
|
||||
FirewallContext *fw_ctx_free(FirewallContext *fw_ctx);
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(FirewallContext *, fw_ctx_free);
|
||||
|
||||
int fw_add_masquerade(
|
||||
FirewallContext **fw_ctx,
|
||||
bool add,
|
||||
int af,
|
||||
const union in_addr_union *source,
|
||||
unsigned source_prefixlen);
|
||||
|
||||
int fw_add_local_dnat(
|
||||
FirewallContext **fw_ctx,
|
||||
bool add,
|
||||
int af,
|
||||
int protocol,
|
||||
@ -22,26 +29,3 @@ int fw_add_local_dnat(
|
||||
const union in_addr_union *remote,
|
||||
uint16_t remote_port,
|
||||
const union in_addr_union *previous_remote);
|
||||
|
||||
#else
|
||||
|
||||
static inline int fw_add_masquerade(
|
||||
bool add,
|
||||
int af,
|
||||
const union in_addr_union *source,
|
||||
unsigned source_prefixlen) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int fw_add_local_dnat(
|
||||
bool add,
|
||||
int af,
|
||||
int protocol,
|
||||
uint16_t local_port,
|
||||
const union in_addr_union *remote,
|
||||
uint16_t remote_port,
|
||||
const union in_addr_union *previous_remote) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
236
src/shared/hostname-setup.c
Normal file
236
src/shared/hostname-setup.c
Normal file
@ -0,0 +1,236 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "hostname-setup.h"
|
||||
#include "hostname-util.h"
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "proc-cmdline.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "util.h"
|
||||
|
||||
static int sethostname_idempotent_full(const char *s, bool really) {
|
||||
char buf[HOST_NAME_MAX + 1] = {};
|
||||
|
||||
assert(s);
|
||||
|
||||
if (gethostname(buf, sizeof(buf) - 1) < 0)
|
||||
return -errno;
|
||||
|
||||
if (streq(buf, s))
|
||||
return 0;
|
||||
|
||||
if (really &&
|
||||
sethostname(s, strlen(s)) < 0)
|
||||
return -errno;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sethostname_idempotent(const char *s) {
|
||||
return sethostname_idempotent_full(s, true);
|
||||
}
|
||||
|
||||
bool get_hostname_filtered(char ret[static HOST_NAME_MAX + 1]) {
|
||||
char buf[HOST_NAME_MAX + 1] = {};
|
||||
|
||||
/* Returns true if we got a good hostname, false otherwise. */
|
||||
|
||||
if (gethostname(buf, sizeof(buf) - 1) < 0)
|
||||
return false; /* This can realistically only fail with ENAMETOOLONG.
|
||||
* Let's treat that case the same as an invalid hostname. */
|
||||
|
||||
if (isempty(buf))
|
||||
return false;
|
||||
|
||||
/* This is the built-in kernel default hostname */
|
||||
if (streq(buf, "(none)"))
|
||||
return false;
|
||||
|
||||
memcpy(ret, buf, sizeof buf);
|
||||
return true;
|
||||
}
|
||||
|
||||
int shorten_overlong(const char *s, char **ret) {
|
||||
char *h, *p;
|
||||
|
||||
/* Shorten an overlong name to HOST_NAME_MAX or to the first dot,
|
||||
* whatever comes earlier. */
|
||||
|
||||
assert(s);
|
||||
|
||||
h = strdup(s);
|
||||
if (!h)
|
||||
return -ENOMEM;
|
||||
|
||||
if (hostname_is_valid(h, 0)) {
|
||||
*ret = h;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = strchr(h, '.');
|
||||
if (p)
|
||||
*p = 0;
|
||||
|
||||
strshorten(h, HOST_NAME_MAX);
|
||||
|
||||
if (!hostname_is_valid(h, 0)) {
|
||||
free(h);
|
||||
return -EDOM;
|
||||
}
|
||||
|
||||
*ret = h;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int read_etc_hostname_stream(FILE *f, char **ret) {
|
||||
int r;
|
||||
|
||||
assert(f);
|
||||
assert(ret);
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
char *p;
|
||||
|
||||
r = read_line(f, LONG_LINE_MAX, &line);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) /* EOF without any hostname? the file is empty, let's treat that exactly like no file at all: ENOENT */
|
||||
return -ENOENT;
|
||||
|
||||
p = strstrip(line);
|
||||
|
||||
/* File may have empty lines or comments, ignore them */
|
||||
if (!IN_SET(*p, '\0', '#')) {
|
||||
char *copy;
|
||||
|
||||
hostname_cleanup(p); /* normalize the hostname */
|
||||
|
||||
if (!hostname_is_valid(p, VALID_HOSTNAME_TRAILING_DOT)) /* check that the hostname we return is valid */
|
||||
return -EBADMSG;
|
||||
|
||||
copy = strdup(p);
|
||||
if (!copy)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = copy;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int read_etc_hostname(const char *path, char **ret) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (!path)
|
||||
path = "/etc/hostname";
|
||||
|
||||
f = fopen(path, "re");
|
||||
if (!f)
|
||||
return -errno;
|
||||
|
||||
return read_etc_hostname_stream(f, ret);
|
||||
}
|
||||
|
||||
void hostname_update_source_hint(const char *hostname, HostnameSource source) {
|
||||
int r;
|
||||
|
||||
/* Why save the value and not just create a flag file? This way we will
|
||||
* notice if somebody sets the hostname directly (not going through hostnamed).
|
||||
*/
|
||||
|
||||
if (source == HOSTNAME_FALLBACK) {
|
||||
r = write_string_file("/run/systemd/fallback-hostname", hostname,
|
||||
WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_ATOMIC);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to create \"/run/systemd/fallback-hostname\": %m");
|
||||
} else
|
||||
unlink_or_warn("/run/systemd/fallback-hostname");
|
||||
}
|
||||
|
||||
int hostname_setup(bool really) {
|
||||
_cleanup_free_ char *b = NULL;
|
||||
const char *hn = NULL;
|
||||
HostnameSource source;
|
||||
bool enoent = false;
|
||||
int r;
|
||||
|
||||
r = proc_cmdline_get_key("systemd.hostname", 0, &b);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to retrieve system hostname from kernel command line, ignoring: %m");
|
||||
else if (r > 0) {
|
||||
if (hostname_is_valid(b, true)) {
|
||||
hn = b;
|
||||
source = HOSTNAME_TRANSIENT;
|
||||
} else {
|
||||
log_warning("Hostname specified on kernel command line is invalid, ignoring: %s", b);
|
||||
b = mfree(b);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hn) {
|
||||
r = read_etc_hostname(NULL, &b);
|
||||
if (r < 0) {
|
||||
if (r == -ENOENT)
|
||||
enoent = true;
|
||||
else
|
||||
log_warning_errno(r, "Failed to read configured hostname: %m");
|
||||
} else {
|
||||
hn = b;
|
||||
source = HOSTNAME_STATIC;
|
||||
}
|
||||
}
|
||||
|
||||
if (isempty(hn)) {
|
||||
/* Don't override the hostname if it is already set and not explicitly configured */
|
||||
|
||||
char buf[HOST_NAME_MAX + 1] = {};
|
||||
if (get_hostname_filtered(buf)) {
|
||||
log_debug("No hostname configured, leaving existing hostname <%s> in place.", buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (enoent)
|
||||
log_info("No hostname configured, using fallback hostname.");
|
||||
|
||||
hn = FALLBACK_HOSTNAME;
|
||||
source = HOSTNAME_FALLBACK;
|
||||
|
||||
}
|
||||
|
||||
r = sethostname_idempotent_full(hn, really);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Failed to set hostname to <%s>: %m", hn);
|
||||
if (r == 0)
|
||||
log_debug("Hostname was already set to <%s>.", hn);
|
||||
else
|
||||
log_info("Hostname %s to <%s>.",
|
||||
really ? "set" : "would have been set",
|
||||
hn);
|
||||
|
||||
if (really)
|
||||
hostname_update_source_hint(hn, source);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static const char* const hostname_source_table[] = {
|
||||
[HOSTNAME_STATIC] = "static",
|
||||
[HOSTNAME_TRANSIENT] = "transient",
|
||||
[HOSTNAME_FALLBACK] = "fallback",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(hostname_source, HostnameSource);
|
||||
24
src/shared/hostname-setup.h
Normal file
24
src/shared/hostname-setup.h
Normal file
@ -0,0 +1,24 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef enum HostnameSource {
|
||||
HOSTNAME_STATIC, /* from /etc/hostname */
|
||||
HOSTNAME_TRANSIENT, /* a transient hostname set through systemd, hostnamed, the container manager, or otherwise */
|
||||
HOSTNAME_FALLBACK, /* the compiled-in fallback was used */
|
||||
_HOSTNAME_INVALID = -1,
|
||||
} HostnameSource;
|
||||
|
||||
const char* hostname_source_to_string(HostnameSource source);
|
||||
int sethostname_idempotent(const char *s);
|
||||
|
||||
int shorten_overlong(const char *s, char **ret);
|
||||
|
||||
int read_etc_hostname_stream(FILE *f, char **ret);
|
||||
int read_etc_hostname(const char *path, char **ret);
|
||||
|
||||
bool get_hostname_filtered(char ret[static HOST_NAME_MAX + 1]);
|
||||
void hostname_update_source_hint(const char *hostname, HostnameSource source);
|
||||
int hostname_setup(bool really);
|
||||
@ -137,41 +137,3 @@ int journal_access_check_and_warn(sd_journal *j, bool quiet, bool want_other_use
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
bool journal_field_valid(const char *p, size_t l, bool allow_protected) {
|
||||
const char *a;
|
||||
|
||||
/* We kinda enforce POSIX syntax recommendations for
|
||||
environment variables here, but make a couple of additional
|
||||
requirements.
|
||||
|
||||
http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */
|
||||
|
||||
if (l == (size_t) -1)
|
||||
l = strlen(p);
|
||||
|
||||
/* No empty field names */
|
||||
if (l <= 0)
|
||||
return false;
|
||||
|
||||
/* Don't allow names longer than 64 chars */
|
||||
if (l > 64)
|
||||
return false;
|
||||
|
||||
/* Variables starting with an underscore are protected */
|
||||
if (!allow_protected && p[0] == '_')
|
||||
return false;
|
||||
|
||||
/* Don't allow digits as first character */
|
||||
if (p[0] >= '0' && p[0] <= '9')
|
||||
return false;
|
||||
|
||||
/* Only allow A-Z0-9 and '_' */
|
||||
for (a = p; a < p + l; a++)
|
||||
if ((*a < 'A' || *a > 'Z') &&
|
||||
(*a < '0' || *a > '9') &&
|
||||
*a != '_')
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -6,6 +6,5 @@
|
||||
|
||||
#include "sd-journal.h"
|
||||
|
||||
bool journal_field_valid(const char *p, size_t l, bool allow_protected);
|
||||
int journal_access_blocked(sd_journal *j);
|
||||
int journal_access_check_and_warn(sd_journal *j, bool quiet, bool want_other_users);
|
||||
|
||||
@ -1495,7 +1495,7 @@ static void json_format_string(FILE *f, const char *q, JsonFormatFlags flags) {
|
||||
fputc('"', f);
|
||||
|
||||
if (flags & JSON_FORMAT_COLOR)
|
||||
fputs(ANSI_GREEN, f);
|
||||
fputs(ansi_green(), f);
|
||||
|
||||
for (; *q; q++)
|
||||
switch (*q) {
|
||||
@ -1557,7 +1557,7 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha
|
||||
return -errno;
|
||||
|
||||
if (flags & JSON_FORMAT_COLOR)
|
||||
fputs(ANSI_HIGHLIGHT_BLUE, f);
|
||||
fputs(ansi_highlight_blue(), f);
|
||||
|
||||
fprintf(f, "%.*Le", DECIMAL_DIG, json_variant_real(v));
|
||||
|
||||
@ -1570,7 +1570,7 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha
|
||||
|
||||
case JSON_VARIANT_INTEGER:
|
||||
if (flags & JSON_FORMAT_COLOR)
|
||||
fputs(ANSI_HIGHLIGHT_BLUE, f);
|
||||
fputs(ansi_highlight_blue(), f);
|
||||
|
||||
fprintf(f, "%" PRIdMAX, json_variant_integer(v));
|
||||
|
||||
@ -1580,7 +1580,7 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha
|
||||
|
||||
case JSON_VARIANT_UNSIGNED:
|
||||
if (flags & JSON_FORMAT_COLOR)
|
||||
fputs(ANSI_HIGHLIGHT_BLUE, f);
|
||||
fputs(ansi_highlight_blue(), f);
|
||||
|
||||
fprintf(f, "%" PRIuMAX, json_variant_unsigned(v));
|
||||
|
||||
|
||||
@ -67,7 +67,7 @@ static int print_catalog(FILE *f, sd_journal *j) {
|
||||
prefix = "--";
|
||||
|
||||
if (colors_enabled())
|
||||
newline = strjoina(ANSI_NORMAL "\n" ANSI_GREY, prefix, ANSI_NORMAL " " ANSI_GREEN);
|
||||
newline = strjoina(ANSI_NORMAL "\n", ansi_grey(), prefix, ANSI_NORMAL " ", ansi_green());
|
||||
else
|
||||
newline = strjoina("\n", prefix, " ");
|
||||
|
||||
@ -76,7 +76,7 @@ static int print_catalog(FILE *f, sd_journal *j) {
|
||||
return log_oom();
|
||||
|
||||
if (colors_enabled())
|
||||
fprintf(f, ANSI_GREY "%s" ANSI_NORMAL " " ANSI_GREEN, prefix);
|
||||
fprintf(f, "%s%s %s%s", ansi_grey(), prefix, ANSI_NORMAL, ansi_green());
|
||||
else
|
||||
fprintf(f, "%s ", prefix);
|
||||
|
||||
@ -702,9 +702,11 @@ static int output_verbose(
|
||||
|
||||
c = memchr(data, '=', length);
|
||||
if (!c)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Invalid field.");
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid field.");
|
||||
|
||||
fieldlen = c - (const char*) data;
|
||||
if (!journal_field_valid(data, fieldlen, true))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid field.");
|
||||
|
||||
r = field_set_test(output_fields, data, fieldlen);
|
||||
if (r < 0)
|
||||
@ -798,6 +800,7 @@ static int output_export(
|
||||
sd_id128_to_string(boot_id, sid));
|
||||
|
||||
JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
|
||||
size_t fieldlen;
|
||||
const char *c;
|
||||
|
||||
/* We already printed the boot id from the data in the header, hence let's suppress it here */
|
||||
@ -806,10 +809,13 @@ static int output_export(
|
||||
|
||||
c = memchr(data, '=', length);
|
||||
if (!c)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Invalid field.");
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid field.");
|
||||
|
||||
r = field_set_test(output_fields, data, c - (const char *) data);
|
||||
fieldlen = c - (const char*) data;
|
||||
if (!journal_field_valid(data, fieldlen, true))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid field.");
|
||||
|
||||
r = field_set_test(output_fields, data, fieldlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!r)
|
||||
@ -820,11 +826,11 @@ static int output_export(
|
||||
else {
|
||||
uint64_t le64;
|
||||
|
||||
fwrite(data, c - (const char*) data, 1, f);
|
||||
fwrite(data, fieldlen, 1, f);
|
||||
fputc('\n', f);
|
||||
le64 = htole64(length - (c - (const char*) data) - 1);
|
||||
le64 = htole64(length - fieldlen - 1);
|
||||
fwrite(&le64, sizeof(le64), 1, f);
|
||||
fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f);
|
||||
fwrite(c + 1, length - fieldlen - 1, 1, f);
|
||||
}
|
||||
|
||||
fputc('\n', f);
|
||||
@ -961,6 +967,7 @@ static int update_json_data_split(
|
||||
const void *data,
|
||||
size_t size) {
|
||||
|
||||
size_t fieldlen;
|
||||
const char *eq;
|
||||
char *name;
|
||||
|
||||
@ -974,14 +981,15 @@ static int update_json_data_split(
|
||||
if (!eq)
|
||||
return 0;
|
||||
|
||||
if (eq == data)
|
||||
return 0;
|
||||
fieldlen = eq - (const char*) data;
|
||||
if (!journal_field_valid(data, fieldlen, true))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid field.");
|
||||
|
||||
name = strndupa(data, eq - (const char*) data);
|
||||
name = strndupa(data, fieldlen);
|
||||
if (output_fields && !set_contains(output_fields, name))
|
||||
return 0;
|
||||
|
||||
return update_json_data(h, flags, name, eq + 1, size - (eq - (const char*) data) - 1);
|
||||
return update_json_data(h, flags, name, eq + 1, size - fieldlen - 1);
|
||||
}
|
||||
|
||||
static int output_json(
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "hashmap.h"
|
||||
#include "hostname-util.h"
|
||||
#include "hostname-setup.h"
|
||||
#include "id128-util.h"
|
||||
#include "lockfile-util.h"
|
||||
#include "log.h"
|
||||
|
||||
@ -101,7 +101,10 @@ shared_sources = files('''
|
||||
fdset.h
|
||||
fileio-label.c
|
||||
fileio-label.h
|
||||
firewall-util.c
|
||||
firewall-util-nft.c
|
||||
firewall-util.h
|
||||
firewall-util-private.h
|
||||
format-table.c
|
||||
format-table.h
|
||||
fsck-util.h
|
||||
@ -115,6 +118,8 @@ shared_sources = files('''
|
||||
gpt.h
|
||||
group-record.c
|
||||
group-record.h
|
||||
hostname-setup.c
|
||||
hostname-setup.h
|
||||
id128-print.c
|
||||
id128-print.h
|
||||
idn-util.c
|
||||
@ -293,7 +298,7 @@ if conf.get('HAVE_SECCOMP') == 1
|
||||
endif
|
||||
|
||||
if conf.get('HAVE_LIBIPTC') == 1
|
||||
shared_sources += files('firewall-util.c')
|
||||
shared_sources += files('firewall-util-iptables.c')
|
||||
endif
|
||||
|
||||
if conf.get('HAVE_KMOD') == 1
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
#include "hexdecoct.h"
|
||||
#include "hostname-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "journal-util.h"
|
||||
#include "journal-file.h"
|
||||
#include "list.h"
|
||||
#include "locale-util.h"
|
||||
#include "memory-util.h"
|
||||
|
||||
@ -60,11 +60,13 @@ sd_netlink *sd_netlink_ref(sd_netlink *nl);
|
||||
sd_netlink *sd_netlink_unref(sd_netlink *nl);
|
||||
|
||||
int sd_netlink_send(sd_netlink *nl, sd_netlink_message *message, uint32_t *serial);
|
||||
int sd_netlink_sendv(sd_netlink *nl, sd_netlink_message *messages[], size_t msgcnt, uint32_t **ret_serial);
|
||||
int sd_netlink_call_async(sd_netlink *nl, sd_netlink_slot **ret_slot, sd_netlink_message *message,
|
||||
sd_netlink_message_handler_t callback, sd_netlink_destroy_t destoy_callback,
|
||||
void *userdata, uint64_t usec, const char *description);
|
||||
int sd_netlink_call(sd_netlink *nl, sd_netlink_message *message, uint64_t timeout,
|
||||
sd_netlink_message **reply);
|
||||
int sd_netlink_read(sd_netlink *nl, uint32_t serial, uint64_t timeout, sd_netlink_message **reply);
|
||||
|
||||
int sd_netlink_get_events(const sd_netlink *nl);
|
||||
int sd_netlink_get_timeout(const sd_netlink *nl, uint64_t *timeout);
|
||||
@ -196,18 +198,18 @@ int sd_rtnl_message_addrlabel_set_prefixlen(sd_netlink_message *m, unsigned char
|
||||
int sd_rtnl_message_addrlabel_get_prefixlen(const sd_netlink_message *m, unsigned char *prefixlen);
|
||||
|
||||
int sd_rtnl_message_new_routing_policy_rule(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int ifal_family);
|
||||
int sd_rtnl_message_routing_policy_rule_set_tos(sd_netlink_message *m, unsigned char tos);
|
||||
int sd_rtnl_message_routing_policy_rule_get_tos(const sd_netlink_message *m, unsigned char *tos);
|
||||
int sd_rtnl_message_routing_policy_rule_set_table(sd_netlink_message *m, unsigned char table);
|
||||
int sd_rtnl_message_routing_policy_rule_get_table(const sd_netlink_message *m, unsigned char *table);
|
||||
int sd_rtnl_message_routing_policy_rule_set_rtm_src_prefixlen(sd_netlink_message *m, unsigned char len);
|
||||
int sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(const sd_netlink_message *m, unsigned char *len);
|
||||
int sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(sd_netlink_message *m, unsigned char len);
|
||||
int sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(const sd_netlink_message *m, unsigned char *len);
|
||||
int sd_rtnl_message_routing_policy_rule_set_rtm_type(sd_netlink_message *m, unsigned char type);
|
||||
int sd_rtnl_message_routing_policy_rule_get_rtm_type(const sd_netlink_message *m, unsigned char *type);
|
||||
int sd_rtnl_message_routing_policy_rule_set_flags(sd_netlink_message *m, unsigned flags);
|
||||
int sd_rtnl_message_routing_policy_rule_get_flags(const sd_netlink_message *m, unsigned *flags);
|
||||
int sd_rtnl_message_routing_policy_rule_set_tos(sd_netlink_message *m, uint8_t tos);
|
||||
int sd_rtnl_message_routing_policy_rule_get_tos(const sd_netlink_message *m, uint8_t *tos);
|
||||
int sd_rtnl_message_routing_policy_rule_set_table(sd_netlink_message *m, uint8_t table);
|
||||
int sd_rtnl_message_routing_policy_rule_get_table(const sd_netlink_message *m, uint8_t *table);
|
||||
int sd_rtnl_message_routing_policy_rule_set_fib_src_prefixlen(sd_netlink_message *m, uint8_t len);
|
||||
int sd_rtnl_message_routing_policy_rule_get_fib_src_prefixlen(const sd_netlink_message *m, uint8_t *len);
|
||||
int sd_rtnl_message_routing_policy_rule_set_fib_dst_prefixlen(sd_netlink_message *m, uint8_t len);
|
||||
int sd_rtnl_message_routing_policy_rule_get_fib_dst_prefixlen(const sd_netlink_message *m, uint8_t *len);
|
||||
int sd_rtnl_message_routing_policy_rule_set_fib_type(sd_netlink_message *m, uint8_t type);
|
||||
int sd_rtnl_message_routing_policy_rule_get_fib_type(const sd_netlink_message *m, uint8_t *type);
|
||||
int sd_rtnl_message_routing_policy_rule_set_flags(sd_netlink_message *m, uint32_t flags);
|
||||
int sd_rtnl_message_routing_policy_rule_get_flags(const sd_netlink_message *m, uint32_t *flags);
|
||||
|
||||
int sd_rtnl_message_new_qdisc(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int tcm_family, int tcm_ifindex);
|
||||
int sd_rtnl_message_set_qdisc_parent(sd_netlink_message *m, uint32_t parent);
|
||||
@ -219,6 +221,32 @@ int sd_rtnl_message_set_tclass_handle(sd_netlink_message *m, uint32_t handle);
|
||||
|
||||
int sd_rtnl_message_new_mdb(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int mdb_ifindex);
|
||||
|
||||
/* nfnl */
|
||||
int sd_nfnl_socket_open(sd_netlink **nl);
|
||||
int sd_nfnl_message_batch_begin(sd_netlink *nfnl, sd_netlink_message **ret);
|
||||
int sd_nfnl_message_batch_end(sd_netlink *nfnl, sd_netlink_message **ret);
|
||||
int sd_nfnl_nft_message_del_table(sd_netlink *nfnl, sd_netlink_message **ret,
|
||||
int family, const char *table);
|
||||
int sd_nfnl_nft_message_new_table(sd_netlink *nfnl, sd_netlink_message **ret,
|
||||
int family, const char *table, uint16_t nl_flags);
|
||||
int sd_nfnl_nft_message_new_basechain(sd_netlink *nfnl, sd_netlink_message **ret,
|
||||
int family, const char *table, const char *chain,
|
||||
const char *type, uint8_t hook, int prio);
|
||||
int sd_nfnl_nft_message_new_rule(sd_netlink *nfnl, sd_netlink_message **ret,
|
||||
int family, const char *table, const char *chain);
|
||||
int sd_nfnl_nft_message_new_set(sd_netlink *nfnl, sd_netlink_message **ret,
|
||||
int family, const char *table, const char *set_name,
|
||||
uint32_t setid, uint32_t klen);
|
||||
int sd_nfnl_nft_message_new_setelems_begin(sd_netlink *nfnl, sd_netlink_message **ret,
|
||||
int family, const char *table, const char *set_name);
|
||||
int sd_nfnl_nft_message_del_setelems_begin(sd_netlink *nfnl, sd_netlink_message **ret,
|
||||
int family, const char *table, const char *set_name);
|
||||
int sd_nfnl_nft_message_add_setelem(sd_netlink_message *m,
|
||||
uint32_t num,
|
||||
const void *key, uint32_t klen,
|
||||
const void *data, uint32_t dlen);
|
||||
int sd_nfnl_nft_message_add_setelem_end(sd_netlink_message *m);
|
||||
|
||||
/* genl */
|
||||
int sd_genl_socket_open(sd_netlink **nl);
|
||||
int sd_genl_message_new(sd_netlink *nl, sd_genl_family family, uint8_t cmd, sd_netlink_message **m);
|
||||
|
||||
@ -105,17 +105,6 @@ tests += [
|
||||
libmount,
|
||||
libblkid]],
|
||||
|
||||
[['src/test/test-hostname.c'],
|
||||
[libcore,
|
||||
libshared],
|
||||
[threads,
|
||||
librt,
|
||||
libseccomp,
|
||||
libselinux,
|
||||
libmount,
|
||||
libblkid],
|
||||
'', 'unsafe'],
|
||||
|
||||
[['src/test/test-dns-domain.c'],
|
||||
[libcore,
|
||||
libshared],
|
||||
@ -339,6 +328,10 @@ tests += [
|
||||
[],
|
||||
[]],
|
||||
|
||||
[['src/test/test-hostname-setup.c'],
|
||||
[],
|
||||
[]],
|
||||
|
||||
[['src/test/test-hostname-util.c'],
|
||||
[],
|
||||
[]],
|
||||
@ -566,8 +559,7 @@ tests += [
|
||||
|
||||
[['src/test/test-firewall-util.c'],
|
||||
[libshared],
|
||||
[],
|
||||
'HAVE_LIBIPTC'],
|
||||
[]],
|
||||
|
||||
[['src/test/test-netlink-manual.c'],
|
||||
[],
|
||||
|
||||
@ -7,34 +7,53 @@
|
||||
#define MAKE_IN_ADDR_UNION(a,b,c,d) (union in_addr_union) { .in.s_addr = htobe32((uint32_t) (a) << 24 | (uint32_t) (b) << 16 | (uint32_t) (c) << 8 | (uint32_t) (d))}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
_cleanup_(fw_ctx_freep) FirewallContext *ctx;
|
||||
int r;
|
||||
test_setup_logging(LOG_DEBUG);
|
||||
uint8_t prefixlen = 32;
|
||||
|
||||
r = fw_add_masquerade(true, AF_INET, NULL, 0);
|
||||
r = fw_ctx_new(&ctx);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to init firewall: %m");
|
||||
|
||||
r = fw_add_masquerade(&ctx, true, AF_INET, NULL, 0);
|
||||
if (r == 0)
|
||||
log_error("Expected failure: NULL source");
|
||||
|
||||
r = fw_add_masquerade(&ctx, true, AF_INET, &MAKE_IN_ADDR_UNION(10,1,2,0), 0);
|
||||
if (r == 0)
|
||||
log_error("Expected failure: 0 prefixlen");
|
||||
|
||||
r = fw_add_masquerade(&ctx, true, AF_INET, &MAKE_IN_ADDR_UNION(10,1,2,3), prefixlen);
|
||||
if (r < 0)
|
||||
log_error_errno(r, "Failed to modify firewall: %m");
|
||||
|
||||
r = fw_add_masquerade(true, AF_INET, NULL, 0);
|
||||
prefixlen = 28;
|
||||
r = fw_add_masquerade(&ctx, true, AF_INET, &MAKE_IN_ADDR_UNION(10,0,2,0), prefixlen);
|
||||
if (r < 0)
|
||||
log_error_errno(r, "Failed to modify firewall: %m");
|
||||
|
||||
r = fw_add_masquerade(false, AF_INET, NULL, 0);
|
||||
r = fw_add_masquerade(&ctx, false, AF_INET, &MAKE_IN_ADDR_UNION(10,0,2,0), prefixlen);
|
||||
if (r < 0)
|
||||
log_error_errno(r, "Failed to modify firewall: %m");
|
||||
|
||||
r = fw_add_local_dnat(true, AF_INET, IPPROTO_TCP, 4711, &MAKE_IN_ADDR_UNION(1, 2, 3, 4), 815, NULL);
|
||||
r = fw_add_masquerade(&ctx, false, AF_INET, &MAKE_IN_ADDR_UNION(10,1,2,3), 32);
|
||||
if (r < 0)
|
||||
log_error_errno(r, "Failed to modify firewall: %m");
|
||||
|
||||
r = fw_add_local_dnat(true, AF_INET, IPPROTO_TCP, 4711, &MAKE_IN_ADDR_UNION(1, 2, 3, 4), 815, NULL);
|
||||
r = fw_add_local_dnat(&ctx, true, AF_INET, IPPROTO_TCP, 4711, &MAKE_IN_ADDR_UNION(1, 2, 3, 4), 815, NULL);
|
||||
if (r < 0)
|
||||
log_error_errno(r, "Failed to modify firewall: %m");
|
||||
|
||||
r = fw_add_local_dnat(true, AF_INET, IPPROTO_TCP, 4711, &MAKE_IN_ADDR_UNION(1, 2, 3, 5), 815, &MAKE_IN_ADDR_UNION(1, 2, 3, 4));
|
||||
r = fw_add_local_dnat(&ctx, true, AF_INET, IPPROTO_TCP, 4711, &MAKE_IN_ADDR_UNION(1, 2, 3, 4), 815, NULL);
|
||||
if (r < 0)
|
||||
log_error_errno(r, "Failed to modify firewall: %m");
|
||||
|
||||
r = fw_add_local_dnat(false, AF_INET, IPPROTO_TCP, 4711, &MAKE_IN_ADDR_UNION(1, 2, 3, 5), 815, NULL);
|
||||
r = fw_add_local_dnat(&ctx, true, AF_INET, IPPROTO_TCP, 4711, &MAKE_IN_ADDR_UNION(1, 2, 3, 5), 815, &MAKE_IN_ADDR_UNION(1, 2, 3, 4));
|
||||
if (r < 0)
|
||||
log_error_errno(r, "Failed to modify firewall: %m");
|
||||
|
||||
r = fw_add_local_dnat(&ctx, false, AF_INET, IPPROTO_TCP, 4711, &MAKE_IN_ADDR_UNION(1, 2, 3, 5), 815, NULL);
|
||||
if (r < 0)
|
||||
log_error_errno(r, "Failed to modify firewall: %m");
|
||||
|
||||
|
||||
72
src/test/test-hostname-setup.c
Normal file
72
src/test/test-hostname-setup.c
Normal file
@ -0,0 +1,72 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "fileio.h"
|
||||
#include "hostname-setup.h"
|
||||
#include "string-util.h"
|
||||
#include "tests.h"
|
||||
#include "tmpfile-util.h"
|
||||
|
||||
static void test_read_etc_hostname(void) {
|
||||
char path[] = "/tmp/hostname.XXXXXX";
|
||||
char *hostname;
|
||||
int fd;
|
||||
|
||||
fd = mkostemp_safe(path);
|
||||
assert(fd > 0);
|
||||
close(fd);
|
||||
|
||||
/* simple hostname */
|
||||
assert_se(write_string_file(path, "foo", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(read_etc_hostname(path, &hostname) == 0);
|
||||
assert_se(streq(hostname, "foo"));
|
||||
hostname = mfree(hostname);
|
||||
|
||||
/* with comment */
|
||||
assert_se(write_string_file(path, "# comment\nfoo", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(read_etc_hostname(path, &hostname) == 0);
|
||||
assert_se(hostname);
|
||||
assert_se(streq(hostname, "foo"));
|
||||
hostname = mfree(hostname);
|
||||
|
||||
/* with comment and extra whitespace */
|
||||
assert_se(write_string_file(path, "# comment\n\n foo ", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(read_etc_hostname(path, &hostname) == 0);
|
||||
assert_se(hostname);
|
||||
assert_se(streq(hostname, "foo"));
|
||||
hostname = mfree(hostname);
|
||||
|
||||
/* cleans up name */
|
||||
assert_se(write_string_file(path, "!foo/bar.com", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(read_etc_hostname(path, &hostname) == 0);
|
||||
assert_se(hostname);
|
||||
assert_se(streq(hostname, "foobar.com"));
|
||||
hostname = mfree(hostname);
|
||||
|
||||
/* no value set */
|
||||
hostname = (char*) 0x1234;
|
||||
assert_se(write_string_file(path, "# nothing here\n", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(read_etc_hostname(path, &hostname) == -ENOENT);
|
||||
assert_se(hostname == (char*) 0x1234); /* does not touch argument on error */
|
||||
|
||||
/* nonexisting file */
|
||||
assert_se(read_etc_hostname("/non/existing", &hostname) == -ENOENT);
|
||||
assert_se(hostname == (char*) 0x1234); /* does not touch argument on error */
|
||||
|
||||
unlink(path);
|
||||
}
|
||||
|
||||
static void test_hostname_setup(void) {
|
||||
hostname_setup(false);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
test_setup_logging(LOG_DEBUG);
|
||||
|
||||
test_read_etc_hostname();
|
||||
test_hostname_setup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -6,8 +6,8 @@
|
||||
#include "fileio.h"
|
||||
#include "hostname-util.h"
|
||||
#include "string-util.h"
|
||||
#include "tests.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "util.h"
|
||||
|
||||
static void test_hostname_is_valid(void) {
|
||||
assert_se(hostname_is_valid("foobar", 0));
|
||||
@ -91,55 +91,6 @@ static void test_hostname_cleanup(void) {
|
||||
assert_se(streq(hostname_cleanup(s), "xxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"));
|
||||
}
|
||||
|
||||
static void test_read_etc_hostname(void) {
|
||||
char path[] = "/tmp/hostname.XXXXXX";
|
||||
char *hostname;
|
||||
int fd;
|
||||
|
||||
fd = mkostemp_safe(path);
|
||||
assert(fd > 0);
|
||||
close(fd);
|
||||
|
||||
/* simple hostname */
|
||||
assert_se(write_string_file(path, "foo", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(read_etc_hostname(path, &hostname) == 0);
|
||||
assert_se(streq(hostname, "foo"));
|
||||
hostname = mfree(hostname);
|
||||
|
||||
/* with comment */
|
||||
assert_se(write_string_file(path, "# comment\nfoo", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(read_etc_hostname(path, &hostname) == 0);
|
||||
assert_se(hostname);
|
||||
assert_se(streq(hostname, "foo"));
|
||||
hostname = mfree(hostname);
|
||||
|
||||
/* with comment and extra whitespace */
|
||||
assert_se(write_string_file(path, "# comment\n\n foo ", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(read_etc_hostname(path, &hostname) == 0);
|
||||
assert_se(hostname);
|
||||
assert_se(streq(hostname, "foo"));
|
||||
hostname = mfree(hostname);
|
||||
|
||||
/* cleans up name */
|
||||
assert_se(write_string_file(path, "!foo/bar.com", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(read_etc_hostname(path, &hostname) == 0);
|
||||
assert_se(hostname);
|
||||
assert_se(streq(hostname, "foobar.com"));
|
||||
hostname = mfree(hostname);
|
||||
|
||||
/* no value set */
|
||||
hostname = (char*) 0x1234;
|
||||
assert_se(write_string_file(path, "# nothing here\n", WRITE_STRING_FILE_CREATE) == 0);
|
||||
assert_se(read_etc_hostname(path, &hostname) == -ENOENT);
|
||||
assert_se(hostname == (char*) 0x1234); /* does not touch argument on error */
|
||||
|
||||
/* nonexisting file */
|
||||
assert_se(read_etc_hostname("/non/existing", &hostname) == -ENOENT);
|
||||
assert_se(hostname == (char*) 0x1234); /* does not touch argument on error */
|
||||
|
||||
unlink(path);
|
||||
}
|
||||
|
||||
static void test_hostname_malloc(void) {
|
||||
_cleanup_free_ char *h = NULL, *l = NULL;
|
||||
|
||||
@ -158,12 +109,10 @@ static void test_fallback_hostname(void) {
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
test_setup_logging(LOG_INFO);
|
||||
|
||||
test_hostname_is_valid();
|
||||
test_hostname_cleanup();
|
||||
test_read_etc_hostname();
|
||||
test_hostname_malloc();
|
||||
|
||||
test_fallback_hostname();
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "hostname-setup.h"
|
||||
#include "util.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int r;
|
||||
|
||||
r = hostname_setup();
|
||||
if (r < 0)
|
||||
log_error_errno(r, "hostname: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
710
src/udev/dmi_memory_id/dmi_memory_id.c
Normal file
710
src/udev/dmi_memory_id/dmi_memory_id.c
Normal file
@ -0,0 +1,710 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* System Memory information
|
||||
*
|
||||
* Copyright (C) 2000-2002 Alan Cox <alan@redhat.com>
|
||||
* Copyright (C) 2002-2020 Jean Delvare <jdelvare@suse.de>
|
||||
* Copyright (C) 2020 Bastien Nocera <hadess@hadess.net>
|
||||
*
|
||||
* Unless specified otherwise, all references are aimed at the "System
|
||||
* Management BIOS Reference Specification, Version 3.2.0" document,
|
||||
* available from http://www.dmtf.org/standards/smbios.
|
||||
*
|
||||
* Note to contributors:
|
||||
* Please reference every value you add or modify, especially if the
|
||||
* information does not come from the above mentioned specification.
|
||||
*
|
||||
* Additional references:
|
||||
* - Intel AP-485 revision 36
|
||||
* "Intel Processor Identification and the CPUID Instruction"
|
||||
* http://www.intel.com/support/processors/sb/cs-009861.htm
|
||||
* - DMTF Common Information Model
|
||||
* CIM Schema version 2.19.1
|
||||
* http://www.dmtf.org/standards/cim/
|
||||
* - IPMI 2.0 revision 1.0
|
||||
* "Intelligent Platform Management Interface Specification"
|
||||
* http://developer.intel.com/design/servers/ipmi/spec.htm
|
||||
* - AMD publication #25481 revision 2.28
|
||||
* "CPUID Specification"
|
||||
* http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/25481.pdf
|
||||
* - BIOS Integrity Services Application Programming Interface version 1.0
|
||||
* http://www.intel.com/design/archives/wfm/downloads/bisspec.htm
|
||||
* - DMTF DSP0239 version 1.1.0
|
||||
* "Management Component Transport Protocol (MCTP) IDs and Codes"
|
||||
* http://www.dmtf.org/standards/pmci
|
||||
* - "TPM Main, Part 2 TPM Structures"
|
||||
* Specification version 1.2, level 2, revision 116
|
||||
* https://trustedcomputinggroup.org/tpm-main-specification/
|
||||
* - "PC Client Platform TPM Profile (PTP) Specification"
|
||||
* Family "2.0", Level 00, Revision 00.43, January 26, 2015
|
||||
* https://trustedcomputinggroup.org/pc-client-platform-tpm-profile-ptp-specification/
|
||||
* - "RedFish Host Interface Specification" (DMTF DSP0270)
|
||||
* https://www.dmtf.org/sites/default/files/DSP0270_1.0.1.pdf
|
||||
*/
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "build.h"
|
||||
#include "fileio.h"
|
||||
#include "main-func.h"
|
||||
#include "string-util.h"
|
||||
#include "udev-util.h"
|
||||
#include "unaligned.h"
|
||||
|
||||
#define SUPPORTED_SMBIOS_VER 0x030300
|
||||
|
||||
#define OUT_OF_SPEC_STR "<OUT OF SPEC>"
|
||||
|
||||
#define SYS_FIRMWARE_DIR "/sys/firmware/dmi/tables"
|
||||
#define SYS_ENTRY_FILE SYS_FIRMWARE_DIR "/smbios_entry_point"
|
||||
#define SYS_TABLE_FILE SYS_FIRMWARE_DIR "/DMI"
|
||||
|
||||
/*
|
||||
* Per SMBIOS v2.8.0 and later, all structures assume a little-endian
|
||||
* ordering convention.
|
||||
*/
|
||||
#define WORD(x) (unaligned_read_le16(x))
|
||||
#define DWORD(x) (unaligned_read_le32(x))
|
||||
#define QWORD(x) (unaligned_read_le64(x))
|
||||
|
||||
struct dmi_header {
|
||||
uint8_t type;
|
||||
uint8_t length;
|
||||
uint16_t handle;
|
||||
const uint8_t *data;
|
||||
};
|
||||
|
||||
static const char *arg_source_file = NULL;
|
||||
|
||||
static bool verify_checksum(const uint8_t *buf, size_t len) {
|
||||
uint8_t sum = 0;
|
||||
|
||||
for (size_t a = 0; a < len; a++)
|
||||
sum += buf[a];
|
||||
return sum == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Type-independant Stuff
|
||||
*/
|
||||
|
||||
static const char *dmi_string(const struct dmi_header *dm, uint8_t s) {
|
||||
const char *bp = (const char *) dm->data;
|
||||
|
||||
if (s == 0)
|
||||
return "Not Specified";
|
||||
|
||||
bp += dm->length;
|
||||
for (;s > 1 && !isempty(bp); s--)
|
||||
bp += strlen(bp) + 1;
|
||||
|
||||
if (isempty(bp))
|
||||
return "<BAD INDEX>";
|
||||
|
||||
return bp;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
MEMORY_SIZE_UNIT_BYTES,
|
||||
MEMORY_SIZE_UNIT_KB
|
||||
} MemorySizeUnit;
|
||||
|
||||
static void dmi_print_memory_size(
|
||||
const char *attr_prefix, const char *attr_suffix,
|
||||
int slot_num, uint64_t code, MemorySizeUnit unit) {
|
||||
if (unit == MEMORY_SIZE_UNIT_KB)
|
||||
code <<= 10;
|
||||
|
||||
if (slot_num >= 0)
|
||||
printf("%s_%u_%s=%"PRIu64"\n", attr_prefix, slot_num, attr_suffix, code);
|
||||
else
|
||||
printf("%s_%s=%"PRIu64"\n", attr_prefix, attr_suffix, code);
|
||||
}
|
||||
|
||||
/*
|
||||
* 7.17 Physical Memory Array (Type 16)
|
||||
*/
|
||||
|
||||
static void dmi_memory_array_location(uint8_t code) {
|
||||
/* 7.17.1 */
|
||||
static const char *location[] = {
|
||||
[0x01] = "Other",
|
||||
[0x02] = "Unknown",
|
||||
[0x03] = "System Board Or Motherboard",
|
||||
[0x04] = "ISA Add-on Card",
|
||||
[0x05] = "EISA Add-on Card",
|
||||
[0x06] = "PCI Add-on Card",
|
||||
[0x07] = "MCA Add-on Card",
|
||||
[0x08] = "PCMCIA Add-on Card",
|
||||
[0x09] = "Proprietary Add-on Card",
|
||||
[0x0A] = "NuBus",
|
||||
};
|
||||
static const char *location_0xA0[] = {
|
||||
[0x00] = "PC-98/C20 Add-on Card", /* 0xA0 */
|
||||
[0x01] = "PC-98/C24 Add-on Card", /* 0xA1 */
|
||||
[0x02] = "PC-98/E Add-on Card", /* 0xA2 */
|
||||
[0x03] = "PC-98/Local Bus Add-on Card", /* 0xA3 */
|
||||
[0x04] = "CXL Flexbus 1.0", /* 0xA4 */
|
||||
};
|
||||
const char *str = OUT_OF_SPEC_STR;
|
||||
|
||||
if (code < ELEMENTSOF(location) && location[code])
|
||||
str = location[code];
|
||||
else if (code >= 0xA0 && code < (ELEMENTSOF(location_0xA0) + 0xA0))
|
||||
str = location_0xA0[code - 0xA0];
|
||||
|
||||
printf("MEMORY_ARRAY_LOCATION=%s\n", str);
|
||||
}
|
||||
|
||||
static void dmi_memory_array_ec_type(uint8_t code) {
|
||||
/* 7.17.3 */
|
||||
static const char *type[] = {
|
||||
[0x01] = "Other",
|
||||
[0x02] = "Unknown",
|
||||
[0x03] = "None",
|
||||
[0x04] = "Parity",
|
||||
[0x05] = "Single-bit ECC",
|
||||
[0x06] = "Multi-bit ECC",
|
||||
[0x07] = "CRC",
|
||||
};
|
||||
|
||||
if (code != 0x03) /* Do not print "None". */
|
||||
printf("MEMORY_ARRAY_EC_TYPE=%s\n",
|
||||
code < ELEMENTSOF(type) && type[code] ? type[code] : OUT_OF_SPEC_STR);
|
||||
}
|
||||
|
||||
/*
|
||||
* 7.18 Memory Device (Type 17)
|
||||
*/
|
||||
|
||||
static void dmi_memory_device_string(
|
||||
const char *attr_suffix, unsigned slot_num,
|
||||
const struct dmi_header *h, uint8_t s) {
|
||||
char *str;
|
||||
|
||||
str = strdupa(dmi_string(h, s));
|
||||
str = strstrip(str);
|
||||
if (!isempty(str))
|
||||
printf("MEMORY_DEVICE_%u_%s=%s\n", slot_num, attr_suffix, str);
|
||||
}
|
||||
|
||||
static void dmi_memory_device_width(
|
||||
const char *attr_suffix,
|
||||
unsigned slot_num, uint16_t code) {
|
||||
|
||||
/* If no memory module is present, width may be 0 */
|
||||
if (!IN_SET(code, 0, 0xFFFF))
|
||||
printf("MEMORY_DEVICE_%u_%s=%u\n", slot_num, attr_suffix, code);
|
||||
}
|
||||
|
||||
static void dmi_memory_device_size(unsigned slot_num, uint16_t code) {
|
||||
if (code == 0)
|
||||
return (void) printf("MEMORY_DEVICE_%u_PRESENT=0\n", slot_num);
|
||||
if (code == 0xFFFF)
|
||||
return;
|
||||
|
||||
uint64_t s = code & 0x7FFF;
|
||||
if (!(code & 0x8000))
|
||||
s <<= 10;
|
||||
dmi_print_memory_size("MEMORY_DEVICE", "SIZE", slot_num, s, MEMORY_SIZE_UNIT_KB);
|
||||
}
|
||||
|
||||
static void dmi_memory_device_extended_size(unsigned slot_num, uint32_t code) {
|
||||
uint64_t capacity = (uint64_t) code * 1024 * 1024;
|
||||
|
||||
printf("MEMORY_DEVICE_%u_SIZE=%"PRIu64"\n", slot_num, capacity);
|
||||
}
|
||||
|
||||
static void dmi_memory_device_rank(unsigned slot_num, uint8_t code) {
|
||||
code &= 0x0F;
|
||||
if (code != 0)
|
||||
printf("MEMORY_DEVICE_%u_RANK=%u\n", slot_num, code);
|
||||
}
|
||||
|
||||
static void dmi_memory_device_voltage_value(
|
||||
const char *attr_suffix,
|
||||
unsigned slot_num, uint16_t code) {
|
||||
if (code == 0)
|
||||
return;
|
||||
if (code % 100 != 0)
|
||||
printf("MEMORY_DEVICE_%u_%s=%g\n", slot_num, attr_suffix, (double)code / 1000);
|
||||
else
|
||||
printf("MEMORY_DEVICE_%u_%s=%.1g\n", slot_num, attr_suffix, (double)code / 1000);
|
||||
}
|
||||
|
||||
static void dmi_memory_device_form_factor(unsigned slot_num, uint8_t code) {
|
||||
/* 7.18.1 */
|
||||
static const char *form_factor[] = {
|
||||
[0x01] = "Other",
|
||||
[0x02] = "Unknown",
|
||||
[0x03] = "SIMM",
|
||||
[0x04] = "SIP",
|
||||
[0x05] = "Chip",
|
||||
[0x06] = "DIP",
|
||||
[0x07] = "ZIP",
|
||||
[0x08] = "Proprietary Card",
|
||||
[0x09] = "DIMM",
|
||||
[0x0A] = "TSOP",
|
||||
[0x0B] = "Row Of Chips",
|
||||
[0x0C] = "RIMM",
|
||||
[0x0D] = "SODIMM",
|
||||
[0x0E] = "SRIMM",
|
||||
[0x0F] = "FB-DIMM",
|
||||
[0x10] = "Die",
|
||||
};
|
||||
|
||||
printf("MEMORY_DEVICE_%u_FORM_FACTOR=%s\n", slot_num,
|
||||
code < ELEMENTSOF(form_factor) && form_factor[code] ? form_factor[code] : OUT_OF_SPEC_STR);
|
||||
}
|
||||
|
||||
static void dmi_memory_device_set(unsigned slot_num, uint8_t code) {
|
||||
if (code == 0xFF)
|
||||
printf("MEMORY_DEVICE_%u_SET=%s\n", slot_num, "Unknown");
|
||||
else if (code != 0)
|
||||
printf("MEMORY_DEVICE_%u_SET=%"PRIu8"\n", slot_num, code);
|
||||
}
|
||||
|
||||
static void dmi_memory_device_type(unsigned slot_num, uint8_t code) {
|
||||
/* 7.18.2 */
|
||||
static const char *type[] = {
|
||||
[0x01] = "Other",
|
||||
[0x02] = "Unknown",
|
||||
[0x03] = "DRAM",
|
||||
[0x04] = "EDRAM",
|
||||
[0x05] = "VRAM",
|
||||
[0x06] = "SRAM",
|
||||
[0x07] = "RAM",
|
||||
[0x08] = "ROM",
|
||||
[0x09] = "Flash",
|
||||
[0x0A] = "EEPROM",
|
||||
[0x0B] = "FEPROM",
|
||||
[0x0C] = "EPROM",
|
||||
[0x0D] = "CDRAM",
|
||||
[0x0E] = "3DRAM",
|
||||
[0x0F] = "SDRAM",
|
||||
[0x10] = "SGRAM",
|
||||
[0x11] = "RDRAM",
|
||||
[0x12] = "DDR",
|
||||
[0x13] = "DDR2",
|
||||
[0x14] = "DDR2 FB-DIMM",
|
||||
[0x15] = "Reserved",
|
||||
[0x16] = "Reserved",
|
||||
[0x17] = "Reserved",
|
||||
[0x18] = "DDR3",
|
||||
[0x19] = "FBD2",
|
||||
[0x1A] = "DDR4",
|
||||
[0x1B] = "LPDDR",
|
||||
[0x1C] = "LPDDR2",
|
||||
[0x1D] = "LPDDR3",
|
||||
[0x1E] = "LPDDR4",
|
||||
[0x1F] = "Logical non-volatile device",
|
||||
[0x20] = "HBM",
|
||||
[0x21] = "HBM2",
|
||||
};
|
||||
|
||||
printf("MEMORY_DEVICE_%u_TYPE=%s\n", slot_num,
|
||||
code < ELEMENTSOF(type) && type[code] ? type[code] : OUT_OF_SPEC_STR);
|
||||
}
|
||||
|
||||
static void dmi_memory_device_type_detail(unsigned slot_num, uint16_t code) {
|
||||
/* 7.18.3 */
|
||||
static const char *detail[] = {
|
||||
[1] = "Other",
|
||||
[2] = "Unknown",
|
||||
[3] = "Fast-paged",
|
||||
[4] = "Static Column",
|
||||
[5] = "Pseudo-static",
|
||||
[6] = "RAMBus",
|
||||
[7] = "Synchronous",
|
||||
[8] = "CMOS",
|
||||
[9] = "EDO",
|
||||
[10] = "Window DRAM",
|
||||
[11] = "Cache DRAM",
|
||||
[12] = "Non-Volatile",
|
||||
[13] = "Registered (Buffered)",
|
||||
[14] = "Unbuffered (Unregistered)",
|
||||
[15] = "LRDIMM",
|
||||
};
|
||||
|
||||
if ((code & 0xFFFE) == 0)
|
||||
printf("MEMORY_DEVICE_%u_TYPE_DETAIL=%s\n", slot_num, "None");
|
||||
else {
|
||||
bool first_element = true;
|
||||
|
||||
printf("MEMORY_DEVICE_%u_TYPE_DETAIL=", slot_num);
|
||||
for (size_t i = 1; i < ELEMENTSOF(detail); i++)
|
||||
if (code & (1 << i)) {
|
||||
printf("%s%s", first_element ? "" : " ", detail[i]);
|
||||
first_element = false;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void dmi_memory_device_speed(
|
||||
const char *attr_suffix,
|
||||
unsigned slot_num, uint16_t code) {
|
||||
if (code != 0)
|
||||
printf("MEMORY_DEVICE_%u_%s=%u\n", slot_num, attr_suffix, code);
|
||||
}
|
||||
|
||||
static void dmi_memory_device_technology(unsigned slot_num, uint8_t code) {
|
||||
/* 7.18.6 */
|
||||
static const char * const technology[] = {
|
||||
[0x01] = "Other",
|
||||
[0x02] = "Unknown",
|
||||
[0x03] = "DRAM",
|
||||
[0x04] = "NVDIMM-N",
|
||||
[0x05] = "NVDIMM-F",
|
||||
[0x06] = "NVDIMM-P",
|
||||
[0x07] = "Intel Optane DC persistent memory",
|
||||
};
|
||||
|
||||
printf("MEMORY_DEVICE_%u_MEMORY_TECHNOLOGY=%s\n", slot_num,
|
||||
code < ELEMENTSOF(technology) && technology[code] ? technology[code] : OUT_OF_SPEC_STR);
|
||||
}
|
||||
|
||||
static void dmi_memory_device_operating_mode_capability(unsigned slot_num, uint16_t code) {
|
||||
/* 7.18.7 */
|
||||
static const char * const mode[] = {
|
||||
[1] = "Other",
|
||||
[2] = "Unknown",
|
||||
[3] = "Volatile memory",
|
||||
[4] = "Byte-accessible persistent memory",
|
||||
[5] = "Block-accessible persistent memory",
|
||||
};
|
||||
|
||||
if ((code & 0xFFFE) != 0) {
|
||||
bool first_element = true;
|
||||
|
||||
printf("MEMORY_DEVICE_%u_MEMORY_OPERATING_MODE_CAPABILITY=", slot_num);
|
||||
for (size_t i = 1; i < ELEMENTSOF(mode); i++)
|
||||
if (code & (1 << i)) {
|
||||
printf("%s%s", first_element ? "" : " ", mode[i]);
|
||||
first_element = false;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void dmi_memory_device_manufacturer_id(
|
||||
const char *attr_suffix,
|
||||
unsigned slot_num, uint16_t code) {
|
||||
/* 7.18.8 */
|
||||
/* 7.18.10 */
|
||||
/* LSB is 7-bit Odd Parity number of continuation codes */
|
||||
if (code != 0)
|
||||
printf("MEMORY_DEVICE_%u_%s=Bank %d, Hex 0x%02X\n", slot_num, attr_suffix,
|
||||
(code & 0x7F) + 1, code >> 8);
|
||||
}
|
||||
|
||||
static void dmi_memory_device_product_id(
|
||||
const char *attr_suffix,
|
||||
unsigned slot_num, uint16_t code) {
|
||||
/* 7.18.9 */
|
||||
/* 7.18.11 */
|
||||
if (code != 0)
|
||||
printf("MEMORY_DEVICE_%u_%s=0x%04X\n", slot_num, attr_suffix, code);
|
||||
}
|
||||
|
||||
static void dmi_memory_device_size_detail(
|
||||
const char *attr_suffix,
|
||||
unsigned slot_num, uint64_t code) {
|
||||
/* 7.18.12 */
|
||||
/* 7.18.13 */
|
||||
if (!IN_SET(code, 0x0LU, 0xFFFFFFFFFFFFFFFFLU))
|
||||
dmi_print_memory_size("MEMORY_DEVICE", attr_suffix, slot_num, code, MEMORY_SIZE_UNIT_BYTES);
|
||||
}
|
||||
|
||||
static void dmi_decode(const struct dmi_header *h) {
|
||||
const uint8_t *data = h->data;
|
||||
static unsigned next_slot_num = 0;
|
||||
unsigned slot_num;
|
||||
|
||||
/*
|
||||
* Note: DMI types 37 and 42 are untested
|
||||
*/
|
||||
switch (h->type) {
|
||||
case 16: /* 7.17 Physical Memory Array */
|
||||
log_debug("Physical Memory Array");
|
||||
if (h->length < 0x0F)
|
||||
break;
|
||||
|
||||
if (data[0x05] != 0x03) /* 7.17.2, Use == "System Memory" */
|
||||
break;
|
||||
|
||||
log_debug("Use: System Memory");
|
||||
dmi_memory_array_location(data[0x04]);
|
||||
dmi_memory_array_ec_type(data[0x06]);
|
||||
if (DWORD(data + 0x07) != 0x80000000)
|
||||
dmi_print_memory_size("MEMORY_ARRAY", "MAX_CAPACITY", -1, DWORD(data + 0x07), MEMORY_SIZE_UNIT_KB);
|
||||
else if (h->length >= 0x17)
|
||||
dmi_print_memory_size("MEMORY_ARRAY", "MAX_CAPACITY", -1, QWORD(data + 0x0F), MEMORY_SIZE_UNIT_BYTES);
|
||||
printf("MEMORY_ARRAY_NUM_DEVICES=%u\n", WORD(data + 0x0D));
|
||||
|
||||
break;
|
||||
|
||||
case 17: /* 7.18 Memory Device */
|
||||
slot_num = next_slot_num;
|
||||
next_slot_num++;
|
||||
|
||||
log_debug("Memory Device");
|
||||
if (h->length < 0x15)
|
||||
break;
|
||||
|
||||
dmi_memory_device_width("TOTAL_WIDTH", slot_num, WORD(data + 0x08));
|
||||
dmi_memory_device_width("DATA_WIDTH", slot_num, WORD(data + 0x0A));
|
||||
if (h->length >= 0x20 && WORD(data + 0x0C) == 0x7FFF)
|
||||
dmi_memory_device_extended_size(slot_num, DWORD(data + 0x1C));
|
||||
else
|
||||
dmi_memory_device_size(slot_num, WORD(data + 0x0C));
|
||||
dmi_memory_device_form_factor(slot_num, data[0x0E]);
|
||||
dmi_memory_device_set(slot_num, data[0x0F]);
|
||||
dmi_memory_device_string("LOCATOR", slot_num, h, data[0x10]);
|
||||
dmi_memory_device_string("BANK_LOCATOR", slot_num, h, data[0x11]);
|
||||
dmi_memory_device_type(slot_num, data[0x12]);
|
||||
dmi_memory_device_type_detail(slot_num, WORD(data + 0x13));
|
||||
if (h->length < 0x17)
|
||||
break;
|
||||
|
||||
dmi_memory_device_speed("SPEED_MTS", slot_num, WORD(data + 0x15));
|
||||
if (h->length < 0x1B)
|
||||
break;
|
||||
|
||||
dmi_memory_device_string("MANUFACTURER", slot_num, h, data[0x17]);
|
||||
dmi_memory_device_string("SERIAL_NUMBER", slot_num, h, data[0x18]);
|
||||
dmi_memory_device_string("ASSET_TAG", slot_num, h, data[0x19]);
|
||||
dmi_memory_device_string("PART_NUMBER", slot_num, h, data[0x1A]);
|
||||
if (h->length < 0x1C)
|
||||
break;
|
||||
|
||||
dmi_memory_device_rank(slot_num, data[0x1B]);
|
||||
if (h->length < 0x22)
|
||||
break;
|
||||
|
||||
dmi_memory_device_speed("CONFIGURED_SPEED_MTS", slot_num, WORD(data + 0x20));
|
||||
if (h->length < 0x28)
|
||||
break;
|
||||
|
||||
dmi_memory_device_voltage_value("MINIMUM_VOLTAGE", slot_num, WORD(data + 0x22));
|
||||
dmi_memory_device_voltage_value("MAXIMUM_VOLTAGE", slot_num, WORD(data + 0x24));
|
||||
dmi_memory_device_voltage_value("CONFIGURED_VOLTAGE", slot_num, WORD(data + 0x26));
|
||||
if (h->length < 0x34)
|
||||
break;
|
||||
|
||||
dmi_memory_device_technology(slot_num, data[0x28]);
|
||||
dmi_memory_device_operating_mode_capability(slot_num, WORD(data + 0x29));
|
||||
dmi_memory_device_string("FIRMWARE_VERSION", slot_num, h, data[0x2B]);
|
||||
dmi_memory_device_manufacturer_id("MODULE_MANUFACTURER_ID", slot_num, WORD(data + 0x2C));
|
||||
dmi_memory_device_product_id("MODULE_PRODUCT_ID", slot_num, WORD(data + 0x2E));
|
||||
dmi_memory_device_manufacturer_id("MEMORY_SUBSYSTEM_CONTROLLER_MANUFACTURER_ID",
|
||||
slot_num, WORD(data + 0x30));
|
||||
dmi_memory_device_product_id("MEMORY_SUBSYSTEM_CONTROLLER_PRODUCT_ID",
|
||||
slot_num, WORD(data + 0x32));
|
||||
if (h->length < 0x3C)
|
||||
break;
|
||||
|
||||
dmi_memory_device_size_detail("NON_VOLATILE_SIZE", slot_num, QWORD(data + 0x34));
|
||||
if (h->length < 0x44)
|
||||
break;
|
||||
|
||||
dmi_memory_device_size_detail("VOLATILE_SIZE", slot_num, QWORD(data + 0x3C));
|
||||
if (h->length < 0x4C)
|
||||
break;
|
||||
|
||||
dmi_memory_device_size_detail("CACHE_SIZE", slot_num, QWORD(data + 0x44));
|
||||
if (h->length < 0x54)
|
||||
break;
|
||||
|
||||
dmi_memory_device_size_detail("LOGICAL_SIZE", slot_num, QWORD(data + 0x4C));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void dmi_table_decode(const uint8_t *buf, size_t len, uint16_t num) {
|
||||
const uint8_t *data = buf;
|
||||
|
||||
/* 4 is the length of an SMBIOS structure header */
|
||||
for (uint16_t i = 0; (i < num || num == 0) && data + 4 <= buf + len; i++) {
|
||||
struct dmi_header h = (struct dmi_header) {
|
||||
.type = data[0],
|
||||
.length = data[1],
|
||||
.handle = WORD(data + 2),
|
||||
.data = data,
|
||||
};
|
||||
bool display = !IN_SET(h.type, 126, 127);
|
||||
const uint8_t *next;
|
||||
|
||||
/* If a short entry is found (less than 4 bytes), not only it
|
||||
* is invalid, but we cannot reliably locate the next entry.
|
||||
* Better stop at this point, and let the user know his/her
|
||||
* table is broken. */
|
||||
if (h.length < 4)
|
||||
break;
|
||||
|
||||
/* In quiet mode, stop decoding at end of table marker */
|
||||
if (h.type == 127)
|
||||
break;
|
||||
|
||||
/* Look for the next handle */
|
||||
next = data + h.length;
|
||||
while ((size_t)(next - buf + 1) < len && (next[0] != 0 || next[1] != 0))
|
||||
next++;
|
||||
next += 2;
|
||||
|
||||
/* Make sure the whole structure fits in the table */
|
||||
if ((size_t)(next - buf) > len)
|
||||
break;
|
||||
|
||||
if (display)
|
||||
dmi_decode(&h);
|
||||
|
||||
data = next;
|
||||
}
|
||||
}
|
||||
|
||||
static int dmi_table(int64_t base, uint32_t len, uint16_t num, const char *devmem, bool no_file_offset) {
|
||||
_cleanup_free_ uint8_t *buf = NULL;
|
||||
size_t size;
|
||||
int r;
|
||||
|
||||
/*
|
||||
* When reading from sysfs or from a dump file, the file may be
|
||||
* shorter than announced. For SMBIOS v3 this is expcted, as we
|
||||
* only know the maximum table size, not the actual table size.
|
||||
* For older implementations (and for SMBIOS v3 too), this
|
||||
* would be the result of the kernel truncating the table on
|
||||
* parse error.
|
||||
*/
|
||||
r = read_full_file_full(AT_FDCWD, devmem, no_file_offset ? 0 : base, len,
|
||||
0, NULL, (char **) &buf, &size);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read table: %m");
|
||||
|
||||
dmi_table_decode(buf, size, num);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Same thing for SMBIOS3 entry points */
|
||||
static int smbios3_decode(const uint8_t *buf, const char *devmem, bool no_file_offset) {
|
||||
uint64_t offset;
|
||||
|
||||
/* Don't let checksum run beyond the buffer */
|
||||
if (buf[0x06] > 0x20)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Entry point length too large (%"PRIu8" bytes, expected %u).",
|
||||
buf[0x06], 0x18U);
|
||||
|
||||
if (!verify_checksum(buf, buf[0x06]))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Faied to verify checksum.");
|
||||
|
||||
offset = QWORD(buf + 0x10);
|
||||
if (!no_file_offset && (offset >> 32) != 0 && sizeof(int64_t) < 8)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "64-bit addresses not supported.");
|
||||
|
||||
return dmi_table(offset, DWORD(buf + 0x0C), 0, devmem, no_file_offset);
|
||||
}
|
||||
|
||||
static int smbios_decode(const uint8_t *buf, const char *devmem, bool no_file_offset) {
|
||||
/* Don't let checksum run beyond the buffer */
|
||||
if (buf[0x05] > 0x20)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Entry point length too large (%"PRIu8" bytes, expected %u).",
|
||||
buf[0x05], 0x1FU);
|
||||
|
||||
if (!verify_checksum(buf, buf[0x05])
|
||||
|| memcmp(buf + 0x10, "_DMI_", 5) != 0
|
||||
|| !verify_checksum(buf + 0x10, 0x0F))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to verify checksum.");
|
||||
|
||||
return dmi_table(DWORD(buf + 0x18), WORD(buf + 0x16), WORD(buf + 0x1C),
|
||||
devmem, no_file_offset);
|
||||
}
|
||||
|
||||
static int legacy_decode(const uint8_t *buf, const char *devmem, bool no_file_offset) {
|
||||
if (!verify_checksum(buf, 0x0F))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to verify checksum.");
|
||||
|
||||
return dmi_table(DWORD(buf + 0x08), WORD(buf + 0x06), WORD(buf + 0x0C),
|
||||
devmem, no_file_offset);
|
||||
}
|
||||
|
||||
static int help(void) {
|
||||
printf("Usage: %s [options]\n"
|
||||
" -F,--from-dump FILE read DMI information from a binary file\n"
|
||||
" -h,--help print this help text\n\n",
|
||||
program_invocation_short_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char * const *argv) {
|
||||
static const struct option options[] = {
|
||||
{ "from-dump", required_argument, NULL, 'F' },
|
||||
{ "version", no_argument, NULL, 'V' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{}
|
||||
};
|
||||
int c;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "F:hV", options, NULL)) >= 0)
|
||||
switch (c) {
|
||||
case 'F':
|
||||
arg_source_file = optarg;
|
||||
break;
|
||||
case 'V':
|
||||
printf("%s\n", GIT_VERSION);
|
||||
return 0;
|
||||
case 'h':
|
||||
return help();
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
default:
|
||||
assert_not_reached("Unknown option");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int run(int argc, char* const* argv) {
|
||||
_cleanup_free_ uint8_t *buf = NULL;
|
||||
bool no_file_offset = false;
|
||||
size_t size;
|
||||
int r;
|
||||
|
||||
log_set_target(LOG_TARGET_AUTO);
|
||||
udev_parse_config();
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
/* Read from dump if so instructed */
|
||||
r = read_full_file_full(AT_FDCWD,
|
||||
arg_source_file ?: SYS_ENTRY_FILE,
|
||||
0, 0x20, 0, NULL, (char **) &buf, &size);
|
||||
if (r < 0)
|
||||
return log_full_errno(!arg_source_file && r == -ENOENT ? LOG_DEBUG : LOG_ERR,
|
||||
r, "Reading \"%s\" failed: %m",
|
||||
arg_source_file ?: SYS_ENTRY_FILE);
|
||||
|
||||
if (!arg_source_file) {
|
||||
arg_source_file = SYS_TABLE_FILE;
|
||||
no_file_offset = true;
|
||||
}
|
||||
|
||||
if (size >= 24 && memory_startswith(buf, size, "_SM3_"))
|
||||
return smbios3_decode(buf, arg_source_file, no_file_offset);
|
||||
if (size >= 31 && memory_startswith(buf, size, "_SM_"))
|
||||
return smbios_decode(buf, arg_source_file, no_file_offset);
|
||||
if (size >= 15 && memory_startswith(buf, size, "_DMI_"))
|
||||
return legacy_decode(buf, arg_source_file, no_file_offset);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
||||
@ -174,6 +174,7 @@ foreach prog : [['ata_id/ata_id.c'],
|
||||
'scsi_id/scsi_serial.c',
|
||||
'scsi_id/scsi.h'],
|
||||
['v4l_id/v4l_id.c'],
|
||||
['dmi_memory_id/dmi_memory_id.c'],
|
||||
['mtd_probe/mtd_probe.c',
|
||||
'mtd_probe/mtd_probe.h',
|
||||
'mtd_probe/probe_smartmedia.c']]
|
||||
|
||||
BIN
test/dmidecode-dumps/Lenovo-ThinkPad-X280-dmidecode-dump.bin
Normal file
BIN
test/dmidecode-dumps/Lenovo-ThinkPad-X280-dmidecode-dump.bin
Normal file
Binary file not shown.
@ -0,0 +1,33 @@
|
||||
MEMORY_ARRAY_LOCATION=System Board Or Motherboard
|
||||
MEMORY_ARRAY_MAX_CAPACITY=34359738368
|
||||
MEMORY_ARRAY_NUM_DEVICES=2
|
||||
MEMORY_DEVICE_0_TOTAL_WIDTH=64
|
||||
MEMORY_DEVICE_0_DATA_WIDTH=64
|
||||
MEMORY_DEVICE_0_SIZE=4294967296
|
||||
MEMORY_DEVICE_0_FORM_FACTOR=SODIMM
|
||||
MEMORY_DEVICE_0_LOCATOR=ChannelA-DIMM0
|
||||
MEMORY_DEVICE_0_BANK_LOCATOR=BANK 0
|
||||
MEMORY_DEVICE_0_TYPE=DDR4
|
||||
MEMORY_DEVICE_0_TYPE_DETAIL=Synchronous Unbuffered (Unregistered)
|
||||
MEMORY_DEVICE_0_SPEED_MTS=2400
|
||||
MEMORY_DEVICE_0_MANUFACTURER=0000
|
||||
MEMORY_DEVICE_0_SERIAL_NUMBER=00000000
|
||||
MEMORY_DEVICE_0_ASSET_TAG=None
|
||||
MEMORY_DEVICE_0_RANK=1
|
||||
MEMORY_DEVICE_0_CONFIGURED_SPEED_MTS=2400
|
||||
MEMORY_DEVICE_0_CONFIGURED_VOLTAGE=1
|
||||
MEMORY_DEVICE_1_TOTAL_WIDTH=64
|
||||
MEMORY_DEVICE_1_DATA_WIDTH=64
|
||||
MEMORY_DEVICE_1_SIZE=4294967296
|
||||
MEMORY_DEVICE_1_FORM_FACTOR=SODIMM
|
||||
MEMORY_DEVICE_1_LOCATOR=ChannelB-DIMM0
|
||||
MEMORY_DEVICE_1_BANK_LOCATOR=BANK 2
|
||||
MEMORY_DEVICE_1_TYPE=DDR4
|
||||
MEMORY_DEVICE_1_TYPE_DETAIL=Synchronous Unbuffered (Unregistered)
|
||||
MEMORY_DEVICE_1_SPEED_MTS=2400
|
||||
MEMORY_DEVICE_1_MANUFACTURER=0000
|
||||
MEMORY_DEVICE_1_SERIAL_NUMBER=00000000
|
||||
MEMORY_DEVICE_1_ASSET_TAG=None
|
||||
MEMORY_DEVICE_1_RANK=1
|
||||
MEMORY_DEVICE_1_CONFIGURED_SPEED_MTS=2400
|
||||
MEMORY_DEVICE_1_CONFIGURED_VOLTAGE=1
|
||||
BIN
test/dmidecode-dumps/Lenovo-Thinkcentre-m720s-dmidecode-dump.bin
Normal file
BIN
test/dmidecode-dumps/Lenovo-Thinkcentre-m720s-dmidecode-dump.bin
Normal file
Binary file not shown.
@ -0,0 +1,67 @@
|
||||
MEMORY_ARRAY_LOCATION=System Board Or Motherboard
|
||||
MEMORY_ARRAY_MAX_CAPACITY=68719476736
|
||||
MEMORY_ARRAY_NUM_DEVICES=4
|
||||
MEMORY_DEVICE_0_TOTAL_WIDTH=64
|
||||
MEMORY_DEVICE_0_DATA_WIDTH=64
|
||||
MEMORY_DEVICE_0_SIZE=8589934592
|
||||
MEMORY_DEVICE_0_FORM_FACTOR=DIMM
|
||||
MEMORY_DEVICE_0_LOCATOR=ChannelA-DIMM0
|
||||
MEMORY_DEVICE_0_BANK_LOCATOR=BANK 0
|
||||
MEMORY_DEVICE_0_TYPE=DDR4
|
||||
MEMORY_DEVICE_0_TYPE_DETAIL=Synchronous
|
||||
MEMORY_DEVICE_0_SPEED_MTS=2667
|
||||
MEMORY_DEVICE_0_MANUFACTURER=Samsung
|
||||
MEMORY_DEVICE_0_SERIAL_NUMBER=416433E9
|
||||
MEMORY_DEVICE_0_ASSET_TAG=9876543210
|
||||
MEMORY_DEVICE_0_PART_NUMBER=M378A1K43CB2-CTD
|
||||
MEMORY_DEVICE_0_RANK=1
|
||||
MEMORY_DEVICE_0_CONFIGURED_SPEED_MTS=2400
|
||||
MEMORY_DEVICE_0_MINIMUM_VOLTAGE=1
|
||||
MEMORY_DEVICE_0_MAXIMUM_VOLTAGE=1
|
||||
MEMORY_DEVICE_0_CONFIGURED_VOLTAGE=1
|
||||
MEMORY_DEVICE_1_TOTAL_WIDTH=64
|
||||
MEMORY_DEVICE_1_DATA_WIDTH=64
|
||||
MEMORY_DEVICE_1_SIZE=8589934592
|
||||
MEMORY_DEVICE_1_FORM_FACTOR=DIMM
|
||||
MEMORY_DEVICE_1_LOCATOR=ChannelA-DIMM1
|
||||
MEMORY_DEVICE_1_BANK_LOCATOR=BANK 1
|
||||
MEMORY_DEVICE_1_TYPE=DDR4
|
||||
MEMORY_DEVICE_1_TYPE_DETAIL=Synchronous
|
||||
MEMORY_DEVICE_1_SPEED_MTS=2400
|
||||
MEMORY_DEVICE_1_MANUFACTURER=859B
|
||||
MEMORY_DEVICE_1_SERIAL_NUMBER=A02550A6
|
||||
MEMORY_DEVICE_1_ASSET_TAG=9876543210
|
||||
MEMORY_DEVICE_1_PART_NUMBER=BLT8G4D26AFTA.16FBD
|
||||
MEMORY_DEVICE_1_RANK=2
|
||||
MEMORY_DEVICE_1_CONFIGURED_SPEED_MTS=2400
|
||||
MEMORY_DEVICE_1_MINIMUM_VOLTAGE=1
|
||||
MEMORY_DEVICE_1_MAXIMUM_VOLTAGE=1
|
||||
MEMORY_DEVICE_1_CONFIGURED_VOLTAGE=1
|
||||
MEMORY_DEVICE_2_PRESENT=0
|
||||
MEMORY_DEVICE_2_FORM_FACTOR=Unknown
|
||||
MEMORY_DEVICE_2_LOCATOR=ChannelB-DIMM0
|
||||
MEMORY_DEVICE_2_BANK_LOCATOR=BANK 2
|
||||
MEMORY_DEVICE_2_TYPE=Unknown
|
||||
MEMORY_DEVICE_2_TYPE_DETAIL=None
|
||||
MEMORY_DEVICE_2_MANUFACTURER=Not Specified
|
||||
MEMORY_DEVICE_2_SERIAL_NUMBER=Not Specified
|
||||
MEMORY_DEVICE_2_ASSET_TAG=Not Specified
|
||||
MEMORY_DEVICE_2_PART_NUMBER=Not Specified
|
||||
MEMORY_DEVICE_3_TOTAL_WIDTH=64
|
||||
MEMORY_DEVICE_3_DATA_WIDTH=64
|
||||
MEMORY_DEVICE_3_SIZE=8589934592
|
||||
MEMORY_DEVICE_3_FORM_FACTOR=DIMM
|
||||
MEMORY_DEVICE_3_LOCATOR=ChannelB-DIMM1
|
||||
MEMORY_DEVICE_3_BANK_LOCATOR=BANK 3
|
||||
MEMORY_DEVICE_3_TYPE=DDR4
|
||||
MEMORY_DEVICE_3_TYPE_DETAIL=Synchronous
|
||||
MEMORY_DEVICE_3_SPEED_MTS=2400
|
||||
MEMORY_DEVICE_3_MANUFACTURER=859B
|
||||
MEMORY_DEVICE_3_SERIAL_NUMBER=A0254F38
|
||||
MEMORY_DEVICE_3_ASSET_TAG=9876543210
|
||||
MEMORY_DEVICE_3_PART_NUMBER=BLT8G4D26AFTA.16FBD
|
||||
MEMORY_DEVICE_3_RANK=2
|
||||
MEMORY_DEVICE_3_CONFIGURED_SPEED_MTS=2400
|
||||
MEMORY_DEVICE_3_MINIMUM_VOLTAGE=1
|
||||
MEMORY_DEVICE_3_MAXIMUM_VOLTAGE=1
|
||||
MEMORY_DEVICE_3_CONFIGURED_VOLTAGE=1
|
||||
BIN
test/fuzz/fuzz-journal-remote/oss-fuzz-25353
Normal file
BIN
test/fuzz/fuzz-journal-remote/oss-fuzz-25353
Normal file
Binary file not shown.
@ -133,4 +133,11 @@ if conf.get('ENABLE_HWDB') == 1
|
||||
endif
|
||||
endif
|
||||
|
||||
if want_tests != false
|
||||
udev_dmi_memory_id_test = find_program('udev-dmi-memory-id-test.sh')
|
||||
test('udev-dmi-memory-id-test',
|
||||
udev_dmi_memory_id_test,
|
||||
timeout : 90)
|
||||
endif
|
||||
|
||||
subdir('fuzz')
|
||||
|
||||
31
test/udev-dmi-memory-id-test.sh
Executable file
31
test/udev-dmi-memory-id-test.sh
Executable file
@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
export SYSTEMD_LOG_LEVEL=info
|
||||
ROOTDIR=$(dirname $(dirname $(readlink -f $0)))
|
||||
UDEV_DMI_MEMORY_ID=./src/udev/dmi_memory_id
|
||||
|
||||
if [ ! -x "$UDEV_DMI_MEMORY_ID" ]; then
|
||||
echo "$UDEV_DMI_MEMORY_ID does not exist, please build first"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
D=$(mktemp --tmpdir --directory "udev-dmi-memory-id.XXXXXXXXXX")
|
||||
trap "rm -rf '$D'" EXIT INT QUIT PIPE
|
||||
|
||||
for i in $ROOTDIR/test/dmidecode-dumps/*.bin ; do
|
||||
$("$UDEV_DMI_MEMORY_ID" -F "$i" 2>&1 > "$D"/out.txt) && rc= || rc=$?
|
||||
if [ -n "$rc" ]; then
|
||||
echo "$UDEV_DMI_MEMORY_ID returned $rc"
|
||||
exit $rc
|
||||
fi
|
||||
err=$(diff -u "$D"/out.txt "$i.txt" 2>&1) && rc= || rc=$?
|
||||
if [ -n "$rc" ]; then
|
||||
echo "Parsing DMI memory information from \"$i\" didn't match expected:"
|
||||
echo "$err"
|
||||
exit $rc
|
||||
fi
|
||||
done
|
||||
@ -221,7 +221,7 @@ else
|
||||
pid1StartUnitWithStrace $testUnit
|
||||
systemctlCheckNUMAProperties $testUnit "default" "0"
|
||||
pid1StopUnit $testUnit
|
||||
# Maks must be ignored
|
||||
# Mask must be ignored
|
||||
grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog
|
||||
|
||||
echo "Unit file NUMAPolicy support - Bind policy w/o mask"
|
||||
@ -276,7 +276,7 @@ else
|
||||
pid1StartUnitWithStrace $testUnit
|
||||
systemctlCheckNUMAProperties $testUnit "local" "0"
|
||||
pid1StopUnit $testUnit
|
||||
# Maks must be ignored
|
||||
# Mask must be ignored
|
||||
grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog
|
||||
|
||||
echo "Unit file CPUAffinity=NUMA support"
|
||||
|
||||
@ -32,7 +32,7 @@ ProtectKernelLogs=yes
|
||||
ProtectKernelModules=yes
|
||||
ProtectKernelTunables=yes
|
||||
ProtectSystem=strict
|
||||
ReadWritePaths=/etc
|
||||
ReadWritePaths=/etc /run/systemd
|
||||
RestrictAddressFamilies=AF_UNIX
|
||||
RestrictNamespaces=yes
|
||||
RestrictRealtime=yes
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user