1
0
mirror of https://github.com/systemd/systemd synced 2026-03-29 03:04:52 +02:00

Compare commits

...

63 Commits

Author SHA1 Message Date
Lennart Poettering
721956f3e9
Merge pull request #20219 from khfeng/use-intel-hid-rfkill
hwdb: Remove intel-hid rfkill mask
2021-09-29 18:53:22 +02:00
Yu Watanabe
aebff2e7ce core/mount: add implicit unit dependencies even if when mount unit is generated from /proc/self/mountinfo
Hopefully fixes #20566.
2021-09-29 17:25:13 +02:00
Yu Watanabe
209abeac6d
Merge pull request #20824 from yuwata/sd-dhcp6-client-cleanups
sd-dhcp6-client: several cleanups for parsing options
2021-09-30 00:08:16 +09:00
Yu Watanabe
ca6bc7ce0d
Merge pull request #20226 from yuwata/network-introduce-source-and-status
network: introduce NetworkConfigSource and NetworkConfigState
2021-09-30 00:06:17 +09:00
Yu Watanabe
bfcc0fedd0
Merge pull request #20871 from mrc0mmand/udevadm-property-value
udev: teach udevadm --property=NAME and --value options
2021-09-30 00:05:02 +09:00
Yu Watanabe
504cfa6573
Merge pull request #20877 from yuwata/network-test-mode
network: do not update state files when running in test mode
2021-09-30 00:04:41 +09:00
Marcel Menzel
3e90ded70c
doc: network: Move "Independent=" flag to the VXLAN section (#20881) 2021-09-29 23:19:20 +09:00
Yu Watanabe
78fac35811
Merge pull request #20823 from mrc0mmand/test-storage-iscsi
test: iSCSI-related udev tests
2021-09-29 23:17:43 +09:00
Hans de Goede
f813515542 hwdb: sensors: Fix some modalias matches no longer working with newer kernels
Kernels >= 5.8 have added new fields to the dmi/id/modalias file in the
middle of the modalias (instead of adding them at the end).

Specifically new ":br<value>:" and (optional) ":efr<value>:" fields have
been added between the ":bd<value>:" and ":svn<value>:" fields.

Note the 5.13.0 and 5.14.0 kernels also added a new ":sku<value>:" field
between the ":pvr<value>:" and ":rvn<value>:" fields, this has been fixed
in later 5.13.y and 5.14.y releases, by moving the sku field to the end:
https://lore.kernel.org/lkml/20210831130508.14511-1-hdegoede@redhat.com/

Unfortunately the same cannot be done for the new br and efr fields since
those have been added more then a year ago and hwdb even already has some
newer entries relying on the new br field being there (and thus not working
with older kernels).

Fix the issue with the br and efr fields through the following changes:

1. Replace any matches on ":br<value>" from newer entries with an '*'
2. Replace "bd<value>:svn<value>" matches with: "bd<value>:*svn<value>"
   inserting an '*' where newer kernels will have the new br + efr fields

This makes these matches working with old as well as new kernels.

Link: https://github.com/systemd/systemd/issues/20550
Link: https://github.com/systemd/systemd/pull/20562
2021-09-29 16:03:06 +02:00
Anita Zhang
14bb729534 basic/unit-file: don't filter out names starting with dot
Fixes #20859
Reverts 3796bdc55d6ba499d1049f749072218879e619a7
2021-09-29 14:42:13 +02:00
Lennart Poettering
e30a3ba16a core: drop "const" from NeedsDaemonReload unit dbus property
It's not "const", it can change any time if people change the fs, and we
don#t send out notifications for it. Hence don't claim it was const.
(Otherwise clients might cache it, but they should not)

Prompted-by: #20792
2021-09-29 14:37:07 +02:00
Lennart Poettering
bee07a3995 resolvconf-compat: make "-u" operation a NOP
According to the various man pages of "resolvconf" the -u switch is for:

"-u Just run the update scripts (if updating is enabled)."

"-u Force resolvconf to update all its subscribers. resolvconf does not
    update the subscribers when adding a resolv.conf that matches what
    it already has for that interface."

We have no "subscribers", we ourselves are the only "subscriber" we
support. Hence it's probably better to ignore such a request and make it
a NOP, then to fail.

Fixes: #20748
2021-09-29 14:36:47 +02:00
Yu Watanabe
6d350f7d82 Revert "CI: run unit tests in a network namespace"
This reverts commit 8b036b223a40fac9e53189db05d3798ec11eb475.
2021-09-29 20:50:37 +09:00
Yu Watanabe
faa2e64f9b network: do not configure anything when running in test mode 2021-09-29 20:50:37 +09:00
Yu Watanabe
4c78dc17e5 network: do not update state files when running in test mode
Fixes #20862.
2021-09-29 20:50:37 +09:00
Yu Watanabe
92fc611cac
Merge pull request #20802 from yuwata/network-receive-nl80211-multicast-messages
network: receive nl80211 multicast messages
2021-09-29 20:49:38 +09:00
Frantisek Sumsal
6c1482b28d udev: teach udevadm --property=NAME and --value options
which allows limiting the properties listed by the `--query=property` option
(and optionally listing only the respective values).
2021-09-29 13:32:25 +02:00
Frantisek Sumsal
3c318caa6f basic: introduce test_strv_split_and_extend() 2021-09-29 13:32:22 +02:00
Luca Boccassi
c1036042f5 CI: run GCC unit test job on push to main
Allows to get coverage data on coveralls.io
2021-09-29 14:10:42 +03:00
Frantisek Sumsal
bbc1bb0742 udev: sort the options alphabetically 2021-09-29 12:52:57 +02:00
Frantisek Sumsal
9cb41c3326 test: iSCSI-related udev tests 2021-09-29 10:05:21 +02:00
Frantisek Sumsal
aedb60043a test: save journals of only failing test cases in TEST-64 2021-09-29 10:05:21 +02:00
Frantisek Sumsal
7074c047c1 test: explicitly report if we fail to install a file into the image 2021-09-29 10:05:21 +02:00
Frantisek Sumsal
f4e64b6e34 test: add an iSCSI helper 2021-09-29 10:05:21 +02:00
Frantisek Sumsal
5f25c30ee8 test: sort the features alphabetically 2021-09-29 10:05:21 +02:00
Yu Watanabe
96f5f9ef9a network: receive genl multicast messages about wlan connections 2021-09-29 15:56:20 +09:00
Yu Watanabe
f12629ae38 network: move error handling of enumerating configs to caller side 2021-09-29 15:56:09 +09:00
Yu Watanabe
bdcd4ab2f1 network: make manager_enumerate_internal() take sd_netlink object
Preparation for dumping information through generic netlink.
2021-09-29 15:38:59 +09:00
Yu Watanabe
96243149bd network: split manager_new() into two part
Initialize dbus or netlink is not necessary for fuzzers.
2021-09-29 15:38:59 +09:00
Yu Watanabe
16653f9782 wifi-util: introduce nl80211_cmd_to_string() 2021-09-29 15:38:59 +09:00
Yu Watanabe
77f75f4fff network: rename wifi_iftype -> wlan_iftype 2021-09-29 15:38:59 +09:00
Yu Watanabe
abad436d4c wifi-util: move, rename, and expose wifi_iftype_to_string() 2021-09-29 15:38:59 +09:00
Yu Watanabe
8e310690b0 sd-netlink: add several attributes for nl80211 2021-09-29 15:38:59 +09:00
Yu Watanabe
f3e235ffb2 sd-netlink, wifi-util: fix attribute type of NL80211_ATTR_SSID 2021-09-29 15:38:59 +09:00
Yu Watanabe
ae2b86de43 wifi-util: add "ret_" prefix for arguments which store results 2021-09-29 15:38:59 +09:00
Yu Watanabe
a1d2ae065b sd-netlink: introduce sd_netlink_message_read_data_suffix0() 2021-09-29 15:38:59 +09:00
Yu Watanabe
84f7bd7698 sd-netlink: make sd_genl_message_new() or friends return -EOPNOTSUPP if a module is not supported by the kernel 2021-09-29 15:38:55 +09:00
Yu Watanabe
4d4d898a4b sd-netlink: also check multicast group to find suitable match callback 2021-09-29 15:36:42 +09:00
Yu Watanabe
ec44d3f407 sd-netlink: allow to set dump flag for genl or netfilter messages 2021-09-29 15:35:08 +09:00
Yu Watanabe
dbc35fe3aa sd-netlink: add type safe macro for sd_genl_add_match() 2021-09-29 15:33:50 +09:00
Yu Watanabe
52888279df sd-netlink: minor coding style fixes 2021-09-29 15:33:50 +09:00
Yu Watanabe
61dcd98232 basic/linux: update nl80211.h 2021-09-29 15:33:50 +09:00
Yu Watanabe
e210f027e0 sd-dhcp6-client: rename dhcp6_lease_set_dns() -> dhcp6_lease_add_dns()
As it is allowed that multiple DNS server options appear in the message.
2021-09-29 15:29:41 +09:00
Yu Watanabe
e693e96961 sd-dhcp6-client: do not merge NTP and SNTP options
Previously, SNTP option is ignored when it appears after NTP option(s),
but merged later NTP options when it appears first.
This makes split the NTP and SNTP addresses, and use SNTP addresses only
when no NTP option is provided.
2021-09-29 15:29:41 +09:00
Yu Watanabe
9c3d46bf8d sd-dhcp6-client: support multiple NTP server options
Also, it is allowed that the suboption appears multiple times, and each
suboption contains one NTP server.
2021-09-29 15:29:41 +09:00
Yu Watanabe
41b14f0329 sd-dhcp6-client: support multiple domains 2021-09-29 15:29:41 +09:00
Yu Watanabe
5cf67bb407 sd-dhcp6-client: make dhcp6_lease_free() accepts NULL 2021-09-29 15:29:40 +09:00
Yu Watanabe
edeee50b3d sd-dhcp6-client: drop domains_count and ntp_fqdn_count 2021-09-29 15:29:40 +09:00
Yu Watanabe
548c33d712 sd-dhcp6-client: use dhcp6_option_parse() in client_parse_message() 2021-09-29 15:29:40 +09:00
Yu Watanabe
3f8227bf83 sd-dhcp6-client: constify several arguments 2021-09-29 15:29:40 +09:00
Yu Watanabe
b27dcf0813 sd-dhcp6-client: slightly modernize dhcp6_option_parse_domainname()/domainname_list() 2021-09-29 15:29:40 +09:00
Yu Watanabe
ad3c84204c sd-dhcp6-client: fix buffer size calculation in dhcp6_option_parse_ip6addrs()
GREEDY_REALLOC() takes number of elements, not buffer size.

This also rename dhcp6_option_parse_ip6addrs() to
dhcp6_option_parse_addresses().
2021-09-29 15:29:40 +09:00
Yu Watanabe
8a8955507a sd-dhcp6-client: modernize dhcp6_option_parse_ia()
This makes
- the function not update the arguments for storing results on error,
- use dhcp6_option_parse() to parse sub options,
- ignore all errors, except for -ENOMEM, in parsing sub options,
- update log messages.
2021-09-29 15:29:40 +09:00
Yu Watanabe
4af39cb8ec sd-dhcp6-client: make dhcp6_option_parse_status() also parse error message
This also introduce dhcp6_option_parse_ia_options(). Currently, it is
assumed that each IA address or PD prefix may contain a status sub-option.
But it is not prohibited that other sub-options or multiple status
options are contained.
2021-09-29 15:29:36 +09:00
Yu Watanabe
e620104956 test: add tests for reading unaligned data 2021-09-29 15:18:59 +09:00
Yu Watanabe
b89a3758e9 sd-dhcp6-client: modernize dhcp6_option_parse()
- merge dhcp6_option_parse() with option_parse_hdr().
- do not assign/update any values on error.
- use assert() instead of assert_return(), as the assertions cannot
  be triggered by a library user.
2021-09-29 15:18:59 +09:00
Yu Watanabe
dc95e21d33 sd-dhcp6-client: constify one argument 2021-09-29 15:18:59 +09:00
Yu Watanabe
b4c7934a3c network: drop unnecessary link_drop_foreign_config()
Now, link_drop_foreign_config() drops subset of configs which can be
dropped by link_drop_config().
2021-09-24 21:46:36 +09:00
Yu Watanabe
b8355de49e network: drop unused features in request queue 2021-09-24 21:46:36 +09:00
Yu Watanabe
b87e96e2f2 network: address_equal() is not used anymore, hence move it to test-network.c 2021-09-24 21:46:36 +09:00
Yu Watanabe
3b6a3bdebf network: use NetworkConfigSource/State to manage addresses and routes
This also fixes #20146.
2021-09-24 21:46:32 +09:00
Kai-Heng Feng
3c88ade5a0 NEWS: Note Intel HID rfkill mask is removed 2021-08-11 23:00:11 +08:00
Kai-Heng Feng
0889107a0a hwdb: Remove intel-hid rfkill mask
Turns out HP is phasing out hp-wireless in new models, so we need to use
intel-hid for rfkill events.

To have best backward and forward compatibility, the solution is
implemented in userspace:
f4dbcf3d7b
2021-08-11 18:36:18 +08:00
80 changed files with 3614 additions and 3275 deletions

View File

@ -6,7 +6,6 @@ ADDITIONAL_DEPS=(
clang clang
expect expect
fdisk fdisk
iproute2
jekyll jekyll
lcov lcov
libfdisk-dev libfdisk-dev
@ -20,7 +19,6 @@ ADDITIONAL_DEPS=(
perl perl
python3-libevdev python3-libevdev
python3-pyparsing python3-pyparsing
util-linux
zstd zstd
) )
@ -55,8 +53,7 @@ for phase in "${PHASES[@]}"; do
fi fi
meson --werror -Dtests=unsafe -Dslow-tests=true -Dfuzz-tests=true "${MESON_ARGS[@]}" build meson --werror -Dtests=unsafe -Dslow-tests=true -Dfuzz-tests=true "${MESON_ARGS[@]}" build
ninja -C build -v ninja -C build -v
# Some of the unsafe tests irreparably break the host's network connectivity, so run them in a namespace meson test -C build --print-errorlogs
unshare -n bash -c 'ip link set dev lo up; meson test -C build --print-errorlogs'
if [[ "$phase" = "RUN_GCC" ]]; then if [[ "$phase" = "RUN_GCC" ]]; then
ninja -C build coverage ninja -C build coverage
fi fi
@ -88,7 +85,7 @@ for phase in "${PHASES[@]}"; do
# during debugging, wonderful), so let's at least keep a workaround # during debugging, wonderful), so let's at least keep a workaround
# here to make the builds stable for the time being. # here to make the builds stable for the time being.
(set +x; while :; do echo -ne "\n[WATCHDOG] $(date)\n"; sleep 30; done) & (set +x; while :; do echo -ne "\n[WATCHDOG] $(date)\n"; sleep 30; done) &
unshare -n bash -c 'ip link set dev lo up; meson test --timeout-multiplier=3 -C build --print-errorlogs' meson test --timeout-multiplier=3 -C build --print-errorlogs
;; ;;
CLEANUP) CLEANUP)
info "Cleanup phase" info "Cleanup phase"

View File

@ -3,6 +3,10 @@
# #
name: Unit tests name: Unit tests
on: on:
# On push/merge to main we only run the GCC job, to get coverage data uploaded to coveralls.io
push:
branches:
- main
pull_request: pull_request:
branches: branches:
- main - main
@ -17,10 +21,13 @@ jobs:
steps: steps:
- name: Repository checkout - name: Repository checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
if: github.event_name == 'pull_request' || matrix.run_phase == 'GCC'
- name: Install build dependencies - name: Install build dependencies
run: sudo -E .github/workflows/unit_tests.sh SETUP run: sudo -E .github/workflows/unit_tests.sh SETUP
if: github.event_name == 'pull_request' || matrix.run_phase == 'GCC'
- name: Build & test (${{ matrix.run_phase }}) - name: Build & test (${{ matrix.run_phase }})
run: sudo -E .github/workflows/unit_tests.sh RUN_${{ matrix.run_phase }} run: sudo -E .github/workflows/unit_tests.sh RUN_${{ matrix.run_phase }}
if: github.event_name == 'pull_request' || matrix.run_phase == 'GCC'
- name: Coveralls - name: Coveralls
if: matrix.run_phase == 'GCC' && github.repository == 'systemd/systemd' if: matrix.run_phase == 'GCC' && github.repository == 'systemd/systemd'
uses: coverallsapp/github-action@master uses: coverallsapp/github-action@master

5
NEWS
View File

@ -518,6 +518,11 @@ CHANGES WITH 249:
distribution does not install it yet, it might make sense to change distribution does not install it yet, it might make sense to change
that. that.
* Intel HID rfkill event is no longer masked, since it's the only
source of rfkill event on newer HP laptops. To have both backward and
forward compatibility, userspace daemon needs to debounce duplicated
events in a short time window.
Contributions from: Aakash Singh, adrian5, Albert Brox, Contributions from: Aakash Singh, adrian5, Albert Brox,
Alexander Sverdlin, Alexander Tsoy, Alexey Rubtsov, alexlzhu, Alexander Sverdlin, Alexander Tsoy, Alexey Rubtsov, alexlzhu,
Allen Webb, Alvin Šipraga, Alyssa Ross, Anders Wenhaug, Allen Webb, Alvin Šipraga, Alyssa Ross, Anders Wenhaug,

View File

@ -494,9 +494,6 @@ evdev:input:b0003v0458p0708*
# Hewlett Packard # Hewlett Packard
########################################################### ###########################################################
evdev:name:Intel HID events:dmi:bvn*:bvr*:bd*:svnHP*:pn*:*
KEYBOARD_KEY_8=unknown # Use hp-wireless instead
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*:* evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*:*
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pn*:* evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pn*:*
KEYBOARD_KEY_81=fn_esc KEYBOARD_KEY_81=fn_esc
@ -560,9 +557,6 @@ evdev:atkbd:dmi:bvn*:bvr*:svnHP*:pnHPElitex21013G3:*
KEYBOARD_KEY_92=brightnessdown KEYBOARD_KEY_92=brightnessdown
KEYBOARD_KEY_97=brightnessup KEYBOARD_KEY_97=brightnessup
evdev:name:Intel HID events:dmi:bvn*:bvr*:svnHP*:pnHPElitex21013G3:*
KEYBOARD_KEY_08=unknown # rfkill is also reported by HP Wireless hotkeys
# Elitebook # Elitebook
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*Compaq*:* evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*Compaq*:*
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*EliteBook*:* evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*EliteBook*:*

View File

@ -230,8 +230,9 @@ sensor:modalias:acpi:BOSC0200*:dmi:*:svnChuwi*:pnHi13:*
# Chuwi HiBook does not have its product name filled, so we # Chuwi HiBook does not have its product name filled, so we
# match the entire dmi-alias, assuming that the use of a BOSC0200 + # match the entire dmi-alias, assuming that the use of a BOSC0200 +
# bios-version + bios-date combo is unique # bios-version + bios-date combo is unique
sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd05/07/2016:svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnHampoo:rnCherryTrailCR:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:* # '*' in ":*svn" is there because kernels >= 5.8 have inserted a br field there
sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd05/28/2016:svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnHampoo:rnCherryTrailCR:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:* sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd05/07/2016:*svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnHampoo:rnCherryTrailCR:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:*
sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd05/28/2016:*svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnHampoo:rnCherryTrailCR:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:*
ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1 ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
# Chuwi HiBook Pro (CWI526) # Chuwi HiBook Pro (CWI526)
@ -241,7 +242,8 @@ sensor:modalias:acpi:BOSC0200*:dmi:*:svnHampoo*:pnP1D6_C109K:*
# Chuwi CoreBook # Chuwi CoreBook
# Chuwi CoreBook does not have its product name filled, so we # Chuwi CoreBook does not have its product name filled, so we
# match the entire dmi-alias # match the entire dmi-alias
sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvrY13D_KB133.103:bd06/01/2018:svnHampoo:pnDefaultstring:pvrV100:rvnHampoo:rnY13D_KB133:rvrV100:cvnDefaultstring:ct9:cvrDefaultstring:* # '*' in ":*svn" is there because kernels >= 5.8 have inserted a br field there
sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvrY13D_KB133.103:bd06/01/2018:*svnHampoo:pnDefaultstring:pvrV100:rvnHampoo:rnY13D_KB133:rvrV100:cvnDefaultstring:ct9:cvrDefaultstring:*
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1 ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
# Chuwi SurBook Mini (CWI540) # Chuwi SurBook Mini (CWI540)
@ -410,13 +412,14 @@ sensor:modalias:acpi:KIOX000A*:dmi:bvnINSYDECorp.:bvrBYT70A.YNCHENG.WIN.007:*:sv
# and no other devices have both board_name *and* product_name set to # and no other devices have both board_name *and* product_name set to
# "Default string". So combined with the sensor modalias and BIOS date this # "Default string". So combined with the sensor modalias and BIOS date this
# should be unique enough to identify the GPDwin # should be unique enough to identify the GPDwin
sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd10/25/2016:svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnAMICorporation:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:* # '*' in ":*svn" is there because kernels >= 5.8 have inserted a br field there
sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd11/18/2016:svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnAMICorporation:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:* sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd10/25/2016:*svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnAMICorporation:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:*
sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd12/23/2016:svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnAMICorporation:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:* sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd11/18/2016:*svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnAMICorporation:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:*
sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd12/26/2016:svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnAMICorporation:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:* sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd12/23/2016:*svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnAMICorporation:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:*
sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd02/21/2017:svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnAMICorporation:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:* sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd12/26/2016:*svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnAMICorporation:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:*
sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd03/20/2017:svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnAMICorporation:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:* sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd02/21/2017:*svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnAMICorporation:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:*
sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd05/25/2017:svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnAMICorporation:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:* sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd03/20/2017:*svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnAMICorporation:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:*
sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd05/25/2017:*svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnAMICorporation:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:*
ACCEL_LOCATION=base ACCEL_LOCATION=base
######################################### #########################################
@ -704,7 +707,8 @@ sensor:modalias:acpi:BMA250E*:dmi:bvnINSYDECorp.:bvrONDA.W89*:svnInsyde:pnONDATa
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1 ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
# Onda v975w, generic DMI strings, match entire dmi modalias inc. bios-date # Onda v975w, generic DMI strings, match entire dmi modalias inc. bios-date
sensor:modalias:acpi:SMO8500*:dmi:bvnAmericanMegatrendsInc.:bvr5.6.5:bd07/25/2014:svnTobefilledbyO.E.M.:pnTobefilledbyO.E.M.:pvrTobefilledbyO.E.M.:rvnAMICorporation:rnAptioCRB:rvrTobefilledbyO.E.M.:cvnToBeFilledByO.E.M.:ct3:cvrToBeFilledByO.E.M.:* # '*' in ":*svn" is there because kernels >= 5.8 have inserted a br field there
sensor:modalias:acpi:SMO8500*:dmi:bvnAmericanMegatrendsInc.:bvr5.6.5:bd07/25/2014:*svnTobefilledbyO.E.M.:pnTobefilledbyO.E.M.:pvrTobefilledbyO.E.M.:rvnAMICorporation:rnAptioCRB:rvrTobefilledbyO.E.M.:cvnToBeFilledByO.E.M.:ct3:cvrToBeFilledByO.E.M.:*
ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1 ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1
######################################### #########################################
@ -713,7 +717,8 @@ sensor:modalias:acpi:SMO8500*:dmi:bvnAmericanMegatrendsInc.:bvr5.6.5:bd07/25/201
# One-netbook OneMix 2s # One-netbook OneMix 2s
# OneMix 2s has no product name filled, matching entire dmi-alias # OneMix 2s has no product name filled, matching entire dmi-alias
sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvr5.12:bd10/26/2018:br5.12:svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnDefaultstring:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:* # '*' in ":*svn" is there because kernels >= 5.8 have inserted a br field there
sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvr5.12:bd10/26/2018:*svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnDefaultstring:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:*
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1 ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
# One-netbook OneMix 3 Pro # One-netbook OneMix 3 Pro
@ -722,7 +727,8 @@ sensor:modalias:acpi:BOSC0200*:dmi:*svnONE-NETBOOKTECHNOLOGYCO*:pnOne-Mix3Pro:*
# One-netbook OneMix 3s # One-netbook OneMix 3s
# OneMix 3s has no product name filled, matching entire dmi-alias # OneMix 3s has no product name filled, matching entire dmi-alias
sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvr5.12:bd07/17/2019:br5.12:svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnDefaultstring:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:* # '*' in ":*svn" is there because kernels >= 5.8 have inserted a br field there
sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvr5.12:bd07/17/2019:*svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnDefaultstring:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:*
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1 ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
######################################### #########################################
@ -754,8 +760,9 @@ sensor:modalias:acpi:BMA250E*:dmi:*:svnShenzhenPLOYER*:pnMOMO7W:*
# The Point of View TAB-P800W does not have its product name filled, so we # The Point of View TAB-P800W does not have its product name filled, so we
# match the entire dmi-alias, assuming that the use of a BMA250E + # match the entire dmi-alias, assuming that the use of a BMA250E +
# bios-version + bios-date combo is unique # bios-version + bios-date combo is unique
sensor:modalias:acpi:BMA250E*:dmi:bvnAmericanMegatrendsInc.:bvr3BAIR1013:bd08/22/2014:svnTobefilledbyO.E.M.:pnTobefilledbyO.E.M.:pvrTobefilledbyO.E.M.:rvnAMICorporation:rnAptioCRB:rvrTobefilledbyO.E.M.:cvnToBeFilledByO.E.M.:ct3:cvrToBeFilledByO.E.M.:* # '*' in ":*svn" is there because kernels >= 5.8 have inserted a br field there
sensor:modalias:acpi:BMA250E*:dmi:bvnAmericanMegatrendsInc.:bvr3BAIR1014:bd10/24/2014:svnTobefilledbyO.E.M.:pnTobefilledbyO.E.M.:pvrTobefilledbyO.E.M.:rvnAMICorporation:rnAptioCRB:rvrTobefilledbyO.E.M.:cvnToBeFilledByO.E.M.:ct3:cvrToBeFilledByO.E.M.:* sensor:modalias:acpi:BMA250E*:dmi:bvnAmericanMegatrendsInc.:bvr3BAIR1013:bd08/22/2014:*svnTobefilledbyO.E.M.:pnTobefilledbyO.E.M.:pvrTobefilledbyO.E.M.:rvnAMICorporation:rnAptioCRB:rvrTobefilledbyO.E.M.:cvnToBeFilledByO.E.M.:ct3:cvrToBeFilledByO.E.M.:*
sensor:modalias:acpi:BMA250E*:dmi:bvnAmericanMegatrendsInc.:bvr3BAIR1014:bd10/24/2014:*svnTobefilledbyO.E.M.:pnTobefilledbyO.E.M.:pvrTobefilledbyO.E.M.:rvnAMICorporation:rnAptioCRB:rvrTobefilledbyO.E.M.:cvnToBeFilledByO.E.M.:ct3:cvrToBeFilledByO.E.M.:*
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1 ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
# Point of View TAB-P1005W-232 (v2.0) # Point of View TAB-P1005W-232 (v2.0)
@ -833,7 +840,8 @@ sensor:modalias:acpi:SMO8500*:dmi:*bd12/19/2014:*:rvnTECLAST:rntPAD:*
ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1 ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
# Teclast X98 Plus I (A5C6), generic DMI strings, match entire dmi modalias inc. bios-date # Teclast X98 Plus I (A5C6), generic DMI strings, match entire dmi modalias inc. bios-date
sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.011:bd11/03/2015:svnTobefilledbyO.E.M.:pnTobefilledbyO.E.M.:pvrTobefilledbyO.E.M.:rvnAMICorporation:rnCherryTrailCR:rvrTobefilledbyO.E.M.:cvnToBeFilledByO.E.M.:ct3:cvrToBeFilledByO.E.M.:* # '*' in ":*svn" is there because kernels >= 5.8 have inserted a br field there
sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.011:bd11/03/2015:*svnTobefilledbyO.E.M.:pnTobefilledbyO.E.M.:pvrTobefilledbyO.E.M.:rvnAMICorporation:rnCherryTrailCR:rvrTobefilledbyO.E.M.:cvnToBeFilledByO.E.M.:ct3:cvrToBeFilledByO.E.M.:*
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1 ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
# Teclast X98 Plus II # Teclast X98 Plus II
@ -845,7 +853,8 @@ sensor:modalias:acpi:KIOX000A*:dmi:*:svnTECLAST:pnX98PlusII:*
######################################### #########################################
# Thundersoft TST168 tablet, generic DMI strings, match entire dmi modalias inc. bios-date # Thundersoft TST168 tablet, generic DMI strings, match entire dmi modalias inc. bios-date
sensor:modalias:acpi:BMA250E*:dmi:bvnAmericanMegatrendsInc.:bvr5.6.5:bd04/15/2014:svnTobefilledbyO.E.M.:pnTobefilledbyO.E.M.:pvrTobefilledbyO.E.M.:rvnAMICorporation:rnAptioCRB:rvrTobefilledbyO.E.M.:cvnToBeFilledByO.E.M.:ct3:cvrToBeFilledByO.E.M.:* # '*' in ":*svn" is there because kernels >= 5.8 have inserted a br field there
sensor:modalias:acpi:BMA250E*:dmi:bvnAmericanMegatrendsInc.:bvr5.6.5:bd04/15/2014:*svnTobefilledbyO.E.M.:pnTobefilledbyO.E.M.:pvrTobefilledbyO.E.M.:rvnAMICorporation:rnAptioCRB:rvrTobefilledbyO.E.M.:cvnToBeFilledByO.E.M.:ct3:cvrToBeFilledByO.E.M.:*
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1 ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
######################################### #########################################
@ -914,7 +923,8 @@ sensor:modalias:acpi:SMO8500*:dmi:*:svnUMAX:pnVisionBook10WiPlus:*
# The Winpad A15 does not have its product name filled, so we # The Winpad A15 does not have its product name filled, so we
# match the entire dmi-alias, assuming that the use of a SMO8500 + # match the entire dmi-alias, assuming that the use of a SMO8500 +
# bios-version + bios-date combo is unique # bios-version + bios-date combo is unique
sensor:modalias:acpi:SMO8500*:dmi:bvnAmericanMegatrendsInc.:bvr5.6.5:bd11/20/2014:br5.6:svnTobefilledbyO.E.M.:pnTobefilledbyO.E.M.:pvrTobefilledbyO.E.M.:rvnAMICorporation:rnAptioCRB:rvrTobefilledbyO.E.M.:cvnToBeFilledByO.E.M.:ct3:cvrToBeFilledByO.E.M.:* # '*' in ":*svn" is there because kernels >= 5.8 have inserted a br field there
sensor:modalias:acpi:SMO8500*:dmi:bvnAmericanMegatrendsInc.:bvr5.6.5:bd11/20/2014:*svnTobefilledbyO.E.M.:pnTobefilledbyO.E.M.:pvrTobefilledbyO.E.M.:rvnAMICorporation:rnAptioCRB:rvrTobefilledbyO.E.M.:cvnToBeFilledByO.E.M.:ct3:cvrToBeFilledByO.E.M.:*
ACCEL_MOUNT_MATRIX=0, -1, 0; 1, 0, 0; 0, 0, -1 ACCEL_MOUNT_MATRIX=0, -1, 0; 1, 0, 0; 0, 0, -1
######################################### #########################################

View File

@ -1711,7 +1711,7 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
readonly s OnFailureJobMode = '...'; readonly s OnFailureJobMode = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b IgnoreOnIsolate = ...; readonly b IgnoreOnIsolate = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const") @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly b NeedDaemonReload = ...; readonly b NeedDaemonReload = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false") @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly as Markers = ['...', ...]; readonly as Markers = ['...', ...];

View File

@ -791,6 +791,14 @@
default will be used.</para> default will be used.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>Independent=</varname></term>
<listitem>
<para>Takes a boolean. When true, the vxlan interface is created without any underlying network
interface. Defaults to false, which means that a .network file that requests this VXLAN interface
using <varname>VXLAN=</varname> is required for the VXLAN to be created.</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect1> </refsect1>
@ -867,14 +875,6 @@
<para>Accepts the same key as in [VXLAN] section.</para> <para>Accepts the same key as in [VXLAN] section.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>Independent=</varname></term>
<listitem>
<para>Takes a boolean. When true, the vxlan interface is created without any underlying network
interface. Defaults to false, which means that a .network file that requests this tunnel using
<varname>Tunnel=</varname> is required for the tunnel to be created.</para>
</listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect1> </refsect1>

View File

@ -98,6 +98,24 @@
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--property=<replaceable>NAME</replaceable></option></term>
<listitem>
<para>When showing device properties using the <option>--query=property</option>
option, limit display to properties specified in the argument. The argument should
be a comma-separated list of property names. If not specified, all known properties
are shown.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--value</option></term>
<listitem>
<para>When showing device properties using the <option>--query=property</option>
option, print only their values, and skip the property name and <literal>=</literal>.</para>
<para>Cannot be used together with <option>-x/--export</option> or
<option>-P/--export-prefix</option>.</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>-p</option></term> <term><option>-p</option></term>
<term><option>--path=<replaceable>DEVPATH</replaceable></option></term> <term><option>--path=<replaceable>DEVPATH</replaceable></option></term>

View File

@ -49,8 +49,8 @@ _udevadm() {
[COMMON]='-h --help -V --version' [COMMON]='-h --help -V --version'
[DEBUG]='-d --debug' [DEBUG]='-d --debug'
[INFO_STANDALONE]='-r --root -a --attribute-walk -x --export -e --export-db -c --cleanup-db [INFO_STANDALONE]='-r --root -a --attribute-walk -x --export -e --export-db -c --cleanup-db
-w --wait-for-initialization' -w --wait-for-initialization --value'
[INFO_ARG]='-q --query -p --path -n --name -P --export-prefix -d --device-id-of-file' [INFO_ARG]='-q --query -p --path -n --name -P --export-prefix -d --device-id-of-file --property'
[TRIGGER_STANDALONE]='-v --verbose -n --dry-run -q --quiet -w --settle --wait-daemon --uuid' [TRIGGER_STANDALONE]='-v --verbose -n --dry-run -q --quiet -w --settle --wait-daemon --uuid'
[TRIGGER_ARG]='-t --type -c --action -s --subsystem-match -S --subsystem-nomatch [TRIGGER_ARG]='-t --type -c --action -s --subsystem-match -S --subsystem-nomatch
-a --attr-match -A --attr-nomatch -p --property-match -a --attr-match -A --attr-nomatch -p --property-match

View File

@ -13,7 +13,9 @@ _udevadm_info(){
'--export-prefix=[Add a prefix to the key name of exported values.]:prefix' \ '--export-prefix=[Add a prefix to the key name of exported values.]:prefix' \
'--device-id-of-file=[Print major/minor numbers of the underlying device, where the file lives on.]:files:_udevadm_mounts' \ '--device-id-of-file=[Print major/minor numbers of the underlying device, where the file lives on.]:files:_udevadm_mounts' \
'--export-db[Export the content of the udev database.]' \ '--export-db[Export the content of the udev database.]' \
'--cleanup-db[Cleanup the udev database.]' '--cleanup-db[Cleanup the udev database.]' \
'--value[When showing properties, print only their values.]' \
'--property=[Show only properties by this name.]'
} }
(( $+functions[_udevadm_trigger] )) || (( $+functions[_udevadm_trigger] )) ||

View File

@ -1185,6 +1185,21 @@
* passed using %NL80211_ATTR_SAR_SPEC. %NL80211_ATTR_WIPHY is used to * passed using %NL80211_ATTR_SAR_SPEC. %NL80211_ATTR_WIPHY is used to
* specify the wiphy index to be applied to. * specify the wiphy index to be applied to.
* *
* @NL80211_CMD_OBSS_COLOR_COLLISION: This notification is sent out whenever
* mac80211/drv detects a bss color collision.
*
* @NL80211_CMD_COLOR_CHANGE_REQUEST: This command is used to indicate that
* userspace wants to change the BSS color.
*
* @NL80211_CMD_COLOR_CHANGE_STARTED: Notify userland, that a color change has
* started
*
* @NL80211_CMD_COLOR_CHANGE_ABORTED: Notify userland, that the color change has
* been aborted
*
* @NL80211_CMD_COLOR_CHANGE_COMPLETED: Notify userland that the color change
* has completed
*
* @NL80211_CMD_MAX: highest used command number * @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use * @__NL80211_CMD_AFTER_LAST: internal use
*/ */
@ -1417,6 +1432,14 @@ enum nl80211_commands {
NL80211_CMD_SET_SAR_SPECS, NL80211_CMD_SET_SAR_SPECS,
NL80211_CMD_OBSS_COLOR_COLLISION,
NL80211_CMD_COLOR_CHANGE_REQUEST,
NL80211_CMD_COLOR_CHANGE_STARTED,
NL80211_CMD_COLOR_CHANGE_ABORTED,
NL80211_CMD_COLOR_CHANGE_COMPLETED,
/* add new commands above here */ /* add new commands above here */
/* used to define NL80211_CMD_MAX below */ /* used to define NL80211_CMD_MAX below */
@ -2560,6 +2583,16 @@ enum nl80211_commands {
* disassoc events to indicate that an immediate reconnect to the AP * disassoc events to indicate that an immediate reconnect to the AP
* is desired. * is desired.
* *
* @NL80211_ATTR_OBSS_COLOR_BITMAP: bitmap of the u64 BSS colors for the
* %NL80211_CMD_OBSS_COLOR_COLLISION event.
*
* @NL80211_ATTR_COLOR_CHANGE_COUNT: u8 attribute specifying the number of TBTT's
* until the color switch event.
* @NL80211_ATTR_COLOR_CHANGE_COLOR: u8 attribute specifying the color that we are
* switching to
* @NL80211_ATTR_COLOR_CHANGE_ELEMS: Nested set of attributes containing the IE
* information for the time while performing a color switch.
*
* @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined * @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use * @__NL80211_ATTR_AFTER_LAST: internal use
@ -3057,6 +3090,12 @@ enum nl80211_attrs {
NL80211_ATTR_DISABLE_HE, NL80211_ATTR_DISABLE_HE,
NL80211_ATTR_OBSS_COLOR_BITMAP,
NL80211_ATTR_COLOR_CHANGE_COUNT,
NL80211_ATTR_COLOR_CHANGE_COLOR,
NL80211_ATTR_COLOR_CHANGE_ELEMS,
/* add attributes here, update the policy in nl80211.c */ /* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST, __NL80211_ATTR_AFTER_LAST,
@ -5953,6 +5992,9 @@ enum nl80211_feature_flags {
* frame protection for all management frames exchanged during the * frame protection for all management frames exchanged during the
* negotiation and range measurement procedure. * negotiation and range measurement procedure.
* *
* @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision
* detection and change announcemnts.
*
* @NUM_NL80211_EXT_FEATURES: number of extended features. * @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index. * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
*/ */
@ -6017,6 +6059,7 @@ enum nl80211_ext_feature_index {
NL80211_EXT_FEATURE_SECURE_LTF, NL80211_EXT_FEATURE_SECURE_LTF,
NL80211_EXT_FEATURE_SECURE_RTT, NL80211_EXT_FEATURE_SECURE_RTT,
NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE, NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
NL80211_EXT_FEATURE_BSS_COLOR,
/* add new features before the definition below */ /* add new features before the definition below */
NUM_NL80211_EXT_FEATURES, NUM_NL80211_EXT_FEATURES,

View File

@ -44,3 +44,8 @@
#ifndef BOND_MAX_ARP_TARGETS #ifndef BOND_MAX_ARP_TARGETS
#define BOND_MAX_ARP_TARGETS 16 #define BOND_MAX_ARP_TARGETS 16
#endif #endif
/* Not exposed but defined in include/linux/ieee80211.h */
#ifndef IEEE80211_MAX_SSID_LEN
#define IEEE80211_MAX_SSID_LEN 32
#endif

View File

@ -300,6 +300,24 @@ int strv_split_full(char ***t, const char *s, const char *separators, ExtractFla
return (int) n; return (int) n;
} }
int strv_split_and_extend_full(char ***t, const char *s, const char *separators, bool filter_duplicates, ExtractFlags flags) {
_cleanup_strv_free_ char **l = NULL;
int r;
assert(t);
assert(s);
r = strv_split_full(&l, s, separators, flags);
if (r < 0)
return r;
r = strv_extend_strv(t, l, filter_duplicates);
if (r < 0)
return r;
return (int) strv_length(*t);
}
int strv_split_colon_pairs(char ***t, const char *s) { int strv_split_colon_pairs(char ***t, const char *s) {
_cleanup_strv_free_ char **l = NULL; _cleanup_strv_free_ char **l = NULL;
size_t n = 0; size_t n = 0;

View File

@ -83,6 +83,9 @@ static inline char **strv_split(const char *s, const char *separators) {
return ret; return ret;
} }
int strv_split_and_extend_full(char ***t, const char *s, const char *separators, bool filter_duplicates, ExtractFlags flags);
#define strv_split_and_extend(t, s, sep, dup) strv_split_and_extend_full(t, s, sep, dup, 0)
int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags); int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags);
static inline char **strv_split_newlines(const char *s) { static inline char **strv_split_newlines(const char *s) {
char **ret; char **ret;

View File

@ -284,7 +284,7 @@ int unit_file_build_name_map(
continue; continue;
} }
FOREACH_DIRENT(de, d, log_warning_errno(errno, "Failed to read \"%s\", ignoring: %m", *dir)) { FOREACH_DIRENT_ALL(de, d, log_warning_errno(errno, "Failed to read \"%s\", ignoring: %m", *dir)) {
char *filename; char *filename;
_unused_ _cleanup_free_ char *_filename_free = NULL; _unused_ _cleanup_free_ char *_filename_free = NULL;
_cleanup_free_ char *simplified = NULL; _cleanup_free_ char *simplified = NULL;

View File

@ -909,7 +909,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("OnSuccessJobMode", "s", property_get_job_mode, offsetof(Unit, on_success_job_mode), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("OnSuccessJobMode", "s", property_get_job_mode, offsetof(Unit, on_success_job_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("OnFailureJobMode", "s", property_get_job_mode, offsetof(Unit, on_failure_job_mode), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("OnFailureJobMode", "s", property_get_job_mode, offsetof(Unit, on_failure_job_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("IgnoreOnIsolate", "b", bus_property_get_bool, offsetof(Unit, ignore_on_isolate), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("IgnoreOnIsolate", "b", bus_property_get_bool, offsetof(Unit, ignore_on_isolate), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NeedDaemonReload", "b", property_get_need_daemon_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("NeedDaemonReload", "b", property_get_need_daemon_reload, 0, 0),
SD_BUS_PROPERTY("Markers", "as", property_get_markers, offsetof(Unit, markers), 0), SD_BUS_PROPERTY("Markers", "as", property_get_markers, offsetof(Unit, markers), 0),
SD_BUS_PROPERTY("JobTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_timeout), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("JobTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_timeout), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("JobRunningTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_running_timeout), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("JobRunningTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_running_timeout), SD_BUS_VTABLE_PROPERTY_CONST),

View File

@ -1575,6 +1575,10 @@ static int mount_setup_new_unit(
if (r < 0) if (r < 0)
return r; return r;
r = mount_add_non_exec_dependencies(MOUNT(u));
if (r < 0)
return r;
/* This unit was generated because /proc/self/mountinfo reported it. Remember this, so that by the time we load /* This unit was generated because /proc/self/mountinfo reported it. Remember this, so that by the time we load
* the unit file for it (and thus add in extra deps right after) we know what source to attributes the deps * the unit file for it (and thus add in extra deps right after) we know what source to attributes the deps
* to. */ * to. */

View File

@ -101,15 +101,29 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class); int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class);
int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *user_class); int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *user_class);
int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options); int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options);
int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
size_t *optlen, uint8_t **optvalue); int dhcp6_option_parse(
int dhcp6_option_parse_status(DHCP6Option *option, size_t len); const uint8_t *buf,
int dhcp6_option_parse_ia(sd_dhcp6_client *client, DHCP6Option *iaoption, be32_t iaid, DHCP6IA *ia, uint16_t *ret_status_code); size_t buflen,
int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, size_t *offset,
struct in6_addr **addrs, size_t count); uint16_t *ret_option_code,
int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen, size_t *ret_option_data_len,
char ***str_arr); const uint8_t **ret_option_data);
int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char **str); int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message);
int dhcp6_option_parse_ia(
sd_dhcp6_client *client,
be32_t iaid,
uint16_t option_code,
size_t option_data_len,
const uint8_t *option_data,
DHCP6IA *ret);
int dhcp6_option_parse_addresses(
const uint8_t *optval,
size_t optlen,
struct in6_addr **addrs,
size_t *count);
int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret);
int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret);
int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address); int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address);
int dhcp6_network_send_udp_socket(int s, struct in6_addr *address, int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,

View File

@ -28,11 +28,11 @@ struct sd_dhcp6_lease {
struct in6_addr *dns; struct in6_addr *dns;
size_t dns_count; size_t dns_count;
char **domains; char **domains;
size_t domains_count;
struct in6_addr *ntp; struct in6_addr *ntp;
size_t ntp_count; size_t ntp_count;
char **ntp_fqdn; char **ntp_fqdn;
size_t ntp_fqdn_count; struct in6_addr *sntp;
size_t sntp_count;
char *fqdn; char *fqdn;
}; };
@ -50,12 +50,10 @@ int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit);
int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid); int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid);
int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid); int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid);
int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
size_t optlen); int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) ;
int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval,
size_t optlen) ;
int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
int dhcp6_lease_new(sd_dhcp6_lease **ret); int dhcp6_lease_new(sd_dhcp6_lease **ret);

View File

@ -14,29 +14,12 @@
#include "dhcp6-lease-internal.h" #include "dhcp6-lease-internal.h"
#include "dhcp6-protocol.h" #include "dhcp6-protocol.h"
#include "dns-domain.h" #include "dns-domain.h"
#include "escape.h"
#include "memory-util.h" #include "memory-util.h"
#include "sparse-endian.h" #include "sparse-endian.h"
#include "strv.h" #include "strv.h"
#include "unaligned.h" #include "unaligned.h"
typedef struct DHCP6StatusOption {
struct DHCP6Option option;
be16_t status;
char msg[];
} _packed_ DHCP6StatusOption;
typedef struct DHCP6AddressOption {
struct DHCP6Option option;
struct iaaddr iaaddr;
uint8_t options[];
} _packed_ DHCP6AddressOption;
typedef struct DHCP6PDPrefixOption {
struct DHCP6Option option;
struct iapdprefix iapdprefix;
uint8_t options[];
} _packed_ DHCP6PDPrefixOption;
#define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na)) #define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na))
#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd)) #define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd))
#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta)) #define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta))
@ -370,367 +353,410 @@ int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const
return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_CLASS, total, p); return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_CLASS, total, p);
} }
static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) { int dhcp6_option_parse(
DHCP6Option *option = (DHCP6Option*) *buf; const uint8_t *buf,
uint16_t len; size_t buflen,
size_t *offset,
uint16_t *ret_option_code,
size_t *ret_option_data_len,
const uint8_t **ret_option_data) {
assert_return(buf, -EINVAL); const DHCP6Option *option;
assert_return(optcode, -EINVAL); size_t len;
assert_return(optlen, -EINVAL);
if (*buflen < offsetof(DHCP6Option, data)) assert(buf);
return -ENOMSG; assert(offset);
assert(ret_option_code);
assert(ret_option_data_len);
assert(ret_option_data);
if (buflen < offsetof(DHCP6Option, data))
return -EBADMSG;
if (*offset >= buflen - offsetof(DHCP6Option, data))
return -EBADMSG;
option = (const DHCP6Option*) (buf + *offset);
len = be16toh(option->len); len = be16toh(option->len);
if (len > *buflen) if (len > buflen - offsetof(DHCP6Option, data) - *offset)
return -EBADMSG;
*offset += offsetof(DHCP6Option, data) + len;
*ret_option_code = be16toh(option->code);
*ret_option_data_len = len;
*ret_option_data = option->data;
return 0;
}
int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message) {
assert(data);
if (data_len < sizeof(uint16_t))
return -EBADMSG;
if (ret_status_message) {
char *msg;
/* The status message MUST NOT be null-terminated. See section 21.13 of RFC8415.
* Let's escape unsafe characters for safety. */
msg = cescape_length((const char*) (data + sizeof(uint16_t)), data_len - sizeof(uint16_t));
if (!msg)
return -ENOMEM;
*ret_status_message = msg;
}
return unaligned_read_be16(data);
}
static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) {
int r;
assert(buf);
for(size_t offset = 0; offset < buflen;) {
const uint8_t *data;
size_t data_len;
uint16_t code;
r = dhcp6_option_parse(buf, buflen, &offset, &code, &data_len, &data);
if (r < 0)
return r;
switch(code) {
case SD_DHCP6_OPTION_STATUS_CODE: {
_cleanup_free_ char *msg = NULL;
r = dhcp6_option_parse_status(data, data_len, &msg);
if (r == -ENOMEM)
return r;
if (r < 0)
/* Let's log but ignore the invalid status option. */
log_dhcp6_client_errno(client, r,
"Received an IA address or PD prefix option with an invalid status sub option, ignoring: %m");
else if (r > 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received an IA address or PD prefix option with non-zero status: %s%s%s",
strempty(msg), isempty(msg) ? "" : ": ",
dhcp6_message_status_to_string(r));
break;
}
default:
log_dhcp6_client(client, "Received an unknown sub option %u in IA address or PD prefix, ignoring.", code);
}
}
return 0;
}
static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) {
uint32_t lt_valid, lt_pref;
DHCP6Address *a;
int r;
assert(data);
assert(ret);
if (len < sizeof(struct iaaddr))
return -EBADMSG;
lt_valid = be32toh(((const struct iaaddr*) data)->lifetime_valid);
lt_pref = be32toh(((const struct iaaddr*) data)->lifetime_preferred);
if (lt_valid == 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received an IA address with zero valid lifetime, ignoring.");
if (lt_pref > lt_valid)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received an IA address with preferred lifetime %"PRIu32
" larger than valid lifetime %"PRIu32", ignoring.",
lt_pref, lt_valid);
if (len > sizeof(struct iaaddr)) {
r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iaaddr), len - sizeof(struct iaaddr));
if (r < 0)
return r;
}
a = new(DHCP6Address, 1);
if (!a)
return -ENOMEM;
LIST_INIT(addresses, a);
memcpy(&a->iaaddr, data, sizeof(struct iaaddr));
*ret = a;
return 0;
}
static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) {
uint32_t lt_valid, lt_pref;
DHCP6Address *a;
int r;
if (len < sizeof(struct iapdprefix))
return -ENOMSG; return -ENOMSG;
*optcode = be16toh(option->code); lt_valid = be32toh(((const struct iapdprefix*) data)->lifetime_valid);
*optlen = len; lt_pref = be32toh(((const struct iapdprefix*) data)->lifetime_preferred);
*buf += 4; if (lt_valid == 0)
*buflen -= 4;
return 0;
}
int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
size_t *optlen, uint8_t **optvalue) {
int r;
assert_return(buf && buflen && optcode && optlen && optvalue, -EINVAL);
r = option_parse_hdr(buf, buflen, optcode, optlen);
if (r < 0)
return r;
if (*optlen > *buflen)
return -ENOBUFS;
*optvalue = *buf;
*buflen -= *optlen;
*buf += *optlen;
return 0;
}
int dhcp6_option_parse_status(DHCP6Option *option, size_t len) {
DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option;
if (len < sizeof(DHCP6StatusOption) ||
be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(DHCP6StatusOption))
return -ENOBUFS;
return be16toh(statusopt->status);
}
static int dhcp6_option_parse_address(sd_dhcp6_client *client, DHCP6Option *option, DHCP6IA *ia, uint32_t *ret_lifetime_valid) {
DHCP6AddressOption *addr_option = (DHCP6AddressOption *)option;
DHCP6Address *addr;
uint32_t lt_valid, lt_pref;
int r;
if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*addr_option))
return -ENOBUFS;
lt_valid = be32toh(addr_option->iaaddr.lifetime_valid);
lt_pref = be32toh(addr_option->iaaddr.lifetime_preferred);
if (lt_valid == 0 || lt_pref > lt_valid)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Valid lifetime of an IA address is zero or " "Received a PD prefix with zero valid lifetime, ignoring.");
"preferred lifetime %"PRIu32" > valid lifetime %"PRIu32, if (lt_pref > lt_valid)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received a PD prefix with preferred lifetime %"PRIu32
" larger than valid lifetime %"PRIu32", ignoring.",
lt_pref, lt_valid); lt_pref, lt_valid);
if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*addr_option)) { if (len > sizeof(struct iapdprefix)) {
r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*addr_option)); r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iapdprefix), len - sizeof(struct iapdprefix));
if (r < 0) if (r < 0)
return r; return r;
if (r > 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Non-zero status code '%s' for address is received",
dhcp6_message_status_to_string(r));
} }
addr = new0(DHCP6Address, 1); a = new(DHCP6Address, 1);
if (!addr) if (!a)
return -ENOMEM; return -ENOMEM;
LIST_INIT(addresses, addr); LIST_INIT(addresses, a);
memcpy(&addr->iaaddr, option->data, sizeof(addr->iaaddr)); memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix));
LIST_PREPEND(addresses, ia->addresses, addr);
*ret_lifetime_valid = be32toh(addr->iaaddr.lifetime_valid);
return 0;
}
static int dhcp6_option_parse_pdprefix(sd_dhcp6_client *client, DHCP6Option *option, DHCP6IA *ia, uint32_t *ret_lifetime_valid) {
DHCP6PDPrefixOption *pdprefix_option = (DHCP6PDPrefixOption *)option;
DHCP6Address *prefix;
uint32_t lt_valid, lt_pref;
int r;
if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*pdprefix_option))
return -ENOBUFS;
lt_valid = be32toh(pdprefix_option->iapdprefix.lifetime_valid);
lt_pref = be32toh(pdprefix_option->iapdprefix.lifetime_preferred);
if (lt_valid == 0 || lt_pref > lt_valid)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Valid lifetieme of a PD prefix is zero or "
"preferred lifetime %"PRIu32" > valid lifetime %"PRIu32,
lt_pref, lt_valid);
if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*pdprefix_option)) {
r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*pdprefix_option));
if (r < 0)
return r;
if (r > 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Non-zero status code '%s' for PD prefix is received",
dhcp6_message_status_to_string(r));
}
prefix = new0(DHCP6Address, 1);
if (!prefix)
return -ENOMEM;
LIST_INIT(addresses, prefix);
memcpy(&prefix->iapdprefix, option->data, sizeof(prefix->iapdprefix));
LIST_PREPEND(addresses, ia->addresses, prefix);
*ret_lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid);
*ret = a;
return 0; return 0;
} }
int dhcp6_option_parse_ia( int dhcp6_option_parse_ia(
sd_dhcp6_client *client, sd_dhcp6_client *client,
DHCP6Option *iaoption,
be32_t iaid, be32_t iaid,
DHCP6IA *ia, uint16_t option_code,
uint16_t *ret_status_code) { size_t option_data_len,
const uint8_t *option_data,
DHCP6IA *ret) {
uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX; _cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {};
uint16_t iatype, optlen; uint32_t lt_t1, lt_t2, lt_min = UINT32_MAX;
size_t iaaddr_offset; be32_t received_iaid;
int r = 0, status; size_t offset;
size_t i, len; int r;
uint16_t opt;
assert_return(ia, -EINVAL); assert(IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA, SD_DHCP6_OPTION_IA_PD));
assert_return(!ia->addresses, -EINVAL); assert(option_data);
assert(ret);
iatype = be16toh(iaoption->code); /* This will return the following:
len = be16toh(iaoption->len); * -ENOMEM: memory allocation error,
* -ENOANO: unmatching IAID,
* -EINVAL: non-zero status code, or invalid lifetime,
* -EBADMSG: invalid message format,
* -ENODATA: no valid address or PD prefix,
* 0: success. */
switch (iatype) { switch (option_code) {
case SD_DHCP6_OPTION_IA_NA: case SD_DHCP6_OPTION_IA_NA:
if (len < DHCP6_OPTION_IA_NA_LEN) if (option_data_len < DHCP6_OPTION_IA_NA_LEN)
return -ENOBUFS; return -EBADMSG;
/* According to RFC8415, IAs which do not match the client's IAID should be ignored, offset = DHCP6_OPTION_IA_NA_LEN;
* but not necessary to ignore or refuse the whole message. */
if (((const struct ia_na*) iaoption->data)->id != iaid)
/* ENOANO indicates the option should be ignored. */
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO),
"Received an IA_NA option with a different IAID "
"from the one chosen by the client, ignoring.");
iaaddr_offset = DHCP6_OPTION_IA_NA_LEN;
memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na));
lt_t1 = be32toh(ia->ia_na.lifetime_t1);
lt_t2 = be32toh(ia->ia_na.lifetime_t2);
if (lt_t1 > lt_t2)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"IA NA T1 %"PRIu32"sec > T2 %"PRIu32"sec",
lt_t1, lt_t2);
received_iaid = ((const struct ia_na*) option_data)->id;
lt_t1 = be32toh(((const struct ia_na*) option_data)->lifetime_t1);
lt_t2 = be32toh(((const struct ia_na*) option_data)->lifetime_t2);
break; break;
case SD_DHCP6_OPTION_IA_PD: case SD_DHCP6_OPTION_IA_PD:
if (len < sizeof(ia->ia_pd)) if (option_data_len < DHCP6_OPTION_IA_PD_LEN)
return -ENOBUFS; return -EBADMSG;
/* According to RFC8415, IAs which do not match the client's IAID should be ignored, offset = DHCP6_OPTION_IA_PD_LEN;
* but not necessary to ignore or refuse the whole message. */
if (((const struct ia_pd*) iaoption->data)->id != iaid)
/* ENOANO indicates the option should be ignored. */
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO),
"Received an IA_PD option with a different IAID "
"from the one chosen by the client, ignoring.");
iaaddr_offset = sizeof(ia->ia_pd);
memcpy(&ia->ia_pd, iaoption->data, sizeof(ia->ia_pd));
lt_t1 = be32toh(ia->ia_pd.lifetime_t1);
lt_t2 = be32toh(ia->ia_pd.lifetime_t2);
if (lt_t1 > lt_t2)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"IA PD T1 %"PRIu32"sec > T2 %"PRIu32"sec",
lt_t1, lt_t2);
received_iaid = ((const struct ia_pd*) option_data)->id;
lt_t1 = be32toh(((const struct ia_pd*) option_data)->lifetime_t1);
lt_t2 = be32toh(((const struct ia_pd*) option_data)->lifetime_t2);
break; break;
case SD_DHCP6_OPTION_IA_TA: case SD_DHCP6_OPTION_IA_TA:
if (len < DHCP6_OPTION_IA_TA_LEN) if (option_data_len < DHCP6_OPTION_IA_TA_LEN)
return -ENOBUFS; return -ENOMSG;
/* According to RFC8415, IAs which do not match the client's IAID should be ignored, offset = DHCP6_OPTION_IA_TA_LEN;
* but not necessary to ignore or refuse the whole message. */
if (((const struct ia_ta*) iaoption->data)->id != iaid)
/* ENOANO indicates the option should be ignored. */
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO),
"Received an IA_TA option with a different IAID "
"from the one chosen by the client, ignoring.");
iaaddr_offset = DHCP6_OPTION_IA_TA_LEN;
memcpy(&ia->ia_ta, iaoption->data, sizeof(ia->ia_ta));
received_iaid = ((const struct ia_ta*) option_data)->id;
lt_t1 = lt_t2 = 0; /* No lifetime for IA_TA. */
break; break;
default: default:
return -EINVAL; assert_not_reached();
} }
ia->type = iatype; /* According to RFC8415, IAs which do not match the client's IAID should be ignored,
i = iaaddr_offset; * but not necessary to ignore or refuse the whole message. */
if (received_iaid != iaid)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO),
"Received an IA option with a different IAID "
"from the one chosen by the client, ignoring.");
while (i < len) { if (lt_t1 > lt_t2)
DHCP6Option *option = (DHCP6Option *)&iaoption->data[i];
if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len))
return -ENOBUFS;
opt = be16toh(option->code);
optlen = be16toh(option->len);
switch (opt) {
case SD_DHCP6_OPTION_IAADDR:
if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA))
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"IA Address option not in IA NA or TA option"); "Received an IA option with T1 %"PRIu32"sec > T2 %"PRIu32"sec, ignoring.",
lt_t1, lt_t2);
r = dhcp6_option_parse_address(client, option, ia, &lt_valid); for (; offset < option_data_len;) {
if (r < 0 && r != -EINVAL) const uint8_t *subdata;
size_t subdata_len;
uint16_t subopt;
r = dhcp6_option_parse(option_data, option_data_len, &offset, &subopt, &subdata_len, &subdata);
if (r < 0)
return r; return r;
if (r >= 0 && lt_valid < lt_min)
lt_min = lt_valid;
switch (subopt) {
case SD_DHCP6_OPTION_IAADDR: {
DHCP6Address *a;
if (!IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) {
log_dhcp6_client(client, "Received an IA_PD option with an IA address, ignoring.");
continue;
}
r = dhcp6_option_parse_ia_address(client, subdata, subdata_len, &a);
if (r == -ENOMEM)
return r;
if (r < 0)
/* Ignore the sub-option on non-critical errors. */
continue;
lt_min = MIN(lt_min, a->iaaddr.lifetime_valid);
LIST_PREPEND(addresses, ia.addresses, a);
break; break;
}
case SD_DHCP6_OPTION_IA_PD_PREFIX: {
DHCP6Address *a;
case SD_DHCP6_OPTION_IA_PD_PREFIX: if (option_code != SD_DHCP6_OPTION_IA_PD) {
log_dhcp6_client(client, "Received an IA_NA or IA_TA option with an PD prefix, ignoring");
continue;
}
if (ia->type != SD_DHCP6_OPTION_IA_PD) r = dhcp6_option_parse_ia_pdprefix(client, subdata, subdata_len, &a);
if (r == -ENOMEM)
return r;
if (r < 0)
/* Ignore the sub-option on non-critical errors. */
continue;
lt_min = MIN(lt_min, a->iapdprefix.lifetime_valid);
LIST_PREPEND(addresses, ia.addresses, a);
break;
}
case SD_DHCP6_OPTION_STATUS_CODE: {
_cleanup_free_ char *msg = NULL;
r = dhcp6_option_parse_status(subdata, subdata_len, &msg);
if (r == -ENOMEM)
return r;
if (r < 0)
log_dhcp6_client_errno(client, r,
"Received an IA option with an invalid status sub option, ignoring: %m");
else if (r > 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"IA PD Prefix option not in IA PD option"); "Received an IA option with non-zero status: %s%s%s",
strempty(msg), isempty(msg) ? "" : ": ",
r = dhcp6_option_parse_pdprefix(client, option, ia, &lt_valid); dhcp6_message_status_to_string(r));
if (r < 0 && r != -EINVAL)
return r;
if (r >= 0 && lt_valid < lt_min)
lt_min = lt_valid;
break; break;
}
default:
log_dhcp6_client(client, "Received an IA option with an unknown sub-option %u, ignoring", subopt);
}
}
case SD_DHCP6_OPTION_STATUS_CODE: if (!ia.addresses)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENODATA),
"Received an IA option without valid IA addresses or PD prefixes, ignoring.");
status = dhcp6_option_parse_status(option, optlen + offsetof(DHCP6Option, data)); if (IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_PD) &&
if (status < 0) lt_t1 == 0 && lt_t2 == 0 && lt_min != UINT32_MAX) {
return status; lt_t1 = lt_min / 2;
lt_t2 = lt_min / 10 * 8;
if (status > 0) { log_dhcp6_client(client, "Received an IA option with both T1 and T2 equal to zero. "
if (ret_status_code) "Adjusting them based on the minimum valid lifetime of IA addresses or PD prefixes: "
*ret_status_code = status; "T1=%"PRIu32"sec, T2=%"PRIu32"sec", lt_t1, lt_t2);
}
log_dhcp6_client(client, "IA status %s", switch(option_code) {
dhcp6_message_status_to_string(status)); case SD_DHCP6_OPTION_IA_NA:
*ret = (DHCP6IA) {
.type = option_code,
.ia_na.id = iaid,
.ia_na.lifetime_t1 = htobe32(lt_t1),
.ia_na.lifetime_t2 = htobe32(lt_t2),
.addresses = TAKE_PTR(ia.addresses),
};
break;
case SD_DHCP6_OPTION_IA_TA:
*ret = (DHCP6IA) {
.type = option_code,
.ia_ta.id = iaid,
.addresses = TAKE_PTR(ia.addresses),
};
break;
case SD_DHCP6_OPTION_IA_PD:
*ret = (DHCP6IA) {
.type = option_code,
.ia_pd.id = iaid,
.ia_pd.lifetime_t1 = htobe32(lt_t1),
.ia_pd.lifetime_t2 = htobe32(lt_t2),
.addresses = TAKE_PTR(ia.addresses),
};
break;
default:
assert_not_reached();
}
return 0; return 0;
} }
break; int dhcp6_option_parse_addresses(
const uint8_t *optval,
size_t optlen,
struct in6_addr **addrs,
size_t *count) {
default: assert(optval);
log_dhcp6_client(client, "Unknown IA option %d", opt); assert(addrs);
break; assert(count);
}
i += sizeof(*option) + optlen;
}
switch(iatype) {
case SD_DHCP6_OPTION_IA_NA:
if (ia->ia_na.lifetime_t1 == 0 && ia->ia_na.lifetime_t2 == 0 && lt_min != UINT32_MAX) {
lt_t1 = lt_min / 2;
lt_t2 = lt_min / 10 * 8;
ia->ia_na.lifetime_t1 = htobe32(lt_t1);
ia->ia_na.lifetime_t2 = htobe32(lt_t2);
log_dhcp6_client(client, "Computed IA NA T1 %"PRIu32"sec and T2 %"PRIu32"sec as both were zero",
lt_t1, lt_t2);
}
break;
case SD_DHCP6_OPTION_IA_PD:
if (ia->ia_pd.lifetime_t1 == 0 && ia->ia_pd.lifetime_t2 == 0 && lt_min != UINT32_MAX) {
lt_t1 = lt_min / 2;
lt_t2 = lt_min / 10 * 8;
ia->ia_pd.lifetime_t1 = htobe32(lt_t1);
ia->ia_pd.lifetime_t2 = htobe32(lt_t2);
log_dhcp6_client(client, "Computed IA PD T1 %"PRIu32"sec and T2 %"PRIu32"sec as both were zero",
lt_t1, lt_t2);
}
break;
default:
break;
}
if (ret_status_code)
*ret_status_code = 0;
return 1;
}
int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
struct in6_addr **addrs, size_t count) {
if (optlen == 0 || optlen % sizeof(struct in6_addr) != 0) if (optlen == 0 || optlen % sizeof(struct in6_addr) != 0)
return -EINVAL; return -EBADMSG;
if (!GREEDY_REALLOC(*addrs, count * sizeof(struct in6_addr) + optlen)) if (!GREEDY_REALLOC(*addrs, *count + optlen / sizeof(struct in6_addr)))
return -ENOMEM; return -ENOMEM;
memcpy(*addrs + count, optval, optlen); memcpy(*addrs + *count, optval, optlen);
*count += optlen / sizeof(struct in6_addr);
count += optlen / sizeof(struct in6_addr); return 0;
return count;
} }
static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain) { static int parse_domain(const uint8_t **data, size_t *len, char **ret) {
_cleanup_free_ char *ret = NULL; _cleanup_free_ char *domain = NULL;
const uint8_t *optval = *data; const uint8_t *optval;
uint16_t optlen = *len; size_t optlen, n = 0;
bool first = true;
size_t n = 0;
int r; int r;
assert(data);
assert(*data);
assert(len);
assert(ret);
optval = *data;
optlen = *len;
if (optlen <= 1) if (optlen <= 1)
return -ENODATA; return -ENODATA;
@ -758,38 +784,40 @@ static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain)
optval += c; optval += c;
optlen -= c; optlen -= c;
if (!GREEDY_REALLOC(ret, n + !first + DNS_LABEL_ESCAPED_MAX)) if (!GREEDY_REALLOC(domain, n + (n != 0) + DNS_LABEL_ESCAPED_MAX))
return -ENOMEM; return -ENOMEM;
if (first) if (n != 0)
first = false; domain[n++] = '.';
else
ret[n++] = '.';
r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); r = dns_label_escape(label, c, domain + n, DNS_LABEL_ESCAPED_MAX);
if (r < 0) if (r < 0)
return r; return r;
n += r; n += r;
} }
if (n) { if (n > 0) {
if (!GREEDY_REALLOC(ret, n + 1)) if (!GREEDY_REALLOC(domain, n + 1))
return -ENOMEM; return -ENOMEM;
ret[n] = 0;
domain[n] = '\0';
} }
*out_domain = TAKE_PTR(ret); *ret = TAKE_PTR(domain);
*data = optval; *data = optval;
*len = optlen; *len = optlen;
return n; return n;
} }
int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char **str) { int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret) {
_cleanup_free_ char *domain = NULL; _cleanup_free_ char *domain = NULL;
int r; int r;
assert(optval);
assert(ret);
r = parse_domain(&optval, &optlen, &domain); r = parse_domain(&optval, &optlen, &domain);
if (r < 0) if (r < 0)
return r; return r;
@ -798,39 +826,38 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char *
if (optlen != 0) if (optlen != 0)
return -EINVAL; return -EINVAL;
*str = TAKE_PTR(domain); *ret = TAKE_PTR(domain);
return 0; return 0;
} }
int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen, char ***str_arr) { int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret) {
size_t idx = 0;
_cleanup_strv_free_ char **names = NULL; _cleanup_strv_free_ char **names = NULL;
int r; int r;
assert(optval);
assert(ret);
if (optlen <= 1) if (optlen <= 1)
return -ENODATA; return -ENODATA;
if (optval[optlen - 1] != '\0') if (optval[optlen - 1] != '\0')
return -EINVAL; return -EINVAL;
while (optlen > 0) { while (optlen > 0) {
_cleanup_free_ char *ret = NULL; _cleanup_free_ char *name = NULL;
r = parse_domain(&optval, &optlen, &ret); r = parse_domain(&optval, &optlen, &name);
if (r < 0) if (r < 0)
return r; return r;
if (r == 0) if (r == 0)
continue; continue;
r = strv_extend(&names, ret); r = strv_consume(&names, TAKE_PTR(name));
if (r < 0) if (r < 0)
return r; return r;
idx++;
} }
*str_arr = TAKE_PTR(names); *ret = TAKE_PTR(names);
return 0;
return idx;
} }
static sd_dhcp6_option* dhcp6_option_free(sd_dhcp6_option *i) { static sd_dhcp6_option* dhcp6_option_free(sd_dhcp6_option *i) {

View File

@ -1109,11 +1109,9 @@ static int client_parse_message(
size_t len, size_t len,
sd_dhcp6_lease *lease) { sd_dhcp6_lease *lease) {
uint16_t ia_na_status = 0, ia_pd_status = 0; uint32_t lt_t1 = UINT32_MAX, lt_t2 = UINT32_MAX;
uint32_t lt_t1 = ~0, lt_t2 = ~0;
usec_t irt = IRT_DEFAULT; usec_t irt = IRT_DEFAULT;
bool clientid = false; bool clientid = false;
size_t pos = 0;
int r; int r;
assert(client); assert(client);
@ -1122,22 +1120,14 @@ static int client_parse_message(
assert(lease); assert(lease);
len -= sizeof(DHCP6Message); len -= sizeof(DHCP6Message);
for (size_t offset = 0; offset < len;) {
uint16_t optcode;
size_t optlen;
const uint8_t *optval;
while (pos < len) { r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval);
DHCP6Option *option = (DHCP6Option *) &message->options[pos]; if (r < 0)
uint16_t optcode, optlen; return r;
int status;
uint8_t *optval;
if (len < pos + offsetof(DHCP6Option, data))
return -ENOBUFS;
optcode = be16toh(option->code);
optlen = be16toh(option->len);
optval = option->data;
if (len < pos + offsetof(DHCP6Option, data) + optlen)
return -ENOBUFS;
switch (optcode) { switch (optcode) {
case SD_DHCP6_OPTION_CLIENTID: case SD_DHCP6_OPTION_CLIENTID:
@ -1176,62 +1166,75 @@ static int client_parse_message(
break; break;
case SD_DHCP6_OPTION_STATUS_CODE: case SD_DHCP6_OPTION_STATUS_CODE: {
status = dhcp6_option_parse_status(option, optlen + sizeof(DHCP6Option)); _cleanup_free_ char *msg = NULL;
if (status < 0)
return status;
if (status > 0) r = dhcp6_option_parse_status(optval, optlen, &msg);
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s Status %s", if (r < 0)
return r;
if (r > 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"Received %s message with non-zero status: %s%s%s",
dhcp6_message_type_to_string(message->type), dhcp6_message_type_to_string(message->type),
dhcp6_message_status_to_string(status)); strempty(msg), isempty(msg) ? "" : ": ",
dhcp6_message_status_to_string(r));
break; break;
}
case SD_DHCP6_OPTION_IA_NA: {
_cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {};
case SD_DHCP6_OPTION_IA_NA:
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode."); log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode.");
break; break;
} }
r = dhcp6_option_parse_ia(client, option, client->ia_pd.ia_na.id, &lease->ia, &ia_na_status); r = dhcp6_option_parse_ia(client, client->ia_pd.ia_na.id, optcode, optlen, optval, &ia);
if (r < 0 && r != -ENOANO) if (r == -ENOMEM)
return r; return r;
if (r < 0)
continue;
if (ia_na_status == DHCP6_STATUS_NO_ADDRS_AVAIL) { if (lease->ia.addresses) {
pos += offsetof(DHCP6Option, data) + optlen; log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring.");
continue; continue;
} }
if (lease->ia.addresses) { lease->ia = ia;
ia = (DHCP6IA) {};
lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1)); lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t2)); lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t2));
}
break; break;
}
case SD_DHCP6_OPTION_IA_PD: {
_cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {};
case SD_DHCP6_OPTION_IA_PD:
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode."); log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode.");
break; break;
} }
r = dhcp6_option_parse_ia(client, option, client->ia_pd.ia_pd.id, &lease->pd, &ia_pd_status); r = dhcp6_option_parse_ia(client, client->ia_pd.ia_pd.id, optcode, optlen, optval, &ia);
if (r < 0 && r != -ENOANO) if (r == -ENOMEM)
return r; return r;
if (r < 0)
continue;
if (ia_pd_status == DHCP6_STATUS_NO_PREFIX_AVAIL) { if (lease->pd.addresses) {
pos += offsetof(DHCP6Option, data) + optlen; log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring.");
continue; continue;
} }
if (lease->pd.addresses) { lease->pd = ia;
ia = (DHCP6IA) {};
lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1)); lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2)); lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2));
}
break; break;
}
case SD_DHCP6_OPTION_RAPID_COMMIT: case SD_DHCP6_OPTION_RAPID_COMMIT:
r = dhcp6_lease_set_rapid_commit(lease); r = dhcp6_lease_set_rapid_commit(lease);
if (r < 0) if (r < 0)
@ -1240,28 +1243,28 @@ static int client_parse_message(
break; break;
case SD_DHCP6_OPTION_DNS_SERVERS: case SD_DHCP6_OPTION_DNS_SERVERS:
r = dhcp6_lease_set_dns(lease, optval, optlen); r = dhcp6_lease_add_dns(lease, optval, optlen);
if (r < 0) if (r < 0)
return r; return r;
break; break;
case SD_DHCP6_OPTION_DOMAIN_LIST: case SD_DHCP6_OPTION_DOMAIN_LIST:
r = dhcp6_lease_set_domains(lease, optval, optlen); r = dhcp6_lease_add_domains(lease, optval, optlen);
if (r < 0) if (r < 0)
return r; return r;
break; break;
case SD_DHCP6_OPTION_NTP_SERVER: case SD_DHCP6_OPTION_NTP_SERVER:
r = dhcp6_lease_set_ntp(lease, optval, optlen); r = dhcp6_lease_add_ntp(lease, optval, optlen);
if (r < 0) if (r < 0)
return r; return r;
break; break;
case SD_DHCP6_OPTION_SNTP_SERVERS: case SD_DHCP6_OPTION_SNTP_SERVERS:
r = dhcp6_lease_set_sntp(lease, optval, optlen); r = dhcp6_lease_add_sntp(lease, optval, optlen);
if (r < 0) if (r < 0)
return r; return r;
@ -1281,13 +1284,8 @@ static int client_parse_message(
irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC; irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC;
break; break;
} }
pos += offsetof(DHCP6Option, data) + optlen;
} }
if (ia_na_status > 0 && ia_pd_status > 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring.");
if (!clientid) if (!clientid)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s has incomplete options", return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s has incomplete options",
dhcp6_message_type_to_string(message->type)); dhcp6_message_type_to_string(message->type));
@ -1297,7 +1295,9 @@ static int client_parse_message(
if (r < 0) if (r < 0)
return log_dhcp6_client_errno(client, r, "%s has no server id", return log_dhcp6_client_errno(client, r, "%s has no server id",
dhcp6_message_type_to_string(message->type)); dhcp6_message_type_to_string(message->type));
}
if (!lease->ia.addresses && !lease->pd.addresses)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring.");
if (lease->ia.addresses) { if (lease->ia.addresses) {
lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1); lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
@ -1308,6 +1308,7 @@ static int client_parse_message(
lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1); lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1);
lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2); lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2);
} }
}
client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM); client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);

View File

@ -193,86 +193,69 @@ void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) {
lease->prefix_iter = lease->pd.addresses; lease->prefix_iter = lease->pd.addresses;
} }
int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
int r;
assert_return(lease, -EINVAL); assert_return(lease, -EINVAL);
assert_return(optval, -EINVAL); assert_return(optval, -EINVAL);
if (!optlen) if (optlen == 0)
return 0; return 0;
r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->dns, return dhcp6_option_parse_addresses(optval, optlen, &lease->dns, &lease->dns_count);
lease->dns_count);
if (r < 0)
return r;
lease->dns_count = r;
return 0;
} }
int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs) { int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
assert_return(lease, -EINVAL); assert_return(lease, -EINVAL);
assert_return(addrs, -EINVAL); assert_return(ret, -EINVAL);
if (lease->dns_count) { if (!lease->dns)
*addrs = lease->dns; return -ENOENT;
*ret = lease->dns;
return lease->dns_count; return lease->dns_count;
} }
return -ENOENT; int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
} _cleanup_strv_free_ char **domains = NULL;
int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval,
size_t optlen) {
int r; int r;
char **domains;
assert_return(lease, -EINVAL); assert_return(lease, -EINVAL);
assert_return(optval, -EINVAL); assert_return(optval, -EINVAL);
if (!optlen) if (optlen == 0)
return 0; return 0;
r = dhcp6_option_parse_domainname_list(optval, optlen, &domains); r = dhcp6_option_parse_domainname_list(optval, optlen, &domains);
if (r < 0) if (r < 0)
return 0;
strv_free_and_replace(lease->domains, domains);
lease->domains_count = r;
return r; return r;
return strv_extend_strv(&lease->domains, domains, true);
} }
int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains) { int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) {
assert_return(lease, -EINVAL); assert_return(lease, -EINVAL);
assert_return(domains, -EINVAL); assert_return(ret, -EINVAL);
if (lease->domains_count) {
*domains = lease->domains;
return lease->domains_count;
}
if (!lease->domains)
return -ENOENT; return -ENOENT;
*ret = lease->domains;
return strv_length(lease->domains);
} }
int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
int r; int r;
uint16_t subopt;
size_t sublen;
uint8_t *subval;
assert_return(lease, -EINVAL); assert_return(lease, -EINVAL);
assert_return(optval, -EINVAL); assert_return(optval, -EINVAL);
lease->ntp = mfree(lease->ntp); for (size_t offset = 0; offset < optlen;) {
lease->ntp_count = 0; const uint8_t *subval;
size_t sublen;
uint16_t subopt;
while ((r = dhcp6_option_parse(&optval, &optlen, &subopt, &sublen, r = dhcp6_option_parse(optval, optlen, &offset, &subopt, &sublen, &subval);
&subval)) >= 0) { if (r < 0)
int s; return r;
char **servers;
switch(subopt) { switch(subopt) {
case DHCP6_NTP_SUBOPTION_SRV_ADDR: case DHCP6_NTP_SUBOPTION_SRV_ADDR:
@ -280,86 +263,74 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
if (sublen != 16) if (sublen != 16)
return 0; return 0;
s = dhcp6_option_parse_ip6addrs(subval, sublen, r = dhcp6_option_parse_addresses(subval, sublen, &lease->ntp, &lease->ntp_count);
&lease->ntp,
lease->ntp_count);
if (s < 0)
return s;
lease->ntp_count = s;
break;
case DHCP6_NTP_SUBOPTION_SRV_FQDN:
r = dhcp6_option_parse_domainname_list(subval, sublen,
&servers);
if (r < 0) if (r < 0)
return 0;
strv_free_and_replace(lease->ntp_fqdn, servers);
lease->ntp_fqdn_count = r;
break;
}
}
if (r != -ENOMSG)
return r; return r;
break;
case DHCP6_NTP_SUBOPTION_SRV_FQDN: {
_cleanup_free_ char *server = NULL;
r = dhcp6_option_parse_domainname(subval, sublen, &server);
if (r < 0)
return r;
if (strv_contains(lease->ntp_fqdn, server))
continue;
r = strv_consume(&lease->ntp_fqdn, TAKE_PTR(server));
if (r < 0)
return r;
break;
}}
}
return 0; return 0;
} }
int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
int r;
assert_return(lease, -EINVAL); assert_return(lease, -EINVAL);
assert_return(optval, -EINVAL); assert_return(optval, -EINVAL);
if (!optlen) if (optlen == 0)
return 0; return 0;
if (lease->ntp || lease->ntp_fqdn) /* SNTP option is defined in RFC4075, and deprecated by RFC5908. */
return -EEXIST; return dhcp6_option_parse_addresses(optval, optlen, &lease->sntp, &lease->sntp_count);
/* Using deprecated SNTP information */
r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->ntp,
lease->ntp_count);
if (r < 0)
return r;
lease->ntp_count = r;
return 0;
} }
int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
const struct in6_addr **addrs) {
assert_return(lease, -EINVAL); assert_return(lease, -EINVAL);
assert_return(addrs, -EINVAL); assert_return(ret, -EINVAL);
if (lease->ntp_count) { if (lease->ntp) {
*addrs = lease->ntp; *ret = lease->ntp;
return lease->ntp_count; return lease->ntp_count;
} }
if (lease->sntp && !lease->ntp_fqdn) {
/* Fallback to the deprecated SNTP option. */
*ret = lease->sntp;
return lease->sntp_count;
}
return -ENOENT; return -ENOENT;
} }
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) { int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) {
assert_return(lease, -EINVAL); assert_return(lease, -EINVAL);
assert_return(ntp_fqdn, -EINVAL); assert_return(ret, -EINVAL);
if (lease->ntp_fqdn_count) {
*ntp_fqdn = lease->ntp_fqdn;
return lease->ntp_fqdn_count;
}
if (!lease->ntp_fqdn)
return -ENOENT; return -ENOENT;
*ret = lease->ntp_fqdn;
return strv_length(lease->ntp_fqdn);
} }
int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
size_t optlen) {
int r; int r;
char *fqdn; char *fqdn;
@ -378,33 +349,31 @@ int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval,
return free_and_replace(lease->fqdn, fqdn); return free_and_replace(lease->fqdn, fqdn);
} }
int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **fqdn) { int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) {
assert_return(lease, -EINVAL); assert_return(lease, -EINVAL);
assert_return(fqdn, -EINVAL); assert_return(ret, -EINVAL);
if (lease->fqdn) { if (!lease->fqdn)
*fqdn = lease->fqdn; return -ENOENT;
*ret = lease->fqdn;
return 0; return 0;
} }
return -ENOENT;
}
static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) { static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
assert(lease); if (!lease)
return NULL;
free(lease->serverid); free(lease->serverid);
dhcp6_lease_free_ia(&lease->ia); dhcp6_lease_free_ia(&lease->ia);
dhcp6_lease_free_ia(&lease->pd); dhcp6_lease_free_ia(&lease->pd);
free(lease->dns); free(lease->dns);
free(lease->fqdn); free(lease->fqdn);
strv_free(lease->domains);
lease->domains = strv_free(lease->domains);
free(lease->ntp); free(lease->ntp);
strv_free(lease->ntp_fqdn);
free(lease->sntp);
lease->ntp_fqdn = strv_free(lease->ntp_fqdn);
return mfree(lease); return mfree(lease);
} }

View File

@ -137,7 +137,7 @@ static int test_parse_domain(sd_event *e) {
data = (uint8_t []) { 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0, data = (uint8_t []) { 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0,
6, 'f', 'o', 'o', 'b', 'a', 'r', 0 }; 6, 'f', 'o', 'o', 'b', 'a', 'r', 0 };
r = dhcp6_option_parse_domainname_list(data, 21, &list); r = dhcp6_option_parse_domainname_list(data, 21, &list);
assert_se(r == 2); assert_se(r == 0);
assert_se(list); assert_se(list);
assert_se(streq(list[0], "example.com")); assert_se(streq(list[0], "example.com"));
assert_se(streq(list[1], "foobar")); assert_se(streq(list[1], "foobar"));
@ -156,7 +156,7 @@ static int test_parse_domain(sd_event *e) {
static int test_option(sd_event *e) { static int test_option(sd_event *e) {
uint8_t packet[] = { uint8_t packet[] = {
'F', 'O', 'O', 'F', 'O', 'O', 'H', 'O', 'G', 'E',
0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x07, 0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x07,
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
0x00, SD_DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09, 0x00, SD_DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09,
@ -164,53 +164,66 @@ static int test_option(sd_event *e) {
'B', 'A', 'R', 'B', 'A', 'R',
}; };
uint8_t result[] = { uint8_t result[] = {
'F', 'O', 'O', 'F', 'O', 'O', 'H', 'O', 'G', 'E',
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
'B', 'A', 'R', 'B', 'A', 'R',
}; };
_cleanup_free_ uint8_t *buf = NULL;
size_t offset, pos, optlen, outlen = sizeof(result);
const uint8_t *optval;
uint16_t optcode; uint16_t optcode;
size_t optlen; uint8_t *out;
uint8_t *optval, *buf, *out;
size_t zero = 0, pos = 3;
size_t buflen = sizeof(packet), outlen = sizeof(result);
log_debug("/* %s */", __func__); log_debug("/* %s */", __func__);
assert_se(buflen == outlen); assert_se(sizeof(packet) == sizeof(result));
assert_se(dhcp6_option_parse(&buf, &zero, &optcode, &optlen, offset = 0;
&optval) == -ENOMSG); assert_se(dhcp6_option_parse(packet, 0, &offset, &optcode, &optlen, &optval) == -EBADMSG);
buflen -= 3; offset = 3;
buf = &packet[3]; assert_se(dhcp6_option_parse(packet, 0, &offset, &optcode, &optlen, &optval) == -EBADMSG);
outlen -= 3;
out = &result[3]; /* Tests for reading unaligned data. */
assert_se(buf = new(uint8_t, sizeof(packet)));
for (size_t i = 0; i <= 7; i++) {
memcpy(buf, packet + i, sizeof(packet) - i);
offset = 7 - i;
assert_se(dhcp6_option_parse(buf, sizeof(packet), &offset, &optcode, &optlen, &optval) >= 0);
assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen,
&optval) >= 0);
pos += 4 + optlen;
assert_se(buf == &packet[pos]);
assert_se(optcode == SD_DHCP6_OPTION_ORO); assert_se(optcode == SD_DHCP6_OPTION_ORO);
assert_se(optlen == 7); assert_se(optlen == 7);
assert_se(buflen + pos == sizeof(packet)); assert_se(optval == buf + 11 - i);
}
assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen, offset = 7;
optval) >= 0); assert_se(dhcp6_option_parse(packet, sizeof(packet), &offset, &optcode, &optlen, &optval) >= 0);
assert_se(optcode == SD_DHCP6_OPTION_ORO);
assert_se(optlen == 7);
assert_se(optval == packet + 11);
pos = 7;
outlen -= 7;
out = &result[pos];
assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen, optval) >= 0);
pos += 4 + optlen;
assert_se(out == &result[pos]); assert_se(out == &result[pos]);
assert_se(*out == 0x00); assert_se(*out == 0x00);
assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen, assert_se(dhcp6_option_parse(packet, sizeof(packet), &offset, &optcode, &optlen, &optval) >= 0);
&optval) >= 0);
pos += 4 + optlen;
assert_se(buf == &packet[pos]);
assert_se(optcode == SD_DHCP6_OPTION_VENDOR_CLASS); assert_se(optcode == SD_DHCP6_OPTION_VENDOR_CLASS);
assert_se(optlen == 9); assert_se(optlen == 9);
assert_se(buflen + pos == sizeof(packet)); assert_se(optval == packet + 22);
assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen, assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen, optval) >= 0);
optval) >= 0);
pos += 4 + optlen;
assert_se(out == &result[pos]); assert_se(out == &result[pos]);
assert_se(*out == 'B'); assert_se(*out == 'B');
@ -236,7 +249,7 @@ static int test_option_status(sd_event *e) {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
0x01, 0x02, 0x03, 0x04, 0x0a, 0x0b, 0x0c, 0x0d, 0x01, 0x02, 0x03, 0x04, 0x0a, 0x0b, 0x0c, 0x0d,
/* status option */ /* IA address status option */
0x00, 0x0d, 0x00, 0x02, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x02, 0x00, 0x01,
}; };
static const uint8_t option3[] = { static const uint8_t option3[] = {
@ -248,7 +261,7 @@ static int test_option_status(sd_event *e) {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
0x01, 0x02, 0x03, 0x04, 0x0a, 0x0b, 0x0c, 0x0d, 0x01, 0x02, 0x03, 0x04, 0x0a, 0x0b, 0x0c, 0x0d,
/* status option */ /* IA address status option */
0x00, 0x0d, 0x00, 0x08, 0x00, 0x00, 'f', 'o', 0x00, 0x0d, 0x00, 0x08, 0x00, 0x00, 'f', 'o',
'o', 'b', 'a', 'r', 'o', 'b', 'a', 'r',
}; };
@ -262,7 +275,7 @@ static int test_option_status(sd_event *e) {
0x80, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0x80, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe,
0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
/* status option */ /* PD prefix status option */
0x00, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x02, 0x00, 0x00,
}; };
static const uint8_t option5[] = { static const uint8_t option5[] = {
@ -275,7 +288,7 @@ static int test_option_status(sd_event *e) {
0x80, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0x80, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe,
0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
/* status option */ /* PD prefix status option */
0x00, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x02, 0x00, 0x00,
/* IA PD Prefix #2 */ /* IA PD Prefix #2 */
0x00, 0x1a, 0x00, 0x1f, 0x00, 0x1a, 0x00, 0x1f,
@ -283,6 +296,7 @@ static int test_option_status(sd_event *e) {
0x80, 0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x0l, 0xd0, 0x80, 0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x0l, 0xd0,
0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
/* PD prefix status option */
0x00, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x02, 0x00, 0x00,
}; };
DHCP6Option *option; DHCP6Option *option;
@ -298,47 +312,47 @@ static int test_option_status(sd_event *e) {
option = (DHCP6Option*) option1; option = (DHCP6Option*) option1;
assert_se(sizeof(option1) == sizeof(DHCP6Option) + be16toh(option->len)); assert_se(sizeof(option1) == sizeof(DHCP6Option) + be16toh(option->len));
r = dhcp6_option_parse_ia(NULL, option, 0, &ia, NULL); r = dhcp6_option_parse_ia(NULL, 0, be16toh(option->code), be16toh(option->len), option->data, &ia);
assert_se(r == -ENOANO); assert_se(r == -ENOANO);
r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, NULL); r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia);
assert_se(r == 0); assert_se(r == -EINVAL);
assert_se(ia.addresses == NULL); assert_se(!ia.addresses);
option->len = htobe16(17); option->len = htobe16(17);
r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, NULL); r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia);
assert_se(r == -ENOBUFS); assert_se(r == -EBADMSG);
assert_se(ia.addresses == NULL); assert_se(!ia.addresses);
option->len = htobe16(sizeof(DHCP6Option)); option->len = htobe16(sizeof(DHCP6Option));
r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, NULL); r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia);
assert_se(r == -ENOBUFS); assert_se(r == -EBADMSG);
assert_se(ia.addresses == NULL); assert_se(!ia.addresses);
zero(ia); zero(ia);
option = (DHCP6Option*) option2; option = (DHCP6Option*) option2;
assert_se(sizeof(option2) == sizeof(DHCP6Option) + be16toh(option->len)); assert_se(sizeof(option2) == sizeof(DHCP6Option) + be16toh(option->len));
r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, NULL); r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia);
assert_se(r >= 0); assert_se(r == -ENODATA);
assert_se(ia.addresses == NULL); assert_se(!ia.addresses);
zero(ia); zero(ia);
option = (DHCP6Option*) option3; option = (DHCP6Option*) option3;
assert_se(sizeof(option3) == sizeof(DHCP6Option) + be16toh(option->len)); assert_se(sizeof(option3) == sizeof(DHCP6Option) + be16toh(option->len));
r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, NULL); r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia);
assert_se(r >= 0); assert_se(r >= 0);
assert_se(ia.addresses != NULL); assert_se(ia.addresses);
dhcp6_lease_free_ia(&ia); dhcp6_lease_free_ia(&ia);
zero(pd); zero(pd);
option = (DHCP6Option*) option4; option = (DHCP6Option*) option4;
assert_se(sizeof(option4) == sizeof(DHCP6Option) + be16toh(option->len)); assert_se(sizeof(option4) == sizeof(DHCP6Option) + be16toh(option->len));
r = dhcp6_option_parse_ia(NULL, option, iaid, &pd, NULL); r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &pd);
assert_se(r >= 0); assert_se(r >= 0);
assert_se(pd.addresses != NULL); assert_se(pd.addresses);
assert_se(memcmp(&pd.ia_pd.id, &option4[4], 4) == 0); assert_se(memcmp(&pd.ia_pd.id, &option4[4], 4) == 0);
assert_se(memcmp(&pd.ia_pd.lifetime_t1, &option4[8], 4) == 0); assert_se(memcmp(&pd.ia_pd.lifetime_t1, &option4[8], 4) == 0);
assert_se(memcmp(&pd.ia_pd.lifetime_t2, &option4[12], 4) == 0); assert_se(memcmp(&pd.ia_pd.lifetime_t2, &option4[12], 4) == 0);
@ -348,9 +362,9 @@ static int test_option_status(sd_event *e) {
option = (DHCP6Option*) option5; option = (DHCP6Option*) option5;
assert_se(sizeof(option5) == sizeof(DHCP6Option) + be16toh(option->len)); assert_se(sizeof(option5) == sizeof(DHCP6Option) + be16toh(option->len));
r = dhcp6_option_parse_ia(NULL, option, iaid, &pd, NULL); r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &pd);
assert_se(r >= 0); assert_se(r >= 0);
assert_se(pd.addresses != NULL); assert_se(pd.addresses);
dhcp6_lease_free_ia(&pd); dhcp6_lease_free_ia(&pd);
return 0; return 0;
@ -468,7 +482,7 @@ static int test_advertise_option(sd_event *e) {
val = htobe32(120); val = htobe32(120);
assert_se(!memcmp(optval + 8, &val, sizeof(val))); assert_se(!memcmp(optval + 8, &val, sizeof(val)));
assert_se(dhcp6_option_parse_ia(NULL, option, iaid, &lease->ia, NULL) >= 0); assert_se(dhcp6_option_parse_ia(NULL, iaid, optcode, optlen, optval, &lease->ia) >= 0);
break; break;
} }
@ -496,20 +510,17 @@ static int test_advertise_option(sd_event *e) {
case SD_DHCP6_OPTION_DNS_SERVERS: case SD_DHCP6_OPTION_DNS_SERVERS:
assert_se(optlen == 16); assert_se(optlen == 16);
assert_se(dhcp6_lease_set_dns(lease, optval, assert_se(dhcp6_lease_add_dns(lease, optval, optlen) >= 0);
optlen) >= 0);
break; break;
case SD_DHCP6_OPTION_DOMAIN_LIST: case SD_DHCP6_OPTION_DOMAIN_LIST:
assert_se(optlen == 11); assert_se(optlen == 11);
assert_se(dhcp6_lease_set_domains(lease, optval, assert_se(dhcp6_lease_add_domains(lease, optval, optlen) >= 0);
optlen) >= 0);
break; break;
case SD_DHCP6_OPTION_SNTP_SERVERS: case SD_DHCP6_OPTION_SNTP_SERVERS:
assert_se(optlen == 16); assert_se(optlen == 16);
assert_se(dhcp6_lease_set_sntp(lease, optval, assert_se(dhcp6_lease_add_sntp(lease, optval, optlen) >= 0);
optlen) >= 0);
break; break;
default: default:
@ -665,7 +676,7 @@ static int test_client_verify_request(DHCP6Message *request, size_t len) {
assert_se(!memcmp(optval + 8, &val, sizeof(val))); assert_se(!memcmp(optval + 8, &val, sizeof(val)));
/* Then, this should refuse all addresses. */ /* Then, this should refuse all addresses. */
assert_se(dhcp6_option_parse_ia(NULL, option, test_iaid, &lease->ia, NULL) >= 0); assert_se(dhcp6_option_parse_ia(NULL, test_iaid, optcode, optlen, optval, &lease->ia) == -ENODATA);
break; break;

View File

@ -53,6 +53,42 @@ void genl_clear_family(sd_netlink *nl) {
nl->genl_family_by_id = hashmap_free_with_destructor(nl->genl_family_by_id, genl_family_free); nl->genl_family_by_id = hashmap_free_with_destructor(nl->genl_family_by_id, genl_family_free);
} }
static int genl_family_new_unsupported(
sd_netlink *nl,
const char *family_name,
const NLTypeSystem *type_system) {
_cleanup_(genl_family_freep) GenericNetlinkFamily *f = NULL;
int r;
assert(nl);
assert(family_name);
assert(type_system);
/* Kernel does not support the genl family? To prevent from resolving the family name again,
* let's store the family with zero id to indicate that. */
f = new(GenericNetlinkFamily, 1);
if (!f)
return -ENOMEM;
*f = (GenericNetlinkFamily) {
.type_system = type_system,
};
f->name = strdup(family_name);
if (!f->name)
return -ENOMEM;
r = hashmap_ensure_put(&nl->genl_family_by_name, &string_hash_ops, f->name, f);
if (r < 0)
return r;
f->genl = nl;
TAKE_PTR(f);
return 0;
}
static int genl_family_new( static int genl_family_new(
sd_netlink *nl, sd_netlink *nl,
const char *expected_family_name, const char *expected_family_name,
@ -79,28 +115,6 @@ static int genl_family_new(
.type_system = type_system, .type_system = type_system,
}; };
if (sd_netlink_message_is_error(message)) {
int e;
/* Kernel does not support the genl family? To prevent from resolving the family name
* again, let's store the family with zero id to indicate that. */
e = sd_netlink_message_get_errno(message);
if (e >= 0) /* Huh? */
e = -EOPNOTSUPP;
f->name = strdup(expected_family_name);
if (!f->name)
return e;
if (hashmap_ensure_put(&nl->genl_family_by_name, &string_hash_ops, f->name, f) < 0)
return e;
f->genl = nl;
TAKE_PTR(f);
return e;
}
r = sd_genl_message_get_family_name(nl, message, &family_name); r = sd_genl_message_get_family_name(nl, message, &family_name);
if (r < 0) if (r < 0)
return r; return r;
@ -261,9 +275,10 @@ static int genl_family_get_by_name_internal(
if (r < 0) if (r < 0)
return r; return r;
r = sd_netlink_call(nl, req, 0, &reply); if (sd_netlink_call(nl, req, 0, &reply) < 0) {
if (r < 0) (void) genl_family_new_unsupported(nl, name, type_system);
return r; return -EOPNOTSUPP;
}
return genl_family_new(nl, name, type_system, reply, ret); return genl_family_new(nl, name, type_system, reply, ret);
} }

View File

@ -123,8 +123,8 @@ struct sd_netlink_message {
struct nlmsghdr *hdr; struct nlmsghdr *hdr;
struct netlink_container containers[NETLINK_CONTAINER_DEPTH]; struct netlink_container containers[NETLINK_CONTAINER_DEPTH];
unsigned n_containers; /* number of containers */ unsigned n_containers; /* number of containers */
uint32_t multicast_group;
bool sealed:1; bool sealed:1;
bool broadcast:1;
sd_netlink_message *next; /* next in a chain of multi-part messages */ sd_netlink_message *next; /* next in a chain of multi-part messages */
}; };

View File

@ -116,8 +116,8 @@ int message_new_synthetic_error(sd_netlink *nl, int error, uint32_t serial, sd_n
int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) { int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) {
assert_return(m, -EINVAL); assert_return(m, -EINVAL);
assert_return(m->hdr, -EINVAL); assert_return(m->hdr, -EINVAL);
assert_return(m->protocol != NETLINK_ROUTE ||
assert_return(IN_SET(m->hdr->nlmsg_type, IN_SET(m->hdr->nlmsg_type,
RTM_GETLINK, RTM_GETLINKPROP, RTM_GETADDR, RTM_GETROUTE, RTM_GETNEIGH, RTM_GETLINK, RTM_GETLINKPROP, RTM_GETADDR, RTM_GETROUTE, RTM_GETNEIGH,
RTM_GETRULE, RTM_GETADDRLABEL, RTM_GETNEXTHOP), -EINVAL); RTM_GETRULE, RTM_GETADDRLABEL, RTM_GETNEXTHOP), -EINVAL);
@ -166,7 +166,7 @@ int sd_netlink_message_set_flags(sd_netlink_message *m, uint16_t flags) {
int sd_netlink_message_is_broadcast(sd_netlink_message *m) { int sd_netlink_message_is_broadcast(sd_netlink_message *m) {
assert_return(m, -EINVAL); assert_return(m, -EINVAL);
return m->broadcast; return m->multicast_group != 0;
} }
/* If successful the updated message will be correctly aligned, if /* If successful the updated message will be correctly aligned, if
@ -751,7 +751,7 @@ int sd_netlink_message_read(sd_netlink_message *m, unsigned short type, size_t s
} }
int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data) { int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data) {
void *attr_data, *data; void *attr_data;
int r; int r;
assert_return(m, -EINVAL); assert_return(m, -EINVAL);
@ -761,6 +761,8 @@ int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, siz
return r; return r;
if (ret_data) { if (ret_data) {
void *data;
data = memdup(attr_data, r); data = memdup(attr_data, r);
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
@ -774,9 +776,34 @@ int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, siz
return r; return r;
} }
int sd_netlink_message_read_data_suffix0(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data) {
void *attr_data;
int r;
assert_return(m, -EINVAL);
r = netlink_message_read_internal(m, type, &attr_data, NULL);
if (r < 0)
return r;
if (ret_data) {
void *data;
data = memdup_suffix0(attr_data, r);
if (!data)
return -ENOMEM;
*ret_data = data;
}
if (ret_size)
*ret_size = r;
return r;
}
int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsigned short type, char **data) { int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsigned short type, char **data) {
void *attr_data; void *attr_data;
char *str;
int r; int r;
assert_return(m, -EINVAL); assert_return(m, -EINVAL);
@ -790,6 +817,8 @@ int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsigned short
return r; return r;
if (data) { if (data) {
char *str;
str = strndup(attr_data, r); str = strndup(attr_data, r);
if (!str) if (!str)
return -ENOMEM; return -ENOMEM;
@ -801,8 +830,8 @@ int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsigned short
} }
int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data) { int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data) {
int r;
void *attr_data; void *attr_data;
int r;
assert_return(m, -EINVAL); assert_return(m, -EINVAL);
@ -813,7 +842,8 @@ int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, c
r = netlink_message_read_internal(m, type, &attr_data, NULL); r = netlink_message_read_internal(m, type, &attr_data, NULL);
if (r < 0) if (r < 0)
return r; return r;
else if (strnlen(attr_data, r) >= (size_t) r)
if (strnlen(attr_data, r) >= (size_t) r)
return -EIO; return -EIO;
if (data) if (data)
@ -823,8 +853,8 @@ int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, c
} }
int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8_t *data) { int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8_t *data) {
int r;
void *attr_data; void *attr_data;
int r;
assert_return(m, -EINVAL); assert_return(m, -EINVAL);
@ -835,7 +865,8 @@ int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8
r = netlink_message_read_internal(m, type, &attr_data, NULL); r = netlink_message_read_internal(m, type, &attr_data, NULL);
if (r < 0) if (r < 0)
return r; return r;
else if ((size_t) r < sizeof(uint8_t))
if ((size_t) r < sizeof(uint8_t))
return -EIO; return -EIO;
if (data) if (data)
@ -858,7 +889,8 @@ int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint
r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder); r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder);
if (r < 0) if (r < 0)
return r; return r;
else if ((size_t) r < sizeof(uint16_t))
if ((size_t) r < sizeof(uint16_t))
return -EIO; return -EIO;
if (data) { if (data) {
@ -885,7 +917,8 @@ int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint
r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder); r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder);
if (r < 0) if (r < 0)
return r; return r;
else if ((size_t) r < sizeof(uint32_t))
if ((size_t) r < sizeof(uint32_t))
return -EIO; return -EIO;
if (data) { if (data) {
@ -899,8 +932,8 @@ int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint
} }
int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short type, struct ether_addr *data) { int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short type, struct ether_addr *data) {
int r;
void *attr_data; void *attr_data;
int r;
assert_return(m, -EINVAL); assert_return(m, -EINVAL);
@ -911,7 +944,8 @@ int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short typ
r = netlink_message_read_internal(m, type, &attr_data, NULL); r = netlink_message_read_internal(m, type, &attr_data, NULL);
if (r < 0) if (r < 0)
return r; return r;
else if ((size_t) r < sizeof(struct ether_addr))
if ((size_t) r < sizeof(struct ether_addr))
return -EIO; return -EIO;
if (data) if (data)
@ -921,8 +955,8 @@ int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short typ
} }
int netlink_message_read_hw_addr(sd_netlink_message *m, unsigned short type, struct hw_addr_data *data) { int netlink_message_read_hw_addr(sd_netlink_message *m, unsigned short type, struct hw_addr_data *data) {
int r;
void *attr_data; void *attr_data;
int r;
assert_return(m, -EINVAL); assert_return(m, -EINVAL);
@ -933,7 +967,8 @@ int netlink_message_read_hw_addr(sd_netlink_message *m, unsigned short type, str
r = netlink_message_read_internal(m, type, &attr_data, NULL); r = netlink_message_read_internal(m, type, &attr_data, NULL);
if (r < 0) if (r < 0)
return r; return r;
else if (r > HW_ADDR_MAX_SIZE)
if (r > HW_ADDR_MAX_SIZE)
return -EIO; return -EIO;
if (data) { if (data) {
@ -945,8 +980,8 @@ int netlink_message_read_hw_addr(sd_netlink_message *m, unsigned short type, str
} }
int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short type, struct ifa_cacheinfo *info) { int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short type, struct ifa_cacheinfo *info) {
int r;
void *attr_data; void *attr_data;
int r;
assert_return(m, -EINVAL); assert_return(m, -EINVAL);
@ -957,7 +992,8 @@ int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short typ
r = netlink_message_read_internal(m, type, &attr_data, NULL); r = netlink_message_read_internal(m, type, &attr_data, NULL);
if (r < 0) if (r < 0)
return r; return r;
else if ((size_t) r < sizeof(struct ifa_cacheinfo))
if ((size_t) r < sizeof(struct ifa_cacheinfo))
return -EIO; return -EIO;
if (info) if (info)
@ -980,7 +1016,8 @@ int netlink_message_read_in_addr_union(sd_netlink_message *m, unsigned short typ
r = netlink_message_read_internal(m, type, &attr_data, NULL); r = netlink_message_read_internal(m, type, &attr_data, NULL);
if (r < 0) if (r < 0)
return r; return r;
else if ((size_t) r < FAMILY_ADDRESS_SIZE(family))
if ((size_t) r < FAMILY_ADDRESS_SIZE(family))
return -EIO; return -EIO;
if (data) if (data)

View File

@ -81,13 +81,11 @@ int socket_bind(sd_netlink *nl) {
addrlen = sizeof(nl->sockaddr); addrlen = sizeof(nl->sockaddr);
r = bind(nl->fd, &nl->sockaddr.sa, addrlen);
/* ignore EINVAL to allow binding an already bound socket */ /* ignore EINVAL to allow binding an already bound socket */
if (r < 0 && errno != EINVAL) if (bind(nl->fd, &nl->sockaddr.sa, addrlen) < 0 && errno != EINVAL)
return -errno; return -errno;
r = getsockname(nl->fd, &nl->sockaddr.sa, &addrlen); if (getsockname(nl->fd, &nl->sockaddr.sa, &addrlen) < 0)
if (r < 0)
return -errno; return -errno;
return broadcast_groups_get(nl); return broadcast_groups_get(nl);
@ -328,7 +326,7 @@ int socket_read_message(sd_netlink *nl) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
size_t size; size_t size;
if (!group && new_msg->nlmsg_pid != nl->sockaddr.nl.nl_pid) if (group == 0 && new_msg->nlmsg_pid != nl->sockaddr.nl.nl_pid)
/* not broadcast and not for us */ /* not broadcast and not for us */
continue; continue;
@ -365,8 +363,7 @@ int socket_read_message(sd_netlink *nl) {
if (r < 0) if (r < 0)
return r; return r;
m->broadcast = !!group; m->multicast_group = group;
m->hdr = memdup(new_msg, new_msg->nlmsg_len); m->hdr = memdup(new_msg, new_msg->nlmsg_len);
if (!m->hdr) if (!m->hdr)
return -ENOMEM; return -ENOMEM;

View File

@ -11,6 +11,7 @@
#include <linux/nl80211.h> #include <linux/nl80211.h>
#include <linux/wireguard.h> #include <linux/wireguard.h>
#include "missing_network.h"
#include "netlink-genl.h" #include "netlink-genl.h"
#include "netlink-types-internal.h" #include "netlink-types-internal.h"
@ -179,10 +180,15 @@ static const NLType genl_macsec_types[] = {
/***************** genl nl80211 type systems *****************/ /***************** genl nl80211 type systems *****************/
static const NLType genl_nl80211_types[] = { static const NLType genl_nl80211_types[] = {
[NL80211_ATTR_WIPHY] = { .type = NETLINK_TYPE_U32 },
[NL80211_ATTR_WIPHY_NAME] = { .type = NETLINK_TYPE_STRING },
[NL80211_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 }, [NL80211_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 },
[NL80211_ATTR_MAC] = { .type = NETLINK_TYPE_ETHER_ADDR }, [NL80211_ATTR_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ-1 },
[NL80211_ATTR_SSID] = { .type = NETLINK_TYPE_STRING },
[NL80211_ATTR_IFTYPE] = { .type = NETLINK_TYPE_U32 }, [NL80211_ATTR_IFTYPE] = { .type = NETLINK_TYPE_U32 },
[NL80211_ATTR_MAC] = { .type = NETLINK_TYPE_ETHER_ADDR, .size = ETH_ALEN },
[NL80211_ATTR_SSID] = { .type = NETLINK_TYPE_BINARY, .size = IEEE80211_MAX_SSID_LEN },
[NL80211_ATTR_STATUS_CODE] = { .type = NETLINK_TYPE_U16 },
[NL80211_ATTR_4ADDR] = { .type = NETLINK_TYPE_U8 },
}; };
/***************** genl wireguard type systems *****************/ /***************** genl wireguard type systems *****************/

View File

@ -116,6 +116,16 @@ int rtnl_log_create_error(int r);
userdata, description); \ userdata, description); \
}) })
#define genl_add_match(nl, ret_slot, family, group, cmd, callback, destroy_callback, userdata, description) \
({ \
int (*_callback_)(sd_netlink *, sd_netlink_message *, typeof(userdata)) = callback; \
void (*_destroy_)(typeof(userdata)) = destroy_callback; \
sd_genl_add_match(nl, ret_slot, family, group, cmd, \
(sd_netlink_message_handler_t) _callback_, \
(sd_netlink_destroy_t) _destroy_, \
userdata, description); \
})
int netlink_message_append_hw_addr(sd_netlink_message *m, unsigned short type, const struct hw_addr_data *data); int netlink_message_append_hw_addr(sd_netlink_message *m, unsigned short type, const struct hw_addr_data *data);
int netlink_message_append_in_addr_union(sd_netlink_message *m, unsigned short type, int family, const union in_addr_union *data); int netlink_message_append_in_addr_union(sd_netlink_message *m, unsigned short type, int family, const union in_addr_union *data);
int netlink_message_append_sockaddr_union(sd_netlink_message *m, unsigned short type, const union sockaddr_union *data); int netlink_message_append_sockaddr_union(sd_netlink_message *m, unsigned short type, const union sockaddr_union *data);

View File

@ -81,8 +81,7 @@ int sd_netlink_new_from_fd(sd_netlink **ret, int fd) {
addrlen = sizeof(nl->sockaddr); addrlen = sizeof(nl->sockaddr);
r = getsockname(fd, &nl->sockaddr.sa, &addrlen); if (getsockname(fd, &nl->sockaddr.sa, &addrlen) < 0)
if (r < 0)
return -errno; return -errno;
if (nl->sockaddr.nl.nl_family != AF_NETLINK) if (nl->sockaddr.nl.nl_family != AF_NETLINK)
@ -446,12 +445,22 @@ static int process_match(sd_netlink *nl, sd_netlink_message *m) {
LIST_FOREACH(match_callbacks, c, nl->match_callbacks) { LIST_FOREACH(match_callbacks, c, nl->match_callbacks) {
sd_netlink_slot *slot; sd_netlink_slot *slot;
bool found = false;
if (c->type != type) if (c->type != type)
continue; continue;
if (c->cmd != 0 && c->cmd != cmd) if (c->cmd != 0 && c->cmd != cmd)
continue; continue;
for (size_t i = 0; i < c->n_groups; i++)
if (c->groups[i] == m->multicast_group) {
found = true;
break;
}
if (!found)
continue;
slot = container_of(c, sd_netlink_slot, match_callback); slot = container_of(c, sd_netlink_slot, match_callback);
r = c->callback(nl, m, slot->userdata); r = c->callback(nl, m, slot->userdata);
@ -483,15 +492,12 @@ static int process_running(sd_netlink *nl, sd_netlink_message **ret) {
if (!m) if (!m)
goto null_message; goto null_message;
if (sd_netlink_message_is_broadcast(m)) { if (sd_netlink_message_is_broadcast(m))
r = process_match(nl, m); r = process_match(nl, m);
if (r != 0) else
goto null_message;
} else {
r = process_reply(nl, m); r = process_reply(nl, m);
if (r != 0) if (r != 0)
goto null_message; goto null_message;
}
if (ret) { if (ret) {
*ret = TAKE_PTR(m); *ret = TAKE_PTR(m);

View File

@ -19,7 +19,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
assert_se(fwrite(data, size, 1, f) == 1); assert_se(fwrite(data, size, 1, f) == 1);
fflush(f); fflush(f);
assert_se(manager_new(&manager) >= 0); assert_se(manager_new(&manager, /* test_mode = */ true) >= 0);
(void) netdev_load_one(manager, netdev_config); (void) netdev_load_one(manager, netdev_config);
return 0; return 0;
} }

View File

@ -22,7 +22,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
assert_se(fwrite(data, size, 1, f) == 1); assert_se(fwrite(data, size, 1, f) == 1);
fflush(f); fflush(f);
assert_se(manager_new(&manager) >= 0); assert_se(manager_new(&manager, /* test_mode = */ true) >= 0);
(void) network_load_one(manager, &manager->networks, network_config); (void) network_load_one(manager, &manager->networks, network_config);
return 0; return 0;
} }

View File

@ -285,10 +285,6 @@ static int l2tp_acquire_local_address(L2tpTunnel *t, Link *link, union in_addr_u
if (l2tp_acquire_local_address_one(t, a, ret) >= 0) if (l2tp_acquire_local_address_one(t, a, ret) >= 0)
return 1; return 1;
SET_FOREACH(a, link->addresses_foreign)
if (l2tp_acquire_local_address_one(t, a, ret) >= 0)
return 1;
return -ENODATA; return -ENODATA;
} }

View File

@ -106,24 +106,6 @@ static bool address_pool_prefix_is_taken(
if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen)) if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen))
return true; return true;
} }
/* Don't clash with assigned foreign addresses */
SET_FOREACH(a, l->addresses_foreign) {
if (a->family != p->family)
continue;
if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen))
return true;
}
/* Don't clash with addresses already pulled from the pool, but not assigned yet */
SET_FOREACH(a, l->pool_addresses) {
if (a->family != p->family)
continue;
if (in_addr_prefix_intersect(p->family, u, prefixlen, &a->in_addr, a->prefixlen))
return true;
}
} }
/* And don't clash with configured but un-assigned addresses either */ /* And don't clash with configured but un-assigned addresses either */

View File

@ -9,10 +9,12 @@
#include "netlink-util.h" #include "netlink-util.h"
#include "networkd-address-pool.h" #include "networkd-address-pool.h"
#include "networkd-address.h" #include "networkd-address.h"
#include "networkd-dhcp-server.h"
#include "networkd-ipv4acd.h" #include "networkd-ipv4acd.h"
#include "networkd-manager.h" #include "networkd-manager.h"
#include "networkd-network.h" #include "networkd-network.h"
#include "networkd-queue.h" #include "networkd-queue.h"
#include "networkd-route.h"
#include "parse-util.h" #include "parse-util.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
@ -131,7 +133,7 @@ static int address_new_static(Network *network, const char *filename, unsigned s
address->network = network; address->network = network;
address->section = TAKE_PTR(n); address->section = TAKE_PTR(n);
address->is_static = true; address->source = NETWORK_CONFIG_SOURCE_STATIC;
r = ordered_hashmap_ensure_put(&network->addresses_by_section, &network_config_hash_ops, address->section, address); r = ordered_hashmap_ensure_put(&network->addresses_by_section, &network_config_hash_ops, address->section, address);
if (r < 0) if (r < 0)
@ -151,23 +153,7 @@ Address *address_free(Address *address) {
} }
if (address->link) { if (address->link) {
NDiscAddress *n;
set_remove(address->link->addresses, address); set_remove(address->link->addresses, address);
set_remove(address->link->addresses_foreign, address);
set_remove(address->link->addresses_ipv4acd, address);
set_remove(address->link->static_addresses, address);
if (address->link->dhcp_address == address)
address->link->dhcp_address = NULL;
if (address->link->dhcp_address_old == address)
address->link->dhcp_address_old = NULL;
set_remove(address->link->dhcp6_addresses, address);
set_remove(address->link->dhcp6_addresses_old, address);
set_remove(address->link->dhcp6_pd_addresses, address);
set_remove(address->link->dhcp6_pd_addresses_old, address);
SET_FOREACH(n, address->link->ndisc_addresses)
if (address_equal(n->address, address))
free(set_remove(address->link->ndisc_addresses, n));
if (address->family == AF_INET6 && if (address->family == AF_INET6 &&
in6_addr_equal(&address->in_addr.in6, &address->link->ipv6ll_address)) in6_addr_equal(&address->in_addr.in6, &address->link->ipv6ll_address))
@ -181,6 +167,41 @@ Address *address_free(Address *address) {
return mfree(address); return mfree(address);
} }
bool address_is_ready(const Address *a) {
assert(a);
if (FLAGS_SET(a->flags, IFA_F_TENTATIVE))
return false;
if (FLAGS_SET(a->state, NETWORK_CONFIG_STATE_REMOVING))
return false;
if (FLAGS_SET(a->state, NETWORK_CONFIG_STATE_PROBING))
return false;
if (!FLAGS_SET(a->state, NETWORK_CONFIG_STATE_CONFIGURED))
return false;
return true;
}
void link_mark_addresses(Link *link, NetworkConfigSource source, const struct in6_addr *router) {
Address *a;
assert(link);
SET_FOREACH(a, link->addresses) {
if (a->source != source)
continue;
if (source == NETWORK_CONFIG_SOURCE_NDISC &&
router && !in6_addr_equal(router, &a->provider.in6))
continue;
address_mark(a);
}
}
static bool address_may_have_broadcast(const Address *a) { static bool address_may_have_broadcast(const Address *a) {
assert(a); assert(a);
@ -295,16 +316,6 @@ int address_compare_func(const Address *a1, const Address *a2) {
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(address_hash_ops, Address, address_hash_func, address_compare_func, address_free); DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(address_hash_ops, Address, address_hash_func, address_compare_func, address_free);
bool address_equal(const Address *a1, const Address *a2) {
if (a1 == a2)
return true;
if (!a1 || !a2)
return false;
return address_compare_func(a1, a2) == 0;
}
int address_dup(const Address *src, Address **ret) { int address_dup(const Address *src, Address **ret) {
_cleanup_(address_freep) Address *dest = NULL; _cleanup_(address_freep) Address *dest = NULL;
int r; int r;
@ -371,67 +382,19 @@ static int address_set_masquerade(Address *address, bool add) {
return 0; return 0;
} }
static int address_add_internal(Link *link, Set **addresses, const Address *in, Address **ret) { static int address_add(Link *link, Address *address) {
_cleanup_(address_freep) Address *address = NULL;
int r; int r;
assert(link); assert(link);
assert(addresses); assert(address);
assert(in);
r = address_dup(in, &address); r = set_ensure_put(&link->addresses, &address_hash_ops, address);
if (r < 0)
return r;
/* Consider address tentative until we get the real flags from the kernel */
address->flags |= IFA_F_TENTATIVE;
r = set_ensure_put(addresses, &address_hash_ops, address);
if (r < 0) if (r < 0)
return r; return r;
if (r == 0) if (r == 0)
return -EEXIST; return -EEXIST;
address->link = link; address->link = link;
if (ret)
*ret = address;
TAKE_PTR(address);
return 0;
}
static int address_add_foreign(Link *link, const Address *in, Address **ret) {
return address_add_internal(link, &link->addresses_foreign, in, ret);
}
static int address_add(Link *link, const Address *in, Address **ret) {
Address *address;
int r;
assert(link);
assert(in);
r = address_get(link, in, &address);
if (r == -ENOENT) {
/* Address does not exist, create a new one */
r = address_add_internal(link, &link->addresses, in, &address);
if (r < 0)
return r;
} else if (r == 0) {
/* Take over a foreign address */
r = set_ensure_put(&link->addresses, &address_hash_ops, address);
if (r < 0)
return r;
set_remove(link->addresses_foreign, address);
} else if (r == 1) {
/* Already exists, do nothing */
;
} else
return r;
if (ret)
*ret = address;
return 0; return 0;
} }
@ -441,13 +404,13 @@ static int address_update(Address *address, const Address *src) {
assert(address); assert(address);
assert(address->link); assert(address->link);
assert(src);
link = address->link; link = address->link;
if (src) {
address->flags = src->flags; address->flags = src->flags;
address->scope = src->scope; address->scope = src->scope;
address->cinfo = src->cinfo; address->cinfo = src->cinfo;
}
if (address_is_ready(address) && if (address_is_ready(address) &&
address->family == AF_INET6 && address->family == AF_INET6 &&
@ -484,8 +447,8 @@ static int address_drop(Address *address) {
bool ready; bool ready;
int r; int r;
if (!address) assert(address);
return 0; assert(address->link);
ready = address_is_ready(address); ready = address_is_ready(address);
link = address->link; link = address->link;
@ -494,6 +457,7 @@ static int address_drop(Address *address) {
if (r < 0) if (r < 0)
log_link_warning_errno(link, r, "Failed to disable IP masquerading, ignoring: %m"); log_link_warning_errno(link, r, "Failed to disable IP masquerading, ignoring: %m");
if (address->state == 0)
address_free(address); address_free(address);
link_update_operstate(link, true); link_update_operstate(link, true);
@ -511,22 +475,14 @@ int address_get(Link *link, const Address *in, Address **ret) {
assert(in); assert(in);
existing = set_get(link->addresses, in); existing = set_get(link->addresses, in);
if (existing) { if (!existing)
if (ret) return -ENOENT;
*ret = existing;
return 1;
}
existing = set_get(link->addresses_foreign, in);
if (existing) {
if (ret) if (ret)
*ret = existing; *ret = existing;
return 0; return 0;
} }
return -ENOENT;
}
int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address **ret) { int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address **ret) {
_cleanup_(address_freep) Address *a = NULL; _cleanup_(address_freep) Address *a = NULL;
int r; int r;
@ -546,27 +502,6 @@ int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address **
return address_get(link, a, ret); return address_get(link, a, ret);
} }
static int addresses_get_ipv4_address(Set *addresses, const struct in_addr *address, Address **ret) {
Address *a;
assert(address);
SET_FOREACH(a, addresses) {
if (a->family != AF_INET)
continue;
if (!in4_addr_equal(&a->in_addr.in, address))
continue;
if (ret)
*ret = a;
return 0;
}
return -ENOENT;
}
int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned char prefixlen, Address **ret) { int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned char prefixlen, Address **ret) {
int r; int r;
@ -587,11 +522,24 @@ int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned ch
a->prefixlen = prefixlen; a->prefixlen = prefixlen;
return address_get(link, a, ret); return address_get(link, a, ret);
} else {
Address *a;
SET_FOREACH(a, link->addresses) {
if (a->family != AF_INET)
continue;
if (!in4_addr_equal(&a->in_addr.in, address))
continue;
if (ret)
*ret = a;
return 0;
} }
if (addresses_get_ipv4_address(link->addresses, address, ret) >= 0) return -ENOENT;
return 0; }
return addresses_get_ipv4_address(link->addresses_foreign, address, ret);
} }
int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready) { int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready) {
@ -606,7 +554,7 @@ int manager_has_address(Manager *manager, int family, const union in_addr_union
if (family == AF_INET) { if (family == AF_INET) {
HASHMAP_FOREACH(link, manager->links_by_index) HASHMAP_FOREACH(link, manager->links_by_index)
if (link_get_ipv4_address(link, &address->in, 0, &a) >= 0) if (link_get_ipv4_address(link, &address->in, 0, &a) >= 0)
return !check_ready || address_is_ready(a); return check_ready ? address_is_ready(a) : address_exists(a);
} else { } else {
_cleanup_(address_freep) Address *tmp = NULL; _cleanup_(address_freep) Address *tmp = NULL;
@ -619,7 +567,7 @@ int manager_has_address(Manager *manager, int family, const union in_addr_union
HASHMAP_FOREACH(link, manager->links_by_index) HASHMAP_FOREACH(link, manager->links_by_index)
if (address_get(link, tmp, &a) >= 0) if (address_get(link, tmp, &a) >= 0)
return !check_ready || address_is_ready(a); return check_ready ? address_is_ready(a) : address_exists(a);
} }
return false; return false;
@ -639,7 +587,7 @@ const char* format_lifetime(char *buf, size_t l, uint32_t lifetime) {
} }
static void log_address_debug(const Address *address, const char *str, const Link *link) { static void log_address_debug(const Address *address, const char *str, const Link *link) {
_cleanup_free_ char *addr = NULL, *peer = NULL, *flags_str = NULL; _cleanup_free_ char *state = NULL, *addr = NULL, *peer = NULL, *flags_str = NULL;
assert(address); assert(address);
assert(str); assert(str);
@ -648,14 +596,16 @@ static void log_address_debug(const Address *address, const char *str, const Lin
if (!DEBUG_LOGGING) if (!DEBUG_LOGGING)
return; return;
(void) network_config_state_to_string_alloc(address->state, &state);
(void) in_addr_to_string(address->family, &address->in_addr, &addr); (void) in_addr_to_string(address->family, &address->in_addr, &addr);
if (in_addr_is_set(address->family, &address->in_addr_peer)) if (in_addr_is_set(address->family, &address->in_addr_peer))
(void) in_addr_to_string(address->family, &address->in_addr_peer, &peer); (void) in_addr_to_string(address->family, &address->in_addr_peer, &peer);
(void) address_flags_to_string_alloc(address->flags, address->family, &flags_str); (void) address_flags_to_string_alloc(address->flags, address->family, &flags_str);
log_link_debug(link, "%s address: %s%s%s/%u (valid %s, preferred %s), flags: %s", log_link_debug(link, "%s %s address (%s): %s%s%s/%u (valid %s, preferred %s), flags: %s",
str, strnull(addr), peer ? " peer " : "", strempty(peer), address->prefixlen, str, strna(network_config_source_to_string(address->source)), strna(state),
strnull(addr), peer ? " peer " : "", strempty(peer), address->prefixlen,
FORMAT_LIFETIME(address->cinfo.ifa_valid), FORMAT_LIFETIME(address->cinfo.ifa_valid),
FORMAT_LIFETIME(address->cinfo.ifa_prefered), FORMAT_LIFETIME(address->cinfo.ifa_prefered),
strna(flags_str)); strna(flags_str));
@ -698,12 +648,8 @@ static int address_set_netlink_message(const Address *address, sd_netlink_messag
static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r; int r;
assert(rtnl);
assert(m); assert(m);
assert(link); assert(link);
assert(link->address_remove_messages > 0);
link->address_remove_messages--;
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 0; return 0;
@ -715,16 +661,19 @@ static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
return 1; return 1;
} }
int address_remove(const Address *address, Link *link) { int address_remove(Address *address) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
Link *link;
int r; int r;
assert(address); assert(address);
assert(IN_SET(address->family, AF_INET, AF_INET6)); assert(IN_SET(address->family, AF_INET, AF_INET6));
assert(link); assert(address->link);
assert(link->ifindex > 0); assert(address->link->ifindex > 0);
assert(link->manager); assert(address->link->manager);
assert(link->manager->rtnl); assert(address->link->manager->rtnl);
link = address->link;
log_address_debug(address, "Removing", link); log_address_debug(address, "Removing", link);
@ -744,27 +693,11 @@ int address_remove(const Address *address, Link *link) {
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
link_ref(link); link_ref(link);
link->address_remove_messages++;
address_enter_removing(address);
return 0; return 0;
} }
static bool link_is_static_address_configured(const Link *link, const Address *address) {
Address *net_address;
assert(link);
assert(address);
if (!link->network)
return false;
ORDERED_HASHMAP_FOREACH(net_address, link->network->addresses_by_section)
if (address_equal(net_address, address))
return true;
return false;
}
bool link_address_is_dynamic(const Link *link, const Address *address) { bool link_address_is_dynamic(const Link *link, const Address *address) {
Route *route; Route *route;
@ -777,7 +710,10 @@ bool link_address_is_dynamic(const Link *link, const Address *address) {
/* Even when the address is leased from a DHCP server, networkd assign the address /* Even when the address is leased from a DHCP server, networkd assign the address
* without lifetime when KeepConfiguration=dhcp. So, let's check that we have * without lifetime when KeepConfiguration=dhcp. So, let's check that we have
* corresponding routes with RTPROT_DHCP. */ * corresponding routes with RTPROT_DHCP. */
SET_FOREACH(route, link->routes_foreign) { SET_FOREACH(route, link->routes) {
if (route->source != NETWORK_CONFIG_SOURCE_FOREIGN)
continue;
if (route->protocol != RTPROT_DHCP) if (route->protocol != RTPROT_DHCP)
continue; continue;
@ -821,6 +757,7 @@ int link_drop_ipv6ll_addresses(Link *link) {
_cleanup_(address_freep) Address *a = NULL; _cleanup_(address_freep) Address *a = NULL;
unsigned char flags, prefixlen; unsigned char flags, prefixlen;
struct in6_addr address; struct in6_addr address;
Address *existing;
int ifindex; int ifindex;
/* NETLINK_GET_STRICT_CHK socket option is supported since kernel 4.20. To support /* NETLINK_GET_STRICT_CHK socket option is supported since kernel 4.20. To support
@ -866,7 +803,15 @@ int link_drop_ipv6ll_addresses(Link *link) {
a->prefixlen = prefixlen; a->prefixlen = prefixlen;
a->flags = flags; a->flags = flags;
r = address_remove(a, link); if (address_get(link, a, &existing) < 0) {
r = address_add(link, a);
if (r < 0)
return r;
existing = TAKE_PTR(a);
}
r = address_remove(existing);
if (r < 0) if (r < 0)
return r; return r;
} }
@ -879,63 +824,80 @@ int link_drop_foreign_addresses(Link *link) {
int k, r = 0; int k, r = 0;
assert(link); assert(link);
assert(link->network);
SET_FOREACH(address, link->addresses_foreign) { /* First, mark all addresses. */
SET_FOREACH(address, link->addresses) {
/* We consider IPv6LL addresses to be managed by the kernel, or dropped in link_drop_ipv6ll_addresses() */ /* We consider IPv6LL addresses to be managed by the kernel, or dropped in link_drop_ipv6ll_addresses() */
if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6)) if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6))
continue; continue;
/* Ignore addresses we configured. */
if (address->source != NETWORK_CONFIG_SOURCE_FOREIGN)
continue;
/* Ignore addresses not assigned yet or already removing. */
if (!address_exists(address))
continue;
if (link_address_is_dynamic(link, address)) { if (link_address_is_dynamic(link, address)) {
if (link->network && FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) if (link->network && FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
continue; continue;
} else if (link->network && FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC)) } else if (link->network && FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
continue; continue;
if (link_is_static_address_configured(link, address)) { address_mark(address);
k = address_add(link, address, NULL);
if (k < 0) {
log_link_error_errno(link, k, "Failed to add address: %m");
if (r >= 0)
r = k;
} }
} else {
k = address_remove(address, link); /* Then, unmark requested addresses. */
ORDERED_HASHMAP_FOREACH(address, link->network->addresses_by_section) {
Address *existing;
if (address_get(link, address, &existing) >= 0)
address_unmark(existing);
}
/* Finally, remove all marked addresses. */
SET_FOREACH(address, link->addresses) {
if (!address_is_marked(address))
continue;
k = address_remove(address);
if (k < 0 && r >= 0) if (k < 0 && r >= 0)
r = k; r = k;
} }
}
return r; return r;
} }
int link_drop_addresses(Link *link) { int link_drop_addresses(Link *link) {
Address *address, *pool_address; Address *address;
int k, r = 0; int k, r = 0;
assert(link); assert(link);
SET_FOREACH(address, link->addresses) { SET_FOREACH(address, link->addresses) {
/* Ignore addresses not assigned yet or already removing. */
if (!address_exists(address))
continue;
/* We consider IPv6LL addresses to be managed by the kernel, or dropped in link_drop_ipv6ll_addresses() */ /* We consider IPv6LL addresses to be managed by the kernel, or dropped in link_drop_ipv6ll_addresses() */
if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6)) if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6))
continue; continue;
k = address_remove(address, link); k = address_remove(address);
if (k < 0 && r >= 0) { if (k < 0 && r >= 0) {
r = k; r = k;
continue; continue;
} }
SET_FOREACH(pool_address, link->pool_addresses)
if (address_equal(address, pool_address))
address_free(set_remove(link->pool_addresses, pool_address));
} }
return r; return r;
} }
static int address_acquire(Link *link, const Address *original, Address **ret) { static int address_acquire(Link *link, const Address *original, Address **ret) {
union in_addr_union in_addr = IN_ADDR_NULL;
_cleanup_(address_freep) Address *na = NULL; _cleanup_(address_freep) Address *na = NULL;
union in_addr_union in_addr;
int r; int r;
assert(link); assert(link);
@ -969,12 +931,6 @@ static int address_acquire(Link *link, const Address *original, Address **ret) {
na->in_addr = in_addr; na->in_addr = in_addr;
address_set_broadcast(na); address_set_broadcast(na);
r = set_ensure_put(&link->pool_addresses, &address_hash_ops, na);
if (r < 0)
return r;
if (r == 0)
return -EEXIST;
*ret = TAKE_PTR(na); *ret = TAKE_PTR(na);
return 1; return 1;
} }
@ -1063,6 +1019,25 @@ static int address_configure(
return 0; return 0;
} }
void address_cancel_request(Address *address) {
Request req;
assert(address);
assert(address->link);
if (!address_is_requesting(address))
return;
req = (Request) {
.link = address->link,
.type = REQUEST_TYPE_ADDRESS,
.address = address,
};
request_drop(ordered_set_get(address->link->manager->request_queue, &req));
address_cancel_requesting(address);
}
static int static_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { static int static_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r; int r;
@ -1084,25 +1059,6 @@ static int static_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
return 1; return 1;
} }
static int static_address_after_configure(Request *req, void *object) {
Address *address = object;
Link *link;
int r;
assert(req);
assert(req->link);
assert(req->type == REQUEST_TYPE_ADDRESS);
assert(address);
link = req->link;
r = set_ensure_put(&link->static_addresses, &address_hash_ops, address);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to store static address: %m");
return 0;
}
int link_request_address( int link_request_address(
Link *link, Link *link,
Address *address, Address *address,
@ -1111,50 +1067,72 @@ int link_request_address(
link_netlink_message_handler_t netlink_handler, link_netlink_message_handler_t netlink_handler,
Request **ret) { Request **ret) {
Address *acquired; Address *acquired, *existing;
int r; int r;
assert(link); assert(link);
assert(address); assert(address);
assert(address->source != NETWORK_CONFIG_SOURCE_FOREIGN);
r = address_acquire(link, address, &acquired); r = address_acquire(link, address, &acquired);
if (r < 0) if (r < 0)
return log_link_warning_errno(link, r, "Failed to acquire an address from pool: %m"); return log_link_warning_errno(link, r, "Failed to acquire an address from pool: %m");
if (r > 0) { if (r > 0) {
if (consume_object) { if (consume_object)
address_free(address); address_free(address);
consume_object = false; /* address from pool is already managed by Link. */
}
address = acquired; address = acquired;
consume_object = true;
} }
if (address_get(link, address, &existing) < 0) {
_cleanup_(address_freep) Address *tmp = NULL;
r = address_dup(address, &tmp);
if (r < 0)
return r;
/* Consider address tentative until we get the real flags from the kernel */
tmp->flags |= IFA_F_TENTATIVE;
r = address_add(link, tmp);
if (r < 0)
return r;
existing = TAKE_PTR(tmp);
} else {
existing->source = address->source;
existing->provider = address->provider;
}
r = ipv4acd_configure(existing);
if (r < 0)
return r;
log_address_debug(address, "Requesting", link); log_address_debug(address, "Requesting", link);
r = link_queue_request(link, REQUEST_TYPE_ADDRESS, address, consume_object, r = link_queue_request(link, REQUEST_TYPE_ADDRESS, address, consume_object,
message_counter, netlink_handler, ret); message_counter, netlink_handler, ret);
if (r < 0) if (r < 0)
return log_link_warning_errno(link, r, "Failed to request address: %m"); return log_link_warning_errno(link, r, "Failed to request address: %m");
return r; if (r == 0)
return 0;
address_enter_requesting(existing);
return 1;
} }
int link_request_static_address(Link *link, Address *address, bool consume) { int link_request_static_address(Link *link, Address *address, bool consume) {
Request *req;
int r;
assert(link); assert(link);
assert(address); assert(address);
assert(address->source == NETWORK_CONFIG_SOURCE_STATIC);
r = link_request_address(link, address, consume, &link->static_address_messages, return link_request_address(link, address, consume, &link->static_address_messages,
static_address_handler, &req); static_address_handler, NULL);
if (r <= 0)
return r;
req->after_configure = static_address_after_configure;
return 0;
} }
int link_request_static_addresses(Link *link) { int link_request_static_addresses(Link *link) {
Address *a; Address *a;
Prefix *p;
int r; int r;
assert(link); assert(link);
@ -1168,52 +1146,13 @@ int link_request_static_addresses(Link *link) {
return r; return r;
} }
HASHMAP_FOREACH(p, link->network->prefixes_by_section) { r = link_request_radv_addresses(link);
_cleanup_(address_freep) Address *address = NULL;
if (!p->assign)
continue;
r = address_new(&address);
if (r < 0)
return log_oom();
r = sd_radv_prefix_get_prefix(p->radv_prefix, &address->in_addr.in6, &address->prefixlen);
if (r < 0)
return log_link_warning_errno(link, r, "Could not get RA prefix: %m");
r = generate_ipv6_eui_64_address(link, &address->in_addr.in6);
if (r < 0)
return log_link_warning_errno(link, r, "Could not generate EUI64 address: %m");
address->family = AF_INET6;
address->route_metric = p->route_metric;
r = link_request_static_address(link, TAKE_PTR(address), true);
if (r < 0) if (r < 0)
return r; return r;
}
if (in4_addr_is_set(&link->network->dhcp_server_address)) { r = link_request_dhcp_server_address(link);
_cleanup_(address_freep) Address *address = NULL;
r = address_new(&address);
if (r < 0)
return log_oom();
address->family = AF_INET;
address->in_addr.in = link->network->dhcp_server_address;
address->prefixlen = link->network->dhcp_server_address_prefixlen;
address_set_broadcast(address);
/* The same address may be explicitly configured in [Address] or [Network] section.
* Configure the DHCP server address only when it is not. */
if (!link_is_static_address_configured(link, address)) {
r = link_request_static_address(link, TAKE_PTR(address), true);
if (r < 0) if (r < 0)
return r; return r;
}
}
if (link->static_address_messages == 0) { if (link->static_address_messages == 0) {
link->static_addresses_configured = true; link->static_addresses_configured = true;
@ -1226,41 +1165,25 @@ int link_request_static_addresses(Link *link) {
return 0; return 0;
} }
static int address_is_ready_to_configure(Link *link, const Address *address) { static bool address_is_ready_to_configure(Link *link, const Address *address) {
int r;
assert(link); assert(link);
assert(address); assert(address);
if (!link_is_ready_to_configure(link, false)) if (!link_is_ready_to_configure(link, false))
return false; return false;
if (link->address_remove_messages > 0) if (FLAGS_SET(address->state, NETWORK_CONFIG_STATE_PROBING))
return false; return false;
if (address_get(link, address, NULL) >= 0) /* Refuse adding more than the limit */
return true;
/* If this is a new address, then refuse adding more than the limit */
if (set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX) if (set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX)
return log_link_warning_errno(link, SYNTHETIC_ERRNO(E2BIG), return false;
"Too many addresses are configured, refusing: %m");
if (address->family == AF_INET &&
address->duplicate_address_detection & ADDRESS_FAMILY_IPV4 &&
link->hw_addr.length == ETH_ALEN &&
!ether_addr_is_null(&link->hw_addr.ether))
return ipv4acd_address_is_ready_to_configure(link, address);
r = address_add(link, address, NULL);
if (r < 0)
return log_link_warning_errno(link, r, "Could not add address: %m");;
return true; return true;
} }
int request_process_address(Request *req) { int request_process_address(Request *req) {
Address *a; Address *existing;
Link *link; Link *link;
int r; int r;
@ -1271,26 +1194,18 @@ int request_process_address(Request *req) {
link = req->link; link = req->link;
r = address_is_ready_to_configure(link, req->address); r = address_get(link, req->address, &existing);
if (r <= 0) if (r < 0)
return r; return log_link_warning_errno(link, r, "Failed to get address: %m");
if (!address_is_ready_to_configure(link, existing))
return 0;
r = address_configure(req->address, link, req->netlink_handler); r = address_configure(req->address, link, req->netlink_handler);
if (r < 0) if (r < 0)
return r; return log_link_warning_errno(link, r, "Failed to configure address: %m");
/* To prevent a double decrement on failure in after_configure(). */ address_enter_configuring(existing);
req->message_counter = NULL;
r = address_get(link, req->address, &a);
if (r < 0)
return r;
if (req->after_configure) {
r = req->after_configure(req, a);
if (r < 0)
return r;
}
return 1; return 1;
} }
@ -1441,10 +1356,14 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
switch (type) { switch (type) {
case RTM_NEWADDR: case RTM_NEWADDR:
log_address_debug(tmp, address ? "Remembering updated" : "Remembering foreign", link); if (address) {
if (!address) { address_enter_configured(address);
/* An address appeared that we did not request */ log_address_debug(address, "Remembering updated", link);
r = address_add_foreign(link, tmp, &address); } else {
address_enter_configured(tmp);
log_address_debug(tmp, "Received new", link);
r = address_add(link, tmp);
if (r < 0) { if (r < 0) {
_cleanup_free_ char *buf = NULL; _cleanup_free_ char *buf = NULL;
@ -1453,6 +1372,8 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
strnull(buf)); strnull(buf));
return 0; return 0;
} }
address = TAKE_PTR(tmp);
} }
/* address_update() logs internally, so we don't need to here. */ /* address_update() logs internally, so we don't need to here. */
@ -1463,8 +1384,12 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
break; break;
case RTM_DELADDR: case RTM_DELADDR:
log_address_debug(tmp, address ? "Forgetting" : "Kernel removed unknown", link); if (address) {
address_enter_removed(address);
log_address_debug(address, address->state == 0 ? "Forgetting" : "Removed", link);
(void) address_drop(address); (void) address_drop(address);
} else
log_address_debug(tmp, "Kernel removed unknown", link);
break; break;
@ -1922,12 +1847,6 @@ int config_parse_duplicate_address_detection(
return 0; return 0;
} }
bool address_is_ready(const Address *a) {
assert(a);
return !(a->flags & IFA_F_TENTATIVE);
}
static int address_section_verify(Address *address) { static int address_section_verify(Address *address) {
if (section_is_invalid(address->section)) if (section_is_invalid(address->section))
return -EINVAL; return -EINVAL;

View File

@ -21,10 +21,12 @@ typedef struct Request Request;
typedef int (*address_ready_callback_t)(Address *address); typedef int (*address_ready_callback_t)(Address *address);
struct Address { struct Address {
Link *link;
Network *network; Network *network;
NetworkConfigSection *section; NetworkConfigSection *section;
NetworkConfigSource source;
Link *link; NetworkConfigState state;
union in_addr_union provider; /* DHCP server or router address */
int family; int family;
unsigned char prefixlen; unsigned char prefixlen;
@ -42,8 +44,6 @@ struct Address {
bool scope_set:1; bool scope_set:1;
bool ip_masquerade_done:1; bool ip_masquerade_done:1;
bool is_static:1; /* currently only used by IPv4ACD */
bool acd_announced:1;
AddressFamily duplicate_address_detection; AddressFamily duplicate_address_detection;
sd_ipv4acd *acd; sd_ipv4acd *acd;
@ -62,8 +62,7 @@ int address_new(Address **ret);
Address* address_free(Address *address); Address* address_free(Address *address);
int address_get(Link *link, const Address *in, Address **ret); int address_get(Link *link, const Address *in, Address **ret);
int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg); int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg);
int address_remove(const Address *address, Link *link); int address_remove(Address *address);
bool address_equal(const Address *a1, const Address *a2);
int address_dup(const Address *src, Address **ret); int address_dup(const Address *src, Address **ret);
bool address_is_ready(const Address *a); bool address_is_ready(const Address *a);
void address_set_broadcast(Address *a); void address_set_broadcast(Address *a);
@ -80,6 +79,7 @@ int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address **
int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned char prefixlen, Address **ret); int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned char prefixlen, Address **ret);
int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready); int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready);
void address_cancel_request(Address *address);
int link_request_address( int link_request_address(
Link *link, Link *link,
Address *address, Address *address,
@ -99,6 +99,16 @@ void address_hash_func(const Address *a, struct siphash *state);
int address_compare_func(const Address *a1, const Address *a2); int address_compare_func(const Address *a1, const Address *a2);
extern const struct hash_ops address_hash_ops; extern const struct hash_ops address_hash_ops;
DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(Address, address);
static inline void address_enter_probing(Address *address) {
address_update_state(address, NETWORK_CONFIG_STATE_PROBING, NETWORK_CONFIG_STATE_PROBING);
}
static inline void address_cancel_probing(Address *address) {
address_update_state(address, NETWORK_CONFIG_STATE_PROBING, 0);
}
void link_mark_addresses(Link *link, NetworkConfigSource source, const struct in6_addr *router);
CONFIG_PARSER_PROTOTYPE(config_parse_address); CONFIG_PARSER_PROTOTYPE(config_parse_address);
CONFIG_PARSER_PROTOTYPE(config_parse_broadcast); CONFIG_PARSER_PROTOTYPE(config_parse_broadcast);
CONFIG_PARSER_PROTOTYPE(config_parse_label); CONFIG_PARSER_PROTOTYPE(config_parse_label);

View File

@ -16,6 +16,7 @@
#include "networkd-manager.h" #include "networkd-manager.h"
#include "networkd-network.h" #include "networkd-network.h"
#include "networkd-queue.h" #include "networkd-queue.h"
#include "networkd-route.h"
#include "parse-util.h" #include "parse-util.h"
#include "socket-netlink.h" #include "socket-netlink.h"
#include "string-table.h" #include "string-table.h"
@ -74,6 +75,40 @@ void network_adjust_dhcp_server(Network *network) {
} }
} }
int link_request_dhcp_server_address(Link *link) {
_cleanup_(address_freep) Address *address = NULL;
Address *existing;
int r;
assert(link);
assert(link->network);
if (!link_dhcp4_server_enabled(link))
return 0;
if (!in4_addr_is_set(&link->network->dhcp_server_address))
return 0;
r = address_new(&address);
if (r < 0)
return r;
address->source = NETWORK_CONFIG_SOURCE_STATIC;
address->family = AF_INET;
address->in_addr.in = link->network->dhcp_server_address;
address->prefixlen = link->network->dhcp_server_address_prefixlen;
address_set_broadcast(address);
if (address_get(link, address, &existing) >= 0 &&
address_exists(existing) &&
existing->source == NETWORK_CONFIG_SOURCE_STATIC)
/* The same address seems explicitly configured in [Address] or [Network] section.
* Configure the DHCP server address only when it is not. */
return 0;
return link_request_static_address(link, TAKE_PTR(address), true);
}
static int link_find_dhcp_server_address(Link *link, Address **ret) { static int link_find_dhcp_server_address(Link *link, Address **ret) {
Address *address; Address *address;
@ -86,10 +121,18 @@ static int link_find_dhcp_server_address(Link *link, Address **ret) {
link->network->dhcp_server_address_prefixlen, ret); link->network->dhcp_server_address_prefixlen, ret);
/* If not, then select one from static addresses. */ /* If not, then select one from static addresses. */
SET_FOREACH(address, link->static_addresses) SET_FOREACH(address, link->addresses) {
if (address->family == AF_INET && if (address->source != NETWORK_CONFIG_SOURCE_STATIC)
!in4_addr_is_localhost(&address->in_addr.in) && continue;
in4_addr_is_null(&address->in_addr_peer.in)) { if (!address_exists(address))
continue;
if (address->family != AF_INET)
continue;
if (in4_addr_is_localhost(&address->in_addr.in))
continue;
if (in4_addr_is_set(&address->in_addr_peer.in))
continue;
*ret = address; *ret = address;
return 0; return 0;
} }
@ -505,9 +548,6 @@ static bool dhcp_server_is_ready_to_configure(Link *link) {
if (!link_has_carrier(link)) if (!link_has_carrier(link))
return false; return false;
if (link->address_remove_messages > 0)
return false;
if (!link->static_addresses_configured) if (!link->static_addresses_configured)
return false; return false;

View File

@ -9,6 +9,7 @@ typedef struct Request Request;
void network_adjust_dhcp_server(Network *network); void network_adjust_dhcp_server(Network *network);
int link_request_dhcp_server_address(Link *link);
int link_request_dhcp_server(Link *link); int link_request_dhcp_server(Link *link);
int request_process_dhcp_server(Request *req); int request_process_dhcp_server(Request *req);

View File

@ -27,7 +27,7 @@
#include "sysctl-util.h" #include "sysctl-util.h"
static int dhcp4_request_address_and_routes(Link *link, bool announce); static int dhcp4_request_address_and_routes(Link *link, bool announce);
static int dhcp4_remove_all(Link *link); static int dhcp4_check_ready(Link *link);
void network_adjust_dhcp4(Network *network) { void network_adjust_dhcp4(Network *network) {
assert(network); assert(network);
@ -54,84 +54,106 @@ void network_adjust_dhcp4(Network *network) {
network->dhcp_client_identifier = network->dhcp_anonymize ? DHCP_CLIENT_ID_MAC : DHCP_CLIENT_ID_DUID; network->dhcp_client_identifier = network->dhcp_anonymize ? DHCP_CLIENT_ID_MAC : DHCP_CLIENT_ID_DUID;
} }
static int dhcp4_release_old_lease(Link *link) { static int dhcp4_remove_address_and_routes(Link *link, bool only_marked) {
Address *address;
Route *route; Route *route;
int k, r = 0; int k, r = 0;
assert(link); assert(link);
if (!link->dhcp_address_old && set_isempty(link->dhcp_routes_old)) SET_FOREACH(route, link->routes) {
return 0; if (route->source != NETWORK_CONFIG_SOURCE_DHCP4)
continue;
if (only_marked && !route_is_marked(route))
continue;
log_link_debug(link, "Removing old DHCPv4 address and routes."); k = route_remove(route);
SET_FOREACH(route, link->dhcp_routes_old) {
k = route_remove(route, NULL, link);
if (k < 0) if (k < 0)
r = k; r = k;
route_cancel_request(route);
} }
if (link->dhcp_address_old) { SET_FOREACH(address, link->addresses) {
k = address_remove(link->dhcp_address_old, link); if (address->source != NETWORK_CONFIG_SOURCE_DHCP4)
continue;
if (only_marked && !address_is_marked(address))
continue;
k = address_remove(address);
if (k < 0) if (k < 0)
r = k; r = k;
address_cancel_request(address);
} }
return r; return r;
} }
static void dhcp4_check_ready(Link *link) { static int dhcp4_address_get(Link *link, Address **ret) {
Address *address;
assert(link);
SET_FOREACH(address, link->addresses) {
if (address->source != NETWORK_CONFIG_SOURCE_DHCP4)
continue;
if (address_is_marked(address))
continue;
if (ret)
*ret = address;
return 0;
}
return -ENOENT;
}
static int dhcp4_address_ready_callback(Address *address) {
assert(address);
assert(address->link);
/* Do not call this again. */
address->callback = NULL;
return dhcp4_check_ready(address->link);
}
static int dhcp4_check_ready(Link *link) {
Address *address;
int r; int r;
assert(link);
if (link->dhcp4_messages > 0) { if (link->dhcp4_messages > 0) {
log_link_debug(link, "%s(): DHCPv4 address and routes are not set.", __func__); log_link_debug(link, "%s(): DHCPv4 address and routes are not set.", __func__);
return; return 0;
} }
if (!link->dhcp_address) { if (dhcp4_address_get(link, &address) < 0) {
log_link_debug(link, "%s(): DHCPv4 address is not set.", __func__); log_link_debug(link, "%s(): DHCPv4 address is not set.", __func__);
return; return 0;
} }
if (!address_is_ready(link->dhcp_address)) { if (!address_is_ready(address)) {
log_link_debug(link, "%s(): DHCPv4 address is not ready.", __func__); log_link_debug(link, "%s(): DHCPv4 address is not ready.", __func__);
return; address->callback = dhcp4_address_ready_callback;
return 0;
} }
link->dhcp4_configured = true; link->dhcp4_configured = true;
log_link_debug(link, "DHCPv4 address and routes set.");
/* New address and routes are configured now. Let's release old lease. */ /* New address and routes are configured now. Let's release old lease. */
r = dhcp4_release_old_lease(link); r = dhcp4_remove_address_and_routes(link, /* only_marked = */ true);
if (r < 0) { if (r < 0)
link_enter_failed(link); return r;
return;
}
r = sd_ipv4ll_stop(link->ipv4ll); r = sd_ipv4ll_stop(link->ipv4ll);
if (r < 0) if (r < 0)
log_link_warning_errno(link, r, "Failed to drop IPv4 link-local address, ignoring: %m"); return log_link_warning_errno(link, r, "Failed to drop IPv4 link-local address: %m");
link_check_ready(link); link_check_ready(link);
}
static int dhcp4_after_route_configure(Request *req, void *object) {
Route *route = object;
Link *link;
int r;
assert(req);
assert(req->link);
assert(req->type == REQUEST_TYPE_ROUTE);
assert(route);
link = req->link;
r = set_ensure_put(&link->dhcp_routes, &route_hash_ops, route);
if (r < 0)
return log_link_error_errno(link, r, "Failed to store DHCPv4 route: %m");
set_remove(link->dhcp_routes_old, route);
return 0; return 0;
} }
@ -140,7 +162,7 @@ static int dhcp4_retry(Link *link) {
assert(link); assert(link);
r = dhcp4_remove_all(link); r = dhcp4_remove_address_and_routes(link, /* only_marked = */ false);
if (r < 0) if (r < 0)
return r; return r;
@ -192,19 +214,29 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li
return 1; return 1;
} }
dhcp4_check_ready(link); r = dhcp4_check_ready(link);
if (r < 0)
link_enter_failed(link);
return 1; return 1;
} }
static int dhcp4_request_route(Route *in, Link *link) { static int dhcp4_request_route(Route *in, Link *link) {
_cleanup_(route_freep) Route *route = in; _cleanup_(route_freep) Route *route = in;
Request *req; struct in_addr server;
Route *existing;
int r; int r;
assert(route); assert(route);
assert(link); assert(link);
assert(link->dhcp_lease);
r = sd_dhcp_lease_get_server_identifier(link->dhcp_lease, &server);
if (r < 0)
return log_link_debug_errno(link, r, "Failed to get DHCP server IP address: %m");
route->source = NETWORK_CONFIG_SOURCE_DHCP4;
route->provider.in = server;
route->family = AF_INET; route->family = AF_INET;
if (!route->protocol_set) if (!route->protocol_set)
route->protocol = RTPROT_DHCP; route->protocol = RTPROT_DHCP;
@ -215,20 +247,13 @@ static int dhcp4_request_route(Route *in, Link *link) {
if (route->mtu == 0) if (route->mtu == 0)
route->mtu = link->network->dhcp_route_mtu; route->mtu = link->network->dhcp_route_mtu;
r = link_has_route(link, route); if (route_get(NULL, link, route, &existing) < 0) /* This is a new route. */
if (r < 0)
return r;
if (r == 0)
link->dhcp4_configured = false; link->dhcp4_configured = false;
else
route_unmark(existing);
r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp4_messages, return link_request_route(link, TAKE_PTR(route), true, &link->dhcp4_messages,
dhcp4_route_handler, &req); dhcp4_route_handler, NULL);
if (r <= 0)
return r;
req->after_configure = dhcp4_after_route_configure;
return 0;
} }
static bool link_prefixroute(Link *link) { static bool link_prefixroute(Link *link) {
@ -675,19 +700,10 @@ static int dhcp4_request_routes_to_ntp(Link *link, const struct in_addr *gw) {
static int dhcp4_request_routes(Link *link) { static int dhcp4_request_routes(Link *link) {
struct in_addr gw = {}; struct in_addr gw = {};
Route *rt;
int r; int r;
assert(link); assert(link);
assert(link->dhcp_lease);
if (!link->dhcp_lease)
return 0;
while ((rt = set_steal_first(link->dhcp_routes))) {
r = set_ensure_put(&link->dhcp_routes_old, &route_hash_ops, rt);
if (r < 0)
return log_link_error_errno(link, r, "Failed to store old DHCPv4 route: %m");
}
r = dhcp4_request_prefix_route(link); r = dhcp4_request_prefix_route(link);
if (r < 0) if (r < 0)
@ -768,27 +784,6 @@ static int dhcp_reset_hostname(Link *link) {
return 0; return 0;
} }
static int dhcp4_remove_all(Link *link) {
Route *route;
int k, r = 0;
assert(link);
SET_FOREACH(route, link->dhcp_routes) {
k = route_remove(route, NULL, link);
if (k < 0)
r = k;
}
if (link->dhcp_address) {
k = address_remove(link->dhcp_address, link);
if (k < 0)
r = k;
}
return r;
}
int dhcp4_lease_lost(Link *link) { int dhcp4_lease_lost(Link *link) {
int k, r = 0; int k, r = 0;
@ -799,12 +794,7 @@ int dhcp4_lease_lost(Link *link) {
link->dhcp4_configured = false; link->dhcp4_configured = false;
/* dhcp4_lease_lost() may be called during renewing IP address. */ k = dhcp4_remove_address_and_routes(link, /* only_marked = */ false);
k = dhcp4_release_old_lease(link);
if (k < 0)
r = k;
k = dhcp4_remove_all(link);
if (k < 0) if (k < 0)
r = k; r = k;
@ -819,24 +809,7 @@ int dhcp4_lease_lost(Link *link) {
link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease); link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
link_dirty(link); link_dirty(link);
if (link->network->dhcp_send_decline) { /* If one of the above failed. Do not request nexthops and routes. */
Address *a;
/* The acquired address may be still ARP probing and not configured. */
SET_FOREACH(a, link->addresses_ipv4acd)
if (!a->is_static && address_get(link, a, NULL) < 0) {
Request req = {
.link = link,
.address = a,
};
log_link_debug(link, "Canceling the request to configure DHCPv4 address "IPV4_ADDRESS_FMT_STR,
IPV4_ADDRESS_FMT_VAL(a->in_addr.in));
request_drop(ordered_set_get(link->manager->request_queue, &req));
}
}
if (r < 0) if (r < 0)
return r; return r;
@ -847,43 +820,6 @@ int dhcp4_lease_lost(Link *link) {
return link_request_static_routes(link, true); return link_request_static_routes(link, true);
} }
static int dhcp4_address_ready_callback(Address *address) {
assert(address);
/* Do not call this again. */
address->callback = NULL;
dhcp4_check_ready(address->link);
return 0;
}
static int dhcp4_after_address_configure(Request *req, void *object) {
Address *address = object;
Link *link;
int r;
assert(req);
assert(req->link);
assert(req->type == REQUEST_TYPE_ADDRESS);
assert(address);
link = req->link;
if (!address_equal(link->dhcp_address, address)) {
if (link->dhcp_address_old &&
!address_equal(link->dhcp_address_old, link->dhcp_address)) {
/* Still too old address exists? Let's remove it immediately. */
r = address_remove(link->dhcp_address_old, link);
if (r < 0)
return r;
}
link->dhcp_address_old = link->dhcp_address;
}
link->dhcp_address = address;
return 0;
}
static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r; int r;
@ -896,12 +832,9 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
if (r <= 0) if (r <= 0)
return r; return r;
if (address_is_ready(link->dhcp_address)) { r = dhcp4_check_ready(link);
r = dhcp4_address_ready_callback(link->dhcp_address);
if (r < 0) if (r < 0)
link_enter_failed(link); link_enter_failed(link);
} else
link->dhcp_address->callback = dhcp4_address_ready_callback;
return 1; return 1;
} }
@ -911,14 +844,12 @@ static int dhcp4_request_address(Link *link, bool announce) {
uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME; uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
struct in_addr address, netmask, server; struct in_addr address, netmask, server;
unsigned prefixlen; unsigned prefixlen;
Request *req; Address *existing;
int r; int r;
assert(link); assert(link);
assert(link->network); assert(link->network);
assert(link->dhcp_lease);
if (!link->dhcp_lease)
return 0;
r = sd_dhcp_lease_get_address(link->dhcp_lease, &address); r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
if (r < 0) if (r < 0)
@ -973,6 +904,8 @@ static int dhcp4_request_address(Link *link, bool announce) {
if (r < 0) if (r < 0)
return log_oom(); return log_oom();
addr->source = NETWORK_CONFIG_SOURCE_DHCP4;
addr->provider.in = server;
addr->family = AF_INET; addr->family = AF_INET;
addr->in_addr.in.s_addr = address.s_addr; addr->in_addr.in.s_addr = address.s_addr;
addr->cinfo.ifa_prefered = lifetime; addr->cinfo.ifa_prefered = lifetime;
@ -988,17 +921,15 @@ static int dhcp4_request_address(Link *link, bool announce) {
if (r < 0) if (r < 0)
return r; return r;
if (address_get(link, addr, NULL) < 0) if (address_get(link, addr, &existing) < 0) /* The address is new. */
link->dhcp4_configured = false; link->dhcp4_configured = false;
else
address_unmark(existing);
r = link_request_address(link, TAKE_PTR(addr), true, &link->dhcp4_messages, r = link_request_address(link, TAKE_PTR(addr), true, &link->dhcp4_messages,
dhcp4_address_handler, &req); dhcp4_address_handler, NULL);
if (r < 0) if (r < 0)
return log_link_error_errno(link, r, "Failed to request DHCPv4 address: %m"); return log_link_error_errno(link, r, "Failed to request DHCPv4 address: %m");
if (r == 0)
return 0;
req->after_configure = dhcp4_after_address_configure;
return 0; return 0;
} }
@ -1008,6 +939,9 @@ static int dhcp4_request_address_and_routes(Link *link, bool announce) {
assert(link); assert(link);
link_mark_addresses(link, NETWORK_CONFIG_SOURCE_DHCP4, NULL);
link_mark_routes(link, NETWORK_CONFIG_SOURCE_DHCP4, NULL);
r = dhcp4_request_address(link, announce); r = dhcp4_request_address(link, announce);
if (r < 0) if (r < 0)
return r; return r;
@ -1016,8 +950,10 @@ static int dhcp4_request_address_and_routes(Link *link, bool announce) {
if (r < 0) if (r < 0)
return r; return r;
if (!link->dhcp4_configured) {
link_set_state(link, LINK_STATE_CONFIGURING); link_set_state(link, LINK_STATE_CONFIGURING);
link_check_ready(link); link_check_ready(link);
}
return 0; return 0;
} }
@ -1372,7 +1308,9 @@ static int dhcp4_set_request_address(Link *link) {
if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
return 0; return 0;
SET_FOREACH(a, link->addresses_foreign) { SET_FOREACH(a, link->addresses) {
if (a->source != NETWORK_CONFIG_SOURCE_FOREIGN)
continue;
if (a->family != AF_INET) if (a->family != AF_INET)
continue; continue;
if (link_address_is_dynamic(link, a)) if (link_address_is_dynamic(link, a))

File diff suppressed because it is too large Load Diff

View File

@ -16,18 +16,9 @@ typedef enum DHCP6ClientStartMode {
typedef struct Link Link; typedef struct Link Link;
typedef struct Request Request; typedef struct Request Request;
typedef struct DHCP6DelegatedPrefix {
struct in6_addr prefix; /* Prefix assigned to the link */
struct in6_addr pd_prefix; /* PD prefix provided by DHCP6 lease */
Link *link;
} DHCP6DelegatedPrefix;
DHCP6DelegatedPrefix *dhcp6_pd_free(DHCP6DelegatedPrefix *p);
DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6DelegatedPrefix*, dhcp6_pd_free);
bool link_dhcp6_with_address_enabled(Link *link); bool link_dhcp6_with_address_enabled(Link *link);
bool link_dhcp6_pd_is_enabled(Link *link); bool link_dhcp6_pd_is_enabled(Link *link);
int dhcp6_pd_remove(Link *link); int dhcp6_pd_remove(Link *link, bool only_marked);
int dhcp6_update_mac(Link *link); int dhcp6_update_mac(Link *link);
int dhcp6_start(Link *link); int dhcp6_start(Link *link);
int dhcp6_request_information(Link *link, int ir); int dhcp6_request_information(Link *link, int ir);

View File

@ -9,19 +9,26 @@
#include "networkd-link.h" #include "networkd-link.h"
#include "networkd-manager.h" #include "networkd-manager.h"
static int static_address_on_stop(Link *link, Address *address) { static int static_ipv4acd_address_remove(Link *link, Address *address, bool on_conflict) {
int r; int r;
assert(link); assert(link);
assert(address); assert(address);
if (address_get(link, address, NULL) < 0) /* Prevent form the address being freed. */
return 0; address_enter_probing(address);
if (!address_exists(address))
return 0; /* Not assigned. */
if (on_conflict)
log_link_warning(link, "Dropping address "IPV4_ADDRESS_FMT_STR", as an address conflict was detected.",
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
else
log_link_debug(link, "Removing address "IPV4_ADDRESS_FMT_STR", as the ACD client is stopped.", log_link_debug(link, "Removing address "IPV4_ADDRESS_FMT_STR", as the ACD client is stopped.",
IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
r = address_remove(address, link); r = address_remove(address);
if (r < 0) if (r < 0)
return log_link_warning_errno(link, r, "Failed to remove address "IPV4_ADDRESS_FMT_STR": %m", return log_link_warning_errno(link, r, "Failed to remove address "IPV4_ADDRESS_FMT_STR": %m",
IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
@ -29,30 +36,7 @@ static int static_address_on_stop(Link *link, Address *address) {
return 0; return 0;
} }
static int static_address_on_conflict(Link *link, Address *address) { static int dhcp4_address_on_conflict(Link *link, Address *address) {
int r;
assert(link);
assert(address);
if (address_get(link, address, NULL) < 0) {
log_link_warning(link, "Cannot configure requested address "IPV4_ADDRESS_FMT_STR", as an address conflict was detected.",
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
return 0;
}
log_link_warning(link, "Dropping address "IPV4_ADDRESS_FMT_STR", as an address conflict was detected.",
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
r = address_remove(address, link);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to remove address "IPV4_ADDRESS_FMT_STR": %m",
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
return 0;
}
static int dhcp4_address_on_conflict(Link *link) {
int r; int r;
assert(link); assert(link);
@ -71,11 +55,14 @@ static int dhcp4_address_on_conflict(Link *link) {
if (r < 0) if (r < 0)
return log_link_warning_errno(link, r, "Failed to drop DHCPv4 lease: %m"); return log_link_warning_errno(link, r, "Failed to drop DHCPv4 lease: %m");
/* make the address will be freed. */
address_cancel_probing(address);
/* It is not necessary to call address_remove() here, as dhcp4_lease_lost() removes it. */ /* It is not necessary to call address_remove() here, as dhcp4_lease_lost() removes it. */
return 0; return 0;
} }
static void on_acd(sd_ipv4acd *acd, int event, void *userdata, bool is_static) { static void on_acd(sd_ipv4acd *acd, int event, void *userdata) {
Address *address = userdata; Address *address = userdata;
Link *link; Link *link;
int r; int r;
@ -85,13 +72,14 @@ static void on_acd(sd_ipv4acd *acd, int event, void *userdata, bool is_static) {
assert(address->acd == acd); assert(address->acd == acd);
assert(address->link); assert(address->link);
assert(address->family == AF_INET); assert(address->family == AF_INET);
assert(IN_SET(address->source, NETWORK_CONFIG_SOURCE_STATIC, NETWORK_CONFIG_SOURCE_DHCP4));
link = address->link; link = address->link;
switch (event) { switch (event) {
case SD_IPV4ACD_EVENT_STOP: case SD_IPV4ACD_EVENT_STOP:
if (is_static) { if (address->source == NETWORK_CONFIG_SOURCE_STATIC) {
r = static_address_on_stop(link, address); r = static_ipv4acd_address_remove(link, address, /* on_conflict = */ false);
if (r < 0) if (r < 0)
link_enter_failed(link); link_enter_failed(link);
} }
@ -104,14 +92,17 @@ static void on_acd(sd_ipv4acd *acd, int event, void *userdata, bool is_static) {
log_link_debug(link, "Successfully claimed address "IPV4_ADDRESS_FMT_STR, log_link_debug(link, "Successfully claimed address "IPV4_ADDRESS_FMT_STR,
IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
address->acd_announced = true; address_cancel_probing(address);
break; break;
case SD_IPV4ACD_EVENT_CONFLICT: case SD_IPV4ACD_EVENT_CONFLICT:
if (is_static) log_link_warning(link, "Dropping address "IPV4_ADDRESS_FMT_STR", as an address conflict was detected.",
r = static_address_on_conflict(link, address); IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
if (address->source == NETWORK_CONFIG_SOURCE_STATIC)
r = static_ipv4acd_address_remove(link, address, /* on_conflict = */ true);
else else
r = dhcp4_address_on_conflict(link); r = dhcp4_address_on_conflict(link, address);
if (r < 0) if (r < 0)
link_enter_failed(link); link_enter_failed(link);
break; break;
@ -121,14 +112,6 @@ static void on_acd(sd_ipv4acd *acd, int event, void *userdata, bool is_static) {
} }
} }
static void static_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
on_acd(acd, event, userdata, true);
}
static void dhcp4_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
on_acd(acd, event, userdata, false);
}
static int ipv4acd_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata) { static int ipv4acd_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata) {
Manager *m = userdata; Manager *m = userdata;
struct hw_addr_data hw_addr; struct hw_addr_data hw_addr;
@ -144,27 +127,31 @@ static int ipv4acd_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void
return link_get_by_hw_addr(m, &hw_addr, NULL) >= 0; return link_get_by_hw_addr(m, &hw_addr, NULL) >= 0;
} }
static int ipv4acd_configure(Link *link, const Address *a) { int ipv4acd_configure(Address *address) {
_cleanup_(address_freep) Address *address = NULL; Link *link;
int r; int r;
assert(link); assert(address);
assert(a); assert(address->link);
assert(a->family == AF_INET);
if (address->family != AF_INET)
return 0;
if (!FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV4))
return 0;
/* Currently, only static and DHCP4 addresses are supported. */
assert(IN_SET(address->source, NETWORK_CONFIG_SOURCE_STATIC, NETWORK_CONFIG_SOURCE_DHCP4));
if (address->acd) {
address_enter_probing(address);
return 0;
}
link = address->link;
log_link_debug(link, "Configuring IPv4ACD for address "IPV4_ADDRESS_FMT_STR, log_link_debug(link, "Configuring IPv4ACD for address "IPV4_ADDRESS_FMT_STR,
IPV4_ADDRESS_FMT_VAL(a->in_addr.in)); IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
r = address_dup(a, &address);
if (r < 0)
return r;
r = set_ensure_put(&link->addresses_ipv4acd, &address_hash_ops, address);
if (r < 0)
return r;
if (r == 0)
return -EEXIST;
address->link = link;
r = sd_ipv4acd_new(&address->acd); r = sd_ipv4acd_new(&address->acd);
if (r < 0) if (r < 0)
@ -186,9 +173,7 @@ static int ipv4acd_configure(Link *link, const Address *a) {
if (r < 0) if (r < 0)
return r; return r;
r = sd_ipv4acd_set_callback(address->acd, r = sd_ipv4acd_set_callback(address->acd, on_acd, address);
address->is_static ? static_address_on_acd : dhcp4_address_on_acd,
address);
if (r < 0) if (r < 0)
return r; return r;
@ -202,36 +187,10 @@ static int ipv4acd_configure(Link *link, const Address *a) {
return r; return r;
} }
TAKE_PTR(address); address_enter_probing(address);
return 0; return 0;
} }
int ipv4acd_address_is_ready_to_configure(Link *link, const Address *address) {
Address *acd_address;
int r;
acd_address = set_get(link->addresses_ipv4acd, address);
if (!acd_address) {
r = ipv4acd_configure(link, address);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to configure IPv4ACD client: %m");
return false;
}
if (!acd_address->acd_announced)
return false;
r = set_ensure_put(&link->addresses, &address_hash_ops, acd_address);
if (r < 0)
return log_oom();
if (r == 0)
return log_link_warning_errno(link, SYNTHETIC_ERRNO(EEXIST), "Address already exists.");
acd_address->flags |= IFA_F_TENTATIVE;
return true;
}
int ipv4acd_update_mac(Link *link) { int ipv4acd_update_mac(Link *link) {
Address *address; Address *address;
int k, r = 0; int k, r = 0;
@ -243,8 +202,9 @@ int ipv4acd_update_mac(Link *link) {
if (ether_addr_is_null(&link->hw_addr.ether)) if (ether_addr_is_null(&link->hw_addr.ether))
return 0; return 0;
SET_FOREACH(address, link->addresses_ipv4acd) { SET_FOREACH(address, link->addresses) {
assert(address->acd); if (!address->acd)
continue;
k = sd_ipv4acd_set_mac(address->acd, &link->hw_addr.ether); k = sd_ipv4acd_set_mac(address->acd, &link->hw_addr.ether);
if (k < 0) if (k < 0)
@ -262,7 +222,10 @@ int ipv4acd_start(Link *link) {
assert(link); assert(link);
SET_FOREACH(address, link->addresses_ipv4acd) { SET_FOREACH(address, link->addresses) {
if (!address->acd)
continue;
if (sd_ipv4acd_is_running(address->acd)) if (sd_ipv4acd_is_running(address->acd))
continue; continue;
@ -280,7 +243,10 @@ int ipv4acd_stop(Link *link) {
assert(link); assert(link);
SET_FOREACH(address, link->addresses_ipv4acd) { SET_FOREACH(address, link->addresses) {
if (!address->acd)
continue;
k = sd_ipv4acd_stop(address->acd); k = sd_ipv4acd_stop(address->acd);
if (k < 0) if (k < 0)
r = k; r = k;
@ -295,7 +261,10 @@ int ipv4acd_set_ifname(Link *link) {
assert(link); assert(link);
SET_FOREACH(address, link->addresses_ipv4acd) { SET_FOREACH(address, link->addresses) {
if (!address->acd)
continue;
r = sd_ipv4acd_set_ifname(address->acd, link->ifname); r = sd_ipv4acd_set_ifname(address->acd, link->ifname);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -4,7 +4,7 @@
typedef struct Address Address; typedef struct Address Address;
typedef struct Link Link; typedef struct Link Link;
int ipv4acd_address_is_ready_to_configure(Link *link, const Address *address); int ipv4acd_configure(Address *address);
int ipv4acd_update_mac(Link *link); int ipv4acd_update_mac(Link *link);
int ipv4acd_start(Link *link); int ipv4acd_start(Link *link);
int ipv4acd_stop(Link *link); int ipv4acd_stop(Link *link);

View File

@ -28,6 +28,7 @@ static int address_new_from_ipv4ll(Link *link, Address **ret) {
if (r < 0) if (r < 0)
return -ENOMEM; return -ENOMEM;
address->source = NETWORK_CONFIG_SOURCE_IPV4LL;
address->family = AF_INET; address->family = AF_INET;
address->in_addr.in = addr; address->in_addr.in = addr;
address->prefixlen = 16; address->prefixlen = 16;
@ -41,6 +42,7 @@ static int address_new_from_ipv4ll(Link *link, Address **ret) {
static int ipv4ll_address_lost(Link *link) { static int ipv4ll_address_lost(Link *link) {
_cleanup_(address_freep) Address *address = NULL; _cleanup_(address_freep) Address *address = NULL;
Address *existing;
int r; int r;
assert(link); assert(link);
@ -53,10 +55,19 @@ static int ipv4ll_address_lost(Link *link) {
if (r < 0) if (r < 0)
return r; return r;
if (address_get(link, address, &existing) < 0)
return 0;
if (existing->source != NETWORK_CONFIG_SOURCE_IPV4LL)
return 0;
if (!address_exists(existing))
return 0;
log_link_debug(link, "IPv4 link-local release "IPV4_ADDRESS_FMT_STR, log_link_debug(link, "IPv4 link-local release "IPV4_ADDRESS_FMT_STR,
IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
return address_remove(address, link); return address_remove(existing);
} }
static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {

View File

@ -48,12 +48,12 @@
#include "networkd-nexthop.h" #include "networkd-nexthop.h"
#include "networkd-queue.h" #include "networkd-queue.h"
#include "networkd-radv.h" #include "networkd-radv.h"
#include "networkd-route.h"
#include "networkd-routing-policy-rule.h" #include "networkd-routing-policy-rule.h"
#include "networkd-setlink.h" #include "networkd-setlink.h"
#include "networkd-sriov.h" #include "networkd-sriov.h"
#include "networkd-state-file.h" #include "networkd-state-file.h"
#include "networkd-sysctl.h" #include "networkd-sysctl.h"
#include "networkd-wifi.h"
#include "set.h" #include "set.h"
#include "socket-util.h" #include "socket-util.h"
#include "stat-util.h" #include "stat-util.h"
@ -228,29 +228,12 @@ static Link *link_free(Link *link) {
link_dns_settings_clear(link); link_dns_settings_clear(link);
link->routes = set_free(link->routes); link->routes = set_free(link->routes);
link->routes_foreign = set_free(link->routes_foreign);
link->dhcp_routes = set_free(link->dhcp_routes);
link->dhcp_routes_old = set_free(link->dhcp_routes_old);
link->dhcp6_routes = set_free(link->dhcp6_routes);
link->dhcp6_routes_old = set_free(link->dhcp6_routes_old);
link->dhcp6_pd_routes = set_free(link->dhcp6_pd_routes);
link->dhcp6_pd_routes_old = set_free(link->dhcp6_pd_routes_old);
link->ndisc_routes = set_free(link->ndisc_routes);
link->nexthops = set_free(link->nexthops); link->nexthops = set_free(link->nexthops);
link->neighbors = set_free(link->neighbors); link->neighbors = set_free(link->neighbors);
link->addresses = set_free(link->addresses); link->addresses = set_free(link->addresses);
link->addresses_foreign = set_free(link->addresses_foreign);
link->addresses_ipv4acd = set_free(link->addresses_ipv4acd);
link->pool_addresses = set_free(link->pool_addresses);
link->static_addresses = set_free(link->static_addresses);
link->dhcp6_addresses = set_free(link->dhcp6_addresses);
link->dhcp6_addresses_old = set_free(link->dhcp6_addresses_old);
link->dhcp6_pd_addresses = set_free(link->dhcp6_pd_addresses);
link->dhcp6_pd_addresses_old = set_free(link->dhcp6_pd_addresses_old);
link->ndisc_addresses = set_free(link->ndisc_addresses);
link->dhcp6_pd_prefixes = set_free(link->dhcp6_pd_prefixes); link->dhcp6_pd_prefixes = set_free(link->dhcp6_pd_prefixes);
@ -395,7 +378,7 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) {
if (k < 0) if (k < 0)
r = log_link_warning_errno(link, k, "Could not stop DHCPv6 client: %m"); r = log_link_warning_errno(link, k, "Could not stop DHCPv6 client: %m");
k = dhcp6_pd_remove(link); k = dhcp6_pd_remove(link, /* only_marked = */ false);
if (k < 0) if (k < 0)
r = log_link_warning_errno(link, k, "Could not remove DHCPv6 PD addresses and routes: %m"); r = log_link_warning_errno(link, k, "Could not remove DHCPv6 PD addresses and routes: %m");
@ -460,7 +443,7 @@ void link_check_ready(Link *link) {
_cleanup_free_ char *str = NULL; _cleanup_free_ char *str = NULL;
(void) in_addr_prefix_to_string(a->family, &a->in_addr, a->prefixlen, &str); (void) in_addr_prefix_to_string(a->family, &a->in_addr, a->prefixlen, &str);
return (void) log_link_debug(link, "%s(): an address %s is not ready.", __func__, strna(str)); return (void) log_link_debug(link, "%s(): address %s is not ready.", __func__, strna(str));
} }
if (!link->static_address_labels_configured) if (!link->static_address_labels_configured)
@ -496,44 +479,42 @@ void link_check_ready(Link *link) {
!in6_addr_is_set(&link->ipv6ll_address)) !in6_addr_is_set(&link->ipv6ll_address))
return (void) log_link_debug(link, "%s(): IPv6LL is not configured yet.", __func__); return (void) log_link_debug(link, "%s(): IPv6LL is not configured yet.", __func__);
bool has_ndisc_address = false; bool has_dynamic_address = false;
NDiscAddress *n; SET_FOREACH(a, link->addresses) {
SET_FOREACH(n, link->ndisc_addresses) if (address_is_marked(a))
if (!n->marked) { continue;
has_ndisc_address = true; if (!address_exists(a))
continue;
if (IN_SET(a->source,
NETWORK_CONFIG_SOURCE_IPV4LL, NETWORK_CONFIG_SOURCE_DHCP4,
NETWORK_CONFIG_SOURCE_DHCP6, NETWORK_CONFIG_SOURCE_NDISC)) {
has_dynamic_address = true;
break; break;
} }
}
if ((link_dhcp4_enabled(link) || link_dhcp6_with_address_enabled(link) || link_ipv4ll_enabled(link)) && if ((link_ipv4ll_enabled(link) || link_dhcp4_enabled(link) || link_dhcp6_with_address_enabled(link) ||
!link->dhcp_address && set_isempty(link->dhcp6_addresses) && !has_ndisc_address && (link_dhcp6_pd_is_enabled(link) && link->network->dhcp6_pd_assign)) && !has_dynamic_address)
!link->ipv4ll_address_configured)
/* When DHCP[46] or IPv4LL is enabled, at least one address is acquired by them. */ /* When DHCP[46] or IPv4LL is enabled, at least one address is acquired by them. */
return (void) log_link_debug(link, "%s(): DHCPv4, DHCPv6 or IPv4LL is enabled but no dynamic address is assigned yet.", __func__); return (void) log_link_debug(link, "%s(): DHCPv4, DHCPv6 or IPv4LL is enabled but no dynamic address is assigned yet.", __func__);
/* Ignore NDisc when ConfigureWithoutCarrier= is enabled, as IPv6AcceptRA= is enabled by default. */ /* Ignore NDisc when ConfigureWithoutCarrier= is enabled, as IPv6AcceptRA= is enabled by default. */
if (link_dhcp4_enabled(link) || link_dhcp6_enabled(link) || link_dhcp6_pd_is_enabled(link) || if (link_ipv4ll_enabled(link) || link_dhcp4_enabled(link) ||
(!link->network->configure_without_carrier && link_ipv6_accept_ra_enabled(link)) || link_dhcp6_enabled(link) || link_dhcp6_pd_is_enabled(link) ||
link_ipv4ll_enabled(link)) { (!link->network->configure_without_carrier && link_ipv6_accept_ra_enabled(link))) {
if (!link->dhcp4_configured && if (!link->ipv4ll_address_configured && !link->dhcp4_configured &&
!(link->dhcp6_address_configured && link->dhcp6_route_configured) && !link->dhcp6_configured && !link->dhcp6_pd_configured && !link->ndisc_configured)
!(link->dhcp6_pd_address_configured && link->dhcp6_pd_route_configured) &&
!(link->ndisc_addresses_configured && link->ndisc_routes_configured) &&
!link->ipv4ll_address_configured)
/* When DHCP[46], NDisc, or IPv4LL is enabled, at least one protocol must be finished. */ /* When DHCP[46], NDisc, or IPv4LL is enabled, at least one protocol must be finished. */
return (void) log_link_debug(link, "%s(): dynamic addresses or routes are not configured.", __func__); return (void) log_link_debug(link, "%s(): dynamic addresses or routes are not configured.", __func__);
log_link_debug(link, "%s(): DHCPv4:%s IPv4LL:%s DHCPv6_addresses:%s DHCPv6_routes:%s " log_link_debug(link, "%s(): IPv4LL:%s DHCPv4:%s DHCPv6:%s DHCPv6PD:%s NDisc:%s",
"DHCPv6PD_addresses:%s DHCPv6PD_routes:%s NDisc_addresses:%s NDisc_routes:%s",
__func__, __func__,
yes_no(link->dhcp4_configured),
yes_no(link->ipv4ll_address_configured), yes_no(link->ipv4ll_address_configured),
yes_no(link->dhcp6_address_configured), yes_no(link->dhcp4_configured),
yes_no(link->dhcp6_route_configured), yes_no(link->dhcp6_configured),
yes_no(link->dhcp6_pd_address_configured), yes_no(link->dhcp6_pd_configured),
yes_no(link->dhcp6_pd_route_configured), yes_no(link->ndisc_configured));
yes_no(link->ndisc_addresses_configured),
yes_no(link->ndisc_routes_configured));
} }
link_set_state(link, LINK_STATE_CONFIGURED); link_set_state(link, LINK_STATE_CONFIGURED);
@ -983,7 +964,9 @@ static Link *link_drop(Link *link) {
link_drop_from_master(link); link_drop_from_master(link);
if (link->state_file)
(void) unlink(link->state_file); (void) unlink(link->state_file);
link_clean(link); link_clean(link);
STRV_FOREACH(n, link->alternative_names) STRV_FOREACH(n, link->alternative_names)
@ -1295,28 +1278,15 @@ static int link_reconfigure_impl(Link *link, bool force) {
return 1; return 1;
} }
static int link_reconfigure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, bool force, bool update_wifi) { static int link_reconfigure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, bool force) {
bool link_was_lower_up;
int r; int r;
assert(link); assert(link);
link_was_lower_up = link->flags & IFF_LOWER_UP;
r = link_getlink_handler_internal(rtnl, m, link, "Failed to update link state"); r = link_getlink_handler_internal(rtnl, m, link, "Failed to update link state");
if (r <= 0) if (r <= 0)
return r; return r;
if (update_wifi && link_was_lower_up && link->flags & IFF_LOWER_UP) {
/* If the interface's L1 was not up, then wifi_get_info() is already called in
* link_update_flags(). So, it is not necessary to re-call here. */
r = wifi_get_info(link);
if (r < 0) {
link_enter_failed(link);
return 0;
}
}
r = link_reconfigure_impl(link, force); r = link_reconfigure_impl(link, force);
if (r < 0) { if (r < 0) {
link_enter_failed(link); link_enter_failed(link);
@ -1327,11 +1297,11 @@ static int link_reconfigure_handler_internal(sd_netlink *rtnl, sd_netlink_messag
} }
static int link_reconfigure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { static int link_reconfigure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
return link_reconfigure_handler_internal(rtnl, m, link, /* force = */ false, /* update_wifi = */ false); return link_reconfigure_handler_internal(rtnl, m, link, /* force = */ false);
} }
static int link_force_reconfigure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { static int link_force_reconfigure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
return link_reconfigure_handler_internal(rtnl, m, link, /* force = */ true, /* update_wifi = */ false); return link_reconfigure_handler_internal(rtnl, m, link, /* force = */ true);
} }
static int link_reconfigure_after_sleep_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { static int link_reconfigure_after_sleep_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
@ -1339,7 +1309,7 @@ static int link_reconfigure_after_sleep_handler(sd_netlink *rtnl, sd_netlink_mes
assert(link); assert(link);
r = link_reconfigure_handler_internal(rtnl, m, link, /* force = */ false, /* update_wifi = */ true); r = link_reconfigure_handler_internal(rtnl, m, link, /* force = */ false);
if (r != 0) if (r != 0)
return r; return r;
@ -1404,6 +1374,12 @@ static int link_initialized_and_synced(Link *link) {
assert(link->ifname); assert(link->ifname);
assert(link->manager); assert(link->manager);
if (link->manager->test_mode) {
log_link_debug(link, "Running in test mode, refusing to enter initialized state.");
link_set_state(link, LINK_STATE_UNMANAGED);
return 0;
}
/* We may get called either from the asynchronous netlink callback, /* We may get called either from the asynchronous netlink callback,
* or directly from link_check_initialized() if running in a container. */ * or directly from link_check_initialized() if running in a container. */
if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_INITIALIZED)) if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_INITIALIZED))
@ -1421,10 +1397,6 @@ static int link_initialized_and_synced(Link *link) {
return r; return r;
if (!link->network) { if (!link->network) {
r = wifi_get_info(link);
if (r < 0)
return r;
r = link_get_network(link, &network); r = link_get_network(link, &network);
if (r == -ENOENT) { if (r == -ENOENT) {
link_set_state(link, LINK_STATE_UNMANAGED); link_set_state(link, LINK_STATE_UNMANAGED);
@ -1637,11 +1609,7 @@ static int link_carrier_lost(Link *link) {
return r; return r;
} }
r = link_drop_config(link); return link_drop_config(link);
if (r < 0)
return r;
return link_drop_foreign_config(link);
} }
static int link_admin_state_up(Link *link) { static int link_admin_state_up(Link *link) {
@ -1767,18 +1735,6 @@ void link_update_operstate(Link *link, bool also_update_master) {
ipv6_scope = MIN(ipv6_scope, address->scope); ipv6_scope = MIN(ipv6_scope, address->scope);
} }
/* for operstate we also take foreign addresses into account */
SET_FOREACH(address, link->addresses_foreign) {
if (!address_is_ready(address))
continue;
if (address->family == AF_INET)
ipv4_scope = MIN(ipv4_scope, address->scope);
if (address->family == AF_INET6)
ipv6_scope = MIN(ipv6_scope, address->scope);
}
ipv4_address_state = address_state_from_scope(ipv4_scope); ipv4_address_state = address_state_from_scope(ipv4_scope);
ipv6_address_state = address_state_from_scope(ipv6_scope); ipv6_address_state = address_state_from_scope(ipv6_scope);
address_state = address_state_from_scope(MIN(ipv4_scope, ipv6_scope)); address_state = address_state_from_scope(MIN(ipv4_scope, ipv6_scope));
@ -1893,7 +1849,7 @@ void link_update_operstate(Link *link, bool also_update_master) {
: "") : "")
static int link_update_flags(Link *link, sd_netlink_message *message) { static int link_update_flags(Link *link, sd_netlink_message *message) {
bool link_was_lower_up, link_was_admin_up, had_carrier; bool link_was_admin_up, had_carrier;
uint8_t operstate; uint8_t operstate;
unsigned flags; unsigned flags;
int r; int r;
@ -1955,7 +1911,6 @@ static int link_update_flags(Link *link, sd_netlink_message *message) {
log_link_debug(link, "Unknown link flags lost, ignoring: %#.5x", unknown_flags_removed); log_link_debug(link, "Unknown link flags lost, ignoring: %#.5x", unknown_flags_removed);
} }
link_was_lower_up = link->flags & IFF_LOWER_UP;
link_was_admin_up = link->flags & IFF_UP; link_was_admin_up = link->flags & IFF_UP;
had_carrier = link_has_carrier(link); had_carrier = link_has_carrier(link);
@ -1964,19 +1919,6 @@ static int link_update_flags(Link *link, sd_netlink_message *message) {
link_update_operstate(link, true); link_update_operstate(link, true);
if (!link_was_lower_up && (link->flags & IFF_LOWER_UP)) {
r = wifi_get_info(link);
if (r < 0)
return r;
if (r > 0) {
/* All link information is up-to-date. So, it is not necessary to call
* RTM_GETLINK netlink method again. */
r = link_reconfigure_impl(link, /* force = */ false);
if (r < 0)
return r;
}
}
if (!link_was_admin_up && (link->flags & IFF_UP)) { if (!link_was_admin_up && (link->flags & IFF_UP)) {
log_link_info(link, "Link UP"); log_link_info(link, "Link UP");
@ -1994,6 +1936,15 @@ static int link_update_flags(Link *link, sd_netlink_message *message) {
if (!had_carrier && link_has_carrier(link)) { if (!had_carrier && link_has_carrier(link)) {
log_link_info(link, "Gained carrier"); log_link_info(link, "Gained carrier");
if (IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) {
/* At this stage, both wlan and link information should be up-to-date. Hence,
* it is not necessary to call RTM_GETLINK, NL80211_CMD_GET_INTERFACE, or
* NL80211_CMD_GET_STATION commands, and simply call link_reconfigure_impl(). */
r = link_reconfigure_impl(link, /* force = */ false);
if (r < 0)
return r;
}
r = link_carrier_gained(link); r = link_carrier_gained(link);
if (r < 0) if (r < 0)
return r; return r;
@ -2381,6 +2332,8 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
return log_debug_errno(r, "rtnl: failed to exit IFLA_LINKINFO container: %m"); return log_debug_errno(r, "rtnl: failed to exit IFLA_LINKINFO container: %m");
} }
if (!manager->test_mode) {
/* Do not update state files when running in test mode. */
if (asprintf(&state_file, "/run/systemd/netif/links/%d", ifindex) < 0) if (asprintf(&state_file, "/run/systemd/netif/links/%d", ifindex) < 0)
return log_oom_debug(); return log_oom_debug();
@ -2389,6 +2342,7 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
if (asprintf(&lldp_file, "/run/systemd/netif/lldp/%d", ifindex) < 0) if (asprintf(&lldp_file, "/run/systemd/netif/lldp/%d", ifindex) < 0)
return log_oom_debug(); return log_oom_debug();
}
link = new(Link, 1); link = new(Link, 1);
if (!link) if (!link)

View File

@ -39,7 +39,6 @@ typedef enum LinkState {
typedef struct Manager Manager; typedef struct Manager Manager;
typedef struct Network Network; typedef struct Network Network;
typedef struct Address Address;
typedef struct DUID DUID; typedef struct DUID DUID;
typedef struct Link { typedef struct Link {
@ -92,8 +91,6 @@ typedef struct Link {
unsigned static_nexthop_messages; unsigned static_nexthop_messages;
unsigned static_route_messages; unsigned static_route_messages;
unsigned static_routing_policy_rule_messages; unsigned static_routing_policy_rule_messages;
unsigned address_remove_messages;
unsigned route_remove_messages;
unsigned tc_messages; unsigned tc_messages;
unsigned sr_iov_messages; unsigned sr_iov_messages;
unsigned set_link_messages; unsigned set_link_messages;
@ -102,19 +99,12 @@ typedef struct Link {
unsigned create_stacked_netdev_after_configured_messages; unsigned create_stacked_netdev_after_configured_messages;
Set *addresses; Set *addresses;
Set *addresses_foreign;
Set *addresses_ipv4acd;
Set *pool_addresses;
Set *static_addresses;
Set *neighbors; Set *neighbors;
Set *routes; Set *routes;
Set *routes_foreign;
Set *nexthops; Set *nexthops;
sd_dhcp_client *dhcp_client; sd_dhcp_client *dhcp_client;
sd_dhcp_lease *dhcp_lease; sd_dhcp_lease *dhcp_lease;
Address *dhcp_address, *dhcp_address_old;
Set *dhcp_routes, *dhcp_routes_old;
char *lease_file; char *lease_file;
unsigned dhcp4_messages; unsigned dhcp4_messages;
bool dhcp4_route_failed:1; bool dhcp4_route_failed:1;
@ -145,31 +135,18 @@ typedef struct Link {
sd_ndisc *ndisc; sd_ndisc *ndisc;
Set *ndisc_rdnss; Set *ndisc_rdnss;
Set *ndisc_dnssl; Set *ndisc_dnssl;
Set *ndisc_addresses; unsigned ndisc_messages;
Set *ndisc_routes; bool ndisc_configured:1;
unsigned ndisc_addresses_messages;
unsigned ndisc_routes_messages;
bool ndisc_addresses_configured:1;
bool ndisc_routes_configured:1;
sd_radv *radv; sd_radv *radv;
sd_dhcp6_client *dhcp6_client; sd_dhcp6_client *dhcp6_client;
sd_dhcp6_lease *dhcp6_lease; sd_dhcp6_lease *dhcp6_lease;
Set *dhcp6_addresses, *dhcp6_addresses_old;
Set *dhcp6_routes, *dhcp6_routes_old;
Set *dhcp6_pd_prefixes; Set *dhcp6_pd_prefixes;
Set *dhcp6_pd_addresses, *dhcp6_pd_addresses_old; unsigned dhcp6_messages;
Set *dhcp6_pd_routes, *dhcp6_pd_routes_old; unsigned dhcp6_pd_messages;
unsigned dhcp6_address_messages; bool dhcp6_configured:1;
unsigned dhcp6_route_messages; bool dhcp6_pd_configured:1;
unsigned dhcp6_pd_address_messages;
unsigned dhcp6_pd_route_messages;
bool dhcp6_address_configured:1;
bool dhcp6_route_configured:1;
bool dhcp6_pd_address_configured:1;
bool dhcp6_pd_route_configured:1;
bool dhcp6_pd_prefixes_assigned:1;
/* This is about LLDP reception */ /* This is about LLDP reception */
sd_lldp_rx *lldp_rx; sd_lldp_rx *lldp_rx;

View File

@ -114,7 +114,9 @@ int link_lldp_save(Link *link) {
int n = 0, r, i; int n = 0, r, i;
assert(link); assert(link);
assert(link->lldp_file);
if (isempty(link->lldp_file))
return 0; /* Do not update state file when running in test mode. */
if (!link->lldp_rx) { if (!link->lldp_rx) {
(void) unlink(link->lldp_file); (void) unlink(link->lldp_file);

View File

@ -6,6 +6,7 @@
#include <linux/if.h> #include <linux/if.h>
#include <linux/fib_rules.h> #include <linux/fib_rules.h>
#include <linux/nexthop.h> #include <linux/nexthop.h>
#include <linux/nl80211.h>
#include "sd-daemon.h" #include "sd-daemon.h"
#include "sd-netlink.h" #include "sd-netlink.h"
@ -26,6 +27,7 @@
#include "netlink-util.h" #include "netlink-util.h"
#include "network-internal.h" #include "network-internal.h"
#include "networkd-address-pool.h" #include "networkd-address-pool.h"
#include "networkd-address.h"
#include "networkd-dhcp-server-bus.h" #include "networkd-dhcp-server-bus.h"
#include "networkd-dhcp6.h" #include "networkd-dhcp6.h"
#include "networkd-link-bus.h" #include "networkd-link-bus.h"
@ -35,9 +37,11 @@
#include "networkd-network-bus.h" #include "networkd-network-bus.h"
#include "networkd-nexthop.h" #include "networkd-nexthop.h"
#include "networkd-queue.h" #include "networkd-queue.h"
#include "networkd-route.h"
#include "networkd-routing-policy-rule.h" #include "networkd-routing-policy-rule.h"
#include "networkd-speed-meter.h" #include "networkd-speed-meter.h"
#include "networkd-state-file.h" #include "networkd-state-file.h"
#include "networkd-wifi.h"
#include "ordered-set.h" #include "ordered-set.h"
#include "path-lookup.h" #include "path-lookup.h"
#include "path-util.h" #include "path-util.h"
@ -109,13 +113,11 @@ static int on_connected(sd_bus_message *message, void *userdata, sd_bus_error *r
return 0; return 0;
} }
int manager_connect_bus(Manager *m) { static int manager_connect_bus(Manager *m) {
int r; int r;
assert(m); assert(m);
assert(!m->bus);
if (m->bus)
return 0;
r = bus_open_system_watch_bind_with_description(&m->bus, "bus-api-network"); r = bus_open_system_watch_bind_with_description(&m->bus, "bus-api-network");
if (r < 0) if (r < 0)
@ -248,6 +250,16 @@ static int manager_connect_genl(Manager *m) {
if (r < 0) if (r < 0)
return r; return r;
r = genl_add_match(m->genl, NULL, NL80211_GENL_NAME, NL80211_MULTICAST_GROUP_CONFIG, 0,
&manager_genl_process_nl80211_config, NULL, m, "network-genl_process_nl80211_config");
if (r < 0 && r != -EOPNOTSUPP)
return r;
r = genl_add_match(m->genl, NULL, NL80211_GENL_NAME, NL80211_MULTICAST_GROUP_MLME, 0,
&manager_genl_process_nl80211_mlme, NULL, m, "network-genl_process_nl80211_mlme");
if (r < 0 && r != -EOPNOTSUPP)
return r;
return 0; return 0;
} }
@ -372,28 +384,10 @@ static int signal_restart_callback(sd_event_source *s, const struct signalfd_sig
return sd_event_exit(sd_event_source_get_event(s), 0); return sd_event_exit(sd_event_source_get_event(s), 0);
} }
int manager_new(Manager **ret) { int manager_setup(Manager *m) {
_cleanup_(manager_freep) Manager *m = NULL;
int r; int r;
m = new(Manager, 1); assert(m);
if (!m)
return -ENOMEM;
*m = (Manager) {
.speed_meter_interval_usec = SPEED_METER_DEFAULT_TIME_INTERVAL,
.online_state = _LINK_ONLINE_STATE_INVALID,
.manage_foreign_routes = true,
.manage_foreign_rules = true,
.ethtool_fd = -1,
.dhcp_duid.type = DUID_TYPE_EN,
.dhcp6_duid.type = DUID_TYPE_EN,
.duid_product_uuid.type = DUID_TYPE_UUID,
};
m->state_file = strdup("/run/systemd/netif/state");
if (!m->state_file)
return -ENOMEM;
r = sd_event_default(&m->event); r = sd_event_default(&m->event);
if (r < 0) if (r < 0)
@ -422,6 +416,13 @@ int manager_new(Manager **ret) {
if (r < 0) if (r < 0)
return r; return r;
if (m->test_mode)
return 0;
r = manager_connect_bus(m);
if (r < 0)
return r;
r = manager_connect_udev(m); r = manager_connect_udev(m);
if (r < 0) if (r < 0)
return r; return r;
@ -438,11 +439,36 @@ int manager_new(Manager **ret) {
if (r < 0) if (r < 0)
return r; return r;
*ret = TAKE_PTR(m); m->state_file = strdup("/run/systemd/netif/state");
if (!m->state_file)
return -ENOMEM;
return 0; return 0;
} }
int manager_new(Manager **ret, bool test_mode) {
_cleanup_(manager_freep) Manager *m = NULL;
m = new(Manager, 1);
if (!m)
return -ENOMEM;
*m = (Manager) {
.test_mode = test_mode,
.speed_meter_interval_usec = SPEED_METER_DEFAULT_TIME_INTERVAL,
.online_state = _LINK_ONLINE_STATE_INVALID,
.manage_foreign_routes = true,
.manage_foreign_rules = true,
.ethtool_fd = -1,
.dhcp_duid.type = DUID_TYPE_EN,
.dhcp6_duid.type = DUID_TYPE_EN,
.duid_product_uuid.type = DUID_TYPE_UUID,
};
*ret = TAKE_PTR(m);
return 0;
}
Manager* manager_free(Manager *m) { Manager* manager_free(Manager *m) {
Link *link; Link *link;
@ -456,9 +482,6 @@ Manager* manager_free(Manager *m) {
m->request_queue = ordered_set_free(m->request_queue); m->request_queue = ordered_set_free(m->request_queue);
m->dhcp6_prefixes = hashmap_free_with_destructor(m->dhcp6_prefixes, dhcp6_pd_free);
m->dhcp6_pd_prefixes = set_free_with_destructor(m->dhcp6_pd_prefixes, dhcp6_pd_free);
m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref); m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref);
m->links_by_name = hashmap_free(m->links_by_name); m->links_by_name = hashmap_free(m->links_by_name);
m->links_by_hw_addr = hashmap_free(m->links_by_hw_addr); m->links_by_hw_addr = hashmap_free(m->links_by_hw_addr);
@ -556,15 +579,15 @@ bool manager_should_reload(Manager *m) {
static int manager_enumerate_internal( static int manager_enumerate_internal(
Manager *m, Manager *m,
sd_netlink *nl,
sd_netlink_message *req, sd_netlink_message *req,
int (*process)(sd_netlink *, sd_netlink_message *, Manager *), int (*process)(sd_netlink *, sd_netlink_message *, Manager *)) {
const char *name) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *reply = NULL; _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *reply = NULL;
int r; int k, r;
assert(m); assert(m);
assert(m->rtnl); assert(nl);
assert(req); assert(req);
assert(process); assert(process);
@ -572,22 +595,14 @@ static int manager_enumerate_internal(
if (r < 0) if (r < 0)
return r; return r;
r = sd_netlink_call(m->rtnl, req, 0, &reply); r = sd_netlink_call(nl, req, 0, &reply);
if (r < 0) { if (r < 0)
if (name && (r == -EOPNOTSUPP || (r == -EINVAL && mac_selinux_enforcing()))) {
log_debug_errno(r, "%s are not supported by the kernel. Ignoring.", name);
return 0;
}
return r; return r;
}
for (sd_netlink_message *reply_one = reply; reply_one; reply_one = sd_netlink_message_next(reply_one)) { for (sd_netlink_message *reply_one = reply; reply_one; reply_one = sd_netlink_message_next(reply_one)) {
int k;
m->enumerating = true; m->enumerating = true;
k = process(m->rtnl, reply_one, m); k = process(nl, reply_one, m);
if (k < 0 && r >= 0) if (k < 0 && r >= 0)
r = k; r = k;
@ -608,7 +623,7 @@ static int manager_enumerate_links(Manager *m) {
if (r < 0) if (r < 0)
return r; return r;
return manager_enumerate_internal(m, req, manager_rtnl_process_link, NULL); return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_link);
} }
static int manager_enumerate_addresses(Manager *m) { static int manager_enumerate_addresses(Manager *m) {
@ -622,7 +637,7 @@ static int manager_enumerate_addresses(Manager *m) {
if (r < 0) if (r < 0)
return r; return r;
return manager_enumerate_internal(m, req, manager_rtnl_process_address, NULL); return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_address);
} }
static int manager_enumerate_neighbors(Manager *m) { static int manager_enumerate_neighbors(Manager *m) {
@ -636,7 +651,7 @@ static int manager_enumerate_neighbors(Manager *m) {
if (r < 0) if (r < 0)
return r; return r;
return manager_enumerate_internal(m, req, manager_rtnl_process_neighbor, NULL); return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_neighbor);
} }
static int manager_enumerate_routes(Manager *m) { static int manager_enumerate_routes(Manager *m) {
@ -653,7 +668,7 @@ static int manager_enumerate_routes(Manager *m) {
if (r < 0) if (r < 0)
return r; return r;
return manager_enumerate_internal(m, req, manager_rtnl_process_route, NULL); return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_route);
} }
static int manager_enumerate_rules(Manager *m) { static int manager_enumerate_rules(Manager *m) {
@ -670,7 +685,7 @@ static int manager_enumerate_rules(Manager *m) {
if (r < 0) if (r < 0)
return r; return r;
return manager_enumerate_internal(m, req, manager_rtnl_process_rule, "Routing policy rules"); return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_rule);
} }
static int manager_enumerate_nexthop(Manager *m) { static int manager_enumerate_nexthop(Manager *m) {
@ -684,7 +699,50 @@ static int manager_enumerate_nexthop(Manager *m) {
if (r < 0) if (r < 0)
return r; return r;
return manager_enumerate_internal(m, req, manager_rtnl_process_nexthop, "Nexthop rules"); return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_nexthop);
}
static int manager_enumerate_nl80211_config(Manager *m) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
assert(m);
assert(m->genl);
r = sd_genl_message_new(m->genl, NL80211_GENL_NAME, NL80211_CMD_GET_INTERFACE, &req);
if (r < 0)
return r;
return manager_enumerate_internal(m, m->genl, req, manager_genl_process_nl80211_config);
}
static int manager_enumerate_nl80211_mlme(Manager *m) {
Link *link;
int r;
assert(m);
assert(m->genl);
HASHMAP_FOREACH(link, m->links_by_index) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
if (link->wlan_iftype != NL80211_IFTYPE_STATION)
continue;
r = sd_genl_message_new(m->genl, NL80211_GENL_NAME, NL80211_CMD_GET_STATION, &req);
if (r < 0)
return r;
r = sd_netlink_message_append_u32(req, NL80211_ATTR_IFINDEX, link->ifindex);
if (r < 0)
return r;
r = manager_enumerate_internal(m, m->genl, req, manager_genl_process_nl80211_mlme);
if (r < 0)
return r;
}
return 0;
} }
int manager_enumerate(Manager *m) { int manager_enumerate(Manager *m) {
@ -702,18 +760,37 @@ int manager_enumerate(Manager *m) {
if (r < 0) if (r < 0)
return log_error_errno(r, "Could not enumerate neighbors: %m"); return log_error_errno(r, "Could not enumerate neighbors: %m");
/* NextHop support is added in kernel v5.3 (65ee00a9409f751188a8cdc0988167858eb4a536),
* and older kernels return -EOPNOTSUPP, or -EINVAL if SELinux is enabled. */
r = manager_enumerate_nexthop(m); r = manager_enumerate_nexthop(m);
if (r < 0) if (r == -EOPNOTSUPP || (r == -EINVAL && mac_selinux_enforcing()))
return log_error_errno(r, "Could not enumerate nexthop rules: %m"); log_debug_errno(r, "Could not enumerate nexthops, ignoring: %m");
else if (r < 0)
return log_error_errno(r, "Could not enumerate nexthops: %m");
r = manager_enumerate_routes(m); r = manager_enumerate_routes(m);
if (r < 0) if (r < 0)
return log_error_errno(r, "Could not enumerate routes: %m"); return log_error_errno(r, "Could not enumerate routes: %m");
/* If kernel is built with CONFIG_FIB_RULES=n, it returns -EOPNOTSUPP. */
r = manager_enumerate_rules(m); r = manager_enumerate_rules(m);
if (r < 0) if (r == -EOPNOTSUPP)
log_debug_errno(r, "Could not enumerate routing policy rules, ignoring: %m");
else if (r < 0)
return log_error_errno(r, "Could not enumerate routing policy rules: %m"); return log_error_errno(r, "Could not enumerate routing policy rules: %m");
r = manager_enumerate_nl80211_config(m);
if (r == -EOPNOTSUPP)
log_debug_errno(r, "Could not enumerate wireless LAN interfaces, ignoring: %m");
else if (r < 0)
return log_error_errno(r, "Could not enumerate wireless LAN interfaces: %m");
r = manager_enumerate_nl80211_mlme(m);
if (r == -EOPNOTSUPP)
log_debug_errno(r, "Could not enumerate wireless LAN stations, ignoring: %m");
else if (r < 0)
return log_error_errno(r, "Could not enumerate wireless LAN stations: %m");
return 0; return 0;
} }

View File

@ -28,6 +28,7 @@ struct Manager {
Hashmap *polkit_registry; Hashmap *polkit_registry;
int ethtool_fd; int ethtool_fd;
bool test_mode;
bool enumerating; bool enumerating;
bool dirty; bool dirty;
bool restarting; bool restarting;
@ -49,8 +50,6 @@ struct Manager {
Hashmap *links_by_hw_addr; Hashmap *links_by_hw_addr;
Hashmap *netdevs; Hashmap *netdevs;
OrderedHashmap *networks; OrderedHashmap *networks;
Hashmap *dhcp6_prefixes;
Set *dhcp6_pd_prefixes;
OrderedSet *address_pools; OrderedSet *address_pools;
usec_t network_dirs_ts_usec; usec_t network_dirs_ts_usec;
@ -96,10 +95,10 @@ struct Manager {
OrderedSet *request_queue; OrderedSet *request_queue;
}; };
int manager_new(Manager **ret); int manager_new(Manager **ret, bool test_mode);
Manager* manager_free(Manager *m); Manager* manager_free(Manager *m);
int manager_connect_bus(Manager *m); int manager_setup(Manager *m);
int manager_start(Manager *m); int manager_start(Manager *m);
int manager_load_config(Manager *m); int manager_load_config(Manager *m);

View File

@ -16,6 +16,7 @@
#include "networkd-manager.h" #include "networkd-manager.h"
#include "networkd-ndisc.h" #include "networkd-ndisc.h"
#include "networkd-queue.h" #include "networkd-queue.h"
#include "networkd-route.h"
#include "networkd-state-file.h" #include "networkd-state-file.h"
#include "string-table.h" #include "string-table.h"
#include "string-util.h" #include "string-util.h"
@ -96,112 +97,62 @@ void network_adjust_ipv6_accept_ra(Network *network) {
network->ndisc_deny_listed_route_prefix = set_free_free(network->ndisc_deny_listed_route_prefix); network->ndisc_deny_listed_route_prefix = set_free_free(network->ndisc_deny_listed_route_prefix);
} }
static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool force); static int ndisc_remove(Link *link, struct in6_addr *router) {
bool updated = false;
static int ndisc_address_callback(Address *address) {
struct in6_addr router = {};
NDiscAddress *n;
assert(address);
assert(address->link);
assert(address->family == AF_INET6);
SET_FOREACH(n, address->link->ndisc_addresses)
if (n->address == address) {
router = n->router;
break;
}
if (in6_addr_is_null(&router)) {
_cleanup_free_ char *buf = NULL;
(void) in6_addr_prefix_to_string(&address->in_addr.in6, address->prefixlen, &buf);
log_link_debug(address->link, "%s is called for %s, but it is already removed, ignoring.",
__func__, strna(buf));
return 0;
}
/* Make this called only once */
SET_FOREACH(n, address->link->ndisc_addresses)
if (in6_addr_equal(&n->router, &router))
n->address->callback = NULL;
return ndisc_remove_old_one(address->link, &router, true);
}
static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool force) {
NDiscAddress *na;
NDiscRoute *nr;
NDiscDNSSL *dnssl; NDiscDNSSL *dnssl;
NDiscRDNSS *rdnss; NDiscRDNSS *rdnss;
Address *address;
Route *route;
int k, r = 0; int k, r = 0;
bool updated = false;
assert(link); assert(link);
assert(router);
if (!force) { SET_FOREACH(route, link->routes) {
bool set_callback = false; if (route->source != NETWORK_CONFIG_SOURCE_NDISC)
continue;
if (!route_is_marked(route))
continue;
if (router && !in6_addr_equal(router, &route->provider.in6))
continue;
SET_FOREACH(na, link->ndisc_addresses) k = route_remove(route);
if (!na->marked && in6_addr_equal(&na->router, router)) {
set_callback = true;
break;
}
if (set_callback)
SET_FOREACH(na, link->ndisc_addresses)
if (!na->marked && address_is_ready(na->address)) {
set_callback = false;
break;
}
if (set_callback) {
SET_FOREACH(na, link->ndisc_addresses)
if (!na->marked && in6_addr_equal(&na->router, router))
na->address->callback = ndisc_address_callback;
if (DEBUG_LOGGING) {
_cleanup_free_ char *buf = NULL;
(void) in6_addr_to_string(router, &buf);
log_link_debug(link, "No SLAAC address obtained from %s is ready. "
"The old NDisc information will be removed later.",
strna(buf));
}
return 0;
}
}
if (DEBUG_LOGGING) {
_cleanup_free_ char *buf = NULL;
(void) in6_addr_to_string(router, &buf);
log_link_debug(link, "Removing old NDisc information obtained from %s.", strna(buf));
}
SET_FOREACH(na, link->ndisc_addresses)
if (na->marked && in6_addr_equal(&na->router, router)) {
k = address_remove(na->address, link);
if (k < 0) if (k < 0)
r = k; r = k;
route_cancel_request(route);
} }
SET_FOREACH(nr, link->ndisc_routes) SET_FOREACH(address, link->addresses) {
if (nr->marked && in6_addr_equal(&nr->router, router)) { if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
k = route_remove(nr->route, NULL, link); continue;
if (!address_is_marked(address))
continue;
if (router && !in6_addr_equal(router, &address->provider.in6))
continue;
k = address_remove(address);
if (k < 0) if (k < 0)
r = k; r = k;
address_cancel_request(address);
} }
SET_FOREACH(rdnss, link->ndisc_rdnss) SET_FOREACH(rdnss, link->ndisc_rdnss) {
if (rdnss->marked && in6_addr_equal(&rdnss->router, router)) { if (!rdnss->marked)
continue;
if (router && !in6_addr_equal(router, &rdnss->router))
continue;
free(set_remove(link->ndisc_rdnss, rdnss)); free(set_remove(link->ndisc_rdnss, rdnss));
updated = true; updated = true;
} }
SET_FOREACH(dnssl, link->ndisc_dnssl) SET_FOREACH(dnssl, link->ndisc_dnssl) {
if (dnssl->marked && in6_addr_equal(&dnssl->router, router)) { if (!dnssl->marked)
continue;
if (router && !in6_addr_equal(router, &dnssl->router))
continue;
free(set_remove(link->ndisc_dnssl, dnssl)); free(set_remove(link->ndisc_dnssl, dnssl));
updated = true; updated = true;
} }
@ -212,197 +163,100 @@ static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool
return r; return r;
} }
static int ndisc_remove_old(Link *link) { static int ndisc_check_ready(Link *link);
_cleanup_set_free_free_ Set *routers = NULL;
_cleanup_free_ struct in6_addr *router = NULL; static int ndisc_address_ready_callback(Address *address) {
struct in6_addr *a; Address *a;
NDiscAddress *na;
NDiscRoute *nr; assert(address);
NDiscDNSSL *dnssl; assert(address->link);
NDiscRDNSS *rdnss;
int k, r; SET_FOREACH(a, address->link->addresses)
if (a->source == NETWORK_CONFIG_SOURCE_NDISC)
a->callback = NULL;
return ndisc_check_ready(address->link);
}
static int ndisc_check_ready(Link *link) {
bool found = false, ready = false;
Address *address;
int r;
assert(link); assert(link);
if (link->ndisc_addresses_messages > 0 || if (link->ndisc_messages > 0) {
link->ndisc_routes_messages > 0) log_link_debug(link, "%s(): SLAAC addresses and routes are not set.", __func__);
return 0; return 0;
}
routers = set_new(&in6_addr_hash_ops); SET_FOREACH(address, link->addresses) {
if (!routers) if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
return -ENOMEM; continue;
SET_FOREACH(na, link->ndisc_addresses) found = true;
if (!set_contains(routers, &na->router)) {
router = newdup(struct in6_addr, &na->router, 1);
if (!router)
return -ENOMEM;
r = set_put(routers, router); if (address_is_ready(address)) {
ready = true;
break;
}
}
if (found && !ready) {
SET_FOREACH(address, link->addresses)
if (address->source == NETWORK_CONFIG_SOURCE_NDISC)
address->callback = ndisc_address_ready_callback;
log_link_debug(link, "%s(): no SLAAC address is ready.", __func__);
return 0;
}
link->ndisc_configured = true;
log_link_debug(link, "SLAAC addresses and routes set.");
r = ndisc_remove(link, NULL);
if (r < 0) if (r < 0)
return r; return r;
assert(r > 0); link_check_ready(link);
TAKE_PTR(router); return 0;
} }
SET_FOREACH(nr, link->ndisc_routes)
if (!set_contains(routers, &nr->router)) {
router = newdup(struct in6_addr, &nr->router, 1);
if (!router)
return -ENOMEM;
r = set_put(routers, router);
if (r < 0)
return r;
assert(r > 0);
TAKE_PTR(router);
}
SET_FOREACH(rdnss, link->ndisc_rdnss)
if (!set_contains(routers, &rdnss->router)) {
router = newdup(struct in6_addr, &rdnss->router, 1);
if (!router)
return -ENOMEM;
r = set_put(routers, router);
if (r < 0)
return r;
assert(r > 0);
TAKE_PTR(router);
}
SET_FOREACH(dnssl, link->ndisc_dnssl)
if (!set_contains(routers, &dnssl->router)) {
router = newdup(struct in6_addr, &dnssl->router, 1);
if (!router)
return -ENOMEM;
r = set_put(routers, router);
if (r < 0)
return r;
assert(r > 0);
TAKE_PTR(router);
}
r = 0;
SET_FOREACH(a, routers) {
k = ndisc_remove_old_one(link, a, false);
if (k < 0)
r = k;
}
return r;
}
static void ndisc_route_hash_func(const NDiscRoute *x, struct siphash *state) {
route_hash_func(x->route, state);
}
static int ndisc_route_compare_func(const NDiscRoute *a, const NDiscRoute *b) {
return route_compare_func(a->route, b->route);
}
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
ndisc_route_hash_ops,
NDiscRoute,
ndisc_route_hash_func,
ndisc_route_compare_func,
free);
static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r; int r;
assert(link); assert(link);
assert(link->ndisc_routes_messages > 0); assert(link->ndisc_messages > 0);
link->ndisc_routes_messages--; link->ndisc_messages--;
r = route_configure_handler_internal(rtnl, m, link, "Could not set NDisc route"); r = route_configure_handler_internal(rtnl, m, link, "Could not set NDisc route");
if (r <= 0) if (r <= 0)
return r; return r;
if (link->ndisc_routes_messages == 0) { r = ndisc_check_ready(link);
log_link_debug(link, "NDisc routes set."); if (r < 0)
link->ndisc_routes_configured = true;
r = ndisc_remove_old(link);
if (r < 0) {
link_enter_failed(link); link_enter_failed(link);
return 1;
}
link_check_ready(link);
}
return 1; return 1;
} }
static int ndisc_after_route_configure(Request *req, void *object) {
_cleanup_free_ NDiscRoute *nr = NULL;
NDiscRoute *nr_exist;
struct in6_addr router;
Route *route = object;
sd_ndisc_router *rt;
Link *link;
int r;
assert(req);
assert(req->link);
assert(req->type == REQUEST_TYPE_ROUTE);
assert(req->userdata);
assert(route);
link = req->link;
rt = req->userdata;
r = sd_ndisc_router_get_address(rt, &router);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get router address from RA: %m");
nr = new(NDiscRoute, 1);
if (!nr)
return log_oom();
*nr = (NDiscRoute) {
.router = router,
.route = route,
};
nr_exist = set_get(link->ndisc_routes, nr);
if (nr_exist) {
nr_exist->marked = false;
nr_exist->router = router;
return 0;
}
r = set_ensure_put(&link->ndisc_routes, &ndisc_route_hash_ops, nr);
if (r < 0)
return log_link_error_errno(link, r, "Failed to store NDisc SLAAC route: %m");
assert(r > 0);
TAKE_PTR(nr);
return 0;
}
static void ndisc_request_on_free(Request *req) {
assert(req);
sd_ndisc_router_unref(req->userdata);
}
static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) { static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) {
_cleanup_(route_freep) Route *route = in; _cleanup_(route_freep) Route *route = in;
Request *req; struct in6_addr router;
Route *existing;
int r; int r;
assert(route); assert(route);
assert(link); assert(link);
assert(rt); assert(rt);
r = sd_ndisc_router_get_address(rt, &router);
if (r < 0)
return r;
route->source = NETWORK_CONFIG_SOURCE_NDISC;
route->provider.in6 = router;
if (!route->table_set) if (!route->table_set)
route->table = link_get_ipv6_accept_ra_route_table(link); route->table = link_get_ipv6_accept_ra_route_table(link);
if (!route->priority_set) if (!route->priority_set)
@ -410,134 +264,58 @@ static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) {
if (!route->protocol_set) if (!route->protocol_set)
route->protocol = RTPROT_RA; route->protocol = RTPROT_RA;
r = link_has_route(link, route); if (route_get(NULL, link, route, &existing) < 0)
if (r < 0) link->ndisc_configured = false;
return r; else
if (r == 0) route_unmark(existing);
link->ndisc_routes_configured = false;
r = link_request_route(link, TAKE_PTR(route), true, &link->ndisc_routes_messages, return link_request_route(link, TAKE_PTR(route), true, &link->ndisc_messages,
ndisc_route_handler, &req); ndisc_route_handler, NULL);
if (r <= 0)
return r;
req->userdata = sd_ndisc_router_ref(rt);
req->after_configure = ndisc_after_route_configure;
req->on_free = ndisc_request_on_free;
return 0;
} }
static void ndisc_address_hash_func(const NDiscAddress *x, struct siphash *state) {
address_hash_func(x->address, state);
}
static int ndisc_address_compare_func(const NDiscAddress *a, const NDiscAddress *b) {
return address_compare_func(a->address, b->address);
}
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
ndisc_address_hash_ops,
NDiscAddress,
ndisc_address_hash_func,
ndisc_address_compare_func,
free);
static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r; int r;
assert(link); assert(link);
assert(link->ndisc_addresses_messages > 0); assert(link->ndisc_messages > 0);
link->ndisc_addresses_messages--; link->ndisc_messages--;
r = address_configure_handler_internal(rtnl, m, link, "Could not set NDisc address"); r = address_configure_handler_internal(rtnl, m, link, "Could not set NDisc address");
if (r <= 0) if (r <= 0)
return r; return r;
if (link->ndisc_addresses_messages == 0) { r = ndisc_check_ready(link);
log_link_debug(link, "NDisc SLAAC addresses set."); if (r < 0)
link->ndisc_addresses_configured = true;
r = ndisc_remove_old(link);
if (r < 0) {
link_enter_failed(link); link_enter_failed(link);
return 1;
}
}
return 1; return 1;
} }
static int ndisc_after_address_configure(Request *req, void *object) {
_cleanup_free_ NDiscAddress *na = NULL;
NDiscAddress *na_exist;
struct in6_addr router;
sd_ndisc_router *rt;
Address *address = object;
Link *link;
int r;
assert(req);
assert(req->link);
assert(req->type == REQUEST_TYPE_ADDRESS);
assert(req->userdata);
assert(address);
link = req->link;
rt = req->userdata;
r = sd_ndisc_router_get_address(rt, &router);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get router address from RA: %m");
na = new(NDiscAddress, 1);
if (!na)
return log_oom();
*na = (NDiscAddress) {
.router = router,
.address = address,
};
na_exist = set_get(link->ndisc_addresses, na);
if (na_exist) {
na_exist->marked = false;
na_exist->router = router;
return 0;
}
r = set_ensure_put(&link->ndisc_addresses, &ndisc_address_hash_ops, na);
if (r < 0)
return log_link_error_errno(link, r, "Failed to store NDisc SLAAC address: %m");
assert(r > 0);
TAKE_PTR(na);
return 0;
}
static int ndisc_request_address(Address *in, Link *link, sd_ndisc_router *rt) { static int ndisc_request_address(Address *in, Link *link, sd_ndisc_router *rt) {
_cleanup_(address_freep) Address *address = in; _cleanup_(address_freep) Address *address = in;
Request *req; struct in6_addr router;
Address *existing;
int r; int r;
assert(address); assert(address);
assert(link); assert(link);
assert(rt); assert(rt);
if (address_get(link, address, NULL) < 0) r = sd_ndisc_router_get_address(rt, &router);
link->ndisc_addresses_configured = false; if (r < 0)
r = link_request_address(link, TAKE_PTR(address), true, &link->ndisc_addresses_messages,
ndisc_address_handler, &req);
if (r <= 0)
return r; return r;
req->userdata = sd_ndisc_router_ref(rt); address->source = NETWORK_CONFIG_SOURCE_NDISC;
req->after_configure = ndisc_after_address_configure; address->provider.in6 = router;
req->on_free = ndisc_request_on_free;
return 0; if (address_get(link, address, &existing) < 0)
link->ndisc_configured = false;
else
address_unmark(existing);
return link_request_address(link, TAKE_PTR(address), true, &link->ndisc_messages,
ndisc_address_handler, NULL);
} }
static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
@ -987,7 +765,6 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
uint32_t lifetime; uint32_t lifetime;
const struct in6_addr *a; const struct in6_addr *a;
struct in6_addr router; struct in6_addr router;
NDiscRDNSS *rdnss;
usec_t time_now; usec_t time_now;
bool updated = false; bool updated = false;
int n, r; int n, r;
@ -1011,10 +788,6 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
if (n < 0) if (n < 0)
return log_link_error_errno(link, n, "Failed to get RDNSS addresses: %m"); return log_link_error_errno(link, n, "Failed to get RDNSS addresses: %m");
SET_FOREACH(rdnss, link->ndisc_rdnss)
if (in6_addr_equal(&rdnss->router, &router))
rdnss->marked = true;
if (lifetime == 0) if (lifetime == 0)
return 0; return 0;
@ -1025,7 +798,7 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
for (int j = 0; j < n; j++) { for (int j = 0; j < n; j++) {
_cleanup_free_ NDiscRDNSS *x = NULL; _cleanup_free_ NDiscRDNSS *x = NULL;
NDiscRDNSS d = { NDiscRDNSS *rdnss, d = {
.address = a[j], .address = a[j],
}; };
@ -1081,7 +854,6 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
struct in6_addr router; struct in6_addr router;
uint32_t lifetime; uint32_t lifetime;
usec_t time_now; usec_t time_now;
NDiscDNSSL *dnssl;
bool updated = false; bool updated = false;
char **j; char **j;
int r; int r;
@ -1105,10 +877,6 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
if (r < 0) if (r < 0)
return log_link_error_errno(link, r, "Failed to get DNSSL addresses: %m"); return log_link_error_errno(link, r, "Failed to get DNSSL addresses: %m");
SET_FOREACH(dnssl, link->ndisc_dnssl)
if (in6_addr_equal(&dnssl->router, &router))
dnssl->marked = true;
if (lifetime == 0) if (lifetime == 0)
return 0; return 0;
@ -1120,6 +888,7 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
STRV_FOREACH(j, l) { STRV_FOREACH(j, l) {
_cleanup_free_ NDiscDNSSL *s = NULL; _cleanup_free_ NDiscDNSSL *s = NULL;
NDiscDNSSL *dnssl;
s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*j) + 1); s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*j) + 1);
if (!s) if (!s)
@ -1242,11 +1011,28 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
} }
} }
static void ndisc_mark(Link *link, const struct in6_addr *router) {
NDiscRDNSS *rdnss;
NDiscDNSSL *dnssl;
assert(link);
assert(router);
link_mark_addresses(link, NETWORK_CONFIG_SOURCE_NDISC, router);
link_mark_routes(link, NETWORK_CONFIG_SOURCE_NDISC, router);
SET_FOREACH(rdnss, link->ndisc_rdnss)
if (in6_addr_equal(&rdnss->router, router))
rdnss->marked = true;
SET_FOREACH(dnssl, link->ndisc_dnssl)
if (in6_addr_equal(&dnssl->router, router))
dnssl->marked = true;
}
static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
struct in6_addr router; struct in6_addr router;
uint64_t flags; uint64_t flags;
NDiscAddress *na;
NDiscRoute *nr;
int r; int r;
assert(link); assert(link);
@ -1271,13 +1057,7 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
return 0; return 0;
} }
SET_FOREACH(na, link->ndisc_addresses) ndisc_mark(link, &router);
if (in6_addr_equal(&na->router, &router))
na->marked = true;
SET_FOREACH(nr, link->ndisc_routes)
if (in6_addr_equal(&nr->router, &router))
nr->marked = true;
r = sd_ndisc_router_get_flags(rt, &flags); r = sd_ndisc_router_get_flags(rt, &flags);
if (r < 0) if (r < 0)
@ -1307,21 +1087,16 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
if (r < 0) if (r < 0)
return r; return r;
if (link->ndisc_addresses_messages == 0) if (link->ndisc_messages == 0) {
link->ndisc_addresses_configured = true; link->ndisc_configured = true;
else
log_link_debug(link, "Setting SLAAC addresses.");
if (link->ndisc_routes_messages == 0) r = ndisc_remove(link, &router);
link->ndisc_routes_configured = true;
else
log_link_debug(link, "Setting NDisc routes.");
r = ndisc_remove_old(link);
if (r < 0) if (r < 0)
return r; return r;
} else
log_link_debug(link, "Setting SLAAC addresses and router.");
if (!link->ndisc_addresses_configured || !link->ndisc_routes_configured) if (!link->ndisc_configured)
link_set_state(link, LINK_STATE_CONFIGURING); link_set_state(link, LINK_STATE_CONFIGURING);
link_check_ready(link); link_check_ready(link);
@ -1349,9 +1124,8 @@ static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event_t event, sd_ndisc_router
case SD_NDISC_EVENT_TIMEOUT: case SD_NDISC_EVENT_TIMEOUT:
log_link_debug(link, "NDisc handler get timeout event"); log_link_debug(link, "NDisc handler get timeout event");
if (link->ndisc_addresses_messages == 0 && link->ndisc_routes_messages == 0) { if (link->ndisc_messages == 0) {
link->ndisc_addresses_configured = true; link->ndisc_configured = true;
link->ndisc_routes_configured = true;
link_check_ready(link); link_check_ready(link);
} }
break; break;
@ -1403,6 +1177,9 @@ int ndisc_start(Link *link) {
if (!link_has_carrier(link)) if (!link_has_carrier(link))
return 0; return 0;
if (in6_addr_is_null(&link->ipv6ll_address))
return 0;
log_link_debug(link, "Discovering IPv6 routers"); log_link_debug(link, "Discovering IPv6 routers");
return sd_ndisc_start(link->ndisc); return sd_ndisc_start(link->ndisc);
@ -1559,14 +1336,15 @@ int config_parse_address_generation_type(
return 0; return 0;
} }
DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_accept_ra_use_domains, dhcp_use_domains, DHCPUseDomains,
"Failed to parse UseDomains= setting");
DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_accept_ra_start_dhcp6_client, ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client,
"Failed to parse DHCPv6Client= setting");
static const char* const ipv6_accept_ra_start_dhcp6_client_table[_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX] = { static const char* const ipv6_accept_ra_start_dhcp6_client_table[_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX] = {
[IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO] = "no", [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO] = "no",
[IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS] = "always", [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS] = "always",
[IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES] = "yes", [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES] = "yes",
}; };
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client, IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES); DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client, IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES);
DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_accept_ra_use_domains, dhcp_use_domains, DHCPUseDomains,
"Failed to parse UseDomains= setting");
DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_accept_ra_start_dhcp6_client, ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client,
"Failed to parse DHCPv6Client= setting");

View File

@ -2,11 +2,11 @@
#pragma once #pragma once
#include "conf-parser.h" #include "conf-parser.h"
#include "networkd-address.h"
#include "networkd-link.h"
#include "networkd-route.h"
#include "time-util.h" #include "time-util.h"
typedef struct Link Link;
typedef struct Network Network;
typedef enum IPv6AcceptRAStartDHCP6Client { typedef enum IPv6AcceptRAStartDHCP6Client {
IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO, IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO,
IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS, IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS,
@ -15,20 +15,6 @@ typedef enum IPv6AcceptRAStartDHCP6Client {
_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_INVALID = -EINVAL, _IPV6_ACCEPT_RA_START_DHCP6_CLIENT_INVALID = -EINVAL,
} IPv6AcceptRAStartDHCP6Client; } IPv6AcceptRAStartDHCP6Client;
typedef struct NDiscAddress {
/* Used when GC'ing old DNS servers when configuration changes. */
bool marked;
struct in6_addr router;
Address *address;
} NDiscAddress;
typedef struct NDiscRoute {
/* Used when GC'ing old DNS servers when configuration changes. */
bool marked;
struct in6_addr router;
Route *route;
} NDiscRoute;
typedef struct NDiscRDNSS { typedef struct NDiscRDNSS {
/* Used when GC'ing old DNS servers when configuration changes. */ /* Used when GC'ing old DNS servers when configuration changes. */
bool marked; bool marked;
@ -61,6 +47,3 @@ void ndisc_flush(Link *link);
CONFIG_PARSER_PROTOTYPE(config_parse_address_generation_type); CONFIG_PARSER_PROTOTYPE(config_parse_address_generation_type);
CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_accept_ra_start_dhcp6_client); CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_accept_ra_start_dhcp6_client);
CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_accept_ra_use_domains); CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_accept_ra_use_domains);
const char* ipv6_accept_ra_start_dhcp6_client_to_string(IPv6AcceptRAStartDHCP6Client i) _const_;
IPv6AcceptRAStartDHCP6Client ipv6_accept_ra_start_dhcp6_client_from_string(const char *s) _pure_;

View File

@ -49,7 +49,7 @@ Match.PermanentMACAddress, config_parse_hwaddrs,
Match.Path, config_parse_match_strv, 0, offsetof(Network, match.path) Match.Path, config_parse_match_strv, 0, offsetof(Network, match.path)
Match.Driver, config_parse_match_strv, 0, offsetof(Network, match.driver) Match.Driver, config_parse_match_strv, 0, offsetof(Network, match.driver)
Match.Type, config_parse_match_strv, 0, offsetof(Network, match.iftype) Match.Type, config_parse_match_strv, 0, offsetof(Network, match.iftype)
Match.WLANInterfaceType, config_parse_match_strv, 0, offsetof(Network, match.wifi_iftype) Match.WLANInterfaceType, config_parse_match_strv, 0, offsetof(Network, match.wlan_iftype)
Match.SSID, config_parse_match_strv, 0, offsetof(Network, match.ssid) Match.SSID, config_parse_match_strv, 0, offsetof(Network, match.ssid)
Match.BSSID, config_parse_hwaddrs, 0, offsetof(Network, match.bssid) Match.BSSID, config_parse_hwaddrs, 0, offsetof(Network, match.bssid)
Match.Name, config_parse_match_ifnames, IFNAME_VALID_ALTERNATIVE, offsetof(Network, match.ifname) Match.Name, config_parse_match_ifnames, IFNAME_VALID_ALTERNATIVE, offsetof(Network, match.ifname)

View File

@ -28,6 +28,7 @@
#include "networkd-network.h" #include "networkd-network.h"
#include "networkd-nexthop.h" #include "networkd-nexthop.h"
#include "networkd-radv.h" #include "networkd-radv.h"
#include "networkd-route.h"
#include "networkd-routing-policy-rule.h" #include "networkd-routing-policy-rule.h"
#include "networkd-sriov.h" #include "networkd-sriov.h"
#include "parse-util.h" #include "parse-util.h"

View File

@ -743,8 +743,6 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) {
return false; return false;
if (nexthop_owned_by_link(nexthop)) { if (nexthop_owned_by_link(nexthop)) {
Link *l;
/* TODO: fdb nexthop does not require IFF_UP. The conditions below needs to be updated /* TODO: fdb nexthop does not require IFF_UP. The conditions below needs to be updated
* when fdb nexthop support is added. See rtm_to_nh_config() in net/ipv4/nexthop.c of * when fdb nexthop support is added. See rtm_to_nh_config() in net/ipv4/nexthop.c of
* kernel. */ * kernel. */
@ -752,13 +750,6 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) {
return false; return false;
if (!FLAGS_SET(link->flags, IFF_UP)) if (!FLAGS_SET(link->flags, IFF_UP))
return false; return false;
HASHMAP_FOREACH(l, link->manager->links_by_index) {
if (l->address_remove_messages > 0)
return false;
if (l->route_remove_messages > 0)
return false;
}
} }
/* All group members must be configured first. */ /* All group members must be configured first. */

View File

@ -70,9 +70,6 @@ static Request *request_free(Request *req) {
/* To prevent from triggering assertions in hash functions, remove this request before /* To prevent from triggering assertions in hash functions, remove this request before
* freeing object below. */ * freeing object below. */
ordered_set_remove(req->link->manager->request_queue, req); ordered_set_remove(req->link->manager->request_queue, req);
if (req->on_free)
/* on_free() may use object. So, let's call this earlier. */
req->on_free(req);
if (req->consume_object) if (req->consume_object)
request_free_object(req->type, req->object); request_free_object(req->type, req->object);
link_unref(req->link); link_unref(req->link);

View File

@ -15,11 +15,6 @@ typedef struct NextHop NextHop;
typedef struct Route Route; typedef struct Route Route;
typedef struct RoutingPolicyRule RoutingPolicyRule; typedef struct RoutingPolicyRule RoutingPolicyRule;
typedef struct Request Request;
typedef int (*request_after_configure_handler_t)(Request*, void*);
typedef void (*request_on_free_handler_t)(Request*);
typedef enum RequestType { typedef enum RequestType {
REQUEST_TYPE_ACTIVATE_LINK, REQUEST_TYPE_ACTIVATE_LINK,
REQUEST_TYPE_ADDRESS, REQUEST_TYPE_ADDRESS,
@ -63,8 +58,6 @@ typedef struct Request {
void *userdata; void *userdata;
unsigned *message_counter; unsigned *message_counter;
link_netlink_message_handler_t netlink_handler; link_netlink_message_handler_t netlink_handler;
request_after_configure_handler_t after_configure;
request_on_free_handler_t on_free;
} Request; } Request;
void request_drop(Request *req); void request_drop(Request *req);

View File

@ -7,11 +7,13 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include "dns-domain.h" #include "dns-domain.h"
#include "networkd-address.h"
#include "networkd-link.h" #include "networkd-link.h"
#include "networkd-manager.h" #include "networkd-manager.h"
#include "networkd-network.h" #include "networkd-network.h"
#include "networkd-queue.h" #include "networkd-queue.h"
#include "networkd-radv.h" #include "networkd-radv.h"
#include "networkd-route.h"
#include "parse-util.h" #include "parse-util.h"
#include "string-util.h" #include "string-util.h"
#include "string-table.h" #include "string-table.h"
@ -202,6 +204,54 @@ void network_adjust_radv(Network *network) {
} }
} }
static bool link_radv_enabled(Link *link) {
assert(link);
if (!link_ipv6ll_enabled(link))
return false;
return link->network->router_prefix_delegation;
}
int link_request_radv_addresses(Link *link) {
Prefix *p;
int r;
assert(link);
if (!link_radv_enabled(link))
return 0;
HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
_cleanup_(address_freep) Address *address = NULL;
if (!p->assign)
continue;
r = address_new(&address);
if (r < 0)
return log_oom();
r = sd_radv_prefix_get_prefix(p->radv_prefix, &address->in_addr.in6, &address->prefixlen);
if (r < 0)
return r;
r = generate_ipv6_eui_64_address(link, &address->in_addr.in6);
if (r < 0)
return r;
address->source = NETWORK_CONFIG_SOURCE_STATIC;
address->family = AF_INET6;
address->route_metric = p->route_metric;
r = link_request_static_address(link, TAKE_PTR(address), true);
if (r < 0)
return r;
}
return 0;
}
int config_parse_prefix( int config_parse_prefix(
const char *unit, const char *unit,
const char *filename, const char *filename,
@ -660,15 +710,6 @@ static int radv_find_uplink(Link *link, Link **ret) {
return 0; return 0;
} }
static bool link_radv_enabled(Link *link) {
assert(link);
if (!link_ipv6ll_enabled(link))
return false;
return link->network->router_prefix_delegation;
}
static int radv_configure(Link *link) { static int radv_configure(Link *link) {
uint16_t router_lifetime; uint16_t router_lifetime;
Link *uplink = NULL; Link *uplink = NULL;

View File

@ -51,6 +51,8 @@ void network_drop_invalid_prefixes(Network *network);
void network_drop_invalid_route_prefixes(Network *network); void network_drop_invalid_route_prefixes(Network *network);
void network_adjust_radv(Network *network); void network_adjust_radv(Network *network);
int link_request_radv_addresses(Link *link);
int radv_update_mac(Link *link); int radv_update_mac(Link *link);
int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len,
uint32_t lifetime_preferred, uint32_t lifetime_valid); uint32_t lifetime_preferred, uint32_t lifetime_valid);

File diff suppressed because it is too large Load Diff

View File

@ -16,11 +16,13 @@ typedef struct Network Network;
typedef struct Request Request; typedef struct Request Request;
typedef struct Route { typedef struct Route {
Network *network;
NetworkConfigSection *section;
Link *link; Link *link;
Manager *manager; Manager *manager;
Network *network;
NetworkConfigSection *section;
NetworkConfigSource source;
NetworkConfigState state;
union in_addr_union provider; /* DHCP server or router address */
int family; int family;
int gw_family; int gw_family;
@ -52,7 +54,6 @@ typedef struct Route {
bool protocol_set:1; bool protocol_set:1;
bool pref_set:1; bool pref_set:1;
bool gateway_from_dhcp_or_ra:1; bool gateway_from_dhcp_or_ra:1;
bool removing:1;
union in_addr_union gw; union in_addr_union gw;
union in_addr_union dst; union in_addr_union dst;
@ -66,7 +67,6 @@ typedef struct Route {
void route_hash_func(const Route *route, struct siphash *state); void route_hash_func(const Route *route, struct siphash *state);
int route_compare_func(const Route *a, const Route *b); int route_compare_func(const Route *a, const Route *b);
bool route_equal(const Route *r1, const Route *r2);
extern const struct hash_ops route_hash_ops; extern const struct hash_ops route_hash_ops;
int route_new(Route **ret); int route_new(Route **ret);
@ -75,15 +75,16 @@ DEFINE_NETWORK_SECTION_FUNCTIONS(Route, route_free);
int route_dup(const Route *src, Route **ret); int route_dup(const Route *src, Route **ret);
int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg); int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg);
int route_remove(const Route *route, Manager *manager, Link *link); int route_remove(Route *route);
int link_has_route(Link *link, const Route *route); int route_get(Manager *manager, Link *link, const Route *in, Route **ret);
int manager_find_uplink(Manager *m, int family, Link *exclude, Link **ret); int manager_find_uplink(Manager *m, int family, Link *exclude, Link **ret);
bool gateway_is_ready(Link *link, int onlink, int family, const union in_addr_union *gw); bool gateway_is_ready(Link *link, int onlink, int family, const union in_addr_union *gw);
int link_drop_routes(Link *link); int link_drop_routes(Link *link);
int link_drop_foreign_routes(Link *link); int link_drop_foreign_routes(Link *link);
void route_cancel_request(Route *route);
int link_request_route( int link_request_route(
Link *link, Link *link,
Route *route, Route *route,
@ -103,6 +104,9 @@ void network_drop_invalid_routes(Network *network);
int manager_get_route_table_from_string(const Manager *m, const char *table, uint32_t *ret); int manager_get_route_table_from_string(const Manager *m, const char *table, uint32_t *ret);
int manager_get_route_table_to_string(const Manager *m, uint32_t table, char **ret); int manager_get_route_table_to_string(const Manager *m, uint32_t table, char **ret);
DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(Route, route);
void link_mark_routes(Link *link, NetworkConfigSource source, const struct in6_addr *router);
CONFIG_PARSER_PROTOTYPE(config_parse_gateway); CONFIG_PARSER_PROTOTYPE(config_parse_gateway);
CONFIG_PARSER_PROTOTYPE(config_parse_preferred_src); CONFIG_PARSER_PROTOTYPE(config_parse_preferred_src);
CONFIG_PARSER_PROTOTYPE(config_parse_destination); CONFIG_PARSER_PROTOTYPE(config_parse_destination);

View File

@ -6,6 +6,7 @@
#include "missing_network.h" #include "missing_network.h"
#include "netlink-util.h" #include "netlink-util.h"
#include "networkd-address.h"
#include "networkd-can.h" #include "networkd-can.h"
#include "networkd-link.h" #include "networkd-link.h"
#include "networkd-manager.h" #include "networkd-manager.h"

View File

@ -119,7 +119,9 @@ int manager_save(Manager *m) {
int r; int r;
assert(m); assert(m);
assert(m->state_file);
if (isempty(m->state_file))
return 0; /* Do not update state file when running in test mode. */
HASHMAP_FOREACH(link, m->links_by_index) { HASHMAP_FOREACH(link, m->links_by_index) {
const struct in_addr *addresses; const struct in_addr *addresses;
@ -423,10 +425,11 @@ int link_save(Link *link) {
int r; int r;
assert(link); assert(link);
assert(link->state_file);
assert(link->lease_file);
assert(link->manager); assert(link->manager);
if (isempty(link->state_file))
return 0; /* Do not update state files when running in test mode. */
if (link->state == LINK_STATE_LINGER) if (link->state == LINK_STATE_LINGER)
return 0; return 0;

View File

@ -3,11 +3,7 @@
#include <net/ethernet.h> #include <net/ethernet.h>
#include <linux/nl80211.h> #include <linux/nl80211.h>
#include "sd-bus.h"
#include "bus-util.h"
#include "ether-addr-util.h" #include "ether-addr-util.h"
#include "netlink-internal.h"
#include "netlink-util.h" #include "netlink-util.h"
#include "networkd-link.h" #include "networkd-link.h"
#include "networkd-manager.h" #include "networkd-manager.h"
@ -15,56 +11,275 @@
#include "string-util.h" #include "string-util.h"
#include "wifi-util.h" #include "wifi-util.h"
int wifi_get_info(Link *link) { static int link_get_wlan_interface(Link *link) {
_cleanup_free_ char *ssid = NULL; _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
enum nl80211_iftype iftype;
bool updated = false;
const char *type;
int r; int r;
assert(link); assert(link);
if (!link->sd_device) r = sd_genl_message_new(link->manager->genl, NL80211_GENL_NAME, NL80211_CMD_GET_INTERFACE, &req);
return 0;
r = sd_device_get_devtype(link->sd_device, &type);
if (r == -ENOENT)
return 0;
else if (r < 0)
return r;
if (!streq(type, "wlan"))
return 0;
r = wifi_get_interface(link->manager->genl, link->ifindex, &iftype, &ssid);
if (r < 0) if (r < 0)
return r; return log_link_debug_errno(link, r, "Failed to create generic netlink message: %m");
if (r == 0)
iftype = link->wlan_iftype; /* Assume iftype is not changed. */
if (iftype == NL80211_IFTYPE_STATION) { r = sd_netlink_message_append_u32(req, NL80211_ATTR_IFINDEX, link->ifindex);
struct ether_addr bssid;
r = wifi_get_station(link->manager->genl, link->ifindex, &bssid);
if (r < 0) if (r < 0)
return r; return log_link_debug_errno(link, r, "Could not append NL80211_ATTR_IFINDEX attribute: %m");
updated = !ether_addr_equal(&link->bssid, &bssid); r = sd_netlink_call(link->manager->genl, req, 0, &reply);
link->bssid = bssid; if (r < 0)
return log_link_debug_errno(link, r, "Failed to request information about wlan interface: %m");
if (!reply) {
log_link_debug(link, "No reply received to request for information about wifi interface, ignoring.");
return 0;
} }
updated = updated || link->wlan_iftype != iftype; return manager_genl_process_nl80211_config(link->manager->genl, reply, link->manager);
link->wlan_iftype = iftype; }
updated = updated || !streq_ptr(link->ssid, ssid);
free_and_replace(link->ssid, ssid); int manager_genl_process_nl80211_config(sd_netlink *genl, sd_netlink_message *message, Manager *manager) {
_cleanup_free_ char *ssid = NULL;
uint32_t ifindex, wlan_iftype;
const char *family, *ifname;
uint8_t cmd;
size_t len;
Link *link;
int r;
assert(genl);
assert(message);
assert(manager);
if (sd_netlink_message_is_error(message)) {
r = sd_netlink_message_get_errno(message);
if (r < 0)
log_message_warning_errno(message, r, "nl80211: received error message, ignoring");
return 0;
}
r = sd_genl_message_get_family_name(genl, message, &family);
if (r < 0) {
log_debug_errno(r, "nl80211: failed to determine genl family, ignoring: %m");
return 0;
}
if (!streq(family, NL80211_GENL_NAME)) {
log_debug("nl80211: received message of unexpected genl family '%s', ignoring.", family);
return 0;
}
r = sd_genl_message_get_command(genl, message, &cmd);
if (r < 0) {
log_debug_errno(r, "nl80211: failed to determine genl message command, ignoring: %m");
return 0;
}
if (!IN_SET(cmd, NL80211_CMD_SET_INTERFACE, NL80211_CMD_NEW_INTERFACE, NL80211_CMD_DEL_INTERFACE)) {
log_debug("nl80211: ignoring nl80211 %s(%u) message.",
strna(nl80211_cmd_to_string(cmd)), cmd);
return 0;
}
r = sd_netlink_message_read_u32(message, NL80211_ATTR_IFINDEX, &ifindex);
if (r < 0) {
log_debug_errno(r, "nl80211: received %s(%u) message without valid ifindex, ignoring: %m",
strna(nl80211_cmd_to_string(cmd)), cmd);
return 0;
}
r = link_get_by_index(manager, ifindex, &link);
if (r < 0) {
log_debug_errno(r, "nl80211: received %s(%u) message for link '%"PRIu32"' we don't know about, ignoring.",
strna(nl80211_cmd_to_string(cmd)), cmd, ifindex);
return 0;
}
r = sd_netlink_message_read_string(message, NL80211_ATTR_IFNAME, &ifname);
if (r < 0) {
log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid interface name, ignoring: %m",
strna(nl80211_cmd_to_string(cmd)), cmd);
return 0;
}
if (!streq(ifname, link->ifname)) {
log_link_debug_errno(link, r, "nl80211: received %s(%u) message with invalid interface name '%s', ignoring: %m",
strna(nl80211_cmd_to_string(cmd)), cmd, ifname);
return 0;
}
r = sd_netlink_message_read_u32(message, NL80211_ATTR_IFTYPE, &wlan_iftype);
if (r < 0) {
log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid wlan interface type, ignoring: %m",
strna(nl80211_cmd_to_string(cmd)), cmd);
return 0;
}
r = sd_netlink_message_read_data_suffix0(message, NL80211_ATTR_SSID, &len, (void**) &ssid);
if (r < 0 && r != -ENODATA) {
log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid SSID, ignoring: %m",
strna(nl80211_cmd_to_string(cmd)), cmd);
return 0;
}
if (r >= 0) {
if (len == 0) {
log_link_debug(link, "nl80211: received SSID has zero length, ignoring the received SSID: %m");
ssid = mfree(ssid);
} else if (strlen_ptr(ssid) != len) {
log_link_debug(link, "nl80211: received SSID contains NUL character(s), ignoring the received SSID.");
ssid = mfree(ssid);
}
}
log_link_debug(link, "nl80211: received %s(%u) message: iftype=%s, ssid=%s",
strna(nl80211_cmd_to_string(cmd)), cmd,
strna(nl80211_iftype_to_string(wlan_iftype)), ssid);
switch(cmd) {
case NL80211_CMD_SET_INTERFACE:
case NL80211_CMD_NEW_INTERFACE:
link->wlan_iftype = wlan_iftype;
free_and_replace(link->ssid, ssid);
break;
case NL80211_CMD_DEL_INTERFACE:
link->wlan_iftype = NL80211_IFTYPE_UNSPECIFIED;
link->ssid = mfree(link->ssid);
break;
default:
assert_not_reached();
}
return 0;
}
int manager_genl_process_nl80211_mlme(sd_netlink *genl, sd_netlink_message *message, Manager *manager) {
const char *family;
uint32_t ifindex;
uint8_t cmd;
Link *link;
int r;
assert(genl);
assert(message);
assert(manager);
if (sd_netlink_message_is_error(message)) {
r = sd_netlink_message_get_errno(message);
if (r < 0)
log_message_warning_errno(message, r, "nl80211: received error message, ignoring");
return 0;
}
r = sd_genl_message_get_family_name(genl, message, &family);
if (r < 0) {
log_debug_errno(r, "nl80211: failed to determine genl family, ignoring: %m");
return 0;
}
if (!streq(family, NL80211_GENL_NAME)) {
log_debug("nl80211: Received message of unexpected genl family '%s', ignoring.", family);
return 0;
}
r = sd_genl_message_get_command(genl, message, &cmd);
if (r < 0) {
log_debug_errno(r, "nl80211: failed to determine genl message command, ignoring: %m");
return 0;
}
r = sd_netlink_message_read_u32(message, NL80211_ATTR_IFINDEX, &ifindex);
if (r < 0) {
log_debug_errno(r, "nl80211: received %s(%u) message without valid ifindex, ignoring: %m",
strna(nl80211_cmd_to_string(cmd)), cmd);
return 0;
}
r = link_get_by_index(manager, ifindex, &link);
if (r < 0) {
log_debug_errno(r, "nl80211: received %s(%u) message for link '%"PRIu32"' we don't know about, ignoring.",
strna(nl80211_cmd_to_string(cmd)), cmd, ifindex);
return 0;
}
switch(cmd) {
case NL80211_CMD_NEW_STATION:
case NL80211_CMD_DEL_STATION: {
struct ether_addr bssid;
r = sd_netlink_message_read_ether_addr(message, NL80211_ATTR_MAC, &bssid);
if (r < 0) {
log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid BSSID, ignoring: %m",
strna(nl80211_cmd_to_string(cmd)), cmd);
return 0;
}
log_link_debug(link, "nl80211: received %s(%u) message: bssid=%s",
strna(nl80211_cmd_to_string(cmd)), cmd, ETHER_ADDR_TO_STR(&bssid));
if (cmd == NL80211_CMD_DEL_STATION) {
link->bssid = ETHER_ADDR_NULL;
return 0;
}
link->bssid = bssid;
if (manager->enumerating &&
link->wlan_iftype == NL80211_IFTYPE_STATION && link->ssid)
log_link_info(link, "Connected WiFi access point: %s (%s)",
link->ssid, ETHER_ADDR_TO_STR(&link->bssid));
break;
}
case NL80211_CMD_CONNECT: {
struct ether_addr bssid;
uint16_t status_code;
r = sd_netlink_message_read_ether_addr(message, NL80211_ATTR_MAC, &bssid);
if (r < 0 && r != -ENODATA) {
log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid BSSID, ignoring: %m",
strna(nl80211_cmd_to_string(cmd)), cmd);
return 0;
}
r = sd_netlink_message_read_u16(message, NL80211_ATTR_STATUS_CODE, &status_code);
if (r < 0) {
log_link_debug_errno(link, r, "nl80211: received %s(%u) message without valid status code, ignoring: %m",
strna(nl80211_cmd_to_string(cmd)), cmd);
return 0;
}
log_link_debug(link, "nl80211: received %s(%u) message: status=%u, bssid=%s",
strna(nl80211_cmd_to_string(cmd)), cmd, status_code, ETHER_ADDR_TO_STR(&bssid));
if (status_code != 0)
return 0;
link->bssid = bssid;
if (!manager->enumerating) {
r = link_get_wlan_interface(link);
if (r < 0) {
log_link_warning_errno(link, r, "Failed to update wireless LAN interface: %m");
link_enter_failed(link);
return 0;
}
}
if (updated) {
if (link->wlan_iftype == NL80211_IFTYPE_STATION && link->ssid) if (link->wlan_iftype == NL80211_IFTYPE_STATION && link->ssid)
log_link_info(link, "Connected WiFi access point: %s (%s)", log_link_info(link, "Connected WiFi access point: %s (%s)",
link->ssid, ETHER_ADDR_TO_STR(&link->bssid)); link->ssid, ETHER_ADDR_TO_STR(&link->bssid));
break;
}
case NL80211_CMD_DISCONNECT:
log_link_debug(link, "nl80211: received %s(%u) message.",
strna(nl80211_cmd_to_string(cmd)), cmd);
return 1; /* Some information is updated. */ link->bssid = ETHER_ADDR_NULL;
link->ssid = mfree(link->ssid);
break;
default:
log_link_debug(link, "nl80211: received %s(%u) message.",
strna(nl80211_cmd_to_string(cmd)), cmd);
} }
return 0; /* No new information. */ return 0;
} }

View File

@ -1,8 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once #pragma once
#include "sd-bus.h" #include "sd-netlink.h"
typedef struct Link Link; typedef struct Manager Manager;
int wifi_get_info(Link *link); int manager_genl_process_nl80211_config(sd_netlink *genl, sd_netlink_message *message, Manager *manager);
int manager_genl_process_nl80211_mlme(sd_netlink *genl, sd_netlink_message *message, Manager *manager);

View File

@ -73,13 +73,13 @@ static int run(int argc, char *argv[]) {
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
r = manager_new(&m); r = manager_new(&m, /* test_mode = */ false);
if (r < 0) if (r < 0)
return log_error_errno(r, "Could not create manager: %m"); return log_error_errno(r, "Could not create manager: %m");
r = manager_connect_bus(m); r = manager_setup(m);
if (r < 0) if (r < 0)
return log_error_errno(r, "Could not connect to bus: %m"); return log_error_errno(r, "Could not setup manager: %m");
r = manager_parse_config_file(m); r = manager_parse_config_file(m);
if (r < 0) if (r < 0)

View File

@ -10,7 +10,9 @@
#include "ether-addr-util.h" #include "ether-addr-util.h"
#include "hostname-setup.h" #include "hostname-setup.h"
#include "network-internal.h" #include "network-internal.h"
#include "networkd-address.h"
#include "networkd-manager.h" #include "networkd-manager.h"
#include "networkd-route.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
#include "tests.h" #include "tests.h"
@ -168,6 +170,16 @@ static int test_load_config(Manager *manager) {
return 0; return 0;
} }
static bool address_equal(const Address *a1, const Address *a2) {
if (a1 == a2)
return true;
if (!a1 || !a2)
return false;
return address_compare_func(a1, a2) == 0;
}
static void test_address_equality(void) { static void test_address_equality(void) {
_cleanup_(address_freep) Address *a1 = NULL, *a2 = NULL; _cleanup_(address_freep) Address *a1 = NULL, *a2 = NULL;
@ -269,7 +281,8 @@ int main(void) {
test_address_equality(); test_address_equality();
test_dhcp_hostname_shorten_overlong(); test_dhcp_hostname_shorten_overlong();
assert_se(manager_new(&manager) >= 0); assert_se(manager_new(&manager, /* test_mode = */ true) >= 0);
assert_se(manager_setup(manager) >= 0);
test_route_tables(manager); test_route_tables(manager);

View File

@ -4,6 +4,7 @@
#include "log.h" #include "log.h"
#include "macro.h" #include "macro.h"
#include "net-condition.h" #include "net-condition.h"
#include "networkd-address.h"
#include "networkd-conf.h" #include "networkd-conf.h"
#include "networkd-network.h" #include "networkd-network.h"
#include "strv.h" #include "strv.h"

View File

@ -39,8 +39,8 @@ static int resolvconf_help(void) {
"This is a compatibility alias for the resolvectl(1) tool, providing native\n" "This is a compatibility alias for the resolvectl(1) tool, providing native\n"
"command line compatibility with the resolvconf(8) tool of various Linux\n" "command line compatibility with the resolvconf(8) tool of various Linux\n"
"distributions and BSD systems. Some options supported by other implementations\n" "distributions and BSD systems. Some options supported by other implementations\n"
"are not supported and are ignored: -m, -p. Various options supported by other\n" "are not supported and are ignored: -m, -p, -u. Various options supported by other\n"
"implementations are not supported and will cause the invocation to fail: -u,\n" "implementations are not supported and will cause the invocation to fail:\n"
"-I, -i, -l, -R, -r, -v, -V, --enable-updates, --disable-updates,\n" "-I, -i, -l, -R, -r, -v, -V, --enable-updates, --disable-updates,\n"
"--updates-are-enabled.\n" "--updates-are-enabled.\n"
"\nSee the %2$s for details.\n", "\nSee the %2$s for details.\n",
@ -171,8 +171,11 @@ int resolvconf_parse_argv(int argc, char *argv[]) {
log_debug("Switch -%c ignored.", c); log_debug("Switch -%c ignored.", c);
break; break;
/* Everybody else can agree on the existence of -u but we don't support it. */ /* -u supposedly should "update all subscribers". We have no subscribers, hence let's make
this a NOP, and exit immediately, cleanly. */
case 'u': case 'u':
log_info("Switch -%c ignored.", c);
return 0;
/* The following options are openresolv inventions we don't support. */ /* The following options are openresolv inventions we don't support. */
case 'I': case 'I':

View File

@ -10,6 +10,7 @@
#include "socket-util.h" #include "socket-util.h"
#include "string-table.h" #include "string-table.h"
#include "strv.h" #include "strv.h"
#include "wifi-util.h"
void net_match_clear(NetMatch *match) { void net_match_clear(NetMatch *match) {
if (!match) if (!match)
@ -22,7 +23,7 @@ void net_match_clear(NetMatch *match) {
match->iftype = strv_free(match->iftype); match->iftype = strv_free(match->iftype);
match->ifname = strv_free(match->ifname); match->ifname = strv_free(match->ifname);
match->property = strv_free(match->property); match->property = strv_free(match->property);
match->wifi_iftype = strv_free(match->wifi_iftype); match->wlan_iftype = strv_free(match->wlan_iftype);
match->ssid = strv_free(match->ssid); match->ssid = strv_free(match->ssid);
match->bssid = set_free_free(match->bssid); match->bssid = set_free_free(match->bssid);
} }
@ -38,7 +39,7 @@ bool net_match_is_empty(const NetMatch *match) {
strv_isempty(match->iftype) && strv_isempty(match->iftype) &&
strv_isempty(match->ifname) && strv_isempty(match->ifname) &&
strv_isempty(match->property) && strv_isempty(match->property) &&
strv_isempty(match->wifi_iftype) && strv_isempty(match->wlan_iftype) &&
strv_isempty(match->ssid) && strv_isempty(match->ssid) &&
set_isempty(match->bssid); set_isempty(match->bssid);
} }
@ -117,23 +118,6 @@ static int net_condition_test_property(char * const *match_property, sd_device *
return true; return true;
} }
static const char *const wifi_iftype_table[NL80211_IFTYPE_MAX+1] = {
[NL80211_IFTYPE_ADHOC] = "ad-hoc",
[NL80211_IFTYPE_STATION] = "station",
[NL80211_IFTYPE_AP] = "ap",
[NL80211_IFTYPE_AP_VLAN] = "ap-vlan",
[NL80211_IFTYPE_WDS] = "wds",
[NL80211_IFTYPE_MONITOR] = "monitor",
[NL80211_IFTYPE_MESH_POINT] = "mesh-point",
[NL80211_IFTYPE_P2P_CLIENT] = "p2p-client",
[NL80211_IFTYPE_P2P_GO] = "p2p-go",
[NL80211_IFTYPE_P2P_DEVICE] = "p2p-device",
[NL80211_IFTYPE_OCB] = "ocb",
[NL80211_IFTYPE_NAN] = "nan",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(wifi_iftype, enum nl80211_iftype);
int net_match_config( int net_match_config(
const NetMatch *match, const NetMatch *match,
sd_device *device, sd_device *device,
@ -143,7 +127,7 @@ int net_match_config(
unsigned short iftype, unsigned short iftype,
const char *ifname, const char *ifname,
char * const *alternative_names, char * const *alternative_names,
enum nl80211_iftype wifi_iftype, enum nl80211_iftype wlan_iftype,
const char *ssid, const char *ssid,
const struct ether_addr *bssid) { const struct ether_addr *bssid) {
@ -194,7 +178,7 @@ int net_match_config(
if (!net_condition_test_property(match->property, device)) if (!net_condition_test_property(match->property, device))
return false; return false;
if (!net_condition_test_strv(match->wifi_iftype, wifi_iftype_to_string(wifi_iftype))) if (!net_condition_test_strv(match->wlan_iftype, nl80211_iftype_to_string(wlan_iftype)))
return false; return false;
if (!net_condition_test_strv(match->ssid, ssid)) if (!net_condition_test_strv(match->ssid, ssid))

View File

@ -18,7 +18,7 @@ typedef struct NetMatch {
char **iftype; char **iftype;
char **ifname; char **ifname;
char **property; char **property;
char **wifi_iftype; char **wlan_iftype;
char **ssid; char **ssid;
Set *bssid; Set *bssid;
} NetMatch; } NetMatch;
@ -35,7 +35,7 @@ int net_match_config(
unsigned short iftype, unsigned short iftype,
const char *ifname, const char *ifname,
char * const *alternative_names, char * const *alternative_names,
enum nl80211_iftype wifi_iftype, enum nl80211_iftype wlan_iftype,
const char *ssid, const char *ssid,
const struct ether_addr *bssid); const struct ether_addr *bssid);

View File

@ -1,12 +1,16 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "log.h" #include "log.h"
#include "string-table.h"
#include "string-util.h" #include "string-util.h"
#include "wifi-util.h" #include "wifi-util.h"
int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftype, char **ssid) { int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *ret_iftype, char **ret_ssid) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *reply = NULL; _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *reply = NULL;
_cleanup_free_ char *ssid = NULL;
const char *family; const char *family;
uint32_t iftype;
size_t len;
int r; int r;
assert(genl); assert(genl);
@ -47,41 +51,47 @@ int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftyp
goto nodata; goto nodata;
} }
if (iftype) { r = sd_netlink_message_read_u32(reply, NL80211_ATTR_IFTYPE, &iftype);
uint32_t t;
r = sd_netlink_message_read_u32(reply, NL80211_ATTR_IFTYPE, &t);
if (r < 0) if (r < 0)
return log_debug_errno(r, "Failed to get NL80211_ATTR_IFTYPE attribute: %m"); return log_debug_errno(r, "Failed to get NL80211_ATTR_IFTYPE attribute: %m");
*iftype = t;
r = sd_netlink_message_read_data_suffix0(reply, NL80211_ATTR_SSID, &len, (void**) &ssid);
if (r < 0 && r != -ENODATA)
return log_debug_errno(r, "Failed to get NL80211_ATTR_SSID attribute: %m");
if (r >= 0) {
if (len == 0) {
log_debug("SSID has zero length, ignoring the received SSID.");
ssid = mfree(ssid);
} else if (strlen_ptr(ssid) != len) {
log_debug("SSID contains NUL character(s), ignoring the received SSID.");
ssid = mfree(ssid);
}
} }
if (ssid) { if (ret_iftype)
r = sd_netlink_message_read_string_strdup(reply, NL80211_ATTR_SSID, ssid); *ret_iftype = iftype;
if (r == -ENODATA)
*ssid = NULL; if (ret_ssid)
else if (r < 0) *ret_ssid = TAKE_PTR(ssid);
return log_debug_errno(r, "Failed to get NL80211_ATTR_SSID attribute: %m");
}
return 1; return 1;
nodata: nodata:
if (iftype) if (ret_iftype)
*iftype = 0; *ret_iftype = 0;
if (ssid) if (ret_ssid)
*ssid = NULL; *ret_ssid = NULL;
return 0; return 0;
} }
int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) { int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *ret_bssid) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *reply = NULL; _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL, *reply = NULL;
const char *family; const char *family;
int r; int r;
assert(genl); assert(genl);
assert(ifindex > 0); assert(ifindex > 0);
assert(bssid); assert(ret_bssid);
r = sd_genl_message_new(genl, NL80211_GENL_NAME, NL80211_CMD_GET_STATION, &m); r = sd_genl_message_new(genl, NL80211_GENL_NAME, NL80211_CMD_GET_STATION, &m);
if (r < 0) if (r < 0)
@ -115,7 +125,7 @@ int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) {
goto nodata; goto nodata;
} }
r = sd_netlink_message_read_ether_addr(reply, NL80211_ATTR_MAC, bssid); r = sd_netlink_message_read_ether_addr(reply, NL80211_ATTR_MAC, ret_bssid);
if (r == -ENODATA) if (r == -ENODATA)
goto nodata; goto nodata;
if (r < 0) if (r < 0)
@ -124,6 +134,173 @@ int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) {
return 1; return 1;
nodata: nodata:
*bssid = (struct ether_addr) {}; *ret_bssid = ETHER_ADDR_NULL;
return 0; return 0;
} }
static const char * const nl80211_iftype_table[NUM_NL80211_IFTYPES] = {
[NL80211_IFTYPE_ADHOC] = "ad-hoc",
[NL80211_IFTYPE_STATION] = "station",
[NL80211_IFTYPE_AP] = "ap",
[NL80211_IFTYPE_AP_VLAN] = "ap-vlan",
[NL80211_IFTYPE_WDS] = "wds",
[NL80211_IFTYPE_MONITOR] = "monitor",
[NL80211_IFTYPE_MESH_POINT] = "mesh-point",
[NL80211_IFTYPE_P2P_CLIENT] = "p2p-client",
[NL80211_IFTYPE_P2P_GO] = "p2p-go",
[NL80211_IFTYPE_P2P_DEVICE] = "p2p-device",
[NL80211_IFTYPE_OCB] = "ocb",
[NL80211_IFTYPE_NAN] = "nan",
};
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(nl80211_iftype, enum nl80211_iftype);
static const char * const nl80211_cmd_table[__NL80211_CMD_AFTER_LAST] = {
[NL80211_CMD_GET_WIPHY] = "get_wiphy",
[NL80211_CMD_SET_WIPHY] = "set_wiphy",
[NL80211_CMD_NEW_WIPHY] = "new_wiphy",
[NL80211_CMD_DEL_WIPHY] = "del_wiphy",
[NL80211_CMD_GET_INTERFACE] = "get_interface",
[NL80211_CMD_SET_INTERFACE] = "set_interface",
[NL80211_CMD_NEW_INTERFACE] = "new_interface",
[NL80211_CMD_DEL_INTERFACE] = "del_interface",
[NL80211_CMD_GET_KEY] = "get_key",
[NL80211_CMD_SET_KEY] = "set_key",
[NL80211_CMD_NEW_KEY] = "new_key",
[NL80211_CMD_DEL_KEY] = "del_key",
[NL80211_CMD_GET_BEACON] = "get_beacon",
[NL80211_CMD_SET_BEACON] = "set_beacon",
[NL80211_CMD_START_AP] = "start_ap",
[NL80211_CMD_STOP_AP] = "stop_ap",
[NL80211_CMD_GET_STATION] = "get_station",
[NL80211_CMD_SET_STATION] = "set_station",
[NL80211_CMD_NEW_STATION] = "new_station",
[NL80211_CMD_DEL_STATION] = "del_station",
[NL80211_CMD_GET_MPATH] = "get_mpath",
[NL80211_CMD_SET_MPATH] = "set_mpath",
[NL80211_CMD_NEW_MPATH] = "new_mpath",
[NL80211_CMD_DEL_MPATH] = "del_mpath",
[NL80211_CMD_SET_BSS] = "set_bss",
[NL80211_CMD_SET_REG] = "set_reg",
[NL80211_CMD_REQ_SET_REG] = "req_set_reg",
[NL80211_CMD_GET_MESH_CONFIG] = "get_mesh_config",
[NL80211_CMD_SET_MESH_CONFIG] = "set_mesh_config",
[NL80211_CMD_SET_MGMT_EXTRA_IE] = "set_mgmt_extra_ie",
[NL80211_CMD_GET_REG] = "get_reg",
[NL80211_CMD_GET_SCAN] = "get_scan",
[NL80211_CMD_TRIGGER_SCAN] = "trigger_scan",
[NL80211_CMD_NEW_SCAN_RESULTS] = "new_scan_results",
[NL80211_CMD_SCAN_ABORTED] = "scan_aborted",
[NL80211_CMD_REG_CHANGE] = "reg_change",
[NL80211_CMD_AUTHENTICATE] = "authenticate",
[NL80211_CMD_ASSOCIATE] = "associate",
[NL80211_CMD_DEAUTHENTICATE] = "deauthenticate",
[NL80211_CMD_DISASSOCIATE] = "disassociate",
[NL80211_CMD_MICHAEL_MIC_FAILURE] = "michael_mic_failure",
[NL80211_CMD_REG_BEACON_HINT] = "reg_beacon_hint",
[NL80211_CMD_JOIN_IBSS] = "join_ibss",
[NL80211_CMD_LEAVE_IBSS] = "leave_ibss",
[NL80211_CMD_TESTMODE] = "testmode",
[NL80211_CMD_CONNECT] = "connect",
[NL80211_CMD_ROAM] = "roam",
[NL80211_CMD_DISCONNECT] = "disconnect",
[NL80211_CMD_SET_WIPHY_NETNS] = "set_wiphy_netns",
[NL80211_CMD_GET_SURVEY] = "get_survey",
[NL80211_CMD_NEW_SURVEY_RESULTS] = "new_survey_results",
[NL80211_CMD_SET_PMKSA] = "set_pmksa",
[NL80211_CMD_DEL_PMKSA] = "del_pmksa",
[NL80211_CMD_FLUSH_PMKSA] = "flush_pmksa",
[NL80211_CMD_REMAIN_ON_CHANNEL] = "remain_on_channel",
[NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL] = "cancel_remain_on_channel",
[NL80211_CMD_SET_TX_BITRATE_MASK] = "set_tx_bitrate_mask",
[NL80211_CMD_REGISTER_FRAME] = "register_frame",
[NL80211_CMD_FRAME] = "frame",
[NL80211_CMD_FRAME_TX_STATUS] = "frame_tx_status",
[NL80211_CMD_SET_POWER_SAVE] = "set_power_save",
[NL80211_CMD_GET_POWER_SAVE] = "get_power_save",
[NL80211_CMD_SET_CQM] = "set_cqm",
[NL80211_CMD_NOTIFY_CQM] = "notify_cqm",
[NL80211_CMD_SET_CHANNEL] = "set_channel",
[NL80211_CMD_SET_WDS_PEER] = "set_wds_peer",
[NL80211_CMD_FRAME_WAIT_CANCEL] = "frame_wait_cancel",
[NL80211_CMD_JOIN_MESH] = "join_mesh",
[NL80211_CMD_LEAVE_MESH] = "leave_mesh",
[NL80211_CMD_UNPROT_DEAUTHENTICATE] = "unprot_deauthenticate",
[NL80211_CMD_UNPROT_DISASSOCIATE] = "unprot_disassociate",
[NL80211_CMD_NEW_PEER_CANDIDATE] = "new_peer_candidate",
[NL80211_CMD_GET_WOWLAN] = "get_wowlan",
[NL80211_CMD_SET_WOWLAN] = "set_wowlan",
[NL80211_CMD_START_SCHED_SCAN] = "start_sched_scan",
[NL80211_CMD_STOP_SCHED_SCAN] = "stop_sched_scan",
[NL80211_CMD_SCHED_SCAN_RESULTS] = "sched_scan_results",
[NL80211_CMD_SCHED_SCAN_STOPPED] = "sched_scan_stopped",
[NL80211_CMD_SET_REKEY_OFFLOAD] = "set_rekey_offload",
[NL80211_CMD_PMKSA_CANDIDATE] = "pmksa_candidate",
[NL80211_CMD_TDLS_OPER] = "tdls_oper",
[NL80211_CMD_TDLS_MGMT] = "tdls_mgmt",
[NL80211_CMD_UNEXPECTED_FRAME] = "unexpected_frame",
[NL80211_CMD_PROBE_CLIENT] = "probe_client",
[NL80211_CMD_REGISTER_BEACONS] = "register_beacons",
[NL80211_CMD_UNEXPECTED_4ADDR_FRAME] = "unexpected_4addr_frame",
[NL80211_CMD_SET_NOACK_MAP] = "set_noack_map",
[NL80211_CMD_CH_SWITCH_NOTIFY] = "ch_switch_notify",
[NL80211_CMD_START_P2P_DEVICE] = "start_p2p_device",
[NL80211_CMD_STOP_P2P_DEVICE] = "stop_p2p_device",
[NL80211_CMD_CONN_FAILED] = "conn_failed",
[NL80211_CMD_SET_MCAST_RATE] = "set_mcast_rate",
[NL80211_CMD_SET_MAC_ACL] = "set_mac_acl",
[NL80211_CMD_RADAR_DETECT] = "radar_detect",
[NL80211_CMD_GET_PROTOCOL_FEATURES] = "get_protocol_features",
[NL80211_CMD_UPDATE_FT_IES] = "update_ft_ies",
[NL80211_CMD_FT_EVENT] = "ft_event",
[NL80211_CMD_CRIT_PROTOCOL_START] = "crit_protocol_start",
[NL80211_CMD_CRIT_PROTOCOL_STOP] = "crit_protocol_stop",
[NL80211_CMD_GET_COALESCE] = "get_coalesce",
[NL80211_CMD_SET_COALESCE] = "set_coalesce",
[NL80211_CMD_CHANNEL_SWITCH] = "channel_switch",
[NL80211_CMD_VENDOR] = "vendor",
[NL80211_CMD_SET_QOS_MAP] = "set_qos_map",
[NL80211_CMD_ADD_TX_TS] = "add_tx_ts",
[NL80211_CMD_DEL_TX_TS] = "del_tx_ts",
[NL80211_CMD_GET_MPP] = "get_mpp",
[NL80211_CMD_JOIN_OCB] = "join_ocb",
[NL80211_CMD_LEAVE_OCB] = "leave_ocb",
[NL80211_CMD_CH_SWITCH_STARTED_NOTIFY] = "ch_switch_started_notify",
[NL80211_CMD_TDLS_CHANNEL_SWITCH] = "tdls_channel_switch",
[NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH] = "tdls_cancel_channel_switch",
[NL80211_CMD_WIPHY_REG_CHANGE] = "wiphy_reg_change",
[NL80211_CMD_ABORT_SCAN] = "abort_scan",
[NL80211_CMD_START_NAN] = "start_nan",
[NL80211_CMD_STOP_NAN] = "stop_nan",
[NL80211_CMD_ADD_NAN_FUNCTION] = "add_nan_function",
[NL80211_CMD_DEL_NAN_FUNCTION] = "del_nan_function",
[NL80211_CMD_CHANGE_NAN_CONFIG] = "change_nan_config",
[NL80211_CMD_NAN_MATCH] = "nan_match",
[NL80211_CMD_SET_MULTICAST_TO_UNICAST] = "set_multicast_to_unicast",
[NL80211_CMD_UPDATE_CONNECT_PARAMS] = "update_connect_params",
[NL80211_CMD_SET_PMK] = "set_pmk",
[NL80211_CMD_DEL_PMK] = "del_pmk",
[NL80211_CMD_PORT_AUTHORIZED] = "port_authorized",
[NL80211_CMD_RELOAD_REGDB] = "reload_regdb",
[NL80211_CMD_EXTERNAL_AUTH] = "external_auth",
[NL80211_CMD_STA_OPMODE_CHANGED] = "sta_opmode_changed",
[NL80211_CMD_CONTROL_PORT_FRAME] = "control_port_frame",
[NL80211_CMD_GET_FTM_RESPONDER_STATS] = "get_ftm_responder_stats",
[NL80211_CMD_PEER_MEASUREMENT_START] = "peer_measurement_start",
[NL80211_CMD_PEER_MEASUREMENT_RESULT] = "peer_measurement_result",
[NL80211_CMD_PEER_MEASUREMENT_COMPLETE] = "peer_measurement_complete",
[NL80211_CMD_NOTIFY_RADAR] = "notify_radar",
[NL80211_CMD_UPDATE_OWE_INFO] = "update_owe_info",
[NL80211_CMD_PROBE_MESH_LINK] = "probe_mesh_link",
[NL80211_CMD_SET_TID_CONFIG] = "set_tid_config",
[NL80211_CMD_UNPROT_BEACON] = "unprot_beacon",
[NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS] = "control_port_frame_tx_status",
[NL80211_CMD_SET_SAR_SPECS] = "set_sar_specs",
[NL80211_CMD_OBSS_COLOR_COLLISION] = "obss_color_collision",
[NL80211_CMD_COLOR_CHANGE_REQUEST] = "color_change_request",
[NL80211_CMD_COLOR_CHANGE_STARTED] = "color_change_started",
[NL80211_CMD_COLOR_CHANGE_ABORTED] = "color_change_aborted",
[NL80211_CMD_COLOR_CHANGE_COMPLETED] = "color_change_completed",
};
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(nl80211_cmd, int);

View File

@ -3,9 +3,13 @@
#pragma once #pragma once
#include <linux/nl80211.h> #include <linux/nl80211.h>
#include <net/ethernet.h>
#include "sd-netlink.h" #include "sd-netlink.h"
int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftype, char **ssid); #include "ether-addr-util.h"
int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid);
int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *ret_iftype, char **ret_ssid);
int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *ret_bssid);
const char *nl80211_iftype_to_string(enum nl80211_iftype iftype) _const_;
const char *nl80211_cmd_to_string(int cmd) _const_;

View File

@ -39,11 +39,11 @@ int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
uint32_t *lifetime_preferred, uint32_t *lifetime_preferred,
uint32_t *lifetime_valid); uint32_t *lifetime_valid);
int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs); int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret);
int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains); int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret);
int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **addrs); int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret);
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn); int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret);
int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **fqdn); int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret);
sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease); sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease);
sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease); sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease);

View File

@ -95,6 +95,7 @@ int sd_netlink_message_close_container(sd_netlink_message *m);
int sd_netlink_message_read(sd_netlink_message *m, unsigned short type, size_t size, void *data); int sd_netlink_message_read(sd_netlink_message *m, unsigned short type, size_t size, void *data);
int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data); int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data);
int sd_netlink_message_read_data_suffix0(sd_netlink_message *m, unsigned short type, size_t *ret_size, void **ret_data);
int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsigned short type, char **data); int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsigned short type, char **data);
int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data); int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, const char **data);
int sd_netlink_message_read_strv(sd_netlink_message *m, unsigned short container_type, unsigned short type_id, char ***ret); int sd_netlink_message_read_strv(sd_netlink_message *m, unsigned short container_type, unsigned short type_id, char ***ret);

View File

@ -390,6 +390,28 @@ static void test_strv_split_full(void) {
assert_se(streq_ptr(l[5], NULL)); assert_se(streq_ptr(l[5], NULL));
} }
static void test_strv_split_and_extend_full(void) {
_cleanup_strv_free_ char **l = NULL;
const char *str1 = ":foo\\:bar:";
const char *str2 = "waldo::::::baz";
int r;
log_info("/* %s */", __func__);
r = strv_split_and_extend(&l, "", ":", false);
assert_se(r == (int) strv_length(l));
r = strv_split_and_extend_full(&l, str1, ":", false, EXTRACT_DONT_COALESCE_SEPARATORS);
assert_se(r == (int) strv_length(l));
assert_se(streq_ptr(l[0], ""));
assert_se(streq_ptr(l[1], "foo:bar"));
assert_se(streq_ptr(l[2], ""));
r = strv_split_and_extend_full(&l, str2, ":", false, 0);
assert_se(r == (int) strv_length(l));
assert_se(streq_ptr(l[3], "waldo"));
assert_se(streq_ptr(l[4], "baz"));
assert_se(streq_ptr(l[5], NULL));
}
static void test_strv_split_colon_pairs(void) { static void test_strv_split_colon_pairs(void) {
_cleanup_strv_free_ char **l = NULL; _cleanup_strv_free_ char **l = NULL;
const char *str = "one:two three four:five six seven:eight\\:nine ten\\:eleven\\\\", const char *str = "one:two three four:five six seven:eight\\:nine ten\\:eleven\\\\",
@ -1028,6 +1050,7 @@ int main(int argc, char *argv[]) {
test_strv_split(); test_strv_split();
test_strv_split_empty(); test_strv_split_empty();
test_strv_split_full(); test_strv_split_full();
test_strv_split_and_extend_full();
test_strv_split_colon_pairs(); test_strv_split_colon_pairs();
test_strv_split_newlines(); test_strv_split_newlines();
test_strv_split_newlines_full(); test_strv_split_newlines_full();

View File

@ -18,6 +18,7 @@
#include "dirent-util.h" #include "dirent-util.h"
#include "fd-util.h" #include "fd-util.h"
#include "sort-util.h" #include "sort-util.h"
#include "static-destruct.h"
#include "string-table.h" #include "string-table.h"
#include "string-util.h" #include "string-util.h"
#include "udev-util.h" #include "udev-util.h"
@ -38,8 +39,10 @@ typedef enum QueryType {
QUERY_ALL, QUERY_ALL,
} QueryType; } QueryType;
static char **arg_properties = NULL;
static bool arg_root = false; static bool arg_root = false;
static bool arg_export = false; static bool arg_export = false;
static bool arg_value = false;
static const char *arg_export_prefix = NULL; static const char *arg_export_prefix = NULL;
static usec_t arg_wait_for_initialization_timeout = 0; static usec_t arg_wait_for_initialization_timeout = 0;
@ -60,6 +63,8 @@ typedef struct SysAttr {
const char *value; const char *value;
} SysAttr; } SysAttr;
STATIC_DESTRUCTOR_REGISTER(arg_properties, strv_freep);
static int sysattr_compare(const SysAttr *a, const SysAttr *b) { static int sysattr_compare(const SysAttr *a, const SysAttr *b) {
return strcmp(a->name, b->name); return strcmp(a->name, b->name);
} }
@ -316,11 +321,18 @@ static int query_device(QueryType query, sd_device* device) {
case QUERY_PROPERTY: { case QUERY_PROPERTY: {
const char *key, *value; const char *key, *value;
FOREACH_DEVICE_PROPERTY(device, key, value) FOREACH_DEVICE_PROPERTY(device, key, value) {
if (arg_properties && !strv_contains(arg_properties, key))
continue;
if (arg_export) if (arg_export)
printf("%s%s='%s'\n", strempty(arg_export_prefix), key, value); printf("%s%s='%s'\n", strempty(arg_export_prefix), key, value);
else if (arg_value)
printf("%s\n", value);
else else
printf("%s=%s\n", key, value); printf("%s=%s\n", key, value);
}
return 0; return 0;
} }
@ -343,6 +355,8 @@ static int help(void) {
" path sysfs device path\n" " path sysfs device path\n"
" property The device properties\n" " property The device properties\n"
" all All values\n" " all All values\n"
" --property=NAME Show only properties by this name\n"
" --value When showing properties, print only their values\n"
" -p --path=SYSPATH sysfs device path used for query or attribute walk\n" " -p --path=SYSPATH sysfs device path used for query or attribute walk\n"
" -n --name=NAME Node or symlink name used for query or attribute walk\n" " -n --name=NAME Node or symlink name used for query or attribute walk\n"
" -r --root Prepend dev directory to path names\n" " -r --root Prepend dev directory to path names\n"
@ -365,20 +379,27 @@ int info_main(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *name = NULL; _cleanup_free_ char *name = NULL;
int c, r; int c, r;
enum {
ARG_PROPERTY = 0x100,
ARG_VALUE,
};
static const struct option options[] = { static const struct option options[] = {
{ "name", required_argument, NULL, 'n' },
{ "path", required_argument, NULL, 'p' },
{ "query", required_argument, NULL, 'q' },
{ "attribute-walk", no_argument, NULL, 'a' }, { "attribute-walk", no_argument, NULL, 'a' },
{ "cleanup-db", no_argument, NULL, 'c' }, { "cleanup-db", no_argument, NULL, 'c' },
{ "export-db", no_argument, NULL, 'e' },
{ "root", no_argument, NULL, 'r' },
{ "device-id-of-file", required_argument, NULL, 'd' }, { "device-id-of-file", required_argument, NULL, 'd' },
{ "export", no_argument, NULL, 'x' }, { "export", no_argument, NULL, 'x' },
{ "export-db", no_argument, NULL, 'e' },
{ "export-prefix", required_argument, NULL, 'P' }, { "export-prefix", required_argument, NULL, 'P' },
{ "wait-for-initialization", optional_argument, NULL, 'w' },
{ "version", no_argument, NULL, 'V' },
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
{ "name", required_argument, NULL, 'n' },
{ "path", required_argument, NULL, 'p' },
{ "property", required_argument, NULL, ARG_PROPERTY },
{ "query", required_argument, NULL, 'q' },
{ "root", no_argument, NULL, 'r' },
{ "value", no_argument, NULL, ARG_VALUE },
{ "version", no_argument, NULL, 'V' },
{ "wait-for-initialization", optional_argument, NULL, 'w' },
{} {}
}; };
@ -387,6 +408,22 @@ int info_main(int argc, char *argv[], void *userdata) {
while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:w::Vh", options, NULL)) >= 0) while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:w::Vh", options, NULL)) >= 0)
switch (c) { switch (c) {
case ARG_PROPERTY:
/* Make sure that if the empty property list was specified, we won't show any
properties. */
if (isempty(optarg) && !arg_properties) {
arg_properties = new0(char*, 1);
if (!arg_properties)
return log_oom();
} else {
r = strv_split_and_extend(&arg_properties, optarg, ",", true);
if (r < 0)
return log_oom();
}
break;
case ARG_VALUE:
arg_value = true;
break;
case 'n': case 'n':
case 'p': { case 'p': {
const char *prefix = c == 'n' ? "/dev/" : "/sys/"; const char *prefix = c == 'n' ? "/dev/" : "/sys/";
@ -478,6 +515,10 @@ int info_main(int argc, char *argv[], void *userdata) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Only one device may be specified with -a/--attribute-walk"); "Only one device may be specified with -a/--attribute-walk");
if (arg_export && arg_value)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"-x/--export or -P/--export-prefix cannot be used with --value");
char **p; char **p;
STRV_FOREACH(p, devices) { STRV_FOREACH(p, devices) {
_cleanup_(sd_device_unrefp) sd_device *device = NULL; _cleanup_(sd_device_unrefp) sd_device *device = NULL;

View File

@ -11,6 +11,8 @@ set -e
TEST_DESCRIPTION="systemd-udev storage tests" TEST_DESCRIPTION="systemd-udev storage tests"
IMAGE_NAME="default" IMAGE_NAME="default"
TEST_NO_NSPAWN=1 TEST_NO_NSPAWN=1
# Save only journals of failing test cases by default (to conserve space)
TEST_SAVE_JOURNAL="${TEST_SAVE_JOURNAL:-fail}"
QEMU_TIMEOUT="${QEMU_TIMEOUT:-600}" QEMU_TIMEOUT="${QEMU_TIMEOUT:-600}"
# shellcheck source=test/test-functions # shellcheck source=test/test-functions
@ -28,14 +30,20 @@ _host_has_feature() {(
set -e set -e
case "${1:?}" in case "${1:?}" in
multipath) btrfs)
command -v multipath && command -v multipathd || return $? modprobe -nv btrfs && command -v mkfs.btrfs && command -v btrfs || return $?
;;
iscsi)
# Client/initiator (Open-iSCSI)
command -v iscsiadm && command -v iscsid || return $?
# Server/target (TGT)
command -v tgtadm && command -v tgtd || return $?
;; ;;
lvm) lvm)
command -v lvm || return $? command -v lvm || return $?
;; ;;
btrfs) multipath)
modprobe -nv btrfs && command -v mkfs.btrfs && command -v btrfs || return $? command -v multipath && command -v multipathd || return $?
;; ;;
*) *)
echo >&2 "ERROR: Unknown feature '$1'" echo >&2 "ERROR: Unknown feature '$1'"
@ -54,6 +62,7 @@ test_append_files() {(
# checked for here # checked for here
local -A features=( local -A features=(
[btrfs]=install_btrfs [btrfs]=install_btrfs
[iscsi]=install_iscsi
[lvm]=install_lvm [lvm]=install_lvm
[multipath]=install_multipath [multipath]=install_multipath
) )
@ -360,6 +369,35 @@ testcase_btrfs_basic() {
rm -f "${TESTDIR:?}"/btrfsbasic*.img rm -f "${TESTDIR:?}"/btrfsbasic*.img
} }
testcase_iscsi_lvm() {
if ! _host_has_feature "iscsi" || ! _host_has_feature "lvm"; then
echo "Missing iSCSI client/server tools (Open-iSCSI/TGT) or LVM utilities, skipping the test..."
return 77
fi
local qemu_opts=("-device ahci,id=ahci0")
local diskpath i size
for i in {0..3}; do
diskpath="${TESTDIR:?}/iscsibasic${i}.img"
# Make the first disk larger for multi-partition tests
[[ $i -eq 0 ]] && size=150 || size=64
# Make the first disk larger for multi-partition tests
dd if=/dev/zero of="$diskpath" bs=1M count="$size"
qemu_opts+=(
"-device ide-hd,bus=ahci0.$i,drive=drive$i,model=foobar,serial=deadbeefiscsi$i"
"-drive format=raw,cache=unsafe,file=$diskpath,if=none,id=drive$i"
)
done
KERNEL_APPEND="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}"
QEMU_OPTIONS="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}"
test_run_one "${1:?}" || return $?
rm -f "${TESTDIR:?}"/iscsibasic*.img
}
# Allow overriding which tests should be run from the "outside", useful for manual # Allow overriding which tests should be run from the "outside", useful for manual
# testing (make -C test/... TESTCASES="testcase1 testcase2") # testing (make -C test/... TESTCASES="testcase1 testcase2")
if [[ -v "TESTCASES" && -n "$TESTCASES" ]]; then if [[ -v "TESTCASES" && -n "$TESTCASES" ]]; then

View File

@ -959,6 +959,45 @@ install_btrfs() {
inst_rules 64-btrfs-dm.rules inst_rules 64-btrfs-dm.rules
} }
install_iscsi() {
# Install both client and server side stuff by default
local inst="${1:-}"
local file
# Install client-side stuff ("initiator" in iSCSI jargon) - Open-iSCSI in this case
# (open-iscsi on Debian, iscsi-initiator-utils on Fedora, etc.)
if [[ -z "$inst" || "$inst" =~ (client|initiator) ]]; then
image_install iscsi-iname iscsiadm iscsid iscsistart
image_install -o "${ROOTLIBDIR:?}"/system/iscsi-{init,onboot,shutdown}.service
image_install "${ROOTLIBDIR:?}"/system/iscsid.{service,socket}
image_install "${ROOTLIBDIR:?}"/system/iscsi.service
mkdir -p "${initdir:?}"/var/lib/iscsi/{ifaces,isns,nodes,send_targets,slp,static}
mkdir -p "${initdir:?}/etc/iscsi"
echo "iscsid.startup = /bin/systemctl start iscsid.socket" >"${initdir:?}/etc/iscsi/iscsid.conf"
inst_simple "/etc/iscsi/initiatorname.iscsi"
fi
# Install server-side stuff ("target" in iSCSI jargon) - TGT in this case
# (tgt on Debian, scsi-target-utils on Fedora, etc.)
if [[ -z "$inst" || "$inst" =~ (server|target) ]]; then
image_install tgt-admin tgt-setup-lun tgtadm tgtd tgtimg
image_install -o /etc/sysconfig/tgtd
image_install "${ROOTLIBDIR:?}"/system/tgtd.service
mkdir -p "${initdir:?}/etc/tgt"
touch "${initdir:?}"/etc/tgt/{tgtd,targets}.conf
# Install perl modules required by tgt-admin
#
# Forgive me father for I have sinned. The monstrosity below appends
# a perl snippet to the `tgt-admin` perl script on the fly, which
# dumps a list of files (perl modules) required by `tgt-admin` at
# the runtime plus any DSOs loaded via DynaLoader. This list is then
# passed to `inst_simple` which installs the necessary files into the image
while read -r file; do
inst_simple "$file"
done < <(perl -- <(cat $(command -v tgt-admin) <(echo -e 'use DynaLoader; print map { "$_\n" } values %INC; print join("\n", @DynaLoader::dl_shared_objects)')) -p | awk '/^\// { print $1 }')
fi
}
install_compiled_systemd() { install_compiled_systemd() {
dinfo "Install compiled systemd" dinfo "Install compiled systemd"
@ -2319,6 +2358,8 @@ inst() {
for fun in inst_symlink inst_script inst_binary inst_simple; do for fun in inst_symlink inst_script inst_binary inst_simple; do
"$fun" "$@" && return 0 "$fun" "$@" && return 0
done done
dwarn "Failed to install '$1'"
return 1 return 1
} }

View File

@ -40,6 +40,57 @@ helper_check_device_symlinks() {(
done < <(find "${paths[@]}" -type l) done < <(find "${paths[@]}" -type l)
)} )}
# Wait for a specific device link to appear
# Arguments:
# $1 - device path
# $2 - number of retries (default: 10)
helper_wait_for_dev() {
local dev="${1:?}"
local ntries="${2:-10}"
local i
for ((i = 0; i < ntries; i++)); do
test ! -e "$dev" || return 0
sleep .2
done
return 1
}
# Wait for the lvm2-pvscan@.service of a specific device to finish
# Arguments:
# $1 - device path
# $2 - number of retries (default: 10)
helper_wait_for_pvscan() {
local dev="${1:?}"
local ntries="${2:-10}"
local MAJOR MINOR pvscan_svc real_dev
# Sanity check we got a valid block device (or a symlink to it)
real_dev="$(readlink -f "$dev")"
if [[ ! -b "$real_dev" ]]; then
echo >&2 "ERROR: '$dev ($real_dev) is not a valid block device'"
return 1
fi
# Get major and minor numbers from the udev database
# (udevadm returns MAJOR= and MINOR= expressions, so let's pull them into
# the current environment via `source` for easier parsing)
source <(udevadm info -q property "$real_dev" | grep -E "(MAJOR|MINOR)=")
# Sanity check if we got correct major and minor numbers
test -e "/sys/dev/block/$MAJOR:$MINOR/"
# Wait n_tries*0.5 seconds until the respective lvm2-pvscan service becomes
# active (i.e. it got executed and finished)
pvscan_svc="lvm2-pvscan@$MAJOR:$MINOR.service"
for ((i = 0; i < ntries; i++)); do
! systemctl -q is-active "$pvscan_svc" || return 0
sleep .5
done
return 1
}
testcase_megasas2_basic() { testcase_megasas2_basic() {
lsblk -S lsblk -S
[[ "$(lsblk --scsi --noheadings | wc -l)" -ge 128 ]] [[ "$(lsblk --scsi --noheadings | wc -l)" -ge 128 ]]
@ -397,6 +448,144 @@ EOF
udevadm settle udevadm settle
} }
testcase_iscsi_lvm() {
local dev i label link lun_id mpoint target_name uuid
local target_ip="127.0.0.1"
local target_port="3260"
local vgroup="iscsi_lvm$RANDOM"
local expected_symlinks=()
local devices=(
/dev/disk/by-id/ata-foobar_deadbeefiscsi{0..3}
)
ls -l "${devices[@]}"
# Start the target daemon
systemctl start tgtd
systemctl status tgtd
echo "iSCSI LUNs backed by devices"
# See RFC3721 and RFC7143
target_name="iqn.2021-09.com.example:iscsi.test"
# Initialize a new iSCSI target <$target_name> consisting of 4 LUNs, each
# backed by a device
tgtadm --lld iscsi --op new --mode target --tid=1 --targetname "$target_name"
for ((i = 0; i < ${#devices[@]}; i++)); do
# lun-0 is reserved by iSCSI
lun_id="$((i + 1))"
tgtadm --lld iscsi --op new --mode logicalunit --tid 1 --lun "$lun_id" -b "${devices[$i]}"
tgtadm --lld iscsi --op update --mode logicalunit --tid 1 --lun "$lun_id"
expected_symlinks+=(
"/dev/disk/by-path/ip-$target_ip:$target_port-iscsi-$target_name-lun-$lun_id"
)
done
tgtadm --lld iscsi --op bind --mode target --tid 1 -I ALL
# Configure the iSCSI initiator
iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover
iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login
udevadm settle
# Check if all device symlinks are valid and if all expected device symlinks exist
for link in "${expected_symlinks[@]}"; do
# We need to do some active waiting anyway, as it may take kernel a bit
# to attach the newly connected SCSI devices
helper_wait_for_dev "$link"
test -e "$link"
done
udevadm settle
helper_check_device_symlinks
# Cleanup
iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout
tgtadm --lld iscsi --op delete --mode target --tid=1
echo "iSCSI LUNs backed by files + LVM"
# Note: we use files here to "trick" LVM the disks are indeed on a different
# host, so it doesn't automagically detect another path to the backing
# device once we disconnect the iSCSI devices
target_name="iqn.2021-09.com.example:iscsi.lvm.test"
mpoint="$(mktemp -d /iscsi_storeXXX)"
expected_symlinks=()
# Use the first device as it's configured with larger capacity
mkfs.ext4 -L iscsi_store "${devices[0]}"
udevadm settle
mount "${devices[0]}" "$mpoint"
for i in {1..4}; do
dd if=/dev/zero of="$mpoint/lun$i.img" bs=1M count=32
done
# Initialize a new iSCSI target <$target_name> consisting of 4 LUNs, each
# backed by a file
tgtadm --lld iscsi --op new --mode target --tid=2 --targetname "$target_name"
# lun-0 is reserved by iSCSI
for i in {1..4}; do
tgtadm --lld iscsi --op new --mode logicalunit --tid 2 --lun "$i" -b "$mpoint/lun$i.img"
tgtadm --lld iscsi --op update --mode logicalunit --tid 2 --lun "$i"
expected_symlinks+=(
"/dev/disk/by-path/ip-$target_ip:$target_port-iscsi-$target_name-lun-$i"
)
done
tgtadm --lld iscsi --op bind --mode target --tid 2 -I ALL
# Configure the iSCSI initiator
iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover
iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login
udevadm settle
# Check if all device symlinks are valid and if all expected device symlinks exist
for link in "${expected_symlinks[@]}"; do
# We need to do some active waiting anyway, as it may take kernel a bit
# to attach the newly connected SCSI devices
helper_wait_for_dev "$link"
test -e "$link"
done
udevadm settle
helper_check_device_symlinks
# Add all iSCSI devices into a LVM volume group, create two logical volumes,
# and check if necessary symlinks exist (and are valid)
lvm pvcreate -y "${expected_symlinks[@]}"
lvm pvs
lvm vgcreate "$vgroup" -y "${expected_symlinks[@]}"
lvm vgs
lvm vgchange -ay "$vgroup"
lvm lvcreate -y -L 4M "$vgroup" -n mypart1
lvm lvcreate -y -L 8M "$vgroup" -n mypart2
lvm lvs
udevadm settle
test -e "/dev/$vgroup/mypart1"
test -e "/dev/$vgroup/mypart2"
mkfs.ext4 -L mylvpart1 "/dev/$vgroup/mypart1"
udevadm settle
test -e "/dev/disk/by-label/mylvpart1"
helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
# Disconnect the iSCSI devices and check all the symlinks
iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout
# "Reset" the DM state, since we yanked the backing storage from under the LVM,
# so the currently active VGs/LVs are invalid
dmsetup remove_all --deferred
udevadm settle
# The LVM and iSCSI related symlinks should be gone
test ! -e "/dev/$vgroup"
test ! -e "/dev/disk/by-label/mylvpart1"
for link in "${expected_symlinks[@]}"; do
test ! -e "$link"
done
helper_check_device_symlinks "/dev/disk"
# Reconnect the iSCSI devices and check if everything get detected correctly
iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover
iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login
udevadm settle
for link in "${expected_symlinks[@]}"; do
helper_wait_for_dev "$link"
helper_wait_for_pvscan "$link"
test -e "$link"
done
udevadm settle
test -e "/dev/$vgroup/mypart1"
test -e "/dev/$vgroup/mypart2"
test -e "/dev/disk/by-label/mylvpart1"
helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
# Cleanup
iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout
tgtadm --lld iscsi --op delete --mode target --tid=2
umount "$mpoint"
rm -rf "$mpoint"
}
: >/failed : >/failed
udevadm settle udevadm settle

View File

@ -68,6 +68,12 @@ cat <<EOF >/tmp/testfile.service
ExecStart = echo hello ExecStart = echo hello
EOF EOF
# Prevent regression from #13380 and #20859 where we can't verify hidden files
cp /tmp/testfile.service /tmp/.testfile.service
systemd-analyze verify /tmp/.testfile.service
rm /tmp/.testfile.service
# Zero exit status since the value used for comparison determine exposure to security threats is by default 100 # Zero exit status since the value used for comparison determine exposure to security threats is by default 100
systemd-analyze security --offline=true /tmp/testfile.service systemd-analyze security --offline=true /tmp/testfile.service