mirror of
https://github.com/systemd/systemd
synced 2026-03-29 11:14:50 +02:00
Compare commits
63 Commits
41a978fdb1
...
721956f3e9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
721956f3e9 | ||
|
|
aebff2e7ce | ||
|
|
209abeac6d | ||
|
|
ca6bc7ce0d | ||
|
|
bfcc0fedd0 | ||
|
|
504cfa6573 | ||
|
|
3e90ded70c | ||
|
|
78fac35811 | ||
|
|
f813515542 | ||
|
|
14bb729534 | ||
|
|
e30a3ba16a | ||
|
|
bee07a3995 | ||
|
|
6d350f7d82 | ||
|
|
faa2e64f9b | ||
|
|
4c78dc17e5 | ||
|
|
92fc611cac | ||
|
|
6c1482b28d | ||
|
|
3c318caa6f | ||
|
|
c1036042f5 | ||
|
|
bbc1bb0742 | ||
|
|
9cb41c3326 | ||
|
|
aedb60043a | ||
|
|
7074c047c1 | ||
|
|
f4e64b6e34 | ||
|
|
5f25c30ee8 | ||
|
|
96f5f9ef9a | ||
|
|
f12629ae38 | ||
|
|
bdcd4ab2f1 | ||
|
|
96243149bd | ||
|
|
16653f9782 | ||
|
|
77f75f4fff | ||
|
|
abad436d4c | ||
|
|
8e310690b0 | ||
|
|
f3e235ffb2 | ||
|
|
ae2b86de43 | ||
|
|
a1d2ae065b | ||
|
|
84f7bd7698 | ||
|
|
4d4d898a4b | ||
|
|
ec44d3f407 | ||
|
|
dbc35fe3aa | ||
|
|
52888279df | ||
|
|
61dcd98232 | ||
|
|
e210f027e0 | ||
|
|
e693e96961 | ||
|
|
9c3d46bf8d | ||
|
|
41b14f0329 | ||
|
|
5cf67bb407 | ||
|
|
edeee50b3d | ||
|
|
548c33d712 | ||
|
|
3f8227bf83 | ||
|
|
b27dcf0813 | ||
|
|
ad3c84204c | ||
|
|
8a8955507a | ||
|
|
4af39cb8ec | ||
|
|
e620104956 | ||
|
|
b89a3758e9 | ||
|
|
dc95e21d33 | ||
|
|
b4c7934a3c | ||
|
|
b8355de49e | ||
|
|
b87e96e2f2 | ||
|
|
3b6a3bdebf | ||
|
|
3c88ade5a0 | ||
|
|
0889107a0a |
7
.github/workflows/unit_tests.sh
vendored
7
.github/workflows/unit_tests.sh
vendored
@ -6,7 +6,6 @@ ADDITIONAL_DEPS=(
|
||||
clang
|
||||
expect
|
||||
fdisk
|
||||
iproute2
|
||||
jekyll
|
||||
lcov
|
||||
libfdisk-dev
|
||||
@ -20,7 +19,6 @@ ADDITIONAL_DEPS=(
|
||||
perl
|
||||
python3-libevdev
|
||||
python3-pyparsing
|
||||
util-linux
|
||||
zstd
|
||||
)
|
||||
|
||||
@ -55,8 +53,7 @@ for phase in "${PHASES[@]}"; do
|
||||
fi
|
||||
meson --werror -Dtests=unsafe -Dslow-tests=true -Dfuzz-tests=true "${MESON_ARGS[@]}" build
|
||||
ninja -C build -v
|
||||
# Some of the unsafe tests irreparably break the host's network connectivity, so run them in a namespace
|
||||
unshare -n bash -c 'ip link set dev lo up; meson test -C build --print-errorlogs'
|
||||
meson test -C build --print-errorlogs
|
||||
if [[ "$phase" = "RUN_GCC" ]]; then
|
||||
ninja -C build coverage
|
||||
fi
|
||||
@ -88,7 +85,7 @@ for phase in "${PHASES[@]}"; do
|
||||
# during debugging, wonderful), so let's at least keep a workaround
|
||||
# here to make the builds stable for the time being.
|
||||
(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)
|
||||
info "Cleanup phase"
|
||||
|
||||
7
.github/workflows/unit_tests.yml
vendored
7
.github/workflows/unit_tests.yml
vendored
@ -3,6 +3,10 @@
|
||||
#
|
||||
name: Unit tests
|
||||
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:
|
||||
branches:
|
||||
- main
|
||||
@ -17,10 +21,13 @@ jobs:
|
||||
steps:
|
||||
- name: Repository checkout
|
||||
uses: actions/checkout@v2
|
||||
if: github.event_name == 'pull_request' || matrix.run_phase == 'GCC'
|
||||
- name: Install build dependencies
|
||||
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 }})
|
||||
run: sudo -E .github/workflows/unit_tests.sh RUN_${{ matrix.run_phase }}
|
||||
if: github.event_name == 'pull_request' || matrix.run_phase == 'GCC'
|
||||
- name: Coveralls
|
||||
if: matrix.run_phase == 'GCC' && github.repository == 'systemd/systemd'
|
||||
uses: coverallsapp/github-action@master
|
||||
|
||||
5
NEWS
5
NEWS
@ -518,6 +518,11 @@ CHANGES WITH 249:
|
||||
distribution does not install it yet, it might make sense to change
|
||||
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,
|
||||
Alexander Sverdlin, Alexander Tsoy, Alexey Rubtsov, alexlzhu,
|
||||
Allen Webb, Alvin Šipraga, Alyssa Ross, Anders Wenhaug,
|
||||
|
||||
@ -494,9 +494,6 @@ evdev:input:b0003v0458p0708*
|
||||
# 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*:svnHP*:pn*:*
|
||||
KEYBOARD_KEY_81=fn_esc
|
||||
@ -560,9 +557,6 @@ evdev:atkbd:dmi:bvn*:bvr*:svnHP*:pnHPElitex21013G3:*
|
||||
KEYBOARD_KEY_92=brightnessdown
|
||||
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
|
||||
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*Compaq*:*
|
||||
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*EliteBook*:*
|
||||
|
||||
@ -230,8 +230,9 @@ sensor:modalias:acpi:BOSC0200*:dmi:*:svnChuwi*:pnHi13:*
|
||||
# Chuwi HiBook does not have its product name filled, so we
|
||||
# match the entire dmi-alias, assuming that the use of a BOSC0200 +
|
||||
# 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:*
|
||||
sensor:modalias:acpi:BOSC0200*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd05/28/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/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
|
||||
|
||||
# Chuwi HiBook Pro (CWI526)
|
||||
@ -241,7 +242,8 @@ sensor:modalias:acpi:BOSC0200*:dmi:*:svnHampoo*:pnP1D6_C109K:*
|
||||
# Chuwi CoreBook
|
||||
# Chuwi CoreBook does not have its product name filled, so we
|
||||
# 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
|
||||
|
||||
# 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
|
||||
# "Default string". So combined with the sensor modalias and BIOS date this
|
||||
# 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:*
|
||||
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/23/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:bd02/21/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:*
|
||||
# '*' in ":*svn" is there because kernels >= 5.8 have inserted a br field there
|
||||
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:bd11/18/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:bd12/26/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: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
|
||||
|
||||
#########################################
|
||||
@ -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
|
||||
|
||||
# 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
|
||||
|
||||
#########################################
|
||||
@ -713,7 +717,8 @@ sensor:modalias:acpi:SMO8500*:dmi:bvnAmericanMegatrendsInc.:bvr5.6.5:bd07/25/201
|
||||
|
||||
# One-netbook OneMix 2s
|
||||
# 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
|
||||
|
||||
# One-netbook OneMix 3 Pro
|
||||
@ -722,7 +727,8 @@ sensor:modalias:acpi:BOSC0200*:dmi:*svnONE-NETBOOKTECHNOLOGYCO*:pnOne-Mix3Pro:*
|
||||
|
||||
# One-netbook OneMix 3s
|
||||
# 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
|
||||
|
||||
#########################################
|
||||
@ -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
|
||||
# match the entire dmi-alias, assuming that the use of a BMA250E +
|
||||
# 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.:*
|
||||
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.:*
|
||||
# '*' in ":*svn" is there because kernels >= 5.8 have inserted a br field there
|
||||
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
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
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
|
||||
|
||||
#########################################
|
||||
@ -914,7 +923,8 @@ sensor:modalias:acpi:SMO8500*:dmi:*:svnUMAX:pnVisionBook10WiPlus:*
|
||||
# The Winpad A15 does not have its product name filled, so we
|
||||
# match the entire dmi-alias, assuming that the use of a SMO8500 +
|
||||
# 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
|
||||
|
||||
#########################################
|
||||
|
||||
@ -1711,7 +1711,7 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
readonly s OnFailureJobMode = '...';
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly b IgnoreOnIsolate = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly b NeedDaemonReload = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
|
||||
readonly as Markers = ['...', ...];
|
||||
|
||||
@ -791,6 +791,14 @@
|
||||
default will be used.</para>
|
||||
</listitem>
|
||||
</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>
|
||||
</refsect1>
|
||||
|
||||
@ -867,14 +875,6 @@
|
||||
<para>Accepts the same key as in [VXLAN] section.</para>
|
||||
</listitem>
|
||||
</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>
|
||||
</refsect1>
|
||||
|
||||
|
||||
@ -98,6 +98,24 @@
|
||||
</listitem>
|
||||
</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>
|
||||
<term><option>-p</option></term>
|
||||
<term><option>--path=<replaceable>DEVPATH</replaceable></option></term>
|
||||
|
||||
@ -49,8 +49,8 @@ _udevadm() {
|
||||
[COMMON]='-h --help -V --version'
|
||||
[DEBUG]='-d --debug'
|
||||
[INFO_STANDALONE]='-r --root -a --attribute-walk -x --export -e --export-db -c --cleanup-db
|
||||
-w --wait-for-initialization'
|
||||
[INFO_ARG]='-q --query -p --path -n --name -P --export-prefix -d --device-id-of-file'
|
||||
-w --wait-for-initialization --value'
|
||||
[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_ARG]='-t --type -c --action -s --subsystem-match -S --subsystem-nomatch
|
||||
-a --attr-match -A --attr-nomatch -p --property-match
|
||||
|
||||
@ -13,7 +13,9 @@ _udevadm_info(){
|
||||
'--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' \
|
||||
'--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] )) ||
|
||||
|
||||
@ -1185,6 +1185,21 @@
|
||||
* passed using %NL80211_ATTR_SAR_SPEC. %NL80211_ATTR_WIPHY is used 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_AFTER_LAST: internal use
|
||||
*/
|
||||
@ -1417,6 +1432,14 @@ enum nl80211_commands {
|
||||
|
||||
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 */
|
||||
|
||||
/* 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
|
||||
* 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
|
||||
* @NL80211_ATTR_MAX: highest attribute number currently defined
|
||||
* @__NL80211_ATTR_AFTER_LAST: internal use
|
||||
@ -3057,6 +3090,12 @@ enum nl80211_attrs {
|
||||
|
||||
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 */
|
||||
|
||||
__NL80211_ATTR_AFTER_LAST,
|
||||
@ -5953,6 +5992,9 @@ enum nl80211_feature_flags {
|
||||
* frame protection for all management frames exchanged during the
|
||||
* 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.
|
||||
* @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_RTT,
|
||||
NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
|
||||
NL80211_EXT_FEATURE_BSS_COLOR,
|
||||
|
||||
/* add new features before the definition below */
|
||||
NUM_NL80211_EXT_FEATURES,
|
||||
|
||||
@ -44,3 +44,8 @@
|
||||
#ifndef BOND_MAX_ARP_TARGETS
|
||||
#define BOND_MAX_ARP_TARGETS 16
|
||||
#endif
|
||||
|
||||
/* Not exposed but defined in include/linux/ieee80211.h */
|
||||
#ifndef IEEE80211_MAX_SSID_LEN
|
||||
#define IEEE80211_MAX_SSID_LEN 32
|
||||
#endif
|
||||
|
||||
@ -300,6 +300,24 @@ int strv_split_full(char ***t, const char *s, const char *separators, ExtractFla
|
||||
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) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
size_t n = 0;
|
||||
|
||||
@ -83,6 +83,9 @@ static inline char **strv_split(const char *s, const char *separators) {
|
||||
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);
|
||||
static inline char **strv_split_newlines(const char *s) {
|
||||
char **ret;
|
||||
|
||||
@ -284,7 +284,7 @@ int unit_file_build_name_map(
|
||||
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;
|
||||
_unused_ _cleanup_free_ char *_filename_free = NULL;
|
||||
_cleanup_free_ char *simplified = NULL;
|
||||
|
||||
@ -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("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("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("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),
|
||||
|
||||
@ -1575,6 +1575,10 @@ static int mount_setup_new_unit(
|
||||
if (r < 0)
|
||||
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
|
||||
* the unit file for it (and thus add in extra deps right after) we know what source to attributes the deps
|
||||
* to. */
|
||||
|
||||
@ -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_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_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
|
||||
size_t *optlen, uint8_t **optvalue);
|
||||
int dhcp6_option_parse_status(DHCP6Option *option, size_t len);
|
||||
int dhcp6_option_parse_ia(sd_dhcp6_client *client, DHCP6Option *iaoption, be32_t iaid, DHCP6IA *ia, uint16_t *ret_status_code);
|
||||
int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
|
||||
struct in6_addr **addrs, size_t count);
|
||||
int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen,
|
||||
char ***str_arr);
|
||||
int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char **str);
|
||||
|
||||
int dhcp6_option_parse(
|
||||
const uint8_t *buf,
|
||||
size_t buflen,
|
||||
size_t *offset,
|
||||
uint16_t *ret_option_code,
|
||||
size_t *ret_option_data_len,
|
||||
const uint8_t **ret_option_data);
|
||||
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_send_udp_socket(int s, struct in6_addr *address,
|
||||
|
||||
@ -28,11 +28,11 @@ struct sd_dhcp6_lease {
|
||||
struct in6_addr *dns;
|
||||
size_t dns_count;
|
||||
char **domains;
|
||||
size_t domains_count;
|
||||
struct in6_addr *ntp;
|
||||
size_t ntp_count;
|
||||
char **ntp_fqdn;
|
||||
size_t ntp_fqdn_count;
|
||||
struct in6_addr *sntp;
|
||||
size_t sntp_count;
|
||||
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_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_set_domains(sd_dhcp6_lease *lease, uint8_t *optval,
|
||||
size_t optlen);
|
||||
int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen);
|
||||
int dhcp6_lease_set_sntp(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_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
|
||||
int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const 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_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
|
||||
|
||||
int dhcp6_lease_new(sd_dhcp6_lease **ret);
|
||||
|
||||
@ -14,29 +14,12 @@
|
||||
#include "dhcp6-lease-internal.h"
|
||||
#include "dhcp6-protocol.h"
|
||||
#include "dns-domain.h"
|
||||
#include "escape.h"
|
||||
#include "memory-util.h"
|
||||
#include "sparse-endian.h"
|
||||
#include "strv.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_PD_LEN (sizeof(struct ia_pd))
|
||||
#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);
|
||||
}
|
||||
|
||||
static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) {
|
||||
DHCP6Option *option = (DHCP6Option*) *buf;
|
||||
uint16_t len;
|
||||
int dhcp6_option_parse(
|
||||
const uint8_t *buf,
|
||||
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);
|
||||
assert_return(optcode, -EINVAL);
|
||||
assert_return(optlen, -EINVAL);
|
||||
const DHCP6Option *option;
|
||||
size_t len;
|
||||
|
||||
if (*buflen < offsetof(DHCP6Option, data))
|
||||
return -ENOMSG;
|
||||
assert(buf);
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
*optcode = be16toh(option->code);
|
||||
*optlen = len;
|
||||
lt_valid = be32toh(((const struct iapdprefix*) data)->lifetime_valid);
|
||||
lt_pref = be32toh(((const struct iapdprefix*) data)->lifetime_preferred);
|
||||
|
||||
*buf += 4;
|
||||
*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)
|
||||
if (lt_valid == 0)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Valid lifetime of an IA address is zero or "
|
||||
"preferred lifetime %"PRIu32" > valid lifetime %"PRIu32,
|
||||
"Received a PD prefix with zero valid lifetime, ignoring.");
|
||||
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);
|
||||
|
||||
if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*addr_option)) {
|
||||
r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*addr_option));
|
||||
if (len > sizeof(struct iapdprefix)) {
|
||||
r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iapdprefix), len - sizeof(struct iapdprefix));
|
||||
if (r < 0)
|
||||
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);
|
||||
if (!addr)
|
||||
a = new(DHCP6Address, 1);
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
LIST_INIT(addresses, addr);
|
||||
memcpy(&addr->iaaddr, option->data, sizeof(addr->iaaddr));
|
||||
|
||||
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);
|
||||
LIST_INIT(addresses, a);
|
||||
memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix));
|
||||
|
||||
*ret = a;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_option_parse_ia(
|
||||
sd_dhcp6_client *client,
|
||||
DHCP6Option *iaoption,
|
||||
be32_t iaid,
|
||||
DHCP6IA *ia,
|
||||
uint16_t *ret_status_code) {
|
||||
uint16_t option_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;
|
||||
uint16_t iatype, optlen;
|
||||
size_t iaaddr_offset;
|
||||
int r = 0, status;
|
||||
size_t i, len;
|
||||
uint16_t opt;
|
||||
_cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {};
|
||||
uint32_t lt_t1, lt_t2, lt_min = UINT32_MAX;
|
||||
be32_t received_iaid;
|
||||
size_t offset;
|
||||
int r;
|
||||
|
||||
assert_return(ia, -EINVAL);
|
||||
assert_return(!ia->addresses, -EINVAL);
|
||||
assert(IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA, SD_DHCP6_OPTION_IA_PD));
|
||||
assert(option_data);
|
||||
assert(ret);
|
||||
|
||||
iatype = be16toh(iaoption->code);
|
||||
len = be16toh(iaoption->len);
|
||||
/* This will return the following:
|
||||
* -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:
|
||||
|
||||
if (len < DHCP6_OPTION_IA_NA_LEN)
|
||||
return -ENOBUFS;
|
||||
if (option_data_len < DHCP6_OPTION_IA_NA_LEN)
|
||||
return -EBADMSG;
|
||||
|
||||
/* According to RFC8415, IAs which do not match the client's IAID should be ignored,
|
||||
* 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);
|
||||
offset = DHCP6_OPTION_IA_NA_LEN;
|
||||
|
||||
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;
|
||||
|
||||
case SD_DHCP6_OPTION_IA_PD:
|
||||
|
||||
if (len < sizeof(ia->ia_pd))
|
||||
return -ENOBUFS;
|
||||
if (option_data_len < DHCP6_OPTION_IA_PD_LEN)
|
||||
return -EBADMSG;
|
||||
|
||||
/* According to RFC8415, IAs which do not match the client's IAID should be ignored,
|
||||
* 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);
|
||||
offset = DHCP6_OPTION_IA_PD_LEN;
|
||||
|
||||
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;
|
||||
|
||||
case SD_DHCP6_OPTION_IA_TA:
|
||||
if (len < DHCP6_OPTION_IA_TA_LEN)
|
||||
return -ENOBUFS;
|
||||
if (option_data_len < DHCP6_OPTION_IA_TA_LEN)
|
||||
return -ENOMSG;
|
||||
|
||||
/* According to RFC8415, IAs which do not match the client's IAID should be ignored,
|
||||
* 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));
|
||||
offset = DHCP6_OPTION_IA_TA_LEN;
|
||||
|
||||
received_iaid = ((const struct ia_ta*) option_data)->id;
|
||||
lt_t1 = lt_t2 = 0; /* No lifetime for IA_TA. */
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
ia->type = iatype;
|
||||
i = iaaddr_offset;
|
||||
/* According to RFC8415, IAs which do not match the client's IAID should be ignored,
|
||||
* 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) {
|
||||
DHCP6Option *option = (DHCP6Option *)&iaoption->data[i];
|
||||
if (lt_t1 > lt_t2)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received an IA option with T1 %"PRIu32"sec > T2 %"PRIu32"sec, ignoring.",
|
||||
lt_t1, lt_t2);
|
||||
|
||||
if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len))
|
||||
return -ENOBUFS;
|
||||
for (; offset < option_data_len;) {
|
||||
const uint8_t *subdata;
|
||||
size_t subdata_len;
|
||||
uint16_t subopt;
|
||||
|
||||
opt = be16toh(option->code);
|
||||
optlen = be16toh(option->len);
|
||||
r = dhcp6_option_parse(option_data, option_data_len, &offset, &subopt, &subdata_len, &subdata);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
switch (opt) {
|
||||
case SD_DHCP6_OPTION_IAADDR:
|
||||
switch (subopt) {
|
||||
case SD_DHCP6_OPTION_IAADDR: {
|
||||
DHCP6Address *a;
|
||||
|
||||
if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA))
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"IA Address option not in IA NA or TA option");
|
||||
|
||||
r = dhcp6_option_parse_address(client, option, ia, <_valid);
|
||||
if (r < 0 && r != -EINVAL)
|
||||
return r;
|
||||
if (r >= 0 && lt_valid < lt_min)
|
||||
lt_min = lt_valid;
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_IA_PD_PREFIX:
|
||||
|
||||
if (ia->type != SD_DHCP6_OPTION_IA_PD)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"IA PD Prefix option not in IA PD option");
|
||||
|
||||
r = dhcp6_option_parse_pdprefix(client, option, ia, <_valid);
|
||||
if (r < 0 && r != -EINVAL)
|
||||
return r;
|
||||
if (r >= 0 && lt_valid < lt_min)
|
||||
lt_min = lt_valid;
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_STATUS_CODE:
|
||||
|
||||
status = dhcp6_option_parse_status(option, optlen + offsetof(DHCP6Option, data));
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
if (status > 0) {
|
||||
if (ret_status_code)
|
||||
*ret_status_code = status;
|
||||
|
||||
log_dhcp6_client(client, "IA status %s",
|
||||
dhcp6_message_status_to_string(status));
|
||||
|
||||
return 0;
|
||||
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;
|
||||
}
|
||||
|
||||
break;
|
||||
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;
|
||||
}
|
||||
case SD_DHCP6_OPTION_IA_PD_PREFIX: {
|
||||
DHCP6Address *a;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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),
|
||||
"Received an IA option with non-zero status: %s%s%s",
|
||||
strempty(msg), isempty(msg) ? "" : ": ",
|
||||
dhcp6_message_status_to_string(r));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
log_dhcp6_client(client, "Unknown IA option %d", opt);
|
||||
break;
|
||||
log_dhcp6_client(client, "Received an IA option with an unknown sub-option %u, ignoring", subopt);
|
||||
}
|
||||
|
||||
i += sizeof(*option) + optlen;
|
||||
}
|
||||
|
||||
switch(iatype) {
|
||||
if (!ia.addresses)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENODATA),
|
||||
"Received an IA option without valid IA addresses or PD prefixes, ignoring.");
|
||||
|
||||
if (IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_PD) &&
|
||||
lt_t1 == 0 && lt_t2 == 0 && lt_min != UINT32_MAX) {
|
||||
lt_t1 = lt_min / 2;
|
||||
lt_t2 = lt_min / 10 * 8;
|
||||
|
||||
log_dhcp6_client(client, "Received an IA option with both T1 and T2 equal to zero. "
|
||||
"Adjusting them based on the minimum valid lifetime of IA addresses or PD prefixes: "
|
||||
"T1=%"PRIu32"sec, T2=%"PRIu32"sec", lt_t1, lt_t2);
|
||||
}
|
||||
|
||||
switch(option_code) {
|
||||
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);
|
||||
}
|
||||
|
||||
*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:
|
||||
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);
|
||||
}
|
||||
|
||||
*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:
|
||||
break;
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (ret_status_code)
|
||||
*ret_status_code = 0;
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
|
||||
struct in6_addr **addrs, size_t count) {
|
||||
int dhcp6_option_parse_addresses(
|
||||
const uint8_t *optval,
|
||||
size_t optlen,
|
||||
struct in6_addr **addrs,
|
||||
size_t *count) {
|
||||
|
||||
assert(optval);
|
||||
assert(addrs);
|
||||
assert(count);
|
||||
|
||||
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;
|
||||
|
||||
memcpy(*addrs + count, optval, optlen);
|
||||
memcpy(*addrs + *count, optval, optlen);
|
||||
*count += optlen / sizeof(struct in6_addr);
|
||||
|
||||
count += optlen / sizeof(struct in6_addr);
|
||||
|
||||
return count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain) {
|
||||
_cleanup_free_ char *ret = NULL;
|
||||
const uint8_t *optval = *data;
|
||||
uint16_t optlen = *len;
|
||||
bool first = true;
|
||||
size_t n = 0;
|
||||
static int parse_domain(const uint8_t **data, size_t *len, char **ret) {
|
||||
_cleanup_free_ char *domain = NULL;
|
||||
const uint8_t *optval;
|
||||
size_t optlen, n = 0;
|
||||
int r;
|
||||
|
||||
assert(data);
|
||||
assert(*data);
|
||||
assert(len);
|
||||
assert(ret);
|
||||
|
||||
optval = *data;
|
||||
optlen = *len;
|
||||
|
||||
if (optlen <= 1)
|
||||
return -ENODATA;
|
||||
|
||||
@ -754,42 +780,44 @@ static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain)
|
||||
return -EMSGSIZE;
|
||||
|
||||
/* Literal label */
|
||||
label = (const char *)optval;
|
||||
label = (const char*) optval;
|
||||
optval += 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;
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
ret[n++] = '.';
|
||||
if (n != 0)
|
||||
domain[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)
|
||||
return r;
|
||||
|
||||
n += r;
|
||||
}
|
||||
|
||||
if (n) {
|
||||
if (!GREEDY_REALLOC(ret, n + 1))
|
||||
if (n > 0) {
|
||||
if (!GREEDY_REALLOC(domain, n + 1))
|
||||
return -ENOMEM;
|
||||
ret[n] = 0;
|
||||
|
||||
domain[n] = '\0';
|
||||
}
|
||||
|
||||
*out_domain = TAKE_PTR(ret);
|
||||
*ret = TAKE_PTR(domain);
|
||||
*data = optval;
|
||||
*len = optlen;
|
||||
|
||||
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;
|
||||
int r;
|
||||
|
||||
assert(optval);
|
||||
assert(ret);
|
||||
|
||||
r = parse_domain(&optval, &optlen, &domain);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -798,39 +826,38 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char *
|
||||
if (optlen != 0)
|
||||
return -EINVAL;
|
||||
|
||||
*str = TAKE_PTR(domain);
|
||||
*ret = TAKE_PTR(domain);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen, char ***str_arr) {
|
||||
size_t idx = 0;
|
||||
int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret) {
|
||||
_cleanup_strv_free_ char **names = NULL;
|
||||
int r;
|
||||
|
||||
assert(optval);
|
||||
assert(ret);
|
||||
|
||||
if (optlen <= 1)
|
||||
return -ENODATA;
|
||||
if (optval[optlen - 1] != '\0')
|
||||
return -EINVAL;
|
||||
|
||||
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)
|
||||
return r;
|
||||
if (r == 0)
|
||||
continue;
|
||||
|
||||
r = strv_extend(&names, ret);
|
||||
r = strv_consume(&names, TAKE_PTR(name));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
*str_arr = TAKE_PTR(names);
|
||||
|
||||
return idx;
|
||||
*ret = TAKE_PTR(names);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static sd_dhcp6_option* dhcp6_option_free(sd_dhcp6_option *i) {
|
||||
|
||||
@ -1109,11 +1109,9 @@ static int client_parse_message(
|
||||
size_t len,
|
||||
sd_dhcp6_lease *lease) {
|
||||
|
||||
uint16_t ia_na_status = 0, ia_pd_status = 0;
|
||||
uint32_t lt_t1 = ~0, lt_t2 = ~0;
|
||||
uint32_t lt_t1 = UINT32_MAX, lt_t2 = UINT32_MAX;
|
||||
usec_t irt = IRT_DEFAULT;
|
||||
bool clientid = false;
|
||||
size_t pos = 0;
|
||||
int r;
|
||||
|
||||
assert(client);
|
||||
@ -1122,22 +1120,14 @@ static int client_parse_message(
|
||||
assert(lease);
|
||||
|
||||
len -= sizeof(DHCP6Message);
|
||||
for (size_t offset = 0; offset < len;) {
|
||||
uint16_t optcode;
|
||||
size_t optlen;
|
||||
const uint8_t *optval;
|
||||
|
||||
while (pos < len) {
|
||||
DHCP6Option *option = (DHCP6Option *) &message->options[pos];
|
||||
uint16_t optcode, optlen;
|
||||
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;
|
||||
r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
switch (optcode) {
|
||||
case SD_DHCP6_OPTION_CLIENTID:
|
||||
@ -1176,62 +1166,75 @@ static int client_parse_message(
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_STATUS_CODE:
|
||||
status = dhcp6_option_parse_status(option, optlen + sizeof(DHCP6Option));
|
||||
if (status < 0)
|
||||
return status;
|
||||
case SD_DHCP6_OPTION_STATUS_CODE: {
|
||||
_cleanup_free_ char *msg = NULL;
|
||||
|
||||
if (status > 0)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s Status %s",
|
||||
r = dhcp6_option_parse_status(optval, optlen, &msg);
|
||||
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_status_to_string(status));
|
||||
|
||||
strempty(msg), isempty(msg) ? "" : ": ",
|
||||
dhcp6_message_status_to_string(r));
|
||||
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) {
|
||||
log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode.");
|
||||
break;
|
||||
}
|
||||
|
||||
r = dhcp6_option_parse_ia(client, option, client->ia_pd.ia_na.id, &lease->ia, &ia_na_status);
|
||||
if (r < 0 && r != -ENOANO)
|
||||
r = dhcp6_option_parse_ia(client, client->ia_pd.ia_na.id, optcode, optlen, optval, &ia);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
if (ia_na_status == DHCP6_STATUS_NO_ADDRS_AVAIL) {
|
||||
pos += offsetof(DHCP6Option, data) + optlen;
|
||||
if (lease->ia.addresses) {
|
||||
log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lease->ia.addresses) {
|
||||
lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
|
||||
lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t2));
|
||||
}
|
||||
lease->ia = ia;
|
||||
ia = (DHCP6IA) {};
|
||||
|
||||
lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
|
||||
lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t2));
|
||||
|
||||
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) {
|
||||
log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode.");
|
||||
break;
|
||||
}
|
||||
|
||||
r = dhcp6_option_parse_ia(client, option, client->ia_pd.ia_pd.id, &lease->pd, &ia_pd_status);
|
||||
if (r < 0 && r != -ENOANO)
|
||||
r = dhcp6_option_parse_ia(client, client->ia_pd.ia_pd.id, optcode, optlen, optval, &ia);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
if (ia_pd_status == DHCP6_STATUS_NO_PREFIX_AVAIL) {
|
||||
pos += offsetof(DHCP6Option, data) + optlen;
|
||||
if (lease->pd.addresses) {
|
||||
log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lease->pd.addresses) {
|
||||
lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
|
||||
lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2));
|
||||
}
|
||||
lease->pd = ia;
|
||||
ia = (DHCP6IA) {};
|
||||
|
||||
lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
|
||||
lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2));
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
case SD_DHCP6_OPTION_RAPID_COMMIT:
|
||||
r = dhcp6_lease_set_rapid_commit(lease);
|
||||
if (r < 0)
|
||||
@ -1240,28 +1243,28 @@ static int client_parse_message(
|
||||
break;
|
||||
|
||||
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)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
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)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
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)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
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)
|
||||
return r;
|
||||
|
||||
@ -1281,13 +1284,8 @@ static int client_parse_message(
|
||||
irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC;
|
||||
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)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s has incomplete options",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
@ -1297,16 +1295,19 @@ static int client_parse_message(
|
||||
if (r < 0)
|
||||
return log_dhcp6_client_errno(client, r, "%s has no server id",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
}
|
||||
|
||||
if (lease->ia.addresses) {
|
||||
lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
|
||||
lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
|
||||
}
|
||||
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->pd.addresses) {
|
||||
lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1);
|
||||
lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2);
|
||||
if (lease->ia.addresses) {
|
||||
lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1);
|
||||
lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2);
|
||||
}
|
||||
|
||||
if (lease->pd.addresses) {
|
||||
lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1);
|
||||
lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2);
|
||||
}
|
||||
}
|
||||
|
||||
client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
|
||||
|
||||
@ -193,86 +193,69 @@ void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) {
|
||||
lease->prefix_iter = lease->pd.addresses;
|
||||
}
|
||||
|
||||
int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
|
||||
int r;
|
||||
|
||||
int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(optval, -EINVAL);
|
||||
|
||||
if (!optlen)
|
||||
if (optlen == 0)
|
||||
return 0;
|
||||
|
||||
r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->dns,
|
||||
lease->dns_count);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
lease->dns_count = r;
|
||||
|
||||
return 0;
|
||||
return dhcp6_option_parse_addresses(optval, optlen, &lease->dns, &lease->dns_count);
|
||||
}
|
||||
|
||||
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(addrs, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (lease->dns_count) {
|
||||
*addrs = lease->dns;
|
||||
return lease->dns_count;
|
||||
}
|
||||
if (!lease->dns)
|
||||
return -ENOENT;
|
||||
|
||||
return -ENOENT;
|
||||
*ret = lease->dns;
|
||||
return lease->dns_count;
|
||||
}
|
||||
|
||||
int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval,
|
||||
size_t optlen) {
|
||||
int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
|
||||
_cleanup_strv_free_ char **domains = NULL;
|
||||
int r;
|
||||
char **domains;
|
||||
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(optval, -EINVAL);
|
||||
|
||||
if (!optlen)
|
||||
if (optlen == 0)
|
||||
return 0;
|
||||
|
||||
r = dhcp6_option_parse_domainname_list(optval, optlen, &domains);
|
||||
if (r < 0)
|
||||
return 0;
|
||||
return r;
|
||||
|
||||
strv_free_and_replace(lease->domains, domains);
|
||||
lease->domains_count = 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(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;
|
||||
uint16_t subopt;
|
||||
size_t sublen;
|
||||
uint8_t *subval;
|
||||
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(optval, -EINVAL);
|
||||
|
||||
lease->ntp = mfree(lease->ntp);
|
||||
lease->ntp_count = 0;
|
||||
for (size_t offset = 0; offset < optlen;) {
|
||||
const uint8_t *subval;
|
||||
size_t sublen;
|
||||
uint16_t subopt;
|
||||
|
||||
while ((r = dhcp6_option_parse(&optval, &optlen, &subopt, &sublen,
|
||||
&subval)) >= 0) {
|
||||
int s;
|
||||
char **servers;
|
||||
r = dhcp6_option_parse(optval, optlen, &offset, &subopt, &sublen, &subval);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
switch(subopt) {
|
||||
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)
|
||||
return 0;
|
||||
|
||||
s = dhcp6_option_parse_ip6addrs(subval, sublen,
|
||||
&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);
|
||||
r = dhcp6_option_parse_addresses(subval, sublen, &lease->ntp, &lease->ntp_count);
|
||||
if (r < 0)
|
||||
return 0;
|
||||
|
||||
strv_free_and_replace(lease->ntp_fqdn, servers);
|
||||
lease->ntp_fqdn_count = r;
|
||||
return r;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (r != -ENOMSG)
|
||||
return r;
|
||||
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;
|
||||
}
|
||||
|
||||
int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
|
||||
int r;
|
||||
|
||||
int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(optval, -EINVAL);
|
||||
|
||||
if (!optlen)
|
||||
if (optlen == 0)
|
||||
return 0;
|
||||
|
||||
if (lease->ntp || lease->ntp_fqdn)
|
||||
return -EEXIST;
|
||||
|
||||
/* 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;
|
||||
/* SNTP option is defined in RFC4075, and deprecated by RFC5908. */
|
||||
return dhcp6_option_parse_addresses(optval, optlen, &lease->sntp, &lease->sntp_count);
|
||||
}
|
||||
|
||||
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) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(addrs, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (lease->ntp_count) {
|
||||
*addrs = lease->ntp;
|
||||
if (lease->ntp) {
|
||||
*ret = lease->ntp;
|
||||
return lease->ntp_count;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(ntp_fqdn, -EINVAL);
|
||||
|
||||
if (lease->ntp_fqdn_count) {
|
||||
*ntp_fqdn = lease->ntp_fqdn;
|
||||
return lease->ntp_fqdn_count;
|
||||
if (lease->sntp && !lease->ntp_fqdn) {
|
||||
/* Fallback to the deprecated SNTP option. */
|
||||
*ret = lease->sntp;
|
||||
return lease->sntp_count;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval,
|
||||
size_t optlen) {
|
||||
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (!lease->ntp_fqdn)
|
||||
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, size_t optlen) {
|
||||
int r;
|
||||
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);
|
||||
}
|
||||
|
||||
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(fqdn, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (lease->fqdn) {
|
||||
*fqdn = lease->fqdn;
|
||||
return 0;
|
||||
}
|
||||
if (!lease->fqdn)
|
||||
return -ENOENT;
|
||||
|
||||
return -ENOENT;
|
||||
*ret = lease->fqdn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
|
||||
assert(lease);
|
||||
if (!lease)
|
||||
return NULL;
|
||||
|
||||
free(lease->serverid);
|
||||
dhcp6_lease_free_ia(&lease->ia);
|
||||
dhcp6_lease_free_ia(&lease->pd);
|
||||
|
||||
free(lease->dns);
|
||||
free(lease->fqdn);
|
||||
|
||||
lease->domains = strv_free(lease->domains);
|
||||
|
||||
strv_free(lease->domains);
|
||||
free(lease->ntp);
|
||||
strv_free(lease->ntp_fqdn);
|
||||
free(lease->sntp);
|
||||
|
||||
lease->ntp_fqdn = strv_free(lease->ntp_fqdn);
|
||||
return mfree(lease);
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
6, 'f', 'o', 'o', 'b', 'a', 'r', 0 };
|
||||
r = dhcp6_option_parse_domainname_list(data, 21, &list);
|
||||
assert_se(r == 2);
|
||||
assert_se(r == 0);
|
||||
assert_se(list);
|
||||
assert_se(streq(list[0], "example.com"));
|
||||
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) {
|
||||
uint8_t packet[] = {
|
||||
'F', 'O', 'O',
|
||||
'F', 'O', 'O', 'H', 'O', 'G', 'E',
|
||||
0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x07,
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G',
|
||||
0x00, SD_DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09,
|
||||
@ -164,53 +164,66 @@ static int test_option(sd_event *e) {
|
||||
'B', 'A', 'R',
|
||||
};
|
||||
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,
|
||||
'B', 'A', 'R',
|
||||
};
|
||||
_cleanup_free_ uint8_t *buf = NULL;
|
||||
size_t offset, pos, optlen, outlen = sizeof(result);
|
||||
const uint8_t *optval;
|
||||
uint16_t optcode;
|
||||
size_t optlen;
|
||||
uint8_t *optval, *buf, *out;
|
||||
size_t zero = 0, pos = 3;
|
||||
size_t buflen = sizeof(packet), outlen = sizeof(result);
|
||||
uint8_t *out;
|
||||
|
||||
log_debug("/* %s */", __func__);
|
||||
|
||||
assert_se(buflen == outlen);
|
||||
assert_se(sizeof(packet) == sizeof(result));
|
||||
|
||||
assert_se(dhcp6_option_parse(&buf, &zero, &optcode, &optlen,
|
||||
&optval) == -ENOMSG);
|
||||
offset = 0;
|
||||
assert_se(dhcp6_option_parse(packet, 0, &offset, &optcode, &optlen, &optval) == -EBADMSG);
|
||||
|
||||
buflen -= 3;
|
||||
buf = &packet[3];
|
||||
outlen -= 3;
|
||||
out = &result[3];
|
||||
offset = 3;
|
||||
assert_se(dhcp6_option_parse(packet, 0, &offset, &optcode, &optlen, &optval) == -EBADMSG);
|
||||
|
||||
/* 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(optcode == SD_DHCP6_OPTION_ORO);
|
||||
assert_se(optlen == 7);
|
||||
assert_se(optval == buf + 11 - i);
|
||||
}
|
||||
|
||||
offset = 7;
|
||||
assert_se(dhcp6_option_parse(packet, 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(optlen == 7);
|
||||
assert_se(buflen + pos == sizeof(packet));
|
||||
assert_se(optval == packet + 11);
|
||||
|
||||
assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen,
|
||||
optval) >= 0);
|
||||
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 == 0x00);
|
||||
|
||||
assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen,
|
||||
&optval) >= 0);
|
||||
pos += 4 + optlen;
|
||||
assert_se(buf == &packet[pos]);
|
||||
assert_se(dhcp6_option_parse(packet, sizeof(packet), &offset, &optcode, &optlen, &optval) >= 0);
|
||||
|
||||
assert_se(optcode == SD_DHCP6_OPTION_VENDOR_CLASS);
|
||||
assert_se(optlen == 9);
|
||||
assert_se(buflen + pos == sizeof(packet));
|
||||
assert_se(optval == packet + 22);
|
||||
|
||||
assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen,
|
||||
optval) >= 0);
|
||||
assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen, optval) >= 0);
|
||||
|
||||
pos += 4 + optlen;
|
||||
assert_se(out == &result[pos]);
|
||||
assert_se(*out == 'B');
|
||||
|
||||
@ -236,7 +249,7 @@ static int test_option_status(sd_event *e) {
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
||||
0x01, 0x02, 0x03, 0x04, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
/* status option */
|
||||
/* IA address status option */
|
||||
0x00, 0x0d, 0x00, 0x02, 0x00, 0x01,
|
||||
};
|
||||
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,
|
||||
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
|
||||
0x01, 0x02, 0x03, 0x04, 0x0a, 0x0b, 0x0c, 0x0d,
|
||||
/* status option */
|
||||
/* IA address status option */
|
||||
0x00, 0x0d, 0x00, 0x08, 0x00, 0x00, 'f', 'o',
|
||||
'o', 'b', 'a', 'r',
|
||||
};
|
||||
@ -262,7 +275,7 @@ static int test_option_status(sd_event *e) {
|
||||
0x80, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe,
|
||||
0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00,
|
||||
/* status option */
|
||||
/* PD prefix status option */
|
||||
0x00, 0x0d, 0x00, 0x02, 0x00, 0x00,
|
||||
};
|
||||
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,
|
||||
0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00,
|
||||
/* status option */
|
||||
/* PD prefix status option */
|
||||
0x00, 0x0d, 0x00, 0x02, 0x00, 0x00,
|
||||
/* IA PD Prefix #2 */
|
||||
0x00, 0x1a, 0x00, 0x1f,
|
||||
@ -283,6 +296,7 @@ static int test_option_status(sd_event *e) {
|
||||
0x80, 0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x0l, 0xd0,
|
||||
0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00,
|
||||
/* PD prefix status option */
|
||||
0x00, 0x0d, 0x00, 0x02, 0x00, 0x00,
|
||||
};
|
||||
DHCP6Option *option;
|
||||
@ -295,62 +309,62 @@ static int test_option_status(sd_event *e) {
|
||||
memcpy(&iaid, option1 + 4, sizeof(iaid));
|
||||
|
||||
zero(ia);
|
||||
option = (DHCP6Option *)option1;
|
||||
option = (DHCP6Option*) option1;
|
||||
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);
|
||||
|
||||
r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, NULL);
|
||||
assert_se(r == 0);
|
||||
assert_se(ia.addresses == NULL);
|
||||
r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia);
|
||||
assert_se(r == -EINVAL);
|
||||
assert_se(!ia.addresses);
|
||||
|
||||
option->len = htobe16(17);
|
||||
r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, NULL);
|
||||
assert_se(r == -ENOBUFS);
|
||||
assert_se(ia.addresses == NULL);
|
||||
r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia);
|
||||
assert_se(r == -EBADMSG);
|
||||
assert_se(!ia.addresses);
|
||||
|
||||
option->len = htobe16(sizeof(DHCP6Option));
|
||||
r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, NULL);
|
||||
assert_se(r == -ENOBUFS);
|
||||
assert_se(ia.addresses == NULL);
|
||||
r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia);
|
||||
assert_se(r == -EBADMSG);
|
||||
assert_se(!ia.addresses);
|
||||
|
||||
zero(ia);
|
||||
option = (DHCP6Option *)option2;
|
||||
option = (DHCP6Option*) option2;
|
||||
assert_se(sizeof(option2) == sizeof(DHCP6Option) + be16toh(option->len));
|
||||
|
||||
r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, NULL);
|
||||
assert_se(r >= 0);
|
||||
assert_se(ia.addresses == NULL);
|
||||
r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia);
|
||||
assert_se(r == -ENODATA);
|
||||
assert_se(!ia.addresses);
|
||||
|
||||
zero(ia);
|
||||
option = (DHCP6Option *)option3;
|
||||
option = (DHCP6Option*) option3;
|
||||
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(ia.addresses != NULL);
|
||||
assert_se(ia.addresses);
|
||||
dhcp6_lease_free_ia(&ia);
|
||||
|
||||
zero(pd);
|
||||
option = (DHCP6Option *)option4;
|
||||
option = (DHCP6Option*) option4;
|
||||
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(pd.addresses != NULL);
|
||||
assert_se(pd.addresses);
|
||||
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_t2, &option4[12], 4) == 0);
|
||||
dhcp6_lease_free_ia(&pd);
|
||||
|
||||
zero(pd);
|
||||
option = (DHCP6Option *)option5;
|
||||
option = (DHCP6Option*) option5;
|
||||
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(pd.addresses != NULL);
|
||||
assert_se(pd.addresses);
|
||||
dhcp6_lease_free_ia(&pd);
|
||||
|
||||
return 0;
|
||||
@ -468,7 +482,7 @@ static int test_advertise_option(sd_event *e) {
|
||||
val = htobe32(120);
|
||||
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;
|
||||
}
|
||||
@ -496,20 +510,17 @@ static int test_advertise_option(sd_event *e) {
|
||||
|
||||
case SD_DHCP6_OPTION_DNS_SERVERS:
|
||||
assert_se(optlen == 16);
|
||||
assert_se(dhcp6_lease_set_dns(lease, optval,
|
||||
optlen) >= 0);
|
||||
assert_se(dhcp6_lease_add_dns(lease, optval, optlen) >= 0);
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_DOMAIN_LIST:
|
||||
assert_se(optlen == 11);
|
||||
assert_se(dhcp6_lease_set_domains(lease, optval,
|
||||
optlen) >= 0);
|
||||
assert_se(dhcp6_lease_add_domains(lease, optval, optlen) >= 0);
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_SNTP_SERVERS:
|
||||
assert_se(optlen == 16);
|
||||
assert_se(dhcp6_lease_set_sntp(lease, optval,
|
||||
optlen) >= 0);
|
||||
assert_se(dhcp6_lease_add_sntp(lease, optval, optlen) >= 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -665,7 +676,7 @@ static int test_client_verify_request(DHCP6Message *request, size_t len) {
|
||||
assert_se(!memcmp(optval + 8, &val, sizeof(val)));
|
||||
|
||||
/* 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;
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
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(
|
||||
sd_netlink *nl,
|
||||
const char *expected_family_name,
|
||||
@ -79,28 +115,6 @@ static int genl_family_new(
|
||||
.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);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -261,9 +275,10 @@ static int genl_family_get_by_name_internal(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_call(nl, req, 0, &reply);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (sd_netlink_call(nl, req, 0, &reply) < 0) {
|
||||
(void) genl_family_new_unsupported(nl, name, type_system);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return genl_family_new(nl, name, type_system, reply, ret);
|
||||
}
|
||||
|
||||
@ -123,8 +123,8 @@ struct sd_netlink_message {
|
||||
struct nlmsghdr *hdr;
|
||||
struct netlink_container containers[NETLINK_CONTAINER_DEPTH];
|
||||
unsigned n_containers; /* number of containers */
|
||||
uint32_t multicast_group;
|
||||
bool sealed:1;
|
||||
bool broadcast:1;
|
||||
|
||||
sd_netlink_message *next; /* next in a chain of multi-part messages */
|
||||
};
|
||||
|
||||
@ -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) {
|
||||
assert_return(m, -EINVAL);
|
||||
assert_return(m->hdr, -EINVAL);
|
||||
|
||||
assert_return(IN_SET(m->hdr->nlmsg_type,
|
||||
assert_return(m->protocol != NETLINK_ROUTE ||
|
||||
IN_SET(m->hdr->nlmsg_type,
|
||||
RTM_GETLINK, RTM_GETLINKPROP, RTM_GETADDR, RTM_GETROUTE, RTM_GETNEIGH,
|
||||
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) {
|
||||
assert_return(m, -EINVAL);
|
||||
|
||||
return m->broadcast;
|
||||
return m->multicast_group != 0;
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
void *attr_data, *data;
|
||||
void *attr_data;
|
||||
int r;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
@ -761,6 +761,8 @@ int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, siz
|
||||
return r;
|
||||
|
||||
if (ret_data) {
|
||||
void *data;
|
||||
|
||||
data = memdup(attr_data, r);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
@ -774,9 +776,34 @@ int sd_netlink_message_read_data(sd_netlink_message *m, unsigned short type, siz
|
||||
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) {
|
||||
void *attr_data;
|
||||
char *str;
|
||||
int r;
|
||||
|
||||
assert_return(m, -EINVAL);
|
||||
@ -790,6 +817,8 @@ int sd_netlink_message_read_string_strdup(sd_netlink_message *m, unsigned short
|
||||
return r;
|
||||
|
||||
if (data) {
|
||||
char *str;
|
||||
|
||||
str = strndup(attr_data, r);
|
||||
if (!str)
|
||||
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 r;
|
||||
void *attr_data;
|
||||
int r;
|
||||
|
||||
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);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else if (strnlen(attr_data, r) >= (size_t) r)
|
||||
|
||||
if (strnlen(attr_data, r) >= (size_t) r)
|
||||
return -EIO;
|
||||
|
||||
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 r;
|
||||
void *attr_data;
|
||||
int r;
|
||||
|
||||
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);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else if ((size_t) r < sizeof(uint8_t))
|
||||
|
||||
if ((size_t) r < sizeof(uint8_t))
|
||||
return -EIO;
|
||||
|
||||
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);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else if ((size_t) r < sizeof(uint16_t))
|
||||
|
||||
if ((size_t) r < sizeof(uint16_t))
|
||||
return -EIO;
|
||||
|
||||
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);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else if ((size_t) r < sizeof(uint32_t))
|
||||
|
||||
if ((size_t) r < sizeof(uint32_t))
|
||||
return -EIO;
|
||||
|
||||
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 r;
|
||||
void *attr_data;
|
||||
int r;
|
||||
|
||||
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);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else if ((size_t) r < sizeof(struct ether_addr))
|
||||
|
||||
if ((size_t) r < sizeof(struct ether_addr))
|
||||
return -EIO;
|
||||
|
||||
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 r;
|
||||
void *attr_data;
|
||||
int r;
|
||||
|
||||
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);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else if (r > HW_ADDR_MAX_SIZE)
|
||||
|
||||
if (r > HW_ADDR_MAX_SIZE)
|
||||
return -EIO;
|
||||
|
||||
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 r;
|
||||
void *attr_data;
|
||||
int r;
|
||||
|
||||
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);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else if ((size_t) r < sizeof(struct ifa_cacheinfo))
|
||||
|
||||
if ((size_t) r < sizeof(struct ifa_cacheinfo))
|
||||
return -EIO;
|
||||
|
||||
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);
|
||||
if (r < 0)
|
||||
return r;
|
||||
else if ((size_t) r < FAMILY_ADDRESS_SIZE(family))
|
||||
|
||||
if ((size_t) r < FAMILY_ADDRESS_SIZE(family))
|
||||
return -EIO;
|
||||
|
||||
if (data)
|
||||
|
||||
@ -81,13 +81,11 @@ int socket_bind(sd_netlink *nl) {
|
||||
|
||||
addrlen = sizeof(nl->sockaddr);
|
||||
|
||||
r = bind(nl->fd, &nl->sockaddr.sa, addrlen);
|
||||
/* 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;
|
||||
|
||||
r = getsockname(nl->fd, &nl->sockaddr.sa, &addrlen);
|
||||
if (r < 0)
|
||||
if (getsockname(nl->fd, &nl->sockaddr.sa, &addrlen) < 0)
|
||||
return -errno;
|
||||
|
||||
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;
|
||||
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 */
|
||||
continue;
|
||||
|
||||
@ -365,8 +363,7 @@ int socket_read_message(sd_netlink *nl) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
m->broadcast = !!group;
|
||||
|
||||
m->multicast_group = group;
|
||||
m->hdr = memdup(new_msg, new_msg->nlmsg_len);
|
||||
if (!m->hdr)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include <linux/nl80211.h>
|
||||
#include <linux/wireguard.h>
|
||||
|
||||
#include "missing_network.h"
|
||||
#include "netlink-genl.h"
|
||||
#include "netlink-types-internal.h"
|
||||
|
||||
@ -179,10 +180,15 @@ static const NLType genl_macsec_types[] = {
|
||||
|
||||
/***************** genl nl80211 type systems *****************/
|
||||
static const NLType genl_nl80211_types[] = {
|
||||
[NL80211_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 },
|
||||
[NL80211_ATTR_MAC] = { .type = NETLINK_TYPE_ETHER_ADDR },
|
||||
[NL80211_ATTR_SSID] = { .type = NETLINK_TYPE_STRING },
|
||||
[NL80211_ATTR_IFTYPE] = { .type = NETLINK_TYPE_U32 },
|
||||
[NL80211_ATTR_WIPHY] = { .type = NETLINK_TYPE_U32 },
|
||||
[NL80211_ATTR_WIPHY_NAME] = { .type = NETLINK_TYPE_STRING },
|
||||
[NL80211_ATTR_IFINDEX] = { .type = NETLINK_TYPE_U32 },
|
||||
[NL80211_ATTR_IFNAME] = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ-1 },
|
||||
[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 *****************/
|
||||
|
||||
@ -116,6 +116,16 @@ int rtnl_log_create_error(int r);
|
||||
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_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);
|
||||
|
||||
@ -81,8 +81,7 @@ int sd_netlink_new_from_fd(sd_netlink **ret, int fd) {
|
||||
|
||||
addrlen = sizeof(nl->sockaddr);
|
||||
|
||||
r = getsockname(fd, &nl->sockaddr.sa, &addrlen);
|
||||
if (r < 0)
|
||||
if (getsockname(fd, &nl->sockaddr.sa, &addrlen) < 0)
|
||||
return -errno;
|
||||
|
||||
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) {
|
||||
sd_netlink_slot *slot;
|
||||
bool found = false;
|
||||
|
||||
if (c->type != type)
|
||||
continue;
|
||||
if (c->cmd != 0 && c->cmd != cmd)
|
||||
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);
|
||||
|
||||
r = c->callback(nl, m, slot->userdata);
|
||||
@ -483,15 +492,12 @@ static int process_running(sd_netlink *nl, sd_netlink_message **ret) {
|
||||
if (!m)
|
||||
goto null_message;
|
||||
|
||||
if (sd_netlink_message_is_broadcast(m)) {
|
||||
if (sd_netlink_message_is_broadcast(m))
|
||||
r = process_match(nl, m);
|
||||
if (r != 0)
|
||||
goto null_message;
|
||||
} else {
|
||||
else
|
||||
r = process_reply(nl, m);
|
||||
if (r != 0)
|
||||
goto null_message;
|
||||
}
|
||||
if (r != 0)
|
||||
goto null_message;
|
||||
|
||||
if (ret) {
|
||||
*ret = TAKE_PTR(m);
|
||||
|
||||
@ -19,7 +19,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
assert_se(fwrite(data, size, 1, f) == 1);
|
||||
|
||||
fflush(f);
|
||||
assert_se(manager_new(&manager) >= 0);
|
||||
assert_se(manager_new(&manager, /* test_mode = */ true) >= 0);
|
||||
(void) netdev_load_one(manager, netdev_config);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
assert_se(fwrite(data, size, 1, f) == 1);
|
||||
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -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)
|
||||
return 1;
|
||||
|
||||
SET_FOREACH(a, link->addresses_foreign)
|
||||
if (l2tp_acquire_local_address_one(t, a, ret) >= 0)
|
||||
return 1;
|
||||
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
|
||||
@ -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))
|
||||
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 */
|
||||
|
||||
@ -9,10 +9,12 @@
|
||||
#include "netlink-util.h"
|
||||
#include "networkd-address-pool.h"
|
||||
#include "networkd-address.h"
|
||||
#include "networkd-dhcp-server.h"
|
||||
#include "networkd-ipv4acd.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-network.h"
|
||||
#include "networkd-queue.h"
|
||||
#include "networkd-route.h"
|
||||
#include "parse-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
@ -131,7 +133,7 @@ static int address_new_static(Network *network, const char *filename, unsigned s
|
||||
|
||||
address->network = network;
|
||||
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);
|
||||
if (r < 0)
|
||||
@ -151,23 +153,7 @@ Address *address_free(Address *address) {
|
||||
}
|
||||
|
||||
if (address->link) {
|
||||
NDiscAddress *n;
|
||||
|
||||
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 &&
|
||||
in6_addr_equal(&address->in_addr.in6, &address->link->ipv6ll_address))
|
||||
@ -181,6 +167,41 @@ Address *address_free(Address *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) {
|
||||
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);
|
||||
|
||||
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) {
|
||||
_cleanup_(address_freep) Address *dest = NULL;
|
||||
int r;
|
||||
@ -371,67 +382,19 @@ static int address_set_masquerade(Address *address, bool add) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int address_add_internal(Link *link, Set **addresses, const Address *in, Address **ret) {
|
||||
_cleanup_(address_freep) Address *address = NULL;
|
||||
static int address_add(Link *link, Address *address) {
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(addresses);
|
||||
assert(in);
|
||||
assert(address);
|
||||
|
||||
r = address_dup(in, &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);
|
||||
r = set_ensure_put(&link->addresses, &address_hash_ops, address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EEXIST;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -441,13 +404,13 @@ static int address_update(Address *address, const Address *src) {
|
||||
|
||||
assert(address);
|
||||
assert(address->link);
|
||||
assert(src);
|
||||
|
||||
link = address->link;
|
||||
|
||||
address->flags = src->flags;
|
||||
address->scope = src->scope;
|
||||
address->cinfo = src->cinfo;
|
||||
if (src) {
|
||||
address->flags = src->flags;
|
||||
address->scope = src->scope;
|
||||
address->cinfo = src->cinfo;
|
||||
}
|
||||
|
||||
if (address_is_ready(address) &&
|
||||
address->family == AF_INET6 &&
|
||||
@ -484,8 +447,8 @@ static int address_drop(Address *address) {
|
||||
bool ready;
|
||||
int r;
|
||||
|
||||
if (!address)
|
||||
return 0;
|
||||
assert(address);
|
||||
assert(address->link);
|
||||
|
||||
ready = address_is_ready(address);
|
||||
link = address->link;
|
||||
@ -494,7 +457,8 @@ static int address_drop(Address *address) {
|
||||
if (r < 0)
|
||||
log_link_warning_errno(link, r, "Failed to disable IP masquerading, ignoring: %m");
|
||||
|
||||
address_free(address);
|
||||
if (address->state == 0)
|
||||
address_free(address);
|
||||
|
||||
link_update_operstate(link, true);
|
||||
|
||||
@ -511,20 +475,12 @@ int address_get(Link *link, const Address *in, Address **ret) {
|
||||
assert(in);
|
||||
|
||||
existing = set_get(link->addresses, in);
|
||||
if (existing) {
|
||||
if (ret)
|
||||
*ret = existing;
|
||||
return 1;
|
||||
}
|
||||
if (!existing)
|
||||
return -ENOENT;
|
||||
|
||||
existing = set_get(link->addresses_foreign, in);
|
||||
if (existing) {
|
||||
if (ret)
|
||||
*ret = existing;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
if (ret)
|
||||
*ret = existing;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address **ret) {
|
||||
@ -546,27 +502,6 @@ int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address **
|
||||
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 r;
|
||||
|
||||
@ -587,11 +522,24 @@ int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned ch
|
||||
a->prefixlen = prefixlen;
|
||||
|
||||
return address_get(link, a, ret);
|
||||
}
|
||||
} else {
|
||||
Address *a;
|
||||
|
||||
if (addresses_get_ipv4_address(link->addresses, address, ret) >= 0)
|
||||
return 0;
|
||||
return addresses_get_ipv4_address(link->addresses_foreign, address, ret);
|
||||
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;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
HASHMAP_FOREACH(link, manager->links_by_index)
|
||||
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 {
|
||||
_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)
|
||||
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;
|
||||
@ -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) {
|
||||
_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(str);
|
||||
@ -648,14 +596,16 @@ static void log_address_debug(const Address *address, const char *str, const Lin
|
||||
if (!DEBUG_LOGGING)
|
||||
return;
|
||||
|
||||
(void) network_config_state_to_string_alloc(address->state, &state);
|
||||
(void) in_addr_to_string(address->family, &address->in_addr, &addr);
|
||||
if (in_addr_is_set(address->family, &address->in_addr_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);
|
||||
|
||||
log_link_debug(link, "%s address: %s%s%s/%u (valid %s, preferred %s), flags: %s",
|
||||
str, strnull(addr), peer ? " peer " : "", strempty(peer), address->prefixlen,
|
||||
log_link_debug(link, "%s %s address (%s): %s%s%s/%u (valid %s, preferred %s), flags: %s",
|
||||
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_prefered),
|
||||
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) {
|
||||
int r;
|
||||
|
||||
assert(rtnl);
|
||||
assert(m);
|
||||
assert(link);
|
||||
assert(link->address_remove_messages > 0);
|
||||
|
||||
link->address_remove_messages--;
|
||||
|
||||
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
|
||||
return 0;
|
||||
@ -715,16 +661,19 @@ static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
|
||||
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;
|
||||
Link *link;
|
||||
int r;
|
||||
|
||||
assert(address);
|
||||
assert(IN_SET(address->family, AF_INET, AF_INET6));
|
||||
assert(link);
|
||||
assert(link->ifindex > 0);
|
||||
assert(link->manager);
|
||||
assert(link->manager->rtnl);
|
||||
assert(address->link);
|
||||
assert(address->link->ifindex > 0);
|
||||
assert(address->link->manager);
|
||||
assert(address->link->manager->rtnl);
|
||||
|
||||
link = address->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");
|
||||
|
||||
link_ref(link);
|
||||
link->address_remove_messages++;
|
||||
|
||||
address_enter_removing(address);
|
||||
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) {
|
||||
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
|
||||
* without lifetime when KeepConfiguration=dhcp. So, let's check that we have
|
||||
* 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)
|
||||
continue;
|
||||
|
||||
@ -821,6 +757,7 @@ int link_drop_ipv6ll_addresses(Link *link) {
|
||||
_cleanup_(address_freep) Address *a = NULL;
|
||||
unsigned char flags, prefixlen;
|
||||
struct in6_addr address;
|
||||
Address *existing;
|
||||
int ifindex;
|
||||
|
||||
/* 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->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)
|
||||
return r;
|
||||
}
|
||||
@ -879,63 +824,80 @@ int link_drop_foreign_addresses(Link *link) {
|
||||
int k, r = 0;
|
||||
|
||||
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() */
|
||||
if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6))
|
||||
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->network && FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
|
||||
continue;
|
||||
} else if (link->network && FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
|
||||
continue;
|
||||
|
||||
if (link_is_static_address_configured(link, 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);
|
||||
if (k < 0 && r >= 0)
|
||||
r = k;
|
||||
}
|
||||
address_mark(address);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
r = k;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int link_drop_addresses(Link *link) {
|
||||
Address *address, *pool_address;
|
||||
Address *address;
|
||||
int k, r = 0;
|
||||
|
||||
assert(link);
|
||||
|
||||
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() */
|
||||
if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6))
|
||||
continue;
|
||||
|
||||
k = address_remove(address, link);
|
||||
k = address_remove(address);
|
||||
if (k < 0 && r >= 0) {
|
||||
r = k;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
union in_addr_union in_addr;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
@ -969,12 +931,6 @@ static int address_acquire(Link *link, const Address *original, Address **ret) {
|
||||
na->in_addr = in_addr;
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
@ -1063,6 +1019,25 @@ static int address_configure(
|
||||
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) {
|
||||
int r;
|
||||
|
||||
@ -1084,25 +1059,6 @@ static int static_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
|
||||
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(
|
||||
Link *link,
|
||||
Address *address,
|
||||
@ -1111,50 +1067,72 @@ int link_request_address(
|
||||
link_netlink_message_handler_t netlink_handler,
|
||||
Request **ret) {
|
||||
|
||||
Address *acquired;
|
||||
Address *acquired, *existing;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(address);
|
||||
assert(address->source != NETWORK_CONFIG_SOURCE_FOREIGN);
|
||||
|
||||
r = address_acquire(link, address, &acquired);
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "Failed to acquire an address from pool: %m");
|
||||
if (r > 0) {
|
||||
if (consume_object) {
|
||||
if (consume_object)
|
||||
address_free(address);
|
||||
consume_object = false; /* address from pool is already managed by Link. */
|
||||
}
|
||||
|
||||
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);
|
||||
r = link_queue_request(link, REQUEST_TYPE_ADDRESS, address, consume_object,
|
||||
message_counter, netlink_handler, ret);
|
||||
if (r < 0)
|
||||
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) {
|
||||
Request *req;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(address);
|
||||
assert(address->source == NETWORK_CONFIG_SOURCE_STATIC);
|
||||
|
||||
r = link_request_address(link, address, consume, &link->static_address_messages,
|
||||
static_address_handler, &req);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
req->after_configure = static_address_after_configure;
|
||||
return 0;
|
||||
return link_request_address(link, address, consume, &link->static_address_messages,
|
||||
static_address_handler, NULL);
|
||||
}
|
||||
|
||||
int link_request_static_addresses(Link *link) {
|
||||
Address *a;
|
||||
Prefix *p;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
@ -1168,52 +1146,13 @@ int link_request_static_addresses(Link *link) {
|
||||
return r;
|
||||
}
|
||||
|
||||
HASHMAP_FOREACH(p, link->network->prefixes_by_section) {
|
||||
_cleanup_(address_freep) Address *address = NULL;
|
||||
r = link_request_radv_addresses(link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
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)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (in4_addr_is_set(&link->network->dhcp_server_address)) {
|
||||
_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)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
r = link_request_dhcp_server_address(link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (link->static_address_messages == 0) {
|
||||
link->static_addresses_configured = true;
|
||||
@ -1226,41 +1165,25 @@ int link_request_static_addresses(Link *link) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int address_is_ready_to_configure(Link *link, const Address *address) {
|
||||
int r;
|
||||
|
||||
static bool address_is_ready_to_configure(Link *link, const Address *address) {
|
||||
assert(link);
|
||||
assert(address);
|
||||
|
||||
if (!link_is_ready_to_configure(link, false))
|
||||
return false;
|
||||
|
||||
if (link->address_remove_messages > 0)
|
||||
if (FLAGS_SET(address->state, NETWORK_CONFIG_STATE_PROBING))
|
||||
return false;
|
||||
|
||||
if (address_get(link, address, NULL) >= 0)
|
||||
return true;
|
||||
|
||||
/* If this is a new address, then refuse adding more than the limit */
|
||||
/* Refuse adding more than the limit */
|
||||
if (set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX)
|
||||
return log_link_warning_errno(link, SYNTHETIC_ERRNO(E2BIG),
|
||||
"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 false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int request_process_address(Request *req) {
|
||||
Address *a;
|
||||
Address *existing;
|
||||
Link *link;
|
||||
int r;
|
||||
|
||||
@ -1271,26 +1194,18 @@ int request_process_address(Request *req) {
|
||||
|
||||
link = req->link;
|
||||
|
||||
r = address_is_ready_to_configure(link, req->address);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
r = address_get(link, req->address, &existing);
|
||||
if (r < 0)
|
||||
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);
|
||||
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(). */
|
||||
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;
|
||||
}
|
||||
address_enter_configuring(existing);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -1441,10 +1356,14 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
|
||||
|
||||
switch (type) {
|
||||
case RTM_NEWADDR:
|
||||
log_address_debug(tmp, address ? "Remembering updated" : "Remembering foreign", link);
|
||||
if (!address) {
|
||||
/* An address appeared that we did not request */
|
||||
r = address_add_foreign(link, tmp, &address);
|
||||
if (address) {
|
||||
address_enter_configured(address);
|
||||
log_address_debug(address, "Remembering updated", link);
|
||||
} else {
|
||||
address_enter_configured(tmp);
|
||||
log_address_debug(tmp, "Received new", link);
|
||||
|
||||
r = address_add(link, tmp);
|
||||
if (r < 0) {
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
|
||||
@ -1453,6 +1372,8 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
|
||||
strnull(buf));
|
||||
return 0;
|
||||
}
|
||||
|
||||
address = TAKE_PTR(tmp);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
case RTM_DELADDR:
|
||||
log_address_debug(tmp, address ? "Forgetting" : "Kernel removed unknown", link);
|
||||
(void) address_drop(address);
|
||||
if (address) {
|
||||
address_enter_removed(address);
|
||||
log_address_debug(address, address->state == 0 ? "Forgetting" : "Removed", link);
|
||||
(void) address_drop(address);
|
||||
} else
|
||||
log_address_debug(tmp, "Kernel removed unknown", link);
|
||||
|
||||
break;
|
||||
|
||||
@ -1922,12 +1847,6 @@ int config_parse_duplicate_address_detection(
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool address_is_ready(const Address *a) {
|
||||
assert(a);
|
||||
|
||||
return !(a->flags & IFA_F_TENTATIVE);
|
||||
}
|
||||
|
||||
static int address_section_verify(Address *address) {
|
||||
if (section_is_invalid(address->section))
|
||||
return -EINVAL;
|
||||
|
||||
@ -21,10 +21,12 @@ typedef struct Request Request;
|
||||
typedef int (*address_ready_callback_t)(Address *address);
|
||||
|
||||
struct Address {
|
||||
Link *link;
|
||||
Network *network;
|
||||
NetworkConfigSection *section;
|
||||
|
||||
Link *link;
|
||||
NetworkConfigSource source;
|
||||
NetworkConfigState state;
|
||||
union in_addr_union provider; /* DHCP server or router address */
|
||||
|
||||
int family;
|
||||
unsigned char prefixlen;
|
||||
@ -42,8 +44,6 @@ struct Address {
|
||||
|
||||
bool scope_set:1;
|
||||
bool ip_masquerade_done:1;
|
||||
bool is_static:1; /* currently only used by IPv4ACD */
|
||||
bool acd_announced:1;
|
||||
AddressFamily duplicate_address_detection;
|
||||
sd_ipv4acd *acd;
|
||||
|
||||
@ -62,8 +62,7 @@ int address_new(Address **ret);
|
||||
Address* address_free(Address *address);
|
||||
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_remove(const Address *address, Link *link);
|
||||
bool address_equal(const Address *a1, const Address *a2);
|
||||
int address_remove(Address *address);
|
||||
int address_dup(const Address *src, Address **ret);
|
||||
bool address_is_ready(const 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 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(
|
||||
Link *link,
|
||||
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);
|
||||
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_broadcast);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_label);
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-network.h"
|
||||
#include "networkd-queue.h"
|
||||
#include "networkd-route.h"
|
||||
#include "parse-util.h"
|
||||
#include "socket-netlink.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) {
|
||||
Address *address;
|
||||
|
||||
@ -86,13 +121,21 @@ static int link_find_dhcp_server_address(Link *link, Address **ret) {
|
||||
link->network->dhcp_server_address_prefixlen, ret);
|
||||
|
||||
/* If not, then select one from static addresses. */
|
||||
SET_FOREACH(address, link->static_addresses)
|
||||
if (address->family == AF_INET &&
|
||||
!in4_addr_is_localhost(&address->in_addr.in) &&
|
||||
in4_addr_is_null(&address->in_addr_peer.in)) {
|
||||
*ret = address;
|
||||
return 0;
|
||||
}
|
||||
SET_FOREACH(address, link->addresses) {
|
||||
if (address->source != NETWORK_CONFIG_SOURCE_STATIC)
|
||||
continue;
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
@ -505,9 +548,6 @@ static bool dhcp_server_is_ready_to_configure(Link *link) {
|
||||
if (!link_has_carrier(link))
|
||||
return false;
|
||||
|
||||
if (link->address_remove_messages > 0)
|
||||
return false;
|
||||
|
||||
if (!link->static_addresses_configured)
|
||||
return false;
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ typedef struct Request Request;
|
||||
|
||||
void network_adjust_dhcp_server(Network *network);
|
||||
|
||||
int link_request_dhcp_server_address(Link *link);
|
||||
int link_request_dhcp_server(Link *link);
|
||||
int request_process_dhcp_server(Request *req);
|
||||
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
#include "sysctl-util.h"
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
static int dhcp4_release_old_lease(Link *link) {
|
||||
static int dhcp4_remove_address_and_routes(Link *link, bool only_marked) {
|
||||
Address *address;
|
||||
Route *route;
|
||||
int k, r = 0;
|
||||
|
||||
assert(link);
|
||||
|
||||
if (!link->dhcp_address_old && set_isempty(link->dhcp_routes_old))
|
||||
return 0;
|
||||
SET_FOREACH(route, link->routes) {
|
||||
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.");
|
||||
|
||||
SET_FOREACH(route, link->dhcp_routes_old) {
|
||||
k = route_remove(route, NULL, link);
|
||||
k = route_remove(route);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
|
||||
route_cancel_request(route);
|
||||
}
|
||||
|
||||
if (link->dhcp_address_old) {
|
||||
k = address_remove(link->dhcp_address_old, link);
|
||||
SET_FOREACH(address, link->addresses) {
|
||||
if (address->source != NETWORK_CONFIG_SOURCE_DHCP4)
|
||||
continue;
|
||||
if (only_marked && !address_is_marked(address))
|
||||
continue;
|
||||
|
||||
k = address_remove(address);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
|
||||
address_cancel_request(address);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
assert(link);
|
||||
|
||||
if (link->dhcp4_messages > 0) {
|
||||
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__);
|
||||
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__);
|
||||
return;
|
||||
address->callback = dhcp4_address_ready_callback;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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. */
|
||||
r = dhcp4_release_old_lease(link);
|
||||
if (r < 0) {
|
||||
link_enter_failed(link);
|
||||
return;
|
||||
}
|
||||
r = dhcp4_remove_address_and_routes(link, /* only_marked = */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_ipv4ll_stop(link->ipv4ll);
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -140,7 +162,7 @@ static int dhcp4_retry(Link *link) {
|
||||
|
||||
assert(link);
|
||||
|
||||
r = dhcp4_remove_all(link);
|
||||
r = dhcp4_remove_address_and_routes(link, /* only_marked = */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -192,19 +214,29 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li
|
||||
return 1;
|
||||
}
|
||||
|
||||
dhcp4_check_ready(link);
|
||||
r = dhcp4_check_ready(link);
|
||||
if (r < 0)
|
||||
link_enter_failed(link);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dhcp4_request_route(Route *in, Link *link) {
|
||||
_cleanup_(route_freep) Route *route = in;
|
||||
Request *req;
|
||||
struct in_addr server;
|
||||
Route *existing;
|
||||
int r;
|
||||
|
||||
assert(route);
|
||||
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;
|
||||
if (!route->protocol_set)
|
||||
route->protocol = RTPROT_DHCP;
|
||||
@ -215,20 +247,13 @@ static int dhcp4_request_route(Route *in, Link *link) {
|
||||
if (route->mtu == 0)
|
||||
route->mtu = link->network->dhcp_route_mtu;
|
||||
|
||||
r = link_has_route(link, route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
if (route_get(NULL, link, route, &existing) < 0) /* This is a new route. */
|
||||
link->dhcp4_configured = false;
|
||||
else
|
||||
route_unmark(existing);
|
||||
|
||||
r = link_request_route(link, TAKE_PTR(route), true, &link->dhcp4_messages,
|
||||
dhcp4_route_handler, &req);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
req->after_configure = dhcp4_after_route_configure;
|
||||
|
||||
return 0;
|
||||
return link_request_route(link, TAKE_PTR(route), true, &link->dhcp4_messages,
|
||||
dhcp4_route_handler, NULL);
|
||||
}
|
||||
|
||||
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) {
|
||||
struct in_addr gw = {};
|
||||
Route *rt;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
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");
|
||||
}
|
||||
assert(link->dhcp_lease);
|
||||
|
||||
r = dhcp4_request_prefix_route(link);
|
||||
if (r < 0)
|
||||
@ -768,27 +784,6 @@ static int dhcp_reset_hostname(Link *link) {
|
||||
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 k, r = 0;
|
||||
|
||||
@ -799,12 +794,7 @@ int dhcp4_lease_lost(Link *link) {
|
||||
|
||||
link->dhcp4_configured = false;
|
||||
|
||||
/* dhcp4_lease_lost() may be called during renewing IP address. */
|
||||
k = dhcp4_release_old_lease(link);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
|
||||
k = dhcp4_remove_all(link);
|
||||
k = dhcp4_remove_address_and_routes(link, /* only_marked = */ false);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
|
||||
@ -819,24 +809,7 @@ int dhcp4_lease_lost(Link *link) {
|
||||
link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
|
||||
link_dirty(link);
|
||||
|
||||
if (link->network->dhcp_send_decline) {
|
||||
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 one of the above failed. Do not request nexthops and routes. */
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -847,43 +820,6 @@ int dhcp4_lease_lost(Link *link) {
|
||||
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) {
|
||||
int r;
|
||||
|
||||
@ -896,12 +832,9 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (address_is_ready(link->dhcp_address)) {
|
||||
r = dhcp4_address_ready_callback(link->dhcp_address);
|
||||
if (r < 0)
|
||||
link_enter_failed(link);
|
||||
} else
|
||||
link->dhcp_address->callback = dhcp4_address_ready_callback;
|
||||
r = dhcp4_check_ready(link);
|
||||
if (r < 0)
|
||||
link_enter_failed(link);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -911,14 +844,12 @@ static int dhcp4_request_address(Link *link, bool announce) {
|
||||
uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
|
||||
struct in_addr address, netmask, server;
|
||||
unsigned prefixlen;
|
||||
Request *req;
|
||||
Address *existing;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->network);
|
||||
|
||||
if (!link->dhcp_lease)
|
||||
return 0;
|
||||
assert(link->dhcp_lease);
|
||||
|
||||
r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
|
||||
if (r < 0)
|
||||
@ -973,6 +904,8 @@ static int dhcp4_request_address(Link *link, bool announce) {
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
addr->source = NETWORK_CONFIG_SOURCE_DHCP4;
|
||||
addr->provider.in = server;
|
||||
addr->family = AF_INET;
|
||||
addr->in_addr.in.s_addr = address.s_addr;
|
||||
addr->cinfo.ifa_prefered = lifetime;
|
||||
@ -988,17 +921,15 @@ static int dhcp4_request_address(Link *link, bool announce) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (address_get(link, addr, NULL) < 0)
|
||||
if (address_get(link, addr, &existing) < 0) /* The address is new. */
|
||||
link->dhcp4_configured = false;
|
||||
else
|
||||
address_unmark(existing);
|
||||
|
||||
r = link_request_address(link, TAKE_PTR(addr), true, &link->dhcp4_messages,
|
||||
dhcp4_address_handler, &req);
|
||||
dhcp4_address_handler, NULL);
|
||||
if (r < 0)
|
||||
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;
|
||||
}
|
||||
@ -1008,6 +939,9 @@ static int dhcp4_request_address_and_routes(Link *link, bool announce) {
|
||||
|
||||
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);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -1016,8 +950,10 @@ static int dhcp4_request_address_and_routes(Link *link, bool announce) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
link_set_state(link, LINK_STATE_CONFIGURING);
|
||||
link_check_ready(link);
|
||||
if (!link->dhcp4_configured) {
|
||||
link_set_state(link, LINK_STATE_CONFIGURING);
|
||||
link_check_ready(link);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1372,7 +1308,9 @@ static int dhcp4_set_request_address(Link *link) {
|
||||
if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
|
||||
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)
|
||||
continue;
|
||||
if (link_address_is_dynamic(link, a))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -16,18 +16,9 @@ typedef enum DHCP6ClientStartMode {
|
||||
typedef struct Link Link;
|
||||
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_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_start(Link *link);
|
||||
int dhcp6_request_information(Link *link, int ir);
|
||||
|
||||
@ -9,42 +9,26 @@
|
||||
#include "networkd-link.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;
|
||||
|
||||
assert(link);
|
||||
assert(address);
|
||||
|
||||
if (address_get(link, address, NULL) < 0)
|
||||
return 0;
|
||||
/* Prevent form the address being freed. */
|
||||
address_enter_probing(address);
|
||||
|
||||
log_link_debug(link, "Removing address "IPV4_ADDRESS_FMT_STR", as the ACD client is stopped.",
|
||||
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
|
||||
if (!address_exists(address))
|
||||
return 0; /* Not assigned. */
|
||||
|
||||
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 static_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.",
|
||||
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));
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
log_link_debug(link, "Removing address "IPV4_ADDRESS_FMT_STR", as the ACD client is stopped.",
|
||||
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
|
||||
|
||||
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);
|
||||
r = address_remove(address);
|
||||
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));
|
||||
@ -52,7 +36,7 @@ static int static_address_on_conflict(Link *link, Address *address) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dhcp4_address_on_conflict(Link *link) {
|
||||
static int dhcp4_address_on_conflict(Link *link, Address *address) {
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
@ -71,11 +55,14 @@ static int dhcp4_address_on_conflict(Link *link) {
|
||||
if (r < 0)
|
||||
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. */
|
||||
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;
|
||||
Link *link;
|
||||
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->link);
|
||||
assert(address->family == AF_INET);
|
||||
assert(IN_SET(address->source, NETWORK_CONFIG_SOURCE_STATIC, NETWORK_CONFIG_SOURCE_DHCP4));
|
||||
|
||||
link = address->link;
|
||||
|
||||
switch (event) {
|
||||
case SD_IPV4ACD_EVENT_STOP:
|
||||
if (is_static) {
|
||||
r = static_address_on_stop(link, address);
|
||||
if (address->source == NETWORK_CONFIG_SOURCE_STATIC) {
|
||||
r = static_ipv4acd_address_remove(link, address, /* on_conflict = */ false);
|
||||
if (r < 0)
|
||||
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,
|
||||
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
|
||||
|
||||
address->acd_announced = true;
|
||||
address_cancel_probing(address);
|
||||
break;
|
||||
|
||||
case SD_IPV4ACD_EVENT_CONFLICT:
|
||||
if (is_static)
|
||||
r = static_address_on_conflict(link, address);
|
||||
log_link_warning(link, "Dropping address "IPV4_ADDRESS_FMT_STR", as an address conflict was detected.",
|
||||
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
|
||||
r = dhcp4_address_on_conflict(link);
|
||||
r = dhcp4_address_on_conflict(link, address);
|
||||
if (r < 0)
|
||||
link_enter_failed(link);
|
||||
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) {
|
||||
Manager *m = userdata;
|
||||
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;
|
||||
}
|
||||
|
||||
static int ipv4acd_configure(Link *link, const Address *a) {
|
||||
_cleanup_(address_freep) Address *address = NULL;
|
||||
int ipv4acd_configure(Address *address) {
|
||||
Link *link;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(a);
|
||||
assert(a->family == AF_INET);
|
||||
assert(address);
|
||||
assert(address->link);
|
||||
|
||||
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,
|
||||
IPV4_ADDRESS_FMT_VAL(a->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;
|
||||
IPV4_ADDRESS_FMT_VAL(address->in_addr.in));
|
||||
|
||||
r = sd_ipv4acd_new(&address->acd);
|
||||
if (r < 0)
|
||||
@ -186,9 +173,7 @@ static int ipv4acd_configure(Link *link, const Address *a) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_ipv4acd_set_callback(address->acd,
|
||||
address->is_static ? static_address_on_acd : dhcp4_address_on_acd,
|
||||
address);
|
||||
r = sd_ipv4acd_set_callback(address->acd, on_acd, address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -202,36 +187,10 @@ static int ipv4acd_configure(Link *link, const Address *a) {
|
||||
return r;
|
||||
}
|
||||
|
||||
TAKE_PTR(address);
|
||||
address_enter_probing(address);
|
||||
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) {
|
||||
Address *address;
|
||||
int k, r = 0;
|
||||
@ -243,8 +202,9 @@ int ipv4acd_update_mac(Link *link) {
|
||||
if (ether_addr_is_null(&link->hw_addr.ether))
|
||||
return 0;
|
||||
|
||||
SET_FOREACH(address, link->addresses_ipv4acd) {
|
||||
assert(address->acd);
|
||||
SET_FOREACH(address, link->addresses) {
|
||||
if (!address->acd)
|
||||
continue;
|
||||
|
||||
k = sd_ipv4acd_set_mac(address->acd, &link->hw_addr.ether);
|
||||
if (k < 0)
|
||||
@ -262,7 +222,10 @@ int ipv4acd_start(Link *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))
|
||||
continue;
|
||||
|
||||
@ -280,7 +243,10 @@ int ipv4acd_stop(Link *link) {
|
||||
|
||||
assert(link);
|
||||
|
||||
SET_FOREACH(address, link->addresses_ipv4acd) {
|
||||
SET_FOREACH(address, link->addresses) {
|
||||
if (!address->acd)
|
||||
continue;
|
||||
|
||||
k = sd_ipv4acd_stop(address->acd);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
@ -295,7 +261,10 @@ int ipv4acd_set_ifname(Link *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);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
typedef struct Address Address;
|
||||
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_start(Link *link);
|
||||
int ipv4acd_stop(Link *link);
|
||||
|
||||
@ -28,6 +28,7 @@ static int address_new_from_ipv4ll(Link *link, Address **ret) {
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
address->source = NETWORK_CONFIG_SOURCE_IPV4LL;
|
||||
address->family = AF_INET;
|
||||
address->in_addr.in = addr;
|
||||
address->prefixlen = 16;
|
||||
@ -41,6 +42,7 @@ static int address_new_from_ipv4ll(Link *link, Address **ret) {
|
||||
|
||||
static int ipv4ll_address_lost(Link *link) {
|
||||
_cleanup_(address_freep) Address *address = NULL;
|
||||
Address *existing;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
@ -53,10 +55,19 @@ static int ipv4ll_address_lost(Link *link) {
|
||||
if (r < 0)
|
||||
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,
|
||||
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) {
|
||||
|
||||
@ -48,12 +48,12 @@
|
||||
#include "networkd-nexthop.h"
|
||||
#include "networkd-queue.h"
|
||||
#include "networkd-radv.h"
|
||||
#include "networkd-route.h"
|
||||
#include "networkd-routing-policy-rule.h"
|
||||
#include "networkd-setlink.h"
|
||||
#include "networkd-sriov.h"
|
||||
#include "networkd-state-file.h"
|
||||
#include "networkd-sysctl.h"
|
||||
#include "networkd-wifi.h"
|
||||
#include "set.h"
|
||||
#include "socket-util.h"
|
||||
#include "stat-util.h"
|
||||
@ -228,29 +228,12 @@ static Link *link_free(Link *link) {
|
||||
link_dns_settings_clear(link);
|
||||
|
||||
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->neighbors = set_free(link->neighbors);
|
||||
|
||||
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);
|
||||
|
||||
@ -395,7 +378,7 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) {
|
||||
if (k < 0)
|
||||
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)
|
||||
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;
|
||||
|
||||
(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)
|
||||
@ -496,44 +479,42 @@ void link_check_ready(Link *link) {
|
||||
!in6_addr_is_set(&link->ipv6ll_address))
|
||||
return (void) log_link_debug(link, "%s(): IPv6LL is not configured yet.", __func__);
|
||||
|
||||
bool has_ndisc_address = false;
|
||||
NDiscAddress *n;
|
||||
SET_FOREACH(n, link->ndisc_addresses)
|
||||
if (!n->marked) {
|
||||
has_ndisc_address = true;
|
||||
bool has_dynamic_address = false;
|
||||
SET_FOREACH(a, link->addresses) {
|
||||
if (address_is_marked(a))
|
||||
continue;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if ((link_dhcp4_enabled(link) || link_dhcp6_with_address_enabled(link) || link_ipv4ll_enabled(link)) &&
|
||||
!link->dhcp_address && set_isempty(link->dhcp6_addresses) && !has_ndisc_address &&
|
||||
!link->ipv4ll_address_configured)
|
||||
if ((link_ipv4ll_enabled(link) || link_dhcp4_enabled(link) || link_dhcp6_with_address_enabled(link) ||
|
||||
(link_dhcp6_pd_is_enabled(link) && link->network->dhcp6_pd_assign)) && !has_dynamic_address)
|
||||
/* 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__);
|
||||
|
||||
/* 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) ||
|
||||
(!link->network->configure_without_carrier && link_ipv6_accept_ra_enabled(link)) ||
|
||||
link_ipv4ll_enabled(link)) {
|
||||
if (link_ipv4ll_enabled(link) || link_dhcp4_enabled(link) ||
|
||||
link_dhcp6_enabled(link) || link_dhcp6_pd_is_enabled(link) ||
|
||||
(!link->network->configure_without_carrier && link_ipv6_accept_ra_enabled(link))) {
|
||||
|
||||
if (!link->dhcp4_configured &&
|
||||
!(link->dhcp6_address_configured && link->dhcp6_route_configured) &&
|
||||
!(link->dhcp6_pd_address_configured && link->dhcp6_pd_route_configured) &&
|
||||
!(link->ndisc_addresses_configured && link->ndisc_routes_configured) &&
|
||||
!link->ipv4ll_address_configured)
|
||||
if (!link->ipv4ll_address_configured && !link->dhcp4_configured &&
|
||||
!link->dhcp6_configured && !link->dhcp6_pd_configured && !link->ndisc_configured)
|
||||
/* 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__);
|
||||
|
||||
log_link_debug(link, "%s(): DHCPv4:%s IPv4LL:%s DHCPv6_addresses:%s DHCPv6_routes:%s "
|
||||
"DHCPv6PD_addresses:%s DHCPv6PD_routes:%s NDisc_addresses:%s NDisc_routes:%s",
|
||||
log_link_debug(link, "%s(): IPv4LL:%s DHCPv4:%s DHCPv6:%s DHCPv6PD:%s NDisc:%s",
|
||||
__func__,
|
||||
yes_no(link->dhcp4_configured),
|
||||
yes_no(link->ipv4ll_address_configured),
|
||||
yes_no(link->dhcp6_address_configured),
|
||||
yes_no(link->dhcp6_route_configured),
|
||||
yes_no(link->dhcp6_pd_address_configured),
|
||||
yes_no(link->dhcp6_pd_route_configured),
|
||||
yes_no(link->ndisc_addresses_configured),
|
||||
yes_no(link->ndisc_routes_configured));
|
||||
yes_no(link->dhcp4_configured),
|
||||
yes_no(link->dhcp6_configured),
|
||||
yes_no(link->dhcp6_pd_configured),
|
||||
yes_no(link->ndisc_configured));
|
||||
}
|
||||
|
||||
link_set_state(link, LINK_STATE_CONFIGURED);
|
||||
@ -983,7 +964,9 @@ static Link *link_drop(Link *link) {
|
||||
|
||||
link_drop_from_master(link);
|
||||
|
||||
(void) unlink(link->state_file);
|
||||
if (link->state_file)
|
||||
(void) unlink(link->state_file);
|
||||
|
||||
link_clean(link);
|
||||
|
||||
STRV_FOREACH(n, link->alternative_names)
|
||||
@ -1295,28 +1278,15 @@ static int link_reconfigure_impl(Link *link, bool force) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int link_reconfigure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, bool force, bool update_wifi) {
|
||||
bool link_was_lower_up;
|
||||
static int link_reconfigure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, bool force) {
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
link_was_lower_up = link->flags & IFF_LOWER_UP;
|
||||
|
||||
r = link_getlink_handler_internal(rtnl, m, link, "Failed to update link state");
|
||||
if (r <= 0)
|
||||
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);
|
||||
if (r < 0) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
@ -1339,7 +1309,7 @@ static int link_reconfigure_after_sleep_handler(sd_netlink *rtnl, sd_netlink_mes
|
||||
|
||||
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)
|
||||
return r;
|
||||
|
||||
@ -1404,6 +1374,12 @@ static int link_initialized_and_synced(Link *link) {
|
||||
assert(link->ifname);
|
||||
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,
|
||||
* or directly from link_check_initialized() if running in a container. */
|
||||
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;
|
||||
|
||||
if (!link->network) {
|
||||
r = wifi_get_info(link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = link_get_network(link, &network);
|
||||
if (r == -ENOENT) {
|
||||
link_set_state(link, LINK_STATE_UNMANAGED);
|
||||
@ -1637,11 +1609,7 @@ static int link_carrier_lost(Link *link) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = link_drop_config(link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return link_drop_foreign_config(link);
|
||||
return link_drop_config(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);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
ipv6_address_state = address_state_from_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) {
|
||||
bool link_was_lower_up, link_was_admin_up, had_carrier;
|
||||
bool link_was_admin_up, had_carrier;
|
||||
uint8_t operstate;
|
||||
unsigned flags;
|
||||
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);
|
||||
}
|
||||
|
||||
link_was_lower_up = link->flags & IFF_LOWER_UP;
|
||||
link_was_admin_up = link->flags & IFF_UP;
|
||||
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);
|
||||
|
||||
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)) {
|
||||
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)) {
|
||||
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);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -2381,14 +2332,17 @@ 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");
|
||||
}
|
||||
|
||||
if (asprintf(&state_file, "/run/systemd/netif/links/%d", ifindex) < 0)
|
||||
return log_oom_debug();
|
||||
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)
|
||||
return log_oom_debug();
|
||||
|
||||
if (asprintf(&lease_file, "/run/systemd/netif/leases/%d", ifindex) < 0)
|
||||
return log_oom_debug();
|
||||
if (asprintf(&lease_file, "/run/systemd/netif/leases/%d", ifindex) < 0)
|
||||
return log_oom_debug();
|
||||
|
||||
if (asprintf(&lldp_file, "/run/systemd/netif/lldp/%d", ifindex) < 0)
|
||||
return log_oom_debug();
|
||||
if (asprintf(&lldp_file, "/run/systemd/netif/lldp/%d", ifindex) < 0)
|
||||
return log_oom_debug();
|
||||
}
|
||||
|
||||
link = new(Link, 1);
|
||||
if (!link)
|
||||
|
||||
@ -39,7 +39,6 @@ typedef enum LinkState {
|
||||
|
||||
typedef struct Manager Manager;
|
||||
typedef struct Network Network;
|
||||
typedef struct Address Address;
|
||||
typedef struct DUID DUID;
|
||||
|
||||
typedef struct Link {
|
||||
@ -92,8 +91,6 @@ typedef struct Link {
|
||||
unsigned static_nexthop_messages;
|
||||
unsigned static_route_messages;
|
||||
unsigned static_routing_policy_rule_messages;
|
||||
unsigned address_remove_messages;
|
||||
unsigned route_remove_messages;
|
||||
unsigned tc_messages;
|
||||
unsigned sr_iov_messages;
|
||||
unsigned set_link_messages;
|
||||
@ -102,19 +99,12 @@ typedef struct Link {
|
||||
unsigned create_stacked_netdev_after_configured_messages;
|
||||
|
||||
Set *addresses;
|
||||
Set *addresses_foreign;
|
||||
Set *addresses_ipv4acd;
|
||||
Set *pool_addresses;
|
||||
Set *static_addresses;
|
||||
Set *neighbors;
|
||||
Set *routes;
|
||||
Set *routes_foreign;
|
||||
Set *nexthops;
|
||||
|
||||
sd_dhcp_client *dhcp_client;
|
||||
sd_dhcp_lease *dhcp_lease;
|
||||
Address *dhcp_address, *dhcp_address_old;
|
||||
Set *dhcp_routes, *dhcp_routes_old;
|
||||
char *lease_file;
|
||||
unsigned dhcp4_messages;
|
||||
bool dhcp4_route_failed:1;
|
||||
@ -145,31 +135,18 @@ typedef struct Link {
|
||||
sd_ndisc *ndisc;
|
||||
Set *ndisc_rdnss;
|
||||
Set *ndisc_dnssl;
|
||||
Set *ndisc_addresses;
|
||||
Set *ndisc_routes;
|
||||
unsigned ndisc_addresses_messages;
|
||||
unsigned ndisc_routes_messages;
|
||||
bool ndisc_addresses_configured:1;
|
||||
bool ndisc_routes_configured:1;
|
||||
unsigned ndisc_messages;
|
||||
bool ndisc_configured:1;
|
||||
|
||||
sd_radv *radv;
|
||||
|
||||
sd_dhcp6_client *dhcp6_client;
|
||||
sd_dhcp6_lease *dhcp6_lease;
|
||||
Set *dhcp6_addresses, *dhcp6_addresses_old;
|
||||
Set *dhcp6_routes, *dhcp6_routes_old;
|
||||
Set *dhcp6_pd_prefixes;
|
||||
Set *dhcp6_pd_addresses, *dhcp6_pd_addresses_old;
|
||||
Set *dhcp6_pd_routes, *dhcp6_pd_routes_old;
|
||||
unsigned dhcp6_address_messages;
|
||||
unsigned dhcp6_route_messages;
|
||||
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;
|
||||
unsigned dhcp6_messages;
|
||||
unsigned dhcp6_pd_messages;
|
||||
bool dhcp6_configured:1;
|
||||
bool dhcp6_pd_configured:1;
|
||||
|
||||
/* This is about LLDP reception */
|
||||
sd_lldp_rx *lldp_rx;
|
||||
|
||||
@ -114,7 +114,9 @@ int link_lldp_save(Link *link) {
|
||||
int n = 0, r, i;
|
||||
|
||||
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) {
|
||||
(void) unlink(link->lldp_file);
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#include <linux/if.h>
|
||||
#include <linux/fib_rules.h>
|
||||
#include <linux/nexthop.h>
|
||||
#include <linux/nl80211.h>
|
||||
|
||||
#include "sd-daemon.h"
|
||||
#include "sd-netlink.h"
|
||||
@ -26,6 +27,7 @@
|
||||
#include "netlink-util.h"
|
||||
#include "network-internal.h"
|
||||
#include "networkd-address-pool.h"
|
||||
#include "networkd-address.h"
|
||||
#include "networkd-dhcp-server-bus.h"
|
||||
#include "networkd-dhcp6.h"
|
||||
#include "networkd-link-bus.h"
|
||||
@ -35,9 +37,11 @@
|
||||
#include "networkd-network-bus.h"
|
||||
#include "networkd-nexthop.h"
|
||||
#include "networkd-queue.h"
|
||||
#include "networkd-route.h"
|
||||
#include "networkd-routing-policy-rule.h"
|
||||
#include "networkd-speed-meter.h"
|
||||
#include "networkd-state-file.h"
|
||||
#include "networkd-wifi.h"
|
||||
#include "ordered-set.h"
|
||||
#include "path-lookup.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;
|
||||
}
|
||||
|
||||
int manager_connect_bus(Manager *m) {
|
||||
static int manager_connect_bus(Manager *m) {
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
||||
if (m->bus)
|
||||
return 0;
|
||||
assert(!m->bus);
|
||||
|
||||
r = bus_open_system_watch_bind_with_description(&m->bus, "bus-api-network");
|
||||
if (r < 0)
|
||||
@ -248,6 +250,16 @@ static int manager_connect_genl(Manager *m) {
|
||||
if (r < 0)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
int manager_new(Manager **ret) {
|
||||
_cleanup_(manager_freep) Manager *m = NULL;
|
||||
int manager_setup(Manager *m) {
|
||||
int r;
|
||||
|
||||
m = new(Manager, 1);
|
||||
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;
|
||||
assert(m);
|
||||
|
||||
r = sd_event_default(&m->event);
|
||||
if (r < 0)
|
||||
@ -422,6 +416,13 @@ int manager_new(Manager **ret) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (m->test_mode)
|
||||
return 0;
|
||||
|
||||
r = manager_connect_bus(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = manager_connect_udev(m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -438,11 +439,36 @@ int manager_new(Manager **ret) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(m);
|
||||
m->state_file = strdup("/run/systemd/netif/state");
|
||||
if (!m->state_file)
|
||||
return -ENOMEM;
|
||||
|
||||
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) {
|
||||
Link *link;
|
||||
|
||||
@ -456,9 +482,6 @@ Manager* manager_free(Manager *m) {
|
||||
|
||||
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->links_by_name = hashmap_free(m->links_by_name);
|
||||
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(
|
||||
Manager *m,
|
||||
sd_netlink *nl,
|
||||
sd_netlink_message *req,
|
||||
int (*process)(sd_netlink *, sd_netlink_message *, Manager *),
|
||||
const char *name) {
|
||||
int (*process)(sd_netlink *, sd_netlink_message *, Manager *)) {
|
||||
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *reply = NULL;
|
||||
int r;
|
||||
int k, r;
|
||||
|
||||
assert(m);
|
||||
assert(m->rtnl);
|
||||
assert(nl);
|
||||
assert(req);
|
||||
assert(process);
|
||||
|
||||
@ -572,22 +595,14 @@ static int manager_enumerate_internal(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_netlink_call(m->rtnl, req, 0, &reply);
|
||||
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;
|
||||
}
|
||||
|
||||
r = sd_netlink_call(nl, req, 0, &reply);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
for (sd_netlink_message *reply_one = reply; reply_one; reply_one = sd_netlink_message_next(reply_one)) {
|
||||
int k;
|
||||
|
||||
m->enumerating = true;
|
||||
|
||||
k = process(m->rtnl, reply_one, m);
|
||||
k = process(nl, reply_one, m);
|
||||
if (k < 0 && r >= 0)
|
||||
r = k;
|
||||
|
||||
@ -608,7 +623,7 @@ static int manager_enumerate_links(Manager *m) {
|
||||
if (r < 0)
|
||||
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) {
|
||||
@ -622,7 +637,7 @@ static int manager_enumerate_addresses(Manager *m) {
|
||||
if (r < 0)
|
||||
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) {
|
||||
@ -636,7 +651,7 @@ static int manager_enumerate_neighbors(Manager *m) {
|
||||
if (r < 0)
|
||||
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) {
|
||||
@ -653,7 +668,7 @@ static int manager_enumerate_routes(Manager *m) {
|
||||
if (r < 0)
|
||||
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) {
|
||||
@ -670,7 +685,7 @@ static int manager_enumerate_rules(Manager *m) {
|
||||
if (r < 0)
|
||||
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) {
|
||||
@ -684,7 +699,50 @@ static int manager_enumerate_nexthop(Manager *m) {
|
||||
if (r < 0)
|
||||
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) {
|
||||
@ -702,18 +760,37 @@ int manager_enumerate(Manager *m) {
|
||||
if (r < 0)
|
||||
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);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Could not enumerate nexthop rules: %m");
|
||||
if (r == -EOPNOTSUPP || (r == -EINVAL && mac_selinux_enforcing()))
|
||||
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);
|
||||
if (r < 0)
|
||||
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);
|
||||
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");
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -28,6 +28,7 @@ struct Manager {
|
||||
Hashmap *polkit_registry;
|
||||
int ethtool_fd;
|
||||
|
||||
bool test_mode;
|
||||
bool enumerating;
|
||||
bool dirty;
|
||||
bool restarting;
|
||||
@ -49,8 +50,6 @@ struct Manager {
|
||||
Hashmap *links_by_hw_addr;
|
||||
Hashmap *netdevs;
|
||||
OrderedHashmap *networks;
|
||||
Hashmap *dhcp6_prefixes;
|
||||
Set *dhcp6_pd_prefixes;
|
||||
OrderedSet *address_pools;
|
||||
|
||||
usec_t network_dirs_ts_usec;
|
||||
@ -96,10 +95,10 @@ struct Manager {
|
||||
OrderedSet *request_queue;
|
||||
};
|
||||
|
||||
int manager_new(Manager **ret);
|
||||
int manager_new(Manager **ret, bool test_mode);
|
||||
Manager* manager_free(Manager *m);
|
||||
|
||||
int manager_connect_bus(Manager *m);
|
||||
int manager_setup(Manager *m);
|
||||
int manager_start(Manager *m);
|
||||
|
||||
int manager_load_config(Manager *m);
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-ndisc.h"
|
||||
#include "networkd-queue.h"
|
||||
#include "networkd-route.h"
|
||||
#include "networkd-state-file.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
@ -96,115 +97,65 @@ void network_adjust_ipv6_accept_ra(Network *network) {
|
||||
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_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;
|
||||
static int ndisc_remove(Link *link, struct in6_addr *router) {
|
||||
bool updated = false;
|
||||
NDiscDNSSL *dnssl;
|
||||
NDiscRDNSS *rdnss;
|
||||
Address *address;
|
||||
Route *route;
|
||||
int k, r = 0;
|
||||
bool updated = false;
|
||||
|
||||
assert(link);
|
||||
assert(router);
|
||||
|
||||
if (!force) {
|
||||
bool set_callback = false;
|
||||
SET_FOREACH(route, link->routes) {
|
||||
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)
|
||||
if (!na->marked && in6_addr_equal(&na->router, router)) {
|
||||
set_callback = true;
|
||||
break;
|
||||
}
|
||||
k = route_remove(route);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
|
||||
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;
|
||||
}
|
||||
route_cancel_request(route);
|
||||
}
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
_cleanup_free_ char *buf = NULL;
|
||||
SET_FOREACH(address, link->addresses) {
|
||||
if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
|
||||
continue;
|
||||
if (!address_is_marked(address))
|
||||
continue;
|
||||
if (router && !in6_addr_equal(router, &address->provider.in6))
|
||||
continue;
|
||||
|
||||
(void) in6_addr_to_string(router, &buf);
|
||||
log_link_debug(link, "Removing old NDisc information obtained from %s.", strna(buf));
|
||||
k = address_remove(address);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
|
||||
address_cancel_request(address);
|
||||
}
|
||||
|
||||
SET_FOREACH(na, link->ndisc_addresses)
|
||||
if (na->marked && in6_addr_equal(&na->router, router)) {
|
||||
k = address_remove(na->address, link);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
}
|
||||
SET_FOREACH(rdnss, link->ndisc_rdnss) {
|
||||
if (!rdnss->marked)
|
||||
continue;
|
||||
if (router && !in6_addr_equal(router, &rdnss->router))
|
||||
continue;
|
||||
|
||||
SET_FOREACH(nr, link->ndisc_routes)
|
||||
if (nr->marked && in6_addr_equal(&nr->router, router)) {
|
||||
k = route_remove(nr->route, NULL, link);
|
||||
if (k < 0)
|
||||
r = k;
|
||||
}
|
||||
free(set_remove(link->ndisc_rdnss, rdnss));
|
||||
updated = true;
|
||||
}
|
||||
|
||||
SET_FOREACH(rdnss, link->ndisc_rdnss)
|
||||
if (rdnss->marked && in6_addr_equal(&rdnss->router, router)) {
|
||||
free(set_remove(link->ndisc_rdnss, rdnss));
|
||||
updated = true;
|
||||
}
|
||||
SET_FOREACH(dnssl, link->ndisc_dnssl) {
|
||||
if (!dnssl->marked)
|
||||
continue;
|
||||
if (router && !in6_addr_equal(router, &dnssl->router))
|
||||
continue;
|
||||
|
||||
SET_FOREACH(dnssl, link->ndisc_dnssl)
|
||||
if (dnssl->marked && in6_addr_equal(&dnssl->router, router)) {
|
||||
free(set_remove(link->ndisc_dnssl, dnssl));
|
||||
updated = true;
|
||||
}
|
||||
free(set_remove(link->ndisc_dnssl, dnssl));
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (updated)
|
||||
link_dirty(link);
|
||||
@ -212,197 +163,100 @@ static int ndisc_remove_old_one(Link *link, const struct in6_addr *router, bool
|
||||
return r;
|
||||
}
|
||||
|
||||
static int ndisc_remove_old(Link *link) {
|
||||
_cleanup_set_free_free_ Set *routers = NULL;
|
||||
_cleanup_free_ struct in6_addr *router = NULL;
|
||||
struct in6_addr *a;
|
||||
NDiscAddress *na;
|
||||
NDiscRoute *nr;
|
||||
NDiscDNSSL *dnssl;
|
||||
NDiscRDNSS *rdnss;
|
||||
int k, r;
|
||||
static int ndisc_check_ready(Link *link);
|
||||
|
||||
static int ndisc_address_ready_callback(Address *address) {
|
||||
Address *a;
|
||||
|
||||
assert(address);
|
||||
assert(address->link);
|
||||
|
||||
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);
|
||||
|
||||
if (link->ndisc_addresses_messages > 0 ||
|
||||
link->ndisc_routes_messages > 0)
|
||||
if (link->ndisc_messages > 0) {
|
||||
log_link_debug(link, "%s(): SLAAC addresses and routes are not set.", __func__);
|
||||
return 0;
|
||||
|
||||
routers = set_new(&in6_addr_hash_ops);
|
||||
if (!routers)
|
||||
return -ENOMEM;
|
||||
|
||||
SET_FOREACH(na, link->ndisc_addresses)
|
||||
if (!set_contains(routers, &na->router)) {
|
||||
router = newdup(struct in6_addr, &na->router, 1);
|
||||
if (!router)
|
||||
return -ENOMEM;
|
||||
|
||||
r = set_put(routers, router);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
assert(r > 0);
|
||||
TAKE_PTR(router);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
SET_FOREACH(address, link->addresses) {
|
||||
if (address->source != NETWORK_CONFIG_SOURCE_NDISC)
|
||||
continue;
|
||||
|
||||
static void ndisc_route_hash_func(const NDiscRoute *x, struct siphash *state) {
|
||||
route_hash_func(x->route, state);
|
||||
}
|
||||
found = true;
|
||||
|
||||
static int ndisc_route_compare_func(const NDiscRoute *a, const NDiscRoute *b) {
|
||||
return route_compare_func(a->route, b->route);
|
||||
}
|
||||
if (address_is_ready(address)) {
|
||||
ready = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
|
||||
ndisc_route_hash_ops,
|
||||
NDiscRoute,
|
||||
ndisc_route_hash_func,
|
||||
ndisc_route_compare_func,
|
||||
free);
|
||||
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)
|
||||
return r;
|
||||
|
||||
link_check_ready(link);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
|
||||
int r;
|
||||
|
||||
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");
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (link->ndisc_routes_messages == 0) {
|
||||
log_link_debug(link, "NDisc routes set.");
|
||||
link->ndisc_routes_configured = true;
|
||||
|
||||
r = ndisc_remove_old(link);
|
||||
if (r < 0) {
|
||||
link_enter_failed(link);
|
||||
return 1;
|
||||
}
|
||||
|
||||
link_check_ready(link);
|
||||
}
|
||||
r = ndisc_check_ready(link);
|
||||
if (r < 0)
|
||||
link_enter_failed(link);
|
||||
|
||||
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) {
|
||||
_cleanup_(route_freep) Route *route = in;
|
||||
Request *req;
|
||||
struct in6_addr router;
|
||||
Route *existing;
|
||||
int r;
|
||||
|
||||
assert(route);
|
||||
assert(link);
|
||||
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)
|
||||
route->table = link_get_ipv6_accept_ra_route_table(link);
|
||||
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)
|
||||
route->protocol = RTPROT_RA;
|
||||
|
||||
r = link_has_route(link, route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
link->ndisc_routes_configured = false;
|
||||
if (route_get(NULL, link, route, &existing) < 0)
|
||||
link->ndisc_configured = false;
|
||||
else
|
||||
route_unmark(existing);
|
||||
|
||||
r = link_request_route(link, TAKE_PTR(route), true, &link->ndisc_routes_messages,
|
||||
ndisc_route_handler, &req);
|
||||
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;
|
||||
return link_request_route(link, TAKE_PTR(route), true, &link->ndisc_messages,
|
||||
ndisc_route_handler, NULL);
|
||||
}
|
||||
|
||||
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) {
|
||||
int r;
|
||||
|
||||
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");
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (link->ndisc_addresses_messages == 0) {
|
||||
log_link_debug(link, "NDisc SLAAC addresses set.");
|
||||
link->ndisc_addresses_configured = true;
|
||||
|
||||
r = ndisc_remove_old(link);
|
||||
if (r < 0) {
|
||||
link_enter_failed(link);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
r = ndisc_check_ready(link);
|
||||
if (r < 0)
|
||||
link_enter_failed(link);
|
||||
|
||||
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) {
|
||||
_cleanup_(address_freep) Address *address = in;
|
||||
Request *req;
|
||||
struct in6_addr router;
|
||||
Address *existing;
|
||||
int r;
|
||||
|
||||
assert(address);
|
||||
assert(link);
|
||||
assert(rt);
|
||||
|
||||
if (address_get(link, address, NULL) < 0)
|
||||
link->ndisc_addresses_configured = false;
|
||||
|
||||
r = link_request_address(link, TAKE_PTR(address), true, &link->ndisc_addresses_messages,
|
||||
ndisc_address_handler, &req);
|
||||
if (r <= 0)
|
||||
r = sd_ndisc_router_get_address(rt, &router);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
req->userdata = sd_ndisc_router_ref(rt);
|
||||
req->after_configure = ndisc_after_address_configure;
|
||||
req->on_free = ndisc_request_on_free;
|
||||
address->source = NETWORK_CONFIG_SOURCE_NDISC;
|
||||
address->provider.in6 = router;
|
||||
|
||||
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) {
|
||||
@ -987,7 +765,6 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
|
||||
uint32_t lifetime;
|
||||
const struct in6_addr *a;
|
||||
struct in6_addr router;
|
||||
NDiscRDNSS *rdnss;
|
||||
usec_t time_now;
|
||||
bool updated = false;
|
||||
int n, r;
|
||||
@ -1011,10 +788,6 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
|
||||
if (n < 0)
|
||||
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)
|
||||
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++) {
|
||||
_cleanup_free_ NDiscRDNSS *x = NULL;
|
||||
NDiscRDNSS d = {
|
||||
NDiscRDNSS *rdnss, d = {
|
||||
.address = a[j],
|
||||
};
|
||||
|
||||
@ -1081,7 +854,6 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
|
||||
struct in6_addr router;
|
||||
uint32_t lifetime;
|
||||
usec_t time_now;
|
||||
NDiscDNSSL *dnssl;
|
||||
bool updated = false;
|
||||
char **j;
|
||||
int r;
|
||||
@ -1105,10 +877,6 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
|
||||
if (r < 0)
|
||||
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)
|
||||
return 0;
|
||||
|
||||
@ -1120,6 +888,7 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
|
||||
|
||||
STRV_FOREACH(j, l) {
|
||||
_cleanup_free_ NDiscDNSSL *s = NULL;
|
||||
NDiscDNSSL *dnssl;
|
||||
|
||||
s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*j) + 1);
|
||||
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) {
|
||||
struct in6_addr router;
|
||||
uint64_t flags;
|
||||
NDiscAddress *na;
|
||||
NDiscRoute *nr;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
@ -1271,13 +1057,7 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SET_FOREACH(na, link->ndisc_addresses)
|
||||
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;
|
||||
ndisc_mark(link, &router);
|
||||
|
||||
r = sd_ndisc_router_get_flags(rt, &flags);
|
||||
if (r < 0)
|
||||
@ -1307,21 +1087,16 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (link->ndisc_addresses_messages == 0)
|
||||
link->ndisc_addresses_configured = true;
|
||||
else
|
||||
log_link_debug(link, "Setting SLAAC addresses.");
|
||||
if (link->ndisc_messages == 0) {
|
||||
link->ndisc_configured = true;
|
||||
|
||||
if (link->ndisc_routes_messages == 0)
|
||||
link->ndisc_routes_configured = true;
|
||||
else
|
||||
log_link_debug(link, "Setting NDisc routes.");
|
||||
r = ndisc_remove(link, &router);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else
|
||||
log_link_debug(link, "Setting SLAAC addresses and router.");
|
||||
|
||||
r = ndisc_remove_old(link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!link->ndisc_addresses_configured || !link->ndisc_routes_configured)
|
||||
if (!link->ndisc_configured)
|
||||
link_set_state(link, LINK_STATE_CONFIGURING);
|
||||
|
||||
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:
|
||||
log_link_debug(link, "NDisc handler get timeout event");
|
||||
if (link->ndisc_addresses_messages == 0 && link->ndisc_routes_messages == 0) {
|
||||
link->ndisc_addresses_configured = true;
|
||||
link->ndisc_routes_configured = true;
|
||||
if (link->ndisc_messages == 0) {
|
||||
link->ndisc_configured = true;
|
||||
link_check_ready(link);
|
||||
}
|
||||
break;
|
||||
@ -1403,6 +1177,9 @@ int ndisc_start(Link *link) {
|
||||
if (!link_has_carrier(link))
|
||||
return 0;
|
||||
|
||||
if (in6_addr_is_null(&link->ipv6ll_address))
|
||||
return 0;
|
||||
|
||||
log_link_debug(link, "Discovering IPv6 routers");
|
||||
|
||||
return sd_ndisc_start(link->ndisc);
|
||||
@ -1559,14 +1336,15 @@ int config_parse_address_generation_type(
|
||||
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] = {
|
||||
[IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO] = "no",
|
||||
[IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS] = "always",
|
||||
[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");
|
||||
|
||||
@ -2,11 +2,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "conf-parser.h"
|
||||
#include "networkd-address.h"
|
||||
#include "networkd-link.h"
|
||||
#include "networkd-route.h"
|
||||
#include "time-util.h"
|
||||
|
||||
typedef struct Link Link;
|
||||
typedef struct Network Network;
|
||||
|
||||
typedef enum IPv6AcceptRAStartDHCP6Client {
|
||||
IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO,
|
||||
IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS,
|
||||
@ -15,20 +15,6 @@ typedef enum IPv6AcceptRAStartDHCP6Client {
|
||||
_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_INVALID = -EINVAL,
|
||||
} 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 {
|
||||
/* Used when GC'ing old DNS servers when configuration changes. */
|
||||
bool marked;
|
||||
@ -61,6 +47,3 @@ void ndisc_flush(Link *link);
|
||||
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_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_;
|
||||
|
||||
@ -49,7 +49,7 @@ Match.PermanentMACAddress, config_parse_hwaddrs,
|
||||
Match.Path, config_parse_match_strv, 0, offsetof(Network, match.path)
|
||||
Match.Driver, config_parse_match_strv, 0, offsetof(Network, match.driver)
|
||||
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.BSSID, config_parse_hwaddrs, 0, offsetof(Network, match.bssid)
|
||||
Match.Name, config_parse_match_ifnames, IFNAME_VALID_ALTERNATIVE, offsetof(Network, match.ifname)
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
#include "networkd-network.h"
|
||||
#include "networkd-nexthop.h"
|
||||
#include "networkd-radv.h"
|
||||
#include "networkd-route.h"
|
||||
#include "networkd-routing-policy-rule.h"
|
||||
#include "networkd-sriov.h"
|
||||
#include "parse-util.h"
|
||||
|
||||
@ -743,8 +743,6 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) {
|
||||
return false;
|
||||
|
||||
if (nexthop_owned_by_link(nexthop)) {
|
||||
Link *l;
|
||||
|
||||
/* 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
|
||||
* kernel. */
|
||||
@ -752,13 +750,6 @@ static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) {
|
||||
return false;
|
||||
if (!FLAGS_SET(link->flags, IFF_UP))
|
||||
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. */
|
||||
|
||||
@ -70,9 +70,6 @@ static Request *request_free(Request *req) {
|
||||
/* To prevent from triggering assertions in hash functions, remove this request before
|
||||
* freeing object below. */
|
||||
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)
|
||||
request_free_object(req->type, req->object);
|
||||
link_unref(req->link);
|
||||
|
||||
@ -15,11 +15,6 @@ typedef struct NextHop NextHop;
|
||||
typedef struct Route Route;
|
||||
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 {
|
||||
REQUEST_TYPE_ACTIVATE_LINK,
|
||||
REQUEST_TYPE_ADDRESS,
|
||||
@ -63,8 +58,6 @@ typedef struct Request {
|
||||
void *userdata;
|
||||
unsigned *message_counter;
|
||||
link_netlink_message_handler_t netlink_handler;
|
||||
request_after_configure_handler_t after_configure;
|
||||
request_on_free_handler_t on_free;
|
||||
} Request;
|
||||
|
||||
void request_drop(Request *req);
|
||||
|
||||
@ -7,11 +7,13 @@
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "dns-domain.h"
|
||||
#include "networkd-address.h"
|
||||
#include "networkd-link.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-network.h"
|
||||
#include "networkd-queue.h"
|
||||
#include "networkd-radv.h"
|
||||
#include "networkd-route.h"
|
||||
#include "parse-util.h"
|
||||
#include "string-util.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(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
@ -660,15 +710,6 @@ static int radv_find_uplink(Link *link, Link **ret) {
|
||||
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) {
|
||||
uint16_t router_lifetime;
|
||||
Link *uplink = NULL;
|
||||
|
||||
@ -51,6 +51,8 @@ void network_drop_invalid_prefixes(Network *network);
|
||||
void network_drop_invalid_route_prefixes(Network *network);
|
||||
void network_adjust_radv(Network *network);
|
||||
|
||||
int link_request_radv_addresses(Link *link);
|
||||
|
||||
int radv_update_mac(Link *link);
|
||||
int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len,
|
||||
uint32_t lifetime_preferred, uint32_t lifetime_valid);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -16,11 +16,13 @@ typedef struct Network Network;
|
||||
typedef struct Request Request;
|
||||
|
||||
typedef struct Route {
|
||||
Network *network;
|
||||
NetworkConfigSection *section;
|
||||
|
||||
Link *link;
|
||||
Manager *manager;
|
||||
Network *network;
|
||||
NetworkConfigSection *section;
|
||||
NetworkConfigSource source;
|
||||
NetworkConfigState state;
|
||||
union in_addr_union provider; /* DHCP server or router address */
|
||||
|
||||
int family;
|
||||
int gw_family;
|
||||
@ -52,7 +54,6 @@ typedef struct Route {
|
||||
bool protocol_set:1;
|
||||
bool pref_set:1;
|
||||
bool gateway_from_dhcp_or_ra:1;
|
||||
bool removing:1;
|
||||
|
||||
union in_addr_union gw;
|
||||
union in_addr_union dst;
|
||||
@ -66,7 +67,6 @@ typedef struct Route {
|
||||
|
||||
void route_hash_func(const Route *route, struct siphash *state);
|
||||
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;
|
||||
|
||||
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_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);
|
||||
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_foreign_routes(Link *link);
|
||||
|
||||
void route_cancel_request(Route *route);
|
||||
int link_request_route(
|
||||
Link *link,
|
||||
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_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_preferred_src);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_destination);
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
#include "missing_network.h"
|
||||
#include "netlink-util.h"
|
||||
#include "networkd-address.h"
|
||||
#include "networkd-can.h"
|
||||
#include "networkd-link.h"
|
||||
#include "networkd-manager.h"
|
||||
|
||||
@ -119,7 +119,9 @@ int manager_save(Manager *m) {
|
||||
int r;
|
||||
|
||||
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) {
|
||||
const struct in_addr *addresses;
|
||||
@ -423,10 +425,11 @@ int link_save(Link *link) {
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->state_file);
|
||||
assert(link->lease_file);
|
||||
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)
|
||||
return 0;
|
||||
|
||||
|
||||
@ -3,11 +3,7 @@
|
||||
#include <net/ethernet.h>
|
||||
#include <linux/nl80211.h>
|
||||
|
||||
#include "sd-bus.h"
|
||||
|
||||
#include "bus-util.h"
|
||||
#include "ether-addr-util.h"
|
||||
#include "netlink-internal.h"
|
||||
#include "netlink-util.h"
|
||||
#include "networkd-link.h"
|
||||
#include "networkd-manager.h"
|
||||
@ -15,56 +11,275 @@
|
||||
#include "string-util.h"
|
||||
#include "wifi-util.h"
|
||||
|
||||
int wifi_get_info(Link *link) {
|
||||
_cleanup_free_ char *ssid = NULL;
|
||||
enum nl80211_iftype iftype;
|
||||
bool updated = false;
|
||||
const char *type;
|
||||
static int link_get_wlan_interface(Link *link) {
|
||||
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
|
||||
if (!link->sd_device)
|
||||
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);
|
||||
r = sd_genl_message_new(link->manager->genl, NL80211_GENL_NAME, NL80211_CMD_GET_INTERFACE, &req);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
iftype = link->wlan_iftype; /* Assume iftype is not changed. */
|
||||
return log_link_debug_errno(link, r, "Failed to create generic netlink message: %m");
|
||||
|
||||
if (iftype == NL80211_IFTYPE_STATION) {
|
||||
struct ether_addr bssid;
|
||||
r = sd_netlink_message_append_u32(req, NL80211_ATTR_IFINDEX, link->ifindex);
|
||||
if (r < 0)
|
||||
return log_link_debug_errno(link, r, "Could not append NL80211_ATTR_IFINDEX attribute: %m");
|
||||
|
||||
r = wifi_get_station(link->manager->genl, link->ifindex, &bssid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
updated = !ether_addr_equal(&link->bssid, &bssid);
|
||||
link->bssid = bssid;
|
||||
r = sd_netlink_call(link->manager->genl, req, 0, &reply);
|
||||
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;
|
||||
link->wlan_iftype = iftype;
|
||||
updated = updated || !streq_ptr(link->ssid, ssid);
|
||||
free_and_replace(link->ssid, ssid);
|
||||
return manager_genl_process_nl80211_config(link->manager->genl, reply, link->manager);
|
||||
}
|
||||
|
||||
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)
|
||||
log_link_info(link, "Connected WiFi access point: %s (%s)",
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#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);
|
||||
|
||||
@ -73,13 +73,13 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
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)
|
||||
return log_error_errno(r, "Could not create manager: %m");
|
||||
|
||||
r = manager_connect_bus(m);
|
||||
r = manager_setup(m);
|
||||
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);
|
||||
if (r < 0)
|
||||
|
||||
@ -10,7 +10,9 @@
|
||||
#include "ether-addr-util.h"
|
||||
#include "hostname-setup.h"
|
||||
#include "network-internal.h"
|
||||
#include "networkd-address.h"
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-route.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "tests.h"
|
||||
@ -168,6 +170,16 @@ static int test_load_config(Manager *manager) {
|
||||
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) {
|
||||
_cleanup_(address_freep) Address *a1 = NULL, *a2 = NULL;
|
||||
|
||||
@ -269,7 +281,8 @@ int main(void) {
|
||||
test_address_equality();
|
||||
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);
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include "log.h"
|
||||
#include "macro.h"
|
||||
#include "net-condition.h"
|
||||
#include "networkd-address.h"
|
||||
#include "networkd-conf.h"
|
||||
#include "networkd-network.h"
|
||||
#include "strv.h"
|
||||
|
||||
@ -39,8 +39,8 @@ static int resolvconf_help(void) {
|
||||
"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"
|
||||
"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"
|
||||
"implementations are not supported and will cause the invocation to fail: -u,\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:\n"
|
||||
"-I, -i, -l, -R, -r, -v, -V, --enable-updates, --disable-updates,\n"
|
||||
"--updates-are-enabled.\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);
|
||||
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':
|
||||
log_info("Switch -%c ignored.", c);
|
||||
return 0;
|
||||
|
||||
/* The following options are openresolv inventions we don't support. */
|
||||
case 'I':
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include "socket-util.h"
|
||||
#include "string-table.h"
|
||||
#include "strv.h"
|
||||
#include "wifi-util.h"
|
||||
|
||||
void net_match_clear(NetMatch *match) {
|
||||
if (!match)
|
||||
@ -22,7 +23,7 @@ void net_match_clear(NetMatch *match) {
|
||||
match->iftype = strv_free(match->iftype);
|
||||
match->ifname = strv_free(match->ifname);
|
||||
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->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->ifname) &&
|
||||
strv_isempty(match->property) &&
|
||||
strv_isempty(match->wifi_iftype) &&
|
||||
strv_isempty(match->wlan_iftype) &&
|
||||
strv_isempty(match->ssid) &&
|
||||
set_isempty(match->bssid);
|
||||
}
|
||||
@ -117,23 +118,6 @@ static int net_condition_test_property(char * const *match_property, sd_device *
|
||||
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(
|
||||
const NetMatch *match,
|
||||
sd_device *device,
|
||||
@ -143,7 +127,7 @@ int net_match_config(
|
||||
unsigned short iftype,
|
||||
const char *ifname,
|
||||
char * const *alternative_names,
|
||||
enum nl80211_iftype wifi_iftype,
|
||||
enum nl80211_iftype wlan_iftype,
|
||||
const char *ssid,
|
||||
const struct ether_addr *bssid) {
|
||||
|
||||
@ -194,7 +178,7 @@ int net_match_config(
|
||||
if (!net_condition_test_property(match->property, device))
|
||||
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;
|
||||
|
||||
if (!net_condition_test_strv(match->ssid, ssid))
|
||||
|
||||
@ -18,7 +18,7 @@ typedef struct NetMatch {
|
||||
char **iftype;
|
||||
char **ifname;
|
||||
char **property;
|
||||
char **wifi_iftype;
|
||||
char **wlan_iftype;
|
||||
char **ssid;
|
||||
Set *bssid;
|
||||
} NetMatch;
|
||||
@ -35,7 +35,7 @@ int net_match_config(
|
||||
unsigned short iftype,
|
||||
const char *ifname,
|
||||
char * const *alternative_names,
|
||||
enum nl80211_iftype wifi_iftype,
|
||||
enum nl80211_iftype wlan_iftype,
|
||||
const char *ssid,
|
||||
const struct ether_addr *bssid);
|
||||
|
||||
|
||||
@ -1,12 +1,16 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "log.h"
|
||||
#include "string-table.h"
|
||||
#include "string-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_free_ char *ssid = NULL;
|
||||
const char *family;
|
||||
uint32_t iftype;
|
||||
size_t len;
|
||||
int r;
|
||||
|
||||
assert(genl);
|
||||
@ -47,41 +51,47 @@ int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftyp
|
||||
goto nodata;
|
||||
}
|
||||
|
||||
if (iftype) {
|
||||
uint32_t t;
|
||||
r = sd_netlink_message_read_u32(reply, NL80211_ATTR_IFTYPE, &iftype);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to get NL80211_ATTR_IFTYPE attribute: %m");
|
||||
|
||||
r = sd_netlink_message_read_u32(reply, NL80211_ATTR_IFTYPE, &t);
|
||||
if (r < 0)
|
||||
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) {
|
||||
r = sd_netlink_message_read_string_strdup(reply, NL80211_ATTR_SSID, ssid);
|
||||
if (r == -ENODATA)
|
||||
*ssid = NULL;
|
||||
else if (r < 0)
|
||||
return log_debug_errno(r, "Failed to get NL80211_ATTR_SSID attribute: %m");
|
||||
}
|
||||
if (ret_iftype)
|
||||
*ret_iftype = iftype;
|
||||
|
||||
if (ret_ssid)
|
||||
*ret_ssid = TAKE_PTR(ssid);
|
||||
|
||||
return 1;
|
||||
|
||||
nodata:
|
||||
if (iftype)
|
||||
*iftype = 0;
|
||||
if (ssid)
|
||||
*ssid = NULL;
|
||||
if (ret_iftype)
|
||||
*ret_iftype = 0;
|
||||
if (ret_ssid)
|
||||
*ret_ssid = NULL;
|
||||
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;
|
||||
const char *family;
|
||||
int r;
|
||||
|
||||
assert(genl);
|
||||
assert(ifindex > 0);
|
||||
assert(bssid);
|
||||
assert(ret_bssid);
|
||||
|
||||
r = sd_genl_message_new(genl, NL80211_GENL_NAME, NL80211_CMD_GET_STATION, &m);
|
||||
if (r < 0)
|
||||
@ -115,7 +125,7 @@ int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) {
|
||||
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)
|
||||
goto nodata;
|
||||
if (r < 0)
|
||||
@ -124,6 +134,173 @@ int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) {
|
||||
return 1;
|
||||
|
||||
nodata:
|
||||
*bssid = (struct ether_addr) {};
|
||||
*ret_bssid = ETHER_ADDR_NULL;
|
||||
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);
|
||||
|
||||
@ -3,9 +3,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <linux/nl80211.h>
|
||||
#include <net/ethernet.h>
|
||||
|
||||
#include "sd-netlink.h"
|
||||
|
||||
int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftype, char **ssid);
|
||||
int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid);
|
||||
#include "ether-addr-util.h"
|
||||
|
||||
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_;
|
||||
|
||||
@ -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_valid);
|
||||
|
||||
int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs);
|
||||
int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains);
|
||||
int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **addrs);
|
||||
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn);
|
||||
int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **fqdn);
|
||||
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 ***ret);
|
||||
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 ***ret);
|
||||
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_unref(sd_dhcp6_lease *lease);
|
||||
|
||||
@ -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_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(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);
|
||||
|
||||
@ -390,6 +390,28 @@ static void test_strv_split_full(void) {
|
||||
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) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
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_empty();
|
||||
test_strv_split_full();
|
||||
test_strv_split_and_extend_full();
|
||||
test_strv_split_colon_pairs();
|
||||
test_strv_split_newlines();
|
||||
test_strv_split_newlines_full();
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
#include "dirent-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "sort-util.h"
|
||||
#include "static-destruct.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "udev-util.h"
|
||||
@ -38,8 +39,10 @@ typedef enum QueryType {
|
||||
QUERY_ALL,
|
||||
} QueryType;
|
||||
|
||||
static char **arg_properties = NULL;
|
||||
static bool arg_root = false;
|
||||
static bool arg_export = false;
|
||||
static bool arg_value = false;
|
||||
static const char *arg_export_prefix = NULL;
|
||||
static usec_t arg_wait_for_initialization_timeout = 0;
|
||||
|
||||
@ -60,6 +63,8 @@ typedef struct SysAttr {
|
||||
const char *value;
|
||||
} SysAttr;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_properties, strv_freep);
|
||||
|
||||
static int sysattr_compare(const SysAttr *a, const SysAttr *b) {
|
||||
return strcmp(a->name, b->name);
|
||||
}
|
||||
@ -316,11 +321,18 @@ static int query_device(QueryType query, sd_device* device) {
|
||||
case QUERY_PROPERTY: {
|
||||
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)
|
||||
printf("%s%s='%s'\n", strempty(arg_export_prefix), key, value);
|
||||
else if (arg_value)
|
||||
printf("%s\n", value);
|
||||
else
|
||||
printf("%s=%s\n", key, value);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -343,6 +355,8 @@ static int help(void) {
|
||||
" path sysfs device path\n"
|
||||
" property The device properties\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"
|
||||
" -n --name=NAME Node or symlink name used for query or attribute walk\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;
|
||||
int c, r;
|
||||
|
||||
enum {
|
||||
ARG_PROPERTY = 0x100,
|
||||
ARG_VALUE,
|
||||
};
|
||||
|
||||
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' },
|
||||
{ "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' },
|
||||
{ "export", no_argument, NULL, 'x' },
|
||||
{ "export-prefix", required_argument, NULL, 'P' },
|
||||
{ "wait-for-initialization", optional_argument, NULL, 'w' },
|
||||
{ "version", no_argument, NULL, 'V' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "attribute-walk", no_argument, NULL, 'a' },
|
||||
{ "cleanup-db", no_argument, NULL, 'c' },
|
||||
{ "device-id-of-file", required_argument, NULL, 'd' },
|
||||
{ "export", no_argument, NULL, 'x' },
|
||||
{ "export-db", no_argument, NULL, 'e' },
|
||||
{ "export-prefix", required_argument, NULL, 'P' },
|
||||
{ "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)
|
||||
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 'p': {
|
||||
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),
|
||||
"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;
|
||||
STRV_FOREACH(p, devices) {
|
||||
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
|
||||
|
||||
@ -11,6 +11,8 @@ set -e
|
||||
TEST_DESCRIPTION="systemd-udev storage tests"
|
||||
IMAGE_NAME="default"
|
||||
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}"
|
||||
|
||||
# shellcheck source=test/test-functions
|
||||
@ -28,14 +30,20 @@ _host_has_feature() {(
|
||||
set -e
|
||||
|
||||
case "${1:?}" in
|
||||
multipath)
|
||||
command -v multipath && command -v multipathd || return $?
|
||||
btrfs)
|
||||
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)
|
||||
command -v lvm || return $?
|
||||
;;
|
||||
btrfs)
|
||||
modprobe -nv btrfs && command -v mkfs.btrfs && command -v btrfs || return $?
|
||||
multipath)
|
||||
command -v multipath && command -v multipathd || return $?
|
||||
;;
|
||||
*)
|
||||
echo >&2 "ERROR: Unknown feature '$1'"
|
||||
@ -54,6 +62,7 @@ test_append_files() {(
|
||||
# checked for here
|
||||
local -A features=(
|
||||
[btrfs]=install_btrfs
|
||||
[iscsi]=install_iscsi
|
||||
[lvm]=install_lvm
|
||||
[multipath]=install_multipath
|
||||
)
|
||||
@ -360,6 +369,35 @@ testcase_btrfs_basic() {
|
||||
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
|
||||
# testing (make -C test/... TESTCASES="testcase1 testcase2")
|
||||
if [[ -v "TESTCASES" && -n "$TESTCASES" ]]; then
|
||||
|
||||
@ -959,6 +959,45 @@ install_btrfs() {
|
||||
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() {
|
||||
dinfo "Install compiled systemd"
|
||||
|
||||
@ -2319,6 +2358,8 @@ inst() {
|
||||
for fun in inst_symlink inst_script inst_binary inst_simple; do
|
||||
"$fun" "$@" && return 0
|
||||
done
|
||||
|
||||
dwarn "Failed to install '$1'"
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
@ -40,6 +40,57 @@ helper_check_device_symlinks() {(
|
||||
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() {
|
||||
lsblk -S
|
||||
[[ "$(lsblk --scsi --noheadings | wc -l)" -ge 128 ]]
|
||||
@ -397,6 +448,144 @@ EOF
|
||||
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
|
||||
|
||||
udevadm settle
|
||||
|
||||
@ -68,6 +68,12 @@ cat <<EOF >/tmp/testfile.service
|
||||
ExecStart = echo hello
|
||||
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
|
||||
systemd-analyze security --offline=true /tmp/testfile.service
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user