aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEd Maste <emaste@FreeBSD.org>2024-06-03 16:17:02 +0000
committerEd Maste <emaste@FreeBSD.org>2024-06-03 16:19:54 +0000
commit1b2aa3deeb0dbbace9fed635fa01b6f6e8480901 (patch)
treed4f991a92b1c7c84e264b900f50f2fa0202c872a
parent96dba636abec6d5451820add99300bda2ca6d86a (diff)
downloadsrc-vendor/dhcpcd.tar.gz
src-vendor/dhcpcd.zip
Event: Kitchener-Waterloo Hackathon 202406 Sponsored by: The FreeBSD Foundation
-rwxr-xr-x.actions/build-bsd97
-rwxr-xr-x.actions/build-linux-clang21
-rwxr-xr-x.actions/build-linux-gcc23
-rwxr-xr-x.actions/build-linux-i686-w64-mingw32-gcc58
-rwxr-xr-x.actions/build-linux-openssl3-clang33
-rwxr-xr-x.actions/build-linux-openssl3-gcc28
-rwxr-xr-x.actions/build-linux-openssl3-i686-w64-mingw32-gcc59
-rwxr-xr-x.actions/build-osx-clang19
-rwxr-xr-x.actions/fuzz-linux93
-rw-r--r--.actions/llvm.gpg52
-rwxr-xr-x.actions/setup_clang17
-rw-r--r--.gitattributes1
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.md80
-rw-r--r--.github/ISSUE_TEMPLATE/config.yml8
-rw-r--r--.github/workflows/alpine_builds.yml39
-rw-r--r--.github/workflows/bsd_builds.yml32
-rw-r--r--.github/workflows/cifuzz_oss.yml46
-rw-r--r--.github/workflows/codeql-analysis.yml42
-rw-r--r--.github/workflows/cygwin_builds.yml30
-rw-r--r--.github/workflows/linux_builds.yml57
-rw-r--r--.github/workflows/linux_fuzz.yml41
-rw-r--r--.github/workflows/macos_builds.yml32
-rw-r--r--.github/workflows/openssl3.yml51
-rw-r--r--.github/workflows/windows_builds.yml32
-rw-r--r--BUILDING.md29
-rw-r--r--LICENSE2
-rw-r--r--Makefile41
-rw-r--r--Makefile.inc9
-rw-r--r--README.md18
-rw-r--r--compat/arc4random.c398
-rw-r--r--compat/arc4random.h2
-rw-r--r--compat/arc4random_uniform.c8
-rw-r--r--compat/chacha_private.h222
-rw-r--r--compat/closefrom.c77
-rw-r--r--compat/closefrom.h24
-rw-r--r--compat/crypt/md5.h2
-rw-r--r--compat/crypt_openssl/hmac.c55
-rw-r--r--compat/crypt_openssl/hmac.h35
-rw-r--r--compat/crypt_openssl/sha256.c56
-rw-r--r--compat/crypt_openssl/sha256.h46
-rw-r--r--compat/pidfile.c6
-rw-r--r--compat/rb.c12
-rw-r--r--compat/strlcpy.c4
-rwxr-xr-xconfigure314
-rw-r--r--hooks/10-wpa_supplicant10
-rw-r--r--hooks/20-resolv.conf2
-rw-r--r--hooks/29-lookup-hostname6
-rw-r--r--hooks/30-hostname.in6
-rw-r--r--hooks/50-ntp.conf6
-rw-r--r--hooks/50-timesyncd.conf60
-rw-r--r--hooks/Makefile2
-rw-r--r--hooks/dhcpcd-run-hooks.8.in29
-rw-r--r--hooks/dhcpcd-run-hooks.in18
-rw-r--r--src/Makefile8
-rw-r--r--src/arp.c48
-rw-r--r--src/arp.h2
-rw-r--r--src/auth.c2
-rw-r--r--src/auth.h2
-rw-r--r--src/bpf.c49
-rw-r--r--src/bpf.h2
-rw-r--r--src/common.c10
-rw-r--r--src/common.h2
-rw-r--r--src/control.c214
-rw-r--r--src/control.h2
-rw-r--r--src/defs.h8
-rw-r--r--src/dev.c11
-rw-r--r--src/dev.h2
-rw-r--r--src/dev/Makefile2
-rw-r--r--src/dev/udev.c2
-rw-r--r--src/dhcp-common.c84
-rw-r--r--src/dhcp-common.h3
-rw-r--r--src/dhcp.c138
-rw-r--r--src/dhcp.h2
-rw-r--r--src/dhcp6.c210
-rw-r--r--src/dhcp6.h3
-rw-r--r--src/dhcpcd-definitions-small.conf2
-rw-r--r--src/dhcpcd-definitions.conf81
-rw-r--r--src/dhcpcd-embedded.c.in2
-rw-r--r--src/dhcpcd-embedded.h.in2
-rw-r--r--src/dhcpcd.8.in31
-rw-r--r--src/dhcpcd.c502
-rw-r--r--src/dhcpcd.conf.5.in52
-rw-r--r--src/dhcpcd.h24
-rw-r--r--src/duid.c13
-rw-r--r--src/duid.h2
-rw-r--r--src/eloop.c927
-rw-r--r--src/eloop.h33
-rw-r--r--src/if-bsd.c133
-rw-r--r--src/if-linux-wext.c4
-rw-r--r--src/if-linux.c215
-rw-r--r--src/if-options.c134
-rw-r--r--src/if-options.h17
-rw-r--r--src/if-sun.c15
-rw-r--r--src/if.c40
-rw-r--r--src/if.h31
-rw-r--r--src/ipv4.c2
-rw-r--r--src/ipv4.h2
-rw-r--r--src/ipv4ll.c19
-rw-r--r--src/ipv4ll.h3
-rw-r--r--src/ipv6.c138
-rw-r--r--src/ipv6.h2
-rw-r--r--src/ipv6nd.c233
-rw-r--r--src/ipv6nd.h23
-rw-r--r--src/logerr.c4
-rw-r--r--src/logerr.h2
-rw-r--r--src/privsep-bpf.c46
-rw-r--r--src/privsep-bpf.h2
-rw-r--r--src/privsep-bsd.c221
-rw-r--r--src/privsep-control.c131
-rw-r--r--src/privsep-control.h2
-rw-r--r--src/privsep-inet.c168
-rw-r--r--src/privsep-inet.h2
-rw-r--r--src/privsep-linux.c106
-rw-r--r--src/privsep-root.c305
-rw-r--r--src/privsep-root.h10
-rw-r--r--src/privsep-sun.c12
-rw-r--r--src/privsep.c459
-rw-r--r--src/privsep.h62
-rw-r--r--src/route.c5
-rw-r--r--src/route.h2
-rw-r--r--src/sa.c48
-rw-r--r--src/sa.h3
-rw-r--r--src/script.c59
-rw-r--r--src/script.h2
-rw-r--r--tests/crypt/Makefile2
-rw-r--r--tests/crypt/run-test.c2
-rw-r--r--tests/crypt/test.h1
-rw-r--r--tests/crypt/test_sha256.c138
-rw-r--r--tests/eloop-bench/Makefile2
-rw-r--r--tests/eloop-bench/eloop-bench.c9
130 files changed, 5979 insertions, 1804 deletions
diff --git a/.actions/build-bsd b/.actions/build-bsd
new file mode 100755
index 000000000000..66fc4e3ba281
--- /dev/null
+++ b/.actions/build-bsd
@@ -0,0 +1,97 @@
+#!/bin/sh -eux
+
+# Copyright (c) 2022 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+# SPDX-License-Identifier: BSD-2-Clause
+
+BASE_URL="https://builds.sr.ht"
+MANIFEST="$(mktemp)"
+LOGFILE="$(mktemp)"
+trap '[ -f "${LOGFILE}" ] && cat -- "${LOGFILE}"' EXIT
+
+# construct the sourcehut build manifest
+cat > "${MANIFEST}" <<- EOF
+image: ${IMAGE}
+packages:
+ - cmake
+ - llvm
+ - pcsc-lite
+EOF
+
+case "${IMAGE}" in
+ freebsd*)
+cat >> "${MANIFEST}" <<- EOF
+ - libcbor
+ - pkgconf
+EOF
+ ;;
+esac
+
+cat >> "${MANIFEST}" <<- EOF
+sources:
+ - ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}#$(git rev-parse HEAD)
+tasks:
+ - build: |
+ if [ "\$(uname)" = "OpenBSD" ]; then
+ SUDO="doas -u root"
+ else
+ SUDO=sudo
+ fi
+ SCAN="/usr/local/bin/scan-build --use-cc=/usr/bin/cc --status-bugs"
+ cd libfido2
+ for T in Debug Release; do
+ mkdir build-\$T
+ (cd build-\$T && \${SCAN} cmake -DCMAKE_BUILD_TYPE=\$T ..)
+ \${SCAN} make -j"\$(sysctl -n hw.ncpu)" -C build-\$T
+ make -C build-\$T regress
+ \${SUDO} make -C build-\$T install
+ done
+EOF
+
+q() {
+ curl \
+ --silent \
+ --oauth2-bearer "${SOURCEHUT_TOKEN}" \
+ --header "Content-Type: application/json" \
+ --data @- -- \
+ "${BASE_URL}/query" \
+ | tee -a -- "${LOGFILE}"
+}
+
+submit_job() {
+ local manifest="$1"
+ jq \
+ --compact-output --null-input \
+ '{ query: $body, variables: { var: $var } }' \
+ --arg body 'mutation($var: String!) { submit(manifest: $var) { id } }' \
+ --rawfile var "${manifest}" \
+ | q \
+ | jq --exit-status --raw-output '.data.submit.id'
+}
+
+job_status() {
+ local id="$1"
+ jq \
+ --compact-output --null-input \
+ '{ query: $body, variables: { var: $var } }' \
+ --arg body 'query($var: Int!) { job(id: $var) { status } }' \
+ --argjson var "${id}" \
+ | q \
+ | jq --exit-status --raw-output '.data.job.status'
+}
+
+JOB_ID="$(submit_job "${MANIFEST}")" || exit 1
+[ -z "${JOB_ID}" ] && exit 1
+echo "Job '${JOB_ID}' running at ${BASE_URL}/~yubico-libfido2/job/${JOB_ID}"
+
+while true; do
+ JOB_STATUS="$(job_status "${JOB_ID}")" || exit 1
+ case "${JOB_STATUS}" in
+ SUCCESS) exit 0;;
+ FAILED) exit 1;;
+ PENDING|QUEUED|RUNNING) ;;
+ *) exit 1;;
+ esac
+ sleep 60
+done
diff --git a/.actions/build-linux-clang b/.actions/build-linux-clang
new file mode 100755
index 000000000000..ba20f1279ec9
--- /dev/null
+++ b/.actions/build-linux-clang
@@ -0,0 +1,21 @@
+#!/bin/sh -eux
+
+# Copyright (c) 2022 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+# SPDX-License-Identifier: BSD-2-Clause
+
+${CC} --version
+SCAN=scan-build${CC#clang}
+
+# Check exports.
+(cd src && ./diff_exports.sh)
+
+# Build, analyze, and install libfido2.
+for T in Debug Release; do
+ mkdir build-$T
+ (cd build-$T && ${SCAN} --use-cc="${CC}" cmake -DCMAKE_BUILD_TYPE=$T ..)
+ ${SCAN} --use-cc="${CC}" --status-bugs make -j"$(nproc)" -C build-$T
+ make -C build-$T regress
+ sudo make -C build-$T install
+done
diff --git a/.actions/build-linux-gcc b/.actions/build-linux-gcc
new file mode 100755
index 000000000000..cd42b5eb7bed
--- /dev/null
+++ b/.actions/build-linux-gcc
@@ -0,0 +1,23 @@
+#!/bin/sh -eux
+
+# Copyright (c) 2022 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+# SPDX-License-Identifier: BSD-2-Clause
+
+${CC} --version
+
+# Build and install libfido2.
+for T in Debug Release; do
+ mkdir build-$T
+ (cd build-$T && cmake -DCMAKE_BUILD_TYPE=$T ..)
+ make -j"$(nproc)" -C build-$T
+ make -C build-$T regress
+ sudo make -C build-$T install
+done
+
+# Check udev/fidodevs.
+[ -x "$(which update-alternatives)" ] && {
+ sudo update-alternatives --set awk "$(which original-awk)"
+}
+udev/check.sh udev/fidodevs
diff --git a/.actions/build-linux-i686-w64-mingw32-gcc b/.actions/build-linux-i686-w64-mingw32-gcc
new file mode 100755
index 000000000000..a89578da0886
--- /dev/null
+++ b/.actions/build-linux-i686-w64-mingw32-gcc
@@ -0,0 +1,58 @@
+#!/bin/sh -eux
+
+# Copyright (c) 2022-2023 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+# SPDX-License-Identifier: BSD-2-Clause
+
+# XXX defining CC and cross-compiling confuses OpenSSL's build.
+unset CC
+
+sudo mkdir /fakeroot
+sudo chmod 755 /fakeroot
+
+cat << EOF > /tmp/mingw.cmake
+SET(CMAKE_SYSTEM_NAME Windows)
+SET(CMAKE_C_COMPILER i686-w64-mingw32-gcc)
+SET(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)
+SET(CMAKE_RC_COMPILER i686-w64-mingw32-windres)
+SET(CMAKE_FIND_ROOT_PATH /fakeroot)
+SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+EOF
+
+# Build and install libcbor.
+git clone --depth=1 https://github.com/pjk/libcbor -b v0.10.1
+cd libcbor
+mkdir build
+(cd build && cmake -DCMAKE_TOOLCHAIN_FILE=/tmp/mingw.cmake \
+ -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=/fakeroot ..)
+make -j"$(nproc)" -C build
+sudo make -C build install
+cd ..
+
+# Build and install OpenSSL 1.1.1w.
+git clone --depth=1 https://github.com/openssl/openssl -b OpenSSL_1_1_1w
+cd openssl
+./Configure mingw --prefix=/fakeroot --openssldir=/fakeroot/openssl \
+ --cross-compile-prefix=i686-w64-mingw32-
+make -j"$(nproc)"
+sudo make install_sw
+cd ..
+
+# Build and install zlib.
+git clone --depth=1 https://github.com/madler/zlib -b v1.3
+cd zlib
+make -fwin32/Makefile.gcc PREFIX=i686-w64-mingw32-
+sudo make -fwin32/Makefile.gcc PREFIX=i686-w64-mingw32- DESTDIR=/fakeroot \
+ INCLUDE_PATH=/include LIBRARY_PATH=/lib BINARY_PATH=/bin install
+cd ..
+
+# Build and install libfido2.
+export PKG_CONFIG_PATH=/fakeroot/lib/pkgconfig
+mkdir build
+(cd build && cmake -DCMAKE_TOOLCHAIN_FILE=/tmp/mingw.cmake \
+ -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=/fakeroot ..)
+make -j"$(nproc)" -C build
+sudo make -C build install
diff --git a/.actions/build-linux-openssl3-clang b/.actions/build-linux-openssl3-clang
new file mode 100755
index 000000000000..2383e51ad8f5
--- /dev/null
+++ b/.actions/build-linux-openssl3-clang
@@ -0,0 +1,33 @@
+#!/bin/sh -eux
+
+# Copyright (c) 2022 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+# SPDX-License-Identifier: BSD-2-Clause
+
+${CC} --version
+SCAN=scan-build${CC#clang}
+FAKEROOT="$(mktemp -d)"
+
+# Check exports.
+(cd src && ./diff_exports.sh)
+
+# Build and install OpenSSL 3.0.12.
+git clone --branch openssl-3.0.12 \
+ --depth=1 https://github.com/openssl/openssl
+cd openssl
+./Configure linux-x86_64-clang --prefix="${FAKEROOT}" \
+ --openssldir="${FAKEROOT}/openssl" --libdir=lib
+make install_sw
+cd ..
+
+# Build, analyze, and install libfido2.
+for T in Debug Release; do
+ mkdir build-$T
+ export PKG_CONFIG_PATH="${FAKEROOT}/lib/pkgconfig"
+ (cd build-$T && ${SCAN} --use-cc="${CC}" \
+ cmake -DCMAKE_BUILD_TYPE=$T ..)
+ ${SCAN} --use-cc="${CC}" --status-bugs make -C build-$T
+ make -C build-$T regress
+ sudo make -C build-$T install
+done
diff --git a/.actions/build-linux-openssl3-gcc b/.actions/build-linux-openssl3-gcc
new file mode 100755
index 000000000000..344fc12bce8c
--- /dev/null
+++ b/.actions/build-linux-openssl3-gcc
@@ -0,0 +1,28 @@
+#!/bin/sh -eux
+
+# Copyright (c) 2022 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+# SPDX-License-Identifier: BSD-2-Clause
+
+${CC} --version
+FAKEROOT="$(mktemp -d)"
+
+# Build and install OpenSSL 3.0.12.
+git clone --branch openssl-3.0.12 \
+ --depth=1 https://github.com/openssl/openssl
+cd openssl
+./Configure linux-x86_64 --prefix="${FAKEROOT}" \
+ --openssldir="${FAKEROOT}/openssl" --libdir=lib
+make install_sw
+cd ..
+
+# Build and install libfido2.
+for T in Debug Release; do
+ mkdir build-$T
+ export PKG_CONFIG_PATH="${FAKEROOT}/lib/pkgconfig"
+ (cd build-$T && cmake -DCMAKE_BUILD_TYPE=$T ..)
+ make -j"$(nproc)" -C build-$T
+ make -C build-$T regress
+ sudo make -C build-$T install
+done
diff --git a/.actions/build-linux-openssl3-i686-w64-mingw32-gcc b/.actions/build-linux-openssl3-i686-w64-mingw32-gcc
new file mode 100755
index 000000000000..3bbb141dad0d
--- /dev/null
+++ b/.actions/build-linux-openssl3-i686-w64-mingw32-gcc
@@ -0,0 +1,59 @@
+#!/bin/sh -eux
+
+# Copyright (c) 2022-2023 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+# SPDX-License-Identifier: BSD-2-Clause
+
+# XXX defining CC and cross-compiling confuses OpenSSL's build.
+unset CC
+
+sudo mkdir /fakeroot
+sudo chmod 755 /fakeroot
+
+cat << EOF > /tmp/mingw.cmake
+SET(CMAKE_SYSTEM_NAME Windows)
+SET(CMAKE_C_COMPILER i686-w64-mingw32-gcc)
+SET(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)
+SET(CMAKE_RC_COMPILER i686-w64-mingw32-windres)
+SET(CMAKE_FIND_ROOT_PATH /fakeroot)
+SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+EOF
+
+# Build and install libcbor.
+git clone --depth=1 https://github.com/pjk/libcbor -b v0.10.1
+cd libcbor
+mkdir build
+(cd build && cmake -DCMAKE_TOOLCHAIN_FILE=/tmp/mingw.cmake \
+ -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=/fakeroot ..)
+make -j"$(nproc)" -C build
+sudo make -C build install
+cd ..
+
+# Build and install OpenSSL 3.0.11.
+git clone --branch openssl-3.0.12 \
+ --depth=1 https://github.com/openssl/openssl
+cd openssl
+./Configure mingw --prefix=/fakeroot --openssldir=/fakeroot/openssl \
+ --cross-compile-prefix=i686-w64-mingw32- --libdir=lib
+make -j"$(nproc)"
+sudo make install_sw
+cd ..
+
+# Build and install zlib.
+git clone --depth=1 https://github.com/madler/zlib -b v1.3
+cd zlib
+make -fwin32/Makefile.gcc PREFIX=i686-w64-mingw32-
+sudo make -fwin32/Makefile.gcc PREFIX=i686-w64-mingw32- DESTDIR=/fakeroot \
+ INCLUDE_PATH=/include LIBRARY_PATH=/lib BINARY_PATH=/bin install
+cd ..
+
+# Build and install libfido2.
+export PKG_CONFIG_PATH=/fakeroot/lib/pkgconfig
+mkdir build
+(cd build && cmake -DCMAKE_TOOLCHAIN_FILE=/tmp/mingw.cmake \
+ -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=/fakeroot ..)
+make -C build 2>&1
+sudo make -C build install
diff --git a/.actions/build-osx-clang b/.actions/build-osx-clang
new file mode 100755
index 000000000000..b4beea221315
--- /dev/null
+++ b/.actions/build-osx-clang
@@ -0,0 +1,19 @@
+#!/bin/sh -eux
+
+# Copyright (c) 2022 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+# SPDX-License-Identifier: BSD-2-Clause
+
+export PKG_CONFIG_PATH="$(brew --prefix openssl@3.0)/lib/pkgconfig"
+SCAN="$(brew --prefix llvm)/bin/scan-build"
+
+# Build, analyze, and install libfido2.
+for T in Debug Release; do
+ mkdir build-$T
+ (cd build-$T && ${SCAN} cmake -DCMAKE_BUILD_TYPE=$T ..)
+ ${SCAN} --status-bugs make -j"$(sysctl -n hw.ncpu)" -C build-$T
+ make -C build-$T man_symlink_html
+ make -C build-$T regress
+ sudo make -C build-$T install
+done
diff --git a/.actions/fuzz-linux b/.actions/fuzz-linux
new file mode 100755
index 000000000000..3f57ac40ff4b
--- /dev/null
+++ b/.actions/fuzz-linux
@@ -0,0 +1,93 @@
+#!/bin/sh -eux
+
+# Copyright (c) 2020-2022 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+# SPDX-License-Identifier: BSD-2-Clause
+
+LIBCBOR_URL="https://github.com/pjk/libcbor"
+LIBCBOR_TAG="v0.10.2"
+LIBCBOR_ASAN="address alignment bounds"
+LIBCBOR_MSAN="memory"
+OPENSSL_URL="https://github.com/openssl/openssl"
+OPENSSL_TAG="openssl-3.0.12"
+ZLIB_URL="https://github.com/madler/zlib"
+ZLIB_TAG="v1.3"
+ZLIB_ASAN="address alignment bounds undefined"
+ZLIB_MSAN="memory"
+FIDO2_ASAN="address bounds fuzzer-no-link implicit-conversion leak"
+FIDO2_ASAN="${FIDO2_ASAN} pointer-compare pointer-subtract undefined"
+FIDO2_MSAN="fuzzer-no-link memory"
+COMMON_CFLAGS="-g2 -fno-omit-frame-pointer"
+COMMON_CFLAGS="${COMMON_CFLAGS} -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION"
+UBSAN_OPTIONS="halt_on_error=1:print_stacktrace=1:strict_string_checks=1"
+ASAN_OPTIONS="${UBSAN_OPTIONS}:detect_invalid_pointer_pairs=2:detect_leaks=1"
+MSAN_OPTIONS="${UBSAN_OPTIONS}"
+
+case "$1" in
+asan)
+ LIBCBOR_CFLAGS="-fsanitize=$(echo "${LIBCBOR_ASAN}" | tr ' ' ',')"
+ ZLIB_CFLAGS="-fsanitize=$(echo "${ZLIB_ASAN}" | tr ' ' ',')"
+ FIDO2_CFLAGS="-fsanitize=$(echo "${FIDO2_ASAN}" | tr ' ' ',')"
+ FIDO2_CFLAGS="${FIDO2_CFLAGS} -fsanitize-address-use-after-scope"
+ ;;
+msan)
+ LIBCBOR_CFLAGS="-fsanitize=$(echo "${LIBCBOR_MSAN}" | tr ' ' ',')"
+ ZLIB_CFLAGS="-fsanitize=$(echo "${ZLIB_MSAN}" | tr ' ' ',')"
+ FIDO2_CFLAGS="-fsanitize=$(echo "${FIDO2_MSAN}" | tr ' ' ',')"
+ FIDO2_CFLAGS="${FIDO2_CFLAGS} -fsanitize-memory-track-origins"
+ ;;
+*)
+ echo "unknown sanitiser \"$1\"" 1>&2 && exit 1
+esac
+
+${CC} --version
+WORKDIR="${WORKDIR:-$(pwd)}"
+FAKEROOT="${FAKEROOT:-$(mktemp -d)}"
+cd "${FAKEROOT}"
+
+# libcbor
+git clone --depth=1 "${LIBCBOR_URL}" -b "${LIBCBOR_TAG}"
+cd libcbor
+patch -p0 -s < "${WORKDIR}/fuzz/README"
+mkdir build
+(cd build && cmake -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Debug \
+ -DCMAKE_C_FLAGS_DEBUG="${LIBCBOR_CFLAGS} ${COMMON_CFLAGS}" \
+ -DCMAKE_INSTALL_LIBDIR=lib -DCMAKE_INSTALL_PREFIX="${FAKEROOT}" \
+ -DSANITIZE=OFF ..)
+make VERBOSE=1 -j"$(nproc)" -C build all install
+cd -
+
+# openssl
+git clone --depth=1 "${OPENSSL_URL}" -b "${OPENSSL_TAG}"
+cd openssl
+./Configure linux-x86_64-clang "enable-$1" --prefix="${FAKEROOT}" \
+ --openssldir="${FAKEROOT}/openssl" --libdir=lib
+make install_sw
+cd -
+
+# zlib
+git clone --depth=1 "${ZLIB_URL}" -b "${ZLIB_TAG}"
+cd zlib
+CFLAGS="${ZLIB_CFLAGS}" LDFLAGS="${ZLIB_CFLAGS}" ./configure \
+ --prefix="${FAKEROOT}"
+make install
+cd -
+
+# libfido2
+mkdir build
+export PKG_CONFIG_PATH="${FAKEROOT}/lib/pkgconfig"
+(cd build && cmake -DCMAKE_BUILD_TYPE=Debug \
+ -DCMAKE_C_FLAGS_DEBUG="${FIDO2_CFLAGS} ${COMMON_CFLAGS}" -DFUZZ=ON \
+ -DFUZZ_LDFLAGS="-fsanitize=fuzzer" "${WORKDIR}")
+make -j"$(nproc)" -C build
+
+# fuzz
+mkdir corpus
+curl -s https://storage.googleapis.com/yubico-libfido2/corpus.tgz |
+ tar -C corpus -zxf -
+export UBSAN_OPTIONS ASAN_OPTIONS MSAN_OPTIONS
+for f in assert bio cred credman hid largeblob mgmt netlink pcsc; do
+ build/fuzz/fuzz_${f} -use_value_profile=1 -reload=30 -print_pcs=1 \
+ -print_funcs=30 -timeout=10 -runs=1 corpus/fuzz_${f}
+done
diff --git a/.actions/llvm.gpg b/.actions/llvm.gpg
new file mode 100644
index 000000000000..aa6b105aa3d7
--- /dev/null
+++ b/.actions/llvm.gpg
@@ -0,0 +1,52 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.12 (GNU/Linux)
+
+mQINBFE9lCwBEADi0WUAApM/mgHJRU8lVkkw0CHsZNpqaQDNaHefD6Rw3S4LxNmM
+EZaOTkhP200XZM8lVdbfUW9xSjA3oPldc1HG26NjbqqCmWpdo2fb+r7VmU2dq3NM
+R18ZlKixiLDE6OUfaXWKamZsXb6ITTYmgTO6orQWYrnW6ckYHSeaAkW0wkDAryl2
+B5v8aoFnQ1rFiVEMo4NGzw4UX+MelF7rxaaregmKVTPiqCOSPJ1McC1dHFN533FY
+Wh/RVLKWo6npu+owtwYFQW+zyQhKzSIMvNujFRzhIxzxR9Gn87MoLAyfgKEzrbbT
+DhqqNXTxS4UMUKCQaO93TzetX/EBrRpJj+vP640yio80h4Dr5pAd7+LnKwgpTDk1
+G88bBXJAcPZnTSKu9I2c6KY4iRNbvRz4i+ZdwwZtdW4nSdl2792L7Sl7Nc44uLL/
+ZqkKDXEBF6lsX5XpABwyK89S/SbHOytXv9o4puv+65Ac5/UShspQTMSKGZgvDauU
+cs8kE1U9dPOqVNCYq9Nfwinkf6RxV1k1+gwtclxQuY7UpKXP0hNAXjAiA5KS5Crq
+7aaJg9q2F4bub0mNU6n7UI6vXguF2n4SEtzPRk6RP+4TiT3bZUsmr+1ktogyOJCc
+Ha8G5VdL+NBIYQthOcieYCBnTeIH7D3Sp6FYQTYtVbKFzmMK+36ERreL/wARAQAB
+tD1TeWx2ZXN0cmUgTGVkcnUgLSBEZWJpYW4gTExWTSBwYWNrYWdlcyA8c3lsdmVz
+dHJlQGRlYmlhbi5vcmc+iQI4BBMBAgAiBQJRPZQsAhsDBgsJCAcDAgYVCAIJCgsE
+FgIDAQIeAQIXgAAKCRAVz00Yr090Ibx+EADArS/hvkDF8juWMXxh17CgR0WZlHCC
+9CTBWkg5a0bNN/3bb97cPQt/vIKWjQtkQpav6/5JTVCSx2riL4FHYhH0iuo4iAPR
+udC7Cvg8g7bSPrKO6tenQZNvQm+tUmBHgFiMBJi92AjZ/Qn1Shg7p9ITivFxpLyX
+wpmnF1OKyI2Kof2rm4BFwfSWuf8Fvh7kDMRLHv+MlnK/7j/BNpKdozXxLcwoFBmn
+l0WjpAH3OFF7Pvm1LJdf1DjWKH0Dc3sc6zxtmBR/KHHg6kK4BGQNnFKujcP7TVdv
+gMYv84kun14pnwjZcqOtN3UJtcx22880DOQzinoMs3Q4w4o05oIF+sSgHViFpc3W
+R0v+RllnH05vKZo+LDzc83DQVrdwliV12eHxrMQ8UYg88zCbF/cHHnlzZWAJgftg
+hB08v1BKPgYRUzwJ6VdVqXYcZWEaUJmQAPuAALyZESw94hSo28FAn0/gzEc5uOYx
+K+xG/lFwgAGYNb3uGM5m0P6LVTfdg6vDwwOeTNIExVk3KVFXeSQef2ZMkhwA7wya
+KJptkb62wBHFE+o9TUdtMCY6qONxMMdwioRE5BYNwAsS1PnRD2+jtlI0DzvKHt7B
+MWd8hnoUKhMeZ9TNmo+8CpsAtXZcBho0zPGz/R8NlJhAWpdAZ1CmcPo83EW86Yq7
+BxQUKnNHcwj2ebkCDQRRPZQsARAA4jxYmbTHwmMjqSizlMJYNuGOpIidEdx9zQ5g
+zOr431/VfWq4S+VhMDhs15j9lyml0y4ok215VRFwrAREDg6UPMr7ajLmBQGau0Fc
+bvZJ90l4NjXp5p0NEE/qOb9UEHT7EGkEhaZ1ekkWFTWCgsy7rRXfZLxB6sk7pzLC
+DshyW3zjIakWAnpQ5j5obiDy708pReAuGB94NSyb1HoW/xGsGgvvCw4r0w3xPStw
+F1PhmScE6NTBIfLliea3pl8vhKPlCh54Hk7I8QGjo1ETlRP4Qll1ZxHJ8u25f/ta
+RES2Aw8Hi7j0EVcZ6MT9JWTI83yUcnUlZPZS2HyeWcUj+8nUC8W4N8An+aNps9l/
+21inIl2TbGo3Yn1JQLnA1YCoGwC34g8QZTJhElEQBN0X29ayWW6OdFx8MDvllbBV
+ymmKq2lK1U55mQTfDli7S3vfGz9Gp/oQwZ8bQpOeUkc5hbZszYwP4RX+68xDPfn+
+M9udl+qW9wu+LyePbW6HX90LmkhNkkY2ZzUPRPDHZANU5btaPXc2H7edX4y4maQa
+xenqD0lGh9LGz/mps4HEZtCI5CY8o0uCMF3lT0XfXhuLksr7Pxv57yue8LLTItOJ
+d9Hmzp9G97SRYYeqU+8lyNXtU2PdrLLq7QHkzrsloG78lCpQcalHGACJzrlUWVP/
+fN3Ht3kAEQEAAYkCHwQYAQIACQUCUT2ULAIbDAAKCRAVz00Yr090IbhWEADbr50X
+OEXMIMGRLe+YMjeMX9NG4jxs0jZaWHc/WrGR+CCSUb9r6aPXeLo+45949uEfdSsB
+pbaEdNWxF5Vr1CSjuO5siIlgDjmT655voXo67xVpEN4HhMrxugDJfCa6z97P0+ML
+PdDxim57uNqkam9XIq9hKQaurxMAECDPmlEXI4QT3eu5qw5/knMzDMZj4Vi6hovL
+wvvAeLHO/jsyfIdNmhBGU2RWCEZ9uo/MeerPHtRPfg74g+9PPfP6nyHD2Wes6yGd
+oVQwtPNAQD6Cj7EaA2xdZYLJ7/jW6yiPu98FFWP74FN2dlyEA2uVziLsfBrgpS4l
+tVOlrO2YzkkqUGrybzbLpj6eeHx+Cd7wcjI8CalsqtL6cG8cUEjtWQUHyTbQWAgG
+5VPEgIAVhJ6RTZ26i/G+4J8neKyRs4vz+57UGwY6zI4AB1ZcWGEE3Bf+CDEDgmnP
+LSwbnHefK9IljT9XU98PelSryUO/5UPw7leE0akXKB4DtekToO226px1VnGp3Bov
+1GBGvpHvL2WizEwdk+nfk8LtrLzej+9FtIcq3uIrYnsac47Pf7p0otcFeTJTjSq3
+krCaoG4Hx0zGQG2ZFpHrSrZTVy6lxvIdfi0beMgY6h78p6M9eYZHQHc02DjFkQXN
+bXb5c6gCHESH5PXwPU4jQEE7Ib9J6sbk7ZT2Mw==
+=j+4q
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/.actions/setup_clang b/.actions/setup_clang
new file mode 100755
index 000000000000..be06709c88b6
--- /dev/null
+++ b/.actions/setup_clang
@@ -0,0 +1,17 @@
+#!/bin/sh -eu
+
+# Copyright (c) 2022 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+# SPDX-License-Identifier: BSD-2-Clause
+
+CC="$1"
+APT="http://apt.llvm.org"
+CODENAME="$(lsb_release -cs)"
+VERSION="${CC#*-}"
+apt-get install -q -y software-properties-common
+apt-key add ./.actions/llvm.gpg
+add-apt-repository \
+ "deb ${APT}/${CODENAME}/ llvm-toolchain-${CODENAME}-${VERSION} main"
+apt-get update -q
+apt-get install -q -y "${CC}" "clang-tools-${VERSION}"
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000000..998f601da4d0
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+.* export-ignore
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 000000000000..6e88c1ff8006
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,80 @@
+---
+name: Bug report
+labels: 'bug report'
+about: Report a bug in libfido2
+
+---
+
+<!--
+
+Please use the questions below as a template, and review your answers
+for potentially sensitive information.
+
+Thank you!
+
+-->
+
+**What version of libfido2 are you using?**
+
+**What operating system are you running?**
+
+**What application are you using in conjunction with libfido2?**
+
+**How does the problem manifest itself?**
+
+**Is the problem reproducible?**
+
+**What are the steps that lead to the problem?**
+
+**Does the problem happen with different authenticators?**
+
+<!--
+
+fido2-token is provided by the fido2-tools package on Debian and Ubuntu,
+and shipped with libfido2 in macOS (Homebrew), Arch Linux, and Windows.
+
+-->
+
+**Please include the output of `fido2-token -L`.**
+
+<details>
+<summary><code>fido2-token -L</code></summary>
+<br>
+<pre>
+$ fido2-token -L
+
+</pre>
+</details>
+
+**Please include the output of `fido2-token -I`.**
+
+<details>
+<summary><code>fido2-token -I</code></summary>
+<br>
+<pre>
+$ fido2-token -I &lt;device&gt;
+
+</pre>
+</details>
+
+<!--
+
+You are strongly encouraged to only capture debug output using test
+credentials. Failure to do so can disclose sensitive information.
+
+-->
+
+**Please include the output of `FIDO_DEBUG=1`.**
+
+<details>
+<summary><code>FIDO_DEBUG=1</code></summary>
+<br>
+<pre>
+$ export FIDO_DEBUG=1
+$ &lt;command1&gt;
+$ &lt;command2&gt;
+(...)
+$ &lt;commandn&gt;
+
+</pre>
+</details>
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 000000000000..3ecb227ffeb0
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,8 @@
+blank_issues_enabled: false
+contact_links:
+ - name: Feature Request
+ url: https://github.com/Yubico/libfido2/discussions/new
+ about: Share ideas for new features
+ - name: Ask a question about libfido2
+ url: https://github.com/Yubico/libfido2/discussions/new
+ about: Ask the community for help
diff --git a/.github/workflows/alpine_builds.yml b/.github/workflows/alpine_builds.yml
new file mode 100644
index 000000000000..c6d826f39835
--- /dev/null
+++ b/.github/workflows/alpine_builds.yml
@@ -0,0 +1,39 @@
+# Copyright (c) 2022-2023 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+# SPDX-License-Identifier: BSD-2-Clause
+
+name: alpine
+
+on:
+ pull_request:
+ branches:
+ - main
+ push:
+ branches:
+ - main
+ - '*-ci'
+
+jobs:
+ build:
+ runs-on: ubuntu-20.04
+ container: alpine:latest
+ strategy:
+ fail-fast: false
+ matrix:
+ cc: [ gcc, clang ]
+ steps:
+ - name: dependencies
+ run: |
+ apk -q update
+ apk add build-base clang clang-analyzer cmake coreutils eudev-dev
+ apk add git linux-headers openssl-dev sudo zlib-dev pcsc-lite-dev \
+ libcbor-dev
+ - name: fix permissions on workdir
+ run: chown root:wheel "${GITHUB_WORKSPACE}"
+ - name: checkout libfido2
+ uses: actions/checkout@v4
+ - name: build libfido2
+ env:
+ CC: ${{ matrix.cc }}
+ run: ./.actions/build-linux-${CC}
diff --git a/.github/workflows/bsd_builds.yml b/.github/workflows/bsd_builds.yml
new file mode 100644
index 000000000000..366ea2141aca
--- /dev/null
+++ b/.github/workflows/bsd_builds.yml
@@ -0,0 +1,32 @@
+# Copyright (c) 2022 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+# SPDX-License-Identifier: BSD-2-Clause
+
+name: bsd
+
+on:
+ push:
+ branches:
+ - main
+ - '*-ci'
+
+jobs:
+ build:
+ if: github.repository == 'Yubico/libfido2'
+ runs-on: ubuntu-22.04
+ strategy:
+ fail-fast: false
+ matrix:
+ image: [freebsd/13.x, openbsd/7.2]
+ steps:
+ - uses: actions/checkout@v4
+ - name: dependencies
+ run: |
+ sudo apt -q update
+ sudo apt install -q -y curl jq
+ - name: build
+ env:
+ IMAGE: ${{ matrix.image }}
+ SOURCEHUT_TOKEN: ${{ secrets.SOURCEHUT_TOKEN }}
+ run: ./.actions/build-bsd
diff --git a/.github/workflows/cifuzz_oss.yml b/.github/workflows/cifuzz_oss.yml
new file mode 100644
index 000000000000..556d5ad36f7c
--- /dev/null
+++ b/.github/workflows/cifuzz_oss.yml
@@ -0,0 +1,46 @@
+# Copyright (c) 2022 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+# SPDX-License-Identifier: BSD-2-Clause
+
+name: cifuzz
+
+on:
+ pull_request:
+ branches:
+ - main
+ push:
+ branches:
+ - main
+ - '*-ci'
+
+jobs:
+ fuzzing:
+ if: github.repository == 'Yubico/libfido2'
+ runs-on: ubuntu-20.04
+ strategy:
+ fail-fast: false
+ matrix:
+ sanitizer: [address, undefined, memory]
+ steps:
+ - name: build fuzzers (${{ matrix.sanitizer }})
+ uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
+ with:
+ oss-fuzz-project-name: 'libfido2'
+ language: c
+ sanitizer: ${{ matrix.sanitizer }}
+ dry-run: false
+ - name: run fuzzers (${{ matrix.sanitizer }})
+ uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
+ with:
+ oss-fuzz-project-name: 'libfido2'
+ language: c
+ sanitizer: ${{ matrix.sanitizer }}
+ fuzz-seconds: 600
+ dry-run: false
+ - name: upload crash
+ uses: actions/upload-artifact@v3
+ if: failure()
+ with:
+ name: ${{ matrix.sanitizer }}-artifacts
+ path: ./out/artifacts
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
new file mode 100644
index 000000000000..a3a8d54d2daa
--- /dev/null
+++ b/.github/workflows/codeql-analysis.yml
@@ -0,0 +1,42 @@
+# Copyright (c) 2022 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+# SPDX-License-Identifier: BSD-2-Clause
+
+name: "codeql"
+
+on:
+ pull_request:
+ branches:
+ - main
+ push:
+ branches:
+ - main
+ - '*-ci'
+ schedule:
+ - cron: '0 0 * * 0'
+
+permissions:
+ security-events: write
+
+jobs:
+ codeql-build:
+ if: github.repository == 'Yubico/libfido2'
+ runs-on: ubuntu-22.04
+ steps:
+ - name: checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 2
+ - name: init codeql
+ uses: github/codeql-action/init@v2
+ - name: build
+ env:
+ CC: gcc
+ run: |
+ sudo apt -q update
+ sudo apt install -q -y libcbor-dev libudev-dev libz-dev original-awk \
+ libpcsclite-dev
+ ./.actions/build-linux-gcc
+ - name: perform codeql analysis
+ uses: github/codeql-action/analyze@v2
diff --git a/.github/workflows/cygwin_builds.yml b/.github/workflows/cygwin_builds.yml
new file mode 100644
index 000000000000..d8146c54904e
--- /dev/null
+++ b/.github/workflows/cygwin_builds.yml
@@ -0,0 +1,30 @@
+# Copyright (c) 2022 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+# SPDX-License-Identifier: BSD-2-Clause
+
+name: cygwin
+
+on:
+ pull_request:
+ branches:
+ - main
+ push:
+ branches:
+ - main
+ - '*-ci'
+
+jobs:
+ build:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ windows-2022 ]
+ arch: [ x64 ]
+ config: [ "Debug", "Release" ]
+ steps:
+ - uses: actions/checkout@v4
+ - name: build
+ run: |
+ .\windows\cygwin.ps1 -Config ${{ matrix.config }}
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml
new file mode 100644
index 000000000000..ec911cb92d92
--- /dev/null
+++ b/.github/workflows/linux_builds.yml
@@ -0,0 +1,57 @@
+# Copyright (c) 2022 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+# SPDX-License-Identifier: BSD-2-Clause
+
+name: linux
+
+on:
+ pull_request:
+ branches:
+ - main
+ push:
+ branches:
+ - main
+ - '*-ci'
+
+jobs:
+ build:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - { os: ubuntu-20.04, cc: gcc-8 }
+ - { os: ubuntu-22.04, cc: gcc-9 }
+ - { os: ubuntu-22.04, cc: gcc-10 }
+ - { os: ubuntu-22.04, cc: gcc-11 }
+ - { os: ubuntu-22.04, cc: gcc-12 }
+ - { os: ubuntu-22.04, cc: clang-13 }
+ - { os: ubuntu-22.04, cc: clang-14 }
+ - { os: ubuntu-22.04, cc: clang-15 }
+ - { os: ubuntu-22.04, cc: clang-16 }
+ - { os: ubuntu-20.04, cc: i686-w64-mingw32-gcc-9 }
+ - { os: ubuntu-22.04, cc: i686-w64-mingw32-gcc-10 }
+ steps:
+ - uses: actions/checkout@v4
+ - name: dependencies
+ run: |
+ sudo apt -q update
+ sudo apt install -q -y libcbor-dev libudev-dev libz-dev \
+ original-awk mandoc libpcsclite-dev
+ - name: compiler
+ env:
+ CC: ${{ matrix.cc }}
+ run: |
+ if [ "${CC%-*}" == "clang" ]; then
+ sudo ./.actions/setup_clang "${CC}"
+ elif [ "${CC%-*}" == "i686-w64-mingw32-gcc" ]; then
+ sudo apt install -q -y binutils-mingw-w64-i686 gcc-mingw-w64 \
+ g++-mingw-w64 mingw-w64-i686-dev
+ else
+ sudo apt install -q -y "${CC}"
+ fi
+ - name: build
+ env:
+ CC: ${{ matrix.cc }}
+ run: ./.actions/build-linux-${CC%-*}
diff --git a/.github/workflows/linux_fuzz.yml b/.github/workflows/linux_fuzz.yml
new file mode 100644
index 000000000000..296c0d9fab23
--- /dev/null
+++ b/.github/workflows/linux_fuzz.yml
@@ -0,0 +1,41 @@
+# Copyright (c) 2022 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+# SPDX-License-Identifier: BSD-2-Clause
+
+name: fuzzer
+
+on:
+ pull_request:
+ branches:
+ - main
+ push:
+ branches:
+ - main
+ - '*-ci'
+
+jobs:
+ build:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ ubuntu-22.04 ]
+ cc: [ clang-16 ]
+ sanitizer: [ asan, msan ]
+ steps:
+ - uses: actions/checkout@v4
+ - name: dependencies
+ run: |
+ sudo apt -q update
+ sudo apt install -q -y libudev-dev libpcsclite-dev
+ - name: compiler
+ env:
+ CC: ${{ matrix.cc }}
+ run: |
+ sudo ./.actions/setup_clang "${CC}"
+ - name: fuzz
+ env:
+ CC: ${{ matrix.cc }}
+ SANITIZER: ${{ matrix.sanitizer }}
+ run: ./.actions/fuzz-linux "${SANITIZER}"
diff --git a/.github/workflows/macos_builds.yml b/.github/workflows/macos_builds.yml
new file mode 100644
index 000000000000..7d84a750ac37
--- /dev/null
+++ b/.github/workflows/macos_builds.yml
@@ -0,0 +1,32 @@
+# Copyright (c) 2022 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+# SPDX-License-Identifier: BSD-2-Clause
+
+name: macos
+
+on:
+ pull_request:
+ branches:
+ - main
+ push:
+ branches:
+ - main
+ - '*-ci'
+
+jobs:
+ build:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ macos-13, macos-12 ]
+ cc: [ clang ]
+ steps:
+ - uses: actions/checkout@v4
+ - name: dependencies
+ run: brew install libcbor llvm mandoc openssl@3.0 pkg-config zlib
+ - name: build
+ env:
+ CC: ${{ matrix.cc }}
+ run: ./.actions/build-osx-clang
diff --git a/.github/workflows/openssl3.yml b/.github/workflows/openssl3.yml
new file mode 100644
index 000000000000..ee70c087d285
--- /dev/null
+++ b/.github/workflows/openssl3.yml
@@ -0,0 +1,51 @@
+# Copyright (c) 2022 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+# SPDX-License-Identifier: BSD-2-Clause
+
+name: openssl3
+
+on:
+ pull_request:
+ branches:
+ - main
+ push:
+ branches:
+ - main
+ - '*-ci'
+
+jobs:
+ build:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - os: ubuntu-22.04
+ cc: gcc-11
+ - os: ubuntu-22.04
+ cc: clang-16
+ - os: ubuntu-22.04
+ cc: i686-w64-mingw32-gcc-10
+ steps:
+ - uses: actions/checkout@v4
+ - name: dependencies
+ env:
+ CC: ${{ matrix.cc }}
+ run: |
+ sudo apt -q update
+ sudo apt install -q -y libcbor-dev libudev-dev libz-dev \
+ original-awk mandoc libpcsclite-dev
+ sudo apt remove -y libssl-dev
+ if [ "${CC%-*}" == "clang" ]; then
+ sudo ./.actions/setup_clang "${CC}"
+ elif [ "${CC%-*}" == "i686-w64-mingw32-gcc" ]; then
+ sudo apt install -q -y binutils-mingw-w64-i686 gcc-mingw-w64 \
+ g++-mingw-w64 mingw-w64-i686-dev
+ else
+ sudo apt install -q -y "${CC}"
+ fi
+ - name: build
+ env:
+ CC: ${{ matrix.cc }}
+ run: ./.actions/build-linux-openssl3-${CC%-*}
diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml
new file mode 100644
index 000000000000..bfc1eb3c1deb
--- /dev/null
+++ b/.github/workflows/windows_builds.yml
@@ -0,0 +1,32 @@
+# Copyright (c) 2022 Yubico AB. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+# SPDX-License-Identifier: BSD-2-Clause
+
+name: windows
+
+on:
+ pull_request:
+ branches:
+ - main
+ push:
+ branches:
+ - main
+ - '*-ci'
+
+jobs:
+ build:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [ windows-2022 ]
+ arch: [ x64, Win32, ARM64, ARM ]
+ type: [ dynamic, static ]
+ config: [ "Release" ]
+ steps:
+ - uses: actions/checkout@v4
+ - name: build
+ run: |
+ .\windows\build.ps1 -Fido2Flags '/analyze' -Arch ${{ matrix.arch }} `
+ -Type ${{ matrix.type }} -Config ${{ matrix.config }}
diff --git a/BUILDING.md b/BUILDING.md
index 8b4e1ceae092..18615397600c 100644
--- a/BUILDING.md
+++ b/BUILDING.md
@@ -2,6 +2,17 @@
This attempts to document various ways of building dhcpcd for your
platform.
+`./configure` is a POSIX shell script that works in a similar way
+to GNU configure.
+This works fine provided you don't force any exotic options down
+which may or may not be silently discarded.
+
+Some build time warnings are expected - the only platforms with zero
+warnings are DragonFlyBSD and NetBSD.
+It is expected that the platforms be improvded to support dhcpcd
+better.
+There maybe some loss of functionality, but for the most part,
+dhcpcd can work around these deficiencies.
## Size is an issue
To compile small dhcpcd, maybe to be used for installation media where
@@ -109,6 +120,16 @@ so don't set either `ipv6ra_own` or `slaac private` in `dhcpcd.conf` if you
want to have working IPv6 temporary addresses.
SLAAC private addresses are just as private, just stable.
+Linux SECCOMP is very dependant on libc vs kernel.
+When libc is changed and uses a syscall that dhcpcd is unaware of,
+SECCOMP may break dhcpcd.
+When this happens you can configure dhcpcd with --disable-seccomp
+so dhcpcd can use a POSIX resource limited sandbox with privilege separation
+still. If you do this, please report the issue so that we can adjust the
+SECCOMP filter so that dhcpcd can use SECCOMP once more.
+Or convince the libc/kernel people to adpot something more maintainable
+like FreeBSD's capsicum or OpenBSD's pledge.
+
## Init systems
We try and detect how dhcpcd should interact with system services at runtime.
If we cannot auto-detect how do to this, or it is wrong then
@@ -129,6 +150,14 @@ You can disable this with `--without-dev`, or `without-udev`.
NOTE: in Gentoo at least, `sys-fs/udev` as provided by systemd leaks memory
`sys-fs/eudev`, the fork of udev does not and as such is recommended.
+## crypto
+dhcpcd ships with some cryptographic routines taken from various upstreams.
+These are routinely monitored and try to be as up to date as possible.
+You can optionally configure dhcpcd with `--with-openssl` to use libcrypto
+to use these instead.
+This is not enabled by default, even if libcrypto is found because libcrypto
+generally lives in /usr and dhcpcd in /sbin which could be a separate
+filesystem.
## Importing into another source control system
To import the full sources, use the import target.
diff --git a/LICENSE b/LICENSE
index aba8b6aa6bdf..8fd3a41dcfce 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/Makefile b/Makefile
index 53f61063476b..a930d301ec0d 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,6 @@
SUBDIRS= src hooks
+PACKAGE= dhcpcd
VERSION!= sed -n 's/\#define VERSION[[:space:]]*"\(.*\)".*/\1/p' src/defs.h
DIST!= if test -d .git; then echo "dist-git"; \
@@ -8,11 +9,11 @@ FOSSILID?= current
GITREF?= HEAD
DISTSUFFIX=
-DISTPREFIX?= dhcpcd-${VERSION}${DISTSUFFIX}
-DISTFILEGZ?= ${DISTPREFIX}.tar.gz
+DISTPREFIX?= ${PACKAGE}-${VERSION}${DISTSUFFIX}
DISTFILE?= ${DISTPREFIX}.tar.xz
DISTINFO= ${DISTFILE}.distinfo
-DISTINFOSIGN= ${DISTINFO}.asc
+DISTINFOMD= ${DISTINFO}.md
+DISTSIGN= ${DISTFILE}.asc
CLEANFILES+= *.tar.xz
@@ -49,29 +50,41 @@ clean:
distclean: clean
rm -f config.h config.mk config.log \
- ${DISTFILE} ${DISTFILEGZ} ${DISTINFO} ${DISTINFOSIGN}
+ ${DISTFILE} ${DISTINFO} ${DISTINFOMD} ${DISTSIGN}
rm -f *.diff *.patch *.orig *.rej
for x in ${SUBDIRS} tests; do cd $$x; ${MAKE} $@ || exit $$?; cd ..; done
dist-git:
- git archive --prefix=${DISTPREFIX}/ ${GITREF} | xz >${DISTFILE}
+ git archive --prefix=${DISTPREFIX}/ v${VERSION} | xz >${DISTFILE}
dist-inst:
mkdir /tmp/${DISTPREFIX}
cp -RPp * /tmp/${DISTPREFIX}
(cd /tmp/${DISTPREFIX}; make clean)
- tar -cvjpf ${DISTFILE} -C /tmp ${DISTPREFIX}
+ tar -cvJpf ${DISTFILE} -C /tmp ${DISTPREFIX}
rm -rf /tmp/${DISTPREFIX}
dist: ${DIST}
distinfo: dist
- rm -f ${DISTINFO} ${DISTINFOSIGN}
- ${CKSUM} ${DISTFILE} >${DISTINFO}
- #printf "SIZE (${DISTFILE}) = %s\n" $$(wc -c <${DISTFILE}) >>${DISTINFO}
- ${PGP} --clearsign --output=${DISTINFOSIGN} ${DISTINFO}
- chmod 644 ${DISTINFOSIGN}
- ls -l ${DISTFILE} ${DISTINFO} ${DISTINFOSIGN}
+ rm -f ${DISTINFO} ${DISTSIGN}
+ ${SHA256} ${DISTFILE} >${DISTINFO}
+ wc -c <${DISTFILE} \
+ | xargs printf 'Size (${DISTFILE}) = %s\n' >>${DISTINFO}
+ ${PGP} --armour --detach-sign ${DISTFILE}
+ chmod 644 ${DISTSIGN}
+ ls -l ${DISTFILE} ${DISTINFO} ${DISTSIGN}
+
+${DISTINFOMD}: ${DISTINFO}
+ echo '```' >${DISTINFOMD}
+ cat ${DISTINFO} >>${DISTINFOMD}
+ echo '```' >>${DISTINFOMD}
+
+release: distinfo ${DISTINFOMD}
+ gh release create v${VERSION} \
+ --title "${PACKAGE} ${VERSION}" --draft --generate-notes \
+ --notes-file ${DISTINFOMD} \
+ ${DISTFILE} ${DISTSIGN}
snapshot:
rm -rf /tmp/${DISTPREFIX}
@@ -87,7 +100,7 @@ _import: dist
tar xvpf ${DISTFILE} -C ${DESTDIR} --strip 1
@${ECHO}
@${ECHO} "============================================================="
- @${ECHO} "dhcpcd-${VERSION} imported to ${DESTDIR}"
+ @${ECHO} "${PACKAGE}-${VERSION} imported to ${DESTDIR}"
import:
${MAKE} _import DESTDIR=`if [ -n "${DESTDIR}" ]; then echo "${DESTDIR}"; else echo /tmp/${DISTPREFIX}; fi`
@@ -100,7 +113,7 @@ _import-src: clean
for x in ${SUBDIRS}; do cd $$x; ${MAKE} DESTDIR=${DESTDIR} $@ || exit $$?; cd ..; done
@${ECHO}
@${ECHO} "============================================================="
- @${ECHO} "dhcpcd-${VERSION} imported to ${DESTDIR}"
+ @${ECHO} "${PACKAGE}-${VERSION} imported to ${DESTDIR}"
import-src:
${MAKE} _import-src DESTDIR=`if [ -n "${DESTDIR}" ]; then echo "${DESTDIR}"; else echo /tmp/${DISTPREFIX}; fi`
diff --git a/Makefile.inc b/Makefile.inc
index 18b2824dd172..de18984da50e 100644
--- a/Makefile.inc
+++ b/Makefile.inc
@@ -15,9 +15,12 @@ LINT?= lint
SED?= sed
HOST_SH?= /bin/sh
-# This isn't very portable, but I generaly make releases from NetBSD
-CKSUM?= cksum -a SHA256
-PGP?= netpgp
+# This isn't very portable, but I generaly make releases from DragonFlyBSD
+SHA256?= sha256
+PGP?= gpg
+# old NetBSD defs
+#SHA256?= cksum -a SHA256
+#PGP?= netpgp
SCRIPT= ${LIBEXECDIR}/dhcpcd-run-hooks
HOOKDIR= ${LIBEXECDIR}/dhcpcd-hooks
diff --git a/README.md b/README.md
index 751ea3d440b2..a2e0892edf2e 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,11 @@
# dhcpcd
dhcpcd is a
-[DHCP](http://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol) and a
-[DHCPv6](http://en.wikipedia.org/wiki/DHCPv6) client.
-It's also an IPv4LL (aka [ZeroConf](http://en.wikipedia.org/wiki/Zeroconf))
+[DHCP](https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol) and a
+[DHCPv6](https://en.wikipedia.org/wiki/DHCPv6) client.
+It's also an IPv4LL (aka [ZeroConf](https://en.wikipedia.org/wiki/Zeroconf))
client.
-In layman's terms, dhcpcd runs on your machine and silently configures your
+In layperson's terms, dhcpcd runs on your machine and silently configures your
computer to work on the attached networks without trouble and mostly without
configuration.
@@ -78,8 +78,8 @@ which only apply to calling dhcpcd from the command line.
## Compatibility
-dhcpcd-5 is only fully command line compatible with dhcpcd-4
-For compatibility with older versions, use dhcpcd-4
+dhcpcd-5 is only fully command line compatible with dhcpcd-4.
+For compatibility with older versions, use dhcpcd-4.
## Upgrading
dhcpcd-7 defaults the database directory to `/var/db/dhcpcd` instead of
@@ -87,10 +87,10 @@ dhcpcd-7 defaults the database directory to `/var/db/dhcpcd` instead of
in /etc.
dhcpcd-9 defaults the run directory to `/var/run/dhcpcd` instead of
-`/var/run` and the prefix of dhcpcd has been removed from the files.
+`/var/run` and the prefix of dhcpcd has been removed from the files therein.
## ChangeLog
We no longer supply a ChangeLog.
However, you're more than welcome to read the
-[commit log](https://roy.marples.name/git/dhcpcd/log) and
-[archived release announcements](http://roy.marples.name/archives/dhcpcd-discuss/).
+[commit log](https://github.com/NetworkConfiguration/dhcpcd/commits) and
+[release announcements](https://github.com/NetworkConfiguration/dhcpcd/releases).
diff --git a/compat/arc4random.c b/compat/arc4random.c
index 90098127c954..d61d61ffa788 100644
--- a/compat/arc4random.c
+++ b/compat/arc4random.c
@@ -1,173 +1,317 @@
+/* $OpenBSD: arc4random.c,v 1.58 2022/07/31 13:41:45 tb Exp $ */
+
/*
- * Arc4 random number generator for OpenBSD.
- * Copyright 1996 David Mazieres <dm@lcs.mit.edu>.
+ * Copyright (c) 1996, David Mazieres <dm@uun.org>
+ * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
+ * Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
+ * Copyright (c) 2014, Theo de Raadt <deraadt@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
*
- * Modification and redistribution in source and binary forms is
- * permitted provided that due credit is given to the author and the
- * OpenBSD project by leaving this copyright notice intact.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
- * This code is derived from section 17.1 of Applied Cryptography,
- * second edition, which describes a stream cipher allegedly
- * compatible with RSA Labs "RC4" cipher (the actual description of
- * which is a trade secret). The same algorithm is used as a stream
- * cipher called "arcfour" in Tatu Ylonen's ssh package.
- *
- * Here the stream cipher has been modified always to include the time
- * when initializing the state. That makes it impossible to
- * regenerate the same random sequence twice, so this can't be used
- * for encryption, but will generate good random numbers.
- *
- * RC4 is a registered trademark of RSA Laboratories.
+ * ChaCha based random number generator for OpenBSD.
*/
-#include <sys/time.h>
+/*
+ * OPENBSD ORIGINAL: lib/libc/crypt/arc4random.c
+ * lib/libc/crypt/arc4random.h
+ */
+
+#include "config.h"
#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
-#include "arc4random.h"
+#if defined(HAVE_OPENSSL)
+#include <openssl/rand.h>
+#endif
-struct arc4_stream {
- uint8_t i;
- uint8_t j;
- uint8_t s[256];
- size_t count;
- pid_t stir_pid;
- int fd;
-};
+#define KEYSTREAM_ONLY
+#include "chacha_private.h"
-#define S(n) (n)
-#define S4(n) S(n), S(n + 1), S(n + 2), S(n + 3)
-#define S16(n) S4(n), S4(n + 4), S4(n + 8), S4(n + 12)
-#define S64(n) S16(n), S16(n + 16), S16(n + 32), S16(n + 48)
-#define S256 S64(0), S64(64), S64(128), S64(192)
+#define minimum(a, b) ((a) < (b) ? (a) : (b))
-static struct arc4_stream rs = { .i = 0xff, .j = 0, .s = { S256 },
- .count = 0, .stir_pid = 0, .fd = -1 };
+#if defined(__GNUC__) || defined(_MSC_VER)
+#define inline __inline
+#else /* __GNUC__ || _MSC_VER */
+#define inline
+#endif /* !__GNUC__ && !_MSC_VER */
-#undef S
-#undef S4
-#undef S16
-#undef S64
-#undef S256
+#define KEYSZ 32
+#define IVSZ 8
+#define BLOCKSZ 64
+#define RSBUFSZ (16*BLOCKSZ)
-static void
-arc4_addrandom(struct arc4_stream *as, unsigned char *dat, int datlen)
+#define REKEY_BASE (1024*1024) /* NB. should be a power of 2 */
+
+/* Marked MAP_INHERIT_ZERO, so zero'd out in fork children. */
+static struct _rs {
+ size_t rs_have; /* valid bytes at end of rs_buf */
+ size_t rs_count; /* bytes till reseed */
+} *rs;
+
+/* Maybe be preserved in fork children, if _rs_allocate() decides. */
+static struct _rsx {
+ chacha_ctx rs_chacha; /* chacha context for random keystream */
+ u_char rs_buf[RSBUFSZ]; /* keystream blocks */
+} *rsx;
+
+static int _dhcpcd_rand_fd = -1; /* /dev/urandom fd */
+
+static int _dhcpcd_getentropy(void *, size_t);
+static inline int _rs_allocate(struct _rs **, struct _rsx **);
+
+/* dhcpcd needs to hold onto the fd at fork due to privsep */
+#if 0
+static inline void _rs_forkdetect(void);
+#else
+#define _rs_forkdetect()
+#define _rs_forkhandler()
+#endif
+
+/* Inline "arc4random.h" */
+#include <sys/types.h>
+#include <sys/mman.h>
+
+static inline void _rs_rekey(u_char *dat, size_t datlen);
+
+/* dhcpcd isn't multithreaded */
+#define _ARC4_LOCK()
+#define _ARC4_UNLOCK()
+
+static int
+_dhcpcd_getentropy(void *buf, size_t length)
+{
+ struct timeval tv;
+ uint8_t *rand = (uint8_t *)buf;
+
+#if defined (HAVE_OPENSSL)
+ if (RAND_priv_bytes(buf, (int)length) == 1)
+ return (0);
+#endif
+
+ if (length < sizeof(tv)) {
+ gettimeofday(&tv, NULL);
+ memcpy(buf, &tv, sizeof(tv));
+ length -= sizeof(tv);
+ rand += sizeof(tv);
+ }
+ if (_dhcpcd_rand_fd == -1)
+ _dhcpcd_rand_fd = open("/dev/urandom", O_RDONLY | O_NONBLOCK);
+ if (_dhcpcd_rand_fd != -1) {
+ /* coverity[check_return] */
+ (void)read(_dhcpcd_rand_fd, rand, length);
+ }
+
+ /* Never fail. If there is an error reading from /dev/urandom,
+ * just use what is on the stack. */
+ return (0);
+}
+
+static inline void
+_getentropy_fail(void)
+{
+ raise(SIGKILL);
+}
+
+#if 0
+static volatile sig_atomic_t _rs_forked;
+
+static inline void
+_rs_forkhandler(void)
+{
+ _rs_forked = 1;
+}
+
+static inline void
+_rs_forkdetect(void)
{
- int n;
- uint8_t si;
-
- as->i--;
- for (n = 0; n < 256; n++) {
- as->i = (uint8_t)(as->i + 1);
- si = as->s[as->i];
- as->j = (uint8_t)(as->j + si + dat[n % datlen]);
- as->s[as->i] = as->s[as->j];
- as->s[as->j] = si;
+ static pid_t _rs_pid = 0;
+ pid_t pid = getpid();
+
+ /* XXX unusual calls to clone() can bypass checks */
+ if (_rs_pid == 0 || _rs_pid == 1 || _rs_pid != pid || _rs_forked) {
+ _rs_pid = pid;
+ _rs_forked = 0;
+ if (rs)
+ memset(rs, 0, sizeof(*rs));
}
- as->j = as->i;
}
+#endif
-static uint8_t
-arc4_getbyte(struct arc4_stream *as)
+static inline int
+_rs_allocate(struct _rs **rsp, struct _rsx **rsxp)
{
- uint8_t si, sj;
-
- as->i = (uint8_t)(as->i + 1);
- si = as->s[as->i];
- as->j = (uint8_t)(as->j + si);
- sj = as->s[as->j];
- as->s[as->i] = sj;
- as->s[as->j] = si;
- return (as->s[(si + sj) & 0xff]);
+ if ((*rsp = mmap(NULL, sizeof(**rsp), PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED)
+ return (-1);
+
+ if ((*rsxp = mmap(NULL, sizeof(**rsxp), PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
+ munmap(*rsp, sizeof(**rsp));
+ *rsp = NULL;
+ return (-1);
+ }
+
+ _rs_forkhandler();
+ return (0);
}
-static uint32_t
-arc4_getword(struct arc4_stream *as)
+static inline void
+_rs_init(u_char *buf, size_t n)
{
- int val;
+ if (n < KEYSZ + IVSZ)
+ return;
+
+ if (rs == NULL) {
+ if (_rs_allocate(&rs, &rsx) == -1)
+ _exit(1);
+ }
- val = (int)((unsigned int)arc4_getbyte(as) << 24);
- val |= arc4_getbyte(as) << 16;
- val |= arc4_getbyte(as) << 8;
- val |= arc4_getbyte(as);
- return (uint32_t)val;
+ chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8);
+ chacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ);
}
-/* We don't care about any error on read, just use what we have
- * on the stack. So mask off this GCC warning. */
-#pragma GCC diagnostic ignored "-Wunused-result"
static void
-arc4_stir(struct arc4_stream *as)
+_rs_stir(void)
{
- struct {
- struct timeval tv;
- unsigned int rnd[(128 - sizeof(struct timeval)) /
- sizeof(unsigned int)];
- } rdat;
- size_t n;
-
- gettimeofday(&rdat.tv, NULL);
- if (as->fd == -1) {
-#ifndef O_CLOEXEC
- int fd_opts;
-#endif
+ u_char rnd[KEYSZ + IVSZ];
+ uint32_t rekey_fuzz = 0;
- as->fd = open("/dev/urandom", O_RDONLY | O_NONBLOCK
-#ifdef O_CLOEXEC
- | O_CLOEXEC
+ if (_dhcpcd_getentropy(rnd, sizeof rnd) == -1)
+ _getentropy_fail();
+
+ if (!rs)
+ _rs_init(rnd, sizeof(rnd));
+ else
+ _rs_rekey(rnd, sizeof(rnd));
+#if defined(HAVE_EXPLICIT_BZERO)
+ explicit_bzero(rnd, sizeof(rnd)); /* discard source seed */
+#elif defined(HAVE_MEMSET_EXPLICIT)
+ (void)memset_explicit(rnd, 0, sizeof(rnd));
+#elif defined(HAVE_MEMSET_S)
+ (void)memset_s(rnd, sizeof(rnd), 0, sizeof(rnd));
+#else
+#warning potentially insecure use of memset discarding the source seed
+ (void)memset(rnd, 0, sizeof(rnd)); /* discard source seed */
#endif
- );
-#ifndef O_CLOEXEC
- if (as->fd != -1 &&
- (fd_opts = fcntl(as->fd, F_GETFD)))
- fcntl(as->fd, F_SETFD, fd_opts | FD_CLOEXEC);
+
+ /* invalidate rs_buf */
+ rs->rs_have = 0;
+ memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
+
+ /* rekey interval should not be predictable */
+ chacha_encrypt_bytes(&rsx->rs_chacha, (uint8_t *)&rekey_fuzz,
+ (uint8_t *)&rekey_fuzz, sizeof(rekey_fuzz));
+ rs->rs_count = REKEY_BASE + (rekey_fuzz % REKEY_BASE);
+}
+
+static inline void
+_rs_stir_if_needed(size_t len)
+{
+ _rs_forkdetect();
+ if (!rs || rs->rs_count <= len)
+ _rs_stir();
+ if (rs->rs_count <= len)
+ rs->rs_count = 0;
+ else
+ rs->rs_count -= len;
+}
+
+static inline void
+_rs_rekey(u_char *dat, size_t datlen)
+{
+#ifndef KEYSTREAM_ONLY
+ memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
#endif
- }
+ /* fill rs_buf with the keystream */
+ chacha_encrypt_bytes(&rsx->rs_chacha, rsx->rs_buf,
+ rsx->rs_buf, sizeof(rsx->rs_buf));
+ /* mix in optional user provided data */
+ if (dat) {
+ size_t i, m;
- if (as->fd != -1) {
- /* If there is an error reading, just use what is
- * on the stack. */
- /* coverity[check_return] */
- (void)read(as->fd, rdat.rnd, sizeof(rdat.rnd));
+ m = minimum(datlen, KEYSZ + IVSZ);
+ for (i = 0; i < m; i++)
+ rsx->rs_buf[i] ^= dat[i];
}
+ /* immediately reinit for backtracking resistance */
+ _rs_init(rsx->rs_buf, KEYSZ + IVSZ);
+ memset(rsx->rs_buf, 0, KEYSZ + IVSZ);
+ rs->rs_have = sizeof(rsx->rs_buf) - KEYSZ - IVSZ;
+}
- /* fd < 0? Ah, what the heck. We'll just take
- * whatever was on the stack... */
- /* coverity[uninit_use_in_call] */
- arc4_addrandom(as, (void *) &rdat, sizeof(rdat));
-
- /*
- * Throw away the first N words of output, as suggested in the
- * paper "Weaknesses in the Key Scheduling Algorithm of RC4"
- * by Fluher, Mantin, and Shamir. (N = 256 in our case.)
- */
- for (n = 0; n < 256 * sizeof(uint32_t); n++)
- arc4_getbyte(as);
- as->count = 1600000;
+static inline void
+_rs_random_buf(void *_buf, size_t n)
+{
+ u_char *buf = (u_char *)_buf;
+ u_char *keystream;
+ size_t m;
+
+ _rs_stir_if_needed(n);
+ while (n > 0) {
+ if (rs->rs_have > 0) {
+ m = minimum(n, rs->rs_have);
+ keystream = rsx->rs_buf + sizeof(rsx->rs_buf)
+ - rs->rs_have;
+ memcpy(buf, keystream, m);
+ memset(keystream, 0, m);
+ buf += m;
+ n -= m;
+ rs->rs_have -= m;
+ }
+ if (rs->rs_have == 0)
+ _rs_rekey(NULL, 0);
+ }
}
-static void
-arc4_stir_if_needed(struct arc4_stream *as)
+static inline void
+_rs_random_u32(uint32_t *val)
{
- pid_t pid;
-
- pid = getpid();
- if (as->count <= sizeof(uint32_t) || as->stir_pid != pid) {
- as->stir_pid = pid;
- arc4_stir(as);
- } else
- as->count -= sizeof(uint32_t);
+ u_char *keystream;
+
+ _rs_stir_if_needed(sizeof(*val));
+ if (rs->rs_have < sizeof(*val))
+ _rs_rekey(NULL, 0);
+ keystream = rsx->rs_buf + sizeof(rsx->rs_buf) - rs->rs_have;
+ memcpy(val, keystream, sizeof(*val));
+ memset(keystream, 0, sizeof(*val));
+ rs->rs_have -= sizeof(*val);
}
uint32_t
-arc4random()
+arc4random(void)
{
+ uint32_t val;
+
+ _ARC4_LOCK();
+ _rs_random_u32(&val);
+ _ARC4_UNLOCK();
+ return val;
+}
- arc4_stir_if_needed(&rs);
- return arc4_getword(&rs);
+void
+arc4random_buf(void *buf, size_t n)
+{
+ _ARC4_LOCK();
+ _rs_random_buf(buf, n);
+ _ARC4_UNLOCK();
}
diff --git a/compat/arc4random.h b/compat/arc4random.h
index a975fef3cd1b..ea1d6369235b 100644
--- a/compat/arc4random.h
+++ b/compat/arc4random.h
@@ -13,4 +13,6 @@
#include <stdint.h>
uint32_t arc4random(void);
+void arc4random_buf(void *, size_t);
+
#endif
diff --git a/compat/arc4random_uniform.c b/compat/arc4random_uniform.c
index 0f1b7b296759..4511722909b3 100644
--- a/compat/arc4random_uniform.c
+++ b/compat/arc4random_uniform.c
@@ -1,3 +1,5 @@
+/* $OpenBSD: arc4random_uniform.c,v 1.3 2019/01/20 02:59:07 bcook Exp $ */
+
/*
* Copyright (c) 2008, Damien Miller <djm@openbsd.org>
*
@@ -48,9 +50,11 @@ arc4random_uniform(uint32_t upper_bound)
* number inside the range we need, so it should rarely need
* to re-roll.
*/
- do
+ for (;;) {
r = arc4random();
- while (r < min);
+ if (r >= min)
+ break;
+ }
return r % upper_bound;
}
diff --git a/compat/chacha_private.h b/compat/chacha_private.h
new file mode 100644
index 000000000000..b0427b6b3e86
--- /dev/null
+++ b/compat/chacha_private.h
@@ -0,0 +1,222 @@
+/*
+chacha-merged.c version 20080118
+D. J. Bernstein
+Public domain.
+*/
+
+/* $OpenBSD: chacha_private.h,v 1.3 2022/02/28 21:56:29 dtucker Exp $ */
+
+typedef unsigned char u8;
+typedef unsigned int u32;
+
+typedef struct
+{
+ u32 input[16]; /* could be compressed */
+} chacha_ctx;
+
+#define U8C(v) (v##U)
+#define U32C(v) (v##U)
+
+#define U8V(v) ((u8)(v) & U8C(0xFF))
+#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF))
+
+#define ROTL32(v, n) \
+ (U32V((v) << (n)) | ((v) >> (32 - (n))))
+
+#define U8TO32_LITTLE(p) \
+ (((u32)((p)[0]) ) | \
+ ((u32)((p)[1]) << 8) | \
+ ((u32)((p)[2]) << 16) | \
+ ((u32)((p)[3]) << 24))
+
+#define U32TO8_LITTLE(p, v) \
+ do { \
+ (p)[0] = U8V((v) ); \
+ (p)[1] = U8V((v) >> 8); \
+ (p)[2] = U8V((v) >> 16); \
+ (p)[3] = U8V((v) >> 24); \
+ } while (0)
+
+#define ROTATE(v,c) (ROTL32(v,c))
+#define XOR(v,w) ((v) ^ (w))
+#define PLUS(v,w) (U32V((v) + (w)))
+#define PLUSONE(v) (PLUS((v),1))
+
+#define QUARTERROUND(a,b,c,d) \
+ a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \
+ c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \
+ a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \
+ c = PLUS(c,d); b = ROTATE(XOR(b,c), 7);
+
+static const char sigma[16] = "expand 32-byte k";
+static const char tau[16] = "expand 16-byte k";
+
+static void
+chacha_keysetup(chacha_ctx *x,const u8 *k,u32 kbits)
+{
+ const char *constants;
+
+ x->input[4] = U8TO32_LITTLE(k + 0);
+ x->input[5] = U8TO32_LITTLE(k + 4);
+ x->input[6] = U8TO32_LITTLE(k + 8);
+ x->input[7] = U8TO32_LITTLE(k + 12);
+ if (kbits == 256) { /* recommended */
+ k += 16;
+ constants = sigma;
+ } else { /* kbits == 128 */
+ constants = tau;
+ }
+ x->input[8] = U8TO32_LITTLE(k + 0);
+ x->input[9] = U8TO32_LITTLE(k + 4);
+ x->input[10] = U8TO32_LITTLE(k + 8);
+ x->input[11] = U8TO32_LITTLE(k + 12);
+ x->input[0] = U8TO32_LITTLE(constants + 0);
+ x->input[1] = U8TO32_LITTLE(constants + 4);
+ x->input[2] = U8TO32_LITTLE(constants + 8);
+ x->input[3] = U8TO32_LITTLE(constants + 12);
+}
+
+static void
+chacha_ivsetup(chacha_ctx *x,const u8 *iv)
+{
+ x->input[12] = 0;
+ x->input[13] = 0;
+ x->input[14] = U8TO32_LITTLE(iv + 0);
+ x->input[15] = U8TO32_LITTLE(iv + 4);
+}
+
+static void
+chacha_encrypt_bytes(chacha_ctx *x,const u8 *m,u8 *c,u32 bytes)
+{
+ u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
+ u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
+ u8 *ctarget = NULL;
+ u8 tmp[64];
+ u_int i;
+
+ if (!bytes) return;
+
+ j0 = x->input[0];
+ j1 = x->input[1];
+ j2 = x->input[2];
+ j3 = x->input[3];
+ j4 = x->input[4];
+ j5 = x->input[5];
+ j6 = x->input[6];
+ j7 = x->input[7];
+ j8 = x->input[8];
+ j9 = x->input[9];
+ j10 = x->input[10];
+ j11 = x->input[11];
+ j12 = x->input[12];
+ j13 = x->input[13];
+ j14 = x->input[14];
+ j15 = x->input[15];
+
+ for (;;) {
+ if (bytes < 64) {
+ for (i = 0;i < bytes;++i) tmp[i] = m[i];
+ m = tmp;
+ ctarget = c;
+ c = tmp;
+ }
+ x0 = j0;
+ x1 = j1;
+ x2 = j2;
+ x3 = j3;
+ x4 = j4;
+ x5 = j5;
+ x6 = j6;
+ x7 = j7;
+ x8 = j8;
+ x9 = j9;
+ x10 = j10;
+ x11 = j11;
+ x12 = j12;
+ x13 = j13;
+ x14 = j14;
+ x15 = j15;
+ for (i = 20;i > 0;i -= 2) {
+ QUARTERROUND( x0, x4, x8,x12)
+ QUARTERROUND( x1, x5, x9,x13)
+ QUARTERROUND( x2, x6,x10,x14)
+ QUARTERROUND( x3, x7,x11,x15)
+ QUARTERROUND( x0, x5,x10,x15)
+ QUARTERROUND( x1, x6,x11,x12)
+ QUARTERROUND( x2, x7, x8,x13)
+ QUARTERROUND( x3, x4, x9,x14)
+ }
+ x0 = PLUS(x0,j0);
+ x1 = PLUS(x1,j1);
+ x2 = PLUS(x2,j2);
+ x3 = PLUS(x3,j3);
+ x4 = PLUS(x4,j4);
+ x5 = PLUS(x5,j5);
+ x6 = PLUS(x6,j6);
+ x7 = PLUS(x7,j7);
+ x8 = PLUS(x8,j8);
+ x9 = PLUS(x9,j9);
+ x10 = PLUS(x10,j10);
+ x11 = PLUS(x11,j11);
+ x12 = PLUS(x12,j12);
+ x13 = PLUS(x13,j13);
+ x14 = PLUS(x14,j14);
+ x15 = PLUS(x15,j15);
+
+#ifndef KEYSTREAM_ONLY
+ x0 = XOR(x0,U8TO32_LITTLE(m + 0));
+ x1 = XOR(x1,U8TO32_LITTLE(m + 4));
+ x2 = XOR(x2,U8TO32_LITTLE(m + 8));
+ x3 = XOR(x3,U8TO32_LITTLE(m + 12));
+ x4 = XOR(x4,U8TO32_LITTLE(m + 16));
+ x5 = XOR(x5,U8TO32_LITTLE(m + 20));
+ x6 = XOR(x6,U8TO32_LITTLE(m + 24));
+ x7 = XOR(x7,U8TO32_LITTLE(m + 28));
+ x8 = XOR(x8,U8TO32_LITTLE(m + 32));
+ x9 = XOR(x9,U8TO32_LITTLE(m + 36));
+ x10 = XOR(x10,U8TO32_LITTLE(m + 40));
+ x11 = XOR(x11,U8TO32_LITTLE(m + 44));
+ x12 = XOR(x12,U8TO32_LITTLE(m + 48));
+ x13 = XOR(x13,U8TO32_LITTLE(m + 52));
+ x14 = XOR(x14,U8TO32_LITTLE(m + 56));
+ x15 = XOR(x15,U8TO32_LITTLE(m + 60));
+#endif
+
+ j12 = PLUSONE(j12);
+ if (!j12) {
+ j13 = PLUSONE(j13);
+ /* stopping at 2^70 bytes per nonce is user's responsibility */
+ }
+
+ U32TO8_LITTLE(c + 0,x0);
+ U32TO8_LITTLE(c + 4,x1);
+ U32TO8_LITTLE(c + 8,x2);
+ U32TO8_LITTLE(c + 12,x3);
+ U32TO8_LITTLE(c + 16,x4);
+ U32TO8_LITTLE(c + 20,x5);
+ U32TO8_LITTLE(c + 24,x6);
+ U32TO8_LITTLE(c + 28,x7);
+ U32TO8_LITTLE(c + 32,x8);
+ U32TO8_LITTLE(c + 36,x9);
+ U32TO8_LITTLE(c + 40,x10);
+ U32TO8_LITTLE(c + 44,x11);
+ U32TO8_LITTLE(c + 48,x12);
+ U32TO8_LITTLE(c + 52,x13);
+ U32TO8_LITTLE(c + 56,x14);
+ U32TO8_LITTLE(c + 60,x15);
+
+ if (bytes <= 64) {
+ if (bytes < 64) {
+ for (i = 0;i < bytes;++i) ctarget[i] = c[i];
+ }
+ x->input[12] = j12;
+ x->input[13] = j13;
+ return;
+ }
+ bytes -= 64;
+ c += 64;
+#ifndef KEYSTREAM_ONLY
+ m += 64;
+#endif
+ }
+}
diff --git a/compat/closefrom.c b/compat/closefrom.c
new file mode 100644
index 000000000000..7161573e76b5
--- /dev/null
+++ b/compat/closefrom.c
@@ -0,0 +1,77 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2004-2005, 2007, 2010, 2012-2015, 2017-2018
+ * Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#ifdef __linux__
+# include <sys/syscall.h>
+# if defined(__NR_close_range) && !defined(SYS_close_range)
+# define SYS_close_range __NR_close_range
+# endif
+#endif
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#if defined(__linux__) && defined(SYS_close_range)
+static inline int
+sys_close_range(unsigned int fd, unsigned int max_fd, unsigned int flags)
+{
+
+ return (int)syscall(SYS_close_range, fd, max_fd, flags);
+}
+#endif
+
+/*
+ * Close all file descriptors greater than or equal to lowfd.
+ * This is the expensive (fallback) method.
+ */
+static int
+closefrom_fallback(int lowfd)
+{
+ int fd, maxfd;
+
+#ifdef _SC_OPEN_MAX
+ maxfd = (int)sysconf(_SC_OPEN_MAX);
+#else
+ maxfd = getdtablesize();
+#endif
+ if (maxfd == -1)
+ return -1;
+
+ for (fd = lowfd; fd < maxfd; fd++)
+ close(fd);
+ return 0;
+}
+
+/*
+ * * Close all file descriptors greater than or equal to lowfd.
+ * * We try the fast way first, falling back on the slow method.
+ * */
+void
+closefrom(int lowfd)
+{
+
+#if defined(__linux__) && defined(SYS_close_range)
+ if (sys_close_range((unsigned int)lowfd, UINT_MAX, 0) == 0)
+ return;
+#endif
+
+ closefrom_fallback(lowfd);
+}
diff --git a/compat/closefrom.h b/compat/closefrom.h
new file mode 100644
index 000000000000..70ce71f6bd63
--- /dev/null
+++ b/compat/closefrom.h
@@ -0,0 +1,24 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2004-2005, 2007, 2010, 2012-2015, 2017-2018
+ * Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef CLOSEFROM_H
+#define CLOSEFROM_H
+
+void closefrom(int);
+
+#endif
diff --git a/compat/crypt/md5.h b/compat/crypt/md5.h
index dd156163315c..402309c33ae6 100644
--- a/compat/crypt/md5.h
+++ b/compat/crypt/md5.h
@@ -19,7 +19,7 @@
#define MD5_H_
#define MD5_DIGEST_LENGTH 16
-#define MD5_BLOCK_LENGTH 64ULL
+#define MD5_BLOCK_LENGTH 64
typedef struct MD5Context {
uint32_t state[4]; /* state (ABCD) */
diff --git a/compat/crypt_openssl/hmac.c b/compat/crypt_openssl/hmac.c
new file mode 100644
index 000000000000..5f55cc30b105
--- /dev/null
+++ b/compat/crypt_openssl/hmac.c
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (c) 2023 Canonical Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "config.h"
+
+#include "openssl/hmac.h"
+
+ssize_t
+hmac(const char *name,
+ const void *key, size_t klen,
+ const void *text, size_t tlen,
+ void *digest, size_t dlen)
+{
+ const EVP_MD *md;
+ unsigned int outlen;
+
+ if (strcmp(name, "md5") == 0)
+ md = EVP_md5();
+ else if (strcmp(name, "sha256") == 0)
+ md = EVP_sha1();
+ else
+ return -1;
+
+ HMAC(md, key, (int)klen, text, tlen, digest, &outlen);
+ if (dlen != outlen)
+ return -1;
+
+ return (ssize_t)outlen;
+}
diff --git a/compat/crypt_openssl/hmac.h b/compat/crypt_openssl/hmac.h
new file mode 100644
index 000000000000..5729ed5b31fa
--- /dev/null
+++ b/compat/crypt_openssl/hmac.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (c) 2023 Canonical Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef HMAC_H
+#define HMAC_H
+
+#include <sys/types.h>
+
+ssize_t hmac(const char *, const void *, size_t, const void *, size_t, void *,
+ size_t);
+
+#endif
diff --git a/compat/crypt_openssl/sha256.c b/compat/crypt_openssl/sha256.c
new file mode 100644
index 000000000000..a1595835d2fe
--- /dev/null
+++ b/compat/crypt_openssl/sha256.c
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (c) 2023 Canonical Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "sha256.h"
+
+#include "openssl/evp.h"
+
+/* SHA-256 initialization. Begins a SHA-256 operation. */
+void
+dhcpcd_SHA256_Init(SHA256_CTX *ctx)
+{
+ ctx->c = EVP_MD_CTX_new();
+ EVP_DigestInit_ex2(ctx->c, EVP_sha256(), NULL);
+}
+
+/* Add bytes into the hash */
+void
+dhcpcd_SHA256_Update(SHA256_CTX *ctx, const void *in, size_t len)
+{
+ EVP_DigestUpdate(ctx->c, in, len);
+}
+
+/*
+ * SHA-256 finalization. Pads the input data, exports the hash value,
+ * and clears the context state.
+ */
+void
+dhcpcd_SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx)
+{
+ EVP_DigestFinal_ex(ctx->c, digest, NULL);
+ EVP_MD_CTX_free(ctx->c);
+}
diff --git a/compat/crypt_openssl/sha256.h b/compat/crypt_openssl/sha256.h
new file mode 100644
index 000000000000..74fabab7e76c
--- /dev/null
+++ b/compat/crypt_openssl/sha256.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (c) 2023 Canonical Ltd.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef SHA256_H_
+#define SHA256_H_
+
+#define SHA256_DIGEST_LENGTH 32
+
+#include "openssl/evp.h"
+typedef struct dhcpcd_SHA256Context {
+ EVP_MD_CTX *c;
+} dhcpcd_SHA256_CTX;
+
+void dhcpcd_SHA256_Init(dhcpcd_SHA256_CTX *);
+void dhcpcd_SHA256_Update(dhcpcd_SHA256_CTX *, const void *, size_t);
+void dhcpcd_SHA256_Final(unsigned char [32], dhcpcd_SHA256_CTX *);
+
+#define SHA256_Init dhcpcd_SHA256_Init
+#define SHA256_Update dhcpcd_SHA256_Update
+#define SHA256_Final dhcpcd_SHA256_Final
+#define SHA256_CTX dhcpcd_SHA256_CTX
+
+#endif
diff --git a/compat/pidfile.c b/compat/pidfile.c
index 9dfd9c468a7a..8d2e5a838c0e 100644
--- a/compat/pidfile.c
+++ b/compat/pidfile.c
@@ -1,4 +1,4 @@
-/* $NetBSD: pidfile.c,v 1.14 2016/04/12 20:40:43 roy Exp $ */
+/* $NetBSD: pidfile.c,v 1.16 2021/08/01 15:29:29 andvar Exp $ */
/*-
* Copyright (c) 1999, 2016 The NetBSD Foundation, Inc.
@@ -131,7 +131,7 @@ pidfile_varrun_path(char *path, size_t len, const char *bname)
}
/* Returns the process ID inside path on success, otherwise -1.
- * If no path is given, use the last pidfile path, othewise the default one. */
+ * If no path is given, use the last pidfile path, otherwise the default one. */
pid_t
pidfile_read(const char *path)
{
@@ -239,7 +239,7 @@ return_pid:
if (errno == EAGAIN) {
/* The pidfile is locked, return the process ID
* it contains.
- * If sucessful, set errno to EEXIST. */
+ * If successful, set errno to EEXIST. */
if ((pid = pidfile_read(path)) != -1)
errno = EEXIST;
} else
diff --git a/compat/rb.c b/compat/rb.c
index 3c0bed5f70d5..4dfbd0d29212 100644
--- a/compat/rb.c
+++ b/compat/rb.c
@@ -1,4 +1,4 @@
-/* $NetBSD: rb.c,v 1.14 2019/03/08 09:14:54 roy Exp $ */
+/* $NetBSD: rb.c,v 1.16 2021/09/16 21:29:41 andvar Exp $ */
/*-
* Copyright (c) 2001 The NetBSD Foundation, Inc.
@@ -32,6 +32,10 @@
#include "config.h"
#include "common.h"
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
#if !defined(_KERNEL) && !defined(_STANDALONE)
#include <sys/types.h>
#include <stddef.h>
@@ -44,10 +48,10 @@
#define KASSERT(s) do { } while (/*CONSTCOND*/ 0)
#define __rbt_unused __unused
#endif
-__RCSID("$NetBSD: rb.c,v 1.14 2019/03/08 09:14:54 roy Exp $");
+__RCSID("$NetBSD: rb.c,v 1.16 2021/09/16 21:29:41 andvar Exp $");
#else
#include <lib/libkern/libkern.h>
-__KERNEL_RCSID(0, "$NetBSD: rb.c,v 1.14 2019/03/08 09:14:54 roy Exp $");
+__KERNEL_RCSID(0, "$NetBSD: rb.c,v 1.16 2021/09/16 21:29:41 andvar Exp $");
#ifndef DIAGNOSTIC
#define __rbt_unused __unused
#else
@@ -312,7 +316,7 @@ rb_tree_insert_node(struct rb_tree *rbt, void *object)
KASSERT(rb_tree_check_node(rbt, self, NULL, true));
}
- /* Succesfully inserted, return our node pointer. */
+ /* Successfully inserted, return our node pointer. */
return object;
}
diff --git a/compat/strlcpy.c b/compat/strlcpy.c
index 1fb4a2f9615d..eceb4b4c05df 100644
--- a/compat/strlcpy.c
+++ b/compat/strlcpy.c
@@ -1,7 +1,7 @@
-/* $OpenBSD: strlcpy.c,v 1.15 2016/10/16 17:37:39 dtucker Exp $ */
+/* $OpenBSD: strlcpy.c,v 1.16 2019/01/25 00:19:25 millert Exp $ */
/*
- * Copyright (c) 1998, 2015 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
diff --git a/configure b/configure
index 19eb431816e7..0a28a0cff777 100755
--- a/configure
+++ b/configure
@@ -13,6 +13,7 @@ IPV4LL=
INET6=
PRIVSEP=
PRIVSEP_USER=
+SECCOMP=
ARC4RANDOM=
CLOSEFROM=
RBTREE=
@@ -36,6 +37,7 @@ POLL=
SMALL=
SANITIZE=no
STATUSARG=
+OPENSSL=
DHCPCD_DEFS=dhcpcd-definitions.conf
@@ -70,8 +72,10 @@ for x do
--enable-auth) AUTH=yes;;
--disable-privsep) PRIVSEP=no;;
--enable-privsep) PRIVSEP=yes;;
+ --disable-seccomp) SECCOMP=no;;
+ --enable-seccomp) SECCOMP=yes;;
--privsepuser) PRIVSEP_USER=$var;;
- --prefix) PREFIX=$var;;
+ --prefix) PREFIX=$var;prefix=$var;; # prefix is set for autotools compat
--sysconfdir) SYSCONFDIR=$var;;
--bindir|--sbindir) SBINDIR=$var;;
--libexecdir) LIBEXECDIR=$var;;
@@ -107,6 +111,8 @@ for x do
--with-udev) DEV=yes; UDEV=yes;;
--without-udev) UDEV=no;;
--with-poll) POLL="$var";;
+ --with-openssl) OPENSSL=yes;;
+ --without-openssl) OPENSSL=no;;
--sanitise|--sanitize) SANITIZEADDRESS="yes";;
--serviceexists) SERVICEEXISTS=$var;;
--servicecmd) SERVICECMD=$var;;
@@ -115,7 +121,7 @@ for x do
--statusarg) STATUSARG=$var;;
--infodir) ;; # ignore autotools
--disable-maintainer-mode|--disable-dependency-tracking) ;;
- --disable-silent-rules) ;;
+ --disable-option-checking|--disable-silent-rules) ;;
-V|--version)
v=$(sed -ne 's/.*VERSION[[:space:]]*"\([^"]*\).*/\1/p' defs.h);
c=$(sed -ne 's/^.*copyright\[\] = "\([^"]*\).*/\1/p' dhcpcd.c);
@@ -257,6 +263,10 @@ echo "Configuring dhcpcd for ... $OS"
rm -f $CONFIG_H $CONFIG_MK
echo "# $OS" >$CONFIG_MK
echo "/* $OS */" >$CONFIG_H
+echo >>$CONFIG_H
+echo "#ifndef CONFIG_H">>$CONFIG_H
+echo "#define CONFIG_H">>$CONFIG_H
+echo >>$CONFIG_H
: ${SYSCONFDIR:=$PREFIX/etc}
: ${SBINDIR:=$PREFIX/sbin}
@@ -357,7 +367,7 @@ else
ALLOW_USR_LIBS=true
fi
case "$OS" in
-linux*|solaris*|sunos*|kfreebsd*) ;;
+linux*|solaris*|sunos*|kfreebsd*|dragonfly*|freebsd*) ;;
*)
# There might be more than one ...
for LDELFN in /libexec/ld-elf.so.[0-9]*; do
@@ -585,7 +595,12 @@ if [ "$PRIVSEP" = yes ]; then
echo "PRIVSEP_SRCS+= privsep-bpf.c" >>$CONFIG_MK
fi
case "$OS" in
- linux*) echo "PRIVSEP_SRCS+= privsep-linux.c" >>$CONFIG_MK;;
+ linux*)
+ echo "PRIVSEP_SRCS+= privsep-linux.c" >>$CONFIG_MK
+ if [ -n "$SECCOMP" ] && [ "$SECCOMP" = no ]; then
+ echo "#define DISABLE_SECCOMP" >>$CONFIG_H
+ fi
+ ;;
solaris*|sunos*) echo "PRIVSEP_SRCS+= privsep-sun.c" >>$CONFIG_MK;;
*) echo "PRIVSEP_SRCS+= privsep-bsd.c" >>$CONFIG_MK;;
esac
@@ -796,6 +811,28 @@ fi
rm -f _clock_gettime.c _clock_gettime
$abort && exit 1
+if [ -z "$CLOSEFROM" ]; then
+ printf "Testing for closefrom ... "
+ cat <<EOF >_closefrom.c
+#include <unistd.h>
+int main(void) {
+ closefrom(3);
+ return 0;
+}
+EOF
+ if $XCC _closefrom.c -o _closefrom 2>&3; then
+ CLOSEFROM=yes
+ else
+ CLOSEFROM=no
+ fi
+ echo "$CLOSEFROM"
+fi
+rm -f _closefrom.c _closefrom
+if [ "$CLOSEFROM" = no ]; then
+ echo "COMPAT_SRCS+= compat/closefrom.c" >>$CONFIG_MK
+ echo "#include \"compat/closefrom.h\"" >>$CONFIG_H
+fi
+
printf "Testing ioctl request type ... "
cat <<EOF >_ioctl.c
#include <sys/ioctl.h>
@@ -848,8 +885,7 @@ if [ -z "$ARC4RANDOM" ]; then
cat <<EOF >_arc4random.c
#include <stdlib.h>
int main(void) {
- arc4random();
- return 0;
+ return (int)arc4random();
}
EOF
if $XCC _arc4random.c -o _arc4random 2>&3; then
@@ -870,8 +906,7 @@ if [ -z "$ARC4RANDOM_UNIFORM" ]; then
cat <<EOF >_arc4random_uniform.c
#include <stdlib.h>
int main(void) {
- arc4random_uniform(100);
- return 0;
+ return (int)arc4random_uniform(100);
}
EOF
if $XCC _arc4random_uniform.c -o _arc4random_uniform 2>&3; then
@@ -887,6 +922,76 @@ if [ "$ARC4RANDOM_UNIFORM" = no ]; then
echo "#include \"compat/arc4random_uniform.h\"" >>$CONFIG_H
fi
+# Our arc4random compat needs memset_explicit, explicit_bzero or memset_s
+if [ -z "$MEMSET_EXPLICIT" ]; then
+ printf "Testing for memset_explicit ... "
+ cat <<EOF >_memset_explicit.c
+#include <string.h>
+int main(void) {
+ int a;
+ (void)memset_explicit(&a, 0, sizeof(a));
+ return 0;
+}
+EOF
+ if $XCC _memset_explicit.c -o _memset_explicit 2>&3; then
+ MEMSET_EXPLICIT=yes
+ else
+ MEMSET_EXPLICIT=no
+ fi
+ echo "$MEMSET_EXPLICIT"
+ rm -f _memset_explicit.c _memset_explicit
+fi
+if [ "$MEMSET_EXPLICIT" = yes ]; then
+ echo "#define HAVE_MEMSET_EXPLICIT" >>$CONFIG_H
+fi
+
+if [ -z "$EXPLICIT_BZERO" ]; then
+ printf "Testing for explicit_bzero ... "
+ cat <<EOF >_explicit_bzero.c
+#define _BSD_SOURCE // musl, will be added for Linux in config.h
+#include <string.h>
+int main(void) {
+ int a;
+ explicit_bzero(&a, sizeof(a));
+ return 0;
+}
+EOF
+ if $XCC _explicit_bzero.c -o _explicit_bzero 2>&3; then
+ EXPLICIT_BZERO=yes
+ else
+ EXPLICIT_BZERO=no
+ fi
+ echo "$EXPLICIT_BZERO"
+ rm -f _explicit_bzero.c _explicit_bzero
+fi
+if [ "$EXPLICIT_BZERO" = yes ]; then
+ echo "#define HAVE_EXPLICIT_BZERO" >>$CONFIG_H
+fi
+
+if [ -z "$MEMSET_S" ]; then
+ printf "Testing for memset_s ... "
+ cat <<EOF >_memset_s.c
+#define __STDC_WANT_LIB_EXT1__ 1
+#include <string.h>
+int main(void) {
+ int a;
+ memset_s(&a, sizeof(a), 0, sizeof(a));
+ return 0;
+}
+EOF
+ if $XCC _memset_s.c -o _memset_s 2>&3; then
+ MEMSET_S=yes
+ else
+ MEMSET_S=no
+ fi
+ echo "$MEMSET_S"
+ rm -f _memset_s.c _memset_s
+fi
+if [ "$MEMSET_S" = yes ]; then
+ echo "#define __STDC_WANT_LIB_EXT1__ 1" >>$CONFIG_H
+ echo "#define HAVE_MEMSET_S" >>$CONFIG_H
+fi
+
if [ -z "$OPEN_MEMSTREAM" ]; then
printf "Testing for open_memstream ... "
cat <<EOF >_open_memstream.c
@@ -915,8 +1020,7 @@ if [ -z "$PIDFILE_LOCK" ]; then
#include <stdlib.h>
#include <util.h>
int main(void) {
- pidfile_lock(NULL);
- return 0;
+ return (int)pidfile_lock(NULL);
}
EOF
# We only want to link to libutil if it exists in /lib
@@ -985,8 +1089,7 @@ if [ -z "$STRLCPY" ]; then
int main(void) {
const char s1[] = "foo";
char s2[10];
- strlcpy(s2, s1, sizeof(s2));
- return 0;
+ return (int)strlcpy(s2, s1, sizeof(s2));
}
EOF
if $XCC _strlcpy.c -o _strlcpy 2>&3; then
@@ -1010,8 +1113,7 @@ if [ -z "$STRTOI" ]; then
#include <inttypes.h>
int main(void) {
int e;
- strtoi("1234", NULL, 0, 0, INT32_MAX, &e);
- return 0;
+ return (int)strtoi("1234", NULL, 0, 0, INT32_MAX, &e);
}
EOF
if $XCC _strtoi.c -o _strtoi 2>&3; then
@@ -1189,6 +1291,7 @@ EOF
rm -f _rbtree.c _rbtree
fi
if [ "$RBTREE" = no ]; then
+ echo "#define HAVE_NBTOOL_CONFIG_H 0" >>$CONFIG_H
echo "#define RBTEST" >>$CONFIG_H
echo "COMPAT_SRCS+= compat/rb.c" >>$CONFIG_MK
echo "#include \"compat/rbtree.h\"" >>$CONFIG_H
@@ -1221,6 +1324,9 @@ fi
# Set this for eloop
echo "#define HAVE_REALLOCARRAY" >>$CONFIG_H
+# Detect a polling mechanism.
+# See src/eloop.c as to why we only detect ppoll, pollts and pselect and
+# not others like epoll or kqueue.
if [ -z "$POLL" ]; then
printf "Testing for ppoll ... "
cat <<EOF >_ppoll.c
@@ -1263,8 +1369,7 @@ if [ -z "$POLL" ]; then
#include <sys/select.h>
#include <stdlib.h>
int main(void) {
- pselect(0, NULL, NULL, NULL, NULL, NULL);
- return 0;
+ return pselect(0, NULL, NULL, NULL, NULL, NULL);
}
EOF
if $XCC _pselect.c -o _pselect 2>&3; then
@@ -1276,6 +1381,17 @@ EOF
rm -f _pselect.c _pselect
fi
case "$POLL" in
+kqueue1)
+ echo "#define HAVE_KQUEUE" >>$CONFIG_H
+ echo "#define HAVE_KQUEUE1" >>$CONFIG_H
+ POLL=kqueue
+ ;;
+kqueue)
+ echo "#define HAVE_KQUEUE" >>$CONFIG_H
+ ;;
+epoll)
+ echo "#define HAVE_EPOLL" >>$CONFIG_H
+ ;;
ppoll)
echo "#define HAVE_PPOLL" >>$CONFIG_H
;;
@@ -1333,14 +1449,6 @@ if [ "$FLS64" = yes ]; then
echo "#define HAVE_SYS_BITOPS_H" >>$CONFIG_H
fi
-# Workaround for DragonFlyBSD import
-if [ "$OS" = dragonfly ]; then
- echo "#ifdef USE_PRIVATECRYPTO" >>$CONFIG_H
- echo "#define HAVE_MD5_H" >>$CONFIG_H
- echo "#define SHA2_H <openssl/sha.h>" >>$CONFIG_H
- echo "#else" >>$CONFIG_H
-fi
-
if [ -z "$MD5" ]; then
MD5_LIB=
printf "Testing for MD5Init ... "
@@ -1371,14 +1479,6 @@ EOF
echo "$MD5"
rm -f _md5.c _md5
fi
-if [ "$MD5" = no ]; then
- echo "#include \"compat/crypt/md5.h\"" >>$CONFIG_H
- echo "MD5_SRC= compat/crypt/md5.c" >>$CONFIG_MK
-else
- echo "MD5_SRC=" >>$CONFIG_MK
- echo "#define HAVE_MD5_H" >>$CONFIG_H
- [ -n "$MD5_LIB" ] && echo "LDADD+= $MD5_LIB" >>$CONFIG_MK
-fi
if [ -z "$SHA2_H" ]; then
if [ -z "$SHA2" ] || [ "$SHA2" != no ]; then
@@ -1464,23 +1564,6 @@ EOF
rm -f _sha256.c _sha256
fi
fi
-if [ "$SHA2" = no ]; then
- echo "#include \"compat/crypt/sha256.h\"" >>$CONFIG_H
- echo "SHA256_SRC= compat/crypt/sha256.c" >>$CONFIG_MK
-else
- echo "SHA256_SRC=" >>$CONFIG_MK
- echo "#define SHA2_H <$SHA2_H>" >>$CONFIG_H
- if [ "$SHA2_RENAMED" = yes ]; then
- echo "#define SHA256_CTX SHA2_CTX" >>$CONFIG_H
- echo "#define SHA256_Init SHA256Init" >>$CONFIG_H
- echo "#define SHA256_Update SHA256Update" >>$CONFIG_H
- echo "#define SHA256_Final SHA256Final" >>$CONFIG_H
- fi
- [ -n "$SHA2_LIB" ] && echo "LDADD+= $SHA2_LIB" >>$CONFIG_MK
-fi
-
-# Workarond for DragonFlyBSD import
-[ "$OS" = dragonfly ] && echo "#endif" >>$CONFIG_H
if [ -z "$HMAC" ]; then
HMAC_LIB=
@@ -1489,8 +1572,7 @@ if [ -z "$HMAC" ]; then
#include <stdlib.h>
#include <hmac.h>
int main(void) {
- hmac(NULL, NULL, 0, NULL, 0, NULL, 0);
- return 0;
+ return (int)hmac(NULL, NULL, 0, NULL, 0, NULL, 0);
}
EOF
if $XCC _hmac.c $MD5_LIB -o _hmac 2>&3; then
@@ -1502,8 +1584,7 @@ EOF
cat <<EOF >_hmac.c
#include <stdlib.h>
int main(void) {
- hmac(NULL, NULL, 0, NULL, 0, NULL, 0);
- return 0;
+ return (int)hmac(NULL, NULL, 0, NULL, 0, NULL, 0);
}
EOF
if $XCC _hmac.c $MD5_LIB -o _hmac 2>&3; then
@@ -1515,7 +1596,108 @@ EOF
echo "$HMAC"
rm -f _hmac.c _hmac
fi
-if [ "$HMAC" = no ]; then
+
+if [ "$OPENSSL" = yes ] ||
+ { [ -z "$OPENSSL" ] && [ "$ALLOW_USR_LIBS" = true ] &&
+ [ "$SHA2" = no ] && [ "$HMAC" = no ];
+ }; then
+ printf "Testing for openssl ... "
+ if type "$PKG_CONFIG" >/dev/null 2>&1; then
+ LIBCRYPTO_CFLAGS=$("$PKG_CONFIG" --cflags libcrypto 2>&3)
+ LIBCRYPTO_LIBS=$("$PKG_CONFIG" --libs libcrypto 2>&3)
+ fi
+
+ cat <<EOF >_openssl.c
+#include <stdio.h>
+#include <openssl/crypto.h>
+int main(void) {
+ return OPENSSL_init_crypto(0, NULL) == 1;
+}
+EOF
+ if $XCC $LIBCRYPTO_CFLAGS _openssl.c -o _openssl $LIBCRYPTO_LIBS 2>&3;
+ then
+ OPENSSL=yes
+ MD5=yes
+ MD5_LIB=
+ if [ -n "$LIBCRYPTO_CFLAGS" ]; then
+ echo "CFLAGS+= $LIBCRYPTO_CFLAGS" >>$CONFIG_MK
+ fi
+ echo "LDADD+= $LIBCRYPTO_LIBS" >>$CONFIG_MK
+ echo "#define HAVE_OPENSSL" >>$CONFIG_H
+ else
+ OPENSSL=no
+ fi
+ echo "$OPENSSL"
+ rm -f _openssl.c _openssl
+fi
+
+if [ "$OPENSSL" = yes ]; then
+ printf "Testing for openssl/sha.h ... "
+ cat <<EOF >_openssl_sha.c
+#include <stdio.h>
+#include <openssl/sha.h>
+
+int main(void) {
+ SHA256_CTX ctx;
+ SHA256_Init(&ctx);
+ return 0;
+}
+EOF
+ if $XCC $LIBCRYPTO_CFLAGS _openssl_sha.c -o _openssl_sha \
+ $LIBCRYPTO_LIBS 2>&3; then
+ SHA2_H=openssl/sha.h
+ SHA2="yes (-lcrypto)"
+ else
+ SHA2=no
+ fi
+ SHA2_LIB=
+ SHA2_RENAMED=
+ echo "$SHA2"
+ rm -f _openssl_sha.c _openssl_sha
+fi
+
+# Workaround for DragonFlyBSD import
+if [ "$OS" = dragonfly ]; then
+ echo "#ifdef USE_PRIVATECRYPTO" >>$CONFIG_H
+ echo "#define HAVE_MD5_H" >>$CONFIG_H
+ echo "#define SHA2_H <openssl/sha.h>" >>$CONFIG_H
+ echo "#else" >>$CONFIG_H
+fi
+
+if [ "$MD5" = no ]; then
+ echo "#include \"compat/crypt/md5.h\"" >>$CONFIG_H
+ echo "MD5_SRC= compat/crypt/md5.c" >>$CONFIG_MK
+else
+ echo "MD5_SRC=" >>$CONFIG_MK
+ [ "$OPENSSL" != yes ] && echo "#define HAVE_MD5_H" >>$CONFIG_H
+ [ -n "$MD5_LIB" ] && echo "LDADD+= $MD5_LIB" >>$CONFIG_MK
+fi
+
+if [ "$OPENSSL" = yes ] && [ "$SHA2" = no ]; then
+ echo "#include \"compat/crypt_openssl/sha256.h\"" >>$CONFIG_H
+ echo "SHA256_SRC= compat/crypt_openssl/sha256.c" >>$CONFIG_MK
+elif [ "$SHA2" = no ]; then
+ echo "#include \"compat/crypt/sha256.h\"" >>$CONFIG_H
+ echo "SHA256_SRC= compat/crypt/sha256.c" >>$CONFIG_MK
+else
+ echo "SHA256_SRC=" >>$CONFIG_MK
+ echo "#define SHA2_H <$SHA2_H>" >>$CONFIG_H
+ if [ "$SHA2_RENAMED" = yes ]; then
+ echo "#define SHA256_CTX SHA2_CTX" >>$CONFIG_H
+ echo "#define SHA256_Init SHA256Init" >>$CONFIG_H
+ echo "#define SHA256_Update SHA256Update" >>$CONFIG_H
+ echo "#define SHA256_Final SHA256Final" >>$CONFIG_H
+ fi
+ [ -n "$SHA2_LIB" ] && echo "LDADD+= $SHA2_LIB" >>$CONFIG_MK
+fi
+
+# Workarond for DragonFlyBSD import
+[ "$OS" = dragonfly ] && echo "#endif" >>$CONFIG_H
+
+if [ "$OPENSSL" = yes ]; then
+ echo "#include \"compat/crypt_openssl/hmac.h\"" >>$CONFIG_H
+ echo "HMAC_SRC= compat/crypt_openssl/hmac.c" >>$CONFIG_MK
+elif [ "$HMAC" = no ]; then
echo "#include \"compat/crypt/hmac.h\"" >>$CONFIG_H
echo "HMAC_SRC= compat/crypt/hmac.c" >>$CONFIG_MK
else
@@ -1575,8 +1757,7 @@ if [ "$DEV" != no ] && [ "$UDEV" != no ] && [ -n "$LIBUDEV_LIBS" ]; then
#include <libudev.h>
#include <stdlib.h>
int main(void) {
- udev_monitor_filter_add_match_subsystem_devtype(NULL, NULL, NULL);
- return 0;
+ return udev_monitor_filter_add_match_subsystem_devtype(NULL, NULL, NULL);
}
EOF
if $XCC $LIBUDEV_CFLAGS _udev.c -o _udev $LIBUDEV_LIBS 2>&3
@@ -1593,8 +1774,7 @@ EOF
#include <libudev.h>
#include <stdlib.h>
int main(void) {
- udev_device_get_is_initialized(NULL);
- return 0;
+ return udev_device_get_is_initialized(NULL);
}
EOF
if $XCC $LIBUDEV_CFLAGS _udev.c -o _udev $LIBUDEV_LIBS 2>&3
@@ -1693,6 +1873,21 @@ if ! $HOOKSET; then
echo "NTP will default to $NTPD"
fi
+ printf "Checking for timesyncd ... "
+ TIMESYNCD=
+ for x in /usr/lib/systemd/systemd-timesyncd; do
+ if [ -x "$x" ]; then
+ TIMESYNCD=$x
+ break
+ fi
+ done
+ if [ -n "$TIMESYNCD" ]; then
+ echo "$TIMESYNCD"
+ HOOKS="$HOOKS${HOOKS:+ }50-timesyncd.conf"
+ else
+ echo "not found"
+ fi
+
printf "Checking for ypbind ... "
YPBIND=$(_which ypbind)
if [ -n "$YPBIND" ]; then
@@ -1749,6 +1944,9 @@ if ! $HOOKSET; then
fi
fi
+echo >>$CONFIG_H
+echo "#endif /*CONFIG_H*/">>$CONFIG_H
+
find_hook()
{
for h in [0-9][0-9]"-$x" [0-9][0-9]"-$x.in" \
diff --git a/hooks/10-wpa_supplicant b/hooks/10-wpa_supplicant
index 04acce088840..e27ea170c532 100644
--- a/hooks/10-wpa_supplicant
+++ b/hooks/10-wpa_supplicant
@@ -102,12 +102,12 @@ wpa_supplicant_stop()
}
if [ "$ifwireless" = "1" ] && \
- type wpa_supplicant >/dev/null 2>&1 && \
- type wpa_cli >/dev/null 2>&1
+ command -v wpa_supplicant >/dev/null 2>&1 && \
+ command -v wpa_cli >/dev/null 2>&1
then
case "$reason" in
- PREINIT) wpa_supplicant_start;;
- RECONFIGURE) wpa_supplicant_reconfigure;;
- DEPARTED) wpa_supplicant_stop;;
+ PREINIT) wpa_supplicant_start;;
+ RECONFIGURE) wpa_supplicant_reconfigure;;
+ DEPARTED|STOPPED) wpa_supplicant_stop;;
esac
fi
diff --git a/hooks/20-resolv.conf b/hooks/20-resolv.conf
index 504a6c5373a4..7c29e2765b36 100644
--- a/hooks/20-resolv.conf
+++ b/hooks/20-resolv.conf
@@ -11,7 +11,7 @@ nocarrier_roaming_dir="$state_dir/roaming"
NL="
"
: ${resolvconf:=resolvconf}
-if type "$resolvconf" >/dev/null 2>&1; then
+if command -v "$resolvconf" >/dev/null 2>&1; then
have_resolvconf=true
else
have_resolvconf=false
diff --git a/hooks/29-lookup-hostname b/hooks/29-lookup-hostname
index 811c7a9c89bf..a1540dd98340 100644
--- a/hooks/29-lookup-hostname
+++ b/hooks/29-lookup-hostname
@@ -4,20 +4,20 @@ lookup_hostname()
{
[ -z "$new_ip_address" ] && return 1
# Silly ISC programs love to send error text to stdout
- if type dig >/dev/null 2>&1; then
+ if command -v dig >/dev/null 2>&1; then
h=$(dig +short -x $new_ip_address)
if [ $? = 0 ]; then
echo "$h" | sed 's/\.$//'
return 0
fi
- elif type host >/dev/null 2>&1; then
+ elif command -v host >/dev/null 2>&1; then
h=$(host $new_ip_address)
if [ $? = 0 ]; then
echo "$h" \
| sed 's/.* domain name pointer \(.*\)./\1/'
return 0
fi
- elif type getent >/dev/null 2>&1; then
+ elif command -v getent >/dev/null 2>&1; then
h=$(getent hosts $new_ip_address)
if [ $? = 0 ]; then
echo "$h" | sed 's/[^ ]* *\([^ ]*\).*/\1/'
diff --git a/hooks/30-hostname.in b/hooks/30-hostname.in
index abeb36967221..98e419f8a523 100644
--- a/hooks/30-hostname.in
+++ b/hooks/30-hostname.in
@@ -25,7 +25,7 @@ _hostname()
if [ -z "${1+x}" ]; then
if [ -r /proc/sys/kernel/hostname ]; then
read name </proc/sys/kernel/hostname && echo "$name"
- elif type hostname >/dev/null 2>/dev/null; then
+ elif command -v hostname >/dev/null 2>/dev/null; then
hostname
elif sysctl kern.hostname >/dev/null 2>&1; then
sysctl -n kern.hostname
@@ -39,7 +39,7 @@ _hostname()
if [ -w /proc/sys/kernel/hostname ]; then
echo "$1" >/proc/sys/kernel/hostname
- elif [ -n "$1" ] && type hostname >/dev/null 2>&1; then
+ elif [ -n "$1" ] && command -v hostname >/dev/null 2>&1; then
hostname "$1"
elif sysctl kern.hostname >/dev/null 2>&1; then
sysctl -w "kern.hostname=$1" >/dev/null
@@ -118,7 +118,7 @@ set_hostname()
*) hshort=true;;
esac
- need_hostname || return
+ need_hostname || return 0
if [ -n "$new_fqdn" ]; then
if ${hfqdn} || ! ${hshort}; then
diff --git a/hooks/50-ntp.conf b/hooks/50-ntp.conf
index 046ab6b33a06..cbaa374408bb 100644
--- a/hooks/50-ntp.conf
+++ b/hooks/50-ntp.conf
@@ -43,7 +43,7 @@ fi
# Debian has a separate file for DHCP config to avoid stamping on
# the master.
-if [ "$ntp_service" = ntpd ] && type invoke-rc.d >/dev/null 2>&1; then
+if [ "$ntp_service" = ntpd ] && command -v invoke-rc.d >/dev/null 2>&1; then
[ -e /var/lib/ntp ] || mkdir /var/lib/ntp
: ${ntp_service:=ntp}
: ${NTP_DHCP_CONF:=/var/lib/ntp/ntp.conf.dhcp}
@@ -113,7 +113,7 @@ add_ntp_conf()
[ -e "$cf" ] && rm "$cf"
[ -d "$ntp_conf_dir" ] || mkdir -p "$ntp_conf_dir"
if [ -n "$new_ntp_servers" ]; then
- for x in $new_ntp_servers; do
+ for x in $(uniqify $new_ntp_servers); do
echo "server $x" >> "$cf"
done
fi
@@ -131,7 +131,7 @@ remove_ntp_conf()
# For ease of use, map DHCP6 names onto our DHCP4 names
case "$reason" in
BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)
- new_ntp_servers="$new_dhcp6_sntp_servers"
+ new_ntp_servers="$new_dhcp6_sntp_servers $new_dhcp6_ntp_server_addr $new_dhcp6_ntp_server_fqdn"
;;
esac
diff --git a/hooks/50-timesyncd.conf b/hooks/50-timesyncd.conf
new file mode 100644
index 000000000000..dff8817324e3
--- /dev/null
+++ b/hooks/50-timesyncd.conf
@@ -0,0 +1,60 @@
+: ${timesyncd_conf_d:=/run/systemd/timesyncd.conf.d}
+timesyncd_conf="${timesyncd_conf_d}/dhcpcd-$ifname.conf"
+timesyncd_tmp_d="$state_dir/timesyncd"
+timesyncd_tmp="$timesyncd_tmp_d/$ifname"
+
+NL="
+"
+
+remove_timesyncd_conf()
+{
+ if [ -e "$timesyncd_conf" ]; then
+ rm "$timesyncd_conf"
+ systemctl try-reload-or-restart --no-block systemd-timesyncd
+ fi
+}
+
+add_timesyncd_conf()
+{
+ if [ -z "$new_ntp_servers" ]; then
+ remove_timesyncd_conf
+ return $?
+ fi
+
+ mkdir -p "$timesyncd_tmp_d" "$timesyncd_conf_d"
+
+ conf="$signature$NL"
+ conf="${conf}[Time]$NL"
+ conf="${conf}NTP="
+ # Trim spaces
+ space=false
+ for ntp_server in $(uniqify $new_ntp_servers); do
+ if ! $space; then
+ space=true
+ else
+ conf="$conf "
+ fi
+ conf="$conf$ntp_server"
+ done
+ conf="$conf$NL"
+
+ printf %s "$conf" > "$timesyncd_tmp"
+ if change_file "$timesyncd_conf" "$timesyncd_tmp"; then
+ systemctl try-reload-or-restart --no-block systemd-timesyncd
+ fi
+}
+
+# For ease of use, map DHCP6 names onto our DHCP4 names
+case "$reason" in
+BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)
+ new_ntp_servers="$new_dhcp6_sntp_servers $new_dhcp6_ntp_server_addr $new_dhcp6_ntp_server_fqdn"
+;;
+esac
+
+if $if_configured; then
+ if $if_up; then
+ add_timesyncd_conf
+ elif $if_down; then
+ remove_timesyncd_conf
+ fi
+fi
diff --git a/hooks/Makefile b/hooks/Makefile
index 7a0c3b1d330a..aee0255c64f9 100644
--- a/hooks/Makefile
+++ b/hooks/Makefile
@@ -23,7 +23,7 @@ FILES+= ${EGHOOKSCRIPTS}
.SUFFIXES: .in
-.in: Makefile ${TOP}/config.mk
+.in:
${SED} ${SED_RUNDIR} ${SED_DBDIR} ${SED_LIBDIR} ${SED_HOOKDIR} \
${SED_SYS} ${SED_SCRIPT} ${SED_DATADIR} \
${SED_SERVICEEXISTS} ${SED_SERVICECMD} ${SED_SERVICESTATUS} \
diff --git a/hooks/dhcpcd-run-hooks.8.in b/hooks/dhcpcd-run-hooks.8.in
index db88d8e2ab17..ac1972a06ad6 100644
--- a/hooks/dhcpcd-run-hooks.8.in
+++ b/hooks/dhcpcd-run-hooks.8.in
@@ -1,4 +1,4 @@
-.\" Copyright (c) 2006-2021 Roy Marples
+.\" Copyright (c) 2006-2023 Roy Marples
.\" All rights reserved
.\"
.\" Redistribution and use in source and binary forms, with or without
@@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd December 27, 2020
+.Dd August 31, 2022
.Dt DHCPCD-RUN-HOOKS 8
.Os
.Sh NAME
@@ -68,15 +68,14 @@ is set to the interface that
.Nm dhcpcd
is run on and
.Ev $reason
-is to the reason why
-q
+is set to the reason why
.Nm
was invoked.
DHCP information to be configured is held in variables starting with the word
new_ and old DHCP information to be removed is held in variables starting with
the word old_.
.Nm dhcpcd
-can display the full list of variables it knows how about by using the
+can display the full list of variables it knows about by using the
.Fl V , -variables
argument.
.Pp
@@ -85,7 +84,7 @@ Here's a list of reasons why
could be invoked:
.Bl -tag -width EXPIREXXXEXPIRE6
.It Dv PREINIT
-dhcpcd is starting up and any pre-initialisation should be done.
+dhcpcd is starting up and any pre-initialisation required should be performed now.
.It Dv CARRIER
dhcpcd has detected the carrier is up.
This is generally just a notification and no action need be taken.
@@ -94,14 +93,14 @@ dhcpcd lost the carrier.
The cable may have been unplugged or association to the wireless point lost.
.It Dv NOCARRIER_ROAMING
dhcpcd lost the carrier but the interface configuration is persisted.
-The OS has to support wireless roaming or IP Persistance for this to happen.
+The OS has to support wireless roaming or IP Persistence for this to happen.
.It Dv INFORM | Dv INFORM6
dhcpcd informed a DHCP server about its address and obtained other
configuration details.
.It Dv BOUND | Dv BOUND6
dhcpcd obtained a new lease from a DHCP server.
.It Dv RENEW | Dv RENEW6
-dhcpcd renewed it's lease.
+dhcpcd renewed its lease.
.It Dv REBIND | Dv REBIND6
dhcpcd has rebound to a new DHCP server.
.It Dv REBOOT | Dv REBOOT6
@@ -181,7 +180,7 @@ flags.
.Ev $interface
MTU.
.It Ev $ifssid
-the name of the SSID the
+the SSID the
.Ev interface
is connected to.
.It Ev $interface_order
@@ -207,24 +206,24 @@ Address family waiting for, as defined in
the name of the profile selected from
.Xr dhcpcd.conf 5 .
.It Ev $new_delegated_dhcp6_prefix
-space separated list of delegated prefixes.
+space-separated list of delegated prefixes.
.El
.Sh FILES
When
.Nm
runs, it loads
-.Pa @SYSCONFDIR@/dhcpcd.enter-hook
-and any scripts found in
+.Pa @SYSCONFDIR@/dhcpcd.enter-hook ,
+any scripts found in
.Pa @HOOKDIR@
-in a lexical order and then finally
-.Pa @SYSCONFDIR@/dhcpcd.exit-hook
+in lexical order, then finally
+.Pa @SYSCONFDIR@/dhcpcd.exit-hook .
.Sh SEE ALSO
.Xr dhcpcd 8
.Sh AUTHORS
.An Roy Marples Aq Mt roy@marples.name
.Sh BUGS
Please report them to
-.Lk http://roy.marples.name/projects/dhcpcd
+.Lk https://roy.marples.name/projects/dhcpcd
.Sh SECURITY CONSIDERATIONS
.Nm dhcpcd
will validate the content of each option against its encoding.
diff --git a/hooks/dhcpcd-run-hooks.in b/hooks/dhcpcd-run-hooks.in
index a237f6af5340..91df64b1a809 100644
--- a/hooks/dhcpcd-run-hooks.in
+++ b/hooks/dhcpcd-run-hooks.in
@@ -67,7 +67,7 @@ key_get_value()
key="$1"
shift
- if type sed >/dev/null 2>&1; then
+ if command -v sed >/dev/null 2>&1; then
sed -n "s/^$key//p" $@
else
for x do
@@ -89,7 +89,7 @@ remove_markers()
in_marker=0
shift; shift
- if type sed >/dev/null 2>&1; then
+ if command -v sed >/dev/null 2>&1; then
sed "/^$m1/,/^$m2/d" $@
else
for x do
@@ -109,9 +109,9 @@ comp_file()
{
[ -e "$1" ] && [ -e "$2" ] || return 1
- if type cmp >/dev/null 2>&1; then
+ if command -v cmp >/dev/null 2>&1; then
cmp -s "$1" "$2"
- elif type diff >/dev/null 2>&1; then
+ elif command -v diff >/dev/null 2>&1; then
diff -q "$1" "$2" >/dev/null
else
# Hopefully we're only working on small text files ...
@@ -178,7 +178,7 @@ syslog()
err|error) echo "$interface: $*" >&2;;
*) echo "$interface: $*";;
esac
- if type logger >/dev/null 2>&1; then
+ if command -v logger >/dev/null 2>&1; then
logger -i -p daemon."$lvl" -t dhcpcd-run-hooks "$interface: $*"
fi
}
@@ -234,11 +234,11 @@ detect_init()
if [ -x /bin/systemctl ] && [ -S /run/systemd/private ]; then
_service_exists="/bin/systemctl --quiet is-enabled \$1.service"
_service_status="/bin/systemctl --quiet is-active \$1.service"
- _service_cmd="/bin/systemctl \$2 \$1.service"
+ _service_cmd="/bin/systemctl \$2 --no-block \$1.service"
elif [ -x /usr/bin/systemctl ] && [ -S /run/systemd/private ]; then
_service_exists="/usr/bin/systemctl --quiet is-enabled \$1.service"
_service_status="/usr/bin/systemctl --quiet is-active \$1.service"
- _service_cmd="/usr/bin/systemctl \$2 \$1.service"
+ _service_cmd="/usr/bin/systemctl \$2 --no-block \$1.service"
elif [ -x /sbin/rc-service ] &&
{ [ -s /libexec/rc/init.d/softlevel ] ||
[ -s /run/openrc/softlevel ]; }
@@ -338,9 +338,11 @@ for hook in \
@HOOKDIR@/* \
@SYSCONFDIR@/dhcpcd.exit-hook
do
+ case "$hook" in
+ */*~) continue;;
+ esac
for skip in $skip_hooks; do
case "$hook" in
- */*~) continue 2;;
*/"$skip") continue 2;;
*/[0-9][0-9]"-$skip") continue 2;;
*/[0-9][0-9]"-$skip.sh") continue 2;;
diff --git a/src/Makefile b/src/Makefile
index 4849a77abd99..ff998b386fcf 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -38,7 +38,7 @@ CLEANFILES+= *.tar.xz
.SUFFIXES: .in
-.in: Makefile ${TOP}/config.mk
+.in:
${SED} ${SED_RUNDIR} ${SED_DBDIR} ${SED_LIBDIR} ${SED_HOOKDIR} \
${SED_SYS} ${SED_SCRIPT} ${SED_DATADIR} \
${SED_SERVICEEXISTS} ${SED_SERVICECMD} ${SED_SERVICESTATUS} \
@@ -51,7 +51,7 @@ all: ${TOP}/config.h ${PROG} ${SCRIPTS} ${MAN5} ${MAN8}
dev:
cd dev && ${MAKE}
-.c.o: Makefile ${TOP}/config.mk
+.c.o:
${CC} ${CFLAGS} ${CPPFLAGS} -c $< -o $@
CLEANFILES+= dhcpcd-embedded.h dhcpcd-embedded.c
@@ -64,8 +64,8 @@ dhcpcd-embedded.c: genembedc ${DHCPCD_DEFS} dhcpcd-embedded.c.in
if-options.c: dhcpcd-embedded.h
-.depend: ${SRCS} ${COMPAT_SRCS} ${CRYPT_SRCS}
- ${CC} ${CPPFLAGS} -MM ${SRCS} ${COMPAT_SRCS} ${CRYPT_SRCS} > .depend
+.depend: ${SRCS} ${PCRYPT_SRCS} ${PCOMPAT_SRC}
+ ${CC} ${CPPFLAGS} -MM ${SRCS} ${PCRYPT_SRCS} ${PCOMPAT_SRCS} > .depend
depend: .depend
diff --git a/src/arp.c b/src/arp.c
index e8a27f42257b..4b894e273924 100644
--- a/src/arp.c
+++ b/src/arp.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - ARP handler
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -173,12 +173,24 @@ arp_found(struct arp_state *astate, const struct arp_msg *amsg)
* the other IPv4LL client will receieve two ARP
* messages.
* If another conflict happens within DEFEND_INTERVAL
- * then we must drop our address and negotiate a new one. */
+ * then we must drop our address and negotiate a new one.
+ * If DHCPCD_ARP_PERSISTDEFENCE is set, that enables
+ * RFC5227 section 2.4.c behaviour. Upon conflict
+ * detection, the host records the time that the
+ * conflicting ARP packet was received, and then
+ * broadcasts one single ARP Announcement. The host then
+ * continues to use the address normally. All further
+ * conflict notifications within the DEFEND_INTERVAL are
+ * ignored. */
clock_gettime(CLOCK_MONOTONIC, &now);
if (timespecisset(&astate->defend) &&
eloop_timespec_diff(&now, &astate->defend, NULL) < DEFEND_INTERVAL)
+ {
logwarnx("%s: %d second defence failed for %s",
ifp->name, DEFEND_INTERVAL, inet_ntoa(astate->addr));
+ if (ifp->options->options & DHCPCD_ARP_PERSISTDEFENCE)
+ return;
+ }
else if (arp_request(astate, &astate->addr) == -1)
logerr(__func__);
else {
@@ -232,6 +244,9 @@ arp_packet(struct interface *ifp, uint8_t *data, size_t len,
const struct iarp_state *state;
struct arp_state *astate, *astaten;
uint8_t *hw_s, *hw_t;
+#ifndef KERNEL_RFC5227
+ bool is_probe;
+#endif /* KERNEL_RFC5227 */
/* Copy the frame header source and destination out */
memset(&arm, 0, sizeof(arm));
@@ -284,6 +299,23 @@ arp_packet(struct interface *ifp, uint8_t *data, size_t len,
memcpy(&arm.tha, hw_t, ar.ar_hln);
memcpy(&arm.tip.s_addr, hw_t + ar.ar_hln, ar.ar_pln);
+#ifndef KERNEL_RFC5227
+ /* During ARP probe the 'sender hardware address' MUST contain the hardware
+ * address of the interface sending the packet. RFC5227, 1.1 */
+ is_probe = ar.ar_op == htons(ARPOP_REQUEST) && IN_IS_ADDR_UNSPECIFIED(&arm.sip) &&
+ bpf_flags & BPF_BCAST;
+ if (is_probe && falen > 0 && (falen != ar.ar_hln ||
+ memcmp(&arm.sha, &arm.fsha, ar.ar_hln))) {
+ char abuf[HWADDR_LEN * 3];
+ char fbuf[HWADDR_LEN * 3];
+ hwaddr_ntoa(&arm.sha, ar.ar_hln, abuf, sizeof(abuf));
+ hwaddr_ntoa(&arm.fsha, falen, fbuf, sizeof(fbuf));
+ logwarnx("%s: invalid ARP probe, sender hw address mismatch (%s, %s)",
+ ifp->name, abuf, fbuf);
+ return;
+ }
+#endif /* KERNEL_RFC5227 */
+
/* Match the ARP probe to our states.
* Ignore Unicast Poll, RFC1122. */
state = ARP_CSTATE(ifp);
@@ -299,7 +331,7 @@ arp_packet(struct interface *ifp, uint8_t *data, size_t len,
}
static void
-arp_read(void *arg)
+arp_read(void *arg, unsigned short events)
{
struct arp_state *astate = arg;
struct bpf *bpf = astate->bpf;
@@ -308,6 +340,9 @@ arp_read(void *arg)
ssize_t bytes;
struct in_addr addr = astate->addr;
+ if (events != ELE_READ)
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
+
/* Some RAW mechanisms are generic file descriptors, not sockets.
* This means we have no kernel call to just get one packet,
* so we have to process the entire buffer. */
@@ -532,7 +567,7 @@ arp_new(struct interface *ifp, const struct in_addr *addr)
struct arp_state *astate;
if ((state = ARP_STATE(ifp)) == NULL) {
- ifp->if_data[IF_DATA_ARP] = malloc(sizeof(*state));
+ ifp->if_data[IF_DATA_ARP] = malloc(sizeof(*state));
state = ARP_STATE(ifp);
if (state == NULL) {
logerr(__func__);
@@ -567,8 +602,9 @@ arp_new(struct interface *ifp, const struct in_addr *addr)
free(astate);
return NULL;
}
- eloop_event_add(ifp->ctx->eloop, astate->bpf->bpf_fd,
- arp_read, astate);
+ if (eloop_event_add(ifp->ctx->eloop, astate->bpf->bpf_fd, ELE_READ,
+ arp_read, astate) == -1)
+ logerr("%s: eloop_event_add", __func__);
}
diff --git a/src/arp.h b/src/arp.h
index 0ac8ef711622..3daf85f689d8 100644
--- a/src/arp.h
+++ b/src/arp.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
diff --git a/src/auth.c b/src/auth.c
index bfb2b5dda57a..9d2caecb4bc0 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
diff --git a/src/auth.h b/src/auth.h
index c6eb428512f6..c1cd30387482 100644
--- a/src/auth.h
+++ b/src/auth.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
diff --git a/src/bpf.c b/src/bpf.c
index 21b73af207be..caf9fda6c328 100644
--- a/src/bpf.c
+++ b/src/bpf.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd: BPF arp and bootp filtering
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -45,7 +45,6 @@
#include <errno.h>
#include <fcntl.h>
-#include <paths.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
@@ -155,6 +154,11 @@ bpf_open(const struct interface *ifp,
struct bpf_version pv = { .bv_major = 0, .bv_minor = 0 };
struct ifreq ifr = { .ifr_flags = 0 };
int ibuf_len = 0;
+#ifdef O_CLOEXEC
+#define BPF_OPEN_FLAGS O_RDWR | O_NONBLOCK | O_CLOEXEC
+#else
+#define BPF_OPEN_FLAGS O_RDWR | O_NONBLOCK
+#endif
#ifdef BIOCIMMEDIATE
unsigned int flags;
#endif
@@ -167,25 +171,19 @@ bpf_open(const struct interface *ifp,
return NULL;
bpf->bpf_ifp = ifp;
-#ifdef _PATH_BPF
- bpf->bpf_fd = open(_PATH_BPF, O_RDWR | O_NONBLOCK
-#ifdef O_CLOEXEC
- | O_CLOEXEC
-#endif
- );
-#else
- char device[32];
- int n = 0;
+ /* /dev/bpf is a cloner on modern kernels */
+ bpf->bpf_fd = open("/dev/bpf", BPF_OPEN_FLAGS);
- do {
- snprintf(device, sizeof(device), "/dev/bpf%d", n++);
- bpf->bpf_fd = open(device, O_RDWR | O_NONBLOCK
-#ifdef O_CLOEXEC
- | O_CLOEXEC
-#endif
- );
- } while (bpf->bpf_fd == -1 && errno == EBUSY);
-#endif
+ /* Support older kernels where /dev/bpf is not a cloner */
+ if (bpf->bpf_fd == -1) {
+ char device[32];
+ int n = 0;
+
+ do {
+ snprintf(device, sizeof(device), "/dev/bpf%d", n++);
+ bpf->bpf_fd = open(device, BPF_OPEN_FLAGS);
+ } while (bpf->bpf_fd == -1 && errno == EBUSY);
+ }
if (bpf->bpf_fd == -1)
goto eexit;
@@ -612,16 +610,19 @@ static const struct bpf_insn bpf_bootp_base[] = {
#define BPF_BOOTP_BASE_LEN __arraycount(bpf_bootp_base)
static const struct bpf_insn bpf_bootp_read[] = {
- /* Make sure it's from and to the right port. */
- BPF_STMT(BPF_LD + BPF_W + BPF_IND, 0),
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (BOOTPS << 16) + BOOTPC, 1, 0),
+ /* Make sure it's to the right port.
+ * RFC2131 makes no mention of enforcing a source port. */
+ BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct udphdr, uh_dport)),
+ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTPC, 1, 0),
BPF_STMT(BPF_RET + BPF_K, 0),
};
#define BPF_BOOTP_READ_LEN __arraycount(bpf_bootp_read)
#ifdef BIOCSETWF
static const struct bpf_insn bpf_bootp_write[] = {
- /* Make sure it's from and to the right port. */
+ /* Make sure it's from and to the right port.
+ * RFC2131 makes no mention of encforcing a source port,
+ * but dhcpcd does enforce it for sending. */
BPF_STMT(BPF_LD + BPF_W + BPF_IND, 0),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (BOOTPC << 16) + BOOTPS, 1, 0),
BPF_STMT(BPF_RET + BPF_K, 0),
diff --git a/src/bpf.h b/src/bpf.h
index 40da95e61ee6..03a92d861e83 100644
--- a/src/bpf.h
+++ b/src/bpf.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd: BPF arp and bootp filtering
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
diff --git a/src/common.c b/src/common.c
index bb89100ef2cf..34746d249625 100644
--- a/src/common.c
+++ b/src/common.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -46,10 +46,15 @@ hwaddr_ntoa(const void *hwaddr, size_t hwlen, char *buf, size_t buflen)
const unsigned char *hp, *ep;
char *p;
- if (buf == NULL || hwlen == 0)
+ /* Allow a hwlen of 0 to be an empty string. */
+ if (buf == NULL || buflen == 0) {
+ errno = ENOBUFS;
return NULL;
+ }
if (hwlen * 3 > buflen) {
+ /* We should still terminate the string just in case. */
+ buf[0] = '\0';
errno = ENOBUFS;
return NULL;
}
@@ -57,7 +62,6 @@ hwaddr_ntoa(const void *hwaddr, size_t hwlen, char *buf, size_t buflen)
hp = hwaddr;
ep = hp + hwlen;
p = buf;
-
while (hp < ep) {
if (hp != hwaddr)
*p ++= ':';
diff --git a/src/common.h b/src/common.h
index ff8f3f8b943b..6682376f0c2a 100644
--- a/src/common.h
+++ b/src/common.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
diff --git a/src/control.c b/src/control.c
index 4d768d887b9f..17fd13aa8a9e 100644
--- a/src/control.c
+++ b/src/control.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -53,6 +53,8 @@
(sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
#endif
+static void control_handle_data(void *, unsigned short);
+
static void
control_queue_free(struct fd_list *fd)
{
@@ -84,41 +86,38 @@ control_free(struct fd_list *fd)
fd->ctx->ps_control_client = NULL;
#endif
- if (eloop_event_remove_writecb(fd->ctx->eloop, fd->fd) == -1)
- logerr(__func__);
+ eloop_event_delete(fd->ctx->eloop, fd->fd);
+ close(fd->fd);
TAILQ_REMOVE(&fd->ctx->control_fds, fd, next);
control_queue_free(fd);
free(fd);
}
-void
-control_delete(struct fd_list *fd)
+static void
+control_hangup(struct fd_list *fd)
{
#ifdef PRIVSEP
- if (IN_PRIVSEP_SE(fd->ctx))
- return;
+ if (IN_PRIVSEP(fd->ctx)) {
+ if (ps_ctl_sendeof(fd) == -1)
+ logerr(__func__);
+ }
#endif
-
- eloop_event_delete(fd->ctx->eloop, fd->fd);
- close(fd->fd);
control_free(fd);
}
-static void
-control_handle_data(void *arg)
+static int
+control_handle_read(struct fd_list *fd)
{
- struct fd_list *fd = arg;
char buffer[1024];
ssize_t bytes;
bytes = read(fd->fd, buffer, sizeof(buffer) - 1);
-
+ if (bytes == -1)
+ logerr(__func__);
if (bytes == -1 || bytes == 0) {
- /* Control was closed or there was an error.
- * Remove it from our list. */
- control_delete(fd);
- return;
+ control_hangup(fd);
+ return -1;
}
#ifdef PRIVSEP
@@ -130,18 +129,96 @@ control_handle_data(void *arg)
fd->flags &= ~FD_SENDLEN;
if (err == -1) {
logerr(__func__);
- return;
+ return 0;
}
if (err == 1 &&
ps_ctl_sendargs(fd, buffer, (size_t)bytes) == -1) {
logerr(__func__);
- control_delete(fd);
+ control_free(fd);
+ return -1;
}
- return;
+ return 0;
}
#endif
control_recvdata(fd, buffer, (size_t)bytes);
+ return 0;
+}
+
+static int
+control_handle_write(struct fd_list *fd)
+{
+ struct iovec iov[2];
+ int iov_len;
+ struct fd_data *data;
+
+ data = TAILQ_FIRST(&fd->queue);
+
+ if (data->data_flags & FD_SENDLEN) {
+ iov[0].iov_base = &data->data_len;
+ iov[0].iov_len = sizeof(size_t);
+ iov[1].iov_base = data->data;
+ iov[1].iov_len = data->data_len;
+ iov_len = 2;
+ } else {
+ iov[0].iov_base = data->data;
+ iov[0].iov_len = data->data_len;
+ iov_len = 1;
+ }
+
+ if (writev(fd->fd, iov, iov_len) == -1) {
+ if (errno != EPIPE && errno != ENOTCONN) {
+ // We don't get ELE_HANGUP for some reason
+ logerr("%s: write", __func__);
+ }
+ control_hangup(fd);
+ return -1;
+ }
+
+ TAILQ_REMOVE(&fd->queue, data, next);
+#ifdef CTL_FREE_LIST
+ TAILQ_INSERT_TAIL(&fd->free_queue, data, next);
+#else
+ if (data->data_size != 0)
+ free(data->data);
+ free(data);
+#endif
+
+ if (TAILQ_FIRST(&fd->queue) != NULL)
+ return 0;
+
+#ifdef PRIVSEP
+ if (IN_PRIVSEP_SE(fd->ctx) && !(fd->flags & FD_LISTEN)) {
+ if (ps_ctl_sendeof(fd) == -1)
+ logerr(__func__);
+ }
+#endif
+
+ /* Done sending data, stop watching write to fd */
+ if (eloop_event_add(fd->ctx->eloop, fd->fd, ELE_READ,
+ control_handle_data, fd) == -1)
+ logerr("%s: eloop_event_add", __func__);
+ return 0;
+}
+
+static void
+control_handle_data(void *arg, unsigned short events)
+{
+ struct fd_list *fd = arg;
+
+ if (!(events & (ELE_READ | ELE_WRITE | ELE_HANGUP)))
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
+
+ if (events & ELE_WRITE && !(events & ELE_HANGUP)) {
+ if (control_handle_write(fd) == -1)
+ return;
+ }
+ if (events & ELE_READ) {
+ if (control_handle_read(fd) == -1)
+ return;
+ }
+ if (events & ELE_HANGUP)
+ control_hangup(fd);
}
void
@@ -192,7 +269,7 @@ control_recvdata(struct fd_list *fd, char *data, size_t len)
if (dhcpcd_handleargs(fd->ctx, fd, argc, argvp) == -1) {
logerr(__func__);
if (errno != EINTR && errno != EAGAIN) {
- control_delete(fd);
+ control_free(fd);
return;
}
}
@@ -220,13 +297,17 @@ control_new(struct dhcpcd_ctx *ctx, int fd, unsigned int flags)
}
static void
-control_handle1(struct dhcpcd_ctx *ctx, int lfd, unsigned int fd_flags)
+control_handle1(struct dhcpcd_ctx *ctx, int lfd, unsigned int fd_flags,
+ unsigned short events)
{
struct sockaddr_un run;
socklen_t len;
struct fd_list *l;
int fd, flags;
+ if (events != ELE_READ)
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
+
len = sizeof(run);
if ((fd = accept(lfd, (struct sockaddr *)&run, &len)) == -1)
goto error;
@@ -248,8 +329,9 @@ control_handle1(struct dhcpcd_ctx *ctx, int lfd, unsigned int fd_flags)
if (l == NULL)
goto error;
- if (eloop_event_add(ctx->eloop, l->fd, control_handle_data, l) == -1)
- logerr(__func__);
+ if (eloop_event_add(ctx->eloop, l->fd, ELE_READ,
+ control_handle_data, l) == -1)
+ logerr("%s: eloop_event_add", __func__);
return;
error:
@@ -259,19 +341,19 @@ error:
}
static void
-control_handle(void *arg)
+control_handle(void *arg, unsigned short events)
{
struct dhcpcd_ctx *ctx = arg;
- control_handle1(ctx, ctx->control_fd, 0);
+ control_handle1(ctx, ctx->control_fd, 0, events);
}
static void
-control_handle_unpriv(void *arg)
+control_handle_unpriv(void *arg, unsigned short events)
{
struct dhcpcd_ctx *ctx = arg;
- control_handle1(ctx, ctx->control_unpriv_fd, FD_UNPRIV);
+ control_handle1(ctx, ctx->control_unpriv_fd, FD_UNPRIV, events);
}
static int
@@ -380,11 +462,15 @@ control_start(struct dhcpcd_ctx *ctx, const char *ifname, sa_family_t family)
return -1;
ctx->control_fd = fd;
- eloop_event_add(ctx->eloop, fd, control_handle, ctx);
+ if (eloop_event_add(ctx->eloop, fd, ELE_READ,
+ control_handle, ctx) == -1)
+ logerr("%s: eloop_event_add", __func__);
if ((fd = control_start1(ctx, ifname, family, S_UNPRIV)) != -1) {
ctx->control_unpriv_fd = fd;
- eloop_event_add(ctx->eloop, fd, control_handle_unpriv, ctx);
+ if (eloop_event_add(ctx->eloop, fd, ELE_READ,
+ control_handle_unpriv, ctx) == -1)
+ logerr("%s: eloop_event_add", __func__);
}
return ctx->control_fd;
}
@@ -419,9 +505,11 @@ control_stop(struct dhcpcd_ctx *ctx)
#ifdef PRIVSEP
if (IN_PRIVSEP_SE(ctx)) {
- if (ps_root_unlink(ctx, ctx->control_sock) == -1)
+ if (ctx->control_sock[0] != '\0' &&
+ ps_root_unlink(ctx, ctx->control_sock) == -1)
retval = -1;
- if (ps_root_unlink(ctx, ctx->control_sock_unpriv) == -1)
+ if (ctx->control_sock_unpriv[0] != '\0' &&
+ ps_root_unlink(ctx, ctx->control_sock_unpriv) == -1)
retval = -1;
return retval;
} else if (ctx->options & DHCPCD_FORKED)
@@ -489,62 +577,11 @@ control_send(struct dhcpcd_ctx *ctx, int argc, char * const *argv)
return write(ctx->control_fd, buffer, len);
}
-static void
-control_writeone(void *arg)
-{
- struct fd_list *fd;
- struct iovec iov[2];
- int iov_len;
- struct fd_data *data;
-
- fd = arg;
- data = TAILQ_FIRST(&fd->queue);
-
- if (data->data_flags & FD_SENDLEN) {
- iov[0].iov_base = &data->data_len;
- iov[0].iov_len = sizeof(size_t);
- iov[1].iov_base = data->data;
- iov[1].iov_len = data->data_len;
- iov_len = 2;
- } else {
- iov[0].iov_base = data->data;
- iov[0].iov_len = data->data_len;
- iov_len = 1;
- }
-
- if (writev(fd->fd, iov, iov_len) == -1) {
- logerr("%s: write", __func__);
- control_delete(fd);
- return;
- }
-
- TAILQ_REMOVE(&fd->queue, data, next);
-#ifdef CTL_FREE_LIST
- TAILQ_INSERT_TAIL(&fd->free_queue, data, next);
-#else
- if (data->data_size != 0)
- free(data->data);
- free(data);
-#endif
-
- if (TAILQ_FIRST(&fd->queue) != NULL)
- return;
-
- if (eloop_event_remove_writecb(fd->ctx->eloop, fd->fd) == -1)
- logerr(__func__);
-#ifdef PRIVSEP
- if (IN_PRIVSEP_SE(fd->ctx) && !(fd->flags & FD_LISTEN)) {
- if (ps_ctl_sendeof(fd) == -1)
- logerr(__func__);
- control_free(fd);
- }
-#endif
-}
-
int
control_queue(struct fd_list *fd, void *data, size_t data_len)
{
struct fd_data *d;
+ unsigned short events;
if (data_len == 0) {
errno = EINVAL;
@@ -589,6 +626,9 @@ control_queue(struct fd_list *fd, void *data, size_t data_len)
d->data_flags = fd->flags & FD_SENDLEN;
TAILQ_INSERT_TAIL(&fd->queue, d, next);
- eloop_event_add_w(fd->ctx->eloop, fd->fd, control_writeone, fd);
- return 0;
+ events = ELE_WRITE;
+ if (fd->flags & FD_LISTEN)
+ events |= ELE_READ;
+ return eloop_event_add(fd->ctx->eloop, fd->fd, events,
+ control_handle_data, fd);
}
diff --git a/src/control.h b/src/control.h
index 110b0a7b2f0b..f5e2bc7e428a 100644
--- a/src/control.h
+++ b/src/control.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
diff --git a/src/defs.h b/src/defs.h
index cde76f8857b0..58897713424d 100644
--- a/src/defs.h
+++ b/src/defs.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -25,11 +25,11 @@
* SUCH DAMAGE.
*/
-#ifndef CONFIG_H
-#define CONFIG_H
+#ifndef DEFS_H
+#define DEFS_H
#define PACKAGE "dhcpcd"
-#define VERSION "9.4.1"
+#define VERSION "10.0.8"
#ifndef PRIVSEP_USER
# define PRIVSEP_USER "_" PACKAGE
diff --git a/src/dev.c b/src/dev.c
index d33c8faaa230..0abc3ee2660e 100644
--- a/src/dev.c
+++ b/src/dev.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -150,7 +150,7 @@ dev_start1(struct dhcpcd_ctx *ctx, const struct dev_dhcpcd *dev_dhcpcd)
dp = opendir(DEVDIR);
if (dp == NULL) {
logdebug("dev: %s", DEVDIR);
- return 0;
+ return -1;
}
r = 0;
@@ -167,10 +167,13 @@ dev_start1(struct dhcpcd_ctx *ctx, const struct dev_dhcpcd *dev_dhcpcd)
}
static void
-dev_handle_data(void *arg)
+dev_handle_data(void *arg, unsigned short events)
{
struct dhcpcd_ctx *ctx;
+ if (events != ELE_READ)
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
+
ctx = arg;
if (ctx->dev->handle_device(arg) == -1) {
/* XXX: an error occured. should we restart dev? */
@@ -191,7 +194,7 @@ dev_start(struct dhcpcd_ctx *ctx, int (*handler)(void *, int, const char *))
ctx->dev_fd = dev_start1(ctx, &dev_dhcpcd);
if (ctx->dev_fd != -1) {
- if (eloop_event_add(ctx->eloop, ctx->dev_fd,
+ if (eloop_event_add(ctx->eloop, ctx->dev_fd, ELE_READ,
dev_handle_data, ctx) == -1)
{
logerr(__func__);
diff --git a/src/dev.h b/src/dev.h
index e7263c5b87da..9aa4072d39c9 100644
--- a/src/dev.h
+++ b/src/dev.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
diff --git a/src/dev/Makefile b/src/dev/Makefile
index 7961149f390d..178a0c0bad47 100644
--- a/src/dev/Makefile
+++ b/src/dev/Makefile
@@ -20,7 +20,7 @@ CLEANFILES+= ${DSOBJ} ${DPLUGS}
.c.So:
${CC} ${PICFLAG} -DPIC ${CPPFLAGS} ${CFLAGS} -c $< -o $@
-.So.so: ${DSOBJ}
+.So.so:
${CC} ${LDFLAGS} -shared -Wl,-x -o $@ -Wl,-soname,$@ \
$< ${LIBS}
diff --git a/src/dev/udev.c b/src/dev/udev.c
index 552f37bc8505..a5c8858241ce 100644
--- a/src/dev/udev.c
+++ b/src/dev/udev.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
diff --git a/src/dhcp-common.c b/src/dhcp-common.c
index dbcfcc564df8..e5dd9efa721f 100644
--- a/src/dhcp-common.c
+++ b/src/dhcp-common.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -125,6 +125,8 @@ dhcp_print_option_encoding(const struct dhcp_opt *opt, int cols)
printf(" binhex");
else if (opt->type & OT_STRING)
printf(" string");
+ else if (opt->type & OT_URI)
+ printf(" uri");
if (opt->type & OT_RFC3361)
printf(" rfc3361");
if (opt->type & OT_RFC3442)
@@ -413,26 +415,23 @@ decode_rfc1035(char *out, size_t len, const uint8_t *p, size_t pl)
}
/* Check for a valid name as per RFC952 and RFC1123 section 2.1 */
-static int
+static ssize_t
valid_domainname(char *lbl, int type)
{
- char *slbl, *lst;
+ char *slbl = lbl, *lst = NULL;
unsigned char c;
- int start, len, errset;
+ int len = 0;
+ bool start = true, errset = false;
if (lbl == NULL || *lbl == '\0') {
errno = EINVAL;
return 0;
}
- slbl = lbl;
- lst = NULL;
- start = 1;
- len = errset = 0;
for (;;) {
c = (unsigned char)*lbl++;
if (c == '\0')
- return 1;
+ return lbl - slbl - 1;
if (c == ' ') {
if (lbl - 1 == slbl) /* No space at start */
break;
@@ -440,7 +439,7 @@ valid_domainname(char *lbl, int type)
break;
/* Skip to the next label */
if (!start) {
- start = 1;
+ start = true;
lst = lbl - 1;
}
if (len)
@@ -459,13 +458,13 @@ valid_domainname(char *lbl, int type)
{
if (++len > NS_MAXLABEL) {
errno = ERANGE;
- errset = 1;
+ errset = true;
break;
}
} else
break;
if (start)
- start = 0;
+ start = false;
}
if (!errset)
@@ -473,7 +472,7 @@ valid_domainname(char *lbl, int type)
if (lst) {
/* At least one valid domain, return it */
*lst = '\0';
- return 1;
+ return lst - slbl;
}
return 0;
}
@@ -521,6 +520,10 @@ print_string(char *dst, size_t len, int type, const uint8_t *data, size_t dl)
errno = EINVAL;
break;
}
+ if (type & OT_URI && isspace(c)) {
+ errno = EINVAL;
+ break;
+ }
if ((type & (OT_ESCSTRING | OT_ESCFILE) &&
(c == '\\' || !isascii(c) || !isprint(c))) ||
(type & OT_ESCFILE && (c == '/' || c == ' ')))
@@ -665,7 +668,7 @@ print_option(FILE *fp, const char *prefix, const struct dhcp_opt *opt,
goto err;
if (sl == 0)
goto done;
- if (valid_domainname(domain, opt->type) == -1)
+ if (!valid_domainname(domain, opt->type))
goto err;
return efprintf(fp, "%s", domain);
}
@@ -678,7 +681,58 @@ print_option(FILE *fp, const char *prefix, const struct dhcp_opt *opt,
return print_rfc3442(fp, data, dl);
#endif
- if (opt->type & OT_STRING) {
+ /* Produces a space separated list of URIs.
+ * This is valid as a URI cannot contain a space. */
+ if ((opt->type & (OT_ARRAY | OT_URI)) == (OT_ARRAY | OT_URI)) {
+#ifdef SMALL
+ errno = ENOTSUP;
+ return -1;
+#else
+ char buf[UINT16_MAX + 1];
+ uint16_t sz;
+ bool first = true;
+
+ while (dl) {
+ if (dl < 2) {
+ errno = EINVAL;
+ goto err;
+ }
+
+ memcpy(&u16, data, sizeof(u16));
+ sz = ntohs(u16);
+ data += sizeof(u16);
+ dl -= sizeof(u16);
+
+ if (sz == 0)
+ continue;
+ if (sz > dl) {
+ errno = EINVAL;
+ goto err;
+ }
+
+ if (print_string(buf, sizeof(buf),
+ opt->type, data, sz) == -1)
+ goto err;
+
+ if (first)
+ first = false;
+ else if (fputc(' ', fp) == EOF)
+ goto err;
+
+ if (fprintf(fp, "%s", buf) == -1)
+ goto err;
+
+ data += sz;
+ dl -= sz;
+ }
+
+ if (fputc('\0', fp) == EOF)
+ goto err;
+ return 0;
+#endif
+ }
+
+ if (opt->type & (OT_STRING | OT_URI)) {
char buf[1024];
if (print_string(buf, sizeof(buf), opt->type, data, dl) == -1)
diff --git a/src/dhcp-common.h b/src/dhcp-common.h
index a82fcd4cec8e..514131404004 100644
--- a/src/dhcp-common.h
+++ b/src/dhcp-common.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -80,6 +80,7 @@
#define OT_ESCFILE (1 << 26)
#define OT_BITFLAG (1 << 27)
#define OT_RESERVED (1 << 28)
+#define OT_URI (1 << 29)
#define DHC_REQ(r, n, o) \
(has_option_mask((r), (o)) && !has_option_mask((n), (o)))
diff --git a/src/dhcp.c b/src/dhcp.c
index fbed2f3ca2e2..6e6b49a2ff01 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -84,11 +84,6 @@
/* We should define a maximum for the NAK exponential backoff */
#define NAKOFF_MAX 60
-/* Wait N nanoseconds between sending a RELEASE and dropping the address.
- * This gives the kernel enough time to actually send it. */
-#define RELEASE_DELAY_S 0
-#define RELEASE_DELAY_NS 10000000
-
#ifndef IPDEFTTL
#define IPDEFTTL 64 /* RFC1340 */
#endif
@@ -137,7 +132,7 @@ static void dhcp_arp_found(struct arp_state *, const struct arp_msg *);
#endif
static void dhcp_handledhcp(struct interface *, struct bootp *, size_t,
const struct in_addr *);
-static void dhcp_handleifudp(void *);
+static void dhcp_handleifudp(void *, unsigned short);
static int dhcp_initstate(struct interface *);
void
@@ -400,7 +395,7 @@ print_rfc3442(FILE *fp, const uint8_t *data, size_t data_len)
static int
decode_rfc3442_rt(rb_tree_t *routes, struct interface *ifp,
- const uint8_t *data, size_t dl, const struct bootp *bootp)
+ const uint8_t *data, size_t dl)
{
const uint8_t *p = data;
const uint8_t *e;
@@ -447,15 +442,6 @@ decode_rfc3442_rt(rb_tree_t *routes, struct interface *ifp,
memcpy(&gateway.s_addr, p, 4);
p += 4;
- /* An on-link host route is normally set by having the
- * gateway match the destination or assigned address */
- if (gateway.s_addr == dest.s_addr ||
- (gateway.s_addr == bootp->yiaddr ||
- gateway.s_addr == bootp->ciaddr))
- {
- gateway.s_addr = INADDR_ANY;
- netmask.s_addr = INADDR_BROADCAST;
- }
if (netmask.s_addr == INADDR_BROADCAST)
rt->rt_flags = RTF_HOST;
@@ -594,7 +580,7 @@ get_option_routes(rb_tree_t *routes, struct interface *ifp,
if (p)
csr = "MS ";
}
- if (p && (n = decode_rfc3442_rt(routes, ifp, p, len, bootp)) != -1) {
+ if (p && (n = decode_rfc3442_rt(routes, ifp, p, len)) != -1) {
const struct dhcp_state *state;
state = D_CSTATE(ifp);
@@ -782,7 +768,7 @@ make_message(struct bootp **bootpm, const struct interface *ifp, uint8_t type)
bootp->op = BOOTREQUEST;
bootp->htype = (uint8_t)ifp->hwtype;
- if (ifp->hwlen != 0 && ifp->hwlen < sizeof(bootp->chaddr)) {
+ if (ifp->hwlen != 0 && ifp->hwlen <= sizeof(bootp->chaddr)) {
bootp->hlen = (uint8_t)ifp->hwlen;
memcpy(&bootp->chaddr, &ifp->hwaddr, ifp->hwlen);
}
@@ -1891,13 +1877,13 @@ dhcp_discover(void *arg)
dhcp_new_xid(ifp);
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
if (!(state->added & STATE_EXPIRED)) {
- if (ifo->fallback)
+ if (ifo->fallback && ifo->fallback_time)
eloop_timeout_add_sec(ifp->ctx->eloop,
- ifo->reboot, dhcp_fallback, ifp);
+ ifo->fallback_time, dhcp_fallback, ifp);
#ifdef IPV4LL
else if (ifo->options & DHCPCD_IPV4LL)
eloop_timeout_add_sec(ifp->ctx->eloop,
- ifo->reboot, ipv4ll_start, ifp);
+ ifo->ipv4ll_time, ipv4ll_start, ifp);
#endif
}
if (ifo->options & DHCPCD_REQUEST)
@@ -1910,12 +1896,31 @@ dhcp_discover(void *arg)
}
static void
+dhcp_requestfailed(void *arg)
+{
+ struct interface *ifp = arg;
+ struct dhcp_state *state = D_STATE(ifp);
+
+ logwarnx("%s: failed to request the lease", ifp->name);
+ free(state->offer);
+ state->offer = NULL;
+ state->offer_len = 0;
+ state->interval = 0;
+ dhcp_discover(ifp);
+}
+
+static void
dhcp_request(void *arg)
{
struct interface *ifp = arg;
struct dhcp_state *state = D_STATE(ifp);
+ struct if_options *ifo = ifp->options;
state->state = DHS_REQUEST;
+ // Handle the server being silent to our request.
+ if (ifo->request_time != 0)
+ eloop_timeout_add_sec(ifp->ctx->eloop, ifo->request_time,
+ dhcp_requestfailed, ifp);
send_request(ifp);
}
@@ -1941,7 +1946,11 @@ dhcp_expire(void *arg)
static void
dhcp_decline(struct interface *ifp)
{
+ struct dhcp_state *state = D_STATE(ifp);
+ // Set the expired state so we send over BPF as this could be
+ // an address defence failure.
+ state->added |= STATE_EXPIRED;
send_message(ifp, DHCP_DECLINE, NULL);
}
#endif
@@ -2005,7 +2014,7 @@ dhcp_finish_dad(struct interface *ifp, struct in_addr *ia)
{
struct dhcp_state *state = D_STATE(ifp);
- if (state->state != DHS_PROBE)
+ if (state->state == DHS_BOUND)
return;
if (state->offer == NULL || state->offer->yiaddr != ia->s_addr)
return;
@@ -2095,8 +2104,12 @@ static void
dhcp_arp_defend_failed(struct arp_state *astate)
{
struct interface *ifp = astate->iface;
+ struct dhcp_state *state = D_STATE(ifp);
+ if (!(ifp->options->options & (DHCPCD_INFORM | DHCPCD_STATIC)))
+ dhcp_decline(ifp);
dhcp_drop(ifp, "EXPIRED");
+ dhcp_unlink(ifp->ctx, state->leasefile);
dhcp_start1(ifp);
}
#endif
@@ -2407,7 +2420,9 @@ openudp:
dhcp_openbpf(ifp);
return;
}
- eloop_event_add(ctx->eloop, state->udp_rfd, dhcp_handleifudp, ifp);
+ if (eloop_event_add(ctx->eloop, state->udp_rfd, ELE_READ,
+ dhcp_handleifudp, ifp) == -1)
+ logerr("%s: eloop_event_add", __func__);
}
static size_t
@@ -2735,7 +2750,7 @@ dhcp_reboot(struct interface *ifp)
/* Need to add this before dhcp_expire and friends. */
if (!ifo->fallback && ifo->options & DHCPCD_IPV4LL)
eloop_timeout_add_sec(ifp->ctx->eloop,
- ifo->reboot, ipv4ll_start, ifp);
+ ifo->ipv4ll_time, ipv4ll_start, ifp);
#endif
if (ifo->options & DHCPCD_LASTLEASE && state->lease.frominfo)
@@ -2753,12 +2768,8 @@ dhcp_reboot(struct interface *ifp)
void
dhcp_drop(struct interface *ifp, const char *reason)
{
- struct dhcp_state *state;
-#ifdef RELEASE_SLOW
- struct timespec ts;
-#endif
+ struct dhcp_state *state = D_STATE(ifp);
- state = D_STATE(ifp);
/* dhcp_start may just have been called and we don't yet have a state
* but we do have a timeout, so punt it. */
if (state == NULL || state->state == DHS_NONE) {
@@ -2792,12 +2803,6 @@ dhcp_drop(struct interface *ifp, const char *reason)
ifp->name, inet_ntoa(state->lease.addr));
dhcp_new_xid(ifp);
send_message(ifp, DHCP_RELEASE, NULL);
-#ifdef RELEASE_SLOW
- /* Give the packet a chance to go */
- ts.tv_sec = RELEASE_DELAY_S;
- ts.tv_nsec = RELEASE_DELAY_NS;
- nanosleep(&ts, NULL);
-#endif
}
}
#ifdef AUTH
@@ -2818,10 +2823,6 @@ dhcp_drop(struct interface *ifp, const char *reason)
dhcp_auth_reset(&state->auth);
#endif
- /* Close DHCP ports so a changed interface family is picked
- * up by a new BPF state. */
- dhcp_close(ifp);
-
state->state = DHS_NONE;
free(state->offer);
state->offer = NULL;
@@ -2845,6 +2846,10 @@ dhcp_drop(struct interface *ifp, const char *reason)
state->lease.addr.s_addr = 0;
ifp->options->options &= ~(DHCPCD_CSR_WARNED |
DHCPCD_ROUTER_HOST_ROUTE_WARNED);
+
+ /* Close DHCP ports so a changed interface family is picked
+ * up by a new BPF state. */
+ dhcp_close(ifp);
}
static int
@@ -3204,8 +3209,8 @@ dhcp_handledhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len,
if (has_option_mask(ifo->requestmask, DHO_IPV6_PREFERRED_ONLY)) {
if (get_option_uint32(ifp->ctx, &v6only_time, bootp, bootp_len,
- DHO_IPV6_PREFERRED_ONLY) == 0 &&
- (state->state == DHS_DISCOVER || state->state == DHS_REBOOT))
+ DHO_IPV6_PREFERRED_ONLY) == 0 && (state->state == DHS_DISCOVER ||
+ state->state == DHS_REBOOT || state->state == DHS_NONE))
{
char v6msg[128];
@@ -3336,7 +3341,8 @@ dhcp_handledhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len,
state->reason = "TEST";
script_runreason(ifp, state->reason);
eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS);
- state->bpf->bpf_flags |= BPF_EOF;
+ if (state->bpf)
+ state->bpf->bpf_flags |= BPF_EOF;
return;
}
eloop_timeout_delete(ifp->ctx->eloop, send_discover, ifp);
@@ -3458,8 +3464,8 @@ is_packet_udp_bootp(void *packet, size_t plen)
if (ip_hlen + ntohs(udp.uh_ulen) > plen)
return false;
- /* Check it's to and from the right ports. */
- if (udp.uh_dport != htons(BOOTPC) || udp.uh_sport != htons(BOOTPS))
+ /* Check it's to the right port. */
+ if (udp.uh_dport != htons(BOOTPC))
return false;
return true;
@@ -3528,12 +3534,6 @@ dhcp_handlebootp(struct interface *ifp, struct bootp *bootp, size_t len,
{
size_t v;
- if (len < offsetof(struct bootp, vend)) {
- logerrx("%s: truncated packet (%zu) from %s",
- ifp->name, len, inet_ntoa(*from));
- return;
- }
-
/* Unlikely, but appeases sanitizers. */
if (len > FRAMELEN_MAX) {
logerrx("%s: packet exceeded frame length (%zu) from %s",
@@ -3623,7 +3623,7 @@ dhcp_packet(struct interface *ifp, uint8_t *data, size_t len,
}
static void
-dhcp_readbpf(void *arg)
+dhcp_readbpf(void *arg, unsigned short events)
{
struct interface *ifp = arg;
uint8_t buf[FRAMELEN_MAX];
@@ -3631,6 +3631,9 @@ dhcp_readbpf(void *arg)
struct dhcp_state *state = D_STATE(ifp);
struct bpf *bpf = state->bpf;
+ if (events != ELE_READ)
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
+
bpf->bpf_flags &= ~BPF_EOF;
while (!(bpf->bpf_flags & BPF_EOF)) {
bytes = bpf_read(bpf, buf, sizeof(buf));
@@ -3663,6 +3666,13 @@ dhcp_recvmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg)
logerr(__func__);
return;
}
+
+ if (iov->iov_len < offsetof(struct bootp, vend)) {
+ logerrx("%s: truncated packet (%zu) from %s",
+ ifp->name, iov->iov_len, inet_ntoa(from->sin_addr));
+ return;
+ }
+
state = D_CSTATE(ifp);
if (state == NULL) {
/* Try re-directing it to another interface. */
@@ -3694,7 +3704,8 @@ dhcp_recvmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg)
}
static void
-dhcp_readudp(struct dhcpcd_ctx *ctx, struct interface *ifp)
+dhcp_readudp(struct dhcpcd_ctx *ctx, struct interface *ifp,
+ unsigned short events)
{
const struct dhcp_state *state;
struct sockaddr_in from;
@@ -3722,6 +3733,9 @@ dhcp_readudp(struct dhcpcd_ctx *ctx, struct interface *ifp)
int s;
ssize_t bytes;
+ if (events != ELE_READ)
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
+
if (ifp != NULL) {
state = D_CSTATE(ifp);
s = state->udp_rfd;
@@ -3739,19 +3753,19 @@ dhcp_readudp(struct dhcpcd_ctx *ctx, struct interface *ifp)
}
static void
-dhcp_handleudp(void *arg)
+dhcp_handleudp(void *arg, unsigned short events)
{
struct dhcpcd_ctx *ctx = arg;
- dhcp_readudp(ctx, NULL);
+ dhcp_readudp(ctx, NULL, events);
}
static void
-dhcp_handleifudp(void *arg)
+dhcp_handleifudp(void *arg, unsigned short events)
{
struct interface *ifp = arg;
- dhcp_readudp(ifp->ctx, ifp);
+ dhcp_readudp(ifp->ctx, ifp, events);
}
static int
@@ -3786,8 +3800,9 @@ dhcp_openbpf(struct interface *ifp)
return -1;
}
- eloop_event_add(ifp->ctx->eloop,
- state->bpf->bpf_fd, dhcp_readbpf, ifp);
+ if (eloop_event_add(ifp->ctx->eloop, state->bpf->bpf_fd, ELE_READ,
+ dhcp_readbpf, ifp) == -1)
+ logerr("%s: eloop_event_add", __func__);
return 0;
}
@@ -3833,6 +3848,7 @@ dhcp_free(struct interface *ifp)
free(ctx->opt_buffer);
ctx->opt_buffer = NULL;
+ ctx->opt_buffer_len = 0;
}
}
@@ -3963,7 +3979,9 @@ dhcp_start1(void *arg)
logerr(__func__);
return;
}
- eloop_event_add(ctx->eloop, ctx->udp_rfd, dhcp_handleudp, ctx);
+ if (eloop_event_add(ctx->eloop, ctx->udp_rfd, ELE_READ,
+ dhcp_handleudp, ctx) == -1)
+ logerr("%s: eloop_event_add", __func__);
}
if (!IN_PRIVSEP(ctx) && ctx->udp_wfd == -1) {
ctx->udp_wfd = xsocket(PF_INET, SOCK_RAW|SOCK_CXNB,IPPROTO_UDP);
diff --git a/src/dhcp.h b/src/dhcp.h
index 9359d564da4d..06f550e9cf30 100644
--- a/src/dhcp.h
+++ b/src/dhcp.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
diff --git a/src/dhcp6.c b/src/dhcp6.c
index 21198c42c059..76a6719b5c04 100644
--- a/src/dhcp6.c
+++ b/src/dhcp6.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -147,12 +147,18 @@ struct dhcp_compat {
uint16_t dhcp6_opt;
};
+/*
+ * RFC 5908 deprecates OPTION_SNTP_SERVERS.
+ * But we can support both as the hook scripts will uniqify the
+ * results if the server returns both options.
+ */
static const struct dhcp_compat dhcp_compats[] = {
{ DHO_DNSSERVER, D6_OPTION_DNS_SERVERS },
{ DHO_HOSTNAME, D6_OPTION_FQDN },
{ DHO_DNSDOMAIN, D6_OPTION_FQDN },
{ DHO_NISSERVER, D6_OPTION_NIS_SERVERS },
{ DHO_NTPSERVER, D6_OPTION_SNTP_SERVERS },
+ { DHO_NTPSERVER, D6_OPTION_NTP_SERVER },
{ DHO_RAPIDCOMMIT, D6_OPTION_RAPID_COMMIT },
{ DHO_FQDN, D6_OPTION_FQDN },
{ DHO_VIVCO, D6_OPTION_VENDOR_CLASS },
@@ -173,8 +179,9 @@ static const char * const dhcp6_statuses[] = {
static void dhcp6_bind(struct interface *, const char *, const char *);
static void dhcp6_failinform(void *);
-static void dhcp6_recvaddr(void *);
+static void dhcp6_recvaddr(void *, unsigned short);
static void dhcp6_startdecline(struct interface *);
+static void dhcp6_startrequest(struct interface *);
#ifdef SMALL
#define dhcp6_hasprefixdelegation(a) (0)
@@ -962,6 +969,8 @@ dhcp6_makemessage(struct interface *ifp)
else
ia_na_len = sizeof(ia_na);
memcpy(ia_na.iaid, ifia->iaid, sizeof(ia_na.iaid));
+ /* RFC 8415 21.4 and 21.21 state that T1 and T2 should be zero.
+ * An RFC compliant server MUST ignore them anyway. */
ia_na.t1 = 0;
ia_na.t2 = 0;
COPYIN(ifia->ia_type, &ia_na, ia_na_len);
@@ -980,11 +989,16 @@ dhcp6_makemessage(struct interface *ifp)
continue;
if (ap->ia_type == D6_OPTION_IA_PD) {
#ifndef SMALL
- struct dhcp6_pd_addr pdp;
+ struct dhcp6_pd_addr pdp = {
+ .prefix_len = ap->prefix_len,
+ /*
+ * RFC 8415 21.22 states that the
+ * valid and preferred lifetimes sent by
+ * the client SHOULD be zero and MUST
+ * be ignored by the server.
+ */
+ };
- pdp.pltime = htonl(ap->prefix_pltime);
- pdp.vltime = htonl(ap->prefix_vltime);
- pdp.prefix_len = ap->prefix_len;
/* pdp.prefix is not aligned, so copy it in. */
memcpy(&pdp.prefix, &ap->prefix, sizeof(pdp.prefix));
COPYIN(D6_OPTION_IAPREFIX, &pdp, sizeof(pdp));
@@ -1009,9 +1023,11 @@ dhcp6_makemessage(struct interface *ifp)
n--;
while (n-- > 0)
*ep++ = *pp--;
- if (u8)
+ n = (size_t)(ep - exb);
+ if (u8) {
*ep = (uint8_t)(*pp << u8);
- n++;
+ n++;
+ }
COPYIN(D6_OPTION_PD_EXCLUDE, exb,
(uint16_t)n);
ia_na_len = (uint16_t)
@@ -1019,11 +1035,16 @@ dhcp6_makemessage(struct interface *ifp)
}
#endif
} else {
- struct dhcp6_ia_addr ia;
+ struct dhcp6_ia_addr ia = {
+ .addr = ap->addr,
+ /*
+ * RFC 8415 21.6 states that the
+ * valid and preferred lifetimes sent by
+ * the client SHOULD be zero and MUST
+ * be ignored by the server.
+ */
+ };
- ia.addr = ap->addr;
- ia.pltime = htonl(ap->prefix_pltime);
- ia.vltime = htonl(ap->prefix_vltime);
COPYIN(D6_OPTION_IA_ADDR, &ia, sizeof(ia));
ia_na_len = (uint16_t)
(ia_na_len + sizeof(o) + sizeof(ia));
@@ -1219,7 +1240,7 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
struct dhcp6_state *state = D6_STATE(ifp);
struct dhcpcd_ctx *ctx = ifp->ctx;
unsigned int RT;
- bool broadcast = true;
+ bool multicast = true;
struct sockaddr_in6 dst = {
.sin6_family = AF_INET6,
/* Setting the port on Linux gives EINVAL when sending.
@@ -1259,24 +1280,24 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
/* Unicasting is denied for these types. */
break;
default:
- broadcast = false;
+ multicast = false;
inet_ntop(AF_INET6, &state->unicast, uaddr,
sizeof(uaddr));
break;
}
}
- dst.sin6_addr = broadcast ? alldhcp : state->unicast;
+ dst.sin6_addr = multicast ? alldhcp : state->unicast;
if (!callback) {
logdebugx("%s: %s %s with xid 0x%02x%02x%02x%s%s",
ifp->name,
- broadcast ? "broadcasting" : "unicasting",
+ multicast ? "multicasting" : "unicasting",
dhcp6_get_op(state->send->type),
state->send->xid[0],
state->send->xid[1],
state->send->xid[2],
- !broadcast ? " " : "",
- !broadcast ? uaddr : "");
+ !multicast ? " " : "",
+ !multicast ? uaddr : "");
RT = 0;
} else {
if (state->IMD &&
@@ -1314,13 +1335,13 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
" next in %0.1f seconds",
ifp->name,
state->IMD != 0 ? "delaying" :
- broadcast ? "broadcasting" : "unicasting",
+ multicast ? "multicasting" : "unicasting",
dhcp6_get_op(state->send->type),
state->send->xid[0],
state->send->xid[1],
state->send->xid[2],
- state->IMD == 0 && !broadcast ? " " : "",
- state->IMD == 0 && !broadcast ? uaddr : "",
+ state->IMD == 0 && !multicast ? " " : "",
+ state->IMD == 0 && !multicast ? uaddr : "",
(float)RT / MSEC_PER_SEC);
/* Wait the initial delay */
@@ -1347,7 +1368,7 @@ dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
#endif
/* Set the outbound interface */
- if (broadcast) {
+ if (multicast) {
struct cmsghdr *cm;
struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index };
@@ -1408,10 +1429,37 @@ dhcp6_sendinform(void *arg)
}
static void
+dhcp6_senddiscover2(void *arg)
+{
+
+ dhcp6_sendmessage(arg, dhcp6_senddiscover2);
+}
+
+static void
+dhcp6_senddiscover1(void *arg)
+{
+ /*
+ * So the initial RT has elapsed.
+ * If we have any ADVERTs we can now REQUEST them.
+ * RFC 8415 15 and 18.2.1
+ */
+ struct interface *ifp = arg;
+ struct dhcp6_state *state = D6_STATE(ifp);
+
+ if (state->recv == NULL || state->recv->type != DHCP6_ADVERTISE)
+ dhcp6_sendmessage(arg, dhcp6_senddiscover2);
+ else
+ dhcp6_startrequest(ifp);
+}
+
+static void
dhcp6_senddiscover(void *arg)
{
+ struct interface *ifp = arg;
+ struct dhcp6_state *state = D6_STATE(ifp);
- dhcp6_sendmessage(arg, dhcp6_senddiscover);
+ dhcp6_sendmessage(arg,
+ state->IMD != 0 ? dhcp6_senddiscover : dhcp6_senddiscover1);
}
static void
@@ -1610,6 +1658,7 @@ dhcp6_startdiscover(void *arg)
struct interface *ifp;
struct dhcp6_state *state;
int llevel;
+ struct ipv6_addr *ia;
ifp = arg;
state = D6_STATE(ifp);
@@ -1634,6 +1683,14 @@ dhcp6_startdiscover(void *arg)
state->new = NULL;
state->new_len = 0;
+ /* If we fail to renew or confirm, our requested addreses will
+ * be marked as stale.
+ To re-request them, just mark them as not stale. */
+ TAILQ_FOREACH(ia, &state->addrs, next) {
+ if (ia->flags & IPV6_AF_REQUEST)
+ ia->flags &= ~IPV6_AF_STALE;
+ }
+
if (dhcp6_makemessage(ifp) == -1)
logerr("%s: %s", __func__, ifp->name);
else
@@ -1649,10 +1706,7 @@ dhcp6_startinform(void *arg)
ifp = arg;
state = D6_STATE(ifp);
- if (state->new_start || (state->new == NULL && !state->failed))
- llevel = LOG_INFO;
- else
- llevel = LOG_DEBUG;
+ llevel = state->failed ? LOG_DEBUG : LOG_INFO;
logmessage(llevel, "%s: requesting DHCPv6 information", ifp->name);
state->state = DH6S_INFORM;
state->RTC = 0;
@@ -1864,7 +1918,6 @@ dhcp6_startrebind(void *arg)
#endif
}
-
static void
dhcp6_startrequest(struct interface *ifp)
{
@@ -2253,9 +2306,7 @@ dhcp6_findpd(struct interface *ifp, const uint8_t *iaid,
} else {
if (!(a->flags & IPV6_AF_DELEGATEDPFX))
a->flags |= IPV6_AF_NEW | IPV6_AF_DELEGATEDPFX;
- a->flags &= ~(IPV6_AF_STALE |
- IPV6_AF_EXTENDED |
- IPV6_AF_REQUEST);
+ a->flags &= ~(IPV6_AF_STALE | IPV6_AF_EXTENDED);
if (a->prefix_vltime != pdp.vltime)
a->flags |= IPV6_AF_NEW;
}
@@ -2407,7 +2458,7 @@ dhcp6_findia(struct interface *ifp, struct dhcp6_message *m, size_t l,
logwarnx("%s: IAID %s T1(%d) > T2(%d) from %s",
ifp->name,
hwaddr_ntoa(iaid, sizeof(iaid), buf,
- sizeof(buf)),
+ sizeof(buf)),
ia.t1, ia.t2, sfrom);
continue;
}
@@ -2565,21 +2616,17 @@ dhcp6_validatelease(struct interface *ifp,
}
state->has_no_binding = false;
nia = dhcp6_findia(ifp, m, len, sfrom, acquired);
- if (nia == 0) {
- if (state->state != DH6S_CONFIRM && ok_errno != 0) {
- logerrx("%s: no useable IA found in lease", ifp->name);
- return -1;
- }
-
- /* We are confirming and have an OK,
- * so look for ia's in our old lease.
- * IA's must have existed here otherwise we would
- * have rejected it earlier. */
- assert(state->new != NULL && state->new_len != 0);
+ if (nia == 0 && state->state == DH6S_CONFIRM && ok_errno == 0 &&
+ state->new && state->new_len)
+ {
state->has_no_binding = false;
nia = dhcp6_findia(ifp, state->new, state->new_len,
sfrom, acquired);
}
+ if (nia == 0) {
+ logerrx("%s: no useable IA found in lease", ifp->name);
+ return -1;
+ }
return nia;
}
@@ -2633,8 +2680,10 @@ dhcp6_readlease(struct interface *ifp, int validate)
/* Check to see if the lease is still valid */
fd = dhcp6_validatelease(ifp, &buf.dhcp6, (size_t)bytes, NULL,
&state->acquired);
- if (fd == -1)
+ if (fd == -1) {
+ bytes = 0; /* We have already reported the error */
goto ex;
+ }
if (state->expire != ND6_INFINITE_LIFETIME &&
(time_t)state->expire < now - mtime &&
@@ -2840,7 +2889,7 @@ dhcp6_script_try_run(struct interface *ifp, int delegated)
if (ap->flags & IPV6_AF_ONLINK) {
if (!(ap->flags & IPV6_AF_DADCOMPLETED) &&
ipv6_iffindaddr(ap->iface, &ap->addr,
- IN6_IFF_TENTATIVE))
+ IN6_IFF_TENTATIVE))
ap->flags |= IPV6_AF_DADCOMPLETED;
if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0
#ifndef SMALL
@@ -3051,7 +3100,7 @@ dhcp6_bind(struct interface *ifp, const char *op, const char *sfrom)
int loglevel;
struct timespec now;
- if (state->state == DH6S_RENEW && !state->new_start) {
+ if (state->state == DH6S_RENEW) {
loglevel = LOG_DEBUG;
TAILQ_FOREACH(ia, &state->addrs, next) {
if (ia->flags & IPV6_AF_NEW) {
@@ -3091,7 +3140,7 @@ dhcp6_bind(struct interface *ifp, const char *op, const char *sfrom)
if (state->reason == NULL)
state->reason = "INFORM6";
o = dhcp6_findmoption(state->new, state->new_len,
- D6_OPTION_INFO_REFRESH_TIME, &ol);
+ D6_OPTION_INFO_REFRESH_TIME, &ol);
if (o == NULL || ol != sizeof(uint32_t))
state->renew = IRT_DEFAULT;
else {
@@ -3439,6 +3488,16 @@ dhcp6_recvif(struct interface *ifp, const char *sfrom,
valid_op = false;
break;
}
+ if (state->recv_len && state->recv->type == DHCP6_ADVERTISE) {
+ /* We already have an advertismemnt.
+ * RFC 8415 says we have to wait for the IRT to elapse.
+ * To keep the same behaviour we won't do anything with
+ * this. In the future we should make a lists of
+ * ADVERTS and pick the "best" one. */
+ logdebugx("%s: discarding ADVERTISEMENT from %s",
+ ifp->name, sfrom);
+ return;
+ }
/* RFC7083 */
o = dhcp6_findmoption(r, len, D6_OPTION_SOL_MAX_RT, &ol);
if (o && ol == sizeof(uint32_t)) {
@@ -3564,7 +3623,7 @@ dhcp6_recvif(struct interface *ifp, const char *sfrom,
else
loginfox("%s: ADV %s from %s",
ifp->name, ia->saddr, sfrom);
- dhcp6_startrequest(ifp);
+ // We will request when the IRT elapses
return;
}
@@ -3711,7 +3770,7 @@ recvif:
}
static void
-dhcp6_recv(struct dhcpcd_ctx *ctx, struct ipv6_addr *ia)
+dhcp6_recv(struct dhcpcd_ctx *ctx, struct ipv6_addr *ia, unsigned short events)
{
struct sockaddr_in6 from;
union {
@@ -3733,6 +3792,9 @@ dhcp6_recv(struct dhcpcd_ctx *ctx, struct ipv6_addr *ia)
int s;
ssize_t bytes;
+ if (events != ELE_READ)
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
+
s = ia != NULL ? ia->dhcp6_fd : ctx->dhcp6_rfd;
bytes = recvmsg(s, &msg, 0);
if (bytes == -1) {
@@ -3745,19 +3807,20 @@ dhcp6_recv(struct dhcpcd_ctx *ctx, struct ipv6_addr *ia)
}
static void
-dhcp6_recvaddr(void *arg)
+
+dhcp6_recvaddr(void *arg, unsigned short events)
{
struct ipv6_addr *ia = arg;
- dhcp6_recv(ia->iface->ctx, ia);
+ dhcp6_recv(ia->iface->ctx, ia, events);
}
static void
-dhcp6_recvctx(void *arg)
+dhcp6_recvctx(void *arg, unsigned short events)
{
struct dhcpcd_ctx *ctx = arg;
- dhcp6_recv(ctx, NULL);
+ dhcp6_recv(ctx, NULL, events);
}
int
@@ -3765,7 +3828,7 @@ dhcp6_openraw(void)
{
int fd, v;
- fd = socket(PF_INET6, SOCK_RAW | SOCK_CXNB, IPPROTO_UDP);
+ fd = xsocket(PF_INET6, SOCK_RAW | SOCK_CXNB, IPPROTO_UDP);
if (fd == -1)
return -1;
@@ -3845,8 +3908,9 @@ dhcp6_activateinterfaces(struct interface *ifp)
sla = &ia->sla[j];
ifd = if_find(ifp->ctx->ifaces, sla->ifname);
if (ifd == NULL) {
- logwarn("%s: cannot delegate to %s",
- ifp->name, sla->ifname);
+ if (*sla->ifname != '-')
+ logwarn("%s: cannot delegate to %s",
+ ifp->name, sla->ifname);
continue;
}
if (!ifd->active) {
@@ -3878,7 +3942,9 @@ dhcp6_start1(void *arg)
logerr(__func__);
return;
}
- eloop_event_add(ctx->eloop, ctx->dhcp6_rfd, dhcp6_recvctx, ctx);
+ if (eloop_event_add(ctx->eloop, ctx->dhcp6_rfd, ELE_READ,
+ dhcp6_recvctx, ctx) == -1)
+ logerr("%s: eloop_event_add", __func__);
}
if (!IN_PRIVSEP(ctx) && ctx->dhcp6_wfd == -1) {
@@ -3936,18 +4002,16 @@ dhcp6_start(struct interface *ifp, enum DH6S init_state)
case DH6S_INIT:
goto gogogo;
case DH6S_INFORM:
+ /* RFC 8415 21.23
+ * If D6_OPTION_INFO_REFRESH_TIME does not exist
+ * then we MUST refresh by IRT_DEFAULT seconds
+ * and should not be influenced by only the
+ * pl/vl time of the RA changing. */
if (state->state == DH6S_INIT ||
- state->state == DH6S_INFORMED ||
(state->state == DH6S_DISCOVER &&
!(ifp->options->options & DHCPCD_IA_FORCED) &&
!ipv6nd_hasradhcp(ifp, true)))
- {
- /* We don't want log spam when the RA
- * has just adjusted it's prefix times. */
- if (state->state != DH6S_INFORMED)
- state->new_start = true;
dhcp6_startinform(ifp);
- }
break;
case DH6S_REQUEST:
if (ifp->options->options & DHCPCD_DHCP6 &&
@@ -4197,13 +4261,13 @@ dhcp6_handleifa(int cmd, struct ipv6_addr *ia, pid_t pid)
if (ia->dhcp6_fd == -1)
ia->dhcp6_fd = dhcp6_openudp(ia->iface->index,
&ia->addr);
- if (ia->dhcp6_fd != -1)
- eloop_event_add(ia->iface->ctx->eloop,
- ia->dhcp6_fd, dhcp6_recvaddr, ia);
+ if (ia->dhcp6_fd != -1 &&
+ eloop_event_add(ia->iface->ctx->eloop,
+ ia->dhcp6_fd, ELE_READ, dhcp6_recvaddr, ia) == -1)
+ logerr("%s: eloop_event_add", __func__);
}
}
-
if ((state = D6_STATE(ifp)) != NULL)
ipv6_handleifa_addrs(cmd, &state->addrs, ia, pid);
}
@@ -4223,6 +4287,7 @@ dhcp6_env(FILE *fp, const char *prefix, const struct interface *ifp,
#ifndef SMALL
const struct dhcp6_state *state;
const struct ipv6_addr *ap;
+ bool first;
#endif
if (m == NULL)
@@ -4314,7 +4379,7 @@ dhcp6_env(FILE *fp, const char *prefix, const struct interface *ifp,
delegated:
#ifndef SMALL
- /* Needed for Delegated Prefixes */
+ /* Needed for Delegated Prefixes */
state = D6_CSTATE(ifp);
TAILQ_FOREACH(ap, &state->addrs, next) {
if (ap->delegating_prefix)
@@ -4324,16 +4389,19 @@ delegated:
return 1;
if (fprintf(fp, "%s_delegated_dhcp6_prefix=", prefix) == -1)
return -1;
+ first = true;
TAILQ_FOREACH(ap, &state->addrs, next) {
if (ap->delegating_prefix == NULL)
continue;
- if (ap != TAILQ_FIRST(&state->addrs)) {
+ if (first)
+ first = false;
+ else {
if (fputc(' ', fp) == EOF)
return -1;
}
if (fprintf(fp, "%s", ap->saddr) == -1)
return -1;
- }
+ }
if (fputc('\0', fp) == EOF)
return -1;
#endif
diff --git a/src/dhcp6.h b/src/dhcp6.h
index 0b257fab6c8d..3995c988e3f9 100644
--- a/src/dhcp6.h
+++ b/src/dhcp6.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -93,6 +93,7 @@
#define D6_OPTION_FQDN 39
#define D6_OPTION_POSIX_TIMEZONE 41
#define D6_OPTION_TZDB_TIMEZONE 42
+#define D6_OPTION_NTP_SERVER 56
#define D6_OPTION_PD_EXCLUDE 67
#define D6_OPTION_SOL_MAX_RT 82
#define D6_OPTION_INF_MAX_RT 83
diff --git a/src/dhcpcd-definitions-small.conf b/src/dhcpcd-definitions-small.conf
index 7f5b8185cbea..d994a913849b 100644
--- a/src/dhcpcd-definitions-small.conf
+++ b/src/dhcpcd-definitions-small.conf
@@ -1,4 +1,4 @@
-# Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+# Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
# All rights reserved
# Bare essentials for automatic IP configuration
diff --git a/src/dhcpcd-definitions.conf b/src/dhcpcd-definitions.conf
index 43e914097923..37ae9744037b 100644
--- a/src/dhcpcd-definitions.conf
+++ b/src/dhcpcd-definitions.conf
@@ -1,4 +1,4 @@
-# Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+# Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
# All rights reserved
# DHCP option definitions for dhcpcd(8)
@@ -12,7 +12,7 @@ define 1 request ipaddress subnet_mask
# RFC3442 states that the CSR has to come before all other routes
# For completeness we also specify static routes then routers
define 121 rfc3442 classless_static_routes
-define 2 uint32 time_offset
+define 2 int32 time_offset
define 3 request array ipaddress routers
define 4 array ipaddress time_servers
define 5 array ipaddress ien116_name_servers
@@ -175,6 +175,9 @@ define 101 string tzdb_timezone
# DHCP IPv6-Only Preferred, RFC8925
define 108 uint32 ipv6_only_preferred
+# DHCP Captive Portal, RFC8910
+define 114 string captive_portal_uri
+
# DHCP Auto-Configuration, RFC2563
define 116 byte auto_configure
@@ -250,7 +253,9 @@ define 141 array domain sip_ua_cs_list
# DHCP ANDSF, RFC6153
define 142 array ipaddress andsf
-define 143 array ip6address andsf6
+
+# DHCP SZTP Redirect, RFC8572
+define 143 array uri sztp_redirect
# DHCP Coordinate LCI, RFC6225
# We have no means of expressing 6 bit lengths
@@ -266,7 +271,11 @@ embed ipaddress primary
embed ipaddress secondary
embed array domain domains
-# Options 147, 148 and 149 are unused, RFC3942
+# Option 149 is unused, RFC3942
+
+# DHCP DOTS, DDoS Open Threat Signaling (DOTS) Agent Discovery RFC8973
+define 147 domain dots_ri
+define 148 array ipaddress dots_address
# DHCP TFTP Server Address, RFC5859
define 150 array ipaddress tftp_servers
@@ -285,7 +294,7 @@ define 150 array ipaddress tftp_servers
#define 156 byte blklqry_state
#define 157 bitflags=0000000R blklqry_source
-# DHCP MUD URL, draft-ietf-opsawg-mud
+# DHCP MUD URL, RFC8520
define 161 string mudurl
# Apart from 161...
@@ -324,7 +333,16 @@ encap 255 flag global
# Options 222 and 223 are unused, RFC3942
-# Options 224-254 are reserved for Private Use
+# Options 224-254 are reserved for site-specific use by RFC3942.
+# For historical reasons, some of these options have well known
+# definitions and we implement those definitions here.
+# Site-specific options are designed to be configured by the end user
+# if needed and any configuration here may change in the future.
+
+# Option 245 is an IANA assigned private number used by Azure DHCP
+# servers to provide the IPv4 address of the Azure WireServer endpoint
+# to virtual machines hosted in Azure.
+define 245 ipaddress azureendpoint
# Option 249 is an IANA assigned private number used by Windows DHCP servers
# to provide the exact same information as option 121, classless static routes
@@ -333,7 +351,39 @@ define 249 rfc3442 ms_classless_static_routes
# An expired RFC for Web Proxy Auto Discovery Protocol does define
# Option 252 which is commonly used by major browsers.
# Apparently the code was assigned by agreement of the DHC working group chair.
-define 252 string wpad_url
+define 252 uri wpad_url
+
+define 224 binhex site_specific_224
+define 225 binhex site_specific_225
+define 226 binhex site_specific_226
+define 227 binhex site_specific_227
+define 228 binhex site_specific_228
+define 229 binhex site_specific_229
+define 230 binhex site_specific_230
+define 231 binhex site_specific_231
+define 232 binhex site_specific_232
+define 233 binhex site_specific_233
+define 234 binhex site_specific_234
+define 235 binhex site_specific_235
+define 236 binhex site_specific_236
+define 237 binhex site_specific_237
+define 238 binhex site_specific_238
+define 239 binhex site_specific_239
+define 240 binhex site_specific_240
+define 241 binhex site_specific_241
+define 242 binhex site_specific_242
+define 243 binhex site_specific_243
+define 244 binhex site_specific_244
+#Option 245 has a custom definition above.
+define 246 binhex site_specific_246
+define 247 binhex site_specific_247
+define 248 binhex site_specific_248
+#Option 249 has a custom definition above.
+define 250 binhex site_specific_250
+define 251 binhex site_specific_251
+#Option 252 has a custom definition above.
+define 253 binhex site_specific_253
+define 254 binhex site_specific_254
# Option 255 End
@@ -538,7 +588,7 @@ define6 57 domain access_domain
define6 58 array domain sip_ua_cs_list
# DHCPv6 Network Boot, RFC5970
-define6 59 string bootfile_url
+define6 59 uri bootfile_url
# We presently cannot decode bootfile_param
define6 60 binhex bootfile_param
define6 61 array uint16 architecture_types
@@ -638,9 +688,22 @@ encap 92 option
# DHCPv6 Address Selection Policy
# Currently not supported
-# DHCPv6 MUD URL, draft-ietf-opsawg-mud
+# DHCPv6 MUD URL, RFC8520
define6 112 string mudurl
+# DHCP Captive Portal, RFC8910
+define6 103 uri captive_portal_uri
+
+# DHCP SZTP Redirect, RFC8572
+define6 136 array uri sztp_redirect
+
+# DHCP DDoS Open Threat Signaling (DOTS) Agent Discovery, RFC8973
+define6 141 domain dots_ri
+define6 142 array ip6address dots_address
+
+# DHCP ANDSF, RFC6153
+define6 143 array ip6address andsf6
+
# Options 86-65535 are unasssinged
##############################################################################
diff --git a/src/dhcpcd-embedded.c.in b/src/dhcpcd-embedded.c.in
index 5ab167055b8b..cd5add5b9ace 100644
--- a/src/dhcpcd-embedded.c.in
+++ b/src/dhcpcd-embedded.c.in
@@ -6,7 +6,7 @@
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
diff --git a/src/dhcpcd-embedded.h.in b/src/dhcpcd-embedded.h.in
index af9122d02429..1aa4a47498e4 100644
--- a/src/dhcpcd-embedded.h.in
+++ b/src/dhcpcd-embedded.h.in
@@ -1,6 +1,6 @@
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
diff --git a/src/dhcpcd.8.in b/src/dhcpcd.8.in
index bc6b3b57d49a..93232840bdd5 100644
--- a/src/dhcpcd.8.in
+++ b/src/dhcpcd.8.in
@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.\" Copyright (c) 2006-2021 Roy Marples
+.\" Copyright (c) 2006-2023 Roy Marples
.\" All rights reserved
.\"
.\" Redistribution and use in source and binary forms, with or without
@@ -24,7 +24,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd August 23, 2021
+.Dd December 10, 2023
.Dt DHCPCD 8
.Os
.Sh NAME
@@ -171,7 +171,7 @@ only works with those interfaces, otherwise
discovers available Ethernet interfaces that can be configured.
When
.Nm
-not limited to one interface on the command line,
+is not limited to one interface on the command line,
it is running in Manager mode.
The
.Nm dhcpcd-ui
@@ -182,16 +182,20 @@ If a single interface is given then
only works for that interface and runs as a separate instance to other
.Nm
processes.
+The
.Fl w , Fl Fl waitip
option is enabled in this instance to maintain compatibility with older
versions.
-Using a single interface also affects the
+Using a single interface,
+optionally further limited to an address protocol,
+also affects the
.Fl k ,
.Fl N ,
.Fl n
and
.Fl x
-options, where the same interface will need to be specified, as a lack of an
+options, where the same interface and any address protocol
+will need to be specified, as a lack of an
interface will imply Manager mode which this is not.
To force starting in Manager mode with only one interface, the
.Fl M , Fl Fl manager
@@ -226,7 +230,6 @@ This script runs each script found in
in a lexical order.
The default installation supplies the scripts
.Pa 01-test ,
-.Pa 02-dump ,
.Pa 20-resolv.conf
and
.Pa 30-hostname .
@@ -267,7 +270,7 @@ instead of the default
.It Fl D , Fl Fl duid Op Ar ll | lt | uuid | value
Use a DHCP Unique Identifier.
If a system UUID is available, that will be used to create a DUID-UUID,
-otheriwse if persistent storage is available then a DUID-LLT
+otherwise if persistent storage is available then a DUID-LLT
(link local address + time) is generated,
otherwise DUID-LL is generated (link local address).
The DUID type can be hinted as an optional parameter if the file
@@ -321,7 +324,7 @@ to put things back afterwards.
does not read a new configuration when this happens - you should rebind if you
need that functionality.
.It Fl F , Fl Fl fqdn Ar fqdn
-Requests that the DHCP server updates DNS using FQDN instead of just a
+Requests that the DHCP server update DNS using FQDN instead of just a
hostname.
Valid values for
.Ar fqdn
@@ -452,9 +455,9 @@ variable for use in
.Pa @SCRIPT@ .
.It Fl p , Fl Fl persistent
.Nm
-normally de-configures the
+de-configures the
.Ar interface
-and configuration when it exits.
+when it exits unless this option is enabled.
Sometimes, this isn't desirable if, for example, you have root mounted over
NFS or SSH clients connect to this host and they need to be notified of
the host shutting down.
@@ -671,7 +674,7 @@ from the DHCP message before processing.
Print the
.Pa pidfile
.Nm
-will use based on commmand-line arguments to stdout.
+will use based on command-line arguments to stdout.
.It Fl Q , Fl Fl require Ar option
Requires the
.Ar option
@@ -790,7 +793,7 @@ If the point to point interface is configured for INFORM, then
unicasts INFORM to the destination, otherwise it defaults to STATIC.
.Sh NOTES
.Nm
-requires a Berkley Packet Filter, or BPF device on BSD based systems and a
+requires a Berkeley Packet Filter, or BPF device on BSD based systems and a
Linux Socket Filter, or LPF device on Linux based systems for all IPv4
configuration.
.Pp
@@ -826,7 +829,7 @@ Linux
.Pa /dev
management modules.
.It Pa @HOOKDIR@
-A directory containing bourne shell scripts that are run by the above script.
+A directory containing Bourne shell scripts that are run by the above script.
Each script can be disabled by using the
.Fl C , Fl Fl nohook
option described above.
@@ -882,4 +885,4 @@ RFC\ 6603, RFC\ 6704, RFC\ 7217, RFC\ 7550, RFC\ 7844.
.An Roy Marples Aq Mt roy@marples.name
.Sh BUGS
Please report them to
-.Lk http://roy.marples.name/projects/dhcpcd
+.Lk https://roy.marples.name/projects/dhcpcd
diff --git a/src/dhcpcd.c b/src/dhcpcd.c
index 6a4c9723742b..a94dbdacc9ba 100644
--- a/src/dhcpcd.c
+++ b/src/dhcpcd.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -26,7 +26,7 @@
* SUCH DAMAGE.
*/
-static const char dhcpcd_copyright[] = "Copyright (c) 2006-2021 Roy Marples";
+static const char dhcpcd_copyright[] = "Copyright (c) 2006-2023 Roy Marples";
#include <sys/file.h>
#include <sys/ioctl.h>
@@ -75,6 +75,9 @@ static const char dhcpcd_copyright[] = "Copyright (c) 2006-2021 Roy Marples";
#ifdef HAVE_CAPSICUM
#include <sys/capsicum.h>
#endif
+#ifdef HAVE_OPENSSL
+#include <openssl/crypto.h>
+#endif
#ifdef HAVE_UTIL_H
#include <util.h>
#endif
@@ -256,7 +259,7 @@ dhcpcd_ifafwaiting(const struct interface *ifp)
bool foundaddr = ipv6_hasaddr(ifp);
if (opts & DHCPCD_WAITIP6 && !foundaddr)
- return AF_INET;
+ return AF_INET6;
if (foundaddr)
foundany = true;
}
@@ -326,6 +329,32 @@ dhcpcd_ipwaited(struct dhcpcd_ctx *ctx)
return 1;
}
+#ifndef THERE_IS_NO_FORK
+void
+dhcpcd_daemonised(struct dhcpcd_ctx *ctx)
+{
+ unsigned int logopts = loggetopts();
+
+ /*
+ * Stop writing to stderr.
+ * On the happy path, only the manager process writes to stderr,
+ * so this just stops wasting fprintf calls to nowhere.
+ */
+ logopts &= ~LOGERR_ERR;
+ logsetopts(logopts);
+
+ /*
+ * We need to do something with stdout/stderr to avoid SIGPIPE.
+ * We know that stdin is already mapped to /dev/null.
+ * TODO: Capture script output and log it to the logfile and/or syslog.
+ */
+ dup2(STDIN_FILENO, STDOUT_FILENO);
+ dup2(STDIN_FILENO, STDERR_FILENO);
+
+ ctx->options |= DHCPCD_DAEMONISED;
+}
+#endif
+
/* Returns the pid of the child, otherwise 0. */
void
dhcpcd_daemonise(struct dhcpcd_ctx *ctx)
@@ -335,8 +364,7 @@ dhcpcd_daemonise(struct dhcpcd_ctx *ctx)
errno = ENOSYS;
return;
#else
- int i;
- unsigned int logopts = loggetopts();
+ int exit_code;
if (ctx->options & DHCPCD_DAEMONISE &&
!(ctx->options & (DHCPCD_DAEMONISED | DHCPCD_NOWAITIP)))
@@ -356,59 +384,61 @@ dhcpcd_daemonise(struct dhcpcd_ctx *ctx)
!(ctx->options & DHCPCD_DAEMONISE))
return;
- /* Don't use loginfo because this makes no sense in a log. */
- if (!(logopts & LOGERR_QUIET) && ctx->stderr_valid)
- (void)fprintf(stderr,
- "forked to background, child pid %d\n", getpid());
- i = EXIT_SUCCESS;
- if (write(ctx->fork_fd, &i, sizeof(i)) == -1)
- logerr("write");
- ctx->options |= DHCPCD_DAEMONISED;
+#ifdef PRIVSEP
+ if (IN_PRIVSEP(ctx))
+ ps_daemonised(ctx);
+ else
+#endif
+ dhcpcd_daemonised(ctx);
+
eloop_event_delete(ctx->eloop, ctx->fork_fd);
+ exit_code = EXIT_SUCCESS;
+ if (write(ctx->fork_fd, &exit_code, sizeof(exit_code)) == -1)
+ logerr(__func__);
close(ctx->fork_fd);
ctx->fork_fd = -1;
-
- /*
- * Stop writing to stderr.
- * On the happy path, only the manager process writes to stderr,
- * so this just stops wasting fprintf calls to nowhere.
- * All other calls - ie errors in privsep processes or script output,
- * will error when printing.
- * If we *really* want to fix that, then we need to suck
- * stderr/stdout in the manager process and either disacrd it or pass
- * it to the launcher process and then to stderr.
- */
- logopts &= ~LOGERR_ERR;
- logsetopts(logopts);
#endif
}
static void
-dhcpcd_drop(struct interface *ifp, int stop)
+dhcpcd_drop_af(struct interface *ifp, int stop, int af)
{
+ if (af == AF_UNSPEC || af == AF_INET6) {
#ifdef DHCP6
- dhcp6_drop(ifp, stop ? NULL : "EXPIRE6");
+ dhcp6_drop(ifp, stop ? NULL : "EXPIRE6");
#endif
#ifdef INET6
- ipv6nd_drop(ifp);
- ipv6_drop(ifp);
+ ipv6nd_drop(ifp);
+ ipv6_drop(ifp);
#endif
+ }
+
+ if (af == AF_UNSPEC || af == AF_INET) {
#ifdef IPV4LL
- ipv4ll_drop(ifp);
+ ipv4ll_drop(ifp);
#endif
#ifdef INET
- dhcp_drop(ifp, stop ? "STOP" : "EXPIRE");
+ dhcp_drop(ifp, stop ? "STOP" : "EXPIRE");
#endif
#ifdef ARP
- arp_drop(ifp);
+ arp_drop(ifp);
#endif
+ }
+
#if !defined(DHCP6) && !defined(DHCP)
UNUSED(stop);
#endif
}
static void
+dhcpcd_drop(struct interface *ifp, int stop)
+{
+
+ dhcpcd_drop_af(ifp, stop, AF_UNSPEC);
+}
+
+static void
stop_interface(struct interface *ifp, const char *reason)
{
struct dhcpcd_ctx *ctx;
@@ -640,20 +670,17 @@ configure_interface(struct interface *ifp, int argc, char **argv,
}
static void
-dhcpcd_initstate2(struct interface *ifp, unsigned long long options)
+dhcpcd_initstate1(struct interface *ifp, int argc, char **argv,
+ unsigned long long options)
{
struct if_options *ifo;
- if (options) {
- if ((ifo = default_config(ifp->ctx)) == NULL) {
- logerr(__func__);
- return;
- }
- ifo->options |= options;
- free(ifp->options);
- ifp->options = ifo;
- } else
- ifo = ifp->options;
+ configure_interface(ifp, argc, argv, options);
+ if (!ifp->active)
+ return;
+
+ ifo = ifp->options;
+ ifo->options |= options;
#ifdef INET6
if (ifo->options & DHCPCD_IPV6 && ipv6_init(ifp->ctx) == -1) {
@@ -664,16 +691,6 @@ dhcpcd_initstate2(struct interface *ifp, unsigned long long options)
}
static void
-dhcpcd_initstate1(struct interface *ifp, int argc, char **argv,
- unsigned long long options)
-{
-
- configure_interface(ifp, argc, argv, options);
- if (ifp->active)
- dhcpcd_initstate2(ifp, 0);
-}
-
-static void
dhcpcd_initstate(struct interface *ifp, unsigned long long options)
{
@@ -1016,15 +1033,17 @@ dhcpcd_activateinterface(struct interface *ifp, unsigned long long options)
if (ifp->active)
return;
+ /* IF_ACTIVE_USER will start protocols when the interface is started.
+ * IF_ACTIVE will ask the protocols for setup,
+ * such as any delegated prefixes. */
ifp->active = IF_ACTIVE;
- dhcpcd_initstate2(ifp, options);
+ dhcpcd_initstate(ifp, options);
/* It's possible we might not have been able to load
* a config. */
if (!ifp->active)
return;
- configure_interface1(ifp);
run_preinit(ifp);
dhcpcd_prestartinterface(ifp);
}
@@ -1105,15 +1124,19 @@ out:
if_free(ifp);
}
free(ifs);
+ if_freeifaddrs(ctx, &ifaddrs);
return e;
}
static void
-dhcpcd_handlelink(void *arg)
+dhcpcd_handlelink(void *arg, unsigned short events)
{
struct dhcpcd_ctx *ctx = arg;
+ if (events != ELE_READ)
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
+
if (if_handlelink(ctx) == -1) {
if (errno == ENOBUFS || errno == ENOMEM) {
dhcpcd_linkoverflow(ctx);
@@ -1241,6 +1264,7 @@ dhcpcd_linkoverflow(struct dhcpcd_ctx *ctx)
if_markaddrsstale(ctx->ifaces);
if_learnaddrs(ctx, ctx->ifaces, &ifaddrs);
if_deletestaleaddrs(ctx->ifaces);
+ if_freeifaddrs(ctx, &ifaddrs);
}
void
@@ -1406,7 +1430,8 @@ dhcpcd_renew(struct dhcpcd_ctx *ctx)
#ifdef USE_SIGNALS
#define sigmsg "received %s, %s"
-static void
+static volatile bool dhcpcd_exiting = false;
+void
dhcpcd_signal_cb(int sig, void *arg)
{
struct dhcpcd_ctx *ctx = arg;
@@ -1465,8 +1490,12 @@ dhcpcd_signal_cb(int sig, void *arg)
logerr("logopen");
return;
case SIGCHLD:
+#ifdef PRIVSEP
+ ps_root_signalcb(sig, ctx);
+#else
while (waitpid(-1, NULL, WNOHANG) > 0)
;
+#endif
return;
default:
logerrx("received signal %d but don't know what to do with it",
@@ -1474,9 +1503,20 @@ dhcpcd_signal_cb(int sig, void *arg)
return;
}
+ /*
+ * Privsep has a mini-eloop for reading data from other processes.
+ * This mini-eloop processes signals as well so we can reap children.
+ * During teardown we don't want to process SIGTERM or SIGINT again,
+ * as that could trigger memory issues.
+ */
+ if (dhcpcd_exiting)
+ return;
+
+ dhcpcd_exiting = true;
if (!(ctx->options & DHCPCD_TEST))
stop_all_interfaces(ctx, opts);
eloop_exit(ctx->eloop, exit_code);
+ dhcpcd_exiting = false;
}
#endif
@@ -1485,8 +1525,9 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd,
int argc, char **argv)
{
struct interface *ifp;
- unsigned long long opts;
- int opt, oi, do_reboot, do_renew, af = AF_UNSPEC;
+ struct if_options *ifo;
+ unsigned long long opts, orig_opts;
+ int opt, oi, oifind, do_reboot, do_renew, af = AF_UNSPEC;
size_t len, l, nifaces;
char *tmp, *p;
@@ -1502,7 +1543,7 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd,
return control_queue(fd, UNCONST(fd->ctx->cffile),
strlen(fd->ctx->cffile) + 1);
} else if (strcmp(*argv, "--getinterfaces") == 0) {
- optind = argc = 0;
+ oifind = argc = 0;
goto dumplease;
} else if (strcmp(*argv, "--listen") == 0) {
fd->flags |= FD_LISTEN;
@@ -1565,6 +1606,9 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd,
}
}
+ /* store the index; the optind will change when a getopt get called */
+ oifind = optind;
+
if (opts & DHCPCD_DUMPLEASE) {
ctx->options |= DHCPCD_DUMPLEASE;
dumplease:
@@ -1572,11 +1616,11 @@ dumplease:
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
if (!ifp->active)
continue;
- for (oi = optind; oi < argc; oi++) {
+ for (oi = oifind; oi < argc; oi++) {
if (strcmp(ifp->name, argv[oi]) == 0)
break;
}
- if (optind == argc || oi < argc) {
+ if (oifind == argc || oi < argc) {
opt = send_interface(NULL, ifp, af);
if (opt == -1)
goto dumperr;
@@ -1588,11 +1632,11 @@ dumplease:
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
if (!ifp->active)
continue;
- for (oi = optind; oi < argc; oi++) {
+ for (oi = oifind; oi < argc; oi++) {
if (strcmp(ifp->name, argv[oi]) == 0)
break;
}
- if (optind == argc || oi < argc) {
+ if (oifind == argc || oi < argc) {
if (send_interface(fd, ifp, af) == -1)
goto dumperr;
}
@@ -1611,30 +1655,50 @@ dumperr:
}
if (opts & (DHCPCD_EXITING | DHCPCD_RELEASE)) {
- if (optind == argc) {
+ if (oifind == argc && af == AF_UNSPEC) {
stop_all_interfaces(ctx, opts);
eloop_exit(ctx->eloop, EXIT_SUCCESS);
return 0;
}
- for (oi = optind; oi < argc; oi++) {
- if ((ifp = if_find(ctx->ifaces, argv[oi])) == NULL)
- continue;
+
+ TAILQ_FOREACH(ifp, ctx->ifaces, next) {
if (!ifp->active)
continue;
- ifp->options->options |= opts;
+ for (oi = oifind; oi < argc; oi++) {
+ if (strcmp(ifp->name, argv[oi]) == 0)
+ break;
+ }
+ if (oi == argc)
+ continue;
+
+ ifo = ifp->options;
+ orig_opts = ifo->options;
+ ifo->options |= opts;
if (opts & DHCPCD_RELEASE)
- ifp->options->options &= ~DHCPCD_PERSISTENT;
- stop_interface(ifp, NULL);
+ ifo->options &= ~DHCPCD_PERSISTENT;
+ switch (af) {
+ case AF_INET:
+ ifo->options &= ~DHCPCD_IPV4;
+ break;
+ case AF_INET6:
+ ifo->options &= ~DHCPCD_IPV6;
+ break;
+ }
+ if (af != AF_UNSPEC)
+ dhcpcd_drop_af(ifp, 1, af);
+ else
+ stop_interface(ifp, NULL);
+ ifo->options = orig_opts;
}
return 0;
}
if (do_renew) {
- if (optind == argc) {
+ if (oifind == argc) {
dhcpcd_renew(ctx);
return 0;
}
- for (oi = optind; oi < argc; oi++) {
+ for (oi = oifind; oi < argc; oi++) {
if ((ifp = if_find(ctx->ifaces, argv[oi])) == NULL)
continue;
dhcpcd_ifrenew(ifp);
@@ -1644,19 +1708,22 @@ dumperr:
reload_config(ctx);
/* XXX: Respect initial commandline options? */
- reconf_reboot(ctx, do_reboot, argc, argv, optind - 1);
+ reconf_reboot(ctx, do_reboot, argc, argv, oifind);
return 0;
}
-static void dhcpcd_readdump1(void *);
+static void dhcpcd_readdump1(void *, unsigned short);
static void
-dhcpcd_readdump2(void *arg)
+dhcpcd_readdump2(void *arg, unsigned short events)
{
struct dhcpcd_ctx *ctx = arg;
ssize_t len;
int exit_code = EXIT_FAILURE;
+ if (events != ELE_READ)
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
+
len = read(ctx->control_fd, ctx->ctl_buf + ctx->ctl_bufpos,
ctx->ctl_buflen - ctx->ctl_bufpos);
if (len == -1) {
@@ -1675,8 +1742,9 @@ dhcpcd_readdump2(void *arg)
fflush(stdout);
if (--ctx->ctl_extra != 0) {
putchar('\n');
- eloop_event_add(ctx->eloop, ctx->control_fd,
- dhcpcd_readdump1, ctx);
+ if (eloop_event_add(ctx->eloop, ctx->control_fd, ELE_READ,
+ dhcpcd_readdump1, ctx) == -1)
+ logerr("%s: eloop_event_add", __func__);
return;
}
exit_code = EXIT_SUCCESS;
@@ -1687,11 +1755,14 @@ finished:
}
static void
-dhcpcd_readdump1(void *arg)
+dhcpcd_readdump1(void *arg, unsigned short events)
{
struct dhcpcd_ctx *ctx = arg;
ssize_t len;
+ if (events != ELE_READ)
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
+
len = read(ctx->control_fd, &ctx->ctl_buflen, sizeof(ctx->ctl_buflen));
if (len != sizeof(ctx->ctl_buflen)) {
if (len != -1)
@@ -1709,8 +1780,9 @@ dhcpcd_readdump1(void *arg)
goto err;
ctx->ctl_bufpos = 0;
- eloop_event_add(ctx->eloop, ctx->control_fd,
- dhcpcd_readdump2, ctx);
+ if (eloop_event_add(ctx->eloop, ctx->control_fd, ELE_READ,
+ dhcpcd_readdump2, ctx) == -1)
+ logerr("%s: eloop_event_add", __func__);
return;
err:
@@ -1719,11 +1791,14 @@ err:
}
static void
-dhcpcd_readdump0(void *arg)
+dhcpcd_readdump0(void *arg, unsigned short events)
{
struct dhcpcd_ctx *ctx = arg;
ssize_t len;
+ if (events != ELE_READ)
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
+
len = read(ctx->control_fd, &ctx->ctl_extra, sizeof(ctx->ctl_extra));
if (len != sizeof(ctx->ctl_extra)) {
if (len != -1)
@@ -1738,8 +1813,9 @@ dhcpcd_readdump0(void *arg)
return;
}
- eloop_event_add(ctx->eloop, ctx->control_fd,
- dhcpcd_readdump1, ctx);
+ if (eloop_event_add(ctx->eloop, ctx->control_fd, ELE_READ,
+ dhcpcd_readdump1, ctx) == -1)
+ logerr("%s: eloop_event_add", __func__);
}
static void
@@ -1759,48 +1835,85 @@ dhcpcd_readdump(struct dhcpcd_ctx *ctx)
if (eloop_timeout_add_sec(ctx->eloop, 5,
dhcpcd_readdumptimeout, ctx) == -1)
return -1;
- return eloop_event_add(ctx->eloop, ctx->control_fd,
+ return eloop_event_add(ctx->eloop, ctx->control_fd, ELE_READ,
dhcpcd_readdump0, ctx);
}
static void
-dhcpcd_fork_cb(void *arg)
+dhcpcd_fork_cb(void *arg, unsigned short events)
{
struct dhcpcd_ctx *ctx = arg;
int exit_code;
ssize_t len;
+ if (!(events & ELE_READ))
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
+
len = read(ctx->fork_fd, &exit_code, sizeof(exit_code));
if (len == -1) {
logerr(__func__);
- exit_code = EXIT_FAILURE;
- } else if ((size_t)len < sizeof(exit_code)) {
+ eloop_exit(ctx->eloop, EXIT_FAILURE);
+ return;
+ }
+ if (len == 0) {
+ if (ctx->options & DHCPCD_FORKED) {
+ logerrx("%s: dhcpcd manager hungup", __func__);
+ eloop_exit(ctx->eloop, EXIT_FAILURE);
+ } else {
+ // Launcher exited
+ eloop_event_delete(ctx->eloop, ctx->fork_fd);
+ close(ctx->fork_fd);
+ ctx->fork_fd = -1;
+ }
+ return;
+ }
+ if ((size_t)len < sizeof(exit_code)) {
logerrx("%s: truncated read %zd (expected %zu)",
__func__, len, sizeof(exit_code));
- exit_code = EXIT_FAILURE;
+ eloop_exit(ctx->eloop, EXIT_FAILURE);
+ return;
}
- if (ctx->options & DHCPCD_FORKED)
+
+ if (ctx->options & DHCPCD_FORKED) {
+ if (exit_code == EXIT_SUCCESS)
+ logdebugx("forked to background");
eloop_exit(ctx->eloop, exit_code);
- else
+ } else
dhcpcd_signal_cb(exit_code, ctx);
}
static void
-dhcpcd_stderr_cb(void *arg)
+dhcpcd_pidfile_timeout(void *arg)
{
struct dhcpcd_ctx *ctx = arg;
- char log[BUFSIZ];
- ssize_t len;
+ pid_t pid;
- len = read(ctx->stderr_fd, log, sizeof(log));
- if (len == -1) {
- if (errno != ECONNRESET)
- logerr(__func__);
- return;
+ pid = pidfile_read(ctx->pidfile);
+
+ if(pid == -1)
+ eloop_exit(ctx->eloop, EXIT_SUCCESS);
+ else if (++ctx->duid_len >= 100) { /* overload duid_len */
+ logerrx("pid %d failed to exit", pid);
+ eloop_exit(ctx->eloop, EXIT_FAILURE);
+ } else
+ eloop_timeout_add_msec(ctx->eloop, 100,
+ dhcpcd_pidfile_timeout, ctx);
+}
+
+static int dup_null(int fd)
+{
+ int fd_null = open(_PATH_DEVNULL, O_WRONLY);
+ int err;
+
+ if (fd_null == -1) {
+ logwarn("open %s", _PATH_DEVNULL);
+ return -1;
}
- log[len] = '\0';
- fprintf(stderr, "%s", log);
+ if ((err = dup2(fd_null, fd)) == -1)
+ logwarn("dup2 %d", fd);
+ close(fd_null);
+ return err;
}
int
@@ -1816,7 +1929,7 @@ main(int argc, char **argv, char **envp)
ssize_t len;
#if defined(USE_SIGNALS) || !defined(THERE_IS_NO_FORK)
pid_t pid;
- int fork_fd[2], stderr_fd[2];
+ int fork_fd[2];
#endif
#ifdef USE_SIGNALS
int sig = 0;
@@ -1868,6 +1981,7 @@ main(int argc, char **argv, char **envp)
}
memset(&ctx, 0, sizeof(ctx));
+ closefrom(STDERR_FILENO + 1);
ifo = NULL;
ctx.cffile = CONFIG;
@@ -1897,18 +2011,21 @@ main(int argc, char **argv, char **envp)
ctx.dhcp6_wfd = -1;
#endif
#ifdef PRIVSEP
- ctx.ps_root_fd = ctx.ps_log_fd = ctx.ps_data_fd = -1;
- ctx.ps_inet_fd = ctx.ps_control_fd = -1;
+ ctx.ps_log_fd = ctx.ps_log_root_fd = -1;
TAILQ_INIT(&ctx.ps_processes);
#endif
- /* Check our streams for validity */
- ctx.stdin_valid = fcntl(STDIN_FILENO, F_GETFD) != -1;
- ctx.stdout_valid = fcntl(STDOUT_FILENO, F_GETFD) != -1;
- ctx.stderr_valid = fcntl(STDERR_FILENO, F_GETFD) != -1;
-
logopts = LOGERR_LOG | LOGERR_LOG_DATE | LOGERR_LOG_PID;
- if (ctx.stderr_valid)
+
+ /* Ensure we have stdin, stdout and stderr file descriptors.
+ * This is important as we do run scripts which expect these. */
+ if (fcntl(STDIN_FILENO, F_GETFD) == -1)
+ dup_null(STDIN_FILENO);
+ if (fcntl(STDOUT_FILENO, F_GETFD) == -1)
+ dup_null(STDOUT_FILENO);
+ if (fcntl(STDERR_FILENO, F_GETFD) == -1)
+ dup_null(STDERR_FILENO);
+ else
logopts |= LOGERR_ERR;
i = 0;
@@ -1940,12 +2057,6 @@ main(int argc, char **argv, char **envp)
sig = SIGHUP;
siga = "HUP";
break;
- case 'g':
- case 'p':
- /* Force going via command socket as we're
- * out of user definable signals. */
- i = 4;
- break;
case 'q':
/* -qq disables console output entirely.
* This is important for systemd because it logs
@@ -2095,6 +2206,14 @@ printpidfile:
snprintf(ctx.pidfile, sizeof(ctx.pidfile),
PIDFILE, "", "", "");
ctx.options |= DHCPCD_MANAGER;
+
+ /*
+ * If we are given any interfaces or a family, we
+ * cannot send a signal as that would impact
+ * other interfaces.
+ */
+ if (optind != argc || family != AF_UNSPEC)
+ sig = 0;
}
if (ctx.options & DHCPCD_PRINT_PIDFILE) {
printf("%s\n", ctx.pidfile);
@@ -2130,35 +2249,29 @@ printpidfile:
if (pid != 0 && pid != -1)
loginfox("sending signal %s to pid %d", siga, pid);
if (pid == 0 || pid == -1 || kill(pid, sig) != 0) {
- if (sig != SIGHUP && sig != SIGUSR1 && errno != EPERM)
- logerrx(PACKAGE" not running");
if (pid != 0 && pid != -1 && errno != ESRCH) {
logerr("kill");
goto exit_failure;
}
unlink(ctx.pidfile);
- if (sig != SIGHUP && sig != SIGUSR1)
- goto exit_failure;
+ /* We can still continue and send the command
+ * via the control socket. */
} else {
- struct timespec ts;
-
if (sig == SIGHUP || sig == SIGUSR1)
goto exit_success;
/* Spin until it exits */
loginfox("waiting for pid %d to exit", pid);
- ts.tv_sec = 0;
- ts.tv_nsec = 100000000; /* 10th of a second */
- for(i = 0; i < 100; i++) {
- nanosleep(&ts, NULL);
- if (pidfile_read(ctx.pidfile) == -1)
- goto exit_success;
- }
- logerrx("pid %d failed to exit", pid);
- goto exit_failure;
+ dhcpcd_pidfile_timeout(&ctx);
+ goto run_loop;
}
}
#endif
+#ifdef HAVE_OPENSSL
+ OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS |
+ OPENSSL_INIT_ADD_ALL_DIGESTS | OPENSSL_INIT_LOAD_CONFIG, NULL);
+#endif
+
#ifdef PRIVSEP
ps_init(&ctx);
#endif
@@ -2207,12 +2320,8 @@ printpidfile:
}
#endif
- /* Test against siga instead of sig to avoid gcc
- * warning about a bogus potential signed overflow.
- * The end result will be the same. */
- if ((siga == NULL || i == 4 || ctx.ifc != 0) &&
- !(ctx.options & DHCPCD_TEST))
- {
+ /* Try and contact the manager process to send the instruction. */
+ if (!(ctx.options & DHCPCD_TEST)) {
ctx.options |= DHCPCD_FORKED; /* avoid socket unlink */
if (!(ctx.options & DHCPCD_MANAGER))
ctx.control_fd = control_open(argv[optind], family,
@@ -2249,9 +2358,14 @@ printpidfile:
} else {
if (errno != ENOENT)
logerr("%s: control_open", __func__);
- if (ctx.options & DHCPCD_DUMPLEASE) {
+ /* If asking dhcpcd to exit and we failed to
+ * send a signal or a message then we
+ * don't proceed past here. */
+ if (ctx.options & DHCPCD_DUMPLEASE ||
+ sig == SIGTERM || sig == SIGALRM)
+ {
if (errno == ENOENT)
- logerrx("dhcpcd is not running");
+ logerrx(PACKAGE" is not running");
goto exit_failure;
}
if (errno == EPERM || errno == EACCES)
@@ -2279,17 +2393,15 @@ printpidfile:
}
loginfox(PACKAGE "-" VERSION " starting");
- if (ctx.stdin_valid && freopen(_PATH_DEVNULL, "w", stdin) == NULL)
- logwarn("freopen stdin");
+
+ // We don't need stdin past this point
+ dup_null(STDIN_FILENO);
#if defined(USE_SIGNALS) && !defined(THERE_IS_NO_FORK)
if (!(ctx.options & DHCPCD_DAEMONISE))
goto start_manager;
- if (xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, fork_fd) == -1 ||
- (ctx.stderr_valid &&
- xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, stderr_fd) == -1))
- {
+ if (xsocketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CXNB, 0, fork_fd) == -1) {
logerr("socketpair");
goto exit_failure;
}
@@ -2306,25 +2418,10 @@ printpidfile:
goto exit_failure;
}
#endif
- eloop_event_add(ctx.eloop, ctx.fork_fd, dhcpcd_fork_cb, &ctx);
+ if (eloop_event_add(ctx.eloop, ctx.fork_fd, ELE_READ,
+ dhcpcd_fork_cb, &ctx) == -1)
+ logerr("%s: eloop_event_add", __func__);
- /*
- * Redirect stderr to the stderr socketpair.
- * Redirect stdout as well.
- * dhcpcd doesn't output via stdout, but something in
- * a called script might.
- */
- if (ctx.stderr_valid) {
- if (dup2(stderr_fd[1], STDERR_FILENO) == -1 ||
- (ctx.stdout_valid &&
- dup2(stderr_fd[1], STDOUT_FILENO) == -1))
- logerr("dup2");
- close(stderr_fd[0]);
- close(stderr_fd[1]);
- } else if (ctx.stdout_valid) {
- if (freopen(_PATH_DEVNULL, "w", stdout) == NULL)
- logerr("freopen stdout");
- }
if (setsid() == -1) {
logerr("%s: setsid", __func__);
goto exit_failure;
@@ -2335,6 +2432,7 @@ printpidfile:
logerr("fork");
goto exit_failure;
case 0:
+ eloop_forked(ctx.eloop);
break;
default:
ctx.options |= DHCPCD_FORKED; /* A lie */
@@ -2353,20 +2451,10 @@ printpidfile:
goto exit_failure;
}
#endif
- eloop_event_add(ctx.eloop, ctx.fork_fd, dhcpcd_fork_cb, &ctx);
+ if (eloop_event_add(ctx.eloop, ctx.fork_fd, ELE_READ,
+ dhcpcd_fork_cb, &ctx) == -1)
+ logerr("%s: eloop_event_add", __func__);
- if (ctx.stderr_valid) {
- ctx.stderr_fd = stderr_fd[0];
- close(stderr_fd[1]);
-#ifdef PRIVSEP_RIGHTS
- if (ps_rights_limit_fd(ctx.stderr_fd) == 1) {
- logerr("ps_rights_limit_fd");
- goto exit_failure;
- }
-#endif
- eloop_event_add(ctx.eloop, ctx.stderr_fd,
- dhcpcd_stderr_cb, &ctx);
- }
#ifdef PRIVSEP
if (IN_PRIVSEP(&ctx) && ps_managersandbox(&ctx, NULL) == -1)
goto exit_failure;
@@ -2374,9 +2462,14 @@ printpidfile:
goto run_loop;
}
+#ifdef DEBUG_FD
+ loginfox("forkfd %d", ctx.fork_fd);
+#endif
+
/* We have now forked, setsid, forked once more.
* From this point on, we are the controlling daemon. */
logdebugx("spawned manager process on PID %d", getpid());
+
start_manager:
ctx.options |= DHCPCD_STARTED;
if ((pid = pidfile_lock(ctx.pidfile)) != 0) {
@@ -2447,7 +2540,9 @@ start_manager:
/* Start handling kernel messages for interfaces, addresses and
* routes. */
- eloop_event_add(ctx.eloop, ctx.link_fd, dhcpcd_handlelink, &ctx);
+ if (eloop_event_add(ctx.eloop, ctx.link_fd, ELE_READ,
+ dhcpcd_handlelink, &ctx) == -1)
+ logerr("%s: eloop_event_add", __func__);
#ifdef PRIVSEP
if (IN_PRIVSEP(&ctx) && ps_managersandbox(&ctx, "stdio route") == -1)
@@ -2476,6 +2571,7 @@ start_manager:
if (ifp->active == IF_ACTIVE_USER)
break;
}
+
if (ifp == NULL) {
if (ctx.ifc == 0) {
int loglevel;
@@ -2497,6 +2593,8 @@ start_manager:
dhcpcd_initstate1(ifp, argc, argv, 0);
}
if_learnaddrs(&ctx, ctx.ifaces, &ifaddrs);
+ if_freeifaddrs(&ctx, &ifaddrs);
+ ifaddrs = NULL;
if (ctx.options & DHCPCD_BACKGROUND)
dhcpcd_daemonise(&ctx);
@@ -2567,17 +2665,7 @@ exit_failure:
exit1:
if (!(ctx.options & DHCPCD_TEST) && control_stop(&ctx) == -1)
logerr("%s: control_stop", __func__);
- if (ifaddrs != NULL) {
-#ifdef PRIVSEP_GETIFADDRS
- if (IN_PRIVSEP(&ctx))
- free(ifaddrs);
- else
-#endif
- freeifaddrs(ifaddrs);
- }
- /* ps_stop will clear DHCPCD_PRIVSEP but we need to
- * remember it to avoid attemping to remove the pidfile */
- oi = ctx.options & DHCPCD_PRIVSEP ? 1 : 0;
+ if_freeifaddrs(&ctx, &ifaddrs);
#ifdef PRIVSEP
ps_stop(&ctx);
#endif
@@ -2599,10 +2687,6 @@ exit1:
free(ctx.script_env);
rt_dispose(&ctx);
free(ctx.duid);
- if (ctx.link_fd != -1) {
- eloop_event_delete(ctx.eloop, ctx.link_fd);
- close(ctx.link_fd);
- }
if_closesockets(&ctx);
free_globals(&ctx);
#ifdef INET6
@@ -2611,28 +2695,38 @@ exit1:
#ifdef PLUGIN_DEV
dev_stop(&ctx);
#endif
-#ifdef PRIVSEP
- eloop_free(ctx.ps_eloop);
-#endif
- eloop_free(ctx.eloop);
if (ctx.script != dhcpcd_default_script)
free(ctx.script);
+#ifdef PRIVSEP
+ if (ps_stopwait(&ctx) != EXIT_SUCCESS)
+ i = EXIT_FAILURE;
+#endif
if (ctx.options & DHCPCD_STARTED && !(ctx.options & DHCPCD_FORKED))
loginfox(PACKAGE " exited");
+#ifdef PRIVSEP
+ if (ps_root_stop(&ctx) == -1)
+ i = EXIT_FAILURE;
+ eloop_free(ctx.ps_eloop);
+#endif
+
+#ifdef USE_SIGNALS
+ /* If still attached, detach from the launcher */
+ if (ctx.options & DHCPCD_STARTED && ctx.fork_fd != -1) {
+ if (write(ctx.fork_fd, &i, sizeof(i)) == -1)
+ logerr("%s: write", __func__);
+ }
+#endif
+
+ eloop_free(ctx.eloop);
logclose();
free(ctx.logfile);
free(ctx.ctl_buf);
#ifdef SETPROCTITLE_H
setproctitle_fini();
#endif
+
#ifdef USE_SIGNALS
- if (ctx.options & DHCPCD_STARTED) {
- /* Try to detach from the launch process. */
- if (ctx.fork_fd != -1 &&
- write(ctx.fork_fd, &i, sizeof(i)) == -1)
- logerr("%s: write", __func__);
- }
- if (ctx.options & DHCPCD_FORKED || oi != 0)
+ if (ctx.options & (DHCPCD_FORKED | DHCPCD_PRIVSEP))
_exit(i); /* so atexit won't remove our pidfile */
#endif
return i;
diff --git a/src/dhcpcd.conf.5.in b/src/dhcpcd.conf.5.in
index 4d8fa38a3505..3af2cc3e7f4a 100644
--- a/src/dhcpcd.conf.5.in
+++ b/src/dhcpcd.conf.5.in
@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.\" Copyright (c) 2006-2021 Roy Marples
+.\" Copyright (c) 2006-2023 Roy Marples
.\" All rights reserved
.\"
.\" Redistribution and use in source and binary forms, with or without
@@ -24,7 +24,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd August 23, 2021
+.Dd May 24, 2024
.Dt DHCPCD.CONF 5
.Os
.Sh NAME
@@ -216,7 +216,7 @@ of the hardware family and the hardware address.
.It Ic duid Op ll | lt | uuid | value
Use a DHCP Unique Identifier.
If a system UUID is available, that will be used to create a DUID-UUID,
-otheriwse if persistent storage is available then a DUID-LLT
+otherwise if persistent storage is available then a DUID-LLT
(link local address + time) is generated,
otherwise DUID-LL is generated (link local address).
The DUID type can be hinted as an optional parameter if the file
@@ -305,6 +305,10 @@ You can use this option to stop this from happening.
.It Ic fallback Ar profile
Fall back to using this profile if DHCP fails.
This allows you to configure a static profile instead of using ZeroConf.
+.It Ic fallback_time Ar seconds
+Start fallback after
+.Ar seconds .
+The default is 5 seconds.
.It Ic hostname Ar name
Sends the hostname
.Ar name
@@ -359,6 +363,7 @@ Otherwise addresses are only assigned for each
.Ar interface
and
.Ar sla_id .
+To avoid delegating to any interface, use - as the invalid interface name.
Each assigned address will have a
.Ar suffix ,
defaulting to 1.
@@ -415,6 +420,7 @@ interface eth0
ia_na 1 # request an IPv6 address
ia_pd 2 eth1/0 # request a PD and assign it to eth1
ia_pd 3 eth2/1 eth3/2 # req a PD and assign it to eth2 and eth3
+ ia_pd 4 - # request a PD but don't assign it
.Ed
.It Ic ipv4only
Only configure IPv4.
@@ -440,6 +446,11 @@ encodes the FQDN hostname as specified in
.It Ic interface Ar interface
Subsequent options are only parsed for this
.Ar interface .
+.It Ic ipv4ll_time Ar seconds
+Wait for
+.Ar seconds
+before starting IPv4LL.
+The default is 5 seconds.
.It Ic ipv6ra_autoconf
Generate SLAAC addresses for each Prefix advertised by an IPv6
Router Advertisement message with the Auto flag set.
@@ -461,7 +472,7 @@ Enables IPv6 Router Advertisement solicitation.
This is on by default, but is documented here in the case where it is disabled
globally but needs to be enabled for one interface.
.It Ic leasetime Ar seconds
-Request a lease time of
+Request DHCP a lease time of
.Ar seconds .
.Ar -1
represents an infinite lease time.
@@ -469,6 +480,8 @@ By default
.Nm dhcpcd
does not request any lease time and leaves it in the hands of the
DHCP server.
+It is not possible to request a DHCPv6 lease time as this is not RFC compliant.
+See RFC 8415 21.4, 21.6, 21.21 and 21.22.
.It Ic link_rcvbuf Ar size
Override the size of the link receive buffer from the kernel default.
While
@@ -506,6 +519,8 @@ adding a new IPv4 address.
.It Ic noarp
Don't send any ARP requests.
This also disables IPv4LL.
+.It Ic arp_persistdefence
+Keep the IP address even if defence fails upon IP Address conflict.
.It Ic noauthrequired
Don't require authentication even though we requested it.
Also allows FORCERENEW and RECONFIGURE messages without authentication.
@@ -643,16 +658,24 @@ Use
.Ar script
instead of the default
.Pa @SCRIPT@ .
+.It Ic request_time Ar seconds
+Request the lease for
+.Ar seconds
+before going back to DISCOVER.
+The default is 180 seconds.
.It Ic ssid Ar ssid
Subsequent options are only parsed for this wireless
.Ar ssid .
-.It Ic slaac Ar hwaddr | Ar private Op Ar temp | Ar temporary
+.It Ic slaac Ic hwaddr | Ic private | Ic token Ar token Op Ic temp | Ic temporary
Selects the interface identifier used for SLAAC generated IPv6 addresses.
If
-.Ar private
+.Ic private
is used, a RFC 7217 address is generated.
+If
+.Ic token Ar token
+is used then the token is combined with the prefix to make the final address.
The
-.Ar temporary
+.Ic temporary
directive will create a temporary address for the prefix as well.
.It Ic static Ar value
Configures a static
@@ -663,6 +686,13 @@ then
.Nm dhcpcd
will not attempt to obtain a lease and will just use the value for the address
with an infinite lease time.
+If you set an empty value this removes all prior static allocations to
+the same value.
+This is useful when using profiles and in the case of
+.Ic ip_address
+it will remove the static allocation.
+Note that setting 0.0.0.0 keeps the static allocation but waits for a 3rdparty
+to configure the address.
If you set
.Ic ip6_address ,
.Nm dhcpcd
@@ -690,7 +720,7 @@ It uses the special
keyword to insert the destination address
into the value.
.D1 interface ppp0
-.D1 static ip_address=
+.D1 static ip_address=0.0.0.0
.D1 destination routers
.It Ic timeout Ar seconds
Time out after
@@ -915,6 +945,10 @@ A fixed value (1) to indicate that the option is present, 0 bytes.
An RFC 3397 encoded string.
.It Ic dname
An RFC 1035 validated string.
+.It Ic uri
+If an array then the first two bytes are the URI length inside the option data.
+Otherwise, the whole option data is the URI.
+As a space is not allowed in the URI encoding, the URIs are space separated.
.It Ic binhex Op : Ic length
Binary data expressed as hexadecimal.
.It Ic embed
@@ -1001,4 +1035,4 @@ Same as
.An Roy Marples Aq Mt roy@marples.name
.Sh BUGS
Please report them to
-.Lk http://roy.marples.name/projects/dhcpcd
+.Lk https://roy.marples.name/projects/dhcpcd
diff --git a/src/dhcpcd.h b/src/dhcpcd.h
index d7fb8164574d..7fee0604768f 100644
--- a/src/dhcpcd.h
+++ b/src/dhcpcd.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -116,10 +116,6 @@ struct passwd;
struct dhcpcd_ctx {
char pidfile[sizeof(PIDFILE) + IF_NAMESIZE + 1];
char vendor[256];
- bool stdin_valid; /* It's possible stdin, stdout and stderr */
- bool stdout_valid; /* could be closed when dhcpcd starts. */
- bool stderr_valid;
- int stderr_fd; /* FD for logging to stderr */
int fork_fd; /* FD for the fork init signal pipe */
const char *cffile;
unsigned long long options;
@@ -198,17 +194,14 @@ struct dhcpcd_ctx {
#ifdef PRIVSEP
struct passwd *ps_user; /* struct passwd for privsep user */
- pid_t ps_root_pid;
- int ps_root_fd; /* Privileged Proxy commands */
+ struct ps_process_head ps_processes; /* List of spawned processes */
+ struct ps_process *ps_root;
+ struct ps_process *ps_inet;
+ struct ps_process *ps_ctl;
+ int ps_data_fd; /* data returned from processes */
int ps_log_fd; /* chroot logging */
- int ps_data_fd; /* Data from root spawned processes */
+ int ps_log_root_fd; /* outside chroot log reader */
struct eloop *ps_eloop; /* eloop for polling root data */
- struct ps_process_head ps_processes; /* List of spawned processes */
- pid_t ps_inet_pid;
- int ps_inet_fd; /* Network Proxy commands and data */
- pid_t ps_control_pid;
- int ps_control_fd; /* Control Proxy - generic listener */
- int ps_control_data_fd; /* Control Proxy - data query */
struct fd_list *ps_control; /* Queue for the above */
struct fd_list *ps_control_client; /* Queue for the above */
#endif
@@ -268,8 +261,11 @@ extern const char *dhcpcd_default_script;
int dhcpcd_ifafwaiting(const struct interface *);
int dhcpcd_afwaiting(const struct dhcpcd_ctx *);
+void dhcpcd_daemonised(struct dhcpcd_ctx *);
void dhcpcd_daemonise(struct dhcpcd_ctx *);
+void dhcpcd_signal_cb(int, void *);
+
void dhcpcd_linkoverflow(struct dhcpcd_ctx *);
int dhcpcd_handleargs(struct dhcpcd_ctx *, struct fd_list *, int, char **);
void dhcpcd_handlecarrier(struct interface *, int, unsigned int);
diff --git a/src/duid.c b/src/duid.c
index be626f510082..39708105bb48 100644
--- a/src/duid.c
+++ b/src/duid.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -54,6 +54,11 @@
#include "duid.h"
#include "logerr.h"
+/*
+ * Machine, system or product UUIDs are not guaranteed unique.
+ * Let's not use them by default.
+ */
+#ifdef USE_MACHINE_UUID
static size_t
duid_machineuuid(char *uuid, size_t uuid_len)
{
@@ -114,6 +119,7 @@ duid_make_uuid(uint8_t *d)
l += hwaddr_aton(d, uuid);
return l;
}
+#endif
size_t
duid_make(void *d, const struct interface *ifp, uint16_t type)
@@ -176,6 +182,7 @@ duid_get(struct dhcpcd_ctx *ctx, const struct interface *ifp)
/* No file? OK, lets make one based the machines UUID */
if (ifp == NULL) {
+#ifdef USE_MACHINE_UUID
if (ctx->duid_type != DUID_DEFAULT &&
ctx->duid_type != DUID_UUID)
len = 0;
@@ -186,6 +193,10 @@ duid_get(struct dhcpcd_ctx *ctx, const struct interface *ifp)
else
ctx->duid = data;
return len;
+#else
+ free(data);
+ return 0;
+#endif
}
/* Regardless of what happens we will create a DUID to use. */
diff --git a/src/duid.h b/src/duid.h
index da3811d94123..add4625ce4e4 100644
--- a/src/duid.h
+++ b/src/duid.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
diff --git a/src/eloop.c b/src/eloop.c
index a6ab43fbaef7..32a90c77eee3 100644
--- a/src/eloop.c
+++ b/src/eloop.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* eloop - portable event based main loop.
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
@@ -26,14 +26,47 @@
* SUCH DAMAGE.
*/
+/* NOTES:
+ * Basically for a small number of fd's (total, not max fd)
+ * of say a few hundred, ppoll(2) performs just fine, if not faster than others.
+ * It also has the smallest memory and binary size footprint.
+ * ppoll(2) is available on all modern OS my software runs on and should be
+ * an up and coming POSIX standard interface.
+ * If ppoll is not available, then pselect(2) can be used instead which has
+ * even smaller memory and binary size footprint.
+ * However, this difference is quite tiny and the ppoll API is superior.
+ * pselect cannot return error conditions such as EOF for example.
+ *
+ * Both epoll(7) and kqueue(2) require an extra fd per process to manage
+ * their respective list of interest AND syscalls to manage it.
+ * So for a small number of fd's, these are more resource intensive,
+ * especially when used with more than one process.
+ *
+ * epoll avoids the resource limit RLIMIT_NOFILE Linux poll stupidly applies.
+ * kqueue avoids the same limit on OpenBSD.
+ * ppoll can still be secured in both by using SEECOMP or pledge.
+ *
+ * kqueue can avoid the signal trick we use here so that we function calls
+ * other than those listed in sigaction(2) in our signal handlers which is
+ * probably more robust than ours at surviving a signal storm.
+ * signalfd(2) is available for Linux which probably works in a similar way
+ * but it's yet another fd to use.
+ *
+ * Taking this all into account, ppoll(2) is the default mechanism used here.
+ */
+
+#if (defined(__unix__) || defined(unix)) && !defined(USG)
+#include <sys/param.h>
+#endif
#include <sys/time.h>
#include <assert.h>
#include <errno.h>
+#include <fcntl.h>
#include <limits.h>
-#include <poll.h>
#include <stdbool.h>
#include <signal.h>
+#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -44,13 +77,44 @@
#include "config.h"
#endif
+/* Prioritise which mechanism we want to use.*/
#if defined(HAVE_PPOLL)
+#undef HAVE_EPOLL
+#undef HAVE_KQUEUE
+#undef HAVE_PSELECT
#elif defined(HAVE_POLLTS)
+#define HAVE_PPOLL
#define ppoll pollts
+#undef HAVE_EPOLL
+#undef HAVE_KQUEUE
+#undef HAVE_PSELECT
+#elif defined(HAVE_KQUEUE)
+#undef HAVE_EPOLL
+#undef HAVE_PSELECT
+#elif defined(HAVE_EPOLL)
+#undef HAVE_KQUEUE
+#undef HAVE_PSELECT
#elif !defined(HAVE_PSELECT)
-#pragma message("Compiling eloop with pselect(2) support.")
-#define HAVE_PSELECT
-#define ppoll eloop_ppoll
+#define HAVE_PPOLL
+#endif
+
+#if defined(HAVE_KQUEUE)
+#include <sys/event.h>
+#if defined(__DragonFly__) || defined(__FreeBSD__)
+#define _kevent(kq, cl, ncl, el, nel, t) \
+ kevent((kq), (cl), (int)(ncl), (el), (int)(nel), (t))
+#else
+#define _kevent kevent
+#endif
+#define NFD 2
+#elif defined(HAVE_EPOLL)
+#include <sys/epoll.h>
+#define NFD 1
+#elif defined(HAVE_PPOLL)
+#include <poll.h>
+#define NFD 1
+#elif defined(HAVE_PSELECT)
+#include <sys/select.h>
#endif
#include "eloop.h"
@@ -66,10 +130,6 @@
#endif
#endif
-#ifdef HAVE_PSELECT
-#include <sys/select.h>
-#endif
-
/* Our structures require TAILQ macros, which really every libc should
* ship as they are useful beyond belief.
* Sadly some libc's don't have sys/queue.h and some that do don't have
@@ -93,6 +153,10 @@
#include <stdio.h>
#endif
+#ifndef __arraycount
+# define __arraycount(__x) (sizeof(__x) / sizeof(__x[0]))
+#endif
+
/*
* Allow a backlog of signals.
* If you use many eloops in the same process, they should all
@@ -117,11 +181,12 @@
struct eloop_event {
TAILQ_ENTRY(eloop_event) next;
int fd;
- void (*read_cb)(void *);
- void *read_cb_arg;
- void (*write_cb)(void *);
- void *write_cb_arg;
+ void (*cb)(void *, unsigned short);
+ void *cb_arg;
+ unsigned short events;
+#ifdef HAVE_PPOLL
struct pollfd *pollfd;
+#endif
};
struct eloop_timeout {
@@ -137,22 +202,34 @@ struct eloop {
TAILQ_HEAD (event_head, eloop_event) events;
size_t nevents;
struct event_head free_events;
- bool events_need_setup;
struct timespec now;
TAILQ_HEAD (timeout_head, eloop_timeout) timeouts;
struct timeout_head free_timeouts;
const int *signals;
- size_t signals_len;
+ size_t nsignals;
void (*signal_cb)(int, void *);
void *signal_cb_ctx;
+#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL)
+ int fd;
+#endif
+#if defined(HAVE_KQUEUE)
+ struct kevent *fds;
+#elif defined(HAVE_EPOLL)
+ struct epoll_event *fds;
+#elif defined(HAVE_PPOLL)
struct pollfd *fds;
+#endif
+#if !defined(HAVE_PSELECT)
size_t nfds;
+#endif
- int exitnow;
int exitcode;
+ bool exitnow;
+ bool events_need_setup;
+ bool cleared;
};
#ifdef HAVE_REALLOCARRAY
@@ -173,142 +250,57 @@ eloop_realloca(void *ptr, size_t n, size_t size)
}
#endif
-#ifdef HAVE_PSELECT
-/* Wrapper around pselect, to imitate the ppoll call. */
-static int
-eloop_ppoll(struct pollfd * fds, nfds_t nfds,
- const struct timespec *ts, const sigset_t *sigmask)
-{
- fd_set read_fds, write_fds;
- nfds_t n;
- int maxfd, r;
- FD_ZERO(&read_fds);
- FD_ZERO(&write_fds);
- maxfd = 0;
- for (n = 0; n < nfds; n++) {
- if (fds[n].events & POLLIN) {
- FD_SET(fds[n].fd, &read_fds);
- if (fds[n].fd > maxfd)
- maxfd = fds[n].fd;
- }
- if (fds[n].events & POLLOUT) {
- FD_SET(fds[n].fd, &write_fds);
- if (fds[n].fd > maxfd)
- maxfd = fds[n].fd;
- }
- }
-
- r = pselect(maxfd + 1, &read_fds, &write_fds, NULL, ts, sigmask);
- if (r > 0) {
- for (n = 0; n < nfds; n++) {
- fds[n].revents =
- FD_ISSET(fds[n].fd, &read_fds) ? POLLIN : 0;
- if (FD_ISSET(fds[n].fd, &write_fds))
- fds[n].revents |= POLLOUT;
- }
- }
-
- return r;
-}
-#endif
-
-unsigned long long
-eloop_timespec_diff(const struct timespec *tsp, const struct timespec *usp,
- unsigned int *nsp)
-{
- unsigned long long tsecs, usecs, secs;
- long nsecs;
-
- if (tsp->tv_sec < 0) /* time wreapped */
- tsecs = UTIME_MAX - (unsigned long long)(-tsp->tv_sec);
- else
- tsecs = (unsigned long long)tsp->tv_sec;
- if (usp->tv_sec < 0) /* time wrapped */
- usecs = UTIME_MAX - (unsigned long long)(-usp->tv_sec);
- else
- usecs = (unsigned long long)usp->tv_sec;
-
- if (usecs > tsecs) /* time wrapped */
- secs = (UTIME_MAX - usecs) + tsecs;
- else
- secs = tsecs - usecs;
-
- nsecs = tsp->tv_nsec - usp->tv_nsec;
- if (nsecs < 0) {
- if (secs == 0)
- nsecs = 0;
- else {
- secs--;
- nsecs += NSEC_PER_SEC;
- }
- }
- if (nsp != NULL)
- *nsp = (unsigned int)nsecs;
- return secs;
-}
-
-static void
-eloop_reduce_timers(struct eloop *eloop)
-{
- struct timespec now;
- unsigned long long secs;
- unsigned int nsecs;
- struct eloop_timeout *t;
-
- clock_gettime(CLOCK_MONOTONIC, &now);
- secs = eloop_timespec_diff(&now, &eloop->now, &nsecs);
-
- TAILQ_FOREACH(t, &eloop->timeouts, next) {
- if (secs > t->seconds) {
- t->seconds = 0;
- t->nseconds = 0;
- } else {
- t->seconds -= (unsigned int)secs;
- if (nsecs > t->nseconds) {
- if (t->seconds == 0)
- t->nseconds = 0;
- else {
- t->seconds--;
- t->nseconds = NSEC_PER_SEC
- - (nsecs - t->nseconds);
- }
- } else
- t->nseconds -= nsecs;
- }
- }
-
- eloop->now = now;
-}
-
-static void
+static int
eloop_event_setup_fds(struct eloop *eloop)
{
struct eloop_event *e, *ne;
+#if defined(HAVE_KQUEUE)
+ struct kevent *pfd;
+ size_t nfds = eloop->nsignals;
+#elif defined(HAVE_EPOLL)
+ struct epoll_event *pfd;
+ size_t nfds = 0;
+#elif defined(HAVE_PPOLL)
struct pollfd *pfd;
+ size_t nfds = 0;
+#endif
+#ifndef HAVE_PSELECT
+ nfds += eloop->nevents * NFD;
+ if (eloop->nfds < nfds) {
+ pfd = eloop_realloca(eloop->fds, nfds, sizeof(*pfd));
+ if (pfd == NULL)
+ return -1;
+ eloop->fds = pfd;
+ eloop->nfds = nfds;
+ }
+#endif
+
+#ifdef HAVE_PPOLL
pfd = eloop->fds;
+#endif
TAILQ_FOREACH_SAFE(e, &eloop->events, next, ne) {
if (e->fd == -1) {
TAILQ_REMOVE(&eloop->events, e, next);
TAILQ_INSERT_TAIL(&eloop->free_events, e, next);
continue;
}
-#ifdef ELOOP_DEBUG
- fprintf(stderr, "%s(%d) fd=%d, rcb=%p, wcb=%p\n",
- __func__, getpid(), e->fd, e->read_cb, e->write_cb);
-#endif
+#ifdef HAVE_PPOLL
e->pollfd = pfd;
pfd->fd = e->fd;
pfd->events = 0;
- if (e->read_cb != NULL)
+ if (e->events & ELE_READ)
pfd->events |= POLLIN;
- if (e->write_cb != NULL)
+ if (e->events & ELE_WRITE)
pfd->events |= POLLOUT;
pfd->revents = 0;
pfd++;
+#endif
}
+
eloop->events_need_setup = false;
+ return 0;
}
size_t
@@ -319,16 +311,22 @@ eloop_event_count(const struct eloop *eloop)
}
int
-eloop_event_add_rw(struct eloop *eloop, int fd,
- void (*read_cb)(void *), void *read_cb_arg,
- void (*write_cb)(void *), void *write_cb_arg)
+eloop_event_add(struct eloop *eloop, int fd, unsigned short events,
+ void (*cb)(void *, unsigned short), void *cb_arg)
{
struct eloop_event *e;
- struct pollfd *pfd;
+ bool added;
+#if defined(HAVE_KQUEUE)
+ struct kevent ke[2], *kep = &ke[0];
+ size_t n;
+#elif defined(HAVE_EPOLL)
+ struct epoll_event epe;
+ int op;
+#endif
assert(eloop != NULL);
- assert(read_cb != NULL || write_cb != NULL);
- if (fd == -1) {
+ assert(cb != NULL && cb_arg != NULL);
+ if (fd == -1 || !(events & (ELE_READ | ELE_WRITE | ELE_HANGUP))) {
errno = EINVAL;
return -1;
}
@@ -339,68 +337,88 @@ eloop_event_add_rw(struct eloop *eloop, int fd,
}
if (e == NULL) {
- if (eloop->nevents + 1 > eloop->nfds) {
- pfd = eloop_realloca(eloop->fds, eloop->nevents + 1,
- sizeof(*pfd));
- if (pfd == NULL)
- return -1;
- eloop->fds = pfd;
- eloop->nfds++;
- }
-
+ added = true;
e = TAILQ_FIRST(&eloop->free_events);
if (e != NULL)
TAILQ_REMOVE(&eloop->free_events, e, next);
else {
e = malloc(sizeof(*e));
- if (e == NULL)
+ if (e == NULL) {
return -1;
+ }
}
TAILQ_INSERT_HEAD(&eloop->events, e, next);
eloop->nevents++;
e->fd = fd;
- e->read_cb = read_cb;
- e->read_cb_arg = read_cb_arg;
- e->write_cb = write_cb;
- e->write_cb_arg = write_cb_arg;
- goto setup;
- }
-
- if (read_cb) {
- e->read_cb = read_cb;
- e->read_cb_arg = read_cb_arg;
+ e->events = 0;
+ } else
+ added = false;
+
+ e->cb = cb;
+ e->cb_arg = cb_arg;
+
+#if defined(HAVE_KQUEUE)
+ n = 2;
+ if (events & ELE_READ && !(e->events & ELE_READ))
+ EV_SET(kep++, (uintptr_t)fd, EVFILT_READ, EV_ADD, 0, 0, e);
+ else if (!(events & ELE_READ) && e->events & ELE_READ)
+ EV_SET(kep++, (uintptr_t)fd, EVFILT_READ, EV_DELETE, 0, 0, e);
+ else
+ n--;
+ if (events & ELE_WRITE && !(e->events & ELE_WRITE))
+ EV_SET(kep++, (uintptr_t)fd, EVFILT_WRITE, EV_ADD, 0, 0, e);
+ else if (!(events & ELE_WRITE) && e->events & ELE_WRITE)
+ EV_SET(kep++, (uintptr_t)fd, EVFILT_WRITE, EV_DELETE, 0, 0, e);
+ else
+ n--;
+#ifdef EVFILT_PROCDESC
+ if (events & ELE_HANGUP)
+ EV_SET(kep++, (uintptr_t)fd, EVFILT_PROCDESC, EV_ADD,
+ NOTE_EXIT, 0, e);
+ else
+ n--;
+#endif
+ if (n != 0 && _kevent(eloop->fd, ke, n, NULL, 0, NULL) == -1) {
+ if (added) {
+ TAILQ_REMOVE(&eloop->events, e, next);
+ TAILQ_INSERT_TAIL(&eloop->free_events, e, next);
+ }
+ return -1;
}
- if (write_cb) {
- e->write_cb = write_cb;
- e->write_cb_arg = write_cb_arg;
+#elif defined(HAVE_EPOLL)
+ memset(&epe, 0, sizeof(epe));
+ epe.data.ptr = e;
+ if (events & ELE_READ)
+ epe.events |= EPOLLIN;
+ if (events & ELE_WRITE)
+ epe.events |= EPOLLOUT;
+ op = added ? EPOLL_CTL_ADD : EPOLL_CTL_MOD;
+ if (epe.events != 0 && epoll_ctl(eloop->fd, op, fd, &epe) == -1) {
+ if (added) {
+ TAILQ_REMOVE(&eloop->events, e, next);
+ TAILQ_INSERT_TAIL(&eloop->free_events, e, next);
+ }
+ return -1;
}
-
-setup:
+#elif defined(HAVE_PPOLL)
e->pollfd = NULL;
+ UNUSED(added);
+#else
+ UNUSED(added);
+#endif
+ e->events = events;
eloop->events_need_setup = true;
return 0;
}
int
-eloop_event_add(struct eloop *eloop, int fd,
- void (*read_cb)(void *), void *read_cb_arg)
-{
-
- return eloop_event_add_rw(eloop, fd, read_cb, read_cb_arg, NULL, NULL);
-}
-
-int
-eloop_event_add_w(struct eloop *eloop, int fd,
- void (*write_cb)(void *), void *write_cb_arg)
-{
-
- return eloop_event_add_rw(eloop, fd, NULL,NULL, write_cb, write_cb_arg);
-}
-
-int
-eloop_event_delete_write(struct eloop *eloop, int fd, int write_only)
+eloop_event_delete(struct eloop *eloop, int fd)
{
struct eloop_event *e;
+#if defined(HAVE_KQUEUE)
+ struct kevent ke[2], *kep = &ke[0];
+ size_t n;
+#endif
assert(eloop != NULL);
if (fd == -1) {
@@ -417,25 +435,96 @@ eloop_event_delete_write(struct eloop *eloop, int fd, int write_only)
return -1;
}
- if (write_only) {
- if (e->read_cb == NULL)
- goto remove;
- e->write_cb = NULL;
- e->write_cb_arg = NULL;
- if (e->pollfd != NULL) {
- e->pollfd->events &= ~POLLOUT;
- e->pollfd->revents &= ~POLLOUT;
- }
- return 1;
+#if defined(HAVE_KQUEUE)
+ n = 0;
+ if (e->events & ELE_READ) {
+ EV_SET(kep++, (uintptr_t)fd, EVFILT_READ, EV_DELETE, 0, 0, e);
+ n++;
}
-
-remove:
+ if (e->events & ELE_WRITE) {
+ EV_SET(kep++, (uintptr_t)fd, EVFILT_WRITE, EV_DELETE, 0, 0, e);
+ n++;
+ }
+ if (n != 0 && _kevent(eloop->fd, ke, n, NULL, 0, NULL) == -1)
+ return -1;
+#elif defined(HAVE_EPOLL)
+ if (epoll_ctl(eloop->fd, EPOLL_CTL_DEL, fd, NULL) == -1)
+ return -1;
+#endif
e->fd = -1;
eloop->nevents--;
eloop->events_need_setup = true;
return 1;
}
+unsigned long long
+eloop_timespec_diff(const struct timespec *tsp, const struct timespec *usp,
+ unsigned int *nsp)
+{
+ unsigned long long tsecs, usecs, secs;
+ long nsecs;
+
+ if (tsp->tv_sec < 0) /* time wreapped */
+ tsecs = UTIME_MAX - (unsigned long long)(-tsp->tv_sec);
+ else
+ tsecs = (unsigned long long)tsp->tv_sec;
+ if (usp->tv_sec < 0) /* time wrapped */
+ usecs = UTIME_MAX - (unsigned long long)(-usp->tv_sec);
+ else
+ usecs = (unsigned long long)usp->tv_sec;
+
+ if (usecs > tsecs) /* time wrapped */
+ secs = (UTIME_MAX - usecs) + tsecs;
+ else
+ secs = tsecs - usecs;
+
+ nsecs = tsp->tv_nsec - usp->tv_nsec;
+ if (nsecs < 0) {
+ if (secs == 0)
+ nsecs = 0;
+ else {
+ secs--;
+ nsecs += NSEC_PER_SEC;
+ }
+ }
+ if (nsp != NULL)
+ *nsp = (unsigned int)nsecs;
+ return secs;
+}
+
+static void
+eloop_reduce_timers(struct eloop *eloop)
+{
+ struct timespec now;
+ unsigned long long secs;
+ unsigned int nsecs;
+ struct eloop_timeout *t;
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ secs = eloop_timespec_diff(&now, &eloop->now, &nsecs);
+
+ TAILQ_FOREACH(t, &eloop->timeouts, next) {
+ if (secs > t->seconds) {
+ t->seconds = 0;
+ t->nseconds = 0;
+ } else {
+ t->seconds -= (unsigned int)secs;
+ if (nsecs > t->nseconds) {
+ if (t->seconds == 0)
+ t->nseconds = 0;
+ else {
+ t->seconds--;
+ t->nseconds = NSEC_PER_SEC
+ - (nsecs - t->nseconds);
+ }
+ } else
+ t->nseconds -= nsecs;
+ }
+ }
+
+ eloop->now = now;
+}
+
/*
* This implementation should cope with UINT_MAX seconds on a system
* where time_t is INT32_MAX. It should also cope with the monotonic timer
@@ -567,30 +656,173 @@ eloop_exit(struct eloop *eloop, int code)
assert(eloop != NULL);
eloop->exitcode = code;
- eloop->exitnow = 1;
+ eloop->exitnow = true;
}
void
eloop_enter(struct eloop *eloop)
{
- eloop->exitnow = 0;
+ assert(eloop != NULL);
+
+ eloop->exitnow = false;
}
-void
+/* Must be called after fork(2) */
+int
+eloop_forked(struct eloop *eloop)
+{
+#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL)
+ struct eloop_event *e;
+#if defined(HAVE_KQUEUE)
+ struct kevent *pfds, *pfd;
+ size_t i;
+#elif defined(HAVE_EPOLL)
+ struct epoll_event epe = { .events = 0 };
+#endif
+
+ assert(eloop != NULL);
+#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL)
+ if (eloop->fd != -1)
+ close(eloop->fd);
+ if (eloop_open(eloop) == -1)
+ return -1;
+#endif
+
+#ifdef HAVE_KQUEUE
+ pfds = malloc((eloop->nsignals + (eloop->nevents * NFD)) * sizeof(*pfds));
+ pfd = pfds;
+
+ if (eloop->signal_cb != NULL) {
+ for (i = 0; i < eloop->nsignals; i++) {
+ EV_SET(pfd++, (uintptr_t)eloop->signals[i],
+ EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
+ }
+ } else
+ i = 0;
+#endif
+
+ TAILQ_FOREACH(e, &eloop->events, next) {
+ if (e->fd == -1)
+ continue;
+#if defined(HAVE_KQUEUE)
+ if (e->events & ELE_READ) {
+ EV_SET(pfd++, (uintptr_t)e->fd,
+ EVFILT_READ, EV_ADD, 0, 0, e);
+ i++;
+ }
+ if (e->events & ELE_WRITE) {
+ EV_SET(pfd++, (uintptr_t)e->fd,
+ EVFILT_WRITE, EV_ADD, 0, 0, e);
+ i++;
+ }
+#elif defined(HAVE_EPOLL)
+ memset(&epe, 0, sizeof(epe));
+ epe.data.ptr = e;
+ if (e->events & ELE_READ)
+ epe.events |= EPOLLIN;
+ if (e->events & ELE_WRITE)
+ epe.events |= EPOLLOUT;
+ if (epoll_ctl(eloop->fd, EPOLL_CTL_ADD, e->fd, &epe) == -1)
+ return -1;
+#endif
+ }
+
+#if defined(HAVE_KQUEUE)
+ if (i == 0)
+ return 0;
+ return _kevent(eloop->fd, pfds, i, NULL, 0, NULL);
+#else
+ return 0;
+#endif
+#else
+ UNUSED(eloop);
+ return 0;
+#endif
+}
+
+int
+eloop_open(struct eloop *eloop)
+{
+#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL)
+ int fd;
+
+ assert(eloop != NULL);
+#if defined(HAVE_KQUEUE1)
+ fd = kqueue1(O_CLOEXEC);
+#elif defined(HAVE_KQUEUE)
+ int flags;
+
+ fd = kqueue();
+ flags = fcntl(fd, F_GETFD, 0);
+ if (!(flags != -1 && !(flags & FD_CLOEXEC) &&
+ fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0))
+ {
+ close(fd);
+ return -1;
+ }
+#elif defined(HAVE_EPOLL)
+ fd = epoll_create1(EPOLL_CLOEXEC);
+#endif
+
+ eloop->fd = fd;
+ return fd;
+#else
+ UNUSED(eloop);
+ return 0;
+#endif
+}
+
+int
eloop_signal_set_cb(struct eloop *eloop,
- const int *signals, size_t signals_len,
+ const int *signals, size_t nsignals,
void (*signal_cb)(int, void *), void *signal_cb_ctx)
{
+#ifdef HAVE_KQUEUE
+ size_t i;
+ struct kevent *ke, *kes;
+#endif
+ int error = 0;
assert(eloop != NULL);
+#ifdef HAVE_KQUEUE
+ ke = kes = malloc(MAX(eloop->nsignals, nsignals) * sizeof(*kes));
+ if (kes == NULL)
+ return -1;
+ for (i = 0; i < eloop->nsignals; i++) {
+ EV_SET(ke++, (uintptr_t)eloop->signals[i],
+ EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL);
+ }
+ if (i != 0 && _kevent(eloop->fd, kes, i, NULL, 0, NULL) == -1) {
+ error = -1;
+ goto out;
+ }
+#endif
+
eloop->signals = signals;
- eloop->signals_len = signals_len;
+ eloop->nsignals = nsignals;
eloop->signal_cb = signal_cb;
eloop->signal_cb_ctx = signal_cb_ctx;
+
+#ifdef HAVE_KQUEUE
+ if (signal_cb == NULL)
+ goto out;
+ ke = kes;
+ for (i = 0; i < eloop->nsignals; i++) {
+ EV_SET(ke++, (uintptr_t)eloop->signals[i],
+ EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
+ }
+ if (i != 0 && _kevent(eloop->fd, kes, i, NULL, 0, NULL) == -1)
+ error = -1;
+out:
+ free(kes);
+#endif
+
+ return error;
}
+#ifndef HAVE_KQUEUE
static volatile int _eloop_sig[ELOOP_NSIGNALS];
static volatile size_t _eloop_nsig;
@@ -608,31 +840,37 @@ eloop_signal3(int sig, __unused siginfo_t *siginfo, __unused void *arg)
_eloop_sig[_eloop_nsig++] = sig;
}
+#endif
int
eloop_signal_mask(struct eloop *eloop, sigset_t *oldset)
{
sigset_t newset;
size_t i;
+#ifndef HAVE_KQUEUE
struct sigaction sa = {
.sa_sigaction = eloop_signal3,
.sa_flags = SA_SIGINFO,
};
+#endif
assert(eloop != NULL);
sigemptyset(&newset);
- for (i = 0; i < eloop->signals_len; i++)
+ for (i = 0; i < eloop->nsignals; i++)
sigaddset(&newset, eloop->signals[i]);
if (sigprocmask(SIG_SETMASK, &newset, oldset) == -1)
return -1;
+#ifndef HAVE_KQUEUE
sigemptyset(&sa.sa_mask);
- for (i = 0; i < eloop->signals_len; i++) {
+ for (i = 0; i < eloop->nsignals; i++) {
if (sigaction(eloop->signals[i], &sa, NULL) == -1)
return -1;
}
+#endif
+
return 0;
}
@@ -657,26 +895,55 @@ eloop_new(void)
TAILQ_INIT(&eloop->free_timeouts);
eloop->exitcode = EXIT_FAILURE;
+#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL)
+ if (eloop_open(eloop) == -1) {
+ eloop_free(eloop);
+ return NULL;
+ }
+#endif
+
return eloop;
}
void
-eloop_clear(struct eloop *eloop)
+eloop_clear(struct eloop *eloop, ...)
{
- struct eloop_event *e;
+ va_list va1, va2;
+ int except_fd;
+ struct eloop_event *e, *ne;
struct eloop_timeout *t;
if (eloop == NULL)
return;
- eloop->nevents = 0;
- eloop->signals = NULL;
- eloop->signals_len = 0;
-
- while ((e = TAILQ_FIRST(&eloop->events))) {
+ va_start(va1, eloop);
+ TAILQ_FOREACH_SAFE(e, &eloop->events, next, ne) {
+ va_copy(va2, va1);
+ do
+ except_fd = va_arg(va2, int);
+ while (except_fd != -1 && except_fd != e->fd);
+ va_end(va2);
+ if (e->fd == except_fd && e->fd != -1)
+ continue;
TAILQ_REMOVE(&eloop->events, e, next);
+ if (e->fd != -1) {
+ close(e->fd);
+ eloop->nevents--;
+ }
free(e);
}
+ va_end(va1);
+
+#if !defined(HAVE_PSELECT)
+ /* Free the pollfd buffer and ensure it's re-created before
+ * the next run. This allows us to shrink it incase we use a lot less
+ * signals and fds to respond to after forking. */
+ free(eloop->fds);
+ eloop->fds = NULL;
+ eloop->nfds = 0;
+ eloop->events_need_setup = true;
+#endif
+
while ((e = TAILQ_FIRST(&eloop->free_events))) {
TAILQ_REMOVE(&eloop->free_events, e, next);
free(e);
@@ -689,40 +956,238 @@ eloop_clear(struct eloop *eloop)
TAILQ_REMOVE(&eloop->free_timeouts, t, next);
free(t);
}
-
- free(eloop->fds);
- eloop->fds = NULL;
- eloop->nfds = 0;
+ eloop->cleared = true;
}
void
eloop_free(struct eloop *eloop)
{
- eloop_clear(eloop);
+ eloop_clear(eloop, -1);
+#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL)
+ if (eloop != NULL && eloop->fd != -1)
+ close(eloop->fd);
+#endif
free(eloop);
}
+#if defined(HAVE_KQUEUE)
+static int
+eloop_run_kqueue(struct eloop *eloop, const struct timespec *ts)
+{
+ int n, nn;
+ struct kevent *ke;
+ struct eloop_event *e;
+ unsigned short events;
+
+ n = _kevent(eloop->fd, NULL, 0, eloop->fds, eloop->nevents, ts);
+ if (n == -1)
+ return -1;
+
+ for (nn = n, ke = eloop->fds; nn != 0; nn--, ke++) {
+ if (eloop->cleared || eloop->exitnow)
+ break;
+ e = (struct eloop_event *)ke->udata;
+ if (ke->filter == EVFILT_SIGNAL) {
+ eloop->signal_cb((int)ke->ident,
+ eloop->signal_cb_ctx);
+ continue;
+ }
+ if (ke->filter == EVFILT_READ)
+ events = ELE_READ;
+ else if (ke->filter == EVFILT_WRITE)
+ events = ELE_WRITE;
+#ifdef EVFILT_PROCDESC
+ else if (ke->filter == EVFILT_PROCDESC &&
+ ke->fflags & NOTE_EXIT)
+ /* exit status is in ke->data.
+ * As we default to using ppoll anyway
+ * we don't have to do anything with it right now. */
+ events = ELE_HANGUP;
+#endif
+ else
+ continue; /* assert? */
+ if (ke->flags & EV_EOF)
+ events |= ELE_HANGUP;
+ if (ke->flags & EV_ERROR)
+ events |= ELE_ERROR;
+ e->cb(e->cb_arg, events);
+ }
+ return n;
+}
+
+#elif defined(HAVE_EPOLL)
+
+static int
+eloop_run_epoll(struct eloop *eloop,
+ const struct timespec *ts, const sigset_t *signals)
+{
+ int timeout, n, nn;
+ struct epoll_event *epe;
+ struct eloop_event *e;
+ unsigned short events;
+
+ if (ts != NULL) {
+ if (ts->tv_sec > INT_MAX / 1000 ||
+ (ts->tv_sec == INT_MAX / 1000 &&
+ ((ts->tv_nsec + 999999) / 1000000 > INT_MAX % 1000000)))
+ timeout = INT_MAX;
+ else
+ timeout = (int)(ts->tv_sec * 1000 +
+ (ts->tv_nsec + 999999) / 1000000);
+ } else
+ timeout = -1;
+
+ if (signals != NULL)
+ n = epoll_pwait(eloop->fd, eloop->fds,
+ (int)eloop->nevents, timeout, signals);
+ else
+ n = epoll_wait(eloop->fd, eloop->fds,
+ (int)eloop->nevents, timeout);
+ if (n == -1)
+ return -1;
+
+ for (nn = n, epe = eloop->fds; nn != 0; nn--, epe++) {
+ if (eloop->cleared || eloop->exitnow)
+ break;
+ e = (struct eloop_event *)epe->data.ptr;
+ if (e->fd == -1)
+ continue;
+ events = 0;
+ if (epe->events & EPOLLIN)
+ events |= ELE_READ;
+ if (epe->events & EPOLLOUT)
+ events |= ELE_WRITE;
+ if (epe->events & EPOLLHUP)
+ events |= ELE_HANGUP;
+ if (epe->events & EPOLLERR)
+ events |= ELE_ERROR;
+ e->cb(e->cb_arg, events);
+ }
+ return n;
+}
+
+#elif defined(HAVE_PPOLL)
+
+static int
+eloop_run_ppoll(struct eloop *eloop,
+ const struct timespec *ts, const sigset_t *signals)
+{
+ int n, nn;
+ struct eloop_event *e;
+ struct pollfd *pfd;
+ unsigned short events;
+
+ n = ppoll(eloop->fds, (nfds_t)eloop->nevents, ts, signals);
+ if (n == -1 || n == 0)
+ return n;
+
+ nn = n;
+ TAILQ_FOREACH(e, &eloop->events, next) {
+ if (eloop->cleared || eloop->exitnow)
+ break;
+ /* Skip freshly added events */
+ if ((pfd = e->pollfd) == NULL)
+ continue;
+ if (e->pollfd->revents) {
+ nn--;
+ events = 0;
+ if (pfd->revents & POLLIN)
+ events |= ELE_READ;
+ if (pfd->revents & POLLOUT)
+ events |= ELE_WRITE;
+ if (pfd->revents & POLLHUP)
+ events |= ELE_HANGUP;
+ if (pfd->revents & POLLERR)
+ events |= ELE_ERROR;
+ if (pfd->revents & POLLNVAL)
+ events |= ELE_NVAL;
+ if (events)
+ e->cb(e->cb_arg, events);
+ }
+ if (nn == 0)
+ break;
+ }
+ return n;
+}
+
+#elif defined(HAVE_PSELECT)
+
+static int
+eloop_run_pselect(struct eloop *eloop,
+ const struct timespec *ts, const sigset_t *sigmask)
+{
+ fd_set read_fds, write_fds;
+ int maxfd, n;
+ struct eloop_event *e;
+ unsigned short events;
+
+ FD_ZERO(&read_fds);
+ FD_ZERO(&write_fds);
+ maxfd = 0;
+ TAILQ_FOREACH(e, &eloop->events, next) {
+ if (e->fd == -1)
+ continue;
+ if (e->events & ELE_READ) {
+ FD_SET(e->fd, &read_fds);
+ if (e->fd > maxfd)
+ maxfd = e->fd;
+ }
+ if (e->events & ELE_WRITE) {
+ FD_SET(e->fd, &write_fds);
+ if (e->fd > maxfd)
+ maxfd = e->fd;
+ }
+ }
+
+ /* except_fd's is for STREAMS devices which we don't use. */
+ n = pselect(maxfd + 1, &read_fds, &write_fds, NULL, ts, sigmask);
+ if (n == -1 || n == 0)
+ return n;
+
+ TAILQ_FOREACH(e, &eloop->events, next) {
+ if (eloop->cleared || eloop->exitnow)
+ break;
+ if (e->fd == -1)
+ continue;
+ events = 0;
+ if (FD_ISSET(e->fd, &read_fds))
+ events |= ELE_READ;
+ if (FD_ISSET(e->fd, &write_fds))
+ events |= ELE_WRITE;
+ if (events)
+ e->cb(e->cb_arg, events);
+ }
+
+ return n;
+}
+#endif
+
int
eloop_start(struct eloop *eloop, sigset_t *signals)
{
- int n;
- struct eloop_event *e;
+ int error;
struct eloop_timeout *t;
struct timespec ts, *tsp;
assert(eloop != NULL);
+#ifdef HAVE_KQUEUE
+ UNUSED(signals);
+#endif
for (;;) {
if (eloop->exitnow)
break;
+#ifndef HAVE_KQUEUE
if (_eloop_nsig != 0) {
- n = _eloop_sig[--_eloop_nsig];
+ int n = _eloop_sig[--_eloop_nsig];
+
if (eloop->signal_cb != NULL)
eloop->signal_cb(n, eloop->signal_cb_ctx);
continue;
}
+#endif
t = TAILQ_FIRST(&eloop->timeouts);
if (t == NULL && eloop->nevents == 0)
@@ -750,37 +1215,27 @@ eloop_start(struct eloop *eloop, sigset_t *signals)
} else
tsp = NULL;
+ eloop->cleared = false;
if (eloop->events_need_setup)
eloop_event_setup_fds(eloop);
- n = ppoll(eloop->fds, (nfds_t)eloop->nevents, tsp, signals);
- if (n == -1) {
+#if defined(HAVE_KQUEUE)
+ UNUSED(signals);
+ error = eloop_run_kqueue(eloop, tsp);
+#elif defined(HAVE_EPOLL)
+ error = eloop_run_epoll(eloop, tsp, signals);
+#elif defined(HAVE_PPOLL)
+ error = eloop_run_ppoll(eloop, tsp, signals);
+#elif defined(HAVE_PSELECT)
+ error = eloop_run_pselect(eloop, tsp, signals);
+#else
+#error no polling mechanism to run!
+#endif
+ if (error == -1) {
if (errno == EINTR)
continue;
return -errno;
}
- if (n == 0)
- continue;
-
- TAILQ_FOREACH(e, &eloop->events, next) {
- /* Skip freshly added events */
- if (e->pollfd == NULL)
- continue;
- if (e->pollfd->revents)
- n--;
- if (e->fd != -1 && e->pollfd->revents & POLLOUT) {
- if (e->write_cb != NULL)
- e->write_cb(e->write_cb_arg);
- }
- if (e->fd != -1 &&
- e->pollfd != NULL && e->pollfd->revents)
- {
- if (e->read_cb != NULL)
- e->read_cb(e->read_cb_arg);
- }
- if (n == 0)
- break;
- }
}
return eloop->exitcode;
diff --git a/src/eloop.h b/src/eloop.h
index 1813eaa64d24..05851dba8d60 100644
--- a/src/eloop.h
+++ b/src/eloop.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -52,22 +52,19 @@
/* Forward declare eloop - the content should be invisible to the outside */
struct eloop;
-unsigned long long eloop_timespec_diff(const struct timespec *tsp,
- const struct timespec *usp, unsigned int *nsp);
+#define ELE_READ 0x0001
+#define ELE_WRITE 0x0002
+#define ELE_ERROR 0x0100
+#define ELE_HANGUP 0x0200
+#define ELE_NVAL 0x0400
+
size_t eloop_event_count(const struct eloop *);
-int eloop_event_add_rw(struct eloop *, int,
- void (*)(void *), void *,
- void (*)(void *), void *);
-int eloop_event_add(struct eloop *, int,
- void (*)(void *), void *);
-int eloop_event_add_w(struct eloop *, int,
- void (*)(void *), void *);
-#define eloop_event_delete(eloop, fd) \
- eloop_event_delete_write((eloop), (fd), 0)
-#define eloop_event_remove_writecb(eloop, fd) \
- eloop_event_delete_write((eloop), (fd), 1)
-int eloop_event_delete_write(struct eloop *, int, int);
+int eloop_event_add(struct eloop *, int, unsigned short,
+ void (*)(void *, unsigned short), void *);
+int eloop_event_delete(struct eloop *, int);
+unsigned long long eloop_timespec_diff(const struct timespec *tsp,
+ const struct timespec *usp, unsigned int *nsp);
#define eloop_timeout_add_tv(eloop, tv, cb, ctx) \
eloop_q_timeout_add_tv((eloop), ELOOP_QUEUE, (tv), (cb), (ctx))
#define eloop_timeout_add_sec(eloop, tv, cb, ctx) \
@@ -84,15 +81,17 @@ int eloop_q_timeout_add_msec(struct eloop *, int,
unsigned long, void (*)(void *), void *);
int eloop_q_timeout_delete(struct eloop *, int, void (*)(void *), void *);
-void eloop_signal_set_cb(struct eloop *, const int *, size_t,
+int eloop_signal_set_cb(struct eloop *, const int *, size_t,
void (*)(int, void *), void *);
int eloop_signal_mask(struct eloop *, sigset_t *oldset);
struct eloop * eloop_new(void);
-void eloop_clear(struct eloop *);
+void eloop_clear(struct eloop *, ...);
void eloop_free(struct eloop *);
void eloop_exit(struct eloop *, int);
void eloop_enter(struct eloop *);
+int eloop_forked(struct eloop *);
+int eloop_open(struct eloop *);
int eloop_start(struct eloop *, sigset_t *);
#endif
diff --git a/src/if-bsd.c b/src/if-bsd.c
index e5ffe500655f..9ebe36a88fd3 100644
--- a/src/if-bsd.c
+++ b/src/if-bsd.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* BSD interface driver for dhcpcd
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -104,6 +104,7 @@
* just won't work without explicit configuration. */
static const char * const ifnames_ignore[] = {
"bridge",
+ "epair", /* Virtual patch cable */
"fwe", /* Firewire */
"fwip", /* Firewire */
"tap",
@@ -112,10 +113,6 @@ static const char * const ifnames_ignore[] = {
NULL
};
-struct priv {
- int pf_inet6_fd;
-};
-
struct rtm
{
struct rt_msghdr hdr;
@@ -157,6 +154,9 @@ if_opensockets_os(struct dhcpcd_ctx *ctx)
#ifdef RTM_CHGADDR
RTM_CHGADDR,
#endif
+#ifdef RTM_DESYNC
+ RTM_DESYNC,
+#endif
RTM_NEWADDR, RTM_DELADDR
};
#ifdef ROUTE_MSGFILTER
@@ -170,15 +170,13 @@ if_opensockets_os(struct dhcpcd_ctx *ctx)
#ifdef INET6
priv->pf_inet6_fd = xsocket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
-#ifdef PRIVSEP_RIGHTS
- if (IN_PRIVSEP(ctx))
- ps_rights_limit_ioctl(priv->pf_inet6_fd);
-#endif
/* Don't return an error so we at least work on kernels witout INET6
* even though we expect INET6 support.
* We will fail noisily elsewhere anyway. */
-#else
- priv->pf_inet6_fd = -1;
+#ifdef PRIVSEP_RIGHTS
+ if (priv->pf_inet6_fd != -1 && IN_PRIVSEP(ctx))
+ ps_rights_limit_ioctl(priv->pf_inet6_fd);
+#endif
#endif
ctx->link_fd = xsocket(PF_ROUTE, SOCK_RAW | SOCK_CXNB, AF_UNSPEC);
@@ -199,6 +197,18 @@ if_opensockets_os(struct dhcpcd_ctx *ctx)
&n, sizeof(n)) == -1)
logerr("%s: SO_USELOOPBACK", __func__);
+#ifdef PRIVSEP
+ if (ctx->options & DHCPCD_PRIVSEPROOT) {
+ /* We only want to write to this socket, so set
+ * a small as possible buffer size. */
+ socklen_t smallbuf = 1;
+
+ if (setsockopt(ctx->link_fd, SOL_SOCKET, SO_RCVBUF,
+ &smallbuf, (socklen_t)sizeof(smallbuf)) == -1)
+ logerr("%s: setsockopt(SO_RCVBUF)", __func__);
+ }
+#endif
+
#if defined(RO_MSGFILTER)
if (setsockopt(ctx->link_fd, PF_ROUTE, RO_MSGFILTER,
&msgfilter, sizeof(msgfilter)) == -1)
@@ -222,6 +232,11 @@ if_opensockets_os(struct dhcpcd_ctx *ctx)
ps_rights_limit_fd_sockopt(ctx->link_fd);
#endif
+#if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */
+ priv->pf_link_fd = xsocket(PF_LINK, SOCK_DGRAM, 0);
+ if (priv->pf_link_fd == -1)
+ logerr("%s: socket(PF_LINK)", __func__);
+#endif
return 0;
}
@@ -231,8 +246,21 @@ if_closesockets_os(struct dhcpcd_ctx *ctx)
struct priv *priv;
priv = (struct priv *)ctx->priv;
- if (priv->pf_inet6_fd != -1)
+ if (priv == NULL)
+ return;
+
+#ifdef INET6
+ if (priv->pf_inet6_fd != -1) {
close(priv->pf_inet6_fd);
+ priv->pf_inet6_fd = -1;
+ }
+#endif
+#if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */
+ if (priv->pf_link_fd != -1) {
+ close(priv->pf_link_fd);
+ priv->pf_link_fd = -1;
+ }
+#endif
free(priv);
ctx->priv = NULL;
free(ctx->rt_missfilter);
@@ -242,22 +270,14 @@ if_closesockets_os(struct dhcpcd_ctx *ctx)
static int
if_ioctllink(struct dhcpcd_ctx *ctx, unsigned long req, void *data, size_t len)
{
- int s;
- int retval;
+ struct priv *priv = (struct priv *)ctx->priv;
#ifdef PRIVSEP
if (ctx->options & DHCPCD_PRIVSEP)
return (int)ps_root_ioctllink(ctx, req, data, len);
-#else
- UNUSED(ctx);
#endif
- s = socket(PF_LINK, SOCK_DGRAM, 0);
- if (s == -1)
- return -1;
- retval = ioctl(s, req, data, len);
- close(s);
- return retval;
+ return ioctl(priv->pf_link_fd, req, data, len);
}
#endif
@@ -914,37 +934,47 @@ if_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, const struct rt_msghdr *rtm)
return 0;
}
+static int
+if_sysctl(struct dhcpcd_ctx *ctx,
+ const int *name, u_int namelen,
+ void *oldp, size_t *oldlenp, void *newp, size_t newlen)
+{
+#if defined(PRIVSEP) && defined(HAVE_CAPSICUM)
+ if (IN_PRIVSEP(ctx))
+ return (int)ps_root_sysctl(ctx, name, namelen,
+ oldp, oldlenp, newp, newlen);
+#else
+ UNUSED(ctx);
+#endif
+
+ return sysctl(name, namelen, oldp, oldlenp, newp, newlen);
+}
+
int
if_initrt(struct dhcpcd_ctx *ctx, rb_tree_t *kroutes, int af)
{
struct rt_msghdr *rtm;
- int mib[6];
- size_t needed;
+ int mib[6] = { CTL_NET, PF_ROUTE, 0, af, NET_RT_DUMP, 0 };
+ size_t bufl;
char *buf, *p, *end;
struct rt rt, *rtn;
- mib[0] = CTL_NET;
- mib[1] = PF_ROUTE;
- mib[2] = 0;
- mib[3] = af;
- mib[4] = NET_RT_DUMP;
- mib[5] = 0;
-
- if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
+ if (if_sysctl(ctx, mib, __arraycount(mib), NULL, &bufl, NULL, 0) == -1)
return -1;
- if (needed == 0)
+ if (bufl == 0)
return 0;
- if ((buf = malloc(needed)) == NULL)
+ if ((buf = malloc(bufl)) == NULL)
return -1;
- if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1) {
+ if (if_sysctl(ctx, mib, __arraycount(mib), buf, &bufl, NULL, 0) == -1)
+ {
free(buf);
return -1;
}
- end = buf + needed;
+ end = buf + bufl;
for (p = buf; p < end; p += rtm->rtm_msglen) {
rtm = (void *)p;
- if (p + rtm->rtm_msglen >= end) {
+ if (p + sizeof(*rtm) > end || p + rtm->rtm_msglen > end) {
errno = EINVAL;
break;
}
@@ -1246,8 +1276,8 @@ if_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)
/* Ignore messages from ourself. */
#ifdef PRIVSEP
- if (ctx->ps_root_pid != 0) {
- if (rtm->rtm_pid == ctx->ps_root_pid)
+ if (ctx->ps_root != NULL) {
+ if (rtm->rtm_pid == ctx->ps_root->psp_pid)
return 0;
}
#endif
@@ -1298,8 +1328,8 @@ if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam)
* We need to process address flag changes though. */
if (ifam->ifam_type == RTM_DELADDR) {
#ifdef PRIVSEP
- if (ctx->ps_root_pid != 0) {
- if (ifam->ifam_pid == ctx->ps_root_pid)
+ if (ctx->ps_root != NULL) {
+ if (ifam->ifam_pid == ctx->ps_root->psp_pid)
return 0;
} else
#endif
@@ -1323,6 +1353,11 @@ if_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam)
ifam->ifam_msglen - sizeof(*ifam), rti_info) == -1)
return -1;
+ /* All BSD's set IFF_UP on the interface when adding an address.
+ * But not all BSD's emit this via RTM_IFINFO when they do this ... */
+ if (ifam->ifam_type == RTM_NEWADDR && !(ifp->flags & IFF_UP))
+ dhcpcd_handlecarrier(ifp, ifp->carrier, ifp->flags | IFF_UP);
+
switch (rti_info[RTAX_IFA]->sa_family) {
case AF_LINK:
{
@@ -1649,12 +1684,11 @@ inet6_sysctl(int code, int val, int action)
mib[3] = code;
size = sizeof(val);
if (action) {
- if (sysctl(mib, sizeof(mib)/sizeof(mib[0]),
- NULL, 0, &val, size) == -1)
+ if (sysctl(mib, __arraycount(mib), NULL, 0, &val, size) == -1)
return -1;
return 0;
}
- if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &val, &size, NULL, 0) == -1)
+ if (sysctl(mib, __arraycount(mib), &val, &size, NULL, 0) == -1)
return -1;
return val;
}
@@ -1747,10 +1781,9 @@ ip6_forwarding(__unused const char *ifname)
static int
if_af_attach(const struct interface *ifp, int af)
{
- struct if_afreq ifar;
+ struct if_afreq ifar = { .ifar_af = af };
strlcpy(ifar.ifar_name, ifp->name, sizeof(ifar.ifar_name));
- ifar.ifar_af = af;
return if_ioctl6(ifp->ctx, SIOCIFAFATTACH, &ifar, sizeof(ifar));
}
#endif
@@ -1824,23 +1857,20 @@ if_disable_rtadv(void)
void
if_setup_inet6(const struct interface *ifp)
{
+#ifdef ND6_NDI_FLAGS
struct priv *priv;
int s;
-#ifdef ND6_NDI_FLAGS
struct in6_ndireq nd;
int flags;
-#endif
priv = (struct priv *)ifp->ctx->priv;
s = priv->pf_inet6_fd;
-#ifdef ND6_NDI_FLAGS
memset(&nd, 0, sizeof(nd));
strlcpy(nd.ifname, ifp->name, sizeof(nd.ifname));
if (ioctl(s, SIOCGIFINFO_IN6, &nd) == -1)
logerr("%s: SIOCGIFINFO_FLAGS", ifp->name);
flags = (int)nd.ndi.flags;
-#endif
#ifdef ND6_IFF_AUTO_LINKLOCAL
/* Unlike the kernel, dhcpcd make make a stable private address. */
@@ -1870,14 +1900,13 @@ if_setup_inet6(const struct interface *ifp)
#endif
#endif
-#ifdef ND6_NDI_FLAGS
if (nd.ndi.flags != (uint32_t)flags) {
nd.ndi.flags = (uint32_t)flags;
if (if_ioctl6(ifp->ctx, SIOCSIFINFO_FLAGS,
&nd, sizeof(nd)) == -1)
logerr("%s: SIOCSIFINFO_FLAGS", ifp->name);
}
-#endif
+#endif /* ND6_NDI_FLAGS */
/* Enabling IPv6 by whatever means must be the
* last action undertaken to ensure kernel RS and
diff --git a/src/if-linux-wext.c b/src/if-linux-wext.c
index a7a268926caa..88545dc9773f 100644
--- a/src/if-linux-wext.c
+++ b/src/if-linux-wext.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2009-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2009-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -69,7 +69,7 @@ if_getssid_wext(const char *ifname, uint8_t *ssid)
int s, retval;
struct iwreq iwr;
- if ((s = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
+ if ((s = xsocket(PF_INET, SOCK_DGRAM, 0)) == -1)
return -1;
memset(&iwr, 0, sizeof(iwr));
strlcpy(iwr.ifr_name, ifname, sizeof(iwr.ifr_name));
diff --git a/src/if-linux.c b/src/if-linux.c
index fd05147bab57..2a5995167646 100644
--- a/src/if-linux.c
+++ b/src/if-linux.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Linux interface driver for dhcpcd
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -60,6 +60,9 @@
#include <linux/if_arp.h>
#endif
+/* Inlcude this *after* net/if.h so we get IFF_DORMANT */
+#include <linux/if.h>
+
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@@ -141,12 +144,6 @@ int if_getssid_wext(const char *ifname, uint8_t *ssid);
(struct rtattr *)(void *)(((char *)(rta)) \
+ RTA_ALIGN((rta)->rta_len)))
-struct priv {
- int route_fd;
- int generic_fd;
- uint32_t route_pid;
-};
-
/* We need this to send a broadcast for InfiniBand.
* Our old code used sendto, but our new code writes to a raw BPF socket.
* What header structure does IPoIB use? */
@@ -161,7 +158,6 @@ static const uint8_t ipv4_bcast_addr[] = {
static int if_addressexists(struct interface *, struct in_addr *);
-#define PROC_INET6 "/proc/net/if_inet6"
#define PROC_PROMOTE "/proc/sys/net/ipv4/conf/%s/promote_secondaries"
#define SYS_BRIDGE "/sys/class/net/%s/bridge/bridge_id"
#define SYS_LAYER2 "/sys/class/net/%s/device/layer2"
@@ -189,18 +185,20 @@ static const char *mproc =
"cpu model"
#elif defined(__frv__)
"System"
+#elif defined(__hppa__)
+ "model"
#elif defined(__i386__) || defined(__x86_64__)
"vendor_id"
#elif defined(__ia64__)
"vendor"
-#elif defined(__hppa__)
- "model"
#elif defined(__m68k__)
"MMU"
#elif defined(__mips__)
"system type"
#elif defined(__powerpc__) || defined(__powerpc64__)
"machine"
+#elif defined(__riscv)
+ "uarch"
#elif defined(__s390__) || defined(__s390x__)
"Manufacturer"
#elif defined(__sh__)
@@ -233,7 +231,7 @@ if_machinearch(char *str, size_t len)
if (strncmp(buf, mproc, strlen(mproc)) == 0 &&
fscanf(fp, "%255s", buf) == 1)
{
- fclose(fp);
+ fclose(fp);
return snprintf(str, len, "%s", buf);
}
}
@@ -446,6 +444,13 @@ if_opensockets_os(struct dhcpcd_ctx *ctx)
int on = 1;
#endif
+#ifdef PRIVSEP
+ if (ctx->options & DHCPCD_PRIVSEPROOT) {
+ ctx->link_fd = -1;
+ goto setup_priv;
+ }
+#endif
+
/* Open the link socket first so it gets pid() for the socket.
* Then open our persistent route socket so we get a unique
* pid that doesn't clash with a process id for after we fork. */
@@ -468,6 +473,9 @@ if_opensockets_os(struct dhcpcd_ctx *ctx)
logerr("%s: NETLINK_BROADCAST_ERROR", __func__);
#endif
+#ifdef PRIVSEP
+setup_priv:
+#endif
if ((priv = calloc(1, sizeof(*priv))) == NULL)
return -1;
@@ -492,13 +500,22 @@ if_opensockets_os(struct dhcpcd_ctx *ctx)
void
if_closesockets_os(struct dhcpcd_ctx *ctx)
{
- struct priv *priv;
+ struct priv *priv = ctx->priv;
+
+ if (priv == NULL)
+ return;
- if (ctx->priv != NULL) {
- priv = (struct priv *)ctx->priv;
+ if (priv->route_fd != -1) {
close(priv->route_fd);
+ priv->route_fd = -1;
+ }
+ if (priv->generic_fd != -1) {
close(priv->generic_fd);
+ priv->generic_fd = -1;
}
+
+ free(priv);
+ ctx->priv = NULL;
}
int
@@ -518,23 +535,39 @@ if_setmac(struct interface *ifp, void *mac, uint8_t maclen)
return if_ioctl(ifp->ctx, SIOCSIFHWADDR, &ifr, sizeof(ifr));
}
+static int if_carrier_from_flags(unsigned int flags)
+{
+
+#ifdef IFF_LOWER_UP
+ return ((flags & (IFF_LOWER_UP | IFF_RUNNING)) ==
+ (IFF_LOWER_UP | IFF_RUNNING))
+#ifdef IFF_DORMANT
+ && !(flags & IFF_DORMANT)
+#endif
+ ? LINK_UP : LINK_DOWN;
+#else
+ return flags & IFF_RUNNING ? LINK_UP : LINK_DOWN;
+#endif
+}
+
int
if_carrier(struct interface *ifp, __unused const void *ifadata)
{
-
- return ifp->flags & IFF_RUNNING ? LINK_UP : LINK_DOWN;
+ return if_carrier_from_flags(ifp->flags);
}
bool
if_roaming(struct interface *ifp)
{
-#ifdef IFF_LOWER_UP
if (!ifp->wireless ||
- ifp->flags & IFF_RUNNING ||
- (ifp->flags & (IFF_UP | IFF_LOWER_UP)) != (IFF_UP | IFF_LOWER_UP))
+ ifp->flags & IFF_RUNNING)
return false;
- return true;
+
+#if defined(IFF_DORMANT)
+ return ifp->flags & IFF_DORMANT;
+#elif defined(IFF_LOWER_UP)
+ return (ifp->flags & (IFF_UP|IFF_LOWER_UP)) == (IFF_UP|IFF_LOWER_UP);
#else
return false;
#endif
@@ -549,15 +582,15 @@ if_getnetlink(struct dhcpcd_ctx *ctx, struct iovec *iov, int fd, int flags,
.msg_name = &nladdr, .msg_namelen = sizeof(nladdr),
.msg_iov = iov, .msg_iovlen = 1,
};
- ssize_t len;
+ size_t len;
struct nlmsghdr *nlm;
int r = 0;
unsigned int again;
bool terminated;
recv_again:
- len = recvmsg(fd, &msg, flags);
- if (len == -1 || len == 0)
+ len = (size_t)recvmsg(fd, &msg, flags);
+ if (len == 0 || (ssize_t)len == -1)
return (int)len;
/* Check sender */
@@ -573,7 +606,7 @@ recv_again:
again = 0;
terminated = false;
for (nlm = iov->iov_base;
- nlm && NLMSG_OK(nlm, (size_t)len);
+ nlm && NLMSG_OK(nlm, len);
nlm = NLMSG_NEXT(nlm, len))
{
again = (nlm->nlmsg_flags & NLM_F_MULTI);
@@ -745,8 +778,8 @@ link_route(struct dhcpcd_ctx *ctx, __unused struct interface *ifp,
/* Ignore messages we sent. */
#ifdef PRIVSEP
- if (ctx->ps_root_pid != 0 &&
- nlm->nlmsg_pid == (uint32_t)ctx->ps_root_pid)
+ if (ctx->ps_root != NULL &&
+ nlm->nlmsg_pid == (uint32_t)ctx->ps_root->psp_pid)
return 0;
#endif
priv = (struct priv *)ctx->priv;
@@ -788,8 +821,8 @@ link_addr(struct dhcpcd_ctx *ctx, struct interface *ifp, struct nlmsghdr *nlm)
* We need to process address flag changes though. */
if (nlm->nlmsg_type == RTM_DELADDR) {
#ifdef PRIVSEP
- if (ctx->ps_root_pid != 0 &&
- nlm->nlmsg_pid == (uint32_t)ctx->ps_root_pid)
+ if (ctx->ps_root != NULL &&
+ nlm->nlmsg_pid == (uint32_t)ctx->ps_root->psp_pid)
return 0;
#endif
priv = (struct priv*)ctx->priv;
@@ -915,7 +948,7 @@ link_neigh(struct dhcpcd_ctx *ctx, __unused struct interface *ifp,
r = NLMSG_DATA(nlm);
rta = RTM_RTA(r);
len = RTM_PAYLOAD(nlm);
- if (r->ndm_family == AF_INET6) {
+ if (r->ndm_family == AF_INET6) {
bool unreachable;
struct in6_addr addr6;
@@ -1040,7 +1073,7 @@ link_netlink(struct dhcpcd_ctx *ctx, void *arg, struct nlmsghdr *nlm)
}
dhcpcd_handlecarrier(ifp,
- ifi->ifi_flags & IFF_RUNNING ? LINK_UP : LINK_DOWN,
+ if_carrier_from_flags(ifi->ifi_flags),
ifi->ifi_flags);
return 0;
}
@@ -1138,7 +1171,8 @@ if_sendnetlink(struct dhcpcd_ctx *ctx, int protocol, struct nlmsghdr *hdr,
}
#define NLMSG_TAIL(nmsg) \
- ((struct rtattr *)(((ptrdiff_t)(nmsg))+NLMSG_ALIGN((nmsg)->nlmsg_len)))
+ ((struct rtattr *)(void *)(((char *)(nmsg)) + \
+ NLMSG_ALIGN((nmsg)->nlmsg_len)))
static int
add_attr_l(struct nlmsghdr *n, unsigned short maxlen, unsigned short type,
@@ -1974,46 +2008,83 @@ if_address6(unsigned char cmd, const struct ipv6_addr *ia)
NULL, NULL);
}
+struct ifiaddr6
+{
+ unsigned int ifa_ifindex;
+ struct in6_addr ifa_addr;
+ uint32_t ifa_flags;
+ bool ifa_found;
+};
+
+static int
+_if_addrflags6(__unused struct dhcpcd_ctx *ctx,
+ void *arg, struct nlmsghdr *nlm)
+{
+ struct ifiaddr6 *ia = arg;
+ size_t len;
+ struct rtattr *rta;
+ struct ifaddrmsg *ifa;
+ struct in6_addr *local = NULL, *address = NULL;
+ uint32_t *flags = NULL;
+
+ ifa = NLMSG_DATA(nlm);
+ if (ifa->ifa_index != ia->ifa_ifindex || ifa->ifa_family != AF_INET6)
+ return 0;
+
+ rta = IFA_RTA(ifa);
+ len = NLMSG_PAYLOAD(nlm, sizeof(*ifa));
+ for (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
+ switch (rta->rta_type) {
+ case IFA_ADDRESS:
+ address = (struct in6_addr *)RTA_DATA(rta);
+ break;
+ case IFA_LOCAL:
+ local = (struct in6_addr *)RTA_DATA(rta);
+ break;
+ case IFA_FLAGS:
+ flags = (uint32_t *)RTA_DATA(rta);
+ break;
+ }
+ }
+
+ if (local) {
+ if (IN6_ARE_ADDR_EQUAL(&ia->ifa_addr, local))
+ ia->ifa_found = true;
+ } else if (address) {
+ if (IN6_ARE_ADDR_EQUAL(&ia->ifa_addr, address))
+ ia->ifa_found = true;
+ }
+ if (flags && ia->ifa_found)
+ memcpy(&ia->ifa_flags, flags, sizeof(ia->ifa_flags));
+ return 0;
+}
+
int
if_addrflags6(const struct interface *ifp, const struct in6_addr *addr,
__unused const char *alias)
{
- char buf[PS_BUFLEN], *bp = buf, *line;
- ssize_t buflen;
- char *p, ifaddress[33], address[33], name[IF_NAMESIZE + 1];
- unsigned int ifindex;
- int prefix, scope, flags, i;
+ struct ifiaddr6 ia = {
+ .ifa_ifindex = ifp->index,
+ .ifa_addr = *addr,
+ .ifa_found = false,
+ };
+ struct nlma nlm = {
+ .hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
+ .hdr.nlmsg_type = RTM_GETADDR,
+ .hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH,
+ .ifa.ifa_family = AF_INET6,
+ .ifa.ifa_index = ifp->index,
+ };
- buflen = dhcp_readfile(ifp->ctx, PROC_INET6, buf, sizeof(buf));
- if (buflen == -1)
+ int error = if_sendnetlink(ifp->ctx, NETLINK_ROUTE, &nlm.hdr,
+ &_if_addrflags6, &ia);
+ if (error == -1)
return -1;
- if ((size_t)buflen == sizeof(buf)) {
- errno = ENOBUFS;
+ if (!ia.ifa_found) {
+ errno = ESRCH;
return -1;
}
-
- p = ifaddress;
- for (i = 0; i < (int)sizeof(addr->s6_addr); i++) {
- p += snprintf(p, 3, "%.2x", addr->s6_addr[i]);
- }
- *p = '\0';
-
- while ((line = get_line(&bp, &buflen)) != NULL) {
- if (sscanf(line,
- "%32[a-f0-9] %x %x %x %x %"TOSTRING(IF_NAMESIZE)"s\n",
- address, &ifindex, &prefix, &scope, &flags, name) != 6 ||
- strlen(address) != 32)
- {
- errno = EINVAL;
- return -1;
- }
- if (strcmp(name, ifp->name) == 0 &&
- strcmp(ifaddress, address) == 0)
- return flags;
- }
-
- errno = ESRCH;
- return -1;
+ return (int)ia.ifa_flags;
}
int
@@ -2090,23 +2161,27 @@ if_setup_inet6(const struct interface *ifp)
int ra;
char path[256];
- /* The kernel cannot make stable private addresses.
+ /* Modern linux kernels can make a stable private address.
* However, a lot of distros ship newer kernel headers than
- * the kernel itself so sweep that error under the table. */
+ * the kernel itself so we sweep that error under the table
+ * from old kernels and just make them ourself regardless. */
if (if_disable_autolinklocal(ctx, ifp->index) == -1 &&
errno != ENODEV && errno != ENOTSUP && errno != EINVAL)
logdebug("%s: if_disable_autolinklocal", ifp->name);
- /*
- * If not doing autoconf, don't disable the kernel from doing it.
- * If we need to, we should have another option actively disable it.
- */
+ /* If not doing autoconf, don't disable the kernel from doing it.
+ * If we need to, we should have another option actively disable it. */
if (!(ifp->options->options & DHCPCD_IPV6RS))
return;
snprintf(path, sizeof(path), "%s/%s/autoconf", p_conf, ifp->name);
ra = check_proc_int(ctx, path);
- if (ra != 1 && ra != -1) {
+ if (ra == -1) {
+ /* The sysctl probably doesn't exist, but this isn't an
+ * error as such so just log it and continue */
+ if (errno != ENOENT)
+ logerr("%s: %s", __func__, path);
+ } else if (ra != 0) {
if (if_writepathuint(ctx, path, 0) == -1)
logerr("%s: %s", __func__, path);
}
diff --git a/src/if-options.c b/src/if-options.c
index dd70c80656b2..ee8644d20c13 100644
--- a/src/if-options.c
+++ b/src/if-options.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -168,6 +168,10 @@ const struct option cf_options[] = {
{"link_rcvbuf", required_argument, NULL, O_LINK_RCVBUF},
{"configure", no_argument, NULL, O_CONFIGURE},
{"noconfigure", no_argument, NULL, O_NOCONFIGURE},
+ {"arp_persistdefence", no_argument, NULL, O_ARP_PERSISTDEFENCE},
+ {"request_time", required_argument, NULL, O_REQUEST_TIME},
+ {"fallback_time", required_argument, NULL, O_FALLBACK_TIME},
+ {"ipv4ll_time", required_argument, NULL, O_IPV4LL_TIME},
{NULL, 0, NULL, '\0'}
};
@@ -193,7 +197,10 @@ add_environ(char ***array, const char *value, int uniq)
l = strlen(match);
while (list && list[i]) {
- if (match && strncmp(list[i], match, l) == 0) {
+ /* We know that it must contain '=' due to the above test */
+ size_t listl = (size_t)(strchr(list[i], '=') - list[i]);
+
+ if (l == listl && strncmp(list[i], match, l) == 0) {
if (uniq) {
n = strdup(value);
if (n == NULL) {
@@ -266,7 +273,13 @@ parse_str(char *sbuf, size_t slen, const char *str, int flags)
}
} else {
l = (size_t)hwaddr_aton(NULL, str);
- if ((ssize_t) l != -1 && l > 1) {
+ if (l > 0) {
+ if ((ssize_t)l == -1) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ if (sbuf == NULL)
+ return (ssize_t)l;
if (l > slen) {
errno = ENOBUFS;
return -1;
@@ -949,11 +962,16 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
break;
case 'w':
ifo->options |= DHCPCD_WAITIP;
- if (arg != NULL && arg[0] != '\0') {
- if (arg[0] == '4' || arg[1] == '4')
+ p = UNCONST(arg);
+ // Generally it's --waitip=46, but some expect
+ // --waitip="4 6" to work as well.
+ // It's easier to allow it rather than have confusing docs.
+ while (p != NULL && p[0] != '\0') {
+ if (p[0] == '4' || p[1] == '4')
ifo->options |= DHCPCD_WAITIP4;
- if (arg[0] == '6' || arg[1] == '6')
+ if (p[0] == '6' || p[1] == '6')
ifo->options |= DHCPCD_WAITIP6;
+ p = strskipwhite(++p);
}
break;
case 'y':
@@ -1108,8 +1126,13 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
logerrx("static assignment required");
return -1;
}
- p++;
+ p = strskipwhite(++p);
if (strncmp(arg, "ip_address=", strlen("ip_address=")) == 0) {
+ if (p == NULL) {
+ ifo->options &= ~DHCPCD_STATIC;
+ ifo->req_addr.s_addr = INADDR_ANY;
+ break;
+ }
if (parse_addr(&ifo->req_addr,
ifo->req_mask.s_addr == 0 ? &ifo->req_mask : NULL,
p) != 0)
@@ -1120,11 +1143,19 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
} else if (strncmp(arg, "subnet_mask=",
strlen("subnet_mask=")) == 0)
{
+ if (p == NULL) {
+ ifo->req_mask.s_addr = INADDR_ANY;
+ break;
+ }
if (parse_addr(&ifo->req_mask, NULL, p) != 0)
return -1;
} else if (strncmp(arg, "broadcast_address=",
strlen("broadcast_address=")) == 0)
{
+ if (p == NULL) {
+ ifo->req_brd.s_addr = INADDR_ANY;
+ break;
+ }
if (parse_addr(&ifo->req_brd, NULL, p) != 0)
return -1;
} else if (strncmp(arg, "routes=", strlen("routes=")) == 0 ||
@@ -1137,6 +1168,12 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
{
struct in_addr addr3;
+ if (p == NULL) {
+ rt_headclear(&ifo->routes, AF_INET);
+ add_environ(&ifo->config, arg, 1);
+ break;
+ }
+
fp = np = strwhite(p);
if (np == NULL) {
logerrx("all routes need a gateway");
@@ -1159,6 +1196,11 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
if (rt_proto_add_ctx(&ifo->routes, rt, ctx))
add_environ(&ifo->config, arg, 0);
} else if (strncmp(arg, "routers=", strlen("routers=")) == 0) {
+ if (p == NULL) {
+ rt_headclear(&ifo->routes, AF_INET);
+ add_environ(&ifo->config, arg, 1);
+ break;
+ }
if (parse_addr(&addr, NULL, p) == -1)
return -1;
if ((rt = rt_new0(ctx)) == NULL)
@@ -1173,6 +1215,8 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
strlen("interface_mtu=")) == 0 ||
strncmp(arg, "mtu=", strlen("mtu=")) == 0)
{
+ if (p == NULL)
+ break;
ifo->mtu = (unsigned int)strtou(p, NULL, 0,
MTU_MIN, MTU_MAX, &e);
if (e) {
@@ -1180,6 +1224,12 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
return -1;
}
} else if (strncmp(arg, "ip6_address=", strlen("ip6_address=")) == 0) {
+ if (p == NULL) {
+ memset(&ifo->req_addr6, 0,
+ sizeof(ifo->req_addr6));
+ break;
+ }
+
np = strchr(p, '/');
if (np)
*np++ = '\0';
@@ -1205,8 +1255,9 @@ parse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,
return -1;
}
} else
- add_environ(&ifo->config, arg, 1);
+ add_environ(&ifo->config, arg, p == NULL ? 1 : 0);
break;
+
case 'W':
if (parse_addr(&addr, &addr2, arg) != 0)
return -1;
@@ -1786,6 +1837,8 @@ err_sla:
t |= OT_ADDRIPV6;
else if (strcasecmp(arg, "string") == 0)
t |= OT_STRING;
+ else if (strcasecmp(arg, "uri") == 0)
+ t |= OT_URI;
else if (strcasecmp(arg, "byte") == 0)
t |= OT_UINT8;
else if (strcasecmp(arg, "bitflags") == 0)
@@ -2061,7 +2114,7 @@ err_sla:
arg = fp;
fp = strend(arg);
if (fp == NULL) {
- logerrx("authtoken requies an a key");
+ logerrx("authtoken requires a realm");
goto invalid_token;
}
*fp++ = '\0';
@@ -2114,7 +2167,7 @@ err_sla:
if (s == -1)
logerr("token_len");
else
- logerrx("authtoken needs a key");
+ logerrx("authtoken requires a key");
goto invalid_token;
}
token->key_len = (size_t)s;
@@ -2229,6 +2282,24 @@ invalid_token:
ifo->options |= DHCPCD_SLAACPRIVATE;
else
ifo->options &= ~DHCPCD_SLAACPRIVATE;
+#ifdef INET6
+ if (strcmp(arg, "token") == 0) {
+ if (np == NULL) {
+ logerrx("slaac token: no token specified");
+ return -1;
+ }
+ arg = np;
+ np = strwhite(np);
+ if (np != NULL) {
+ *np++ = '\0';
+ np = strskipwhite(np);
+ }
+ if (inet_pton(AF_INET6, arg, &ifo->token) != 1) {
+ logerrx("slaac token: invalid token");
+ return -1;
+ }
+ }
+#endif
if (np != NULL &&
(strcmp(np, "temp") == 0 || strcmp(np, "temporary") == 0))
ifo->options |= DHCPCD_SLAACTEMP;
@@ -2270,6 +2341,38 @@ invalid_token:
case O_NOCONFIGURE:
ifo->options &= ~DHCPCD_CONFIGURE;
break;
+ case O_ARP_PERSISTDEFENCE:
+ ifo->options |= DHCPCD_ARP_PERSISTDEFENCE;
+ break;
+ case O_REQUEST_TIME:
+ ARG_REQUIRED;
+ ifo->request_time =
+ (uint32_t)strtou(arg, NULL, 0, 0, UINT32_MAX, &e);
+ if (e) {
+ logerrx("invalid request time: %s", arg);
+ return -1;
+ }
+ break;
+#ifdef INET
+ case O_FALLBACK_TIME:
+ ARG_REQUIRED;
+ ifo->request_time =
+ (uint32_t)strtou(arg, NULL, 0, 0, UINT32_MAX, &e);
+ if (e) {
+ logerrx("invalid fallback time: %s", arg);
+ return -1;
+ }
+ break;
+ case O_IPV4LL_TIME:
+ ARG_REQUIRED;
+ ifo->ipv4ll_time =
+ (uint32_t)strtou(arg, NULL, 0, 0, UINT32_MAX, &e);
+ if (e) {
+ logerrx("invalid ipv4ll time: %s", arg);
+ return -1;
+ }
+ break;
+#endif
default:
return 0;
}
@@ -2340,7 +2443,7 @@ finish_config(struct if_options *ifo)
~(DHCPCD_IPV6RA_AUTOCONF | DHCPCD_IPV6RA_REQRDNSS);
}
-struct if_options *
+static struct if_options *
default_config(struct dhcpcd_ctx *ctx)
{
struct if_options *ifo;
@@ -2353,6 +2456,11 @@ default_config(struct dhcpcd_ctx *ctx)
ifo->options |= DHCPCD_IF_UP | DHCPCD_LINK | DHCPCD_INITIAL_DELAY;
ifo->timeout = DEFAULT_TIMEOUT;
ifo->reboot = DEFAULT_REBOOT;
+ ifo->request_time = DEFAULT_REQUEST;
+#ifdef INET
+ ifo->fallback_time = DEFAULT_FALLBACK;
+ ifo->ipv4ll_time = DEFAULT_IPV4LL;
+#endif
ifo->metric = -1;
ifo->auth.options |= DHCPCD_AUTH_REQUIRE;
rb_tree_init(&ifo->routes, &rt_compare_list_ops);
@@ -2394,7 +2502,7 @@ read_config(struct dhcpcd_ctx *ctx,
default_options |= DHCPCD_CONFIGURE | DHCPCD_DAEMONISE |
DHCPCD_GATEWAY;
#ifdef INET
- skip = socket(PF_INET, SOCK_DGRAM, 0);
+ skip = xsocket(PF_INET, SOCK_DGRAM, 0);
if (skip != -1) {
close(skip);
default_options |= DHCPCD_IPV4 | DHCPCD_ARP |
@@ -2402,7 +2510,7 @@ read_config(struct dhcpcd_ctx *ctx,
}
#endif
#ifdef INET6
- skip = socket(PF_INET6, SOCK_DGRAM, 0);
+ skip = xsocket(PF_INET6, SOCK_DGRAM, 0);
if (skip != -1) {
close(skip);
default_options |= DHCPCD_IPV6 | DHCPCD_IPV6RS |
diff --git a/src/if-options.h b/src/if-options.h
index f80119d6c15f..14c698fe1754 100644
--- a/src/if-options.h
+++ b/src/if-options.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -49,6 +49,9 @@
#define DEFAULT_TIMEOUT 30
#define DEFAULT_REBOOT 5
+#define DEFAULT_REQUEST 180 /* secs to request, mirror DHCP6 */
+#define DEFAULT_FALLBACK 5 /* secs until fallback */
+#define DEFAULT_IPV4LL 5 /* secs until ipv4ll */
#ifndef HOSTNAME_MAX_LEN
#define HOSTNAME_MAX_LEN 250 /* 255 - 3 (FQDN) - 2 (DNS enc) */
@@ -65,6 +68,7 @@
#define DHCPCD_GATEWAY (1ULL << 3)
#define DHCPCD_STATIC (1ULL << 4)
#define DHCPCD_DEBUG (1ULL << 5)
+#define DHCPCD_ARP_PERSISTDEFENCE (1ULL << 6)
#define DHCPCD_LASTLEASE (1ULL << 7)
#define DHCPCD_INFORM (1ULL << 8)
#define DHCPCD_REQUEST (1ULL << 9)
@@ -183,6 +187,10 @@
#define O_CONFIGURE O_BASE + 50
#define O_NOCONFIGURE O_BASE + 51
#define O_RANDOMISE_HWADDR O_BASE + 52
+#define O_ARP_PERSISTDEFENCE O_BASE + 53
+#define O_REQUEST_TIME O_BASE + 54
+#define O_FALLBACK_TIME O_BASE + 55
+#define O_IPV4LL_TIME O_BASE + 56
extern const struct option cf_options[];
@@ -234,6 +242,9 @@ struct if_options {
uint32_t leasetime;
uint32_t timeout;
uint32_t reboot;
+ uint32_t request_time;
+ uint32_t fallback_time;
+ uint32_t ipv4ll_time;
unsigned long long options;
bool randomise_hwaddr;
@@ -266,6 +277,9 @@ struct if_options {
struct if_ia *ia;
size_t ia_len;
+#ifdef INET6
+ struct in6_addr token;
+#endif
struct dhcp_opt *dhcp_override;
size_t dhcp_override_len;
@@ -282,7 +296,6 @@ struct if_options {
struct auth auth;
};
-struct if_options *default_config(struct dhcpcd_ctx *);
struct if_options *read_config(struct dhcpcd_ctx *,
const char *, const char *, const char *);
int add_options(struct dhcpcd_ctx *, const char *,
diff --git a/src/if-sun.c b/src/if-sun.c
index d25bbc817a29..37d009619672 100644
--- a/src/if-sun.c
+++ b/src/if-sun.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Solaris interface driver for dhcpcd
- * Copyright (c) 2016-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2016-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -94,12 +94,6 @@ extern int getallifaddrs(sa_family_t, struct ifaddrs **, int64_t);
#define COPYSA(dst, src) memcpy((dst), (src), sa_len((src)))
-struct priv {
-#ifdef INET6
- int pf_inet6_fd;
-#endif
-};
-
struct rtm
{
struct rt_msghdr hdr;
@@ -160,7 +154,7 @@ if_opensockets_os(struct dhcpcd_ctx *ctx)
* We will fail noisily elsewhere anyway. */
#endif
- ctx->link_fd = socket(PF_ROUTE,
+ ctx->link_fd = xsocket(PF_ROUTE,
SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
if (ctx->link_fd == -1) {
@@ -186,12 +180,13 @@ if_closesockets_os(struct dhcpcd_ctx *ctx)
struct priv *priv;
priv = (struct priv *)ctx->priv;
- if (priv->pf_inet6_fd != -1)
+ if (priv && priv->pf_inet6_fd != -1)
close(priv->pf_inet6_fd);
#endif
/* each interface should have closed itself */
free(ctx->priv);
+ ctx->priv = NULL;
}
int
@@ -733,7 +728,7 @@ if_route_get(struct dhcpcd_ctx *ctx, struct rt *rt)
if_route0(ctx, &rtm, RTM_GET, rt);
rt = NULL;
- s = socket(PF_ROUTE, SOCK_RAW | SOCK_CLOEXEC, 0);
+ s = xsocket(PF_ROUTE, SOCK_RAW | SOCK_CLOEXEC, 0);
if (s == -1)
return NULL;
if (write(s, &rtm, rtm.hdr.rtm_msglen) == -1)
diff --git a/src/if.c b/src/if.c
index b00f5e6f384b..3ed9f6b38efb 100644
--- a/src/if.c
+++ b/src/if.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -132,17 +132,18 @@ void
if_closesockets(struct dhcpcd_ctx *ctx)
{
- if (ctx->pf_inet_fd != -1)
- close(ctx->pf_inet_fd);
-#ifdef PF_LINK
- if (ctx->pf_link_fd != -1)
- close(ctx->pf_link_fd);
-#endif
+ if (ctx->link_fd != -1) {
+ eloop_event_delete(ctx->eloop, ctx->link_fd);
+ close(ctx->link_fd);
+ ctx->link_fd = -1;
+ }
- if (ctx->priv) {
- if_closesockets_os(ctx);
- free(ctx->priv);
+ if (ctx->pf_inet_fd != -1) {
+ close(ctx->pf_inet_fd);
+ ctx->pf_inet_fd = -1;
}
+
+ if_closesockets_os(ctx);
}
int
@@ -356,6 +357,16 @@ if_learnaddrs(struct dhcpcd_ctx *ctx, struct if_head *ifs,
#endif
}
}
+}
+
+void if_freeifaddrs(struct dhcpcd_ctx *ctx, struct ifaddrs **ifaddrs)
+{
+#ifndef PRIVSEP_GETIFADDRS
+ UNUSED(ctx);
+#endif
+
+ if (ifaddrs == NULL)
+ return;
#ifdef PRIVSEP_GETIFADDRS
if (IN_PRIVSEP(ctx))
@@ -363,7 +374,6 @@ if_learnaddrs(struct dhcpcd_ctx *ctx, struct if_head *ifs,
else
#endif
freeifaddrs(*ifaddrs);
- *ifaddrs = NULL;
}
void
@@ -973,6 +983,10 @@ xsocket(int domain, int type, int protocol)
if ((s = socket(domain, type, protocol)) == -1)
return -1;
+#ifdef DEBUG_FD
+ logerrx("pid %d fd=%d domain=%d type=%d protocol=%d",
+ getpid(), s, domain, type, protocol);
+#endif
#ifndef HAVE_SOCK_CLOEXEC
if ((xtype & SOCK_CLOEXEC) && ((xflags = fcntl(s, F_GETFD)) == -1 ||
@@ -1014,6 +1028,10 @@ xsocketpair(int domain, int type, int protocol, int fd[2])
if ((s = socketpair(domain, type, protocol, fd)) == -1)
return -1;
+#ifdef DEBUG_FD
+ logerrx("pid %d fd[0]=%d fd[1]=%d", getpid(), fd[0], fd[1]);
+#endif
+
#ifndef HAVE_SOCK_CLOEXEC
if ((xtype & SOCK_CLOEXEC) && ((xflags = fcntl(fd[0], F_GETFD)) == -1 ||
fcntl(fd[0], F_SETFD, xflags | FD_CLOEXEC) == -1))
diff --git a/src/if.h b/src/if.h
index d24fbc92cf48..df5f921e6398 100644
--- a/src/if.h
+++ b/src/if.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -119,7 +119,35 @@ typedef unsigned long ioctl_request_t;
* It used to work, but lukily Solaris can fall back to
* IP_PKTINFO. */
#undef IP_RECVIF
+#endif
+/* Private structures specific to an OS */
+#ifdef BSD
+struct priv {
+#ifdef INET6
+ int pf_inet6_fd;
+#endif
+#if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */
+ int pf_link_fd;
+#endif
+};
+#endif
+#ifdef __linux__
+struct priv {
+ int route_fd;
+ int generic_fd;
+ uint32_t route_pid;
+};
+#endif
+#ifdef __sun
+struct priv {
+#ifdef INET6
+ int pf_inet6_fd;
+#endif
+};
+#endif
+
+#ifdef __sun
/* Solaris getifaddrs is very un-suitable for dhcpcd.
* See if-sun.c for details why. */
struct ifaddrs;
@@ -142,6 +170,7 @@ bool if_is_link_up(const struct interface *);
bool if_valid_hwaddr(const uint8_t *, size_t);
struct if_head *if_discover(struct dhcpcd_ctx *, struct ifaddrs **,
int, char * const *);
+void if_freeifaddrs(struct dhcpcd_ctx *ctx, struct ifaddrs **);
void if_markaddrsstale(struct if_head *);
void if_learnaddrs(struct dhcpcd_ctx *, struct if_head *, struct ifaddrs **);
void if_deletestaleaddrs(struct if_head *);
diff --git a/src/ipv4.c b/src/ipv4.c
index 7bf7db38278a..21063e058aaa 100644
--- a/src/ipv4.c
+++ b/src/ipv4.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
diff --git a/src/ipv4.h b/src/ipv4.h
index 6c0ac8e889bc..4f64534a41fe 100644
--- a/src/ipv4.h
+++ b/src/ipv4.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
diff --git a/src/ipv4ll.c b/src/ipv4ll.c
index faaad70f6751..89b3dce62840 100644
--- a/src/ipv4ll.c
+++ b/src/ipv4ll.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -335,6 +335,10 @@ ipv4ll_start(void *arg)
}
}
+ if (state->running)
+ return;
+ state->running = true;
+
/* RFC 3927 Section 2.1 states that the random number generator
* SHOULD be seeded with a value derived from persistent information
* such as the IEEE 802 MAC address so that it usually picks
@@ -435,11 +439,14 @@ ipv4ll_drop(struct interface *ifp)
return;
state = IPV4LL_STATE(ifp);
- if (state && state->addr != NULL) {
- if (ifp->options->options & DHCPCD_CONFIGURE)
- ipv4_deladdr(state->addr, 1);
- state->addr = NULL;
- dropped = true;
+ if (state) {
+ state->running = false;
+ if (state->addr != NULL) {
+ if (ifp->options->options & DHCPCD_CONFIGURE)
+ ipv4_deladdr(state->addr, 1);
+ state->addr = NULL;
+ dropped = true;
+ }
}
/* Free any other link local addresses that might exist. */
diff --git a/src/ipv4ll.h b/src/ipv4ll.h
index 0dcf8572302d..78af32e5191b 100644
--- a/src/ipv4ll.h
+++ b/src/ipv4ll.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -44,6 +44,7 @@ struct ipv4ll_state {
struct in_addr pickedaddr;
struct ipv4_addr *addr;
char randomstate[128];
+ bool running;
bool seeded;
bool down;
size_t conflicts;
diff --git a/src/ipv6.c b/src/ipv6.c
index 04bf7746e080..ce985d4ec5b1 100644
--- a/src/ipv6.c
+++ b/src/ipv6.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -359,51 +359,6 @@ again:
}
#endif
-int
-ipv6_makeaddr(struct in6_addr *addr, struct interface *ifp,
- const struct in6_addr *prefix, int prefix_len, unsigned int flags)
-{
- const struct ipv6_addr *ap;
- int dad;
-
- if (prefix_len < 0 || prefix_len > 120) {
- errno = EINVAL;
- return -1;
- }
-
-#ifdef IPV6_AF_TEMPORARY
- if (flags & IPV6_AF_TEMPORARY)
- return ipv6_maketemporaryaddress(addr, prefix, prefix_len, ifp);
-#else
- UNUSED(flags);
-#endif
-
- if (ifp->options->options & DHCPCD_SLAACPRIVATE) {
- dad = 0;
- if (ipv6_makestableprivate(addr,
- prefix, prefix_len, ifp, &dad) == -1)
- return -1;
- return dad;
- }
-
- if (prefix_len > 64) {
- errno = EINVAL;
- return -1;
- }
- if ((ap = ipv6_linklocal(ifp)) == NULL) {
- /* We delay a few functions until we get a local-link address
- * so this should never be hit. */
- errno = ENOENT;
- return -1;
- }
-
- /* Make the address from the first local-link address */
- memcpy(addr, prefix, sizeof(*prefix));
- addr->s6_addr32[2] = ap->addr.s6_addr32[2];
- addr->s6_addr32[3] = ap->addr.s6_addr32[3];
- return 0;
-}
-
static int
ipv6_makeprefix(struct in6_addr *prefix, const struct in6_addr *addr, int len)
{
@@ -478,6 +433,68 @@ ipv6_prefixlen(const struct in6_addr *mask)
return (uint8_t)(x * NBBY + y);
}
+int
+ipv6_makeaddr(struct in6_addr *addr, struct interface *ifp,
+ const struct in6_addr *prefix, int prefix_len, unsigned int flags)
+{
+ const struct ipv6_addr *ap;
+ const struct if_options *ifo = ifp->options;
+ int dad;
+
+ if (prefix_len < 0 || prefix_len > 120) {
+ errno = EINVAL;
+ return -1;
+ }
+
+#ifdef IPV6_AF_TEMPORARY
+ if (flags & IPV6_AF_TEMPORARY)
+ return ipv6_maketemporaryaddress(addr, prefix, prefix_len, ifp);
+#else
+ UNUSED(flags);
+#endif
+
+ if (ifo->options & DHCPCD_SLAACPRIVATE) {
+ dad = 0;
+ if (ipv6_makestableprivate(addr,
+ prefix, prefix_len, ifp, &dad) == -1)
+ return -1;
+ return dad;
+ } else if (!IN6_IS_ADDR_UNSPECIFIED(&ifo->token)) {
+ int bytes = prefix_len / NBBY;
+ int bits = prefix_len % NBBY;
+
+ // Copy the token into the address.
+ *addr = ifo->token;
+
+ // If we have any dangling bits, just copy that in also.
+ // XXX Can we preserve part of the token still?
+ if (bits != 0)
+ bytes++;
+
+ // Copy the prefix in.
+ if (bytes > 0)
+ memcpy(addr->s6_addr, prefix->s6_addr, (size_t)bytes);
+ return 0;
+ }
+
+ if (prefix_len > 64) {
+ errno = EINVAL;
+ return -1;
+ }
+ if ((ap = ipv6_linklocal(ifp)) == NULL) {
+ /* We delay a few functions until we get a local-link address
+ * so this should never be hit. */
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* Make the address from the first local-link address */
+ memcpy(addr, prefix, sizeof(*prefix));
+ addr->s6_addr32[2] = ap->addr.s6_addr32[2];
+ addr->s6_addr32[3] = ap->addr.s6_addr32[3];
+ return 0;
+}
+
static void
in6_to_h64(uint64_t *vhigh, uint64_t *vlow, const struct in6_addr *addr)
{
@@ -2301,7 +2318,9 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx)
{
struct rt *rt;
struct ra *rap;
+ const struct routeinfo *rinfo;
const struct ipv6_addr *addr;
+ struct in6_addr netmask;
if (ctx->ra_routers == NULL)
return 0;
@@ -2309,6 +2328,27 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx)
TAILQ_FOREACH(rap, ctx->ra_routers, next) {
if (rap->expired)
continue;
+
+ /* add rfc4191 route information routes */
+ TAILQ_FOREACH (rinfo, &rap->rinfos, next) {
+ if(rinfo->lifetime == 0)
+ continue;
+ if ((rt = inet6_makeroute(rap->iface, rap)) == NULL)
+ continue;
+
+ in6_addr_fromprefix(&netmask, rinfo->prefix_len);
+
+ sa_in6_init(&rt->rt_dest, &rinfo->prefix);
+ sa_in6_init(&rt->rt_netmask, &netmask);
+ sa_in6_init(&rt->rt_gateway, &rap->from);
+#ifdef HAVE_ROUTE_PREF
+ rt->rt_pref = ipv6nd_rtpref(rinfo->flags);
+#endif
+
+ rt_proto_add(routes, rt);
+ }
+
+ /* add subnet routes */
TAILQ_FOREACH(addr, &rap->addrs, next) {
if (addr->prefix_vltime == 0)
continue;
@@ -2316,11 +2356,13 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx)
if (rt) {
rt->rt_dflags |= RTDF_RA;
#ifdef HAVE_ROUTE_PREF
- rt->rt_pref = ipv6nd_rtpref(rap);
+ rt->rt_pref = ipv6nd_rtpref(rap->flags);
#endif
rt_proto_add(routes, rt);
}
}
+
+ /* add default route */
if (rap->lifetime == 0)
continue;
if (ipv6_anyglobal(rap->iface) == NULL)
@@ -2330,7 +2372,7 @@ inet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx)
continue;
rt->rt_dflags |= RTDF_RA;
#ifdef HAVE_ROUTE_PREF
- rt->rt_pref = ipv6nd_rtpref(rap);
+ rt->rt_pref = ipv6nd_rtpref(rap->flags);
#endif
rt_proto_add(routes, rt);
}
diff --git a/src/ipv6.h b/src/ipv6.h
index 1fe1d5c224c7..a895e2471325 100644
--- a/src/ipv6.h
+++ b/src/ipv6.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
diff --git a/src/ipv6nd.c b/src/ipv6nd.c
index b0174a775de6..9264dce7300c 100644
--- a/src/ipv6nd.c
+++ b/src/ipv6nd.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - IPv6 ND handling
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -71,6 +71,20 @@
#define ND_OPT_PI_FLAG_ROUTER 0x20 /* Router flag in PI */
#endif
+#ifndef ND_OPT_RI
+#define ND_OPT_RI 24
+struct nd_opt_ri { /* Route Information option RFC4191 */
+ uint8_t nd_opt_ri_type;
+ uint8_t nd_opt_ri_len;
+ uint8_t nd_opt_ri_prefixlen;
+ uint8_t nd_opt_ri_flags_reserved;
+ uint32_t nd_opt_ri_lifetime;
+ struct in6_addr nd_opt_ri_prefix;
+};
+__CTASSERT(sizeof(struct nd_opt_ri) == 24);
+#define OPT_RI_FLAG_PREFERENCE(flags) ((flags & 0x18) >> 3)
+#endif
+
#ifndef ND_OPT_RDNSS
#define ND_OPT_RDNSS 25
struct nd_opt_rdnss { /* RDNSS option RFC 6106 */
@@ -78,7 +92,7 @@ struct nd_opt_rdnss { /* RDNSS option RFC 6106 */
uint8_t nd_opt_rdnss_len;
uint16_t nd_opt_rdnss_reserved;
uint32_t nd_opt_rdnss_lifetime;
- /* followed by list of IP prefixes */
+ /* followed by list of IP prefixes */
};
__CTASSERT(sizeof(struct nd_opt_rdnss) == 8);
#endif
@@ -92,7 +106,7 @@ struct nd_opt_dnssl { /* DNSSL option RFC 6106 */
uint32_t nd_opt_dnssl_lifetime;
/* followed by list of DNS servers */
};
-__CTASSERT(sizeof(struct nd_opt_rdnss) == 8);
+__CTASSERT(sizeof(struct nd_opt_dnssl) == 8);
#endif
/* Impossible options, so we can easily add extras */
@@ -131,7 +145,9 @@ __CTASSERT(sizeof(struct nd_opt_rdnss) == 8);
//#define DEBUG_NS
//
-static void ipv6nd_handledata(void *);
+static void ipv6nd_handledata(void *, unsigned short);
+static struct routeinfo *routeinfo_findalloc(struct ra *, const struct in6_addr *, uint8_t);
+static void routeinfohead_free(struct routeinfohead *);
/*
* Android ships buggy ICMP6 filter headers.
@@ -281,8 +297,14 @@ ipv6nd_openif(struct interface *ifp)
return -1;
}
+ if (eloop_event_add(ifp->ctx->eloop, fd, ELE_READ,
+ ipv6nd_handledata, ifp) == -1)
+ {
+ close(fd);
+ return -1;
+ }
+
state->nd_fd = fd;
- eloop_event_add(ifp->ctx->eloop, fd, ipv6nd_handledata, ifp);
return fd;
}
#endif
@@ -388,7 +410,9 @@ ipv6nd_sendrsprobe(void *arg)
logerr(__func__);
return;
}
- eloop_event_add(ctx->eloop, ctx->nd_fd, ipv6nd_handledata, ctx);
+ if (eloop_event_add(ctx->eloop, ctx->nd_fd, ELE_READ,
+ ipv6nd_handledata, ctx) == -1)
+ logerr("%s: eloop_event_add", __func__);
}
s = ifp->ctx->nd_fd;
#endif
@@ -604,10 +628,10 @@ ipv6nd_startexpire(struct interface *ifp)
}
int
-ipv6nd_rtpref(struct ra *rap)
+ipv6nd_rtpref(uint8_t flags)
{
- switch (rap->flags & ND_RA_FLAG_RTPREF_MASK) {
+ switch (flags & ND_RA_FLAG_RTPREF_MASK) {
case ND_RA_FLAG_RTPREF_HIGH:
return RTPREF_HIGH;
case ND_RA_FLAG_RTPREF_MEDIUM:
@@ -616,7 +640,7 @@ ipv6nd_rtpref(struct ra *rap)
case ND_RA_FLAG_RTPREF_LOW:
return RTPREF_LOW;
default:
- logerrx("%s: impossible RA flag %x", __func__, rap->flags);
+ logerrx("%s: impossible RA flag %x", __func__, flags);
return RTPREF_INVALID;
}
/* NOTREACHED */
@@ -641,7 +665,7 @@ ipv6nd_sortrouters(struct dhcpcd_ctx *ctx)
continue;
if (!ra1->isreachable && ra2->reachable)
continue;
- if (ipv6nd_rtpref(ra1) <= ipv6nd_rtpref(ra2))
+ if (ipv6nd_rtpref(ra1->flags) <= ipv6nd_rtpref(ra2->flags))
continue;
/* All things being equal, prefer older routers. */
/* We don't need to check time, becase newer
@@ -819,6 +843,7 @@ ipv6nd_removefreedrop_ra(struct ra *rap, int remove_ra, int drop_ra)
if (remove_ra)
TAILQ_REMOVE(rap->iface->ctx->ra_routers, rap, next);
ipv6_freedrop_addrs(&rap->addrs, drop_ra, NULL);
+ routeinfohead_free(&rap->rinfos);
free(rap->data);
free(rap);
}
@@ -1097,6 +1122,8 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
struct nd_opt_prefix_info pi;
struct nd_opt_mtu mtu;
struct nd_opt_rdnss rdnss;
+ struct nd_opt_ri ri;
+ struct routeinfo *rinfo;
uint8_t *p;
struct ra *rap;
struct in6_addr pi_prefix;
@@ -1198,6 +1225,7 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
rap->from = from->sin6_addr;
strlcpy(rap->sfrom, sfrom, sizeof(rap->sfrom));
TAILQ_INIT(&rap->addrs);
+ TAILQ_INIT(&rap->rinfos);
new_rap = true;
rap->isreachable = true;
} else
@@ -1229,9 +1257,6 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
rap->flags = nd_ra->nd_ra_flags_reserved;
old_lifetime = rap->lifetime;
rap->lifetime = ntohs(nd_ra->nd_ra_router_lifetime);
- if (!new_rap && rap->lifetime == 0 && old_lifetime != 0)
- logwarnx("%s: %s: no longer a default router",
- ifp->name, rap->sfrom);
if (nd_ra->nd_ra_curhoplimit != 0)
rap->hoplimit = nd_ra->nd_ra_curhoplimit;
else
@@ -1306,6 +1331,9 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
switch (ndo.nd_opt_type) {
case ND_OPT_PREFIX_INFORMATION:
+ {
+ uint32_t vltime, pltime;
+
loglevel = new_data ? LOG_ERR : LOG_DEBUG;
if (ndo.nd_opt_len != 4) {
logmessage(loglevel,
@@ -1329,9 +1357,10 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
ifp->name);
continue;
}
- if (ntohl(pi.nd_opt_pi_preferred_time) >
- ntohl(pi.nd_opt_pi_valid_time))
- {
+
+ vltime = ntohl(pi.nd_opt_pi_valid_time);
+ pltime = ntohl(pi.nd_opt_pi_preferred_time);
+ if (pltime > vltime) {
logmessage(loglevel, "%s: pltime > vltime",
ifp->name);
continue;
@@ -1356,10 +1385,15 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
&pi_prefix, pi.nd_opt_pi_prefix_len, flags);
if (ia == NULL)
break;
+
ia->prefix = pi_prefix;
+ ia->created = ia->acquired = rap->acquired;
+ ia->prefix_vltime = vltime;
+ ia->prefix_pltime = pltime;
+
if (flags & IPV6_AF_AUTOCONF)
ia->dadcallback = ipv6nd_dadcallback;
- ia->created = ia->acquired = rap->acquired;
+
TAILQ_INSERT_TAIL(&rap->addrs, ia, next);
#ifdef IPV6_MANAGETEMPADDR
@@ -1376,18 +1410,58 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
else
new_ia = true;
#endif
+
} else {
-#ifdef IPV6_MANAGETEMPADDR
- new_ia = false;
-#endif
+ uint32_t rmtime;
+
+ /*
+ * RFC 4862 5.5.3.e
+ * Don't terminate existing connections.
+ * This means that to actually remove the
+ * existing prefix, the RA needs to stop
+ * broadcasting the prefix and just let it
+ * expire in 2 hours.
+ * It might want to broadcast it to reduce
+ * the vltime if it was greater than 2 hours
+ * to start with/
+ */
+ ia->prefix_pltime = pltime;
+ if (ia->prefix_vltime) {
+ uint32_t elapsed;
+
+ elapsed = (uint32_t)eloop_timespec_diff(
+ &rap->acquired, &ia->acquired,
+ NULL);
+ rmtime = ia->prefix_vltime - elapsed;
+ if (rmtime > ia->prefix_vltime)
+ rmtime = 0;
+ } else
+ rmtime = 0;
+ if (vltime > MIN_EXTENDED_VLTIME ||
+ vltime > rmtime)
+ ia->prefix_vltime = vltime;
+ else if (rmtime <= MIN_EXTENDED_VLTIME)
+ /* No SEND support from RFC 3971 so
+ * leave vltime alone */
+ ia->prefix_vltime = rmtime;
+ else
+ ia->prefix_vltime = MIN_EXTENDED_VLTIME;
+
+ /* Ensure pltime still fits */
+ if (pltime < ia->prefix_vltime)
+ ia->prefix_pltime = pltime;
+ else
+ ia->prefix_pltime = ia->prefix_vltime;
+
ia->flags |= flags;
ia->flags &= ~IPV6_AF_STALE;
ia->acquired = rap->acquired;
+
+#ifdef IPV6_MANAGETEMPADDR
+ new_ia = false;
+#endif
}
- ia->prefix_vltime =
- ntohl(pi.nd_opt_pi_valid_time);
- ia->prefix_pltime =
- ntohl(pi.nd_opt_pi_preferred_time);
+
if (ia->prefix_vltime != 0 &&
ia->flags & IPV6_AF_AUTOCONF)
has_address = true;
@@ -1410,6 +1484,7 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
}
#endif
break;
+ }
case ND_OPT_MTU:
if (len < sizeof(mtu)) {
@@ -1444,6 +1519,46 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
rdnss.nd_opt_rdnss_len > 1)
rap->hasdns = 1;
break;
+ case ND_OPT_RI:
+ if (ndo.nd_opt_len > 3) {
+ logmessage(loglevel, "%s: invalid route info option",
+ ifp->name);
+ break;
+ }
+ memset(&ri, 0, sizeof(ri));
+ memcpy(&ri, p, olen); /* may be smaller than sizeof(ri), pad with zero */
+ if(ri.nd_opt_ri_prefixlen > 128) {
+ logmessage(loglevel, "%s: invalid route info prefix length",
+ ifp->name);
+ break;
+ }
+
+ /* rfc4191 3.1 - RI for ::/0 applies to default route */
+ if(ri.nd_opt_ri_prefixlen == 0) {
+ rap->lifetime = ntohl(ri.nd_opt_ri_lifetime);
+
+ /* Update preference leaving other flags intact */
+ rap->flags = ((rap->flags & (~ (unsigned int)ND_RA_FLAG_RTPREF_MASK))
+ | ri.nd_opt_ri_flags_reserved) & 0xff;
+
+ break;
+ }
+
+ /* Update existing route info instead of rebuilding all routes so that
+ previously announced but now absent routes can stay alive. To kill a
+ route early, an RI with lifetime=0 needs to be received (rfc4191 3.1)*/
+ rinfo = routeinfo_findalloc(rap, &ri.nd_opt_ri_prefix, ri.nd_opt_ri_prefixlen);
+ if(rinfo == NULL) {
+ logerr(__func__);
+ break;
+ }
+
+ /* Update/initialize other route info params */
+ rinfo->flags = ri.nd_opt_ri_flags_reserved;
+ rinfo->lifetime = ntohl(ri.nd_opt_ri_lifetime);
+ rinfo->acquired = rap->acquired;
+
+ break;
default:
continue;
}
@@ -1479,6 +1594,10 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx,
ia->prefix_pltime = 0;
}
+ if (!new_rap && rap->lifetime == 0 && old_lifetime != 0)
+ logwarnx("%s: %s: no longer a default router (lifetime = 0)",
+ ifp->name, rap->sfrom);
+
if (new_data && !has_address && rap->lifetime && !ipv6_anyglobal(ifp))
logwarnx("%s: no global addresses for default route",
ifp->name);
@@ -1641,7 +1760,7 @@ ipv6nd_env(FILE *fp, const struct interface *ifp)
return -1;
if (efprintf(fp, "%s_hoplimit=%u", ndprefix, rap->hoplimit) == -1)
return -1;
- pref = ipv6nd_rtpref(rap);
+ pref = ipv6nd_rtpref(rap->flags);
if (efprintf(fp, "%s_flags=%s%s%s%s%s", ndprefix,
rap->flags & ND_RA_FLAG_MANAGED ? "M" : "",
rap->flags & ND_RA_FLAG_OTHER ? "O" : "",
@@ -1746,6 +1865,7 @@ ipv6nd_expirera(void *arg)
uint32_t elapsed;
bool expired, valid;
struct ipv6_addr *ia;
+ struct routeinfo *rinfo, *rinfob;
size_t len, olen;
uint8_t *p;
struct nd_opt_hdr ndo;
@@ -1765,7 +1885,8 @@ ipv6nd_expirera(void *arg)
if (rap->iface != ifp || rap->expired)
continue;
valid = false;
- if (rap->lifetime) {
+ /* lifetime may be set to infinite by rfc4191 route information */
+ if (rap->lifetime && rap->lifetime != ND6_INFINITE_LIFETIME) {
elapsed = (uint32_t)eloop_timespec_diff(&now,
&rap->acquired, NULL);
if (elapsed >= rap->lifetime || rap->doexpire) {
@@ -1821,6 +1942,20 @@ ipv6nd_expirera(void *arg)
}
}
+ /* Expire route information */
+ TAILQ_FOREACH_SAFE(rinfo, &rap->rinfos, next, rinfob) {
+ if (rinfo->lifetime == ND6_INFINITE_LIFETIME &&
+ !rap->doexpire)
+ continue;
+ elapsed = (uint32_t)eloop_timespec_diff(&now,
+ &rinfo->acquired, NULL);
+ if (elapsed >= rinfo->lifetime || rap->doexpire) {
+ logwarnx("%s: expired route %s",
+ rap->iface->name, rinfo->sprefix);
+ TAILQ_REMOVE(&rap->rinfos, rinfo, next);
+ }
+ }
+
/* Work out expiry for ND options */
elapsed = (uint32_t)eloop_timespec_diff(&now,
&rap->acquired, NULL);
@@ -1977,7 +2112,7 @@ ipv6nd_recvmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg)
}
static void
-ipv6nd_handledata(void *arg)
+ipv6nd_handledata(void *arg, unsigned short events)
{
struct dhcpcd_ctx *ctx;
int fd;
@@ -2013,6 +2148,10 @@ ipv6nd_handledata(void *arg)
ctx = arg;
fd = ctx->nd_fd;
#endif
+
+ if (events != ELE_READ)
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
+
len = recvmsg(fd, &msg, 0);
if (len == -1) {
logerr(__func__);
@@ -2073,3 +2212,43 @@ ipv6nd_startrs(struct interface *ifp)
eloop_timeout_add_msec(ifp->ctx->eloop, delay, ipv6nd_startrs1, ifp);
return;
}
+
+static struct routeinfo *routeinfo_findalloc(struct ra *rap, const struct in6_addr *prefix, uint8_t prefix_len)
+{
+ struct routeinfo *ri;
+ char buf[INET6_ADDRSTRLEN];
+ const char *p;
+
+ TAILQ_FOREACH(ri, &rap->rinfos, next) {
+ if (ri->prefix_len == prefix_len &&
+ IN6_ARE_ADDR_EQUAL(&ri->prefix, prefix))
+ return ri;
+ }
+
+ ri = malloc(sizeof(struct routeinfo));
+ if (ri == NULL)
+ return NULL;
+
+ memcpy(&ri->prefix, prefix, sizeof(ri->prefix));
+ ri->prefix_len = prefix_len;
+ p = inet_ntop(AF_INET6, prefix, buf, sizeof(buf));
+ if (p)
+ snprintf(ri->sprefix,
+ sizeof(ri->sprefix),
+ "%s/%d",
+ p, prefix_len);
+ else
+ ri->sprefix[0] = '\0';
+ TAILQ_INSERT_TAIL(&rap->rinfos, ri, next);
+ return ri;
+}
+
+static void routeinfohead_free(struct routeinfohead *head)
+{
+ struct routeinfo *ri;
+
+ while ((ri = TAILQ_FIRST(head))) {
+ TAILQ_REMOVE(head, ri, next);
+ free(ri);
+ }
+}
diff --git a/src/ipv6nd.h b/src/ipv6nd.h
index fd5990cfea97..837b7d0f1e3e 100644
--- a/src/ipv6nd.h
+++ b/src/ipv6nd.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - IPv6 ND handling
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -37,6 +37,20 @@
#include "dhcpcd.h"
#include "ipv6.h"
+/* rfc4191 */
+struct routeinfo {
+ TAILQ_ENTRY(routeinfo) next;
+ struct in6_addr prefix;
+ uint8_t prefix_len;
+ uint32_t lifetime;
+ uint8_t flags;
+ struct timespec acquired;
+ char sprefix[INET6_ADDRSTRLEN];
+};
+
+TAILQ_HEAD(routeinfohead, routeinfo);
+
+
struct ra {
TAILQ_ENTRY(ra) next;
struct interface *iface;
@@ -45,13 +59,14 @@ struct ra {
uint8_t *data;
size_t data_len;
struct timespec acquired;
- unsigned char flags;
+ uint8_t flags;
uint32_t lifetime;
uint32_t reachable;
uint32_t retrans;
uint32_t mtu;
uint8_t hoplimit;
struct ipv6_addrhead addrs;
+ struct routeinfohead rinfos;
bool hasdns;
bool expired;
bool willexpire;
@@ -98,12 +113,14 @@ struct rs_state {
#define RETRANS_TIMER 1000 /* milliseconds */
#define DELAY_FIRST_PROBE_TIME 5 /* seconds */
+#define MIN_EXTENDED_VLTIME 7200 /* seconds */
+
int ipv6nd_open(bool);
#ifdef __sun
int ipv6nd_openif(struct interface *);
#endif
void ipv6nd_recvmsg(struct dhcpcd_ctx *, struct msghdr *);
-int ipv6nd_rtpref(struct ra *);
+int ipv6nd_rtpref(uint8_t);
void ipv6nd_printoptions(const struct dhcpcd_ctx *,
const struct dhcp_opt *, size_t);
void ipv6nd_startrs(struct interface *);
diff --git a/src/logerr.c b/src/logerr.c
index 7a650e87f2c7..319d6f48e6ce 100644
--- a/src/logerr.c
+++ b/src/logerr.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* logerr: errx with logging
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -376,6 +376,8 @@ logsetfd(int fd)
struct logctx *ctx = &_logctx;
ctx->log_fd = fd;
+ if (fd != -1)
+ closelog();
#ifndef SMALL
if (fd != -1 && ctx->log_file != NULL) {
fclose(ctx->log_file);
diff --git a/src/logerr.h b/src/logerr.h
index ba7e4f6a6a01..6b2d49411363 100644
--- a/src/logerr.h
+++ b/src/logerr.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* logerr: errx with logging
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
diff --git a/src/privsep-bpf.c b/src/privsep-bpf.c
index f402ea183071..742865351abf 100644
--- a/src/privsep-bpf.c
+++ b/src/privsep-bpf.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Privilege Separation BPF Initiator
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -53,8 +53,10 @@
#include "logerr.h"
#include "privsep.h"
+/* We expect to have open 3 SEQPACKET and one RAW fd */
+
static void
-ps_bpf_recvbpf(void *arg)
+ps_bpf_recvbpf(void *arg, unsigned short events)
{
struct ps_process *psp = arg;
struct bpf *bpf = psp->psp_bpf;
@@ -65,6 +67,9 @@ ps_bpf_recvbpf(void *arg)
.ps_cmd = psp->psp_id.psi_cmd,
};
+ if (events != ELE_READ)
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
+
bpf->bpf_flags &= ~BPF_EOF;
/* A BPF read can read more than one filtered packet at time.
* This mechanism allows us to read each packet from the buffer. */
@@ -131,19 +136,18 @@ ps_bpf_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
}
static void
-ps_bpf_recvmsg(void *arg)
+ps_bpf_recvmsg(void *arg, unsigned short events)
{
struct ps_process *psp = arg;
- if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd,
+ if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events,
ps_bpf_recvmsgcb, arg) == -1)
logerr(__func__);
}
static int
-ps_bpf_start_bpf(void *arg)
+ps_bpf_start_bpf(struct ps_process *psp)
{
- struct ps_process *psp = arg;
struct dhcpcd_ctx *ctx = psp->psp_ctx;
char *addr;
struct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr;
@@ -158,14 +162,17 @@ ps_bpf_start_bpf(void *arg)
ps_freeprocesses(ctx, psp);
psp->psp_bpf = bpf_open(&psp->psp_ifp, psp->psp_filter, ia);
+#ifdef DEBUG_FD
+ logdebugx("pid %d bpf_fd=%d", getpid(), psp->psp_bpf->bpf_fd);
+#endif
if (psp->psp_bpf == NULL)
logerr("%s: bpf_open",__func__);
#ifdef PRIVSEP_RIGHTS
else if (ps_rights_limit_fd(psp->psp_bpf->bpf_fd) == -1)
logerr("%s: ps_rights_limit_fd", __func__);
#endif
- else if (eloop_event_add(ctx->eloop,
- psp->psp_bpf->bpf_fd, ps_bpf_recvbpf, psp) == -1)
+ else if (eloop_event_add(ctx->eloop, psp->psp_bpf->bpf_fd, ELE_READ,
+ ps_bpf_recvbpf, psp) == -1)
logerr("%s: eloop_event_add", __func__);
else {
psp->psp_work_fd = psp->psp_bpf->bpf_fd;
@@ -184,6 +191,8 @@ ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
pid_t start;
struct iovec *iov = msg->msg_iov;
struct interface *ifp;
+ struct in_addr *ia = &psm->ps_id.psi_addr.psa_in_addr;
+ const char *addr;
cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
psp = ps_findprocess(ctx, &psm->ps_id);
@@ -241,11 +250,16 @@ ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
break;
}
- start = ps_dostart(ctx,
- &psp->psp_pid, &psp->psp_fd,
- ps_bpf_recvmsg, NULL, psp,
- ps_bpf_start_bpf, NULL,
- PSF_DROPPRIVS);
+ if (ia->s_addr == INADDR_ANY)
+ addr = NULL;
+ else
+ addr = inet_ntoa(*ia);
+ snprintf(psp->psp_name, sizeof(psp->psp_name), "BPF %s%s%s",
+ psp->psp_protostr,
+ addr != NULL ? " " : "", addr != NULL ? addr : "");
+
+ start = ps_startprocess(psp, ps_bpf_recvmsg, NULL,
+ ps_bpf_start_bpf, NULL, PSF_DROPPRIVS);
switch (start) {
case -1:
ps_freeprocess(psp);
@@ -254,8 +268,8 @@ ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
ps_entersandbox("stdio", NULL);
break;
default:
- logdebugx("%s: spawned BPF %s on PID %d",
- psp->psp_ifname, psp->psp_protostr, start);
+ logdebugx("%s: spawned %s on PID %d",
+ psp->psp_ifname, psp->psp_name, psp->psp_pid);
break;
}
return start;
@@ -319,7 +333,7 @@ ps_bpf_send(const struct interface *ifp, const struct in_addr *ia,
if (ia != NULL)
psm.ps_id.psi_addr.psa_in_addr = *ia;
- return ps_sendpsmdata(ctx, ctx->ps_root_fd, &psm, data, len);
+ return ps_sendpsmdata(ctx, PS_ROOT_FD(ctx), &psm, data, len);
}
#ifdef ARP
diff --git a/src/privsep-bpf.h b/src/privsep-bpf.h
index 50c132379d24..882fd2984683 100644
--- a/src/privsep-bpf.h
+++ b/src/privsep-bpf.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Privilege Separation for dhcpcd
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
diff --git a/src/privsep-bsd.c b/src/privsep-bsd.c
index 22472625af52..755ad6d530d6 100644
--- a/src/privsep-bsd.c
+++ b/src/privsep-bsd.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Privilege Separation for dhcpcd, BSD driver
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -27,6 +27,8 @@
*/
#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
/* Need these for filtering the ioctls */
#include <arpa/inet.h>
@@ -51,17 +53,43 @@
#endif
#include <errno.h>
+#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "dhcpcd.h"
+#include "if.h"
#include "logerr.h"
#include "privsep.h"
static ssize_t
-ps_root_doioctldom(int domain, unsigned long req, void *data, size_t len)
+ps_root_doioctldom(struct dhcpcd_ctx *ctx, int domain, unsigned long req, void *data, size_t len)
{
- int s, err;
+#if defined(INET6) || (defined(SIOCALIFADDR) && defined(IFLR_ACTIVE))
+ struct priv *priv = (struct priv *)ctx->priv;
+#endif
+ int s;
+
+ switch(domain) {
+#ifdef INET
+ case PF_INET:
+ s = ctx->pf_inet_fd;
+ break;
+#endif
+#ifdef INET6
+ case PF_INET6:
+ s = priv->pf_inet6_fd;
+ break;
+#endif
+#if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */
+ case PF_LINK:
+ s = priv->pf_link_fd;
+ break;
+#endif
+ default:
+ errno = EPFNOSUPPORT;
+ return -1;
+ }
/* Only allow these ioctls */
switch(req) {
@@ -104,33 +132,20 @@ ps_root_doioctldom(int domain, unsigned long req, void *data, size_t len)
return -1;
}
- s = socket(domain, SOCK_DGRAM, 0);
- if (s == -1)
- return -1;
- err = ioctl(s, req, data, len);
- close(s);
- return err;
+ return ioctl(s, req, data, len);
}
static ssize_t
-ps_root_doroute(void *data, size_t len)
+ps_root_doroute(struct dhcpcd_ctx *ctx, void *data, size_t len)
{
- int s;
- ssize_t err;
- s = socket(PF_ROUTE, SOCK_RAW, 0);
- if (s != -1)
- err = write(s, data, len);
- else
- err = -1;
- if (s != -1)
- close(s);
- return err;
+ return write(ctx->link_fd, data, len);
}
#if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE)
static ssize_t
-ps_root_doindirectioctl(unsigned long req, void *data, size_t len)
+ps_root_doindirectioctl(struct dhcpcd_ctx *ctx,
+ unsigned long req, void *data, size_t len)
{
char *p = data;
struct ifreq ifr = { .ifr_flags = 0 };
@@ -147,33 +162,97 @@ ps_root_doindirectioctl(unsigned long req, void *data, size_t len)
memmove(data, p + IFNAMSIZ, len);
ifr.ifr_data = data;
- return ps_root_doioctldom(PF_INET, req, &ifr, sizeof(ifr));
+ return ps_root_doioctldom(ctx, PF_INET, req, &ifr, sizeof(ifr));
}
#endif
#ifdef HAVE_PLEDGE
static ssize_t
-ps_root_doifignoregroup(void *data, size_t len)
+ps_root_doifignoregroup(struct dhcpcd_ctx *ctx, void *data, size_t len)
{
- int s, err;
if (len == 0 || ((const char *)data)[len - 1] != '\0') {
errno = EINVAL;
return -1;
}
- s = socket(PF_INET, SOCK_DGRAM, 0);
- if (s == -1)
+ return if_ignoregroup(ctx->pf_inet_fd, data);
+}
+#endif
+
+#ifdef HAVE_CAPSICUM
+static ssize_t
+ps_root_dosysctl(unsigned long flags,
+ void *data, size_t len, void **rdata, size_t *rlen)
+{
+ char *p = data, *e = p + len;
+ int name[10];
+ unsigned int namelen;
+ void *oldp;
+ size_t *oldlenp, oldlen, nlen;
+ void *newp;
+ size_t newlen;
+ int err;
+
+ if (sizeof(namelen) >= len) {
+ errno = EINVAL;
+ return -1;
+ }
+ memcpy(&namelen, p, sizeof(namelen));
+ p += sizeof(namelen);
+ nlen = sizeof(*name) * namelen;
+ if (namelen > __arraycount(name)) {
+ errno = ENOBUFS;
+ return -1;
+ }
+ if (p + nlen > e) {
+ errno = EINVAL;
+ return -1;
+ }
+ memcpy(name, p, nlen);
+ p += nlen;
+ if (p + sizeof(oldlen) > e) {
+ errno = EINVAL;
+ return -1;
+ }
+ memcpy(&oldlen, p, sizeof(oldlen));
+ p += sizeof(oldlen);
+ if (p + sizeof(newlen) > e) {
+ errno = EINVAL;
return -1;
- err = if_ignoregroup(s, data);
- close(s);
+ }
+ memcpy(&newlen, p, sizeof(newlen));
+ p += sizeof(newlen);
+ if (p + newlen > e) {
+ errno = EINVAL;
+ return -1;
+ }
+ newp = newlen ? p : NULL;
+
+ if (flags & PS_SYSCTL_OLEN) {
+ *rlen = sizeof(oldlen) + oldlen;
+ *rdata = malloc(*rlen);
+ if (*rdata == NULL)
+ return -1;
+ oldlenp = (size_t *)*rdata;
+ *oldlenp = oldlen;
+ if (flags & PS_SYSCTL_ODATA)
+ oldp = (char *)*rdata + sizeof(oldlen);
+ else
+ oldp = NULL;
+ } else {
+ oldlenp = NULL;
+ oldp = NULL;
+ }
+
+ err = sysctl(name, namelen, oldp, oldlenp, newp, newlen);
return err;
}
#endif
ssize_t
-ps_root_os(struct ps_msghdr *psm, struct msghdr *msg,
- void **rdata, size_t *rlen)
+ps_root_os(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg,
+ void **rdata, size_t *rlen, bool *free_rdata)
{
struct iovec *iov = msg->msg_iov;
void *data = iov->iov_base;
@@ -182,21 +261,28 @@ ps_root_os(struct ps_msghdr *psm, struct msghdr *msg,
switch (psm->ps_cmd) {
case PS_IOCTLLINK:
- err = ps_root_doioctldom(PF_LINK, psm->ps_flags, data, len);
+ err = ps_root_doioctldom(ctx, PF_LINK, psm->ps_flags, data, len);
break;
case PS_IOCTL6:
- err = ps_root_doioctldom(PF_INET6, psm->ps_flags, data, len);
+ err = ps_root_doioctldom(ctx, PF_INET6, psm->ps_flags, data, len);
break;
case PS_ROUTE:
- return ps_root_doroute(data, len);
+ return ps_root_doroute(ctx, data, len);
#if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE)
case PS_IOCTLINDIRECT:
- err = ps_root_doindirectioctl(psm->ps_flags, data, len);
+ err = ps_root_doindirectioctl(ctx, psm->ps_flags, data, len);
break;
#endif
#ifdef HAVE_PLEDGE
case PS_IFIGNOREGRP:
- return ps_root_doifignoregroup(data, len);
+ return ps_root_doifignoregroup(ctx, data, len);
+#endif
+#ifdef HAVE_CAPSICUM
+ case PS_SYSCTL:
+ *free_rdata = true;
+ return ps_root_dosysctl(psm->ps_flags, data, len, rdata, rlen);
+#else
+ UNUSED(free_rdata);
#endif
default:
errno = ENOTSUP;
@@ -215,7 +301,7 @@ ps_root_ioctldom(struct dhcpcd_ctx *ctx, uint16_t domain, unsigned long request,
void *data, size_t len)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, domain,
+ if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), domain,
request, data, len) == -1)
return -1;
return ps_root_readerror(ctx, data, len);
@@ -241,7 +327,7 @@ ssize_t
ps_root_route(struct dhcpcd_ctx *ctx, void *data, size_t len)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_ROUTE, 0, data, len) == -1)
+ if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_ROUTE, 0, data, len) == -1)
return -1;
return ps_root_readerror(ctx, data, len);
}
@@ -260,19 +346,76 @@ ps_root_indirectioctl(struct dhcpcd_ctx *ctx, unsigned long request,
strlcpy(buf, ifname, IFNAMSIZ);
memcpy(buf + IFNAMSIZ, data, len);
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IOCTLINDIRECT,
+ if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_IOCTLINDIRECT,
request, buf, IFNAMSIZ + len) == -1)
return -1;
return ps_root_readerror(ctx, data, len);
}
+#endif
+#ifdef HAVE_PLEDGE
ssize_t
ps_root_ifignoregroup(struct dhcpcd_ctx *ctx, const char *ifname)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IFIGNOREGRP, 0,
+ if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_IFIGNOREGRP, 0,
ifname, strlen(ifname) + 1) == -1)
return -1;
return ps_root_readerror(ctx, NULL, 0);
}
#endif
+
+#ifdef HAVE_CAPSICUM
+ssize_t
+ps_root_sysctl(struct dhcpcd_ctx *ctx,
+ const int *name, unsigned int namelen,
+ void *oldp, size_t *oldlenp, const void *newp, size_t newlen)
+{
+ char buf[PS_BUFLEN], *p = buf;
+ unsigned long flags = 0;
+ size_t olen = (oldp && oldlenp) ? *oldlenp : 0, nolen;
+
+ if (sizeof(namelen) + (sizeof(*name) * namelen) +
+ sizeof(oldlenp) +
+ sizeof(newlen) + newlen > sizeof(buf))
+ {
+ errno = ENOBUFS;
+ return -1;
+ }
+
+ if (oldlenp)
+ flags |= PS_SYSCTL_OLEN;
+ if (oldp)
+ flags |= PS_SYSCTL_ODATA;
+ memcpy(p, &namelen, sizeof(namelen));
+ p += sizeof(namelen);
+ memcpy(p, name, sizeof(*name) * namelen);
+ p += sizeof(*name) * namelen;
+ memcpy(p, &olen, sizeof(olen));
+ p += sizeof(olen);
+ memcpy(p, &newlen, sizeof(newlen));
+ p += sizeof(newlen);
+ if (newlen) {
+ memcpy(p, newp, newlen);
+ p += newlen;
+ }
+
+ if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_SYSCTL,
+ flags, buf, (size_t)(p - buf)) == -1)
+ return -1;
+
+ if (ps_root_readerror(ctx, buf, sizeof(buf)) == -1)
+ return -1;
+
+ p = buf;
+ memcpy(&nolen, p, sizeof(nolen));
+ p += sizeof(nolen);
+ if (oldlenp) {
+ *oldlenp = nolen;
+ if (oldp && nolen <= olen)
+ memcpy(oldp, p, nolen);
+ }
+
+ return 0;
+}
+#endif
diff --git a/src/privsep-control.c b/src/privsep-control.c
index fe9bbbf22732..5bce41746d3f 100644
--- a/src/privsep-control.c
+++ b/src/privsep-control.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Privilege Separation for dhcpcd, control proxy
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -36,10 +36,12 @@
#include "logerr.h"
#include "privsep.h"
+/* We expect to have open 2 SEQPACKET, 2 STREAM and 2 file STREAM fds */
+
static int
-ps_ctl_startcb(void *arg)
+ps_ctl_startcb(struct ps_process *psp)
{
- struct dhcpcd_ctx *ctx = arg;
+ struct dhcpcd_ctx *ctx = psp->psp_ctx;
sa_family_t af;
if (ctx->options & DHCPCD_MANAGER) {
@@ -60,33 +62,16 @@ ps_ctl_startcb(void *arg)
af = AF_UNSPEC;
}
- ctx->ps_control_pid = getpid();
-
return control_start(ctx,
ctx->options & DHCPCD_MANAGER ? NULL : *ctx->ifv, af);
}
-static ssize_t
-ps_ctl_recvmsgcb(void *arg, struct ps_msghdr *psm, __unused struct msghdr *msg)
-{
- struct dhcpcd_ctx *ctx = arg;
-
- if (psm->ps_cmd != PS_CTL_EOF) {
- errno = ENOTSUP;
- return -1;
- }
-
- if (ctx->ps_control_client != NULL)
- ctx->ps_control_client = NULL;
- return 0;
-}
-
static void
-ps_ctl_recvmsg(void *arg)
+ps_ctl_recvmsg(void *arg, unsigned short events)
{
- struct dhcpcd_ctx *ctx = arg;
+ struct ps_process *psp = arg;
- if (ps_recvpsmsg(ctx, ctx->ps_control_fd, ps_ctl_recvmsgcb, ctx) == -1)
+ if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, NULL, NULL) == -1)
logerr(__func__);
}
@@ -144,14 +129,14 @@ ps_ctl_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
logerrx("%s: cannot handle another client", __func__);
return 0;
}
- fd = control_new(ctx, ctx->ps_control_data_fd, fd_flags);
+ fd = control_new(ctx, ctx->ps_ctl->psp_work_fd, fd_flags);
if (fd == NULL)
return -1;
ctx->ps_control_client = fd;
control_recvdata(fd, iov->iov_base, iov->iov_len);
break;
case PS_CTL_EOF:
- control_free(ctx->ps_control_client);
+ ctx->ps_control_client = NULL;
break;
default:
errno = ENOTSUP;
@@ -161,46 +146,57 @@ ps_ctl_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
}
static void
-ps_ctl_dodispatch(void *arg)
+ps_ctl_dodispatch(void *arg, unsigned short events)
{
- struct dhcpcd_ctx *ctx = arg;
+ struct ps_process *psp = arg;
- if (ps_recvpsmsg(ctx, ctx->ps_control_fd, ps_ctl_dispatch, ctx) == -1)
+ if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events,
+ ps_ctl_dispatch, psp->psp_ctx) == -1)
logerr(__func__);
}
static void
-ps_ctl_recv(void *arg)
+ps_ctl_recv(void *arg, unsigned short events)
{
struct dhcpcd_ctx *ctx = arg;
char buf[BUFSIZ];
ssize_t len;
- errno = 0;
- len = read(ctx->ps_control_data_fd, buf, sizeof(buf));
- if (len == -1 || len == 0) {
- logerr("%s: read", __func__);
- eloop_exit(ctx->eloop, EXIT_FAILURE);
- return;
+ if (!(events & (ELE_READ | ELE_HANGUP)))
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
+
+ if (events & ELE_READ) {
+ len = read(ctx->ps_ctl->psp_work_fd, buf, sizeof(buf));
+ if (len == -1)
+ logerr("%s: read", __func__);
+ else if (len == 0)
+ // FIXME: Why does this happen?
+ ;
+ else if (ctx->ps_control_client == NULL)
+ logerrx("%s: clientfd #%d disconnected (len=%zd)",
+ __func__, ctx->ps_ctl->psp_work_fd, len);
+ else {
+ errno = 0;
+ if (control_queue(ctx->ps_control_client,
+ buf, (size_t)len) == -1)
+ logerr("%s: control_queue", __func__);
+ }
}
- if (ctx->ps_control_client == NULL) /* client disconnected */
- return;
- errno = 0;
- if (control_queue(ctx->ps_control_client, buf, (size_t)len) == -1)
- logerr("%s: control_queue", __func__);
}
static void
-ps_ctl_listen(void *arg)
+ps_ctl_listen(void *arg, unsigned short events)
{
struct dhcpcd_ctx *ctx = arg;
char buf[BUFSIZ];
ssize_t len;
struct fd_list *fd;
- errno = 0;
+ if (!(events & ELE_READ))
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
+
len = read(ctx->ps_control->fd, buf, sizeof(buf));
- if (len == -1 || len == 0) {
+ if (len == -1) {
logerr("%s: read", __func__);
eloop_exit(ctx->eloop, EXIT_FAILURE);
return;
@@ -218,48 +214,55 @@ ps_ctl_listen(void *arg)
pid_t
ps_ctl_start(struct dhcpcd_ctx *ctx)
{
- int data_fd[2], listen_fd[2];
+ struct ps_id id = {
+ .psi_ifindex = 0,
+ .psi_cmd = PS_CTL,
+ };
+ struct ps_process *psp;
+ int work_fd[2], listen_fd[2];
pid_t pid;
- if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, data_fd) == -1 ||
+ if_closesockets(ctx);
+
+ if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, work_fd) == -1 ||
xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, listen_fd) == -1)
return -1;
#ifdef PRIVSEP_RIGHTS
- if (ps_rights_limit_fdpair(data_fd) == -1 ||
+ if (ps_rights_limit_fdpair(work_fd) == -1 ||
ps_rights_limit_fdpair(listen_fd) == -1)
return -1;
#endif
- pid = ps_dostart(ctx, &ctx->ps_control_pid, &ctx->ps_control_fd,
- ps_ctl_recvmsg, ps_ctl_dodispatch, ctx,
- ps_ctl_startcb, NULL,
- PSF_DROPPRIVS);
+ psp = ctx->ps_ctl = ps_newprocess(ctx, &id);
+ strlcpy(psp->psp_name, "control proxy", sizeof(psp->psp_name));
+ pid = ps_startprocess(psp, ps_ctl_recvmsg, ps_ctl_dodispatch,
+ ps_ctl_startcb, NULL, PSF_DROPPRIVS);
if (pid == -1)
return -1;
else if (pid != 0) {
- ctx->ps_control_data_fd = data_fd[1];
- close(data_fd[0]);
+ psp->psp_work_fd = work_fd[0];
+ close(work_fd[1]);
+ close(listen_fd[1]);
ctx->ps_control = control_new(ctx,
- listen_fd[1], FD_SENDLEN | FD_LISTEN);
+ listen_fd[0], FD_SENDLEN | FD_LISTEN);
if (ctx->ps_control == NULL)
return -1;
- close(listen_fd[0]);
return pid;
}
- ctx->ps_control_data_fd = data_fd[0];
- close(data_fd[1]);
- if (eloop_event_add(ctx->eloop, ctx->ps_control_data_fd,
+ close(work_fd[0]);
+ close(listen_fd[0]);
+
+ psp->psp_work_fd = work_fd[1];
+ if (eloop_event_add(ctx->eloop, psp->psp_work_fd, ELE_READ,
ps_ctl_recv, ctx) == -1)
return -1;
- ctx->ps_control = control_new(ctx,
- listen_fd[0], 0);
- close(listen_fd[1]);
+ ctx->ps_control = control_new(ctx, listen_fd[1], 0);
if (ctx->ps_control == NULL)
return -1;
- if (eloop_event_add(ctx->eloop, ctx->ps_control->fd,
+ if (eloop_event_add(ctx->eloop, ctx->ps_control->fd, ELE_READ,
ps_ctl_listen, ctx) == -1)
return -1;
@@ -271,7 +274,7 @@ int
ps_ctl_stop(struct dhcpcd_ctx *ctx)
{
- return ps_dostop(ctx, &ctx->ps_control_pid, &ctx->ps_control_fd);
+ return ps_stopprocess(ctx->ps_ctl);
}
ssize_t
@@ -282,7 +285,7 @@ ps_ctl_sendargs(struct fd_list *fd, void *data, size_t len)
if (ctx->ps_control_client != NULL && ctx->ps_control_client != fd)
logerrx("%s: cannot deal with another client", __func__);
ctx->ps_control_client = fd;
- return ps_sendcmd(ctx, ctx->ps_control_fd, PS_CTL,
+ return ps_sendcmd(ctx, ctx->ps_ctl->psp_fd, PS_CTL,
fd->flags & FD_UNPRIV ? PS_CTL_UNPRIV : PS_CTL_PRIV,
data, len);
}
@@ -292,5 +295,5 @@ ps_ctl_sendeof(struct fd_list *fd)
{
struct dhcpcd_ctx *ctx = fd->ctx;
- return ps_sendcmd(ctx, ctx->ps_control_fd, PS_CTL_EOF, 0, NULL, 0);
+ return ps_sendcmd(ctx, ctx->ps_ctl->psp_fd, PS_CTL_EOF, 0, NULL, 0);
}
diff --git a/src/privsep-control.h b/src/privsep-control.h
index 8f01b536cf85..b6f76aafd4a6 100644
--- a/src/privsep-control.h
+++ b/src/privsep-control.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Privilege Separation for dhcpcd
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
diff --git a/src/privsep-inet.c b/src/privsep-inet.c
index 3a192ee0c461..919fe9540b0a 100644
--- a/src/privsep-inet.c
+++ b/src/privsep-inet.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Privilege Separation for dhcpcd, network proxy
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -47,32 +47,37 @@
#include "logerr.h"
#include "privsep.h"
+/* We expect to have open 2 SEQPACKET, 1 udp, 1 udp6 and 1 raw6 fds */
+
#ifdef INET
static void
-ps_inet_recvbootp(void *arg)
+ps_inet_recvbootp(void *arg, unsigned short events)
{
struct dhcpcd_ctx *ctx = arg;
- if (ps_recvmsg(ctx, ctx->udp_rfd, PS_BOOTP, ctx->ps_inet_fd) == -1)
+ if (ps_recvmsg(ctx->udp_rfd, events,
+ PS_BOOTP, ctx->ps_inet->psp_fd) == -1)
logerr(__func__);
}
#endif
#ifdef INET6
static void
-ps_inet_recvra(void *arg)
+ps_inet_recvra(void *arg, unsigned short events)
{
#ifdef __sun
struct interface *ifp = arg;
struct rs_state *state = RS_STATE(ifp);
struct dhcpcd_ctx *ctx = ifp->ctx;
- if (ps_recvmsg(ctx, state->nd_fd, PS_ND, ctx->ps_inet_fd) == -1)
+ if (ps_recvmsg(state->nd_fd, events,
+ PS_ND, ctx->ps_inet->psp_fd) == -1)
logerr(__func__);
#else
struct dhcpcd_ctx *ctx = arg;
- if (ps_recvmsg(ctx, ctx->nd_fd, PS_ND, ctx->ps_inet_fd) == -1)
+ if (ps_recvmsg(ctx->nd_fd, events,
+ PS_ND, ctx->ps_inet->psp_fd) == -1)
logerr(__func__);
#endif
}
@@ -80,11 +85,12 @@ ps_inet_recvra(void *arg)
#ifdef DHCP6
static void
-ps_inet_recvdhcp6(void *arg)
+ps_inet_recvdhcp6(void *arg, unsigned short events)
{
struct dhcpcd_ctx *ctx = arg;
- if (ps_recvmsg(ctx, ctx->dhcp6_rfd, PS_DHCP6, ctx->ps_inet_fd) == -1)
+ if (ps_recvmsg(ctx->dhcp6_rfd, events,
+ PS_DHCP6, ctx->ps_inet->psp_fd) == -1)
logerr(__func__);
}
#endif
@@ -112,16 +118,16 @@ ps_inet_canstart(const struct dhcpcd_ctx *ctx)
}
static int
-ps_inet_startcb(void *arg)
+ps_inet_startcb(struct ps_process *psp)
{
- struct dhcpcd_ctx *ctx = arg;
+ struct dhcpcd_ctx *ctx = psp->psp_ctx;
int ret = 0;
if (ctx->options & DHCPCD_MANAGER)
setproctitle("[network proxy]");
else
setproctitle("[network proxy] %s%s%s",
- ctx->ifv[0],
+ ctx->ifc != 0 ? ctx->ifv[0] : "",
ctx->options & DHCPCD_IPV4 ? " [ip4]" : "",
ctx->options & DHCPCD_IPV6 ? " [ip6]" : "");
@@ -145,7 +151,7 @@ ps_inet_startcb(void *arg)
ctx->udp_rfd = -1;
}
#endif
- else if (eloop_event_add(ctx->eloop, ctx->udp_rfd,
+ else if (eloop_event_add(ctx->eloop, ctx->udp_rfd, ELE_READ,
ps_inet_recvbootp, ctx) == -1)
{
logerr("%s: eloop_event_add DHCP", __func__);
@@ -167,7 +173,7 @@ ps_inet_startcb(void *arg)
ctx->nd_fd = -1;
}
#endif
- else if (eloop_event_add(ctx->eloop, ctx->nd_fd,
+ else if (eloop_event_add(ctx->eloop, ctx->nd_fd, ELE_READ,
ps_inet_recvra, ctx) == -1)
{
logerr("%s: eloop_event_add RA", __func__);
@@ -191,7 +197,7 @@ ps_inet_startcb(void *arg)
ctx->dhcp6_rfd = -1;
}
#endif
- else if (eloop_event_add(ctx->eloop, ctx->dhcp6_rfd,
+ else if (eloop_event_add(ctx->eloop, ctx->dhcp6_rfd, ELE_READ,
ps_inet_recvdhcp6, ctx) == -1)
{
logerr("%s: eloop_event_add DHCP6", __func__);
@@ -299,12 +305,12 @@ dosend:
}
static void
-ps_inet_recvmsg(void *arg)
+ps_inet_recvmsg(void *arg, unsigned short events)
{
- struct dhcpcd_ctx *ctx = arg;
+ struct ps_process *psp = arg;
/* Receive shutdown */
- if (ps_recvpsmsg(ctx, ctx->ps_inet_fd, NULL, NULL) == -1)
+ if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, NULL, NULL) == -1)
logerr(__func__);
}
@@ -337,23 +343,32 @@ ps_inet_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
}
static void
-ps_inet_dodispatch(void *arg)
+ps_inet_dodispatch(void *arg, unsigned short events)
{
- struct dhcpcd_ctx *ctx = arg;
+ struct ps_process *psp = arg;
- if (ps_recvpsmsg(ctx, ctx->ps_inet_fd, ps_inet_dispatch, ctx) == -1)
+ if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events,
+ ps_inet_dispatch, psp->psp_ctx) == -1)
logerr(__func__);
}
pid_t
ps_inet_start(struct dhcpcd_ctx *ctx)
{
+ struct ps_id id = {
+ .psi_ifindex = 0,
+ .psi_cmd = PS_INET,
+ };
+ struct ps_process *psp;
pid_t pid;
- pid = ps_dostart(ctx, &ctx->ps_inet_pid, &ctx->ps_inet_fd,
- ps_inet_recvmsg, ps_inet_dodispatch, ctx,
- ps_inet_startcb, NULL,
- PSF_DROPPRIVS);
+ psp = ctx->ps_inet = ps_newprocess(ctx, &id);
+ if (psp == NULL)
+ return -1;
+
+ strlcpy(psp->psp_name, "network proxy", sizeof(psp->psp_name));
+ pid = ps_startprocess(psp, ps_inet_recvmsg, ps_inet_dodispatch,
+ ps_inet_startcb, NULL, PSF_DROPPRIVS);
if (pid == 0)
ps_entersandbox("stdio", NULL);
@@ -365,29 +380,28 @@ int
ps_inet_stop(struct dhcpcd_ctx *ctx)
{
- return ps_dostop(ctx, &ctx->ps_inet_pid, &ctx->ps_inet_fd);
+ return ps_stopprocess(ctx->ps_inet);
}
#ifdef INET
static void
-ps_inet_recvinbootp(void *arg)
+ps_inet_recvinbootp(void *arg, unsigned short events)
{
struct ps_process *psp = arg;
- if (ps_recvmsg(psp->psp_ctx, psp->psp_work_fd,
+ if (ps_recvmsg(psp->psp_work_fd, events,
PS_BOOTP, psp->psp_ctx->ps_data_fd) == -1)
logerr(__func__);
}
static int
-ps_inet_listenin(void *arg)
+ps_inet_listenin(struct ps_process *psp)
{
- struct ps_process *psp = arg;
struct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr;
char buf[INET_ADDRSTRLEN];
inet_ntop(AF_INET, ia, buf, sizeof(buf));
- setproctitle("[network proxy] %s", buf);
+ setproctitle("[%s proxy] %s", psp->psp_protostr, buf);
psp->psp_work_fd = dhcp_openudp(ia);
if (psp->psp_work_fd == -1) {
@@ -402,14 +416,12 @@ ps_inet_listenin(void *arg)
}
#endif
- if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd,
+ if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd, ELE_READ,
ps_inet_recvinbootp, psp) == -1)
{
logerr("%s: eloop_event_add DHCP", __func__);
return -1;
}
-
- logdebugx("spawned listener %s on PID %d", buf, getpid());
return 0;
}
#endif
@@ -420,15 +432,14 @@ ps_inet_recvin6nd(void *arg)
{
struct ps_process *psp = arg;
- if (ps_recvmsg(psp->psp_ctx, psp->psp_work_fd,
+ if (ps_recvmsg(psp->psp_work_fd,
PS_ND, psp->psp_ctx->ps_data_fd) == -1)
logerr(__func__);
}
static int
-ps_inet_listennd(void *arg)
+ps_inet_listennd(struct ps_process *psp)
{
- struct ps_process *psp = arg;
setproctitle("[ND network proxy]");
@@ -451,32 +462,29 @@ ps_inet_listennd(void *arg)
logerr(__func__);
return -1;
}
-
- logdebugx("spawned ND listener on PID %d", getpid());
return 0;
}
#endif
#ifdef DHCP6
static void
-ps_inet_recvin6dhcp6(void *arg)
+ps_inet_recvin6dhcp6(void *arg, unsigned short events)
{
struct ps_process *psp = arg;
- if (ps_recvmsg(psp->psp_ctx, psp->psp_work_fd,
+ if (ps_recvmsg(psp->psp_work_fd, events,
PS_DHCP6, psp->psp_ctx->ps_data_fd) == -1)
logerr(__func__);
}
static int
-ps_inet_listenin6(void *arg)
+ps_inet_listenin6(struct ps_process *psp)
{
- struct ps_process *psp = arg;
struct in6_addr *ia = &psp->psp_id.psi_addr.psa_in6_addr;
char buf[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, ia, buf, sizeof(buf));
- setproctitle("[network proxy] %s", buf);
+ setproctitle("[%s proxy] %s", psp->psp_protostr, buf);
psp->psp_work_fd = dhcp6_openudp(psp->psp_id.psi_ifindex, ia);
if (psp->psp_work_fd == -1) {
@@ -491,25 +499,23 @@ ps_inet_listenin6(void *arg)
}
#endif
- if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd,
+ if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd, ELE_READ,
ps_inet_recvin6dhcp6, psp) == -1)
{
logerr("%s: eloop_event_add DHCP", __func__);
return -1;
}
-
- logdebugx("spawned listener %s on PID %d", buf, getpid());
return 0;
}
#endif
static void
-ps_inet_recvmsgpsp(void *arg)
+ps_inet_recvmsgpsp(void *arg, unsigned short events)
{
struct ps_process *psp = arg;
/* Receive shutdown. */
- if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, NULL, NULL) == -1)
+ if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, NULL, NULL) == -1)
logerr(__func__);
}
@@ -518,8 +524,11 @@ ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
{
uint16_t cmd;
struct ps_process *psp;
- int (*start_func)(void *);
+ int (*start_func)(struct ps_process *);
pid_t start;
+ struct ps_addr *psa = &psm->ps_id.psi_addr;
+ void *ia;
+ char buf[INET_MAX_ADDRSTRLEN];
cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
if (cmd == psm->ps_cmd)
@@ -536,21 +545,40 @@ ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
return 0;
}
+ if (!(psm->ps_cmd & PS_START)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (psp != NULL)
+ return 1;
+
+ psp = ps_newprocess(ctx, &psm->ps_id);
+ if (psp == NULL)
+ return -1;
+
+
switch (cmd) {
#ifdef INET
case PS_BOOTP:
start_func = ps_inet_listenin;
+ psp->psp_protostr = "BOOTP";
+ ia = &psa->psa_in_addr;
break;
#endif
#ifdef INET6
#ifdef __sun
case PS_ND:
start_func = ps_inet_listennd;
+ psp->psp_protostr = "ND";
+ ia = &psa->psa_in6_addr;
break;
#endif
#ifdef DHCP6
case PS_DHCP6:
start_func = ps_inet_listenin6;
+ psp->psp_protostr = "DHCP6";
+ ia = &psa->psa_in6_addr;
break;
#endif
#endif
@@ -560,23 +588,11 @@ ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
return -1;
}
- if (!(psm->ps_cmd & PS_START)) {
- errno = EINVAL;
- return -1;
- }
-
- if (psp != NULL)
- return 1;
-
- psp = ps_newprocess(ctx, &psm->ps_id);
- if (psp == NULL)
- return -1;
-
- start = ps_dostart(ctx,
- &psp->psp_pid, &psp->psp_fd,
- ps_inet_recvmsgpsp, NULL, psp,
- start_func, NULL,
- PSF_DROPPRIVS);
+ snprintf(psp->psp_name, sizeof(psp->psp_name),
+ "%s proxy %s", psp->psp_protostr,
+ inet_ntop(psa->psa_family, ia, buf, sizeof(buf)));
+ start = ps_startprocess(psp, ps_inet_recvmsgpsp, NULL,
+ start_func, NULL, PSF_DROPPRIVS);
switch (start) {
case -1:
ps_freeprocess(psp);
@@ -585,6 +601,8 @@ ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
ps_entersandbox("stdio", NULL);
break;
default:
+ logdebugx("%s: spawned %s on PID %d",
+ psp->psp_ifname, psp->psp_name, psp->psp_pid);
break;
}
return start;
@@ -601,11 +619,12 @@ ps_inet_in_docmd(struct ipv4_addr *ia, uint16_t cmd, const struct msghdr *msg)
.ps_id = {
.psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),
.psi_ifindex = ia->iface->index,
+ .psi_addr.psa_family = AF_INET,
.psi_addr.psa_in_addr = ia->addr,
},
};
- return ps_sendpsmmsg(ctx, ctx->ps_root_fd, &psm, msg);
+ return ps_sendpsmmsg(ctx, PS_ROOT_FD(ctx), &psm, msg);
}
ssize_t
@@ -625,8 +644,9 @@ ps_inet_closebootp(struct ipv4_addr *ia)
ssize_t
ps_inet_sendbootp(struct interface *ifp, const struct msghdr *msg)
{
+ struct dhcpcd_ctx *ctx = ifp->ctx;
- return ps_sendmsg(ifp->ctx, ifp->ctx->ps_root_fd, PS_BOOTP, 0, msg);
+ return ps_sendmsg(ctx, PS_ROOT_FD(ctx), PS_BOOTP, 0, msg);
}
#endif /* INET */
@@ -641,10 +661,11 @@ ps_inet_ifp_docmd(struct interface *ifp, uint16_t cmd, const struct msghdr *msg)
.ps_id = {
.psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),
.psi_ifindex = ifp->index,
+ .psi_addr.psa_family = AF_INET6,
},
};
- return ps_sendpsmmsg(ctx, ctx->ps_root_fd, &psm, msg);
+ return ps_sendpsmmsg(ctx, PS_ROOT_FD(ctx), &psm, msg);
}
ssize_t
@@ -671,8 +692,9 @@ ps_inet_sendnd(struct interface *ifp, const struct msghdr *msg)
ssize_t
ps_inet_sendnd(struct interface *ifp, const struct msghdr *msg)
{
+ struct dhcpcd_ctx *ctx = ifp->ctx;
- return ps_sendmsg(ifp->ctx, ifp->ctx->ps_root_fd, PS_ND, 0, msg);
+ return ps_sendmsg(ctx, PS_ROOT_FD(ctx), PS_ND, 0, msg);
}
#endif
@@ -686,11 +708,12 @@ ps_inet_in6_docmd(struct ipv6_addr *ia, uint16_t cmd, const struct msghdr *msg)
.ps_id = {
.psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),
.psi_ifindex = ia->iface->index,
+ .psi_addr.psa_family = AF_INET6,
.psi_addr.psa_in6_addr = ia->addr,
},
};
- return ps_sendpsmmsg(ctx, ctx->ps_root_fd, &psm, msg);
+ return ps_sendpsmmsg(ctx, PS_ROOT_FD(ctx), &psm, msg);
}
ssize_t
@@ -710,8 +733,9 @@ ps_inet_closedhcp6(struct ipv6_addr *ia)
ssize_t
ps_inet_senddhcp6(struct interface *ifp, const struct msghdr *msg)
{
+ struct dhcpcd_ctx *ctx = ifp->ctx;
- return ps_sendmsg(ifp->ctx, ifp->ctx->ps_root_fd, PS_DHCP6, 0, msg);
+ return ps_sendmsg(ctx, PS_ROOT_FD(ctx), PS_DHCP6, 0, msg);
}
#endif /* DHCP6 */
#endif /* INET6 */
diff --git a/src/privsep-inet.h b/src/privsep-inet.h
index dc4a072d8d72..7da47dd2d3f2 100644
--- a/src/privsep-inet.h
+++ b/src/privsep-inet.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Privilege Separation for dhcpcd
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
diff --git a/src/privsep-linux.c b/src/privsep-linux.c
index ee2e22d2e95d..a40f2979238e 100644
--- a/src/privsep-linux.c
+++ b/src/privsep-linux.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Privilege Separation for dhcpcd, Linux driver
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -30,7 +30,6 @@
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/syscall.h>
-#include <sys/termios.h> /* For TCGETS */
#include <linux/audit.h>
#include <linux/filter.h>
@@ -40,10 +39,12 @@
#include <errno.h>
#include <fcntl.h>
+#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <termios.h> /* For TCGETS */
#include <unistd.h>
#include "common.h"
@@ -60,39 +61,42 @@
//#define SECCOMP_FILTER_DEBUG
static ssize_t
-ps_root_dosendnetlink(int protocol, struct msghdr *msg)
+ps_root_dosendnetlink(struct dhcpcd_ctx *ctx, int protocol, struct msghdr *msg)
{
- struct sockaddr_nl snl = { .nl_family = AF_NETLINK };
+ struct priv *priv = (struct priv *)ctx->priv;
int s;
unsigned char buf[16 * 1024];
struct iovec riov = {
.iov_base = buf,
.iov_len = sizeof(buf),
};
- ssize_t retval;
- if ((s = if_linksocket(&snl, protocol, 0)) == -1)
+ switch(protocol) {
+ case NETLINK_GENERIC:
+ s = priv->generic_fd;
+ break;
+ case NETLINK_ROUTE:
+ s = priv->route_fd;
+ break;
+ default:
+ errno = EPFNOSUPPORT;
return -1;
-
- if (sendmsg(s, msg, 0) == -1) {
- retval = -1;
- goto out;
}
- retval = if_getnetlink(NULL, &riov, s, 0, NULL, NULL);
-out:
- close(s);
- return retval;
+ if (sendmsg(s, msg, 0) == -1)
+ return -1;
+
+ return if_getnetlink(NULL, &riov, s, 0, NULL, NULL);
}
ssize_t
-ps_root_os(struct ps_msghdr *psm, struct msghdr *msg,
- __unused void **rdata, __unused size_t *rlen)
+ps_root_os(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg,
+ __unused void **rdata, __unused size_t *rlen, __unused bool *free_data)
{
switch (psm->ps_cmd) {
case PS_ROUTE:
- return ps_root_dosendnetlink((int)psm->ps_flags, msg);
+ return ps_root_dosendnetlink(ctx, (int)psm->ps_flags, msg);
default:
errno = ENOTSUP;
return -1;
@@ -103,12 +107,16 @@ ssize_t
ps_root_sendnetlink(struct dhcpcd_ctx *ctx, int protocol, struct msghdr *msg)
{
- if (ps_sendmsg(ctx, ctx->ps_root_fd, PS_ROUTE,
+ if (ps_sendmsg(ctx, PS_ROOT_FD(ctx), PS_ROUTE,
(unsigned long)protocol, msg) == -1)
return -1;
return ps_root_readerror(ctx, NULL, 0);
}
+#ifdef DISABLE_SECCOMP
+#warning SECCOMP has been disabled
+#else
+
#if (BYTE_ORDER == LITTLE_ENDIAN)
# define SECCOMP_ARG_LO 0
# define SECCOMP_ARG_HI sizeof(uint32_t)
@@ -165,6 +173,16 @@ ps_root_sendnetlink(struct dhcpcd_ctx *ctx, int protocol, struct msghdr *msg)
# else
# error "Platform does not support seccomp filter yet"
# endif
+#elif defined(__ARCV3__)
+# if defined(__ARC64__)
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARCV3
+# else
+# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARCV3BE
+# endif
+# else
+# error "Platform does not support seccomp filter yet"
+# endif
#elif defined(__arm__)
# ifndef EM_ARM
# define EM_ARM 40
@@ -186,6 +204,12 @@ ps_root_sendnetlink(struct dhcpcd_ctx *ctx, int protocol, struct msghdr *msg)
# endif
#elif defined(__ia64__)
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_IA64
+#elif defined(__loongarch__)
+# if defined(__LP64__)
+# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_LOONGARCH64
+# else
+# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_LOONGARCH32
+# endif
#elif defined(__microblaze__)
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MICROBLAZE
#elif defined(__m68k__)
@@ -213,7 +237,11 @@ ps_root_sendnetlink(struct dhcpcd_ctx *ctx, int protocol, struct msghdr *msg)
#elif defined(__or1k__)
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_OPENRISC
#elif defined(__powerpc64__)
-# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_PPC64
+# if (BYTE_ORDER == LITTLE_ENDIAN)
+# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_PPC64LE
+# else
+# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_PPC64
+# endif
#elif defined(__powerpc__)
# define SECCOMP_AUDIT_ARCH AUDIT_ARCH_PPC
#elif defined(__riscv)
@@ -273,12 +301,30 @@ static struct sock_filter ps_seccomp_filter[] = {
#if defined(__x86_64__) && defined(__ILP32__) && defined(__X32_SYSCALL_BIT)
SECCOMP_ALLOW(__NR_clock_gettime & ~__X32_SYSCALL_BIT),
#endif
+#ifdef __NR_clock_gettime32
+ SECCOMP_ALLOW(__NR_clock_gettime32),
+#endif
#ifdef __NR_clock_gettime64
SECCOMP_ALLOW(__NR_clock_gettime64),
#endif
#ifdef __NR_close
SECCOMP_ALLOW(__NR_close),
#endif
+#ifdef __NR_dup2
+ SECCOMP_ALLOW(__NR_dup2), // daemonising dups stderr to stdin(/dev/null)
+#endif
+#ifdef __NR_dup3
+ SECCOMP_ALLOW(__NR_dup3),
+#endif
+#ifdef __NR_epoll_ctl
+ SECCOMP_ALLOW(__NR_epoll_ctl),
+#endif
+#ifdef __NR_epoll_wait
+ SECCOMP_ALLOW(__NR_epoll_wait),
+#endif
+#ifdef __NR_epoll_pwait
+ SECCOMP_ALLOW(__NR_epoll_pwait),
+#endif
#ifdef __NR_exit_group
SECCOMP_ALLOW(__NR_exit_group),
#endif
@@ -300,6 +346,9 @@ static struct sock_filter ps_seccomp_filter[] = {
#ifdef __NR_getpid
SECCOMP_ALLOW(__NR_getpid),
#endif
+#ifdef __NR_getrandom
+ SECCOMP_ALLOW(__NR_getrandom),
+#endif
#ifdef __NR_getsockopt
/* For route socket overflow */
SECCOMP_ALLOW_ARG(__NR_getsockopt, 1, SOL_SOCKET),
@@ -313,17 +362,25 @@ static struct sock_filter ps_seccomp_filter[] = {
SECCOMP_ALLOW_ARG(__NR_ioctl, 1, SIOCGIFVLAN),
/* printf over serial terminal requires this */
SECCOMP_ALLOW_ARG(__NR_ioctl, 1, TCGETS),
+ /* dumping leases on musl requires this */
+ SECCOMP_ALLOW_ARG(__NR_ioctl, 1, TIOCGWINSZ),
/* SECCOMP BPF is newer than nl80211 so we don't need SIOCGIWESSID
* which lives in the impossible to include linux/wireless.h header */
#endif
+#ifdef __NR_madvise /* needed for musl */
+ SECCOMP_ALLOW(__NR_madvise),
+#endif
#ifdef __NR_mmap
SECCOMP_ALLOW(__NR_mmap),
#endif
+#ifdef __NR_mmap2
+ SECCOMP_ALLOW(__NR_mmap2),
+#endif
#ifdef __NR_munmap
SECCOMP_ALLOW(__NR_munmap),
#endif
-#ifdef __NR_nanosleep
- SECCOMP_ALLOW(__NR_nanosleep), /* XXX should use ppoll instead */
+#ifdef __NR_newfstatat
+ SECCOMP_ALLOW(__NR_newfstatat),
#endif
#ifdef __NR_ppoll
SECCOMP_ALLOW(__NR_ppoll),
@@ -331,6 +388,12 @@ static struct sock_filter ps_seccomp_filter[] = {
#ifdef __NR_ppoll_time64
SECCOMP_ALLOW(__NR_ppoll_time64),
#endif
+#ifdef __NR_pselect6
+ SECCOMP_ALLOW(__NR_pselect6),
+#endif
+#ifdef __NR_pselect6_time64
+ SECCOMP_ALLOW(__NR_pselect6_time64),
+#endif
#ifdef __NR_read
SECCOMP_ALLOW(__NR_read),
#endif
@@ -453,3 +516,4 @@ ps_seccomp_enter(void)
}
return 0;
}
+#endif /* !DISABLE_SECCOMP */
diff --git a/src/privsep-root.c b/src/privsep-root.c
index 45af3910feed..ed4bb699769e 100644
--- a/src/privsep-root.c
+++ b/src/privsep-root.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Privilege Separation for dhcpcd, privileged proxy
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -74,7 +74,7 @@ struct psr_ctx {
};
static void
-ps_root_readerrorcb(void *arg)
+ps_root_readerrorcb(void *arg, unsigned short events)
{
struct psr_ctx *psr_ctx = arg;
struct dhcpcd_ctx *ctx = psr_ctx->psr_ctx;
@@ -87,6 +87,9 @@ ps_root_readerrorcb(void *arg)
ssize_t len;
int exit_code = EXIT_FAILURE;
+ if (events != ELE_READ)
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
+
#define PSR_ERROR(e) \
do { \
psr_error->psr_result = -1; \
@@ -94,7 +97,7 @@ ps_root_readerrorcb(void *arg)
goto out; \
} while (0 /* CONSTCOND */)
- len = readv(ctx->ps_root_fd, iov, __arraycount(iov));
+ len = readv(PS_ROOT_FD(ctx), iov, __arraycount(iov));
if (len == -1)
PSR_ERROR(errno);
else if ((size_t)len < sizeof(*psr_error))
@@ -112,13 +115,15 @@ ps_root_readerror(struct dhcpcd_ctx *ctx, void *data, size_t len)
.psr_ctx = ctx,
.psr_data = data, .psr_datalen = len,
};
+ int fd = PS_ROOT_FD(ctx);
- if (eloop_event_add(ctx->ps_eloop, ctx->ps_root_fd,
+ if (eloop_event_add(ctx->ps_eloop, fd, ELE_READ,
ps_root_readerrorcb, &psr_ctx) == -1)
return -1;
eloop_enter(ctx->ps_eloop);
eloop_start(ctx->ps_eloop, &ctx->sigset);
+ eloop_event_delete(ctx->ps_eloop, fd);
errno = psr_ctx.psr_error.psr_errno;
return psr_ctx.psr_error.psr_result;
@@ -126,7 +131,7 @@ ps_root_readerror(struct dhcpcd_ctx *ctx, void *data, size_t len)
#ifdef PRIVSEP_GETIFADDRS
static void
-ps_root_mreaderrorcb(void *arg)
+ps_root_mreaderrorcb(void *arg, unsigned short events)
{
struct psr_ctx *psr_ctx = arg;
struct dhcpcd_ctx *ctx = psr_ctx->psr_ctx;
@@ -138,7 +143,10 @@ ps_root_mreaderrorcb(void *arg)
ssize_t len;
int exit_code = EXIT_FAILURE;
- len = recv(ctx->ps_root_fd, psr_error, sizeof(*psr_error), MSG_PEEK);
+ if (events != ELE_READ)
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
+
+ len = recv(PS_ROOT_FD(ctx), psr_error, sizeof(*psr_error), MSG_PEEK);
if (len == -1)
PSR_ERROR(errno);
else if ((size_t)len < sizeof(*psr_error))
@@ -155,7 +163,7 @@ ps_root_mreaderrorcb(void *arg)
iov[1].iov_len = psr_ctx->psr_datalen;
}
- len = readv(ctx->ps_root_fd, iov, __arraycount(iov));
+ len = readv(PS_ROOT_FD(ctx), iov, __arraycount(iov));
if (len == -1)
PSR_ERROR(errno);
else if ((size_t)len != sizeof(*psr_error) + psr_ctx->psr_datalen)
@@ -172,13 +180,15 @@ ps_root_mreaderror(struct dhcpcd_ctx *ctx, void **data, size_t *len)
struct psr_ctx psr_ctx = {
.psr_ctx = ctx,
};
+ int fd = PS_ROOT_FD(ctx);
- if (eloop_event_add(ctx->ps_eloop, ctx->ps_root_fd,
+ if (eloop_event_add(ctx->ps_eloop, fd, ELE_READ,
ps_root_mreaderrorcb, &psr_ctx) == -1)
return -1;
eloop_enter(ctx->ps_eloop);
eloop_start(ctx->ps_eloop, &ctx->sigset);
+ eloop_event_delete(ctx->ps_eloop, fd);
errno = psr_ctx.psr_error.psr_errno;
*data = psr_ctx.psr_data;
@@ -200,12 +210,27 @@ ps_root_writeerror(struct dhcpcd_ctx *ctx, ssize_t result,
{ .iov_base = &psr, .iov_len = sizeof(psr) },
{ .iov_base = data, .iov_len = len },
};
+ ssize_t err;
+ int fd = PS_ROOT_FD(ctx);
#ifdef PRIVSEP_DEBUG
logdebugx("%s: result %zd errno %d", __func__, result, errno);
#endif
- return writev(ctx->ps_root_fd, iov, __arraycount(iov));
+ err = writev(fd, iov, __arraycount(iov));
+
+ /* Error sending the message? Try sending the error of sending. */
+ if (err == -1) {
+ logerr("%s: result=%zd, data=%p, len=%zu",
+ __func__, result, data, len);
+ psr.psr_result = err;
+ psr.psr_errno = errno;
+ iov[1].iov_base = NULL;
+ iov[1].iov_len = 0;
+ err = writev(fd, iov, __arraycount(iov));
+ }
+
+ return err;
}
static ssize_t
@@ -234,7 +259,7 @@ ps_root_doioctl(unsigned long req, void *data, size_t len)
return -1;
}
- s = socket(PF_INET, SOCK_DGRAM, 0);
+ s = xsocket(PF_INET, SOCK_DGRAM, 0);
if (s != -1)
#ifdef IOCTL_REQUEST_TYPE
{
@@ -270,6 +295,7 @@ ps_root_run_script(struct dhcpcd_ctx *ctx, const void *data, size_t len)
pid = script_exec(argv, ctx->script_env);
if (pid == -1)
return -1;
+
/* Wait for the script to finish */
while (waitpid(pid, &status, 0) == -1) {
if (errno != EINTR) {
@@ -469,12 +495,13 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
if (psp != NULL) {
if (psm->ps_cmd & PS_STOP) {
- int ret = ps_dostop(ctx, &psp->psp_pid, &psp->psp_fd);
-
- ps_freeprocess(psp);
- return ret;
+ return ps_stopprocess(psp);
} else if (psm->ps_cmd & PS_START) {
/* Process has already started .... */
+ logdebugx("%s%sprocess %s already started on pid %d",
+ psp->psp_ifname,
+ psp->psp_ifname[0] != '\0' ? ": " : "",
+ psp->psp_name, psp->psp_pid);
return 0;
}
@@ -482,9 +509,6 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
if (err == -1) {
logerr("%s: failed to send message to pid %d",
__func__, psp->psp_pid);
- shutdown(psp->psp_fd, SHUT_RDWR);
- close(psp->psp_fd);
- psp->psp_fd = -1;
ps_freeprocess(psp);
}
return 0;
@@ -532,6 +556,14 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
case PS_SCRIPT:
err = ps_root_run_script(ctx, data, len);
break;
+ case PS_STOPPROCS:
+ ctx->options |= DHCPCD_EXITING;
+ TAILQ_FOREACH(psp, &ctx->ps_processes, next) {
+ if (psp != ctx->ps_root)
+ ps_stopprocess(psp);
+ }
+ err = ps_stopwait(ctx);
+ break;
case PS_UNLINK:
if (!ps_root_validpath(ctx, psm->ps_cmd, data)) {
err = -1;
@@ -593,7 +625,7 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
break;
#endif
default:
- err = ps_root_os(psm, msg, &rdata, &rlen);
+ err = ps_root_os(ctx, psm, msg, &rdata, &rlen, &free_rdata);
break;
}
@@ -605,11 +637,12 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
/* Receive from state engine, do an action. */
static void
-ps_root_recvmsg(void *arg)
+ps_root_recvmsg(void *arg, unsigned short events)
{
- struct dhcpcd_ctx *ctx = arg;
+ struct ps_process *psp = arg;
- if (ps_recvpsmsg(ctx, ctx->ps_root_fd, ps_root_recvmsgcb, ctx) == -1)
+ if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events,
+ ps_root_recvmsgcb, psp->psp_ctx) == -1)
logerr(__func__);
}
@@ -631,15 +664,15 @@ ps_root_handleinterface(void *arg, int action, const char *ifname)
return -1;
}
- return (int)ps_sendcmd(ctx, ctx->ps_data_fd, PS_DEV_IFCMD, flag,
- ifname, strlen(ifname) + 1);
+ return (int)ps_sendcmd(ctx, ctx->ps_data_fd, PS_DEV_IFCMD,
+ flag, ifname, strlen(ifname) + 1);
}
#endif
static int
-ps_root_startcb(void *arg)
+ps_root_startcb(struct ps_process *psp)
{
- struct dhcpcd_ctx *ctx = arg;
+ struct dhcpcd_ctx *ctx = psp->psp_ctx;
if (ctx->options & DHCPCD_MANAGER)
setproctitle("[privileged proxy]");
@@ -648,15 +681,17 @@ ps_root_startcb(void *arg)
ctx->ifv[0],
ctx->options & DHCPCD_IPV4 ? " [ip4]" : "",
ctx->options & DHCPCD_IPV6 ? " [ip6]" : "");
- ctx->ps_root_pid = getpid();
ctx->options |= DHCPCD_PRIVSEPROOT;
+ if (if_opensockets(ctx) == -1)
+ logerr("%s: if_opensockets", __func__);
+
/* Open network sockets for sending.
* This is a small bit wasteful for non sandboxed OS's
* but makes life very easy for unicasting DHCPv6 in non manager
* mode as we no longer care about address selection.
* We can't call shutdown SHUT_RD on the socket because it's
- * not connectd. All we can do is try and set a zero sized
+ * not connected. All we can do is try and set a zero sized
* receive buffer and just let it overflow.
* Reading from it just to drain it is a waste of CPU time. */
#ifdef INET
@@ -708,15 +743,54 @@ ps_root_startcb(void *arg)
return 0;
}
-static void
-ps_root_signalcb(int sig, __unused void *arg)
+void
+ps_root_signalcb(int sig, void *arg)
{
+ struct dhcpcd_ctx *ctx = arg;
+ int status;
+ pid_t pid;
+ const char *ifname, *name;
+ struct ps_process *psp;
- if (sig == SIGCHLD) {
- while (waitpid(-1, NULL, WNOHANG) > 0)
- ;
+ if (sig != SIGCHLD)
return;
+
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+ psp = ps_findprocesspid(ctx, pid);
+ if (psp != NULL) {
+ ifname = psp->psp_ifname;
+ name = psp->psp_name;
+ } else {
+ /* Ignore logging the double fork */
+ if (ctx->options & DHCPCD_LAUNCHER)
+ continue;
+ ifname = "";
+ name = "unknown process";
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
+ logerrx("%s%s%s exited unexpectedly from PID %d,"
+ " code=%d",
+ ifname, ifname[0] != '\0' ? ": " : "",
+ name, pid, WEXITSTATUS(status));
+ else if (WIFSIGNALED(status))
+ logerrx("%s%s%s exited unexpectedly from PID %d,"
+ " signal=%s",
+ ifname, ifname[0] != '\0' ? ": " : "",
+ name, pid, strsignal(WTERMSIG(status)));
+ else
+ logdebugx("%s%s%s exited from PID %d",
+ ifname, ifname[0] != '\0' ? ": " : "",
+ name, pid);
+
+ if (psp != NULL)
+ ps_freeprocess(psp);
}
+
+ if (!(ctx->options & DHCPCD_EXITING))
+ return;
+ if (!(ps_waitforprocs(ctx)))
+ eloop_exit(ctx->ps_eloop, EXIT_SUCCESS);
}
int (*handle_interface)(void *, int, const char *);
@@ -775,37 +849,46 @@ ps_root_dispatchcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
}
static void
-ps_root_dispatch(void *arg)
+ps_root_dispatch(void *arg, unsigned short events)
{
struct dhcpcd_ctx *ctx = arg;
- if (ps_recvpsmsg(ctx, ctx->ps_data_fd, ps_root_dispatchcb, ctx) == -1)
+ if (ps_recvpsmsg(ctx, ctx->ps_data_fd, events,
+ ps_root_dispatchcb, ctx) == -1)
logerr(__func__);
}
static void
-ps_root_log(void *arg)
+ps_root_log(void *arg, unsigned short events)
{
struct dhcpcd_ctx *ctx = arg;
- if (logreadfd(ctx->ps_log_fd) == -1)
+ if (events != ELE_READ)
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
+
+ if (logreadfd(ctx->ps_log_root_fd) == -1)
logerr(__func__);
}
pid_t
ps_root_start(struct dhcpcd_ctx *ctx)
{
+ struct ps_id id = {
+ .psi_ifindex = 0,
+ .psi_cmd = PS_ROOT,
+ };
+ struct ps_process *psp;
int logfd[2], datafd[2];
pid_t pid;
- if (xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, logfd) == -1)
+ if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, logfd) == -1)
return -1;
#ifdef PRIVSEP_RIGHTS
if (ps_rights_limit_fdpair(logfd) == -1)
return -1;
#endif
- if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, datafd) == -1)
+ if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, datafd) == -1)
return -1;
if (ps_setbuf_fdpair(datafd) == -1)
return -1;
@@ -814,16 +897,17 @@ ps_root_start(struct dhcpcd_ctx *ctx)
return -1;
#endif
- pid = ps_dostart(ctx, &ctx->ps_root_pid, &ctx->ps_root_fd,
- ps_root_recvmsg, NULL, ctx,
- ps_root_startcb, ps_root_signalcb, 0);
+ psp = ctx->ps_root = ps_newprocess(ctx, &id);
+ strlcpy(psp->psp_name, "privileged proxy", sizeof(psp->psp_name));
+ pid = ps_startprocess(psp, ps_root_recvmsg, NULL,
+ ps_root_startcb, ps_root_signalcb, PSF_ELOOP);
if (pid == 0) {
- ctx->ps_log_fd = logfd[1];
- if (eloop_event_add(ctx->eloop, ctx->ps_log_fd,
+ ctx->ps_log_fd = logfd[0]; /* Keep open to pass to processes */
+ ctx->ps_log_root_fd = logfd[1];
+ if (eloop_event_add(ctx->eloop, ctx->ps_log_root_fd, ELE_READ,
ps_root_log, ctx) == -1)
return -1;
- close(logfd[0]);
ctx->ps_data_fd = datafd[1];
close(datafd[0]);
return 0;
@@ -835,32 +919,96 @@ ps_root_start(struct dhcpcd_ctx *ctx)
ctx->ps_data_fd = datafd[0];
close(datafd[1]);
- if (eloop_event_add(ctx->eloop, ctx->ps_data_fd,
+ if (eloop_event_add(ctx->eloop, ctx->ps_data_fd, ELE_READ,
ps_root_dispatch, ctx) == -1)
- return -1;
+ return 1;
- if ((ctx->ps_eloop = eloop_new()) == NULL)
- return -1;
+ return pid;
+}
- eloop_signal_set_cb(ctx->ps_eloop,
- dhcpcd_signals, dhcpcd_signals_len,
- ps_root_signalcb, ctx);
+void
+ps_root_close(struct dhcpcd_ctx *ctx)
+{
- return pid;
+ if_closesockets(ctx);
+
+#ifdef INET
+ if (ctx->udp_wfd != -1) {
+ close(ctx->udp_wfd);
+ ctx->udp_wfd = -1;
+ }
+#endif
+#ifdef INET6
+ if (ctx->nd_fd != -1) {
+ close(ctx->nd_fd);
+ ctx->nd_fd = -1;
+ }
+#endif
+#ifdef DHCP6
+ if (ctx->dhcp6_wfd != -1) {
+ close(ctx->dhcp6_wfd);
+ ctx->dhcp6_wfd = -1;
+ }
+#endif
}
int
ps_root_stop(struct dhcpcd_ctx *ctx)
{
+ struct ps_process *psp = ctx->ps_root;
+
+ if (!(ctx->options & DHCPCD_PRIVSEP) ||
+ ctx->eloop == NULL)
+ return 0;
+
+ /* If we are the root process then remove the pidfile */
+ if (ctx->options & DHCPCD_PRIVSEPROOT &&
+ !(ctx->options & DHCPCD_TEST))
+ {
+ if (unlink(ctx->pidfile) == -1)
+ logerr("%s: unlink: %s", __func__, ctx->pidfile);
+ }
+
+ /* Only the manager process gets past this point. */
+ if (ctx->options & DHCPCD_FORKED)
+ return 0;
+
+ /* We cannot log the root process exited before we
+ * log dhcpcd exits because the latter requires the former.
+ * So we just log the intent to exit.
+ * Even sending this will be a race to exit. */
+ if (psp) {
+ logdebugx("%s%s%s will exit from PID %d",
+ psp->psp_ifname,
+ psp->psp_ifname[0] != '\0' ? ": " : "",
+ psp->psp_name, psp->psp_pid);
+
+ if (ps_stopprocess(psp) == -1)
+ return -1;
+ } /* else the root process has already exited :( */
+
+ return ps_stopwait(ctx);
+}
+
+ssize_t
+ps_root_stopprocesses(struct dhcpcd_ctx *ctx)
+{
- return ps_dostop(ctx, &ctx->ps_root_pid, &ctx->ps_root_fd);
+ if (!(IN_PRIVSEP_SE(ctx)))
+ return 0;
+
+ if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_STOPPROCS, 0,
+ NULL, 0) == -1)
+ return -1;
+ return ps_root_readerror(ctx, NULL, 0);
}
ssize_t
ps_root_script(struct dhcpcd_ctx *ctx, const void *data, size_t len)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_SCRIPT, 0, data, len) == -1)
+ if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_SCRIPT,
+ 0, data, len) == -1)
return -1;
return ps_root_readerror(ctx, NULL, 0);
}
@@ -869,14 +1017,15 @@ ssize_t
ps_root_ioctl(struct dhcpcd_ctx *ctx, ioctl_request_t req, void *data,
size_t len)
{
+ int fd = PS_ROOT_FD(ctx);
#ifdef IOCTL_REQUEST_TYPE
unsigned long ulreq = 0;
memcpy(&ulreq, &req, sizeof(req));
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IOCTL, ulreq, data, len) == -1)
+ if (ps_sendcmd(ctx, fd, PS_IOCTL, ulreq, data, len) == -1)
return -1;
#else
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IOCTL, req, data, len) == -1)
+ if (ps_sendcmd(ctx, fd, PS_IOCTL, req, data, len) == -1)
return -1;
#endif
return ps_root_readerror(ctx, data, len);
@@ -886,7 +1035,7 @@ ssize_t
ps_root_unlink(struct dhcpcd_ctx *ctx, const char *file)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_UNLINK, 0,
+ if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_UNLINK, 0,
file, strlen(file) + 1) == -1)
return -1;
return ps_root_readerror(ctx, NULL, 0);
@@ -896,7 +1045,7 @@ ssize_t
ps_root_readfile(struct dhcpcd_ctx *ctx, const char *file,
void *data, size_t len)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_READFILE, 0,
+ if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_READFILE, 0,
file, strlen(file) + 1) == -1)
return -1;
return ps_root_readerror(ctx, data, len);
@@ -917,7 +1066,7 @@ ps_root_writefile(struct dhcpcd_ctx *ctx, const char *file, mode_t mode,
}
memcpy(buf + flen, data, len);
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_WRITEFILE, mode,
+ if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_WRITEFILE, mode,
buf, flen + len) == -1)
return -1;
return ps_root_readerror(ctx, NULL, 0);
@@ -927,7 +1076,7 @@ ssize_t
ps_root_filemtime(struct dhcpcd_ctx *ctx, const char *file, time_t *time)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_FILEMTIME, 0,
+ if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_FILEMTIME, 0,
file, strlen(file) + 1) == -1)
return -1;
return ps_root_readerror(ctx, time, sizeof(*time));
@@ -937,7 +1086,8 @@ ssize_t
ps_root_logreopen(struct dhcpcd_ctx *ctx)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_LOGREOPEN, 0, NULL, 0) == -1)
+ if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_LOGREOPEN, 0,
+ NULL, 0) == -1)
return -1;
return ps_root_readerror(ctx, NULL, 0);
}
@@ -953,7 +1103,7 @@ ps_root_getifaddrs(struct dhcpcd_ctx *ctx, struct ifaddrs **ifahead)
size_t len;
ssize_t err;
- if (ps_sendcmd(ctx, ctx->ps_root_fd,
+ if (ps_sendcmd(ctx, PS_ROOT_FD(ctx),
PS_GETIFADDRS, 0, NULL, 0) == -1)
return -1;
err = ps_root_mreaderror(ctx, &buf, &len);
@@ -981,17 +1131,17 @@ ps_root_getifaddrs(struct dhcpcd_ctx *ctx, struct ifaddrs **ifahead)
len -= ALIGN(sizeof(*ifa)) +
ALIGN(IFNAMSIZ) + ALIGN(sizeof(salen) * IFA_NADDRS);
-#define COPYOUTSA(addr) \
- do { \
- memcpy(&salen, sap, sizeof(salen)); \
- if (len < salen) \
- goto err; \
- if (salen != 0) { \
- (addr) = (struct sockaddr *)bp; \
- bp += ALIGN(salen); \
- len -= ALIGN(salen); \
- } \
- sap += sizeof(salen); \
+#define COPYOUTSA(addr) \
+ do { \
+ memcpy(&salen, sap, sizeof(salen)); \
+ if (len < salen) \
+ goto err; \
+ if (salen != 0) { \
+ (addr) = (struct sockaddr *)(void *)bp; \
+ bp += ALIGN(salen); \
+ len -= ALIGN(salen); \
+ } \
+ sap += sizeof(salen); \
} while (0 /* CONSTCOND */)
COPYOUTSA(ifa->ifa_addr);
@@ -1028,7 +1178,7 @@ ssize_t
ps_root_ip6forwarding(struct dhcpcd_ctx *ctx, const char *ifname)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IP6FORWARDING, 0,
+ if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_IP6FORWARDING, 0,
ifname, ifname != NULL ? strlen(ifname) + 1 : 0) == -1)
return -1;
return ps_root_readerror(ctx, NULL, 0);
@@ -1040,7 +1190,7 @@ int
ps_root_getauthrdm(struct dhcpcd_ctx *ctx, uint64_t *rdm)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_AUTH_MONORDM, 0,
+ if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_AUTH_MONORDM, 0,
rdm, sizeof(*rdm))== -1)
return -1;
return (int)ps_root_readerror(ctx, rdm, sizeof(*rdm));
@@ -1052,7 +1202,7 @@ int
ps_root_dev_initialised(struct dhcpcd_ctx *ctx, const char *ifname)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_DEV_INITTED, 0,
+ if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_DEV_INITTED, 0,
ifname, strlen(ifname) + 1)== -1)
return -1;
return (int)ps_root_readerror(ctx, NULL, 0);
@@ -1062,7 +1212,8 @@ int
ps_root_dev_listening(struct dhcpcd_ctx * ctx)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_DEV_LISTENING, 0, NULL, 0)== -1)
+ if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_DEV_LISTENING,
+ 0, NULL, 0) == -1)
return -1;
return (int)ps_root_readerror(ctx, NULL, 0);
}
diff --git a/src/privsep-root.h b/src/privsep-root.h
index 7fdd9f69f212..ce824db97664 100644
--- a/src/privsep-root.h
+++ b/src/privsep-root.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Privilege Separation for dhcpcd
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -36,7 +36,9 @@
#endif
pid_t ps_root_start(struct dhcpcd_ctx *ctx);
+void ps_root_close(struct dhcpcd_ctx *ctx);
int ps_root_stop(struct dhcpcd_ctx *ctx);
+void ps_root_signalcb(int, void *);
ssize_t ps_root_readerror(struct dhcpcd_ctx *, void *, size_t);
ssize_t ps_root_mreaderror(struct dhcpcd_ctx *, void **, size_t *);
@@ -49,12 +51,14 @@ ssize_t ps_root_writefile(struct dhcpcd_ctx *, const char *, mode_t,
const void *, size_t);
ssize_t ps_root_logreopen(struct dhcpcd_ctx *);
ssize_t ps_root_script(struct dhcpcd_ctx *, const void *, size_t);
+ssize_t ps_root_stopprocesses(struct dhcpcd_ctx *);
int ps_root_getauthrdm(struct dhcpcd_ctx *, uint64_t *);
#ifdef PRIVSEP_GETIFADDRS
int ps_root_getifaddrs(struct dhcpcd_ctx *, struct ifaddrs **);
#endif
-ssize_t ps_root_os(struct ps_msghdr *, struct msghdr *, void **, size_t *);
+ssize_t ps_root_os(struct dhcpcd_ctx *, struct ps_msghdr *, struct msghdr *,
+ void **, size_t *, bool *);
#if defined(BSD) || defined(__sun)
ssize_t ps_root_route(struct dhcpcd_ctx *, void *, size_t);
ssize_t ps_root_ioctllink(struct dhcpcd_ctx *, unsigned long, void *, size_t);
@@ -62,6 +66,8 @@ ssize_t ps_root_ioctl6(struct dhcpcd_ctx *, unsigned long, void *, size_t);
ssize_t ps_root_indirectioctl(struct dhcpcd_ctx *, unsigned long, const char *,
void *, size_t);
ssize_t ps_root_ifignoregroup(struct dhcpcd_ctx *, const char *);
+ssize_t ps_root_sysctl(struct dhcpcd_ctx *, const int *, unsigned int,
+ void *, size_t *, const void *, size_t);
#endif
#ifdef __linux__
ssize_t ps_root_sendnetlink(struct dhcpcd_ctx *, int, struct msghdr *);
diff --git a/src/privsep-sun.c b/src/privsep-sun.c
index 8043eed6ef65..6e539c198663 100644
--- a/src/privsep-sun.c
+++ b/src/privsep-sun.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Privilege Separation for dhcpcd, Solaris driver
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -45,7 +45,7 @@ ps_root_doioctl6(unsigned long req, void *data, size_t len)
{
int s, err;
- s = socket(PF_INET6, SOCK_DGRAM, 0);
+ s = xsocket(PF_INET6, SOCK_DGRAM, 0);
if (s != -1)
err = ioctl(s, req, data, len);
else
@@ -63,7 +63,7 @@ ps_root_doroute(void *data, size_t len)
int s;
ssize_t err;
- s = socket(PF_ROUTE, SOCK_RAW, 0);
+ s = xsocket(PF_ROUTE, SOCK_RAW, 0);
if (s != -1)
err = write(s, data, len);
else
@@ -77,7 +77,7 @@ ps_root_doroute(void *data, size_t len)
ssize_t
ps_root_os(struct ps_msghdr *psm, struct msghdr *msg,
- void **rdata, size_t *rlen)
+ void **rdata, size_t *rlen, __unused bool *free_rdata)
{
struct iovec *iov = msg->msg_iov;
void *data = iov->iov_base;
@@ -105,7 +105,7 @@ ssize_t
ps_root_ioctl6(struct dhcpcd_ctx *ctx, unsigned long request, void *data, size_t len)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IOCTL6,
+ if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_IOCTL6,
request, data, len) == -1)
return -1;
return ps_root_readerror(ctx, data, len);
@@ -115,7 +115,7 @@ ssize_t
ps_root_route(struct dhcpcd_ctx *ctx, void *data, size_t len)
{
- if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_ROUTE, 0, data, len) == -1)
+ if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_ROUTE, 0, data, len) == -1)
return -1;
return ps_root_readerror(ctx, data, len);
}
diff --git a/src/privsep.c b/src/privsep.c
index d574a2bcaee7..4cccfbdb2b87 100644
--- a/src/privsep.c
+++ b/src/privsep.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Privilege Separation for dhcpcd
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -75,6 +75,7 @@
#ifdef HAVE_CAPSICUM
#include <sys/capsicum.h>
+#include <sys/procdesc.h>
#include <capsicum_helpers.h>
#endif
#ifdef HAVE_UTIL_H
@@ -144,41 +145,34 @@ ps_dropprivs(struct dhcpcd_ctx *ctx)
struct rlimit rzero = { .rlim_cur = 0, .rlim_max = 0 };
- if (ctx->ps_control_pid != getpid()) {
- /* Prohibit new files, sockets, etc */
-#if defined(__linux__) || defined(__sun) || defined(__OpenBSD__)
- /*
- * If poll(2) is called with nfds > RLIMIT_NOFILE
- * then it returns EINVAL.
- * This blows.
- * Do the best we can and limit to what we need.
- * An attacker could potentially close a file and
- * open a new one still, but that cannot be helped.
- */
- unsigned long maxfd;
- maxfd = (unsigned long)eloop_event_count(ctx->eloop);
- if (IN_PRIVSEP_SE(ctx))
- maxfd++; /* XXX why? */
-
- struct rlimit rmaxfd = {
- .rlim_cur = maxfd,
- .rlim_max = maxfd
- };
- if (setrlimit(RLIMIT_NOFILE, &rmaxfd) == -1)
- logerr("setrlimit RLIMIT_NOFILE");
-#else
+ /* Prohibit new files, sockets, etc */
+ /*
+ * If poll(2) is called with nfds>RLIMIT_NOFILE then it returns EINVAL.
+ * We don't know the final value of nfds at this point *easily*.
+ * Sadly, this is a POSIX limitation and most platforms adhere to it.
+ * However, some are not that strict and are whitelisted below.
+ * Also, if we're not using poll then we can be restrictive.
+ *
+ * For the non whitelisted platforms there should be a sandbox to
+ * fallback to where we don't allow new files, etc:
+ * Linux:seccomp, FreeBSD:capsicum, OpenBSD:pledge
+ * Solaris users are sadly out of luck on both counts.
+ */
+#if defined(__NetBSD__) || defined(__DragonFly__) || \
+ defined(HAVE_KQUEUE) || defined(HAVE_EPOLL)
+ /* The control proxy *does* need to create new fd's via accept(2). */
+ if (ctx->ps_ctl == NULL || ctx->ps_ctl->psp_pid != getpid()) {
if (setrlimit(RLIMIT_NOFILE, &rzero) == -1)
logerr("setrlimit RLIMIT_NOFILE");
-#endif
}
+#endif
#define DHC_NOCHKIO (DHCPCD_STARTED | DHCPCD_DAEMONISE)
/* Prohibit writing to files.
* Obviously this won't work if we are using a logfile
* or redirecting stderr to a file. */
if ((ctx->options & DHC_NOCHKIO) == DHC_NOCHKIO ||
- (ctx->logfile == NULL &&
- (!ctx->stderr_valid || isatty(STDERR_FILENO) == 1)))
+ (ctx->logfile == NULL && isatty(STDERR_FILENO) == 1))
{
if (setrlimit(RLIMIT_FSIZE, &rzero) == -1)
logerr("setrlimit RLIMIT_FSIZE");
@@ -305,36 +299,57 @@ ps_rights_limit_fdpair(int fd[])
}
static int
-ps_rights_limit_stdio(struct dhcpcd_ctx *ctx)
+ps_rights_limit_stdio()
{
const int iebadf = CAPH_IGNORE_EBADF;
int error = 0;
- if (ctx->stdin_valid &&
- caph_limit_stream(STDIN_FILENO, CAPH_READ | iebadf) == -1)
+ if (caph_limit_stream(STDIN_FILENO, CAPH_READ | iebadf) == -1)
error = -1;
- if (ctx->stdout_valid &&
- caph_limit_stream(STDOUT_FILENO, CAPH_WRITE | iebadf) == -1)
+ if (caph_limit_stream(STDOUT_FILENO, CAPH_WRITE | iebadf) == -1)
error = -1;
- if (ctx->stderr_valid &&
- caph_limit_stream(STDERR_FILENO, CAPH_WRITE | iebadf) == -1)
+ if (caph_limit_stream(STDERR_FILENO, CAPH_WRITE | iebadf) == -1)
error = -1;
return error;
}
#endif
+#ifdef HAVE_CAPSICUM
+static void
+ps_processhangup(void *arg, unsigned short events)
+{
+ struct ps_process *psp = arg;
+ struct dhcpcd_ctx *ctx = psp->psp_ctx;
+
+ if (!(events & ELE_HANGUP))
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
+
+ logdebugx("%s%s%s exited from PID %d",
+ psp->psp_ifname, psp->psp_ifname[0] != '\0' ? ": " : "",
+ psp->psp_name, psp->psp_pid);
+
+ ps_freeprocess(psp);
+
+ if (!(ctx->options & DHCPCD_EXITING))
+ return;
+ if (!(ps_waitforprocs(ctx)))
+ eloop_exit(ctx->ps_eloop, EXIT_SUCCESS);
+}
+#endif
+
pid_t
-ps_dostart(struct dhcpcd_ctx *ctx,
- pid_t *priv_pid, int *priv_fd,
- void (*recv_msg)(void *), void (*recv_unpriv_msg),
- void *recv_ctx, int (*callback)(void *), void (*signal_cb)(int, void *),
+ps_startprocess(struct ps_process *psp,
+ void (*recv_msg)(void *, unsigned short),
+ void (*recv_unpriv_msg)(void *, unsigned short),
+ int (*callback)(struct ps_process *), void (*signal_cb)(int, void *),
unsigned int flags)
{
+ struct dhcpcd_ctx *ctx = psp->psp_ctx;
int fd[2];
pid_t pid;
- if (xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, fd) == -1) {
+ if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, fd) == -1) {
logerr("%s: socketpair", __func__);
return -1;
}
@@ -349,37 +364,69 @@ ps_dostart(struct dhcpcd_ctx *ctx,
}
#endif
- switch (pid = fork()) {
+#ifdef HAVE_CAPSICUM
+ pid = pdfork(&psp->psp_pfd, PD_CLOEXEC);
+#else
+ pid = fork();
+#endif
+ switch (pid) {
case -1:
+#ifdef HAVE_CAPSICUM
+ logerr("pdfork");
+#else
logerr("fork");
+#endif
return -1;
case 0:
- *priv_fd = fd[1];
+ psp->psp_pid = getpid();
+ psp->psp_fd = fd[1];
close(fd[0]);
break;
default:
- *priv_pid = pid;
- *priv_fd = fd[0];
+ psp->psp_pid = pid;
+ psp->psp_fd = fd[0];
close(fd[1]);
if (recv_unpriv_msg == NULL)
;
- else if (eloop_event_add(ctx->eloop, *priv_fd,
- recv_unpriv_msg, recv_ctx) == -1)
+ else if (eloop_event_add(ctx->eloop, psp->psp_fd, ELE_READ,
+ recv_unpriv_msg, psp) == -1)
+ {
+ logerr("%s: eloop_event_add fd %d",
+ __func__, psp->psp_fd);
+ return -1;
+ }
+#ifdef HAVE_CAPSICUM
+ if (eloop_event_add(ctx->eloop, psp->psp_pfd, ELE_HANGUP,
+ ps_processhangup, psp) == -1)
{
- logerr("%s: eloop_event_add", __func__);
+ logerr("%s: eloop_event_add pfd %d",
+ __func__, psp->psp_pfd);
return -1;
}
+#endif
+ psp->psp_started = true;
return pid;
}
- ctx->options |= DHCPCD_FORKED;
- if (ctx->fork_fd != -1) {
- close(ctx->fork_fd);
- ctx->fork_fd = -1;
+ /* If we are not the root process, close un-needed stuff. */
+ if (ctx->ps_root != psp) {
+ ps_root_close(ctx);
+#ifdef PLUGIN_DEV
+ dev_stop(ctx);
+#endif
}
- pidfile_clean();
- eloop_clear(ctx->eloop);
+ ctx->options |= DHCPCD_FORKED;
+ if (ctx->ps_log_fd != -1)
+ logsetfd(ctx->ps_log_fd);
+
+#ifdef DEBUG_FD
+ logerrx("pid %d log_fd=%d data_fd=%d psp_fd=%d",
+ getpid(), ctx->ps_log_fd, ctx->ps_data_fd, psp->psp_fd);
+#endif
+
+ eloop_clear(ctx->eloop, -1);
+ eloop_forked(ctx->eloop);
eloop_signal_set_cb(ctx->eloop,
dhcpcd_signals, dhcpcd_signals_len, signal_cb, ctx);
/* ctx->sigset aready has the initial sigmask set in main() */
@@ -388,75 +435,108 @@ ps_dostart(struct dhcpcd_ctx *ctx,
goto errexit;
}
- /* We are not root */
- if (priv_fd != &ctx->ps_root_fd) {
- ps_freeprocesses(ctx, recv_ctx);
- if (ctx->ps_root_fd != -1) {
- close(ctx->ps_root_fd);
- ctx->ps_root_fd = -1;
- }
+ if (ctx->fork_fd != -1) {
+ /* Already removed from eloop thanks to above clear. */
+ close(ctx->fork_fd);
+ ctx->fork_fd = -1;
+ }
+ /* This process has no need of the blocking inner eloop. */
+ if (!(flags & PSF_ELOOP)) {
+ eloop_free(ctx->ps_eloop);
+ ctx->ps_eloop = NULL;
+ } else
+ eloop_forked(ctx->ps_eloop);
+
+ pidfile_clean();
+ ps_freeprocesses(ctx, psp);
+
+ if (ctx->ps_root != psp) {
+ ctx->options &= ~DHCPCD_PRIVSEPROOT;
+ ctx->ps_root = NULL;
+ if (ctx->ps_log_root_fd != -1) {
+ /* Already removed from eloop thanks to above clear. */
+ close(ctx->ps_log_root_fd);
+ ctx->ps_log_root_fd = -1;
+ }
#ifdef PRIVSEP_RIGHTS
- /* We cannot limit the root process in any way. */
- if (ps_rights_limit_stdio(ctx) == -1) {
+ if (ps_rights_limit_stdio() == -1) {
logerr("ps_rights_limit_stdio");
goto errexit;
}
#endif
}
- if (priv_fd != &ctx->ps_inet_fd && ctx->ps_inet_fd != -1) {
- close(ctx->ps_inet_fd);
- ctx->ps_inet_fd = -1;
- }
-
- if (eloop_event_add(ctx->eloop, *priv_fd, recv_msg, recv_ctx) == -1)
+ if (eloop_event_add(ctx->eloop, psp->psp_fd, ELE_READ,
+ recv_msg, psp) == -1)
{
- logerr("%s: eloop_event_add", __func__);
+ logerr("%d %s: eloop_event_add XX fd %d", getpid(), __func__, psp->psp_fd);
goto errexit;
}
- if (callback(recv_ctx) == -1)
+ if (callback(psp) == -1)
goto errexit;
if (flags & PSF_DROPPRIVS)
ps_dropprivs(ctx);
+ psp->psp_started = true;
return 0;
errexit:
- /* Failure to start root or inet processes is fatal. */
- if (priv_fd == &ctx->ps_root_fd || priv_fd == &ctx->ps_inet_fd)
- (void)ps_sendcmd(ctx, *priv_fd, PS_STOP, 0, NULL, 0);
- shutdown(*priv_fd, SHUT_RDWR);
- *priv_fd = -1;
+ if (psp->psp_fd != -1) {
+ close(psp->psp_fd);
+ psp->psp_fd = -1;
+ }
eloop_exit(ctx->eloop, EXIT_FAILURE);
return -1;
}
+void
+ps_process_timeout(void *arg)
+{
+ struct dhcpcd_ctx *ctx = arg;
+
+ logerrx("%s: timed out", __func__);
+ eloop_exit(ctx->eloop, EXIT_FAILURE);
+}
+
int
-ps_dostop(struct dhcpcd_ctx *ctx, pid_t *pid, int *fd)
+ps_stopprocess(struct ps_process *psp)
{
int err = 0;
+ if (psp == NULL)
+ return 0;
+
+ psp->psp_started = false;
+
#ifdef PRIVSEP_DEBUG
- logdebugx("%s: pid=%d fd=%d", __func__, *pid, *fd);
+ logdebugx("%s: me=%d pid=%d fd=%d %s", __func__,
+ getpid(), psp->psp_pid, psp->psp_fd, psp->psp_name);
#endif
- if (*fd != -1) {
- eloop_event_delete(ctx->eloop, *fd);
- if (ps_sendcmd(ctx, *fd, PS_STOP, 0, NULL, 0) == -1) {
+ if (psp->psp_fd != -1) {
+ eloop_event_delete(psp->psp_ctx->eloop, psp->psp_fd);
+#if 0
+ if (ps_sendcmd(psp->psp_ctx, psp->psp_fd, PS_STOP, 0,
+ NULL, 0) == -1)
+ {
+ logerr("%d %d %s %s", getpid(), psp->psp_pid, psp->psp_name, __func__);
+ err = -1;
+ }
+ shutdown(psp->psp_fd, SHUT_WR);
+#else
+ if (shutdown(psp->psp_fd, SHUT_WR) == -1) {
logerr(__func__);
err = -1;
}
- (void)shutdown(*fd, SHUT_RDWR);
- close(*fd);
- *fd = -1;
+#endif
}
/* Don't wait for the process as it may not respond to the shutdown
- * request. We'll reap the process on receipt of SIGCHLD. */
- *pid = 0;
+ * request. We'll reap the process on receipt of SIGCHLD where we
+ * also close the fd. */
return err;
}
@@ -467,6 +547,13 @@ ps_start(struct dhcpcd_ctx *ctx)
TAILQ_INIT(&ctx->ps_processes);
+ /* We need an inner eloop to block with. */
+ if ((ctx->ps_eloop = eloop_new()) == NULL)
+ return -1;
+ eloop_signal_set_cb(ctx->ps_eloop,
+ dhcpcd_signals, dhcpcd_signals_len,
+ dhcpcd_signal_cb, ctx);
+
switch (pid = ps_root_start(ctx)) {
case -1:
logerr("ps_root_start");
@@ -527,6 +614,9 @@ ps_entersandbox(const char *_pledge, const char **sandbox)
#elif defined(HAVE_PLEDGE)
if (sandbox != NULL)
*sandbox = "pledge";
+ // There is no need to use unveil(2) because we are in an empty chroot
+ // This is encouraged by Theo de Raadt himself:
+ // https://www.mail-archive.com/misc@openbsd.org/msg171655.html
return pledge(_pledge, NULL);
#elif defined(HAVE_SECCOMP)
if (sandbox != NULL)
@@ -557,7 +647,7 @@ ps_managersandbox(struct dhcpcd_ctx *ctx, const char *_pledge)
* If it cannot be opened before chrooting then syslog(3) will fail.
* openlog(3) does not return an error which doubly sucks.
*/
- if (ctx->ps_root_fd == -1) {
+ if (ctx->ps_root == NULL) {
unsigned int logopts = loggetopts();
logopts &= ~LOGERR_LOG;
@@ -572,7 +662,7 @@ ps_managersandbox(struct dhcpcd_ctx *ctx, const char *_pledge)
#ifdef PRIVSEP_RIGHTS
if ((ctx->pf_inet_fd != -1 &&
ps_rights_limit_ioctl(ctx->pf_inet_fd) == -1) ||
- ps_rights_limit_stdio(ctx) == -1)
+ ps_rights_limit_stdio() == -1)
{
logerr("%s: cap_rights_limit", __func__);
return -1;
@@ -606,39 +696,106 @@ ps_stop(struct dhcpcd_ctx *ctx)
ctx->eloop == NULL)
return 0;
- r = ps_ctl_stop(ctx);
- if (r != 0)
- ret = r;
-
- r = ps_inet_stop(ctx);
- if (r != 0)
- ret = r;
+ if (ctx->ps_ctl != NULL) {
+ r = ps_ctl_stop(ctx);
+ if (r != 0)
+ ret = r;
+ }
- /* We've been chrooted, so we need to tell the
- * privileged proxy to remove the pidfile. */
- ps_root_unlink(ctx, ctx->pidfile);
+ if (ctx->ps_inet != NULL) {
+ r = ps_inet_stop(ctx);
+ if (r != 0)
+ ret = r;
+ }
- r = ps_root_stop(ctx);
- if (r != 0)
- ret = r;
+ if (ctx->ps_root != NULL) {
+ if (ps_root_stopprocesses(ctx) == -1)
+ ret = -1;
+ }
- ctx->options &= ~DHCPCD_PRIVSEP;
return ret;
}
+bool
+ps_waitforprocs(struct dhcpcd_ctx *ctx)
+{
+ struct ps_process *psp = TAILQ_FIRST(&ctx->ps_processes);
+
+ if (psp == NULL)
+ return false;
+
+ /* Different processes */
+ if (psp != TAILQ_LAST(&ctx->ps_processes, ps_process_head))
+ return true;
+
+ return !psp->psp_started;
+}
+
+int
+ps_stopwait(struct dhcpcd_ctx *ctx)
+{
+ int error = EXIT_SUCCESS;
+
+ if (ctx->ps_eloop == NULL || !ps_waitforprocs(ctx))
+ return 0;
+
+ ctx->options |= DHCPCD_EXITING;
+ if (eloop_timeout_add_sec(ctx->ps_eloop, PS_PROCESS_TIMEOUT,
+ ps_process_timeout, ctx) == -1)
+ logerr("%s: eloop_timeout_add_sec", __func__);
+ eloop_enter(ctx->ps_eloop);
+
+#ifdef HAVE_CAPSICUM
+ struct ps_process *psp;
+
+ TAILQ_FOREACH(psp, &ctx->ps_processes, next) {
+ if (psp->psp_pfd == -1)
+ continue;
+ if (eloop_event_add(ctx->ps_eloop, psp->psp_pfd,
+ ELE_HANGUP, ps_processhangup, psp) == -1)
+ logerr("%s: eloop_event_add pfd %d",
+ __func__, psp->psp_pfd);
+ }
+#endif
+
+ error = eloop_start(ctx->ps_eloop, &ctx->sigset);
+ if (error != EXIT_SUCCESS)
+ logerr("%s: eloop_start", __func__);
+
+ eloop_timeout_delete(ctx->ps_eloop, ps_process_timeout, ctx);
+
+ return error;
+}
+
void
ps_freeprocess(struct ps_process *psp)
{
+ struct dhcpcd_ctx *ctx = psp->psp_ctx;
+
+ TAILQ_REMOVE(&ctx->ps_processes, psp, next);
- TAILQ_REMOVE(&psp->psp_ctx->ps_processes, psp, next);
if (psp->psp_fd != -1) {
- eloop_event_delete(psp->psp_ctx->eloop, psp->psp_fd);
+ eloop_event_delete(ctx->eloop, psp->psp_fd);
close(psp->psp_fd);
}
if (psp->psp_work_fd != -1) {
- eloop_event_delete(psp->psp_ctx->eloop, psp->psp_work_fd);
+ eloop_event_delete(ctx->eloop, psp->psp_work_fd);
close(psp->psp_work_fd);
}
+#ifdef HAVE_CAPSICUM
+ if (psp->psp_pfd != -1) {
+ eloop_event_delete(ctx->eloop, psp->psp_pfd);
+ if (ctx->ps_eloop != NULL)
+ eloop_event_delete(ctx->ps_eloop, psp->psp_pfd);
+ close(psp->psp_pfd);
+ }
+#endif
+ if (ctx->ps_root == psp)
+ ctx->ps_root = NULL;
+ if (ctx->ps_inet == psp)
+ ctx->ps_inet = NULL;
+ if (ctx->ps_ctl == psp)
+ ctx->ps_ctl = NULL;
#ifdef INET
if (psp->psp_bpf != NULL)
bpf_close(psp->psp_bpf);
@@ -649,12 +806,23 @@ ps_freeprocess(struct ps_process *psp)
static void
ps_free(struct dhcpcd_ctx *ctx)
{
- struct ps_process *psp;
- bool stop = ctx->ps_root_pid == getpid();
+ struct ps_process *ppsp, *psp;
+ bool stop;
+
+ if (ctx->ps_root != NULL)
+ ppsp = ctx->ps_root;
+ else if (ctx->ps_ctl != NULL)
+ ppsp = ctx->ps_ctl;
+ else
+ ppsp = NULL;
+ if (ppsp != NULL)
+ stop = ppsp->psp_pid == getpid();
+ else
+ stop = false;
while ((psp = TAILQ_FIRST(&ctx->ps_processes)) != NULL) {
- if (stop)
- ps_dostop(ctx, &psp->psp_pid, &psp->psp_fd);
+ if (stop && psp != ppsp)
+ ps_stopprocess(psp);
ps_freeprocess(psp);
}
}
@@ -760,7 +928,6 @@ ps_sendpsmmsg(struct dhcpcd_ctx *ctx, int fd,
len = writev(fd, iov, iovlen);
if (len == -1) {
- logerr(__func__);
if (ctx->options & DHCPCD_FORKED &&
!(ctx->options & DHCPCD_PRIVSEPROOT))
eloop_exit(ctx->eloop, EXIT_FAILURE);
@@ -896,7 +1063,7 @@ nobufs:
}
ssize_t
-ps_recvmsg(struct dhcpcd_ctx *ctx, int rfd, uint16_t cmd, int wfd)
+ps_recvmsg(int rfd, unsigned short events, uint16_t cmd, int wfd)
{
struct sockaddr_storage ss = { .ss_family = AF_UNSPEC };
uint8_t controlbuf[sizeof(struct sockaddr_storage)] = { 0 };
@@ -909,32 +1076,46 @@ ps_recvmsg(struct dhcpcd_ctx *ctx, int rfd, uint16_t cmd, int wfd)
.msg_control = controlbuf, .msg_controllen = sizeof(controlbuf),
.msg_iov = iov, .msg_iovlen = 1,
};
+ ssize_t len;
- ssize_t len = recvmsg(rfd, &msg, 0);
+ if (!(events & ELE_READ))
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
- if (len == -1)
+ len = recvmsg(rfd, &msg, 0);
+ if (len == -1) {
logerr("%s: recvmsg", __func__);
- if (len == -1 || len == 0) {
- if (ctx->options & DHCPCD_FORKED &&
- !(ctx->options & DHCPCD_PRIVSEPROOT))
- eloop_exit(ctx->eloop,
- len == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
return len;
}
iov[0].iov_len = (size_t)len;
len = ps_sendcmdmsg(wfd, cmd, &msg);
- if (len == -1) {
- logerr("ps_sendcmdmsg");
- if (ctx->options & DHCPCD_FORKED &&
- !(ctx->options & DHCPCD_PRIVSEPROOT))
- eloop_exit(ctx->eloop, EXIT_FAILURE);
- }
+ if (len == -1)
+ logerr("%s: ps_sendcmdmsg", __func__);
return len;
}
ssize_t
-ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd,
+ps_daemonised(struct dhcpcd_ctx *ctx)
+{
+ struct ps_process *psp;
+ ssize_t err = 0;
+
+ dhcpcd_daemonised(ctx);
+
+ /* Echo the message to all processes */
+ TAILQ_FOREACH(psp, &ctx->ps_processes, next) {
+ if (psp->psp_pid == getpid())
+ continue;
+ if (ps_sendcmd(psp->psp_ctx, psp->psp_fd, PS_DAEMONISED,
+ 0, NULL, 0) == -1)
+ err = -1;
+ }
+
+ return err;
+}
+
+ssize_t
+ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd, unsigned short events,
ssize_t (*callback)(void *, struct ps_msghdr *, struct msghdr *),
void *cbctx)
{
@@ -945,6 +1126,9 @@ ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd,
struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1 };
bool stop = false;
+ if (!(events & ELE_READ))
+ logerrx("%s: unexpected event 0x%04x", __func__, events);
+
len = read(fd, &psm, sizeof(psm));
#ifdef PRIVSEP_DEBUG
logdebugx("%s: %zd", __func__, len);
@@ -962,17 +1146,18 @@ ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd,
if (psm.psm_hdr.ps_cmd == PS_STOP) {
stop = true;
len = 0;
+ } else if (psm.psm_hdr.ps_cmd == PS_DAEMONISED) {
+ ps_daemonised(ctx);
+ return 0;
}
}
if (stop) {
+ ctx->options |= DHCPCD_EXITING;
#ifdef PRIVSEP_DEBUG
logdebugx("process %d stopping", getpid());
#endif
ps_free(ctx);
-#ifdef PLUGIN_DEV
- dev_stop(ctx);
-#endif
eloop_exit(ctx->eloop, len != -1 ? EXIT_SUCCESS : EXIT_FAILURE);
return len;
}
@@ -994,6 +1179,8 @@ ps_findprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid)
struct ps_process *psp;
TAILQ_FOREACH(psp, &ctx->ps_processes, next) {
+ if (!(psp->psp_started))
+ continue;
if (memcmp(&psp->psp_id, psid, sizeof(psp->psp_id)) == 0)
return psp;
}
@@ -1002,6 +1189,19 @@ ps_findprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid)
}
struct ps_process *
+ps_findprocesspid(struct dhcpcd_ctx *ctx, pid_t pid)
+{
+ struct ps_process *psp;
+
+ TAILQ_FOREACH(psp, &ctx->ps_processes, next) {
+ if (psp->psp_pid == pid)
+ return psp;
+ }
+ errno = ESRCH;
+ return NULL;
+}
+
+struct ps_process *
ps_newprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid)
{
struct ps_process *psp;
@@ -1011,7 +1211,14 @@ ps_newprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid)
return NULL;
psp->psp_ctx = ctx;
memcpy(&psp->psp_id, psid, sizeof(psp->psp_id));
+ psp->psp_fd = -1;
psp->psp_work_fd = -1;
+#ifdef HAVE_CAPSICUM
+ psp->psp_pfd = -1;
+#endif
+
+ if (!(ctx->options & DHCPCD_MANAGER))
+ strlcpy(psp->psp_ifname, ctx->ifv[0], sizeof(psp->psp_ifname));
TAILQ_INSERT_TAIL(&ctx->ps_processes, psp, next);
return psp;
}
diff --git a/src/privsep.h b/src/privsep.h
index d843dda8fdf0..cab175503572 100644
--- a/src/privsep.h
+++ b/src/privsep.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Privilege Separation for dhcpcd
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -33,6 +33,7 @@
/* Start flags */
#define PSF_DROPPRIVS 0x01
+#define PSF_ELOOP 0x02
/* Protocols */
#define PS_BOOTP 0x0001
@@ -53,14 +54,22 @@
#define PS_CTL 0x0018
#define PS_CTL_EOF 0x0019
#define PS_LOGREOPEN 0x0020
+#define PS_STOPPROCS 0x0021
+#define PS_DAEMONISED 0x0022
+
+/* Domains */
+#define PS_ROOT 0x0101
+#define PS_INET 0x0102
+#define PS_CONTROL 0x0103
/* BSD Commands */
-#define PS_IOCTLLINK 0x0101
-#define PS_IOCTL6 0x0102
-#define PS_IOCTLINDIRECT 0x0103
-#define PS_IP6FORWARDING 0x0104
-#define PS_GETIFADDRS 0x0105
-#define PS_IFIGNOREGRP 0x0106
+#define PS_IOCTLLINK 0x0201
+#define PS_IOCTL6 0x0202
+#define PS_IOCTLINDIRECT 0x0203
+#define PS_IP6FORWARDING 0x0204
+#define PS_GETIFADDRS 0x0205
+#define PS_IFIGNOREGRP 0x0206
+#define PS_SYSCTL 0x0207
/* Dev Commands */
#define PS_DEV_LISTENING 0x1001
@@ -76,6 +85,10 @@
#define PS_CTL_PRIV 0x0004
#define PS_CTL_UNPRIV 0x0005
+/* Sysctl Needs (via flags) */
+#define PS_SYSCTL_OLEN 0x0001
+#define PS_SYSCTL_ODATA 0x0002
+
/* Process commands */
#define PS_START 0x4000
#define PS_STOP 0x8000
@@ -87,17 +100,23 @@
CMSG_SPACE(sizeof(struct in6_pktinfo) + \
sizeof(int)))
+#define PSP_NAMESIZE 16 + INET_MAX_ADDRSTRLEN
+
/* Handy macro to work out if in the privsep engine or not. */
#define IN_PRIVSEP(ctx) \
((ctx)->options & DHCPCD_PRIVSEP)
#define IN_PRIVSEP_SE(ctx) \
(((ctx)->options & (DHCPCD_PRIVSEP | DHCPCD_FORKED)) == DHCPCD_PRIVSEP)
+#define PS_PROCESS_TIMEOUT 5 /* seconds to stop all processes */
+
#if defined(PRIVSEP) && defined(HAVE_CAPSICUM)
#define PRIVSEP_RIGHTS
#endif
-#ifdef __linux__
+#define PS_ROOT_FD(ctx) ((ctx)->ps_root ? (ctx)->ps_root->psp_fd : -1)
+
+#if !defined(DISABLE_SECCOMP) && defined(__linux__)
# include <linux/version.h>
# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
# define HAVE_SECCOMP
@@ -145,6 +164,7 @@ struct ps_msg {
};
struct bpf;
+
struct ps_process {
TAILQ_ENTRY(ps_process) next;
struct dhcpcd_ctx *psp_ctx;
@@ -154,14 +174,20 @@ struct ps_process {
int psp_work_fd;
unsigned int psp_ifindex;
char psp_ifname[IF_NAMESIZE];
+ char psp_name[PSP_NAMESIZE];
uint16_t psp_proto;
const char *psp_protostr;
+ bool psp_started;
#ifdef INET
int (*psp_filter)(const struct bpf *, const struct in_addr *);
struct interface psp_ifp; /* Move BPF gubbins elsewhere */
struct bpf *psp_bpf;
#endif
+
+#ifdef HAVE_CAPSICUM
+ int psp_pfd;
+#endif
};
TAILQ_HEAD(ps_process_head, ps_process);
@@ -175,8 +201,10 @@ TAILQ_HEAD(ps_process_head, ps_process);
int ps_init(struct dhcpcd_ctx *);
int ps_start(struct dhcpcd_ctx *);
int ps_stop(struct dhcpcd_ctx *);
+int ps_stopwait(struct dhcpcd_ctx *);
int ps_entersandbox(const char *, const char **);
int ps_managersandbox(struct dhcpcd_ctx *, const char *);
+ssize_t ps_daemonised(struct dhcpcd_ctx *);
int ps_unrollmsg(struct msghdr *, struct ps_msghdr *, const void *, size_t);
ssize_t ps_sendpsmmsg(struct dhcpcd_ctx *, int,
@@ -187,8 +215,8 @@ ssize_t ps_sendmsg(struct dhcpcd_ctx *, int, uint16_t, unsigned long,
const struct msghdr *);
ssize_t ps_sendcmd(struct dhcpcd_ctx *, int, uint16_t, unsigned long,
const void *data, size_t len);
-ssize_t ps_recvmsg(struct dhcpcd_ctx *, int, uint16_t, int);
-ssize_t ps_recvpsmsg(struct dhcpcd_ctx *, int,
+ssize_t ps_recvmsg(int, unsigned short, uint16_t, int);
+ssize_t ps_recvpsmsg(struct dhcpcd_ctx *, int, unsigned short,
ssize_t (*callback)(void *, struct ps_msghdr *, struct msghdr *), void *);
/* Internal privsep functions. */
@@ -207,15 +235,17 @@ int ps_rights_limit_fdpair(int []);
int ps_seccomp_enter(void);
#endif
-pid_t ps_dostart(struct dhcpcd_ctx * ctx,
- pid_t *priv_pid, int *priv_fd,
- void (*recv_msg)(void *), void (*recv_unpriv_msg),
- void *recv_ctx, int (*callback)(void *), void (*)(int, void *),
+pid_t ps_startprocess(struct ps_process *,
+ void (*recv_msg)(void *, unsigned short),
+ void (*recv_unpriv_msg)(void *, unsigned short),
+ int (*callback)(struct ps_process *), void (*)(int, void *),
unsigned int);
-int ps_dostop(struct dhcpcd_ctx *ctx, pid_t *pid, int *fd);
-
+int ps_stopprocess(struct ps_process *);
struct ps_process *ps_findprocess(struct dhcpcd_ctx *, struct ps_id *);
+struct ps_process *ps_findprocesspid(struct dhcpcd_ctx *, pid_t);
struct ps_process *ps_newprocess(struct dhcpcd_ctx *, struct ps_id *);
+bool ps_waitforprocs(struct dhcpcd_ctx *ctx);
+void ps_process_timeout(void *);
void ps_freeprocess(struct ps_process *);
void ps_freeprocesses(struct dhcpcd_ctx *, struct ps_process *);
#endif
diff --git a/src/route.c b/src/route.c
index ef9c41258000..00b3c046aa5a 100644
--- a/src/route.c
+++ b/src/route.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - route management
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -711,7 +711,8 @@ rt_build(struct dhcpcd_ctx *ctx, int af)
rb_tree_init(&routes, &rt_compare_proto_ops);
rb_tree_init(&added, &rt_compare_os_ops);
rb_tree_init(&kroutes, &rt_compare_os_ops);
- if_initrt(ctx, &kroutes, af);
+ if (if_initrt(ctx, &kroutes, af) != 0)
+ logerr("%s: if_initrt", __func__);
ctx->rt_order = 0;
ctx->options |= DHCPCD_RTBUILD;
diff --git a/src/route.h b/src/route.h
index 45f0e1a7dc96..df8fc4772926 100644
--- a/src/route.h
+++ b/src/route.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - route management
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* rEDISTRIBUTION AND USE IN SOURCE AND BINARY FORMS, WITH OR WITHOUT
diff --git a/src/sa.c b/src/sa.c
index c6a19d1bef7a..05009d3bae76 100644
--- a/src/sa.c
+++ b/src/sa.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Socket Address handling for dhcpcd
- * Copyright (c) 2015-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2015-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -300,11 +300,39 @@ sa_toprefix(const struct sockaddr *sa)
return prefix;
}
+static void
+ipbytes_fromprefix(uint8_t *ap, int prefix, int max_prefix)
+{
+ int bytes, bits, i;
+
+ bytes = prefix / NBBY;
+ bits = prefix % NBBY;
+
+ for (i = 0; i < bytes; i++)
+ *ap++ = 0xff;
+ if (bits) {
+ uint8_t a;
+
+ a = 0xff;
+ a = (uint8_t)(a << (8 - bits));
+ *ap++ = a;
+ }
+ bytes = (max_prefix - prefix) / NBBY;
+ for (i = 0; i < bytes; i++)
+ *ap++ = 0x00;
+}
+
+void
+in6_addr_fromprefix(struct in6_addr *addr, int prefix)
+{
+ ipbytes_fromprefix((uint8_t *)addr, prefix, 128);
+}
+
int
sa_fromprefix(struct sockaddr *sa, int prefix)
{
uint8_t *ap;
- int max_prefix, bytes, bits, i;
+ int max_prefix;
switch (sa->sa_family) {
#ifdef INET
@@ -328,22 +356,8 @@ sa_fromprefix(struct sockaddr *sa, int prefix)
return -1;
}
- bytes = prefix / NBBY;
- bits = prefix % NBBY;
-
ap = (uint8_t *)sa + sa_addroffset(sa);
- for (i = 0; i < bytes; i++)
- *ap++ = 0xff;
- if (bits) {
- uint8_t a;
-
- a = 0xff;
- a = (uint8_t)(a << (8 - bits));
- *ap++ = a;
- }
- bytes = (max_prefix - prefix) / NBBY;
- for (i = 0; i < bytes; i++)
- *ap++ = 0x00;
+ ipbytes_fromprefix(ap, prefix, max_prefix);
#ifndef NDEBUG
/* Ensure the calculation is correct */
diff --git a/src/sa.h b/src/sa.h
index 69724cd8570c..902229afbe67 100644
--- a/src/sa.h
+++ b/src/sa.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Socket Address handling for dhcpcd
- * Copyright (c) 2015-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2015-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -67,6 +67,7 @@ bool sa_is_loopback(const struct sockaddr *);
void *sa_toaddr(struct sockaddr *);
int sa_toprefix(const struct sockaddr *);
int sa_fromprefix(struct sockaddr *, int);
+void in6_addr_fromprefix(struct in6_addr *, int);
const char *sa_addrtop(const struct sockaddr *, char *, socklen_t);
int sa_cmp(const struct sockaddr *, const struct sockaddr *);
void sa_in_init(struct sockaddr *, const struct in_addr *);
diff --git a/src/script.c b/src/script.c
index 6173b4020f38..1a61f29dd10b 100644
--- a/src/script.c
+++ b/src/script.c
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
@@ -682,33 +682,42 @@ send_interface(struct fd_list *fd, const struct interface *ifp, int af)
}
static int
+script_status(const char *script, int status)
+{
+
+ if (WIFEXITED(status)) {
+ if (WEXITSTATUS(status))
+ logerrx("%s: %s: WEXITSTATUS %d",
+ __func__, script, WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status))
+ logerrx("%s: %s: %s",
+ __func__, script, strsignal(WTERMSIG(status)));
+
+ return WEXITSTATUS(status);
+}
+
+static int
script_run(struct dhcpcd_ctx *ctx, char **argv)
{
pid_t pid;
- int status = 0;
+ int status;
pid = script_exec(argv, ctx->script_env);
- if (pid == -1)
+ if (pid == -1) {
logerr("%s: %s", __func__, argv[0]);
- else if (pid != 0) {
- /* Wait for the script to finish */
- while (waitpid(pid, &status, 0) == -1) {
- if (errno != EINTR) {
- logerr("%s: waitpid", __func__);
- status = 0;
- break;
- }
+ return -1;
+ } else if (pid == 0)
+ return 0;
+
+ /* Wait for the script to finish */
+ while (waitpid(pid, &status, 0) == -1) {
+ if (errno != EINTR) {
+ logerr("%s: waitpid", __func__);
+ status = 0;
+ break;
}
- if (WIFEXITED(status)) {
- if (WEXITSTATUS(status))
- logerrx("%s: %s: WEXITSTATUS %d",
- __func__, argv[0], WEXITSTATUS(status));
- } else if (WIFSIGNALED(status))
- logerrx("%s: %s: %s",
- __func__, argv[0], strsignal(WTERMSIG(status)));
}
-
- return WEXITSTATUS(status);
+ return script_status(argv[0], status);
}
int
@@ -762,10 +771,14 @@ script_runreason(const struct interface *ifp, const char *reason)
logdebugx("%s: executing: %s %s", ifp->name, argv[0], reason);
#ifdef PRIVSEP
- if (ctx->options & DHCPCD_PRIVSEP) {
- if (ps_root_script(ctx,
- ctx->script_buf, ctx->script_buflen) == -1)
+ if (IN_PRIVSEP(ctx)) {
+ ssize_t err;
+
+ err = ps_root_script(ctx, ctx->script_buf, (size_t)buflen);
+ if (err == -1)
logerr(__func__);
+ else
+ script_status(ctx->script, (int)err);
goto send_listeners;
}
#endif
diff --git a/src/script.h b/src/script.h
index feb8574d5912..6321d4c71398 100644
--- a/src/script.h
+++ b/src/script.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* dhcpcd - DHCP client daemon
- * Copyright (c) 2006-2021 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved
* Redistribution and use in source and binary forms, with or without
diff --git a/tests/crypt/Makefile b/tests/crypt/Makefile
index dd676f4b2aa9..c3df1a31f5ad 100644
--- a/tests/crypt/Makefile
+++ b/tests/crypt/Makefile
@@ -3,7 +3,7 @@ include ${TOP}/iconfig.mk
PROG= run-test
SRCS= run-test.c
-SRCS+= test_hmac_md5.c
+SRCS+= test_hmac_md5.c test_sha256.c
CFLAGS?= -O2
CSTD?= c99
diff --git a/tests/crypt/run-test.c b/tests/crypt/run-test.c
index 42f52d1fce2d..91cc89830305 100644
--- a/tests/crypt/run-test.c
+++ b/tests/crypt/run-test.c
@@ -33,6 +33,8 @@ int main(void)
if (test_hmac_md5())
r = -1;
+ if (test_sha256())
+ r = -1;
return r;
}
diff --git a/tests/crypt/test.h b/tests/crypt/test.h
index 4f716f1220f4..3c9070a5c83d 100644
--- a/tests/crypt/test.h
+++ b/tests/crypt/test.h
@@ -28,5 +28,6 @@
#ifndef TEST_H
int test_hmac_md5(void);
+int test_sha256(void);
#endif
diff --git a/tests/crypt/test_sha256.c b/tests/crypt/test_sha256.c
new file mode 100644
index 000000000000..986e0513c34c
--- /dev/null
+++ b/tests/crypt/test_sha256.c
@@ -0,0 +1,138 @@
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2023 Tobias Heider <tobias.heider@canonical.com>
+ * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+#include "test.h"
+
+#ifdef SHA2_H
+#include SHA2_H
+#endif
+
+# ifndef SHA256_DIGEST_LENGTH
+# define SHA256_DIGEST_LENGTH 32
+# endif
+
+static void
+print_md(FILE *stream, const uint8_t *md)
+{
+ int i;
+
+ fprintf(stream, "digest = 0x");
+ for (i = 0; i < SHA256_DIGEST_LENGTH; i++)
+ fprintf(stream, "%02x", *md++);
+ fprintf(stream, "\n");
+}
+
+static void
+test_md(const uint8_t *md, const uint8_t *tst)
+{
+ print_md(stdout, md);
+ if (memcmp(md, tst, SHA256_DIGEST_LENGTH) == 0)
+ return;
+ fprintf(stderr, "FAILED!\nExpected\t\t\t");
+ print_md(stderr, tst);
+ exit(EXIT_FAILURE);
+}
+
+static void
+sha256_test1(void)
+{
+ const uint8_t text[] = "Hi There";
+ const uint8_t expect[SHA256_DIGEST_LENGTH] = {
+ 0xcc, 0x6d, 0x58, 0x96, 0xd7, 0x70, 0x10, 0x1e,
+ 0xf0, 0x28, 0x0c, 0x94, 0x3a, 0x2d, 0x3c, 0x3f,
+ 0x24, 0xcd ,0x5b, 0x11, 0x46, 0x4a, 0x51, 0x86,
+ 0xda, 0xf7, 0xa2, 0x38, 0x47, 0x71, 0x62, 0xac
+ };
+ uint8_t digest[SHA256_DIGEST_LENGTH];
+ SHA256_CTX ctx;
+
+ printf ("SHA256 Test 1:\t\t");
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, text, 8);
+ SHA256_Final(digest, &ctx);
+ test_md(digest, expect);
+}
+
+static void
+sha256_test2(void)
+{
+ const uint8_t text[] = "what do ya want for nothing?";
+ const uint8_t expect[SHA256_DIGEST_LENGTH] = {
+ 0xb3, 0x81, 0xe7, 0xfe, 0xc6, 0x53, 0xfc, 0x3a,
+ 0xb9, 0xb1, 0x78, 0x27, 0x23, 0x66, 0xb8, 0xac,
+ 0x87, 0xfe, 0xd8, 0xd3, 0x1c, 0xb2, 0x5e, 0xd1,
+ 0xd0, 0xe1, 0xf3, 0x31, 0x86, 0x44, 0xc8, 0x9c,
+ };
+ uint8_t digest[SHA256_DIGEST_LENGTH];
+ SHA256_CTX ctx;
+
+ printf ("SHA256 Test 2:\t\t");
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, text, 28);
+ SHA256_Final(digest, &ctx);
+ test_md(digest, expect);
+}
+
+static void
+sha256_test3(void)
+{
+ const uint8_t expect[SHA256_DIGEST_LENGTH] = {
+ 0x5c, 0xf6, 0x18, 0xb5, 0xb6, 0xd3, 0x8b, 0xd1,
+ 0x6c, 0x2e, 0x55, 0x8e, 0xef, 0x4d, 0x4b, 0x6d,
+ 0x52, 0x82, 0x84, 0x54, 0x7f, 0xd4, 0xa0, 0x9d,
+ 0xa2, 0xab, 0xb6, 0xf0, 0x98, 0xec, 0x61, 0x93,
+ };
+ uint8_t digest[SHA256_DIGEST_LENGTH];
+ uint8_t text[50];
+ int i;
+ SHA256_CTX ctx;
+
+ printf ("SHA256 Test 3:\t\t");
+ for (i = 0; i < 50; i++)
+ text[i] = 0xdd;
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, text, 50);
+ SHA256_Final(digest, &ctx);
+ test_md(digest, expect);
+}
+
+int test_sha256(void)
+{
+ printf ("Starting SHA256 tests...\n\n");
+ sha256_test1();
+ sha256_test2();
+ sha256_test3();
+ printf("\nAll tests pass.\n");
+ return 0;
+}
diff --git a/tests/eloop-bench/Makefile b/tests/eloop-bench/Makefile
index 2827c6070422..a0ebafd9c0f9 100644
--- a/tests/eloop-bench/Makefile
+++ b/tests/eloop-bench/Makefile
@@ -39,7 +39,7 @@ distclean: clean
depend:
${PROG}: ${DEPEND} ${OBJS}
- ${CC} ${LDFLAGS} -o $@ ${OBJS}
+ ${CC} ${LDFLAGS} -o $@ ${OBJS} ${LDADD}
test: ${PROG}
./${PROG}
diff --git a/tests/eloop-bench/eloop-bench.c b/tests/eloop-bench/eloop-bench.c
index 0f90e824c3ad..fd0fb2edd3db 100644
--- a/tests/eloop-bench/eloop-bench.c
+++ b/tests/eloop-bench/eloop-bench.c
@@ -1,6 +1,6 @@
/*
* eloop benchmark
- * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>
+ * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
@@ -58,11 +58,14 @@ static struct pipe *pipes;
static struct eloop *e;
static void
-read_cb(void *arg)
+read_cb(void *arg, unsigned short events)
{
struct pipe *p = arg;
unsigned char buf[1];
+ if (events != ELE_READ)
+ warn("%s: unexpected events 0x%04x", __func__, events);
+
if (read(p->fd[0], buf, 1) != 1) {
warn("%s: read", __func__);
bad++;
@@ -156,7 +159,7 @@ main(int argc, char **argv)
for (i = 0, p = pipes; i < npipes; i++, p++) {
if (pipe2(p->fd, O_CLOEXEC | O_NONBLOCK) == -1)
err(EXIT_FAILURE, "pipe");
- if (eloop_event_add(e, p->fd[0], read_cb, p) == -1)
+ if (eloop_event_add(e, p->fd[0], ELE_READ, read_cb, p) == -1)
err(EXIT_FAILURE, "eloop_event_add");
}