1
0
mirror of https://github.com/systemd/systemd synced 2026-03-05 20:54:45 +01:00

Compare commits

...

41 Commits

Author SHA1 Message Date
Lennart Poettering
e4dde4e87d
Merge pull request #17702 from rnhmjoj/master
Extend $SYSTEMD_COLORS to switch colors mode
2020-12-16 19:26:40 +01:00
Lennart Poettering
a8af734e75
Merge pull request #17026 from fw-strlen/nft_16
add networkd/nspawn nftables backend
2020-12-16 19:18:22 +01:00
Bastien Nocera
4dd465cb4e udev: Extract RAM properties from DMI information
Add memory_id program to set properties about the physical memory
devices in the system. This is useful on machines with removable memory
modules to show how the machine can be upgraded, and on all devices to
detect the actual RAM size, without relying on the OS accessible amount.

Closes: #16651
2020-12-16 18:32:29 +01:00
Yu Watanabe
84a1ff9457 tree-wide: fix typo 2020-12-16 18:30:15 +01:00
Zbigniew Jędrzejewski-Szmek
ac5644635d test-login: skip consistency checks when logind is not active
There are two ways in swich sd_login_* functions acquire data:
some are derived from the cgroup path, but others use the data serialized
by logind.

When the tests are executed under Fedora's mock, without systemd-spawn
but instead in a traditional chroot, test-login gets confused:
the "outside" cgroup path is visible, so sd_pid_get_unit() and
sd_pid_get_session() work, but sd_session_is_active() and other functions
that need logind data fail.

Such a buildroot setup is fairly bad, but it can be encountered in the wild, so
let's just skip the tests in that case.

/* Information printed is from the live system */
sd_pid_get_unit(0, …) → "session-237.scope"
sd_pid_get_user_unit(0, …) → "n/a"
sd_pid_get_slice(0, …) → "user-1000.slice"
sd_pid_get_session(0, …) → "237"
sd_pid_get_owner_uid(0, …) → 1000
sd_pid_get_cgroup(0, …) → "/user.slice/user-1000.slice/session-237.scope"
sd_uid_get_display(1000, …) → "(null)"
sd_uid_get_sessions(1000, …) → [0] ""
sd_uid_get_seats(1000, …) → [0] ""
Assertion 'r >= 0' failed at src/libsystemd/sd-login/test-login.c:104, function test_login(). Aborting.
2020-12-16 18:22:32 +01:00
Devon Pringle
221019166f networkd: handle ignoring ll gateway being link ll
In the event where network discovery gets a route with the gateway being
the interfaces local link address, networkd will fail the interface.

systemd-networkd[44319]: br_lan: Configuring route: dst: fdcd:41a4:5559:ec03::/64, src: n/a, gw: fe80::e4da:7eff:fe77:5c5e, prefsrc: n/a, scope: global, table: main, proto: ra, type: unicast
systemd-networkd[44319]: br_lan: Could not set NDisc route or address: Gateway can not be a local address. Invalid argument
systemd-networkd[44319]: br_lan: Failed
systemd-networkd[44319]: br_lan: State changed: configuring -> failed

This patch, instead of allowing the interface to fail, will instead log
the event and skip setting the route.
2020-12-16 17:56:01 +01:00
Zbigniew Jędrzejewski-Szmek
199f75205b
Merge pull request #17799 from yuwata/oss-fuzz-25353
logs-show: skip non-utf8 name entries
2020-12-16 17:52:35 +01:00
Luna Jernberg
91174d2803 Translated using Weblate (Swedish)
Currently translated at 67.3% (126 of 187 strings)

Co-authored-by: Luna Jernberg <bittin@reimu.nl>
Translate-URL: https://translate.fedoraproject.org/projects/systemd/master/sv/
Translation: systemd/master
2020-12-16 17:27:22 +01:00
Yu Watanabe
24ee0f9d16 tree-wide: fix typo 2020-12-16 17:21:48 +01:00
Yu Watanabe
ecfbbf098c
Merge pull request #17859 from keszybz/hostnamed-export-hostname-origin-and-simplify-logic
Export hostname origin and simplify logic in hostamed
2020-12-17 00:55:49 +09:00
Susant Sahani
b43dfb6e6e sd-netlink: routing policy rule port to fib_rule_hdr 2020-12-17 00:17:32 +09:00
Lennart Poettering
5777c6130b resolved: always take a timestamp when first seeing a packet
This is later useful if we want to adjust the TTLs of packets we want to
propagate to clients.
2020-12-16 14:20:49 +01:00
Dan Streetman
8bc63a0b13 sd-dhcp-client: fix renew/rebind timeout calculation to avoid infinite loop
unfortunately I missed adding the timeout to 'now' which results in
an endless loop of renewal timeouts.

Fixes: 3a23834d6b0da391c1ba9cb79a7d7deea7125f4b
2020-12-16 19:49:31 +09:00
Zbigniew Jędrzejewski-Szmek
60e4fb4240 hostnamed,shared/hostname-setup: expose the origin of the current hostname
In hostnamed this is exposed as a dbus property, and in the logs in both
places.

This is of interest to network management software and such: if the fallback
hostname is used, it's not as useful as the real configured thing. Right now
various programs try to guess the source of hostname by looking at the string.
E.g. "localhost" is assumed to be not the real hostname, but "fedora" is. Any
such attempts are bound to fail, because we cannot distinguish "fedora" (a
fallback value set by a distro), from "fedora" (received from reverse dns),
from "fedora" read from /etc/hostname.

/run/systemd/fallback-hostname is written with the fallback hostname when
either pid1 or hostnamed sets the kernel hostname to the fallback value. Why
remember the fallback value and not the transient hostname in /run/hostname
instead?
We have three hostname types: "static", "transient", fallback".
– Distinguishing "static" is easy: the hostname that is set matches what
  is in /etc/hostname.
– Distingiushing "transient" and "fallback" is not easy. And the
  "transient" hostname may be set outside of pid1+hostnamed. In particular,
  it may be set by container manager, some non-systemd tool in the initramfs,
  or even by a direct call. All those mechanisms count as "transient". Trying
  to get those cases to write /run/hostname is futile. It is much easier to
  isolate the "fallback" case which is mostly under our control.
And since the file is only used as a flag to mark the hostname as fallback,
it can be hidden inside of our /run/systemd directory.

For https://bugzilla.redhat.com/show_bug.cgi?id=1892235.
2020-12-16 11:03:36 +01:00
Zbigniew Jędrzejewski-Szmek
d39079fcaa hostnamed: stop discriminating against "localhost" in /etc/hostname
We would sometimes ignore localhost-style names in /etc/hostname. That is
brittle. If the user configured some hostname, it's most likely because they
want to use that as the hostname. If they don't want to use such a hostname,
they should just not create the config. Everything becomes simples if we just
use the configured hostname as-is.

This behaviour seems to have been a workaround for Anaconda installer and other
tools writing out /etc/hostname with the default of "localhost.localdomain".
Anaconda PR to stop doing that: https://github.com/rhinstaller/anaconda/pull/3040.
That might have been useful as a work-around for other programs misbehaving if
/etc/hostname was not present, but nowadays it's not useful because systemd
mostly controls the hostname and it is perfectly happy without that file.

Apart from making things simpler, this allows users to set a hostname like
"localhost" and have it honoured, if such a whim strikes them.
2020-12-16 11:02:18 +01:00
Zbigniew Jędrzejewski-Szmek
468695c8cd hostnamed: improve message about static hostname
Changed static hostname to 'n/a' is not very nice.
2020-12-16 11:02:18 +01:00
Zbigniew Jędrzejewski-Szmek
536970d4f9 hostnamed: minor style cleanups 2020-12-16 11:02:18 +01:00
Zbigniew Jędrzejewski-Szmek
efda832d4f hostnamed: when hostname is set to existing value, suppress notifications
When the hostname is set through network config or such, let's
optimize things a bit by suppressing the logs and dbus notifications.
2020-12-16 11:02:17 +01:00
Zbigniew Jędrzejewski-Szmek
39ede7cc37 shared/hostname-setup: leave the terminator byte alone
gethostname(3) says it's unspecified whether the string is properly terminated
when the hostname is too long. We created a buffer with one extra byte, and it
seems the intent was to let that byte serve as terminator even if we get an
unterminated string from gethostname().
2020-12-16 11:02:17 +01:00
Zbigniew Jędrzejewski-Szmek
b6fad30665 shared/hostname-setup: add mode where we check what would be set, without doing
This allows the 'unsafe' mark to be removed from the test.
2020-12-16 11:02:17 +01:00
Zbigniew Jędrzejewski-Szmek
e2054217d5 Move hostname setup logic to new shared/hostname-setup.[ch]
No functional change, just moving a bunch of things around. Before
we needed a rather complicated setup to test hostname_setup(), because
the code was in src/core/. When things are moved to src/shared/
we can just test it as any function.

The test is still "unsafe" because hostname_setup() may modify the
hostname.
2020-12-16 11:02:15 +01:00
Zbigniew Jędrzejewski-Szmek
7d9ec60990 hostnamed: fix return value 2020-12-16 10:54:57 +01:00
Zbigniew Jędrzejewski-Szmek
ce6b138c75 hostnamed: expose the fallback-hostname setting as a const dbus property
Various users want to know what the fallback hostname is. Since it was made
configurable in 8146c32b9264a6915d467a5cab1a24311fbede7e, we didn't expose this
nicely.
2020-12-16 10:54:57 +01:00
Zbigniew Jędrzejewski-Szmek
de31bbc6b1 man/hostnamectl,hostaned,hostname1: adjust the docs to match reality
The semantics were significantly changed in c779a44222161155c039a7fd2fd304c006590ac7
("hostnamed: Fix the way that static and transient host names interact", Feb. 2014),
but when the dbus api documentation was imported much later, it wasn't properly
adjusted to describe those new semantics.

34293dfafd2a81d80727938199769906dab321bd which added systemd.hostname= also
added new behaviour.

Let's ove various bits and pieces around so that they are in more appropriate
places. Drop recommendations to set the hostname for DHCP or mDNS purposes.
Nowadays we expect tools that want to expose some different hostname to the
outside to manage that internally without affecting visible state. Also drop
mentions of DHCP or mDNS directly setting the hostname, since nowadays network
management software is expected to (and does) go through hostnamed.

Also, add a high-level description of semantics. It glosses over the details of
handling of localhost-style names. Later commits will remove this special handling
anyway.
2020-12-16 10:54:57 +01:00
Florian Westphal
bc5a9b82d5 firewall-util-nft: attempt table recreation when add operation fails
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. IOW, the next add operation will 'just work'.

In the nftables case however, the entire table gets removed.

When the systemd nat table is removed by an external entity next
attempt to add a set element will yield -ENOENT.

If this happens, recreate the table, and, if successful, re-do
the add operation.

Note that this doesn't protect against external sabotage such as
a running 'while true; nft flush ruleset;done'. However, there is
nothing that could be done short of extending the kernel to allow
tables to be "frozen" or otherwise tied to a process such as
systemd-networkd.
2020-12-16 01:07:11 +01:00
Florian Westphal
715a70e721 firewall-util: add nftables backend
Idea is to use a static ruleset, added when the first attempt to
add a masquerade or dnat rule is made.

The alternative would be to add the ruleset when the init function is called.
The disadvantage is that this enables connection tracking and NAT in the kernel
(as the ruleset needs this to work), which comes with some overhead that might
not be needed (no nspawn usage and no IPMasquerade option set).

There is no additional dependency on the 'nft' userspace binary or other libraries.
sd-netlinks nfnetlink backend is used to modify the nftables ruleset.

The commit message/comments still use nft syntax since that is what
users will see when they use the nft tool to list the ruleset.

The added initial skeleton (added on first fw_add_masquerade/local_dnat
call) looks like this:

table ip io.systemd.nat {
        set masq_saddr {
                type ipv4_addr
                flags interval
                elements = { 192.168.59.160/28 }
        }

        map map_port_ipport {
                type inet_proto . inet_service : ipv4_addr . inet_service
                elements = { tcp . 2222 : 192.168.59.169 . 22 }
        }

        chain prerouting {
                type nat hook prerouting priority dstnat + 1; policy accept;
                fib daddr type local dnat ip addr . port to meta l4proto . th dport map @map_port_ipport
        }

        chain output {
                type nat hook output priority -99; policy accept;
                ip daddr != 127.0.0.0/8 oif "lo" dnat ip addr . port to meta l4proto . th dport map @map_port_ipport
        }

        chain postrouting {
                type nat hook postrouting priority srcnat + 1; policy accept;
                ip saddr @masq_saddr masquerade
        }
}

Next calls to fw_add_masquerade/add_local_dnat will then only add/delete the
element/mapping to masq_saddr and map_port_ipport, i.e. the ruleset doesn't
change -- only the set/map content does.

Running test-firewall-util with this backend gives following output
on a parallel 'nft monitor':

$ nft monitor
add table ip io.systemd.nat
add chain ip io.systemd.nat prerouting { type nat hook prerouting priority dstnat + 1; policy accept; }
add chain ip io.systemd.nat output { type nat hook output priority -99; policy accept; }
add chain ip io.systemd.nat postrouting { type nat hook postrouting priority srcnat + 1; policy accept; }
add set ip io.systemd.nat masq_saddr { type ipv4_addr; flags interval; }
add map ip io.systemd.nat map_port_ipport { type inet_proto . inet_service : ipv4_addr . inet_service; }
add rule ip io.systemd.nat prerouting fib daddr type local dnat ip addr . port to meta l4proto . th dport map @map_port_ipport
add rule ip io.systemd.nat output ip daddr != 127.0.0.0/8 fib daddr type local dnat ip addr . port to meta l4proto . th dport map @map_port_ipport
add rule ip io.systemd.nat postrouting ip saddr @masq_saddr masquerade
add element ip io.systemd.nat masq_saddr { 10.1.2.3 }
add element ip io.systemd.nat masq_saddr { 10.0.2.0/28 }
delete element ip io.systemd.nat masq_saddr { 10.0.2.0/28 }
delete element ip io.systemd.nat masq_saddr { 10.1.2.3 }
add element ip io.systemd.nat map_port_ipport { tcp . 4711 : 1.2.3.4 . 815 }
delete element ip io.systemd.nat map_port_ipport { tcp . 4711 : 1.2.3.4 . 815 }
add element ip io.systemd.nat map_port_ipport { tcp . 4711 : 1.2.3.5 . 815 }
delete element ip io.systemd.nat map_port_ipport { tcp . 4711 : 1.2.3.5 . 815 }
CTRL-C

Things not implemented/supported:
1. Change monitoring.  The kernel allows userspace to learn about changes
   made by other clients (using nfnetlink notifications). It would be
   possible to detect when e.g. someone removes the systemd nat table.
   This would need more work.  Its also not clear on how to react to
   external changes -- it doesn't seem like a good idea to just auto-undo
   everthing.
2. 'set masq_saddr' doesn't handle overlaps.
   Example:

   fw_add_masquerade(true, AF_INET, "10.0.0.0" , 16);
   fw_add_masquerade(true, AF_INET, "10.0.0.0" , 8); /* fails */

With the iptables backend the second call works, as it adds an
independent iptables rule.

With the nftables backend, the range 10.0.0.0-10.255.255.255 clashes with
the existing range of 10.0.0.0-10.0.255.255 so 2nd add gets rejected by the
kernel.

This will generate an error message from networkd ("Could not enable IP
masquerading: File exists").

To resolve this it would be needed to either keep track of the added elements
and perform range merging when overlaps are detected.

However, the add erquests are done using the configured network on a
device, so no overlaps should occur in normal setups.

IPv6 support is added in a extra changeset.

Fixes: #13307
2020-12-16 01:07:08 +01:00
Florian Westphal
4df42cd99d sd-netlink: add a read function
Will be used by nftables nfnetlink backend.
It sends a series of netlink messages that form a nftables
update transaction.

The transaction will then generate a series of ack messages
(or an error).

This function will be used to read these acks.
2020-12-16 00:35:56 +01:00
Florian Westphal
99c41c0de4 sd-netlink: add sd_netlink_sendv
nftables uses a transaction-based netlink model: one netlink write
comes with multiple messages.

A 'BEGIN' message to tell nf_tables/kernel that a new transaction starts.

Then, one more messages to add/delete tables/chains/rules etc.

Lastly, an END message that commits all changes.

This function will be used to send all the individual messages that should
make up a single transaction as a single write.
2020-12-16 00:35:56 +01:00
Florian Westphal
347ea16797 sd-netlink: add nfnetlink helper routines
add nfnetlink_nftables helper functions to:
 * open a new nfnetlink socket to kernel
 * add tables, chains, rules, sets and maps
 * delete/flush table
 * add and delete elements from sets/maps
2020-12-16 00:35:56 +01:00
Florian Westphal
6f00fd9b7c sd-netlink: add nfnetlink/nftables type system
Will be used by upcoming nftables support -- it will use the netlink
interface directly rather than add another library dependency.
2020-12-16 00:35:56 +01:00
Florian Westphal
bcd1a2bb5c linux: import nf_tables and nfnetlink headers from Linux 5.8
Will be used/needed in the upcoming nfnetlink/nftables support.
This follows existing model where kernel uapi headers are cached
locally.
2020-12-16 00:35:56 +01:00
Florian Westphal
761cf19d7b firewall-util: introduce context structure
for planned nft backend we have three choices:

- open/close a new nfnetlink socket for every operation
- keep a nfnetlink socket open internally
- expose a opaque fw_ctx and stash all internal data here.

Originally I opted for the 2nd option, but during review it was
suggested to avoid static storage duration because of perceived
problems with threaded applications.

This adds fw_ctx and new/free functions, then converts the existing api
and nspawn and networkd to use it.
2020-12-16 00:35:56 +01:00
Florian Westphal
f51343d0af nspawn: pass userdata pointer, not inet_addr union
Next patch will need to pass two pointers to the callback instead
of just the addr mask.  Caller will pass a compound structure, so
make this 'void *userdata' to de-clutter the next patch.
2020-12-16 00:35:56 +01:00
Florian Westphal
3122097217 firewall-util: prepare for alternative to iptables backend
In a nutshell:
1. git mv firewall-util.c firewall-util-iptables.c
2. existing external functions gain _iptables_ in their names
3. firewall-util.c provides old function names
4. build system always compiles firewall-util.c,
   firewall-util-iptables.c is conditional instead (libiptc).
5. On first call to any of the 'old' API functions performs
   a probe that should return the preferred backend.

In a future step, can add firewall-util-FOOTYPE.c, add its
probe function to firewall-util.c and then have calls to
fw_add_masq/local_dnat handed to the detected backend.

For now, only iptables backend exists, and no special probing
takes place for it, i.e. when systemd was built with iptables,
that will be used.  If not, requets to add masquerade/dnat will
fail with same error (-EOPNOTSUPP) as before this change.

For reference, the rules added by the libiptc/iptables backend look like this:

for service export (via systemd-nspawn):
[0:0] -A PREROUTING -p tcp -m tcp --dport $exportedport -m addrtype --dst-type LOCAL -j DNAT --to-destination $containerip:$port
[0:0] -A OUTPUT ! -d 127.0.0.0/8 -p tcp -m tcp --dport $exportedport -m addrtype --dst-type LOCAL -j DNAT --to-destination $containerip:$port

for ip masquerade:
[0:0] -A POSTROUTING -s network/prefix -j MASQUERADE
2020-12-16 00:35:56 +01:00
Florian Westphal
47ed20e1e0 firewall-util: reject NULL source or address with prefixlen 0
Make sure we don't add masquerading rules without a explicitly
specified network range we should be masquerading for.

The only caller aside from test case is
networkd-address.c which never passes a NULL source.

As it also passes the network prefix, that should always be > 0 as well.

This causes expected test failure:
Failed to modify firewall: Invalid argument
Failed to modify firewall: Invalid argument
Failed to modify firewall: Invalid argument
Failed to modify firewall: Protocol not available
Failed to modify firewall: Protocol not available
Failed to modify firewall: Protocol not available
Failed to modify firewall: Protocol not available

The failing test cases are amended to expect failure on
NULL source or prefix instead of success.
2020-12-16 00:35:56 +01:00
Yu Watanabe
805d67c565 logs-show: refuse data which contain invalid fields 2020-12-16 04:56:57 +09:00
Yu Watanabe
f2bd032044 journal: refuse data which contain invalid fields
Fixes https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=25353.
2020-12-16 04:56:35 +09:00
Yu Watanabe
adce225a10 journal: move journal_field_valid() to journal_file.c 2020-12-16 04:48:44 +09:00
rnhmjoj
dcdd9030ba
basic/term-util: inline colors_enabled function
There is no need to cache colors_enabled because the function
is now simply calling get_color_mode, which is already cached.
2020-12-15 19:40:30 +01:00
rnhmjoj
25e4608b8b
tree-wide: avoid direct use of color macros 2020-12-15 19:29:42 +01:00
rnhmjoj
c4fea19abb
basic/term-util: extend $SYSTEMD_COLORS
This commit extends $SYSTEMD_COLORS to an enum variable (compared to
a simple boolean) which specifies the "colors mode". This means that, in
addition to disabling colors altogether, it's now possible to restrict
the console output to 16 or 256 colors only.
2020-12-15 19:29:42 +01:00
92 changed files with 5990 additions and 1080 deletions

1
.gitattributes vendored
View File

@ -1 +1,2 @@
*.[ch] whitespace=tab-in-indent,trailing-space
test/dmidecode-dumps/*.bin binary

2
TODO
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View 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 */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
int hostname_setup(void);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -123,7 +123,7 @@ fuzzers += [
[libshared],
[]],
[['src/fuzz/fuzz-hostname-util.c'],
[['src/fuzz/fuzz-hostname-setup.c'],
[libshared],
[]],

View File

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

View File

@ -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. */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View 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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

Binary file not shown.

View File

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

View File

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

View File

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