[
  {
    "path": ".clang-format",
    "content": "# Basic .clang-format taken from FreeBSD\n---\nBasedOnStyle: WebKit\nAlignAfterOpenBracket: DontAlign\nAlignConsecutiveMacros: AcrossEmptyLines\nAlignConsecutiveAssignments: false\nAlignConsecutiveDeclarations: false\nAlignEscapedNewlines: Left\nAlignOperands: false\nAlignTrailingComments: true\nAllowAllArgumentsOnNextLine: false\nAllowAllParametersOfDeclarationOnNextLine: false\nAllowShortBlocksOnASingleLine: Never\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortFunctionsOnASingleLine: InlineOnly\nAllowShortIfStatementsOnASingleLine: Never\nAllowShortLoopsOnASingleLine: false\nAlwaysBreakAfterReturnType: TopLevelDefinitions\nAlwaysBreakBeforeMultilineStrings: false\nAlwaysBreakTemplateDeclarations: MultiLine\nBinPackArguments: true\nBinPackParameters: true\nBreakBeforeBinaryOperators: None\nBreakBeforeBraces: WebKit\nBreakBeforeTernaryOperators: false\n# TODO: BreakStringLiterals can cause very strange formatting so turn it off?\nBreakStringLiterals: false\n# Prefer:\n# some_var = function(arg1,\n#    arg2)\n# over:\n# some_var =\n#     function(arg1, arg2)\nPenaltyBreakAssignment: 100\n# Prefer:\n# some_long_function(arg1, arg2\n#     arg3)\n# over:\n# some_long_function(\n#     arg1, arg2, arg3)\nPenaltyBreakBeforeFirstCallParameter: 100\nCompactNamespaces: true\nDerivePointerAlignment: false\nDisableFormat: false\nForEachMacros:\n  - ARB_ARRFOREACH\n  - ARB_ARRFOREACH_REVWCOND\n  - ARB_ARRFOREACH_REVERSE\n  - ARB_FOREACH\n  - ARB_FOREACH_FROM\n  - ARB_FOREACH_SAFE\n  - ARB_FOREACH_REVERSE\n  - ARB_FOREACH_REVERSE_FROM\n  - ARB_FOREACH_REVERSE_SAFE\n  - BIT_FOREACH_ISCLR\n  - BIT_FOREACH_ISSET\n  - CPU_FOREACH\n  - CPU_FOREACH_ISCLR\n  - CPU_FOREACH_ISSET\n  - FOREACH_THREAD_IN_PROC\n  - FOREACH_PROC_IN_SYSTEM\n  - FOREACH_PRISON_CHILD\n  - FOREACH_PRISON_DESCENDANT\n  - FOREACH_PRISON_DESCENDANT_LOCKED\n  - FOREACH_PRISON_DESCENDANT_LOCKED_LEVEL\n  - MNT_VNODE_FOREACH_ALL\n  - MNT_VNODE_FOREACH_ACTIVE\n  - RB_FOREACH\n  - RB_FOREACH_FROM\n  - RB_FOREACH_SAFE\n  - RB_FOREACH_REVERSE\n  - RB_FOREACH_REVERSE_FROM\n  - RB_FOREACH_REVERSE_SAFE\n  - SLIST_FOREACH\n  - SLIST_FOREACH_FROM\n  - SLIST_FOREACH_FROM_SAFE\n  - SLIST_FOREACH_SAFE\n  - SLIST_FOREACH_PREVPTR\n  - SPLAY_FOREACH\n  - LIST_FOREACH\n  - LIST_FOREACH_FROM\n  - LIST_FOREACH_FROM_SAFE\n  - LIST_FOREACH_SAFE\n  - STAILQ_FOREACH\n  - STAILQ_FOREACH_FROM\n  - STAILQ_FOREACH_FROM_SAFE\n  - STAILQ_FOREACH_SAFE\n  - TAILQ_FOREACH\n  - TAILQ_FOREACH_FROM\n  - TAILQ_FOREACH_FROM_SAFE\n  - TAILQ_FOREACH_REVERSE\n  - TAILQ_FOREACH_REVERSE_FROM\n  - TAILQ_FOREACH_REVERSE_FROM_SAFE\n  - TAILQ_FOREACH_REVERSE_SAFE\n  - TAILQ_FOREACH_SAFE\n  - VM_MAP_ENTRY_FOREACH\n  - VM_PAGE_DUMP_FOREACH\nSpaceBeforeParens: ControlStatementsExceptForEachMacros\nIndentCaseLabels: false\nIndentPPDirectives: None\nLanguage: Cpp\nNamespaceIndentation: None\nPointerAlignment: Right\nContinuationIndentWidth: 4\nIndentWidth: 8\nTabWidth: 8\nColumnLimit: 80\nUseTab: Always\nSpaceAfterCStyleCast: false\nIncludeBlocks: Regroup\nIncludeCategories:\n  - Regex: '^\\\"opt_.*\\.h\\\"'\n    Priority: 1\n    SortPriority: 10\n  - Regex: '^<sys/cdefs\\.h>'\n    Priority: 2\n    SortPriority: 20\n  - Regex: '^<sys/types\\.h>'\n    Priority: 2\n    SortPriority: 21\n  - Regex: '^<sys/param\\.h>'\n    Priority: 2\n    SortPriority: 22\n  - Regex: '^<sys/systm\\.h>'\n    Priority: 2\n    SortPriority: 23\n  - Regex: '^<sys.*/'\n    Priority: 2\n    SortPriority: 24\n  - Regex: '^<vm/vm\\.h>'\n    Priority: 3\n    SortPriority: 30\n  - Regex: '^<vm/'\n    Priority: 3\n    SortPriority: 31\n  - Regex: '^<machine/'\n    Priority: 4\n    SortPriority: 40\n  - Regex: '^<(x86|amd64|i386|xen)/'\n    Priority: 5\n    SortPriority: 50\n  - Regex: '^<dev/'\n    Priority: 6\n    SortPriority: 60\n# Most BSD require <netinet/in.h> before this ...\n  - Regex: '^<netinet/if_ether.h>'\n    Priority: 7\n    SortPriority:  71\n# Most BSD require <netinet/in.h> before this ...\n  - Regex: '^<netinet/icmp6.h>'\n    Priority: 7\n    SortPriority:  71\n  - Regex: '^<net.*/'\n    Priority: 7\n    SortPriority: 70\n  - Regex: '^<protocols/'\n    Priority: 7\n    SortPriority: 72\n  - Regex: '^<(fs|nfs(|client|server)|ufs)/'\n    Priority: 8\n    SortPriority: 80\n  - Regex: '^<[^/].*\\.h'\n    Priority: 9\n    SortPriority: 90\n  - Regex: '^\\\".*\\.h\\\"'\n    Priority: 10\n    SortPriority: 100\n# LLVM's header include ordering style is almost the exact opposite of ours.\n# Unfortunately, they have hard-coded their preferences into clang-format.\n# Clobbering this regular expression to avoid matching prevents non-system\n# headers from being forcibly moved to the top of the include list.\n# http://llvm.org/docs/CodingStandards.html#include-style\nIncludeIsMainRegex: 'BLAH_DONT_MATCH_ANYTHING'\nSortIncludes: true\nKeepEmptyLinesAtTheStartOfBlocks: false\nTypenameMacros:\n  - ARB_ELMTYPE\n  - ARB_HEAD\n  - ARB8_HEAD\n  - ARB16_HEAD\n  - ARB32_HEAD\n  - ARB_ENTRY\n  - ARB8_ENTRY\n  - ARB16_ENTRY\n  - ARB32_ENTRY\n  - LIST_CLASS_ENTRY\n  - LIST_CLASS_HEAD\n  - LIST_ENTRY\n  - LIST_HEAD\n  - QUEUE_TYPEOF\n  - RB_ENTRY\n  - RB_HEAD\n  - SLIST_CLASS_HEAD\n  - SLIST_CLASS_ENTRY\n  - SLIST_HEAD\n  - SLIST_ENTRY\n  - SMR_POINTER\n  - SPLAY_ENTRY\n  - SPLAY_HEAD\n  - STAILQ_CLASS_ENTRY\n  - STAILQ_CLASS_HEAD\n  - STAILQ_ENTRY\n  - STAILQ_HEAD\n  - TAILQ_CLASS_ENTRY\n  - TAILQ_CLASS_HEAD\n  - TAILQ_ENTRY\n  - TAILQ_HEAD\n"
  },
  {
    "path": ".clang-tidy",
    "content": "# Disable DeprecatedOrUnsafeBufferHandling - our use of these is fine\n# and musl does not support the the _s variants.\n# Disable valist due to clang-tidy bug fixed in v19?\nChecks: \"cert-*,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,-clang-analyzer-valist.Uninitialized\"\nHeaderFilterRegex: \".*\"\n"
  },
  {
    "path": ".clangd",
    "content": "If:\n  PathMatch: src/dev/.*\\.c\nCompileFlags:\n  Add: [-I.., -I../.., -I../../compat, -I../../vendor]\n---\nIf:\n  PathMatch: src/.*\\.(c|h)\nCompileFlags:\n  Add: [-I.., -I../compat, -I../vendor]\n---\nCompileFlags:\n  Add: [-DINET, -DARP, -DARPING, -DIPV4LL, -DINET6, -DDHCP6, -DPLUGIN_DEV, -DAUTH, -DPRIVSEP]\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\nenv:\n  CC: clang\n\njobs:\n  ubuntu:\n    strategy:\n      matrix:\n        os: [ ubuntu-latest, ubuntu-22.04 ]\n    runs-on: ${{ matrix.os }}\n\n    steps:\n    - uses: actions/checkout@v2\n    \n    - name: Configure\n      run: ./configure\n\n    - name: Build\n      run: make\n\n    - name: Tests\n      run: make tests\n\n  # Sadly the BSDs have stopped building ....\n  \n  # openbsd:\n  #   runs-on: ubuntu-latest\n  #   steps:\n  #   - name: Bootstrap OpenBSD-latest\n  #     uses: mario-campos/emulate@v1\n  #     with:\n  #       operating-system: openbsd-latest\n\n  #   - name: Install Dependencies\n  #     run: pkg_add git\n\n  #   - name: Build\n  #     run: |\n  #       git clone --depth=1 \"${{ github.server_url }}/${{ github.repository }}\" build\n  #       cd build\n  #       [ \"${{ github.event.pull_request.number }}\" = \"\" ] || (echo \"fetching PR ${{ github.event.pull_request.number }}\"; git fetch origin pull/${{ github.event.pull_request.number }}/head:pr-${{ github.event.pull_request.number }} && git checkout \"pr-${{ github.event.pull_request.number }}\")\n  #       ./configure\n  #       make\n\n  #   - name: Tests\n  #     run: |\n  #       ulimit -n 1024\n  #       cd build\n  #       make tests\n\n  # freebsd:\n  #   runs-on: ubuntu-latest\n  #   steps:\n  #   - name: Bootstrap FreeBSD-latest\n  #     uses: mario-campos/emulate@v1\n  #     with:\n  #       operating-system: freebsd-latest\n\n  #   - name: Install Dependencies\n  #     run: pkg install -y git\n\n  #   - name: Build\n  #     run: |\n  #       git clone --depth=1 \"${{ github.server_url }}/${{ github.repository }}\" build\n  #       cd build\n  #       [ \"${{ github.event.pull_request.number }}\" = \"\" ] || (echo \"fetching PR ${{ github.event.pull_request.number }}\"; git fetch origin pull/${{ github.event.pull_request.number }}/head:pr-${{ github.event.pull_request.number }} && git checkout \"pr-${{ github.event.pull_request.number }}\")\n  #       ./configure\n  #       make\n\n  #   - name: Tests\n  #     run: |\n  #        cd build\n  #        make tests\n\n  # netbsd:\n  #   strategy:\n  #     matrix:\n  #       args:\n  #         -\n  #         - --disable-ipv4\n  #         - --disable-arp\n  #         - --disable-ipv4ll\n  #         - --disable-ipv6\n  #         - --disable-dhcp6\n  #       cppflags:\n  #         -\n  #         - -DSMALL\n  #   runs-on: ubuntu-latest\n  #   steps:\n  #   - name: Bootstrap NetBSD-latest\n  #     uses: mario-campos/emulate@v1\n  #     with:\n  #       operating-system: netbsd-latest\n\n  #   - name: Build\n  #     run: |\n  #       git clone --depth=1 \"${{ github.server_url }}/${{ github.repository }}\" build\n  #       cd build\n  #       [ \"${{ github.event.pull_request.number }}\" = \"\" ] || (echo \"fetching PR ${{ github.event.pull_request.number }}\"; git fetch origin pull/${{ github.event.pull_request.number }}/head:pr-${{ github.event.pull_request.number }} && git checkout \"pr-${{ github.event.pull_request.number }}\")\n  #       CFLAGS=-Werror CPPFLAGS=\"${{ matrix.cppflags }}\" ./configure ${{ matrix.args }}\n  #       make\n\n  #   - name: Tests\n  #     run: |\n  #       cd build\n  #       make tests\n"
  },
  {
    "path": ".github/workflows/format.yml",
    "content": "name: Clang Format Checker\non:\n  push:\n  pull_request:\n    branches: [master]\njobs:\n  clang-format-checking:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v5\n        # This is pinned to clang-format v19\n      - uses: RafikFarhad/clang-format-github-action@v5\n        with:\n          style: \"file\"\n          sources: \"src/**/*.h,src/**/*.c\"\n"
  },
  {
    "path": ".gitignore",
    "content": "# Ignore configure generated files\nconfig.h\nconfig.mk\nconfig.log\n\n# Ignore object files\n.depend\n*.o\n*.So\n*.so\ndhcpcd\n\n# Ignore generated embedded files\ndhcpcd-embedded.c\ndhcpcd-embedded.h\n\n# Ignore generated man pages and scripts\ndhcpcd.8\ndhcpcd-run-hooks\ndhcpcd-run-hooks.8\ndhcpcd.conf.5\nhooks/30-hostname\nhooks/50-ypbind\n\n# Ignore distribution\ndhcpcd*.xz*\n\n# Ignore patch files\n*.diff\n*.patch\n*.orig\n*.rej\n\n# Ignore swap files\n*.swp\n\n# Ignore Coverity\ncov-int\n\n# Ignore VSCode\n.vscode\n\n# Ignore macOS hidden files\n.DS_Store\n"
  },
  {
    "path": "BUILDING.md",
    "content": "<!-- SPDX-License-Identifier: BSD-2-Clause -->\n<!-- Copyright (c) 2017-2025 Roy Marples <roy@marples.name> -->\n\n# Building dhcpcd\n\nThis attempts to document various ways of building dhcpcd for your\nplatform.\n`./configure` is a POSIX shell script that works in a similar way\nto GNU configure.\nThis works fine provided you don't force any exotic options down\nwhich may or may not be silently discarded.\n\nSome build time warnings are expected - the only platforms with zero\nwarnings are DragonFlyBSD and NetBSD.\nIt is expected that the platforms be improvded to support dhcpcd\nbetter.\nThere maybe some loss of functionality, but for the most part,\ndhcpcd can work around these deficiencies.\n\n## Size is an issue\nTo compile small dhcpcd, maybe to be used for installation media where\nsize is a concern, you can use the `--small` configure option to enable\na reduced feature set within dhcpcd.\nCurrently this just removes non important options out of\n`dhcpcd-definitions.conf`, the logfile option,\nDHCPv6 Prefix Delegation and IPv6 address announcement *(to prefer an\naddress on another interface)*.\nOther features maybe dropped as and when required.\ndhcpcd can also be made smaller by removing the IPv4 or IPv6 stack:\n  *  `--disable-inet`\n  *  `--disable-inet6`\n\nOr by removing the following features:\n  *  `--disable-auth`\n  *  `--disable-arp`\n  *  `--disable-arping`\n  *  `--disable-ipv4ll`\n  *  `--disable-dhcp6`\n  *  `--disable-privsep`\n\nYou can also move the embedded extended configuration from the dhcpcd binary\nto an external file (LIBEXECDIR/dhcpcd-definitions.conf)\n  *  `--disable-embedded`\nIf dhcpcd cannot load this file at runtime, dhcpcd will work but will not be\nable to decode any DHCP/DHCPv6 options that are not defined by the user\nin /etc/dhcpcd.conf. This does not really change the total on disk size.\n\n## Cross compiling\nIf you're cross compiling you may need set the platform if OS is different\nfrom the host.  \n`--target=sparc-sun-netbsd5.0`\n\nIf you're building for an MMU-less system where fork() does not work, you\nshould `./configure --disable-fork`.\nThis also puts the `--no-background` flag on and stops the `--background` flag\nfrom working.\n\n## Default directories\nYou can change the default dirs with these knobs.\nFor example, to satisfy FHS compliance you would do this:\n`./configure --libexecdir=/lib/dhcpcd dbdir=/var/lib/dhcpcd`\n\n## Compile Issues\nWe now default to using `-std=c99`. For 64-bit linux, this always works, but\nfor 32-bit linux it requires either gnu99 or a patch to `asm/types.h`.\nMost distros patch linux headers so this should work fine.\nlinux-2.6.24 finally ships with a working 32-bit header.\nIf your linux headers are older, or your distro hasn't patched them you can\nset `CSTD=gnu99` to work around this.\n\nArchLinux presently sanitises all kernel headers to the latest version\nregardless of the version for your CPU. As such, Arch presently ships a\n3.12 kernel with 3.17 headers which claim that it supports temporary address\nmanagement and no automatic prefix route generation, both of which are\nobviously false. You will have to patch support either in the kernel or\nout of the headers (or dhcpcd itself) to have correct operation.\n\nLinux netlink headers cause a sign conversion error.\nI [submitted a patch](https://lkml.org/lkml/2019/12/17/680),\nbut as yet it's not upstreamed.\n\nGLIBC ships an icmp6.h header which will result in signedness warnings.\nTheir [bug #22489](https://sourceware.org/bugzilla/show_bug.cgi?id=22489)\nwill solve this once it's actually applied.\n\n## OS specific issues\nSome BSD systems do not allow the manipulation of automatically added subnet\nroutes. You can find discussion here:\n    http://mail-index.netbsd.org/tech-net/2008/12/03/msg000896.html\nBSD systems where this has been fixed or is known to work are:\n    NetBSD-5.0\n    FreeBSD-10.0\n\nSome BSD systems protect against IPv6 NS/NA messages by ensuring that the\nsource address matches a prefix on the recieved by a RA message.\nThis is an error as the correct check is for on-link prefixes as the\nkernel may not be handling RA itself.\nBSD systems where this has been fixed or is known to work are:\n    NetBSD-7.0\n    OpenBSD-5.0\n    patch submitted against FreeBSD-10.0\n\nSome BSD systems do not announce IPv6 address flag changes, such as\n`IN6_IFF_TENTATIVE`, `IN6_IFF_DUPLICATED`, etc. On these systems,\ndhcpcd will poll a freshly added address until either `IN6_IFF_TENTATIVE` is\ncleared or `IN6_IFF_DUPLICATED` is set and take action accordingly.\nBSD systems where this has been fixed or is known to work are:\n    NetBSD-7.0\n\nOpenBSD will always add it's own link-local address if no link-local address\nexists, because it doesn't check if the address we are adding is a link-local\naddress or not.\n\nSome BSD systems do not announce cached neighbour route changes based\non reachability to userland. For such systems, IPv6 routers will always\nbe assumed to be reachable until they either stop being a router or expire.\nBSD systems where this has been fixed or is known to work are:\n    NetBSD-7.99.3\n\nLinux prior to 3.17 won't allow userland to manage IPv6 temporary addresses.\nEither upgrade or don't allow dhcpcd to manage the RA,\nso don't set either `ipv6ra_own` or `slaac private` in `dhcpcd.conf` if you\nwant to have working IPv6 temporary addresses.\nSLAAC private addresses are just as private, just stable.\n\nLinux SECCOMP is very dependant on libc vs kernel.\nWhen libc is changed and uses a syscall that dhcpcd is unaware of,\nSECCOMP may break dhcpcd.\nWhen this happens you can configure dhcpcd with --disable-seccomp\nso dhcpcd can use a POSIX resource limited sandbox with privilege separation\nstill. If you do this, please report the issue so that we can adjust the\nSECCOMP filter so that dhcpcd can use SECCOMP once more.\nOr convince the libc/kernel people to adpot something more maintainable\nlike FreeBSD's capsicum or OpenBSD's pledge.\nTo test ASAN with privsep you need to add ASAN to CPPFLAGS.\nTo test Valgrind with privsep you can optionally add VALGRIND to CPPFLAGS.\nFor both they need some syscalls which are potentially dangerous and thus\nare disabled by default.\nFor Valgrind, it needs to unlink the pipe files which it can't do anyway\nas it's dropped permissions. Otherwise it works fine.\n\nEnable libpcap support with --with-libpcap.\nThis should only be done on systems that lack the needed kernel hooks\nas libpcap does not support a write filter and is vulnerable\nif the application is exploited.\nAn upstream PR has been submitted to add `pcap_setwritefilter()` and\n`pcap_lockfilter()` to libpcap:\n    https://github.com/the-tcpdump-group/libpcap/pull/1683\n\n## Init systems\nWe try and detect how dhcpcd should interact with system services at runtime.\nIf we cannot auto-detect how do to this, or it is wrong then\nyou can change this by passing shell commands to `--serviceexists`,\n`--servicecmd` and optionally `--servicestatus` to `./configure` or overriding\nthe service variables in a hook.\n\n\n## /dev management\nSome systems have `/dev` management systems and some of these like to rename\ninterfaces. As this system would listen in the same way as dhcpcd to new\ninterface arrivals, dhcpcd needs to listen to the `/dev` management sytem\ninstead of the kernel. However, if the `/dev` management system breaks, stops\nworking, or changes to a new one, dhcpcd should still try and continue to work.\nTo facilitate this, dhcpcd allows a plugin to load to instruct dhcpcd when it\ncan use an interface. As of the time of writing only udev support is included.\nYou can disable this with `--without-dev`, or `without-udev`.\nNOTE: in Gentoo at least, `sys-fs/udev` as provided by systemd leaks memory\n`sys-fs/eudev`, the fork of udev does not and as such is recommended.\n\n## crypto\ndhcpcd ships with some cryptographic routines taken from various upstreams.\nThese are routinely monitored and try to be as up to date as possible.\nYou can optionally configure dhcpcd with `--with-openssl` to use libcrypto\nto use these instead.\nThis is not enabled by default, even if libcrypto is found because libcrypto\ngenerally lives in /usr and dhcpcd in /sbin which could be a separate\nfilesystem.\n\n## Importing into another source control system\nTo import the full sources, use the import target.\nTo import only the needed sources and documentation, use the import-src\ntarget.\nBoth targets support DESTDIR to set the installation directory,\nif unset it defaults to `/tmp/dhcpcd-$VERSION`\nExample: `make DESTDIR=/usr/src/contrib/dhcpcd import-src`\n\n\n## Hooks\nNot all the hooks in dhcpcd-hooks are installed by default.\nBy default we install `01-test`, `20-resolv.conf`and `30-hostname`.\nThe other hooks, `10-wpa_supplicant`, `15-timezone` and `29-lookup-hostname`\nare installed to `$(datadir)/dhcpcd/hooks` by default and need to be\ncopied to `$(libexecdir)/dhcpcd-hooks` for use.\nThe configure program attempts to find hooks for systems you have installed.\nTo add more simply\n`./configure -with-hook=ntp.conf`\n\nIf using resolvconf, the `20-resolv.conf` hook now requires a version with the\n`-C` and `-c` options to deprecate and activate interfaces to support wireless\nroaming (Linux) or carrier just drops (NetBSD).\nIf your resolvconf does not support this then you will see a warning\nabout an illegal option when the carrier changes, but things should still work.\nIn this instance the DNS information cannot be Deprecated and may not\nbe optimal for multi-homed hosts.\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n1. Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright\n   notice, this list of conditions and the following disclaimer in the\n   documentation and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\nOR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\nHOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\nLIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\nOUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGE.\n"
  },
  {
    "path": "Makefile",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n\nSUBDIRS=\tsrc hooks\n\nPACKAGE=\tdhcpcd\nVERSION!=\tsed -n 's/\\#define VERSION[[:space:]]*\"\\(.*\\)\".*/\\1/p' src/defs.h\n\nDIST!=\t\tif test -d .git; then echo \"dist-git\"; \\\n\t\telse echo \"dist-inst\"; fi\nFOSSILID?=\tcurrent\nGITREF?=\tHEAD\n\nDISTSUFFIX=\nDISTPREFIX?=\t${PACKAGE}-${VERSION}${DISTSUFFIX}\nDISTFILE?=\t${DISTPREFIX}.tar.xz\nDISTINFO=\t${DISTFILE}.distinfo\nDISTINFOMD=\t${DISTINFO}.md\nDISTSIGN=\t${DISTFILE}.asc\n\nCLEANFILES+=\t*.tar.xz\n\n.PHONY:\t\thooks import import-bsd tests\n\n.SUFFIXES:\t.in\n\nall: config.h\n\tfor x in ${SUBDIRS}; do cd $$x; ${MAKE} $@ || exit $$?; cd ..; done\n\ndepend: config.h\n\tfor x in ${SUBDIRS}; do cd $$x; ${MAKE} $@ || exit $$?; cd ..; done\n\ntests:\n\tcd $@; ${MAKE} $@\n\ntest: tests\n\nhooks:\n\tcd $@; ${MAKE}\n\neginstall:\n\tfor x in ${SUBDIRS}; do cd $$x; ${MAKE} $@ || exit $$?; cd ..; done\n\ninstall:\n\tfor x in ${SUBDIRS}; do cd $$x; ${MAKE} $@ || exit $$?; cd ..; done\n\nproginstall:\n\tfor x in ${SUBDIRS}; do cd $$x; ${MAKE} $@ || exit $$?; cd ..; done\n\nclean:\n\trm -rf cov-int dhcpcd.xz\n\tfor x in ${SUBDIRS} tests; do cd $$x; ${MAKE} $@ || exit $$?; cd ..; done\n\ndistclean: clean\n\trm -f config.h config.mk config.log \\\n\t\t${DISTFILE} ${DISTINFO} ${DISTINFOMD} ${DISTSIGN}\n\trm -f *.diff *.patch *.orig *.rej\n\tfor x in ${SUBDIRS} tests; do cd $$x; ${MAKE} $@ || exit $$?; cd ..; done\n\ndist-git:\n\tgit archive --prefix=${DISTPREFIX}/ v${VERSION} | xz >${DISTFILE}\n\ndist-inst:\n\tmkdir /tmp/${DISTPREFIX}\n\tcp -RPp * /tmp/${DISTPREFIX}\n\t(cd /tmp/${DISTPREFIX}; make clean)\n\ttar -cvJpf ${DISTFILE} -C /tmp ${DISTPREFIX}\n\trm -rf /tmp/${DISTPREFIX}\n\ndist: ${DIST}\n\ndistinfo: dist\n\trm -f ${DISTINFO} ${DISTSIGN}\n\t${SHA256} ${DISTFILE} >${DISTINFO}\n\twc -c <${DISTFILE} \\\n\t\t| xargs printf 'Size   (${DISTFILE}) = %s\\n' >>${DISTINFO}\n\t${PGP} --sign --armour --detach ${DISTFILE}\n\tchmod 644 ${DISTSIGN}\n\tls -l ${DISTFILE} ${DISTINFO} ${DISTSIGN}\n\n${DISTINFOMD}: ${DISTINFO}\n\techo '```' >${DISTINFOMD}\n\tcat ${DISTINFO} >>${DISTINFOMD}\n\techo '```' >>${DISTINFOMD}\n\nrelease: distinfo ${DISTINFOMD}\n\tgh release create v${VERSION} \\\n\t\t--title \"${PACKAGE} ${VERSION}\" --draft --generate-notes \\\n\t\t--notes-file ${DISTINFOMD} \\\n\t\t${DISTFILE} ${DISTSIGN}\n\nsnapshot:\n\trm -rf /tmp/${DISTPREFIX}\n\t${INSTALL} -d /tmp/${DISTPREFIX}\n\tcp -RPp * /tmp/${DISTPREFIX}\n\t${MAKE} -C /tmp/${DISTPREFIX} distclean\n\ttar cf - -C /tmp ${DISTPREFIX} | xz >${DISTFILE}\n\tls -l ${DISTFILE}\n\n_import: dist\n\trm -rf ${DESTDIR}/*\n\t${INSTALL} -d ${DESTDIR}\n\ttar xvpf ${DISTFILE} -C ${DESTDIR} --strip 1\n\t@${ECHO}\n\t@${ECHO} \"=============================================================\"\n\t@${ECHO} \"${PACKAGE}-${VERSION} imported to ${DESTDIR}\"\n\nimport:\n\t${MAKE} _import DESTDIR=`if [ -n \"${DESTDIR}\" ]; then echo \"${DESTDIR}\"; else  echo /tmp/${DISTPREFIX}; fi`\n\n\n_import-src: clean\n\trm -rf ${DESTDIR}/*\n\t${INSTALL} -d ${DESTDIR}\n\tcp LICENSE README.md ${DESTDIR};\n\tfor x in ${SUBDIRS}; do cd $$x; ${MAKE} DESTDIR=${DESTDIR} $@ || exit $$?; cd ..; done\n\t@${ECHO}\n\t@${ECHO} \"=============================================================\"\n\t@${ECHO} \"${PACKAGE}-${VERSION} imported to ${DESTDIR}\"\n\nimport-src:\n\t${MAKE} _import-src DESTDIR=`if [ -n \"${DESTDIR}\" ]; then echo \"${DESTDIR}\"; else  echo /tmp/${DISTPREFIX}; fi`\n\ninclude Makefile.inc\n"
  },
  {
    "path": "Makefile.inc",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2009-2025 Roy Marples <roy@marples.name>\n\n# System definitions\n\nPICFLAG?=\t-fPIC\n\nBINMODE?=\t0555\nNONBINMODE?=\t0444\nMANMODE?=\t${NONBINMODE}\nCONFMODE?=\t0644\nDBMODE?=\t0750\n\nCC?=\t\tcc\nECHO?=\t\techo\nINSTALL?=\tinstall\nLINT?=\t\tlint\nSED?=\t\tsed\nHOST_SH?=\t/bin/sh\n\n# This isn't very portable, but I generaly make releases from NetBSD\nSHA256?=\tsha256\n# Force gpg2 to avoid SHA1 signatures from gpg1\nPGP?=\t\tgpg2\n\nSCRIPT=\t\t${LIBEXECDIR}/dhcpcd-run-hooks\nHOOKDIR=\t${LIBEXECDIR}/dhcpcd-hooks\n\nSED_RUNDIR=\t\t-e 's:@RUNDIR@:${RUNDIR}:g'\nSED_DBDIR=\t\t-e 's:@DBDIR@:${DBDIR}:g'\nSED_LIBDIR=\t\t-e 's:@LIBDIR@:${LIBDIR}:g'\nSED_DATADIR=\t\t-e 's:@DATADIR@:${DATADIR}:g'\nSED_HOOKDIR=\t\t-e 's:@HOOKDIR@:${HOOKDIR}:g'\nSED_SERVICEEXISTS=\t-e 's:@SERVICEEXISTS@:${SERVICEEXISTS}:g'\nSED_SERVICECMD=\t\t-e 's:@SERVICECMD@:${SERVICECMD}:g'\nSED_SERVICESTATUS=\t-e 's:@SERVICESTATUS@:${SERVICESTATUS}:g'\nSED_STATUSARG=\t\t-e 's:@STATUSARG@:${STATUSARG}:g'\nSED_SCRIPT=\t\t-e 's:@SCRIPT@:${SCRIPT}:g'\nSED_SYS=\t\t-e 's:@SYSCONFDIR@:${SYSCONFDIR}:g'\nSED_DEFAULT_HOSTNAME=\t-e 's:@DEFAULT_HOSTNAME@:${DEFAULT_HOSTNAME}:g'\n"
  },
  {
    "path": "README.md",
    "content": "<!-- SPDX-License-Identifier: BSD-2-Clause -->\n<!-- Copyright (c) 2017-2023 Roy Marples <roy@marples.name> -->\n\n# dhcpcd\n\ndhcpcd is a\n[DHCP](https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol) and a\n[DHCPv6](https://en.wikipedia.org/wiki/DHCPv6) client.\nIt's also an IPv4LL (aka [ZeroConf](https://en.wikipedia.org/wiki/Zeroconf))\nclient.\nIn layperson's terms, dhcpcd runs on your machine and silently configures your\ncomputer to work on the attached networks without trouble and mostly without\nconfiguration.\n\nIf you're a desktop user then you may also be interested in\n[Network Configurator (dhcpcd-ui)](http://roy.marples.name/projects/dhcpcd-ui)\nwhich sits in the notification area and monitors the state of the network via\ndhcpcd.\nIt also has a nice configuration dialog and the ability to enter a pass phrase\nfor wireless networks.\n\ndhcpcd may not be the only daemon running that wants to configure DNS on the\nhost, so it uses [openresolv](http://roy.marples.name/projects/openresolv)\nto ensure they can co-exist.\n\nSee [BUILDING.md](BUILDING.md) for how to build dhcpcd.\n\n## Configuration\n\nYou should read the dhcpcd.conf man page\nand put your options into `/etc/dhcpcd.conf`.\nThe default configuration file should work for most people just fine.\nHere it is, in case you lose it.\n\n```\n# A sample configuration for dhcpcd.\n# See dhcpcd.conf(5) for details.\n\n# Allow users of this group to interact with dhcpcd via the control socket.\n#controlgroup wheel\n\n# Inform the DHCP server of our hostname for DDNS.\nhostname\n\n# Use the hardware address of the interface for the Client ID.\n#clientid\n# or\n# Use the same DUID + IAID as set in DHCPv6 for DHCPv4 ClientID as per RFC4361.\n# Some non-RFC compliant DHCP servers do not reply with this set.\n# In this case, comment out duid and enable clientid above.\nduid\n\n# Persist interface configuration when dhcpcd exits.\npersistent\n\n# Rapid commit support.\n# Safe to enable by default because it requires the equivalent option set\n# on the server to actually work.\noption rapid_commit\n\n# A list of options to request from the DHCP server.\noption domain_name_servers, domain_name, domain_search, host_name\noption classless_static_routes\n# Respect the network MTU. This is applied to DHCP routes.\noption interface_mtu\n\n# Most distributions have NTP support.\n#option ntp_servers\n\n# A ServerID is required by RFC2131.\nrequire dhcp_server_identifier\n\n# Generate SLAAC address using the Hardware Address of the interface\n#slaac hwaddr\n# OR generate Stable Private IPv6 Addresses based from the DUID\nslaac private\n```\n\nThe dhcpcd man page has a lot of the same options and more,\nwhich only apply to calling dhcpcd from the command line.\n\n\n## Compatibility\ndhcpcd-5 is only fully command line compatible with dhcpcd-4. \nFor compatibility with older versions, use dhcpcd-4.\n\n## Upgrading\ndhcpcd-7 defaults the database directory to `/var/db/dhcpcd` instead of\n`/var/db` and now stores dhcpcd.duid and dhcpcd.secret in there instead of\nin /etc.\n\ndhcpcd-9 defaults the run directory to `/var/run/dhcpcd` instead of\n`/var/run` and the prefix of dhcpcd has been removed from the files therein.\n\n## ChangeLog\nWe no longer supply a ChangeLog.\nHowever, you're more than welcome to read the\n[commit log](https://github.com/NetworkConfiguration/dhcpcd/commits) and\n[release announcements](https://github.com/NetworkConfiguration/dhcpcd/releases).\n"
  },
  {
    "path": "compat/_strtoi.h",
    "content": "/*\t$NetBSD: _strtoi.h,v 1.1 2015/01/22 02:15:59 christos Exp $\t*/\n\n/*-\n * SPDX-License-Identifier: BSD-3-Clause\n * Copyright (c) 1990, 1993\n *\tThe Regents of the University of California.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the University nor the names of its contributors\n *    may be used to endorse or promote products derived from this software\n *    without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n *\n * Original version ID:\n * NetBSD: src/lib/libc/locale/_wcstoul.h,v 1.2 2003/08/07 16:43:03 agc Exp\n *\n * Created by Kamil Rytarowski, based on ID:\n * NetBSD: src/common/lib/libc/stdlib/_strtoul.h,v 1.7 2013/05/17 12:55:56 joerg Exp\n */\n\n#ifndef _STRTOI_H\n#define\t_STRTOI_H\n\n/*\n * function template for strtoi and strtou\n *\n * parameters:\n *\t_FUNCNAME    : function name\n *      __TYPE       : return and range limits type\n *      __WRAPPED    : wrapped function, strtoimax or strtoumax\n */\n\n__TYPE\n_FUNCNAME(const char * __restrict nptr, char ** __restrict endptr, int base,\n          __TYPE lo, __TYPE hi, int * rstatus)\n{\n\tint serrno;\n\t__TYPE im;\n\tchar *ep;\n\tint rep;\n\n\t/* endptr may be NULL */\n\n\tif (endptr == NULL)\n\t\tendptr = &ep;\n\n\tif (rstatus == NULL)\n\t\trstatus = &rep;\n\n\tserrno = errno;\n\terrno = 0;\n\n\tim = __WRAPPED(nptr, endptr, base);\n\n\t*rstatus = errno;\n\terrno = serrno;\n\n\tif (*rstatus == 0) {\n\t\t/* No digits were found */\n\t\tif (nptr == *endptr)\n\t\t\t*rstatus = ECANCELED;\n\t\t/* There are further characters after number */\n\t\telse if (**endptr != '\\0')\n\t\t\t*rstatus = ENOTSUP;\n\t}\n\n\tif (im < lo) {\n\t\tif (*rstatus == 0)\n\t\t\t*rstatus = ERANGE;\n\t\treturn lo;\n\t}\n\tif (im > hi) {\n\t\tif (*rstatus == 0)\n\t\t\t*rstatus = ERANGE;\n\t\treturn hi;\n\t}\n\n\treturn im;\n}\n#endif\n"
  },
  {
    "path": "compat/arc4random.c",
    "content": "/*\t$OpenBSD: arc4random.c,v 1.58 2022/07/31 13:41:45 tb Exp $\t*/\n\n/*\n * SPDX-License-Identifier: ISC\n * Copyright (c) 1996, David Mazieres <dm@uun.org>\n * Copyright (c) 2008, Damien Miller <djm@openbsd.org>\n * Copyright (c) 2013, Markus Friedl <markus@openbsd.org>\n * Copyright (c) 2014, Theo de Raadt <deraadt@openbsd.org>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n/*\n * ChaCha based random number generator for OpenBSD.\n */\n\n/*\n * OPENBSD ORIGINAL: lib/libc/crypt/arc4random.c\n *                   lib/libc/crypt/arc4random.h\n */\n\n#include \"config.h\"\n\n#include <fcntl.h>\n#include <limits.h>\n#include <signal.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <sys/types.h>\n#include <sys/time.h>\n\n#if defined(HAVE_OPENSSL)\n#include <openssl/rand.h>\n#endif\n\n#define KEYSTREAM_ONLY\n#include \"chacha_private.h\"\n\n#define minimum(a, b) ((a) < (b) ? (a) : (b))\n\n#if defined(__GNUC__) || defined(_MSC_VER)\n#define inline __inline\n#else\t\t\t\t/* __GNUC__ || _MSC_VER */\n#define inline\n#endif\t\t\t\t/* !__GNUC__ && !_MSC_VER */\n\n#define KEYSZ\t32\n#define IVSZ\t8\n#define BLOCKSZ\t64\n#define RSBUFSZ\t(16*BLOCKSZ)\n\n#define REKEY_BASE\t(1024*1024) /* NB. should be a power of 2 */\n\n/* Marked MAP_INHERIT_ZERO, so zero'd out in fork children. */\nstatic struct _rs {\n\tsize_t\t\trs_have;\t/* valid bytes at end of rs_buf */\n\tsize_t\t\trs_count;\t/* bytes till reseed */\n} *rs;\n\n/* Maybe be preserved in fork children, if _rs_allocate() decides. */\nstatic struct _rsx {\n\tchacha_ctx\trs_chacha;\t/* chacha context for random keystream */\n\tu_char\t\trs_buf[RSBUFSZ];\t/* keystream blocks */\n} *rsx;\n\nstatic int _dhcpcd_rand_fd = -1;\t/* /dev/urandom fd */\n\nstatic int _dhcpcd_getentropy(void *, size_t);\nstatic inline int _rs_allocate(struct _rs **, struct _rsx **);\n\n/* dhcpcd needs to hold onto the fd at fork due to privsep */\n#if 0\nstatic inline void _rs_forkdetect(void);\n#else\n#define _rs_forkdetect()\n#define _rs_forkhandler()\n#endif\n\n/* Inline \"arc4random.h\" */\n#include <sys/types.h>\n#include <sys/mman.h>\n\nstatic inline void _rs_rekey(u_char *dat, size_t datlen);\n\n/* dhcpcd isn't multithreaded */\n#define _ARC4_LOCK()\n#define _ARC4_UNLOCK()\n\nstatic int\n_dhcpcd_getentropy(void *buf, size_t length)\n{\n\tstruct timeval\t tv;\n\tuint8_t\t\t*rand = (uint8_t *)buf;\n\n#if defined (HAVE_OPENSSL)\n\tif (RAND_priv_bytes(buf, (int)length) == 1)\n\t\treturn (0);\n#endif\n\n\tif (length < sizeof(tv)) {\n\t\tgettimeofday(&tv, NULL);\n\t\tmemcpy(buf, &tv, sizeof(tv));\n\t\tlength -= sizeof(tv);\n\t\trand += sizeof(tv);\n\t}\n\tif (_dhcpcd_rand_fd == -1)\n\t\t_dhcpcd_rand_fd = open(\"/dev/urandom\", O_RDONLY | O_NONBLOCK);\n\tif (_dhcpcd_rand_fd != -1) {\n\t\t/* coverity[check_return] */\n\t\t(void)read(_dhcpcd_rand_fd, rand, length);\n\t}\n\n\t/* Never fail. If there is an error reading from /dev/urandom,\n         * just use what is on the stack. */\n\treturn (0);\n}\n\nstatic inline void\n_getentropy_fail(void)\n{\n\traise(SIGKILL);\n}\n\n#if 0\nstatic volatile sig_atomic_t _rs_forked;\n\nstatic inline void\n_rs_forkhandler(void)\n{\n\t_rs_forked = 1;\n}\n\nstatic inline void\n_rs_forkdetect(void)\n{\n\tstatic pid_t _rs_pid = 0;\n\tpid_t pid = getpid();\n\n        /* XXX unusual calls to clone() can bypass checks */\n\tif (_rs_pid == 0 || _rs_pid == 1 || _rs_pid != pid || _rs_forked) {\n\t\t_rs_pid = pid;\n\t\t_rs_forked = 0;\n\t\tif (rs)\n\t\t\tmemset(rs, 0, sizeof(*rs));\n\t}\n}\n#endif\n\nstatic inline int\n_rs_allocate(struct _rs **rsp, struct _rsx **rsxp)\n{\n\tif ((*rsp = mmap(NULL, sizeof(**rsp), PROT_READ|PROT_WRITE,\n\t    MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED)\n\t\treturn (-1);\n\n\tif ((*rsxp = mmap(NULL, sizeof(**rsxp), PROT_READ|PROT_WRITE,\n\t    MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) {\n\t\tmunmap(*rsp, sizeof(**rsp));\n\t\t*rsp = NULL;\n\t\treturn (-1);\n\t}\n\n\t_rs_forkhandler();\n\treturn (0);\n}\n\nstatic inline void\n_rs_init(u_char *buf, size_t n)\n{\n\tif (n < KEYSZ + IVSZ)\n\t\treturn;\n\n\tif (rs == NULL) {\n\t\tif (_rs_allocate(&rs, &rsx) == -1)\n\t\t\t_exit(1);\n\t}\n\n\tchacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8);\n\tchacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ);\n}\n\nstatic void\n_rs_stir(void)\n{\n\tu_char rnd[KEYSZ + IVSZ];\n\tuint32_t rekey_fuzz = 0;\n\n\tif (_dhcpcd_getentropy(rnd, sizeof rnd) == -1)\n\t\t_getentropy_fail();\n\n\tif (!rs)\n\t\t_rs_init(rnd, sizeof(rnd));\n\telse\n\t\t_rs_rekey(rnd, sizeof(rnd));\n#if defined(HAVE_EXPLICIT_BZERO)\n\texplicit_bzero(rnd, sizeof(rnd));\t/* discard source seed */\n#elif defined(HAVE_MEMSET_EXPLICIT)\n\t(void)memset_explicit(rnd, 0, sizeof(rnd));\n#elif defined(HAVE_MEMSET_S)\n\t(void)memset_s(rnd, sizeof(rnd), 0, sizeof(rnd));\n#else\n#warning potentially insecure use of memset discarding the source seed\n\t(void)memset(rnd, 0, sizeof(rnd));\t/* discard source seed */\n#endif\n\n\t/* invalidate rs_buf */\n\trs->rs_have = 0;\n\tmemset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));\n\n\t/* rekey interval should not be predictable */\n\tchacha_encrypt_bytes(&rsx->rs_chacha, (uint8_t *)&rekey_fuzz,\n\t    (uint8_t *)&rekey_fuzz, sizeof(rekey_fuzz));\n\trs->rs_count = REKEY_BASE + (rekey_fuzz % REKEY_BASE);\n}\n\nstatic inline void\n_rs_stir_if_needed(size_t len)\n{\n\t_rs_forkdetect();\n\tif (!rs || rs->rs_count <= len)\n\t\t_rs_stir();\n\tif (rs->rs_count <= len)\n\t\trs->rs_count = 0;\n\telse\n\t\trs->rs_count -= len;\n}\n\nstatic inline void\n_rs_rekey(u_char *dat, size_t datlen)\n{\n#ifndef KEYSTREAM_ONLY\n\tmemset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));\n#endif\n\t/* fill rs_buf with the keystream */\n\tchacha_encrypt_bytes(&rsx->rs_chacha, rsx->rs_buf,\n\t    rsx->rs_buf, sizeof(rsx->rs_buf));\n\t/* mix in optional user provided data */\n\tif (dat) {\n\t\tsize_t i, m;\n\n\t\tm = minimum(datlen, KEYSZ + IVSZ);\n\t\tfor (i = 0; i < m; i++)\n\t\t\trsx->rs_buf[i] ^= dat[i];\n\t}\n\t/* immediately reinit for backtracking resistance */\n\t_rs_init(rsx->rs_buf, KEYSZ + IVSZ);\n\tmemset(rsx->rs_buf, 0, KEYSZ + IVSZ);\n\trs->rs_have = sizeof(rsx->rs_buf) - KEYSZ - IVSZ;\n}\n\nstatic inline void\n_rs_random_buf(void *_buf, size_t n)\n{\n\tu_char *buf = (u_char *)_buf;\n\tu_char *keystream;\n\tsize_t m;\n\n\t_rs_stir_if_needed(n);\n\twhile (n > 0) {\n\t\tif (rs->rs_have > 0) {\n\t\t\tm = minimum(n, rs->rs_have);\n\t\t\tkeystream = rsx->rs_buf + sizeof(rsx->rs_buf)\n\t\t\t    - rs->rs_have;\n\t\t\tmemcpy(buf, keystream, m);\n\t\t\tmemset(keystream, 0, m);\n\t\t\tbuf += m;\n\t\t\tn -= m;\n\t\t\trs->rs_have -= m;\n\t\t}\n\t\tif (rs->rs_have == 0)\n\t\t\t_rs_rekey(NULL, 0);\n\t}\n}\n\nstatic inline void\n_rs_random_u32(uint32_t *val)\n{\n\tu_char *keystream;\n\n\t_rs_stir_if_needed(sizeof(*val));\n\tif (rs->rs_have < sizeof(*val))\n\t\t_rs_rekey(NULL, 0);\n\tkeystream = rsx->rs_buf + sizeof(rsx->rs_buf) - rs->rs_have;\n\tmemcpy(val, keystream, sizeof(*val));\n\tmemset(keystream, 0, sizeof(*val));\n\trs->rs_have -= sizeof(*val);\n}\n\nuint32_t\narc4random(void)\n{\n\tuint32_t val;\n\n\t_ARC4_LOCK();\n\t_rs_random_u32(&val);\n\t_ARC4_UNLOCK();\n\treturn val;\n}\n\nvoid\narc4random_buf(void *buf, size_t n)\n{\n\t_ARC4_LOCK();\n\t_rs_random_buf(buf, n);\n\t_ARC4_UNLOCK();\n}\n"
  },
  {
    "path": "compat/arc4random.h",
    "content": "/*\n * Arc4 random number generator for OpenBSD.\n * SPDX-License-Identifier: ISC\n * Copyright 1996 David Mazieres <dm@lcs.mit.edu>.\n *\n * Modification and redistribution in source and binary forms is\n * permitted provided that due credit is given to the author and the\n * OpenBSD project by leaving this copyright notice intact.\n */\n\n#ifndef ARC4RANDOM_H\n#define ARC4RANDOM_H\n\n#include <stdint.h>\n\nuint32_t arc4random(void);\nvoid arc4random_buf(void *, size_t);\n\n#endif\n"
  },
  {
    "path": "compat/arc4random_uniform.c",
    "content": "/*\t$OpenBSD: arc4random_uniform.c,v 1.3 2019/01/20 02:59:07 bcook Exp $\t*/\n\n/*\n * SPDX-License-Identifier: ISC\n * Copyright (c) 2008, Damien Miller <djm@openbsd.org>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include <stdint.h>\n#include <stdlib.h>\n\n/* We need to include config.h so we pickup either the system arc4random\n * or our compat one. */\n#include \"config.h\"\n\n/*\n * Calculate a uniformly distributed random number less than upper_bound\n * avoiding \"modulo bias\".\n *\n * Uniformity is achieved by generating new random numbers until the one\n * returned is outside the range [0, 2**32 % upper_bound).  This\n * guarantees the selected random number will be inside\n * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound)\n * after reduction modulo upper_bound.\n */\nuint32_t\narc4random_uniform(uint32_t upper_bound)\n{\n\tuint32_t r, min;\n\n\tif (upper_bound < 2)\n\t\treturn 0;\n\n\t/* 2**32 % x == (2**32 - x) % x */\n\tmin = -upper_bound % upper_bound;\n\n\t/*\n\t * This could theoretically loop forever but each retry has\n\t * p > 0.5 (worst case, usually far better) of selecting a\n\t * number inside the range we need, so it should rarely need\n\t * to re-roll.\n\t */\n\tfor (;;) {\n\t\tr = arc4random();\n\t\tif (r >= min)\n\t\t\tbreak;\n\t}\n\n\treturn r % upper_bound;\n}\n"
  },
  {
    "path": "compat/arc4random_uniform.h",
    "content": "/*\n * SPDX-License-Identifier: ISC\n * Copyright (c) 2008, Damien Miller <djm@openbsd.org>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#ifndef ARC4RANDOM_UNIFORM_H\n#define ARC4RANDOM_UNIFORM_H\n\n#include <stdint.h>\n\nuint32_t arc4random_uniform(uint32_t);\n#endif\n"
  },
  {
    "path": "compat/bitops.h",
    "content": "/*\t$NetBSD: bitops.h,v 1.11 2012/12/07 02:27:58 christos Exp $\t*/\n\n/*-\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2007, 2010 The NetBSD Foundation, Inc.\n * All rights reserved.\n *\n * This code is derived from software contributed to The NetBSD Foundation\n * by Christos Zoulas and Joerg Sonnenberger.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS\n * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\n * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS\n * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef COMPAT_BITOPS_H\n#define COMPAT_BITOPS_H\n\n#include <stdint.h>\n#include \"common.h\"\n\n/*\n * Find First Set functions\n */\n#ifndef ffs32\nstatic inline int __unused\nffs32(uint32_t _n)\n{\n\tint _v;\n\n\tif (!_n)\n\t\treturn 0;\n\n\t_v = 1;\n\tif ((_n & 0x0000FFFFU) == 0) {\n\t\t_n >>= 16;\n\t\t_v += 16;\n\t}\n\tif ((_n & 0x000000FFU) == 0) {\n\t\t_n >>= 8;\n\t\t_v += 8;\n\t}\n\tif ((_n & 0x0000000FU) == 0) {\n\t\t_n >>= 4;\n\t\t_v += 4;\n\t}\n\tif ((_n & 0x00000003U) == 0) {\n\t\t_n >>= 2;\n\t\t_v += 2;\n\t}\n\tif ((_n & 0x00000001U) == 0) {\n\t\t//_n >>= 1;\n\t\t_v += 1;\n\t}\n\treturn _v;\n}\n#endif\n\n#ifndef ffs64\nstatic inline int __unused\nffs64(uint64_t _n)\n{\n\tint _v;\n\n\tif (!_n)\n\t\treturn 0;\n\n\t_v = 1;\n\tif ((_n & 0x00000000FFFFFFFFULL) == 0) {\n\t\t_n >>= 32;\n\t\t_v += 32;\n\t}\n\tif ((_n & 0x000000000000FFFFULL) == 0) {\n\t\t_n >>= 16;\n\t\t_v += 16;\n\t}\n\tif ((_n & 0x00000000000000FFULL) == 0) {\n\t\t_n >>= 8;\n\t\t_v += 8;\n\t}\n\tif ((_n & 0x000000000000000FULL) == 0) {\n\t\t_n >>= 4;\n\t\t_v += 4;\n\t}\n\tif ((_n & 0x0000000000000003ULL) == 0) {\n\t\t_n >>= 2;\n\t\t_v += 2;\n\t}\n\tif ((_n & 0x0000000000000001ULL) == 0) {\n\t\t//_n >>= 1;\n\t\t_v += 1;\n\t}\n\treturn _v;\n}\n#endif\n\n/*\n * Find Last Set functions\n */\n#ifndef fls32\nstatic __inline int __unused\nfls32(uint32_t _n)\n{\n\tint _v;\n\n\tif (!_n)\n\t\treturn 0;\n\n\t_v = 32;\n\tif ((_n & 0xFFFF0000U) == 0) {\n\t\t_n <<= 16;\n\t\t_v -= 16;\n\t}\n\tif ((_n & 0xFF000000U) == 0) {\n\t\t_n <<= 8;\n\t\t_v -= 8;\n\t}\n\tif ((_n & 0xF0000000U) == 0) {\n\t\t_n <<= 4;\n\t\t_v -= 4;\n\t}\n\tif ((_n & 0xC0000000U) == 0) {\n\t\t_n <<= 2;\n\t\t_v -= 2;\n\t}\n\tif ((_n & 0x80000000U) == 0) {\n\t\t//_n <<= 1;\n\t\t_v -= 1;\n\t}\n\treturn _v;\n}\n#endif\n\n#ifndef fls64\nstatic int __unused\nfls64(uint64_t _n)\n{\n\tint _v;\n\n\tif (!_n)\n\t\treturn 0;\n\n\t_v = 64;\n\tif ((_n & 0xFFFFFFFF00000000ULL) == 0) {\n\t\t_n <<= 32;\n\t\t_v -= 32;\n\t}\n\tif ((_n & 0xFFFF000000000000ULL) == 0) {\n\t\t_n <<= 16;\n\t\t_v -= 16;\n\t}\n\tif ((_n & 0xFF00000000000000ULL) == 0) {\n\t\t_n <<= 8;\n\t\t_v -= 8;\n\t}\n\tif ((_n & 0xF000000000000000ULL) == 0) {\n\t\t_n <<= 4;\n\t\t_v -= 4;\n\t}\n\tif ((_n & 0xC000000000000000ULL) == 0) {\n\t\t_n <<= 2;\n\t\t_v -= 2;\n\t}\n\tif ((_n & 0x8000000000000000ULL) == 0) {\n\t\t//_n <<= 1;\n\t\t_v -= 1;\n\t}\n\treturn _v;\n}\n#endif\n\n#endif /* COMPAT_BITOPS_H_ */\n"
  },
  {
    "path": "compat/chacha_private.h",
    "content": "/*\n * SPDX-License-Identifier: CC0-1.0\n *\n * chacha-merged.c version 20080118\n * D. J. Bernstein\n * Public domain.\n */\n\n/* $OpenBSD: chacha_private.h,v 1.3 2022/02/28 21:56:29 dtucker Exp $ */\n\ntypedef unsigned char u8;\ntypedef unsigned int u32;\n\ntypedef struct\n{\n  u32 input[16]; /* could be compressed */\n} chacha_ctx;\n\n#define U8C(v) (v##U)\n#define U32C(v) (v##U)\n\n#define U8V(v) ((u8)(v) & U8C(0xFF))\n#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF))\n\n#define ROTL32(v, n) \\\n  (U32V((v) << (n)) | ((v) >> (32 - (n))))\n\n#define U8TO32_LITTLE(p) \\\n  (((u32)((p)[0])      ) | \\\n   ((u32)((p)[1]) <<  8) | \\\n   ((u32)((p)[2]) << 16) | \\\n   ((u32)((p)[3]) << 24))\n\n#define U32TO8_LITTLE(p, v) \\\n  do { \\\n    (p)[0] = U8V((v)      ); \\\n    (p)[1] = U8V((v) >>  8); \\\n    (p)[2] = U8V((v) >> 16); \\\n    (p)[3] = U8V((v) >> 24); \\\n  } while (0)\n\n#define ROTATE(v,c) (ROTL32(v,c))\n#define XOR(v,w) ((v) ^ (w))\n#define PLUS(v,w) (U32V((v) + (w)))\n#define PLUSONE(v) (PLUS((v),1))\n\n#define QUARTERROUND(a,b,c,d) \\\n  a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \\\n  c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \\\n  a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \\\n  c = PLUS(c,d); b = ROTATE(XOR(b,c), 7);\n\nstatic const char sigma[16] = \"expand 32-byte k\";\nstatic const char tau[16] = \"expand 16-byte k\";\n\nstatic void\nchacha_keysetup(chacha_ctx *x,const u8 *k,u32 kbits)\n{\n  const char *constants;\n\n  x->input[4] = U8TO32_LITTLE(k + 0);\n  x->input[5] = U8TO32_LITTLE(k + 4);\n  x->input[6] = U8TO32_LITTLE(k + 8);\n  x->input[7] = U8TO32_LITTLE(k + 12);\n  if (kbits == 256) { /* recommended */\n    k += 16;\n    constants = sigma;\n  } else { /* kbits == 128 */\n    constants = tau;\n  }\n  x->input[8] = U8TO32_LITTLE(k + 0);\n  x->input[9] = U8TO32_LITTLE(k + 4);\n  x->input[10] = U8TO32_LITTLE(k + 8);\n  x->input[11] = U8TO32_LITTLE(k + 12);\n  x->input[0] = U8TO32_LITTLE(constants + 0);\n  x->input[1] = U8TO32_LITTLE(constants + 4);\n  x->input[2] = U8TO32_LITTLE(constants + 8);\n  x->input[3] = U8TO32_LITTLE(constants + 12);\n}\n\nstatic void\nchacha_ivsetup(chacha_ctx *x,const u8 *iv)\n{\n  x->input[12] = 0;\n  x->input[13] = 0;\n  x->input[14] = U8TO32_LITTLE(iv + 0);\n  x->input[15] = U8TO32_LITTLE(iv + 4);\n}\n\nstatic void\nchacha_encrypt_bytes(chacha_ctx *x,const u8 *m,u8 *c,u32 bytes)\n{\n  u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;\n  u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;\n  u8 *ctarget = NULL;\n  u8 tmp[64];\n  u_int i;\n\n  if (!bytes) return;\n\n  j0 = x->input[0];\n  j1 = x->input[1];\n  j2 = x->input[2];\n  j3 = x->input[3];\n  j4 = x->input[4];\n  j5 = x->input[5];\n  j6 = x->input[6];\n  j7 = x->input[7];\n  j8 = x->input[8];\n  j9 = x->input[9];\n  j10 = x->input[10];\n  j11 = x->input[11];\n  j12 = x->input[12];\n  j13 = x->input[13];\n  j14 = x->input[14];\n  j15 = x->input[15];\n\n  for (;;) {\n    if (bytes < 64) {\n      for (i = 0;i < bytes;++i) tmp[i] = m[i];\n      m = tmp;\n      ctarget = c;\n      c = tmp;\n    }\n    x0 = j0;\n    x1 = j1;\n    x2 = j2;\n    x3 = j3;\n    x4 = j4;\n    x5 = j5;\n    x6 = j6;\n    x7 = j7;\n    x8 = j8;\n    x9 = j9;\n    x10 = j10;\n    x11 = j11;\n    x12 = j12;\n    x13 = j13;\n    x14 = j14;\n    x15 = j15;\n    for (i = 20;i > 0;i -= 2) {\n      QUARTERROUND( x0, x4, x8,x12)\n      QUARTERROUND( x1, x5, x9,x13)\n      QUARTERROUND( x2, x6,x10,x14)\n      QUARTERROUND( x3, x7,x11,x15)\n      QUARTERROUND( x0, x5,x10,x15)\n      QUARTERROUND( x1, x6,x11,x12)\n      QUARTERROUND( x2, x7, x8,x13)\n      QUARTERROUND( x3, x4, x9,x14)\n    }\n    x0 = PLUS(x0,j0);\n    x1 = PLUS(x1,j1);\n    x2 = PLUS(x2,j2);\n    x3 = PLUS(x3,j3);\n    x4 = PLUS(x4,j4);\n    x5 = PLUS(x5,j5);\n    x6 = PLUS(x6,j6);\n    x7 = PLUS(x7,j7);\n    x8 = PLUS(x8,j8);\n    x9 = PLUS(x9,j9);\n    x10 = PLUS(x10,j10);\n    x11 = PLUS(x11,j11);\n    x12 = PLUS(x12,j12);\n    x13 = PLUS(x13,j13);\n    x14 = PLUS(x14,j14);\n    x15 = PLUS(x15,j15);\n\n#ifndef KEYSTREAM_ONLY\n    x0 = XOR(x0,U8TO32_LITTLE(m + 0));\n    x1 = XOR(x1,U8TO32_LITTLE(m + 4));\n    x2 = XOR(x2,U8TO32_LITTLE(m + 8));\n    x3 = XOR(x3,U8TO32_LITTLE(m + 12));\n    x4 = XOR(x4,U8TO32_LITTLE(m + 16));\n    x5 = XOR(x5,U8TO32_LITTLE(m + 20));\n    x6 = XOR(x6,U8TO32_LITTLE(m + 24));\n    x7 = XOR(x7,U8TO32_LITTLE(m + 28));\n    x8 = XOR(x8,U8TO32_LITTLE(m + 32));\n    x9 = XOR(x9,U8TO32_LITTLE(m + 36));\n    x10 = XOR(x10,U8TO32_LITTLE(m + 40));\n    x11 = XOR(x11,U8TO32_LITTLE(m + 44));\n    x12 = XOR(x12,U8TO32_LITTLE(m + 48));\n    x13 = XOR(x13,U8TO32_LITTLE(m + 52));\n    x14 = XOR(x14,U8TO32_LITTLE(m + 56));\n    x15 = XOR(x15,U8TO32_LITTLE(m + 60));\n#endif\n\n    j12 = PLUSONE(j12);\n    if (!j12) {\n      j13 = PLUSONE(j13);\n      /* stopping at 2^70 bytes per nonce is user's responsibility */\n    }\n\n    U32TO8_LITTLE(c + 0,x0);\n    U32TO8_LITTLE(c + 4,x1);\n    U32TO8_LITTLE(c + 8,x2);\n    U32TO8_LITTLE(c + 12,x3);\n    U32TO8_LITTLE(c + 16,x4);\n    U32TO8_LITTLE(c + 20,x5);\n    U32TO8_LITTLE(c + 24,x6);\n    U32TO8_LITTLE(c + 28,x7);\n    U32TO8_LITTLE(c + 32,x8);\n    U32TO8_LITTLE(c + 36,x9);\n    U32TO8_LITTLE(c + 40,x10);\n    U32TO8_LITTLE(c + 44,x11);\n    U32TO8_LITTLE(c + 48,x12);\n    U32TO8_LITTLE(c + 52,x13);\n    U32TO8_LITTLE(c + 56,x14);\n    U32TO8_LITTLE(c + 60,x15);\n\n    if (bytes <= 64) {\n      if (bytes < 64) {\n        for (i = 0;i < bytes;++i) ctarget[i] = c[i];\n      }\n      x->input[12] = j12;\n      x->input[13] = j13;\n      return;\n    }\n    bytes -= 64;\n    c += 64;\n#ifndef KEYSTREAM_ONLY\n    m += 64;\n#endif\n  }\n}\n"
  },
  {
    "path": "compat/closefrom.c",
    "content": "/*\n * SPDX-License-Identifier: ISC\n *\n * Copyright (c) 2004-2005, 2007, 2010, 2012-2015, 2017-2018\n *\tTodd C. Miller <Todd.Miller@sudo.ws>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include \"config.h\"\n\n#ifdef __linux__\n# include <sys/syscall.h>\n# if defined(__NR_close_range) && !defined(SYS_close_range)\n#  define SYS_close_range __NR_close_range\n# endif\n#endif\n\n#include <fcntl.h>\n#include <limits.h>\n#include <unistd.h>\n\n#if defined(__linux__) && defined(SYS_close_range)\nstatic inline int\nsys_close_range(unsigned int fd, unsigned int max_fd, unsigned int flags)\n{\n\n\treturn (int)syscall(SYS_close_range, fd, max_fd, flags);\n}\n#endif\n\n/*\n * Close all file descriptors greater than or equal to lowfd.\n * This is the expensive (fallback) method.\n */\nstatic int\nclosefrom_fallback(int lowfd)\n{\n\tint fd, maxfd;\n\n#ifdef _SC_OPEN_MAX\n\tmaxfd = (int)sysconf(_SC_OPEN_MAX);\n#else\n\tmaxfd = getdtablesize();\n#endif\n\tif (maxfd == -1)\n\t\treturn -1;\n\n\tfor (fd = lowfd; fd < maxfd; fd++)\n\t       close(fd);\n\treturn 0;\n}\n\n/*\n *  * Close all file descriptors greater than or equal to lowfd.\n *   * We try the fast way first, falling back on the slow method.\n *    */\nvoid\nclosefrom(int lowfd)\n{\n\n#if defined(__linux__) && defined(SYS_close_range)\n\tif (sys_close_range((unsigned int)lowfd, UINT_MAX, 0) == 0)\n\t\treturn;\n#endif\n\n\tclosefrom_fallback(lowfd);\n}\n"
  },
  {
    "path": "compat/closefrom.h",
    "content": "/*\n * SPDX-License-Identifier: ISC\n * Copyright (c) 2004-2005, 2007, 2010, 2012-2015, 2017-2018\n *\tTodd C. Miller <Todd.Miller@sudo.ws>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n#ifndef CLOSEFROM_H\n#define CLOSEFROM_H\n\nvoid closefrom(int);\n\n#endif\n"
  },
  {
    "path": "compat/consttime_memequal.h",
    "content": "/*\n * SPDX-License-Identifier: CC0-1.0\n * Written by Matthias Drochner <drochner@NetBSD.org>.\n * Public domain.\n */\n\n#ifndef CONSTTIME_MEMEQUAL_H\n#define CONSTTIME_MEMEQUAL_H\ninline static int\nconsttime_memequal(const void *b1, const void *b2, size_t len)\n{\n\tconst unsigned char *c1 = b1, *c2 = b2;\n\tunsigned int res = 0;\n\n\twhile (len--)\n\t\tres |= *c1++ ^ *c2++;\n\n\t/*\n\t * Map 0 to 1 and [1, 256) to 0 using only constant-time\n\t * arithmetic.\n\t *\n\t * This is not simply `!res' because although many CPUs support\n\t * branchless conditional moves and many compilers will take\n\t * advantage of them, certain compilers generate branches on\n\t * certain CPUs for `!res'.\n\t */\n\treturn (1 & ((res - 1) >> 8));\n}\n#endif /* CONSTTIME_MEMEQUAL_H */\n"
  },
  {
    "path": "compat/crypt/hmac.c",
    "content": "/*\t$NetBSD: hmac.c,v 1.5 2017/10/05 09:59:04 roy Exp $\t*/\n\n/*-\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2016 The NetBSD Foundation, Inc.\n * All rights reserved.\n *\n * This code is derived from software contributed to The NetBSD Foundation\n * by Christos Zoulas.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS\n * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\n * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS\n * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <string.h>\n#include <stdlib.h>\n\n#include \"config.h\"\n\n#if defined(HAVE_MD5_H) && !defined(DEPGEN)\n#include <md5.h>\n#endif\n\n#ifdef SHA2_H\n#  include SHA2_H\n#endif\n\n#ifndef __arraycount\n#define\t__arraycount(__x)       (sizeof(__x) / sizeof(__x[0]))\n#endif\n\n#if 0\n#include <md2.h>\n#include <md4.h>\n#include <md5.h>\n#include <rmd160.h>\n#include <sha1.h>\n#include <sha2.h>\n#endif\n\n#ifndef MD5_BLOCK_LENGTH\n#define\tMD5_BLOCK_LENGTH\t64\n#endif\n#ifndef SHA256_BLOCK_LENGTH\n#define\tSHA256_BLOCK_LENGTH\t64\n#endif\n\n#define HMAC_SIZE\t128\n#define HMAC_IPAD\t0x36\n#define HMAC_OPAD\t0x5C\n\nstatic const struct hmac {\n\tconst char *name;\n\tsize_t ctxsize;\n\tsize_t digsize;\n\tsize_t blocksize;\n\tvoid (*init)(void *);\n\tvoid (*update)(void *, const uint8_t *, unsigned int);\n\tvoid (*final)(uint8_t *, void *);\n} hmacs[] = {\n#if 0\n\t{\n\t\t\"md2\", sizeof(MD2_CTX), MD2_DIGEST_LENGTH, MD2_BLOCK_LENGTH,\n\t\t(void *)MD2Init, (void *)MD2Update, (void *)MD2Final,\n\t},\n\t{\n\t\t\"md4\", sizeof(MD4_CTX), MD4_DIGEST_LENGTH, MD4_BLOCK_LENGTH,\n\t\t(void *)MD4Init, (void *)MD4Update, (void *)MD4Final,\n\t},\n#endif\n\t{\n\t\t\"md5\", sizeof(MD5_CTX), MD5_DIGEST_LENGTH, MD5_BLOCK_LENGTH,\n\t\t(void *)MD5Init, (void *)MD5Update, (void *)MD5Final,\n\t},\n#if 0\n\t{\n\t\t\"rmd160\", sizeof(RMD160_CTX), RMD160_DIGEST_LENGTH,\n\t\tRMD160_BLOCK_LENGTH,\n\t\t(void *)RMD160Init, (void *)RMD160Update, (void *)RMD160Final,\n\t},\n\t{\n\t\t\"sha1\", sizeof(SHA1_CTX), SHA1_DIGEST_LENGTH, SHA1_BLOCK_LENGTH,\n\t\t(void *)SHA1Init, (void *)SHA1Update, (void *)SHA1Final,\n\t},\n\t{\n\t\t\"sha224\", sizeof(SHA224_CTX), SHA224_DIGEST_LENGTH,\n\t\tSHA224_BLOCK_LENGTH,\n\t\t(void *)SHA224_Init, (void *)SHA224_Update,\n\t\t(void *)SHA224_Final,\n\t},\n#endif\n\t{\n\t\t\"sha256\", sizeof(SHA256_CTX), SHA256_DIGEST_LENGTH,\n\t\tSHA256_BLOCK_LENGTH,\n\t\t(void *)SHA256_Init, (void *)SHA256_Update,\n\t\t(void *)SHA256_Final,\n\t},\n#if 0\n\t{\n\t\t\"sha384\", sizeof(SHA384_CTX), SHA384_DIGEST_LENGTH,\n\t\tSHA384_BLOCK_LENGTH,\n\t\t(void *)SHA384_Init, (void *)SHA384_Update,\n\t\t(void *)SHA384_Final,\n\t},\n\t{\n\t\t\"sha512\", sizeof(SHA512_CTX), SHA512_DIGEST_LENGTH,\n\t\tSHA512_BLOCK_LENGTH,\n\t\t(void *)SHA512_Init, (void *)SHA512_Update,\n\t\t(void *)SHA512_Final,\n\t},\n#endif\n};\n\nstatic const struct hmac *\nhmac_find(const char *name)\n{\n\tfor (size_t i = 0; i < __arraycount(hmacs); i++) {\n\t\tif (strcmp(hmacs[i].name, name) != 0)\n\t\t\tcontinue;\n\t\treturn &hmacs[i];\n\t}\n\treturn NULL;\n}\n\nssize_t\nhmac(const char *name,\n    const void *key, size_t klen,\n    const void *text, size_t tlen,\n    void *digest, size_t dlen)\n{\n\tuint8_t ipad[HMAC_SIZE], opad[HMAC_SIZE], d[HMAC_SIZE];\n\tconst uint8_t *k = key;\n\tconst struct hmac *h;\n\tuint64_t c[32];\n\tvoid *p;\n\n\tif ((h = hmac_find(name)) == NULL)\n\t\treturn -1;\n\n\n\tif (klen > h->blocksize) {\n\t\t(*h->init)(c);\n\t\t(*h->update)(c, k, (unsigned int)klen);\n\t\t(*h->final)(d, c);\n\t\tk = (void *)d;\n\t\tklen = h->digsize;\n\t}\n\n\t/* Form input and output pads for the digests */\n\tfor (size_t i = 0; i < sizeof(ipad); i++) {\n\t\tipad[i] = (i < klen ? k[i] : 0) ^ HMAC_IPAD;\n\t\topad[i] = (i < klen ? k[i] : 0) ^ HMAC_OPAD;\n\t}\n\n\tp = dlen >= h->digsize ? digest : d;\n\tif (p != digest) {\n\t\tmemcpy(p, digest, dlen);\n\t\tmemset((char *)p + dlen, 0, h->digsize - dlen);\n\t}\n\t(*h->init)(c);\n\t(*h->update)(c, ipad, (unsigned int)h->blocksize);\n\t(*h->update)(c, text, (unsigned int)tlen);\n\t(*h->final)(p, c);\n\n\t(*h->init)(c);\n\t(*h->update)(c, opad, (unsigned int)h->blocksize);\n\t(*h->update)(c, digest, (unsigned int)h->digsize);\n\t(*h->final)(p, c);\n\n\tif (p != digest)\n\t\tmemcpy(digest, p, dlen);\n\n\treturn (ssize_t)h->digsize;\n}\n"
  },
  {
    "path": "compat/crypt/hmac.h",
    "content": "/*\t$NetBSD: hmac.c,v 1.5 2017/10/05 09:59:04 roy Exp $\t*/\n\n/*-\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2016 The NetBSD Foundation, Inc.\n * All rights reserved.\n *\n * This code is derived from software contributed to The NetBSD Foundation\n * by Christos Zoulas.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS\n * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\n * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS\n * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef HMAC_H\n#define HMAC_H\n\n#include <sys/types.h>\n\nssize_t\t hmac(const char *, const void *, size_t, const void *, size_t, void *,\n   size_t);\n\n#endif\n"
  },
  {
    "path": "compat/crypt/md5.c",
    "content": "/* SPDX-License-Identifier: CC0-1.0\n * This code implements the MD5 message-digest algorithm.\n * The algorithm is due to Ron Rivest.\tThis code was\n * written by Colin Plumb in 1993, no copyright is claimed.\n * This code is in the public domain; do with it what you wish.\n *\n * Equivalent code is available from RSA Data Security, Inc.\n * This code has been tested against that, and is equivalent,\n * except that you don't need to include two pages of legalese\n * with every copy.\n *\n * To compute the message digest of a chunk of bytes, declare an\n * MD5Context structure, pass it to MD5Init, call MD5Update as\n * needed on buffers full of bytes, and then call MD5Final, which\n * will fill a supplied 16-byte array with the digest.\n */\n\n#include <sys/param.h>\n#include <inttypes.h>\n\n#include <string.h>\n\n#include \"md5.h\"\n\n#define PUT_64BIT_LE(cp, value) do {\t\t\t\t\t\\\n\t(cp)[7] = (uint8_t)((value) >> 56);\t\t\t\t\\\n\t(cp)[6] = (uint8_t)((value) >> 48);\t\t\t\t\\\n\t(cp)[5] = (uint8_t)((value) >> 40);\t\t\t\t\\\n\t(cp)[4] = (uint8_t)((value) >> 32);\t\t\t\t\\\n\t(cp)[3] = (uint8_t)((value) >> 24);\t\t\t\t\\\n\t(cp)[2] = (uint8_t)((value) >> 16);\t\t\t\t\\\n\t(cp)[1] = (uint8_t)((value) >> 8);\t\t\t\t\\\n\t(cp)[0] = (uint8_t)(value); } while (0)\n\n#define PUT_32BIT_LE(cp, value) do {\t\t\t\t\t\\\n\t(cp)[3] = (uint8_t)((value) >> 24);\t\t\t\t\\\n\t(cp)[2] = (uint8_t)((value) >> 16);\t\t\t\t\\\n\t(cp)[1] = (uint8_t)((value) >> 8);\t\t\t\t\\\n\t(cp)[0] = (uint8_t)(value); } while (0)\n\nstatic uint8_t PADDING[MD5_BLOCK_LENGTH] = {\n\t0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n};\n\n/*\n * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious\n * initialization constants.\n */\nvoid\nMD5Init(MD5_CTX *ctx)\n{\n\tctx->count = 0;\n\tctx->state[0] = 0x67452301;\n\tctx->state[1] = 0xefcdab89;\n\tctx->state[2] = 0x98badcfe;\n\tctx->state[3] = 0x10325476;\n}\n\n\n/* The four core functions - F1 is optimized somewhat */\n\n/* #define F1(x, y, z) (x & y | ~x & z) */\n#define F1(x, y, z) (z ^ (x & (y ^ z)))\n#define F2(x, y, z) F1(z, x, y)\n#define F3(x, y, z) (x ^ y ^ z)\n#define F4(x, y, z) (y ^ (x | ~z))\n\n/* This is the central step in the MD5 algorithm. */\n#define MD5STEP(f, w, x, y, z, data, s) \\\n\t( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )\n\n/*\n * The core of the MD5 algorithm, this alters an existing MD5 hash to\n * reflect the addition of 16 longwords of new data.  MD5Update blocks\n * the data and converts bytes into longwords for this routine.\n */\nstatic void\nMD5Transform(uint32_t state[4], const uint8_t block[MD5_BLOCK_LENGTH])\n{\n\tuint32_t a, b, c, d, in[MD5_BLOCK_LENGTH / 4];\n\n#if BYTE_ORDER == LITTLE_ENDIAN\n\tmemcpy(in, block, sizeof(in));\n#else\n\tfor (a = 0; a < MD5_BLOCK_LENGTH / 4; a++) {\n\t\tin[a] = (uint32_t)(\n\t\t    (uint32_t)(block[a * 4 + 0]) |\n\t\t    (uint32_t)(block[a * 4 + 1]) <<  8 |\n\t\t    (uint32_t)(block[a * 4 + 2]) << 16 |\n\t\t    (uint32_t)(block[a * 4 + 3]) << 24);\n\t}\n#endif\n\n\ta = state[0];\n\tb = state[1];\n\tc = state[2];\n\td = state[3];\n\n\tMD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478,  7);\n\tMD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12);\n\tMD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17);\n\tMD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22);\n\tMD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf,  7);\n\tMD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12);\n\tMD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17);\n\tMD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22);\n\tMD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8,  7);\n\tMD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12);\n\tMD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);\n\tMD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);\n\tMD5STEP(F1, a, b, c, d, in[12] + 0x6b901122,  7);\n\tMD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);\n\tMD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);\n\tMD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);\n\n\tMD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562,  5);\n\tMD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340,  9);\n\tMD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);\n\tMD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20);\n\tMD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d,  5);\n\tMD5STEP(F2, d, a, b, c, in[10] + 0x02441453,  9);\n\tMD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);\n\tMD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20);\n\tMD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6,  5);\n\tMD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6,  9);\n\tMD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14);\n\tMD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20);\n\tMD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905,  5);\n\tMD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8,  9);\n\tMD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14);\n\tMD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);\n\n\tMD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942,  4);\n\tMD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11);\n\tMD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);\n\tMD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);\n\tMD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44,  4);\n\tMD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11);\n\tMD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16);\n\tMD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);\n\tMD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6,  4);\n\tMD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11);\n\tMD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16);\n\tMD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23);\n\tMD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039,  4);\n\tMD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);\n\tMD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);\n\tMD5STEP(F3, b, c, d, a, in[2 ] + 0xc4ac5665, 23);\n\n\tMD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244,  6);\n\tMD5STEP(F4, d, a, b, c, in[7 ] + 0x432aff97, 10);\n\tMD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);\n\tMD5STEP(F4, b, c, d, a, in[5 ] + 0xfc93a039, 21);\n\tMD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3,  6);\n\tMD5STEP(F4, d, a, b, c, in[3 ] + 0x8f0ccc92, 10);\n\tMD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);\n\tMD5STEP(F4, b, c, d, a, in[1 ] + 0x85845dd1, 21);\n\tMD5STEP(F4, a, b, c, d, in[8 ] + 0x6fa87e4f,  6);\n\tMD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);\n\tMD5STEP(F4, c, d, a, b, in[6 ] + 0xa3014314, 15);\n\tMD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);\n\tMD5STEP(F4, a, b, c, d, in[4 ] + 0xf7537e82,  6);\n\tMD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);\n\tMD5STEP(F4, c, d, a, b, in[2 ] + 0x2ad7d2bb, 15);\n\tMD5STEP(F4, b, c, d, a, in[9 ] + 0xeb86d391, 21);\n\n\tstate[0] += a;\n\tstate[1] += b;\n\tstate[2] += c;\n\tstate[3] += d;\n}\n\n/*\n * Update context to reflect the concatenation of another buffer full\n * of bytes.\n */\nvoid\nMD5Update(MD5_CTX *ctx, const unsigned char *input, size_t len)\n{\n\tsize_t have, need;\n\n\t/* Check how many bytes we already have and how many more we need. */\n\thave = (size_t)((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1));\n\tneed = MD5_BLOCK_LENGTH - have;\n\n\t/* Update bitcount */\n\tctx->count += (uint64_t)len << 3;\n\n\tif (len >= need) {\n\t\tif (have != 0) {\n\t\t\tmemcpy(ctx->buffer + have, input, need);\n\t\t\tMD5Transform(ctx->state, ctx->buffer);\n\t\t\tinput += need;\n\t\t\tlen -= need;\n\t\t\thave = 0;\n\t\t}\n\n\t\t/* Process data in MD5_BLOCK_LENGTH-byte chunks. */\n\t\twhile (len >= MD5_BLOCK_LENGTH) {\n\t\t\tMD5Transform(ctx->state, input);\n\t\t\tinput += MD5_BLOCK_LENGTH;\n\t\t\tlen -= MD5_BLOCK_LENGTH;\n\t\t}\n\t}\n\n\t/* Handle any remaining bytes of data. */\n\tif (len != 0)\n\t\tmemcpy(ctx->buffer + have, input, len);\n}\n\n/*\n * Final wrapup - pad to 64-byte boundary with the bit pattern\n * 1 0* (64-bit count of bits processed, MSB-first)\n */\nvoid\nMD5Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *ctx)\n{\n\tuint8_t count[8];\n\tsize_t padlen;\n\tint i;\n\n\t/* Convert count to 8 bytes in little endian order. */\n\tPUT_64BIT_LE(count, ctx->count);\n\n\t/* Pad out to 56 mod 64. */\n\tpadlen = MD5_BLOCK_LENGTH -\n\t    ((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1));\n\tif (padlen < 1 + 8)\n\t\tpadlen += MD5_BLOCK_LENGTH;\n\tMD5Update(ctx, PADDING, padlen - 8);\t\t/* padlen - 8 <= 64 */\n\tMD5Update(ctx, count, 8);\n\n\tif (digest != NULL) {\n\t\tfor (i = 0; i < 4; i++)\n\t\t\tPUT_32BIT_LE(digest + i * 4, ctx->state[i]);\n\t}\n\tmemset(ctx, 0, sizeof(*ctx));\t/* in case it's sensitive */\n}\n\n\n"
  },
  {
    "path": "compat/crypt/md5.h",
    "content": "/*\n * This code implements the MD5 message-digest algorithm.\n * SPDX-License-Identifier: CC0-1.0\n * The algorithm is due to Ron Rivest.\tThis code was\n * written by Colin Plumb in 1993, no copyright is claimed.\n * This code is in the public domain; do with it what you wish.\n *\n * Equivalent code is available from RSA Data Security, Inc.\n * This code has been tested against that, and is equivalent,\n * except that you don't need to include two pages of legalese\n * with every copy.\n *\n * To compute the message digest of a chunk of bytes, declare an\n * MD5Context structure, pass it to MD5Init, call MD5Update as\n * needed on buffers full of bytes, and then call MD5Final, which\n * will fill a supplied 16-byte array with the digest.\n */\n\n#ifndef MD5_H_\n#define MD5_H_\n\n#define MD5_DIGEST_LENGTH\t16\n#define MD5_BLOCK_LENGTH\t64\n\ntypedef struct MD5Context {\n\tuint32_t state[4];\t/* state (ABCD) */\n\tuint64_t count;\t\t/* number of bits, modulo 2^64 (lsb first) */\n\tunsigned char buffer[MD5_BLOCK_LENGTH]; /* input buffer */\n} MD5_CTX;\n\nvoid\tMD5Init(MD5_CTX *);\nvoid\tMD5Update(MD5_CTX *, const unsigned char *, size_t);\nvoid\tMD5Final(unsigned char[MD5_DIGEST_LENGTH], MD5_CTX *);\n#endif\n"
  },
  {
    "path": "compat/crypt/sha256.c",
    "content": "/*-\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright 2005 Colin Percival\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n/* For BSD */\n#if (defined(__unix__) || defined(unix)) && !defined(USG)\n#include <sys/param.h>\n#endif\n\n#include <inttypes.h>\n#include <string.h>\n\n#ifdef __GLIBC__\n#  include <endian.h>\n#endif\n#ifdef BSD\n#  ifndef __QNX__\n#    include <sys/endian.h>\n#  endif\n#endif\n\n#include \"config.h\"\n#include \"sha256.h\"\n\n#if BYTE_ORDER == BIG_ENDIAN\n\n/* Copy a vector of big-endian uint32_t into a vector of bytes */\n#define be32enc_vect(dst, src, len)\t\\\n\tmemcpy((void *)dst, (const void *)src, (size_t)len)\n\n/* Copy a vector of bytes into a vector of big-endian uint32_t */\n#define be32dec_vect(dst, src, len)\t\\\n\tmemcpy((void *)dst, (const void *)src, (size_t)len)\n\n#else /* BYTE_ORDER != BIG_ENDIAN */\n\n/*\n * Encode a length len/4 vector of (uint32_t) into a length len vector of\n * (unsigned char) in big-endian form.  Assumes len is a multiple of 4.\n */\nstatic void\nbe32enc_vect(unsigned char *dst, const uint32_t *src, size_t len)\n{\n\tsize_t i;\n\n\tfor (i = 0; i < len / 4; i++)\n\t\tbe32enc(dst + i * 4, src[i]);\n}\n\n/*\n * Decode a big-endian length len vector of (unsigned char) into a length\n * len/4 vector of (uint32_t).  Assumes len is a multiple of 4.\n */\nstatic void\nbe32dec_vect(uint32_t *dst, const unsigned char *src, size_t len)\n{\n\tsize_t i;\n\n\tfor (i = 0; i < len / 4; i++)\n\t\tdst[i] = be32dec(src + i * 4);\n}\n\n#endif /* BYTE_ORDER != BIG_ENDIAN */\n\n/* Elementary functions used by SHA256 */\n#define Ch(x, y, z)\t((x & (y ^ z)) ^ z)\n#define Maj(x, y, z)\t((x & (y | z)) | (y & z))\n#define SHR(x, n)\t(x >> n)\n#define ROTR(x, n)\t((x >> n) | (x << (32 - n)))\n#define S0(x)\t\t(ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))\n#define S1(x)\t\t(ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))\n#define s0(x)\t\t(ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3))\n#define s1(x)\t\t(ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10))\n\n/* SHA256 round function */\n#define RND(a, b, c, d, e, f, g, h, k)\t\t\t\\\n\tt0 = h + S1(e) + Ch(e, f, g) + k;\t\t\\\n\tt1 = S0(a) + Maj(a, b, c);\t\t\t\\\n\td += t0;\t\t\t\t\t\\\n\th  = t0 + t1;\n\n/* Adjusted round function for rotating state */\n#define RNDr(S, W, i, k)\t\t\t\\\n\tRND(S[(64 - i) % 8], S[(65 - i) % 8],\t\\\n\t    S[(66 - i) % 8], S[(67 - i) % 8],\t\\\n\t    S[(68 - i) % 8], S[(69 - i) % 8],\t\\\n\t    S[(70 - i) % 8], S[(71 - i) % 8],\t\\\n\t    W[i] + k)\n\n/*\n * SHA256 block compression function.  The 256-bit state is transformed via\n * the 512-bit input block to produce a new state.\n */\nstatic void\nSHA256_Transform(uint32_t * state, const unsigned char block[64])\n{\n\tuint32_t W[64];\n\tuint32_t S[8];\n\tuint32_t t0, t1;\n\tint i;\n\n\t/* 1. Prepare message schedule W. */\n\tbe32dec_vect(W, block, 64);\n\tfor (i = 16; i < 64; i++)\n\t\tW[i] = s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16];\n\n\t/* 2. Initialize working variables. */\n\tmemcpy(S, state, 32);\n\n\t/* 3. Mix. */\n\tRNDr(S, W, 0, 0x428a2f98);\n\tRNDr(S, W, 1, 0x71374491);\n\tRNDr(S, W, 2, 0xb5c0fbcf);\n\tRNDr(S, W, 3, 0xe9b5dba5);\n\tRNDr(S, W, 4, 0x3956c25b);\n\tRNDr(S, W, 5, 0x59f111f1);\n\tRNDr(S, W, 6, 0x923f82a4);\n\tRNDr(S, W, 7, 0xab1c5ed5);\n\tRNDr(S, W, 8, 0xd807aa98);\n\tRNDr(S, W, 9, 0x12835b01);\n\tRNDr(S, W, 10, 0x243185be);\n\tRNDr(S, W, 11, 0x550c7dc3);\n\tRNDr(S, W, 12, 0x72be5d74);\n\tRNDr(S, W, 13, 0x80deb1fe);\n\tRNDr(S, W, 14, 0x9bdc06a7);\n\tRNDr(S, W, 15, 0xc19bf174);\n\tRNDr(S, W, 16, 0xe49b69c1);\n\tRNDr(S, W, 17, 0xefbe4786);\n\tRNDr(S, W, 18, 0x0fc19dc6);\n\tRNDr(S, W, 19, 0x240ca1cc);\n\tRNDr(S, W, 20, 0x2de92c6f);\n\tRNDr(S, W, 21, 0x4a7484aa);\n\tRNDr(S, W, 22, 0x5cb0a9dc);\n\tRNDr(S, W, 23, 0x76f988da);\n\tRNDr(S, W, 24, 0x983e5152);\n\tRNDr(S, W, 25, 0xa831c66d);\n\tRNDr(S, W, 26, 0xb00327c8);\n\tRNDr(S, W, 27, 0xbf597fc7);\n\tRNDr(S, W, 28, 0xc6e00bf3);\n\tRNDr(S, W, 29, 0xd5a79147);\n\tRNDr(S, W, 30, 0x06ca6351);\n\tRNDr(S, W, 31, 0x14292967);\n\tRNDr(S, W, 32, 0x27b70a85);\n\tRNDr(S, W, 33, 0x2e1b2138);\n\tRNDr(S, W, 34, 0x4d2c6dfc);\n\tRNDr(S, W, 35, 0x53380d13);\n\tRNDr(S, W, 36, 0x650a7354);\n\tRNDr(S, W, 37, 0x766a0abb);\n\tRNDr(S, W, 38, 0x81c2c92e);\n\tRNDr(S, W, 39, 0x92722c85);\n\tRNDr(S, W, 40, 0xa2bfe8a1);\n\tRNDr(S, W, 41, 0xa81a664b);\n\tRNDr(S, W, 42, 0xc24b8b70);\n\tRNDr(S, W, 43, 0xc76c51a3);\n\tRNDr(S, W, 44, 0xd192e819);\n\tRNDr(S, W, 45, 0xd6990624);\n\tRNDr(S, W, 46, 0xf40e3585);\n\tRNDr(S, W, 47, 0x106aa070);\n\tRNDr(S, W, 48, 0x19a4c116);\n\tRNDr(S, W, 49, 0x1e376c08);\n\tRNDr(S, W, 50, 0x2748774c);\n\tRNDr(S, W, 51, 0x34b0bcb5);\n\tRNDr(S, W, 52, 0x391c0cb3);\n\tRNDr(S, W, 53, 0x4ed8aa4a);\n\tRNDr(S, W, 54, 0x5b9cca4f);\n\tRNDr(S, W, 55, 0x682e6ff3);\n\tRNDr(S, W, 56, 0x748f82ee);\n\tRNDr(S, W, 57, 0x78a5636f);\n\tRNDr(S, W, 58, 0x84c87814);\n\tRNDr(S, W, 59, 0x8cc70208);\n\tRNDr(S, W, 60, 0x90befffa);\n\tRNDr(S, W, 61, 0xa4506ceb);\n\tRNDr(S, W, 62, 0xbef9a3f7);\n\tRNDr(S, W, 63, 0xc67178f2);\n\n\t/* 4. Mix local working variables into global state */\n\tfor (i = 0; i < 8; i++)\n\t\tstate[i] += S[i];\n}\n\nstatic unsigned char PAD[64] = {\n\t0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n\t0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n};\n\n/* Add padding and terminating bit-count. */\nstatic void\nSHA256_Pad(SHA256_CTX * ctx)\n{\n\tunsigned char len[8];\n\tuint32_t r, plen;\n\n\t/*\n\t * Convert length to a vector of bytes -- we do this now rather\n\t * than later because the length will change after we pad.\n\t */\n\tbe64enc(len, ctx->count);\n\n\t/* Add 1--64 bytes so that the resulting length is 56 mod 64 */\n\tr = (ctx->count >> 3) & 0x3f;\n\tplen = (r < 56) ? (56 - r) : (120 - r);\n\tSHA256_Update(ctx, PAD, (size_t)plen);\n\n\t/* Add the terminating bit-count */\n\tSHA256_Update(ctx, len, 8);\n}\n\n/* SHA-256 initialization.  Begins a SHA-256 operation. */\nvoid\nSHA256_Init(SHA256_CTX * ctx)\n{\n\n\t/* Zero bits processed so far */\n\tctx->count = 0;\n\n\t/* Magic initialization constants */\n\tctx->state[0] = 0x6A09E667;\n\tctx->state[1] = 0xBB67AE85;\n\tctx->state[2] = 0x3C6EF372;\n\tctx->state[3] = 0xA54FF53A;\n\tctx->state[4] = 0x510E527F;\n\tctx->state[5] = 0x9B05688C;\n\tctx->state[6] = 0x1F83D9AB;\n\tctx->state[7] = 0x5BE0CD19;\n}\n\n/* Add bytes into the hash */\nvoid\nSHA256_Update(SHA256_CTX * ctx, const void *in, size_t len)\n{\n\tuint64_t bitlen;\n\tuint32_t r;\n\tconst unsigned char *src = in;\n\n\t/* Number of bytes left in the buffer from previous updates */\n\tr = (ctx->count >> 3) & 0x3f;\n\n\t/* Convert the length into a number of bits */\n\tbitlen = len << 3;\n\n\t/* Update number of bits */\n\tctx->count += bitlen;\n\n\t/* Handle the case where we don't need to perform any transforms */\n\tif (len < 64 - r) {\n\t\tmemcpy(&ctx->buf[r], src, len);\n\t\treturn;\n\t}\n\n\t/* Finish the current block */\n\tmemcpy(&ctx->buf[r], src, 64 - r);\n\tSHA256_Transform(ctx->state, ctx->buf);\n\tsrc += 64 - r;\n\tlen -= 64 - r;\n\n\t/* Perform complete blocks */\n\twhile (len >= 64) {\n\t\tSHA256_Transform(ctx->state, src);\n\t\tsrc += 64;\n\t\tlen -= 64;\n\t}\n\n\t/* Copy left over data into buffer */\n\tmemcpy(ctx->buf, src, len);\n}\n\n/*\n * SHA-256 finalization.  Pads the input data, exports the hash value,\n * and clears the context state.\n */\nvoid\nSHA256_Final(unsigned char digest[32], SHA256_CTX * ctx)\n{\n\n\t/* Add padding */\n\tSHA256_Pad(ctx);\n\n\t/* Write the hash */\n\tbe32enc_vect(digest, ctx->state, 32);\n\n\t/* Clear the context state */\n\tmemset((void *)ctx, 0, sizeof(*ctx));\n}\n"
  },
  {
    "path": "compat/crypt/sha256.h",
    "content": "/*-\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright 2005 Colin Percival\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n *\n * $FreeBSD$\n */\n\n#ifndef SHA256_H_\n#define SHA256_H_\n\n#include <sys/types.h>\n\n#define SHA256_DIGEST_LENGTH\t\t32\n\ntypedef struct SHA256Context {\n\tuint32_t state[8];\n\tuint64_t count;\n\tunsigned char buf[64];\n} SHA256_CTX;\n\nvoid\tSHA256_Init(SHA256_CTX *);\nvoid\tSHA256_Update(SHA256_CTX *, const void *, size_t);\nvoid\tSHA256_Final(unsigned char [32], SHA256_CTX *);\n\n#endif\n"
  },
  {
    "path": "compat/crypt_openssl/hmac.c",
    "content": "/*\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2023 Canonical Ltd.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <string.h>\n#include <stdlib.h>\n\n#include \"config.h\"\n\n#include \"openssl/hmac.h\"\n\nssize_t\nhmac(const char *name,\n    const void *key, size_t klen,\n    const void *text, size_t tlen,\n    void *digest, size_t dlen)\n{\n\tconst EVP_MD\t*md;\n\tunsigned int\t outlen;\n\n\tif (strcmp(name, \"md5\") == 0)\n\t\tmd = EVP_md5();\n\telse if (strcmp(name, \"sha256\") == 0)\n\t\tmd = EVP_sha1();\n\telse\n\t\treturn -1;\n\n\tHMAC(md, key, (int)klen, text, tlen, digest, &outlen);\n\tif (dlen != outlen)\n\t\treturn -1;\n\n\treturn (ssize_t)outlen;\n}\n"
  },
  {
    "path": "compat/crypt_openssl/hmac.h",
    "content": "/*\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2023 Canonical Ltd.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef HMAC_H\n#define HMAC_H\n\n#include <sys/types.h>\n\nssize_t\t hmac(const char *, const void *, size_t, const void *, size_t, void *,\n   size_t);\n\n#endif\n"
  },
  {
    "path": "compat/crypt_openssl/sha256.c",
    "content": "/*\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2023 Canonical Ltd.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include \"config.h\"\n#include \"sha256.h\"\n\n#include \"openssl/evp.h\"\n\n/* SHA-256 initialization.  Begins a SHA-256 operation. */\nvoid\ndhcpcd_SHA256_Init(SHA256_CTX *ctx)\n{\n\tctx->c = EVP_MD_CTX_new();\n\tEVP_DigestInit_ex2(ctx->c, EVP_sha256(), NULL);\n}\n\n/* Add bytes into the hash */\nvoid\ndhcpcd_SHA256_Update(SHA256_CTX *ctx, const void *in, size_t len)\n{\n\tEVP_DigestUpdate(ctx->c, in, len);\n}\n\n/*\n * SHA-256 finalization.  Pads the input data, exports the hash value,\n * and clears the context state.\n */\nvoid\ndhcpcd_SHA256_Final(unsigned char digest[32], SHA256_CTX *ctx)\n{\n\tEVP_DigestFinal_ex(ctx->c, digest, NULL);\n\tEVP_MD_CTX_free(ctx->c);\n}\n"
  },
  {
    "path": "compat/crypt_openssl/sha256.h",
    "content": "/*\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2023 Canonical Ltd.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef SHA256_H_\n#define SHA256_H_\n\n#define SHA256_DIGEST_LENGTH\t\t32\n\n#include \"openssl/evp.h\"\ntypedef struct dhcpcd_SHA256Context {\n\tEVP_MD_CTX *c;\n} dhcpcd_SHA256_CTX;\n\nvoid\tdhcpcd_SHA256_Init(dhcpcd_SHA256_CTX *);\nvoid\tdhcpcd_SHA256_Update(dhcpcd_SHA256_CTX *, const void *, size_t);\nvoid\tdhcpcd_SHA256_Final(unsigned char [32], dhcpcd_SHA256_CTX *);\n\n#define SHA256_Init\tdhcpcd_SHA256_Init\n#define SHA256_Update\tdhcpcd_SHA256_Update\n#define SHA256_Final\tdhcpcd_SHA256_Final\n#define SHA256_CTX\tdhcpcd_SHA256_CTX\n\n#endif\n"
  },
  {
    "path": "compat/dprintf.c",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2017 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <stdarg.h>\n#include <unistd.h>\n\n#include \"dprintf.h\"\n\nint\nvdprintf(int fd, const char * __restrict fmt, va_list va)\n{\n\tint e;\n\tFILE *fp;\n\n\tif ((e = dup(fd)) == -1)\n\t\treturn -1;\n\n\tif ((fp = fdopen(e, \"a\")) == NULL) {\n\t\tclose(e);\n\t\treturn -1;\n\t}\n\n\te = vfprintf(fp, fmt, va);\n\tfclose(fp);\n\treturn e;\n}\n\nint\ndprintf(int fd, const char * __restrict fmt, ...)\n{\n\tint e;\n\tva_list va;\n\n\tva_start(va, fmt);\n\te = vdprintf(fd, fmt, va);\n\tva_end(va);\n\treturn e;\n}\n"
  },
  {
    "path": "compat/dprintf.h",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2017 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef DPRINTF_H\n#define DPRINTF_H\n\n#include <stdarg.h>\n\n#ifndef __printflike\n# if __GNUC__ > 2 || defined(__INTEL_COMPILER)\n#  define __printflike(a, b) __attribute__((format(printf, a, b)))\n# else\n#  define __printflike(a, b)\n# endif\n#endif\n\n__printflike(2, 0) int vdprintf(int, const char * __restrict, va_list);\n__printflike(2, 3) int dprintf(int, const char * __restrict, ...);\n#endif\n"
  },
  {
    "path": "compat/endian.h",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2014 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef ENDIAN_H\n#define ENDIAN_H\n\n#include <stdint.h>\n\ninline static void\nbe32enc(uint8_t *buf, uint32_t u)\n{\n\n\tbuf[0] = (uint8_t)((u >> 24) & 0xff);\n\tbuf[1] = (uint8_t)((u >> 16) & 0xff);\n\tbuf[2] = (uint8_t)((u >> 8) & 0xff);\n\tbuf[3] = (uint8_t)(u & 0xff);\n}\n\ninline static void\nbe64enc(uint8_t *buf, uint64_t u)\n{\n\n\tbe32enc(buf, (uint32_t)(u >> 32));\n\tbe32enc(buf + sizeof(uint32_t), (uint32_t)(u & 0xffffffffULL));\n}\n\ninline static uint16_t\nbe16dec(const uint8_t *buf)\n{\n\n\treturn (uint16_t)(buf[0] << 8 | buf[1]);\n}\n\ninline static uint32_t\nbe32dec(const uint8_t *buf)\n{\n\n\treturn (uint32_t)((uint32_t)be16dec(buf) << 16 | be16dec(buf + 2));\n}\n\ninline static uint64_t\nbe64dec(const uint8_t *buf)\n{\n\n\treturn (uint64_t)((uint64_t)be32dec(buf) << 32 | be32dec(buf + 4));\n}\n#endif\n"
  },
  {
    "path": "compat/pidfile.c",
    "content": "/*\t$NetBSD: pidfile.c,v 1.16 2021/08/01 15:29:29 andvar Exp $\t*/\n\n/*-\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 1999, 2016 The NetBSD Foundation, Inc.\n * All rights reserved.\n *\n * This code is derived from software contributed to The NetBSD Foundation\n * by Jason R. Thorpe, Matthias Scheler, Julio Merino and Roy Marples.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS\n * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\n * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS\n * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <sys/param.h>\n\n#include <errno.h>\n#include <fcntl.h>\n#include <inttypes.h>\n#include <limits.h>\n#include <paths.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <sys/file.h>\t/* for flock(2) */\n#include \"config.h\"\n#include \"defs.h\"\n\nstatic pid_t pidfile_pid;\nstatic char pidfile_path[PATH_MAX];\nstatic int pidfile_fd = -1;\n\n/* Closes pidfile resources.\n *\n * Returns 0 on success, otherwise -1. */\nstatic int\npidfile_close(void)\n{\n\tint error;\n\n\tpidfile_pid = 0;\n\terror = close(pidfile_fd);\n\tpidfile_fd = -1;\n\tpidfile_path[0] = '\\0';\n\treturn error;\n}\n\n/* Truncate, close and unlink an existent pidfile,\n * if and only if it was created by this process.\n * The pidfile is truncated because we may have dropped permissions\n * or entered a chroot and thus unable to unlink it.\n *\n * Returns 0 on truncation success, otherwise -1. */\nint\npidfile_clean(void)\n{\n\tint error;\n\n\tif (pidfile_fd == -1) {\n\t\terrno = EBADF;\n\t\treturn -1;\n\t}\n\n\tif (pidfile_pid != getpid())\n\t\terror = EPERM;\n\telse if (ftruncate(pidfile_fd, 0) == -1)\n\t\terror = errno;\n\telse {\n#ifndef HAVE_PLEDGE /* Avoid a pledge violating segfault. */\n\t\t(void)unlink(pidfile_path);\n#endif\n\t\terror = 0;\n\t}\n\n\t(void) pidfile_close();\n\n\tif (error != 0) {\n\t\terrno = error;\n\t\treturn -1;\n\t}\n\treturn 0;\n}\n\n/* atexit shim for pidfile_clean */\nstatic void\npidfile_cleanup(void)\n{\n\n\tpidfile_clean();\n}\n\n/* Constructs a name for a pidfile in the default location (/var/run).\n * If 'bname' is NULL, uses the name of the current program for the name of\n * the pidfile.\n *\n * Returns 0 on success, otherwise -1. */\nstatic int\npidfile_varrun_path(char *path, size_t len, const char *bname)\n{\n\n\tif (bname == NULL)\n\t\tbname = PACKAGE;\n\n\t/* _PATH_VARRUN includes trailing / */\n\tif ((size_t)snprintf(path, len, \"%s%s.pid\", _PATH_VARRUN, bname) >= len)\n\t{\n\t\terrno = ENAMETOOLONG;\n\t\treturn -1;\n\t}\n\treturn 0;\n}\n\n/* Returns the process ID inside path on success, otherwise -1.\n * If no path is given, use the last pidfile path, otherwise the default one. */\npid_t\npidfile_read(const char *path)\n{\n\tchar dpath[PATH_MAX], buf[16], *eptr;\n\tint fd, error;\n\tssize_t n;\n\tpid_t pid;\n\n\tif (path == NULL && pidfile_path[0] != '\\0')\n\t\tpath = pidfile_path;\n\tif (path == NULL || strchr(path, '/') == NULL) {\n\t\tif (pidfile_varrun_path(dpath, sizeof(dpath), path) == -1)\n\t\t\treturn -1;\n\t\tpath = dpath;\n\t}\n\n\tif ((fd = open(path, O_RDONLY | O_NONBLOCK)) == -1)\n\t\treturn  -1;\n\tn = read(fd, buf, sizeof(buf) - 1);\n\terror = errno;\n\t(void) close(fd);\n\tif (n == -1) {\n\t\terrno = error;\n\t\treturn -1;\n\t}\n\tbuf[n] = '\\0';\n\tpid = (pid_t)strtoi(buf, &eptr, 10, 1, INT_MAX, &error);\n\tif (error && !(error == ENOTSUP && *eptr == '\\n')) {\n\t\terrno = error;\n\t\treturn -1;\n\t}\n\treturn pid;\n}\n\n/* Locks the pidfile specified by path and writes the process pid to it.\n * The new pidfile is \"registered\" in the global variables pidfile_fd,\n * pidfile_path and pidfile_pid so that any further call to pidfile_lock(3)\n * can check if we are recreating the same file or a new one.\n *\n * Returns 0 on success, otherwise the pid of the process who owns the\n * lock if it can be read, otherwise -1. */\npid_t\npidfile_lock(const char *path)\n{\n\tchar dpath[PATH_MAX];\n\tstatic bool registered_atexit = false;\n\n\t/* Register for cleanup with atexit. */\n\tif (!registered_atexit) {\n\t\tif (atexit(pidfile_cleanup) == -1)\n\t\t\treturn -1;\n\t\tregistered_atexit = true;\n\t}\n\n\tif (path == NULL || strchr(path, '/') == NULL) {\n\t\tif (pidfile_varrun_path(dpath, sizeof(dpath), path) == -1)\n\t\t\treturn -1;\n\t\tpath = dpath;\n\t}\n\n\t/* If path has changed (no good reason), clean up the old pidfile. */\n\tif (pidfile_fd != -1 && strcmp(pidfile_path, path) != 0)\n\t\tpidfile_clean();\n\n\tif (pidfile_fd == -1) {\n\t\tint fd, opts;\n\n\t\topts = O_WRONLY | O_CREAT | O_NONBLOCK;\n#ifdef O_CLOEXEC\n\t\topts |= O_CLOEXEC;\n#endif\n#ifdef O_EXLOCK\n\t\topts |=\tO_EXLOCK;\n#endif\n\t\tif ((fd = open(path, opts, 0644)) == -1)\n\t\t\tgoto return_pid;\n#ifndef O_CLOEXEC\n\t\tif ((opts = fcntl(fd, F_GETFD)) == -1 ||\n\t\t    fcntl(fd, F_SETFL, opts | FD_CLOEXEC) == -1)\n\t\t{\n\t\t\tint error = errno;\n\n\t\t\t(void) close(fd);\n\t\t\terrno = error;\n\t\t\treturn -1;\n\t\t}\n#endif\n#ifndef O_EXLOCK\n\t\tif (flock(fd, LOCK_EX | LOCK_NB) == -1) {\n\t\t\tint error = errno;\n\n\t\t\t(void) close(fd);\n\t\t\tif (error != EAGAIN) {\n\t\t\t\terrno = error;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tfd = -1;\n\t\t}\n#endif\n\nreturn_pid:\n\t\tif (fd == -1) {\n\t\t\tpid_t pid;\n\n\t\t\tif (errno == EAGAIN) {\n\t\t\t\t/* The pidfile is locked, return the process ID\n\t\t\t\t * it contains.\n\t\t\t\t * If successful, set errno to EEXIST. */\n\t\t\t\tif ((pid = pidfile_read(path)) != -1)\n\t\t\t\t\terrno = EEXIST;\n\t\t\t} else\n\t\t\t\tpid = -1;\n\n\t\t\treturn pid;\n\t\t}\n\t\tpidfile_fd = fd;\n\t\tstrlcpy(pidfile_path, path, sizeof(pidfile_path));\n\t}\n\n\tpidfile_pid = getpid();\n\n\t/* Truncate the file, as we could be re-writing it.\n\t * Then write the process ID. */\n\tif (ftruncate(pidfile_fd, 0) == -1 ||\n\t    lseek(pidfile_fd, 0, SEEK_SET) == -1 ||\n\t    dprintf(pidfile_fd, \"%d\\n\", (int)pidfile_pid) == -1)\n\t{\n\t\tint error = errno;\n\n\t\tpidfile_cleanup();\n\t\terrno = error;\n\t\treturn -1;\n\t}\n\n\t/* Hold the fd open to persist the lock. */\n\treturn 0;\n}\n"
  },
  {
    "path": "compat/pidfile.h",
    "content": "/*-\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 1999, 2016 The NetBSD Foundation, Inc.\n * All rights reserved.\n *\n * This code is derived from software contributed to The NetBSD Foundation\n * by Jason R. Thorpe, Matthias Scheler, Julio Merino and Roy Marples.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS\n * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\n * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS\n * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef PIDFILE_H\n#define PIDFILE_H\n\n#include <unistd.h>\n\nint\t\tpidfile_clean(void);\npid_t\t\tpidfile_lock(const char *);\npid_t\t\tpidfile_read(const char *);\n\n#endif\n"
  },
  {
    "path": "compat/queue.h",
    "content": "/*\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2014-2025 Roy Marples <roy@marples.name>\n */\n\n/* This stub exists to avoid including queue.h in the vendor folder\n * for source imports */\n#ifdef BSD\n#include <sys/queue.h>\n#else\n#include \"../vendor/queue.h\"\n#endif\n"
  },
  {
    "path": "compat/reallocarray.c",
    "content": "/* $NetBSD: reallocarr.c,v 1.4 2015/08/20 20:08:04 joerg Exp $ */\n\n/*-\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2015 Joerg Sonnenberger <joerg@NetBSD.org>.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE\n * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <errno.h>\n#include <limits.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <stdlib.h>\n\n/*\n * To be clear, this is NetBSD's more refined reallocarr(3) function\n * made to look like OpenBSD's more useable reallocarray(3) interface.\n */\n#include \"reallocarray.h\"\n\n#define SQRT_SIZE_MAX (((size_t)1) << (sizeof(size_t) * CHAR_BIT / 2))\nvoid *\nreallocarray(void *ptr, size_t n, size_t size)\n{\n\n\t/*\n\t * Try to avoid division here.\n\t *\n\t * It isn't possible to overflow during multiplication if neither\n\t * operand uses any of the most significant half of the bits.\n\t */\n\tif ((n | size) >= SQRT_SIZE_MAX && n > SIZE_MAX / size) {\n\t\terrno = EOVERFLOW;\n\t\treturn NULL;\n\t}\n\treturn realloc(ptr, n * size);\n}\n"
  },
  {
    "path": "compat/reallocarray.h",
    "content": "/* $NetBSD: reallocarr.c,v 1.4 2015/08/20 20:08:04 joerg Exp $ */\n\n/*-\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2015 Joerg Sonnenberger <joerg@NetBSD.org>.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE\n * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef REALLOCARRAY_H\n#define REALLOCARRAY_H\n\nvoid *reallocarray(void *, size_t, size_t);\n\n#endif\n"
  },
  {
    "path": "compat/setproctitle.c",
    "content": "/*\n * SPDX-License-Identifier: MIT\n * Copyright © 2010 William Ahern\n * Copyright © 2012-2013 Guillem Jover <guillem@hadrons.org>\n *\n * Permission is hereby granted, free of charge, to any person obtaining a\n * copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to permit\n * persons to whom the Software is furnished to do so, subject to the\n * following conditions:\n *\n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n * USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#include <errno.h>\n#include <stddef.h>\n#include <stdarg.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <err.h>\n#include <unistd.h>\n#include <string.h>\n//#include \"local-link.h\"\n\n#include \"config.h\"\n\nstatic struct {\n\t/* Original value. */\n\tchar *arg0;\n\n\t/* Title space available. */\n\tchar *base, *end;\n\n\t /* Pointer to original nul character within base. */\n\tchar *nul;\n\n\tbool warned;\n\tbool reset;\n\tint error;\n\n\t/* Our copy of args and environment to free. */\n\tint argc;\n\tchar **argv;\n\tchar **tmp_environ;\n} SPT;\n\n\nstatic inline size_t\nspt_min(size_t a, size_t b)\n{\n\treturn a < b ? a : b;\n}\n\n/*\n * For discussion on the portability of the various methods, see\n * https://lists.freebsd.org/pipermail/freebsd-stable/2008-June/043136.html\n */\nstatic int\nspt_clearenv(void)\n{\n#ifdef HAVE_CLEARENV\n\treturn clearenv();\n#else\n\tSPT.tmp_environ = malloc(sizeof(*SPT.tmp_environ));\n\tif (SPT.tmp_environ == NULL)\n\t\treturn errno;\n\n\tSPT.tmp_environ[0] = NULL;\n\tenviron = SPT.tmp_environ;\n\n\treturn 0;\n#endif\n}\n\nstatic int\nspt_copyenv(int envc, char *envp[])\n{\n\tchar **envcopy;\n\tchar *eq;\n\tsize_t envsize;\n\tint i, error;\n\n\tif (environ != envp)\n\t\treturn 0;\n\n\t/* Make a copy of the old environ array of pointers, in case\n\t * clearenv() or setenv() is implemented to free the internal\n\t * environ array, because we will need to access the old environ\n\t * contents to make the new copy. */\n\tenvsize = (size_t)(envc + 1) * sizeof(char *);\n\tenvcopy = malloc(envsize);\n\tif (envcopy == NULL)\n\t\treturn errno;\n\tmemcpy(envcopy, envp, envsize);\n\n\terror = spt_clearenv();\n\tif (error) {\n\t\tenviron = envp;\n\t\tfree(envcopy);\n\t\treturn error;\n\t}\n\n\tfor (i = 0; envcopy[i]; i++) {\n\t\teq = strchr(envcopy[i], '=');\n\t\tif (eq == NULL)\n\t\t\tcontinue;\n\n\t\t*eq = '\\0';\n\t\tif (setenv(envcopy[i], eq + 1, 1) < 0)\n\t\t\terror = errno;\n\t\t*eq = '=';\n\n\t\tif (error) {\n#ifdef HAVE_CLEARENV\n\t\t\t/* Because the old environ might not be available\n\t\t\t * anymore we will make do with the shallow copy. */\n\t\t\tenviron = envcopy;\n#else\n\t\t\tenviron = envp;\n\t\t\tfree(envcopy);\n#endif\n\t\t\treturn error;\n\t\t}\n\t}\n\n\t/* Dispose of the shallow copy, now that we've finished transfering\n\t * the old environment. */\n\tfree(envcopy);\n\n\treturn 0;\n}\n\nstatic int\nspt_copyargs(int argc, char *argv[])\n{\n\tchar *tmp;\n\tint i;\n\n\tfor (i = 1; i < argc || (i >= argc && argv[i]); i++) {\n\t\tif (argv[i] == NULL)\n\t\t\tcontinue;\n\n\t\ttmp = strdup(argv[i]);\n\t\tif (tmp == NULL)\n\t\t\treturn errno;\n\n\t\targv[i] = tmp;\n\t}\n\n\treturn 0;\n}\n\nvoid\nsetproctitle_init(int argc, char *argv[], char *envp[])\n{\n\tchar *base, *end, *nul, *tmp;\n\tint i, envc, error;\n\n\t/* Try to make sure we got called with main() arguments. */\n\tif (argc < 0)\n\t\treturn;\n\n\tbase = argv[0];\n\tif (base == NULL)\n\t\treturn;\n\n\tnul = &base[strlen(base)];\n\tend = nul + 1;\n\n\tfor (i = 0; i < argc || (i >= argc && argv[i]); i++) {\n\t\tif (argv[i] == NULL || argv[i] != end)\n\t\t\tcontinue;\n\n\t\tend = argv[i] + strlen(argv[i]) + 1;\n\t}\n\n\tfor (i = 0; envp[i]; i++) {\n\t\tif (envp[i] != end)\n\t\t\tcontinue;\n\n\t\tend = envp[i] + strlen(envp[i]) + 1;\n\t}\n\tenvc = i;\n\n\tSPT.arg0 = strdup(argv[0]);\n\tif (SPT.arg0 == NULL) {\n\t\tSPT.error = errno;\n\t\treturn;\n\t}\n\n\ttmp = strdup(getprogname());\n\tif (tmp == NULL) {\n\t\tSPT.error = errno;\n\t\treturn;\n\t}\n\tsetprogname(tmp);\n\n\terror = spt_copyenv(envc, envp);\n\tif (error) {\n\t\tSPT.error = error;\n\t\treturn;\n\t}\n\n\terror = spt_copyargs(argc, argv);\n\tif (error) {\n\t\tSPT.error = error;\n\t\treturn;\n\t}\n\n\tSPT.argc = argc;\n\tSPT.argv = argv;\n\n\tSPT.nul  = nul;\n\tSPT.base = base;\n\tSPT.end  = end;\n}\n\nvoid\nsetproctitle_fini(void)\n{\n\tint i;\n\n\tfree(SPT.arg0);\n\tSPT.arg0 = NULL;\n\n\tfor (i = 1; i < SPT.argc; i++) {\n\t\tif (SPT.argv[i] != NULL)\n\t\t\tfree(SPT.argv[i]);\n\t}\n\tSPT.argc = 0;\n\n\tfree(SPT.tmp_environ);\n\tSPT.tmp_environ = NULL;\n}\n\n#ifndef SPT_MAXTITLE\n#define SPT_MAXTITLE 255\n#endif\n\n__printflike(1, 2) static void\nsetproctitle_impl(const char *fmt, ...)\n{\n\t/* Use buffer in case argv[0] is passed. */\n\tchar buf[SPT_MAXTITLE + 1];\n\tva_list ap;\n\tchar *nul;\n\tint l;\n\tsize_t len, base_len;\n\n\tif (SPT.base == NULL) {\n\t\tif (!SPT.warned) {\n\t\t\twarnx(\"setproctitle not initialized, please either call \"\n\t\t\t      \"setproctitle_init() or link against libbsd-ctor.\");\n\t\t\tSPT.warned = true;\n\t\t}\n\t\treturn;\n\t}\n\n\tif (fmt) {\n\t\tif (fmt[0] == '-') {\n\t\t\t/* Skip program name prefix. */\n\t\t\tfmt++;\n\t\t\tlen = 0;\n\t\t} else {\n\t\t\t/* Print program name heading for grep. */\n\t\t\tl = snprintf(buf, sizeof(buf), \"%s: \", getprogname());\n\t\t\tif (l <= 0)\n\t\t\t\treturn;\n\t\t\tlen = (size_t)l;\n\t\t}\n\n\t\tva_start(ap, fmt);\n\t\tl = vsnprintf(buf + len, sizeof(buf) - len, fmt, ap);\n\t\tva_end(ap);\n\t} else {\n\t\tlen = 0;\n\t\tl = snprintf(buf, sizeof(buf), \"%s\", SPT.arg0);\n\t}\n\n\tif (l <= 0) {\n\t\tSPT.error = errno;\n\t\treturn;\n\t}\n\tlen += (size_t)l;\n\n\tbase_len = (size_t)(SPT.end - SPT.base);\n\tif (!SPT.reset) {\n\t\tmemset(SPT.base, 0, base_len);\n\t\tSPT.reset = true;\n\t} else {\n\t\tmemset(SPT.base, 0, spt_min(sizeof(buf), base_len));\n\t}\n\n\tlen = spt_min(len, spt_min(sizeof(buf), base_len) - 1);\n\tmemcpy(SPT.base, buf, len);\n\tnul = &SPT.base[len];\n\n\tif (nul < SPT.nul) {\n\t\t*SPT.nul = '.';\n\t} else if (nul == SPT.nul && &nul[1] < SPT.end) {\n\t\t*SPT.nul = ' ';\n\t\t*++nul = '\\0';\n\t}\n}\nlibbsd_symver_default(setproctitle, setproctitle_impl, LIBBSD_0.5);\n\n/* The original function introduced in 0.2 was a stub, it only got implemented\n * in 0.5, make the implementation available in the old version as an alias\n * for code linking against that version, and change the default to use the\n * new version, so that new code depends on the implemented version. */\n#ifdef HAVE_TYPEOF\nextern __typeof__(setproctitle_impl)\nsetproctitle_stub\n\t__attribute__((__alias__(\"setproctitle_impl\")));\n#else\nvoid\nsetproctitle_stub(const char *fmt, ...)\n\t__attribute__((__alias__(\"setproctitle_impl\")));\n#endif\nlibbsd_symver_variant(setproctitle, setproctitle_stub, LIBBSD_0.2);\n"
  },
  {
    "path": "compat/setproctitle.h",
    "content": "/*\n * SPDX-License-Identifier: MIT\n * Copyright © 2010 William Ahern\n * Copyright © 2012-2013 Guillem Jover <guillem@hadrons.org>\n *\n * Permission is hereby granted, free of charge, to any person obtaining a\n * copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to permit\n * persons to whom the Software is furnished to do so, subject to the\n * following conditions:\n *\n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n * USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef SETPROCTITLE_H\n#define SETPROCTITLE_H\n\n#ifndef __printflike\n#if __GNUC__ > 2 || defined(__INTEL_COMPILER)\n#define\t__printflike(a, b) __attribute__((format(printf, a, b)))\n#else\n#define\t__printflike(a, b)\n#endif\n#endif /* !__printflike */\n\n/* WEXITSTATUS is defined in stdlib.h which defines free() */\n#ifdef WEXITSTATUS\nstatic inline const char *\ngetprogname(void)\n{\n\treturn \"dhcpcd\";\n}\nstatic inline void\nsetprogname(char *name)\n{\n\tfree(name);\n}\n#endif\n\nvoid setproctitle_init(int, char *[], char *[]);\n__printflike(1, 2) void setproctitle(const char *, ...);\nvoid setproctitle_fini(void);\n\n#define libbsd_symver_default(alias, symbol, version) \\\n    extern __typeof(symbol) alias __attribute__((__alias__(#symbol)))\n\n#define libbsd_symver_variant(alias, symbol, version)\n#endif\n"
  },
  {
    "path": "compat/strlcpy.c",
    "content": "/*\t$OpenBSD: strlcpy.c,v 1.16 2019/01/25 00:19:25 millert Exp $\t*/\n\n/*\n * SPDX-License-Identifier: ISC\n * Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include <sys/types.h>\n\n#include \"strlcpy.h\"\n\n/*\n * Copy string src to buffer dst of size dsize.  At most dsize-1\n * chars will be copied.  Always NUL terminates (unless dsize == 0).\n * Returns strlen(src); if retval >= dsize, truncation occurred.\n */\nsize_t\nstrlcpy(char *dst, const char *src, size_t dsize)\n{\n\tconst char *osrc = src;\n\tsize_t nleft = dsize;\n\n\t/* Copy as many bytes as will fit. */\n\tif (nleft != 0) {\n\t\twhile (--nleft != 0) {\n\t\t\tif ((*dst++ = *src++) == '\\0')\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* Not enough room in dst, add NUL and traverse rest of src. */\n\tif (nleft == 0) {\n\t\tif (dsize != 0)\n\t\t\t*dst = '\\0';\t\t/* NUL-terminate dst */\n\t\twhile (*src++)\n\t\t\t;\n\t}\n\n\treturn (size_t)(src - osrc - 1);\t/* count does not include NUL */\n}\n"
  },
  {
    "path": "compat/strlcpy.h",
    "content": "/*\t$OpenBSD: strlcpy.c,v 1.15 2016/10/16 17:37:39 dtucker Exp $\t*/\n\n/*\n * SPDX-License-Identifier: ISC\n * Copyright (c) 1998, 2015 Todd C. Miller <Todd.Miller@courtesan.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#ifndef STRLCPY_H\n#define STRLCPY_H\n\nsize_t strlcpy(char *, const char *, size_t);\n\n#endif\n"
  },
  {
    "path": "compat/strtoi.c",
    "content": "/*\t$NetBSD: strtoi.c,v 1.3 2019/11/28 12:33:23 roy Exp $\t*/\n\n/*-\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2005 The DragonFly Project.  All rights reserved.\n * Copyright (c) 2003 Citrus Project,\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n *\n * Created by Kamil Rytarowski, based on ID:\n * NetBSD: src/common/lib/libc/stdlib/strtoul.c,v 1.3 2008/08/20 19:58:34 oster Exp\n */\n\n#if defined(HAVE_NBTOOL_CONFIG_H) && HAVE_NBTOOL_CONFIG_H\n#include \"nbtool_config.h\"\n#endif\n\n#ifdef _LIBC\n#include \"namespace.h\"\n#endif\n\n#if defined(_KERNEL)\n#include <sys/param.h>\n#include <sys/types.h>\n#include <lib/libkern/libkern.h>\n#elif defined(_STANDALONE)\n#include <sys/param.h>\n#include <sys/types.h>\n#include <lib/libkern/libkern.h>\n#include <lib/libsa/stand.h>\n#else\n#include <stddef.h>\n#include <assert.h>\n#include <errno.h>\n#include <inttypes.h>\n#endif\n\n#include \"strtoi.h\"\n\n#define\t_FUNCNAME\tstrtoi\n#define\t__TYPE\t\tintmax_t\n#define\t__WRAPPED\tstrtoimax\n\n#include \"_strtoi.h\"\n\n#ifdef _LIBC\n__weak_alias(strtoi, _strtoi)\n__weak_alias(strtoi_l, _strtoi_l)\n#endif\n"
  },
  {
    "path": "compat/strtoi.h",
    "content": "/*-\n * SPDX-License-Identifier: BSD-3-Clause\n * Copyright (c) 1990, 1993\n *\tThe Regents of the University of California.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the University nor the names of its contributors\n *    may be used to endorse or promote products derived from this software\n *    without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n *\n * Original version ID:\n * NetBSD: src/lib/libc/locale/_wcstoul.h,v 1.2 2003/08/07 16:43:03 agc Exp\n *\n * Created by Kamil Rytarowski, based on ID:\n * NetBSD: src/common/lib/libc/stdlib/_strtoul.h,v 1.7 2013/05/17 12:55:56 joerg Exp\n */\n\n#ifndef STRTOI_H\n#define STRTOI_H\n\n#include <inttypes.h>\n\nintmax_t strtoi(const char * __restrict nptr, char ** __restrict endptr,\n    int base, intmax_t lo, intmax_t hi, int *rstatus);\nuintmax_t strtou(const char * __restrict nptr, char ** __restrict endptr,\n    int base, uintmax_t lo, uintmax_t hi, int *rstatus);\n#endif\n"
  },
  {
    "path": "compat/strtou.c",
    "content": "/*\t$NetBSD: strtou.c,v 1.3 2019/11/28 12:33:23 roy Exp $\t*/\n\n/*-\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2005 The DragonFly Project.  All rights reserved.\n * Copyright (c) 2003 Citrus Project,\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n *\n * Created by Kamil Rytarowski, based on ID:\n * NetBSD: src/common/lib/libc/stdlib/strtoul.c,v 1.3 2008/08/20 19:58:34 oster Exp\n */\n\n#if defined(HAVE_NBTOOL_CONFIG_H) && HAVE_NBTOOL_CONFIG_H\n#include \"nbtool_config.h\"\n#endif\n\n#ifdef _LIBC\n#include \"namespace.h\"\n#endif\n\n#if defined(_KERNEL)\n#include <sys/param.h>\n#include <sys/types.h>\n#include <lib/libkern/libkern.h>\n#elif defined(_STANDALONE)\n#include <sys/param.h>\n#include <sys/types.h>\n#include <lib/libkern/libkern.h>\n#include <lib/libsa/stand.h>\n#else\n#include <stddef.h>\n#include <assert.h>\n#include <errno.h>\n#include <inttypes.h>\n#endif\n\n#include \"strtoi.h\"\n\n#define\t_FUNCNAME\tstrtou\n#define\t__TYPE\t\tuintmax_t\n#define\t__WRAPPED\tstrtoumax\n\n#include \"_strtoi.h\"\n\n#ifdef _LIBC\n__weak_alias(strtou, _strtou)\n__weak_alias(strtou_l, _strtou_l)\n#endif\n"
  },
  {
    "path": "config-null.mk",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2014 Roy Marples <roy@marples.name>\n\n# This space left intentionally blank\n\nDHCPCD_SRCS+=\tdhcpcd-embedded.c\n"
  },
  {
    "path": "configure",
    "content": "#!/bin/sh\n# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2009-2025 Roy Marples <roy@marples.name>\n\n# Try and be like autotools configure, but without autotools\n\necho \"configure args: $*\"\nexec 3>config.log\n\n# Ensure that we do not inherit these from env\nHOOKSET=false\nINET=\nARP=\nARPING=\nIPV4LL=\nINET6=\nPRIVSEP=\nPRIVSEP_USER=\nSECCOMP=\nARC4RANDOM=\nCLOSEFROM=\nRBTREE=\nCONSTTIME_MEMEQUAL=\nOPEN_MEMSTREAM=\nSTRLCPY=\nUDEV=\nOS=\nBUILD=\nHOST=\nHOSTCC=\nTARGET=\nINCLUDEDIR=\nDEBUG=\nFORK=\nSTATIC=\nDEVS=\nEMBEDDED=\nAUTH=\nNTP=\nPOLL=\nSMALL=\nSANITIZE=no\nSTATUSARG=\nOPENSSL=\nLIBPCAP=\n\nDHCPCD_DEFS=dhcpcd-definitions.conf\n\nfor x do\n\topt=${x%%=*}\n\tvar=${x#*=}\n\tcase \"$opt\" in\n\t--os|OS) OS=$var;;\n\t--debug) DEBUG=$var;;\n\t--disable-debug) DEBUG=no;;\n\t--enable-debug) DEBUG=yes;;\n\t--fork) FORK=$var;;\n\t--disable-fork) FORK=no;;\n\t--enable-fork) FORK=yes;;\n\t--disable-static) STATIC=no;;\n\t--enable-static) STATIC=yes;;\n\t--disable-ipv4|--disable-inet) INET=no; ARP=no; ARPING=no; IPV4LL=no;;\n\t--enable-ipv4|--enable-inet) INET=yes;;\n\t--disable-arp) ARP=no; ARPING=no; IPV4LL=no;;\n\t--enable-arp) ARP=yes; INET=yes;;\n\t--disable-arping) ARPING=no;;\n\t--enable-arping) ARPING=yes; ARP=yes; INET=yes;;\n\t--disable-ipv4ll) IPV4LL=no;;\n\t--enable-ipv4ll) IPV4LL=yes; ARP=yes; INET=yes;;\n\t--disable-ipv6|--disable-inet6) INET6=no; DHCP6=no;;\n\t--enable-ipv6|--enable-inet6) INET6=yes;;\n\t--disable-dhcp6) DHCP6=no;;\n\t--enable-dhcp6) DHCP6=yes;;\n\t--disable-embedded) EMBEDDED=no;;\n\t--enable-embedded) EMBEDDED=yes;;\n\t--disable-auth) AUTH=no;;\n\t--enable-auth) AUTH=yes;;\n\t--disable-privsep) PRIVSEP=no;;\n\t--enable-privsep) PRIVSEP=yes;;\n\t--disable-seccomp) SECCOMP=no;;\n\t--enable-seccomp) SECCOMP=yes;;\n\t--disable-ntp) NTP=no;;\n\t--enable-ntp) NTP=yes;;\n\t--privsepuser) PRIVSEP_USER=$var;;\n\t--prefix) PREFIX=$var;prefix=$var;; # prefix is set for autotools compat\n\t--sysconfdir) SYSCONFDIR=$var;;\n\t--bindir|--sbindir) SBINDIR=$var;;\n\t--libexecdir) LIBEXECDIR=$var;;\n\t--statedir|--localstatedir) STATEDIR=$var;;\n\t--dbdir) DBDIR=$var;;\n\t--rundir) RUNDIR=$var;;\n\t--runstatedir) RUNSTATEDIR=$var;;\n\t--mandir) MANDIR=$var;;\n\t--datadir) DATADIR=$var;;\n\t--with-ccopts|CFLAGS) CFLAGS=$var;;\n\t-I|--includedir) INCLUDEDIR=\"$INCLUDEDIR${INCLUDEDIR:+ }-I$var\";;\n\tCC) CC=$var;;\n\tCPPFLAGS) CPPFLAGS=$var;;\n\tPKG_CONFIG) PKG_CONFIG=$var;;\n\t--with-hook) HOOKSCRIPTS=\"$HOOKSCRIPTS${HOOKSCRIPTS:+ }$var\";;\n\t--with-hooks|HOOKSCRIPTS) HOOKSCRIPTS=$var; HOOKSET=true;;\n\t--with-eghook) EGHOOKSCRIPTS=\"$EGHOOKSCRIPTS${EGHOOKSCRIPTS+ }$var\";;\n\t--with-eghooks|EGHOOKSCRIPTS) EGHOOKSCRIPTS=$var; EGHOOKSET=true;;\n\t--with-default-hostname) _DEFAULT_HOSTNAME=$var;;\n\t--build) BUILD=$var;;\n\t--host) HOST=$var; HOSTCC=$var-;;\n\t--target) TARGET=$var;;\n\t--libdir) LIBDIR=$var;;\n\t--without-arc4random) ARC4RANDOM=no;;\n\t--without-strlcpy) STRLCPY=no;;\n\t--without-pidfile_lock) PIDFILE_LOCK=no;;\n\t--without-reallocarrray) REALLOCARRAY=no;;\n\t--without-md5) MD5=no;;\n\t--without-sha2) SHA2=no;;\n\t--without-sha256) SHA2=no;;\n\t--without-hmac) HMAC=no;;\n\t--without-dev) DEV=no;;\n\t--with-udev) DEV=yes; UDEV=yes;;\n\t--without-udev) UDEV=no;;\n\t--with-poll) POLL=\"$var\";;\n\t--with-openssl) OPENSSL=yes;;\n\t--without-openssl) OPENSSL=no;;\n\t--with-libpcap) LIBPCAP=yes;;\n\t--without-libpcap) LIBPCAP=no;;\n\t--sanitise|--sanitize) SANITIZEADDRESS=\"yes\";;\n\t--serviceexists) SERVICEEXISTS=$var;;\n\t--servicecmd) SERVICECMD=$var;;\n\t--servicestatus) SERVICESTATUS=$var;;\n\t--small) SMALL=yes;;\n\t--statusarg) STATUSARG=$var;;\n\t--infodir) ;; # ignore autotools\n\t--disable-maintainer-mode|--disable-dependency-tracking) ;;\n\t--disable-option-checking|--disable-silent-rules) ;;\n\t-V|--version)\n\t\tv=$(sed -ne 's/.*VERSION[[:space:]]*\"\\([^\"]*\\).*/\\1/p' defs.h);\n\t\tc=$(sed -ne 's/^.*copyright\\[\\] = \"\\([^\"]*\\).*/\\1/p' dhcpcd.c);\n\t\techo \"dhcpcd-$v $c\";\n\t\texit 0;;\n\t-h|--help) cat <<EOF\n\\`configure' configures this package to adapt to many kinds of systems.\n\nUsage: configure [OPTION]... [VAR=VALUE]...\n\nTo assign environment variables (e.g., CC, CFLAGS...), specify them as\nVAR=VALUE.  See below for descriptions of some of the useful variables.\n\nDefaults for the options are specified in brackets.\n\nConfiguration:\n  -h, --help              display this help and exit\n      --help=short        display options specific to this package\n  -V, --version           display version information and exit\n\nInstallation directories:\n  --prefix=PREFIX         install architecture-independent files in PREFIX [/]\n\nBy default, \\`make install' will install all the files in \\'/sbin',\n\\`/libexec', etc. You can specify\nan installation prefix other than \\`/' using \\`--prefix',\nfor instance \\`--prefix=$HOME'.\n\nFor better control, use the options below.\n\nFine tuning of the installation directories:\n  --bindir=DIR            user executables [PREFIX/bin]\n  --sbindir=DIR           system admin executables [PREFIX/sbin]\n  --libexecdir=DIR        program executables [PREFIX/libexec]\n  --dbdir=DIR             database [STATEDIR/db/dhcpcd]\n  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]\n  --localstatedir=DIR     modifiable single-machine data [/var]\n  --libdir=DIR            object code libraries [PREFIX/lib]\n  --includedir=DIR        C header files [PREFIX/include]\n  --mandir=DIR            man documentation [PREFIX/man]\n\nSystem types:\n  --build=BUILD           configure for building on BUILD [guessed]\n  --host=HOST             build programs to run on HOST [BUILD]\n  --target=TARGET         configure for building compilers for TARGET [HOST]\n\nOptional Features:\n  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)\n  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]\n\nSome influential environment variables:\n  CC          C compiler command\n  CFLAGS      C compiler flags\n  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a\n\t      nonstandard directory <lib dir>\n  CPPFLAGS    C/C++ preprocessor flags, e.g. -I<include dir> if you have\n\t      headers in a nonstandard directory <include dir>\n  CPP         C preprocessor\n  PKG_CONFIG  pkg-config executable\n\nUse these variables to override the choices made by \\`configure' or to help\nit to find libraries and programs with nonstandard names/locations.\nEOF\nexit 0\n;;\n\t*) echo \"$0: WARNING: unknown option $opt\" >&2;;\n\tesac\ndone\n\n: ${SED:=sed}\n: ${GREP:=grep}\n: ${PKG_CONFIG:=pkg-config}\n: ${WC:=wc}\n\n: ${FORK:=yes}\n_which()\n{\n\tx=\"$(which \"$1\" 2>/dev/null)\"\n\tif [ $? = 0 ] && [ -n \"$x\" ]; then\n\t\techo \"$x\"\n\t\treturn 0\n\tfi\n\tfor x in /sbin/\"$1\" /usr/sbin/\"$1\" \\\n\t\t/usr/pkg/sbin/\"$1\" /usr/local/sbin/\"$1\"\n\tdo\n\t\tif [ -e \"$x\" ]; then\n\t\t\techo \"$x\"\n\t\t\treturn 0\n\t\tfi\n\tdone\n\treturn 1\n}\n\nCONFIG_H=config.h\nCONFIG_MK=config.mk\n\nif [ -z \"$BUILD\" ]; then\n\t# autoconf target triplet: cpu-vendor-os\n\tBUILD=$(uname -m)-unknown-$(uname -s | tr '[:upper:]' '[:lower:]')\nfi\n: ${HOST:=$BUILD}\n\nif [ -z \"$OS\" ]; then\n\techo \"Deriving operating system from ... $HOST\"\n\t# Derive OS from cpu-vendor-[kernel-]os\n\tCPU=${HOST%%-*}\n\tREST=${HOST#*-}\n\tif [ \"$CPU\" != \"$REST\" ]; then\n\t\tVENDOR=${REST%%-*}\n\t\tREST=${REST#*-}\n\t\tif [ \"$VENDOR\" != \"$REST\" ]; then\n\t\t\t# Use kernel if given, otherwise os\n\t\t\tOS=${REST%%-*}\n\t\telse\n\t\t\t# 2 tupple\n\t\t\tOS=$VENDOR\n\t\t\tVENDOR=\n\t\tfi\n\tfi\n\n\t# Work with cpu-kernel-os, ie Debian\n\tcase \"$VENDOR\" in\n\tlinux*|kfreebsd*) OS=$VENDOR; VENDOR= ;;\n\tesac\n\tcase \"$REST\" in\n\tgnu/kfreebsd*)\t\tOS=\"kfreebsd\"; VENDOR= ;;\n\tesac\n\t# Special case\n\tcase \"$OS\" in\n\tdragonfly*)\n\t\t# This means /usr HAS to be mounted not via dhcpcd\n\t\t: ${LIBEXECDIR:=${PREFIX:-/usr}/libexec}\n\t\t;;\n\tgnu*) OS=hurd;; # No HURD support as yet\n\tesac\nfi\n\necho \"Configuring dhcpcd for ... $OS\"\nrm -f $CONFIG_H $CONFIG_MK\necho \"# $OS\" >$CONFIG_MK\necho \"/* $OS */\" >$CONFIG_H\necho >>$CONFIG_H\necho \"#ifndef CONFIG_H\">>$CONFIG_H\necho \"#define CONFIG_H\">>$CONFIG_H\necho >>$CONFIG_H\n\n: ${SYSCONFDIR:=$PREFIX/etc}\n: ${SBINDIR:=$PREFIX/sbin}\n: ${LIBDIR:=$PREFIX/lib}\n: ${LIBEXECDIR:=$PREFIX/libexec}\n: ${STATEDIR:=/var}\n: ${DBDIR:=$STATEDIR/db/dhcpcd}\n: ${RUNSTATEDIR:=$STATEDIR/run}\n: ${RUNDIR:=$RUNSTATEDIR/dhcpcd}\n: ${MANDIR:=${PREFIX:-/usr}/share/man}\n: ${DATADIR:=${PREFIX:-/usr}/share}\n\neval SYSCONFDIR=\"$SYSCONFDIR\"\neval LIBDIR=\"$LIBDIR\"\neval LIBEXECDIR=\"$LIBEXECDIR\"\neval STATEDIR=\"$STATEDIR\"\neval DBDIR=\"$DBDIR\"\neval RUNDIR=\"$RUNDIR\"\neval MANDIR=\"$MANDIR\"\neval DATADIR=\"$DATADIR\"\n\necho \"#ifndef\tSYSCONFDIR\" >>$CONFIG_H\nfor x in SYSCONFDIR SBINDIR LIBDIR LIBEXECDIR DBDIR RUNDIR; do\n\teval v=\\$$x\n\t# Make files look nice for import\n\tl=$((10 - ${#x}))\n\tunset t\n\t[ $l -gt 3 ] && t=\"\t\"\n\techo \"$x=$t\t$v\" >>$CONFIG_MK\n\tunset t\n\t[ $l -gt 2 ] && t=\"\t\"\n\techo \"#define\t$x$t\t\t\\\"$v\\\"\" >>$CONFIG_H\ndone\necho \"#endif\" >>$CONFIG_H\n\necho \"LIBDIR=\t\t$LIBDIR\" >>$CONFIG_MK\necho \"MANDIR=\t\t$MANDIR\" >>$CONFIG_MK\necho \"DATADIR=\t$DATADIR\" >>$CONFIG_MK\n\n# Always obey CC.\nif [ -n \"$CC\" ]; then\n\tHOSTCC=\nelse\n\tCC=cc\n\t_COMPILERS=\"cc clang gcc pcc icc\"\nfi\n# Only look for a cross compiler if --host and --build are not the same\nif [ -n \"$HOSTCC\" ] && [ \"$BUILD\" != \"$HOST\" ]; then\n\tfor _CC in $_COMPILERS; do\n\t\t_CC=$(_which \"$HOSTCC$_CC\")\n\t\tif [ -x \"$_CC\" ]; then\n\t\t\tCC=$_CC\n\t\t\tbreak\n\t\tfi\n\tdone\nfi\nif ! type \"$CC\" >/dev/null 2>&1; then\n\tfor _CC in $_COMPILERS; do\n\t\t_CC=$(_which \"$_CC\")\n\t\tif [ -x \"$_CC\" ]; then\n\t\t\tCC=$_CC\n\t\t\tbreak\n\t\tfi\n\tdone\nfi\n\n# Set to blank, then append user config\n# We do this so our SED call to append to XCC remains portable\nif [ -n \"$CFLAGS\" ]; then\n\techo \"CFLAGS=\" >>$CONFIG_MK\n\techo \"CFLAGS+=\t$CFLAGS\" >>$CONFIG_MK\nfi\nif [ -n \"$CPPFLAGS\" ]; then\n\techo \"CPPFLAGS=\" >>$CONFIG_MK\n\techo \"CPPFLAGS+=\t$CPPFLAGS\" >>$CONFIG_MK\nfi\nif [ -n \"$INCLUDEDIR\" ]; then\n\techo \"CPPFLAGS+=\t$INCLUDEDIR\" >>$CONFIG_MK\nfi\nif [ -n \"$LDFLAGS\" ]; then\n\techo \"LDFLAGS=\" >>$CONFIG_MK\n\techo \"LDFLAGS+=\t$LDFLAGS\" >>$CONFIG_MK\nfi\n\necho \"CPPFLAGS+=\t-DHAVE_CONFIG_H\" >>$CONFIG_MK\n\n# NetBSD: Even if we build for $PREFIX, the clueless user might move us to /\nLDELF=/libexec/ld.elf_so\nif [ -e \"$LDELF\" ]; then\n\techo \"Linking against $LDELF\"\n\techo \"LDFLAGS+=\t-Wl,-dynamic-linker=$LDELF\" >>$CONFIG_MK\n\techo \"LDFLAGS+=\t-Wl,-rpath=${LIBDIR}\" >>$CONFIG_MK\nfi\n\nif [ -z \"$PREFIX\" ] || [ \"$PREFIX\" = / ]; then\n\tALLOW_USR_LIBS=false\nelse\n\tALLOW_USR_LIBS=true\nfi\ncase \"$OS\" in\nlinux*|solaris*|sunos*|kfreebsd*|dragonfly*|freebsd*) ;;\n*)\n\t# There might be more than one ...\n\tfor LDELFN in /libexec/ld-elf.so.[0-9]*; do\n\t\t[ -x \"$LDELFN\" ] && break\n\tdone\n\tif ! [ -x \"$LDELF\" ] || [ -x \"$LDELFN\" ]; then\n\t    if [ -z \"$PREFIX\" ] || [ \"$PREFIX\" = \"/\" ]; then\n\t\t\techo \"Forcing a static build for $OS and \\$PREFIX of /\"\n\t\t\tSTATIC=yes\n\t\t\tALLOW_USR_LIBS=true\n\t\tfi\n\tfi\n\t;;\nesac\nif [ \"$STATIC\" = yes ]; then\n\techo \"LDFLAGS+=\t-static\" >>$CONFIG_MK\nfi\n\nif [ -z \"$DEBUG\" ] && [ -d .git ]; then\n\tprintf \"Found git checkout ... \"\n\tDEBUG=yes\nfi\nif [ -n \"$DEBUG\" ] && [ \"$DEBUG\" != no ] && [ \"$DEBUG\" != false ]; then\n\techo \"Adding debugging CFLAGS\"\n\n\tcat <<EOF >>$CONFIG_MK\nCFLAGS+=\t-g -Wall -Wextra -Wundef\nCFLAGS+=\t-Wmissing-prototypes -Wmissing-declarations\nCFLAGS+=\t-Wmissing-format-attribute -Wnested-externs\nCFLAGS+=\t-Winline -Wcast-align -Wcast-qual -Wpointer-arith\nCFLAGS+=\t-Wreturn-type -Wswitch -Wshadow\nCFLAGS+=\t-Wcast-qual -Wwrite-strings\nCFLAGS+=\t-Wformat=2\nCFLAGS+=\t-Wpointer-sign -Wmissing-noreturn\nEOF\n\n\tcase \"$OS\" in\n\tmirbsd*|openbsd*);; # OpenBSD has many redundant decs in system headers\n\tbitrig*|solaris*|sunos*)\n\t\t\techo \"CFLAGS+=\t-Wredundant-decls\" >>$CONFIG_MK\n\t\t\t;; # Bitrig spouts many conversion errors with htons\n\t\t\t   # sunos has many as well\n\t*)\t\techo \"CFLAGS+=\t-Wredundant-decls\" >>$CONFIG_MK\n\t\t\techo \"CFLAGS+=\t-Wconversion\" >>$CONFIG_MK\n\t\t\t;;\n\tesac\n\n\tcase \"$OS\" in\n\tsolaris*|sunos*);;\n\t*)\t\techo \"CFLAGS+=\t-Wstrict-overflow\" >>$CONFIG_MK;;\n\tesac\n\n\t# Turn on extra per compiler debugging\n\tcase \"$CC\" in\n\t*gcc*)\t\techo \"CFLAGS+=\t-Wlogical-op\" >>$CONFIG_MK;;\n\tesac\n\n\tif [ \"$SANITIZEADDRESS\" = yes ]; then\n\t\tprintf \"Testing compiler supports address sanitisation ...\"\n\tcat <<EOF >_test.c\nint main(void) {\n\treturn 0;\n}\nEOF\n\t\tif $CC -fsanitize=address _test.c -o _test 2>&3; then\n\t\t\techo \"yes\"\n\t\t\techo \"CPPFLAGS+=\t-DASAN\" >>$CONFIG_MK\n\t\t\techo \"CFLAGS+=\t-fsanitize=address\" >>$CONFIG_MK\n\t\t\techo \"CFLAGS+=\t-fno-omit-frame-pointer\" >>$CONFIG_MK\n\t\t\techo \"LDFLAGS+=\t-fsanitize=address\" >>$CONFIG_MK\n\t\telse\n\t\t\techo \"no\"\n\t\tfi\n\t\trm -rf _test.c _test\n\tfi\nelse\n\techo \"CPPFLAGS+=\t-DNDEBUG\" >>$CONFIG_MK\nfi\n\nif [ -n \"$FORK\" ] && [ \"$FORK\" != yes ] && [ \"$FORK\" != true ]; then\n\techo \"There is no fork\"\n\techo \"CPPFLAGS+=\t-DTHERE_IS_NO_FORK\" >>$CONFIG_MK\nfi\n\nif [ \"$SMALL\" = yes ]; then\n\techo \"Building with -DSMALL\"\n\techo \"CPPFLAGS+=\t-DSMALL\" >>$CONFIG_MK\n\tDHCPCD_DEFS=dhcpcd-definitions-small.conf\n\techo \"DHCPCD_DEFS=\t$DHCPCD_DEFS\" >>$CONFIG_MK\nfi\n\nBPF_OS=\ncase \"$OS\" in\nfreebsd*|kfreebsd*)\n\t# FreeBSD hide some newer POSIX APIs behind _GNU_SOURCE ...\n\techo \"CPPFLAGS+=\t-D_GNU_SOURCE\" >>$CONFIG_MK\n\tcase \"$OS\" in\n\tkfreebsd*)\techo \"CPPFLAGS+=\t-DBSD\" >>$CONFIG_MK;;\n\tesac\n\techo \"DHCPCD_SRCS+=\tif-bsd.c\" >>$CONFIG_MK\n\t# Whacky includes needed to buck the trend\n\tcase \"$OS\" in\n\tkfreebsd*)\techo \"#include\t\t<inttypes.h>\" >>$CONFIG_H;\n\tesac\n\techo \"#include\t\t\t<net/if.h>\" >>$CONFIG_H\n\techo \"#include\t\t\t<net/if_var.h>\" >>$CONFIG_H\n\t;;\nnetbsd*)\n\t# reallocarray(3) is guarded by _OPENBSD_SOURCE\n\techo \"CPPFLAGS+=\t-D_OPENBSD_SOURCE\" >>$CONFIG_MK\n\techo \"DHCPCD_SRCS+=\tif-bsd.c\" >>$CONFIG_MK\n\t;;\nlinux*)\n\techo \"CPPFLAGS+=\t-D_GNU_SOURCE\" >>$CONFIG_MK\n\t# Large File Support, should be fine for 32-bit systems.\n\t# But if this is the case, why is it not set by default?\n\techo \"CPPFLAGS+=\t-D_FILE_OFFSET_BITS=64\" >>$CONFIG_MK\n\techo \"CPPFLAGS+=\t-D_LARGEFILE_SOURCE\" >>$CONFIG_MK\n\techo \"CPPFLAGS+=\t-D_LARGEFILE64_SOURCE\" >>$CONFIG_MK\n\techo \"DHCPCD_SRCS+=\tif-linux.c\" >>$CONFIG_MK\n\t# for RTM_NEWADDR and friends\n\techo \"#include\t\t<asm/types.h> /* fix broken headers */\" >>$CONFIG_H\n\techo \"#include\t\t<sys/socket.h> /* fix broken headers */\" >>$CONFIG_H\n\techo \"#include\t\t<linux/rtnetlink.h>\" >>$CONFIG_H\n\tBPF_OS=\"bpf-linux.c\"\n\t# cksum does't support -a and netpgp is rare\n\techo \"CKSUM=\t\tsha256sum --tag\" >>$CONFIG_MK\n\techo \"PGP=\t\tgpg2\" >>$CONFIG_MK\n\t;;\nqnx*)\n\techo \"CPPFLAGS+=\t-D__EXT\" >>$CONFIG_MK\n\techo \"DHCPCD_SRCS+=\tif-bsd.c\" >>$CONFIG_MK\n\t;;\nsolaris*|sunos*)\n\techo \"CPPFLAGS+=\t-D_XPG4_2 -D__EXTENSIONS__ -DBSD_COMP\" \\\n\t    >>$CONFIG_MK\n\techo \"DHCPCD_SRCS+=\tif-sun.c\" >>$CONFIG_MK\n\techo \"LDADD+=\t\t-ldlpi -lkstat\" >>$CONFIG_MK\n\tBPF_OS=\"bpf-bsd.c bpf-dlpi.c\"\n\t;;\n*)\n\techo \"DHCPCD_SRCS+=\tif-bsd.c\" >>$CONFIG_MK\n\t;;\nesac\n\nif [ -z \"$BPF_OS\" ]; then\n\tBPF_OS=\"bpf-bsd.c\"\nfi\nif [ \"$LIBPCAP\" = yes ]; then\n\tBPF_OS=\"bpf-pcap.c\"\nfi\n\nif [ -n \"${_DEFAULT_HOSTNAME+x}\" ]; then\n\tDEFAULT_HOSTNAME=\"${_DEFAULT_HOSTNAME}\"\nelse\n\tcase \"$OS\" in\n\tlinux*)\tDEFAULT_HOSTNAME=\"(none)\";;\n\t*)\tDEFAULT_HOSTNAME=\"\";;\n\tesac\nfi\necho \"DEFAULT_HOSTNAME=\t\t$DEFAULT_HOSTNAME\" >>$CONFIG_MK\n\nif [ -z \"$INET\" ] || [ \"$INET\" = yes ]; then\n\techo \"Enabling INET support\"\n\techo \"CPPFLAGS+=\t-DINET\" >>$CONFIG_MK\n\techo \"DHCPCD_SRCS+=\tdhcp.c ipv4.c bpf.c $BPF_OS\" >>$CONFIG_MK\n\tif [ -z \"$ARP\" ] || [ \"$ARP\" = yes ]; then\n\t\techo \"Enabling ARP support\"\n\t\techo \"CPPFLAGS+=\t-DARP\" >>$CONFIG_MK\n\t\techo \"DHCPCD_SRCS+=\tarp.c\" >>$CONFIG_MK\n\tfi\n\tif [ -z \"$ARPING\" ] || [ \"$ARPING\" = yes ]; then\n\t\techo \"Enabling ARPing support\"\n\t\techo \"CPPFLAGS+=\t-DARPING\" >>$CONFIG_MK\n\tfi\n\tif [ -z \"$IPV4LL\" ] || [ \"$IPV4LL\" = yes ]; then\n\t\techo \"Enabling IPv4LL support\"\n\t\techo \"CPPFLAGS+=\t-DIPV4LL\" >>$CONFIG_MK\n\t\techo \"DHCPCD_SRCS+=\tipv4ll.c\" >>$CONFIG_MK\n\tfi\nfi\nif [ -z \"$INET6\" ] || [ \"$INET6\" = yes ]; then\n\techo \"Enabling INET6 support\"\n\techo \"CPPFLAGS+=\t-DINET6\" >>$CONFIG_MK\n\techo \"DHCPCD_SRCS+=\tipv6.c ipv6nd.c\" >>$CONFIG_MK\n\tif [ -z \"$DHCP6\" ] || [ \"$DHCP6\" = yes ]; then\n\t\techo \"Enabling DHCPv6 support\"\n\t\techo \"CPPFLAGS+=\t-DDHCP6\" >>$CONFIG_MK\n\t\techo \"DHCPCD_SRCS+=\tdhcp6.c\" >>$CONFIG_MK\n\tfi\nfi\nif [ -z \"$AUTH\" ] || [ \"$AUTH\" = yes ]; then\n\techo \"Enabling Authentication\"\n\techo \"CPPFLAGS+=\t-DAUTH\" >>$CONFIG_MK\n\techo \"SRCS+=\t\tauth.c\" >>$CONFIG_MK\nfi\n\nif [ -z \"$PRIVSEP\" ]; then\n\t# privilege separation works fine .... except on Solaris\n\tcase \"$OS\" in\n\tsolaris*|sunos*)\tPRIVSEP=no;;\n\t*)\t\t\tPRIVSEP=yes;;\n\tesac\nfi\n\nif [ \"$PRIVSEP\" = yes ]; then\n\techo \"Enabling Privilege Separation\"\n\n\t# Try and work out system user\n\tif [ -z \"$PRIVSEP_USER\" ]; then\n\t\tprintf \"Detecting a suitable user for dhcpcd ... \"\n\t\tfor x in _dhcpcd _dhcp dhcpcd; do\n\t\t\thome=$(getent passwd $x 2>/dev/null | cut -d: -f6)\n\t\t\tif [ -d \"$home\" ]; then\n\t\t\t\tPRIVSEP_USER=\"$x\"\n\t\t\t\tbreak\n\t\t\tfi\n\t\tdone\n\tfi\n\tif [ -n \"$PRIVSEP_USER\" ]; then\n\t\techo \"$PRIVSEP_USER\"\n\telse\n\t\tPRIVSEP_USER=dhcpcd\n\t\techo\n\t\techo \"No suitable user found for Priviledge Separation!\"\n\tfi\n\n\techo \"CPPFLAGS+=\t-DPRIVSEP\" >>$CONFIG_MK\n\techo \"PRIVSEP_USER?=\t$PRIVSEP_USER\" >>$CONFIG_MK\n\techo \"#ifndef PRIVSEP_USER\" >>$CONFIG_H\n\techo \"#define PRIVSEP_USER\t\t \\\"$PRIVSEP_USER\\\"\" >>$CONFIG_H\n\techo \"#endif\" >>$CONFIG_H\n\techo \"PRIVSEP_SRCS=\tprivsep.c privsep-root.c\" >>$CONFIG_MK\n\techo \"PRIVSEP_SRCS+=\tprivsep-control.c privsep-inet.c\" >>$CONFIG_MK\n\tif [ -z \"$INET\" ] || [ \"$INET\" = yes ]; then\n\t\techo \"PRIVSEP_SRCS+=\tprivsep-bpf.c\" >>$CONFIG_MK\n\tfi\n\tcase \"$OS\" in\n\tlinux*)\n\t\t\t echo \"PRIVSEP_SRCS+=\tprivsep-linux.c\" >>$CONFIG_MK\n\t\t\t if [ -n \"$SECCOMP\" ] && [ \"$SECCOMP\" = no ]; then\n\t\t\t\t echo \"#define\tDISABLE_SECCOMP\" >>$CONFIG_H\n\t\t\t fi\n\t\t\t ;;\n\tsolaris*|sunos*) echo \"PRIVSEP_SRCS+=\tprivsep-sun.c\" >>$CONFIG_MK;;\n\t*)\t\t echo \"PRIVSEP_SRCS+=\tprivsep-bsd.c\" >>$CONFIG_MK;;\n\tesac\nelse\n\techo \"PRIVSEP_SRCS=\" >>$CONFIG_MK\nfi\n\necho \"Using compiler .. $CC\"\n# Add CPPFLAGS and CFLAGS to CC for testing features\nXCC=\"$CC `$SED -n -e 's/CPPFLAGS+=*\\(.*\\)/\\1/p' $CONFIG_MK`\"\nXCC=\"$XCC `$SED -n -e 's/CFLAGS+=*\\(.*\\)/\\1/p' $CONFIG_MK`\"\n\n# When running tests, treat all warnings as errors.\n# This avoids the situation where we link to a libc symbol\n# without the correct header because it might be hidden behind\n# a _*_SOURCE #define guard.\nXCC=\"$XCC -Wall -Werror\"\n\n# Now test we can use the compiler with our CFLAGS\ncat <<EOF >_test.c\nint main(void) {\n\treturn 0;\n}\nEOF\n_CC=false\nif $XCC _test.c -o _test >/dev/null 2>&3; then\n\t[ -x _test ] && _CC=true\nfi\nrm -f _test.c _test\nif ! $_CC; then\n\techo $XCC\n\techo \"$CC does not create executables\" >&2\n\texit 1\nfi\n[ \"$CC\" != cc ] && echo \"CC=\t\t$CC\" >>$CONFIG_MK\n$CC --version | $SED -e '1!d'\n\nif [ \"$PRIVSEP\" = yes ]; then\n\tprintf \"Testing for capsicum ... \"\n\tcat <<EOF >_capsicum.c\n#include <sys/capsicum.h>\nint main(void) {\n\treturn cap_enter();\n}\nEOF\n\tif $XCC _capsicum.c -o _capsicum 2>&3; then\n\t\techo \"yes\"\n\t\techo \"#define\tHAVE_CAPSICUM\" >>$CONFIG_H\n\telse\n\t\techo \"no\"\n\tfi\n\trm -f _capsicum.c _capsicum\n\n\tprintf \"Testing for pledge ... \"\n\tcat <<EOF >_pledge.c\n#include <unistd.h>\nint main(void) {\n\treturn pledge(\"stdio\", NULL);\n}\nEOF\n\tif $XCC _pledge.c -o _pledge 2>&3; then\n\t\techo \"yes\"\n\t\techo \"#define\tHAVE_PLEDGE\" >>$CONFIG_H\n\telse\n\t\techo \"no\"\n\tfi\n\trm -f _pledge.c _pledge\nfi\n\n# This block needs to be after the compiler test due to embedded quotes.\nif [ -z \"$EMBEDDED\" ] || [ \"$EMBEDDED\" = yes ]; then\n\techo \"$DHCPCD_DEFS will be embedded in dhcpcd itself\"\n\techo \"DHCPCD_SRCS+=\tdhcpcd-embedded.c\" >>$CONFIG_MK\nelse\n\techo \"$DHCPCD_DEFS will be installed to $LIBEXECDIR\"\n\techo \"CPPFLAGS+=\t-DEMBEDDED_CONFIG=\\\\\\\"$LIBEXECDIR/dhcpcd-definitions.conf\\\\\\\"\" >>$CONFIG_MK\n\techo \"EMBEDDEDINSTALL=\t_embeddedinstall\" >>$CONFIG_MK\nfi\n\nif [ \"$OS\" = linux ]; then\n\tprintf \"Testing for nl80211 ... \"\n\tcat <<EOF >_nl80211.c\n#include <linux/nl80211.h>\nint main(void) {\n\treturn 0;\n}\nEOF\n\tif $XCC _nl80211.c -o _nl80211 2>&3; then\n\t\techo \"yes\"\n\t\techo \"#define\tHAVE_NL80211_H\" >>$CONFIG_H\n\telse\n\t\techo \"no\"\n\t\techo \"DHCPCD_SRCS+=\tif-linux-wext.c\" >>$CONFIG_MK\n\tfi\n\trm -f _nl80211.c _nl80211\n\n\tprintf \"Testing for IN6_ADDR_GEN_MODE_NONE ... \"\n\tcat <<EOF >_IN6_ADDR_GEN_MODE_NONE.c\n#include <linux/if_link.h>\nint main(void) {\n\tint x = IN6_ADDR_GEN_MODE_NONE;\n\treturn x;\n}\nEOF\n\tif $XCC _IN6_ADDR_GEN_MODE_NONE.c -o _IN6_ADDR_GEN_MODE_NONE 2>&3; then\n\t\techo \"yes\"\n\t\techo \"#define\tHAVE_IN6_ADDR_GEN_MODE_NONE\" >>$CONFIG_H\n\telse\n\t\techo \"no\"\n\tfi\n\trm -f _IN6_ADDR_GEN_MODE_NONE.c _IN6_ADDR_GEN_MODE_NONE\nelse\n\tprintf \"Testing for ifam_pid ... \"\n\tcat <<EOF >_ifam_pid.c\n#include <net/if.h>\nint main(void) {\n\tstruct ifa_msghdr ifam = { };\n\treturn (int)ifam.ifam_pid;\n}\nEOF\n\tif $XCC _ifam_pid.c -o _ifam_pid 2>&3; then\n\t\techo \"yes\"\n\t\techo \"#define\tHAVE_IFAM_PID\" >>$CONFIG_H\n\telse\n\t\techo \"no\"\n\tfi\n\trm -f _ifam_pid.c _ifam_pid\n\n\tprintf \"Testing for ifam_addrflags ... \"\n\tcat <<EOF >_ifam_addrflags.c\n#include <net/if.h>\nint main(void) {\n\tstruct ifa_msghdr ifam = { };\n\treturn (int)ifam.ifam_addrflags;\n}\nEOF\n\tif $XCC _ifam_addrflags.c -o _ifam_addrflags 2>&3; then\n\t\techo \"yes\"\n\t\techo \"#define\tHAVE_IFAM_ADDRFLAGS\" >>$CONFIG_H\n\telse\n\t\techo \"no\"\n\tfi\n\trm -f _ifam_addrflags.c _ifam_addrflags\nfi\n\nabort=false\n# We require the libc to support non standard functions, like getifaddrs\nprintf \"Testing for getifaddrs ... \"\ncat <<EOF >_getifaddrs.c\n#include <sys/types.h>\n#include <ifaddrs.h>\nint main(void) {\n\tstruct ifaddrs *ifap;\n\treturn getifaddrs(&ifap);\n}\nEOF\nLIBSOCKET=\nif $XCC _getifaddrs.c -o _getifaddrs 2>&3; then\n\techo \"yes\"\nelif $XCC _getifaddrs.c -o _getifaddrs -lsocket 2>&3; then\n\tLIBSOCKET=-lsocket\n\techo \"yes (-lsocket)\"\n\techo \"LDADD+=\t\t-lsocket\" >>$CONFIG_MK\nelse\n\techo \"no\"\n\techo \"libc support for getifaddrs is required - aborting\" >&2\n\tabort=true\nfi\nrm -f _getifaddrs.c _getifaddrs\n$abort && exit 1\n\nprintf \"Testing for ifaddrs.ifa_addrflags ... \"\ncat <<EOF >_getifaddrs_addrflags.c\n#include <sys/types.h>\n#include <ifaddrs.h>\nint main(void) {\n\tstruct ifaddrs *ifap;\n\tgetifaddrs(&ifap);\n\treturn (int)ifap->ifa_addrflags;\n}\nEOF\nif $XCC _getifaddrs_addrflags.c -o _getifaddrs_addrflags $LIBSOCKET 2>&3; then\n\techo \"yes\"\n\techo \"#define\tHAVE_IFADDRS_ADDRFLAGS\" >>$CONFIG_H\nelse\n\techo \"no\"\nfi\nrm -f _getifaddrs_addrflags.c _getifaddrs_addrflags\n\nprintf \"Testing for clock_gettime ... \"\ncat <<EOF >_clock_gettime.c\n#include <time.h>\nint main(void) {\n\tstruct timespec ts;\n\treturn clock_gettime(CLOCK_MONOTONIC, &ts);\n}\nEOF\nif $XCC _clock_gettime.c -o _clock_gettime 2>&3; then\n\techo \"yes\"\nelif $XCC _clock_gettime.c -lrt -o _clock_gettime 2>&3; then\n\techo \"yes (-lrt)\"\n\techo \"LDADD+=\t\t-lrt\" >>$CONFIG_MK\nelse\n\techo \"no\"\n\techo \"libc support for clock_getttime is required - aborting\" >&2\n\tabort=true\nfi\nrm -f _clock_gettime.c _clock_gettime\n$abort && exit 1\n\nif [ -z \"$CLOSEFROM\" ]; then\n\tprintf \"Testing for closefrom ... \"\n\tcat <<EOF >_closefrom.c\n#include <stdlib.h>\n#include <unistd.h>\nint main(void) {\n\tclosefrom(3);\n\treturn 0;\n}\nEOF\n\tif $XCC _closefrom.c -o _closefrom 2>&3; then\n\t\tCLOSEFROM=yes\n\telse\n\t\tCLOSEFROM=no\n\tfi\n\techo \"$CLOSEFROM\"\nfi\nrm -f _closefrom.c _closefrom\nif [ \"$CLOSEFROM\" = no ]; then\n\techo \"COMPAT_SRCS+=\tcompat/closefrom.c\" >>$CONFIG_MK\n\techo \"#include\t\t\t\\\"compat/closefrom.h\\\"\" >>$CONFIG_H\nfi\n\nprintf \"Testing ioctl request type ... \"\ncat <<EOF >_ioctl.c\n#include <sys/ioctl.h>\nint main(void) {\n\tunsigned long req = 0;\n\treturn ioctl(3, req, &req);\n}\nEOF\nif $XCC _ioctl.c -o _ioctl 2>&3; then\n\tIOCTL_REQ=\"unsigned long\"\nelse\n\tIOCTL_REQ=\"int\"\nfi\necho \"$IOCTL_REQ\"\n# Our default is unsigned long\n# We can still define it, but it makes the code path slightly bigger\nif [ \"$IOCTL_REQ\" != \"unsigned long\" ]; then\n\techo \"#define\tIOCTL_REQUEST_TYPE\t$IOCTL_REQ\" >>$CONFIG_H\nfi\nrm -f _ioctl.c _ioctl\n\nprintf \"Testing for inet_ntoa ... \"\ncat <<EOF >_inet_ntoa.c\n#include <netinet/in.h>\n#include <arpa/inet.h>\nint main(void) {\n\tstruct in_addr in = { .s_addr = 0 };\n\tinet_ntoa(in);\n\treturn 0;\n}\nEOF\nif $XCC _inet_ntoa.c -o _inet_ntoa 2>&3; then\n\techo \"yes\"\nelif $XCC _inet_ntoa.c -lnsl -o _inet_ntoa 2>&3; then\n\techo \"yes (-lnsl)\"\n\techo \"LDADD+=\t\t-lnsl\" >>$CONFIG_MK\nelif $XCC _inet_ntoa.c -lsocket -o _inet_ntoa 2>&3; then\n\techo \"yes (-lsocket)\"\n\techo \"LDADD+=\t\t-lsocket\" >>$CONFIG_MK\nelse\n\techo \"no\"\n\techo \"libc support for inet_ntoa is required - aborting\" >&2\n\tabort=true\nfi\nrm -f _inet_ntoa.c _inet_ntoa\n$abort && exit 1\n\nif [ -z \"$ARC4RANDOM\" ]; then\n\tprintf \"Testing for arc4random ... \"\n\tcat <<EOF >_arc4random.c\n#include <stdlib.h>\nint main(void) {\n\treturn (int)arc4random();\n}\nEOF\n\tif $XCC _arc4random.c -o _arc4random 2>&3; then\n\t\tARC4RANDOM=yes\n\telse\n\t\tARC4RANDOM=no\n\tfi\n\techo \"$ARC4RANDOM\"\n\trm -f _arc4random.c _arc4random\nfi\nif [ \"$ARC4RANDOM\" = no ]; then\n\techo \"COMPAT_SRCS+=\tcompat/arc4random.c\" >>$CONFIG_MK\n\techo \"#include\t\t\t\\\"compat/arc4random.h\\\"\" >>$CONFIG_H\nfi\n\nif [ -z \"$ARC4RANDOM_UNIFORM\" ]; then\n\tprintf \"Testing for arc4random_uniform ... \"\n\tcat <<EOF >_arc4random_uniform.c\n#include <stdlib.h>\nint main(void) {\n\treturn (int)arc4random_uniform(100);\n}\nEOF\n\tif $XCC _arc4random_uniform.c -o _arc4random_uniform 2>&3; then\n\t\tARC4RANDOM_UNIFORM=yes\n\telse\n\t\tARC4RANDOM_UNIFORM=no\n\tfi\n\techo \"$ARC4RANDOM_UNIFORM\"\n\trm -f _arc4random_uniform.c _arc4random_uniform\nfi\nif [ \"$ARC4RANDOM_UNIFORM\" = no ]; then\n\techo \"COMPAT_SRCS+=\tcompat/arc4random_uniform.c\" >>$CONFIG_MK\n\techo \"#include\t\t\t\\\"compat/arc4random_uniform.h\\\"\" >>$CONFIG_H\nfi\n\n# Our arc4random compat needs memset_explicit, explicit_bzero or memset_s\nif [ -z \"$MEMSET_EXPLICIT\" ]; then\n\tprintf \"Testing for memset_explicit ... \"\n\tcat <<EOF >_memset_explicit.c\n#include <string.h>\nint main(void) {\n\tint a;\n\t(void)memset_explicit(&a, 0, sizeof(a));\n\treturn 0;\n}\nEOF\n\tif $XCC _memset_explicit.c -o _memset_explicit 2>&3; then\n\t\tMEMSET_EXPLICIT=yes\n\telse\n\t\tMEMSET_EXPLICIT=no\n\tfi\n\techo \"$MEMSET_EXPLICIT\"\n\trm -f _memset_explicit.c _memset_explicit\nfi\nif [ \"$MEMSET_EXPLICIT\" = yes ]; then\n\techo \"#define\tHAVE_MEMSET_EXPLICIT\" >>$CONFIG_H\nfi\n\nif [ -z \"$EXPLICIT_BZERO\" ]; then\n\tprintf \"Testing for explicit_bzero ... \"\n\tcat <<EOF >_explicit_bzero.c\n#define _BSD_SOURCE // musl, will be added for Linux in config.h\n#include <string.h>\nint main(void) {\n\tint a;\n\texplicit_bzero(&a, sizeof(a));\n\treturn 0;\n}\nEOF\n\tif $XCC _explicit_bzero.c -o _explicit_bzero 2>&3; then\n\t\tEXPLICIT_BZERO=yes\n\telse\n\t\tEXPLICIT_BZERO=no\n\tfi\n\techo \"$EXPLICIT_BZERO\"\n\trm -f _explicit_bzero.c _explicit_bzero\nfi\nif [ \"$EXPLICIT_BZERO\" = yes ]; then\n\techo \"#define\tHAVE_EXPLICIT_BZERO\" >>$CONFIG_H\nfi\n\nif [ -z \"$MEMSET_S\" ]; then\n\tprintf \"Testing for memset_s ... \"\n\tcat <<EOF >_memset_s.c\n#define\t__STDC_WANT_LIB_EXT1__ 1\n#include <string.h>\nint main(void) {\n\tint a;\n\tmemset_s(&a, sizeof(a), 0, sizeof(a));\n\treturn 0;\n}\nEOF\n\tif $XCC _memset_s.c -o _memset_s 2>&3; then\n\t\tMEMSET_S=yes\n\telse\n\t\tMEMSET_S=no\n\tfi\n\techo \"$MEMSET_S\"\n\trm -f _memset_s.c _memset_s\nfi\nif [ \"$MEMSET_S\" = yes ]; then\n\techo \"#define\t__STDC_WANT_LIB_EXT1__\t1\" >>$CONFIG_H\n\techo \"#define\tHAVE_MEMSET_S\" >>$CONFIG_H\nfi\n\nif [ -z \"$OPEN_MEMSTREAM\" ]; then\n\tprintf \"Testing for open_memstream ... \"\n\tcat <<EOF >_open_memstream.c\n#include <stdio.h>\nint main(void) {\n\treturn open_memstream(NULL, NULL) != NULL ? 0 : 1;\n}\nEOF\n\tif $XCC _open_memstream.c -o _open_memstream 2>&3; then\n\t\tOPEN_MEMSTREAM=yes\n\telse\n\t\tOPEN_MEMSTREAM=no\n\tfi\n\techo \"$OPEN_MEMSTREAM\"\n\trm -f _open_memstream.c _open_memstream\nfi\nif [ \"$OPEN_MEMSTREAM\" = yes ]; then\n\techo \"#define\tHAVE_OPEN_MEMSTREAM\" >>$CONFIG_H\nelif [ \"$PRIVSEP\" = yes ]; then\n\techo \"WARNING: Ensure that /tmp exists in the privsep users chroot\"\nfi\n\nif [ -z \"$PIDFILE_LOCK\" ]; then\n\tprintf \"Testing for pidfile_lock ... \"\n\tcat <<EOF >_pidfile.c\n#include <stdlib.h>\n#include <util.h>\nint main(void) {\n\treturn (int)pidfile_lock(NULL);\n}\nEOF\n\t# We only want to link to libutil if it exists in /lib\n\tif $ALLOW_USR_LIBS; then\n\t\tset -- /\n\telse\n\t\tset -- $(ls /lib/libutil.so.* 2>/dev/null)\n\tfi\n\tif $XCC _pidfile.c -o _pidfile 2>&3; then\n\t\tPIDFILE_LOCK=yes\n\telif [ -e \"$1\" ] &&  $XCC _pidfile.c -o _pidfile -lutil 2>&3; then\n\t\tPIDFILE_LOCK=\"yes (-lutil)\"\n\t\tLIBUTIL=\"-lutil\"\n\telse\n\t\tPIDFILE_LOCK=no\n\tfi\n\techo \"$PIDFILE_LOCK\"\n\trm -f _pidfile.c _pidfile\nfi\nif [ \"$PIDFILE_LOCK\" = no ]; then\n\techo \"COMPAT_SRCS+=\tcompat/pidfile.c\" >>$CONFIG_MK\n\techo \"#include\t\t\t\\\"compat/pidfile.h\\\"\" >>$CONFIG_H\nelse\n\techo \"#define\tHAVE_UTIL_H\" >>$CONFIG_H\n\tif [ -n \"$LIBUTIL\" ]; then\n\t\techo \"LDADD+=\t\t$LIBUTIL\" >>$CONFIG_MK\n\tfi\nfi\n\nif [ -z \"$SETPROCTITLE\" ]; then\n\tprintf \"Testing for setproctitle ... \"\n\tcat << EOF >_setproctitle.c\n#include <stdlib.h>\n#include <unistd.h>\nint main(void) {\n\tsetproctitle(\"foo\");\n\treturn 0;\n}\nEOF\n\tif $XCC _setproctitle.c -o _setproctitle 2>&3; then\n\t\tSETPROCTITLE=yes\n\telse\n\t\tSETPROCTITLE=no\n\tfi\n\techo \"$SETPROCTITLE\"\n\trm -f _setproctitle.c _setproctitle\nfi\nif [ \"$SETPROCTITLE\" = no ]; then\n\tcase \"$OS\" in\n\tsolaris*|sunos*)\n\t\techo \"$OS has no support for setting process title\"\n\t\techo \"#define\tsetproctitle(...)\" >>$CONFIG_H\n\t\t;;\n\t*)\n\t\techo \"COMPAT_SRCS+=\tcompat/setproctitle.c\" >>$CONFIG_MK\n\t\techo \"#include\t\t\t\\\"compat/setproctitle.h\\\"\" \\\n\t\t\t>>$CONFIG_H\n\t\t;;\n\tesac\nfi\n\nif [ -z \"$STRLCPY\" ]; then\n\tprintf \"Testing for strlcpy ... \"\n\tcat <<EOF >_strlcpy.c\n#include <string.h>\nint main(void) {\n\tconst char s1[] = \"foo\";\n\tchar s2[10];\n\treturn (int)strlcpy(s2, s1, sizeof(s2));\n}\nEOF\n\tif $XCC _strlcpy.c -o _strlcpy 2>&3; then\n\t\tSTRLCPY=yes\n\telse\n\t\tSTRLCPY=no\n\tfi\n\techo \"$STRLCPY\"\n\trm -f _strlcpy.c _strlcpy\nfi\nif [ \"$STRLCPY\" = no ]; then\n\techo \"COMPAT_SRCS+=\tcompat/strlcpy.c\" >>$CONFIG_MK\n\techo \"#include\t\t\t\\\"compat/strlcpy.h\\\"\" >>$CONFIG_H\nfi\n\nif [ -z \"$STRTOI\" ]; then\n\tprintf \"Testing for strtoi ... \"\n\tcat <<EOF >_strtoi.c\n#include <stdlib.h>\n#include <limits.h>\n#include <inttypes.h>\nint main(void) {\n\tint e;\n\treturn (int)strtoi(\"1234\", NULL, 0, 0, INT32_MAX, &e);\n}\nEOF\n\tif $XCC _strtoi.c -o _strtoi 2>&3; then\n\t\tSTRTOI=yes\n\telse\n\t\tSTRTOI=no\n\tfi\n\techo \"$STRTOI\"\n\trm -f _strtoi.c _strtoi\nfi\nif [ \"$STRTOI\" = no ]; then\n\techo \"COMPAT_SRCS+=\tcompat/strtoi.c compat/strtou.c\" >>$CONFIG_MK\n\techo \"#include\t\t\t\\\"compat/strtoi.h\\\"\" >>$CONFIG_H\nfi\n\nif [ -z \"$CONSTTIME_MEMEQUAL\" ]; then\n\tprintf \"Testing for consttime_memequal ... \"\n\tcat <<EOF >_consttime_memequal.c\n#include <string.h>\nint main(void) {\n\treturn consttime_memequal(\"deadbeef\", \"deadbeef\", 8);\n}\nEOF\n\tif $XCC _consttime_memequal.c -o _consttime_memequal 2>&3; then\n\t\tCONSTTIME_MEMEQUAL=yes\n\telse\n\t\tCONSTTIME_MEMEQUAL=no\n\tfi\n\techo \"$CONSTTIME_MEMEQUAL\"\n\trm -f _consttime_memequal.c _consttime_memequal\nfi\nif [ \"$CONSTTIME_MEMEQUAL\" = no ] && [ -z \"$TIMINGSAFE_BCMP\" ]; then\n\tprintf \"Testing for timingsafe_bcmp ... \"\n\tcat <<EOF >_timingsafe_bcmp.c\n#include <string.h>\nint main(void) {\n\treturn timingsafe_bcmp(\"deadbeef\", \"deadbeef\", 8);\n}\nEOF\n\tif $XCC _timingsafe_bcmp.c -o _timingsafe_bcmp 2>&3; then\n\t\tTIMINGSAFE_BCMP=yes\n\telse\n\t\tTIMINGSAFE_BCMP=no\n\tfi\n\techo \"$TIMINGSAFE_BCMP\"\n\trm -f _timingsafe_bcmp.c _timingsafe_bcmp\nfi\n\nif [ \"$CONSTTIME_MEMEQUAL\" = no ]; then\n\tif [ \"$TIMINGSAFE_BCMP\" = yes ]; then\n\t\techo \"#define\tconsttime_memequal\t!timingsafe_bcmp\" \\\n\t\t    >>$CONFIG_H\n\telse\n\t\techo \"#include\t\t\t\\\"compat/consttime_memequal.h\\\"\" \\\n\t\t    >>$CONFIG_H\n\tfi\nfi\n\nif [ -z \"$DPRINTF\" ]; then\n\tprintf \"Testing for dprintf ... \"\n\tcat <<EOF >_dprintf.c\n#include <stdio.h>\nint main(void) {\n\treturn dprintf(0, \"%d\", 0);\n}\nEOF\n\tif $XCC _dprintf.c -o _dprintf 2>&3; then\n\t\tDPRINTF=yes\n\telse\n\t\tDPRINTF=no\n\tfi\n\techo \"$DPRINTF\"\n\trm -f _dprintf.c _dprintf\nfi\nif [ \"$DPRINTF\" = no ]; then\n\techo \"COMPAT_SRCS+=\tcompat/dprintf.c\" >>$CONFIG_MK\n\techo \"#include\t\t\t\\\"compat/dprintf.h\\\"\" >>$CONFIG_H\nfi\n\nif [ -z \"$RBTREE\" ]; then\n\tprintf \"Testing for rb_tree_init ... \"\n\tcat <<EOF >_rbtree.c\n#include <sys/rbtree.h>\nstatic rb_tree_ops_t ops;\nint main(void) {\n\trb_tree_t tree;\n\trb_tree_init(&tree, &ops);\n\treturn 0;\n}\nEOF\n\tif $XCC _rbtree.c -o _rbtree 2>&3; then\n\t\tRBTREE=yes\n\telse\n\t\tRBTREE=no\n\tfi\n\techo \"$RBTREE\"\n\trm -f _rbtree.c _rbtree\nfi\nif [ \"$RBTREE\" = no ]; then\n\techo \"VENDOR_SRCS+=\tvendor/rbtree.c\" >>$CONFIG_MK\n\t# Tell rbtree.c to #include \"rbtree.h\" rather than sys/rbtree.h\n\techo \"RBTREE_CPPFLAGS+=\t-DRBLOCAL\" >>$CONFIG_MK\n\t# Remove unused warning from compile\n\techo \"RBTREE_CPPFLAGS+=\t\\\"-D__unused=__attribute__((__unused__))\\\"\" >>$CONFIG_MK\n\t# Enjoy the same optimisation as NetBSD's libc\n\techo \"RBTREE_CPPFLAGS+=\t\\\"-D__predict_false(exp)=__builtin_expect((exp) ? 1 : 0, 0)\\\"\" >>$CONFIG_MK\nelse\n\techo \"#define\tHAVE_SYS_RBTREE_H\" >>$CONFIG_H\nfi\n\nif [ -z \"$REALLOCARRAY\" ]; then\n\tprintf \"Testing for reallocarray ... \"\n\tcat <<EOF >_reallocarray.c\n#include <stdlib.h>\n\nint main(void) {\n\tvoid *foo = reallocarray(NULL, 0, 0);\n\treturn foo == NULL ? 1 : 0;\n}\nEOF\n\tif $XCC _reallocarray.c -o _reallocarray 2>&3; then\n\t\tREALLOCARRAY=yes\n\telse\n\t\tREALLOCARRAY=no\n\tfi\n\techo \"$REALLOCARRAY\"\n\trm -f _reallocarray.c _reallocarray\nfi\nif [ \"$REALLOCARRAY\" = no ]; then\n\techo \"COMPAT_SRCS+=\tcompat/reallocarray.c\" >>$CONFIG_MK\n\techo \"#include\t\t\t\\\"compat/reallocarray.h\\\"\">>$CONFIG_H\nfi\n# Set this for eloop\necho \"#define\tHAVE_REALLOCARRAY\" >>$CONFIG_H\n\nif [ -z \"$BE64ENC\" ]; then\n\tprintf \"Testing for be64enc ... \"\n\tcat <<EOF >_be64enc.c\n#include <sys/endian.h>\n#include <stdlib.h>\nint main(void) {\n\tbe64enc(NULL, 0);\n\treturn 0;\n}\nEOF\n\tif $XCC _be64enc.c -o _be64enc 2>&3; then\n\t\tBE64ENC=yes\n\telse\n\t\tBE64ENC=no\n\tfi\n\techo \"$BE64ENC\"\n\trm -f _be64enc.c _be64enc\nfi\nif [ \"$BE64ENC\" = no ]; then\n\techo \"#include\t\t\t\\\"compat/endian.h\\\"\" >>$CONFIG_H\nfi\n\nif [ -z \"$FLS64\" ]; then\n\tprintf \"Testing for fls64 ... \"\n\tcat <<EOF >_fls64.c\n#include <sys/bitops.h>\nint main(void) {\n\treturn (int)fls64(1337);\n}\nEOF\n\tif $XCC _fls64.c -o _fls64 2>&3; then\n\t\tFLS64=yes\n\telse\n\t\tFLS64=no\n\tfi\n\techo \"$FLS64\"\n\trm -f _fls64.c _fls64\nfi\nif [ \"$FLS64\" = yes ]; then\n\techo \"#define\tHAVE_SYS_BITOPS_H\" >>$CONFIG_H\nfi\n\nif [ -z \"$MD5\" ]; then\n\tMD5_LIB=\n\tprintf \"Testing for MD5Init ... \"\n\tcat <<EOF >_md5.c\n#include <sys/types.h>\n#include <md5.h>\n#include <stdlib.h>\nint main(void) {\n\tMD5_CTX context;\n\tMD5Init(&context);\n\treturn 0;\n}\nEOF\n\t# We only want to link to libmd if it exists in /lib\n\tif $ALLOW_USR_LIBS; then\n\t\tset -- /\n\telse\n\t\tset -- $(ls /lib/libmd.so.* 2>/dev/null)\n\tfi\n\tif $XCC _md5.c -o _md5 2>&3; then\n\t\tMD5=yes\n\telif [ -e \"$1\" ] && $XCC _md5.c -lmd -o _md5 2>&3; then\n\t\tMD5=\"yes (-lmd)\"\n\t\tMD5_LIB=-lmd\n\telse\n\t\tMD5=no\n\tfi\n\techo \"$MD5\"\n\trm -f _md5.c _md5\nfi\n\nif [ -z \"$SHA2_H\" ]; then\n\tif [ -z \"$SHA2\" ] || [ \"$SHA2\" != no ]; then\n\t\tprintf \"Testing for sha2.h ... \"\n\t\tif [ -e /usr/include/sha2.h ]; then\n\t\t\tSHA2_H=sha2.h\n\t\telif [ -e /usr/include/sha256.h ]; then\n\t\t\tSHA2_H=sha256.h\n\t\tfi\n\t\tif [ -n \"$SHA2_H\" ]; then\n\t\t\techo \"$SHA2_H\"\n\t\telse\n\t\t\techo \"no\"\n\t\tfi\n\tfi\nfi\n\nif [ -z \"$SHA2\" ]; then\n\tSHA2_LIB=\n\tSHA2_RENAMED=\n\tprintf \"Testing for SHA256_Init ... \"\n\tcat <<EOF >_sha256.c\n#include <sys/types.h>\nEOF\n\t[ -n \"$SHA2_H\" ] && echo \"#include <$SHA2_H>\">>_sha256.c\n\tcat <<EOF >>_sha256.c\n#include <stdlib.h>\nint main(void) {\n\tSHA256_CTX context;\n\tSHA256_Init(&context);\n\treturn 0;\n}\nEOF\n\t# We only want to link to libmd if it exists in /lib\n\tif $ALLOW_USR_LIBS; then\n\t\tset -- /\n\telse\n\t\tset -- $(ls /lib/libmd.so.* 2>/dev/null)\n\tfi\n\tif $XCC _sha256.c -o _sha256 2>&3; then\n\t\tSHA2=yes\n\telif [ -e \"$1\" ] && $XCC _sha256.c -lmd -o _sha256 2>&3; then\n\t\tSHA2=\"yes (-lmd)\"\n\t\tSHA2_LIB=-lmd\n\telse\n\t\tSHA2=no\n\tfi\n\techo \"$SHA2\"\n\trm -f _sha256.c _sha256\n\tif [ \"$SHA2\" = no ]; then\n\t\t# Did OpenBSD really change this? grrrr\n\t\tprintf \"Testing for SHA256Init ... \"\n\t\tcat <<EOF >_sha256.c\n#include <sys/types.h>\nEOF\n\t\t[ -n \"$SHA2_H\" ] && echo \"#include <$SHA2_H>\">>_sha256.c\n\t\tcat <<EOF >>_sha256.c\n#include <stdlib.h>\nint main(void) {\n\tSHA2_CTX context;\n\tSHA256Init(&context);\n\treturn 0;\n}\nEOF\n\t\t# We only want to link to libmd if it exists in /lib\n\t\tif $ALLOW_USR_LIBS; then\n\t\t\tset -- /\n\t\telse\n\t\t\tset -- $(ls /lib/libmd.so.* 2>/dev/null)\n\t\tfi\n\t\tif $XCC\t_sha256.c -o _sha256 2>&3; then\n\t\t\tSHA2=yes\n\t\t\tSHA2_RENAMED=yes\n\t\telif [ -e \"$1\" ] && $XCC _sha256.c -lmd -o _sha256 2>&3\n\t\tthen\n\t\t\tSHA2=\"yes (-lmd)\"\n\t\t\tSHA2_LIB=-lmd\n\t\t\tSHA2_RENAMED=yes\n\t\telse\n\t\t\tSHA2=no\n\t\tfi\n\t\techo \"$SHA2\"\n\t\trm -f _sha256.c _sha256\n\tfi\nfi\n\nif [ -z \"$HMAC\" ]; then\n\tHMAC_LIB=\n\tprintf \"Testing for hmac ... \"\n\tcat <<EOF >_hmac.c\n#include <stdlib.h>\n#include <hmac.h>\nint main(void) {\n\treturn (int)hmac(NULL, NULL, 0, NULL, 0, NULL, 0);\n}\nEOF\n\tif $XCC _hmac.c $MD5_LIB -o _hmac 2>&3; then\n\t\tHMAC=yes\n\t\techo \"#define\tHAVE_HMAC_H\" >>$CONFIG_H\n\telse\n\t\t# Remove this test if NetBSD-8 ships with\n\t\t# hmac in it's own header and not stdlib.h\n\t\tcat <<EOF >_hmac.c\n#include <stdlib.h>\nint main(void) {\n\treturn (int)hmac(NULL, NULL, 0, NULL, 0, NULL, 0);\n}\nEOF\n\t\tif $XCC _hmac.c $MD5_LIB -o _hmac 2>&3; then\n\t\t\tHMAC=yes\n\t\telse\n\t\t\tHMAC=no\n\t\tfi\n\tfi\n\techo \"$HMAC\"\n\trm -f _hmac.c _hmac\nfi\n\nif [ \"$OPENSSL\" = yes ] ||\n   { [ -z \"$OPENSSL\" ] && [ \"$ALLOW_USR_LIBS\" = true ] &&\n     [ \"$SHA2\" = no ] &&  [ \"$HMAC\" = no ];\n   }; then\n\tprintf \"Testing for openssl ... \"\n\tif type \"$PKG_CONFIG\" >/dev/null 2>&1; then\n\t\tLIBCRYPTO_CFLAGS=$(\"$PKG_CONFIG\" --cflags libcrypto 2>&3)\n\t\tLIBCRYPTO_LIBS=$(\"$PKG_CONFIG\" --libs libcrypto 2>&3)\n\tfi\n\n\tcat <<EOF >_openssl.c\n#include <stdio.h>\n#include <openssl/crypto.h>\nint main(void) {\n\treturn OPENSSL_init_crypto(0, NULL) == 1;\n}\nEOF\n\tif $XCC $LIBCRYPTO_CFLAGS _openssl.c -o _openssl $LIBCRYPTO_LIBS 2>&3;\n\tthen\n\t\tOPENSSL=yes\n\t\tMD5=yes\n\t\tMD5_LIB=\n\t\tif [ -n \"$LIBCRYPTO_CFLAGS\" ]; then\n\t\t\techo \"CFLAGS+=\t\t$LIBCRYPTO_CFLAGS\" >>$CONFIG_MK\n\t\tfi\n\t\techo \"LDADD+=\t\t$LIBCRYPTO_LIBS\" >>$CONFIG_MK\n\t\techo \"#define\tHAVE_OPENSSL\" >>$CONFIG_H\n\telse\n\t\tOPENSSL=no\n\tfi\n\techo \"$OPENSSL\"\n\trm -f _openssl.c _openssl\nfi\n\nif [ \"$OPENSSL\" = yes ]; then\n\tprintf \"Testing for openssl/sha.h ... \"\n\tcat <<EOF >_openssl_sha.c\n#include <stdio.h>\n#include <openssl/sha.h>\n\nint main(void) {\n\tSHA256_CTX ctx;\n\tSHA256_Init(&ctx);\n\treturn 0;\n}\nEOF\n\tif $XCC $LIBCRYPTO_CFLAGS _openssl_sha.c -o _openssl_sha \\\n\t   $LIBCRYPTO_LIBS 2>&3; then\n\t\tSHA2_H=openssl/sha.h\n\t\tSHA2=\"yes (-lcrypto)\"\n\telse\n\t\tSHA2=no\n\tfi\n\tSHA2_LIB=\n\tSHA2_RENAMED=\n\techo \"$SHA2\"\n\trm -f _openssl_sha.c _openssl_sha\nfi\n\nif [ \"$LIBPCAP\" = yes ]; then\n\tprintf \"Testing for libpcap ... \"\n\tif $PKG_CONFIG --exists libpcap 2>/dev/null; then\n\t\tLIBPCAP_CFLAGS=$($PKG_CONFIG --cflags libpcap 2>/dev/null)\n\t\tLIBPCAP_LIBS=$($PKG_CONFIG --libs libpcap 2>/dev/null)\n\t\techo \"yes\"\n\telse\n\t\tcat <<EOF >_libpcap.c\n#include <pcap.h>\n\nint main(void) {\n\treturn pcap_activate(NULL);\n}\nEOF\n\t\tif $XCC _libpcap.c -o libpcap -lpcap 2>&3; then\n\t\t\techo \"yes\"\n\t\t\tLIBPCAP_CFLAGS=\n\t\t\tLIBPCAP_LIBS=\"-lpcap\"\n\t\t\tabort=false\n\t\telse\n\t\t\techo \"no\"\n\t\t\tabort=true\n\t\tfi\n\t\trm -f _libpcap.c libpcap\n\t\t$abort && exit 1\n\tfi\n\techo \"CFLAGS+=\t$LIBPCAP_CFLAGS\" >>$CONFIG_MK\n\techo \"LDADD+=\t\t$LIBPCAP_LIBS\" >>$CONFIG_MK\n\techo \"#define\tUSE_LIBPCAP\" >>$CONFIG_H\n\n\tprintf \"Testing for pcap_set_immediate_mode() ... \"\n\tcat <<EOF >_libpcap_imm.c\n#include <pcap.h>\n\nint main(void) {\n\treturn pcap_set_immediate_mode(NULL, 1);\n}\nEOF\n\tif $XCC $LIBPCAP_CFLAGS _libpcap_imm.c -o libpcap_imm $LIBPCAP_LIBS 2>&3; then\n\t\techo \"yes\"\n\t\techo \"#define HAVE_PCAP_SET_IMMEDIATE_MODE\" >>$CONFIG_H\n\telse\n\t\techo \"no\"\n\tfi\n\trm -f _libpcap_imm.c libpcap_imm\n\n\tprintf \"Testing for pcap_setwritefilter() ... \"\n\tcat <<EOF >_libpcap_write.c\n#include <pcap.h>\n\nint main(void) {\n\treturn pcap_setwritefilter(NULL, NULL);\n}\nEOF\n\tif $XCC $LIBPCAP_CFLAGS _libpcap_write.c -o libpcap_write $LIBPCAP_LIBS 2>&3; then\n\t\techo \"yes\"\n\t\techo \"#define HAVE_PCAP_SETWRITEFILTER\" >>$CONFIG_H\n\telse\n\t\techo \"no\"\n\tfi\n\trm -f _libpcap_write.c libpcap_write\n\n\tprintf \"Testing for pcap_lockfilter() ... \"\n\tcat <<EOF >_libpcap_lock.c\n#include <pcap.h>\n\nint main(void) {\n\treturn pcap_lockfilter(NULL);\n}\nEOF\n\tif $XCC $LIBPCAP_CFLAGS _libpcap_lock.c -o libpcap_lock $LIBPCAP_LIBS 2>&3; then\n\t\techo \"yes\"\n\t\techo \"#define HAVE_PCAP_LOCKFILTER\" >>$CONFIG_H\n\telse\n\t\techo \"no\"\n\tfi\n\trm -f _libpcap_lock.c libpcap_lock\nfi\n\n# Workaround for DragonFlyBSD import\nif [ \"$OS\" = dragonfly ]; then\n\techo \"#ifdef\tUSE_PRIVATECRYPTO\" >>$CONFIG_H\n\techo \"#define\tHAVE_MD5_H\" >>$CONFIG_H\n\techo \"#define\tSHA2_H\t\t\t<openssl/sha.h>\" >>$CONFIG_H\n\techo \"#else\" >>$CONFIG_H\nfi\n\nif [ \"$MD5\" = no ]; then\n\techo \"#include\t\t\t\\\"compat/crypt/md5.h\\\"\" >>$CONFIG_H\n\techo \"MD5_SRC=\tcompat/crypt/md5.c\" >>$CONFIG_MK\nelse\n\techo \"MD5_SRC=\" >>$CONFIG_MK\n\t[ \"$OPENSSL\" != yes ] && echo \"#define\tHAVE_MD5_H\" >>$CONFIG_H\n\t[ -n \"$MD5_LIB\" ] && echo \"LDADD+=\t\t$MD5_LIB\" >>$CONFIG_MK\nfi\n\nif [ \"$OPENSSL\" = yes ] && [ \"$SHA2\" = no ]; then\n\techo \"#include\t\t\t\\\"compat/crypt_openssl/sha256.h\\\"\" >>$CONFIG_H\n\techo \"SHA256_SRC=\tcompat/crypt_openssl/sha256.c\" >>$CONFIG_MK\nelif [ \"$SHA2\" = no ]; then\n\techo \"#include\t\t\t\\\"compat/crypt/sha256.h\\\"\" >>$CONFIG_H\n\techo \"SHA256_SRC=\tcompat/crypt/sha256.c\" >>$CONFIG_MK\nelse\n\techo \"SHA256_SRC=\" >>$CONFIG_MK\n\techo \"#define\tSHA2_H\t\t\t<$SHA2_H>\" >>$CONFIG_H\n\tif [ \"$SHA2_RENAMED\" = yes ]; then\n\t\techo \"#define\tSHA256_CTX\tSHA2_CTX\" >>$CONFIG_H\n\t\techo \"#define\tSHA256_Init\tSHA256Init\" >>$CONFIG_H\n\t\techo \"#define\tSHA256_Update\tSHA256Update\" >>$CONFIG_H\n\t\techo \"#define\tSHA256_Final\tSHA256Final\" >>$CONFIG_H\n\tfi\n\t[ -n \"$SHA2_LIB\" ] && echo \"LDADD+=\t\t$SHA2_LIB\" >>$CONFIG_MK\nfi\n\n# Workarond for DragonFlyBSD import\n[ \"$OS\" = dragonfly ] && echo \"#endif\" >>$CONFIG_H\n\nif [ \"$OPENSSL\" = yes ]; then\n\techo \"#include\t\t\t\\\"compat/crypt_openssl/hmac.h\\\"\" >>$CONFIG_H\n\techo \"HMAC_SRC=\tcompat/crypt_openssl/hmac.c\" >>$CONFIG_MK\nelif [ \"$HMAC\" = no ]; then\n\techo \"#include\t\t\t\\\"compat/crypt/hmac.h\\\"\" >>$CONFIG_H\n\techo \"HMAC_SRC=\tcompat/crypt/hmac.c\" >>$CONFIG_MK\nelse\n\t# echo \"#define\tHAVE_HMAC_H\" >>$CONFIG_H\n\techo \"HMAC_SRC=\" >>$CONFIG_MK\nfi\n\nif [ -z \"$AUTH\" ] || [ \"$AUTH\" = yes ]; then\n\tif [ \"$HMAC\" = no ]; then\n\t\techo \"CRYPT_SRCS+=\t\\${HMAC_SRC}\" >>$CONFIG_MK\n\tfi\nfi\nif [ -z \"$INET6\" ] || [ \"$INET6\" = yes ] || \\\n    [ -z \"$AUTH\" ] || [ \"$AUTH\" = yes ]; then\n\tif [ \"$MD5\" = no ]; then\n\t\techo \"CRYPT_SRCS+=\t\\${MD5_SRC}\" >>$CONFIG_MK\n\tfi\n\tif [ \"$SHA2\" = no ]; then\n\t\techo \"CRYPT_SRCS+=\t\\${SHA256_SRC}\" >>$CONFIG_MK\n\tfi\nfi\n\nif [ \"$DEV\" != no ] && [ \"$UDEV\" != no ]; then\n\tprintf \"Checking for libudev ... \"\n\tif type \"$PKG_CONFIG\" >/dev/null 2>&1; then\n\t\tLIBUDEV_CFLAGS=$(\"$PKG_CONFIG\" --cflags libudev 2>&3)\n\t\tLIBUDEV_LIBS=$(\"$PKG_CONFIG\" --libs libudev 2>&3)\n\tfi\n\tif [ -n \"$LIBUDEV_LIBS\" ] && [ \"$UDEV\" = yes ]; then\n\t\techo \"yes\"\n\telif [ -n \"$LIBUDEV_LIBS\" ]; then\n\t\tcase \"$OS\" in\n\t\tlinux*)\techo \"yes\";;\n\t\t*)\techo \"yes (disabled)\"\n\t\t\t# FreeBSD libudev fails to return a udev device\n\t\t\t# with udev_device_new_from_subsystem_sysname\n\t\t\t# which breaks our test for device initialisation\n\t\t\tLIBUDEV_CFLAGS=\n\t\t\tLIBUDEV_LIBS=\n\t\t\t;;\n\t\tesac\n\telse\n\t\techo \"no\"\n\tfi\nfi\n\nif [ \"$DEV\" != no ] && [ \"$UDEV\" != no ] && [ -n \"$LIBUDEV_LIBS\" ]; then\n\t[ -z \"$DEV\" ] && DEV=yes\n\techo \"DEV_PLUGINS+=\tudev\" >>$CONFIG_MK\n\tif [ -n \"$LIBUDEV_CFLAGS\" ]; then\n\t\techo \"LIBUDEV_CFLAGS=\t\t$LIBUDEV_CFLAGS\" >>$CONFIG_MK\n\tfi\n\techo \"LIBUDEV_LIBS=\t$LIBUDEV_LIBS\" >>$CONFIG_MK\n\n\tprintf \"Checking udev_monitor_filter_add_match_subsystem_devtype ... \"\n\tcat <<EOF >_udev.c\n#include <libudev.h>\n#include <stdlib.h>\nint main(void) {\n\treturn udev_monitor_filter_add_match_subsystem_devtype(NULL, NULL, NULL);\n}\nEOF\n\tif $XCC $LIBUDEV_CFLAGS _udev.c -o _udev $LIBUDEV_LIBS 2>&3\n\tthen\n\t\techo \"yes\"\n\telse\n\t\techo \"LIBUDEV_CPPFLAGS+=\t-DLIBUDEV_NOFILTER\" >>$CONFIG_MK\n\t\techo \"no\"\n\tfi\n\trm -f _udev.c _udev\n\n\tprintf \"Checking udev_device_get_is_initialized ... \"\n\tcat <<EOF >_udev.c\n#include <libudev.h>\n#include <stdlib.h>\nint main(void) {\n\treturn udev_device_get_is_initialized(NULL);\n}\nEOF\n\tif $XCC $LIBUDEV_CFLAGS _udev.c -o _udev $LIBUDEV_LIBS 2>&3\n\tthen\n\t\techo \"yes\"\n\telse\n\t\techo \"LIBUDEV_CPPFLAGS+=\t-DLIBUDEV_NOINIT\" >>$CONFIG_MK\n\t\techo \"no\"\n\tfi\n\trm -f _udev.c _udev\nelif [ \"$DEV\" != no ] && [ \"$UDEV\" != no ] && [ -n \"$UDEV\" ]; then\n\techo \"udev has been explicitly requested ... aborting\" >&2\n\texit 1\nfi\n\nif [ \"$DEV\" = yes ]; then\n\techo \"DHCPCD_SRCS+=\tdev.c\" >>$CONFIG_MK\n\techo \"CPPFLAGS+=\t-DPLUGIN_DEV\" >>$CONFIG_MK\n\techo \"MKDIRS+=\tdev\" >>$CONFIG_MK\n\n\t# So the plugins have access to logerr\n\techo \"LDFLAGS+=\t-Wl,-export-dynamic\" >>$CONFIG_MK\n\n\tprintf \"Testing for dlopen ... \"\n\tcat <<EOF >_dlopen.c\n#include <dlfcn.h>\n#include <stdlib.h>\nint main(void) {\n\tvoid *h = dlopen(\"/foo/bar\", 0);\n\tvoid (*s)(char *) = dlsym(h, \"deadbeef\");\n\ts(dlerror());\n\tdlclose(h);\n\treturn 0;\n}\nEOF\n\tif $XCC _dlopen.c -o _dlopen 2>&3; then\n\t\techo \"yes\"\n\telif $XCC _dlopen.c -ldl -o _dlopen 2>&3; then\n\t\techo \"yes (-ldl)\"\n\t\techo \"LDADD+=\t\t-ldl\" >>$CONFIG_MK\n\telse\n\t\techo \"no\"\n\t\techo \"libc for dlopen is required - aborting\"\n\tfi\n\trm -f _dlopen.c _dlopen\n\t$abort && exit 1\nfi\n\n# Transform for a make file\nSERVICEEXISTS=$(echo \"$SERVICEEXISTS\" | $SED \\\n\t-e 's:\\\\:\\\\\\\\:g' \\\n\t-e 's:\\&:\\\\\\&:g' \\\n\t-e 's:\\$:\\\\\\\\\\$\\$:g' \\\n)\necho \"SERVICEEXISTS=\t$SERVICEEXISTS\" >>config.mk\nSERVICECMD=$(echo \"$SERVICECMD\" | $SED \\\n\t-e 's:\\\\:\\\\\\\\:g' \\\n\t-e 's:\\&:\\\\\\&:g' \\\n\t-e 's:\\$:\\\\\\\\\\$\\$:g' \\\n)\necho \"SERVICECMD=\t$SERVICECMD\" >>config.mk\nSERVICESTATUS=$(echo \"$SERVICESTATUS\" | $SED \\\n\t-e 's:\\\\:\\\\\\\\:g' \\\n\t-e 's:\\&:\\\\\\&:g' \\\n\t-e 's:\\$:\\\\\\\\\\$\\$:g' \\\n)\necho \"SERVICESTATUS=\t$SERVICESTATUS\" >>config.mk\nif [ -z \"$STATUSARG\" ]; then\n\tcase \"$OS\" in\n\tdragonfly*|freebsd*)\tSTATUSARG=\"onestatus\";;\n\tesac\nfi\necho \"STATUSARG=\t$STATUSARG\" >>config.mk\n\nHOOKS=\nif ! $HOOKSET; then\n\tprintf \"Checking for ntpd ... \"\n\tNTPD=$(_which ntpd)\n\tif [ -n \"$NTPD\" ]; then\n\t\techo \"$NTPD (50-ntp.conf)\"\n\telse\n\t\techo \"not found\"\n\tfi\n\tprintf \"Checking for chronyd ... \"\n\tCHRONYD=$(_which chronyd)\n\tif [ -n \"$CHRONYD\" ]; then\n\t\techo \"$CHRONYD (50-ntp.conf)\"\n\telse\n\t\techo \"not found\"\n\tfi\n\tif [ -n \"$NTPD\" ] || [ -n \"$CHRONYD\" ]; then\n\t\tHOOKS=\"$HOOKS${HOOKS:+ }50-ntp.conf\"\n\tfi\n\t# Warn if both are detected\n\tif [ -n \"$NTPD\" ] && [ -n \"$CHRONYD\" ]; then\n\t\techo \"NTP will default to $NTPD\"\n\tfi\n\n\tprintf \"Checking for timesyncd ... \"\n\tTIMESYNCD=\n\tfor x in /usr/lib/systemd/systemd-timesyncd; do\n\t\tif [ -x \"$x\" ]; then\n\t\t\tTIMESYNCD=$x\n\t\t\tbreak\n\t\tfi\n\tdone\n\tif [ -n \"$TIMESYNCD\" ]; then\n\t\techo \"$TIMESYNCD\"\n\t\tHOOKS=\"$HOOKS${HOOKS:+ }50-timesyncd.conf\"\n\telse\n\t\techo \"not found\"\n\tfi\n\n\tprintf \"Checking for ypbind ... \"\n\tYPBIND=$(_which ypbind)\n\tif [ -n \"$YPBIND\" ]; then\n\t\tYPHOOK=\"50-ypbind\"\n\t\tif strings \"$YPBIND\" | $GREP -q yp\\\\.conf; then\n\t\t\tYPHOOK=\"50-yp.conf\"\n\t\t\tYPOS=\"Linux\"\n\t\telif strings \"$YPBIND\" | $GREP -q \\\\.ypservers; then\n\t\t\tYPOS=\"NetBSD\"\n\t\t\techo \"YPDOMAIN_DIR=\t/var/yp\" >>$CONFIG_MK\n\t\t\techo \"YPDOMAIN_SUFFIX=.ypservers\" >>$CONFIG_MK\n\t\telif strings \"$YPBIND\" | $GREP -q /etc/yp; then\n\t\t\tYPOS=\"OpenBSD\"\n\t\t\techo \"YPDOMAIN_DIR=\t/etc/yp\" >>$CONFIG_MK\n\t\t\techo \"YPDOMAIN_SUFFIX=\" >>$CONFIG_MK\n\t\telse\n\t\t\tYPOS=\"FreeBSD\"\n\t\t\techo \"YPDOMAIN_DIR=\" >>$CONFIG_MK\n\t\t\techo \"YPDOMAIN_SUFFIX=\" >>$CONFIG_MK\n\t\tfi\n\t\techo \"$YPBIND ($YPHOOK${YPOS:+ }$YPOS)\"\n\t\tEGHOOKS=\"$EGHOOKS${EGHOOKS:+ }$YPHOOK\"\n\telse\n\t\techo \"not found\"\n\t\tYPHOOK=\"50-ypbind\"\n\t\tcase \"$OS\" in\n\t\tlinux*)\n\t\t\tYPHOOK=\"50-yp.conf\"\n\t\t\tYPOS=\"Linux\"\n\t\t\t;;\n\t\tfreebsd*|dragonfly*)\n\t\t\tYPOS=\"FreeBSD\"\n\t\t\techo \"YPDOMAIN_DIR=\" >>$CONFIG_MK\n\t\t\techo \"YPDOMAIN_SUFFIX=\" >>$CONFIG_MK\n\t\t\t;;\n\t\tnetbsd*)\n\t\t\tYPOS=\"NetBSD\"\n\t\t\techo \"YPDOMAIN_DIR=\t/var/yp\" >>$CONFIG_MK\n\t\t\techo \"YPDOMAIN_SUFFIX=.ypservers\" >>$CONFIG_MK\n\t\t\t;;\n\t\topenbsd*)\n\t\t\tYPOS=\"OpenBSD\"\n\t\t\techo \"YPDOMAIN_DIR=\t/etc/yp\" >>$CONFIG_MK\n\t\t\techo \"YPDOMAIN_SUFFIX=\" >>$CONFIG_MK\n\t\t\t;;\n\t\t*)\n\t\t\tYPHOOK=\n\t\t\t;;\n\t\tesac\n\t\tif [ -n \"$YPHOOK\" ]; then\n\t\t\techo \"Assuming ypbind is $YPOS\"\n\t\t\tEGHOOKS=\"$EGHOOKS${EGHOOKS:+ }$YPHOOK\"\n\t\tfi\n\tfi\nfi\nif [ \"$NTP\" = yes ]; then\n\t# --enable-ntp\n\techo \"UNCOMMENT_NTP=\tyes\" >>$CONFIG_MK\nfi\n\necho >>$CONFIG_H\necho \"#endif /*CONFIG_H*/\">>$CONFIG_H\n\nfind_hook()\n{\n\tfor h in [0-9][0-9]\"-$x\" [0-9][0-9]\"-$x.in\" \\\n\t    [0-9][0-9]\"-$x.sh\" [0-9][0-9]\"-$x.sh.in\" \\\n\t    [0-9][0-9]\"-$x.conf\" [0-9][0-9]\"-$x.conf.in\"\n\tdo\n\t\t[ -e \"$h\" ] && break\n\tdone\n\t[ -e \"$h\" ] || return 1\n\techo \"${h%%.in}\"\n\treturn 0\n}\n\nif cd hooks; then\n\tfor x in $HOOKSCRIPTS; do\n\t\tprintf \"Finding hook $x ... \"\n\t\th=$(find_hook \"$x\")\n\t\tif [ -z \"$h\" ]; then\n\t\t\techo \"no\"\n\t\telse\n\t\t\techo \"$h\"\n\t\t\tcase \" $HOOKS \" in\n\t\t\t*\" $h \"*)\t;;\n\t\t\t*)\t\tHOOKS=\"$HOOKS${HOOKS:+ }$h\";;\n\t\t\tesac\n\t\tfi\n\tdone\n\tfor x in $EGHOOKSCRIPTS; do\n\t\tprintf \"Finding example hook $x ... \"\n\t\th=$(find_hook \"$x\")\n\t\tif [ -z \"$h\" ]; then\n\t\t\techo \"no\"\n\t\telse\n\t\t\techo \"$h\"\n\t\t\tcase \" $EGHOOKS \" in\n\t\t\t*\" $h \"*)\t;;\n\t\t\t*)\t\tEGHOOKS=\"$EGHOOKS${EGHOOKS:+ }$h\";;\n\t\t\tesac\n\t\tfi\n\tdone\n\tcd ..\nfi\necho \"HOOKSCRIPTS=\t$HOOKS\" >>$CONFIG_MK\necho \"EGHOOKSCRIPTS=\t$EGHOOKS\" >>$CONFIG_MK\n\necho\necho \"   SYSCONFDIR =\t\t$SYSCONFDIR\"\necho \"   SBINDIR =\t\t$SBINDIR\"\necho \"   LIBDIR =\t\t$LIBDIR\"\necho \"   LIBEXECDIR =\t\t$LIBEXECDIR\"\necho \"   DBDIR =\t\t$DBDIR\"\necho \"   RUNDIR =\t\t$RUNDIR\"\necho \"   MANDIR =\t\t$MANDIR\"\necho \"   DATADIR =\t\t$DATADIR\"\necho \"   HOOKSCRIPTS =\t$HOOKS\"\necho \"   EGHOOKSCRIPTS =\t$EGHOOKS\"\necho \"   STATUSARG =\t\t$STATUSARG\"\nif [ \"$PRIVSEP\" = yes ]; then\n\techo \"   PRIVSEPUSER =\t$PRIVSEP_USER\"\nfi\necho\n\nrm -f dhcpcd tests/test\n"
  },
  {
    "path": "hooks/01-test",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2017-2020 Roy Marples <roy@marples.name>\n\n# Echo the interface flags, reason and message options\n\nif [ \"$reason\" = \"TEST\" ]; then\n\t# General variables at the top\n\tset | while read line; do\n\t\tcase \"$line\" in\n\t\tinterface=*|pid=*|reason=*|protocol=*|profile=*|skip_hooks=*)\n\t\t\tprintf '%s\\n' \"$line\";;\n\t\tesac\n\tdone\n\t# Interface flags\n\tset | while read line; do\n\t\tcase \"$line\" in\n\t\tifcarrier=*|ifflags=*|ifmetric=*|ifmtu=*|ifwireless=*|ifssid=*)\n\t\t\tprintf '%s\\n' \"$line\";;\n\t\tesac\n\tdone\n\t# Old lease\n\tset | while read line; do\n\t\tcase \"$line\" in\n\t\told_*) printf '%s\\n' \"$line\";;\n\t\tesac\n\tdone\n\t# New lease\n\tset | while read line; do\n\t\tcase \"$line\" in\n\t\tnew_*) printf '%s\\n' \"$line\";;\n\t\tesac\n\tdone\n\t# Router Advertisements\n\tset | while read line; do\n\t\tcase \"$line\" in\n\t\tnd[0-9]*_*) printf '%s\\n' \"$line\";;\n\t\tesac\n\tdone\n\texit 0\nfi\n"
  },
  {
    "path": "hooks/10-wpa_supplicant",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2017-2024 Roy Marples <roy@marples.name>\n\n# Start, reconfigure and stop wpa_supplicant per wireless interface.\n#\n# This is only needed when using wpa_supplicant-2.5 or older, OR\n# when wpa_supplicant has not been built with CONFIG_MATCH_IFACE, OR\n# wpa_supplicant was launched without the -M flag to activate\n# interface matching.\n\nif [ -z \"$wpa_supplicant_conf\" ]; then\n\tfor x in \\\n\t\t/etc/wpa_supplicant/wpa_supplicant-\"$interface\".conf \\\n\t\t/etc/wpa_supplicant/wpa_supplicant.conf \\\n\t\t/etc/wpa_supplicant-\"$interface\".conf \\\n\t\t/etc/wpa_supplicant.conf \\\n\t; do\n\t\tif [ -s \"$x\" ]; then\n\t\t\twpa_supplicant_conf=\"$x\"\n\t\t\tbreak\n\t\tfi\n\tdone\nfi\n: ${wpa_supplicant_conf:=/etc/wpa_supplicant.conf}\n\nwpa_supplicant_ctrldir()\n{\n\tdir=$(key_get_value \"[[:space:]]*ctrl_interface=\" \\\n\t\t\"$wpa_supplicant_conf\")\n\tdir=$(trim \"$dir\")\n\tcase \"$dir\" in\n\tDIR=*)\n\t\tdir=${dir##DIR=}\n\t\tdir=${dir%%[[:space:]]GROUP=*}\n\t\tdir=$(trim \"$dir\")\n\t\t;;\n\tesac\n\tprintf %s \"$dir\"\n}\n\nwpa_supplicant_start()\n{\n\t# If the carrier is up, don't bother checking anything\n\t[ \"$ifcarrier\" = \"up\" ] && return 0\n\n\t# Pre flight checks\n\tif [ ! -s \"$wpa_supplicant_conf\" ]; then\n\t\tsyslog warn \\\n\t\t\t\"$wpa_supplicant_conf does not exist\"\n\t\tsyslog warn \"not interacting with wpa_supplicant(8)\"\n\t\treturn 1\n\tfi\n\tdir=$(wpa_supplicant_ctrldir)\n\tif [ -z \"$dir\" ]; then\n\t\tsyslog warn \\\n\t\t\t\"ctrl_interface not defined in $wpa_supplicant_conf\"\n\t\tsyslog warn \"not interacting with wpa_supplicant(8)\"\n\t\treturn 1\n\tfi\n\n\twpa_cli -p \"$dir\" -i \"$interface\" status >/dev/null 2>&1 && return 0\n\tsyslog info \"starting wpa_supplicant\"\n\tdriver=${wpa_supplicant_driver:+-D}$wpa_supplicant_driver\n\terr=$(wpa_supplicant -B -c\"$wpa_supplicant_conf\" -i\"$interface\" \\\n\t    \"$driver\" 2>&1)\n\terrn=$?\n\tif [ $errn != 0 ]; then\n\t\tsyslog err \"failed to start wpa_supplicant\"\n\t\tsyslog err \"$err\"\n\tfi\n\treturn $errn\n}\n\nwpa_supplicant_reconfigure()\n{\n\tdir=$(wpa_supplicant_ctrldir)\n\t[ -z \"$dir\" ] && return 1\n\tif ! wpa_cli -p \"$dir\" -i \"$interface\" status >/dev/null 2>&1; then\n\t\twpa_supplicant_start\n\t\treturn $?\n\tfi\n\tsyslog info \"reconfiguring wpa_supplicant\"\n\terr=$(wpa_cli -p \"$dir\" -i \"$interface\" reconfigure 2>&1)\n\terrn=$?\n\tif [ $errn != 0 ]; then\n\t\tsyslog err \"failed to reconfigure wpa_supplicant\"\n\t\tsyslog err \"$err\"\n\tfi\n\treturn $errn\n}\n\nwpa_supplicant_stop()\n{\n\tdir=$(wpa_supplicant_ctrldir)\n\t[ -z \"$dir\" ] && return 1\n\twpa_cli -p \"$dir\" -i \"$interface\" status >/dev/null 2>&1 || return 0\n\tsyslog info \"stopping wpa_supplicant\"\n\terr=$(wpa_cli -i\"$interface\" terminate 2>&1)\n\terrn=$?\n\tif [ $errn != 0 ]; then\n\t\tsyslog err \"failed to stop wpa_supplicant\"\n\t\tsyslog err \"$err\"\n\tfi\n\treturn $errn\n}\n\nif [ \"$ifwireless\" = \"1\" ] && \\\n    command -v wpa_supplicant >/dev/null 2>&1 && \\\n    command -v wpa_cli >/dev/null 2>&1\nthen\n\tcase \"$reason\" in\n\tPREINIT)\t\twpa_supplicant_start;;\n\tRECONFIGURE)\t\twpa_supplicant_reconfigure;;\n\tDEPARTED|STOPPED)\twpa_supplicant_stop;;\n\tesac\nfi\n"
  },
  {
    "path": "hooks/15-timezone",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2017-2020 Roy Marples <roy@marples.name>\n\n# Configure timezone\n\n: ${localtime:=/etc/localtime}\n\nset_zoneinfo()\n{\n\t[ -z \"$new_tzdb_timezone\" ] && return 0\n\n\tzoneinfo_dir=\n\tfor d in \\\n\t\t/usr/share/zoneinfo\t\\\n\t\t/usr/lib/zoneinfo\t\\\n\t\t/var/share/zoneinfo\t\\\n\t\t/var/zoneinfo\t\t\\\n\t; do\n\t\tif [ -d \"$d\" ]; then\n\t\t\tzoneinfo_dir=\"$d\"\n\t\t\tbreak\n\t\tfi\n\tdone\n\n\tif [ -z \"$zoneinfo_dir\" ]; then\n\t\tsyslog warning \"timezone directory not found\"\n\t\treturn 1\n\tfi\n\n\tzone_file=\"$zoneinfo_dir/$new_tzdb_timezone\"\n\tif [ ! -e \"$zone_file\" ]; then\n\t\tsyslog warning \"no timezone definition for $new_tzdb_timezone\"\n\t\treturn 1\n\tfi\n\n\tif copy_file \"$zone_file\" \"$localtime\"; then\n\t\tsyslog info \"timezone changed to $new_tzdb_timezone\"\n\tfi\n}\n\n# For ease of use, map DHCP6 names onto our DHCP4 names\ncase \"$reason\" in\nBOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)\n\tnew_tzdb_timezone=\"$new_dhcp6_tzdb_timezone\"\n\t;;\nesac\n\nif $if_configured && $if_up; then\n \tset_zoneinfo\nfi\n"
  },
  {
    "path": "hooks/20-resolv.conf",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2017-2025 Roy Marples <roy@marples.name>\n\n# Generate /etc/resolv.conf\n# Support resolvconf(8) if available\n# We can merge other dhcpcd resolv.conf files into one like resolvconf,\n# but resolvconf is preferred as other applications like VPN clients\n# can readily hook into it.\n# Also, resolvconf can configure local nameservers such as bind\n# or dnsmasq. This is important as the libc resolver isn't that powerful.\n\nresolv_conf_dir=\"$state_dir/resolv.conf\"\nnocarrier_roaming_dir=\"$state_dir/roaming\"\nNL=\"\n\"\n: ${resolvconf:=resolvconf}\nif command -v \"$resolvconf\" >/dev/null 2>&1; then\n\thave_resolvconf=true\nelse\n\thave_resolvconf=false\nfi\n\nbuild_resolv_conf()\n{\n\tcf=\"$state_dir/resolv.conf.$ifname\"\n\n\t# Build a list of interfaces\n\tinterfaces=$(list_interfaces \"$resolv_conf_dir\")\n\n\t# Build the resolv.conf\n\theader=\n\tif [ -n \"$interfaces\" ]; then\n\t\t# Build the header\n\t\tfor x in ${interfaces}; do\n\t\t\theader=\"$header${header:+, }$x\"\n\t\tdone\n\n\t\t# Build the search list\n\t\tdomain=$(cd \"$resolv_conf_dir\"; \\\n\t\t\tkey_get_value \"domain \" ${interfaces})\n\t\tsearch=$(cd \"$resolv_conf_dir\"; \\\n\t\t\tkey_get_value \"search \" ${interfaces})\n\t\tset -- ${domain}\n\t\tdomain=\"$1\"\n\t\t[ -n \"$2\" ] && search=\"$search $*\"\n\t\t[ -n \"$search\" ] && search=\"$(uniqify $search)\"\n\t\t[ \"$domain\" = \"$search\" ] && search=\n\t\t[ -n \"$domain\" ] && domain=\"domain $domain$NL\"\n\t\t[ -n \"$search\" ] && search=\"search $search$NL\"\n\n\t\t# Build the nameserver list\n\t\tsrvs=$(cd \"$resolv_conf_dir\"; \\\n\t\t\tkey_get_value \"nameserver \" ${interfaces})\n\t\tfor x in $(uniqify $srvs); do\n\t\t\tservers=\"${servers}nameserver $x$NL\"\n\t\tdone\n\tfi\n\theader=\"$signature_base${header:+ $from }$header\"\n\n\t# Assemble resolv.conf using our head and tail files\n\t[ -f \"$cf\" ] && rm -f \"$cf\"\n\t[ -d \"$resolv_conf_dir\" ] || mkdir -p \"$resolv_conf_dir\"\n\tprintf '%s\\n' \"$header\" > \"$cf\"\n\tif [ -f /etc/resolv.conf.head ]; then\n\t\tcat /etc/resolv.conf.head >> \"$cf\"\n\telse\n\t\tprintf '%s\\n' \"# /etc/resolv.conf.head can replace this line\" >> \"$cf\"\n\tfi\n\tprintf %s \"$domain$search$servers\" >> \"$cf\"\n\tif [ -f /etc/resolv.conf.tail ]; then\n\t\tcat /etc/resolv.conf.tail >> \"$cf\"\n\telse\n\t\tprintf '%s\\n' \"# /etc/resolv.conf.tail can replace this line\" >> \"$cf\"\n\tfi\n\tif change_file /etc/resolv.conf \"$cf\"; then\n\t\tchmod 644 /etc/resolv.conf\n\tfi\n\trm -f \"$cf\"\n}\n\n# Extract any ND DNS options from the RA\n# Obey the lifetimes\neval_nd_dns()\n{\n\n\teval rdnsstime=\\$nd${i}_rdnss${j}_lifetime\n\tif [ -n \"$rdnsstime\" ]; then\n\t\tltime=$(($rdnsstime - $offset))\n\t\tif [ \"$ltime\" -gt 0 ]; then\n\t\t\teval rdnss=\\$nd${i}_rdnss${j}_servers\n\t\t\tif [ -n \"$rdnss\" ]; then\n\t\t\t\tnew_rdnss=\"$new_rdnss${new_rdnss:+ }$rdnss\"\n\t\t\tfi\n\t\tfi\n\tfi\n\n\teval dnssltime=\\$nd${i}_dnssl${j}_lifetime\n\tif [ -n \"$dnssltime\" ]; then\n\t\tltime=$(($dnssltime - $offset))\n\t\tif [ \"$ltime\" -gt 0 ]; then\n\t\t\teval dnssl=\\$nd${i}_dnssl${j}_search\n\t\t\tif [ -n \"$dnssl\" ]; then\n\t\t\t\tnew_dnssl=\"$new_dnssl${new_dnssl:+ }$dnssl\"\n\t\t\tfi\n\t\tfi\n\tfi\n\n\t# Break when we don't have either\n\t[ -z \"$rdnsstime\" ] && [ -z \"$dnssltime\" ] && return 1\n\n\tj=$(($j + 1))\n\treturn 0\n}\n\nadd_resolv_conf()\n{\n\tconf=\"$signature$NL\"\n\twarn=true\n\n\t# Loop to extract the ND DNS options using our indexed shell values\n\ti=1\n\tj=1\n\twhile true; do\n\t\teval acquired=\\$nd${i}_acquired\n\t\t[ -z \"$acquired\" ] && break\n\t\teval now=\\$nd${i}_now\n\t\t[ -z \"$now\" ] && break\n\t\toffset=$(($now - $acquired))\n\t\twhile true; do\n\t\t\teval_nd_dns || break\n\t\tdone\n\t\ti=$(($i + 1))\n\t\tj=1\n\tdone\n\t[ -n \"$new_rdnss\" ] && \\\n\t    new_domain_name_servers=\"$new_domain_name_servers${new_domain_name_servers:+ }$new_rdnss\"\n\t[ -n \"$new_dnssl\" ] && \\\n\t    new_domain_search=\"$new_domain_search${new_domain_search:+ }$new_dnssl\"\n\n\t# Derive a new domain from our various hostname options\n\tif [ -z \"$new_domain_name\" ]; then\n\t\tif [ \"$new_dhcp6_fqdn\" != \"${new_dhcp6_fqdn#*.}\" ]; then\n\t\t\tnew_domain_name=\"${new_dhcp6_fqdn#*.}\"\n\t\telif [ \"$new_fqdn\" != \"${new_fqdn#*.}\" ]; then\n\t\t\tnew_domain_name=\"${new_fqdn#*.}\"\n\t\telif [ \"$new_host_name\" != \"${new_host_name#*.}\" ]; then\n\t\t\tnew_domain_name=\"${new_host_name#*.}\"\n\t\tfi\n\tfi\n\n\t# If we don't have any configuration, remove it\n\tif [ -z \"$new_domain_name_servers\" ] &&\n\t   [ -z \"$new_domain_name\" ] &&\n\t   [ -z \"$new_domain_search\" ]; then\n\t\tremove_resolv_conf\n\t\treturn $?\n\tfi\n\n\tif [ -n \"$new_domain_name\" ]; then\n\t\tset -- $new_domain_name\n\t\tif valid_domainname \"$1\"; then\n\t\t\tconf=\"${conf}domain $1$NL\"\n\t\telse\n\t\t\tsyslog err \"Invalid domain name: $1\"\n\t\tfi\n\t\t# If there is no search this, make this one\n\t\tif [ -z \"$new_domain_search\" ]; then\n\t\t\tnew_domain_search=\"$new_domain_name\"\n\t\t\t[ \"$new_domain_name\" = \"$1\" ] && warn=true\n\t\tfi\n\tfi\n\tif [ -n \"$new_domain_search\" ]; then\n\t\tnew_domain_search=$(uniqify $new_domain_search)\n\t\tif valid_domainname_list $new_domain_search; then\n\t\t\tconf=\"${conf}search $new_domain_search$NL\"\n\t\telif ! $warn; then\n\t\t\tsyslog err \"Invalid domain name in list:\" \\\n\t\t\t    \"$new_domain_search\"\n\t\tfi\n\tfi\n\tnew_domain_name_servers=$(uniqify $new_domain_name_servers)\n\tfor x in ${new_domain_name_servers}; do\n\t\tconf=\"${conf}nameserver $x$NL\"\n\tdone\n\tif $have_resolvconf; then\n\t\t[ -n \"$ifmetric\" ] && export IF_METRIC=\"$ifmetric\"\n\t\tprintf %s \"$conf\" | \"$resolvconf\" -a \"$ifname\"\n\t\treturn $?\n\tfi\n\n\tif [ -e \"$resolv_conf_dir/$ifname\" ]; then\n\t\trm -f \"$resolv_conf_dir/$ifname\"\n\tfi\n\t[ -d \"$resolv_conf_dir\" ] || mkdir -p \"$resolv_conf_dir\"\n\tprintf %s \"$conf\" > \"$resolv_conf_dir/$ifname\"\n\tbuild_resolv_conf\n}\n\nremove_resolv_conf()\n{\n\tif $have_resolvconf; then\n\t\t\"$resolvconf\" -d \"$ifname\" -f\n\telse\n\t\tif [ -e \"$resolv_conf_dir/$ifname\" ]; then\n\t\t\trm -f \"$resolv_conf_dir/$ifname\"\n\t\tfi\n\t\tbuild_resolv_conf\n\tfi\n}\n\n# For ease of use, map DHCP6 names onto our DHCP4 names\ncase \"$reason\" in\nBOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)\n\tnew_domain_name_servers=\"$new_dhcp6_name_servers\"\n\tnew_domain_search=\"$new_dhcp6_domain_search\"\n\t;;\nesac\n\nif $if_configured; then\n\tif $have_resolvconf && [ \"$reason\" = NOCARRIER_ROAMING ]; then\n\t\t# avoid calling resolvconf -c on CARRIER unless we roam\n\t\tmkdir -p \"$nocarrier_roaming_dir\"\n\t\tprintf ' \\n' >\"$nocarrier_roaming_dir/$interface\"\n\t\t\"$resolvconf\" -C \"$interface.*\"\n\telif $have_resolvconf && [ \"$reason\" = CARRIER ]; then\n\t\t# Not all resolvconf implementations support -c\n\t\tif [ -e \"$nocarrier_roaming_dir/$interface\" ]; then\n\t\t\trm -f \"$nocarrier_roaming_dir/$interface\"\n\t\t\t\"$resolvconf\" -c \"$interface.*\"\n\t\tfi\n\telif $if_up || [ \"$reason\" = ROUTERADVERT ]; then\n\t\tadd_resolv_conf\n\telif $if_down; then\n\t\tremove_resolv_conf\n\tfi\nfi\n"
  },
  {
    "path": "hooks/29-lookup-hostname",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2017-2022 Roy Marples <roy@marples.name>\n\n# Lookup the hostname in DNS if not set\n\nlookup_hostname()\n{\n\t[ -z \"$new_ip_address\" ] && return 1\n\t# Silly ISC programs love to send error text to stdout\n\tif command -v dig >/dev/null 2>&1; then\n\t\th=$(dig +short -x $new_ip_address)\n\t\tif [ $? = 0 ]; then\n\t\t\tprintf '%s\\n' \"$h\" | sed 's/\\.$//'\n\t\t\treturn 0\n\t\tfi\n\telif command -v host >/dev/null 2>&1; then\n\t\th=$(host $new_ip_address)\n\t\tif [ $? = 0 ]; then \n\t\t\tprintf '%s\\n' \"$h\" \\\n\t\t\t| sed 's/.* domain name pointer \\(.*\\)./\\1/'\n\t\t\treturn 0\n\t\tfi\n\telif command -v getent >/dev/null 2>&1; then\n\t\th=$(getent hosts $new_ip_address)\n\t\tif [ $? = 0 ]; then\n\t\t\tprintf '%s\\n' \"$h\" | sed 's/[^ ]* *\\([^ ]*\\).*/\\1/'\n\t\t\treturn 0\n\t\t fi\n\tfi\n\treturn 1\n}\n\nset_hostname()\n{\n\tif [ -z \"${new_host_name}${new_fqdn_name}\" ]; then\n\t\texport new_host_name=\"$(lookup_hostname)\"\n\tfi\n}\n\nif $if_up; then\n\tset_hostname\nfi\n"
  },
  {
    "path": "hooks/30-hostname.in",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2020-2024 Roy Marples <roy@marples.name>\n\n# Set the hostname from DHCP data if required\n\n# A hostname can either be a short hostname or a FQDN.\n# hostname_fqdn=true\n# hostname_fqdn=false\n# hostname_fqdn=server\n\n# A value of server means just what the server says, don't manipulate it.\n# This could lead to an inconsistent hostname on a DHCPv4 and DHCPv6 network\n# where the DHCPv4 hostname is short and the DHCPv6 has an FQDN.\n# DHCPv6 has no hostname option.\n# RFC4702 section 3.1 says FQDN should be prefered over hostname.\n#\n# As such, the default is hostname_fqdn=true so that a consistent hostname\n# is always assigned.\n: ${hostname_fqdn:=true}\n\n# If we used to set the hostname, but relinquish control of it, we should\n# reset to the default value.\n: ${hostname_default=@DEFAULT_HOSTNAME@}\n\n# Some systems don't have hostname(1)\n_hostname()\n{\n\tif [ -z \"${1+x}\" ]; then\n\t\tif [ -r /proc/sys/kernel/hostname ]; then\n\t\t\tread name </proc/sys/kernel/hostname && printf '%s\\n' \"$name\"\n\t\telif command -v hostname >/dev/null 2>/dev/null; then\n\t\t\thostname\n\t\telif sysctl kern.hostname >/dev/null 2>&1; then\n\t\t\tsysctl -n kern.hostname\n\t\telif sysctl kernel.hostname >/dev/null 2>&1; then\n\t\t\tsysctl -n kernel.hostname\n\t\telse\n\t\t\treturn 1\n\t\tfi\n\t\treturn $?\n\tfi\n\n\tif [ -w /proc/sys/kernel/hostname ]; then\n\t\tprintf '%s\\n' \"$1\" >/proc/sys/kernel/hostname\n\telif [ -n \"$1\" ] && command -v hostname >/dev/null 2>&1; then\n\t\thostname \"$1\"\n\telif sysctl kern.hostname >/dev/null 2>&1; then\n\t\tsysctl -w \"kern.hostname=$1\" >/dev/null\n\telif sysctl kernel.hostname >/dev/null 2>&1; then\n\t\tsysctl -w \"kernel.hostname=$1\" >/dev/null\n\telse\n\t\t# May fail to set a blank hostname\n\t\thostname \"$1\"\n\tfi\n}\n\nis_default_hostname()\n{\n\tcase \"$1\" in\n\t\"\"|\"$hostname_default\"|localhost|localhost.localdomain)\n\t\treturn 0;;\n\tesac\n\treturn 1\n}\n\nneed_hostname()\n{\n\t# Always load the hostname variable for future use\n\thostname=\"$(_hostname)\"\n\tis_default_hostname \"$hostname\" && return 0\n\n\tcase \"$force_hostname\" in\n\t[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|1) return 0;;\n\tesac\n\n\tif [ -n \"$old_fqdn\" ]; then\n\t\tif ${hfqdn} || ! ${hshort}; then\n\t\t\t[ \"$hostname\" = \"$old_fqdn\" ]\n\t\telse\n\t\t\t[ \"$hostname\" = \"${old_fqdn%%.*}\" ]\n\t\tfi\n\telif [ -n \"$old_host_name\" ]; then\n\t\tif ${hfqdn}; then\n\t\t\tif [ -n \"$old_domain_name\" ] &&\n\t\t\t   [ \"$old_host_name\" = \"${old_host_name#*.}\" ]\n\t\t\tthen\n\t\t\t\t[ \"$hostname\" = \\\n\t\t\t\t    \"$old_host_name.$old_domain_name\" ]\n\t\t\telse\n\t\t\t\t[ \"$hostname\" = \"$old_host_name\" ]\n\t\t\tfi\n\t\telif ${hshort}; then\n\t\t\t[ \"$hostname\" = \"${old_host_name%%.*}\" ]\n\t\telse\n\t\t\t[ \"$hostname\" = \"$old_host_name\" ]\n\t\tfi\n\telse\n\t\t# No old hostname\n\t\tfalse\n\tfi\n}\n\ntry_hostname()\n{\n\t[ \"$hostname\" = \"$1\" ] && return 0\n\tif valid_domainname \"$1\"; then\n\t\tsyslog info \"Setting hostname: $1\"\n\t\t_hostname \"$1\"\n\telse\n\t\tsyslog err \"Invalid hostname: $1\"\n\tfi\n}\n\nset_hostname()\n{\n\thfqdn=false\n\thshort=false\n\tcase \"$hostname_fqdn\" in\n\t[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|1)\thfqdn=true;;\n\t\"\"|[Ss][Ee][Rr][Vv][Ee][Rr])\t\t;;\n\t*)\t\t\t\t\thshort=true;;\n\tesac\n\n\tneed_hostname || return 0\n\n\tif [ -n \"$new_fqdn\" ]; then\n\t\tif ${hfqdn} || ! ${hshort}; then\n\t\t\ttry_hostname \"$new_fqdn\"\n\t\telse\n\t\t\ttry_hostname \"${new_fqdn%%.*}\"\n\t\tfi\n\telif [ -n \"$new_host_name\" ]; then\n\t\tif ${hfqdn}; then\n\t\t\tif [ -n \"$new_domain_name\" ] &&\n\t\t\t   [ \"$new_host_name\" = \"${new_host_name#*.}\" ]\n\t\t\tthen\n\t\t\t\ttry_hostname \"$new_host_name.$new_domain_name\"\n\t\t\telse\n\t\t\t\ttry_hostname \"$new_host_name\"\n\t\t\tfi\n\t\telif ${hshort}; then\n\t\t\ttry_hostname \"${new_host_name%%.*}\"\n\t\telse\n\t\t\ttry_hostname \"$new_host_name\"\n\t\tfi\n\telif ! is_default_hostname \"$hostname\"; then\n\t\ttry_hostname \"$hostname_default\"\n\tfi\n}\n\n# For ease of use, map DHCP6 names onto our DHCP4 names\ncase \"$reason\" in\nBOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)\n\tnew_fqdn=\"$new_dhcp6_fqdn\"\n\told_fqdn=\"$old_dhcp6_fqdn\"\n\t;;\nesac\n\nif $if_configured && $if_up && [ \"$reason\" != ROUTERADVERT ]; then\n\tset_hostname\nfi\n"
  },
  {
    "path": "hooks/50-dhcpcd-compat",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2017 Roy Marples <roy@marples.name>\n\n# Compat enter hook shim for older dhcpcd versions\n\nIPADDR=$new_ip_address\nINTERFACE=$interface\nNETMASK=$new_subnet_mask\nBROADCAST=$new_broadcast_address\nNETWORK=$new_network_number\nDHCPSID=$new_dhcp_server_identifier\nGATEWAYS=$new_routers\nDNSSERVERS=$new_domain_name_servers\nDNSDOMAIN=$new_domain_name\nDNSSEARCH=$new_domain_search\nNISDOMAIN=$new_nis_domain\nNISSERVERS=$new_nis_servers\nNTPSERVERS=$new_ntp_servers\n\nGATEWAY=\nfor x in $new_routers; do\n\tGATEWAY=\"$GATEWAY${GATEWAY:+,}$x\"\ndone\nDNS=\nfor x in $new_domain_name_servers; do\n\tDNS=\"$DNS${DNS:+,}$x\"\ndone\n\nr=\"down\"\ncase \"$reason\" in\nRENEW) r=\"up\";;\nBOUND|INFORM|REBIND|REBOOT|TEST|TIMEOUT|IPV4LL) r=\"new\";;\nesac\n\nif [ \"$r\" != \"down\" ]; then\n\trm -f /var/lib/dhcpcd-\"$INTERFACE\".info\n\tfor x in IPADDR INTERFACE NETMASK BROADCAST NETWORK DHCPSID GATEWAYS \\\n\t\tDNSSERVERS DNSDOMAIN DNSSEARCH NISDOMAIN NISSERVERS \\\n\t\tNTPSERVERS GATEWAY DNS; do\n\t\teval printf '%s\\\\n' \"$x=\\'\\$$x\\'\" >> /var/lib/dhcpcd-\"$INTERFACE\".info\n\tdone\nfi\n\nset -- /var/lib/dhcpcd-\"$INTERFACE\".info \"$r\"\n"
  },
  {
    "path": "hooks/50-ntp.conf",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2017-2026 Roy Marples <roy@marples.name>\n\n# Sample dhcpcd hook script for NTP\n# It will configure either one of NTP, OpenNTP or Chrony (in that order)\n# and will default to NTP if no default config is found.\n\n# Like our resolv.conf hook script, we store a database of ntp.conf files\n# and merge into /etc/ntp.conf\n\n# You can set the env var NTP_CONF to override the derived default on\n# systems with >1 NTP client installed.\n# Here is an example for OpenNTP\n#   dhcpcd -e NTP_CONF=/usr/pkg/etc/ntpd.conf\n# or by adding this to /etc/dhcpcd.conf\n#   env NTP_CONF=/usr/pkg/etc/ntpd.conf\n# or by adding this to /etc/dhcpcd.enter-hook\n#   NTP_CONF=/usr/pkg/etc/ntpd.conf\n# To use Chrony instead, simply change ntpd.conf to chrony.conf in the\n# above examples.\n\n: ${ntp_confs:=ntp.conf ntpd.conf chrony.conf}\n: ${ntp_conf_dirs=/etc /usr/pkg/etc /usr/local/etc}\nntp_conf_dir=\"$state_dir/ntp.conf\"\n\n# If NTP_CONF is not set, work out a good default\nif [ -z \"$NTP_CONF\" ]; then\n\tfor d in ${ntp_conf_dirs}; do\n\t\tfor f in ${ntp_confs}; do\n\t\t\tif [ -e \"$d/$f\" ]; then\n\t\t\t\tNTP_CONF=\"$d/$f\"\n\t\t\t\tbreak 2\n\t\t\tfi\n\t\tdone\n\tdone\n\t[ -e \"$NTP_CONF\" ] || NTP_CONF=/etc/ntp.conf\nfi\n\n# Derive service name from configuration\nif [ -z \"$ntp_service\" ]; then\n\tcase \"$NTP_CONF\" in\n\t*chrony.conf)\t\tntp_service=chronyd;;\n\t*)\t\t\tntp_service=ntpd;;\n\tesac\nfi\n\n# Debian has a separate file for DHCP config to avoid stamping on\n# the master.\nif [ \"$ntp_service\" = ntpd ] && command -v invoke-rc.d >/dev/null 2>&1; then\n\t[ -e /var/lib/ntp ] || mkdir /var/lib/ntp\n\t: ${ntp_service:=ntp}\n\t: ${NTP_DHCP_CONF:=/var/lib/ntp/ntp.conf.dhcp}\nfi\n\n: ${ntp_restart_cmd:=service_condcommand $ntp_service restart}\n\nntp_conf=${NTP_CONF}\nNL=\"\n\"\n\nbuild_ntp_conf()\n{\n\tcf=\"$state_dir/ntp.conf.$ifname\"\n\n\t# Build a list of interfaces\n\tinterfaces=$(list_interfaces \"$ntp_conf_dir\")\n\n\theader=\n\tservers=\n\tif [ -n \"$interfaces\" ]; then\n\t\t# Build the header\n\t\tfor x in ${interfaces}; do\n\t\t\theader=\"$header${header:+, }$x\"\n\t\tdone\n\n\t\t# Build a server list\n\t\tsrvs=$(cd \"$ntp_conf_dir\";\n\t\t\tkey_get_value \"server \" $interfaces)\n\t\tif [ -n \"$srvs\" ]; then\n\t\t\tfor x in $(uniqify $srvs); do\n\t\t\t\tservers=\"${servers}server $x$NL\"\n\t\t\tdone\n\t\tfi\n\tfi\n\n\t# Merge our config into ntp.conf\n\t[ -e \"$cf\" ] && rm -f \"$cf\"\n\t[ -d \"$ntp_conf_dir\" ] || mkdir -p \"$ntp_conf_dir\"\n\n\tif [ -n \"$NTP_DHCP_CONF\" ]; then\n\t\t[ -e \"$ntp_conf\" ] && cp \"$ntp_conf\" \"$cf\"\n\t\tntp_conf=\"$NTP_DHCP_CONF\"\n\telif [ -e \"$ntp_conf\" ]; then\n\t\tremove_markers \"$signature_base\" \"$signature_base_end\" \\\n\t\t\t\"$ntp_conf\" > \"$cf\"\n\tfi\n\n\tif [ -n \"$servers\" ]; then\n\t\tprintf '%s\\n' \"$signature_base${header:+ $from }$header\" >> \"$cf\"\n\t\tprintf '%s' \"$servers\" >> \"$cf\"\n\t\tprintf '%s\\n' \"$signature_base_end${header:+ $from }$header\" >> \"$cf\"\n\telse\n\t\t[ -e \"$ntp_conf\" ] && [ -e \"$cf\" ] || return\n\tfi\n\n\t# If we changed anything, restart ntpd\n\tif change_file \"$ntp_conf\" \"$cf\"; then\n\t\t[ -n \"$ntp_restart_cmd\" ] && eval $ntp_restart_cmd\n\tfi\n}\n\nadd_ntp_conf()\n{\n\tcf=\"$ntp_conf_dir/$ifname\"\n\n\t[ -e \"$cf\" ] && rm \"$cf\"\n\t[ -d \"$ntp_conf_dir\" ] || mkdir -p \"$ntp_conf_dir\"\n\tif [ -n \"$new_ntp_servers\" ]; then\n\t\tfor x in $(uniqify $new_ntp_servers); do\n\t\t\tprintf '%s\\n' \"server $x\" >> \"$cf\"\n\t\tdone\n\tfi\n\tbuild_ntp_conf\n}\n\nremove_ntp_conf()\n{\n\tif [ -e \"$ntp_conf_dir/$ifname\" ]; then\n\t\trm \"$ntp_conf_dir/$ifname\"\n\tfi\n\tbuild_ntp_conf\n}\n\n# For ease of use, map DHCP6 names onto our DHCP4 names\ncase \"$reason\" in\nBOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)\n\tnew_ntp_servers=\"$new_dhcp6_sntp_servers\"\n\tnew_ntp_servers=\"$new_ntp_servers${new_ntp_servers:+ }$new_dhcp6_ntp_server_addr\"\n\tnew_ntp_servers=\"$new_ntp_servers${new_ntp_servers:+ }$new_dhcp6_ntp_server_fqdn\"\n;;\nesac\n\nif $if_configured; then\n\tif $if_up; then\n\t\tadd_ntp_conf\n\telif $if_down; then\n\t\tremove_ntp_conf\n\tfi\nfi\n"
  },
  {
    "path": "hooks/50-timesyncd.conf",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2022-2026 Roy Marples <roy@marples.name>\n\nif [ ! -d /run/systemd/system ]; then\n\treturn\nfi\nif [ ! -x /lib/systemd/systemd-timesyncd ]; then\n\treturn\nfi\n\n: ${timesyncd_conf_d:=/run/systemd/timesyncd.conf.d}\ntimesyncd_conf=\"${timesyncd_conf_d}/dhcpcd-$ifname.conf\"\ntimesyncd_tmp_d=\"$state_dir/timesyncd\"\ntimesyncd_tmp=\"$timesyncd_tmp_d/$ifname\"\n\nNL=\"\n\"\n\nremove_timesyncd_conf()\n{\n\tif [ -e \"$timesyncd_conf\" ]; then\n\t\trm \"$timesyncd_conf\"\n\t\tsystemctl try-reload-or-restart --no-block systemd-timesyncd\n\tfi\n}\n\nadd_timesyncd_conf()\n{\n\tif [ -z \"$new_ntp_servers\" ]; then\n\t\tremove_timesyncd_conf\n\t\treturn $?\n\tfi\n\n\tmkdir -p \"$timesyncd_tmp_d\" \"$timesyncd_conf_d\"\n\n\tconf=\"$signature$NL\"\n\tconf=\"${conf}[Time]$NL\"\n\tconf=\"${conf}NTP=\"\n\t# Trim spaces\n\tspace=false\n\tfor ntp_server in $(uniqify $new_ntp_servers); do\n\t\tif ! $space; then\n\t\t\tspace=true\n\t\telse\n\t\t\tconf=\"$conf \"\n\t\tfi\n\t\tconf=\"$conf$ntp_server\"\n\tdone\n\tconf=\"$conf$NL\"\n\n\tprintf %s \"$conf\" > \"$timesyncd_tmp\"\n\tif change_file \"$timesyncd_conf\" \"$timesyncd_tmp\"; then\n\t\tsystemctl try-reload-or-restart --no-block systemd-timesyncd\n\tfi\n}\n\n# For ease of use, map DHCP6 names onto our DHCP4 names\ncase \"$reason\" in\nBOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)\n\tnew_ntp_servers=\"$new_dhcp6_sntp_servers\"\n\tnew_ntp_servers=\"$new_ntp_servers${new_ntp_servers:+ }$new_dhcp6_ntp_server_addr\"\n\tnew_ntp_servers=\"$new_ntp_servers${new_ntp_servers:+ }$new_dhcp6_ntp_server_fqdn\"\n;;\nesac\n\nif $if_configured; then\n\tif $if_up; then\n\t\tadd_timesyncd_conf\n\telif $if_down; then\n\t\tremove_timesyncd_conf\n\tfi\nfi\n"
  },
  {
    "path": "hooks/50-yp.conf",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2017-2020 Roy Marples <roy@marples.name>\n\n# Sample dhcpcd hook for ypbind\n# This script is only suitable for the Linux version.\n\nypbind_pid()\n{\n\t[ -s /var/run/ypbind.pid ] && cat /var/run/ypbind.pid\n}\n\nmake_yp_conf()\n{\n\t[ -z \"${new_nis_domain}${new_nis_servers}\" ] && return 0\n\tcf=/etc/yp.conf.\"$ifname\"\n\trm -f \"$cf\"\n\tprintf '%s\\n' \"$signature\" > \"$cf\"\n\tprefix=\n\tif [ -n \"$new_nis_domain\" ]; then\n\t\tif ! valid_domainname \"$new_nis_domain\"; then\n\t\t\tsyslog err \"Invalid NIS domain name: $new_nis_domain\"\n\t\t\trm -f \"$cf\"\n\t\t\treturn 1\n\t\tfi\n\t\tdomainname \"$new_nis_domain\"\n\t\tif [ -n \"$new_nis_servers\" ]; then\n\t\t\tprefix=\"domain $new_nis_domain server \"\n\t\telse\n\t\t\tprintf '%s\\n' \"domain $new_nis_domain broadcast\" >> \"$cf\"\n\t\tfi\n\telse\n\t\tprefix=\"ypserver \"\n\tfi\n\tfor x in $new_nis_servers; do\n\t\tprintf '%s\\n' \"$prefix$x\" >> \"$cf\"\n\tdone\n\tsave_conf /etc/yp.conf\n\tcat \"$cf\" > /etc/yp.conf\n\trm -f \"$cf\"\n\tpid=\"$(ypbind_pid)\"\n\tif [ -n \"$pid\" ]; then\n\t\tkill -HUP \"$pid\"\n\tfi\n}\n\nrestore_yp_conf()\n{\n\t[ -n \"$old_nis_domain\" ] && domainname \"\"\n\trestore_conf /etc/yp.conf || return 0\n\tpid=\"$(ypbind_pid)\"\n\tif [ -n \"$pid\" ]; then\n\t\tkill -HUP \"$pid\"\n\tfi\n}\n\nif $if_configured; then\n\tif $if_up; then\n\t\tmake_yp_conf\n\telif $if_down; then\n\t\trestore_yp_conf\n\tfi\nfi\n"
  },
  {
    "path": "hooks/50-ypbind.in",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2017-2021 Roy Marples <roy@marples.name>\n\n# Sample dhcpcd hook for ypbind\n# This script is only suitable for the BSD versions.\n\n: ${ypbind_restart_cmd:=service_command ypbind restart}\n: ${ypbind_stop_cmd:=service_condcommand ypbind stop}\nypbind_dir=\"$state_dir/ypbind\"\n: ${ypdomain_dir:=@YPDOMAIN_DIR@}\n: ${ypdomain_suffix:=@YPDOMAIN_SUFFIX@}\n\nbest_domain()\n{\n\tfor i in \"$ypbind_dir/$interface_order\".*; do\n\t\tif [ -f \"$i\" ]; then\n\t\t\tcat \"$i\"\n\t\t\treturn 0\n\t\tfi\n\tdone\n\treturn 1\n}\n\nmake_yp_binding()\n{\n\t[ -d \"$ypbind_dir\" ] || mkdir -p \"$ypbind_dir\"\n\tprintf '%s\\n' \"$new_nis_domain\" >\"$ypbind_dir/$ifname\"\n\n\tif [ -z \"$ypdomain_dir\" ]; then\n\t\tfalse\n\telse\n\t\tcf=\"$ypdomain_dir/$new_nis_domain$ypdomain_suffix\"\n\t\tif [ -n \"$new_nis_servers\" ]; then\n\t\t\tncf=\"$cf.$ifname\"\n\t\t\trm -f \"$ncf\"\n\t\t\tfor x in $new_nis_servers; do\n\t\t\t\tprintf '%s\\n' \"$x\" >>\"$ncf\"\n\t\t\tdone\n\t\t\tchange_file \"$cf\" \"$ncf\"\n\t\telse\n\t\t\t[ -e \"$cf\" ] && rm \"$cf\"\n\t\tfi\n\tfi\n\n\tnd=\"$(best_domain)\"\n\tif [ $? = 0 ] && [ \"$nd\" != \"$(domainname)\" ]; then\n\t\tdomainname \"$nd\"\n\t\tif [ -n \"$ypbind_restart_cmd\" ]; then\n\t\t\teval $ypbind_restart_cmd\n\t\tfi\n\tfi\n}\n\nrestore_yp_binding()\n{\n\trm -f \"$ypbind_dir/$ifname\"\n\tnd=\"$(best_domain)\"\n\t# We need to stop ypbind if there is no best domain\n\t# otherwise it will just stall as we cannot set domainname\n\t# to blank :/\n\tif [ -z \"$nd\" ]; then\n\t\tif [ -n \"$ypbind_stop_cmd\" ]; then\n\t\t\teval $ypbind_stop_cmd\n\t\tfi\n\telif [ \"$nd\" != \"$(domainname)\" ]; then\n\t\tdomainname \"$nd\"\n\t\tif [ -n \"$ypbind_restart_cmd\" ]; then\n\t\t\teval $ypbind_restart_cmd\n\t\tfi\n\tfi\n}\n\nif ! $if_configured; then\n\t;\nelif [ \"$reason\" = PREINIT ]; then\n\trm -f \"$ypbind_dir/$interface\".*\nelif $if_up || $if_down; then\n\tif [ -n \"$new_nis_domain\" ]; then\n\t\tif valid_domainname \"$new_nis_domain\"; then\n\t\t\tmake_yp_binding\n\t\telse\n\t\t\tsyslog err \"Invalid NIS domain name: $new_nis_domain\"\n\t\tfi\n\telif [ -n \"$old_nis_domain\" ]; then\n\t\trestore_yp_binding\n\tfi\nfi\n"
  },
  {
    "path": "hooks/Makefile",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2017-2023 Roy Marples <roy@marples.name>\n\nTOP=\t../\ninclude ${TOP}/iconfig.mk\n\nPROG=\t\tdhcpcd-run-hooks\nBINDIR=\t\t${LIBEXECDIR}\nCLEANFILES=\tdhcpcd-run-hooks\nMAN8=\t\tdhcpcd-run-hooks.8\nCLEANFILES+=\tdhcpcd-run-hooks.8\n\nSCRIPTSDIR=\t${HOOKDIR}\nSCRIPTS=\t01-test\nSCRIPTS+=\t20-resolv.conf\nSCRIPTS+=\t30-hostname\nSCRIPTS+=\t${HOOKSCRIPTS}\nCLEANFILES+=\t30-hostname\n\n# Some hooks should not be installed by default\nFILESDIR=\t${DATADIR}/dhcpcd/hooks\nFILES=\t\t10-wpa_supplicant\nFILES+=\t\t15-timezone\nFILES+=\t\t29-lookup-hostname\nFILES+=\t\t${EGHOOKSCRIPTS}\n\n.SUFFIXES:\t.in\n\n.in:\n\t${SED} ${SED_RUNDIR} ${SED_DBDIR} ${SED_LIBDIR} ${SED_HOOKDIR} \\\n\t\t${SED_SYS} ${SED_SCRIPT} ${SED_DATADIR} \\\n\t\t${SED_SERVICEEXISTS} ${SED_SERVICECMD} ${SED_SERVICESTATUS} \\\n\t\t${SED_STATUSARG} \\\n\t\t${SED_DEFAULT_HOSTNAME} \\\n\t\t-e 's:@YPDOMAIN_DIR@:${YPDOMAIN_DIR}:g' \\\n\t\t-e 's:@YPDOMAIN_SUFFIX@:${YPDOMAIN_SUFFIX}:g' \\\n\t\t$< > $@\n\nall: ${PROG} ${MAN8} ${SCRIPTS} ${EGHOOKSCRIPTS}\n\nclean:\n\trm -f ${CLEANFILES} 50-ypbind\n\ndistclean: clean\n\trm -f *.diff *.patch *.orig *.rej\n\ndepend:\n\nproginstall: ${PROG} ${SCRIPTS}\n\t${INSTALL} -d ${DESTDIR}${BINDIR}\n\t${INSTALL} -m ${BINMODE} ${PROG} ${DESTDIR}${BINDIR}\n\t${INSTALL} -d ${DESTDIR}${SCRIPTSDIR}\n\t${INSTALL} -m ${NONBINMODE} ${SCRIPTS} ${DESTDIR}${SCRIPTSDIR}\n\t# We need to remove the old MTU change script if we at all can.\n\trm -f ${DESTDIR}${SCRIPTSDIR}/10-mtu\n\neginstall: ${FILES}\n\t${INSTALL} -d ${DESTDIR}${FILESDIR}\n\t${INSTALL} -m ${NONBINMODE} ${FILES} ${DESTDIR}${FILESDIR}\n\nmaninstall: ${MAN8}\n\t${INSTALL} -d ${DESTDIR}${MANDIR}/man8\n\t${INSTALL} -m ${MANMODE} ${MAN8} ${DESTDIR}${MANDIR}/man8\n\ninstall: proginstall eginstall maninstall\n\nimport: ${SCRIPTS} ${FILES}\n\t${INSTALL} -d /tmp/${DISTPREFIX}/dhcpcd-hooks\n\t${INSTALL} -m ${NONBINMODE} ${SCRIPTS} /tmp/${DISTPREFIX}/dhcpcd-hooks\n\t${INSTALL} -m ${NONBINMODE} ${FILES} /tmp/${DISTPREFIX}/dhcpcd-hooks\n\n_import-src: all\n\t${INSTALL} -d ${DESTDIR}/hooks\n\t${INSTALL} -m ${NONBINMODE} ${PROG} ${MAN8} ${DESTDIR}/hooks\n\t${INSTALL} -m ${NONBINMODE} ${SCRIPTS} ${DESTDIR}/hooks\n\t${INSTALL} -m ${NONBINMODE} ${FILES} ${DESTDIR}/hooks\n\ninclude ${TOP}/Makefile.inc\n"
  },
  {
    "path": "hooks/dhcpcd-run-hooks.8.in",
    "content": ".\\\" SPDX-License-Identifier: BSD-2-Clause\n.\\\" Copyright (c) 2006-2025 Roy Marples\n.\\\" All rights reserved\n.\\\"\n.\\\" Redistribution and use in source and binary forms, with or without\n.\\\" modification, are permitted provided that the following conditions\n.\\\" are met:\n.\\\" 1. Redistributions of source code must retain the above copyright\n.\\\"    notice, this list of conditions and the following disclaimer.\n.\\\" 2. Redistributions in binary form must reproduce the above copyright\n.\\\"    notice, this list of conditions and the following disclaimer in the\n.\\\"    documentation and/or other materials provided with the distribution.\n.\\\"\n.\\\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n.\\\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n.\\\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n.\\\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n.\\\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n.\\\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n.\\\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n.\\\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n.\\\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n.\\\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n.\\\" SUCH DAMAGE.\n.\\\"\n.Dd October 6, 2025\n.Dt DHCPCD-RUN-HOOKS 8\n.Os\n.Sh NAME\n.Nm dhcpcd-run-hooks\n.Nd DHCP client configuration script\n.Sh DESCRIPTION\n.Nm\nis used by\n.Xr dhcpcd 8\nto run any system and user defined hook scripts.\nSystem hook scripts are found in\n.Pa @HOOKDIR@\nand the user defined hooks are\n.Pa @SYSCONFDIR@/dhcpcd.enter-hook .\nand\n.Pa @SYSCONFDIR@/dhcpcd.exit-hook .\nThe default install supplies hook scripts for configuring\n.Pa /etc/resolv.conf\nand the hostname.\nYour distribution may have included other hook scripts to say configure\nntp or ypbind.\nA test hook is also supplied that simply echos the dhcp variables to the\nconsole from DISCOVER message.\n.Pp\nThe hooks scripts are loaded into the current shell rather than executed\nin their own process.\nThis allows each hook script, such as\n.Pa @SYSCONFDIR@/dhcpcd.enter-hook\nto customise environment variables or provide alternative functions to hooks\nfurther down the chain.\nAs such, using the shell builtins\n.Ic exit ,\n.Ic exec\nor similar will cause\n.Nm\nto exit at that point.\n.Pp\nEach time\n.Nm\nis invoked,\n.Ev $interface\nis set to the interface that\n.Nm dhcpcd\nis run on and\n.Ev $reason\nis set to the reason why\n.Nm\nwas invoked.\nDHCP information to be configured is held in variables starting with the word\nnew_ and old DHCP information to be removed is held in variables starting with\nthe word old_.\n.Nm dhcpcd\ncan display the full list of variables it knows about by using the\n.Fl V , -variables\nargument.\n.Pp\nHere's a list of reasons why\n.Nm\ncould be invoked:\n.Bl -tag -width EXPIREXXXEXPIRE6\n.It Dv PREINIT\ndhcpcd is starting up and any pre-initialisation required should be performed now.\n.It Dv CARRIER\ndhcpcd has detected the carrier is up.\nThis is generally just a notification and no action need be taken.\n.It Dv NOCARRIER\ndhcpcd lost the carrier.\nThe cable may have been unplugged or association to the wireless point lost.\n.It Dv NOCARRIER_ROAMING\ndhcpcd lost the carrier but the interface configuration is persisted.\nThe OS has to support wireless roaming or IP Persistence for this to happen.\n.It Dv INFORM | Dv INFORM6\ndhcpcd informed a DHCP server about its address and obtained other\nconfiguration details.\n.It Dv BOUND | Dv BOUND6\ndhcpcd obtained a new lease from a DHCP server.\n.It Dv RENEW | Dv RENEW6\ndhcpcd renewed its lease.\n.It Dv REBIND | Dv REBIND6\ndhcpcd has rebound to a new DHCP server.\n.It Dv REBOOT | Dv REBOOT6\ndhcpcd successfully requested a lease from a DHCP server.\n.It Dv RELEASE | Dv RELEASE6\ndhcpcd has released the lease.\n.It Dv DELEGATED6\ndhcpcd assigned a delegated prefix to the interface.\n.It Dv IPV4LL\ndhcpcd obtained an IPV4LL address, or one was removed.\n.It Dv STATIC\ndhcpcd has been configured with a static configuration which has not been\nobtained from a DHCP server.\n.It Dv 3RDPARTY\ndhcpcd is monitoring the interface for a 3rd party to give it an IP address.\n.It Dv TIMEOUT | TIMEOUT6\ndhcpcd failed to contact any DHCP servers but was able to use an old lease.\n.It Dv EXPIRE | EXPIRE6\ndhcpcd's lease or state expired and it failed to obtain a new one.\n.It Dv NAK\ndhcpcd received a NAK from the DHCP server.\nThis should be treated as EXPIRE.\n.It Dv RECONFIGURE\ndhcpcd has been instructed to reconfigure an interface.\n.It Dv ROUTERADVERT\ndhcpcd has received an IPv6 Router Advertisement, or one has expired.\n.It Dv STOP | Dv STOP6\ndhcpcd stopped running on the interface.\n.It Dv STOPPED\ndhcpcd has stopped entirely.\n.It Dv DEPARTED\nThe interface has been removed.\n.It Dv FAIL\ndhcpcd failed to operate on the interface.\nThis normally happens when dhcpcd does not support the raw interface, which\nmeans it cannot work as a DHCP or ZeroConf client.\nStatic configuration and DHCP INFORM is still allowed.\n.It Dv TEST\ndhcpcd received an OFFER from a DHCP server but will not configure the\ninterface.\nThis is primarily used to test the variables are filled correctly for the\nscript to process them.\n.El\n.Sh ENVIRONMENT\n.Nm dhcpcd\nwill clear the environment variables aside from\n.Ev $PATH .\nThe following variables will then be set, along with any protocol supplied\nones.\n.Bl -tag -width xnew_delegated_dhcp6_prefix\n.It Ev $interface\nthe name of the interface.\n.It Ev $protocol\nthe protocol that triggered the event.\n.It Ev $reason\nas described above.\n.It Ev $pid\nthe pid of\n.Nm dhcpcd .\n.It Ev $ifcarrier\nthe link status of\n.Ev $interface :\n.Dv unknown ,\n.Dv up\nor\n.Dv down .\n.It Ev $ifmetric\n.Ev $interface\npreference, lower is better.\n.It Ev $ifwireless\n.Dv 1 if\n.Ev $interface\nis wireless, otherwise\n.Dv 0 .\n.It Ev $ifflags\n.Ev $interface\nflags.\n.It Ev $ifmtu\n.Ev $interface\nMTU.\n.It Ev $ifssid\nthe SSID the\n.Ev interface\nis connected to.\n.It Ev $interface_order\nA list of interfaces, in order of preference.\n.It Ev $if_configured\n.Dv true\nif dhcpcd has configured the\n.Ev interface\notherwise\n.Dv false .\n.It Ev $if_up\n.Dv true\nif the\n.Ev interface\nis up, otherwise\n.Dv false .\nThis is more than IFF_UP and may not be equal.\n.It Ev $if_down\n.Dv true\nif the\n.Ev interface\nis down, otherwise\n.Dv false .\nThis is more than IFF_UP and may not be equal.\n.It Ev $af_waiting\nAddress family waiting for, as defined in\n.Xr dhcpcd.conf 5 .\n.It Ev $profile\nthe name of the profile selected from\n.Xr dhcpcd.conf 5 .\n.It Ev $new_delegated_dhcp6_prefix\nspace-separated list of delegated prefixes.\n.El\n.Sh FILES\nWhen\n.Nm\nruns, it loads\n.Pa @SYSCONFDIR@/dhcpcd.enter-hook ,\nany scripts found in\n.Pa @HOOKDIR@\nin lexical order, then finally\n.Pa @SYSCONFDIR@/dhcpcd.exit-hook .\n.Sh SEE ALSO\n.Xr dhcpcd 8\n.Sh AUTHORS\n.An Roy Marples Aq Mt roy@marples.name\n.Sh BUGS\nPlease report them to\n.Lk https://roy.marples.name/projects/dhcpcd\n.Sh SECURITY CONSIDERATIONS\n.Nm dhcpcd\nwill validate the content of each option against its encoding.\nFor string, ascii, raw or binhex encoding it's up to the user to validate it\nfor the intended purpose.\n.Pp\nWhen used in a shell script, each variable must be quoted correctly.\n"
  },
  {
    "path": "hooks/dhcpcd-run-hooks.in",
    "content": "#!/bin/sh\n# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2017-2022 Roy Marples <roy@marples.name>\n\n# dhcpcd client configuration script \n\n# Handy variables and functions for our hooks to use\nifname=\"$interface${protocol+.}$protocol\"\nfrom=from\nsignature_base=\"# Generated by dhcpcd\"\nsignature=\"$signature_base $from $ifname\"\nsignature_base_end=\"# End of dhcpcd\"\nsignature_end=\"$signature_base_end $from $ifname\"\nstate_dir=@RUNDIR@/hook-state\n_detected_init=false\n\n: ${if_up:=false}\n: ${if_down:=false}\n: ${syslog_debug:=false}\n\n# Ensure that all arguments are unique\nuniqify()\n{\n\tresult=\n\tfor i do\n\t\tcase \" $result \" in\n\t\t\t*\" $i \"*);;\n\t\t\t*) result=\"$result${result:+ }$i\";;\n\t\tesac\n\tdone\n\tprintf '%s\\n' \"$result\"\n}\n\n# List interface config files in a directory.\n# If dhcpcd is running as a single instance then it will have a list of\n# interfaces in the preferred order.\n# Otherwise we just use what we have.\nlist_interfaces()\n{\n\tifaces=\n\tfor i in $interface_order; do\n\t\tfor x in \"$1\"/$i.*; do\n\t\t\t[ -f \"$x\" ] && ifaces=\"$ifaces${ifaces:+ }${x##*/}\"\n\t\tdone\n\tdone\n\tfor x in \"$1\"/*; do\n\t\t[ -f \"$x\" ] && ifaces=\"$ifaces${ifaces:+ }${x##*/}\"\n\tdone\n\tuniqify $ifaces\n}\n\n# Trim function\ntrim()\n{\n\tvar=\"$*\"\n\tvar=${var#\"${var%%[![:space:]]*}\"}\n\tvar=${var%\"${var##*[![:space:]]}\"}\n\tif [ -z \"$var\" ]; then\n\t\t# So it seems our shell doesn't support wctype(3) patterns\n\t\t# Fall back to sed\n\t\tvar=$(printf '%s\\n' \"$*\" | sed -e 's/^[[:space:]]*//;s/[[:space:]]*$//')\n\tfi\n\tprintf %s \"$var\"\n}\n\n# We normally use sed to extract values using a key from a list of files\n# but sed may not always be available at the time.\nkey_get_value()\n{\n\tkey=\"$1\"\n\tshift\n\n\tif command -v sed >/dev/null 2>&1; then\n\t\tsed -n \"s/^$key//p\" $@\n\telse\n\t\tfor x do\n\t\t\twhile read line; do\n\t\t\t\tcase \"$line\" in\n\t\t\t\t\"$key\"*) printf '%s\\n' \"${line##$key}\";;\n\t\t\t\tesac\n\t\t\tdone < \"$x\"\n\t\tdone\n\tfi\n}\n\n# We normally use sed to remove markers from a configuration file\n# but sed may not always be available at the time.\nremove_markers()\n{\n\tm1=\"$1\"\n\tm2=\"$2\"\n\tin_marker=0\n\n\tshift; shift\n\tif command -v sed >/dev/null 2>&1; then\n\t\tsed \"/^$m1/,/^$m2/d\" $@\n\telse\n\t\tfor x do\n\t\t\twhile read line; do\n\t\t\t\tcase \"$line\" in\n\t\t\t\t\"$m1\"*) in_marker=1;;\n\t\t\t\t\"$m2\"*) in_marker=0;;\n\t\t\t\t*) [ $in_marker = 0 ] && printf '%s\\n' \"$line\";;\n\t\t\t\tesac\n\t\t\tdone < \"$x\"\n\t\tdone\n\tfi\n}\n\n# Compare two files.\ncomp_file()\n{\n\t[ -e \"$1\" ] && [ -e \"$2\" ] || return 1\n\n\tif command -v cmp >/dev/null 2>&1; then\n\t\tcmp -s \"$1\" \"$2\"\n\telif command -v diff >/dev/null 2>&1; then\n\t\tdiff -q \"$1\" \"$2\" >/dev/null\n\telse\n\t\t# Hopefully we're only working on small text files ...\n\t\t[ \"$(cat \"$1\")\" = \"$(cat \"$2\")\" ]\n\tfi\n}\n\n# Compare two files.\n# If different, replace first with second otherwise remove second.\nchange_file()\n{\n\tif [ -e \"$1\" ]; then\n\t\tif comp_file \"$1\" \"$2\"; then\n\t\t\trm -f \"$2\"\n\t\t\treturn 1\n\t\tfi\n\tfi\n\tcat \"$2\" > \"$1\"\n\trm -f \"$2\"\n\treturn 0\n}\n\n# Compare two files.\n# If different, copy or link depending on target type\ncopy_file()\n{\n\tif [ -h \"$2\" ]; then\n\t\t[ \"$(readlink \"$2\")\" = \"$1\" ] && return 1\n\t\tln -sf \"$1\" \"$2\"\n\telse\n\t\tcomp_file \"$1\" \"$2\" && return 1\n\t\tcat \"$1\" >\"$2\"\n\tfi\n}\n\n# Save a config file\nsave_conf()\n{\n\tif [ -f \"$1\" ]; then\n\t\trm -f \"$1-pre.$interface\"\n\t\tcat \"$1\" > \"$1-pre.$interface\"\n\tfi\n}\n\n# Restore a config file\nrestore_conf()\n{\n\t[ -f \"$1-pre.$interface\" ] || return 1\n\tcat \"$1-pre.$interface\" > \"$1\"\n\trm -f \"$1-pre.$interface\"\n}\n\n# Write a syslog entry\nsyslog()\n{\n\tlvl=\"$1\"\n\n\tif [ \"$lvl\" = debug ]; then\n\t\t${syslog_debug} || return 0\n\tfi\n\t[ -n \"$lvl\" ] && shift\n\t[ -n \"$*\" ] || return 0\n\tcase \"$lvl\" in\n\terr|error)\tprintf '%s\\n' \"$interface: $*\" >&2;;\n\t*)\t\tprintf '%s\\n' \"$interface: $*\";;\n\tesac\n\tif command -v logger >/dev/null 2>&1; then\n\t\tlogger -i -p daemon.\"$lvl\" -t dhcpcd-run-hooks \"$interface: $*\"\n\tfi\n}\n\n# Check for a valid name as per RFC952 and RFC1123 section 2.1\nvalid_domainname()\n{\n\tname=\"$1\"\n\t[ -z \"$name\" ] || [ ${#name} -gt 255 ] && return 1\n\t\n\twhile [ -n \"$name\" ]; do\n\t\tlabel=\"${name%%.*}\"\n\t\t[ -z \"$label\" ] || [ ${#label} -gt 63 ] && return 1\n\t\tcase \"$label\" in\n\t\t-*|_*|*-|*_)\t\treturn 1;;\n\t\t*[![:alnum:]_-]*)\treturn 1;;\n\t\t\"$name\")\t\treturn 0;;\n\t\tesac\n\t\tname=\"${name#*.}\"\n\tdone\n\treturn 0\n}\n\nvalid_domainname_list()\n{\n\tfor name do\n\t\tvalid_domainname \"$name\" || return $?\n\tdone\n\treturn 0\n}\n\n# With the advent of alternative init systems, it's possible to have\n# more than one installed. So we need to try to guess what one we're\n# using unless overridden by configure.\ndetect_init()\n{\n\t_service_exists=\"@SERVICEEXISTS@\"\n\t_service_cmd=\"@SERVICECMD@\"\n\t_service_status=\"@SERVICESTATUS@\"\n\n\t[ -n \"$_service_cmd\" ] && return 0\n\n\tif $_detected_init; then\n\t\t[ -n \"$_service_cmd\" ]\n\t\treturn $?\n\tfi\n\n\t# Detect the running init system.\n\t# As systemd and OpenRC can be installed on top of legacy init\n\t# systems we try to detect them first.\n\tstatus=\"@STATUSARG@\"\n\t: ${status:=status}\n\tif [ -x /bin/systemctl ] && [ -S /run/systemd/private ]; then\n\t\t_service_exists=\"/bin/systemctl --quiet is-enabled \\$1.service\"\n\t\t_service_status=\"/bin/systemctl --quiet is-active \\$1.service\"\n\t\t_service_cmd=\"/bin/systemctl \\$2 --no-block \\$1.service\"\n\telif [ -x /usr/bin/systemctl ] && [ -S /run/systemd/private ]; then\n\t\t_service_exists=\"/usr/bin/systemctl --quiet is-enabled \\$1.service\"\n\t\t_service_status=\"/usr/bin/systemctl --quiet is-active \\$1.service\"\n\t\t_service_cmd=\"/usr/bin/systemctl \\$2 --no-block \\$1.service\"\n\telif [ -x /sbin/rc-service ] &&\n\t     { [ -s /libexec/rc/init.d/softlevel ] ||\n\t     [ -s /run/openrc/softlevel ]; }\n\tthen\n\t\t_service_exists=\"/sbin/rc-service -e \\$1\"\n\t\t_service_cmd=\"/sbin/rc-service \\$1 -- -D \\$2\"\n\telif [ -x /usr/sbin/invoke-rc.d ]; then\n\t\t_service_exists=\"/usr/sbin/invoke-rc.d --query --quiet \\$1 start >/dev/null 2>&1 || [ \\$? = 104 ]\"\n\t\t_service_cmd=\"/usr/sbin/invoke-rc.d \\$1 \\$2\"\n\telif [ -x /sbin/service ]; then\n\t\t_service_exists=\"/sbin/service \\$1 >/dev/null 2>&1\"\n\t\t_service_cmd=\"/sbin/service \\$1 \\$2\"\n\telif [ -x /usr/sbin/service ]; then\n\t\t_service_exists=\"/usr/sbin/service \\$1 $status >/dev/null 2>&1\"\n\t\t_service_cmd=\"/usr/sbin/service \\$1 \\$2\"\n\telif [ -x /bin/sv ]; then\n\t\t_service_exists=\"/bin/sv status \\$1 >/dev/null 2>&1\"\n\t\t_service_cmd=\"/bin/sv \\$2 \\$1\"\n\telif [ -x /usr/bin/sv ]; then\n\t\t_service_exists=\"/usr/bin/sv status \\$1 >/dev/null 2>&1\"\n\t\t_service_cmd=\"/usr/bin/sv \\$2 \\$1\"\n\telif [ -e /etc/slackware-version ] && [ -d /etc/rc.d ]; then\n\t\t_service_exists=\"[ -x /etc/rc.d/rc.\\$1 ]\"\n\t\t_service_cmd=\"/etc/rc.d/rc.\\$1 \\$2\"\n\t\t_service_status=\"/etc/rc.d/rc.\\$1 status >/dev/null 2>&1\"\n\telse\n\t\tfor x in /etc/init.d/rc.d /etc/rc.d /etc/init.d; do\n\t\t\tif [ -d $x ]; then\n\t\t\t\t_service_exists=\"[ -x $x/\\$1 ]\"\n\t\t\t\t_service_cmd=\"$x/\\$1 \\$2\"\n\t\t\t\t_service_status=\"$x/\\$1 $status >/dev/null 2>&1\"\n\t\t\t\tbreak\n\t\t\tfi\n\t\tdone\n\t\tif [ -e /etc/arch-release ]; then\n\t\t\t_service_status=\"[ -e /var/run/daemons/\\$1 ]\"\n\t\telif [ \"$x\" = \"/etc/rc.d\" ] && [ -e /etc/rc.d/rc.subr ]; then\n\t\t\t_service_status=\"$x/\\$1 check >/dev/null 2>&1\"\n\t\tfi\n\tfi\n\n\t_detected_init=true\n\tif [ -z \"$_service_cmd\" ]; then\n\t\tsyslog err \"could not detect a useable init system\"\n\t\treturn 1\n\tfi\n\treturn 0\n}\n\n# Check a system service exists \nservice_exists()\n{\n\tif [ -z \"$_service_exists\" ]; then\n\t\tdetect_init || return 1\n\tfi\n\teval $_service_exists\n}\n\n# Send a command to a system service\nservice_cmd()\n{\n\tif [ -z \"$_service_cmd\" ]; then\n\t\tdetect_init || return 1\n\tfi\n\teval $_service_cmd\n}\n\n# Send a command to a system service if it is running\nservice_status()\n{\n\tif [ -z \"$_service_cmd\" ]; then\n\t\tdetect_init || return 1\n\tfi\n\tif [ -n \"$_service_status\" ]; then\n\t\teval $_service_status\n\telse\n\t\tservice_command $1 status >/dev/null 2>&1\n\tfi\n}\n\n# Handy macros for our hooks\nservice_command()\n{\n\tservice_exists $1 && service_cmd $1 $2\n}\nservice_condcommand()\n{\n\tservice_exists $1 && service_status $1 && service_cmd $1 $2\n}\n\n# We source each script into this one so that scripts run earlier can\n# remove variables from the environment so later scripts don't see them.\n# Thus, the user can create their dhcpcd.enter/exit-hook script to configure\n# /etc/resolv.conf how they want and stop the system scripts ever updating it.\nfor hook in \\\n\t@SYSCONFDIR@/dhcpcd.enter-hook \\\n\t@HOOKDIR@/* \\\n\t@SYSCONFDIR@/dhcpcd.exit-hook\ndo\n\tcase \"$hook\" in\n\t\t*/*~)\tcontinue;;\n\tesac\n\tfor skip in $skip_hooks; do\n\t\tcase \"$hook\" in\n\t\t\t*/\"$skip\")\t\t\tcontinue 2;;\n\t\t\t*/[0-9][0-9]\"-$skip\")\t\tcontinue 2;;\n\t\t\t*/[0-9][0-9]\"-$skip.sh\")\tcontinue 2;;\n\t\tesac\n\tdone\n\tif [ -f \"$hook\" ]; then\n\t\t. \"$hook\"\n\tfi\ndone\n"
  },
  {
    "path": "iconfig.mk",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2014-2019 Roy Marples <roy@marples.name>\n\n# Nasty hack so that make clean works without configure being run\nTOP?=\t\t.\n_CONFIG_MK!=\ttest -e ${TOP}/config.mk && \\\n\t\t    echo config.mk || echo config-null.mk\n_CONFIG_MK?=\t$(shell test -e ${TOP}/config.mk && \\\n\t\t    echo config.mk || echo config-null.mk)\nCONFIG_MK?=\t${_CONFIG_MK}\ninclude\t\t${TOP}/${CONFIG_MK}\n"
  },
  {
    "path": "src/GNUmakefile",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2017 Roy Marples <roy@marples.name>\n\n# GNU Make does not automagically include .depend\n# Luckily it does read GNUmakefile over Makefile so we can work around it\n\n# Nasty hack so that make clean works without configure being run\nTOP?=\t\t..\nCONFIG_MK?=\t$(shell test -e ${TOP}/config.mk && \\\n\t\t    echo config.mk || echo config-null.mk)\n\ninclude Makefile\nifneq ($(wildcard .depend), )\ninclude .depend\nendif\n"
  },
  {
    "path": "src/Makefile",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2017-2025 Roy Marples <roy@marples.name>\n\n# dhcpcd Makefile\n\nPROG=\t\tdhcpcd\nSRCS=\t\tcommon.c control.c dhcpcd.c duid.c eloop.c logerr.c\nSRCS+=\t\tif.c if-options.c sa.c route.c\nSRCS+=\t\tdhcp-common.c script.c\n\nCFLAGS?=\t-O2\nSUBDIRS+=\t${MKDIRS}\n\nTOP=\t\t..\ninclude\t\t${TOP}/iconfig.mk\n\nCSTD?=\t\tc99\nCFLAGS+=\t-std=${CSTD}\nCPPFLAGS+=\t-I${TOP} -I${TOP}/src -I./crypt -I${TOP}/vendor\n\nSRCS+=\t\t${DHCPCD_SRCS} ${PRIVSEP_SRCS}\nDHCPCD_DEF?=\tdhcpcd-definitions.conf\nDHCPCD_DEFS=\tdhcpcd-definitions.conf dhcpcd-definitions-small.conf\n\nPCOMPAT_SRCS=\t${COMPAT_SRCS:compat/%=${TOP}/compat/%}\nPCRYPT_SRCS=\t${CRYPT_SRCS:compat/%=${TOP}/compat/%}\nPVENDOR_SRCS=\t${VENDOR_SRCS:vendor/%=${TOP}/vendor/%}\nOBJS+=\t\t${SRCS:.c=.o}\nOBJS+=\t\t${PCRYPT_SRCS:.c=.o}\nOBJS+=\t\t${PCOMPAT_SRCS:.c=.o}\nOBJS+=\t\t${PVENDOR_SRCS:.c=.o}\n\nMAN5=\t\tdhcpcd.conf.5\nMAN8=\t\tdhcpcd.8\nCLEANFILES=\tdhcpcd.conf.5 dhcpcd.8\n\nFILES=\t\tdhcpcd.conf\nFILESDIR=\t${SYSCONFDIR}\n\nDEPEND!=\ttest -e .depend && echo \".depend\" || echo \"\"\n\nCLEANFILES+=\t*.tar.xz\n\n.PHONY:\t\timport import-bsd dev test\n\n.SUFFIXES:\t.in\n\n.in:\n\t${SED} ${SED_RUNDIR} ${SED_DBDIR} ${SED_LIBDIR} ${SED_HOOKDIR} \\\n\t\t${SED_SYS} ${SED_SCRIPT} ${SED_DATADIR} \\\n\t\t${SED_SERVICEEXISTS} ${SED_SERVICECMD} ${SED_SERVICESTATUS} \\\n\t\t${SED_STATUSARG} \\\n\t\t$< > $@\n\nall: ${TOP}/config.h ${PROG} ${SCRIPTS} ${MAN5} ${MAN8}\n\tfor x in ${SUBDIRS}; do cd $$x; ${MAKE} $@ || exit $$?; cd ..; done\n\ndev:\n\tcd dev && ${MAKE}\n\n../vendor/rbtree.o:\n\t${CC} ${CFLAGS} ${CPPFLAGS} ${RBTREE_CFLAGS} ${RBTREE_CPPFLAGS} -c ../vendor/rbtree.c -o $@\n\n.c.o:\n\t${CC} ${CFLAGS} ${CPPFLAGS} -c $< -o $@\n\nCLEANFILES+=\tdhcpcd-embedded.h dhcpcd-embedded.c\n\ndhcpcd-embedded.h: genembedh ${DHCPCD_DEFS} dhcpcd-embedded.h.in\n\t${HOST_SH} ${.ALLSRC} $^ > $@\n\ndhcpcd-embedded.c: genembedc ${DHCPCD_DEFS} dhcpcd-embedded.c.in\n\t${HOST_SH} ${.ALLSRC} $^ > $@\n\nif-options.c: dhcpcd-embedded.h\n\n.depend: ${SRCS} ${PCRYPT_SRCS} ${PCOMPAT_SRC}\n\t${CC} ${CPPFLAGS} -MM ${SRCS} ${PCRYPT_SRCS} ${PCOMPAT_SRCS} > .depend\n\ndepend: .depend\n\n${PROG}: ${DEPEND} ${OBJS}\n\t${CC} ${LDFLAGS} -o $@ ${OBJS} ${LDADD}\n\nlint:\n\t${LINT} -Suz ${CPPFLAGS} ${SRCS} ${PCRYPT_SRCS} ${PCOMPAT_SRCS}\n\n_embeddedinstall: ${DHCPCD_DEF}\n\t${INSTALL} -d ${DESTDIR}${LIBEXECDIR}\n\t${INSTALL} -m ${CONFMODE} ${DHCPCD_DEF} ${DESTDIR}${LIBEXECDIR}\n\n_proginstall: ${PROG}\n\t${INSTALL} -d ${DESTDIR}${SBINDIR}\n\t${INSTALL} -m ${BINMODE} ${PROG} ${DESTDIR}${SBINDIR}\n\t${INSTALL} -m ${DBMODE} -d ${DESTDIR}${DBDIR}\n\nproginstall: _proginstall ${EMBEDDEDINSTALL}\n\tfor x in ${SUBDIRS}; do cd $$x; ${MAKE} $@ || exit $$?; cd ..; done\n\n_maninstall: ${MAN5} ${MAN8}\n\t${INSTALL} -d ${DESTDIR}${MANDIR}/man5\n\t${INSTALL} -m ${MANMODE} ${MAN5} ${DESTDIR}${MANDIR}/man5\n\t${INSTALL} -d ${DESTDIR}${MANDIR}/man8\n\t${INSTALL} -m ${MANMODE} ${MAN8} ${DESTDIR}${MANDIR}/man8\n\n_confinstall:\n\t${INSTALL} -d ${DESTDIR}${SYSCONFDIR}\n\t# Install a new default config if not present\n\tif ! [ -e ${DESTDIR}${SYSCONFDIR}/dhcpcd.conf ]; then \\\n\t\t${INSTALL} -m ${CONFMODE} dhcpcd.conf ${DESTDIR}${SYSCONFDIR}; \\\n\t\tif [ \"${UNCOMMENT_NTP}\" = yes ]; then \\\n\t\t\t${SED} -i \\\n\t\t\t    -e 's/#option ntp_servers/option ntp_servers/' \\\n\t\t\t    ${DESTDIR}/${SYSCONFDIR}/dhcpcd.conf; \\\n\t\tfi; \\\n\tfi\n\t\neginstall:\n\ninstall: proginstall _maninstall _confinstall eginstall\n\nclean:\n\trm -f ${OBJS} ${PROG} ${PROG}.core ${CLEANFILES}\n\tfor x in ${SUBDIRS}; do cd $$x; ${MAKE} $@ || exit $$?; cd ..; done\n\ndistclean: clean\n\trm -f .depend\n\trm -f *.diff *.patch *.orig *.rej\n\n_import-src: ${SRCS} ${MAN5} ${MAN8}\n\t@if ! [ -e ../config.h ]; then \\\n\t\techo \"Cowardly refusing to import-src unless configured\" >&2; \\\n\t\texit 1; \\\n\tfi\n\t${INSTALL} -d ${DESTDIR}/src\n\tfor x in defs.h ${SRCS} ${SRCS:.c=.h} queue.h dev.h ${MAN5} ${MAN8}; do \\\n\t\t[ ! -e \"$$x\" ] || cp $$x ${DESTDIR}/src; \\\n\tdone\n\tcp dhcpcd.conf ${DESTDIR}/src\n\tif [ -n \"${COMPAT_SRCS}\" ]; then \\\n\t\t${INSTALL} -d ${DESTDIR}/compat; \\\n\t\tfor x in ${COMPAT_SRCS} ${COMPAT_SRCS:.c=.h}; do \\\n\t\t\t[ ! -e \"../$$x\" ] || cp \"../$$x\" ${DESTDIR}/compat; \\\n\t\tdone; \\\n\tfi\n\tif [ -n \"${VENDOR_SRCS}\" ]; then \\\n\t\t${INSTALL} -d ${DESTDIR}/vendor; \\\n\t\tcp ../vendor/queue.h ${DESTDIR}/vendor; \\\n\t\tfor x in ${VENDOR_SRCS} ${VENDOR_SRCS:.c=.h}; do \\\n\t\t\t[ ! -e \"../$$x\" ] || cp \"../$$x\" ${DESTDIR}/compat; \\\n\t\tdone; \\\n\tfi\n\tif ! grep HAVE_SYS_BITOPS_H ../config.h; then \\\n\t\tcp ../compat/bitops.h ${DESTDIR}/compat; \\\n\tfi\n\tif grep compat/consttime_memequal.h ../config.h; then \\\n\t\tcp ../compat/consttime_memequal.h ${DESTDIR}/compat; \\\n\tfi\n\tif [ -e ${DESTDIR}/vendor/rbtree.c ]; then \\\n\t\tcp ../vendor/rbtree.h ${DESTDIR}/vendor; \\\n\tfi\n\tif [ -e ${DESTDIR}/compat/strtoi.c ]; then \\\n\t\tcp ../compat/_strtoi.h ${DESTDIR}/compat; \\\n\tfi\n\tif [ -n \"${CRYPT_SRCS}\" ]; then \\\n\t\t${INSTALL} -d ${DESTDIR}/compat/crypt; \\\n\t\tfor x in ${CRYPT_SRCS} ${CRYPT_SRCS:.c=.h}; do \\\n\t\t\tcp \"../$$x\" ${DESTDIR}/compat/crypt; \\\n\t\tdone; \\\n\tfi\n\t# DragonFlyBSD builds base version with private crypto\n\tif [ `uname` = DragonFly ]; then rm ${DESTDIR}/compat/crypt/md5* ${DESTDIR}/compat/crypt/sha256*; fi\n\ninclude Makefile.inc\n"
  },
  {
    "path": "src/Makefile.inc",
    "content": "check-format:\n\tfind . -iname \"*.c\" -o -iname \"*.h\" | \\\n\t\txargs -- clang-format --dry-run --Werror\n\nformat:\n\tfind . -iname \"*.c\" -o -iname \"*.h\" | \\\n\t\txargs -- clang-format -i\n\n_lint:\n\tclang-tidy ${SRCS} -- ${CPPFLAGS} ${CFLAGS} -isystem ${TOP}/vendor\n\ninclude ${TOP}/Makefile.inc\n"
  },
  {
    "path": "src/arp.c",
    "content": "/*\n * dhcpcd - ARP handler\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/types.h>\n#include <sys/socket.h>\n\n#include <net/if.h>\n#include <netinet/in.h>\n#include <netinet/if_ether.h>\n\n#include <arpa/inet.h>\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#define ELOOP_QUEUE ELOOP_ARP\n#include \"arp.h\"\n#include \"bpf.h\"\n#include \"common.h\"\n#include \"config.h\"\n#include \"dhcpcd.h\"\n#include \"eloop.h\"\n#include \"if-options.h\"\n#include \"if.h\"\n#include \"ipv4.h\"\n#include \"ipv4ll.h\"\n#include \"logerr.h\"\n#include \"privsep.h\"\n\n#if defined(ARP)\n#define ARP_LEN                                                             \\\n\t(FRAMEHDRLEN_MAX + sizeof(struct arphdr) + (2 * sizeof(uint32_t)) + \\\n\t    (2 * HWADDR_LEN))\n\n/* ARP debugging can be quite noisy. Enable this for more noise! */\n// #define\tARP_DEBUG\n\n/* Assert the correct structure size for on wire */\n__CTASSERT(sizeof(struct arphdr) == 8);\n\nstatic ssize_t\narp_request(const struct arp_state *astate, const struct in_addr *sip)\n{\n\tconst struct interface *ifp = astate->iface;\n\tconst struct in_addr *tip = &astate->addr;\n\tuint8_t arp_buffer[ARP_LEN];\n\tstruct arphdr ar;\n\tsize_t len;\n\tuint8_t *p;\n\n\tar.ar_hrd = htons(ifp->hwtype);\n\tar.ar_pro = htons(ETHERTYPE_IP);\n\tar.ar_hln = ifp->hwlen;\n\tar.ar_pln = sizeof(tip->s_addr);\n\tar.ar_op = htons(ARPOP_REQUEST);\n\n\tp = arp_buffer;\n\tlen = 0;\n\n#define CHECK(fun, b, l)                            \\\n\tdo {                                        \\\n\t\tif (len + (l) > sizeof(arp_buffer)) \\\n\t\t\tgoto eexit;                 \\\n\t\tfun(p, (b), (l));                   \\\n\t\tp += (l);                           \\\n\t\tlen += (l);                         \\\n\t} while (/* CONSTCOND */ 0)\n#define APPEND(b, l) CHECK(memcpy, b, l)\n#define ZERO(l)\t     CHECK(memset, 0, l)\n\n\tAPPEND(&ar, sizeof(ar));\n\tAPPEND(ifp->hwaddr, ifp->hwlen);\n\tif (sip != NULL)\n\t\tAPPEND(&sip->s_addr, sizeof(sip->s_addr));\n\telse\n\t\tZERO(sizeof(tip->s_addr));\n\tZERO(ifp->hwlen);\n\tAPPEND(&tip->s_addr, sizeof(tip->s_addr));\n\n#ifdef PRIVSEP\n\tif (ifp->ctx->options & DHCPCD_PRIVSEP)\n\t\treturn ps_bpf_sendarp(ifp, tip, arp_buffer, len);\n#endif\n\t/* Note that well formed ethernet will add extra padding\n\t * to ensure that the packet is at least 60 bytes (64 including FCS). */\n\treturn bpf_send(astate->bpf, ETHERTYPE_ARP, arp_buffer, len);\n\neexit:\n\terrno = ENOBUFS;\n\treturn -1;\n}\n\nstatic void\narp_report_conflicted(const struct arp_state *astate,\n    const struct arp_msg *amsg)\n{\n\tchar abuf[HWADDR_LEN * 3];\n\tchar fbuf[HWADDR_LEN * 3];\n\n\tif (amsg == NULL) {\n\t\tlogerrx(\"%s: DAD detected %s\", astate->iface->name,\n\t\t    inet_ntoa(astate->addr));\n\t\treturn;\n\t}\n\n\thwaddr_ntoa(amsg->sha, astate->iface->hwlen, abuf, sizeof(abuf));\n\tif (bpf_frame_header_len(astate->iface) == 0) {\n\t\tlogwarnx(\"%s: %s claims %s\", astate->iface->name, abuf,\n\t\t    inet_ntoa(astate->addr));\n\t\treturn;\n\t}\n\n\tlogwarnx(\"%s: %s(%s) claims %s\", astate->iface->name, abuf,\n\t    hwaddr_ntoa(amsg->fsha, astate->iface->hwlen, fbuf, sizeof(fbuf)),\n\t    inet_ntoa(astate->addr));\n}\n\nstatic void\narp_found(struct arp_state *astate, const struct arp_msg *amsg)\n{\n\tstruct interface *ifp;\n\tstruct ipv4_addr *ia;\n#ifndef KERNEL_RFC5227\n\tstruct timespec now;\n#endif\n\n\tarp_report_conflicted(astate, amsg);\n\tifp = astate->iface;\n\n\t/* If we haven't added the address we're doing a probe. */\n\tia = ipv4_iffindaddr(ifp, &astate->addr, NULL);\n\tif (ia == NULL) {\n\t\tif (astate->found_cb != NULL)\n\t\t\tastate->found_cb(astate, amsg);\n\t\treturn;\n\t}\n\n#ifndef KERNEL_RFC5227\n\t/* RFC 3927 Section 2.5 says a defence should\n\t * broadcast an ARP announcement.\n\t * Because the kernel will also unicast a reply to the\n\t * hardware address which requested the IP address\n\t * the other IPv4LL client will receieve two ARP\n\t * messages.\n\t * If another conflict happens within DEFEND_INTERVAL\n\t * then we must drop our address and negotiate a new one.\n\t * If DHCPCD_ARP_PERSISTDEFENCE is set, that enables\n\t * RFC5227 section 2.4.c behaviour. Upon conflict\n\t * detection, the host records the time that the\n\t * conflicting ARP packet was received, and then\n\t * broadcasts one single ARP Announcement. The host then\n\t * continues to use the address normally. All further\n\t * conflict notifications within the DEFEND_INTERVAL are\n\t * ignored. */\n\tclock_gettime(CLOCK_MONOTONIC, &now);\n\tif (timespecisset(&astate->defend) &&\n\t    eloop_timespec_diff(&now, &astate->defend, NULL) <\n\t\tDEFEND_INTERVAL) {\n\t\tlogwarnx(\"%s: %d second defence failed for %s\", ifp->name,\n\t\t    DEFEND_INTERVAL, inet_ntoa(astate->addr));\n\t\tif (ifp->options->options & DHCPCD_ARP_PERSISTDEFENCE)\n\t\t\treturn;\n\t} else if (arp_request(astate, &astate->addr) == -1)\n\t\tlogerr(__func__);\n\telse {\n\t\tlogdebugx(\"%s: defended address %s\", ifp->name,\n\t\t    inet_ntoa(astate->addr));\n\t\tastate->defend = now;\n\t\treturn;\n\t}\n#endif\n\n\tif (astate->defend_failed_cb != NULL)\n\t\tastate->defend_failed_cb(astate);\n}\n\nstatic bool\narp_validate(const struct interface *ifp, struct arphdr *arp)\n{\n\t/* Address type must match */\n\tif (arp->ar_hrd != htons(ifp->hwtype))\n\t\treturn false;\n\n\t/* Protocol must be IP. */\n\tif (arp->ar_pro != htons(ETHERTYPE_IP))\n\t\treturn false;\n\n\t/* lladdr length matches */\n\tif (arp->ar_hln != ifp->hwlen)\n\t\treturn false;\n\n\t/* Protocol length must match in_addr_t */\n\tif (arp->ar_pln != sizeof(in_addr_t))\n\t\treturn false;\n\n\t/* Only these types are recognised */\n\tif (arp->ar_op != htons(ARPOP_REPLY) &&\n\t    arp->ar_op != htons(ARPOP_REQUEST))\n\t\treturn false;\n\n\treturn true;\n}\n\nvoid\narp_packet(struct interface *ifp, uint8_t *data, size_t len,\n    unsigned int bpf_flags)\n{\n\tsize_t fl = bpf_frame_header_len(ifp), falen;\n\tstruct arphdr ar;\n\tstruct arp_msg arm;\n\tconst struct iarp_state *state;\n\tstruct arp_state *astate, *astaten;\n\tuint8_t *hw_s, *hw_t;\n#ifndef KERNEL_RFC5227\n\tbool is_probe;\n#endif /* KERNEL_RFC5227 */\n\n\t/* Copy the frame header source and destination out */\n\tmemset(&arm, 0, sizeof(arm));\n\tif (fl != 0) {\n\t\thw_s = bpf_frame_header_src(ifp, data, &falen);\n\t\tif (hw_s != NULL && falen <= sizeof(arm.fsha))\n\t\t\tmemcpy(arm.fsha, hw_s, falen);\n\t\thw_t = bpf_frame_header_dst(ifp, data, &falen);\n\t\tif (hw_t != NULL && falen <= sizeof(arm.ftha))\n\t\t\tmemcpy(arm.ftha, hw_t, falen);\n\n\t\t/* Skip past the frame header */\n\t\tdata += fl;\n\t\tlen -= fl;\n\t}\n\n\t/* We must have a full ARP header */\n\tif (len < sizeof(ar))\n\t\treturn;\n\tmemcpy(&ar, data, sizeof(ar));\n\n\tif (!arp_validate(ifp, &ar)) {\n#ifdef BPF_DEBUG\n\t\tlogerrx(\"%s: ARP BPF validation failure\", ifp->name);\n#endif\n\t\treturn;\n\t}\n\n\t/* Get pointers to the hardware addresses */\n\thw_s = data + sizeof(ar);\n\thw_t = hw_s + ar.ar_hln + ar.ar_pln;\n\t/* Ensure we got all the data */\n\tif ((size_t)((hw_t + ar.ar_hln + ar.ar_pln) - data) > len)\n\t\treturn;\n\t/* Ignore messages from ourself */\n\tif (ar.ar_hln == ifp->hwlen &&\n\t    memcmp(hw_s, ifp->hwaddr, ifp->hwlen) == 0) {\n#ifdef ARP_DEBUG\n\t\tlogdebugx(\"%s: ignoring ARP from self\", ifp->name);\n#endif\n\t\treturn;\n\t}\n\t/* Copy out the HW and IP addresses */\n\tmemcpy(&arm.sha, hw_s, ar.ar_hln);\n\tmemcpy(&arm.sip.s_addr, hw_s + ar.ar_hln, ar.ar_pln);\n\tmemcpy(&arm.tha, hw_t, ar.ar_hln);\n\tmemcpy(&arm.tip.s_addr, hw_t + ar.ar_hln, ar.ar_pln);\n\n#ifndef KERNEL_RFC5227\n\t/* During ARP probe the 'sender hardware address' MUST contain the\n\t * hardware address of the interface sending the packet. RFC5227, 1.1 */\n\tis_probe = ar.ar_op == htons(ARPOP_REQUEST) &&\n\t    IN_IS_ADDR_UNSPECIFIED(&arm.sip) && bpf_flags & BPF_BCAST;\n\tif (is_probe && falen > 0 &&\n\t    (falen != ar.ar_hln || memcmp(&arm.sha, &arm.fsha, ar.ar_hln))) {\n\t\tchar abuf[HWADDR_LEN * 3];\n\t\tchar fbuf[HWADDR_LEN * 3];\n\t\thwaddr_ntoa(&arm.sha, ar.ar_hln, abuf, sizeof(abuf));\n\t\thwaddr_ntoa(&arm.fsha, falen, fbuf, sizeof(fbuf));\n\t\tlogwarnx(\n\t\t    \"%s: invalid ARP probe, sender hw address mismatch (%s, %s)\",\n\t\t    ifp->name, abuf, fbuf);\n\t\treturn;\n\t}\n#endif /* KERNEL_RFC5227 */\n\n\t/* Match the ARP probe to our states.\n\t * Ignore Unicast Poll, RFC1122. */\n\tstate = ARP_CSTATE(ifp);\n\tif (state == NULL)\n\t\treturn;\n\tTAILQ_FOREACH_SAFE(astate, &state->arp_states, next, astaten) {\n\t\tif (IN_ARE_ADDR_EQUAL(&arm.sip, &astate->addr) ||\n\t\t    (IN_IS_ADDR_UNSPECIFIED(&arm.sip) &&\n\t\t\tIN_ARE_ADDR_EQUAL(&arm.tip, &astate->addr) &&\n\t\t\tbpf_flags & BPF_BCAST))\n\t\t\tarp_found(astate, &arm);\n\t}\n}\n\nstatic void\narp_read(void *arg, unsigned short events)\n{\n\tstruct arp_state *astate = arg;\n\tstruct bpf *bpf = astate->bpf;\n\tstruct interface *ifp = astate->iface;\n\tuint8_t buf[ARP_LEN];\n\tssize_t bytes;\n\tstruct in_addr addr = astate->addr;\n\n\tif (events != ELE_READ)\n\t\tlogerrx(\"%s: unexpected event 0x%04x\", __func__, events);\n\n\t/* Some RAW mechanisms are generic file descriptors, not sockets.\n\t * This means we have no kernel call to just get one packet,\n\t * so we have to process the entire buffer. */\n\tbpf->bpf_flags &= ~BPF_EOF;\n\twhile (!(bpf->bpf_flags & BPF_EOF)) {\n\t\tbytes = bpf_read(bpf, buf, sizeof(buf));\n\t\tif (bytes == -1) {\n\t\t\tlogerr(\"%s: %s\", __func__, ifp->name);\n\t\t\tarp_free(astate);\n\t\t\treturn;\n\t\t}\n\t\tarp_packet(ifp, buf, (size_t)bytes, bpf->bpf_flags);\n\t\t/* Check we still have a state after processing. */\n\t\tif ((astate = arp_find(ifp, &addr)) == NULL)\n\t\t\tbreak;\n\t\tif ((bpf = astate->bpf) == NULL)\n\t\t\tbreak;\n\t}\n}\n\nstatic void\narp_probed(void *arg)\n{\n\tstruct arp_state *astate = arg;\n\n\ttimespecclear(&astate->defend);\n\tastate->not_found_cb(astate);\n}\n\nstatic void\narp_probe1(void *arg)\n{\n\tstruct arp_state *astate = arg;\n\tstruct interface *ifp = astate->iface;\n\tunsigned int delay;\n\n\tif (++astate->probes < PROBE_NUM) {\n\t\tdelay = (PROBE_MIN * MSEC_PER_SEC) +\n\t\t    (arc4random_uniform(\n\t\t\t(PROBE_MAX - PROBE_MIN) * MSEC_PER_SEC));\n\t\teloop_timeout_add_msec(ifp->ctx->eloop, delay, arp_probe1,\n\t\t    astate);\n\t} else {\n\t\tdelay = ANNOUNCE_WAIT * MSEC_PER_SEC;\n\t\teloop_timeout_add_msec(ifp->ctx->eloop, delay, arp_probed,\n\t\t    astate);\n\t}\n\tlogdebugx(\"%s: ARP probing %s (%d of %d), next in %0.1f seconds\",\n\t    ifp->name, inet_ntoa(astate->addr),\n\t    astate->probes ? astate->probes : PROBE_NUM, PROBE_NUM,\n\t    (float)delay / MSEC_PER_SEC);\n\tif (arp_request(astate, NULL) == -1)\n\t\tlogerr(__func__);\n}\n\nvoid\narp_probe(struct arp_state *astate)\n{\n\tastate->probes = 0;\n\tlogdebugx(\"%s: probing for %s\", astate->iface->name,\n\t    inet_ntoa(astate->addr));\n\tarp_probe1(astate);\n}\n#endif /* ARP */\n\nstruct arp_state *\narp_find(struct interface *ifp, const struct in_addr *addr)\n{\n\tstruct iarp_state *state;\n\tstruct arp_state *astate;\n\n\tif ((state = ARP_STATE(ifp)) == NULL)\n\t\tgoto out;\n\tTAILQ_FOREACH(astate, &state->arp_states, next) {\n\t\tif (astate->addr.s_addr == addr->s_addr && astate->iface == ifp)\n\t\t\treturn astate;\n\t}\nout:\n\terrno = ESRCH;\n\treturn NULL;\n}\n\n#ifndef KERNEL_RFC5227\nstatic void\narp_announced(void *arg)\n{\n\tstruct arp_state *astate = arg;\n\n\tif (astate->announced_cb) {\n\t\tastate->announced_cb(astate);\n\t\treturn;\n\t}\n\n\t/* Keep the ARP state open to handle ongoing ACD. */\n}\n\nstatic void\narp_announce1(void *arg)\n{\n\tstruct arp_state *astate = arg;\n\tstruct interface *ifp = astate->iface;\n\tstruct ipv4_addr *ia;\n\n\tif (++astate->claims < ANNOUNCE_NUM)\n\t\tlogdebugx(\"%s: ARP announcing %s (%d of %d), \"\n\t\t\t  \"next in %d.0 seconds\",\n\t\t    ifp->name, inet_ntoa(astate->addr), astate->claims,\n\t\t    ANNOUNCE_NUM, ANNOUNCE_WAIT);\n\telse\n\t\tlogdebugx(\"%s: ARP announcing %s (%d of %d)\", ifp->name,\n\t\t    inet_ntoa(astate->addr), astate->claims, ANNOUNCE_NUM);\n\n\t/* The kernel will send a Gratuitous ARP for newly added addresses.\n\t * So we can avoid sending the same.\n\t * Linux is special and doesn't send one. */\n\tia = ipv4_iffindaddr(ifp, &astate->addr, NULL);\n#ifndef __linux__\n\tif (astate->claims == 1 && ia != NULL && ia->flags & IPV4_AF_NEW)\n\t\tgoto skip_request;\n#endif\n\n\tif (arp_request(astate, &astate->addr) == -1)\n\t\tlogerr(__func__);\n\n#ifndef __linux__\nskip_request:\n#endif\n\t/* No longer a new address. */\n\tif (ia != NULL)\n\t\tia->flags |= ~IPV4_AF_NEW;\n\n\teloop_timeout_add_sec(ifp->ctx->eloop, ANNOUNCE_WAIT,\n\t    astate->claims < ANNOUNCE_NUM ? arp_announce1 : arp_announced,\n\t    astate);\n}\n\nstatic void\narp_announce(struct arp_state *astate)\n{\n\tstruct iarp_state *state;\n\tstruct interface *ifp;\n\tstruct arp_state *a2;\n\tint r;\n\n\t/* Cancel any other ARP announcements for this address. */\n\tTAILQ_FOREACH(ifp, astate->iface->ctx->ifaces, next) {\n\t\tstate = ARP_STATE(ifp);\n\t\tif (state == NULL)\n\t\t\tcontinue;\n\t\tTAILQ_FOREACH(a2, &state->arp_states, next) {\n\t\t\tif (astate == a2 ||\n\t\t\t    a2->addr.s_addr != astate->addr.s_addr)\n\t\t\t\tcontinue;\n\t\t\tr = eloop_timeout_delete(a2->iface->ctx->eloop,\n\t\t\t    a2->claims < ANNOUNCE_NUM ? arp_announce1 :\n\t\t\t\t\t\t\tarp_announced,\n\t\t\t    a2);\n\t\t\tif (r == -1)\n\t\t\t\tlogerr(__func__);\n\t\t\telse if (r != 0) {\n\t\t\t\tlogdebugx(\"%s: ARP announcement \"\n\t\t\t\t\t  \"of %s cancelled\",\n\t\t\t\t    a2->iface->name, inet_ntoa(a2->addr));\n\t\t\t\tarp_announced(a2);\n\t\t\t}\n\t\t}\n\t}\n\n\tastate->claims = 0;\n\tarp_announce1(astate);\n}\n\nstruct arp_state *\narp_ifannounceaddr(struct interface *ifp, const struct in_addr *ia)\n{\n\tstruct arp_state *astate;\n\n\tif (ifp->flags & IFF_NOARP || !(ifp->options->options & DHCPCD_ARP))\n\t\treturn NULL;\n\n\tastate = arp_find(ifp, ia);\n\tif (astate == NULL) {\n\t\tastate = arp_new(ifp, ia);\n\t\tif (astate == NULL)\n\t\t\treturn NULL;\n\t\tastate->announced_cb = arp_free;\n\t}\n\tarp_announce(astate);\n\treturn astate;\n}\n#endif\n\nstruct arp_state *\narp_new(struct interface *ifp, const struct in_addr *addr)\n{\n\tstruct iarp_state *state;\n\tstruct arp_state *astate;\n\n\tif ((state = ARP_STATE(ifp)) == NULL) {\n\t\tifp->if_data[IF_DATA_ARP] = malloc(sizeof(*state));\n\t\tstate = ARP_STATE(ifp);\n\t\tif (state == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\treturn NULL;\n\t\t}\n\t\tTAILQ_INIT(&state->arp_states);\n\t} else {\n\t\tif ((astate = arp_find(ifp, addr)) != NULL)\n\t\t\treturn astate;\n\t}\n\n\tif ((astate = calloc(1, sizeof(*astate))) == NULL) {\n\t\tlogerr(__func__);\n\t\treturn NULL;\n\t}\n\tastate->iface = ifp;\n\tastate->addr = *addr;\n\n#ifdef PRIVSEP\n\tif (IN_PRIVSEP(ifp->ctx)) {\n\t\tif (ps_bpf_openarp(ifp, addr) == -1) {\n\t\t\tlogerr(__func__);\n\t\t\tfree(astate);\n\t\t\treturn NULL;\n\t\t}\n\t} else\n#endif\n\t{\n\t\tastate->bpf = bpf_open(ifp, bpf_filter_arp, addr);\n\t\tif (astate->bpf == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\tfree(astate);\n\t\t\treturn NULL;\n\t\t}\n\t\tif (eloop_event_add(ifp->ctx->eloop, astate->bpf->bpf_fd,\n\t\t\tELE_READ, arp_read, astate) == -1)\n\t\t\tlogerr(\"%s: eloop_event_add\", __func__);\n\t}\n\n\tstate = ARP_STATE(ifp);\n\tTAILQ_INSERT_TAIL(&state->arp_states, astate, next);\n\treturn astate;\n}\n\nvoid\narp_free(struct arp_state *astate)\n{\n\tstruct interface *ifp;\n\tstruct dhcpcd_ctx *ctx;\n\tstruct iarp_state *state;\n\n\tif (astate == NULL)\n\t\treturn;\n\n\tifp = astate->iface;\n\tctx = ifp->ctx;\n\teloop_timeout_delete(ctx->eloop, NULL, astate);\n\n\tstate = ARP_STATE(ifp);\n\tTAILQ_REMOVE(&state->arp_states, astate, next);\n\tif (astate->free_cb)\n\t\tastate->free_cb(astate);\n\n#ifdef PRIVSEP\n\tif (IN_PRIVSEP(ctx) && ps_bpf_closearp(ifp, &astate->addr) == -1)\n\t\tlogerr(__func__);\n#endif\n\tif (astate->bpf != NULL) {\n\t\teloop_event_delete(ctx->eloop, astate->bpf->bpf_fd);\n\t\tbpf_close(astate->bpf);\n\t}\n\n\tfree(astate);\n\n\tif (TAILQ_FIRST(&state->arp_states) == NULL) {\n\t\tfree(state);\n\t\tifp->if_data[IF_DATA_ARP] = NULL;\n\t}\n}\n\nvoid\narp_freeaddr(struct interface *ifp, const struct in_addr *ia)\n{\n\tstruct arp_state *astate;\n\n\tastate = arp_find(ifp, ia);\n\tarp_free(astate);\n}\n\nvoid\narp_drop(struct interface *ifp)\n{\n\tstruct iarp_state *state;\n\tstruct arp_state *astate;\n\n\twhile ((state = ARP_STATE(ifp)) != NULL &&\n\t    (astate = TAILQ_FIRST(&state->arp_states)) != NULL)\n\t\tarp_free(astate);\n}\n"
  },
  {
    "path": "src/arp.h",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef ARP_H\n#define ARP_H\n\n/* ARP timings from RFC5227 */\n#define PROBE_WAIT\t    1\n#define PROBE_NUM\t    3\n#define PROBE_MIN\t    1\n#define PROBE_MAX\t    2\n#define ANNOUNCE_WAIT\t    2\n#define ANNOUNCE_NUM\t    2\n#define ANNOUNCE_INTERVAL   2\n#define MAX_CONFLICTS\t    10\n#define RATE_LIMIT_INTERVAL 60\n#define DEFEND_INTERVAL\t    10\n\n#include \"bpf.h\"\n#include \"dhcpcd.h\"\n#include \"if.h\"\n\n#ifdef IN_IFF_DUPLICATED\n/* NetBSD gained RFC 5227 support in the kernel.\n * This means dhcpcd doesn't need ARP except for ARPing support\n * and ARP announcing an address. */\n#if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 799003900\n#define KERNEL_RFC5227\n#endif\n#endif\n\nstruct arp_msg {\n\tuint16_t op;\n\tuint8_t sha[HWADDR_LEN];\n\tstruct in_addr sip;\n\tuint8_t tha[HWADDR_LEN];\n\tstruct in_addr tip;\n\t/* Frame header and sender to diagnose failures */\n\tuint8_t fsha[HWADDR_LEN];\n\tuint8_t ftha[HWADDR_LEN];\n};\n\nstruct arp_state {\n\tTAILQ_ENTRY(arp_state) next;\n\tstruct interface *iface;\n\tstruct in_addr addr;\n\tstruct bpf *bpf;\n\n\tint probes;\n\tint claims;\n\tstruct timespec defend;\n\n\tvoid (*found_cb)(struct arp_state *, const struct arp_msg *);\n\tvoid (*not_found_cb)(struct arp_state *);\n\tvoid (*announced_cb)(struct arp_state *);\n\tvoid (*defend_failed_cb)(struct arp_state *);\n\tvoid (*free_cb)(struct arp_state *);\n};\nTAILQ_HEAD(arp_statehead, arp_state);\n\nstruct iarp_state {\n\tstruct arp_statehead arp_states;\n};\n\n#define ARP_STATE(ifp)\t((struct iarp_state *)(ifp)->if_data[IF_DATA_ARP])\n#define ARP_CSTATE(ifp) ((const struct iarp_state *)(ifp)->if_data[IF_DATA_ARP])\n\n#ifdef ARP\nvoid arp_packet(struct interface *, uint8_t *, size_t, unsigned int);\nstruct arp_state *arp_new(struct interface *, const struct in_addr *);\nvoid arp_probe(struct arp_state *);\nstruct arp_state *arp_ifannounceaddr(struct interface *,\n    const struct in_addr *);\nstruct arp_state *arp_find(struct interface *, const struct in_addr *);\nvoid arp_free(struct arp_state *);\nvoid arp_freeaddr(struct interface *, const struct in_addr *);\nvoid arp_drop(struct interface *);\n#endif /* ARP */\n#endif /* ARP_H */\n"
  },
  {
    "path": "src/auth.c",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/file.h>\n#include <sys/stat.h>\n\n#include <errno.h>\n#include <fcntl.h>\n#include <inttypes.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n#include <unistd.h>\n\n#include \"auth.h\"\n#include \"config.h\"\n#include \"dhcp.h\"\n#include \"dhcp6.h\"\n#include \"dhcpcd.h\"\n#include \"privsep-root.h\"\n\n#ifdef HAVE_HMAC_H\n#include <hmac.h>\n#endif\n\n#ifdef __sun\n#define htonll\n#define ntohll\n#endif\n\n#ifndef htonll\n#if (BYTE_ORDER == LITTLE_ENDIAN)\n#define htonll(x)                                 \\\n\t((uint64_t)htonl((uint32_t)((x) >> 32)) | \\\n\t    (uint64_t)htonl((uint32_t)((x) & 0x00000000ffffffffULL)) << 32)\n#else /* (BYTE_ORDER == LITTLE_ENDIAN) */\n#define htonll(x) (x)\n#endif\n#endif /* htonll */\n\n#ifndef ntohll\n#if (BYTE_ORDER == LITTLE_ENDIAN)\n#define ntohll(x)                                 \\\n\t((uint64_t)ntohl((uint32_t)((x) >> 32)) | \\\n\t    (uint64_t)ntohl((uint32_t)((x) & 0x00000000ffffffffULL)) << 32)\n#else /* (BYTE_ORDER == LITTLE_ENDIAN) */\n#define ntohll(x) (x)\n#endif\n#endif /* ntohll */\n\n#define HMAC_LENGTH 16\n\nvoid\ndhcp_auth_reset(struct authstate *state)\n{\n\tstate->replay = 0;\n\tif (state->token) {\n\t\tfree(state->token->key);\n\t\tfree(state->token->realm);\n\t\tfree(state->token);\n\t\tstate->token = NULL;\n\t}\n\tif (state->reconf) {\n\t\tfree(state->reconf->key);\n\t\tfree(state->reconf->realm);\n\t\tfree(state->reconf);\n\t\tstate->reconf = NULL;\n\t}\n}\n\n/*\n * Authenticate a DHCP message.\n * m and mlen refer to the whole message.\n * t is the DHCP type, pass it 4 or 6.\n * data and dlen refer to the authentication option within the message.\n */\nconst struct token *\ndhcp_auth_validate(struct authstate *state, const struct auth *auth,\n    const void *vm, size_t mlen, int mp, int mt, const void *vdata, size_t dlen)\n{\n\tconst uint8_t *m, *data;\n\tuint8_t protocol, algorithm, rdm, *mm, type;\n\tuint64_t replay;\n\tuint32_t secretid;\n\tconst uint8_t *d, *realm;\n\tsize_t realm_len;\n\tconst struct token *t;\n\ttime_t now;\n\tuint8_t hmac_code[HMAC_LENGTH];\n\n\tif (dlen < 3 + sizeof(replay)) {\n\t\terrno = EINVAL;\n\t\treturn NULL;\n\t}\n\n\tm = vm;\n\tdata = vdata;\n\t/* Ensure that d is inside m which *may* not be the case for DHCPv4.\n\t * This can occur if the authentication option is split using\n\t * DHCP long option from RFC 3399. Section 9 which does infact note that\n\t * implementations should take this into account.\n\t * Fixing this would be problematic, patches welcome. */\n\tif (data < m || data > m + mlen || data + dlen > m + mlen) {\n\t\terrno = ERANGE;\n\t\treturn NULL;\n\t}\n\n\td = data;\n\tprotocol = *d++;\n\talgorithm = *d++;\n\trdm = *d++;\n\tif (!(auth->options & DHCPCD_AUTH_SEND)) {\n\t\t/* If we didn't send any authorisation, it can only be a\n\t\t * reconfigure key */\n\t\tif (protocol != AUTH_PROTO_RECONFKEY) {\n\t\t\terrno = EINVAL;\n\t\t\treturn NULL;\n\t\t}\n\t} else if (protocol != auth->protocol || algorithm != auth->algorithm ||\n\t    rdm != auth->rdm) {\n\t\t/* As we don't require authentication, we should still\n\t\t * accept a reconfigure key */\n\t\tif (protocol != AUTH_PROTO_RECONFKEY ||\n\t\t    auth->options & DHCPCD_AUTH_REQUIRE) {\n\t\t\terrno = EPERM;\n\t\t\treturn NULL;\n\t\t}\n\t}\n\tdlen -= 3;\n\n\tmemcpy(&replay, d, sizeof(replay));\n\treplay = ntohll(replay);\n\t/*\n\t * Test for a replay attack.\n\t *\n\t * NOTE: Some servers always send a replay data value of zero.\n\t * This is strictly compliant with RFC 3315 and 3318 which say:\n\t * \"If the RDM field contains 0x00, the replay detection field MUST be\n\t *    set to the value of a monotonically increasing counter.\"\n\t * An example of a monotonically increasing sequence is:\n\t * 1, 2, 2, 2, 2, 2, 2\n\t * Errata 3474 updates RFC 3318 to say:\n\t * \"If the RDM field contains 0x00, the replay detection field MUST be\n\t *    set to the value of a strictly increasing counter.\"\n\t *\n\t * Taking the above into account, dhcpcd will only test for\n\t * strictly speaking replay attacks if it receives any non zero\n\t * replay data to validate against.\n\t */\n\tif (state->token && state->replay != 0) {\n\t\tif (state->replay == (replay ^ 0x8000000000000000ULL)) {\n\t\t\t/* We don't know if the singular point is increasing\n\t\t\t * or decreasing. */\n\t\t\terrno = EPERM;\n\t\t\treturn NULL;\n\t\t}\n\t\tif ((uint64_t)(replay - state->replay) <= 0) {\n\t\t\t/* Replay attack detected */\n\t\t\terrno = EPERM;\n\t\t\treturn NULL;\n\t\t}\n\t}\n\td += sizeof(replay);\n\tdlen -= sizeof(replay);\n\n\trealm = NULL;\n\trealm_len = 0;\n\n\t/* Extract realm and secret.\n\t * Rest of data is MAC. */\n\tswitch (protocol) {\n\tcase AUTH_PROTO_TOKEN:\n\t\tsecretid = auth->token_rcv_secretid;\n\t\tbreak;\n\tcase AUTH_PROTO_DELAYED:\n\t\tif (dlen < sizeof(secretid) + sizeof(hmac_code)) {\n\t\t\terrno = EINVAL;\n\t\t\treturn NULL;\n\t\t}\n\t\tmemcpy(&secretid, d, sizeof(secretid));\n\t\tsecretid = ntohl(secretid);\n\t\td += sizeof(secretid);\n\t\tdlen -= sizeof(secretid);\n\t\tbreak;\n\tcase AUTH_PROTO_DELAYEDREALM:\n\t\tif (dlen < sizeof(secretid) + sizeof(hmac_code)) {\n\t\t\terrno = EINVAL;\n\t\t\treturn NULL;\n\t\t}\n\t\trealm_len = dlen - (sizeof(secretid) + sizeof(hmac_code));\n\t\tif (realm_len) {\n\t\t\trealm = d;\n\t\t\td += realm_len;\n\t\t\tdlen -= realm_len;\n\t\t}\n\t\tmemcpy(&secretid, d, sizeof(secretid));\n\t\tsecretid = ntohl(secretid);\n\t\td += sizeof(secretid);\n\t\tdlen -= sizeof(secretid);\n\t\tbreak;\n\tcase AUTH_PROTO_RECONFKEY:\n\t\tif (dlen != 1 + 16) {\n\t\t\terrno = EINVAL;\n\t\t\treturn NULL;\n\t\t}\n\t\ttype = *d++;\n\t\tdlen--;\n\t\tswitch (type) {\n\t\tcase 1:\n\t\t\tif ((mp == 4 && mt == DHCP_ACK) ||\n\t\t\t    (mp == 6 && mt == DHCP6_REPLY)) {\n\t\t\t\tif (state->reconf == NULL) {\n\t\t\t\t\tstate->reconf = malloc(\n\t\t\t\t\t    sizeof(*state->reconf));\n\t\t\t\t\tif (state->reconf == NULL)\n\t\t\t\t\t\treturn NULL;\n\t\t\t\t\tstate->reconf->key = malloc(16);\n\t\t\t\t\tif (state->reconf->key == NULL) {\n\t\t\t\t\t\tfree(state->reconf);\n\t\t\t\t\t\tstate->reconf = NULL;\n\t\t\t\t\t\treturn NULL;\n\t\t\t\t\t}\n\t\t\t\t\tstate->reconf->secretid = 0;\n\t\t\t\t\tstate->reconf->expire = 0;\n\t\t\t\t\tstate->reconf->realm = NULL;\n\t\t\t\t\tstate->reconf->realm_len = 0;\n\t\t\t\t\tstate->reconf->key_len = 16;\n\t\t\t\t}\n\t\t\t\tmemcpy(state->reconf->key, d, 16);\n\t\t\t} else {\n\t\t\t\terrno = EINVAL;\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tif (state->reconf == NULL)\n\t\t\t\terrno = ENOENT;\n\t\t\t/* Free the old token so we log acceptance */\n\t\t\tif (state->token) {\n\t\t\t\tfree(state->token);\n\t\t\t\tstate->token = NULL;\n\t\t\t}\n\t\t\t/* Nothing to validate, just accepting the key */\n\t\t\treturn state->reconf;\n\t\tcase 2:\n\t\t\tif (!((mp == 4 && mt == DHCP_FORCERENEW) ||\n\t\t\t\t(mp == 6 && mt == DHCP6_RECONFIGURE))) {\n\t\t\t\terrno = EINVAL;\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tif (state->reconf == NULL) {\n\t\t\t\terrno = ENOENT;\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tt = state->reconf;\n\t\t\tgoto gottoken;\n\t\tdefault:\n\t\t\terrno = EINVAL;\n\t\t\treturn NULL;\n\t\t}\n\tdefault:\n\t\terrno = ENOTSUP;\n\t\treturn NULL;\n\t}\n\n\t/* Find a token for the realm and secret */\n\tTAILQ_FOREACH(t, &auth->tokens, next) {\n\t\tif (t->secretid == secretid && t->realm_len == realm_len &&\n\t\t    (t->realm_len == 0 ||\n\t\t\tmemcmp(t->realm, realm, t->realm_len) == 0))\n\t\t\tbreak;\n\t}\n\tif (t == NULL) {\n\t\terrno = ESRCH;\n\t\treturn NULL;\n\t}\n\tif (t->expire) {\n\t\tif (time(&now) == -1)\n\t\t\treturn NULL;\n\t\tif (t->expire < now) {\n\t\t\terrno = EFAULT;\n\t\t\treturn NULL;\n\t\t}\n\t}\n\ngottoken:\n\t/* First message from the server */\n\tif (state->token &&\n\t    (state->token->secretid != t->secretid ||\n\t\tstate->token->realm_len != t->realm_len ||\n\t\tmemcmp(state->token->realm, t->realm, t->realm_len))) {\n\t\terrno = EPERM;\n\t\treturn NULL;\n\t}\n\n\t/* Special case as no hashing needs to be done. */\n\tif (protocol == AUTH_PROTO_TOKEN) {\n\t\tif (dlen != t->key_len || memcmp(d, t->key, dlen)) {\n\t\t\terrno = EPERM;\n\t\t\treturn NULL;\n\t\t}\n\t\tgoto finish;\n\t}\n\n\t/* Make a duplicate of the message, but zero out the MAC part */\n\tmm = malloc(mlen);\n\tif (mm == NULL)\n\t\treturn NULL;\n\tmemcpy(mm, m, mlen);\n\tmemset(mm + (d - m), 0, dlen);\n\n\t/* RFC3318, section 5.2 - zero giaddr and hops */\n\tif (mp == 4) {\n\t\t/* Assert the bootp structure is correct size. */\n\t\t__CTASSERT(sizeof(struct bootp) == 300);\n\n\t\t*(mm + offsetof(struct bootp, hops)) = '\\0';\n\t\tmemset(mm + offsetof(struct bootp, giaddr), 0, 4);\n\t}\n\n\tmemset(hmac_code, 0, sizeof(hmac_code));\n\tswitch (algorithm) {\n\tcase AUTH_ALG_HMAC_MD5:\n\t\thmac(\"md5\", t->key, t->key_len, mm, mlen, hmac_code,\n\t\t    sizeof(hmac_code));\n\t\tbreak;\n\tdefault:\n\t\terrno = ENOSYS;\n\t\tfree(mm);\n\t\treturn NULL;\n\t}\n\n\tfree(mm);\n\tif (!consttime_memequal(d, &hmac_code, dlen)) {\n\t\terrno = EPERM;\n\t\treturn NULL;\n\t}\n\nfinish:\n\t/* If we got here then authentication passed */\n\tstate->replay = replay;\n\tif (state->token == NULL) {\n\t\t/* We cannot just save a pointer because a reconfigure will\n\t\t * recreate the token list. So we duplicate it. */\n\t\tstate->token = malloc(sizeof(*state->token));\n\t\tif (state->token) {\n\t\t\tstate->token->secretid = t->secretid;\n\t\t\tstate->token->key = malloc(t->key_len);\n\t\t\tif (state->token->key) {\n\t\t\t\tstate->token->key_len = t->key_len;\n\t\t\t\tmemcpy(state->token->key, t->key, t->key_len);\n\t\t\t} else {\n\t\t\t\tfree(state->token);\n\t\t\t\tstate->token = NULL;\n\t\t\t\treturn NULL;\n\t\t\t}\n\t\t\tif (t->realm_len) {\n\t\t\t\tstate->token->realm = malloc(t->realm_len);\n\t\t\t\tif (state->token->realm) {\n\t\t\t\t\tstate->token->realm_len = t->realm_len;\n\t\t\t\t\tmemcpy(state->token->realm, t->realm,\n\t\t\t\t\t    t->realm_len);\n\t\t\t\t} else {\n\t\t\t\t\tfree(state->token->key);\n\t\t\t\t\tfree(state->token);\n\t\t\t\t\tstate->token = NULL;\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tstate->token->realm = NULL;\n\t\t\t\tstate->token->realm_len = 0;\n\t\t\t}\n\t\t}\n\t\t/* If we cannot save the token, we must invalidate */\n\t\tif (state->token == NULL)\n\t\t\treturn NULL;\n\t}\n\n\treturn t;\n}\n\nint\nauth_get_rdm_monotonic(uint64_t *rdm)\n{\n\tFILE *fp;\n\tint err;\n#ifdef LOCK_EX\n\tint flocked;\n#endif\n\n\tfp = fopen(RDM_MONOFILE, \"r+\");\n\tif (fp == NULL) {\n\t\tif (errno != ENOENT)\n\t\t\treturn -1;\n\t\tfp = fopen(RDM_MONOFILE, \"w\");\n\t\tif (fp == NULL)\n\t\t\treturn -1;\n\t\tif (chmod(RDM_MONOFILE, 0400) == -1) {\n\t\t\tfclose(fp);\n\t\t\tunlink(RDM_MONOFILE);\n\t\t\treturn -1;\n\t\t}\n#ifdef LOCK_EX\n\t\tflocked = flock(fileno(fp), LOCK_EX);\n#endif\n\t\t*rdm = 0;\n\t} else {\n#ifdef LOCK_EX\n\t\tflocked = flock(fileno(fp), LOCK_EX);\n#endif\n\t\tif (fscanf(fp, \"0x%016\" PRIu64, rdm) != 1) {\n\t\t\tfclose(fp);\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\t(*rdm)++;\n\tif (fseek(fp, 0, SEEK_SET) == -1 || ftruncate(fileno(fp), 0) == -1 ||\n\t    fprintf(fp, \"0x%016\" PRIu64 \"\\n\", *rdm) != 19 || fflush(fp) == EOF)\n\t\terr = -1;\n\telse\n\t\terr = 0;\n#ifdef LOCK_EX\n\tif (flocked == 0)\n\t\tflock(fileno(fp), LOCK_UN);\n#endif\n\tfclose(fp);\n\treturn err;\n}\n\n#define NTP_EPOCH      2208988800U  /* 1970 - 1900 in seconds */\n#define NTP_SCALE_FRAC 4294967295.0 /* max value of the fractional part */\nstatic uint64_t\nget_next_rdm_monotonic_clock(struct auth *auth)\n{\n\tstruct timespec ts;\n\tuint64_t secs, rdm;\n\tdouble frac;\n\n\tif (clock_gettime(CLOCK_REALTIME, &ts) != 0)\n\t\treturn ++auth->last_replay; /* report error? */\n\n\tsecs = (uint64_t)ts.tv_sec + NTP_EPOCH;\n\tfrac = ((double)ts.tv_nsec / 1e9 * NTP_SCALE_FRAC);\n\trdm = (secs << 32) | (uint64_t)frac;\n\treturn rdm;\n}\n\nstatic uint64_t\nget_next_rdm_monotonic(struct dhcpcd_ctx *ctx, struct auth *auth)\n{\n#ifndef PRIVSEP\n\tUNUSED(ctx);\n#endif\n\n\tif (auth->options & DHCPCD_AUTH_RDM_COUNTER) {\n\t\tuint64_t rdm;\n\t\tint err;\n\n#ifdef PRIVSEP\n\t\tif (IN_PRIVSEP(ctx)) {\n\t\t\terr = ps_root_getauthrdm(ctx, &rdm);\n\t\t} else\n#endif\n\t\t\terr = auth_get_rdm_monotonic(&rdm);\n\t\tif (err == -1)\n\t\t\treturn ++auth->last_replay;\n\n\t\tauth->last_replay = rdm;\n\t\treturn rdm;\n\t}\n\treturn get_next_rdm_monotonic_clock(auth);\n}\n\n/*\n * Encode a DHCP message.\n * Either we know which token to use from the server response\n * or we are using a basic configuration token.\n * token is the token to encrypt with.\n * m and mlen refer to the whole message.\n * mp is the DHCP type, pass it 4 or 6.\n * mt is the DHCP message type.\n * data and dlen refer to the authentication option within the message.\n */\nssize_t\ndhcp_auth_encode(struct dhcpcd_ctx *ctx, struct auth *auth,\n    const struct token *t, void *vm, size_t mlen, int mp, int mt, void *vdata,\n    size_t dlen)\n{\n\tuint64_t rdm;\n\tuint8_t hmac_code[HMAC_LENGTH];\n\ttime_t now;\n\tuint8_t hops, *p, *m, *data;\n\tuint32_t giaddr, secretid;\n\tbool auth_info;\n\n\t/* Ignore the token argument given to us - always send using the\n\t * configured token. */\n\tif (auth->protocol == AUTH_PROTO_TOKEN) {\n\t\tTAILQ_FOREACH(t, &auth->tokens, next) {\n\t\t\tif (t->secretid == auth->token_snd_secretid)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (t == NULL) {\n\t\t\terrno = EINVAL;\n\t\t\treturn -1;\n\t\t}\n\t\tif (t->expire) {\n\t\t\tif (time(&now) == -1)\n\t\t\t\treturn -1;\n\t\t\tif (t->expire < now) {\n\t\t\t\terrno = EPERM;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t}\n\n\tswitch (auth->protocol) {\n\tcase AUTH_PROTO_TOKEN:\n\tcase AUTH_PROTO_DELAYED:\n\tcase AUTH_PROTO_DELAYEDREALM:\n\t\t/* We don't ever send a reconf key */\n\t\tbreak;\n\tdefault:\n\t\terrno = ENOTSUP;\n\t\treturn -1;\n\t}\n\n\tswitch (auth->algorithm) {\n\tcase AUTH_ALG_NONE:\n\tcase AUTH_ALG_HMAC_MD5:\n\t\tbreak;\n\tdefault:\n\t\terrno = ENOTSUP;\n\t\treturn -1;\n\t}\n\n\tswitch (auth->rdm) {\n\tcase AUTH_RDM_MONOTONIC:\n\t\tbreak;\n\tdefault:\n\t\terrno = ENOTSUP;\n\t\treturn -1;\n\t}\n\n\t/* DISCOVER or INFORM messages don't write auth info */\n\tif ((mp == 4 && (mt == DHCP_DISCOVER || mt == DHCP_INFORM)) ||\n\t    (mp == 6 && (mt == DHCP6_SOLICIT || mt == DHCP6_INFORMATION_REQ)))\n\t\tauth_info = false;\n\telse\n\t\tauth_info = true;\n\n\t/* Work out the auth area size.\n\t * We only need to do this for DISCOVER messages */\n\tif (vdata == NULL) {\n\t\tdlen = 1 + 1 + 1 + 8;\n\t\tswitch (auth->protocol) {\n\t\tcase AUTH_PROTO_TOKEN:\n\t\t\tdlen += t->key_len;\n\t\t\tbreak;\n\t\tcase AUTH_PROTO_DELAYEDREALM:\n\t\t\tif (auth_info && t)\n\t\t\t\tdlen += t->realm_len;\n\t\t\t/* FALLTHROUGH */\n\t\tcase AUTH_PROTO_DELAYED:\n\t\t\tif (auth_info && t)\n\t\t\t\tdlen += sizeof(t->secretid) + sizeof(hmac_code);\n\t\t\tbreak;\n\t\t}\n\t\treturn (ssize_t)dlen;\n\t}\n\n\tif (dlen < 1 + 1 + 1 + 8) {\n\t\terrno = ENOBUFS;\n\t\treturn -1;\n\t}\n\n\t/* Ensure that d is inside m which *may* not be the case for DHPCPv4 */\n\tm = vm;\n\tdata = vdata;\n\tif (data < m || data > m + mlen || data + dlen > m + mlen) {\n\t\terrno = ERANGE;\n\t\treturn -1;\n\t}\n\n\t/* Write out our option */\n\t*data++ = auth->protocol;\n\t*data++ = auth->algorithm;\n\t/*\n\t * RFC 3315 21.4.4.1 says that SOLICIT in DELAYED authentication\n\t * should not set RDM or it's data.\n\t * An expired draft draft-ietf-dhc-dhcpv6-clarify-auth-01 suggets\n\t * this should not be set for INFORMATION REQ messages as well,\n\t * which is probably a good idea because both states start from zero.\n\t */\n\tif (auth_info ||\n\t    !(auth->protocol &\n\t\t(AUTH_PROTO_DELAYED | AUTH_PROTO_DELAYEDREALM))) {\n\t\t*data++ = auth->rdm;\n\t\tswitch (auth->rdm) {\n\t\tcase AUTH_RDM_MONOTONIC:\n\t\t\trdm = get_next_rdm_monotonic(ctx, auth);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t/* This block appeases gcc, clang doesn't need it */\n\t\t\trdm = get_next_rdm_monotonic(ctx, auth);\n\t\t\tbreak;\n\t\t}\n\t\trdm = htonll(rdm);\n\t\tmemcpy(data, &rdm, 8);\n\t} else {\n\t\t*data++ = 0;\t    /* rdm */\n\t\tmemset(data, 0, 8); /* replay detection data */\n\t}\n\tdata += 8;\n\tdlen -= 1 + 1 + 1 + 8;\n\n\t/* Special case as no hashing needs to be done. */\n\tif (auth->protocol == AUTH_PROTO_TOKEN) {\n\t\t/* Should be impossible, but still */\n\t\tif (t == NULL) {\n\t\t\terrno = EINVAL;\n\t\t\treturn -1;\n\t\t}\n\t\tif (dlen < t->key_len) {\n\t\t\terrno = ENOBUFS;\n\t\t\treturn -1;\n\t\t}\n\t\tmemcpy(data, t->key, t->key_len);\n\t\treturn (ssize_t)(dlen - t->key_len);\n\t}\n\n\t/* DISCOVER or INFORM messages don't write auth info */\n\tif (!auth_info)\n\t\treturn (ssize_t)dlen;\n\n\t/* Loading a saved lease without an authentication option */\n\tif (t == NULL)\n\t\treturn 0;\n\n\t/* Write out the Realm */\n\tif (auth->protocol == AUTH_PROTO_DELAYEDREALM) {\n\t\tif (dlen < t->realm_len) {\n\t\t\terrno = ENOBUFS;\n\t\t\treturn -1;\n\t\t}\n\t\tmemcpy(data, t->realm, t->realm_len);\n\t\tdata += t->realm_len;\n\t\tdlen -= t->realm_len;\n\t}\n\n\t/* Write out the SecretID */\n\tif (auth->protocol == AUTH_PROTO_DELAYED ||\n\t    auth->protocol == AUTH_PROTO_DELAYEDREALM) {\n\t\tif (dlen < sizeof(t->secretid)) {\n\t\t\terrno = ENOBUFS;\n\t\t\treturn -1;\n\t\t}\n\t\tsecretid = htonl(t->secretid);\n\t\tmemcpy(data, &secretid, sizeof(secretid));\n\t\tdata += sizeof(secretid);\n\t\tdlen -= sizeof(secretid);\n\t}\n\n\t/* Zero what's left, the MAC */\n\tmemset(data, 0, dlen);\n\n\t/* RFC3318, section 5.2 - zero giaddr and hops */\n\tif (mp == 4) {\n\t\tp = m + offsetof(struct bootp, hops);\n\t\thops = *p;\n\t\t*p = '\\0';\n\t\tp = m + offsetof(struct bootp, giaddr);\n\t\tmemcpy(&giaddr, p, sizeof(giaddr));\n\t\tmemset(p, 0, sizeof(giaddr));\n\t} else {\n\t\t/* appease GCC again */\n\t\thops = 0;\n\t\tgiaddr = 0;\n\t}\n\n\t/* Create our hash and write it out */\n\tswitch (auth->algorithm) {\n\tcase AUTH_ALG_HMAC_MD5:\n\t\thmac(\"md5\", t->key, t->key_len, m, mlen, hmac_code,\n\t\t    sizeof(hmac_code));\n\t\tmemcpy(data, hmac_code, sizeof(hmac_code));\n\t\tbreak;\n\t}\n\n\t/* RFC3318, section 5.2 - restore giaddr and hops */\n\tif (mp == 4) {\n\t\tp = m + offsetof(struct bootp, hops);\n\t\t*p = hops;\n\t\tp = m + offsetof(struct bootp, giaddr);\n\t\tmemcpy(p, &giaddr, sizeof(giaddr));\n\t}\n\n\t/* Done! */\n\treturn (int)(dlen - sizeof(hmac_code)); /* should be zero */\n}\n"
  },
  {
    "path": "src/auth.h",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef AUTH_H\n#define AUTH_H\n\n#include <stdint.h>\n\n#include \"queue.h\"\n\n#define DHCPCD_AUTH_SEND\t(1 << 0)\n#define DHCPCD_AUTH_REQUIRE\t(1 << 1)\n#define DHCPCD_AUTH_RDM_COUNTER (1 << 2)\n\n#define DHCPCD_AUTH_SENDREQUIRE (DHCPCD_AUTH_SEND | DHCPCD_AUTH_REQUIRE)\n\n#define AUTH_PROTO_TOKEN\t0\n#define AUTH_PROTO_DELAYED\t1\n#define AUTH_PROTO_DELAYEDREALM 2\n#define AUTH_PROTO_RECONFKEY\t3\n\n#define AUTH_ALG_NONE\t\t0\n#define AUTH_ALG_HMAC_MD5\t1\n\n#define AUTH_RDM_MONOTONIC\t0\n\nstruct token {\n\tTAILQ_ENTRY(token) next;\n\tuint32_t secretid;\n\tsize_t realm_len;\n\tunsigned char *realm;\n\tsize_t key_len;\n\tunsigned char *key;\n\ttime_t expire;\n};\n\nTAILQ_HEAD(token_head, token);\n\nstruct auth {\n\tint options;\n#ifdef AUTH\n\tuint8_t protocol;\n\tuint8_t algorithm;\n\tuint8_t rdm;\n\tuint64_t last_replay;\n\tuint8_t last_replay_set;\n\tstruct token_head tokens;\n\tuint32_t token_snd_secretid;\n\tuint32_t token_rcv_secretid;\n#endif\n};\n\nstruct authstate {\n\tuint64_t replay;\n\tstruct token *token;\n\tstruct token *reconf;\n};\n\nvoid dhcp_auth_reset(struct authstate *);\n\nconst struct token *dhcp_auth_validate(struct authstate *, const struct auth *,\n    const void *, size_t, int, int, const void *, size_t);\n\nstruct dhcpcd_ctx;\nssize_t dhcp_auth_encode(struct dhcpcd_ctx *, struct auth *,\n    const struct token *, void *, size_t, int, int, void *, size_t);\n\nint auth_get_rdm_monotonic(uint64_t *rdm);\n#endif\n"
  },
  {
    "path": "src/bpf-bsd.c",
    "content": "/*\n * BPF BSD interface\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/ioctl.h>\n#include <sys/socket.h>\n\n#include <net/bpf.h>\n\n#include <errno.h>\n#include <fcntl.h>\n#include <stddef.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"bpf.h\"\n#ifdef __sun\n#include \"bpf-dlpi.h\"\n#endif\n#include \"logerr.h\"\n\nconst char *bpf_name = \"Berkeley Packet Filter\";\n\nstruct bpf *\nbpf_open(const struct interface *ifp,\n    int (*filter)(const struct bpf *, const struct in_addr *),\n    const struct in_addr *ia)\n{\n\tstruct bpf *bpf;\n\tstruct bpf_version pv = { .bv_major = 0, .bv_minor = 0 };\n\tstruct ifreq ifr = { .ifr_flags = 0 };\n\tint ibuf_len = 0;\n#ifdef O_CLOEXEC\n#define BPF_OPEN_FLAGS O_RDWR | O_NONBLOCK | O_CLOEXEC\n#else\n#define BPF_OPEN_FLAGS O_RDWR | O_NONBLOCK\n#endif\n#ifdef BIOCIMMEDIATE\n\tunsigned int flags;\n#endif\n#ifndef O_CLOEXEC\n\tint fd_opts;\n#endif\n\n\tbpf = calloc(1, sizeof(*bpf));\n\tif (bpf == NULL)\n\t\treturn NULL;\n\n\t/* /dev/bpf is a cloner on modern kernels */\n\tbpf->bpf_fd = open(\"/dev/bpf\", BPF_OPEN_FLAGS);\n\n\t/* Support older kernels where /dev/bpf is not a cloner */\n\tif (bpf->bpf_fd == -1) {\n\t\tchar device[32];\n\t\tint n = 0;\n\n\t\tdo {\n\t\t\tsnprintf(device, sizeof(device), \"/dev/bpf%d\", n++);\n\t\t\tbpf->bpf_fd = open(device, BPF_OPEN_FLAGS);\n\t\t} while (bpf->bpf_fd == -1 && errno == EBUSY);\n\t}\n\n\tif (bpf->bpf_fd == -1)\n\t\tgoto eexit;\n\n\tbpf->bpf_ifp = ifp;\n\tbpf->bpf_flags = BPF_EOF;\n\n#ifndef O_CLOEXEC\n\tif ((fd_opts = fcntl(bpf->bpf_fd, F_GETFD)) == -1 ||\n\t    fcntl(bpf->bpf_fd, F_SETFD, fd_opts | FD_CLOEXEC) == -1)\n\t\tgoto eexit;\n#endif\n\n\tif (ioctl(bpf->bpf_fd, BIOCVERSION, &pv) == -1)\n\t\tgoto eexit;\n\tif (pv.bv_major != BPF_MAJOR_VERSION ||\n\t    pv.bv_minor < BPF_MINOR_VERSION) {\n\t\tlogerrx(\"BPF version mismatch - recompile\");\n\t\tgoto eexit;\n\t}\n\n\tstrlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));\n\tif (ioctl(bpf->bpf_fd, BIOCSETIF, &ifr) == -1)\n\t\tgoto eexit;\n\n#ifdef BIOCIMMEDIATE\n\tflags = 1;\n\tif (ioctl(bpf->bpf_fd, BIOCIMMEDIATE, &flags) == -1)\n\t\tgoto eexit;\n#endif\n\n\tif (filter(bpf, ia) != 0)\n\t\tgoto eexit;\n\n\t/* Get the required BPF buffer length from the kernel. */\n\tif (ioctl(bpf->bpf_fd, BIOCGBLEN, &ibuf_len) == -1)\n\t\tgoto eexit;\n\n\tbpf->bpf_size = (size_t)ibuf_len;\n\tbpf->bpf_buffer = malloc(bpf->bpf_size);\n\tif (bpf->bpf_buffer == NULL)\n\t\tgoto eexit;\n\n#ifdef __sun\n\tif (bpf_dlpi_open(bpf) == -1)\n\t\tgoto eexit;\n#endif\n\n\treturn bpf;\n\neexit:\n\tbpf_close(bpf);\n\treturn NULL;\n}\n\n/* BPF requires that we read the entire buffer.\n * So we pass the buffer in the API so we can loop on >1 packet. */\nssize_t\nbpf_read(struct bpf *bpf, void *data, size_t len)\n{\n\tssize_t bytes;\n\tstruct bpf_hdr packet;\n\tsize_t hdr_max;\n\tconst uint8_t *payload;\n\n\tbpf->bpf_flags &= ~BPF_EOF;\n\tif (bpf->bpf_len == 0) {\n\t\tbytes = read(bpf->bpf_fd, bpf->bpf_buffer, bpf->bpf_size);\n#ifdef __sun\n\t\t/* After 2^31 bytes, the kernel offset overflows.\n\t\t * To work around this bug, lseek 0. */\n\t\tif (bytes == -1 && errno == EINVAL) {\n\t\t\tlseek(bpf->bpf_fd, 0, SEEK_SET);\n\t\t\treturn 0;\n\t\t}\n#endif\n\t\tif (bytes == -1 || bytes == 0)\n\t\t\treturn bytes;\n\t\tbpf->bpf_len = (size_t)bytes;\n\t\tbpf->bpf_pos = 0;\n\t}\n\n\tif (bpf->bpf_pos + sizeof(packet) > bpf->bpf_len) {\n\t\terrno = EINVAL;\n\t\tgoto err;\n\t}\n\n\tpayload = (const uint8_t *)bpf->bpf_buffer + bpf->bpf_pos;\n\tmemcpy(&packet, payload, sizeof(packet));\n\n\thdr_max = SIZE_MAX - packet.bh_caplen;\n\tif (packet.bh_hdrlen > hdr_max) {\n\t\terrno = EOVERFLOW;\n\t\tgoto err;\n\t}\n\tif (packet.bh_hdrlen + packet.bh_caplen > bpf->bpf_len - bpf->bpf_pos) {\n\t\terrno = EBADMSG;\n\t\tgoto err;\n\t}\n\n\tpayload += packet.bh_hdrlen;\n\tif (packet.bh_caplen > len)\n\t\tbytes = (ssize_t)len;\n\telse\n\t\tbytes = (ssize_t)packet.bh_caplen;\n\n\tif (bpf_frame_bcast(bpf->bpf_ifp, payload) == 0)\n\t\tbpf->bpf_flags |= BPF_BCAST;\n\telse\n\t\tbpf->bpf_flags &= ~BPF_BCAST;\n\tmemcpy(data, payload, (size_t)bytes);\n\n\tbpf->bpf_pos += BPF_WORDALIGN(packet.bh_hdrlen + packet.bh_caplen);\n\tif (bpf->bpf_pos >= bpf->bpf_len) {\n\t\tbpf->bpf_len = bpf->bpf_pos = 0;\n\t\tbpf->bpf_flags |= BPF_EOF;\n\t}\n\treturn bytes;\n\nerr:\n\tbpf->bpf_len = bpf->bpf_pos = 0;\n\tbpf->bpf_flags |= BPF_EOF;\n\treturn -1;\n}\n\nint\nbpf_setfilter(const struct bpf *bpf, void *filter, unsigned int filter_len)\n{\n\tstruct bpf_program pf = { .bf_insns = filter, .bf_len = filter_len };\n\n\t/* Install the filter. */\n\treturn ioctl(bpf->bpf_fd, BIOCSETF, &pf);\n}\n\nint\nbpf_setwfilter(const struct bpf *bpf, void *filter, unsigned int filter_len)\n{\n#ifdef BIOCSETWF\n\tstruct bpf_program pf = { .bf_insns = filter, .bf_len = filter_len };\n\n\t/* Install the filter. */\n\treturn ioctl(bpf->bpf_fd, BIOCSETWF, &pf);\n#else\n#warning No BIOCSETWF support - a compromised BPF can be used as a raw socket\n\tUNUSED(bpf);\n\tUNUSED(filter);\n\tUNUSED(filter_len);\n\terrno = ENOSYS;\n\treturn -1;\n#endif\n}\n\nint\nbpf_lockfilter(const struct bpf *bpf)\n{\n#ifdef BIOCLOCK\n\treturn ioctl(bpf->bpf_fd, BIOCLOCK);\n#else\n\tUNUSED(bpf);\n\terrno = ENOSYS;\n\treturn -1;\n#endif\n}\n\n#if !defined(__sun)\n/* SunOS is special too - sending via BPF goes nowhere. */\nssize_t\nbpf_writev(const struct bpf *bpf, struct iovec *iov, int iovcnt)\n{\n\treturn writev(bpf->bpf_fd, iov, iovcnt);\n}\n#endif\n\nvoid\nbpf_close(struct bpf *bpf)\n{\n#ifdef __sun\n\tbpf_dlpi_close(bpf);\n#endif\n\tif (bpf->bpf_fd != -1)\n\t\tclose(bpf->bpf_fd);\n\tfree(bpf->bpf_buffer);\n\tfree(bpf);\n}\n"
  },
  {
    "path": "src/bpf-dlpi.c",
    "content": "/*\n * BPF DLPI interface\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/types.h>\n#include <net/if.h>\n#include <netinet/if_ether.h>\n\n#include <errno.h>\n#include <libdlpi.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"bpf.h\"\n#include \"bpf-dlpi.h\"\n\nstruct bpf_dlpi {\n\tdlpi_handle_t bd_handle;\n\tvoid *bd_buffer;\n\tsize_t bd_bufferlen;\n};\n\nint\nbpf_dlpi_open(struct bpf *bpf)\n{\n\tconst struct interface *ifp = bpf->bpf_ifp;\n\tstruct bpf_dlpi *bd;\n\tint mtu;\n\n\tbd = calloc(1, sizeof(*bd));\n\tif (bd == NULL)\n\t\treturn -1;\n\n\tif (dlpi_open(ifp->name, &bd->bd_handle, DLPI_RAW) != DLPI_SUCCESS) {\n\t\tfree(bd);\n\t\treturn -1;\n\t}\n\n\tbpf->bpf_handle = bd;\n\n\tif (dlpi_bind(bd->bd_handle, DLPI_ANY_SAP, NULL) != DLPI_SUCCESS)\n\t\treturn -1;\n\n\tmtu = ifp->mtu ? ifp->mtu : ETHERMTU;\n\tbd->bd_bufferlen = bpf_frame_header_len(ifp) + (size_t)mtu;\n\tbd->bd_buffer = malloc(bd->bd_bufferlen);\n\tif (bpf->bpf_buffer == NULL)\n\t\treturn -1;\n\n\treturn 0;\n}\n\nssize_t\nbpf_writev(const struct bpf *bpf, struct iovec *iov, int iovcnt)\n{\n\tstruct bpf_dlpi *bd = bpf->bpf_handle;\n\tint i;\n\tsize_t len = 0;\n\tuint8_t *bp = bd->bd_buffer;\n\n\tfor (i = 0; i < iovcnt; i++) {\n\t\t/* This should be impossible. */\n\t\tif (iov[i].iov_len > bd->bd_bufferlen - len) {\n\t\t\terrno = ENOBUFS;\n\t\t\treturn -1;\n\t\t}\n\n\t\tmemcpy(bp, iov[i].iov_base, iov[i].iov_len);\n\t\tbp += iov[i].iov_len;\n\t\tlen += iov[i].iov_len;\n\t}\n\n\ti = dlpi_send(bd->bd_handle, NULL, 0, bd->bd_buffer, len, NULL);\n\treturn i == DLPI_SUCCESS ? (ssize_t)len : -1;\n}\n\nvoid\nbpf_dlpi_close(struct bpf *bpf)\n{\n\tstruct bpf_dlpi *bd = bpf->bpf_handle;\n\n\tif (bd == NULL)\n\t\treturn;\n\n\tdlpi_close(bd->bd_handle);\n\tfree(bd->bd_buffer);\n\tfree(bd);\n}\n"
  },
  {
    "path": "src/bpf-dlpi.h",
    "content": "/*\n * BPF DLPI interface\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef BPF_DLPI_HEADER\n#define BPF_DLPI_HEADER\n\nstruct bpf;\n\nint bpf_dlpi_open(struct bpf *);\nvoid bpf_dlpi_close(struct bpf *);\n#endif\n"
  },
  {
    "path": "src/bpf-linux.c",
    "content": "/*\n * BPF Linux interface\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n#include <sys/ioctl.h>\n#include <sys/socket.h>\n\n#include <linux/if_ether.h>\n#include <linux/if_packet.h>\n#include <linux/filter.h>\n\n#include <errno.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"bpf.h\"\n\nconst char *bpf_name = \"Packet Socket\";\n\nstruct bpf *\nbpf_open(const struct interface *ifp,\n    int (*filter)(const struct bpf *, const struct in_addr *),\n    const struct in_addr *ia)\n{\n\tstruct bpf *bpf;\n\tunion sockunion {\n\t\tstruct sockaddr sa;\n\t\tstruct sockaddr_ll sll;\n\t\tstruct sockaddr_storage ss;\n\t} su = { .sll = {\n\t\t     .sll_family = PF_PACKET,\n\t\t     .sll_protocol = htons(ETH_P_ALL),\n\t\t     .sll_ifindex = (int)ifp->index,\n\t\t } };\n\tint mtu;\n#ifdef PACKET_AUXDATA\n\tint n;\n#endif\n\n\tbpf = calloc(1, sizeof(*bpf));\n\tif (bpf == NULL)\n\t\treturn NULL;\n\tbpf->bpf_ifp = ifp;\n\tbpf->bpf_flags = BPF_EOF;\n\tbpf->bpf_fd = -1;\n\n\tmtu = ifp->mtu ? ifp->mtu : ETH_DATA_LEN;\n\tbpf->bpf_size = bpf_frame_header_len(ifp) + (size_t)mtu;\n\tbpf->bpf_buffer = malloc(bpf->bpf_size);\n\tif (bpf->bpf_buffer == NULL)\n\t\tgoto eexit;\n\n\tbpf->bpf_fd = xsocket(PF_PACKET, SOCK_RAW | SOCK_CXNB,\n\t    htons(ETH_P_ALL));\n\tif (bpf->bpf_fd == -1)\n\t\tgoto eexit;\n\n\t/* We cannot validate the correct interface,\n\t * so we MUST set this first. */\n\tif (bind(bpf->bpf_fd, &su.sa, sizeof(su.sll)) == -1)\n\t\tgoto eexit;\n\n\tif (filter(bpf, ia) != 0)\n\t\tgoto eexit;\n\n\t/* In the ideal world, this would be set before the bind and filter. */\n#ifdef PACKET_AUXDATA\n\tn = 1;\n\tif (setsockopt(bpf->bpf_fd, SOL_PACKET, PACKET_AUXDATA, &n,\n\t\tsizeof(n)) != 0) {\n\t\tif (errno != ENOPROTOOPT)\n\t\t\tgoto eexit;\n\t}\n#endif\n\n\t/*\n\t * At this point we could have received packets for the wrong\n\t * interface or which don't pass the filter.\n\t * Linux should flush upon setting the filter like every other OS.\n\t * There is no way of flushing them from userland.\n\t * As such, consumers need to inspect each packet to ensure it's valid.\n\t * Or to put it another way, don't trust the Linux BPF filter.\n\t */\n\n\treturn bpf;\n\neexit:\n\tbpf_close(bpf);\n\treturn NULL;\n}\n\n/* BPF requires that we read the entire buffer.\n * So we pass the buffer in the API so we can loop on >1 packet. */\nssize_t\nbpf_read(struct bpf *bpf, void *data, size_t len)\n{\n\tssize_t bytes;\n\tstruct iovec iov = {\n\t\t.iov_base = bpf->bpf_buffer,\n\t\t.iov_len = bpf->bpf_size,\n\t};\n\tstruct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 };\n#ifdef PACKET_AUXDATA\n\tunion {\n\t\tstruct cmsghdr hdr;\n\t\tuint8_t buf[CMSG_SPACE(sizeof(struct tpacket_auxdata))];\n\t} cmsgbuf = { .buf = { 0 } };\n\tstruct cmsghdr *cmsg;\n\tstruct tpacket_auxdata *aux;\n#endif\n\n#ifdef PACKET_AUXDATA\n\tmsg.msg_control = cmsgbuf.buf;\n\tmsg.msg_controllen = sizeof(cmsgbuf.buf);\n#endif\n\n\tbytes = recvmsg(bpf->bpf_fd, &msg, 0);\n\tif (bytes == -1)\n\t\treturn -1;\n\tif (msg.msg_flags & MSG_TRUNC) {\n\t\terrno = ENOBUFS;\n\t\treturn -1;\n\t}\n\tbpf->bpf_flags |= BPF_EOF; /* We only ever read one packet. */\n\tbpf->bpf_flags &= ~BPF_PARTIALCSUM;\n\tif (bytes) {\n\t\tif (bpf_frame_bcast(bpf->bpf_ifp, bpf->bpf_buffer) == 0)\n\t\t\tbpf->bpf_flags |= BPF_BCAST;\n\t\telse\n\t\t\tbpf->bpf_flags &= ~BPF_BCAST;\n\t\tif ((size_t)bytes > len)\n\t\t\tbytes = (ssize_t)len;\n\t\tmemcpy(data, bpf->bpf_buffer, (size_t)bytes);\n#ifdef PACKET_AUXDATA\n\t\tfor (cmsg = CMSG_FIRSTHDR(&msg); cmsg;\n\t\t    cmsg = CMSG_NXTHDR(&msg, cmsg)) {\n\t\t\tif (cmsg->cmsg_level == SOL_PACKET &&\n\t\t\t    cmsg->cmsg_type == PACKET_AUXDATA) {\n\t\t\t\taux = (void *)CMSG_DATA(cmsg);\n\t\t\t\tif (aux->tp_status & TP_STATUS_CSUMNOTREADY)\n\t\t\t\t\tbpf->bpf_flags |= BPF_PARTIALCSUM;\n\t\t\t}\n\t\t}\n#endif\n\t}\n\treturn bytes;\n}\n\nint\nbpf_setfilter(const struct bpf *bpf, void *filter, unsigned int filter_len)\n{\n\tstruct sock_fprog pf = {\n\t\t.filter = filter,\n\t\t.len = (unsigned short)filter_len,\n\t};\n\tint s = bpf->bpf_fd;\n\n\t/* Install the filter. */\n\treturn setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &pf, sizeof(pf));\n}\n\nint\nbpf_setwfilter(__unused const struct bpf *bpf, __unused void *filter, __unused unsigned int filter_len)\n{\n#warning A compromised PF_PACKET socket can be used as a raw socket\n\n\terrno = ENOSYS;\n\treturn -1;\n}\n\nint\nbpf_lockfilter(const struct bpf *bpf)\n{\n#ifdef SO_LOCK_FILTER\n\tint fd = bpf->bpf_fd, on = 1;\n\n\treturn setsockopt(fd, SOL_SOCKET, SO_LOCK_FILTER, &on, sizeof(on));\n#else\n\tUNUSED(bpf);\n\terrno = ENOSYS;\n\treturn -1;\n#endif\n}\n\nssize_t\nbpf_writev(const struct bpf *bpf, struct iovec *iov, int iovcnt)\n{\n\treturn writev(bpf->bpf_fd, iov, iovcnt);\n}\n\nvoid\nbpf_close(struct bpf *bpf)\n{\n\tif (bpf->bpf_fd != -1)\n\t\tclose(bpf->bpf_fd);\n\tfree(bpf->bpf_buffer);\n\tfree(bpf);\n}\n\n"
  },
  {
    "path": "src/bpf-pcap.c",
    "content": "/*\n * BPF libpcap interface\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2025 Joan Lledó <jlledom@member.fsf.org>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/ioctl.h>\n\n#include <errno.h>\n#include <pcap.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"bpf.h\"\n#include \"logerr.h\"\n\n#define PCAP_CHECK(call, name)                                         \\\n\tdo {                                                           \\\n\t\tint status = (call);                                   \\\n\t\tif (status < 0) {                                      \\\n\t\t\tlogerrx(\"%s: %s failed: %s\", __func__, name,   \\\n\t\t\t    pcap_statustostr(status));                 \\\n\t\t\tgoto eexit;                                    \\\n\t\t} else if (status > 0)                                 \\\n\t\t\tlogwarnx(\"%s: %s warning: %s\", __func__, name, \\\n\t\t\t    pcap_statustostr(status));                 \\\n\t} while (0)\n\n#define ETH_MTU 1500\n\nconst char *bpf_name = \"Berkeley Packet Filter (libpcap)\";\n\nstruct bpf *\nbpf_open(const struct interface *ifp,\n    int (*filter)(const struct bpf *, const struct in_addr *),\n    const struct in_addr *ia)\n{\n\tint err;\n\tstruct bpf *bpf;\n\tpcap_t *handle;\n\tchar errbuf[PCAP_ERRBUF_SIZE];\n\tint mtu;\n\n\tbpf = calloc(1, sizeof(*bpf));\n\tif (bpf == NULL)\n\t\treturn NULL;\n\n\tmtu = ifp->mtu ? ifp->mtu : ETH_MTU;\n\tbpf->bpf_ifp = ifp;\n\tbpf->bpf_size = bpf_frame_header_len(ifp) + (size_t)mtu;\n\tbpf->bpf_buffer = malloc(bpf->bpf_size);\n\tif (bpf->bpf_buffer == NULL)\n\t\tgoto eexit;\n\tbpf->bpf_len = 0;\n\tbpf->bpf_pos = 0;\n\tbpf->bpf_flags = BPF_EOF;\n\n\tbpf->bpf_handle = handle = pcap_create(ifp->name, errbuf);\n\tif (handle == NULL) {\n\t\tlogerrx(\"%s: pcap_create: %s\", __func__, errbuf);\n\t\tgoto eexit;\n\t}\n\n\tPCAP_CHECK(pcap_set_snaplen(handle, (int)bpf->bpf_size),\n\t    \"pcap_set_snaplen\");\n\tPCAP_CHECK(pcap_set_promisc(handle, 0), \"pcap_set_promisc\");\n#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE\n\tPCAP_CHECK(pcap_set_immediate_mode(handle, 1),\n#endif\n\t    \"pcap_set_immediate_mode\");\n\n\terr = pcap_activate(handle);\n\tif (err != 0) {\n\t\tif (err < 0) {\n\t\t\tlogerrx(\"%s: pcap_activate failed: %s\", __func__,\n\t\t\t    pcap_statustostr(err));\n\t\t\tgoto eexit;\n\t\t}\n\t\tlogwarnx(\"%s: pcap_activate warning: %s\", __func__,\n\t\t    pcap_statustostr(err));\n\t}\n\n\tbpf->bpf_fd = pcap_get_selectable_fd(handle);\n\tif (bpf->bpf_fd < 0) {\n\t\tlogerrx(\"%s: pcap_get_selectable_fd failed\", __func__);\n\t\tgoto eexit;\n\t}\n\n\tif (filter(bpf, ia) != 0)\n\t\tgoto eexit;\n\n\treturn bpf;\n\neexit:\n\tbpf_close(bpf);\n\treturn NULL;\n}\n\nssize_t\nbpf_read(struct bpf *bpf, void *data, size_t len)\n{\n\tstruct pcap_pkthdr *pkt_header;\n\tconst u_char *pkt_data;\n\tsize_t cap_len;\n\tint err;\n\n\tbpf->bpf_flags |= BPF_EOF; /* We only read one packet per call */\n\n\terr = pcap_next_ex(bpf->bpf_handle, &pkt_header, &pkt_data);\n\n\tif (err == 0)\n\t\treturn 0;\n\tif (err < 0)\n\t\treturn -1;\n\n\t/* Packet read successfully */\n\tcap_len = pkt_header->caplen;\n\tif (cap_len > len)\n\t\tcap_len = len;\n\tmemcpy(data, pkt_data, cap_len);\n\n\tif (bpf_frame_bcast(bpf->bpf_ifp, pkt_data) == 0)\n\t\tbpf->bpf_flags |= BPF_BCAST;\n\telse\n\t\tbpf->bpf_flags &= ~BPF_BCAST;\n\n\treturn (ssize_t)cap_len;\n}\n\nssize_t\nbpf_writev(const struct bpf *bpf, struct iovec *iov, int iovcnt)\n{\n\tint i;\n\tsize_t len = 0;\n\tuint8_t *bp = bpf->bpf_buffer;\n\n\tfor (i = 0; i < iovcnt; i++) {\n\t\t/* This should be impossible. */\n\t\tif (iov[i].iov_len > bpf->bpf_size - len) {\n\t\t\terrno = ENOBUFS;\n\t\t\treturn -1;\n\t\t}\n\n\t\tmemcpy(bp, iov[i].iov_base, iov[i].iov_len);\n\t\tbp += iov[i].iov_len;\n\t\tlen += iov[i].iov_len;\n\t}\n\n\ti = pcap_inject(bpf->bpf_handle, bpf->bpf_buffer, len);\n\tif (i < 0) {\n\t\tlogerrx(\"%s: %s\", __func__, pcap_geterr(bpf->bpf_handle));\n\t\treturn -1;\n\t}\n\treturn i;\n}\n\nint\nbpf_setfilter(const struct bpf *bpf, void *filter, unsigned int filter_len)\n{\n\tstruct bpf_program pf = { .bf_insns = filter, .bf_len = filter_len };\n\n\t/* Install the filter. */\n\treturn pcap_setfilter(bpf->bpf_handle, &pf);\n}\n\nint\nbpf_setwfilter(const struct bpf *bpf, void *filter, unsigned int filter_len)\n{\n#ifdef HAVE_PCAP_SETWRITEFILTER\n\tstruct bpf_program pf = { .bf_insns = filter, .bf_len = filter_len };\n\n\treturn pcap_setwritefilter(bpf->bpf_handle, &pf);\n#elif defined(BIOCSETWF)\n\tstruct bpf_program pf = { .bf_insns = filter, .bf_len = filter_len };\n\tint fd = pcap_fileno(bpf->bpf_handle);\n\n\tif (fd == -1) {\n\t\terrno = EBADF;\n\t\treturn -1;\n\t}\n\treturn ioctl(fd, BIOCSETWF, &pf);\n#else\n#warning a compromised libpcap can inject arbitary packets\n\tUNUSED(bpf);\n\tUNUSED(filter);\n\tUNUSED(filter_len);\n\terrno = ENOSYS;\n\treturn -1;\n#endif\n}\n\nint\nbpf_lockfilter(const struct bpf *bpf)\n{\n#ifdef HAVE_PCAP_LOCKFILTER\n\treturn pcap_lockfilter(bpf->bpf_handle);\n#elif defined(BIOCLOCK)\n\tint fd = pcap_fileno(bpf->bpf_handle);\n\n\tif (fd == -1) {\n\t\terrno = EBADF;\n\t\treturn -1;\n\t}\n\treturn ioctl(fd, BIOCLOCK);\n#else\n\tUNUSED(bpf);\n\terrno = ENOSYS;\n\treturn -1;\n#endif\n}\n\nvoid\nbpf_close(struct bpf *bpf)\n{\n\tif (bpf->bpf_handle != NULL)\n\t\tpcap_close(bpf->bpf_handle);\n\tfree(bpf->bpf_buffer);\n\tfree(bpf);\n}\n"
  },
  {
    "path": "src/bpf.c",
    "content": "/*\n * dhcpcd: BPF arp and bootp filtering\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/ioctl.h>\n#include <sys/socket.h>\n\n#include <net/if.h>\n#include <netinet/in.h>\n#include <netinet/if_ether.h>\n\n#include <arpa/inet.h>\n\n#ifdef __linux__\n/* Special BPF snowflake. */\n#include <linux/filter.h>\n#define bpf_insn sock_filter\n#else\n#include <net/bpf.h>\n#endif\n\n#include <errno.h>\n#include <fcntl.h>\n#include <stddef.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"arp.h\"\n#include \"bpf.h\"\n#include \"common.h\"\n#include \"dhcp.h\"\n#include \"if.h\"\n#include \"logerr.h\"\n\n/* BPF helper macros */\n#ifdef __linux__\n#define BPF_WHOLEPACKET 0x7fffffff /* work around buggy LPF filters */\n#else\n#define BPF_WHOLEPACKET ~0U\n#endif\n\n/* Macros to update the BPF structure */\n#define BPF_SET_STMT(insn, c, v)           \\\n\t{                                  \\\n\t\t(insn)->code = (c);        \\\n\t\t(insn)->jt = 0;            \\\n\t\t(insn)->jf = 0;            \\\n\t\t(insn)->k = (uint32_t)(v); \\\n\t}\n\n#define BPF_SET_JUMP(insn, c, v, t, f)     \\\n\t{                                  \\\n\t\t(insn)->code = (c);        \\\n\t\t(insn)->jt = (t);          \\\n\t\t(insn)->jf = (f);          \\\n\t\t(insn)->k = (uint32_t)(v); \\\n\t}\n\nsize_t\nbpf_frame_header_len(const struct interface *ifp)\n{\n\tswitch (ifp->hwtype) {\n\tcase ARPHRD_ETHER:\n\t\treturn sizeof(struct ether_header);\n\tdefault:\n\t\treturn 0;\n\t}\n}\n\nvoid *\nbpf_frame_header_src(const struct interface *ifp, void *fh, size_t *len)\n{\n\tuint8_t *f = fh;\n\n\tswitch (ifp->hwtype) {\n\tcase ARPHRD_ETHER:\n\t\t*len = sizeof(((struct ether_header *)0)->ether_shost);\n\t\treturn f + offsetof(struct ether_header, ether_shost);\n\tdefault:\n\t\t*len = 0;\n\t\terrno = ENOTSUP;\n\t\treturn NULL;\n\t}\n}\n\nvoid *\nbpf_frame_header_dst(const struct interface *ifp, void *fh, size_t *len)\n{\n\tuint8_t *f = fh;\n\n\tswitch (ifp->hwtype) {\n\tcase ARPHRD_ETHER:\n\t\t*len = sizeof(((struct ether_header *)0)->ether_dhost);\n\t\treturn f + offsetof(struct ether_header, ether_dhost);\n\tdefault:\n\t\t*len = 0;\n\t\terrno = ENOTSUP;\n\t\treturn NULL;\n\t}\n}\n\nstatic const uint8_t etherbcastaddr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };\n\nint\nbpf_frame_bcast(const struct interface *ifp, const void *frame)\n{\n\tswitch (ifp->hwtype) {\n\tcase ARPHRD_ETHER:\n\t\treturn memcmp((const char *)frame +\n\t\t\toffsetof(struct ether_header, ether_dhost),\n\t\t    etherbcastaddr, sizeof(etherbcastaddr));\n\tdefault:\n\t\treturn -1;\n\t}\n}\n\nssize_t\nbpf_send(const struct bpf *bpf, uint16_t protocol, const void *data, size_t len)\n{\n\tstruct iovec iov[2];\n\tstruct ether_header eh;\n\n\tswitch (bpf->bpf_ifp->hwtype) {\n\tcase ARPHRD_ETHER:\n\t\tmemset(&eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));\n\t\tmemcpy(&eh.ether_shost, bpf->bpf_ifp->hwaddr,\n\t\t    sizeof(eh.ether_shost));\n\t\teh.ether_type = htons(protocol);\n\t\tiov[0].iov_base = &eh;\n\t\tiov[0].iov_len = sizeof(eh);\n\t\tbreak;\n\tdefault:\n\t\tiov[0].iov_base = NULL;\n\t\tiov[0].iov_len = 0;\n\t\tbreak;\n\t}\n\tiov[1].iov_base = UNCONST(data);\n\tiov[1].iov_len = len;\n\n\treturn bpf_writev(bpf, iov, __arraycount(iov));\n}\n\n#ifdef ARP\n#define BPF_CMP_HWADDR_LEN ((((HWADDR_LEN / 4) + 2) * 2) + 1)\nstatic unsigned int\nbpf_cmp_hwaddr(struct bpf_insn *bpf, size_t bpf_len, size_t off, bool equal,\n    const uint8_t *hwaddr, size_t hwaddr_len)\n{\n\tstruct bpf_insn *bp;\n\tsize_t maclen, nlft, njmps;\n\tuint32_t mac32;\n\tuint16_t mac16;\n\tuint8_t jt, jf;\n\n\t/* Calc the number of jumps */\n\tif ((hwaddr_len / 4) >= 128) {\n\t\terrno = EINVAL;\n\t\treturn 0;\n\t}\n\tnjmps = (hwaddr_len / 4) * 2; /* 2 instructions per check */\n\t/* We jump after the 1st check. */\n\tif (njmps)\n\t\tnjmps -= 2;\n\tnlft = hwaddr_len % 4;\n\tif (nlft) {\n\t\tnjmps += (nlft / 2) * 2;\n\t\tnlft = nlft % 2;\n\t\tif (nlft)\n\t\t\tnjmps += 2;\n\t}\n\n\t/* Skip to positive finish. */\n\tnjmps++;\n\tif (equal) {\n\t\tjt = (uint8_t)njmps;\n\t\tjf = 0;\n\t} else {\n\t\tjt = 0;\n\t\tjf = (uint8_t)njmps;\n\t}\n\n\tbp = bpf;\n\tfor (; hwaddr_len > 0;\n\t    hwaddr += maclen, hwaddr_len -= maclen, off += maclen) {\n\t\tif (bpf_len < 3) {\n\t\t\terrno = ENOBUFS;\n\t\t\treturn 0;\n\t\t}\n\t\tbpf_len -= 3;\n\n\t\tif (hwaddr_len >= 4) {\n\t\t\tmaclen = sizeof(mac32);\n\t\t\tmemcpy(&mac32, hwaddr, maclen);\n\t\t\tBPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND, off);\n\t\t\tbp++;\n\t\t\tBPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,\n\t\t\t    htonl(mac32), jt, jf);\n\t\t} else if (hwaddr_len >= 2) {\n\t\t\tmaclen = sizeof(mac16);\n\t\t\tmemcpy(&mac16, hwaddr, maclen);\n\t\t\tBPF_SET_STMT(bp, BPF_LD + BPF_H + BPF_IND, off);\n\t\t\tbp++;\n\t\t\tBPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,\n\t\t\t    htons(mac16), jt, jf);\n\t\t} else {\n\t\t\tmaclen = sizeof(*hwaddr);\n\t\t\tBPF_SET_STMT(bp, BPF_LD + BPF_B + BPF_IND, off);\n\t\t\tbp++;\n\t\t\tBPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, *hwaddr, jt,\n\t\t\t    jf);\n\t\t}\n\t\tif (jt)\n\t\t\tjt = (uint8_t)(jt - 2);\n\t\tif (jf)\n\t\t\tjf = (uint8_t)(jf - 2);\n\t\tbp++;\n\t}\n\n\t/* Last step is always return failure.\n\t * Next step is a positive finish. */\n\tBPF_SET_STMT(bp, BPF_RET + BPF_K, 0);\n\tbp++;\n\n\treturn (unsigned int)(bp - bpf);\n}\n#endif\n\n#ifdef ARP\nstatic const struct bpf_insn bpf_arp_ether[] = {\n\t/* Check this is an ARP packet. */\n\tBPF_STMT(BPF_LD + BPF_H + BPF_ABS,\n\t    offsetof(struct ether_header, ether_type)),\n\tBPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_ARP, 1, 0),\n\tBPF_STMT(BPF_RET + BPF_K, 0),\n\n\t/* Load frame header length into X */\n\tBPF_STMT(BPF_LDX + BPF_W + BPF_IMM, sizeof(struct ether_header)),\n\n\t/* Make sure the hardware type matches. */\n\tBPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_hrd)),\n\tBPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0),\n\tBPF_STMT(BPF_RET + BPF_K, 0),\n\n\t/* Make sure the hardware length matches. */\n\tBPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct arphdr, ar_hln)),\n\tBPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K,\n\t    sizeof(((struct ether_arp *)0)->arp_sha), 1, 0),\n\tBPF_STMT(BPF_RET + BPF_K, 0),\n};\n#define BPF_ARP_ETHER_LEN __arraycount(bpf_arp_ether)\n\nstatic const struct bpf_insn bpf_arp_filter[] = {\n\t/* Make sure this is for IP. */\n\tBPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_pro)),\n\tBPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0),\n\tBPF_STMT(BPF_RET + BPF_K, 0),\n\t/* Make sure this is an ARP REQUEST. */\n\tBPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_op)),\n\tBPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0),\n\t/* or ARP REPLY. */\n\tBPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0),\n\tBPF_STMT(BPF_RET + BPF_K, 0),\n\t/* Make sure the protocol length matches. */\n\tBPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct arphdr, ar_pln)),\n\tBPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(in_addr_t), 1, 0),\n\tBPF_STMT(BPF_RET + BPF_K, 0),\n};\n#define BPF_ARP_FILTER_LEN __arraycount(bpf_arp_filter)\n\n/* One address is two checks of two statements. */\n#define BPF_NADDRS\t  1\n#define BPF_ARP_ADDRS_LEN 5 + ((BPF_NADDRS * 2) * 2)\n\n#define BPF_ARP_LEN                                                   \\\n\tBPF_ARP_ETHER_LEN + BPF_ARP_FILTER_LEN + BPF_CMP_HWADDR_LEN + \\\n\t    BPF_ARP_ADDRS_LEN\n\nstatic int\nbpf_arp_rw(const struct bpf *bpf, const struct in_addr *ia, bool recv)\n{\n\tconst struct interface *ifp = bpf->bpf_ifp;\n\tstruct bpf_insn buf[BPF_ARP_LEN + 1];\n\tstruct bpf_insn *bp;\n\tuint16_t arp_len;\n\tunsigned int len;\n\n\tbp = buf;\n\t/* Check frame header. */\n\tswitch (ifp->hwtype) {\n\tcase ARPHRD_ETHER:\n\t\tmemcpy(bp, bpf_arp_ether, sizeof(bpf_arp_ether));\n\t\tbp += BPF_ARP_ETHER_LEN;\n\t\tarp_len = sizeof(struct ether_header) +\n\t\t    sizeof(struct ether_arp);\n\t\tbreak;\n\tdefault:\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\t/* Copy in the main filter. */\n\tmemcpy(bp, bpf_arp_filter, sizeof(bpf_arp_filter));\n\tbp += BPF_ARP_FILTER_LEN;\n\n\t/* Ensure it's not from us. */\n\tbp += bpf_cmp_hwaddr(bp, BPF_CMP_HWADDR_LEN, sizeof(struct arphdr),\n\t    !recv, ifp->hwaddr, ifp->hwlen);\n\n\t/* Match sender protocol address */\n\tBPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,\n\t    sizeof(struct arphdr) + ifp->hwlen);\n\tbp++;\n\tBPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, htonl(ia->s_addr), 0, 1);\n\tbp++;\n\tBPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len);\n\tbp++;\n\n\t/* If we didn't match sender, then we're only interested in\n\t * ARP probes to us, so check the null host sender. */\n\tBPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, INADDR_ANY, 1, 0);\n\tbp++;\n\tBPF_SET_STMT(bp, BPF_RET + BPF_K, 0);\n\tbp++;\n\n\t/* Match target protocol address */\n\tBPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,\n\t    (sizeof(struct arphdr) + (size_t)(ifp->hwlen * 2) +\n\t\tsizeof(in_addr_t)));\n\tbp++;\n\tBPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, htonl(ia->s_addr), 0, 1);\n\tbp++;\n\tBPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len);\n\tbp++;\n\n\t/* No match, drop it */\n\tBPF_SET_STMT(bp, BPF_RET + BPF_K, 0);\n\tbp++;\n\n\tlen = (unsigned int)(bp - buf);\n\tif (recv)\n\t\treturn bpf_setfilter(bpf, buf, len);\n\treturn bpf_setwfilter(bpf, buf, len);\n}\n\nint\nbpf_filter_arp(const struct bpf *bpf, const struct in_addr *ia)\n{\n\tif (bpf_arp_rw(bpf, ia, true) == -1)\n\t\treturn -1;\n\tif (bpf_arp_rw(bpf, ia, false) == -1 && errno != ENOSYS)\n\t\treturn -1;\n\tif (bpf_lockfilter(bpf) == -1 && errno != ENOSYS)\n\t\treturn -1;\n\treturn 0;\n}\n#endif\n\n#ifdef ARPHRD_NONE\nstatic const struct bpf_insn bpf_bootp_none[] = {};\n#define BPF_BOOTP_NONE_LEN __arraycount(bpf_bootp_none)\n#endif\n\nstatic const struct bpf_insn bpf_bootp_ether[] = {\n\t/* Make sure this is an IP packet. */\n\tBPF_STMT(BPF_LD + BPF_H + BPF_ABS,\n\t    offsetof(struct ether_header, ether_type)),\n\tBPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0),\n\tBPF_STMT(BPF_RET + BPF_K, 0),\n\n\t/* Advance to the IP header. */\n\tBPF_STMT(BPF_LDX + BPF_K, sizeof(struct ether_header)),\n};\n#define BPF_BOOTP_ETHER_LEN __arraycount(bpf_bootp_ether)\n\nstatic const struct bpf_insn bpf_bootp_base[] = {\n\t/* Make sure it's an IPv4 packet. */\n\tBPF_STMT(BPF_LD + BPF_B + BPF_IND, 0),\n\tBPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0xf0),\n\tBPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x40, 1, 0),\n\tBPF_STMT(BPF_RET + BPF_K, 0),\n\n\t/* Make sure it's a UDP packet. */\n\tBPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct ip, ip_p)),\n\tBPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0),\n\tBPF_STMT(BPF_RET + BPF_K, 0),\n\n\t/* Make sure this isn't a fragment. */\n\tBPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct ip, ip_off)),\n\tBPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 0, 1),\n\tBPF_STMT(BPF_RET + BPF_K, 0),\n\n\t/* Advance to the UDP header. */\n\tBPF_STMT(BPF_LD + BPF_B + BPF_IND, 0),\n\tBPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x0f),\n\tBPF_STMT(BPF_ALU + BPF_MUL + BPF_K, 4),\n\tBPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0),\n\tBPF_STMT(BPF_MISC + BPF_TAX, 0),\n};\n#define BPF_BOOTP_BASE_LEN __arraycount(bpf_bootp_base)\n\nstatic const struct bpf_insn bpf_bootp_read[] = {\n\t/* Make sure it's to the right port.\n\t * RFC2131 makes no mention of enforcing a source port. */\n\tBPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct udphdr, uh_dport)),\n\tBPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTPC, 1, 0),\n\tBPF_STMT(BPF_RET + BPF_K, 0),\n};\n#define BPF_BOOTP_READ_LEN __arraycount(bpf_bootp_read)\n\nstatic const struct bpf_insn bpf_bootp_write[] = {\n\t/* Make sure it's from and to the right port.\n\t * RFC2131 makes no mention of encforcing a source port,\n\t * but dhcpcd does enforce it for sending. */\n\tBPF_STMT(BPF_LD + BPF_W + BPF_IND, 0),\n\tBPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (BOOTPC << 16) + BOOTPS, 1, 0),\n\tBPF_STMT(BPF_RET + BPF_K, 0),\n};\n#define BPF_BOOTP_WRITE_LEN  __arraycount(bpf_bootp_write)\n\n#define BPF_BOOTP_CHADDR_LEN ((BOOTP_CHADDR_LEN / 4) * 3)\n#define BPF_BOOTP_XID_LEN    4 /* BOUND check is 4 instructions */\n\n#define BPF_BOOTP_LEN                                                   \\\n\tBPF_BOOTP_ETHER_LEN + BPF_BOOTP_BASE_LEN + BPF_BOOTP_READ_LEN + \\\n\t    BPF_BOOTP_XID_LEN + BPF_BOOTP_CHADDR_LEN + 4\n\nstatic int\nbpf_bootp_rw(const struct bpf *bpf, bool read)\n{\n\tstruct bpf_insn buf[BPF_BOOTP_LEN + 1];\n\tstruct bpf_insn *bp;\n\n\tbp = buf;\n\t/* Check frame header. */\n\tswitch (bpf->bpf_ifp->hwtype) {\n#ifdef ARPHRD_NONE\n\tcase ARPHRD_NONE:\n\t\tmemcpy(bp, bpf_bootp_none, sizeof(bpf_bootp_none));\n\t\tbp += BPF_BOOTP_NONE_LEN;\n\t\tbreak;\n#endif\n\tcase ARPHRD_ETHER:\n\t\tmemcpy(bp, bpf_bootp_ether, sizeof(bpf_bootp_ether));\n\t\tbp += BPF_BOOTP_ETHER_LEN;\n\t\tbreak;\n\tdefault:\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\t/* Copy in the main filter. */\n\tmemcpy(bp, bpf_bootp_base, sizeof(bpf_bootp_base));\n\tbp += BPF_BOOTP_BASE_LEN;\n\n\tif (!read) {\n\t\tmemcpy(bp, bpf_bootp_write, sizeof(bpf_bootp_write));\n\t\tbp += BPF_BOOTP_WRITE_LEN;\n\n\t\t/* All passed, return the packet. */\n\t\tBPF_SET_STMT(bp, BPF_RET + BPF_K, BPF_WHOLEPACKET);\n\t\tbp++;\n\n\t\treturn bpf_setwfilter(bpf, buf, (unsigned int)(bp - buf));\n\t}\n\n\tmemcpy(bp, bpf_bootp_read, sizeof(bpf_bootp_read));\n\tbp += BPF_BOOTP_READ_LEN;\n\n\t/* All passed, return the packet. */\n\tBPF_SET_STMT(bp, BPF_RET + BPF_K, BPF_WHOLEPACKET);\n\tbp++;\n\n\treturn bpf_setfilter(bpf, buf, (unsigned int)(bp - buf));\n}\n\nint\nbpf_filter_bootp(const struct bpf *bpf, __unused const struct in_addr *ia)\n{\n\tif (bpf_bootp_rw(bpf, true) == -1)\n\t\treturn -1;\n\tif (bpf_bootp_rw(bpf, false) == -1 && errno != ENOSYS)\n\t\treturn -1;\n\tif (bpf_lockfilter(bpf) == -1 && errno != ENOSYS)\n\t\treturn -1;\n\treturn 0;\n}\n"
  },
  {
    "path": "src/bpf.h",
    "content": "/*\n * dhcpcd: BPF arp and bootp filtering\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef BPF_HEADER\n#define BPF_HEADER\n\n#define BPF_EOF\t\t0x01U\n#define BPF_PARTIALCSUM 0x02U\n#define BPF_BCAST\t0x04U\n\n/*\n * Even though we program the BPF filter should we trust it?\n * On Linux at least there is a window between opening the socket,\n * binding the interface and setting the filter where we receive data.\n * This data is NOT checked OR flushed and IS returned when reading.\n * We have no way of flushing it other than reading these packets!\n * But we don't know if they passed the filter or not ..... so we need\n * to validate each and every packet that comes through ourselves as well.\n * Even if Linux does fix this sorry state, who is to say other kernels\n * don't have bugs causing a similar effect?\n *\n * As such, let's strive to keep the filters just for pattern matching\n * to avoid waking dhcpcd up.\n *\n * If you want to be notified of any packet failing the BPF filter,\n * define BPF_DEBUG below.\n */\n// #define\tBPF_DEBUG\n\n#include \"dhcpcd.h\"\n\nstruct bpf {\n\tconst struct interface *bpf_ifp;\n\tvoid *bpf_handle;\n\tint bpf_fd;\n\tunsigned int bpf_flags;\n\tvoid *bpf_buffer;\n\tsize_t bpf_size;\n\tsize_t bpf_len;\n\tsize_t bpf_pos;\n};\nstruct iovec;\n\nextern const char *bpf_name;\nsize_t bpf_frame_header_len(const struct interface *);\nvoid *bpf_frame_header_src(const struct interface *, void *, size_t *);\nvoid *bpf_frame_header_dst(const struct interface *, void *, size_t *);\nint bpf_frame_bcast(const struct interface *, const void *);\nstruct bpf *bpf_open(const struct interface *,\n    int (*)(const struct bpf *, const struct in_addr *),\n    const struct in_addr *);\nvoid bpf_close(struct bpf *);\nint bpf_setfilter(const struct bpf *, void *, unsigned int);\nint bpf_setwfilter(const struct bpf *, void *, unsigned int);\nint bpf_lockfilter(const struct bpf *);\nssize_t bpf_send(const struct bpf *, uint16_t, const void *, size_t);\nssize_t bpf_writev(const struct bpf *, struct iovec *, int);\nssize_t bpf_read(struct bpf *, void *, size_t);\n\nint bpf_filter_arp(const struct bpf *, const struct in_addr *);\nint bpf_filter_bootp(const struct bpf *, const struct in_addr *);\n#endif\n"
  },
  {
    "path": "src/common.c",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/stat.h>\n#include <sys/statvfs.h>\n\n#include <ctype.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"common.h\"\n#include \"dhcpcd.h\"\n#include \"eloop.h\"\n#include \"if-options.h\"\n\nconst char *\nhwaddr_ntoa(const void *hwaddr, size_t hwlen, char *buf, size_t buflen)\n{\n\tconst unsigned char *hp, *ep;\n\tchar *p;\n\n\t/* Allow a hwlen of 0 to be an empty string. */\n\tif (buf == NULL || buflen == 0) {\n\t\terrno = ENOBUFS;\n\t\treturn NULL;\n\t}\n\n\tif (hwlen * 3 > buflen) {\n\t\t/* We should still terminate the string just in case. */\n\t\tbuf[0] = '\\0';\n\t\terrno = ENOBUFS;\n\t\treturn NULL;\n\t}\n\n\thp = hwaddr;\n\tep = hp + hwlen;\n\tp = buf;\n\twhile (hp < ep) {\n\t\tif (hp != hwaddr)\n\t\t\t*p++ = ':';\n\t\tp += snprintf(p, 3, \"%.2x\", *hp++);\n\t}\n\t*p++ = '\\0';\n\treturn buf;\n}\n\nsize_t\nhwaddr_aton(uint8_t *buffer, const char *addr)\n{\n\tchar c[3];\n\tconst char *p = addr;\n\tuint8_t *bp = buffer;\n\tsize_t len = 0;\n\n\tc[2] = '\\0';\n\twhile (*p != '\\0') {\n\t\t/* Skip separators */\n\t\tc[0] = *p++;\n\t\tswitch (c[0]) {\n\t\tcase '\\n': /* long duid split on lines */\n\t\tcase ':':  /* typical mac address */\n\t\tcase '-':  /* uuid */\n\t\t\tcontinue;\n\t\t}\n\t\tc[1] = *p++;\n\t\t/* Ensure that digits are hex */\n\t\tif (isxdigit((unsigned char)c[0]) == 0 ||\n\t\t    isxdigit((unsigned char)c[1]) == 0) {\n\t\t\terrno = EINVAL;\n\t\t\treturn 0;\n\t\t}\n\t\t/* We should have at least two entries 00:01 */\n\t\tif (len == 0 && *p == '\\0') {\n\t\t\terrno = EINVAL;\n\t\t\treturn 0;\n\t\t}\n\t\tif (bp)\n\t\t\t*bp++ = (uint8_t)strtol(c, NULL, 16);\n\t\tlen++;\n\t}\n\treturn len;\n}\n\nssize_t\nreadfile(const char *file, void *data, size_t len)\n{\n\tint fd;\n\tssize_t bytes;\n\n\tfd = open(file, O_RDONLY);\n\tif (fd == -1)\n\t\treturn -1;\n\tbytes = read(fd, data, len);\n\tclose(fd);\n\tif ((size_t)bytes == len) {\n\t\terrno = ENOBUFS;\n\t\treturn -1;\n\t}\n\treturn bytes;\n}\n\nssize_t\nwritefile(const char *file, mode_t mode, const void *data, size_t len)\n{\n\tint fd;\n\tssize_t bytes;\n\n\tfd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode);\n\tif (fd == -1)\n\t\treturn -1;\n\tbytes = write(fd, data, len);\n\tclose(fd);\n\treturn bytes;\n}\n\nint\nfilemtime(const char *file, time_t *time)\n{\n\tstruct stat st;\n\n\tif (stat(file, &st) == -1)\n\t\treturn -1;\n\t*time = st.st_mtime;\n\treturn 0;\n}\n\n/* Handy routine to read very long lines in text files.\n * This means we read the whole line and avoid any nasty buffer overflows.\n * We strip leading space and avoid comment lines, making the code that calls\n * us smaller. */\nchar *\nget_line(char **__restrict buf, ssize_t *__restrict buflen)\n{\n\tchar *p, *c;\n\tbool quoted;\n\n\tdo {\n\t\tp = *buf;\n\t\tif (*buf == NULL)\n\t\t\treturn NULL;\n\t\tc = memchr(*buf, '\\n', (size_t)*buflen);\n\t\tif (c == NULL) {\n\t\t\tc = memchr(*buf, '\\0', (size_t)*buflen);\n\t\t\tif (c == NULL)\n\t\t\t\treturn NULL;\n\t\t\t*buflen = c - *buf;\n\t\t\t*buf = NULL;\n\t\t} else {\n\t\t\t*c++ = '\\0';\n\t\t\t*buflen -= c - *buf;\n\t\t\t*buf = c;\n\t\t}\n\t\tfor (; *p == ' ' || *p == '\\t'; p++)\n\t\t\t;\n\t} while (*p == '\\0' || *p == '\\n' || *p == '#' || *p == ';');\n\n\t/* Strip embedded comments unless in a quoted string or escaped */\n\tquoted = false;\n\tfor (c = p; *c != '\\0'; c++) {\n\t\tif (*c == '\\\\') {\n\t\t\tc++; /* escaped */\n\t\t\tcontinue;\n\t\t}\n\t\tif (*c == '\"')\n\t\t\tquoted = !quoted;\n\t\telse if (*c == '#' && !quoted) {\n\t\t\t*c = '\\0';\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn p;\n}\n\nint\nis_root_local(void)\n{\n#ifdef ST_LOCAL\n\tstruct statvfs vfs;\n\n\tif (statvfs(\"/\", &vfs) == -1)\n\t\treturn -1;\n\treturn vfs.f_flag & ST_LOCAL ? 1 : 0;\n#else\n\terrno = ENOTSUP;\n\treturn -1;\n#endif\n}\n\nuint32_t\nlifetime_left(uint32_t lifetime, const struct timespec *acquired,\n    struct timespec *now)\n{\n\tuint32_t elapsed;\n\tstruct timespec n;\n\n\tif (lifetime == INFINITE_LIFETIME)\n\t\treturn lifetime;\n\n\tif (now == NULL) {\n\t\ttimespecclear(&n);\n\t\tnow = &n;\n\t}\n\tif (!timespecisset(now))\n\t\tclock_gettime(CLOCK_MONOTONIC, now);\n\n\telapsed = (uint32_t)eloop_timespec_diff(now, acquired, NULL);\n\tif (elapsed > lifetime)\n\t\treturn 0;\n\n\treturn lifetime - elapsed;\n}\n"
  },
  {
    "path": "src/common.h",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef COMMON_H\n#define COMMON_H\n\n#include <sys/types.h>\n#include <sys/param.h>\n#include <sys/time.h>\n\n#include <stdint.h>\n#include <stdio.h>\n\n/* Define eloop queues here, as other apps share eloop.h */\n#define ELOOP_DHCPCD\t    1 /* default queue */\n#define ELOOP_DHCP\t    2\n#define ELOOP_ARP\t    3\n#define ELOOP_IPV4LL\t    4\n#define ELOOP_IPV6\t    5\n#define ELOOP_IPV6ND\t    6\n#define ELOOP_IPV6RA_EXPIRE 7\n#define ELOOP_DHCP6\t    8\n#define ELOOP_IF\t    9\n\n#ifndef HOSTNAME_MAX_LEN\n#define HOSTNAME_MAX_LEN 250 /* 255 - 3 (FQDN) - 2 (DNS enc) */\n#endif\n\n#ifndef MIN\n#define MIN(a, b) ((/*CONSTCOND*/ (a) < (b)) ? (a) : (b))\n#define MAX(a, b) ((/*CONSTCOND*/ (a) > (b)) ? (a) : (b))\n#endif\n\n#define UNCONST(a)   ((void *)(uintptr_t)(const void *)(a))\n#define STRINGIFY(a) #a\n#define TOSTRING(a)  STRINGIFY(a)\n#define UNUSED(a)    (void)(a)\n\n#define ROUNDUP4(a)  (1 + (((a) - 1) | 3))\n#define ROUNDUP8(a)  (1 + (((a) - 1) | 7))\n\n/* Some systems don't define timespec macros */\n#ifndef timespecclear\n#define timespecclear(tsp) (tsp)->tv_sec = (time_t)((tsp)->tv_nsec = 0L)\n#define timespecisset(tsp) ((tsp)->tv_sec || (tsp)->tv_nsec)\n#define timespecadd(tsp, usp, vsp)                                \\\n\tdo {                                                      \\\n\t\t(vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec;    \\\n\t\t(vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec; \\\n\t\tif ((vsp)->tv_nsec >= 1000000000L) {              \\\n\t\t\t(vsp)->tv_sec++;                          \\\n\t\t\t(vsp)->tv_nsec -= 1000000000L;            \\\n\t\t}                                                 \\\n\t} while (0)\n#define timespecsub(tsp, usp, vsp)                                \\\n\tdo {                                                      \\\n\t\t(vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec;    \\\n\t\t(vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \\\n\t\tif ((vsp)->tv_nsec < 0) {                         \\\n\t\t\t(vsp)->tv_sec--;                          \\\n\t\t\t(vsp)->tv_nsec += 1000000000L;            \\\n\t\t}                                                 \\\n\t} while (0)\n#endif\n\n#if __GNUC__ > 2 || defined(__INTEL_COMPILER)\n#ifndef __unused\n#define __unused __attribute__((__unused__))\n#endif\n#else\n#ifndef __unused\n#define __unused\n#endif\n#endif\n\n/* Needed for rbtree(3) compat */\n#ifndef __RCSID\n#define __RCSID(a)\n#endif\n#ifndef __predict_false\n#if __GNUC__ > 2\n#define __predict_true(exp)  __builtin_expect((exp) != 0, 1)\n#define __predict_false(exp) __builtin_expect((exp) != 0, 0)\n#else\n#define __predict_true(exp)  (exp)\n#define __predict_false(exp) (exp)\n#endif\n#endif\n#ifndef __BEGIN_DECLS\n#if defined(__cplusplus)\n#define __BEGIN_DECLS extern \"C\" {\n#define __END_DECLS \\\n\t}           \\\n\t;\n#else /* __BEGIN_DECLS */\n#define __BEGIN_DECLS\n#define __END_DECLS\n#endif /* __BEGIN_DECLS */\n#endif /* __BEGIN_DECLS */\n\n#ifndef __fallthrough\n#if __GNUC__ >= 7\n#define __fallthrough __attribute__((fallthrough))\n#else\n#define __fallthrough\n#endif\n#endif\n\n/*\n * Compile Time Assertion.\n */\n#ifndef __CTASSERT\n#ifdef __COUNTER__\n#define __CTASSERT(x) __CTASSERT0(x, __ctassert, __COUNTER__)\n#else\n#define __CTASSERT(x) __CTASSERT99(x, __INCLUDE_LEVEL__, __LINE__)\n#define __CTASSERT99(x, a, b) \\\n\t__CTASSERT0(x, __CONCAT(__ctassert, a), __CONCAT(_, b))\n#endif\n#define __CTASSERT0(x, y, z) __CTASSERT1(x, y, z)\n#define __CTASSERT1(x, y, z) \\\n\ttypedef char y##z[/*CONSTCOND*/ (x) ? 1 : -1] __unused\n#endif\n\n#ifndef __arraycount\n#define __arraycount(__x) (sizeof(__x) / sizeof(__x[0]))\n#endif\n\n/* We don't really need this as our supported systems define __restrict\n * automatically for us, but it is here for completeness. */\n#ifndef __restrict\n#if defined(__lint__)\n#define __restrict\n#elif __STDC_VERSION__ >= 199901L\n#define __restrict restrict\n#elif !(2 < __GNUC__ || (2 == __GNU_C && 95 <= __GNUC_VERSION__))\n#define __restrict\n#endif\n#endif\n\n#define INFINITE_LIFETIME (~0U)\n\nconst char *hwaddr_ntoa(const void *, size_t, char *, size_t);\nsize_t hwaddr_aton(uint8_t *, const char *);\nssize_t readfile(const char *, void *, size_t);\nssize_t writefile(const char *, mode_t, const void *, size_t);\nint filemtime(const char *, time_t *);\nchar *get_line(char **__restrict, ssize_t *__restrict);\nint is_root_local(void);\nuint32_t lifetime_left(uint32_t, const struct timespec *, struct timespec *);\n#endif\n"
  },
  {
    "path": "src/control.c",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <sys/uio.h>\n#include <sys/un.h>\n\n#include <errno.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n#include <unistd.h>\n\n#include \"common.h\"\n#include \"config.h\"\n#include \"control.h\"\n#include \"dhcpcd.h\"\n#include \"eloop.h\"\n#include \"if.h\"\n#include \"logerr.h\"\n#include \"privsep.h\"\n\n#ifndef SUN_LEN\n#define SUN_LEN(su) \\\n\t(sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))\n#endif\n\nstatic void control_handle_data(void *, unsigned short);\n\nstatic void\ncontrol_queue_free(struct fd_list *fd)\n{\n\tstruct fd_data *fdp;\n\n\twhile ((fdp = TAILQ_FIRST(&fd->queue))) {\n\t\tTAILQ_REMOVE(&fd->queue, fdp, next);\n\t\tif (fdp->data_size != 0)\n\t\t\tfree(fdp->data);\n\t\tfree(fdp);\n\t}\n\n#ifdef CTL_FREE_LIST\n\twhile ((fdp = TAILQ_FIRST(&fd->free_queue))) {\n\t\tTAILQ_REMOVE(&fd->free_queue, fdp, next);\n\t\tif (fdp->data_size != 0)\n\t\t\tfree(fdp->data);\n\t\tfree(fdp);\n\t}\n#endif\n}\n\nvoid\ncontrol_free(struct fd_list *fd)\n{\n#ifdef PRIVSEP\n\tif (fd->ctx->ps_control_client == fd)\n\t\tfd->ctx->ps_control_client = NULL;\n#endif\n\n\teloop_event_delete(fd->ctx->eloop, fd->fd);\n\tclose(fd->fd);\n\tTAILQ_REMOVE(&fd->ctx->control_fds, fd, next);\n\tcontrol_queue_free(fd);\n\tfree(fd);\n}\n\nstatic void\ncontrol_hangup(struct fd_list *fd)\n{\n#ifdef PRIVSEP\n\tif (IN_PRIVSEP(fd->ctx)) {\n\t\tif (ps_ctl_sendeof(fd) == -1)\n\t\t\tlogerr(__func__);\n\t}\n#endif\n\tcontrol_free(fd);\n}\n\nstatic int\ncontrol_handle_read(struct fd_list *fd)\n{\n\tchar buffer[1024];\n\tssize_t bytes;\n\n\tbytes = read(fd->fd, buffer, sizeof(buffer) - 1);\n\tif (bytes == -1)\n\t\tlogerr(__func__);\n\tif (bytes == -1 || bytes == 0) {\n\t\tcontrol_hangup(fd);\n\t\treturn -1;\n\t}\n\n#ifdef PRIVSEP\n\tif (IN_PRIVSEP(fd->ctx)) {\n\t\tssize_t err;\n\n\t\tfd->flags |= FD_SENDLEN;\n\t\terr = ps_ctl_handleargs(fd, buffer, (size_t)bytes);\n\t\tfd->flags &= ~FD_SENDLEN;\n\t\tif (err == -1) {\n\t\t\tlogerr(__func__);\n\t\t\treturn 0;\n\t\t}\n\t\tif (err == 1 &&\n\t\t    ps_ctl_sendargs(fd, buffer, (size_t)bytes) == -1) {\n\t\t\tlogerr(__func__);\n\t\t\tcontrol_free(fd);\n\t\t\treturn -1;\n\t\t}\n\t\treturn 0;\n\t}\n#endif\n\n\tcontrol_recvdata(fd, buffer, (size_t)bytes);\n\treturn 0;\n}\n\nstatic int\ncontrol_handle_write(struct fd_list *fd)\n{\n\tstruct iovec iov[2];\n\tint iov_len;\n\tstruct fd_data *data;\n\n\tdata = TAILQ_FIRST(&fd->queue);\n\n\tif (data->data_flags & FD_SENDLEN) {\n\t\tiov[0].iov_base = &data->data_len;\n\t\tiov[0].iov_len = sizeof(size_t);\n\t\tiov[1].iov_base = data->data;\n\t\tiov[1].iov_len = data->data_len;\n\t\tiov_len = 2;\n\t} else {\n\t\tiov[0].iov_base = data->data;\n\t\tiov[0].iov_len = data->data_len;\n\t\tiov_len = 1;\n\t}\n\n\tif (writev(fd->fd, iov, iov_len) == -1) {\n\t\tif (errno != EPIPE && errno != ENOTCONN) {\n\t\t\t// We don't get ELE_HANGUP for some reason\n\t\t\tlogerr(\"%s: write\", __func__);\n\t\t}\n\t\tcontrol_hangup(fd);\n\t\treturn -1;\n\t}\n\n\tTAILQ_REMOVE(&fd->queue, data, next);\n#ifdef CTL_FREE_LIST\n\tTAILQ_INSERT_TAIL(&fd->free_queue, data, next);\n#else\n\tif (data->data_size != 0)\n\t\tfree(data->data);\n\tfree(data);\n#endif\n\n\tif (TAILQ_FIRST(&fd->queue) != NULL)\n\t\treturn 0;\n\n#ifdef PRIVSEP\n\tif (IN_PRIVSEP_SE(fd->ctx) && !(fd->flags & FD_LISTEN)) {\n\t\tif (ps_ctl_sendeof(fd) == -1)\n\t\t\tlogerr(__func__);\n\t}\n#endif\n\n\t/* Done sending data, stop watching write to fd */\n\tif (eloop_event_add(fd->ctx->eloop, fd->fd, ELE_READ,\n\t\tcontrol_handle_data, fd) == -1)\n\t\tlogerr(\"%s: eloop_event_add\", __func__);\n\treturn 0;\n}\n\nstatic void\ncontrol_handle_data(void *arg, unsigned short events)\n{\n\tstruct fd_list *fd = arg;\n\n\tif (!(events & (ELE_READ | ELE_WRITE | ELE_HANGUP)))\n\t\tlogerrx(\"%s: unexpected event 0x%04x\", __func__, events);\n\n\tif (events & ELE_WRITE && !(events & ELE_HANGUP)) {\n\t\tif (control_handle_write(fd) == -1)\n\t\t\treturn;\n\t}\n\tif (events & ELE_READ) {\n\t\tif (control_handle_read(fd) == -1)\n\t\t\treturn;\n\t}\n\tif (events & ELE_HANGUP)\n\t\tcontrol_hangup(fd);\n}\n\nvoid\ncontrol_recvdata(struct fd_list *fd, char *data, size_t len)\n{\n\tchar *p = data, *e;\n\tchar *argvp[255], **ap;\n\tint argc;\n\n\t/* Each command is \\n terminated\n\t * Each argument is NULL separated */\n\twhile (len != 0) {\n\t\targc = 0;\n\t\tap = argvp;\n\t\twhile (len != 0) {\n\t\t\tif (*p == '\\0') {\n\t\t\t\tp++;\n\t\t\t\tlen--;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\te = memchr(p, '\\0', len);\n\t\t\tif (e == NULL) {\n\t\t\t\terrno = EINVAL;\n\t\t\t\tlogerrx(\"%s: no terminator\", __func__);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif ((size_t)argc >= sizeof(argvp) / sizeof(argvp[0])) {\n\t\t\t\terrno = ENOBUFS;\n\t\t\t\tlogerrx(\"%s: no arg buffer\", __func__);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t*ap++ = p;\n\t\t\targc++;\n\t\t\te++;\n\t\t\tlen -= (size_t)(e - p);\n\t\t\tp = e;\n\t\t\te--;\n\t\t\tif (*(--e) == '\\n') {\n\t\t\t\t*e = '\\0';\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (argc == 0) {\n\t\t\tlogerrx(\"%s: no args\", __func__);\n\t\t\tcontinue;\n\t\t}\n\t\t*ap = NULL;\n\t\tif (dhcpcd_handleargs(fd->ctx, fd, argc, argvp) == -1) {\n\t\t\tlogerr(__func__);\n\t\t\tif (errno != EINTR && errno != EAGAIN) {\n\t\t\t\tcontrol_free(fd);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n}\n\nstruct fd_list *\ncontrol_new(struct dhcpcd_ctx *ctx, int fd, unsigned int flags)\n{\n\tstruct fd_list *l;\n\n\tl = malloc(sizeof(*l));\n\tif (l == NULL)\n\t\treturn NULL;\n\n\tl->ctx = ctx;\n\tl->fd = fd;\n\tl->flags = flags;\n\tTAILQ_INIT(&l->queue);\n#ifdef CTL_FREE_LIST\n\tTAILQ_INIT(&l->free_queue);\n#endif\n\tTAILQ_INSERT_TAIL(&ctx->control_fds, l, next);\n\treturn l;\n}\n\nstatic void\ncontrol_handle1(struct dhcpcd_ctx *ctx, int lfd, unsigned int fd_flags,\n    unsigned short events)\n{\n\tstruct sockaddr_un run;\n\tsocklen_t len;\n\tstruct fd_list *l;\n\tint fd, flags;\n\n\tif (events != ELE_READ)\n\t\tlogerrx(\"%s: unexpected event 0x%04x\", __func__, events);\n\n\tlen = sizeof(run);\n\tif ((fd = accept(lfd, (struct sockaddr *)&run, &len)) == -1)\n\t\tgoto error;\n\tif ((flags = fcntl(fd, F_GETFD, 0)) == -1 ||\n\t    fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)\n\t\tgoto error;\n\tif ((flags = fcntl(fd, F_GETFL, 0)) == -1 ||\n\t    fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)\n\t\tgoto error;\n\n#ifdef PRIVSEP\n\tif (IN_PRIVSEP(ctx) && !IN_PRIVSEP_SE(ctx))\n\t\t;\n\telse\n#endif\n\t\tfd_flags |= FD_SENDLEN;\n\n\tl = control_new(ctx, fd, fd_flags);\n\tif (l == NULL)\n\t\tgoto error;\n\n\tif (eloop_event_add(ctx->eloop, l->fd, ELE_READ, control_handle_data,\n\t\tl) == -1)\n\t\tlogerr(\"%s: eloop_event_add\", __func__);\n\treturn;\n\nerror:\n\tlogerr(__func__);\n\tif (fd != -1)\n\t\tclose(fd);\n}\n\nstatic void\ncontrol_handle(void *arg, unsigned short events)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\n\tcontrol_handle1(ctx, ctx->control_fd, 0, events);\n}\n\nstatic void\ncontrol_handle_unpriv(void *arg, unsigned short events)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\n\tcontrol_handle1(ctx, ctx->control_unpriv_fd, FD_UNPRIV, events);\n}\n\nstatic int\nmake_path(char *path, size_t len, const char *ifname, sa_family_t family,\n    bool unpriv)\n{\n\tconst char *per;\n\tconst char *sunpriv;\n\n\tswitch (family) {\n\tcase AF_INET:\n\t\tper = \"-4\";\n\t\tbreak;\n\tcase AF_INET6:\n\t\tper = \"-6\";\n\t\tbreak;\n\tdefault:\n\t\tper = \"\";\n\t\tbreak;\n\t}\n\tif (unpriv)\n\t\tsunpriv = ifname ? \".unpriv\" : \"unpriv.\";\n\telse\n\t\tsunpriv = \"\";\n\treturn snprintf(path, len, CONTROLSOCKET, ifname ? ifname : \"\",\n\t    ifname ? per : \"\", sunpriv, ifname ? \".\" : \"\");\n}\n\nstatic int\nmake_sock(struct sockaddr_un *sa, const char *ifname, sa_family_t family,\n    bool unpriv)\n{\n\tint fd;\n\n\tif ((fd = xsocket(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0)) == -1)\n\t\treturn -1;\n\tmemset(sa, 0, sizeof(*sa));\n\tsa->sun_family = AF_UNIX;\n\tmake_path(sa->sun_path, sizeof(sa->sun_path), ifname, family, unpriv);\n\treturn fd;\n}\n\n#define S_PRIV\t (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)\n#define S_UNPRIV (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)\n\nstatic int\ncontrol_start1(struct dhcpcd_ctx *ctx, const char *ifname, sa_family_t family,\n    mode_t fmode)\n{\n\tstruct sockaddr_un sa;\n\tint fd;\n\tsocklen_t len;\n\n\tfd = make_sock(&sa, ifname, family, (fmode & S_UNPRIV) == S_UNPRIV);\n\tif (fd == -1)\n\t\treturn -1;\n\n\tlen = (socklen_t)SUN_LEN(&sa);\n\tunlink(sa.sun_path);\n\tif (bind(fd, (struct sockaddr *)&sa, len) == -1 ||\n\t    chmod(sa.sun_path, fmode) == -1 ||\n\t    (ctx->control_group &&\n\t\tchown(sa.sun_path, geteuid(), ctx->control_group) == -1) ||\n\t    listen(fd, sizeof(ctx->control_fds)) == -1) {\n\t\tclose(fd);\n\t\tunlink(sa.sun_path);\n\t\treturn -1;\n\t}\n\n#ifdef PRIVSEP_RIGHTS\n\tif (IN_PRIVSEP(ctx) && ps_rights_limit_fd_fctnl(fd) == -1) {\n\t\tclose(fd);\n\t\tunlink(sa.sun_path);\n\t\treturn -1;\n\t}\n#endif\n\n\tif ((fmode & S_UNPRIV) == S_UNPRIV)\n\t\tstrlcpy(ctx->control_sock_unpriv, sa.sun_path,\n\t\t    sizeof(ctx->control_sock_unpriv));\n\telse\n\t\tstrlcpy(ctx->control_sock, sa.sun_path,\n\t\t    sizeof(ctx->control_sock));\n\treturn fd;\n}\n\nint\ncontrol_start(struct dhcpcd_ctx *ctx, const char *ifname, sa_family_t family)\n{\n\tint fd;\n\n#ifdef PRIVSEP\n\tif (IN_PRIVSEP_SE(ctx)) {\n\t\tmake_path(ctx->control_sock, sizeof(ctx->control_sock), ifname,\n\t\t    family, false);\n\t\tmake_path(ctx->control_sock_unpriv,\n\t\t    sizeof(ctx->control_sock_unpriv), ifname, family, true);\n\t\treturn 0;\n\t}\n#endif\n\n\tif ((fd = control_start1(ctx, ifname, family, S_PRIV)) == -1)\n\t\treturn -1;\n\n\tctx->control_fd = fd;\n\tif (eloop_event_add(ctx->eloop, fd, ELE_READ, control_handle, ctx) ==\n\t    -1)\n\t\tlogerr(\"%s: eloop_event_add\", __func__);\n\n\tif ((fd = control_start1(ctx, ifname, family, S_UNPRIV)) != -1) {\n\t\tctx->control_unpriv_fd = fd;\n\t\tif (eloop_event_add(ctx->eloop, fd, ELE_READ,\n\t\t\tcontrol_handle_unpriv, ctx) == -1)\n\t\t\tlogerr(\"%s: eloop_event_add\", __func__);\n\t}\n\treturn ctx->control_fd;\n}\n\nstatic int\ncontrol_unlink(struct dhcpcd_ctx *ctx, const char *file)\n{\n\tint retval = 0;\n\n\terrno = 0;\n#ifdef PRIVSEP\n\tif (IN_PRIVSEP(ctx))\n\t\tretval = (int)ps_root_unlink(ctx, file);\n\telse\n#else\n\tUNUSED(ctx);\n#endif\n\t\tretval = unlink(file);\n\n\treturn retval == -1 && errno != ENOENT ? -1 : 0;\n}\n\nint\ncontrol_stop(struct dhcpcd_ctx *ctx)\n{\n\tint retval = 0;\n\tstruct fd_list *l;\n\n\twhile ((l = TAILQ_FIRST(&ctx->control_fds)) != NULL) {\n\t\tcontrol_free(l);\n\t}\n\n#ifdef PRIVSEP\n\tif (IN_PRIVSEP_SE(ctx)) {\n\t\tif (ctx->control_sock[0] != '\\0' &&\n\t\t    ps_root_unlink(ctx, ctx->control_sock) == -1)\n\t\t\tretval = -1;\n\t\tif (ctx->control_sock_unpriv[0] != '\\0' &&\n\t\t    ps_root_unlink(ctx, ctx->control_sock_unpriv) == -1)\n\t\t\tretval = -1;\n\t\treturn retval;\n\t} else if (ctx->options & DHCPCD_FORKED)\n\t\treturn retval;\n#endif\n\n\tif (ctx->control_fd != -1) {\n\t\teloop_event_delete(ctx->eloop, ctx->control_fd);\n\t\tclose(ctx->control_fd);\n\t\tctx->control_fd = -1;\n\t\tif (control_unlink(ctx, ctx->control_sock) == -1)\n\t\t\tretval = -1;\n\t}\n\n\tif (ctx->control_unpriv_fd != -1) {\n\t\teloop_event_delete(ctx->eloop, ctx->control_unpriv_fd);\n\t\tclose(ctx->control_unpriv_fd);\n\t\tctx->control_unpriv_fd = -1;\n\t\tif (control_unlink(ctx, ctx->control_sock_unpriv) == -1)\n\t\t\tretval = -1;\n\t}\n\n\treturn retval;\n}\n\nint\ncontrol_open(const char *ifname, sa_family_t family, bool unpriv)\n{\n\tstruct sockaddr_un sa;\n\tint fd;\n\n\tif ((fd = make_sock(&sa, ifname, family, unpriv)) != -1) {\n\t\tsocklen_t len;\n\n\t\tlen = (socklen_t)SUN_LEN(&sa);\n\t\tif (connect(fd, (struct sockaddr *)&sa, len) == -1) {\n\t\t\tclose(fd);\n\t\t\tfd = -1;\n\t\t}\n\t}\n\treturn fd;\n}\n\nssize_t\ncontrol_send(struct dhcpcd_ctx *ctx, int argc, char *const *argv)\n{\n\tchar buffer[1024];\n\tint i;\n\tsize_t len, l;\n\n\tif (argc > 255) {\n\t\terrno = ENOBUFS;\n\t\treturn -1;\n\t}\n\tlen = 0;\n\tfor (i = 0; i < argc; i++) {\n\t\tl = strlen(argv[i]) + 1;\n\t\tif (len + l > sizeof(buffer)) {\n\t\t\terrno = ENOBUFS;\n\t\t\treturn -1;\n\t\t}\n\t\tmemcpy(buffer + len, argv[i], l);\n\t\tlen += l;\n\t}\n\treturn write(ctx->control_fd, buffer, len);\n}\n\nint\ncontrol_queue(struct fd_list *fd, void *data, size_t data_len)\n{\n\tstruct fd_data *d;\n\tunsigned short events;\n\n\tif (data_len == 0) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n#ifdef CTL_FREE_LIST\n\tstruct fd_data *df;\n\n\td = NULL;\n\tTAILQ_FOREACH(df, &fd->free_queue, next) {\n\t\tif (d == NULL || d->data_size < df->data_size) {\n\t\t\td = df;\n\t\t\tif (d->data_size <= data_len)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\tif (d != NULL)\n\t\tTAILQ_REMOVE(&fd->free_queue, d, next);\n\telse\n#endif\n\t{\n\t\td = calloc(1, sizeof(*d));\n\t\tif (d == NULL)\n\t\t\treturn -1;\n\t}\n\n\tif (d->data_size == 0)\n\t\td->data = NULL;\n\tif (d->data_size < data_len) {\n\t\tvoid *nbuf = realloc(d->data, data_len);\n\t\tif (nbuf == NULL) {\n\t\t\tfree(d->data);\n\t\t\tfree(d);\n\t\t\treturn -1;\n\t\t}\n\t\td->data = nbuf;\n\t\td->data_size = data_len;\n\t}\n\tmemcpy(d->data, data, data_len);\n\td->data_len = data_len;\n\td->data_flags = fd->flags & FD_SENDLEN;\n\n\tTAILQ_INSERT_TAIL(&fd->queue, d, next);\n\tevents = ELE_WRITE;\n\tif (fd->flags & FD_LISTEN)\n\t\tevents |= ELE_READ;\n\treturn eloop_event_add(fd->ctx->eloop, fd->fd, events,\n\t    control_handle_data, fd);\n}\n"
  },
  {
    "path": "src/control.h",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef CONTROL_H\n#define CONTROL_H\n\n#include <sys/socket.h>\n\n#include <stdbool.h>\n\n#include \"queue.h\"\n\n#if !defined(CTL_FREE_LIST)\n#define CTL_FREE_LIST 1\n#elif CTL_FREE_LIST == 0\n#undef CTL_FREE_LIST\n#endif\n\n/* Limit queue size per fd */\n#define CONTROL_QUEUE_MAX 100\n\nstruct fd_data {\n\tTAILQ_ENTRY(fd_data) next;\n\tvoid *data;\n\tsize_t data_size;\n\tsize_t data_len;\n\tunsigned int data_flags;\n};\nTAILQ_HEAD(fd_data_head, fd_data);\n\nstruct fd_list {\n\tTAILQ_ENTRY(fd_list) next;\n\tstruct dhcpcd_ctx *ctx;\n\tint fd;\n\tunsigned int flags;\n\tstruct fd_data_head queue;\n#ifdef CTL_FREE_LIST\n\tstruct fd_data_head free_queue;\n#endif\n};\nTAILQ_HEAD(fd_list_head, fd_list);\n\n#define FD_LISTEN  0x01U\n#define FD_UNPRIV  0x02U\n#define FD_SENDLEN 0x04U\n\nint control_start(struct dhcpcd_ctx *, const char *, sa_family_t);\nint control_stop(struct dhcpcd_ctx *);\nint control_open(const char *, sa_family_t, bool);\nssize_t control_send(struct dhcpcd_ctx *, int, char *const *);\nstruct fd_list *control_new(struct dhcpcd_ctx *, int, unsigned int);\nvoid control_free(struct fd_list *);\nvoid control_delete(struct fd_list *);\nint control_queue(struct fd_list *, void *, size_t);\nvoid control_recvdata(struct fd_list *fd, char *, size_t);\n#endif\n"
  },
  {
    "path": "src/defs.h",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef DEFS_H\n#define DEFS_H\n\n#define PACKAGE \"dhcpcd\"\n#define VERSION \"10.3.2\"\n\n#ifndef PRIVSEP_USER\n#define PRIVSEP_USER \"_\" PACKAGE\n#endif\n\n#ifndef CONFIG\n#define CONFIG SYSCONFDIR \"/\" PACKAGE \".conf\"\n#endif\n#ifndef SCRIPT\n#define SCRIPT LIBEXECDIR \"/\" PACKAGE \"-run-hooks\"\n#endif\n#ifndef DEVDIR\n#define DEVDIR LIBDIR \"/\" PACKAGE \"/dev\"\n#endif\n#ifndef DUID\n#define DUID DBDIR \"/duid\"\n#endif\n#ifndef SECRET\n#define SECRET DBDIR \"/secret\"\n#endif\n#ifndef LEASEFILE\n#define LEASEFILE DBDIR \"/%s%s.lease\"\n#endif\n#ifndef LEASEFILE6\n#define LEASEFILE6 LEASEFILE \"6\"\n#endif\n#ifndef PIDFILE\n#define PIDFILE RUNDIR \"/%s%s%spid\"\n#endif\n#ifndef CONTROLSOCKET\n#define CONTROLSOCKET RUNDIR \"/%s%s%s%ssock\"\n#endif\n#ifndef RDM_MONOFILE\n#define RDM_MONOFILE DBDIR \"/rdm_monotonic\"\n#endif\n\n#ifndef NO_SIGNALS\n#define USE_SIGNALS\n#endif\n#ifndef USE_SIGNALS\n#ifndef THERE_IS_NO_FORK\n#define THERE_IS_NO_FORK\n#endif\n#endif\n\n#endif\n"
  },
  {
    "path": "src/dev/Makefile",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2017-2025 Roy Marples <roy@marples.name>\n\nTOP=\t../../\ninclude ${TOP}/Makefile.inc\ninclude ${TOP}/config.mk\n\nCFLAGS?=\t-O2\nCSTD?=\t\tc99\nCFLAGS+=\t-std=${CSTD}\nCPPFLAGS+=\t-I${TOP} -I${TOP}/src -I${TOP}/vendor\n\nDEVDIR=\t\t${LIBDIR}/dhcpcd/dev\nDSRC=\t\t${DEV_PLUGINS:=.c}\nDOBJ=\t\t${DSRC:.c=.o}\nDSOBJ=\t\t${DOBJ:.o=.So}\nDPLUGS=\t\t${DEV_PLUGINS:=.so}\n\nCLEANFILES+=\t${DSOBJ} ${DPLUGS}\n\n.SUFFIXES:\t.So .so\n\n.c.So:\n\t${CC} ${PICFLAG} -DPIC ${CPPFLAGS} ${CFLAGS} -c $< -o $@\n\n.So.so:\n\t${CC} ${LDFLAGS} -shared -Wl,-x -o $@ -Wl,-soname,$@ \\\n\t    $< ${LIBS}\n\nall: ${DPLUGS}\n\nudev.So:\nCFLAGS+=\t${LIBUDEV_CFLAGS}\nCPPFLAGS+=\t${LIBUDEV_CPPFLAGS}\n\nudev.so:\nLIBS+=\t\t${LIBUDEV_LIBS}\n\nproginstall: ${DPLUGS}\n\t${INSTALL} -d ${DESTDIR}${DEVDIR}\n\t${INSTALL} -m ${BINMODE} ${PROG} ${DPLUGS} ${DESTDIR}${DEVDIR}\n\neginstall:\n\ninstall: proginstall\n\nclean:\n\trm -f ${CLEANFILES}\n\ninclude ${TOP}/src//Makefile.inc\n"
  },
  {
    "path": "src/dev/udev.c",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifdef LIBUDEV_NOINIT\n#define LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE\n#warning This version of udev is too old does not support\n#warning per device initialization checks.\n#warning As such, dhcpcd will need to depend on the\n#warning udev-settle service or similar if starting\n#warning in master mode.\n#endif\n\n#include <libudev.h>\n#include <string.h>\n\n#include \"../common.h\"\n#include \"../dev.h\"\n#include \"../if.h\"\n#include \"../logerr.h\"\n\nstatic const char udev_name[] = \"udev\";\nstatic struct udev *udev;\nstatic struct udev_monitor *monitor;\n\nstatic struct dev_dhcpcd dhcpcd;\n\nstatic int\nudev_listening(void)\n{\n\treturn monitor ? 1 : 0;\n}\n\nstatic int\nudev_initialised(const char *ifname)\n{\n\tstruct udev_device *device;\n\tint r;\n\n\tdevice = udev_device_new_from_subsystem_sysname(udev, \"net\", ifname);\n\tif (device) {\n#ifndef LIBUDEV_NOINIT\n\t\tr = udev_device_get_is_initialized(device);\n#else\n\t\tr = 1;\n#endif\n\t\tudev_device_unref(device);\n\t} else\n\t\tr = 0;\n\treturn r;\n}\n\nstatic int\nudev_handle_device(void *ctx)\n{\n\tstruct udev_device *device;\n\tconst char *subsystem, *ifname, *action;\n\n\tdevice = udev_monitor_receive_device(monitor);\n\tif (device == NULL) {\n\t\tlogerrx(\"libudev: received NULL device\");\n\t\treturn -1;\n\t}\n\n\tsubsystem = udev_device_get_subsystem(device);\n\tifname = udev_device_get_sysname(device);\n\taction = udev_device_get_action(device);\n\n\t/* udev filter documentation says \"usually\" so double check */\n\tif (strcmp(subsystem, \"net\") == 0) {\n\t\tlogdebugx(\"%s: libudev: %s\", ifname, action);\n\t\tif (strcmp(action, \"add\") == 0 || strcmp(action, \"move\") == 0)\n\t\t\tdhcpcd.handle_interface(ctx, 1, ifname);\n\t\telse if (strcmp(action, \"remove\") == 0)\n\t\t\tdhcpcd.handle_interface(ctx, -1, ifname);\n\t}\n\n\tudev_device_unref(device);\n\treturn 1;\n}\n\nstatic void\nudev_stop(void)\n{\n\tif (monitor) {\n\t\tudev_monitor_unref(monitor);\n\t\tmonitor = NULL;\n\t}\n\n\tif (udev) {\n\t\tudev_unref(udev);\n\t\tudev = NULL;\n\t}\n}\n\nstatic int\nudev_start(void)\n{\n\tchar netns[PATH_MAX];\n\tint fd;\n\n\tif (if_getnetworknamespace(netns, sizeof(netns)) != NULL) {\n\t\tlogdebugx(\"udev does not work in a network namespace\");\n\t\treturn -1;\n\t}\n\n\tif (udev) {\n\t\tlogerrx(\"udev: already started\");\n\t\treturn -1;\n\t}\n\n\tlogdebugx(\"udev: starting\");\n\tudev = udev_new();\n\tif (udev == NULL) {\n\t\tlogerr(\"udev_new\");\n\t\treturn -1;\n\t}\n\tmonitor = udev_monitor_new_from_netlink(udev, \"udev\");\n\tif (monitor == NULL) {\n\t\tlogerr(\"udev_monitor_new_from_netlink\");\n\t\tgoto bad;\n\t}\n#ifndef LIBUDEV_NOFILTER\n\tif (udev_monitor_filter_add_match_subsystem_devtype(monitor, \"net\",\n\t\tNULL) != 0) {\n\t\tlogerr(\"udev_monitor_filter_add_match_subsystem_devtype\");\n\t\tgoto bad;\n\t}\n#endif\n\tif (udev_monitor_enable_receiving(monitor) != 0) {\n\t\tlogerr(\"udev_monitor_enable_receiving\");\n\t\tgoto bad;\n\t}\n\tfd = udev_monitor_get_fd(monitor);\n\tif (fd == -1) {\n\t\tlogerr(\"udev_monitor_get_fd\");\n\t\tgoto bad;\n\t}\n\treturn fd;\n\nbad:\n\tudev_stop();\n\treturn -1;\n}\n\nint\ndev_init(struct dev *dev, const struct dev_dhcpcd *dev_dhcpcd)\n{\n\tdev->name = udev_name;\n\tdev->initialised = udev_initialised;\n\tdev->listening = udev_listening;\n\tdev->handle_device = udev_handle_device;\n\tdev->stop = udev_stop;\n\tdev->start = udev_start;\n\n\tdhcpcd = *dev_dhcpcd;\n\n\treturn 0;\n}\n"
  },
  {
    "path": "src/dev.c",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <dirent.h>\n#include <dlfcn.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define _INDEV\n#include \"common.h\"\n#include \"dev.h\"\n#include \"dhcpcd.h\"\n#include \"eloop.h\"\n#include \"logerr.h\"\n\nint\ndev_initialised(struct dhcpcd_ctx *ctx, const char *ifname)\n{\n#ifdef PRIVSEP\n\tif (ctx->options & DHCPCD_PRIVSEP &&\n\t    !(ctx->options & DHCPCD_PRIVSEPROOT))\n\t\treturn ps_root_dev_initialised(ctx, ifname);\n#endif\n\n\tif (ctx->dev == NULL)\n\t\treturn 1;\n\treturn ctx->dev->initialised(ifname);\n}\n\nint\ndev_listening(struct dhcpcd_ctx *ctx)\n{\n#ifdef PRIVSEP\n\tif (ctx->options & DHCPCD_PRIVSEP &&\n\t    !(ctx->options & DHCPCD_PRIVSEPROOT))\n\t\treturn ps_root_dev_listening(ctx);\n#endif\n\n\tif (ctx->dev == NULL)\n\t\treturn 0;\n\treturn ctx->dev->listening();\n}\n\nstatic void\ndev_stop1(struct dhcpcd_ctx *ctx, int stop)\n{\n\tif (ctx->dev) {\n\t\tif (stop)\n\t\t\tlogdebugx(\"dev: unloaded %s\", ctx->dev->name);\n\t\teloop_event_delete(ctx->eloop, ctx->dev_fd);\n\t\tctx->dev->stop();\n\t\tfree(ctx->dev);\n\t\tctx->dev = NULL;\n\t\tctx->dev_fd = -1;\n\t}\n\tif (ctx->dev_handle) {\n\t\tdlclose(ctx->dev_handle);\n\t\tctx->dev_handle = NULL;\n\t}\n}\n\nvoid\ndev_stop(struct dhcpcd_ctx *ctx)\n{\n\tdev_stop1(ctx, !(ctx->options & DHCPCD_FORKED));\n}\n\nstatic int\ndev_start2(struct dhcpcd_ctx *ctx, const struct dev_dhcpcd *dev_dhcpcd,\n    const char *name)\n{\n\tchar file[PATH_MAX];\n\tvoid *h;\n\tvoid (*fptr)(struct dev *, const struct dev_dhcpcd *);\n\tint r;\n\n\tsnprintf(file, sizeof(file), DEVDIR \"/%s\", name);\n\th = dlopen(file, RTLD_LAZY);\n\tif (h == NULL) {\n\t\tlogerrx(\"dlopen: %s\", dlerror());\n\t\treturn -1;\n\t}\n\tfptr = dlsym(h, \"dev_init\");\n\tif (fptr == NULL) {\n\t\tlogerrx(\"dlsym: %s\", dlerror());\n\t\tdlclose(h);\n\t\treturn -1;\n\t}\n\tif ((ctx->dev = calloc(1, sizeof(*ctx->dev))) == NULL) {\n\t\tlogerr(\"%s: calloc\", __func__);\n\t\tdlclose(h);\n\t\treturn -1;\n\t}\n\tfptr(ctx->dev, dev_dhcpcd);\n\tif (ctx->dev->start == NULL || (r = ctx->dev->start()) == -1) {\n\t\tfree(ctx->dev);\n\t\tctx->dev = NULL;\n\t\tdlclose(h);\n\t\treturn -1;\n\t}\n\tloginfox(\"dev: loaded %s\", ctx->dev->name);\n\tctx->dev_handle = h;\n\treturn r;\n}\n\nstatic int\ndev_start1(struct dhcpcd_ctx *ctx, const struct dev_dhcpcd *dev_dhcpcd)\n{\n\tDIR *dp;\n\tstruct dirent *d;\n\tint r;\n\n\tif (ctx->dev) {\n\t\tlogerrx(\"dev: already started %s\", ctx->dev->name);\n\t\treturn -1;\n\t}\n\n\tif (ctx->dev_load)\n\t\treturn dev_start2(ctx, dev_dhcpcd, ctx->dev_load);\n\n\tdp = opendir(DEVDIR);\n\tif (dp == NULL) {\n\t\tlogdebug(\"dev: %s\", DEVDIR);\n\t\treturn -1;\n\t}\n\n\tr = 0;\n\twhile ((d = readdir(dp))) {\n\t\tif (d->d_name[0] == '.')\n\t\t\tcontinue;\n\n\t\tr = dev_start2(ctx, dev_dhcpcd, d->d_name);\n\t\tif (r != -1)\n\t\t\tbreak;\n\t}\n\tclosedir(dp);\n\treturn r;\n}\n\nstatic void\ndev_handle_data(void *arg, unsigned short events)\n{\n\tstruct dhcpcd_ctx *ctx;\n\n\tif (events != ELE_READ)\n\t\tlogerrx(\"%s: unexpected event 0x%04x\", __func__, events);\n\n\tctx = arg;\n\tif (ctx->dev->handle_device(arg) == -1) {\n\t\t/* XXX: an error occured. should we restart dev? */\n\t}\n}\n\nint\ndev_start(struct dhcpcd_ctx *ctx, int (*handler)(void *, int, const char *))\n{\n\tstruct dev_dhcpcd dev_dhcpcd = {\n\t\t.handle_interface = handler,\n\t};\n\n\tif (ctx->dev_fd != -1) {\n\t\tlogerrx(\"%s: already started on fd %d\", __func__, ctx->dev_fd);\n\t\treturn ctx->dev_fd;\n\t}\n\n\tctx->dev_fd = dev_start1(ctx, &dev_dhcpcd);\n\tif (ctx->dev_fd != -1) {\n\t\tif (eloop_event_add(ctx->eloop, ctx->dev_fd, ELE_READ,\n\t\t\tdev_handle_data, ctx) == -1) {\n\t\t\tlogerr(__func__);\n\t\t\tdev_stop1(ctx, 1);\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\treturn ctx->dev_fd;\n}\n"
  },
  {
    "path": "src/dev.h",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef DEV_H\n#define DEV_H\n\n// dev plugin setup\nstruct dev {\n\tconst char *name;\n\tint (*initialised)(const char *);\n\tint (*listening)(void);\n\tint (*handle_device)(void *);\n\tint (*start)(void);\n\tvoid (*stop)(void);\n};\n\nstruct dev_dhcpcd {\n\tint (*handle_interface)(void *, int, const char *);\n};\n\nint dev_init(struct dev *, const struct dev_dhcpcd *);\n\n// hooks for dhcpcd\n#ifdef PLUGIN_DEV\n#include \"dhcpcd.h\"\nint dev_initialised(struct dhcpcd_ctx *, const char *);\nint dev_listening(struct dhcpcd_ctx *);\nint dev_start(struct dhcpcd_ctx *, int (*)(void *, int, const char *));\nvoid dev_stop(struct dhcpcd_ctx *);\n#endif\n\n#endif\n"
  },
  {
    "path": "src/dhcp-common.c",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/utsname.h>\n\n#include <ctype.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <inttypes.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include \"common.h\"\n#include \"config.h\"\n#include \"dhcp-common.h\"\n#include \"dhcp.h\"\n#include \"if.h\"\n#include \"ipv6.h\"\n#include \"logerr.h\"\n#include \"script.h\"\n\nconst char *\ndhcp_get_hostname(char *buf, size_t buf_len, const struct if_options *ifo)\n{\n\tif (ifo->hostname[0] == '\\0') {\n\t\tif (gethostname(buf, buf_len) != 0)\n\t\t\treturn NULL;\n\t\tbuf[buf_len - 1] = '\\0';\n\t} else\n\t\tstrlcpy(buf, ifo->hostname, buf_len);\n\n\t/* Deny sending of these local hostnames */\n\tif (buf[0] == '\\0' || buf[0] == '.' || strcmp(buf, \"(none)\") == 0 ||\n\t    strcmp(buf, \"localhost\") == 0 ||\n\t    strncmp(buf, \"localhost.\", strlen(\"localhost.\")) == 0)\n\t\treturn NULL;\n\n\t/* Shorten the hostname if required */\n\tif (ifo->options & DHCPCD_HOSTNAME_SHORT) {\n\t\tchar *hp;\n\n\t\thp = strchr(buf, '.');\n\t\tif (hp != NULL)\n\t\t\t*hp = '\\0';\n\t}\n\n\treturn buf;\n}\n\nvoid\ndhcp_print_option_encoding(const struct dhcp_opt *opt, int cols)\n{\n\twhile (cols < 40) {\n\t\tputchar(' ');\n\t\tcols++;\n\t}\n\tputchar('\\t');\n\tif (opt->type & OT_EMBED)\n\t\tprintf(\" embed\");\n\tif (opt->type & OT_ENCAP)\n\t\tprintf(\" encap\");\n\tif (opt->type & OT_INDEX)\n\t\tprintf(\" index\");\n\tif (opt->type & OT_ARRAY)\n\t\tprintf(\" array\");\n\tif (opt->type & OT_UINT8)\n\t\tprintf(\" uint8\");\n\telse if (opt->type & OT_INT8)\n\t\tprintf(\" int8\");\n\telse if (opt->type & OT_UINT16)\n\t\tprintf(\" uint16\");\n\telse if (opt->type & OT_INT16)\n\t\tprintf(\" int16\");\n\telse if (opt->type & OT_UINT32)\n\t\tprintf(\" uint32\");\n\telse if (opt->type & OT_INT32)\n\t\tprintf(\" int32\");\n\telse if (opt->type & OT_ADDRIPV4)\n\t\tprintf(\" ipaddress\");\n\telse if (opt->type & OT_ADDRIPV6)\n\t\tprintf(\" ip6address\");\n\telse if (opt->type & OT_FLAG)\n\t\tprintf(\" flag\");\n\telse if (opt->type & OT_BITFLAG)\n\t\tprintf(\" bitflags\");\n\telse if (opt->type & OT_RFC1035)\n\t\tprintf(\" domain\");\n\telse if (opt->type & OT_DOMAIN)\n\t\tprintf(\" dname\");\n\telse if (opt->type & OT_ASCII)\n\t\tprintf(\" ascii\");\n\telse if (opt->type & OT_RAW)\n\t\tprintf(\" raw\");\n\telse if (opt->type & OT_BINHEX)\n\t\tprintf(\" binhex\");\n\telse if (opt->type & OT_STRING)\n\t\tprintf(\" string\");\n\telse if (opt->type & OT_URI)\n\t\tprintf(\" uri\");\n\tif (opt->type & OT_RFC3361)\n\t\tprintf(\" rfc3361\");\n\tif (opt->type & OT_RFC3442)\n\t\tprintf(\" rfc3442\");\n\tif (opt->type & OT_REQUEST)\n\t\tprintf(\" request\");\n\tif (opt->type & OT_NOREQ)\n\t\tprintf(\" norequest\");\n\tif (opt->type & OT_TRUNCATED)\n\t\tprintf(\" truncated\");\n\tputchar('\\n');\n\tfflush(stdout);\n}\n\nstruct dhcp_opt *\nvivso_find(uint32_t iana_en, const void *arg)\n{\n\tconst struct interface *ifp;\n\tsize_t i;\n\tstruct dhcp_opt *opt;\n\n\tifp = arg;\n\tfor (i = 0, opt = ifp->options->vivso_override;\n\t    i < ifp->options->vivso_override_len; i++, opt++)\n\t\tif (opt->option == iana_en)\n\t\t\treturn opt;\n\tfor (i = 0, opt = ifp->ctx->vivso; i < ifp->ctx->vivso_len; i++, opt++)\n\t\tif (opt->option == iana_en)\n\t\t\treturn opt;\n\treturn NULL;\n}\n\nssize_t\ndhcp_vendor(char *str, size_t len)\n{\n\tstruct utsname utn;\n\tchar *p;\n\tint l;\n\n\tif (uname(&utn) == -1)\n\t\treturn (ssize_t)snprintf(str, len, \"%s-%s\", PACKAGE, VERSION);\n\tp = str;\n\tl = snprintf(p, len, \"%s-%s:%s-%s:%s\", PACKAGE, VERSION, utn.sysname,\n\t    utn.release, utn.machine);\n\tif (l == -1 || (size_t)(l + 1) > len)\n\t\treturn -1;\n\tp += l;\n\tlen -= (size_t)l;\n\tl = if_machinearch(p + 1, len - 1);\n\tif (l == -1 || (size_t)(l + 1) > len)\n\t\treturn -1;\n\t*p = ':';\n\tp += l;\n\treturn p - str;\n}\n\nint\nmake_option_mask(const struct dhcp_opt *dopts, size_t dopts_len,\n    const struct dhcp_opt *odopts, size_t odopts_len, uint8_t *mask,\n    const char *opts, int add)\n{\n\tchar *token, *o, *p;\n\tconst struct dhcp_opt *opt;\n\tint match, e;\n\tunsigned int n;\n\tsize_t i;\n\n\tif (opts == NULL)\n\t\treturn -1;\n\to = p = strdup(opts);\n\twhile ((token = strsep(&p, \", \"))) {\n\t\tif (*token == '\\0')\n\t\t\tcontinue;\n\t\tif (strncmp(token, \"dhcp6_\", 6) == 0)\n\t\t\ttoken += 6;\n\t\tif (strncmp(token, \"nd_\", 3) == 0)\n\t\t\ttoken += 3;\n\t\tmatch = 0;\n\t\tfor (i = 0, opt = odopts; i < odopts_len; i++, opt++) {\n\t\t\tif (opt->var == NULL || opt->option == 0)\n\t\t\t\tcontinue; /* buggy dhcpcd-definitions.conf */\n\t\t\tif (strcmp(opt->var, token) == 0)\n\t\t\t\tmatch = 1;\n\t\t\telse {\n\t\t\t\tn = (unsigned int)strtou(token, NULL, 0, 0,\n\t\t\t\t    UINT_MAX, &e);\n\t\t\t\tif (e == 0 && opt->option == n)\n\t\t\t\t\tmatch = 1;\n\t\t\t}\n\t\t\tif (match)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (match == 0) {\n\t\t\tfor (i = 0, opt = dopts; i < dopts_len; i++, opt++) {\n\t\t\t\tif (strcmp(opt->var, token) == 0)\n\t\t\t\t\tmatch = 1;\n\t\t\t\telse {\n\t\t\t\t\tn = (unsigned int)strtou(token, NULL, 0,\n\t\t\t\t\t    0, UINT_MAX, &e);\n\t\t\t\t\tif (e == 0 && opt->option == n)\n\t\t\t\t\t\tmatch = 1;\n\t\t\t\t}\n\t\t\t\tif (match)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!match || !opt->option) {\n\t\t\tfree(o);\n\t\t\terrno = ENOENT;\n\t\t\treturn -1;\n\t\t}\n\t\tif (add == 2 && !(opt->type & OT_ADDRIPV4)) {\n\t\t\tfree(o);\n\t\t\terrno = EINVAL;\n\t\t\treturn -1;\n\t\t}\n\t\tif (add == 1 || add == 2)\n\t\t\tadd_option_mask(mask, opt->option);\n\t\telse\n\t\t\tdel_option_mask(mask, opt->option);\n\t}\n\tfree(o);\n\treturn 0;\n}\n\nsize_t\nencode_rfc1035(const char *src, uint8_t *dst)\n{\n\tuint8_t *p;\n\tuint8_t *lp;\n\tsize_t len;\n\tuint8_t has_dot;\n\n\tif (src == NULL || *src == '\\0')\n\t\treturn 0;\n\n\tif (dst) {\n\t\tp = dst;\n\t\tlp = p++;\n\t}\n\t/* Silence bogus GCC warnings */\n\telse\n\t\tp = lp = NULL;\n\n\tlen = 1;\n\thas_dot = 0;\n\tfor (; *src; src++) {\n\t\tif (*src == '\\0')\n\t\t\tbreak;\n\t\tif (*src == '.') {\n\t\t\t/* Skip the trailing . */\n\t\t\tif (src[1] == '\\0')\n\t\t\t\tbreak;\n\t\t\thas_dot = 1;\n\t\t\tif (dst) {\n\t\t\t\t*lp = (uint8_t)(p - lp - 1);\n\t\t\t\tif (*lp == '\\0')\n\t\t\t\t\treturn len;\n\t\t\t\tlp = p++;\n\t\t\t}\n\t\t} else if (dst)\n\t\t\t*p++ = (uint8_t)*src;\n\t\tlen++;\n\t}\n\n\tif (dst) {\n\t\t*lp = (uint8_t)(p - lp - 1);\n\t\tif (has_dot)\n\t\t\t*p++ = '\\0';\n\t}\n\n\tif (has_dot)\n\t\tlen++;\n\n\treturn len;\n}\n\n/* Decode an RFC1035 DNS search order option into a space\n * separated string. Returns length of string (including\n * terminating zero) or zero on error. out may be NULL\n * to just determine output length. */\nssize_t\ndecode_rfc1035(char *out, size_t len, const uint8_t *p, size_t pl)\n{\n\tconst char *start;\n\tsize_t start_len, l, d_len, o_len;\n\tconst uint8_t *r, *q = p, *e;\n\tint hops;\n\tuint8_t ltype;\n\n\to_len = 0;\n\tstart = out;\n\tstart_len = len;\n\tq = p;\n\te = p + pl;\n\twhile (q < e) {\n\t\tr = NULL;\n\t\td_len = 0;\n\t\thops = 0;\n\t\t/* Check we are inside our length again in-case\n\t\t * the name isn't fully qualified (ie, not terminated) */\n\t\twhile (q < e && (l = (size_t)*q++)) {\n\t\t\tltype = l & 0xc0;\n\t\t\tif (ltype == 0x80 || ltype == 0x40) {\n\t\t\t\t/* Currently reserved for future use as noted\n\t\t\t\t * in RFC1035 4.1.4 as the 10 and 01\n\t\t\t\t * combinations. */\n\t\t\t\terrno = ENOTSUP;\n\t\t\t\treturn -1;\n\t\t\t} else if (ltype == 0xc0) { /* pointer */\n\t\t\t\tif (q == e) {\n\t\t\t\t\terrno = ERANGE;\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t\tl = (l & 0x3f) << 8;\n\t\t\t\tl |= *q++;\n\t\t\t\t/* save source of first jump. */\n\t\t\t\tif (!r)\n\t\t\t\t\tr = q;\n\t\t\t\thops++;\n\t\t\t\tif (hops > 255) {\n\t\t\t\t\terrno = ERANGE;\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t\tq = p + l;\n\t\t\t\tif (q >= e) {\n\t\t\t\t\terrno = ERANGE;\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t/* straightforward name segment, add with '.' */\n\t\t\t\tif (q + l > e) {\n\t\t\t\t\terrno = ERANGE;\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t\tif (l > NS_MAXLABEL) {\n\t\t\t\t\terrno = EINVAL;\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t\td_len += l + 1;\n\t\t\t\tif (out) {\n\t\t\t\t\tif (l + 1 > len) {\n\t\t\t\t\t\terrno = ENOBUFS;\n\t\t\t\t\t\treturn -1;\n\t\t\t\t\t}\n\t\t\t\t\tmemcpy(out, q, l);\n\t\t\t\t\tout += l;\n\t\t\t\t\t*out++ = '.';\n\t\t\t\t\tlen -= l;\n\t\t\t\t\tlen--;\n\t\t\t\t}\n\t\t\t\tq += l;\n\t\t\t}\n\t\t}\n\n\t\t/* Don't count the trailing NUL */\n\t\tif (d_len > NS_MAXDNAME + 1) {\n\t\t\terrno = E2BIG;\n\t\t\treturn -1;\n\t\t}\n\t\to_len += d_len;\n\n\t\t/* change last dot to space */\n\t\tif (out && out != start)\n\t\t\t*(out - 1) = ' ';\n\t\tif (r)\n\t\t\tq = r;\n\t}\n\n\t/* change last space to zero terminator */\n\tif (out) {\n\t\tif (out != start)\n\t\t\t*(out - 1) = '\\0';\n\t\telse if (start_len > 0)\n\t\t\t*out = '\\0';\n\t}\n\n\t/* Remove the trailing NUL */\n\tif (o_len != 0)\n\t\to_len--;\n\n\treturn (ssize_t)o_len;\n}\n\n/* Check for a valid name as per RFC952 and RFC1123 section 2.1 */\nstatic ssize_t\nvalid_domainname(char *lbl, int type)\n{\n\tchar *slbl = lbl, *lst = NULL;\n\tunsigned char c;\n\tint len = 0;\n\tbool start = true, errset = false;\n\n\tif (lbl == NULL || *lbl == '\\0') {\n\t\terrno = EINVAL;\n\t\treturn 0;\n\t}\n\n\tfor (;;) {\n\t\tc = (unsigned char)*lbl++;\n\t\tif (c == '\\0')\n\t\t\treturn lbl - slbl - 1;\n\t\tif (c == ' ') {\n\t\t\tif (lbl - 1 == slbl) /* No space at start */\n\t\t\t\tbreak;\n\t\t\tif (!(type & OT_ARRAY))\n\t\t\t\tbreak;\n\t\t\t/* Skip to the next label */\n\t\t\tif (!start) {\n\t\t\t\tstart = true;\n\t\t\t\tlst = lbl - 1;\n\t\t\t}\n\t\t\tif (len)\n\t\t\t\tlen = 0;\n\t\t\tcontinue;\n\t\t}\n\t\tif (c == '.') {\n\t\t\tif (*lbl == '.')\n\t\t\t\tbreak;\n\t\t\tlen = 0;\n\t\t\tcontinue;\n\t\t}\n\t\tif (((c == '-' || c == '_') && !start && *lbl != ' ' &&\n\t\t\t*lbl != '\\0') ||\n\t\t    isalnum(c)) {\n\t\t\tif (++len > NS_MAXLABEL) {\n\t\t\t\terrno = ERANGE;\n\t\t\t\terrset = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else\n\t\t\tbreak;\n\t\tif (start)\n\t\t\tstart = false;\n\t}\n\n\tif (!errset)\n\t\terrno = EINVAL;\n\tif (lst) {\n\t\t/* At least one valid domain, return it */\n\t\t*lst = '\\0';\n\t\treturn lst - slbl;\n\t}\n\treturn 0;\n}\n\n/*\n * Prints a chunk of data to a string.\n * PS_SHELL goes as it is these days, it's upto the target to validate it.\n * PS_SAFE has all non ascii and non printables changes to escaped octal.\n */\nstatic const char hexchrs[] = \"0123456789abcdef\";\nssize_t\nprint_string(char *dst, size_t len, int type, const void *data, size_t dl)\n{\n\tconst uint8_t *d = (const uint8_t *)data;\n\tchar *odst;\n\tuint8_t c;\n\tconst uint8_t *e;\n\tsize_t bytes;\n\n\todst = dst;\n\tbytes = 0;\n\te = d + dl;\n\n\twhile (d < e) {\n\t\tc = *d++;\n\t\tif (type & OT_BINHEX) {\n\t\t\tif (dst) {\n\t\t\t\tif (len == 0 || len == 1) {\n\t\t\t\t\terrno = ENOBUFS;\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t\t*dst++ = hexchrs[(c & 0xF0) >> 4];\n\t\t\t\t*dst++ = hexchrs[(c & 0x0F)];\n\t\t\t\tlen -= 2;\n\t\t\t}\n\t\t\tbytes += 2;\n\t\t\tcontinue;\n\t\t}\n\t\tif (type & OT_ASCII && (!isascii(c))) {\n\t\t\terrno = EINVAL;\n\t\t\tbreak;\n\t\t}\n\t\tif (!(type & (OT_ASCII | OT_RAW | OT_ESCSTRING | OT_ESCFILE)) &&\n\t\t    (!isascii(c) && !isprint(c))) {\n\t\t\terrno = EINVAL;\n\t\t\tbreak;\n\t\t}\n\t\tif (type & OT_URI && isspace(c)) {\n\t\t\terrno = EINVAL;\n\t\t\tbreak;\n\t\t}\n\t\tif ((type & (OT_ESCSTRING | OT_ESCFILE) &&\n\t\t\t(c == '\\\\' || !isascii(c) || !isprint(c))) ||\n\t\t    (type & OT_ESCFILE && (c == '/' || c == ' '))) {\n\t\t\terrno = EINVAL;\n\t\t\tif (c == '\\\\') {\n\t\t\t\tif (dst) {\n\t\t\t\t\tif (len == 0 || len == 1) {\n\t\t\t\t\t\terrno = ENOBUFS;\n\t\t\t\t\t\treturn -1;\n\t\t\t\t\t}\n\t\t\t\t\t*dst++ = '\\\\';\n\t\t\t\t\t*dst++ = '\\\\';\n\t\t\t\t\tlen -= 2;\n\t\t\t\t}\n\t\t\t\tbytes += 2;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (dst) {\n\t\t\t\tif (len < 5) {\n\t\t\t\t\terrno = ENOBUFS;\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t\t*dst++ = '\\\\';\n\t\t\t\t*dst++ = (char)(((c >> 6) & 03) + '0');\n\t\t\t\t*dst++ = (char)(((c >> 3) & 07) + '0');\n\t\t\t\t*dst++ = (char)((c & 07) + '0');\n\t\t\t\tlen -= 4;\n\t\t\t}\n\t\t\tbytes += 4;\n\t\t} else {\n\t\t\tif (dst) {\n\t\t\t\tif (len == 0) {\n\t\t\t\t\terrno = ENOBUFS;\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t\t*dst++ = (char)c;\n\t\t\t\tlen--;\n\t\t\t}\n\t\t\tbytes++;\n\t\t}\n\t}\n\n\t/* NULL */\n\tif (dst) {\n\t\tif (len == 0) {\n\t\t\terrno = ENOBUFS;\n\t\t\treturn -1;\n\t\t}\n\t\t*dst = '\\0';\n\n\t\t/* Now we've printed it, validate the domain */\n\t\tif (type & OT_DOMAIN && !valid_domainname(odst, type)) {\n\t\t\t*odst = '\\0';\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\treturn (ssize_t)bytes;\n}\n\n#define ADDR6SZ 16\nstatic ssize_t\ndhcp_optlen(const struct dhcp_opt *opt, size_t dl)\n{\n\tsize_t sz;\n\n\tif (opt->type & OT_ADDRIPV6)\n\t\tsz = ADDR6SZ;\n\telse if (opt->type & (OT_INT32 | OT_UINT32 | OT_ADDRIPV4))\n\t\tsz = sizeof(uint32_t);\n\telse if (opt->type & (OT_INT16 | OT_UINT16))\n\t\tsz = sizeof(uint16_t);\n\telse if (opt->type & (OT_INT8 | OT_UINT8 | OT_BITFLAG))\n\t\tsz = sizeof(uint8_t);\n\telse if (opt->type & OT_FLAG)\n\t\treturn 0;\n\telse {\n\t\t/* All other types are variable length */\n\t\tif (opt->len) {\n\t\t\tif ((size_t)opt->len > dl) {\n\t\t\t\terrno = EOVERFLOW;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\treturn (ssize_t)opt->len;\n\t\t}\n\t\treturn (ssize_t)dl;\n\t}\n\tif (dl < sz) {\n\t\tif (opt->type & OT_TRUNCATED)\n\t\t\treturn (ssize_t)dl;\n\t\terrno = EOVERFLOW;\n\t\treturn -1;\n\t}\n\n\t/* Trim any extra data.\n\t * Maybe we need a setting to reject DHCP options with extra data? */\n\tif (opt->type & OT_ARRAY)\n\t\treturn (ssize_t)(dl - (dl % sz));\n\treturn (ssize_t)sz;\n}\n\nstatic ssize_t\nprint_option(FILE *fp, const char *prefix, const struct dhcp_opt *opt,\n    int vname, const uint8_t *data, size_t dl, const char *ifname)\n{\n\tfpos_t fp_pos;\n\tconst uint8_t *e, *t;\n\tuint16_t u16;\n\tint16_t s16;\n\tuint32_t u32;\n\tint32_t s32;\n\tstruct in_addr addr;\n\tssize_t sl;\n\tsize_t l;\n\n\t/* Ensure a valid length */\n\tdl = (size_t)dhcp_optlen(opt, dl);\n\tif ((ssize_t)dl == -1)\n\t\treturn 0;\n\n\tif (fgetpos(fp, &fp_pos) == -1)\n\t\treturn -1;\n\tif (fprintf(fp, \"%s\", prefix) == -1)\n\t\tgoto err;\n\n\t/* We printed something, so always goto err from now-on\n\t * to terminate the string. */\n\tif (vname) {\n\t\tif (fprintf(fp, \"_%s\", opt->var) == -1)\n\t\t\tgoto err;\n\t}\n\tif (fputc('=', fp) == EOF)\n\t\tgoto err;\n\tif (dl == 0)\n\t\tgoto done;\n\n\tif (opt->type & OT_RFC1035) {\n\t\tchar domain[NS_MAXDNAME];\n\n\t\tsl = decode_rfc1035(domain, sizeof(domain), data, dl);\n\t\tif (sl == -1)\n\t\t\tgoto err;\n\t\tif (sl == 0)\n\t\t\tgoto done;\n\t\tif (!valid_domainname(domain, opt->type))\n\t\t\tgoto err;\n\t\treturn efprintf(fp, \"%s\", domain);\n\t}\n\n#ifdef INET\n\tif (opt->type & OT_RFC3361)\n\t\treturn print_rfc3361(fp, data, dl);\n\n\tif (opt->type & OT_RFC3442)\n\t\treturn print_rfc3442(fp, data, dl);\n#endif\n\n\t/* Produces a space separated list of URIs.\n\t * This is valid as a URI cannot contain a space. */\n\tif ((opt->type & (OT_ARRAY | OT_URI)) == (OT_ARRAY | OT_URI)) {\n#ifdef SMALL\n\t\terrno = ENOTSUP;\n\t\treturn -1;\n#else\n\t\tchar buf[UINT16_MAX + 1];\n\t\tuint16_t sz;\n\t\tbool first = true;\n\n\t\twhile (dl) {\n\t\t\tif (dl < 2) {\n\t\t\t\terrno = EINVAL;\n\t\t\t\tgoto err;\n\t\t\t}\n\n\t\t\tmemcpy(&u16, data, sizeof(u16));\n\t\t\tsz = ntohs(u16);\n\t\t\tdata += sizeof(u16);\n\t\t\tdl -= sizeof(u16);\n\n\t\t\tif (sz == 0)\n\t\t\t\tcontinue;\n\t\t\tif (sz > dl) {\n\t\t\t\terrno = EINVAL;\n\t\t\t\tgoto err;\n\t\t\t}\n\n\t\t\tif (print_string(buf, sizeof(buf), opt->type, data,\n\t\t\t\tsz) == -1)\n\t\t\t\tgoto err;\n\n\t\t\tif (first)\n\t\t\t\tfirst = false;\n\t\t\telse if (fputc(' ', fp) == EOF)\n\t\t\t\tgoto err;\n\n\t\t\tif (fprintf(fp, \"%s\", buf) == -1)\n\t\t\t\tgoto err;\n\n\t\t\tdata += sz;\n\t\t\tdl -= sz;\n\t\t}\n\n\t\tif (fputc('\\0', fp) == EOF)\n\t\t\tgoto err;\n\t\treturn 0;\n#endif\n\t}\n\n\tif (opt->type & (OT_STRING | OT_URI)) {\n\t\tchar buf[1024];\n\n\t\tif (print_string(buf, sizeof(buf), opt->type, data, dl) == -1)\n\t\t\tgoto err;\n\t\treturn efprintf(fp, \"%s\", buf);\n\t}\n\n\tif (opt->type & OT_FLAG)\n\t\treturn efprintf(fp, \"1\");\n\n\tif (opt->type & OT_BITFLAG) {\n\t\t/* bitflags are a string, MSB first, such as ABCDEFGH\n\t\t * where A is 10000000, B is 01000000, etc. */\n\t\tfor (l = 0, sl = sizeof(opt->bitflags) - 1;\n\t\t    l < sizeof(opt->bitflags); l++, sl--) {\n\t\t\tif (opt->bitflags[l] == '1') {\n\t\t\t\tif (fprintf(fp, \"%d\", *data & (1 << sl)) == -1)\n\t\t\t\t\tgoto err;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t/* Don't print NULL or 0 flags */\n\t\t\tif (opt->bitflags[l] != '\\0' &&\n\t\t\t    opt->bitflags[l] != '0' && *data & (1 << sl)) {\n\t\t\t\tif (fputc(opt->bitflags[l], fp) == EOF)\n\t\t\t\t\tgoto err;\n\t\t\t}\n\t\t}\n\t\tgoto done;\n\t}\n\n\tt = data;\n\te = data + dl;\n\twhile (data < e) {\n\t\tif (data != t) {\n\t\t\tif (fputc(' ', fp) == EOF)\n\t\t\t\tgoto err;\n\t\t}\n\t\tif (opt->type & OT_UINT8) {\n\t\t\tif (fprintf(fp, \"%u\", *data) == -1)\n\t\t\t\tgoto err;\n\t\t\tdata++;\n\t\t} else if (opt->type & OT_INT8) {\n\t\t\tif (fprintf(fp, \"%d\", *data) == -1)\n\t\t\t\tgoto err;\n\t\t\tdata++;\n\t\t} else if (opt->type & OT_UINT16) {\n\t\t\tmemcpy(&u16, data, sizeof(u16));\n\t\t\tu16 = ntohs(u16);\n\t\t\tif (fprintf(fp, \"%u\", u16) == -1)\n\t\t\t\tgoto err;\n\t\t\tdata += sizeof(u16);\n\t\t} else if (opt->type & OT_INT16) {\n\t\t\tmemcpy(&u16, data, sizeof(u16));\n\t\t\ts16 = (int16_t)ntohs(u16);\n\t\t\tif (fprintf(fp, \"%d\", s16) == -1)\n\t\t\t\tgoto err;\n\t\t\tdata += sizeof(u16);\n\t\t} else if (opt->type & OT_UINT32) {\n\t\t\tmemcpy(&u32, data, sizeof(u32));\n\t\t\tu32 = ntohl(u32);\n\t\t\tif (fprintf(fp, \"%u\", u32) == -1)\n\t\t\t\tgoto err;\n\t\t\tdata += sizeof(u32);\n\t\t} else if (opt->type & OT_INT32) {\n\t\t\tmemcpy(&u32, data, sizeof(u32));\n\t\t\ts32 = (int32_t)ntohl(u32);\n\t\t\tif (fprintf(fp, \"%d\", s32) == -1)\n\t\t\t\tgoto err;\n\t\t\tdata += sizeof(u32);\n\t\t} else if (opt->type & OT_ADDRIPV4) {\n\t\t\tmemcpy(&addr.s_addr, data, sizeof(addr.s_addr));\n\t\t\tif (fprintf(fp, \"%s\", inet_ntoa(addr)) == -1)\n\t\t\t\tgoto err;\n\t\t\tdata += sizeof(addr.s_addr);\n\t\t} else if (opt->type & OT_ADDRIPV6) {\n\t\t\tuint8_t databuf[sizeof(struct in6_addr)] = { 0 };\n\t\t\tsize_t datalen = e - data >= 16 ? 16 :\n\t\t\t\t\t\t\t  (size_t)(e - data);\n\t\t\tchar buf[INET6_ADDRSTRLEN];\n\n\t\t\t/* avoid inet_ntop going beyond our option space by\n\t\t\t * copying out into a temporary buffer. */\n\t\t\tmemcpy(databuf, data, datalen);\n\t\t\tif (inet_ntop(AF_INET6, databuf, buf, sizeof(buf)) ==\n\t\t\t    NULL)\n\t\t\t\tgoto err;\n\t\t\tif (fprintf(fp, \"%s\", buf) == -1)\n\t\t\t\tgoto err;\n\t\t\tif (data[0] == 0xfe && (data[1] & 0xc0) == 0x80) {\n\t\t\t\tif (fprintf(fp, \"%%%s\", ifname) == -1)\n\t\t\t\t\tgoto err;\n\t\t\t}\n\t\t\tdata += 16;\n\t\t} else {\n\t\t\terrno = EINVAL;\n\t\t\tgoto err;\n\t\t}\n\t}\n\ndone:\n\tif (fputc('\\0', fp) == EOF)\n\t\treturn -1;\n\treturn 1;\n\nerr:\n\t(void)fsetpos(fp, &fp_pos);\n\treturn -1;\n}\n\nint\ndhcp_set_leasefile(char *leasefile, size_t len, int family,\n    const struct interface *ifp)\n{\n\tchar ssid[1 + (IF_SSIDLEN * 4) + 1]; /* - prefix and NUL terminated. */\n\n\tif (ifp->name[0] == '\\0') {\n\t\tstrlcpy(leasefile, ifp->ctx->pidfile, len);\n\t\treturn 0;\n\t}\n\n\tswitch (family) {\n\tcase AF_INET:\n\tcase AF_INET6:\n\t\tbreak;\n\tdefault:\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tif (ifp->wireless) {\n\t\tssid[0] = '-';\n\t\tprint_string(ssid + 1, sizeof(ssid) - 1, OT_ESCFILE,\n\t\t    ifp->ssid, ifp->ssid_len);\n\t} else\n\t\tssid[0] = '\\0';\n\treturn snprintf(leasefile, len,\n\t    family == AF_INET ? LEASEFILE : LEASEFILE6, ifp->name, ssid);\n}\n\nvoid\ndhcp_envoption(struct dhcpcd_ctx *ctx, FILE *fp, const char *prefix,\n    const char *ifname, struct dhcp_opt *opt,\n    const uint8_t *(*dgetopt)(struct dhcpcd_ctx *, size_t *, unsigned int *,\n\tsize_t *, const uint8_t *, size_t, struct dhcp_opt **),\n    const uint8_t *od, size_t ol)\n{\n\tsize_t i, eos, eol;\n\tssize_t eo;\n\tunsigned int eoc;\n\tconst uint8_t *eod;\n\tint ov;\n\tstruct dhcp_opt *eopt, *oopt;\n\tchar *pfx;\n\n\t/* If no embedded or encapsulated options, it's easy */\n\tif (opt->embopts_len == 0 && opt->encopts_len == 0) {\n\t\tif (opt->type & OT_RESERVED)\n\t\t\treturn;\n\t\tif (print_option(fp, prefix, opt, 1, od, ol, ifname) == -1)\n\t\t\tlogerr(\"%s: %s %d\", ifname, __func__, opt->option);\n\t\treturn;\n\t}\n\n\t/* Create a new prefix based on the option */\n\tif (opt->type & OT_INDEX) {\n\t\tif (asprintf(&pfx, \"%s_%s%d\", prefix, opt->var, ++opt->index) ==\n\t\t    -1)\n\t\t\tpfx = NULL;\n\t} else {\n\t\tif (asprintf(&pfx, \"%s_%s\", prefix, opt->var) == -1)\n\t\t\tpfx = NULL;\n\t}\n\tif (pfx == NULL) {\n\t\tlogerr(__func__);\n\t\treturn;\n\t}\n\n\t/* Embedded options are always processed first as that\n\t * is a fixed layout */\n\tfor (i = 0, eopt = opt->embopts; i < opt->embopts_len; i++, eopt++) {\n\t\teo = dhcp_optlen(eopt, ol);\n\t\tif (eo == -1) {\n\t\t\tlogerrx(\"%s: %s %d.%d/%zu: \"\n\t\t\t\t\"malformed embedded option\",\n\t\t\t    ifname, __func__, opt->option, eopt->option, i);\n\t\t\tgoto out;\n\t\t}\n\t\tif (eo == 0) {\n\t\t\t/* An option was expected, but there is no data\n\t\t\t * data for it.\n\t\t\t * This may not be an error as some options like\n\t\t\t * DHCP FQDN in RFC4702 have a string as the last\n\t\t\t * option which is optional. */\n\t\t\tif (ol != 0 || !(eopt->type & OT_OPTIONAL))\n\t\t\t\tlogerrx(\"%s: %s %d.%d/%zu: \"\n\t\t\t\t\t\"missing embedded option\",\n\t\t\t\t    ifname, __func__, opt->option, eopt->option,\n\t\t\t\t    i);\n\t\t\tgoto out;\n\t\t}\n\t\t/* Use the option prefix if the embedded option\n\t\t * name is different.\n\t\t * This avoids new_fqdn_fqdn which would be silly. */\n\t\tif (!(eopt->type & OT_RESERVED)) {\n\t\t\tov = strcmp(opt->var, eopt->var);\n\t\t\tif (print_option(fp, pfx, eopt, ov, od, (size_t)eo,\n\t\t\t\tifname) == -1)\n\t\t\t\tlogerr(\"%s: %s %d.%d/%zu\", ifname, __func__,\n\t\t\t\t    opt->option, eopt->option, i);\n\t\t}\n\t\tod += (size_t)eo;\n\t\tol -= (size_t)eo;\n\t}\n\n\t/* Enumerate our encapsulated options */\n\tif (opt->encopts_len && ol > 0) {\n\t\t/* Zero any option indexes\n\t\t * We assume that referenced encapsulated options are NEVER\n\t\t * recursive as the index order could break. */\n\t\tfor (i = 0, eopt = opt->encopts; i < opt->encopts_len;\n\t\t    i++, eopt++) {\n\t\t\teoc = opt->option;\n\t\t\tif (eopt->type & OT_OPTION) {\n\t\t\t\tdgetopt(ctx, NULL, &eoc, NULL, NULL, 0, &oopt);\n\t\t\t\tif (oopt)\n\t\t\t\t\toopt->index = 0;\n\t\t\t}\n\t\t}\n\n\t\twhile ((eod = dgetopt(ctx, &eos, &eoc, &eol, od, ol, &oopt))) {\n\t\t\tfor (i = 0, eopt = opt->encopts; i < opt->encopts_len;\n\t\t\t    i++, eopt++) {\n\t\t\t\tif (eopt->option != eoc)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (eopt->type & OT_OPTION) {\n\t\t\t\t\tif (oopt == NULL)\n\t\t\t\t\t\t/* Report error? */\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tdhcp_envoption(ctx, fp, pfx, ifname,\n\t\t\t\t    eopt->type & OT_OPTION ? oopt : eopt,\n\t\t\t\t    dgetopt, eod, eol);\n\t\t\t}\n\t\t\tod += eos + eol;\n\t\t\tol -= eos + eol;\n\t\t}\n\t}\n\nout:\n\tfree(pfx);\n}\n\nvoid\ndhcp_zero_index(struct dhcp_opt *opt)\n{\n\tsize_t i;\n\tstruct dhcp_opt *o;\n\n\topt->index = 0;\n\tfor (i = 0, o = opt->embopts; i < opt->embopts_len; i++, o++)\n\t\tdhcp_zero_index(o);\n\tfor (i = 0, o = opt->encopts; i < opt->encopts_len; i++, o++)\n\t\tdhcp_zero_index(o);\n}\n\nssize_t\ndhcp_readfile(struct dhcpcd_ctx *ctx, const char *file, void *data, size_t len)\n{\n#ifdef PRIVSEP\n\tif (ctx->options & DHCPCD_PRIVSEP &&\n\t    !(ctx->options & DHCPCD_PRIVSEPROOT))\n\t\treturn ps_root_readfile(ctx, file, data, len);\n#else\n\tUNUSED(ctx);\n#endif\n\n\treturn readfile(file, data, len);\n}\n\nssize_t\ndhcp_writefile(struct dhcpcd_ctx *ctx, const char *file, mode_t mode,\n    const void *data, size_t len)\n{\n#ifdef PRIVSEP\n\tif (ctx->options & DHCPCD_PRIVSEP &&\n\t    !(ctx->options & DHCPCD_PRIVSEPROOT))\n\t\treturn ps_root_writefile(ctx, file, mode, data, len);\n#else\n\tUNUSED(ctx);\n#endif\n\n\treturn writefile(file, mode, data, len);\n}\n\nint\ndhcp_filemtime(struct dhcpcd_ctx *ctx, const char *file, time_t *time)\n{\n#ifdef PRIVSEP\n\tif (ctx->options & DHCPCD_PRIVSEP &&\n\t    !(ctx->options & DHCPCD_PRIVSEPROOT))\n\t\treturn (int)ps_root_filemtime(ctx, file, time);\n#else\n\tUNUSED(ctx);\n#endif\n\n\treturn filemtime(file, time);\n}\n\nint\ndhcp_unlink(struct dhcpcd_ctx *ctx, const char *file)\n{\n#ifdef PRIVSEP\n\tif (ctx->options & DHCPCD_PRIVSEP &&\n\t    !(ctx->options & DHCPCD_PRIVSEPROOT))\n\t\treturn (int)ps_root_unlink(ctx, file);\n#else\n\tUNUSED(ctx);\n#endif\n\n\treturn unlink(file);\n}\n\nsize_t\ndhcp_read_hwaddr_aton(struct dhcpcd_ctx *ctx, uint8_t **data, const char *file)\n{\n\tchar buf[BUFSIZ];\n\tssize_t bytes;\n\tsize_t len;\n\n\tbytes = dhcp_readfile(ctx, file, buf, sizeof(buf));\n\tif (bytes == -1 || bytes == sizeof(buf))\n\t\treturn 0;\n\n\tbytes[buf] = '\\0';\n\tlen = hwaddr_aton(NULL, buf);\n\tif (len == 0)\n\t\treturn 0;\n\t*data = malloc(len);\n\tif (*data == NULL)\n\t\treturn 0;\n\thwaddr_aton(*data, buf);\n\treturn len;\n}\n"
  },
  {
    "path": "src/dhcp-common.h",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef DHCPCOMMON_H\n#define DHCPCOMMON_H\n\n#include <netinet/in.h>\n\n#include <arpa/inet.h>\n#include <arpa/nameser.h> /* after normal includes for sunos */\n#include <stdint.h>\n\n#include \"common.h\"\n#include \"dhcpcd.h\"\n\n/* Support very old arpa/nameser.h as found in OpenBSD */\n#ifndef NS_MAXDNAME\n#define NS_MAXCDNAME MAXCDNAME\n#define NS_MAXDNAME  MAXDNAME\n#define NS_MAXLABEL  MAXLABEL\n#endif\n\n#define OT_REQUEST   (1 << 0)\n#define OT_UINT8     (1 << 1)\n#define OT_INT8\t     (1 << 2)\n#define OT_UINT16    (1 << 3)\n#define OT_INT16     (1 << 4)\n#define OT_UINT32    (1 << 5)\n#define OT_INT32     (1 << 6)\n#define OT_ADDRIPV4  (1 << 7)\n#define OT_STRING    (1 << 8)\n#define OT_ARRAY     (1 << 9)\n#define OT_RFC3361   (1 << 10)\n#define OT_RFC1035   (1 << 11)\n#define OT_RFC3442   (1 << 12)\n#define OT_OPTIONAL  (1 << 13)\n#define OT_ADDRIPV6  (1 << 14)\n#define OT_BINHEX    (1 << 15)\n#define OT_FLAG\t     (1 << 16)\n#define OT_NOREQ     (1 << 17)\n#define OT_EMBED     (1 << 18)\n#define OT_ENCAP     (1 << 19)\n#define OT_INDEX     (1 << 20)\n#define OT_OPTION    (1 << 21)\n#define OT_DOMAIN    (1 << 22)\n#define OT_ASCII     (1 << 23)\n#define OT_RAW\t     (1 << 24)\n#define OT_ESCSTRING (1 << 25)\n#define OT_ESCFILE   (1 << 26)\n#define OT_BITFLAG   (1 << 27)\n#define OT_RESERVED  (1 << 28)\n#define OT_URI\t     (1 << 29)\n#define OT_TRUNCATED (1 << 30)\n\n#define DHC_REQ(r, n, o) \\\n\t(has_option_mask((r), (o)) && !has_option_mask((n), (o)))\n\n#define DHC_REQOPT(o, r, n)                                                  \\\n\t(!((o)->type & OT_NOREQ) &&                                          \\\n\t    ((o)->type & OT_REQUEST || has_option_mask((r), (o)->option)) && \\\n\t    !has_option_mask((n), (o)->option))\n\nstruct dhcp_opt {\n\tuint32_t option; /* Also used for IANA Enterpise Number */\n\tint type;\n\tsize_t len;\n\tchar *var;\n\n\tint index; /* Index counter for many instances of the same option */\n\tchar bitflags[8];\n\n\t/* Embedded options.\n\t * The option code is irrelevant here. */\n\tstruct dhcp_opt *embopts;\n\tsize_t embopts_len;\n\n\t/* Encapsulated options */\n\tstruct dhcp_opt *encopts;\n\tsize_t encopts_len;\n};\n\nconst char *dhcp_get_hostname(char *, size_t, const struct if_options *);\nstruct dhcp_opt *vivso_find(uint32_t, const void *);\n\nssize_t dhcp_vendor(char *, size_t);\n\nvoid dhcp_print_option_encoding(const struct dhcp_opt *opt, int cols);\n#define add_option_mask(var, val) \\\n\t((var)[(val) >> 3] = (uint8_t)((var)[(val) >> 3] | 1 << ((val) & 7)))\n#define del_option_mask(var, val) \\\n\t((var)[(val) >> 3] = (uint8_t)((var)[(val) >> 3] & ~(1 << ((val) & 7))))\n#define has_option_mask(var, val) \\\n\t((var)[(val) >> 3] & (uint8_t)(1 << ((val) & 7)))\nint make_option_mask(const struct dhcp_opt *, size_t, const struct dhcp_opt *,\n    size_t, uint8_t *, const char *, int);\n\nsize_t encode_rfc1035(const char *src, uint8_t *dst);\nssize_t decode_rfc1035(char *, size_t, const uint8_t *, size_t);\nssize_t print_string(char *, size_t, int, const void *, size_t);\nint dhcp_set_leasefile(char *, size_t, int, const struct interface *);\n\nvoid dhcp_envoption(struct dhcpcd_ctx *, FILE *, const char *, const char *,\n    struct dhcp_opt *,\n    const uint8_t *(*dgetopt)(struct dhcpcd_ctx *, size_t *, unsigned int *,\n\tsize_t *, const uint8_t *, size_t, struct dhcp_opt **),\n    const uint8_t *od, size_t ol);\nvoid dhcp_zero_index(struct dhcp_opt *);\n\nssize_t dhcp_readfile(struct dhcpcd_ctx *, const char *, void *, size_t);\nssize_t dhcp_writefile(struct dhcpcd_ctx *, const char *, mode_t, const void *,\n    size_t);\nint dhcp_filemtime(struct dhcpcd_ctx *, const char *, time_t *);\nint dhcp_unlink(struct dhcpcd_ctx *, const char *);\nsize_t dhcp_read_hwaddr_aton(struct dhcpcd_ctx *, uint8_t **, const char *);\n#endif\n"
  },
  {
    "path": "src/dhcp.c",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/param.h>\n#include <sys/socket.h>\n\n#include <net/if.h>\n#include <net/route.h>\n#include <netinet/in.h>\n#include <netinet/in_systm.h>\n#include <netinet/ip.h>\n#include <netinet/if_ether.h>\n\n#include <arpa/inet.h>\n#define __FAVOR_BSD /* Nasty glibc hack so we can use BSD semantics for UDP */\n#include <netinet/udp.h>\n#undef __FAVOR_BSD\n\n#ifdef AF_LINK\n#include <net/if_dl.h>\n#endif\n\n#include <assert.h>\n#include <ctype.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <inttypes.h>\n#include <stdalign.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <syslog.h>\n#include <unistd.h>\n\n#define ELOOP_QUEUE ELOOP_DHCP\n#include \"arp.h\"\n#include \"bpf.h\"\n#include \"common.h\"\n#include \"config.h\"\n#include \"dhcp-common.h\"\n#include \"dhcp.h\"\n#include \"dhcpcd.h\"\n#include \"duid.h\"\n#include \"eloop.h\"\n#include \"if.h\"\n#include \"ipv4.h\"\n#include \"ipv4ll.h\"\n#include \"logerr.h\"\n#include \"privsep.h\"\n#include \"sa.h\"\n#include \"script.h\"\n\n#define DAD\t       \"Duplicate address detected\"\n#define DHCP_MIN_LEASE 20\n\n#define IPV4A\t       ADDRIPV4 | ARRAY\n#define IPV4R\t       ADDRIPV4 | REQUEST\n\n/* We should define a maximum for the NAK exponential backoff */\n#define NAKOFF_MAX 60\n\n#ifndef IPDEFTTL\n#define IPDEFTTL 64 /* RFC1340 */\n#endif\n\n/* Support older systems with different defines */\n#if !defined(IP_RECVPKTINFO) && defined(IP_PKTINFO)\n#define IP_RECVPKTINFO IP_PKTINFO\n#endif\n\n/* Assert the correct structure size for on wire */\n__CTASSERT(sizeof(struct ip) == 20);\n__CTASSERT(sizeof(struct udphdr) == 8);\n__CTASSERT(sizeof(struct bootp) == 300);\n#define IP_UDP_SIZE   (sizeof(struct ip) + sizeof(struct udphdr))\n#define BOOTP_MIN_MTU (IP_UDP_SIZE + sizeof(struct bootp))\n\nstruct dhcp_op {\n\tuint8_t value;\n\tconst char *name;\n};\n\nstatic const struct dhcp_op dhcp_ops[] = { { DHCP_DISCOVER, \"DISCOVER\" },\n\t{ DHCP_OFFER, \"OFFER\" }, { DHCP_REQUEST, \"REQUEST\" },\n\t{ DHCP_DECLINE, \"DECLINE\" }, { DHCP_ACK, \"ACK\" }, { DHCP_NAK, \"NAK\" },\n\t{ DHCP_RELEASE, \"RELEASE\" }, { DHCP_INFORM, \"INFORM\" },\n\t{ DHCP_FORCERENEW, \"FORCERENEW\" }, { 0, NULL } };\n\nstatic const char *const dhcp_params[] = { \"ip_address\", \"subnet_cidr\",\n\t\"network_number\", \"filename\", \"server_name\", NULL };\n\nstatic int dhcp_openbpf(struct interface *);\nstatic void dhcp_start1(void *);\n#if defined(ARP) && (!defined(KERNEL_RFC5227) || defined(ARPING))\nstatic void dhcp_arp_found(struct arp_state *, const struct arp_msg *);\n#endif\nstatic void dhcp_handledhcp(struct interface *, struct bootp *, size_t,\n    const struct in_addr *);\nstatic void dhcp_handleifudp(void *, unsigned short);\nstatic int dhcp_initstate(struct interface *);\n\nvoid\ndhcp_printoptions(const struct dhcpcd_ctx *ctx, const struct dhcp_opt *opts,\n    size_t opts_len)\n{\n\tconst char *const *p;\n\tsize_t i, j;\n\tconst struct dhcp_opt *opt, *opt2;\n\tint cols;\n\n\tfor (p = dhcp_params; *p; p++)\n\t\tprintf(\"    %s\\n\", *p);\n\n\tfor (i = 0, opt = ctx->dhcp_opts; i < ctx->dhcp_opts_len; i++, opt++) {\n\t\tfor (j = 0, opt2 = opts; j < opts_len; j++, opt2++)\n\t\t\tif (opt->option == opt2->option)\n\t\t\t\tbreak;\n\t\tif (j == opts_len) {\n\t\t\tcols = printf(\"%03d %s\", opt->option, opt->var);\n\t\t\tdhcp_print_option_encoding(opt, cols);\n\t\t}\n\t}\n\tfor (i = 0, opt = opts; i < opts_len; i++, opt++) {\n\t\tcols = printf(\"%03d %s\", opt->option, opt->var);\n\t\tdhcp_print_option_encoding(opt, cols);\n\t}\n}\n\nstatic const uint8_t *\nget_option(struct dhcpcd_ctx *ctx, const struct bootp *bootp, size_t bootp_len,\n    unsigned int opt, size_t *opt_len)\n{\n\tconst uint8_t *p, *e;\n\tuint8_t l, o, ol, overl, *bp;\n\tconst uint8_t *op;\n\tsize_t bl;\n\n\tif (bootp == NULL || bootp_len < DHCP_MIN_LEN) {\n\t\terrno = EINVAL;\n\t\treturn NULL;\n\t}\n\n\t/* Check we have the magic cookie */\n\tif (!IS_DHCP(bootp)) {\n\t\terrno = ENOTSUP;\n\t\treturn NULL;\n\t}\n\n\tp = bootp->vend + 4; /* options after the 4 byte cookie */\n\te = (const uint8_t *)bootp + bootp_len;\n\tol = o = overl = 0;\n\tbp = NULL;\n\top = NULL;\n\tbl = 0;\n\twhile (p < e) {\n\t\to = *p++;\n\t\tswitch (o) {\n\t\tcase DHO_PAD:\n\t\t\t/* No length to read */\n\t\t\tcontinue;\n\t\tcase DHO_END:\n\t\t\tif (overl & 1) {\n\t\t\t\t/* bit 1 set means parse boot file */\n\t\t\t\toverl = (uint8_t)(overl & ~1);\n\t\t\t\tp = bootp->file;\n\t\t\t\te = p + sizeof(bootp->file);\n\t\t\t} else if (overl & 2) {\n\t\t\t\t/* bit 2 set means parse server name */\n\t\t\t\toverl = (uint8_t)(overl & ~2);\n\t\t\t\tp = bootp->sname;\n\t\t\t\te = p + sizeof(bootp->sname);\n\t\t\t} else\n\t\t\t\tgoto exit;\n\t\t\t/* No length to read */\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* Check we can read the length */\n\t\tif (p == e) {\n\t\t\terrno = EINVAL;\n\t\t\treturn NULL;\n\t\t}\n\t\tl = *p++;\n\n\t\t/* Check we can read the option data, if present */\n\t\tif (p + l > e) {\n\t\t\terrno = EINVAL;\n\t\t\treturn NULL;\n\t\t}\n\n\t\tif (o == DHO_OPTSOVERLOADED) {\n\t\t\t/* Ensure we only get this option once by setting\n\t\t\t * the last bit as well as the value.\n\t\t\t * This is valid because only the first two bits\n\t\t\t * actually mean anything in RFC2132 Section 9.3 */\n\t\t\tif (l == 1 && !overl)\n\t\t\t\toverl = 0x80 | p[0];\n\t\t}\n\n\t\tif (o == opt) {\n\t\t\tif (op) {\n\t\t\t\t/* We must concatonate the options. */\n\t\t\t\tif (bl + l > ctx->opt_buffer_len) {\n\t\t\t\t\tsize_t pos;\n\t\t\t\t\tuint8_t *nb;\n\n\t\t\t\t\tif (bp)\n\t\t\t\t\t\tpos = (size_t)(bp -\n\t\t\t\t\t\t    ctx->opt_buffer);\n\t\t\t\t\telse\n\t\t\t\t\t\tpos = 0;\n\t\t\t\t\tnb = realloc(ctx->opt_buffer, bl + l);\n\t\t\t\t\tif (nb == NULL)\n\t\t\t\t\t\treturn NULL;\n\t\t\t\t\tctx->opt_buffer = nb;\n\t\t\t\t\tctx->opt_buffer_len = bl + l;\n\t\t\t\t\tbp = ctx->opt_buffer + pos;\n\t\t\t\t}\n\t\t\t\tif (bp == NULL)\n\t\t\t\t\tbp = ctx->opt_buffer;\n\t\t\t\tmemcpy(bp, op, ol);\n\t\t\t\tbp += ol;\n\t\t\t}\n\t\t\tol = l;\n\t\t\top = p;\n\t\t\tbl += ol;\n\t\t}\n\t\tp += l;\n\t}\n\nexit:\n\tif (opt_len)\n\t\t*opt_len = bl;\n\tif (bp) {\n\t\tmemcpy(bp, op, ol);\n\t\treturn (const uint8_t *)ctx->opt_buffer;\n\t}\n\tif (op)\n\t\treturn op;\n\terrno = ENOENT;\n\treturn NULL;\n}\n\nstatic int\nget_option_addr(struct dhcpcd_ctx *ctx, struct in_addr *a,\n    const struct bootp *bootp, size_t bootp_len, uint8_t option)\n{\n\tconst uint8_t *p;\n\tsize_t len;\n\n\tp = get_option(ctx, bootp, bootp_len, option, &len);\n\tif (!p || len < (ssize_t)sizeof(a->s_addr))\n\t\treturn -1;\n\tmemcpy(&a->s_addr, p, sizeof(a->s_addr));\n\treturn 0;\n}\n\nstatic int\nget_option_uint32(struct dhcpcd_ctx *ctx, uint32_t *i,\n    const struct bootp *bootp, size_t bootp_len, uint8_t option)\n{\n\tconst uint8_t *p;\n\tsize_t len;\n\tuint32_t d;\n\n\tp = get_option(ctx, bootp, bootp_len, option, &len);\n\tif (!p || len != (ssize_t)sizeof(d))\n\t\treturn -1;\n\tmemcpy(&d, p, sizeof(d));\n\tif (i)\n\t\t*i = ntohl(d);\n\treturn 0;\n}\n\nstatic int\nget_option_uint16(struct dhcpcd_ctx *ctx, uint16_t *i,\n    const struct bootp *bootp, size_t bootp_len, uint8_t option)\n{\n\tconst uint8_t *p;\n\tsize_t len;\n\tuint16_t d;\n\n\tp = get_option(ctx, bootp, bootp_len, option, &len);\n\tif (!p || len != (ssize_t)sizeof(d))\n\t\treturn -1;\n\tmemcpy(&d, p, sizeof(d));\n\tif (i)\n\t\t*i = ntohs(d);\n\treturn 0;\n}\n\nstatic int\nget_option_uint8(struct dhcpcd_ctx *ctx, uint8_t *i, const struct bootp *bootp,\n    size_t bootp_len, uint8_t option)\n{\n\tconst uint8_t *p;\n\tsize_t len;\n\n\tp = get_option(ctx, bootp, bootp_len, option, &len);\n\tif (!p || len != (ssize_t)sizeof(*p))\n\t\treturn -1;\n\tif (i)\n\t\t*i = *(p);\n\treturn 0;\n}\n\nssize_t\nprint_rfc3442(FILE *fp, const uint8_t *data, size_t data_len)\n{\n\tconst uint8_t *p = data, *e;\n\tsize_t ocets;\n\tuint8_t cidr;\n\tstruct in_addr addr;\n\n\t/* Minimum is 5 -first is CIDR and a router length of 4 */\n\tif (data_len < 5) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\te = p + data_len;\n\twhile (p < e) {\n\t\tif (p != data) {\n\t\t\tif (fputc(' ', fp) == EOF)\n\t\t\t\treturn -1;\n\t\t}\n\t\tcidr = *p++;\n\t\tif (cidr > 32) {\n\t\t\terrno = EINVAL;\n\t\t\treturn -1;\n\t\t}\n\t\tocets = (size_t)(cidr + 7) / NBBY;\n\t\tif (p + 4 + ocets > e) {\n\t\t\terrno = ERANGE;\n\t\t\treturn -1;\n\t\t}\n\t\t/* If we have ocets then we have a destination and netmask */\n\t\taddr.s_addr = 0;\n\t\tif (ocets > 0) {\n\t\t\tmemcpy(&addr.s_addr, p, ocets);\n\t\t\tp += ocets;\n\t\t}\n\t\tif (fprintf(fp, \"%s/%d\", inet_ntoa(addr), cidr) == -1)\n\t\t\treturn -1;\n\n\t\t/* Finally, snag the router */\n\t\tmemcpy(&addr.s_addr, p, 4);\n\t\tp += 4;\n\t\tif (fprintf(fp, \" %s\", inet_ntoa(addr)) == -1)\n\t\t\treturn -1;\n\t}\n\n\tif (fputc('\\0', fp) == EOF)\n\t\treturn -1;\n\treturn 1;\n}\n\nstatic int\ndecode_rfc3442_rt(rb_tree_t *routes, struct interface *ifp, const uint8_t *data,\n    size_t dl)\n{\n\tconst uint8_t *p = data;\n\tconst uint8_t *e;\n\tuint8_t cidr;\n\tsize_t ocets;\n\tstruct rt *rt = NULL;\n\tstruct in_addr dest, netmask, gateway;\n\tint n;\n\n\t/* Minimum is 5 -first is CIDR and a router length of 4 */\n\tif (dl < 5) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tn = 0;\n\te = p + dl;\n\twhile (p < e) {\n\t\tcidr = *p++;\n\t\tif (cidr > 32) {\n\t\t\terrno = EINVAL;\n\t\t\treturn -1;\n\t\t}\n\n\t\tocets = (size_t)(cidr + 7) / NBBY;\n\t\tif (p + 4 + ocets > e) {\n\t\t\terrno = ERANGE;\n\t\t\treturn -1;\n\t\t}\n\n\t\tif ((rt = rt_new(ifp)) == NULL)\n\t\t\treturn -1;\n\n\t\t/* If we have ocets then we have a destination and netmask */\n\t\tdest.s_addr = 0;\n\t\tif (ocets > 0) {\n\t\t\tmemcpy(&dest.s_addr, p, ocets);\n\t\t\tp += ocets;\n\t\t\tnetmask.s_addr = htonl(~0U << (32 - cidr));\n\t\t} else\n\t\t\tnetmask.s_addr = 0;\n\n\t\t/* Finally, snag the router */\n\t\tmemcpy(&gateway.s_addr, p, 4);\n\t\tp += 4;\n\n\t\tif (netmask.s_addr == INADDR_BROADCAST)\n\t\t\trt->rt_flags = RTF_HOST;\n\n\t\tsa_in_init(&rt->rt_dest, &dest);\n\t\tsa_in_init(&rt->rt_netmask, &netmask);\n\t\tsa_in_init(&rt->rt_gateway, &gateway);\n\t\tif (rt_proto_add(routes, rt))\n\t\t\tn = 1;\n\t}\n\treturn n;\n}\n\nssize_t\nprint_rfc3361(FILE *fp, const uint8_t *data, size_t dl)\n{\n\tuint8_t enc;\n\tchar sip[NS_MAXDNAME];\n\tstruct in_addr addr;\n\n\tif (dl < 2) {\n\t\terrno = EINVAL;\n\t\treturn 0;\n\t}\n\n\tenc = *data++;\n\tdl--;\n\tswitch (enc) {\n\tcase 0:\n\t\tif (decode_rfc1035(sip, sizeof(sip), data, dl) == -1)\n\t\t\treturn -1;\n\t\tif (efprintf(fp, \"%s\", sip) == -1)\n\t\t\treturn -1;\n\t\tbreak;\n\tcase 1:\n\t\tif (dl % 4 != 0) {\n\t\t\terrno = EINVAL;\n\t\t\tbreak;\n\t\t}\n\t\taddr.s_addr = INADDR_BROADCAST;\n\t\tfor (; dl != 0;\n\t\t    data += sizeof(addr.s_addr), dl -= sizeof(addr.s_addr)) {\n\t\t\tmemcpy(&addr.s_addr, data, sizeof(addr.s_addr));\n\t\t\tif (fprintf(fp, \"%s\", inet_ntoa(addr)) == -1)\n\t\t\t\treturn -1;\n\t\t\tif (dl != sizeof(addr.s_addr)) {\n\t\t\t\tif (fputc(' ', fp) == EOF)\n\t\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t\tif (fputc('\\0', fp) == EOF)\n\t\t\treturn -1;\n\t\tbreak;\n\tdefault:\n\t\terrno = EINVAL;\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n\nstatic char *\nget_option_string(struct dhcpcd_ctx *ctx, const struct bootp *bootp,\n    size_t bootp_len, uint8_t option)\n{\n\tsize_t len;\n\tconst uint8_t *p;\n\tchar *s;\n\n\tp = get_option(ctx, bootp, bootp_len, option, &len);\n\tif (!p || len == 0 || *p == '\\0')\n\t\treturn NULL;\n\n\ts = malloc(sizeof(char) * (len + 1));\n\tif (s) {\n\t\tmemcpy(s, p, len);\n\t\ts[len] = '\\0';\n\t}\n\treturn s;\n}\n\n/* This calculates the netmask that we should use for static routes.\n * This IS different from the calculation used to calculate the netmask\n * for an interface address. */\nstatic uint32_t\nroute_netmask(uint32_t ip_in)\n{\n\t/* used to be unsigned long - check if error */\n\tuint32_t p = ntohl(ip_in);\n\tuint32_t t;\n\n\tif (IN_CLASSA(p))\n\t\tt = ~IN_CLASSA_NET;\n\telse {\n\t\tif (IN_CLASSB(p))\n\t\t\tt = ~IN_CLASSB_NET;\n\t\telse {\n\t\t\tif (IN_CLASSC(p))\n\t\t\t\tt = ~IN_CLASSC_NET;\n\t\t\telse\n\t\t\t\tt = 0;\n\t\t}\n\t}\n\n\twhile (t & p)\n\t\tt >>= 1;\n\n\treturn (htonl(~t));\n}\n\n/* We need to obey routing options.\n * If we have a CSR then we only use that.\n * Otherwise we add static routes and then routers. */\nstatic int\nget_option_routes(rb_tree_t *routes, struct interface *ifp,\n    const struct bootp *bootp, size_t bootp_len)\n{\n\tstruct if_options *ifo = ifp->options;\n\tconst uint8_t *p;\n\tconst uint8_t *e;\n\tstruct rt *rt = NULL;\n\tstruct in_addr dest, netmask, gateway;\n\tsize_t len;\n\tconst char *csr = \"\";\n\tint n;\n\n\t/* If we have CSR's then we MUST use these only */\n\tif (!has_option_mask(ifo->nomask, DHO_CSR))\n\t\tp = get_option(ifp->ctx, bootp, bootp_len, DHO_CSR, &len);\n\telse\n\t\tp = NULL;\n\t/* Check for crappy MS option */\n\tif (!p && !has_option_mask(ifo->nomask, DHO_MSCSR)) {\n\t\tp = get_option(ifp->ctx, bootp, bootp_len, DHO_MSCSR, &len);\n\t\tif (p)\n\t\t\tcsr = \"MS \";\n\t}\n\tif (p && (n = decode_rfc3442_rt(routes, ifp, p, len)) != -1) {\n\t\tconst struct dhcp_state *state;\n\n\t\tstate = D_CSTATE(ifp);\n\t\tif (!(ifo->options & DHCPCD_CSR_WARNED) &&\n\t\t    !(state->added & STATE_FAKE)) {\n\t\t\tlogdebugx(\"%s: using %sClassless Static Routes\",\n\t\t\t    ifp->name, csr);\n\t\t\tifo->options |= DHCPCD_CSR_WARNED;\n\t\t}\n\t\treturn n;\n\t}\n\n\tn = 0;\n\t/* OK, get our static routes first. */\n\tif (!has_option_mask(ifo->nomask, DHO_STATICROUTE))\n\t\tp = get_option(ifp->ctx, bootp, bootp_len, DHO_STATICROUTE,\n\t\t    &len);\n\telse\n\t\tp = NULL;\n\t/* RFC 2131 Section 5.8 states length MUST be in multiples of 8 */\n\tif (p && len % 8 == 0) {\n\t\te = p + len;\n\t\twhile (p < e) {\n\t\t\tmemcpy(&dest.s_addr, p, sizeof(dest.s_addr));\n\t\t\tp += 4;\n\t\t\tmemcpy(&gateway.s_addr, p, sizeof(gateway.s_addr));\n\t\t\tp += 4;\n\t\t\t/* RFC 2131 Section 5.8 states default route is\n\t\t\t * illegal */\n\t\t\tif (gateway.s_addr == INADDR_ANY)\n\t\t\t\tcontinue;\n\t\t\tif ((rt = rt_new(ifp)) == NULL)\n\t\t\t\treturn -1;\n\n\t\t\t/* A on-link host route is normally set by having the\n\t\t\t * gateway match the destination or assigned address */\n\t\t\tif (gateway.s_addr == dest.s_addr ||\n\t\t\t    (gateway.s_addr == bootp->yiaddr ||\n\t\t\t\tgateway.s_addr == bootp->ciaddr)) {\n\t\t\t\tgateway.s_addr = INADDR_ANY;\n\t\t\t\tnetmask.s_addr = INADDR_BROADCAST;\n\t\t\t} else\n\t\t\t\tnetmask.s_addr = route_netmask(dest.s_addr);\n\t\t\tif (netmask.s_addr == INADDR_BROADCAST)\n\t\t\t\trt->rt_flags = RTF_HOST;\n\n\t\t\tsa_in_init(&rt->rt_dest, &dest);\n\t\t\tsa_in_init(&rt->rt_netmask, &netmask);\n\t\t\tsa_in_init(&rt->rt_gateway, &gateway);\n\t\t\tif (rt_proto_add(routes, rt))\n\t\t\t\tn++;\n\t\t}\n\t}\n\n\t/* Now grab our routers */\n\tif (!has_option_mask(ifo->nomask, DHO_ROUTER))\n\t\tp = get_option(ifp->ctx, bootp, bootp_len, DHO_ROUTER, &len);\n\telse\n\t\tp = NULL;\n\tif (p && len % 4 == 0) {\n\t\te = p + len;\n\t\tdest.s_addr = INADDR_ANY;\n\t\tnetmask.s_addr = INADDR_ANY;\n\t\twhile (p < e) {\n\t\t\tif ((rt = rt_new(ifp)) == NULL)\n\t\t\t\treturn -1;\n\t\t\tmemcpy(&gateway.s_addr, p, sizeof(gateway.s_addr));\n\t\t\tp += 4;\n\t\t\tsa_in_init(&rt->rt_dest, &dest);\n\t\t\tsa_in_init(&rt->rt_netmask, &netmask);\n\t\t\tsa_in_init(&rt->rt_gateway, &gateway);\n\t\t\tif (rt_proto_add(routes, rt))\n\t\t\t\tn++;\n\t\t}\n\t}\n\n\treturn n;\n}\n\nuint16_t\ndhcp_get_mtu(const struct interface *ifp)\n{\n\tconst struct dhcp_state *state;\n\tuint16_t mtu;\n\n\tif (ifp->options->mtu)\n\t\treturn (uint16_t)ifp->options->mtu;\n\tmtu = 0; /* bogus gcc warning */\n\tif ((state = D_CSTATE(ifp)) == NULL ||\n\t    has_option_mask(ifp->options->nomask, DHO_MTU) ||\n\t    get_option_uint16(ifp->ctx, &mtu, state->new, state->new_len,\n\t\tDHO_MTU) == -1)\n\t\treturn 0;\n\tif (mtu < IPV4_MMTU)\n\t\treturn IPV4_MMTU;\n\treturn mtu;\n}\n\n/* Grab our routers from the DHCP message and apply any MTU value\n * the message contains */\nint\ndhcp_get_routes(rb_tree_t *routes, struct interface *ifp)\n{\n\tconst struct dhcp_state *state;\n\n\tif ((state = D_CSTATE(ifp)) == NULL || !(state->added & STATE_ADDED))\n\t\treturn 0;\n\treturn get_option_routes(routes, ifp, state->new, state->new_len);\n}\n\n/* Assumes DHCP options */\nstatic int\ndhcp_message_add_addr(struct bootp *bootp, uint8_t type, struct in_addr addr)\n{\n\tuint8_t *p;\n\tsize_t len;\n\n\tp = bootp->vend;\n\twhile (*p != DHO_END) {\n\t\tp++;\n\t\tp += *p + 1;\n\t}\n\n\tlen = (size_t)(p - bootp->vend);\n\tif (len + 6 > sizeof(bootp->vend)) {\n\t\terrno = ENOMEM;\n\t\treturn -1;\n\t}\n\n\t*p++ = type;\n\t*p++ = 4;\n\tmemcpy(p, &addr.s_addr, 4);\n\tp += 4;\n\t*p = DHO_END;\n\treturn 0;\n}\n\n#ifndef SMALL\nstruct rfc3396_ctx {\n\tuint8_t code;\n\tuint8_t *len;\n\tuint8_t **buf;\n\tsize_t buflen;\n};\n\n/* Encode data as a DHCP Long Option, RFC 3396. */\n/* NOTE: Wireshark does not decode this correctly\n * when the option overflows the boundary and another option\n * is created to hold the resta of the data.\n * Tested against Wireshark-4.4.1 */\n#define RFC3396_BOUNDARY 255UL\nstatic ssize_t\nrfc3396_write(struct rfc3396_ctx *ctx, void *data, size_t len)\n{\n\tuint8_t *datap = data;\n\tsize_t wlen, left, r = 0;\n\n\twhile (len != 0) {\n\t\tif (ctx->len == NULL || *ctx->len == RFC3396_BOUNDARY) {\n\t\t\tif (ctx->buflen < 2) {\n\t\t\t\terrno = ENOMEM;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\t*(*ctx->buf)++ = ctx->code;\n\t\t\tctx->len = (*ctx->buf)++;\n\t\t\t*ctx->len = 0;\n\t\t\tctx->buflen -= 2;\n\t\t\tr += 2;\n\t\t}\n\n\t\twlen = len < RFC3396_BOUNDARY ? len : RFC3396_BOUNDARY;\n\t\tleft = RFC3396_BOUNDARY - *ctx->len;\n\t\tif (left < wlen)\n\t\t\twlen = left;\n\t\tif (ctx->buflen < wlen) {\n\t\t\terrno = ENOMEM;\n\t\t\treturn -1;\n\t\t}\n\n\t\tmemcpy(*ctx->buf, datap, wlen);\n\t\tdatap += wlen;\n\t\t*ctx->buf += wlen;\n\t\tctx->buflen -= wlen;\n\t\t*ctx->len = (uint8_t)(*ctx->len + wlen);\n\t\tlen -= wlen;\n\t\tr += wlen;\n\t}\n\n\treturn (ssize_t)r;\n}\n\nstatic ssize_t\nrfc3396_write_byte(struct rfc3396_ctx *ctx, uint8_t byte)\n{\n\treturn rfc3396_write(ctx, &byte, sizeof(byte));\n}\n\nstatic uint8_t *\nrfc3396_zero(struct rfc3396_ctx *ctx)\n{\n\tuint8_t *zerop = *ctx->buf, zero = 0;\n\n\tif (rfc3396_write(ctx, &zero, sizeof(zero)) == -1)\n\t\treturn NULL;\n\treturn zerop;\n}\n#endif\n\nstatic ssize_t\nmake_message(struct bootp **bootpm, const struct interface *ifp, uint8_t type)\n{\n\tstruct bootp *bootp;\n\tuint8_t *lp, *p, *e;\n\tuint8_t *n_params = NULL;\n\tuint32_t ul;\n\tuint16_t sz;\n\tsize_t len, i;\n\tconst struct dhcp_opt *opt;\n\tstruct if_options *ifo = ifp->options;\n\tconst struct dhcp_state *state = D_CSTATE(ifp);\n\tconst struct dhcp_lease *lease = &state->lease;\n\tchar hbuf[HOSTNAME_MAX_LEN + 1];\n\tconst char *hostname;\n\tint mtu;\n#ifdef AUTH\n\tuint8_t *auth, auth_len;\n#endif\n\n\t/* We could take the DHCPv6 approach and work out the\n\t * message length up front rather than this big hammer approach. */\n\tif ((mtu = if_getmtu(ifp)) == -1) {\n\t\tlogerr(\"%s: if_getmtu\", ifp->name);\n\t\treturn -1;\n\t}\n\tif ((size_t)mtu < BOOTP_MIN_MTU) {\n\t\tlogerr(\"%s: interface mtu is too small (%d<%zu)\", ifp->name,\n\t\t    mtu, BOOTP_MIN_MTU);\n\t\treturn -1;\n\t}\n\n\tif (ifo->options & DHCPCD_BOOTP) {\n\t\tbootp = calloc(1, sizeof(*bootp));\n\t} else {\n\t\t/* Make the maximal message we could send */\n\t\tbootp = calloc(1, (size_t)mtu - IP_UDP_SIZE);\n\t}\n\n\tif (bootp == NULL)\n\t\treturn -1;\n\t*bootpm = bootp;\n\n\tif (state->addr != NULL &&\n\t    (type == DHCP_INFORM || type == DHCP_RELEASE ||\n\t\t(type == DHCP_REQUEST &&\n\t\t    state->addr->mask.s_addr == lease->mask.s_addr &&\n\t\t    (state->new == NULL || IS_DHCP(state->new)) &&\n\t\t    !(state->added & (STATE_FAKE | STATE_EXPIRED)))))\n\t\tbootp->ciaddr = state->addr->addr.s_addr;\n\n\tbootp->op = BOOTREQUEST;\n\tbootp->htype = (uint8_t)ifp->hwtype;\n\tif (ifp->hwlen != 0 && ifp->hwlen <= sizeof(bootp->chaddr)) {\n\t\tbootp->hlen = (uint8_t)ifp->hwlen;\n\t\tmemcpy(&bootp->chaddr, &ifp->hwaddr, ifp->hwlen);\n\t}\n\n\tif (ifo->options & DHCPCD_BROADCAST && bootp->ciaddr == INADDR_ANY &&\n\t    type != DHCP_DECLINE && type != DHCP_RELEASE)\n\t\tbootp->flags = htons(BROADCAST_FLAG);\n\n\tif (type != DHCP_DECLINE && type != DHCP_RELEASE) {\n\t\tstruct timespec tv;\n\t\tunsigned long long secs;\n\n\t\tclock_gettime(CLOCK_MONOTONIC, &tv);\n\t\tsecs = eloop_timespec_diff(&tv, &state->started, NULL);\n\t\tif (secs > UINT16_MAX)\n\t\t\tbootp->secs = htons((uint16_t)UINT16_MAX);\n\t\telse\n\t\t\tbootp->secs = htons((uint16_t)secs);\n\t}\n\n\tbootp->xid = htonl(state->xid);\n\n\tif (ifo->options & DHCPCD_BOOTP)\n\t\treturn sizeof(*bootp);\n\n\tp = bootp->vend;\n\te = (uint8_t *)bootp + ((size_t)mtu - IP_UDP_SIZE - 1 /* DHO_END */);\n\n\tul = htonl(MAGIC_COOKIE);\n\tmemcpy(p, &ul, sizeof(ul));\n\tp += sizeof(ul);\n\n#define AREA_LEFT (size_t)(e - p)\n#define AREA_FIT(s)          \\\n\tif ((s) > AREA_LEFT) \\\n\tgoto toobig\n#define AREA_CHECK(s)              \\\n\tif ((s) + 2UL > AREA_LEFT) \\\n\tgoto toobig\n#define PUT_ADDR(o, a)                      \\\n\tdo {                                \\\n\t\tAREA_CHECK(4);              \\\n\t\t*p++ = (o);                 \\\n\t\t*p++ = 4;                   \\\n\t\tmemcpy(p, &(a)->s_addr, 4); \\\n\t\tp += 4;                     \\\n\t} while (0 /* CONSTCOND */)\n\n\t/*\n\t * RFC 7844 3.1 says options should be randomised, but if not\n\t * then in numerical order.\n\t * RFC 2131 makes no mention of any ordering requirement by the client.\n\t * RFC 2132 says this about the Parameter Request List option:\n\t *     The client MAY list the options in order of preference.\n\t *\n\t * Some DHCP servers sadly ignore this and require message type first.\n\t */\n\n\tAREA_CHECK(3);\n\t*p++ = DHO_MESSAGETYPE;\n\t*p++ = 1;\n\t*p++ = type;\n\n\tbool putip = false;\n\tif (lease->addr.s_addr && lease->cookie == htonl(MAGIC_COOKIE)) {\n\t\tif (type == DHCP_DECLINE ||\n\t\t    (type == DHCP_REQUEST &&\n\t\t\t(state->addr == NULL ||\n\t\t\t    state->added & (STATE_FAKE | STATE_EXPIRED) ||\n\t\t\t    lease->addr.s_addr != state->addr->addr.s_addr))) {\n\t\t\tputip = true;\n\t\t\tPUT_ADDR(DHO_IPADDRESS, &lease->addr);\n\t\t}\n\t}\n\n\tif (lease->addr.s_addr && lease->cookie == htonl(MAGIC_COOKIE)) {\n\t\tif (type == DHCP_RELEASE || putip) {\n\t\t\tif (lease->server.s_addr)\n\t\t\t\tPUT_ADDR(DHO_SERVERID, &lease->server);\n\t\t}\n\t}\n\n\tif (type == DHCP_DECLINE) {\n\t\tlen = strlen(DAD);\n\t\tif (len > AREA_LEFT) {\n\t\t\t*p++ = DHO_MESSAGE;\n\t\t\t*p++ = (uint8_t)len;\n\t\t\tmemcpy(p, DAD, len);\n\t\t\tp += len;\n\t\t}\n\t}\n\n#define DHCP_DIR(type)                                       \\\n\t((type) == DHCP_DISCOVER || (type) == DHCP_INFORM || \\\n\t    (type) == DHCP_REQUEST)\n\n\tif (DHCP_DIR(type)) {\n\t\t/* vendor is already encoded correctly, so just add it */\n\t\tif (ifo->vendor[0]) {\n\t\t\tAREA_CHECK(ifo->vendor[0]);\n\t\t\t*p++ = DHO_VENDOR;\n\t\t\tmemcpy(p, ifo->vendor, (size_t)ifo->vendor[0] + 1);\n\t\t\tp += ifo->vendor[0] + 1;\n\t\t}\n\t}\n\n\tif (type == DHCP_DISCOVER && ifo->options & DHCPCD_REQUEST)\n\t\tPUT_ADDR(DHO_IPADDRESS, &ifo->req_addr);\n\n\tif (DHCP_DIR(type)) {\n\t\tif (type != DHCP_INFORM) {\n\t\t\tif (ifo->leasetime != 0) {\n\t\t\t\tAREA_CHECK(4);\n\t\t\t\t*p++ = DHO_LEASETIME;\n\t\t\t\t*p++ = 4;\n\t\t\t\tul = htonl(ifo->leasetime);\n\t\t\t\tmemcpy(p, &ul, 4);\n\t\t\t\tp += 4;\n\t\t\t}\n\t\t}\n\n\t\tAREA_CHECK(0);\n\t\t*p++ = DHO_PARAMETERREQUESTLIST;\n\t\tn_params = p;\n\t\t*p++ = 0;\n\t\tfor (i = 0, opt = ifp->ctx->dhcp_opts;\n\t\t    i < ifp->ctx->dhcp_opts_len; i++, opt++) {\n\t\t\tif (!DHC_REQOPT(opt, ifo->requestmask, ifo->nomask))\n\t\t\t\tcontinue;\n\t\t\tif (type == DHCP_INFORM &&\n\t\t\t    (opt->option == DHO_RENEWALTIME ||\n\t\t\t\topt->option == DHO_REBINDTIME))\n\t\t\t\tcontinue;\n\t\t\tAREA_FIT(1);\n\t\t\t*p++ = (uint8_t)opt->option;\n\t\t}\n\t\tfor (i = 0, opt = ifo->dhcp_override;\n\t\t    i < ifo->dhcp_override_len; i++, opt++) {\n\t\t\t/* Check if added above */\n\t\t\tfor (lp = n_params + 1; lp < p; lp++)\n\t\t\t\tif (*lp == (uint8_t)opt->option)\n\t\t\t\t\tbreak;\n\t\t\tif (lp < p)\n\t\t\t\tcontinue;\n\t\t\tif (!DHC_REQOPT(opt, ifo->requestmask, ifo->nomask))\n\t\t\t\tcontinue;\n\t\t\tif (type == DHCP_INFORM &&\n\t\t\t    (opt->option == DHO_RENEWALTIME ||\n\t\t\t\topt->option == DHO_REBINDTIME))\n\t\t\t\tcontinue;\n\t\t\tAREA_FIT(1);\n\t\t\t*p++ = (uint8_t)opt->option;\n\t\t}\n\t\t*n_params = (uint8_t)(p - n_params - 1);\n\n\t\tif (mtu != -1 &&\n\t\t    !(has_option_mask(ifo->nomask, DHO_MAXMESSAGESIZE))) {\n\t\t\tAREA_CHECK(2);\n\t\t\t*p++ = DHO_MAXMESSAGESIZE;\n\t\t\t*p++ = 2;\n\t\t\tsz = htons((uint16_t)((size_t)mtu - IP_UDP_SIZE));\n\t\t\tmemcpy(p, &sz, 2);\n\t\t\tp += 2;\n\t\t}\n\n\t\tif (ifo->userclass[0] &&\n\t\t    !has_option_mask(ifo->nomask, DHO_USERCLASS)) {\n\t\t\tAREA_CHECK(ifo->userclass[0]);\n\t\t\t*p++ = DHO_USERCLASS;\n\t\t\tmemcpy(p, ifo->userclass,\n\t\t\t    (size_t)ifo->userclass[0] + 1);\n\t\t\tp += ifo->userclass[0] + 1;\n\t\t}\n\t}\n\n\tif (state->clientid) {\n\t\tAREA_CHECK(state->clientid[0]);\n\t\t*p++ = DHO_CLIENTID;\n\t\tmemcpy(p, state->clientid, (size_t)state->clientid[0] + 1);\n\t\tp += state->clientid[0] + 1;\n\t}\n\n\tif (DHCP_DIR(type) &&\n\t    !has_option_mask(ifo->nomask, DHO_VENDORCLASSID) &&\n\t    ifo->vendorclassid[0]) {\n\t\tAREA_CHECK(ifo->vendorclassid[0]);\n\t\t*p++ = DHO_VENDORCLASSID;\n\t\tmemcpy(p, ifo->vendorclassid,\n\t\t    (size_t)ifo->vendorclassid[0] + 1);\n\t\tp += ifo->vendorclassid[0] + 1;\n\t}\n\n\tif (type == DHCP_DISCOVER && !(ifp->ctx->options & DHCPCD_TEST) &&\n\t    DHC_REQ(ifo->requestmask, ifo->nomask, DHO_RAPIDCOMMIT)) {\n\t\t/* RFC 4039 Section 3 */\n\t\tAREA_CHECK(0);\n\t\t*p++ = DHO_RAPIDCOMMIT;\n\t\t*p++ = 0;\n\t}\n\n\tif (DHCP_DIR(type)) {\n\t\thostname = dhcp_get_hostname(hbuf, sizeof(hbuf), ifo);\n\n\t\t/*\n\t\t * RFC4702 3.1 States that if we send the Client FQDN option\n\t\t * then we MUST NOT also send the Host Name option.\n\t\t * Technically we could, but that is not RFC conformant and\n\t\t * also seems to break some DHCP server implemetations such as\n\t\t * Windows. On the other hand, ISC dhcpd is just as non RFC\n\t\t * conformant by not accepting a partially qualified FQDN.\n\t\t */\n\t\tif (ifo->fqdn != FQDN_DISABLE) {\n\t\t\t/* IETF DHC-FQDN option (81), RFC4702 */\n\t\t\ti = 3;\n\t\t\tif (hostname)\n\t\t\t\ti += encode_rfc1035(hostname, NULL);\n\t\t\tAREA_CHECK(i);\n\t\t\t*p++ = DHO_FQDN;\n\t\t\t*p++ = (uint8_t)i;\n\t\t\t/*\n\t\t\t * Flags: 0000NEOS\n\t\t\t * S: 1 => Client requests Server to update\n\t\t\t *         a RR in DNS as well as PTR\n\t\t\t * O: 1 => Server indicates to client that\n\t\t\t *         DNS has been updated\n\t\t\t * E: 1 => Name data is DNS format\n\t\t\t * N: 1 => Client requests Server to not\n\t\t\t *         update DNS\n\t\t\t */\n\t\t\tif (hostname)\n\t\t\t\t*p++ = (uint8_t)((ifo->fqdn & 0x09) | 0x04);\n\t\t\telse\n\t\t\t\t*p++ = (FQDN_NONE & 0x09) | 0x04;\n\t\t\t*p++ = 0; /* from server for PTR RR */\n\t\t\t*p++ = 0; /* from server for A RR if S=1 */\n\t\t\tif (hostname) {\n\t\t\t\ti = encode_rfc1035(hostname, p);\n\t\t\t\tp += i;\n\t\t\t}\n\t\t} else if (ifo->options & DHCPCD_HOSTNAME && hostname) {\n\t\t\tlen = strlen(hostname);\n\t\t\tAREA_CHECK(len);\n\t\t\t*p++ = DHO_HOSTNAME;\n\t\t\t*p++ = (uint8_t)len;\n\t\t\tmemcpy(p, hostname, len);\n\t\t\tp += len;\n\t\t}\n\t}\n\n#ifdef AUTH\n\tauth = NULL; /* appease GCC */\n\tauth_len = 0;\n\tif (ifo->auth.options & DHCPCD_AUTH_SEND) {\n\t\tssize_t alen = dhcp_auth_encode(ifp->ctx, &ifo->auth,\n\t\t    state->auth.token, NULL, 0, 4, type, NULL, 0);\n\t\tif (alen != -1 && alen > UINT8_MAX) {\n\t\t\terrno = ERANGE;\n\t\t\talen = -1;\n\t\t}\n\t\tif (alen == -1)\n\t\t\tlogerr(\"%s: dhcp_auth_encode\", ifp->name);\n\t\telse if (alen != 0) {\n\t\t\tauth_len = (uint8_t)alen;\n\t\t\tAREA_CHECK(auth_len);\n\t\t\t*p++ = DHO_AUTHENTICATION;\n\t\t\t*p++ = auth_len;\n\t\t\tauth = p;\n\t\t\tp += auth_len;\n\t\t}\n\t}\n#endif\n\n\t/* RFC 2563 Auto Configure */\n\tif (type == DHCP_DISCOVER && ifo->options & DHCPCD_IPV4LL &&\n\t    !(has_option_mask(ifo->nomask, DHO_AUTOCONFIGURE))) {\n\t\tAREA_CHECK(1);\n\t\t*p++ = DHO_AUTOCONFIGURE;\n\t\t*p++ = 1;\n\t\t*p++ = 1;\n\t}\n\n\tif (DHCP_DIR(type)) {\n\t\tif (ifo->mudurl[0]) {\n\t\t\tAREA_CHECK(ifo->mudurl[0]);\n\t\t\t*p++ = DHO_MUDURL;\n\t\t\tmemcpy(p, ifo->mudurl, (size_t)ifo->mudurl[0] + 1);\n\t\t\tp += ifo->mudurl[0] + 1;\n\t\t}\n\n#ifndef SMALL\n\t\tif (ifo->vivco_len &&\n\t\t    !has_option_mask(ifo->nomask, DHO_VIVCO)) {\n\t\t\tstruct vivco *vivco = ifo->vivco;\n\t\t\tsize_t vlen = ifo->vivco_len;\n\t\t\tstruct rfc3396_ctx rctx = {\n\t\t\t\t.code = DHO_VIVCO,\n\t\t\t\t.buf = &p,\n\t\t\t\t.buflen = AREA_LEFT,\n\t\t\t};\n\n\t\t\tfor (; vlen > 0; vivco++, vlen--) {\n\t\t\t\tul = htonl(vivco->en);\n\t\t\t\tif (rfc3396_write(&rctx, &ul, sizeof(ul)) == -1)\n\t\t\t\t\tgoto toobig;\n\t\t\t\tlp = rfc3396_zero(&rctx);\n\t\t\t\tif (lp == NULL)\n\t\t\t\t\tgoto toobig;\n\t\t\t\tif (rfc3396_write_byte(&rctx,\n\t\t\t\t\t(uint8_t)vivco->len) == -1)\n\t\t\t\t\tgoto toobig;\n\t\t\t\tif (rfc3396_write(&rctx, vivco->data,\n\t\t\t\t\tvivco->len) == -1)\n\t\t\t\t\tgoto toobig;\n\t\t\t\t*lp = (uint8_t)(*lp + vivco->len + 1);\n\t\t\t}\n\t\t}\n\n\t\tif (ifo->vsio_len && !has_option_mask(ifo->nomask, DHO_VIVSO)) {\n\t\t\tstruct vsio *vso = ifo->vsio;\n\t\t\tsize_t vlen = ifo->vsio_len;\n\t\t\tstruct vsio_so *so;\n\t\t\tsize_t slen;\n\t\t\tstruct rfc3396_ctx rctx = {\n\t\t\t\t.code = DHO_VIVSO,\n\t\t\t\t.buf = &p,\n\t\t\t\t.buflen = AREA_LEFT,\n\t\t\t};\n\n\t\t\tfor (; vlen > 0; vso++, vlen--) {\n\t\t\t\tif (vso->so_len == 0)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tso = vso->so;\n\t\t\t\tslen = vso->so_len;\n\n\t\t\t\tul = htonl(vso->en);\n\t\t\t\tif (rfc3396_write(&rctx, &ul, sizeof(ul)) == -1)\n\t\t\t\t\tgoto toobig;\n\t\t\t\tlp = rfc3396_zero(&rctx);\n\t\t\t\tif (lp == NULL)\n\t\t\t\t\tgoto toobig;\n\n\t\t\t\tfor (; slen > 0; so++, slen--) {\n\t\t\t\t\tif (rfc3396_write_byte(&rctx,\n\t\t\t\t\t\t(uint8_t)so->opt) == -1)\n\t\t\t\t\t\tgoto toobig;\n\t\t\t\t\tif (rfc3396_write_byte(&rctx,\n\t\t\t\t\t\t(uint8_t)so->len) == -1)\n\t\t\t\t\t\tgoto toobig;\n\t\t\t\t\tif (rfc3396_write(&rctx, so->data,\n\t\t\t\t\t\tso->len) == -1)\n\t\t\t\t\t\tgoto toobig;\n\t\t\t\t\t*lp = (uint8_t)(*lp + so->len + 2);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n\n#ifdef AUTH\n\t\tif ((ifo->auth.options & DHCPCD_AUTH_SENDREQUIRE) !=\n\t\t\tDHCPCD_AUTH_SENDREQUIRE &&\n\t\t    !has_option_mask(ifo->nomask, DHO_FORCERENEW_NONCE)) {\n\t\t\t/* We support HMAC-MD5 */\n\t\t\tAREA_CHECK(1);\n\t\t\t*p++ = DHO_FORCERENEW_NONCE;\n\t\t\t*p++ = 1;\n\t\t\t*p++ = AUTH_ALG_HMAC_MD5;\n\t\t}\n#endif\n\t}\n\n\t*p++ = DHO_END;\n\tlen = (size_t)(p - (uint8_t *)bootp);\n\n\t/* Pad out to the BOOTP message length.\n\t * Even if we send a DHCP packet with a variable length vendor area,\n\t * some servers / relay agents don't like packets smaller than\n\t * a BOOTP message which is fine because that's stipulated\n\t * in RFC1542 section 2.1. */\n\twhile (len < sizeof(*bootp)) {\n\t\t*p++ = DHO_PAD;\n\t\tlen++;\n\t}\n\n#ifdef AUTH\n\tif (ifo->auth.options & DHCPCD_AUTH_SEND && auth_len != 0)\n\t\tdhcp_auth_encode(ifp->ctx, &ifo->auth, state->auth.token,\n\t\t    (uint8_t *)bootp, len, 4, type, auth, auth_len);\n#endif\n\n\treturn (ssize_t)len;\n\ntoobig:\n\tlogerrx(\"%s: DHCP message too big\", ifp->name);\n\tfree(bootp);\n\treturn -1;\n}\n\nstatic size_t\nread_lease(struct interface *ifp, struct bootp **bootp)\n{\n\tunion {\n\t\tstruct bootp bootp;\n\t\tuint8_t buf[FRAMELEN_MAX];\n\t} buf;\n\tstruct dhcp_state *state = D_STATE(ifp);\n\tssize_t sbytes;\n\tsize_t bytes;\n\tuint8_t type;\n#ifdef AUTH\n\tconst uint8_t *auth;\n\tsize_t auth_len;\n#endif\n\n\t/* Safety */\n\t*bootp = NULL;\n\n\tif (state->leasefile[0] == '\\0') {\n\t\tlogdebugx(\"reading standard input\");\n\t\tsbytes = read(fileno(stdin), buf.buf, sizeof(buf.buf));\n\t} else {\n\t\tlogdebugx(\"%s: reading lease: %s\", ifp->name, state->leasefile);\n\t\tsbytes = dhcp_readfile(ifp->ctx, state->leasefile, buf.buf,\n\t\t    sizeof(buf.buf));\n\t}\n\tif (sbytes == -1) {\n\t\tif (errno != ENOENT)\n\t\t\tlogerr(\"%s: %s\", ifp->name, state->leasefile);\n\t\treturn 0;\n\t}\n\tbytes = (size_t)sbytes;\n\n\t/* Ensure the packet is at lease BOOTP sized\n\t * with a vendor area of 4 octets\n\t * (it should be more, and our read packet enforces this so this\n\t * code should not be needed, but of course people could\n\t * scribble whatever in the stored lease file. */\n\tif (bytes < DHCP_MIN_LEN) {\n\t\tlogerrx(\"%s: %s: truncated lease\", ifp->name, __func__);\n\t\treturn 0;\n\t}\n\n\tif (ifp->ctx->options & DHCPCD_DUMPLEASE)\n\t\tgoto out;\n\n\t/* We may have found a BOOTP server */\n\tif (get_option_uint8(ifp->ctx, &type, &buf.bootp, bytes,\n\t\tDHO_MESSAGETYPE) == -1)\n\t\ttype = 0;\n\n#ifdef AUTH\n\t/* Authenticate the message */\n\tauth = get_option(ifp->ctx, &buf.bootp, bytes, DHO_AUTHENTICATION,\n\t    &auth_len);\n\tif (auth) {\n\t\tif (dhcp_auth_validate(&state->auth, &ifp->options->auth,\n\t\t\t&buf.bootp, bytes, 4, type, auth, auth_len) == NULL) {\n\t\t\tlogerr(\"%s: authentication failed\", ifp->name);\n\t\t\treturn 0;\n\t\t}\n\t\tif (state->auth.token)\n\t\t\tlogdebugx(\"%s: validated using 0x%08\" PRIu32, ifp->name,\n\t\t\t    state->auth.token->secretid);\n\t\telse\n\t\t\tlogdebugx(\"%s: accepted reconfigure key\", ifp->name);\n\t} else if ((ifp->options->auth.options & DHCPCD_AUTH_SENDREQUIRE) ==\n\t    DHCPCD_AUTH_SENDREQUIRE) {\n\t\tlogerrx(\"%s: authentication now required\", ifp->name);\n\t\treturn 0;\n\t}\n#endif\n\nout:\n\t*bootp = malloc(bytes);\n\tif (*bootp == NULL) {\n\t\tlogerr(__func__);\n\t\treturn 0;\n\t}\n\tmemcpy(*bootp, buf.buf, bytes);\n\treturn bytes;\n}\n\nstatic const struct dhcp_opt *\ndhcp_getoverride(const struct if_options *ifo, unsigned int o)\n{\n\tsize_t i;\n\tconst struct dhcp_opt *opt;\n\n\tfor (i = 0, opt = ifo->dhcp_override; i < ifo->dhcp_override_len;\n\t    i++, opt++) {\n\t\tif (opt->option == o)\n\t\t\treturn opt;\n\t}\n\treturn NULL;\n}\n\nstatic const uint8_t *\ndhcp_getoption(struct dhcpcd_ctx *ctx, size_t *os, unsigned int *code,\n    size_t *len, const uint8_t *od, size_t ol, struct dhcp_opt **oopt)\n{\n\tsize_t i;\n\tstruct dhcp_opt *opt;\n\n\tif (od) {\n\t\tif (ol < 2) {\n\t\t\terrno = EINVAL;\n\t\t\treturn NULL;\n\t\t}\n\t\t*os = 2; /* code + len */\n\t\t*code = (unsigned int)*od++;\n\t\t*len = (size_t)*od++;\n\t\tif (*len > ol - *os) {\n\t\t\terrno = ERANGE;\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\t*oopt = NULL;\n\tfor (i = 0, opt = ctx->dhcp_opts; i < ctx->dhcp_opts_len; i++, opt++) {\n\t\tif (opt->option == *code) {\n\t\t\t*oopt = opt;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn od;\n}\n\nssize_t\ndhcp_env(FILE *fenv, const char *prefix, const struct interface *ifp,\n    const struct bootp *bootp, size_t bootp_len)\n{\n\tconst struct if_options *ifo;\n\tconst uint8_t *p;\n\tstruct in_addr addr;\n\tstruct in_addr net;\n\tstruct in_addr brd;\n\tstruct dhcp_opt *opt, *vo;\n\tsize_t i, pl;\n\tchar safe[(BOOTP_FILE_LEN * 4) + 1];\n\tuint8_t overl = 0;\n\tuint32_t en;\n\n\tifo = ifp->options;\n\tif (get_option_uint8(ifp->ctx, &overl, bootp, bootp_len,\n\t\tDHO_OPTSOVERLOADED) == -1)\n\t\toverl = 0;\n\n\tif (bootp->yiaddr || bootp->ciaddr) {\n\t\t/* Set some useful variables that we derive from the DHCP\n\t\t * message but are not necessarily in the options */\n\t\taddr.s_addr = bootp->yiaddr ? bootp->yiaddr : bootp->ciaddr;\n\t\tif (efprintf(fenv, \"%s_ip_address=%s\", prefix,\n\t\t\tinet_ntoa(addr)) == -1)\n\t\t\treturn -1;\n\t\tif (get_option_addr(ifp->ctx, &net, bootp, bootp_len,\n\t\t\tDHO_SUBNETMASK) == -1) {\n\t\t\tnet.s_addr = ipv4_getnetmask(addr.s_addr);\n\t\t\tif (efprintf(fenv, \"%s_subnet_mask=%s\", prefix,\n\t\t\t\tinet_ntoa(net)) == -1)\n\t\t\t\treturn -1;\n\t\t}\n\t\tif (efprintf(fenv, \"%s_subnet_cidr=%d\", prefix,\n\t\t\tinet_ntocidr(net)) == -1)\n\t\t\treturn -1;\n\t\tif (get_option_addr(ifp->ctx, &brd, bootp, bootp_len,\n\t\t\tDHO_BROADCAST) == -1) {\n\t\t\tbrd.s_addr = addr.s_addr | ~net.s_addr;\n\t\t\tif (efprintf(fenv, \"%s_broadcast_address=%s\", prefix,\n\t\t\t\tinet_ntoa(brd)) == -1)\n\t\t\t\treturn -1;\n\t\t}\n\t\taddr.s_addr = bootp->yiaddr & net.s_addr;\n\t\tif (efprintf(fenv, \"%s_network_number=%s\", prefix,\n\t\t\tinet_ntoa(addr)) == -1)\n\t\t\treturn -1;\n\t}\n\n\tif (*bootp->file && !(overl & 1)) {\n\t\tprint_string(safe, sizeof(safe), OT_STRING, bootp->file,\n\t\t    sizeof(bootp->file));\n\t\tif (efprintf(fenv, \"%s_filename=%s\", prefix, safe) == -1)\n\t\t\treturn -1;\n\t}\n\tif (*bootp->sname && !(overl & 2)) {\n\t\tprint_string(safe, sizeof(safe), OT_STRING | OT_DOMAIN,\n\t\t    bootp->sname, sizeof(bootp->sname));\n\t\tif (efprintf(fenv, \"%s_server_name=%s\", prefix, safe) == -1)\n\t\t\treturn -1;\n\t}\n\n\t/* Zero our indexes */\n\tfor (i = 0, opt = ifp->ctx->dhcp_opts; i < ifp->ctx->dhcp_opts_len;\n\t    i++, opt++)\n\t\tdhcp_zero_index(opt);\n\tfor (i = 0, opt = ifp->options->dhcp_override;\n\t    i < ifp->options->dhcp_override_len; i++, opt++)\n\t\tdhcp_zero_index(opt);\n\tfor (i = 0, opt = ifp->ctx->vivso; i < ifp->ctx->vivso_len; i++, opt++)\n\t\tdhcp_zero_index(opt);\n\n\tfor (i = 0, opt = ifp->ctx->dhcp_opts; i < ifp->ctx->dhcp_opts_len;\n\t    i++, opt++) {\n\t\tif (has_option_mask(ifo->nomask, opt->option))\n\t\t\tcontinue;\n\t\tif (dhcp_getoverride(ifo, opt->option))\n\t\t\tcontinue;\n\t\tp = get_option(ifp->ctx, bootp, bootp_len, opt->option, &pl);\n\t\tif (p == NULL)\n\t\t\tcontinue;\n\t\tdhcp_envoption(ifp->ctx, fenv, prefix, ifp->name, opt,\n\t\t    dhcp_getoption, p, pl);\n\n\t\tif (opt->option != DHO_VIVSO || pl <= (int)sizeof(uint32_t))\n\t\t\tcontinue;\n\t\tmemcpy(&en, p, sizeof(en));\n\t\ten = ntohl(en);\n\t\tvo = vivso_find(en, ifp);\n\t\tif (vo == NULL)\n\t\t\tcontinue;\n\t\t/* Skip over en + total size */\n\t\tp += sizeof(en) + 1;\n\t\tpl -= sizeof(en) + 1;\n\t\tdhcp_envoption(ifp->ctx, fenv, prefix, ifp->name, vo,\n\t\t    dhcp_getoption, p, pl);\n\t}\n\n\tfor (i = 0, opt = ifo->dhcp_override; i < ifo->dhcp_override_len;\n\t    i++, opt++) {\n\t\tif (has_option_mask(ifo->nomask, opt->option))\n\t\t\tcontinue;\n\t\tp = get_option(ifp->ctx, bootp, bootp_len, opt->option, &pl);\n\t\tif (p == NULL)\n\t\t\tcontinue;\n\t\tdhcp_envoption(ifp->ctx, fenv, prefix, ifp->name, opt,\n\t\t    dhcp_getoption, p, pl);\n\t}\n\n\treturn 1;\n}\n\nstatic void\nget_lease(struct interface *ifp, struct dhcp_lease *lease,\n    const struct bootp *bootp, size_t len)\n{\n\tstruct dhcpcd_ctx *ctx;\n\n\tassert(bootp != NULL);\n\n\tmemcpy(&lease->cookie, bootp->vend, sizeof(lease->cookie));\n\t/* BOOTP does not set yiaddr for replies when ciaddr is set. */\n\tlease->addr.s_addr = bootp->yiaddr ? bootp->yiaddr : bootp->ciaddr;\n\tctx = ifp->ctx;\n\tif (ifp->options->options & (DHCPCD_STATIC | DHCPCD_INFORM)) {\n\t\tif (ifp->options->req_addr.s_addr != INADDR_ANY) {\n\t\t\tlease->mask = ifp->options->req_mask;\n\t\t\tif (ifp->options->req_brd.s_addr != INADDR_ANY)\n\t\t\t\tlease->brd = ifp->options->req_brd;\n\t\t\telse\n\t\t\t\tlease->brd.s_addr = lease->addr.s_addr |\n\t\t\t\t    ~lease->mask.s_addr;\n\t\t} else {\n\t\t\tconst struct ipv4_addr *ia;\n\n\t\t\tia = ipv4_iffindaddr(ifp, &lease->addr, NULL);\n\t\t\tif (ia == NULL) {\n\t\t\t\tlease->mask.s_addr = ipv4_getnetmask(\n\t\t\t\t    lease->addr.s_addr);\n\t\t\t\tlease->brd.s_addr = lease->addr.s_addr |\n\t\t\t\t    ~lease->mask.s_addr;\n\t\t\t} else {\n\t\t\t\tlease->mask = ia->mask;\n\t\t\t\tlease->brd = ia->brd;\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif (get_option_addr(ctx, &lease->mask, bootp, len,\n\t\t\tDHO_SUBNETMASK) == -1)\n\t\t\tlease->mask.s_addr = ipv4_getnetmask(\n\t\t\t    lease->addr.s_addr);\n\t\tif (get_option_addr(ctx, &lease->brd, bootp, len,\n\t\t\tDHO_BROADCAST) == -1)\n\t\t\tlease->brd.s_addr = lease->addr.s_addr |\n\t\t\t    ~lease->mask.s_addr;\n\t}\n\tif (get_option_uint32(ctx, &lease->leasetime, bootp, len,\n\t\tDHO_LEASETIME) != 0)\n\t\tlease->leasetime = DHCP_INFINITE_LIFETIME;\n\tif (get_option_uint32(ctx, &lease->renewaltime, bootp, len,\n\t\tDHO_RENEWALTIME) != 0)\n\t\tlease->renewaltime = 0;\n\tif (get_option_uint32(ctx, &lease->rebindtime, bootp, len,\n\t\tDHO_REBINDTIME) != 0)\n\t\tlease->rebindtime = 0;\n\tif (get_option_addr(ctx, &lease->server, bootp, len, DHO_SERVERID) != 0)\n\t\tlease->server.s_addr = INADDR_ANY;\n}\n\nstatic const char *\nget_dhcp_op(uint8_t type)\n{\n\tconst struct dhcp_op *d;\n\n\tfor (d = dhcp_ops; d->name; d++)\n\t\tif (d->value == type)\n\t\t\treturn d->name;\n\treturn NULL;\n}\n\nstatic void\ndhcp_fallback(void *arg)\n{\n\tstruct interface *iface;\n\n\tiface = (struct interface *)arg;\n\tdhcpcd_selectprofile(iface, iface->options->fallback);\n\tdhcpcd_startinterface(iface);\n}\n\nstatic void\ndhcp_new_xid(struct interface *ifp)\n{\n\tstruct dhcp_state *state;\n\tconst struct interface *ifp1;\n\tconst struct dhcp_state *state1;\n\n\tstate = D_STATE(ifp);\n\tif (ifp->options->options & DHCPCD_XID_HWADDR &&\n\t    ifp->hwlen >= sizeof(state->xid))\n\t\t/* The lower bits are probably more unique on the network */\n\t\tmemcpy(&state->xid,\n\t\t    (ifp->hwaddr + ifp->hwlen) - sizeof(state->xid),\n\t\t    sizeof(state->xid));\n\telse {\n\tagain:\n\t\tstate->xid = arc4random();\n\t}\n\n\t/* Ensure it's unique */\n\tTAILQ_FOREACH(ifp1, ifp->ctx->ifaces, next) {\n\t\tif (ifp == ifp1)\n\t\t\tcontinue;\n\t\tif ((state1 = D_CSTATE(ifp1)) == NULL)\n\t\t\tcontinue;\n\t\tif (state1->xid == state->xid)\n\t\t\tbreak;\n\t}\n\tif (ifp1 != NULL) {\n\t\tif (ifp->options->options & DHCPCD_XID_HWADDR &&\n\t\t    ifp->hwlen >= sizeof(state->xid)) {\n\t\t\tlogerrx(\"%s: duplicate xid on %s\", ifp->name,\n\t\t\t    ifp1->name);\n\t\t\treturn;\n\t\t}\n\t\tgoto again;\n\t}\n\n\t/* We can't do this when sharing leases across interfaes */\n#if 0\n\t/* As the XID changes, re-apply the filter. */\n\tif (state->bpf_fd != -1) {\n\t\tif (bpf_bootp(ifp, state->bpf_fd) == -1)\n\t\t\tlogerr(__func__); /* try to continue */\n\t}\n#endif\n}\n\nstatic void\ndhcp_closebpf(struct interface *ifp)\n{\n\tstruct dhcpcd_ctx *ctx = ifp->ctx;\n\tstruct dhcp_state *state = D_STATE(ifp);\n\n#ifdef PRIVSEP\n\tif (IN_PRIVSEP_SE(ctx))\n\t\tps_bpf_closebootp(ifp);\n#endif\n\n\tif (state->bpf != NULL) {\n\t\teloop_event_delete(ctx->eloop, state->bpf->bpf_fd);\n\t\tbpf_close(state->bpf);\n\t\tstate->bpf = NULL;\n\t}\n}\n\nstatic void\ndhcp_closeinet(struct interface *ifp)\n{\n\tstruct dhcpcd_ctx *ctx = ifp->ctx;\n\tstruct dhcp_state *state = D_STATE(ifp);\n\n#ifdef PRIVSEP\n\tif (IN_PRIVSEP_SE(ctx)) {\n\t\tif (state->addr != NULL)\n\t\t\tps_inet_closebootp(state->addr);\n\t}\n#endif\n\n\tif (state->udp_rfd != -1) {\n\t\teloop_event_delete(ctx->eloop, state->udp_rfd);\n\t\tclose(state->udp_rfd);\n\t\tstate->udp_rfd = -1;\n\t}\n}\n\nvoid\ndhcp_close(struct interface *ifp)\n{\n\tstruct dhcp_state *state = D_STATE(ifp);\n\n\tif (state == NULL)\n\t\treturn;\n\n\tdhcp_closebpf(ifp);\n\tdhcp_closeinet(ifp);\n\n\tstate->interval = 0;\n}\n\nint\ndhcp_openudp(struct in_addr *ia)\n{\n\tint s;\n\tstruct sockaddr_in sin;\n\tint n;\n\n\tif ((s = xsocket(PF_INET, SOCK_DGRAM | SOCK_CXNB, IPPROTO_UDP)) == -1)\n\t\treturn -1;\n\n\tn = 1;\n\tif (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1)\n\t\tgoto errexit;\n#ifdef IP_RECVIF\n\tif (setsockopt(s, IPPROTO_IP, IP_RECVIF, &n, sizeof(n)) == -1)\n\t\tgoto errexit;\n#else\n\tif (setsockopt(s, IPPROTO_IP, IP_RECVPKTINFO, &n, sizeof(n)) == -1)\n\t\tgoto errexit;\n#endif\n#ifdef SO_RERROR\n\tif (setsockopt(s, SOL_SOCKET, SO_RERROR, &n, sizeof(n)) == -1)\n\t\tgoto errexit;\n#endif\n\n\tmemset(&sin, 0, sizeof(sin));\n\tsin.sin_family = AF_INET;\n\tsin.sin_port = htons(BOOTPC);\n\tif (ia != NULL)\n\t\tsin.sin_addr = *ia;\n\tif (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1)\n\t\tgoto errexit;\n\n\treturn s;\n\nerrexit:\n\tclose(s);\n\treturn -1;\n}\n\nstatic uint16_t\nin_cksum(const void *data, size_t len, uint32_t *isum)\n{\n\tconst uint16_t *word = data;\n\tuint32_t sum = isum != NULL ? *isum : 0;\n\n\tfor (; len > 1; len -= sizeof(*word))\n\t\tsum += *word++;\n\n\tif (len == 1)\n\t\tsum += htons((uint16_t)(*(const uint8_t *)word << 8));\n\n\tif (isum != NULL)\n\t\t*isum = sum;\n\n\tsum = (sum >> 16) + (sum & 0xffff);\n\tsum += (sum >> 16);\n\n\treturn (uint16_t)~sum;\n}\n\nstatic struct bootp_pkt *\ndhcp_makeudppacket(size_t *sz, const uint8_t *data, size_t length,\n    struct in_addr source, struct in_addr dest)\n{\n\tstruct bootp_pkt *udpp;\n\tstruct ip *ip;\n\tstruct udphdr *udp;\n\n\tif ((udpp = calloc(1, sizeof(*ip) + sizeof(*udp) + length)) == NULL)\n\t\treturn NULL;\n\tip = &udpp->ip;\n\tudp = &udpp->udp;\n\n\t/* OK, this is important :)\n\t * We copy the data to our packet and then create a small part of the\n\t * ip structure and an invalid ip_len (basically udp length).\n\t * We then fill the udp structure and put the checksum\n\t * of the whole packet into the udp checksum.\n\t * Finally we complete the ip structure and ip checksum.\n\t * If we don't do the ordering like so then the udp checksum will be\n\t * broken, so find another way of doing it! */\n\n\tmemcpy(&udpp->bootp, data, length);\n\n\tip->ip_p = IPPROTO_UDP;\n\tip->ip_src.s_addr = source.s_addr;\n\tif (dest.s_addr == INADDR_ANY)\n\t\tip->ip_dst.s_addr = INADDR_BROADCAST;\n\telse\n\t\tip->ip_dst.s_addr = dest.s_addr;\n\n\tudp->uh_sport = htons(BOOTPC);\n\tudp->uh_dport = htons(BOOTPS);\n\tudp->uh_ulen = htons((uint16_t)(sizeof(*udp) + length));\n\tip->ip_len = udp->uh_ulen;\n\tudp->uh_sum = in_cksum(udpp, sizeof(*ip) + sizeof(*udp) + length, NULL);\n\n\tip->ip_v = IPVERSION;\n\tip->ip_hl = sizeof(*ip) >> 2;\n\tip->ip_id = (uint16_t)arc4random_uniform(UINT16_MAX);\n\tip->ip_ttl = IPDEFTTL;\n\tip->ip_len = htons((uint16_t)(sizeof(*ip) + sizeof(*udp) + length));\n\tip->ip_sum = in_cksum(ip, sizeof(*ip), NULL);\n\tif (ip->ip_sum == 0)\n\t\tip->ip_sum = 0xffff; /* RFC 768 */\n\n\t*sz = sizeof(*ip) + sizeof(*udp) + length;\n\treturn udpp;\n}\n\nstatic ssize_t\ndhcp_sendudp(struct interface *ifp, struct in_addr *to, void *data, size_t len)\n{\n\tstruct sockaddr_in sin = {\n\t\t.sin_family = AF_INET,\n\t\t.sin_addr = *to,\n\t\t.sin_port = htons(BOOTPS),\n#ifdef HAVE_SA_LEN\n\t\t.sin_len = sizeof(sin),\n#endif\n\t};\n\tstruct udphdr udp = {\n\t\t.uh_sport = htons(BOOTPC),\n\t\t.uh_dport = htons(BOOTPS),\n\t\t.uh_ulen = htons((uint16_t)(sizeof(udp) + len)),\n\t};\n\tstruct iovec iov[] = {\n\t\t{\n\t\t    .iov_base = &udp,\n\t\t    .iov_len = sizeof(udp),\n\t\t},\n\t\t{\n\t\t    .iov_base = data,\n\t\t    .iov_len = len,\n\t\t},\n\t};\n\tstruct msghdr msg = {\n\t\t.msg_name = (void *)&sin,\n\t\t.msg_namelen = sizeof(sin),\n\t\t.msg_iov = iov,\n\t\t.msg_iovlen = __arraycount(iov),\n\t};\n\tstruct dhcpcd_ctx *ctx = ifp->ctx;\n\n#ifdef PRIVSEP\n\tif (ctx->options & DHCPCD_PRIVSEP)\n\t\treturn ps_inet_sendbootp(ifp, &msg);\n#endif\n\treturn sendmsg(ctx->udp_wfd, &msg, 0);\n}\n\nstatic void\nsend_message(struct interface *ifp, uint8_t type, void (*callback)(void *))\n{\n\tstruct dhcp_state *state = D_STATE(ifp);\n\tstruct if_options *ifo = ifp->options;\n\tstruct bootp *bootp;\n\tstruct bootp_pkt *udp;\n\tsize_t len, ulen;\n\tssize_t r;\n\tstruct in_addr from, to;\n\tunsigned int RT;\n\n\tif (callback == NULL) {\n\t\t/* No carrier? Don't bother sending the packet. */\n\t\tif (!if_is_link_up(ifp))\n\t\t\treturn;\n\t\tlogdebugx(\"%s: sending %s with xid 0x%x\", ifp->name,\n\t\t    ifo->options & DHCPCD_BOOTP ? \"BOOTP\" : get_dhcp_op(type),\n\t\t    state->xid);\n\t\tRT = 0; /* bogus gcc warning */\n\t} else {\n\t\tif (state->interval == 0)\n\t\t\tstate->interval = 4;\n\t\telse {\n\t\t\tstate->interval *= 2;\n\t\t\tif (state->interval > 64)\n\t\t\t\tstate->interval = 64;\n\t\t}\n\t\tRT = (state->interval * MSEC_PER_SEC) +\n\t\t    (arc4random_uniform(MSEC_PER_SEC * 2) - MSEC_PER_SEC);\n\t\t/* No carrier? Don't bother sending the packet.\n\t\t * However, we do need to advance the timeout. */\n\t\tif (!if_is_link_up(ifp))\n\t\t\tgoto fail;\n\t\tlogdebugx(\"%s: sending %s (xid 0x%x), next in %0.1f seconds\",\n\t\t    ifp->name,\n\t\t    ifo->options & DHCPCD_BOOTP ? \"BOOTP\" : get_dhcp_op(type),\n\t\t    state->xid, (float)RT / MSEC_PER_SEC);\n\t}\n\n\tr = make_message(&bootp, ifp, type);\n\tif (r == -1)\n\t\tgoto fail;\n\tlen = (size_t)r;\n\n\tif (!(state->added & (STATE_FAKE | STATE_EXPIRED)) &&\n\t    state->addr != NULL &&\n\t    ipv4_iffindaddr(ifp, &state->lease.addr, NULL) != NULL)\n\t\tfrom.s_addr = state->lease.addr.s_addr;\n\telse\n\t\tfrom.s_addr = INADDR_ANY;\n\tif (from.s_addr != INADDR_ANY &&\n\t    state->lease.server.s_addr != INADDR_ANY)\n\t\tto.s_addr = state->lease.server.s_addr;\n\telse\n\t\tto.s_addr = INADDR_BROADCAST;\n\n\t/*\n\t * If not listening on the unspecified address we can\n\t * only receive broadcast messages via BPF.\n\t * Sockets bound to an address cannot receive broadcast messages\n\t * even if they are setup to send them.\n\t * Broadcasting from UDP is only an optimisation for rebinding\n\t * and on BSD, at least, is reliant on the subnet route being\n\t * correctly configured to receive the unicast reply.\n\t * As such, we always broadcast and receive the reply to it via BPF.\n\t * This also guarantees we have a DHCP server attached to the\n\t * interface we want to configure because we can't dictate the\n\t * interface via IP_PKTINFO unlike for IPv6.\n\t */\n\tif (to.s_addr != INADDR_BROADCAST) {\n\t\tif (dhcp_sendudp(ifp, &to, bootp, len) != -1)\n\t\t\tgoto out;\n\t\tlogerr(\"%s: dhcp_sendudp\", ifp->name);\n\t}\n\n\tif (dhcp_openbpf(ifp) == -1)\n\t\tgoto out;\n\n\tudp = dhcp_makeudppacket(&ulen, (uint8_t *)bootp, len, from, to);\n\tif (udp == NULL) {\n\t\tlogerr(\"%s: dhcp_makeudppacket\", ifp->name);\n\t\tr = 0;\n#ifdef PRIVSEP\n\t} else if (ifp->ctx->options & DHCPCD_PRIVSEP) {\n\t\tr = ps_bpf_sendbootp(ifp, udp, ulen);\n\t\tfree(udp);\n#endif\n\t} else {\n\t\tr = bpf_send(state->bpf, ETHERTYPE_IP, udp, ulen);\n\t\tfree(udp);\n\t}\n\t/* If we failed to send a raw packet this normally means\n\t * we don't have the ability to work beneath the IP layer\n\t * for this interface.\n\t * As such we remove it from consideration without actually\n\t * stopping the interface. */\n\tif (r == -1) {\n\t\tlogerr(\"%s: bpf_send\", ifp->name);\n\t\tswitch (errno) {\n\t\tcase ENETDOWN:\n\t\tcase ENETRESET:\n\t\tcase ENETUNREACH:\n\t\tcase ENOBUFS:\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tif (!(ifp->ctx->options & DHCPCD_TEST))\n\t\t\t\tdhcp_drop(ifp, \"FAIL\");\n\t\t\teloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);\n\t\t\tcallback = NULL;\n\t\t}\n\t}\n\nout:\n\tfree(bootp);\n\nfail:\n\t/* Even if we fail to send a packet we should continue as we are\n\t * as our failure timeouts will change out codepath when needed. */\n\tif (callback != NULL)\n\t\teloop_timeout_add_msec(ifp->ctx->eloop, RT, callback, ifp);\n}\n\nstatic void\nsend_inform(void *arg)\n{\n\tsend_message((struct interface *)arg, DHCP_INFORM, send_inform);\n}\n\nstatic void\nsend_discover(void *arg)\n{\n\tsend_message((struct interface *)arg, DHCP_DISCOVER, send_discover);\n}\n\nstatic void\nsend_request(void *arg)\n{\n\tsend_message((struct interface *)arg, DHCP_REQUEST, send_request);\n}\n\nstatic void\nsend_renew(void *arg)\n{\n\tsend_message((struct interface *)arg, DHCP_REQUEST, send_renew);\n}\n\nstatic void\nsend_rebind(void *arg)\n{\n\tsend_message((struct interface *)arg, DHCP_REQUEST, send_rebind);\n}\n\nvoid\ndhcp_discover(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tstruct dhcp_state *state = D_STATE(ifp);\n\tstruct if_options *ifo = ifp->options;\n\n\tstate->state = DHS_DISCOVER;\n\tdhcp_new_xid(ifp);\n\teloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);\n\tif (!(state->added & STATE_EXPIRED)) {\n\t\tif (ifo->fallback && ifo->fallback_time)\n\t\t\teloop_timeout_add_sec(ifp->ctx->eloop,\n\t\t\t    ifo->fallback_time, dhcp_fallback, ifp);\n#ifdef IPV4LL\n\t\telse if (ifo->options & DHCPCD_IPV4LL)\n\t\t\teloop_timeout_add_sec(ifp->ctx->eloop, ifo->ipv4ll_time,\n\t\t\t    ipv4ll_start, ifp);\n#endif\n\t}\n\tif (ifo->options & DHCPCD_REQUEST)\n\t\tloginfox(\"%s: soliciting a DHCP lease (requesting %s)\",\n\t\t    ifp->name, inet_ntoa(ifo->req_addr));\n\telse\n\t\tloginfox(\"%s: soliciting a %s lease\", ifp->name,\n\t\t    ifo->options & DHCPCD_BOOTP ? \"BOOTP\" : \"DHCP\");\n\tsend_discover(ifp);\n}\n\nstatic void\ndhcp_requestfailed(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tstruct dhcp_state *state = D_STATE(ifp);\n\n\tlogwarnx(\"%s: failed to request the lease\", ifp->name);\n\tfree(state->offer);\n\tstate->offer = NULL;\n\tstate->offer_len = 0;\n\tstate->interval = 0;\n\tdhcp_discover(ifp);\n}\n\nstatic void\ndhcp_request(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tstruct dhcp_state *state = D_STATE(ifp);\n\tstruct if_options *ifo = ifp->options;\n\n\tstate->state = DHS_REQUEST;\n\t// Handle the server being silent to our request.\n\tif (ifo->request_time != 0)\n\t\teloop_timeout_add_sec(ifp->ctx->eloop, ifo->request_time,\n\t\t    dhcp_requestfailed, ifp);\n\tsend_request(ifp);\n}\n\nstatic void\ndhcp_expire(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tstruct dhcp_state *state = D_STATE(ifp);\n\n\tif (ifp->options->options & DHCPCD_LASTLEASE_EXTEND) {\n\t\tlogwarnx(\"%s: DHCP lease expired, extending lease\", ifp->name);\n\t\tstate->added |= STATE_EXPIRED;\n\t} else {\n\t\tlogerrx(\"%s: DHCP lease expired\", ifp->name);\n\t\tdhcp_drop(ifp, \"EXPIRE\");\n\t\tdhcp_unlink(ifp->ctx, state->leasefile);\n\t}\n\tstate->interval = 0;\n\tdhcp_discover(ifp);\n}\n\n#if defined(ARP) || defined(IN_IFF_DUPLICATED)\nstatic void\ndhcp_decline(struct interface *ifp)\n{\n\tstruct dhcp_state *state = D_STATE(ifp);\n\n\t// Set the expired state so we send over BPF as this could be\n\t// an address defence failure.\n\tstate->added |= STATE_EXPIRED;\n\tsend_message(ifp, DHCP_DECLINE, NULL);\n}\n#endif\n\nstatic void\ndhcp_startrenew(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tstruct dhcp_state *state;\n\tstruct dhcp_lease *lease;\n\n\tif ((state = D_STATE(ifp)) == NULL)\n\t\treturn;\n\n\t/* Only renew in the bound or renew states */\n\tif (state->state != DHS_BOUND && state->state != DHS_RENEW)\n\t\treturn;\n\n\t/* Remove the timeout as the renew may have been forced. */\n\teloop_timeout_delete(ifp->ctx->eloop, dhcp_startrenew, ifp);\n\n\tlease = &state->lease;\n\tlogdebugx(\"%s: renewing lease of %s\", ifp->name,\n\t    inet_ntoa(lease->addr));\n\tstate->state = DHS_RENEW;\n\tdhcp_new_xid(ifp);\n\tstate->interval = 0;\n\tsend_renew(ifp);\n}\n\nvoid\ndhcp_renew(struct interface *ifp)\n{\n\tdhcp_startrenew(ifp);\n}\n\nstatic void\ndhcp_rebind(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tstruct dhcp_state *state = D_STATE(ifp);\n\tstruct dhcp_lease *lease = &state->lease;\n\n\tlogwarnx(\"%s: failed to renew DHCP, rebinding\", ifp->name);\n\tlogdebugx(\"%s: expire in %\" PRIu32 \" seconds\", ifp->name,\n\t    lease->leasetime - lease->rebindtime);\n\tstate->state = DHS_REBIND;\n\teloop_timeout_delete(ifp->ctx->eloop, send_renew, ifp);\n\tstate->lease.server.s_addr = INADDR_ANY;\n\tstate->interval = 0;\n\tifp->options->options &= ~(\n\t    DHCPCD_CSR_WARNED | DHCPCD_ROUTER_HOST_ROUTE_WARNED);\n\tsend_rebind(ifp);\n}\n\n#if defined(ARP) || defined(IN_IFF_DUPLICATED)\nstatic void\ndhcp_finish_dad(struct interface *ifp, struct in_addr *ia)\n{\n\tstruct dhcp_state *state = D_STATE(ifp);\n\n\tif (state->state == DHS_BOUND)\n\t\treturn;\n\tif (state->offer == NULL || state->offer->yiaddr != ia->s_addr)\n\t\treturn;\n\n\tlogdebugx(\"%s: DAD completed for %s\", ifp->name, inet_ntoa(*ia));\n\tif (!(ifp->options->options & DHCPCD_INFORM))\n\t\tdhcp_bind(ifp);\n#ifndef IN_IFF_DUPLICATED\n\telse {\n\t\tstruct bootp *bootp;\n\t\tsize_t len;\n\n\t\tbootp = state->new;\n\t\tlen = state->new_len;\n\t\tstate->new = state->offer;\n\t\tstate->new_len = state->offer_len;\n\t\tget_lease(ifp, &state->lease, state->new, state->new_len);\n\t\tipv4_applyaddr(ifp);\n\t\tstate->new = bootp;\n\t\tstate->new_len = len;\n\t}\n#endif\n\n#ifdef IPV4LL\n\t/* Stop IPv4LL now we have a working DHCP address */\n\tif ((!IN_LINKLOCAL(ntohl(ia->s_addr))) &&\n\t    (ifp->options->options & DHCPCD_IPV4LL))\n\t\tipv4ll_drop(ifp);\n#endif\n\n\tif (ifp->options->options & DHCPCD_INFORM)\n\t\tdhcp_inform(ifp);\n}\n\nstatic bool\ndhcp_addr_duplicated(struct interface *ifp, struct in_addr *ia)\n{\n\tstruct dhcp_state *state = D_STATE(ifp);\n\tunsigned long long opts = ifp->options->options;\n\tstruct dhcpcd_ctx *ctx = ifp->ctx;\n\tbool deleted = false;\n#ifdef IN_IFF_DUPLICATED\n\tstruct ipv4_addr *iap;\n#endif\n\n\tif ((state->offer == NULL || state->offer->yiaddr != ia->s_addr) &&\n\t    !IN_ARE_ADDR_EQUAL(ia, &state->lease.addr))\n\t\treturn deleted;\n\n\t/* RFC 2131 3.1.5, Client-server interaction */\n\tlogerrx(\"%s: DAD detected %s\", ifp->name, inet_ntoa(*ia));\n\tdhcp_unlink(ifp->ctx, state->leasefile);\n\tif (!(opts & DHCPCD_STATIC) && !state->lease.frominfo)\n\t\tdhcp_decline(ifp);\n#ifdef IN_IFF_DUPLICATED\n\tif ((iap = ipv4_iffindaddr(ifp, ia, NULL)) != NULL) {\n\t\tipv4_deladdr(iap, 0);\n\t\tdeleted = true;\n\t}\n#endif\n\teloop_timeout_delete(ctx->eloop, NULL, ifp);\n\tif (opts & (DHCPCD_STATIC | DHCPCD_INFORM)) {\n\t\tstate->reason = \"EXPIRE\";\n\t\tscript_runreason(ifp, state->reason);\n#define NOT_ONLY_SELF (DHCPCD_MANAGER | DHCPCD_IPV6RS | DHCPCD_DHCP6)\n\t\tif (!(ctx->options & NOT_ONLY_SELF))\n\t\t\teloop_exit(ifp->ctx->eloop, EXIT_FAILURE);\n\t\treturn deleted;\n\t}\n\teloop_timeout_add_sec(ifp->ctx->eloop, DHCP_RAND_MAX, dhcp_discover,\n\t    ifp);\n\treturn deleted;\n}\n#endif\n\n#ifdef ARP\n#ifdef KERNEL_RFC5227\n#ifdef ARPING\nstatic void\ndhcp_arp_announced(struct arp_state *state)\n{\n\tarp_free(state);\n}\n#endif\n#else\nstatic void\ndhcp_arp_defend_failed(struct arp_state *astate)\n{\n\tstruct interface *ifp = astate->iface;\n\tstruct dhcp_state *state = D_STATE(ifp);\n\tunsigned int delay;\n\n\tif (!(ifp->options->options & (DHCPCD_INFORM | DHCPCD_STATIC)))\n\t\tdhcp_decline(ifp);\n\tdhcp_drop(ifp, \"EXPIRED\");\n\tdhcp_unlink(ifp->ctx, state->leasefile);\n\n\t// Delay restarting to give time for the BPF ARP process to exit\n\t// as we may spawn a new one with a different filter fairly quickly\n\tdelay = MSEC_PER_SEC +\n\t    (arc4random_uniform(MSEC_PER_SEC * 2) - MSEC_PER_SEC);\n\teloop_timeout_add_msec(ifp->ctx->eloop, delay, dhcp_start1, ifp);\n}\n#endif\n\n#if !defined(KERNEL_RFC5227) || defined(ARPING)\nstatic void dhcp_arp_not_found(struct arp_state *);\n\nstatic struct arp_state *\ndhcp_arp_new(struct interface *ifp, struct in_addr *addr)\n{\n\tstruct arp_state *astate;\n\n\tastate = arp_new(ifp, addr);\n\tif (astate == NULL)\n\t\treturn NULL;\n\n\tastate->found_cb = dhcp_arp_found;\n\tastate->not_found_cb = dhcp_arp_not_found;\n#ifdef KERNEL_RFC5227\n\tastate->announced_cb = dhcp_arp_announced;\n#else\n\tastate->announced_cb = NULL;\n\tastate->defend_failed_cb = dhcp_arp_defend_failed;\n#endif\n\treturn astate;\n}\n#endif\n\n#ifdef ARPING\nstatic int\ndhcp_arping(struct interface *ifp)\n{\n\tstruct dhcp_state *state;\n\tstruct if_options *ifo;\n\tstruct arp_state *astate;\n\tstruct in_addr addr;\n\n\tstate = D_STATE(ifp);\n\tifo = ifp->options;\n\n\tif (ifo->arping_len == 0 || state->arping_index > ifo->arping_len)\n\t\treturn 0;\n\n\tif (state->arping_index + 1 == ifo->arping_len) {\n\t\tstate->arping_index++;\n\t\tdhcpcd_startinterface(ifp);\n\t\treturn 1;\n\t}\n\n\taddr.s_addr = ifo->arping[++state->arping_index];\n\tastate = dhcp_arp_new(ifp, &addr);\n\tif (astate == NULL) {\n\t\tlogerr(__func__);\n\t\treturn -1;\n\t}\n\tarp_probe(astate);\n\treturn 1;\n}\n#endif\n\n#if !defined(KERNEL_RFC5227) || defined(ARPING)\nstatic void\ndhcp_arp_not_found(struct arp_state *astate)\n{\n\tstruct interface *ifp;\n\n\tifp = astate->iface;\n#ifdef ARPING\n\tif (dhcp_arping(ifp) == 1) {\n\t\tarp_free(astate);\n\t\treturn;\n\t}\n#endif\n\n\tdhcp_finish_dad(ifp, &astate->addr);\n}\n\nstatic void\ndhcp_arp_found(struct arp_state *astate, const struct arp_msg *amsg)\n{\n\tstruct in_addr addr;\n\tstruct interface *ifp = astate->iface;\n#ifdef ARPING\n\tstruct dhcp_state *state;\n\tstruct if_options *ifo;\n\n\tstate = D_STATE(ifp);\n\n\tifo = ifp->options;\n\tif (state->arping_index != -1 &&\n\t    state->arping_index < ifo->arping_len && amsg &&\n\t    amsg->sip.s_addr == ifo->arping[state->arping_index]) {\n\t\tchar buf[HWADDR_LEN * 3];\n\n\t\thwaddr_ntoa(amsg->sha, ifp->hwlen, buf, sizeof(buf));\n\t\tif (dhcpcd_selectprofile(ifp, buf) == -1 &&\n\t\t    dhcpcd_selectprofile(ifp, inet_ntoa(amsg->sip)) == -1) {\n\t\t\t/* We didn't find a profile for this\n\t\t\t * address or hwaddr, so move to the next\n\t\t\t * arping profile */\n\t\t\tdhcp_arp_not_found(astate);\n\t\t\treturn;\n\t\t}\n\t\tarp_free(astate);\n\t\teloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);\n\t\tdhcpcd_startinterface(ifp);\n\t\treturn;\n\t}\n#else\n\tUNUSED(amsg);\n#endif\n\n\taddr = astate->addr;\n\tarp_free(astate);\n\tdhcp_addr_duplicated(ifp, &addr);\n}\n#endif\n\n#endif /* ARP */\n\nstatic void\ndhcp_bound(struct interface *ifp, uint8_t old_state)\n{\n\tstruct dhcpcd_ctx *ctx = ifp->ctx;\n\tstruct dhcp_state *state = D_STATE(ifp);\n\n\t/* Close the BPF filter as we can now receive DHCP messages\n\t * on a UDP socket. */\n\tdhcp_closebpf(ifp);\n\n\t/* If not in manager mode, open an address specific socket. */\n\tif (ctx->options & DHCPCD_MANAGER ||\n\t    ifp->options->options & DHCPCD_STATIC ||\n\t    (state->old != NULL &&\n\t\tstate->old->yiaddr ==\n\t\t    state->new->yiaddr &&old_state &STATE_ADDED &&\n\t\t!(old_state & STATE_FAKE)))\n\t\treturn;\n\n\tdhcp_closeinet(ifp);\n#ifdef PRIVSEP\n\tif (IN_PRIVSEP_SE(ctx)) {\n\t\tif (ps_inet_openbootp(state->addr) == -1)\n\t\t\tlogerr(__func__);\n\t\treturn;\n\t}\n#endif\n\n\tstate->udp_rfd = dhcp_openudp(&state->addr->addr);\n\tif (state->udp_rfd == -1) {\n\t\tlogerr(__func__);\n\t\t/* We still need to work, so re-open BPF. */\n\t\tdhcp_openbpf(ifp);\n\t\treturn;\n\t}\n\n\tif (eloop_event_add(ctx->eloop, state->udp_rfd, ELE_READ,\n\t\tdhcp_handleifudp, ifp) == -1)\n\t\tlogerr(\"%s: eloop_event_add\", __func__);\n}\n\nvoid\ndhcp_bind(struct interface *ifp)\n{\n\tstruct dhcpcd_ctx *ctx = ifp->ctx;\n\tstruct dhcp_state *state = D_STATE(ifp);\n\tstruct if_options *ifo = ifp->options;\n\tstruct dhcp_lease *lease = &state->lease;\n\tuint8_t old_state;\n\n\tstate->reason = NULL;\n\t/* If we don't have an offer, we are re-binding a lease on preference,\n\t * normally when two interfaces have a lease matching IP addresses. */\n\tif (state->offer) {\n\t\tfree(state->old);\n\t\tstate->old = state->new;\n\t\tstate->old_len = state->new_len;\n\t\tstate->new = state->offer;\n\t\tstate->new_len = state->offer_len;\n\t\tstate->offer = NULL;\n\t\tstate->offer_len = 0;\n\t}\n\tget_lease(ifp, lease, state->new, state->new_len);\n\tif (ifo->options & DHCPCD_STATIC) {\n\t\tloginfox(\"%s: using static address %s/%d\", ifp->name,\n\t\t    inet_ntoa(lease->addr), inet_ntocidr(lease->mask));\n\t\tlease->leasetime = DHCP_INFINITE_LIFETIME;\n\t\tstate->reason = \"STATIC\";\n\t} else if (ifo->options & DHCPCD_INFORM) {\n\t\tloginfox(\"%s: received approval for %s\", ifp->name,\n\t\t    inet_ntoa(lease->addr));\n\t\tlease->leasetime = DHCP_INFINITE_LIFETIME;\n\t\tstate->reason = \"INFORM\";\n\t} else {\n\t\tif (lease->frominfo)\n\t\t\tstate->reason = \"TIMEOUT\";\n\t\tif (lease->leasetime == DHCP_INFINITE_LIFETIME) {\n\t\t\tlease->renewaltime = lease->rebindtime =\n\t\t\t    lease->leasetime;\n\t\t\tloginfox(\"%s: leased %s for infinity\", ifp->name,\n\t\t\t    inet_ntoa(lease->addr));\n\t\t} else {\n\t\t\tif (lease->leasetime < DHCP_MIN_LEASE) {\n\t\t\t\tlogwarnx(\"%s: minimum lease is %d seconds\",\n\t\t\t\t    ifp->name, DHCP_MIN_LEASE);\n\t\t\t\tlease->leasetime = DHCP_MIN_LEASE;\n\t\t\t}\n\t\t\tif (lease->rebindtime == 0)\n\t\t\t\tlease->rebindtime =\n\t\t\t\t    (uint32_t)(lease->leasetime * T2);\n\t\t\telse if (lease->rebindtime >= lease->leasetime) {\n\t\t\t\tlease->rebindtime =\n\t\t\t\t    (uint32_t)(lease->leasetime * T2);\n\t\t\t\tlogwarnx(\"%s: rebind time greater than lease \"\n\t\t\t\t\t \"time, forcing to %\" PRIu32 \" seconds\",\n\t\t\t\t    ifp->name, lease->rebindtime);\n\t\t\t}\n\t\t\tif (lease->renewaltime == 0)\n\t\t\t\tlease->renewaltime =\n\t\t\t\t    (uint32_t)(lease->leasetime * T1);\n\t\t\telse if (lease->renewaltime > lease->rebindtime) {\n\t\t\t\tlease->renewaltime =\n\t\t\t\t    (uint32_t)(lease->leasetime * T1);\n\t\t\t\tlogwarnx(\"%s: renewal time greater than \"\n\t\t\t\t\t \"rebind time, forcing to %\" PRIu32\n\t\t\t\t\t \" seconds\",\n\t\t\t\t    ifp->name, lease->renewaltime);\n\t\t\t}\n\t\t\tif (state->state == DHS_RENEW && state->addr &&\n\t\t\t    lease->addr.s_addr == state->addr->addr.s_addr &&\n\t\t\t    !(state->added & STATE_FAKE))\n\t\t\t\tlogdebugx(\"%s: leased %s for %\" PRIu32\n\t\t\t\t\t  \" seconds\",\n\t\t\t\t    ifp->name, inet_ntoa(lease->addr),\n\t\t\t\t    lease->leasetime);\n\t\t\telse\n\t\t\t\tloginfox(\"%s: leased %s for %\" PRIu32\n\t\t\t\t\t \" seconds\",\n\t\t\t\t    ifp->name, inet_ntoa(lease->addr),\n\t\t\t\t    lease->leasetime);\n\t\t}\n\t}\n\tif (ctx->options & DHCPCD_TEST) {\n\t\tstate->reason = \"TEST\";\n\t\tscript_runreason(ifp, state->reason);\n\t\teloop_exit(ctx->eloop, EXIT_SUCCESS);\n\t\treturn;\n\t}\n\tif (state->reason == NULL) {\n\t\tif (state->old &&\n\t\t    !(state->added & (STATE_FAKE | STATE_EXPIRED))) {\n\t\t\tif (state->old->yiaddr == state->new->yiaddr &&\n\t\t\t    lease->server.s_addr && state->state != DHS_REBIND)\n\t\t\t\tstate->reason = \"RENEW\";\n\t\t\telse\n\t\t\t\tstate->reason = \"REBIND\";\n\t\t} else if (state->state == DHS_REBOOT)\n\t\t\tstate->reason = \"REBOOT\";\n\t\telse\n\t\t\tstate->reason = \"BOUND\";\n\t}\n\tif (lease->leasetime == DHCP_INFINITE_LIFETIME)\n\t\tlease->renewaltime = lease->rebindtime = lease->leasetime;\n\telse {\n\t\teloop_timeout_add_sec(ctx->eloop, lease->renewaltime,\n\t\t    dhcp_startrenew, ifp);\n\t\teloop_timeout_add_sec(ctx->eloop, lease->rebindtime,\n\t\t    dhcp_rebind, ifp);\n\t\teloop_timeout_add_sec(ctx->eloop, lease->leasetime, dhcp_expire,\n\t\t    ifp);\n\t\tlogdebugx(\"%s: renew in %\" PRIu32 \" seconds, rebind in %\" PRIu32\n\t\t\t  \" seconds\",\n\t\t    ifp->name, lease->renewaltime, lease->rebindtime);\n\t}\n\tstate->state = DHS_BOUND;\n\tif (!state->lease.frominfo &&\n\t    !(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC))) {\n\t\tlogdebugx(\"%s: writing lease: %s\", ifp->name, state->leasefile);\n\t\tif (dhcp_writefile(ifp->ctx, state->leasefile, 0640, state->new,\n\t\t\tstate->new_len) == -1)\n\t\t\tlogerr(\"dhcp_writefile: %s\", state->leasefile);\n\t}\n\n\told_state = state->added;\n\n\tif (ifo->options & DHCPCD_CONFIGURE) {\n\t\t/* Add the address */\n\t\tif (ipv4_applyaddr(ifp) == NULL) {\n\t\t\t/* There was an error adding the address.\n\t\t\t * If we are in oneshot, exit with a failure. */\n\t\t\tif (ctx->options & DHCPCD_ONESHOT) {\n\t\t\t\tloginfox(\"exiting due to oneshot\");\n\t\t\t\teloop_exit(ctx->eloop, EXIT_FAILURE);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t} else {\n\t\tstruct ipv4_addr *ia;\n\n\t\tscript_runreason(ifp, state->reason);\n\t\tdhcpcd_daemonise(ifp->ctx);\n\n\t\t/* We we are not configuring the address, we need to keep\n\t\t * the BPF socket open if the address does not exist. */\n\t\tia = ipv4_iffindaddr(ifp, &state->lease.addr, NULL);\n\t\tif (ia == NULL)\n\t\t\treturn;\n\t\tstate->addr = ia;\n\t\tstate->added = STATE_ADDED;\n\t}\n\n\tdhcp_bound(ifp, old_state);\n}\n\nstatic size_t\ndhcp_message_new(struct bootp **bootp, const struct in_addr *addr,\n    const struct in_addr *mask)\n{\n\tuint8_t *p;\n\tuint32_t cookie;\n\n\tif ((*bootp = calloc(1, sizeof(**bootp))) == NULL)\n\t\treturn 0;\n\n\t(*bootp)->yiaddr = addr->s_addr;\n\tp = (*bootp)->vend;\n\n\tcookie = htonl(MAGIC_COOKIE);\n\tmemcpy(p, &cookie, sizeof(cookie));\n\tp += sizeof(cookie);\n\n\tif (mask->s_addr != INADDR_ANY) {\n\t\t*p++ = DHO_SUBNETMASK;\n\t\t*p++ = sizeof(mask->s_addr);\n\t\tmemcpy(p, &mask->s_addr, sizeof(mask->s_addr));\n\t\tp += sizeof(mask->s_addr);\n\t}\n\n\t*p = DHO_END;\n\treturn sizeof(**bootp);\n}\n\n#if defined(ARP) || defined(KERNEL_RFC5227)\nstatic int\ndhcp_arp_address(struct interface *ifp)\n{\n\tstruct dhcp_state *state;\n\tstruct in_addr addr;\n\tstruct ipv4_addr *ia;\n\n\teloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);\n\n\tstate = D_STATE(ifp);\n\taddr.s_addr = state->offer->yiaddr == INADDR_ANY ?\n\t    state->offer->ciaddr :\n\t    state->offer->yiaddr;\n\t/* If the interface already has the address configured\n\t * then we can't ARP for duplicate detection. */\n\tia = ipv4_iffindaddr(ifp, &addr, NULL);\n#ifdef IN_IFF_NOTUSEABLE\n\tif (ia == NULL || ia->addr_flags & IN_IFF_NOTUSEABLE) {\n\t\tstate->state = DHS_PROBE;\n\t\tif (ia == NULL) {\n\t\t\tstruct dhcp_lease l;\n\n\t\t\tget_lease(ifp, &l, state->offer, state->offer_len);\n\t\t\t/* Add the address now, let the kernel handle DAD. */\n\t\t\tipv4_addaddr(ifp, &l.addr, &l.mask, &l.brd, l.leasetime,\n\t\t\t    l.rebindtime);\n\t\t} else if (ia->addr_flags & IN_IFF_DUPLICATED)\n\t\t\tdhcp_addr_duplicated(ifp, &ia->addr);\n\t\telse\n\t\t\tloginfox(\"%s: waiting for DAD on %s\", ifp->name,\n\t\t\t    inet_ntoa(addr));\n\t\treturn 0;\n\t}\n#else\n\tif (!(ifp->flags & IFF_NOARP) && ifp->options->options & DHCPCD_ARP) {\n\t\tstruct arp_state *astate;\n\t\tstruct dhcp_lease l;\n\n\t\t/* Even if the address exists, we need to defend it. */\n\t\tastate = dhcp_arp_new(ifp, &addr);\n\t\tif (astate == NULL)\n\t\t\treturn -1;\n\n\t\tif (ia == NULL) {\n\t\t\tstate->state = DHS_PROBE;\n\t\t\tget_lease(ifp, &l, state->offer, state->offer_len);\n\t\t\tloginfox(\"%s: probing address %s/%d\", ifp->name,\n\t\t\t    inet_ntoa(l.addr), inet_ntocidr(l.mask));\n\t\t\t/* We need to handle DAD. */\n\t\t\tarp_probe(astate);\n\t\t\treturn 0;\n\t\t}\n\t}\n#endif\n\n\treturn 1;\n}\n\nstatic void\ndhcp_arp_bind(struct interface *ifp)\n{\n\tif (ifp->ctx->options & DHCPCD_TEST || dhcp_arp_address(ifp) == 1)\n\t\tdhcp_bind(ifp);\n}\n#endif\n\nstatic void\ndhcp_lastlease(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tstruct dhcp_state *state = D_STATE(ifp);\n\n\tloginfox(\"%s: timed out contacting a DHCP server, using last lease\",\n\t    ifp->name);\n#if defined(ARP) || defined(KERNEL_RFC5227)\n\tdhcp_arp_bind(ifp);\n#else\n\tdhcp_bind(ifp);\n#endif\n\t/* Set expired here because dhcp_bind() -> ipv4_addaddr() will reset\n\t * state */\n\tstate->added |= STATE_EXPIRED;\n\tstate->interval = 0;\n\tdhcp_discover(ifp);\n}\n\nstatic void\ndhcp_static(struct interface *ifp)\n{\n\tstruct if_options *ifo;\n\tstruct dhcp_state *state;\n\tstruct ipv4_addr *ia;\n\n\tstate = D_STATE(ifp);\n\tifo = ifp->options;\n\n\tia = NULL;\n\tif (ifo->req_addr.s_addr == INADDR_ANY &&\n\t    (ia = ipv4_iffindaddr(ifp, NULL, NULL)) == NULL) {\n\t\tloginfox(\"%s: waiting for 3rd party to \"\n\t\t\t \"configure IP address\",\n\t\t    ifp->name);\n\t\tstate->reason = \"3RDPARTY\";\n\t\tscript_runreason(ifp, state->reason);\n\t\treturn;\n\t}\n\n\tstate->offer_len = dhcp_message_new(&state->offer,\n\t    ia ? &ia->addr : &ifo->req_addr, ia ? &ia->mask : &ifo->req_mask);\n\tif (state->offer_len)\n#if defined(ARP) || defined(KERNEL_RFC5227)\n\t\tdhcp_arp_bind(ifp);\n#else\n\t\tdhcp_bind(ifp);\n#endif\n}\n\nvoid\ndhcp_inform(struct interface *ifp)\n{\n\tstruct dhcp_state *state;\n\tstruct if_options *ifo;\n\tstruct ipv4_addr *ia;\n\n\tstate = D_STATE(ifp);\n\tifo = ifp->options;\n\n\tfree(state->offer);\n\tstate->offer = NULL;\n\tstate->offer_len = 0;\n\n\tif (ifo->req_addr.s_addr == INADDR_ANY) {\n\t\tia = ipv4_iffindaddr(ifp, NULL, NULL);\n\t\tif (ia == NULL) {\n\t\t\tloginfox(\"%s: waiting for 3rd party to \"\n\t\t\t\t \"configure IP address\",\n\t\t\t    ifp->name);\n\t\t\tif (!(ifp->ctx->options & DHCPCD_TEST)) {\n\t\t\t\tstate->reason = \"3RDPARTY\";\n\t\t\t\tscript_runreason(ifp, state->reason);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t} else {\n\t\tia = ipv4_iffindaddr(ifp, &ifo->req_addr, &ifo->req_mask);\n\t\tif (ia == NULL) {\n\t\t\tif (ifp->ctx->options & DHCPCD_TEST) {\n\t\t\t\tlogerrx(\n\t\t\t\t    \"%s: cannot add IP address in test mode\",\n\t\t\t\t    ifp->name);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tia = ipv4_iffindaddr(ifp, &ifo->req_addr, NULL);\n\t\t\tif (ia != NULL)\n\t\t\t\t/* Netmask must be different, delete it. */\n\t\t\t\tipv4_deladdr(ia, 1);\n\t\t\tstate->offer_len = dhcp_message_new(&state->offer,\n\t\t\t    &ifo->req_addr, &ifo->req_mask);\n#ifdef ARP\n\t\t\tif (dhcp_arp_address(ifp) != 1)\n\t\t\t\treturn;\n#endif\n\t\t\tia = ipv4_iffindaddr(ifp, &ifo->req_addr,\n\t\t\t    &ifo->req_mask);\n\t\t\tassert(ia != NULL);\n\t\t}\n\t}\n\n\tstate->state = DHS_INFORM;\n\tstate->addr = ia;\n\tstate->offer_len = dhcp_message_new(&state->offer, &ia->addr,\n\t    &ia->mask);\n\tif (state->offer_len) {\n\t\tdhcp_new_xid(ifp);\n\t\tget_lease(ifp, &state->lease, state->offer, state->offer_len);\n\t\tsend_inform(ifp);\n\t}\n}\n\nvoid\ndhcp_reboot_newopts(struct interface *ifp, unsigned long long oldopts)\n{\n\tstruct if_options *ifo;\n\tstruct dhcp_state *state = D_STATE(ifp);\n\n\tif (state == NULL || state->state == DHS_NONE)\n\t\treturn;\n\tifo = ifp->options;\n\tif ((ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC) &&\n\t\t(state->addr == NULL ||\n\t\t    state->addr->addr.s_addr != ifo->req_addr.s_addr)) ||\n\t    (oldopts & (DHCPCD_INFORM | DHCPCD_STATIC) &&\n\t\t!(ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC)))) {\n\t\tdhcp_drop(ifp, \"EXPIRE\");\n\t}\n}\n\nstatic void\ndhcp_reboot(struct interface *ifp)\n{\n\tstruct if_options *ifo;\n\tstruct dhcp_state *state = D_STATE(ifp);\n\n\tif (state == NULL || state->state == DHS_NONE)\n\t\treturn;\n\tifo = ifp->options;\n\tstate->state = DHS_REBOOT;\n\tstate->interval = 0;\n\n\tif (ifo->options & DHCPCD_LINK && !if_is_link_up(ifp)) {\n\t\tloginfox(\"%s: waiting for carrier\", ifp->name);\n\t\treturn;\n\t}\n\tif (ifo->options & DHCPCD_STATIC) {\n\t\tdhcp_static(ifp);\n\t\treturn;\n\t}\n\tif (ifo->options & DHCPCD_INFORM) {\n\t\tloginfox(\"%s: informing address of %s\", ifp->name,\n\t\t    inet_ntoa(state->lease.addr));\n\t\tdhcp_inform(ifp);\n\t\treturn;\n\t}\n\tif (ifo->reboot == 0 || state->offer == NULL) {\n\t\tdhcp_discover(ifp);\n\t\treturn;\n\t}\n\tif (!IS_DHCP(state->offer))\n\t\treturn;\n\n\tloginfox(\"%s: rebinding lease of %s\", ifp->name,\n\t    inet_ntoa(state->lease.addr));\n\n#if defined(ARP) && !defined(KERNEL_RFC5227)\n\t/* Create the DHCP ARP state so we can defend it. */\n\t(void)dhcp_arp_new(ifp, &state->lease.addr);\n#endif\n\n\tdhcp_new_xid(ifp);\n\tstate->lease.server.s_addr = INADDR_ANY;\n\teloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);\n\n#ifdef IPV4LL\n\t/* Need to add this before dhcp_expire and friends. */\n\tif (!ifo->fallback && ifo->options & DHCPCD_IPV4LL)\n\t\teloop_timeout_add_sec(ifp->ctx->eloop, ifo->ipv4ll_time,\n\t\t    ipv4ll_start, ifp);\n#endif\n\n\tif (ifo->options & DHCPCD_LASTLEASE && state->lease.frominfo)\n\t\teloop_timeout_add_sec(ifp->ctx->eloop, ifo->reboot,\n\t\t    dhcp_lastlease, ifp);\n\telse if (!(ifo->options & DHCPCD_INFORM))\n\t\teloop_timeout_add_sec(ifp->ctx->eloop, ifo->reboot, dhcp_expire,\n\t\t    ifp);\n\n\t/* Don't bother ARP checking as the server could NAK us first.\n\t * Don't call dhcp_request as that would change the state */\n\tsend_request(ifp);\n}\n\nstatic void\ndhcp_deconfigure(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tstruct dhcp_state *state = D_STATE(ifp);\n\tstruct if_options *ifo = ifp->options;\n\tconst char *reason;\n\n#ifdef AUTH\n\tdhcp_auth_reset(&state->auth);\n#endif\n\n\tif (state->state == DHS_RELEASE)\n\t\treason = \"RELEASE\";\n\telse\n\t\treason = state->reason;\n\tstate->state = DHS_NONE;\n\tfree(state->offer);\n\tstate->offer = NULL;\n\tstate->offer_len = 0;\n\tfree(state->old);\n\tstate->old = state->new;\n\tstate->old_len = state->new_len;\n\tstate->new = NULL;\n\tstate->new_len = 0;\n\tif (ifo->options & DHCPCD_CONFIGURE)\n\t\tipv4_applyaddr(ifp);\n\telse {\n\t\tstate->addr = NULL;\n\t\tstate->added = 0;\n\t\tscript_runreason(ifp, reason);\n\t}\n\tfree(state->old);\n\tstate->old = NULL;\n\tstate->old_len = 0;\n\tstate->lease.addr.s_addr = 0;\n\tifo->options &= ~(DHCPCD_CSR_WARNED | DHCPCD_ROUTER_HOST_ROUTE_WARNED);\n\n\tif (ifo->options & DHCPCD_STOPPING) {\n\t\tdhcp_free(ifp);\n\t\tdhcpcd_dropped(ifp);\n\t} else\n\t\tdhcp_close(ifp);\n}\n\nvoid\ndhcp_drop(struct interface *ifp, const char *reason)\n{\n\tstruct dhcp_state *state = D_STATE(ifp);\n\tstruct if_options *ifo = ifp->options;\n\n\t/* dhcp_start may just have been called and we don't yet have a state\n\t * but we do have a timeout, so punt it. */\n\tif (state == NULL || state->state == DHS_NONE) {\n\t\teloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);\n\t\tdhcp_free(ifp);\n\t\tdhcpcd_dropped(ifp);\n\t\treturn;\n\t}\n\n#ifdef ARP\n\tif (state->addr != NULL)\n\t\tarp_freeaddr(ifp, &state->addr->addr);\n#endif\n#ifdef ARPING\n\tstate->arping_index = -1;\n#endif\n\tstate->reason = reason;\n\n\tif (ifo->options & DHCPCD_RELEASE && !(ifo->options & DHCPCD_INFORM)) {\n\t\t/* Failure to send the release may cause this function to\n\t\t * re-enter so guard by setting the state. */\n\t\tif (state->state == DHS_RELEASE)\n\t\t\treturn;\n\t\tstate->state = DHS_RELEASE;\n\n\t\tdhcp_unlink(ifp->ctx, state->leasefile);\n\t\tif (if_is_link_up(ifp) && state->new != NULL &&\n\t\t    state->lease.server.s_addr != INADDR_ANY) {\n\t\t\t/* We need to delay removal of the IP address so the\n\t\t\t * message can be sent.\n\t\t\t * Unlike DHCPv6, there is no acknowledgement. */\n\t\t\tconst struct timespec delay = {\n\t\t\t\t.tv_sec = 1,\n\t\t\t};\n\n\t\t\tloginfox(\"%s: releasing lease of %s\", ifp->name,\n\t\t\t    inet_ntoa(state->lease.addr));\n\t\t\tdhcp_new_xid(ifp);\n\t\t\tsend_message(ifp, DHCP_RELEASE, NULL);\n\t\t\teloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);\n\t\t\teloop_timeout_add_tv(ifp->ctx->eloop, &delay,\n\t\t\t    dhcp_deconfigure, ifp);\n\t\t\treturn;\n\t\t}\n\t}\n#ifdef AUTH\n\telse if (state->auth.reconf != NULL) {\n\t\t/*\n\t\t * Drop the lease as the token may only be present\n\t\t * in the initial reply message and not subsequent\n\t\t * renewals.\n\t\t * If dhcpcd is restarted, the token is lost.\n\t\t * XXX persist this in another file?\n\t\t */\n\t\tdhcp_unlink(ifp->ctx, state->leasefile);\n\t}\n#endif\n\n\teloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);\n\tdhcp_deconfigure(ifp);\n}\n\nstatic int\nblacklisted_ip(const struct if_options *ifo, in_addr_t addr)\n{\n\tsize_t i;\n\n\tfor (i = 0; i < ifo->blacklist_len; i += 2)\n\t\tif (ifo->blacklist[i] == (addr & ifo->blacklist[i + 1]))\n\t\t\treturn 1;\n\treturn 0;\n}\n\n#define WHTLST_NONE    0\n#define WHTLST_MATCH   1\n#define WHTLST_NOMATCH 2\nstatic unsigned int\nwhitelisted_ip(const struct if_options *ifo, in_addr_t addr)\n{\n\tsize_t i;\n\n\tif (ifo->whitelist_len == 0)\n\t\treturn WHTLST_NONE;\n\tfor (i = 0; i < ifo->whitelist_len; i += 2)\n\t\tif (ifo->whitelist[i] == (addr & ifo->whitelist[i + 1]))\n\t\t\treturn WHTLST_MATCH;\n\treturn WHTLST_NOMATCH;\n}\n\nstatic void\nlog_dhcp(int loglevel, const char *msg, const struct interface *ifp,\n    const struct bootp *bootp, size_t bootp_len, const struct in_addr *from,\n    int ad)\n{\n\tconst char *tfrom;\n\tchar *a, sname[sizeof(bootp->sname) * 4];\n\tstruct in_addr addr;\n\tint r;\n\tuint8_t overl;\n\n\tif (strcmp(msg, \"NAK:\") == 0) {\n\t\ta = get_option_string(ifp->ctx, bootp, bootp_len, DHO_MESSAGE);\n\t\tif (a) {\n\t\t\tchar *tmp;\n\t\t\tsize_t al, tmpl;\n\n\t\t\tal = strlen(a);\n\t\t\ttmpl = (al * 4) + 1;\n\t\t\ttmp = malloc(tmpl);\n\t\t\tif (tmp == NULL) {\n\t\t\t\tlogerr(__func__);\n\t\t\t\tfree(a);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tprint_string(tmp, tmpl, OT_STRING, a, al);\n\t\t\tfree(a);\n\t\t\ta = tmp;\n\t\t}\n\t} else if (ad && bootp->yiaddr != 0) {\n\t\taddr.s_addr = bootp->yiaddr;\n\t\ta = strdup(inet_ntoa(addr));\n\t\tif (a == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\treturn;\n\t\t}\n\t} else\n\t\ta = NULL;\n\n\ttfrom = \"from\";\n\tr = get_option_addr(ifp->ctx, &addr, bootp, bootp_len, DHO_SERVERID);\n\tif (get_option_uint8(ifp->ctx, &overl, bootp, bootp_len,\n\t\tDHO_OPTSOVERLOADED) == -1)\n\t\toverl = 0;\n\tif (bootp->sname[0] && r == 0 && !(overl & 2)) {\n\t\tprint_string(sname, sizeof(sname), OT_STRING | OT_DOMAIN,\n\t\t    bootp->sname, sizeof(bootp->sname));\n\t\tif (a == NULL)\n\t\t\tlogmessage(loglevel, \"%s: %s %s %s %s\", ifp->name, msg,\n\t\t\t    tfrom, inet_ntoa(addr), sname);\n\t\telse\n\t\t\tlogmessage(loglevel, \"%s: %s %s %s %s %s\", ifp->name,\n\t\t\t    msg, a, tfrom, inet_ntoa(addr), sname);\n\t} else {\n\t\tif (r != 0) {\n\t\t\ttfrom = \"via\";\n\t\t\taddr = *from;\n\t\t}\n\t\tif (a == NULL)\n\t\t\tlogmessage(loglevel, \"%s: %s %s %s\", ifp->name, msg,\n\t\t\t    tfrom, inet_ntoa(addr));\n\t\telse\n\t\t\tlogmessage(loglevel, \"%s: %s %s %s %s\", ifp->name, msg,\n\t\t\t    a, tfrom, inet_ntoa(addr));\n\t}\n\tfree(a);\n}\n\n/* If we're sharing the same IP address with another interface on the\n * same network, we may receive the DHCP reply on the wrong interface.\n * Try and re-direct it here. */\nstatic void\ndhcp_redirect_dhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len,\n    const struct in_addr *from)\n{\n\tstruct interface *ifn;\n\tconst struct dhcp_state *state;\n\tuint32_t xid;\n\n\txid = ntohl(bootp->xid);\n\tTAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) {\n\t\tif (ifn == ifp)\n\t\t\tcontinue;\n\t\tstate = D_CSTATE(ifn);\n\t\tif (state == NULL || state->state == DHS_NONE)\n\t\t\tcontinue;\n\t\tif (state->xid != xid)\n\t\t\tcontinue;\n\t\tif (ifn->hwlen <= sizeof(bootp->chaddr) &&\n\t\t    memcmp(bootp->chaddr, ifn->hwaddr, ifn->hwlen))\n\t\t\tcontinue;\n\t\tlogdebugx(\"%s: redirecting DHCP message to %s\", ifp->name,\n\t\t    ifn->name);\n\t\tdhcp_handledhcp(ifn, bootp, bootp_len, from);\n\t}\n}\n\nstatic void\ndhcp_handledhcp(struct interface *ifp, struct bootp *bootp, size_t bootp_len,\n    const struct in_addr *from)\n{\n\tstruct dhcp_state *state = D_STATE(ifp);\n\tstruct if_options *ifo = ifp->options;\n\tstruct dhcp_lease *lease = &state->lease;\n\tuint8_t type;\n\tstruct in_addr addr;\n\tunsigned int i;\n\tchar *msg;\n\tbool bootp_copied;\n\tuint32_t v6only_time = 0;\n\tbool use_v6only = false, has_auto_conf = false;\n#ifdef AUTH\n\tconst uint8_t *auth;\n\tsize_t auth_len;\n#endif\n#ifdef IPV4LL\n\tuint8_t tmp;\n#endif\n#ifdef IN_IFF_DUPLICATED\n\tstruct ipv4_addr *ia;\n#endif\n\n#define LOGDHCP0(l, m) log_dhcp((l), (m), ifp, bootp, bootp_len, from, 0)\n#define LOGDHCP(l, m)  log_dhcp((l), (m), ifp, bootp, bootp_len, from, 1)\n\n#define IS_STATE_ACTIVE(s)                                    \\\n\t((s) - state != DHS_NONE && (s)->state != DHS_INIT && \\\n\t    (s)->state != DHS_BOUND)\n\n\t/* Don't do anything if the user hasn't configured it. */\n\tif (ifp->active != IF_ACTIVE_USER ||\n\t    ifp->options->options & DHCPCD_STOPPING ||\n\t    !(ifp->options->options & DHCPCD_DHCP))\n\t\treturn;\n\n\tif (bootp->op != BOOTREPLY) {\n\t\tif (IS_STATE_ACTIVE(state))\n\t\t\tlogdebugx(\"%s: op (%d) is not BOOTREPLY\", ifp->name,\n\t\t\t    bootp->op);\n\t\treturn;\n\t}\n\n\tif (state->xid != ntohl(bootp->xid)) {\n\t\tif (IS_STATE_ACTIVE(state))\n\t\t\tlogdebugx(\"%s: wrong xid 0x%x (expecting 0x%x) from %s\",\n\t\t\t    ifp->name, ntohl(bootp->xid), state->xid,\n\t\t\t    inet_ntoa(*from));\n\t\tdhcp_redirect_dhcp(ifp, bootp, bootp_len, from);\n\t\treturn;\n\t}\n\n\tif (ifp->hwlen <= sizeof(bootp->chaddr) &&\n\t    memcmp(bootp->chaddr, ifp->hwaddr, ifp->hwlen)) {\n\t\tif (IS_STATE_ACTIVE(state)) {\n\t\t\tchar buf[sizeof(bootp->chaddr) * 3];\n\n\t\t\tlogdebugx(\"%s: xid 0x%x is for hwaddr %s\", ifp->name,\n\t\t\t    ntohl(bootp->xid),\n\t\t\t    hwaddr_ntoa(bootp->chaddr, sizeof(bootp->chaddr),\n\t\t\t\tbuf, sizeof(buf)));\n\t\t}\n\t\tdhcp_redirect_dhcp(ifp, bootp, bootp_len, from);\n\t\treturn;\n\t}\n\n\tif (!ifp->active)\n\t\treturn;\n\n\ti = whitelisted_ip(ifp->options, from->s_addr);\n\tswitch (i) {\n\tcase WHTLST_NOMATCH:\n\t\tlogwarnx(\"%s: non whitelisted DHCP packet from %s\", ifp->name,\n\t\t    inet_ntoa(*from));\n\t\treturn;\n\tcase WHTLST_MATCH:\n\t\tbreak;\n\tcase WHTLST_NONE:\n\t\tif (blacklisted_ip(ifp->options, from->s_addr) == 1) {\n\t\t\tlogwarnx(\"%s: blacklisted DHCP packet from %s\",\n\t\t\t    ifp->name, inet_ntoa(*from));\n\t\t\treturn;\n\t\t}\n\t}\n\n\t/* We may have found a BOOTP server */\n\tif (get_option_uint8(ifp->ctx, &type, bootp, bootp_len,\n\t\tDHO_MESSAGETYPE) == -1)\n\t\ttype = 0;\n\telse if (ifo->options & DHCPCD_BOOTP) {\n\t\tlogdebugx(\"%s: ignoring DHCP reply (expecting BOOTP)\",\n\t\t    ifp->name);\n\t\treturn;\n\t}\n\n#ifdef AUTH\n\t/* Authenticate the message */\n\tauth = get_option(ifp->ctx, bootp, bootp_len, DHO_AUTHENTICATION,\n\t    &auth_len);\n\tif (auth) {\n\t\tif (dhcp_auth_validate(&state->auth, &ifo->auth,\n\t\t\t(uint8_t *)bootp, bootp_len, 4, type, auth,\n\t\t\tauth_len) == NULL) {\n\t\t\tLOGDHCP0(LOG_ERR, \"authentication failed\");\n\t\t\treturn;\n\t\t}\n\t\tif (state->auth.token)\n\t\t\tlogdebugx(\"%s: validated using 0x%08\" PRIu32, ifp->name,\n\t\t\t    state->auth.token->secretid);\n\t\telse\n\t\t\tloginfox(\"%s: accepted reconfigure key\", ifp->name);\n\t} else if (ifo->auth.options & DHCPCD_AUTH_SEND) {\n\t\tif (ifo->auth.options & DHCPCD_AUTH_REQUIRE) {\n\t\t\tLOGDHCP0(LOG_ERR, \"no authentication\");\n\t\t\treturn;\n\t\t}\n\t\tLOGDHCP0(LOG_WARNING, \"no authentication\");\n\t}\n#endif\n\n\t/* RFC 3203 */\n\tif (type == DHCP_FORCERENEW) {\n\t\tif (from->s_addr == INADDR_ANY ||\n\t\t    from->s_addr == INADDR_BROADCAST) {\n\t\t\tLOGDHCP(LOG_ERR, \"discarding Force Renew\");\n\t\t\treturn;\n\t\t}\n#ifdef AUTH\n\t\tif (auth == NULL) {\n\t\t\tLOGDHCP(LOG_ERR, \"unauthenticated Force Renew\");\n\t\t\tif (ifo->auth.options & DHCPCD_AUTH_REQUIRE)\n\t\t\t\treturn;\n\t\t}\n\t\tif (state->state != DHS_BOUND && state->state != DHS_INFORM) {\n\t\t\tLOGDHCP(LOG_DEBUG, \"not bound, ignoring Force Renew\");\n\t\t\treturn;\n\t\t}\n\t\tLOGDHCP(LOG_INFO, \"Force Renew from\");\n\t\t/* The rebind and expire timings are still the same, we just\n\t\t * enter the renew state early */\n\t\tif (state->state == DHS_BOUND)\n\t\t\tdhcp_renew(ifp);\n\t\telse {\n\t\t\teloop_timeout_delete(ifp->ctx->eloop, send_inform, ifp);\n\t\t\tdhcp_inform(ifp);\n\t\t}\n#else\n\t\tLOGDHCP(LOG_ERR, \"unauthenticated Force Renew\");\n#endif\n\t\treturn;\n\t}\n\n\tif (state->state == DHS_BOUND) {\n\t\tLOGDHCP(LOG_DEBUG, \"bound, ignoring\");\n\t\treturn;\n\t}\n\n\tif (state->state == DHS_PROBE) {\n\t\t/* Ignore any DHCP messages whilst probing a lease to bind. */\n\t\tLOGDHCP(LOG_DEBUG, \"probing, ignoring\");\n\t\treturn;\n\t}\n\n\t/* reset the message counter */\n\tstate->interval = 0;\n\n\t/* Ensure that no reject options are present */\n\tfor (i = 1; i < 255; i++) {\n\t\tif (has_option_mask(ifo->rejectmask, i) &&\n\t\t    get_option(ifp->ctx, bootp, bootp_len, (uint8_t)i, NULL)) {\n\t\t\tLOGDHCP(LOG_WARNING, \"reject DHCP\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (type == DHCP_NAK) {\n\t\t/* For NAK, only check if we require the ServerID */\n\t\tif (has_option_mask(ifo->requiremask, DHO_SERVERID) &&\n\t\t    get_option_addr(ifp->ctx, &addr, bootp, bootp_len,\n\t\t\tDHO_SERVERID) == -1) {\n\t\t\tLOGDHCP(LOG_WARNING, \"reject NAK\");\n\t\t\treturn;\n\t\t}\n\n\t\t/* We should restart on a NAK */\n\t\tLOGDHCP(LOG_WARNING, \"NAK:\");\n\t\tif ((msg = get_option_string(ifp->ctx, bootp, bootp_len,\n\t\t\t DHO_MESSAGE))) {\n\t\t\tlogwarnx(\"%s: message: %s\", ifp->name, msg);\n\t\t\tfree(msg);\n\t\t}\n\t\tif (state->state == DHS_INFORM) /* INFORM should not be NAKed */\n\t\t\treturn;\n\t\tif (!(ifp->ctx->options & DHCPCD_TEST)) {\n\t\t\tdhcp_drop(ifp, \"NAK\");\n\t\t\tdhcp_unlink(ifp->ctx, state->leasefile);\n\t\t}\n\n\t\t/* If we constantly get NAKS then we should slowly back off */\n\t\teloop_timeout_add_sec(ifp->ctx->eloop, state->nakoff,\n\t\t    dhcp_discover, ifp);\n\t\tif (state->nakoff == 0)\n\t\t\tstate->nakoff = 1;\n\t\telse {\n\t\t\tstate->nakoff *= 2;\n\t\t\tif (state->nakoff > NAKOFF_MAX)\n\t\t\t\tstate->nakoff = NAKOFF_MAX;\n\t\t}\n\t\treturn;\n\t}\n\n\t/* Ensure that all required options are present */\n\tfor (i = 1; i < 255; i++) {\n\t\tif (has_option_mask(ifo->requiremask, i) &&\n\t\t    !get_option(ifp->ctx, bootp, bootp_len, (uint8_t)i, NULL)) {\n\t\t\t/* If we are BOOTP, then ignore the need for serverid.\n\t\t\t * To ignore BOOTP, require dhcp_message_type.\n\t\t\t * However, nothing really stops BOOTP from providing\n\t\t\t * DHCP style options as well so the above isn't\n\t\t\t * always true. */\n\t\t\tif (type == 0 && i == DHO_SERVERID)\n\t\t\t\tcontinue;\n\t\t\tLOGDHCP(LOG_WARNING, \"reject DHCP\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (has_option_mask(ifo->requestmask, DHO_IPV6_PREFERRED_ONLY)) {\n\t\tif (get_option_uint32(ifp->ctx, &v6only_time, bootp, bootp_len,\n\t\t\tDHO_IPV6_PREFERRED_ONLY) == 0 &&\n\t\t    (state->state == DHS_DISCOVER ||\n\t\t\tstate->state == DHS_REBOOT ||\n\t\t\tstate->state == DHS_NONE)) {\n\t\t\tchar v6msg[128];\n\n\t\t\tuse_v6only = true;\n\t\t\tif (v6only_time < MIN_V6ONLY_WAIT)\n\t\t\t\tv6only_time = MIN_V6ONLY_WAIT;\n\t\t\tsnprintf(v6msg, sizeof(v6msg),\n\t\t\t    \"IPv6-Only Preferred received (%u seconds)\",\n\t\t\t    v6only_time);\n\t\t\tLOGDHCP(LOG_INFO, v6msg);\n\t\t}\n\t}\n\n\t/* DHCP Auto-Configure, RFC 2563 */\n\tif (type == DHCP_OFFER && bootp->yiaddr == INADDR_ANY) {\n\t\tLOGDHCP(LOG_WARNING, \"no address offered\");\n\t\tif ((msg = get_option_string(ifp->ctx, bootp, bootp_len,\n\t\t\t DHO_MESSAGE))) {\n\t\t\tlogwarnx(\"%s: message: %s\", ifp->name, msg);\n\t\t\tfree(msg);\n\t\t}\n#ifdef IPV4LL\n\t\tif (state->state == DHS_DISCOVER &&\n\t\t    get_option_uint8(ifp->ctx, &tmp, bootp, bootp_len,\n\t\t\tDHO_AUTOCONFIGURE) == 0) {\n\t\t\thas_auto_conf = true;\n\t\t\tswitch (tmp) {\n\t\t\tcase 0:\n\t\t\t\tLOGDHCP(LOG_WARNING, \"IPv4LL disabled from\");\n\t\t\t\tif (ifp->options->options & DHCPCD_IPV4LL)\n\t\t\t\t\tipv4ll_drop(ifp);\n#ifdef ARP\n\t\t\t\tarp_drop(ifp);\n#endif\n\t\t\t\tbreak;\n\t\t\tcase 1:\n\t\t\t\tLOGDHCP(LOG_WARNING, \"IPv4LL enabled from\");\n\t\t\t\tipv4ll_start(ifp);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tlogerrx(\"%s: unknown auto configuration \"\n\t\t\t\t\t\"option %d\",\n\t\t\t\t    ifp->name, tmp);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n#endif\n\t}\n\n\tif (use_v6only) {\n\t\tdhcp_drop(ifp, \"EXPIRE\");\n\t\tdhcp_unlink(ifp->ctx, state->leasefile);\n\t}\n\tif (use_v6only || has_auto_conf) {\n\t\teloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);\n\t\teloop_timeout_add_sec(ifp->ctx->eloop,\n\t\t    use_v6only ? v6only_time : DHCP_MAX, dhcp_discover, ifp);\n\t\treturn;\n\t}\n\n\t/* No hints as what to do with no address?\n\t * All we can do is continue. */\n\tif (type == DHCP_OFFER && bootp->yiaddr == INADDR_ANY)\n\t\treturn;\n\n\t/* Ensure that the address offered is valid */\n\tif ((type == 0 || type == DHCP_OFFER || type == DHCP_ACK) &&\n\t    (bootp->ciaddr == INADDR_ANY ||\n\t\tbootp->ciaddr == INADDR_BROADCAST) &&\n\t    (bootp->yiaddr == INADDR_ANY ||\n\t\tbootp->yiaddr == INADDR_BROADCAST)) {\n\t\tLOGDHCP(LOG_WARNING, \"reject invalid address\");\n\t\treturn;\n\t}\n\n#ifdef IN_IFF_DUPLICATED\n\tia = ipv4_iffindaddr(ifp, &lease->addr, NULL);\n\tif (ia && ia->addr_flags & IN_IFF_DUPLICATED) {\n\t\tLOGDHCP(LOG_WARNING, \"declined duplicate address\");\n\t\tif (type)\n\t\t\tdhcp_decline(ifp);\n\t\tipv4_deladdr(ia, 0);\n\t\teloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);\n\t\teloop_timeout_add_sec(ifp->ctx->eloop, DHCP_RAND_MAX,\n\t\t    dhcp_discover, ifp);\n\t\treturn;\n\t}\n#endif\n\n\tbootp_copied = false;\n\tif ((type == 0 || type == DHCP_OFFER) && state->state == DHS_DISCOVER) {\n\t\tlease->frominfo = 0;\n\t\tlease->addr.s_addr = bootp->yiaddr;\n\t\tmemcpy(&lease->cookie, bootp->vend, sizeof(lease->cookie));\n\t\tif (type == 0 ||\n\t\t    get_option_addr(ifp->ctx, &lease->server, bootp, bootp_len,\n\t\t\tDHO_SERVERID) != 0)\n\t\t\tlease->server.s_addr = INADDR_ANY;\n\n\t\t/* Test for rapid commit in the OFFER */\n\t\tif (!(ifp->ctx->options & DHCPCD_TEST) &&\n\t\t    has_option_mask(ifo->requestmask, DHO_RAPIDCOMMIT) &&\n\t\t    get_option(ifp->ctx, bootp, bootp_len, DHO_RAPIDCOMMIT,\n\t\t\tNULL)) {\n\t\t\tstate->state = DHS_REQUEST;\n\t\t\tgoto rapidcommit;\n\t\t}\n\n\t\tLOGDHCP(LOG_INFO, \"offered\");\n\t\tif (state->offer_len < bootp_len) {\n\t\t\tfree(state->offer);\n\t\t\tif ((state->offer = malloc(bootp_len)) == NULL) {\n\t\t\t\tlogerr(__func__);\n\t\t\t\tstate->offer_len = 0;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tstate->offer_len = bootp_len;\n\t\tmemcpy(state->offer, bootp, bootp_len);\n\t\tbootp_copied = true;\n\t\tif (ifp->ctx->options & DHCPCD_TEST) {\n\t\t\tfree(state->old);\n\t\t\tstate->old = state->new;\n\t\t\tstate->old_len = state->new_len;\n\t\t\tstate->new = state->offer;\n\t\t\tstate->new_len = state->offer_len;\n\t\t\tstate->offer = NULL;\n\t\t\tstate->offer_len = 0;\n\t\t\tstate->reason = \"TEST\";\n\t\t\tscript_runreason(ifp, state->reason);\n\t\t\teloop_exit(ifp->ctx->eloop, EXIT_SUCCESS);\n\t\t\tif (state->bpf)\n\t\t\t\tstate->bpf->bpf_flags |= BPF_EOF;\n\t\t\treturn;\n\t\t}\n\t\teloop_timeout_delete(ifp->ctx->eloop, send_discover, ifp);\n\t\t/* We don't request BOOTP addresses */\n\t\tif (type) {\n\t\t\t/* We used to ARP check here, but that seems to be in\n\t\t\t * violation of RFC2131 where it only describes\n\t\t\t * DECLINE after REQUEST.\n\t\t\t * It also seems that some MS DHCP servers actually\n\t\t\t * ignore DECLINE if no REQUEST, ie we decline a\n\t\t\t * DISCOVER. */\n\t\t\tdhcp_request(ifp);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (type) {\n\t\tif (type == DHCP_OFFER) {\n\t\t\tLOGDHCP(LOG_WARNING, \"ignoring offer of\");\n\t\t\treturn;\n\t\t}\n\n\t\t/* We should only be dealing with acks */\n\t\tif (type != DHCP_ACK) {\n\t\t\tLOGDHCP(LOG_ERR, \"not ACK or OFFER\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (state->state == DHS_DISCOVER) {\n\t\t\t/* We only allow ACK of rapid commit DISCOVER. */\n\t\t\tif (has_option_mask(ifo->requestmask,\n\t\t\t\tDHO_RAPIDCOMMIT) &&\n\t\t\t    get_option(ifp->ctx, bootp, bootp_len,\n\t\t\t\tDHO_RAPIDCOMMIT, NULL))\n\t\t\t\tstate->state = DHS_REQUEST;\n\t\t\telse {\n\t\t\t\tLOGDHCP(LOG_DEBUG, \"ignoring ack of\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\trapidcommit:\n\t\tif (!(ifo->options & DHCPCD_INFORM))\n\t\t\tLOGDHCP(LOG_DEBUG, \"acknowledged\");\n\t\telse\n\t\t\tifo->options &= ~DHCPCD_STATIC;\n\t}\n\n\t/* No NAK, so reset the backoff\n\t * We don't reset on an OFFER message because the server could\n\t * potentially NAK the REQUEST. */\n\tstate->nakoff = 0;\n\n\t/* BOOTP could have already assigned this above. */\n\tif (!bootp_copied) {\n\t\tif (state->offer_len < bootp_len) {\n\t\t\tfree(state->offer);\n\t\t\tif ((state->offer = malloc(bootp_len)) == NULL) {\n\t\t\t\tlogerr(__func__);\n\t\t\t\tstate->offer_len = 0;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tstate->offer_len = bootp_len;\n\t\tmemcpy(state->offer, bootp, bootp_len);\n\t}\n\n\tlease->frominfo = 0;\n\teloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);\n\n#if defined(ARP) || defined(KERNEL_RFC5227)\n\tdhcp_arp_bind(ifp);\n#else\n\tdhcp_bind(ifp);\n#endif\n}\n\nstatic void *\nget_udp_data(void *packet, size_t *len)\n{\n\tconst struct ip *ip = packet;\n\tsize_t ip_hl = (size_t)ip->ip_hl * 4;\n\tchar *p = packet;\n\n\tp += ip_hl + sizeof(struct udphdr);\n\t*len = (size_t)ntohs(ip->ip_len) - sizeof(struct udphdr) - ip_hl;\n\treturn p;\n}\n\nstatic bool\nis_packet_udp_bootp(void *packet, size_t plen)\n{\n\tstruct ip *ip = packet;\n\tsize_t ip_hlen;\n\tstruct udphdr udp;\n\n\tif (plen < sizeof(*ip))\n\t\treturn false;\n\n\tif (ip->ip_v != IPVERSION || ip->ip_p != IPPROTO_UDP)\n\t\treturn false;\n\n\t/* Sanity. */\n\tif (ntohs(ip->ip_len) > plen)\n\t\treturn false;\n\n\tip_hlen = (size_t)ip->ip_hl * 4;\n\tif (ip_hlen < sizeof(*ip))\n\t\treturn false;\n\n\t/* Check we have a UDP header and BOOTP. */\n\tif (ip_hlen + sizeof(udp) + offsetof(struct bootp, vend) > plen)\n\t\treturn false;\n\n\t/* Sanity. */\n\tmemcpy(&udp, (char *)ip + ip_hlen, sizeof(udp));\n\tif (ntohs(udp.uh_ulen) < sizeof(udp))\n\t\treturn false;\n\tif (ip_hlen + ntohs(udp.uh_ulen) > plen)\n\t\treturn false;\n\n\t/* Check it's to the right port. */\n\tif (udp.uh_dport != htons(BOOTPC))\n\t\treturn false;\n\n\treturn true;\n}\n\n/* IPv4 pseudo header used for computing TCP and UDP checksums. */\nstruct ip_pseudo {\n\tstruct in_addr ipp_src;\n\tstruct in_addr ipp_dst;\n\tuint8_t ipp_pad; /* must be zero */\n\tuint8_t ipp_p;\n\tuint16_t ipp_len;\n};\n\n/* Lengths have already been checked. */\nstatic bool\nchecksums_valid(const void *packet, struct in_addr *from, unsigned int flags)\n{\n\tconst struct ip *ip = packet;\n\tsize_t ip_hlen;\n\tstruct udphdr udp;\n\tconst char *udpp;\n\tuint32_t csum;\n\tstruct ip_pseudo ip_pseudo;\n\t/* We create a buffer to copy ip_pseudo into and send that to\n\t * in_cksum() to avoid memory issues. */\n\tuint8_t ip_pseudo_buf[sizeof(struct ip_pseudo)];\n\n\tif (from != NULL)\n\t\tfrom->s_addr = ip->ip_src.s_addr;\n\n\tip_hlen = (size_t)ip->ip_hl * 4;\n\t/* RFC 1071 states that the check of the checksum is equal to 0. */\n\tif (in_cksum(ip, ip_hlen, NULL) != 0)\n\t\treturn false;\n\n\tif (flags & BPF_PARTIALCSUM)\n\t\treturn true;\n\n\tudpp = (const char *)ip + ip_hlen;\n\tmemcpy(&udp, udpp, sizeof(udp));\n\t/* RFC 768 states that zero means no checksum to verify. */\n\tif (udp.uh_sum == 0)\n\t\treturn true;\n\n\t/* UDP checksum is based on a pseudo IP header alongside\n\t * the UDP header and payload. */\n\tip_pseudo.ipp_src = ip->ip_src;\n\tip_pseudo.ipp_dst = ip->ip_dst;\n\tip_pseudo.ipp_pad = 0;\n\tip_pseudo.ipp_p = ip->ip_p;\n\tip_pseudo.ipp_len = udp.uh_ulen;\n\tmemcpy(ip_pseudo_buf, &ip_pseudo, sizeof(ip_pseudo_buf));\n\n\t/* Checksum pseudo header and then UDP + payload. */\n\tcsum = 0;\n\tin_cksum(ip_pseudo_buf, sizeof(ip_pseudo_buf), &csum);\n\tcsum = in_cksum(udpp, ntohs(udp.uh_ulen), &csum);\n\n\t/* RFC 1071 states that the check of the checksum is equal to 0. */\n\treturn csum == 0;\n}\n\nstatic void\ndhcp_handlebootp(struct interface *ifp, struct bootp *bootp, size_t len,\n    struct in_addr *from)\n{\n\tsize_t v;\n\n\t/* Unlikely, but appeases sanitizers. */\n\tif (len > FRAMELEN_MAX) {\n\t\tlogerrx(\"%s: packet exceeded frame length (%zu) from %s\",\n\t\t    ifp->name, len, inet_ntoa(*from));\n\t\treturn;\n\t}\n\n\t/* To make our IS_DHCP macro easy, ensure the vendor\n\t * area has at least 4 octets. */\n\tv = len - offsetof(struct bootp, vend);\n\twhile (v < 4) {\n\t\tbootp->vend[v++] = '\\0';\n\t\tlen++;\n\t}\n\n\tdhcp_handledhcp(ifp, bootp, len, from);\n}\n\nvoid\ndhcp_packet(struct interface *ifp, uint8_t *data, size_t len,\n    unsigned int bpf_flags)\n{\n\tstruct bootp *bootp;\n\tstruct in_addr from;\n\tsize_t udp_len;\n\tsize_t fl = bpf_frame_header_len(ifp);\n#ifdef PRIVSEP\n\tconst struct dhcp_state *state = D_CSTATE(ifp);\n\n\t/* It's possible that an interface departs and arrives in short\n\t * order to receive a BPF frame out of order.\n\t * There is a similar check in ARP, but much lower down the stack.\n\t * It's not needed for other inet protocols because we send the\n\t * message as a whole and select the interface off that and then\n\t * check state. BPF on the other hand is very interface\n\t * specific and we do need this check. */\n\tif (state == NULL)\n\t\treturn;\n\n\t/* Ignore double reads */\n\tif (IN_PRIVSEP(ifp->ctx)) {\n\t\tswitch (state->state) {\n\t\tcase DHS_BOUND: /* FALLTHROUGH */\n\t\tcase DHS_RENEW:\n\t\t\treturn;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n#endif\n\n\t/* Trim frame header */\n\tif (fl != 0) {\n\t\tif (len < fl) {\n\t\t\tlogerrx(\"%s: %s: short frame header %zu\", __func__,\n\t\t\t    ifp->name, len);\n\t\t\treturn;\n\t\t}\n\t\tlen -= fl;\n\t\t/* Move the data to avoid alignment errors. */\n\t\tmemmove(data, data + fl, len);\n\t}\n\n\t/* Validate filter. */\n\tif (!is_packet_udp_bootp(data, len)) {\n#ifdef BPF_DEBUG\n\t\tlogerrx(\"%s: DHCP BPF validation failure\", ifp->name);\n#endif\n\t\treturn;\n\t}\n\n\tif (!checksums_valid(data, &from, bpf_flags)) {\n\t\tlogerrx(\"%s: checksum failure from %s\", ifp->name,\n\t\t    inet_ntoa(from));\n\t\treturn;\n\t}\n\n\t/*\n\t * DHCP has a variable option area rather than a fixed vendor area.\n\t * Because DHCP uses the BOOTP protocol it should still send BOOTP\n\t * sized packets to be RFC compliant.\n\t * However some servers send a truncated vendor area.\n\t * dhcpcd can work fine without the vendor area being sent.\n\t */\n\tbootp = get_udp_data(data, &udp_len);\n\tdhcp_handlebootp(ifp, bootp, udp_len, &from);\n}\n\nstatic void\ndhcp_readbpf(void *arg, unsigned short events)\n{\n\tstruct interface *ifp = arg;\n\t/* Sparc64 needs this buffer aligned */\n\talignas(sizeof(struct ip *)) uint8_t buf[FRAMELEN_MAX];\n\tssize_t bytes;\n\tstruct dhcp_state *state = D_STATE(ifp);\n\tstruct bpf *bpf = state->bpf;\n\n\tif (events != ELE_READ)\n\t\tlogerrx(\"%s: unexpected event 0x%04x\", __func__, events);\n\n\tbpf->bpf_flags &= ~BPF_EOF;\n\twhile (!(bpf->bpf_flags & BPF_EOF)) {\n\t\tbytes = bpf_read(bpf, buf, sizeof(buf));\n\t\tif (bytes == -1) {\n\t\t\tif (state->state != DHS_NONE) {\n\t\t\t\tlogerr(\"%s: %s\", __func__, ifp->name);\n\t\t\t\tdhcp_close(ifp);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tdhcp_packet(ifp, buf, (size_t)bytes, bpf->bpf_flags);\n\t\t/* Check we still have a state after processing. */\n\t\tif ((state = D_STATE(ifp)) == NULL)\n\t\t\tbreak;\n\t\tif ((bpf = state->bpf) == NULL)\n\t\t\tbreak;\n\t}\n}\n\nvoid\ndhcp_recvmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg)\n{\n\tstruct sockaddr_in *from = (struct sockaddr_in *)msg->msg_name;\n\tstruct iovec *iov = &msg->msg_iov[0];\n\tstruct interface *ifp;\n\tconst struct dhcp_state *state;\n\n\tifp = if_findifpfromcmsg(ctx, msg, NULL);\n\tif (ifp == NULL) {\n\t\tlogerr(__func__);\n\t\treturn;\n\t}\n\n\tif (iov->iov_len < offsetof(struct bootp, vend)) {\n\t\tlogerrx(\"%s: truncated packet (%zu) from %s\", ifp->name,\n\t\t    iov->iov_len, inet_ntoa(from->sin_addr));\n\t\treturn;\n\t}\n\n\tstate = D_CSTATE(ifp);\n\tif (state == NULL) {\n\t\t/* Try re-directing it to another interface. */\n\t\tdhcp_redirect_dhcp(ifp, (struct bootp *)iov->iov_base,\n\t\t    iov->iov_len, &from->sin_addr);\n\t\treturn;\n\t}\n\n\tif (state->bpf != NULL) {\n\t\t/* Avoid a duplicate read if BPF is open for the interface. */\n\t\treturn;\n\t}\n#ifdef PRIVSEP\n\tif (IN_PRIVSEP(ctx)) {\n\t\tswitch (state->state) {\n\t\tcase DHS_BOUND: /* FALLTHROUGH */\n\t\tcase DHS_RENEW:\n\t\t\tbreak;\n\t\tdefault:\n\t\t\t/* Any other state we ignore it or will receive\n\t\t\t * via BPF. */\n\t\t\treturn;\n\t\t}\n\t}\n#endif\n\n\tdhcp_handlebootp(ifp, iov->iov_base, iov->iov_len, &from->sin_addr);\n}\n\nstatic void\ndhcp_readudp(struct dhcpcd_ctx *ctx, struct interface *ifp,\n    unsigned short events)\n{\n\tconst struct dhcp_state *state;\n\tstruct sockaddr_in from;\n\tunion {\n\t\tstruct bootp bootp;\n\t\tuint8_t buf[10 * 1024]; /* Maximum MTU */\n\t} iovbuf;\n\tstruct iovec iov = {\n\t\t.iov_base = iovbuf.buf,\n\t\t.iov_len = sizeof(iovbuf.buf),\n\t};\n\tunion {\n\t\tstruct cmsghdr hdr;\n#ifdef IP_RECVIF\n\t\tuint8_t buf[CMSG_SPACE(sizeof(struct sockaddr_dl))];\n#else\n\t\tuint8_t buf[CMSG_SPACE(sizeof(struct in_pktinfo))];\n#endif\n\t} cmsgbuf = { .buf = { 0 } };\n\tstruct msghdr msg = {\n\t\t.msg_name = &from,\n\t\t.msg_namelen = sizeof(from),\n\t\t.msg_iov = &iov,\n\t\t.msg_iovlen = 1,\n\t\t.msg_control = cmsgbuf.buf,\n\t\t.msg_controllen = sizeof(cmsgbuf.buf),\n\t};\n\tint s;\n\tssize_t bytes;\n\n\tif (events != ELE_READ)\n\t\tlogerrx(\"%s: unexpected event 0x%04x\", __func__, events);\n\n\tif (ifp != NULL) {\n\t\tstate = D_CSTATE(ifp);\n\t\ts = state->udp_rfd;\n\t} else\n\t\ts = ctx->udp_rfd;\n\n\tbytes = recvmsg(s, &msg, 0);\n\tif (bytes == -1) {\n\t\tlogerr(__func__);\n\t\treturn;\n\t}\n\n\tiov.iov_len = (size_t)bytes;\n\tdhcp_recvmsg(ctx, &msg);\n}\n\nstatic void\ndhcp_handleudp(void *arg, unsigned short events)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\n\tdhcp_readudp(ctx, NULL, events);\n}\n\nstatic void\ndhcp_handleifudp(void *arg, unsigned short events)\n{\n\tstruct interface *ifp = arg;\n\n\tdhcp_readudp(ifp->ctx, ifp, events);\n}\n\nstatic int\ndhcp_openbpf(struct interface *ifp)\n{\n\tstruct dhcp_state *state;\n\n\tstate = D_STATE(ifp);\n\n#ifdef PRIVSEP\n\tif (IN_PRIVSEP_SE(ifp->ctx)) {\n\t\tif (ps_bpf_openbootp(ifp) == -1) {\n\t\t\tlogerr(__func__);\n\t\t\treturn -1;\n\t\t}\n\t\treturn 0;\n\t}\n#endif\n\n\tif (state->bpf != NULL)\n\t\treturn 0;\n\n\tstate->bpf = bpf_open(ifp, bpf_filter_bootp, NULL);\n\tif (state->bpf == NULL) {\n\t\tif (errno == ENOENT) {\n\t\t\tlogerrx(\"%s not found\", bpf_name);\n\t\t\t/* May as well disable IPv4 entirely at\n\t\t\t * this point as we really need it. */\n\t\t\tifp->options->options &= ~DHCPCD_IPV4;\n\t\t} else\n\t\t\tlogerr(\"%s: %s\", __func__, ifp->name);\n\t\treturn -1;\n\t}\n\n\tif (eloop_event_add(ifp->ctx->eloop, state->bpf->bpf_fd, ELE_READ,\n\t\tdhcp_readbpf, ifp) == -1)\n\t\tlogerr(\"%s: eloop_event_add\", __func__);\n\treturn 0;\n}\n\nvoid\ndhcp_free(struct interface *ifp)\n{\n\tstruct dhcp_state *state = D_STATE(ifp);\n\tstruct dhcpcd_ctx *ctx;\n\n\tdhcp_close(ifp);\n#ifdef ARP\n\tarp_drop(ifp);\n#endif\n\tif (state) {\n\t\tstate->state = DHS_NONE;\n\t\tfree(state->old);\n\t\tfree(state->new);\n\t\tfree(state->offer);\n\t\tfree(state->clientid);\n\t\tfree(state);\n\t\tifp->if_data[IF_DATA_DHCP] = NULL;\n\t}\n\n\tctx = ifp->ctx;\n\t/* If we don't have any more DHCP enabled interfaces,\n\t * close the global socket and release resources */\n\tif (ctx->ifaces) {\n\t\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\t\tstate = D_STATE(ifp);\n\t\t\tif (state != NULL && state->state != DHS_NONE)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\tif (ifp == NULL) {\n\t\tif (ctx->udp_rfd != -1) {\n\t\t\teloop_event_delete(ctx->eloop, ctx->udp_rfd);\n\t\t\tclose(ctx->udp_rfd);\n\t\t\tctx->udp_rfd = -1;\n\t\t}\n\t\tif (ctx->udp_wfd != -1) {\n\t\t\tclose(ctx->udp_wfd);\n\t\t\tctx->udp_wfd = -1;\n\t\t}\n\n\t\tfree(ctx->opt_buffer);\n\t\tctx->opt_buffer = NULL;\n\t\tctx->opt_buffer_len = 0;\n\t}\n}\n\nstatic int\ndhcp_initstate(struct interface *ifp)\n{\n\tstruct dhcp_state *state;\n\n\tstate = D_STATE(ifp);\n\tif (state != NULL)\n\t\treturn 0;\n\n\tifp->if_data[IF_DATA_DHCP] = calloc(1, sizeof(*state));\n\tstate = D_STATE(ifp);\n\tif (state == NULL)\n\t\treturn -1;\n\n\tstate->state = DHS_NONE;\n\t/* 0 is a valid fd, so init to -1 */\n\tstate->udp_rfd = -1;\n#ifdef ARPING\n\tstate->arping_index = -1;\n#endif\n\treturn 1;\n}\n\nstatic int\ndhcp_init(struct interface *ifp)\n{\n\tstruct dhcp_state *state;\n\tstruct if_options *ifo;\n\tuint8_t len;\n\tchar buf[(sizeof(ifo->clientid) - 1) * 3];\n\n\tif (dhcp_initstate(ifp) == -1)\n\t\treturn -1;\n\n\tstate = D_STATE(ifp);\n\tstate->state = DHS_INIT;\n\tstate->reason = \"PREINIT\";\n\tstate->nakoff = 0;\n\tdhcp_set_leasefile(state->leasefile, sizeof(state->leasefile), AF_INET,\n\t    ifp);\n\n\tifo = ifp->options;\n\t/* We need to drop the leasefile so that dhcp_start\n\t * doesn't load it. */\n\tif (ifo->options & DHCPCD_REQUEST)\n\t\tdhcp_unlink(ifp->ctx, state->leasefile);\n\n\tfree(state->clientid);\n\tstate->clientid = NULL;\n\n\tif (ifo->options & DHCPCD_ANONYMOUS) {\n\t\t/* Removing the option could show that we want anonymous.\n\t\t * As such keep it as it's already in the hwaddr field. */\n\t\tgoto make_clientid;\n\t} else if (*ifo->clientid) {\n\t\tstate->clientid = malloc((size_t)(ifo->clientid[0] + 1));\n\t\tif (state->clientid == NULL)\n\t\t\tgoto eexit;\n\t\tmemcpy(state->clientid, ifo->clientid,\n\t\t    (size_t)(ifo->clientid[0]) + 1);\n\t} else if (ifo->options & DHCPCD_CLIENTID) {\n\t\tif (ifo->options & DHCPCD_DUID) {\n\t\t\tstate->clientid = malloc(ifp->ctx->duid_len + 6);\n\t\t\tif (state->clientid == NULL)\n\t\t\t\tgoto eexit;\n\t\t\tstate->clientid[0] = (uint8_t)(ifp->ctx->duid_len + 5);\n\t\t\tstate->clientid[1] = 255; /* RFC 4361 */\n\t\t\tmemcpy(state->clientid + 2, ifo->iaid, 4);\n\t\t\tmemcpy(state->clientid + 6, ifp->ctx->duid,\n\t\t\t    ifp->ctx->duid_len);\n\t\t} else {\n\t\tmake_clientid:\n\t\t\tlen = (uint8_t)(ifp->hwlen + 1);\n\t\t\tstate->clientid = malloc((size_t)len + 1);\n\t\t\tif (state->clientid == NULL)\n\t\t\t\tgoto eexit;\n\t\t\tstate->clientid[0] = len;\n\t\t\tstate->clientid[1] = (uint8_t)ifp->hwtype;\n\t\t\tmemcpy(state->clientid + 2, ifp->hwaddr, ifp->hwlen);\n\t\t}\n\t}\n\n\tif (ifo->options & DHCPCD_DUID)\n\t\t/* Don't bother logging as DUID and IAID are reported\n\t\t * at device start. */\n\t\treturn 0;\n\n\tif (ifo->options & DHCPCD_CLIENTID && state->clientid != NULL)\n\t\tlogdebugx(\"%s: using ClientID %s\", ifp->name,\n\t\t    hwaddr_ntoa(state->clientid + 1, state->clientid[0], buf,\n\t\t\tsizeof(buf)));\n\telse if (ifp->hwlen)\n\t\tlogdebugx(\"%s: using hwaddr %s\", ifp->name,\n\t\t    hwaddr_ntoa(ifp->hwaddr, ifp->hwlen, buf, sizeof(buf)));\n\treturn 0;\n\neexit:\n\tlogerr(__func__);\n\treturn -1;\n}\n\nstatic void\ndhcp_start1(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tstruct dhcpcd_ctx *ctx = ifp->ctx;\n\tstruct if_options *ifo = ifp->options;\n\tstruct dhcp_state *state;\n\tuint32_t l;\n\tint nolease;\n\n\tif (!(ifo->options & DHCPCD_IPV4))\n\t\treturn;\n\n\t/* Listen on *.*.*.*:bootpc so that the kernel never sends an\n\t * ICMP port unreachable message back to the DHCP server.\n\t * Only do this in manager mode so we don't swallow messages\n\t * for dhcpcd running on another interface. */\n\tif ((ctx->options & (DHCPCD_MANAGER | DHCPCD_PRIVSEP)) ==\n\t\tDHCPCD_MANAGER &&\n\t    ctx->udp_rfd == -1) {\n\t\tctx->udp_rfd = dhcp_openudp(NULL);\n\t\tif (ctx->udp_rfd == -1) {\n\t\t\tlogerr(__func__);\n\t\t\treturn;\n\t\t}\n\t\tif (eloop_event_add(ctx->eloop, ctx->udp_rfd, ELE_READ,\n\t\t\tdhcp_handleudp, ctx) == -1)\n\t\t\tlogerr(\"%s: eloop_event_add\", __func__);\n\t}\n\tif (!IN_PRIVSEP(ctx) && ctx->udp_wfd == -1) {\n\t\tctx->udp_wfd = xsocket(PF_INET, SOCK_RAW | SOCK_CXNB,\n\t\t    IPPROTO_UDP);\n\t\tif (ctx->udp_wfd == -1) {\n\t\t\tlogerr(__func__);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (dhcp_init(ifp) == -1) {\n\t\tlogerr(\"%s: dhcp_init\", ifp->name);\n\t\treturn;\n\t}\n\n\tstate = D_STATE(ifp);\n\tclock_gettime(CLOCK_MONOTONIC, &state->started);\n\tstate->interval = 0;\n\tfree(state->offer);\n\tstate->offer = NULL;\n\tstate->offer_len = 0;\n\n#ifdef ARPING\n\tif (ifo->arping_len && state->arping_index < ifo->arping_len) {\n\t\tdhcp_arping(ifp);\n\t\treturn;\n\t}\n#endif\n\n\tif (ifo->options & DHCPCD_STATIC) {\n\t\tdhcp_static(ifp);\n\t\treturn;\n\t}\n\n\tif (ifo->options & DHCPCD_INFORM) {\n\t\tdhcp_inform(ifp);\n\t\treturn;\n\t}\n\n\t/* We don't want to read the old lease if we NAK an old test */\n\tnolease = state->offer && ifp->ctx->options & DHCPCD_TEST;\n\tif (!nolease && ifo->options & DHCPCD_DHCP) {\n\t\tstate->offer_len = read_lease(ifp, &state->offer);\n\t\t/* Check the saved lease matches the type we want */\n\t\tif (state->offer) {\n#ifdef IN_IFF_DUPLICATED\n\t\t\tstruct in_addr addr;\n\t\t\tstruct ipv4_addr *ia;\n\n\t\t\taddr.s_addr = state->offer->yiaddr;\n\t\t\tia = ipv4_iffindaddr(ifp, &addr, NULL);\n#endif\n\n\t\t\tif ((!IS_DHCP(state->offer) &&\n\t\t\t\t!(ifo->options & DHCPCD_BOOTP)) ||\n#ifdef IN_IFF_DUPLICATED\n\t\t\t    (ia && ia->addr_flags & IN_IFF_DUPLICATED) ||\n#endif\n\t\t\t    (IS_DHCP(state->offer) &&\n\t\t\t\tifo->options & DHCPCD_BOOTP)) {\n\t\t\t\tfree(state->offer);\n\t\t\t\tstate->offer = NULL;\n\t\t\t\tstate->offer_len = 0;\n\t\t\t}\n\t\t}\n\t}\n\tif (state->offer) {\n\t\tstruct ipv4_addr *ia;\n\t\ttime_t mtime;\n\n\t\tget_lease(ifp, &state->lease, state->offer, state->offer_len);\n\t\tstate->lease.frominfo = 1;\n\t\tif (state->new == NULL &&\n\t\t    (ia = ipv4_iffindaddr(ifp, &state->lease.addr,\n\t\t\t &state->lease.mask)) != NULL) {\n\t\t\t/* We still have the IP address from the last lease.\n\t\t\t * Fake add the address and routes from it so the lease\n\t\t\t * can be cleaned up. */\n\t\t\tstate->new = malloc(state->offer_len);\n\t\t\tif (state->new) {\n\t\t\t\tmemcpy(state->new, state->offer,\n\t\t\t\t    state->offer_len);\n\t\t\t\tstate->new_len = state->offer_len;\n\t\t\t\tstate->addr = ia;\n\t\t\t\tstate->added |= STATE_ADDED | STATE_FAKE;\n\t\t\t\trt_build(ifp->ctx, AF_INET);\n\t\t\t} else\n\t\t\t\tlogerr(__func__);\n\t\t}\n\t\tif (!IS_DHCP(state->offer)) {\n\t\t\tfree(state->offer);\n\t\t\tstate->offer = NULL;\n\t\t\tstate->offer_len = 0;\n\t\t} else if (!(ifo->options & DHCPCD_LASTLEASE_EXTEND) &&\n\t\t    state->lease.leasetime != DHCP_INFINITE_LIFETIME &&\n\t\t    dhcp_filemtime(ifp->ctx, state->leasefile, &mtime) == 0) {\n\t\t\ttime_t now;\n\n\t\t\t/* Offset lease times and check expiry */\n\t\t\tnow = time(NULL);\n\t\t\tif (now == -1 ||\n\t\t\t    (time_t)state->lease.leasetime < now - mtime) {\n\t\t\t\tlogdebugx(\"%s: discarding expired lease\",\n\t\t\t\t    ifp->name);\n\t\t\t\tfree(state->offer);\n\t\t\t\tstate->offer = NULL;\n\t\t\t\tstate->offer_len = 0;\n\t\t\t\tstate->lease.addr.s_addr = 0;\n\t\t\t\t/* Technically we should discard the lease\n\t\t\t\t * as it's expired, just as DHCPv6 addresses\n\t\t\t\t * would be by the kernel.\n\t\t\t\t * However, this may violate POLA so\n\t\t\t\t * we currently leave it be.\n\t\t\t\t * If we get a totally different lease from\n\t\t\t\t * the DHCP server we'll drop it anyway, as\n\t\t\t\t * we will on any other event which would\n\t\t\t\t * trigger a lease drop.\n\t\t\t\t * This should only happen if dhcpcd stops\n\t\t\t\t * running and the lease expires before\n\t\t\t\t * dhcpcd starts again. */\n#if 0\n\t\t\t\tif (state->new)\n\t\t\t\t\tdhcp_drop(ifp, \"EXPIRE\");\n#endif\n\t\t\t} else {\n\t\t\t\tl = (uint32_t)(now - mtime);\n\t\t\t\tstate->lease.leasetime -= l;\n\t\t\t\tstate->lease.renewaltime -= l;\n\t\t\t\tstate->lease.rebindtime -= l;\n\t\t\t}\n\t\t}\n\t}\n\n#ifdef IPV4LL\n\tif (!(ifo->options & DHCPCD_DHCP)) {\n\t\tif (ifo->options & DHCPCD_IPV4LL)\n\t\t\tipv4ll_start(ifp);\n\t\treturn;\n\t}\n#endif\n\n\tif (state->offer == NULL || !IS_DHCP(state->offer) ||\n\t    ifo->options & DHCPCD_ANONYMOUS)\n\t\tdhcp_discover(ifp);\n\telse\n\t\tdhcp_reboot(ifp);\n}\n\nvoid\ndhcp_start(struct interface *ifp)\n{\n\tunsigned int delay;\n#ifdef ARPING\n\tconst struct dhcp_state *state;\n#endif\n\n\tif (!(ifp->options->options & DHCPCD_IPV4))\n\t\treturn;\n\n\t/* If we haven't been given a netmask for our requested address,\n\t * set it now. */\n\tif (ifp->options->req_addr.s_addr != INADDR_ANY &&\n\t    ifp->options->req_mask.s_addr == INADDR_ANY)\n\t\tifp->options->req_mask.s_addr = ipv4_getnetmask(\n\t\t    ifp->options->req_addr.s_addr);\n\n\t/* If we haven't specified a ClientID and our hardware address\n\t * length is greater than BOOTP CHADDR then we enforce a ClientID\n\t * of the hardware address type and the hardware address.\n\t * If there is no hardware address and no ClientID set,\n\t * force a DUID based ClientID. */\n\tif (ifp->hwlen > 16)\n\t\tifp->options->options |= DHCPCD_CLIENTID;\n\telse if (ifp->hwlen == 0 && !(ifp->options->options & DHCPCD_CLIENTID))\n\t\tifp->options->options |= DHCPCD_CLIENTID | DHCPCD_DUID;\n\n\t/* Firewire and InfiniBand interfaces require ClientID and\n\t * the broadcast option being set. */\n\tswitch (ifp->hwtype) {\n\tcase ARPHRD_IEEE1394: /* FALLTHROUGH */\n\tcase ARPHRD_INFINIBAND:\n\t\tifp->options->options |= DHCPCD_CLIENTID | DHCPCD_BROADCAST;\n\t\tbreak;\n\t}\n\n\t/* If we violate RFC2131 section 3.7 then require ARP\n\t * to detect if any other client wants our address. */\n\tif (ifp->options->options & DHCPCD_LASTLEASE_EXTEND)\n\t\tifp->options->options |= DHCPCD_ARP;\n\n\t/* No point in delaying a static configuration */\n\tif (ifp->options->options & DHCPCD_STATIC ||\n\t    !(ifp->options->options & DHCPCD_INITIAL_DELAY)) {\n\t\tdhcp_start1(ifp);\n\t\treturn;\n\t}\n\n#ifdef ARPING\n\t/* If we have arpinged then we have already delayed. */\n\tstate = D_CSTATE(ifp);\n\tif (state != NULL && state->arping_index != -1) {\n\t\tdhcp_start1(ifp);\n\t\treturn;\n\t}\n#endif\n\tdelay = MSEC_PER_SEC +\n\t    (arc4random_uniform(MSEC_PER_SEC * 2) - MSEC_PER_SEC);\n\tlogdebugx(\"%s: delaying IPv4 for %0.1f seconds\", ifp->name,\n\t    (float)delay / MSEC_PER_SEC);\n\n\teloop_timeout_add_msec(ifp->ctx->eloop, delay, dhcp_start1, ifp);\n}\n\nvoid\ndhcp_abort(struct interface *ifp)\n{\n\tstruct dhcp_state *state;\n\n\tstate = D_STATE(ifp);\n#ifdef ARPING\n\tif (state != NULL)\n\t\tstate->arping_index = -1;\n#endif\n\n\teloop_timeout_delete(ifp->ctx->eloop, dhcp_start1, ifp);\n\n\tif (state != NULL && state->added)\n\t\trt_build(ifp->ctx, AF_INET);\n}\n\nstruct ipv4_addr *\ndhcp_handleifa(int cmd, struct ipv4_addr *ia, pid_t pid)\n{\n\tstruct interface *ifp;\n\tstruct dhcp_state *state;\n\tstruct if_options *ifo;\n\tuint8_t i;\n\n\tifp = ia->iface;\n\tstate = D_STATE(ifp);\n\tif (state == NULL || state->state == DHS_NONE)\n\t\treturn ia;\n\n\tif (cmd == RTM_DELADDR) {\n\t\tif (state->addr == ia) {\n\t\t\tloginfox(\"%s: pid %d deleted IP address %s\", ifp->name,\n\t\t\t    (int)pid, ia->saddr);\n\t\t\tdhcp_close(ifp);\n\t\t\tstate->addr = NULL;\n\t\t\t/* Don't clear the added state as we need\n\t\t\t * to drop the lease. */\n\t\t\tdhcp_drop(ifp, \"EXPIRE\");\n\t\t\tdhcp_start1(ifp);\n\t\t\treturn ia;\n\t\t}\n\t}\n\n\tif (cmd != RTM_NEWADDR)\n\t\treturn ia;\n\n#ifdef IN_IFF_NOTUSEABLE\n\tif (!(ia->addr_flags & IN_IFF_NOTUSEABLE))\n\t\tdhcp_finish_dad(ifp, &ia->addr);\n\telse if (ia->addr_flags & IN_IFF_DUPLICATED)\n\t\treturn dhcp_addr_duplicated(ifp, &ia->addr) ? NULL : ia;\n#endif\n\n\tifo = ifp->options;\n\n\tif (!(ifp->ctx->options & (DHCPCD_MANAGER | DHCPCD_CONFIGURE)) &&\n\t    IN_ARE_ADDR_EQUAL(&state->lease.addr, &ia->addr)) {\n\t\tuint8_t old_state = state->added;\n\n\t\tstate->addr = ia;\n\t\tstate->added = STATE_ADDED;\n\t\tdhcp_bound(ifp, old_state);\n\t}\n\n\t/* If we have requested a specific address, return now.\n\t * The below code is only for when inform or static has been\n\t * requested without a specific address. */\n\tif (ifo->req_addr.s_addr != INADDR_ANY)\n\t\treturn ia;\n\n\t/* Only inform if we are NOT in the inform state or bound. */\n\tif (ifo->options & DHCPCD_INFORM) {\n\t\tif (state->state != DHS_INFORM && state->state != DHS_BOUND)\n\t\t\tdhcp_inform(ifp);\n\t\treturn ia;\n\t}\n\n\t/* Static and inform are mutually exclusive. If not static, return. */\n\tif (!(ifo->options & DHCPCD_STATIC))\n\t\treturn ia;\n\n\tfree(state->old);\n\tstate->old = state->new;\n\tstate->new_len = dhcp_message_new(&state->new, &ia->addr, &ia->mask);\n\tif (state->new == NULL)\n\t\treturn ia;\n\n\tif (ifp->flags & IFF_POINTOPOINT) {\n\t\tfor (i = 1; i < 255; i++)\n\t\t\tif (i != DHO_ROUTER && has_option_mask(ifo->dstmask, i))\n\t\t\t\tdhcp_message_add_addr(state->new, i, ia->brd);\n\t}\n\n\tstate->reason = \"STATIC\";\n\trt_build(ifp->ctx, AF_INET);\n\tscript_runreason(ifp, state->reason);\n\n\treturn ia;\n}\n\n#ifndef SMALL\nint\ndhcp_dump(struct interface *ifp)\n{\n\tstruct dhcp_state *state;\n\n\tifp->if_data[IF_DATA_DHCP] = state = calloc(1, sizeof(*state));\n\tif (state == NULL) {\n\t\tlogerr(__func__);\n\t\treturn -1;\n\t}\n\tstate->new_len = read_lease(ifp, &state->new);\n\tif (state->new == NULL) {\n\t\tlogerr(\"read_lease\");\n\t\treturn -1;\n\t}\n\tstate->reason = \"DUMP\";\n\treturn script_runreason(ifp, state->reason);\n}\n#endif\n"
  },
  {
    "path": "src/dhcp.h",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef DHCP_H\n#define DHCP_H\n\n#include <netinet/in.h>\n#include <netinet/ip.h>\n\n#include <arpa/inet.h>\n#define __FAVOR_BSD /* Nasty glibc hack so we can use BSD semantics for UDP */\n#include <netinet/udp.h>\n#undef __FAVOR_BSD\n\n#include <limits.h>\n#include <stdint.h>\n\n#include \"arp.h\"\n#include \"auth.h\"\n#include \"bpf.h\"\n#include \"dhcp-common.h\"\n\n/* UDP port numbers for BOOTP */\n#define BOOTPS\t       67\n#define BOOTPC\t       68\n\n#define MAGIC_COOKIE   0x63825363\n#define BROADCAST_FLAG 0x8000\n\n/* BOOTP message OP code */\n#define BOOTREQUEST 1\n#define BOOTREPLY   2\n\n/* DHCP message type */\n#define DHCP_DISCOVER\t1\n#define DHCP_OFFER\t2\n#define DHCP_REQUEST\t3\n#define DHCP_DECLINE\t4\n#define DHCP_ACK\t5\n#define DHCP_NAK\t6\n#define DHCP_RELEASE\t7\n#define DHCP_INFORM\t8\n#define DHCP_FORCERENEW 9\n\n/* Constants taken from RFC 2131. */\n#define T1\t      0.5\n#define T2\t      0.875\n#define DHCP_BASE     4\n#define DHCP_MAX      64\n#define DHCP_RAND_MIN -1\n#define DHCP_RAND_MAX 1\n\n#ifdef RFC2131_STRICT\n/* Be strictly conformant for section 4.1.1 */\n#define DHCP_MIN_DELAY 1\n#define DHCP_MAX_DELAY 10\n#else\n/* or mirror the more modern IPv6RS and DHCPv6 delays */\n#define DHCP_MIN_DELAY 0\n#define DHCP_MAX_DELAY 1\n#endif\n\n/* DHCP options */\nenum DHO {\n\tDHO_PAD = 0,\n\tDHO_SUBNETMASK = 1,\n\tDHO_ROUTER = 3,\n\tDHO_DNSSERVER = 6,\n\tDHO_HOSTNAME = 12,\n\tDHO_DNSDOMAIN = 15,\n\tDHO_MTU = 26,\n\tDHO_BROADCAST = 28,\n\tDHO_STATICROUTE = 33,\n\tDHO_NISDOMAIN = 40,\n\tDHO_NISSERVER = 41,\n\tDHO_NTPSERVER = 42,\n\tDHO_VENDOR = 43,\n\tDHO_IPADDRESS = 50,\n\tDHO_LEASETIME = 51,\n\tDHO_OPTSOVERLOADED = 52,\n\tDHO_MESSAGETYPE = 53,\n\tDHO_SERVERID = 54,\n\tDHO_PARAMETERREQUESTLIST = 55,\n\tDHO_MESSAGE = 56,\n\tDHO_MAXMESSAGESIZE = 57,\n\tDHO_RENEWALTIME = 58,\n\tDHO_REBINDTIME = 59,\n\tDHO_VENDORCLASSID = 60,\n\tDHO_CLIENTID = 61,\n\tDHO_USERCLASS = 77,   /* RFC 3004 */\n\tDHO_RAPIDCOMMIT = 80, /* RFC 4039 */\n\tDHO_FQDN = 81,\n\tDHO_AUTHENTICATION = 90,       /* RFC 3118 */\n\tDHO_IPV6_PREFERRED_ONLY = 108, /* RFC 8925 */\n\tDHO_AUTOCONFIGURE = 116,       /* RFC 2563 */\n\tDHO_DNSSEARCH = 119,\t       /* RFC 3397 */\n\tDHO_CSR = 121,\t\t       /* RFC 3442 */\n\tDHO_VIVCO = 124,\t       /* RFC 3925 */\n\tDHO_VIVSO = 125,\t       /* RFC 3925 */\n\tDHO_FORCERENEW_NONCE = 145,    /* RFC 6704 */\n\tDHO_MUDURL = 161,\t       /* draft-ietf-opsawg-mud */\n\tDHO_SIXRD = 212,\t       /* RFC 5969 */\n\tDHO_MSCSR = 249,\t       /* MS code for RFC 3442 */\n\tDHO_END = 255\n};\n\n/* FQDN values - lsnybble used in flags\n * hsnybble to create order\n * and to allow 0x00 to mean disable\n */\nenum FQDN {\n\tFQDN_DISABLE = 0x00,\n\tFQDN_NONE = 0x18,\n\tFQDN_PTR = 0x20,\n\tFQDN_BOTH = 0x31\n};\n\n#define MIN_V6ONLY_WAIT 300 /* seconds, RFC 8925 */\n\n/* Sizes for BOOTP options */\n#define BOOTP_CHADDR_LEN 16\n#define BOOTP_SNAME_LEN\t 64\n#define BOOTP_FILE_LEN\t 128\n#define BOOTP_VEND_LEN\t 64\n\n/* DHCP is basically an extension to BOOTP */\nstruct bootp {\n\tuint8_t op;\t /* message type */\n\tuint8_t htype;\t /* hardware address type */\n\tuint8_t hlen;\t /* hardware address length */\n\tuint8_t hops;\t /* should be zero in client message */\n\tuint32_t xid;\t /* transaction id */\n\tuint16_t secs;\t /* elapsed time in sec. from boot */\n\tuint16_t flags;\t /* such as broadcast flag */\n\tuint32_t ciaddr; /* (previously allocated) client IP */\n\tuint32_t yiaddr; /* 'your' client IP address */\n\tuint32_t siaddr; /* should be zero in client's messages */\n\tuint32_t giaddr; /* should be zero in client's messages */\n\tuint8_t chaddr[BOOTP_CHADDR_LEN]; /* client's hardware address */\n\tuint8_t sname[BOOTP_SNAME_LEN];\t  /* server host name */\n\tuint8_t file[BOOTP_FILE_LEN];\t  /* boot file name */\n\tuint8_t vend[BOOTP_VEND_LEN];\t  /* vendor specific area */\n\t/* DHCP allows a variable length vendor area */\n};\n\n#define DHCP_MIN_LEN (offsetof(struct bootp, vend) + 4)\n\nstruct bootp_pkt {\n\tstruct ip ip;\n\tstruct udphdr udp;\n\tstruct bootp bootp;\n};\n\nstruct dhcp_lease {\n\tstruct in_addr addr;\n\tstruct in_addr mask;\n\tstruct in_addr brd;\n\tuint32_t leasetime;\n\tuint32_t renewaltime;\n\tuint32_t rebindtime;\n\tstruct in_addr server;\n\tuint8_t frominfo;\n\tuint32_t cookie;\n};\n\n#ifndef DHCP_INFINITE_LIFETIME\n#define DHCP_INFINITE_LIFETIME (~0U)\n#endif\n\nenum DHS {\n\tDHS_NONE,\n\tDHS_INIT,\n\tDHS_DISCOVER,\n\tDHS_REQUEST,\n\tDHS_PROBE,\n\tDHS_BOUND,\n\tDHS_RENEW,\n\tDHS_REBIND,\n\tDHS_REBOOT,\n\tDHS_INFORM,\n\tDHS_RENEW_REQUESTED,\n\tDHS_RELEASE\n};\n\nstruct dhcp_state {\n\tenum DHS state;\n\tstruct bootp *sent;\n\tsize_t sent_len;\n\tstruct bootp *offer;\n\tsize_t offer_len;\n\tstruct bootp *new;\n\tsize_t new_len;\n\tstruct bootp *old;\n\tsize_t old_len;\n\tstruct dhcp_lease lease;\n\tconst char *reason;\n\tunsigned int interval;\n\tunsigned int nakoff;\n\tuint32_t xid;\n\tint socket;\n\n\tstruct bpf *bpf;\n\tint udp_rfd;\n\tstruct ipv4_addr *addr;\n\tuint8_t added;\n\n\tchar leasefile[sizeof(LEASEFILE) + IF_NAMESIZE + (IF_SSIDLEN * 4)];\n\tstruct timespec started;\n\tunsigned char *clientid;\n\tstruct authstate auth;\n#ifdef ARPING\n\tssize_t arping_index;\n#endif\n};\n\n#ifdef INET\n#define D_STATE(ifp)  ((struct dhcp_state *)(ifp)->if_data[IF_DATA_DHCP])\n#define D_CSTATE(ifp) ((const struct dhcp_state *)(ifp)->if_data[IF_DATA_DHCP])\n#define D_STATE_RUNNING(ifp) \\\n\t(D_CSTATE((ifp)) && D_CSTATE((ifp))->new && D_CSTATE((ifp))->reason)\n\n#define IS_DHCP(b)                                       \\\n\t((b)->vend[0] == 0x63 && (b)->vend[1] == 0x82 && \\\n\t    (b)->vend[2] == 0x53 && (b)->vend[3] == 0x63)\n\n#include \"dhcpcd.h\"\n#include \"if-options.h\"\n\nssize_t print_rfc3361(FILE *, const uint8_t *, size_t);\nssize_t print_rfc3442(FILE *, const uint8_t *, size_t);\n\nint dhcp_openudp(struct in_addr *);\nvoid dhcp_packet(struct interface *, uint8_t *, size_t, unsigned int);\nvoid dhcp_recvmsg(struct dhcpcd_ctx *, struct msghdr *);\nvoid dhcp_printoptions(const struct dhcpcd_ctx *, const struct dhcp_opt *,\n    size_t);\nuint16_t dhcp_get_mtu(const struct interface *);\nint dhcp_get_routes(rb_tree_t *, struct interface *);\nssize_t dhcp_env(FILE *, const char *, const struct interface *,\n    const struct bootp *, size_t);\n\nstruct ipv4_addr *dhcp_handleifa(int, struct ipv4_addr *, pid_t pid);\nvoid dhcp_drop(struct interface *, const char *);\nvoid dhcp_start(struct interface *);\nvoid dhcp_abort(struct interface *);\nvoid dhcp_discover(void *);\nvoid dhcp_inform(struct interface *);\nvoid dhcp_renew(struct interface *);\nvoid dhcp_bind(struct interface *);\nvoid dhcp_reboot_newopts(struct interface *, unsigned long long);\nvoid dhcp_close(struct interface *);\nvoid dhcp_free(struct interface *);\nint dhcp_dump(struct interface *);\n#endif /* INET */\n\n#endif /* DHCP_H */\n"
  },
  {
    "path": "src/dhcp6.c",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/types.h>\n#include <sys/utsname.h>\n\n#include <netinet/in.h>\n#include <netinet/ip6.h>\n\n#include <assert.h>\n#include <ctype.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <inttypes.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdlib.h>\n#include <string.h>\n#include <syslog.h>\n#include <unistd.h>\n\n#define ELOOP_QUEUE ELOOP_DHCP6\n#include \"common.h\"\n#include \"config.h\"\n#include \"dhcp.h\"\n#include \"dhcp6.h\"\n#include \"duid.h\"\n#include \"eloop.h\"\n#include \"if-options.h\"\n#include \"if.h\"\n#include \"ipv6nd.h\"\n#include \"logerr.h\"\n#include \"privsep.h\"\n#include \"script.h\"\n\n#ifdef HAVE_SYS_BITOPS_H\n#include <sys/bitops.h>\n#else\n#include \"compat/bitops.h\"\n#endif\n\n/* DHCPCD Project has been assigned an IANA PEN of 40712 */\n#define DHCPCD_IANA_PEN 40712\n\n/* Unsure if I want this */\n// #define VENDOR_SPLIT\n\n/* Support older systems with different defines */\n#if !defined(IPV6_RECVPKTINFO) && defined(IPV6_PKTINFO)\n#define IPV6_RECVPKTINFO IPV6_PKTINFO\n#endif\n\n#ifdef DHCP6\n\n/* Assert the correct structure size for on wire */\nstruct dhcp6_message {\n\tuint8_t type;\n\tuint8_t xid[3];\n\t/* followed by options */\n};\n__CTASSERT(sizeof(struct dhcp6_message) == 4);\n\nstruct dhcp6_option {\n\tuint16_t code;\n\tuint16_t len;\n\t/* followed by data */\n};\n__CTASSERT(sizeof(struct dhcp6_option) == 4);\n\nstruct dhcp6_ia_na {\n\tuint8_t iaid[4];\n\tuint32_t t1;\n\tuint32_t t2;\n};\n__CTASSERT(sizeof(struct dhcp6_ia_na) == 12);\n\nstruct dhcp6_ia_ta {\n\tuint8_t iaid[4];\n};\n__CTASSERT(sizeof(struct dhcp6_ia_ta) == 4);\n\nstruct dhcp6_ia_addr {\n\tstruct in6_addr addr;\n\tuint32_t pltime;\n\tuint32_t vltime;\n};\n__CTASSERT(sizeof(struct dhcp6_ia_addr) == 16 + 8);\n\n/* Some compilers do not support packed structures.\n * We manually decode this. */\n#if 0\nstruct dhcp6_pd_addr {\n\tuint32_t pltime;\n\tuint32_t vltime;\n\tuint8_t prefix_len;\n\tstruct in6_addr prefix;\n} __packed;\n__CTASSERT(sizeof(struct dhcp6_pd_addr) == 8 + 1 + 16);\n#endif\n\n#define DHCP6_PD_ADDR_SIZE   (8 + 1 + 16)\n#define DHCP6_PD_ADDR_PLTIME 0\n#define DHCP6_PD_ADDR_VLTIME 4\n#define DHCP6_PD_ADDR_PLEN   8\n#define DHCP6_PD_ADDR_PREFIX 9\n\nstruct dhcp6_op {\n\tuint16_t type;\n\tconst char *name;\n};\n\nstatic const struct dhcp6_op dhcp6_ops[] = { { DHCP6_SOLICIT, \"SOLICIT6\" },\n\t{ DHCP6_ADVERTISE, \"ADVERTISE6\" }, { DHCP6_REQUEST, \"REQUEST6\" },\n\t{ DHCP6_REPLY, \"REPLY6\" }, { DHCP6_RENEW, \"RENEW6\" },\n\t{ DHCP6_REBIND, \"REBIND6\" }, { DHCP6_CONFIRM, \"CONFIRM6\" },\n\t{ DHCP6_INFORMATION_REQ, \"INFORM6\" }, { DHCP6_RELEASE, \"RELEASE6\" },\n\t{ DHCP6_RECONFIGURE, \"RECONFIGURE6\" }, { DHCP6_DECLINE, \"DECLINE6\" },\n\t{ 0, NULL } };\n\nstruct dhcp_compat {\n\tuint8_t dhcp_opt;\n\tuint16_t dhcp6_opt;\n};\n\n/*\n * RFC 5908 deprecates OPTION_SNTP_SERVERS.\n * But we can support both as the hook scripts will uniqify the\n * results if the server returns both options.\n */\nstatic const struct dhcp_compat dhcp_compats[] = { { DHO_DNSSERVER,\n\t\t\t\t\t\t       D6_OPTION_DNS_SERVERS },\n\t{ DHO_HOSTNAME, D6_OPTION_FQDN }, { DHO_DNSDOMAIN, D6_OPTION_FQDN },\n\t{ DHO_NISSERVER, D6_OPTION_NIS_SERVERS },\n\t{ DHO_NTPSERVER, D6_OPTION_SNTP_SERVERS },\n\t{ DHO_NTPSERVER, D6_OPTION_NTP_SERVER },\n\t{ DHO_RAPIDCOMMIT, D6_OPTION_RAPID_COMMIT },\n\t{ DHO_FQDN, D6_OPTION_FQDN }, { DHO_VIVCO, D6_OPTION_VENDOR_CLASS },\n\t{ DHO_VIVSO, D6_OPTION_VENDOR_OPTS },\n\t{ DHO_DNSSEARCH, D6_OPTION_DOMAIN_LIST }, { 0, 0 } };\n\nstatic const char *const dhcp6_statuses[] = { \"Success\", \"Unspecified Failure\",\n\t\"No Addresses Available\", \"No Binding\", \"Not On Link\", \"Use Multicast\",\n\t\"No Prefix Available\" };\n\nstatic void dhcp6_bind(struct interface *, const char *, const char *);\nstatic void dhcp6_failinform(void *);\nstatic void dhcp6_startrebind(void *arg);\nstatic void dhcp6_recvaddr(void *, unsigned short);\nstatic void dhcp6_startdecline(struct interface *);\nstatic void dhcp6_startrequest(struct interface *);\n\n#ifdef SMALL\n#define dhcp6_hasprefixdelegation(a) (0)\n#else\nstatic int dhcp6_hasprefixdelegation(struct interface *);\n#endif\n\n#define DECLINE_IA(ia)                                                  \\\n\t((ia)->addr_flags & IN6_IFF_DUPLICATED && (ia)->ia_type != 0 && \\\n\t    (ia)->ia_type != D6_OPTION_IA_PD &&                         \\\n\t    !((ia)->flags & IPV6_AF_STALE) && (ia)->prefix_vltime != 0)\n\n/* Gets a pointer to the length part of the option to fill it\n * in later. */\n#define NEXTLEN(p) ((p) + offsetof(struct dhcp6_option, len))\n\nvoid\ndhcp6_printoptions(const struct dhcpcd_ctx *ctx, const struct dhcp_opt *opts,\n    size_t opts_len)\n{\n\tsize_t i, j;\n\tconst struct dhcp_opt *opt, *opt2;\n\tint cols;\n\n\tfor (i = 0, opt = ctx->dhcp6_opts; i < ctx->dhcp6_opts_len;\n\t    i++, opt++) {\n\t\tfor (j = 0, opt2 = opts; j < opts_len; j++, opt2++)\n\t\t\tif (opt2->option == opt->option)\n\t\t\t\tbreak;\n\t\tif (j == opts_len) {\n\t\t\tcols = printf(\"%05d %s\", opt->option, opt->var);\n\t\t\tdhcp_print_option_encoding(opt, cols);\n\t\t}\n\t}\n\tfor (i = 0, opt = opts; i < opts_len; i++, opt++) {\n\t\tcols = printf(\"%05d %s\", opt->option, opt->var);\n\t\tdhcp_print_option_encoding(opt, cols);\n\t}\n}\n\nstatic size_t\ndhcp6_makeuser(void *data, const struct interface *ifp)\n{\n\tconst struct if_options *ifo = ifp->options;\n\tstruct dhcp6_option o;\n\tuint8_t *p;\n\tconst uint8_t *up, *ue;\n\tuint16_t ulen, unlen;\n\tsize_t olen;\n\n\t/* Convert the DHCPv4 user class option to DHCPv6 */\n\tup = ifo->userclass;\n\tulen = *up++;\n\tif (ulen == 0)\n\t\treturn 0;\n\n\tp = data;\n\tolen = 0;\n\tif (p != NULL)\n\t\tp += sizeof(o);\n\n\tue = up + ulen;\n\tfor (; up < ue; up += ulen) {\n\t\tulen = *up++;\n\t\tolen += sizeof(ulen) + ulen;\n\t\tif (data == NULL)\n\t\t\tcontinue;\n\t\tunlen = htons(ulen);\n\t\tmemcpy(p, &unlen, sizeof(unlen));\n\t\tp += sizeof(unlen);\n\t\tmemcpy(p, up, ulen);\n\t\tp += ulen;\n\t}\n\tif (data != NULL) {\n\t\to.code = htons(D6_OPTION_USER_CLASS);\n\t\to.len = htons((uint16_t)olen);\n\t\tmemcpy(data, &o, sizeof(o));\n\t}\n\n\treturn sizeof(o) + olen;\n}\n\n#ifndef SMALL\n/* DHCPv6 Option 16 (Vendor Class Option) */\nstatic size_t\ndhcp6_makevendor(void *data, const struct interface *ifp)\n{\n\tconst struct if_options *ifo;\n\tsize_t len = 0, optlen, vlen, i;\n\tuint8_t *p;\n\tconst struct vivco *vivco;\n\tstruct dhcp6_option o;\n\n\tifo = ifp->options;\n\tif (ifo->vivco_len > 0) {\n\t\tfor (i = 0, vivco = ifo->vivco; i < ifo->vivco_len;\n\t\t    i++, vivco++)\n\t\t\tlen += sizeof(o) + sizeof(uint32_t) + sizeof(uint16_t) +\n\t\t\t    vivco->len;\n\t} else if (ifo->vendorclassid[0] != '\\0') {\n\t\t/* dhcpcd owns DHCPCD_IANA_PEN.\n\t\t * If you need your own string, get your own IANA PEN. */\n\t\tvlen = strlen(ifp->ctx->vendor);\n\t\tlen += sizeof(o) + sizeof(uint32_t) + sizeof(uint16_t) + vlen;\n\t} else\n\t\treturn 0;\n\n\tif (len > UINT16_MAX) {\n\t\tlogerrx(\"%s: DHCPv6 Vendor Class too big\", ifp->name);\n\t\treturn 0;\n\t}\n\n\tif (data != NULL) {\n\t\tuint32_t pen;\n\t\tuint16_t hvlen;\n\n\t\tp = data;\n\n\t\tif (ifo->vivco_len > 0) {\n\t\t\tfor (i = 0, vivco = ifo->vivco; i < ifo->vivco_len;\n\t\t\t    i++, vivco++) {\n\t\t\t\toptlen = sizeof(uint32_t) + sizeof(uint16_t) +\n\t\t\t\t    vivco->len;\n\t\t\t\to.code = htons(D6_OPTION_VENDOR_CLASS);\n\t\t\t\to.len = htons((uint16_t)optlen);\n\t\t\t\tmemcpy(p, &o, sizeof(o));\n\t\t\t\tp += sizeof(o);\n\t\t\t\tpen = htonl(vivco->en);\n\t\t\t\tmemcpy(p, &pen, sizeof(pen));\n\t\t\t\tp += sizeof(pen);\n\t\t\t\thvlen = htons((uint16_t)vivco->len);\n\t\t\t\tmemcpy(p, &hvlen, sizeof(hvlen));\n\t\t\t\tp += sizeof(hvlen);\n\t\t\t\tmemcpy(p, vivco->data, vivco->len);\n\t\t\t\tp += vivco->len;\n\t\t\t}\n\t\t} else if (ifo->vendorclassid[0] != '\\0') {\n\t\t\toptlen = sizeof(uint32_t) + sizeof(uint16_t) + vlen;\n\t\t\to.code = htons(D6_OPTION_VENDOR_CLASS);\n\t\t\to.len = htons((uint16_t)optlen);\n\t\t\tmemcpy(p, &o, sizeof(o));\n\t\t\tp += sizeof(o);\n\t\t\tpen = htonl(DHCPCD_IANA_PEN);\n\t\t\tmemcpy(p, &pen, sizeof(pen));\n\t\t\tp += sizeof(pen);\n\t\t\thvlen = htons((uint16_t)vlen);\n\t\t\tmemcpy(p, &hvlen, sizeof(hvlen));\n\t\t\tp += sizeof(hvlen);\n\t\t\tmemcpy(p, ifp->ctx->vendor, vlen);\n\t\t}\n\t}\n\treturn len;\n}\n\n/* DHCPv6 Option 17 (Vendor-Specific Information Option) */\nstatic size_t\ndhcp6_makevendoropts(void *data, const struct interface *ifp)\n{\n\tuint8_t *p = data, *olenp;\n\tconst struct if_options *ifo = ifp->options;\n\tsize_t len = 0, olen;\n\tconst struct vsio *vsio, *vsio_endp = ifo->vsio6 + ifo->vsio6_len;\n\tconst struct vsio_so *so, *so_endp;\n\tstruct dhcp6_option o;\n\tuint32_t en;\n\tuint16_t opt, slen;\n\n\tfor (vsio = ifo->vsio6; vsio != vsio_endp; ++vsio) {\n\t\tif (vsio->so_len == 0)\n\t\t\tcontinue;\n\n\t\tif (p != NULL) {\n\t\t\tolenp = NEXTLEN(p);\n\t\t\to.code = htons(D6_OPTION_VENDOR_OPTS);\n\t\t\to.len = 0;\n\t\t\tmemcpy(p, &o, sizeof(o));\n\t\t\tp += sizeof(o);\n\n\t\t\ten = htonl(vsio->en);\n\t\t\tmemcpy(p, &en, sizeof(en));\n\t\t\tp += sizeof(en);\n\t\t} else\n\t\t\tolenp = NULL;\n\n\t\tolen = sizeof(en);\n\n\t\tso_endp = vsio->so + vsio->so_len;\n\t\tfor (so = vsio->so; so != so_endp; so++) {\n\t\t\tif (olen + sizeof(opt) + sizeof(slen) + so->len >\n\t\t\t    UINT16_MAX) {\n\t\t\t\tlogerrx(\"%s: option too big\", __func__);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (p != NULL) {\n\t\t\t\topt = htons(so->opt);\n\t\t\t\tmemcpy(p, &opt, sizeof(opt));\n\t\t\t\tp += sizeof(opt);\n\t\t\t\tslen = htons(so->len);\n\t\t\t\tmemcpy(p, &slen, sizeof(slen));\n\t\t\t\tp += sizeof(slen);\n\t\t\t\tmemcpy(p, so->data, so->len);\n\t\t\t\tp += so->len;\n\t\t\t}\n\n\t\t\tolen += sizeof(opt) + sizeof(slen) + so->len;\n\t\t}\n\n\t\tif (olenp != NULL) {\n\t\t\tslen = htons((uint16_t)olen);\n\t\t\tmemcpy(olenp, &slen, sizeof(slen));\n\t\t}\n\n\t\tlen += sizeof(o) + olen;\n\t}\n\n\treturn len;\n}\n#endif\n\nstatic void *\ndhcp6_findoption(void *data, size_t data_len, uint16_t code, uint16_t *len)\n{\n\tuint8_t *d;\n\tstruct dhcp6_option o;\n\n\tcode = htons(code);\n\tfor (d = data; data_len != 0; d += o.len, data_len -= o.len) {\n\t\tif (data_len < sizeof(o)) {\n\t\t\terrno = EINVAL;\n\t\t\treturn NULL;\n\t\t}\n\t\tmemcpy(&o, d, sizeof(o));\n\t\td += sizeof(o);\n\t\tdata_len -= sizeof(o);\n\t\to.len = htons(o.len);\n\t\tif (data_len < o.len) {\n\t\t\terrno = EINVAL;\n\t\t\treturn NULL;\n\t\t}\n\t\tif (o.code == code) {\n\t\t\tif (len != NULL)\n\t\t\t\t*len = o.len;\n\t\t\treturn d;\n\t\t}\n\t}\n\n\terrno = ENOENT;\n\treturn NULL;\n}\n\nstatic void *\ndhcp6_findmoption(void *data, size_t data_len, uint16_t code, uint16_t *len)\n{\n\tuint8_t *d;\n\n\tif (data_len < sizeof(struct dhcp6_message)) {\n\t\terrno = EINVAL;\n\t\treturn false;\n\t}\n\td = data;\n\td += sizeof(struct dhcp6_message);\n\tdata_len -= sizeof(struct dhcp6_message);\n\treturn dhcp6_findoption(d, data_len, code, len);\n}\n\nstatic const uint8_t *\ndhcp6_getoption(struct dhcpcd_ctx *ctx, size_t *os, unsigned int *code,\n    size_t *len, const uint8_t *od, size_t ol, struct dhcp_opt **oopt)\n{\n\tstruct dhcp6_option o;\n\tsize_t i;\n\tstruct dhcp_opt *opt;\n\n\tif (od != NULL) {\n\t\t*os = sizeof(o);\n\t\tif (ol < *os) {\n\t\t\terrno = EINVAL;\n\t\t\treturn NULL;\n\t\t}\n\t\tmemcpy(&o, od, sizeof(o));\n\t\t*len = ntohs(o.len);\n\t\tif (*len > ol - *os) {\n\t\t\terrno = ERANGE;\n\t\t\treturn NULL;\n\t\t}\n\t\t*code = ntohs(o.code);\n\t}\n\n\t*oopt = NULL;\n\tfor (i = 0, opt = ctx->dhcp6_opts; i < ctx->dhcp6_opts_len;\n\t    i++, opt++) {\n\t\tif (opt->option == *code) {\n\t\t\t*oopt = opt;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (od != NULL)\n\t\treturn od + sizeof(o);\n\treturn NULL;\n}\n\nstatic bool\ndhcp6_updateelapsed(struct interface *ifp, struct dhcp6_message *m, size_t len)\n{\n\tuint8_t *opt;\n\tuint16_t opt_len;\n\tstruct dhcp6_state *state;\n\tstruct timespec tv;\n\tunsigned long long hsec;\n\tuint16_t sec;\n\n\topt = dhcp6_findmoption(m, len, D6_OPTION_ELAPSED, &opt_len);\n\tif (opt == NULL)\n\t\treturn false;\n\tif (opt_len != sizeof(sec)) {\n\t\terrno = EINVAL;\n\t\treturn false;\n\t}\n\n\tstate = D6_STATE(ifp);\n\tclock_gettime(CLOCK_MONOTONIC, &tv);\n\tif (state->RTC == 0) {\n\t\t/* An RTC of zero means we're the first message\n\t\t * out of the door, so the elapsed time is zero. */\n\t\tstate->started = tv;\n\t\thsec = 0;\n\t} else {\n\t\tunsigned long long secs;\n\t\tunsigned int nsecs;\n\n\t\tsecs = eloop_timespec_diff(&tv, &state->started, &nsecs);\n\t\t/* Elapsed time is measured in centiseconds.\n\t\t * We need to be sure it will not potentially overflow. */\n\t\tif (secs >= (UINT16_MAX / CSEC_PER_SEC) + 1)\n\t\t\thsec = UINT16_MAX;\n\t\telse {\n\t\t\thsec = (secs * CSEC_PER_SEC) + (nsecs / NSEC_PER_CSEC);\n\t\t\tif (hsec > UINT16_MAX)\n\t\t\t\thsec = UINT16_MAX;\n\t\t}\n\t}\n\tsec = htons((uint16_t)hsec);\n\tmemcpy(opt, &sec, sizeof(sec));\n\treturn true;\n}\n\nstatic void\ndhcp6_newxid(const struct interface *ifp, struct dhcp6_message *m)\n{\n\tconst struct interface *ifp1;\n\tconst struct dhcp6_state *state1;\n\tuint32_t xid;\n\n\tif (ifp->options->options & DHCPCD_XID_HWADDR &&\n\t    ifp->hwlen >= sizeof(xid))\n\t\t/* The lower bits are probably more unique on the network */\n\t\tmemcpy(&xid, (ifp->hwaddr + ifp->hwlen) - sizeof(xid),\n\t\t    sizeof(xid));\n\telse {\n\tagain:\n\t\txid = arc4random();\n\t}\n\n\tm->xid[0] = (xid >> 16) & 0xff;\n\tm->xid[1] = (xid >> 8) & 0xff;\n\tm->xid[2] = xid & 0xff;\n\n\t/* Ensure it's unique */\n\tTAILQ_FOREACH(ifp1, ifp->ctx->ifaces, next) {\n\t\tif (ifp == ifp1)\n\t\t\tcontinue;\n\t\tif ((state1 = D6_CSTATE(ifp1)) == NULL)\n\t\t\tcontinue;\n\t\tif (state1->send != NULL && state1->send->xid[0] == m->xid[0] &&\n\t\t    state1->send->xid[1] == m->xid[1] &&\n\t\t    state1->send->xid[2] == m->xid[2])\n\t\t\tbreak;\n\t}\n\n\tif (ifp1 != NULL) {\n\t\tif (ifp->options->options & DHCPCD_XID_HWADDR &&\n\t\t    ifp->hwlen >= sizeof(xid)) {\n\t\t\tlogerrx(\"%s: duplicate xid on %s\", ifp->name,\n\t\t\t    ifp1->name);\n\t\t\treturn;\n\t\t}\n\t\tgoto again;\n\t}\n}\n\n#ifndef SMALL\nstatic const struct if_sla *\ndhcp6_findselfsla(struct interface *ifp)\n{\n\tsize_t i, j;\n\tstruct if_ia *ia;\n\n\tfor (i = 0; i < ifp->options->ia_len; i++) {\n\t\tia = &ifp->options->ia[i];\n\t\tif (ia->ia_type != D6_OPTION_IA_PD)\n\t\t\tcontinue;\n\t\tfor (j = 0; j < ia->sla_len; j++) {\n\t\t\tif (strcmp(ia->sla[j].ifname, ifp->name) == 0)\n\t\t\t\treturn &ia->sla[j];\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic int\ndhcp6_delegateaddr(struct in6_addr *addr, struct interface *ifp,\n    const struct ipv6_addr *prefix, const struct if_sla *sla, struct if_ia *ia)\n{\n\tstruct dhcp6_state *state;\n\tstruct if_sla asla;\n\tchar sabuf[INET6_ADDRSTRLEN];\n\tconst char *sa;\n\n\tstate = D6_STATE(ifp);\n\tif (state == NULL) {\n\t\tifp->if_data[IF_DATA_DHCP6] = calloc(1, sizeof(*state));\n\t\tstate = D6_STATE(ifp);\n\t\tif (state == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\treturn -1;\n\t\t}\n\n\t\tTAILQ_INIT(&state->addrs);\n\t\tstate->state = DH6S_DELEGATED;\n\t\tstate->reason = \"DELEGATED6\";\n\t}\n\n\tif (sla == NULL || !sla->sla_set) {\n\t\t/* No SLA set, so make an assumption of\n\t\t * desired SLA and prefix length. */\n\t\tasla.sla = ifp->index;\n\t\tasla.prefix_len = 0;\n\t\tasla.sla_set = false;\n\t\tsla = &asla;\n\t} else if (sla->prefix_len == 0) {\n\t\t/* An SLA was given, but prefix length was not.\n\t\t * We need to work out a suitable prefix length for\n\t\t * potentially more than one interface. */\n\t\tasla.sla = sla->sla;\n\t\tasla.prefix_len = 0;\n\t\tasla.sla_set = sla->sla_set;\n\t\tsla = &asla;\n\t}\n\n\tif (sla->prefix_len == 0) {\n\t\tuint32_t sla_max;\n\t\tint bits;\n\n\t\tsla_max = ia->sla_max;\n\t\tif (sla_max == 0 && (sla == NULL || !sla->sla_set)) {\n\t\t\tconst struct interface *ifi;\n\n\t\t\tTAILQ_FOREACH(ifi, ifp->ctx->ifaces, next) {\n\t\t\t\tif (ifi->index > sla_max)\n\t\t\t\t\tsla_max = ifi->index;\n\t\t\t}\n\t\t}\n\n\t\tbits = fls32(sla_max);\n\n\t\tif (prefix->prefix_len + bits > (int)UINT8_MAX)\n\t\t\tasla.prefix_len = UINT8_MAX;\n\t\telse {\n\t\t\tasla.prefix_len = (uint8_t)(prefix->prefix_len + bits);\n\n\t\t\t/* Make a 64 prefix by default, as this makes SLAAC\n\t\t\t * possible.\n\t\t\t * Otherwise round up to the nearest 4 bits. */\n\t\t\tif (asla.prefix_len <= 64)\n\t\t\t\tasla.prefix_len = 64;\n\t\t\telse\n\t\t\t\tasla.prefix_len = (uint8_t)ROUNDUP4(\n\t\t\t\t    asla.prefix_len);\n\t\t}\n\n#define BIT(n)\t      (1UL << (n))\n#define BIT_MASK(len) (BIT(len) - 1)\n\t\tif (ia->sla_max == 0) {\n\t\t\t/* Work out the real sla_max from our bits used */\n\t\t\tbits = asla.prefix_len - prefix->prefix_len;\n\t\t\t/* Make static analysis happy.\n\t\t\t * Bits cannot be bigger than 32 thanks to fls32. */\n\t\t\tassert(bits <= 32);\n\t\t\tia->sla_max = (uint32_t)BIT_MASK(bits);\n\t\t}\n\t}\n\n\tif (ipv6_userprefix(&prefix->prefix, prefix->prefix_len, sla->sla, addr,\n\t\tsla->prefix_len) == -1) {\n\t\tsa = inet_ntop(AF_INET6, &prefix->prefix, sabuf, sizeof(sabuf));\n\t\tlogerr(\"%s: invalid prefix %s/%d + %d/%d\", ifp->name, sa,\n\t\t    prefix->prefix_len, sla->sla, sla->prefix_len);\n\t\treturn -1;\n\t}\n\n\tif (prefix->prefix_exclude_len &&\n\t    IN6_ARE_ADDR_EQUAL(addr, &prefix->prefix_exclude)) {\n\t\tsa = inet_ntop(AF_INET6, &prefix->prefix_exclude, sabuf,\n\t\t    sizeof(sabuf));\n\t\tlogerrx(\"%s: cannot delegate excluded prefix %s/%d\", ifp->name,\n\t\t    sa, prefix->prefix_exclude_len);\n\t\treturn -1;\n\t}\n\n\treturn sla->prefix_len;\n}\n#endif\n\nstatic int\ndhcp6_makemessage(struct interface *ifp)\n{\n\tstruct dhcp6_state *state;\n\tstruct dhcp6_message *m;\n\tstruct dhcp6_option o;\n\tuint8_t *p, *si, *unicast, IA;\n\tsize_t n, l, len, ml, hl;\n\tuint8_t type;\n\tuint16_t si_len, uni_len, n_options;\n\tuint8_t *o_lenp;\n\tstruct if_options *ifo = ifp->options;\n\tconst struct dhcp_opt *opt, *opt2;\n\tconst struct ipv6_addr *ap;\n\tchar hbuf[HOSTNAME_MAX_LEN + 1];\n\tconst char *hostname;\n\tint fqdn;\n\tstruct dhcp6_ia_na ia_na;\n\tuint16_t ia_na_len;\n\tstruct if_ia *ifia;\n#ifdef AUTH\n\tuint16_t auth_len;\n#endif\n\tuint8_t duid[DUID_LEN];\n\tsize_t duid_len = 0;\n\n\tstate = D6_STATE(ifp);\n\tif (state->send) {\n\t\tfree(state->send);\n\t\tstate->send = NULL;\n\t}\n\n\tswitch (state->state) {\n\tcase DH6S_INIT: /* FALLTHROUGH */\n\tcase DH6S_DISCOVER:\n\t\ttype = DHCP6_SOLICIT;\n\t\tbreak;\n\tcase DH6S_REQUEST:\n\t\ttype = DHCP6_REQUEST;\n\t\tbreak;\n\tcase DH6S_CONFIRM:\n\t\ttype = DHCP6_CONFIRM;\n\t\tbreak;\n\tcase DH6S_REBIND:\n\t\ttype = DHCP6_REBIND;\n\t\tbreak;\n\tcase DH6S_RENEW:\n\t\ttype = DHCP6_RENEW;\n\t\tbreak;\n\tcase DH6S_INFORM:\n\t\ttype = DHCP6_INFORMATION_REQ;\n\t\tbreak;\n\tcase DH6S_RELEASE:\n\t\ttype = DHCP6_RELEASE;\n\t\tbreak;\n\tcase DH6S_DECLINE:\n\t\ttype = DHCP6_DECLINE;\n\t\tbreak;\n\tdefault:\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\t/* RFC 4704 Section 5 says we can only send FQDN for these\n\t * message types. */\n\tswitch (type) {\n\tcase DHCP6_SOLICIT:\n\tcase DHCP6_REQUEST:\n\tcase DHCP6_RENEW:\n\tcase DHCP6_REBIND:\n\t\tfqdn = ifo->fqdn;\n\t\tbreak;\n\tdefault:\n\t\tfqdn = FQDN_DISABLE;\n\t\tbreak;\n\t}\n\n\tif (fqdn == FQDN_DISABLE && ifo->options & DHCPCD_HOSTNAME) {\n\t\t/* We're sending the DHCPv4 hostname option, so send FQDN as\n\t\t * DHCPv6 has no FQDN option and DHCPv4 must not send\n\t\t * hostname and FQDN according to RFC4702 */\n\t\tfqdn = FQDN_BOTH;\n\t}\n\tif (fqdn != FQDN_DISABLE)\n\t\thostname = dhcp_get_hostname(hbuf, sizeof(hbuf), ifo);\n\telse\n\t\thostname = NULL; /* appearse gcc */\n\n\t/* Work out option size first */\n\tn_options = 0;\n\tlen = 0;\n\tsi = NULL;\n\thl = 0; /* Appease gcc */\n\tif (state->state != DH6S_RELEASE && state->state != DH6S_DECLINE) {\n\t\tfor (l = 0, opt = ifp->ctx->dhcp6_opts;\n\t\t    l < ifp->ctx->dhcp6_opts_len; l++, opt++) {\n\t\t\tfor (n = 0, opt2 = ifo->dhcp6_override;\n\t\t\t    n < ifo->dhcp6_override_len; n++, opt2++) {\n\t\t\t\tif (opt->option == opt2->option)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (n < ifo->dhcp6_override_len)\n\t\t\t\tcontinue;\n\t\t\tif (!DHC_REQOPT(opt, ifo->requestmask6, ifo->nomask6))\n\t\t\t\tcontinue;\n\t\t\tn_options++;\n\t\t\tlen += sizeof(o.len);\n\t\t}\n#ifndef SMALL\n\t\tfor (l = 0, opt = ifo->dhcp6_override;\n\t\t    l < ifo->dhcp6_override_len; l++, opt++) {\n\t\t\tif (!DHC_REQOPT(opt, ifo->requestmask6, ifo->nomask6))\n\t\t\t\tcontinue;\n\t\t\tn_options++;\n\t\t\tlen += sizeof(o.len);\n\t\t}\n\t\tif (dhcp6_findselfsla(ifp)) {\n\t\t\tn_options++;\n\t\t\tlen += sizeof(o.len);\n\t\t}\n#endif\n\t\tif (len)\n\t\t\tlen += sizeof(o);\n\n\t\tif (fqdn != FQDN_DISABLE) {\n\t\t\thl = encode_rfc1035(hostname, NULL);\n\t\t\tlen += sizeof(o) + 1 + hl;\n\t\t}\n\n\t\tif (!has_option_mask(ifo->nomask6, D6_OPTION_MUDURL) &&\n\t\t    ifo->mudurl[0])\n\t\t\tlen += sizeof(o) + ifo->mudurl[0];\n\n#ifdef AUTH\n\t\tif ((ifo->auth.options & DHCPCD_AUTH_SENDREQUIRE) !=\n\t\t\tDHCPCD_AUTH_SENDREQUIRE &&\n\t\t    DHC_REQ(ifo->requestmask6, ifo->nomask6,\n\t\t\tD6_OPTION_RECONF_ACCEPT))\n\t\t\tlen += sizeof(o); /* Reconfigure Accept */\n#endif\n\t}\n\n\tlen += sizeof(*state->send);\n\tlen += sizeof(o) + sizeof(uint16_t); /* elapsed */\n\n\tif (ifo->options & DHCPCD_ANONYMOUS) {\n\t\tduid_len = duid_make(duid, ifp, DUID_LL);\n\t\tlen += sizeof(o) + duid_len;\n\t} else {\n\t\tlen += sizeof(o) + ifp->ctx->duid_len;\n\t}\n\n\tif (!has_option_mask(ifo->nomask6, D6_OPTION_USER_CLASS))\n\t\tlen += dhcp6_makeuser(NULL, ifp);\n\n#ifndef SMALL\n\tif (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS))\n\t\tlen += dhcp6_makevendor(NULL, ifp);\n\tif (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_OPTS))\n\t\tlen += dhcp6_makevendoropts(NULL, ifp);\n#endif\n\n\t/* IA */\n\tm = NULL;\n\tml = 0;\n\tswitch (state->state) {\n\tcase DH6S_REQUEST:\n\t\tm = state->recv;\n\t\tml = state->recv_len;\n\t\t/* FALLTHROUGH */\n\tcase DH6S_DECLINE:\n\t\t/* FALLTHROUGH */\n\tcase DH6S_RELEASE:\n\t\t/* FALLTHROUGH */\n\tcase DH6S_RENEW:\n\t\tif (m == NULL) {\n\t\t\tm = state->new;\n\t\t\tml = state->new_len;\n\t\t}\n\t\tsi = dhcp6_findmoption(m, ml, D6_OPTION_SERVERID, &si_len);\n\t\tif (si == NULL)\n\t\t\treturn -1;\n\t\tlen += sizeof(o) + si_len;\n\t\t/* FALLTHROUGH */\n\tcase DH6S_REBIND:\n\t\t/* FALLTHROUGH */\n\tcase DH6S_CONFIRM:\n\t\t/* FALLTHROUGH */\n\tcase DH6S_DISCOVER:\n\t\tif (m == NULL) {\n\t\t\tm = state->new;\n\t\t\tml = state->new_len;\n\t\t}\n\t\tTAILQ_FOREACH(ap, &state->addrs, next) {\n\t\t\tif (ap->flags & IPV6_AF_STALE)\n\t\t\t\tcontinue;\n\t\t\tif (!(ap->flags & IPV6_AF_REQUEST) &&\n\t\t\t    (ap->prefix_vltime == 0 ||\n\t\t\t\tstate->state == DH6S_DISCOVER))\n\t\t\t\tcontinue;\n\t\t\tif (DECLINE_IA(ap) && state->state != DH6S_DECLINE)\n\t\t\t\tcontinue;\n\t\t\tif (ap->ia_type == D6_OPTION_IA_PD) {\n#ifndef SMALL\n\t\t\t\tlen += sizeof(o) + DHCP6_PD_ADDR_SIZE;\n\t\t\t\tif (ap->prefix_exclude_len)\n\t\t\t\t\tlen += sizeof(o) + 1 +\n\t\t\t\t\t    (uint8_t)((ap->prefix_exclude_len -\n\t\t\t\t\t\t\t  ap->prefix_len - 1) /\n\t\t\t\t\t\tNBBY) +\n\t\t\t\t\t    1;\n#endif\n\t\t\t} else\n\t\t\t\tlen += sizeof(o) + sizeof(struct dhcp6_ia_addr);\n\t\t}\n\t\t/* FALLTHROUGH */\n\tcase DH6S_INIT:\n\t\tfor (l = 0; l < ifo->ia_len; l++) {\n\t\t\tlen += sizeof(o) + sizeof(uint32_t); /* IAID */\n\t\t\t/* IA_TA does not have T1 or T2 timers */\n\t\t\tif (ifo->ia[l].ia_type != D6_OPTION_IA_TA)\n\t\t\t\tlen += sizeof(uint32_t) + sizeof(uint32_t);\n\t\t}\n\t\tIA = 1;\n\t\tbreak;\n\tdefault:\n\t\tIA = 0;\n\t}\n\n\tif (state->state == DH6S_DISCOVER &&\n\t    !(ifp->ctx->options & DHCPCD_TEST) &&\n\t    DHC_REQ(ifo->requestmask6, ifo->nomask6, D6_OPTION_RAPID_COMMIT))\n\t\tlen += sizeof(o);\n\n\tif (m == NULL) {\n\t\tm = state->new;\n\t\tml = state->new_len;\n\t}\n\n\tswitch (state->state) {\n\tcase DH6S_REQUEST: /* FALLTHROUGH */\n\tcase DH6S_RENEW:   /* FALLTHROUGH */\n\tcase DH6S_RELEASE:\n\t\tif (has_option_mask(ifo->nomask6, D6_OPTION_UNICAST)) {\n\t\t\tunicast = NULL;\n\t\t\tbreak;\n\t\t}\n\t\tunicast = dhcp6_findmoption(m, ml, D6_OPTION_UNICAST, &uni_len);\n\t\tbreak;\n\tdefault:\n\t\tunicast = NULL;\n\t\tbreak;\n\t}\n\n\t/* In non manager mode we listen and send from fixed addresses.\n\t * We should try and match an address we have to unicast to,\n\t * but for now this is the safest policy. */\n\tif (unicast != NULL && !(ifp->ctx->options & DHCPCD_MANAGER)) {\n\t\tlogdebugx(\"%s: ignoring unicast option as not manager\",\n\t\t    ifp->name);\n\t\tunicast = NULL;\n\t}\n\n#ifdef AUTH\n\tauth_len = 0;\n\tif (ifo->auth.options & DHCPCD_AUTH_SEND) {\n\t\tssize_t alen = dhcp_auth_encode(ifp->ctx, &ifo->auth,\n\t\t    state->auth.token, NULL, 0, 6, type, NULL, 0);\n\t\tif (alen != -1 && alen > UINT16_MAX) {\n\t\t\terrno = ERANGE;\n\t\t\talen = -1;\n\t\t}\n\t\tif (alen == -1)\n\t\t\tlogerr(\"%s: %s: dhcp_auth_encode\", __func__, ifp->name);\n\t\telse if (alen != 0) {\n\t\t\tauth_len = (uint16_t)alen;\n\t\t\tlen += sizeof(o) + auth_len;\n\t\t}\n\t}\n#endif\n\n\tstate->send = malloc(len);\n\tif (state->send == NULL)\n\t\treturn -1;\n\n\tstate->send_len = len;\n\tstate->send->type = type;\n\n\t/* If we found a unicast option, copy it to our state for sending */\n\tif (unicast && uni_len == sizeof(state->unicast))\n\t\tmemcpy(&state->unicast, unicast, sizeof(state->unicast));\n\telse\n\t\tstate->unicast = in6addr_any;\n\n\tdhcp6_newxid(ifp, state->send);\n\n#define COPYIN1(_code, _len)              \\\n\t{                                 \\\n\t\to.code = htons((_code));  \\\n\t\to.len = htons((_len));    \\\n\t\tmemcpy(p, &o, sizeof(o)); \\\n\t\tp += sizeof(o);           \\\n\t}\n#define COPYIN(_code, _data, _len)                  \\\n\tdo {                                        \\\n\t\tCOPYIN1((_code), (_len));           \\\n\t\tif ((_len) != 0) {                  \\\n\t\t\tmemcpy(p, (_data), (_len)); \\\n\t\t\tp += (_len);                \\\n\t\t}                                   \\\n\t} while (0 /* CONSTCOND */)\n\n\t/* Options are listed in numerical order as per RFC 7844 Section 4.1\n\t * XXX: They should be randomised. */\n\n\tp = (uint8_t *)state->send + sizeof(*state->send);\n\tif (ifo->options & DHCPCD_ANONYMOUS)\n\t\tCOPYIN(D6_OPTION_CLIENTID, duid, (uint16_t)duid_len);\n\telse\n\t\tCOPYIN(D6_OPTION_CLIENTID, ifp->ctx->duid,\n\t\t    (uint16_t)ifp->ctx->duid_len);\n\n\tif (si != NULL)\n\t\tCOPYIN(D6_OPTION_SERVERID, si, si_len);\n\n\tfor (l = 0; IA && l < ifo->ia_len; l++) {\n\t\tifia = &ifo->ia[l];\n\t\to_lenp = NEXTLEN(p);\n\t\t/* TA structure is the same as the others,\n\t\t * it just lacks the T1 and T2 timers.\n\t\t * These happen to be at the end of the struct,\n\t\t * so we just don't copy them in. */\n\t\tif (ifia->ia_type == D6_OPTION_IA_TA)\n\t\t\tia_na_len = sizeof(struct dhcp6_ia_ta);\n\t\telse\n\t\t\tia_na_len = sizeof(ia_na);\n\t\tmemcpy(ia_na.iaid, ifia->iaid, sizeof(ia_na.iaid));\n\t\t/* RFC 8415 21.4 and 21.21 state that T1 and T2 should be zero.\n\t\t * An RFC compliant server MUST ignore them anyway. */\n\t\tia_na.t1 = 0;\n\t\tia_na.t2 = 0;\n\t\tCOPYIN(ifia->ia_type, &ia_na, ia_na_len);\n\t\tTAILQ_FOREACH(ap, &state->addrs, next) {\n\t\t\tif (ap->flags & IPV6_AF_STALE)\n\t\t\t\tcontinue;\n\t\t\tif (!(ap->flags & IPV6_AF_REQUEST) &&\n\t\t\t    (ap->prefix_vltime == 0 ||\n\t\t\t\tstate->state == DH6S_DISCOVER))\n\t\t\t\tcontinue;\n\t\t\tif (DECLINE_IA(ap) && state->state != DH6S_DECLINE)\n\t\t\t\tcontinue;\n\t\t\tif (ap->ia_type != ifia->ia_type)\n\t\t\t\tcontinue;\n\t\t\tif (memcmp(ap->iaid, ifia->iaid, sizeof(ap->iaid)))\n\t\t\t\tcontinue;\n\t\t\tif (ap->ia_type == D6_OPTION_IA_PD) {\n#ifndef SMALL\n\t\t\t\tuint8_t pdp[DHCP6_PD_ADDR_SIZE];\n\n\t\t\t\tmemset(pdp, 0, DHCP6_PD_ADDR_PLEN);\n\t\t\t\tpdp[DHCP6_PD_ADDR_PLEN] = (uint8_t)\n\t\t\t\t\t\t\t      ap->prefix_len;\n\t\t\t\tmemcpy(pdp + DHCP6_PD_ADDR_PREFIX, &ap->prefix,\n\t\t\t\t    DHCP6_PD_ADDR_SIZE - DHCP6_PD_ADDR_PREFIX);\n\t\t\t\tCOPYIN(D6_OPTION_IAPREFIX, pdp, sizeof(pdp));\n\n\t\t\t\tia_na_len = (uint16_t)(ia_na_len + sizeof(o) +\n\t\t\t\t    sizeof(pdp));\n\n\t\t\t\t/* RFC6603 Section 4.2 */\n\t\t\t\tif (ap->prefix_exclude_len) {\n\t\t\t\t\tuint8_t exb[16], *ep, u8;\n\t\t\t\t\tconst uint8_t *pp;\n\n\t\t\t\t\tn = (size_t)((ap->prefix_exclude_len -\n\t\t\t\t\t\t\t ap->prefix_len - 1) /\n\t\t\t\t\t\tNBBY) +\n\t\t\t\t\t    1;\n\t\t\t\t\tep = exb;\n\t\t\t\t\t*ep++ = (uint8_t)ap->prefix_exclude_len;\n\t\t\t\t\tpp = ap->prefix_exclude.s6_addr;\n\t\t\t\t\tpp += (size_t)((ap->prefix_len - 1) /\n\t\t\t\t\t\t  NBBY) +\n\t\t\t\t\t    (n - 1);\n\t\t\t\t\tu8 = ap->prefix_len % NBBY;\n\t\t\t\t\tif (u8)\n\t\t\t\t\t\tn--;\n\t\t\t\t\twhile (n-- > 0)\n\t\t\t\t\t\t*ep++ = *pp--;\n\t\t\t\t\tn = (size_t)(ep - exb);\n\t\t\t\t\tif (u8) {\n\t\t\t\t\t\t*ep = (uint8_t)(*pp << u8);\n\t\t\t\t\t\tn++;\n\t\t\t\t\t}\n\t\t\t\t\tCOPYIN(D6_OPTION_PD_EXCLUDE, exb,\n\t\t\t\t\t    (uint16_t)n);\n\t\t\t\t\tia_na_len = (uint16_t)(ia_na_len +\n\t\t\t\t\t    sizeof(o) + n);\n\t\t\t\t}\n#endif\n\t\t\t} else {\n\t\t\t\tstruct dhcp6_ia_addr ia = {\n\t\t\t\t\t.addr = ap->addr,\n\t\t\t\t\t/*\n\t\t\t\t\t * RFC 8415 21.6 states that the\n\t\t\t\t\t * valid and preferred lifetimes sent by\n\t\t\t\t\t * the client SHOULD be zero and MUST\n\t\t\t\t\t * be ignored by the server.\n\t\t\t\t\t */\n\t\t\t\t};\n\n\t\t\t\tCOPYIN(D6_OPTION_IA_ADDR, &ia, sizeof(ia));\n\t\t\t\tia_na_len = (uint16_t)(ia_na_len + sizeof(o) +\n\t\t\t\t    sizeof(ia));\n\t\t\t}\n\t\t}\n\n\t\t/* Update the total option lenth. */\n\t\tia_na_len = htons(ia_na_len);\n\t\tmemcpy(o_lenp, &ia_na_len, sizeof(ia_na_len));\n\t}\n\n\tif (state->send->type != DHCP6_RELEASE &&\n\t    state->send->type != DHCP6_DECLINE && n_options) {\n\t\to_lenp = NEXTLEN(p);\n\t\to.len = 0;\n\t\tCOPYIN1(D6_OPTION_ORO, 0);\n\t\tfor (l = 0, opt = ifp->ctx->dhcp6_opts;\n\t\t    l < ifp->ctx->dhcp6_opts_len; l++, opt++) {\n#ifndef SMALL\n\t\t\tfor (n = 0, opt2 = ifo->dhcp6_override;\n\t\t\t    n < ifo->dhcp6_override_len; n++, opt2++) {\n\t\t\t\tif (opt->option == opt2->option)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (n < ifo->dhcp6_override_len)\n\t\t\t\tcontinue;\n#endif\n\t\t\tif (!DHC_REQOPT(opt, ifo->requestmask6, ifo->nomask6))\n\t\t\t\tcontinue;\n\t\t\to.code = htons((uint16_t)opt->option);\n\t\t\tmemcpy(p, &o.code, sizeof(o.code));\n\t\t\tp += sizeof(o.code);\n\t\t\to.len = (uint16_t)(o.len + sizeof(o.code));\n\t\t}\n#ifndef SMALL\n\t\tfor (l = 0, opt = ifo->dhcp6_override;\n\t\t    l < ifo->dhcp6_override_len; l++, opt++) {\n\t\t\tif (!DHC_REQOPT(opt, ifo->requestmask6, ifo->nomask6))\n\t\t\t\tcontinue;\n\t\t\to.code = htons((uint16_t)opt->option);\n\t\t\tmemcpy(p, &o.code, sizeof(o.code));\n\t\t\tp += sizeof(o.code);\n\t\t\to.len = (uint16_t)(o.len + sizeof(o.code));\n\t\t}\n\t\tif (dhcp6_findselfsla(ifp)) {\n\t\t\to.code = htons(D6_OPTION_PD_EXCLUDE);\n\t\t\tmemcpy(p, &o.code, sizeof(o.code));\n\t\t\tp += sizeof(o.code);\n\t\t\to.len = (uint16_t)(o.len + sizeof(o.code));\n\t\t}\n#endif\n\t\to.len = htons(o.len);\n\t\tmemcpy(o_lenp, &o.len, sizeof(o.len));\n\t}\n\n\tsi_len = 0;\n\tCOPYIN(D6_OPTION_ELAPSED, &si_len, sizeof(si_len));\n\n\tif (state->state == DH6S_DISCOVER &&\n\t    !(ifp->ctx->options & DHCPCD_TEST) &&\n\t    DHC_REQ(ifo->requestmask6, ifo->nomask6, D6_OPTION_RAPID_COMMIT))\n\t\tCOPYIN1(D6_OPTION_RAPID_COMMIT, 0);\n\n\tif (!has_option_mask(ifo->nomask6, D6_OPTION_USER_CLASS))\n\t\tp += dhcp6_makeuser(p, ifp);\n\n#ifndef SMALL\n\tif (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS))\n\t\tp += dhcp6_makevendor(p, ifp);\n\tif (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_OPTS))\n\t\tp += dhcp6_makevendoropts(p, ifp);\n#endif\n\n\tif (state->send->type != DHCP6_RELEASE &&\n\t    state->send->type != DHCP6_DECLINE) {\n\t\tif (fqdn != FQDN_DISABLE) {\n\t\t\to_lenp = NEXTLEN(p);\n\t\t\tCOPYIN1(D6_OPTION_FQDN, 0);\n\t\t\tif (hl == 0)\n\t\t\t\t*p = D6_FQDN_NONE;\n\t\t\telse {\n\t\t\t\tswitch (fqdn) {\n\t\t\t\tcase FQDN_BOTH:\n\t\t\t\t\t*p = D6_FQDN_BOTH;\n\t\t\t\t\tbreak;\n\t\t\t\tcase FQDN_PTR:\n\t\t\t\t\t*p = D6_FQDN_PTR;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t*p = D6_FQDN_NONE;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tp++;\n\t\t\tencode_rfc1035(hostname, p);\n\t\t\tp += hl;\n\t\t\to.len = htons((uint16_t)(hl + 1));\n\t\t\tmemcpy(o_lenp, &o.len, sizeof(o.len));\n\t\t}\n\n\t\tif (!has_option_mask(ifo->nomask6, D6_OPTION_MUDURL) &&\n\t\t    ifo->mudurl[0])\n\t\t\tCOPYIN(D6_OPTION_MUDURL, ifo->mudurl + 1,\n\t\t\t    ifo->mudurl[0]);\n\n#ifdef AUTH\n\t\tif ((ifo->auth.options & DHCPCD_AUTH_SENDREQUIRE) !=\n\t\t\tDHCPCD_AUTH_SENDREQUIRE &&\n\t\t    DHC_REQ(ifo->requestmask6, ifo->nomask6,\n\t\t\tD6_OPTION_RECONF_ACCEPT))\n\t\t\tCOPYIN1(D6_OPTION_RECONF_ACCEPT, 0);\n#endif\n\t}\n\n#ifdef AUTH\n\t/* This has to be the last option */\n\tif (ifo->auth.options & DHCPCD_AUTH_SEND && auth_len != 0) {\n\t\tCOPYIN1(D6_OPTION_AUTH, auth_len);\n\t\t/* data will be filled at send message time */\n\t}\n#endif\n\n\treturn 0;\n}\n\nstatic const char *\ndhcp6_get_op(uint16_t type)\n{\n\tconst struct dhcp6_op *d;\n\n\tfor (d = dhcp6_ops; d->name; d++)\n\t\tif (d->type == type)\n\t\t\treturn d->name;\n\treturn NULL;\n}\n\nstatic void\ndhcp6_freedrop_addrs(struct interface *ifp, int drop, unsigned int notflags,\n    const struct interface *ifd)\n{\n\tstruct dhcp6_state *state;\n\n\tstate = D6_STATE(ifp);\n\tif (state) {\n\t\tipv6_freedrop_addrs(&state->addrs, drop, notflags, ifd);\n\t\tif (drop)\n\t\t\trt_build(ifp->ctx, AF_INET6);\n\t}\n}\n\n#ifndef SMALL\nstatic void\ndhcp6_delete_delegates(struct interface *ifp)\n{\n\tstruct interface *ifp0;\n\n\tif (ifp->ctx->ifaces) {\n\t\tTAILQ_FOREACH(ifp0, ifp->ctx->ifaces, next) {\n\t\t\tif (ifp0 != ifp)\n\t\t\t\tdhcp6_freedrop_addrs(ifp0, 1, 0, ifp);\n\t\t}\n\t}\n}\n#endif\n\n#ifdef AUTH\nstatic ssize_t\ndhcp6_update_auth(struct interface *ifp, struct dhcp6_message *m, size_t len)\n{\n\tstruct dhcp6_state *state;\n\tuint8_t *opt;\n\tuint16_t opt_len;\n\n\topt = dhcp6_findmoption(m, len, D6_OPTION_AUTH, &opt_len);\n\tif (opt == NULL)\n\t\treturn -1;\n\n\tstate = D6_STATE(ifp);\n\treturn dhcp_auth_encode(ifp->ctx, &ifp->options->auth,\n\t    state->auth.token, (uint8_t *)state->send, state->send_len, 6,\n\t    state->send->type, opt, opt_len);\n}\n#endif\n\nstatic const struct in6_addr alldhcp = IN6ADDR_LINKLOCAL_ALLDHCP_INIT;\nstatic int\ndhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))\n{\n\tstruct dhcp6_state *state = D6_STATE(ifp);\n\tstruct dhcpcd_ctx *ctx = ifp->ctx;\n\tunsigned int RT;\n\tbool multicast = true;\n\tstruct sockaddr_in6 dst = {\n\t\t.sin6_family = AF_INET6,\n\t/* Setting the port on Linux gives EINVAL when sending.\n\t * This looks like a kernel bug as the equivalent works\n\t * fine with the DHCP counterpart. */\n#ifndef __linux__\n\t\t.sin6_port = htons(DHCP6_SERVER_PORT),\n#endif\n\t};\n\tstruct udphdr udp = {\n\t\t.uh_sport = htons(DHCP6_CLIENT_PORT),\n\t\t.uh_dport = htons(DHCP6_SERVER_PORT),\n\t\t.uh_ulen = htons((uint16_t)(sizeof(udp) + state->send_len)),\n\t};\n\tstruct iovec iov[] = {\n\t\t{\n\t\t    .iov_base = &udp,\n\t\t    .iov_len = sizeof(udp),\n\t\t},\n\t\t{\n\t\t    .iov_base = state->send,\n\t\t    .iov_len = state->send_len,\n\t\t},\n\t};\n\tunion {\n\t\tstruct cmsghdr hdr;\n\t\tuint8_t buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];\n\t} cmsgbuf = { .buf = { 0 } };\n\tstruct msghdr msg = {\n\t\t.msg_name = &dst,\n\t\t.msg_namelen = sizeof(dst),\n\t\t.msg_iov = iov,\n\t\t.msg_iovlen = __arraycount(iov),\n\t};\n\tchar uaddr[INET6_ADDRSTRLEN];\n\n\tif (!callback && !if_is_link_up(ifp))\n\t\treturn 0;\n\n\tif (!IN6_IS_ADDR_UNSPECIFIED(&state->unicast)) {\n\t\tswitch (state->send->type) {\n\t\tcase DHCP6_SOLICIT: /* FALLTHROUGH */\n\t\tcase DHCP6_CONFIRM: /* FALLTHROUGH */\n\t\tcase DHCP6_REBIND:\n\t\t\t/* Unicasting is denied for these types. */\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmulticast = false;\n\t\t\tinet_ntop(AF_INET6, &state->unicast, uaddr,\n\t\t\t    sizeof(uaddr));\n\t\t\tbreak;\n\t\t}\n\t}\n\tdst.sin6_addr = multicast ? alldhcp : state->unicast;\n\n\tif (!callback) {\n\t\tlogdebugx(\"%s: %s %s with xid 0x%02x%02x%02x%s%s\", ifp->name,\n\t\t    multicast ? \"multicasting\" : \"unicasting\",\n\t\t    dhcp6_get_op(state->send->type), state->send->xid[0],\n\t\t    state->send->xid[1], state->send->xid[2],\n\t\t    !multicast ? \" \" : \"\", !multicast ? uaddr : \"\");\n\t\tRT = 0;\n\t} else {\n\t\tif (state->IMD &&\n\t\t    !(ifp->options->options & DHCPCD_INITIAL_DELAY))\n\t\t\tstate->IMD = 0;\n\t\tif (state->IMD) {\n\t\t\tstate->RT = state->IMD * MSEC_PER_SEC;\n\t\t\t/* Some buggy PPP servers close the link too early\n\t\t\t * after sending an invalid status in their reply\n\t\t\t * which means this host won't see it.\n\t\t\t * 1 second grace seems to be the sweet spot. */\n\t\t\tif (ifp->flags & IFF_POINTOPOINT)\n\t\t\t\tstate->RT += MSEC_PER_SEC;\n\t\t} else if (state->RTC == 0)\n\t\t\tstate->RT = state->IRT * MSEC_PER_SEC;\n\n\t\tif (state->MRT != 0) {\n\t\t\tunsigned int mrt = state->MRT * MSEC_PER_SEC;\n\n\t\t\tif (state->RT > mrt)\n\t\t\t\tstate->RT = mrt;\n\t\t}\n\n\t\t/* Add -.1 to .1 * RT randomness as per RFC8415 section 15 */\n\t\tuint32_t lru = arc4random_uniform(state->RTC == 0 ?\n\t\t\tDHCP6_RAND_MAX :\n\t\t\tDHCP6_RAND_MAX - DHCP6_RAND_MIN);\n\t\tint lr = (int)lru - (state->RTC == 0 ? 0 : DHCP6_RAND_MAX);\n\t\tRT = state->RT +\n\t\t    (unsigned int)((float)state->RT *\n\t\t\t((float)lr / DHCP6_RAND_DIV));\n\n\t\tif (if_is_link_up(ifp))\n\t\t\tlogdebugx(\"%s: %s %s (xid 0x%02x%02x%02x)%s%s,\"\n\t\t\t\t  \" next in %0.1f seconds\",\n\t\t\t    ifp->name,\n\t\t\t    state->IMD != 0 ? \"delaying\" :\n\t\t\t\tmulticast   ? \"multicasting\" :\n\t\t\t\t\t      \"unicasting\",\n\t\t\t    dhcp6_get_op(state->send->type),\n\t\t\t    state->send->xid[0], state->send->xid[1],\n\t\t\t    state->send->xid[2],\n\t\t\t    state->IMD == 0 && !multicast ? \" \" : \"\",\n\t\t\t    state->IMD == 0 && !multicast ? uaddr : \"\",\n\t\t\t    (float)RT / MSEC_PER_SEC);\n\n\t\t/* Wait the initial delay */\n\t\tif (state->IMD != 0) {\n\t\t\tstate->IMD = 0;\n\t\t\teloop_timeout_add_msec(ctx->eloop, RT, callback, ifp);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tif (!if_is_link_up(ifp))\n\t\treturn 0;\n\n\t/* Update the elapsed time */\n\tdhcp6_updateelapsed(ifp, state->send, state->send_len);\n#ifdef AUTH\n\tif (ifp->options->auth.options & DHCPCD_AUTH_SEND &&\n\t    dhcp6_update_auth(ifp, state->send, state->send_len) == -1) {\n\t\tlogerr(\"%s: %s: dhcp6_updateauth\", __func__, ifp->name);\n\t\tif (errno != ESRCH)\n\t\t\treturn -1;\n\t}\n#endif\n\n\t/* Set the outbound interface */\n\tif (multicast) {\n\t\tstruct cmsghdr *cm;\n\t\tstruct in6_pktinfo pi = { .ipi6_ifindex = ifp->index };\n\n\t\tdst.sin6_scope_id = ifp->index;\n\t\tmsg.msg_control = cmsgbuf.buf;\n\t\tmsg.msg_controllen = sizeof(cmsgbuf.buf);\n\t\tcm = CMSG_FIRSTHDR(&msg);\n\t\tif (cm == NULL) /* unlikely */\n\t\t\treturn -1;\n\t\tcm->cmsg_level = IPPROTO_IPV6;\n\t\tcm->cmsg_type = IPV6_PKTINFO;\n\t\tcm->cmsg_len = CMSG_LEN(sizeof(pi));\n\t\tmemcpy(CMSG_DATA(cm), &pi, sizeof(pi));\n\t}\n\n#ifdef PRIVSEP\n\tif (IN_PRIVSEP(ifp->ctx)) {\n\t\tif (ps_inet_senddhcp6(ifp, &msg) == -1)\n\t\t\tlogerr(__func__);\n\t\tgoto sent;\n\t}\n#endif\n\n\tif (sendmsg(ctx->dhcp6_wfd, &msg, 0) == -1) {\n\t\tlogerr(\"%s: %s: sendmsg\", __func__, ifp->name);\n\t\t/* Allow DHCPv6 to continue .... the errors\n\t\t * would be rate limited by the protocol.\n\t\t * Generally the error is ENOBUFS when struggling to\n\t\t * associate with an access point. */\n\t}\n\n#ifdef PRIVSEP\nsent:\n#endif\n\tstate->RTC++;\n\tif (callback) {\n\t\tstate->RT = RT * 2;\n\t\tif (state->RT < RT) /* Check overflow */\n\t\t\tstate->RT = RT;\n\t\tif (state->MRC == 0 || state->RTC <= state->MRC)\n\t\t\teloop_timeout_add_msec(ctx->eloop, RT, callback, ifp);\n\t\telse if (state->MRC != 0 && state->MRCcallback)\n\t\t\teloop_timeout_add_msec(ctx->eloop, RT,\n\t\t\t    state->MRCcallback, ifp);\n\t\telse\n\t\t\tlogwarnx(\"%s: sent %d times with no reply\", ifp->name,\n\t\t\t    state->RTC);\n\t}\n\treturn 0;\n}\n\nstatic void\ndhcp6_sendinform(void *arg)\n{\n\tdhcp6_sendmessage(arg, dhcp6_sendinform);\n}\n\nstatic void\ndhcp6_senddiscover2(void *arg)\n{\n\tdhcp6_sendmessage(arg, dhcp6_senddiscover2);\n}\n\nstatic void\ndhcp6_senddiscover1(void *arg)\n{\n\t/*\n\t * So the initial RT has elapsed.\n\t * If we have any ADVERTs we can now REQUEST them.\n\t * RFC 8415 15 and 18.2.1\n\t */\n\tstruct interface *ifp = arg;\n\tstruct dhcp6_state *state = D6_STATE(ifp);\n\n\tif (state->recv == NULL || state->recv->type != DHCP6_ADVERTISE)\n\t\tdhcp6_sendmessage(arg, dhcp6_senddiscover2);\n\telse\n\t\tdhcp6_startrequest(ifp);\n}\n\nstatic void\ndhcp6_senddiscover(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tstruct dhcp6_state *state = D6_STATE(ifp);\n\n\tdhcp6_sendmessage(arg,\n\t    state->IMD != 0 ? dhcp6_senddiscover : dhcp6_senddiscover1);\n}\n\nstatic void\ndhcp6_sendrequest(void *arg)\n{\n\tdhcp6_sendmessage(arg, dhcp6_sendrequest);\n}\n\nstatic void\ndhcp6_sendrebind(void *arg)\n{\n\tdhcp6_sendmessage(arg, dhcp6_sendrebind);\n}\n\nstatic void\ndhcp6_sendrenew(void *arg)\n{\n\tdhcp6_sendmessage(arg, dhcp6_sendrenew);\n}\n\nstatic void\ndhcp6_sendconfirm(void *arg)\n{\n\tdhcp6_sendmessage(arg, dhcp6_sendconfirm);\n}\n\nstatic void\ndhcp6_senddecline(void *arg)\n{\n\tdhcp6_sendmessage(arg, dhcp6_senddecline);\n}\n\nstatic void\ndhcp6_sendrelease(void *arg)\n{\n\tdhcp6_sendmessage(arg, dhcp6_sendrelease);\n}\n\nstatic void\ndhcp6_startrenew(void *arg)\n{\n\tstruct interface *ifp;\n\tstruct dhcp6_state *state;\n\n\tifp = arg;\n\tif ((state = D6_STATE(ifp)) == NULL)\n\t\treturn;\n\n\t/* Only renew in the bound or renew states */\n\tif (state->state != DH6S_BOUND && state->state != DH6S_RENEW)\n\t\treturn;\n\n\t/* Remove the timeout as the renew may have been forced. */\n\teloop_timeout_delete(ifp->ctx->eloop, dhcp6_startrenew, ifp);\n\n\tstate->state = DH6S_RENEW;\n\tstate->RTC = 0;\n\tstate->IMD = REN_MAX_DELAY;\n\tstate->IRT = REN_TIMEOUT;\n\tstate->MRT = REN_MAX_RT;\n\tstate->MRC = 0;\n\n\tif (dhcp6_makemessage(ifp) == -1)\n\t\tlogerr(\"%s: %s\", __func__, ifp->name);\n\telse\n\t\tdhcp6_sendrenew(ifp);\n}\n\nvoid\ndhcp6_renew(struct interface *ifp)\n{\n\tdhcp6_startrenew(ifp);\n}\n\nbool\ndhcp6_dadcompleted(const struct interface *ifp)\n{\n\tconst struct dhcp6_state *state;\n\tconst struct ipv6_addr *ap;\n\n\tstate = D6_CSTATE(ifp);\n\tTAILQ_FOREACH(ap, &state->addrs, next) {\n\t\tif (ap->flags & IPV6_AF_ADDED &&\n\t\t    !(ap->flags & IPV6_AF_DADCOMPLETED))\n\t\t\treturn false;\n\t}\n\treturn true;\n}\n\nstatic void\ndhcp6_dadcallback(void *arg)\n{\n\tstruct ipv6_addr *ia = arg;\n\tstruct interface *ifp;\n\tstruct dhcp6_state *state;\n\tstruct ipv6_addr *ia2;\n\tbool completed, valid, oneduplicated;\n\n\tcompleted = (ia->flags & IPV6_AF_DADCOMPLETED);\n\tia->flags |= IPV6_AF_DADCOMPLETED;\n\tif (ia->addr_flags & IN6_IFF_DUPLICATED)\n\t\tlogwarnx(\"%s: DAD detected %s\", ia->iface->name, ia->saddr);\n\n\tif (completed)\n\t\treturn;\n\n\tifp = ia->iface;\n\tstate = D6_STATE(ifp);\n\tif (state->state != DH6S_BOUND && state->state != DH6S_DELEGATED)\n\t\treturn;\n\n#ifdef SMALL\n\tvalid = true;\n#else\n\tvalid = (ia->delegating_prefix == NULL);\n#endif\n\tcompleted = true;\n\toneduplicated = false;\n\tTAILQ_FOREACH(ia2, &state->addrs, next) {\n\t\tif (ia2->flags & IPV6_AF_ADDED &&\n\t\t    !(ia2->flags & IPV6_AF_DADCOMPLETED)) {\n\t\t\tcompleted = false;\n\t\t\tbreak;\n\t\t}\n\t\tif (DECLINE_IA(ia))\n\t\t\toneduplicated = true;\n\t}\n\tif (!completed)\n\t\treturn;\n\n\tlogdebugx(\"%s: DHCPv6 DAD completed\", ifp->name);\n\n\tif (oneduplicated && state->state == DH6S_BOUND) {\n\t\tdhcp6_startdecline(ifp);\n\t\treturn;\n\t}\n\n\tscript_runreason(ifp,\n#ifndef SMALL\n\t    ia->delegating_prefix ? \"DELEGATED6\" :\n#endif\n\t\t\t\t    state->reason);\n\tif (valid)\n\t\tdhcpcd_daemonise(ifp->ctx);\n}\n\nstatic void\ndhcp6_addrequestedaddrs(struct interface *ifp)\n{\n\tstruct dhcp6_state *state;\n\tsize_t i;\n\tstruct if_ia *ia;\n\tstruct ipv6_addr *a;\n\n\tstate = D6_STATE(ifp);\n\t/* Add any requested prefixes / addresses */\n\tfor (i = 0; i < ifp->options->ia_len; i++) {\n\t\tia = &ifp->options->ia[i];\n\t\tif (!((ia->ia_type == D6_OPTION_IA_PD && ia->prefix_len) ||\n\t\t\t!IN6_IS_ADDR_UNSPECIFIED(&ia->addr)))\n\t\t\tcontinue;\n\t\ta = ipv6_newaddr(ifp, &ia->addr,\n\t\t    /*\n\t\t     * RFC 5942 Section 5\n\t\t     * We cannot assume any prefix length, nor tie the\n\t\t     * address to an existing one as it could expire\n\t\t     * before the address.\n\t\t     * As such we just give it a 128 prefix.\n\t\t     */\n\t\t    ia->ia_type == D6_OPTION_IA_PD ? ia->prefix_len : 128,\n\t\t    IPV6_AF_REQUEST);\n\t\tif (a == NULL)\n\t\t\tcontinue;\n\t\ta->dadcallback = dhcp6_dadcallback;\n\t\tmemcpy(&a->iaid, &ia->iaid, sizeof(a->iaid));\n\t\ta->ia_type = ia->ia_type;\n\t\tTAILQ_INSERT_TAIL(&state->addrs, a, next);\n\t}\n}\n\nstatic void\ndhcp6_startdiscover(void *arg)\n{\n\tstruct interface *ifp;\n\tstruct if_options *ifo;\n\tstruct dhcp6_state *state;\n\tint llevel;\n\tstruct ipv6_addr *ia;\n\n\tifp = arg;\n\tstate = D6_STATE(ifp);\n\tifo = ifp->options;\n#ifndef SMALL\n\tif (state->reason == NULL || strcmp(state->reason, \"TIMEOUT6\") != 0)\n\t\tdhcp6_delete_delegates(ifp);\n#endif\n\t/* Ensure we never request INFO_REFRESH_TIME,\n\t * this only belongs in Information-Request messages */\n\tdel_option_mask(ifo->requestmask6, D6_OPTION_INFO_REFRESH_TIME);\n\n\tif (state->new == NULL && !state->failed)\n\t\tllevel = LOG_INFO;\n\telse\n\t\tllevel = LOG_DEBUG;\n\tlogmessage(llevel, \"%s: soliciting a DHCPv6 lease\", ifp->name);\n\tstate->state = DH6S_DISCOVER;\n\tstate->RTC = 0;\n\tstate->IMD = SOL_MAX_DELAY;\n\tstate->IRT = SOL_TIMEOUT;\n\tstate->MRT = state->sol_max_rt;\n\tstate->MRC = SOL_MAX_RC;\n\n\t/* If we fail to renew or confirm, our requested addreses will\n\t * be marked as stale.\n\t To re-request them, just mark them as not stale. */\n\tTAILQ_FOREACH(ia, &state->addrs, next) {\n\t\tif (ia->flags & IPV6_AF_REQUEST)\n\t\t\tia->flags &= ~IPV6_AF_STALE;\n\t}\n\n\tif (dhcp6_makemessage(ifp) == -1)\n\t\tlogerr(\"%s: %s\", __func__, ifp->name);\n\telse\n\t\tdhcp6_senddiscover(ifp);\n}\n\nstatic void\ndhcp6_startinform(void *arg)\n{\n\tstruct interface *ifp;\n\tstruct dhcp6_state *state;\n\tint llevel;\n\tstruct if_options *ifo;\n\n\tifp = arg;\n\tstate = D6_STATE(ifp);\n\tifo = ifp->options;\n\tllevel = state->failed ? LOG_DEBUG : LOG_INFO;\n\tlogmessage(llevel, \"%s: requesting DHCPv6 information\", ifp->name);\n\tstate->state = DH6S_INFORM;\n\tstate->RTC = 0;\n\tstate->IMD = INF_MAX_DELAY;\n\tstate->IRT = INF_TIMEOUT;\n\tstate->MRT = state->inf_max_rt;\n\tstate->MRC = 0;\n\n\t/* Ensure we always request INFO_REFRESH_TIME as per rfc8415 */\n\tadd_option_mask(ifo->requestmask6, D6_OPTION_INFO_REFRESH_TIME);\n\n\tif (dhcp6_makemessage(ifp) == -1) {\n\t\tlogerr(\"%s: %s\", __func__, ifp->name);\n\t\treturn;\n\t}\n\tdhcp6_sendinform(ifp);\n\t/* RFC3315 18.1.2 says that if CONFIRM failed then the prior addresses\n\t * SHOULD be used. The wording here is poor, because the addresses are\n\t * merely one facet of the lease as a whole.\n\t * This poor wording might explain the lack of similar text for INFORM\n\t * in 18.1.5 because there are no addresses in the INFORM message. */\n\tif (!state->failed)\n\t\teloop_timeout_add_sec(ifp->ctx->eloop, INF_MAX_RD,\n\t\t    dhcp6_failinform, ifp);\n}\n\nstatic bool\ndhcp6_startdiscoinform(struct interface *ifp)\n{\n\tunsigned long long opts = ifp->options->options;\n\n\tif (opts & DHCPCD_IA_FORCED || ipv6nd_hasradhcp(ifp, true))\n\t\tdhcp6_startdiscover(ifp);\n\telse if (opts & DHCPCD_INFORM6 || ipv6nd_hasradhcp(ifp, false))\n\t\tdhcp6_startinform(ifp);\n\telse\n\t\treturn false;\n\treturn true;\n}\n\nstatic void\ndhcp6_fail(struct interface *ifp, bool drop)\n{\n\tstruct dhcp6_state *state = D6_STATE(ifp);\n\n\tstate->failed = true;\n\n\tif (drop) {\n\t\tdhcp6_freedrop_addrs(ifp, 1,\n\t\t    IPV6_AF_DELEGATED | IPV6_AF_PFXDELEGATION, NULL);\n#ifndef SMALL\n\t\tdhcp6_delete_delegates(ifp);\n#endif\n\t\tfree(state->old);\n\t\tstate->old = state->new;\n\t\tstate->old_len = state->new_len;\n\t\tstate->new = NULL;\n\t\tstate->new_len = 0;\n\t\tif (state->old != NULL)\n\t\t\tscript_runreason(ifp, \"EXPIRE6\");\n\t\tdhcp_unlink(ifp->ctx, state->leasefile);\n\t\tdhcp6_addrequestedaddrs(ifp);\n\t\teloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);\n\t} else if ((state->state == DH6S_CONFIRM ||\n\t\t       state->state == DH6S_REBIND) &&\n\t    ifp->options->options & DHCPCD_LASTLEASE) {\n\t\tdhcp6_bind(ifp, NULL, NULL);\n\t\tstate->state = DH6S_REBIND;\n\t\tdhcp6_startrebind(ifp);\n\t\treturn;\n\t} else if (state->new) {\n\t\tscript_runreason(ifp, \"TIMEOUT6\");\n\t\t// We need to keep the expire timeout alive\n\t}\n\n\tif (!dhcp6_startdiscoinform(ifp)) {\n\t\tlogwarnx(\"%s: no advertising IPv6 router wants DHCP\",\n\t\t    ifp->name);\n\t\tstate->state = DH6S_INIT;\n\t}\n}\n\nstatic int\ndhcp6_failloglevel(struct interface *ifp)\n{\n\tconst struct dhcp6_state *state = D6_CSTATE(ifp);\n\n\treturn state->failed ? LOG_DEBUG : LOG_ERR;\n}\n\nstatic void\ndhcp6_failconfirm(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tint llevel = dhcp6_failloglevel(ifp);\n\n\tlogmessage(llevel, \"%s: failed to confirm prior DHCPv6 address\",\n\t    ifp->name);\n\teloop_timeout_delete(ifp->ctx->eloop, dhcp6_sendconfirm, ifp);\n\n\t/* RFC8415 18.2.3 says that prior addresses SHOULD be used on failure.\n\t */\n\tdhcp6_fail(ifp, false);\n}\n\nstatic void\ndhcp6_failrequest(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tint llevel = dhcp6_failloglevel(ifp);\n\n\tlogmessage(llevel, \"%s: failed to request DHCPv6 address\", ifp->name);\n\tdhcp6_fail(ifp, true);\n}\n\nstatic void\ndhcp6_failinform(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tint llevel = dhcp6_failloglevel(ifp);\n\n\tlogmessage(llevel, \"%s: failed to request DHCPv6 information\",\n\t    ifp->name);\n\tdhcp6_fail(ifp, true);\n}\n\n#ifndef SMALL\nstatic void\ndhcp6_failrebindpd(void *arg)\n{\n\tstruct interface *ifp = arg;\n\n\tlogerrx(\"%s: failed to rebind prior DHCPv6 delegation\", ifp->name);\n\teloop_timeout_delete(ifp->ctx->eloop, dhcp6_sendrebind, ifp);\n\n\t/* RFC8415 18.2.3 says that prior addresses SHOULD be used on failure.\n\t * 18.2 says REBIND rather than CONFIRM with PD but use CONFIRM timings.\n\t */\n\tdhcp6_fail(ifp, false);\n}\n\nstatic int\ndhcp6_hasprefixdelegation(struct interface *ifp)\n{\n\tsize_t i;\n\tuint16_t t;\n\n\tt = 0;\n\tfor (i = 0; i < ifp->options->ia_len; i++) {\n\t\tif (t && t != ifp->options->ia[i].ia_type) {\n\t\t\tif (t == D6_OPTION_IA_PD ||\n\t\t\t    ifp->options->ia[i].ia_type == D6_OPTION_IA_PD)\n\t\t\t\treturn 2;\n\t\t}\n\t\tt = ifp->options->ia[i].ia_type;\n\t}\n\treturn t == D6_OPTION_IA_PD ? 1 : 0;\n}\n#endif\n\nstatic void\ndhcp6_startrebind(void *arg)\n{\n\tstruct interface *ifp;\n\tstruct dhcp6_state *state;\n\n\tifp = arg;\n\teloop_timeout_delete(ifp->ctx->eloop, dhcp6_sendrenew, ifp);\n\tstate = D6_STATE(ifp);\n\n\tstate->IMD = REB_MAX_DELAY;\n\tstate->IRT = REB_TIMEOUT;\n\tstate->MRT = REB_MAX_RT;\n\tstate->RTC = 0;\n\tstate->MRC = 0;\n\n\tif (state->state == DH6S_RENEW)\n\t\tlogwarnx(\"%s: failed to renew DHCPv6, rebinding\", ifp->name);\n\telse {\n\t\tloginfox(\"%s: rebinding prior DHCPv6 lease\", ifp->name);\n\n#ifndef SMALL\n\t\t/* RFC 8415 18.2.5 */\n\t\tif (dhcp6_hasprefixdelegation(ifp)) {\n\t\t\tstate->IMD = CNF_MAX_DELAY;\n\t\t\tstate->IRT = CNF_TIMEOUT;\n\t\t\tstate->MRT = CNF_MAX_RT;\n\t\t\teloop_timeout_add_sec(ifp->ctx->eloop, CNF_MAX_RD,\n\t\t\t    dhcp6_failrebindpd, ifp);\n\t\t}\n#endif\n\t}\n\n\tstate->state = DH6S_REBIND;\n\tif (dhcp6_makemessage(ifp) == -1)\n\t\tlogerr(\"%s: %s\", __func__, ifp->name);\n\telse\n\t\tdhcp6_sendrebind(ifp);\n}\n\nstatic void\ndhcp6_startrequest(struct interface *ifp)\n{\n\tstruct dhcp6_state *state;\n\n\teloop_timeout_delete(ifp->ctx->eloop, dhcp6_senddiscover, ifp);\n\tstate = D6_STATE(ifp);\n\tstate->state = DH6S_REQUEST;\n\tstate->RTC = 0;\n\tstate->IMD = 0;\n\tstate->IRT = REQ_TIMEOUT;\n\tstate->MRT = REQ_MAX_RT;\n\tstate->MRC = REQ_MAX_RC;\n\tstate->MRCcallback = dhcp6_failrequest;\n\n\tif (dhcp6_makemessage(ifp) == -1) {\n\t\tlogerr(\"%s: %s\", __func__, ifp->name);\n\t\treturn;\n\t}\n\n\tdhcp6_sendrequest(ifp);\n}\n\nstatic void\ndhcp6_startconfirm(struct interface *ifp)\n{\n\tstruct dhcp6_state *state;\n\tstruct ipv6_addr *ia;\n\n\tstate = D6_STATE(ifp);\n\n\tTAILQ_FOREACH(ia, &state->addrs, next) {\n\t\tif (!DECLINE_IA(ia))\n\t\t\tcontinue;\n\t\tlogerrx(\"%s: prior DHCPv6 has a duplicated address\", ifp->name);\n\t\tdhcp6_startdecline(ifp);\n\t\treturn;\n\t}\n\n\tstate->state = DH6S_CONFIRM;\n\tstate->RTC = 0;\n\tstate->IMD = CNF_MAX_DELAY;\n\tstate->IRT = CNF_TIMEOUT;\n\tstate->MRT = CNF_MAX_RT;\n\tstate->MRC = CNF_MAX_RC;\n\n\tloginfox(\"%s: confirming prior DHCPv6 lease\", ifp->name);\n\n\tif (dhcp6_makemessage(ifp) == -1) {\n\t\tlogerr(\"%s: %s\", __func__, ifp->name);\n\t\treturn;\n\t}\n\tdhcp6_sendconfirm(ifp);\n\teloop_timeout_add_sec(ifp->ctx->eloop, CNF_MAX_RD, dhcp6_failconfirm,\n\t    ifp);\n}\n\nstatic void\ndhcp6_startexpire(void *arg)\n{\n\tstruct interface *ifp;\n\n\tifp = arg;\n\teloop_timeout_delete(ifp->ctx->eloop, dhcp6_sendrebind, ifp);\n\n\tlogerrx(\"%s: DHCPv6 lease expired\", ifp->name);\n\tdhcp6_fail(ifp, true);\n}\n\nstatic void\ndhcp6_faildecline(void *arg)\n{\n\tstruct interface *ifp = arg;\n\n\tlogerrx(\"%s: failed to decline duplicated DHCPv6 addresses\", ifp->name);\n\tdhcp6_fail(ifp, true);\n}\n\nstatic void\ndhcp6_startdecline(struct interface *ifp)\n{\n\tstruct dhcp6_state *state;\n\n\tstate = D6_STATE(ifp);\n\tloginfox(\"%s: declining failed DHCPv6 addresses\", ifp->name);\n\tstate->state = DH6S_DECLINE;\n\tstate->RTC = 0;\n\tstate->IMD = 0;\n\tstate->IRT = DEC_TIMEOUT;\n\tstate->MRT = 0;\n\tstate->MRC = DEC_MAX_RC;\n\tstate->MRCcallback = dhcp6_faildecline;\n\n\tif (dhcp6_makemessage(ifp) == -1)\n\t\tlogerr(\"%s: %s\", __func__, ifp->name);\n\telse\n\t\tdhcp6_senddecline(ifp);\n}\n\nstatic void\ndhcp6_finishrelease(void *arg)\n{\n\tstruct interface *ifp;\n\tstruct dhcp6_state *state;\n\n\tifp = (struct interface *)arg;\n\tif ((state = D6_STATE(ifp)) != NULL) {\n\t\tstate->state = DH6S_RELEASED;\n\t\tdhcp6_drop(ifp, \"RELEASE6\");\n\t}\n}\n\nstatic void\ndhcp6_startrelease(struct interface *ifp)\n{\n\tstruct dhcp6_state *state;\n\n\tstate = D6_STATE(ifp);\n\tif (state->state != DH6S_BOUND) {\n\t\tdhcp6_finishrelease(ifp);\n\t\treturn;\n\t}\n\n\tstate->state = DH6S_RELEASE;\n\tstate->RTC = 0;\n\tstate->IMD = REL_MAX_DELAY;\n\tstate->IRT = REL_TIMEOUT;\n\tstate->MRT = REL_MAX_RT;\n\tstate->MRC = REL_MAX_RC;\n\tstate->MRCcallback = dhcp6_finishrelease;\n\n\tif (dhcp6_makemessage(ifp) == -1) {\n\t\tlogerr(\"%s: %s\", __func__, ifp->name);\n\t\t/* not much we can do apart from finish now */\n\t\tdhcp6_finishrelease(ifp);\n\t} else\n\t\tdhcp6_sendrelease(ifp);\n}\n\nstatic int\ndhcp6_checkstatusok(const struct interface *ifp, struct dhcp6_message *m,\n    uint8_t *p, size_t len)\n{\n\tstruct dhcp6_state *state;\n\tuint8_t *opt;\n\tuint16_t opt_len, code;\n\tsize_t mlen;\n\tvoid *(*f)(void *, size_t, uint16_t, uint16_t *), *farg;\n\tchar buf[32], *sbuf;\n\tconst char *status;\n\tint loglevel;\n\n\tstate = D6_STATE(ifp);\n\tf = p ? dhcp6_findoption : dhcp6_findmoption;\n\tif (p)\n\t\tfarg = p;\n\telse\n\t\tfarg = m;\n\tif ((opt = f(farg, len, D6_OPTION_STATUS_CODE, &opt_len)) == NULL) {\n\t\t// logdebugx(\"%s: no status\", ifp->name);\n\t\tstate->lerror = 0;\n\t\terrno = ESRCH;\n\t\treturn 0;\n\t}\n\n\tif (opt_len < sizeof(code)) {\n\t\tlogerrx(\"%s: status truncated\", ifp->name);\n\t\treturn -1;\n\t}\n\tmemcpy(&code, opt, sizeof(code));\n\tcode = ntohs(code);\n\tif (code == D6_STATUS_OK) {\n\t\tstate->lerror = 0;\n\t\terrno = 0;\n\t\treturn 0;\n\t}\n\n\t/* Anything after the code is a message. */\n\topt += sizeof(code);\n\tmlen = opt_len - sizeof(code);\n\tif (mlen == 0) {\n\t\tsbuf = NULL;\n\t\tif (code < sizeof(dhcp6_statuses) / sizeof(char *))\n\t\t\tstatus = dhcp6_statuses[code];\n\t\telse {\n\t\t\tsnprintf(buf, sizeof(buf), \"Unknown Status (%d)\", code);\n\t\t\tstatus = buf;\n\t\t}\n\t} else {\n\t\tif ((sbuf = malloc(mlen + 1)) == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\treturn -1;\n\t\t}\n\t\tmemcpy(sbuf, opt, mlen);\n\t\tsbuf[mlen] = '\\0';\n\t\tstatus = sbuf;\n\t}\n\n\tif (state->lerror == code || state->state == DH6S_INIT)\n\t\tloglevel = LOG_DEBUG;\n\telse\n\t\tloglevel = LOG_ERR;\n\tlogmessage(loglevel, \"%s: DHCPv6 REPLY: %s\", ifp->name, status);\n\tfree(sbuf);\n\tstate->lerror = code;\n\terrno = 0;\n\n\t/* RFC 8415 18.2.10 */\n\tif (code == D6_STATUS_USEMULTICAST) {\n\t\tlogdebugx(\"%s: server sent USEMULTICAST\", ifp->name);\n\t\tstate->unicast = in6addr_any;\n\t}\n\n\t/* code cannot be D6_STATUS_OK, so there is a failure */\n\tif (ifp->ctx->options & DHCPCD_TEST)\n\t\teloop_exit(ifp->ctx->eloop, EXIT_FAILURE);\n\n\treturn (int)code;\n}\n\nconst struct ipv6_addr *\ndhcp6_iffindaddr(const struct interface *ifp, const struct in6_addr *addr,\n    unsigned int flags)\n{\n\tconst struct dhcp6_state *state;\n\tconst struct ipv6_addr *ap;\n\n\tif ((state = D6_STATE(ifp)) != NULL) {\n\t\tTAILQ_FOREACH(ap, &state->addrs, next) {\n\t\t\tif (ipv6_findaddrmatch(ap, addr, flags))\n\t\t\t\treturn ap;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstruct ipv6_addr *\ndhcp6_findaddr(struct dhcpcd_ctx *ctx, const struct in6_addr *addr,\n    unsigned int flags)\n{\n\tstruct interface *ifp;\n\tstruct ipv6_addr *ap;\n\tstruct dhcp6_state *state;\n\n\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\tif ((state = D6_STATE(ifp)) != NULL) {\n\t\t\tTAILQ_FOREACH(ap, &state->addrs, next) {\n\t\t\t\tif (ipv6_findaddrmatch(ap, addr, flags))\n\t\t\t\t\treturn ap;\n\t\t\t}\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic int\ndhcp6_findna(struct interface *ifp, uint16_t ot, const uint8_t *iaid,\n    uint8_t *d, size_t l, const struct timespec *acquired)\n{\n\tstruct dhcp6_state *state;\n\tuint8_t *o, *nd;\n\tuint16_t ol;\n\tstruct ipv6_addr *a;\n\tint i;\n\tstruct dhcp6_ia_addr ia;\n\n\ti = 0;\n\tstate = D6_STATE(ifp);\n\twhile ((o = dhcp6_findoption(d, l, D6_OPTION_IA_ADDR, &ol))) {\n\t\t/* Set d and l first to ensure we find the next option. */\n\t\tnd = o + ol;\n\t\tl -= (size_t)(nd - d);\n\t\td = nd;\n\t\tif (ol < sizeof(ia)) {\n\t\t\terrno = EINVAL;\n\t\t\tlogerrx(\"%s: IA Address option truncated\", ifp->name);\n\t\t\tcontinue;\n\t\t}\n\t\tmemcpy(&ia, o, sizeof(ia));\n\t\tia.pltime = ntohl(ia.pltime);\n\t\tia.vltime = ntohl(ia.vltime);\n\t\t/* RFC 3315 22.6 */\n\t\tif (ia.pltime > ia.vltime) {\n\t\t\terrno = EINVAL;\n\t\t\tlogerr(\"%s: IA Address pltime %\" PRIu32\n\t\t\t       \" > vltime %\" PRIu32,\n\t\t\t    ifp->name, ia.pltime, ia.vltime);\n\t\t\tcontinue;\n\t\t}\n\t\tTAILQ_FOREACH(a, &state->addrs, next) {\n\t\t\tif (ipv6_findaddrmatch(a, &ia.addr, 0))\n\t\t\t\tbreak;\n\t\t}\n\t\tif (a == NULL) {\n\t\t\t/*\n\t\t\t * RFC 5942 Section 5\n\t\t\t * We cannot assume any prefix length, nor tie the\n\t\t\t * address to an existing one as it could expire\n\t\t\t * before the address.\n\t\t\t * As such we just give it a 128 prefix.\n\t\t\t */\n\t\t\ta = ipv6_newaddr(ifp, &ia.addr, 128, IPV6_AF_ONLINK);\n\t\t\ta->dadcallback = dhcp6_dadcallback;\n\t\t\ta->ia_type = ot;\n\t\t\tmemcpy(a->iaid, iaid, sizeof(a->iaid));\n\t\t\ta->created = *acquired;\n\n\t\t\tTAILQ_INSERT_TAIL(&state->addrs, a, next);\n\t\t} else {\n\t\t\tif (!(a->flags & IPV6_AF_ONLINK))\n\t\t\t\ta->flags |= IPV6_AF_ONLINK | IPV6_AF_NEW;\n\t\t\ta->flags &= ~(IPV6_AF_STALE | IPV6_AF_EXTENDED);\n\t\t}\n\t\ta->acquired = *acquired;\n\t\ta->prefix_pltime = ia.pltime;\n\t\tif (a->prefix_vltime != ia.vltime) {\n\t\t\ta->flags |= IPV6_AF_NEW;\n\t\t\ta->prefix_vltime = ia.vltime;\n\t\t}\n\t\tif (a->prefix_pltime && a->prefix_pltime < state->lowpl)\n\t\t\tstate->lowpl = a->prefix_pltime;\n\t\tif (a->prefix_vltime && a->prefix_vltime > state->expire)\n\t\t\tstate->expire = a->prefix_vltime;\n\t\ti++;\n\t}\n\treturn i;\n}\n\n#ifndef SMALL\nstatic int\ndhcp6_findpd(struct interface *ifp, const uint8_t *iaid, uint8_t *d, size_t l,\n    const struct timespec *acquired)\n{\n\tstruct dhcp6_state *state;\n\tuint8_t *o, *nd;\n\tstruct ipv6_addr *a;\n\tint i;\n\tuint8_t nb, *pw;\n\tuint16_t ol;\n\tuint32_t pdp_vltime, pdp_pltime;\n\tuint8_t pdp_plen;\n\tstruct in6_addr pdp_prefix;\n\n\ti = 0;\n\tstate = D6_STATE(ifp);\n\twhile ((o = dhcp6_findoption(d, l, D6_OPTION_IAPREFIX, &ol))) {\n\t\t/* Set d and l first to ensure we find the next option. */\n\t\tnd = o + ol;\n\t\tl -= (size_t)(nd - d);\n\t\td = nd;\n\t\tif (ol < DHCP6_PD_ADDR_SIZE) {\n\t\t\terrno = EINVAL;\n\t\t\tlogerrx(\"%s: IA Prefix option truncated\", ifp->name);\n\t\t\tcontinue;\n\t\t}\n\n\t\tmemcpy(&pdp_pltime, o, sizeof(pdp_pltime));\n\t\to += sizeof(pdp_pltime);\n\t\tmemcpy(&pdp_vltime, o, sizeof(pdp_vltime));\n\t\to += sizeof(pdp_vltime);\n\t\tmemcpy(&pdp_plen, o, sizeof(pdp_plen));\n\t\to += sizeof(pdp_plen);\n\n\t\tpdp_pltime = ntohl(pdp_pltime);\n\t\tpdp_vltime = ntohl(pdp_vltime);\n\t\t/* RFC 3315 22.6 */\n\t\tif (pdp_pltime > pdp_vltime) {\n\t\t\terrno = EINVAL;\n\t\t\tlogerrx(\"%s: IA Prefix pltime %\" PRIu32\n\t\t\t\t\" > vltime %\" PRIu32,\n\t\t\t    ifp->name, pdp_pltime, pdp_vltime);\n\t\t\tcontinue;\n\t\t}\n\n\t\tmemcpy(&pdp_prefix, o, sizeof(pdp_prefix));\n\t\to += sizeof(pdp_prefix);\n\t\tol = (uint16_t)(ol - sizeof(pdp_pltime) - sizeof(pdp_vltime) -\n\t\t    sizeof(pdp_plen) - sizeof(pdp_prefix));\n\n\t\tTAILQ_FOREACH(a, &state->addrs, next) {\n\t\t\tif (IN6_ARE_ADDR_EQUAL(&a->prefix, &pdp_prefix))\n\t\t\t\tbreak;\n\t\t}\n\n\t\tif (a == NULL) {\n\t\t\ta = ipv6_newaddr(ifp, &pdp_prefix, pdp_plen,\n\t\t\t    IPV6_AF_PFXDELEGATION);\n\t\t\tif (a == NULL)\n\t\t\t\tbreak;\n\t\t\ta->created = *acquired;\n\t\t\ta->dadcallback = dhcp6_dadcallback;\n\t\t\ta->ia_type = D6_OPTION_IA_PD;\n\t\t\tmemcpy(a->iaid, iaid, sizeof(a->iaid));\n\t\t\tTAILQ_INSERT_TAIL(&state->addrs, a, next);\n\t\t} else {\n\t\t\tif (!(a->flags & IPV6_AF_PFXDELEGATION))\n\t\t\t\ta->flags |= IPV6_AF_NEW | IPV6_AF_PFXDELEGATION;\n\t\t\ta->flags &= ~(IPV6_AF_STALE | IPV6_AF_EXTENDED);\n\t\t\tif (a->prefix_vltime != pdp_vltime)\n\t\t\t\ta->flags |= IPV6_AF_NEW;\n\t\t}\n\n\t\ta->acquired = *acquired;\n\t\ta->prefix_pltime = pdp_pltime;\n\t\ta->prefix_vltime = pdp_vltime;\n\n\t\tif (a->prefix_pltime && a->prefix_pltime < state->lowpl)\n\t\t\tstate->lowpl = a->prefix_pltime;\n\t\tif (a->prefix_vltime && a->prefix_vltime > state->expire)\n\t\t\tstate->expire = a->prefix_vltime;\n\t\ti++;\n\n\t\ta->prefix_exclude_len = 0;\n\t\tmemset(&a->prefix_exclude, 0, sizeof(a->prefix_exclude));\n\t\to = dhcp6_findoption(o, ol, D6_OPTION_PD_EXCLUDE, &ol);\n\t\tif (o == NULL)\n\t\t\tcontinue;\n\n\t\t/* RFC 6603 4.2 says option length MUST be between 2 and 17.\n\t\t * This allows 1 octet for prefix length and 16 for the\n\t\t * subnet ID. */\n\t\tif (ol < 2 || ol > 17) {\n\t\t\tlogerrx(\"%s: invalid PD Exclude option\", ifp->name);\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* RFC 6603 4.2 says prefix length MUST be between the\n\t\t * length of the IAPREFIX prefix length + 1 and 128. */\n\t\tif (*o < a->prefix_len + 1 || *o > 128) {\n\t\t\tlogerrx(\"%s: invalid PD Exclude length\", ifp->name);\n\t\t\tcontinue;\n\t\t}\n\n\t\tol--;\n\t\t/* Check option length matches prefix length. */\n\t\tif (((*o - a->prefix_len - 1) / NBBY) + 1 != ol) {\n\t\t\tlogerrx(\"%s: PD Exclude length mismatch\", ifp->name);\n\t\t\tcontinue;\n\t\t}\n\t\ta->prefix_exclude_len = *o++;\n\n\t\tmemcpy(&a->prefix_exclude, &a->prefix,\n\t\t    sizeof(a->prefix_exclude));\n\t\tnb = a->prefix_len % NBBY;\n\t\tif (nb)\n\t\t\tol--;\n\t\tpw = a->prefix_exclude.s6_addr +\n\t\t    (a->prefix_exclude_len / NBBY) - 1;\n\t\twhile (ol-- > 0)\n\t\t\t*pw-- = *o++;\n\t\tif (nb)\n\t\t\t*pw = (uint8_t)(*pw | (*o >> nb));\n\t}\n\treturn i;\n}\n#endif\n\nstatic int\ndhcp6_findia(struct interface *ifp, struct dhcp6_message *m, size_t l,\n    const char *sfrom, const struct timespec *acquired)\n{\n\tstruct dhcp6_state *state;\n\tconst struct if_options *ifo;\n\tstruct dhcp6_option o;\n\tuint8_t *d, *p;\n\tstruct dhcp6_ia_na ia;\n\tint i, e, error;\n\tsize_t j;\n\tuint16_t nl;\n\tuint8_t iaid[4];\n\tchar buf[sizeof(iaid) * 3];\n\tstruct ipv6_addr *ap;\n\tstruct if_ia *ifia;\n\n\tif (l < sizeof(*m)) {\n\t\t/* Should be impossible with guards at packet in\n\t\t * and reading leases */\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tifo = ifp->options;\n\ti = e = 0;\n\tstate = D6_STATE(ifp);\n\tTAILQ_FOREACH(ap, &state->addrs, next) {\n\t\t/* Anything not from a lease for this interface should be\n\t\t * marked as stale. */\n\t\tif (!(ap->flags & IPV6_AF_DELEGATED))\n\t\t\tap->flags |= IPV6_AF_STALE;\n\t}\n\n\td = (uint8_t *)m + sizeof(*m);\n\tl -= sizeof(*m);\n\twhile (l > sizeof(o)) {\n\t\tmemcpy(&o, d, sizeof(o));\n\t\to.len = ntohs(o.len);\n\t\tif (o.len > l || sizeof(o) + o.len > l) {\n\t\t\terrno = EINVAL;\n\t\t\tlogerrx(\"%s: option overflow\", ifp->name);\n\t\t\tbreak;\n\t\t}\n\t\tp = d + sizeof(o);\n\t\td = p + o.len;\n\t\tl -= sizeof(o) + o.len;\n\n\t\to.code = ntohs(o.code);\n\t\tswitch (o.code) {\n\t\tcase D6_OPTION_IA_TA:\n\t\t\tnl = 4;\n\t\t\tbreak;\n\t\tcase D6_OPTION_IA_NA:\n\t\tcase D6_OPTION_IA_PD:\n\t\t\tnl = 12;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tcontinue;\n\t\t}\n\t\tif (o.len < nl) {\n\t\t\terrno = EINVAL;\n\t\t\tlogerrx(\"%s: IA option truncated\", ifp->name);\n\t\t\tcontinue;\n\t\t}\n\n\t\tmemcpy(&ia, p, nl);\n\t\tp += nl;\n\t\to.len = (uint16_t)(o.len - nl);\n\n\t\tfor (j = 0; j < ifo->ia_len; j++) {\n\t\t\tifia = &ifo->ia[j];\n\t\t\tif (ifia->ia_type == o.code &&\n\t\t\t    memcmp(ifia->iaid, ia.iaid, sizeof(ia.iaid)) == 0)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (j == ifo->ia_len &&\n\t\t    !(ifo->ia_len == 0 &&\n\t\t\tifp->ctx->options & DHCPCD_DUMPLEASE)) {\n\t\t\tlogdebugx(\"%s: ignoring unrequested IAID %s\", ifp->name,\n\t\t\t    hwaddr_ntoa(ia.iaid, sizeof(ia.iaid), buf,\n\t\t\t\tsizeof(buf)));\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (o.code != D6_OPTION_IA_TA) {\n\t\t\tia.t1 = ntohl(ia.t1);\n\t\t\tia.t2 = ntohl(ia.t2);\n\t\t\t/* RFC 3315 22.4 */\n\t\t\tif (ia.t2 > 0 && ia.t1 > ia.t2) {\n\t\t\t\tlogwarnx(\"%s: IAID %s T1(%d) > T2(%d) from %s\",\n\t\t\t\t    ifp->name,\n\t\t\t\t    hwaddr_ntoa(iaid, sizeof(iaid), buf,\n\t\t\t\t\tsizeof(buf)),\n\t\t\t\t    ia.t1, ia.t2, sfrom);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t} else\n\t\t\tia.t1 = ia.t2 = 0; /* appease gcc */\n\t\tif ((error = dhcp6_checkstatusok(ifp, NULL, p, o.len)) != 0) {\n\t\t\tif (error == D6_STATUS_NOBINDING)\n\t\t\t\tstate->has_no_binding = true;\n\t\t\te = 1;\n\t\t\tcontinue;\n\t\t}\n\t\tif (o.code == D6_OPTION_IA_PD) {\n#ifndef SMALL\n\t\t\tif (dhcp6_findpd(ifp, ia.iaid, p, o.len, acquired) ==\n\t\t\t    0) {\n\t\t\t\tlogwarnx(\"%s: %s: DHCPv6 REPLY missing Prefix\",\n\t\t\t\t    ifp->name, sfrom);\n\t\t\t\tcontinue;\n\t\t\t}\n#endif\n\t\t} else {\n\t\t\tif (dhcp6_findna(ifp, o.code, ia.iaid, p, o.len,\n\t\t\t\tacquired) == 0) {\n\t\t\t\tlogwarnx(\"%s: %s: DHCPv6 REPLY missing \"\n\t\t\t\t\t \"IA Address\",\n\t\t\t\t    ifp->name, sfrom);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tif (o.code != D6_OPTION_IA_TA) {\n\t\t\tif (ia.t1 != 0 &&\n\t\t\t    (ia.t1 < state->renew || state->renew == 0))\n\t\t\t\tstate->renew = ia.t1;\n\t\t\tif (ia.t2 != 0 &&\n\t\t\t    (ia.t2 < state->rebind || state->rebind == 0))\n\t\t\t\tstate->rebind = ia.t2;\n\t\t}\n\t\ti++;\n\t}\n\n\tif (i == 0 && e)\n\t\treturn -1;\n\treturn i;\n}\n\n#ifndef SMALL\nstatic void\ndhcp6_deprecatedele(struct ipv6_addr *ia)\n{\n\tstruct ipv6_addr *da, *dan, *dda;\n\tstruct timespec now;\n\tstruct dhcp6_state *state;\n\n\ttimespecclear(&now);\n\tTAILQ_FOREACH_SAFE(da, &ia->pd_pfxs, pd_next, dan) {\n\t\tif (ia->prefix_vltime == 0) {\n\t\t\tif (da->prefix_vltime != 0)\n\t\t\t\tda->prefix_vltime = 0;\n\t\t\telse\n\t\t\t\tcontinue;\n\t\t} else if (da->prefix_pltime != 0)\n\t\t\tda->prefix_pltime = 0;\n\t\telse\n\t\t\tcontinue;\n\n\t\tif (ipv6_doaddr(da, &now) != -1)\n\t\t\tcontinue;\n\n\t\t/* Delegation deleted, forget it. */\n\t\tTAILQ_REMOVE(&ia->pd_pfxs, da, pd_next);\n\n\t\t/* Delete it from the interface. */\n\t\tstate = D6_STATE(da->iface);\n\t\tTAILQ_FOREACH(dda, &state->addrs, next) {\n\t\t\tif (IN6_ARE_ADDR_EQUAL(&dda->addr, &da->addr))\n\t\t\t\tbreak;\n\t\t}\n\t\tif (dda != NULL) {\n\t\t\tTAILQ_REMOVE(&state->addrs, dda, next);\n\t\t\tipv6_freeaddr(dda);\n\t\t}\n\t}\n}\n#endif\n\nstatic void\ndhcp6_deprecateaddrs(struct ipv6_addrhead *addrs)\n{\n\tstruct ipv6_addr *ia, *ian;\n\n\tTAILQ_FOREACH_SAFE(ia, addrs, next, ian) {\n\t\tif (ia->flags & IPV6_AF_EXTENDED)\n\t\t\t;\n\t\telse if (ia->flags & IPV6_AF_STALE) {\n\t\t\tif (ia->prefix_vltime != 0)\n\t\t\t\tlogdebugx(\"%s: %s: became stale\",\n\t\t\t\t    ia->iface->name, ia->saddr);\n\t\t\t/* Technically this violates RFC 8415 18.2.10.1,\n\t\t\t * but we need a mechanism to tell the kernel to\n\t\t\t * try and prefer other addresses. */\n\t\t\tia->prefix_pltime = 0;\n\t\t} else if (ia->prefix_vltime == 0)\n\t\t\tloginfox(\"%s: %s: no valid lifetime\", ia->iface->name,\n\t\t\t    ia->saddr);\n\t\telse\n\t\t\tcontinue;\n\n#ifndef SMALL\n\t\t/* If we delegated from this prefix, deprecate or remove\n\t\t * the delegations. */\n\t\tif (ia->flags & IPV6_AF_PFXDELEGATION)\n\t\t\tdhcp6_deprecatedele(ia);\n#endif\n\n\t\tif (ia->flags & IPV6_AF_REQUEST) {\n\t\t\tia->prefix_vltime = ia->prefix_pltime = 0;\n\t\t\teloop_q_timeout_delete(ia->iface->ctx->eloop,\n\t\t\t    ELOOP_QUEUE_ALL, NULL, ia);\n\t\t\tcontinue;\n\t\t}\n\t\tTAILQ_REMOVE(addrs, ia, next);\n\t\tif (!(ia->flags & IPV6_AF_EXTENDED))\n\t\t\tipv6_deleteaddr(ia);\n\t\tipv6_freeaddr(ia);\n\t}\n}\n\nstatic int\ndhcp6_validatelease(struct interface *ifp, struct dhcp6_message *m, size_t len,\n    const char *sfrom, const struct timespec *acquired)\n{\n\tstruct dhcp6_state *state;\n\tint nia, ok_errno;\n\tstruct timespec aq;\n\n\tif (len <= sizeof(*m)) {\n\t\tlogerrx(\"%s: DHCPv6 lease truncated\", ifp->name);\n\t\treturn -1;\n\t}\n\n\tstate = D6_STATE(ifp);\n\terrno = 0;\n\tif (dhcp6_checkstatusok(ifp, m, NULL, len) != 0)\n\t\treturn -1;\n\tok_errno = errno;\n\n\tstate->renew = state->rebind = state->expire = 0;\n\tstate->lowpl = ND6_INFINITE_LIFETIME;\n\tif (!acquired) {\n\t\tclock_gettime(CLOCK_MONOTONIC, &aq);\n\t\tacquired = &aq;\n\t}\n\tstate->has_no_binding = false;\n\tnia = dhcp6_findia(ifp, m, len, sfrom, acquired);\n\tif (nia == 0 && state->state == DH6S_CONFIRM && ok_errno == 0 &&\n\t    state->new && state->new_len) {\n\t\tstate->has_no_binding = false;\n\t\tnia = dhcp6_findia(ifp, state->new, state->new_len, sfrom,\n\t\t    acquired);\n\t}\n\tif (nia == 0) {\n\t\tlogerrx(\"%s: no useable IA found in lease\", ifp->name);\n\t\treturn -1;\n\t}\n\treturn nia;\n}\n\nstatic ssize_t\ndhcp6_readlease(struct interface *ifp, int validate)\n{\n\tunion {\n\t\tstruct dhcp6_message dhcp6;\n\t\tuint8_t buf[UDPLEN_MAX];\n\t} buf;\n\tstruct dhcp6_state *state;\n\tssize_t bytes;\n\tint fd;\n\ttime_t mtime, now;\n#ifdef AUTH\n\tuint8_t *o;\n\tuint16_t ol;\n#endif\n\n\tstate = D6_STATE(ifp);\n\tif (state->leasefile[0] == '\\0') {\n\t\tlogdebugx(\"reading standard input\");\n\t\tbytes = read(fileno(stdin), buf.buf, sizeof(buf.buf));\n\t} else {\n\t\tlogdebugx(\"%s: reading lease: %s\", ifp->name, state->leasefile);\n\t\tbytes = dhcp_readfile(ifp->ctx, state->leasefile, buf.buf,\n\t\t    sizeof(buf.buf));\n\t}\n\tif (bytes == -1)\n\t\tgoto ex;\n\n\tif (ifp->ctx->options & DHCPCD_DUMPLEASE || state->leasefile[0] == '\\0')\n\t\tgoto out;\n\n\tif (bytes == 0)\n\t\tgoto ex;\n\n\t/* If not validating IA's and if they have expired,\n\t * skip to the auth check. */\n\tif (!validate)\n\t\tgoto auth;\n\n\tif (dhcp_filemtime(ifp->ctx, state->leasefile, &mtime) == -1)\n\t\tgoto ex;\n\tclock_gettime(CLOCK_MONOTONIC, &state->acquired);\n\tif ((now = time(NULL)) == -1)\n\t\tgoto ex;\n\tstate->acquired.tv_sec -= now - mtime;\n\n\t/* Check to see if the lease is still valid */\n\tfd = dhcp6_validatelease(ifp, &buf.dhcp6, (size_t)bytes, NULL,\n\t    &state->acquired);\n\tif (fd == -1) {\n\t\tbytes = 0; /* We have already reported the error */\n\t\tgoto ex;\n\t}\n\n\tif (state->expire != ND6_INFINITE_LIFETIME &&\n\t    (time_t)state->expire < now - mtime) {\n\t\tlogdebugx(\"%s: discarding expired lease\", ifp->name);\n\t\tbytes = 0;\n\t\tgoto ex;\n\t}\n\nauth:\n#ifdef AUTH\n\t/* Authenticate the message */\n\to = dhcp6_findmoption(&buf.dhcp6, (size_t)bytes, D6_OPTION_AUTH, &ol);\n\tif (o) {\n\t\tif (dhcp_auth_validate(&state->auth, &ifp->options->auth,\n\t\t\tbuf.buf, (size_t)bytes, 6, buf.dhcp6.type, o,\n\t\t\tol) == NULL) {\n\t\t\tlogerr(\"%s: authentication failed\", ifp->name);\n\t\t\tbytes = 0;\n\t\t\tgoto ex;\n\t\t}\n\t\tif (state->auth.token)\n\t\t\tlogdebugx(\"%s: validated using 0x%08\" PRIu32, ifp->name,\n\t\t\t    state->auth.token->secretid);\n\t\telse\n\t\t\tloginfox(\"%s: accepted reconfigure key\", ifp->name);\n\t} else if ((ifp->options->auth.options & DHCPCD_AUTH_SENDREQUIRE) ==\n\t    DHCPCD_AUTH_SENDREQUIRE) {\n\t\tlogerrx(\"%s: authentication now required\", ifp->name);\n\t\tgoto ex;\n\t}\n#endif\n\nout:\n\tfree(state->new);\n\tstate->new = malloc((size_t)bytes);\n\tif (state->new == NULL) {\n\t\tlogerr(__func__);\n\t\tgoto ex;\n\t}\n\n\tmemcpy(state->new, buf.buf, (size_t)bytes);\n\tstate->new_len = (size_t)bytes;\n\treturn bytes;\n\nex:\n\tdhcp6_freedrop_addrs(ifp, 0, IPV6_AF_DELEGATED, NULL);\n\tdhcp_unlink(ifp->ctx, state->leasefile);\n\tfree(state->new);\n\tstate->new = NULL;\n\tstate->new_len = 0;\n\tdhcp6_addrequestedaddrs(ifp);\n\treturn bytes == 0 ? 0 : -1;\n}\n\nstatic void\ndhcp6_startinit(struct interface *ifp)\n{\n\tstruct dhcp6_state *state;\n\tstruct if_options *ifo;\n\tssize_t r;\n\tuint8_t has_ta, has_non_ta;\n\tsize_t i;\n\n\tstate = D6_STATE(ifp);\n\tifo = ifp->options;\n\tstate->expire = ND6_INFINITE_LIFETIME;\n\tstate->lowpl = ND6_INFINITE_LIFETIME;\n\n\tdhcp6_addrequestedaddrs(ifp);\n\thas_ta = has_non_ta = 0;\n\tfor (i = 0; i < ifo->ia_len; i++) {\n\t\tswitch (ifo->ia[i].ia_type) {\n\t\tcase D6_OPTION_IA_TA:\n\t\t\thas_ta = 1;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\thas_non_ta = 1;\n\t\t}\n\t}\n\n\tif (!(ifp->ctx->options & DHCPCD_TEST) && !(has_ta && !has_non_ta) &&\n\t    ifo->reboot != 0) {\n\t\tr = dhcp6_readlease(ifp, 1);\n\t\tif (r == -1) {\n\t\t\tif (errno != ENOENT && errno != ESRCH)\n\t\t\t\tlogerr(\"%s: %s\", __func__, state->leasefile);\n\t\t} else if (r != 0 && !(ifo->options & DHCPCD_ANONYMOUS)) {\n\t\t\t/* RFC 3633 section 12.1 */\n#ifndef SMALL\n\t\t\tif (state->state == DH6S_MANUALREBIND ||\n\t\t\t    dhcp6_hasprefixdelegation(ifp))\n\t\t\t\tdhcp6_startrebind(ifp);\n\t\t\telse\n#endif\n\t\t\t\tdhcp6_startconfirm(ifp);\n\t\t\treturn;\n\t\t}\n\t}\n\tdhcp6_startdiscoinform(ifp);\n}\n\n#ifndef SMALL\nstatic struct ipv6_addr *\ndhcp6_ifdelegateaddr(struct interface *ifp, struct ipv6_addr *prefix,\n    const struct if_sla *sla, struct if_ia *if_ia)\n{\n\tstruct dhcp6_state *state;\n\tstruct in6_addr addr, daddr;\n\tstruct ipv6_addr *ia;\n\tint pfxlen, dadcounter;\n\tuint64_t vl;\n\n\t/* RFC6603 Section 4.2 */\n\tif (strcmp(ifp->name, prefix->iface->name) == 0) {\n\t\tif (prefix->prefix_exclude_len == 0) {\n\t\t\t/* Don't spam the log automatically */\n\t\t\tif (sla != NULL)\n\t\t\t\tlogwarnx(\"%s: DHCPv6 server does not support \"\n\t\t\t\t\t \"OPTION_PD_EXCLUDE\",\n\t\t\t\t    ifp->name);\n\t\t\treturn NULL;\n\t\t}\n\t\tpfxlen = prefix->prefix_exclude_len;\n\t\tmemcpy(&addr, &prefix->prefix_exclude, sizeof(addr));\n\t} else if ((pfxlen = dhcp6_delegateaddr(&addr, ifp, prefix, sla,\n\t\t\tif_ia)) == -1)\n\t\treturn NULL;\n\n\tif (sla != NULL && fls64(sla->suffix) > 128 - pfxlen) {\n\t\tlogerrx(\"%s: suffix %\" PRIu64 \" + prefix_len %d > 128\",\n\t\t    ifp->name, sla->suffix, pfxlen);\n\t\treturn NULL;\n\t}\n\n\t/* Add our suffix */\n\tif (sla != NULL && sla->suffix != 0) {\n\t\tdaddr = addr;\n\t\tvl = be64dec(addr.s6_addr + 8);\n\t\tvl |= sla->suffix;\n\t\tbe64enc(daddr.s6_addr + 8, vl);\n\t} else {\n\t\tdadcounter = ipv6_makeaddr(&daddr, ifp, &addr, pfxlen, 0);\n\t\tif (dadcounter == -1) {\n\t\t\tlogerrx(\"%s: error adding slaac to prefix_len %d\",\n\t\t\t    ifp->name, pfxlen);\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\t/* Find an existing address */\n\tstate = D6_STATE(ifp);\n\tTAILQ_FOREACH(ia, &state->addrs, next) {\n\t\tif (IN6_ARE_ADDR_EQUAL(&ia->addr, &daddr))\n\t\t\tbreak;\n\t}\n\tif (ia == NULL) {\n\t\tia = ipv6_newaddr(ifp, &daddr, (uint8_t)pfxlen, IPV6_AF_ONLINK);\n\t\tif (ia == NULL)\n\t\t\treturn NULL;\n\t\tia->dadcallback = dhcp6_dadcallback;\n\t\tmemcpy(&ia->iaid, &prefix->iaid, sizeof(ia->iaid));\n\t\tia->created = prefix->acquired;\n\n\t\tTAILQ_INSERT_TAIL(&state->addrs, ia, next);\n\t\tTAILQ_INSERT_TAIL(&prefix->pd_pfxs, ia, pd_next);\n\t}\n\tia->delegating_prefix = prefix;\n\tia->prefix = addr;\n\tia->prefix_len = (uint8_t)pfxlen;\n\tia->acquired = prefix->acquired;\n\tia->prefix_pltime = prefix->prefix_pltime;\n\tia->prefix_vltime = prefix->prefix_vltime;\n\n\t/* If the prefix length hasn't changed,\n\t * don't install a reject route. */\n\tif (prefix->prefix_len == pfxlen)\n\t\tprefix->flags |= IPV6_AF_NOREJECT;\n\telse\n\t\tprefix->flags &= ~IPV6_AF_NOREJECT;\n\n\treturn ia;\n}\n#endif\n\nstatic void\ndhcp6_script_try_run(struct interface *ifp, int delegated)\n{\n\tstruct dhcp6_state *state;\n\tstruct ipv6_addr *ap;\n\tint completed;\n\n\tstate = D6_STATE(ifp);\n\tcompleted = 1;\n\t/* If all addresses have completed DAD run the script */\n\tTAILQ_FOREACH(ap, &state->addrs, next) {\n\t\tif (!(ap->flags & IPV6_AF_ADDED))\n\t\t\tcontinue;\n\t\tif (ap->flags & IPV6_AF_ONLINK) {\n\t\t\tif (!(ap->flags & IPV6_AF_DADCOMPLETED) &&\n\t\t\t    ipv6_iffindaddr(ap->iface, &ap->addr,\n\t\t\t\tIN6_IFF_TENTATIVE))\n\t\t\t\tap->flags |= IPV6_AF_DADCOMPLETED;\n\t\t\tif ((ap->flags & IPV6_AF_DADCOMPLETED) == 0\n#ifndef SMALL\n\t\t\t    && ((delegated && ap->delegating_prefix) ||\n\t\t\t\t   (!delegated && !ap->delegating_prefix))\n#endif\n\t\t\t) {\n\t\t\t\tcompleted = 0;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tif (completed) {\n\t\tscript_runreason(ifp, delegated ? \"DELEGATED6\" : state->reason);\n\t\tif (!delegated)\n\t\t\tdhcpcd_daemonise(ifp->ctx);\n\t} else\n\t\tlogdebugx(\"%s: waiting for DHCPv6 DAD to complete\", ifp->name);\n}\n\n#ifdef SMALL\nsize_t\ndhcp6_find_delegates(__unused struct interface *ifp)\n{\n\treturn 0;\n}\n#else\nstatic void\ndhcp6_delegate_prefix(struct interface *ifp)\n{\n\tstruct if_options *ifo;\n\tstruct dhcp6_state *state;\n\tstruct ipv6_addr *ap;\n\tsize_t i, j, k;\n\tstruct if_ia *ia;\n\tstruct if_sla *sla;\n\tstruct interface *ifd;\n\tbool carrier_warned;\n\n\tifo = ifp->options;\n\tstate = D6_STATE(ifp);\n\n\t/* Clear the logged flag. */\n\tTAILQ_FOREACH(ap, &state->addrs, next) {\n\t\tap->flags &= ~IPV6_AF_DELEGATEDLOG;\n\t}\n\n\tTAILQ_FOREACH(ifd, ifp->ctx->ifaces, next) {\n\t\tif (!ifd->active)\n\t\t\tcontinue;\n\t\tif (!(ifd->options->options & DHCPCD_CONFIGURE))\n\t\t\tcontinue;\n\t\tk = 0;\n\t\tcarrier_warned = false;\n\t\tTAILQ_FOREACH(ap, &state->addrs, next) {\n\t\t\tif (!(ap->flags & IPV6_AF_PFXDELEGATION))\n\t\t\t\tcontinue;\n\t\t\tif (!(ap->flags & IPV6_AF_DELEGATEDLOG)) {\n\t\t\t\tint loglevel;\n\n\t\t\t\tif (ap->flags & IPV6_AF_NEW)\n\t\t\t\t\tloglevel = LOG_INFO;\n\t\t\t\telse\n\t\t\t\t\tloglevel = LOG_DEBUG;\n\t\t\t\t/* We only want to log this the once as we loop\n\t\t\t\t * through many interfaces first. */\n\t\t\t\tap->flags |= IPV6_AF_DELEGATEDLOG;\n\t\t\t\tlogmessage(loglevel, \"%s: delegated prefix %s\",\n\t\t\t\t    ifp->name, ap->saddr);\n\t\t\t\tap->flags &= ~IPV6_AF_NEW;\n\t\t\t}\n\t\t\tfor (i = 0; i < ifo->ia_len; i++) {\n\t\t\t\tia = &ifo->ia[i];\n\t\t\t\tif (ia->ia_type != D6_OPTION_IA_PD)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (memcmp(ia->iaid, ap->iaid,\n\t\t\t\t\tsizeof(ia->iaid)))\n\t\t\t\t\tcontinue;\n\t\t\t\tif (ia->sla_len == 0) {\n\t\t\t\t\t/* no SLA configured, so lets\n\t\t\t\t\t * automate it */\n\t\t\t\t\tif (!if_is_link_up(ifd)) {\n\t\t\t\t\t\tlogdebugx(\n\t\t\t\t\t\t    \"%s: has no carrier, cannot\"\n\t\t\t\t\t\t    \" delegate addresses\",\n\t\t\t\t\t\t    ifd->name);\n\t\t\t\t\t\tcarrier_warned = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (dhcp6_ifdelegateaddr(ifd, ap, NULL,\n\t\t\t\t\t\tia))\n\t\t\t\t\t\tk++;\n\t\t\t\t}\n\t\t\t\tfor (j = 0; j < ia->sla_len; j++) {\n\t\t\t\t\tsla = &ia->sla[j];\n\t\t\t\t\tif (strcmp(ifd->name, sla->ifname))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (!if_is_link_up(ifd)) {\n\t\t\t\t\t\tlogdebugx(\n\t\t\t\t\t\t    \"%s: has no carrier, cannot\"\n\t\t\t\t\t\t    \" delegate addresses\",\n\t\t\t\t\t\t    ifd->name);\n\t\t\t\t\t\tcarrier_warned = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (dhcp6_ifdelegateaddr(ifd, ap, sla,\n\t\t\t\t\t\tia))\n\t\t\t\t\t\tk++;\n\t\t\t\t}\n\t\t\t\tif (carrier_warned)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (carrier_warned)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (k && !carrier_warned) {\n\t\t\tstruct dhcp6_state *s = D6_STATE(ifd);\n\n\t\t\tipv6_addaddrs(&s->addrs);\n\t\t\tdhcp6_script_try_run(ifd, 1);\n\t\t}\n\t}\n\n\t/* Now all addresses have been added, rebuild the routing table. */\n\trt_build(ifp->ctx, AF_INET6);\n}\n\nstatic void\ndhcp6_find_delegates1(void *arg)\n{\n\tdhcp6_find_delegates(arg);\n}\n\nsize_t\ndhcp6_find_delegates(struct interface *ifp)\n{\n\tstruct if_options *ifo;\n\tstruct dhcp6_state *state;\n\tstruct ipv6_addr *ap;\n\tsize_t i, j, k;\n\tstruct if_ia *ia;\n\tstruct if_sla *sla;\n\tstruct interface *ifd;\n\n\tif (ifp->options != NULL && !(ifp->options->options & DHCPCD_CONFIGURE))\n\t\treturn 0;\n\n\tk = 0;\n\tTAILQ_FOREACH(ifd, ifp->ctx->ifaces, next) {\n\t\tifo = ifd->options;\n\t\tstate = D6_STATE(ifd);\n\t\tif (state == NULL || state->state != DH6S_BOUND)\n\t\t\tcontinue;\n\t\tTAILQ_FOREACH(ap, &state->addrs, next) {\n\t\t\tif (!(ap->flags & IPV6_AF_PFXDELEGATION))\n\t\t\t\tcontinue;\n\t\t\tfor (i = 0; i < ifo->ia_len; i++) {\n\t\t\t\tia = &ifo->ia[i];\n\t\t\t\tif (ia->ia_type != D6_OPTION_IA_PD)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (memcmp(ia->iaid, ap->iaid,\n\t\t\t\t\tsizeof(ia->iaid)))\n\t\t\t\t\tcontinue;\n\t\t\t\tfor (j = 0; j < ia->sla_len; j++) {\n\t\t\t\t\tsla = &ia->sla[j];\n\t\t\t\t\tif (strcmp(ifp->name, sla->ifname))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (ipv6_linklocal(ifp) == NULL) {\n\t\t\t\t\t\tlogdebugx(\n\t\t\t\t\t\t    \"%s: delaying adding\"\n\t\t\t\t\t\t    \" delegated addresses for\"\n\t\t\t\t\t\t    \" LL address\",\n\t\t\t\t\t\t    ifp->name);\n\t\t\t\t\t\tipv6_addlinklocalcallback(ifp,\n\t\t\t\t\t\t    dhcp6_find_delegates1, ifp);\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\t}\n\t\t\t\t\tif (dhcp6_ifdelegateaddr(ifp, ap, sla,\n\t\t\t\t\t\tia))\n\t\t\t\t\t\tk++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (k) {\n\t\tloginfox(\"%s: adding delegated prefixes\", ifp->name);\n\t\tstate = D6_STATE(ifp);\n\t\tipv6_addaddrs(&state->addrs);\n\t\trt_build(ifp->ctx, AF_INET6);\n\t\tdhcp6_script_try_run(ifp, 1);\n\t}\n\treturn k;\n}\n#endif\n\nstatic void\ndhcp6_bind(struct interface *ifp, const char *op, const char *sfrom)\n{\n\tstruct dhcp6_state *state = D6_STATE(ifp);\n\tbool timedout = (op == NULL), confirmed;\n\tstruct ipv6_addr *ia;\n\tint loglevel;\n\tstruct timespec now;\n\n\tif (state->state == DH6S_RENEW) {\n\t\tloglevel = LOG_DEBUG;\n\t\tTAILQ_FOREACH(ia, &state->addrs, next) {\n\t\t\tif (ia->flags & IPV6_AF_NEW) {\n\t\t\t\tloglevel = LOG_INFO;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t} else if (state->state == DH6S_INFORM)\n\t\tloglevel = state->new_start ? LOG_INFO : LOG_DEBUG;\n\telse\n\t\tloglevel = LOG_INFO;\n\tstate->new_start = false;\n\n\tif (!timedout) {\n\t\tlogmessage(loglevel, \"%s: %s received from %s\", ifp->name, op,\n\t\t    sfrom);\n#ifndef SMALL\n\t\t/* If we delegated from an unconfirmed lease we MUST drop\n\t\t * them now. Hopefully we have new delegations. */\n\t\tif (state->reason != NULL &&\n\t\t    strcmp(state->reason, \"TIMEOUT6\") == 0)\n\t\t\tdhcp6_delete_delegates(ifp);\n#endif\n\t\tstate->reason = NULL;\n\t} else\n\t\tstate->reason = \"TIMEOUT6\";\n\n\teloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);\n\tclock_gettime(CLOCK_MONOTONIC, &now);\n\n\tswitch (state->state) {\n\tcase DH6S_INFORM: {\n\t\tstruct dhcp6_option *o;\n\t\tuint16_t ol;\n\n\t\tif (state->reason == NULL)\n\t\t\tstate->reason = \"INFORM6\";\n\t\to = dhcp6_findmoption(state->recv, state->recv_len,\n\t\t    D6_OPTION_INFO_REFRESH_TIME, &ol);\n\t\tif (o == NULL || ol != sizeof(uint32_t))\n\t\t\tstate->renew = IRT_DEFAULT;\n\t\telse {\n\t\t\tmemcpy(&state->renew, o, ol);\n\t\t\tstate->renew = ntohl(state->renew);\n\t\t\tif (state->renew < IRT_MINIMUM)\n\t\t\t\tstate->renew = IRT_MINIMUM;\n\t\t}\n\t\tstate->rebind = 0;\n\t\tstate->expire = ND6_INFINITE_LIFETIME;\n\t\tstate->lowpl = ND6_INFINITE_LIFETIME;\n\t} break;\n\n\tcase DH6S_REQUEST:\n\t\tif (state->reason == NULL)\n\t\t\tstate->reason = \"BOUND6\";\n\t\t/* FALLTHROUGH */\n\tcase DH6S_RENEW:\n\t\tif (state->reason == NULL)\n\t\t\tstate->reason = \"RENEW6\";\n\t\t/* FALLTHROUGH */\n\tcase DH6S_REBIND:\n\t\tif (state->reason == NULL)\n\t\t\tstate->reason = \"REBIND6\";\n\t\t/* FALLTHROUGH */\n\tcase DH6S_CONFIRM:\n\t\tif (state->reason == NULL)\n\t\t\tstate->reason = \"REBOOT6\";\n\t\tif (state->renew != 0) {\n\t\t\tbool all_expired = true;\n\n\t\t\tTAILQ_FOREACH(ia, &state->addrs, next) {\n\t\t\t\tif (ia->flags & IPV6_AF_STALE)\n\t\t\t\t\tcontinue;\n\t\t\t\tif (!(state->renew == ND6_INFINITE_LIFETIME &&\n\t\t\t\t\tia->prefix_vltime ==\n\t\t\t\t\t    ND6_INFINITE_LIFETIME) &&\n\t\t\t\t    ia->prefix_vltime != 0 &&\n\t\t\t\t    ia->prefix_vltime <= state->renew)\n\t\t\t\t\tlogwarnx(\n\t\t\t\t\t    \"%s: %s will expire before renewal\",\n\t\t\t\t\t    ifp->name, ia->saddr);\n\t\t\t\telse\n\t\t\t\t\tall_expired = false;\n\t\t\t}\n\t\t\tif (all_expired) {\n\t\t\t\t/* All address's vltime happens at or before\n\t\t\t\t * the configured T1 in the IA.\n\t\t\t\t * This is a badly configured server and we\n\t\t\t\t * have to use our own notion of what\n\t\t\t\t * T1 and T2 should be as a result.\n\t\t\t\t *\n\t\t\t\t * Doing this violates RFC 3315 22.4:\n\t\t\t\t * In a message sent by a server to a client,\n\t\t\t\t * the client MUST use the values in the T1\n\t\t\t\t * and T2 fields for the T1 and T2 parameters,\n\t\t\t\t * unless those values in those fields are 0.\n\t\t\t\t */\n\t\t\t\tlogwarnx(\"%s: ignoring T1 %\" PRIu32\n\t\t\t\t\t \" due to address expiry\",\n\t\t\t\t    ifp->name, state->renew);\n\t\t\t\tstate->renew = state->rebind = 0;\n\t\t\t}\n\t\t}\n\t\tif (state->renew == 0 && state->lowpl != ND6_INFINITE_LIFETIME)\n\t\t\tstate->renew = (uint32_t)(state->lowpl * 0.5);\n\t\tif (state->rebind == 0 && state->lowpl != ND6_INFINITE_LIFETIME)\n\t\t\tstate->rebind = (uint32_t)(state->lowpl * 0.8);\n\t\tbreak;\n\tdefault:\n\t\tstate->reason = \"UNKNOWN6\";\n\t\tbreak;\n\t}\n\n\tif (state->state != DH6S_CONFIRM && !timedout) {\n\t\tstate->acquired = now;\n\t\tfree(state->old);\n\t\tstate->old = state->new;\n\t\tstate->old_len = state->new_len;\n\t\tstate->new = state->recv;\n\t\tstate->new_len = state->recv_len;\n\t\tstate->recv = NULL;\n\t\tstate->recv_len = 0;\n\t\tconfirmed = false;\n\t} else {\n\t\t/* Reduce timers based on when we got the lease. */\n\t\tuint32_t elapsed;\n\n\t\telapsed = (uint32_t)eloop_timespec_diff(&now, &state->acquired,\n\t\t    NULL);\n\t\tif (state->renew && state->renew != ND6_INFINITE_LIFETIME) {\n\t\t\tif (state->renew > elapsed)\n\t\t\t\tstate->renew -= elapsed;\n\t\t\telse\n\t\t\t\tstate->renew = 0;\n\t\t}\n\t\tif (state->rebind && state->rebind != ND6_INFINITE_LIFETIME) {\n\t\t\tif (state->rebind > elapsed)\n\t\t\t\tstate->rebind -= elapsed;\n\t\t\telse\n\t\t\t\tstate->rebind = 0;\n\t\t}\n\t\tif (state->expire && state->expire != ND6_INFINITE_LIFETIME) {\n\t\t\tif (state->expire > elapsed)\n\t\t\t\tstate->expire -= elapsed;\n\t\t\telse\n\t\t\t\tstate->expire = 0;\n\t\t}\n\t\tconfirmed = true;\n\t}\n\n\tif (ifp->ctx->options & DHCPCD_TEST)\n\t\tscript_runreason(ifp, \"TEST\");\n\telse {\n\t\tif (state->state == DH6S_INFORM)\n\t\t\tstate->state = DH6S_INFORMED;\n\t\telse\n\t\t\tstate->state = DH6S_BOUND;\n\t\tstate->failed = false;\n\n\t\t/* If we CONFIRM we might need to enter RENEW\n\t\t * or REBIND right away if the timers have expired */\n\t\tif ((state->renew || (state->rebind && confirmed)) &&\n\t\t    state->renew != ND6_INFINITE_LIFETIME)\n\t\t\teloop_timeout_add_sec(ifp->ctx->eloop, state->renew,\n\t\t\t    state->state == DH6S_INFORMED ? dhcp6_startinform :\n\t\t\t\t\t\t\t    dhcp6_startrenew,\n\t\t\t    ifp);\n\t\tif ((state->rebind || (state->expire && confirmed)) &&\n\t\t    state->rebind != ND6_INFINITE_LIFETIME)\n\t\t\teloop_timeout_add_sec(ifp->ctx->eloop, state->rebind,\n\t\t\t    dhcp6_startrebind, ifp);\n\t\tif (state->expire != ND6_INFINITE_LIFETIME)\n\t\t\teloop_timeout_add_sec(ifp->ctx->eloop, state->expire,\n\t\t\t    dhcp6_startexpire, ifp);\n\n\t\tif (ifp->options->options & DHCPCD_CONFIGURE) {\n\t\t\tipv6_addaddrs(&state->addrs);\n\t\t\tif (!timedout)\n\t\t\t\tdhcp6_deprecateaddrs(&state->addrs);\n\t\t}\n\n\t\tif (state->state == DH6S_INFORMED)\n\t\t\tlogmessage(loglevel,\n\t\t\t    \"%s: refresh in %\" PRIu32 \" seconds\", ifp->name,\n\t\t\t    state->renew);\n\t\telse if (state->renew == ND6_INFINITE_LIFETIME)\n\t\t\tlogmessage(loglevel, \"%s: leased for infinity\",\n\t\t\t    ifp->name);\n\t\telse if (state->renew || state->rebind)\n\t\t\tlogmessage(loglevel,\n\t\t\t    \"%s: renew in %\" PRIu32 \", \"\n\t\t\t    \"rebind in %\" PRIu32 \", \"\n\t\t\t    \"expire in %\" PRIu32 \" seconds\",\n\t\t\t    ifp->name, state->renew, state->rebind,\n\t\t\t    state->expire);\n\t\telse if (state->expire == 0)\n\t\t\tlogmessage(loglevel, \"%s: will expire\", ifp->name);\n\t\telse\n\t\t\tlogmessage(loglevel,\n\t\t\t    \"%s: expire in %\" PRIu32 \" seconds\", ifp->name,\n\t\t\t    state->expire);\n\t\trt_build(ifp->ctx, AF_INET6);\n\t\tif (!confirmed && !timedout) {\n\t\t\tlogdebugx(\"%s: writing lease: %s\", ifp->name,\n\t\t\t    state->leasefile);\n\t\t\tif (dhcp_writefile(ifp->ctx, state->leasefile, 0640,\n\t\t\t\tstate->new, state->new_len) == -1)\n\t\t\t\tlogerr(\"dhcp_writefile: %s\", state->leasefile);\n\t\t}\n#ifndef SMALL\n\t\tdhcp6_delegate_prefix(ifp);\n#endif\n\t\tdhcp6_script_try_run(ifp, 0);\n\t}\n\n\tif (ifp->ctx->options & DHCPCD_TEST)\n\t\teloop_exit(ifp->ctx->eloop, EXIT_SUCCESS);\n}\n\nstatic void\ndhcp6_adjust_max_rt(struct interface *ifp, struct dhcp6_message *r, size_t len)\n{\n\tstruct dhcp6_state *state = D6_STATE(ifp);\n\tuint8_t *o;\n\tuint16_t ol;\n\n\t/* RFC 8415 */\n\to = dhcp6_findmoption(r, len, D6_OPTION_SOL_MAX_RT, &ol);\n\tif (o != NULL && ol == sizeof(uint32_t)) {\n\t\tuint32_t max_rt;\n\n\t\tmemcpy(&max_rt, o, sizeof(max_rt));\n\t\tmax_rt = ntohl(max_rt);\n\t\tif (max_rt >= 60 && max_rt <= 86400) {\n\t\t\tlogdebugx(\"%s: SOL_MAX_RT %llu -> %u\", ifp->name,\n\t\t\t    (unsigned long long)state->sol_max_rt, max_rt);\n\t\t\tstate->sol_max_rt = max_rt;\n\t\t} else\n\t\t\tlogerrx(\"%s: invalid SOL_MAX_RT %u\", ifp->name, max_rt);\n\t}\n\n\to = dhcp6_findmoption(r, len, D6_OPTION_INF_MAX_RT, &ol);\n\tif (o != NULL && ol == sizeof(uint32_t)) {\n\t\tuint32_t max_rt;\n\n\t\tmemcpy(&max_rt, o, sizeof(max_rt));\n\t\tmax_rt = ntohl(max_rt);\n\t\tif (max_rt >= 60 && max_rt <= 86400) {\n\t\t\tlogdebugx(\"%s: INF_MAX_RT %llu -> %u\", ifp->name,\n\t\t\t    (unsigned long long)state->inf_max_rt, max_rt);\n\t\t\tstate->inf_max_rt = max_rt;\n\t\t} else\n\t\t\tlogerrx(\"%s: invalid INF_MAX_RT %u\", ifp->name, max_rt);\n\t}\n}\n\nstatic void\ndhcp6_recvif(struct interface *ifp, const char *sfrom, struct dhcp6_message *r,\n    size_t len)\n{\n\tstruct dhcpcd_ctx *ctx;\n\tsize_t i;\n\tconst char *op;\n\tstruct dhcp6_state *state;\n\tuint8_t *o, preference = 0;\n\tuint16_t ol;\n\tconst struct dhcp_opt *opt;\n\tconst struct if_options *ifo;\n\tbool valid_op;\n#ifdef AUTH\n\tuint8_t *auth;\n\tuint16_t auth_len;\n#endif\n\n\tctx = ifp->ctx;\n\tstate = D6_STATE(ifp);\n\tif (state == NULL || state->send == NULL) {\n\t\tlogdebugx(\"%s: DHCPv6 reply received but not running\",\n\t\t    ifp->name);\n\t\treturn;\n\t}\n\n\t/* We're already bound and this message is for another machine */\n\t/* XXX DELEGATED? */\n\tif (r->type != DHCP6_RECONFIGURE &&\n\t    (state->state == DH6S_BOUND || state->state == DH6S_INFORMED)) {\n\t\tlogdebugx(\"%s: DHCPv6 reply received but already bound\",\n\t\t    ifp->name);\n\t\treturn;\n\t}\n\n\tif (dhcp6_findmoption(r, len, D6_OPTION_SERVERID, NULL) == NULL) {\n\t\tlogdebugx(\"%s: no DHCPv6 server ID from %s\", ifp->name, sfrom);\n\t\treturn;\n\t}\n\n\tifo = ifp->options;\n\tfor (i = 0, opt = ctx->dhcp6_opts; i < ctx->dhcp6_opts_len;\n\t    i++, opt++) {\n\t\tif (has_option_mask(ifo->requiremask6, opt->option) &&\n\t\t    !dhcp6_findmoption(r, len, (uint16_t)opt->option, NULL)) {\n\t\t\tlogwarnx(\"%s: reject DHCPv6 (no option %s) from %s\",\n\t\t\t    ifp->name, opt->var, sfrom);\n\t\t\treturn;\n\t\t}\n\t\tif (has_option_mask(ifo->rejectmask6, opt->option) &&\n\t\t    dhcp6_findmoption(r, len, (uint16_t)opt->option, NULL)) {\n\t\t\tlogwarnx(\"%s: reject DHCPv6 (option %s) from %s\",\n\t\t\t    ifp->name, opt->var, sfrom);\n\t\t\treturn;\n\t\t}\n\t}\n\n#ifdef AUTH\n\t/* Authenticate the message */\n\tauth = dhcp6_findmoption(r, len, D6_OPTION_AUTH, &auth_len);\n\tif (auth != NULL) {\n\t\tif (dhcp_auth_validate(&state->auth, &ifo->auth, (uint8_t *)r,\n\t\t\tlen, 6, r->type, auth, auth_len) == NULL) {\n\t\t\tlogerr(\"%s: authentication failed from %s\", ifp->name,\n\t\t\t    sfrom);\n\t\t\treturn;\n\t\t}\n\t\tif (state->auth.token)\n\t\t\tlogdebugx(\"%s: validated using 0x%08\" PRIu32, ifp->name,\n\t\t\t    state->auth.token->secretid);\n\t\telse\n\t\t\tloginfox(\"%s: accepted reconfigure key\", ifp->name);\n\t} else if (ifo->auth.options & DHCPCD_AUTH_SEND) {\n\t\tif (ifo->auth.options & DHCPCD_AUTH_REQUIRE) {\n\t\t\tlogerrx(\"%s: no authentication from %s\", ifp->name,\n\t\t\t    sfrom);\n\t\t\treturn;\n\t\t}\n\t\tlogwarnx(\"%s: no authentication from %s\", ifp->name, sfrom);\n\t}\n#endif\n\n\top = dhcp6_get_op(r->type);\n\tvalid_op = op != NULL;\n\tswitch (r->type) {\n\tcase DHCP6_REPLY:\n\t\tswitch (state->state) {\n\t\tcase DH6S_INFORM:\n\t\t\tif (dhcp6_checkstatusok(ifp, r, NULL, len) != 0)\n\t\t\t\treturn;\n\t\t\tbreak;\n\t\tcase DH6S_CONFIRM:\n\t\t\tif (dhcp6_validatelease(ifp, r, len, sfrom, NULL) ==\n\t\t\t    -1) {\n\t\t\t\tdhcp6_startdiscoinform(ifp);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase DH6S_DISCOVER:\n\t\t\t/* Only accept REPLY in DISCOVER for RAPID_COMMIT.\n\t\t\t * Normally we get an ADVERTISE for a DISCOVER. */\n\t\t\tif (!has_option_mask(ifo->requestmask6,\n\t\t\t\tD6_OPTION_RAPID_COMMIT) ||\n\t\t\t    !dhcp6_findmoption(r, len, D6_OPTION_RAPID_COMMIT,\n\t\t\t\tNULL)) {\n\t\t\t\tvalid_op = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t/* Validate lease before setting state to REQUEST. */\n\t\t\t/* FALLTHROUGH */\n\t\tcase DH6S_REQUEST: /* FALLTHROUGH */\n\t\tcase DH6S_RENEW:   /* FALLTHROUGH */\n\t\tcase DH6S_REBIND:\n\t\t\tdhcp6_adjust_max_rt(ifp, r, len);\n\t\t\tif (dhcp6_validatelease(ifp, r, len, sfrom, NULL) ==\n\t\t\t    -1) {\n\t\t\t\t/*\n\t\t\t\t * If we can't use the lease, fallback to\n\t\t\t\t * DISCOVER and try and get a new one.\n\t\t\t\t *\n\t\t\t\t * This is needed become some servers\n\t\t\t\t * renumber the prefix or address\n\t\t\t\t * and deny the current one before it expires\n\t\t\t\t * rather than sending it back with a zero\n\t\t\t\t * lifetime along with the new prefix or\n\t\t\t\t * address to use.\n\t\t\t\t * This behavior is wrong, but moving to the\n\t\t\t\t * DISCOVER phase works around it.\n\t\t\t\t *\n\t\t\t\t * The currently held lease is still valid\n\t\t\t\t * until a new one is found.\n\t\t\t\t */\n\t\t\t\tif (state->state != DH6S_DISCOVER)\n\t\t\t\t\tdhcp6_startdiscoinform(ifp);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t/* RFC8415 18.2.10.1 */\n\t\t\tif ((state->state == DH6S_RENEW ||\n\t\t\t\tstate->state == DH6S_REBIND) &&\n\t\t\t    state->has_no_binding) {\n\t\t\t\tdhcp6_startrequest(ifp);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (state->state == DH6S_DISCOVER)\n\t\t\t\tstate->state = DH6S_REQUEST;\n\t\t\tbreak;\n\t\tcase DH6S_DECLINE:\n\t\t\t/* This isnt really a failure, but an\n\t\t\t * acknowledgement of one. */\n\t\t\tloginfox(\"%s: %s acknowledged DECLINE6\", ifp->name,\n\t\t\t    sfrom);\n\t\t\tdhcp6_fail(ifp, true);\n\t\t\treturn;\n\t\tcase DH6S_RELEASE:\n\t\t\tloginfox(\"%s: %s acknowledged RELEASE6\", ifp->name,\n\t\t\t    sfrom);\n\t\t\tdhcp6_finishrelease(ifp);\n\t\t\treturn;\n\t\tdefault:\n\t\t\tvalid_op = false;\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase DHCP6_ADVERTISE:\n\t\tif (state->state != DH6S_DISCOVER) {\n\t\t\tvalid_op = false;\n\t\t\tbreak;\n\t\t}\n\n\t\to = dhcp6_findmoption(r, len, D6_OPTION_PREFERENCE, &ol);\n\t\tif (o && ol == sizeof(uint8_t))\n\t\t\tpreference = *o;\n\n\t\t/* If we already have an advertisement check that this one\n\t\t * has a higher preference value. */\n\t\tif (state->recv_len && state->recv->type == DHCP6_ADVERTISE) {\n\t\t\to = dhcp6_findmoption(state->recv, state->recv_len,\n\t\t\t    D6_OPTION_PREFERENCE, &ol);\n\t\t\tif (o && ol == sizeof(uint8_t) && *o >= preference) {\n\t\t\t\tlogdebugx(\n\t\t\t\t    \"%s: discarding ADVERTISEMENT from %s (%u)\",\n\t\t\t\t    ifp->name, sfrom, preference);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tdhcp6_adjust_max_rt(ifp, r, len);\n\t\tif (dhcp6_validatelease(ifp, r, len, sfrom, NULL) == -1)\n\t\t\treturn;\n\t\tbreak;\n\tcase DHCP6_RECONFIGURE:\n#ifdef AUTH\n\t\tif (auth == NULL) {\n#endif\n\t\t\tlogerrx(\"%s: unauthenticated %s from %s\", ifp->name, op,\n\t\t\t    sfrom);\n\t\t\tif (ifo->auth.options & DHCPCD_AUTH_REQUIRE)\n\t\t\t\treturn;\n#ifdef AUTH\n\t\t}\n\t\tloginfox(\"%s: %s from %s\", ifp->name, op, sfrom);\n\t\to = dhcp6_findmoption(r, len, D6_OPTION_RECONF_MSG, &ol);\n\t\tif (o == NULL) {\n\t\t\tlogerrx(\"%s: missing Reconfigure Message option\",\n\t\t\t    ifp->name);\n\t\t\treturn;\n\t\t}\n\t\tif (ol != 1) {\n\t\t\tlogerrx(\"%s: missing Reconfigure Message type\",\n\t\t\t    ifp->name);\n\t\t\treturn;\n\t\t}\n\t\tswitch (*o) {\n\t\tcase DHCP6_RENEW:\n\t\t\tif (state->state != DH6S_BOUND) {\n\t\t\t\tlogerrx(\"%s: not bound, ignoring %s\", ifp->name,\n\t\t\t\t    op);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdhcp6_startrenew(ifp);\n\t\t\tbreak;\n\t\tcase DHCP6_INFORMATION_REQ:\n\t\t\tif (state->state != DH6S_INFORMED) {\n\t\t\t\tlogerrx(\"%s: not informed, ignoring %s\",\n\t\t\t\t    ifp->name, op);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\teloop_timeout_delete(ifp->ctx->eloop, dhcp6_sendinform,\n\t\t\t    ifp);\n\t\t\tdhcp6_startinform(ifp);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tlogerr(\"%s: unsupported %s type %d\", ifp->name, op, *o);\n\t\t\tbreak;\n\t\t}\n\t\treturn;\n#else\n\t\tbreak;\n#endif\n\tdefault:\n\t\tlogerrx(\"%s: invalid DHCP6 type %s (%d)\", ifp->name, op,\n\t\t    r->type);\n\t\treturn;\n\t}\n\tif (!valid_op) {\n\t\tlogwarnx(\"%s: invalid state for DHCP6 type %s (%d)\", ifp->name,\n\t\t    op, r->type);\n\t\treturn;\n\t}\n\n\tif (state->recv_len < (size_t)len) {\n\t\tfree(state->recv);\n\t\tstate->recv = malloc(len);\n\t\tif (state->recv == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\treturn;\n\t\t}\n\t}\n\tmemcpy(state->recv, r, len);\n\tstate->recv_len = len;\n\n\tif (r->type == DHCP6_ADVERTISE) {\n\t\tstruct ipv6_addr *ia;\n\n\t\tTAILQ_FOREACH(ia, &state->addrs, next) {\n\t\t\tif (!(ia->flags & (IPV6_AF_STALE | IPV6_AF_REQUEST)))\n\t\t\t\tbreak;\n\t\t}\n\t\tif (ia == NULL)\n\t\t\tia = TAILQ_FIRST(&state->addrs);\n\t\tif (ia == NULL)\n\t\t\tloginfox(\"%s: ADV (no address) from %s (%u)\", ifp->name,\n\t\t\t    sfrom, preference);\n\t\telse\n\t\t\tloginfox(\"%s: ADV %s from %s (%u)\", ifp->name,\n\t\t\t    ia->saddr, sfrom, preference);\n\n\t\t/*\n\t\t * RFC 8415 18.2.1 says we must collect until ADVERTISEMENTs\n\t\t * until we get one with a preference of 255 or\n\t\t * the initial RT has elpased.\n\t\t */\n\t\tif (preference == 255 || state->RTC > 1)\n\t\t\tdhcp6_startrequest(ifp);\n\t\treturn;\n\t}\n\n\tdhcp6_bind(ifp, op, sfrom);\n}\n\nvoid\ndhcp6_recvmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg, struct ipv6_addr *ia)\n{\n\tstruct sockaddr_in6 *from = msg->msg_name;\n\tsize_t len = msg->msg_iov[0].iov_len;\n\tchar sfrom[INET6_ADDRSTRLEN];\n\tstruct interface *ifp;\n\tstruct dhcp6_message *r;\n\tconst struct dhcp6_state *state;\n\tuint8_t *o;\n\tuint16_t ol;\n\n\tinet_ntop(AF_INET6, &from->sin6_addr, sfrom, sizeof(sfrom));\n\tif (len < sizeof(struct dhcp6_message)) {\n\t\tlogerrx(\"DHCPv6 packet too short from %s\", sfrom);\n\t\treturn;\n\t}\n\n\tif (ia != NULL)\n\t\tifp = ia->iface;\n\telse {\n\t\tifp = if_findifpfromcmsg(ctx, msg, NULL);\n\t\tif (ifp == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tr = (struct dhcp6_message *)msg->msg_iov[0].iov_base;\n\n\tuint8_t duid[DUID_LEN], *dp;\n\tsize_t duid_len;\n\to = dhcp6_findmoption(r, len, D6_OPTION_CLIENTID, &ol);\n\tif (ifp->options->options & DHCPCD_ANONYMOUS) {\n\t\tduid_len = duid_make(duid, ifp, DUID_LL);\n\t\tdp = duid;\n\t} else {\n\t\tduid_len = ctx->duid_len;\n\t\tdp = ctx->duid;\n\t}\n\tif (o == NULL || ol != duid_len || memcmp(o, dp, ol) != 0) {\n\t\tlogdebugx(\"%s: incorrect client ID from %s\", ifp->name, sfrom);\n\t\treturn;\n\t}\n\n\tif (dhcp6_findmoption(r, len, D6_OPTION_SERVERID, NULL) == NULL) {\n\t\tlogdebugx(\"%s: no DHCPv6 server ID from %s\", ifp->name, sfrom);\n\t\treturn;\n\t}\n\n\tif (r->type == DHCP6_RECONFIGURE) {\n\t\tif (!IN6_IS_ADDR_LINKLOCAL(&from->sin6_addr)) {\n\t\t\tlogerrx(\"%s: RECONFIGURE6 recv from %s, not LL\",\n\t\t\t    ifp->name, sfrom);\n\t\t\treturn;\n\t\t}\n\t\tgoto recvif;\n\t}\n\n\tstate = D6_CSTATE(ifp);\n\tif (state == NULL || r->xid[0] != state->send->xid[0] ||\n\t    r->xid[1] != state->send->xid[1] ||\n\t    r->xid[2] != state->send->xid[2]) {\n\t\tstruct interface *ifp1;\n\t\tconst struct dhcp6_state *state1;\n\n\t\t/* Find an interface with a matching xid. */\n\t\tTAILQ_FOREACH(ifp1, ctx->ifaces, next) {\n\t\t\tstate1 = D6_CSTATE(ifp1);\n\t\t\tif (state1 == NULL || state1->send == NULL)\n\t\t\t\tcontinue;\n\t\t\tif (r->xid[0] == state1->send->xid[0] &&\n\t\t\t    r->xid[1] == state1->send->xid[1] &&\n\t\t\t    r->xid[2] == state1->send->xid[2])\n\t\t\t\tbreak;\n\t\t}\n\n\t\tif (ifp1 == NULL) {\n\t\t\tif (state != NULL)\n\t\t\t\tlogdebugx(\"%s: wrong xid 0x%02x%02x%02x\"\n\t\t\t\t\t  \" (expecting 0x%02x%02x%02x) from %s\",\n\t\t\t\t    ifp->name, r->xid[0], r->xid[1], r->xid[2],\n\t\t\t\t    state->send->xid[0], state->send->xid[1],\n\t\t\t\t    state->send->xid[2], sfrom);\n\t\t\treturn;\n\t\t}\n\t\tlogdebugx(\"%s: redirecting DHCP6 message to %s\", ifp->name,\n\t\t    ifp1->name);\n\t\tifp = ifp1;\n\t}\n\n#if 0\n\t/*\n\t * Handy code to inject raw DHCPv6 packets over responses\n\t * from our server.\n\t * This allows me to take a 3rd party wireshark trace and\n\t * replay it in my code.\n\t */\n\tstatic int replyn = 0;\n\tchar fname[PATH_MAX], tbuf[UDPLEN_MAX];\n\tint fd;\n\tssize_t tlen;\n\tuint8_t *si1, *si2;\n\tuint16_t si_len1, si_len2;\n\n\tsnprintf(fname, sizeof(fname),\n\t    \"/tmp/dhcp6.reply%d.raw\", replyn++);\n\tfd = open(fname, O_RDONLY, 0);\n\tif (fd == -1) {\n\t\tlogerr(\"%s: open: %s\", __func__, fname);\n\t\treturn;\n\t}\n\ttlen = read(fd, tbuf, sizeof(tbuf));\n\tif (tlen == -1)\n\t\tlogerr(\"%s: read: %s\", __func__, fname);\n\tclose(fd);\n\n\t/* Copy across ServerID so we can work with our own server. */\n\tsi1 = dhcp6_findmoption(r, len, D6_OPTION_SERVERID, &si_len1);\n\tsi2 = dhcp6_findmoption(tbuf, (size_t)tlen,\n\t    D6_OPTION_SERVERID, &si_len2);\n\tif (si1 != NULL && si2 != NULL && si_len1 == si_len2)\n\t\tmemcpy(si2, si1, si_len2);\n\tr = (struct dhcp6_message *)tbuf;\n\tlen = (size_t)tlen;\n#endif\n\nrecvif:\n\tdhcp6_recvif(ifp, sfrom, r, len);\n}\n\nstatic void\ndhcp6_recv(struct dhcpcd_ctx *ctx, struct ipv6_addr *ia, unsigned short events)\n{\n\tstruct sockaddr_in6 from;\n\tunion {\n\t\tstruct dhcp6_message dhcp6;\n\t\tuint8_t buf[UDPLEN_MAX]; /* Maximum UDP message size */\n\t} iovbuf;\n\tstruct iovec iov = {\n\t\t.iov_base = iovbuf.buf,\n\t\t.iov_len = sizeof(iovbuf.buf),\n\t};\n\tunion {\n\t\tstruct cmsghdr hdr;\n\t\tuint8_t buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];\n\t} cmsgbuf = { .buf = { 0 } };\n\tstruct msghdr msg = {\n\t\t.msg_name = &from,\n\t\t.msg_namelen = sizeof(from),\n\t\t.msg_iov = &iov,\n\t\t.msg_iovlen = 1,\n\t\t.msg_control = cmsgbuf.buf,\n\t\t.msg_controllen = sizeof(cmsgbuf.buf),\n\t};\n\tint s;\n\tssize_t bytes;\n\n\tif (events != ELE_READ)\n\t\tlogerrx(\"%s: unexpected event 0x%04x\", __func__, events);\n\n\ts = ia != NULL ? ia->dhcp6_fd : ctx->dhcp6_rfd;\n\tbytes = recvmsg(s, &msg, 0);\n\tif (bytes == -1) {\n\t\tlogerr(__func__);\n\t\treturn;\n\t}\n\n\tiov.iov_len = (size_t)bytes;\n\tdhcp6_recvmsg(ctx, &msg, ia);\n}\n\nstatic void\n\ndhcp6_recvaddr(void *arg, unsigned short events)\n{\n\tstruct ipv6_addr *ia = arg;\n\n\tdhcp6_recv(ia->iface->ctx, ia, events);\n}\n\nstatic void\ndhcp6_recvctx(void *arg, unsigned short events)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\n\tdhcp6_recv(ctx, NULL, events);\n}\n\nint\ndhcp6_openraw(void)\n{\n\tint fd, v;\n\n\tfd = xsocket(PF_INET6, SOCK_RAW | SOCK_CXNB, IPPROTO_UDP);\n\tif (fd == -1)\n\t\treturn -1;\n\n\tv = 1;\n\tif (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &v, sizeof(v)) == -1)\n\t\tgoto errexit;\n\n\tv = offsetof(struct udphdr, uh_sum);\n\tif (setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &v, sizeof(v)) == -1)\n\t\tgoto errexit;\n\n\treturn fd;\n\nerrexit:\n\tclose(fd);\n\treturn -1;\n}\n\nint\ndhcp6_openudp(unsigned int ifindex, struct in6_addr *ia)\n{\n\tstruct sockaddr_in6 sa;\n\tint n, s;\n\n\ts = xsocket(PF_INET6, SOCK_DGRAM | SOCK_CXNB, IPPROTO_UDP);\n\tif (s == -1)\n\t\tgoto errexit;\n\n\tmemset(&sa, 0, sizeof(sa));\n\tsa.sin6_family = AF_INET6;\n\tsa.sin6_port = htons(DHCP6_CLIENT_PORT);\n#ifdef BSD\n\tsa.sin6_len = sizeof(sa);\n#endif\n\n\tif (ia != NULL) {\n\t\tmemcpy(&sa.sin6_addr, ia, sizeof(sa.sin6_addr));\n\t\tipv6_setscope(&sa, ifindex);\n\t}\n\n\tif (bind(s, (struct sockaddr *)&sa, sizeof(sa)) == -1)\n\t\tgoto errexit;\n\n\tn = 1;\n\tif (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &n, sizeof(n)) == -1)\n\t\tgoto errexit;\n\n#ifdef SO_RERROR\n\tn = 1;\n\tif (setsockopt(s, SOL_SOCKET, SO_RERROR, &n, sizeof(n)) == -1)\n\t\tgoto errexit;\n#endif\n\n\treturn s;\n\nerrexit:\n\tlogerr(__func__);\n\tif (s != -1)\n\t\tclose(s);\n\treturn -1;\n}\n\n#ifndef SMALL\nstatic void\ndhcp6_activateinterfaces(struct interface *ifp)\n{\n\tstruct interface *ifd;\n\tsize_t i, j;\n\tstruct if_ia *ia;\n\tstruct if_sla *sla;\n\n\tfor (i = 0; i < ifp->options->ia_len; i++) {\n\t\tia = &ifp->options->ia[i];\n\t\tif (ia->ia_type != D6_OPTION_IA_PD)\n\t\t\tcontinue;\n\t\tfor (j = 0; j < ia->sla_len; j++) {\n\t\t\tsla = &ia->sla[j];\n\t\t\tifd = if_find(ifp->ctx->ifaces, sla->ifname);\n\t\t\tif (ifd == NULL) {\n\t\t\t\tif (*sla->ifname != '-')\n\t\t\t\t\tlogwarn(\"%s: cannot delegate to %s\",\n\t\t\t\t\t    ifp->name, sla->ifname);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!ifd->active) {\n\t\t\t\tloginfox(\"%s: activating for delegation\",\n\t\t\t\t    sla->ifname);\n\t\t\t\tdhcpcd_activateinterface(ifd,\n\t\t\t\t    DHCPCD_IPV6 | DHCPCD_DHCP6);\n\t\t\t}\n\t\t}\n\t}\n}\n#endif\n\nstatic void\ndhcp6_start1(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tstruct dhcpcd_ctx *ctx = ifp->ctx;\n\tstruct if_options *ifo = ifp->options;\n\tstruct dhcp6_state *state;\n\tsize_t i;\n\tconst struct dhcp_compat *dhc;\n\n\tif ((ctx->options & (DHCPCD_MANAGER | DHCPCD_PRIVSEP)) ==\n\t\tDHCPCD_MANAGER &&\n\t    ctx->dhcp6_rfd == -1) {\n\t\tctx->dhcp6_rfd = dhcp6_openudp(0, NULL);\n\t\tif (ctx->dhcp6_rfd == -1) {\n\t\t\tlogerr(__func__);\n\t\t\treturn;\n\t\t}\n\t\tif (eloop_event_add(ctx->eloop, ctx->dhcp6_rfd, ELE_READ,\n\t\t\tdhcp6_recvctx, ctx) == -1)\n\t\t\tlogerr(\"%s: eloop_event_add\", __func__);\n\t}\n\n\tif (!IN_PRIVSEP(ctx) && ctx->dhcp6_wfd == -1) {\n\t\tctx->dhcp6_wfd = dhcp6_openraw();\n\t\tif (ctx->dhcp6_wfd == -1) {\n\t\t\tlogerr(__func__);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tstate = D6_STATE(ifp);\n\t/* If no DHCPv6 options are configured,\n\t   match configured DHCPv4 options to DHCPv6 equivalents. */\n\tfor (i = 0; i < sizeof(ifo->requestmask6); i++) {\n\t\tif (ifo->requestmask6[i] != '\\0')\n\t\t\tbreak;\n\t}\n\tif (i == sizeof(ifo->requestmask6)) {\n\t\tfor (dhc = dhcp_compats; dhc->dhcp_opt; dhc++) {\n\t\t\tif (DHC_REQ(ifo->requestmask, ifo->nomask,\n\t\t\t\tdhc->dhcp_opt))\n\t\t\t\tadd_option_mask(ifo->requestmask6,\n\t\t\t\t    dhc->dhcp6_opt);\n\t\t}\n\t\tif (ifo->fqdn != FQDN_DISABLE || ifo->options & DHCPCD_HOSTNAME)\n\t\t\tadd_option_mask(ifo->requestmask6, D6_OPTION_FQDN);\n\t}\n\n#ifndef SMALL\n\t/* Rapid commit won't work with Prefix Delegation Exclusion */\n\tif (dhcp6_findselfsla(ifp))\n\t\tdel_option_mask(ifo->requestmask6, D6_OPTION_RAPID_COMMIT);\n#endif\n\n\tif (state->state == DH6S_INFORM)\n\t\tdhcp6_startinform(ifp);\n\telse\n\t\tdhcp6_startinit(ifp);\n\n#ifndef SMALL\n\tdhcp6_activateinterfaces(ifp);\n#endif\n}\n\nint\ndhcp6_start(struct interface *ifp, enum DH6S init_state)\n{\n\tstruct dhcp6_state *state;\n\n\tstate = D6_STATE(ifp);\n\tif (state != NULL) {\n\t\tswitch (init_state) {\n\t\tcase DH6S_INIT:\n\t\t\tgoto gogogo;\n\t\tcase DH6S_INFORM:\n\t\t\t/* RFC 8415 21.23\n\t\t\t * If D6_OPTION_INFO_REFRESH_TIME does not exist\n\t\t\t * then we MUST refresh by IRT_DEFAULT seconds\n\t\t\t * and should not be influenced by only the\n\t\t\t * pl/vl time of the RA changing. */\n\t\t\tif (state->state == DH6S_INIT ||\n\t\t\t    (state->state == DH6S_DISCOVER &&\n\t\t\t\t!(ifp->options->options & DHCPCD_IA_FORCED) &&\n\t\t\t\t!ipv6nd_hasradhcp(ifp, true)))\n\t\t\t\tdhcp6_startinform(ifp);\n\t\t\tbreak;\n\t\tcase DH6S_REQUEST:\n\t\t\tif (ifp->options->options & DHCPCD_DHCP6 &&\n\t\t\t    (state->state == DH6S_INIT ||\n\t\t\t\tstate->state == DH6S_INFORM ||\n\t\t\t\tstate->state == DH6S_INFORMED ||\n\t\t\t\tstate->state == DH6S_DELEGATED)) {\n\t\t\t\t/* Change from stateless to stateful */\n\t\t\t\tinit_state = DH6S_INIT;\n\t\t\t\tgoto gogogo;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase DH6S_CONFIRM:\n\t\t\t/*\n\t\t\t * CONFIRM a prior lease from a RA.\n\t\t\t * This could be triggered by a roaming interface.\n\t\t\t * We could also get here if we are delegated to.\n\t\t\t * Now that we don't remove delegated addresses when\n\t\t\t * reading the lease file this is the safe path.\n\t\t\t */\n\t\t\tif (state->state == DH6S_MANUALREBIND)\n\t\t\t\tinit_state = DH6S_MANUALREBIND;\n\t\t\telse\n\t\t\t\tinit_state = DH6S_INIT;\n\t\t\tgoto gogogo;\n\t\tdefault:\n\t\t\t/* Not possible, but sushes some compiler warnings. */\n\t\t\tbreak;\n\t\t}\n\t\treturn 0;\n\t} else {\n\t\tswitch (init_state) {\n\t\tcase DH6S_CONFIRM:\n\t\t\t/* No DHCPv6 config, no existing state\n\t\t\t * so nothing to do. */\n\t\t\treturn 0;\n\t\tcase DH6S_INFORM:\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tinit_state = DH6S_INIT;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (!(ifp->options->options & DHCPCD_DHCP6))\n\t\treturn 0;\n\n\tifp->if_data[IF_DATA_DHCP6] = calloc(1, sizeof(*state));\n\tstate = D6_STATE(ifp);\n\tif (state == NULL)\n\t\treturn -1;\n\n\tstate->sol_max_rt = SOL_MAX_RT;\n\tstate->inf_max_rt = INF_MAX_RT;\n\tTAILQ_INIT(&state->addrs);\n\ngogogo:\n\tstate->state = init_state;\n\tstate->new_start = true;\n\tstate->lerror = 0;\n\tstate->failed = false;\n\tdhcp_set_leasefile(state->leasefile, sizeof(state->leasefile), AF_INET6,\n\t    ifp);\n\tif (ipv6_linklocal(ifp) == NULL) {\n\t\tlogdebugx(\"%s: delaying DHCPv6 for LL address\", ifp->name);\n\t\tipv6_addlinklocalcallback(ifp, dhcp6_start1, ifp);\n\t\treturn 0;\n\t}\n\n\tdhcp6_start1(ifp);\n\treturn 0;\n}\n\nvoid\ndhcp6_reboot(struct interface *ifp)\n{\n\tstruct dhcp6_state *state;\n\n\tstate = D6_STATE(ifp);\n\tif (state == NULL)\n\t\treturn;\n\n\tswitch (state->state) {\n\tcase DH6S_RENEW: /* FALLTHROUGH */\n\tcase DH6S_BOUND: /* FALLTHROUGH */\n\tcase DH6S_REBIND:\n\t\tstate->state = DH6S_MANUALREBIND;\n\t\tbreak;\n\tdefault: /* Appease compilers */\n\t\tbreak;\n\t}\n\n\t/* Do nothing. On confirming the next lease we will REBIND instead. */\n}\n\nstatic void\ndhcp6_freedrop(struct interface *ifp, int drop, const char *reason)\n{\n\tstruct dhcp6_state *state;\n\tstruct dhcpcd_ctx *ctx;\n\tunsigned long long options;\n\n\tif (ifp->options)\n\t\toptions = ifp->options->options;\n\telse\n\t\toptions = ifp->ctx->options;\n\n\tif (ifp->ctx->eloop)\n\t\teloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);\n\n#ifndef SMALL\n\t/* If we're dropping the lease, drop delegated addresses.\n\t * If, for whatever reason, we don't drop them in the future\n\t * then they should at least be marked as deprecated (pltime 0). */\n\tif (drop && (options & DHCPCD_NODROP) != DHCPCD_NODROP)\n\t\tdhcp6_delete_delegates(ifp);\n#endif\n\n\tstate = D6_STATE(ifp);\n\tif (state) {\n\t\t/* Failure to send the release may cause this function to\n\t\t * re-enter */\n\t\tif (state->state == DH6S_RELEASE) {\n\t\t\tdhcp6_finishrelease(ifp);\n\t\t\treturn;\n\t\t}\n\n\t\tif (drop && options & DHCPCD_RELEASE &&\n\t\t    state->state != DH6S_DELEGATED) {\n\t\t\tif (if_is_link_up(ifp) &&\n\t\t\t    state->state != DH6S_RELEASED &&\n\t\t\t    state->state != DH6S_INFORMED) {\n\t\t\t\tdhcp6_startrelease(ifp);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tdhcp_unlink(ifp->ctx, state->leasefile);\n\t\t}\n#ifdef AUTH\n\t\telse if (state->auth.reconf != NULL) {\n\t\t\t/*\n\t\t\t * Drop the lease as the token may only be present\n\t\t\t * in the initial reply message and not subsequent\n\t\t\t * renewals.\n\t\t\t * If dhcpcd is restarted, the token is lost.\n\t\t\t * XXX persist this in another file?\n\t\t\t */\n\t\t\tdhcp_unlink(ifp->ctx, state->leasefile);\n\t\t}\n#endif\n\n\t\tdhcp6_freedrop_addrs(ifp, drop, 0, NULL);\n\t\tfree(state->old);\n\t\tstate->old = state->new;\n\t\tstate->old_len = state->new_len;\n\t\tstate->new = NULL;\n\t\tstate->new_len = 0;\n\t\tif (drop && state->old &&\n\t\t    (options & DHCPCD_NODROP) != DHCPCD_NODROP) {\n\t\t\tif (reason == NULL)\n\t\t\t\treason = \"STOP6\";\n\t\t\tscript_runreason(ifp, reason);\n\t\t}\n\t\tfree(state->old);\n\t\tfree(state->send);\n\t\tfree(state->recv);\n\t\tfree(state);\n\t\tifp->if_data[IF_DATA_DHCP6] = NULL;\n\t}\n\tdhcpcd_dropped(ifp);\n\n\t/* If we don't have any more DHCP6 enabled interfaces,\n\t * close the global socket and release resources */\n\tctx = ifp->ctx;\n\tif (ctx->ifaces) {\n\t\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\t\tif (D6_STATE(ifp))\n\t\t\t\tbreak;\n\t\t}\n\t}\n\tif (ifp == NULL && ctx->dhcp6_rfd != -1) {\n\t\teloop_event_delete(ctx->eloop, ctx->dhcp6_rfd);\n\t\tclose(ctx->dhcp6_rfd);\n\t\tctx->dhcp6_rfd = -1;\n\t}\n}\n\nvoid\ndhcp6_drop(struct interface *ifp, const char *reason)\n{\n\tdhcp6_freedrop(ifp, 1, reason);\n}\n\nvoid\ndhcp6_free(struct interface *ifp)\n{\n\tdhcp6_freedrop(ifp, 0, NULL);\n}\n\nvoid\ndhcp6_abort(struct interface *ifp)\n{\n\tstruct dhcp6_state *state;\n\n\teloop_timeout_delete(ifp->ctx->eloop, dhcp6_start1, ifp);\n\tstate = D6_STATE(ifp);\n\tif (state == NULL)\n\t\treturn;\n\n\teloop_timeout_delete(ifp->ctx->eloop, dhcp6_startdiscover, ifp);\n\teloop_timeout_delete(ifp->ctx->eloop, dhcp6_senddiscover, ifp);\n\teloop_timeout_delete(ifp->ctx->eloop, dhcp6_startinform, ifp);\n\teloop_timeout_delete(ifp->ctx->eloop, dhcp6_sendinform, ifp);\n\n\tswitch (state->state) {\n\tcase DH6S_DISCOVER: /* FALLTHROUGH */\n\tcase DH6S_REQUEST:  /* FALLTHROUGH */\n\tcase DH6S_INFORM:\n\t\tstate->state = DH6S_INIT;\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n}\n\nvoid\ndhcp6_handleifa(int cmd, struct ipv6_addr *ia, pid_t pid)\n{\n\tstruct dhcp6_state *state;\n\tstruct interface *ifp = ia->iface;\n\n\t/* If not running in manager mode, listen to this address */\n\tif (cmd == RTM_NEWADDR && !(ia->addr_flags & IN6_IFF_NOTUSEABLE) &&\n\t    ifp->active == IF_ACTIVE_USER &&\n\t    !(ifp->ctx->options & DHCPCD_MANAGER) &&\n\t    ifp->options->options & DHCPCD_DHCP6) {\n#ifdef PRIVSEP\n\t\tif (IN_PRIVSEP_SE(ifp->ctx)) {\n\t\t\tif (ps_inet_opendhcp6(ia) == -1)\n\t\t\t\tlogerr(__func__);\n\t\t} else\n#endif\n\t\t{\n\t\t\tif (ia->dhcp6_fd == -1)\n\t\t\t\tia->dhcp6_fd = dhcp6_openudp(ia->iface->index,\n\t\t\t\t    &ia->addr);\n\t\t\tif (ia->dhcp6_fd != -1 &&\n\t\t\t    eloop_event_add(ia->iface->ctx->eloop, ia->dhcp6_fd,\n\t\t\t\tELE_READ, dhcp6_recvaddr, ia) == -1)\n\t\t\t\tlogerr(\"%s: eloop_event_add\", __func__);\n\t\t}\n\t}\n\n\tif ((state = D6_STATE(ifp)) != NULL)\n\t\tipv6_handleifa_addrs(cmd, &state->addrs, ia, pid);\n}\n\nssize_t\ndhcp6_env(FILE *fp, const char *prefix, const struct interface *ifp,\n    const struct dhcp6_message *m, size_t len)\n{\n\tconst struct if_options *ifo;\n\tstruct dhcp_opt *opt, *vo;\n\tconst uint8_t *p;\n\tstruct dhcp6_option o;\n\tsize_t i;\n\tchar *pfx;\n\tuint32_t en;\n\tconst struct dhcpcd_ctx *ctx;\n#ifndef SMALL\n\tconst struct dhcp6_state *state;\n\tconst struct ipv6_addr *ap;\n\tbool first;\n#endif\n\n\tif (m == NULL)\n\t\tgoto delegated;\n\n\tif (len < sizeof(*m)) {\n\t\t/* Should be impossible with guards at packet in\n\t\t * and reading leases */\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tifo = ifp->options;\n\tctx = ifp->ctx;\n\n\t/* Zero our indexes */\n\tfor (i = 0, opt = ctx->dhcp6_opts; i < ctx->dhcp6_opts_len; i++, opt++)\n\t\tdhcp_zero_index(opt);\n\tfor (i = 0, opt = ifp->options->dhcp6_override;\n\t    i < ifp->options->dhcp6_override_len; i++, opt++)\n\t\tdhcp_zero_index(opt);\n\tfor (i = 0, opt = ctx->vivso; i < ctx->vivso_len; i++, opt++)\n\t\tdhcp_zero_index(opt);\n\tif (asprintf(&pfx, \"%s_dhcp6\", prefix) == -1)\n\t\treturn -1;\n\n\t/* Unlike DHCP, DHCPv6 options *may* occur more than once.\n\t * There is also no provision for option concatenation unlike DHCP. */\n\tp = (const uint8_t *)m + sizeof(*m);\n\tlen -= sizeof(*m);\n\tfor (; len != 0; p += o.len, len -= o.len) {\n\t\tif (len < sizeof(o)) {\n\t\t\terrno = EINVAL;\n\t\t\tbreak;\n\t\t}\n\t\tmemcpy(&o, p, sizeof(o));\n\t\tp += sizeof(o);\n\t\tlen -= sizeof(o);\n\t\to.len = ntohs(o.len);\n\t\tif (len < o.len) {\n\t\t\terrno = EINVAL;\n\t\t\tbreak;\n\t\t}\n\t\to.code = ntohs(o.code);\n\t\tif (has_option_mask(ifo->nomask6, o.code))\n\t\t\tcontinue;\n\t\tfor (i = 0, opt = ifo->dhcp6_override;\n\t\t    i < ifo->dhcp6_override_len; i++, opt++)\n\t\t\tif (opt->option == o.code)\n\t\t\t\tbreak;\n\t\tif (i == ifo->dhcp6_override_len &&\n\t\t    o.code == D6_OPTION_VENDOR_OPTS && o.len > sizeof(en)) {\n\t\t\tmemcpy(&en, p, sizeof(en));\n\t\t\ten = ntohl(en);\n\t\t\tvo = vivso_find(en, ifp);\n\t\t} else\n\t\t\tvo = NULL;\n\t\tif (i == ifo->dhcp6_override_len) {\n\t\t\tfor (i = 0, opt = ctx->dhcp6_opts;\n\t\t\t    i < ctx->dhcp6_opts_len; i++, opt++)\n\t\t\t\tif (opt->option == o.code)\n\t\t\t\t\tbreak;\n\t\t\tif (i == ctx->dhcp6_opts_len)\n\t\t\t\topt = NULL;\n\t\t}\n\t\tif (opt) {\n\t\t\tdhcp_envoption(ifp->ctx, fp, pfx, ifp->name, opt,\n\t\t\t    dhcp6_getoption, p, o.len);\n\t\t}\n\t\tif (vo) {\n\t\t\tdhcp_envoption(ifp->ctx, fp, pfx, ifp->name, vo,\n\t\t\t    dhcp6_getoption, p + sizeof(en),\n\t\t\t    o.len - sizeof(en));\n\t\t}\n\t}\n\tfree(pfx);\n\ndelegated:\n#ifndef SMALL\n\t/* Needed for Delegated Prefixes */\n\tstate = D6_CSTATE(ifp);\n\tTAILQ_FOREACH(ap, &state->addrs, next) {\n\t\tif (ap->delegating_prefix)\n\t\t\tbreak;\n\t}\n\tif (ap == NULL)\n\t\treturn 1;\n\tif (fprintf(fp, \"%s_delegated_dhcp6_prefix=\", prefix) == -1)\n\t\treturn -1;\n\tfirst = true;\n\tTAILQ_FOREACH(ap, &state->addrs, next) {\n\t\tif (ap->delegating_prefix == NULL)\n\t\t\tcontinue;\n\t\tif (first)\n\t\t\tfirst = false;\n\t\telse {\n\t\t\tif (fputc(' ', fp) == EOF)\n\t\t\t\treturn -1;\n\t\t}\n\t\tif (fprintf(fp, \"%s\", ap->saddr) == -1)\n\t\t\treturn -1;\n\t}\n\tif (fputc('\\0', fp) == EOF)\n\t\treturn -1;\n#endif\n\n\treturn 1;\n}\n#endif\n\n#ifndef SMALL\nint\ndhcp6_dump(struct interface *ifp)\n{\n\tstruct dhcp6_state *state;\n\n\tifp->if_data[IF_DATA_DHCP6] = state = calloc(1, sizeof(*state));\n\tif (state == NULL) {\n\t\tlogerr(__func__);\n\t\treturn -1;\n\t}\n\tTAILQ_INIT(&state->addrs);\n\tif (dhcp6_readlease(ifp, 0) == -1) {\n\t\tlogerr(\"dhcp6_readlease\");\n\t\treturn -1;\n\t}\n\tstate->reason = \"DUMP6\";\n\treturn script_runreason(ifp, state->reason);\n}\n#endif\n"
  },
  {
    "path": "src/dhcp6.h",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef DHCP6_H\n#define DHCP6_H\n\n#include \"dhcpcd.h\"\n\n#define IN6ADDR_LINKLOCAL_ALLDHCP_INIT                             \\\n\t{                                                          \\\n\t\t{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \\\n\t    0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 }}      \\\n\t}\n\n/* UDP port numbers for DHCP */\n#define DHCP6_CLIENT_PORT 546\n#define DHCP6_SERVER_PORT 547\n\n/* DHCP message type */\n#define DHCP6_SOLICIT\t\t1\n#define DHCP6_ADVERTISE\t\t2\n#define DHCP6_REQUEST\t\t3\n#define DHCP6_CONFIRM\t\t4\n#define DHCP6_RENEW\t\t5\n#define DHCP6_REBIND\t\t6\n#define DHCP6_REPLY\t\t7\n#define DHCP6_RELEASE\t\t8\n#define DHCP6_DECLINE\t\t9\n#define DHCP6_RECONFIGURE\t10\n#define DHCP6_INFORMATION_REQ\t11\n#define DHCP6_RELAY_FLOW\t12\n#define DHCP6_RELAY_REPL\t13\n#define DHCP6_RECONFIGURE_REQ\t18\n#define DHCP6_RECONFIGURE_REPLY 19\n\n#ifdef DHCP6\n\n#define D6_OPTION_CLIENTID\t      1\n#define D6_OPTION_SERVERID\t      2\n#define D6_OPTION_IA_NA\t\t      3\n#define D6_OPTION_IA_TA\t\t      4\n#define D6_OPTION_ORO\t\t      6\n#define D6_OPTION_IA_ADDR\t      5\n#define D6_OPTION_PREFERENCE\t      7\n#define D6_OPTION_ELAPSED\t      8\n#define D6_OPTION_AUTH\t\t      11\n#define D6_OPTION_UNICAST\t      12\n#define D6_OPTION_STATUS_CODE\t      13\n#define D6_OPTION_RAPID_COMMIT\t      14\n#define D6_OPTION_USER_CLASS\t      15\n#define D6_OPTION_VENDOR_CLASS\t      16\n#define D6_OPTION_VENDOR_OPTS\t      17\n#define D6_OPTION_INTERFACE_ID\t      18\n#define D6_OPTION_RECONF_MSG\t      19\n#define D6_OPTION_RECONF_ACCEPT\t      20\n#define D6_OPTION_SIP_SERVERS_NAME    21\n#define D6_OPTION_SIP_SERVERS_ADDRESS 22\n#define D6_OPTION_DNS_SERVERS\t      23\n#define D6_OPTION_DOMAIN_LIST\t      24\n#define D6_OPTION_IA_PD\t\t      25\n#define D6_OPTION_IAPREFIX\t      26\n#define D6_OPTION_NIS_SERVERS\t      27\n#define D6_OPTION_NISP_SERVERS\t      28\n#define D6_OPTION_NIS_DOMAIN_NAME     29\n#define D6_OPTION_NISP_DOMAIN_NAME    30\n#define D6_OPTION_SNTP_SERVERS\t      31\n#define D6_OPTION_INFO_REFRESH_TIME   32\n#define D6_OPTION_BCMS_SERVER_D\t      33\n#define D6_OPTION_BCMS_SERVER_A\t      34\n#define D6_OPTION_FQDN\t\t      39\n#define D6_OPTION_POSIX_TIMEZONE      41\n#define D6_OPTION_TZDB_TIMEZONE\t      42\n#define D6_OPTION_NTP_SERVER\t      56\n#define D6_OPTION_PD_EXCLUDE\t      67\n#define D6_OPTION_SOL_MAX_RT\t      82\n#define D6_OPTION_INF_MAX_RT\t      83\n#define D6_OPTION_MUDURL\t      112\n\n#define D6_FQDN_PTR\t\t      0x00\n#define D6_FQDN_BOTH\t\t      0x01\n#define D6_FQDN_NONE\t\t      0x04\n\n#include \"dhcp.h\"\n#include \"ipv6.h\"\n\n#define D6_STATUS_OK\t       0\n#define D6_STATUS_FAIL\t       1\n#define D6_STATUS_NOADDR       2\n#define D6_STATUS_NOBINDING    3\n#define D6_STATUS_NOTONLINK    4\n#define D6_STATUS_USEMULTICAST 5\n\n#define SOL_MAX_DELAY\t       1\n#define SOL_TIMEOUT\t       1\n#define SOL_MAX_RT\t       3600 /* RFC 8415 */\n#define SOL_MAX_RC\t       0\n#define REQ_MAX_DELAY\t       0\n#define REQ_TIMEOUT\t       1\n#define REQ_MAX_RT\t       30\n#define REQ_MAX_RC\t       10\n#define CNF_MAX_DELAY\t       1\n#define CNF_TIMEOUT\t       1\n#define CNF_MAX_RT\t       4\n#define CNF_MAX_RC\t       0\n#define CNF_MAX_RD\t       10\n#define REN_MAX_DELAY\t       0\n#define REN_TIMEOUT\t       10\n#define REN_MAX_RT\t       600\n#define REB_MAX_DELAY\t       0\n#define REB_TIMEOUT\t       10\n#define REB_MAX_RT\t       600\n#define INF_MAX_DELAY\t       1\n#define INF_TIMEOUT\t       1\n#define INF_MAX_RD\t       CNF_MAX_RD /* NOT RFC defined */\n#define INF_MAX_RT\t       3600\t  /* RFC 8415*/\n#define REL_MAX_DELAY\t       0\n#define REL_TIMEOUT\t       1\n#define REL_MAX_RT\t       0\n#define REL_MAX_RC\t       4 /* RFC 8415 */\n#define DEC_MAX_DELAY\t       0\n#define DEC_TIMEOUT\t       1\n#define DEC_MAX_RC\t       4 /* RFC 8415 */\n#define REC_MAX_DELAY\t       0\n#define REC_TIMEOUT\t       2\n#define REC_MAX_RC\t       8\n#define HOP_COUNT_LIMIT\t       32\n\n/* RFC4242 3.1 */\n#define IRT_DEFAULT 86400\n#define IRT_MINIMUM 600\n\n/* These should give -.1 to .1 randomness */\n#define DHCP6_RAND_MIN -100\n#define DHCP6_RAND_MAX 100\n#define DHCP6_RAND_DIV 1000.0f\n\nenum DH6S {\n\tDH6S_INIT,\n\tDH6S_DISCOVER,\n\tDH6S_REQUEST,\n\tDH6S_BOUND,\n\tDH6S_RENEW,\n\tDH6S_REBIND,\n\tDH6S_CONFIRM,\n\tDH6S_INFORM,\n\tDH6S_INFORMED,\n\tDH6S_RENEW_REQUESTED,\n\tDH6S_PROBE,\n\tDH6S_DECLINE,\n\tDH6S_DELEGATED,\n\tDH6S_RELEASE,\n\tDH6S_RELEASED,\n\tDH6S_MANUALREBIND,\n};\n\nstruct dhcp6_state {\n\tenum DH6S state;\n\tstruct timespec started;\n\n\t/* Message retransmission timings in seconds */\n\tunsigned int IMD;\n\tunsigned int RTC;\n\tunsigned int IRT;\n\tunsigned int MRC;\n\tunsigned int MRT;\n\tvoid (*MRCcallback)(void *);\n\tunsigned int sol_max_rt;\n\tunsigned int inf_max_rt;\n\tunsigned int RT; /* retransmission timer in milliseconds\n\t\t\t  * maximal RT is 1 day + RAND,\n\t\t\t  * so should be enough */\n\n\tstruct dhcp6_message *send;\n\tsize_t send_len;\n\tstruct dhcp6_message *recv;\n\tsize_t recv_len;\n\tstruct dhcp6_message *new;\n\tsize_t new_len;\n\tstruct dhcp6_message *old;\n\tsize_t old_len;\n\n\tstruct timespec acquired;\n\tuint32_t renew;\n\tuint32_t rebind;\n\tuint32_t expire;\n\tstruct in6_addr unicast;\n\tstruct ipv6_addrhead addrs;\n\tuint32_t lowpl;\n\t/* The +3 is for the possible .pd extension for prefix delegation */\n\tchar leasefile[sizeof(LEASEFILE6) + IF_NAMESIZE + (IF_SSIDLEN * 4) + 3];\n\tconst char *reason;\n\tuint16_t lerror; /* Last error received from DHCPv6 reply. */\n\tbool has_no_binding;\n\tbool failed;\t/* Entered the failed state - used to rate limit log. */\n\tbool new_start; /* New external start, to determine log type. */\n#ifdef AUTH\n\tstruct authstate auth;\n#endif\n};\n\n#define D6_STATE(ifp) ((struct dhcp6_state *)(ifp)->if_data[IF_DATA_DHCP6])\n#define D6_CSTATE(ifp) \\\n\t((const struct dhcp6_state *)(ifp)->if_data[IF_DATA_DHCP6])\n#define D6_STATE_RUNNING(ifp)                            \\\n\t(D6_CSTATE((ifp)) && D6_CSTATE((ifp))->reason && \\\n\t    dhcp6_dadcompleted((ifp)))\n\nint dhcp6_openraw(void);\nint dhcp6_openudp(unsigned int, struct in6_addr *);\nvoid dhcp6_recvmsg(struct dhcpcd_ctx *, struct msghdr *, struct ipv6_addr *);\nvoid dhcp6_printoptions(const struct dhcpcd_ctx *, const struct dhcp_opt *,\n    size_t);\nconst struct ipv6_addr *dhcp6_iffindaddr(const struct interface *ifp,\n    const struct in6_addr *addr, unsigned int flags);\nstruct ipv6_addr *dhcp6_findaddr(struct dhcpcd_ctx *, const struct in6_addr *,\n    unsigned int);\nsize_t dhcp6_find_delegates(struct interface *);\nint dhcp6_start(struct interface *, enum DH6S);\nvoid dhcp6_reboot(struct interface *);\nvoid dhcp6_renew(struct interface *);\nssize_t dhcp6_env(FILE *, const char *, const struct interface *,\n    const struct dhcp6_message *, size_t);\nvoid dhcp6_free(struct interface *);\nvoid dhcp6_handleifa(int, struct ipv6_addr *, pid_t);\nbool dhcp6_dadcompleted(const struct interface *);\nvoid dhcp6_abort(struct interface *);\nvoid dhcp6_drop(struct interface *, const char *);\nint dhcp6_dump(struct interface *);\n#endif /* DHCP6 */\n\n#endif /* DHCP6_H */\n"
  },
  {
    "path": "src/dhcpcd-definitions-small.conf",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n# All rights reserved\n\n# Bare essentials for automatic IP configuration\n\n##############################################################################\n# DHCP RFC2132 options unless otheriwse stated\ndefine 1\trequest ipaddress\tsubnet_mask\n# RFC3442 states that the CSR has to come before all other routes\n# For completeness we also specify static routes then routers\ndefine 121\trfc3442\t\t\tclassless_static_routes\ndefine 3\trequest array ipaddress\trouters\ndefine 6\tarray ipaddress\t\tdomain_name_servers\ndefine 12\tdname\t\t\thost_name\ndefine 15\tarray dname\t\tdomain_name\ndefine 26\tuint16\t\t\tinterface_mtu\ndefine 28\trequest ipaddress\tbroadcast_address\ndefine 33\tarray ipaddress\t\tstatic_routes\ndefine 50\tipaddress\t\tdhcp_requested_address\ndefine 51\tuint32\t\t\tdhcp_lease_time\ndefine 52\tbyte\t\t\tdhcp_option_overload\ndefine 53\tbyte\t\t\tdhcp_message_type\ndefine 54\tipaddress\t\tdhcp_server_identifier\ndefine 55\tarray byte\t\tdhcp_parameter_request_list\ndefine 56\tstring\t\t\tdhcp_message\ndefine 57\tuint16\t\t\tdhcp_max_message_size\ndefine 58\tuint32\t\t\tdhcp_renewal_time\ndefine 59\tuint32\t\t\tdhcp_rebinding_time\ndefine 60\tstring\t\t\tvendor_class_identifier\ndefine 61\tbinhex\t\t\tdhcp_client_identifier\n\n# DHCP Rapid Commit, RFC4039\ndefine 80\tnorequest flag\t\trapid_commit\n\n# DHCP Fully Qualified Domain Name, RFC4702\ndefine 81\tembed\t\t\tfqdn\nembed\t\tbitflags=0000NEOS\tflags\nembed\t\tbyte\t\t\trcode1\nembed\t\tbyte\t\t\trcode2\n# dhcpcd always sets the E bit which means the fqdn itself is always\n# RFC1035 encoded.\n# The server MUST use the encoding as specified by the client as noted\n# in RFC4702 Section 2.1.\nembed\t\toptional domain\t\tfqdn\n\n# DHCP Domain Search, RFC3397\ndefine 119\tarray domain\t\tdomain_search\n\n# Option 249 is an IANA assigned private number used by Windows DHCP servers\n# to provide the exact same information as option 121, classless static routes\ndefine 249\trfc3442\t\t\tms_classless_static_routes\n\n##############################################################################\n# ND6 options, RFC4861\ndefinend 1\tbinhex\t\t\tsource_address\ndefinend 2\tbinhex\t\t\ttarget_address\n\ndefinend 3\tindex embed\t\tprefix_information\nembed\t\tbyte\t\t\tlength\nembed\t\tbitflags=LAH\t\tflags\nembed\t\tuint32\t\t\tvltime\nembed\t\tuint32\t\t\tpltime\nembed\t\tuint32\t\t\treserved\nembed\t\tip6address\t\tprefix\n\n# option 4 is only for Redirect messages\n\ndefinend 5\tembed\t\t\tmtu\nembed\t\tuint16\t\t\treserved\nembed\t\tuint32\t\t\tmtu\n\ndefinend 24\tindex embed\t\troute_information\nembed\t\tbyte\t\t\tlength\n# bits 4 and 5 are route preference\nembed\t\tbitflags=00011\t\tprf\nembed\t\tuint32\t\t\tlifetime\nembed\t\toptional truncated ip6address\tprefix\n\n# ND6 options, RFC6101\ndefinend 25\tindex embed\t\trdnss\nembed\t\tuint16\t\t\treserved\nembed\t\tuint32\t\t\tlifetime\nembed\t\tarray ip6address\tservers\n\ndefinend 31\tindex embed\t\tdnssl\nembed\t\tuint16\t\t\treserved\nembed\t\tuint32\t\t\tlifetime\nembed\t\tarray domain\t\tsearch\n\n##############################################################################\n# DHCPv6 options, RFC3315\ndefine6 1\tbinhex\t\t\tclient_id\ndefine6 2\tbinhex\t\t\tserver_id\n\ndefine6 3\tnorequest index embed\tia_na\nembed\t\tbinhex:4\t\tiaid\nembed\t\tuint32\t\t\tt1\nembed\t\tuint32\t\t\tt2\nencap 5\t\toption\nencap 13\toption\n\ndefine6 4\tnorequest index embed\tia_ta\nembed\t\tuint32\t\t\tiaid\nencap 5\t\toption\nencap 13\toption\n\ndefine6 5\tnorequest index embed\tia_addr\nembed\t\tip6address\t\tia_addr\nembed\t\tuint32\t\t\tpltime\nembed\t\tuint32\t\t\tvltime\nencap 13\toption\n\ndefine6 7\tbyte\t\t\tpreference\ndefine6 12\tip6address\t\tunicast\n\ndefine6 13\tnorequest embed\t\tstatus_code\nembed\t\tuint16\t\t\tstatus_code\nembed\t\toptional string\t\tmessage\n\ndefine6 18\tbinhex\t\t\tinterface_id\ndefine6 19\tbyte\t\t\treconfigure_msg\ndefine6 20\tflag\t\t\treconfigure_accept\n\n# DHCPv6 DNS Configuration Options, RFC3646\ndefine6 23\tarray ip6address\tname_servers\ndefine6 24\tarray domain\t\tdomain_search\n\n# DHCPv6 Fully Qualified Domain Name, RFC4704\ndefine6 39\tembed\t\t\tfqdn\nembed\t\tbitflags=00000NOS\tflags\nembed\t\toptional domain\t\tfqdn\n\n# DHCPv6 SOL_MAX_RT, RFC7083\ndefine6 82\trequest uint32\t\tsol_max_rt\ndefine6\t83\trequest uint32\t\tinf_max_rt\n"
  },
  {
    "path": "src/dhcpcd-definitions.conf",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n# All rights reserved\n\n# DHCP option definitions for dhcpcd(8)\n# These are used to translate DHCP options into shell variables\n# for use in dhcpcd-run-hooks(8)\n# See dhcpcd.conf(5) for details\n\n##############################################################################\n# DHCP RFC2132 options unless otheriwse stated\ndefine 1\trequest ipaddress\tsubnet_mask\n# RFC3442 states that the CSR has to come before all other routes\n# For completeness we also specify static routes then routers\ndefine 121\trfc3442\t\t\tclassless_static_routes\ndefine 2\tint32\t\t\ttime_offset\ndefine 3\trequest array ipaddress\trouters\ndefine 4\tarray ipaddress\t\ttime_servers\ndefine 5\tarray ipaddress\t\tien116_name_servers\ndefine 6\tarray ipaddress\t\tdomain_name_servers\ndefine 7\tarray ipaddress\t\tlog_servers\ndefine 8\tarray ipaddress\t\tcookie_servers\ndefine 9\tarray ipaddress\t\tlpr_servers\ndefine 10\tarray ipaddress\t\timpress_servers\ndefine 11\tarray ipaddress\t\tresource_location_servers\ndefine 12\tdname\t\t\thost_name\ndefine 13\tuint16\t\t\tboot_size\ndefine 14\tstring\t\t\tmerit_dump\n# Technically domain_name is not an array, but many servers expect clients\n# to treat it as one.\ndefine 15\tarray dname\t\tdomain_name\ndefine 16\tipaddress\t\tswap_server\ndefine 17\tstring\t\t\troot_path\ndefine 18\tstring\t\t\textensions_path\ndefine 19\tbyte\t\t\tip_forwarding\ndefine 20\tbyte\t\t\tnon_local_source_routing\ndefine 21\tarray ipaddress\t\tpolicy_filter\ndefine 22\tuint16\t\t\tmax_dgram_reassembly\ndefine 23\tbyte\t\t\tdefault_ip_ttl\ndefine 24\tuint32\t\t\tpath_mtu_aging_timeout\ndefine 25\tarray uint16\t\tpath_mtu_plateau_table\ndefine 26\tuint16\t\t\tinterface_mtu\ndefine 27\tbyte\t\t\tall_subnets_local\ndefine 28\trequest ipaddress\tbroadcast_address\ndefine 29\tbyte\t\t\tperform_mask_discovery\ndefine 30\tbyte\t\t\tmask_supplier\ndefine 31\tbyte\t\t\trouter_discovery\ndefine 32\tipaddress\t\trouter_solicitation_address\ndefine 33\tarray ipaddress\t\tstatic_routes\ndefine 34\tbyte\t\t\ttrailer_encapsulation\ndefine 35\tuint32\t\t\tarp_cache_timeout\ndefine 36\tuint16\t\t\tieee802_3_encapsulation\ndefine 37\tbyte\t\t\tdefault_tcp_ttl\ndefine 38\tuint32\t\t\ttcp_keepalive_interval\ndefine 39\tbyte\t\t\ttcp_keepalive_garbage\ndefine 40\tstring\t\t\tnis_domain\ndefine 41\tarray ipaddress\t\tnis_servers\ndefine 42\tarray ipaddress\t\tntp_servers\ndefine 43\tbinhex\t\t\tvendor_encapsulated_options\ndefine 44\tarray ipaddress\t\tnetbios_name_servers\ndefine 45\tipaddress\t\tnetbios_dd_server\ndefine 46\tbyte\t\t\tnetbios_node_type\ndefine 47\tstring\t\t\tnetbios_scope\ndefine 48\tarray ipaddress\t\tfont_servers\ndefine 49\tarray ipaddress\t\tx_display_manager\ndefine 50\tipaddress\t\tdhcp_requested_address\ndefine 51\tuint32\t\t\tdhcp_lease_time\ndefine 52\tbyte\t\t\tdhcp_option_overload\ndefine 53\tbyte\t\t\tdhcp_message_type\ndefine 54\tipaddress\t\tdhcp_server_identifier\ndefine 55\tarray byte\t\tdhcp_parameter_request_list\ndefine 56\tstring\t\t\tdhcp_message\ndefine 57\tuint16\t\t\tdhcp_max_message_size\ndefine 58\tuint32\t\t\tdhcp_renewal_time\ndefine 59\tuint32\t\t\tdhcp_rebinding_time\ndefine 60\tstring\t\t\tvendor_class_identifier\ndefine 61\tbinhex\t\t\tdhcp_client_identifier\ndefine 64\tstring\t\t\tnisplus_domain\ndefine 65\tarray ipaddress\t\tnisplus_servers\ndefine 66\tdname\t\t\ttftp_server_name\ndefine 67\tstring\t\t\tbootfile_name\ndefine 68\tarray ipaddress\t\tmobile_ip_home_agent\ndefine 69\tarray ipaddress\t\tsmtp_server\ndefine 70\tarray ipaddress\t\tpop_server\ndefine 71\tarray ipaddress\t\tnntp_server\ndefine 72\tarray ipaddress\t\twww_server\ndefine 73\tarray ipaddress\t\tfinger_server\ndefine 74\tarray ipaddress\t\tirc_server\ndefine 75\tarray ipaddress\t\tstreettalk_server\ndefine 76\tarray ipaddress\t\tstreettalk_directory_assistance_server\n\n# DHCP User Class, RFC3004\ndefine 77\tbinhex\t\t\tuser_class\n\n# DHCP SLP Directory Agent, RFC2610\ndefine 78\tembed\t\t\tslp_agent\nembed\t\tbyte\t\t\tmandatory\nembed\t\tarray ipaddress\t\taddress\ndefine 79\tembed\t\t\tslp_service\nembed\t\tbyte\t\t\tmandatory\nembed\t\tascii\t\t\tscope_list\n\n# DHCP Rapid Commit, RFC4039\ndefine 80\tnorequest flag\t\trapid_commit\n\n# DHCP Fully Qualified Domain Name, RFC4702\ndefine 81\tembed\t\t\tfqdn\nembed\t\tbitflags=0000NEOS\tflags\nembed\t\tbyte\t\t\trcode1\nembed\t\tbyte\t\t\trcode2\n# dhcpcd always sets the E bit which means the fqdn itself is always\n# RFC1035 encoded.\n# The server MUST use the encoding as specified by the client as noted\n# in RFC4702 Section 2.1.\nembed\t\toptional domain\t\tfqdn\n\n# Option 82 is for Relay Agents and DHCP servers\n\n# iSNS, RFC4174\ndefine 83\tembed\t\t\tisns\nembed\t\tbyte\t\t\treserved1\nembed\t\tbitflags=00000SAE\tfunctions\nembed\t\tbyte\t\t\treserved2\nembed\t\tbitflags=00fFsSCE\tdd\nembed\t\tbyte\t\t\treserved3\nembed\t\tbitflags=0000DMHE\tadmin\nembed\t\tuint16\t\t\treserved4\nembed\t\tbyte\t\t\treserved5\nembed\t\tbitflags=0TXPAMSE\tserver_security\nembed\t\tarray ipaddress\t\tservers\n\n# Option 84 are unused, RFC3679\n\n# DHCP Novell Directory Services, RFC2241\ndefine 85\tarray ipaddress\t\tnds_servers\ndefine 86\traw\t\t\tnds_tree_name\ndefine 87\traw\t\t\tnds_context\n\n# DHCP Broadcast and Multicast Control Server, RFC4280\ndefine 88\tarray domain\t\tbcms_controller_names\ndefine 89\tarray ipaddress\t\tbcms_controller_address\n\n# DHCP Authentication, RFC3118\ndefine 90\tembed\t\t\tauth\nembed\t\tbyte\t\t\tprotocol\nembed\t\tbyte\t\t\talgorithm\nembed\t\tbyte\t\t\trdm\nembed\t\tbinhex:8\t\treplay\nembed\t\tbinhex\t\t\tinformation\n\n# DHCP Leasequery, RFC4388\ndefine 91\tuint32\t\t\tclient_last_transaction_time\ndefine 92\tarray ipaddress\t\tassociated_ip\n\n# DHCP Options for Intel Preboot eXecution Environent (PXE), RFC4578\n# Options 93, 94 and 97 are used but of no use to dhcpcd\n\n# Option 95 used by Apple but never published RFC3679\n# Option 96 is unused, RFC3679\n\n# DHCP The Open Group's User Authentication Protocol, RFC2485\ndefine 98\tstring\t\t\tuap_servers\n\n# DHCP Civic Addresses Configuration Information, RFC4776\ndefine 99\tencap\t\t\tgeoconf_civic\nembed\t\tbyte\t\t\twhat\nembed\t\tuint16\t\t\tcountry_code\n# The rest of this option is not supported\n\n# DHCP Timezone, RFC4883\ndefine 100\tstring\t\t\tposix_timezone\ndefine 101\tstring\t\t\ttzdb_timezone\n\n# Options 102-115 are unused, RFC3679\n\n# DHCP IPv6-Only Preferred, RFC8925\ndefine 108\tuint32\t\t\tipv6_only_preferred\n\n# DHCP Captive Portal, RFC8910\ndefine 114\tstring\t\t\tcaptive_portal_uri\n\n# DHCP Auto-Configuration, RFC2563\ndefine 116\tbyte\t\t\tauto_configure\n\n# DHCP Name Service Search, RFC2937\ndefine 117\tarray uint16\t\tname_service_search\n\n# DHCP Subnet Selection, RFC3011\ndefine 118\tipaddress\t\tsubnet_selection\n\n# DHCP Domain Search, RFC3397\ndefine 119\tarray domain\t\tdomain_search\n\n# DHCP Session Initiated Protocol Servers, RFC3361\ndefine 120\trfc3361\t\t\tsip_server\n\n# Option 121 is defined at the top of this file\n\n# DHCP CableLabs Client, RFC3495\ndefine 122\tencap\t\t\ttsp\nencap 1\t\tipaddress\t\tdhcp_server\nencap 2\t\tipaddress\t\tdhcp_secondary_server\nencap 3\t\trfc3361\t\t\tprovisioning_server\nencap 4\t\tembed\t\t\tas_req_as_rep_backoff\nembed\t\tuint32\t\t\tnominal\nembed\t\tuint32\t\t\tmaximum\nembed\t\tuint32\t\t\tretry\nencap 5\t\tembed\t\t\tap_req_ap_rep_backoff\nembed\t\tuint32\t\t\tnominal\nembed\t\tuint32\t\t\tmaximum\nembed\t\tuint32\t\t\tretry\nencap 6\t\tdomain\t\t\tkerberos_realm\nencap 7\t\tbyte\t\t\tticket_granting_server_utilization\nencap 8\t\tbyte\t\t\tprovisioning_timer\n\n# DHCP Coordinate LCI, RFC6225\n# We have no means of expressing 6 bit lengths\ndefine 123\tbinhex\t\t\tgeoconf\n\n# DHCP Vendor-Identifying Vendor Options, RFC3925\ndefine 124\tbinhex\t\t\tvivco\ndefine 125\tembed\t\t\tvivso\nembed\t\tuint32\t\t\tenterprise_number\n# Vendor options are shared between DHCP/DHCPv6\n# Their code is matched to the enterprise number defined above\n# see the end of this file for an example\n\n# Options 126 and 127 are unused, RFC3679\n\n# DHCP Options for Intel Preboot eXecution Environent (PXE), RFC4578\n# Options 128-135 are used but of no use to dhcpcd\n\n# DHCP PANA Authentication Agent, RFC5192\ndefine 136\tarray ipaddress\t\tpana_agent\n\n# DHCP Lost Server, RFC5223\ndefine 137\tdomain\t\t\tlost_server\n\n# DHCP CAPWAP, RFC5417\ndefine 138\tarray ipaddress\t\tcapwap_ac\n\n# DHCP Mobility Services, RFC5678\ndefine 139\tencap\t\t\tmos_ip\nencap 1\t\tarray ipaddress\t\tis\nencap 2\t\tarray ipaddress\t\tcs\nencap 3\t\tarray ipaddress\t\tes\ndefine 140\tencap\t\t\tmos_domain\nencap 1\t\tdomain\t\t\tis\nencap 2\t\tdomain\t\t\tcs\nencap 3\t\tdomain\t\t\tes\n\n# DHCP SIP UA, RFC6011\ndefine 141\tarray domain\t\tsip_ua_cs_list\n\n# DHCP ANDSF, RFC6153\ndefine 142\tarray ipaddress\t\tandsf\n\n# DHCP SZTP Redirect, RFC8572\ndefine 143\tarray uri\t\tsztp_redirect\n\n# DHCP Coordinate LCI, RFC6225\n# We have no means of expressing 6 bit lengths\ndefine 144\tbinhex\t\t\tgeoloc\n\n# DHCP FORCERENEW Nonce Capability, RFC6704\ndefine 145\tarray byte\t\tforcerenew_nonce_capable\n\n# DHCP RDNSS Selection for MIF Nodes, RFC6731\ndefine 146\tembed\t\t\trdnss_selection\nembed\t\tbyte\t\t\tprf\nembed\t\tipaddress\t\tprimary\nembed\t\tipaddress\t\tsecondary\nembed\t\tarray domain\t\tdomains\n\n# Option 149 is unused, RFC3942\n\n# DHCP DOTS, DDoS Open Threat Signaling (DOTS) Agent Discovery RFC8973\ndefine 147\tdomain\t\t\tdots_ri\ndefine 148\tarray ipaddress\t\tdots_address\n\n# DHCP TFTP Server Address, RFC5859\ndefine 150\tarray ipaddress\t\ttftp_servers\n\n# DHCP Bulk Lease Query, RFC6926\n# dhcpcd doesn't perform a lease query, but if it did these\n# fields might be of use\n#define 151\tembed\t\t\tblklqry\n#embed\t\tbyte\t\t\tstatus_code\n#embed\t\tstring\t\t\tstatus_msg\n\n#define 152\tuint32\t\t\tblklqry_base_time\n#define 153\tuint32\t\t\tblklqry_state_start_time\n#define 154\tuint32\t\t\tblklqry_start_time\n#define 155\tuint32\t\t\tblklqry_end_time\n#define 156\tbyte\t\t\tblklqry_state\n#define 157\tbitflags=0000000R\tblklqry_source\n\n# DHCP MUD URL, RFC8520\ndefine 161\tstring\t\t\tmudurl\n\n# Apart from 161...\n# Options 151-157 are used for Lease Query, RFC6926 and not for dhcpcd\n# Options 158-174 are unused, RFC3942\n\n# Options 175-177 are tentativel assigned for Etherboot\n# Options 178-207 are unused, RFC3942\n\n# DHCP PXELINUX, RFC5071\ndefine 208\tbinhex\t\t\tpxelinux_magic\ndefine 209\tstring\t\t\tconfig_file\ndefine 210\tstring\t\t\tpath_prefix\ndefine 211\tuint32\t\t\treboot_time\n\n# DHCP IPv6 Rapid Deployment on IPv4 Infrastructures, RFC5969\ndefine 212\tembed\t\t\tsixrd\nembed\t\tbyte\t\t\tmask_len\nembed\t\tbyte\t\t\tprefix_len\nembed\t\tip6address\t\tprefix\nembed\t\tarray ipaddress\t\tbrip_address\n\n# DHCP Access Network Domain Name, RFC5986\ndefine 213\tdomain\t\t\taccess_domain\n\n# Options 214-219 are unused, RFC3942\n\n# DHCP Subnet Allocation, RFC6656\n# Option 220 looks specific to Cisco hardware.\n\n# DHCP Virtual Subnet Selection, RFC6607\ndefine 221\tencap\t\t\tvss\nencap 0\t\tstring\t\t\tnvt\nencap 1\t\tbinhex\t\t\tvpn_id\nencap 255\tflag\t\t\tglobal\n\n# Options 222 and 223 are unused, RFC3942\n\n# Options 224-254 are reserved for site-specific use by RFC3942.\n# For historical reasons, some of these options have well known\n# definitions and we implement those definitions here.\n# Site-specific options are designed to be configured by the end user\n# if needed and any configuration here may change in the future.\n\n# Option 245 is an IANA assigned private number used by Azure DHCP\n# servers to provide the IPv4 address of the Azure WireServer endpoint\n# to virtual machines hosted in Azure.\ndefine 245\tipaddress\t\tazureendpoint\n\n# Option 249 is an IANA assigned private number used by Windows DHCP servers\n# to provide the exact same information as option 121, classless static routes\ndefine 249\trfc3442\t\t\tms_classless_static_routes\n\n# An expired RFC for Web Proxy Auto Discovery Protocol does define\n# Option 252 which is commonly used by major browsers.\n# Apparently the code was assigned by agreement of the DHC working group chair.\ndefine 252\turi\t\t\twpad_url\n\ndefine 224\tbinhex\t\t\tsite_specific_224\ndefine 225\tbinhex\t\t\tsite_specific_225\ndefine 226\tbinhex\t\t\tsite_specific_226\ndefine 227\tbinhex\t\t\tsite_specific_227\ndefine 228\tbinhex\t\t\tsite_specific_228\ndefine 229\tbinhex\t\t\tsite_specific_229\ndefine 230\tbinhex\t\t\tsite_specific_230\ndefine 231\tbinhex\t\t\tsite_specific_231\ndefine 232\tbinhex\t\t\tsite_specific_232\ndefine 233\tbinhex\t\t\tsite_specific_233\ndefine 234\tbinhex\t\t\tsite_specific_234\ndefine 235\tbinhex\t\t\tsite_specific_235\ndefine 236\tbinhex\t\t\tsite_specific_236\ndefine 237\tbinhex\t\t\tsite_specific_237\ndefine 238\tbinhex\t\t\tsite_specific_238\ndefine 239\tbinhex\t\t\tsite_specific_239\ndefine 240\tbinhex\t\t\tsite_specific_240\ndefine 241\tbinhex\t\t\tsite_specific_241\ndefine 242\tbinhex\t\t\tsite_specific_242\ndefine 243\tbinhex\t\t\tsite_specific_243\ndefine 244\tbinhex\t\t\tsite_specific_244\n#Option 245 has a custom definition above.\ndefine 246\tbinhex\t\t\tsite_specific_246\ndefine 247\tbinhex\t\t\tsite_specific_247\ndefine 248\tbinhex\t\t\tsite_specific_248\n#Option 249 has a custom definition above.\ndefine 250\tbinhex\t\t\tsite_specific_250\ndefine 251\tbinhex\t\t\tsite_specific_251\n#Option 252 has a custom definition above.\ndefine 253\tbinhex\t\t\tsite_specific_253\ndefine 254\tbinhex\t\t\tsite_specific_254\n\n# Option 255 End\n\n##############################################################################\n# ND6 options, RFC4861\ndefinend 1\tbinhex\t\t\tsource_address\ndefinend 2\tbinhex\t\t\ttarget_address\n\ndefinend 3\tindex embed\t\tprefix_information\nembed\t\tbyte\t\t\tlength\nembed\t\tbitflags=LAH\t\tflags\nembed\t\tuint32\t\t\tvltime\nembed\t\tuint32\t\t\tpltime\nembed\t\tuint32\t\t\treserved\nembed\t\tip6address\t\tprefix\n\n# option 4 is only for Redirect messages\n\ndefinend 5\tembed\t\t\tmtu\nembed\t\tuint16\t\t\treserved\nembed\t\tuint32\t\t\tmtu\n\n# ND6 Mobile IP, RFC6275\ndefinend 8\tembed\t\t\thomeagent_information\nembed\t\tuint16\t\t\treserved\nembed\t\tuint16\t\t\tpreference\nembed\t\tuint16\t\t\tlifetime\n\ndefinend 24\tindex embed\t\troute_information\nembed\t\tbyte\t\t\tlength\n# bits 4 and 5 are route preference\nembed\t\tbitflags=00011\t\tprf\nembed\t\tuint32\t\t\tlifetime\nembed\t\toptional truncated ip6address\tprefix\n\n# ND6 options, RFC6101\ndefinend 25\tindex embed\t\trdnss\nembed\t\tuint16\t\t\treserved\nembed\t\tuint32\t\t\tlifetime\nembed\t\tarray ip6address\tservers\n\ndefinend 31\tindex embed\t\tdnssl\nembed\t\tuint16\t\t\treserved\nembed\t\tuint32\t\t\tlifetime\nembed\t\tarray domain\t\tsearch\n\n##############################################################################\n# DHCPv6 options, RFC3315\ndefine6 1\tbinhex\t\t\tclient_id\ndefine6 2\tbinhex\t\t\tserver_id\n\ndefine6 3\tnorequest index embed\tia_na\nembed\t\tbinhex:4\t\tiaid\nembed\t\tuint32\t\t\tt1\nembed\t\tuint32\t\t\tt2\nencap 5\t\toption\nencap 13\toption\n\ndefine6 4\tnorequest index embed\tia_ta\nembed\t\tuint32\t\t\tiaid\nencap 5\t\toption\nencap 13\toption\n\ndefine6 5\tnorequest index embed\tia_addr\nembed\t\tip6address\t\tia_addr\nembed\t\tuint32\t\t\tpltime\nembed\t\tuint32\t\t\tvltime\nencap 13\toption\n\ndefine6 6\tarray uint16\t\toption_request\ndefine6 7\tbyte\t\t\tpreference\ndefine6 8\tuint16\t\t\telased_time\ndefine6 9\tbinhex\t\t\tdhcp_relay_msg\n\n# Option 10 is unused\n\ndefine6 11\tembed\t\t\tauth\nembed\t\tbyte\t\t\tprotocol\nembed\t\tbyte\t\t\talgorithm\nembed\t\tbyte\t\t\trdm\nembed\t\tbinhex:8\t\treplay\nembed\t\tbinhex\t\t\tinformation\n\ndefine6 12\tip6address\t\tunicast\n\ndefine6 13\tnorequest embed\t\tstatus_code\nembed\t\tuint16\t\t\tstatus_code\nembed\t\toptional string\t\tmessage\n\ndefine6 14\tnorequest flag\t\trapid_commit\ndefine6 15\tbinhex\t\t\tuser_class\n\ndefine6 16\tbinhex\t\t\tvivco\ndefine6 17\tembed\t\t\tvivso\nembed\t\tuint32\t\t\tenterprise_number\n# Vendor options are shared between DHCP/DHCPv6\n# Their code is matched to the enterprise number defined above\n# See the end of this file for an example\n\ndefine6 18\tbinhex\t\t\tinterface_id\ndefine6 19\tbyte\t\t\treconfigure_msg\ndefine6 20\tflag\t\t\treconfigure_accept\n\n# DHCPv6 Session Initiation Protocol Options, RFC3319\ndefine6 21\tarray domain\t\tsip_servers_names\ndefine6 22\tarray ip6address\tsip_servers_addresses\n\n# DHCPv6 DNS Configuration Options, RFC3646\ndefine6 23\tarray ip6address\tname_servers\ndefine6 24\tarray domain\t\tdomain_search\n\n# DHCPv6 Prefix Options, RFC6603\ndefine6 25\tnorequest index embed\tia_pd\nembed\t\tbinhex:4\t\tiaid\nembed\t\tuint32\t\t\tt1\nembed\t\tuint32\t\t\tt2\nencap 26\toption\ndefine6 26\tindex embed\t\tprefix\nembed\t\tuint32\t\t\tpltime\nembed\t\tuint32\t\t\tvltime\nembed\t\tbyte\t\t\tlength\nembed\t\tip6address\t\tprefix\nencap 13\toption\nencap 67\toption\n\n# DHCPv6 Network Information Service Options, RFC3898\ndefine6 27\tarray ip6address\tnis_servers\ndefine6 28\tarray ip6address\tnisp_servers\ndefine6 29\tstring\t\t\tnis_domain_name\ndefine6 30\tstring\t\t\tnisp_domain_name\n\n# DHCPv6 Simple Network Time Protocol Servers Option, RFC4075\ndefine6 31\tarray ip6address\tsntp_servers\n\n# DHCPv6 Information Refresh Time, RFC4242\ndefine6 32\tuint32\t\t\tinfo_refresh_time\n\n# DHCPv6 Broadcast and Multicast Control Server, RFC4280\ndefine6 33\tarray domain\t\tbcms_server_d\ndefine6 34\tarray ip6address\tbcms_server_a\n\n# DHCP Civic Addresses Configuration Information, RFC4776\ndefine6 36\tencap\t\t\tgeoconf_civic\nembed\t\tbyte\t\t\twhat\nembed\t\tuint16\t\t\tcountry_code\n# The rest of this option is not supported\n\n# DHCP Relay Agent Remote-ID, RFC4649\ndefine6 37\tembed\t\t\tremote_id\nembed\t\tuint32\t\t\tenterprise_number\nembed\t\tbinhex\t\t\tremote_id\n\n# DHCP Relay Agent Subscriber-ID, RFC4580\ndefine6 38\tbinhex\t\t\tsubscriber_id\n\n# DHCPv6 Fully Qualified Domain Name, RFC4704\ndefine6 39\tembed\t\t\tfqdn\nembed\t\tbitflags=00000NOS\tflags\nembed\t\toptional domain\t\tfqdn\n\n# DHCPv6 PANA Authentication Agnet, RC5192\ndefine6 40\tarray ip6address\tpana_agent\n\n# DHCPv6 Timezone options, RFC4883\ndefine6 41\tstring\t\t\tposix_timezone\ndefine6 42\tstring\t\t\ttzdb_timezone\n\n# DHCPv6 Relay Agent Echo Request\ndefine6 43\tarray uint16\t\tero\n\n# Options 44-48 are used for Lease Query, RFC5007 and not for dhcpcd\n\n# DHCPv6 Home Info Discovery in MIPv6, RFC6610\ndefine6 49\tdomain\t\t\tmip6_hnidf\ndefine6 50\tencap\t\t\tmip6_vdinf\nencap 71\toption\nencap 72\toption\nencap 73\toption\n\n# DHCPv6 Lost Server, RFC5223\ndefine6 51\tdomain\t\t\tlost_server\n\n# DHCPv6 CAPWAP, RFC5417\ndefine6 52\tarray ip6address\tcapwap_ac\n\n# DHCPv6 Relay-ID, RFC5460\ndefine6 53\tbinhex\t\t\trelay_id\n\n# DHCP Mobility Services, RFC5678\ndefine6 54\tencap\t\t\tmos_ip\nencap 1\t\tarray ip6address\tis\nencap 2\t\tarray ip6address\tcs\nencap 3\t\tarray ip6address\tes\ndefine6 55 \tencap\t\t\tmos_domain\nencap 1\t\tdomain\t\t\tis\nencap 2\t\tdomain\t\t\tcs\nencap 3\t\tdomain\t\t\tes\n\n# DHCPv6 Network Time Protocol Server, RFC5908\ndefine6 56\tencap\t\t\tntp_server\nencap 1\t\tip6address\t\taddr\nencap 2\t\tip6address\t\tmcast_addr\nencap 3\t\tdomain\t\t\tfqdn\n\n# DHCPv6 LIS Discovery, RFC5986\ndefine6 57\tdomain\t\t\taccess_domain\n\n# DHCPv6 SIP UA, RFC6011\ndefine6 58\tarray domain\t\tsip_ua_cs_list\n\n# DHCPv6 Network Boot, RFC5970\ndefine6 59\turi\t\t\tbootfile_url\n# We presently cannot decode bootfile_param\ndefine6 60\tbinhex\t\t\tbootfile_param\ndefine6 61\tarray uint16\t\tarchitecture_types\ndefine6 62\tembed\t\t\tnii\nembed\t\tbyte\t\t\ttype\nembed\t\tbyte\t\t\tmajor\nembed\t\tbyte\t\t\tminor\n\n# DHCPv6 Coordinate LCI, RFC6225\n# We have no means of expressing 6 bit lengths\ndefine6 63\tbinhex\t\t\tgeoloc\n\n# DHCPv6 AFTR-Name, RFC6334\ndefine6 64\tdomain\t\t\taftr_name\n\n# DHCPv6 Prefix Exclude Option, RFC6603\ndefine6 67\tembed\t\t\tpd_exclude\nembed\t\tbyte\t\t\tprefix_len\nembed\t\tbinhex\t\t\tsubnetID\n\n# DHCPv6 Home Info Discovery in MIPv6, RFC6610\ndefine6 69\tencap\t\t\tmip6_idinf\nencap 71\toption\nencap 72\toption\nencap 73\toption\ndefine6 70\tencap\t\t\tmip6_udinf\nencap 71\toption\nencap 72\toption\nencap 73\toption\ndefine6\t71\tembed\t\t\tmip6_hnp\nembed\t\tbyte\t\t\tprefix_len\nembed\t\tip6address\t\tprefix\ndefine6 72\tip6address\t\tmip6_haa\ndefine6 73\tdomain\t\t\tmip6_haf\n\n# DHCPv6 RDNSS Selection for MIF Nodes, RFC6731\ndefine6 74\tembed\t\t\trdnss_selection\nembed\t\tip6address\t\tserver\nembed\t\tbyte\t\t\tprf\nembed\t\tarray domain\t\tdomains\n\n# DHCPv6 Kerberos, RFC6784\ndefine6 75\tstring\t\t\tkrb_principal_name\ndefine6 76\tstring\t\t\tkrb_realm_name\ndefine6 78\tembed\t\t\tkrb_kdc\nembed\t\tuint16\t\t\tpriority\nembed\t\tuint16\t\t\tweight\nembed\t\tbyte\t\t\ttransport_type\nembed\t\tuint16\t\t\tport\nembed\t\tip6address\t\taddress\nembed\t\tstring\t\t\trealm_name\n\n# DHCPv6 Client Link-Layer Address, RFC6939\n# Section 7 states that clients MUST ignore the option 79\n\n# DHCPv6 Relay-Triggered Reconfiguraion, RFC6977\ndefine6 80\tip6address\t\tlink_address\n\n# DHCPv6 Radius, RFC7037\n# Section 7 states that clients MUST ignore the option 81\n\n# DHCPv6 SOL_MAX_RT, RFC7083\ndefine6 82\trequest uint32\t\tsol_max_rt\ndefine6\t83\trequest uint32\t\tinf_max_rt\n\n# DHCPv6 Softwire Address and Port-Mapped Clients, RFC7598\ndefine6\t89\tembed\t\t\ts46_rule\nembed\t\tbitflags=0000000F\tflags\nembed\t\tbyte\t\t\tea_len\nembed\t\tbyte\t\t\tprefix4_len\nembed\t\tipaddress\t\tipv4_prefix\nembed\t\tip6address\t\tipv6_prefix\ndefine6\t90\tip6address\t\ts64_br\ndefine6\t91\tembed\t\t\ts46_dmr\nembed\t\tbyte\t\t\tprefix_len\nembed\t\tbinhex\t\t\tprefix\ndefine6\t92\tembed\t\t\ts46_v4v6bind\nembed\t\tipaddress\t\tipv4_address\nembed\t\tbyte\t\t\tipv6_prefix_len\nembed\t\tbinhex\t\t\tipv6_prefix_and_options\n# Cannot decode options after variable length address ...\n#encap\t93\toption\ndefine6\t93\tembed\t\t\ts46_portparams\nembed\t\tbyte\t\t\toffset\nembed\t\tbyte\t\t\tpsid_len\nembed\t\tuint16\t\t\tpsid\ndefine6\t94\tembed\t\t\ts46_cont_mape\nencap\t89\toption\nencap\t90\toption\ndefine6\t95\tembed\t\t\ts46_cont_mapt\nencap\t89\toption\nencap\t91\toption\ndefine6\t96\tembed\t\t\ts46_cont_lw\nencap\t90\toption\nencap\t92\toption\n\n# DHCPv6 Address Selection Policy\n# Currently not supported\n\n# DHCPv6 MUD URL, RFC8520\ndefine6 112\tstring\t\t\tmudurl\n\n# DHCP Captive Portal, RFC8910\ndefine6 103\turi\t\t\tcaptive_portal_uri\n\n# DHCP SZTP Redirect, RFC8572\ndefine6 136\tarray uri\t\tsztp_redirect\n\n# DHCP DDoS Open Threat Signaling (DOTS) Agent Discovery, RFC8973\ndefine6 141\tdomain\t\t\tdots_ri\ndefine6 142\tarray ip6address\tdots_address\n\n# DHCP ANDSF, RFC6153\ndefine6 143\tarray ip6address\tandsf6\n\n# Options 86-65535 are unasssinged\n\n##############################################################################\n# Vendor-Identifying Vendor Options\n# An example:\n#vendopt 12345\tencap\t\t\tfrobozzco\n#encap 1\tstring\t\t\tmaze_location\n#encap 2\tbyte\t\t\tgrue_probability\n"
  },
  {
    "path": "src/dhcpcd-embedded.c.in",
    "content": "/*\n * DO NOT EDIT!\n * Automatically generated from dhcpcd-embedded.conf\n * Ths allows us to simply generate DHCP structure without any C programming.\n */\n\n/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <unistd.h>\n\nconst char dhcpcd_embedded_conf[] =\n"
  },
  {
    "path": "src/dhcpcd-embedded.h.in",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifdef SMALL\n#define INITDEFINES\t@INITDEFINES_SMALL@\n#define INITDEFINENDS\t@INITDEFINENDS_SMALL@\n#define INITDEFINE6S\t@INITDEFINE6S_SMALL@\n#else\n#define INITDEFINES\t@INITDEFINES@\n#define INITDEFINENDS\t@INITDEFINENDS@\n#define INITDEFINE6S\t@INITDEFINE6S@\n#endif\n\nextern const char dhcpcd_embedded_conf[];\n"
  },
  {
    "path": "src/dhcpcd.8.in",
    "content": ".\\\" SPDX-License-Identifier: BSD-2-Clause\n.\\\" Copyright (c) 2006-2025 Roy Marples\n.\\\" All rights reserved\n.\\\"\n.\\\" Redistribution and use in source and binary forms, with or without\n.\\\" modification, are permitted provided that the following conditions\n.\\\" are met:\n.\\\" 1. Redistributions of source code must retain the above copyright\n.\\\"    notice, this list of conditions and the following disclaimer.\n.\\\" 2. Redistributions in binary form must reproduce the above copyright\n.\\\"    notice, this list of conditions and the following disclaimer in the\n.\\\"    documentation and/or other materials provided with the distribution.\n.\\\"\n.\\\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n.\\\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n.\\\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n.\\\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n.\\\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n.\\\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n.\\\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n.\\\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n.\\\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n.\\\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n.\\\" SUCH DAMAGE.\n.\\\"\n.Dd May 8, 2025\n.Dt DHCPCD 8\n.Os\n.Sh NAME\n.Nm dhcpcd\n.Nd a DHCP client\n.Sh SYNOPSIS\n.Nm\n.Op Fl 146ABbDdEGgHJKLMNPpqTV\n.Op Fl C , Fl Fl nohook Ar hook\n.Op Fl c , Fl Fl script Ar script\n.Op Fl e , Fl Fl env Ar value\n.Op Fl F , Fl Fl fqdn Ar FQDN\n.Op Fl f , Fl Fl config Ar file\n.Op Fl h , Fl Fl hostname Ar hostname\n.Op Fl I , Fl Fl clientid Ar clientid\n.Op Fl i , Fl Fl vendorclassid Ar vendorclassid\n.Op Fl j , Fl Fl logfile Ar logfile\n.Op Fl l , Fl Fl leasetime Ar seconds\n.Op Fl m , Fl Fl metric Ar metric\n.Op Fl O , Fl Fl nooption Ar option\n.Op Fl o , Fl Fl option Ar option\n.Op Fl Q , Fl Fl require Ar option\n.Op Fl r , Fl Fl request Ar address\n.Op Fl S , Fl Fl static Ar value\n.Op Fl s , Fl Fl inform Ar address Ns Op Ar /cidr Ns Op Ar /broadcast_address\n.Op Fl Fl inform6\n.Op Fl t , Fl Fl timeout Ar seconds\n.Op Fl u , Fl Fl userclass Ar class\n.Op Fl v , Fl Fl vendor Ar code , Ar value\n.Op Fl W , Fl Fl whitelist Ar address Ns Op Ar /cidr\n.Op Fl w\n.Op Fl Fl waitip Ns = Ns Op 4 | 6\n.Op Fl y , Fl Fl reboot Ar seconds\n.Op Fl X , Fl Fl blacklist Ar address Ns Op Ar /cidr\n.Op Fl Z , Fl Fl denyinterfaces Ar pattern\n.Op Fl z , Fl Fl allowinterfaces Ar pattern\n.Op Fl Fl inactive\n.Op Fl Fl configure\n.Op Fl Fl noconfigure\n.Op interface\n.Op ...\n.Nm\n.Fl n , Fl Fl rebind\n.Op interface\n.Nm\n.Fl k , Fl Fl release\n.Op interface\n.Nm\n.Fl U , Fl Fl dumplease\n.Op Ar interface\n.Nm\n.Fl Fl version\n.Nm\n.Fl x , Fl Fl exit\n.Op interface\n.Sh DESCRIPTION\n.Nm\nis an implementation of the DHCP client specified in\n.Li RFC 2131 .\n.Nm\ngets the host information\n.Po\nIP address, routes, etc\n.Pc\nfrom a DHCP server and configures the network\n.Ar interface\nof the\nmachine on which it is running.\n.Nm\nthen runs the configuration script which writes DNS information to\n.Xr resolvconf 8 ,\nif available, otherwise directly to\n.Pa /etc/resolv.conf .\nIf the hostname is currently blank, (null) or localhost, or\n.Va force_hostname\nis YES or TRUE or 1 then\n.Nm\nsets the hostname to the one supplied by the DHCP server.\n.Nm\nthen daemonises and waits for the lease renewal time to lapse.\nIt will then attempt to renew its lease and reconfigure if the new lease\nchanges when the lease begins to expire or the DHCP server sends a message\nto renew early.\n.Pp\nIf any interface reports a working carrier then\n.Nm\nwill try to obtain a lease before forking to the background,\notherwise it will fork right away.\nThis behaviour can be modified with the\n.Fl b , Fl Fl background\nand\n.Fl w , Fl Fl waitip\noptions.\n.Pp\n.Nm\nis also an implementation of the BOOTP client specified in\n.Li RFC 951 .\n.Pp\n.Nm\nis also an implementation of the IPv6 Router Solicitor as specified in\n.Li RFC 4861\nand\n.Li RFC 6106 .\n.Pp\n.Nm\nis also an implementation of the IPv6 Privacy Extensions to AutoConf as\nspecified in\n.Li RFC 4941 .\nThis feature needs to be enabled in the kernel and\n.Nm\nwill start using it.\n.Pp\n.Nm\nis also an implementation of the DHCPv6 client as specified in\n.Li RFC 3315 .\nBy default,\n.Nm\nonly starts DHCPv6 when instructed to do so by an IPV6 Router Advertisement.\nIf no Identity Association is configured,\nthen a Non-temporary Address is requested.\n.Ss Local Link configuration\nIf\n.Nm\nfailed to obtain a lease, it probes for a valid IPv4LL address\n.Po\naka ZeroConf, aka APIPA\n.Pc .\nOnce obtained it restarts the process of looking for a DHCP server to get a\nproper address.\n.Pp\nWhen using IPv4LL,\n.Nm\nnearly always succeeds and returns an exit code of 0.\nIn the rare case it fails, it normally means that there is a reverse ARP proxy\ninstalled which always defeats IPv4LL probing.\nTo disable this behaviour, you can use the\n.Fl L , Fl Fl noipv4ll\noption.\n.Ss Multiple interfaces\nIf a list of interfaces are given on the command line, then\n.Nm\nonly works with those interfaces, otherwise\n.Nm\ndiscovers available Ethernet interfaces that can be configured.\nWhen\n.Nm\nis not limited to one interface on the command line,\nit is running in Manager mode.\nThe\n.Nm dhcpcd-ui\nproject expects dhcpcd to be running this way.\n.Pp\nIf a single interface is given then\n.Nm\nonly works for that interface and runs as a separate instance to other\n.Nm\nprocesses.\nThe\n.Fl w , Fl Fl waitip\noption is enabled in this instance to maintain compatibility with older\nversions.\nUsing a single interface,\noptionally further limited to an address protocol,\nalso affects the\n.Fl k ,\n.Fl N ,\n.Fl n\nand\n.Fl x\noptions, where the same interface and any address protocol\nwill need to be specified, as a lack of an\ninterface will imply Manager mode which this is not.\nTo force starting in Manager mode with only one interface, the\n.Fl M , Fl Fl manager\noption can be used.\n.Pp\nInterfaces are preferred by carrier, DHCP lease/IPv4LL and then lowest metric.\nFor systems that support route metrics, each route will be tagged with the\nmetric, otherwise\n.Nm\nchanges the routes to use the interface with the same route and the lowest\nmetric.\nSee options below for controlling which interfaces we allow and deny through\nthe use of patterns.\n.Pp\nNon-ethernet interfaces and some virtual ethernet interfaces\nsuch as TAP and bridge are ignored by default,\nas is the FireWire interface.\nTo work with these devices they either need to be specified on the command line,\nbe listed in\n.Fl Fl allowinterfaces\nor have an interface directive in\n.Pa @SYSCONFDIR@/dhcpcd.conf .\n.Ss Hooking into events\n.Nm\nruns\n.Pa @SCRIPT@ ,\nor the script specified by the\n.Fl c , Fl Fl script\noption.\nThis script runs each script found in\n.Pa @HOOKDIR@\nin a lexical order.\nThe default installation supplies the scripts\n.Pa 01-test ,\n.Pa 20-resolv.conf\nand\n.Pa 30-hostname .\nYou can disable each script by using the\n.Fl C , Fl Fl nohook\noption.\nSee\n.Xr dhcpcd-run-hooks 8\nfor details on how these scripts work.\n.Nm\ncurrently ignores the exit code of the script.\n.Pp\nMore scripts are supplied in\n.Pa @DATADIR@/dhcpcd/hooks\nand need to be copied to\n.Pa @HOOKDIR@\nif you intend to use them.\nFor example, you could install\n.Pa 29-lookup-hostname\nso that\n.Nm\ncan lookup the hostname of the IP address in DNS if no hostname\nis given by the lease and one is not already set.\n.Ss Fine tuning\nYou can fine-tune the behaviour of\n.Nm\nwith the following options:\n.Bl -tag -width indent\n.It Fl b , Fl Fl background\nBackground immediately.\nThis is useful for startup scripts which don't disable link messages for\ncarrier status.\n.It Fl c , Fl Fl script Ar script\nUse this\n.Ar script\ninstead of the default\n.Pa @SCRIPT@ .\n.It Fl D , Fl Fl duid Op Ar ll | lt | uuid | value\nUse a DHCP Unique Identifier.\nIf persistent storage is available then a DUID-LLT\n(link local address + time) is generated,\notherwise DUID-LL is generated (link local address).\nThe DUID type can be hinted as an optional parameter if the file\n.Pa @DBDIR@/duid\ndoes not exist.\nIf not\n.Va ll ,\n.Va lt\nor\n.Va uuid\nthen\n.Va value\nwill be converted from 00:11:22:33 format.\nThis, plus the IAID will be used as the\n.Fl I , Fl Fl clientid .\nThe DUID generated will be held in\n.Pa @DBDIR@/duid\nand should not be copied to other hosts.\nThis file also takes precedence over the above rules except for setting a value.\n.It Fl d , Fl Fl debug\nEcho debug messages to the stderr and syslog.\n.It Fl E , Fl Fl lastlease\nIf\n.Nm\ncannot obtain a lease, then try to use the last lease acquired for the\ninterface.\n.It Fl Fl lastleaseextend\nSame as the above, but the lease will be retained even if it expires.\n.Nm\nwill give it up if any other host tries to claim it for their own via ARP.\nThis violates RFC 2131, section 3.7, which states the lease should be\ndropped once it has expired.\n.It Fl e , Fl Fl env Ar value\nPush\n.Ar value\nto the environment for use in\n.Xr dhcpcd-run-hooks 8 .\nFor example, you can force the hostname hook to always set the hostname with\n.Fl e\n.Va force_hostname=YES .\n.It Fl g , Fl Fl reconfigure\n.Nm\nwill re-apply IP address, routing and run\n.Xr dhcpcd-run-hooks 8\nfor each interface.\nThis is useful so that a 3rd party such as PPP or VPN can change the routing\ntable and / or DNS, etc and then instruct\n.Nm\nto put things back afterwards.\n.Nm\ndoes not read a new configuration when this happens - you should rebind if you\nneed that functionality.\n.It Fl F , Fl Fl fqdn Ar fqdn\nRequests that the DHCP server update DNS using FQDN instead of just a\nhostname.\nValid values for\n.Ar fqdn\nare disable, none, ptr and both.\n.Nm\nitself never does any DNS updates.\n.Nm\nencodes the FQDN hostname as specified in\n.Li RFC 1035 .\n.It Fl f , Fl Fl config Ar file\nSpecify a config to load instead of\n.Pa @SYSCONFDIR@/dhcpcd.conf .\n.Nm\nalways processes the config file before any command line options.\n.It Fl h , Fl Fl hostname Ar hostname\nSends\n.Ar hostname\nto the DHCP server so it can be registered in DNS.\nIf\n.Ar hostname\nis an empty string then the current system hostname is sent.\nIf\n.Ar hostname\nis a FQDN (i.e., contains a .) then it will be encoded as such.\n.It Fl I , Fl Fl clientid Ar clientid\nSend the\n.Ar clientid .\nIf the string is of the format 01:02:03 then it is encoded as hex.\nFor interfaces whose hardware address is longer than 8 bytes, or if the\n.Ar clientid\nis an empty string then\n.Nm\nsends a default\n.Ar clientid\nof the hardware family and the hardware address.\n.It Fl i , Fl Fl vendorclassid Ar vendorclassid\nOverride the DHCPv4\n.Ar vendorclassid\nfield sent.\nThe default is\ndhcpcd-<version>:<os>:<machine>:<platform>.\nFor example\n.D1 dhcpcd-5.5.6:NetBSD-6.99.5:i386:i386\nIf not set then none is sent.\nSome badly configured DHCP servers reject unknown vendorclassids.\nTo work around it, try and impersonate Windows by using the MSFT vendorclassid.\n.It Fl j , Fl Fl logfile Ar logfile\nWrites to the specified\n.Ar logfile .\n.Nm\nstill writes to\n.Xr syslog 3 .\nThe\n.Ar logfile\nis reopened when\n.Nm\nreceives the\n.Dv SIGUSR2\nsignal.\n.It Fl k , Fl Fl release Op Ar interface\nThis causes an existing\n.Nm\nprocess running on the\n.Ar interface\nto release its lease and de-configure the\n.Ar interface\nregardless of the\n.Fl p , Fl Fl persistent\noption.\nIf no\n.Ar interface\nis specified then this applies to all interfaces in Manager mode.\nIf no interfaces are left running,\n.Nm\nwill exit.\n.It Fl l , Fl Fl leasetime Ar seconds\nRequest a lease time of\n.Ar seconds .\n.Ar -1\nrepresents an infinite lease time.\nBy default\n.Nm\ndoes not request any lease time and leaves it in the hands of the\nDHCP server.\n.It Fl M , Fl Fl manager\nStart\n.Nm\nin Manager mode even if only one interface specified on the command line.\nSee the Multiple Interfaces section above.\n.It Fl m , Fl Fl metric Ar metric\nMetrics are used to prefer an interface over another one, lowest wins.\n.Nm\nwill supply a default metric of 1000 +\n.Xr if_nametoindex 3 .\nThis will be offset by 2000 for wireless interfaces, with additional offsets\nof 1000000 for IPv4LL and 2000000 for roaming interfaces.\n.It Fl n , Fl Fl rebind Op Ar interface\nNotifies\n.Nm\nto reload its configuration and rebind the specified\n.Ar interface .\nIf no\n.Ar interface\nis specified then this applies to all interfaces in Manager mode.\nIf\n.Nm\nis not running, then it starts up as normal.\n.It Fl N , Fl Fl renew Op Ar interface\nNotifies\n.Nm\nto renew existing addresses on the specified\n.Ar interface .\nIf no\n.Ar interface\nis specified then this applies to all interfaces in Manager mode.\nIf\n.Nm\nis not running, then it starts up as normal.\nUnlike the\n.Fl n , Fl Fl rebind\noption above, the configuration for\n.Nm\nis not reloaded.\n.It Fl o , Fl Fl option Ar option\nRequest the DHCP\n.Ar option\nvariable for use in\n.Pa @SCRIPT@ .\n.It Fl p , Fl Fl persistent\n.Nm\nde-configures the\n.Ar interface\nwhen it exits unless this option is enabled.\nSometimes, this isn't desirable if, for example, you have root mounted over\nNFS or SSH clients connect to this host and they need to be notified of\nthe host shutting down.\nYou can use this option to stop this from happening.\n.It Fl r , Fl Fl request Ar address\nRequest the\n.Ar address\nin the DHCP DISCOVER message.\nThere is no guarantee this is the address the DHCP server will actually give.\nIf no\n.Ar address\nis given then the first address currently assigned to the\n.Ar interface\nis used.\n.It Fl s , Fl Fl inform Ar address Ns Op Ar /cidr Ns Op Ar /broadcast_address\nBehaves like\n.Fl r , Fl Fl request\nas above, but sends a DHCP INFORM instead of DISCOVER/REQUEST.\nThis does not get a lease as such, just notifies the DHCP server of the\n.Ar address\nin use.\nYou should also include the optional\n.Ar cidr\nnetwork number in case the address is not already configured on the interface.\n.Nm\nremains running and pretends it has an infinite lease.\n.Nm\nwill not de-configure the interface when it exits.\nIf\n.Nm\nfails to contact a DHCP server then it returns a failure instead of falling\nback on IPv4LL.\n.It Fl Fl inform6\nPerforms a DHCPv6 Information Request.\nNo address is requested or specified, but all other DHCPv6 options are allowed.\nThis is normally performed automatically when the IPv6 Router Advertises\nthat the client should perform this operation.\nThis option is only needed when\n.Nm\nis not processing IPv6RA messages and the need for DHCPv6 Information Request\nexists.\n.It Fl S , Fl Fl static Ar value\nConfigures a static DHCP\n.Ar value .\nIf you set\n.Ic ip_address\nthen\n.Nm\nwill not attempt to obtain a lease and just use the value for the address with\nan infinite lease time.\n.Pp\nHere is an example which configures a static address, routes and DNS.\n.D1 dhcpcd -S ip_address=192.168.0.10/24 \\e\n.D1 -S routers=192.168.0.1 \\e\n.D1 -S domain_name_servers=192.168.0.1 \\e\n.D1 eth0\n.Pp\nYou cannot presently set static DHCPv6 values.\nUse the\n.Fl e , Fl Fl env\noption instead.\n.It Fl t , Fl Fl timeout Ar seconds\nTimeout after\n.Ar seconds ,\ninstead of the default 30.\nOn timeout,\n.Nm\nwill exit if the\n.Fl 1 ,\n.Fl Fl oneshot\noption has been given,\notherwise it will fork into the background and keep on\ntrying.\nA setting of 0\n.Ar seconds\ncauses\n.Nm\nto wait forever to get a lease.\n.It Fl u , Fl Fl userclass Ar class\nTags the DHCPv4 message with the userclass\n.Ar class .\nDHCP servers use this to give members of the class DHCP options other than the\ndefault, without having to know things like hardware address or hostname.\n.It Fl v , Fl Fl vendor Ar code , Ns Ar value\nAdd an encapsulated vendor option.\n.Ar code\nshould be between 1 and 254 inclusive.\nTo add a raw vendor string, omit\n.Ar code\nbut keep the comma.\nExamples.\n.Pp\nSet the vendor option 01 with an IP address.\n.D1 dhcpcd \\-v 01,192.168.0.2 eth0\nSet the vendor option 02 with a hex code.\n.D1 dhcpcd \\-v 02,01:02:03:04:05 eth0\nSet the vendor option 03 with an IP address as a string.\n.D1 dhcpcd \\-v 03,\\e\"192.168.0.2\\e\" eth0\nSet un-encapsulated vendor option to hello world.\n.D1 dhcpcd \\-v ,\"hello world\" eth0\n.It Fl Fl version\nDisplay both program version and copyright information.\n.Nm\nthen exits before doing any configuration.\n.It Fl w\nWait for an address to be assigned before forking to the background.\nDoes not take an argument, unlike the below option.\n.It Fl Fl waitip Ns = Ns Op 4 | 6\nWait for an address to be assigned before forking to the background.\n4 means wait for an IPv4 address to be assigned.\n6 means wait for an IPv6 address to be assigned.\nIf no argument is given,\n.Nm\nwill wait for any address protocol to be assigned.\nIt is possible to wait for more than one address protocol and\n.Nm\nwill only fork to the background when all waiting conditions are satisfied.\n.It Fl x , Fl Fl exit Op Ar interface\nThis will signal an existing\n.Nm\nprocess running on the\n.Ar interface\nto exit.\nIf no\n.Ar interface\nis specified, then the above is applied to all interfaces in Manager mode.\nSee the\n.Fl p , Fl Fl persistent\noption to control configuration persistence on exit,\nwhich is enabled by default in\n.Xr dhcpcd.conf 5 .\n.Nm\nthen waits until this process has exited.\n.It Fl y , Fl Fl reboot Ar seconds\nAllow\n.Ar reboot\nseconds before moving to the discover phase if we have an old lease to use.\nAllow\n.Ar reboot\nseconds before starting fallback states from the discover phase.\nIPv4LL is started when the first\n.Ar reboot\ntimeout is reached.\nThe default is 5 seconds.\nA setting of 0 seconds causes\n.Nm\nto skip the reboot phase and go straight into discover.\nThis has no effect on DHCPv6 other than skipping the reboot phase.\n.El\n.Ss Restricting behaviour\n.Nm\nwill try to do as much as it can by default.\nHowever, there are sometimes situations where you don't want the things to be\nconfigured exactly how the DHCP server wants.\nHere are some options that deal with turning these bits off.\n.Pp\nNote that when\n.Nm\nis restricted to a single interface then the interface also needs to be\nspecified when asking\n.Nm\nto exit using the commandline.\nIf the protocol is restricted as well then the protocol needs to be included\nwith the exit instruction.\n.Bl -tag -width indent\n.It Fl 1 , Fl Fl oneshot\nExit after configuring an interface.\nUse the\n.Fl w , Fl Fl waitip\noption to specify which protocol(s) to configure before exiting.\n.It Fl 4 , Fl Fl ipv4only\nConfigure IPv4 only.\n.It Fl 6 , Fl Fl ipv6only\nConfigure IPv6 only.\n.It Fl A , Fl Fl noarp\nDon't request or claim the address by ARP.\nThis also disables IPv4LL.\n.It Fl B , Fl Fl nobackground\nDon't run in the background when we acquire a lease.\nThis is mainly useful for running under the control of another process, such\nas a debugger or a network manager.\n.It Fl C , Fl Fl nohook Ar script\nDon't run this hook script.\nMatches full name, or prefixed with 2 numbers optionally ending with\n.Pa .sh .\n.Pp\nSo to stop\n.Nm\nfrom touching your DNS settings you would do:-\n.D1 dhcpcd -C resolv.conf eth0\n.It Fl G , Fl Fl nogateway\nDon't set any default routes.\n.It Fl H , Fl Fl xidhwaddr\nUse the last four bytes of the hardware address as the DHCP xid instead\nof a randomly generated number.\n.It Fl J , Fl Fl broadcast\nInstructs the DHCP server to broadcast replies back to the client.\nNormally this is only set for non-Ethernet interfaces,\nsuch as FireWire and InfiniBand.\nIn most instances,\n.Nm\nwill set this automatically.\n.It Fl K , Fl Fl nolink\nDon't receive link messages for carrier status.\nYou should only have to use this with buggy device drivers or running\n.Nm\nthrough a network manager.\n.It Fl L , Fl Fl noipv4ll\nDon't use IPv4LL (aka APIPA, aka Bonjour, aka ZeroConf).\n.It Fl O , Fl Fl nooption Ar option\nRemoves the\n.Ar option\nfrom the DHCP message before processing.\n.It Fl P , Fl Fl printpidfile\nPrint the\n.Pa pidfile\n.Nm\nwill use based on command-line arguments to stdout.\n.It Fl Q , Fl Fl require Ar option\nRequires the\n.Ar option\nto be present in all DHCP messages, otherwise the message is ignored.\nTo enforce that\n.Nm\nonly responds to DHCP servers and not BOOTP servers, you can\n.Fl Q\n.Ar dhcp_message_type .\n.It Fl q , Fl Fl quiet\nQuiet\n.Nm\non the command line, only warnings and errors will be displayed.\nIf this option is used another time then all console output is disabled.\nThese messages are still logged via\n.Xr syslog 3 .\n.It Fl T , Fl Fl test\nOn receipt of DHCP messages just call\n.Pa @SCRIPT@\nwith the reason of TEST which echos the DHCP variables found in the message\nto the console.\nThe interface configuration isn't touched and neither are any configuration\nfiles.\nThe\n.Ar rapid_commit\noption is not sent in TEST mode so that the server does not lease an address.\nTo test INFORM the interface needs to be configured with the desired address\nbefore starting\n.Nm .\n.It Fl U , Fl Fl dumplease Op Ar interface\nDumps the current lease for the\n.Ar interface\nto stdout.\nIf no\n.Ar interface\nis given then all interfaces are dumped.\nUse the\n.Fl 4\nor\n.Fl 6\nflags to specify an address family.\nIf a lease is piped in via standard input then use the special interface named\n.Ar -\nto dump it.\nIn this case, specifying an address family is mandatory.\n.It Fl V , Fl Fl variables\nDisplay a list of option codes, the associated variable and encoding for use in\n.Xr dhcpcd-run-hooks 8 .\nVariables are prefixed with new_ and old_ unless the option number is -.\nVariables without an option are part of the DHCP message and cannot be\ndirectly requested.\n.It Fl W , Fl Fl whitelist Ar address Ns Op /cidr\nOnly accept packets from\n.Ar address Ns Op /cidr .\n.Fl X , Fl Fl blacklist\nis ignored if\n.Fl W , Fl Fl whitelist\nis set.\n.It Fl X , Fl Fl blacklist Ar address Ns Op Ar /cidr\nIgnore all packets from\n.Ar address Ns Op Ar /cidr .\n.It Fl Z , Fl Fl denyinterfaces Ar pattern\nWhen discovering interfaces, the interface name must not match\n.Ar pattern\nwhich is a space or comma separated list of patterns passed to\n.Xr fnmatch 3 .\n.It Fl z , Fl Fl allowinterfaces Ar pattern\nWhen discovering interfaces, the interface name must match\n.Ar pattern\nwhich is a space or comma separated list of patterns passed to\n.Xr fnmatch 3 .\nIf the same interface is matched in\n.Fl Z , Fl Fl denyinterfaces\nthen it is still denied.\n.It Fl Fl inactive\nDon't start any interfaces other than those specified on the command line.\nThis allows\n.Nm\nto be started in Manager mode and then wait for subsequent\n.Nm\ncommands to start each interface as required.\n.It Fl Fl configure\nAllows\n.Nm\nto configure the system.\nThis is the default behaviour and sets\n.Ev if_configured=true .\n.It Fl Fl noconfigure\n.Nm\nwill not configure the system at all.\nThis is only of use if the\n.Fl Fl script\nthat\n.Nm\ncalls at each network event configures the system instead.\nThis is different from\n.Fl T , Fl Fl test\nmode in that it's not one shot and the only change to the environment is the\naddition of\n.Ev if_configured=false .\n.It Fl Fl nodev\nDon't load any\n.Pa /dev\nmanagement modules.\n.El\n.Sh 3RDPARTY LINK MANAGEMENT\nSome interfaces require configuration by 3rd parties, such as PPP or VPN.\nWhen an interface configuration in\n.Nm\nis marked as STATIC or INFORM without an address then\n.Nm\nwill monitor the interface until an address is added or removed from it and\nact accordingly.\nFor point to point interfaces (like PPP), a default route to its\ndestination is automatically added to the configuration.\nIf the point to point interface is configured for INFORM, then\n.Nm\nunicasts INFORM to the destination, otherwise it defaults to STATIC.\n.Sh NOTES\n.Nm\nrequires a Berkeley Packet Filter, or BPF device on BSD based systems and a\nLinux Socket Filter, or LPF device on Linux based systems for all IPv4\nconfiguration.\n.Pp\nIf restricting\n.Nm\nto a single interface and optionally address family via the command-line\nthen all further calls to\n.Nm\nto rebind, reconfigure or exit need to include the same restrictive flags\nso that\n.Nm\nknows which process to signal.\n.Pp\nSome DHCP servers implement ClientID filtering.\nIf\n.Nm\nis replacing an in-use DHCP client then you might need to adjust the clientid\noption\n.Nm\nsends to match.\nIf using a DUID in place of the ClientID, edit\n.Pa @DBDIR@/duid\naccordingly.\n.Sh FILES\n.Bl -ohang\n.It Pa @SYSCONFDIR@/dhcpcd.conf\nConfiguration file for dhcpcd.\nIf you always use the same options, put them here.\n.It Pa @SCRIPT@\nBourne shell script that is run to configure or de-configure an interface.\n.It Pa @LIBDIR@/dhcpcd/dev\nLinux\n.Pa /dev\nmanagement modules.\n.It Pa @HOOKDIR@\nA directory containing Bourne shell scripts that are run by the above script.\nEach script can be disabled by using the\n.Fl C , Fl Fl nohook\noption described above.\n.It Pa @DBDIR@/duid\nText file that holds the DUID used to identify the host.\n.It Pa @DBDIR@/secret\nText file that holds a secret key known only to the host.\n.It Pa @DBDIR@/ Ns Ar interface Ns Ar -ssid Ns .lease\nThe actual DHCP message sent by the server.\nWe use this when reading the last\nlease and use the file's mtime as when it was issued.\n.It Pa @DBDIR@/ Ns Ar interface Ns Ar -ssid Ns .lease6\nThe actual DHCPv6 message sent by the server.\nWe use this when reading the last\nlease and use the file's mtime as when it was issued.\n.It Pa @DBDIR@/rdm_monotonic\nStores the monotonic counter used in the\n.Ar replay\nfield in Authentication Options.\n.It Pa @RUNDIR@/pid\nStores the PID of\n.Nm\nrunning on all interfaces.\n.It Pa @RUNDIR@/ Ns Ar interface Ns .pid\nStores the PID of\n.Nm\nrunning on the\n.Ar interface .\n.It Pa @RUNDIR@/sock\nControl socket to the manager daemon.\n.It Pa @RUNDIR@/unpriv.sock\nUnprivileged socket to the manager daemon, only allows state retrieval.\n.It Pa @RUNDIR@/ Ns Ar interface Ns .sock\nControl socket to per interface daemon.\n.It Pa @RUNDIR@/ Ns Ar interface Ns .unpriv.sock\nUnprivileged socket to per interface daemon, only allows state retrieval.\n.El\n.Sh SEE ALSO\n.Xr fnmatch 3 ,\n.Xr if_nametoindex 3 ,\n.Xr dhcpcd.conf 5 ,\n.Xr resolv.conf 5 ,\n.Xr dhcpcd-run-hooks 8 ,\n.Xr resolvconf 8\n.Sh STANDARDS\nRFC\\ 951, RFC\\ 1534, RFC\\ 2104, RFC\\ 2131, RFC\\ 2132, RFC\\ 2563, RFC\\ 2855,\nRFC\\ 3004, RFC\\ 3118, RFC\\ 3203, RFC\\ 3315, RFC\\ 3361, RFC\\ 3633, RFC\\ 3396,\nRFC\\ 3397, RFC\\ 3442, RFC\\ 3495, RFC\\ 3925, RFC\\ 3927, RFC\\ 4039, RFC\\ 4075,\nRFC\\ 4242, RFC\\ 4361, RFC\\ 4390, RFC\\ 4702, RFC\\ 4074, RFC\\ 4861, RFC\\ 4833,\nRFC\\ 4941, RFC\\ 5227, RFC\\ 5942, RFC\\ 5969, RFC\\ 6106, RFC\\ 6334, RFC\\ 6355,\nRFC\\ 6603, RFC\\ 6704, RFC\\ 7217, RFC\\ 7550, RFC\\ 7844.\n.Sh AUTHORS\n.An Roy Marples Aq Mt roy@marples.name\n.Sh BUGS\nPlease report them to\n.Lk https://roy.marples.name/projects/dhcpcd\n"
  },
  {
    "path": "src/dhcpcd.c",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\nstatic const char dhcpcd_copyright[] = \"Copyright (c) 2006-2025 Roy Marples\";\n\n#include <sys/types.h>\n#include <sys/file.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <sys/time.h>\n#include <sys/uio.h>\n#include <sys/wait.h>\n\n#include <ctype.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <getopt.h>\n#include <limits.h>\n#include <paths.h>\n#include <signal.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <syslog.h>\n#include <time.h>\n#include <unistd.h>\n\n#include \"arp.h\"\n#include \"common.h\"\n#include \"config.h\"\n#include \"control.h\"\n#include \"dev.h\"\n#include \"dhcp-common.h\"\n#include \"dhcp.h\"\n#include \"dhcp6.h\"\n#include \"dhcpcd.h\"\n#include \"duid.h\"\n#include \"eloop.h\"\n#include \"if-options.h\"\n#include \"if.h\"\n#include \"ipv4.h\"\n#include \"ipv4ll.h\"\n#include \"ipv6.h\"\n#include \"ipv6nd.h\"\n#include \"logerr.h\"\n#include \"privsep.h\"\n#include \"script.h\"\n\n#ifdef HAVE_CAPSICUM\n#include <sys/capsicum.h>\n#endif\n#ifdef HAVE_OPENSSL\n#include <openssl/crypto.h>\n#endif\n#ifdef HAVE_UTIL_H\n#include <util.h>\n#endif\n\n#ifdef USE_SIGNALS\nconst int dhcpcd_signals[] = {\n\tSIGTERM,\n\tSIGINT,\n\tSIGALRM,\n\tSIGHUP,\n\tSIGUSR1,\n\tSIGUSR2,\n\tSIGCHLD,\n};\nconst size_t dhcpcd_signals_len = __arraycount(dhcpcd_signals);\n\nconst int dhcpcd_signals_ignore[] = {\n\tSIGPIPE,\n};\nconst size_t dhcpcd_signals_ignore_len = __arraycount(dhcpcd_signals_ignore);\n#endif\n\nconst char *dhcpcd_default_script = SCRIPT;\n\nstatic void\nusage(void)\n{\n\tprintf(\n\t    \"usage: \" PACKAGE \"\\t[-146ABbDdEGgHJKLMNPpqTV]\\n\"\n\t    \"\\t\\t[-C, --nohook hook] [-c, --script script]\\n\"\n\t    \"\\t\\t[-e, --env value] [-F, --fqdn FQDN] [-f, --config file]\\n\"\n\t    \"\\t\\t[-h, --hostname hostname] [-I, --clientid clientid]\\n\"\n\t    \"\\t\\t[-i, --vendorclassid vendorclassid] [-j, --logfile logfile]\\n\"\n\t    \"\\t\\t[-l, --leasetime seconds] [-m, --metric metric]\\n\"\n\t    \"\\t\\t[-O, --nooption option] [-o, --option option]\\n\"\n\t    \"\\t\\t[-Q, --require option] [-r, --request address]\\n\"\n\t    \"\\t\\t[-S, --static value]\\n\"\n\t    \"\\t\\t[-s, --inform address[/cidr[/broadcast_address]]] [--inform6]\\n\"\n\t    \"\\t\\t[-t, --timeout seconds] [-u, --userclass class]\\n\"\n\t    \"\\t\\t[-v, --vendor code, value] [-W, --whitelist address[/cidr]] [-w]\\n\"\n\t    \"\\t\\t[--waitip [4 | 6]] [-y, --reboot seconds]\\n\"\n\t    \"\\t\\t[-X, --blacklist address[/cidr]] [-Z, --denyinterfaces pattern]\\n\"\n\t    \"\\t\\t[-z, --allowinterfaces pattern] [--inactive] [interface] [...]\\n\"\n\t    \"       \" PACKAGE \"\\t-n, --rebind [interface]\\n\"\n\t    \"       \" PACKAGE \"\\t-k, --release [interface]\\n\"\n\t    \"       \" PACKAGE \"\\t-U, --dumplease interface\\n\"\n\t    \"       \" PACKAGE \"\\t--version\\n\"\n\t    \"       \" PACKAGE \"\\t-x, --exit [interface]\\n\");\n}\n\nstatic void\nfree_globals(struct dhcpcd_ctx *ctx)\n{\n\tstruct dhcp_opt *opt;\n\n\tif (ctx->ifac) {\n\t\tfor (; ctx->ifac > 0; ctx->ifac--)\n\t\t\tfree(ctx->ifav[ctx->ifac - 1]);\n\t\tfree(ctx->ifav);\n\t\tctx->ifav = NULL;\n\t}\n\tif (ctx->ifdc) {\n\t\tfor (; ctx->ifdc > 0; ctx->ifdc--)\n\t\t\tfree(ctx->ifdv[ctx->ifdc - 1]);\n\t\tfree(ctx->ifdv);\n\t\tctx->ifdv = NULL;\n\t}\n\tif (ctx->ifcc) {\n\t\tfor (; ctx->ifcc > 0; ctx->ifcc--)\n\t\t\tfree(ctx->ifcv[ctx->ifcc - 1]);\n\t\tfree(ctx->ifcv);\n\t\tctx->ifcv = NULL;\n\t}\n\n#ifdef INET\n\tif (ctx->dhcp_opts) {\n\t\tfor (opt = ctx->dhcp_opts; ctx->dhcp_opts_len > 0;\n\t\t    opt++, ctx->dhcp_opts_len--)\n\t\t\tfree_dhcp_opt_embenc(opt);\n\t\tfree(ctx->dhcp_opts);\n\t\tctx->dhcp_opts = NULL;\n\t}\n#endif\n#ifdef INET6\n\tif (ctx->nd_opts) {\n\t\tfor (opt = ctx->nd_opts; ctx->nd_opts_len > 0;\n\t\t    opt++, ctx->nd_opts_len--)\n\t\t\tfree_dhcp_opt_embenc(opt);\n\t\tfree(ctx->nd_opts);\n\t\tctx->nd_opts = NULL;\n\t}\n#ifdef DHCP6\n\tif (ctx->dhcp6_opts) {\n\t\tfor (opt = ctx->dhcp6_opts; ctx->dhcp6_opts_len > 0;\n\t\t    opt++, ctx->dhcp6_opts_len--)\n\t\t\tfree_dhcp_opt_embenc(opt);\n\t\tfree(ctx->dhcp6_opts);\n\t\tctx->dhcp6_opts = NULL;\n\t}\n#endif\n#endif\n\tif (ctx->vivso) {\n\t\tfor (opt = ctx->vivso; ctx->vivso_len > 0;\n\t\t    opt++, ctx->vivso_len--)\n\t\t\tfree_dhcp_opt_embenc(opt);\n\t\tfree(ctx->vivso);\n\t\tctx->vivso = NULL;\n\t}\n}\n\nstatic void\nhandle_exit_timeout(void *arg)\n{\n\tstruct dhcpcd_ctx *ctx;\n\n\tctx = arg;\n\tlogerrx(\"timed out\");\n\tif (ctx->options & DHCPCD_ONESHOT) {\n\t\tstruct interface *ifp;\n\n\t\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\t\tif (ifp->active == IF_ACTIVE_USER)\n\t\t\t\tscript_runreason(ifp, \"STOPPED\");\n\t\t}\n\t\teloop_exit(ctx->eloop, EXIT_FAILURE);\n\t\treturn;\n\t}\n\n\tctx->options |= DHCPCD_BACKGROUND;\n\tdhcpcd_daemonise(ctx);\n}\n\nstatic const char *\ndhcpcd_af(int af)\n{\n\tswitch (af) {\n\tcase AF_UNSPEC:\n\t\treturn \"IP\";\n\tcase AF_INET:\n\t\treturn \"IPv4\";\n\tcase AF_INET6:\n\t\treturn \"IPv6\";\n\tdefault:\n\t\treturn NULL;\n\t}\n}\n\nint\ndhcpcd_ifafwaiting(const struct interface *ifp)\n{\n\tunsigned long long opts;\n\tbool foundany = false;\n\n\tif (ifp->active != IF_ACTIVE_USER)\n\t\treturn AF_MAX;\n\n#define DHCPCD_WAITALL (DHCPCD_WAITIP4 | DHCPCD_WAITIP6)\n\topts = ifp->options->options;\n#ifdef INET\n\tif (opts & DHCPCD_WAITIP4 ||\n\t    (opts & DHCPCD_WAITIP && !(opts & DHCPCD_WAITALL))) {\n\t\tbool foundaddr = ipv4_hasaddr(ifp);\n\n\t\tif (opts & DHCPCD_WAITIP4 && !foundaddr)\n\t\t\treturn AF_INET;\n\t\tif (foundaddr)\n\t\t\tfoundany = true;\n\t}\n#endif\n#ifdef INET6\n\tif (opts & DHCPCD_WAITIP6 ||\n\t    (opts & DHCPCD_WAITIP && !(opts & DHCPCD_WAITALL))) {\n\t\tbool foundaddr = ipv6_hasaddr(ifp);\n\n\t\tif (opts & DHCPCD_WAITIP6 && !foundaddr)\n\t\t\treturn AF_INET6;\n\t\tif (foundaddr)\n\t\t\tfoundany = true;\n\t}\n#endif\n\n\tif (opts & DHCPCD_WAITIP && !(opts & DHCPCD_WAITALL) && !foundany)\n\t\treturn AF_UNSPEC;\n\treturn AF_MAX;\n}\n\nint\ndhcpcd_afwaiting(const struct dhcpcd_ctx *ctx)\n{\n\tunsigned long long opts;\n\tconst struct interface *ifp;\n\tint af;\n\n\tif (!(ctx->options & DHCPCD_WAITOPTS))\n\t\treturn AF_MAX;\n\n\topts = ctx->options;\n\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n#ifdef INET\n\t\tif (opts & (DHCPCD_WAITIP | DHCPCD_WAITIP4) &&\n\t\t    ipv4_hasaddr(ifp))\n\t\t\topts &= ~(DHCPCD_WAITIP | DHCPCD_WAITIP4);\n#endif\n#ifdef INET6\n\t\tif (opts & (DHCPCD_WAITIP | DHCPCD_WAITIP6) &&\n\t\t    ipv6_hasaddr(ifp))\n\t\t\topts &= ~(DHCPCD_WAITIP | DHCPCD_WAITIP6);\n#endif\n\t\tif (!(opts & DHCPCD_WAITOPTS))\n\t\t\tbreak;\n\t}\n\tif (opts & DHCPCD_WAITIP)\n\t\taf = AF_UNSPEC;\n\telse if (opts & DHCPCD_WAITIP4)\n\t\taf = AF_INET;\n\telse if (opts & DHCPCD_WAITIP6)\n\t\taf = AF_INET6;\n\telse\n\t\treturn AF_MAX;\n\treturn af;\n}\n\nstatic int\ndhcpcd_ipwaited(struct dhcpcd_ctx *ctx)\n{\n\tstruct interface *ifp;\n\tint af;\n\n\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\tif ((af = dhcpcd_ifafwaiting(ifp)) != AF_MAX) {\n\t\t\tlogdebugx(\"%s: waiting for an %s address\", ifp->name,\n\t\t\t    dhcpcd_af(af));\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tif ((af = dhcpcd_afwaiting(ctx)) != AF_MAX) {\n\t\tlogdebugx(\"waiting for an %s address\", dhcpcd_af(af));\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n\n#ifndef THERE_IS_NO_FORK\nvoid\ndhcpcd_daemonised(struct dhcpcd_ctx *ctx)\n{\n\tunsigned int logopts = loggetopts();\n\n\t/*\n\t * Stop writing to stderr.\n\t * On the happy path, only the manager process writes to stderr,\n\t * so this just stops wasting fprintf calls to nowhere.\n\t */\n\tlogopts &= ~LOGERR_ERR;\n\tlogsetopts(logopts);\n\n\t/*\n\t * We need to do something with stdout/stderr to avoid SIGPIPE.\n\t * We know that stdin is already mapped to /dev/null.\n\t * TODO: Capture script output and log it to the logfile and/or syslog.\n\t */\n\tdup2(STDIN_FILENO, STDOUT_FILENO);\n\tdup2(STDIN_FILENO, STDERR_FILENO);\n\n\tctx->options |= DHCPCD_DAEMONISED;\n}\n#endif\n\n/* Returns the pid of the child, otherwise 0. */\nvoid\ndhcpcd_daemonise(struct dhcpcd_ctx *ctx)\n{\n#ifdef THERE_IS_NO_FORK\n\teloop_timeout_delete(ctx->eloop, handle_exit_timeout, ctx);\n\terrno = ENOSYS;\n\treturn;\n#else\n\tint exit_code;\n\n\tif (ctx->options & DHCPCD_DAEMONISE &&\n\t    !(ctx->options & (DHCPCD_DAEMONISED | DHCPCD_BACKGROUND))) {\n\t\tif (!dhcpcd_ipwaited(ctx))\n\t\t\treturn;\n\t}\n\n\tif (ctx->options & DHCPCD_ONESHOT) {\n\t\tloginfox(\"exiting due to oneshot\");\n\t\teloop_exit(ctx->eloop, EXIT_SUCCESS);\n\t\treturn;\n\t}\n\n\teloop_timeout_delete(ctx->eloop, handle_exit_timeout, ctx);\n\tif (ctx->options & DHCPCD_DAEMONISED ||\n\t    !(ctx->options & DHCPCD_DAEMONISE))\n\t\treturn;\n\n#ifdef PRIVSEP\n\tif (IN_PRIVSEP(ctx))\n\t\tps_daemonised(ctx);\n\telse\n#endif\n\t\tdhcpcd_daemonised(ctx);\n\n\teloop_event_delete(ctx->eloop, ctx->fork_fd);\n\texit_code = EXIT_SUCCESS;\n\tif (send(ctx->fork_fd, &exit_code, sizeof(exit_code), 0) == -1)\n\t\tlogerr(__func__);\n\tclose(ctx->fork_fd);\n\tctx->fork_fd = -1;\n#endif\n}\n\nstatic void\ndhcpcd_drop_af(struct interface *ifp, int stop, int af)\n{\n\tif (af == AF_UNSPEC || af == AF_INET6) {\n#ifdef DHCP6\n\t\tdhcp6_drop(ifp, stop ? NULL : \"EXPIRE6\");\n#endif\n#ifdef INET6\n\t\tipv6nd_drop(ifp);\n\t\tipv6_drop(ifp);\n#endif\n\t}\n\n\tif (af == AF_UNSPEC || af == AF_INET) {\n#ifdef IPV4LL\n\t\tipv4ll_drop(ifp);\n#endif\n#ifdef INET\n\t\tdhcp_drop(ifp, stop ? \"STOP\" : \"EXPIRE\");\n#endif\n#ifdef ARP\n\t\tarp_drop(ifp);\n#endif\n\t}\n\n#if !defined(DHCP6) && !defined(DHCP)\n\tUNUSED(stop);\n#endif\n}\n\nstatic void\ndhcpcd_drop(struct interface *ifp, int stop)\n{\n\tdhcpcd_drop_af(ifp, stop, AF_UNSPEC);\n}\n\nstatic bool\ndhcpcd_ifrunning(struct interface *ifp)\n{\n#ifdef INET\n\tif (D_CSTATE(ifp) != NULL)\n\t\treturn true;\n#ifdef IPV4LL\n\tif (IPV4LL_CSTATE(ifp) != NULL)\n\t\treturn true;\n#endif\n#endif\n#ifdef DHCP6\n\tif (D6_CSTATE(ifp) != NULL)\n\t\treturn true;\n#endif\n\treturn false;\n}\n\nvoid\ndhcpcd_dropped(struct interface *ifp)\n{\n\tstruct dhcpcd_ctx *ctx = ifp->ctx;\n\n\tif (ifp->options == NULL ||\n\t    !(ifp->options->options & DHCPCD_STOPPING) || dhcpcd_ifrunning(ifp))\n\t\treturn;\n\n\t/* De-activate the interface */\n\tif (ifp->active) {\n\t\tifp->active = IF_INACTIVE;\n\t\tifp->options->options &= ~DHCPCD_STOPPING;\n\t\tscript_runreason(ifp, \"STOPPED\");\n\t}\n\n\tif (!(ctx->options & DHCPCD_EXITING))\n\t\treturn;\n\n\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\tif (dhcpcd_ifrunning(ifp))\n\t\t\tbreak;\n\t}\n\n\t/* All interfaces have stopped, we can exit */\n\tif (ifp == NULL)\n\t\teloop_exit(ctx->eloop, EXIT_SUCCESS);\n}\n\nstatic void\nstop_interface(struct interface *ifp)\n{\n\tloginfox(\"%s: removing interface\", ifp->name);\n\tifp->options->options |= DHCPCD_STOPPING;\n\tdhcpcd_drop(ifp, 1);\n}\n\nstatic void\nconfigure_interface1(struct interface *ifp)\n{\n\tstruct if_options *ifo = ifp->options;\n\n\t/* Do any platform specific configuration */\n\tif_conf(ifp);\n\n\t/* If we want to release a lease, we can't really persist the\n\t * address either. */\n\tif (ifo->options & DHCPCD_RELEASE)\n\t\tifo->options &= ~DHCPCD_PERSISTENT;\n\n\tif (ifp->flags & (IFF_POINTOPOINT | IFF_LOOPBACK)) {\n\t\tifo->options &= ~DHCPCD_ARP;\n\t\tif (!(ifp->flags & IFF_MULTICAST))\n\t\t\tifo->options &= ~DHCPCD_IPV6RS;\n\t\tif (!(ifo->options & (DHCPCD_INFORM | DHCPCD_WANTDHCP)))\n\t\t\tifo->options |= DHCPCD_STATIC;\n\t}\n\n\tif (ifo->metric != -1)\n\t\tifp->metric = (unsigned int)ifo->metric;\n\n#ifdef INET6\n\t/* We want to setup INET6 on the interface as soon as possible. */\n\tif (ifp->active == IF_ACTIVE_USER && ifo->options & DHCPCD_IPV6 &&\n\t    !(ifp->ctx->options & (DHCPCD_DUMPLEASE | DHCPCD_TEST))) {\n\t\t/* If not doing any DHCP, disable the RDNSS requirement. */\n\t\tif (!(ifo->options & (DHCPCD_DHCP | DHCPCD_DHCP6)))\n\t\t\tifo->options &= ~DHCPCD_IPV6RA_REQRDNSS;\n\t\tif_setup_inet6(ifp);\n\t}\n#endif\n\n\tif (!(ifo->options & DHCPCD_IAID)) {\n\t\t/*\n\t\t * An IAID is for identifying a unqiue interface within\n\t\t * the client. It is 4 bytes long. Working out a default\n\t\t * value is problematic.\n\t\t *\n\t\t * Interface name and number are not stable\n\t\t * between different OS's. Some OS's also cannot make\n\t\t * up their mind what the interface should be called\n\t\t * (yes, udev, I'm looking at you).\n\t\t * Also, the name could be longer than 4 bytes.\n\t\t * Also, with pluggable interfaces the name and index\n\t\t * could easily get swapped per actual interface.\n\t\t *\n\t\t * The MAC address is 6 bytes long, the final 3\n\t\t * being unique to the manufacturer and the initial 3\n\t\t * being unique to the organisation which makes it.\n\t\t * We could use the last 4 bytes of the MAC address\n\t\t * as the IAID as it's the most stable part given the\n\t\t * above, but equally it's not guaranteed to be\n\t\t * unique.\n\t\t *\n\t\t * Given the above, and our need to reliably work\n\t\t * between reboots without persitent storage,\n\t\t * generating the IAID from the MAC address is the only\n\t\t * logical default.\n\t\t * Saying that, if a VLANID has been specified then we\n\t\t * can use that. It's possible that different interfaces\n\t\t * can have the same VLANID, but this is no worse than\n\t\t * generating the IAID from the duplicate MAC address.\n\t\t *\n\t\t * dhclient uses the last 4 bytes of the MAC address.\n\t\t * dibbler uses an increamenting counter.\n\t\t * wide-dhcpv6 uses 0 or a configured value.\n\t\t * odhcp6c uses 1.\n\t\t * Windows 7 uses the first 3 bytes of the MAC address\n\t\t * and an unknown byte.\n\t\t * dhcpcd-6.1.0 and earlier used the interface name,\n\t\t * falling back to interface index if name > 4.\n\t\t */\n\t\tif (ifp->vlanid != 0) {\n\t\t\tuint32_t vlanid;\n\n\t\t\t/* Maximal VLANID is 4095, so prefix with 0xff\n\t\t\t * so we don't conflict with an interface index. */\n\t\t\tvlanid = htonl(ifp->vlanid | 0xff000000);\n\t\t\tmemcpy(ifo->iaid, &vlanid, sizeof(vlanid));\n\t\t} else if (ifo->options & DHCPCD_ANONYMOUS)\n\t\t\tmemset(ifo->iaid, 0, sizeof(ifo->iaid));\n\t\telse if (ifp->hwlen >= sizeof(ifo->iaid)) {\n\t\t\tmemcpy(ifo->iaid,\n\t\t\t    ifp->hwaddr + ifp->hwlen - sizeof(ifo->iaid),\n\t\t\t    sizeof(ifo->iaid));\n\t\t} else {\n\t\t\tuint32_t len;\n\n\t\t\tlen = (uint32_t)strlen(ifp->name);\n\t\t\tif (len <= sizeof(ifo->iaid)) {\n\t\t\t\tmemcpy(ifo->iaid, ifp->name, len);\n\t\t\t\tif (len < sizeof(ifo->iaid))\n\t\t\t\t\tmemset(ifo->iaid + len, 0,\n\t\t\t\t\t    sizeof(ifo->iaid) - len);\n\t\t\t} else {\n\t\t\t\t/* IAID is the same size as a uint32_t */\n\t\t\t\tlen = htonl(ifp->index);\n\t\t\t\tmemcpy(ifo->iaid, &len, sizeof(ifo->iaid));\n\t\t\t}\n\t\t}\n\t\tifo->options |= DHCPCD_IAID;\n\t}\n\n#ifdef DHCP6\n\tif (ifo->ia_len == 0 && ifo->options & DHCPCD_IPV6 &&\n\t    ifp->name[0] != '\\0') {\n\t\tifo->ia = malloc(sizeof(*ifo->ia));\n\t\tif (ifo->ia == NULL)\n\t\t\tlogerr(__func__);\n\t\telse {\n\t\t\tifo->ia_len = 1;\n\t\t\tifo->ia->ia_type = D6_OPTION_IA_NA;\n\t\t\tmemcpy(ifo->ia->iaid, ifo->iaid, sizeof(ifo->iaid));\n\t\t\tmemset(&ifo->ia->addr, 0, sizeof(ifo->ia->addr));\n#ifndef SMALL\n\t\t\tifo->ia->sla = NULL;\n\t\t\tifo->ia->sla_len = 0;\n#endif\n\t\t}\n\t} else {\n\t\tsize_t i;\n\n\t\tfor (i = 0; i < ifo->ia_len; i++) {\n\t\t\tif (!ifo->ia[i].iaid_set) {\n\t\t\t\tmemcpy(&ifo->ia[i].iaid, ifo->iaid,\n\t\t\t\t    sizeof(ifo->ia[i].iaid));\n\t\t\t\tifo->ia[i].iaid_set = 1;\n\t\t\t}\n\t\t}\n\t}\n#endif\n\n\t/* If root is network mounted, we don't want to kill the connection\n\t * if the DHCP server goes the way of the dodo OR dhcpcd is rebooting\n\t * and the lease file has expired. */\n\tif (is_root_local() == 0)\n\t\tifo->options |= DHCPCD_LASTLEASE_EXTEND;\n}\n\nint\ndhcpcd_selectprofile(struct interface *ifp, const char *profile)\n{\n\tstruct if_options *ifo;\n\tchar pssid[PROFILE_LEN];\n\n\tif (ifp->ssid_len) {\n\t\tssize_t r;\n\n\t\tr = print_string(pssid, sizeof(pssid), OT_ESCSTRING, ifp->ssid,\n\t\t    ifp->ssid_len);\n\t\tif (r == -1) {\n\t\t\tlogerr(__func__);\n\t\t\tpssid[0] = '\\0';\n\t\t}\n\t} else\n\t\tpssid[0] = '\\0';\n\tifo = read_config(ifp->ctx, ifp->name, pssid, profile);\n\tif (ifo == NULL) {\n\t\tlogdebugx(\"%s: no profile %s\", ifp->name, profile);\n\t\treturn -1;\n\t}\n\tif (profile != NULL) {\n\t\tstrlcpy(ifp->profile, profile, sizeof(ifp->profile));\n\t\tloginfox(\"%s: selected profile %s\", ifp->name, profile);\n\t} else\n\t\t*ifp->profile = '\\0';\n\n\tfree_options(ifp->ctx, ifp->options);\n\tifp->options = ifo;\n\tif (profile) {\n\t\tadd_options(ifp->ctx, ifp->name, ifp->options, ifp->ctx->argc,\n\t\t    ifp->ctx->argv);\n\t\tconfigure_interface1(ifp);\n\t}\n\treturn 1;\n}\n\nstatic void\nconfigure_interface(struct interface *ifp, int argc, char **argv,\n    unsigned long long options)\n{\n\ttime_t old;\n\n\told = ifp->options ? ifp->options->mtime : 0;\n\tdhcpcd_selectprofile(ifp, NULL);\n\tif (ifp->options == NULL) {\n\t\t/* dhcpcd cannot continue with this interface. */\n\t\tifp->active = IF_INACTIVE;\n\t\treturn;\n\t}\n\tadd_options(ifp->ctx, ifp->name, ifp->options, argc, argv);\n\tifp->options->options |= options;\n\tconfigure_interface1(ifp);\n\n\t/* If the mtime has changed drop any old lease */\n\tif (old != 0 && ifp->options->mtime != old) {\n\t\tlogwarnx(\"%s: config file changed, expiring leases\", ifp->name);\n\t\tdhcpcd_drop(ifp, 0);\n\t}\n}\n\nstatic void\ndhcpcd_initstate1(struct interface *ifp, int argc, char **argv,\n    unsigned long long options)\n{\n\tstruct if_options *ifo;\n\n\tconfigure_interface(ifp, argc, argv, options);\n\tif (!ifp->active)\n\t\treturn;\n\n\tifo = ifp->options;\n\tifo->options |= options;\n\n#ifdef INET6\n\tif (ifo->options & DHCPCD_IPV6 && ipv6_init(ifp->ctx) == -1) {\n\t\tlogerr(__func__);\n\t\tifo->options &= ~DHCPCD_IPV6;\n\t}\n#endif\n}\n\nstatic void\ndhcpcd_initstate(struct interface *ifp, unsigned long long options)\n{\n\tdhcpcd_initstate1(ifp, ifp->argv ? ifp->argc : ifp->ctx->argc,\n\t    ifp->argv ? ifp->argv : ifp->ctx->argv, options);\n}\n\nstatic void\ndhcpcd_reportssid(struct interface *ifp)\n{\n\tchar pssid[IF_SSIDLEN * 4];\n\n\tif (print_string(pssid, sizeof(pssid), OT_ESCSTRING, ifp->ssid,\n\t\tifp->ssid_len) == -1) {\n\t\tlogerr(__func__);\n\t\treturn;\n\t}\n\n\tloginfox(\"%s: connected to Access Point: %s\", ifp->name, pssid);\n}\n\nstatic void\ndhcpcd_nocarrier_roaming(struct interface *ifp)\n{\n\tloginfox(\"%s: carrier lost - roaming\", ifp->name);\n\n#ifdef ARP\n\tarp_drop(ifp);\n#endif\n#ifdef INET\n\tdhcp_abort(ifp);\n#endif\n#ifdef INET6\n\tipv6nd_abort(ifp);\n#endif\n#ifdef DHCP6\n\tdhcp6_abort(ifp);\n#endif\n\n\trt_build(ifp->ctx, AF_UNSPEC);\n\tscript_runreason(ifp, \"NOCARRIER_ROAMING\");\n}\n\nvoid\ndhcpcd_handlecarrier(struct interface *ifp, int carrier, unsigned int flags)\n{\n\tbool was_link_up = if_is_link_up(ifp);\n\tbool was_roaming = if_roaming(ifp);\n\n\tifp->carrier = carrier;\n\tifp->flags = flags;\n\n\t/*\n\t * Inactive interfaces may not have options, we so check the\n\t * global context if we are stopping or not.\n\t * This allows an active interface to stop even if dhcpcd is not.\n\t */\n\tif (ifp->options != NULL) {\n\t\tif (ifp->options->options & DHCPCD_STOPPING)\n\t\t\treturn;\n\t} else if (ifp->ctx->options & DHCPCD_EXITING)\n\t\treturn;\n\n\tif (!if_is_link_up(ifp)) {\n\t\tif (!ifp->active || (!was_link_up && !was_roaming))\n\t\t\treturn;\n\n\t\t/*\n\t\t * If the interface is roaming (generally on wireless)\n\t\t * then while we are not up, we are not down either.\n\t\t * Preserve the network state until we either disconnect\n\t\t * or re-connect.\n\t\t */\n\t\tif (!ifp->options->randomise_hwaddr && if_roaming(ifp)) {\n\t\t\tdhcpcd_nocarrier_roaming(ifp);\n\t\t\treturn;\n\t\t}\n\n\t\tloginfox(\"%s: carrier lost\", ifp->name);\n\t\tscript_runreason(ifp, \"NOCARRIER\");\n\t\tdhcpcd_drop(ifp, 0);\n\n\t\tif (ifp->options->randomise_hwaddr) {\n\t\t\tbool is_up = ifp->flags & IFF_UP;\n\n\t\t\tif (is_up)\n\t\t\t\tif_down(ifp);\n\t\t\tif (if_randomisemac(ifp) == -1 && errno != ENXIO)\n\t\t\t\tlogerr(__func__);\n\t\t\tif (is_up)\n\t\t\t\tif_up(ifp);\n\t\t}\n\n\t\treturn;\n\t}\n\n\t/*\n\t * At this point carrier is NOT DOWN and we have IFF_UP.\n\t * We should treat LINK_UNKNOWN as up as the driver may not support\n\t * link state changes.\n\t * The consideration of any other information about carrier should\n\t * be handled in the OS specific if_carrier() function.\n\t */\n\tif (was_link_up)\n\t\treturn;\n\n\tif (ifp->active) {\n\t\tif (carrier == LINK_UNKNOWN)\n\t\t\tloginfox(\"%s: carrier unknown, assuming up\", ifp->name);\n\t\telse\n\t\t\tloginfox(\"%s: carrier acquired\", ifp->name);\n\t}\n\n#if !defined(__linux__) && !defined(__NetBSD__)\n\t/* BSD does not emit RTM_NEWADDR or RTM_CHGADDR when the\n\t * hardware address changes so we have to go\n\t * through the disovery process to work it out. */\n\tdhcpcd_handleinterface(ifp->ctx, 0, ifp->name);\n#endif\n\n\tif (ifp->wireless) {\n\t\tuint8_t ossid[IF_SSIDLEN];\n\t\tsize_t olen;\n\n\t\tolen = ifp->ssid_len;\n\t\tmemcpy(ossid, ifp->ssid, ifp->ssid_len);\n\t\tif_getssid(ifp);\n\n\t\t/* If we changed SSID network, drop leases */\n\t\tif ((ifp->ssid_len != olen ||\n\t\t\tmemcmp(ifp->ssid, ossid, ifp->ssid_len)) &&\n\t\t    ifp->active) {\n\t\t\tdhcpcd_reportssid(ifp);\n\t\t\tdhcpcd_drop(ifp, 0);\n#ifdef IPV4LL\n\t\t\tipv4ll_reset(ifp);\n#endif\n\t\t}\n\t}\n\n\tif (!ifp->active)\n\t\treturn;\n\n\tdhcpcd_initstate(ifp, 0);\n\tscript_runreason(ifp, \"CARRIER\");\n\n#ifdef INET6\n\t/* Set any IPv6 Routers we remembered to expire faster than they\n\t * would normally as we maybe on a new network. */\n\tipv6nd_startexpire(ifp);\n#ifdef IPV6_MANAGETEMPADDR\n\t/* RFC4941 Section 3.5 */\n\tipv6_regentempaddrs(ifp);\n#endif\n#endif\n\n\tdhcpcd_startinterface(ifp);\n}\n\nstatic void\nwarn_iaid_conflict(struct interface *ifp, uint16_t ia_type, uint8_t *iaid)\n{\n\tstruct interface *ifn;\n#ifdef INET6\n\tsize_t i;\n\tstruct if_ia *ia;\n#endif\n\n\tTAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) {\n\t\tif (ifn == ifp || !ifn->active)\n\t\t\tcontinue;\n\t\tif (ifn->options->options & DHCPCD_ANONYMOUS)\n\t\t\tcontinue;\n\t\tif (ia_type == 0 &&\n\t\t    memcmp(ifn->options->iaid, iaid,\n\t\t\tsizeof(ifn->options->iaid)) == 0)\n\t\t\tbreak;\n#ifdef INET6\n\t\tfor (i = 0; i < ifn->options->ia_len; i++) {\n\t\t\tia = &ifn->options->ia[i];\n\t\t\tif (ia->ia_type == ia_type &&\n\t\t\t    memcmp(ia->iaid, iaid, sizeof(ia->iaid)) == 0)\n\t\t\t\tbreak;\n\t\t}\n#endif\n\t}\n\n\t/* This is only a problem if the interfaces are on the same network. */\n\tif (ifn)\n\t\tlogerrx(\"%s: IAID conflicts with one assigned to %s\", ifp->name,\n\t\t    ifn->name);\n}\n\nstatic void\ndhcpcd_initduid(struct dhcpcd_ctx *ctx, struct interface *ifp)\n{\n\tchar buf[DUID_LEN * 3];\n\n\tif (ctx->duid != NULL) {\n\t\tif (ifp == NULL)\n\t\t\tgoto log;\n\t\treturn;\n\t}\n\n\tduid_init(ctx, ifp);\n\tif (ctx->duid == NULL)\n\t\treturn;\n\nlog:\n\tloginfox(\"DUID %s\",\n\t    hwaddr_ntoa(ctx->duid, ctx->duid_len, buf, sizeof(buf)));\n}\n\nvoid\ndhcpcd_startinterface(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tstruct if_options *ifo = ifp->options;\n\n\tif (ifo->options & DHCPCD_LINK && !if_is_link_up(ifp)) {\n\t\tloginfox(\"%s: waiting for carrier\", ifp->name);\n\t\treturn;\n\t}\n\n\tif (ifo->options & (DHCPCD_DUID | DHCPCD_IPV6) &&\n\t    !(ifo->options & DHCPCD_ANONYMOUS)) {\n\t\tchar buf[sizeof(ifo->iaid) * 3];\n#ifdef INET6\n\t\tsize_t i;\n\t\tstruct if_ia *ia;\n#endif\n\n\t\t/* Try and init DUID from the interface hardware address */\n\t\tdhcpcd_initduid(ifp->ctx, ifp);\n\n\t\t/* Report IAIDs */\n\t\tloginfox(\"%s: IAID %s\", ifp->name,\n\t\t    hwaddr_ntoa(ifo->iaid, sizeof(ifo->iaid), buf,\n\t\t\tsizeof(buf)));\n\t\twarn_iaid_conflict(ifp, 0, ifo->iaid);\n\n#ifdef INET6\n\t\tfor (i = 0; i < ifo->ia_len; i++) {\n\t\t\tia = &ifo->ia[i];\n\t\t\tif (memcmp(ifo->iaid, ia->iaid, sizeof(ifo->iaid))) {\n\t\t\t\tloginfox(\"%s: IA type %u IAID %s\", ifp->name,\n\t\t\t\t    ia->ia_type,\n\t\t\t\t    hwaddr_ntoa(ia->iaid, sizeof(ia->iaid), buf,\n\t\t\t\t\tsizeof(buf)));\n\t\t\t\twarn_iaid_conflict(ifp, ia->ia_type, ia->iaid);\n\t\t\t}\n\t\t}\n#endif\n\t}\n\n#ifdef INET6\n\tif (ifo->options & DHCPCD_IPV6 && ipv6_start(ifp) == -1) {\n\t\tlogerr(\"%s: ipv6_start\", ifp->name);\n\t\tifo->options &= ~DHCPCD_IPV6;\n\t}\n\n\tif (ifo->options & DHCPCD_IPV6) {\n\t\tif (ifp->active == IF_ACTIVE_USER) {\n\t\t\tipv6_startstatic(ifp);\n\n\t\t\tif (ifo->options & DHCPCD_IPV6RS)\n\t\t\t\tipv6nd_startrs(ifp);\n\t\t}\n\n#ifdef DHCP6\n\t\t/* DHCPv6 could be turned off, but the interface\n\t\t * is still delegated to. */\n\t\tif (ifp->active)\n\t\t\tdhcp6_find_delegates(ifp);\n\n\t\tif (ifo->options & DHCPCD_DHCP6) {\n\t\t\tif (ifp->active == IF_ACTIVE_USER) {\n\t\t\t\tenum DH6S d6_state;\n\n\t\t\t\tif (ifo->options & DHCPCD_IA_FORCED)\n\t\t\t\t\td6_state = DH6S_INIT;\n\t\t\t\telse if (ifo->options & DHCPCD_INFORM6)\n\t\t\t\t\td6_state = DH6S_INFORM;\n\t\t\t\telse\n\t\t\t\t\t/* CONFIRM lease triggered from RA */\n\t\t\t\t\td6_state = DH6S_CONFIRM;\n\t\t\t\tif (dhcp6_start(ifp, d6_state) == -1)\n\t\t\t\t\tlogerr(\"%s: dhcp6_start\", ifp->name);\n\t\t\t}\n\t\t}\n#endif\n\t}\n#endif\n\n#ifdef INET\n\tif (ifo->options & DHCPCD_IPV4 && ifp->active == IF_ACTIVE_USER) {\n\t\t/* Ensure we have an IPv4 state before starting DHCP */\n\t\tif (ipv4_getstate(ifp) != NULL)\n\t\t\tdhcp_start(ifp);\n\t}\n#endif\n}\n\nstatic void\ndhcpcd_prestartinterface(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tstruct dhcpcd_ctx *ctx = ifp->ctx;\n\tbool randmac_down;\n\n\tif (ifp->carrier <= LINK_DOWN && ifp->options->randomise_hwaddr &&\n\t    ifp->flags & IFF_UP) {\n\t\tif_down(ifp);\n\t\trandmac_down = true;\n\t} else\n\t\trandmac_down = false;\n\n\tif ((!(ctx->options & DHCPCD_MANAGER) ||\n\t\tifp->options->options & DHCPCD_IF_UP || randmac_down) &&\n\t    !(ifp->flags & IFF_UP)) {\n\t\tif (ifp->options->randomise_hwaddr &&\n\t\t    if_randomisemac(ifp) == -1)\n\t\t\tlogerr(__func__);\n\t\tif (if_up(ifp) == -1)\n\t\t\tlogerr(__func__);\n\t}\n\n\tdhcpcd_startinterface(ifp);\n}\n\nstatic void\nrun_preinit(struct interface *ifp)\n{\n\tif (ifp->ctx->options & DHCPCD_TEST)\n\t\treturn;\n\n\tscript_runreason(ifp, \"PREINIT\");\n\tif (ifp->wireless && if_is_link_up(ifp))\n\t\tdhcpcd_reportssid(ifp);\n\tif (ifp->options->options & DHCPCD_LINK && ifp->carrier != LINK_UNKNOWN)\n\t\tscript_runreason(ifp,\n\t\t    ifp->carrier == LINK_UP ? \"CARRIER\" : \"NOCARRIER\");\n}\n\nvoid\ndhcpcd_activateinterface(struct interface *ifp, unsigned long long options)\n{\n\tif (ifp->active)\n\t\treturn;\n\n\t/* IF_ACTIVE_USER will start protocols when the interface is started.\n\t * IF_ACTIVE will ask the protocols for setup,\n\t * such as any delegated prefixes. */\n\tifp->active = IF_ACTIVE;\n\tdhcpcd_initstate(ifp, options);\n\n\t/* It's possible we might not have been able to load\n\t * a config. */\n\tif (!ifp->active)\n\t\treturn;\n\n\trun_preinit(ifp);\n\tdhcpcd_prestartinterface(ifp);\n}\n\nint\ndhcpcd_handleinterface(void *arg, int action, const char *ifname)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\tstruct ifaddrs *ifaddrs;\n\tstruct if_head *ifs;\n\tstruct interface *ifp, *iff;\n\tconst char *const argv[] = { ifname };\n\tint e;\n\n\tif (action == -1) {\n\t\tifp = if_find(ctx->ifaces, ifname);\n\t\tif (ifp == NULL) {\n\t\t\terrno = ESRCH;\n\t\t\treturn -1;\n\t\t}\n\t\tif (ifp->active) {\n\t\t\tlogdebugx(\"%s: interface departed\", ifp->name);\n\t\t\tstop_interface(ifp);\n\t\t}\n\t\tTAILQ_REMOVE(ctx->ifaces, ifp, next);\n\t\tif_free(ifp);\n\t\treturn 0;\n\t}\n\n\tif (ctx->options & DHCPCD_EXITING)\n\t\treturn 0;\n\n\tifs = if_discover(ctx, &ifaddrs, -1, UNCONST(argv));\n\tif (ifs == NULL) {\n\t\tlogerr(__func__);\n\t\treturn -1;\n\t}\n\n\tifp = if_find(ifs, ifname);\n\tif (ifp == NULL) {\n\t\t/* This can happen if an interface is quickly added\n\t\t * and then removed. */\n\t\terrno = ENOENT;\n\t\te = -1;\n\t\tgoto out;\n\t}\n\te = 1;\n\n\t/* Check if we already have the interface */\n\tiff = if_find(ctx->ifaces, ifp->name);\n\n\tif (iff != NULL) {\n\t\tif (iff->active)\n\t\t\tlogdebugx(\"%s: interface updated\", iff->name);\n\t\t/* The flags and hwaddr could have changed */\n\t\tiff->flags = ifp->flags;\n\t\tiff->hwlen = ifp->hwlen;\n\t\tif (ifp->hwlen != 0)\n\t\t\tmemcpy(iff->hwaddr, ifp->hwaddr, iff->hwlen);\n\t} else {\n\t\tTAILQ_REMOVE(ifs, ifp, next);\n\t\tTAILQ_INSERT_TAIL(ctx->ifaces, ifp, next);\n\t\tif (ifp->active) {\n\t\t\tlogdebugx(\"%s: interface added\", ifp->name);\n\t\t\tdhcpcd_initstate(ifp, 0);\n\t\t\trun_preinit(ifp);\n\t\t}\n\t\tiff = ifp;\n\t}\n\n\tif (action > 0) {\n\t\tif_learnaddrs(ctx, ifs, &ifaddrs);\n\t\tif (iff->active)\n\t\t\tdhcpcd_prestartinterface(iff);\n\t}\n\nout:\n\t/* Free our discovered list */\n\twhile ((ifp = TAILQ_FIRST(ifs))) {\n\t\tTAILQ_REMOVE(ifs, ifp, next);\n\t\tif_free(ifp);\n\t}\n\tfree(ifs);\n\tif_freeifaddrs(ctx, &ifaddrs);\n\n\treturn e;\n}\n\nstatic void\ndhcpcd_handlelink(void *arg, unsigned short events)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\n\tif (events != ELE_READ)\n\t\tlogerrx(\"%s: unexpected event 0x%04x\", __func__, events);\n\n\tif (if_handlelink(ctx) == -1) {\n\t\tif (errno == ENOBUFS || errno == ENOMEM) {\n\t\t\tdhcpcd_linkoverflow(ctx);\n\t\t\treturn;\n\t\t}\n\t\tif (errno != ENOTSUP && errno != ENXIO)\n\t\t\tlogerr(__func__);\n\t}\n}\n\nstatic void\ndhcpcd_checkcarrier(void *arg)\n{\n\tstruct interface *ifp0 = arg, *ifp;\n\n\tifp = if_find(ifp0->ctx->ifaces, ifp0->name);\n\tif (ifp != NULL)\n\t\tdhcpcd_handlecarrier(ifp, ifp0->carrier, ifp0->flags);\n\tif_free(ifp0);\n}\n\n#ifndef SMALL\nstatic void\ndhcpcd_setlinkrcvbuf(struct dhcpcd_ctx *ctx)\n{\n\tsocklen_t socklen;\n\n\tif (ctx->link_rcvbuf == 0)\n\t\treturn;\n\n\tlogdebugx(\"setting route socket receive buffer size to %d bytes\",\n\t    ctx->link_rcvbuf);\n\n\tsocklen = sizeof(ctx->link_rcvbuf);\n\tif (setsockopt(ctx->link_fd, SOL_SOCKET, SO_RCVBUF, &ctx->link_rcvbuf,\n\t\tsocklen) == -1)\n\t\tlogerr(__func__);\n}\n#endif\n\nstatic void\ndhcpcd_runprestartinterface(void *arg)\n{\n\tstruct interface *ifp = arg;\n\n\trun_preinit(ifp);\n\tdhcpcd_prestartinterface(ifp);\n}\n\nvoid\ndhcpcd_linkoverflow(struct dhcpcd_ctx *ctx)\n{\n\tsocklen_t socklen;\n\tint rcvbuflen;\n\tchar buf[2048];\n\tssize_t rlen;\n\tsize_t rcnt;\n\tstruct if_head *ifaces;\n\tstruct ifaddrs *ifaddrs;\n\tstruct interface *ifp, *ifn, *ifp1;\n\n\tsocklen = sizeof(rcvbuflen);\n\tif (getsockopt(ctx->link_fd, SOL_SOCKET, SO_RCVBUF, &rcvbuflen,\n\t\t&socklen) == -1) {\n\t\tlogerr(\"%s: getsockopt\", __func__);\n\t\trcvbuflen = 0;\n\t}\n#ifdef __linux__\n\telse\n\t\trcvbuflen /= 2;\n#endif\n\n\tlogerrx(\"route socket overflowed (rcvbuflen %d)\"\n\t\t\" - learning interface state\",\n\t    rcvbuflen);\n\n\t/* Drain the socket.\n\t * We cannot open a new one due to privsep. */\n\trcnt = 0;\n\tdo {\n\t\trlen = read(ctx->link_fd, buf, sizeof(buf));\n\t\tif (++rcnt % 1000 == 0)\n\t\t\tlogwarnx(\"drained %zu messages\", rcnt);\n\t} while (rlen != -1 || errno == ENOBUFS || errno == ENOMEM);\n\tif (rcnt % 1000 != 0)\n\t\tlogwarnx(\"drained %zu messages\", rcnt);\n\n\t/* Work out the current interfaces. */\n\tifaces = if_discover(ctx, &ifaddrs, ctx->ifc, ctx->ifv);\n\tif (ifaces == NULL) {\n\t\tlogerr(__func__);\n\t\treturn;\n\t}\n\n\t/* Punt departed interfaces */\n\tTAILQ_FOREACH_SAFE(ifp, ctx->ifaces, next, ifn) {\n\t\tif (if_find(ifaces, ifp->name) != NULL)\n\t\t\tcontinue;\n\t\tdhcpcd_handleinterface(ctx, -1, ifp->name);\n\t}\n\n\t/* Add new interfaces */\n\twhile ((ifp = TAILQ_FIRST(ifaces)) != NULL) {\n\t\tTAILQ_REMOVE(ifaces, ifp, next);\n\t\tifp1 = if_find(ctx->ifaces, ifp->name);\n\t\tif (ifp1 != NULL) {\n\t\t\t/* If the interface already exists,\n\t\t\t * check carrier state.\n\t\t\t * dhcpcd_checkcarrier will free ifp. */\n\t\t\teloop_timeout_add_sec(ctx->eloop, 0,\n\t\t\t    dhcpcd_checkcarrier, ifp);\n\t\t\tcontinue;\n\t\t}\n\t\tTAILQ_INSERT_TAIL(ctx->ifaces, ifp, next);\n\t\tif (ifp->active) {\n\t\t\tdhcpcd_initstate(ifp, 0);\n\t\t\teloop_timeout_add_sec(ctx->eloop, 0,\n\t\t\t    dhcpcd_runprestartinterface, ifp);\n\t\t}\n\t}\n\tfree(ifaces);\n\n\t/* Update address state. */\n\tif_markaddrsstale(ctx->ifaces);\n\tif_learnaddrs(ctx, ctx->ifaces, &ifaddrs);\n\tif_deletestaleaddrs(ctx->ifaces);\n\tif_freeifaddrs(ctx, &ifaddrs);\n}\n\nvoid\ndhcpcd_handlehwaddr(struct interface *ifp, uint16_t hwtype, const void *hwaddr,\n    uint8_t hwlen)\n{\n\tchar buf[sizeof(ifp->hwaddr) * 3];\n\n\tif (hwaddr == NULL || !if_valid_hwaddr(hwaddr, hwlen))\n\t\thwlen = 0;\n\n\tif (hwlen > sizeof(ifp->hwaddr)) {\n\t\terrno = ENOBUFS;\n\t\tlogerr(\"%s: %s\", __func__, ifp->name);\n\t\treturn;\n\t}\n\n\tif (ifp->hwtype != hwtype) {\n\t\tif (ifp->active)\n\t\t\tloginfox(\"%s: hardware address type changed\"\n\t\t\t\t \" from %d to %d\",\n\t\t\t    ifp->name, ifp->hwtype, hwtype);\n\t\tifp->hwtype = hwtype;\n\t}\n\n\tif (ifp->hwlen == hwlen &&\n\t    (hwlen == 0 || memcmp(ifp->hwaddr, hwaddr, hwlen) == 0))\n\t\treturn;\n\n\tif (ifp->active) {\n\t\tloginfox(\"%s: old hardware address: %s\", ifp->name,\n\t\t    hwaddr_ntoa(ifp->hwaddr, ifp->hwlen, buf, sizeof(buf)));\n\t\tloginfox(\"%s: new hardware address: %s\", ifp->name,\n\t\t    hwaddr_ntoa(hwaddr, hwlen, buf, sizeof(buf)));\n\t}\n\tifp->hwlen = hwlen;\n\tif (hwaddr != NULL)\n\t\tmemcpy(ifp->hwaddr, hwaddr, hwlen);\n}\n\nstatic void\nif_reboot(struct interface *ifp, int argc, char **argv)\n{\n#ifdef INET\n\tunsigned long long oldopts;\n\n\toldopts = ifp->options->options;\n#endif\n\tscript_runreason(ifp, \"RECONFIGURE\");\n\tdhcpcd_initstate1(ifp, argc, argv, 0);\n#ifdef INET\n\tif (ifp->options->options & DHCPCD_DHCP)\n\t\tdhcp_reboot_newopts(ifp, oldopts);\n#endif\n#ifdef DHCP6\n\tif (ifp->options->options & DHCPCD_DHCP6)\n\t\tdhcp6_reboot(ifp);\n#endif\n\tdhcpcd_prestartinterface(ifp);\n}\n\nstatic void\nreload_config(struct dhcpcd_ctx *ctx)\n{\n\tstruct if_options *ifo;\n\n\tfree_globals(ctx);\n\tif ((ifo = read_config(ctx, NULL, NULL, NULL)) == NULL)\n\t\treturn;\n\tadd_options(ctx, NULL, ifo, ctx->argc, ctx->argv);\n\t/* We need to preserve these options. */\n\tif (ctx->options & DHCPCD_STARTED)\n\t\tifo->options |= DHCPCD_STARTED;\n\tif (ctx->options & DHCPCD_MANAGER)\n\t\tifo->options |= DHCPCD_MANAGER;\n\tif (ctx->options & DHCPCD_DAEMONISED)\n\t\tifo->options |= DHCPCD_DAEMONISED;\n\tif (ctx->options & DHCPCD_PRIVSEP)\n\t\tifo->options |= DHCPCD_PRIVSEP;\n\tctx->options = ifo->options;\n\tfree_options(ctx, ifo);\n}\n\nstatic void\nreconf_reboot(struct dhcpcd_ctx *ctx, const bool reboot, const int argc,\n    char **argv, const int oi)\n{\n\tint i;\n\tstruct interface *ifp;\n\tbool all_interfaces = argc == oi, iface_found;\n\n\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\tfor (i = oi; i < argc; i++) {\n\t\t\tif (strcmp(ifp->name, argv[i]) == 0)\n\t\t\t\tbreak;\n\t\t}\n\n\t\tiface_found = i != argc;\n\t\tif (!all_interfaces && !iface_found)\n\t\t\tcontinue;\n\n\t\tif (ifp->active == IF_ACTIVE_USER) {\n\t\t\tif (reboot)\n\t\t\t\tif_reboot(ifp, argc, argv);\n#ifdef INET\n\t\t\telse\n\t\t\t\tipv4_applyaddr(ifp);\n#endif\n\t\t} else if (iface_found) {\n\t\t\tifp->active = IF_ACTIVE_USER;\n\t\t\tdhcpcd_initstate1(ifp, argc, argv, 0);\n\n\t\t\tfree(ifp->argv);\n\t\t\tif (argc > 0) {\n\t\t\t\tifp->argv = alloc_args(argc, argv);\n\t\t\t\tif (ifp->argv == NULL) {\n\t\t\t\t\tlogerr(\"alloc_args\");\n\t\t\t\t\tgoto alloc_args_err;\n\t\t\t\t}\n\t\t\t\tifp->argc = argc;\n\t\t\t} else {\n\t\t\talloc_args_err:\n\t\t\t\tifp->argv = NULL;\n\t\t\t\tifp->argc = 0;\n\t\t\t}\n\n\t\t\trun_preinit(ifp);\n\t\t\tdhcpcd_prestartinterface(ifp);\n\t\t}\n\t}\n}\n\nstatic bool\nstop_all_interfaces(struct dhcpcd_ctx *ctx, unsigned long long opts)\n{\n\tstruct interface *ifp;\n\tbool anystopped = false;\n\n\tctx->options |= opts;\n\tif (ctx->ifaces == NULL)\n\t\treturn anystopped;\n\n\tif (ctx->options & DHCPCD_RELEASE)\n\t\tctx->options &= ~DHCPCD_PERSISTENT;\n\n\t/* Drop the last interface first */\n\tTAILQ_FOREACH_REVERSE(ifp, ctx->ifaces, if_head, next) {\n\t\tif (!ifp->active)\n\t\t\tcontinue;\n\t\tifp->options->options |= opts;\n\t\tif (ifp->options->options & DHCPCD_RELEASE)\n\t\t\tifp->options->options &= ~DHCPCD_PERSISTENT;\n\t\tifp->options->options |= DHCPCD_EXITING;\n\t\tanystopped = true;\n\t\tstop_interface(ifp);\n\t}\n\treturn anystopped;\n}\n\nstatic void\ndhcpcd_ifrenew(struct interface *ifp)\n{\n\tif (!ifp->active)\n\t\treturn;\n\n\tif (ifp->options->options & DHCPCD_LINK && !if_is_link_up(ifp))\n\t\treturn;\n\n#ifdef INET\n\tdhcp_renew(ifp);\n#endif\n#ifdef INET6\n#define DHCPCD_RARENEW (DHCPCD_IPV6 | DHCPCD_IPV6RS)\n\tif ((ifp->options->options & DHCPCD_RARENEW) == DHCPCD_RARENEW)\n\t\tipv6nd_startrs(ifp);\n#endif\n#ifdef DHCP6\n\tdhcp6_renew(ifp);\n#endif\n}\n\nstatic void\ndhcpcd_renew(struct dhcpcd_ctx *ctx)\n{\n\tstruct interface *ifp;\n\n\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\tdhcpcd_ifrenew(ifp);\n\t}\n}\n\n#ifdef USE_SIGNALS\n#define sigmsg \"received %s, %s\"\nstatic void\ndhcpcd_signal_cb(int sig, void *arg)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\tunsigned long long opts;\n\tint exit_code;\n\n\tif (ctx->options & DHCPCD_DUMPLEASE) {\n\t\teloop_exit(ctx->eloop, EXIT_FAILURE);\n\t\treturn;\n\t}\n\n\tif (sig != SIGCHLD && ctx->options & DHCPCD_FORKED) {\n\t\tif (ctx->fork_fd != -1 && sig != SIGHUP &&\n\t\t    send(ctx->fork_fd, &sig, sizeof(sig), 0) == -1)\n\t\t\tlogerr(\"%s: send\", __func__);\n\t\treturn;\n\t}\n\n\topts = 0;\n\texit_code = EXIT_FAILURE;\n\tswitch (sig) {\n\tcase SIGINT:\n\t\tloginfox(sigmsg, \"SIGINT\", \"stopping\");\n\t\tbreak;\n\tcase SIGTERM:\n\t\tloginfox(sigmsg, \"SIGTERM\", \"stopping\");\n\t\texit_code = EXIT_SUCCESS;\n\t\tbreak;\n\tcase SIGALRM:\n\t\tloginfox(sigmsg, \"SIGALRM\", \"releasing\");\n\t\topts |= DHCPCD_RELEASE;\n\t\texit_code = EXIT_SUCCESS;\n\t\tbreak;\n\tcase SIGHUP:\n\t\tloginfox(sigmsg, \"SIGHUP\", \"rebinding\");\n\t\treload_config(ctx);\n\t\t/* Preserve any options passed on the commandline\n\t\t * when we were started. */\n\t\treconf_reboot(ctx, true, ctx->argc, ctx->argv,\n\t\t    ctx->argc - ctx->ifc);\n\t\treturn;\n\tcase SIGUSR1:\n\t\tloginfox(sigmsg, \"SIGUSR1\", \"renewing\");\n\t\tdhcpcd_renew(ctx);\n\t\treturn;\n\tcase SIGUSR2:\n\t\tloginfox(sigmsg, \"SIGUSR2\", \"reopening log\");\n#ifdef PRIVSEP\n\t\tif (IN_PRIVSEP(ctx)) {\n\t\t\tif (ps_root_logreopen(ctx) == -1)\n\t\t\t\tlogerr(\"ps_root_logreopen\");\n\t\t\treturn;\n\t\t}\n#endif\n\t\tif (logopen(ctx->logfile) == -1)\n\t\t\tlogerr(\"logopen\");\n\t\treturn;\n\tcase SIGCHLD:\n#ifdef PRIVSEP\n\t\tps_root_signalcb(sig, ctx);\n#else\n\t\twhile (waitpid(-1, NULL, WNOHANG) > 0)\n\t\t\t;\n#endif\n\t\treturn;\n\tdefault:\n\t\tlogerrx(\"received signal %d but don't know what to do with it\",\n\t\t    sig);\n\t\treturn;\n\t}\n\n\t/*\n\t * Privsep has a mini-eloop for reading data from other processes.\n\t * This mini-eloop processes signals as well so we can reap children.\n\t * During teardown we don't want to process SIGTERM or SIGINT again,\n\t * as that could trigger memory issues.\n\t */\n\tif (ctx->options & DHCPCD_EXITING)\n\t\treturn;\n\n\tctx->options |= DHCPCD_EXITING;\n\tif (!(ctx->options & DHCPCD_TEST) && stop_all_interfaces(ctx, opts)) {\n\t\t/* We stopped something, we will exit once that is done. */\n\t\treturn;\n\t}\n\n\teloop_exit(ctx->eloop, exit_code);\n}\n#endif\n\nint\ndhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd, int argc,\n    char **argv)\n{\n\tstruct interface *ifp;\n\tstruct if_options *ifo;\n\tunsigned long long opts, orig_opts;\n\tint opt, oi, oifind, af = AF_UNSPEC;\n\tbool do_reboot, do_renew;\n\tsize_t len, l, nifaces;\n\tchar *tmp, *p;\n\n\t/* Special commands for our control socket\n\t * as the other end should be blocking until it gets the\n\t * expected reply we should be safely able just to change the\n\t * write callback on the fd */\n\t/* Make any change here in privsep-control.c as well. */\n\tif (strcmp(*argv, \"--version\") == 0) {\n\t\treturn control_queue(fd, UNCONST(VERSION), strlen(VERSION) + 1);\n\t} else if (strcmp(*argv, \"--getconfigfile\") == 0) {\n\t\treturn control_queue(fd, UNCONST(fd->ctx->cffile),\n\t\t    strlen(fd->ctx->cffile) + 1);\n\t} else if (strcmp(*argv, \"--getinterfaces\") == 0) {\n\t\toifind = argc = 0;\n\t\tgoto dumplease;\n\t} else if (strcmp(*argv, \"--listen\") == 0) {\n\t\tfd->flags |= FD_LISTEN;\n\t\treturn 0;\n\t}\n\n\t/* Log the command */\n\tlen = 1;\n\tfor (opt = 0; opt < argc; opt++)\n\t\tlen += strlen(argv[opt]) + 1;\n\ttmp = malloc(len);\n\tif (tmp == NULL)\n\t\treturn -1;\n\tp = tmp;\n\tfor (opt = 0; opt < argc; opt++) {\n\t\tl = strlen(argv[opt]);\n\t\tstrlcpy(p, argv[opt], len);\n\t\tlen -= l + 1;\n\t\tp += l;\n\t\t*p++ = ' ';\n\t}\n\t*--p = '\\0';\n\tloginfox(\"control command: %s\", tmp);\n\tfree(tmp);\n\n\toptind = 0;\n\toi = 0;\n\topts = 0;\n\tdo_reboot = do_renew = false;\n\twhile (\n\t    (opt = getopt_long(argc, argv, IF_OPTS, cf_options, &oi)) != -1) {\n\t\tswitch (opt) {\n\t\tcase 'g':\n\t\t\t/* Assumed if below not set */\n\t\t\tbreak;\n\t\tcase 'k':\n\t\t\topts |= DHCPCD_RELEASE;\n\t\t\tbreak;\n\t\tcase 'n':\n\t\t\tdo_reboot = true;\n\t\t\tbreak;\n\t\tcase 'p':\n\t\t\topts |= DHCPCD_PERSISTENT;\n\t\t\tbreak;\n\t\tcase 'x':\n\t\t\topts |= DHCPCD_EXITING;\n\t\t\tbreak;\n\t\tcase 'N':\n\t\t\tdo_renew = true;\n\t\t\tbreak;\n\t\tcase 'U':\n\t\t\topts |= DHCPCD_DUMPLEASE;\n\t\t\tbreak;\n\t\tcase '4':\n\t\t\taf = AF_INET;\n\t\t\tbreak;\n\t\tcase '6':\n\t\t\taf = AF_INET6;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* store the index; the optind will change when a getopt get called */\n\toifind = optind;\n\n\tif (opts & DHCPCD_DUMPLEASE) {\n\t\tctx->options |= DHCPCD_DUMPLEASE;\n\tdumplease:\n\t\tnifaces = 0;\n\t\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\t\tif (!ifp->active)\n\t\t\t\tcontinue;\n\t\t\tfor (oi = oifind; oi < argc; oi++) {\n\t\t\t\tif (strcmp(ifp->name, argv[oi]) == 0)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (oifind == argc || oi < argc) {\n\t\t\t\topt = send_interface(NULL, ifp, af);\n\t\t\t\tif (opt == -1)\n\t\t\t\t\tgoto dumperr;\n\t\t\t\tnifaces += (size_t)opt;\n\t\t\t}\n\t\t}\n\t\tif (write(fd->fd, &nifaces, sizeof(nifaces)) != sizeof(nifaces))\n\t\t\tgoto dumperr;\n\t\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\t\tif (!ifp->active)\n\t\t\t\tcontinue;\n\t\t\tfor (oi = oifind; oi < argc; oi++) {\n\t\t\t\tif (strcmp(ifp->name, argv[oi]) == 0)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (oifind == argc || oi < argc) {\n\t\t\t\tif (send_interface(fd, ifp, af) == -1)\n\t\t\t\t\tgoto dumperr;\n\t\t\t}\n\t\t}\n\t\tctx->options &= ~DHCPCD_DUMPLEASE;\n\t\treturn 0;\n\tdumperr:\n\t\tctx->options &= ~DHCPCD_DUMPLEASE;\n\t\treturn -1;\n\t}\n\n\t/* Only privileged users can control dhcpcd via the socket. */\n\tif (fd->flags & FD_UNPRIV) {\n\t\terrno = EPERM;\n\t\treturn -1;\n\t}\n\n\tif (opts & (DHCPCD_EXITING | DHCPCD_RELEASE)) {\n\t\tif (oifind == argc && af == AF_UNSPEC) {\n\t\t\tctx->options |= DHCPCD_EXITING;\n\t\t\tif (stop_all_interfaces(ctx, opts) == false)\n\t\t\t\teloop_exit(ctx->eloop, EXIT_SUCCESS);\n\t\t\t/* We did stop an interface, it will notify us once\n\t\t\t * dropped so we can exit. */\n\t\t\treturn 0;\n\t\t}\n\n\t\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\t\tif (!ifp->active)\n\t\t\t\tcontinue;\n\t\t\tfor (oi = oifind; oi < argc; oi++) {\n\t\t\t\tif (strcmp(ifp->name, argv[oi]) == 0)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (oi == argc)\n\t\t\t\tcontinue;\n\n\t\t\tifo = ifp->options;\n\t\t\torig_opts = ifo->options;\n\t\t\tifo->options |= opts;\n\t\t\tif (opts & DHCPCD_RELEASE)\n\t\t\t\tifo->options &= ~DHCPCD_PERSISTENT;\n\t\t\tswitch (af) {\n\t\t\tcase AF_INET:\n\t\t\t\tifo->options &= ~DHCPCD_IPV4;\n\t\t\t\tbreak;\n\t\t\tcase AF_INET6:\n\t\t\t\tifo->options &= ~DHCPCD_IPV6;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (af != AF_UNSPEC)\n\t\t\t\tdhcpcd_drop_af(ifp, 1, af);\n\t\t\telse\n\t\t\t\tstop_interface(ifp);\n\t\t\tifo->options = orig_opts;\n\t\t}\n\t\treturn 0;\n\t}\n\n\tif (do_renew) {\n\t\tif (oifind == argc) {\n\t\t\tdhcpcd_renew(ctx);\n\t\t\treturn 0;\n\t\t}\n\t\tfor (oi = oifind; oi < argc; oi++) {\n\t\t\tif ((ifp = if_find(ctx->ifaces, argv[oi])) == NULL)\n\t\t\t\tcontinue;\n\t\t\tdhcpcd_ifrenew(ifp);\n\t\t}\n\t\treturn 0;\n\t}\n\n\treload_config(ctx);\n\treconf_reboot(ctx, do_reboot, argc, argv, oifind);\n\treturn 0;\n}\n\nstatic void dhcpcd_readdump1(void *, unsigned short);\n\nstatic void\ndhcpcd_readdump2(void *arg, unsigned short events)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\tssize_t len;\n\tint exit_code = EXIT_FAILURE;\n\n\tif (events != ELE_READ)\n\t\tlogerrx(\"%s: unexpected event 0x%04x\", __func__, events);\n\n\tlen = read(ctx->control_fd, ctx->ctl_buf + ctx->ctl_bufpos,\n\t    ctx->ctl_buflen - ctx->ctl_bufpos);\n\tif (len == -1) {\n\t\tlogerr(__func__);\n\t\tgoto finished;\n\t} else if (len == 0)\n\t\tgoto finished;\n\tif ((size_t)len + ctx->ctl_bufpos != ctx->ctl_buflen) {\n\t\tctx->ctl_bufpos += (size_t)len;\n\t\treturn;\n\t}\n\n\tif (ctx->ctl_buf[ctx->ctl_buflen - 1] != '\\0') /* unlikely */\n\t\tctx->ctl_buf[ctx->ctl_buflen - 1] = '\\0';\n\tscript_dump(ctx->ctl_buf, ctx->ctl_buflen);\n\tfflush(stdout);\n\tif (--ctx->ctl_extra != 0) {\n\t\tputchar('\\n');\n\t\tif (eloop_event_add(ctx->eloop, ctx->control_fd, ELE_READ,\n\t\t\tdhcpcd_readdump1, ctx) == -1)\n\t\t\tlogerr(\"%s: eloop_event_add\", __func__);\n\t\treturn;\n\t}\n\texit_code = EXIT_SUCCESS;\n\nfinished:\n\tshutdown(ctx->control_fd, SHUT_RDWR);\n\teloop_exit(ctx->eloop, exit_code);\n}\n\nstatic void\ndhcpcd_readdump1(void *arg, unsigned short events)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\tssize_t len;\n\n\tif (events != ELE_READ)\n\t\tlogerrx(\"%s: unexpected event 0x%04x\", __func__, events);\n\n\tlen = read(ctx->control_fd, &ctx->ctl_buflen, sizeof(ctx->ctl_buflen));\n\tif (len != sizeof(ctx->ctl_buflen)) {\n\t\tif (len != -1)\n\t\t\terrno = EINVAL;\n\t\tgoto err;\n\t}\n\tif (ctx->ctl_buflen > SSIZE_MAX) {\n\t\terrno = ENOBUFS;\n\t\tgoto err;\n\t}\n\n\tfree(ctx->ctl_buf);\n\tctx->ctl_buf = malloc(ctx->ctl_buflen);\n\tif (ctx->ctl_buf == NULL)\n\t\tgoto err;\n\n\tctx->ctl_bufpos = 0;\n\tif (eloop_event_add(ctx->eloop, ctx->control_fd, ELE_READ,\n\t\tdhcpcd_readdump2, ctx) == -1)\n\t\tlogerr(\"%s: eloop_event_add\", __func__);\n\treturn;\n\nerr:\n\tlogerr(__func__);\n\teloop_exit(ctx->eloop, EXIT_FAILURE);\n}\n\nstatic void\ndhcpcd_readdump0(void *arg, unsigned short events)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\tssize_t len;\n\n\tif (events != ELE_READ)\n\t\tlogerrx(\"%s: unexpected event 0x%04x\", __func__, events);\n\n\tlen = read(ctx->control_fd, &ctx->ctl_extra, sizeof(ctx->ctl_extra));\n\tif (len != sizeof(ctx->ctl_extra)) {\n\t\tif (len != -1)\n\t\t\terrno = EINVAL;\n\t\tlogerr(__func__);\n\t\teloop_exit(ctx->eloop, EXIT_FAILURE);\n\t\treturn;\n\t}\n\n\tif (ctx->ctl_extra == 0) {\n\t\teloop_exit(ctx->eloop, EXIT_SUCCESS);\n\t\treturn;\n\t}\n\n\tif (eloop_event_add(ctx->eloop, ctx->control_fd, ELE_READ,\n\t\tdhcpcd_readdump1, ctx) == -1)\n\t\tlogerr(\"%s: eloop_event_add\", __func__);\n}\n\nstatic void\ndhcpcd_readdumptimeout(void *arg)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\n\tlogerrx(__func__);\n\teloop_exit(ctx->eloop, EXIT_FAILURE);\n}\n\nstatic int\ndhcpcd_readdump(struct dhcpcd_ctx *ctx)\n{\n\tctx->options |= DHCPCD_FORKED;\n\tif (eloop_timeout_add_sec(ctx->eloop, 5, dhcpcd_readdumptimeout, ctx) ==\n\t    -1)\n\t\treturn -1;\n\treturn eloop_event_add(ctx->eloop, ctx->control_fd, ELE_READ,\n\t    dhcpcd_readdump0, ctx);\n}\n\nstatic void\ndhcpcd_fork_cb(void *arg, unsigned short events)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\tint exit_code;\n\tssize_t len;\n\n\tif (!(events & ELE_READ))\n\t\tlogerrx(\"%s: unexpected event 0x%04x\", __func__, events);\n\n\tlen = recv(ctx->fork_fd, &exit_code, sizeof(exit_code), MSG_WAITALL);\n\tif (len == -1) {\n\t\tlogerr(__func__);\n\t\teloop_exit(ctx->eloop, EXIT_FAILURE);\n\t\treturn;\n\t}\n\tif (len == 0) {\n\t\tif (ctx->options & DHCPCD_FORKED) {\n\t\t\tlogerrx(\"%s: dhcpcd manager hungup\", __func__);\n\t\t\teloop_exit(ctx->eloop, EXIT_FAILURE);\n\t\t} else {\n\t\t\t// Launcher exited\n\t\t\teloop_event_delete(ctx->eloop, ctx->fork_fd);\n\t\t\tclose(ctx->fork_fd);\n\t\t\tctx->fork_fd = -1;\n\t\t}\n\t\treturn;\n\t}\n\tif ((size_t)len < sizeof(exit_code)) {\n\t\tlogerrx(\"%s: truncated read %zd (expected %zu)\", __func__, len,\n\t\t    sizeof(exit_code));\n\t\teloop_exit(ctx->eloop, EXIT_FAILURE);\n\t\treturn;\n\t}\n\n\tif (ctx->options & DHCPCD_FORKED) {\n\t\tif (exit_code == EXIT_SUCCESS)\n\t\t\tlogdebugx(\"forked to background\");\n\t\teloop_exit(ctx->eloop, exit_code);\n\t} else\n\t\tdhcpcd_signal_cb(exit_code, ctx);\n}\n\nstatic void\ndhcpcd_pidfile_timeout(void *arg)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\tpid_t pid;\n\n\tpid = pidfile_read(ctx->pidfile);\n\tif (pid == -1)\n\t\teloop_exit(ctx->eloop, EXIT_SUCCESS);\n\telse\n\t\teloop_timeout_add_msec(ctx->eloop, 100, dhcpcd_pidfile_timeout,\n\t\t    ctx);\n}\n\nstatic void\ndhcpcd_exit_timeout(void *arg)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\tpid_t pid;\n\n\tpid = pidfile_read(ctx->pidfile);\n\tif (pid == -1)\n\t\teloop_exit(ctx->eloop, EXIT_SUCCESS);\n\telse {\n\t\tlogwarnx(\"pid %lld failed to exit\", (long long)pid);\n\t\teloop_exit(ctx->eloop, EXIT_FAILURE);\n\t}\n}\n\nstatic int\ndup_null(int fd)\n{\n\tint fd_null = open(_PATH_DEVNULL, O_WRONLY);\n\tint err;\n\n\tif (fd_null == -1) {\n\t\tlogwarn(\"open %s\", _PATH_DEVNULL);\n\t\treturn -1;\n\t}\n\n\tif ((err = dup2(fd_null, fd)) == -1)\n\t\tlogwarn(\"dup2 %d\", fd);\n\tclose(fd_null);\n\treturn err;\n}\n\nint\nmain(int argc, char **argv, char **envp)\n{\n\tstruct dhcpcd_ctx ctx;\n\tstruct ifaddrs *ifaddrs = NULL;\n\tstruct if_options *ifo;\n\tstruct interface *ifp;\n\tsa_family_t family = AF_UNSPEC;\n\tint opt, oi = 0, i;\n\tunsigned int logopts, t;\n\tssize_t len;\n#if defined(USE_SIGNALS) || !defined(THERE_IS_NO_FORK)\n\tpid_t pid;\n\tint fork_fd[2];\n#endif\n#ifdef USE_SIGNALS\n\tint sig = 0;\n\tconst char *siga = NULL;\n\tsize_t si;\n#endif\n\n#ifdef SETPROCTITLE_H\n\tsetproctitle_init(argc, argv, envp);\n#else\n\tUNUSED(envp);\n#endif\n\n\t/* Test for --help and --version */\n\tif (argc > 1) {\n\t\tif (strcmp(argv[1], \"--help\") == 0) {\n\t\t\tusage();\n\t\t\treturn EXIT_SUCCESS;\n\t\t} else if (strcmp(argv[1], \"--version\") == 0) {\n\t\t\tprintf(\"\" PACKAGE \" \" VERSION \"\\n%s\\n\",\n\t\t\t    dhcpcd_copyright);\n\t\t\tprintf(\"Compiled in features:\"\n#ifdef INET\n\t\t\t       \" INET\"\n#endif\n#ifdef ARP\n\t\t\t       \" ARP\"\n#endif\n#ifdef ARPING\n\t\t\t       \" ARPing\"\n#endif\n#ifdef IPV4LL\n\t\t\t       \" IPv4LL\"\n#endif\n#ifdef INET6\n\t\t\t       \" INET6\"\n#endif\n#ifdef DHCP6\n\t\t\t       \" DHCPv6\"\n#endif\n#ifdef AUTH\n\t\t\t       \" AUTH\"\n#endif\n#ifdef PRIVSEP\n\t\t\t       \" PRIVSEP\"\n#endif\n\t\t\t       \"\\n\");\n\t\t\treturn EXIT_SUCCESS;\n\t\t}\n\t}\n\n\tmemset(&ctx, 0, sizeof(ctx));\n\tclosefrom(STDERR_FILENO + 1);\n\n\tifo = NULL;\n\tctx.cffile = CONFIG;\n\tctx.script = UNCONST(dhcpcd_default_script);\n\tctx.control_fd = ctx.control_unpriv_fd = ctx.link_fd = -1;\n\tctx.pf_inet_fd = -1;\n#ifdef PF_LINK\n\tctx.pf_link_fd = -1;\n#endif\n\n\tTAILQ_INIT(&ctx.control_fds);\n#ifdef USE_SIGNALS\n\tctx.fork_fd = -1;\n#endif\n#ifdef PLUGIN_DEV\n\tctx.dev_fd = -1;\n#endif\n#ifdef INET\n\tctx.udp_rfd = -1;\n\tctx.udp_wfd = -1;\n#endif\n#if defined(INET6) && !defined(__sun)\n\tctx.nd_fd = -1;\n#endif\n#ifdef DHCP6\n\tctx.dhcp6_rfd = -1;\n\tctx.dhcp6_wfd = -1;\n#endif\n#ifdef PRIVSEP\n\tctx.ps_log_fd = ctx.ps_log_root_fd = -1;\n\tTAILQ_INIT(&ctx.ps_processes);\n#endif\n\n\tlogopts = LOGERR_LOG | LOGERR_LOG_DATE | LOGERR_LOG_PID;\n\n\t/* Ensure we have stdin, stdout and stderr file descriptors.\n\t * This is important as we do run scripts which expect these. */\n\tif (fcntl(STDIN_FILENO, F_GETFD) == -1)\n\t\tdup_null(STDIN_FILENO);\n\tif (fcntl(STDOUT_FILENO, F_GETFD) == -1)\n\t\tdup_null(STDOUT_FILENO);\n\tif (fcntl(STDERR_FILENO, F_GETFD) == -1)\n\t\tdup_null(STDERR_FILENO);\n\telse\n\t\tlogopts |= LOGERR_ERR;\n\n\ti = 0;\n\n\twhile (\n\t    (opt = getopt_long(argc, argv,\n\t\t ctx.options & DHCPCD_PRINT_PIDFILE ? NOERR_IF_OPTS : IF_OPTS,\n\t\t cf_options, &oi)) != -1) {\n\t\tswitch (opt) {\n\t\tcase '4':\n\t\t\tfamily = AF_INET;\n\t\t\tbreak;\n\t\tcase '6':\n\t\t\tfamily = AF_INET6;\n\t\t\tbreak;\n\t\tcase 'f':\n\t\t\tctx.cffile = optarg;\n\t\t\tbreak;\n\t\tcase 'j':\n\t\t\tfree(ctx.logfile);\n\t\t\tctx.logfile = strdup(optarg);\n\t\t\tbreak;\n#ifdef USE_SIGNALS\n\t\tcase 'k':\n\t\t\tsig = SIGALRM;\n\t\t\tsiga = \"ALRM\";\n\t\t\tbreak;\n\t\tcase 'n':\n\t\t\tsig = SIGHUP;\n\t\t\tsiga = \"HUP\";\n\t\t\tbreak;\n\t\tcase 'q':\n\t\t\t/* -qq disables console output entirely.\n\t\t\t * This is important for systemd because it logs\n\t\t\t * both console AND syslog to the same log\n\t\t\t * resulting in untold confusion. */\n\t\t\tif (logopts & LOGERR_QUIET)\n\t\t\t\tlogopts &= ~LOGERR_ERR;\n\t\t\telse\n\t\t\t\tlogopts |= LOGERR_QUIET;\n\t\t\tbreak;\n\t\tcase 'x':\n\t\t\tsig = SIGTERM;\n\t\t\tsiga = \"TERM\";\n\t\t\tbreak;\n\t\tcase 'N':\n\t\t\tsig = SIGUSR1;\n\t\t\tsiga = \"USR1\";\n\t\t\tbreak;\n#endif\n\t\tcase 'P':\n\t\t\tctx.options |= DHCPCD_PRINT_PIDFILE;\n\t\t\tlogopts &= ~(LOGERR_LOG | LOGERR_ERR);\n\t\t\tbreak;\n\t\tcase 'T':\n\t\t\ti = 1;\n\t\t\tlogopts &= ~LOGERR_LOG;\n\t\t\tbreak;\n\t\tcase 'U':\n\t\t\ti = 3;\n\t\t\tbreak;\n\t\tcase 'V':\n\t\t\ti = 2;\n\t\t\tbreak;\n\t\tcase '?':\n\t\t\tif (ctx.options & DHCPCD_PRINT_PIDFILE)\n\t\t\t\tcontinue;\n\t\t\tusage();\n\t\t\tgoto exit_failure;\n\t\t}\n\t}\n\n\tif (optind != argc - 1)\n\t\tctx.options |= DHCPCD_MANAGER;\n\n\tlogsetopts(logopts);\n\tlogopen(ctx.logfile);\n\n\tctx.argv = argv;\n\tctx.argc = argc;\n\tctx.ifc = argc - optind;\n\tctx.ifv = argv + optind;\n\n\trt_init(&ctx);\n\n\tifo = read_config(&ctx, NULL, NULL, NULL);\n\tif (ifo == NULL) {\n\t\tif (ctx.options & DHCPCD_PRINT_PIDFILE)\n\t\t\tgoto printpidfile;\n\t\tgoto exit_failure;\n\t}\n\n\topt = add_options(&ctx, NULL, ifo, argc, argv);\n\tif (opt != 1) {\n\t\tif (ctx.options & DHCPCD_PRINT_PIDFILE)\n\t\t\tgoto printpidfile;\n\t\tif (opt == 0)\n\t\t\tusage();\n\t\tgoto exit_failure;\n\t}\n\tif (i == 2) {\n\t\tprintf(\"Interface options:\\n\");\n\t\tif (optind == argc - 1) {\n\t\t\tfree_options(&ctx, ifo);\n\t\t\tifo = read_config(&ctx, argv[optind], NULL, NULL);\n\t\t\tif (ifo == NULL)\n\t\t\t\tgoto exit_failure;\n\t\t\tadd_options(&ctx, NULL, ifo, argc, argv);\n\t\t}\n\t\tif_printoptions();\n#ifdef INET\n\t\tif (family == 0 || family == AF_INET) {\n\t\t\tprintf(\"\\nDHCPv4 options:\\n\");\n\t\t\tdhcp_printoptions(&ctx, ifo->dhcp_override,\n\t\t\t    ifo->dhcp_override_len);\n\t\t}\n#endif\n#ifdef INET6\n\t\tif (family == 0 || family == AF_INET6) {\n\t\t\tprintf(\"\\nND options:\\n\");\n\t\t\tipv6nd_printoptions(&ctx, ifo->nd_override,\n\t\t\t    ifo->nd_override_len);\n#ifdef DHCP6\n\t\t\tprintf(\"\\nDHCPv6 options:\\n\");\n\t\t\tdhcp6_printoptions(&ctx, ifo->dhcp6_override,\n\t\t\t    ifo->dhcp6_override_len);\n#endif\n\t\t}\n#endif\n\t\tgoto exit_success;\n\t}\n\tctx.options |= ifo->options;\n\n\tif (i == 1 || i == 3) {\n\t\tif (i == 1)\n\t\t\tctx.options |= DHCPCD_TEST;\n\t\telse\n\t\t\tctx.options |= DHCPCD_DUMPLEASE;\n\t\tctx.options |= DHCPCD_PERSISTENT;\n\t\tctx.options &= ~DHCPCD_DAEMONISE;\n\t}\n\n#ifdef THERE_IS_NO_FORK\n\tctx.options &= ~DHCPCD_DAEMONISE;\n#endif\n\n\tif (!(ctx.options & (DHCPCD_TEST | DHCPCD_DUMPLEASE))) {\n\tprintpidfile:\n\t\t/* If we have any other args, we should run as a single dhcpcd\n\t\t *  instance for that interface. */\n\t\tif (optind == argc - 1 && !(ctx.options & DHCPCD_MANAGER)) {\n\t\t\tconst char *per;\n\t\t\tconst char *ifname;\n\n\t\t\tifname = *ctx.ifv;\n\t\t\tif (ifname == NULL || strlen(ifname) > IF_NAMESIZE) {\n\t\t\t\terrno = ifname == NULL ? EINVAL : E2BIG;\n\t\t\t\tlogerr(\"%s: \", ifname);\n\t\t\t\tgoto exit_failure;\n\t\t\t}\n\t\t\t/* Allow a dhcpcd interface per address family */\n\t\t\tswitch (family) {\n\t\t\tcase AF_INET:\n\t\t\t\tper = \"-4\";\n\t\t\t\tbreak;\n\t\t\tcase AF_INET6:\n\t\t\t\tper = \"-6\";\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tper = \"\";\n\t\t\t}\n\t\t\tsnprintf(ctx.pidfile, sizeof(ctx.pidfile), PIDFILE,\n\t\t\t    ifname, per, \".\");\n\t\t} else {\n\t\t\tsnprintf(ctx.pidfile, sizeof(ctx.pidfile), PIDFILE, \"\",\n\t\t\t    \"\", \"\");\n\t\t\tctx.options |= DHCPCD_MANAGER;\n\n\t\t\t/*\n\t\t\t * If we are given any interfaces or a family, we\n\t\t\t * cannot send a signal as that would impact\n\t\t\t * other interfaces.\n\t\t\t */\n\t\t\tif (optind != argc || family != AF_UNSPEC)\n\t\t\t\tsig = 0;\n\t\t}\n\t\tif (ctx.options & DHCPCD_PRINT_PIDFILE) {\n\t\t\tprintf(\"%s\\n\", ctx.pidfile);\n\t\t\tgoto exit_success;\n\t\t}\n\t}\n\n\tif (chdir(\"/\") == -1)\n\t\tlogerr(\"%s: chdir: /\", __func__);\n\n\t/* Freeing allocated addresses from dumping leases can trigger\n\t * eloop removals as well, so init here. */\n\tif ((ctx.eloop = eloop_new()) == NULL) {\n\t\tlogerr(\"%s: eloop_init\", __func__);\n\t\tgoto exit_failure;\n\t}\n\n#ifdef USE_SIGNALS\n\tfor (si = 0; si < dhcpcd_signals_ignore_len; si++)\n\t\tsignal(dhcpcd_signals_ignore[si], SIG_IGN);\n\n\t/* Save signal mask, block and redirect signals to our handler */\n\tif (eloop_signal_set_cb(ctx.eloop, dhcpcd_signals, dhcpcd_signals_len,\n\t\tdhcpcd_signal_cb, &ctx) == -1) {\n\t\tlogerr(\"%s: eloop_signal_set_cb\", __func__);\n\t\tgoto exit_failure;\n\t}\n\tif (eloop_signal_mask(ctx.eloop) == -1) {\n\t\tlogerr(\"%s: eloop_signal_mask\", __func__);\n\t\tgoto exit_failure;\n\t}\n\n\tif (sig != 0) {\n\t\tpid = pidfile_read(ctx.pidfile);\n\t\tif (pid != 0 && pid != -1)\n\t\t\tloginfox(\"sending signal %s to pid %d\", siga, (int)pid);\n\t\tif (pid == 0 || pid == -1 || kill(pid, sig) != 0) {\n\t\t\tif (pid != 0 && pid != -1 && errno != ESRCH) {\n\t\t\t\tlogerr(\"kill\");\n\t\t\t\tgoto exit_failure;\n\t\t\t}\n\t\t\tunlink(ctx.pidfile);\n\t\t\t/* We can still continue and send the command\n\t\t\t * via the control socket. */\n\t\t} else {\n\t\t\tif (sig == SIGHUP || sig == SIGUSR1)\n\t\t\t\tgoto exit_success;\n\t\t\t/* Spin until it exits */\n\t\t\tloginfox(\"waiting for pid %d to exit\", (int)pid);\n\t\t\tdhcpcd_pidfile_timeout(&ctx);\n\t\t\teloop_timeout_add_sec(ctx.eloop, 50,\n\t\t\t    dhcpcd_exit_timeout, &ctx);\n\t\t\tgoto run_loop;\n\t\t}\n\t}\n#endif\n\n#ifdef HAVE_OPENSSL\n\tOPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS |\n\t\tOPENSSL_INIT_ADD_ALL_DIGESTS | OPENSSL_INIT_LOAD_CONFIG,\n\t    NULL);\n#endif\n\n#ifdef PRIVSEP\n\tps_init(&ctx);\n#endif\n\n#ifndef SMALL\n\tif (ctx.options & DHCPCD_DUMPLEASE && ctx.ifc == 1 &&\n\t    ctx.ifv[0][0] == '-' && ctx.ifv[0][1] == '\\0') {\n\t\tctx.options |= DHCPCD_FORKED; /* pretend child process */\n#ifdef PRIVSEP\n\t\tif (IN_PRIVSEP(&ctx) && ps_managersandbox(&ctx, NULL) == -1)\n\t\t\tgoto exit_failure;\n#endif\n\t\tifp = calloc(1, sizeof(*ifp));\n\t\tif (ifp == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\tgoto exit_failure;\n\t\t}\n\t\tifp->ctx = &ctx;\n\t\tifp->options = ifo;\n\t\tswitch (family) {\n\t\tcase AF_INET:\n#ifdef INET\n\t\t\tif (dhcp_dump(ifp) == -1)\n\t\t\t\tgoto exit_failure;\n\t\t\tbreak;\n#else\n\t\t\tlogerrx(\"No DHCP support\");\n\t\t\tgoto exit_failure;\n#endif\n\t\tcase AF_INET6:\n#ifdef DHCP6\n\t\t\tif (dhcp6_dump(ifp) == -1)\n\t\t\t\tgoto exit_failure;\n\t\t\tbreak;\n#else\n\t\t\tlogerrx(\"No DHCP6 support\");\n\t\t\tgoto exit_failure;\n#endif\n\t\tdefault:\n\t\t\tlogerrx(\"Family not specified. Please use -4 or -6.\");\n\t\t\tgoto exit_failure;\n\t\t}\n\t\tgoto exit_success;\n\t}\n#endif\n\n\t/* Try and contact the manager process to send the instruction. */\n\tif (!(ctx.options & DHCPCD_TEST)) {\n\t\tctx.options |= DHCPCD_FORKED; /* avoid socket unlink */\n\t\tif (!(ctx.options & DHCPCD_MANAGER))\n\t\t\tctx.control_fd = control_open(argv[optind], family,\n\t\t\t    ctx.options & DHCPCD_DUMPLEASE);\n\t\tif (!(ctx.options & DHCPCD_MANAGER) && ctx.control_fd == -1)\n\t\t\tctx.control_fd = control_open(argv[optind], AF_UNSPEC,\n\t\t\t    ctx.options & DHCPCD_DUMPLEASE);\n\t\tif (ctx.control_fd == -1)\n\t\t\tctx.control_fd = control_open(NULL, AF_UNSPEC,\n\t\t\t    ctx.options & DHCPCD_DUMPLEASE);\n\t\tif (ctx.control_fd != -1) {\n#ifdef PRIVSEP\n\t\t\tif (IN_PRIVSEP(&ctx) &&\n\t\t\t    ps_managersandbox(&ctx, NULL) == -1)\n\t\t\t\tgoto exit_failure;\n#endif\n\t\t\tif (!(ctx.options & DHCPCD_DUMPLEASE))\n\t\t\t\tloginfox(\"sending commands to dhcpcd process\");\n\t\t\tlen = control_send(&ctx, argc, argv);\n\t\t\tif (len > 0)\n\t\t\t\tlogdebugx(\"send OK\");\n\t\t\telse {\n\t\t\t\tlogerr(\"%s: control_send\", __func__);\n\t\t\t\tgoto exit_failure;\n\t\t\t}\n\t\t\tif (ctx.options & DHCPCD_DUMPLEASE) {\n\t\t\t\tif (dhcpcd_readdump(&ctx) == -1) {\n\t\t\t\t\tlogerr(\"%s: dhcpcd_readdump\", __func__);\n\t\t\t\t\tgoto exit_failure;\n\t\t\t\t}\n\t\t\t\tgoto run_loop;\n\t\t\t}\n\t\t\tgoto exit_success;\n\t\t} else {\n\t\t\tif (errno != ENOENT)\n\t\t\t\tlogerr(\"%s: control_open\", __func__);\n\t\t\t/* If asking dhcpcd to exit and we failed to\n\t\t\t * send a signal or a message then we\n\t\t\t * don't proceed past here. */\n\t\t\tif (ctx.options & DHCPCD_DUMPLEASE || sig == SIGTERM ||\n\t\t\t    sig == SIGALRM) {\n\t\t\t\tif (errno == ENOENT)\n\t\t\t\t\tlogerrx(PACKAGE \" is not running\");\n\t\t\t\tgoto exit_failure;\n\t\t\t}\n\t\t\tif (errno == EPERM || errno == EACCES)\n\t\t\t\tgoto exit_failure;\n\t\t}\n\t\tctx.options &= ~DHCPCD_FORKED;\n\t}\n\n\tif (!(ctx.options & DHCPCD_TEST)) {\n\t\t/* Ensure we have the needed directories */\n\t\tif (mkdir(DBDIR, 0750) == -1 && errno != EEXIST)\n\t\t\tlogerr(\"%s: mkdir: %s\", __func__, DBDIR);\n\t\tif (mkdir(RUNDIR, 0755) == -1 && errno != EEXIST)\n\t\t\tlogerr(\"%s: mkdir: %s\", __func__, RUNDIR);\n\t\tif ((pid = pidfile_lock(ctx.pidfile)) != 0) {\n\t\t\tif (pid == -1)\n\t\t\t\tlogerr(\"%s: pidfile_lock: %s\", __func__,\n\t\t\t\t    ctx.pidfile);\n\t\t\telse\n\t\t\t\tlogerrx(PACKAGE\n\t\t\t\t    \" already running on pid %d (%s)\",\n\t\t\t\t    (int)pid, ctx.pidfile);\n\t\t\tgoto exit_failure;\n\t\t}\n\t}\n\n\tloginfox(PACKAGE \"-\" VERSION \" starting\");\n\n\t// We don't need stdin past this point\n\tdup_null(STDIN_FILENO);\n\n#if defined(USE_SIGNALS) && !defined(THERE_IS_NO_FORK)\n\tif (!(ctx.options & DHCPCD_DAEMONISE))\n\t\tgoto start_manager;\n\n\tif (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fork_fd) ==\n\t    -1) {\n\t\tlogerr(\"socketpair\");\n\t\tgoto exit_failure;\n\t}\n\n\tswitch (pid = fork()) {\n\tcase -1:\n\t\tlogerr(\"fork\");\n\t\tgoto exit_failure;\n\tcase 0:\n\t\tctx.fork_fd = fork_fd[1];\n\t\tclose(fork_fd[0]);\n#ifdef PRIVSEP_RIGHTS\n\t\tif (ps_rights_limit_fd(ctx.fork_fd) == -1) {\n\t\t\tlogerr(\"ps_rights_limit_fdpair\");\n\t\t\tgoto exit_failure;\n\t\t}\n#endif\n\t\tif (setsid() == -1) {\n\t\t\tlogerr(\"%s: setsid\", __func__);\n\t\t\tgoto exit_failure;\n\t\t}\n\t\t/* Ensure we can never get a controlling terminal */\n\t\tpid = fork();\n\t\tif (pid == -1) {\n\t\t\tlogerr(\"fork\");\n\t\t\tgoto exit_failure;\n\t\t}\n\t\t/* setsid again to ensure our child processes have the\n\t\t * correct ppid */\n\t\tif (pid == 0 && setsid() == -1) {\n\t\t\tlogerr(\"%s: setsid\", __func__);\n\t\t\tgoto exit_failure;\n\t\t}\n\t\tif (eloop_forked(ctx.eloop, ELF_KEEP_ALL) == -1) {\n\t\t\tlogerr(\"%s: eloop_forked\", __func__);\n\t\t\tgoto exit_failure;\n\t\t}\n\t\tif (eloop_event_add(ctx.eloop, ctx.fork_fd, ELE_READ,\n\t\t\tdhcpcd_fork_cb, &ctx) == -1) {\n\t\t\tlogerr(\"%s: eloop_event_add\", __func__);\n\t\t\tgoto exit_failure;\n\t\t}\n\t\tif (pid != 0) {\n\t\t\tctx.options |= DHCPCD_FORKED; /* A lie */\n\t\t\ti = EXIT_SUCCESS;\n\t\t\tgoto exit1;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tsetproctitle(\"[launcher]\");\n\t\tctx.options |= DHCPCD_FORKED | DHCPCD_LAUNCHER;\n\t\tctx.fork_fd = fork_fd[0];\n\t\tclose(fork_fd[1]);\n#ifdef PRIVSEP_RIGHTS\n\t\tif (ps_rights_limit_fd(ctx.fork_fd) == -1) {\n\t\t\tlogerr(\"ps_rights_limit_fd\");\n\t\t\tgoto exit_failure;\n\t\t}\n#endif\n\t\tif (eloop_event_add(ctx.eloop, ctx.fork_fd, ELE_READ,\n\t\t\tdhcpcd_fork_cb, &ctx) == -1)\n\t\t\tlogerr(\"%s: eloop_event_add\", __func__);\n\n#ifdef PRIVSEP\n\t\tif (IN_PRIVSEP(&ctx) && ps_managersandbox(&ctx, NULL) == -1)\n\t\t\tgoto exit_failure;\n#endif\n\t\tgoto run_loop;\n\t}\n\n#ifdef DEBUG_FD\n\tloginfox(\"forkfd %d\", ctx.fork_fd);\n#endif\n\n\t/* We have now forked, setsid, forked once more.\n\t * From this point on, we are the controlling daemon. */\n\tlogdebugx(\"spawned manager process on PID %d\", (int)getpid());\n\nstart_manager:\n\tctx.options |= DHCPCD_STARTED;\n\tif ((pid = pidfile_lock(ctx.pidfile)) != 0) {\n\t\tlogerr(\"%s: pidfile_lock %d\", __func__, (int)pid);\n#ifdef PRIVSEP\n\t\t/* privsep has not started ... */\n\t\tctx.options &= ~DHCPCD_PRIVSEP;\n#endif\n\t\tgoto exit_failure;\n\t}\n#endif\n\n\tos_init();\n\n#if defined(BSD) && defined(INET6)\n\t/* Disable the kernel RTADV sysctl as early as possible. */\n\tif (ctx.options & DHCPCD_IPV6 && ctx.options & DHCPCD_IPV6RS)\n\t\tif_disable_rtadv();\n#endif\n\n#ifdef PRIVSEP\n\tif (IN_PRIVSEP(&ctx) && ps_start(&ctx) == -1) {\n\t\tlogerr(\"ps_start\");\n\t\tgoto exit_failure;\n\t}\n\tif (ctx.options & DHCPCD_FORKED)\n\t\tgoto run_loop;\n#endif\n\n\tif (!(ctx.options & DHCPCD_TEST)) {\n\t\tif (control_start(&ctx,\n\t\t\tctx.options & DHCPCD_MANAGER ? NULL : argv[optind],\n\t\t\tfamily) == -1) {\n\t\t\tlogerr(\"%s: control_start\", __func__);\n\t\t\tgoto exit_failure;\n\t\t}\n\t}\n\n#ifdef PLUGIN_DEV\n\t/* Start any dev listening plugin which may want to\n\t * change the interface name provided by the kernel */\n\tif (!IN_PRIVSEP(&ctx) &&\n\t    (ctx.options & (DHCPCD_MANAGER | DHCPCD_DEV)) ==\n\t\t(DHCPCD_MANAGER | DHCPCD_DEV))\n\t\tdev_start(&ctx, dhcpcd_handleinterface);\n#endif\n\n\tsetproctitle(\"%s%s%s\",\n\t    ctx.options & DHCPCD_MANAGER ? \"[manager]\" : argv[optind],\n\t    ctx.options & DHCPCD_IPV4 ? \" [ip4]\" : \"\",\n\t    ctx.options & DHCPCD_IPV6 ? \" [ip6]\" : \"\");\n\n\tif (if_opensockets(&ctx) == -1) {\n\t\tlogerr(\"%s: if_opensockets\", __func__);\n\t\tgoto exit_failure;\n\t}\n#ifndef SMALL\n\tdhcpcd_setlinkrcvbuf(&ctx);\n#endif\n\n\t/* Try and create DUID from the machine UUID. */\n\tdhcpcd_initduid(&ctx, NULL);\n\n\t/* Cache the default vendor option. */\n\tif (dhcp_vendor(ctx.vendor, sizeof(ctx.vendor)) == -1)\n\t\tlogerr(\"dhcp_vendor\");\n\n\t/* Start handling kernel messages for interfaces, addresses and\n\t * routes. */\n\tif (eloop_event_add(ctx.eloop, ctx.link_fd, ELE_READ, dhcpcd_handlelink,\n\t\t&ctx) == -1)\n\t\tlogerr(\"%s: eloop_event_add\", __func__);\n\n#ifdef PRIVSEP\n\tif (IN_PRIVSEP(&ctx) && ps_managersandbox(&ctx, \"stdio route\") == -1)\n\t\tgoto exit_failure;\n#endif\n\n\t/* When running dhcpcd against a single interface, we need to retain\n\t * the old behaviour of waiting for an IP address */\n\tif (ctx.ifc == 1 && !(ctx.options & DHCPCD_BACKGROUND))\n\t\tctx.options |= DHCPCD_WAITIP;\n\n\tctx.ifaces = if_discover(&ctx, &ifaddrs, ctx.ifc, ctx.ifv);\n\tif (ctx.ifaces == NULL) {\n\t\tlogerr(\"%s: if_discover\", __func__);\n\t\tgoto exit_failure;\n\t}\n\tfor (i = 0; i < ctx.ifc; i++) {\n\t\tif ((ifp = if_find(ctx.ifaces, ctx.ifv[i])) == NULL)\n\t\t\tlogerrx(\"%s: interface not found\", ctx.ifv[i]);\n\t\telse if (!ifp->active)\n\t\t\tlogerrx(\"%s: interface has an invalid configuration\",\n\t\t\t    ctx.ifv[i]);\n\t}\n\tTAILQ_FOREACH(ifp, ctx.ifaces, next) {\n\t\tif (ifp->active == IF_ACTIVE_USER)\n\t\t\tbreak;\n\t}\n\n\tif (ifp == NULL) {\n\t\tif (ctx.ifc == 0) {\n\t\t\tint loglevel;\n\n\t\t\tloglevel = ctx.options & DHCPCD_INACTIVE ? LOG_DEBUG :\n\t\t\t\t\t\t\t\t   LOG_ERR;\n\t\t\tlogmessage(loglevel, \"no valid interfaces found\");\n\t\t\tdhcpcd_daemonise(&ctx);\n\t\t} else\n\t\t\tgoto exit_failure;\n\t\tif (!(ctx.options & DHCPCD_LINK)) {\n\t\t\tlogerrx(\"aborting as link detection is disabled\");\n\t\t\tgoto exit_failure;\n\t\t}\n\t}\n\n\tTAILQ_FOREACH(ifp, ctx.ifaces, next) {\n\t\tif (ifp->active)\n\t\t\tdhcpcd_initstate1(ifp, argc, argv, 0);\n\t}\n\tif_learnaddrs(&ctx, ctx.ifaces, &ifaddrs);\n\tif_freeifaddrs(&ctx, &ifaddrs);\n\tifaddrs = NULL;\n\n\tif (ctx.options & DHCPCD_BACKGROUND)\n\t\tdhcpcd_daemonise(&ctx);\n\n\topt = 0;\n\tTAILQ_FOREACH(ifp, ctx.ifaces, next) {\n\t\tif (ifp->active) {\n\t\t\trun_preinit(ifp);\n\t\t\tif (if_is_link_up(ifp))\n\t\t\t\topt = 1;\n\t\t}\n\t}\n\n\tif (!(ctx.options & DHCPCD_BACKGROUND)) {\n\t\tif (ctx.options & DHCPCD_MANAGER)\n\t\t\tt = ifo->timeout;\n\t\telse {\n\t\t\tt = 0;\n\t\t\tTAILQ_FOREACH(ifp, ctx.ifaces, next) {\n\t\t\t\tif (ifp->active) {\n\t\t\t\t\tt = ifp->options->timeout;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (opt == 0 && ctx.options & DHCPCD_LINK &&\n\t\t    !(ctx.options & DHCPCD_WAITIP)) {\n\t\t\tint loglevel;\n\n\t\t\tloglevel = ctx.options & DHCPCD_INACTIVE ? LOG_DEBUG :\n\t\t\t\t\t\t\t\t   LOG_WARNING;\n\t\t\tlogmessage(loglevel, \"no interfaces have a carrier\");\n\t\t\tdhcpcd_daemonise(&ctx);\n\t\t} else if (t > 0 &&\n\t\t    /* Test mode removes the daemonise bit, so check for both */\n\t\t    ctx.options & (DHCPCD_DAEMONISE | DHCPCD_TEST)) {\n\t\t\teloop_timeout_add_sec(ctx.eloop, t, handle_exit_timeout,\n\t\t\t    &ctx);\n\t\t}\n\t}\n\tfree_options(&ctx, ifo);\n\tifo = NULL;\n\n\tTAILQ_FOREACH(ifp, ctx.ifaces, next) {\n\t\tif (ifp->active)\n\t\t\teloop_timeout_add_sec(ctx.eloop, 0,\n\t\t\t    dhcpcd_prestartinterface, ifp);\n\t}\n\nrun_loop:\n\ti = eloop_start(ctx.eloop);\n\tif (i < 0) {\n\t\tlogerr(\"%s: eloop_start\", __func__);\n\t\tgoto exit_failure;\n\t}\n\tgoto exit1;\n\nexit_success:\n\ti = EXIT_SUCCESS;\n\tgoto exit1;\n\nexit_failure:\n\ti = EXIT_FAILURE;\n\nexit1:\n\tif (!(ctx.options & DHCPCD_TEST) && control_stop(&ctx) == -1)\n\t\tlogerr(\"%s: control_stop\", __func__);\n\tif_freeifaddrs(&ctx, &ifaddrs);\n#ifdef PRIVSEP\n\tps_stop(&ctx);\n#endif\n\t/* Free memory and close fd's */\n\tif (ctx.ifaces) {\n\t\twhile ((ifp = TAILQ_FIRST(ctx.ifaces))) {\n\t\t\tTAILQ_REMOVE(ctx.ifaces, ifp, next);\n\t\t\tif_free(ifp);\n\t\t}\n\t\tfree(ctx.ifaces);\n\t\tctx.ifaces = NULL;\n\t}\n\tfree_options(&ctx, ifo);\n#ifdef HAVE_OPEN_MEMSTREAM\n\tif (ctx.script_fp)\n\t\tfclose(ctx.script_fp);\n#endif\n\tfree(ctx.script_buf);\n\tfree(ctx.script_env);\n\trt_dispose(&ctx);\n\tfree(ctx.duid);\n\tif_closesockets(&ctx);\n\tfree_globals(&ctx);\n#ifdef INET6\n\tipv6_ctxfree(&ctx);\n#endif\n#ifdef PLUGIN_DEV\n\tdev_stop(&ctx);\n#endif\n\tif (ctx.script != dhcpcd_default_script)\n\t\tfree(ctx.script);\n#ifdef PRIVSEP\n\tif (ps_stopwait(&ctx) != EXIT_SUCCESS)\n\t\ti = EXIT_FAILURE;\n#endif\n\tif (ctx.options & DHCPCD_STARTED && !(ctx.options & DHCPCD_FORKED))\n\t\tloginfox(PACKAGE \" exited\");\n#ifdef PRIVSEP\n\tif (ps_root_stop(&ctx) == -1)\n\t\ti = EXIT_FAILURE;\n#endif\n\n#ifdef USE_SIGNALS\n\t/* If still attached, detach from the launcher */\n\tif (ctx.options & DHCPCD_STARTED && ctx.fork_fd != -1) {\n\t\tif (send(ctx.fork_fd, &i, sizeof(i), 0) == -1)\n\t\t\tlogerr(\"%s: send\", __func__);\n\t}\n#endif\n\n\teloop_free(ctx.eloop);\n\tlogclose();\n\tfree(ctx.logfile);\n\tfflush(stdout);\n\tfree(ctx.ctl_buf);\n#ifdef SETPROCTITLE_H\n\tsetproctitle_fini();\n#endif\n\n#ifdef USE_SIGNALS\n\tif (ctx.options & (DHCPCD_FORKED | DHCPCD_PRIVSEP))\n\t\t_exit(i); /* so atexit won't remove our pidfile */\n#endif\n\treturn i;\n}\n"
  },
  {
    "path": "src/dhcpcd.conf",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2017-2025 Roy Marples <roy@marples.name>\n\n# A sample configuration for dhcpcd.\n# See dhcpcd.conf(5) for details.\n\n# Allow users of this group to interact with dhcpcd via the control socket.\n#controlgroup wheel\n\n# Inform the DHCP server of our hostname for DDNS.\n#hostname\n\n# Use the hardware address of the interface for the Client ID.\n#clientid\n# or\n# Use the same DUID + IAID as set in DHCPv6 for DHCPv4 ClientID as per RFC4361.\n# Some non-RFC compliant DHCP servers do not reply with this set.\n# In this case, comment out duid and enable clientid above.\nduid\n\n# Persist interface configuration when dhcpcd exits.\npersistent\n\n# vendorclassid is set to blank to avoid sending the default of\n# dhcpcd-<version>:<os>:<machine>:<platform>\nvendorclassid\n\n# A list of options to request from the DHCP server.\noption domain_name_servers, domain_name, domain_search\noption static_routes, classless_static_routes\n# Respect the network MTU. This is applied to DHCP routes.\noption interface_mtu\n\n# Request a hostname from the network\noption host_name\n\n# Most distributions have NTP support.\n#option ntp_servers\n\n# A ServerID is required by RFC2131.\nrequire dhcp_server_identifier\n\n# Generate SLAAC address using the Hardware Address of the interface\n#slaac hwaddr\n# OR generate Stable Private IPv6 Addresses based from the DUID\nslaac private\n"
  },
  {
    "path": "src/dhcpcd.conf.5.in",
    "content": ".\\\" SPDX-License-Identifier: BSD-2-Clause\n.\\\" Copyright (c) 2006-2025 Roy Marples\n.\\\" All rights reserved\n.\\\"\n.\\\" Redistribution and use in source and binary forms, with or without\n.\\\" modification, are permitted provided that the following conditions\n.\\\" are met:\n.\\\" 1. Redistributions of source code must retain the above copyright\n.\\\"    notice, this list of conditions and the following disclaimer.\n.\\\" 2. Redistributions in binary form must reproduce the above copyright\n.\\\"    notice, this list of conditions and the following disclaimer in the\n.\\\"    documentation and/or other materials provided with the distribution.\n.\\\"\n.\\\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n.\\\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n.\\\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n.\\\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n.\\\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n.\\\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n.\\\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n.\\\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n.\\\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n.\\\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n.\\\" SUCH DAMAGE.\n.\\\"\n.Dd June 12, 2025\n.Dt DHCPCD.CONF 5\n.Os\n.Sh NAME\n.Nm dhcpcd.conf\n.Nd dhcpcd configuration file\n.Sh DESCRIPTION\nAlthough\n.Nm dhcpcd\ncan do everything from the command line, there are cases where it's just easier\nto do it once in a configuration file.\nMost of the options found in\n.Xr dhcpcd 8\ncan be used here.\nThe first word on the line is the option and the rest of the line is the value.\nLeading and trailing whitespace for the option and value are trimmed.\nYou can escape characters in the value using the \\\\ character.\nComments can be prefixed with the # character.\nString values should be quoted with the \" character.\n.Pp\nHere's a list of available options:\n.Bl -tag -width indent\n.It Ic allowinterfaces Ar pattern\nWhen discovering interfaces, the interface name must match\n.Ar pattern\nwhich is a space or comma separated list of patterns passed to\n.Xr fnmatch 3 .\nIf the same interface is matched in\n.Ic denyinterfaces\nthen it is still denied.\n.It Ic denyinterfaces Ar pattern\nWhen discovering interfaces, the interface name must not match\n.Ar pattern\nwhich is a space or comma separated list of patterns passed to\n.Xr fnmatch 3 .\n.It Ic anonymous\nEnables Anonymity Profiles for DHCP, RFC 7844.\nAny DUID is ignored and ClientID is set to LL only.\nAll non essential options are then masked at this point,\nbut they could be unmasked by explicitly requesting the option\n.Sy after\nthe\n.Ic anonymous\noption is processed.\nAs such, the\n.Ic anonymous\noption\n.Sy should\nbe the last option in the configuration unless you really want to\nsend something which could identify you.\n.Nm dhcpcd\nwill not try and reboot an old lease, it will go straight into\nDISCOVER/SOLICIT.\n.It Ic randomise_hwaddr\nForces a hardware address randomisation when the interface is brought up\nor when the carrier is lost.\nThis is generally used in tandem with the anonymous option.\n.It Ic arping Ar address Op address\n.Nm dhcpcd\nwill arping each address in order before attempting DHCP.\nIf an address is found, we will select the replying hardware address as the\nprofile, otherwise the IP address.\nExample:\n.Pp\n.D1 interface bge0\n.D1 arping 192.168.0.1\n.Pp\n.D1 # My specific 192.168.0.1 network\n.D1 profile dd:ee:aa:dd:bb:ee\n.D1 static ip_address=192.168.0.10/24\n.Pp\n.D1 # A generic 192.168.0.1 network\n.D1 profile 192.168.0.1\n.D1 static ip_address=192.168.0.98/24\n.It Ic authprotocol Ar protocol Op Ar algorithm Op Ar rdm\nAuthenticate DHCP messages.\nSee the Supported Authentication Protocols section.\nIf\n.Ar protocol\nis\n.Ar token\nthen\n.Ar algorithm is\nsnd_secretid/rcv_secretid so you can send and receive different tokens.\n.It Ic authtoken Ar secretid Ar realm Ar expire Ar key\nDefine a shared key for use in authentication.\n.Ar realm\ncan be \"\" to for use with the\n.Ar delayed\nprotocol.\n.Ar expire\nis the date the token expires and should be formatted \"yyy-mm-dd HH:MM\".\nYou can use the keyword\n.Ar forever\nor\n.Ar 0\nwhich means the token never expires.\nFor the token protocol,\n.Ar secretid\nneeds to be 0 and\n.Ar realm\nneeds to be \"\".\nIf\n.Nm dhcpcd\nhas the error\n.D1 dhcp_auth_encode: Invalid argument\nthen it means that\n.Nm dhcpcd\ncould not find the correct authentication token in your configuration.\n.It Ic background\nFork to the background immediately.\nThis is useful for startup scripts which don't disable link messages for\ncarrier status.\n.It Ic blacklist Ar address Ns Op /cidr\nIgnores all packets from\n.Ar address Ns Op /cidr .\n.It Ic whitelist Ar address Ns Op /cidr\nOnly accept packets from\n.Ar address Ns Op /cidr .\n.Ic blacklist\nis ignored if\n.Ic whitelist\nis set.\n.It Ic bootp\nBe a BOOTP client.\nBasically, this just doesn't send a DHCP Message Type option and will only\ninteract with a BOOTP server.\nAll other DHCP options still work.\n.It Ic broadcast\nInstructs the DHCP server to broadcast replies back to the client.\nNormally this is only set for non-Ethernet interfaces,\nsuch as FireWire and InfiniBand.\nIn most cases,\n.Nm dhcpcd\nwill set this automatically.\n.It Ic controlgroup Ar group\nSets the group ownership of\n.Pa @RUNDIR@/sock\nso that users other than root can connect to\n.Nm dhcpcd .\n.It Ic debug\nEcho debug messages to the stderr and syslog.\n.It Ic dev Ar value\nLoad the\n.Ar value\n.Pa /dev\nmanagement module.\n.Nm dhcpcd\nwill load the first one found to work, if any.\n.It Ic env Ar value\nPush\n.Ar value\nto the environment for use in\n.Xr dhcpcd-run-hooks 8 .\nFor example, you can force the hostname hook to always set the hostname with\n.Ic env\n.Va force_hostname=YES .\nOr set which driver\n.Xr wpa_supplicant 8\nshould use with\n.Ic env\n.Va wpa_supplicant_driver=nl80211\n.Pp\nIf the hostname is set, it will be will set to the FQDN if possible as per\nRFC 4702, section 3.1.\nIf the FQDN option is missing,\n.Nm dhcpcd\nwill still try and set a FQDN from the hostname and domain options for\nconsistency.\nTo override this, set\n.Ic env\n.Va hostname_fqdn=[YES|NO|SERVER] .\nA value of\n.Va SERVER\nmeans just what the server says, don't manipulate it.\nThis could lead to an inconsistent hostname on a DHCPv4 and DHCPv6 network\nwhere the DHCPv4 hostname is short and the DHCPv6 has an FQDN.\nDHCPv6 has no hostname option.\n.It Ic clientid Ar string\nSend the\n.Ar clientid .\nIf the string is of the format 01:02:03 then it is encoded as hex.\nFor interfaces whose hardware address is longer than 8 bytes, or if the\n.Ar clientid\nis an empty string then\n.Nm dhcpcd\nsends a default\n.Ar clientid\nof the hardware family and the hardware address.\n.It Ic duid Op ll | lt | uuid | value\nUse a DHCP Unique Identifier.\nIf persistent storage is available then a DUID-LLT\n(link local address + time) is generated,\notherwise DUID-LL is generated (link local address).\nThe DUID type can be hinted as an optional parameter if the file\n.Pa @DBDIR@/duid\ndoes not exist.\nIf not\n.Va ll ,\n.Va lt\nor\n.Va uuid\nthen\n.Va value\nwill be converted from 00:11:22:33 format.\nThis, plus the IAID will be used as the\n.Ic clientid .\nThe DUID generated will be held in\n.Pa @DBDIR@/duid\nand should not be copied to other hosts.\nThis file also takes precedence over the above rules except for setting a value.\n.It Ic iaid Ar iaid\nSet the Interface Association Identifier to\n.Ar iaid .\nThis option must be used in an\n.Ic interface\nblock.\nThis defaults to the VLANID (prefixed with 0xff) for the interface if set,\notherwise the last 4 bytes of the hardware address assigned to the\ninterface.\nEach instance of this should be unique within the scope of the client and\n.Nm dhcpcd\nwarns if a conflict is detected.\nIf there is a conflict, it is only a problem if the conflicted IAIDs are\nused on the same network.\n.It Ic dhcp\nEnable DHCP on the interface, on by default.\n.It Ic dhcp6\nEnable DHCPv6 on the interface, on by default.\n.It Ic ipv4\nEnable IPv4 on the interface, on by default.\n.It Ic ipv6\nEnable IPv6 on the interface, on by default.\n.It Ic request Op Ar address\nRequest the\n.Ar address\nin the DHCP DISCOVER message.\nThere is no guarantee this is the address the DHCP server will actually give.\nIf no\n.Ar address\nis given then the first address currently assigned to the\n.Ar interface\nis used.\n.It Ic inform Op Ar address Ns Op Ar /cidr Ns Op Ar /broadcast_address\nBehaves like\n.Ic request\nas above, but sends a DHCP INFORM instead of DISCOVER/REQUEST.\nThis does not get a lease as such, just notifies the DHCP server of the\n.Ar address\nin use.\nYou should also include the optional\n.Ar cidr\nnetwork number in case the address is not already configured on the interface.\n.Nm dhcpcd\nremains running and pretends it has an infinite lease.\n.Nm dhcpcd\nwill not de-configure the interface when it exits.\nIf\n.Nm dhcpcd\nfails to contact a DHCP server then it returns a failure instead of falling\nback on IPv4LL.\n.It Ic inform6\nPerforms a DHCPv6 Information Request.\nNo address is requested or specified, but all other DHCPv6 options are allowed.\nThis is normally performed automatically when an IPv6 Router Advertisement\nindicates that the client should perform this operation.\nThis option is only needed when\n.Nm dhcpcd\nis not processing IPv6 RA messages and the need for a DHCPv6 Information Request\nexists.\n.It Ic persistent\n.Nm dhcpcd\nnormally de-configures the interface and configuration when it exits.\nSometimes, this isn't desirable if, for example, you have root mounted over\nNFS or SSH clients connect to this host and they need to be notified of\nthe host shutting down.\nYou can use this option to stop this from happening.\n.It Ic fallback Ar profile\nFall back to using this profile if DHCP fails.\nThis allows you to configure a static profile instead of using ZeroConf.\n.It Ic fallback_time Ar seconds\nStart fallback after\n.Ar seconds .\nThe default is 5 seconds.\n.It Ic hostname Ar name\nSends the hostname\n.Ar name\nto the DHCP server so it can be registered in DNS.\nIf\n.Ar name\nis an empty string then the current system hostname is sent.\nIf\n.Ar name\nis a FQDN (i.e., contains a .) then it will be encoded as such.\n.It Ic hostname_short\nSends the short hostname to the DHCP server instead of the FQDN.\nThis is useful because DHCP servers will not register the FQDN in their\nDNS if the domain part does not match theirs.\n.Pp\nAlso, see the\n.Ic env\noption above to control how the hostname is set on the host.\n.It Ic ia_na Op Ar iaid Op / address\nRequest a DHCPv6 Normal Address for\n.Ar iaid .\n.Ar iaid\ndefaults to the\n.Ic iaid\noption as described above.\nYou can request more than one ia_na by specifying a unique\n.Ar iaid\nfor each one.\n.It Ic ia_ta Op Ar iaid\nRequest a DHCPv6 Temporary Address for\n.Ar iaid .\nYou can request more than one ia_ta by specifying a unique\n.Ar iaid\nfor each one.\n.It Ic ia_pd Op Ar iaid Oo / Ar prefix / Ar prefix_len Oc Op Ar interface Op / Ar sla_id Op / Ar prefix_len Op / Ar suffix\nRequest a DHCPv6 Delegated Prefix for\n.Ar iaid .\nThis option must be used in an\n.Ic interface\nblock.\nUnless a\n.Ar sla_id\nof 0 is assigned with the same resultant prefix length as the delegation,\na reject route is installed for the Delegated Prefix to\nstop unallocated addresses being resolved upstream.\nIf no\n.Ar interface\nis given then we will assign a prefix to every other interface with a\n.Ar sla_id\nequivalent to the interface index assigned by the OS.\nOtherwise addresses are only assigned for each\n.Ar interface\nand\n.Ar sla_id .\nTo avoid delegating to any interface, use - as the invalid interface name.\nEach assigned address will have a\n.Ar suffix ,\ndefaulting to 1.\nIf the\n.Ar suffix\nis 0 then a SLAAC address is assigned.\nYou cannot assign a prefix to the requesting interface unless the\nDHCPv6 server supports the\n.Li RFC 6603\nPrefix Exclude Option.\n.Nm dhcpcd\nhas to be running for all the interfaces it is delegating to.\nA default\n.Ar prefix_len\nof 64 is assumed, unless the maximum\n.Ar sla_id\ndoes not fit.\nIn this case\n.Ar prefix_len\nis increased to the highest multiple of 8 that can accommodate the\n.Ar sla_id .\n.Ar sla_id\nis an integer which must be unique inside the\n.Ar iaid\nand is added to the prefix which must fit inside\n.Ar prefix_len\nless the length of the delegated prefix.\nYou can specify multiple\n.Ar interface /\n.Ar sla_id /\n.Ar prefix_len\nper\n.Ic ia_pd ,\nspace separated.\nIPv6RS should be disabled globally when requesting a Prefix Delegation.\n.Pp\nIn the following example eth0 is the externally facing interface to be\nconfigured for both IPv4 and IPv6.\nThe DHCPv4 server will provide us with an IPv4 address and a default route.\nThe DHCPv6 server is going to provide us with an IPv6 address, a default\nroute and a /64 subnet to be delegated to the internal interface.\nThe eth1 interface will be automatically configured\nfor IPv6 using the first address (::1) from the delegated prefix.\nA second prefix is requested and assigned to two other interfaces.\n.Xr rtadvd 8\ncan be used with an empty configuration file on eth1, eth2 and eth3,\nto provide automatic\nIPv6 address configuration for the internal network.\n.Bd -literal\nnoipv6rs                 # disable routing solicitation\ndenyinterfaces eth2      # Don't touch eth2 at all\ninterface eth0\n  ipv6rs                 # enable routing solicitation for eth0\n  ia_na 1                # request an IPv6 address\n  ia_pd 2 eth1/0         # request a PD and assign it to eth1\n  ia_pd 3 eth2/1 eth3/2  # req a PD and assign it to eth2 and eth3\n  ia_pd 4 -              # request a PD but don't assign it\n.Ed\n.It Ic ipv4only\nOnly configure IPv4.\n.It Ic ipv6only\nOnly configure IPv6.\n.It Ic fqdn Op disable | none | ptr | both\n.Ar none\nwill not ask the DHCP server to update DNS.\n.Ar ptr\njust asks the DHCP server to update the PTR\nrecord of the host in DNS, whereas\n.Ar both\nalso updates the A record.\n.Ar disable\nwill disable the FQDN option.\nThe default is\n.Ar both .\n.Nm dhcpcd\nitself never does any DNS updates.\n.Nm dhcpcd\nencodes the FQDN hostname as specified in\n.Li RFC 1035 .\n.It Ic interface Ar interface\nSubsequent options are only parsed for this\n.Ar interface .\nPattern matching is allowed by\n.Xr fnmatch 3 .\n.It Ic ipv4ll_time Ar seconds\nWait for\n.Ar seconds\nbefore starting IPv4LL.\nThe default is 5 seconds.\n.It Ic ipv6ra_autoconf\nGenerate SLAAC addresses for each Prefix advertised by an IPv6\nRouter Advertisement message with the Auto flag set.\nOn by default.\n.It Ic ipv6ra_noautoconf\nDisables the above option.\n.It Ic ipv6ra_fork\nBy default, when\n.Nm dhcpcd\nreceives an IPv6 Router Advertisement,\n.Nm dhcpcd\nwill only fork to the background if the RA contains at least one unexpired\nRDNSS option and a valid prefix or no DHCPv6 instruction.\nSet this option so to make\n.Nm dhcpcd\nalways fork on a RA.\n.It Ic ipv6rs\nEnables IPv6 Router Advertisement solicitation.\nThis is on by default, but is documented here in the case where it is disabled\nglobally but needs to be enabled for one interface.\n.It Ic lastlease\nIf\n.Nm dhcpcd\ncannot obtain a lease, then try to use the last lease acquired for the\ninterface.\n.It Ic lastleaseextend\nSame as the above, but the lease will be retained even if it expires.\n.Nm dhcpcd\nwill give it up if any other host tries to claim it for their own via ARP.\nThis violates RFC 2131, section 3.7, which states the lease should be\ndropped once it has expired.\n.It Ic leasetime Ar seconds\nRequest DHCP a lease time of\n.Ar seconds .\n.Ar -1\nrepresents an infinite lease time.\nBy default\n.Nm dhcpcd\ndoes not request any lease time and leaves it in the hands of the\nDHCP server.\nIt is not possible to request a DHCPv6 lease time as this is not RFC compliant.\nSee RFC 8415 21.4, 21.6, 21.21 and 21.22.\n.It Ic link_rcvbuf Ar size\nOverride the size of the link receive buffer from the kernel default.\nWhile\n.Nm dhcpcd\nwill recover from link buffer overflows,\nthis may not be desirable on heavily loaded systems.\n.It Ic logfile Ar logfile\nWrites to the specified\n.Ar logfile .\n.Nm dhcpcd\nstill writes to\n.Xr syslog 3 .\nThe\n.Ar logfile\nis reopened when\n.Nm dhcpcd\nreceives the\n.Dv SIGUSR2\nsignal.\n.It Ic metric Ar metric\nMetrics are used to prefer an interface over another one, lowest wins.\n.Nm dhcpcd\nwill supply a default metric of 1000 +\n.Xr if_nametoindex 3 .\nThis will be offset by 2000 for wireless interfaces, with additional offsets\nof 1000000 for IPv4LL and 2000000 for roaming interfaces.\n.It Ic mudurl Ar url\nSpecifies the URL for a Manufacturer Usage Description (MUD).\nThe description is used by upstream network devices to instantiate any\ndesired access lists.\nSee draft-ietf-opsawg-mud for more information.\n.It Ic noalias\nAny pre-existing IPv4 addresses will be removed from the interface when\nadding a new IPv4 address.\n.It Ic noarp\nDon't send any ARP requests.\nThis also disables IPv4LL.\n.It Ic arp_persistdefence\nKeep the IP address even if defence fails upon IP Address conflict.\n.It Ic noauthrequired\nDon't require authentication even though we requested it.\nAlso allows FORCERENEW and RECONFIGURE messages without authentication.\n.It Ic nodelay\nDon't delay for an initial randomised time when starting protocols.\n.It Ic nodev\nDon't load\n.Pa /dev\nmanagement modules.\n.It Ic nodhcp\nDon't start DHCP or listen to DHCP messages.\nThis is only useful when allowing IPv4LL.\n.It Ic nodhcp6\nDon't start DHCPv6 or listen to DHCPv6 messages.\nNormally DHCPv6 is started by an IPv6 Router Advertisement instruction or\nconfiguration.\n.It Ic nogateway\nDon't install any default routes.\n.It Ic gateway\nInstall a default route if available (default).\n.It Ic nohook Ar script\nDon't run this hook script.\nMatches full name, or prefixed with 2 numbers optionally ending with\n.Pa .sh .\n.Pp\nSo to stop\n.Nm dhcpcd\nfrom touching your DNS settings or starting wpa_supplicant you would do:-\n.D1 nohook resolv.conf, wpa_supplicant\n.It Ic noipv4\nDon't attempt to configure an IPv4 address.\n.It Ic noipv4ll\nDon't attempt to obtain an IPv4LL address if we failed to get one via DHCP.\nSee\n.Rs\n.%T \"RFC 3927\"\n.Re\n.It Ic noipv6\nDon't solicit or accept IPv6 Router Advertisements and DHCPv6.\n.It Ic noipv6rs\nDon't solicit or accept IPv6 Router Advertisements.\n.It Ic nolink\nDon't receive link messages about carrier status.\nYou should only set this for buggy interface drivers.\n.It Ic nosyslog\nDisable writing to syslog(3).\n.It Ic noup\nDon't bring the interface up when in manager mode.\n.It Ic option Ar option\nRequests the\n.Ar option\nfrom the server.\nIt can be a variable to be used in\n.Xr dhcpcd-run-hooks 8\nor the numerical value.\nYou can specify more\n.Ar option Ns s\nseparated by commas, spaces or more\n.Ic option\nlines.\nPrepend dhcp6_ to\n.Ar option\nto request a DHCPv6 option.\nIf no DHCPv6 options are configured,\nthen DHCPv4 options are mapped to equivalent DHCPv6 options.\n.Pp\nPrepend nd_ to\n.Ar option\nto handle ND options, but this only works for the\n.Ic nooption ,\n.Ic reject\nand\n.Ic require\noptions.\n.Pp\nTo see a list of options you can use, call\n.Nm dhcpcd\nwith the\n.Fl V , Fl Fl variables\nargument.\n.It Ic nooption Ar option\nRemove the option from the message before it's processed.\n.It Ic require Ar option\nRequires the\n.Ar option\nto be present in all messages, otherwise the message is ignored.\nTo enforce that\n.Nm dhcpcd\nonly responds to DHCP servers and not BOOTP servers, you can\n.Ic require\n.Ar dhcp_message_type .\nThis isn't an exact science though because a BOOTP server can send DHCP-like\noptions.\n.It Ic reject Ar option\nReject a message that contains the\n.Ar option .\nThis is useful when you cannot use\n.Ic require\nto select / de-select BOOTP messages.\n.It Ic destination Ar option\nIf\n.Nm\ndetects an address added to a point to point interface (PPP, TUN, etc) then\nit will set the listed DHCP options to the destination address of the\ninterface.\n.It Ic profile Ar name\nSubsequent options are only parsed for this profile\n.Ar name .\n.It Ic quiet\nSuppress any dhcpcd output to the console, except for errors.\n.It Ic reboot Ar seconds\nAllow\n.Ar reboot\nseconds before moving to the DISCOVER phase if we have an old lease to use.\nAllow\n.Ar reboot\nseconds before starting fallback states from the DISCOVER phase.\nIPv4LL is started when the first\n.Ar reboot\ntimeout is reached.\nThe default is 5 seconds.\nA setting of 0 seconds causes\n.Nm\nto skip the reboot phase and go straight into DISCOVER.\nThis is desirable for mobile users because if you change from network A to\nnetwork B and they use the same subnet and the address from network A isn't\nin use on network B, then the DHCP server will remain silent even if\nauthoritative which means\n.Nm dhcpcd\nwill timeout before moving back to the DISCOVER phase.\nThis has no effect on DHCPv6 other than skipping the reboot phase.\n.It Ic release\n.Nm dhcpcd\nwill release the lease prior to stopping the interface.\n.It Ic script Ar script\nUse\n.Ar script\ninstead of the default\n.Pa @SCRIPT@ .\n.It Ic request_time Ar seconds\nRequest the lease for\n.Ar seconds\nbefore going back to DISCOVER.\nThe default is 180 seconds.\n.It Ic ssid Ar ssid\nSubsequent options are only parsed for this wireless\n.Ar ssid .\n.It Ic slaac Ic hwaddr | Ic private | Ic token Ar token Op Ic temp | Ic temporary\nSelects the interface identifier used for SLAAC generated IPv6 addresses.\nIf\n.Ic private\nis used, a RFC 7217 address is generated.\nIf\n.Ic token Ar token\nis used then the token is combined with the prefix to make the final address.\nThe\n.Ic temporary\ndirective will create a temporary address for the prefix as well.\n.It Ic static Ar value\nConfigures a static\n.Ar value .\nIf you set\n.Ic ip_address\nthen\n.Nm dhcpcd\nwill not attempt to obtain a lease and will just use the value for the address\nwith an infinite lease time.\nIf you set an empty value this removes all prior static allocations to\nthe same value.\nThis is useful when using profiles and in the case of\n.Ic ip_address\nit will remove the static allocation.\nNote that setting 0.0.0.0 keeps the static allocation but waits for a 3rdparty\nto configure the address.\nIf you set\n.Ic ip6_address ,\n.Nm dhcpcd\nwill continue auto-configuration as normal.\n.Pp\nHere is an example which configures two static address, overriding the default\nIPv4 broadcast address, an IPv4 router, DNS and disables IPv6 auto-configuration.\nYou could also use the\n.Ic inform6\ncommand here if you wished to obtain more information via DHCPv6.\nFor IPv4, you should use the\n.Ic inform Ar ipaddress\noption instead of setting a static address.\n.D1 interface eth0\n.D1 noipv6rs\n.D1 static ip_address=192.168.0.10/24\n.D1 static broadcast_address=192.168.0.63\n.D1 static ip6_address=fd51:42f8:caae:d92e::ff/64\n.D1 static routers=192.168.0.1\n.D1 static domain_name_servers=192.168.0.1 fd51:42f8:caae:d92e::1\n.Pp\nHere is an example for PPP which gives the destination a default route.\nIt uses the special\n.Ar destination\nkeyword to insert the destination address\ninto the value.\n.D1 interface ppp0\n.D1 static ip_address=0.0.0.0\n.D1 destination routers\n.It Ic timeout Ar seconds\nTime out after\n.Ar seconds ,\ninstead of the default 30.\nA setting of 0\n.Ar seconds\ncauses\n.Nm dhcpcd\nto wait forever to get a lease.\nIf\n.Nm dhcpcd\nis working on a single interface then\n.Nm dhcpcd\nwill exit when a timeout occurs, otherwise\n.Nm dhcpcd\nwill fork into the background.\nIf using IPv4LL then\n.Nm dhcpcd\nstart the IPv4LL process after the timeout and then wait a little longer\nbefore really timing out.\n.It Ic userclass Ar string\nTag the DHCPv4 message with the userclass.\nYou can specify more than one.\n.It Ic msuserclass Ar string\nTag the DHCPv4 mesasge with the Microsoft userclass.\nUnlike the\n.Ic userclass\noption, this one can only be added once.\nIt should only be used for Microsoft DHCP servers and the\n.Ic vendorclassid\nshould be set to \"MSFT 98\" or \"MSFT 5.0\".\nThis option is not RFC compliant.\n.It Ic vendor Ar code , Ns Ar value\nAdd an encapsulated vendor-specific information option (DHCP Option 43).\n.Ar code\nshould be between 1 and 254 inclusive.\nTo add a raw vendor string, omit\n.Ar code\nbut keep the comma.\nExamples.\n.Pp\nSet the vendor option 01 with an IP address.\n.D1 vendor 01,192.168.0.2\nSet the vendor option 02 with a hex code.\n.D1 vendor 02,01:02:03:04:05\nSet the vendor option 03 with an IP address as a string.\n.D1 vendor 03,\\e\"192.168.0.2\\e\"\nSet un-encapsulated vendor option to hello world.\n.D1 vendor ,\"hello world\"\n.It Ic vsio Ar en Ar code, Ns Ar value\nAdd an encapsulated vendor-specific information option (DHCP Option 125) with\nIANA assigned Enterprise Number\n.Ar en\nproceeding with the\n.Ar code\nwhich should be between 1 and 255 inclusive, and the\n.Ar value\nafter the comma.\nExamples:\n.Pp\nSet the vsio for enterprise number 155 option 01 with an IPv4 address.\n.D1 vsio 155 01,192.168.1.1\nSet the vsio for enterprise number 155 option 02 with a string.\n.D1 vsio 155 02,\"hello world\"\nSet the vsio for enterprise number 255 option 01 with a hex code.\n.D1 vsio 255 01,01:02:03:04:05\n.It Ic vsio6 Ar en Ar code, Ns Ar value\nAdd an encapsulated vendor-specific information option (DHCPv6 Option 17) with\nIANA assigned Enterprise Number\n.Ar en\nproceeding with the\n.Ar code\nwhich should be between 1 and 65535 inclusive, and the\n.Ar value\nafter the comma.\nExamples:\n.Pp\nSet the vsio for enterprise number 155 option 01 with an IPv6 address.\n.D1 vsio6 155 01,2001:0db8:85a3:0000:0000:8a2e:0370:7334\nSet the vsio for enterprise number 155 option 02 with a string.\n.D1 vsio6 155 02,\"hello world\"\nSet the vsio for enterprise number 255 option 01 with a hex code.\n.D1 vsio6 255 01,01:02:03:04:05\n.It Ic vendorclassid Ar string\nSet the DHCP Vendor Class.\nDHCPv6 has its own option as shown below.\nThe default is\ndhcpcd-<version>:<os>:<machine>:<platform>.\nFor example\n.D1 dhcpcd-5.5.6:NetBSD-6.99.5:i386:i386\nIf not set then none is sent.\nSome badly configured DHCP servers reject unknown vendorclassids.\nTo work around it, try and impersonate Windows by using the MSFT vendorclassid.\n.It Ic vendclass Ar en Ar data\nAdd the DHCPv6 Vendor Indetifying Vendor Class with the IANA assigned Enterprise\nNumber\n.Ar en\nwith the\n.Ar data .\nThis option can be set more than once to add more data, but the behaviour,\nas per RFC 3925 is undefined if the Enterprise Number differs.\n.It Ic waitip Op 4 | 6\nWait for an address to be assigned before forking to the background.\n4 means wait for an IPv4 address to be assigned.\n6 means wait for an IPv6 address to be assigned.\nIf no argument is given,\n.Nm\nwill wait for any address protocol to be assigned.\nIt is possible to wait for more than one address protocol and\n.Nm\nwill only fork to the background when all waiting conditions are satisfied.\n.It Ic xidhwaddr\nUse the last four bytes of the hardware address as the DHCP xid instead\nof a randomly generated number.\n.El\n.Ss Defining new options\nDHCP, ND and DHCPv6 allow for the use of custom options, and RFC 3925 vendor\noptions for DHCP can also be supplied.\nEach option needs to be started with the\n.Ic define ,\n.Ic definend ,\n.Ic define6\nor\n.Ic vendopt\ndirective.\nThis can optionally be followed by both\n.Ic embed\nor\n.Ic encap\noptions.\nBoth can be specified more than once and\n.Ic embed\nmust come before\n.Ic encap .\n.Bl -tag -width indent\n.It Ic define Ar code Ar type Ar variable\nDefines the DHCP option\n.Ar code\nof\n.Ar type\nwith a name of\n.Ar variable\nexported to\n.Xr dhcpcd-run-hooks 8 .\n.It Ic definend Ar code Ar type Ar variable\nDefines the ND option\n.Ar code\nof\n.Ar type\nwith a name of\n.Ar variable\nexported to\n.Xr dhcpcd-run-hooks 8 ,\nwith a prefix of\n.Va nd_ .\n.It Ic define6 Ar code Ar type Ar variable\nDefines the DHCPv6 option\n.Ar code\nof\n.Ar type\nwith a name of\n.Ar variable\nexported to\n.Xr dhcpcd-run-hooks 8 ,\nwith a prefix of\n.Va dhcp6_ .\n.It Ic vendopt Ar code Ar type Ar variable\nDefines the Vendor-Identifying Vendor Options.\nThe\n.Ar code\nis the IANA Enterprise Number which will uniquely describe the encapsulated\noptions.\n.Ar type\nis normally\n.Ar encap .\n.Ar variable\nnames the Vendor option to be exported.\n.It Ic embed Ar type Ar variable\nDefines an embedded variable within the defined option.\nThe length is determined by the\n.Ar type .\nIf the\n.Ar variable\nis not the same as defined in the parent option,\nit is prefixed with the parent\n.Ar variable\nfirst with an underscore.\nIf the\n.Ar variable\nhas the name of\n.Ar reserved\nthen it is not processed.\n.It Ic encap Ar code Ar type Ar variable\nDefines an encapsulated variable within the defined option.\nThe length is determined by the\n.Ar type .\nIf the\n.Ar variable\nis not the same as defined in the parent option,\nit is prefixed with the parent\n.Ar variable\nfirst with an underscore.\n.El\n.Ss Type prefix\nThese keywords come before the type itself, to describe it more fully.\nYou can use more than one, but they must appear in the order listed below.\n.Bl -tag -width -indent\n.It Ic request\nRequests the option by default without having to be specified in user\nconfiguration.\n.It Ic norequest\nThis option cannot be requested, regardless of user configuration.\n.It Ic optional\nThis option is optional.\nOnly makes sense for embedded options like the client FQDN option, where\nthe FQDN string itself is optional.\n.It Ic index\nThe option can appear more than once and will be indexed.\n.It Ic array\nThe option data is split into a space separated array, each element being\nthe same type.\n.It Ic truncated\nThe option might truncated from its normal length.\nThe end of the normal size is zero padded on expansion.\nCurrently this is only supported for ip6address where it's a prefix.\n.El\n.Ss Types to define\nThe type directly affects the length of data consumed inside the option.\nAny remaining data is normally discarded.\nLengths can be specified for string and binhex types, but this is generally\nwith other data embedded afterwards in the same option.\n.Bl -tag -width indent\n.It Ic ipaddress\nAn IPv4 address, 4 bytes.\n.It Ic ip6address\nAn IPv6 address, 16 bytes.\n.It Ic string Op : Ic length\nA NVT ASCII string of printable characters.\n.It Ic byte\nA byte.\n.It Ic bitflags : Ic flags\nA byte represented as a string of flags, most significant bit first.\nFor example, using ABCDEFGH then A would equal 10000000, B 01000000,\nC 00100000, etc.\nIf the bit is not set, the flag is not printed.\nA flag of 0 is not printed even if the bit position is set.\nThis is to allow reservation of the first bits while assigning the last bits.\nA flag of 1 prints the bit set or unset.\n.It Ic int16\nA signed 16bit integer, 2 bytes.\n.It Ic uint16\nAn unsigned 16bit integer, 2 bytes.\n.It Ic int32\nA signed 32bit integer, 4 bytes.\n.It Ic uint32\nAn unsigned 32bit integer, 4 bytes.\n.It Ic flag\nA fixed value (1) to indicate that the option is present, 0 bytes.\n.It Ic domain\nAn RFC 3397 encoded string.\n.It Ic dname\nAn RFC 1035 validated string.\n.It Ic uri\nIf an array then the first two bytes are the URI length inside the option data.\nOtherwise, the whole option data is the URI.\nAs a space is not allowed in the URI encoding, the URIs are space separated.\n.It Ic binhex Op : Ic length\nBinary data expressed as hexadecimal.\n.It Ic embed\nContains embedded options (implies encap as well).\n.It Ic encap\nContains encapsulated options (implies embed as well).\n.It Ic option\nReferences an option from the global definition.\n.El\n.Ss Example definition\n.D1 # DHCP option 81, Fully Qualified Domain Name, RFC 4702\n.D1 define 81 embed fqdn\n.D1 embed byte flags\n.D1 embed byte rcode1\n.D1 embed byte rcode2\n.D1 embed domain fqdn\n.Pp\n.D1 # DHCP option 125, Vendor Specific Information Option, RFC 3925\n.D1 define 125 encap vsio\n.D1 embed uint32 enterprise_number\n.D1 # Options defined for the enterprise number\n.D1 encap 1 ipaddress ipaddress\n.Ss Supported Authentication Protocols\n.Bl -tag -width -indent\n.It Ic token\nSends a plain text token the server expects and matches a token sent by\nthe server.\nThe tokens do not have to be the same.\nIf unspecified, the token with a\n.Ar secretid\nof 0 will be used in sending messages\nand validating received messages.\n.It Ic delayedrealm\nDelayed Authentication.\n.Nm dhcpcd\nwill send an authentication option with no key or MAC.\nThe server will see this option, and select a key for\n.Nm , writing the\n.Ar realm\nand\n.Ar secretid\nin it.\n.Nm dhcpcd\nwill then look for an unexpired token with a matching\n.Ar realm\nand\n.Ar secretid .\nThis token is used to authenticate all other messages.\n.It Ic delayed\nSame as above, but without a realm.\n.El\n.Ss Supported Authentication Algorithms\nIf none specified,\n.Ic hmac-md5\nis the default.\n.Bl -tag -width -indent\n.It Ic hmac-md5\n.El\n.Ss Supported Replay Detection Mechanisms\nIf none specified,\n.Ic monotonic\nis the default.\nIf this is changed from what was previously used,\nor the means of calculating or storing it is broken, then the DHCP server\nwill probably have to have its notion of the client's Replay Detection Value\nreset.\n.Bl -tag -width -indent\n.It Ic monocounter\nRead the number in the file\n.Pa @DBDIR@/dhcpcd-rdm.monotonic\nand add one to it.\n.It Ic monotime\nCreate an NTP timestamp from the system time.\n.It Ic monotonic\nSame as\n.Ic monotime .\n.El\n.Sh SEE ALSO\n.Xr fnmatch 3 ,\n.Xr if_nametoindex 3 ,\n.Xr dhcpcd 8 ,\n.Xr dhcpcd-run-hooks 8\n.Sh AUTHORS\n.An Roy Marples Aq Mt roy@marples.name\n.Sh BUGS\nPlease report them to\n.Lk https://roy.marples.name/projects/dhcpcd\n"
  },
  {
    "path": "src/dhcpcd.h",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef DHCPCD_H\n#define DHCPCD_H\n\n#include <sys/socket.h>\n\n#include <net/if.h>\n\n#include <stdio.h>\n\n#include \"config.h\"\n#include \"control.h\"\n#include \"defs.h\"\n#include \"if-options.h\"\n#include \"queue.h\"\n\n#define HWADDR_LEN     20\n#define IF_SSIDLEN     32\n#define PROFILE_LEN    64\n#define SECRET_LEN     64\n\n#define IF_INACTIVE    0\n#define IF_ACTIVE      1\n#define IF_ACTIVE_USER 2\n\n#define LINK_UP\t       1\n#define LINK_UNKNOWN   0\n#define LINK_DOWN      -1\n\n#define IF_DATA_IPV4   0\n#define IF_DATA_ARP    1\n#define IF_DATA_IPV4LL 2\n#define IF_DATA_DHCP   3\n#define IF_DATA_IPV6   4\n#define IF_DATA_IPV6ND 5\n#define IF_DATA_DHCP6  6\n#define IF_DATA_MAX    7\n\n#ifdef __QNX__\n/* QNX carries defines for, but does not actually support PF_LINK */\n#undef IFLR_ACTIVE\n#endif\n\nstruct interface {\n\tstruct dhcpcd_ctx *ctx;\n\tTAILQ_ENTRY(interface) next;\n\tchar name[IF_NAMESIZE];\n\tunsigned int index;\n\tunsigned int active;\n\tunsigned int flags;\n\tuint16_t hwtype; /* ARPHRD_ETHER for example */\n\tunsigned char hwaddr[HWADDR_LEN];\n\tuint8_t hwlen;\n\tint mtu;\n\tunsigned short vlanid;\n\tunsigned int metric;\n\tint carrier;\n\tbool wireless;\n\tuint8_t ssid[IF_SSIDLEN];\n\tunsigned int ssid_len;\n\n\tint argc;\n\tchar **argv;\n\n\tchar profile[PROFILE_LEN];\n\tstruct if_options *options;\n\tvoid *if_data[IF_DATA_MAX];\n};\nTAILQ_HEAD(if_head, interface);\n\n#include \"privsep.h\"\n\n/* dhcpcd requires CMSG_SPACE to evaluate to a compile time constant. */\n#if defined(__QNX) || \\\n    (defined(__NetBSD_Version__) && __NetBSD_Version__ < 600000000)\n#undef CMSG_SPACE\n#endif\n\n#ifndef ALIGNBYTES\n#define ALIGNBYTES (sizeof(int) - 1)\n#endif\n#ifndef ALIGN\n#define ALIGN(p) (((unsigned int)(p) + ALIGNBYTES) & ~ALIGNBYTES)\n#endif\n#ifndef CMSG_SPACE\n#define CMSG_SPACE(len) (ALIGN(sizeof(struct cmsghdr)) + ALIGN(len))\n#endif\n\nstruct passwd;\n\nstruct dhcpcd_ctx {\n\tchar pidfile[sizeof(PIDFILE) + IF_NAMESIZE + 1];\n\tchar vendor[256];\n\tint fork_fd; /* FD for the fork init signal pipe */\n\tconst char *cffile;\n\tunsigned long long options;\n\tchar *logfile;\n\tint argc;\n\tchar **argv;\n\tint ifac;    /* allowed interfaces */\n\tchar **ifav; /* allowed interfaces */\n\tint ifdc;    /* denied interfaces */\n\tchar **ifdv; /* denied interfaces */\n\tint ifc;     /* listed interfaces */\n\tchar **ifv;  /* listed interfaces */\n\tint ifcc;    /* configured interfaces */\n\tchar **ifcv; /* configured interfaces */\n\tuint8_t duid_type;\n\tunsigned char *duid;\n\tsize_t duid_len;\n\tstruct if_head *ifaces;\n\n\tchar *ctl_buf;\n\tsize_t ctl_buflen;\n\tsize_t ctl_bufpos;\n\tsize_t ctl_extra;\n\n\trb_tree_t routes; /* our routes */\n#ifdef RT_FREE_ROUTE_TABLE\n\trb_tree_t froutes; /* free routes for re-use */\n#endif\n\tsize_t rt_order; /* route order storage */\n\n\tint pf_inet_fd;\n#ifdef PF_LINK\n\tint pf_link_fd;\n#endif\n\tvoid *priv;\n\tint link_fd;\n#ifndef SMALL\n\tint link_rcvbuf;\n#endif\n\tint seq;  /* route message sequence no */\n\tint sseq; /* successful seq no sent */\n\n\tstruct eloop *eloop;\n\n\tchar *script;\n#ifdef HAVE_OPEN_MEMSTREAM\n\tFILE *script_fp;\n#endif\n\tchar *script_buf;\n\tsize_t script_buflen;\n\tchar **script_env;\n\tsize_t script_envlen;\n\n\tint control_fd;\n\tint control_unpriv_fd;\n\tstruct fd_list_head control_fds;\n\tchar control_sock[sizeof(CONTROLSOCKET) + IF_NAMESIZE];\n\tchar control_sock_unpriv[sizeof(CONTROLSOCKET) + IF_NAMESIZE + 7];\n\tgid_t control_group;\n\n\t/* DHCP Enterprise options, RFC3925 */\n\tstruct dhcp_opt *vivso;\n\tsize_t vivso_len;\n\n\tchar *randomstate; /* original state */\n\n\t/* For filtering RTM_MISS messages per router */\n#ifdef BSD\n\tuint8_t *rt_missfilter;\n\tsize_t rt_missfilterlen;\n\tsize_t rt_missfiltersize;\n#endif\n\n#ifdef PRIVSEP\n\tstruct passwd *ps_user; /* struct passwd for privsep user */\n\tstruct ps_process_head ps_processes; /* List of spawned processes */\n\tstruct ps_process *ps_root;\n\tstruct ps_process *ps_inet;\n\tstruct ps_process *ps_ctl;\n\tint ps_data_fd;\t\t\t   /* data returned from processes */\n\tint ps_log_fd;\t\t\t   /* chroot logging */\n\tint ps_log_root_fd;\t\t   /* outside chroot log reader */\n\tstruct fd_list *ps_control;\t   /* Queue for the above */\n\tstruct fd_list *ps_control_client; /* Queue for the above */\n#endif\n\n#ifdef INET\n\tstruct dhcp_opt *dhcp_opts;\n\tsize_t dhcp_opts_len;\n\n\tint udp_rfd;\n\tint udp_wfd;\n\n\t/* Our aggregate option buffer.\n\t * We ONLY use this when options are split, which for most purposes is\n\t * practically never. See RFC3396 for details. */\n\tuint8_t *opt_buffer;\n\tsize_t opt_buffer_len;\n#endif\n#ifdef INET6\n\tuint8_t *secret;\n\tsize_t secret_len;\n\n#ifndef __sun\n\tint nd_fd;\n#endif\n\tstruct ra_head *ra_routers;\n\n\tstruct dhcp_opt *nd_opts;\n\tsize_t nd_opts_len;\n#ifdef DHCP6\n\tint dhcp6_rfd;\n\tint dhcp6_wfd;\n\tstruct dhcp_opt *dhcp6_opts;\n\tsize_t dhcp6_opts_len;\n#endif\n\n#ifndef __linux__\n\tint ra_global;\n#endif\n#endif /* INET6 */\n\n#ifdef PLUGIN_DEV\n\tchar *dev_load;\n\tint dev_fd;\n\tstruct dev *dev;\n\tvoid *dev_handle;\n#endif\n};\n\n#ifdef USE_SIGNALS\nextern const int dhcpcd_signals[];\nextern const size_t dhcpcd_signals_len;\nextern const int dhcpcd_signals_ignore[];\nextern const size_t dhcpcd_signals_ignore_len;\n#endif\n\nextern const char *dhcpcd_default_script;\n\nint dhcpcd_ifafwaiting(const struct interface *);\nint dhcpcd_afwaiting(const struct dhcpcd_ctx *);\nvoid dhcpcd_daemonised(struct dhcpcd_ctx *);\nvoid dhcpcd_daemonise(struct dhcpcd_ctx *);\n\nvoid dhcpcd_linkoverflow(struct dhcpcd_ctx *);\nint dhcpcd_handleargs(struct dhcpcd_ctx *, struct fd_list *, int, char **);\nvoid dhcpcd_handlecarrier(struct interface *, int, unsigned int);\nint dhcpcd_handleinterface(void *, int, const char *);\nvoid dhcpcd_handlehwaddr(struct interface *, uint16_t, const void *, uint8_t);\nvoid dhcpcd_dropinterface(struct interface *, const char *);\nvoid dhcpcd_dropped(struct interface *);\nint dhcpcd_selectprofile(struct interface *, const char *);\n\nvoid dhcpcd_startinterface(void *);\nvoid dhcpcd_activateinterface(struct interface *, unsigned long long);\n\n#endif\n"
  },
  {
    "path": "src/duid.c",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#define UUID_LEN\t36\n#define DUID_TIME_EPOCH 946684800\n\n#include <sys/types.h>\n#include <sys/param.h>\n#include <sys/socket.h>\n#ifdef BSD\n#include <sys/sysctl.h>\n#endif\n\n#include <net/if.h>\n#include <net/if_arp.h>\n\n#include <arpa/inet.h>\n#include <errno.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n#include <unistd.h>\n\n#include \"common.h\"\n#include \"dhcpcd.h\"\n#include \"duid.h\"\n#include \"logerr.h\"\n\n/*\n * Machine, system or product UUIDs are not guaranteed unique.\n * Let's not use them by default.\n */\n#ifdef USE_MACHINE_UUID\nstatic size_t\nduid_machineuuid(char *uuid, size_t uuid_len)\n{\n\tint r;\n\tsize_t len = uuid_len;\n\n#if defined(HW_UUID) /* OpenBSD */\n\tint mib[] = { CTL_HW, HW_UUID };\n\n\tr = sysctl(mib, sizeof(mib) / sizeof(mib[0]), uuid, &len, NULL, 0);\n#elif defined(KERN_HOSTUUID) /* FreeBSD */\n\tint mib[] = { CTL_KERN, KERN_HOSTUUID };\n\n\tr = sysctl(mib, sizeof(mib) / sizeof(mib[0]), uuid, &len, NULL, 0);\n#elif defined(__NetBSD__)\n\tr = sysctlbyname(\"machdep.dmi.system-uuid\", uuid, &len, NULL, 0);\n#elif defined(__linux__)\n\tFILE *fp;\n\n\tfp = fopen(\"/sys/class/dmi/id/product_uuid\", \"r\");\n\tif (fp == NULL)\n\t\treturn 0;\n\tif (fgets(uuid, (int)uuid_len, fp) == NULL) {\n\t\tfclose(fp);\n\t\treturn 0;\n\t}\n\tlen = strlen(uuid) + 1;\n\tfclose(fp);\n\tr = len == 1 ? -1 : 0;\n#else\n\tUNUSED(uuid);\n\tr = -1;\n\terrno = ENOSYS;\n#endif\n\n\tif (r == -1)\n\t\treturn 0;\n\treturn len;\n}\n\nstatic size_t\nduid_make_uuid(uint8_t *d)\n{\n\tuint16_t type = htons(DUID_UUID);\n\tchar uuid[UUID_LEN + 1];\n\tsize_t l;\n\n\tif (duid_machineuuid(uuid, sizeof(uuid)) != sizeof(uuid))\n\t\treturn 0;\n\n\t/* All zeros UUID is not valid */\n\tif (strcmp(\"00000000-0000-0000-0000-000000000000\", uuid) == 0)\n\t\treturn 0;\n\n\tmemcpy(d, &type, sizeof(type));\n\tl = sizeof(type);\n\td += sizeof(type);\n\tl += hwaddr_aton(d, uuid);\n\treturn l;\n}\n#endif\n\nsize_t\nduid_make(void *d, const struct interface *ifp, uint16_t type)\n{\n\tuint8_t *p;\n\tuint16_t u16;\n\ttime_t t;\n\tuint32_t u32;\n\n\tif (ifp->hwlen == 0)\n\t\treturn 0;\n\n\tp = d;\n\tu16 = htons(type);\n\tmemcpy(p, &u16, sizeof(u16));\n\tp += sizeof(u16);\n\tu16 = htons(ifp->hwtype);\n\tmemcpy(p, &u16, sizeof(u16));\n\tp += sizeof(u16);\n\tif (type == DUID_LLT) {\n\t\t/* time returns seconds from jan 1 1970, but DUID-LLT is\n\t\t * seconds from jan 1 2000 modulo 2^32 */\n\t\tt = time(NULL) - DUID_TIME_EPOCH;\n\t\tu32 = htonl((uint32_t)t & 0xffffffff);\n\t\tmemcpy(p, &u32, sizeof(u32));\n\t\tp += sizeof(u32);\n\t}\n\t/* Finally, add the MAC address of the interface */\n\tmemcpy(p, ifp->hwaddr, ifp->hwlen);\n\tp += ifp->hwlen;\n\treturn (size_t)(p - (uint8_t *)d);\n}\n\n#define DUID_STRLEN DUID_LEN * 3\nstatic size_t\nduid_get(struct dhcpcd_ctx *ctx, const struct interface *ifp)\n{\n\tuint8_t *data;\n\tsize_t len, slen;\n\tchar line[DUID_STRLEN];\n\tconst struct interface *ifp2;\n\n\t/* If we already have a DUID then use it as it's never supposed\n\t * to change once we have one even if the interfaces do */\n\tif ((len = dhcp_read_hwaddr_aton(ctx, &data, DUID)) != 0) {\n\t\tif (len <= DUID_LEN) {\n\t\t\tctx->duid = data;\n\t\t\treturn len;\n\t\t}\n\t\tlogerrx(\"DUID too big (max %u): %s\", DUID_LEN, DUID);\n\t\t/* Keep the buffer, will assign below. */\n\t} else {\n\t\tif (errno != ENOENT)\n\t\t\tlogerr(\"%s\", DUID);\n\t\tif ((data = malloc(DUID_LEN)) == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t/* No file? OK, lets make one based the machines UUID */\n\tif (ifp == NULL) {\n#ifdef USE_MACHINE_UUID\n\t\tif (ctx->duid_type != DUID_DEFAULT &&\n\t\t    ctx->duid_type != DUID_UUID)\n\t\t\tlen = 0;\n\t\telse\n\t\t\tlen = duid_make_uuid(data);\n\t\tif (len == 0)\n\t\t\tfree(data);\n\t\telse\n\t\t\tctx->duid = data;\n\t\treturn len;\n#else\n\t\tfree(data);\n\t\treturn 0;\n#endif\n\t}\n\n\t/* Regardless of what happens we will create a DUID to use. */\n\tctx->duid = data;\n\n\t/* No UUID? OK, lets make one based on our interface */\n\tif (ifp->hwlen == 0) {\n\t\tlogwarnx(\"%s: does not have hardware address\", ifp->name);\n\t\tTAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {\n\t\t\tif (ifp2->hwlen != 0)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (ifp2) {\n\t\t\tifp = ifp2;\n\t\t\tlogwarnx(\"picked interface %s to generate a DUID\",\n\t\t\t    ifp->name);\n\t\t} else {\n\t\t\tif (ctx->duid_type != DUID_LL)\n\t\t\t\tlogwarnx(\"no interfaces have a fixed hardware \"\n\t\t\t\t\t \"address\");\n\t\t\treturn duid_make(data, ifp, DUID_LL);\n\t\t}\n\t}\n\n\tlen = duid_make(data, ifp,\n\t    ctx->duid_type == DUID_LL ? DUID_LL : DUID_LLT);\n\thwaddr_ntoa(data, len, line, sizeof(line));\n\tslen = strlen(line);\n\tif (slen < sizeof(line) - 2) {\n\t\tline[slen++] = '\\n';\n\t\tline[slen] = '\\0';\n\t}\n\tif (dhcp_writefile(ctx, DUID, 0640, line, slen) == -1) {\n\t\tlogerr(\"%s: cannot write duid\", __func__);\n\t\tif (ctx->duid_type != DUID_LL)\n\t\t\treturn duid_make(data, ifp, DUID_LL);\n\t}\n\treturn len;\n}\n\nsize_t\nduid_init(struct dhcpcd_ctx *ctx, const struct interface *ifp)\n{\n\tif (ctx->duid == NULL)\n\t\tctx->duid_len = duid_get(ctx, ifp);\n\treturn ctx->duid_len;\n}\n"
  },
  {
    "path": "src/duid.h",
    "content": "/* dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef DUID_H\n#define DUID_H\n\n#define DUID_LEN     128 + 2\n#define DUID_DEFAULT 0\n#define DUID_LLT     1\n#define DUID_LL\t     3\n#define DUID_UUID    4\n\nsize_t duid_make(void *, const struct interface *, uint16_t);\nsize_t duid_init(struct dhcpcd_ctx *, const struct interface *);\n\n#endif\n"
  },
  {
    "path": "src/eloop.c",
    "content": "/*\n * eloop - portable event based main loop.\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2026 Roy Marples <roy@marples.name>\n * All rights reserved.\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#if (defined(__unix__) || defined(unix)) && !defined(USG)\n#include <sys/param.h>\n#endif\n#include <sys/time.h>\n\n/*\n * On BSD use kqueue(2)\n * On Linux use epoll(7)\n * Everywhere else use ppoll(2)\n */\n#if defined(BSD) || defined(__APPLE__)\n#include <sys/event.h>\n#define USE_KQUEUE\n#if defined(__NetBSD__)\n#define HAVE_KQUEUE1\n#define KEVENT_N size_t\n#else\n#define KEVENT_N int\n#endif\n#elif defined(__linux__)\n#include <sys/epoll.h>\n#define USE_EPOLL\n/* musl does not support epoll_wait2 and some distros\n * mismatch headers vs actual kernel so it's too problematic. */\n#else\n#define USE_PPOLL\n#endif\n\n#include <errno.h>\n#include <fcntl.h>\n#include <limits.h>\n#include <poll.h>\n#include <signal.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include \"eloop.h\"\n#include \"queue.h\"\n\n/*\n * Allow a backlog of signals.\n * If you use many eloops in the same process, they should all\n * use the same signal handler or have the signal handler unset.\n * Otherwise the signal might not behave as expected.\n */\n#define ELOOP_NSIGNALS 5\n\n/*\n * time_t is a signed integer of an unspecified size.\n * To adjust for time_t wrapping, we need to work the maximum signed\n * value and use that as a maximum.\n */\n#ifndef TIME_MAX\n#define TIME_MAX ((1ULL << (sizeof(time_t) * NBBY - 1)) - 1)\n#endif\n/* The unsigned maximum is then simple - multiply by two and add one. */\n#ifndef UTIME_MAX\n#define UTIME_MAX (TIME_MAX * 2) + 1\n#endif\n\n#ifndef UNUSED\n#define UNUSED(a) (void)(a)\n#endif\n\nstruct eloop_event {\n\tTAILQ_ENTRY(eloop_event) next;\n\tint fd;\n\tvoid (*cb)(void *, unsigned short);\n\tvoid *cb_arg;\n\tunsigned short events;\n#ifdef USE_PPOLL\n\tstruct pollfd *pollfd;\n#endif\n};\n\nstruct eloop_timeout {\n\tTAILQ_ENTRY(eloop_timeout) next;\n\tunsigned int seconds;\n\tunsigned int nseconds;\n\tvoid (*callback)(void *);\n\tvoid *arg;\n\tint queue;\n};\n\nstruct eloop {\n\tTAILQ_ENTRY(eloop) next;\n\tTAILQ_HEAD(event_head, eloop_event) events;\n\tsize_t nevents;\n\tstruct event_head free_events;\n\n\tstruct timespec now;\n\tTAILQ_HEAD(timeout_head, eloop_timeout) timeouts;\n\tstruct timeout_head free_timeouts;\n\n\tconst int *signals;\n\tsize_t nsignals;\n\tsigset_t sigset;\n\tvoid (*signal_cb)(int, void *);\n\tvoid *signal_cb_ctx;\n\n#if defined(USE_KQUEUE) || defined(USE_EPOLL)\n\tint fd;\n#endif\n#if defined(USE_KQUEUE)\n\tstruct kevent *fds;\n#elif defined(USE_EPOLL)\n\tstruct epoll_event *fds;\n#elif defined(USE_PPOLL)\n\tstruct pollfd *fds;\n#endif\n\tsize_t nfds;\n\n\tint exitcode;\n\tbool exitnow;\n\tbool events_need_setup;\n\tbool events_invalid;\n};\n\n#ifdef HAVE_REALLOCARRAY\n#define eloop_realloca reallocarray\n#else\n/* Handy routing to check for potential overflow.\n * reallocarray(3) and reallocarr(3) are not portable. */\n#define SQRT_SIZE_MAX (((size_t)1) << (sizeof(size_t) * CHAR_BIT / 2))\nstatic void *\neloop_realloca(void *ptr, size_t n, size_t size)\n{\n\tif ((n | size) >= SQRT_SIZE_MAX && n > SIZE_MAX / size) {\n\t\terrno = EOVERFLOW;\n\t\treturn NULL;\n\t}\n\treturn realloc(ptr, n * size);\n}\n#endif\n\nstatic int\neloop_event_setup_fds(struct eloop *eloop)\n{\n\tstruct eloop_event *e, *ne;\n#ifdef USE_PPOLL\n\tstruct pollfd *pfd;\n\n\tif (eloop->nevents > eloop->nfds) {\n\t\tpfd = eloop_realloca(eloop->fds, eloop->nevents, sizeof(*pfd));\n\t\tif (pfd == NULL)\n\t\t\treturn -1;\n\t\teloop->fds = pfd;\n\t\teloop->nfds = eloop->nevents;\n\t} else\n\t\tpfd = eloop->fds;\n#endif\n\n\tTAILQ_FOREACH_SAFE(e, &eloop->events, next, ne) {\n\t\tif (e->fd == -1) {\n\t\t\tTAILQ_REMOVE(&eloop->events, e, next);\n\t\t\tTAILQ_INSERT_TAIL(&eloop->free_events, e, next);\n\t\t\tcontinue;\n\t\t}\n#ifdef USE_PPOLL\n\t\te->pollfd = pfd;\n\t\tpfd->fd = e->fd;\n\t\tpfd->events = 0;\n\t\tif (e->events & ELE_READ)\n\t\t\tpfd->events |= POLLIN;\n\t\tif (e->events & ELE_WRITE)\n\t\t\tpfd->events |= POLLOUT;\n\t\tpfd->revents = 0;\n\t\tpfd++;\n#endif\n\t}\n\n\teloop->events_need_setup = false;\n\treturn 0;\n}\n\n#ifndef USE_PPOLL\nstatic int\neloop_grow_events(struct eloop *eloop)\n{\n#if defined(USE_KQUEUE)\n\tstruct kevent *pfd;\n#elif defined(USE_EPOLL)\n\tstruct epoll_event *pfd;\n#endif\n\tsize_t nfds = eloop->nfds == 0 ? 4 : eloop->nfds * 2;\n\n\tpfd = eloop_realloca(eloop->fds, nfds, sizeof(*pfd));\n\tif (pfd == NULL)\n\t\treturn -1;\n\teloop->fds = pfd;\n\teloop->nfds = nfds;\n\treturn 0;\n}\n#endif\n\nsize_t\neloop_event_count(const struct eloop *eloop)\n{\n\treturn eloop->nevents;\n}\n\n#if defined(USE_KQUEUE)\n\nstatic int\neloop_signal_kqueue(struct eloop *eloop, const int *signals, size_t nsignals)\n{\n\tunsigned int cmd = nsignals == 0 ? EV_DELETE : EV_ADD;\n\tstruct kevent *ke, *kep;\n\tsize_t i;\n\tint err;\n\n\tif (nsignals == 0) {\n\t\tsignals = eloop->signals;\n\t\tnsignals = eloop->nsignals;\n\t}\n\tif (nsignals == 0)\n\t\treturn 0;\n\n\tke = kep = eloop_realloca(NULL, nsignals, sizeof(*ke));\n\tif (ke == NULL)\n\t\treturn -1;\n\n\tfor (i = 0; i < nsignals; i++)\n\t\tEV_SET(kep++, (uintptr_t)signals[i], EVFILT_SIGNAL, cmd, 0, 0,\n\t\t    NULL);\n\n\terr = kevent(eloop->fd, ke, (KEVENT_N)nsignals, NULL, 0, NULL);\n\tfree(ke);\n\treturn err;\n}\n\nstatic int\neloop_event_kqueue(struct eloop *eloop, struct eloop_event *e,\n    unsigned short events)\n{\n#ifdef EVFILT_PROCDESC\n#define NKE 3\n#else\n#define NKE 2\n#endif\n\tstruct kevent ke[NKE], *kep = ke;\n\tint fd = e->fd;\n\n\tif (events & ELE_READ && !(e->events & ELE_READ))\n\t\tEV_SET(kep++, (uintptr_t)fd, EVFILT_READ, EV_ADD, 0, 0, e);\n\telse if (!(events & ELE_READ) && e->events & ELE_READ)\n\t\tEV_SET(kep++, (uintptr_t)fd, EVFILT_READ, EV_DELETE, 0, 0, e);\n\tif (events & ELE_WRITE && !(e->events & ELE_WRITE))\n\t\tEV_SET(kep++, (uintptr_t)fd, EVFILT_WRITE, EV_ADD, 0, 0, e);\n\telse if (!(events & ELE_WRITE) && e->events & ELE_WRITE)\n\t\tEV_SET(kep++, (uintptr_t)fd, EVFILT_WRITE, EV_DELETE, 0, 0, e);\n#ifdef EVFILT_PROCDESC\n\tif (events & ELE_HANGUP && !(e->events & ELE_HANGUP))\n\t\tEV_SET(kep++, (uintptr_t)fd, EVFILT_PROCDESC, EV_ADD, NOTE_EXIT,\n\t\t    0, e);\n\telse if (!(events & ELE_HANGUP) && e->events & ELE_HANGUP)\n\t\tEV_SET(kep++, (uintptr_t)fd, EVFILT_PROCDESC, EV_DELETE,\n\t\t    NOTE_EXIT, 0, e);\n#endif\n\tif (kep == ke)\n\t\treturn 0;\n\tif (kevent(eloop->fd, ke, (KEVENT_N)(kep - ke), NULL, 0, NULL) == -1)\n\t\treturn -1;\n\treturn 1;\n}\n\n#elif defined(USE_EPOLL)\n\nstatic int\neloop_event_epoll(struct eloop *eloop, struct eloop_event *e,\n    unsigned short events)\n{\n\tstruct epoll_event epe;\n\tint op;\n\n\tmemset(&epe, 0, sizeof(epe));\n\tepe.data.ptr = e;\n\tif (events & ELE_READ)\n\t\tepe.events |= EPOLLIN;\n\tif (events & ELE_WRITE)\n\t\tepe.events |= EPOLLOUT;\n\top = e->events == 0 ? EPOLL_CTL_ADD : EPOLL_CTL_MOD;\n\tif (epe.events == 0)\n\t\treturn 0;\n\tif (epoll_ctl(eloop->fd, op, e->fd, &epe) == -1)\n\t\treturn -1;\n\treturn 1;\n}\n#endif\n\nint\neloop_event_add(struct eloop *eloop, int fd, unsigned short events,\n    void (*cb)(void *, unsigned short), void *cb_arg)\n{\n\tstruct eloop_event *e;\n\tbool added;\n\tint kadded;\n\n\tif (fd == -1 || !(events & (ELE_READ | ELE_WRITE | ELE_HANGUP))) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tTAILQ_FOREACH(e, &eloop->events, next) {\n\t\tif (e->fd == fd)\n\t\t\tbreak;\n\t}\n\n\tif (e == NULL) {\n\t\tadded = true;\n\t\te = TAILQ_FIRST(&eloop->free_events);\n\t\tif (e != NULL)\n\t\t\tTAILQ_REMOVE(&eloop->free_events, e, next);\n\t\telse {\n\t\t\te = malloc(sizeof(*e));\n\t\t\tif (e == NULL) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t\tTAILQ_INSERT_HEAD(&eloop->events, e, next);\n\t\teloop->nevents++;\n\t\te->fd = fd;\n\t\te->events = 0;\n\t} else\n\t\tadded = false;\n\n\te->cb = cb;\n\te->cb_arg = cb_arg;\n\n#if defined(USE_KQUEUE)\n\tkadded = eloop_event_kqueue(eloop, e, events);\n#elif defined(USE_EPOLL)\n\tkadded = eloop_event_epoll(eloop, e, events);\n#elif defined(USE_PPOLL)\n\te->pollfd = NULL;\n\tkadded = 1;\n#endif\n\n\tif (kadded != 1 && added) {\n\t\tTAILQ_REMOVE(&eloop->events, e, next);\n\t\tTAILQ_INSERT_TAIL(&eloop->free_events, e, next);\n\t}\n\n\te->events = events;\n\teloop->events_need_setup = true;\n\treturn kadded;\n}\n\nint\neloop_event_delete(struct eloop *eloop, int fd)\n{\n\tstruct eloop_event *e;\n#if defined(USE_KQUEUE)\n\tstruct kevent ke[2], *kep = &ke[0];\n\tsize_t n;\n#endif\n\n\tif (fd == -1) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tTAILQ_FOREACH(e, &eloop->events, next) {\n\t\tif (e->fd == fd)\n\t\t\tbreak;\n\t}\n\tif (e == NULL) {\n\t\terrno = ENOENT;\n\t\treturn -1;\n\t}\n\n#if defined(USE_KQUEUE)\n\tn = 0;\n\tif (e->events & ELE_READ) {\n\t\tEV_SET(kep++, (uintptr_t)fd, EVFILT_READ, EV_DELETE, 0, 0, e);\n\t\tn++;\n\t}\n\tif (e->events & ELE_WRITE) {\n\t\tEV_SET(kep++, (uintptr_t)fd, EVFILT_WRITE, EV_DELETE, 0, 0, e);\n\t\tn++;\n\t}\n\tif (n != 0 && kevent(eloop->fd, ke, (KEVENT_N)n, NULL, 0, NULL) == -1)\n\t\treturn -1;\n#elif defined(USE_EPOLL)\n\tif (epoll_ctl(eloop->fd, EPOLL_CTL_DEL, fd, NULL) == -1)\n\t\treturn -1;\n#endif\n\n\te->fd = -1;\n\teloop->nevents--;\n\teloop->events_need_setup = true;\n\treturn 1;\n}\n\nunsigned long long\neloop_timespec_diff(const struct timespec *tsp, const struct timespec *usp,\n    unsigned int *nsp)\n{\n\tunsigned long long tsecs, usecs, secs;\n\tlong nsecs;\n\n\tif (tsp->tv_sec < 0) /* time wrapped */\n\t\ttsecs = UTIME_MAX - (unsigned long long)(-tsp->tv_sec);\n\telse\n\t\ttsecs = (unsigned long long)tsp->tv_sec;\n\tif (usp->tv_sec < 0) /* time wrapped */\n\t\tusecs = UTIME_MAX - (unsigned long long)(-usp->tv_sec);\n\telse\n\t\tusecs = (unsigned long long)usp->tv_sec;\n\n\tif (usecs > tsecs) /* time wrapped */\n\t\tsecs = (UTIME_MAX - usecs) + tsecs;\n\telse\n\t\tsecs = tsecs - usecs;\n\n\tnsecs = tsp->tv_nsec - usp->tv_nsec;\n\tif (nsecs < 0) {\n\t\tif (secs == 0)\n\t\t\tnsecs = 0;\n\t\telse {\n\t\t\tsecs--;\n\t\t\tnsecs += NSEC_PER_SEC;\n\t\t}\n\t}\n\tif (nsp != NULL)\n\t\t*nsp = (unsigned int)nsecs;\n\treturn secs;\n}\n\nstatic int\neloop_reduce_timers(struct eloop *eloop)\n{\n\tstruct timespec now;\n\tunsigned long long secs;\n\tunsigned int nsecs;\n\tstruct eloop_timeout *t;\n\n\tif (clock_gettime(CLOCK_MONOTONIC, &now) == -1)\n\t\treturn -1;\n\tsecs = eloop_timespec_diff(&now, &eloop->now, &nsecs);\n\n\tTAILQ_FOREACH(t, &eloop->timeouts, next) {\n\t\tif (secs > t->seconds) {\n\t\t\tt->seconds = 0;\n\t\t\tt->nseconds = 0;\n\t\t} else {\n\t\t\tt->seconds -= (unsigned int)secs;\n\t\t\tif (nsecs > t->nseconds) {\n\t\t\t\tif (t->seconds == 0)\n\t\t\t\t\tt->nseconds = 0;\n\t\t\t\telse {\n\t\t\t\t\tt->seconds--;\n\t\t\t\t\tt->nseconds = NSEC_PER_SEC -\n\t\t\t\t\t    (nsecs - t->nseconds);\n\t\t\t\t}\n\t\t\t} else\n\t\t\t\tt->nseconds -= nsecs;\n\t\t}\n\t}\n\n\teloop->now = now;\n\treturn 0;\n}\n\n/*\n * This implementation should cope with UINT_MAX seconds on a system\n * where time_t is INT32_MAX. It should also cope with the monotonic timer\n * wrapping, although this is highly unlikely.\n * unsigned int should match or be greater than any on wire specified timeout.\n */\nstatic int\neloop_q_timeout_add(struct eloop *eloop, int queue, unsigned int seconds,\n    unsigned int nseconds, void (*callback)(void *), void *arg)\n{\n\tstruct eloop_timeout *t, *tt = NULL;\n\n\t/* Remove existing timeout if present. */\n\tTAILQ_FOREACH(t, &eloop->timeouts, next) {\n\t\tif (t->callback == callback && t->arg == arg) {\n\t\t\tTAILQ_REMOVE(&eloop->timeouts, t, next);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (eloop_reduce_timers(eloop) == -1) {\n\t\tif (t != NULL)\n\t\t\tfree(t);\n\t\treturn -1;\n\t}\n\n\tif (t == NULL) {\n\t\t/* No existing, so allocate or grab one from the free pool. */\n\t\tif ((t = TAILQ_FIRST(&eloop->free_timeouts))) {\n\t\t\tTAILQ_REMOVE(&eloop->free_timeouts, t, next);\n\t\t} else {\n\t\t\tif ((t = malloc(sizeof(*t))) == NULL)\n\t\t\t\treturn -1;\n\t\t}\n\t}\n\n\tt->seconds = seconds;\n\tt->nseconds = nseconds;\n\tt->callback = callback;\n\tt->arg = arg;\n\tt->queue = queue;\n\n\t/* The timeout list should be in chronological order,\n\t * soonest first. */\n\tTAILQ_FOREACH(tt, &eloop->timeouts, next) {\n\t\tif (t->seconds < tt->seconds ||\n\t\t    (t->seconds == tt->seconds && t->nseconds < tt->nseconds)) {\n\t\t\tTAILQ_INSERT_BEFORE(tt, t, next);\n\t\t\treturn 0;\n\t\t}\n\t}\n\tTAILQ_INSERT_TAIL(&eloop->timeouts, t, next);\n\treturn 0;\n}\n\nint\neloop_q_timeout_add_tv(struct eloop *eloop, int queue,\n    const struct timespec *when, void (*callback)(void *), void *arg)\n{\n\tif (when->tv_sec < 0 || (unsigned long)when->tv_sec > UINT_MAX) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\tif (when->tv_nsec < 0 || when->tv_nsec > NSEC_PER_SEC) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\treturn eloop_q_timeout_add(eloop, queue, (unsigned int)when->tv_sec,\n\t    (unsigned int)when->tv_sec, callback, arg);\n}\n\nint\neloop_q_timeout_add_sec(struct eloop *eloop, int queue, unsigned int seconds,\n    void (*callback)(void *), void *arg)\n{\n\treturn eloop_q_timeout_add(eloop, queue, seconds, 0, callback, arg);\n}\n\nint\neloop_q_timeout_add_msec(struct eloop *eloop, int queue, unsigned long when,\n    void (*callback)(void *), void *arg)\n{\n\tunsigned long seconds, nseconds;\n\n\tseconds = when / MSEC_PER_SEC;\n\tif (seconds > UINT_MAX) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tnseconds = (when % MSEC_PER_SEC) * NSEC_PER_MSEC;\n\treturn eloop_q_timeout_add(eloop, queue, (unsigned int)seconds,\n\t    (unsigned int)nseconds, callback, arg);\n}\n\nint\neloop_q_timeout_delete(struct eloop *eloop, int queue, void (*callback)(void *),\n    void *arg)\n{\n\tstruct eloop_timeout *t, *tt;\n\tint n;\n\n\tn = 0;\n\tTAILQ_FOREACH_SAFE(t, &eloop->timeouts, next, tt) {\n\t\tif ((queue == 0 || t->queue == queue) && t->arg == arg &&\n\t\t    (!callback || t->callback == callback)) {\n\t\t\tTAILQ_REMOVE(&eloop->timeouts, t, next);\n\t\t\tTAILQ_INSERT_TAIL(&eloop->free_timeouts, t, next);\n\t\t\tn++;\n\t\t}\n\t}\n\treturn n;\n}\n\nvoid\neloop_exit(struct eloop *eloop, int code)\n{\n\teloop->exitcode = code;\n\teloop->exitnow = true;\n}\n\n#if defined(USE_KQUEUE) || defined(USE_EPOLL)\nstatic int\neloop_open(struct eloop *eloop)\n{\n\tint fd;\n\n#if defined(HAVE_KQUEUE1)\n\tfd = kqueue1(O_CLOEXEC);\n#elif defined(KQUEUE_CLOEXEC)\n\tfd = kqueuex(KQUEUE_CLOEXEC);\n#elif defined(USE_KQUEUE) && defined(__APPLE__)\n\t/* macOS does not allow setting CLOEXEC on kqueue.\n\t * This should not be a problem because eloop consumers\n\t * fork and exec rather than just exec and kqueue is\n\t * automatically closed on fork. */\n\tfd = kqueue();\n#elif defined(USE_KQUEUE)\n\tint flags;\n\n\tfd = kqueue();\n\tflags = fcntl(fd, F_GETFD, 0);\n\tif (!(flags != -1 && !(flags & FD_CLOEXEC) &&\n\t\tfcntl(fd, F_SETFD, flags | FD_CLOEXEC) == 0)) {\n\t\tclose(fd);\n\t\treturn -1;\n\t}\n#elif defined(USE_EPOLL)\n\tfd = epoll_create1(EPOLL_CLOEXEC);\n#endif\n\n\teloop->fd = fd;\n\treturn fd;\n}\n#endif\n\nstatic void\neloop_clear(struct eloop *eloop, unsigned short flags)\n{\n\tif (eloop == NULL)\n\t\treturn;\n\n\tif (!(flags & ELF_KEEP_SIGNALS)) {\n\t\teloop->signals = NULL;\n\t\teloop->nsignals = 0;\n\t\teloop->signal_cb = NULL;\n\t\teloop->signal_cb_ctx = NULL;\n\t}\n\n\tif (!(flags & ELF_KEEP_EVENTS)) {\n\t\tstruct eloop_event *e, *en;\n\n\t\tTAILQ_FOREACH_SAFE(e, &eloop->events, next, en)\n\t\t\tfree(e);\n\t\tTAILQ_INIT(&eloop->events);\n\n\t\tTAILQ_FOREACH_SAFE(e, &eloop->free_events, next, en)\n\t\t\tfree(e);\n\t\tTAILQ_INIT(&eloop->free_events);\n\n\t\teloop->nevents = 0;\n\t\teloop->events_invalid = true;\n\t}\n\n\tif (!(flags & ELF_KEEP_TIMEOUTS)) {\n\t\tstruct eloop_timeout *t, *tn;\n\n\t\tTAILQ_FOREACH_SAFE(t, &eloop->timeouts, next, tn)\n\t\t\tfree(t);\n\t\tTAILQ_INIT(&eloop->timeouts);\n\n\t\tTAILQ_FOREACH_SAFE(t, &eloop->free_timeouts, next, tn)\n\t\t\tfree(t);\n\t\tTAILQ_INIT(&eloop->free_timeouts);\n\t}\n}\n\n/* Must be called after fork(2) */\nint\neloop_forked(struct eloop *eloop, unsigned short flags)\n{\n#if defined(USE_KQUEUE) || defined(USE_EPOLL)\n\tstruct eloop_event *e;\n\tunsigned short events;\n\tint err;\n\n/* kqueue invalidates the fd on fork.\n * epoll shares state across fork, so we close the old and create a new one. */\n#ifdef USE_EPOLL\n\tclose(eloop->fd);\n#endif\n\teloop->fd = -1;\n\tif (flags && eloop_open(eloop) == -1)\n\t\treturn -1;\n\n\teloop_clear(eloop, flags);\n\tif (!flags)\n\t\treturn 0;\n\n#ifdef USE_KQUEUE\n\tif (eloop_signal_kqueue(eloop, eloop->signals, eloop->nsignals) == -1)\n\t\treturn -1;\n#endif\n\n\tTAILQ_FOREACH(e, &eloop->events, next) {\n\t\tif (e->fd == -1)\n\t\t\tcontinue;\n\t\tevents = e->events;\n\t\te->events = 0;\n#if defined(USE_KQUEUE)\n\t\terr = eloop_event_kqueue(eloop, e, events);\n#elif defined(USE_EPOLL)\n\t\terr = eloop_event_epoll(eloop, e, events);\n#endif\n\t\tif (err == -1)\n\t\t\treturn -1;\n\t}\n\treturn 0;\n#else\n\teloop_clear(eloop, flags);\n\treturn 0;\n#endif\n}\n\nint\neloop_signal_set_cb(struct eloop *eloop, const int *signals, size_t nsignals,\n    void (*signal_cb)(int, void *), void *signal_cb_ctx)\n{\n#ifdef USE_KQUEUE\n\tif (eloop_signal_kqueue(eloop, NULL, 0) == -1)\n\t\treturn -1;\n#endif\n\n\teloop->signals = signals;\n\teloop->nsignals = nsignals;\n\teloop->signal_cb = signal_cb;\n\teloop->signal_cb_ctx = signal_cb_ctx;\n\n#ifdef USE_KQUEUE\n\tif (eloop_signal_kqueue(eloop, signals, nsignals) == -1)\n\t\treturn -1;\n#endif\n\n\treturn 0;\n}\n\n#ifndef USE_KQUEUE\nstatic volatile int eloop_sig[ELOOP_NSIGNALS];\nstatic volatile size_t eloop_nsig;\n\nstatic void\neloop_signal3(int sig, siginfo_t *siginfo, void *arg)\n{\n\t(void)(siginfo);\n\t(void)(arg);\n\n\tif (eloop_nsig == sizeof(eloop_sig) / sizeof(eloop_sig[0])) {\n#ifdef ELOOP_DEBUG\n\t\tfprintf(stderr, \"%s: signal storm, discarding signal %d\\n\",\n\t\t    __func__, sig);\n#endif\n\t\treturn;\n\t}\n\teloop_sig[eloop_nsig++] = sig;\n}\n#endif\n\nint\neloop_signal_mask(struct eloop *eloop)\n{\n\tsigset_t newset;\n\tsize_t i;\n#ifndef USE_KQUEUE\n\tstruct sigaction sa = {\n\t\t.sa_sigaction = eloop_signal3,\n\t\t.sa_flags = SA_SIGINFO,\n\t};\n#endif\n\n\tsigemptyset(&newset);\n\tfor (i = 0; i < eloop->nsignals; i++)\n\t\tsigaddset(&newset, eloop->signals[i]);\n\tif (sigprocmask(SIG_SETMASK, &newset, &eloop->sigset) == -1)\n\t\treturn -1;\n\n#ifndef USE_KQUEUE\n\tsigemptyset(&sa.sa_mask);\n\n\tfor (i = 0; i < eloop->nsignals; i++) {\n\t\tif (sigaction(eloop->signals[i], &sa, NULL) == -1)\n\t\t\treturn -1;\n\t}\n#endif\n\n\treturn 0;\n}\n\nstruct eloop *\neloop_new(void)\n{\n\tstruct eloop *eloop;\n\n\teloop = calloc(1, sizeof(*eloop));\n\tif (eloop == NULL)\n\t\treturn NULL;\n\n\t/* Check we have a working monotonic clock. */\n\tif (clock_gettime(CLOCK_MONOTONIC, &eloop->now) == -1) {\n\t\tfree(eloop);\n\t\treturn NULL;\n\t}\n\n\tTAILQ_INIT(&eloop->events);\n\tTAILQ_INIT(&eloop->free_events);\n\tTAILQ_INIT(&eloop->timeouts);\n\tTAILQ_INIT(&eloop->free_timeouts);\n\teloop->exitcode = EXIT_FAILURE;\n\n#if defined(USE_KQUEUE) || defined(USE_EPOLL)\n\tif (eloop_open(eloop) == -1 || eloop_grow_events(eloop) == -1) {\n\t\teloop_free(eloop);\n\t\treturn NULL;\n\t}\n#endif\n\n\treturn eloop;\n}\n\nvoid\neloop_free(struct eloop *eloop)\n{\n\tif (eloop == NULL)\n\t\treturn;\n\n\teloop_clear(eloop, 0);\n#if defined(USE_KQUEUE) || defined(USE_EPOLL)\n\tif (eloop->fd != -1)\n\t\tclose(eloop->fd);\n#endif\n\tfree(eloop->fds);\n\tfree(eloop);\n}\n\nstatic unsigned short\neloop_pollevents(struct pollfd *pfd)\n{\n\tunsigned short events = 0;\n\n\tif (pfd->revents & POLLIN)\n\t\tevents |= ELE_READ;\n\tif (pfd->revents & POLLOUT)\n\t\tevents |= ELE_WRITE;\n\tif (pfd->revents & POLLHUP)\n\t\tevents |= ELE_HANGUP;\n\tif (pfd->revents & POLLERR)\n\t\tevents |= ELE_ERROR;\n\tif (pfd->revents & POLLNVAL)\n\t\tevents |= ELE_NVAL;\n\treturn events;\n}\n\nint\neloop_waitfd(int fd)\n{\n\tstruct pollfd pfd = { .fd = fd, .events = POLLIN };\n\tint err;\n\n\terr = poll(&pfd, 1, -1);\n\tif (err == -1 || err == 0)\n\t\treturn err;\n\n\treturn (int)eloop_pollevents(&pfd);\n}\n\n#if defined(USE_KQUEUE)\n\nstatic int\neloop_run_kqueue(struct eloop *eloop, const struct timespec *ts)\n{\n\tint n, nn;\n\tstruct kevent *ke;\n\tstruct eloop_event *e;\n\tunsigned short events;\n\n\tn = kevent(eloop->fd, NULL, 0, eloop->fds, (KEVENT_N)eloop->nfds, ts);\n\tif (n == -1)\n\t\treturn -1;\n\n\tfor (nn = n, ke = eloop->fds; nn != 0; nn--, ke++) {\n\t\tif (eloop->exitnow || eloop->events_invalid)\n\t\t\tbreak;\n\t\tif (ke->filter == EVFILT_SIGNAL) {\n\t\t\tif (eloop->signal_cb != NULL)\n\t\t\t\teloop->signal_cb((int)ke->ident,\n\t\t\t\t    eloop->signal_cb_ctx);\n\t\t\tcontinue;\n\t\t}\n\t\tif (ke->filter == EVFILT_READ)\n\t\t\tevents = ELE_READ;\n\t\telse if (ke->filter == EVFILT_WRITE)\n\t\t\tevents = ELE_WRITE;\n#ifdef EVFILT_PROCDESC\n\t\telse if (ke->filter == EVFILT_PROCDESC &&\n\t\t    ke->fflags & NOTE_EXIT)\n\t\t\t/* exit status is in ke->data\n\t\t\t * should we do anything with it? */\n\t\t\tevents = ELE_HANGUP;\n#endif\n\t\telse\n\t\t\tcontinue; /* assert? */\n\t\tif (ke->flags & EV_EOF)\n\t\t\tevents |= ELE_HANGUP;\n\t\tif (ke->flags & EV_ERROR)\n\t\t\tevents |= ELE_ERROR;\n\t\te = (struct eloop_event *)ke->udata;\n\t\te->cb(e->cb_arg, events);\n\t}\n\n\tif ((size_t)n == eloop->nfds)\n\t\teloop_grow_events(eloop);\n\treturn n;\n}\n\n#elif defined(USE_EPOLL)\n\nstatic int\neloop_run_epoll(struct eloop *eloop, const struct timespec *ts)\n{\n\tint n, nn;\n\tstruct epoll_event *epe;\n\tstruct eloop_event *e;\n\tunsigned short events;\n\n\t/* epoll does not work with zero events */\n\tif (eloop->nfds == 0)\n\t\tn = ppoll(NULL, 0, ts, &eloop->sigset);\n\telse {\n\t\tint timeout;\n\n\t\tif (ts != NULL) {\n\t\t\tif (ts->tv_sec > INT_MAX / MSEC_PER_SEC ||\n\t\t\t    (ts->tv_sec == INT_MAX / MSEC_PER_SEC &&\n\t\t\t\t((ts->tv_nsec + (NSEC_PER_MSEC - 1)) /\n\t\t\t\t\tNSEC_PER_MSEC >\n\t\t\t\t    INT_MAX % NSEC_PER_MSEC)))\n\t\t\t\ttimeout = INT_MAX;\n\t\t\telse\n\t\t\t\ttimeout = (int)(ts->tv_sec * MSEC_PER_SEC +\n\t\t\t\t    (ts->tv_nsec + (NSEC_PER_MSEC - 1)) /\n\t\t\t\t\tNSEC_PER_MSEC);\n\t\t} else\n\t\t\ttimeout = -1;\n\n\t\tn = epoll_pwait(eloop->fd, eloop->fds, (int)eloop->nfds,\n\t\t    timeout, &eloop->sigset);\n\t}\n\tif (n == -1)\n\t\treturn -1;\n\n\tfor (nn = n, epe = eloop->fds; nn != 0; nn--, epe++) {\n\t\tif (eloop->exitnow || eloop->events_invalid)\n\t\t\tbreak;\n\t\te = (struct eloop_event *)epe->data.ptr;\n\t\tif (e->fd == -1)\n\t\t\tcontinue;\n\t\tevents = 0;\n\t\tif (epe->events & EPOLLIN)\n\t\t\tevents |= ELE_READ;\n\t\tif (epe->events & EPOLLOUT)\n\t\t\tevents |= ELE_WRITE;\n\t\tif (epe->events & EPOLLHUP)\n\t\t\tevents |= ELE_HANGUP;\n\t\tif (epe->events & EPOLLERR)\n\t\t\tevents |= ELE_ERROR;\n\t\te->cb(e->cb_arg, events);\n\t}\n\n\tif ((size_t)n == eloop->nfds)\n\t\teloop_grow_events(eloop);\n\treturn n;\n}\n\n#elif defined(USE_PPOLL)\n\nstatic int\neloop_run_ppoll(struct eloop *eloop, const struct timespec *ts)\n{\n\tint n, nn;\n\tstruct eloop_event *e;\n\tstruct pollfd *pfd;\n\tunsigned short events;\n\n\tn = ppoll(eloop->fds, (nfds_t)eloop->nevents, ts, &eloop->sigset);\n\tif (n == -1 || n == 0)\n\t\treturn n;\n\n\tnn = n;\n\tTAILQ_FOREACH(e, &eloop->events, next) {\n\t\tif (eloop->exitnow || eloop->events_invalid)\n\t\t\tbreak;\n\t\t/* Skip freshly added events */\n\t\tif ((pfd = e->pollfd) == NULL)\n\t\t\tcontinue;\n\t\tif (e->pollfd->revents) {\n\t\t\tnn--;\n\t\t\tevents = eloop_pollevents(pfd);\n\t\t\tif (events)\n\t\t\t\te->cb(e->cb_arg, events);\n\t\t}\n\t\tif (nn == 0)\n\t\t\tbreak;\n\t}\n\treturn n;\n}\n\n#endif\n\nint\neloop_start(struct eloop *eloop)\n{\n\tint error;\n\tstruct eloop_timeout *t;\n\tstruct timespec ts, *tsp;\n\n\teloop->exitnow = false;\n\n\tfor (;;) {\n\t\tif (eloop->exitnow)\n\t\t\tbreak;\n\t\tif (eloop->events_invalid) {\n\t\t\teloop->events_invalid = false;\n\t\t\teloop->events_need_setup = true;\n\t\t}\n\n#ifndef USE_KQUEUE\n\t\tif (eloop_nsig != 0) {\n\t\t\tint sig = eloop_sig[--eloop_nsig];\n\n\t\t\tif (eloop->signal_cb != NULL)\n\t\t\t\teloop->signal_cb(sig, eloop->signal_cb_ctx);\n\t\t\tcontinue;\n\t\t}\n#endif\n\n\t\tt = TAILQ_FIRST(&eloop->timeouts);\n\t\tif (t == NULL && eloop->nevents == 0)\n\t\t\tbreak;\n\n\t\tif (t != NULL)\n\t\t\teloop_reduce_timers(eloop);\n\n\t\tif (t != NULL && t->seconds == 0 && t->nseconds == 0) {\n\t\t\tTAILQ_REMOVE(&eloop->timeouts, t, next);\n\t\t\tt->callback(t->arg);\n\t\t\tTAILQ_INSERT_TAIL(&eloop->free_timeouts, t, next);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (t != NULL) {\n\t\t\tif (t->seconds > INT_MAX) {\n\t\t\t\tts.tv_sec = (time_t)INT_MAX;\n\t\t\t\tts.tv_nsec = 0;\n\t\t\t} else {\n\t\t\t\tts.tv_sec = (time_t)t->seconds;\n\t\t\t\tts.tv_nsec = (long)t->nseconds;\n\t\t\t}\n\t\t\ttsp = &ts;\n\t\t} else\n\t\t\ttsp = NULL;\n\n\t\tif (eloop->events_need_setup)\n\t\t\teloop_event_setup_fds(eloop);\n\n#if defined(USE_KQUEUE)\n\t\terror = eloop_run_kqueue(eloop, tsp);\n#elif defined(USE_EPOLL)\n\t\terror = eloop_run_epoll(eloop, tsp);\n#elif defined(USE_PPOLL)\n\t\terror = eloop_run_ppoll(eloop, tsp);\n#endif\n\t\tif (error == -1) {\n\t\t\tif (errno == EINTR)\n\t\t\t\tcontinue;\n\t\t\treturn -errno;\n\t\t}\n\t}\n\n\treturn eloop->exitcode;\n}\n"
  },
  {
    "path": "src/eloop.h",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2026 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef ELOOP_H\n#define ELOOP_H\n\n#include <time.h>\n\n/* Handy macros to create subsecond timeouts */\n#define CSEC_PER_SEC  100\n#define MSEC_PER_SEC  1000\n#define NSEC_PER_CSEC 10000000\n#define NSEC_PER_MSEC 1000000\n#define NSEC_PER_SEC  1000000000\n\n/* eloop queues are really only for deleting timeouts registered\n * for a function or object.\n * The idea being that one interface has different timeouts for\n * say DHCP and DHCPv6. */\n#ifndef ELOOP_QUEUE\n#define ELOOP_QUEUE 1\n#endif\n\n/* Used for deleting a timeout for all queues. */\n#define ELOOP_QUEUE_ALL 0\n\n/* Forward declare eloop - the content should be invisible to the outside */\nstruct eloop;\n\n/* eloop fd events */\n#define ELE_READ   0x0001\n#define ELE_WRITE  0x0002\n#define ELE_ERROR  0x0100\n#define ELE_HANGUP 0x0200\n#define ELE_NVAL   0x0400\n\n/* What to keep when forking */\n#define ELF_KEEP_SIGNALS  0x0001\n#define ELF_KEEP_EVENTS\t  0x0002\n#define ELF_KEEP_TIMEOUTS 0x0004\n\n#define ELF_KEEP_ALL\t  (ELF_KEEP_SIGNALS | ELF_KEEP_EVENTS | ELF_KEEP_TIMEOUTS)\n\nsize_t eloop_event_count(const struct eloop *);\nint eloop_event_add(struct eloop *, int, unsigned short,\n    void (*)(void *, unsigned short), void *);\nint eloop_event_delete(struct eloop *, int);\n\nunsigned long long eloop_timespec_diff(const struct timespec *tsp,\n    const struct timespec *usp, unsigned int *nsp);\n#define eloop_timeout_add_tv(eloop, tv, cb, ctx) \\\n\teloop_q_timeout_add_tv((eloop), ELOOP_QUEUE, (tv), (cb), (ctx))\n#define eloop_timeout_add_sec(eloop, tv, cb, ctx) \\\n\teloop_q_timeout_add_sec((eloop), ELOOP_QUEUE, (tv), (cb), (ctx))\n#define eloop_timeout_add_msec(eloop, ms, cb, ctx) \\\n\teloop_q_timeout_add_msec((eloop), ELOOP_QUEUE, (ms), (cb), (ctx))\n#define eloop_timeout_delete(eloop, cb, ctx) \\\n\teloop_q_timeout_delete((eloop), ELOOP_QUEUE, (cb), (ctx))\nint eloop_q_timeout_add_tv(struct eloop *, int, const struct timespec *,\n    void (*)(void *), void *);\nint eloop_q_timeout_add_sec(struct eloop *, int, unsigned int, void (*)(void *),\n    void *);\nint eloop_q_timeout_add_msec(struct eloop *, int, unsigned long,\n    void (*)(void *), void *);\nint eloop_q_timeout_delete(struct eloop *, int, void (*)(void *), void *);\n\nint eloop_signal_set_cb(struct eloop *, const int *, size_t,\n    void (*)(int, void *), void *);\nint eloop_signal_mask(struct eloop *);\n\nstruct eloop *eloop_new(void);\nvoid eloop_free(struct eloop *);\nvoid eloop_exit(struct eloop *, int);\nint eloop_forked(struct eloop *, unsigned short);\nint eloop_waitfd(int);\nint eloop_start(struct eloop *);\n\n#endif\n"
  },
  {
    "path": "src/genembedc",
    "content": "#!/bin/sh\n# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2017-2020 Roy Marples <roy@marples.name>\n\nset -e\n\n: ${TOOL_CAT:=cat}\n: ${TOOL_SED:=sed}\nCONF=${1:-dhcpcd-definitions.conf}\nCONF_SMALL=${2:-dhcpcd-definitions.conf}\nC=${3:-dhcpcd-embedded.c.in}\n\n$TOOL_CAT $C\necho \"#ifdef SMALL\"\n$TOOL_SED \\\n\t-e 's/#.*$//' \\\n\t-e '/^$/d' \\\n\t-e 's/^/\"/g' \\\n\t-e 's/$/\\\\n\\\"/g' \\\n\t-e 's/ [ ]*/ /g' \\\n\t-e 's/\t[\t]*/ /g' \\\n\t$CONF_SMALL\necho \"#else\"\n$TOOL_SED \\\n\t-e 's/#.*$//' \\\n\t-e '/^$/d' \\\n\t-e 's/^/\"/g' \\\n\t-e 's/$/\\\\n\"/g' \\\n\t-e 's/ [ ]*/ /g' \\\n\t-e 's/\t[\t]*/ /g' \\\n\t$CONF\necho \"#endif\"\nprintf \"%s\\n%s\\n\" '\"\\0\";'\n"
  },
  {
    "path": "src/genembedh",
    "content": "#!/bin/sh\n# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2017-2019 Roy Marples <roy@marples.name>\n\nset -e\n\n: ${TOOL_SED:=sed}\n: ${TOOL_GREP:=grep}\n: ${TOOL_WC:=wc}\nCONF=${1:-dhcpcd-definitions.conf}\nCONF_SMALL=${2:-dhcpcd-definitions-small.conf}\nH=${3:-dhcpcd-embedded.h.in}\n\nINITDEFINES=$($TOOL_GREP \"^define \" $CONF | $TOOL_WC -l)\nINITDEFINENDS=$($TOOL_GREP \"^definend \" $CONF | $TOOL_WC -l)\nINITDEFINE6S=$($TOOL_GREP \"^define6 \" $CONF | $TOOL_WC -l)\nINITDEFINES_SMALL=$($TOOL_GREP \"^define \" $CONF_SMALL | $TOOL_WC -l)\nINITDEFINENDS_SMALL=$($TOOL_GREP \"^definend \" $CONF_SMALL | $TOOL_WC -l)\nINITDEFINE6S_SMALL=$($TOOL_GREP \"^define6 \" $CONF_SMALL | $TOOL_WC -l)\n$TOOL_SED \\\n\t-e \"s/@INITDEFINES@/$INITDEFINES/\" \\\n\t-e \"s/@INITDEFINENDS@/$INITDEFINENDS/\" \\\n\t-e \"s/@INITDEFINE6S@/$INITDEFINE6S/\" \\\n\t-e \"s/@INITDEFINES_SMALL@/$INITDEFINES_SMALL/\" \\\n\t-e \"s/@INITDEFINENDS_SMALL@/$INITDEFINENDS_SMALL/\" \\\n\t-e \"s/@INITDEFINE6S_SMALL@/$INITDEFINE6S_SMALL/\" \\\n\t$H\n"
  },
  {
    "path": "src/if-bsd.c",
    "content": "/*\n * BSD interface driver for dhcpcd\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/types.h>\n#include <sys/param.h>\n#include <sys/ioctl.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <sys/sysctl.h>\n#include <sys/time.h>\n#include <sys/uio.h>\n#include <sys/utsname.h>\n\n#include <net/bpf.h>\n#include <net/if.h>\n#include <net/if_dl.h>\n#include <net/if_media.h>\n#include <net/route.h>\n#include <netinet/in.h>\n#include <netinet/in_var.h>\n#include <netinet6/in6_var.h>\n#include <netinet6/nd6.h>\n#include <netinet/if_ether.h>\n\n#include <arpa/inet.h>\n\n#include \"config.h\"\n#ifdef __NetBSD__\n#include <net/if_vlanvar.h> /* Needs netinet/if_ether.h */\n#elif defined(__DragonFly__)\n#include <net/vlan/if_vlan_var.h>\n#else\n#include <net/if_vlan_var.h>\n#endif\n#ifdef __DragonFly__\n#include <netproto/802_11/ieee80211_ioctl.h>\n#else\n#include <net80211/ieee80211.h>\n#include <net80211/ieee80211_ioctl.h>\n#endif\n\n#include <assert.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <fnmatch.h>\n#include <paths.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#if defined(OpenBSD) && OpenBSD >= 201411\n/* OpenBSD dropped the global setting from sysctl but left the #define\n * which causes a EPERM error when trying to use it.\n * I think both the error and keeping the define are wrong, so we #undef it. */\n#undef IPV6CTL_ACCEPT_RTADV\n#endif\n\n#include \"common.h\"\n#include \"dhcp.h\"\n#include \"if-options.h\"\n#include \"if.h\"\n#include \"ipv4.h\"\n#include \"ipv4ll.h\"\n#include \"ipv6.h\"\n#include \"ipv6nd.h\"\n#include \"logerr.h\"\n#include \"privsep.h\"\n#include \"route.h\"\n#include \"sa.h\"\n\n#ifndef RT_ROUNDUP\n#define RT_ROUNDUP(a) \\\n\t((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))\n#define RT_ADVANCE(x, n) (x += RT_ROUNDUP((n)->sa_len))\n#endif\n\n/* Ignore these interface names which look like ethernet but are virtual or\n * just won't work without explicit configuration. */\nstatic const char *const ifnames_ignore[] = { \"bridge\",\n\t\"epair\",\t\t /* Virtual patch cable */\n\t\"fwe\",\t\t\t /* Firewire */\n\t\"fwip\",\t\t\t /* Firewire */\n\t\"tap\", \"vether\", \"xvif\", /* XEN DOM0 -> guest interface */\n\tNULL };\n\nstruct rtm {\n\tstruct rt_msghdr hdr;\n\tchar buffer[sizeof(struct sockaddr_storage) * RTAX_MAX];\n};\n\nint\nos_init(void)\n{\n\treturn 0;\n}\n\nint\nif_init(__unused struct interface *iface)\n{\n\t/* BSD promotes secondary address by default */\n\treturn 0;\n}\n\nint\nif_conf(__unused struct interface *iface)\n{\n\t/* No extra checks needed on BSD */\n\treturn 0;\n}\n\nint\nif_opensockets_os(struct dhcpcd_ctx *ctx)\n{\n\tstruct priv *priv;\n\tint n;\n#if defined(RO_MSGFILTER) || defined(ROUTE_MSGFILTER)\n\tunsigned char msgfilter[] = { RTM_IFINFO,\n#ifdef RTM_IFANNOUNCE\n\t\tRTM_IFANNOUNCE,\n#endif\n\t\tRTM_ADD, RTM_CHANGE, RTM_DELETE, RTM_MISS,\n#ifdef RTM_CHGADDR\n\t\tRTM_CHGADDR,\n#endif\n#ifdef RTM_DESYNC\n\t\tRTM_DESYNC,\n#endif\n\t\tRTM_NEWADDR, RTM_DELADDR };\n#ifdef ROUTE_MSGFILTER\n\tunsigned int i, msgfilter_mask;\n#endif\n#endif\n\n\tif ((priv = malloc(sizeof(*priv))) == NULL)\n\t\treturn -1;\n\tctx->priv = priv;\n\n#ifdef INET6\n\tpriv->pf_inet6_fd = xsocket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);\n\t/* Don't return an error so we at least work on kernels witout INET6\n\t * even though we expect INET6 support.\n\t * We will fail noisily elsewhere anyway. */\n#ifdef PRIVSEP_RIGHTS\n\tif (priv->pf_inet6_fd != -1 && IN_PRIVSEP(ctx))\n\t\tps_rights_limit_ioctl(priv->pf_inet6_fd);\n#endif\n#endif\n\n\tctx->link_fd = xsocket(PF_ROUTE, SOCK_RAW | SOCK_CXNB, AF_UNSPEC);\n\tif (ctx->link_fd == -1)\n\t\treturn -1;\n\n#ifdef SO_RERROR\n\tn = 1;\n\tif (setsockopt(ctx->link_fd, SOL_SOCKET, SO_RERROR, &n, sizeof(n)) ==\n\t    -1)\n\t\tlogerr(\"%s: SO_RERROR\", __func__);\n#endif\n\n\t/* Ignore our own route(4) messages.\n\t * Sadly there is no way of doing this for route(4) messages\n\t * generated from addresses we add/delete. */\n\tn = 0;\n\tif (setsockopt(ctx->link_fd, SOL_SOCKET, SO_USELOOPBACK, &n,\n\t\tsizeof(n)) == -1)\n\t\tlogerr(\"%s: SO_USELOOPBACK\", __func__);\n\n#ifdef PRIVSEP\n\tif (ctx->options & DHCPCD_PRIVSEPROOT) {\n\t\t/* We only want to write to this socket, so set\n\t\t * a small as possible buffer size. */\n\t\tsocklen_t smallbuf = 1;\n\n\t\tif (setsockopt(ctx->link_fd, SOL_SOCKET, SO_RCVBUF, &smallbuf,\n\t\t\t(socklen_t)sizeof(smallbuf)) == -1)\n\t\t\tlogerr(\"%s: setsockopt(SO_RCVBUF)\", __func__);\n\t}\n#endif\n\n#if defined(RO_MSGFILTER)\n\tif (setsockopt(ctx->link_fd, PF_ROUTE, RO_MSGFILTER, &msgfilter,\n\t\tsizeof(msgfilter)) == -1)\n\t\tlogerr(__func__);\n#elif defined(ROUTE_MSGFILTER)\n\t/* Convert the array into a bitmask. */\n\tmsgfilter_mask = 0;\n\tfor (i = 0; i < __arraycount(msgfilter); i++)\n\t\tmsgfilter_mask |= ROUTE_FILTER(msgfilter[i]);\n\tif (setsockopt(ctx->link_fd, PF_ROUTE, ROUTE_MSGFILTER, &msgfilter_mask,\n\t\tsizeof(msgfilter_mask)) == -1)\n\t\tlogerr(__func__);\n#else\n#warning kernel does not support route message filtering\n#endif\n\n#ifdef PRIVSEP_RIGHTS\n\t/* We need to getsockopt for SO_RCVBUF and\n\t * setsockopt for RO_MISSFILTER. */\n\tif (IN_PRIVSEP(ctx))\n\t\tps_rights_limit_fd_sockopt(ctx->link_fd);\n#endif\n\n#if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */\n\tpriv->pf_link_fd = xsocket(PF_LINK, SOCK_DGRAM, 0);\n\tif (priv->pf_link_fd == -1)\n\t\tlogerr(\"%s: socket(PF_LINK)\", __func__);\n#endif\n\treturn 0;\n}\n\nvoid\nif_closesockets_os(struct dhcpcd_ctx *ctx)\n{\n\tstruct priv *priv;\n\n\tpriv = (struct priv *)ctx->priv;\n\tif (priv == NULL)\n\t\treturn;\n\n#ifdef INET6\n\tif (priv->pf_inet6_fd != -1) {\n\t\tclose(priv->pf_inet6_fd);\n\t\tpriv->pf_inet6_fd = -1;\n\t}\n#endif\n#if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */\n\tif (priv->pf_link_fd != -1) {\n\t\tclose(priv->pf_link_fd);\n\t\tpriv->pf_link_fd = -1;\n\t}\n#endif\n\tfree(priv);\n\tctx->priv = NULL;\n\tfree(ctx->rt_missfilter);\n}\n\n#if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */\nstatic int\nif_ioctllink(struct dhcpcd_ctx *ctx, unsigned long req, void *data, size_t len)\n{\n\tstruct priv *priv = (struct priv *)ctx->priv;\n\n#ifdef PRIVSEP\n\tif (ctx->options & DHCPCD_PRIVSEP)\n\t\treturn (int)ps_root_ioctllink(ctx, req, data, len);\n#endif\n\n\treturn ioctl(priv->pf_link_fd, req, data, len);\n}\n#endif\n\nint\nif_setmac(struct interface *ifp, void *mac, uint8_t maclen)\n{\n\tif (ifp->hwlen != maclen) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n#if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */\n\tstruct if_laddrreq iflr = { .flags = IFLR_ACTIVE };\n\tstruct sockaddr_dl *sdl = satosdl(&iflr.addr);\n\tint retval;\n\n\tstrlcpy(iflr.iflr_name, ifp->name, sizeof(iflr.iflr_name));\n\tsdl->sdl_family = AF_LINK;\n\tsdl->sdl_len = sizeof(*sdl);\n\tsdl->sdl_alen = maclen;\n\tmemcpy(LLADDR(sdl), mac, maclen);\n\tretval = if_ioctllink(ifp->ctx, SIOCALIFADDR, &iflr, sizeof(iflr));\n\n\t/* Try and remove the old address */\n\tmemcpy(LLADDR(sdl), ifp->hwaddr, ifp->hwlen);\n\tif_ioctllink(ifp->ctx, SIOCDLIFADDR, &iflr, sizeof(iflr));\n\n\treturn retval;\n#else\n\tstruct ifreq ifr = {\n\t\t.ifr_addr.sa_family = AF_LINK,\n\t\t.ifr_addr.sa_len = maclen,\n\t};\n\n\tstrlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));\n\tmemcpy(ifr.ifr_addr.sa_data, mac, maclen);\n\treturn if_ioctl(ifp->ctx, SIOCSIFLLADDR, &ifr, sizeof(ifr));\n#endif\n}\n\nstatic bool\nif_ignore1(const char *drvname)\n{\n\tconst char *const *p;\n\n\tfor (p = ifnames_ignore; *p; p++) {\n\t\tif (strcmp(*p, drvname) == 0)\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\n#ifdef SIOCGIFGROUP\nint\nif_ignoregroup(int s, const char *ifname)\n{\n\tstruct ifgroupreq ifgr = { .ifgr_len = 0 };\n\tstruct ifg_req *ifg;\n\tsize_t ifg_len;\n\n\t/* Sadly it is possible to remove the device name\n\t * from the interface groups, but hopefully this\n\t * will be very unlikely.... */\n\n\tstrlcpy(ifgr.ifgr_name, ifname, sizeof(ifgr.ifgr_name));\n\tif (ioctl(s, SIOCGIFGROUP, &ifgr) == -1 ||\n\t    (ifgr.ifgr_groups = malloc(ifgr.ifgr_len)) == NULL ||\n\t    ioctl(s, SIOCGIFGROUP, &ifgr) == -1) {\n\t\tlogerr(__func__);\n\t\treturn -1;\n\t}\n\n\tfor (ifg = ifgr.ifgr_groups, ifg_len = ifgr.ifgr_len;\n\t    ifg && ifg_len >= sizeof(*ifg); ifg++, ifg_len -= sizeof(*ifg)) {\n\t\tif (if_ignore1(ifg->ifgrq_group))\n\t\t\treturn 1;\n\t}\n\treturn 0;\n}\n#endif\n\nbool\nif_ignore(struct dhcpcd_ctx *ctx, const char *ifname)\n{\n\tstruct if_spec spec;\n\n\tif (if_nametospec(ifname, &spec) != 0)\n\t\treturn false;\n\n\tif (if_ignore1(spec.drvname))\n\t\treturn true;\n\n#ifdef SIOCGIFGROUP\n#if defined(PRIVSEP) && defined(HAVE_PLEDGE)\n\tif (IN_PRIVSEP(ctx))\n\t\treturn ps_root_ifignoregroup(ctx, ifname) == 1 ? true : false;\n#endif\n\telse\n\t\treturn if_ignoregroup(ctx->pf_inet_fd, ifname) == 1 ? true :\n\t\t\t\t\t\t\t\t      false;\n#else\n\tUNUSED(ctx);\n\treturn false;\n#endif\n}\n\nstatic int\nif_indirect_ioctl(struct dhcpcd_ctx *ctx, const char *ifname, unsigned long cmd,\n    void *data, size_t len)\n{\n\tstruct ifreq ifr = { .ifr_flags = 0 };\n\n#if defined(PRIVSEP) && (defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE))\n\tif (IN_PRIVSEP(ctx))\n\t\treturn (int)ps_root_indirectioctl(ctx, cmd, ifname, data, len);\n#else\n\tUNUSED(len);\n#endif\n\n\tstrlcpy(ifr.ifr_name, ifname, IFNAMSIZ);\n\tifr.ifr_data = data;\n\treturn ioctl(ctx->pf_inet_fd, cmd, &ifr);\n}\n\nint\nif_carrier(struct interface *ifp, const void *ifadata)\n{\n\tconst struct if_data *ifi = ifadata;\n\n\t/*\n\t * Every BSD returns this and it is the sole source of truth.\n\t * Not all BSD's support SIOCGIFDATA and not all interfaces\n\t * support SIOCGIFMEDIA.\n\t */\n\tassert(ifadata != NULL);\n\n\tif (ifi->ifi_link_state >= LINK_STATE_UP)\n\t\treturn LINK_UP;\n\tif (ifi->ifi_link_state == LINK_STATE_UNKNOWN) {\n\t\t/*\n\t\t * Work around net80211 issues in some BSDs.\n\t\t * Wireless MUST support link state change.\n\t\t */\n\t\tif (ifp->wireless)\n\t\t\treturn LINK_DOWN;\n\t\treturn LINK_UNKNOWN;\n\t}\n\treturn LINK_DOWN;\n}\n\nbool\nif_roaming(struct interface *ifp)\n{\n/* Check for NetBSD as a safety measure.\n * If other BSD's gain IN_IFF_TENTATIVE check they re-do DAD\n * when the carrier comes up again. */\n#if defined(IN_IFF_TENTATIVE) && defined(__NetBSD__)\n\treturn ifp->flags & IFF_UP && ifp->carrier == LINK_DOWN;\n#else\n\tUNUSED(ifp);\n\treturn false;\n#endif\n}\n\nstatic void\nif_linkaddr(struct sockaddr_dl *sdl, const struct interface *ifp)\n{\n\tmemset(sdl, 0, sizeof(*sdl));\n\tsdl->sdl_family = AF_LINK;\n\tsdl->sdl_len = sizeof(*sdl);\n\tsdl->sdl_nlen = sdl->sdl_alen = sdl->sdl_slen = 0;\n\tsdl->sdl_index = (unsigned short)ifp->index;\n}\n\nstatic int\nif_getssid1(struct dhcpcd_ctx *ctx, const char *ifname, void *ssid)\n{\n\tint retval = -1;\n#if defined(SIOCG80211NWID)\n\tstruct ieee80211_nwid nwid;\n#elif defined(IEEE80211_IOC_SSID)\n\tstruct ieee80211req ireq;\n\tchar nwid[IEEE80211_NWID_LEN];\n#endif\n\n#if defined(SIOCG80211NWID) /* NetBSD */\n\tmemset(&nwid, 0, sizeof(nwid));\n\tif (if_indirect_ioctl(ctx, ifname, SIOCG80211NWID, &nwid,\n\t\tsizeof(nwid)) == 0) {\n\t\tif (ssid == NULL)\n\t\t\tretval = nwid.i_len;\n\t\telse if (nwid.i_len > IF_SSIDLEN)\n\t\t\terrno = ENOBUFS;\n\t\telse {\n\t\t\tretval = nwid.i_len;\n\t\t\tmemcpy(ssid, nwid.i_nwid, nwid.i_len);\n\t\t}\n\t}\n#elif defined(IEEE80211_IOC_SSID) /* FreeBSD */\n\tmemset(&ireq, 0, sizeof(ireq));\n\tstrlcpy(ireq.i_name, ifname, sizeof(ireq.i_name));\n\tireq.i_type = IEEE80211_IOC_SSID;\n\tireq.i_val = -1;\n\tmemset(nwid, 0, sizeof(nwid));\n\tireq.i_data = &nwid;\n\tif (ioctl(ctx->pf_inet_fd, SIOCG80211, &ireq) == 0) {\n\t\tif (ssid == NULL)\n\t\t\tretval = ireq.i_len;\n\t\telse if (ireq.i_len > IF_SSIDLEN)\n\t\t\terrno = ENOBUFS;\n\t\telse {\n\t\t\tretval = ireq.i_len;\n\t\t\tmemcpy(ssid, nwid, ireq.i_len);\n\t\t}\n\t}\n#else\n\terrno = ENOSYS;\n#endif\n\n\treturn retval;\n}\n\nint\nif_getssid(struct interface *ifp)\n{\n\tint r;\n\n\tr = if_getssid1(ifp->ctx, ifp->name, ifp->ssid);\n\tif (r != -1)\n\t\tifp->ssid_len = (unsigned int)r;\n\telse\n\t\tifp->ssid_len = 0;\n\tifp->ssid[ifp->ssid_len] = '\\0';\n\treturn r;\n}\n\n/*\n * FreeBSD allows for Virtual Access Points\n * We need to check if the interface is a Virtual Interface Master\n * and if so, don't use it.\n * This check is made by virtue of being a IEEE80211 device but\n * returning the SSID gives an error.\n */\nint\nif_vimaster(struct dhcpcd_ctx *ctx, const char *ifname)\n{\n\tint r;\n\tstruct ifmediareq ifmr = { .ifm_active = 0 };\n\n\tstrlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));\n\tr = ioctl(ctx->pf_inet_fd, SIOCGIFMEDIA, &ifmr);\n\tif (r == -1)\n\t\treturn -1;\n\tif (ifmr.ifm_status & IFM_AVALID &&\n\t    IFM_TYPE(ifmr.ifm_active) == IFM_IEEE80211) {\n\t\tif (if_getssid1(ctx, ifname, NULL) == -1)\n\t\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nunsigned short\nif_vlanid(const struct interface *ifp)\n{\n#ifdef SIOCGETVLAN\n\tstruct vlanreq vlr = { .vlr_tag = 0 };\n\n\tif (if_indirect_ioctl(ifp->ctx, ifp->name, SIOCGETVLAN, &vlr,\n\t\tsizeof(vlr)) != 0)\n\t\treturn 0; /* 0 means no VLANID */\n\treturn vlr.vlr_tag;\n#elif defined(SIOCGVNETID)\n\tstruct ifreq ifr = { .ifr_vnetid = 0 };\n\n\tstrlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));\n\tif (ioctl(ifp->ctx->pf_inet_fd, SIOCGVNETID, &ifr) != 0)\n\t\treturn 0; /* 0 means no VLANID */\n\treturn ifr.ifr_vnetid;\n#else\n\tUNUSED(ifp);\n\treturn 0; /* 0 means no VLANID */\n#endif\n}\n\nstatic int\nget_addrs(int type, const void *data, size_t data_len,\n    const struct sockaddr **sa)\n{\n\tconst char *cp, *ep;\n\tint i;\n\n\tcp = data;\n\tep = cp + data_len;\n\tfor (i = 0; i < RTAX_MAX; i++) {\n\t\tif (type & (1 << i)) {\n\t\t\tif (cp >= ep) {\n\t\t\t\terrno = EINVAL;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tsa[i] = (const struct sockaddr *)cp;\n\t\t\tRT_ADVANCE(cp, sa[i]);\n\t\t} else\n\t\t\tsa[i] = NULL;\n\t}\n\n\treturn 0;\n}\n\nstatic struct interface *\nif_findsdl(struct dhcpcd_ctx *ctx, const struct sockaddr_dl *sdl)\n{\n\tif (sdl->sdl_index)\n\t\treturn if_findindex(ctx->ifaces, sdl->sdl_index);\n\n\tif (sdl->sdl_nlen) {\n\t\tchar ifname[IF_NAMESIZE];\n\n\t\tmemcpy(ifname, sdl->sdl_data, sdl->sdl_nlen);\n\t\tifname[sdl->sdl_nlen] = '\\0';\n\t\treturn if_find(ctx->ifaces, ifname);\n\t}\n\tif (sdl->sdl_alen) {\n\t\tstruct interface *ifp;\n\n\t\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\t\tif (ifp->hwlen == sdl->sdl_alen &&\n\t\t\t    memcmp(ifp->hwaddr, sdl->sdl_data, sdl->sdl_alen) ==\n\t\t\t\t0)\n\t\t\t\treturn ifp;\n\t\t}\n\t}\n\n\terrno = ENOENT;\n\treturn NULL;\n}\n\nstatic struct interface *\nif_findsa(struct dhcpcd_ctx *ctx, const struct sockaddr *sa)\n{\n\tif (sa == NULL) {\n\t\terrno = EINVAL;\n\t\treturn NULL;\n\t}\n\n\tswitch (sa->sa_family) {\n\tcase AF_LINK: {\n\t\tconst struct sockaddr_dl *sdl;\n\n\t\tsdl = (const void *)sa;\n\t\treturn if_findsdl(ctx, sdl);\n\t}\n#ifdef INET\n\tcase AF_INET: {\n\t\tconst struct sockaddr_in *sin;\n\t\tstruct ipv4_addr *ia;\n\n\t\tsin = (const void *)sa;\n\t\tif ((ia = ipv4_findmaskaddr(ctx, &sin->sin_addr)))\n\t\t\treturn ia->iface;\n\t\tif ((ia = ipv4_findmaskbrd(ctx, &sin->sin_addr)))\n\t\t\treturn ia->iface;\n\t\tbreak;\n\t}\n#endif\n#ifdef INET6\n\tcase AF_INET6: {\n\t\tconst struct sockaddr_in6 *sin;\n\t\tunsigned int scope;\n\t\tstruct ipv6_addr *ia;\n\n\t\tsin = (const void *)sa;\n\t\tscope = ipv6_getscope(sin);\n\t\tif (scope != 0)\n\t\t\treturn if_findindex(ctx->ifaces, scope);\n\t\tif ((ia = ipv6_findmaskaddr(ctx, &sin->sin6_addr)))\n\t\t\treturn ia->iface;\n\t\tif ((ia = ipv6_finddstaddr(ctx, &sin->sin6_addr)))\n\t\t\treturn ia->iface;\n\t\tbreak;\n\t}\n#endif\n\tdefault:\n\t\terrno = EAFNOSUPPORT;\n\t\treturn NULL;\n\t}\n\n\terrno = ENOENT;\n\treturn NULL;\n}\n\nstatic void\nif_copysa(struct sockaddr *dst, const struct sockaddr *src)\n{\n\tassert(dst != NULL);\n\tassert(src != NULL);\n\n\tmemcpy(dst, src, src->sa_len);\n#if defined(INET6) && defined(__KAME__)\n\tif (dst->sa_family == AF_INET6) {\n\t\tstruct in6_addr *in6;\n\n\t\tin6 = &satosin6(dst)->sin6_addr;\n\t\tif (IN6_IS_ADDR_LINKLOCAL(in6))\n\t\t\tin6->s6_addr[2] = in6->s6_addr[3] = '\\0';\n\t}\n#endif\n}\n\nint\nif_route(unsigned char cmd, const struct rt *rt)\n{\n\tstruct dhcpcd_ctx *ctx;\n\tstruct rtm rtmsg;\n\tstruct rt_msghdr *rtm = &rtmsg.hdr;\n\tchar *bp = rtmsg.buffer;\n\tstruct sockaddr_dl sdl;\n\tbool gateway_unspec;\n\n\tassert(rt != NULL);\n\tassert(rt->rt_ifp != NULL);\n\tassert(rt->rt_ifp->ctx != NULL);\n\tctx = rt->rt_ifp->ctx;\n\n#define ADDSA(sa)                               \\\n\tdo {                                    \\\n\t\tmemcpy(bp, (sa), (sa)->sa_len); \\\n\t\tbp += RT_ROUNDUP((sa)->sa_len); \\\n\t} while (0 /* CONSTCOND */)\n\n\tmemset(&rtmsg, 0, sizeof(rtmsg));\n\trtm->rtm_version = RTM_VERSION;\n\trtm->rtm_type = cmd;\n#ifdef __OpenBSD__\n\trtm->rtm_pid = getpid();\n#endif\n\trtm->rtm_seq = ++ctx->seq;\n\trtm->rtm_flags = (int)rt->rt_flags;\n\trtm->rtm_addrs = RTA_DST;\n#ifdef RTF_PINNED\n\tif (cmd != RTM_ADD)\n\t\trtm->rtm_flags |= RTF_PINNED;\n#endif\n\n\tgateway_unspec = sa_is_unspecified(&rt->rt_gateway);\n\n\tif (cmd == RTM_ADD || cmd == RTM_CHANGE) {\n\t\tbool netmask_bcast = sa_is_allones(&rt->rt_netmask);\n\n\t\trtm->rtm_flags |= RTF_UP;\n\t\trtm->rtm_addrs |= RTA_GATEWAY;\n\t\tif (!(rtm->rtm_flags & RTF_REJECT) &&\n\t\t    !sa_is_loopback(&rt->rt_gateway)) {\n\t\t\trtm->rtm_index = (unsigned short)rt->rt_ifp->index;\n/*\n * OpenBSD rejects this for on-link routes when there is no default route\n * OpenBSD does not allow the same IPv6 address on different\n * interfaces on the same network, so let's try to encourage someone to\n * fix that by logging a waring during compile.\n */\n#ifdef __OpenBSD__\n#warning kernel does not allow IPv6 address sharing\n\t\t\tif (!gateway_unspec ||\n\t\t\t    rt->rt_dest.sa_family != AF_INET6)\n#endif\n\t\t\t\trtm->rtm_addrs |= RTA_IFP;\n\t\t\tif (!sa_is_unspecified(&rt->rt_ifa))\n\t\t\t\trtm->rtm_addrs |= RTA_IFA;\n\t\t}\n\t\tif (netmask_bcast)\n\t\t\trtm->rtm_flags |= RTF_HOST;\n\t\t/* Network routes are cloning or connected if supported.\n\t\t * All other routes are static. */\n\t\tif (gateway_unspec && !(rtm->rtm_flags & RTF_REJECT)) {\n#ifdef RTF_CLONING\n\t\t\trtm->rtm_flags |= RTF_CLONING;\n#endif\n#ifdef RTF_CONNECTED\n\t\t\trtm->rtm_flags |= RTF_CONNECTED;\n#endif\n#ifdef RTP_CONNECTED\n\t\t\trtm->rtm_priority = RTP_CONNECTED;\n#endif\n#ifdef RTF_CLONING\n\t\t\tif (netmask_bcast) {\n\t\t\t\t/*\n\t\t\t\t * We add a cloning network route for a single\n\t\t\t\t * host. Traffic to the host will generate a\n\t\t\t\t * cloned route and the hardware address will\n\t\t\t\t * resolve correctly.\n\t\t\t\t * It might be more correct to use RTF_HOST\n\t\t\t\t * instead of RTF_CLONING, and that does work,\n\t\t\t\t * but some OS generate an arp warning\n\t\t\t\t * diagnostic which we don't want to do.\n\t\t\t\t */\n\t\t\t\trtm->rtm_flags &= ~RTF_HOST;\n\t\t\t}\n#endif\n\t\t} else\n\t\t\trtm->rtm_flags |= RTF_GATEWAY;\n\n\t\tif (rt->rt_dflags & RTDF_STATIC)\n\t\t\trtm->rtm_flags |= RTF_STATIC;\n\n\t\tif (rt->rt_mtu != 0) {\n\t\t\trtm->rtm_inits |= RTV_MTU;\n\t\t\trtm->rtm_rmx.rmx_mtu = rt->rt_mtu;\n\t\t}\n\t}\n\n\tif (!(rtm->rtm_flags & RTF_HOST))\n\t\trtm->rtm_addrs |= RTA_NETMASK;\n\n\tif_linkaddr(&sdl, rt->rt_ifp);\n\n\tADDSA(&rt->rt_dest);\n\n\tif (rtm->rtm_addrs & RTA_GATEWAY) {\n\t\tif (gateway_unspec)\n\t\t\tADDSA((struct sockaddr *)&sdl);\n\t\telse {\n\t\t\tunion sa_ss gateway;\n\n\t\t\tif_copysa(&gateway.sa, &rt->rt_gateway);\n#ifdef INET6\n\t\t\tif (gateway.sa.sa_family == AF_INET6)\n\t\t\t\tipv6_setscope(&gateway.sin6, rt->rt_ifp->index);\n#endif\n\t\t\tADDSA(&gateway.sa);\n\t\t}\n\t}\n\n\tif (rtm->rtm_addrs & RTA_NETMASK)\n\t\tADDSA(&rt->rt_netmask);\n\n\tif (rtm->rtm_addrs & RTA_IFP)\n\t\tADDSA((struct sockaddr *)&sdl);\n\n\tif (rtm->rtm_addrs & RTA_IFA)\n\t\tADDSA(&rt->rt_ifa);\n\n#undef ADDSA\n\n\trtm->rtm_msglen = (unsigned short)(bp - (char *)rtm);\n\n#ifdef PRIVSEP\n\tif (ctx->options & DHCPCD_PRIVSEP) {\n\t\tif (ps_root_route(ctx, rtm, rtm->rtm_msglen) == -1)\n\t\t\treturn -1;\n\t\treturn 0;\n\t}\n#endif\n\tif (write(ctx->link_fd, rtm, rtm->rtm_msglen) == -1)\n\t\treturn -1;\n\treturn 0;\n}\n\nstatic bool\nif_realroute(const struct rt_msghdr *rtm)\n{\n#ifdef RTF_CLONED\n\tif (rtm->rtm_flags & RTF_CLONED)\n\t\treturn false;\n#endif\n#ifdef RTF_WASCLONED\n\tif (rtm->rtm_flags & RTF_WASCLONED)\n\t\treturn false;\n#endif\n#ifdef RTF_LOCAL\n\tif (rtm->rtm_flags & RTF_LOCAL)\n\t\treturn false;\n#endif\n#ifdef RTF_BROADCAST\n\tif (rtm->rtm_flags & RTF_BROADCAST)\n\t\treturn false;\n#endif\n\treturn true;\n}\n\nstatic int\nif_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, const struct rt_msghdr *rtm)\n{\n\tconst struct sockaddr *rti_info[RTAX_MAX];\n\n\tif (!(rtm->rtm_addrs & RTA_DST)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\tif (rtm->rtm_type != RTM_MISS && !(rtm->rtm_addrs & RTA_GATEWAY)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tif (get_addrs(rtm->rtm_addrs, (const char *)rtm + sizeof(*rtm),\n\t\trtm->rtm_msglen - sizeof(*rtm), rti_info) == -1)\n\t\treturn -1;\n\tmemset(rt, 0, sizeof(*rt));\n\n\trt->rt_flags = (unsigned int)rtm->rtm_flags;\n\tif_copysa(&rt->rt_dest, rti_info[RTAX_DST]);\n\n\tif (rtm->rtm_addrs & RTA_NETMASK) {\n\t\tif_copysa(&rt->rt_netmask, rti_info[RTAX_NETMASK]);\n\t\t/*\n\t\t * Netmask family and length are ignored by traditional\n\t\t * userland tools such as route and netstat and are assumed\n\t\t * to match the destination sockaddr.\n\t\t * This is fortunate because BSD kernels use a radix tree\n\t\t * to store routes which adjusts the netmask at the point\n\t\t * of insertion where this information is lost.\n\t\t * We can just sub in the values from the destination address.\n\t\t *\n\t\t * This is currently true for all BSD kernels.\n\t\t */\n\t\trt->rt_netmask.sa_family = rt->rt_dest.sa_family;\n\t\trt->rt_netmask.sa_len = rt->rt_dest.sa_len;\n\t}\n\n\t/* dhcpcd likes an unspecified gateway to indicate via the link.\n\t * However we need to know if gateway was a link with an address. */\n\tif (rtm->rtm_addrs & RTA_GATEWAY) {\n\t\tif (rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) {\n\t\t\tconst struct sockaddr_dl *sdl;\n\n\t\t\tsdl = (const struct sockaddr_dl *)(const void *)\n\t\t\t    rti_info[RTAX_GATEWAY];\n\t\t\tif (sdl->sdl_alen != 0)\n\t\t\t\trt->rt_dflags |= RTDF_GATELINK;\n\t\t} else if (rtm->rtm_flags & RTF_GATEWAY)\n\t\t\tif_copysa(&rt->rt_gateway, rti_info[RTAX_GATEWAY]);\n\t}\n\n\tif (rtm->rtm_addrs & RTA_IFA)\n\t\tif_copysa(&rt->rt_ifa, rti_info[RTAX_IFA]);\n\n\trt->rt_mtu = (unsigned int)rtm->rtm_rmx.rmx_mtu;\n\n\tif (rtm->rtm_index)\n\t\trt->rt_ifp = if_findindex(ctx->ifaces, rtm->rtm_index);\n\telse if (rtm->rtm_addrs & RTA_IFP)\n\t\trt->rt_ifp = if_findsa(ctx, rti_info[RTAX_IFP]);\n\telse if (rtm->rtm_addrs & RTA_GATEWAY)\n\t\trt->rt_ifp = if_findsa(ctx, rti_info[RTAX_GATEWAY]);\n\telse\n\t\trt->rt_ifp = if_findsa(ctx, rti_info[RTAX_DST]);\n\n\tif (rt->rt_ifp == NULL && rtm->rtm_type == RTM_MISS)\n\t\trt->rt_ifp = if_find(ctx->ifaces, \"lo0\");\n\n\tif (rt->rt_ifp == NULL) {\n\t\terrno = ESRCH;\n\t\treturn -1;\n\t}\n\treturn 0;\n}\n\nstatic int\nif_sysctl(struct dhcpcd_ctx *ctx, const int *name, u_int namelen, void *oldp,\n    size_t *oldlenp, void *newp, size_t newlen)\n{\n#if defined(PRIVSEP) && defined(HAVE_CAPSICUM)\n\tif (IN_PRIVSEP(ctx))\n\t\treturn (int)ps_root_sysctl(ctx, name, namelen, oldp, oldlenp,\n\t\t    newp, newlen);\n#else\n\tUNUSED(ctx);\n#endif\n\n\treturn sysctl(name, namelen, oldp, oldlenp, newp, newlen);\n}\n\nint\nif_initrt(struct dhcpcd_ctx *ctx, rb_tree_t *kroutes, int af)\n{\n\tstruct rt_msghdr *rtm;\n\tint mib[6] = { CTL_NET, PF_ROUTE, 0, af, NET_RT_DUMP, 0 };\n\tsize_t bufl;\n\tchar *buf = NULL, *p, *end;\n\tstruct rt rt, *rtn;\n\nagain:\n\tif (if_sysctl(ctx, mib, __arraycount(mib), NULL, &bufl, NULL, 0) == -1)\n\t\tgoto err;\n\tif (bufl == 0) {\n\t\tfree(buf);\n\t\treturn 0;\n\t}\n\tif ((p = realloc(buf, bufl)) == NULL)\n\t\tgoto err;\n\tbuf = p;\n\tif (if_sysctl(ctx, mib, __arraycount(mib), buf, &bufl, NULL, 0) == -1) {\n\t\tif (errno == ENOMEM)\n\t\t\tgoto again;\n\t\tgoto err;\n\t}\n\n\tend = buf + bufl;\n\tfor (p = buf; p < end; p += rtm->rtm_msglen) {\n\t\trtm = (void *)p;\n\t\tif (p + sizeof(*rtm) > end || p + rtm->rtm_msglen > end) {\n\t\t\terrno = EINVAL;\n\t\t\tbreak;\n\t\t}\n\t\tif (!if_realroute(rtm))\n\t\t\tcontinue;\n\t\tif (if_copyrt(ctx, &rt, rtm) != 0)\n\t\t\tcontinue;\n\t\tif ((rtn = rt_new(rt.rt_ifp)) == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\tbreak;\n\t\t}\n\t\tmemcpy(rtn, &rt, sizeof(*rtn));\n\t\tif (rb_tree_insert_node(kroutes, rtn) != rtn)\n\t\t\trt_free(rtn);\n\t}\n\tfree(buf);\n\treturn p == end ? 0 : -1;\n\nerr:\n\tfree(buf);\n\treturn -1;\n}\n\n#ifdef INET\nint\nif_address(unsigned char cmd, const struct ipv4_addr *ia)\n{\n\tint r;\n\tstruct in_aliasreq ifra;\n\tstruct dhcpcd_ctx *ctx = ia->iface->ctx;\n\n\tmemset(&ifra, 0, sizeof(ifra));\n\tstrlcpy(ifra.ifra_name, ia->iface->name, sizeof(ifra.ifra_name));\n\n#define ADDADDR(var, addr)                       \\\n\tdo {                                     \\\n\t\t(var)->sin_family = AF_INET;     \\\n\t\t(var)->sin_len = sizeof(*(var)); \\\n\t\t(var)->sin_addr = *(addr);       \\\n\t} while (/*CONSTCOND*/ 0)\n\tADDADDR(&ifra.ifra_addr, &ia->addr);\n\tADDADDR(&ifra.ifra_mask, &ia->mask);\n\tif (cmd == RTM_NEWADDR && ia->brd.s_addr != INADDR_ANY)\n\t\tADDADDR(&ifra.ifra_broadaddr, &ia->brd);\n#undef ADDADDR\n\n\tr = if_ioctl(ctx, cmd == RTM_DELADDR ? SIOCDIFADDR : SIOCAIFADDR, &ifra,\n\t    sizeof(ifra));\n\treturn r;\n}\n\n#if !(defined(HAVE_IFADDRS_ADDRFLAGS) && defined(HAVE_IFAM_ADDRFLAGS))\nint\nif_addrflags(const struct interface *ifp, const struct in_addr *addr,\n    __unused const char *alias)\n{\n#ifdef SIOCGIFAFLAG_IN\n\tstruct ifreq ifr;\n\tstruct sockaddr_in *sin;\n\n\tmemset(&ifr, 0, sizeof(ifr));\n\tstrlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));\n\tsin = (void *)&ifr.ifr_addr;\n\tsin->sin_family = AF_INET;\n\tsin->sin_addr = *addr;\n\tif (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFAFLAG_IN, &ifr) == -1)\n\t\treturn -1;\n\treturn ifr.ifr_addrflags;\n#else\n\tUNUSED(ifp);\n\tUNUSED(addr);\n\treturn 0;\n#endif\n}\n#endif\n#endif /* INET */\n\n#ifdef INET6\nstatic int\nif_ioctl6(struct dhcpcd_ctx *ctx, unsigned long req, void *data, size_t len)\n{\n\tstruct priv *priv;\n\n#ifdef PRIVSEP\n\tif (ctx->options & DHCPCD_PRIVSEP)\n\t\treturn (int)ps_root_ioctl6(ctx, req, data, len);\n#endif\n\n\tpriv = ctx->priv;\n\treturn ioctl(priv->pf_inet6_fd, req, data, len);\n}\n\nint\nif_address6(unsigned char cmd, const struct ipv6_addr *ia)\n{\n\tstruct in6_aliasreq ifa = { .ifra_flags = 0 };\n\tstruct in6_addr mask;\n\tstruct dhcpcd_ctx *ctx = ia->iface->ctx;\n\n\tstrlcpy(ifa.ifra_name, ia->iface->name, sizeof(ifa.ifra_name));\n#if defined(__FreeBSD__) || defined(__DragonFly__)\n\t/* This is a bug - the kernel should work this out. */\n\tif (ia->addr_flags & IN6_IFF_TENTATIVE)\n\t\tifa.ifra_flags |= IN6_IFF_TENTATIVE;\n#endif\n#if (defined(__NetBSD__) || defined(__OpenBSD__)) && \\\n    (defined(IPV6CTL_ACCEPT_RTADV) || defined(ND6_IFF_ACCEPT_RTADV))\n\t/* These kernels don't accept userland setting IN6_IFF_AUTOCONF */\n#else\n\tif (ia->flags & IPV6_AF_AUTOCONF)\n\t\tifa.ifra_flags |= IN6_IFF_AUTOCONF;\n#endif\n#ifdef IPV6_MANAGETEMPADDR\n\tif (ia->flags & IPV6_AF_TEMPORARY)\n\t\tifa.ifra_flags |= IN6_IFF_TEMPORARY;\n#endif\n\n#define ADDADDR(v, addr)                     \\\n\t{                                    \\\n\t\t(v)->sin6_family = AF_INET6; \\\n\t\t(v)->sin6_len = sizeof(*v);  \\\n\t\t(v)->sin6_addr = *(addr);    \\\n\t}\n\n\tADDADDR(&ifa.ifra_addr, &ia->addr);\n\tipv6_setscope(&ifa.ifra_addr, ia->iface->index);\n\tipv6_mask(&mask, ia->prefix_len);\n\tADDADDR(&ifa.ifra_prefixmask, &mask);\n\n#undef ADDADDR\n\n\t/*\n\t * Every BSD kernel wants to add the prefix of the address to it's\n\t * list of RA received prefixes.\n\t * THIS IS WRONG because there (as the comments in the kernel state)\n\t * is no API for managing prefix lifetime and the kernel should not\n\t * pretend it's from a RA either.\n\t *\n\t * The issue is that the very first assigned prefix will inherit the\n\t * lifetime of the address, but any subsequent alteration of the\n\t * address OR it's lifetime will not affect the prefix lifetime.\n\t * As such, we cannot stop the prefix from timing out and then\n\t * constantly removing the prefix route dhcpcd is capable of adding\n\t * in it's absense.\n\t *\n\t * What we can do to mitigate the issue is to add the address with\n\t * infinite lifetimes, so the prefix route will never time out.\n\t * Once done, we can then set lifetimes on the address and all is good.\n\t * The downside of this approach is that we need to manually remove\n\t * the kernel route because it has no lifetime, but this is OK as\n\t * dhcpcd will handle this too.\n\t *\n\t * This issue is discussed on the NetBSD mailing lists here:\n\t * http://mail-index.netbsd.org/tech-net/2016/08/05/msg006044.html\n\t *\n\t * Fixed in NetBSD-7.99.36\n\t * NOT fixed in FreeBSD - bug 195197\n\t * Fixed in OpenBSD-5.9\n\t */\n\n#if !((defined(__NetBSD_Version__) && __NetBSD_Version__ >= 799003600) || \\\n    (defined(__OpenBSD__) && OpenBSD >= 201605))\n\tif (cmd == RTM_NEWADDR && !(ia->flags & IPV6_AF_ADDED)) {\n\t\tifa.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;\n\t\tifa.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;\n\t\t(void)if_ioctl6(ctx, SIOCAIFADDR_IN6, &ifa, sizeof(ifa));\n\t}\n#endif\n\n#if defined(__OpenBSD__) && OpenBSD <= 201705\n\t/* BUT OpenBSD older than 6.2 does not reset the address lifetime\n\t * for subsequent calls...\n\t * Luckily dhcpcd will remove the lease when it expires so\n\t * just set an infinite lifetime, unless a temporary address. */\n\tif (ifa.ifra_flags & IN6_IFF_PRIVACY) {\n\t\tifa.ifra_lifetime.ia6t_vltime = ia->prefix_vltime;\n\t\tifa.ifra_lifetime.ia6t_pltime = ia->prefix_pltime;\n\t} else {\n\t\tifa.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;\n\t\tifa.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;\n\t}\n#else\n\tifa.ifra_lifetime.ia6t_vltime = ia->prefix_vltime;\n\tifa.ifra_lifetime.ia6t_pltime = ia->prefix_pltime;\n#endif\n\n\treturn if_ioctl6(ctx,\n\t    cmd == RTM_DELADDR ? SIOCDIFADDR_IN6 : SIOCAIFADDR_IN6, &ifa,\n\t    sizeof(ifa));\n}\n\nint\nif_addrflags6(const struct interface *ifp, const struct in6_addr *addr,\n    __unused const char *alias)\n{\n\tint flags;\n\tstruct in6_ifreq ifr6;\n\tstruct priv *priv;\n\n\tmemset(&ifr6, 0, sizeof(ifr6));\n\tstrlcpy(ifr6.ifr_name, ifp->name, sizeof(ifr6.ifr_name));\n\tifr6.ifr_addr.sin6_family = AF_INET6;\n\tifr6.ifr_addr.sin6_addr = *addr;\n\tipv6_setscope(&ifr6.ifr_addr, ifp->index);\n\tpriv = (struct priv *)ifp->ctx->priv;\n\tif (ioctl(priv->pf_inet6_fd, SIOCGIFAFLAG_IN6, &ifr6) != -1)\n\t\tflags = ifr6.ifr_ifru.ifru_flags6;\n\telse\n\t\tflags = -1;\n\treturn flags;\n}\n\nint\nif_getlifetime6(struct ipv6_addr *ia)\n{\n\tstruct in6_ifreq ifr6;\n\ttime_t t;\n\tstruct in6_addrlifetime *lifetime;\n\tstruct priv *priv;\n\n\tmemset(&ifr6, 0, sizeof(ifr6));\n\tstrlcpy(ifr6.ifr_name, ia->iface->name, sizeof(ifr6.ifr_name));\n\tifr6.ifr_addr.sin6_family = AF_INET6;\n\tifr6.ifr_addr.sin6_addr = ia->addr;\n\tipv6_setscope(&ifr6.ifr_addr, ia->iface->index);\n\tpriv = (struct priv *)ia->iface->ctx->priv;\n\tif (ioctl(priv->pf_inet6_fd, SIOCGIFALIFETIME_IN6, &ifr6) == -1)\n\t\treturn -1;\n\tclock_gettime(CLOCK_MONOTONIC, &ia->created);\n\n#if defined(__FreeBSD__) || defined(__DragonFly__)\n\tt = ia->created.tv_sec;\n#else\n\tt = time(NULL);\n#endif\n\n\tlifetime = &ifr6.ifr_ifru.ifru_lifetime;\n\tif (lifetime->ia6t_preferred)\n\t\tia->prefix_pltime = (uint32_t)(lifetime->ia6t_preferred -\n\t\t    MIN(t, lifetime->ia6t_preferred));\n\telse\n\t\tia->prefix_pltime = ND6_INFINITE_LIFETIME;\n\tif (lifetime->ia6t_expire) {\n\t\tia->prefix_vltime = (uint32_t)(lifetime->ia6t_expire -\n\t\t    MIN(t, lifetime->ia6t_expire));\n\t\t/* Calculate the created time */\n\t\tia->created.tv_sec -= lifetime->ia6t_vltime - ia->prefix_vltime;\n\t} else\n\t\tia->prefix_vltime = ND6_INFINITE_LIFETIME;\n\treturn 0;\n}\n#endif\n\nstatic int\nif_announce(struct dhcpcd_ctx *ctx, const struct if_announcemsghdr *ifan)\n{\n\tif (ifan->ifan_msglen < sizeof(*ifan)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tswitch (ifan->ifan_what) {\n\tcase IFAN_ARRIVAL:\n\t\treturn dhcpcd_handleinterface(ctx, 1, ifan->ifan_name);\n\tcase IFAN_DEPARTURE:\n\t\treturn dhcpcd_handleinterface(ctx, -1, ifan->ifan_name);\n\t}\n\n\treturn 0;\n}\n\nstatic int\nif_ifinfo(struct dhcpcd_ctx *ctx, const struct if_msghdr *ifm)\n{\n\tstruct interface *ifp;\n\tint link_state;\n\n\tif (ifm->ifm_msglen < sizeof(*ifm)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tif ((ifp = if_findindex(ctx->ifaces, ifm->ifm_index)) == NULL)\n\t\treturn 0;\n\n\tifp->mtu = if_getmtu(ifp);\n\tlink_state = if_carrier(ifp, &ifm->ifm_data);\n\tdhcpcd_handlecarrier(ifp, link_state, (unsigned int)ifm->ifm_flags);\n\treturn 0;\n}\n\nstatic int\nif_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)\n{\n\tstruct rt rt;\n\n\tif (rtm->rtm_msglen < sizeof(*rtm)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\t/* Ignore errors. */\n\tif (rtm->rtm_errno != 0)\n\t\treturn 0;\n\n\t/* Ignore messages from ourself. */\n#ifdef PRIVSEP\n\tif (ctx->ps_root != NULL) {\n\t\tif (rtm->rtm_pid == ctx->ps_root->psp_pid)\n\t\t\treturn 0;\n\t}\n#endif\n\n\tif (if_copyrt(ctx, &rt, rtm) == -1)\n\t\treturn errno == ENOTSUP ? 0 : -1;\n\n#ifdef INET6\n\t/*\n\t * BSD announces host routes.\n\t * As such, we should be notified of reachability by its\n\t * existance with a hardware address.\n\t * Ensure we don't call this for a newly incomplete state.\n\t */\n\tif (rt.rt_dest.sa_family == AF_INET6 &&\n\t    (rt.rt_flags & RTF_HOST || rtm->rtm_type == RTM_MISS) &&\n\t    !(rtm->rtm_type == RTM_ADD && !(rt.rt_dflags & RTDF_GATELINK))) {\n\t\tbool reachable;\n\n\t\treachable = (rtm->rtm_type == RTM_ADD ||\n\t\t\t\trtm->rtm_type == RTM_CHANGE) &&\n\t\t    rt.rt_dflags & RTDF_GATELINK;\n\t\tipv6nd_neighbour(ctx, &rt.rt_ss_dest.sin6.sin6_addr, reachable);\n\t}\n#endif\n\n\tif (rtm->rtm_type != RTM_MISS && if_realroute(rtm))\n\t\trt_recvrt(rtm->rtm_type, &rt, rtm->rtm_pid);\n\treturn 0;\n}\n\nstatic int\nif_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam)\n{\n\tstruct interface *ifp;\n\tconst struct sockaddr *rti_info[RTAX_MAX];\n\tint flags;\n\tpid_t pid;\n\n\tif (ifam->ifam_msglen < sizeof(*ifam)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n#ifdef HAVE_IFAM_PID\n\t/* Ignore address deletions from ourself.\n\t * We need to process address flag changes though. */\n\tif (ifam->ifam_type == RTM_DELADDR) {\n#ifdef PRIVSEP\n\t\tif (ctx->ps_root != NULL) {\n\t\t\tif (ifam->ifam_pid == ctx->ps_root->psp_pid)\n\t\t\t\treturn 0;\n\t\t} else\n#endif\n\t\t\t/* address management is done via ioctl,\n\t\t\t * so SO_USELOOPBACK has no effect,\n\t\t\t * so we do need to check the pid. */\n\t\t\tif (ifam->ifam_pid == getpid())\n\t\t\t\treturn 0;\n\t}\n\tpid = ifam->ifam_pid;\n#else\n\tpid = 0;\n#endif\n\n\tif (~ifam->ifam_addrs & RTA_IFA)\n\t\treturn 0;\n\tif ((ifp = if_findindex(ctx->ifaces, ifam->ifam_index)) == NULL)\n\t\treturn 0;\n\n\tif (get_addrs(ifam->ifam_addrs, (const char *)ifam + sizeof(*ifam),\n\t\tifam->ifam_msglen - sizeof(*ifam), rti_info) == -1)\n\t\treturn -1;\n\n\t/* All BSD's set IFF_UP on the interface when adding an address.\n\t * But not all BSD's emit this via RTM_IFINFO when they do this ... */\n\tif (ifam->ifam_type == RTM_NEWADDR && !(ifp->flags & IFF_UP)) {\n\t\tstruct ifreq ifr = { .ifr_flags = 0 };\n\n\t\t/* Don't blindly assume the interface is up though.\n\t\t * We might get the address via a state change. */\n\t\tstrlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));\n\t\tif (ioctl(ctx->pf_inet_fd, SIOCGIFFLAGS, &ifr) == -1)\n\t\t\treturn -1;\n\t\tif (ifr.ifr_flags & IFF_UP)\n\t\t\tdhcpcd_handlecarrier(ifp, ifp->carrier,\n\t\t\t    ifp->flags | IFF_UP);\n\t}\n\n\tswitch (rti_info[RTAX_IFA]->sa_family) {\n\tcase AF_LINK: {\n\t\tstruct sockaddr_dl sdl;\n\n#ifdef RTM_CHGADDR\n\t\tif (ifam->ifam_type != RTM_CHGADDR)\n\t\t\tbreak;\n#else\n\t\tif (ifam->ifam_type != RTM_NEWADDR)\n\t\t\tbreak;\n#endif\n\t\tmemcpy(&sdl, rti_info[RTAX_IFA], rti_info[RTAX_IFA]->sa_len);\n\t\tdhcpcd_handlehwaddr(ifp, ifp->hwtype, CLLADDR(&sdl),\n\t\t    sdl.sdl_alen);\n\t\tbreak;\n\t}\n#ifdef INET\n\tcase AF_INET:\n\tcase 255: /* FIXME: Why 255? */\n\t{\n\t\tconst struct sockaddr_in *sin;\n\t\tstruct in_addr addr, mask, bcast;\n\n\t\tsin = (const void *)rti_info[RTAX_IFA];\n\t\taddr.s_addr = sin != NULL && sin->sin_family == AF_INET ?\n\t\t    sin->sin_addr.s_addr :\n\t\t    INADDR_ANY;\n\t\tsin = (const void *)rti_info[RTAX_NETMASK];\n\t\tmask.s_addr = sin != NULL && sin->sin_family == AF_INET ?\n\t\t    sin->sin_addr.s_addr :\n\t\t    INADDR_ANY;\n\t\tsin = (const void *)rti_info[RTAX_BRD];\n\t\tbcast.s_addr = sin != NULL && sin->sin_family == AF_INET ?\n\t\t    sin->sin_addr.s_addr :\n\t\t    INADDR_ANY;\n\n\t\t/*\n\t\t * NetBSD-7 and older send an invalid broadcast address.\n\t\t * So we need to query the actual address to get\n\t\t * the right one.\n\t\t * We can also use this to test if the address\n\t\t * has really been added or deleted.\n\t\t */\n#ifdef SIOCGIFALIAS\n\t\tstruct in_aliasreq ifra;\n\n\t\tmemset(&ifra, 0, sizeof(ifra));\n\t\tstrlcpy(ifra.ifra_name, ifp->name, sizeof(ifra.ifra_name));\n\t\tifra.ifra_addr.sin_family = AF_INET;\n\t\tifra.ifra_addr.sin_len = sizeof(ifra.ifra_addr);\n\t\tifra.ifra_addr.sin_addr = addr;\n\t\tif (ioctl(ctx->pf_inet_fd, SIOCGIFALIAS, &ifra) == -1) {\n\t\t\tif (errno != ENXIO && errno != EADDRNOTAVAIL)\n\t\t\t\tlogerr(\"%s: SIOCGIFALIAS\", __func__);\n\t\t\tif (ifam->ifam_type != RTM_DELADDR)\n\t\t\t\tbreak;\n\t\t} else {\n\t\t\tif (ifam->ifam_type == RTM_DELADDR)\n\t\t\t\tbreak;\n#if defined(__NetBSD_Version__) && __NetBSD_Version__ < 800000000\n\t\t\tbcast = ifra.ifra_broadaddr.sin_addr;\n#endif\n\t\t}\n#else\n#warning No SIOCGIFALIAS support\n\t\t/*\n\t\t * No SIOCGIFALIAS? That sucks!\n\t\t * This makes this call very heavy weight, but we\n\t\t * really need to know if the message is late or not.\n\t\t */\n\t\tconst struct sockaddr *sa;\n\t\tstruct ifaddrs *ifaddrs = NULL, *ifa;\n\n\t\tsa = rti_info[RTAX_IFA];\n#ifdef PRIVSEP_GETIFADDRS\n\t\tif (IN_PRIVSEP(ctx)) {\n\t\t\tif (ps_root_getifaddrs(ctx, &ifaddrs) == -1) {\n\t\t\t\tlogerr(\"ps_root_getifaddrs\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else\n#endif\n\t\t    if (getifaddrs(&ifaddrs) == -1) {\n\t\t\tlogerr(\"getifaddrs\");\n\t\t\tbreak;\n\t\t}\n\t\tfor (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {\n\t\t\tif (ifa->ifa_addr == NULL)\n\t\t\t\tcontinue;\n\t\t\tif (sa_cmp(ifa->ifa_addr, sa) == 0 &&\n\t\t\t    strcmp(ifa->ifa_name, ifp->name) == 0)\n\t\t\t\tbreak;\n\t\t}\n#ifdef PRIVSEP_GETIFADDRS\n\t\tif (IN_PRIVSEP(ctx))\n\t\t\tfree(ifaddrs);\n\t\telse\n#endif\n\t\t\tfreeifaddrs(ifaddrs);\n\t\tif (ifam->ifam_type == RTM_DELADDR) {\n\t\t\tif (ifa != NULL)\n\t\t\t\tbreak;\n\t\t} else {\n\t\t\tif (ifa == NULL)\n\t\t\t\tbreak;\n\t\t}\n#endif\n\n#ifdef HAVE_IFAM_ADDRFLAGS\n\t\tflags = ifam->ifam_addrflags;\n#else\n\t\tflags = 0;\n#endif\n\n\t\tipv4_handleifa(ctx, ifam->ifam_type, NULL, ifp->name, &addr,\n\t\t    &mask, &bcast, flags, pid);\n\t\tbreak;\n\t}\n#endif\n#ifdef INET6\n\tcase AF_INET6: {\n\t\tstruct in6_addr addr6, mask6;\n\t\tconst struct in6_addr *dstaddr6;\n\t\tconst struct sockaddr_in6 *sin6;\n\n\t\tsin6 = (const void *)rti_info[RTAX_IFA];\n\t\taddr6 = sin6->sin6_addr;\n\t\tsin6 = (const void *)rti_info[RTAX_NETMASK];\n\t\tmask6 = sin6->sin6_addr;\n\t\tsin6 = (const void *)rti_info[RTAX_BRD];\n\t\tdstaddr6 = sin6 ? &sin6->sin6_addr : NULL;\n\n\t\t/*\n\t\t * If the address was deleted, lets check if it's\n\t\t * a late message and it still exists (maybe modified).\n\t\t * If so, ignore it as deleting an address causes\n\t\t * dhcpcd to drop any lease to which it belongs.\n\t\t * Also check an added address was really added.\n\t\t */\n\t\tflags = if_addrflags6(ifp, &addr6, NULL);\n\t\tif (flags == -1) {\n\t\t\tif (errno != ENXIO && errno != EADDRNOTAVAIL)\n\t\t\t\tlogerr(\"%s: if_addrflags6\", __func__);\n\t\t\tif (ifam->ifam_type != RTM_DELADDR)\n\t\t\t\tbreak;\n\t\t\tflags = 0;\n\t\t} else if (ifam->ifam_type == RTM_DELADDR)\n\t\t\tbreak;\n\n#ifdef __KAME__\n\t\tif (IN6_IS_ADDR_LINKLOCAL(&addr6))\n\t\t\t/* Remove the scope from the address */\n\t\t\taddr6.s6_addr[2] = addr6.s6_addr[3] = '\\0';\n#endif\n\n\t\tipv6_handleifa(ctx, ifam->ifam_type, NULL, ifp->name, &addr6,\n\t\t    ipv6_prefixlen(&mask6), dstaddr6, flags, pid);\n\t\tbreak;\n\t}\n#endif\n\t}\n\n\treturn 0;\n}\n\nstatic int\nif_dispatch(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)\n{\n\tif (rtm->rtm_version != RTM_VERSION)\n\t\treturn 0;\n\n\tswitch (rtm->rtm_type) {\n#ifdef RTM_IFANNOUNCE\n\tcase RTM_IFANNOUNCE:\n\t\treturn if_announce(ctx, (const void *)rtm);\n#endif\n\tcase RTM_IFINFO:\n\t\treturn if_ifinfo(ctx, (const void *)rtm);\n\tcase RTM_ADD:\t /* FALLTHROUGH */\n\tcase RTM_CHANGE: /* FALLTHROUGH */\n\tcase RTM_DELETE: /* FALLTHROUGH */\n\tcase RTM_MISS:\n\t\treturn if_rtm(ctx, (const void *)rtm);\n#ifdef RTM_CHGADDR\n\tcase RTM_CHGADDR: /* FALLTHROUGH */\n#endif\n\tcase RTM_DELADDR: /* FALLTHROUGH */\n\tcase RTM_NEWADDR:\n\t\treturn if_ifa(ctx, (const void *)rtm);\n#ifdef RTM_DESYNC\n\tcase RTM_DESYNC:\n\t\tdhcpcd_linkoverflow(ctx);\n#elif !defined(SO_RERROR)\n#warning cannot detect route socket overflow within kernel\n#endif\n\t}\n\n\treturn 0;\n}\n\nstatic int\nif_missfilter0(struct dhcpcd_ctx *ctx, struct interface *ifp,\n    struct sockaddr *sa)\n{\n\tsize_t salen = (size_t)RT_ROUNDUP(sa->sa_len);\n\tsize_t newlen = ctx->rt_missfilterlen + salen;\n\tsize_t diff = salen - (sa->sa_len);\n\tuint8_t *cp;\n\n\tif (ctx->rt_missfiltersize < newlen) {\n\t\tvoid *n = realloc(ctx->rt_missfilter, newlen);\n\t\tif (n == NULL)\n\t\t\treturn -1;\n\t\tctx->rt_missfilter = n;\n\t\tctx->rt_missfiltersize = newlen;\n\t}\n\n#ifdef INET6\n\tif (sa->sa_family == AF_INET6)\n\t\tipv6_setscope(satosin6(sa), ifp->index);\n#else\n\tUNUSED(ifp);\n#endif\n\n\tcp = ctx->rt_missfilter + ctx->rt_missfilterlen;\n\tmemcpy(cp, sa, sa->sa_len);\n\tif (diff != 0)\n\t\tmemset(cp + sa->sa_len, 0, diff);\n\tctx->rt_missfilterlen += salen;\n\n#ifdef INET6\n\tif (sa->sa_family == AF_INET6)\n\t\tipv6_setscope(satosin6(sa), 0);\n#endif\n\n\treturn 0;\n}\n\nint\nif_missfilter(struct interface *ifp, struct sockaddr *sa)\n{\n\treturn if_missfilter0(ifp->ctx, ifp, sa);\n}\n\nint\nif_missfilter_apply(struct dhcpcd_ctx *ctx)\n{\n#ifdef RO_MISSFILTER\n\tif (ctx->rt_missfilterlen == 0) {\n\t\tstruct sockaddr sa = {\n\t\t\t.sa_family = AF_UNSPEC,\n\t\t\t.sa_len = sizeof(sa),\n\t\t};\n\n\t\tif (if_missfilter0(ctx, NULL, &sa) == -1)\n\t\t\treturn -1;\n\t}\n\n\treturn setsockopt(ctx->link_fd, PF_ROUTE, RO_MISSFILTER,\n\t    ctx->rt_missfilter, (socklen_t)ctx->rt_missfilterlen);\n#else\n#warning kernel does not support RTM_MISS DST filtering\n\tUNUSED(ctx);\n\terrno = ENOTSUP;\n\treturn -1;\n#endif\n}\n\n__CTASSERT(offsetof(struct rt_msghdr, rtm_msglen) == 0);\nint\nif_handlelink(struct dhcpcd_ctx *ctx)\n{\n\tstruct rtm rtm;\n\tssize_t len;\n\n\tlen = read(ctx->link_fd, &rtm, sizeof(rtm));\n\tif (len == -1)\n\t\treturn -1;\n\tif (len == 0)\n\t\treturn 0;\n\tif ((size_t)len < sizeof(rtm.hdr.rtm_msglen) ||\n\t    len != rtm.hdr.rtm_msglen) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\t/*\n\t * Coverity thinks that the data could be tainted from here.\n\t * I have no idea how because the length of the data we read\n\t * is guarded by len and checked to match rtm_msglen.\n\t * The issue seems to be related to extracting the addresses\n\t * at the end of the header, but seems to have no issues with the\n\t * equivalent call in if_initrt.\n\t */\n\t/* coverity[tainted_data] */\n\treturn if_dispatch(ctx, &rtm.hdr);\n}\n\n#ifndef SYS_NMLN /* OSX */\n#define SYS_NMLN __SYS_NAMELEN\n#endif\n#ifndef HW_MACHINE_ARCH\n#ifdef HW_MODEL /* OpenBSD */\n#define HW_MACHINE_ARCH HW_MODEL\n#endif\n#endif\nint\nif_machinearch(char *str, size_t len)\n{\n\tint mib[2] = { CTL_HW, HW_MACHINE_ARCH };\n\n\treturn sysctl(mib, sizeof(mib) / sizeof(mib[0]), str, &len, NULL, 0);\n}\n\n#ifdef INET6\n#if (defined(IPV6CTL_ACCEPT_RTADV) && !defined(ND6_IFF_ACCEPT_RTADV))\n#define get_inet6_sysctl(code)\t    inet6_sysctl(code, 0, 0)\n#define set_inet6_sysctl(code, val) inet6_sysctl(code, val, 1)\nstatic int\ninet6_sysctl(int code, int val, int action)\n{\n\tint mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };\n\tsize_t size;\n\n\tmib[3] = code;\n\tsize = sizeof(val);\n\tif (action) {\n\t\tif (sysctl(mib, __arraycount(mib), NULL, 0, &val, size) == -1)\n\t\t\treturn -1;\n\t\treturn 0;\n\t}\n\tif (sysctl(mib, __arraycount(mib), &val, &size, NULL, 0) == -1)\n\t\treturn -1;\n\treturn val;\n}\n#endif\n\nint\nif_applyra(const struct ra *rap)\n{\n#ifdef SIOCSIFINFO_IN6\n\tstruct in6_ndireq nd = { .ndi.chlim = 0 };\n\tstruct dhcpcd_ctx *ctx = rap->iface->ctx;\n\tint error;\n\n\tstrlcpy(nd.ifname, rap->iface->name, sizeof(nd.ifname));\n\n#ifdef IPV6CTL_ACCEPT_RTADV\n\tstruct priv *priv = ctx->priv;\n\n\t/*\n\t * NetBSD changed SIOCSIFINFO_IN6 to NOT set flags when kernel\n\t * RA was removed, however both FreeBSD and DragonFlyBSD still do.\n\t * linkmtu was also removed.\n\t * Hopefully this guard will still work if either remove kernel RA.\n\t */\n\tif (ioctl(priv->pf_inet6_fd, SIOCGIFINFO_IN6, &nd, sizeof(nd)) == -1)\n\t\treturn -1;\n\n\tnd.ndi.linkmtu = rap->mtu;\n#endif\n\n\tnd.ndi.chlim = rap->hoplimit;\n\tnd.ndi.retrans = rap->retrans;\n\tnd.ndi.basereachable = rap->reachable;\n\terror = if_ioctl6(ctx, SIOCSIFINFO_IN6, &nd, sizeof(nd));\n#ifdef IPV6CTL_ACCEPT_RTADV\n\tif (error == -1 && errno == EINVAL) {\n\t\t/*\n\t\t * Very likely that this is caused by a dodgy MTU\n\t\t * setting specific to the interface.\n\t\t * Let's set it to \"unspecified\" and try again.\n\t\t * Doesn't really matter as we fix the MTU against the\n\t\t * routes we add as not all OS support SIOCSIFINFO_IN6.\n\t\t */\n\t\tnd.ndi.linkmtu = 0;\n\t\terror = if_ioctl6(ctx, SIOCSIFINFO_IN6, &nd, sizeof(nd));\n\t}\n#endif\n\treturn error;\n#else\n#warning OS does not allow setting of RA bits hoplimit, retrans or reachable\n\tUNUSED(rap);\n\treturn 0;\n#endif\n}\n\n#ifdef SIOCIFAFATTACH\nstatic int\nif_af_attach(const struct interface *ifp, int af)\n{\n\tstruct if_afreq ifar = { .ifar_af = af };\n\n\tstrlcpy(ifar.ifar_name, ifp->name, sizeof(ifar.ifar_name));\n\treturn if_ioctl6(ifp->ctx, SIOCIFAFATTACH, &ifar, sizeof(ifar));\n}\n#endif\n\n#ifdef SIOCGIFXFLAGS\nstatic int\nif_set_ifxflags(const struct interface *ifp)\n{\n\tstruct ifreq ifr;\n\tint flags;\n\tstruct priv *priv = ifp->ctx->priv;\n\n\tstrlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));\n\tif (ioctl(priv->pf_inet6_fd, SIOCGIFXFLAGS, &ifr) == -1)\n\t\treturn -1;\n\tflags = ifr.ifr_flags;\n#ifdef IFXF_NOINET6\n\tflags &= ~IFXF_NOINET6;\n#endif\n\t/*\n\t * If not doing autoconf, don't disable the kernel from doing it.\n\t * If we need to, we should have another option actively disable it.\n\t *\n\t * OpenBSD moved from kernel based SLAAC to userland via slaacd(8).\n\t * It has a similar featureset to dhcpcd such as stable private\n\t * addresses, but lacks the ability to handle DNS inside the RA\n\t * which is a serious shortfall in this day and age.\n\t * Appease their user base by working alongside slaacd(8) if\n\t * dhcpcd is instructed not to do auto configuration of addresses.\n\t */\n#if defined(ND6_IFF_ACCEPT_RTADV)\n#define BSD_AUTOCONF DHCPCD_IPV6RS\n#else\n#define BSD_AUTOCONF DHCPCD_IPV6RA_AUTOCONF\n#endif\n\tif (ifp->options->options & BSD_AUTOCONF)\n\t\tflags &= ~IFXF_AUTOCONF6;\n\tif (ifr.ifr_flags == flags)\n\t\treturn 0;\n\tifr.ifr_flags = flags;\n\treturn if_ioctl6(ifp->ctx, SIOCSIFXFLAGS, &ifr, sizeof(ifr));\n}\n#endif\n\n/* OpenBSD removed ND6 flags entirely, so we need to check for their\n * existance. */\n#if defined(ND6_IFF_AUTO_LINKLOCAL) || defined(ND6_IFF_PERFORMNUD) ||   \\\n    defined(ND6_IFF_ACCEPT_RTADV) || defined(ND6_IFF_OVERRIDE_RTADV) || \\\n    defined(ND6_IFF_IFDISABLED)\n#define ND6_NDI_FLAGS\n#endif\n\nvoid\nif_disable_rtadv(void)\n{\n#if defined(IPV6CTL_ACCEPT_RTADV) && !defined(ND6_IFF_ACCEPT_RTADV)\n\tint ra = get_inet6_sysctl(IPV6CTL_ACCEPT_RTADV);\n\n\tif (ra == -1) {\n\t\tif (errno != ENOENT)\n\t\t\tlogerr(\"IPV6CTL_ACCEPT_RTADV\");\n\t\telse if (ra != 0)\n\t\t\tif (set_inet6_sysctl(IPV6CTL_ACCEPT_RTADV, 0) == -1)\n\t\t\t\tlogerr(\"IPV6CTL_ACCEPT_RTADV\");\n\t}\n#endif\n}\n\nvoid\nif_setup_inet6(const struct interface *ifp)\n{\n#ifdef ND6_NDI_FLAGS\n\tstruct priv *priv;\n\tint s;\n\tstruct in6_ndireq nd;\n\tint flags;\n\n\tpriv = (struct priv *)ifp->ctx->priv;\n\ts = priv->pf_inet6_fd;\n\n\tmemset(&nd, 0, sizeof(nd));\n\tstrlcpy(nd.ifname, ifp->name, sizeof(nd.ifname));\n\tif (ioctl(s, SIOCGIFINFO_IN6, &nd) == -1)\n\t\tlogerr(\"%s: SIOCGIFINFO_FLAGS\", ifp->name);\n\tflags = (int)nd.ndi.flags;\n\n#ifdef ND6_IFF_AUTO_LINKLOCAL\n\t/* Unlike the kernel, dhcpcd make make a stable private address. */\n\tflags &= ~ND6_IFF_AUTO_LINKLOCAL;\n#endif\n\n#ifdef ND6_IFF_PERFORMNUD\n\t/* NUD is kind of essential. */\n\tflags |= ND6_IFF_PERFORMNUD;\n#endif\n\n#ifdef ND6_IFF_IFDISABLED\n\t/* Ensure the interface is not disabled. */\n\tflags &= ~ND6_IFF_IFDISABLED;\n#endif\n\n\t/*\n\t * If not doing autoconf, don't disable the kernel from doing it.\n\t * If we need to, we should have another option actively disable it.\n\t */\n#ifdef ND6_IFF_ACCEPT_RTADV\n\tif (ifp->options->options & DHCPCD_IPV6RS)\n\t\tflags &= ~ND6_IFF_ACCEPT_RTADV;\n#ifdef ND6_IFF_OVERRIDE_RTADV\n\tif (ifp->options->options & DHCPCD_IPV6RS)\n\t\tflags |= ND6_IFF_OVERRIDE_RTADV;\n#endif\n#endif\n\n\tif (nd.ndi.flags != (uint32_t)flags) {\n\t\tnd.ndi.flags = (uint32_t)flags;\n\t\tif (if_ioctl6(ifp->ctx, SIOCSIFINFO_FLAGS, &nd, sizeof(nd)) ==\n\t\t    -1)\n\t\t\tlogerr(\"%s: SIOCSIFINFO_FLAGS\", ifp->name);\n\t}\n#endif /* ND6_NDI_FLAGS */\n\n\t/* Enabling IPv6 by whatever means must be the\n\t * last action undertaken to ensure kernel RS and\n\t * LLADDR auto configuration are disabled where applicable. */\n#ifdef SIOCIFAFATTACH\n\tif (if_af_attach(ifp, AF_INET6) == -1)\n\t\tlogerr(\"%s: if_af_attach\", ifp->name);\n#endif\n\n#ifdef SIOCGIFXFLAGS\n\tif (if_set_ifxflags(ifp) == -1)\n\t\tlogerr(\"%s: set_ifxflags\", ifp->name);\n#endif\n\n#ifdef SIOCSRTRFLUSH_IN6\n\t/* Flush the kernel knowledge of advertised routers\n\t * and prefixes so the kernel does not expire prefixes\n\t * and default routes we are trying to own. */\n\tif (ifp->options->options & DHCPCD_IPV6RS) {\n\t\tstruct in6_ifreq ifr;\n\n\t\tmemset(&ifr, 0, sizeof(ifr));\n\t\tstrlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));\n\t\tif (if_ioctl6(ifp->ctx, SIOCSRTRFLUSH_IN6, &ifr, sizeof(ifr)) ==\n\t\t\t-1 &&\n\t\t    errno != ENOTSUP && errno != ENOTTY)\n\t\t\tlogwarn(\"SIOCSRTRFLUSH_IN6 %d\", errno);\n#ifdef SIOCSPFXFLUSH_IN6\n\t\tif (if_ioctl6(ifp->ctx, SIOCSPFXFLUSH_IN6, &ifr, sizeof(ifr)) ==\n\t\t\t-1 &&\n\t\t    errno != ENOTSUP && errno != ENOTTY)\n\t\t\tlogwarn(\"SIOCSPFXFLUSH_IN6\");\n#endif\n\t}\n#endif\n}\n#endif\n"
  },
  {
    "path": "src/if-linux-wext.c",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2009-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n/*\n * THIS IS A NASTY HACK THAT SHOULD NEVER HAVE HAPPENED\n * Basically we cannot include linux/if.h and net/if.h because\n * they have conflicting structures.\n * Sadly, linux/wireless.h includes linux/if.h all the time.\n * Some kernel-header installs fix this and some do not.\n * This file solely exists for those who do not.\n *\n * We *could* include wireless.h as that is designed for userspace,\n * but that then depends on the correct version of wireless-tools being\n * installed which isn't always the case.\n */\n\n#include <sys/ioctl.h>\n#include <sys/socket.h>\n\n#include <linux/rtnetlink.h>\n#include <linux/types.h>\n/* Support older kernels */\n#ifdef IFLA_WIRELESS\n#include <linux/if.h>\n#include <linux/wireless.h>\n#else\n#define IFLA_WIRELESS (IFLA_MASTER + 1)\n#endif\n\n#include <string.h>\n#include <unistd.h>\n\n#include \"config.h\"\n\n/* We can't include if.h or dhcpcd.h because\n * they would pull in net/if.h, which defeats the purpose of this hack. */\n#define IF_SSIDLEN 32\nint if_getssid_wext(const char *ifname, uint8_t *ssid);\n\nint\nif_getssid_wext(const char *ifname, uint8_t *ssid)\n{\n#ifdef SIOCGIWESSID\n\tint s, retval;\n\tstruct iwreq iwr;\n\n\tif ((s = socket(PF_INET, SOCK_DGRAM, 0)) == -1)\n\t\treturn -1;\n\tmemset(&iwr, 0, sizeof(iwr));\n\tstrlcpy(iwr.ifr_name, ifname, sizeof(iwr.ifr_name));\n\tiwr.u.essid.pointer = ssid;\n\tiwr.u.essid.length = IF_SSIDLEN;\n\n\tif (ioctl(s, SIOCGIWESSID, &iwr) == 0)\n\t\tretval = iwr.u.essid.length;\n\telse\n\t\tretval = -1;\n\tclose(s);\n\treturn retval;\n#else\n\t/* Stop gcc warning about unused parameters */\n\tifname = ssid;\n\treturn -1;\n#endif\n}\n"
  },
  {
    "path": "src/if-linux.c",
    "content": "/*\n * Linux interface driver for dhcpcd\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/types.h>\n#include <sys/param.h>\n#include <sys/ioctl.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n\n#include <net/if.h>\n#include <net/route.h>\n#include <netinet/in.h>\n#include <netinet/in_systm.h>\n\n#include <arpa/inet.h>\n#include <asm/types.h> /* Needed for 2.4 kernels */\n#include <linux/filter.h>\n#include <linux/icmpv6.h>\n#include <linux/if_addr.h>\n#include <linux/if_link.h>\n#include <linux/if_packet.h>\n#include <linux/if_tun.h>\n#include <linux/if_vlan.h>\n#include <linux/netlink.h>\n#include <linux/rtnetlink.h>\n#include <linux/sockios.h>\n\n/* musl has its own definition of struct ethhdr, so only include\n * netinet/if_ether.h on systems with GLIBC.  For the ARPHRD constants,\n * we must include linux/if_arp.h instead. */\n#if defined(__GLIBC__)\n#include <netinet/if_ether.h>\n#else\n#include <linux/if_arp.h>\n#endif\n\n#include <ctype.h>\n#include <dirent.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n#include <unistd.h>\n\n#include \"bpf.h\"\n#include \"common.h\"\n#include \"config.h\"\n#include \"dev.h\"\n#include \"dhcp.h\"\n#include \"if.h\"\n#include \"ipv4.h\"\n#include \"ipv4ll.h\"\n#include \"ipv6.h\"\n#include \"ipv6nd.h\"\n#include \"logerr.h\"\n#include \"privsep.h\"\n#include \"route.h\"\n#include \"sa.h\"\n\n#ifdef HAVE_NL80211_H\n#include <linux/genetlink.h>\n#include <linux/nl80211.h>\n#else\nint if_getssid_wext(const char *ifname, uint8_t *ssid);\n#endif\n\n/* Support older kernels */\n#ifndef IFLA_WIRELESS\n#define IFLA_WIRELESS (IFLA_MASTER + 1)\n#endif\n\n/* Buggy CentOS and RedHat */\n#ifndef SOL_NETLINK\n#define SOL_NETLINK 270\n#endif\n\n/*\n * We cannot include linux/if.h due to the need to support old kernels.\n * IFLA_LINKINFO is a define which was added after IFF_LOWER_UP and\n * IFF_DORMANT.\n * So we miss a few versions, but it's the best we can do.\n */\n#ifdef IFLA_LINKINFO\n#ifndef IFF_LOWER_UP\n#define IFF_LOWER_UP 0x10000\n#endif\n#ifndef IFF_DORMANT\n#define IFF_DORMANT 0x20000\n#endif\n#endif\n\n/* Linux defines IFA_FLAGS as an enum.\n * For older kernels we know it exists if IFA_F_MANAGETEMPADDR does. */\n#ifdef IFA_F_MANAGETEMPADDR\n#define IFA_FLAGS IFA_FLAGS\n#endif\n\n/*\n * Someone should fix kernel headers for clang alignment warnings.\n * But this is unlikely.\n * https://www.spinics.net/lists/netdev/msg646934.html\n */\n\n#undef NLA_ALIGNTO\n#undef NLA_ALIGN\n#undef NLA_HDRLEN\n#define NLA_ALIGNTO    4U\n#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))\n#define NLA_HDRLEN     ((int)NLA_ALIGN(sizeof(struct nlattr)))\n\n#undef IFA_RTA\n#define IFA_RTA(r)                                 \\\n\t((struct rtattr *)(void *)(((char *)(r)) + \\\n\t    NLMSG_ALIGN(sizeof(struct ifaddrmsg))))\n#undef IFLA_RTA\n#define IFLA_RTA(r)                                \\\n\t((struct rtattr *)(void *)(((char *)(r)) + \\\n\t    NLMSG_ALIGN(sizeof(struct ifinfomsg))))\n#undef NLMSG_NEXT\n#define NLMSG_NEXT(nlh, len)                              \\\n\t((len) -= NLMSG_ALIGN((nlh)->nlmsg_len),          \\\n\t    (struct nlmsghdr *)(void *)(((char *)(nlh)) + \\\n\t\tNLMSG_ALIGN((nlh)->nlmsg_len)))\n#undef RTM_RTA\n#define RTM_RTA(r) (void *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg)))\n#undef RTA_NEXT\n#define RTA_NEXT(rta, attrlen)                          \\\n\t((attrlen) -= RTA_ALIGN((rta)->rta_len),        \\\n\t    (struct rtattr *)(void *)(((char *)(rta)) + \\\n\t\tRTA_ALIGN((rta)->rta_len)))\n\n/* We need this to send a broadcast for InfiniBand.\n * Our old code used sendto, but our new code writes to a raw BPF socket.\n * What header structure does IPoIB use? */\n#if 0\n/* Broadcast address for IPoIB */\nstatic const uint8_t ipv4_bcast_addr[] = {\n\t0x00, 0xff, 0xff, 0xff,\n\t0xff, 0x12, 0x40, 0x1b, 0x00, 0x00, 0x00, 0x00,\n\t0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff\n};\n#endif\n\nstatic int if_addressexists(struct interface *, struct in_addr *);\n\n#define PROC_PROMOTE \"/proc/sys/net/ipv4/conf/%s/promote_secondaries\"\n#define SYS_BRIDGE   \"/sys/class/net/%s/bridge/bridge_id\"\n#define SYS_LAYER2   \"/sys/class/net/%s/device/layer2\"\n#define SYS_TUNTAP   \"/sys/class/net/%s/tun_flags\"\n\n#if defined(__aarch64__)\nstatic const char *mproc = \"AArch64\";\nint\nif_machinearch(char *str, size_t len)\n{\n\treturn snprintf(str, len, \"%s\", mproc);\n}\n#else\nstatic const char *mproc =\n#if defined(__alpha__)\n    \"system type\"\n#elif defined(__arm__)\n    \"Hardware\"\n#elif defined(__avr32__)\n    \"cpu family\"\n#elif defined(__bfin__)\n    \"BOARD Name\"\n#elif defined(__cris__)\n    \"cpu model\"\n#elif defined(__frv__)\n    \"System\"\n#elif defined(__hppa__)\n    \"model\"\n#elif defined(__i386__) || defined(__x86_64__)\n    \"vendor_id\"\n#elif defined(__ia64__)\n    \"vendor\"\n#elif defined(__m68k__)\n    \"MMU\"\n#elif defined(__mips__)\n    \"system type\"\n#elif defined(__powerpc__) || defined(__powerpc64__)\n    \"machine\"\n#elif defined(__riscv)\n    \"uarch\"\n#elif defined(__s390__) || defined(__s390x__)\n    \"Manufacturer\"\n#elif defined(__sh__)\n    \"machine\"\n#elif defined(sparc) || defined(__sparc__)\n    \"cpu\"\n#elif defined(__vax__)\n    \"cpu\"\n#else\n    NULL\n#endif\n    ;\n\nint\nif_machinearch(char *str, size_t len)\n{\n\tFILE *fp;\n\tchar buf[256];\n\n\tif (mproc == NULL) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tfp = fopen(\"/proc/cpuinfo\", \"r\");\n\tif (fp == NULL)\n\t\treturn -1;\n\n\twhile (fscanf(fp, \"%255s : \", buf) != EOF) {\n\t\tif (strncmp(buf, mproc, strlen(mproc)) == 0 &&\n\t\t    fscanf(fp, \"%255s\", buf) == 1) {\n\t\t\tfclose(fp);\n\t\t\treturn snprintf(str, len, \"%s\", buf);\n\t\t}\n\t}\n\tfclose(fp);\n\terrno = ESRCH;\n\treturn -1;\n}\n#endif\n\nstatic int\ncheck_proc_int(struct dhcpcd_ctx *ctx, const char *path)\n{\n\tchar buf[64];\n\tint error, i;\n\n\tif (dhcp_readfile(ctx, path, buf, sizeof(buf)) == -1)\n\t\treturn -1;\n\ti = (int)strtoi(buf, NULL, 0, INT_MIN, INT_MAX, &error);\n\tif (error != 0 && error != ENOTSUP) {\n\t\terrno = error;\n\t\treturn -1;\n\t}\n\treturn i;\n}\n\nstatic int\ncheck_proc_uint(struct dhcpcd_ctx *ctx, const char *path, unsigned int *u)\n{\n\tchar buf[64];\n\tint error;\n\n\tif (dhcp_readfile(ctx, path, buf, sizeof(buf)) == -1)\n\t\treturn -1;\n\t*u = (unsigned int)strtou(buf, NULL, 0, 0, UINT_MAX, &error);\n\tif (error != 0 && error != ENOTSUP) {\n\t\terrno = error;\n\t\treturn error;\n\t}\n\treturn 0;\n}\n\nstatic ssize_t\nif_writepathuint(struct dhcpcd_ctx *ctx, const char *path, unsigned int val)\n{\n\tchar buf[64];\n\tint len;\n\n\tlen = snprintf(buf, sizeof(buf), \"%u\\n\", val);\n\tif (len == -1)\n\t\treturn -1;\n\treturn dhcp_writefile(ctx, path, 0664, buf, (size_t)len);\n}\n\nint\nif_init(struct interface *ifp)\n{\n\tchar path[sizeof(PROC_PROMOTE) + IF_NAMESIZE];\n\tint n;\n\n\t/* We enable promote_secondaries so that we can do this\n\t * add 192.168.1.2/24\n\t * add 192.168.1.3/24\n\t * del 192.168.1.2/24\n\t * and the subnet mask moves onto 192.168.1.3/24\n\t * This matches the behaviour of BSD which makes coding dhcpcd\n\t * a little easier as there's just one behaviour. */\n\tsnprintf(path, sizeof(path), PROC_PROMOTE, ifp->name);\n\tn = check_proc_int(ifp->ctx, path);\n\tif (n == -1)\n\t\treturn errno == ENOENT ? 0 : -1;\n\tif (n == 1)\n\t\treturn 0;\n\treturn if_writepathuint(ifp->ctx, path, 1) == -1 ? -1 : 0;\n}\n\nint\nif_conf(struct interface *ifp)\n{\n\tchar path[sizeof(SYS_LAYER2) + IF_NAMESIZE];\n\tint n;\n\n\t/* Some qeth setups require the use of the broadcast flag. */\n\tsnprintf(path, sizeof(path), SYS_LAYER2, ifp->name);\n\tn = check_proc_int(ifp->ctx, path);\n\tif (n == -1)\n\t\treturn errno == ENOENT ? 0 : -1;\n\tif (n == 0)\n\t\tifp->options->options |= DHCPCD_BROADCAST;\n\treturn 0;\n}\n\nstatic bool\nif_bridge(struct dhcpcd_ctx *ctx, const char *ifname)\n{\n\tchar path[sizeof(SYS_BRIDGE) + IF_NAMESIZE], buf[64];\n\n\tsnprintf(path, sizeof(path), SYS_BRIDGE, ifname);\n\tif (dhcp_readfile(ctx, path, buf, sizeof(buf)) == -1)\n\t\treturn false;\n\treturn true;\n}\n\nstatic bool\nif_tap(struct dhcpcd_ctx *ctx, const char *ifname)\n{\n\tchar path[sizeof(SYS_TUNTAP) + IF_NAMESIZE];\n\tunsigned int u;\n\n\tsnprintf(path, sizeof(path), SYS_TUNTAP, ifname);\n\tif (check_proc_uint(ctx, path, &u) == -1)\n\t\treturn false;\n\treturn u & IFF_TAP;\n}\n\nbool\nif_ignore(struct dhcpcd_ctx *ctx, const char *ifname)\n{\n\tif (if_tap(ctx, ifname) || if_bridge(ctx, ifname))\n\t\treturn true;\n\treturn false;\n}\n\n/* XXX work out Virtal Interface Masters */\nint\nif_vimaster(__unused struct dhcpcd_ctx *ctx, __unused const char *ifname)\n{\n\treturn 0;\n}\n\nunsigned short\nif_vlanid(const struct interface *ifp)\n{\n\tstruct vlan_ioctl_args v;\n\n\tmemset(&v, 0, sizeof(v));\n\tstrlcpy(v.device1, ifp->name, sizeof(v.device1));\n\tv.cmd = GET_VLAN_VID_CMD;\n\tif (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFVLAN, &v) != 0)\n\t\treturn 0; /* 0 means no VLANID */\n\treturn (unsigned short)v.u.VID;\n}\n\nint\nif_linksocket(struct sockaddr_nl *nl, int protocol, int flags)\n{\n\tint fd;\n\n\tfd = xsocket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC | flags, protocol);\n\tif (fd == -1)\n\t\treturn -1;\n\tnl->nl_family = AF_NETLINK;\n\tif (bind(fd, (struct sockaddr *)nl, sizeof(*nl)) == -1) {\n\t\tclose(fd);\n\t\treturn -1;\n\t}\n\treturn fd;\n}\n\nchar *\nif_getnetworknamespace(char *buf, size_t len)\n{\n\tstruct stat sb_self, sb_netns;\n\tDIR *dir;\n\tstruct dirent *de;\n\tchar file[PATH_MAX], *bufp = NULL;\n\n\tif (stat(\"/proc/self/ns/net\", &sb_self) == -1)\n\t\treturn NULL;\n\n\tdir = opendir(\"/var/run/netns\");\n\tif (dir == NULL)\n\t\treturn NULL;\n\n\twhile ((de = readdir(dir)) != NULL) {\n\t\tsnprintf(file, sizeof(file), \"/var/run/netns/%s\", de->d_name);\n\t\tif (stat(file, &sb_netns) == -1)\n\t\t\tcontinue;\n\t\tif (sb_self.st_dev != sb_netns.st_dev ||\n\t\t    sb_self.st_ino != sb_netns.st_ino)\n\t\t\tcontinue;\n\t\tstrlcpy(buf, de->d_name, len);\n\t\tbufp = buf;\n\t\tbreak;\n\t}\n\tclosedir(dir);\n\treturn bufp;\n}\n\nint\nos_init(void)\n{\n\tchar netns[PATH_MAX], *p;\n\n\tp = if_getnetworknamespace(netns, sizeof(netns));\n\tif (p != NULL)\n\t\tloginfox(\"network namespace: %s\", p);\n\n\treturn 0;\n}\n\nint\nif_opensockets_os(struct dhcpcd_ctx *ctx)\n{\n\tstruct priv *priv;\n\tstruct sockaddr_nl snl;\n\tsocklen_t len;\n#ifdef NETLINK_BROADCAST_ERROR\n\tint on = 1;\n#endif\n\n#ifdef PRIVSEP\n\tif (ctx->options & DHCPCD_PRIVSEPROOT) {\n\t\tctx->link_fd = -1;\n\t\tgoto setup_priv;\n\t}\n#endif\n\n\t/* Open the link socket first so it gets pid() for the socket.\n\t * Then open our persistent route socket so we get a unique\n\t * pid that doesn't clash with a process id for after we fork. */\n\tmemset(&snl, 0, sizeof(snl));\n\tsnl.nl_groups = RTMGRP_LINK;\n\n#ifdef INET\n\tsnl.nl_groups |= RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR;\n#endif\n#ifdef INET6\n\tsnl.nl_groups |= RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR | RTMGRP_NEIGH;\n#endif\n\n\tctx->link_fd = if_linksocket(&snl, NETLINK_ROUTE, SOCK_NONBLOCK);\n\tif (ctx->link_fd == -1)\n\t\treturn -1;\n#ifdef NETLINK_BROADCAST_ERROR\n\tif (setsockopt(ctx->link_fd, SOL_NETLINK, NETLINK_BROADCAST_ERROR, &on,\n\t\tsizeof(on)) == -1)\n\t\tlogerr(\"%s: NETLINK_BROADCAST_ERROR\", __func__);\n#endif\n\n#ifdef PRIVSEP\nsetup_priv:\n#endif\n\tif ((priv = calloc(1, sizeof(*priv))) == NULL)\n\t\treturn -1;\n\n\tctx->priv = priv;\n\tmemset(&snl, 0, sizeof(snl));\n\tpriv->route_fd = if_linksocket(&snl, NETLINK_ROUTE, 0);\n\tif (priv->route_fd == -1)\n\t\treturn -1;\n\tlen = sizeof(snl);\n\tif (getsockname(priv->route_fd, (struct sockaddr *)&snl, &len) == -1)\n\t\treturn -1;\n\tpriv->route_pid = snl.nl_pid;\n\n\tmemset(&snl, 0, sizeof(snl));\n\tpriv->generic_fd = if_linksocket(&snl, NETLINK_GENERIC, 0);\n\tif (priv->generic_fd == -1)\n\t\treturn -1;\n\n\treturn 0;\n}\n\nvoid\nif_closesockets_os(struct dhcpcd_ctx *ctx)\n{\n\tstruct priv *priv = ctx->priv;\n\n\tif (priv == NULL)\n\t\treturn;\n\n\tif (priv->route_fd != -1) {\n\t\tclose(priv->route_fd);\n\t\tpriv->route_fd = -1;\n\t}\n\tif (priv->generic_fd != -1) {\n\t\tclose(priv->generic_fd);\n\t\tpriv->generic_fd = -1;\n\t}\n\n\tfree(priv);\n\tctx->priv = NULL;\n}\n\nint\nif_setmac(struct interface *ifp, void *mac, uint8_t maclen)\n{\n\tstruct ifreq ifr = {\n\t\t.ifr_hwaddr.sa_family = ifp->hwtype,\n\t};\n\n\tif (ifp->hwlen != maclen || maclen > sizeof(ifr.ifr_hwaddr.sa_data)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tstrlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));\n\tmemcpy(ifr.ifr_hwaddr.sa_data, mac, maclen);\n\treturn if_ioctl(ifp->ctx, SIOCSIFHWADDR, &ifr, sizeof(ifr));\n}\n\nstatic int\nif_carrier_from_flags(unsigned int flags)\n{\n#ifdef IFF_LOWER_UP\n\treturn ((flags & (IFF_LOWER_UP | IFF_RUNNING)) ==\n\t\t   (IFF_LOWER_UP | IFF_RUNNING))\n#ifdef IFF_DORMANT\n\t\t/*\n\t\t * IFF_DORMANT means L1 is up but waiting for an external\n\t\t * event, for example 802.1X\n\t\t * We treat this as DOWN, but then return true for if_roaming()\n\t\t * so that the interface status is persisted.\n\t\t */\n\t\t&& !(flags & IFF_DORMANT)\n#endif\n\t    ?\n\t    LINK_UP :\n\t    LINK_DOWN;\n#else\n\treturn flags & IFF_RUNNING ? LINK_UP : LINK_DOWN;\n#endif\n}\n\nint\nif_carrier(struct interface *ifp, __unused const void *ifadata)\n{\n\treturn if_carrier_from_flags(ifp->flags);\n}\n\nbool\nif_roaming(struct interface *ifp)\n{\n\treturn\n#ifdef IFF_DORMANT\n\t    ifp->flags & IFF_DORMANT ||\n#endif\n#ifdef IFF_LOWER_UP\n\t    /*\n\t     * IFF_DORMANT only occurs for supplicant initiated roaming.\n\t     * For firmware initiated roaming we don't get IFF_DORMANT.\n\t     * Seems weird that the driver can't set it though.\n\t     * We can check that IFF_RUNNING is not set but UP and L1 are\n\t     * to get the same effect.\n\t     */\n\t    (ifp->flags & (IFF_UP | IFF_LOWER_UP | IFF_RUNNING)) ==\n\t    (IFF_UP | IFF_LOWER_UP) ||\n#endif\n\t    false;\n}\n\nint\nif_getnetlink(struct dhcpcd_ctx *ctx, struct iovec *iov, int fd, int flags,\n    int (*cb)(struct dhcpcd_ctx *, void *, struct nlmsghdr *), void *cbarg)\n{\n\tstruct sockaddr_nl nladdr = { .nl_pid = 0 };\n\tstruct msghdr msg = {\n\t\t.msg_name = &nladdr,\n\t\t.msg_namelen = sizeof(nladdr),\n\t\t.msg_iov = iov,\n\t\t.msg_iovlen = 1,\n\t};\n\tsize_t len;\n\tstruct nlmsghdr *nlm;\n\tint r = 0;\n\tunsigned int again;\n\tbool terminated;\n\nrecv_again:\n\tlen = (size_t)recvmsg(fd, &msg, flags);\n\tif (len == 0 || (ssize_t)len == -1)\n\t\treturn (int)len;\n\n\t/* Check sender */\n\tif (msg.msg_namelen != sizeof(nladdr)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\t/* Ignore message if it is not from kernel */\n\tif (nladdr.nl_pid != 0)\n\t\treturn 0;\n\n\tagain = 0;\n\tterminated = false;\n\tfor (nlm = iov->iov_base; nlm && NLMSG_OK(nlm, len);\n\t    nlm = NLMSG_NEXT(nlm, len)) {\n\t\tagain = (nlm->nlmsg_flags & NLM_F_MULTI);\n\t\tif (nlm->nlmsg_type == NLMSG_NOOP)\n\t\t\tcontinue;\n\n\t\tif (nlm->nlmsg_type == NLMSG_ERROR) {\n\t\t\tstruct nlmsgerr *err;\n\n\t\t\tif (nlm->nlmsg_len - sizeof(*nlm) < sizeof(*err)) {\n\t\t\t\terrno = EBADMSG;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\terr = (struct nlmsgerr *)NLMSG_DATA(nlm);\n\t\t\tif (err->error != 0) {\n\t\t\t\terrno = -err->error;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tagain = 0;\n\t\t\tterminated = true;\n\t\t\tbreak;\n\t\t}\n\t\tif (nlm->nlmsg_type == NLMSG_DONE) {\n\t\t\tagain = 0;\n\t\t\tterminated = true;\n\t\t\tbreak;\n\t\t}\n\t\tif (cb == NULL)\n\t\t\tcontinue;\n\t\tif (nlm->nlmsg_seq != (uint32_t)ctx->seq && fd != ctx->link_fd)\n\t\t\tlogwarnx(\"%s: received sequence %u, expecting %d\",\n\t\t\t    __func__, nlm->nlmsg_seq, ctx->seq);\n\t\telse\n\t\t\tr = cb(ctx, cbarg, nlm);\n\t}\n\n\tif ((again || !terminated) && (ctx != NULL && ctx->link_fd != fd))\n\t\tgoto recv_again;\n\n\treturn r;\n}\n\nstatic int\nif_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, struct nlmsghdr *nlm)\n{\n\tsize_t len;\n\tstruct rtmsg *rtm;\n\tstruct rtattr *rta;\n\tunsigned int ifindex;\n\tstruct sockaddr *sa;\n\n\tlen = nlm->nlmsg_len - sizeof(*nlm);\n\tif (len < sizeof(*rtm)) {\n\t\terrno = EBADMSG;\n\t\treturn -1;\n\t}\n\trtm = (struct rtmsg *)NLMSG_DATA(nlm);\n\tif (rtm->rtm_table != RT_TABLE_MAIN)\n\t\treturn -1;\n\n\tmemset(rt, 0, sizeof(*rt));\n\tif (rtm->rtm_type == RTN_UNREACHABLE)\n\t\trt->rt_flags |= RTF_REJECT;\n\n\trta = RTM_RTA(rtm);\n\tlen = RTM_PAYLOAD(nlm);\n\tfor (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {\n\t\tsa = NULL;\n\t\tswitch (rta->rta_type) {\n\t\tcase RTA_DST:\n\t\t\tsa = &rt->rt_dest;\n\t\t\tbreak;\n\t\tcase RTA_SRC: {\n\t\t\tunion sa_ss ssa;\n\t\t\tstruct sockaddr *psa = (struct sockaddr *)&ssa;\n\t\t\tsocklen_t salen;\n\n\t\t\tpsa->sa_family = rtm->rtm_family;\n\t\t\tsalen = sa_addrlen(psa);\n\t\t\tmemcpy((char *)psa + sa_addroffset(psa), RTA_DATA(rta),\n\t\t\t    MIN(salen, RTA_PAYLOAD(rta)));\n\t\t\t/* if ip-route \"from\" address is not unspecified,\n\t\t\t   route is source-based, eg:\n\t\t\t     <dest-net> from <source-net> via ... dev ...\n\t\t\t   ignore the route as may otherwise appear to overlap\n\t\t\t   with routes set/removed by dhcpcd */\n\t\t\tif (!sa_is_unspecified(psa)) {\n\t\t\t\terrno = ENOTSUP;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcase RTA_GATEWAY:\n\t\t\tsa = &rt->rt_gateway;\n\t\t\tbreak;\n\t\tcase RTA_PREFSRC:\n\t\t\tsa = &rt->rt_ifa;\n\t\t\tbreak;\n\t\tcase RTA_OIF:\n\t\t\tifindex = *(unsigned int *)RTA_DATA(rta);\n\t\t\trt->rt_ifp = if_findindex(ctx->ifaces, ifindex);\n\t\t\tbreak;\n\t\tcase RTA_PRIORITY:\n\t\t\trt->rt_metric = *(unsigned int *)RTA_DATA(rta);\n\t\t\tbreak;\n\t\tcase RTA_METRICS: {\n\t\t\tstruct rtattr *r2;\n\t\t\tsize_t l2;\n\n\t\t\tl2 = rta->rta_len;\n\t\t\tr2 = (struct rtattr *)RTA_DATA(rta);\n\t\t\tfor (; RTA_OK(r2, l2); r2 = RTA_NEXT(r2, l2)) {\n\t\t\t\tswitch (r2->rta_type) {\n\t\t\t\tcase RTAX_MTU:\n\t\t\t\t\trt->rt_mtu = *(unsigned int *)RTA_DATA(\n\t\t\t\t\t    r2);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n#ifdef HAVE_ROUTE_LIFETIME\n\t\tcase RTA_CACHEINFO: {\n\t\t\tstruct rta_cacheinfo ci;\n\t\t\tstatic long hz;\n\n\t\t\tif (hz == 0) {\n\t\t\t\thz = sysconf(_SC_CLK_TCK);\n\t\t\t\tif (hz == -1)\n\t\t\t\t\thz = CLOCKS_PER_SEC;\n\t\t\t}\n\n\t\t\tmemcpy(&ci, RTA_DATA(rta), sizeof(ci));\n\t\t\trt->rt_lifetime = (uint32_t)(ci.rta_expires / hz);\n\t\t\tbreak;\n\t\t}\n#endif\n#if 0\n\t\tcase RTA_EXPIRES:\n\t\t\t/* Reading the kernel source, this is only\n\t\t\t * emitted by IPv4 multicast routes as a UINT64.\n\t\t\t * Although we can set it for IPv6 routes as a UINT32,\n\t\t\t * the kernel will massage the value to HZ and put it\n\t\t\t * into RTA_CACHINFO as read above.\n\t\t\t * Gotta love that consistency! */\n\t\t\trt->rt_lifetime = (uint32_t)*(uint64_t *)RTA_DATA(rta);\n\t\t\tbreak;\n#endif\n\t\t}\n\n\t\tif (sa != NULL) {\n\t\t\tsocklen_t salen;\n\n\t\t\tsa->sa_family = rtm->rtm_family;\n\t\t\tsalen = sa_addrlen(sa);\n\t\t\t/* sa is a union where sockaddr_in6 is the biggest. */\n\t\t\t/* coverity[overrun-buffer-arg] */\n\t\t\tmemcpy((char *)sa + sa_addroffset(sa), RTA_DATA(rta),\n\t\t\t    MIN(salen, RTA_PAYLOAD(rta)));\n\t\t}\n\t}\n\n\t/* If no RTA_DST set the unspecified address for the family. */\n\tif (rt->rt_dest.sa_family == AF_UNSPEC)\n\t\trt->rt_dest.sa_family = rtm->rtm_family;\n\n\trt->rt_netmask.sa_family = rtm->rtm_family;\n\tsa_fromprefix(&rt->rt_netmask, rtm->rtm_dst_len);\n\tif (sa_is_allones(&rt->rt_netmask))\n\t\trt->rt_flags |= RTF_HOST;\n\n#if 0\n\tif (rt->rtp_ifp == NULL && rt->src.s_addr != INADDR_ANY) {\n\t\tstruct ipv4_addr *ap;\n\n\t\t/* For some reason the default route comes back with the\n\t\t * loopback interface in RTA_OIF? Lets find it by\n\t\t * preferred source address */\n\t\tif ((ap = ipv4_findaddr(ctx, &rt->src)))\n\t\t\trt->iface = ap->iface;\n\t}\n#endif\n\n\tif (rt->rt_ifp == NULL) {\n\t\terrno = ESRCH;\n\t\treturn -1;\n\t}\n\treturn 0;\n}\n\nstatic int\nlink_route(struct dhcpcd_ctx *ctx, __unused struct interface *ifp,\n    struct nlmsghdr *nlm)\n{\n\tsize_t len;\n\tint cmd;\n\tstruct priv *priv;\n\tstruct rt rt;\n\n\tswitch (nlm->nlmsg_type) {\n\tcase RTM_NEWROUTE:\n\t\tcmd = RTM_ADD;\n\t\tbreak;\n\tcase RTM_DELROUTE:\n\t\tcmd = RTM_DELETE;\n\t\tbreak;\n\tdefault:\n\t\treturn 0;\n\t}\n\n\tlen = nlm->nlmsg_len - sizeof(*nlm);\n\tif (len < sizeof(struct rtmsg)) {\n\t\terrno = EBADMSG;\n\t\treturn -1;\n\t}\n\n\t/* Ignore messages we sent. */\n#ifdef PRIVSEP\n\tif (ctx->ps_root != NULL &&\n\t    nlm->nlmsg_pid == (uint32_t)ctx->ps_root->psp_pid)\n\t\treturn 0;\n#endif\n\tpriv = (struct priv *)ctx->priv;\n\tif (nlm->nlmsg_pid == priv->route_pid)\n\t\treturn 0;\n\n\tif (if_copyrt(ctx, &rt, nlm) == 0)\n\t\trt_recvrt(cmd, &rt, (pid_t)nlm->nlmsg_pid);\n\n\treturn 0;\n}\n\nstatic int\nlink_addr(struct dhcpcd_ctx *ctx, struct interface *ifp, struct nlmsghdr *nlm)\n{\n\tsize_t len;\n\tstruct rtattr *rta;\n\tstruct ifaddrmsg *ifa;\n\tstruct priv *priv;\n#ifdef INET\n\tstruct in_addr addr, net, brd;\n\tint ret;\n#endif\n#ifdef INET6\n\tstruct in6_addr *local6 = NULL, *addr6 = NULL, *dstaddr6 = NULL;\n\tint flags;\n#endif\n\n\tif (nlm->nlmsg_type != RTM_DELADDR && nlm->nlmsg_type != RTM_NEWADDR)\n\t\treturn 0;\n\n\tlen = nlm->nlmsg_len - sizeof(*nlm);\n\tif (len < sizeof(*ifa)) {\n\t\terrno = EBADMSG;\n\t\treturn -1;\n\t}\n\n\t/* Ignore address deletions from ourself.\n\t * We need to process address flag changes though. */\n\tif (nlm->nlmsg_type == RTM_DELADDR) {\n#ifdef PRIVSEP\n\t\tif (ctx->ps_root != NULL &&\n\t\t    nlm->nlmsg_pid == (uint32_t)ctx->ps_root->psp_pid)\n\t\t\treturn 0;\n#endif\n\t\tpriv = (struct priv *)ctx->priv;\n\t\tif (nlm->nlmsg_pid == priv->route_pid)\n\t\t\treturn 0;\n\t}\n\n\tifa = NLMSG_DATA(nlm);\n\tif ((ifp = if_findindex(ctx->ifaces, ifa->ifa_index)) == NULL) {\n\t\t/* We don't know about the interface the address is for\n\t\t * so it's not really an error */\n\t\treturn 1;\n\t}\n\trta = IFA_RTA(ifa);\n\tlen = NLMSG_PAYLOAD(nlm, sizeof(*ifa));\n\tswitch (ifa->ifa_family) {\n#ifdef INET\n\tcase AF_INET:\n\t\taddr.s_addr = brd.s_addr = INADDR_ANY;\n\t\tinet_cidrtoaddr(ifa->ifa_prefixlen, &net);\n\t\tfor (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {\n\t\t\tswitch (rta->rta_type) {\n\t\t\tcase IFA_ADDRESS:\n\t\t\t\tif (ifp->flags & IFF_POINTOPOINT) {\n\t\t\t\t\tmemcpy(&brd.s_addr, RTA_DATA(rta),\n\t\t\t\t\t    sizeof(brd.s_addr));\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase IFA_BROADCAST:\n\t\t\t\tmemcpy(&brd.s_addr, RTA_DATA(rta),\n\t\t\t\t    sizeof(brd.s_addr));\n\t\t\t\tbreak;\n\t\t\tcase IFA_LOCAL:\n\t\t\t\tmemcpy(&addr.s_addr, RTA_DATA(rta),\n\t\t\t\t    sizeof(addr.s_addr));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t/* Validate RTM_DELADDR really means address deleted\n\t\t * and anything else really means address exists. */\n\t\tret = if_addressexists(ifp, &addr);\n\t\tif (ret == -1) {\n\t\t\tlogerr(\"if_addressexists: %s\", inet_ntoa(addr));\n\t\t\tbreak;\n\t\t} else if (ret == 1) {\n\t\t\tif (nlm->nlmsg_type == RTM_DELADDR)\n\t\t\t\tbreak;\n\t\t} else {\n\t\t\tif (nlm->nlmsg_type != RTM_DELADDR)\n\t\t\t\tbreak;\n\t\t}\n\n\t\tipv4_handleifa(ctx, nlm->nlmsg_type, NULL, ifp->name, &addr,\n\t\t    &net, &brd, ifa->ifa_flags, (pid_t)nlm->nlmsg_pid);\n\t\tbreak;\n#endif\n#ifdef INET6\n\tcase AF_INET6:\n\t\tfor (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {\n\t\t\tswitch (rta->rta_type) {\n\t\t\tcase IFA_ADDRESS:\n\t\t\t\taddr6 = (struct in6_addr *)RTA_DATA(rta);\n\t\t\t\tbreak;\n\t\t\tcase IFA_BROADCAST:\n\t\t\t\tdstaddr6 = (struct in6_addr *)RTA_DATA(rta);\n\t\t\t\tbreak;\n\t\t\tcase IFA_LOCAL:\n\t\t\t\tlocal6 = (struct in6_addr *)RTA_DATA(rta);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (local6 != NULL)\n\t\t\taddr6 = local6;\n\t\tif (addr6 == NULL)\n\t\t\tbreak; /* should be impossible */\n\n\t\t/* Validate RTM_DELADDR really means address deleted\n\t\t * and anything else really means address exists. */\n\t\tflags = if_addrflags6(ifp, addr6, NULL);\n\t\tif (nlm->nlmsg_type == RTM_DELADDR) {\n\t\t\tif (flags != -1)\n\t\t\t\tbreak;\n\t\t} else {\n\t\t\tif (flags == -1)\n\t\t\t\tbreak;\n\t\t}\n\n\t\tipv6_handleifa(ctx, nlm->nlmsg_type, NULL, ifp->name, addr6,\n\t\t    ifa->ifa_prefixlen, dstaddr6, ifa->ifa_flags,\n\t\t    (pid_t)nlm->nlmsg_pid);\n\t\tbreak;\n#endif\n\t}\n\treturn 0;\n}\n\nstatic uint8_t\nl2addr_len(unsigned short if_type)\n{\n\tswitch (if_type) {\n\tcase ARPHRD_ETHER:   /* FALLTHROUGH */\n\tcase ARPHRD_IEEE802: /*FALLTHROUGH */\n\tcase ARPHRD_IEEE80211:\n\t\treturn 6;\n\tcase ARPHRD_IEEE1394:\n\t\treturn 8;\n\tcase ARPHRD_INFINIBAND:\n\t\treturn 20;\n\t}\n\n\t/* Impossible */\n\treturn 0;\n}\n\n#ifdef INET6\nstatic int\nlink_neigh(struct dhcpcd_ctx *ctx, __unused struct interface *ifp,\n    struct nlmsghdr *nlm)\n{\n\tstruct ndmsg *r;\n\tstruct rtattr *rta;\n\tsize_t len;\n\n\tif (nlm->nlmsg_type != RTM_NEWNEIGH && nlm->nlmsg_type != RTM_DELNEIGH)\n\t\treturn 0;\n\tif (nlm->nlmsg_len < sizeof(*r))\n\t\treturn -1;\n\n\tr = NLMSG_DATA(nlm);\n\trta = RTM_RTA(r);\n\tlen = RTM_PAYLOAD(nlm);\n\tif (r->ndm_family == AF_INET6) {\n\t\tbool unreachable;\n\t\tstruct in6_addr addr6;\n\n\t\tunreachable = (nlm->nlmsg_type == RTM_NEWNEIGH &&\n\t\t    r->ndm_state & NUD_FAILED);\n\t\tmemset(&addr6, 0, sizeof(addr6));\n\t\tfor (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {\n\t\t\tswitch (rta->rta_type) {\n\t\t\tcase NDA_DST:\n\t\t\t\tmemcpy(&addr6.s6_addr, RTA_DATA(rta),\n\t\t\t\t    sizeof(addr6.s6_addr));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tipv6nd_neighbour(ctx, &addr6, !unreachable);\n\t}\n\n\treturn 0;\n}\n#endif\n\nstatic int\nlink_netlink(struct dhcpcd_ctx *ctx, void *arg, struct nlmsghdr *nlm)\n{\n\tstruct interface *ifp = arg;\n\tint r;\n\tsize_t len;\n\tstruct rtattr *rta, *hwaddr, *mtu;\n\tstruct ifinfomsg *ifi;\n\tchar ifn[IF_NAMESIZE + 1];\n\n\tr = link_route(ctx, ifp, nlm);\n\tif (r != 0)\n\t\treturn r;\n\tr = link_addr(ctx, ifp, nlm);\n\tif (r != 0)\n\t\treturn r;\n#ifdef INET6\n\tr = link_neigh(ctx, ifp, nlm);\n\tif (r != 0)\n\t\treturn r;\n#endif\n\n\tif (nlm->nlmsg_type != RTM_NEWLINK && nlm->nlmsg_type != RTM_DELLINK)\n\t\treturn 0;\n\tlen = nlm->nlmsg_len - sizeof(*nlm);\n\tif ((size_t)len < sizeof(*ifi)) {\n\t\terrno = EBADMSG;\n\t\treturn -1;\n\t}\n\tifi = NLMSG_DATA(nlm);\n\tif (ifi->ifi_flags & IFF_LOOPBACK)\n\t\treturn 0;\n\trta = (void *)((char *)ifi + NLMSG_ALIGN(sizeof(*ifi)));\n\tlen = NLMSG_PAYLOAD(nlm, sizeof(*ifi));\n\t*ifn = '\\0';\n\thwaddr = mtu = NULL;\n\n\tfor (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {\n\t\tswitch (rta->rta_type) {\n\t\tcase IFLA_WIRELESS:\n\t\t\t/* Ignore wireless messages */\n\t\t\tif (nlm->nlmsg_type == RTM_NEWLINK &&\n\t\t\t    ifi->ifi_change == 0)\n\t\t\t\treturn 0;\n\t\t\tbreak;\n\t\tcase IFLA_IFNAME:\n\t\t\tstrlcpy(ifn, (char *)RTA_DATA(rta), sizeof(ifn));\n\t\t\tbreak;\n\t\tcase IFLA_ADDRESS:\n\t\t\thwaddr = rta;\n\t\t\tbreak;\n\t\tcase IFLA_MTU:\n\t\t\tmtu = rta;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (nlm->nlmsg_type == RTM_DELLINK) {\n#ifdef PLUGIN_DEV\n\t\t/* If are listening to a dev manager, let that remove\n\t\t * the interface rather than the kernel. */\n\t\tif (dev_listening(ctx) < 1)\n#endif\n\t\t\tdhcpcd_handleinterface(ctx, -1, ifn);\n\t\treturn 0;\n\t}\n\n\t/* Virtual interfaces may not get a valid hardware address\n\t * at this point.\n\t * To trigger a valid hardware address pickup we need to pretend\n\t * that that don't exist until they have one. */\n\tif (ifi->ifi_flags & IFF_MASTER && !hwaddr) {\n\t\tdhcpcd_handleinterface(ctx, -1, ifn);\n\t\treturn 0;\n\t}\n\n\t/* Check for a new interface */\n\tifp = if_findindex(ctx->ifaces, (unsigned int)ifi->ifi_index);\n\tif (ifp == NULL) {\n#ifdef PLUGIN_DEV\n\t\t/* If are listening to a dev manager, let that announce\n\t\t * the interface rather than the kernel. */\n\t\tif (dev_listening(ctx) < 1)\n#endif\n\t\t\tdhcpcd_handleinterface(ctx, 1, ifn);\n\t\treturn 0;\n\t}\n\n\t/* Handle interface being renamed */\n\tif (strcmp(ifp->name, ifn) != 0) {\n\t\tdhcpcd_handleinterface(ctx, -1, ifp->name);\n\t\tdhcpcd_handleinterface(ctx, 1, ifn);\n\t\treturn 0;\n\t}\n\n\tif (mtu != NULL)\n\t\tifp->mtu = (int)*(unsigned int *)RTA_DATA(mtu);\n\n\t/* Re-read hardware address and friends */\n\tif (!(ifi->ifi_flags & IFF_UP)) {\n\t\tvoid *hwa = hwaddr != NULL ? RTA_DATA(hwaddr) : NULL;\n\t\tuint8_t hwl = l2addr_len(ifi->ifi_type);\n\n\t\tif (hwaddr != NULL && hwaddr->rta_len != RTA_LENGTH(hwl))\n\t\t\thwa = NULL;\n\t\tdhcpcd_handlehwaddr(ifp, ifi->ifi_type, hwa, hwl);\n\t}\n\n\tdhcpcd_handlecarrier(ifp, if_carrier_from_flags(ifi->ifi_flags),\n\t    ifi->ifi_flags);\n\treturn 0;\n}\n\nint\nif_handlelink(struct dhcpcd_ctx *ctx)\n{\n\tunsigned char buf[16 * 1024];\n\tstruct iovec iov = {\n\t\t.iov_base = buf,\n\t\t.iov_len = sizeof(buf),\n\t};\n\n\treturn if_getnetlink(ctx, &iov, ctx->link_fd, MSG_DONTWAIT,\n\t    &link_netlink, NULL);\n}\n\n#ifdef PRIVSEP\nstatic bool\nif_netlinkpriv(int protocol, struct nlmsghdr *nlm)\n{\n\tif (protocol != NETLINK_ROUTE)\n\t\treturn false;\n\n\tswitch (nlm->nlmsg_type) {\n\tcase RTM_NEWADDR:  /* FALLTHROUGH */\n\tcase RTM_DELADDR:  /* FALLTHROUGH */\n\tcase RTM_NEWROUTE: /* FALLTHROUGH */\n\tcase RTM_DELROUTE: /* FALLTHROUGH */\n\tcase RTM_NEWLINK:\n\t\treturn true;\n\tdefault:\n\t\treturn false;\n\t}\n}\n#endif\n\nstatic int\nif_sendnetlink(struct dhcpcd_ctx *ctx, int protocol, struct nlmsghdr *hdr,\n    int (*cb)(struct dhcpcd_ctx *, void *, struct nlmsghdr *), void *cbarg)\n{\n\tint s;\n\tstruct sockaddr_nl snl = { .nl_family = AF_NETLINK };\n\tstruct iovec iov = { .iov_base = hdr, .iov_len = hdr->nlmsg_len };\n\tstruct msghdr msg = { .msg_name = &snl,\n\t\t.msg_namelen = sizeof(snl),\n\t\t.msg_iov = &iov,\n\t\t.msg_iovlen = 1 };\n\tstruct priv *priv = (struct priv *)ctx->priv;\n\tunsigned char buf[16 * 1024];\n\tstruct iovec riov = {\n\t\t.iov_base = buf,\n\t\t.iov_len = sizeof(buf),\n\t};\n\n\t/* Request a reply */\n\thdr->nlmsg_flags |= NLM_F_ACK;\n\thdr->nlmsg_seq = (uint32_t)++ctx->seq;\n\tif ((unsigned int)ctx->seq > UINT32_MAX)\n\t\tctx->seq = 0;\n\n#ifdef PRIVSEP\n\tif (ctx->options & DHCPCD_PRIVSEP && if_netlinkpriv(protocol, hdr))\n\t\treturn (int)ps_root_sendnetlink(ctx, protocol, &msg);\n#endif\n\n\tswitch (protocol) {\n\tcase NETLINK_ROUTE:\n\t\ts = priv->route_fd;\n\t\tbreak;\n\tcase NETLINK_GENERIC:\n\t\ts = priv->generic_fd;\n#if 0\n#ifdef NETLINK_GET_STRICT_CHK\n\t\tif (hdr->nlmsg_type == RTM_GETADDR) {\n\t\t\tint on = 1;\n\n\t\t\tif (setsockopt(s, SOL_NETLINK, NETLINK_GET_STRICT_CHK,\n\t\t\t    &on, sizeof(on)) == -1 && errno != ENOPROTOOPT)\n\t\t\t\tlogerr(\"%s: NETLINK_GET_STRICT_CHK\", __func__);\n\t\t}\n#endif\n#endif\n\t\tbreak;\n\tdefault:\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tif (sendmsg(s, &msg, 0) == -1)\n\t\treturn -1;\n\n\treturn if_getnetlink(ctx, &riov, s, 0, cb, cbarg);\n}\n\n#define NLMSG_TAIL(nmsg)                              \\\n\t((struct rtattr *)(void *)(((char *)(nmsg)) + \\\n\t    NLMSG_ALIGN((nmsg)->nlmsg_len)))\n\nstatic int\nadd_attr_l(struct nlmsghdr *n, unsigned short maxlen, unsigned short type,\n    const void *data, unsigned short alen)\n{\n\tunsigned short len = (unsigned short)RTA_LENGTH(alen);\n\tstruct rtattr *rta;\n\n\tif (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {\n\t\terrno = ENOBUFS;\n\t\treturn -1;\n\t}\n\n\trta = NLMSG_TAIL(n);\n\trta->rta_type = type;\n\trta->rta_len = len;\n\tif (alen)\n\t\tmemcpy(RTA_DATA(rta), data, alen);\n\tn->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);\n\n\treturn 0;\n}\n\n#if defined(HAVE_ROUTE_PREF) || defined(HAVE_IN6_ADDR_GEN_MODE_NONE)\nstatic int\nadd_attr_8(struct nlmsghdr *n, unsigned short maxlen, unsigned short type,\n    uint8_t data)\n{\n\treturn add_attr_l(n, maxlen, type, &data, sizeof(data));\n}\n#endif\n\nstatic int\nadd_attr_32(struct nlmsghdr *n, unsigned short maxlen, unsigned short type,\n    uint32_t data)\n{\n\tunsigned short len = RTA_LENGTH(sizeof(data));\n\tstruct rtattr *rta;\n\n\tif (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) {\n\t\terrno = ENOBUFS;\n\t\treturn -1;\n\t}\n\n\trta = NLMSG_TAIL(n);\n\trta->rta_type = type;\n\trta->rta_len = len;\n\tmemcpy(RTA_DATA(rta), &data, sizeof(data));\n\tn->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;\n\n\treturn 0;\n}\n\nstatic int\nrta_add_attr_32(struct rtattr *rta, unsigned short maxlen, unsigned short type,\n    uint32_t data)\n{\n\tunsigned short len = RTA_LENGTH(sizeof(data));\n\tstruct rtattr *subrta;\n\n\tif (RTA_ALIGN(rta->rta_len) + len > maxlen) {\n\t\terrno = ENOBUFS;\n\t\treturn -1;\n\t}\n\n\tsubrta = (void *)((char *)rta + RTA_ALIGN(rta->rta_len));\n\tsubrta->rta_type = type;\n\tsubrta->rta_len = len;\n\tmemcpy(RTA_DATA(subrta), &data, sizeof(data));\n\trta->rta_len = (unsigned short)(NLMSG_ALIGN(rta->rta_len) + len);\n\treturn 0;\n}\n\n#ifdef HAVE_NL80211_H\nstatic struct nlattr *\nnla_next(struct nlattr *nla, size_t *rem)\n{\n\t*rem -= (size_t)NLA_ALIGN(nla->nla_len);\n\treturn (void *)((char *)nla + NLA_ALIGN(nla->nla_len));\n}\n\n#define NLA_TYPE(nla) ((nla)->nla_type & NLA_TYPE_MASK)\n#define NLA_LEN(nla)  (unsigned int)((nla)->nla_len - NLA_HDRLEN)\n#define NLA_OK(nla, rem)                   \\\n\t((rem) >= sizeof(struct nlattr) && \\\n\t    (nla)->nla_len >= sizeof(struct nlattr) && (nla)->nla_len <= rem)\n#define NLA_DATA(nla) (void *)((char *)(nla) + NLA_HDRLEN)\n#define NLA_FOR_EACH_ATTR(pos, head, len, rem)        \\\n\tfor (pos = head, rem = len; NLA_OK(pos, rem); \\\n\t    pos = nla_next(pos, &(rem)))\n\nstruct nlmg {\n\tstruct nlmsghdr hdr;\n\tstruct genlmsghdr ghdr;\n\tchar buffer[64];\n};\n\nstatic int\nnla_put_32(struct nlmsghdr *n, unsigned short maxlen, unsigned short type,\n    uint32_t data)\n{\n\tunsigned short len;\n\tstruct nlattr *nla;\n\n\tlen = NLA_ALIGN(NLA_HDRLEN + sizeof(data));\n\tif (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) {\n\t\terrno = ENOBUFS;\n\t\treturn -1;\n\t}\n\n\tnla = (struct nlattr *)NLMSG_TAIL(n);\n\tnla->nla_type = type;\n\tnla->nla_len = len;\n\tmemcpy(NLA_DATA(nla), &data, sizeof(data));\n\tn->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;\n\n\treturn 0;\n}\n\nstatic int\nnla_put_string(struct nlmsghdr *n, unsigned short maxlen, unsigned short type,\n    const char *data)\n{\n\tstruct nlattr *nla;\n\tsize_t len, sl;\n\n\tsl = strlen(data) + 1;\n\tlen = NLA_ALIGN(NLA_HDRLEN + sl);\n\tif (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) {\n\t\terrno = ENOBUFS;\n\t\treturn -1;\n\t}\n\n\tnla = (struct nlattr *)NLMSG_TAIL(n);\n\tnla->nla_type = type;\n\tnla->nla_len = (unsigned short)len;\n\tmemcpy(NLA_DATA(nla), data, sl);\n\tn->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + (unsigned short)len;\n\treturn 0;\n}\n\nstatic int\nnla_parse(struct nlattr *tb[], struct nlattr *head, size_t len, int maxtype)\n{\n\tstruct nlattr *nla;\n\tsize_t rem;\n\tint type;\n\n\tmemset(tb, 0, sizeof(*tb) * ((unsigned int)maxtype + 1));\n\tNLA_FOR_EACH_ATTR(nla, head, len, rem)\n\t{\n\t\ttype = NLA_TYPE(nla);\n\t\tif (type > maxtype)\n\t\t\tcontinue;\n\t\ttb[type] = nla;\n\t}\n\treturn 0;\n}\n\nstatic int\ngenl_parse(struct nlmsghdr *nlm, struct nlattr *tb[], int maxtype)\n{\n\tstruct genlmsghdr *ghdr;\n\tstruct nlattr *head;\n\tsize_t len;\n\n\tghdr = NLMSG_DATA(nlm);\n\thead = (void *)((char *)ghdr + GENL_HDRLEN);\n\tlen = nlm->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN;\n\treturn nla_parse(tb, head, len, maxtype);\n}\n\nstatic int\n_gnl_getfamily(__unused struct dhcpcd_ctx *ctx, __unused void *arg,\n    struct nlmsghdr *nlm)\n{\n\tstruct nlattr *tb[CTRL_ATTR_FAMILY_ID + 1];\n\tuint16_t family;\n\n\tif (genl_parse(nlm, tb, CTRL_ATTR_FAMILY_ID) == -1)\n\t\treturn -1;\n\tif (tb[CTRL_ATTR_FAMILY_ID] == NULL) {\n\t\terrno = ENOENT;\n\t\treturn -1;\n\t}\n\tmemcpy(&family, NLA_DATA(tb[CTRL_ATTR_FAMILY_ID]), sizeof(family));\n\treturn (int)family;\n}\n\nstatic int\ngnl_getfamily(struct dhcpcd_ctx *ctx, const char *name)\n{\n\tstruct nlmg nlm;\n\n\tmemset(&nlm, 0, sizeof(nlm));\n\tnlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct genlmsghdr));\n\tnlm.hdr.nlmsg_type = GENL_ID_CTRL;\n\tnlm.hdr.nlmsg_flags = NLM_F_REQUEST;\n\tnlm.ghdr.cmd = CTRL_CMD_GETFAMILY;\n\tnlm.ghdr.version = 1;\n\tif (nla_put_string(&nlm.hdr, sizeof(nlm), CTRL_ATTR_FAMILY_NAME,\n\t\tname) == -1)\n\t\treturn -1;\n\treturn if_sendnetlink(ctx, NETLINK_GENERIC, &nlm.hdr, &_gnl_getfamily,\n\t    NULL);\n}\n\nstatic int\n_if_getssid_nl80211(__unused struct dhcpcd_ctx *ctx, void *arg,\n    struct nlmsghdr *nlm)\n{\n\tstruct interface *ifp = arg;\n\tstruct nlattr *tb[NL80211_ATTR_BSS + 1];\n\tstruct nlattr *bss[NL80211_BSS_STATUS + 1];\n\tuint32_t status;\n\tunsigned char *ie;\n\tint ie_len;\n\n\tif (genl_parse(nlm, tb, NL80211_ATTR_BSS) == -1)\n\t\treturn 0;\n\n\tif (tb[NL80211_ATTR_BSS] == NULL)\n\t\treturn 0;\n\n\tif (nla_parse(bss, NLA_DATA(tb[NL80211_ATTR_BSS]),\n\t\tNLA_LEN(tb[NL80211_ATTR_BSS]), NL80211_BSS_STATUS) == -1)\n\t\treturn 0;\n\n\tif (bss[NL80211_BSS_BSSID] == NULL || bss[NL80211_BSS_STATUS] == NULL)\n\t\treturn 0;\n\n\tmemcpy(&status, NLA_DATA(bss[NL80211_BSS_STATUS]), sizeof(status));\n\tif (status != NL80211_BSS_STATUS_ASSOCIATED)\n\t\treturn 0;\n\n\tif (bss[NL80211_BSS_INFORMATION_ELEMENTS] == NULL)\n\t\treturn 0;\n\n\tie = NLA_DATA(bss[NL80211_BSS_INFORMATION_ELEMENTS]);\n\tie_len = (int)NLA_LEN(bss[NL80211_BSS_INFORMATION_ELEMENTS]);\n\t/* ie[0] is type, ie[1] is lenth, ie[2..] is data */\n\twhile (ie_len >= 2 && ie_len >= ie[1]) {\n\t\tif (ie[0] == 0) {\n\t\t\t/* SSID */\n\t\t\tif (ie[1] > IF_SSIDLEN) {\n\t\t\t\terrno = ENOBUFS;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tifp->ssid_len = ie[1];\n\t\t\tmemcpy(ifp->ssid, ie + 2, ifp->ssid_len);\n\t\t\treturn (int)ifp->ssid_len;\n\t\t}\n\t\tie_len -= ie[1] + 2;\n\t\tie += ie[1] + 2;\n\t}\n\n\treturn 0;\n}\n\nstatic int\nif_getssid_nl80211(struct interface *ifp)\n{\n\tint family;\n\tstruct nlmg nlm;\n\n\terrno = 0;\n\tfamily = gnl_getfamily(ifp->ctx, \"nl80211\");\n\tif (family == -1)\n\t\treturn -1;\n\n\t/* Is this a wireless interface? */\n\tmemset(&nlm, 0, sizeof(nlm));\n\tnlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct genlmsghdr));\n\tnlm.hdr.nlmsg_type = (unsigned short)family;\n\tnlm.hdr.nlmsg_flags = NLM_F_REQUEST;\n\tnlm.ghdr.cmd = NL80211_CMD_GET_WIPHY;\n\tnla_put_32(&nlm.hdr, sizeof(nlm), NL80211_ATTR_IFINDEX, ifp->index);\n\tif (if_sendnetlink(ifp->ctx, NETLINK_GENERIC, &nlm.hdr, NULL, NULL) ==\n\t    -1)\n\t\treturn -1;\n\n\t/* We need to parse out the list of scan results and find the one\n\t * we are connected to. */\n\tmemset(&nlm, 0, sizeof(nlm));\n\tnlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct genlmsghdr));\n\tnlm.hdr.nlmsg_type = (unsigned short)family;\n\tnlm.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;\n\tnlm.ghdr.cmd = NL80211_CMD_GET_SCAN;\n\tnla_put_32(&nlm.hdr, sizeof(nlm), NL80211_ATTR_IFINDEX, ifp->index);\n\n\treturn if_sendnetlink(ifp->ctx, NETLINK_GENERIC, &nlm.hdr,\n\t    &_if_getssid_nl80211, ifp);\n}\n#endif\n\nint\nif_getssid(struct interface *ifp)\n{\n\tint r;\n\n#ifdef HAVE_NL80211_H\n\tr = if_getssid_nl80211(ifp);\n\tif (r == -1)\n\t\tifp->ssid_len = 0;\n#else\n\tr = if_getssid_wext(ifp->name, ifp->ssid);\n\tif (r != -1)\n\t\tifp->ssid_len = (unsigned int)r;\n#endif\n\n\tifp->ssid[ifp->ssid_len] = '\\0';\n\treturn r;\n}\n\nstruct nlma {\n\tstruct nlmsghdr hdr;\n\tstruct ifaddrmsg ifa;\n\tchar buffer[64];\n};\n\n#ifdef INET\nstruct ifiaddr {\n\tunsigned int ifa_ifindex;\n\tstruct in_addr ifa_addr;\n\tbool ifa_found;\n};\n\nstatic int\n_if_addressexists(__unused struct dhcpcd_ctx *ctx, void *arg,\n    struct nlmsghdr *nlm)\n{\n\tstruct ifiaddr *ia = arg;\n\tin_addr_t this_addr;\n\tsize_t len;\n\tstruct rtattr *rta;\n\tstruct ifaddrmsg *ifa;\n\n\tifa = NLMSG_DATA(nlm);\n\tif (ifa->ifa_index != ia->ifa_ifindex || ifa->ifa_family != AF_INET)\n\t\treturn 0;\n\n\trta = IFA_RTA(ifa);\n\tlen = NLMSG_PAYLOAD(nlm, sizeof(*ifa));\n\tfor (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {\n\t\tswitch (rta->rta_type) {\n\t\tcase IFA_LOCAL:\n\t\t\tmemcpy(&this_addr, RTA_DATA(rta), sizeof(this_addr));\n\t\t\tif (this_addr == ia->ifa_addr.s_addr) {\n\t\t\t\tia->ifa_found = true;\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn 0;\n}\n\nstatic int\nif_addressexists(struct interface *ifp, struct in_addr *addr)\n{\n\tstruct ifiaddr ia = {\n\t\t.ifa_ifindex = ifp->index,\n\t\t.ifa_addr = *addr,\n\t\t.ifa_found = false,\n\t};\n\tstruct nlma nlm = {\n\t\t.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),\n\t\t.hdr.nlmsg_type = RTM_GETADDR,\n\t\t.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH,\n\t\t.ifa.ifa_family = AF_INET,\n\t\t.ifa.ifa_index = ifp->index,\n\t};\n\n\tint error = if_sendnetlink(ifp->ctx, NETLINK_ROUTE, &nlm.hdr,\n\t    &_if_addressexists, &ia);\n\tif (error == -1)\n\t\treturn -1;\n\treturn ia.ifa_found ? 1 : 0;\n}\n#endif\n\nstruct nlmr {\n\tstruct nlmsghdr hdr;\n\tstruct rtmsg rt;\n\tchar buffer[256];\n};\n\nint\nif_route(unsigned char cmd, const struct rt *rt)\n{\n\tstruct nlmr nlm;\n\tbool gateway_unspec;\n\n\tmemset(&nlm, 0, sizeof(nlm));\n\tnlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));\n\tswitch (cmd) {\n\tcase RTM_CHANGE:\n\t\tnlm.hdr.nlmsg_type = RTM_NEWROUTE;\n\t\tnlm.hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_REPLACE;\n\t\tbreak;\n\tcase RTM_ADD:\n\t\tnlm.hdr.nlmsg_type = RTM_NEWROUTE;\n\t\tnlm.hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL;\n\t\tbreak;\n\tcase RTM_DELETE:\n\t\tnlm.hdr.nlmsg_type = RTM_DELROUTE;\n\t\tbreak;\n\t}\n\tnlm.hdr.nlmsg_flags |= NLM_F_REQUEST;\n\tnlm.rt.rtm_family = (unsigned char)rt->rt_dest.sa_family;\n\tnlm.rt.rtm_table = RT_TABLE_MAIN;\n\n\tgateway_unspec = sa_is_unspecified(&rt->rt_gateway);\n\n\tif (cmd == RTM_DELETE) {\n\t\tnlm.rt.rtm_scope = RT_SCOPE_NOWHERE;\n\t} else {\n\t\t/* Address generated routes are RTPROT_KERNEL,\n\t\t * otherwise RTPROT_BOOT */\n#ifdef RTPROT_RA\n\t\tif (rt->rt_dflags & RTDF_RA)\n\t\t\tnlm.rt.rtm_protocol = RTPROT_RA;\n\t\telse\n#endif\n#ifdef RTPROT_DHCP\n\t\t    if (rt->rt_dflags & RTDF_DHCP)\n\t\t\tnlm.rt.rtm_protocol = RTPROT_DHCP;\n\t\telse\n#endif\n\t\t    if (rt->rt_dflags & RTDF_IFA_ROUTE)\n\t\t\tnlm.rt.rtm_protocol = RTPROT_KERNEL;\n\t\telse\n\t\t\tnlm.rt.rtm_protocol = RTPROT_BOOT;\n\t\tif (rt->rt_ifp->flags & IFF_LOOPBACK)\n\t\t\tnlm.rt.rtm_scope = RT_SCOPE_HOST;\n\t\telse if (gateway_unspec)\n\t\t\tnlm.rt.rtm_scope = RT_SCOPE_LINK;\n\t\telse\n\t\t\tnlm.rt.rtm_scope = RT_SCOPE_UNIVERSE;\n\t\tif (rt->rt_flags & RTF_REJECT)\n\t\t\tnlm.rt.rtm_type = RTN_UNREACHABLE;\n\t\telse\n\t\t\tnlm.rt.rtm_type = RTN_UNICAST;\n\t}\n\n#define ADDSA(type, sa)                               \\\n\tadd_attr_l(&nlm.hdr, sizeof(nlm), (type),     \\\n\t    (const char *)(sa) + sa_addroffset((sa)), \\\n\t    (unsigned short)sa_addrlen((sa)));\n\tnlm.rt.rtm_dst_len = (unsigned char)sa_toprefix(&rt->rt_netmask);\n\t/* rt->rt_dest and rt->gateway are unions where sockaddr_in6\n\t * is the biggest member. However, we access them as the\n\t * generic sockaddr and coverity thinks this will overrun. */\n\t/* coverity[overrun-buffer-arg] */\n\tADDSA(RTA_DST, &rt->rt_dest);\n\tif (cmd == RTM_ADD || cmd == RTM_CHANGE) {\n\t\tif (!gateway_unspec) {\n\t\t\t/* coverity[overrun-buffer-arg] */\n\t\t\tADDSA(RTA_GATEWAY, &rt->rt_gateway);\n\t\t}\n\t\t/* Cannot add tentative source addresses.\n\t\t * We don't know this here, so just skip INET6 ifa's.*/\n\t\tif (!sa_is_unspecified(&rt->rt_ifa) &&\n\t\t    rt->rt_ifa.sa_family != AF_INET6)\n\t\t\tADDSA(RTA_PREFSRC, &rt->rt_ifa);\n\t\tif (rt->rt_mtu) {\n\t\t\tchar metricsbuf[32];\n\t\t\tstruct rtattr *metrics = (void *)metricsbuf;\n\n\t\t\tmetrics->rta_type = RTA_METRICS;\n\t\t\tmetrics->rta_len = RTA_LENGTH(0);\n\t\t\trta_add_attr_32(metrics, sizeof(metricsbuf), RTAX_MTU,\n\t\t\t    rt->rt_mtu);\n\t\t\tadd_attr_l(&nlm.hdr, sizeof(nlm), RTA_METRICS,\n\t\t\t    RTA_DATA(metrics),\n\t\t\t    (unsigned short)RTA_PAYLOAD(metrics));\n\t\t}\n\n#ifdef HAVE_ROUTE_PREF\n\t\tif (rt->rt_dflags & RTDF_RA) {\n\t\t\tuint8_t pref;\n\n\t\t\tswitch (rt->rt_pref) {\n\t\t\tcase RTPREF_LOW:\n\t\t\t\tpref = ICMPV6_ROUTER_PREF_LOW;\n\t\t\t\tbreak;\n\t\t\tcase RTPREF_MEDIUM:\n\t\t\t\tpref = ICMPV6_ROUTER_PREF_MEDIUM;\n\t\t\t\tbreak;\n\t\t\tcase RTPREF_HIGH:\n\t\t\t\tpref = ICMPV6_ROUTER_PREF_HIGH;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tpref = ICMPV6_ROUTER_PREF_INVALID;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tadd_attr_8(&nlm.hdr, sizeof(nlm), RTA_PREF, pref);\n\t\t}\n#endif\n\t}\n\n\tif (!sa_is_loopback(&rt->rt_gateway))\n\t\tadd_attr_32(&nlm.hdr, sizeof(nlm), RTA_OIF, rt->rt_ifp->index);\n\n#ifdef HAVE_ROUTE_LIFETIME\n\tif (rt->rt_lifetime != 0) {\n\t\tuint32_t expires;\n\n\t\texpires = lifetime_left(rt->rt_lifetime, &rt->rt_aquired, NULL);\n\t\tadd_attr_32(&nlm.hdr, sizeof(nlm), RTA_EXPIRES, expires);\n\t}\n#endif\n\n\tif (rt->rt_metric != 0)\n\t\tadd_attr_32(&nlm.hdr, sizeof(nlm), RTA_PRIORITY, rt->rt_metric);\n\n\treturn if_sendnetlink(rt->rt_ifp->ctx, NETLINK_ROUTE, &nlm.hdr, NULL,\n\t    NULL);\n}\n\nstatic int\n_if_initrt(struct dhcpcd_ctx *ctx, void *arg, struct nlmsghdr *nlm)\n{\n\tstruct rt rt, *rtn;\n\trb_tree_t *kroutes = arg;\n\n\tif (if_copyrt(ctx, &rt, nlm) != 0)\n\t\treturn 0;\n\tif ((rtn = rt_new(rt.rt_ifp)) == NULL) {\n\t\tlogerr(__func__);\n\t\treturn 0;\n\t}\n\tmemcpy(rtn, &rt, sizeof(*rtn));\n\tif (rb_tree_insert_node(kroutes, rtn) != rtn)\n\t\trt_free(rtn);\n\treturn 0;\n}\n\nint\nif_initrt(struct dhcpcd_ctx *ctx, rb_tree_t *kroutes, int af)\n{\n\tstruct nlmr nlm = {\n\t\t.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)),\n\t\t.hdr.nlmsg_type = RTM_GETROUTE,\n\t\t.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH,\n\t\t.rt.rtm_table = RT_TABLE_MAIN,\n\t\t.rt.rtm_family = (unsigned char)af,\n\t};\n\n\treturn if_sendnetlink(ctx, NETLINK_ROUTE, &nlm.hdr, &_if_initrt,\n\t    kroutes);\n}\n\n#ifdef INET\nint\nif_address(unsigned char cmd, const struct ipv4_addr *ia)\n{\n\tstruct nlma nlm;\n\tstruct ifa_cacheinfo cinfo;\n\tint retval = 0;\n#ifdef IFA_F_NOPREFIXROUTE\n\tuint32_t flags = 0;\n#endif\n\n\tmemset(&nlm, 0, sizeof(nlm));\n\tnlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));\n\tnlm.hdr.nlmsg_flags = NLM_F_REQUEST;\n\tnlm.hdr.nlmsg_type = cmd;\n\tif (cmd == RTM_NEWADDR)\n\t\tnlm.hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;\n\tnlm.ifa.ifa_index = ia->iface->index;\n\tnlm.ifa.ifa_family = AF_INET;\n\n\tnlm.ifa.ifa_prefixlen = inet_ntocidr(ia->mask);\n\n#if 0\n\t/* This creates the aliased interface */\n\tadd_attr_l(&nlm.hdr, sizeof(nlm), IFA_LABEL,\n\t    ia->iface->alias,\n\t    (unsigned short)(strlen(ia->iface->alias) + 1));\n#endif\n\n\tadd_attr_l(&nlm.hdr, sizeof(nlm), IFA_LOCAL, &ia->addr.s_addr,\n\t    sizeof(ia->addr.s_addr));\n\n\tif (cmd == RTM_NEWADDR) {\n#ifdef IFA_F_NOPREFIXROUTE\n\t\tif (nlm.ifa.ifa_prefixlen < 32)\n\t\t\tflags |= IFA_F_NOPREFIXROUTE;\n\t\tadd_attr_32(&nlm.hdr, sizeof(nlm), IFA_FLAGS, flags);\n#endif\n\n\t\tadd_attr_l(&nlm.hdr, sizeof(nlm), IFA_BROADCAST,\n\t\t    &ia->brd.s_addr, sizeof(ia->brd.s_addr));\n\n\t\tmemset(&cinfo, 0, sizeof(cinfo));\n\t\tcinfo.ifa_prefered = ia->pltime;\n\t\tcinfo.ifa_valid = ia->vltime;\n\t\tadd_attr_l(&nlm.hdr, sizeof(nlm), IFA_CACHEINFO, &cinfo,\n\t\t    sizeof(cinfo));\n\t}\n\n\tif (if_sendnetlink(ia->iface->ctx, NETLINK_ROUTE, &nlm.hdr, NULL,\n\t\tNULL) == -1)\n\t\tretval = -1;\n\treturn retval;\n}\n\nint\nif_addrflags(__unused const struct interface *ifp,\n    __unused const struct in_addr *addr, __unused const char *alias)\n{\n\t/* Linux has no support for IPv4 address flags */\n\treturn 0;\n}\n#endif\n\n#ifdef INET6\nint\nif_address6(unsigned char cmd, const struct ipv6_addr *ia)\n{\n\tstruct nlma nlm;\n\tstruct ifa_cacheinfo cinfo;\n#if defined(IFA_F_MANAGETEMPADDR) || defined(IFA_F_NOPREFIXROUTE)\n\tuint32_t flags = 0;\n#endif\n\n\tmemset(&nlm, 0, sizeof(nlm));\n\tnlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));\n\tnlm.hdr.nlmsg_flags = NLM_F_REQUEST;\n\tnlm.hdr.nlmsg_type = cmd;\n\tif (cmd == RTM_NEWADDR)\n\t\tnlm.hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;\n\tnlm.ifa.ifa_index = ia->iface->index;\n\tnlm.ifa.ifa_family = AF_INET6;\n\n\t/* Add as /128 if no IFA_F_NOPREFIXROUTE ? */\n\tnlm.ifa.ifa_prefixlen = ia->prefix_len;\n\n#if 0\n\t/* This creates the aliased interface */\n\tadd_attr_l(&nlm.hdr, sizeof(nlm), IFA_LABEL,\n\t    ia->iface->alias, (unsigned short)(strlen(ia->iface->alias) + 1));\n#endif\n\tadd_attr_l(&nlm.hdr, sizeof(nlm), IFA_LOCAL, &ia->addr.s6_addr,\n\t    sizeof(ia->addr.s6_addr));\n\n\tif (cmd == RTM_NEWADDR) {\n#ifdef IPV6_MANAGETEMPADDR\n\t\tif (ia->flags & IPV6_AF_TEMPORARY) {\n\t\t\t/* Currently the kernel filters out these flags */\n#ifdef IFA_F_NOPREFIXROUTE\n\t\t\tflags |= IFA_F_TEMPORARY;\n#else\n\t\t\tnlm.ifa.ifa_flags |= IFA_F_TEMPORARY;\n#endif\n\t\t}\n#elif defined(IFA_F_MANAGETEMPADDR)\n\t\tif (ia->flags & IPV6_AF_AUTOCONF && IA6_CANAUTOCONF(ia))\n\t\t\tflags |= IFA_F_MANAGETEMPADDR;\n#endif\n#ifdef IFA_F_NOPREFIXROUTE\n\t\tif (!IN6_IS_ADDR_LINKLOCAL(&ia->addr))\n\t\t\tflags |= IFA_F_NOPREFIXROUTE;\n#endif\n#if defined(IFA_F_MANAGETEMPADDR) || defined(IFA_F_NOPREFIXROUTE)\n\t\tadd_attr_32(&nlm.hdr, sizeof(nlm), IFA_FLAGS, flags);\n#endif\n\n\t\tmemset(&cinfo, 0, sizeof(cinfo));\n\t\tcinfo.ifa_prefered = ia->prefix_pltime;\n\t\tcinfo.ifa_valid = ia->prefix_vltime;\n\t\tadd_attr_l(&nlm.hdr, sizeof(nlm), IFA_CACHEINFO, &cinfo,\n\t\t    sizeof(cinfo));\n\t}\n\n\treturn if_sendnetlink(ia->iface->ctx, NETLINK_ROUTE, &nlm.hdr, NULL,\n\t    NULL);\n}\n\nstruct ifiaddr6 {\n\tunsigned int ifa_ifindex;\n\tstruct in6_addr ifa_addr;\n\tuint32_t ifa_flags;\n\tbool ifa_found;\n};\n\nstatic int\n_if_addrflags6(__unused struct dhcpcd_ctx *ctx, void *arg, struct nlmsghdr *nlm)\n{\n\tstruct ifiaddr6 *ia = arg;\n\tsize_t len;\n\tstruct rtattr *rta;\n\tstruct ifaddrmsg *ifa;\n\tstruct in6_addr *local = NULL, *address = NULL;\n\tuint32_t flags;\n\n\tifa = NLMSG_DATA(nlm);\n\tif (ifa->ifa_index != ia->ifa_ifindex || ifa->ifa_family != AF_INET6)\n\t\treturn 0;\n\n\t/* Old kernels set flags here, newer ones as attributed data. */\n\tflags = ifa->ifa_flags;\n\n\trta = IFA_RTA(ifa);\n\tlen = NLMSG_PAYLOAD(nlm, sizeof(*ifa));\n\tfor (; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {\n\t\tswitch (rta->rta_type) {\n\t\tcase IFA_ADDRESS:\n\t\t\taddress = (struct in6_addr *)RTA_DATA(rta);\n\t\t\tbreak;\n\t\tcase IFA_LOCAL:\n\t\t\tlocal = (struct in6_addr *)RTA_DATA(rta);\n\t\t\tbreak;\n#ifdef IFA_F_MANAGETEMPADDR /* IFA_FLAGS is an enum, can't test that */\n\t\tcase IFA_FLAGS:\n\t\t\tmemcpy(&flags, RTA_DATA(rta), sizeof(flags));\n\t\t\tbreak;\n#endif\n\t\t}\n\t}\n\n\tif (local) {\n\t\tif (IN6_ARE_ADDR_EQUAL(&ia->ifa_addr, local))\n\t\t\tia->ifa_found = true;\n\t} else if (address) {\n\t\tif (IN6_ARE_ADDR_EQUAL(&ia->ifa_addr, address))\n\t\t\tia->ifa_found = true;\n\t}\n\tif (ia->ifa_found)\n\t\tia->ifa_flags = flags;\n\treturn 0;\n}\n\nint\nif_addrflags6(const struct interface *ifp, const struct in6_addr *addr,\n    __unused const char *alias)\n{\n\tstruct ifiaddr6 ia = {\n\t\t.ifa_ifindex = ifp->index,\n\t\t.ifa_addr = *addr,\n\t\t.ifa_found = false,\n\t};\n\tstruct nlma nlm = {\n\t\t.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),\n\t\t.hdr.nlmsg_type = RTM_GETADDR,\n\t\t.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH,\n\t\t.ifa.ifa_family = AF_INET6,\n\t\t.ifa.ifa_index = ifp->index,\n\t};\n\n\tint error = if_sendnetlink(ifp->ctx, NETLINK_ROUTE, &nlm.hdr,\n\t    &_if_addrflags6, &ia);\n\tif (error == -1)\n\t\treturn -1;\n\tif (!ia.ifa_found) {\n\t\terrno = ESRCH;\n\t\treturn -1;\n\t}\n\treturn (int)ia.ifa_flags;\n}\n\nint\nif_getlifetime6(__unused struct ipv6_addr *ia)\n{\n\t/* God knows how to work out address lifetimes on Linux */\n\terrno = ENOTSUP;\n\treturn -1;\n}\n\nstruct nlml {\n\tstruct nlmsghdr hdr;\n\tstruct ifinfomsg i;\n\tchar buffer[32];\n};\n\n#ifdef HAVE_IN6_ADDR_GEN_MODE_NONE\nstatic struct rtattr *\nadd_attr_nest(struct nlmsghdr *n, unsigned short maxlen, unsigned short type)\n{\n\tstruct rtattr *nest;\n\n\tnest = NLMSG_TAIL(n);\n\tadd_attr_l(n, maxlen, type, NULL, 0);\n\treturn nest;\n}\n\nstatic void\nadd_attr_nest_end(struct nlmsghdr *n, struct rtattr *nest)\n{\n\tnest->rta_len = (unsigned short)((char *)NLMSG_TAIL(n) - (char *)nest);\n}\n#endif\n\nstatic int\nif_disable_autolinklocal(struct dhcpcd_ctx *ctx, unsigned int ifindex)\n{\n#ifdef HAVE_IN6_ADDR_GEN_MODE_NONE\n\tstruct nlml nlm;\n\tstruct rtattr *afs, *afs6;\n\n\tmemset(&nlm, 0, sizeof(nlm));\n\tnlm.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));\n\tnlm.hdr.nlmsg_type = RTM_NEWLINK;\n\tnlm.hdr.nlmsg_flags = NLM_F_REQUEST;\n\tnlm.i.ifi_family = AF_INET6;\n\tnlm.i.ifi_index = (int)ifindex;\n\tafs = add_attr_nest(&nlm.hdr, sizeof(nlm), IFLA_AF_SPEC);\n\tafs6 = add_attr_nest(&nlm.hdr, sizeof(nlm), AF_INET6);\n\tadd_attr_8(&nlm.hdr, sizeof(nlm), IFLA_INET6_ADDR_GEN_MODE,\n\t    IN6_ADDR_GEN_MODE_NONE);\n\tadd_attr_nest_end(&nlm.hdr, afs6);\n\tadd_attr_nest_end(&nlm.hdr, afs);\n\n\treturn if_sendnetlink(ctx, NETLINK_ROUTE, &nlm.hdr, NULL, NULL);\n#else\n\tUNUSED(ctx);\n\tUNUSED(ifindex);\n\terrno = ENOTSUP;\n\treturn -1;\n#endif\n}\n\nstatic const char *p_conf = \"/proc/sys/net/ipv6/conf\";\nstatic const char *p_neigh = \"/proc/sys/net/ipv6/neigh\";\n\nvoid\nif_setup_inet6(const struct interface *ifp)\n{\n\tstruct dhcpcd_ctx *ctx = ifp->ctx;\n\tint ra;\n\tchar path[256];\n\n\t/* Modern linux kernels can make a stable private address.\n\t * However, a lot of distros ship newer kernel headers than\n\t * the kernel itself so we sweep that error under the table\n\t * from old kernels and just make them ourself regardless. */\n\tif (if_disable_autolinklocal(ctx, ifp->index) == -1 &&\n\t    errno != ENODEV && errno != ENOTSUP && errno != EINVAL)\n\t\tlogdebug(\"%s: if_disable_autolinklocal\", ifp->name);\n\n\t/* If not doing autoconf, don't disable the kernel from doing it.\n\t * If we need to, we should have another option actively disable it. */\n\tif (!(ifp->options->options & DHCPCD_IPV6RS))\n\t\treturn;\n\n\tsnprintf(path, sizeof(path), \"%s/%s/autoconf\", p_conf, ifp->name);\n\tra = check_proc_int(ctx, path);\n\tif (ra == -1) {\n\t\t/* The sysctl probably doesn't exist, but this isn't an\n\t\t * error as such so just log it and continue */\n\t\tif (errno != ENOENT)\n\t\t\tlogerr(\"%s: %s\", __func__, path);\n\t} else if (ra != 0) {\n\t\tif (if_writepathuint(ctx, path, 0) == -1)\n\t\t\tlogerr(\"%s: %s\", __func__, path);\n\t}\n\n\tsnprintf(path, sizeof(path), \"%s/%s/accept_ra\", p_conf, ifp->name);\n\tra = check_proc_int(ctx, path);\n\tif (ra == -1) {\n\t\t/* The sysctl probably doesn't exist, but this isn't an\n\t\t * error as such so just log it and continue */\n\t\tif (errno != ENOENT)\n\t\t\tlogerr(\"%s: %s\", __func__, path);\n\t} else if (ra != 0) {\n\t\tif (if_writepathuint(ctx, path, 0) == -1)\n\t\t\tlogerr(\"%s: %s\", __func__, path);\n\t}\n}\n\nint\nif_applyra(const struct ra *rap)\n{\n\tchar path[256];\n\tconst char *ifname = rap->iface->name;\n\tstruct dhcpcd_ctx *ctx = rap->iface->ctx;\n\tint error = 0;\n\n\tif (rap->hoplimit != 0) {\n\t\tsnprintf(path, sizeof(path), \"%s/%s/hop_limit\", p_conf, ifname);\n\t\tif (if_writepathuint(ctx, path, rap->hoplimit) == -1)\n\t\t\terror = -1;\n\t}\n\n\tif (rap->retrans != 0) {\n\t\tsnprintf(path, sizeof(path), \"%s/%s/retrans_time_ms\", p_neigh,\n\t\t    ifname);\n\t\tif (if_writepathuint(ctx, path, rap->retrans) == -1)\n\t\t\terror = -1;\n\t}\n\n\tif (rap->reachable != 0) {\n\t\tsnprintf(path, sizeof(path), \"%s/%s/base_reachable_time_ms\",\n\t\t    p_neigh, ifname);\n\t\tif (if_writepathuint(ctx, path, rap->reachable) == -1)\n\t\t\terror = -1;\n\t}\n\n\treturn error;\n}\n\n#endif /* INET6 */\n"
  },
  {
    "path": "src/if-options.c",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/types.h>\n#include <sys/param.h>\n\n#include <arpa/inet.h>\n#include <assert.h>\n#include <ctype.h>\n#include <errno.h>\n#include <fnmatch.h>\n#include <getopt.h>\n#include <grp.h>\n#include <inttypes.h>\n#include <limits.h>\n#include <paths.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n#include <unistd.h>\n\n#include \"common.h\"\n#include \"config.h\"\n#include \"dhcp.h\"\n#include \"dhcp6.h\"\n#include \"dhcpcd-embedded.h\"\n#include \"duid.h\"\n#include \"if-options.h\"\n#include \"if.h\"\n#include \"ipv4.h\"\n#include \"logerr.h\"\n#include \"sa.h\"\n\n#define IN_CONFIG_BLOCK(ifo)\t((ifo)->options & DHCPCD_FORKED)\n#define SET_CONFIG_BLOCK(ifo)\t((ifo)->options |= DHCPCD_FORKED)\n#define CLEAR_CONFIG_BLOCK(ifo) ((ifo)->options &= ~DHCPCD_FORKED)\n\nstatic unsigned long long default_options;\n\nconst struct option cf_options[] = { { \"background\", no_argument, NULL, 'b' },\n\t{ \"script\", required_argument, NULL, 'c' },\n\t{ \"debug\", no_argument, NULL, 'd' },\n\t{ \"env\", required_argument, NULL, 'e' },\n\t{ \"config\", required_argument, NULL, 'f' },\n\t{ \"reconfigure\", no_argument, NULL, 'g' },\n\t{ \"hostname\", optional_argument, NULL, 'h' },\n\t{ \"vendorclassid\", optional_argument, NULL, 'i' },\n\t{ \"logfile\", required_argument, NULL, 'j' },\n\t{ \"release\", no_argument, NULL, 'k' },\n\t{ \"leasetime\", required_argument, NULL, 'l' },\n\t{ \"metric\", required_argument, NULL, 'm' },\n\t{ \"rebind\", no_argument, NULL, 'n' },\n\t{ \"option\", required_argument, NULL, 'o' },\n\t{ \"persistent\", no_argument, NULL, 'p' },\n\t{ \"quiet\", no_argument, NULL, 'q' },\n\t{ \"request\", optional_argument, NULL, 'r' },\n\t{ \"inform\", optional_argument, NULL, 's' },\n\t{ \"inform6\", optional_argument, NULL, O_INFORM6 },\n\t{ \"timeout\", required_argument, NULL, 't' },\n\t{ \"userclass\", required_argument, NULL, 'u' },\n#ifndef SMALL\n\t{ \"msuserclass\", required_argument, NULL, O_MSUSERCLASS },\n#endif\n\t{ \"vsio\", required_argument, NULL, O_VSIO },\n\t{ \"vsio6\", required_argument, NULL, O_VSIO6 },\n\t{ \"vendor\", required_argument, NULL, 'v' },\n\t{ \"waitip\", optional_argument, NULL, 'w' },\n\t{ \"exit\", no_argument, NULL, 'x' },\n\t{ \"allowinterfaces\", required_argument, NULL, 'z' },\n\t{ \"reboot\", required_argument, NULL, 'y' },\n\t{ \"noarp\", no_argument, NULL, 'A' },\n\t{ \"nobackground\", no_argument, NULL, 'B' },\n\t{ \"nohook\", required_argument, NULL, 'C' },\n\t{ \"duid\", optional_argument, NULL, 'D' },\n\t{ \"lastlease\", no_argument, NULL, 'E' },\n\t{ \"fqdn\", optional_argument, NULL, 'F' },\n\t{ \"nogateway\", no_argument, NULL, 'G' },\n\t{ \"xidhwaddr\", no_argument, NULL, 'H' },\n\t{ \"clientid\", optional_argument, NULL, 'I' },\n\t{ \"broadcast\", no_argument, NULL, 'J' },\n\t{ \"nolink\", no_argument, NULL, 'K' },\n\t{ \"noipv4ll\", no_argument, NULL, 'L' },\n\t{ \"manager\", no_argument, NULL, 'M' },\n\t{ \"renew\", no_argument, NULL, 'N' },\n\t{ \"nooption\", required_argument, NULL, 'O' },\n\t{ \"printpidfile\", no_argument, NULL, 'P' },\n\t{ \"require\", required_argument, NULL, 'Q' },\n\t{ \"static\", required_argument, NULL, 'S' },\n\t{ \"test\", no_argument, NULL, 'T' },\n\t{ \"dumplease\", no_argument, NULL, 'U' },\n\t{ \"variables\", no_argument, NULL, 'V' },\n\t{ \"whitelist\", required_argument, NULL, 'W' },\n\t{ \"blacklist\", required_argument, NULL, 'X' },\n\t{ \"denyinterfaces\", required_argument, NULL, 'Z' },\n\t{ \"oneshot\", no_argument, NULL, '1' },\n\t{ \"ipv4only\", no_argument, NULL, '4' },\n\t{ \"ipv6only\", no_argument, NULL, '6' },\n\t{ \"anonymous\", no_argument, NULL, O_ANONYMOUS },\n\t{ \"randomise_hwaddr\", no_argument, NULL, O_RANDOMISE_HWADDR },\n\t{ \"arping\", required_argument, NULL, O_ARPING },\n\t{ \"destination\", required_argument, NULL, O_DESTINATION },\n\t{ \"fallback\", required_argument, NULL, O_FALLBACK },\n\t{ \"ipv6rs\", no_argument, NULL, O_IPV6RS },\n\t{ \"noipv6rs\", no_argument, NULL, O_NOIPV6RS },\n\t{ \"ipv6ra_autoconf\", no_argument, NULL, O_IPV6RA_AUTOCONF },\n\t{ \"ipv6ra_noautoconf\", no_argument, NULL, O_IPV6RA_NOAUTOCONF },\n\t{ \"ipv6ra_fork\", no_argument, NULL, O_IPV6RA_FORK },\n\t{ \"ipv4\", no_argument, NULL, O_IPV4 },\n\t{ \"noipv4\", no_argument, NULL, O_NOIPV4 },\n\t{ \"ipv6\", no_argument, NULL, O_IPV6 },\n\t{ \"noipv6\", no_argument, NULL, O_NOIPV6 },\n\t{ \"noalias\", no_argument, NULL, O_NOALIAS },\n\t{ \"iaid\", required_argument, NULL, O_IAID },\n\t{ \"ia_na\", optional_argument, NULL, O_IA_NA },\n\t{ \"ia_ta\", optional_argument, NULL, O_IA_TA },\n\t{ \"ia_pd\", optional_argument, NULL, O_IA_PD },\n\t{ \"hostname_short\", no_argument, NULL, O_HOSTNAME_SHORT },\n\t{ \"dev\", required_argument, NULL, O_DEV },\n\t{ \"nodev\", no_argument, NULL, O_NODEV },\n\t{ \"define\", required_argument, NULL, O_DEFINE },\n\t{ \"definend\", required_argument, NULL, O_DEFINEND },\n\t{ \"define6\", required_argument, NULL, O_DEFINE6 },\n\t{ \"embed\", required_argument, NULL, O_EMBED },\n\t{ \"encap\", required_argument, NULL, O_ENCAP },\n\t{ \"vendopt\", required_argument, NULL, O_VENDOPT },\n\t{ \"vendclass\", required_argument, NULL, O_VENDCLASS },\n\t{ \"authprotocol\", required_argument, NULL, O_AUTHPROTOCOL },\n\t{ \"authtoken\", required_argument, NULL, O_AUTHTOKEN },\n\t{ \"noauthrequired\", no_argument, NULL, O_AUTHNOTREQUIRED },\n\t{ \"dhcp\", no_argument, NULL, O_DHCP },\n\t{ \"nodhcp\", no_argument, NULL, O_NODHCP },\n\t{ \"dhcp6\", no_argument, NULL, O_DHCP6 },\n\t{ \"nodhcp6\", no_argument, NULL, O_NODHCP6 },\n\t{ \"controlgroup\", required_argument, NULL, O_CONTROLGRP },\n\t{ \"slaac\", required_argument, NULL, O_SLAAC },\n\t{ \"gateway\", no_argument, NULL, O_GATEWAY },\n\t{ \"reject\", required_argument, NULL, O_REJECT },\n\t{ \"bootp\", no_argument, NULL, O_BOOTP },\n\t{ \"nodelay\", no_argument, NULL, O_NODELAY },\n\t{ \"noup\", no_argument, NULL, O_NOUP },\n\t{ \"lastleaseextend\", no_argument, NULL, O_LASTLEASE_EXTEND },\n\t{ \"inactive\", no_argument, NULL, O_INACTIVE },\n\t{ \"mudurl\", required_argument, NULL, O_MUDURL },\n\t{ \"link_rcvbuf\", required_argument, NULL, O_LINK_RCVBUF },\n\t{ \"configure\", no_argument, NULL, O_CONFIGURE },\n\t{ \"noconfigure\", no_argument, NULL, O_NOCONFIGURE },\n\t{ \"arp_persistdefence\", no_argument, NULL, O_ARP_PERSISTDEFENCE },\n\t{ \"request_time\", required_argument, NULL, O_REQUEST_TIME },\n\t{ \"fallback_time\", required_argument, NULL, O_FALLBACK_TIME },\n\t{ \"ipv4ll_time\", required_argument, NULL, O_IPV4LL_TIME },\n\t{ \"nosyslog\", no_argument, NULL, O_NOSYSLOG },\n\t{ NULL, 0, NULL, '\\0' } };\n\nstatic char *\nadd_environ(char ***array, const char *value, int uniq)\n{\n\tchar **newlist, **list = *array;\n\tsize_t i = 0, l, lv;\n\tchar *match = NULL, *p, *n;\n\n\tmatch = strdup(value);\n\tif (match == NULL) {\n\t\tlogerr(__func__);\n\t\treturn NULL;\n\t}\n\tp = strchr(match, '=');\n\tif (p == NULL) {\n\t\tlogerrx(\"%s: no assignment: %s\", __func__, value);\n\t\tfree(match);\n\t\treturn NULL;\n\t}\n\t*p++ = '\\0';\n\tl = strlen(match);\n\n\twhile (list && list[i]) {\n\t\t/* We know that it must contain '=' due to the above test */\n\t\tsize_t listl = (size_t)(strchr(list[i], '=') - list[i]);\n\n\t\tif (l == listl && strncmp(list[i], match, l) == 0) {\n\t\t\tif (uniq) {\n\t\t\t\tn = strdup(value);\n\t\t\t\tif (n == NULL) {\n\t\t\t\t\tlogerr(__func__);\n\t\t\t\t\tfree(match);\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t\tfree(list[i]);\n\t\t\t\tlist[i] = n;\n\t\t\t} else {\n\t\t\t\t/* Append a space and the value to it */\n\t\t\t\tl = strlen(list[i]);\n\t\t\t\tlv = strlen(p);\n\t\t\t\tn = realloc(list[i], l + lv + 2);\n\t\t\t\tif (n == NULL) {\n\t\t\t\t\tlogerr(__func__);\n\t\t\t\t\tfree(match);\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t\tlist[i] = n;\n\t\t\t\tlist[i][l] = ' ';\n\t\t\t\tmemcpy(list[i] + l + 1, p, lv);\n\t\t\t\tlist[i][l + lv + 1] = '\\0';\n\t\t\t}\n\t\t\tfree(match);\n\t\t\treturn list[i];\n\t\t}\n\t\ti++;\n\t}\n\n\tfree(match);\n\tn = strdup(value);\n\tif (n == NULL) {\n\t\tlogerr(__func__);\n\t\treturn NULL;\n\t}\n\tnewlist = reallocarray(list, i + 2, sizeof(char *));\n\tif (newlist == NULL) {\n\t\tlogerr(__func__);\n\t\tfree(n);\n\t\treturn NULL;\n\t}\n\tnewlist[i] = n;\n\tnewlist[i + 1] = NULL;\n\t*array = newlist;\n\treturn newlist[i];\n}\n\n#define PARSE_STRING\t       0\n#define PARSE_STRING_NULL      1\n#define PARSE_HWADDR\t       2\n#define parse_string(a, b, c)  parse_str((a), (b), (c), PARSE_STRING)\n#define parse_nstring(a, b, c) parse_str((a), (b), (c), PARSE_STRING_NULL)\n#define parse_hwaddr(a, b, c)  parse_str((a), (b), (c), PARSE_HWADDR)\nstatic ssize_t\nparse_str(char *sbuf, size_t slen, const char *str, int flags)\n{\n\tsize_t l;\n\tconst char *p, *end;\n\tint i;\n\tchar c[4], cmd;\n\n\tend = str + strlen(str);\n\t/* If surrounded by quotes then it's a string */\n\tif (*str == '\"') {\n\t\tp = end - 1;\n\t\tif (*p == '\"') {\n\t\t\tstr++;\n\t\t\tend = p;\n\t\t}\n\t} else {\n\t\tl = (size_t)hwaddr_aton(NULL, str);\n\t\tif (l > 0) {\n\t\t\tif ((ssize_t)l == -1) {\n\t\t\t\terrno = ENOBUFS;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tif (sbuf == NULL)\n\t\t\t\treturn (ssize_t)l;\n\t\t\tif (l > slen) {\n\t\t\t\terrno = ENOBUFS;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\thwaddr_aton((uint8_t *)sbuf, str);\n\t\t\treturn (ssize_t)l;\n\t\t}\n\t}\n\n\t/* Process escapes */\n\tl = 0;\n\t/* If processing a string on the clientid, first byte should be\n\t * 0 to indicate a non hardware type */\n\tif (flags == PARSE_HWADDR && *str) {\n\t\tif (sbuf)\n\t\t\t*sbuf++ = 0;\n\t\tl++;\n\t}\n\tc[3] = '\\0';\n\twhile (str < end) {\n\t\tif (++l > slen && sbuf) {\n\t\t\terrno = ENOBUFS;\n\t\t\treturn -1;\n\t\t}\n\t\tif (*str == '\\\\') {\n\t\t\tstr++;\n\t\t\tswitch ((cmd = *str++)) {\n\t\t\tcase '\\0':\n\t\t\t\tstr--;\n\t\t\t\tbreak;\n\t\t\tcase 'b':\n\t\t\t\tif (sbuf)\n\t\t\t\t\t*sbuf++ = '\\b';\n\t\t\t\tbreak;\n\t\t\tcase 'n':\n\t\t\t\tif (sbuf)\n\t\t\t\t\t*sbuf++ = '\\n';\n\t\t\t\tbreak;\n\t\t\tcase 'r':\n\t\t\t\tif (sbuf)\n\t\t\t\t\t*sbuf++ = '\\r';\n\t\t\t\tbreak;\n\t\t\tcase 't':\n\t\t\t\tif (sbuf)\n\t\t\t\t\t*sbuf++ = '\\t';\n\t\t\t\tbreak;\n\t\t\tcase 'x':\n\t\t\t\t/* Grab a hex code */\n\t\t\t\tc[1] = '\\0';\n\t\t\t\tfor (i = 0; i < 2; i++) {\n\t\t\t\t\tif (isxdigit((unsigned char)*str) == 0)\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tc[i] = *str++;\n\t\t\t\t}\n\t\t\t\tif (c[1] != '\\0') {\n\t\t\t\t\tc[2] = '\\0';\n\t\t\t\t\tif (sbuf)\n\t\t\t\t\t\t*sbuf++ = (char)strtol(c, NULL,\n\t\t\t\t\t\t    16);\n\t\t\t\t} else\n\t\t\t\t\tl--;\n\t\t\t\tbreak;\n\t\t\tcase '0':\n\t\t\t\t/* Grab an octal code */\n\t\t\t\tc[2] = '\\0';\n\t\t\t\tfor (i = 0; i < 3; i++) {\n\t\t\t\t\tif (*str < '0' || *str > '7')\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tc[i] = *str++;\n\t\t\t\t}\n\t\t\t\tif (c[2] != '\\0') {\n\t\t\t\t\ti = (int)strtol(c, NULL, 8);\n\t\t\t\t\tif (i > 255)\n\t\t\t\t\t\ti = 255;\n\t\t\t\t\tif (sbuf)\n\t\t\t\t\t\t*sbuf++ = (char)i;\n\t\t\t\t} else\n\t\t\t\t\tl--;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tif (sbuf)\n\t\t\t\t\t*sbuf++ = cmd;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else {\n\t\t\tif (sbuf)\n\t\t\t\t*sbuf++ = *str;\n\t\t\tstr++;\n\t\t}\n\t}\n\tif (flags == PARSE_STRING_NULL) {\n\t\tl++;\n\t\tif (sbuf != NULL) {\n\t\t\tif (l > slen) {\n\t\t\t\terrno = ENOBUFS;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\t*sbuf = '\\0';\n\t\t}\n\t}\n\treturn (ssize_t)l;\n}\n\nstatic int\nparse_iaid1(uint8_t *iaid, const char *arg, size_t len, int n)\n{\n\tint e;\n\tuint32_t narg;\n\tssize_t s;\n\n\tnarg = (uint32_t)strtou(arg, NULL, 0, 0, UINT32_MAX, &e);\n\tif (e == 0) {\n\t\tif (n)\n\t\t\tnarg = htonl(narg);\n\t\tmemcpy(iaid, &narg, sizeof(narg));\n\t\treturn 0;\n\t}\n\n\tif ((s = parse_string((char *)iaid, len, arg)) < 1)\n\t\treturn -1;\n\tif (s < 4)\n\t\tiaid[3] = '\\0';\n\tif (s < 3)\n\t\tiaid[2] = '\\0';\n\tif (s < 2)\n\t\tiaid[1] = '\\0';\n\treturn 0;\n}\n\nstatic int\nparse_iaid(uint8_t *iaid, const char *arg, size_t len)\n{\n\treturn parse_iaid1(iaid, arg, len, 1);\n}\n\n#ifdef AUTH\nstatic int\nparse_uint32(uint32_t *i, const char *arg)\n{\n\treturn parse_iaid1((uint8_t *)i, arg, sizeof(uint32_t), 0);\n}\n#endif\n\nstatic char **\nsplitv(int *argc, char **argv, const char *arg)\n{\n\tchar **n, **v = argv;\n\tchar *o = strdup(arg), *p, *t, *nt;\n\n\tif (o == NULL) {\n\t\tlogerr(__func__);\n\t\treturn v;\n\t}\n\tp = o;\n\twhile ((t = strsep(&p, \", \"))) {\n\t\tnt = strdup(t);\n\t\tif (nt == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\tfree(o);\n\t\t\treturn v;\n\t\t}\n\t\tn = reallocarray(v, (size_t)(*argc) + 1, sizeof(char *));\n\t\tif (n == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\tfree(o);\n\t\t\tfree(nt);\n\t\t\treturn v;\n\t\t}\n\t\tv = n;\n\t\tv[(*argc)++] = nt;\n\t}\n\tfree(o);\n\treturn v;\n}\n\n#ifdef INET\nstatic int\nparse_addr(struct in_addr *addr, struct in_addr *net, const char *arg)\n{\n\tchar *p;\n\n\tif (arg == NULL || *arg == '\\0') {\n\t\tif (addr != NULL)\n\t\t\taddr->s_addr = 0;\n\t\tif (net != NULL)\n\t\t\tnet->s_addr = 0;\n\t\treturn 0;\n\t}\n\tif ((p = strchr(arg, '/')) != NULL) {\n\t\tint e;\n\t\tintmax_t i;\n\n\t\t*p++ = '\\0';\n\t\ti = strtoi(p, NULL, 10, 0, 32, &e);\n\t\tif (e != 0 ||\n\t\t    (net != NULL && inet_cidrtoaddr((int)i, net) != 0)) {\n\t\t\tlogerrx(\"invalid CIDR: %s\", p);\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\tif (addr != NULL && inet_aton(arg, addr) == 0) {\n\t\tlogerrx(\"invalid IP address: %s\", arg);\n\t\treturn -1;\n\t}\n\tif (p != NULL)\n\t\t*--p = '/';\n\telse if (net != NULL && addr != NULL)\n\t\tnet->s_addr = ipv4_getnetmask(addr->s_addr);\n\treturn 0;\n}\n#else\nstatic int\nparse_addr(__unused struct in_addr *addr, __unused struct in_addr *net,\n    __unused const char *arg)\n{\n\tlogerrx(\"No IPv4 support\");\n\treturn -1;\n}\n#endif\n\nstatic void\nset_option_space(struct dhcpcd_ctx *ctx, const char *arg,\n    const struct dhcp_opt **d, size_t *dl, const struct dhcp_opt **od,\n    size_t *odl, struct if_options *ifo, uint8_t *request[], uint8_t *require[],\n    uint8_t *no[], uint8_t *reject[])\n{\n#if !defined(INET) && !defined(INET6)\n\tUNUSED(ctx);\n#endif\n\n#ifdef INET6\n\tif (strncmp(arg, \"nd_\", strlen(\"nd_\")) == 0) {\n\t\t*d = ctx->nd_opts;\n\t\t*dl = ctx->nd_opts_len;\n\t\t*od = ifo->nd_override;\n\t\t*odl = ifo->nd_override_len;\n\t\t*request = ifo->requestmasknd;\n\t\t*require = ifo->requiremasknd;\n\t\t*no = ifo->nomasknd;\n\t\t*reject = ifo->rejectmasknd;\n\t\treturn;\n\t}\n\n#ifdef DHCP6\n\tif (strncmp(arg, \"dhcp6_\", strlen(\"dhcp6_\")) == 0) {\n\t\t*d = ctx->dhcp6_opts;\n\t\t*dl = ctx->dhcp6_opts_len;\n\t\t*od = ifo->dhcp6_override;\n\t\t*odl = ifo->dhcp6_override_len;\n\t\t*request = ifo->requestmask6;\n\t\t*require = ifo->requiremask6;\n\t\t*no = ifo->nomask6;\n\t\t*reject = ifo->rejectmask6;\n\t\treturn;\n\t}\n#endif\n#else\n\tUNUSED(arg);\n#endif\n\n#ifdef INET\n\t*d = ctx->dhcp_opts;\n\t*dl = ctx->dhcp_opts_len;\n\t*od = ifo->dhcp_override;\n\t*odl = ifo->dhcp_override_len;\n#else\n\t*d = NULL;\n\t*dl = 0;\n\t*od = NULL;\n\t*odl = 0;\n#endif\n\t*request = ifo->requestmask;\n\t*require = ifo->requiremask;\n\t*no = ifo->nomask;\n\t*reject = ifo->rejectmask;\n}\n\nvoid\nfree_dhcp_opt_embenc(struct dhcp_opt *opt)\n{\n\tsize_t i;\n\tstruct dhcp_opt *o;\n\n\tfree(opt->var);\n\n\tfor (i = 0, o = opt->embopts; i < opt->embopts_len; i++, o++)\n\t\tfree_dhcp_opt_embenc(o);\n\tfree(opt->embopts);\n\topt->embopts_len = 0;\n\topt->embopts = NULL;\n\n\tfor (i = 0, o = opt->encopts; i < opt->encopts_len; i++, o++)\n\t\tfree_dhcp_opt_embenc(o);\n\tfree(opt->encopts);\n\topt->encopts_len = 0;\n\topt->encopts = NULL;\n}\n\nstatic char *\nstrwhite(const char *s)\n{\n\tif (s == NULL)\n\t\treturn NULL;\n\twhile (*s != ' ' && *s != '\\t') {\n\t\tif (*s == '\\0')\n\t\t\treturn NULL;\n\t\ts++;\n\t}\n\treturn UNCONST(s);\n}\n\nstatic char *\nstrskipwhite(const char *s)\n{\n\tif (s == NULL || *s == '\\0')\n\t\treturn NULL;\n\twhile (*s == ' ' || *s == '\\t') {\n\t\ts++;\n\t\tif (*s == '\\0')\n\t\t\treturn NULL;\n\t}\n\treturn UNCONST(s);\n}\n\n#ifdef AUTH\n/* Find the end pointer of a string. */\nstatic char *\nstrend(const char *s)\n{\n\ts = strskipwhite(s);\n\tif (s == NULL)\n\t\treturn NULL;\n\tif (*s != '\"')\n\t\treturn strchr(s, ' ');\n\ts++;\n\tfor (; *s != '\"'; s++) {\n\t\tif (*s == '\\0')\n\t\t\treturn NULL;\n\t\tif (*s == '\\\\') {\n\t\t\tif (*(++s) == '\\0')\n\t\t\t\treturn NULL;\n\t\t}\n\t}\n\treturn UNCONST(++s);\n}\n#endif\n\nstatic int\nparse_option(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,\n    int opt, const char *arg, struct dhcp_opt **ldop, struct dhcp_opt **edop)\n{\n\tint e, i, t;\n\tlong l;\n\tunsigned long u;\n\tchar *p = NULL, *bp, *fp, *np;\n\tssize_t s;\n\tstruct in_addr addr, addr2;\n\tin_addr_t *naddr;\n\tconst struct dhcp_opt *d, *od;\n\tuint8_t *request, *require, *no, *reject;\n\tstruct dhcp_opt **dop, *ndop;\n\tsize_t *dop_len, dl, odl;\n\tstruct group *grp;\n#ifdef AUTH\n\tstruct token *token;\n#endif\n#ifdef _REENTRANT\n\tstruct group grpbuf;\n#endif\n#ifdef INET\n\tstruct rt *rt;\n#endif\n#ifdef DHCP6\n\tstruct if_ia *ia;\n\tuint8_t iaid[4];\n#endif\n#if defined(DHCP6) || ((defined(INET) || defined(INET6)) && !defined(SMALL))\n\tsize_t sl;\n#endif\n#ifndef SMALL\n#ifdef DHCP6\n\tstruct if_sla *sla, *slap;\n#endif\n\tstruct vivco *vivco;\n\tconst struct vivco *vivco_endp = ifo->vivco + ifo->vivco_len;\n\tstruct in6_addr in6addr;\n\tstruct vsio **vsiop = NULL, *vsio;\n\tsize_t *vsio_lenp = NULL, opt_max, opt_header;\n\tstruct vsio_so *vsio_so;\n#endif\n\n\tdop = NULL;\n\tdop_len = NULL;\n#ifdef INET6\n\ti = 0;\n#endif\n\n/* Add a guard for static analysers.\n * This should not be needed really because of the argument_required option\n * in the options declaration above. */\n#define ARG_REQUIRED     \\\n\tif (arg == NULL) \\\n\tgoto arg_required\n\n\tswitch (opt) {\n\tcase 'f': /* FALLTHROUGH */\n\tcase 'g': /* FALLTHROUGH */\n\tcase 'n': /* FALLTHROUGH */\n\tcase 'q': /* FALLTHROUGH */\n\tcase 'x': /* FALLTHROUGH */\n\tcase 'N': /* FALLTHROUGH */\n\tcase 'P': /* FALLTHROUGH */\n\tcase 'T': /* FALLTHROUGH */\n\tcase 'U': /* FALLTHROUGH */\n\tcase 'V': /* We need to handle non interface options */\n\t\tbreak;\n\tcase 'b':\n\t\tifo->options |= DHCPCD_BACKGROUND;\n\t\tbreak;\n\tcase 'c':\n\t\tARG_REQUIRED;\n\t\tif (IN_CONFIG_BLOCK(ifo)) {\n\t\t\tlogerrx(\"%s: per interface scripts\"\n\t\t\t\t\" are no longer supported\",\n\t\t\t    ifname);\n\t\t\treturn -1;\n\t\t}\n\t\tif (ctx->script != dhcpcd_default_script)\n\t\t\tfree(ctx->script);\n\t\ts = parse_nstring(NULL, 0, arg);\n\t\tif (s == 0) {\n\t\t\tctx->script = NULL;\n\t\t\tbreak;\n\t\t}\n\t\tdl = (size_t)s;\n\t\tif (s == -1 || (ctx->script = malloc(dl)) == NULL) {\n\t\t\tctx->script = NULL;\n\t\t\tlogerr(__func__);\n\t\t\treturn -1;\n\t\t}\n\t\ts = parse_nstring(ctx->script, dl, arg);\n\t\tif (s == -1 || ctx->script[0] == '\\0' ||\n\t\t    strcmp(ctx->script, \"/dev/null\") == 0) {\n\t\t\tfree(ctx->script);\n\t\t\tctx->script = NULL;\n\t\t}\n\t\tbreak;\n\tcase 'd':\n\t\tlogsetopts(loggetopts() | LOGERR_DEBUG);\n\t\tbreak;\n\tcase 'e':\n\t\tARG_REQUIRED;\n\t\tadd_environ(&ifo->environ, arg, 1);\n\t\tbreak;\n\tcase 'h':\n\t\tif (!arg) {\n\t\t\tifo->options |= DHCPCD_HOSTNAME;\n\t\t\tbreak;\n\t\t}\n\t\ts = parse_nstring(ifo->hostname, sizeof(ifo->hostname), arg);\n\t\tif (s == -1) {\n\t\t\tlogerr(\"%s: hostname\", __func__);\n\t\t\treturn -1;\n\t\t}\n\t\tif (s != 0 && ifo->hostname[0] == '.') {\n\t\t\tlogerrx(\"hostname cannot begin with .\");\n\t\t\treturn -1;\n\t\t}\n\t\tif (ifo->hostname[0] == '\\0')\n\t\t\tifo->options &= ~DHCPCD_HOSTNAME;\n\t\telse\n\t\t\tifo->options |= DHCPCD_HOSTNAME;\n\t\tbreak;\n\tcase 'i':\n\t\tif (arg)\n\t\t\ts = parse_string((char *)ifo->vendorclassid + 1,\n\t\t\t    sizeof(ifo->vendorclassid) - 1, arg);\n\t\telse\n\t\t\ts = 0;\n\t\tif (s == -1) {\n\t\t\tlogerr(\"vendorclassid\");\n\t\t\treturn -1;\n\t\t}\n\t\t*ifo->vendorclassid = (uint8_t)s;\n\t\tbreak;\n\tcase 'j':\n\t\tARG_REQUIRED;\n\t\t/* per interface logging is not supported\n\t\t * don't want to overide the commandline */\n\t\tif (!IN_CONFIG_BLOCK(ifo) && ctx->logfile == NULL) {\n\t\t\tlogclose();\n\t\t\tctx->logfile = strdup(arg);\n\t\t\tlogopen(ctx->logfile);\n\t\t}\n\t\tbreak;\n\tcase 'k':\n\t\tifo->options |= DHCPCD_RELEASE;\n\t\tbreak;\n\tcase 'l':\n\t\tARG_REQUIRED;\n\t\tif (strcmp(arg, \"-1\") == 0) {\n\t\t\tifo->leasetime = DHCP_INFINITE_LIFETIME;\n\t\t\tbreak;\n\t\t}\n\t\tifo->leasetime = (uint32_t)strtou(arg, NULL, 0, 0, UINT32_MAX,\n\t\t    &e);\n\t\tif (e) {\n\t\t\tlogerrx(\"failed to convert leasetime %s\", arg);\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n\tcase 'm':\n\t\tARG_REQUIRED;\n\t\tifo->metric = (int)strtoi(arg, NULL, 0, 0, INT32_MAX, &e);\n\t\tif (e) {\n\t\t\tlogerrx(\"failed to convert metric %s\", arg);\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n\tcase 'o':\n\t\tARG_REQUIRED;\n\t\tif (ctx->options & DHCPCD_PRINT_PIDFILE)\n\t\t\tbreak;\n\t\tset_option_space(ctx, arg, &d, &dl, &od, &odl, ifo, &request,\n\t\t    &require, &no, &reject);\n\t\tif (make_option_mask(d, dl, od, odl, request, arg, 1) != 0 ||\n\t\t    make_option_mask(d, dl, od, odl, no, arg, -1) != 0 ||\n\t\t    make_option_mask(d, dl, od, odl, reject, arg, -1) != 0) {\n\t\t\tlogerrx(\"unknown option: %s\", arg);\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n\tcase O_REJECT:\n\t\tARG_REQUIRED;\n\t\tif (ctx->options & DHCPCD_PRINT_PIDFILE)\n\t\t\tbreak;\n\t\tset_option_space(ctx, arg, &d, &dl, &od, &odl, ifo, &request,\n\t\t    &require, &no, &reject);\n\t\tif (make_option_mask(d, dl, od, odl, reject, arg, 1) != 0 ||\n\t\t    make_option_mask(d, dl, od, odl, request, arg, -1) != 0 ||\n\t\t    make_option_mask(d, dl, od, odl, require, arg, -1) != 0) {\n\t\t\tlogerrx(\"unknown option: %s\", arg);\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n\tcase 'p':\n\t\tifo->options |= DHCPCD_PERSISTENT;\n\t\tbreak;\n\tcase 'r':\n\t\tif (parse_addr(&ifo->req_addr, NULL, arg) != 0)\n\t\t\treturn -1;\n\t\tifo->options |= DHCPCD_REQUEST;\n\t\tifo->req_mask.s_addr = 0;\n\t\tbreak;\n\tcase 's':\n\t\tif (arg && *arg != '\\0') {\n\t\t\t/* Strip out a broadcast address */\n\t\t\tp = strchr(arg, '/');\n\t\t\tif (p != NULL) {\n\t\t\t\tp = strchr(p + 1, '/');\n\t\t\t\tif (p != NULL)\n\t\t\t\t\t*p = '\\0';\n\t\t\t}\n\t\t\ti = parse_addr(&ifo->req_addr, &ifo->req_mask, arg);\n\t\t\tif (p != NULL) {\n\t\t\t\t/* Ensure the original string is preserved */\n\t\t\t\t*p++ = '/';\n\t\t\t\tif (i == 0)\n\t\t\t\t\ti = parse_addr(&ifo->req_brd, NULL, p);\n\t\t\t}\n\t\t\tif (i != 0)\n\t\t\t\treturn -1;\n\t\t} else {\n\t\t\tifo->req_addr.s_addr = 0;\n\t\t\tifo->req_mask.s_addr = 0;\n\t\t}\n\t\tifo->options |= DHCPCD_INFORM | DHCPCD_PERSISTENT;\n\t\tifo->options &= ~DHCPCD_STATIC;\n\t\tbreak;\n\tcase O_INFORM6:\n\t\tifo->options |= DHCPCD_INFORM6;\n\t\tbreak;\n\tcase 't':\n\t\tARG_REQUIRED;\n\t\tifo->timeout = (uint32_t)strtou(arg, NULL, 0, 0, UINT32_MAX,\n\t\t    &e);\n\t\tif (e) {\n\t\t\tlogerrx(\"failed to convert timeout %s\", arg);\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n\tcase 'u':\n\t\tdl = sizeof(ifo->userclass) - ifo->userclass[0] - 1;\n\t\ts = parse_string((char *)ifo->userclass + ifo->userclass[0] + 2,\n\t\t    dl, arg);\n\t\tif (s == -1) {\n\t\t\tlogerr(\"userclass\");\n\t\t\treturn -1;\n\t\t}\n\t\tif (s != 0) {\n\t\t\tifo->userclass[ifo->userclass[0] + 1] = (uint8_t)s;\n\t\t\tifo->userclass[0] = (uint8_t)(ifo->userclass[0] + s +\n\t\t\t    1);\n\t\t}\n\t\tbreak;\n#ifndef SMALL\n\tcase O_MSUSERCLASS:\n\t\t/* Some Microsoft DHCP servers expect userclass to be an\n\t\t * opaque blob. This is not RFC 3004 compliant. */\n\t\ts = parse_string((char *)ifo->userclass + 1,\n\t\t    sizeof(ifo->userclass) - 1, arg);\n\t\tif (s == -1) {\n\t\t\tlogerr(\"msuserclass\");\n\t\t\treturn -1;\n\t\t}\n\t\tifo->userclass[0] = (uint8_t)s;\n\t\tbreak;\n#endif\n\n\tcase O_VSIO:\n#ifndef SMALL\n\t\tvsiop = &ifo->vsio;\n\t\tvsio_lenp = &ifo->vsio_len;\n\t\topt_max = UINT8_MAX;\n\t\topt_header = sizeof(uint8_t) + sizeof(uint8_t);\n#endif\n\t\t/* FALLTHROUGH */\n\tcase O_VSIO6:\n#ifndef SMALL\n\t\tif (vsiop == NULL) {\n\t\t\tvsiop = &ifo->vsio6;\n\t\t\tvsio_lenp = &ifo->vsio6_len;\n\t\t\topt_max = UINT16_MAX;\n\t\t\topt_header = sizeof(uint16_t) + sizeof(uint16_t);\n\t\t}\n#endif\n\t\tARG_REQUIRED;\n#ifdef SMALL\n\t\tlogwarnx(\"%s: vendor options not compiled in\", ifname);\n\t\treturn -1;\n#else\n\t\tfp = strwhite(arg);\n\t\tif (fp != NULL)\n\t\t\t*fp++ = '\\0';\n\t\tu = (uint32_t)strtou(arg, NULL, 0, 0, UINT32_MAX, &e);\n\t\tif (e) {\n\t\t\tlogerrx(\"invalid code: %s\", arg);\n\t\t\treturn -1;\n\t\t}\n\n\t\tif (fp != NULL)\n\t\t\tfp = strskipwhite(fp);\n\t\tif (fp != NULL)\n\t\t\tp = strchr(fp, ',');\n\t\telse\n\t\t\tp = NULL;\n\t\tif (p == NULL || p[1] == '\\0') {\n\t\t\tlogerrx(\"invalid vendor format: %s\", arg);\n\t\t\treturn -1;\n\t\t}\n\n\t\t/* Strip and preserve the comma */\n\t\t*p = '\\0';\n\t\ti = (int)strtoi(fp, NULL, 0, 1, (intmax_t)opt_max, &e);\n\t\t*p = ',';\n\t\tif (e) {\n\t\t\tlogerrx(\"vendor option should be between\"\n\t\t\t\t\" 1 and %zu inclusive\",\n\t\t\t    opt_max);\n\t\t\treturn -1;\n\t\t}\n\n\t\tfp = p + 1;\n\n\t\tif (fp) {\n\t\t\tif (inet_pton(AF_INET, fp, &addr) == 1) {\n\t\t\t\ts = sizeof(addr.s_addr);\n\t\t\t\tdl = (size_t)s;\n\t\t\t\tnp = malloc(dl);\n\t\t\t\tif (np == NULL) {\n\t\t\t\t\tlogerr(__func__);\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t\tmemcpy(np, &addr.s_addr, dl);\n\t\t\t} else if (inet_pton(AF_INET6, fp, &in6addr) == 1) {\n\t\t\t\ts = sizeof(in6addr.s6_addr);\n\t\t\t\tdl = (size_t)s;\n\t\t\t\tnp = malloc(dl);\n\t\t\t\tif (np == NULL) {\n\t\t\t\t\tlogerr(__func__);\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t\tmemcpy(np, &in6addr.s6_addr, dl);\n\t\t\t} else {\n\t\t\t\ts = parse_string(NULL, 0, fp);\n\t\t\t\tif (s == -1) {\n\t\t\t\t\tlogerr(__func__);\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t\tdl = (size_t)s;\n\t\t\t\tnp = malloc(dl);\n\t\t\t\tif (np == NULL) {\n\t\t\t\t\tlogerr(__func__);\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t\tparse_string(np, dl, fp);\n\t\t\t}\n\t\t} else {\n\t\t\tdl = 0;\n\t\t\tnp = NULL;\n\t\t}\n\n\t\tfor (sl = 0, vsio = *vsiop; sl < *vsio_lenp; sl++, vsio++) {\n\t\t\tif (vsio->en == (uint32_t)u)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (sl == *vsio_lenp) {\n\t\t\tvsio = reallocarray(*vsiop, *vsio_lenp + 1,\n\t\t\t    sizeof(**vsiop));\n\t\t\tif (vsio == NULL) {\n\t\t\t\tlogerr(\"%s: reallocarray vsio\", __func__);\n\t\t\t\tfree(np);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\t*vsiop = vsio;\n\t\t\tvsio = &(*vsiop)[(*vsio_lenp)++];\n\t\t\tvsio->en = (uint32_t)u;\n\t\t\tvsio->so = NULL;\n\t\t\tvsio->so_len = 0;\n\t\t}\n\n\t\tfor (sl = 0, vsio_so = vsio->so; sl < vsio->so_len;\n\t\t    sl++, vsio_so++)\n\t\t\topt_max -= opt_header + vsio_so->len;\n\t\tif (opt_header + dl > opt_max) {\n\t\t\tlogerrx(\"vsio is too big: %s\", fp);\n\t\t\tfree(np);\n\t\t\treturn -1;\n\t\t}\n\n\t\tvsio_so = reallocarray(vsio->so, vsio->so_len + 1,\n\t\t    sizeof(*vsio_so));\n\t\tif (vsio_so == NULL) {\n\t\t\tlogerr(\"%s: reallocarray vsio_so\", __func__);\n\t\t\tfree(np);\n\t\t\treturn -1;\n\t\t}\n\n\t\tvsio->so = vsio_so;\n\t\tvsio_so = &vsio->so[vsio->so_len++];\n\t\tvsio_so->opt = (uint16_t)i;\n\t\tvsio_so->len = (uint16_t)dl;\n\t\tvsio_so->data = np;\n\t\tbreak;\n#endif\n\tcase 'v':\n\t\tARG_REQUIRED;\n\t\tp = strchr(arg, ',');\n\t\tif (!p || !p[1]) {\n\t\t\tlogerrx(\"invalid vendor format: %s\", arg);\n\t\t\treturn -1;\n\t\t}\n\n\t\t/* If vendor starts with , then it is not encapsulated */\n\t\tif (p == arg) {\n\t\t\targ++;\n\t\t\ts = parse_string((char *)ifo->vendor + 1,\n\t\t\t    sizeof(ifo->vendor) - 1, arg);\n\t\t\tif (s == -1) {\n\t\t\t\tlogerr(\"vendor\");\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tifo->vendor[0] = (uint8_t)s;\n\t\t\tifo->options |= DHCPCD_VENDORRAW;\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Encapsulated vendor options */\n\t\tif (ifo->options & DHCPCD_VENDORRAW) {\n\t\t\tifo->options &= ~DHCPCD_VENDORRAW;\n\t\t\tifo->vendor[0] = 0;\n\t\t}\n\n\t\t/* Strip and preserve the comma */\n\t\t*p = '\\0';\n\t\ti = (int)strtoi(arg, NULL, 0, 1, 254, &e);\n\t\t*p = ',';\n\t\tif (e) {\n\t\t\tlogerrx(\"vendor option should be between\"\n\t\t\t\t\" 1 and 254 inclusive\");\n\t\t\treturn -1;\n\t\t}\n\n\t\targ = p + 1;\n\t\ts = (ssize_t)sizeof(ifo->vendor) - 1 - ifo->vendor[0] - 2;\n\t\tif (inet_aton(arg, &addr) == 1) {\n\t\t\tif (s < 6) {\n\t\t\t\ts = -1;\n\t\t\t\terrno = ENOBUFS;\n\t\t\t} else {\n\t\t\t\tmemcpy(ifo->vendor + ifo->vendor[0] + 3,\n\t\t\t\t    &addr.s_addr, sizeof(addr.s_addr));\n\t\t\t\ts = sizeof(addr.s_addr);\n\t\t\t}\n\t\t} else {\n\t\t\ts = parse_string((char *)ifo->vendor + ifo->vendor[0] +\n\t\t\t\t3,\n\t\t\t    (size_t)s, arg);\n\t\t}\n\t\tif (s == -1) {\n\t\t\tlogerr(\"vendor\");\n\t\t\treturn -1;\n\t\t}\n\t\tif (s != 0) {\n\t\t\tifo->vendor[ifo->vendor[0] + 1] = (uint8_t)i;\n\t\t\tifo->vendor[ifo->vendor[0] + 2] = (uint8_t)s;\n\t\t\tifo->vendor[0] = (uint8_t)(ifo->vendor[0] + s + 2);\n\t\t}\n\t\tbreak;\n\tcase 'w':\n\t\tifo->options |= DHCPCD_WAITIP;\n\t\tp = UNCONST(arg);\n\t\t// Generally it's --waitip=46, but some expect\n\t\t// --waitip=\"4 6\" to work as well.\n\t\t// It's easier to allow it rather than have confusing docs.\n\t\twhile (p != NULL && p[0] != '\\0') {\n\t\t\tif (p[0] == '4' || p[1] == '4')\n\t\t\t\tifo->options |= DHCPCD_WAITIP4;\n\t\t\tif (p[0] == '6' || p[1] == '6')\n\t\t\t\tifo->options |= DHCPCD_WAITIP6;\n\t\t\tp = strskipwhite(++p);\n\t\t}\n\t\tbreak;\n\tcase 'y':\n\t\tARG_REQUIRED;\n\t\tifo->reboot = (uint32_t)strtou(arg, NULL, 0, 0, UINT32_MAX, &e);\n\t\tif (e) {\n\t\t\tlogerr(\"failed to convert reboot %s\", arg);\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n\tcase 'z':\n\t\tARG_REQUIRED;\n\t\tif (!IN_CONFIG_BLOCK(ifo))\n\t\t\tctx->ifav = splitv(&ctx->ifac, ctx->ifav, arg);\n\t\tbreak;\n\tcase 'A':\n\t\tifo->options &= ~DHCPCD_ARP;\n\t\t/* IPv4LL requires ARP */\n\t\tifo->options &= ~DHCPCD_IPV4LL;\n\t\tbreak;\n\tcase 'B':\n\t\tifo->options &= ~DHCPCD_DAEMONISE;\n\t\tbreak;\n\tcase 'C':\n\t\tARG_REQUIRED;\n\t\t/* Commas to spaces for shell */\n\t\twhile ((p = strchr(arg, ',')))\n\t\t\t*p = ' ';\n\t\tdl = strlen(\"skip_hooks=\") + strlen(arg) + 1;\n\t\tp = malloc(sizeof(char) * dl);\n\t\tif (p == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\treturn -1;\n\t\t}\n\t\tsnprintf(p, dl, \"skip_hooks=%s\", arg);\n\t\tadd_environ(&ifo->environ, p, 0);\n\t\tfree(p);\n\t\tbreak;\n\tcase 'D':\n\t\tifo->options |= DHCPCD_CLIENTID | DHCPCD_DUID;\n\t\tif (ifname != NULL) /* duid type only a global option */\n\t\t\tbreak;\n\t\tif (arg == NULL)\n\t\t\tctx->duid_type = DUID_DEFAULT;\n\t\telse if (strcmp(arg, \"ll\") == 0)\n\t\t\tctx->duid_type = DUID_LL;\n\t\telse if (strcmp(arg, \"llt\") == 0)\n\t\t\tctx->duid_type = DUID_LLT;\n\t\telse if (strcmp(arg, \"uuid\") == 0)\n\t\t\tctx->duid_type = DUID_UUID;\n\t\telse {\n\t\t\tdl = hwaddr_aton(NULL, arg);\n\t\t\tif (dl != 0) {\n\t\t\t\tno = realloc(ctx->duid, dl);\n\t\t\t\tif (no == NULL)\n\t\t\t\t\tlogerrx(__func__);\n\t\t\t\telse {\n\t\t\t\t\tctx->duid = no;\n\t\t\t\t\tctx->duid_len = hwaddr_aton(no, arg);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbreak;\n\tcase 'E':\n\t\tifo->options |= DHCPCD_LASTLEASE;\n\t\tbreak;\n\tcase 'F':\n\t\tif (!arg) {\n\t\t\tifo->fqdn = FQDN_BOTH;\n\t\t\tbreak;\n\t\t}\n\t\tif (strcmp(arg, \"none\") == 0)\n\t\t\tifo->fqdn = FQDN_NONE;\n\t\telse if (strcmp(arg, \"ptr\") == 0)\n\t\t\tifo->fqdn = FQDN_PTR;\n\t\telse if (strcmp(arg, \"both\") == 0)\n\t\t\tifo->fqdn = FQDN_BOTH;\n\t\telse if (strcmp(arg, \"disable\") == 0)\n\t\t\tifo->fqdn = FQDN_DISABLE;\n\t\telse {\n\t\t\tlogerrx(\"invalid FQDN value: %s\", arg);\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n\tcase 'G':\n\t\tifo->options &= ~DHCPCD_GATEWAY;\n\t\tbreak;\n\tcase 'H':\n\t\tifo->options |= DHCPCD_XID_HWADDR;\n\t\tbreak;\n\tcase 'I':\n\t\tif (arg)\n\t\t\t/* If parse_hwaddr cannot decoded arg as a\n\t\t\t * hardware address then the first byte\n\t\t\t * in the clientid will be zero to indicate\n\t\t\t * a string value. */\n\t\t\ts = parse_hwaddr((char *)ifo->clientid + 1,\n\t\t\t    sizeof(ifo->clientid) - 1, arg);\n\t\telse\n\t\t\ts = 0;\n\t\tif (s == -1) {\n\t\t\tlogerr(\"clientid\");\n\t\t\treturn -1;\n\t\t}\n\t\tifo->options |= DHCPCD_CLIENTID;\n\t\tifo->clientid[0] = (uint8_t)s;\n\t\tifo->options &= ~DHCPCD_DUID;\n\t\tbreak;\n\tcase 'J':\n\t\tifo->options |= DHCPCD_BROADCAST;\n\t\tbreak;\n\tcase 'K':\n\t\tifo->options &= ~DHCPCD_LINK;\n\t\tbreak;\n\tcase 'L':\n\t\tifo->options &= ~DHCPCD_IPV4LL;\n\t\tbreak;\n\tcase 'M':\n\t\tifo->options |= DHCPCD_MANAGER;\n\t\tbreak;\n\tcase 'O':\n\t\tARG_REQUIRED;\n\t\tif (ctx->options & DHCPCD_PRINT_PIDFILE)\n\t\t\tbreak;\n\t\tset_option_space(ctx, arg, &d, &dl, &od, &odl, ifo, &request,\n\t\t    &require, &no, &reject);\n\t\tif (make_option_mask(d, dl, od, odl, request, arg, -1) != 0 ||\n\t\t    make_option_mask(d, dl, od, odl, require, arg, -1) != 0 ||\n\t\t    make_option_mask(d, dl, od, odl, no, arg, 1) != 0) {\n\t\t\tlogerrx(\"unknown option: %s\", arg);\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n\tcase 'Q':\n\t\tARG_REQUIRED;\n\t\tif (ctx->options & DHCPCD_PRINT_PIDFILE)\n\t\t\tbreak;\n\t\tset_option_space(ctx, arg, &d, &dl, &od, &odl, ifo, &request,\n\t\t    &require, &no, &reject);\n\t\tif (make_option_mask(d, dl, od, odl, require, arg, 1) != 0 ||\n\t\t    make_option_mask(d, dl, od, odl, request, arg, 1) != 0 ||\n\t\t    make_option_mask(d, dl, od, odl, no, arg, -1) != 0 ||\n\t\t    make_option_mask(d, dl, od, odl, reject, arg, -1) != 0) {\n\t\t\tlogerrx(\"unknown option: %s\", arg);\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n\tcase 'S':\n\t\tARG_REQUIRED;\n\t\tp = strchr(arg, '=');\n\t\tif (p == NULL) {\n\t\t\tlogerrx(\"static assignment required\");\n\t\t\treturn -1;\n\t\t}\n\t\tp = strskipwhite(++p);\n\t\tif (strncmp(arg, \"ip_address=\", strlen(\"ip_address=\")) == 0) {\n\t\t\tif (p == NULL) {\n\t\t\t\tifo->options &= ~DHCPCD_STATIC;\n\t\t\t\tifo->req_addr.s_addr = INADDR_ANY;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (parse_addr(&ifo->req_addr,\n\t\t\t\tifo->req_mask.s_addr == 0 ? &ifo->req_mask :\n\t\t\t\t\t\t\t    NULL,\n\t\t\t\tp) != 0)\n\t\t\t\treturn -1;\n\n\t\t\tifo->options |= DHCPCD_STATIC;\n\t\t\tifo->options &= ~DHCPCD_INFORM;\n\t\t} else if (strncmp(arg,\n\t\t\t       \"subnet_mask=\", strlen(\"subnet_mask=\")) == 0) {\n\t\t\tif (p == NULL) {\n\t\t\t\tifo->req_mask.s_addr = INADDR_ANY;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (parse_addr(&ifo->req_mask, NULL, p) != 0)\n\t\t\t\treturn -1;\n\t\t} else if (strncmp(arg, \"broadcast_address=\",\n\t\t\t       strlen(\"broadcast_address=\")) == 0) {\n\t\t\tif (p == NULL) {\n\t\t\t\tifo->req_brd.s_addr = INADDR_ANY;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (parse_addr(&ifo->req_brd, NULL, p) != 0)\n\t\t\t\treturn -1;\n\t\t} else if (strncmp(arg, \"routes=\", strlen(\"routes=\")) == 0 ||\n\t\t    strncmp(arg, \"static_routes=\", strlen(\"static_routes=\")) ==\n\t\t\t0 ||\n\t\t    strncmp(arg, \"classless_static_routes=\",\n\t\t\tstrlen(\"classless_static_routes=\")) == 0 ||\n\t\t    strncmp(arg, \"ms_classless_static_routes=\",\n\t\t\tstrlen(\"ms_classless_static_routes=\")) == 0) {\n#ifdef INET\n\t\t\tstruct in_addr addr3;\n\n\t\t\tif (p == NULL) {\n\t\t\t\trt_headclear(&ifo->routes, AF_INET);\n\t\t\t\tadd_environ(&ifo->config, arg, 1);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tfp = np = strwhite(p);\n\t\t\tif (np == NULL) {\n\t\t\t\tlogerrx(\"all routes need a gateway\");\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\t*np++ = '\\0';\n\t\t\tnp = strskipwhite(np);\n\t\t\tif (parse_addr(&addr, &addr2, p) == -1 ||\n\t\t\t    parse_addr(&addr3, NULL, np) == -1) {\n\t\t\t\t*fp = ' ';\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\t*fp = ' ';\n\t\t\tif ((rt = rt_new0(ctx)) == NULL)\n\t\t\t\treturn -1;\n\t\t\tsa_in_init(&rt->rt_dest, &addr);\n\t\t\tsa_in_init(&rt->rt_netmask, &addr2);\n\t\t\tsa_in_init(&rt->rt_gateway, &addr3);\n\t\t\tif (rt_proto_add_ctx(&ifo->routes, rt, ctx))\n\t\t\t\tadd_environ(&ifo->config, arg, 0);\n#else\n\t\t\tlogerrx(\"no inet support for option: %s\", arg);\n\t\t\treturn -1;\n#endif\n\t\t} else if (strncmp(arg, \"routers=\", strlen(\"routers=\")) == 0) {\n#ifdef INET\n\t\t\tif (p == NULL) {\n\t\t\t\trt_headclear(&ifo->routes, AF_INET);\n\t\t\t\tadd_environ(&ifo->config, arg, 1);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (parse_addr(&addr, NULL, p) == -1)\n\t\t\t\treturn -1;\n\t\t\tif ((rt = rt_new0(ctx)) == NULL)\n\t\t\t\treturn -1;\n\t\t\taddr2.s_addr = INADDR_ANY;\n\t\t\tsa_in_init(&rt->rt_dest, &addr2);\n\t\t\tsa_in_init(&rt->rt_netmask, &addr2);\n\t\t\tsa_in_init(&rt->rt_gateway, &addr);\n\t\t\tif (rt_proto_add_ctx(&ifo->routes, rt, ctx))\n\t\t\t\tadd_environ(&ifo->config, arg, 0);\n#else\n\t\t\tlogerrx(\"no inet support for option: %s\", arg);\n\t\t\treturn -1;\n#endif\n\t\t} else if (strncmp(arg, \"interface_mtu=\",\n\t\t\t       strlen(\"interface_mtu=\")) == 0 ||\n\t\t    strncmp(arg, \"mtu=\", strlen(\"mtu=\")) == 0) {\n\t\t\tif (p == NULL)\n\t\t\t\tbreak;\n\t\t\tifo->mtu = (unsigned int)strtou(p, NULL, 0, IPV4_MMTU,\n\t\t\t    UINT_MAX, &e);\n\t\t\tif (e) {\n\t\t\t\tlogerrx(\"invalid MTU %s\", p);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t} else if (strncmp(arg,\n\t\t\t       \"ip6_address=\", strlen(\"ip6_address=\")) == 0) {\n#ifdef INET6\n\t\t\tif (p == NULL) {\n\t\t\t\tmemset(&ifo->req_addr6, 0,\n\t\t\t\t    sizeof(ifo->req_addr6));\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tnp = strchr(p, '/');\n\t\t\tif (np)\n\t\t\t\t*np++ = '\\0';\n\t\t\tif ((i = inet_pton(AF_INET6, p, &ifo->req_addr6)) ==\n\t\t\t    1) {\n\t\t\t\tif (np) {\n\t\t\t\t\tifo->req_prefix_len = (uint8_t)\n\t\t\t\t\t    strtou(np, NULL, 0, 0, 128, &e);\n\t\t\t\t\tif (e) {\n\t\t\t\t\t\tlogerrx(\"%s: failed to \"\n\t\t\t\t\t\t\t\"convert prefix len\",\n\t\t\t\t\t\t    ifname);\n\t\t\t\t\t\treturn -1;\n\t\t\t\t\t}\n\t\t\t\t} else\n\t\t\t\t\tifo->req_prefix_len = 128;\n\t\t\t}\n\t\t\tif (np)\n\t\t\t\t*(--np) = '\\0';\n\t\t\tif (i != 1) {\n\t\t\t\tlogerrx(\"invalid AF_INET6: %s\", p);\n\t\t\t\tmemset(&ifo->req_addr6, 0,\n\t\t\t\t    sizeof(ifo->req_addr6));\n\t\t\t\treturn -1;\n\t\t\t}\n#else\n\t\t\tlogerrx(\"no inet6 support for option: %s\", arg);\n\t\t\treturn -1;\n#endif\n\t\t} else\n\t\t\tadd_environ(&ifo->config, arg, p == NULL ? 1 : 0);\n\t\tbreak;\n\n\tcase 'W':\n\t\tif (parse_addr(&addr, &addr2, arg) != 0)\n\t\t\treturn -1;\n\t\tif (strchr(arg, '/') == NULL)\n\t\t\taddr2.s_addr = INADDR_BROADCAST;\n\t\tnaddr = reallocarray(ifo->whitelist, ifo->whitelist_len + 2,\n\t\t    sizeof(in_addr_t));\n\t\tif (naddr == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\treturn -1;\n\t\t}\n\t\tifo->whitelist = naddr;\n\t\tifo->whitelist[ifo->whitelist_len++] = addr.s_addr;\n\t\tifo->whitelist[ifo->whitelist_len++] = addr2.s_addr;\n\t\tbreak;\n\tcase 'X':\n\t\tif (parse_addr(&addr, &addr2, arg) != 0)\n\t\t\treturn -1;\n\t\tif (strchr(arg, '/') == NULL)\n\t\t\taddr2.s_addr = INADDR_BROADCAST;\n\t\tnaddr = reallocarray(ifo->blacklist, ifo->blacklist_len + 2,\n\t\t    sizeof(in_addr_t));\n\t\tif (naddr == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\treturn -1;\n\t\t}\n\t\tifo->blacklist = naddr;\n\t\tifo->blacklist[ifo->blacklist_len++] = addr.s_addr;\n\t\tifo->blacklist[ifo->blacklist_len++] = addr2.s_addr;\n\t\tbreak;\n\tcase 'Z':\n\t\tARG_REQUIRED;\n\t\tif (!IN_CONFIG_BLOCK(ifo))\n\t\t\tctx->ifdv = splitv(&ctx->ifdc, ctx->ifdv, arg);\n\t\tbreak;\n\tcase '1':\n\t\tifo->options |= DHCPCD_ONESHOT;\n\t\tbreak;\n\tcase '4':\n#ifdef INET\n\t\tifo->options &= ~DHCPCD_IPV6;\n\t\tifo->options |= DHCPCD_IPV4;\n\t\tbreak;\n#else\n\t\tlogerrx(\"INET has been compiled out\");\n\t\treturn -1;\n#endif\n\tcase '6':\n#ifdef INET6\n\t\tifo->options &= ~DHCPCD_IPV4;\n\t\tifo->options |= DHCPCD_IPV6;\n\t\tbreak;\n#else\n\t\tlogerrx(\"INET6 has been compiled out\");\n\t\treturn -1;\n#endif\n\tcase O_IPV4:\n\t\tifo->options |= DHCPCD_IPV4;\n\t\tbreak;\n\tcase O_NOIPV4:\n\t\tifo->options &= ~DHCPCD_IPV4;\n\t\tbreak;\n\tcase O_IPV6:\n\t\tifo->options |= DHCPCD_IPV6;\n\t\tbreak;\n\tcase O_NOIPV6:\n\t\tifo->options &= ~DHCPCD_IPV6;\n\t\tbreak;\n\tcase O_ANONYMOUS:\n\t\tifo->options |= DHCPCD_ANONYMOUS;\n\t\tifo->options &= ~DHCPCD_HOSTNAME;\n\t\tifo->fqdn = FQDN_DISABLE;\n\n\t\t/* Block everything */\n\t\tmemset(ifo->nomask, 0xff, sizeof(ifo->nomask));\n\t\tmemset(ifo->nomask6, 0xff, sizeof(ifo->nomask6));\n\n\t\t/* Allow the bare minimum through */\n#ifdef INET\n\t\tdel_option_mask(ifo->nomask, DHO_SUBNETMASK);\n\t\tdel_option_mask(ifo->nomask, DHO_CSR);\n\t\tdel_option_mask(ifo->nomask, DHO_ROUTER);\n\t\tdel_option_mask(ifo->nomask, DHO_DNSSERVER);\n\t\tdel_option_mask(ifo->nomask, DHO_DNSDOMAIN);\n\t\tdel_option_mask(ifo->nomask, DHO_BROADCAST);\n\t\tdel_option_mask(ifo->nomask, DHO_STATICROUTE);\n\t\tdel_option_mask(ifo->nomask, DHO_SERVERID);\n\t\tdel_option_mask(ifo->nomask, DHO_RENEWALTIME);\n\t\tdel_option_mask(ifo->nomask, DHO_REBINDTIME);\n\t\tdel_option_mask(ifo->nomask, DHO_DNSSEARCH);\n#endif\n\n#ifdef DHCP6\n\t\tdel_option_mask(ifo->nomask6, D6_OPTION_DNS_SERVERS);\n\t\tdel_option_mask(ifo->nomask6, D6_OPTION_DOMAIN_LIST);\n\t\tdel_option_mask(ifo->nomask6, D6_OPTION_SOL_MAX_RT);\n\t\tdel_option_mask(ifo->nomask6, D6_OPTION_INF_MAX_RT);\n#endif\n\n\t\tbreak;\n\tcase O_RANDOMISE_HWADDR:\n\t\tifo->randomise_hwaddr = true;\n\t\tbreak;\n#ifdef INET\n\tcase O_ARPING:\n\t\twhile (arg != NULL) {\n\t\t\tfp = strwhite(arg);\n\t\t\tif (fp)\n\t\t\t\t*fp++ = '\\0';\n\t\t\tif (parse_addr(&addr, NULL, arg) != 0)\n\t\t\t\treturn -1;\n\t\t\tnaddr = reallocarray(ifo->arping,\n\t\t\t    (size_t)ifo->arping_len + 1, sizeof(in_addr_t));\n\t\t\tif (naddr == NULL) {\n\t\t\t\tlogerr(__func__);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tifo->arping = naddr;\n\t\t\tifo->arping[ifo->arping_len++] = addr.s_addr;\n\t\t\targ = strskipwhite(fp);\n\t\t}\n\t\tbreak;\n\tcase O_DESTINATION:\n\t\tARG_REQUIRED;\n\t\tif (ctx->options & DHCPCD_PRINT_PIDFILE)\n\t\t\tbreak;\n\t\tset_option_space(ctx, arg, &d, &dl, &od, &odl, ifo, &request,\n\t\t    &require, &no, &reject);\n\t\tif (make_option_mask(d, dl, od, odl, ifo->dstmask, arg, 2) !=\n\t\t    0) {\n\t\t\tif (errno == EINVAL)\n\t\t\t\tlogerrx(\"option does not take\"\n\t\t\t\t\t\" an IPv4 address: %s\",\n\t\t\t\t    arg);\n\t\t\telse\n\t\t\t\tlogerrx(\"unknown option: %s\", arg);\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n\tcase O_FALLBACK:\n\t\tARG_REQUIRED;\n\t\tfree(ifo->fallback);\n\t\tifo->fallback = strdup(arg);\n\t\tif (ifo->fallback == NULL) {\n\t\t\tlogerrx(__func__);\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n#endif\n\tcase O_IAID:\n\t\tARG_REQUIRED;\n\t\tif (ctx->options & DHCPCD_MANAGER && !IN_CONFIG_BLOCK(ifo)) {\n\t\t\tlogerrx(\"IAID must belong in an interface block\");\n\t\t\treturn -1;\n\t\t}\n\t\tif (parse_iaid(ifo->iaid, arg, sizeof(ifo->iaid)) == -1) {\n\t\t\tlogerrx(\"invalid IAID %s\", arg);\n\t\t\treturn -1;\n\t\t}\n\t\tifo->options |= DHCPCD_IAID;\n\t\tbreak;\n\tcase O_IPV6RS:\n\t\tifo->options |= DHCPCD_IPV6RS;\n\t\tbreak;\n\tcase O_NOIPV6RS:\n\t\tifo->options &= ~DHCPCD_IPV6RS;\n\t\tbreak;\n\tcase O_IPV6RA_FORK:\n\t\tifo->options &= ~DHCPCD_IPV6RA_REQRDNSS;\n\t\tbreak;\n\tcase O_IPV6RA_AUTOCONF:\n\t\tifo->options |= DHCPCD_IPV6RA_AUTOCONF;\n\t\tbreak;\n\tcase O_IPV6RA_NOAUTOCONF:\n\t\tifo->options &= ~DHCPCD_IPV6RA_AUTOCONF;\n\t\tbreak;\n\tcase O_NOALIAS:\n\t\tifo->options |= DHCPCD_NOALIAS;\n\t\tbreak;\n#ifdef DHCP6\n\tcase O_IA_NA:\n\t\ti = D6_OPTION_IA_NA;\n\t\t/* FALLTHROUGH */\n\tcase O_IA_TA:\n\t\tif (i == 0)\n\t\t\ti = D6_OPTION_IA_TA;\n\t\t/* FALLTHROUGH */\n\tcase O_IA_PD:\n\t\tif (i == 0) {\n#ifdef SMALL\n\t\t\tlogwarnx(\"%s: IA_PD not compiled in\", ifname);\n\t\t\treturn -1;\n#else\n\t\t\tif (ctx->options & DHCPCD_MANAGER &&\n\t\t\t    !IN_CONFIG_BLOCK(ifo)) {\n\t\t\t\tlogerrx(\"IA PD must belong in an \"\n\t\t\t\t\t\"interface block\");\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\ti = D6_OPTION_IA_PD;\n#endif\n\t\t}\n\t\tif (ctx->options & DHCPCD_MANAGER && !IN_CONFIG_BLOCK(ifo) &&\n\t\t    arg) {\n\t\t\tlogerrx(\"IA with IAID must belong in an \"\n\t\t\t\t\"interface block\");\n\t\t\treturn -1;\n\t\t}\n\t\tifo->options |= DHCPCD_IA_FORCED;\n\t\tfp = strwhite(arg);\n\t\tif (fp) {\n\t\t\t*fp++ = '\\0';\n\t\t\tfp = strskipwhite(fp);\n\t\t}\n\t\tif (arg) {\n\t\t\tp = strchr(arg, '/');\n\t\t\tif (p)\n\t\t\t\t*p++ = '\\0';\n\t\t\tif (parse_iaid(iaid, arg, sizeof(iaid)) == -1) {\n\t\t\t\tlogerr(\"invalid IAID: %s\", arg);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t\tia = NULL;\n\t\tfor (sl = 0; sl < ifo->ia_len; sl++) {\n\t\t\tif ((arg == NULL && !ifo->ia[sl].iaid_set) ||\n\t\t\t    (arg != NULL && ifo->ia[sl].iaid_set &&\n\t\t\t\tifo->ia[sl].ia_type == (uint16_t)i &&\n\t\t\t\tifo->ia[sl].iaid[0] == iaid[0] &&\n\t\t\t\tifo->ia[sl].iaid[1] == iaid[1] &&\n\t\t\t\tifo->ia[sl].iaid[2] == iaid[2] &&\n\t\t\t\tifo->ia[sl].iaid[3] == iaid[3])) {\n\t\t\t\tia = &ifo->ia[sl];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (ia == NULL) {\n\t\t\tia = reallocarray(ifo->ia, ifo->ia_len + 1,\n\t\t\t    sizeof(*ifo->ia));\n\t\t\tif (ia == NULL) {\n\t\t\t\tlogerr(__func__);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tifo->ia = ia;\n\t\t\tia = &ifo->ia[ifo->ia_len++];\n\t\t\tia->ia_type = (uint16_t)i;\n\t\t\tif (arg) {\n\t\t\t\tia->iaid[0] = iaid[0];\n\t\t\t\tia->iaid[1] = iaid[1];\n\t\t\t\tia->iaid[2] = iaid[2];\n\t\t\t\tia->iaid[3] = iaid[3];\n\t\t\t\tia->iaid_set = 1;\n\t\t\t} else\n\t\t\t\tia->iaid_set = 0;\n\t\t\tif (!ia->iaid_set || p == NULL ||\n\t\t\t    ia->ia_type == D6_OPTION_IA_TA) {\n\t\t\t\tmemset(&ia->addr, 0, sizeof(ia->addr));\n\t\t\t\tia->prefix_len = 0;\n\t\t\t} else {\n\t\t\t\targ = p;\n\t\t\t\tp = strchr(arg, '/');\n\t\t\t\tif (p)\n\t\t\t\t\t*p++ = '\\0';\n\t\t\t\tif (inet_pton(AF_INET6, arg, &ia->addr) != 1) {\n\t\t\t\t\tlogerrx(\"invalid AF_INET6: %s\", arg);\n\t\t\t\t\tmemset(&ia->addr, 0, sizeof(ia->addr));\n\t\t\t\t}\n\t\t\t\tif (p && ia->ia_type == D6_OPTION_IA_PD) {\n\t\t\t\t\tia->prefix_len = (uint8_t)strtou(p,\n\t\t\t\t\t    NULL, 0, 8, 120, &e);\n\t\t\t\t\tif (e) {\n\t\t\t\t\t\tlogerrx(\"%s: failed to convert\"\n\t\t\t\t\t\t\t\" prefix len\",\n\t\t\t\t\t\t    p);\n\t\t\t\t\t\tia->prefix_len = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n#ifndef SMALL\n\t\t\tia->sla_max = 0;\n\t\t\tia->sla_len = 0;\n\t\t\tia->sla = NULL;\n#endif\n\t\t}\n\n#ifdef SMALL\n\t\tbreak;\n#else\n\t\tif (ia->ia_type != D6_OPTION_IA_PD)\n\t\t\tbreak;\n\n\t\tfor (p = fp; p; p = fp) {\n\t\t\tfp = strwhite(p);\n\t\t\tif (fp) {\n\t\t\t\t*fp++ = '\\0';\n\t\t\t\tfp = strskipwhite(fp);\n\t\t\t}\n\t\t\tsla = reallocarray(ia->sla, ia->sla_len + 1,\n\t\t\t    sizeof(*ia->sla));\n\t\t\tif (sla == NULL) {\n\t\t\t\tlogerr(__func__);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tia->sla = sla;\n\t\t\tsla = &ia->sla[ia->sla_len++];\n\t\t\tnp = strchr(p, '/');\n\t\t\tif (np)\n\t\t\t\t*np++ = '\\0';\n\t\t\tif (strlcpy(sla->ifname, p, sizeof(sla->ifname)) >=\n\t\t\t    sizeof(sla->ifname)) {\n\t\t\t\tlogerrx(\"%s: interface name too long\", arg);\n\t\t\t\tgoto err_sla;\n\t\t\t}\n\t\t\tsla->sla_set = false;\n\t\t\tsla->prefix_len = 0;\n\t\t\tsla->suffix = 1;\n\t\t\tp = np;\n\t\t\tif (p) {\n\t\t\t\tnp = strchr(p, '/');\n\t\t\t\tif (np)\n\t\t\t\t\t*np++ = '\\0';\n\t\t\t\tif (*p != '\\0') {\n\t\t\t\t\tsla->sla = (uint32_t)strtou(p, NULL, 0,\n\t\t\t\t\t    0, UINT32_MAX, &e);\n\t\t\t\t\tsla->sla_set = true;\n\t\t\t\t\tif (e) {\n\t\t\t\t\t\tlogerrx(\"%s: failed to convert \"\n\t\t\t\t\t\t\t\"sla\",\n\t\t\t\t\t\t    ifname);\n\t\t\t\t\t\tgoto err_sla;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tp = np;\n\t\t\t}\n\t\t\tif (p) {\n\t\t\t\tnp = strchr(p, '/');\n\t\t\t\tif (np)\n\t\t\t\t\t*np++ = '\\0';\n\t\t\t\tif (*p != '\\0') {\n\t\t\t\t\tsla->prefix_len = (uint8_t)strtou(p,\n\t\t\t\t\t    NULL, 0, 0, 120, &e);\n\t\t\t\t\tif (e) {\n\t\t\t\t\t\tlogerrx(\"%s: failed to \"\n\t\t\t\t\t\t\t\"convert prefix len\",\n\t\t\t\t\t\t    ifname);\n\t\t\t\t\t\tgoto err_sla;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tp = np;\n\t\t\t}\n\t\t\tif (p) {\n\t\t\t\tnp = strchr(p, '/');\n\t\t\t\tif (np)\n\t\t\t\t\t*np = '\\0';\n\t\t\t\tif (*p != '\\0') {\n\t\t\t\t\tsla->suffix = (uint64_t)strtou(p, NULL,\n\t\t\t\t\t    0, 0, UINT64_MAX, &e);\n\t\t\t\t\tif (e) {\n\t\t\t\t\t\tlogerrx(\"%s: failed to \"\n\t\t\t\t\t\t\t\"convert suffix\",\n\t\t\t\t\t\t    ifname);\n\t\t\t\t\t\tgoto err_sla;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t/* Sanity check */\n\t\t\tfor (sl = 0; sl < ia->sla_len - 1; sl++) {\n\t\t\t\tslap = &ia->sla[sl];\n\t\t\t\tif (slap->sla_set != sla->sla_set) {\n\t\t\t\t\tlogerrx(\"%s: cannot mix automatic \"\n\t\t\t\t\t\t\"and fixed SLA\",\n\t\t\t\t\t    sla->ifname);\n\t\t\t\t\tgoto err_sla;\n\t\t\t\t}\n\t\t\t\tif (ia->prefix_len &&\n\t\t\t\t    (sla->prefix_len == ia->prefix_len ||\n\t\t\t\t\tslap->prefix_len == ia->prefix_len)) {\n\t\t\t\t\tlogerrx(\"%s: cannot delegte the same\"\n\t\t\t\t\t\t\"prefix length more than once\",\n\t\t\t\t\t    sla->ifname);\n\t\t\t\t\tgoto err_sla;\n\t\t\t\t}\n\t\t\t\tif (!sla->sla_set &&\n\t\t\t\t    strcmp(slap->ifname, sla->ifname) == 0) {\n\t\t\t\t\tlogwarnx(\"%s: cannot specify the \"\n\t\t\t\t\t\t \"same interface twice with \"\n\t\t\t\t\t\t \"an automatic SLA\",\n\t\t\t\t\t    sla->ifname);\n\t\t\t\t\tgoto err_sla;\n\t\t\t\t}\n\t\t\t\tif (slap->sla_set && sla->sla_set &&\n\t\t\t\t    slap->sla == sla->sla) {\n\t\t\t\t\tlogerrx(\"%s: cannot\"\n\t\t\t\t\t\t\" assign the same SLA %u\"\n\t\t\t\t\t\t\" more than once\",\n\t\t\t\t\t    sla->ifname, sla->sla);\n\t\t\t\t\tgoto err_sla;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (sla->sla_set && sla->sla > ia->sla_max)\n\t\t\t\tia->sla_max = sla->sla;\n\t\t}\n\t\tbreak;\n\terr_sla:\n\t\tia->sla_len--;\n\t\treturn -1;\n#endif\n#endif\n\tcase O_HOSTNAME_SHORT:\n\t\tifo->options |= DHCPCD_HOSTNAME | DHCPCD_HOSTNAME_SHORT;\n\t\tbreak;\n\tcase O_DEV:\n\t\tARG_REQUIRED;\n#ifdef PLUGIN_DEV\n\t\tif (ctx->dev_load)\n\t\t\tfree(ctx->dev_load);\n\t\tctx->dev_load = strdup(arg);\n#endif\n\t\tbreak;\n\tcase O_NODEV:\n\t\tifo->options &= ~DHCPCD_DEV;\n\t\tbreak;\n\tcase O_DEFINE:\n\t\tdop = &ifo->dhcp_override;\n\t\tdop_len = &ifo->dhcp_override_len;\n\t\t/* FALLTHROUGH */\n\tcase O_DEFINEND:\n\t\tif (dop == NULL) {\n\t\t\tdop = &ifo->nd_override;\n\t\t\tdop_len = &ifo->nd_override_len;\n\t\t}\n\t\t/* FALLTHROUGH */\n\tcase O_DEFINE6:\n\t\tif (dop == NULL) {\n\t\t\tdop = &ifo->dhcp6_override;\n\t\t\tdop_len = &ifo->dhcp6_override_len;\n\t\t}\n\t\t/* FALLTHROUGH */\n\tcase O_VENDOPT:\n\t\tif (dop == NULL) {\n\t\t\tdop = &ifo->vivso_override;\n\t\t\tdop_len = &ifo->vivso_override_len;\n\t\t}\n\t\t*edop = *ldop = NULL;\n\t\t/* FALLTHROUGH */\n\tcase O_EMBED:\n\t\tif (dop == NULL) {\n\t\t\tif (*edop) {\n\t\t\t\tdop = &(*edop)->embopts;\n\t\t\t\tdop_len = &(*edop)->embopts_len;\n\t\t\t} else if (*ldop) {\n\t\t\t\tdop = &(*ldop)->embopts;\n\t\t\t\tdop_len = &(*ldop)->embopts_len;\n\t\t\t} else {\n\t\t\t\tlogerrx(\"embed must be after a define \"\n\t\t\t\t\t\"or encap\");\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t\t/* FALLTHROUGH */\n\tcase O_ENCAP:\n\t\tARG_REQUIRED;\n\t\tif (dop == NULL) {\n\t\t\tif (*ldop == NULL) {\n\t\t\t\tlogerrx(\"encap must be after a define\");\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tdop = &(*ldop)->encopts;\n\t\t\tdop_len = &(*ldop)->encopts_len;\n\t\t}\n\n\t\t/* Shared code for define, define6, embed and encap */\n\n\t\t/* code */\n\t\tif (opt == O_EMBED) /* Embedded options don't have codes */\n\t\t\tu = 0;\n\t\telse {\n\t\t\tfp = strwhite(arg);\n\t\t\tif (fp == NULL) {\n\t\t\t\tlogerrx(\"invalid syntax: %s\", arg);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\t*fp++ = '\\0';\n\t\t\tu = (uint32_t)strtou(arg, NULL, 0, 0, UINT32_MAX, &e);\n\t\t\tif (e) {\n\t\t\t\tlogerrx(\"invalid code: %s\", arg);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\targ = strskipwhite(fp);\n\t\t\tif (arg == NULL) {\n\t\t\t\tlogerrx(\"invalid syntax\");\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t\t/* type */\n\t\tfp = strwhite(arg);\n\t\tif (fp)\n\t\t\t*fp++ = '\\0';\n\t\tnp = strchr(arg, ':');\n\t\t/* length */\n\t\tif (np) {\n\t\t\t*np++ = '\\0';\n\t\t\tbp = NULL; /* No bitflag */\n\t\t\tl = (long)strtou(np, NULL, 0, 0, LONG_MAX, &e);\n\t\t\tif (e) {\n\t\t\t\tlogerrx(\"failed to convert length\");\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t} else {\n\t\t\tl = 0;\n\t\t\tbp = strchr(arg, '='); /* bitflag assignment */\n\t\t\tif (bp)\n\t\t\t\t*bp++ = '\\0';\n\t\t}\n\t\tt = 0;\n\t\tif (strcasecmp(arg, \"request\") == 0) {\n\t\t\tt |= OT_REQUEST;\n\t\t\targ = strskipwhite(fp);\n\t\t\tfp = strwhite(arg);\n\t\t\tif (fp == NULL) {\n\t\t\t\tlogerrx(\"incomplete request type\");\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\t*fp++ = '\\0';\n\t\t} else if (strcasecmp(arg, \"norequest\") == 0) {\n\t\t\tt |= OT_NOREQ;\n\t\t\targ = strskipwhite(fp);\n\t\t\tfp = strwhite(arg);\n\t\t\tif (fp == NULL) {\n\t\t\t\tlogerrx(\"incomplete request type\");\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\t*fp++ = '\\0';\n\t\t}\n\t\tif (strcasecmp(arg, \"optional\") == 0) {\n\t\t\tt |= OT_OPTIONAL;\n\t\t\targ = strskipwhite(fp);\n\t\t\tfp = strwhite(arg);\n\t\t\tif (fp == NULL) {\n\t\t\t\tlogerrx(\"incomplete optional type\");\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\t*fp++ = '\\0';\n\t\t}\n\t\tif (strcasecmp(arg, \"index\") == 0) {\n\t\t\tt |= OT_INDEX;\n\t\t\targ = strskipwhite(fp);\n\t\t\tfp = strwhite(arg);\n\t\t\tif (fp == NULL) {\n\t\t\t\tlogerrx(\"incomplete index type\");\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\t*fp++ = '\\0';\n\t\t}\n\t\tif (strcasecmp(arg, \"array\") == 0) {\n\t\t\tt |= OT_ARRAY;\n\t\t\targ = strskipwhite(fp);\n\t\t\tfp = strwhite(arg);\n\t\t\tif (fp == NULL) {\n\t\t\t\tlogerrx(\"incomplete array type\");\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\t*fp++ = '\\0';\n\t\t} else if (strcasecmp(arg, \"truncated\") == 0) {\n\t\t\tt |= OT_TRUNCATED;\n\t\t\targ = strskipwhite(fp);\n\t\t\tfp = strwhite(arg);\n\t\t\tif (fp == NULL) {\n\t\t\t\tlogerrx(\"incomplete truncated type\");\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\t*fp++ = '\\0';\n\t\t}\n\t\tif (strcasecmp(arg, \"ipaddress\") == 0)\n\t\t\tt |= OT_ADDRIPV4;\n\t\telse if (strcasecmp(arg, \"ip6address\") == 0)\n\t\t\tt |= OT_ADDRIPV6;\n\t\telse if (strcasecmp(arg, \"string\") == 0)\n\t\t\tt |= OT_STRING;\n\t\telse if (strcasecmp(arg, \"uri\") == 0)\n\t\t\tt |= OT_URI;\n\t\telse if (strcasecmp(arg, \"byte\") == 0)\n\t\t\tt |= OT_UINT8;\n\t\telse if (strcasecmp(arg, \"bitflags\") == 0)\n\t\t\tt |= OT_BITFLAG;\n\t\telse if (strcasecmp(arg, \"uint8\") == 0)\n\t\t\tt |= OT_UINT8;\n\t\telse if (strcasecmp(arg, \"int8\") == 0)\n\t\t\tt |= OT_INT8;\n\t\telse if (strcasecmp(arg, \"uint16\") == 0)\n\t\t\tt |= OT_UINT16;\n\t\telse if (strcasecmp(arg, \"int16\") == 0)\n\t\t\tt |= OT_INT16;\n\t\telse if (strcasecmp(arg, \"uint32\") == 0)\n\t\t\tt |= OT_UINT32;\n\t\telse if (strcasecmp(arg, \"int32\") == 0)\n\t\t\tt |= OT_INT32;\n\t\telse if (strcasecmp(arg, \"flag\") == 0)\n\t\t\tt |= OT_FLAG;\n\t\telse if (strcasecmp(arg, \"raw\") == 0)\n\t\t\tt |= OT_STRING | OT_RAW;\n\t\telse if (strcasecmp(arg, \"ascii\") == 0)\n\t\t\tt |= OT_STRING | OT_ASCII;\n\t\telse if (strcasecmp(arg, \"domain\") == 0)\n\t\t\tt |= OT_STRING | OT_DOMAIN | OT_RFC1035;\n\t\telse if (strcasecmp(arg, \"dname\") == 0)\n\t\t\tt |= OT_STRING | OT_DOMAIN;\n\t\telse if (strcasecmp(arg, \"binhex\") == 0)\n\t\t\tt |= OT_STRING | OT_BINHEX;\n\t\telse if (strcasecmp(arg, \"embed\") == 0)\n\t\t\tt |= OT_EMBED;\n\t\telse if (strcasecmp(arg, \"encap\") == 0)\n\t\t\tt |= OT_ENCAP;\n\t\telse if (strcasecmp(arg, \"rfc3361\") == 0)\n\t\t\tt |= OT_STRING | OT_RFC3361;\n\t\telse if (strcasecmp(arg, \"rfc3442\") == 0)\n\t\t\tt |= OT_STRING | OT_RFC3442;\n\t\telse if (strcasecmp(arg, \"option\") == 0)\n\t\t\tt |= OT_OPTION;\n\t\telse {\n\t\t\tlogerrx(\"unknown type: %s\", arg);\n\t\t\treturn -1;\n\t\t}\n\t\tif (l && !(t & (OT_STRING | OT_BINHEX))) {\n\t\t\tlogwarnx(\"ignoring length for type: %s\", arg);\n\t\t\tl = 0;\n\t\t}\n\t\tif (t & OT_ARRAY && t & (OT_STRING | OT_BINHEX) &&\n\t\t    !(t & (OT_RFC1035 | OT_DOMAIN))) {\n\t\t\tlogwarnx(\"ignoring array for strings\");\n\t\t\tt &= ~OT_ARRAY;\n\t\t}\n\t\tif (t & OT_BITFLAG) {\n\t\t\tif (bp == NULL)\n\t\t\t\tlogwarnx(\"missing bitflag assignment\");\n\t\t}\n\t\t/* variable */\n\t\tif (!fp) {\n\t\t\tif (!(t & OT_OPTION)) {\n\t\t\t\tlogerrx(\"type %s requires a variable name\",\n\t\t\t\t    arg);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tnp = NULL;\n\t\t} else {\n\t\t\targ = strskipwhite(fp);\n\t\t\tfp = strwhite(arg);\n\t\t\tif (fp)\n\t\t\t\t*fp++ = '\\0';\n\t\t\tif (strcasecmp(arg, \"reserved\")) {\n\t\t\t\tnp = strdup(arg);\n\t\t\t\tif (np == NULL) {\n\t\t\t\t\tlogerr(__func__);\n\t\t\t\t\treturn -1;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tnp = NULL;\n\t\t\t\tt |= OT_RESERVED;\n\t\t\t}\n\t\t}\n\t\tif (t & OT_TRUNCATED && !(t & OT_ADDRIPV6)) {\n\t\t\tlogerrx(\"truncated only works for ip6address\");\n\t\t\treturn -1;\n\t\t}\n\t\tif (opt != O_EMBED) {\n\t\t\tfor (dl = 0, ndop = *dop; dl < *dop_len; dl++, ndop++) {\n\t\t\t\t/* type 0 seems freshly malloced struct\n\t\t\t\t * for us to use */\n\t\t\t\tif (ndop->option == u || ndop->type == 0)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (dl == *dop_len)\n\t\t\t\tndop = NULL;\n\t\t} else\n\t\t\tndop = NULL;\n\t\tif (ndop == NULL) {\n\t\t\tndop = reallocarray(*dop, *dop_len + 1, sizeof(**dop));\n\t\t\tif (ndop == NULL) {\n\t\t\t\tlogerr(__func__);\n\t\t\t\tfree(np);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\t*dop = ndop;\n\t\t\tndop = &(*dop)[(*dop_len)++];\n\t\t\tndop->embopts = NULL;\n\t\t\tndop->embopts_len = 0;\n\t\t\tndop->encopts = NULL;\n\t\t\tndop->encopts_len = 0;\n\t\t} else\n\t\t\tfree_dhcp_opt_embenc(ndop);\n\t\tndop->option = (uint32_t)u; /* could have been 0 */\n\t\tndop->type = t;\n\t\tndop->len = (size_t)l;\n\t\tndop->var = np;\n\t\tif (bp) {\n\t\t\tdl = strlen(bp);\n\t\t\tif (dl > sizeof(ndop->bitflags)) {\n\t\t\t\tlogwarnx(\"bitflag string too long %s\", bp);\n\t\t\t\tdl = sizeof(ndop->bitflags);\n\t\t\t}\n\t\t\tmemcpy(ndop->bitflags, bp, dl);\n\t\t\tmemset(ndop->bitflags + dl, 0,\n\t\t\t    sizeof(ndop->bitflags) - dl);\n\t\t} else\n\t\t\tmemset(ndop->bitflags, 0, sizeof(ndop->bitflags));\n\t\t/* Save the define for embed and encap options */\n\t\tswitch (opt) {\n\t\tcase O_DEFINE:\n\t\tcase O_DEFINEND:\n\t\tcase O_DEFINE6:\n\t\tcase O_VENDOPT:\n\t\t\t*ldop = ndop;\n\t\t\tbreak;\n\t\tcase O_ENCAP:\n\t\t\t*edop = ndop;\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase O_VENDCLASS:\n\t\tARG_REQUIRED;\n#ifdef SMALL\n\t\tlogwarnx(\"%s: vendor options not compiled in\", ifname);\n\t\treturn -1;\n#else\n\t\tfp = strwhite(arg);\n\t\tif (fp)\n\t\t\t*fp++ = '\\0';\n\t\tu = (uint32_t)strtou(arg, NULL, 0, 0, UINT32_MAX, &e);\n\t\tif (e) {\n\t\t\tlogerrx(\"invalid code: %s\", arg);\n\t\t\treturn -1;\n\t\t}\n\t\tfor (vivco = ifo->vivco; vivco != vivco_endp; vivco++) {\n\t\t\tif (vivco->en == (uint32_t)u) {\n\t\t\t\tlogerrx(\n\t\t\t\t    \"vendor class option for enterprise number %u already defined\",\n\t\t\t\t    vivco->en);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\t\tfp = strskipwhite(fp);\n\t\tif (fp) {\n\t\t\ts = parse_string(NULL, 0, fp);\n\t\t\tif (s == -1) {\n\t\t\t\tlogerr(__func__);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tdl = (size_t)s;\n\t\t\tif (dl + (sizeof(uint16_t) * 2) > UINT16_MAX) {\n\t\t\t\tlogerrx(\"vendor class is too big\");\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tnp = malloc(dl);\n\t\t\tif (np == NULL) {\n\t\t\t\tlogerr(__func__);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tparse_string(np, dl, fp);\n\t\t} else {\n\t\t\tdl = 0;\n\t\t\tnp = NULL;\n\t\t}\n\t\tvivco = reallocarray(ifo->vivco, ifo->vivco_len + 1,\n\t\t    sizeof(*ifo->vivco));\n\t\tif (vivco == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\tfree(np);\n\t\t\treturn -1;\n\t\t}\n\t\tifo->vivco = vivco;\n\t\tvivco = &ifo->vivco[ifo->vivco_len++];\n\t\tvivco->en = (uint32_t)u;\n\t\tvivco->len = dl;\n\t\tvivco->data = (uint8_t *)np;\n\t\tbreak;\n#endif\n\tcase O_AUTHPROTOCOL:\n\t\tARG_REQUIRED;\n#ifdef AUTH\n\t\tfp = strwhite(arg);\n\t\tif (fp)\n\t\t\t*fp++ = '\\0';\n\t\tif (strcasecmp(arg, \"token\") == 0)\n\t\t\tifo->auth.protocol = AUTH_PROTO_TOKEN;\n\t\telse if (strcasecmp(arg, \"delayed\") == 0)\n\t\t\tifo->auth.protocol = AUTH_PROTO_DELAYED;\n\t\telse if (strcasecmp(arg, \"delayedrealm\") == 0)\n\t\t\tifo->auth.protocol = AUTH_PROTO_DELAYEDREALM;\n\t\telse {\n\t\t\tlogerrx(\"%s: unsupported protocol\", arg);\n\t\t\treturn -1;\n\t\t}\n\t\targ = strskipwhite(fp);\n\t\tfp = strwhite(arg);\n\t\tif (arg == NULL) {\n\t\t\tifo->auth.options |= DHCPCD_AUTH_SEND;\n\t\t\tif (ifo->auth.protocol == AUTH_PROTO_TOKEN)\n\t\t\t\tifo->auth.protocol = AUTH_ALG_NONE;\n\t\t\telse\n\t\t\t\tifo->auth.algorithm = AUTH_ALG_HMAC_MD5;\n\t\t\tifo->auth.rdm = AUTH_RDM_MONOTONIC;\n\t\t\tbreak;\n\t\t}\n\t\tif (fp)\n\t\t\t*fp++ = '\\0';\n\t\tif (ifo->auth.protocol == AUTH_PROTO_TOKEN) {\n\t\t\tnp = strchr(arg, '/');\n\t\t\tif (np) {\n\t\t\t\tif (fp == NULL || np < fp)\n\t\t\t\t\t*np++ = '\\0';\n\t\t\t\telse\n\t\t\t\t\tnp = NULL;\n\t\t\t}\n\t\t\tif (parse_uint32(&ifo->auth.token_snd_secretid, arg) ==\n\t\t\t    -1)\n\t\t\t\tlogerrx(\"%s: not a number\", arg);\n\t\t\telse\n\t\t\t\tifo->auth.token_rcv_secretid =\n\t\t\t\t    ifo->auth.token_snd_secretid;\n\t\t\tif (np &&\n\t\t\t    parse_uint32(&ifo->auth.token_rcv_secretid, np) ==\n\t\t\t\t-1)\n\t\t\t\tlogerrx(\"%s: not a number\", arg);\n\t\t} else {\n\t\t\tif (strcasecmp(arg, \"hmacmd5\") == 0 ||\n\t\t\t    strcasecmp(arg, \"hmac-md5\") == 0)\n\t\t\t\tifo->auth.algorithm = AUTH_ALG_HMAC_MD5;\n\t\t\telse {\n\t\t\t\tlogerrx(\"%s: unsupported algorithm\", arg);\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\t\targ = fp;\n\t\tif (arg == NULL) {\n\t\t\tifo->auth.options |= DHCPCD_AUTH_SEND;\n\t\t\tifo->auth.rdm = AUTH_RDM_MONOTONIC;\n\t\t\tbreak;\n\t\t}\n\t\tif (strcasecmp(arg, \"monocounter\") == 0) {\n\t\t\tifo->auth.rdm = AUTH_RDM_MONOTONIC;\n\t\t\tifo->auth.options |= DHCPCD_AUTH_RDM_COUNTER;\n\t\t} else if (strcasecmp(arg, \"monotonic\") == 0 ||\n\t\t    strcasecmp(arg, \"monotime\") == 0)\n\t\t\tifo->auth.rdm = AUTH_RDM_MONOTONIC;\n\t\telse {\n\t\t\tlogerrx(\"%s: unsupported RDM\", arg);\n\t\t\treturn -1;\n\t\t}\n\t\tifo->auth.options |= DHCPCD_AUTH_SEND;\n\t\tbreak;\n#else\n\t\tlogerrx(\"no authentication support\");\n\t\treturn -1;\n#endif\n\tcase O_AUTHTOKEN:\n\t\tARG_REQUIRED;\n#ifdef AUTH\n\t\tfp = strwhite(arg);\n\t\tif (fp == NULL) {\n\t\t\tlogerrx(\"authtoken requires a realm\");\n\t\t\treturn -1;\n\t\t}\n\t\t*fp++ = '\\0';\n\t\ttoken = calloc(1, sizeof(*token));\n\t\tif (token == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\treturn -1;\n\t\t}\n\t\tif (parse_uint32(&token->secretid, arg) == -1) {\n\t\t\tlogerrx(\"%s: not a number\", arg);\n\t\t\tgoto invalid_token;\n\t\t}\n\t\targ = fp;\n\t\tfp = strend(arg);\n\t\tif (fp == NULL) {\n\t\t\tlogerrx(\"authtoken requires a realm\");\n\t\t\tgoto invalid_token;\n\t\t}\n\t\t*fp++ = '\\0';\n\t\ts = parse_string(NULL, 0, arg);\n\t\tif (s == -1) {\n\t\t\tlogerr(\"realm_len\");\n\t\t\tgoto invalid_token;\n\t\t}\n\t\tif (s != 0) {\n\t\t\ttoken->realm_len = (size_t)s;\n\t\t\ttoken->realm = malloc(token->realm_len);\n\t\t\tif (token->realm == NULL) {\n\t\t\t\tlogerr(__func__);\n\t\t\t\tgoto invalid_token;\n\t\t\t}\n\t\t\tparse_string((char *)token->realm, token->realm_len,\n\t\t\t    arg);\n\t\t}\n\t\targ = fp;\n\t\tfp = strend(arg);\n\t\tif (fp == NULL) {\n\t\t\tlogerrx(\"authtoken requies an expiry date\");\n\t\t\tgoto invalid_token;\n\t\t}\n\t\t*fp++ = '\\0';\n\t\tif (*arg == '\"') {\n\t\t\targ++;\n\t\t\tnp = strchr(arg, '\"');\n\t\t\tif (np)\n\t\t\t\t*np = '\\0';\n\t\t}\n\t\tif (strcmp(arg, \"0\") == 0 || strcasecmp(arg, \"forever\") == 0)\n\t\t\ttoken->expire = 0;\n\t\telse {\n\t\t\tstruct tm tm;\n\n\t\t\tmemset(&tm, 0, sizeof(tm));\n\t\t\tif (strptime(arg, \"%Y-%m-%d %H:%M\", &tm) == NULL) {\n\t\t\t\tlogerrx(\"%s: invalid date time\", arg);\n\t\t\t\tgoto invalid_token;\n\t\t\t}\n\t\t\tif ((token->expire = mktime(&tm)) == (time_t)-1) {\n\t\t\t\tlogerr(\"%s: mktime\", __func__);\n\t\t\t\tgoto invalid_token;\n\t\t\t}\n\t\t}\n\t\targ = fp;\n\t\ts = parse_string(NULL, 0, arg);\n\t\tif (s == -1 || s == 0) {\n\t\t\tif (s == -1)\n\t\t\t\tlogerr(\"token_len\");\n\t\t\telse\n\t\t\t\tlogerrx(\"authtoken requires a key\");\n\t\t\tgoto invalid_token;\n\t\t}\n\t\ttoken->key_len = (size_t)s;\n\t\ttoken->key = malloc(token->key_len);\n\t\tif (token->key == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\tgoto invalid_token;\n\t\t}\n\t\tparse_string((char *)token->key, token->key_len, arg);\n\t\tTAILQ_INSERT_TAIL(&ifo->auth.tokens, token, next);\n\t\tbreak;\n\n\tinvalid_token:\n\t\tfree(token->realm);\n\t\tfree(token);\n#else\n\t\tlogerrx(\"no authentication support\");\n#endif\n\t\treturn -1;\n\tcase O_AUTHNOTREQUIRED:\n\t\tifo->auth.options &= ~DHCPCD_AUTH_REQUIRE;\n\t\tbreak;\n\tcase O_DHCP:\n\t\tifo->options |= DHCPCD_DHCP | DHCPCD_WANTDHCP | DHCPCD_IPV4;\n\t\tbreak;\n\tcase O_NODHCP:\n\t\tifo->options &= ~DHCPCD_DHCP;\n\t\tbreak;\n\tcase O_DHCP6:\n\t\tifo->options |= DHCPCD_DHCP6 | DHCPCD_IPV6;\n\t\tbreak;\n\tcase O_NODHCP6:\n\t\tifo->options &= ~DHCPCD_DHCP6;\n\t\tbreak;\n\tcase O_CONTROLGRP:\n\t\tARG_REQUIRED;\n#ifdef PRIVSEP\n\t\t/* Control group is already set by this point.\n\t\t * We don't need to pledge getpw either with this. */\n\t\tif (IN_PRIVSEP(ctx))\n\t\t\tbreak;\n#endif\n#ifdef _REENTRANT\n\t\tl = sysconf(_SC_GETGR_R_SIZE_MAX);\n\t\tif (l == -1)\n\t\t\tdl = 1024;\n\t\telse\n\t\t\tdl = (size_t)l;\n\t\tp = malloc(dl);\n\t\tif (p == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\treturn -1;\n\t\t}\n\t\twhile ((i = getgrnam_r(arg, &grpbuf, p, dl, &grp)) == ERANGE) {\n\t\t\tsize_t nl = dl * 2;\n\t\t\tif (nl < dl) {\n\t\t\t\tlogerrx(\"control_group: out of buffer\");\n\t\t\t\tfree(p);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tdl = nl;\n\t\t\tnp = realloc(p, dl);\n\t\t\tif (np == NULL) {\n\t\t\t\tlogerr(__func__);\n\t\t\t\tfree(p);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tp = np;\n\t\t}\n\t\tif (i != 0) {\n\t\t\terrno = i;\n\t\t\tlogerr(\"getgrnam_r\");\n\t\t\tfree(p);\n\t\t\treturn -1;\n\t\t}\n\t\tif (grp == NULL) {\n\t\t\tif (!ctx->control_group)\n\t\t\t\tlogerrx(\"controlgroup: %s: not found\", arg);\n\t\t\tfree(p);\n\t\t\treturn -1;\n\t\t}\n\t\tctx->control_group = grp->gr_gid;\n\t\tfree(p);\n#else\n\t\tgrp = getgrnam(arg);\n\t\tif (grp == NULL) {\n\t\t\tif (!ctx->control_group)\n\t\t\t\tlogerrx(\"controlgroup: %s: not found\", arg);\n\t\t\treturn -1;\n\t\t}\n\t\tctx->control_group = grp->gr_gid;\n#endif\n\t\tbreak;\n\tcase O_GATEWAY:\n\t\tifo->options |= DHCPCD_GATEWAY;\n\t\tbreak;\n\tcase O_NOUP:\n\t\tifo->options &= ~DHCPCD_IF_UP;\n\t\tbreak;\n\tcase O_SLAAC:\n\t\tARG_REQUIRED;\n\t\tnp = strwhite(arg);\n\t\tif (np != NULL) {\n\t\t\t*np++ = '\\0';\n\t\t\tnp = strskipwhite(np);\n\t\t}\n\t\tif (strcmp(arg, \"private\") == 0 ||\n\t\t    strcmp(arg, \"stableprivate\") == 0 ||\n\t\t    strcmp(arg, \"stable\") == 0)\n\t\t\tifo->options |= DHCPCD_SLAACPRIVATE;\n\t\telse\n\t\t\tifo->options &= ~DHCPCD_SLAACPRIVATE;\n#ifdef INET6\n\t\tif (strcmp(arg, \"token\") == 0) {\n\t\t\tif (np == NULL) {\n\t\t\t\tlogerrx(\"slaac token: no token specified\");\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\targ = np;\n\t\t\tnp = strwhite(np);\n\t\t\tif (np != NULL) {\n\t\t\t\t*np++ = '\\0';\n\t\t\t\tnp = strskipwhite(np);\n\t\t\t}\n\t\t\tif (inet_pton(AF_INET6, arg, &ifo->token) != 1) {\n\t\t\t\tlogerrx(\"slaac token: invalid token\");\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n#endif\n\t\tif (np != NULL &&\n\t\t    (strcmp(np, \"temp\") == 0 || strcmp(np, \"temporary\") == 0))\n\t\t\tifo->options |= DHCPCD_SLAACTEMP;\n\t\tbreak;\n\tcase O_BOOTP:\n\t\tifo->options |= DHCPCD_BOOTP;\n\t\tbreak;\n\tcase O_NODELAY:\n\t\tifo->options &= ~DHCPCD_INITIAL_DELAY;\n\t\tbreak;\n\tcase O_LASTLEASE_EXTEND:\n\t\tifo->options |= DHCPCD_LASTLEASE | DHCPCD_LASTLEASE_EXTEND;\n\t\tbreak;\n\tcase O_INACTIVE:\n\t\tifo->options |= DHCPCD_INACTIVE;\n\t\tbreak;\n\tcase O_MUDURL:\n\t\tARG_REQUIRED;\n\t\ts = parse_string((char *)ifo->mudurl + 1,\n\t\t    sizeof(ifo->mudurl) - 1, arg);\n\t\tif (s == -1) {\n\t\t\tlogerr(\"mudurl\");\n\t\t\treturn -1;\n\t\t}\n\t\t*ifo->mudurl = (uint8_t)s;\n\t\tbreak;\n\tcase O_LINK_RCVBUF:\n#ifndef SMALL\n\t\tARG_REQUIRED;\n\t\tctx->link_rcvbuf = (int)strtoi(arg, NULL, 0, 0, INT32_MAX, &e);\n\t\tif (e) {\n\t\t\tlogerrx(\"failed to convert link_rcvbuf %s\", arg);\n\t\t\treturn -1;\n\t\t}\n#endif\n\t\tbreak;\n\tcase O_CONFIGURE:\n\t\tifo->options |= DHCPCD_CONFIGURE;\n\t\tbreak;\n\tcase O_NOCONFIGURE:\n\t\tifo->options &= ~DHCPCD_CONFIGURE;\n\t\tbreak;\n\tcase O_ARP_PERSISTDEFENCE:\n\t\tifo->options |= DHCPCD_ARP_PERSISTDEFENCE;\n\t\tbreak;\n\tcase O_REQUEST_TIME:\n\t\tARG_REQUIRED;\n\t\tifo->request_time = (uint32_t)strtou(arg, NULL, 0, 0,\n\t\t    UINT32_MAX, &e);\n\t\tif (e) {\n\t\t\tlogerrx(\"invalid request time: %s\", arg);\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n#ifdef INET\n\tcase O_FALLBACK_TIME:\n\t\tARG_REQUIRED;\n\t\tifo->fallback_time = (uint32_t)strtou(arg, NULL, 0, 0,\n\t\t    UINT32_MAX, &e);\n\t\tif (e) {\n\t\t\tlogerrx(\"invalid fallback time: %s\", arg);\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n\tcase O_IPV4LL_TIME:\n\t\tARG_REQUIRED;\n\t\tifo->ipv4ll_time = (uint32_t)strtou(arg, NULL, 0, 0, UINT32_MAX,\n\t\t    &e);\n\t\tif (e) {\n\t\t\tlogerrx(\"invalid ipv4ll time: %s\", arg);\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n#endif\n\tcase O_NOSYSLOG: {\n\t\tunsigned int logopts = loggetopts();\n\n\t\tlogopts &= ~LOGERR_LOG;\n\t\tlogsetopts(logopts);\n\t} break;\n\tdefault:\n\t\treturn 0;\n\t}\n\n\treturn 1;\n\n#ifdef ARG_REQUIRED\narg_required:\n\tlogerrx(\"option %d requires an argument\", opt);\n\treturn -1;\n#undef ARG_REQUIRED\n#endif\n}\n\nstatic int\nparse_config_line(struct dhcpcd_ctx *ctx, const char *ifname,\n    struct if_options *ifo, const char *opt, char *line, struct dhcp_opt **ldop,\n    struct dhcp_opt **edop)\n{\n\tunsigned int i;\n\n\tfor (i = 0; i < sizeof(cf_options) / sizeof(cf_options[0]); i++) {\n\t\tif (!cf_options[i].name || strcmp(cf_options[i].name, opt) != 0)\n\t\t\tcontinue;\n\n\t\tif (cf_options[i].has_arg == required_argument && !line) {\n\t\t\tlogerrx(\"option requires an argument -- %s\", opt);\n\t\t\treturn -1;\n\t\t}\n\n\t\treturn parse_option(ctx, ifname, ifo, cf_options[i].val, line,\n\t\t    ldop, edop);\n\t}\n\n\tif (!(ctx->options & DHCPCD_PRINT_PIDFILE))\n\t\tlogerrx(\"unknown option: %s\", opt);\n\treturn -1;\n}\n\nstatic void\nfinish_config(struct if_options *ifo)\n{\n\t/* Terminate the encapsulated options */\n\tif (ifo->vendor[0] && !(ifo->options & DHCPCD_VENDORRAW)) {\n\t\tifo->vendor[0]++;\n\t\tifo->vendor[ifo->vendor[0]] = DHO_END;\n\t\t/* We are called twice.\n\t\t * This should be fixed, but in the meantime, this\n\t\t * guard should suffice */\n\t\tifo->options |= DHCPCD_VENDORRAW;\n\t}\n\n\tif (!(ifo->options & DHCPCD_ARP) ||\n\t    ifo->options & (DHCPCD_INFORM | DHCPCD_STATIC))\n\t\tifo->options &= ~DHCPCD_IPV4LL;\n\n\tif (!(ifo->options & DHCPCD_IPV4))\n\t\tifo->options &= ~(DHCPCD_DHCP | DHCPCD_IPV4LL | DHCPCD_WAITIP4);\n\n\tif (!(ifo->options & DHCPCD_IPV6))\n\t\tifo->options &= ~(\n\t\t    DHCPCD_IPV6RS | DHCPCD_DHCP6 | DHCPCD_WAITIP6);\n\n\tif (!(ifo->options & DHCPCD_IPV6RS))\n\t\tifo->options &= ~(\n\t\t    DHCPCD_IPV6RA_AUTOCONF | DHCPCD_IPV6RA_REQRDNSS);\n}\n\nstatic struct if_options *\ndefault_config(struct dhcpcd_ctx *ctx)\n{\n\tstruct if_options *ifo;\n\n\t/* Seed our default options */\n\tif ((ifo = calloc(1, sizeof(*ifo))) == NULL) {\n\t\tlogerr(__func__);\n\t\treturn NULL;\n\t}\n\tifo->options |= DHCPCD_IF_UP | DHCPCD_LINK | DHCPCD_INITIAL_DELAY;\n\tifo->timeout = DEFAULT_TIMEOUT;\n\tifo->reboot = DEFAULT_REBOOT;\n\tifo->request_time = DEFAULT_REQUEST;\n#ifdef INET\n\tifo->fallback_time = DEFAULT_FALLBACK;\n\tifo->ipv4ll_time = DEFAULT_IPV4LL;\n#endif\n\tifo->metric = -1;\n\tifo->auth.options |= DHCPCD_AUTH_REQUIRE;\n\trb_tree_init(&ifo->routes, &rt_compare_list_ops);\n#ifdef AUTH\n\tTAILQ_INIT(&ifo->auth.tokens);\n#endif\n\n\t/* Inherit some global defaults */\n\tif (ctx->options & DHCPCD_CONFIGURE)\n\t\tifo->options |= DHCPCD_CONFIGURE;\n\tif (ctx->options & DHCPCD_PERSISTENT)\n\t\tifo->options |= DHCPCD_PERSISTENT;\n\tif (ctx->options & DHCPCD_SLAACPRIVATE)\n\t\tifo->options |= DHCPCD_SLAACPRIVATE;\n\n\treturn ifo;\n}\n\nstruct if_options *\nread_config(struct dhcpcd_ctx *ctx, const char *ifname, const char *ssid,\n    const char *profile)\n{\n\tstruct if_options *ifo;\n\tchar buf[UDPLEN_MAX], *bp; /* 64k max config file size */\n\tchar *line, *option, *p;\n\tssize_t buflen;\n\tsize_t vlen;\n\tint skip, have_profile, new_block, had_block;\n#if !defined(INET) || !defined(INET6)\n\tsize_t i;\n\tstruct dhcp_opt *opt;\n#endif\n\tstruct dhcp_opt *ldop, *edop;\n\n\t/* Seed our default options */\n\tif ((ifo = default_config(ctx)) == NULL)\n\t\treturn NULL;\n\tif (default_options == 0) {\n\t\tdefault_options |= DHCPCD_CONFIGURE | DHCPCD_DAEMONISE |\n\t\t    DHCPCD_GATEWAY;\n#ifdef INET\n\t\tskip = xsocket(PF_INET, SOCK_DGRAM, 0);\n\t\tif (skip != -1) {\n\t\t\tclose(skip);\n\t\t\tdefault_options |= DHCPCD_IPV4 | DHCPCD_ARP |\n\t\t\t    DHCPCD_DHCP | DHCPCD_IPV4LL;\n\t\t}\n#endif\n#ifdef INET6\n\t\tskip = xsocket(PF_INET6, SOCK_DGRAM, 0);\n\t\tif (skip != -1) {\n\t\t\tclose(skip);\n\t\t\tdefault_options |= DHCPCD_IPV6 | DHCPCD_IPV6RS |\n\t\t\t    DHCPCD_IPV6RA_AUTOCONF | DHCPCD_IPV6RA_REQRDNSS |\n\t\t\t    DHCPCD_DHCP6;\n\t\t}\n#endif\n#ifdef PLUGIN_DEV\n\t\tdefault_options |= DHCPCD_DEV;\n#endif\n\t}\n\tifo->options |= default_options;\n\n\tCLEAR_CONFIG_BLOCK(ifo);\n\n\tvlen = strlcpy((char *)ifo->vendorclassid + 1, ctx->vendor,\n\t    sizeof(ifo->vendorclassid) - 1);\n\tifo->vendorclassid[0] = (uint8_t)(vlen > 255 ? 0 : vlen);\n\n\t/* Reset route order */\n\tctx->rt_order = 0;\n\n\t/* Parse our embedded options file */\n\tif (ifname == NULL && !(ctx->options & DHCPCD_PRINT_PIDFILE)) {\n\t\t/* Space for initial estimates */\n#if defined(INET) && defined(INITDEFINES)\n\t\tifo->dhcp_override = calloc(INITDEFINES,\n\t\t    sizeof(*ifo->dhcp_override));\n\t\tif (ifo->dhcp_override == NULL)\n\t\t\tlogerr(__func__);\n\t\telse\n\t\t\tifo->dhcp_override_len = INITDEFINES;\n#endif\n\n#if defined(INET6) && defined(INITDEFINENDS)\n\t\tifo->nd_override = calloc(INITDEFINENDS,\n\t\t    sizeof(*ifo->nd_override));\n\t\tif (ifo->nd_override == NULL)\n\t\t\tlogerr(__func__);\n\t\telse\n\t\t\tifo->nd_override_len = INITDEFINENDS;\n#endif\n#if defined(INET6) && defined(INITDEFINE6S)\n\t\tifo->dhcp6_override = calloc(INITDEFINE6S,\n\t\t    sizeof(*ifo->dhcp6_override));\n\t\tif (ifo->dhcp6_override == NULL)\n\t\t\tlogerr(__func__);\n\t\telse\n\t\t\tifo->dhcp6_override_len = INITDEFINE6S;\n#endif\n\n\t\t/* Now load our embedded config */\n#ifdef EMBEDDED_CONFIG\n\t\tbuflen = dhcp_readfile(ctx, EMBEDDED_CONFIG, buf, sizeof(buf));\n\t\tif (buflen == -1) {\n\t\t\tlogerr(\"%s: %s\", __func__, EMBEDDED_CONFIG);\n\t\t\treturn ifo;\n\t\t}\n\t\tif (buf[buflen - 1] != '\\0') {\n\t\t\tif ((size_t)buflen < sizeof(buf) - 1)\n\t\t\t\tbuflen++;\n\t\t\tbuf[buflen - 1] = '\\0';\n\t\t}\n#else\n\t\tbuflen = (ssize_t)strlcpy(buf, dhcpcd_embedded_conf,\n\t\t    sizeof(buf));\n\t\tif ((size_t)buflen >= sizeof(buf)) {\n\t\t\tlogerrx(\"%s: embedded config too big\", __func__);\n\t\t\treturn ifo;\n\t\t}\n\t\t/* Our embedded config is NULL terminated */\n#endif\n\t\tbp = buf;\n\t\twhile ((line = get_line(&bp, &buflen)) != NULL) {\n\t\t\toption = strsep(&line, \" \\t\");\n\t\t\tif (line)\n\t\t\t\tline = strskipwhite(line);\n\t\t\t/* Trim trailing whitespace */\n\t\t\tif (line) {\n\t\t\t\tp = line + strlen(line) - 1;\n\t\t\t\twhile (p != line && (*p == ' ' || *p == '\\t') &&\n\t\t\t\t    *(p - 1) != '\\\\')\n\t\t\t\t\t*p-- = '\\0';\n\t\t\t}\n\t\t\tparse_config_line(ctx, NULL, ifo, option, line, &ldop,\n\t\t\t    &edop);\n\t\t}\n\n#ifdef INET\n\t\tctx->dhcp_opts = ifo->dhcp_override;\n\t\tctx->dhcp_opts_len = ifo->dhcp_override_len;\n#else\n\t\tfor (i = 0, opt = ifo->dhcp_override;\n\t\t    i < ifo->dhcp_override_len; i++, opt++)\n\t\t\tfree_dhcp_opt_embenc(opt);\n\t\tfree(ifo->dhcp_override);\n#endif\n\t\tifo->dhcp_override = NULL;\n\t\tifo->dhcp_override_len = 0;\n\n#ifdef INET6\n\t\tctx->nd_opts = ifo->nd_override;\n\t\tctx->nd_opts_len = ifo->nd_override_len;\n#ifdef DHCP6\n\t\tctx->dhcp6_opts = ifo->dhcp6_override;\n\t\tctx->dhcp6_opts_len = ifo->dhcp6_override_len;\n#endif\n#else\n\t\tfor (i = 0, opt = ifo->nd_override; i < ifo->nd_override_len;\n\t\t    i++, opt++)\n\t\t\tfree_dhcp_opt_embenc(opt);\n\t\tfree(ifo->nd_override);\n\t\tfor (i = 0, opt = ifo->dhcp6_override;\n\t\t    i < ifo->dhcp6_override_len; i++, opt++)\n\t\t\tfree_dhcp_opt_embenc(opt);\n\t\tfree(ifo->dhcp6_override);\n#endif\n\t\tifo->nd_override = NULL;\n\t\tifo->nd_override_len = 0;\n\t\tifo->dhcp6_override = NULL;\n\t\tifo->dhcp6_override_len = 0;\n\n\t\tctx->vivso = ifo->vivso_override;\n\t\tctx->vivso_len = ifo->vivso_override_len;\n\t\tifo->vivso_override = NULL;\n\t\tifo->vivso_override_len = 0;\n\t}\n\n\t/* Parse our options file */\n\tbuflen = dhcp_readfile(ctx, ctx->cffile, buf, sizeof(buf));\n\tif (buflen == -1) {\n\t\t/* dhcpcd can continue without it, but no DNS options\n\t\t * would be requested ... */\n\t\tlogerr(\"%s: %s\", __func__, ctx->cffile);\n\t\treturn ifo;\n\t}\n\tif (buf[buflen - 1] != '\\0') {\n\t\tif ((size_t)buflen < sizeof(buf) - 1)\n\t\t\tbuflen++;\n\t\tbuf[buflen - 1] = '\\0';\n\t}\n\tdhcp_filemtime(ctx, ctx->cffile, &ifo->mtime);\n\n\tldop = edop = NULL;\n\tskip = have_profile = new_block = 0;\n\thad_block = ifname == NULL ? 1 : 0;\n\tbp = buf;\n\twhile ((line = get_line(&bp, &buflen)) != NULL) {\n\t\toption = strsep(&line, \" \\t\");\n\t\tif (line)\n\t\t\tline = strskipwhite(line);\n\t\t/* Trim trailing whitespace */\n\t\tif (line) {\n\t\t\tp = line + strlen(line) - 1;\n\t\t\twhile (p != line && (*p == ' ' || *p == '\\t') &&\n\t\t\t    *(p - 1) != '\\\\')\n\t\t\t\t*p-- = '\\0';\n\t\t}\n\t\tif (skip == 0 && new_block) {\n\t\t\thad_block = 1;\n\t\t\tnew_block = 0;\n\t\t\tifo->options &= ~DHCPCD_WAITOPTS;\n\t\t\tSET_CONFIG_BLOCK(ifo);\n\t\t}\n\n\t\t/* Start of an interface block, skip if not ours */\n\t\tif (strcmp(option, \"interface\") == 0) {\n\t\t\tchar **n;\n\n\t\t\tnew_block = 1;\n\t\t\tif (line == NULL) {\n\t\t\t\t/* No interface given */\n\t\t\t\tskip = 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (ifname && fnmatch(line, ifname, 0) == 0)\n\t\t\t\tskip = 0;\n\t\t\telse\n\t\t\t\tskip = 1;\n\t\t\tif (ifname)\n\t\t\t\tcontinue;\n\n\t\t\tn = reallocarray(ctx->ifcv, (size_t)ctx->ifcc + 1,\n\t\t\t    sizeof(char *));\n\t\t\tif (n == NULL) {\n\t\t\t\tlogerr(__func__);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tctx->ifcv = n;\n\t\t\tctx->ifcv[ctx->ifcc] = strdup(line);\n\t\t\tif (ctx->ifcv[ctx->ifcc] == NULL) {\n\t\t\t\tlogerr(__func__);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tctx->ifcc++;\n\t\t\tcontinue;\n\t\t}\n\t\t/* Start of an ssid block, skip if not ours */\n\t\tif (strcmp(option, \"ssid\") == 0) {\n\t\t\tnew_block = 1;\n\t\t\tif (ssid && line && strcmp(line, ssid) == 0)\n\t\t\t\tskip = 0;\n\t\t\telse\n\t\t\t\tskip = 1;\n\t\t\tcontinue;\n\t\t}\n\t\t/* Start of a profile block, skip if not ours */\n\t\tif (strcmp(option, \"profile\") == 0) {\n\t\t\tnew_block = 1;\n\t\t\tif (profile && line && strcmp(line, profile) == 0) {\n\t\t\t\tskip = 0;\n\t\t\t\thave_profile = 1;\n\t\t\t} else\n\t\t\t\tskip = 1;\n\t\t\tcontinue;\n\t\t}\n\t\t/* Skip arping if we have selected a profile but not parsing\n\t\t * one. */\n\t\tif (profile && !have_profile && strcmp(option, \"arping\") == 0)\n\t\t\tcontinue;\n\t\tif (skip)\n\t\t\tcontinue;\n\n\t\tparse_config_line(ctx, ifname, ifo, option, line, &ldop, &edop);\n\t}\n\n\tif (profile && !have_profile) {\n\t\tfree_options(ctx, ifo);\n\t\terrno = ENOENT;\n\t\treturn NULL;\n\t}\n\n\tif (!had_block)\n\t\tifo->options &= ~DHCPCD_WAITOPTS;\n\tCLEAR_CONFIG_BLOCK(ifo);\n\tfinish_config(ifo);\n\treturn ifo;\n}\n\nint\nadd_options(struct dhcpcd_ctx *ctx, const char *ifname, struct if_options *ifo,\n    int argc, char **argv)\n{\n\tint oi, opt, r;\n\tunsigned long long wait_opts;\n\n\tif (argc == 0)\n\t\treturn 1;\n\n\toptind = 0;\n\tr = 1;\n\t/* Don't apply the command line wait options to each interface,\n\t * only use the dhcpcd.conf entry for that. */\n\tif (ifname != NULL)\n\t\twait_opts = ifo->options & DHCPCD_WAITOPTS;\n\twhile (\n\t    (opt = getopt_long(argc, argv,\n\t\t ctx->options & DHCPCD_PRINT_PIDFILE ? NOERR_IF_OPTS : IF_OPTS,\n\t\t cf_options, &oi)) != -1) {\n\t\tr = parse_option(ctx, ifname, ifo, opt, optarg, NULL, NULL);\n\t\tif (r != 1)\n\t\t\tbreak;\n\t}\n\tif (ifname != NULL) {\n\t\tifo->options &= ~DHCPCD_WAITOPTS;\n\t\tifo->options |= wait_opts;\n\t}\n\n\tfinish_config(ifo);\n\treturn r;\n}\n\nchar **\nalloc_args(int argc, char **argv)\n{\n\tint i;\n\tsize_t strslen = 0, len;\n\tsize_t nptrs = (size_t)argc;\n\tsize_t ptrslen = nptrs * sizeof(char *);\n\tvoid *buf;\n\tchar **ptrs, *strsp;\n\n\tfor (i = 0; i < argc; i++) {\n\t\tstrslen += strlen(argv[i]) + 1;\n\t}\n\tif (strslen == 0)\n\t\treturn NULL;\n\n\tbuf = malloc(ptrslen + strslen);\n\tif (!buf)\n\t\treturn NULL;\n\n\tptrs = buf;\n\tstrsp = (char *)&ptrs[nptrs];\n\n\tfor (i = 0; i < argc; i++) {\n\t\tlen = strlcpy(strsp, argv[i], strslen);\n\t\tif (len >= strslen) /* truncated */\n\t\t\tgoto err;\n\n\t\tptrs[i] = strsp;\n\t\tstrsp += len + 1;\n\t\tassert(strslen >= len + 1);\n\t\tstrslen -= len + 1;\n\t}\n\n\tassert(strslen == 0);\n\treturn ptrs;\n\nerr:\n\tfree(buf);\n\treturn NULL;\n}\n\nvoid\nfree_options(struct dhcpcd_ctx *ctx, struct if_options *ifo)\n{\n\tsize_t i;\n#ifdef RT_FREE_ROUTE_TABLE\n\tstruct interface *ifp;\n\tstruct rt *rt;\n#endif\n\tstruct dhcp_opt *opt;\n#ifdef AUTH\n\tstruct token *token;\n#endif\n#ifndef SMALL\n\tstruct vivco *vo;\n\tstruct vsio *vsio;\n\tstruct vsio_so *vsio_so;\n#endif\n\n\tif (ifo == NULL)\n\t\treturn;\n\n\tif (ifo->environ) {\n\t\ti = 0;\n\t\twhile (ifo->environ[i])\n\t\t\tfree(ifo->environ[i++]);\n\t\tfree(ifo->environ);\n\t}\n\tif (ifo->config) {\n\t\ti = 0;\n\t\twhile (ifo->config[i])\n\t\t\tfree(ifo->config[i++]);\n\t\tfree(ifo->config);\n\t}\n\n#ifdef RT_FREE_ROUTE_TABLE\n\t/* Stupidly, we don't know the interface when creating the options.\n\t * As such, make sure each route has one so they can goto the\n\t * free list. */\n\tifp = ctx->ifaces != NULL ? TAILQ_FIRST(ctx->ifaces) : NULL;\n\tif (ifp != NULL) {\n\t\tRB_TREE_FOREACH(rt, &ifo->routes)\n\t\t{\n\t\t\tif (rt->rt_ifp == NULL)\n\t\t\t\trt->rt_ifp = ifp;\n\t\t}\n\t}\n#endif\n\trt_headclear0(ctx, &ifo->routes, AF_UNSPEC);\n\n\tfree(ifo->arping);\n\tfree(ifo->blacklist);\n\tfree(ifo->fallback);\n\n\tfor (opt = ifo->dhcp_override; ifo->dhcp_override_len > 0;\n\t    opt++, ifo->dhcp_override_len--)\n\t\tfree_dhcp_opt_embenc(opt);\n\tfree(ifo->dhcp_override);\n\tfor (opt = ifo->nd_override; ifo->nd_override_len > 0;\n\t    opt++, ifo->nd_override_len--)\n\t\tfree_dhcp_opt_embenc(opt);\n\tfree(ifo->nd_override);\n\tfor (opt = ifo->dhcp6_override; ifo->dhcp6_override_len > 0;\n\t    opt++, ifo->dhcp6_override_len--)\n\t\tfree_dhcp_opt_embenc(opt);\n\tfree(ifo->dhcp6_override);\n#ifndef SMALL\n\tfor (vo = ifo->vivco; ifo->vivco_len > 0; vo++, ifo->vivco_len--)\n\t\tfree(vo->data);\n\tfree(ifo->vivco);\n\tfor (vsio = ifo->vsio; ifo->vsio_len > 0; vsio++, ifo->vsio_len--) {\n\t\tfor (vsio_so = vsio->so; vsio->so_len > 0;\n\t\t    vsio_so++, vsio->so_len--)\n\t\t\tfree(vsio_so->data);\n\t\tfree(vsio->so);\n\t}\n\tfree(ifo->vsio);\n\tfor (vsio = ifo->vsio6; ifo->vsio6_len > 0; vsio++, ifo->vsio6_len--) {\n\t\tfor (vsio_so = vsio->so; vsio->so_len > 0;\n\t\t    vsio_so++, vsio->so_len--)\n\t\t\tfree(vsio_so->data);\n\t\tfree(vsio->so);\n\t}\n\tfree(ifo->vsio6);\n#endif\n\tfor (opt = ifo->vivso_override; ifo->vivso_override_len > 0;\n\t    opt++, ifo->vivso_override_len--)\n\t\tfree_dhcp_opt_embenc(opt);\n\tfree(ifo->vivso_override);\n\n#if defined(INET6) && !defined(SMALL)\n\tfor (; ifo->ia_len > 0; ifo->ia_len--)\n\t\tfree(ifo->ia[ifo->ia_len - 1].sla);\n#endif\n\tfree(ifo->ia);\n\n#ifdef AUTH\n\twhile ((token = TAILQ_FIRST(&ifo->auth.tokens))) {\n\t\tTAILQ_REMOVE(&ifo->auth.tokens, token, next);\n\t\tif (token->realm_len)\n\t\t\tfree(token->realm);\n\t\tfree(token->key);\n\t\tfree(token);\n\t}\n#endif\n\tfree(ifo);\n}\n"
  },
  {
    "path": "src/if-options.h",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef IF_OPTIONS_H\n#define IF_OPTIONS_H\n\n#include <sys/param.h>\n#include <sys/socket.h>\n\n#include <net/if.h>\n#include <netinet/in.h>\n\n#include <getopt.h>\n#include <limits.h>\n#include <stdint.h>\n\n#include \"auth.h\"\n#include \"route.h\"\n\n/* Don't set any optional arguments here so we retain POSIX\n * compatibility with getopt */\n#define IF_OPTS                                        \\\n\t\"146bc:de:f:gh:i:j:kl:m:no:pqr:s:t:u:v:wxy:z:\" \\\n\t\"ABC:DEF:GHI:JKLMNO:PQ:S:TUVW:X:Z:\"\n#define NOERR_IF_OPTS\t \":\" IF_OPTS\n\n#define DEFAULT_TIMEOUT\t 30\n#define DEFAULT_REBOOT\t 5\n#define DEFAULT_REQUEST\t 180 /* secs to request, mirror DHCP6 */\n#define DEFAULT_FALLBACK 5   /* secs until fallback */\n#define DEFAULT_IPV4LL\t 5   /* secs until ipv4ll */\n\n#ifndef HOSTNAME_MAX_LEN\n#define HOSTNAME_MAX_LEN 250 /* 255 - 3 (FQDN) - 2 (DNS enc) */\n#endif\n#define DHCP_OPTION_MAX_LEN 255\n\n#define DHCPCD_ARP\t    (1ULL << 0)\n#define DHCPCD_RELEASE\t    (1ULL << 1)\n#define DHCPCD_RTBUILD\t    (1ULL << 2)\n#define DHCPCD_GATEWAY\t    (1ULL << 3)\n#define DHCPCD_STATIC\t    (1ULL << 4)\n// unused\t\t\t\t(1ULL << 5)\n#define DHCPCD_ARP_PERSISTDEFENCE (1ULL << 6)\n#define DHCPCD_LASTLEASE\t  (1ULL << 7)\n#define DHCPCD_INFORM\t\t  (1ULL << 8)\n#define DHCPCD_REQUEST\t\t  (1ULL << 9)\n#define DHCPCD_IPV4LL\t\t  (1ULL << 10)\n#define DHCPCD_DUID\t\t  (1ULL << 11)\n#define DHCPCD_PERSISTENT\t  (1ULL << 12)\n#define DHCPCD_DAEMONISE\t  (1ULL << 14)\n#define DHCPCD_DAEMONISED\t  (1ULL << 15)\n#define DHCPCD_TEST\t\t  (1ULL << 16)\n#define DHCPCD_MANAGER\t\t  (1ULL << 17)\n#define DHCPCD_HOSTNAME\t\t  (1ULL << 18)\n#define DHCPCD_CLIENTID\t\t  (1ULL << 19)\n#define DHCPCD_LINK\t\t  (1ULL << 20)\n#define DHCPCD_ANONYMOUS\t  (1ULL << 21)\n#define DHCPCD_BACKGROUND\t  (1ULL << 22)\n#define DHCPCD_VENDORRAW\t  (1ULL << 23)\n// unused\t\t\t\t(1ULL << 24)\n#define DHCPCD_WAITIP\t\t\t(1ULL << 25)\n#define DHCPCD_SLAACPRIVATE\t\t(1ULL << 26)\n#define DHCPCD_CSR_WARNED\t\t(1ULL << 27)\n#define DHCPCD_XID_HWADDR\t\t(1ULL << 28)\n#define DHCPCD_BROADCAST\t\t(1ULL << 29)\n#define DHCPCD_DUMPLEASE\t\t(1ULL << 30)\n#define DHCPCD_IPV6RS\t\t\t(1ULL << 31)\n#define DHCPCD_IPV6RA_REQRDNSS\t\t(1ULL << 32)\n#define DHCPCD_PRIVSEP\t\t\t(1ULL << 33)\n#define DHCPCD_CONFIGURE\t\t(1ULL << 34)\n#define DHCPCD_IPV4\t\t\t(1ULL << 35)\n#define DHCPCD_FORKED\t\t\t(1ULL << 36)\n#define DHCPCD_IPV6\t\t\t(1ULL << 37)\n#define DHCPCD_STARTED\t\t\t(1ULL << 38)\n#define DHCPCD_NOALIAS\t\t\t(1ULL << 39)\n#define DHCPCD_IA_FORCED\t\t(1ULL << 40)\n#define DHCPCD_STOPPING\t\t\t(1ULL << 41)\n#define DHCPCD_LAUNCHER\t\t\t(1ULL << 42)\n#define DHCPCD_HOSTNAME_SHORT\t\t(1ULL << 43)\n#define DHCPCD_EXITING\t\t\t(1ULL << 44)\n#define DHCPCD_WAITIP4\t\t\t(1ULL << 45)\n#define DHCPCD_WAITIP6\t\t\t(1ULL << 46)\n#define DHCPCD_DEV\t\t\t(1ULL << 47)\n#define DHCPCD_IAID\t\t\t(1ULL << 48)\n#define DHCPCD_DHCP\t\t\t(1ULL << 49)\n#define DHCPCD_DHCP6\t\t\t(1ULL << 50)\n#define DHCPCD_IF_UP\t\t\t(1ULL << 51)\n#define DHCPCD_INFORM6\t\t\t(1ULL << 52)\n#define DHCPCD_WANTDHCP\t\t\t(1ULL << 53)\n#define DHCPCD_IPV6RA_AUTOCONF\t\t(1ULL << 54)\n#define DHCPCD_ROUTER_HOST_ROUTE_WARNED (1ULL << 55)\n#define DHCPCD_LASTLEASE_EXTEND\t\t(1ULL << 56)\n#define DHCPCD_BOOTP\t\t\t(1ULL << 57)\n#define DHCPCD_INITIAL_DELAY\t\t(1ULL << 58)\n#define DHCPCD_PRINT_PIDFILE\t\t(1ULL << 59)\n#define DHCPCD_ONESHOT\t\t\t(1ULL << 60)\n#define DHCPCD_INACTIVE\t\t\t(1ULL << 61)\n#define DHCPCD_SLAACTEMP\t\t(1ULL << 62)\n#define DHCPCD_PRIVSEPROOT\t\t(1ULL << 63)\n\n#define DHCPCD_NODROP\t\t\t(DHCPCD_EXITING | DHCPCD_PERSISTENT)\n\n#define DHCPCD_WAITOPTS\t\t\t(DHCPCD_WAITIP | DHCPCD_WAITIP4 | DHCPCD_WAITIP6)\n\n#define DHCPCD_WARNINGS\t\t\t(DHCPCD_CSR_WARNED | DHCPCD_ROUTER_HOST_ROUTE_WARNED)\n\n/* These options only make sense in the config file, so don't use any\n   valid short options for them */\n#define O_BASE\t\t     MAX('z', 'Z') + 1\n#define O_ARPING\t     O_BASE + 1\n#define O_FALLBACK\t     O_BASE + 2\n#define O_DESTINATION\t     O_BASE + 3\n#define O_IPV6RS\t     O_BASE + 4\n#define O_NOIPV6RS\t     O_BASE + 5\n#define O_IPV6RA_FORK\t     O_BASE + 6\n#define O_LINK_RCVBUF\t     O_BASE + 7\n#define O_ANONYMOUS\t     O_BASE + 8\n#define O_NOALIAS\t     O_BASE + 9\n#define O_IA_NA\t\t     O_BASE + 10\n#define O_IA_TA\t\t     O_BASE + 11\n#define O_IA_PD\t\t     O_BASE + 12\n#define O_HOSTNAME_SHORT     O_BASE + 13\n#define O_DEV\t\t     O_BASE + 14\n#define O_NODEV\t\t     O_BASE + 15\n#define O_NOIPV4\t     O_BASE + 16\n#define O_NOIPV6\t     O_BASE + 17\n#define O_IAID\t\t     O_BASE + 18\n#define O_DEFINE\t     O_BASE + 19\n#define O_DEFINE6\t     O_BASE + 20\n#define O_EMBED\t\t     O_BASE + 21\n#define O_ENCAP\t\t     O_BASE + 22\n#define O_VENDOPT\t     O_BASE + 23\n#define O_VENDCLASS\t     O_BASE + 24\n#define O_AUTHPROTOCOL\t     O_BASE + 25\n#define O_AUTHTOKEN\t     O_BASE + 26\n#define O_AUTHNOTREQUIRED    O_BASE + 27\n#define O_NODHCP\t     O_BASE + 28\n#define O_NODHCP6\t     O_BASE + 29\n#define O_DHCP\t\t     O_BASE + 30\n#define O_DHCP6\t\t     O_BASE + 31\n#define O_IPV4\t\t     O_BASE + 32\n#define O_IPV6\t\t     O_BASE + 33\n#define O_CONTROLGRP\t     O_BASE + 34\n#define O_SLAAC\t\t     O_BASE + 35\n#define O_GATEWAY\t     O_BASE + 36\n#define O_NOUP\t\t     O_BASE + 37\n#define O_IPV6RA_AUTOCONF    O_BASE + 38\n#define O_IPV6RA_NOAUTOCONF  O_BASE + 39\n#define O_REJECT\t     O_BASE + 40\n#define O_BOOTP\t\t     O_BASE + 42\n#define O_DEFINEND\t     O_BASE + 43\n#define O_NODELAY\t     O_BASE + 44\n#define O_INFORM6\t     O_BASE + 45\n#define O_LASTLEASE_EXTEND   O_BASE + 46\n#define O_INACTIVE\t     O_BASE + 47\n#define O_MUDURL\t     O_BASE + 48\n#define O_MSUSERCLASS\t     O_BASE + 49\n#define O_CONFIGURE\t     O_BASE + 50\n#define O_NOCONFIGURE\t     O_BASE + 51\n#define O_RANDOMISE_HWADDR   O_BASE + 52\n#define O_ARP_PERSISTDEFENCE O_BASE + 53\n#define O_REQUEST_TIME\t     O_BASE + 54\n#define O_FALLBACK_TIME\t     O_BASE + 55\n#define O_IPV4LL_TIME\t     O_BASE + 56\n#define O_VSIO\t\t     O_BASE + 57\n#define O_VSIO6\t\t     O_BASE + 58\n#define O_NOSYSLOG\t     O_BASE + 59\n\nextern const struct option cf_options[];\n\nstruct if_sla {\n\tchar ifname[IF_NAMESIZE];\n\tuint32_t sla;\n\tuint8_t prefix_len;\n\tuint64_t suffix;\n\tbool sla_set;\n};\n\nstruct if_ia {\n\tuint8_t iaid[4];\n#ifdef INET6\n\tuint16_t ia_type;\n\tuint8_t iaid_set;\n\tstruct in6_addr addr;\n\tuint8_t prefix_len;\n#ifndef SMALL\n\tuint32_t sla_max;\n\tsize_t sla_len;\n\tstruct if_sla *sla;\n#endif\n#endif\n};\n\n#ifndef SMALL\nstruct vivco {\n\tuint32_t en;\n\tsize_t len;\n\tuint8_t *data;\n};\nstruct vsio_so {\n\tuint16_t opt;\n\tuint16_t len;\n\tvoid *data;\n};\nstruct vsio {\n\tuint32_t en;\n\tsize_t so_len;\n\tstruct vsio_so *so;\n};\n#endif\n\nstruct if_options {\n\ttime_t mtime;\n\tuint8_t iaid[4];\n\tint metric;\n\tuint8_t requestmask[256 / NBBY];\n\tuint8_t requiremask[256 / NBBY];\n\tuint8_t nomask[256 / NBBY];\n\tuint8_t rejectmask[256 / NBBY];\n\tuint8_t dstmask[256 / NBBY];\n\tuint8_t requestmasknd[(UINT16_MAX + 1) / NBBY];\n\tuint8_t requiremasknd[(UINT16_MAX + 1) / NBBY];\n\tuint8_t nomasknd[(UINT16_MAX + 1) / NBBY];\n\tuint8_t rejectmasknd[(UINT16_MAX + 1) / NBBY];\n\tuint8_t requestmask6[(UINT16_MAX + 1) / NBBY];\n\tuint8_t requiremask6[(UINT16_MAX + 1) / NBBY];\n\tuint8_t nomask6[(UINT16_MAX + 1) / NBBY];\n\tuint8_t rejectmask6[(UINT16_MAX + 1) / NBBY];\n\tuint32_t leasetime;\n\tuint32_t timeout;\n\tuint32_t reboot;\n\tuint32_t request_time;\n\tuint32_t fallback_time;\n\tuint32_t ipv4ll_time;\n\tunsigned long long options;\n\tbool randomise_hwaddr;\n\n\tstruct in_addr req_addr;\n\tstruct in_addr req_mask;\n\tstruct in_addr req_brd;\n\trb_tree_t routes;\n\tstruct in6_addr req_addr6;\n\tuint8_t req_prefix_len;\n\tunsigned int mtu;\n\tchar **config;\n\n\tchar **environ;\n\n\tchar hostname[HOSTNAME_MAX_LEN + 1]; /* NUL terminated */\n\tuint8_t fqdn;\n\t/* The first byte is the option length */\n\tuint8_t vendorclassid[DHCP_OPTION_MAX_LEN + 1];\n\tuint8_t clientid[DHCP_OPTION_MAX_LEN + 1];\n\tuint8_t userclass[DHCP_OPTION_MAX_LEN + 1];\n\tuint8_t vendor[DHCP_OPTION_MAX_LEN + 1];\n\tuint8_t mudurl[DHCP_OPTION_MAX_LEN + 1];\n\n\tsize_t blacklist_len;\n\tin_addr_t *blacklist;\n\tsize_t whitelist_len;\n\tin_addr_t *whitelist;\n\tssize_t arping_len;\n\tin_addr_t *arping;\n\tchar *fallback;\n\n\tstruct if_ia *ia;\n\tsize_t ia_len;\n#ifdef INET6\n\tstruct in6_addr token;\n#endif\n\n\tstruct dhcp_opt *dhcp_override;\n\tsize_t dhcp_override_len;\n\tstruct dhcp_opt *nd_override;\n\tsize_t nd_override_len;\n\tstruct dhcp_opt *dhcp6_override;\n\tsize_t dhcp6_override_len;\n\tstruct dhcp_opt *vivso_override;\n\tsize_t vivso_override_len;\n\n#ifndef SMALL\n\tsize_t vivco_len;\n\tstruct vivco *vivco;\n\tsize_t vsio_len;\n\tstruct vsio *vsio;\n\tsize_t vsio6_len;\n\tstruct vsio *vsio6;\n#endif\n\n\tstruct auth auth;\n};\n\nstruct if_options *read_config(struct dhcpcd_ctx *, const char *, const char *,\n    const char *);\nint add_options(struct dhcpcd_ctx *, const char *, struct if_options *, int,\n    char **);\nvoid free_dhcp_opt_embenc(struct dhcp_opt *);\nvoid free_options(struct dhcpcd_ctx *, struct if_options *);\n\nchar **alloc_args(int argc, char **argv);\n\n#endif\n"
  },
  {
    "path": "src/if-sun.c",
    "content": "/*\n * Solaris interface driver for dhcpcd\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2016-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n/* Stop sunos headers including the system queue ... */\n#include \"queue.h\"\n\n#include <sys/ioctl.h>\n#include <sys/mac.h>\n#include <sys/pfmod.h>\n#include <sys/tihdr.h>\n#include <sys/utsname.h>\n\n#include <net/if.h>\n#include <net/if_dl.h>\n#include <net/if_types.h>\n#include <netinet/if_ether.h>\n#include <netinet/in.h>\n#include <netinet/udp.h>\n\n#include <assert.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <ifaddrs.h>\n#include <inet/ip.h>\n#include <kstat.h>\n#include <libdlpi.h>\n#include <stddef.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stropts.h>\n#include <unistd.h>\n\n/* Private libsocket interface we can hook into to get\n * a better getifaddrs(3).\n * From libsocket_priv.h, which is not always distributed so is here. */\nextern int getallifaddrs(sa_family_t, struct ifaddrs **, int64_t);\n\n#include \"bpf.h\"\n#include \"common.h\"\n#include \"config.h\"\n#include \"dhcp.h\"\n#include \"if-options.h\"\n#include \"if.h\"\n#include \"ipv4.h\"\n#include \"ipv6.h\"\n#include \"ipv6nd.h\"\n#include \"logerr.h\"\n#include \"route.h\"\n#include \"sa.h\"\n\n#ifndef ARP_MOD_NAME\n#define ARP_MOD_NAME \"arp\"\n#endif\n\n#ifndef RT_ROUNDUP\n#define RT_ROUNDUP(a) \\\n\t((a) > 0 ? (1 + (((a) - 1) | (sizeof(int32_t) - 1))) : sizeof(int32_t))\n#define RT_ADVANCE(x, n) ((x) += RT_ROUNDUP(sa_len((n))))\n#endif\n\n#define COPYOUT(sin, sa)                                                     \\\n\tdo {                                                                 \\\n\t\tif ((sa) && ((sa)->sa_family == AF_INET))                    \\\n\t\t\t(sin) =                                              \\\n\t\t\t    ((const struct sockaddr_in *)(const void *)(sa)) \\\n\t\t\t\t->sin_addr;                                  \\\n\t} while (0)\n\n#define COPYOUT6(sin, sa)                                                     \\\n\tdo {                                                                  \\\n\t\tif ((sa) && ((sa)->sa_family == AF_INET6))                    \\\n\t\t\t(sin) =                                               \\\n\t\t\t    ((const struct sockaddr_in6 *)(const void *)(sa)) \\\n\t\t\t\t->sin6_addr;                                  \\\n\t} while (0)\n\n#define COPYSA(dst, src) memcpy((dst), (src), sa_len((src)))\n\nstruct rtm {\n\tstruct rt_msghdr hdr;\n\tchar buffer[sizeof(struct sockaddr_storage) * RTAX_MAX];\n};\n\nstatic int if_plumb(int, const struct dhcpcd_ctx *, int, const char *);\n\nint\nos_init(void)\n{\n\treturn 0;\n}\n\nint\nif_init(struct interface *ifp)\n{\n#ifdef INET\n\tif (if_plumb(RTM_NEWADDR, ifp->ctx, AF_INET, ifp->name) == -1 &&\n\t    errno != EEXIST)\n\t\treturn -1;\n#endif\n\n#ifdef INET6\n\tif (if_plumb(RTM_NEWADDR, ifp->ctx, AF_INET6, ifp->name) == -1 &&\n\t    errno != EEXIST)\n\t\treturn -1;\n#endif\n\n\tif (ifp->index == 0)\n\t\tifp->index = if_nametoindex(ifp->name);\n\n\treturn 0;\n}\n\nint\nif_conf(__unused struct interface *ifp)\n{\n\treturn 0;\n}\n\nint\nif_opensockets_os(struct dhcpcd_ctx *ctx)\n{\n\tstruct priv *priv;\n\tint n;\n\n\tif ((priv = malloc(sizeof(*priv))) == NULL)\n\t\treturn -1;\n\tctx->priv = priv;\n\n#ifdef INET6\n\tpriv->pf_inet6_fd = xsocket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);\n\t/* Don't return an error so we at least work on kernels witout INET6\n\t * even though we expect INET6 support.\n\t * We will fail noisily elsewhere anyway. */\n#endif\n\n\tctx->link_fd = xsocket(PF_ROUTE,\n\t    SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);\n\n\tif (ctx->link_fd == -1) {\n\t\tfree(ctx->priv);\n\t\treturn -1;\n\t}\n\n\t/* Ignore our own route(4) messages.\n\t * Sadly there is no way of doing this for route(4) messages\n\t * generated from addresses we add/delete. */\n\tn = 0;\n\tif (setsockopt(ctx->link_fd, SOL_SOCKET, SO_USELOOPBACK, &n,\n\t\tsizeof(n)) == -1)\n\t\tlogerr(\"%s: SO_USELOOPBACK\", __func__);\n\n\treturn 0;\n}\n\nvoid\nif_closesockets_os(struct dhcpcd_ctx *ctx)\n{\n#ifdef INET6\n\tstruct priv *priv;\n\n\tpriv = (struct priv *)ctx->priv;\n\tif (priv && priv->pf_inet6_fd != -1)\n\t\tclose(priv->pf_inet6_fd);\n#endif\n\n\t/* each interface should have closed itself */\n\tfree(ctx->priv);\n\tctx->priv = NULL;\n}\n\nint\nif_setmac(struct interface *ifp, void *mac, uint8_t maclen)\n{\n\terrno = ENOTSUP;\n\treturn -1;\n}\n\nint\nif_carrier(struct interface *ifp, __unused const void *ifadata)\n{\n\tkstat_ctl_t *kcp;\n\tkstat_t *ksp;\n\tkstat_named_t *knp;\n\tlink_state_t linkstate;\n\n\tkcp = kstat_open();\n\tif (kcp == NULL)\n\t\tgoto err;\n\tksp = kstat_lookup(kcp, UNCONST(\"link\"), 0, ifp->name);\n\tif (ksp == NULL)\n\t\tgoto err;\n\tif (kstat_read(kcp, ksp, NULL) == -1)\n\t\tgoto err;\n\tknp = kstat_data_lookup(ksp, UNCONST(\"link_state\"));\n\tif (knp == NULL)\n\t\tgoto err;\n\tif (knp->data_type != KSTAT_DATA_UINT32)\n\t\tgoto err;\n\tlinkstate = (link_state_t)knp->value.ui32;\n\tkstat_close(kcp);\n\n\tswitch (linkstate) {\n\tcase LINK_STATE_UP:\n\t\tifp->flags |= IFF_UP; /* XXX Why? */\n\t\treturn LINK_UP;\n\tcase LINK_STATE_DOWN:\n\t\treturn LINK_DOWN;\n\tdefault:\n\t\treturn LINK_UNKNOWN;\n\t}\n\nerr:\n\tif (kcp != NULL)\n\t\tkstat_close(kcp);\n\treturn LINK_UNKNOWN;\n}\n\nbool\nif_roaming(__unused struct interface *ifp)\n{\n\treturn false;\n}\n\nint\nif_mtu_os(const struct interface *ifp)\n{\n\tdlpi_handle_t dh;\n\tdlpi_info_t dlinfo;\n\tint mtu;\n\n\tif (dlpi_open(ifp->name, &dh, 0) != DLPI_SUCCESS)\n\t\treturn -1;\n\tif (dlpi_info(dh, &dlinfo, 0) == DLPI_SUCCESS)\n\t\tmtu = dlinfo.di_max_sdu;\n\telse\n\t\tmtu = -1;\n\tdlpi_close(dh);\n\treturn mtu;\n}\n\nint\nif_getssid(__unused struct interface *ifp)\n{\n\terrno = ENOTSUP;\n\treturn -1;\n}\n\n/* XXX work out TAP interfaces? */\nbool\nif_ignore(__unused struct dhcpcd_ctx *ctx, __unused const char *ifname)\n{\n\treturn false;\n}\n\nunsigned short\nif_vlanid(__unused const struct interface *ifp)\n{\n\treturn 0;\n}\n\nint\nif_vimaster(__unused struct dhcpcd_ctx *ctx, __unused const char *ifname)\n{\n\treturn 0;\n}\n\nint\nif_machinearch(__unused char *str, __unused size_t len)\n{\n\t/* There is no extra data really.\n\t * isainfo -v does return amd64, but also i386. */\n\treturn 0;\n}\n\nstruct linkwalk {\n\tstruct ifaddrs *lw_ifa;\n\tint lw_error;\n};\n\nstatic boolean_t\nif_newaddr(const char *ifname, void *arg)\n{\n\tstruct linkwalk *lw = arg;\n\tint error;\n\tstruct ifaddrs *ifa;\n\tdlpi_handle_t dh;\n\tdlpi_info_t dlinfo;\n\tuint8_t pa[DLPI_PHYSADDR_MAX];\n\tsize_t pa_len;\n\tstruct sockaddr_dl *sdl;\n\n\tifa = NULL;\n\terror = dlpi_open(ifname, &dh, 0);\n\tif (error == DLPI_ENOLINK) /* Just vanished or in global zone */\n\t\treturn B_FALSE;\n\tif (error != DLPI_SUCCESS)\n\t\tgoto failed1;\n\tif (dlpi_info(dh, &dlinfo, 0) != DLPI_SUCCESS)\n\t\tgoto failed;\n\n\t/* For some reason, dlpi_info won't return the\n\t * physical address, it's all zero's.\n\t * So cal dlpi_get_physaddr. */\n\tpa_len = DLPI_PHYSADDR_MAX;\n\tif (dlpi_get_physaddr(dh, DL_CURR_PHYS_ADDR, pa, &pa_len) !=\n\t    DLPI_SUCCESS)\n\t\tgoto failed;\n\n\tif ((ifa = calloc(1, sizeof(*ifa))) == NULL)\n\t\tgoto failed;\n\tif ((ifa->ifa_name = strdup(ifname)) == NULL)\n\t\tgoto failed;\n\tif ((sdl = calloc(1, sizeof(*sdl))) == NULL)\n\t\tgoto failed;\n\n\tifa->ifa_addr = (struct sockaddr *)sdl;\n\tsdl->sdl_index = if_nametoindex(ifname);\n\tsdl->sdl_family = AF_LINK;\n\tswitch (dlinfo.di_mactype) {\n\tcase DL_ETHER:\n\t\tsdl->sdl_type = IFT_ETHER;\n\t\tbreak;\n\tcase DL_IB:\n\t\tsdl->sdl_type = IFT_IB;\n\t\tbreak;\n\tdefault:\n\t\tsdl->sdl_type = IFT_OTHER;\n\t\tbreak;\n\t}\n\n\tsdl->sdl_alen = pa_len;\n\tmemcpy(sdl->sdl_data, pa, pa_len);\n\n\tifa->ifa_next = lw->lw_ifa;\n\tlw->lw_ifa = ifa;\n\tdlpi_close(dh);\n\treturn B_FALSE;\n\nfailed:\n\tdlpi_close(dh);\n\tif (ifa != NULL) {\n\t\tfree(ifa->ifa_name);\n\t\tfree(ifa->ifa_addr);\n\t\tfree(ifa);\n\t}\nfailed1:\n\tlw->lw_error = errno;\n\treturn B_TRUE;\n}\n\n/* Creates an empty sockaddr_dl for lo0. */\nstatic struct ifaddrs *\nif_ifa_lo0(void)\n{\n\tstruct ifaddrs *ifa;\n\tstruct sockaddr_dl *sdl;\n\n\tif ((ifa = calloc(1, sizeof(*ifa))) == NULL)\n\t\treturn NULL;\n\tif ((sdl = calloc(1, sizeof(*sdl))) == NULL) {\n\t\tfree(ifa);\n\t\treturn NULL;\n\t}\n\tif ((ifa->ifa_name = strdup(\"lo0\")) == NULL) {\n\t\tfree(ifa);\n\t\tfree(sdl);\n\t\treturn NULL;\n\t}\n\n\tifa->ifa_addr = (struct sockaddr *)sdl;\n\tifa->ifa_flags = IFF_LOOPBACK;\n\tsdl->sdl_family = AF_LINK;\n\tsdl->sdl_index = if_nametoindex(\"lo0\");\n\n\treturn ifa;\n}\n\n/* getifaddrs(3) does not support AF_LINK, strips aliases and won't\n * report addresses that are not UP.\n * As such it's just totally useless, so we need to roll our own. */\nint\nif_getifaddrs(struct ifaddrs **ifap)\n{\n\tstruct linkwalk lw;\n\tstruct ifaddrs *ifa;\n\n\t/* Private libc function which we should not have to call\n\t * to get non UP addresses. */\n\tif (getallifaddrs(AF_UNSPEC, &lw.lw_ifa, 0) == -1)\n\t\treturn -1;\n\n\t/* Start with some AF_LINK addresses. */\n\tlw.lw_error = 0;\n\tdlpi_walk(if_newaddr, &lw, 0);\n\tif (lw.lw_error != 0) {\n\t\tfreeifaddrs(lw.lw_ifa);\n\t\terrno = lw.lw_error;\n\t\treturn -1;\n\t}\n\n\t/* lo0 doesn't appear in dlpi_walk, so fudge it. */\n\tif ((ifa = if_ifa_lo0()) == NULL) {\n\t\tfreeifaddrs(lw.lw_ifa);\n\t\treturn -1;\n\t}\n\tifa->ifa_next = lw.lw_ifa;\n\n\t*ifap = ifa;\n\treturn 0;\n}\n\nstatic void\nif_linkaddr(struct sockaddr_dl *sdl, const struct interface *ifp)\n{\n\tmemset(sdl, 0, sizeof(*sdl));\n\tsdl->sdl_family = AF_LINK;\n\tsdl->sdl_nlen = sdl->sdl_alen = sdl->sdl_slen = 0;\n\tsdl->sdl_index = (unsigned short)ifp->index;\n}\n\nstatic int\nget_addrs(int type, const void *data, size_t data_len,\n    const struct sockaddr **sa)\n{\n\tconst char *cp, *ep;\n\tint i;\n\n\tcp = data;\n\tep = cp + data_len;\n\tfor (i = 0; i < RTAX_MAX; i++) {\n\t\tif (type & (1 << i)) {\n\t\t\tif (cp >= ep) {\n\t\t\t\terrno = EINVAL;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tsa[i] = (const struct sockaddr *)cp;\n\t\t\tRT_ADVANCE(cp, sa[i]);\n\t\t} else\n\t\t\tsa[i] = NULL;\n\t}\n\n\treturn 0;\n}\n\nstatic struct interface *\nif_findsdl(struct dhcpcd_ctx *ctx, const struct sockaddr_dl *sdl)\n{\n\tif (sdl->sdl_index)\n\t\treturn if_findindex(ctx->ifaces, sdl->sdl_index);\n\n\tif (sdl->sdl_nlen) {\n\t\tchar ifname[IF_NAMESIZE];\n\n\t\tmemcpy(ifname, sdl->sdl_data, sdl->sdl_nlen);\n\t\tifname[sdl->sdl_nlen] = '\\0';\n\t\treturn if_find(ctx->ifaces, ifname);\n\t}\n\tif (sdl->sdl_alen) {\n\t\tstruct interface *ifp;\n\n\t\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\t\tif (ifp->hwlen == sdl->sdl_alen &&\n\t\t\t    memcmp(ifp->hwaddr, sdl->sdl_data, sdl->sdl_alen) ==\n\t\t\t\t0)\n\t\t\t\treturn ifp;\n\t\t}\n\t}\n\n\terrno = ENOENT;\n\treturn NULL;\n}\n\nstatic struct interface *\nif_findsa(struct dhcpcd_ctx *ctx, const struct sockaddr *sa)\n{\n\tif (sa == NULL) {\n\t\terrno = EINVAL;\n\t\treturn NULL;\n\t}\n\n\tswitch (sa->sa_family) {\n\tcase AF_LINK: {\n\t\tconst struct sockaddr_dl *sdl;\n\n\t\tsdl = (const void *)sa;\n\t\treturn if_findsdl(ctx, sdl);\n\t}\n#ifdef INET\n\tcase AF_INET: {\n\t\tconst struct sockaddr_in *sin;\n\t\tstruct ipv4_addr *ia;\n\n\t\tsin = (const void *)sa;\n\t\tif ((ia = ipv4_findmaskaddr(ctx, &sin->sin_addr)))\n\t\t\treturn ia->iface;\n\t\tif ((ia = ipv4_findmaskbrd(ctx, &sin->sin_addr)))\n\t\t\treturn ia->iface;\n\t\tbreak;\n\t}\n#endif\n#ifdef INET6\n\tcase AF_INET6: {\n\t\tconst struct sockaddr_in6 *sin;\n\t\tstruct ipv6_addr *ia;\n\n\t\tsin = (const void *)sa;\n\t\tif ((ia = ipv6_findmaskaddr(ctx, &sin->sin6_addr)))\n\t\t\treturn ia->iface;\n\t\tif ((ia = ipv6_finddstaddr(ctx, &sin->sin6_addr)))\n\t\t\treturn ia->iface;\n\t\tbreak;\n\t}\n#endif\n\tdefault:\n\t\terrno = EAFNOSUPPORT;\n\t\treturn NULL;\n\t}\n\n\terrno = ENOENT;\n\treturn NULL;\n}\n\nstatic void\nif_route0(struct dhcpcd_ctx *ctx, struct rtm *rtmsg, unsigned char cmd,\n    const struct rt *rt)\n{\n\tstruct rt_msghdr *rtm;\n\tchar *bp = rtmsg->buffer;\n\tsocklen_t sl;\n\tbool gateway_unspec;\n\n\t/* WARNING: Solaris will not allow you to delete RTF_KERNEL routes.\n\t * This includes subnet/prefix routes. */\n\n#define ADDSA(sa)                     \\\n\tdo {                          \\\n\t\tsl = sa_len((sa));    \\\n\t\tmemcpy(bp, (sa), sl); \\\n\t\tbp += RT_ROUNDUP(sl); \\\n\t} while (/* CONSTCOND */ 0)\n\n\tmemset(rtmsg, 0, sizeof(*rtmsg));\n\trtm = &rtmsg->hdr;\n\trtm->rtm_version = RTM_VERSION;\n\trtm->rtm_type = cmd;\n\trtm->rtm_seq = ++ctx->seq;\n\trtm->rtm_flags = rt->rt_flags;\n\trtm->rtm_addrs = RTA_DST | RTA_GATEWAY;\n\n\tgateway_unspec = sa_is_unspecified(&rt->rt_gateway);\n\n\tif (cmd == RTM_ADD || cmd == RTM_CHANGE) {\n\t\tbool netmask_bcast = sa_is_allones(&rt->rt_netmask);\n\n\t\trtm->rtm_flags |= RTF_UP;\n\t\tif (!(rtm->rtm_flags & RTF_REJECT) &&\n\t\t    !sa_is_loopback(&rt->rt_gateway)) {\n\t\t\trtm->rtm_addrs |= RTA_IFP;\n\t\t\t/* RTA_IFA is currently ignored by the kernel.\n\t\t\t * RTA_SRC and RTF_SETSRC look like what we want,\n\t\t\t * but they don't work with RTF_GATEWAY.\n\t\t\t * We set RTA_IFA just in the hope that the\n\t\t\t * kernel will one day support this. */\n\t\t\tif (!sa_is_unspecified(&rt->rt_ifa))\n\t\t\t\trtm->rtm_addrs |= RTA_IFA;\n\t\t}\n\n\t\tif (netmask_bcast)\n\t\t\trtm->rtm_flags |= RTF_HOST;\n\t\telse if (!gateway_unspec)\n\t\t\trtm->rtm_flags |= RTF_GATEWAY;\n\n\t\t/* Make static so that in.routed does not delete it */\n\t\trtm->rtm_flags |= RTF_STATIC;\n\n\t\tif (rt->rt_mtu != 0) {\n\t\t\trtm->rtm_inits |= RTV_MTU;\n\t\t\trtm->rtm_rmx.rmx_mtu = rt->rt_mtu;\n\t\t}\n\t}\n\n\tif (!(rtm->rtm_flags & RTF_HOST))\n\t\trtm->rtm_addrs |= RTA_NETMASK;\n\n\tADDSA(&rt->rt_dest);\n\n\tif (gateway_unspec)\n\t\tADDSA(&rt->rt_ifa);\n\telse\n\t\tADDSA(&rt->rt_gateway);\n\n\tif (rtm->rtm_addrs & RTA_NETMASK)\n\t\tADDSA(&rt->rt_netmask);\n\n\tif (rtm->rtm_addrs & RTA_IFP) {\n\t\tstruct sockaddr_dl sdl;\n\n\t\tif_linkaddr(&sdl, rt->rt_ifp);\n\t\tADDSA((struct sockaddr *)&sdl);\n\t}\n\n\tif (rtm->rtm_addrs & RTA_IFA)\n\t\tADDSA(&rt->rt_ifa);\n\n#if 0\n\tif (rtm->rtm_addrs & RTA_SRC)\n\t\tADDSA(&rt->rt_ifa);\n#endif\n\n\trtm->rtm_msglen = (unsigned short)(bp - (char *)rtm);\n}\n\nint\nif_route(unsigned char cmd, const struct rt *rt)\n{\n\tstruct rtm rtm;\n\tstruct dhcpcd_ctx *ctx = rt->rt_ifp->ctx;\n\n\tif_route0(ctx, &rtm, cmd, rt);\n\n\tif (write(ctx->link_fd, &rtm, rtm.hdr.rtm_msglen) == -1)\n\t\treturn -1;\n\treturn 0;\n}\n\nstatic int\nif_copyrt(struct dhcpcd_ctx *ctx, struct rt *rt, const struct rt_msghdr *rtm)\n{\n\tconst struct sockaddr *rti_info[RTAX_MAX];\n\n\tif (~rtm->rtm_addrs & RTA_DST) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tif (get_addrs(rtm->rtm_addrs, (const char *)rtm + sizeof(*rtm),\n\t\trtm->rtm_msglen - sizeof(*rtm), rti_info) == -1)\n\t\treturn -1;\n\n\tmemset(rt, 0, sizeof(*rt));\n\n\trt->rt_flags = (unsigned int)rtm->rtm_flags;\n\tCOPYSA(&rt->rt_dest, rti_info[RTAX_DST]);\n\tif (rtm->rtm_addrs & RTA_NETMASK)\n\t\tCOPYSA(&rt->rt_netmask, rti_info[RTAX_NETMASK]);\n\n\t/* dhcpcd likes an unspecified gateway to indicate via the link.\n\t * However we need to know if gateway was a link with an address. */\n\tif (rtm->rtm_addrs & RTA_GATEWAY) {\n\t\tif (rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) {\n\t\t\tconst struct sockaddr_dl *sdl;\n\n\t\t\tsdl = (const struct sockaddr_dl *)(const void *)\n\t\t\t    rti_info[RTAX_GATEWAY];\n\t\t\tif (sdl->sdl_alen != 0)\n\t\t\t\trt->rt_dflags |= RTDF_GATELINK;\n\t\t} else if (rtm->rtm_flags & RTF_GATEWAY)\n\t\t\tCOPYSA(&rt->rt_gateway, rti_info[RTAX_GATEWAY]);\n\t}\n\n\tif (rtm->rtm_addrs & RTA_SRC)\n\t\tCOPYSA(&rt->rt_ifa, rti_info[RTAX_SRC]);\n\trt->rt_mtu = (unsigned int)rtm->rtm_rmx.rmx_mtu;\n\n\tif (rtm->rtm_index)\n\t\trt->rt_ifp = if_findindex(ctx->ifaces, rtm->rtm_index);\n\telse if (rtm->rtm_addrs & RTA_IFP)\n\t\trt->rt_ifp = if_findsa(ctx, rti_info[RTAX_IFP]);\n\telse if (rtm->rtm_addrs & RTA_GATEWAY)\n\t\trt->rt_ifp = if_findsa(ctx, rti_info[RTAX_GATEWAY]);\n\telse\n\t\trt->rt_ifp = if_findsa(ctx, rti_info[RTAX_DST]);\n\n\tif (rt->rt_ifp == NULL && rtm->rtm_type == RTM_MISS)\n\t\trt->rt_ifp = if_loopback(ctx);\n\n\tif (rt->rt_ifp == NULL) {\n\t\terrno = ESRCH;\n\t\treturn -1;\n\t}\n\n\treturn 0;\n}\n\nstatic struct rt *\nif_route_get(struct dhcpcd_ctx *ctx, struct rt *rt)\n{\n\tstruct rtm rtm;\n\tint s;\n\tstruct iovec iov = { .iov_base = &rtm, .iov_len = sizeof(rtm) };\n\tstruct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 };\n\tssize_t len;\n\tstruct rt *rtw = rt;\n\n\tif_route0(ctx, &rtm, RTM_GET, rt);\n\trt = NULL;\n\ts = xsocket(PF_ROUTE, SOCK_RAW | SOCK_CLOEXEC, 0);\n\tif (s == -1)\n\t\treturn NULL;\n\tif (write(s, &rtm, rtm.hdr.rtm_msglen) == -1)\n\t\tgoto out;\n\tif ((len = recvmsg(s, &msg, 0)) == -1)\n\t\tgoto out;\n\tif ((size_t)len < sizeof(rtm.hdr) || len < rtm.hdr.rtm_msglen) {\n\t\terrno = EINVAL;\n\t\tgoto out;\n\t}\n\tif (if_copyrt(ctx, rtw, &rtm.hdr) == -1)\n\t\tgoto out;\n\trt = rtw;\n\nout:\n\tclose(s);\n\treturn rt;\n}\n\nstatic int\nif_finishrt(struct dhcpcd_ctx *ctx, struct rt *rt)\n{\n\tint mtu;\n\n\t/* Solaris has a subnet route with the gateway\n\t * of the owning address.\n\t * dhcpcd has a blank gateway here to indicate a\n\t * subnet route. */\n\tif (!sa_is_unspecified(&rt->rt_dest) &&\n\t    !sa_is_unspecified(&rt->rt_gateway)) {\n\t\tswitch (rt->rt_gateway.sa_family) {\n#ifdef INET\n\t\tcase AF_INET: {\n\t\t\tstruct in_addr *in;\n\n\t\t\tin = &satosin(&rt->rt_gateway)->sin_addr;\n\t\t\tif (ipv4_findaddr(ctx, in))\n\t\t\t\tin->s_addr = INADDR_ANY;\n\t\t\tbreak;\n\t\t}\n#endif\n#ifdef INET6\n\t\tcase AF_INET6: {\n\t\t\tstruct in6_addr *in6;\n\n\t\t\tin6 = &satosin6(&rt->rt_gateway)->sin6_addr;\n\t\t\tif (ipv6_findaddr(ctx, in6, 0))\n\t\t\t\t*in6 = in6addr_any;\n\t\t\tbreak;\n\t\t}\n#endif\n\t\t}\n\t}\n\n\t/* Solaris doesn't set interfaces for some routes.\n\t * This sucks, so we need to call RTM_GET to\n\t * work out the interface. */\n\tif (rt->rt_ifp == NULL) {\n\t\tif (if_route_get(ctx, rt) == NULL) {\n\t\t\trt->rt_ifp = if_loopback(ctx);\n\t\t\tif (rt->rt_ifp == NULL)\n\t\t\t\treturn -1;\n\t\t}\n\t}\n\n\t/* Solaris likes to set route MTU to match\n\t * interface MTU when adding routes.\n\t * This confuses dhcpcd as it expects MTU to be 0\n\t * when no explicit MTU has been set. */\n\tmtu = if_getmtu(rt->rt_ifp);\n\tif (mtu == -1)\n\t\treturn -1;\n\tif (rt->rt_mtu == (unsigned int)mtu)\n\t\trt->rt_mtu = 0;\n\n\treturn 0;\n}\n\nstatic int\nif_addrflags0(int fd, int af, const char *ifname)\n{\n\tstruct lifreq lifr;\n\tint flags;\n\n\tmemset(&lifr, 0, sizeof(lifr));\n\tstrlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));\n\tif (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)\n\t\treturn -1;\n\n\tflags = 0;\n\tif (lifr.lifr_flags & IFF_DUPLICATE)\n\t\tflags |= af == AF_INET6 ? IN6_IFF_DUPLICATED :\n\t\t\t\t\t  IN_IFF_DUPLICATED;\n\telse if (!(lifr.lifr_flags & IFF_UP))\n\t\tflags |= af == AF_INET6 ? IN6_IFF_TENTATIVE : IN_IFF_TENTATIVE;\n\treturn flags;\n}\n\nstatic int\nif_rtm(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)\n{\n\tconst struct sockaddr *sa;\n\tstruct rt rt;\n\n\tif (rtm->rtm_msglen < sizeof(*rtm) + sizeof(*sa)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tif (if_copyrt(ctx, &rt, rtm) == -1 && errno != ESRCH)\n\t\treturn -1;\n\n#ifdef INET6\n\t/*\n\t * BSD announces host routes.\n\t * As such, we should be notified of reachability by its\n\t * existance with a hardware address.\n\t * Ensure we don't call this for a newly incomplete state.\n\t */\n\tif (rt.rt_dest.sa_family == AF_INET6 &&\n\t    (rt.rt_flags & RTF_HOST || rtm->rtm_type == RTM_MISS) &&\n\t    !(rtm->rtm_type == RTM_ADD && !(rt.rt_dflags & RTDF_GATELINK))) {\n\t\tbool reachable;\n\n\t\treachable = (rtm->rtm_type == RTM_ADD ||\n\t\t\t\trtm->rtm_type == RTM_CHANGE) &&\n\t\t    rt.rt_dflags & RTDF_GATELINK;\n\t\tipv6nd_neighbour(ctx, &rt.rt_ss_dest.sin6.sin6_addr, reachable);\n\t}\n#endif\n\n\tif (if_finishrt(ctx, &rt) == -1)\n\t\treturn -1;\n\trt_recvrt(rtm->rtm_type, &rt, rtm->rtm_pid);\n\treturn 0;\n}\n\nstatic bool\nif_getalias(struct interface *ifp, const struct sockaddr *sa, char *alias)\n{\n\tstruct ifaddrs *ifaddrs, *ifa;\n\tstruct interface *ifpx;\n\tbool found;\n\n\tifaddrs = NULL;\n\tif (getallifaddrs(sa->sa_family, &ifaddrs, 0) == -1)\n\t\treturn false;\n\tfound = false;\n\tfor (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {\n\t\tif (ifa->ifa_addr == NULL)\n\t\t\tcontinue;\n\t\tif (sa_cmp(sa, ifa->ifa_addr) != 0)\n\t\t\tcontinue;\n\t\t/* Check it's for the right interace. */\n\t\tifpx = if_find(ifp->ctx->ifaces, ifa->ifa_name);\n\t\tif (ifp == ifpx) {\n\t\t\tstrlcpy(alias, ifa->ifa_name, IF_NAMESIZE);\n\t\t\tfound = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\tfreeifaddrs(ifaddrs);\n\treturn found;\n}\n\nstatic int\nif_getbrdaddr(struct dhcpcd_ctx *ctx, const char *ifname, struct in_addr *brd)\n{\n\tstruct lifreq lifr = { 0 };\n\tint r;\n\n\tmemset(&lifr, 0, sizeof(lifr));\n\tstrlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));\n\terrno = 0;\n\tr = ioctl(ctx->pf_inet_fd, SIOCGLIFBRDADDR, &lifr, sizeof(lifr));\n\tif (r != -1)\n\t\tCOPYOUT(*brd, (struct sockaddr *)&lifr.lifr_broadaddr);\n\treturn r;\n}\n\nstatic int\nif_ifa(struct dhcpcd_ctx *ctx, const struct ifa_msghdr *ifam)\n{\n\tstruct interface *ifp;\n\tconst struct sockaddr *sa, *rti_info[RTAX_MAX];\n\tint flags;\n\tchar ifalias[IF_NAMESIZE];\n\n\tif (ifam->ifam_msglen < sizeof(*ifam)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\tif (~ifam->ifam_addrs & RTA_IFA)\n\t\treturn 0;\n\n\tif (get_addrs(ifam->ifam_addrs, (const char *)ifam + sizeof(*ifam),\n\t\tifam->ifam_msglen - sizeof(*ifam), rti_info) == -1)\n\t\treturn -1;\n\tsa = rti_info[RTAX_IFA];\n\n\t/* XXX We have no way of knowing who generated these\n\t * messages wich truely sucks because we want to\n\t * avoid listening to our own delete messages. */\n\tif ((ifp = if_findindex(ctx->ifaces, ifam->ifam_index)) == NULL)\n\t\treturn 0;\n\n\t/*\n\t * ifa_msghdr does not supply the alias, just the interface index.\n\t * This is very bad, because it means we have to call getifaddrs\n\t * and trawl the list of addresses to find the added address.\n\t * To make life worse, you can have the same address on the same\n\t * interface with different aliases.\n\t * So this hack is not entirely accurate.\n\t *\n\t * IF anyone is going to fix Solaris, plesse consider adding the\n\t * following fields to extend ifa_msghdr:\n\t *   ifam_alias\n\t *   ifam_pid\n\t */\n\tif (ifam->ifam_type != RTM_DELADDR && !if_getalias(ifp, sa, ifalias))\n\t\treturn 0;\n\n\tswitch (sa->sa_family) {\n\tcase AF_LINK: {\n\t\tstruct sockaddr_dl sdl;\n\n\t\tif (ifam->ifam_type != RTM_CHGADDR &&\n\t\t    ifam->ifam_type != RTM_NEWADDR)\n\t\t\tbreak;\n\t\tmemcpy(&sdl, rti_info[RTAX_IFA], sizeof(sdl));\n\t\tdhcpcd_handlehwaddr(ifp, ifp->hwtype, CLLADDR(&sdl),\n\t\t    sdl.sdl_alen);\n\t\tbreak;\n\t}\n#ifdef INET\n\tcase AF_INET: {\n\t\tstruct in_addr addr, mask, bcast;\n\n\t\tCOPYOUT(addr, rti_info[RTAX_IFA]);\n\t\tCOPYOUT(mask, rti_info[RTAX_NETMASK]);\n\t\tCOPYOUT(bcast, rti_info[RTAX_BRD]);\n\n\t\tif (ifam->ifam_type == RTM_DELADDR) {\n\t\t\tstruct ipv4_addr *ia;\n\n\t\t\tia = ipv4_iffindaddr(ifp, &addr, &mask);\n\t\t\tif (ia == NULL)\n\t\t\t\treturn 0;\n\t\t\tstrlcpy(ifalias, ia->alias, sizeof(ifalias));\n\t\t} else if (bcast.s_addr == INADDR_ANY) {\n\t\t\t/* Work around a bug where broadcast\n\t\t\t * address is not correctly reported. */\n\t\t\tif (if_getbrdaddr(ctx, ifalias, &bcast) == -1)\n\t\t\t\treturn 0;\n\t\t}\n\t\tflags = if_addrflags(ifp, NULL, ifalias);\n\t\tif (ifam->ifam_type == RTM_DELADDR) {\n\t\t\tif (flags != -1)\n\t\t\t\treturn 0;\n\t\t} else if (flags == -1)\n\t\t\treturn 0;\n\n\t\tipv4_handleifa(ctx,\n\t\t    ifam->ifam_type == RTM_CHGADDR ? RTM_NEWADDR :\n\t\t\t\t\t\t     ifam->ifam_type,\n\t\t    NULL, ifalias, &addr, &mask, &bcast, flags, 0);\n\t\tbreak;\n\t}\n#endif\n#ifdef INET6\n\tcase AF_INET6: {\n\t\tstruct in6_addr addr6, mask6;\n\t\tconst struct in6_addr *dstaddr6;\n\t\tconst struct sockaddr_in6 *sin6;\n\n\t\tsin6 = (const void *)rti_info[RTAX_IFA];\n\t\taddr6 = sin6->sin6_addr;\n\t\tsin6 = (const void *)rti_info[RTAX_NETMASK];\n\t\tmask6 = sin6->sin6_addr;\n\t\tsin6 = (const void *)rti_info[RTAX_BRD];\n\t\tdstaddr6 = sin6 ? &sin6->sin6_addr : NULL;\n\n\t\tif (ifam->ifam_type == RTM_DELADDR) {\n\t\t\tstruct ipv6_addr *ia;\n\n\t\t\tia = ipv6_iffindaddr(ifp, &addr6, 0);\n\t\t\tif (ia == NULL)\n\t\t\t\treturn 0;\n\t\t\tstrlcpy(ifalias, ia->alias, sizeof(ifalias));\n\t\t}\n\t\tflags = if_addrflags6(ifp, NULL, ifalias);\n\t\tif (ifam->ifam_type == RTM_DELADDR) {\n\t\t\tif (flags != -1)\n\t\t\t\treturn 0;\n\t\t} else if (flags == -1)\n\t\t\treturn 0;\n\n\t\tipv6_handleifa(ctx,\n\t\t    ifam->ifam_type == RTM_CHGADDR ? RTM_NEWADDR :\n\t\t\t\t\t\t     ifam->ifam_type,\n\t\t    NULL, ifalias, &addr6, ipv6_prefixlen(&mask6), dstaddr6,\n\t\t    flags, 0);\n\t\tbreak;\n\t}\n#endif\n\t}\n\n\treturn 0;\n}\n\nstatic int\nif_ifinfo(struct dhcpcd_ctx *ctx, const struct if_msghdr *ifm)\n{\n\tstruct interface *ifp;\n\tint state;\n\tunsigned int flags;\n\n\tif (ifm->ifm_msglen < sizeof(*ifm)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tif ((ifp = if_findindex(ctx->ifaces, ifm->ifm_index)) == NULL)\n\t\treturn 0;\n\tflags = (unsigned int)ifm->ifm_flags;\n\tif (ifm->ifm_flags & IFF_OFFLINE)\n\t\tstate = LINK_DOWN;\n\telse {\n\t\tstate = LINK_UP;\n\t\tflags |= IFF_UP;\n\t}\n\tifp->mtu = if_getmtu(ifp);\n\tdhcpcd_handlecarrier(ifp, state, flags);\n\treturn 0;\n}\n\nstatic int\nif_dispatch(struct dhcpcd_ctx *ctx, const struct rt_msghdr *rtm)\n{\n\tif (rtm->rtm_version != RTM_VERSION)\n\t\treturn 0;\n\n\tswitch (rtm->rtm_type) {\n\tcase RTM_IFINFO:\n\t\treturn if_ifinfo(ctx, (const void *)rtm);\n\tcase RTM_ADD:\t /* FALLTHROUGH */\n\tcase RTM_CHANGE: /* FALLTHROUGH */\n\tcase RTM_DELETE: /* FALLTHROUGH */\n\tcase RTM_MISS:\n\t\treturn if_rtm(ctx, (const void *)rtm);\n\tcase RTM_CHGADDR: /* FALLTHROUGH */\n\tcase RTM_DELADDR: /* FALLTHROUGH */\n\tcase RTM_NEWADDR:\n\t\treturn if_ifa(ctx, (const void *)rtm);\n\t}\n\n\treturn 0;\n}\n\nint\nif_handlelink(struct dhcpcd_ctx *ctx)\n{\n\tstruct rtm rtm;\n\tssize_t len;\n\n\tlen = read(ctx->link_fd, &rtm, sizeof(rtm));\n\tif (len == -1)\n\t\treturn -1;\n\tif (len == 0)\n\t\treturn 0;\n\tif ((size_t)len < sizeof(rtm.hdr.rtm_msglen) ||\n\t    len != rtm.hdr.rtm_msglen) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\t/*\n\t * Coverity thinks that the data could be tainted from here.\n\t * I have no idea how because the length of the data we read\n\t * is guarded by len and checked to match rtm_msglen.\n\t * The issue seems to be related to extracting the addresses\n\t * at the end of the header, but seems to have no issues with the\n\t * equivalent call in if_initrt.\n\t */\n\t/* coverity[tainted_data] */\n\treturn if_dispatch(ctx, &rtm.hdr);\n}\n\nstatic void\nif_octetstr(char *buf, const Octet_t *o, ssize_t len)\n{\n\tint i;\n\tchar *p;\n\n\tp = buf;\n\tfor (i = 0; i < o->o_length; i++) {\n\t\tif ((p + 1) - buf < len)\n\t\t\t*p++ = o->o_bytes[i];\n\t\telse\n\t\t\tbreak;\n\t}\n\t*p = '\\0';\n}\n\nstatic int\nif_setflags(int fd, const char *ifname, uint64_t flags)\n{\n\tstruct lifreq lifr = { .lifr_addrlen = 0 };\n\n\tstrlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));\n\tif (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)\n\t\treturn -1;\n\tif ((lifr.lifr_flags & flags) != flags) {\n\t\tlifr.lifr_flags |= flags;\n\t\tif (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1)\n\t\t\treturn -1;\n\t}\n\treturn 0;\n}\n\nstatic int\nif_addaddr(int fd, const char *ifname, struct sockaddr_storage *addr,\n    struct sockaddr_storage *mask, struct sockaddr_storage *brd, uint8_t plen)\n{\n\tstruct lifreq lifr = { .lifr_addrlen = plen };\n\n\tstrlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));\n\n\t/* First assign the netmask. */\n\tlifr.lifr_addr = *mask;\n\tif (addr == NULL) {\n\t\tlifr.lifr_addrlen = plen;\n\t\tif (ioctl(fd, SIOCSLIFSUBNET, &lifr) == -1)\n\t\t\treturn -1;\n\t\tgoto up;\n\t} else {\n\t\tif (ioctl(fd, SIOCSLIFNETMASK, &lifr) == -1)\n\t\t\treturn -1;\n\t}\n\n\t/* Then assign the address. */\n\tlifr.lifr_addr = *addr;\n\tif (ioctl(fd, SIOCSLIFADDR, &lifr) == -1)\n\t\treturn -1;\n\n\t/* Then assign the broadcast address. */\n\tif (brd != NULL) {\n\t\tlifr.lifr_broadaddr = *brd;\n\t\tif (ioctl(fd, SIOCSLIFBRDADDR, &lifr) == -1)\n\t\t\treturn -1;\n\t}\n\nup:\n\treturn if_setflags(fd, ifname, IFF_UP);\n}\n\nstatic int\nif_getaf_fd(const struct dhcpcd_ctx *ctx, int af)\n{\n\tif (af == AF_INET)\n\t\treturn ctx->pf_inet_fd;\n\tif (af == AF_INET6) {\n\t\tstruct priv *priv;\n\n\t\tpriv = (struct priv *)ctx->priv;\n\t\treturn priv->pf_inet6_fd;\n\t}\n\n\terrno = EAFNOSUPPORT;\n\treturn -1;\n}\n\nint\nif_getsubnet(struct dhcpcd_ctx *ctx, const char *ifname, int af, void *subnet,\n    size_t subnet_len)\n{\n\tstruct lifreq lifr = { .lifr_addrlen = 0 };\n\tint fd;\n\n\tfd = if_getaf_fd(ctx, af);\n\tstrlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));\n\tif (ioctl(fd, SIOCGLIFSUBNET, &lifr) == -1)\n\t\treturn -1;\n\tmemcpy(subnet, &lifr.lifr_addr,\n\t    MIN(subnet_len, sizeof(lifr.lifr_addr)));\n\treturn 0;\n}\n\nstatic int\nif_plumblif(int cmd, const struct dhcpcd_ctx *ctx, int af, const char *ifname)\n{\n\tstruct lifreq lifr;\n\tint s;\n\n\tmemset(&lifr, 0, sizeof(lifr));\n\tstrlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));\n\tlifr.lifr_addr.ss_family = af;\n\ts = if_getaf_fd(ctx, af);\n\treturn ioctl(s, cmd == RTM_NEWADDR ? SIOCLIFADDIF : SIOCLIFREMOVEIF,\n\t\t   &lifr) == -1 &&\n\t\terrno != EEXIST ?\n\t    -1 :\n\t    0;\n}\n\nstatic int\nif_plumbif(const struct dhcpcd_ctx *ctx, int af, const char *ifname)\n{\n\tdlpi_handle_t dh, dh_arp = NULL;\n\tint fd, af_fd, mux_fd, arp_fd = -1, mux_id, retval;\n\tuint64_t flags;\n\tstruct lifreq lifr;\n\tconst char *udp_dev;\n\tstruct strioctl ioc;\n\tstruct if_spec spec;\n\n\tif (if_nametospec(ifname, &spec) == -1)\n\t\treturn -1;\n\n\taf_fd = if_getaf_fd(ctx, af);\n\n\tswitch (af) {\n\tcase AF_INET:\n\t\tflags = IFF_IPV4;\n\t\tudp_dev = UDP_DEV_NAME;\n\t\tbreak;\n\tcase AF_INET6:\n\t\t/* We will take care of setting the link local address. */\n\t\tflags = IFF_IPV6 | IFF_NOLINKLOCAL;\n\t\tudp_dev = UDP6_DEV_NAME;\n\t\tbreak;\n\tdefault:\n\t\terrno = EPROTONOSUPPORT;\n\t\treturn -1;\n\t}\n\n\tif (dlpi_open(ifname, &dh, DLPI_NOATTACH) != DLPI_SUCCESS) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tfd = dlpi_fd(dh);\n\tretval = -1;\n\tmux_fd = -1;\n\tif (ioctl(fd, I_PUSH, IP_MOD_NAME) == -1)\n\t\tgoto out;\n\tmemset(&lifr, 0, sizeof(lifr));\n\tstrlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));\n\tlifr.lifr_ppa = spec.ppa;\n\tlifr.lifr_flags = flags;\n\tif (ioctl(fd, SIOCSLIFNAME, &lifr) == -1)\n\t\tgoto out;\n\n\t/* Get full flags. */\n\tif (ioctl(af_fd, SIOCGLIFFLAGS, &lifr) == -1)\n\t\tgoto out;\n\tflags = lifr.lifr_flags;\n\n\t/* Open UDP as a multiplexor to PLINK the interface stream.\n\t * UDP is used because STREAMS will not let you PLINK a driver\n\t * under itself and IP is generally  at the bottom of the stream. */\n\tif ((mux_fd = open(udp_dev, O_RDWR)) == -1)\n\t\tgoto out;\n\t/* POP off all undesired modules. */\n\twhile (ioctl(mux_fd, I_POP, 0) != -1)\n\t\t;\n\tif (errno != EINVAL)\n\t\tgoto out;\n\tif (ioctl(mux_fd, I_PUSH, ARP_MOD_NAME) == -1)\n\t\tgoto out;\n\n\tif (flags & (IFF_NOARP | IFF_IPV6)) {\n\t\t/* PLINK the interface stream so it persists. */\n\t\tif (ioctl(mux_fd, I_PLINK, fd) == -1)\n\t\t\tgoto out;\n\t\tgoto done;\n\t}\n\n\tif (dlpi_open(ifname, &dh_arp, DLPI_NOATTACH) != DLPI_SUCCESS)\n\t\tgoto out;\n\tarp_fd = dlpi_fd(dh_arp);\n\tif (ioctl(arp_fd, I_PUSH, ARP_MOD_NAME) == -1)\n\t\tgoto out;\n\n\tmemset(&lifr, 0, sizeof(lifr));\n\tstrlcpy(lifr.lifr_name, ifname, sizeof(lifr.lifr_name));\n\tlifr.lifr_ppa = spec.ppa;\n\tlifr.lifr_flags = flags;\n\tmemset(&ioc, 0, sizeof(ioc));\n\tioc.ic_cmd = SIOCSLIFNAME;\n\tioc.ic_dp = (char *)&lifr;\n\tioc.ic_len = sizeof(lifr);\n\tif (ioctl(arp_fd, I_STR, &ioc) == -1)\n\t\tgoto out;\n\n\t/* PLINK the interface stream so it persists. */\n\tmux_id = ioctl(mux_fd, I_PLINK, fd);\n\tif (mux_id == -1)\n\t\tgoto out;\n\tif (ioctl(mux_fd, I_PLINK, arp_fd) == -1) {\n\t\tioctl(mux_fd, I_PUNLINK, mux_id);\n\t\tgoto out;\n\t}\n\ndone:\n\tretval = 0;\n\nout:\n\tdlpi_close(dh);\n\tif (dh_arp != NULL)\n\t\tdlpi_close(dh_arp);\n\tif (mux_fd != -1)\n\t\tclose(mux_fd);\n\treturn retval;\n}\n\nstatic int\nif_unplumbif(const struct dhcpcd_ctx *ctx, int af, const char *ifname)\n{\n\tstruct sockaddr_storage addr = { .ss_family = af };\n\tint fd;\n\n\t/* For the time being, don't unplumb the interface, just\n\t * set the address to zero. */\n\tfd = if_getaf_fd(ctx, af);\n\treturn if_addaddr(fd, ifname, &addr, &addr,\n\t    af == AF_INET ? &addr : NULL, 0);\n}\n\nstatic int\nif_plumb(int cmd, const struct dhcpcd_ctx *ctx, int af, const char *ifname)\n{\n\tstruct if_spec spec;\n\n\tif (if_nametospec(ifname, &spec) == -1)\n\t\treturn -1;\n\tif (spec.lun != -1)\n\t\treturn if_plumblif(cmd, ctx, af, ifname);\n\tif (cmd == RTM_NEWADDR)\n\t\treturn if_plumbif(ctx, af, ifname);\n\telse\n\t\treturn if_unplumbif(ctx, af, ifname);\n}\n\n#ifdef INET\nstatic int\nif_walkrt(struct dhcpcd_ctx *ctx, rb_tree_t *routes, char *data, size_t len)\n{\n\tmib2_ipRouteEntry_t *re, *e;\n\tstruct rt rt, *rtn;\n\tchar ifname[IF_NAMESIZE];\n\tstruct in_addr in;\n\n\tif (len % sizeof(*re) != 0) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tre = (mib2_ipRouteEntry_t *)data;\n\te = (mib2_ipRouteEntry_t *)(data + len);\n\tdo {\n\t\t/* Skip route types we don't want. */\n\t\tswitch (re->ipRouteInfo.re_ire_type) {\n\t\tcase IRE_IF_CLONE:\n\t\tcase IRE_BROADCAST:\n\t\tcase IRE_MULTICAST:\n\t\tcase IRE_NOROUTE:\n\t\tcase IRE_LOCAL:\n\t\t\tcontinue;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\n\t\tmemset(&rt, 0, sizeof(rt));\n\t\tin.s_addr = re->ipRouteDest;\n\t\tsa_in_init(&rt.rt_dest, &in);\n\t\tin.s_addr = re->ipRouteMask;\n\t\tsa_in_init(&rt.rt_netmask, &in);\n\t\tin.s_addr = re->ipRouteNextHop;\n\t\tsa_in_init(&rt.rt_gateway, &in);\n\t\trt.rt_flags = re->ipRouteInfo.re_flags;\n\t\tin.s_addr = re->ipRouteInfo.re_src_addr;\n\t\tsa_in_init(&rt.rt_ifa, &in);\n\t\trt.rt_mtu = re->ipRouteInfo.re_max_frag;\n\t\tif_octetstr(ifname, &re->ipRouteIfIndex, sizeof(ifname));\n\t\trt.rt_ifp = if_find(ctx->ifaces, ifname);\n\t\tif (if_finishrt(ctx, &rt) == -1) {\n\t\t\tlogerr(__func__);\n\t\t\tcontinue;\n\t\t}\n\t\tif ((rtn = rt_new(rt.rt_ifp)) == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\tbreak;\n\t\t}\n\t\tmemcpy(rtn, &rt, sizeof(*rtn));\n\t\tif (rb_tree_insert_node(routes, rtn) != rtn)\n\t\t\trt_free(rtn);\n\t} while (++re < e);\n\treturn 0;\n}\n#endif\n\n#ifdef INET6\nstatic int\nif_walkrt6(struct dhcpcd_ctx *ctx, rb_tree_t *routes, char *data, size_t len)\n{\n\tmib2_ipv6RouteEntry_t *re, *e;\n\tstruct rt rt, *rtn;\n\tchar ifname[IF_NAMESIZE];\n\tstruct in6_addr in6;\n\n\tif (len % sizeof(*re) != 0) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tre = (mib2_ipv6RouteEntry_t *)data;\n\te = (mib2_ipv6RouteEntry_t *)(data + len);\n\n\tdo {\n\t\t/* Skip route types we don't want. */\n\t\tswitch (re->ipv6RouteInfo.re_ire_type) {\n\t\tcase IRE_IF_CLONE:\n\t\tcase IRE_BROADCAST:\n\t\tcase IRE_MULTICAST:\n\t\tcase IRE_NOROUTE:\n\t\tcase IRE_LOCAL:\n\t\t\tcontinue;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\n\t\tmemset(&rt, 0, sizeof(rt));\n\t\tsa_in6_init(&rt.rt_dest, &re->ipv6RouteDest);\n\t\tipv6_mask(&in6, re->ipv6RoutePfxLength);\n\t\tsa_in6_init(&rt.rt_netmask, &in6);\n\t\tsa_in6_init(&rt.rt_gateway, &re->ipv6RouteNextHop);\n\t\tsa_in6_init(&rt.rt_ifa, &re->ipv6RouteInfo.re_src_addr);\n\t\trt.rt_mtu = re->ipv6RouteInfo.re_max_frag;\n\t\tif_octetstr(ifname, &re->ipv6RouteIfIndex, sizeof(ifname));\n\t\trt.rt_ifp = if_find(ctx->ifaces, ifname);\n\t\tif (if_finishrt(ctx, &rt) == -1) {\n\t\t\tlogerr(__func__);\n\t\t\tcontinue;\n\t\t}\n\t\tif ((rtn = rt_new(rt.rt_ifp)) == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\tbreak;\n\t\t}\n\t\tmemcpy(rtn, &rt, sizeof(*rtn));\n\t\tif (rb_tree_insert_node(routes, rtn) != rtn)\n\t\t\trt_free(rtn);\n\t} while (++re < e);\n\treturn 0;\n}\n#endif\n\nstatic int\nif_parsert(struct dhcpcd_ctx *ctx, rb_tree_t *routes, unsigned int level,\n    unsigned int name,\n    int (*walkrt)(struct dhcpcd_ctx *, rb_tree_t *, char *, size_t))\n{\n\tint s, retval, code, flags;\n\tuintptr_t buf[512 / sizeof(uintptr_t)];\n\tstruct strbuf ctlbuf, databuf;\n\tstruct T_optmgmt_req *tor = (struct T_optmgmt_req *)buf;\n\tstruct T_optmgmt_ack *toa = (struct T_optmgmt_ack *)buf;\n\tstruct T_error_ack *tea = (struct T_error_ack *)buf;\n\tstruct opthdr *req;\n\n\tif ((s = open(\"/dev/arp\", O_RDWR)) == -1)\n\t\treturn -1;\n\n\t/* Assume we are erroring. */\n\tretval = -1;\n\n\ttor->PRIM_type = T_SVR4_OPTMGMT_REQ;\n\ttor->OPT_offset = sizeof(struct T_optmgmt_req);\n\ttor->OPT_length = sizeof(struct opthdr);\n\ttor->MGMT_flags = T_CURRENT;\n\n\treq = (struct opthdr *)&tor[1];\n\treq->level = EXPER_IP_AND_ALL_IRES;\n\treq->name = 0;\n\treq->len = 1;\n\n\tctlbuf.buf = (char *)buf;\n\tctlbuf.len = tor->OPT_length + tor->OPT_offset;\n\tif (putmsg(s, &ctlbuf, NULL, 0) == 1)\n\t\tgoto out;\n\n\treq = (struct opthdr *)&toa[1];\n\tctlbuf.maxlen = sizeof(buf);\n\n\t/* Create a reasonable buffer to start with */\n\tdatabuf.maxlen = BUFSIZ * 2;\n\tif ((databuf.buf = malloc(databuf.maxlen)) == NULL)\n\t\tgoto out;\n\n\tfor (;;) {\n\t\tflags = 0;\n\t\tif ((code = getmsg(s, &ctlbuf, 0, &flags)) == -1)\n\t\t\tbreak;\n\t\tif (code == 0 && toa->PRIM_type == T_OPTMGMT_ACK &&\n\t\t    toa->MGMT_flags == T_SUCCESS &&\n\t\t    (size_t)ctlbuf.len >= sizeof(struct T_optmgmt_ack)) {\n\t\t\t/* End of messages, so return success! */\n\t\t\tretval = 0;\n\t\t\tbreak;\n\t\t}\n\t\tif (tea->PRIM_type == T_ERROR_ACK) {\n\t\t\terrno = tea->TLI_error == TSYSERR ? tea->UNIX_error :\n\t\t\t\t\t\t\t    EPROTO;\n\t\t\tbreak;\n\t\t}\n\t\tif (code != MOREDATA || toa->PRIM_type != T_OPTMGMT_ACK ||\n\t\t    toa->MGMT_flags != T_SUCCESS) {\n\t\t\terrno = ENOMSG;\n\t\t\tbreak;\n\t\t}\n\n\t\t/* Try to ensure out buffer is big enough\n\t\t * for future messages as well. */\n\t\tif ((size_t)databuf.maxlen < req->len) {\n\t\t\tsize_t newlen;\n\n\t\t\tfree(databuf.buf);\n\t\t\tnewlen = roundup(req->len, BUFSIZ);\n\t\t\tif ((databuf.buf = malloc(newlen)) == NULL)\n\t\t\t\tbreak;\n\t\t\tdatabuf.maxlen = newlen;\n\t\t}\n\n\t\tflags = 0;\n\t\tif (getmsg(s, NULL, &databuf, &flags) == -1)\n\t\t\tbreak;\n\n\t\t/* We always have to get the data before moving onto\n\t\t * the next item, so don't move this test higher up\n\t\t * to avoid the buffer allocation and getmsg calls. */\n\t\tif (req->level == level && req->name == name) {\n\t\t\tif (walkrt(ctx, routes, databuf.buf, req->len) == -1)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tfree(databuf.buf);\nout:\n\tclose(s);\n\treturn retval;\n}\n\nint\nif_initrt(struct dhcpcd_ctx *ctx, rb_tree_t *routes, int af)\n{\n#ifdef INET\n\tif ((af == AF_UNSPEC || af == AF_INET) &&\n\t    if_parsert(ctx, routes, MIB2_IP, MIB2_IP_ROUTE, if_walkrt) == -1)\n\t\treturn -1;\n#endif\n#ifdef INET6\n\tif ((af == AF_UNSPEC || af == AF_INET6) &&\n\t    if_parsert(ctx, routes, MIB2_IP6, MIB2_IP6_ROUTE, if_walkrt6) == -1)\n\t\treturn -1;\n#endif\n\treturn 0;\n}\n\n#ifdef INET\nint\nif_address(unsigned char cmd, const struct ipv4_addr *ia)\n{\n\tunion {\n\t\tstruct sockaddr sa;\n\t\tstruct sockaddr_storage ss;\n\t} addr, mask, brd;\n\tint fd = ia->iface->ctx->pf_inet_fd;\n\n\t/* Either remove the alias or ensure it exists. */\n\tif (if_plumb(cmd, ia->iface->ctx, AF_INET, ia->alias) == -1 &&\n\t    errno != EEXIST)\n\t\treturn -1;\n\n\tif (cmd == RTM_DELADDR)\n\t\treturn 0;\n\n\tif (cmd != RTM_NEWADDR) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\t/* We need to update the index now */\n\tia->iface->index = if_nametoindex(ia->alias);\n\n\tsa_in_init(&addr.sa, &ia->addr);\n\tsa_in_init(&mask.sa, &ia->mask);\n\tsa_in_init(&brd.sa, &ia->brd);\n\treturn if_addaddr(fd, ia->alias, &addr.ss, &mask.ss, &brd.ss, 0);\n}\n\nint\nif_addrflags(const struct interface *ifp, __unused const struct in_addr *ia,\n    const char *alias)\n{\n\treturn if_addrflags0(ifp->ctx->pf_inet_fd, AF_INET, alias);\n}\n\n#endif\n\n#ifdef INET6\nint\nif_address6(unsigned char cmd, const struct ipv6_addr *ia)\n{\n\tunion {\n\t\tstruct sockaddr sa;\n\t\tstruct sockaddr_in6 sin6;\n\t\tstruct sockaddr_storage ss;\n\t} addr, mask;\n\tint fd, r;\n\n\t/* Either remove the alias or ensure it exists. */\n\tif (if_plumb(cmd, ia->iface->ctx, AF_INET6, ia->alias) == -1 &&\n\t    errno != EEXIST)\n\t\treturn -1;\n\n\tif (cmd == RTM_DELADDR)\n\t\treturn 0;\n\n\tif (cmd != RTM_NEWADDR) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tfd = if_getaf_fd(ia->iface->ctx, AF_INET6);\n\n\tif (!(ia->flags & IPV6_AF_AUTOCONF) && ia->flags & IPV6_AF_RAPFX) {\n\t\tif (if_setflags(fd, ia->alias, IFF_NOLOCAL) == -1)\n\t\t\treturn -1;\n\t\tsa_in6_init(&mask.sa, &ia->prefix);\n\t\tr = if_addaddr(fd, ia->alias, NULL, &mask.ss, NULL,\n\t\t    ia->prefix_len);\n\t} else {\n\t\tsa_in6_init(&addr.sa, &ia->addr);\n\t\tmask.sin6.sin6_family = AF_INET6;\n\t\tipv6_mask(&mask.sin6.sin6_addr, ia->prefix_len);\n\t\tr = if_addaddr(fd, ia->alias, &addr.ss, &mask.ss, NULL,\n\t\t    ia->prefix_len);\n\t}\n\tif (r == -1 && errno == EEXIST)\n\t\treturn 0;\n\treturn r;\n}\n\nint\nif_addrflags6(const struct interface *ifp, __unused const struct in6_addr *ia,\n    const char *alias)\n{\n\tint fd;\n\n\tfd = if_getaf_fd(ifp->ctx, AF_INET6);\n\treturn if_addrflags0(fd, AF_INET6, alias);\n}\n\nint\nif_getlifetime6(struct ipv6_addr *addr)\n{\n\tUNUSED(addr);\n\terrno = ENOTSUP;\n\treturn -1;\n}\n\nint\nif_applyra(const struct ra *rap)\n{\n\tstruct lifreq lifr = {\n\t\t.lifr_ifinfo.lir_maxhops = rap->hoplimit,\n\t\t.lifr_ifinfo.lir_reachtime = rap->reachable,\n\t\t.lifr_ifinfo.lir_reachretrans = rap->retrans,\n\t};\n\n\tstrlcpy(lifr.lifr_name, rap->iface->name, sizeof(lifr.lifr_name));\n\treturn ioctl(rap->iface->ctx->pf_inet_fd, SIOCSLIFLNKINFO, &lifr);\n}\n\nvoid\nif_setup_inet6(__unused const struct interface *ifp)\n{\n}\n#endif\n"
  },
  {
    "path": "src/if.c",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/types.h>\n#include <sys/param.h>\n#include <sys/ioctl.h>\n#include <sys/socket.h>\n\n#include <net/if.h>\n#include <net/if_arp.h>\n#include <netinet/in.h>\n\n#include <fcntl.h> /* Needs to be here for old Linux */\n\n#include \"config.h\"\n#ifdef AF_LINK\n#include <net/if_dl.h>\n#include <net/if_types.h>\n#include <netinet/in_var.h>\n#undef AF_PACKET /* Newer Illumos defines this */\n#endif\n#ifdef AF_PACKET\n#include <netpacket/packet.h>\n#endif\n#ifdef SIOCGIFMEDIA\n#include <net/if_media.h>\n#endif\n#include <net/route.h>\n\n#include <ctype.h>\n#include <errno.h>\n#include <fnmatch.h>\n#include <ifaddrs.h>\n#include <inttypes.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <syslog.h>\n#include <unistd.h>\n\n#define ELOOP_QUEUE ELOOP_IF\n#include \"common.h\"\n#include \"dev.h\"\n#include \"dhcp.h\"\n#include \"dhcp6.h\"\n#include \"eloop.h\"\n#include \"if-options.h\"\n#include \"if.h\"\n#include \"ipv4.h\"\n#include \"ipv4ll.h\"\n#include \"ipv6nd.h\"\n#include \"logerr.h\"\n#include \"privsep.h\"\n\nvoid\nif_free(struct interface *ifp)\n{\n\tif (ifp == NULL)\n\t\treturn;\n#ifdef IPV4LL\n\tipv4ll_free(ifp);\n#endif\n#ifdef INET\n\tdhcp_free(ifp);\n\tipv4_free(ifp);\n#endif\n#ifdef DHCP6\n\tdhcp6_free(ifp);\n#endif\n#ifdef INET6\n\tipv6nd_free(ifp);\n\tipv6_free(ifp);\n#endif\n\trt_freeif(ifp);\n\tfree_options(ifp->ctx, ifp->options);\n\tfree(ifp->argv);\n\tfree(ifp);\n}\n\nint\nif_opensockets(struct dhcpcd_ctx *ctx)\n{\n\tif (if_opensockets_os(ctx) == -1)\n\t\treturn -1;\n\n#ifdef IFLR_ACTIVE\n\tctx->pf_link_fd = xsocket(PF_LINK, SOCK_DGRAM | SOCK_CLOEXEC, 0);\n\tif (ctx->pf_link_fd == -1)\n\t\treturn -1;\n#ifdef HAVE_CAPSICUM\n\tif (ps_rights_limit_ioctl(ctx->pf_link_fd) == -1)\n\t\treturn -1;\n#endif\n#endif\n\n\t/* We use this socket for some operations without INET. */\n\tctx->pf_inet_fd = xsocket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);\n\tif (ctx->pf_inet_fd == -1)\n\t\treturn -1;\n\n\treturn 0;\n}\n\nvoid\nif_closesockets(struct dhcpcd_ctx *ctx)\n{\n\tif (ctx->link_fd != -1) {\n\t\teloop_event_delete(ctx->eloop, ctx->link_fd);\n\t\tclose(ctx->link_fd);\n\t\tctx->link_fd = -1;\n\t}\n\n\tif (ctx->pf_inet_fd != -1) {\n\t\tclose(ctx->pf_inet_fd);\n\t\tctx->pf_inet_fd = -1;\n\t}\n\n\tif_closesockets_os(ctx);\n}\n\nint\nif_ioctl(struct dhcpcd_ctx *ctx, ioctl_request_t req, void *data, size_t len)\n{\n#ifdef PRIVSEP\n\tif (ctx->options & DHCPCD_PRIVSEP)\n\t\treturn (int)ps_root_ioctl(ctx, req, data, len);\n#endif\n\treturn ioctl(ctx->pf_inet_fd, req, data, len);\n}\n\nint\nif_setflag(struct interface *ifp, short setflag, short unsetflag)\n{\n\tstruct ifreq ifr = { .ifr_flags = 0 };\n\tshort oflags;\n\n\tstrlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));\n\tif (ioctl(ifp->ctx->pf_inet_fd, SIOCGIFFLAGS, &ifr) == -1)\n\t\treturn -1;\n\n\toflags = ifr.ifr_flags;\n\tifr.ifr_flags |= setflag;\n\tifr.ifr_flags &= (short)~unsetflag;\n\tif (ifr.ifr_flags != oflags &&\n\t    if_ioctl(ifp->ctx, SIOCSIFFLAGS, &ifr, sizeof(ifr)) == -1)\n\t\treturn -1;\n\n\t/*\n\t * Do NOT set ifp->flags here.\n\t * We need to listen for flag updates from the kernel as they\n\t * need to sync with carrier.\n\t */\n\treturn 0;\n}\n\nbool\nif_is_link_up(const struct interface *ifp)\n{\n\treturn ifp->flags & IFF_UP &&\n\t    (ifp->carrier != LINK_DOWN ||\n\t\t(ifp->options != NULL &&\n\t\t    !(ifp->options->options & DHCPCD_LINK)));\n}\n\nint\nif_randomisemac(struct interface *ifp)\n{\n\tuint32_t randnum;\n\tsize_t hwlen = ifp->hwlen, rlen = 0;\n\tuint8_t buf[HWADDR_LEN], *bp = buf, *rp = (uint8_t *)&randnum;\n\tchar sbuf[HWADDR_LEN * 3];\n\tint retval;\n\n\tif (hwlen == 0) {\n\t\terrno = ENOTSUP;\n\t\treturn -1;\n\t}\n\tif (hwlen > sizeof(buf)) {\n\t\terrno = ENOBUFS;\n\t\treturn -1;\n\t}\n\n\tfor (; hwlen != 0; hwlen--) {\n\t\tif (rlen == 0) {\n\t\t\trandnum = arc4random();\n\t\t\trp = (uint8_t *)&randnum;\n\t\t\trlen = sizeof(randnum);\n\t\t}\n\t\t*bp++ = *rp++;\n\t\trlen--;\n\t}\n\n\t/* Unicast address and locally administered. */\n\tbuf[0] &= 0xFC;\n\tbuf[0] |= 0x02;\n\n\tlogdebugx(\"%s: hardware address randomised to %s\", ifp->name,\n\t    hwaddr_ntoa(buf, ifp->hwlen, sbuf, sizeof(sbuf)));\n\tretval = if_setmac(ifp, buf, ifp->hwlen);\n\tif (retval == 0)\n\t\tmemcpy(ifp->hwaddr, buf, ifp->hwlen);\n\treturn retval;\n}\n\nstatic int\nif_hasconf(struct dhcpcd_ctx *ctx, const char *ifname)\n{\n\tint i;\n\n\tfor (i = 0; i < ctx->ifcc; i++) {\n\t\tif (fnmatch(ctx->ifcv[i], ifname, 0) == 0)\n\t\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nvoid\nif_markaddrsstale(struct if_head *ifs)\n{\n\tstruct interface *ifp;\n\n\tTAILQ_FOREACH(ifp, ifs, next) {\n#ifdef INET\n\t\tipv4_markaddrsstale(ifp);\n#endif\n#ifdef INET6\n\t\tipv6_markaddrsstale(ifp, 0);\n#endif\n\t}\n}\n\nvoid\nif_learnaddrs(struct dhcpcd_ctx *ctx, struct if_head *ifs,\n    struct ifaddrs **ifaddrs)\n{\n\tstruct ifaddrs *ifa;\n\tstruct interface *ifp;\n#ifdef INET\n\tconst struct sockaddr_in *addr, *net, *brd;\n#endif\n#ifdef INET6\n\tstruct sockaddr_in6 *addr6, *net6, *dstaddr6;\n#endif\n\tint addrflags;\n\n\tfor (ifa = *ifaddrs; ifa; ifa = ifa->ifa_next) {\n\t\tif (ifa->ifa_addr == NULL)\n\t\t\tcontinue;\n\t\tif ((ifp = if_find(ifs, ifa->ifa_name)) == NULL)\n\t\t\tcontinue;\n#ifdef HAVE_IFADDRS_ADDRFLAGS\n\t\taddrflags = (int)ifa->ifa_addrflags;\n#endif\n\t\tswitch (ifa->ifa_addr->sa_family) {\n#ifdef INET\n\t\tcase AF_INET:\n\t\t\taddr = (void *)ifa->ifa_addr;\n\t\t\tnet = (void *)ifa->ifa_netmask;\n\t\t\tif (ifa->ifa_flags & IFF_POINTOPOINT)\n\t\t\t\tbrd = (void *)ifa->ifa_dstaddr;\n\t\t\telse\n\t\t\t\tbrd = (void *)ifa->ifa_broadaddr;\n#ifndef HAVE_IFADDRS_ADDRFLAGS\n\t\t\taddrflags = if_addrflags(ifp, &addr->sin_addr,\n\t\t\t    ifa->ifa_name);\n\t\t\tif (addrflags == -1) {\n\t\t\t\tif (errno != EEXIST && errno != EADDRNOTAVAIL) {\n\t\t\t\t\tchar dbuf[INET_ADDRSTRLEN];\n\t\t\t\t\tconst char *dbp;\n\n\t\t\t\t\tdbp = inet_ntop(AF_INET,\n\t\t\t\t\t    &addr->sin_addr, dbuf,\n\t\t\t\t\t    sizeof(dbuf));\n\t\t\t\t\tlogerr(\"%s: if_addrflags: %s%%%s\",\n\t\t\t\t\t    __func__, dbp, ifp->name);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n#endif\n\t\t\tipv4_handleifa(ctx, RTM_NEWADDR, ifs, ifa->ifa_name,\n\t\t\t    &addr->sin_addr, &net->sin_addr,\n\t\t\t    brd ? &brd->sin_addr : NULL, addrflags, 0);\n\t\t\tbreak;\n#endif\n#ifdef INET6\n\t\tcase AF_INET6:\n\t\t\taddr6 = (void *)ifa->ifa_addr;\n\t\t\tdstaddr6 = (void *)ifa->ifa_dstaddr;\n\t\t\tnet6 = (void *)ifa->ifa_netmask;\n\n#ifdef __KAME__\n\t\t\tif (IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr))\n\t\t\t\t/* Remove the scope from the address */\n\t\t\t\taddr6->sin6_addr.s6_addr[2] =\n\t\t\t\t    addr6->sin6_addr.s6_addr[3] = '\\0';\n#endif\n#ifndef HAVE_IFADDRS_ADDRFLAGS\n\t\t\taddrflags = if_addrflags6(ifp, &addr6->sin6_addr,\n\t\t\t    ifa->ifa_name);\n\t\t\tif (addrflags == -1) {\n\t\t\t\tif (errno != EEXIST && errno != EADDRNOTAVAIL) {\n\t\t\t\t\tchar dbuf[INET6_ADDRSTRLEN];\n\t\t\t\t\tconst char *dbp;\n\n\t\t\t\t\tdbp = inet_ntop(AF_INET6,\n\t\t\t\t\t    &addr6->sin6_addr, dbuf,\n\t\t\t\t\t    sizeof(dbuf));\n\t\t\t\t\tlogerr(\"%s: if_addrflags6: %s%%%s\",\n\t\t\t\t\t    __func__, dbp, ifp->name);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n#endif\n\t\t\tipv6_handleifa(ctx, RTM_NEWADDR, ifs, ifa->ifa_name,\n\t\t\t    &addr6->sin6_addr, ipv6_prefixlen(&net6->sin6_addr),\n\t\t\t    dstaddr6 ? &dstaddr6->sin6_addr : NULL, addrflags,\n\t\t\t    0);\n\t\t\tbreak;\n#endif\n\t\t}\n\t}\n}\n\nvoid\nif_freeifaddrs(struct dhcpcd_ctx *ctx, struct ifaddrs **ifaddrs)\n{\n#ifndef PRIVSEP_GETIFADDRS\n\tUNUSED(ctx);\n#endif\n\n\tif (ifaddrs == NULL)\n\t\treturn;\n\n#ifdef PRIVSEP_GETIFADDRS\n\tif (IN_PRIVSEP(ctx))\n\t\tfree(*ifaddrs);\n\telse\n#endif\n\t\tfreeifaddrs(*ifaddrs);\n}\n\nvoid\nif_deletestaleaddrs(struct if_head *ifs)\n{\n\tstruct interface *ifp;\n\n\tTAILQ_FOREACH(ifp, ifs, next) {\n#ifdef INET\n\t\tipv4_deletestaleaddrs(ifp);\n#endif\n#ifdef INET6\n\t\tipv6_deletestaleaddrs(ifp);\n#endif\n\t}\n}\n\nbool\nif_valid_hwaddr(const uint8_t *hwaddr, size_t hwlen)\n{\n\tsize_t i;\n\tbool all_zeros, all_ones;\n\n\tall_zeros = all_ones = true;\n\tfor (i = 0; i < hwlen; i++) {\n\t\tif (hwaddr[i] != 0x00)\n\t\t\tall_zeros = false;\n\t\tif (hwaddr[i] != 0xff)\n\t\t\tall_ones = false;\n\t\tif (!all_zeros && !all_ones)\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\n#if defined(AF_PACKET) && !defined(AF_LINK)\nstatic unsigned int\nif_check_arphrd(struct interface *ifp, unsigned int active, bool if_noconf)\n{\n\tswitch (ifp->hwtype) {\n\tcase ARPHRD_ETHER:\t/* FALLTHROUGH */\n\tcase ARPHRD_IEEE1394:\t/* FALLTHROUGH */\n\tcase ARPHRD_INFINIBAND: /* FALLTHROUGH */\n\tcase ARPHRD_NONE:\t/* FALLTHROUGH */\n\t\tbreak;\n\tcase ARPHRD_LOOPBACK:\n\tcase ARPHRD_PPP:\n\t\tif (if_noconf && active) {\n\t\t\tlogdebugx(\"%s: ignoring due to interface type and\"\n\t\t\t\t  \" no config\",\n\t\t\t    ifp->name);\n\t\t\tactive = IF_INACTIVE;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tif (active) {\n\t\t\tint i;\n\n\t\t\tif (if_noconf)\n\t\t\t\tactive = IF_INACTIVE;\n\t\t\ti = active ? LOG_WARNING : LOG_DEBUG;\n\t\t\tlogmessage(i,\n\t\t\t    \"%s: unsupported\"\n\t\t\t    \" interface type 0x%.2x\",\n\t\t\t    ifp->name, ifp->hwtype);\n\t\t}\n\t\tbreak;\n\t}\n\n\treturn active;\n}\n#endif\n\nstruct if_head *\nif_discover(struct dhcpcd_ctx *ctx, struct ifaddrs **ifaddrs, int argc,\n    char *const *argv)\n{\n\tstruct ifaddrs *ifa;\n\tint i;\n\tunsigned int active;\n\tstruct if_head *ifs;\n\tstruct interface *ifp;\n\tstruct if_spec spec;\n\tbool if_noconf;\n#ifdef AF_LINK\n\tconst struct sockaddr_dl *sdl;\n#ifdef IFLR_ACTIVE\n\tstruct if_laddrreq iflr = { .flags = IFLR_PREFIX };\n#endif\n#elif defined(AF_PACKET)\n\tconst struct sockaddr_ll *sll;\n#endif\n#if defined(SIOCGIFPRIORITY)\n\tstruct ifreq ifr;\n#endif\n\n\tif ((ifs = malloc(sizeof(*ifs))) == NULL) {\n\t\tlogerr(__func__);\n\t\treturn NULL;\n\t}\n\tTAILQ_INIT(ifs);\n\n#ifdef PRIVSEP_GETIFADDRS\n\tif (ctx->options & DHCPCD_PRIVSEP) {\n\t\tif (ps_root_getifaddrs(ctx, ifaddrs) == -1) {\n\t\t\tlogerr(\"ps_root_getifaddrs\");\n\t\t\tfree(ifs);\n\t\t\treturn NULL;\n\t\t}\n\t} else\n#endif\n\t    if (getifaddrs(ifaddrs) == -1) {\n\t\tlogerr(\"getifaddrs\");\n\t\tfree(ifs);\n\t\treturn NULL;\n\t}\n\n\tfor (ifa = *ifaddrs; ifa; ifa = ifa->ifa_next) {\n\t\tif (ifa->ifa_addr != NULL) {\n#ifdef AF_LINK\n\t\t\tif (ifa->ifa_addr->sa_family != AF_LINK)\n\t\t\t\tcontinue;\n#elif defined(AF_PACKET)\n\t\t\tif (ifa->ifa_addr->sa_family != AF_PACKET)\n\t\t\t\tcontinue;\n#endif\n\t\t}\n\t\tif (if_nametospec(ifa->ifa_name, &spec) != 0)\n\t\t\tcontinue;\n\n\t\t/* It's possible for an interface to have >1 AF_LINK.\n\t\t * For our purposes, we use the first one. */\n\t\tTAILQ_FOREACH(ifp, ifs, next) {\n\t\t\tif (strcmp(ifp->name, spec.devname) == 0)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (ifp)\n\t\t\tcontinue;\n\n\t\tif (argc > 0) {\n\t\t\tfor (i = 0; i < argc; i++) {\n\t\t\t\tif (strcmp(argv[i], spec.devname) == 0)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tactive = (i == argc) ? IF_INACTIVE : IF_ACTIVE_USER;\n\t\t} else {\n\t\t\t/* -1 means we're discovering against a specific\n\t\t\t * interface, but we still need the below rules\n\t\t\t * to apply. */\n\t\t\tif (argc == -1 && strcmp(argv[0], spec.devname) != 0)\n\t\t\t\tcontinue;\n\t\t\tactive = ctx->options & DHCPCD_INACTIVE ?\n\t\t\t    IF_INACTIVE :\n\t\t\t    IF_ACTIVE_USER;\n\t\t}\n\n\t\tfor (i = 0; i < ctx->ifdc; i++)\n\t\t\tif (fnmatch(ctx->ifdv[i], spec.devname, 0) == 0)\n\t\t\t\tbreak;\n\t\tif (i < ctx->ifdc)\n\t\t\tactive = IF_INACTIVE;\n\t\tfor (i = 0; i < ctx->ifc; i++)\n\t\t\tif (fnmatch(ctx->ifv[i], spec.devname, 0) == 0)\n\t\t\t\tbreak;\n\t\tif (ctx->ifc && i == ctx->ifc)\n\t\t\tactive = IF_INACTIVE;\n\t\tfor (i = 0; i < ctx->ifac; i++)\n\t\t\tif (fnmatch(ctx->ifav[i], spec.devname, 0) == 0)\n\t\t\t\tbreak;\n\t\tif (ctx->ifac && i == ctx->ifac)\n\t\t\tactive = IF_INACTIVE;\n\n#ifdef PLUGIN_DEV\n\t\t/* Ensure that the interface name has settled */\n\t\tif (!dev_initialised(ctx, spec.devname)) {\n\t\t\tlogdebugx(\"%s: waiting for interface to initialise\",\n\t\t\t    spec.devname);\n\t\t\tcontinue;\n\t\t}\n#endif\n\n\t\tif (if_vimaster(ctx, spec.devname) == 1) {\n\t\t\tint loglevel = argc != 0 ? LOG_ERR : LOG_DEBUG;\n\t\t\tlogmessage(loglevel,\n\t\t\t    \"%s: is a Virtual Interface Master, skipping\",\n\t\t\t    spec.devname);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif_noconf = ((argc == 0 || argc == -1) && ctx->ifac == 0 &&\n\t\t    !if_hasconf(ctx, spec.devname));\n\n\t\t/* Don't allow some reserved interface names unless explicit. */\n\t\tif (if_noconf && if_ignore(ctx, spec.devname)) {\n\t\t\tlogdebugx(\"%s: ignoring due to interface type and\"\n\t\t\t\t  \" no config\",\n\t\t\t    spec.devname);\n\t\t\tactive = IF_INACTIVE;\n\t\t}\n\n\t\tifp = calloc(1, sizeof(*ifp));\n\t\tif (ifp == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\tbreak;\n\t\t}\n\t\tifp->ctx = ctx;\n\t\tstrlcpy(ifp->name, spec.devname, sizeof(ifp->name));\n\t\tifp->flags = ifa->ifa_flags;\n\n\t\tif (ifa->ifa_addr != NULL) {\n#ifdef AF_LINK\n\t\t\tsdl = (const void *)ifa->ifa_addr;\n\n#ifdef IFLR_ACTIVE\n\t\t\t/* We need to check for active address */\n\t\t\tstrlcpy(iflr.iflr_name, ifp->name,\n\t\t\t    sizeof(iflr.iflr_name));\n\t\t\tmemcpy(&iflr.addr, ifa->ifa_addr,\n\t\t\t    MIN(ifa->ifa_addr->sa_len, sizeof(iflr.addr)));\n\t\t\tiflr.flags = IFLR_PREFIX;\n\t\t\tiflr.prefixlen = (unsigned int)sdl->sdl_alen * NBBY;\n\t\t\tif (ioctl(ctx->pf_link_fd, SIOCGLIFADDR, &iflr) == -1 ||\n\t\t\t    !(iflr.flags & IFLR_ACTIVE)) {\n\t\t\t\tif_free(ifp);\n\t\t\t\tcontinue;\n\t\t\t}\n#endif\n\n\t\t\tifp->index = sdl->sdl_index;\n\t\t\tswitch (sdl->sdl_type) {\n#ifdef IFT_BRIDGE\n\t\t\tcase IFT_BRIDGE: /* FALLTHROUGH */\n#endif\n#ifdef IFT_PROPVIRTUAL\n\t\t\tcase IFT_PROPVIRTUAL: /* FALLTHROUGH */\n#endif\n#ifdef IFT_TUNNEL\n\t\t\tcase IFT_TUNNEL: /* FALLTHROUGH */\n#endif\n\t\t\tcase IFT_LOOP: /* FALLTHROUGH */\n\t\t\tcase IFT_PPP:\n\t\t\t\t/* Don't allow unless explicit */\n\t\t\t\tif (if_noconf && active) {\n\t\t\t\t\tlogdebugx(\"%s: ignoring due to\"\n\t\t\t\t\t\t  \" interface type and\"\n\t\t\t\t\t\t  \" no config\",\n\t\t\t\t\t    ifp->name);\n\t\t\t\t\tactive = IF_INACTIVE;\n\t\t\t\t}\n\t\t\t\t__fallthrough; /* appease gcc */\n\t\t\t\t\t       /* FALLTHROUGH */\n#ifdef IFT_L2VLAN\n\t\t\tcase IFT_L2VLAN: /* FALLTHROUGH */\n#endif\n#ifdef IFT_L3IPVLAN\n\t\t\tcase IFT_L3IPVLAN: /* FALLTHROUGH */\n#endif\n\t\t\tcase IFT_ETHER:\n\t\t\t\tifp->hwtype = ARPHRD_ETHER;\n\t\t\t\tbreak;\n#ifdef IFT_IEEE1394\n\t\t\tcase IFT_IEEE1394:\n\t\t\t\tifp->hwtype = ARPHRD_IEEE1394;\n\t\t\t\tbreak;\n#endif\n#ifdef IFT_INFINIBAND\n\t\t\tcase IFT_INFINIBAND:\n\t\t\t\tifp->hwtype = ARPHRD_INFINIBAND;\n\t\t\t\tbreak;\n#endif\n\t\t\tdefault:\n\t\t\t\t/* Don't allow unless explicit */\n\t\t\t\tif (active) {\n\t\t\t\t\tif (if_noconf)\n\t\t\t\t\t\tactive = IF_INACTIVE;\n\t\t\t\t\ti = active ? LOG_WARNING : LOG_DEBUG;\n\t\t\t\t\tlogmessage(i,\n\t\t\t\t\t    \"%s: unsupported\"\n\t\t\t\t\t    \" interface type 0x%.2x\",\n\t\t\t\t\t    ifp->name, sdl->sdl_type);\n\t\t\t\t}\n\t\t\t\t/* Pretend it's ethernet */\n\t\t\t\tifp->hwtype = ARPHRD_ETHER;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tifp->hwlen = sdl->sdl_alen;\n\t\t\tmemcpy(ifp->hwaddr, CLLADDR(sdl), ifp->hwlen);\n#elif defined(AF_PACKET)\n\t\t\tsll = (const void *)ifa->ifa_addr;\n\t\t\tifp->index = (unsigned int)sll->sll_ifindex;\n\t\t\tifp->hwtype = sll->sll_hatype;\n\t\t\tifp->hwlen = sll->sll_halen;\n\t\t\tif (ifp->hwlen != 0)\n\t\t\t\tmemcpy(ifp->hwaddr, sll->sll_addr, ifp->hwlen);\n\t\t\tactive = if_check_arphrd(ifp, active, if_noconf);\n#endif\n\t\t}\n#ifdef __linux__\n\t\telse {\n\t\t\tstruct ifreq ifr = { .ifr_flags = 0 };\n\n\t\t\t/* This is a huge bug in getifaddrs(3) as there\n\t\t\t * is no reason why this can't be returned in\n\t\t\t * ifa_addr. */\n\t\t\tstrlcpy(ifr.ifr_name, ifa->ifa_name,\n\t\t\t    sizeof(ifr.ifr_name));\n\t\t\tif (ioctl(ctx->pf_inet_fd, SIOCGIFHWADDR, &ifr) == -1)\n\t\t\t\tlogerr(\"%s: SIOCGIFHWADDR\", ifa->ifa_name);\n\t\t\tifp->hwtype = ifr.ifr_hwaddr.sa_family;\n\t\t\tif (ioctl(ctx->pf_inet_fd, SIOCGIFINDEX, &ifr) == -1)\n\t\t\t\tlogerr(\"%s: SIOCGIFINDEX\", ifa->ifa_name);\n\t\t\tifp->index = (unsigned int)ifr.ifr_ifindex;\n\t\t\tif_check_arphrd(ifp, active, if_noconf);\n\t\t}\n#endif\n\n\t\tif (!(ctx->options & (DHCPCD_DUMPLEASE | DHCPCD_TEST))) {\n\t\t\t/* Handle any platform init for the interface */\n\t\t\tif (active != IF_INACTIVE && if_init(ifp) == -1) {\n\t\t\t\tlogerr(\"%s: if_init\", ifp->name);\n\t\t\t\tif_free(ifp);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tifp->mtu = if_getmtu(ifp);\n\t\tifp->vlanid = if_vlanid(ifp);\n\n#ifdef SIOCGIFPRIORITY\n\t\t/* Respect the interface priority */\n\t\tmemset(&ifr, 0, sizeof(ifr));\n\t\tstrlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));\n\t\tif (pioctl(ctx, SIOCGIFPRIORITY, &ifr, sizeof(ifr)) == 0)\n\t\t\tifp->metric = (unsigned int)ifr.ifr_metric;\n\t\tif_getssid(ifp);\n#else\n\t\t/* Leave a low portion for user config */\n\t\tifp->metric = RTMETRIC_BASE + ifp->index;\n\t\tif (if_getssid(ifp) != -1) {\n\t\t\tifp->wireless = true;\n\t\t\tifp->metric += RTMETRIC_WIRELESS;\n\t\t}\n#endif\n\n\t\tifp->active = active;\n\t\tifp->carrier = if_carrier(ifp, ifa->ifa_data);\n\t\tTAILQ_INSERT_TAIL(ifs, ifp, next);\n\t}\n\n\treturn ifs;\n}\n\n/*\n * eth0.100:2 OR eth0i100:2 (seems to be NetBSD xvif(4) only)\n *\n * drvname == eth\n * devname == eth0.100 OR eth0i100\n * ppa = 0\n * lun = 2\n */\nint\nif_nametospec(const char *ifname, struct if_spec *spec)\n{\n\tchar *ep, *pp;\n\tint e;\n\n\tif (ifname == NULL || *ifname == '\\0' ||\n\t    strlcpy(spec->ifname, ifname, sizeof(spec->ifname)) >=\n\t\tsizeof(spec->ifname) ||\n\t    strlcpy(spec->drvname, ifname, sizeof(spec->drvname)) >=\n\t\tsizeof(spec->drvname)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\t/* :N is an alias */\n\tep = strchr(spec->drvname, ':');\n\tif (ep) {\n\t\tspec->lun = (int)strtoi(ep + 1, NULL, 10, 0, INT_MAX, &e);\n\t\tif (e != 0) {\n\t\t\terrno = e;\n\t\t\treturn -1;\n\t\t}\n\t\t*ep = '\\0';\n#ifdef __sun\n\t\tep--;\n#endif\n\t} else {\n\t\tspec->lun = -1;\n#ifdef __sun\n\t\tep = spec->drvname + strlen(spec->drvname) - 1;\n#endif\n\t}\n\n\tstrlcpy(spec->devname, spec->drvname, sizeof(spec->devname));\n#ifdef __sun\n\t/* Solaris has numbers in the driver name, such as e1000g */\n\twhile (ep > spec->drvname && isdigit((int)*ep))\n\t\tep--;\n\tif (*ep++ == ':') {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n#else\n\t/* BSD and Linux no not have numbers in the driver name */\n\tfor (ep = spec->drvname; *ep != '\\0' && !isdigit((int)*ep); ep++) {\n\t\tif (*ep == ':') {\n\t\t\terrno = EINVAL;\n\t\t\treturn -1;\n\t\t}\n\t}\n#endif\n\tspec->ppa = (int)strtoi(ep, &pp, 10, 0, INT_MAX, &e);\n\t*ep = '\\0';\n\n#ifndef __sun\n\t/*\n\t * . is used for VLAN style names\n\t * i is used on NetBSD for xvif interfaces\n\t */\n\tif (pp != NULL && (*pp == '.' || *pp == 'i')) {\n\t\tspec->vlid = (int)strtoi(pp + 1, NULL, 10, 0, INT_MAX, &e);\n\t\tif (e)\n\t\t\tspec->vlid = -1;\n\t} else\n#endif\n\t\tspec->vlid = -1;\n\n\treturn 0;\n}\n\nstatic struct interface *\nif_findindexname(struct if_head *ifaces, unsigned int idx, const char *name)\n{\n\tif (ifaces != NULL) {\n\t\tstruct if_spec spec;\n\t\tstruct interface *ifp;\n\n\t\tif (name && if_nametospec(name, &spec) == -1)\n\t\t\treturn NULL;\n\n\t\tTAILQ_FOREACH(ifp, ifaces, next) {\n\t\t\tif ((name && strcmp(ifp->name, spec.devname) == 0) ||\n\t\t\t    (!name && ifp->index == idx))\n\t\t\t\treturn ifp;\n\t\t}\n\t}\n\n\terrno = ENXIO;\n\treturn NULL;\n}\n\nstruct interface *\nif_find(struct if_head *ifaces, const char *name)\n{\n\treturn if_findindexname(ifaces, 0, name);\n}\n\nstruct interface *\nif_findindex(struct if_head *ifaces, unsigned int idx)\n{\n\treturn if_findindexname(ifaces, idx, NULL);\n}\n\nstruct interface *\nif_loopback(struct dhcpcd_ctx *ctx)\n{\n\tstruct interface *ifp;\n\n\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\tif (ifp->flags & IFF_LOOPBACK)\n\t\t\treturn ifp;\n\t}\n\treturn NULL;\n}\n\nint\nif_getmtu(const struct interface *ifp)\n{\n#ifdef __sun\n\treturn if_mtu_os(ifp);\n#else\n\tstruct ifreq ifr = { .ifr_mtu = 0 };\n\n\tstrlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));\n\tif (pioctl(ifp->ctx, SIOCGIFMTU, &ifr, sizeof(ifr)) == -1)\n\t\treturn -1;\n\treturn ifr.ifr_mtu;\n#endif\n}\n\n#ifdef ALIAS_ADDR\nint\nif_makealias(char *alias, size_t alias_len, const char *ifname, int lun)\n{\n\tif (lun == 0)\n\t\treturn strlcpy(alias, ifname, alias_len);\n\treturn snprintf(alias, alias_len, \"%s:%u\", ifname, lun);\n}\n#endif\n\nstruct interface *\nif_findifpfromcmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg, int *hoplimit)\n{\n\tstruct cmsghdr *cm;\n\tunsigned int ifindex = 0;\n\tstruct interface *ifp;\n#ifdef INET\n#ifdef IP_RECVIF\n\tstruct sockaddr_dl sdl;\n#else\n\tstruct in_pktinfo ipi;\n#endif\n#endif\n#ifdef INET6\n\tstruct in6_pktinfo ipi6;\n#else\n\tUNUSED(hoplimit);\n#endif\n\n\tfor (cm = (struct cmsghdr *)CMSG_FIRSTHDR(msg); cm;\n\t    cm = (struct cmsghdr *)CMSG_NXTHDR(msg, cm)) {\n#ifdef INET\n\t\tif (cm->cmsg_level == IPPROTO_IP) {\n\t\t\tswitch (cm->cmsg_type) {\n#ifdef IP_RECVIF\n\t\t\tcase IP_RECVIF:\n\t\t\t\tif (cm->cmsg_len <\n\t\t\t\t    offsetof(struct sockaddr_dl, sdl_index) +\n\t\t\t\t\tsizeof(sdl.sdl_index))\n\t\t\t\t\tcontinue;\n\t\t\t\tmemcpy(&sdl, CMSG_DATA(cm),\n\t\t\t\t    MIN(sizeof(sdl), cm->cmsg_len));\n\t\t\t\tifindex = sdl.sdl_index;\n\t\t\t\tbreak;\n#else\n\t\t\tcase IP_PKTINFO:\n\t\t\t\tif (cm->cmsg_len != CMSG_LEN(sizeof(ipi)))\n\t\t\t\t\tcontinue;\n\t\t\t\tmemcpy(&ipi, CMSG_DATA(cm), sizeof(ipi));\n\t\t\t\tifindex = (unsigned int)ipi.ipi_ifindex;\n\t\t\t\tbreak;\n#endif\n\t\t\t}\n\t\t}\n#endif\n#ifdef INET6\n\t\tif (cm->cmsg_level == IPPROTO_IPV6) {\n\t\t\tswitch (cm->cmsg_type) {\n\t\t\tcase IPV6_PKTINFO:\n\t\t\t\tif (cm->cmsg_len != CMSG_LEN(sizeof(ipi6)))\n\t\t\t\t\tcontinue;\n\t\t\t\tmemcpy(&ipi6, CMSG_DATA(cm), sizeof(ipi6));\n\t\t\t\tifindex = (unsigned int)ipi6.ipi6_ifindex;\n\t\t\t\tbreak;\n\t\t\tcase IPV6_HOPLIMIT:\n\t\t\t\tif (cm->cmsg_len != CMSG_LEN(sizeof(int)))\n\t\t\t\t\tcontinue;\n\t\t\t\tif (hoplimit == NULL)\n\t\t\t\t\tbreak;\n\t\t\t\tmemcpy(hoplimit, CMSG_DATA(cm), sizeof(int));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n#endif\n\t}\n\n\t/* Find the receiving interface */\n\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\tif (ifp->index == ifindex)\n\t\t\tbreak;\n\t}\n\tif (ifp == NULL)\n\t\terrno = ESRCH;\n\treturn ifp;\n}\n\nint\nxsocket(int domain, int type, int protocol)\n{\n\tint s;\n#if !defined(HAVE_SOCK_CLOEXEC) || !defined(HAVE_SOCK_NONBLOCK)\n\tint xflags, xtype = type;\n#endif\n\n#ifndef HAVE_SOCK_CLOEXEC\n\tif (xtype & SOCK_CLOEXEC)\n\t\ttype &= ~SOCK_CLOEXEC;\n#endif\n#ifndef HAVE_SOCK_NONBLOCK\n\tif (xtype & SOCK_NONBLOCK)\n\t\ttype &= ~SOCK_NONBLOCK;\n#endif\n\n\tif ((s = socket(domain, type, protocol)) == -1)\n\t\treturn -1;\n#ifdef DEBUG_FD\n\tlogerrx(\"pid %d fd=%d domain=%d type=%d protocol=%d\", getpid(), s,\n\t    domain, type, protocol);\n#endif\n\n#ifndef HAVE_SOCK_CLOEXEC\n\tif ((xtype & SOCK_CLOEXEC) &&\n\t    ((xflags = fcntl(s, F_GETFD)) == -1 ||\n\t\tfcntl(s, F_SETFD, xflags | FD_CLOEXEC) == -1))\n\t\tgoto out;\n#endif\n#ifndef HAVE_SOCK_NONBLOCK\n\tif ((xtype & SOCK_NONBLOCK) &&\n\t    ((xflags = fcntl(s, F_GETFL)) == -1 ||\n\t\tfcntl(s, F_SETFL, xflags | O_NONBLOCK) == -1))\n\t\tgoto out;\n#endif\n\n\treturn s;\n\n#if !defined(HAVE_SOCK_CLOEXEC) || !defined(HAVE_SOCK_NONBLOCK)\nout:\n\tclose(s);\n\treturn -1;\n#endif\n}\n\nint\nxsocketpair(int domain, int type, int protocol, int fd[2])\n{\n\tint s;\n#if !defined(HAVE_SOCK_CLOEXEC) || !defined(HAVE_SOCK_NONBLOCK)\n\tint xflags, xtype = type;\n#endif\n\n#ifndef HAVE_SOCK_CLOEXEC\n\tif (xtype & SOCK_CLOEXEC)\n\t\ttype &= ~SOCK_CLOEXEC;\n#endif\n#ifndef HAVE_SOCK_NONBLOCK\n\tif (xtype & SOCK_NONBLOCK)\n\t\ttype &= ~SOCK_NONBLOCK;\n#endif\n\n\tif ((s = socketpair(domain, type, protocol, fd)) == -1)\n\t\treturn -1;\n\n#ifdef DEBUG_FD\n\tlogerrx(\"pid %d fd[0]=%d fd[1]=%d\", getpid(), fd[0], fd[1]);\n#endif\n\n#ifndef HAVE_SOCK_CLOEXEC\n\tif ((xtype & SOCK_CLOEXEC) &&\n\t    ((xflags = fcntl(fd[0], F_GETFD)) == -1 ||\n\t\tfcntl(fd[0], F_SETFD, xflags | FD_CLOEXEC) == -1))\n\t\tgoto out;\n\tif ((xtype & SOCK_CLOEXEC) &&\n\t    ((xflags = fcntl(fd[1], F_GETFD)) == -1 ||\n\t\tfcntl(fd[1], F_SETFD, xflags | FD_CLOEXEC) == -1))\n\t\tgoto out;\n#endif\n#ifndef HAVE_SOCK_NONBLOCK\n\tif ((xtype & SOCK_NONBLOCK) &&\n\t    ((xflags = fcntl(fd[0], F_GETFL)) == -1 ||\n\t\tfcntl(fd[0], F_SETFL, xflags | O_NONBLOCK) == -1))\n\t\tgoto out;\n\tif ((xtype & SOCK_NONBLOCK) &&\n\t    ((xflags = fcntl(fd[1], F_GETFL)) == -1 ||\n\t\tfcntl(fd[1], F_SETFL, xflags | O_NONBLOCK) == -1))\n\t\tgoto out;\n#endif\n\n\treturn s;\n\n#if !defined(HAVE_SOCK_CLOEXEC) || !defined(HAVE_SOCK_NONBLOCK)\nout:\n\tclose(fd[0]);\n\tclose(fd[1]);\n\treturn -1;\n#endif\n}\n"
  },
  {
    "path": "src/if.h",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef INTERFACE_H\n#define INTERFACE_H\n\n#include <net/if.h>\n#include <net/route.h> /* for RTM_ADD et all */\n#include <netinet/in.h>\n#ifdef BSD\n#include <netinet/in_var.h> /* for IN_IFF_TENTATIVE et all */\n#endif\n\n#include <ifaddrs.h>\n\n/* If the interface does not support carrier status (ie PPP),\n * dhcpcd can poll it for the relevant flags periodically */\n#define IF_POLL_UP 100 /* milliseconds */\n\n/*\n * Systems which handle 1 address per alias.\n * Currenly this is just Solaris.\n * While Linux can do aliased addresses, it is only useful for their\n * legacy ifconfig(8) tool which cannot display >1 IPv4 address\n * (it can display many IPv6 addresses which makes the limitation odd).\n * Linux has ip(8) which is a more feature rich tool, without the above\n * restriction.\n */\n#ifndef ALIAS_ADDR\n#ifdef __sun\n#define ALIAS_ADDR\n#endif\n#endif\n\n#include \"config.h\"\n\n/* POSIX defines ioctl request as an int, which Solaris and musl use.\n * Everyone else use an unsigned long, which happens to be the bigger one\n * so we use that in our on wire API. */\n#ifdef IOCTL_REQUEST_TYPE\ntypedef IOCTL_REQUEST_TYPE ioctl_request_t;\n#else\ntypedef unsigned long ioctl_request_t;\n#endif\n\n#include \"dhcpcd.h\"\n#include \"ipv4.h\"\n#include \"ipv6.h\"\n#include \"route.h\"\n\n#define EUI64_ADDR_LEN\t    8\n#define INFINIBAND_ADDR_LEN 20\n\n/* Linux 2.4 doesn't define this */\n#ifndef ARPHRD_IEEE1394\n#define ARPHRD_IEEE1394 24\n#endif\n\n/* The BSD's don't define this yet */\n#ifndef ARPHRD_INFINIBAND\n#define ARPHRD_INFINIBAND 32\n#endif\n\n/* Maximum frame length.\n * Support jumbo frames and some extra. */\n#define FRAMEHDRLEN_MAX 14 /* only ethernet support */\n#define FRAMELEN_MAX\t(FRAMEHDRLEN_MAX + 9216)\n\n#define UDPLEN_MAX\t64 * 1024\n\n/* Work out if we have a private address or not\n * 10/8\n * 172.16/12\n * 192.168/16\n */\n#ifndef IN_PRIVATE\n#define IN_PRIVATE(addr)                           \\\n\t(((addr & IN_CLASSA_NET) == 0x0a000000) || \\\n\t    ((addr & 0xfff00000) == 0xac100000) || \\\n\t    ((addr & IN_CLASSB_NET) == 0xc0a80000))\n#endif\n\n#ifndef CLLADDR\n#ifdef AF_LINK\n#define CLLADDR(sdl) (const void *)((sdl)->sdl_data + (sdl)->sdl_nlen)\n#endif\n#endif\n\n#ifdef __sun\n/* Solaris stupidly defines this for compat with BSD\n * but then ignores it. */\n#undef RTF_CLONING\n\n/* This interface is busted on DilOS at least.\n * It used to work, but lukily Solaris can fall back to\n * IP_PKTINFO. */\n#undef IP_RECVIF\n#endif\n\n/* Private structures specific to an OS */\n#ifdef BSD\nstruct priv {\n#ifdef INET6\n\tint pf_inet6_fd;\n#endif\n#if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */\n\tint pf_link_fd;\n#endif\n};\n#endif\n#ifdef __linux__\nstruct priv {\n\tint route_fd;\n\tint generic_fd;\n\tuint32_t route_pid;\n};\n#endif\n#ifdef __sun\nstruct priv {\n#ifdef INET6\n\tint pf_inet6_fd;\n#endif\n};\n#endif\n\n#ifdef __sun\n/* Solaris getifaddrs is very un-suitable for dhcpcd.\n * See if-sun.c for details why. */\nstruct ifaddrs;\nint if_getifaddrs(struct ifaddrs **);\n#define getifaddrs if_getifaddrs\nint if_getsubnet(struct dhcpcd_ctx *, const char *, int, void *, size_t);\n#endif\n\nint if_ioctl(struct dhcpcd_ctx *, ioctl_request_t, void *, size_t);\n#ifdef HAVE_PLEDGE\n#define pioctl(ctx, req, data, len) if_ioctl((ctx), (req), (data), (len))\n#else\n#define pioctl(ctx, req, data, len) \\\n\tioctl((ctx)->pf_inet_fd, (req), (data), (len))\n#endif\nint if_setflag(struct interface *, short, short);\nint if_getmtu(const struct interface *);\n#define if_up(ifp)   if_setflag((ifp), (IFF_UP | IFF_RUNNING), 0)\n#define if_down(ifp) if_setflag((ifp), 0, IFF_UP);\nbool if_is_link_up(const struct interface *);\nbool if_valid_hwaddr(const uint8_t *, size_t);\nstruct if_head *if_discover(struct dhcpcd_ctx *, struct ifaddrs **, int,\n    char *const *);\nvoid if_freeifaddrs(struct dhcpcd_ctx *ctx, struct ifaddrs **);\nvoid if_markaddrsstale(struct if_head *);\nvoid if_learnaddrs(struct dhcpcd_ctx *, struct if_head *, struct ifaddrs **);\nvoid if_deletestaleaddrs(struct if_head *);\nstruct interface *if_find(struct if_head *, const char *);\nstruct interface *if_findindex(struct if_head *, unsigned int);\nstruct interface *if_loopback(struct dhcpcd_ctx *);\nvoid if_free(struct interface *);\nint if_carrier(struct interface *, const void *);\nbool if_roaming(struct interface *);\n\n#ifdef ALIAS_ADDR\nint if_makealias(char *, size_t, const char *, int);\n#endif\n\nint if_mtu_os(const struct interface *);\n\n/*\n * Helper to decode an interface name of bge0:1 to\n * devname = bge0, drvname = bge0, ppa = 0, lun = 1.\n * If ppa or lun are invalid they are set to -1.\n */\nstruct if_spec {\n\tchar ifname[IF_NAMESIZE];\n\tchar devname[IF_NAMESIZE];\n\tchar drvname[IF_NAMESIZE];\n\tint ppa;\n\tint vlid;\n\tint lun;\n};\nint if_nametospec(const char *, struct if_spec *);\n\n/* The below functions are provided by if-KERNEL.c */\nint os_init(void);\nint if_conf(struct interface *);\nint if_init(struct interface *);\nint if_getssid(struct interface *);\nint if_ignoregroup(int, const char *);\nbool if_ignore(struct dhcpcd_ctx *, const char *);\nint if_vimaster(struct dhcpcd_ctx *ctx, const char *);\nunsigned short if_vlanid(const struct interface *);\nchar *if_getnetworknamespace(char *, size_t); /* used by udev */\nint if_opensockets(struct dhcpcd_ctx *);\nint if_opensockets_os(struct dhcpcd_ctx *);\nvoid if_closesockets(struct dhcpcd_ctx *);\nvoid if_closesockets_os(struct dhcpcd_ctx *);\nint if_handlelink(struct dhcpcd_ctx *);\nint if_randomisemac(struct interface *);\nint if_setmac(struct interface *ifp, void *, uint8_t);\n\n/* dhcpcd uses the same routing flags as BSD.\n * If the platform doesn't use these flags,\n * map them in the platform interace file. */\n#ifndef RTM_ADD\n#define RTM_ADD\t   0x1 /* Add Route */\n#define RTM_DELETE 0x2 /* Delete Route */\n#define RTM_CHANGE 0x3 /* Change Metrics or flags */\n#define RTM_GET\t   0x4 /* Report Metrics */\n#endif\n\n/* Define SOCK_CLOEXEC and SOCK_NONBLOCK for systems that lack it.\n * xsocket() in if.c will map them to fctnl FD_CLOEXEC and O_NONBLOCK. */\n#ifdef SOCK_CLOEXEC\n#define HAVE_SOCK_CLOEXEC\n#else\n#define SOCK_CLOEXEC 0x10000000\n#endif\n#ifdef SOCK_NONBLOCK\n#define HAVE_SOCK_NONBLOCK\n#else\n#define SOCK_NONBLOCK 0x20000000\n#endif\n#ifndef SOCK_CXNB\n#define SOCK_CXNB SOCK_CLOEXEC | SOCK_NONBLOCK\n#endif\nint xsocket(int, int, int);\nint xsocketpair(int, int, int, int[2]);\n\nint if_route(unsigned char, const struct rt *rt);\nint if_initrt(struct dhcpcd_ctx *, rb_tree_t *, int);\n\nint if_missfilter(struct interface *, struct sockaddr *);\nint if_missfilter_apply(struct dhcpcd_ctx *);\n\n#ifdef INET\nint if_address(unsigned char, const struct ipv4_addr *);\nint if_addrflags(const struct interface *, const struct in_addr *,\n    const char *);\n\n#endif\n\n#ifdef INET6\nvoid if_disable_rtadv(void);\nvoid if_setup_inet6(const struct interface *);\nint ip6_forwarding(const char *ifname);\n\nstruct ra;\nstruct ipv6_addr;\n\nint if_applyra(const struct ra *);\nint if_address6(unsigned char, const struct ipv6_addr *);\nint if_addrflags6(const struct interface *, const struct in6_addr *,\n    const char *);\nint if_getlifetime6(struct ipv6_addr *);\n\n#else\n#define if_checkipv6(a, b, c) (-1)\n#endif\n\nint if_machinearch(char *, size_t);\nstruct interface *if_findifpfromcmsg(struct dhcpcd_ctx *, struct msghdr *,\n    int *);\n\n#ifdef __linux__\nint if_linksocket(struct sockaddr_nl *, int, int);\nint if_getnetlink(struct dhcpcd_ctx *, struct iovec *, int, int,\n    int (*)(struct dhcpcd_ctx *, void *, struct nlmsghdr *), void *);\n#endif\n#endif\n"
  },
  {
    "path": "src/ipv4.c",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/types.h>\n#include <sys/socket.h>\n\n#include <net/if.h>\n#include <net/route.h>\n#include <netinet/in.h>\n#include <netinet/if_ether.h>\n\n#include <arpa/inet.h>\n#include <assert.h>\n#include <ctype.h>\n#include <errno.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include \"arp.h\"\n#include \"common.h\"\n#include \"config.h\"\n#include \"dhcp.h\"\n#include \"dhcpcd.h\"\n#include \"eloop.h\"\n#include \"if-options.h\"\n#include \"if.h\"\n#include \"ipv4.h\"\n#include \"ipv4ll.h\"\n#include \"logerr.h\"\n#include \"route.h\"\n#include \"sa.h\"\n#include \"script.h\"\n\n#define IPV4_LOOPBACK_ROUTE\n#if defined(__linux__) || defined(__sun) || (defined(BSD) && defined(RTF_LOCAL))\n/* Linux has had loopback routes in the local table since 2.2\n * Solaris does not seem to support loopback routes. */\n#undef IPV4_LOOPBACK_ROUTE\n#endif\n\nuint8_t\ninet_ntocidr(struct in_addr address)\n{\n\tuint8_t cidr = 0;\n\tuint32_t mask = htonl(address.s_addr);\n\n\twhile (mask) {\n\t\tcidr++;\n\t\tmask <<= 1;\n\t}\n\treturn cidr;\n}\n\nint\ninet_cidrtoaddr(int cidr, struct in_addr *addr)\n{\n\tint ocets;\n\n\tif (cidr < 1 || cidr > 32) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\tocets = (cidr + 7) / NBBY;\n\n\taddr->s_addr = 0;\n\tif (ocets > 0) {\n\t\tmemset(&addr->s_addr, 255, (size_t)ocets - 1);\n\t\tmemset((unsigned char *)&addr->s_addr + (ocets - 1),\n\t\t    (256 - (1 << (32 - cidr) % NBBY)), 1);\n\t}\n\n\treturn 0;\n}\n\nuint32_t\nipv4_getnetmask(uint32_t addr)\n{\n\tuint32_t dst;\n\n\tif (addr == 0)\n\t\treturn 0;\n\n\tdst = htonl(addr);\n\tif (IN_CLASSA(dst))\n\t\treturn ntohl(IN_CLASSA_NET);\n\tif (IN_CLASSB(dst))\n\t\treturn ntohl(IN_CLASSB_NET);\n\tif (IN_CLASSC(dst))\n\t\treturn ntohl(IN_CLASSC_NET);\n\n\treturn 0;\n}\n\nstruct ipv4_addr *\nipv4_iffindaddr(struct interface *ifp, const struct in_addr *addr,\n    const struct in_addr *mask)\n{\n\tstruct ipv4_state *state;\n\tstruct ipv4_addr *ap;\n\n\tstate = IPV4_STATE(ifp);\n\tif (state) {\n\t\tTAILQ_FOREACH(ap, &state->addrs, next) {\n\t\t\tif ((addr == NULL || ap->addr.s_addr == addr->s_addr) &&\n\t\t\t    (mask == NULL || ap->mask.s_addr == mask->s_addr))\n\t\t\t\treturn ap;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstruct ipv4_addr *\nipv4_iffindlladdr(struct interface *ifp)\n{\n\tstruct ipv4_state *state;\n\tstruct ipv4_addr *ap;\n\n\tstate = IPV4_STATE(ifp);\n\tif (state) {\n\t\tTAILQ_FOREACH(ap, &state->addrs, next) {\n\t\t\tif (IN_LINKLOCAL(ntohl(ap->addr.s_addr)))\n\t\t\t\treturn ap;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic struct ipv4_addr *\nipv4_iffindmaskaddr(struct interface *ifp, const struct in_addr *addr)\n{\n\tstruct ipv4_state *state;\n\tstruct ipv4_addr *ap;\n\n\tstate = IPV4_STATE(ifp);\n\tif (state) {\n\t\tTAILQ_FOREACH(ap, &state->addrs, next) {\n\t\t\tif ((ap->addr.s_addr & ap->mask.s_addr) ==\n\t\t\t    (addr->s_addr & ap->mask.s_addr))\n\t\t\t\treturn ap;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic struct ipv4_addr *\nipv4_iffindmaskbrd(struct interface *ifp, const struct in_addr *addr)\n{\n\tstruct ipv4_state *state;\n\tstruct ipv4_addr *ap;\n\n\tstate = IPV4_STATE(ifp);\n\tif (state) {\n\t\tTAILQ_FOREACH(ap, &state->addrs, next) {\n\t\t\tif ((ap->brd.s_addr & ap->mask.s_addr) ==\n\t\t\t    (addr->s_addr & ap->mask.s_addr))\n\t\t\t\treturn ap;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstruct ipv4_addr *\nipv4_findaddr(struct dhcpcd_ctx *ctx, const struct in_addr *addr)\n{\n\tstruct interface *ifp;\n\tstruct ipv4_addr *ap;\n\n\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\tap = ipv4_iffindaddr(ifp, addr, NULL);\n\t\tif (ap)\n\t\t\treturn ap;\n\t}\n\treturn NULL;\n}\n\nstruct ipv4_addr *\nipv4_findmaskaddr(struct dhcpcd_ctx *ctx, const struct in_addr *addr)\n{\n\tstruct interface *ifp;\n\tstruct ipv4_addr *ap;\n\n\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\tap = ipv4_iffindmaskaddr(ifp, addr);\n\t\tif (ap)\n\t\t\treturn ap;\n\t}\n\treturn NULL;\n}\n\nstruct ipv4_addr *\nipv4_findmaskbrd(struct dhcpcd_ctx *ctx, const struct in_addr *addr)\n{\n\tstruct interface *ifp;\n\tstruct ipv4_addr *ap;\n\n\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\tap = ipv4_iffindmaskbrd(ifp, addr);\n\t\tif (ap)\n\t\t\treturn ap;\n\t}\n\treturn NULL;\n}\n\nint\nipv4_hasaddr(const struct interface *ifp)\n{\n\tconst struct dhcp_state *dstate;\n\n#ifdef IPV4LL\n\tif (IPV4LL_STATE_RUNNING(ifp))\n\t\treturn 1;\n#endif\n\n\tdstate = D_CSTATE(ifp);\n\treturn (dstate && dstate->added == STATE_ADDED && dstate->addr != NULL);\n}\n\n/* Interface comparer for working out ordering. */\nint\nipv4_ifcmp(const struct interface *si, const struct interface *ti)\n{\n\tconst struct dhcp_state *sis, *tis;\n\n\tsis = D_CSTATE(si);\n\ttis = D_CSTATE(ti);\n\tif (sis && !tis)\n\t\treturn -1;\n\tif (!sis && tis)\n\t\treturn 1;\n\tif (!sis && !tis)\n\t\treturn 0;\n\t/* If one has a lease and the other not, it takes precedence. */\n\tif (sis->new && !tis->new)\n\t\treturn -1;\n\tif (!sis->new && tis->new)\n\t\treturn 1;\n\t/* Always prefer proper leases */\n\tif (!(sis->added & STATE_FAKE) && (tis->added & STATE_FAKE))\n\t\treturn -1;\n\tif ((sis->added & STATE_FAKE) && !(tis->added & STATE_FAKE))\n\t\treturn 1;\n\t/* If we are either, they neither have a lease, or they both have.\n\t * We need to check for IPv4LL and make it non-preferred. */\n\tif (sis->new && tis->new) {\n\t\tif (IS_DHCP(sis->new) && !IS_DHCP(tis->new))\n\t\t\treturn -1;\n\t\tif (!IS_DHCP(sis->new) && IS_DHCP(tis->new))\n\t\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nstatic int\ninet_dhcproutes(rb_tree_t *routes, struct interface *ifp, bool *have_default)\n{\n\tconst struct dhcp_state *state;\n\trb_tree_t nroutes;\n\tstruct rt *rt, *r = NULL;\n\tstruct in_addr in;\n\tuint16_t mtu;\n\tint n;\n\n\tstate = D_CSTATE(ifp);\n\tif (state == NULL || !(state->added & STATE_ADDED))\n\t\treturn 0;\n\n\t/* An address does have to exist. */\n\tassert(state->addr);\n\n\trb_tree_init(&nroutes, &rt_compare_proto_ops);\n\n\t/* First, add a subnet route. */\n\tif (state->addr->mask.s_addr != INADDR_ANY\n#ifndef BSD\n\t    /* BSD adds a route in this instance */\n\t    && state->addr->mask.s_addr != INADDR_BROADCAST\n#endif\n\t) {\n\t\tif ((rt = rt_new(ifp)) == NULL)\n\t\t\treturn -1;\n\t\trt->rt_dflags |= RTDF_IFA_ROUTE;\n\t\tin.s_addr = state->addr->addr.s_addr & state->addr->mask.s_addr;\n\t\tsa_in_init(&rt->rt_dest, &in);\n\t\tin.s_addr = state->addr->mask.s_addr;\n\t\tsa_in_init(&rt->rt_netmask, &in);\n\t\t// in.s_addr = INADDR_ANY;\n\t\t// sa_in_init(&rt->rt_gateway, &in);\n\t\trt->rt_gateway.sa_family = AF_UNSPEC;\n\t\trt_proto_add(&nroutes, rt);\n\t}\n\n\t/* If any set routes, grab them, otherwise DHCP routes. */\n\tif (RB_TREE_MIN(&ifp->options->routes)) {\n\t\tRB_TREE_FOREACH(r, &ifp->options->routes)\n\t\t{\n\t\t\tif (sa_is_unspecified(&r->rt_gateway))\n\t\t\t\tbreak;\n\t\t\tif ((rt = rt_new0(ifp->ctx)) == NULL)\n\t\t\t\treturn -1;\n\t\t\tmemcpy(rt, r, sizeof(*rt));\n\t\t\trt_setif(rt, ifp);\n\t\t\trt->rt_dflags = RTDF_STATIC;\n\t\t\trt_proto_add(&nroutes, rt);\n\t\t}\n\t} else {\n\t\tif (dhcp_get_routes(&nroutes, ifp) == -1)\n\t\t\treturn -1;\n\t}\n\n\t/* If configured, install a gateway to the desintion\n\t * for P2P interfaces. */\n\tif (ifp->flags & IFF_POINTOPOINT &&\n\t    has_option_mask(ifp->options->dstmask, DHO_ROUTER)) {\n\t\tif ((rt = rt_new(ifp)) == NULL)\n\t\t\treturn -1;\n\t\tin.s_addr = INADDR_ANY;\n\t\tsa_in_init(&rt->rt_dest, &in);\n\t\tsa_in_init(&rt->rt_netmask, &in);\n\t\tsa_in_init(&rt->rt_gateway, &state->addr->brd);\n\t\tsa_in_init(&rt->rt_ifa, &state->addr->addr);\n\t\trt_proto_add(&nroutes, rt);\n\t}\n\n\t/* Copy our address as the source address and set mtu */\n\tmtu = dhcp_get_mtu(ifp);\n\tn = 0;\n\twhile ((rt = RB_TREE_MIN(&nroutes)) != NULL) {\n\t\trb_tree_remove_node(&nroutes, rt);\n\t\trt->rt_mtu = mtu;\n\t\tif (!(rt->rt_dflags & RTDF_STATIC))\n\t\t\trt->rt_dflags |= RTDF_DHCP;\n\t\tif (state->added & STATE_FAKE)\n\t\t\trt->rt_dflags |= RTDF_FAKE;\n\t\tsa_in_init(&rt->rt_ifa, &state->addr->addr);\n\t\tif (rb_tree_insert_node(routes, rt) != rt) {\n\t\t\trt_free(rt);\n\t\t\tcontinue;\n\t\t}\n\t\tif (rt_is_default(rt))\n\t\t\t*have_default = true;\n\t\tn = 1;\n\t}\n\n\treturn n;\n}\n\n/* We should check to ensure the routers are on the same subnet\n * OR supply a host route. If not, warn and add a host route. */\nstatic int\ninet_routerhostroute(rb_tree_t *routes, struct interface *ifp)\n{\n\tstruct rt *rt, *rth, *rtp;\n\tstruct sockaddr_in *dest, *netmask, *gateway;\n\tconst char *cp, *cp2, *cp3, *cplim;\n\tstruct if_options *ifo;\n\tconst struct dhcp_state *state;\n\tstruct in_addr in;\n\trb_tree_t troutes;\n\n\t/* Don't add a host route for these interfaces. */\n\tif (ifp->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))\n\t\treturn 0;\n\n\trb_tree_init(&troutes, &rt_compare_proto_ops);\n\n\tRB_TREE_FOREACH(rt, routes)\n\t{\n\t\tif (rt->rt_dest.sa_family != AF_INET)\n\t\t\tcontinue;\n\t\tif (!sa_is_unspecified(&rt->rt_dest) ||\n\t\t    sa_is_unspecified(&rt->rt_gateway))\n\t\t\tcontinue;\n\t\tgateway = satosin(&rt->rt_gateway);\n\t\t/* Scan for a route to match */\n\t\tRB_TREE_FOREACH(rth, routes)\n\t\t{\n\t\t\tif (rth == rt)\n\t\t\t\tbreak;\n\t\t\t/* match host */\n\t\t\tif (sa_cmp(&rth->rt_dest, &rt->rt_gateway) == 0)\n\t\t\t\tbreak;\n\t\t\t/* match subnet */\n\t\t\t/* XXX ADD TO RT_COMARE? XXX */\n\t\t\tcp = (const char *)&gateway->sin_addr.s_addr;\n\t\t\tdest = satosin(&rth->rt_dest);\n\t\t\tcp2 = (const char *)&dest->sin_addr.s_addr;\n\t\t\tnetmask = satosin(&rth->rt_netmask);\n\t\t\tcp3 = (const char *)&netmask->sin_addr.s_addr;\n\t\t\tcplim = cp3 + sizeof(netmask->sin_addr.s_addr);\n\t\t\twhile (cp3 < cplim) {\n\t\t\t\tif ((*cp++ ^ *cp2++) & *cp3++)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (cp3 == cplim)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (rth != rt)\n\t\t\tcontinue;\n\t\tif ((state = D_CSTATE(ifp)) == NULL)\n\t\t\tcontinue;\n\t\tifo = ifp->options;\n\t\tif (ifp->flags & IFF_NOARP) {\n\t\t\tif (!(ifo->options & DHCPCD_ROUTER_HOST_ROUTE_WARNED) &&\n\t\t\t    !(state->added & STATE_FAKE)) {\n\t\t\t\tchar buf[INET_MAX_ADDRSTRLEN];\n\n\t\t\t\tifo->options |= DHCPCD_ROUTER_HOST_ROUTE_WARNED;\n\t\t\t\tlogwarnx(\"%s: forcing router %s through \"\n\t\t\t\t\t \"interface\",\n\t\t\t\t    ifp->name,\n\t\t\t\t    sa_addrtop(&rt->rt_gateway, buf,\n\t\t\t\t\tsizeof(buf)));\n\t\t\t}\n\t\t\tgateway->sin_addr.s_addr = INADDR_ANY;\n\t\t\tcontinue;\n\t\t}\n\t\tif (!(ifo->options & DHCPCD_ROUTER_HOST_ROUTE_WARNED) &&\n\t\t    !(state->added & STATE_FAKE)) {\n\t\t\tchar buf[INET_MAX_ADDRSTRLEN];\n\n\t\t\tifo->options |= DHCPCD_ROUTER_HOST_ROUTE_WARNED;\n\t\t\tlogwarnx(\"%s: router %s requires a host route\",\n\t\t\t    ifp->name,\n\t\t\t    sa_addrtop(&rt->rt_gateway, buf, sizeof(buf)));\n\t\t}\n\n\t\tif ((rth = rt_new(ifp)) == NULL)\n\t\t\treturn -1;\n\t\trth->rt_flags |= RTF_HOST;\n\t\tsa_in_init(&rth->rt_dest, &gateway->sin_addr);\n\t\tin.s_addr = INADDR_BROADCAST;\n\t\tsa_in_init(&rth->rt_netmask, &in);\n\t\tin.s_addr = INADDR_ANY;\n\t\tsa_in_init(&rth->rt_gateway, &in);\n\t\trth->rt_mtu = dhcp_get_mtu(ifp);\n\t\tif (state->addr != NULL)\n\t\t\tsa_in_init(&rth->rt_ifa, &state->addr->addr);\n\t\telse\n\t\t\trth->rt_ifa.sa_family = AF_UNSPEC;\n\n\t\t/* We need to insert the host route just before the router. */\n\t\twhile ((rtp = RB_TREE_MAX(routes)) != NULL) {\n\t\t\trb_tree_remove_node(routes, rtp);\n\t\t\trt_proto_add(&troutes, rtp);\n\t\t\tif (rtp == rt)\n\t\t\t\tbreak;\n\t\t}\n\t\trt_proto_add(routes, rth);\n\t\t/* troutes is now reversed, so add backwards again. */\n\t\twhile ((rtp = RB_TREE_MAX(&troutes)) != NULL) {\n\t\t\trb_tree_remove_node(&troutes, rtp);\n\t\t\trt_proto_add(routes, rtp);\n\t\t}\n\t}\n\treturn 0;\n}\n\nbool\ninet_getroutes(struct dhcpcd_ctx *ctx, rb_tree_t *routes)\n{\n\tstruct interface *ifp;\n\tbool have_default = false;\n\n\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\tif (!ifp->active)\n\t\t\tcontinue;\n\t\tif (inet_dhcproutes(routes, ifp, &have_default) == -1)\n\t\t\treturn false;\n#ifdef IPV4LL\n\t\tif (ipv4ll_subnetroute(routes, ifp) == -1)\n\t\t\treturn false;\n#endif\n\t\tif (inet_routerhostroute(routes, ifp) == -1)\n\t\t\treturn false;\n\t}\n\n#ifdef IPV4LL\n\t/* If there is no default route, see if we can use an IPv4LL one. */\n\tif (have_default)\n\t\treturn true;\n\n\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\tif (ifp->active && ipv4ll_defaultroute(routes, ifp) == 1)\n\t\t\tbreak;\n\t}\n#endif\n\n\treturn true;\n}\n\nint\nipv4_deladdr(struct ipv4_addr *addr, int keeparp)\n{\n\tint r;\n\tstruct ipv4_state *state;\n\tstruct ipv4_addr *ap;\n\n\tassert(addr != NULL);\n\tlogdebugx(\"%s: deleting IP address %s\", addr->iface->name, addr->saddr);\n\n\tr = if_address(RTM_DELADDR, addr);\n\tif (r == -1 && errno != EADDRNOTAVAIL && errno != ESRCH &&\n\t    errno != ENXIO && errno != ENODEV)\n\t\tlogerr(\"%s: %s\", addr->iface->name, __func__);\n\n#ifdef ARP\n\tif (!keeparp)\n\t\tarp_freeaddr(addr->iface, &addr->addr);\n#else\n\tUNUSED(keeparp);\n#endif\n\n\tstate = IPV4_STATE(addr->iface);\n\tTAILQ_FOREACH(ap, &state->addrs, next) {\n\t\tif (IPV4_MASK_EQ(ap, addr)) {\n\t\t\tstruct dhcp_state *dstate;\n\n\t\t\tdstate = D_STATE(ap->iface);\n\t\t\tTAILQ_REMOVE(&state->addrs, ap, next);\n\t\t\tfree(ap);\n\n\t\t\tif (dstate && dstate->addr == ap) {\n\t\t\t\tdstate->added = 0;\n\t\t\t\tdstate->addr = NULL;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn r;\n}\n\nstruct ipv4_state *\nipv4_getstate(struct interface *ifp)\n{\n\tstruct ipv4_state *state;\n\n\tstate = IPV4_STATE(ifp);\n\tif (state == NULL) {\n\t\tifp->if_data[IF_DATA_IPV4] = malloc(sizeof(*state));\n\t\tstate = IPV4_STATE(ifp);\n\t\tif (state == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\treturn NULL;\n\t\t}\n\t\tTAILQ_INIT(&state->addrs);\n\t}\n\treturn state;\n}\n\n#ifdef ALIAS_ADDR\n/* Find the next logical aliase address we can use. */\nstatic int\nipv4_aliasaddr(struct ipv4_addr *ia, struct ipv4_addr **repl)\n{\n\tstruct ipv4_state *state;\n\tstruct ipv4_addr *iap;\n\tunsigned int lun;\n\tchar alias[IF_NAMESIZE];\n\n\tif (ia->alias[0] != '\\0')\n\t\treturn 0;\n\n\tlun = 0;\n\tstate = IPV4_STATE(ia->iface);\nfind_lun:\n\tif (if_makealias(alias, IF_NAMESIZE, ia->iface->name, lun) >=\n\t    IF_NAMESIZE) {\n\t\terrno = ENOMEM;\n\t\treturn -1;\n\t}\n\tTAILQ_FOREACH(iap, &state->addrs, next) {\n\t\tif (iap->alias[0] != '\\0' && iap->addr.s_addr == INADDR_ANY) {\n\t\t\t/* No address assigned? Lets use it. */\n\t\t\tstrlcpy(ia->alias, iap->alias, sizeof(ia->alias));\n\t\t\tif (repl)\n\t\t\t\t*repl = iap;\n\t\t\treturn 1;\n\t\t}\n\t\tif (strcmp(iap->alias, alias) == 0)\n\t\t\tbreak;\n\t}\n\n\tif (iap != NULL) {\n\t\tif (lun == UINT_MAX) {\n\t\t\terrno = ERANGE;\n\t\t\treturn -1;\n\t\t}\n\t\tlun++;\n\t\tgoto find_lun;\n\t}\n\n\tstrlcpy(ia->alias, alias, sizeof(ia->alias));\n\treturn 0;\n}\n#endif\n\nstruct ipv4_addr *\nipv4_addaddr(struct interface *ifp, const struct in_addr *addr,\n    const struct in_addr *mask, const struct in_addr *bcast, uint32_t vltime,\n    uint32_t pltime)\n{\n\tstruct ipv4_state *state;\n\tstruct ipv4_addr *ia;\n#ifdef ALIAS_ADDR\n\tint replaced, blank;\n\tstruct ipv4_addr *replaced_ia;\n#endif\n\n\tif ((state = ipv4_getstate(ifp)) == NULL) {\n\t\tlogerr(__func__);\n\t\treturn NULL;\n\t}\n\tif (ifp->options->options & DHCPCD_NOALIAS) {\n\t\tstruct ipv4_addr *ian;\n\n\t\tTAILQ_FOREACH_SAFE(ia, &state->addrs, next, ian) {\n\t\t\tif (ia->addr.s_addr != addr->s_addr)\n\t\t\t\tipv4_deladdr(ia, 0);\n\t\t}\n\t}\n\n\tia = ipv4_iffindaddr(ifp, addr, NULL);\n\tif (ia == NULL) {\n\t\tia = malloc(sizeof(*ia));\n\t\tif (ia == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\treturn NULL;\n\t\t}\n\t\tia->iface = ifp;\n\t\tia->addr = *addr;\n#ifdef IN_IFF_TENTATIVE\n\t\tia->addr_flags = IN_IFF_TENTATIVE;\n#endif\n\t\tia->flags = IPV4_AF_NEW;\n\t} else\n\t\tia->flags &= ~IPV4_AF_NEW;\n\n\tia->mask = *mask;\n\tia->brd = *bcast;\n#ifdef IP_LIFETIME\n\tif (ifp->options->options & DHCPCD_LASTLEASE_EXTEND) {\n\t\t/* We don't want the kernel to expire the address. */\n\t\tia->vltime = ia->pltime = DHCP_INFINITE_LIFETIME;\n\t} else {\n\t\tia->vltime = vltime;\n\t\tia->pltime = pltime;\n\t}\n#else\n\tUNUSED(vltime);\n\tUNUSED(pltime);\n#endif\n\tsnprintf(ia->saddr, sizeof(ia->saddr), \"%s/%d\", inet_ntoa(*addr),\n\t    inet_ntocidr(*mask));\n\n#ifdef ALIAS_ADDR\n\tblank = (ia->alias[0] == '\\0');\n\tif ((replaced = ipv4_aliasaddr(ia, &replaced_ia)) == -1) {\n\t\tlogerr(\"%s: ipv4_aliasaddr\", ifp->name);\n\t\tfree(ia);\n\t\treturn NULL;\n\t}\n\tif (blank)\n\t\tlogdebugx(\"%s: aliased %s\", ia->alias, ia->saddr);\n#endif\n\n\tlogdebugx(\"%s: adding IP address %s %s %s\", ifp->name, ia->saddr,\n\t    ifp->flags & IFF_POINTOPOINT ? \"destination\" : \"broadcast\",\n\t    inet_ntoa(*bcast));\n\tif (if_address(RTM_NEWADDR, ia) == -1) {\n\t\tif (errno != EEXIST)\n\t\t\tlogerr(\"%s: if_addaddress\", __func__);\n\t\tif (ia->flags & IPV4_AF_NEW)\n\t\t\tfree(ia);\n\t\treturn NULL;\n\t}\n\n#ifdef ALIAS_ADDR\n\tif (replaced) {\n\t\tTAILQ_REMOVE(&state->addrs, replaced_ia, next);\n\t\tfree(replaced_ia);\n\t}\n#endif\n\n\tif (ia->flags & IPV4_AF_NEW) {\n\t\tTAILQ_INSERT_TAIL(&state->addrs, ia, next);\n#if defined(ARP) && !defined(KERNEL_RFC5227)\n\t\tarp_ifannounceaddr(ifp, &ia->addr);\n#endif\n\t}\n\n\treturn ia;\n}\n\nstatic int\nipv4_daddaddr(struct interface *ifp, const struct dhcp_lease *lease)\n{\n\tstruct dhcp_state *state;\n\tstruct ipv4_addr *ia;\n\n\tia = ipv4_addaddr(ifp, &lease->addr, &lease->mask, &lease->brd,\n\t    lease->leasetime, lease->rebindtime);\n\tif (ia == NULL)\n\t\treturn -1;\n\n\tstate = D_STATE(ifp);\n\tstate->added = STATE_ADDED;\n\tstate->addr = ia;\n\treturn 0;\n}\n\nstruct ipv4_addr *\nipv4_applyaddr(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tstruct dhcp_state *state = D_STATE(ifp);\n\tstruct dhcp_lease *lease;\n\tstruct if_options *ifo = ifp->options;\n\tstruct ipv4_addr *ia, *old_ia;\n\n\tif (state == NULL)\n\t\treturn NULL;\n\n\tlease = &state->lease;\n\tif (state->new == NULL) {\n\t\tif ((ifo->options & (DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=\n\t\t    (DHCPCD_EXITING | DHCPCD_PERSISTENT)) {\n\t\t\tif (state->added) {\n\t\t\t\t/* Someone might have deleted our address */\n\t\t\t\tif (state->addr != NULL)\n\t\t\t\t\tipv4_deladdr(state->addr, 0);\n\t\t\t\trt_build(ifp->ctx, AF_INET);\n\t\t\t}\n\t\t\tscript_runreason(ifp, state->reason);\n\t\t} else\n\t\t\trt_build(ifp->ctx, AF_INET);\n\t\treturn NULL;\n\t}\n\n\t/* ipv4_dadaddr() will overwrite this, we need it to purge later */\n\told_ia = state->addr;\n\n\tia = ipv4_iffindaddr(ifp, &lease->addr, NULL);\n\t/* If the netmask or broadcast is different, re-add the addresss.\n\t * If IP addresses do not have lifetimes, there is a very real chance\n\t * that re-adding them will scrub the subnet route temporarily\n\t * which is a bad thing, so avoid it. */\n\tif (ia != NULL && ia->mask.s_addr == lease->mask.s_addr &&\n\t    ia->brd.s_addr == lease->brd.s_addr) {\n#ifndef IP_LIFETIME\n\t\tlogdebugx(\"%s: IP address %s already exists\", ifp->name,\n\t\t    ia->saddr);\n#endif\n\t} else {\n#ifdef __linux__\n\t\t/* Linux does not change netmask/broadcast address\n\t\t * for re-added addresses, so we need to delete the old one\n\t\t * first. */\n\t\tif (ia != NULL)\n\t\t\tipv4_deladdr(ia, 0);\n#endif\n#ifndef IP_LIFETIME\n\t\tif (ipv4_daddaddr(ifp, lease) == -1 && errno != EEXIST)\n\t\t\treturn NULL;\n#endif\n\t}\n#ifdef IP_LIFETIME\n\tif (ipv4_daddaddr(ifp, lease) == -1 && errno != EEXIST)\n\t\treturn NULL;\n#endif\n\n\tia = ipv4_iffindaddr(ifp, &lease->addr, NULL);\n\tif (ia == NULL) {\n\t\tlogerrx(\"%s: added address vanished\", ifp->name);\n\t\treturn NULL;\n\t}\n#if defined(ARP) && defined(IN_IFF_NOTUSEABLE)\n\tif (ia->addr_flags & IN_IFF_NOTUSEABLE)\n\t\treturn NULL;\n#endif\n\n\t/* Delete the old address if different */\n\tif (old_ia && old_ia->addr.s_addr != lease->addr.s_addr)\n\t\tipv4_deladdr(old_ia, 0);\n\n\tstate->addr = ia;\n\tstate->added = STATE_ADDED;\n\n\trt_build(ifp->ctx, AF_INET);\n\n\tif (state->state == DHS_BOUND) {\n\t\tscript_runreason(ifp, state->reason);\n\t\tdhcpcd_daemonise(ifp->ctx);\n\t}\n\treturn ia;\n}\n\nvoid\nipv4_markaddrsstale(struct interface *ifp)\n{\n\tstruct ipv4_state *state;\n\tstruct ipv4_addr *ia;\n\n\tstate = IPV4_STATE(ifp);\n\tif (state == NULL)\n\t\treturn;\n\n\tTAILQ_FOREACH(ia, &state->addrs, next) {\n\t\tia->flags |= IPV4_AF_STALE;\n\t}\n}\n\nvoid\nipv4_deletestaleaddrs(struct interface *ifp)\n{\n\tstruct ipv4_state *state;\n\tstruct ipv4_addr *ia, *ia1;\n\n\tstate = IPV4_STATE(ifp);\n\tif (state == NULL)\n\t\treturn;\n\n\tTAILQ_FOREACH_SAFE(ia, &state->addrs, next, ia1) {\n\t\tif (!(ia->flags & IPV4_AF_STALE))\n\t\t\tcontinue;\n\t\tipv4_handleifa(ifp->ctx, RTM_DELADDR, ifp->ctx->ifaces,\n\t\t    ifp->name, &ia->addr, &ia->mask, &ia->brd, 0, getpid());\n\t}\n}\n\nvoid\nipv4_handleifa(struct dhcpcd_ctx *ctx, int cmd, struct if_head *ifs,\n    const char *ifname, const struct in_addr *addr, const struct in_addr *mask,\n    const struct in_addr *brd, int addrflags, pid_t pid)\n{\n\tstruct interface *ifp;\n\tstruct ipv4_state *state;\n\tstruct ipv4_addr *ia;\n\tbool ia_is_new;\n\n#if 0\n\tchar sbrdbuf[INET_ADDRSTRLEN];\n\tconst char *sbrd;\n\n\tif (brd)\n\t\tsbrd = inet_ntop(AF_INET, brd, sbrdbuf, sizeof(sbrdbuf));\n\telse\n\t\tsbrd = NULL;\n\tlogdebugx(\"%s: %s %s/%d %s %d\", ifname,\n\t    cmd == RTM_NEWADDR ? \"RTM_NEWADDR\" :\n\t    cmd == RTM_DELADDR ? \"RTM_DELADDR\" : \"???\",\n\t    inet_ntoa(*addr), inet_ntocidr(*mask), sbrd, addrflags);\n#endif\n\n\tif (ifs == NULL)\n\t\tifs = ctx->ifaces;\n\tif (ifs == NULL) {\n\t\terrno = ESRCH;\n\t\treturn;\n\t}\n\tif ((ifp = if_find(ifs, ifname)) == NULL)\n\t\treturn;\n\tif ((state = ipv4_getstate(ifp)) == NULL) {\n\t\terrno = ENOENT;\n\t\treturn;\n\t}\n\n\tia = ipv4_iffindaddr(ifp, addr, NULL);\n\tswitch (cmd) {\n\tcase RTM_NEWADDR:\n\t\tif (ia == NULL) {\n\t\t\tif ((ia = malloc(sizeof(*ia))) == NULL) {\n\t\t\t\tlogerr(__func__);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tia->iface = ifp;\n\t\t\tia->addr = *addr;\n\t\t\tia->mask = *mask;\n\t\t\tia->flags = 0;\n\t\t\tia_is_new = true;\n#ifdef ALIAS_ADDR\n\t\t\tstrlcpy(ia->alias, ifname, sizeof(ia->alias));\n#endif\n\t\t\tTAILQ_INSERT_TAIL(&state->addrs, ia, next);\n\t\t} else\n\t\t\tia_is_new = false;\n\t\t/* Mask could have changed */\n\t\tif (ia_is_new ||\n\t\t    (mask->s_addr != INADDR_ANY &&\n\t\t\tmask->s_addr != ia->mask.s_addr)) {\n\t\t\tia->mask = *mask;\n\t\t\tsnprintf(ia->saddr, sizeof(ia->saddr), \"%s/%d\",\n\t\t\t    inet_ntoa(*addr), inet_ntocidr(*mask));\n\t\t}\n\t\tif (brd != NULL)\n\t\t\tia->brd = *brd;\n\t\telse\n\t\t\tia->brd.s_addr = INADDR_ANY;\n\t\tia->addr_flags = addrflags;\n\t\tia->flags &= ~IPV4_AF_STALE;\n\t\tbreak;\n\tcase RTM_DELADDR:\n\t\tif (ia == NULL)\n\t\t\treturn;\n\t\tif (mask->s_addr != INADDR_ANY &&\n\t\t    mask->s_addr != ia->mask.s_addr)\n\t\t\treturn;\n\t\tia->addr_flags = addrflags;\n\t\tTAILQ_REMOVE(&state->addrs, ia, next);\n\t\tbreak;\n\tdefault:\n\t\treturn;\n\t}\n\n\tif (addr->s_addr != INADDR_ANY && addr->s_addr != INADDR_BROADCAST) {\n\t\tia = dhcp_handleifa(cmd, ia, pid);\n#ifdef IPV4LL\n\t\tif (ia != NULL)\n\t\t\tipv4ll_handleifa(cmd, ia, pid);\n#endif\n\t}\n\n\tif (cmd == RTM_DELADDR)\n\t\tfree(ia);\n}\n\nvoid\nipv4_free(struct interface *ifp)\n{\n\tstruct ipv4_state *state;\n\tstruct ipv4_addr *ia;\n\n\tif (ifp == NULL || (state = IPV4_STATE(ifp)) == NULL)\n\t\treturn;\n\n\twhile ((ia = TAILQ_FIRST(&state->addrs))) {\n\t\tTAILQ_REMOVE(&state->addrs, ia, next);\n\t\tfree(ia);\n\t}\n\tfree(state);\n}\n"
  },
  {
    "path": "src/ipv4.h",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef IPV4_H\n#define IPV4_H\n\n#include \"dhcpcd.h\"\n\n/* Prefer our macro */\n#ifdef HTONL\n#undef HTONL\n#endif\n\n#ifndef BYTE_ORDER\n#define BIG_ENDIAN    1234\n#define LITTLE_ENDIAN 4321\n#if defined(_BIG_ENDIAN)\n#define BYTE_ORDER BIG_ENDIAN\n#elif defined(_LITTLE_ENDIAN)\n#define BYTE_ORDER LITTLE_ENDIAN\n#else\n#error Endian unknown\n#endif\n#endif\n\n#if BYTE_ORDER == BIG_ENDIAN\n#define HTONL(A) (A)\n#elif BYTE_ORDER == LITTLE_ENDIAN\n#define HTONL(A)                                  \\\n\t((((uint32_t)(A) & 0xff000000) >> 24) |   \\\n\t    (((uint32_t)(A) & 0x00ff0000) >> 8) | \\\n\t    (((uint32_t)(A) & 0x0000ff00) << 8) | \\\n\t    (((uint32_t)(A) & 0x000000ff) << 24))\n#endif /* BYTE_ORDER */\n\n#ifndef IPV4_MMTU\n#define IPV4_MMTU 68\n#endif\n\n#ifdef __sun\n/* Solaris lacks these defines.\n * While it supports DaD, to seems to only expose IFF_DUPLICATE\n * so we have no way of knowing if it's tentative or not.\n * I don't even know if Solaris has any special treatment for tentative. */\n#define IN_IFF_TENTATIVE  0x01\n#define IN_IFF_DUPLICATED 0x02\n#define IN_IFF_DETACHED\t  0x00\n#endif\n\n#ifdef IN_IFF_TENTATIVE\n#define IN_IFF_NOTUSEABLE \\\n\t(IN_IFF_TENTATIVE | IN_IFF_DUPLICATED | IN_IFF_DETACHED)\n#endif\n\n#define IN_ARE_ADDR_EQUAL(a, b)\t  ((a)->s_addr == (b)->s_addr)\n#define IN_IS_ADDR_UNSPECIFIED(a) ((a)->s_addr == INADDR_ANY)\n\n#ifdef __linux__\n#define IP_LIFETIME\n#endif\n\nstruct ipv4_addr {\n\tTAILQ_ENTRY(ipv4_addr) next;\n\tstruct in_addr addr;\n\tstruct in_addr mask;\n\tstruct in_addr brd;\n\tstruct interface *iface;\n\tint addr_flags;\n\tunsigned int flags;\n#ifdef IP_LIFETIME\n\tuint32_t vltime;\n\tuint32_t pltime;\n#endif\n\tchar saddr[INET_ADDRSTRLEN + 3];\n#ifdef ALIAS_ADDR\n\tchar alias[IF_NAMESIZE];\n#endif\n};\nTAILQ_HEAD(ipv4_addrhead, ipv4_addr);\n\n#define IPV4_AF_STALE\t      (1U << 0)\n#define IPV4_AF_NEW\t      (1U << 1)\n\n#define IPV4_ADDR_EQ(a1, a2)  ((a1) && (a1)->addr.s_addr == (a2)->addr.s_addr)\n#define IPV4_MASK1_EQ(a1, a2) ((a1) && (a1)->mask.s_addr == (a2)->mask.s_addr)\n#define IPV4_MASK_EQ(a1, a2)  (IPV4_ADDR_EQ(a1, a2) && IPV4_MASK1_EQ(a1, a2))\n#define IPV4_BRD1_EQ(a1, a2)  ((a1) && (a1)->brd.s_addr == (a2)->brd.s_addr)\n#define IPV4_BRD_EQ(a1, a2)   (IPV4_MASK_EQ(a1, a2) && IPV4_BRD1_EQ(a1, a2))\n\nstruct ipv4_state {\n\tstruct ipv4_addrhead addrs;\n};\n\n#define IPV4_STATE(ifp) ((struct ipv4_state *)(ifp)->if_data[IF_DATA_IPV4])\n#define IPV4_CSTATE(ifp) \\\n\t((const struct ipv4_state *)(ifp)->if_data[IF_DATA_IPV4])\n\n#ifdef INET\nstruct ipv4_state *ipv4_getstate(struct interface *);\nint ipv4_ifcmp(const struct interface *, const struct interface *);\nuint8_t inet_ntocidr(struct in_addr);\nint inet_cidrtoaddr(int, struct in_addr *);\nuint32_t ipv4_getnetmask(uint32_t);\nint ipv4_hasaddr(const struct interface *);\n\nbool inet_getroutes(struct dhcpcd_ctx *, rb_tree_t *);\n\n#define STATE_ADDED   0x01\n#define STATE_FAKE    0x02\n#define STATE_EXPIRED 0x04\n\nint ipv4_deladdr(struct ipv4_addr *, int);\nstruct ipv4_addr *ipv4_addaddr(struct interface *, const struct in_addr *,\n    const struct in_addr *, const struct in_addr *, uint32_t, uint32_t);\nstruct ipv4_addr *ipv4_applyaddr(void *);\n\nstruct ipv4_addr *ipv4_iffindaddr(struct interface *, const struct in_addr *,\n    const struct in_addr *);\nstruct ipv4_addr *ipv4_iffindlladdr(struct interface *);\nstruct ipv4_addr *ipv4_findaddr(struct dhcpcd_ctx *, const struct in_addr *);\nstruct ipv4_addr *ipv4_findmaskaddr(struct dhcpcd_ctx *,\n    const struct in_addr *);\nstruct ipv4_addr *ipv4_findmaskbrd(struct dhcpcd_ctx *, const struct in_addr *);\nvoid ipv4_markaddrsstale(struct interface *);\nvoid ipv4_deletestaleaddrs(struct interface *);\nvoid ipv4_handleifa(struct dhcpcd_ctx *, int, struct if_head *, const char *,\n    const struct in_addr *, const struct in_addr *, const struct in_addr *, int,\n    pid_t);\n\nvoid ipv4_free(struct interface *);\n#endif /* INET */\n\n#endif /* IPV4_H */\n"
  },
  {
    "path": "src/ipv4ll.c",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <arpa/inet.h>\n#include <assert.h>\n#include <errno.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#define ELOOP_QUEUE IPV4LL\n#include \"arp.h\"\n#include \"common.h\"\n#include \"config.h\"\n#include \"dhcp.h\"\n#include \"eloop.h\"\n#include \"if-options.h\"\n#include \"if.h\"\n#include \"ipv4.h\"\n#include \"ipv4ll.h\"\n#include \"logerr.h\"\n#include \"sa.h\"\n#include \"script.h\"\n\nstatic void ipv4ll_start_arp(void *arg);\n\nstatic const struct in_addr inaddr_llmask = { .s_addr = HTONL(LINKLOCAL_MASK) };\nstatic const struct in_addr inaddr_llbcast = { .s_addr = HTONL(\n\t\t\t\t\t\t   LINKLOCAL_BCAST) };\n\nstatic void\nipv4ll_pickaddr(struct interface *ifp)\n{\n\tstruct in_addr addr = { .s_addr = 0 };\n\tstruct ipv4ll_state *state;\n\n\tstate = IPV4LL_STATE(ifp);\n\tsetstate(state->randomstate);\n\n\tdo {\n\t\tlong r;\n\n\tagain:\n\t\t/* RFC 3927 Section 2.1 states that the first 256 and\n\t\t * last 256 addresses are reserved for future use.\n\t\t * See ipv4ll_start for why we don't use arc4random. */\n\t\t/* coverity[dont_call] */\n\t\tr = random();\n\t\taddr.s_addr = ntohl(\n\t\t    LINKLOCAL_ADDR | ((uint32_t)(r % 0xFD00) + 0x0100));\n\n\t\t/* No point using a failed address */\n\t\tif (IN_ARE_ADDR_EQUAL(&addr, &state->pickedaddr))\n\t\t\tgoto again;\n\t\t/* Ensure we don't have the address on another interface */\n\t} while (ipv4_findaddr(ifp->ctx, &addr) != NULL);\n\n\t/* Restore the original random state */\n\tsetstate(ifp->ctx->randomstate);\n\tstate->pickedaddr = addr;\n}\n\nint\nipv4ll_subnetroute(rb_tree_t *routes, struct interface *ifp)\n{\n\tstruct ipv4ll_state *state;\n\tstruct rt *rt;\n\tstruct in_addr in;\n\n\tassert(ifp != NULL);\n\tif ((state = IPV4LL_STATE(ifp)) == NULL || state->addr == NULL)\n\t\treturn 0;\n\n\tif ((rt = rt_new(ifp)) == NULL)\n\t\treturn -1;\n\n\tin.s_addr = state->addr->addr.s_addr & state->addr->mask.s_addr;\n\tsa_in_init(&rt->rt_dest, &in);\n\tin.s_addr = state->addr->mask.s_addr;\n\tsa_in_init(&rt->rt_netmask, &in);\n\tin.s_addr = INADDR_ANY;\n\tsa_in_init(&rt->rt_gateway, &in);\n\tsa_in_init(&rt->rt_ifa, &state->addr->addr);\n\trt->rt_dflags |= RTDF_IPV4LL;\n\treturn rt_proto_add(routes, rt) ? 1 : 0;\n}\n\nint\nipv4ll_defaultroute(rb_tree_t *routes, struct interface *ifp)\n{\n\tstruct ipv4ll_state *state;\n\tstruct rt *rt;\n\tstruct in_addr in;\n\n\tassert(ifp != NULL);\n\tif ((state = IPV4LL_STATE(ifp)) == NULL || state->addr == NULL)\n\t\treturn 0;\n\n\tif ((rt = rt_new(ifp)) == NULL)\n\t\treturn -1;\n\n\tin.s_addr = INADDR_ANY;\n\tsa_in_init(&rt->rt_dest, &in);\n\tsa_in_init(&rt->rt_netmask, &in);\n\tsa_in_init(&rt->rt_gateway, &in);\n\tsa_in_init(&rt->rt_ifa, &state->addr->addr);\n\trt->rt_dflags |= RTDF_IPV4LL;\n#ifdef HAVE_ROUTE_METRIC\n\trt->rt_metric += RTMETRIC_IPV4LL;\n#endif\n\treturn rt_proto_add(routes, rt) ? 1 : 0;\n}\n\nssize_t\nipv4ll_env(FILE *fp, const char *prefix, const struct interface *ifp)\n{\n\tconst struct ipv4ll_state *state;\n\tconst char *pf = prefix == NULL ? \"\" : \"_\";\n\tstruct in_addr netnum;\n\n\tassert(ifp != NULL);\n\tif ((state = IPV4LL_CSTATE(ifp)) == NULL || state->addr == NULL)\n\t\treturn 0;\n\n\t/* Emulate a DHCP environment */\n\tif (efprintf(fp, \"%s%sip_address=%s\", prefix, pf,\n\t\tinet_ntoa(state->addr->addr)) == -1)\n\t\treturn -1;\n\tif (efprintf(fp, \"%s%ssubnet_mask=%s\", prefix, pf,\n\t\tinet_ntoa(state->addr->mask)) == -1)\n\t\treturn -1;\n\tif (efprintf(fp, \"%s%ssubnet_cidr=%d\", prefix, pf,\n\t\tinet_ntocidr(state->addr->mask)) == -1)\n\t\treturn -1;\n\tif (efprintf(fp, \"%s%sbroadcast_address=%s\", prefix, pf,\n\t\tinet_ntoa(state->addr->brd)) == -1)\n\t\treturn -1;\n\tnetnum.s_addr = state->addr->addr.s_addr & state->addr->mask.s_addr;\n\tif (efprintf(fp, \"%s%snetwork_number=%s\", prefix, pf,\n\t\tinet_ntoa(netnum)) == -1)\n\t\treturn -1;\n\treturn 5;\n}\n\n#ifndef KERNEL_RFC5227\nstatic void\nipv4ll_announced_arp(struct arp_state *astate)\n{\n\tstruct ipv4ll_state *state = IPV4LL_STATE(astate->iface);\n\n\tstate->conflicts = 0;\n}\n\n/* This is the callback by ARP freeing */\nstatic void\nipv4ll_free_arp(struct arp_state *astate)\n{\n\tstruct ipv4ll_state *state;\n\n\tstate = IPV4LL_STATE(astate->iface);\n\tif (state != NULL && state->arp == astate)\n\t\tstate->arp = NULL;\n}\n\n/* This is us freeing any ARP state */\nstatic void\nipv4ll_freearp(struct interface *ifp)\n{\n\tstruct ipv4ll_state *state;\n\n\tstate = IPV4LL_STATE(ifp);\n\tif (state == NULL || state->arp == NULL)\n\t\treturn;\n\n\teloop_timeout_delete(ifp->ctx->eloop, NULL, state->arp);\n\tarp_free(state->arp);\n\tstate->arp = NULL;\n}\n#else\n#define ipv4ll_freearp(ifp)\n#endif\n\nstatic void\nipv4ll_not_found(struct interface *ifp)\n{\n\tstruct ipv4ll_state *state;\n\tstruct ipv4_addr *ia;\n\n\tstate = IPV4LL_STATE(ifp);\n\tia = ipv4_iffindaddr(ifp, &state->pickedaddr, &inaddr_llmask);\n#ifdef IN_IFF_NOTREADY\n\tif (ia == NULL || ia->addr_flags & IN_IFF_NOTREADY)\n#endif\n\t\tloginfox(\"%s: using IPv4LL address %s\", ifp->name,\n\t\t    inet_ntoa(state->pickedaddr));\n\tif (ia == NULL) {\n\t\tif (ifp->ctx->options & DHCPCD_TEST) {\n\t\t\tia = malloc(sizeof(*ia));\n\t\t\tif (ia == NULL) {\n\t\t\t\tlogerr(__func__);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tia->iface = ifp;\n\t\t\tia->addr = state->pickedaddr;\n\t\t} else if (!(ifp->options->options & DHCPCD_CONFIGURE)) {\n\t\t\tlogwarnx(\"%s: refusing to add IPv4LL address %s\",\n\t\t\t    ifp->name, inet_ntoa(state->pickedaddr));\n\t\t\treturn;\n\t\t}\n\t\tia = ipv4_addaddr(ifp, &state->pickedaddr, &inaddr_llmask,\n\t\t    &inaddr_llbcast, DHCP_INFINITE_LIFETIME,\n\t\t    DHCP_INFINITE_LIFETIME);\n\t}\n\tif (ia == NULL)\n\t\treturn;\n#ifdef IN_IFF_NOTREADY\n\tif (ia->addr_flags & IN_IFF_NOTREADY)\n\t\treturn;\n\tlogdebugx(\"%s: DAD completed for %s\", ifp->name, ia->saddr);\n#endif\n\n\tstate->addr = ia;\n\tstate->down = false;\n\tif (ifp->ctx->options & DHCPCD_TEST) {\n\t\tscript_runreason(ifp, \"TEST\");\n\t\teloop_exit(ifp->ctx->eloop, EXIT_SUCCESS);\n\t\treturn;\n\t}\n\trt_build(ifp->ctx, AF_INET);\n\n\tscript_runreason(ifp, \"IPV4LL\");\n\tdhcpcd_daemonise(ifp->ctx);\n}\n\nstatic void\nipv4ll_found(struct interface *ifp)\n{\n\tstruct ipv4ll_state *state = IPV4LL_STATE(ifp);\n\n\tipv4ll_freearp(ifp);\n\tif (++state->conflicts == MAX_CONFLICTS)\n\t\tlogerrx(\"%s: failed to acquire an IPv4LL address\", ifp->name);\n\tipv4ll_pickaddr(ifp);\n\teloop_timeout_add_sec(ifp->ctx->eloop,\n\t    state->conflicts >= MAX_CONFLICTS ? RATE_LIMIT_INTERVAL :\n\t\t\t\t\t\tPROBE_WAIT,\n\t    ipv4ll_start_arp, ifp);\n}\n\nstatic void\nipv4ll_defend_failed(struct interface *ifp)\n{\n\tstruct ipv4ll_state *state = IPV4LL_STATE(ifp);\n\n\tipv4ll_freearp(ifp);\n\tif (ifp->options->options & DHCPCD_CONFIGURE)\n\t\tipv4_deladdr(state->addr, 1);\n\tstate->addr = NULL;\n\trt_build(ifp->ctx, AF_INET);\n\tscript_runreason(ifp, \"IPV4LL\");\n\tipv4ll_pickaddr(ifp);\n\tipv4ll_start_arp(ifp);\n}\n\n#ifndef KERNEL_RFC5227\nstatic void\nipv4ll_not_found_arp(struct arp_state *astate)\n{\n\tipv4ll_not_found(astate->iface);\n}\n\nstatic void\nipv4ll_found_arp(struct arp_state *astate, __unused const struct arp_msg *amsg)\n{\n\tipv4ll_found(astate->iface);\n}\n\nstatic void\nipv4ll_defend_failed_arp(struct arp_state *astate)\n{\n\tipv4ll_defend_failed(astate->iface);\n}\n#endif\n\nvoid\nipv4ll_start(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tstruct ipv4ll_state *state;\n\tstruct ipv4_addr *ia;\n\tbool repick;\n\n\tif ((state = IPV4LL_STATE(ifp)) == NULL) {\n\t\tifp->if_data[IF_DATA_IPV4LL] = calloc(1, sizeof(*state));\n\t\tif ((state = IPV4LL_STATE(ifp)) == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (state->running)\n\t\treturn;\n\tstate->running = true;\n\n\t/* RFC 3927 Section 2.1 states that the random number generator\n\t * SHOULD be seeded with a value derived from persistent information\n\t * such as the IEEE 802 MAC address so that it usually picks\n\t * the same address without persistent storage. */\n\tif (!state->seeded) {\n\t\tunsigned int seed;\n\t\tchar *orig;\n\n\t\tif (sizeof(seed) > ifp->hwlen) {\n\t\t\tseed = 0;\n\t\t\tmemcpy(&seed, ifp->hwaddr, ifp->hwlen);\n\t\t} else\n\t\t\tmemcpy(&seed, ifp->hwaddr + ifp->hwlen - sizeof(seed),\n\t\t\t    sizeof(seed));\n\t\t/* coverity[dont_call] */\n\t\torig = initstate(seed, state->randomstate,\n\t\t    sizeof(state->randomstate));\n\n\t\t/* Save the original state. */\n\t\tif (ifp->ctx->randomstate == NULL)\n\t\t\tifp->ctx->randomstate = orig;\n\n\t\t/* Set back the original state until we need the seeded one. */\n\t\tsetstate(ifp->ctx->randomstate);\n\t\tstate->seeded = true;\n\t}\n\n\t/* Find the previosuly used address. */\n\tif (state->pickedaddr.s_addr != INADDR_ANY)\n\t\tia = ipv4_iffindaddr(ifp, &state->pickedaddr, NULL);\n\telse\n\t\tia = NULL;\n\n\t/* Find an existing IPv4LL address and ensure we can work with it. */\n\tif (ia == NULL)\n\t\tia = ipv4_iffindlladdr(ifp);\n\n\trepick = false;\n#ifdef IN_IFF_DUPLICATED\n\tif (ia != NULL && ia->addr_flags & IN_IFF_DUPLICATED) {\n\t\tstate->pickedaddr = ia->addr; /* So it's not picked again. */\n\t\trepick = true;\n\t\tif (ifp->options->options & DHCPCD_CONFIGURE)\n\t\t\tipv4_deladdr(ia, 0);\n\t\tia = NULL;\n\t}\n#endif\n\n\tstate->addr = ia;\n\tstate->down = true;\n\tif (ia != NULL) {\n\t\tstate->pickedaddr = ia->addr;\n#ifdef IN_IFF_TENTATIVE\n\t\tif (ia->addr_flags & (IN_IFF_TENTATIVE | IN_IFF_DETACHED)) {\n\t\t\tloginfox(\"%s: waiting for DAD to complete on %s\",\n\t\t\t    ifp->name, inet_ntoa(ia->addr));\n\t\t\treturn;\n\t\t}\n#endif\n#ifdef IN_IFF_DUPLICATED\n\t\tloginfox(\"%s: using IPv4LL address %s\", ifp->name, ia->saddr);\n#endif\n\t} else if (ifp->options->options & DHCPCD_CONFIGURE) {\n\t\tloginfox(\"%s: probing for an IPv4LL address\", ifp->name);\n\t\tif (repick || state->pickedaddr.s_addr == INADDR_ANY)\n\t\t\tipv4ll_pickaddr(ifp);\n\t} else {\n\t\tlogwarnx(\"%s: refusing to configure IPv4LL\", ifp->name);\n\t\treturn;\n\t}\n\n\tipv4ll_start_arp(ifp);\n}\n\nstatic void\nipv4ll_start_arp(void *arg)\n{\n\tstruct interface *ifp = arg;\n#ifdef KERNEL_RFC5227\n\tipv4ll_not_found(ifp);\n#else\n\tstruct ipv4ll_state *state;\n\tstruct arp_state *astate;\n\n\tstate = IPV4LL_STATE(ifp);\n\n\tipv4ll_freearp(ifp);\n\tstate->arp = astate = arp_new(ifp, &state->pickedaddr);\n\tif (state->arp == NULL)\n\t\treturn;\n\n\tastate->found_cb = ipv4ll_found_arp;\n\tastate->not_found_cb = ipv4ll_not_found_arp;\n\tastate->announced_cb = ipv4ll_announced_arp;\n\tastate->defend_failed_cb = ipv4ll_defend_failed_arp;\n\tastate->free_cb = ipv4ll_free_arp;\n\tarp_probe(astate);\n#endif\n}\n\nvoid\nipv4ll_drop(struct interface *ifp)\n{\n\tstruct ipv4ll_state *state;\n\tbool dropped = false;\n\tstruct ipv4_state *istate;\n\n\tassert(ifp != NULL);\n\n\teloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);\n\tipv4ll_freearp(ifp);\n\n\tif ((ifp->options->options & DHCPCD_NODROP) == DHCPCD_NODROP)\n\t\tgoto free;\n\n\tstate = IPV4LL_STATE(ifp);\n\tif (state) {\n\t\tstate->running = false;\n\t\tif (state->addr != NULL) {\n\t\t\tif (ifp->options->options & DHCPCD_CONFIGURE)\n\t\t\t\tipv4_deladdr(state->addr, 1);\n\t\t\tstate->addr = NULL;\n\t\t\tdropped = true;\n\t\t}\n\t}\n\n\t/* Free any other link local addresses that might exist. */\n\tif ((istate = IPV4_STATE(ifp)) != NULL) {\n\t\tstruct ipv4_addr *ia, *ian;\n\n\t\tTAILQ_FOREACH_SAFE(ia, &istate->addrs, next, ian) {\n\t\t\tif (IN_LINKLOCAL(ntohl(ia->addr.s_addr))) {\n\t\t\t\tif (ifp->options->options & DHCPCD_CONFIGURE)\n\t\t\t\t\tipv4_deladdr(ia, 0);\n\t\t\t\tdropped = true;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (dropped) {\n\t\trt_build(ifp->ctx, AF_INET);\n\t\tscript_runreason(ifp, \"IPV4LL\");\n\t}\n\nfree:\n\tipv4ll_free(ifp);\n\tdhcpcd_dropped(ifp);\n}\n\nvoid\nipv4ll_reset(struct interface *ifp)\n{\n\tstruct ipv4ll_state *state = IPV4LL_STATE(ifp);\n\n\tif (state == NULL)\n\t\treturn;\n\tipv4ll_freearp(ifp);\n\tstate->pickedaddr.s_addr = INADDR_ANY;\n\tstate->seeded = false;\n}\n\nvoid\nipv4ll_free(struct interface *ifp)\n{\n\tassert(ifp != NULL);\n\n\tipv4ll_freearp(ifp);\n\tfree(IPV4LL_STATE(ifp));\n\tifp->if_data[IF_DATA_IPV4LL] = NULL;\n}\n\n/* This may cause issues in BSD systems, where running as a single dhcpcd\n * daemon would solve this issue easily. */\n#ifdef HAVE_ROUTE_METRIC\nint\nipv4ll_recvrt(__unused int cmd, const struct rt *rt)\n{\n\tstruct dhcpcd_ctx *ctx;\n\tstruct interface *ifp;\n\n\t/* Only interested in default route changes. */\n\tif (sa_is_unspecified(&rt->rt_dest))\n\t\treturn 0;\n\n\t/* If any interface is running IPv4LL, rebuild our routing table. */\n\tctx = rt->rt_ifp->ctx;\n\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\tif (IPV4LL_STATE_RUNNING(ifp)) {\n\t\t\trt_build(ctx, AF_INET);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn 0;\n}\n#endif\n\nstruct ipv4_addr *\nipv4ll_handleifa(int cmd, struct ipv4_addr *ia, pid_t pid)\n{\n\tstruct interface *ifp;\n\tstruct ipv4ll_state *state;\n\n\tifp = ia->iface;\n\tstate = IPV4LL_STATE(ifp);\n\tif (state == NULL)\n\t\treturn ia;\n\n\tif (cmd == RTM_DELADDR && state->addr != NULL &&\n\t    IN_ARE_ADDR_EQUAL(&state->addr->addr, &ia->addr)) {\n\t\tloginfox(\"%s: pid %d deleted IP address %s\", ifp->name,\n\t\t    (int)pid, ia->saddr);\n\t\tipv4ll_defend_failed(ifp);\n\t\treturn ia;\n\t}\n\n#ifdef IN_IFF_DUPLICATED\n\tif (cmd != RTM_NEWADDR)\n\t\treturn ia;\n\tif (!IN_ARE_ADDR_EQUAL(&state->pickedaddr, &ia->addr))\n\t\treturn ia;\n\tif (!(ia->addr_flags & IN_IFF_NOTUSEABLE))\n\t\tipv4ll_not_found(ifp);\n\telse if (ia->addr_flags & IN_IFF_DUPLICATED) {\n\t\tlogerrx(\"%s: DAD detected %s\", ifp->name, ia->saddr);\n\t\tipv4ll_freearp(ifp);\n\t\tif (ifp->options->options & DHCPCD_CONFIGURE)\n\t\t\tipv4_deladdr(ia, 1);\n\t\tstate->addr = NULL;\n\t\trt_build(ifp->ctx, AF_INET);\n\t\tipv4ll_found(ifp);\n\t\treturn NULL;\n\t}\n#endif\n\n\treturn ia;\n}\n"
  },
  {
    "path": "src/ipv4ll.h",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef IPV4LL_H\n#define IPV4LL_H\n\n#define LINKLOCAL_ADDR\t0xa9fe0000\n#define LINKLOCAL_MASK\tIN_CLASSB_NET\n#define LINKLOCAL_BCAST (LINKLOCAL_ADDR | ~LINKLOCAL_MASK)\n\n#ifndef IN_LINKLOCAL\n#define IN_LINKLOCAL(addr) ((addr & IN_CLASSB_NET) == LINKLOCAL_ADDR)\n#endif\n\n#ifdef IPV4LL\n#include \"arp.h\"\n\nstruct ipv4ll_state {\n\tstruct in_addr pickedaddr;\n\tstruct ipv4_addr *addr;\n\tchar randomstate[128];\n\tbool running;\n\tbool seeded;\n\tbool down;\n\tsize_t conflicts;\n#ifndef KERNEL_RFC5227\n\tstruct arp_state *arp;\n#endif\n};\n\n#define IPV4LL_STATE(ifp) \\\n\t((struct ipv4ll_state *)(ifp)->if_data[IF_DATA_IPV4LL])\n#define IPV4LL_CSTATE(ifp) \\\n\t((const struct ipv4ll_state *)(ifp)->if_data[IF_DATA_IPV4LL])\n#define IPV4LL_STATE_RUNNING(ifp)                               \\\n\t(IPV4LL_CSTATE((ifp)) && !IPV4LL_CSTATE((ifp))->down && \\\n\t    (IPV4LL_CSTATE((ifp))->addr != NULL))\n\nint ipv4ll_subnetroute(rb_tree_t *, struct interface *);\nint ipv4ll_defaultroute(rb_tree_t *, struct interface *);\nssize_t ipv4ll_env(FILE *, const char *, const struct interface *);\nvoid ipv4ll_start(void *);\nvoid ipv4ll_claimed(void *);\nvoid ipv4ll_handle_failure(void *);\nstruct ipv4_addr *ipv4ll_handleifa(int, struct ipv4_addr *, pid_t pid);\n#ifdef HAVE_ROUTE_METRIC\nint ipv4ll_recvrt(int, const struct rt *);\n#endif\n\nvoid ipv4ll_reset(struct interface *);\nvoid ipv4ll_drop(struct interface *);\nvoid ipv4ll_free(struct interface *);\n#endif\n\n#endif\n"
  },
  {
    "path": "src/ipv6.c",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/types.h>\n#include <sys/param.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n\n#include <net/if.h>\n#include <net/route.h>\n#include <netinet/in.h>\n#include <netinet/if_ether.h>\n\n#include <arpa/inet.h>\n\n#include \"config.h\"\n\n#ifdef HAVE_SYS_BITOPS_H\n#include <sys/bitops.h>\n#else\n#include \"compat/bitops.h\"\n#endif\n\n#ifdef BSD\n/* Purely for the ND6_IFF_AUTO_LINKLOCAL #define which is solely used\n * to generate our CAN_ADD_LLADDR #define. */\n#include <netinet6/in6_var.h>\n#include <netinet6/nd6.h>\n#endif\n\n#include <errno.h>\n#include <ifaddrs.h>\n#include <inttypes.h>\n#include <stdlib.h>\n#include <string.h>\n#include <syslog.h>\n#include <unistd.h>\n\n#define ELOOP_QUEUE ELOOP_IPV6\n#include \"common.h\"\n#include \"dhcp6.h\"\n#include \"dhcpcd.h\"\n#include \"eloop.h\"\n#include \"if.h\"\n#include \"ipv6.h\"\n#include \"ipv6nd.h\"\n#include \"logerr.h\"\n#include \"privsep.h\"\n#include \"sa.h\"\n#include \"script.h\"\n\n#ifdef HAVE_MD5_H\n#ifndef DEPGEN\n#include <md5.h>\n#endif\n#endif\n\n#ifdef SHA2_H\n#include SHA2_H\n#endif\n\n#ifndef SHA256_DIGEST_LENGTH\n#define SHA256_DIGEST_LENGTH 32\n#endif\n\n#ifdef IPV6_POLLADDRFLAG\n#warning kernel does not report IPv6 address flag changes\n#endif\n\n/* Hackery at it's finest. */\n#ifndef s6_addr32\n#ifdef __sun\n#define s6_addr32 _S6_un._S6_u32\n#else\n#define s6_addr32 __u6_addr.__u6_addr32\n#endif\n#endif\n\n#if defined(HAVE_IN6_ADDR_GEN_MODE_NONE) || defined(ND6_IFF_AUTO_LINKLOCAL) || \\\n    defined(IFF_NOLINKLOCAL)\n/* Only add the LL address if we have a carrier, so DaD works. */\n#define CAN_ADD_LLADDR(ifp) \\\n\t(!((ifp)->options->options & DHCPCD_LINK) || if_is_link_up((ifp)))\n#ifdef __sun\n/* Although we can add our own LL address, we cannot drop it\n * without unplumbing the if which is a lot of code.\n * So just keep it for the time being. */\n#define CAN_DROP_LLADDR(ifp) (0)\n#else\n#define CAN_DROP_LLADDR(ifp) (1)\n#endif\n#else\n/* We have no control over the OS adding the LLADDR, so just let it do it\n * as we cannot force our own view on it. */\n#define CAN_ADD_LLADDR(ifp)  (0)\n#define CAN_DROP_LLADDR(ifp) (0)\n#endif\n\n#ifdef IPV6_MANAGETEMPADDR\nstatic void ipv6_regentempaddr(void *);\n#endif\n\nint\nipv6_init(struct dhcpcd_ctx *ctx)\n{\n\tif (ctx->ra_routers != NULL)\n\t\treturn 0;\n\n\tctx->ra_routers = malloc(sizeof(*ctx->ra_routers));\n\tif (ctx->ra_routers == NULL)\n\t\treturn -1;\n\tTAILQ_INIT(ctx->ra_routers);\n\n#ifndef __sun\n\tctx->nd_fd = -1;\n#endif\n#ifdef DHCP6\n\tctx->dhcp6_rfd = -1;\n\tctx->dhcp6_wfd = -1;\n#endif\n\treturn 0;\n}\n\nstatic ssize_t\nipv6_readsecret(struct dhcpcd_ctx *ctx)\n{\n\tchar line[1024];\n\tunsigned char *p;\n\tsize_t len;\n\tuint32_t r;\n\n\tctx->secret_len = dhcp_read_hwaddr_aton(ctx, &ctx->secret, SECRET);\n\tif (ctx->secret_len != 0)\n\t\treturn (ssize_t)ctx->secret_len;\n\n\tif (errno != ENOENT)\n\t\tlogerr(\"%s: cannot read secret\", __func__);\n\n\t/* Chaining arc4random should be good enough.\n\t * RFC7217 section 5.1 states the key SHOULD be at least 128 bits.\n\t * To attempt and future proof ourselves, we'll generate a key of\n\t * 512 bits (64 bytes). */\n\tif (ctx->secret_len < 64) {\n\t\tif ((ctx->secret = malloc(64)) == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\treturn -1;\n\t\t}\n\t\tctx->secret_len = 64;\n\t}\n\tp = ctx->secret;\n\tfor (len = 0; len < 512 / NBBY; len += sizeof(r)) {\n\t\tr = arc4random();\n\t\tmemcpy(p, &r, sizeof(r));\n\t\tp += sizeof(r);\n\t}\n\n\thwaddr_ntoa(ctx->secret, ctx->secret_len, line, sizeof(line));\n\tlen = strlen(line);\n\tif (len < sizeof(line) - 2) {\n\t\tline[len++] = '\\n';\n\t\tline[len] = '\\0';\n\t}\n\tif (dhcp_writefile(ctx, SECRET, S_IRUSR, line, len) == -1) {\n\t\tlogerr(\"%s: cannot write secret\", __func__);\n\t\tctx->secret_len = 0;\n\t\treturn -1;\n\t}\n\treturn (ssize_t)ctx->secret_len;\n}\n\n/* http://www.iana.org/assignments/ipv6-interface-ids/ipv6-interface-ids.xhtml\n * RFC5453 */\nstatic const struct reslowhigh {\n\tconst uint8_t high[8];\n\tconst uint8_t low[8];\n} reslowhigh[] = {\n\t/* RFC4291 + RFC6543 */\n\t{ { 0x02, 0x00, 0x5e, 0xff, 0xfe, 0x00, 0x00, 0x00 },\n\t    { 0x02, 0x00, 0x5e, 0xff, 0xfe, 0xff, 0xff, 0xff } },\n\t/* RFC2526 */\n\t{ { 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80 },\n\t    { 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }\n};\n\nstatic bool\nipv6_reserved(const struct in6_addr *addr)\n{\n\tuint64_t id, low, high;\n\tsize_t i;\n\tconst struct reslowhigh *r;\n\n\tid = be64dec(addr->s6_addr + sizeof(id));\n\tif (id == 0) /* RFC4291 */\n\t\treturn 1;\n\tfor (i = 0; i < __arraycount(reslowhigh); i++) {\n\t\tr = &reslowhigh[i];\n\t\tlow = be64dec(r->low);\n\t\thigh = be64dec(r->high);\n\t\tif (id >= low && id <= high)\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\nstatic int\nipv6_makehwaddr(struct in6_addr *addr, const struct in6_addr *prefix,\n    int prefix_len, const struct interface *ifp)\n{\n\tif (prefix_len > 64) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tmemcpy(addr->s6_addr, prefix->s6_addr, 8);\n\tswitch (ifp->hwtype) {\n\tcase ARPHRD_ETHER:\n\t\tif (ifp->hwlen == 6) {\n\t\t\taddr->s6_addr[8] = ifp->hwaddr[0];\n\t\t\taddr->s6_addr[9] = ifp->hwaddr[1];\n\t\t\taddr->s6_addr[10] = ifp->hwaddr[2];\n\t\t\taddr->s6_addr[11] = 0xff;\n\t\t\taddr->s6_addr[12] = 0xfe;\n\t\t\taddr->s6_addr[13] = ifp->hwaddr[3];\n\t\t\taddr->s6_addr[14] = ifp->hwaddr[4];\n\t\t\taddr->s6_addr[15] = ifp->hwaddr[5];\n\t\t} else if (ifp->hwlen == 8)\n\t\t\tmemcpy(&addr->s6_addr[8], ifp->hwaddr, 8);\n\t\telse {\n\t\t\terrno = ENOTSUP;\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\terrno = ENOTSUP;\n\t\treturn -1;\n\t}\n\n\t/* Sanity check: g bit must not indciate \"group\" */\n\tif (EUI64_GROUP(addr)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\tEUI64_TO_IFID(addr);\n\treturn 0;\n}\n\n/* RFC7217 */\nstatic int\nipv6_makestableprivate1(struct dhcpcd_ctx *ctx, struct in6_addr *addr,\n    const struct in6_addr *prefix, int prefix_len,\n    const unsigned char *netiface, size_t netiface_len,\n    const unsigned char *netid, size_t netid_len, unsigned short vlanid,\n    uint32_t *dad_counter)\n{\n\tunsigned char buf[2048], *p, digest[SHA256_DIGEST_LENGTH];\n\tsize_t len, l;\n\tSHA256_CTX sha_ctx;\n\n\tif (prefix_len < 0 || prefix_len > 120) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tif (ctx->secret_len == 0) {\n\t\tif (ipv6_readsecret(ctx) == -1)\n\t\t\treturn -1;\n\t}\n\n\tl = (size_t)(ROUNDUP8(prefix_len) / NBBY);\n\tlen = l + netiface_len + netid_len + sizeof(*dad_counter) +\n\t    ctx->secret_len;\n\tif (vlanid != 0)\n\t\tlen += sizeof(vlanid);\n\tif (len > sizeof(buf)) {\n\t\terrno = ENOBUFS;\n\t\treturn -1;\n\t}\n\n\tfor (;; (*dad_counter)++) {\n\t\t/* Combine all parameters into one buffer */\n\t\tp = buf;\n\t\tmemcpy(p, prefix, l);\n\t\tp += l;\n\t\tmemcpy(p, netiface, netiface_len);\n\t\tp += netiface_len;\n\t\tmemcpy(p, netid, netid_len);\n\t\tp += netid_len;\n\t\t/* Don't use a vlanid if not set.\n\t\t * This ensures prior versions have the same unique address. */\n\t\tif (vlanid != 0) {\n\t\t\tmemcpy(p, &vlanid, sizeof(vlanid));\n\t\t\tp += sizeof(vlanid);\n\t\t}\n\t\tmemcpy(p, dad_counter, sizeof(*dad_counter));\n\t\tp += sizeof(*dad_counter);\n\t\tmemcpy(p, ctx->secret, ctx->secret_len);\n\n\t\t/* Make an address using the digest of the above.\n\t\t * RFC7217 Section 5.1 states that we shouldn't use MD5.\n\t\t * Pity as we use that for HMAC-MD5 which is still deemed OK.\n\t\t * SHA-256 is recommended */\n\t\tSHA256_Init(&sha_ctx);\n\t\tSHA256_Update(&sha_ctx, buf, len);\n\t\tSHA256_Final(digest, &sha_ctx);\n\n\t\tp = addr->s6_addr;\n\t\tmemcpy(p, prefix, l);\n\t\t/* RFC7217 section 5.2 says we need to start taking the id from\n\t\t * the least significant bit */\n\t\tlen = sizeof(addr->s6_addr) - l;\n\t\tmemcpy(p + l, digest + (sizeof(digest) - len), len);\n\n\t\t/* Ensure that the Interface ID does not match a reserved one,\n\t\t * if it does then treat it as a DAD failure.\n\t\t * RFC7217 section 5.2 */\n\t\tif (prefix_len != 64)\n\t\t\tbreak;\n\t\tif (!ipv6_reserved(addr))\n\t\t\tbreak;\n\t}\n\n\treturn 0;\n}\n\nint\nipv6_makestableprivate(struct in6_addr *addr, const struct in6_addr *prefix,\n    int prefix_len, const struct interface *ifp, int *dad_counter)\n{\n\tuint32_t dad;\n\tint r;\n\n\tdad = (uint32_t)*dad_counter;\n\n\t/* For our implementation, we shall set the hardware address\n\t * as the interface identifier */\n\tr = ipv6_makestableprivate1(ifp->ctx, addr, prefix, prefix_len,\n\t    ifp->hwaddr, ifp->hwlen, ifp->ssid, ifp->ssid_len, ifp->vlanid,\n\t    &dad);\n\n\tif (r == 0)\n\t\t*dad_counter = (int)dad;\n\treturn r;\n}\n\n#ifdef IPV6_AF_TEMPORARY\nstatic int\nipv6_maketemporaryaddress(struct in6_addr *addr, const struct in6_addr *prefix,\n    int prefix_len, const struct interface *ifp)\n{\n\tstruct in6_addr mask;\n\tstruct interface *ifpn;\n\n\tif (ipv6_mask(&mask, prefix_len) == -1)\n\t\treturn -1;\n\t*addr = *prefix;\n\nagain:\n\taddr->s6_addr32[2] |= (arc4random() & ~mask.s6_addr32[2]);\n\taddr->s6_addr32[3] |= (arc4random() & ~mask.s6_addr32[3]);\n\n\tTAILQ_FOREACH(ifpn, ifp->ctx->ifaces, next) {\n\t\tif (ipv6_iffindaddr(ifpn, addr, 0) != NULL)\n\t\t\tbreak;\n\t}\n\tif (ifpn != NULL)\n\t\tgoto again;\n\tif (ipv6_reserved(addr))\n\t\tgoto again;\n\treturn 0;\n}\n#endif\n\nstatic int\nipv6_makeprefix(struct in6_addr *prefix, const struct in6_addr *addr, int len)\n{\n\tstruct in6_addr mask;\n\tsize_t i;\n\n\tif (ipv6_mask(&mask, len) == -1)\n\t\treturn -1;\n\t*prefix = *addr;\n\tfor (i = 0; i < sizeof(prefix->s6_addr); i++)\n\t\tprefix->s6_addr[i] &= mask.s6_addr[i];\n\treturn 0;\n}\n\nint\nipv6_mask(struct in6_addr *mask, int len)\n{\n\tstatic const unsigned char masks[NBBY] = { 0x80, 0xc0, 0xe0, 0xf0, 0xf8,\n\t\t0xfc, 0xfe, 0xff };\n\tint bytes, bits, i;\n\n\tif (len < 0 || len > 128) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tmemset(mask, 0, sizeof(*mask));\n\tbytes = len / NBBY;\n\tbits = len % NBBY;\n\tfor (i = 0; i < bytes; i++)\n\t\tmask->s6_addr[i] = 0xff;\n\tif (bits != 0) {\n\t\t/* Coverify false positive.\n\t\t * bytelen cannot be 16 if bitlen is non zero */\n\t\t/* coverity[overrun-local] */\n\t\tmask->s6_addr[bytes] = masks[bits - 1];\n\t}\n\treturn 0;\n}\n\nuint8_t\nipv6_prefixlen(const struct in6_addr *mask)\n{\n\tint x = 0, y;\n\tconst unsigned char *lim, *p;\n\n\tlim = (const unsigned char *)mask + sizeof(*mask);\n\tfor (p = (const unsigned char *)mask; p < lim; x++, p++) {\n\t\tif (*p != 0xff)\n\t\t\tbreak;\n\t}\n\ty = 0;\n\tif (p < lim) {\n\t\tfor (y = 0; y < NBBY; y++) {\n\t\t\tif ((*p & (0x80 >> y)) == 0)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/*\n\t * when the limit pointer is given, do a stricter check on the\n\t * remaining bits.\n\t */\n\tif (p < lim) {\n\t\tif (y != 0 && (*p & (0x00ff >> y)) != 0)\n\t\t\treturn 0;\n\t\tfor (p = p + 1; p < lim; p++)\n\t\t\tif (*p != 0)\n\t\t\t\treturn 0;\n\t}\n\n\treturn (uint8_t)(x * NBBY + y);\n}\n\nint\nipv6_makeaddr(struct in6_addr *addr, struct interface *ifp,\n    const struct in6_addr *prefix, int prefix_len, unsigned int flags)\n{\n\tconst struct ipv6_addr *ap;\n\tconst struct if_options *ifo = ifp->options;\n\tint dad;\n\n\tif (prefix_len < 0 || prefix_len > 120) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n#ifdef IPV6_AF_TEMPORARY\n\tif (flags & IPV6_AF_TEMPORARY)\n\t\treturn ipv6_maketemporaryaddress(addr, prefix, prefix_len, ifp);\n#else\n\tUNUSED(flags);\n#endif\n\n\tif (ifo->options & DHCPCD_SLAACPRIVATE) {\n\t\tdad = 0;\n\t\tif (ipv6_makestableprivate(addr, prefix, prefix_len, ifp,\n\t\t\t&dad) == -1)\n\t\t\treturn -1;\n\t\treturn dad;\n\t} else if (!IN6_IS_ADDR_UNSPECIFIED(&ifo->token)) {\n\t\tint bytes = prefix_len / NBBY;\n\t\tint bits = prefix_len % NBBY;\n\n\t\t// Copy the token into the address.\n\t\t*addr = ifo->token;\n\n\t\t// If we have any dangling bits, just copy that in also.\n\t\t// XXX Can we preserve part of the token still?\n\t\tif (bits != 0)\n\t\t\tbytes++;\n\n\t\t// Copy the prefix in.\n\t\tif (bytes > 0)\n\t\t\tmemcpy(addr->s6_addr, prefix->s6_addr, (size_t)bytes);\n\t\treturn 0;\n\t}\n\n\tif (prefix_len > 64) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\t/* If we don't have a hardware address, then use the first link-local\n\t * address to base it on. */\n\tif (ifp->hwlen == 0) {\n\t\tif ((ap = ipv6_linklocal(ifp)) == NULL) {\n\t\t\t/* We delay a few functions until we get a local-link\n\t\t\t * address so this should never be hit. */\n\t\t\terrno = ENOENT;\n\t\t\treturn -1;\n\t\t}\n\n\t\t/* Make the address from the first local-link address */\n\t\tmemcpy(addr, prefix, sizeof(*prefix));\n\t\taddr->s6_addr32[2] = ap->addr.s6_addr32[2];\n\t\taddr->s6_addr32[3] = ap->addr.s6_addr32[3];\n\t\treturn 0;\n\t}\n\n\treturn ipv6_makehwaddr(addr, prefix, prefix_len, ifp);\n}\n\nstatic void\nin6_to_h64(uint64_t *vhigh, uint64_t *vlow, const struct in6_addr *addr)\n{\n\t*vhigh = be64dec(addr->s6_addr);\n\t*vlow = be64dec(addr->s6_addr + 8);\n}\n\nstatic void\nh64_to_in6(struct in6_addr *addr, uint64_t vhigh, uint64_t vlow)\n{\n\tbe64enc(addr->s6_addr, vhigh);\n\tbe64enc(addr->s6_addr + 8, vlow);\n}\n\nint\nipv6_userprefix(const struct in6_addr *prefix, // prefix from router\n    short prefix_len,\t\t\t       // length of prefix received\n    uint64_t user_number,\t\t       // \"random\" number from user\n    struct in6_addr *result,\t\t       // resultant prefix\n    short result_len)\t\t\t       // desired prefix length\n{\n\tuint64_t vh, vl, user_low, user_high;\n\n\tif (prefix_len < 1 || prefix_len > 128 || result_len < 1 ||\n\t    result_len > 128) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\t/* Check that the user_number fits inside result_len less prefix_len */\n\tif (result_len < prefix_len ||\n\t    fls64(user_number) > result_len - prefix_len) {\n\t\terrno = ERANGE;\n\t\treturn -1;\n\t}\n\n\t/* If user_number is zero, just copy the prefix into the result. */\n\tif (user_number == 0) {\n\t\t*result = *prefix;\n\t\treturn 0;\n\t}\n\n\t/* Shift user_number so it fit's just inside result_len.\n\t * Shifting by 0 or sizeof(user_number) is undefined,\n\t * so we cater for that. */\n\tif (result_len == 128) {\n\t\tuser_high = 0;\n\t\tuser_low = user_number;\n\t} else if (result_len > 64) {\n\t\tif (prefix_len >= 64)\n\t\t\tuser_high = 0;\n\t\telse\n\t\t\tuser_high = user_number >> (result_len - prefix_len);\n\t\tuser_low = user_number << (128 - result_len);\n\t} else if (result_len == 64) {\n\t\tuser_high = user_number;\n\t\tuser_low = 0;\n\t} else {\n\t\tuser_high = user_number << (64 - result_len);\n\t\tuser_low = 0;\n\t}\n\n\t/* convert to two 64bit host order values */\n\tin6_to_h64(&vh, &vl, prefix);\n\n\tvh |= user_high;\n\tvl |= user_low;\n\n\t/* copy back result */\n\th64_to_in6(result, vh, vl);\n\n\treturn 0;\n}\n\n#ifdef IPV6_POLLADDRFLAG\nvoid\nipv6_checkaddrflags(void *arg)\n{\n\tstruct ipv6_addr *ia;\n\tint flags;\n\tconst char *alias;\n\n\tia = arg;\n#ifdef ALIAS_ADDR\n\talias = ia->alias;\n#else\n\talias = NULL;\n#endif\n\tif ((flags = if_addrflags6(ia->iface, &ia->addr, alias)) == -1) {\n\t\tif (errno != EEXIST && errno != EADDRNOTAVAIL)\n\t\t\tlogerr(\"%s: if_addrflags6\", __func__);\n\t\treturn;\n\t}\n\n\tif (!(flags & IN6_IFF_TENTATIVE)) {\n\t\t/* Simulate the kernel announcing the new address. */\n\t\tipv6_handleifa(ia->iface->ctx, RTM_NEWADDR,\n\t\t    ia->iface->ctx->ifaces, ia->iface->name, &ia->addr,\n\t\t    ia->prefix_len, &ia->dstaddr, flags, 0);\n\t} else {\n\t\t/* Still tentative? Check again in a bit. */\n\t\teloop_timeout_add_msec(ia->iface->ctx->eloop, RETRANS_TIMER / 2,\n\t\t    ipv6_checkaddrflags, ia);\n\t}\n}\n#endif\n\nstatic void\nipv6_deletedaddr(struct ipv6_addr *ia)\n{\n#ifdef DHCP6\n#ifdef PRIVSEP\n\tif (!(ia->iface->ctx->options & DHCPCD_MANAGER))\n\t\tps_inet_closedhcp6(ia);\n#endif\n#ifndef SMALL\n\t/* NOREJECT is set if we delegated exactly the prefix to another\n\t * address.\n\t * This can only be one address, so just clear the flag.\n\t * This should ensure the reject route will be restored. */\n\tif (ia->delegating_prefix != NULL)\n\t\tia->delegating_prefix->flags &= ~IPV6_AF_NOREJECT;\n#endif\n#endif\n\n#if !defined(DHCP6) || (!defined(PRIVSEP) && defined(SMALL))\n\tUNUSED(ia);\n#endif\n}\n\nvoid\nipv6_deleteaddr(struct ipv6_addr *ia)\n{\n\tstruct ipv6_state *state;\n\tstruct ipv6_addr *ap;\n\n\tloginfox(\"%s: deleting address %s\", ia->iface->name, ia->saddr);\n\tif (if_address6(RTM_DELADDR, ia) == -1 && errno != EADDRNOTAVAIL &&\n\t    errno != ESRCH && errno != ENXIO && errno != ENODEV)\n\t\tlogerr(__func__);\n\n\tipv6_deletedaddr(ia);\n\n\tstate = IPV6_STATE(ia->iface);\n\tTAILQ_FOREACH(ap, &state->addrs, next) {\n\t\tif (IN6_ARE_ADDR_EQUAL(&ap->addr, &ia->addr)) {\n\t\t\tTAILQ_REMOVE(&state->addrs, ap, next);\n\t\t\tipv6_freeaddr(ap);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nstatic struct ipv6_state *\nipv6_getstate(struct interface *ifp)\n{\n\tstruct ipv6_state *state;\n\n\tstate = IPV6_STATE(ifp);\n\tif (state == NULL) {\n\t\tifp->if_data[IF_DATA_IPV6] = calloc(1, sizeof(*state));\n\t\tstate = IPV6_STATE(ifp);\n\t\tif (state == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\treturn NULL;\n\t\t}\n\t\tTAILQ_INIT(&state->addrs);\n\t\tTAILQ_INIT(&state->ll_callbacks);\n\t}\n\treturn state;\n}\n\nstatic int\nipv6_addaddr1(struct ipv6_addr *ia, struct timespec *now)\n{\n\tstruct interface *ifp;\n\tuint32_t pltime, vltime;\n\tint loglevel;\n\tstruct ipv6_addr *iaf;\n\n#ifdef __sun\n\t/* If we re-add then address on Solaris then the prefix\n\t * route will be scrubbed and re-added. Something might\n\t * be using it, so let's avoid it. */\n\tif (ia->flags & IPV6_AF_DADCOMPLETED) {\n\t\tlogdebugx(\"%s: IP address %s already exists\", ia->iface->name,\n\t\t    ia->saddr);\n\t\treturn 0;\n\t}\n#endif\n\n\t/* Remember the interface of the address. */\n\tifp = ia->iface;\n\n\t/* Find any existing address. */\n\tiaf = ipv6_iffindaddr(ifp, &ia->addr, 0);\n\tif (iaf != NULL && !(iaf->addr_flags & IN6_IFF_NOTUSEABLE))\n\t\tia->flags |= IPV6_AF_DADCOMPLETED;\n\n\t/* Adjust plftime and vltime based on acquired time */\n\tpltime = ia->prefix_pltime;\n\tvltime = ia->prefix_vltime;\n\n\tif (ifp->options->options & DHCPCD_LASTLEASE_EXTEND) {\n\t\t/* We don't want the kernel to expire the address.\n\t\t * The saved times will be re-applied to the ia\n\t\t * before exiting this function. */\n\t\tia->prefix_vltime = ia->prefix_pltime = ND6_INFINITE_LIFETIME;\n\t} else if (timespecisset(&ia->acquired)) {\n\t\tia->prefix_pltime = lifetime_left(ia->prefix_pltime,\n\t\t    &ia->acquired, now);\n\t\tia->prefix_vltime = lifetime_left(ia->prefix_vltime,\n\t\t    &ia->acquired, now);\n\t}\n\n\tloglevel = ia->flags & IPV6_AF_NEW ? LOG_INFO : LOG_DEBUG;\n\tlogmessage(loglevel, \"%s: adding %saddress %s\", ifp->name,\n#ifdef IPV6_AF_TEMPORARY\n\t    ia->flags & IPV6_AF_TEMPORARY ? \"temporary \" : \"\",\n#else\n\t    \"\",\n#endif\n\t    ia->saddr);\n\tif (ia->prefix_pltime == ND6_INFINITE_LIFETIME &&\n\t    ia->prefix_vltime == ND6_INFINITE_LIFETIME)\n\t\tlogdebugx(\"%s: pltime infinity, vltime infinity\", ifp->name);\n\telse if (ia->prefix_pltime == ND6_INFINITE_LIFETIME)\n\t\tlogdebugx(\"%s: pltime infinity, vltime %\" PRIu32 \" seconds\",\n\t\t    ifp->name, ia->prefix_vltime);\n\telse if (ia->prefix_vltime == ND6_INFINITE_LIFETIME)\n\t\tlogdebugx(\"%s: pltime %\" PRIu32 \"seconds, vltime infinity\",\n\t\t    ifp->name, ia->prefix_pltime);\n\telse\n\t\tlogdebugx(\"%s: pltime %\" PRIu32 \" seconds, vltime %\" PRIu32\n\t\t\t  \" seconds\",\n\t\t    ifp->name, ia->prefix_pltime, ia->prefix_vltime);\n\n\tif (if_address6(RTM_NEWADDR, ia) == -1) {\n\t\tlogerr(__func__);\n\t\t/* Restore real pltime and vltime */\n\t\tia->prefix_pltime = pltime;\n\t\tia->prefix_vltime = vltime;\n\t\treturn -1;\n\t}\n\n#ifdef IPV6_MANAGETEMPADDR\n\t/* RFC4941 Section 3.4 */\n\tif (ia->flags & IPV6_AF_TEMPORARY && ia->prefix_pltime &&\n\t    ia->prefix_vltime && ifp->options->options & DHCPCD_SLAACTEMP)\n\t\teloop_timeout_add_sec(ifp->ctx->eloop,\n\t\t    ia->prefix_pltime - REGEN_ADVANCE, ipv6_regentempaddr, ia);\n#endif\n\n\t/* Restore real pltime and vltime */\n\tia->prefix_pltime = pltime;\n\tia->prefix_vltime = vltime;\n\n\tia->flags &= ~IPV6_AF_NEW;\n\tia->flags |= IPV6_AF_ADDED;\n#ifndef SMALL\n\tif (ia->delegating_prefix != NULL)\n\t\tia->flags |= IPV6_AF_DELEGATED;\n#endif\n\n#ifdef IPV6_POLLADDRFLAG\n\teloop_timeout_delete(ifp->ctx->eloop, ipv6_checkaddrflags, ia);\n\tif (!(ia->flags & IPV6_AF_DADCOMPLETED)) {\n\t\teloop_timeout_add_msec(ifp->ctx->eloop, RETRANS_TIMER / 2,\n\t\t    ipv6_checkaddrflags, ia);\n\t}\n#endif\n\n\t/* Take a copy of the address and add it to our state if\n\t * it does not exist.\n\t * This is important if route overflow loses the message. */\n\tif (iaf == NULL) {\n\t\tstruct ipv6_state *state = ipv6_getstate(ifp);\n\n\t\tif ((iaf = malloc(sizeof(*iaf))) == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\treturn 0; /* Well, we did add the address */\n\t\t}\n\t\tmemcpy(iaf, ia, sizeof(*iaf));\n\t\tTAILQ_INSERT_TAIL(&state->addrs, iaf, next);\n\t}\n\n\treturn 0;\n}\n\n#ifdef ALIAS_ADDR\n/* Find the next logical alias address we can use. */\nstatic int\nipv6_aliasaddr(struct ipv6_addr *ia, struct ipv6_addr **repl)\n{\n\tstruct ipv6_state *state;\n\tstruct ipv6_addr *iap;\n\tunsigned int lun;\n\tchar alias[IF_NAMESIZE];\n\n\tif (ia->alias[0] != '\\0')\n\t\treturn 0;\n\tstate = IPV6_STATE(ia->iface);\n\n\t/* First find an existng address.\n\t * This can happen when dhcpcd restarts as ND and DHCPv6\n\t * maintain their own lists of addresses. */\n\tTAILQ_FOREACH(iap, &state->addrs, next) {\n\t\tif (iap->alias[0] != '\\0' &&\n\t\t    IN6_ARE_ADDR_EQUAL(&iap->addr, &ia->addr)) {\n\t\t\tstrlcpy(ia->alias, iap->alias, sizeof(ia->alias));\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tlun = 0;\nfind_unit:\n\tif (if_makealias(alias, IF_NAMESIZE, ia->iface->name, lun) >=\n\t    IF_NAMESIZE) {\n\t\terrno = ENOMEM;\n\t\treturn -1;\n\t}\n\tTAILQ_FOREACH(iap, &state->addrs, next) {\n\t\tif (iap->alias[0] == '\\0')\n\t\t\tcontinue;\n\t\tif (IN6_IS_ADDR_UNSPECIFIED(&iap->addr)) {\n\t\t\t/* No address assigned? Lets use it. */\n\t\t\tstrlcpy(ia->alias, iap->alias, sizeof(ia->alias));\n\t\t\tif (repl)\n\t\t\t\t*repl = iap;\n\t\t\treturn 1;\n\t\t}\n\t\tif (strcmp(iap->alias, alias) == 0)\n\t\t\tbreak;\n\t}\n\n\tif (iap != NULL) {\n\t\tif (lun == UINT_MAX) {\n\t\t\terrno = ERANGE;\n\t\t\treturn -1;\n\t\t}\n\t\tlun++;\n\t\tgoto find_unit;\n\t}\n\n\tstrlcpy(ia->alias, alias, sizeof(ia->alias));\n\treturn 0;\n}\n#endif\n\nint\nipv6_addaddr(struct ipv6_addr *ia, struct timespec *now)\n{\n\tint r;\n#ifdef ALIAS_ADDR\n\tint replaced, blank;\n\tstruct ipv6_addr *replaced_ia;\n\n\tblank = (ia->alias[0] == '\\0');\n\tif ((replaced = ipv6_aliasaddr(ia, &replaced_ia)) == -1)\n\t\treturn -1;\n\tif (blank)\n\t\tlogdebugx(\"%s: aliased %s\", ia->alias, ia->saddr);\n#endif\n\n\tif ((r = ipv6_addaddr1(ia, now)) == 0) {\n#ifdef ALIAS_ADDR\n\t\tif (replaced) {\n\t\t\tstruct ipv6_state *state;\n\n\t\t\tstate = IPV6_STATE(ia->iface);\n\t\t\tTAILQ_REMOVE(&state->addrs, replaced_ia, next);\n\t\t\tipv6_freeaddr(replaced_ia);\n\t\t}\n#endif\n\t}\n\treturn r;\n}\n\nint\nipv6_findaddrmatch(const struct ipv6_addr *addr, const struct in6_addr *match,\n    unsigned int flags)\n{\n\tif (match == NULL) {\n\t\tif ((addr->flags & (IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED)) ==\n\t\t    (IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED))\n\t\t\treturn 1;\n\t} else if (addr->prefix_vltime &&\n\t    IN6_ARE_ADDR_EQUAL(&addr->addr, match) &&\n\t    (!flags || addr->flags & flags))\n\t\treturn 1;\n\n\treturn 0;\n}\n\nstruct ipv6_addr *\nipv6_findaddr(struct dhcpcd_ctx *ctx, const struct in6_addr *addr,\n    unsigned int flags)\n{\n\tstruct ipv6_addr *nap;\n#ifdef DHCP6\n\tstruct ipv6_addr *dap;\n#endif\n\n\tnap = ipv6nd_findaddr(ctx, addr, flags);\n#ifdef DHCP6\n\tdap = dhcp6_findaddr(ctx, addr, flags);\n\tif (!dap && !nap)\n\t\treturn NULL;\n\tif (dap && !nap)\n\t\treturn dap;\n\tif (nap && !dap)\n\t\treturn nap;\n\tif (nap->iface->metric < dap->iface->metric)\n\t\treturn nap;\n\treturn dap;\n#else\n\treturn nap;\n#endif\n}\n\nint\nipv6_doaddr(struct ipv6_addr *ia, struct timespec *now)\n{\n\t/* A delegated prefix is not an address. */\n\tif (ia->flags & IPV6_AF_PFXDELEGATION)\n\t\treturn 0;\n\n\tif (ia->prefix_vltime == 0) {\n\t\tif (ia->flags & IPV6_AF_ADDED)\n\t\t\tipv6_deleteaddr(ia);\n\t\teloop_q_timeout_delete(ia->iface->ctx->eloop, ELOOP_QUEUE_ALL,\n\t\t    NULL, ia);\n\t\tif (ia->flags & IPV6_AF_REQUEST) {\n\t\t\tia->flags &= ~IPV6_AF_ADDED;\n\t\t\treturn 0;\n\t\t}\n\t\treturn -1;\n\t}\n\n\tif (ia->flags & IPV6_AF_STALE || IN6_IS_ADDR_UNSPECIFIED(&ia->addr))\n\t\treturn 0;\n\n\tipv6_addaddr(ia, now);\n\treturn ia->flags & IPV6_AF_NEW ? 1 : 0;\n}\n\nssize_t\nipv6_addaddrs(struct ipv6_addrhead *iaddrs)\n{\n\tstruct timespec now;\n\tstruct ipv6_addr *ia, *ian;\n\tssize_t i, r;\n\n\ti = 0;\n\ttimespecclear(&now);\n\tTAILQ_FOREACH_SAFE(ia, iaddrs, next, ian) {\n\t\tr = ipv6_doaddr(ia, &now);\n\t\tif (r != 0)\n\t\t\ti++;\n\t\tif (r == -1) {\n\t\t\tTAILQ_REMOVE(iaddrs, ia, next);\n\t\t\tipv6_freeaddr(ia);\n\t\t}\n\t}\n\treturn i;\n}\n\nvoid\nipv6_freeaddr(struct ipv6_addr *ia)\n{\n\tstruct eloop *eloop = ia->iface->ctx->eloop;\n#ifndef SMALL\n\tstruct ipv6_addr *iad;\n\n\t/* Forget the reference */\n\tif (ia->flags & IPV6_AF_PFXDELEGATION) {\n\t\tTAILQ_FOREACH(iad, &ia->pd_pfxs, pd_next) {\n\t\t\tiad->delegating_prefix = NULL;\n\t\t}\n\t} else if (ia->delegating_prefix != NULL) {\n\t\tTAILQ_REMOVE(&ia->delegating_prefix->pd_pfxs, ia, pd_next);\n\t}\n#endif\n\n\tif (ia->dhcp6_fd != -1) {\n\t\tclose(ia->dhcp6_fd);\n\t\teloop_event_delete(eloop, ia->dhcp6_fd);\n\t}\n\n\teloop_q_timeout_delete(eloop, ELOOP_QUEUE_ALL, NULL, ia);\n\tfree(ia->na);\n\tfree(ia);\n}\n\nvoid\nipv6_freedrop_addrs(struct ipv6_addrhead *addrs, int drop,\n    unsigned int notflags, const struct interface *ifd)\n{\n\tstruct ipv6_addr *ap, *apn, *apf;\n\tstruct timespec now;\n\n#ifdef SMALL\n\tUNUSED(ifd);\n#endif\n\ttimespecclear(&now);\n\tTAILQ_FOREACH_SAFE(ap, addrs, next, apn) {\n\t\tif (ap->flags & notflags)\n\t\t\tcontinue;\n#ifndef SMALL\n\t\tif (ifd != NULL &&\n\t\t    (ap->delegating_prefix == NULL ||\n\t\t\tap->delegating_prefix->iface != ifd))\n\t\t\tcontinue;\n#endif\n\t\tif (drop != 2)\n\t\t\tTAILQ_REMOVE(addrs, ap, next);\n\t\tif (drop && ap->flags & IPV6_AF_ADDED &&\n\t\t    (ap->iface->options->options &\n\t\t\t(DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=\n\t\t\t(DHCPCD_EXITING | DHCPCD_PERSISTENT)) {\n\t\t\t/* Don't drop link-local addresses. */\n\t\t\tif (!IN6_IS_ADDR_LINKLOCAL(&ap->addr) ||\n\t\t\t    CAN_DROP_LLADDR(ap->iface)) {\n\t\t\t\tif (drop == 2)\n\t\t\t\t\tTAILQ_REMOVE(addrs, ap, next);\n\t\t\t\t/* Find the same address somewhere else */\n\t\t\t\tapf = ipv6_findaddr(ap->iface->ctx, &ap->addr,\n\t\t\t\t    0);\n\t\t\t\tif ((apf == NULL || (apf->iface != ap->iface)))\n\t\t\t\t\tipv6_deleteaddr(ap);\n\t\t\t\tif (!(ap->iface->options->options &\n\t\t\t\t\tDHCPCD_EXITING) &&\n\t\t\t\t    apf)\n\t\t\t\t\tipv6_addaddr(apf, &now);\n\t\t\t\tif (drop == 2)\n\t\t\t\t\tipv6_freeaddr(ap);\n\t\t\t}\n\t\t}\n\t\tif (drop != 2)\n\t\t\tipv6_freeaddr(ap);\n\t}\n}\n\nstatic struct ipv6_addr *\nipv6_ifanyglobal(struct interface *ifp)\n{\n\tstruct ipv6_state *state;\n\tstruct ipv6_addr *ia;\n\n\tstate = IPV6_STATE(ifp);\n\tif (state == NULL)\n\t\treturn NULL;\n\n\tTAILQ_FOREACH(ia, &state->addrs, next) {\n\t\tif (IN6_IS_ADDR_LINKLOCAL(&ia->addr) ||\n\t\t    IN6_IS_ADDR_LOOPBACK(&ia->addr))\n\t\t\tcontinue;\n\t\t/* Let's be optimistic.\n\t\t * Any decent OS won't forward or accept traffic\n\t\t * from/to tentative or detached addresses. */\n\t\tif (!(ia->addr_flags & IN6_IFF_DUPLICATED))\n\t\t\treturn ia;\n\t}\n\n\treturn NULL;\n}\n\nstruct ipv6_addr *\nipv6_anyglobal(struct interface *sifp)\n{\n\tstruct ipv6_addr *ia;\n\tstruct interface *ifp;\n\n\t/*\n\t * IPv6 source address selection will prefer the outgoing interface,\n\t * but will also use any other interface if it things the address is\n\t * a better fit for the destination.\n\t * This logic is pretty much baked into all kernels and you\n\t * don't need to be a router either.\n\t * We only have this logic to work around badly configured IPv6\n\t * setups where there is a default router, but you're not handed\n\t * a reachable address. This results in network timeouts which we\n\t * want to actively avoid.\n\t */\n\tTAILQ_FOREACH(ifp, sifp->ctx->ifaces, next) {\n\t\tia = ipv6_ifanyglobal(ifp);\n\t\tif (ia != NULL)\n\t\t\treturn ia;\n\t}\n\treturn NULL;\n}\n\nvoid\nipv6_handleifa(struct dhcpcd_ctx *ctx, int cmd, struct if_head *ifs,\n    const char *ifname, const struct in6_addr *addr, uint8_t prefix_len,\n    const struct in6_addr *dstaddr, int addrflags, pid_t pid)\n{\n\tstruct interface *ifp;\n\tstruct ipv6_state *state;\n\tstruct ipv6_addr *ia;\n\tstruct ll_callback *cb;\n\tbool anyglobal;\n\n#ifdef __sun\n\tstruct sockaddr_in6 subnet;\n\n\t/* Solaris on-link route is an unspecified address! */\n\tif (IN6_IS_ADDR_UNSPECIFIED(addr)) {\n\t\tif (if_getsubnet(ctx, ifname, AF_INET6, &subnet,\n\t\t\tsizeof(subnet)) == -1) {\n\t\t\tlogerr(__func__);\n\t\t\treturn;\n\t\t}\n\t\taddr = &subnet.sin6_addr;\n\t}\n#endif\n\n#if 0\n\tchar abuf[INET6_ADDRSTRLEN], dbuf[INET6_ADDRSTRLEN];\n\tconst char *abp, *dbp;\n\n\tabp = inet_ntop(AF_INET6, &addr->s6_addr, abuf, sizeof(abuf));\n\tdbp = dstaddr ?\n\t    inet_ntop(AF_INET6, &dstaddr->s6_addr, dbuf, sizeof(dbuf))\n\t    : \"::\";\n\tloginfox(\"%s: cmd %d addr %s dstaddr %s addrflags %d\",\n\t    ifname, cmd, abp, dbp, addrflags);\n#endif\n\n\tif (ifs == NULL)\n\t\tifs = ctx->ifaces;\n\tif (ifs == NULL)\n\t\treturn;\n\tif ((ifp = if_find(ifs, ifname)) == NULL)\n\t\treturn;\n\tif ((state = ipv6_getstate(ifp)) == NULL)\n\t\treturn;\n\tanyglobal = ipv6_anyglobal(ifp) != NULL;\n\n\tTAILQ_FOREACH(ia, &state->addrs, next) {\n\t\tif (IN6_ARE_ADDR_EQUAL(&ia->addr, addr)) {\n\t\t\tia->addr_flags = addrflags;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tswitch (cmd) {\n\tcase RTM_DELADDR:\n\t\tif (ia != NULL) {\n\t\t\tTAILQ_REMOVE(&state->addrs, ia, next);\n\t\t\t/* We'll free it at the end of the function. */\n\t\t}\n\t\tbreak;\n\n\tcase RTM_NEWADDR:\n\t\tif (ia == NULL) {\n\t\t\tia = ipv6_newaddr(ifp, addr, prefix_len, 0);\n#ifdef ALIAS_ADDR\n\t\t\tstrlcpy(ia->alias, ifname, sizeof(ia->alias));\n#endif\n\t\t\tif (if_getlifetime6(ia) == -1) {\n\t\t\t\t/* No support or address vanished.\n\t\t\t\t * Either way, just set a deprecated\n\t\t\t\t * infinite time lifetime and continue.\n\t\t\t\t * This is fine because we only want\n\t\t\t\t * to know this when trying to extend\n\t\t\t\t * temporary addresses.\n\t\t\t\t * As we can't extend infinite, we'll\n\t\t\t\t * create a new temporary address. */\n\t\t\t\tia->prefix_pltime = 0;\n\t\t\t\tia->prefix_vltime = ND6_INFINITE_LIFETIME;\n\t\t\t}\n\t\t\t/* This is a minor regression against RFC 4941\n\t\t\t * because the kernel only knows when the\n\t\t\t * lifetimes were last updated, not when the\n\t\t\t * address was initially created.\n\t\t\t * Provided dhcpcd is not restarted, this\n\t\t\t * won't be a problem.\n\t\t\t * If we don't like it, we can always\n\t\t\t * pretend lifetimes are infinite and always\n\t\t\t * generate a new temporary address on\n\t\t\t * restart. */\n\t\t\tia->acquired = ia->created;\n\t\t\tia->addr_flags = addrflags;\n\t\t\tTAILQ_INSERT_TAIL(&state->addrs, ia, next);\n\t\t}\n\t\tia->dstaddr = dstaddr ? *dstaddr : in6addr_any;\n\t\tia->flags &= ~IPV6_AF_STALE;\n#ifdef IPV6_MANAGETEMPADDR\n\t\tif (ia->addr_flags & IN6_IFF_TEMPORARY)\n\t\t\tia->flags |= IPV6_AF_TEMPORARY;\n#endif\n\n#ifdef IPV6_POLLADDRFLAG\n\t\tif ((IN6_IS_ADDR_LINKLOCAL(&ia->addr) || ia->dadcallback) &&\n\t\t    ia->addr_flags & IN6_IFF_TENTATIVE) {\n\t\t\teloop_timeout_add_msec(ia->iface->ctx->eloop,\n\t\t\t    RETRANS_TIMER / 2, ipv6_checkaddrflags, ia);\n\t\t}\n#endif\n\n\t\tbreak;\n\n\tdefault:\n\t\treturn;\n\t}\n\n\tif (ia == NULL)\n\t\treturn;\n\n\tif (ia->dadcallback &&\n\t    ((ia->addr_flags & (IN6_IFF_DETACHED | IN6_IFF_TENTATIVE)) == 0 ||\n\t\tia->addr_flags & IN6_IFF_DUPLICATED))\n\t\tia->dadcallback(ia);\n\n\tif (IN6_IS_ADDR_LINKLOCAL(&ia->addr) &&\n\t    !(ia->addr_flags & IN6_IFF_NOTUSEABLE)) {\n\t\t/* Now run any callbacks.\n\t\t * Typically IPv6RS or DHCPv6 */\n\t\twhile ((cb = TAILQ_FIRST(&state->ll_callbacks))) {\n\t\t\tTAILQ_REMOVE(&state->ll_callbacks, cb, next);\n\t\t\tcb->callback(cb->arg);\n\t\t\tfree(cb);\n\t\t}\n\t}\n\n\tctx->options &= ~DHCPCD_RTBUILD;\n\tipv6nd_handleifa(cmd, ia, pid);\n#ifdef DHCP6\n\tdhcp6_handleifa(cmd, ia, pid);\n#endif\n\n\t/* Done with the ia now, so free it. */\n\tif (cmd == RTM_DELADDR)\n\t\tipv6_freeaddr(ia);\n\telse if (!(ia->addr_flags & IN6_IFF_NOTUSEABLE))\n\t\tia->flags |= IPV6_AF_DADCOMPLETED;\n\n\t/* If we've not already called rt_build via the IPv6ND\n\t * or DHCP6 handlers and the existance of any useable\n\t * global address on the interface has changed,\n\t * call rt_build to add/remove the default route. */\n\tif (ifp->active &&\n\t    ((ifp->options != NULL && ifp->options->options & DHCPCD_IPV6) ||\n\t\t(ifp->options == NULL && ctx->options & DHCPCD_IPV6)) &&\n\t    !(ctx->options & DHCPCD_RTBUILD) &&\n\t    (ipv6_anyglobal(ifp) != NULL) != anyglobal)\n\t\trt_build(ctx, AF_INET6);\n}\n\nint\nipv6_hasaddr(const struct interface *ifp)\n{\n\tif (ipv6nd_iffindaddr(ifp, NULL, 0) != NULL)\n\t\treturn 1;\n#ifdef DHCP6\n\tif (dhcp6_iffindaddr(ifp, NULL, 0) != NULL)\n\t\treturn 1;\n#endif\n\treturn 0;\n}\n\nstruct ipv6_addr *\nipv6_iffindaddr(struct interface *ifp, const struct in6_addr *addr,\n    int revflags)\n{\n\tstruct ipv6_state *state;\n\tstruct ipv6_addr *ap;\n\n\tstate = IPV6_STATE(ifp);\n\tif (state == NULL)\n\t\treturn NULL;\n\n\tTAILQ_FOREACH(ap, &state->addrs, next) {\n\t\tif (addr == NULL) {\n\t\t\tif (IN6_IS_ADDR_LINKLOCAL(&ap->addr) &&\n\t\t\t    (!revflags || !(ap->addr_flags & revflags)))\n\t\t\t\treturn ap;\n\t\t} else if (IN6_ARE_ADDR_EQUAL(&ap->addr, addr)) {\n\t\t\t/* This is our address so we will return now */\n\t\t\tif (!revflags || !(ap->addr_flags & revflags))\n\t\t\t\treturn ap;\n\t\t\treturn NULL;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic struct ipv6_addr *\nipv6_iffindmaskaddr(const struct interface *ifp, const struct in6_addr *addr)\n{\n\tstruct ipv6_state *state;\n\tstruct ipv6_addr *ap;\n\tstruct in6_addr mask;\n\n\tstate = IPV6_STATE(ifp);\n\tif (state) {\n\t\tTAILQ_FOREACH(ap, &state->addrs, next) {\n\t\t\tif (ipv6_mask(&mask, ap->prefix_len) == -1)\n\t\t\t\tcontinue;\n\t\t\tif (IN6_ARE_MASKED_ADDR_EQUAL(&ap->addr, addr, &mask))\n\t\t\t\treturn ap;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstruct ipv6_addr *\nipv6_findmaskaddr(struct dhcpcd_ctx *ctx, const struct in6_addr *addr)\n{\n\tstruct interface *ifp;\n\tstruct ipv6_addr *ap;\n\n\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\tap = ipv6_iffindmaskaddr(ifp, addr);\n\t\tif (ap != NULL)\n\t\t\treturn ap;\n\t}\n\treturn NULL;\n}\n\nstatic struct ipv6_addr *\nipv6_iffinddstaddr(const struct interface *ifp, const struct in6_addr *addr)\n{\n\tstruct ipv6_state *state;\n\tstruct ipv6_addr *ap;\n\n\tstate = IPV6_STATE(ifp);\n\tif (state) {\n\t\tTAILQ_FOREACH(ap, &state->addrs, next) {\n\t\t\tif (IN6_ARE_ADDR_EQUAL(&ap->dstaddr, addr))\n\t\t\t\treturn ap;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstruct ipv6_addr *\nipv6_finddstaddr(struct dhcpcd_ctx *ctx, const struct in6_addr *addr)\n{\n\tstruct interface *ifp;\n\tstruct ipv6_addr *ap;\n\n\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\tap = ipv6_iffinddstaddr(ifp, addr);\n\t\tif (ap != NULL)\n\t\t\treturn ap;\n\t}\n\treturn NULL;\n}\n\nint\nipv6_addlinklocalcallback(struct interface *ifp, void (*callback)(void *),\n    void *arg)\n{\n\tstruct ipv6_state *state;\n\tstruct ll_callback *cb;\n\n\tstate = ipv6_getstate(ifp);\n\tTAILQ_FOREACH(cb, &state->ll_callbacks, next) {\n\t\tif (cb->callback == callback && cb->arg == arg)\n\t\t\treturn 0;\n\t}\n\n\tcb = malloc(sizeof(*cb));\n\tif (cb == NULL) {\n\t\tlogerr(__func__);\n\t\treturn -1;\n\t}\n\tcb->callback = callback;\n\tcb->arg = arg;\n\tTAILQ_INSERT_TAIL(&state->ll_callbacks, cb, next);\n\treturn 0;\n}\n\nstatic struct ipv6_addr *\nipv6_newlinklocal(struct interface *ifp)\n{\n\tstruct ipv6_addr *ia;\n\tstruct in6_addr in6;\n\n\tmemset(&in6, 0, sizeof(in6));\n\tin6.s6_addr32[0] = htonl(0xfe800000);\n\tia = ipv6_newaddr(ifp, &in6, 64, 0);\n\tif (ia != NULL) {\n\t\tia->prefix_pltime = ND6_INFINITE_LIFETIME;\n\t\tia->prefix_vltime = ND6_INFINITE_LIFETIME;\n\t}\n\treturn ia;\n}\n\nstatic const uint8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };\nstatic const uint8_t allone[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,\n\t0xff };\n\nstatic int\nipv6_addlinklocal(struct interface *ifp)\n{\n\tstruct ipv6_state *state;\n\tstruct ipv6_addr *ap, *ap2;\n\tint dadcounter;\n\n\tif (!(ifp->options->options & DHCPCD_CONFIGURE))\n\t\treturn 0;\n\n\t/* Check sanity before malloc */\n\tif (!(ifp->options->options & DHCPCD_SLAACPRIVATE)) {\n\t\tswitch (ifp->hwtype) {\n\t\tcase ARPHRD_ETHER:\n\t\t\t/* Check for a valid hardware address */\n\t\t\tif (ifp->hwlen != 6 && ifp->hwlen != 8) {\n\t\t\t\terrno = ENOTSUP;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tif (memcmp(ifp->hwaddr, allzero, ifp->hwlen) == 0 ||\n\t\t\t    memcmp(ifp->hwaddr, allone, ifp->hwlen) == 0) {\n\t\t\t\terrno = EINVAL;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\terrno = ENOTSUP;\n\t\t\treturn -1;\n\t\t}\n\t}\n\n\tstate = ipv6_getstate(ifp);\n\tif (state == NULL)\n\t\treturn -1;\n\n\tap = ipv6_newlinklocal(ifp);\n\tif (ap == NULL)\n\t\treturn -1;\n\n\tdadcounter = 0;\n\tif (ifp->options->options & DHCPCD_SLAACPRIVATE) {\n\tnextslaacprivate:\n\t\tif (ipv6_makestableprivate(&ap->addr, &ap->prefix,\n\t\t\tap->prefix_len, ifp, &dadcounter) == -1) {\n\t\t\tfree(ap);\n\t\t\treturn -1;\n\t\t}\n\t\tap->dadcounter = dadcounter;\n\t} else if (ipv6_makehwaddr(&ap->addr, &ap->prefix, ap->prefix_len,\n\t\t       ifp) == -1) {\n\t\tfree(ap);\n\t\treturn -1;\n\t}\n\n\t/* Do we already have this address? */\n\tTAILQ_FOREACH(ap2, &state->addrs, next) {\n\t\tif (IN6_ARE_ADDR_EQUAL(&ap->addr, &ap2->addr)) {\n\t\t\tif (ap2->addr_flags & IN6_IFF_DUPLICATED) {\n\t\t\t\tif (ifp->options->options &\n\t\t\t\t    DHCPCD_SLAACPRIVATE) {\n\t\t\t\t\tdadcounter++;\n\t\t\t\t\tgoto nextslaacprivate;\n\t\t\t\t}\n\t\t\t\tfree(ap);\n\t\t\t\terrno = EADDRNOTAVAIL;\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\tlogwarnx(\"%s: waiting for %s to complete\",\n\t\t\t    ap2->iface->name, ap2->saddr);\n\t\t\tfree(ap);\n\t\t\terrno = EEXIST;\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tinet_ntop(AF_INET6, &ap->addr, ap->saddr, sizeof(ap->saddr));\n\tTAILQ_INSERT_TAIL(&state->addrs, ap, next);\n\tipv6_addaddr(ap, NULL);\n\treturn 1;\n}\n\nstatic int\nipv6_tryaddlinklocal(struct interface *ifp)\n{\n\tstruct ipv6_addr *ia;\n\n\t/* We can't assign a link-locak address to this,\n\t * the ppp process has to. */\n\tif (ifp->flags & IFF_POINTOPOINT)\n\t\treturn 0;\n\n\tia = ipv6_iffindaddr(ifp, NULL, IN6_IFF_DUPLICATED);\n\tif (ia != NULL) {\n#ifdef IPV6_POLLADDRFLAG\n\t\tif (ia->addr_flags & IN6_IFF_TENTATIVE) {\n\t\t\teloop_timeout_add_msec(ia->iface->ctx->eloop,\n\t\t\t    RETRANS_TIMER / 2, ipv6_checkaddrflags, ia);\n\t\t}\n#endif\n\t\treturn 0;\n\t}\n\tif (!CAN_ADD_LLADDR(ifp))\n\t\treturn 0;\n\n\treturn ipv6_addlinklocal(ifp);\n}\n\nvoid\nipv6_setscope(struct sockaddr_in6 *sin, unsigned int ifindex)\n{\n#ifdef __KAME__\n\t/* KAME based systems want to store the scope inside the sin6_addr\n\t * for link local addresses */\n\tif (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) {\n\t\tuint16_t scope = htons((uint16_t)ifindex);\n\t\tmemcpy(&sin->sin6_addr.s6_addr[2], &scope, sizeof(scope));\n\t}\n\tsin->sin6_scope_id = 0;\n#else\n\tif (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr))\n\t\tsin->sin6_scope_id = ifindex;\n\telse\n\t\tsin->sin6_scope_id = 0;\n#endif\n}\n\nunsigned int\nipv6_getscope(const struct sockaddr_in6 *sin)\n{\n#ifdef __KAME__\n\tuint16_t scope;\n#endif\n\n\tif (!IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr))\n\t\treturn 0;\n#ifdef __KAME__\n\tmemcpy(&scope, &sin->sin6_addr.s6_addr[2], sizeof(scope));\n\treturn (unsigned int)ntohs(scope);\n#else\n\treturn (unsigned int)sin->sin6_scope_id;\n#endif\n}\n\nstruct ipv6_addr *\nipv6_newaddr(struct interface *ifp, const struct in6_addr *addr,\n    uint8_t prefix_len, unsigned int flags)\n{\n\tstruct ipv6_addr *ia, *iaf;\n\tchar buf[INET6_ADDRSTRLEN];\n\tconst char *cbp;\n\n\tia = calloc(1, sizeof(*ia));\n\tif (ia == NULL)\n\t\tgoto err;\n\n\tia->iface = ifp;\n\tia->flags = IPV6_AF_NEW | flags;\n\tia->prefix_len = prefix_len;\n\tia->dhcp6_fd = -1;\n\n#ifndef SMALL\n\tTAILQ_INIT(&ia->pd_pfxs);\n#endif\n\n\tif (prefix_len == 128)\n\t\tgoto makepfx;\n\telse if (ia->flags & IPV6_AF_AUTOCONF) {\n\t\tia->prefix = *addr;\n\t\tiaf = ipv6nd_iffindprefix(ifp, addr, prefix_len);\n\t\tif (iaf != NULL)\n\t\t\tmemcpy(&ia->addr, &iaf->addr, sizeof(ia->addr));\n\t\telse {\n\t\t\tia->dadcounter = ipv6_makeaddr(&ia->addr, ifp,\n\t\t\t    &ia->prefix, ia->prefix_len, ia->flags);\n\t\t\tif (ia->dadcounter == -1)\n\t\t\t\tgoto err;\n\t\t}\n\t} else if (ia->flags & IPV6_AF_RAPFX) {\n\t\tia->prefix = *addr;\n#ifdef __sun\n\t\tia->addr = *addr;\n\t\tcbp = inet_ntop(AF_INET6, &ia->addr, buf, sizeof(buf));\n\t\tgoto paddr;\n#else\n\t\tgoto flags;\n#endif\n\t} else if (ia->flags & (IPV6_AF_REQUEST | IPV6_AF_PFXDELEGATION)) {\n\t\tia->prefix = *addr;\n\t\tcbp = inet_ntop(AF_INET6, &ia->prefix, buf, sizeof(buf));\n\t\tgoto paddr;\n\t} else {\n\tmakepfx:\n\t\tia->addr = *addr;\n\t\tif (ipv6_makeprefix(&ia->prefix, &ia->addr, ia->prefix_len) ==\n\t\t    -1)\n\t\t\tgoto err;\n\t}\n\n\tcbp = inet_ntop(AF_INET6, &ia->addr, buf, sizeof(buf));\npaddr:\n\tif (cbp == NULL)\n\t\tgoto err;\n\tsnprintf(ia->saddr, sizeof(ia->saddr), \"%s/%d\", cbp, ia->prefix_len);\n\n#ifndef __sun\nflags:\n#endif\n\t/* If adding a new DHCP / RA derived address, check current flags\n\t * from an existing address. */\n\tiaf = ipv6_iffindaddr(ifp, &ia->addr, 0);\n\tif (iaf != NULL) {\n\t\tia->addr_flags = iaf->addr_flags;\n\t\tia->flags |= IPV6_AF_ADDED;\n\t} else\n\t\tia->addr_flags |= IN6_IFF_TENTATIVE;\n\n\tif (!(ia->addr_flags & IN6_IFF_NOTUSEABLE))\n\t\tia->flags |= IPV6_AF_DADCOMPLETED;\n\n\treturn ia;\n\nerr:\n\tlogerr(__func__);\n\tfree(ia);\n\treturn NULL;\n}\n\nstatic void\nipv6_staticdadcallback(void *arg)\n{\n\tstruct ipv6_addr *ia = arg;\n\tint wascompleted;\n\n\twascompleted = (ia->flags & IPV6_AF_DADCOMPLETED);\n\tia->flags |= IPV6_AF_DADCOMPLETED;\n\tif (ia->addr_flags & IN6_IFF_DUPLICATED)\n\t\tlogwarnx(\"%s: DAD detected %s\", ia->iface->name, ia->saddr);\n\telse if (!wascompleted) {\n\t\tlogdebugx(\"%s: IPv6 static DAD completed\", ia->iface->name);\n\t}\n\n#define FINISHED (IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED)\n\tif (!wascompleted) {\n\t\tstruct interface *ifp;\n\t\tstruct ipv6_state *state;\n\n\t\tifp = ia->iface;\n\t\tstate = IPV6_STATE(ifp);\n\t\tTAILQ_FOREACH(ia, &state->addrs, next) {\n\t\t\tif (ia->flags & IPV6_AF_STATIC &&\n\t\t\t    (ia->flags & FINISHED) != FINISHED) {\n\t\t\t\twascompleted = 1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (!wascompleted)\n\t\t\tscript_runreason(ifp, \"STATIC6\");\n\t}\n#undef FINISHED\n}\n\nssize_t\nipv6_env(FILE *fp, const char *prefix, const struct interface *ifp)\n{\n\tstruct ipv6_addr *ia;\n\n\tia = ipv6_iffindaddr(UNCONST(ifp), &ifp->options->req_addr6,\n\t    IN6_IFF_NOTUSEABLE);\n\tif (ia == NULL)\n\t\treturn 0;\n\tif (efprintf(fp, \"%s_ip6_address=%s\", prefix, ia->saddr) == -1)\n\t\treturn -1;\n\treturn 1;\n}\n\nint\nipv6_staticdadcompleted(const struct interface *ifp)\n{\n\tconst struct ipv6_state *state;\n\tconst struct ipv6_addr *ia;\n\tint n;\n\n\tif ((state = IPV6_CSTATE(ifp)) == NULL)\n\t\treturn 0;\n\tn = 0;\n#define COMPLETED (IPV6_AF_STATIC | IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED)\n\tTAILQ_FOREACH(ia, &state->addrs, next) {\n\t\tif ((ia->flags & COMPLETED) == COMPLETED &&\n\t\t    !(ia->addr_flags & IN6_IFF_NOTUSEABLE))\n\t\t\tn++;\n\t}\n\treturn n;\n}\n\nint\nipv6_startstatic(struct interface *ifp)\n{\n\tstruct ipv6_addr *ia;\n\tint run_script;\n\n\tif (IN6_IS_ADDR_UNSPECIFIED(&ifp->options->req_addr6))\n\t\treturn 0;\n\n\tia = ipv6_iffindaddr(ifp, &ifp->options->req_addr6, 0);\n\tif (ia != NULL &&\n\t    (ia->prefix_len != ifp->options->req_prefix_len ||\n\t\tia->addr_flags & IN6_IFF_NOTUSEABLE)) {\n\t\tipv6_deleteaddr(ia);\n\t\tia = NULL;\n\t}\n\tif (ia == NULL) {\n\t\tstruct ipv6_state *state;\n\n\t\tia = ipv6_newaddr(ifp, &ifp->options->req_addr6,\n\t\t    ifp->options->req_prefix_len, 0);\n\t\tif (ia == NULL)\n\t\t\treturn -1;\n\t\tstate = IPV6_STATE(ifp);\n\t\tTAILQ_INSERT_TAIL(&state->addrs, ia, next);\n\t\trun_script = 0;\n\t} else\n\t\trun_script = 1;\n\tia->flags |= IPV6_AF_STATIC | IPV6_AF_ONLINK;\n\tia->prefix_vltime = ND6_INFINITE_LIFETIME;\n\tia->prefix_pltime = ND6_INFINITE_LIFETIME;\n\tia->dadcallback = ipv6_staticdadcallback;\n\tipv6_addaddr(ia, NULL);\n\trt_build(ifp->ctx, AF_INET6);\n\tif (run_script)\n\t\tscript_runreason(ifp, \"STATIC6\");\n\treturn 1;\n}\n\n/* Ensure the interface has a link-local address */\nint\nipv6_start(struct interface *ifp)\n{\n#ifdef IPV6_POLLADDRFLAG\n\tstruct ipv6_state *state;\n\n\t/* We need to update the address flags. */\n\tif ((state = IPV6_STATE(ifp)) != NULL) {\n\t\tstruct ipv6_addr *ia;\n\t\tconst char *alias;\n\t\tint flags;\n\n\t\tTAILQ_FOREACH(ia, &state->addrs, next) {\n#ifdef ALIAS_ADDR\n\t\t\talias = ia->alias;\n#else\n\t\t\talias = NULL;\n#endif\n\t\t\tflags = if_addrflags6(ia->iface, &ia->addr, alias);\n\t\t\tif (flags != -1)\n\t\t\t\tia->addr_flags = flags;\n\t\t}\n\t}\n#endif\n\n\tif (ipv6_tryaddlinklocal(ifp) == -1)\n\t\treturn -1;\n\n\treturn 0;\n}\n\nvoid\nipv6_freedrop(struct interface *ifp, int drop)\n{\n\tstruct ipv6_state *state;\n\tstruct ll_callback *cb;\n\n\tif (ifp == NULL)\n\t\treturn;\n\n\tif ((state = IPV6_STATE(ifp)) == NULL)\n\t\treturn;\n\n\t/* If we got here, we can get rid of any LL callbacks. */\n\twhile ((cb = TAILQ_FIRST(&state->ll_callbacks))) {\n\t\tTAILQ_REMOVE(&state->ll_callbacks, cb, next);\n\t\tfree(cb);\n\t}\n\n\tipv6_freedrop_addrs(&state->addrs, drop ? 2 : 0, 0, NULL);\n\tif (drop) {\n\t\tif (ifp->ctx->ra_routers != NULL)\n\t\t\trt_build(ifp->ctx, AF_INET6);\n\t} else {\n\t\t/* Because we need to cache the addresses we don't control,\n\t\t * we only free the state on when NOT dropping addresses. */\n\t\tfree(state);\n\t\tifp->if_data[IF_DATA_IPV6] = NULL;\n\t\teloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);\n\t}\n}\n\nvoid\nipv6_ctxfree(struct dhcpcd_ctx *ctx)\n{\n\tfree(ctx->ra_routers);\n\tfree(ctx->secret);\n}\n\nint\nipv6_handleifa_addrs(int cmd, struct ipv6_addrhead *addrs,\n    const struct ipv6_addr *addr, pid_t pid)\n{\n\tstruct ipv6_addr *ia, *ian;\n\tint found = 0, alldadcompleted = 1;\n\n\tif (cmd != RTM_NEWADDR && cmd != RTM_DELADDR) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tTAILQ_FOREACH_SAFE(ia, addrs, next, ian) {\n\t\tif (!IN6_ARE_ADDR_EQUAL(&addr->addr, &ia->addr)) {\n\t\t\tif (ia->flags & IPV6_AF_ADDED &&\n\t\t\t    !(ia->flags & IPV6_AF_DADCOMPLETED))\n\t\t\t\talldadcompleted = 0;\n\t\t\tcontinue;\n\t\t}\n\n\t\tia->addr_flags = addr->addr_flags;\n\n\t\tif (cmd == RTM_DELADDR && ia->flags & IPV6_AF_ADDED)\n\t\t\tlogwarnx(\"%s: pid %d deleted address %s\",\n\t\t\t    ia->iface->name, (int)pid, ia->saddr);\n\n\t\t/* Check DAD.\n\t\t * On Linux we can get IN6_IFF_DUPLICATED via RTM_DELADDR. */\n\t\tif (((ia->addr_flags &\n\t\t\t (IN6_IFF_DETACHED | IN6_IFF_TENTATIVE)) == 0 ||\n\t\t\tia->addr_flags & IN6_IFF_DUPLICATED) &&\n\t\t    (ia->flags & IPV6_AF_DADCOMPLETED) == 0) {\n\t\t\tfound++;\n\t\t\tif (ia->dadcallback)\n\t\t\t\tia->dadcallback(ia);\n\t\t\t/* We need to set this here in-case the\n\t\t\t * dadcallback function checks it */\n\t\t\tia->flags |= IPV6_AF_DADCOMPLETED;\n\t\t}\n\n\t\tif (cmd == RTM_DELADDR) {\n\t\t\tia->flags &= ~IPV6_AF_ADDED;\n\t\t\tipv6_deletedaddr(ia);\n\t\t\tif (ia->flags & IPV6_AF_DELEGATED) {\n\t\t\t\tTAILQ_REMOVE(addrs, ia, next);\n\t\t\t\tipv6_freeaddr(ia);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn alldadcompleted ? found : 0;\n}\n\n#ifdef IPV6_MANAGETEMPADDR\nstatic void\nipv6_regen_desync(struct interface *ifp, bool force)\n{\n\tstruct ipv6_state *state;\n\tunsigned int max;\n\n\tstate = IPV6_STATE(ifp);\n\n\t/* RFC4941 Section 5 states that DESYNC_FACTOR must never be\n\t * greater than TEMP_VALID_LIFETIME - REGEN_ADVANCE.\n\t * I believe this is an error and it should be never be greater than\n\t * TEMP_PREFERRED_LIFETIME - REGEN_ADVANCE. */\n\tmax = TEMP_PREFERRED_LIFETIME - REGEN_ADVANCE;\n\tif (state->desync_factor && !force && state->desync_factor < max)\n\t\treturn;\n\tif (state->desync_factor == 0)\n\t\tstate->desync_factor = arc4random_uniform(\n\t\t    MIN(MAX_DESYNC_FACTOR, max));\n\tmax = TEMP_PREFERRED_LIFETIME - state->desync_factor - REGEN_ADVANCE;\n\teloop_timeout_add_sec(ifp->ctx->eloop, max, ipv6_regentempaddrs, ifp);\n}\n\n/* RFC4941 Section 3.3.7 */\nstatic void\nipv6_tempdadcallback(void *arg)\n{\n\tstruct ipv6_addr *ia = arg;\n\n\tif (ia->addr_flags & IN6_IFF_DUPLICATED) {\n\t\tstruct ipv6_addr *ia1;\n\t\tstruct timespec tv;\n\n\t\tif (++ia->dadcounter == TEMP_IDGEN_RETRIES) {\n\t\t\tlogerrx(\"%s: too many duplicate temporary addresses\",\n\t\t\t    ia->iface->name);\n\t\t\treturn;\n\t\t}\n\t\tclock_gettime(CLOCK_MONOTONIC, &tv);\n\t\tif ((ia1 = ipv6_createtempaddr(ia, &tv)) == NULL)\n\t\t\tlogerr(__func__);\n\t\telse\n\t\t\tia1->dadcounter = ia->dadcounter;\n\t\tipv6_deleteaddr(ia);\n\t\tif (ia1)\n\t\t\tipv6_addaddr(ia1, &ia1->acquired);\n\t}\n}\n\nstruct ipv6_addr *\nipv6_createtempaddr(struct ipv6_addr *ia0, const struct timespec *now)\n{\n\tstruct ipv6_state *state;\n\tstruct interface *ifp = ia0->iface;\n\tstruct ipv6_addr *ia;\n\n\tia = ipv6_newaddr(ifp, &ia0->prefix, ia0->prefix_len,\n\t    IPV6_AF_AUTOCONF | IPV6_AF_TEMPORARY);\n\tif (ia == NULL)\n\t\treturn NULL;\n\n\tia->dadcallback = ipv6_tempdadcallback;\n\tia->created = ia->acquired = now ? *now : ia0->acquired;\n\n\t/* Ensure desync is still valid */\n\tipv6_regen_desync(ifp, false);\n\n\t/* RFC4941 Section 3.3.4 */\n\tstate = IPV6_STATE(ia->iface);\n\tia->prefix_pltime = MIN(ia0->prefix_pltime,\n\t    TEMP_PREFERRED_LIFETIME - state->desync_factor);\n\tia->prefix_vltime = MIN(ia0->prefix_vltime, TEMP_VALID_LIFETIME);\n\tif (ia->prefix_pltime <= REGEN_ADVANCE ||\n\t    ia->prefix_pltime > ia0->prefix_vltime) {\n\t\terrno = EINVAL;\n\t\tfree(ia);\n\t\treturn NULL;\n\t}\n\n\tTAILQ_INSERT_TAIL(&state->addrs, ia, next);\n\treturn ia;\n}\n\nstruct ipv6_addr *\nipv6_settemptime(struct ipv6_addr *ia, int flags)\n{\n\tstruct ipv6_state *state;\n\tstruct ipv6_addr *ap, *first;\n\n\tstate = IPV6_STATE(ia->iface);\n\tfirst = NULL;\n\tTAILQ_FOREACH_REVERSE(ap, &state->addrs, ipv6_addrhead, next) {\n\t\tif (ap->flags & IPV6_AF_TEMPORARY && ap->prefix_pltime &&\n\t\t    IN6_ARE_ADDR_EQUAL(&ia->prefix, &ap->prefix)) {\n\t\t\tunsigned int max, ext;\n\n\t\t\tif (flags == 0) {\n\t\t\t\tif (ap->prefix_pltime -\n\t\t\t\t\t(uint32_t)(ia->acquired.tv_sec -\n\t\t\t\t\t    ap->acquired.tv_sec) <\n\t\t\t\t    REGEN_ADVANCE)\n\t\t\t\t\tcontinue;\n\n\t\t\t\treturn ap;\n\t\t\t}\n\n\t\t\tif (!(ap->flags & IPV6_AF_ADDED))\n\t\t\t\tap->flags |= IPV6_AF_NEW | IPV6_AF_AUTOCONF;\n\t\t\tap->flags &= ~IPV6_AF_STALE;\n\n\t\t\t/* RFC4941 Section 3.4\n\t\t\t * Deprecated prefix, deprecate the temporary address */\n\t\t\tif (ia->prefix_pltime == 0) {\n\t\t\t\tap->prefix_pltime = 0;\n\t\t\t\tgoto valid;\n\t\t\t}\n\n\t\t\t/* Ensure desync is still valid */\n\t\t\tipv6_regen_desync(ap->iface, false);\n\n\t\t\t/* RFC4941 Section 3.3.2\n\t\t\t * Extend temporary times, but ensure that they\n\t\t\t * never last beyond the system limit. */\n\t\t\text = (unsigned int)ia->acquired.tv_sec +\n\t\t\t    ia->prefix_pltime;\n\t\t\tmax = (unsigned int)(ap->created.tv_sec +\n\t\t\t    TEMP_PREFERRED_LIFETIME - state->desync_factor);\n\t\t\tif (ext < max)\n\t\t\t\tap->prefix_pltime = ia->prefix_pltime;\n\t\t\telse\n\t\t\t\tap->prefix_pltime = (uint32_t)(max -\n\t\t\t\t    ia->acquired.tv_sec);\n\n\t\tvalid:\n\t\t\text = (unsigned int)ia->acquired.tv_sec +\n\t\t\t    ia->prefix_vltime;\n\t\t\tmax = (unsigned int)(ap->created.tv_sec +\n\t\t\t    TEMP_VALID_LIFETIME);\n\t\t\tif (ext < max)\n\t\t\t\tap->prefix_vltime = ia->prefix_vltime;\n\t\t\telse\n\t\t\t\tap->prefix_vltime = (uint32_t)(max -\n\t\t\t\t    ia->acquired.tv_sec);\n\n\t\t\t/* Just extend the latest matching prefix */\n\t\t\tap->acquired = ia->acquired;\n\n\t\t\t/* If extending return the last match as\n\t\t\t * it's the most current.\n\t\t\t * If deprecating, deprecate any other addresses we\n\t\t\t * may have, although this should not be needed */\n\t\t\tif (ia->prefix_pltime)\n\t\t\t\treturn ap;\n\t\t\tif (first == NULL)\n\t\t\t\tfirst = ap;\n\t\t}\n\t}\n\treturn first;\n}\n\nvoid\nipv6_addtempaddrs(struct interface *ifp, struct timespec *now)\n{\n\tstruct ipv6_state *state;\n\tstruct ipv6_addr *ia;\n\n\tstate = IPV6_STATE(ifp);\n\tTAILQ_FOREACH(ia, &state->addrs, next) {\n\t\tif (ia->flags & IPV6_AF_TEMPORARY &&\n\t\t    !(ia->flags & IPV6_AF_STALE))\n\t\t\tipv6_addaddr(ia, now);\n\t}\n}\n\nstatic void\nipv6_regentempaddr0(struct ipv6_addr *ia, struct timespec *tv)\n{\n\tstruct ipv6_addr *ia1;\n\n\tlogdebugx(\"%s: regen temp addr %s\", ia->iface->name, ia->saddr);\n\tia1 = ipv6_createtempaddr(ia, tv);\n\tif (ia1)\n\t\tipv6_addaddr(ia1, tv);\n\telse\n\t\tlogerr(__func__);\n}\n\nstatic void\nipv6_regentempaddr(void *arg)\n{\n\tstruct timespec tv;\n\n\tclock_gettime(CLOCK_MONOTONIC, &tv);\n\tipv6_regentempaddr0(arg, &tv);\n}\n\nvoid\nipv6_regentempaddrs(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tstruct timespec tv;\n\tstruct ipv6_state *state;\n\tstruct ipv6_addr *ia;\n\n\tstate = IPV6_STATE(ifp);\n\tif (state == NULL)\n\t\treturn;\n\n\tipv6_regen_desync(ifp, true);\n\n\tclock_gettime(CLOCK_MONOTONIC, &tv);\n\n\t/* Mark addresses for regen so we don't infinite loop. */\n\tTAILQ_FOREACH(ia, &state->addrs, next) {\n\t\tif (ia->flags & IPV6_AF_TEMPORARY &&\n\t\t    ia->flags & IPV6_AF_ADDED && !(ia->flags & IPV6_AF_STALE))\n\t\t\tia->flags |= IPV6_AF_REGEN;\n\t\telse\n\t\t\tia->flags &= ~IPV6_AF_REGEN;\n\t}\n\n\t/* Now regen temp addrs */\n\tTAILQ_FOREACH(ia, &state->addrs, next) {\n\t\tif (ia->flags & IPV6_AF_REGEN) {\n\t\t\tipv6_regentempaddr0(ia, &tv);\n\t\t\tia->flags &= ~IPV6_AF_REGEN;\n\t\t}\n\t}\n}\n#endif /* IPV6_MANAGETEMPADDR */\n\nvoid\nipv6_markaddrsstale(struct interface *ifp, unsigned int flags)\n{\n\tstruct ipv6_state *state;\n\tstruct ipv6_addr *ia;\n\n\tstate = IPV6_STATE(ifp);\n\tif (state == NULL)\n\t\treturn;\n\n\tTAILQ_FOREACH(ia, &state->addrs, next) {\n\t\tif (flags == 0 || ia->flags & flags)\n\t\t\tia->flags |= IPV6_AF_STALE;\n\t}\n}\n\nvoid\nipv6_deletestaleaddrs(struct interface *ifp)\n{\n\tstruct ipv6_state *state;\n\tstruct ipv6_addr *ia, *ia1;\n\n\tstate = IPV6_STATE(ifp);\n\tif (state == NULL)\n\t\treturn;\n\n\tTAILQ_FOREACH_SAFE(ia, &state->addrs, next, ia1) {\n\t\tif (ia->flags & IPV6_AF_STALE)\n\t\t\tipv6_handleifa(ifp->ctx, RTM_DELADDR, ifp->ctx->ifaces,\n\t\t\t    ifp->name, &ia->addr, ia->prefix_len, &ia->dstaddr,\n\t\t\t    0, getpid());\n\t}\n}\n\nstatic struct rt *\ninet6_makeroute(struct interface *ifp, const struct ra *rap)\n{\n\tstruct rt *rt;\n\n\tif ((rt = rt_new(ifp)) == NULL)\n\t\treturn NULL;\n\n#ifdef HAVE_ROUTE_METRIC\n\trt->rt_metric = ifp->metric;\n#endif\n\tif (rap != NULL)\n\t\trt->rt_mtu = rap->mtu;\n\treturn rt;\n}\n\nstatic struct rt *\ninet6_makeprefix(struct interface *ifp, const struct ra *rap,\n    const struct ipv6_addr *addr)\n{\n\tstruct rt *rt;\n\tstruct in6_addr netmask;\n\n\tif (addr == NULL || addr->prefix_len > 128) {\n\t\terrno = EINVAL;\n\t\treturn NULL;\n\t}\n\n\t/* There is no point in trying to manage a /128 prefix,\n\t * ones without a lifetime.  */\n\tif (addr->prefix_len == 128 || addr->prefix_vltime == 0)\n\t\treturn NULL;\n\n\t/* Don't install a reject route when not creating bigger prefixes. */\n\tif (addr->flags & IPV6_AF_NOREJECT)\n\t\treturn NULL;\n\n\t/* This address is the delegated prefix, so add a reject route for\n\t * it via the loopback interface. */\n\tif (addr->flags & IPV6_AF_PFXDELEGATION) {\n\t\tstruct interface *lo0;\n\n\t\tTAILQ_FOREACH(lo0, ifp->ctx->ifaces, next) {\n\t\t\tif (lo0->flags & IFF_LOOPBACK)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (lo0 == NULL)\n\t\t\tlogwarnx(\"cannot find a loopback interface \"\n\t\t\t\t \"to reject via\");\n\t\telse\n\t\t\tifp = lo0;\n\t}\n\n\tif ((rt = inet6_makeroute(ifp, rap)) == NULL)\n\t\treturn NULL;\n\n\tsa_in6_init(&rt->rt_dest, &addr->prefix);\n\tipv6_mask(&netmask, addr->prefix_len);\n\tsa_in6_init(&rt->rt_netmask, &netmask);\n\tif (addr->flags & IPV6_AF_PFXDELEGATION) {\n\t\trt->rt_flags |= RTF_REJECT;\n\t\t/* Linux does not like a gateway for a reject route. */\n#ifndef __linux__\n\t\tsa_in6_init(&rt->rt_gateway, &in6addr_loopback);\n#endif\n\t} else if (!(addr->flags & IPV6_AF_ONLINK))\n\t\tsa_in6_init(&rt->rt_gateway, &rap->from);\n\telse\n\t\trt->rt_gateway.sa_family = AF_UNSPEC;\n\tsa_in6_init(&rt->rt_ifa, &addr->addr);\n\treturn rt;\n}\n\nstatic struct rt *\ninet6_makerouter(struct ra *rap)\n{\n\tstruct rt *rt;\n\n\tif ((rt = inet6_makeroute(rap->iface, rap)) == NULL)\n\t\treturn NULL;\n\tsa_in6_init(&rt->rt_dest, &in6addr_any);\n\tsa_in6_init(&rt->rt_netmask, &in6addr_any);\n\tsa_in6_init(&rt->rt_gateway, &rap->from);\n\treturn rt;\n}\n\n#define RT_IS_DEFAULT(rtp)                                   \\\n\t(IN6_ARE_ADDR_EQUAL(&((rtp)->dest), &in6addr_any) && \\\n\t    IN6_ARE_ADDR_EQUAL(&((rtp)->mask), &in6addr_any))\n\nstatic int\ninet6_staticroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx)\n{\n\tstruct interface *ifp;\n\tstruct ipv6_state *state;\n\tstruct ipv6_addr *ia;\n\tstruct rt *rt;\n\n\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\tif ((state = IPV6_STATE(ifp)) == NULL)\n\t\t\tcontinue;\n\t\tTAILQ_FOREACH(ia, &state->addrs, next) {\n\t\t\tif ((ia->flags & (IPV6_AF_ADDED | IPV6_AF_STATIC)) ==\n\t\t\t    (IPV6_AF_ADDED | IPV6_AF_STATIC)) {\n\t\t\t\trt = inet6_makeprefix(ifp, NULL, ia);\n\t\t\t\tif (rt)\n\t\t\t\t\trt_proto_add(routes, rt);\n\t\t\t}\n\t\t}\n\t}\n\treturn 0;\n}\n\nstatic int\ninet6_raroutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx)\n{\n\tstruct rt *rt;\n\tstruct ra *rap;\n\tconst struct routeinfo *rinfo;\n\tconst struct ipv6_addr *addr;\n\tstruct in6_addr netmask;\n\n\tif (ctx->ra_routers == NULL)\n\t\treturn 0;\n\n\tTAILQ_FOREACH(rap, ctx->ra_routers, next) {\n\t\tif (rap->expired)\n\t\t\tcontinue;\n\n\t\t/* add rfc4191 route information routes */\n\t\tTAILQ_FOREACH(rinfo, &rap->rinfos, next) {\n\t\t\tif (rinfo->lifetime == 0)\n\t\t\t\tcontinue;\n\t\t\tif ((rt = inet6_makeroute(rap->iface, rap)) == NULL)\n\t\t\t\tcontinue;\n\n\t\t\tin6_addr_fromprefix(&netmask, rinfo->prefix_len);\n\n\t\t\tsa_in6_init(&rt->rt_dest, &rinfo->prefix);\n\t\t\tsa_in6_init(&rt->rt_netmask, &netmask);\n\t\t\tsa_in6_init(&rt->rt_gateway, &rap->from);\n\t\t\trt->rt_dflags |= RTDF_RA;\n#ifdef HAVE_ROUTE_PREF\n\t\t\trt->rt_pref = ipv6nd_rtpref(rinfo->flags);\n#endif\n#ifdef HAVE_ROUTE_LIFETIME\n\t\t\trt->rt_aquired = rinfo->acquired;\n\t\t\trt->rt_lifetime = rinfo->lifetime,\n#endif\n\t\t\trt_proto_add(routes, rt);\n\t\t}\n\n\t\t/* add subnet routes */\n\t\tTAILQ_FOREACH(addr, &rap->addrs, next) {\n\t\t\tif (addr->prefix_vltime == 0)\n\t\t\t\tcontinue;\n\t\t\trt = inet6_makeprefix(rap->iface, rap, addr);\n\t\t\tif (rt) {\n\t\t\t\trt->rt_dflags |= RTDF_RA;\n#ifdef HAVE_ROUTE_PREF\n\t\t\t\trt->rt_pref = ipv6nd_rtpref(rap->flags);\n#endif\n#ifdef HAVE_ROUTE_LIFETIME\n\t\t\t\trt->rt_aquired = addr->acquired;\n\t\t\t\trt->rt_lifetime = addr->prefix_vltime;\n#endif\n\n\t\t\t\trt_proto_add(routes, rt);\n\t\t\t}\n\t\t}\n\n\t\t/* add default route */\n\t\tif (rap->lifetime == 0)\n\t\t\tcontinue;\n\t\t/*\n\t\t * We only want to install a default route if we have\n\t\t * an address that we can use over it.\n\t\t * If we don't have any global addresses then the link-local\n\t\t * address would be used instead and we wouldn't reach\n\t\t * our destination and even if we could, they wouldn't\n\t\t * be able to reply back to us.\n\t\t * This avoids timeouts on badly configured IPv6 setups\n\t\t * where there is a default router but it or a DHCPv6 server\n\t\t * doesn't hand out an address.\n\t\t * If an address appears from anywhere, dhcpcd will spot this\n\t\t * and then add the default like.\n\t\t * Likewise, if all global addresses are removed then dhcpcd\n\t\t * will remove the default route.\n\t\t */\n\t\tif (ipv6_anyglobal(rap->iface) == NULL)\n\t\t\tcontinue;\n\t\trt = inet6_makerouter(rap);\n\t\tif (rt == NULL)\n\t\t\tcontinue;\n\t\trt->rt_dflags |= RTDF_RA;\n#ifdef HAVE_ROUTE_PREF\n\t\trt->rt_pref = ipv6nd_rtpref(rap->flags);\n#endif\n#ifdef HAVE_ROUTE_LIFETIME\n\t\trt->rt_aquired = rap->acquired;\n\t\trt->rt_lifetime = rap->lifetime;\n#endif\n\n\t\trt_proto_add(routes, rt);\n\t}\n\treturn 0;\n}\n\n#ifdef DHCP6\nstatic int\ninet6_dhcproutes(rb_tree_t *routes, struct dhcpcd_ctx *ctx, enum DH6S dstate)\n{\n\tstruct interface *ifp;\n\tconst struct dhcp6_state *d6_state;\n\tconst struct ipv6_addr *ia;\n\tstruct rt *rt;\n\n\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\td6_state = D6_CSTATE(ifp);\n\t\tif (d6_state == NULL)\n\t\t\tcontinue;\n\n\t\t// Don't test the actual state as we could\n\t\t// be between states with still valid routes\n\n\t\tTAILQ_FOREACH(ia, &d6_state->addrs, next) {\n\t\t\tif (dstate == DH6S_DELEGATED) {\n\t\t\t\t// Reject route won't have IPV6_AF_ADDED\n\t\t\t\tif (!(ia->flags & IPV6_AF_PFXDELEGATION))\n\t\t\t\t\tcontinue;\n\t\t\t} else if (!(ia->flags & IPV6_AF_ADDED))\n\t\t\t\tcontinue;\n\n\t\t\trt = inet6_makeprefix(ifp, NULL, ia);\n\t\t\tif (rt == NULL)\n\t\t\t\tcontinue;\n\t\t\trt->rt_dflags |= RTDF_DHCP;\n#ifdef HAVE_ROUTE_LIFETIME\n\t\t\trt->rt_aquired = ia->acquired;\n\t\t\trt->rt_lifetime = ia->prefix_vltime;\n#endif\n\t\t\trt_proto_add(routes, rt);\n\t\t}\n\t}\n\treturn 0;\n}\n#endif\n\nbool\ninet6_getroutes(struct dhcpcd_ctx *ctx, rb_tree_t *routes)\n{\n\t/* Should static take priority? */\n\tif (inet6_staticroutes(routes, ctx) == -1)\n\t\treturn false;\n\n\t/* First add reachable routers and their prefixes */\n\tif (inet6_raroutes(routes, ctx) == -1)\n\t\treturn false;\n\n#ifdef DHCP6\n\t/* We have no way of knowing if prefixes added by DHCP are reachable\n\t * or not, so we have to assume they are.\n\t * Add bound before delegated so we can prefer interfaces better. */\n\tif (inet6_dhcproutes(routes, ctx, DH6S_BOUND) == -1)\n\t\treturn false;\n\tif (inet6_dhcproutes(routes, ctx, DH6S_DELEGATED) == -1)\n\t\treturn false;\n#endif\n\n\treturn true;\n}\n"
  },
  {
    "path": "src/ipv6.h",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef IPV6_H\n#define IPV6_H\n\n#include <sys/uio.h>\n\n#include <netinet/in.h>\n\n#include \"config.h\"\n#include \"if.h\"\n\n#ifndef __linux__\n#if !defined(__QNX__) && !defined(__sun)\n#include <sys/endian.h>\n#endif\n#include <net/if.h>\n#ifndef __sun\n#include <netinet6/in6_var.h>\n#endif\n#endif\n\n#define EUI64_GBIT 0x01\n#define EUI64_UBIT 0x02\n#define EUI64_TO_IFID(in6)                       \\\n\tdo {                                     \\\n\t\t(in6)->s6_addr[8] ^= EUI64_UBIT; \\\n\t} while (0)\n#define EUI64_GROUP(in6) ((in6)->s6_addr[8] & EUI64_GBIT)\n\n#ifndef ND6_INFINITE_LIFETIME\n#define ND6_INFINITE_LIFETIME ((uint32_t)~0)\n#endif\n\n/* RFC4941 constants */\n#define TEMP_VALID_LIFETIME\t604800 /* 1 week */\n#define TEMP_PREFERRED_LIFETIME 86400  /* 1 day */\n#define REGEN_ADVANCE\t\t5      /* seconds */\n#define MAX_DESYNC_FACTOR\t600    /* 10 minutes */\n#define TEMP_IDGEN_RETRIES\t3\n\n/* RFC7217 constants */\n#define IDGEN_RETRIES 3\n#define IDGEN_DELAY   1 /* second */\n\n/* Interface identifier length. Prefix + this == 128 for autoconf */\n#define ipv6_ifidlen(ifp) 64\n#define IA6_CANAUTOCONF(ia) \\\n\t((ia)->prefix_len + ipv6_ifidlen((ia)->iface) == 128)\n\n#ifndef IN6_ARE_MASKED_ADDR_EQUAL\n#define IN6_ARE_MASKED_ADDR_EQUAL(d, a, m)                                     \\\n\t((((d)->s6_addr32[0] ^ (a)->s6_addr32[0]) & (m)->s6_addr32[0]) == 0 && \\\n\t    (((d)->s6_addr32[1] ^ (a)->s6_addr32[1]) & (m)->s6_addr32[1]) ==   \\\n\t\t0 &&                                                           \\\n\t    (((d)->s6_addr32[2] ^ (a)->s6_addr32[2]) & (m)->s6_addr32[2]) ==   \\\n\t\t0 &&                                                           \\\n\t    (((d)->s6_addr32[3] ^ (a)->s6_addr32[3]) & (m)->s6_addr32[3]) ==   \\\n\t\t0)\n#endif\n\n#ifndef IN6ADDR_LINKLOCAL_ALLNODES_INIT\n#define IN6ADDR_LINKLOCAL_ALLNODES_INIT                            \\\n\t{                                                          \\\n\t\t{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\t\\\n\t    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}      \\\n\t}\n#endif\n#ifndef IN6ADDR_LINKLOCAL_ALLROUTERS_INIT\n#define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT                          \\\n\t{                                                          \\\n\t\t{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\t\\\n\t    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }}      \\\n\t}\n#endif\n\n/*\n * BSD kernels don't inform userland of DAD results.\n * See the discussion here:\n *    http://mail-index.netbsd.org/tech-net/2013/03/15/msg004019.html\n */\n#ifndef __linux__\n/* We guard here to avoid breaking a compile on linux ppc-64 headers */\n#include <sys/param.h>\n#endif\n#ifdef BSD\n#define IPV6_POLLADDRFLAG\n#endif\n\n/* This was fixed in NetBSD */\n#if (defined(__DragonFly_version) && __DragonFly_version >= 500704) || \\\n    (defined(__NetBSD_Version__) && __NetBSD_Version__ >= 699002000)\n#undef IPV6_POLLADDRFLAG\n#endif\n\n/* Of course OpenBSD has their own special name. */\n#if !defined(IN6_IFF_TEMPORARY) && defined(IN6_IFF_PRIVACY)\n#define IN6_IFF_TEMPORARY IN6_IFF_PRIVACY\n#endif\n\n#ifdef __sun\n/* Solaris lacks these defines.\n * While it supports DaD, to seems to only expose IFF_DUPLICATE\n * so we have no way of knowing if it's tentative or not.\n * I don't even know if Solaris has any special treatment for tentative. */\n#define IN6_IFF_TENTATIVE  0x02\n#define IN6_IFF_DUPLICATED 0x04\n#define IN6_IFF_DETACHED   0x00\n#endif\n\n#define IN6_IFF_NOTUSEABLE \\\n\t(IN6_IFF_TENTATIVE | IN6_IFF_DUPLICATED | IN6_IFF_DETACHED)\n\n/*\n * If dhcpcd handles RA processing instead of the kernel, the kernel needs\n * to either allow userland to set temporary addresses or mark an address\n * for the kernel to manage temporary addresses from.\n * If the kernel allows the former, a global #define is needed, otherwise\n * the address marking will be handled in the platform specific address handler.\n *\n * Some BSDs do not allow userland to set temporary addresses.\n * Linux-3.18 allows the marking of addresses from which to manage temp addrs.\n */\n#if defined(IN6_IFF_TEMPORARY) && !defined(__linux__)\n#define IPV6_MANAGETEMPADDR\n#endif\n\n#ifdef __linux__\n/* Match Linux defines to BSD */\n#ifdef IFA_F_TEMPORARY\n#define IN6_IFF_TEMPORARY IFA_F_TEMPORARY\n#endif\n#ifdef IFA_F_OPTIMISTIC\n#define IN6_IFF_TENTATIVE (IFA_F_TENTATIVE | IFA_F_OPTIMISTIC)\n#else\n#define IN6_IFF_TENTATIVE (IFA_F_TENTATIVE | 0x04)\n#endif\n#ifdef IF_F_DADFAILED\n#define IN6_IFF_DUPLICATED IFA_F_DADFAILED\n#else\n#define IN6_IFF_DUPLICATED 0x08\n#endif\n#define IN6_IFF_DETACHED 0\n#endif\n\n#ifdef INET6\nTAILQ_HEAD(ipv6_addrhead, ipv6_addr);\nstruct ipv6_addr {\n\tTAILQ_ENTRY(ipv6_addr) next;\n\tstruct interface *iface;\n\tstruct in6_addr prefix;\n\tuint8_t prefix_len;\n\tuint32_t prefix_vltime;\n\tuint32_t prefix_pltime;\n\tstruct timespec created;\n\tstruct timespec acquired;\n\tstruct in6_addr addr;\n\tstruct in6_addr dstaddr;\n\tint addr_flags;\n\tunsigned int flags;\n\tchar saddr[INET6_ADDRSTRLEN];\n\tuint8_t iaid[4];\n\tuint16_t ia_type;\n\tint dhcp6_fd;\n\n#ifndef SMALL\n\tstruct ipv6_addr *delegating_prefix;\n\tstruct ipv6_addrhead pd_pfxs;\n\tTAILQ_ENTRY(ipv6_addr) pd_next;\n\n\tuint8_t prefix_exclude_len;\n\tstruct in6_addr prefix_exclude;\n#endif\n\n\tvoid (*dadcallback)(void *);\n\tint dadcounter;\n\n\tstruct nd_neighbor_advert *na;\n\tsize_t na_len;\n\tint na_count;\n\n#ifdef ALIAS_ADDR\n\tchar alias[IF_NAMESIZE];\n#endif\n};\n\n#define IPV6_AF_ONLINK\t      (1U << 0)\n#define IPV6_AF_NEW\t      (1U << 1)\n#define IPV6_AF_STALE\t      (1U << 2)\n#define IPV6_AF_ADDED\t      (1U << 3)\n#define IPV6_AF_AUTOCONF      (1U << 4)\n#define IPV6_AF_DADCOMPLETED  (1U << 5)\n#define IPV6_AF_PFXDELEGATION (1U << 6)\n#define IPV6_AF_DELEGATED     (1U << 7)\n#define IPV6_AF_NOREJECT      (1U << 8)\n#define IPV6_AF_REQUEST\t      (1U << 9)\n#define IPV6_AF_STATIC\t      (1U << 10)\n#define IPV6_AF_DELEGATEDLOG  (1U << 11)\n#define IPV6_AF_RAPFX\t      (1U << 12)\n#define IPV6_AF_EXTENDED      (1U << 13)\n#define IPV6_AF_REGEN\t      (1U << 14)\n#define IPV6_AF_ROUTER\t      (1U << 15)\n#define IPV6_AF_ADVERTISED    (1U << 16)\n#ifdef IPV6_MANAGETEMPADDR\n#define IPV6_AF_TEMPORARY (1U << 17)\n#endif\n\nstruct ll_callback {\n\tTAILQ_ENTRY(ll_callback) next;\n\tvoid (*callback)(void *);\n\tvoid *arg;\n};\nTAILQ_HEAD(ll_callback_head, ll_callback);\n\nstruct ipv6_state {\n\tstruct ipv6_addrhead addrs;\n\tstruct ll_callback_head ll_callbacks;\n\n#ifdef IPV6_MANAGETEMPADDR\n\tuint32_t desync_factor;\n#endif\n};\n\n#define IPV6_STATE(ifp) ((struct ipv6_state *)(ifp)->if_data[IF_DATA_IPV6])\n#define IPV6_CSTATE(ifp) \\\n\t((const struct ipv6_state *)(ifp)->if_data[IF_DATA_IPV6])\n#define IPV6_STATE_RUNNING(ifp) ipv6_staticdadcompleted((ifp))\n\nint ipv6_init(struct dhcpcd_ctx *);\nint ipv6_makestableprivate(struct in6_addr *, const struct in6_addr *, int,\n    const struct interface *, int *);\nint ipv6_makeaddr(struct in6_addr *, struct interface *,\n    const struct in6_addr *, int, unsigned int);\nint ipv6_mask(struct in6_addr *, int);\nuint8_t ipv6_prefixlen(const struct in6_addr *);\nint ipv6_userprefix(const struct in6_addr *, short prefix_len,\n    uint64_t user_number, struct in6_addr *result, short result_len);\nvoid ipv6_checkaddrflags(void *);\nvoid ipv6_markaddrsstale(struct interface *, unsigned int);\nvoid ipv6_deletestaleaddrs(struct interface *);\nint ipv6_addaddr(struct ipv6_addr *, struct timespec *);\nint ipv6_doaddr(struct ipv6_addr *, struct timespec *);\nssize_t ipv6_addaddrs(struct ipv6_addrhead *addrs);\nvoid ipv6_deleteaddr(struct ipv6_addr *);\nvoid ipv6_freedrop_addrs(struct ipv6_addrhead *, int, unsigned int,\n    const struct interface *);\nvoid ipv6_handleifa(struct dhcpcd_ctx *ctx, int, struct if_head *, const char *,\n    const struct in6_addr *, uint8_t, const struct in6_addr *, int, pid_t);\nint ipv6_handleifa_addrs(int, struct ipv6_addrhead *, const struct ipv6_addr *,\n    pid_t);\nstruct ipv6_addr *ipv6_iffindaddr(struct interface *, const struct in6_addr *,\n    int);\nint ipv6_hasaddr(const struct interface *);\nstruct ipv6_addr *ipv6_anyglobal(struct interface *);\nint ipv6_findaddrmatch(const struct ipv6_addr *, const struct in6_addr *,\n    unsigned int);\nstruct ipv6_addr *ipv6_findaddr(struct dhcpcd_ctx *, const struct in6_addr *,\n    unsigned int);\nstruct ipv6_addr *ipv6_findmaskaddr(struct dhcpcd_ctx *,\n    const struct in6_addr *);\nstruct ipv6_addr *ipv6_finddstaddr(struct dhcpcd_ctx *,\n    const struct in6_addr *);\n#define ipv6_linklocal(ifp) ipv6_iffindaddr((ifp), NULL, IN6_IFF_NOTUSEABLE)\nint ipv6_addlinklocalcallback(struct interface *, void (*)(void *), void *);\nvoid ipv6_setscope(struct sockaddr_in6 *, unsigned int);\nunsigned int ipv6_getscope(const struct sockaddr_in6 *);\nstruct ipv6_addr *ipv6_newaddr(struct interface *, const struct in6_addr *,\n    uint8_t, unsigned int);\nvoid ipv6_freeaddr(struct ipv6_addr *);\nvoid ipv6_freedrop(struct interface *, int);\n#define ipv6_free(ifp) ipv6_freedrop((ifp), 0)\n#define ipv6_drop(ifp) ipv6_freedrop((ifp), 2)\n\n#ifdef IPV6_MANAGETEMPADDR\nstruct ipv6_addr *ipv6_createtempaddr(struct ipv6_addr *,\n    const struct timespec *);\nstruct ipv6_addr *ipv6_settemptime(struct ipv6_addr *, int);\nvoid ipv6_addtempaddrs(struct interface *, struct timespec *);\nvoid ipv6_regentempaddrs(void *);\n#endif\n\nint ipv6_start(struct interface *);\nint ipv6_staticdadcompleted(const struct interface *);\nint ipv6_startstatic(struct interface *);\nssize_t ipv6_env(FILE *, const char *, const struct interface *);\nvoid ipv6_ctxfree(struct dhcpcd_ctx *);\nbool inet6_getroutes(struct dhcpcd_ctx *, rb_tree_t *);\n#endif /* INET6 */\n\n#endif /* INET6_H */\n"
  },
  {
    "path": "src/ipv6nd.c",
    "content": "/*\n * dhcpcd - IPv6 ND handling\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/param.h>\n#include <sys/ioctl.h>\n#include <sys/socket.h>\n\n#include <net/if.h>\n#include <net/route.h>\n#include <netinet/in.h>\n#include <netinet/ip6.h>\n#include <netinet/icmp6.h>\n\n#include <assert.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <stddef.h>\n#include <stdlib.h>\n#include <string.h>\n#include <syslog.h>\n#include <unistd.h>\n\n#define ELOOP_QUEUE ELOOP_IPV6ND\n#include \"common.h\"\n#include \"dhcp-common.h\"\n#include \"dhcp6.h\"\n#include \"dhcpcd.h\"\n#include \"eloop.h\"\n#include \"if.h\"\n#include \"ipv6.h\"\n#include \"ipv6nd.h\"\n#include \"logerr.h\"\n#include \"privsep.h\"\n#include \"route.h\"\n#include \"script.h\"\n\n/* Debugging Router Solicitations is a lot of spam, so disable it */\n// #define DEBUG_RS\n\n#ifndef ND_RA_FLAG_HOME_AGENT\n#define ND_RA_FLAG_HOME_AGENT 0x20 /* Home Agent flag in RA */\n#endif\n#ifndef ND_RA_FLAG_PROXY\n#define ND_RA_FLAG_PROXY 0x04 /* Proxy */\n#endif\n#ifndef ND_OPT_PI_FLAG_ROUTER\n#define ND_OPT_PI_FLAG_ROUTER 0x20 /* Router flag in PI */\n#endif\n\n#ifndef ND_OPT_RI\n#define ND_OPT_RI 24\nstruct nd_opt_ri { /* Route Information option RFC4191 */\n\tuint8_t nd_opt_ri_type;\n\tuint8_t nd_opt_ri_len;\n\tuint8_t nd_opt_ri_prefixlen;\n\tuint8_t nd_opt_ri_flags_reserved;\n\tuint32_t nd_opt_ri_lifetime;\n\tstruct in6_addr nd_opt_ri_prefix;\n};\n__CTASSERT(sizeof(struct nd_opt_ri) == 24);\n#define OPT_RI_FLAG_PREFERENCE(flags) ((flags & 0x18) >> 3)\n#endif\n\n#ifndef ND_OPT_RDNSS\n#define ND_OPT_RDNSS 25\nstruct nd_opt_rdnss { /* RDNSS option RFC 6106 */\n\tuint8_t nd_opt_rdnss_type;\n\tuint8_t nd_opt_rdnss_len;\n\tuint16_t nd_opt_rdnss_reserved;\n\tuint32_t nd_opt_rdnss_lifetime;\n\t/* followed by list of IP prefixes */\n};\n__CTASSERT(sizeof(struct nd_opt_rdnss) == 8);\n#endif\n\n#ifndef ND_OPT_DNSSL\n#define ND_OPT_DNSSL 31\nstruct nd_opt_dnssl { /* DNSSL option RFC 6106 */\n\tuint8_t nd_opt_dnssl_type;\n\tuint8_t nd_opt_dnssl_len;\n\tuint16_t nd_opt_dnssl_reserved;\n\tuint32_t nd_opt_dnssl_lifetime;\n\t/* followed by list of DNS servers */\n};\n__CTASSERT(sizeof(struct nd_opt_dnssl) == 8);\n#endif\n\n/* Impossible options, so we can easily add extras */\n#define _ND_OPT_PREFIX_ADDR 255 + 1\n\n/* Minimal IPv6 MTU */\n#ifndef IPV6_MMTU\n#define IPV6_MMTU 1280\n#endif\n\n#ifndef ND_RA_FLAG_RTPREF_HIGH\n#define ND_RA_FLAG_RTPREF_MASK\t 0x18\n#define ND_RA_FLAG_RTPREF_HIGH\t 0x08\n#define ND_RA_FLAG_RTPREF_MEDIUM 0x00\n#define ND_RA_FLAG_RTPREF_LOW\t 0x18\n#define ND_RA_FLAG_RTPREF_RSV\t 0x10\n#endif\n\n#define EXPIRED_MAX                              \\\n\t5 /* Remember 5 expired routers to avoid \\\n\t     logspam. */\n\n#define MIN_RANDOM_FACTOR   500\t\t\t     /* millisecs */\n#define MAX_RANDOM_FACTOR   1500\t\t     /* millisecs */\n#define MIN_RANDOM_FACTOR_U MIN_RANDOM_FACTOR * 1000 /* usecs */\n#define MAX_RANDOM_FACTOR_U MAX_RANDOM_FACTOR * 1000 /* usecs */\n\n#if BYTE_ORDER == BIG_ENDIAN\n#define IPV6_ADDR_INT32_ONE 1\n#define IPV6_ADDR_INT16_MLL 0xff02\n#elif BYTE_ORDER == LITTLE_ENDIAN\n#define IPV6_ADDR_INT32_ONE 0x01000000\n#define IPV6_ADDR_INT16_MLL 0x02ff\n#endif\n\n/* Debugging Neighbor Solicitations is a lot of spam, so disable it */\n// #define DEBUG_NS\n//\n\nstatic void ipv6nd_handledata(void *, unsigned short);\nstatic struct routeinfo *routeinfo_findalloc(struct ra *,\n    const struct in6_addr *, uint8_t);\nstatic void routeinfohead_free(struct routeinfohead *);\n\n/*\n * Android ships buggy ICMP6 filter headers.\n * Supply our own until they fix their shit.\n * References:\n *     https://android-review.googlesource.com/#/c/58438/\n *     http://code.google.com/p/android/issues/original?id=32621&seq=24\n */\n#ifdef __ANDROID__\n#undef ICMP6_FILTER_WILLPASS\n#undef ICMP6_FILTER_WILLBLOCK\n#undef ICMP6_FILTER_SETPASS\n#undef ICMP6_FILTER_SETBLOCK\n#undef ICMP6_FILTER_SETPASSALL\n#undef ICMP6_FILTER_SETBLOCKALL\n#define ICMP6_FILTER_WILLPASS(type, filterp) \\\n\t((((filterp)->icmp6_filt[(type) >> 5]) & (1 << ((type) & 31))) == 0)\n#define ICMP6_FILTER_WILLBLOCK(type, filterp) \\\n\t((((filterp)->icmp6_filt[(type) >> 5]) & (1 << ((type) & 31))) != 0)\n#define ICMP6_FILTER_SETPASS(type, filterp) \\\n\t((((filterp)->icmp6_filt[(type) >> 5]) &= ~(1 << ((type) & 31))))\n#define ICMP6_FILTER_SETBLOCK(type, filterp) \\\n\t((((filterp)->icmp6_filt[(type) >> 5]) |= (1 << ((type) & 31))))\n#define ICMP6_FILTER_SETPASSALL(filterp) \\\n\tmemset(filterp, 0, sizeof(struct icmp6_filter));\n#define ICMP6_FILTER_SETBLOCKALL(filterp) \\\n\tmemset(filterp, 0xff, sizeof(struct icmp6_filter));\n#endif\n\n/* Support older systems with different defines */\n#if !defined(IPV6_RECVHOPLIMIT) && defined(IPV6_HOPLIMIT)\n#define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT\n#endif\n#if !defined(IPV6_RECVPKTINFO) && defined(IPV6_PKTINFO)\n#define IPV6_RECVPKTINFO IPV6_PKTINFO\n#endif\n\n/* Handy defines */\n#define ipv6nd_free_ra(ra) ipv6nd_freedrop_ra((ra), 0)\n#define ipv6nd_drop_ra(ra) ipv6nd_freedrop_ra((ra), 1)\n\n/* Clear these addrflags on receipt of a new RA before adding the new flags\n * dervived from the RA. */\n#define RA_STALE_FLAGS \\\n\t(IPV6_AF_ONLINK | IPV6_AF_AUTOCONF | IPV6_AF_ROUTER | IPV6_AF_STALE)\n\nvoid\nipv6nd_printoptions(const struct dhcpcd_ctx *ctx, const struct dhcp_opt *opts,\n    size_t opts_len)\n{\n\tsize_t i, j;\n\tconst struct dhcp_opt *opt, *opt2;\n\tint cols;\n\n\tfor (i = 0, opt = ctx->nd_opts; i < ctx->nd_opts_len; i++, opt++) {\n\t\tfor (j = 0, opt2 = opts; j < opts_len; j++, opt2++)\n\t\t\tif (opt2->option == opt->option)\n\t\t\t\tbreak;\n\t\tif (j == opts_len) {\n\t\t\tcols = printf(\"%03d %s\", opt->option, opt->var);\n\t\t\tdhcp_print_option_encoding(opt, cols);\n\t\t}\n\t}\n\tfor (i = 0, opt = opts; i < opts_len; i++, opt++) {\n\t\tcols = printf(\"%03d %s\", opt->option, opt->var);\n\t\tdhcp_print_option_encoding(opt, cols);\n\t}\n}\n\nint\nipv6nd_open(bool recv)\n{\n\tint fd, on;\n\tstruct icmp6_filter filt;\n\n\tfd = xsocket(PF_INET6, SOCK_RAW | SOCK_CXNB, IPPROTO_ICMPV6);\n\tif (fd == -1)\n\t\treturn -1;\n\n\tICMP6_FILTER_SETBLOCKALL(&filt);\n\n\t/* RFC4861 4.1 */\n\ton = 255;\n\tif (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &on,\n\t\tsizeof(on)) == -1)\n\t\tgoto eexit;\n\n\tif (recv) {\n\t\ton = 1;\n\t\tif (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,\n\t\t\tsizeof(on)) == -1)\n\t\t\tgoto eexit;\n\n\t\ton = 1;\n\t\tif (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,\n\t\t\tsizeof(on)) == -1)\n\t\t\tgoto eexit;\n\n\t\tICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);\n\n#ifdef SO_RERROR\n\t\ton = 1;\n\t\tif (setsockopt(fd, SOL_SOCKET, SO_RERROR, &on, sizeof(on)) ==\n\t\t    -1)\n\t\t\tgoto eexit;\n#endif\n\t}\n\n\tif (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, sizeof(filt)) ==\n\t    -1)\n\t\tgoto eexit;\n\n\treturn fd;\n\neexit:\n\tclose(fd);\n\treturn -1;\n}\n\n#ifdef __sun\nint\nipv6nd_openif(struct interface *ifp)\n{\n\tint fd;\n\tstruct ipv6_mreq mreq = {\n\t\t.ipv6mr_multiaddr = IN6ADDR_LINKLOCAL_ALLNODES_INIT,\n\t\t.ipv6mr_interface = ifp->index\n\t};\n\tstruct rs_state *state = RS_STATE(ifp);\n\tuint_t ifindex = ifp->index;\n\n\tif (state->nd_fd != -1)\n\t\treturn state->nd_fd;\n\n\tfd = ipv6nd_open(true);\n\tif (fd == -1)\n\t\treturn -1;\n\n\tif (setsockopt(fd, IPPROTO_IPV6, IPV6_BOUND_IF, &ifindex,\n\t\tsizeof(ifindex)) == -1) {\n\t\tclose(fd);\n\t\treturn -1;\n\t}\n\n\tif (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq,\n\t\tsizeof(mreq)) == -1) {\n\t\tclose(fd);\n\t\treturn -1;\n\t}\n\n\tif (eloop_event_add(ifp->ctx->eloop, fd, ELE_READ, ipv6nd_handledata,\n\t\tifp) == -1) {\n\t\tclose(fd);\n\t\treturn -1;\n\t}\n\n\tstate->nd_fd = fd;\n\treturn fd;\n}\n#endif\n\nstatic int\nipv6nd_makersprobe(struct interface *ifp)\n{\n\tstruct rs_state *state;\n\tstruct nd_router_solicit *rs;\n\n\tstate = RS_STATE(ifp);\n\tfree(state->rs);\n\tstate->rslen = sizeof(*rs);\n\tif (ifp->hwlen != 0)\n\t\tstate->rslen += (size_t)ROUNDUP8(ifp->hwlen + 2);\n\tstate->rs = calloc(1, state->rslen);\n\tif (state->rs == NULL)\n\t\treturn -1;\n\trs = state->rs;\n\trs->nd_rs_type = ND_ROUTER_SOLICIT;\n\t// rs->nd_rs_code = 0;\n\t// rs->nd_rs_cksum = 0;\n\t// rs->nd_rs_reserved = 0;\n\n\tif (ifp->hwlen != 0) {\n\t\tstruct nd_opt_hdr *nd;\n\n\t\tnd = (struct nd_opt_hdr *)(state->rs + 1);\n\t\tnd->nd_opt_type = ND_OPT_SOURCE_LINKADDR;\n\t\tnd->nd_opt_len = (uint8_t)((ROUNDUP8(ifp->hwlen + 2)) >> 3);\n\t\tmemcpy(nd + 1, ifp->hwaddr, ifp->hwlen);\n\t}\n\treturn 0;\n}\n\nstatic void\nipv6nd_sendrsprobe(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tstruct rs_state *state = RS_STATE(ifp);\n\tstruct sockaddr_in6 dst = {\n\t\t.sin6_family = AF_INET6,\n\t\t.sin6_addr = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT,\n\t\t.sin6_scope_id = ifp->index,\n\t};\n\tstruct iovec iov = { .iov_base = state->rs, .iov_len = state->rslen };\n\tunion {\n\t\tstruct cmsghdr hdr;\n\t\tuint8_t buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];\n\t} cmsgbuf = { .buf = { 0 } };\n\tstruct msghdr msg = {\n\t\t.msg_name = &dst,\n\t\t.msg_namelen = sizeof(dst),\n\t\t.msg_iov = &iov,\n\t\t.msg_iovlen = 1,\n\t\t.msg_control = cmsgbuf.buf,\n\t\t.msg_controllen = sizeof(cmsgbuf.buf),\n\t};\n\tstruct cmsghdr *cm;\n\tstruct in6_pktinfo pi = { .ipi6_ifindex = ifp->index };\n\tint s;\n#ifndef __sun\n\tstruct dhcpcd_ctx *ctx = ifp->ctx;\n#endif\n\n\tif (ipv6_linklocal(ifp) == NULL) {\n\t\tlogdebugx(\"%s: delaying Router Solicitation for LL address\",\n\t\t    ifp->name);\n\t\tipv6_addlinklocalcallback(ifp, ipv6nd_sendrsprobe, ifp);\n\t\treturn;\n\t}\n\n#ifdef HAVE_SA_LEN\n\tdst.sin6_len = sizeof(dst);\n#endif\n\n\t/* Set the outbound interface */\n\tcm = CMSG_FIRSTHDR(&msg);\n\tif (cm == NULL) /* unlikely */\n\t\treturn;\n\tcm->cmsg_level = IPPROTO_IPV6;\n\tcm->cmsg_type = IPV6_PKTINFO;\n\tcm->cmsg_len = CMSG_LEN(sizeof(pi));\n\tmemcpy(CMSG_DATA(cm), &pi, sizeof(pi));\n\n\tlogdebugx(\"%s: sending Router Solicitation\", ifp->name);\n#ifdef PRIVSEP\n\tif (IN_PRIVSEP(ifp->ctx)) {\n\t\tif (ps_inet_sendnd(ifp, &msg) == -1)\n\t\t\tlogerr(__func__);\n\t\tgoto sent;\n\t}\n#endif\n#ifdef __sun\n\tif (state->nd_fd == -1) {\n\t\tif (ipv6nd_openif(ifp) == -1) {\n\t\t\tlogerr(__func__);\n\t\t\treturn;\n\t\t}\n\t}\n\ts = state->nd_fd;\n#else\n\tif (ctx->nd_fd == -1) {\n\t\tctx->nd_fd = ipv6nd_open(true);\n\t\tif (ctx->nd_fd == -1) {\n\t\t\tlogerr(__func__);\n\t\t\treturn;\n\t\t}\n\t\tif (eloop_event_add(ctx->eloop, ctx->nd_fd, ELE_READ,\n\t\t\tipv6nd_handledata, ctx) == -1)\n\t\t\tlogerr(\"%s: eloop_event_add\", __func__);\n\t}\n\ts = ifp->ctx->nd_fd;\n#endif\n\tif (sendmsg(s, &msg, 0) == -1) {\n\t\tlogerr(__func__);\n\t\t/* Allow IPv6ND to continue .... at most a few errors\n\t\t * would be logged.\n\t\t * Generally the error is ENOBUFS when struggling to\n\t\t * associate with an access point. */\n\t}\n\n#ifdef PRIVSEP\nsent:\n#endif\n\tif (state->rsprobes++ < MAX_RTR_SOLICITATIONS)\n\t\teloop_timeout_add_sec(ifp->ctx->eloop,\n\t\t    RTR_SOLICITATION_INTERVAL, ipv6nd_sendrsprobe, ifp);\n\telse\n\t\tlogwarnx(\"%s: no IPv6 Routers available\", ifp->name);\n}\n\nstatic void\nipv6nd_expire(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tstruct ra *rap;\n\n\tif (ifp->ctx->ra_routers == NULL)\n\t\treturn;\n\n\tTAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {\n\t\tif (rap->iface == ifp && rap->willexpire)\n\t\t\trap->doexpire = true;\n\t}\n\tipv6nd_expirera(ifp);\n}\n\nvoid\nipv6nd_startexpire(struct interface *ifp)\n{\n\tstruct ra *rap;\n\n\tif (ifp->ctx->ra_routers == NULL)\n\t\treturn;\n\n\tTAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {\n\t\tif (rap->iface == ifp)\n\t\t\trap->willexpire = true;\n\t}\n\teloop_q_timeout_add_sec(ifp->ctx->eloop, ELOOP_IPV6RA_EXPIRE,\n\t    RTR_CARRIER_EXPIRE, ipv6nd_expire, ifp);\n}\n\nint\nipv6nd_rtpref(uint8_t flags)\n{\n\tswitch (flags & ND_RA_FLAG_RTPREF_MASK) {\n\tcase ND_RA_FLAG_RTPREF_HIGH:\n\t\treturn RTPREF_HIGH;\n\tcase ND_RA_FLAG_RTPREF_MEDIUM:\n\tcase ND_RA_FLAG_RTPREF_RSV:\n\t\treturn RTPREF_MEDIUM;\n\tcase ND_RA_FLAG_RTPREF_LOW:\n\t\treturn RTPREF_LOW;\n\tdefault:\n\t\tlogerrx(\"%s: impossible RA flag %x\", __func__, flags);\n\t\treturn RTPREF_INVALID;\n\t}\n\t/* NOTREACHED */\n}\n\nstatic void\nipv6nd_sortrouters(struct dhcpcd_ctx *ctx)\n{\n\tstruct ra_head sorted_routers = TAILQ_HEAD_INITIALIZER(sorted_routers);\n\tstruct ra *ra1, *ra2;\n\n\twhile ((ra1 = TAILQ_FIRST(ctx->ra_routers)) != NULL) {\n\t\tTAILQ_REMOVE(ctx->ra_routers, ra1, next);\n\t\tTAILQ_FOREACH(ra2, &sorted_routers, next) {\n\t\t\tif (ra1->iface->metric > ra2->iface->metric)\n\t\t\t\tcontinue;\n\t\t\tif (ra1->expired && !ra2->expired)\n\t\t\t\tcontinue;\n\t\t\tif (ra1->willexpire && !ra2->willexpire)\n\t\t\t\tcontinue;\n\t\t\tif (ra1->lifetime == 0 && ra2->lifetime != 0)\n\t\t\t\tcontinue;\n\t\t\tif (!ra1->isreachable && ra2->isreachable)\n\t\t\t\tcontinue;\n\t\t\tif (ipv6nd_rtpref(ra1->flags) <=\n\t\t\t    ipv6nd_rtpref(ra2->flags))\n\t\t\t\tcontinue;\n\t\t\t/* All things being equal, prefer older routers. */\n\t\t\t/* We don't need to check time, becase newer\n\t\t\t * routers are always added to the tail and then\n\t\t\t * sorted. */\n\t\t\tTAILQ_INSERT_BEFORE(ra2, ra1, next);\n\t\t\tbreak;\n\t\t}\n\t\tif (ra2 == NULL)\n\t\t\tTAILQ_INSERT_TAIL(&sorted_routers, ra1, next);\n\t}\n\n\tTAILQ_CONCAT(ctx->ra_routers, &sorted_routers, next);\n}\n\nstatic void\nipv6nd_applyra(struct interface *ifp)\n{\n\tstruct ra *rap;\n\tstruct rs_state *state = RS_STATE(ifp);\n\tstruct ra defra = {\n\t\t.iface = ifp,\n\t\t.hoplimit = IPV6_DEFHLIM,\n\t\t.reachable = REACHABLE_TIME,\n\t\t.retrans = RETRANS_TIMER,\n\t};\n\n\tTAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {\n\t\tif (rap->iface == ifp)\n\t\t\tbreak;\n\t}\n\n\t/* If we have no Router Advertisement, then set default values. */\n\tif (rap == NULL || rap->expired || rap->willexpire)\n\t\trap = &defra;\n\n\tstate->retrans = rap->retrans;\n\tif (if_applyra(rap) == -1 && errno != ENOENT)\n\t\tlogerr(__func__);\n}\n\n/*\n * Neighbour reachability.\n *\n * RFC 4681 6.2.5 says when a node is no longer a router it MUST\n * send a RA with a zero lifetime.\n * All OS's I know of set the NA router flag if they are a router\n * or not and disregard that they are actively advertising or\n * shutting down. If the interface is disabled, it cant't send a NA at all.\n *\n * As such we CANNOT rely on the NA Router flag and MUST use\n * unreachability or receive a RA with a lifetime of zero to remove\n * the node as a default router.\n */\nvoid\nipv6nd_neighbour(struct dhcpcd_ctx *ctx, struct in6_addr *addr, bool reachable)\n{\n\tstruct ra *rap, *rapr;\n\n\tif (ctx->ra_routers == NULL)\n\t\treturn;\n\n\tTAILQ_FOREACH(rap, ctx->ra_routers, next) {\n\t\tif (IN6_ARE_ADDR_EQUAL(&rap->from, addr))\n\t\t\tbreak;\n\t}\n\n\tif (rap == NULL || rap->expired || rap->isreachable == reachable)\n\t\treturn;\n\n\trap->isreachable = reachable;\n\tloginfox(\"%s: %s is %s\", rap->iface->name, rap->sfrom,\n\t    reachable ? \"reachable again\" : \"unreachable\");\n\n\t/* See if we can install a reachable default router. */\n\tipv6nd_sortrouters(ctx);\n\tipv6nd_applyra(rap->iface);\n\trt_build(ctx, AF_INET6);\n\n\tif (reachable)\n\t\treturn;\n\n\t/* If we have no reachable default routers, try and solicit one. */\n\tTAILQ_FOREACH(rapr, ctx->ra_routers, next) {\n\t\tif (rap == rapr || rap->iface != rapr->iface)\n\t\t\tcontinue;\n\t\tif (rapr->isreachable && !rapr->expired && rapr->lifetime)\n\t\t\tbreak;\n\t}\n\n\tif (rapr == NULL)\n\t\tipv6nd_startrs(rap->iface);\n}\n\nconst struct ipv6_addr *\nipv6nd_iffindaddr(const struct interface *ifp, const struct in6_addr *addr,\n    unsigned int flags)\n{\n\tstruct ra *rap;\n\tstruct ipv6_addr *ap;\n\n\tif (ifp->ctx->ra_routers == NULL)\n\t\treturn NULL;\n\n\tTAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {\n\t\tif (rap->iface != ifp)\n\t\t\tcontinue;\n\t\tTAILQ_FOREACH(ap, &rap->addrs, next) {\n\t\t\tif (ipv6_findaddrmatch(ap, addr, flags))\n\t\t\t\treturn ap;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstruct ipv6_addr *\nipv6nd_findaddr(struct dhcpcd_ctx *ctx, const struct in6_addr *addr,\n    unsigned int flags)\n{\n\tstruct ra *rap;\n\tstruct ipv6_addr *ap;\n\n\tif (ctx->ra_routers == NULL)\n\t\treturn NULL;\n\n\tTAILQ_FOREACH(rap, ctx->ra_routers, next) {\n\t\tTAILQ_FOREACH(ap, &rap->addrs, next) {\n\t\t\tif (ipv6_findaddrmatch(ap, addr, flags))\n\t\t\t\treturn ap;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic struct ipv6_addr *\nipv6nd_rapfindprefix(struct ra *rap, const struct in6_addr *pfx, uint8_t pfxlen)\n{\n\tstruct ipv6_addr *ia;\n\n\tTAILQ_FOREACH(ia, &rap->addrs, next) {\n\t\tif (ia->prefix_vltime == 0)\n\t\t\tcontinue;\n\t\tif (ia->prefix_len == pfxlen &&\n\t\t    IN6_ARE_ADDR_EQUAL(&ia->prefix, pfx))\n\t\t\tbreak;\n\t}\n\treturn ia;\n}\n\nstruct ipv6_addr *\nipv6nd_iffindprefix(struct interface *ifp, const struct in6_addr *pfx,\n    uint8_t pfxlen)\n{\n\tstruct ra *rap;\n\tstruct ipv6_addr *ia;\n\n\tia = NULL;\n\tTAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {\n\t\tif (rap->iface != ifp)\n\t\t\tcontinue;\n\t\tia = ipv6nd_rapfindprefix(rap, pfx, pfxlen);\n\t\tif (ia != NULL)\n\t\t\tbreak;\n\t}\n\treturn ia;\n}\n\nstatic void\nipv6nd_removefreedrop_ra(struct ra *rap, int remove_ra, int drop_ra)\n{\n\tstruct dhcpcd_ctx *ctx = rap->iface->ctx;\n\n\teloop_q_timeout_delete(ctx->eloop, ELOOP_IPV6RA_EXPIRE, NULL,\n\t    rap->iface);\n\teloop_timeout_delete(ctx->eloop, NULL, rap->iface);\n\teloop_timeout_delete(ctx->eloop, NULL, rap);\n\tif (remove_ra)\n\t\tTAILQ_REMOVE(ctx->ra_routers, rap, next);\n\tipv6_freedrop_addrs(&rap->addrs, drop_ra, 0, NULL);\n\trouteinfohead_free(&rap->rinfos);\n\tfree(rap->data);\n\tfree(rap);\n}\n\nstatic void\nipv6nd_freedrop_ra(struct ra *rap, int drop)\n{\n\tipv6nd_removefreedrop_ra(rap, 1, drop);\n}\n\nssize_t\nipv6nd_free(struct interface *ifp)\n{\n\tstruct rs_state *state;\n\tstruct ra *rap, *ran;\n\tstruct dhcpcd_ctx *ctx;\n\tssize_t n;\n\n\tstate = RS_STATE(ifp);\n\tif (state == NULL)\n\t\treturn 0;\n\n\tctx = ifp->ctx;\n#ifdef __sun\n\teloop_event_delete(ctx->eloop, state->nd_fd);\n\tclose(state->nd_fd);\n#endif\n\tfree(state->rs);\n\tfree(state);\n\tifp->if_data[IF_DATA_IPV6ND] = NULL;\n\tn = 0;\n\tTAILQ_FOREACH_SAFE(rap, ifp->ctx->ra_routers, next, ran) {\n\t\tif (rap->iface == ifp) {\n\t\t\tipv6nd_free_ra(rap);\n\t\t\tn++;\n\t\t}\n\t}\n\n#ifndef __sun\n\t/* If we don't have any more IPv6 enabled interfaces,\n\t * close the global socket and release resources */\n\tTAILQ_FOREACH(ifp, ctx->ifaces, next) {\n\t\tif (RS_STATE(ifp))\n\t\t\tbreak;\n\t}\n\tif (ifp == NULL) {\n\t\tif (ctx->nd_fd != -1) {\n\t\t\teloop_event_delete(ctx->eloop, ctx->nd_fd);\n\t\t\tclose(ctx->nd_fd);\n\t\t\tctx->nd_fd = -1;\n\t\t}\n\t}\n#endif\n\n\treturn n;\n}\n\nstatic void\nipv6nd_scriptrun(struct ra *rap)\n{\n\tint hasdns, hasaddress;\n\tstruct ipv6_addr *ap;\n\n\thasaddress = 0;\n\t/* If all addresses have completed DAD run the script */\n\tTAILQ_FOREACH(ap, &rap->addrs, next) {\n\t\tif ((ap->flags & (IPV6_AF_AUTOCONF | IPV6_AF_ADDED)) ==\n\t\t    (IPV6_AF_AUTOCONF | IPV6_AF_ADDED)) {\n\t\t\thasaddress = 1;\n\t\t\tif (!(ap->flags & IPV6_AF_DADCOMPLETED) &&\n\t\t\t    ipv6_iffindaddr(ap->iface, &ap->addr,\n\t\t\t\tIN6_IFF_TENTATIVE))\n\t\t\t\tap->flags |= IPV6_AF_DADCOMPLETED;\n\t\t\tif ((ap->flags & IPV6_AF_DADCOMPLETED) == 0) {\n\t\t\t\tlogdebugx(\"%s: waiting for Router Advertisement\"\n\t\t\t\t\t  \" DAD to complete\",\n\t\t\t\t    rap->iface->name);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t/* If we don't require RDNSS then set hasdns = 1 so we fork */\n\tif (!(rap->iface->options->options & DHCPCD_IPV6RA_REQRDNSS))\n\t\thasdns = 1;\n\telse {\n\t\thasdns = rap->hasdns;\n\t}\n\n\tscript_runreason(rap->iface, \"ROUTERADVERT\");\n\tif (hasdns &&\n\t    (hasaddress ||\n\t\t!(rap->flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER))))\n\t\tdhcpcd_daemonise(rap->iface->ctx);\n#if 0\n\telse if (options & DHCPCD_DAEMONISE &&\n\t    !(options & DHCPCD_DAEMONISED) && new_data)\n\t\tlogwarnx(\"%s: did not fork due to an absent\"\n\t\t    \" RDNSS option in the RA\",\n\t\t    ifp->name);\n#endif\n}\n\nstatic void\nipv6nd_addaddr(void *arg)\n{\n\tstruct ipv6_addr *ap = arg;\n\n\tipv6_addaddr(ap, NULL);\n}\n\nint\nipv6nd_dadcompleted(const struct interface *ifp)\n{\n\tconst struct ra *rap;\n\tconst struct ipv6_addr *ap;\n\n\tTAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {\n\t\tif (rap->iface != ifp)\n\t\t\tcontinue;\n\t\tTAILQ_FOREACH(ap, &rap->addrs, next) {\n\t\t\tif (ap->flags & IPV6_AF_AUTOCONF &&\n\t\t\t    ap->flags & IPV6_AF_ADDED &&\n\t\t\t    !(ap->flags & IPV6_AF_DADCOMPLETED))\n\t\t\t\treturn 0;\n\t\t}\n\t}\n\treturn 1;\n}\n\nstatic void\nipv6nd_dadcallback(void *arg)\n{\n\tstruct ipv6_addr *ia = arg, *rapap;\n\tstruct interface *ifp;\n\tstruct ra *rap;\n\tint wascompleted, found;\n\tchar buf[INET6_ADDRSTRLEN];\n\tconst char *p;\n\tint dadcounter;\n\n\tifp = ia->iface;\n\twascompleted = (ia->flags & IPV6_AF_DADCOMPLETED);\n\tia->flags |= IPV6_AF_DADCOMPLETED;\n\tif (ia->addr_flags & IN6_IFF_DUPLICATED) {\n\t\tia->dadcounter++;\n\t\tlogwarnx(\"%s: DAD detected %s\", ifp->name, ia->saddr);\n\n\t\t/* Try and make another stable private address.\n\t\t * Because ap->dadcounter is always increamented,\n\t\t * a different address is generated. */\n\t\t/* XXX Cache DAD counter per prefix/id/ssid? */\n\t\tif (ifp->options->options & DHCPCD_SLAACPRIVATE &&\n\t\t    IA6_CANAUTOCONF(ia)) {\n\t\t\tunsigned int delay;\n\n\t\t\tif (ia->dadcounter >= IDGEN_RETRIES) {\n\t\t\t\tlogerrx(\"%s: unable to obtain a\"\n\t\t\t\t\t\" stable private address\",\n\t\t\t\t    ifp->name);\n\t\t\t\tgoto try_script;\n\t\t\t}\n\t\t\tloginfox(\"%s: deleting address %s\", ifp->name,\n\t\t\t    ia->saddr);\n\t\t\tif (if_address6(RTM_DELADDR, ia) == -1 &&\n\t\t\t    errno != EADDRNOTAVAIL && errno != ENXIO)\n\t\t\t\tlogerr(__func__);\n\t\t\tdadcounter = ia->dadcounter;\n\t\t\tif (ipv6_makestableprivate(&ia->addr, &ia->prefix,\n\t\t\t\tia->prefix_len, ifp, &dadcounter) == -1) {\n\t\t\t\tlogerr(\"ipv6_makestableprivate\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tia->dadcounter = dadcounter;\n\t\t\tia->flags &= ~(IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED);\n\t\t\tia->flags |= IPV6_AF_NEW;\n\t\t\tp = inet_ntop(AF_INET6, &ia->addr, buf, sizeof(buf));\n\t\t\tif (p)\n\t\t\t\tsnprintf(ia->saddr, sizeof(ia->saddr), \"%s/%d\",\n\t\t\t\t    p, ia->prefix_len);\n\t\t\telse\n\t\t\t\tia->saddr[0] = '\\0';\n\t\t\tdelay = arc4random_uniform(IDGEN_DELAY * MSEC_PER_SEC);\n\t\t\teloop_timeout_add_msec(ifp->ctx->eloop, delay,\n\t\t\t    ipv6nd_addaddr, ia);\n\t\t\treturn;\n\t\t}\n\t}\n\ntry_script:\n\tif (!wascompleted) {\n\t\tTAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {\n\t\t\tif (rap->iface != ifp)\n\t\t\t\tcontinue;\n\t\t\twascompleted = 1;\n\t\t\tfound = 0;\n\t\t\tTAILQ_FOREACH(rapap, &rap->addrs, next) {\n\t\t\t\tif (rapap->flags & IPV6_AF_AUTOCONF &&\n\t\t\t\t    rapap->flags & IPV6_AF_ADDED &&\n\t\t\t\t    (rapap->flags & IPV6_AF_DADCOMPLETED) ==\n\t\t\t\t\t0) {\n\t\t\t\t\twascompleted = 0;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tif (rapap == ia)\n\t\t\t\t\tfound = 1;\n\t\t\t}\n\n\t\t\tif (wascompleted && found) {\n\t\t\t\tlogdebugx(\"%s: Router Advertisement DAD \"\n\t\t\t\t\t  \"completed\",\n\t\t\t\t    rap->iface->name);\n\t\t\t\tipv6nd_scriptrun(rap);\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic struct ipv6_addr *\nipv6nd_findmarkstale(struct ra *rap, struct ipv6_addr *ia, bool mark)\n{\n\tstruct dhcpcd_ctx *ctx = ia->iface->ctx;\n\tstruct ra *rap2;\n\tstruct ipv6_addr *ia2;\n\n\tTAILQ_FOREACH(rap2, ctx->ra_routers, next) {\n\t\tif (rap2 == rap || rap2->iface != rap->iface || rap2->expired)\n\t\t\tcontinue;\n\t\tTAILQ_FOREACH(ia2, &rap2->addrs, next) {\n\t\t\tif (!IN6_ARE_ADDR_EQUAL(&ia->prefix, &ia2->prefix))\n\t\t\t\tcontinue;\n\t\t\tif (!(ia2->flags & IPV6_AF_STALE))\n\t\t\t\treturn ia2;\n\t\t\tif (mark)\n\t\t\t\tia2->prefix_pltime = 0;\n\t\t}\n\t}\n\treturn NULL;\n}\n\n#ifndef DHCP6\n/* If DHCPv6 is compiled out, supply a shim to provide an error message\n * if IPv6RA requests DHCPv6. */\nenum DH6S {\n\tDH6S_REQUEST,\n\tDH6S_INFORM,\n};\nstatic int\ndhcp6_start(__unused struct interface *ifp, __unused enum DH6S init_state)\n{\n\terrno = ENOTSUP;\n\treturn -1;\n}\n#endif\n\nstatic void\nipv6nd_handlera(struct dhcpcd_ctx *ctx, const struct sockaddr_in6 *from,\n    const char *sfrom, struct interface *ifp, struct icmp6_hdr *icp, size_t len,\n    int hoplimit)\n{\n\tsize_t i, olen;\n\tstruct nd_router_advert *nd_ra;\n\tstruct nd_opt_hdr ndo;\n\tstruct nd_opt_prefix_info pi;\n\tstruct nd_opt_mtu mtu;\n\tstruct nd_opt_rdnss rdnss;\n\tstruct nd_opt_ri ri;\n\tstruct routeinfo *rinfo;\n\tuint8_t *p;\n\tstruct ra *rap;\n\tstruct in6_addr pi_prefix;\n\tstruct ipv6_addr *ia;\n\tstruct dhcp_opt *dho;\n\tbool new_rap, new_data, has_address;\n\tuint32_t old_lifetime;\n\tint ifmtu;\n\tint loglevel;\n\tunsigned int flags;\n#ifdef IPV6_MANAGETEMPADDR\n\tbool new_ia;\n#endif\n\n#define FREE_RAP(rap)                                \\\n\tif (new_rap)                                 \\\n\t\tipv6nd_removefreedrop_ra(rap, 0, 0); \\\n\telse                                         \\\n\t\tipv6nd_free_ra(rap);\n\n\tif (ifp == NULL || RS_STATE(ifp) == NULL) {\n#ifdef DEBUG_RS\n\t\tlogdebugx(\"RA for unexpected interface from %s\", sfrom);\n#endif\n\t\treturn;\n\t}\n\n\tif (len < sizeof(struct nd_router_advert)) {\n\t\tlogerrx(\"IPv6 RA packet too short from %s\", sfrom);\n\t\treturn;\n\t}\n\n\t/* RFC 4861 7.1.2 */\n\tif (hoplimit != 255) {\n\t\tlogerrx(\"invalid hoplimit(%d) in RA from %s\", hoplimit, sfrom);\n\t\treturn;\n\t}\n\tif (!IN6_IS_ADDR_LINKLOCAL(&from->sin6_addr)) {\n\t\tlogerrx(\"RA from non local address %s\", sfrom);\n\t\treturn;\n\t}\n\n\tif (!(ifp->options->options & DHCPCD_IPV6RS)) {\n#ifdef DEBUG_RS\n\t\tlogerrx(\"%s: unexpected RA from %s\", ifp->name, sfrom);\n#endif\n\t\treturn;\n\t}\n\n\t/* We could receive a RA before we sent a RS*/\n\tif (ipv6_linklocal(ifp) == NULL) {\n#ifdef DEBUG_RS\n\t\tlogdebugx(\"%s: received RA from %s (no link-local)\", ifp->name,\n\t\t    sfrom);\n#endif\n\t\treturn;\n\t}\n\n\tif (ipv6_iffindaddr(ifp, &from->sin6_addr, IN6_IFF_TENTATIVE)) {\n\t\tlogdebugx(\"%s: ignoring RA from ourself %s\", ifp->name, sfrom);\n\t\treturn;\n\t}\n\n\t/*\n\t * Because we preserve RA's and expire them quickly after\n\t * carrier up, it's important to reset the kernels notion of\n\t * reachable timers back to default values before applying\n\t * new RA values.\n\t */\n\tTAILQ_FOREACH(rap, ctx->ra_routers, next) {\n\t\tif (ifp == rap->iface)\n\t\t\tbreak;\n\t}\n\tif (rap != NULL && rap->willexpire)\n\t\tipv6nd_applyra(ifp);\n\n\tTAILQ_FOREACH(rap, ctx->ra_routers, next) {\n\t\tif (ifp == rap->iface &&\n\t\t    IN6_ARE_ADDR_EQUAL(&rap->from, &from->sin6_addr))\n\t\t\tbreak;\n\t}\n\n\tnd_ra = (struct nd_router_advert *)icp;\n\n\t/* We don't want to spam the log with the fact we got an RA every\n\t * 30 seconds or so, so only spam the log if it's different. */\n\tif (rap == NULL ||\n\t    (rap->data_len != len ||\n\t\tmemcmp(rap->data, (unsigned char *)icp, rap->data_len) != 0)) {\n\t\tif (rap) {\n\t\t\tfree(rap->data);\n\t\t\trap->data_len = 0;\n\t\t}\n\t\tnew_data = true;\n\t} else\n\t\tnew_data = false;\n\tif (rap == NULL) {\n\t\trap = calloc(1, sizeof(*rap));\n\t\tif (rap == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\treturn;\n\t\t}\n\t\trap->iface = ifp;\n\t\trap->from = from->sin6_addr;\n\t\tstrlcpy(rap->sfrom, sfrom, sizeof(rap->sfrom));\n\t\tTAILQ_INIT(&rap->addrs);\n\t\tTAILQ_INIT(&rap->rinfos);\n\t\tnew_rap = true;\n\t\trap->isreachable = true;\n\t} else\n\t\tnew_rap = false;\n\tif (rap->data_len == 0) {\n\t\trap->data = malloc(len);\n\t\tif (rap->data == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\tif (new_rap)\n\t\t\t\tfree(rap);\n\t\t\treturn;\n\t\t}\n\t\tmemcpy(rap->data, icp, len);\n\t\trap->data_len = len;\n\t}\n\n\t/* We could change the debug level based on new_data, but some\n\t * routers like to decrease the advertised valid and preferred times\n\t * in accordance with the own prefix times which would result in too\n\t * much needless log spam. */\n\tif (rap->willexpire)\n\t\tnew_data = true;\n\tloglevel = new_rap || rap->willexpire || !rap->isreachable ? LOG_INFO :\n\t\t\t\t\t\t\t\t     LOG_DEBUG;\n\tlogmessage(loglevel, \"%s: Router Advertisement from %s\", ifp->name,\n\t    rap->sfrom);\n\n\tclock_gettime(CLOCK_MONOTONIC, &rap->acquired);\n\trap->flags = nd_ra->nd_ra_flags_reserved;\n\told_lifetime = rap->lifetime;\n\trap->lifetime = ntohs(nd_ra->nd_ra_router_lifetime);\n\tif (nd_ra->nd_ra_curhoplimit != 0)\n\t\trap->hoplimit = nd_ra->nd_ra_curhoplimit;\n\telse\n\t\trap->hoplimit = IPV6_DEFHLIM;\n\tif (nd_ra->nd_ra_reachable != 0) {\n\t\trap->reachable = ntohl(nd_ra->nd_ra_reachable);\n\t\tif (rap->reachable > MAX_REACHABLE_TIME)\n\t\t\trap->reachable = 0;\n\t} else\n\t\trap->reachable = REACHABLE_TIME;\n\tif (nd_ra->nd_ra_retransmit != 0)\n\t\trap->retrans = ntohl(nd_ra->nd_ra_retransmit);\n\telse\n\t\trap->retrans = RETRANS_TIMER;\n\trap->expired = rap->willexpire = rap->doexpire = false;\n\trap->hasdns = false;\n\trap->isreachable = true;\n\thas_address = false;\n\trap->mtu = 0;\n\n#ifdef IPV6_AF_TEMPORARY\n\tipv6_markaddrsstale(ifp, IPV6_AF_TEMPORARY);\n#endif\n\tTAILQ_FOREACH(ia, &rap->addrs, next) {\n\t\tia->flags |= IPV6_AF_STALE;\n\t}\n\n\tlen -= sizeof(struct nd_router_advert);\n\tp = ((uint8_t *)icp) + sizeof(struct nd_router_advert);\n\tfor (; len > 0; p += olen, len -= olen) {\n\t\tif (len < sizeof(ndo)) {\n\t\t\tlogerrx(\"%s: short option\", ifp->name);\n\t\t\tbreak;\n\t\t}\n\t\tmemcpy(&ndo, p, sizeof(ndo));\n\t\tolen = (size_t)ndo.nd_opt_len * 8;\n\t\tif (olen == 0) {\n\t\t\t/* RFC4681 4.6 says we MUST discard this ND packet. */\n\t\t\tlogerrx(\"%s: zero length option\", ifp->name);\n\t\t\tFREE_RAP(rap);\n\t\t\treturn;\n\t\t}\n\t\tif (olen > len) {\n\t\t\tlogerrx(\"%s: option length exceeds message\", ifp->name);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (has_option_mask(ifp->options->rejectmasknd,\n\t\t\tndo.nd_opt_type)) {\n\t\t\tfor (i = 0, dho = ctx->nd_opts; i < ctx->nd_opts_len;\n\t\t\t    i++, dho++) {\n\t\t\t\tif (dho->option == ndo.nd_opt_type)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (dho != NULL)\n\t\t\t\tlogwarnx(\"%s: reject RA (option %s) from %s\",\n\t\t\t\t    ifp->name, dho->var, rap->sfrom);\n\t\t\telse\n\t\t\t\tlogwarnx(\"%s: reject RA (option %d) from %s\",\n\t\t\t\t    ifp->name, ndo.nd_opt_type, rap->sfrom);\n\t\t\tFREE_RAP(rap);\n\t\t\treturn;\n\t\t}\n\n\t\tif (has_option_mask(ifp->options->nomasknd, ndo.nd_opt_type))\n\t\t\tcontinue;\n\n\t\tswitch (ndo.nd_opt_type) {\n\t\tcase ND_OPT_PREFIX_INFORMATION: {\n\t\t\tuint32_t vltime, pltime;\n\n\t\t\tloglevel = new_data ? LOG_ERR : LOG_DEBUG;\n\t\t\tif (ndo.nd_opt_len != 4) {\n\t\t\t\tlogmessage(loglevel,\n\t\t\t\t    \"%s: invalid option len for prefix\",\n\t\t\t\t    ifp->name);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tmemcpy(&pi, p, sizeof(pi));\n\t\t\tif (pi.nd_opt_pi_prefix_len > 128) {\n\t\t\t\tlogmessage(loglevel, \"%s: invalid prefix len\",\n\t\t\t\t    ifp->name);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t/* nd_opt_pi_prefix is not aligned. */\n\t\t\tmemcpy(&pi_prefix, &pi.nd_opt_pi_prefix,\n\t\t\t    sizeof(pi_prefix));\n\t\t\tif (IN6_IS_ADDR_MULTICAST(&pi_prefix) ||\n\t\t\t    IN6_IS_ADDR_LINKLOCAL(&pi_prefix)) {\n\t\t\t\tlogmessage(loglevel, \"%s: invalid prefix in RA\",\n\t\t\t\t    ifp->name);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tvltime = ntohl(pi.nd_opt_pi_valid_time);\n\t\t\tpltime = ntohl(pi.nd_opt_pi_preferred_time);\n\t\t\tif (pltime > vltime) {\n\t\t\t\tlogmessage(loglevel, \"%s: pltime > vltime\",\n\t\t\t\t    ifp->name);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tflags = IPV6_AF_RAPFX;\n\t\t\t/* If no flags are set, that means the prefix is\n\t\t\t * available via the router. */\n\t\t\tif (pi.nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK)\n\t\t\t\tflags |= IPV6_AF_ONLINK;\n\t\t\tif (pi.nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO &&\n\t\t\t    rap->iface->options->options &\n\t\t\t\tDHCPCD_IPV6RA_AUTOCONF)\n\t\t\t\tflags |= IPV6_AF_AUTOCONF;\n\t\t\tif (pi.nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ROUTER)\n\t\t\t\tflags |= IPV6_AF_ROUTER;\n\n\t\t\tia = ipv6nd_rapfindprefix(rap, &pi_prefix,\n\t\t\t    pi.nd_opt_pi_prefix_len);\n\t\t\tif (ia == NULL) {\n\t\t\t\tia = ipv6_newaddr(rap->iface, &pi_prefix,\n\t\t\t\t    pi.nd_opt_pi_prefix_len, flags);\n\t\t\t\tif (ia == NULL)\n\t\t\t\t\tbreak;\n\n\t\t\t\tia->prefix = pi_prefix;\n\t\t\t\tia->created = ia->acquired = rap->acquired;\n\t\t\t\tia->prefix_vltime = vltime;\n\t\t\t\tia->prefix_pltime = pltime;\n\n\t\t\t\tif (flags & IPV6_AF_AUTOCONF)\n\t\t\t\t\tia->dadcallback = ipv6nd_dadcallback;\n\n\t\t\t\tTAILQ_INSERT_TAIL(&rap->addrs, ia, next);\n\n#ifdef IPV6_MANAGETEMPADDR\n\t\t\t\t/* New address to dhcpcd RA handling.\n\t\t\t\t * If the address already exists and a valid\n\t\t\t\t * temporary address also exists then\n\t\t\t\t * extend the existing one rather than\n\t\t\t\t * create a new one */\n\t\t\t\tif (flags & IPV6_AF_AUTOCONF &&\n\t\t\t\t    ipv6_iffindaddr(ifp, &ia->addr,\n\t\t\t\t\tIN6_IFF_NOTUSEABLE) &&\n\t\t\t\t    ipv6_settemptime(ia, 0))\n\t\t\t\t\tnew_ia = false;\n\t\t\t\telse\n\t\t\t\t\tnew_ia = true;\n#endif\n\n\t\t\t} else {\n\t\t\t\tuint32_t rmtime;\n\n\t\t\t\t/*\n\t\t\t\t * RFC 4862 5.5.3.e\n\t\t\t\t * Don't terminate existing connections.\n\t\t\t\t * This means that to actually remove the\n\t\t\t\t * existing prefix, the RA needs to stop\n\t\t\t\t * broadcasting the prefix and just let it\n\t\t\t\t * expire in 2 hours.\n\t\t\t\t * It might want to broadcast it to reduce\n\t\t\t\t * the vltime if it was greater than 2 hours\n\t\t\t\t * to start with/\n\t\t\t\t */\n\t\t\t\tia->prefix_pltime = pltime;\n\t\t\t\tif (ia->prefix_vltime) {\n\t\t\t\t\tuint32_t elapsed;\n\n\t\t\t\t\telapsed = (uint32_t)\n\t\t\t\t\t    eloop_timespec_diff(&rap->acquired,\n\t\t\t\t\t\t&ia->acquired, NULL);\n\t\t\t\t\trmtime = ia->prefix_vltime - elapsed;\n\t\t\t\t\tif (rmtime > ia->prefix_vltime)\n\t\t\t\t\t\trmtime = 0;\n\t\t\t\t} else\n\t\t\t\t\trmtime = 0;\n\t\t\t\tif (vltime > MIN_EXTENDED_VLTIME ||\n\t\t\t\t    vltime > rmtime)\n\t\t\t\t\tia->prefix_vltime = vltime;\n\t\t\t\telse if (rmtime <= MIN_EXTENDED_VLTIME)\n\t\t\t\t\t/* No SEND support from RFC 3971 so\n\t\t\t\t\t * leave vltime alone */\n\t\t\t\t\tia->prefix_vltime = rmtime;\n\t\t\t\telse\n\t\t\t\t\tia->prefix_vltime = MIN_EXTENDED_VLTIME;\n\n\t\t\t\t/* Ensure pltime still fits */\n\t\t\t\tif (pltime < ia->prefix_vltime)\n\t\t\t\t\tia->prefix_pltime = pltime;\n\t\t\t\telse\n\t\t\t\t\tia->prefix_pltime = ia->prefix_vltime;\n\n\t\t\t\tia->flags &= ~RA_STALE_FLAGS;\n\t\t\t\tia->flags |= flags;\n\t\t\t\tia->acquired = rap->acquired;\n\n#ifdef IPV6_MANAGETEMPADDR\n\t\t\t\tnew_ia = false;\n#endif\n\t\t\t}\n\n\t\t\tif (ia->prefix_vltime != 0 &&\n\t\t\t    ia->flags & IPV6_AF_AUTOCONF)\n\t\t\t\thas_address = true;\n\n#ifdef IPV6_MANAGETEMPADDR\n\t\t\t/* RFC4941 Section 3.3.3 */\n\t\t\tif (ia->flags & IPV6_AF_AUTOCONF &&\n\t\t\t    ia->iface->options->options & DHCPCD_SLAACTEMP &&\n\t\t\t    IA6_CANAUTOCONF(ia)) {\n\t\t\t\tif (!new_ia) {\n\t\t\t\t\tif (ipv6_settemptime(ia, 1) == NULL)\n\t\t\t\t\t\tnew_ia = true;\n\t\t\t\t}\n\t\t\t\tif (new_ia && ia->prefix_pltime) {\n\t\t\t\t\tif (ipv6_createtempaddr(ia,\n\t\t\t\t\t\t&ia->acquired) == NULL)\n\t\t\t\t\t\tlogerr(\"ipv6_createtempaddr\");\n\t\t\t\t}\n\t\t\t}\n#endif\n\t\t\tbreak;\n\t\t}\n\n\t\tcase ND_OPT_MTU:\n\t\t\tif (len < sizeof(mtu)) {\n\t\t\t\tlogmessage(loglevel, \"%s: short MTU option\",\n\t\t\t\t    ifp->name);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tmemcpy(&mtu, p, sizeof(mtu));\n\t\t\tmtu.nd_opt_mtu_mtu = ntohl(mtu.nd_opt_mtu_mtu);\n\t\t\tif (mtu.nd_opt_mtu_mtu < IPV6_MMTU) {\n\t\t\t\tlogmessage(loglevel, \"%s: invalid MTU %d\",\n\t\t\t\t    ifp->name, mtu.nd_opt_mtu_mtu);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tifmtu = if_getmtu(ifp);\n\t\t\tif (ifmtu == -1)\n\t\t\t\tlogerr(\"if_getmtu\");\n\t\t\telse if (mtu.nd_opt_mtu_mtu > (uint32_t)ifmtu) {\n\t\t\t\tlogmessage(loglevel,\n\t\t\t\t    \"%s: advertised MTU %d\"\n\t\t\t\t    \" is greater than link MTU %d\",\n\t\t\t\t    ifp->name, mtu.nd_opt_mtu_mtu, ifmtu);\n\t\t\t\trap->mtu = (uint32_t)ifmtu;\n\t\t\t} else\n\t\t\t\trap->mtu = mtu.nd_opt_mtu_mtu;\n\t\t\tbreak;\n\t\tcase ND_OPT_RDNSS:\n\t\t\tif (len < sizeof(rdnss)) {\n\t\t\t\tlogmessage(loglevel, \"%s: short RDNSS option\",\n\t\t\t\t    ifp->name);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tmemcpy(&rdnss, p, sizeof(rdnss));\n\t\t\tif (rdnss.nd_opt_rdnss_lifetime &&\n\t\t\t    rdnss.nd_opt_rdnss_len > 1)\n\t\t\t\trap->hasdns = 1;\n\t\t\tbreak;\n\t\tcase ND_OPT_RI:\n\t\t\tif (ndo.nd_opt_len > 3) {\n\t\t\t\tlogmessage(loglevel,\n\t\t\t\t    \"%s: invalid route info option\", ifp->name);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tmemset(&ri, 0, sizeof(ri));\n\t\t\tmemcpy(&ri, p, olen); /* may be smaller than sizeof(ri),\n\t\t\t\t\t\t pad with zero */\n\t\t\tif (ri.nd_opt_ri_prefixlen > 128) {\n\t\t\t\tlogmessage(loglevel,\n\t\t\t\t    \"%s: invalid route info prefix length\",\n\t\t\t\t    ifp->name);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/* rfc4191 3.1 - RI for ::/0 applies to default route */\n\t\t\tif (ri.nd_opt_ri_prefixlen == 0) {\n\t\t\t\trap->lifetime = ntohl(ri.nd_opt_ri_lifetime);\n\n\t\t\t\t/* Update preference leaving other flags intact\n\t\t\t\t */\n\t\t\t\trap->flags =\n\t\t\t\t    ((rap->flags &\n\t\t\t\t\t (~(unsigned int)\n\t\t\t\t\t\t ND_RA_FLAG_RTPREF_MASK)) |\n\t\t\t\t\tri.nd_opt_ri_flags_reserved) &\n\t\t\t\t    0xff;\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/* Update existing route info instead of rebuilding all\n\t\t\troutes so that previously announced but now absent\n\t\t\troutes can stay alive.  To kill a route early, an RI\n\t\t\twith lifetime=0 needs to be received (rfc4191 3.1)*/\n\t\t\trinfo = routeinfo_findalloc(rap, &ri.nd_opt_ri_prefix,\n\t\t\t    ri.nd_opt_ri_prefixlen);\n\t\t\tif (rinfo == NULL) {\n\t\t\t\tlogerr(__func__);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/* Update/initialize other route info params */\n\t\t\trinfo->flags = ri.nd_opt_ri_flags_reserved;\n\t\t\trinfo->lifetime = ntohl(ri.nd_opt_ri_lifetime);\n\t\t\trinfo->acquired = rap->acquired;\n\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tcontinue;\n\t\t}\n\t}\n\n\tfor (i = 0, dho = ctx->nd_opts; i < ctx->nd_opts_len; i++, dho++) {\n\t\tif (has_option_mask(ifp->options->requiremasknd, dho->option)) {\n\t\t\tlogwarnx(\"%s: reject RA (no option %s) from %s\",\n\t\t\t    ifp->name, dho->var, rap->sfrom);\n\t\t\tFREE_RAP(rap);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tTAILQ_FOREACH(ia, &rap->addrs, next) {\n\t\tif (!(ia->flags & IPV6_AF_STALE) || ia->prefix_pltime == 0)\n\t\t\tcontinue;\n\t\tif (ipv6nd_findmarkstale(rap, ia, false) != NULL)\n\t\t\tcontinue;\n\t\tipv6nd_findmarkstale(rap, ia, true);\n\t\tlogdebugx(\"%s: %s: became stale\", ifp->name, ia->saddr);\n\t\t/* Technically this violates RFC 4861 6.3.4,\n\t\t * but we need a mechanism to tell the kernel to\n\t\t * try and prefer other addresses. */\n\t\tia->prefix_pltime = 0;\n\t}\n\n\tif (!new_rap && rap->lifetime == 0 && old_lifetime != 0)\n\t\tlogwarnx(\"%s: %s: no longer a default router (lifetime = 0)\",\n\t\t    ifp->name, rap->sfrom);\n\n\tif (new_data && !has_address && rap->lifetime &&\n\t    ifp->options->options & DHCPCD_GATEWAY && !ipv6_anyglobal(ifp))\n\t\tlogwarnx(\"%s: no global addresses for default route\",\n\t\t    ifp->name);\n\n\tif (new_rap)\n\t\tTAILQ_INSERT_TAIL(ctx->ra_routers, rap, next);\n\tif (new_data)\n\t\tipv6nd_sortrouters(ifp->ctx);\n\n\tif (ifp->ctx->options & DHCPCD_TEST) {\n\t\tscript_runreason(ifp, \"TEST\");\n\t\tgoto handle_flag;\n\t}\n\n\tif (!(ifp->options->options & DHCPCD_CONFIGURE))\n\t\tgoto run;\n\n\tipv6nd_applyra(ifp);\n\tipv6_addaddrs(&rap->addrs);\n#ifdef IPV6_MANAGETEMPADDR\n\tipv6_addtempaddrs(ifp, &rap->acquired);\n#endif\n\trt_build(ifp->ctx, AF_INET6);\n\nrun:\n\tipv6nd_scriptrun(rap);\n\n\teloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);\n\teloop_timeout_delete(ifp->ctx->eloop, NULL, rap); /* reachable timer */\n\nhandle_flag:\n\tif (!(ifp->options->options & DHCPCD_DHCP6))\n\t\tgoto nodhcp6;\n/* Only log a DHCPv6 start error if compiled in or debugging is enabled. */\n#ifdef DHCP6\n#define LOG_DHCP6 logerr\n#else\n#define LOG_DHCP6 logdebug\n#endif\n\tif (rap->flags & ND_RA_FLAG_MANAGED) {\n\t\tif (new_data && dhcp6_start(ifp, DH6S_REQUEST) == -1)\n\t\t\tLOG_DHCP6(\"dhcp6_start: %s\", ifp->name);\n\t} else if (rap->flags & ND_RA_FLAG_OTHER) {\n\t\tif (new_data && dhcp6_start(ifp, DH6S_INFORM) == -1)\n\t\t\tLOG_DHCP6(\"dhcp6_start: %s\", ifp->name);\n\t} else {\n#ifdef DHCP6\n\t\tif (new_data)\n\t\t\tlogdebugx(\"%s: No DHCPv6 instruction in RA\", ifp->name);\n#endif\n\tnodhcp6:\n\t\tif (ifp->ctx->options & DHCPCD_TEST) {\n\t\t\teloop_exit(ifp->ctx->eloop, EXIT_SUCCESS);\n\t\t\treturn;\n\t\t}\n\t}\n\n\t/* Expire should be called last as the rap object could be destroyed */\n\tipv6nd_expirera(ifp);\n#undef FREE_RAP\n}\n\nbool\nipv6nd_hasralifetime(const struct interface *ifp, bool lifetime)\n{\n\tconst struct ra *rap;\n\n\tif (ifp->ctx->ra_routers) {\n\t\tTAILQ_FOREACH(rap, ifp->ctx->ra_routers, next)\n\t\t\tif (rap->iface == ifp && !rap->expired &&\n\t\t\t    (!lifetime || rap->lifetime))\n\t\t\t\treturn true;\n\t}\n\treturn false;\n}\n\nbool\nipv6nd_hasradhcp(const struct interface *ifp, bool managed)\n{\n\tconst struct ra *rap;\n\n\tif (ifp->ctx->ra_routers) {\n\t\tTAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {\n\t\t\tif (rap->iface == ifp && !rap->expired &&\n\t\t\t    !rap->willexpire &&\n\t\t\t    ((managed && rap->flags & ND_RA_FLAG_MANAGED) ||\n\t\t\t\t(!managed && rap->flags & ND_RA_FLAG_OTHER)))\n\t\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nstatic const uint8_t *\nipv6nd_getoption(struct dhcpcd_ctx *ctx, size_t *os, unsigned int *code,\n    size_t *len, const uint8_t *od, size_t ol, struct dhcp_opt **oopt)\n{\n\tstruct nd_opt_hdr ndo;\n\tsize_t i;\n\tstruct dhcp_opt *opt;\n\n\tif (od) {\n\t\t*os = sizeof(ndo);\n\t\tif (ol < *os) {\n\t\t\terrno = EINVAL;\n\t\t\treturn NULL;\n\t\t}\n\t\tmemcpy(&ndo, od, sizeof(ndo));\n\t\ti = (size_t)(ndo.nd_opt_len * 8);\n\t\tif (i > ol) {\n\t\t\terrno = EINVAL;\n\t\t\treturn NULL;\n\t\t}\n\t\t*len = i;\n\t\t*code = ndo.nd_opt_type;\n\t}\n\n\tfor (i = 0, opt = ctx->nd_opts; i < ctx->nd_opts_len; i++, opt++) {\n\t\tif (opt->option == *code) {\n\t\t\t*oopt = opt;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (od)\n\t\treturn od + sizeof(ndo);\n\treturn NULL;\n}\n\nssize_t\nipv6nd_env(FILE *fp, const struct interface *ifp)\n{\n\tsize_t i, j, n, len, olen;\n\tstruct ra *rap;\n\tchar ndprefix[32];\n\tstruct dhcp_opt *opt;\n\tuint8_t *p;\n\tstruct nd_opt_hdr ndo;\n\tstruct ipv6_addr *ia;\n\tstruct timespec now;\n\tint pref;\n\n\tclock_gettime(CLOCK_MONOTONIC, &now);\n\ti = n = 0;\n\tTAILQ_FOREACH(rap, ifp->ctx->ra_routers, next) {\n\t\tif (rap->iface != ifp || rap->expired)\n\t\t\tcontinue;\n\t\ti++;\n\t\tsnprintf(ndprefix, sizeof(ndprefix), \"nd%zu\", i);\n\t\tif (efprintf(fp, \"%s_from=%s\", ndprefix, rap->sfrom) == -1)\n\t\t\treturn -1;\n\t\tif (efprintf(fp, \"%s_acquired=%lld\", ndprefix,\n\t\t\t(long long)rap->acquired.tv_sec) == -1)\n\t\t\treturn -1;\n\t\tif (efprintf(fp, \"%s_now=%lld\", ndprefix,\n\t\t\t(long long)now.tv_sec) == -1)\n\t\t\treturn -1;\n\t\tif (efprintf(fp, \"%s_hoplimit=%u\", ndprefix, rap->hoplimit) ==\n\t\t    -1)\n\t\t\treturn -1;\n\t\tpref = ipv6nd_rtpref(rap->flags);\n\t\tif (efprintf(fp, \"%s_flags=%s%s%s%s%s\", ndprefix,\n\t\t\trap->flags & ND_RA_FLAG_MANAGED ? \"M\" : \"\",\n\t\t\trap->flags & ND_RA_FLAG_OTHER ? \"O\" : \"\",\n\t\t\trap->flags & ND_RA_FLAG_HOME_AGENT ? \"H\" : \"\",\n\t\t\tpref == RTPREF_HIGH    ? \"h\" :\n\t\t\t    pref == RTPREF_LOW ? \"l\" :\n\t\t\t\t\t\t \"\",\n\t\t\trap->flags & ND_RA_FLAG_PROXY ? \"P\" : \"\") == -1)\n\t\t\treturn -1;\n\t\tif (efprintf(fp, \"%s_lifetime=%u\", ndprefix, rap->lifetime) ==\n\t\t    -1)\n\t\t\treturn -1;\n\n\t\t/* Zero our indexes */\n\t\tfor (j = 0, opt = rap->iface->ctx->nd_opts;\n\t\t    j < rap->iface->ctx->nd_opts_len; j++, opt++)\n\t\t\tdhcp_zero_index(opt);\n\t\tfor (j = 0, opt = rap->iface->options->nd_override;\n\t\t    j < rap->iface->options->nd_override_len; j++, opt++)\n\t\t\tdhcp_zero_index(opt);\n\n\t\t/* Unlike DHCP, ND6 options *may* occur more than once.\n\t\t * There is also no provision for option concatenation\n\t\t * unlike DHCP. */\n\t\tlen = rap->data_len - sizeof(struct nd_router_advert);\n\t\tfor (p = rap->data + sizeof(struct nd_router_advert);\n\t\t    len >= sizeof(ndo); p += olen, len -= olen) {\n\t\t\tmemcpy(&ndo, p, sizeof(ndo));\n\t\t\tolen = (size_t)(ndo.nd_opt_len * 8);\n\t\t\tif (olen > len) {\n\t\t\t\terrno = EINVAL;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (has_option_mask(rap->iface->options->nomasknd,\n\t\t\t\tndo.nd_opt_type))\n\t\t\t\tcontinue;\n\t\t\tfor (j = 0, opt = rap->iface->options->nd_override;\n\t\t\t    j < rap->iface->options->nd_override_len;\n\t\t\t    j++, opt++)\n\t\t\t\tif (opt->option == ndo.nd_opt_type)\n\t\t\t\t\tbreak;\n\t\t\tif (j == rap->iface->options->nd_override_len) {\n\t\t\t\tfor (j = 0, opt = rap->iface->ctx->nd_opts;\n\t\t\t\t    j < rap->iface->ctx->nd_opts_len;\n\t\t\t\t    j++, opt++)\n\t\t\t\t\tif (opt->option == ndo.nd_opt_type)\n\t\t\t\t\t\tbreak;\n\t\t\t\tif (j == rap->iface->ctx->nd_opts_len)\n\t\t\t\t\topt = NULL;\n\t\t\t}\n\t\t\tif (opt == NULL)\n\t\t\t\tcontinue;\n\t\t\tdhcp_envoption(rap->iface->ctx, fp, ndprefix,\n\t\t\t    rap->iface->name, opt, ipv6nd_getoption,\n\t\t\t    p + sizeof(ndo), olen - sizeof(ndo));\n\t\t}\n\n\t\t/* We need to output the addresses we actually made\n\t\t * from the prefix information options as well. */\n\t\tj = 0;\n\t\tTAILQ_FOREACH(ia, &rap->addrs, next) {\n\t\t\tif (!(ia->flags & IPV6_AF_AUTOCONF) ||\n#ifdef IPV6_AF_TEMPORARY\n\t\t\t    ia->flags & IPV6_AF_TEMPORARY ||\n#endif\n\t\t\t    !(ia->flags & IPV6_AF_ADDED) ||\n\t\t\t    ia->prefix_vltime == 0)\n\t\t\t\tcontinue;\n\t\t\tif (efprintf(fp, \"%s_addr%zu=%s\", ndprefix, ++j,\n\t\t\t\tia->saddr) == -1)\n\t\t\t\treturn -1;\n\t\t}\n\t}\n\treturn 1;\n}\n\nvoid\nipv6nd_handleifa(int cmd, struct ipv6_addr *addr, pid_t pid)\n{\n\tstruct ra *rap;\n\n\t/* IPv6 init may not have happened yet if we are learning\n\t * existing addresses when dhcpcd starts. */\n\tif (addr->iface->ctx->ra_routers == NULL)\n\t\treturn;\n\n\tTAILQ_FOREACH(rap, addr->iface->ctx->ra_routers, next) {\n\t\tif (rap->iface != addr->iface)\n\t\t\tcontinue;\n\t\tipv6_handleifa_addrs(cmd, &rap->addrs, addr, pid);\n\t}\n}\n\nvoid\nipv6nd_expirera(void *arg)\n{\n\tstruct interface *ifp;\n\tstruct ra *rap, *ran;\n\tstruct timespec now;\n\tbool expired, valid;\n\tstruct ipv6_addr *ia;\n\tstruct routeinfo *rinfo, *rinfob;\n\tsize_t len, olen;\n\tuint8_t *p;\n\tstruct nd_opt_hdr ndo;\n#if 0\n\tstruct nd_opt_prefix_info pi;\n#endif\n\tstruct nd_opt_dnssl dnssl;\n\tstruct nd_opt_rdnss rdnss;\n\tuint32_t next = 0, ltime, elapsed;\n\tsize_t nexpired = 0;\n\n\tifp = arg;\n\ttimespecclear(&now);\n\texpired = false;\n\n\tTAILQ_FOREACH_SAFE(rap, ifp->ctx->ra_routers, next, ran) {\n\t\tif (rap->iface != ifp || rap->expired)\n\t\t\tcontinue;\n\t\tvalid = false;\n\t\t/* lifetime may be set to infinite by rfc4191 route information\n\t\t */\n\t\tif (rap->lifetime) {\n\t\t\tltime = lifetime_left(rap->lifetime, &rap->acquired,\n\t\t\t    &now);\n\t\t\tif (ltime == 0 || rap->doexpire) {\n\t\t\t\tif (!rap->expired) {\n\t\t\t\t\tlogwarnx(\"%s: %s: router expired\",\n\t\t\t\t\t    ifp->name, rap->sfrom);\n\t\t\t\t\trap->lifetime = 0;\n\t\t\t\t\texpired = true;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tvalid = true;\n\t\t\t\tif (next == 0 || ltime < next)\n\t\t\t\t\tnext = ltime;\n\t\t\t}\n\t\t}\n\n\t\t/* Not every prefix is tied to an address which\n\t\t * the kernel can expire, so we need to handle it ourself.\n\t\t * Also, some OS don't support address lifetimes (Solaris). */\n\t\tTAILQ_FOREACH(ia, &rap->addrs, next) {\n\t\t\tif (ia->prefix_vltime == 0)\n\t\t\t\tcontinue;\n\t\t\tltime = lifetime_left(ia->prefix_vltime, &ia->acquired,\n\t\t\t    &now);\n\t\t\tif (ltime == 0 || rap->doexpire) {\n\t\t\t\tif (ia->flags & IPV6_AF_ADDED) {\n\t\t\t\t\tlogwarnx(\"%s: expired %s %s\",\n\t\t\t\t\t    ia->iface->name,\n\t\t\t\t\t    ia->flags & IPV6_AF_AUTOCONF ?\n\t\t\t\t\t\t\"address\" :\n\t\t\t\t\t\t\"prefix\",\n\t\t\t\t\t    ia->saddr);\n\t\t\t\t\tif (if_address6(RTM_DELADDR, ia) ==\n\t\t\t\t\t\t-1 &&\n\t\t\t\t\t    errno != EADDRNOTAVAIL &&\n\t\t\t\t\t    errno != ENXIO)\n\t\t\t\t\t\tlogerr(__func__);\n\t\t\t\t}\n\t\t\t\tia->prefix_vltime = ia->prefix_pltime = 0;\n\t\t\t\tia->flags &= ~(\n\t\t\t\t    IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED);\n\t\t\t\texpired = true;\n\t\t\t} else {\n\t\t\t\tvalid = true;\n\t\t\t\tif (next == 0 || ltime < next)\n\t\t\t\t\tnext = ltime;\n\t\t\t}\n\t\t}\n\n\t\t/* Expire route information */\n\t\tTAILQ_FOREACH_SAFE(rinfo, &rap->rinfos, next, rinfob) {\n\t\t\tltime = lifetime_left(rinfo->lifetime, &rinfo->acquired,\n\t\t\t    &now);\n\t\t\tif (ltime == 0 || rap->doexpire) {\n\t\t\t\tlogwarnx(\"%s: expired route %s\",\n\t\t\t\t    rap->iface->name, rinfo->sprefix);\n\t\t\t\tTAILQ_REMOVE(&rap->rinfos, rinfo, next);\n\t\t\t}\n\t\t}\n\n\t\t/* Work out expiry for ND options */\n\t\telapsed = (uint32_t)eloop_timespec_diff(&now, &rap->acquired,\n\t\t    NULL);\n\t\tlen = rap->data_len - sizeof(struct nd_router_advert);\n\t\tfor (p = rap->data + sizeof(struct nd_router_advert);\n\t\t    len >= sizeof(ndo); p += olen, len -= olen) {\n\t\t\tmemcpy(&ndo, p, sizeof(ndo));\n\t\t\tolen = (size_t)(ndo.nd_opt_len * 8);\n\t\t\tif (olen > len) {\n\t\t\t\terrno = EINVAL;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (has_option_mask(rap->iface->options->nomasknd,\n\t\t\t\tndo.nd_opt_type))\n\t\t\t\tcontinue;\n\n\t\t\tswitch (ndo.nd_opt_type) {\n\t\t\t\t/* Prefix info is already checked in the above\n\t\t\t\t * loop. */\n#if 0\n\t\t\tcase ND_OPT_PREFIX_INFORMATION:\n\t\t\t\tif (len < sizeof(pi))\n\t\t\t\t\tbreak;\n\t\t\t\tmemcpy(&pi, p, sizeof(pi));\n\t\t\t\tltime = pi.nd_opt_pi_valid_time;\n\t\t\t\tbreak;\n#endif\n\t\t\tcase ND_OPT_DNSSL:\n\t\t\t\tif (len < sizeof(dnssl))\n\t\t\t\t\tcontinue;\n\t\t\t\tmemcpy(&dnssl, p, sizeof(dnssl));\n\t\t\t\tltime = dnssl.nd_opt_dnssl_lifetime;\n\t\t\t\tbreak;\n\t\t\tcase ND_OPT_RDNSS:\n\t\t\t\tif (len < sizeof(rdnss))\n\t\t\t\t\tcontinue;\n\t\t\t\tmemcpy(&rdnss, p, sizeof(rdnss));\n\t\t\t\tltime = rdnss.nd_opt_rdnss_lifetime;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (ltime == 0)\n\t\t\t\tcontinue;\n\t\t\tif (rap->doexpire) {\n\t\t\t\texpired = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (ltime == ND6_INFINITE_LIFETIME) {\n\t\t\t\tvalid = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tltime = ntohl(ltime);\n\t\t\tif (elapsed >= ltime) {\n\t\t\t\texpired = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tvalid = true;\n\t\t\tltime -= elapsed;\n\t\t\tif (next == 0 || ltime < next)\n\t\t\t\tnext = ltime;\n\t\t}\n\n\t\tif (valid)\n\t\t\tcontinue;\n\n\t\t/* Router has expired. Let's not keep a lot of them. */\n\t\trap->expired = true;\n\t\tif (++nexpired > EXPIRED_MAX)\n\t\t\tipv6nd_free_ra(rap);\n\t}\n\n\tif (next != 0)\n\t\teloop_timeout_add_sec(ifp->ctx->eloop, next, ipv6nd_expirera,\n\t\t    ifp);\n\tif (expired) {\n\t\tlogwarnx(\"%s: part of a Router Advertisement expired\",\n\t\t    ifp->name);\n\t\tipv6nd_sortrouters(ifp->ctx);\n\t\tipv6nd_applyra(ifp);\n\t\trt_build(ifp->ctx, AF_INET6);\n\t\tscript_runreason(ifp, \"ROUTERADVERT\");\n\t}\n}\n\nvoid\nipv6nd_drop(struct interface *ifp)\n{\n\tstruct ra *rap, *ran;\n\tbool expired = false;\n\n\tif (ifp->ctx->ra_routers == NULL)\n\t\treturn;\n\n\teloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);\n\tTAILQ_FOREACH_SAFE(rap, ifp->ctx->ra_routers, next, ran) {\n\t\tif (rap->iface == ifp) {\n\t\t\trap->expired = expired = true;\n\t\t\tipv6nd_drop_ra(rap);\n\t\t}\n\t}\n\tif (expired) {\n\t\tipv6nd_applyra(ifp);\n\t\trt_build(ifp->ctx, AF_INET6);\n\t\tif ((ifp->options->options & DHCPCD_NODROP) != DHCPCD_NODROP)\n\t\t\tscript_runreason(ifp, \"ROUTERADVERT\");\n\t}\n}\n\nvoid\nipv6nd_recvmsg(struct dhcpcd_ctx *ctx, struct msghdr *msg)\n{\n\tstruct sockaddr_in6 *from = (struct sockaddr_in6 *)msg->msg_name;\n\tchar sfrom[INET6_ADDRSTRLEN];\n\tint hoplimit = 0;\n\tstruct icmp6_hdr *icp;\n\tstruct interface *ifp;\n\tsize_t len = msg->msg_iov[0].iov_len;\n\n\tinet_ntop(AF_INET6, &from->sin6_addr, sfrom, sizeof(sfrom));\n\tif ((size_t)len < sizeof(struct icmp6_hdr)) {\n\t\tlogerrx(\"IPv6 ICMP packet too short from %s\", sfrom);\n\t\treturn;\n\t}\n\n\tifp = if_findifpfromcmsg(ctx, msg, &hoplimit);\n\tif (ifp == NULL) {\n\t\tlogerr(__func__);\n\t\treturn;\n\t}\n\n\t/* Don't do anything if the user hasn't configured it. */\n\tif (ifp->active != IF_ACTIVE_USER ||\n\t    ifp->options->options & DHCPCD_STOPPING ||\n\t    !(ifp->options->options & DHCPCD_IPV6))\n\t\treturn;\n\n\ticp = (struct icmp6_hdr *)msg->msg_iov[0].iov_base;\n\tif (icp->icmp6_code == 0) {\n\t\tswitch (icp->icmp6_type) {\n\t\tcase ND_ROUTER_ADVERT:\n\t\t\tipv6nd_handlera(ctx, from, sfrom, ifp, icp, (size_t)len,\n\t\t\t    hoplimit);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tlogerrx(\"invalid IPv6 type %d or code %d from %s\", icp->icmp6_type,\n\t    icp->icmp6_code, sfrom);\n}\n\nstatic void\nipv6nd_handledata(void *arg, unsigned short events)\n{\n\tstruct dhcpcd_ctx *ctx;\n\tint fd;\n\tstruct sockaddr_in6 from;\n\tunion {\n\t\tstruct icmp6_hdr hdr;\n\t\tuint8_t buf[64 * 1024]; /* Maximum ICMPv6 size */\n\t} iovbuf;\n\tstruct iovec iov = {\n\t\t.iov_base = iovbuf.buf,\n\t\t.iov_len = sizeof(iovbuf.buf),\n\t};\n\tunion {\n\t\tstruct cmsghdr hdr;\n\t\tuint8_t buf[CMSG_SPACE(sizeof(struct in6_pktinfo)) +\n\t\t    CMSG_SPACE(sizeof(int))];\n\t} cmsgbuf = { .buf = { 0 } };\n\tstruct msghdr msg = {\n\t\t.msg_name = &from,\n\t\t.msg_namelen = sizeof(from),\n\t\t.msg_iov = &iov,\n\t\t.msg_iovlen = 1,\n\t\t.msg_control = cmsgbuf.buf,\n\t\t.msg_controllen = sizeof(cmsgbuf.buf),\n\t};\n\tssize_t len;\n\n#ifdef __sun\n\tstruct interface *ifp;\n\tstruct rs_state *state;\n\n\tifp = arg;\n\tstate = RS_STATE(ifp);\n\tctx = ifp->ctx;\n\tfd = state->nd_fd;\n#else\n\tctx = arg;\n\tfd = ctx->nd_fd;\n#endif\n\n\tif (events != ELE_READ)\n\t\tlogerrx(\"%s: unexpected event 0x%04x\", __func__, events);\n\n\tlen = recvmsg(fd, &msg, 0);\n\tif (len == -1) {\n\t\tlogerr(__func__);\n\t\treturn;\n\t}\n\n\tiov.iov_len = (size_t)len;\n\tipv6nd_recvmsg(ctx, &msg);\n}\n\nstatic void\nipv6nd_startrs2(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tstruct rs_state *state;\n\n\tloginfox(\"%s: soliciting an IPv6 router\", ifp->name);\n\tstate = RS_STATE(ifp);\n\tif (state == NULL) {\n\t\tifp->if_data[IF_DATA_IPV6ND] = calloc(1, sizeof(*state));\n\t\tstate = RS_STATE(ifp);\n\t\tif (state == NULL) {\n\t\t\tlogerr(__func__);\n\t\t\treturn;\n\t\t}\n#ifdef __sun\n\t\tstate->nd_fd = -1;\n#endif\n\t}\n\n\t/* Always make a new probe as the underlying hardware\n\t * address could have changed. */\n\tipv6nd_makersprobe(ifp);\n\tif (state->rs == NULL) {\n\t\tlogerr(__func__);\n\t\treturn;\n\t}\n\n\tstate->retrans = RETRANS_TIMER;\n\tstate->rsprobes = 0;\n\tipv6nd_sendrsprobe(ifp);\n}\n\nstatic void\nipv6nd_startrs1(void *arg)\n{\n\tstruct interface *ifp = arg;\n\tunsigned int delay;\n\n\tif (!(ifp->options->options & DHCPCD_INITIAL_DELAY)) {\n\t\tipv6nd_startrs2(ifp);\n\t\treturn;\n\t}\n\n\tdelay = arc4random_uniform(MAX_RTR_SOLICITATION_DELAY * MSEC_PER_SEC);\n\tlogdebugx(\"%s: delaying IPv6 router solicitation for %0.1f seconds\",\n\t    ifp->name, (float)delay / MSEC_PER_SEC);\n\teloop_timeout_add_msec(ifp->ctx->eloop, delay, ipv6nd_startrs2, ifp);\n}\n\nvoid\nipv6nd_startrs(struct interface *ifp)\n{\n\tif (ipv6_linklocal(ifp) == NULL) {\n\t\tlogdebugx(\"%s: \"\n\t\t\t  \"delaying IPv6 Router Solicitation for LL address\",\n\t\t    ifp->name);\n\t\tipv6_addlinklocalcallback(ifp, ipv6nd_startrs1, ifp);\n\t} else\n\t\tipv6nd_startrs1(ifp);\n}\n\nvoid\nipv6nd_abort(struct interface *ifp)\n{\n\teloop_timeout_delete(ifp->ctx->eloop, ipv6nd_startrs1, ifp);\n\teloop_timeout_delete(ifp->ctx->eloop, ipv6nd_startrs2, ifp);\n\teloop_timeout_delete(ifp->ctx->eloop, ipv6nd_sendrsprobe, ifp);\n}\n\nstatic struct routeinfo *\nrouteinfo_findalloc(struct ra *rap, const struct in6_addr *prefix,\n    uint8_t prefix_len)\n{\n\tstruct routeinfo *ri;\n\tchar buf[INET6_ADDRSTRLEN];\n\tconst char *p;\n\n\tTAILQ_FOREACH(ri, &rap->rinfos, next) {\n\t\tif (ri->prefix_len == prefix_len &&\n\t\t    IN6_ARE_ADDR_EQUAL(&ri->prefix, prefix))\n\t\t\treturn ri;\n\t}\n\n\tri = malloc(sizeof(struct routeinfo));\n\tif (ri == NULL)\n\t\treturn NULL;\n\n\tmemcpy(&ri->prefix, prefix, sizeof(ri->prefix));\n\tri->prefix_len = prefix_len;\n\tp = inet_ntop(AF_INET6, prefix, buf, sizeof(buf));\n\tif (p)\n\t\tsnprintf(ri->sprefix, sizeof(ri->sprefix), \"%s/%d\", p,\n\t\t    prefix_len);\n\telse\n\t\tri->sprefix[0] = '\\0';\n\tTAILQ_INSERT_TAIL(&rap->rinfos, ri, next);\n\treturn ri;\n}\n\nstatic void\nrouteinfohead_free(struct routeinfohead *head)\n{\n\tstruct routeinfo *ri;\n\n\twhile ((ri = TAILQ_FIRST(head))) {\n\t\tTAILQ_REMOVE(head, ri, next);\n\t\tfree(ri);\n\t}\n}\n"
  },
  {
    "path": "src/ipv6nd.h",
    "content": "/*\n * dhcpcd - IPv6 ND handling\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef IPV6ND_H\n#define IPV6ND_H\n\n#ifdef INET6\n\n#include <time.h>\n\n#include \"config.h\"\n#include \"dhcpcd.h\"\n#include \"ipv6.h\"\n\n/* rfc4191 */\nstruct routeinfo {\n\tTAILQ_ENTRY(routeinfo) next;\n\tstruct in6_addr prefix;\n\tuint8_t prefix_len;\n\tuint32_t lifetime;\n\tuint8_t flags;\n\tstruct timespec acquired;\n\tchar sprefix[INET6_ADDRSTRLEN];\n};\n\nTAILQ_HEAD(routeinfohead, routeinfo);\n\nstruct ra {\n\tTAILQ_ENTRY(ra) next;\n\tstruct interface *iface;\n\tstruct in6_addr from;\n\tchar sfrom[INET6_ADDRSTRLEN];\n\tuint8_t *data;\n\tsize_t data_len;\n\tstruct timespec acquired;\n\tuint8_t flags;\n\tuint32_t lifetime;\n\tuint32_t reachable;\n\tuint32_t retrans;\n\tuint32_t mtu;\n\tuint8_t hoplimit;\n\tstruct ipv6_addrhead addrs;\n\tstruct routeinfohead rinfos;\n\tbool hasdns;\n\tbool expired;\n\tbool willexpire;\n\tbool doexpire;\n\tbool isreachable;\n};\n\nTAILQ_HEAD(ra_head, ra);\n\nstruct rs_state {\n\tstruct nd_router_solicit *rs;\n\tsize_t rslen;\n\tint rsprobes;\n\tuint32_t retrans;\n#ifdef __sun\n\tint nd_fd;\n#endif\n};\n\n#define RS_STATE(a)\t    ((struct rs_state *)(ifp)->if_data[IF_DATA_IPV6ND])\n#define RS_CSTATE(a)\t    ((const struct rs_state *)(ifp)->if_data[IF_DATA_IPV6ND])\n#define RS_STATE_RUNNING(a) (ipv6nd_hasra((a)) && ipv6nd_dadcompleted((a)))\n\n#ifndef MAX_RTR_SOLICITATION_DELAY\n#define MAX_RTR_SOLICITATION_DELAY 1 /* seconds */\n#define MAX_UNICAST_SOLICIT\t   3 /* 3 transmissions */\n#define RTR_SOLICITATION_INTERVAL  4 /* seconds */\n#define MAX_RTR_SOLICITATIONS\t   3 /* times */\n#define MAX_NEIGHBOR_ADVERTISEMENT 3 /* 3 transmissions */\n\n#ifndef IPV6_DEFHLIM\n#define IPV6_DEFHLIM 64\n#endif\n#endif\n\n/* On carrier up, expire known routers after RTR_CARRIER_EXPIRE seconds. */\n#define RTR_CARRIER_EXPIRE            \\\n\t(MAX_RTR_SOLICITATION_DELAY + \\\n\t    (MAX_RTR_SOLICITATIONS + 1) * RTR_SOLICITATION_INTERVAL)\n\n#define MAX_REACHABLE_TIME     3600000 /* milliseconds */\n#define REACHABLE_TIME\t       30000   /* milliseconds */\n#define RETRANS_TIMER\t       1000    /* milliseconds */\n#define DELAY_FIRST_PROBE_TIME 5       /* seconds */\n\n#define MIN_EXTENDED_VLTIME    7200 /* seconds */\n\nint ipv6nd_open(bool);\n#ifdef __sun\nint ipv6nd_openif(struct interface *);\n#endif\nvoid ipv6nd_recvmsg(struct dhcpcd_ctx *, struct msghdr *);\nint ipv6nd_rtpref(uint8_t);\nvoid ipv6nd_printoptions(const struct dhcpcd_ctx *, const struct dhcp_opt *,\n    size_t);\nvoid ipv6nd_startrs(struct interface *);\nssize_t ipv6nd_env(FILE *, const struct interface *);\nconst struct ipv6_addr *ipv6nd_iffindaddr(const struct interface *ifp,\n    const struct in6_addr *addr, unsigned int flags);\nstruct ipv6_addr *ipv6nd_findaddr(struct dhcpcd_ctx *, const struct in6_addr *,\n    unsigned int);\nstruct ipv6_addr *ipv6nd_iffindprefix(struct interface *,\n    const struct in6_addr *, uint8_t);\nssize_t ipv6nd_free(struct interface *);\nvoid ipv6nd_expirera(void *arg);\nbool ipv6nd_hasralifetime(const struct interface *, bool);\n#define ipv6nd_hasra(i) ipv6nd_hasralifetime((i), false)\nbool ipv6nd_hasradhcp(const struct interface *, bool);\nvoid ipv6nd_handleifa(int, struct ipv6_addr *, pid_t);\nint ipv6nd_dadcompleted(const struct interface *);\nvoid ipv6nd_advertise(struct ipv6_addr *);\nvoid ipv6nd_startexpire(struct interface *);\nvoid ipv6nd_drop(struct interface *);\nvoid ipv6nd_neighbour(struct dhcpcd_ctx *, struct in6_addr *, bool);\nvoid ipv6nd_abort(struct interface *);\n#endif /* INET6 */\n\n#endif /* IPV6ND_H */\n"
  },
  {
    "path": "src/logerr.c",
    "content": "/*\n * logerr: errx with logging\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/socket.h>\n#include <sys/time.h>\n\n#include <errno.h>\n#include <stdarg.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <syslog.h>\n#include <time.h>\n#include <unistd.h>\n\n#include \"logerr.h\"\n\n#ifndef LOGERR_SYSLOG_FACILITY\n#define LOGERR_SYSLOG_FACILITY LOG_DAEMON\n#endif\n\n#ifdef SMALL\n#undef LOGERR_TAG\n#endif\n\n/* syslog protocol is 1k message max, RFC 3164 section 4.1 */\n#define LOGERR_SYSLOGBUF 1024 + sizeof(int) + sizeof(pid_t)\n\n#define UNUSED(a)\t (void)(a)\n\nstruct logctx {\n\tchar log_buf[BUFSIZ];\n\tunsigned int log_opts;\n\tint log_fd;\n\tpid_t log_pid;\n#ifndef SMALL\n\tFILE *log_file;\n#ifdef LOGERR_TAG\n\tconst char *log_tag;\n#endif\n#endif\n};\n\nstatic struct logctx _logctx = {\n\t/* syslog style, but without the hostname or tag. */\n\t.log_opts = LOGERR_LOG | LOGERR_LOG_DATE | LOGERR_LOG_PID,\n\t.log_fd = -1,\n\t.log_pid = 0,\n};\n\n#if defined(__linux__)\n/* Poor man's getprogname(3). */\nstatic char *_logprog;\nstatic const char *\ngetprogname(void)\n{\n\tconst char *p;\n\n\t/* Use PATH_MAX + 1 to avoid truncation. */\n\tif (_logprog == NULL) {\n\t\t/* readlink(2) does not append a NULL byte,\n\t\t * so zero the buffer. */\n\t\tif ((_logprog = calloc(1, PATH_MAX + 1)) == NULL)\n\t\t\treturn NULL;\n\t\tif (readlink(\"/proc/self/exe\", _logprog, PATH_MAX + 1) == -1) {\n\t\t\tfree(_logprog);\n\t\t\t_logprog = NULL;\n\t\t\treturn NULL;\n\t\t}\n\t}\n\tif (_logprog[0] == '[')\n\t\treturn NULL;\n\tp = strrchr(_logprog, '/');\n\tif (p == NULL)\n\t\treturn _logprog;\n\treturn p + 1;\n}\n#endif\n\n#ifndef SMALL\n/* Write the time, syslog style. month day time - */\nstatic int\nlogprintdate(FILE *stream)\n{\n\tstruct timeval tv;\n\ttime_t now;\n\tstruct tm tmnow;\n\tchar buf[32];\n\n\tif (gettimeofday(&tv, NULL) == -1)\n\t\treturn -1;\n\n\tnow = tv.tv_sec;\n\tif (localtime_r(&now, &tmnow) == NULL)\n\t\treturn -1;\n\tif (strftime(buf, sizeof(buf), \"%b %d %T \", &tmnow) == 0)\n\t\treturn -1;\n\treturn fprintf(stream, \"%s\", buf);\n}\n#endif\n\n__printflike(3, 0) static int vlogprintf_r(struct logctx *ctx, FILE *stream,\n    const char *fmt, va_list args)\n{\n\tint len = 0, e;\n\tva_list a;\n#ifndef SMALL\n\tbool log_pid;\n#ifdef LOGERR_TAG\n\tbool log_tag;\n#endif\n\n\tif ((stream == stderr && ctx->log_opts & LOGERR_ERR_DATE) ||\n\t    (stream != stderr && ctx->log_opts & LOGERR_LOG_DATE)) {\n\t\tif ((e = logprintdate(stream)) == -1)\n\t\t\treturn -1;\n\t\tlen += e;\n\t}\n\n#ifdef LOGERR_TAG\n\tlog_tag = ((stream == stderr && ctx->log_opts & LOGERR_ERR_TAG) ||\n\t    (stream != stderr && ctx->log_opts & LOGERR_LOG_TAG));\n\tif (log_tag) {\n\t\tif (ctx->log_tag == NULL)\n\t\t\tctx->log_tag = getprogname();\n\t\tif ((e = fprintf(stream, \"%s\", ctx->log_tag)) == -1)\n\t\t\treturn -1;\n\t\tlen += e;\n\t}\n#endif\n\n\tlog_pid = ((stream == stderr && ctx->log_opts & LOGERR_ERR_PID) ||\n\t    (stream != stderr && ctx->log_opts & LOGERR_LOG_PID));\n\tif (log_pid) {\n\t\tpid_t pid;\n\n\t\tif (ctx->log_pid == 0)\n\t\t\tpid = getpid();\n\t\telse\n\t\t\tpid = ctx->log_pid;\n\t\tif ((e = fprintf(stream, \"[%d]\", (int)pid)) == -1)\n\t\t\treturn -1;\n\t\tlen += e;\n\t}\n\n#ifdef LOGERR_TAG\n\tif (log_tag || log_pid)\n#else\n\tif (log_pid)\n#endif\n\t{\n\t\tif ((e = fprintf(stream, \": \")) == -1)\n\t\t\treturn -1;\n\t\tlen += e;\n\t}\n#else\n\tUNUSED(ctx);\n#endif\n\n\tva_copy(a, args);\n\te = vfprintf(stream, fmt, a);\n\tif (fputc('\\n', stream) == EOF)\n\t\te = -1;\n\telse if (e != -1)\n\t\te++;\n\tva_end(a);\n\n\treturn e == -1 ? -1 : len + e;\n}\n\n/*\n * NetBSD's gcc has been modified to check for the non standard %m in printf\n * like functions and warn noisily about it that they should be marked as\n * syslog like instead.\n * This is all well and good, but our logger also goes via vfprintf and\n * when marked as a sysloglike funcion, gcc will then warn us that the\n * function should be printflike instead!\n * This creates an infinte loop of gcc warnings.\n * Until NetBSD solves this issue, we have to disable a gcc diagnostic\n * for our fully standards compliant code in the logger function.\n */\n#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5))\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wmissing-format-attribute\"\n#endif\n__printflike(2, 0) static int vlogmessage(int pri, const char *fmt,\n    va_list args)\n{\n\tstruct logctx *ctx = &_logctx;\n\tint len = 0;\n\n\tif (ctx->log_fd != -1) {\n\t\tpid_t pid = getpid();\n\t\tchar buf[LOGERR_SYSLOGBUF];\n\t\tsize_t mlen = 0;\n\t\tstruct iovec iov[] = {\n\t\t\t{ .iov_base = &pri, .iov_len = sizeof(pri) },\n\t\t\t{ .iov_base = &pid, .iov_len = sizeof(pid) },\n\t\t\t{ .iov_base = &mlen, .iov_len = sizeof(mlen) },\n\t\t\t{ .iov_base = buf },\n\t\t};\n\n\t\tlen = vsnprintf(buf, sizeof(buf), fmt, args);\n\t\tif (len != -1) {\n\t\t\tmlen = (size_t)len + 1;\n\t\t\tif (mlen > sizeof(buf))\n\t\t\t\tmlen = sizeof(buf);\n\t\t\tiov[3].iov_len = mlen;\n\t\t\tstruct msghdr msg = {\n\t\t\t\t.msg_iov = iov,\n\t\t\t\t.msg_iovlen = sizeof(iov) / sizeof(iov[0]),\n\t\t\t};\n\t\t\tlen = (int)sendmsg(ctx->log_fd, &msg, 0);\n\t\t}\n\t\treturn len;\n\t}\n\n\tif (ctx->log_opts & LOGERR_ERR &&\n\t    (pri <= LOG_ERR ||\n\t\t(!(ctx->log_opts & LOGERR_QUIET) && pri <= LOG_INFO) ||\n\t\t(ctx->log_opts & LOGERR_DEBUG && pri <= LOG_DEBUG)))\n\t\tlen = vlogprintf_r(ctx, stderr, fmt, args);\n\n#ifndef SMALL\n\tif (ctx->log_file != NULL &&\n\t    (pri != LOG_DEBUG || (ctx->log_opts & LOGERR_DEBUG)))\n\t\tlen = vlogprintf_r(ctx, ctx->log_file, fmt, args);\n#endif\n\n\tif (ctx->log_opts & LOGERR_LOG)\n\t\tvsyslog(pri, fmt, args);\n\n\treturn len;\n}\n#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5))\n#pragma GCC diagnostic pop\n#endif\n\n__printflike(2, 3) void logmessage(int pri, const char *fmt, ...)\n{\n\tva_list args;\n\n\tva_start(args, fmt);\n\tvlogmessage(pri, fmt, args);\n\tva_end(args);\n}\n\n__printflike(2, 0) static void vlogerrmessage(int pri, const char *fmt,\n    va_list args)\n{\n\tint _errno = errno;\n\tchar buf[1024];\n\n\tvsnprintf(buf, sizeof(buf), fmt, args);\n\tlogmessage(pri, \"%s: %s\", buf, strerror(_errno));\n\terrno = _errno;\n}\n\n__printflike(2, 3) void logerrmessage(int pri, const char *fmt, ...)\n{\n\tva_list args;\n\n\tva_start(args, fmt);\n\tvlogerrmessage(pri, fmt, args);\n\tva_end(args);\n}\n\nvoid\nlog_debug(const char *fmt, ...)\n{\n\tva_list args;\n\n\tva_start(args, fmt);\n\tvlogerrmessage(LOG_DEBUG, fmt, args);\n\tva_end(args);\n}\n\nvoid\nlog_debugx(const char *fmt, ...)\n{\n\tva_list args;\n\n\tva_start(args, fmt);\n\tvlogmessage(LOG_DEBUG, fmt, args);\n\tva_end(args);\n}\n\nvoid\nlog_info(const char *fmt, ...)\n{\n\tva_list args;\n\n\tva_start(args, fmt);\n\tvlogerrmessage(LOG_INFO, fmt, args);\n\tva_end(args);\n}\n\nvoid\nlog_infox(const char *fmt, ...)\n{\n\tva_list args;\n\n\tva_start(args, fmt);\n\tvlogmessage(LOG_INFO, fmt, args);\n\tva_end(args);\n}\n\nvoid\nlog_warn(const char *fmt, ...)\n{\n\tva_list args;\n\n\tva_start(args, fmt);\n\tvlogerrmessage(LOG_WARNING, fmt, args);\n\tva_end(args);\n}\n\nvoid\nlog_warnx(const char *fmt, ...)\n{\n\tva_list args;\n\n\tva_start(args, fmt);\n\tvlogmessage(LOG_WARNING, fmt, args);\n\tva_end(args);\n}\n\nvoid\nlog_err(const char *fmt, ...)\n{\n\tva_list args;\n\n\tva_start(args, fmt);\n\tvlogerrmessage(LOG_ERR, fmt, args);\n\tva_end(args);\n}\n\nvoid\nlog_errx(const char *fmt, ...)\n{\n\tva_list args;\n\n\tva_start(args, fmt);\n\tvlogmessage(LOG_ERR, fmt, args);\n\tva_end(args);\n}\n\nint\nloggetfd(void)\n{\n\tstruct logctx *ctx = &_logctx;\n\n\treturn ctx->log_fd;\n}\n\nvoid\nlogsetfd(int fd)\n{\n\tstruct logctx *ctx = &_logctx;\n\n\tctx->log_fd = fd;\n\tif (fd != -1)\n\t\tcloselog();\n#ifndef SMALL\n\tif (fd != -1 && ctx->log_file != NULL) {\n\t\tfclose(ctx->log_file);\n\t\tctx->log_file = NULL;\n\t}\n#endif\n}\n\nssize_t\nlogreadfd(int fd)\n{\n\tstruct logctx *ctx = &_logctx;\n\tint pri;\n\tpid_t pid;\n\tchar buf[LOGERR_SYSLOGBUF] = { '\\0' };\n\tsize_t mlen;\n\tstruct iovec iov[] = {\n\t\t{ .iov_base = &pri, .iov_len = sizeof(pri) },\n\t\t{ .iov_base = &pid, .iov_len = sizeof(pid) },\n\t\t{ .iov_base = &mlen, .iov_len = sizeof(mlen) },\n\t};\n\tstruct msghdr msg = { .msg_iov = iov,\n\t\t.msg_iovlen = sizeof(iov) / sizeof(iov[0]) };\n\tssize_t len;\n\n\tlen = recvmsg(fd, &msg, MSG_WAITALL);\n\tif (len == -1 || len == 0)\n\t\treturn len;\n\tif ((size_t)len != sizeof(pri) + sizeof(pid) + sizeof(mlen)) {\n\t\terrno = EMSGSIZE;\n\t\treturn -1;\n\t}\n\tif (mlen > sizeof(buf)) {\n\t\terrno = ENOBUFS;\n\t\treturn -1;\n\t}\n\n\tlen = recv(fd, buf, mlen, MSG_WAITALL);\n\tif (len == -1)\n\t\treturn -1;\n\tif ((size_t)len != mlen) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\t/* Ensure what we receive is NUL terminated */\n\tbuf[mlen == sizeof(buf) ? mlen - 1 : mlen] = '\\0';\n\n\tctx->log_pid = pid;\n\tlogmessage(pri, \"%s\", buf);\n\tctx->log_pid = 0;\n\treturn len;\n}\n\nunsigned int\nloggetopts(void)\n{\n\tstruct logctx *ctx = &_logctx;\n\n\treturn ctx->log_opts;\n}\n\nvoid\nlogsetopts(unsigned int opts)\n{\n\tstruct logctx *ctx = &_logctx;\n\n\tctx->log_opts = opts;\n\tsetlogmask(LOG_UPTO(opts & LOGERR_DEBUG ? LOG_DEBUG : LOG_INFO));\n\tif (!(ctx->log_opts & LOGERR_LOG))\n\t\tcloselog();\n}\n\n#ifdef LOGERR_TAG\nvoid\nlogsettag(const char *tag)\n{\n#if !defined(SMALL)\n\tstruct logctx *ctx = &_logctx;\n\n\tctx->log_tag = tag;\n#else\n\tUNUSED(tag);\n#endif\n}\n#endif\n\nint\nlogopen(const char *path)\n{\n\tstruct logctx *ctx = &_logctx;\n\tint opts = LOG_NDELAY; /* Ensure openlog gets a fd */\n\n\t/* Cache timezone */\n\ttzset();\n\n\t(void)setvbuf(stderr, ctx->log_buf, _IOLBF, sizeof(ctx->log_buf));\n\n#ifndef SMALL\n\tif (ctx->log_file != NULL) {\n\t\tfclose(ctx->log_file);\n\t\tctx->log_file = NULL;\n\t}\n#endif\n\n\tif (ctx->log_opts & LOGERR_LOG_PID)\n\t\topts |= LOG_PID;\n\tif (ctx->log_opts & LOGERR_LOG)\n\t\topenlog(getprogname(), opts, LOGERR_SYSLOG_FACILITY);\n\tif (path == NULL)\n\t\treturn 1;\n\n#ifndef SMALL\n\tif ((ctx->log_file = fopen(path, \"ae\")) == NULL)\n\t\treturn -1;\n\tsetlinebuf(ctx->log_file);\n\treturn fileno(ctx->log_file);\n#else\n\terrno = ENOTSUP;\n\treturn -1;\n#endif\n}\n\nvoid\nlogclose(void)\n{\n#ifndef SMALL\n\tstruct logctx *ctx = &_logctx;\n#endif\n\n\tcloselog();\n#if defined(__linux__)\n\tfree(_logprog);\n\t_logprog = NULL;\n#endif\n#ifndef SMALL\n\tif (ctx->log_file == NULL)\n\t\treturn;\n\tfclose(ctx->log_file);\n\tctx->log_file = NULL;\n#endif\n}\n"
  },
  {
    "path": "src/logerr.h",
    "content": "/*\n * logerr: errx with logging\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef LOGERR_H\n#define LOGERR_H\n\n#include <sys/param.h>\n\n#ifndef __printflike\n#if __GNUC__ > 2 || defined(__INTEL_COMPILER)\n#define __printflike(a, b) __attribute__((format(printf, a, b)))\n#else\n#define __printflike(a, b)\n#endif\n#endif /* !__printflike */\n\n/* Please do not call log_* functions directly, use macros below */\n__printflike(1, 2) void log_debug(const char *, ...);\n__printflike(1, 2) void log_debugx(const char *, ...);\n__printflike(1, 2) void log_info(const char *, ...);\n__printflike(1, 2) void log_infox(const char *, ...);\n__printflike(1, 2) void log_warn(const char *, ...);\n__printflike(1, 2) void log_warnx(const char *, ...);\n__printflike(1, 2) void log_err(const char *, ...);\n__printflike(1, 2) void log_errx(const char *, ...);\n#define LOGERROR logerr(\"%s: %d\", __FILE__, __LINE__)\n\n__printflike(2, 3) void logmessage(int pri, const char *fmt, ...);\n__printflike(2, 3) void logerrmessage(int pri, const char *fmt, ...);\n\n/*\n * These are macros to prevent taking address of them so\n * __FILE__, __LINE__, etc can easily be added.\n *\n * We should be using\n * #define loginfox(fmt, __VA_OPT__(,) __VA_ARGS__)\n * but that requires gcc-8 or clang-6 and we still have a need to support\n * old OS's without modern compilers.\n *\n * Likewise, ##__VA_ARGS__ can't be used as that's a gcc only extension.\n *\n * The solution is to put fmt into __VA_ARGS__.\n * It's not pretty but it's 100% portable.\n */\n#define logdebug(...)  log_debug(__VA_ARGS__)\n#define logdebugx(...) log_debugx(__VA_ARGS__)\n#define loginfo(...)   log_info(__VA_ARGS__)\n#define loginfox(...)  log_infox(__VA_ARGS__)\n#define logwarn(...)   log_warn(__VA_ARGS__)\n#define logwarnx(...)  log_warnx(__VA_ARGS__)\n#define logerr(...)    log_err(__VA_ARGS__)\n#define logerrx(...)   log_errx(__VA_ARGS__)\n\n/* For logging in a chroot using SOCK_STREAM */\nint loggetfd(void);\nvoid logsetfd(int);\nssize_t logreadfd(int);\n\nunsigned int loggetopts(void);\nvoid logsetopts(unsigned int);\n#define LOGERR_DEBUG\t(1U << 6)\n#define LOGERR_QUIET\t(1U << 7)\n#define LOGERR_LOG\t(1U << 11)\n#define LOGERR_LOG_DATE (1U << 12)\n#define LOGERR_LOG_HOST (1U << 13)\n#define LOGERR_LOG_TAG\t(1U << 14)\n#define LOGERR_LOG_PID\t(1U << 15)\n#define LOGERR_ERR\t(1U << 21)\n#define LOGERR_ERR_DATE (1U << 22)\n#define LOGERR_ERR_HOST (1U << 23)\n#define LOGERR_ERR_TAG\t(1U << 24)\n#define LOGERR_ERR_PID\t(1U << 25)\n\n/* To build tag support or not. */\n// #define\tLOGERR_TAG\n#if defined(LOGERR_TAG)\nvoid logsettag(const char *);\n#endif\n\n/* Can be called more than once. */\nint logopen(const char *);\n\n/* Should only be called at program exit. */\nvoid logclose(void);\n\n#endif\n"
  },
  {
    "path": "src/privsep-bpf.c",
    "content": "/*\n * Privilege Separation BPF Initiator\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/types.h>\n#include <sys/socket.h>\n\n/* Need these headers just for if_ether on some OS. */\n#ifndef __NetBSD__\n#include <net/if.h>\n#include <net/if_arp.h>\n#include <netinet/in.h>\n#endif\n#include <netinet/if_ether.h>\n\n#include <assert.h>\n#include <errno.h>\n#include <pwd.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include \"arp.h\"\n#include \"bpf.h\"\n#include \"dhcp.h\"\n#include \"dhcp6.h\"\n#include \"eloop.h\"\n#include \"ipv6nd.h\"\n#include \"logerr.h\"\n#include \"privsep.h\"\n\n/* We expect to have open 3 SOCK_STREAM and one RAW fd */\n\nstatic void\nps_bpf_recvbpf(void *arg, unsigned short events)\n{\n\tstruct ps_process *psp = arg;\n\tstruct bpf *bpf = psp->psp_bpf;\n\tuint8_t buf[FRAMELEN_MAX];\n\tssize_t len;\n\tstruct ps_msghdr psm = {\n\t\t.ps_id = psp->psp_id,\n\t\t.ps_cmd = psp->psp_id.psi_cmd,\n\t};\n\n\tif (!(events & (ELE_READ | ELE_ERROR)))\n\t\tlogerrx(\"%s: unexpected event 0x%04x\", __func__, events);\n\n\tbpf->bpf_flags &= ~BPF_EOF;\n\t/* A BPF read can read more than one filtered packet at time.\n\t * This mechanism allows us to read each packet from the buffer. */\n\twhile (!(bpf->bpf_flags & BPF_EOF)) {\n\t\tlen = bpf_read(bpf, buf, sizeof(buf));\n\t\tif (len == -1) {\n\t\t\tint error = errno;\n\n\t\t\tif (errno != ENETDOWN)\n\t\t\t\tlogerr(\"%s: %s\", psp->psp_ifname, __func__);\n\t\t\tif (error != ENXIO)\n\t\t\t\tbreak;\n\t\t\t/* If the interface has departed, close the BPF\n\t\t\t * socket. This stops log spam if RTM_IFANNOUNCE is\n\t\t\t * delayed in announcing the departing interface. */\n\t\t\teloop_event_delete(psp->psp_ctx->eloop, bpf->bpf_fd);\n\t\t\tbpf_close(bpf);\n\t\t\tpsp->psp_bpf = NULL;\n\t\t\tbreak;\n\t\t}\n\t\tif (len == 0)\n\t\t\tbreak;\n\t\tpsm.ps_flags = bpf->bpf_flags;\n\t\tlen = ps_sendpsmdata(psp->psp_ctx, psp->psp_ctx->ps_data_fd,\n\t\t    &psm, buf, (size_t)len);\n\t\tif (len == -1)\n\t\t\tlogerr(__func__);\n\t\tif (len == -1 || len == 0)\n\t\t\tbreak;\n\t}\n}\n\nstatic ssize_t\nps_bpf_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)\n{\n\tstruct ps_process *psp = arg;\n\tstruct iovec *iov = msg->msg_iov;\n\n#ifdef PRIVSEP_DEBUG\n\tlogerrx(\"%s: IN cmd %x, psp %p\", __func__, psm->ps_cmd, psp);\n#endif\n\n\tswitch (psm->ps_cmd) {\n#ifdef ARP\n\tcase PS_BPF_ARP: /* FALLTHROUGH */\n#endif\n\tcase PS_BPF_BOOTP:\n\t\tbreak;\n\tdefault:\n\t\t/* IPC failure, we should not be processing any commands\n\t\t * at this point!/ */\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\t/* We might have had an earlier ENXIO error. */\n\tif (psp->psp_bpf == NULL) {\n\t\terrno = ENXIO;\n\t\treturn -1;\n\t}\n\n\treturn bpf_send(psp->psp_bpf, psp->psp_proto, iov->iov_base,\n\t    iov->iov_len);\n}\n\nstatic void\nps_bpf_recvmsg(void *arg, unsigned short events)\n{\n\tstruct ps_process *psp = arg;\n\n\tif (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, ps_bpf_recvmsgcb,\n\t\targ) == -1)\n\t\tlogerr(__func__);\n}\n\nstatic int\nps_bpf_start_bpf(struct ps_process *psp)\n{\n\tstruct dhcpcd_ctx *ctx = psp->psp_ctx;\n\tchar *addr;\n\tstruct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr;\n\n\tif (ia->s_addr == INADDR_ANY) {\n\t\tia = NULL;\n\t\taddr = NULL;\n\t} else\n\t\taddr = inet_ntoa(*ia);\n\tsetproctitle(\"[BPF %s] %s%s%s\", psp->psp_protostr, psp->psp_ifname,\n\t    addr != NULL ? \" \" : \"\", addr != NULL ? addr : \"\");\n\tps_freeprocesses(ctx, psp);\n\n\tpsp->psp_bpf = bpf_open(&psp->psp_ifp, psp->psp_filter, ia);\n#ifdef DEBUG_FD\n\tlogdebugx(\"pid %d bpf_fd=%d\", getpid(), psp->psp_bpf->bpf_fd);\n#endif\n\tif (psp->psp_bpf == NULL)\n\t\tlogerr(\"%s: bpf_open\", __func__);\n#ifdef PRIVSEP_RIGHTS\n\telse if (ps_rights_limit_fd(psp->psp_bpf->bpf_fd) == -1)\n\t\tlogerr(\"%s: ps_rights_limit_fd\", __func__);\n#endif\n\telse if (eloop_event_add(ctx->eloop, psp->psp_bpf->bpf_fd, ELE_READ,\n\t\t     ps_bpf_recvbpf, psp) == -1)\n\t\tlogerr(\"%s: eloop_event_add\", __func__);\n\telse {\n\t\tpsp->psp_work_fd = psp->psp_bpf->bpf_fd;\n\t\treturn 0;\n\t}\n\n\teloop_exit(ctx->eloop, EXIT_FAILURE);\n\treturn -1;\n}\n\nssize_t\nps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)\n{\n\tuint16_t cmd;\n\tstruct ps_process *psp;\n\tpid_t start;\n\tstruct iovec *iov = msg->msg_iov;\n\tstruct interface *ifp;\n\tstruct in_addr *ia = &psm->ps_id.psi_addr.psa_in_addr;\n\tconst char *addr;\n\n\tcmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));\n\tpsp = ps_findprocess(ctx, &psm->ps_id);\n\n#ifdef PRIVSEP_DEBUG\n\tlogerrx(\"%s: IN cmd %x, psp %p\", __func__, psm->ps_cmd, psp);\n#endif\n\n\tswitch (cmd) {\n#ifdef ARP\n\tcase PS_BPF_ARP: /* FALLTHROUGH */\n#endif\n\tcase PS_BPF_BOOTP:\n\t\tbreak;\n\tdefault:\n\t\tlogerrx(\"%s: unknown command %x\", __func__, psm->ps_cmd);\n\t\terrno = ENOTSUP;\n\t\treturn -1;\n\t}\n\n\tif (!(psm->ps_cmd & PS_START)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tif (psp != NULL)\n\t\treturn 1;\n\n\tpsp = ps_newprocess(ctx, &psm->ps_id);\n\tif (psp == NULL)\n\t\treturn -1;\n\n\tifp = &psp->psp_ifp;\n\tassert(msg->msg_iovlen == 1);\n\tassert(iov->iov_len == sizeof(*ifp));\n\tmemcpy(ifp, iov->iov_base, sizeof(*ifp));\n\tifp->ctx = psp->psp_ctx;\n\tifp->options = NULL;\n\tmemset(ifp->if_data, 0, sizeof(ifp->if_data));\n\n\tmemcpy(psp->psp_ifname, ifp->name, sizeof(psp->psp_ifname));\n\n\tswitch (cmd) {\n#ifdef ARP\n\tcase PS_BPF_ARP:\n\t\tpsp->psp_proto = ETHERTYPE_ARP;\n\t\tpsp->psp_protostr = \"ARP\";\n\t\tpsp->psp_filter = bpf_filter_arp;\n\t\tbreak;\n#endif\n\tcase PS_BPF_BOOTP:\n\t\tpsp->psp_proto = ETHERTYPE_IP;\n\t\tpsp->psp_protostr = \"BOOTP\";\n\t\tpsp->psp_filter = bpf_filter_bootp;\n\t\tbreak;\n\t}\n\n\tif (ia->s_addr == INADDR_ANY)\n\t\taddr = NULL;\n\telse\n\t\taddr = inet_ntoa(*ia);\n\tsnprintf(psp->psp_name, sizeof(psp->psp_name), \"BPF %s%s%s\",\n\t    psp->psp_protostr, addr != NULL ? \" \" : \"\",\n\t    addr != NULL ? addr : \"\");\n\n\tstart = ps_startprocess(psp, ps_bpf_recvmsg, NULL, ps_bpf_start_bpf,\n\t    PSF_DROPPRIVS);\n\n\tswitch (start) {\n\tcase -1:\n\t\tps_freeprocess(psp);\n\t\treturn -1;\n\tcase 0:\n\t\tps_entersandbox(\"stdio\", NULL);\n\t\tbreak;\n\tdefault:\n\t\tlogdebugx(\"%s: spawned %s on PID %d\", psp->psp_ifname,\n\t\t    psp->psp_name, psp->psp_pid);\n\t\tbreak;\n\t}\n\treturn start;\n}\n\nssize_t\nps_bpf_dispatch(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm,\n    struct msghdr *msg)\n{\n\tstruct iovec *iov = msg->msg_iov;\n\tstruct interface *ifp;\n\tuint8_t *bpf;\n\tsize_t bpf_len;\n\n\tswitch (psm->ps_cmd) {\n#ifdef ARP\n\tcase PS_BPF_ARP:\n#endif\n\tcase PS_BPF_BOOTP:\n\t\tbreak;\n\tdefault:\n\t\terrno = ENOTSUP;\n\t\treturn -1;\n\t}\n\n\tifp = if_findindex(ctx->ifaces, psm->ps_id.psi_ifindex);\n\t/* interface may have departed .... */\n\tif (ifp == NULL)\n\t\treturn -1;\n\n\tbpf = iov->iov_base;\n\tbpf_len = iov->iov_len;\n\n\tswitch (psm->ps_cmd) {\n#ifdef ARP\n\tcase PS_BPF_ARP:\n\t\tarp_packet(ifp, bpf, bpf_len, (unsigned int)psm->ps_flags);\n\t\tbreak;\n#endif\n\tcase PS_BPF_BOOTP:\n\t\tdhcp_packet(ifp, bpf, bpf_len, (unsigned int)psm->ps_flags);\n\t\tbreak;\n\t}\n\n\treturn 1;\n}\n\nstatic ssize_t\nps_bpf_send(const struct interface *ifp, const struct in_addr *ia, uint16_t cmd,\n    const void *data, size_t len)\n{\n\tstruct dhcpcd_ctx *ctx = ifp->ctx;\n\tstruct ps_msghdr psm = {\n\t\t.ps_cmd = cmd,\n\t\t.ps_id = {\n\t\t\t.psi_ifindex = ifp->index,\n\t\t\t.psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),\n\t\t},\n\t};\n\n\tif (ia != NULL)\n\t\tpsm.ps_id.psi_addr.psa_in_addr = *ia;\n\n\treturn ps_sendpsmdata(ctx, PS_ROOT_FD(ctx), &psm, data, len);\n}\n\n#ifdef ARP\nssize_t\nps_bpf_openarp(const struct interface *ifp, const struct in_addr *ia)\n{\n\tassert(ia != NULL);\n\treturn ps_bpf_send(ifp, ia, PS_BPF_ARP | PS_START, ifp, sizeof(*ifp));\n}\n\nssize_t\nps_bpf_closearp(const struct interface *ifp, const struct in_addr *ia)\n{\n\treturn ps_bpf_send(ifp, ia, PS_BPF_ARP | PS_STOP, NULL, 0);\n}\n\nssize_t\nps_bpf_sendarp(const struct interface *ifp, const struct in_addr *ia,\n    const void *data, size_t len)\n{\n\tassert(ia != NULL);\n\treturn ps_bpf_send(ifp, ia, PS_BPF_ARP, data, len);\n}\n#endif\n\nssize_t\nps_bpf_openbootp(const struct interface *ifp)\n{\n\treturn ps_bpf_send(ifp, NULL, PS_BPF_BOOTP | PS_START, ifp,\n\t    sizeof(*ifp));\n}\n\nssize_t\nps_bpf_closebootp(const struct interface *ifp)\n{\n\treturn ps_bpf_send(ifp, NULL, PS_BPF_BOOTP | PS_STOP, NULL, 0);\n}\n\nssize_t\nps_bpf_sendbootp(const struct interface *ifp, const void *data, size_t len)\n{\n\treturn ps_bpf_send(ifp, NULL, PS_BPF_BOOTP, data, len);\n}\n"
  },
  {
    "path": "src/privsep-bpf.h",
    "content": "/*\n * Privilege Separation for dhcpcd\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef PRIVSEP_BPF_H\n#define PRIVSEP_BPF_H\n\nssize_t ps_bpf_cmd(struct dhcpcd_ctx *, struct ps_msghdr *, struct msghdr *);\nssize_t ps_bpf_dispatch(struct dhcpcd_ctx *, struct ps_msghdr *,\n    struct msghdr *);\n\n#ifdef ARP\nssize_t ps_bpf_openarp(const struct interface *, const struct in_addr *);\nssize_t ps_bpf_closearp(const struct interface *, const struct in_addr *);\nssize_t ps_bpf_sendarp(const struct interface *, const struct in_addr *,\n    const void *, size_t);\n#endif\n\nssize_t ps_bpf_openbootp(const struct interface *);\nssize_t ps_bpf_closebootp(const struct interface *);\nssize_t ps_bpf_sendbootp(const struct interface *, const void *, size_t);\nssize_t ps_bpf_openbootpudp(const struct interface *);\nssize_t ps_bpf_closebootpudp(const struct interface *);\nssize_t ps_bpf_sendbootpudp(const struct interface *, const void *, size_t);\n#endif\n"
  },
  {
    "path": "src/privsep-bsd.c",
    "content": "/*\n * Privilege Separation for dhcpcd, BSD driver\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/types.h>\n#include <sys/ioctl.h>\n#include <sys/sysctl.h>\n\n/* Need these for filtering the ioctls */\n#include <net/if.h>\n#include <netinet/in.h>\n#include <netinet6/in6_var.h>\n#include <netinet6/nd6.h>\n#include <netinet/if_ether.h>\n\n#include <arpa/inet.h>\n#ifdef __NetBSD__\n#include <net/if_vlanvar.h> /* Needs netinet/if_ether.h */\n#include <netinet/if_ether.h>\n#elif defined(__DragonFly__)\n#include <net/vlan/if_vlan_var.h>\n#else\n#include <net/if_vlan_var.h>\n#endif\n#ifdef __DragonFly__\n#include <netproto/802_11/ieee80211_ioctl.h>\n#else\n#include <net80211/ieee80211.h>\n#include <net80211/ieee80211_ioctl.h>\n#endif\n\n#include <errno.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include \"dhcpcd.h\"\n#include \"if.h\"\n#include \"logerr.h\"\n#include \"privsep.h\"\n\nstatic ssize_t\nps_root_doioctldom(struct dhcpcd_ctx *ctx, int domain, unsigned long req,\n    void *data, size_t len)\n{\n#if defined(INET6) || (defined(SIOCALIFADDR) && defined(IFLR_ACTIVE))\n\tstruct priv *priv = (struct priv *)ctx->priv;\n#endif\n\tint s;\n\n\tswitch (domain) {\n#ifdef INET\n\tcase PF_INET:\n\t\ts = ctx->pf_inet_fd;\n\t\tbreak;\n#endif\n#ifdef INET6\n\tcase PF_INET6:\n\t\ts = priv->pf_inet6_fd;\n\t\tbreak;\n#endif\n#if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */\n\tcase PF_LINK:\n\t\ts = priv->pf_link_fd;\n\t\tbreak;\n#endif\n\tdefault:\n\t\terrno = EPFNOSUPPORT;\n\t\treturn -1;\n\t}\n\n\t/* Only allow these ioctls */\n\tswitch (req) {\n#ifdef SIOCGIFDATA\n\tcase SIOCGIFDATA: /* FALLTHROUGH */\n#endif\n#ifdef SIOCG80211NWID\n\tcase SIOCG80211NWID: /* FALLTHROUGH */\n#endif\n#ifdef SIOCGETVLAN\n\tcase SIOCGETVLAN: /* FALLTHROUGH */\n#endif\n#ifdef SIOCIFAFATTACH\n\tcase SIOCIFAFATTACH: /* FALLTHROUGH */\n#endif\n#ifdef SIOCSIFXFLAGS\n\tcase SIOCSIFXFLAGS: /* FALLTHROUGH */\n#endif\n#ifdef SIOCSIFINFO_FLAGS\n\tcase SIOCSIFINFO_FLAGS: /* FALLTHROUGH */\n#endif\n#ifdef SIOCSRTRFLUSH_IN6\n\tcase SIOCSRTRFLUSH_IN6: /* FALLTHROUGH */\n\tcase SIOCSPFXFLUSH_IN6: /* FALLTHROUGH */\n#endif\n#if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE)\n\tcase SIOCALIFADDR: /* FALLTHROUGH */\n\tcase SIOCDLIFADDR: /* FALLTHROUGH */\n#else\n\tcase SIOCSIFLLADDR: /* FALLTHROUGH */\n#endif\n#ifdef SIOCSIFINFO_IN6\n\tcase SIOCSIFINFO_IN6: /* FALLTHROUGH */\n#endif\n\tcase SIOCAIFADDR_IN6: /* FALLTHROUGH */\n\tcase SIOCDIFADDR_IN6:\n\t\tbreak;\n\tdefault:\n\t\terrno = EPERM;\n\t\treturn -1;\n\t}\n\n\treturn ioctl(s, req, data, len);\n}\n\nstatic ssize_t\nps_root_doroute(struct dhcpcd_ctx *ctx, void *data, size_t len)\n{\n\treturn write(ctx->link_fd, data, len);\n}\n\n#if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE)\nstatic ssize_t\nps_root_doindirectioctl(struct dhcpcd_ctx *ctx, unsigned long req, void *data,\n    size_t len)\n{\n\tchar *p = data;\n\tstruct ifreq ifr = { .ifr_flags = 0 };\n\tsize_t ifnamelen;\n\n\t/* ioctl filtering is done in ps_root_doioctldom */\n\n\tif (len < sizeof(ifnamelen)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\tmemcpy(&ifnamelen, p, sizeof(ifnamelen));\n\tlen -= sizeof(ifnamelen);\n\n\tif (len < ifnamelen || ifnamelen > sizeof(ifr.ifr_name)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tmemcpy(ifr.ifr_name, p, ifnamelen);\n\tlen -= ifnamelen;\n\n\tif (len != 0) {\n\t\t/* Ensure data is now aligned */\n\t\tmemmove(data, p + ifnamelen, len);\n\t\tifr.ifr_data = data;\n\t}\n\n\treturn ps_root_doioctldom(ctx, PF_INET, req, &ifr, sizeof(ifr));\n}\n#endif\n\n#ifdef HAVE_PLEDGE\nstatic ssize_t\nps_root_doifignoregroup(struct dhcpcd_ctx *ctx, void *data, size_t len)\n{\n\tif (len == 0 || ((const char *)data)[len - 1] != '\\0') {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\treturn if_ignoregroup(ctx->pf_inet_fd, data);\n}\n#endif\n\n#ifdef HAVE_CAPSICUM\nstatic ssize_t\nps_root_dosysctl(unsigned long flags, void *data, size_t len, void **rdata,\n    size_t *rlen)\n{\n\tchar *p = data, *e = p + len;\n\tint name[10];\n\tunsigned int namelen;\n\tvoid *oldp;\n\tsize_t *oldlenp, oldlen, nlen;\n\tvoid *newp;\n\tsize_t newlen;\n\tint err;\n\n\tif (sizeof(namelen) >= len) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\tmemcpy(&namelen, p, sizeof(namelen));\n\tp += sizeof(namelen);\n\tnlen = sizeof(*name) * namelen;\n\tif (namelen > __arraycount(name)) {\n\t\terrno = ENOBUFS;\n\t\treturn -1;\n\t}\n\tif (p + nlen > e) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\tmemcpy(name, p, nlen);\n\tp += nlen;\n\tif (p + sizeof(oldlen) > e) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\tmemcpy(&oldlen, p, sizeof(oldlen));\n\tp += sizeof(oldlen);\n\tif (p + sizeof(newlen) > e) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\tmemcpy(&newlen, p, sizeof(newlen));\n\tp += sizeof(newlen);\n\tif (p + newlen > e) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\tnewp = newlen ? p : NULL;\n\n\tif (flags & PS_SYSCTL_OLEN) {\n\t\t*rlen = sizeof(oldlen) + oldlen;\n\t\t*rdata = malloc(*rlen);\n\t\tif (*rdata == NULL)\n\t\t\treturn -1;\n\t\toldlenp = (size_t *)*rdata;\n\t\t*oldlenp = oldlen;\n\t\tif (flags & PS_SYSCTL_ODATA)\n\t\t\toldp = (char *)*rdata + sizeof(oldlen);\n\t\telse\n\t\t\toldp = NULL;\n\t} else {\n\t\toldlenp = NULL;\n\t\toldp = NULL;\n\t}\n\n\terr = sysctl(name, namelen, oldp, oldlenp, newp, newlen);\n\treturn err;\n}\n#endif\n\nssize_t\nps_root_os(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg,\n    void **rdata, size_t *rlen, bool *free_rdata)\n{\n\tstruct iovec *iov = msg->msg_iov;\n\tvoid *data = iov->iov_base;\n\tsize_t len = iov->iov_len;\n\tssize_t err;\n\n\tswitch (psm->ps_cmd) {\n\tcase PS_IOCTLLINK:\n\t\terr = ps_root_doioctldom(ctx, PF_LINK, psm->ps_flags, data,\n\t\t    len);\n\t\tbreak;\n\tcase PS_IOCTL6:\n\t\terr = ps_root_doioctldom(ctx, PF_INET6, psm->ps_flags, data,\n\t\t    len);\n\t\tbreak;\n\tcase PS_ROUTE:\n\t\treturn ps_root_doroute(ctx, data, len);\n#if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE)\n\tcase PS_IOCTLINDIRECT:\n\t\terr = ps_root_doindirectioctl(ctx, psm->ps_flags, data, len);\n\t\tbreak;\n#endif\n#ifdef HAVE_PLEDGE\n\tcase PS_IFIGNOREGRP:\n\t\treturn ps_root_doifignoregroup(ctx, data, len);\n#endif\n#ifdef HAVE_CAPSICUM\n\tcase PS_SYSCTL:\n\t\t*free_rdata = true;\n\t\treturn ps_root_dosysctl(psm->ps_flags, data, len, rdata, rlen);\n#else\n\t\tUNUSED(free_rdata);\n#endif\n\tdefault:\n\t\terrno = ENOTSUP;\n\t\treturn -1;\n\t}\n\n\tif (err != -1) {\n\t\t*rdata = data;\n\t\t*rlen = len;\n\t}\n\treturn err;\n}\n\nstatic ssize_t\nps_root_ioctldom(struct dhcpcd_ctx *ctx, uint16_t domain, unsigned long request,\n    void *data, size_t len)\n{\n\tif (ps_sendcmd(ctx, PS_ROOT_FD(ctx), domain, request, data, len) == -1)\n\t\treturn -1;\n\treturn ps_root_readerror(ctx, data, len);\n}\n\nssize_t\nps_root_ioctllink(struct dhcpcd_ctx *ctx, unsigned long request, void *data,\n    size_t len)\n{\n\treturn ps_root_ioctldom(ctx, PS_IOCTLLINK, request, data, len);\n}\n\nssize_t\nps_root_ioctl6(struct dhcpcd_ctx *ctx, unsigned long request, void *data,\n    size_t len)\n{\n\treturn ps_root_ioctldom(ctx, PS_IOCTL6, request, data, len);\n}\n\nssize_t\nps_root_route(struct dhcpcd_ctx *ctx, void *data, size_t len)\n{\n\tif (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_ROUTE, 0, data, len) == -1)\n\t\treturn -1;\n\treturn ps_root_readerror(ctx, data, len);\n}\n\n#if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE)\nssize_t\nps_root_indirectioctl(struct dhcpcd_ctx *ctx, unsigned long request,\n    const char *ifname, void *data, size_t len)\n{\n\tsize_t ifnamelen = strlen(ifname + 1);\n\n\tstruct iovec iov[] = { {\n\t\t\t\t   .iov_base = &ifnamelen,\n\t\t\t\t   .iov_len = sizeof(ifnamelen),\n\t\t\t       },\n\t\t{\n\t\t    .iov_base = UNCONST(ifname),\n\t\t    .iov_len = ifnamelen,\n\t\t},\n\t\t{\n\t\t    .iov_base = data,\n\t\t    .iov_len = len,\n\t\t} };\n\tstruct msghdr msg = {\n\t\t.msg_iov = iov,\n\t\t.msg_iovlen = __arraycount(iov),\n\t};\n\n\tif (ps_sendcmdmsg(ctx, PS_ROOT_FD(ctx), PS_IOCTLINDIRECT, request,\n\t\t&msg) == -1)\n\t\treturn -1;\n\treturn ps_root_readerror(ctx, data, len);\n}\n#endif\n\n#ifdef HAVE_PLEDGE\nssize_t\nps_root_ifignoregroup(struct dhcpcd_ctx *ctx, const char *ifname)\n{\n\tif (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_IFIGNOREGRP, 0, ifname,\n\t\tstrlen(ifname) + 1) == -1)\n\t\treturn -1;\n\treturn ps_root_readerror(ctx, NULL, 0);\n}\n#endif\n\n#ifdef HAVE_CAPSICUM\nssize_t\nps_root_sysctl(struct dhcpcd_ctx *ctx, const int *name, unsigned int namelen,\n    void *oldp, size_t *oldlenp, const void *newp, size_t newlen)\n{\n\tchar buf[PS_BUFLEN], *p = buf;\n\tunsigned long flags = 0;\n\tsize_t olen = (oldp && oldlenp) ? *oldlenp : 0, nolen;\n\n\tif (sizeof(namelen) + (sizeof(*name) * namelen) + sizeof(oldlenp) +\n\t\tsizeof(newlen) + newlen >\n\t    sizeof(buf)) {\n\t\terrno = ENOBUFS;\n\t\treturn -1;\n\t}\n\n\tif (oldlenp)\n\t\tflags |= PS_SYSCTL_OLEN;\n\tif (oldp)\n\t\tflags |= PS_SYSCTL_ODATA;\n\tmemcpy(p, &namelen, sizeof(namelen));\n\tp += sizeof(namelen);\n\tmemcpy(p, name, sizeof(*name) * namelen);\n\tp += sizeof(*name) * namelen;\n\tmemcpy(p, &olen, sizeof(olen));\n\tp += sizeof(olen);\n\tmemcpy(p, &newlen, sizeof(newlen));\n\tp += sizeof(newlen);\n\tif (newlen) {\n\t\tmemcpy(p, newp, newlen);\n\t\tp += newlen;\n\t}\n\n\tif (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_SYSCTL, flags, buf,\n\t\t(size_t)(p - buf)) == -1)\n\t\treturn -1;\n\n\tif (ps_root_readerror(ctx, buf, sizeof(buf)) == -1)\n\t\treturn -1;\n\n\tp = buf;\n\tmemcpy(&nolen, p, sizeof(nolen));\n\tp += sizeof(nolen);\n\tif (oldlenp) {\n\t\t*oldlenp = nolen;\n\t\tif (oldp && nolen <= olen)\n\t\t\tmemcpy(oldp, p, nolen);\n\t}\n\n\treturn 0;\n}\n#endif\n"
  },
  {
    "path": "src/privsep-control.c",
    "content": "/*\n * Privilege Separation for dhcpcd, control proxy\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <errno.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"control.h\"\n#include \"dhcpcd.h\"\n#include \"eloop.h\"\n#include \"logerr.h\"\n#include \"privsep.h\"\n\n/* We expect to have open 2 privsep STREAM, 2 STREAM and 2 file STREAM fds */\n\nstatic int\nps_ctl_startcb(struct ps_process *psp)\n{\n\tstruct dhcpcd_ctx *ctx = psp->psp_ctx;\n\tsa_family_t af;\n\n\tif (ctx->options & DHCPCD_MANAGER) {\n\t\tsetproctitle(\"[control proxy]\");\n\t\taf = AF_UNSPEC;\n\t} else {\n\t\tsetproctitle(\"[control proxy] %s%s%s\", ctx->ifv[0],\n\t\t    ctx->options & DHCPCD_IPV4 ? \" [ip4]\" : \"\",\n\t\t    ctx->options & DHCPCD_IPV6 ? \" [ip6]\" : \"\");\n\t\tif ((ctx->options & (DHCPCD_IPV4 | DHCPCD_IPV6)) == DHCPCD_IPV4)\n\t\t\taf = AF_INET;\n\t\telse if ((ctx->options & (DHCPCD_IPV4 | DHCPCD_IPV6)) ==\n\t\t    DHCPCD_IPV6)\n\t\t\taf = AF_INET6;\n\t\telse\n\t\t\taf = AF_UNSPEC;\n\t}\n\n\treturn control_start(ctx,\n\t    ctx->options & DHCPCD_MANAGER ? NULL : *ctx->ifv, af);\n}\n\nstatic void\nps_ctl_recvmsg(void *arg, unsigned short events)\n{\n\tstruct ps_process *psp = arg;\n\n\tif (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, NULL, NULL) == -1)\n\t\tlogerr(__func__);\n}\n\nssize_t\nps_ctl_handleargs(struct fd_list *fd, char *data, size_t len)\n{\n\t/* Make any change here in dhcpcd.c as well. */\n\tif (strncmp(data, \"--version\", MIN(strlen(\"--version\"), len)) == 0) {\n\t\treturn control_queue(fd, UNCONST(VERSION), strlen(VERSION) + 1);\n\t} else if (strncmp(data, \"--getconfigfile\",\n\t\t       MIN(strlen(\"--getconfigfile\"), len)) == 0) {\n\t\treturn control_queue(fd, UNCONST(fd->ctx->cffile),\n\t\t    strlen(fd->ctx->cffile) + 1);\n\t} else if (strncmp(data, \"--listen\", MIN(strlen(\"--listen\"), len)) ==\n\t    0) {\n\t\tfd->flags |= FD_LISTEN;\n\t\treturn 0;\n\t}\n\n\tif (fd->ctx->ps_control_client != NULL &&\n\t    fd->ctx->ps_control_client != fd) {\n\t\tlogerrx(\"%s: cannot handle another client\", __func__);\n\t\treturn 0;\n\t}\n\treturn 1;\n}\n\nstatic ssize_t\nps_ctl_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\tstruct iovec *iov = msg->msg_iov;\n\tstruct fd_list *fd;\n\tunsigned int fd_flags = FD_SENDLEN;\n\n\tswitch (psm->ps_flags) {\n\tcase PS_CTL_PRIV:\n\t\tbreak;\n\tcase PS_CTL_UNPRIV:\n\t\tfd_flags |= FD_UNPRIV;\n\t\tbreak;\n\t}\n\n\tswitch (psm->ps_cmd) {\n\tcase PS_CTL:\n\t\tif (msg->msg_iovlen != 1) {\n\t\t\terrno = EINVAL;\n\t\t\treturn -1;\n\t\t}\n\t\tif (ctx->ps_control_client != NULL) {\n\t\t\tlogerrx(\"%s: cannot handle another client\", __func__);\n\t\t\treturn 0;\n\t\t}\n\t\tfd = control_new(ctx, ctx->ps_ctl->psp_work_fd, fd_flags);\n\t\tif (fd == NULL)\n\t\t\treturn -1;\n\t\tctx->ps_control_client = fd;\n\t\tcontrol_recvdata(fd, iov->iov_base, iov->iov_len);\n\t\tbreak;\n\tcase PS_CTL_EOF:\n\t\tctx->ps_control_client = NULL;\n\t\tbreak;\n\tdefault:\n\t\terrno = ENOTSUP;\n\t\treturn -1;\n\t}\n\treturn 0;\n}\n\nstatic void\nps_ctl_dodispatch(void *arg, unsigned short events)\n{\n\tstruct ps_process *psp = arg;\n\n\tif (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, ps_ctl_dispatch,\n\t\tpsp->psp_ctx) == -1)\n\t\tlogerr(__func__);\n}\n\nstatic void\nps_ctl_recv(void *arg, unsigned short events)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\tchar buf[BUFSIZ];\n\tssize_t len;\n\n\tif (!(events & (ELE_READ | ELE_HANGUP)))\n\t\tlogerrx(\"%s: unexpected event 0x%04x\", __func__, events);\n\n\tif (events & ELE_READ) {\n\t\tlen = read(ctx->ps_ctl->psp_work_fd, buf, sizeof(buf));\n\t\tif (len == -1)\n\t\t\tlogerr(\"%s: read\", __func__);\n\t\telse if (len == 0)\n\t\t\t// FIXME: Why does this happen?\n\t\t\t;\n\t\telse if (ctx->ps_control_client == NULL)\n\t\t\tlogerrx(\"%s: clientfd #%d disconnected (len=%zd)\",\n\t\t\t    __func__, ctx->ps_ctl->psp_work_fd, len);\n\t\telse {\n\t\t\terrno = 0;\n\t\t\tif (control_queue(ctx->ps_control_client, buf,\n\t\t\t\t(size_t)len) == -1)\n\t\t\t\tlogerr(\"%s: control_queue\", __func__);\n\t\t}\n\t}\n}\n\nstatic void\nps_ctl_listen(void *arg, unsigned short events)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\tchar buf[BUFSIZ];\n\tssize_t len;\n\tstruct fd_list *fd;\n\n\tif (!(events & ELE_READ))\n\t\tlogerrx(\"%s: unexpected event 0x%04x\", __func__, events);\n\n\tlen = read(ctx->ps_control->fd, buf, sizeof(buf));\n\tif (len == -1) {\n\t\tlogerr(\"%s: read\", __func__);\n\t\teloop_exit(ctx->eloop, EXIT_FAILURE);\n\t\treturn;\n\t}\n\n\t/* Send to our listeners */\n\tTAILQ_FOREACH(fd, &ctx->control_fds, next) {\n\t\tif (!(fd->flags & FD_LISTEN))\n\t\t\tcontinue;\n\t\tif (control_queue(fd, buf, (size_t)len) == -1)\n\t\t\tlogerr(\"%s: control_queue\", __func__);\n\t}\n}\n\npid_t\nps_ctl_start(struct dhcpcd_ctx *ctx)\n{\n\tstruct ps_id id = {\n\t\t.psi_ifindex = 0,\n\t\t.psi_cmd = PS_CTL,\n\t};\n\tstruct ps_process *psp;\n\tint work_fd[2], listen_fd[2];\n\tpid_t pid;\n\n\tif_closesockets(ctx);\n\n\tif (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, work_fd) == -1 ||\n\t    xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, listen_fd) == -1)\n\t\treturn -1;\n#ifdef PRIVSEP_RIGHTS\n\tif (ps_rights_limit_fdpair(work_fd) == -1 ||\n\t    ps_rights_limit_fdpair(listen_fd) == -1)\n\t\treturn -1;\n#endif\n\n\tpsp = ctx->ps_ctl = ps_newprocess(ctx, &id);\n\tstrlcpy(psp->psp_name, \"control proxy\", sizeof(psp->psp_name));\n\tpid = ps_startprocess(psp, ps_ctl_recvmsg, ps_ctl_dodispatch,\n\t    ps_ctl_startcb, PSF_DROPPRIVS);\n\n\tif (pid == -1)\n\t\treturn -1;\n\telse if (pid != 0) {\n\t\tpsp->psp_work_fd = work_fd[0];\n\t\tclose(work_fd[1]);\n\t\tclose(listen_fd[1]);\n\t\tctx->ps_control = control_new(ctx, listen_fd[0],\n\t\t    FD_SENDLEN | FD_LISTEN);\n\t\tif (ctx->ps_control == NULL)\n\t\t\treturn -1;\n\t\treturn pid;\n\t}\n\n\tclose(work_fd[0]);\n\tclose(listen_fd[0]);\n\n\tpsp->psp_work_fd = work_fd[1];\n\tif (eloop_event_add(ctx->eloop, psp->psp_work_fd, ELE_READ, ps_ctl_recv,\n\t\tctx) == -1)\n\t\treturn -1;\n\n\tctx->ps_control = control_new(ctx, listen_fd[1], 0);\n\tif (ctx->ps_control == NULL)\n\t\treturn -1;\n\tif (eloop_event_add(ctx->eloop, ctx->ps_control->fd, ELE_READ,\n\t\tps_ctl_listen, ctx) == -1)\n\t\treturn -1;\n\n\tps_entersandbox(\"stdio inet\", NULL);\n\treturn 0;\n}\n\nint\nps_ctl_stop(struct dhcpcd_ctx *ctx)\n{\n\treturn ps_stopprocess(ctx->ps_ctl);\n}\n\nssize_t\nps_ctl_sendargs(struct fd_list *fd, void *data, size_t len)\n{\n\tstruct dhcpcd_ctx *ctx = fd->ctx;\n\n\tif (ctx->ps_control_client != NULL && ctx->ps_control_client != fd)\n\t\tlogerrx(\"%s: cannot deal with another client\", __func__);\n\tctx->ps_control_client = fd;\n\treturn ps_sendcmd(ctx, ctx->ps_ctl->psp_fd, PS_CTL,\n\t    fd->flags & FD_UNPRIV ? PS_CTL_UNPRIV : PS_CTL_PRIV, data, len);\n}\n\nssize_t\nps_ctl_sendeof(struct fd_list *fd)\n{\n\tstruct dhcpcd_ctx *ctx = fd->ctx;\n\n\treturn ps_sendcmd(ctx, ctx->ps_ctl->psp_fd, PS_CTL_EOF, 0, NULL, 0);\n}\n"
  },
  {
    "path": "src/privsep-control.h",
    "content": "/*\n * Privilege Separation for dhcpcd\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef PRIVSEP_CTL_H\n#define PRIVSEP_CTL_H\n\n#define IN_PRIVSEP_CONTROLLER(ctx) \\\n\t(IN_PRIVSEP((ctx)) && (ctx)->ps_control_pid == getpid())\n\npid_t ps_ctl_start(struct dhcpcd_ctx *);\nint ps_ctl_stop(struct dhcpcd_ctx *);\nssize_t ps_ctl_handleargs(struct fd_list *, char *, size_t);\nssize_t ps_ctl_sendargs(struct fd_list *, void *, size_t);\nssize_t ps_ctl_sendeof(struct fd_list *fd);\n\n#endif\n"
  },
  {
    "path": "src/privsep-inet.c",
    "content": "/*\n * Privilege Separation for dhcpcd, network proxy\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/types.h>\n#include <sys/socket.h>\n\n#include <netinet/in.h>\n#include <netinet/icmp6.h>\n\n#include <assert.h>\n#include <errno.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include \"arp.h\"\n#include \"bpf.h\"\n#include \"dhcp.h\"\n#include \"dhcp6.h\"\n#include \"eloop.h\"\n#include \"ipv6nd.h\"\n#include \"logerr.h\"\n#include \"privsep.h\"\n\n/* We expect to have open 2 SOCK_STREAM, 1 udp, 1 udp6 and 1 raw6 fds */\n\n#ifdef INET\nstatic void\nps_inet_recvbootp(void *arg, unsigned short events)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\n\tif (ps_recvmsg(ctx->udp_rfd, events, PS_BOOTP, ctx->ps_inet->psp_fd) ==\n\t    -1)\n\t\tlogerr(__func__);\n}\n#endif\n\n#ifdef INET6\nstatic void\nps_inet_recvra(void *arg, unsigned short events)\n{\n#ifdef __sun\n\tstruct interface *ifp = arg;\n\tstruct rs_state *state = RS_STATE(ifp);\n\tstruct dhcpcd_ctx *ctx = ifp->ctx;\n\n\tif (ps_recvmsg(state->nd_fd, events, PS_ND, ctx->ps_inet->psp_fd) == -1)\n\t\tlogerr(__func__);\n#else\n\tstruct dhcpcd_ctx *ctx = arg;\n\n\tif (ps_recvmsg(ctx->nd_fd, events, PS_ND, ctx->ps_inet->psp_fd) == -1)\n\t\tlogerr(__func__);\n#endif\n}\n#endif\n\n#ifdef DHCP6\nstatic void\nps_inet_recvdhcp6(void *arg, unsigned short events)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\n\tif (ps_recvmsg(ctx->dhcp6_rfd, events, PS_DHCP6,\n\t\tctx->ps_inet->psp_fd) == -1)\n\t\tlogerr(__func__);\n}\n#endif\n\nbool\nps_inet_canstart(const struct dhcpcd_ctx *ctx)\n{\n#ifdef INET\n\tif ((ctx->options & (DHCPCD_IPV4 | DHCPCD_MANAGER)) ==\n\t    (DHCPCD_IPV4 | DHCPCD_MANAGER))\n\t\treturn true;\n#endif\n#if defined(INET6) && !defined(__sun)\n\tif (ctx->options & DHCPCD_IPV6)\n\t\treturn true;\n#endif\n#ifdef DHCP6\n\tif ((ctx->options & (DHCPCD_IPV6 | DHCPCD_MANAGER)) ==\n\t    (DHCPCD_IPV6 | DHCPCD_MANAGER))\n\t\treturn true;\n#endif\n\n\treturn false;\n}\n\nstatic int\nps_inet_startcb(struct ps_process *psp)\n{\n\tstruct dhcpcd_ctx *ctx = psp->psp_ctx;\n\tint ret = 0;\n\n\tif (ctx->options & DHCPCD_MANAGER)\n\t\tsetproctitle(\"[network proxy]\");\n\telse\n\t\tsetproctitle(\"[network proxy] %s%s%s\",\n\t\t    ctx->ifc != 0 ? ctx->ifv[0] : \"\",\n\t\t    ctx->options & DHCPCD_IPV4 ? \" [ip4]\" : \"\",\n\t\t    ctx->options & DHCPCD_IPV6 ? \" [ip6]\" : \"\");\n\n\t/* This end is the main engine, so it's useless for us. */\n\tclose(ctx->ps_data_fd);\n\tctx->ps_data_fd = -1;\n\n\terrno = 0;\n\n#ifdef INET\n\tif ((ctx->options & (DHCPCD_IPV4 | DHCPCD_MANAGER)) ==\n\t    (DHCPCD_IPV4 | DHCPCD_MANAGER)) {\n\t\tctx->udp_rfd = dhcp_openudp(NULL);\n\t\tif (ctx->udp_rfd == -1)\n\t\t\tlogerr(\"%s: dhcp_open\", __func__);\n#ifdef PRIVSEP_RIGHTS\n\t\telse if (ps_rights_limit_fd_rdonly(ctx->udp_rfd) == -1) {\n\t\t\tlogerr(\"%s: ps_rights_limit_fd_rdonly\", __func__);\n\t\t\tclose(ctx->udp_rfd);\n\t\t\tctx->udp_rfd = -1;\n\t\t}\n#endif\n\t\telse if (eloop_event_add(ctx->eloop, ctx->udp_rfd, ELE_READ,\n\t\t\t     ps_inet_recvbootp, ctx) == -1) {\n\t\t\tlogerr(\"%s: eloop_event_add DHCP\", __func__);\n\t\t\tclose(ctx->udp_rfd);\n\t\t\tctx->udp_rfd = -1;\n\t\t} else\n\t\t\tret++;\n\t}\n#endif\n#if defined(INET6) && !defined(__sun)\n\tif (ctx->options & DHCPCD_IPV6) {\n\t\tctx->nd_fd = ipv6nd_open(true);\n\t\tif (ctx->nd_fd == -1)\n\t\t\tlogerr(\"%s: ipv6nd_open\", __func__);\n#ifdef PRIVSEP_RIGHTS\n\t\telse if (ps_rights_limit_fd_rdonly(ctx->nd_fd) == -1) {\n\t\t\tlogerr(\"%s: ps_rights_limit_fd_rdonly\", __func__);\n\t\t\tclose(ctx->nd_fd);\n\t\t\tctx->nd_fd = -1;\n\t\t}\n#endif\n\t\telse if (eloop_event_add(ctx->eloop, ctx->nd_fd, ELE_READ,\n\t\t\t     ps_inet_recvra, ctx) == -1) {\n\t\t\tlogerr(\"%s: eloop_event_add RA\", __func__);\n\t\t\tclose(ctx->nd_fd);\n\t\t\tctx->nd_fd = -1;\n\t\t} else\n\t\t\tret++;\n\t}\n#endif\n#ifdef DHCP6\n\tif ((ctx->options & (DHCPCD_IPV6 | DHCPCD_MANAGER)) ==\n\t    (DHCPCD_IPV6 | DHCPCD_MANAGER)) {\n\t\tctx->dhcp6_rfd = dhcp6_openudp(0, NULL);\n\t\tif (ctx->dhcp6_rfd == -1)\n\t\t\tlogerr(\"%s: dhcp6_open\", __func__);\n#ifdef PRIVSEP_RIGHTS\n\t\telse if (ps_rights_limit_fd_rdonly(ctx->dhcp6_rfd) == -1) {\n\t\t\tlogerr(\"%s: ps_rights_limit_fd_rdonly\", __func__);\n\t\t\tclose(ctx->dhcp6_rfd);\n\t\t\tctx->dhcp6_rfd = -1;\n\t\t}\n#endif\n\t\telse if (eloop_event_add(ctx->eloop, ctx->dhcp6_rfd, ELE_READ,\n\t\t\t     ps_inet_recvdhcp6, ctx) == -1) {\n\t\t\tlogerr(\"%s: eloop_event_add DHCP6\", __func__);\n\t\t\tclose(ctx->dhcp6_rfd);\n\t\t\tctx->dhcp6_rfd = -1;\n\t\t} else\n\t\t\tret++;\n\t}\n#endif\n\n\tif (ret == 0 && errno == 0) {\n\t\terrno = ENXIO;\n\t\treturn -1;\n\t}\n\treturn ret;\n}\n\n#if defined(INET) || defined(DHCP6)\nstatic bool\nps_inet_validudp(struct msghdr *msg, uint16_t sport, uint16_t dport)\n{\n\tstruct udphdr udp;\n\tstruct iovec *iov = msg->msg_iov;\n\n\tif (msg->msg_iovlen == 0 || iov->iov_len < sizeof(udp)) {\n\t\terrno = EINVAL;\n\t\treturn false;\n\t}\n\n\tmemcpy(&udp, iov->iov_base, sizeof(udp));\n\tif (udp.uh_sport != htons(sport) || udp.uh_dport != htons(dport)) {\n\t\terrno = EPERM;\n\t\treturn false;\n\t}\n\treturn true;\n}\n#endif\n\n#ifdef INET6\nstatic bool\nps_inet_validnd(struct msghdr *msg)\n{\n\tstruct icmp6_hdr icmp6;\n\tstruct iovec *iov = msg->msg_iov;\n\n\tif (msg->msg_iovlen == 0 || iov->iov_len < sizeof(icmp6)) {\n\t\terrno = EINVAL;\n\t\treturn false;\n\t}\n\n\tmemcpy(&icmp6, iov->iov_base, sizeof(icmp6));\n\tswitch (icmp6.icmp6_type) {\n\tcase ND_ROUTER_SOLICIT:\n\tcase ND_NEIGHBOR_ADVERT:\n\t\tbreak;\n\tdefault:\n\t\terrno = EPERM;\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n#endif\n\nstatic ssize_t\nps_inet_sendmsg(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm,\n    struct msghdr *msg)\n{\n\tstruct ps_process *psp;\n\tint s;\n\n\tpsp = ps_findprocess(ctx, &psm->ps_id);\n\tif (psp != NULL) {\n\t\ts = psp->psp_work_fd;\n\t\tgoto dosend;\n\t}\n\n\tswitch (psm->ps_cmd) {\n#ifdef INET\n\tcase PS_BOOTP:\n\t\tif (!ps_inet_validudp(msg, BOOTPC, BOOTPS))\n\t\t\treturn -1;\n\t\ts = ctx->udp_wfd;\n\t\tbreak;\n#endif\n#if defined(INET6) && !defined(__sun)\n\tcase PS_ND:\n\t\tif (!ps_inet_validnd(msg))\n\t\t\treturn -1;\n\t\ts = ctx->nd_fd;\n\t\tbreak;\n#endif\n#ifdef DHCP6\n\tcase PS_DHCP6:\n\t\tif (!ps_inet_validudp(msg, DHCP6_CLIENT_PORT,\n\t\t\tDHCP6_SERVER_PORT))\n\t\t\treturn -1;\n\t\ts = ctx->dhcp6_wfd;\n\t\tbreak;\n#endif\n\tdefault:\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\ndosend:\n\treturn sendmsg(s, msg, 0);\n}\n\nstatic void\nps_inet_recvmsg(void *arg, unsigned short events)\n{\n\tstruct ps_process *psp = arg;\n\n\t/* Receive shutdown */\n\tif (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, NULL, NULL) == -1)\n\t\tlogerr(__func__);\n}\n\nssize_t\nps_inet_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\n\tswitch (psm->ps_cmd) {\n#ifdef INET\n\tcase PS_BOOTP:\n\t\tdhcp_recvmsg(ctx, msg);\n\t\tbreak;\n#endif\n#ifdef INET6\n\tcase PS_ND:\n\t\tipv6nd_recvmsg(ctx, msg);\n\t\tbreak;\n#endif\n#ifdef DHCP6\n\tcase PS_DHCP6:\n\t\tdhcp6_recvmsg(ctx, msg, NULL);\n\t\tbreak;\n#endif\n\tdefault:\n\t\terrno = ENOTSUP;\n\t\treturn -1;\n\t}\n\treturn 1;\n}\n\nstatic void\nps_inet_dodispatch(void *arg, unsigned short events)\n{\n\tstruct ps_process *psp = arg;\n\n\tif (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, ps_inet_dispatch,\n\t\tpsp->psp_ctx) == -1)\n\t\tlogerr(__func__);\n}\n\npid_t\nps_inet_start(struct dhcpcd_ctx *ctx)\n{\n\tstruct ps_id id = {\n\t\t.psi_ifindex = 0,\n\t\t.psi_cmd = PS_INET,\n\t};\n\tstruct ps_process *psp;\n\tpid_t pid;\n\n\tpsp = ctx->ps_inet = ps_newprocess(ctx, &id);\n\tif (psp == NULL)\n\t\treturn -1;\n\n\tstrlcpy(psp->psp_name, \"network proxy\", sizeof(psp->psp_name));\n\tpid = ps_startprocess(psp, ps_inet_recvmsg, ps_inet_dodispatch,\n\t    ps_inet_startcb, PSF_DROPPRIVS);\n\n\tif (pid == 0)\n\t\tps_entersandbox(\"stdio\", NULL);\n\n\treturn pid;\n}\n\nint\nps_inet_stop(struct dhcpcd_ctx *ctx)\n{\n\treturn ps_stopprocess(ctx->ps_inet);\n}\n\n#ifdef INET\nstatic void\nps_inet_recvinbootp(void *arg, unsigned short events)\n{\n\tstruct ps_process *psp = arg;\n\n\tif (ps_recvmsg(psp->psp_work_fd, events, PS_BOOTP,\n\t\tpsp->psp_ctx->ps_data_fd) == -1)\n\t\tlogerr(__func__);\n}\n\nstatic int\nps_inet_listenin(struct ps_process *psp)\n{\n\tstruct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr;\n\tchar buf[INET_ADDRSTRLEN];\n\n\tinet_ntop(AF_INET, ia, buf, sizeof(buf));\n\tsetproctitle(\"[%s proxy] %s\", psp->psp_protostr, buf);\n\n\tpsp->psp_work_fd = dhcp_openudp(ia);\n\tif (psp->psp_work_fd == -1) {\n\t\tlogerr(__func__);\n\t\treturn -1;\n\t}\n\n#ifdef PRIVSEP_RIGHTS\n\tif (ps_rights_limit_fd_rdonly(psp->psp_work_fd) == -1) {\n\t\tlogerr(\"%s: ps_rights_limit_fd_rdonly\", __func__);\n\t\treturn -1;\n\t}\n#endif\n\n\tif (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd, ELE_READ,\n\t\tps_inet_recvinbootp, psp) == -1) {\n\t\tlogerr(\"%s: eloop_event_add DHCP\", __func__);\n\t\treturn -1;\n\t}\n\treturn 0;\n}\n#endif\n\n#if defined(INET6) && defined(__sun)\nstatic void\nps_inet_recvin6nd(void *arg)\n{\n\tstruct ps_process *psp = arg;\n\n\tif (ps_recvmsg(psp->psp_work_fd, PS_ND, psp->psp_ctx->ps_data_fd) == -1)\n\t\tlogerr(__func__);\n}\n\nstatic int\nps_inet_listennd(struct ps_process *psp)\n{\n\tsetproctitle(\"[ND network proxy]\");\n\n\tpsp->psp_work_fd = ipv6nd_open(&psp->psp_ifp);\n\tif (psp->psp_work_fd == -1) {\n\t\tlogerr(__func__);\n\t\treturn -1;\n\t}\n\n#ifdef PRIVSEP_RIGHTS\n\tif (ps_rights_limit_fd_rdonly(psp->psp_work_fd) == -1) {\n\t\tlogerr(\"%s: ps_rights_limit_fd_rdonly\", __func__);\n\t\treturn -1;\n\t}\n#endif\n\n\tif (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd,\n\t\tps_inet_recvin6nd, psp) == -1) {\n\t\tlogerr(__func__);\n\t\treturn -1;\n\t}\n\treturn 0;\n}\n#endif\n\n#ifdef DHCP6\nstatic void\nps_inet_recvin6dhcp6(void *arg, unsigned short events)\n{\n\tstruct ps_process *psp = arg;\n\n\tif (ps_recvmsg(psp->psp_work_fd, events, PS_DHCP6,\n\t\tpsp->psp_ctx->ps_data_fd) == -1)\n\t\tlogerr(__func__);\n}\n\nstatic int\nps_inet_listenin6(struct ps_process *psp)\n{\n\tstruct in6_addr *ia = &psp->psp_id.psi_addr.psa_in6_addr;\n\tchar buf[INET6_ADDRSTRLEN];\n\n\tinet_ntop(AF_INET6, ia, buf, sizeof(buf));\n\tsetproctitle(\"[%s proxy] %s\", psp->psp_protostr, buf);\n\n\tpsp->psp_work_fd = dhcp6_openudp(psp->psp_id.psi_ifindex, ia);\n\tif (psp->psp_work_fd == -1) {\n\t\tlogerr(__func__);\n\t\treturn -1;\n\t}\n\n#ifdef PRIVSEP_RIGHTS\n\tif (ps_rights_limit_fd_rdonly(psp->psp_work_fd) == -1) {\n\t\tlogerr(\"%s: ps_rights_limit_fd_rdonly\", __func__);\n\t\treturn -1;\n\t}\n#endif\n\n\tif (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd, ELE_READ,\n\t\tps_inet_recvin6dhcp6, psp) == -1) {\n\t\tlogerr(\"%s: eloop_event_add DHCP\", __func__);\n\t\treturn -1;\n\t}\n\treturn 0;\n}\n#endif\n\nstatic void\nps_inet_recvmsgpsp(void *arg, unsigned short events)\n{\n\tstruct ps_process *psp = arg;\n\n\t/* Receive shutdown. */\n\tif (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, NULL, NULL) == -1)\n\t\tlogerr(__func__);\n}\n\nssize_t\nps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)\n{\n\tuint16_t cmd;\n\tstruct ps_process *psp;\n\tint (*start_func)(struct ps_process *);\n\tpid_t start;\n\tstruct ps_addr *psa = &psm->ps_id.psi_addr;\n\tvoid *ia;\n\tchar buf[INET_MAX_ADDRSTRLEN];\n\n\tcmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));\n\tif (cmd == psm->ps_cmd)\n\t\treturn ps_inet_sendmsg(ctx, psm, msg);\n\n\tpsp = ps_findprocess(ctx, &psm->ps_id);\n\n#ifdef PRIVSEP_DEBUG\n\tlogerrx(\"%s: IN cmd %x, psp %p\", __func__, psm->ps_cmd, psp);\n#endif\n\n\tif (psm->ps_cmd & PS_STOP) {\n\t\tassert(psp == NULL);\n\t\treturn 0;\n\t}\n\n\tif (!(psm->ps_cmd & PS_START)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tif (psp != NULL)\n\t\treturn 1;\n\n\tpsp = ps_newprocess(ctx, &psm->ps_id);\n\tif (psp == NULL)\n\t\treturn -1;\n\n\tswitch (cmd) {\n#ifdef INET\n\tcase PS_BOOTP:\n\t\tstart_func = ps_inet_listenin;\n\t\tpsp->psp_protostr = \"BOOTP\";\n\t\tia = &psa->psa_in_addr;\n\t\tbreak;\n#endif\n#ifdef INET6\n#ifdef __sun\n\tcase PS_ND:\n\t\tstart_func = ps_inet_listennd;\n\t\tpsp->psp_protostr = \"ND\";\n\t\tia = &psa->psa_in6_addr;\n\t\tbreak;\n#endif\n#ifdef DHCP6\n\tcase PS_DHCP6:\n\t\tstart_func = ps_inet_listenin6;\n\t\tpsp->psp_protostr = \"DHCP6\";\n\t\tia = &psa->psa_in6_addr;\n\t\tbreak;\n#endif\n#endif\n\tdefault:\n\t\tlogerrx(\"%s: unknown command %x\", __func__, psm->ps_cmd);\n\t\terrno = ENOTSUP;\n\t\treturn -1;\n\t}\n\n\tsnprintf(psp->psp_name, sizeof(psp->psp_name), \"%s proxy %s\",\n\t    psp->psp_protostr,\n\t    inet_ntop(psa->psa_family, ia, buf, sizeof(buf)));\n\tstart = ps_startprocess(psp, ps_inet_recvmsgpsp, NULL, start_func,\n\t    PSF_DROPPRIVS);\n\tswitch (start) {\n\tcase -1:\n\t\tps_freeprocess(psp);\n\t\treturn -1;\n\tcase 0:\n\t\tps_entersandbox(\"stdio\", NULL);\n\t\tbreak;\n\tdefault:\n\t\tlogdebugx(\"%s: spawned %s on PID %d\", psp->psp_ifname,\n\t\t    psp->psp_name, psp->psp_pid);\n\t\tbreak;\n\t}\n\treturn start;\n}\n\n#ifdef INET\nstatic ssize_t\nps_inet_in_docmd(struct ipv4_addr *ia, uint16_t cmd, const struct msghdr *msg)\n{\n\tassert(ia != NULL);\n\tstruct dhcpcd_ctx *ctx = ia->iface->ctx;\n\tstruct ps_msghdr psm = {\n\t\t.ps_cmd = cmd,\n\t\t.ps_id = {\n\t\t\t.psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),\n\t\t\t.psi_ifindex = ia->iface->index,\n\t\t\t.psi_addr.psa_family = AF_INET,\n\t\t\t.psi_addr.psa_in_addr = ia->addr,\n\t\t},\n\t};\n\n\treturn ps_sendpsmmsg(ctx, PS_ROOT_FD(ctx), &psm, msg);\n}\n\nssize_t\nps_inet_openbootp(struct ipv4_addr *ia)\n{\n\treturn ps_inet_in_docmd(ia, PS_START | PS_BOOTP, NULL);\n}\n\nssize_t\nps_inet_closebootp(struct ipv4_addr *ia)\n{\n\treturn ps_inet_in_docmd(ia, PS_STOP | PS_BOOTP, NULL);\n}\n\nssize_t\nps_inet_sendbootp(struct interface *ifp, const struct msghdr *msg)\n{\n\tstruct dhcpcd_ctx *ctx = ifp->ctx;\n\n\treturn ps_sendmsg(ctx, PS_ROOT_FD(ctx), PS_BOOTP, 0, msg);\n}\n#endif /* INET */\n\n#ifdef INET6\n#ifdef __sun\nstatic ssize_t\nps_inet_ifp_docmd(struct interface *ifp, uint16_t cmd, const struct msghdr *msg)\n{\n\tstruct dhcpcd_ctx *ctx = ifp->ctx;\n\tstruct ps_msghdr psm = {\n\t\t.ps_cmd = cmd,\n\t\t.ps_id = {\n\t\t\t.psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),\n\t\t\t.psi_ifindex = ifp->index,\n\t\t\t.psi_addr.psa_family = AF_INET6,\n\t\t},\n\t};\n\n\treturn ps_sendpsmmsg(ctx, PS_ROOT_FD(ctx), &psm, msg);\n}\n\nssize_t\nps_inet_opennd(struct interface *ifp)\n{\n\treturn ps_inet_ifp_docmd(ifp, PS_ND | PS_START, NULL);\n}\n\nssize_t\nps_inet_closend(struct interface *ifp)\n{\n\treturn ps_inet_ifp_docmd(ifp, PS_ND | PS_STOP, NULL);\n}\n\nssize_t\nps_inet_sendnd(struct interface *ifp, const struct msghdr *msg)\n{\n\treturn ps_inet_ifp_docmd(ifp, PS_ND, msg);\n}\n#else\nssize_t\nps_inet_sendnd(struct interface *ifp, const struct msghdr *msg)\n{\n\tstruct dhcpcd_ctx *ctx = ifp->ctx;\n\n\treturn ps_sendmsg(ctx, PS_ROOT_FD(ctx), PS_ND, 0, msg);\n}\n#endif\n\n#ifdef DHCP6\nstatic ssize_t\nps_inet_in6_docmd(struct ipv6_addr *ia, uint16_t cmd, const struct msghdr *msg)\n{\n\tstruct dhcpcd_ctx *ctx = ia->iface->ctx;\n\tstruct ps_msghdr psm = {\n\t\t.ps_cmd = cmd,\n\t\t.ps_id = {\n\t\t\t.psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),\n\t\t\t.psi_ifindex = ia->iface->index,\n\t\t\t.psi_addr.psa_family = AF_INET6,\n\t\t\t.psi_addr.psa_in6_addr = ia->addr,\n\t\t},\n\t};\n\n\treturn ps_sendpsmmsg(ctx, PS_ROOT_FD(ctx), &psm, msg);\n}\n\nssize_t\nps_inet_opendhcp6(struct ipv6_addr *ia)\n{\n\treturn ps_inet_in6_docmd(ia, PS_DHCP6 | PS_START, NULL);\n}\n\nssize_t\nps_inet_closedhcp6(struct ipv6_addr *ia)\n{\n\treturn ps_inet_in6_docmd(ia, PS_DHCP6 | PS_STOP, NULL);\n}\n\nssize_t\nps_inet_senddhcp6(struct interface *ifp, const struct msghdr *msg)\n{\n\tstruct dhcpcd_ctx *ctx = ifp->ctx;\n\n\treturn ps_sendmsg(ctx, PS_ROOT_FD(ctx), PS_DHCP6, 0, msg);\n}\n#endif /* DHCP6 */\n#endif /* INET6 */\n"
  },
  {
    "path": "src/privsep-inet.h",
    "content": "/*\n * Privilege Separation for dhcpcd\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef PRIVSEP_INET_H\n#define PRIVSEP_INET_H\n\nbool ps_inet_canstart(const struct dhcpcd_ctx *);\npid_t ps_inet_start(struct dhcpcd_ctx *);\nint ps_inet_stop(struct dhcpcd_ctx *);\nssize_t ps_inet_cmd(struct dhcpcd_ctx *, struct ps_msghdr *, struct msghdr *);\nssize_t ps_inet_dispatch(void *, struct ps_msghdr *, struct msghdr *);\n\n#ifdef INET\nstruct ipv4_addr;\nssize_t ps_inet_openbootp(struct ipv4_addr *);\nssize_t ps_inet_closebootp(struct ipv4_addr *);\nssize_t ps_inet_sendbootp(struct interface *, const struct msghdr *);\n#endif\n\n#ifdef INET6\nstruct ipv6_addr;\n#ifdef __sun\nssize_t ps_inet_opennd(struct interface *);\nssize_t ps_inet_closend(struct interface *);\n#endif\nssize_t ps_inet_sendnd(struct interface *, const struct msghdr *);\n#ifdef DHCP6\nssize_t ps_inet_opendhcp6(struct ipv6_addr *);\nssize_t ps_inet_closedhcp6(struct ipv6_addr *);\nssize_t ps_inet_senddhcp6(struct interface *, const struct msghdr *);\n#endif /* DHCP6 */\n#endif /* INET6 */\n#endif\n"
  },
  {
    "path": "src/privsep-linux.c",
    "content": "/*\n * Privilege Separation for dhcpcd, Linux driver\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/ioctl.h>\n#include <sys/prctl.h>\n#include <sys/socket.h>\n#include <sys/syscall.h>\n\n#include <errno.h>\n#include <fcntl.h>\n#include <linux/audit.h>\n#include <linux/filter.h>\n#include <linux/net.h>\n#include <linux/seccomp.h>\n#include <linux/sockios.h>\n#include <signal.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <termios.h> /* For TCGETS */\n#include <unistd.h>\n\n#include \"common.h\"\n#include \"if.h\"\n#include \"logerr.h\"\n#include \"privsep.h\"\n\n/*\n * Set this to debug SECCOMP.\n * Then run dhcpcd with strace -f and strace will even translate\n * the failing syscall into the __NR_name define we need to use below.\n * DO NOT ENABLE THIS FOR PRODUCTION BUILDS!\n */\n// #define SECCOMP_FILTER_DEBUG\n\nstatic ssize_t\nps_root_dosendnetlink(struct dhcpcd_ctx *ctx, int protocol, struct msghdr *msg)\n{\n\tstruct priv *priv = (struct priv *)ctx->priv;\n\tint s;\n\tunsigned char buf[16 * 1024];\n\tstruct iovec riov = {\n\t\t.iov_base = buf,\n\t\t.iov_len = sizeof(buf),\n\t};\n\n\tswitch (protocol) {\n\tcase NETLINK_GENERIC:\n\t\ts = priv->generic_fd;\n\t\tbreak;\n\tcase NETLINK_ROUTE:\n\t\ts = priv->route_fd;\n\t\tbreak;\n\tdefault:\n\t\terrno = EPFNOSUPPORT;\n\t\treturn -1;\n\t}\n\n\tif (sendmsg(s, msg, 0) == -1)\n\t\treturn -1;\n\n\treturn if_getnetlink(NULL, &riov, s, 0, NULL, NULL);\n}\n\nssize_t\nps_root_os(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg,\n    __unused void **rdata, __unused size_t *rlen, __unused bool *free_data)\n{\n\tswitch (psm->ps_cmd) {\n\tcase PS_ROUTE:\n\t\treturn ps_root_dosendnetlink(ctx, (int)psm->ps_flags, msg);\n\tdefault:\n\t\terrno = ENOTSUP;\n\t\treturn -1;\n\t}\n}\n\nssize_t\nps_root_sendnetlink(struct dhcpcd_ctx *ctx, int protocol, struct msghdr *msg)\n{\n\tif (ps_sendmsg(ctx, PS_ROOT_FD(ctx), PS_ROUTE, (unsigned long)protocol,\n\t\tmsg) == -1)\n\t\treturn -1;\n\treturn ps_root_readerror(ctx, NULL, 0);\n}\n\n#ifdef DISABLE_SECCOMP\n#warning SECCOMP has been disabled\n#else\n\n#if (BYTE_ORDER == LITTLE_ENDIAN)\n#define SECCOMP_ARG_LO 0\n#define SECCOMP_ARG_HI sizeof(uint32_t)\n#elif (BYTE_ORDER == BIG_ENDIAN)\n#define SECCOMP_ARG_LO sizeof(uint32_t)\n#define SECCOMP_ARG_HI 0\n#else\n#error \"Uknown endian\"\n#endif\n\n#define SECCOMP_ALLOW(_nr)                                \\\n\tBPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (_nr), 0, 1), \\\n\t    BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW)\n\n#define SECCOMP_ALLOW_ARG(_nr, _arg, _val)                                     \\\n\tBPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (_nr), 0, 6),                      \\\n\t    BPF_STMT(BPF_LD + BPF_W + BPF_ABS,                                 \\\n\t\toffsetof(struct seccomp_data, args[(_arg)]) + SECCOMP_ARG_LO), \\\n\t    BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ((_val) & 0xffffffff), 0, 3),  \\\n\t    BPF_STMT(BPF_LD + BPF_W + BPF_ABS,                                 \\\n\t\toffsetof(struct seccomp_data, args[(_arg)]) + SECCOMP_ARG_HI), \\\n\t    BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K,                                \\\n\t\t(((uint32_t)((uint64_t)(_val) >> 32)) & 0xffffffff), 0, 1),    \\\n\t    BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),                      \\\n\t    BPF_STMT(BPF_LD + BPF_W + BPF_ABS,                                 \\\n\t\toffsetof(struct seccomp_data, nr))\n\n#ifdef SECCOMP_FILTER_DEBUG\n#define SECCOMP_FILTER_FAIL SECCOMP_RET_TRAP\n#else\n#define SECCOMP_FILTER_FAIL SECCOMP_RET_KILL\n#endif\n\n/* I personally find this quite nutty.\n * Why can a system header not define a default for this? */\n#if defined(__i386__)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_I386\n#elif defined(__x86_64__)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_X86_64\n#elif defined(__arc__)\n#if defined(__A7__)\n#if (BYTE_ORDER == LITTLE_ENDIAN)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARCOMPACT\n#else\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARCOMPACTBE\n#endif\n#elif defined(__HS__)\n#if (BYTE_ORDER == LITTLE_ENDIAN)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARCV2\n#else\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARCV2BE\n#endif\n#else\n#error \"Platform does not support seccomp filter yet\"\n#endif\n#elif defined(__ARCV3__)\n#if defined(__ARC64__)\n#if (BYTE_ORDER == LITTLE_ENDIAN)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARCV3\n#else\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARCV3BE\n#endif\n#else\n#error \"Platform does not support seccomp filter yet\"\n#endif\n#elif defined(__arm__)\n#ifndef EM_ARM\n#define EM_ARM 40\n#endif\n#if (BYTE_ORDER == LITTLE_ENDIAN)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARM\n#else\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ARMEB\n#endif\n#elif defined(__aarch64__)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_AARCH64\n#elif defined(__alpha__)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_ALPHA\n#elif defined(__hppa__)\n#if defined(__LP64__)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_PARISC64\n#else\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_PARISC\n#endif\n#elif defined(__ia64__)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_IA64\n#elif defined(__loongarch__)\n#if defined(__LP64__)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_LOONGARCH64\n#else\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_LOONGARCH32\n#endif\n#elif defined(__microblaze__)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MICROBLAZE\n#elif defined(__m68k__)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_M68K\n#elif defined(__mips__)\n#if defined(__MIPSEL__)\n#if defined(__LP64__)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPSEL64\n#else\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPSEL\n#endif\n#elif defined(__LP64__)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPS64\n#else\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_MIPS\n#endif\n#elif defined(__nds32__)\n#if (BYTE_ORDER == LITTLE_ENDIAN)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_NDS32\n#else\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_NDS32BE\n#endif\n#elif defined(__nios2__)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_NIOS2\n#elif defined(__or1k__)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_OPENRISC\n#elif defined(__powerpc64__)\n#if (BYTE_ORDER == LITTLE_ENDIAN)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_PPC64LE\n#else\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_PPC64\n#endif\n#elif defined(__powerpc__)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_PPC\n#elif defined(__riscv)\n#if defined(__LP64__)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_RISCV64\n#else\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_RISCV32\n#endif\n#elif defined(__s390x__)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_S390X\n#elif defined(__s390__)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_S390\n#elif defined(__sh__)\n#if defined(__LP64__)\n#if (BYTE_ORDER == LITTLE_ENDIAN)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_SHEL64\n#else\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_SH64\n#endif\n#else\n#if (BYTE_ORDER == LITTLE_ENDIAN)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_SHEL\n#else\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_SH\n#endif\n#endif\n#elif defined(__sparc__)\n#if defined(__arch64__)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_SPARC64\n#else\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_SPARC\n#endif\n#elif defined(__xtensa__)\n#define SECCOMP_AUDIT_ARCH AUDIT_ARCH_XTENSA\n#else\n#error \"Platform does not support seccomp filter yet\"\n#endif\n\nstatic struct sock_filter ps_seccomp_filter[] = {\n\t/* Check syscall arch */\n\tBPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct seccomp_data, arch)),\n\tBPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, SECCOMP_AUDIT_ARCH, 1, 0),\n\tBPF_STMT(BPF_RET + BPF_K, SECCOMP_FILTER_FAIL),\n\t/* Allow syscalls */\n\tBPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct seccomp_data, nr)),\n#ifdef __NR_accept\n\tSECCOMP_ALLOW(__NR_accept),\n#endif\n#ifdef __NR_brk\n\tSECCOMP_ALLOW(__NR_brk),\n#endif\n#ifdef __NR_clock_gettime\n\tSECCOMP_ALLOW(__NR_clock_gettime),\n#endif\n#if defined(__x86_64__) && defined(__ILP32__) && defined(__X32_SYSCALL_BIT)\n\tSECCOMP_ALLOW(__NR_clock_gettime & ~__X32_SYSCALL_BIT),\n#endif\n#ifdef __NR_clock_gettime32\n\tSECCOMP_ALLOW(__NR_clock_gettime32),\n#endif\n#ifdef __NR_clock_gettime64\n\tSECCOMP_ALLOW(__NR_clock_gettime64),\n#endif\n#ifdef __NR_close\n\tSECCOMP_ALLOW(__NR_close),\n#endif\n#ifdef __NR_dup2\n\tSECCOMP_ALLOW(__NR_dup2), // daemonising dups stderr to stdin(/dev/null)\n#endif\n#ifdef __NR_dup3\n\tSECCOMP_ALLOW(__NR_dup3),\n#endif\n#ifdef __NR_epoll_ctl\n\tSECCOMP_ALLOW(__NR_epoll_ctl),\n#endif\n#ifdef __NR_epoll_wait\n\tSECCOMP_ALLOW(__NR_epoll_wait),\n#endif\n#ifdef __NR_epoll_pwait\n\tSECCOMP_ALLOW(__NR_epoll_pwait),\n#endif\n#ifdef __NR_epoll_pwait2\n\tSECCOMP_ALLOW(__NR_epoll_pwait2),\n#endif\n#ifdef __NR_exit_group\n\tSECCOMP_ALLOW(__NR_exit_group),\n#endif\n#ifdef __NR_fcntl\n\tSECCOMP_ALLOW(__NR_fcntl),\n#endif\n#ifdef __NR_fcntl64\n\tSECCOMP_ALLOW(__NR_fcntl64),\n#endif\n#ifdef __NR_fstat\n\tSECCOMP_ALLOW(__NR_fstat),\n#endif\n#ifdef __NR_fstat64\n\tSECCOMP_ALLOW(__NR_fstat64),\n#endif\n#ifdef __NR_gettimeofday\n\tSECCOMP_ALLOW(__NR_gettimeofday),\n#endif\n#ifdef __NR_getpid\n\tSECCOMP_ALLOW(__NR_getpid),\n#endif\n#ifdef __NR_getrandom\n\tSECCOMP_ALLOW(__NR_getrandom),\n#endif\n#ifdef __NR_getsockopt\n\t/* For route socket overflow */\n\tSECCOMP_ALLOW_ARG(__NR_getsockopt, 1, SOL_SOCKET),\n\tSECCOMP_ALLOW_ARG(__NR_getsockopt, 2, SO_RCVBUF),\n#endif\n#ifdef __NR_ioctl\n\tSECCOMP_ALLOW_ARG(__NR_ioctl, 1, SIOCGIFFLAGS),\n\tSECCOMP_ALLOW_ARG(__NR_ioctl, 1, SIOCGIFHWADDR),\n\tSECCOMP_ALLOW_ARG(__NR_ioctl, 1, SIOCGIFINDEX),\n\tSECCOMP_ALLOW_ARG(__NR_ioctl, 1, SIOCGIFMTU),\n\tSECCOMP_ALLOW_ARG(__NR_ioctl, 1, SIOCGIFVLAN),\n\t/* printf over serial terminal requires this */\n\tSECCOMP_ALLOW_ARG(__NR_ioctl, 1, TCGETS),\n\t/* dumping leases on musl requires this */\n\tSECCOMP_ALLOW_ARG(__NR_ioctl, 1, TIOCGWINSZ),\n/* SECCOMP BPF is newer than nl80211 so we don't need SIOCGIWESSID\n * which lives in the impossible to include linux/wireless.h header */\n#endif\n#ifdef __NR_madvise /* needed for musl */\n\tSECCOMP_ALLOW(__NR_madvise),\n#endif\n#ifdef __NR_mmap\n\tSECCOMP_ALLOW(__NR_mmap),\n#endif\n#ifdef __NR_mmap2\n\tSECCOMP_ALLOW(__NR_mmap2),\n#endif\n#ifdef __NR_munmap\n\tSECCOMP_ALLOW(__NR_munmap),\n#endif\n#ifdef __NR_newfstatat\n\tSECCOMP_ALLOW(__NR_newfstatat),\n#endif\n#ifdef __NR_ppoll\n\tSECCOMP_ALLOW(__NR_ppoll),\n#endif\n#ifdef __NR_ppoll_time64\n\tSECCOMP_ALLOW(__NR_ppoll_time64),\n#endif\n#ifdef __NR_pselect6\n\tSECCOMP_ALLOW(__NR_pselect6),\n#endif\n#ifdef __NR_pselect6_time64\n\tSECCOMP_ALLOW(__NR_pselect6_time64),\n#endif\n#ifdef __NR_read\n\tSECCOMP_ALLOW(__NR_read),\n#endif\n#ifdef __NR_readv\n\tSECCOMP_ALLOW(__NR_readv),\n#endif\n#ifdef __NR_recv\n\tSECCOMP_ALLOW(__NR_recv),\n#endif\n#ifdef __NR_recvfrom\n\tSECCOMP_ALLOW(__NR_recvfrom),\n#endif\n#ifdef __NR_recvmsg\n\tSECCOMP_ALLOW(__NR_recvmsg),\n#endif\n#ifdef __NR_rt_sigprocmask\n\tSECCOMP_ALLOW(__NR_rt_sigprocmask),\n#endif\n#ifdef __NR_rt_sigreturn\n\tSECCOMP_ALLOW(__NR_rt_sigreturn),\n#endif\n#ifdef __NR_send\n\tSECCOMP_ALLOW(__NR_send),\n#endif\n#ifdef __NR_sendmsg\n\tSECCOMP_ALLOW(__NR_sendmsg),\n#endif\n#ifdef __NR_sendto\n\tSECCOMP_ALLOW(__NR_sendto),\n#endif\n#ifdef __NR_socketcall\n\t/* i386 needs this and demonstrates why SECCOMP\n\t * is poor compared to OpenBSD pledge(2) and FreeBSD capsicum(4)\n\t * as this is soooo tied to the kernel API which changes per arch\n\t * and likely libc as well. */\n\tSECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_ACCEPT),\n\tSECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_ACCEPT4),\n\tSECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_LISTEN),\n\tSECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_GETSOCKOPT), /* overflow */\n\tSECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_RECV),\n\tSECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_RECVFROM),\n\tSECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_RECVMSG),\n\tSECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_SEND),\n\tSECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_SENDMSG),\n\tSECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_SENDTO),\n\tSECCOMP_ALLOW_ARG(__NR_socketcall, 0, SYS_SHUTDOWN),\n#endif\n#ifdef __NR_shutdown\n\tSECCOMP_ALLOW(__NR_shutdown),\n#endif\n#ifdef __NR_statx\n\tSECCOMP_ALLOW(__NR_statx),\n#endif\n#ifdef __NR_time\n\tSECCOMP_ALLOW(__NR_time),\n#endif\n#ifdef __NR_wait4\n\tSECCOMP_ALLOW(__NR_wait4),\n#endif\n#ifdef __NR_waitpid\n\tSECCOMP_ALLOW(__NR_waitpid),\n#endif\n#ifdef __NR_write\n\tSECCOMP_ALLOW(__NR_write),\n#endif\n#ifdef __NR_writev\n\tSECCOMP_ALLOW(__NR_writev),\n#endif\n#ifdef __NR_uname\n\tSECCOMP_ALLOW(__NR_uname),\n#endif\n\n/* These are for compiling with address sanitization */\n#ifdef ASAN\n#ifdef __NR_openat\n\tSECCOMP_ALLOW(__NR_openat),\n#endif\n#ifdef __NR_readlink\n\tSECCOMP_ALLOW(__NR_readlink),\n#endif\n#ifdef __NR_sigaltstack\n\tSECCOMP_ALLOW(__NR_sigaltstack),\n#endif\n\n/* coredumps */\n#ifdef __NR_tgkill\n\tSECCOMP_ALLOW(__NR_tgkill),\n#endif\n#endif\n\n/* valgrind */\n#ifdef __NR_futex\n\tSECCOMP_ALLOW(__NR_futex),\n#endif\n#ifdef __NR_gettid\n\tSECCOMP_ALLOW(__NR_gettid),\n#endif\n#ifdef __NR_rt_sigtimedwait\n\tSECCOMP_ALLOW(__NR_rt_sigtimedwait),\n#endif\n#ifdef VALGRIND\n#ifdef __NR_unlink\n\t/* This is dangerous, and also pointless as in privsep\n\t * we are no longer root and thus cannot unlink the valgrind\n\t * pipes anyway. */\n\tSECCOMP_ALLOW(__NR_unlink),\n#endif\n#endif\n\n/* hardened-malloc */\n#ifdef __NR_mprotect\n\tSECCOMP_ALLOW(__NR_mprotect),\n#endif\n#ifdef __NR_mremap\n\tSECCOMP_ALLOW(__NR_mremap),\n#endif\n#ifdef __NR_pkey_alloc\n\tSECCOMP_ALLOW(__NR_pkey_alloc),\n#endif\n#ifdef __NR_pkey_mprotect\n\tSECCOMP_ALLOW(__NR_pkey_mprotect),\n#endif\n\n\t/* Deny everything else */\n\tBPF_STMT(BPF_RET + BPF_K, SECCOMP_FILTER_FAIL),\n};\n\nstatic struct sock_fprog ps_seccomp_prog = {\n\t.len = (unsigned short)__arraycount(ps_seccomp_filter),\n\t.filter = ps_seccomp_filter,\n};\n\n#ifdef SECCOMP_FILTER_DEBUG\nstatic void\nps_seccomp_violation(__unused int signum, siginfo_t *si, __unused void *context)\n{\n\tlogerrx(\"%s: unexpected syscall %d (arch=0x%x)\", __func__,\n\t    si->si_syscall, si->si_arch);\n\t_exit(EXIT_FAILURE);\n}\n\nstatic int\nps_seccomp_debug(void)\n{\n\tstruct sigaction sa = {\n\t\t.sa_flags = SA_SIGINFO,\n\t\t.sa_sigaction = &ps_seccomp_violation,\n\t};\n\tsigset_t mask;\n\n\t/* Install a signal handler to catch any issues with our filter. */\n\tsigemptyset(&mask);\n\tsigaddset(&mask, SIGSYS);\n\tif (sigaction(SIGSYS, &sa, NULL) == -1 ||\n\t    sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)\n\t\treturn -1;\n\n\treturn 0;\n}\n#endif\n\nint\nps_seccomp_enter(void)\n{\n\n#ifdef SECCOMP_FILTER_DEBUG\n\tps_seccomp_debug();\n#endif\n\n\tif (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1 ||\n\t    prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &ps_seccomp_prog) ==\n\t\t-1) {\n\t\tif (errno == EINVAL)\n\t\t\terrno = ENOSYS;\n\t\treturn -1;\n\t}\n\treturn 0;\n}\n#endif /* !DISABLE_SECCOMP */\n"
  },
  {
    "path": "src/privsep-root.c",
    "content": "/*\n * Privilege Separation for dhcpcd, privileged proxy\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/types.h>\n#include <sys/ioctl.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <sys/time.h>\n#include <sys/wait.h>\n\n#include <assert.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <poll.h>\n#include <pwd.h>\n#include <signal.h>\n#include <stddef.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include \"auth.h\"\n#include \"common.h\"\n#include \"dev.h\"\n#include \"dhcp6.h\"\n#include \"dhcpcd.h\"\n#include \"eloop.h\"\n#include \"if.h\"\n#include \"ipv6nd.h\"\n#include \"logerr.h\"\n#include \"privsep.h\"\n#include \"sa.h\"\n#include \"script.h\"\n\n__CTASSERT(sizeof(ioctl_request_t) <= sizeof(unsigned long));\n\nstruct psr_error {\n\tssize_t psr_result;\n\tint psr_errno;\n\tchar psr_pad[sizeof(ssize_t) - sizeof(int)];\n\tsize_t psr_datalen;\n};\n\nstruct psr_ctx {\n\tstruct dhcpcd_ctx *psr_ctx;\n\tstruct psr_error psr_error;\n\tsize_t psr_datalen;\n\tvoid *psr_data;\n\tbool psr_mallocdata;\n};\n\nstatic ssize_t\nps_root_readerrorcb(struct psr_ctx *pc)\n{\n\tstruct dhcpcd_ctx *ctx = pc->psr_ctx;\n\tint fd = PS_ROOT_FD(ctx);\n\tstruct psr_error *psr_error = &pc->psr_error;\n\tssize_t len;\n\n#define PSR_ERROR(e)                        \\\n\tdo {                                \\\n\t\tpsr_error->psr_errno = (e); \\\n\t\tgoto error;                 \\\n\t} while (0 /* CONSTCOND */)\n\n\tif (eloop_waitfd(fd) == -1)\n\t\tPSR_ERROR(errno);\n\n\tlen = recv(fd, psr_error, sizeof(*psr_error), MSG_WAITALL);\n\tif (len == -1)\n\t\tPSR_ERROR(errno);\n\n\tif ((size_t)len < sizeof(*psr_error))\n\t\tPSR_ERROR(EINVAL);\n\n\tif (psr_error->psr_datalen == 0)\n\t\treturn len;\n\n\tif (pc->psr_mallocdata) {\n\t\tpc->psr_data = malloc(psr_error->psr_datalen);\n\t\tif (pc->psr_data == NULL)\n\t\t\tPSR_ERROR(errno);\n\t} else if (psr_error->psr_datalen > pc->psr_datalen)\n\t\tPSR_ERROR(EMSGSIZE);\n\n\tlen = recv(fd, pc->psr_data, psr_error->psr_datalen, MSG_WAITALL);\n\tif (len == -1)\n\t\tPSR_ERROR(errno);\n\telse if ((size_t)len != psr_error->psr_datalen) {\n#ifdef PRIVSEP_DEBUG\n\t\tlogerrx(\"%s: recvmsg returned %zd, expecting %zu\", __func__,\n\t\t    len, sizeof(*psr_error) + psr_error->psr_datalen);\n#endif\n\t\tPSR_ERROR(EBADMSG);\n\t}\n\treturn len;\n\nerror:\n\tpsr_error->psr_result = -1;\n\tif (pc->psr_mallocdata && pc->psr_data != NULL) {\n\t\tfree(pc->psr_data);\n\t\tpc->psr_data = NULL;\n\t}\n\treturn -1;\n}\n\nssize_t\nps_root_readerror(struct dhcpcd_ctx *ctx, void *data, size_t len)\n{\n\tstruct psr_ctx pc = { .psr_ctx = ctx,\n\t\t.psr_data = data,\n\t\t.psr_datalen = len,\n\t\t.psr_mallocdata = false };\n\n\tps_root_readerrorcb(&pc);\n\n\terrno = pc.psr_error.psr_errno;\n\treturn pc.psr_error.psr_result;\n}\n\nssize_t\nps_root_mreaderror(struct dhcpcd_ctx *ctx, void **data, size_t *len)\n{\n\tstruct psr_ctx pc = { .psr_ctx = ctx,\n\t\t.psr_data = NULL,\n\t\t.psr_datalen = 0,\n\t\t.psr_mallocdata = true };\n\n\tps_root_readerrorcb(&pc);\n\n\terrno = pc.psr_error.psr_errno;\n\t*data = pc.psr_data;\n\t*len = pc.psr_error.psr_datalen;\n\treturn pc.psr_error.psr_result;\n}\n\nstatic ssize_t\nps_root_writeerror(struct dhcpcd_ctx *ctx, ssize_t result, void *data,\n    size_t len)\n{\n\tstruct psr_error psr = {\n\t\t.psr_result = result,\n\t\t.psr_errno = errno,\n\t\t.psr_datalen = len,\n\t};\n\tstruct iovec iov[] = {\n\t\t{ .iov_base = &psr, .iov_len = sizeof(psr) },\n\t\t{ .iov_base = data, .iov_len = len },\n\t};\n\tstruct msghdr msg = { .msg_iov = iov, .msg_iovlen = __arraycount(iov) };\n\tssize_t err;\n\tint fd = PS_ROOT_FD(ctx);\n\n#ifdef PRIVSEP_DEBUG\n\tlogdebugx(\"%s: result %zd errno %d\", __func__, result, errno);\n#endif\n\n\tif (len == 0)\n\t\tmsg.msg_iovlen = 1;\n\terr = sendmsg(fd, &msg, 0);\n\n\t/* Error sending the message? Try sending the error of sending. */\n\tif (err == -1 && errno != EPIPE) {\n\t\tlogerr(\"%s: result=%zd, data=%p, len=%zu\", __func__, result,\n\t\t    data, len);\n\t\tpsr.psr_result = err;\n\t\tpsr.psr_errno = errno;\n\t\tpsr.psr_datalen = 0;\n\t\tmsg.msg_iovlen = 1;\n\t\terr = sendmsg(fd, &msg, 0);\n\t}\n\n\treturn err;\n}\n\nstatic ssize_t\nps_root_doioctl(unsigned long req, void *data, size_t len)\n{\n\tint s, err;\n\n\t/* Only allow these ioctls */\n\tswitch (req) {\n#ifdef SIOCAIFADDR\n\tcase SIOCAIFADDR: /* FALLTHROUGH */\n\tcase SIOCDIFADDR: /* FALLTHROUGH */\n#endif\n#ifdef SIOCSIFHWADDR\n\tcase SIOCSIFHWADDR: /* FALLTHROUGH */\n#endif\n#ifdef SIOCGIFPRIORITY\n\tcase SIOCGIFPRIORITY: /* FALLTHROUGH */\n#endif\n\tcase SIOCSIFFLAGS: /* FALLTHROUGH */\n\tcase SIOCGIFMTU:\n\t\tbreak;\n\tdefault:\n\t\terrno = EPERM;\n\t\treturn -1;\n\t}\n\n\ts = xsocket(PF_INET, SOCK_DGRAM, 0);\n\tif (s != -1)\n#ifdef IOCTL_REQUEST_TYPE\n\t{\n\t\tioctl_request_t reqt;\n\n\t\tmemcpy(&reqt, &req, sizeof(reqt));\n\t\terr = ioctl(s, reqt, data, len);\n\t}\n#else\n\t\terr = ioctl(s, req, data, len);\n#endif\n\telse\n\t\terr = -1;\n\tif (s != -1)\n\t\tclose(s);\n\treturn err;\n}\n\nstatic ssize_t\nps_root_run_script(struct dhcpcd_ctx *ctx, const void *data, size_t len)\n{\n\tconst char *envbuf = data;\n\tchar *const argv[] = { ctx->script, NULL };\n\tpid_t pid;\n\tint status;\n\n\tif (len == 0)\n\t\treturn 0;\n\n\tif (script_buftoenv(ctx, UNCONST(envbuf), len) == NULL)\n\t\treturn -1;\n\n\tpid = script_exec(argv, ctx->script_env);\n\tif (pid == -1)\n\t\treturn -1;\n\n\t/* Wait for the script to finish */\n\twhile (waitpid(pid, &status, 0) == -1) {\n\t\tif (errno != EINTR) {\n\t\t\tlogerr(__func__);\n\t\t\tstatus = 0;\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn status;\n}\n\nstatic bool\nps_root_validpath(const struct dhcpcd_ctx *ctx, uint16_t cmd, const char *path)\n{\n\t/* Avoid a previous directory attack to avoid /proc/../\n\t * dhcpcd should never use a path with double dots. */\n\tif (strstr(path, \"..\") != NULL)\n\t\treturn false;\n\n\tif (cmd == PS_READFILE) {\n#ifdef EMBEDDED_CONFIG\n\t\tif (strcmp(ctx->cffile, EMBEDDED_CONFIG) == 0)\n\t\t\treturn true;\n#endif\n\t\tif (strcmp(ctx->cffile, path) == 0)\n\t\t\treturn true;\n\t}\n\tif (strncmp(DBDIR, path, strlen(DBDIR)) == 0)\n\t\treturn true;\n\tif (strncmp(RUNDIR, path, strlen(RUNDIR)) == 0)\n\t\treturn true;\n\n#ifdef __linux__\n\tif (strncmp(\"/proc/net/\", path, strlen(\"/proc/net/\")) == 0 ||\n\t    strncmp(\"/proc/sys/net/\", path, strlen(\"/proc/sys/net/\")) == 0 ||\n\t    strncmp(\"/sys/class/net/\", path, strlen(\"/sys/class/net/\")) == 0)\n\t\treturn true;\n#endif\n\n\terrno = EPERM;\n\treturn false;\n}\n\nstatic ssize_t\nps_root_dowritefile(const struct dhcpcd_ctx *ctx, mode_t mode, void *data,\n    size_t len)\n{\n\tchar *file = data, *nc;\n\n\tnc = memchr(file, '\\0', len);\n\tif (nc == NULL) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tif (!ps_root_validpath(ctx, PS_WRITEFILE, file))\n\t\treturn -1;\n\tnc++;\n\treturn writefile(file, mode, nc, len - (size_t)(nc - file));\n}\n\n#ifdef AUTH\nstatic ssize_t\nps_root_monordm(uint64_t *rdm, size_t len)\n{\n\tif (len != sizeof(*rdm)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\treturn auth_get_rdm_monotonic(rdm);\n}\n#endif\n\n#ifdef PRIVSEP_GETIFADDRS\n#define IFA_NADDRS 4\nstatic ssize_t\nps_root_dogetifaddrs(void **rdata, size_t *rlen)\n{\n\tstruct ifaddrs *ifaddrs, *ifa;\n\tsize_t len;\n\tuint8_t *buf, *sap;\n\tsocklen_t salen;\n\n\tif (getifaddrs(&ifaddrs) == -1)\n\t\treturn -1;\n\tif (ifaddrs == NULL) {\n\t\t*rdata = NULL;\n\t\t*rlen = 0;\n\t\treturn 0;\n\t}\n\n\t/* Work out the buffer length required.\n\t * Ensure everything is aligned correctly, which does\n\t * create a larger buffer than what is needed to send,\n\t * but makes creating the same structure in the client\n\t * much easier. */\n\tlen = 0;\n\tfor (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {\n\t\tlen += ALIGN(sizeof(*ifa));\n\t\tlen += ALIGN(IFNAMSIZ);\n\t\tlen += ALIGN(sizeof(salen) * IFA_NADDRS);\n\t\tif (ifa->ifa_addr != NULL)\n\t\t\tlen += ALIGN(sa_len(ifa->ifa_addr));\n\t\tif (ifa->ifa_netmask != NULL)\n\t\t\tlen += ALIGN(sa_len(ifa->ifa_netmask));\n\t\tif (ifa->ifa_broadaddr != NULL)\n\t\t\tlen += ALIGN(sa_len(ifa->ifa_broadaddr));\n#ifdef BSD\n\t\t/*\n\t\t * On BSD we need to carry ifa_data so we can access\n\t\t * if_data->ifi_link_state\n\t\t */\n\t\tif (ifa->ifa_addr != NULL &&\n\t\t    ifa->ifa_addr->sa_family == AF_LINK)\n\t\t\tlen += ALIGN(sizeof(struct if_data));\n#endif\n\t}\n\n\t/* Use calloc to set everything to zero.\n\t * This satisfies memory sanitizers because don't write\n\t * where we don't need to. */\n\tbuf = calloc(1, len);\n\tif (buf == NULL) {\n\t\tfreeifaddrs(ifaddrs);\n\t\treturn -1;\n\t}\n\t*rdata = buf;\n\t*rlen = len;\n\n\tfor (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {\n\t\tmemcpy(buf, ifa, sizeof(*ifa));\n\t\tbuf += ALIGN(sizeof(*ifa));\n\n\t\tstrlcpy((char *)buf, ifa->ifa_name, IFNAMSIZ);\n\t\tbuf += ALIGN(IFNAMSIZ);\n\t\tsap = buf;\n\t\tbuf += ALIGN(sizeof(salen) * IFA_NADDRS);\n\n#define COPYINSA(addr)                                      \\\n\tdo {                                                \\\n\t\tif ((addr) != NULL)                         \\\n\t\t\tsalen = sa_len((addr));             \\\n\t\telse                                        \\\n\t\t\tsalen = 0;                          \\\n\t\tif (salen != 0) {                           \\\n\t\t\tmemcpy(sap, &salen, sizeof(salen)); \\\n\t\t\tmemcpy(buf, (addr), salen);         \\\n\t\t\tbuf += ALIGN(salen);                \\\n\t\t}                                           \\\n\t\tsap += sizeof(salen);                       \\\n\t} while (0 /*CONSTCOND */)\n\n\t\tCOPYINSA(ifa->ifa_addr);\n\t\tCOPYINSA(ifa->ifa_netmask);\n\t\tCOPYINSA(ifa->ifa_broadaddr);\n\n#ifdef BSD\n\t\tif (ifa->ifa_addr != NULL &&\n\t\t    ifa->ifa_addr->sa_family == AF_LINK) {\n\t\t\tsalen = (socklen_t)sizeof(struct if_data);\n\t\t\tmemcpy(buf, ifa->ifa_data, salen);\n\t\t\tbuf += ALIGN(salen);\n\t\t} else\n#endif\n\t\t\tsalen = 0;\n\t\tmemcpy(sap, &salen, sizeof(salen));\n\t}\n\n\tfreeifaddrs(ifaddrs);\n\treturn 0;\n}\n#endif\n\nstatic ssize_t\nps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\tuint16_t cmd;\n\tstruct ps_process *psp;\n\tstruct iovec *iov = msg->msg_iov;\n\tvoid *data = iov->iov_base, *rdata = NULL;\n\tsize_t len = iov->iov_len, rlen = 0;\n\tuint8_t buf[PS_BUFLEN];\n\ttime_t mtime;\n\tssize_t err;\n\tbool free_rdata = false;\n\n\tcmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));\n\tpsp = ps_findprocess(ctx, &psm->ps_id);\n\n#ifdef PRIVSEP_DEBUG\n\tlogerrx(\"%s: IN cmd %x, psp %p\", __func__, psm->ps_cmd, psp);\n#endif\n\n\tif (psp != NULL) {\n\t\tif (psm->ps_cmd & PS_STOP) {\n\t\t\treturn ps_stopprocess(psp);\n\t\t} else if (psm->ps_cmd & PS_START) {\n\t\t\t/* Process has already started .... */\n\t\t\tlogdebugx(\"%s%sprocess %s already started on pid %d\",\n\t\t\t    psp->psp_ifname,\n\t\t\t    psp->psp_ifname[0] != '\\0' ? \": \" : \"\",\n\t\t\t    psp->psp_name, psp->psp_pid);\n\t\t\treturn 0;\n\t\t}\n\n\t\terr = ps_sendpsmmsg(ctx, psp->psp_fd, psm, msg);\n\t\tif (err == -1) {\n\t\t\tlogerr(\"%s: failed to send message to pid %d\", __func__,\n\t\t\t    psp->psp_pid);\n\t\t\tps_freeprocess(psp);\n\t\t}\n\t\treturn 0;\n\t}\n\n\tif (psm->ps_cmd & PS_STOP && psp == NULL)\n\t\treturn 0;\n\n\tswitch (cmd) {\n#ifdef INET\n#ifdef ARP\n\tcase PS_BPF_ARP: /* FALLTHROUGH */\n#endif\n\tcase PS_BPF_BOOTP:\n\t\treturn ps_bpf_cmd(ctx, psm, msg);\n#endif\n#ifdef INET\n\tcase PS_BOOTP:\n\t\treturn ps_inet_cmd(ctx, psm, msg);\n#endif\n#ifdef INET6\n#ifdef DHCP6\n\tcase PS_DHCP6: /* FALLTHROUGH */\n#endif\n\tcase PS_ND:\n\t\treturn ps_inet_cmd(ctx, psm, msg);\n#endif\n\tdefault:\n\t\tbreak;\n\t}\n\n\tassert(msg->msg_iovlen == 0 || msg->msg_iovlen == 1);\n\n\t/* Reset errno */\n\terrno = 0;\n\n\tswitch (psm->ps_cmd) {\n\tcase PS_IOCTL:\n\t\terr = ps_root_doioctl(psm->ps_flags, data, len);\n\t\tif (err != -1) {\n\t\t\trdata = data;\n\t\t\trlen = len;\n\t\t}\n\t\tbreak;\n\tcase PS_SCRIPT:\n\t\terr = ps_root_run_script(ctx, data, len);\n\t\tbreak;\n\tcase PS_STOPPROCS:\n\t\tctx->options |= DHCPCD_EXITING;\n\t\tTAILQ_FOREACH(psp, &ctx->ps_processes, next) {\n\t\t\tif (psp != ctx->ps_root)\n\t\t\t\tps_stopprocess(psp);\n\t\t}\n\t\terr = ps_stopwait(ctx);\n\t\tbreak;\n\tcase PS_UNLINK:\n\t\tif (!ps_root_validpath(ctx, psm->ps_cmd, data)) {\n\t\t\terr = -1;\n\t\t\tbreak;\n\t\t}\n\t\terr = unlink(data);\n\t\tbreak;\n\tcase PS_READFILE:\n\t\tif (!ps_root_validpath(ctx, psm->ps_cmd, data)) {\n\t\t\terr = -1;\n\t\t\tbreak;\n\t\t}\n\t\terr = readfile(data, buf, sizeof(buf));\n\t\tif (err != -1) {\n\t\t\trdata = buf;\n\t\t\trlen = (size_t)err;\n\t\t}\n\t\tbreak;\n\tcase PS_WRITEFILE:\n\t\terr = ps_root_dowritefile(ctx, (mode_t)psm->ps_flags, data,\n\t\t    len);\n\t\tbreak;\n\tcase PS_FILEMTIME:\n\t\terr = filemtime(data, &mtime);\n\t\tif (err != -1) {\n\t\t\trdata = &mtime;\n\t\t\trlen = sizeof(mtime);\n\t\t}\n\t\tbreak;\n\tcase PS_LOGREOPEN:\n\t\terr = logopen(ctx->logfile);\n\t\tbreak;\n#ifdef AUTH\n\tcase PS_AUTH_MONORDM:\n\t\terr = ps_root_monordm(data, len);\n\t\tif (err != -1) {\n\t\t\trdata = data;\n\t\t\trlen = len;\n\t\t}\n\t\tbreak;\n#endif\n#ifdef PRIVSEP_GETIFADDRS\n\tcase PS_GETIFADDRS:\n\t\terr = ps_root_dogetifaddrs(&rdata, &rlen);\n\t\tfree_rdata = true;\n\t\tbreak;\n#endif\n#ifdef PLUGIN_DEV\n\tcase PS_DEV_INITTED:\n\t\terr = dev_initialised(ctx, data);\n\t\tbreak;\n\tcase PS_DEV_LISTENING:\n\t\terr = dev_listening(ctx);\n\t\tbreak;\n#endif\n\tdefault:\n\t\terr = ps_root_os(ctx, psm, msg, &rdata, &rlen, &free_rdata);\n\t\tbreak;\n\t}\n\n\terr = ps_root_writeerror(ctx, err, rdata, rlen);\n\tif (free_rdata)\n\t\tfree(rdata);\n\treturn err;\n}\n\n/* Receive from state engine, do an action. */\nstatic void\nps_root_recvmsg(void *arg, unsigned short events)\n{\n\tstruct ps_process *psp = arg;\n\n\tif (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, ps_root_recvmsgcb,\n\t\tpsp->psp_ctx) == -1)\n\t\tlogerr(__func__);\n}\n\n#ifdef PLUGIN_DEV\nstatic int\nps_root_handleinterface(void *arg, int action, const char *ifname)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\tunsigned long flag;\n\n\tif (action == 1)\n\t\tflag = PS_DEV_IFADDED;\n\telse if (action == -1)\n\t\tflag = PS_DEV_IFREMOVED;\n\telse if (action == 0)\n\t\tflag = PS_DEV_IFUPDATED;\n\telse {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\treturn (int)ps_sendcmd(ctx, ctx->ps_data_fd, PS_DEV_IFCMD, flag, ifname,\n\t    strlen(ifname) + 1);\n}\n#endif\n\nstatic int\nps_root_startcb(struct ps_process *psp)\n{\n\tstruct dhcpcd_ctx *ctx = psp->psp_ctx;\n\n\tif (ctx->options & DHCPCD_MANAGER)\n\t\tsetproctitle(\"[privileged proxy]\");\n\telse\n\t\tsetproctitle(\"[privileged proxy] %s%s%s\", ctx->ifv[0],\n\t\t    ctx->options & DHCPCD_IPV4 ? \" [ip4]\" : \"\",\n\t\t    ctx->options & DHCPCD_IPV6 ? \" [ip6]\" : \"\");\n\tctx->options |= DHCPCD_PRIVSEPROOT;\n\n\tif (if_opensockets(ctx) == -1)\n\t\tlogerr(\"%s: if_opensockets\", __func__);\n\n\t/* Open network sockets for sending.\n\t * This is a small bit wasteful for non sandboxed OS's\n\t * but makes life very easy for unicasting DHCPv6 in non manager\n\t * mode as we no longer care about address selection.\n\t * We can't call shutdown SHUT_RD on the socket because it's\n\t * not connected. All we can do is try and set a zero sized\n\t * receive buffer and just let it overflow.\n\t * Reading from it just to drain it is a waste of CPU time. */\n#ifdef INET\n\tif (ctx->options & DHCPCD_IPV4) {\n\t\tint buflen = 1;\n\n\t\tctx->udp_wfd = xsocket(PF_INET, SOCK_RAW | SOCK_CXNB,\n\t\t    IPPROTO_UDP);\n\t\tif (ctx->udp_wfd == -1)\n\t\t\tlogerr(\"%s: dhcp_openraw\", __func__);\n\t\telse if (setsockopt(ctx->udp_wfd, SOL_SOCKET, SO_RCVBUF,\n\t\t\t     &buflen, sizeof(buflen)) == -1)\n\t\t\tlogerr(\"%s: setsockopt SO_RCVBUF DHCP\", __func__);\n\t}\n#endif\n#ifdef INET6\n\tif (ctx->options & DHCPCD_IPV6) {\n\t\tint buflen = 1;\n\n\t\tctx->nd_fd = ipv6nd_open(false);\n\t\tif (ctx->nd_fd == -1)\n\t\t\tlogerr(\"%s: ipv6nd_open\", __func__);\n\t\telse if (setsockopt(ctx->nd_fd, SOL_SOCKET, SO_RCVBUF, &buflen,\n\t\t\t     sizeof(buflen)) == -1)\n\t\t\tlogerr(\"%s: setsockopt SO_RCVBUF ND\", __func__);\n\t}\n#endif\n#ifdef DHCP6\n\tif (ctx->options & DHCPCD_IPV6) {\n\t\tint buflen = 1;\n\n\t\tctx->dhcp6_wfd = dhcp6_openraw();\n\t\tif (ctx->dhcp6_wfd == -1)\n\t\t\tlogerr(\"%s: dhcp6_openraw\", __func__);\n\t\telse if (setsockopt(ctx->dhcp6_wfd, SOL_SOCKET, SO_RCVBUF,\n\t\t\t     &buflen, sizeof(buflen)) == -1)\n\t\t\tlogerr(\"%s: setsockopt SO_RCVBUF DHCP6\", __func__);\n\t}\n#endif\n\n#ifdef PLUGIN_DEV\n\t/* Start any dev listening plugin which may want to\n\t * change the interface name provided by the kernel */\n\tif ((ctx->options & (DHCPCD_MANAGER | DHCPCD_DEV)) ==\n\t    (DHCPCD_MANAGER | DHCPCD_DEV))\n\t\tdev_start(ctx, ps_root_handleinterface);\n#endif\n\n\treturn 0;\n}\n\nvoid\nps_root_signalcb(int sig, void *arg)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\tint status;\n\tpid_t pid;\n\tconst char *ifname, *name;\n\tstruct ps_process *psp;\n\n\tif (sig != SIGCHLD)\n\t\treturn;\n\n\twhile ((pid = waitpid(-1, &status, WNOHANG)) > 0) {\n\t\tpsp = ps_findprocesspid(ctx, pid);\n\t\tif (psp != NULL) {\n\t\t\tifname = psp->psp_ifname;\n\t\t\tname = psp->psp_name;\n\t\t} else {\n\t\t\t/* Ignore logging the double fork */\n\t\t\tif (ctx->options & DHCPCD_LAUNCHER)\n\t\t\t\tcontinue;\n\t\t\tifname = \"\";\n\t\t\tname = \"unknown process\";\n\t\t}\n\n\t\tif (WIFEXITED(status) && WEXITSTATUS(status) != 0)\n\t\t\tlogerrx(\"%s%s%s exited unexpectedly from PID %d,\"\n\t\t\t\t\" code=%d\",\n\t\t\t    ifname, ifname[0] != '\\0' ? \": \" : \"\", name, pid,\n\t\t\t    WEXITSTATUS(status));\n\t\telse if (WIFSIGNALED(status))\n\t\t\tlogerrx(\"%s%s%s exited unexpectedly from PID %d,\"\n\t\t\t\t\" signal=%s\",\n\t\t\t    ifname, ifname[0] != '\\0' ? \": \" : \"\", name, pid,\n\t\t\t    strsignal(WTERMSIG(status)));\n\t\telse\n\t\t\tlogdebugx(\"%s%s%s exited from PID %d\", ifname,\n\t\t\t    ifname[0] != '\\0' ? \": \" : \"\", name, pid);\n\n\t\tif (psp != NULL)\n\t\t\tps_freeprocess(psp);\n\t}\n\n\tif (!(ctx->options & DHCPCD_EXITING))\n\t\treturn;\n\tif (!(ps_waitforprocs(ctx)))\n\t\teloop_exit(ctx->eloop, EXIT_SUCCESS);\n}\n\nint (*handle_interface)(void *, int, const char *);\n\n#ifdef PLUGIN_DEV\nstatic ssize_t\nps_root_devcb(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)\n{\n\tint action;\n\tstruct iovec *iov = msg->msg_iov;\n\n\tif (msg->msg_iovlen != 1) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tswitch (psm->ps_flags) {\n\tcase PS_DEV_IFADDED:\n\t\taction = 1;\n\t\tbreak;\n\tcase PS_DEV_IFREMOVED:\n\t\taction = -1;\n\t\tbreak;\n\tcase PS_DEV_IFUPDATED:\n\t\taction = 0;\n\t\tbreak;\n\tdefault:\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\treturn dhcpcd_handleinterface(ctx, action, iov->iov_base);\n}\n#endif\n\nstatic ssize_t\nps_root_dispatchcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\tssize_t err;\n\n\tswitch (psm->ps_cmd) {\n#ifdef PLUGIN_DEV\n\tcase PS_DEV_IFCMD:\n\t\terr = ps_root_devcb(ctx, psm, msg);\n\t\tbreak;\n#endif\n\tdefault:\n#ifdef INET\n\t\terr = ps_bpf_dispatch(ctx, psm, msg);\n\t\tif (err == -1 && errno == ENOTSUP)\n#endif\n\t\t\terr = ps_inet_dispatch(ctx, psm, msg);\n\t}\n\treturn err;\n}\n\nstatic void\nps_root_dispatch(void *arg, unsigned short events)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\n\tif (ps_recvpsmsg(ctx, ctx->ps_data_fd, events, ps_root_dispatchcb,\n\t\tctx) == -1)\n\t\tlogerr(__func__);\n}\n\nstatic void\nps_root_log(void *arg, unsigned short events)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\n\tif (events != ELE_READ)\n\t\tlogerrx(\"%s: unexpected event 0x%04x\", __func__, events);\n\n\tif (logreadfd(ctx->ps_log_root_fd) == -1)\n\t\tlogerr(__func__);\n}\n\npid_t\nps_root_start(struct dhcpcd_ctx *ctx)\n{\n\tstruct ps_id id = {\n\t\t.psi_ifindex = 0,\n\t\t.psi_cmd = PS_ROOT,\n\t};\n\tstruct ps_process *psp;\n\tint logfd[2] = { -1, -1 }, datafd[2] = { -1, -1 };\n\tpid_t pid;\n\n\tif (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, logfd) == -1)\n\t\treturn -1;\n#ifdef PRIVSEP_RIGHTS\n\tif (ps_rights_limit_fdpair(logfd) == -1)\n\t\treturn -1;\n#endif\n\n\tif (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, datafd) == -1)\n\t\treturn -1;\n\n\tif (ps_setbuf_fdpair(datafd) == -1)\n\t\treturn -1;\n#ifdef PRIVSEP_RIGHTS\n\tif (ps_rights_limit_fdpair(datafd) == -1)\n\t\treturn -1;\n#endif\n\n\tpsp = ctx->ps_root = ps_newprocess(ctx, &id);\n\tif (psp == NULL)\n\t\treturn -1;\n\n\tstrlcpy(psp->psp_name, \"privileged proxy\", sizeof(psp->psp_name));\n\tpid = ps_startprocess(psp, ps_root_recvmsg, NULL, ps_root_startcb,\n\t    PSF_ELOOP);\n\tif (pid == -1)\n\t\treturn -1;\n\n\tif (pid == 0) {\n\t\tctx->ps_log_fd = logfd[0]; /* Keep open to pass to processes */\n\t\tctx->ps_log_root_fd = logfd[1];\n\t\tif (eloop_event_add(ctx->eloop, ctx->ps_log_root_fd, ELE_READ,\n\t\t\tps_root_log, ctx) == -1)\n\t\t\treturn -1;\n\t\tctx->ps_data_fd = datafd[1];\n\t\tclose(datafd[0]);\n\t\treturn 0;\n\t} else if (pid == -1)\n\t\treturn -1;\n\n\tlogsetfd(logfd[0]);\n\tclose(logfd[1]);\n\n\tctx->ps_data_fd = datafd[0];\n\tclose(datafd[1]);\n\tif (eloop_event_add(ctx->eloop, ctx->ps_data_fd, ELE_READ,\n\t\tps_root_dispatch, ctx) == -1)\n\t\treturn -1;\n\n\treturn pid;\n}\n\nvoid\nps_root_close(struct dhcpcd_ctx *ctx)\n{\n\tif_closesockets(ctx);\n\n#ifdef INET\n\tif (ctx->udp_wfd != -1) {\n\t\tclose(ctx->udp_wfd);\n\t\tctx->udp_wfd = -1;\n\t}\n#endif\n#ifdef INET6\n\tif (ctx->nd_fd != -1) {\n\t\tclose(ctx->nd_fd);\n\t\tctx->nd_fd = -1;\n\t}\n#endif\n#ifdef DHCP6\n\tif (ctx->dhcp6_wfd != -1) {\n\t\tclose(ctx->dhcp6_wfd);\n\t\tctx->dhcp6_wfd = -1;\n\t}\n#endif\n}\n\nint\nps_root_stop(struct dhcpcd_ctx *ctx)\n{\n\tstruct ps_process *psp = ctx->ps_root;\n\tint err;\n\n\tif (!(ctx->options & DHCPCD_PRIVSEP))\n\t\treturn 0;\n\n\t/* If we are the root process then remove the pidfile */\n\tif (ctx->options & DHCPCD_PRIVSEPROOT) {\n\t\tif (!(ctx->options & DHCPCD_TEST) && unlink(ctx->pidfile) == -1)\n\t\t\tlogerr(\"%s: unlink: %s\", __func__, ctx->pidfile);\n\n\t\t/* drain the log */\n\t\tif (ctx->ps_log_root_fd != -1) {\n\t\t\tssize_t loglen;\n\t\t\tstruct pollfd pfd = {\n\t\t\t\t.fd = ctx->ps_log_root_fd,\n\t\t\t\t.events = POLLIN\n\t\t\t};\n\t\t\tint n;\n\n\t\t\t/* the socket is blocking and we may not be able to\n\t\t\t * change it to non blocking, so poll for data */\n\t\t\tfor (;;) {\n\t\t\t\tn = poll(&pfd, 1, 1);\n\t\t\t\tif (n == -1 || n == 0)\n\t\t\t\t\tbreak;\n\t\t\t\tloglen = logreadfd(ctx->ps_log_root_fd);\n\t\t\t\tif (loglen == -1 || loglen == 0)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (ctx->ps_log_root_fd != -1) {\n\t\tclose(ctx->ps_log_root_fd);\n\t\tctx->ps_log_root_fd = -1;\n\t}\n\n\tif (ctx->ps_data_fd != -1) {\n\t\teloop_event_delete(ctx->eloop, ctx->ps_data_fd);\n\t\tclose(ctx->ps_data_fd);\n\t\tctx->ps_data_fd = -1;\n\t}\n\n\t/* Only the manager process gets past this point. */\n\tif (ctx->options & DHCPCD_FORKED) {\n\t\terr = 0;\n\t\tgoto out;\n\t}\n\n\t/* We cannot log the root process exited before we\n\t * log dhcpcd exits because the latter requires the former.\n\t * So we just log the intent to exit.\n\t * Even sending this will be a race to exit. */\n\tif (psp) {\n\t\tlogdebugx(\"%s%s%s will exit from PID %d\", psp->psp_ifname,\n\t\t    psp->psp_ifname[0] != '\\0' ? \": \" : \"\", psp->psp_name,\n\t\t    psp->psp_pid);\n\n\t\tif (ps_stopprocess(psp) == -1)\n\t\t\treturn -1;\n\t} /* else the root process has already exited :( */\n\n\terr = ps_stopwait(ctx);\nout:\n\tif (ctx->ps_root != NULL)\n\t\tps_freeprocess(ctx->ps_root);\n\treturn err;\n}\n\nssize_t\nps_root_stopprocesses(struct dhcpcd_ctx *ctx)\n{\n\tif (!(IN_PRIVSEP_SE(ctx)))\n\t\treturn 0;\n\n\tif (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_STOPPROCS, 0, NULL, 0) == -1)\n\t\treturn -1;\n\treturn ps_root_readerror(ctx, NULL, 0);\n}\n\nssize_t\nps_root_script(struct dhcpcd_ctx *ctx, const void *data, size_t len)\n{\n\tif (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_SCRIPT, 0, data, len) == -1)\n\t\treturn -1;\n\treturn ps_root_readerror(ctx, NULL, 0);\n}\n\nssize_t\nps_root_ioctl(struct dhcpcd_ctx *ctx, ioctl_request_t req, void *data,\n    size_t len)\n{\n\tint fd = PS_ROOT_FD(ctx);\n#ifdef IOCTL_REQUEST_TYPE\n\tunsigned long ulreq = 0;\n\n\tmemcpy(&ulreq, &req, sizeof(req));\n\tif (ps_sendcmd(ctx, fd, PS_IOCTL, ulreq, data, len) == -1)\n\t\treturn -1;\n#else\n\tif (ps_sendcmd(ctx, fd, PS_IOCTL, req, data, len) == -1)\n\t\treturn -1;\n#endif\n\treturn ps_root_readerror(ctx, data, len);\n}\n\nssize_t\nps_root_unlink(struct dhcpcd_ctx *ctx, const char *file)\n{\n\tif (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_UNLINK, 0, file,\n\t\tstrlen(file) + 1) == -1)\n\t\treturn -1;\n\treturn ps_root_readerror(ctx, NULL, 0);\n}\n\nssize_t\nps_root_readfile(struct dhcpcd_ctx *ctx, const char *file, void *data,\n    size_t len)\n{\n\tif (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_READFILE, 0, file,\n\t\tstrlen(file) + 1) == -1)\n\t\treturn -1;\n\treturn ps_root_readerror(ctx, data, len);\n}\n\nssize_t\nps_root_writefile(struct dhcpcd_ctx *ctx, const char *file, mode_t mode,\n    const void *data, size_t len)\n{\n\tchar buf[PS_BUFLEN];\n\tsize_t flen;\n\n\tflen = strlcpy(buf, file, sizeof(buf));\n\tflen += 1;\n\tif (flen > sizeof(buf) || flen + len > sizeof(buf)) {\n\t\terrno = ENOBUFS;\n\t\treturn -1;\n\t}\n\tmemcpy(buf + flen, data, len);\n\n\tif (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_WRITEFILE, mode, buf,\n\t\tflen + len) == -1)\n\t\treturn -1;\n\treturn ps_root_readerror(ctx, NULL, 0);\n}\n\nssize_t\nps_root_filemtime(struct dhcpcd_ctx *ctx, const char *file, time_t *time)\n{\n\tif (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_FILEMTIME, 0, file,\n\t\tstrlen(file) + 1) == -1)\n\t\treturn -1;\n\treturn ps_root_readerror(ctx, time, sizeof(*time));\n}\n\nssize_t\nps_root_logreopen(struct dhcpcd_ctx *ctx)\n{\n\tif (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_LOGREOPEN, 0, NULL, 0) == -1)\n\t\treturn -1;\n\treturn ps_root_readerror(ctx, NULL, 0);\n}\n\n#ifdef PRIVSEP_GETIFADDRS\nint\nps_root_getifaddrs(struct dhcpcd_ctx *ctx, struct ifaddrs **ifahead)\n{\n\tstruct ifaddrs *ifa;\n\tvoid *buf = NULL;\n\tchar *bp, *sap;\n\tsocklen_t salen;\n\tsize_t len;\n\tssize_t err;\n\n\tif (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_GETIFADDRS, 0, NULL, 0) == -1)\n\t\treturn -1;\n\terr = ps_root_mreaderror(ctx, &buf, &len);\n\n\tif (err == -1)\n\t\treturn -1;\n\n\t/* Should be impossible - lo0 will always exist. */\n\tif (len == 0) {\n\t\t*ifahead = NULL;\n\t\treturn 0;\n\t}\n\n\tbp = buf;\n\t*ifahead = (struct ifaddrs *)(void *)bp;\n\tfor (ifa = *ifahead; ifa != NULL; ifa = ifa->ifa_next) {\n\t\tif (len < ALIGN(sizeof(*ifa)) + ALIGN(IFNAMSIZ) +\n\t\t\tALIGN(sizeof(salen) * IFA_NADDRS))\n\t\t\tgoto err;\n\t\tbp += ALIGN(sizeof(*ifa));\n\t\tifa->ifa_name = bp;\n\t\tbp += ALIGN(IFNAMSIZ);\n\t\tsap = bp;\n\t\tbp += ALIGN(sizeof(salen) * IFA_NADDRS);\n\t\tlen -= ALIGN(sizeof(*ifa)) + ALIGN(IFNAMSIZ) +\n\t\t    ALIGN(sizeof(salen) * IFA_NADDRS);\n\n#define COPYOUTSA(addr)                                         \\\n\tdo {                                                    \\\n\t\tmemcpy(&salen, sap, sizeof(salen));             \\\n\t\tif (len < salen)                                \\\n\t\t\tgoto err;                               \\\n\t\tif (salen != 0) {                               \\\n\t\t\t(addr) = (struct sockaddr *)(void *)bp; \\\n\t\t\tbp += ALIGN(salen);                     \\\n\t\t\tlen -= ALIGN(salen);                    \\\n\t\t}                                               \\\n\t\tsap += sizeof(salen);                           \\\n\t} while (0 /* CONSTCOND */)\n\n\t\tCOPYOUTSA(ifa->ifa_addr);\n\t\tCOPYOUTSA(ifa->ifa_netmask);\n\t\tCOPYOUTSA(ifa->ifa_broadaddr);\n\n\t\tmemcpy(&salen, sap, sizeof(salen));\n\t\tif (len < salen)\n\t\t\tgoto err;\n\t\tif (salen != 0) {\n\t\t\tifa->ifa_data = bp;\n\t\t\tbp += ALIGN(salen);\n\t\t\tlen -= ALIGN(salen);\n\t\t} else\n\t\t\tifa->ifa_data = NULL;\n\n\t\tif (len != 0)\n\t\t\tifa->ifa_next = (struct ifaddrs *)(void *)bp;\n\t\telse\n\t\t\tifa->ifa_next = NULL;\n\t}\n\treturn 0;\n\nerr:\n\tfree(buf);\n\t*ifahead = NULL;\n\terrno = EINVAL;\n\treturn -1;\n}\n#endif\n\n#ifdef AUTH\nint\nps_root_getauthrdm(struct dhcpcd_ctx *ctx, uint64_t *rdm)\n{\n\tif (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_AUTH_MONORDM, 0, rdm,\n\t\tsizeof(*rdm)) == -1)\n\t\treturn -1;\n\treturn (int)ps_root_readerror(ctx, rdm, sizeof(*rdm));\n}\n#endif\n\n#ifdef PLUGIN_DEV\nint\nps_root_dev_initialised(struct dhcpcd_ctx *ctx, const char *ifname)\n{\n\tif (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_DEV_INITTED, 0, ifname,\n\t\tstrlen(ifname) + 1) == -1)\n\t\treturn -1;\n\treturn (int)ps_root_readerror(ctx, NULL, 0);\n}\n\nint\nps_root_dev_listening(struct dhcpcd_ctx *ctx)\n{\n\tif (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_DEV_LISTENING, 0, NULL, 0) ==\n\t    -1)\n\t\treturn -1;\n\treturn (int)ps_root_readerror(ctx, NULL, 0);\n}\n#endif\n"
  },
  {
    "path": "src/privsep-root.h",
    "content": "/*\n * Privilege Separation for dhcpcd\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef PRIVSEP_ROOT_H\n#define PRIVSEP_ROOT_H\n\n#include \"if.h\"\n\n#if defined(PRIVSEP) && (defined(HAVE_CAPSICUM) || defined(__linux__))\n#define PRIVSEP_GETIFADDRS\n#endif\n\npid_t ps_root_start(struct dhcpcd_ctx *ctx);\nvoid ps_root_close(struct dhcpcd_ctx *ctx);\nint ps_root_stop(struct dhcpcd_ctx *ctx);\nvoid ps_root_signalcb(int, void *);\n\nssize_t ps_root_readerror(struct dhcpcd_ctx *, void *, size_t);\nssize_t ps_root_mreaderror(struct dhcpcd_ctx *, void **, size_t *);\nssize_t ps_root_ioctl(struct dhcpcd_ctx *, ioctl_request_t, void *, size_t);\nssize_t ps_root_unlink(struct dhcpcd_ctx *, const char *);\nssize_t ps_root_filemtime(struct dhcpcd_ctx *, const char *, time_t *);\nssize_t ps_root_readfile(struct dhcpcd_ctx *, const char *, void *, size_t);\nssize_t ps_root_writefile(struct dhcpcd_ctx *, const char *, mode_t,\n    const void *, size_t);\nssize_t ps_root_logreopen(struct dhcpcd_ctx *);\nssize_t ps_root_script(struct dhcpcd_ctx *, const void *, size_t);\nssize_t ps_root_stopprocesses(struct dhcpcd_ctx *);\nint ps_root_getauthrdm(struct dhcpcd_ctx *, uint64_t *);\n#ifdef PRIVSEP_GETIFADDRS\nint ps_root_getifaddrs(struct dhcpcd_ctx *, struct ifaddrs **);\n#endif\n\nssize_t ps_root_os(struct dhcpcd_ctx *, struct ps_msghdr *, struct msghdr *,\n    void **, size_t *, bool *);\n#if defined(BSD) || defined(__sun)\nssize_t ps_root_route(struct dhcpcd_ctx *, void *, size_t);\nssize_t ps_root_ioctllink(struct dhcpcd_ctx *, unsigned long, void *, size_t);\nssize_t ps_root_ioctl6(struct dhcpcd_ctx *, unsigned long, void *, size_t);\nssize_t ps_root_indirectioctl(struct dhcpcd_ctx *, unsigned long, const char *,\n    void *, size_t);\nssize_t ps_root_ifignoregroup(struct dhcpcd_ctx *, const char *);\nssize_t ps_root_sysctl(struct dhcpcd_ctx *, const int *, unsigned int, void *,\n    size_t *, const void *, size_t);\n#endif\n#ifdef __linux__\nssize_t ps_root_sendnetlink(struct dhcpcd_ctx *, int, struct msghdr *);\n#endif\n\n#ifdef PLUGIN_DEV\nint ps_root_dev_initialised(struct dhcpcd_ctx *, const char *);\nint ps_root_dev_listening(struct dhcpcd_ctx *);\n#endif\n\n#endif\n"
  },
  {
    "path": "src/privsep-sun.c",
    "content": "/*\n * Privilege Separation for dhcpcd, Solaris driver\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/ioctl.h>\n\n#include <errno.h>\n#include <unistd.h>\n\n#include \"dhcpcd.h\"\n#include \"logerr.h\"\n#include \"privsep.h\"\n\n#warning Solaris privsep should compile but wont work,\n#warning no DLPI support, ioctl support need rework\n/* We should implement privileges(5) as well.\n * https://illumos.org/man/5/privileges */\n\nstatic ssize_t\nps_root_doioctl6(unsigned long req, void *data, size_t len)\n{\n\tint s, err;\n\n\ts = xsocket(PF_INET6, SOCK_DGRAM, 0);\n\tif (s != -1)\n\t\terr = ioctl(s, req, data, len);\n\telse\n\t\terr = -1;\n\tif (err == -1)\n\t\tlogerr(__func__);\n\tif (s != -1)\n\t\tclose(s);\n\treturn err;\n}\n\nstatic ssize_t\nps_root_doroute(void *data, size_t len)\n{\n\tint s;\n\tssize_t err;\n\n\ts = xsocket(PF_ROUTE, SOCK_RAW, 0);\n\tif (s != -1)\n\t\terr = write(s, data, len);\n\telse\n\t\terr = -1;\n\tif (err == -1)\n\t\tlogerr(__func__);\n\tif (s != -1)\n\t\tclose(s);\n\treturn err;\n}\n\nssize_t\nps_root_os(struct ps_msghdr *psm, struct msghdr *msg, void **rdata,\n    size_t *rlen, __unused bool *free_rdata)\n{\n\tstruct iovec *iov = msg->msg_iov;\n\tvoid *data = iov->iov_base;\n\tsize_t len = iov->iov_len;\n\tssize_t err;\n\n\tswitch (psm->ps_cmd) {\n\tcase PS_IOCTL6:\n\t\terr = ps_root_doioctl6(psm->ps_flags, data, len);\n\tcase PS_ROUTE:\n\t\treturn ps_root_doroute(data, len);\n\tdefault:\n\t\terrno = ENOTSUP;\n\t\treturn -1;\n\t}\n\n\tif (err != -1) {\n\t\t*rdata = data;\n\t\t*rlen = len;\n\t}\n\treturn err;\n}\n\nssize_t\nps_root_ioctl6(struct dhcpcd_ctx *ctx, unsigned long request, void *data,\n    size_t len)\n{\n\tif (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_IOCTL6, request, data, len) ==\n\t    -1)\n\t\treturn -1;\n\treturn ps_root_readerror(ctx, data, len);\n}\n\nssize_t\nps_root_route(struct dhcpcd_ctx *ctx, void *data, size_t len)\n{\n\tif (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_ROUTE, 0, data, len) == -1)\n\t\treturn -1;\n\treturn ps_root_readerror(ctx, data, len);\n}\n"
  },
  {
    "path": "src/privsep.c",
    "content": "/*\n * Privilege Separation for dhcpcd\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n/*\n * The current design is this:\n * Spawn a priv process to carry out privileged actions and\n * spawning unpriv process to initate network connections such as BPF\n * or address specific listener.\n * Spawn an unpriv process to send/receive common network data.\n * Then drop all privs and start running.\n * Every process aside from the privileged proxy is chrooted.\n * All privsep processes ignore signals - only the manager process accepts them.\n *\n * dhcpcd will maintain the config file in the chroot, no need to handle\n * this in a script or something.\n */\n\n#include <sys/types.h>\n#include <sys/resource.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <sys/wait.h>\n\n#ifdef AF_LINK\n#include <net/if_dl.h>\n#endif\n\n#include <assert.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <grp.h>\n#include <paths.h>\n#include <pwd.h>\n#include <signal.h>\n#include <stddef.h> /* For offsetof, struct padding debug */\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include \"arp.h\"\n#include \"common.h\"\n#include \"control.h\"\n#include \"dev.h\"\n#include \"dhcp.h\"\n#include \"dhcp6.h\"\n#include \"eloop.h\"\n#include \"ipv6nd.h\"\n#include \"logerr.h\"\n#include \"privsep.h\"\n\n#ifdef HAVE_CAPSICUM\n#include <sys/capsicum.h>\n#include <sys/procdesc.h>\n\n#include <capsicum_helpers.h>\n#endif\n#ifdef HAVE_UTIL_H\n#include <util.h>\n#endif\n\n/* CMSG_ALIGN is a Linux extension */\n#ifndef CMSG_ALIGN\n#define CMSG_ALIGN(n) (CMSG_SPACE((n)) - CMSG_SPACE(0))\n#endif\n\n/* Calculate number of padding bytes to achieve 'struct cmsghdr' alignment */\n#define CALC_CMSG_PADLEN(has_cmsg, pos) \\\n\t((has_cmsg) ? (socklen_t)(CMSG_ALIGN((pos)) - (pos)) : 0)\n\nint\nps_init(struct dhcpcd_ctx *ctx)\n{\n\tstruct passwd *pw;\n\tstruct stat st;\n\n\terrno = 0;\n\tif ((ctx->ps_user = pw = getpwnam(PRIVSEP_USER)) == NULL) {\n\t\tctx->options &= ~DHCPCD_PRIVSEP;\n\t\tif (errno == 0) {\n\t\t\tlogerrx(\"no such user %s\", PRIVSEP_USER);\n\t\t\t/* Just incase logerrx caused an error... */\n\t\t\terrno = 0;\n\t\t} else\n\t\t\tlogerr(\"getpwnam\");\n\t\treturn -1;\n\t}\n\n\tif (stat(pw->pw_dir, &st) == -1 || !S_ISDIR(st.st_mode)) {\n\t\tctx->options &= ~DHCPCD_PRIVSEP;\n\t\tlogerrx(\"refusing chroot: %s: %s\", PRIVSEP_USER, pw->pw_dir);\n\t\terrno = 0;\n\t\treturn -1;\n\t}\n\n\tctx->options |= DHCPCD_PRIVSEP;\n\treturn 0;\n}\n\nstatic int\nps_dropprivs(struct dhcpcd_ctx *ctx)\n{\n\tstruct passwd *pw = ctx->ps_user;\n\tint fd_out = ctx->options & DHCPCD_DUMPLEASE ? STDOUT_FILENO :\n\t\t\t\t\t\t       STDERR_FILENO;\n\n\tif (ctx->options & DHCPCD_LAUNCHER)\n#ifdef ASAN\n\t\tlogwarnx(\"not chrooting as compiled for ASAN\");\n#else\n\t\tlogdebugx(\"chrooting as %s to %s\", pw->pw_name, pw->pw_dir);\n\n\tif (chroot(pw->pw_dir) == -1 &&\n\t    (errno != EPERM || ctx->options & DHCPCD_FORKED))\n\t\tlogerr(\"%s: chroot: %s\", __func__, pw->pw_dir);\n#endif\n\n\tif (chdir(\"/\") == -1)\n\t\tlogerr(\"%s: chdir: /\", __func__);\n\n\tif ((setgroups(1, &pw->pw_gid) == -1 || setgid(pw->pw_gid) == -1 ||\n\t\tsetuid(pw->pw_uid) == -1) &&\n\t    (errno != EPERM || ctx->options & DHCPCD_FORKED)) {\n\t\tlogerr(\"failed to drop privileges\");\n\t\treturn -1;\n\t}\n\n\tstruct rlimit rzero = { .rlim_cur = 0, .rlim_max = 0 };\n\n\t/* Prohibit new files, sockets, etc */\n\t/*\n\t * If poll(2) is called with nfds>RLIMIT_NOFILE then it returns EINVAL.\n\t * We don't know the final value of nfds at this point *easily*.\n\t * Sadly, this is a POSIX limitation and most platforms adhere to it.\n\t * However, some are not that strict and are whitelisted below.\n\t * Also, if we're not using poll then we can be restrictive.\n\t *\n\t * For the non whitelisted platforms there should be a sandbox to\n\t * fallback to where we don't allow new files, etc:\n\t *      Linux:seccomp, FreeBSD:capsicum, OpenBSD:pledge\n\t * Solaris users are sadly out of luck on both counts.\n\t */\n#if defined(__NetBSD__) || defined(__DragonFly__) || defined(HAVE_KQUEUE) || \\\n    defined(HAVE_EPOLL)\n\t/* The control proxy *does* need to create new fd's via accept(2). */\n\tif (ctx->ps_ctl == NULL || ctx->ps_ctl->psp_pid != getpid()) {\n\t\tif (setrlimit(RLIMIT_NOFILE, &rzero) == -1)\n\t\t\tlogerr(\"setrlimit RLIMIT_NOFILE\");\n\t}\n#endif\n\n#define DHC_NOCHKIO (DHCPCD_STARTED | DHCPCD_DAEMONISE)\n\t/* Prohibit writing to files.\n\t * Obviously this won't work if we are using a logfile\n\t * or redirecting stderr to a file. */\n\tif ((ctx->options & DHC_NOCHKIO) == DHC_NOCHKIO ||\n\t    (ctx->logfile == NULL && isatty(fd_out) == 1)) {\n\t\tif (setrlimit(RLIMIT_FSIZE, &rzero) == -1)\n\t\t\tlogerr(\"setrlimit RLIMIT_FSIZE\");\n\t}\n\n#ifdef RLIMIT_NPROC\n\t/* Prohibit forks */\n\tif (setrlimit(RLIMIT_NPROC, &rzero) == -1)\n\t\tlogerr(\"setrlimit RLIMIT_NPROC\");\n#endif\n\n\treturn 0;\n}\n\nstatic int\nps_setbuf0(int fd, int ctl, int minlen)\n{\n\tint len;\n\tsocklen_t slen;\n\n\tslen = sizeof(len);\n\tif (getsockopt(fd, SOL_SOCKET, ctl, &len, &slen) == -1)\n\t\treturn -1;\n\n#ifdef __linux__\n\tlen /= 2;\n#endif\n\tif (len >= minlen)\n\t\treturn 0;\n\n\treturn setsockopt(fd, SOL_SOCKET, ctl, &minlen, sizeof(minlen));\n}\n\nstatic int\nps_setbuf(int fd)\n{\n\t/* Ensure we can receive a fully sized privsep message.\n\t * Double the send buffer. */\n\tint minlen = (int)sizeof(struct ps_msg);\n\n\tif (ps_setbuf0(fd, SO_RCVBUF, minlen) == -1 ||\n\t    ps_setbuf0(fd, SO_SNDBUF, minlen * 2) == -1) {\n\t\tlogerr(__func__);\n\t\treturn -1;\n\t}\n\treturn 0;\n}\n\nint\nps_setbuf_fdpair(int fd[])\n{\n\tif (ps_setbuf(fd[0]) == -1 || ps_setbuf(fd[1]) == -1)\n\t\treturn -1;\n\treturn 0;\n}\n\n#ifdef PRIVSEP_RIGHTS\nint\nps_rights_limit_ioctl(int fd)\n{\n\tcap_rights_t rights;\n\n\tcap_rights_init(&rights, CAP_IOCTL);\n\tif (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)\n\t\treturn -1;\n\treturn 0;\n}\n\nint\nps_rights_limit_fd_fctnl(int fd)\n{\n\tcap_rights_t rights;\n\n\tcap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_ACCEPT,\n\t    CAP_FCNTL);\n\tif (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)\n\t\treturn -1;\n\treturn 0;\n}\n\nint\nps_rights_limit_fd(int fd)\n{\n\tcap_rights_t rights;\n\n\tcap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_SHUTDOWN);\n\tif (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)\n\t\treturn -1;\n\treturn 0;\n}\n\nint\nps_rights_limit_fd_sockopt(int fd)\n{\n\tcap_rights_t rights;\n\n\tcap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_GETSOCKOPT,\n\t    CAP_SETSOCKOPT);\n\tif (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)\n\t\treturn -1;\n\treturn 0;\n}\n\nint\nps_rights_limit_fd_rdonly(int fd)\n{\n\tcap_rights_t rights;\n\n\tcap_rights_init(&rights, CAP_READ, CAP_EVENT);\n\tif (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)\n\t\treturn -1;\n\treturn 0;\n}\n\nint\nps_rights_limit_fdpair(int fd[])\n{\n\tif (ps_rights_limit_fd(fd[0]) == -1 || ps_rights_limit_fd(fd[1]) == -1)\n\t\treturn -1;\n\treturn 0;\n}\n\nstatic int\nps_rights_limit_stdio()\n{\n\tconst int iebadf = CAPH_IGNORE_EBADF;\n\tint error = 0;\n\n\tif (caph_limit_stream(STDIN_FILENO, CAPH_READ | iebadf) == -1)\n\t\terror = -1;\n\tif (caph_limit_stream(STDOUT_FILENO, CAPH_WRITE | iebadf) == -1)\n\t\terror = -1;\n\tif (caph_limit_stream(STDERR_FILENO, CAPH_WRITE | iebadf) == -1)\n\t\terror = -1;\n\n\treturn error;\n}\n#endif\n\n#ifdef HAVE_CAPSICUM\nstatic void\nps_processhangup(void *arg, unsigned short events)\n{\n\tstruct ps_process *psp = arg;\n\tstruct dhcpcd_ctx *ctx = psp->psp_ctx;\n\n\tif (!(events & ELE_HANGUP))\n\t\tlogerrx(\"%s: unexpected event 0x%04x\", __func__, events);\n\n\tlogdebugx(\"%s%s%s exited from PID %d\", psp->psp_ifname,\n\t    psp->psp_ifname[0] != '\\0' ? \": \" : \"\", psp->psp_name,\n\t    psp->psp_pid);\n\n\tps_freeprocess(psp);\n\n\tif (!(ctx->options & DHCPCD_EXITING))\n\t\treturn;\n\tif (!(ps_waitforprocs(ctx)))\n\t\teloop_exit(ctx->eloop, EXIT_SUCCESS);\n}\n#endif\n\npid_t\nps_startprocess(struct ps_process *psp,\n    void (*recv_msg)(void *, unsigned short),\n    void (*recv_unpriv_msg)(void *, unsigned short),\n    int (*callback)(struct ps_process *), unsigned int flags)\n{\n\tstruct dhcpcd_ctx *ctx = psp->psp_ctx;\n\tint fd[2];\n\tpid_t pid;\n\n\tif (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fd) == -1) {\n\t\tlogerr(\"%s: socketpair\", __func__);\n\t\treturn -1;\n\t}\n\tif (ps_setbuf_fdpair(fd) == -1) {\n\t\tlogerr(\"%s: ps_setbuf_fdpair\", __func__);\n\t\treturn -1;\n\t}\n#ifdef PRIVSEP_RIGHTS\n\tif (ps_rights_limit_fdpair(fd) == -1) {\n\t\tlogerr(\"%s: ps_rights_limit_fdpair\", __func__);\n\t\treturn -1;\n\t}\n#endif\n\n#ifdef HAVE_CAPSICUM\n\tpid = pdfork(&psp->psp_pfd, PD_CLOEXEC);\n#else\n\tpid = fork();\n#endif\n\tswitch (pid) {\n\tcase -1:\n#ifdef HAVE_CAPSICUM\n\t\tlogerr(\"pdfork\");\n#else\n\t\tlogerr(\"fork\");\n#endif\n\t\treturn -1;\n\tcase 0:\n\t\tpsp->psp_pid = getpid();\n\t\tpsp->psp_fd = fd[1];\n\t\tclose(fd[0]);\n\t\tbreak;\n\tdefault:\n\t\tpsp->psp_pid = pid;\n\t\tpsp->psp_fd = fd[0];\n\t\tclose(fd[1]);\n\t\tif (recv_unpriv_msg == NULL)\n\t\t\t;\n\t\telse if (eloop_event_add(ctx->eloop, psp->psp_fd, ELE_READ,\n\t\t\t     recv_unpriv_msg, psp) == -1) {\n\t\t\tlogerr(\"%s: eloop_event_add fd %d\", __func__,\n\t\t\t    psp->psp_fd);\n\t\t\treturn -1;\n\t\t}\n#ifdef HAVE_CAPSICUM\n\t\tif (eloop_event_add(ctx->eloop, psp->psp_pfd, ELE_HANGUP,\n\t\t\tps_processhangup, psp) == -1) {\n\t\t\tlogerr(\"%s: eloop_event_add pfd %d\", __func__,\n\t\t\t    psp->psp_pfd);\n\t\t\treturn -1;\n\t\t}\n#endif\n\t\tpsp->psp_started = true;\n\t\treturn pid;\n\t}\n\n\t/* If we are not the root process, close un-needed stuff. */\n\tif (ctx->ps_root != psp) {\n\t\tps_root_close(ctx);\n#ifdef PLUGIN_DEV\n\t\tdev_stop(ctx);\n#endif\n\t}\n\n\tctx->options |= DHCPCD_FORKED;\n\tif (ctx->ps_log_fd != -1)\n\t\tlogsetfd(ctx->ps_log_fd);\n\n#ifdef DEBUG_FD\n\tlogerrx(\"pid %d log_fd=%d data_fd=%d psp_fd=%d\", getpid(),\n\t    ctx->ps_log_fd, ctx->ps_data_fd, psp->psp_fd);\n#endif\n\n\tif (ctx->fork_fd != -1) {\n\t\tclose(ctx->fork_fd);\n\t\tctx->fork_fd = -1;\n\t}\n\n\tif (eloop_forked(ctx->eloop, ELF_KEEP_SIGNALS) == -1) {\n\t\tlogerr(\"%s: eloop_forked\", __func__);\n\t\tgoto errexit;\n\t}\n\n\tpidfile_clean();\n\tps_freeprocesses(ctx, psp);\n\n\tif (ctx->ps_root != psp) {\n\t\tctx->options &= ~DHCPCD_PRIVSEPROOT;\n\t\tctx->ps_root = NULL;\n\t\tif (ctx->ps_log_root_fd != -1) {\n\t\t\t/* Already removed from eloop thanks to above clear. */\n\t\t\tclose(ctx->ps_log_root_fd);\n\t\t\tctx->ps_log_root_fd = -1;\n\t\t}\n#ifdef PRIVSEP_RIGHTS\n\t\tif (ps_rights_limit_stdio() == -1) {\n\t\t\tlogerr(\"ps_rights_limit_stdio\");\n\t\t\tgoto errexit;\n\t\t}\n#endif\n\t}\n\n\tif (eloop_event_add(ctx->eloop, psp->psp_fd, ELE_READ, recv_msg, psp) ==\n\t    -1) {\n\t\tlogerr(\"%s: eloop_event_add\", __func__);\n\t\tgoto errexit;\n\t}\n\n\tif (callback(psp) == -1)\n\t\tgoto errexit;\n\n\tif (flags & PSF_DROPPRIVS)\n\t\tps_dropprivs(ctx);\n\n\tpsp->psp_started = true;\n\treturn 0;\n\nerrexit:\n\tif (psp->psp_fd != -1) {\n\t\tclose(psp->psp_fd);\n\t\tpsp->psp_fd = -1;\n\t}\n\teloop_exit(ctx->eloop, EXIT_FAILURE);\n\treturn -1;\n}\n\nvoid\nps_process_timeout(void *arg)\n{\n\tstruct dhcpcd_ctx *ctx = arg;\n\n\tlogerrx(\"%s: timed out\", __func__);\n\teloop_exit(ctx->eloop, EXIT_FAILURE);\n}\n\nint\nps_stopprocess(struct ps_process *psp)\n{\n\tint err = 0;\n\n\tif (psp == NULL)\n\t\treturn 0;\n\n\tpsp->psp_started = false;\n\n#ifdef PRIVSEP_DEBUG\n\tlogdebugx(\"%s: me=%d pid=%d fd=%d %s\", __func__, getpid(), psp->psp_pid,\n\t    psp->psp_fd, psp->psp_name);\n#endif\n\n\tif (psp->psp_fd != -1) {\n\t\teloop_event_delete(psp->psp_ctx->eloop, psp->psp_fd);\n#if 0\n\t\tif (ps_sendcmd(psp->psp_ctx, psp->psp_fd, PS_STOP, 0,\n\t\t    NULL, 0) == -1)\n\t\t{\n\t\t\tlogerr(\"%d %d %s %s\", getpid(), psp->psp_pid, psp->psp_name, __func__);\n\t\t\terr = -1;\n\t\t}\n\t\tshutdown(psp->psp_fd, SHUT_WR);\n#else\n\t\tif (shutdown(psp->psp_fd, SHUT_WR) == -1) {\n\t\t\tlogerr(__func__);\n\t\t\terr = -1;\n\t\t}\n#endif\n\t}\n\n\t/* Don't wait for the process as it may not respond to the shutdown\n\t * request. We'll reap the process on receipt of SIGCHLD where we\n\t * also close the fd. */\n\treturn err;\n}\n\nint\nps_start(struct dhcpcd_ctx *ctx)\n{\n\tpid_t pid;\n\n\tTAILQ_INIT(&ctx->ps_processes);\n\n\tswitch (pid = ps_root_start(ctx)) {\n\tcase -1:\n\t\tlogerr(\"ps_root_start\");\n\t\treturn -1;\n\tcase 0:\n\t\treturn 0;\n\tdefault:\n\t\tlogdebugx(\"spawned privileged proxy on PID %d\", pid);\n\t}\n\n\t/* No point in spawning the generic network listener if we're\n\t * not going to use it. */\n\tif (!ps_inet_canstart(ctx))\n\t\tgoto started_net;\n\n\tswitch (pid = ps_inet_start(ctx)) {\n\tcase -1:\n\t\treturn -1;\n\tcase 0:\n\t\treturn 0;\n\tdefault:\n\t\tlogdebugx(\"spawned network proxy on PID %d\", pid);\n\t}\n\nstarted_net:\n\tif (!(ctx->options & DHCPCD_TEST)) {\n\t\tswitch (pid = ps_ctl_start(ctx)) {\n\t\tcase -1:\n\t\t\treturn -1;\n\t\tcase 0:\n\t\t\treturn 0;\n\t\tdefault:\n\t\t\tlogdebugx(\"spawned controller proxy on PID %d\", pid);\n\t\t}\n\t}\n\n#ifdef ARC4RANDOM_H\n\t/* Seed the random number generator early incase it needs /dev/urandom\n\t * which won't be available in the chroot. */\n\tarc4random();\n#endif\n\n\treturn 1;\n}\n\nint\nps_entersandbox(const char *_pledge, const char **sandbox)\n{\n#if !defined(HAVE_PLEDGE)\n\tUNUSED(_pledge);\n#endif\n\n#if defined(HAVE_CAPSICUM)\n\tif (sandbox != NULL)\n\t\t*sandbox = \"capsicum\";\n\treturn cap_enter();\n#elif defined(HAVE_PLEDGE)\n\tif (sandbox != NULL)\n\t\t*sandbox = \"pledge\";\n\t// There is no need to use unveil(2) because we are in an empty chroot\n\t// This is encouraged by Theo de Raadt himself:\n\t// https://www.mail-archive.com/misc@openbsd.org/msg171655.html\n\treturn pledge(_pledge, NULL);\n#elif defined(HAVE_SECCOMP)\n\tif (sandbox != NULL)\n\t\t*sandbox = \"seccomp\";\n\treturn ps_seccomp_enter();\n#else\n\tif (sandbox != NULL)\n\t\t*sandbox = \"posix resource limited\";\n\treturn 0;\n#endif\n}\n\nint\nps_managersandbox(struct dhcpcd_ctx *ctx, const char *_pledge)\n{\n\tconst char *sandbox = NULL;\n\tbool forked;\n\tint dropped;\n\n\tforked = ctx->options & DHCPCD_FORKED;\n\tctx->options &= ~DHCPCD_FORKED;\n\tdropped = ps_dropprivs(ctx);\n\tif (forked)\n\t\tctx->options |= DHCPCD_FORKED;\n\n\t/*\n\t * If we don't have a root process, we cannot use syslog.\n\t * If it cannot be opened before chrooting then syslog(3) will fail.\n\t * openlog(3) does not return an error which doubly sucks.\n\t */\n\tif (ctx->ps_root == NULL) {\n\t\tunsigned int logopts = loggetopts();\n\n\t\tlogopts &= ~LOGERR_LOG;\n\t\tlogsetopts(logopts);\n\t}\n\n\tif (dropped == -1) {\n\t\tlogerr(\"%s: ps_dropprivs\", __func__);\n\t\treturn -1;\n\t}\n\n#ifdef PRIVSEP_RIGHTS\n\tif ((ctx->pf_inet_fd != -1 &&\n\t\tps_rights_limit_ioctl(ctx->pf_inet_fd) == -1) ||\n\t    ps_rights_limit_stdio() == -1) {\n\t\tlogerr(\"%s: cap_rights_limit\", __func__);\n\t\treturn -1;\n\t}\n#endif\n\n\tif (_pledge == NULL)\n\t\t_pledge = \"stdio\";\n\tif (ps_entersandbox(_pledge, &sandbox) == -1) {\n\t\tif (errno == ENOSYS) {\n\t\t\tif (sandbox != NULL)\n\t\t\t\tlogwarnx(\"sandbox unavailable: %s\", sandbox);\n\t\t\treturn 0;\n\t\t}\n\t\tlogerr(\"%s: %s\", __func__, sandbox);\n\t\treturn -1;\n\t} else if (ctx->options & DHCPCD_LAUNCHER ||\n\t    ((!(ctx->options & DHCPCD_DAEMONISE)) &&\n\t\tctx->options & DHCPCD_MANAGER))\n\t\tlogdebugx(\"sandbox: %s\", sandbox);\n\treturn 0;\n}\n\nint\nps_stop(struct dhcpcd_ctx *ctx)\n{\n\tint r, ret = 0;\n\n\tif (!(ctx->options & DHCPCD_PRIVSEP) || ctx->options & DHCPCD_FORKED ||\n\t    ctx->eloop == NULL)\n\t\treturn 0;\n\n\tif (ctx->ps_ctl != NULL) {\n\t\tr = ps_ctl_stop(ctx);\n\t\tif (r != 0)\n\t\t\tret = r;\n\t}\n\n\tif (ctx->ps_inet != NULL) {\n\t\tr = ps_inet_stop(ctx);\n\t\tif (r != 0)\n\t\t\tret = r;\n\t}\n\n\tif (ctx->ps_root != NULL) {\n\t\tif (ps_root_stopprocesses(ctx) == -1)\n\t\t\tret = -1;\n\t}\n\n\treturn ret;\n}\n\nbool\nps_waitforprocs(struct dhcpcd_ctx *ctx)\n{\n\tstruct ps_process *psp = TAILQ_FIRST(&ctx->ps_processes);\n\n\tif (psp == NULL)\n\t\treturn false;\n\n\t/* Different processes */\n\tif (psp != TAILQ_LAST(&ctx->ps_processes, ps_process_head))\n\t\treturn true;\n\n\treturn !psp->psp_started;\n}\n\nint\nps_stopwait(struct dhcpcd_ctx *ctx)\n{\n\tint error = EXIT_SUCCESS;\n\n\tif (!ps_waitforprocs(ctx))\n\t\treturn 0;\n\n\tctx->options |= DHCPCD_EXITING;\n\tif (eloop_timeout_add_sec(ctx->eloop, PS_PROCESS_TIMEOUT,\n\t\tps_process_timeout, ctx) == -1)\n\t\tlogerr(\"%s: eloop_timeout_add_sec\", __func__);\n\n#ifdef HAVE_CAPSICUM\n\tstruct ps_process *psp;\n\n\tTAILQ_FOREACH(psp, &ctx->ps_processes, next) {\n\t\tif (psp->psp_pfd == -1)\n\t\t\tcontinue;\n\t\tif (eloop_event_add(ctx->eloop, psp->psp_pfd, ELE_HANGUP,\n\t\t\tps_processhangup, psp) == -1)\n\t\t\tlogerr(\"%s: eloop_event_add pfd %d\", __func__,\n\t\t\t    psp->psp_pfd);\n\t}\n#endif\n\n\terror = eloop_start(ctx->eloop);\n\tif (error < 0)\n\t\tlogerr(\"%s: eloop_start\", __func__);\n\n\teloop_timeout_delete(ctx->eloop, ps_process_timeout, ctx);\n\n\treturn error;\n}\n\nvoid\nps_freeprocess(struct ps_process *psp)\n{\n\tstruct dhcpcd_ctx *ctx = psp->psp_ctx;\n\n\tTAILQ_REMOVE(&ctx->ps_processes, psp, next);\n\n\tif (psp->psp_fd != -1) {\n\t\teloop_event_delete(ctx->eloop, psp->psp_fd);\n\t\tclose(psp->psp_fd);\n\t}\n\tif (psp->psp_work_fd != -1) {\n\t\teloop_event_delete(ctx->eloop, psp->psp_work_fd);\n\t\tclose(psp->psp_work_fd);\n\t}\n#ifdef HAVE_CAPSICUM\n\tif (psp->psp_pfd != -1) {\n\t\teloop_event_delete(ctx->eloop, psp->psp_pfd);\n\t\tclose(psp->psp_pfd);\n\t}\n#endif\n\tif (ctx->ps_root == psp)\n\t\tctx->ps_root = NULL;\n\tif (ctx->ps_inet == psp)\n\t\tctx->ps_inet = NULL;\n\tif (ctx->ps_ctl == psp)\n\t\tctx->ps_ctl = NULL;\n#ifdef INET\n\tif (psp->psp_bpf != NULL)\n\t\tbpf_close(psp->psp_bpf);\n#endif\n\tfree(psp);\n}\n\nstatic void\nps_free(struct dhcpcd_ctx *ctx)\n{\n\tstruct ps_process *ppsp, *psp;\n\tbool stop;\n\n\tif (ctx->ps_root != NULL)\n\t\tppsp = ctx->ps_root;\n\telse if (ctx->ps_ctl != NULL)\n\t\tppsp = ctx->ps_ctl;\n\telse\n\t\tppsp = NULL;\n\tif (ppsp != NULL)\n\t\tstop = ppsp->psp_pid == getpid();\n\telse\n\t\tstop = false;\n\n\twhile ((psp = TAILQ_FIRST(&ctx->ps_processes)) != NULL) {\n\t\tif (stop && psp != ppsp)\n\t\t\tps_stopprocess(psp);\n\t\tps_freeprocess(psp);\n\t}\n}\n\nint\nps_unrollmsg(struct msghdr *msg, struct ps_msghdr *psm, const void *data,\n    size_t len)\n{\n\tuint8_t *datap, *namep, *controlp;\n\tsocklen_t cmsg_padlen = CALC_CMSG_PADLEN(psm->ps_controllen,\n\t    psm->ps_namelen);\n\n\tnamep = UNCONST(data);\n\tcontrolp = namep + psm->ps_namelen + cmsg_padlen;\n\tdatap = controlp + psm->ps_controllen;\n\n\tif (psm->ps_namelen != 0) {\n\t\tif (psm->ps_namelen > len) {\n\t\t\terrno = EINVAL;\n\t\t\treturn -1;\n\t\t}\n\t\tmsg->msg_name = namep;\n\t\tlen -= psm->ps_namelen;\n\t} else\n\t\tmsg->msg_name = NULL;\n\tmsg->msg_namelen = psm->ps_namelen;\n\n\tif (psm->ps_controllen != 0) {\n\t\tif (psm->ps_controllen > len) {\n\t\t\terrno = EINVAL;\n\t\t\treturn -1;\n\t\t}\n\t\tmsg->msg_control = controlp;\n\t\tlen -= psm->ps_controllen + cmsg_padlen;\n\t} else\n\t\tmsg->msg_control = NULL;\n\tmsg->msg_controllen = psm->ps_controllen;\n\n\tif (len != 0) {\n\t\tmsg->msg_iovlen = 1;\n\t\tmsg->msg_iov[0].iov_base = datap;\n\t\tmsg->msg_iov[0].iov_len = len;\n\t} else {\n\t\tmsg->msg_iovlen = 0;\n\t\tmsg->msg_iov[0].iov_base = NULL;\n\t\tmsg->msg_iov[0].iov_len = 0;\n\t}\n\treturn 0;\n}\n\nssize_t\nps_sendpsmmsg(struct dhcpcd_ctx *ctx, int fd, struct ps_msghdr *psm,\n    const struct msghdr *msg)\n{\n\tlong padding[1] = { 0 };\n\tstruct iovec iov[] = {\n\t\t{ .iov_base = UNCONST(psm), .iov_len = sizeof(*psm) },\n\t\t{\n\t\t    .iov_base = NULL,\n\t\t}, /* name */\n\t\t{\n\t\t    .iov_base = NULL,\n\t\t}, /* control padding */\n\t\t{\n\t\t    .iov_base = NULL,\n\t\t}, /* control */\n\t\t{\n\t\t    .iov_base = NULL,\n\t\t}, /* payload 1 */\n\t\t{\n\t\t    .iov_base = NULL,\n\t\t}, /* payload 2 */\n\t\t{\n\t\t    .iov_base = NULL,\n\t\t}, /* payload 3 */\n\t};\n\tstruct msghdr m = { .msg_iov = iov, .msg_iovlen = 1 };\n\tssize_t len;\n\n\tif (msg != NULL) {\n\t\tstruct iovec *iovp = &iov[1];\n\t\tint i;\n\t\tsocklen_t cmsg_padlen;\n\n\t\tpsm->ps_namelen = msg->msg_namelen;\n\t\tpsm->ps_controllen = (socklen_t)msg->msg_controllen;\n\t\tpsm->ps_datalen = 0;\n\n\t\tiovp->iov_base = msg->msg_name;\n\t\tiovp->iov_len = msg->msg_namelen;\n\t\tiovp++;\n\t\tm.msg_iovlen++;\n\n\t\tcmsg_padlen = CALC_CMSG_PADLEN(msg->msg_controllen,\n\t\t    msg->msg_namelen);\n\t\tassert(cmsg_padlen <= sizeof(padding));\n\t\tiovp->iov_len = cmsg_padlen;\n\t\tiovp->iov_base = cmsg_padlen != 0 ? padding : NULL;\n\t\tiovp++;\n\t\tm.msg_iovlen++;\n\n\t\tiovp->iov_base = msg->msg_control;\n\t\tiovp->iov_len = msg->msg_controllen;\n\t\tiovp++;\n\t\tm.msg_iovlen++;\n\n\t\tfor (i = 0; i < (int)msg->msg_iovlen; i++) {\n\t\t\tif ((size_t)m.msg_iovlen >= __arraycount(iov)) {\n\t\t\t\terrno = ENOBUFS;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t\tm.msg_iovlen++;\n\t\t\tiovp->iov_base = msg->msg_iov[i].iov_base;\n\t\t\tiovp->iov_len = msg->msg_iov[i].iov_len;\n\t\t\tiovp++;\n\t\t\tpsm->ps_datalen += msg->msg_iov[i].iov_len;\n\t\t}\n\t}\n\n\tlen = sendmsg(fd, &m, 0);\n\n\tif (len == -1 && ctx != NULL) {\n\t\tif (ctx->options & DHCPCD_FORKED &&\n\t\t    !(ctx->options & DHCPCD_PRIVSEPROOT))\n\t\t\teloop_exit(ctx->eloop, EXIT_FAILURE);\n\t}\n\treturn len;\n}\n\nssize_t\nps_sendpsmdata(struct dhcpcd_ctx *ctx, int fd, struct ps_msghdr *psm,\n    const void *data, size_t len)\n{\n\tstruct iovec iov[] = {\n\t\t{ .iov_base = UNCONST(data), .iov_len = len },\n\t};\n\tstruct msghdr msg = {\n\t\t.msg_iov = iov,\n\t\t.msg_iovlen = 1,\n\t};\n\n\treturn ps_sendpsmmsg(ctx, fd, psm, &msg);\n}\n\nssize_t\nps_sendmsg(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags,\n    const struct msghdr *msg)\n{\n\tstruct ps_msghdr psm = {\n\t\t.ps_cmd = cmd,\n\t\t.ps_flags = flags,\n\t\t.ps_namelen = msg->msg_namelen,\n\t\t.ps_controllen = (socklen_t)msg->msg_controllen,\n\t};\n\tsize_t i;\n\n\tfor (i = 0; i < (size_t)msg->msg_iovlen; i++)\n\t\tpsm.ps_datalen += msg->msg_iov[i].iov_len;\n\n#if 0 /* For debugging structure padding. */\n\tlogerrx(\"psa.family %lu %zu\", offsetof(struct ps_addr, psa_family), sizeof(psm.ps_id.psi_addr.psa_family));\n\tlogerrx(\"psa.pad %lu %zu\", offsetof(struct ps_addr, psa_pad), sizeof(psm.ps_id.psi_addr.psa_pad));\n\tlogerrx(\"psa.psa_u %lu %zu\", offsetof(struct ps_addr, psa_u), sizeof(psm.ps_id.psi_addr.psa_u));\n\tlogerrx(\"psa %zu\", sizeof(psm.ps_id.psi_addr));\n\n\tlogerrx(\"psi.addr %lu %zu\", offsetof(struct ps_id, psi_addr), sizeof(psm.ps_id.psi_addr));\n\tlogerrx(\"psi.index %lu %zu\", offsetof(struct ps_id, psi_ifindex), sizeof(psm.ps_id.psi_ifindex));\n\tlogerrx(\"psi.cmd %lu %zu\", offsetof(struct ps_id, psi_cmd), sizeof(psm.ps_id.psi_cmd));\n\tlogerrx(\"psi.pad %lu %zu\", offsetof(struct ps_id, psi_pad), sizeof(psm.ps_id.psi_pad));\n\tlogerrx(\"psi %zu\", sizeof(struct ps_id));\n\n\tlogerrx(\"ps_cmd %lu\", offsetof(struct ps_msghdr, ps_cmd));\n\tlogerrx(\"ps_pad %lu %zu\", offsetof(struct ps_msghdr, ps_pad), sizeof(psm.ps_pad));\n\tlogerrx(\"ps_flags %lu %zu\", offsetof(struct ps_msghdr, ps_flags), sizeof(psm.ps_flags));\n\n\tlogerrx(\"ps_id %lu %zu\", offsetof(struct ps_msghdr, ps_id), sizeof(psm.ps_id));\n\n\tlogerrx(\"ps_namelen %lu %zu\", offsetof(struct ps_msghdr, ps_namelen), sizeof(psm.ps_namelen));\n\tlogerrx(\"ps_controllen %lu %zu\", offsetof(struct ps_msghdr, ps_controllen), sizeof(psm.ps_controllen));\n\tlogerrx(\"ps_pad2 %lu %zu\", offsetof(struct ps_msghdr, ps_pad2), sizeof(psm.ps_pad2));\n\tlogerrx(\"ps_datalen %lu %zu\", offsetof(struct ps_msghdr, ps_datalen), sizeof(psm.ps_datalen));\n\tlogerrx(\"psm %zu\", sizeof(psm));\n#endif\n\n\treturn ps_sendpsmmsg(ctx, fd, &psm, msg);\n}\n\nssize_t\nps_sendcmd(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags,\n    const void *data, size_t len)\n{\n\tstruct ps_msghdr psm = {\n\t\t.ps_cmd = cmd,\n\t\t.ps_flags = flags,\n\t};\n\tstruct iovec iov[] = { { .iov_base = UNCONST(data), .iov_len = len } };\n\tstruct msghdr msg = {\n\t\t.msg_iov = iov,\n\t\t.msg_iovlen = 1,\n\t};\n\n\treturn ps_sendpsmmsg(ctx, fd, &psm, &msg);\n}\n\nssize_t\nps_sendcmdmsg(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags,\n    const struct msghdr *msg)\n{\n\tstruct ps_msghdr psm = { .ps_cmd = cmd, .ps_flags = flags };\n\n\treturn ps_sendpsmmsg(ctx, fd, &psm, msg);\n}\n\nssize_t\nps_recvmsg(int rfd, unsigned short events, uint16_t cmd, int wfd)\n{\n\tstruct sockaddr_storage ss = { .ss_family = AF_UNSPEC };\n\tuint8_t controlbuf[sizeof(struct sockaddr_storage)] = { 0 };\n\tuint8_t databuf[64 * 1024];\n\tstruct iovec iov[] = { { .iov_base = databuf,\n\t    .iov_len = sizeof(databuf) } };\n\tstruct msghdr msg = {\n\t\t.msg_name = &ss,\n\t\t.msg_namelen = sizeof(ss),\n\t\t.msg_control = controlbuf,\n\t\t.msg_controllen = sizeof(controlbuf),\n\t\t.msg_iov = iov,\n\t\t.msg_iovlen = 1,\n\t};\n\tssize_t len;\n\n\tif (!(events & ELE_READ))\n\t\tlogerrx(\"%s: unexpected event 0x%04x\", __func__, events);\n\n\tlen = recvmsg(rfd, &msg, MSG_WAITALL);\n\tif (len == -1) {\n\t\tlogerr(\"%s: recvmsg\", __func__);\n\t\treturn len;\n\t}\n\n\tiov[0].iov_len = (size_t)len;\n\tlen = ps_sendcmdmsg(NULL, wfd, cmd, 0, &msg);\n\tif (len == -1)\n\t\tlogerr(\"%s: ps_sendcmdmsg\", __func__);\n\treturn len;\n}\n\nssize_t\nps_daemonised(struct dhcpcd_ctx *ctx)\n{\n\tstruct ps_process *psp;\n\tssize_t err = 0;\n\n\tdhcpcd_daemonised(ctx);\n\n\t/* Echo the message to all processes */\n\tTAILQ_FOREACH(psp, &ctx->ps_processes, next) {\n\t\tif (psp->psp_pid == getpid())\n\t\t\tcontinue;\n\t\tif (ps_sendcmd(psp->psp_ctx, psp->psp_fd, PS_DAEMONISED, 0,\n\t\t\tNULL, 0) == -1)\n\t\t\terr = -1;\n\t}\n\n\treturn err;\n}\n\nssize_t\nps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd, unsigned short events,\n    ssize_t (*callback)(void *, struct ps_msghdr *, struct msghdr *),\n    void *cbctx)\n{\n\tstruct ps_msghdr psm;\n\tuint8_t ps_data[PS_BUFLEN];\n\tssize_t len;\n\tsize_t dlen;\n\tstruct iovec iov[1];\n\tstruct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1 };\n\tbool stop = false;\n\tsocklen_t cmsg_padlen;\n\n\tif (events & ELE_HANGUP) {\n\t\tlen = 0;\n\t\tgoto stop;\n\t}\n\tif (!(events & ELE_READ))\n\t\tlogerrx(\"%s: unexpected event 0x%04x\", __func__, events);\n\n\tlen = recv(fd, &psm, sizeof(psm), MSG_WAITALL);\n#ifdef PRIVSEP_DEBUG\n\tlogdebugx(\"%s: pid=%d fd=%d len=%zd\", __func__, getpid(), fd, len);\n#endif\n\n\tif (len == -1 || len == 0)\n\t\tstop = true;\n\telse {\n\t\tdlen = (size_t)len;\n\t\tif (dlen < sizeof(psm)) {\n\t\t\terrno = EINVAL;\n\t\t\tgoto stop;\n\t\t}\n\n\t\tif (psm.ps_cmd == PS_STOP) {\n\t\t\tstop = true;\n\t\t\tlen = 0;\n\t\t} else if (psm.ps_cmd == PS_DAEMONISED) {\n\t\t\tps_daemonised(ctx);\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tif (stop) {\n\tstop:\n\t\tctx->options |= DHCPCD_EXITING;\n#ifdef PRIVSEP_DEBUG\n\t\tlogdebugx(\"process %d stopping\", getpid());\n#endif\n\t\tps_free(ctx);\n\t\teloop_exit(ctx->eloop, len != -1 ? EXIT_SUCCESS : EXIT_FAILURE);\n\t\treturn len;\n\t}\n\n\tcmsg_padlen = CALC_CMSG_PADLEN(psm.ps_controllen,\n\t    psm.ps_namelen);\n\tdlen = psm.ps_namelen + psm.ps_controllen + cmsg_padlen + psm.ps_datalen;\n\tif (dlen != 0) {\n\t\tif (dlen > sizeof(ps_data)) {\n\t\t\terrno = EMSGSIZE;\n\t\t\tgoto stop;\n\t\t}\n\t\tlen = recv(fd, ps_data, dlen, MSG_WAITALL);\n\t\tif ((size_t)len != dlen) {\n\t\t\terrno = EINVAL;\n\t\t\tgoto stop;\n\t\t}\n\t}\n\n\tif (ps_unrollmsg(&msg, &psm, ps_data, dlen) == -1)\n\t\treturn -1;\n\n\tif (callback == NULL)\n\t\treturn 0;\n\n\terrno = 0;\n\treturn callback(cbctx, &psm, &msg);\n}\n\nstruct ps_process *\nps_findprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid)\n{\n\tstruct ps_process *psp;\n\n\tTAILQ_FOREACH(psp, &ctx->ps_processes, next) {\n\t\tif (!(psp->psp_started))\n\t\t\tcontinue;\n\t\tif (memcmp(&psp->psp_id, psid, sizeof(psp->psp_id)) == 0)\n\t\t\treturn psp;\n\t}\n\terrno = ESRCH;\n\treturn NULL;\n}\n\nstruct ps_process *\nps_findprocesspid(struct dhcpcd_ctx *ctx, pid_t pid)\n{\n\tstruct ps_process *psp;\n\n\tTAILQ_FOREACH(psp, &ctx->ps_processes, next) {\n\t\tif (psp->psp_pid == pid)\n\t\t\treturn psp;\n\t}\n\terrno = ESRCH;\n\treturn NULL;\n}\n\nstruct ps_process *\nps_newprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid)\n{\n\tstruct ps_process *psp;\n\n\tpsp = calloc(1, sizeof(*psp));\n\tif (psp == NULL)\n\t\treturn NULL;\n\tpsp->psp_ctx = ctx;\n\tmemcpy(&psp->psp_id, psid, sizeof(psp->psp_id));\n\tpsp->psp_fd = -1;\n\tpsp->psp_work_fd = -1;\n#ifdef HAVE_CAPSICUM\n\tpsp->psp_pfd = -1;\n#endif\n\n\tif (!(ctx->options & DHCPCD_MANAGER))\n\t\tstrlcpy(psp->psp_ifname, ctx->ifv[0], sizeof(psp->psp_ifname));\n\tTAILQ_INSERT_TAIL(&ctx->ps_processes, psp, next);\n\treturn psp;\n}\n\nvoid\nps_freeprocesses(struct dhcpcd_ctx *ctx, struct ps_process *notthis)\n{\n\tstruct ps_process *psp, *psn;\n\n\tTAILQ_FOREACH_SAFE(psp, &ctx->ps_processes, next, psn) {\n\t\tif (psp == notthis)\n\t\t\tcontinue;\n\t\tps_freeprocess(psp);\n\t}\n}\n"
  },
  {
    "path": "src/privsep.h",
    "content": "/*\n * Privilege Separation for dhcpcd\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef PRIVSEP_H\n#define PRIVSEP_H\n\n// #define PRIVSEP_DEBUG\n\n/* Start flags */\n#define PSF_DROPPRIVS 0x01\n#define PSF_ELOOP     0x02\n\n/* Protocols */\n#define PS_BOOTP     0x0001\n#define PS_ND\t     0x0002\n#define PS_DHCP6     0x0003\n#define PS_BPF_BOOTP 0x0004\n#define PS_BPF_ARP   0x0005\n\n/* Generic commands */\n#define PS_IOCTL\t0x0010\n#define PS_ROUTE\t0x0011 /* Also used for NETLINK */\n#define PS_SCRIPT\t0x0012\n#define PS_UNLINK\t0x0013\n#define PS_READFILE\t0x0014\n#define PS_WRITEFILE\t0x0015\n#define PS_FILEMTIME\t0x0016\n#define PS_AUTH_MONORDM 0x0017\n#define PS_CTL\t\t0x0018\n#define PS_CTL_EOF\t0x0019\n#define PS_LOGREOPEN\t0x0020\n#define PS_STOPPROCS\t0x0021\n#define PS_DAEMONISED\t0x0022\n\n/* Domains */\n#define PS_ROOT\t   0x0101\n#define PS_INET\t   0x0102\n#define PS_CONTROL 0x0103\n\n/* BSD Commands */\n#define PS_IOCTLLINK\t 0x0201\n#define PS_IOCTL6\t 0x0202\n#define PS_IOCTLINDIRECT 0x0203\n#define PS_IP6FORWARDING 0x0204\n#define PS_GETIFADDRS\t 0x0205\n#define PS_IFIGNOREGRP\t 0x0206\n#define PS_SYSCTL\t 0x0207\n\n/* Dev Commands */\n#define PS_DEV_LISTENING 0x1001\n#define PS_DEV_INITTED\t 0x1002\n#define PS_DEV_IFCMD\t 0x1003\n\n/* Dev Interface Commands (via flags) */\n#define PS_DEV_IFADDED\t 0x0001\n#define PS_DEV_IFREMOVED 0x0002\n#define PS_DEV_IFUPDATED 0x0003\n\n/* Control Type (via flags) */\n#define PS_CTL_PRIV   0x0004\n#define PS_CTL_UNPRIV 0x0005\n\n/* Sysctl Needs (via flags) */\n#define PS_SYSCTL_OLEN\t0x0001\n#define PS_SYSCTL_ODATA 0x0002\n\n/* Process commands */\n#define PS_START 0x4000\n#define PS_STOP\t 0x8000\n\n/* Max INET message size + meta data for IPC */\n#define PS_BUFLEN                                                         \\\n\t((64 * 1024) + sizeof(struct ps_msghdr) + sizeof(struct msghdr) + \\\n\t    CMSG_SPACE(sizeof(struct in6_pktinfo) + sizeof(int)))\n\n#define PSP_NAMESIZE 16 + INET_MAX_ADDRSTRLEN\n\n/* Handy macro to work out if in the privsep engine or not. */\n#define IN_PRIVSEP(ctx) ((ctx)->options & DHCPCD_PRIVSEP)\n#define IN_PRIVSEP_SE(ctx) \\\n\t(((ctx)->options & (DHCPCD_PRIVSEP | DHCPCD_FORKED)) == DHCPCD_PRIVSEP)\n\n#define PS_PROCESS_TIMEOUT 5 /* seconds to stop all processes */\n\n#ifdef PRIVSEP\n#ifdef HAVE_CAPSICUM\n#define PRIVSEP_RIGHTS\n#endif\n/* Pledge and Capsicum deny nearly all sysctls.\n * Linux needs directory access to sysctls. */\n#if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE) || defined(__linux__)\n#define PRIVSEP_SYSCTL\n#endif\n#endif\n\n#define PS_ROOT_FD(ctx) ((ctx)->ps_root ? (ctx)->ps_root->psp_fd : -1)\n\n#if !defined(DISABLE_SECCOMP) && defined(__linux__)\n#include <linux/version.h>\n#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)\n#define HAVE_SECCOMP\n#endif\n#endif\n\n#include \"arp.h\"\n#include \"config.h\"\n#include \"dhcp.h\"\n#include \"dhcpcd.h\"\n\nstruct ps_addr {\n\tsa_family_t psa_family;\n\tuint8_t psa_pad[4 - sizeof(sa_family_t)];\n\tunion {\n\t\tstruct in_addr psau_in_addr;\n\t\tstruct in6_addr psau_in6_addr;\n\t} psa_u;\n#define psa_in_addr  psa_u.psau_in_addr\n#define psa_in6_addr psa_u.psau_in6_addr\n};\n\n/* Uniquely identify a process */\nstruct ps_id {\n\tstruct ps_addr psi_addr;\n\tunsigned int psi_ifindex;\n\tuint16_t psi_cmd;\n\tuint8_t psi_pad[2];\n};\n\nstruct ps_msghdr {\n\tuint16_t ps_cmd;\n\tuint8_t ps_pad[sizeof(unsigned long) - sizeof(uint16_t)];\n\tunsigned long ps_flags;\n\tstruct ps_id ps_id;\n\tsocklen_t ps_namelen;\n\tsocklen_t ps_controllen;\n\tuint8_t ps_pad2[sizeof(size_t) - sizeof(socklen_t)];\n\tsize_t ps_datalen;\n};\n\nstruct ps_msg {\n\tstruct ps_msghdr psm_hdr;\n\tuint8_t psm_data[PS_BUFLEN];\n};\n\nstruct bpf;\n\nstruct ps_process {\n\tTAILQ_ENTRY(ps_process) next;\n\tstruct dhcpcd_ctx *psp_ctx;\n\tstruct ps_id psp_id;\n\tpid_t psp_pid;\n\tint psp_fd;\n\tint psp_work_fd;\n\tunsigned int psp_ifindex;\n\tchar psp_ifname[IF_NAMESIZE];\n\tchar psp_name[PSP_NAMESIZE];\n\tuint16_t psp_proto;\n\tconst char *psp_protostr;\n\tbool psp_started;\n\n#ifdef INET\n\tint (*psp_filter)(const struct bpf *, const struct in_addr *);\n\tstruct interface psp_ifp; /* Move BPF gubbins elsewhere */\n\tstruct bpf *psp_bpf;\n#endif\n\n#ifdef HAVE_CAPSICUM\n\tint psp_pfd;\n#endif\n};\nTAILQ_HEAD(ps_process_head, ps_process);\n\n#include \"privsep-control.h\"\n#include \"privsep-inet.h\"\n#include \"privsep-root.h\"\n#ifdef INET\n#include \"privsep-bpf.h\"\n#endif\n\nint ps_init(struct dhcpcd_ctx *);\nint ps_start(struct dhcpcd_ctx *);\nint ps_stop(struct dhcpcd_ctx *);\nint ps_stopwait(struct dhcpcd_ctx *);\nint ps_entersandbox(const char *, const char **);\nint ps_managersandbox(struct dhcpcd_ctx *, const char *);\nssize_t ps_daemonised(struct dhcpcd_ctx *);\n\nint ps_unrollmsg(struct msghdr *, struct ps_msghdr *, const void *, size_t);\nssize_t ps_sendpsmmsg(struct dhcpcd_ctx *, int, struct ps_msghdr *,\n    const struct msghdr *);\nssize_t ps_sendpsmdata(struct dhcpcd_ctx *, int, struct ps_msghdr *,\n    const void *, size_t);\nssize_t ps_sendmsg(struct dhcpcd_ctx *, int, uint16_t, unsigned long,\n    const struct msghdr *);\nssize_t ps_sendcmd(struct dhcpcd_ctx *, int, uint16_t, unsigned long,\n    const void *data, size_t len);\nssize_t ps_sendcmdmsg(struct dhcpcd_ctx *, int fd, uint16_t cmd,\n    unsigned long flags, const struct msghdr *msg);\nssize_t ps_recvmsg(int, unsigned short, uint16_t, int);\nssize_t ps_recvpsmsg(struct dhcpcd_ctx *, int, unsigned short,\n    ssize_t (*callback)(void *, struct ps_msghdr *, struct msghdr *), void *);\n\n/* Internal privsep functions. */\nint ps_setbuf_fdpair(int[]);\n\n#ifdef PRIVSEP_RIGHTS\nint ps_rights_limit_ioctl(int);\nint ps_rights_limit_fd_fctnl(int);\nint ps_rights_limit_fd_rdonly(int);\nint ps_rights_limit_fd_sockopt(int);\nint ps_rights_limit_fd(int);\nint ps_rights_limit_fdpair(int[]);\n#endif\n\n#ifdef HAVE_SECCOMP\nint ps_seccomp_enter(void);\n#endif\n\npid_t ps_startprocess(struct ps_process *,\n    void (*recv_msg)(void *, unsigned short),\n    void (*recv_unpriv_msg)(void *, unsigned short),\n    int (*callback)(struct ps_process *), unsigned int);\nint ps_stopprocess(struct ps_process *);\nstruct ps_process *ps_findprocess(struct dhcpcd_ctx *, struct ps_id *);\nstruct ps_process *ps_findprocesspid(struct dhcpcd_ctx *, pid_t);\nstruct ps_process *ps_newprocess(struct dhcpcd_ctx *, struct ps_id *);\nbool ps_waitforprocs(struct dhcpcd_ctx *ctx);\nvoid ps_process_timeout(void *);\nvoid ps_freeprocess(struct ps_process *);\nvoid ps_freeprocesses(struct dhcpcd_ctx *, struct ps_process *);\n#endif\n"
  },
  {
    "path": "src/queue.h",
    "content": "/*\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2025 Roy Marples <roy@marples.name>\n */\n\n/*\n * This stub exists becuase we know a modern BSD supports all TAILQ\n * and glibc, musl et all, don't.\n */\n#if (defined(__unix__) || defined(unix)) && !defined(USG)\n#include <sys/param.h>\n#endif\n#ifdef BSD\n#include <sys/queue.h>\n/* Dragonfly BSD needs this :( */\n#if !defined(TAILQ_FOREACH_SAFE) && defined(TAILQ_FOREACH_MUTABLE)\n#define TAILQ_FOREACH_SAFE TAILQ_FOREACH_MUTABLE\n#endif\n#else\n#include \"../vendor/queue.h\"\n#endif\n"
  },
  {
    "path": "src/route.c",
    "content": "/*\n * dhcpcd - route management\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <assert.h>\n#include <ctype.h>\n#include <errno.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdlib.h>\n#include <string.h>\n#include <syslog.h>\n#include <unistd.h>\n\n#include \"common.h\"\n#include \"config.h\"\n#include \"dhcpcd.h\"\n#include \"if-options.h\"\n#include \"if.h\"\n#include \"ipv4.h\"\n#include \"ipv4ll.h\"\n#include \"ipv6.h\"\n#include \"logerr.h\"\n#include \"route.h\"\n#include \"sa.h\"\n\n/* Needed for NetBSD-6, 7 and 8. */\n#ifndef RB_TREE_FOREACH_SAFE\n#ifndef RB_TREE_PREV\n#define RB_TREE_NEXT(T, N) rb_tree_iterate((T), (N), RB_DIR_RIGHT)\n#define RB_TREE_PREV(T, N) rb_tree_iterate((T), (N), RB_DIR_LEFT)\n#endif\n#define RB_TREE_FOREACH_SAFE(N, T, S)                                        \\\n\tfor ((N) = RB_TREE_MIN(T); (N) && ((S) = RB_TREE_NEXT((T), (N)), 1); \\\n\t    (N) = (S))\n#define RB_TREE_FOREACH_REVERSE_SAFE(N, T, S)                                \\\n\tfor ((N) = RB_TREE_MAX(T); (N) && ((S) = RB_TREE_PREV((T), (N)), 1); \\\n\t    (N) = (S))\n#endif\n\n/*\n * For our purposes, RTF_CONNECTED is the same as RTF_CLONING.\n * If we change the route, we want to flush anything dynamically created.\n */\n#if defined(BSD) && !defined(RTF_CLONING) && defined(RTF_CONNECTED)\n#define RTF_CLONING RTF_CONNECTED\n#endif\n\n#ifdef RT_FREE_ROUTE_TABLE_STATS\nstatic size_t croutes;\nstatic size_t nroutes;\nstatic size_t froutes;\nstatic size_t mroutes;\n#endif\n\nstatic void\nrt_maskedaddr(struct sockaddr *dst, const struct sockaddr *addr,\n    const struct sockaddr *netmask)\n{\n\tconst char *addrp = addr->sa_data, *netmaskp = netmask->sa_data;\n\tchar *dstp = dst->sa_data;\n\tconst char *addre = (char *)dst + sa_len(addr);\n\tconst char *netmaske = (char *)dst + MIN(sa_len(addr), sa_len(netmask));\n\n\tdst->sa_family = addr->sa_family;\n#ifdef HAVE_SA_LEN\n\tdst->sa_len = addr->sa_len;\n#endif\n\n\tif (sa_is_unspecified(netmask)) {\n\t\tif (addre > dstp)\n\t\t\tmemcpy(dstp, addrp, (size_t)(addre - dstp));\n\t\treturn;\n\t}\n\n\twhile (dstp < netmaske)\n\t\t*dstp++ = *addrp++ & *netmaskp++;\n\tif (dstp < addre)\n\t\tmemset(dstp, 0, (size_t)(addre - dstp));\n}\n\n/*\n * On some systems, host routes have no need for a netmask.\n * However DHCP specifies host routes using an all-ones netmask.\n * This handy function allows easy comparison when the two\n * differ.\n */\nstatic int\nrt_cmp_netmask(const struct rt *rt1, const struct rt *rt2)\n{\n\tif (rt1->rt_flags & RTF_HOST && rt2->rt_flags & RTF_HOST)\n\t\treturn 0;\n\treturn sa_cmp(&rt1->rt_netmask, &rt2->rt_netmask);\n}\n\nint\nrt_cmp_dest(const struct rt *rt1, const struct rt *rt2)\n{\n\tunion sa_ss ma1 = { .sa.sa_family = AF_UNSPEC };\n\tunion sa_ss ma2 = { .sa.sa_family = AF_UNSPEC };\n\tint c;\n\n\trt_maskedaddr(&ma1.sa, &rt1->rt_dest, &rt1->rt_netmask);\n\trt_maskedaddr(&ma2.sa, &rt2->rt_dest, &rt2->rt_netmask);\n\tc = sa_cmp(&ma1.sa, &ma2.sa);\n\tif (c != 0)\n\t\treturn c;\n\n\treturn rt_cmp_netmask(rt1, rt2);\n}\n\nstatic int\nrt_compare_os(__unused void *context, const void *node1, const void *node2)\n{\n\tconst struct rt *rt1 = node1, *rt2 = node2;\n\tint c;\n\n\t/* Sort by masked destination. */\n\tc = rt_cmp_dest(rt1, rt2);\n\tif (c != 0)\n\t\treturn c;\n\n#ifdef HAVE_ROUTE_METRIC\n\tc = (int)(rt1->rt_ifp->metric - rt2->rt_ifp->metric);\n#endif\n\treturn c;\n}\n\nstatic int\nrt_compare_list(__unused void *context, const void *node1, const void *node2)\n{\n\tconst struct rt *rt1 = node1, *rt2 = node2;\n\n\tif (rt1->rt_order > rt2->rt_order)\n\t\treturn 1;\n\tif (rt1->rt_order < rt2->rt_order)\n\t\treturn -1;\n\treturn 0;\n}\n\nstatic int\nrt_compare_proto(void *context, const void *node1, const void *node2)\n{\n\tconst struct rt *rt1 = node1, *rt2 = node2;\n\tint c;\n\tstruct interface *ifp1, *ifp2;\n\n\tassert(rt1->rt_ifp != NULL);\n\tassert(rt2->rt_ifp != NULL);\n\tifp1 = rt1->rt_ifp;\n\tifp2 = rt2->rt_ifp;\n\n\t/* Prefer interfaces with a carrier. */\n\tc = ifp1->carrier - ifp2->carrier;\n\tif (c != 0)\n\t\treturn -c;\n\n\t/* Prefer roaming over non roaming if both carriers are down. */\n\tif (ifp1->carrier == LINK_DOWN && ifp2->carrier == LINK_DOWN) {\n\t\tbool roam1 = if_roaming(ifp1);\n\t\tbool roam2 = if_roaming(ifp2);\n\n\t\tif (roam1 != roam2)\n\t\t\treturn roam1 ? 1 : -1;\n\t}\n\n#ifdef INET\n\t/* IPv4LL routes always come last */\n\tif (rt1->rt_dflags & RTDF_IPV4LL && !(rt2->rt_dflags & RTDF_IPV4LL))\n\t\treturn -1;\n\telse if (!(rt1->rt_dflags & RTDF_IPV4LL) &&\n\t    rt2->rt_dflags & RTDF_IPV4LL)\n\t\treturn 1;\n#endif\n\n\t/* Lower metric interfaces come first. */\n\tc = (int)(ifp1->metric - ifp2->metric);\n\tif (c != 0)\n\t\treturn c;\n\n\t/* Finally the order in which the route was given to us. */\n\treturn rt_compare_list(context, rt1, rt2);\n}\n\nstatic const rb_tree_ops_t rt_compare_os_ops = {\n\t.rbto_compare_nodes = rt_compare_os,\n\t.rbto_compare_key = rt_compare_os,\n\t.rbto_node_offset = offsetof(struct rt, rt_tree),\n\t.rbto_context = NULL\n};\n\nconst rb_tree_ops_t rt_compare_list_ops = {\n\t.rbto_compare_nodes = rt_compare_list,\n\t.rbto_compare_key = rt_compare_list,\n\t.rbto_node_offset = offsetof(struct rt, rt_tree),\n\t.rbto_context = NULL\n};\n\nconst rb_tree_ops_t rt_compare_proto_ops = {\n\t.rbto_compare_nodes = rt_compare_proto,\n\t.rbto_compare_key = rt_compare_proto,\n\t.rbto_node_offset = offsetof(struct rt, rt_tree),\n\t.rbto_context = NULL\n};\n\n#ifdef RT_FREE_ROUTE_TABLE\nstatic int\nrt_compare_free(__unused void *context, const void *node1, const void *node2)\n{\n\treturn node1 == node2 ? 0 : node1 < node2 ? -1 : 1;\n}\n\nstatic const rb_tree_ops_t rt_compare_free_ops = {\n\t.rbto_compare_nodes = rt_compare_free,\n\t.rbto_compare_key = rt_compare_free,\n\t.rbto_node_offset = offsetof(struct rt, rt_tree),\n\t.rbto_context = NULL\n};\n#endif\n\nvoid\nrt_init(struct dhcpcd_ctx *ctx)\n{\n\trb_tree_init(&ctx->routes, &rt_compare_os_ops);\n#ifdef RT_FREE_ROUTE_TABLE\n\trb_tree_init(&ctx->froutes, &rt_compare_free_ops);\n#endif\n}\n\nbool\nrt_is_default(const struct rt *rt)\n{\n\treturn sa_is_unspecified(&rt->rt_dest) &&\n\t    sa_is_unspecified(&rt->rt_netmask);\n}\n\nstatic void\nrt_desc(int loglevel, const char *cmd, const struct rt *rt)\n{\n\tchar dest[INET_MAX_ADDRSTRLEN], gateway[INET_MAX_ADDRSTRLEN];\n\tint prefix;\n\tconst char *ifname;\n\tbool gateway_unspec;\n\n\tassert(cmd != NULL);\n\tassert(rt != NULL);\n\n\tsa_addrtop(&rt->rt_dest, dest, sizeof(dest));\n\tprefix = sa_toprefix(&rt->rt_netmask);\n\tsa_addrtop(&rt->rt_gateway, gateway, sizeof(gateway));\n\tgateway_unspec = sa_is_unspecified(&rt->rt_gateway);\n\tifname = rt->rt_ifp == NULL ? \"(null)\" : rt->rt_ifp->name;\n\n\tif (rt->rt_flags & RTF_HOST) {\n\t\tif (gateway_unspec)\n\t\t\tlogmessage(loglevel, \"%s: %s host route to %s\", ifname,\n\t\t\t    cmd, dest);\n\t\telse\n\t\t\tlogmessage(loglevel, \"%s: %s host route to %s via %s\",\n\t\t\t    ifname, cmd, dest, gateway);\n\t} else if (rt_is_default(rt)) {\n\t\tif (gateway_unspec)\n\t\t\tlogmessage(loglevel, \"%s: %s default route\", ifname,\n\t\t\t    cmd);\n\t\telse\n\t\t\tlogmessage(loglevel, \"%s: %s default route via %s\",\n\t\t\t    ifname, cmd, gateway);\n\t} else if (gateway_unspec)\n\t\tlogmessage(loglevel, \"%s: %s%s route to %s/%d\", ifname, cmd,\n\t\t    rt->rt_flags & RTF_REJECT ? \" reject\" : \"\", dest, prefix);\n\telse\n\t\tlogmessage(loglevel, \"%s: %s%s route to %s/%d via %s\", ifname,\n\t\t    cmd, rt->rt_flags & RTF_REJECT ? \" reject\" : \"\", dest,\n\t\t    prefix, gateway);\n}\n\nvoid\nrt_headclear0(struct dhcpcd_ctx *ctx, rb_tree_t *rts, int af)\n{\n\tstruct rt *rt, *rtn;\n\n\tif (rts == NULL)\n\t\treturn;\n#ifdef RT_FREE_ROUTE_TABLE\n\tif (ctx != NULL)\n\t\tassert(&ctx->froutes != rts);\n#endif\n\n\tRB_TREE_FOREACH_SAFE(rt, rts, rtn)\n\t{\n\t\tif (af != AF_UNSPEC && rt->rt_dest.sa_family != af &&\n\t\t    rt->rt_gateway.sa_family != af)\n\t\t\tcontinue;\n\t\trb_tree_remove_node(rts, rt);\n\t\trt_free(rt);\n\t}\n}\n\nvoid\nrt_headclear(rb_tree_t *rts, int af)\n{\n\tstruct rt *rt;\n\n\tif (rts == NULL || (rt = RB_TREE_MIN(rts)) == NULL)\n\t\treturn;\n\trt_headclear0(rt->rt_ifp ? rt->rt_ifp->ctx : NULL, rts, af);\n}\n\nstatic void\nrt_headfree(rb_tree_t *rts)\n{\n\tstruct rt *rt;\n\n\twhile ((rt = RB_TREE_MIN(rts)) != NULL) {\n\t\trb_tree_remove_node(rts, rt);\n\t\tfree(rt);\n\t}\n}\n\nvoid\nrt_dispose(struct dhcpcd_ctx *ctx)\n{\n\tassert(ctx != NULL);\n\trt_headfree(&ctx->routes);\n#ifdef RT_FREE_ROUTE_TABLE\n\trt_headfree(&ctx->froutes);\n#ifdef RT_FREE_ROUTE_TABLE_STATS\n\tlogdebugx(\"free route list used %zu times\", froutes);\n\tlogdebugx(\"new routes from route free list %zu\", nroutes);\n\tlogdebugx(\"maximum route free list size %zu\", mroutes);\n#endif\n#endif\n}\n\nstruct rt *\nrt_new0(struct dhcpcd_ctx *ctx)\n{\n\tstruct rt *rt;\n\n\tassert(ctx != NULL);\n#ifdef RT_FREE_ROUTE_TABLE\n\tif ((rt = RB_TREE_MIN(&ctx->froutes)) != NULL) {\n\t\trb_tree_remove_node(&ctx->froutes, rt);\n#ifdef RT_FREE_ROUTE_TABLE_STATS\n\t\tcroutes--;\n\t\tnroutes++;\n#endif\n\t} else\n#endif\n\t    if ((rt = malloc(sizeof(*rt))) == NULL) {\n\t\tlogerr(__func__);\n\t\treturn NULL;\n\t}\n\tmemset(rt, 0, sizeof(*rt));\n\treturn rt;\n}\n\nvoid\nrt_setif(struct rt *rt, struct interface *ifp)\n{\n\tassert(rt != NULL);\n\tassert(ifp != NULL);\n\trt->rt_ifp = ifp;\n#ifdef HAVE_ROUTE_METRIC\n\trt->rt_metric = ifp->metric;\n\tif (if_roaming(ifp))\n\t\trt->rt_metric += RTMETRIC_ROAM;\n#endif\n}\n\nstruct rt *\nrt_new(struct interface *ifp)\n{\n\tstruct rt *rt;\n\n\tassert(ifp != NULL);\n\tif ((rt = rt_new0(ifp->ctx)) == NULL)\n\t\treturn NULL;\n\trt_setif(rt, ifp);\n\treturn rt;\n}\n\nstruct rt *\nrt_proto_add_ctx(rb_tree_t *tree, struct rt *rt, struct dhcpcd_ctx *ctx)\n{\n\trt->rt_order = ctx->rt_order++;\n\tif (rb_tree_insert_node(tree, rt) == rt)\n\t\treturn rt;\n\n\trt_free(rt);\n\terrno = EEXIST;\n\treturn NULL;\n}\n\nstruct rt *\nrt_proto_add(rb_tree_t *tree, struct rt *rt)\n{\n\tassert(rt->rt_ifp != NULL);\n\treturn rt_proto_add_ctx(tree, rt, rt->rt_ifp->ctx);\n}\n\nvoid\nrt_free(struct rt *rt)\n{\n#ifdef RT_FREE_ROUTE_TABLE\n\tstruct dhcpcd_ctx *ctx;\n\n\tassert(rt != NULL);\n\tif (rt->rt_ifp == NULL) {\n\t\tfree(rt);\n\t\treturn;\n\t}\n\n\tctx = rt->rt_ifp->ctx;\n\trb_tree_insert_node(&ctx->froutes, rt);\n#ifdef RT_FREE_ROUTE_TABLE_STATS\n\tcroutes++;\n\tfroutes++;\n\tif (croutes > mroutes)\n\t\tmroutes = croutes;\n#endif\n#else\n\tfree(rt);\n#endif\n}\n\nvoid\nrt_freeif(struct interface *ifp)\n{\n\tstruct dhcpcd_ctx *ctx;\n\tstruct rt *rt, *rtn;\n\n\tif (ifp == NULL)\n\t\treturn;\n\tctx = ifp->ctx;\n\tRB_TREE_FOREACH_SAFE(rt, &ctx->routes, rtn)\n\t{\n\t\tif (rt->rt_ifp == ifp) {\n\t\t\trb_tree_remove_node(&ctx->routes, rt);\n\t\t\trt_free(rt);\n\t\t}\n\t}\n}\n\n/* If something other than dhcpcd removes a route,\n * we need to remove it from our internal table. */\nvoid\nrt_recvrt(int cmd, const struct rt *rt, pid_t pid)\n{\n\tstruct dhcpcd_ctx *ctx;\n\tstruct rt *f;\n\n\tassert(rt != NULL);\n\tassert(rt->rt_ifp != NULL);\n\tassert(rt->rt_ifp->ctx != NULL);\n\n\tctx = rt->rt_ifp->ctx;\n\n\tswitch (cmd) {\n\tcase RTM_DELETE:\n\t\tf = rb_tree_find_node(&ctx->routes, rt);\n\t\tif (f != NULL) {\n\t\t\tchar buf[32];\n\n\t\t\trb_tree_remove_node(&ctx->routes, f);\n\t\t\tsnprintf(buf, sizeof(buf), \"pid %d deleted\", (int)pid);\n\t\t\trt_desc(LOG_WARNING, buf, f);\n\t\t\trt_free(f);\n\t\t}\n\t\tbreak;\n\t}\n\n#if defined(IPV4LL) && defined(HAVE_ROUTE_METRIC)\n\tif (rt->rt_dest.sa_family == AF_INET)\n\t\tipv4ll_recvrt(cmd, rt);\n#endif\n}\n\n/* Compare miscellaneous route details */\nstatic int\nrt_cmp_mtu(struct rt *nrt, struct rt *ort)\n{\n#if defined(__FreeBSD__) || defined(__DragonFly__)\n\t/* FreeBSD puts the interface MTU into the route MTU\n\t * if the route does not define it's own. */\n\tunsigned int nmtu, omtu;\n\n\tnmtu = nrt->rt_mtu ? nrt->rt_mtu : (unsigned int)nrt->rt_ifp->mtu;\n\tomtu = ort->rt_mtu ? ort->rt_mtu : (unsigned int)ort->rt_ifp->mtu;\n\tif (omtu != nmtu)\n\t\treturn 1;\n#else\n\tif (ort->rt_mtu != nrt->rt_mtu)\n\t\treturn 1;\n#endif\n\n\treturn 0;\n}\n\n#ifdef HAVE_ROUTE_LIFETIME\nstatic int\nrt_cmp_lifetime(struct rt *nrt, struct rt *ort)\n{\n\t/* There might be a minor difference between kernel route\n\t * lifetime and our lifetime due to processing times.\n\t * We allow a small deviation to avoid needless route changes.\n\t * dhcpcd will expire the route regardless of route lifetime support.\n\t */\n\tstruct timespec ts;\n\tuint32_t deviation;\n\n\ttimespecsub(&nrt->rt_aquired, &ort->rt_aquired, &ts);\n\tif (ts.tv_sec < 0)\n\t\tts.tv_sec = -ts.tv_sec;\n\tif (ts.tv_sec > RTLIFETIME_DEV_MAX)\n\t\treturn 1;\n\tif (nrt->rt_lifetime > ort->rt_lifetime)\n\t\tdeviation = nrt->rt_lifetime - ort->rt_lifetime;\n\telse\n\t\tdeviation = ort->rt_lifetime - nrt->rt_lifetime;\n\tif (deviation > RTLIFETIME_DEV_MAX)\n\t\treturn 1;\n\n\treturn 0;\n}\n#endif\n\nstatic bool\nrt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)\n{\n\tstruct dhcpcd_ctx *ctx;\n\tstruct rt *krt;\n\tint loglevel = LOG_INFO;\n\tbool change, result = false;\n\n\tassert(nrt != NULL);\n\tctx = nrt->rt_ifp->ctx;\n\n\t/*\n\t * Don't install a gateway if not asked to.\n\t * This option is mainly for VPN users who want their VPN to be the\n\t * default route.\n\t * Because VPN's generally don't care about route management\n\t * beyond their own, a longer term solution would be to remove this\n\t * and get the VPN to inject the default route into dhcpcd somehow.\n\t */\n\tif (((nrt->rt_ifp->active &&\n\t\t !(nrt->rt_ifp->options->options & DHCPCD_GATEWAY)) ||\n\t\t(!nrt->rt_ifp->active && !(ctx->options & DHCPCD_GATEWAY))) &&\n\t    sa_is_unspecified(&nrt->rt_dest) &&\n\t    sa_is_unspecified(&nrt->rt_netmask))\n\t\treturn false;\n\n\tkrt = rb_tree_find_node(kroutes, nrt);\n\tif (krt != NULL && krt->rt_ifp == nrt->rt_ifp &&\n\t    /* Only test flags dhcpcd controls */\n\t    (krt->rt_flags & (RTF_HOST | RTF_REJECT)) == nrt->rt_flags &&\n#ifdef HAVE_ROUTE_METRIC\n\t    krt->rt_metric == nrt->rt_metric &&\n#endif\n\t    sa_cmp(&krt->rt_dest, &nrt->rt_dest) == 0 &&\n\t    rt_cmp_netmask(krt, nrt) == 0 &&\n\t    sa_cmp(&krt->rt_gateway, &nrt->rt_gateway) == 0 &&\n\t    (nrt->rt_ifp->flags & IFF_LOOPBACK || rt_cmp_mtu(krt, nrt) == 0)) {\n#ifdef HAVE_ROUTE_LIFETIME\n\t\tif (rt_cmp_lifetime(krt, nrt) == 0) {\n\t\t\trt_desc(LOG_DEBUG, \"keeping\", krt);\n\t\t\treturn true;\n\t\t} else\n\t\t\tloglevel = LOG_DEBUG;\n#else\n\t\trt_desc(LOG_DEBUG, \"keeping\", krt);\n\t\treturn true;\n#endif\n\t}\n\n\trt_desc(loglevel, ort == NULL ? \"adding\" : \"changing\", nrt);\n\n\tchange = krt != NULL;\n#ifdef RTF_CLONING\n\t/* BSD can set routes to be cloning routes.\n\t * Cloned routes inherit the parent flags.\n\t * As such, we need to delete and re-add the route to flush children\n\t * to correct the flags. */\n\tif (change && krt != NULL && krt->rt_flags & RTF_CLONING)\n\t\tchange = false;\n#endif\n\t/* Reject routes have a gateway, non reject routes don't.\n\t * BSD kernels at least preserve RTF_GATEWAY so we need to punt it. */\n\tif (change && krt->rt_flags & RTF_REJECT &&\n\t    !(nrt->rt_flags & RTF_REJECT))\n\t\tchange = false;\n\n\tif (change) {\n\t\tif (if_route(RTM_CHANGE, nrt) != -1) {\n\t\t\tresult = true;\n\t\t\tgoto out;\n\t\t}\n\t\tif (errno != ESRCH)\n\t\t\tlogerr(\"if_route (CHG)\");\n\t}\n\n#ifdef HAVE_ROUTE_METRIC\n\t/* With route metrics, we can safely add the new route before\n\t * deleting the old route. */\n\tif (if_route(RTM_ADD, nrt) != -1) {\n\t\tif (krt != NULL) {\n\t\t\tif (if_route(RTM_DELETE, krt) == -1 && errno != ESRCH)\n\t\t\t\tlogerr(\"if_route (DEL)\");\n\t\t}\n\t\tresult = true;\n\t\tgoto out;\n\t}\n\n\t/* If the kernel claims the route exists we need to rip out the\n\t * old one first. */\n\tif (errno != EEXIST || ort == NULL)\n\t\tgoto logerr;\n#endif\n\n\t/* No route metrics, we need to delete the old route before\n\t * adding the new one. */\n#ifdef ROUTE_PER_GATEWAY\n\terrno = 0;\n#endif\n\tif (krt != NULL) {\n\t\tif (if_route(RTM_DELETE, krt) == -1 && errno != ESRCH)\n\t\t\tlogerr(\"if_route (DEL)\");\n\t}\n#ifdef ROUTE_PER_GATEWAY\n\t/* The OS allows many routes to the same dest with different gateways.\n\t * dhcpcd does not support this yet, so for the time being just keep on\n\t * deleting the route until there is an error. */\n\tif (krt != NULL && errno == 0) {\n\t\tfor (;;) {\n\t\t\tif (if_route(RTM_DELETE, krt) == -1)\n\t\t\t\tbreak;\n\t\t}\n\t}\n#endif\n\n\t/* Shouldn't need to check for EEXIST, but some kernels don't\n\t * dump the subnet route just after we added the address. */\n\tif (if_route(RTM_ADD, nrt) != -1 || errno == EEXIST) {\n\t\tresult = true;\n\t\tgoto out;\n\t}\n\n#ifdef HAVE_ROUTE_METRIC\nlogerr:\n#endif\n\tlogerr(\"if_route (ADD)\");\n\nout:\n\tif (krt != NULL) {\n\t\trb_tree_remove_node(kroutes, krt);\n\t\trt_free(krt);\n\t}\n\treturn result;\n}\n\nstatic bool\nrt_delete(struct rt *rt)\n{\n\tint retval;\n\n\trt_desc(LOG_INFO, \"deleting\", rt);\n\tretval = if_route(RTM_DELETE, rt) == -1 ? false : true;\n\tif (!retval && errno != ENOENT && errno != ESRCH)\n\t\tlogerr(__func__);\n\treturn retval;\n}\n\nstatic int\nrt_cmp(const struct rt *r1, const struct rt *r2)\n{\n\tif (r1->rt_ifp == r2->rt_ifp &&\n#ifdef HAVE_ROUTE_METRIC\n\t    r1->rt_metric == r2->rt_metric &&\n#endif\n\t    sa_cmp(&r1->rt_gateway, &r2->rt_gateway) == 0)\n\t\treturn 0;\n\treturn 1;\n}\n\nstatic bool\nrt_doroute(rb_tree_t *kroutes, struct rt *rt)\n{\n\tstruct dhcpcd_ctx *ctx;\n\tstruct rt * or ;\n\n\tctx = rt->rt_ifp->ctx;\n\t/* Do we already manage it? */\n\tor = rb_tree_find_node(&ctx->routes, rt);\n\tif (or != NULL) {\n\t\tif (rt->rt_dflags & RTDF_FAKE)\n\t\t\treturn true;\n\t\tif (or->rt_dflags & RTDF_FAKE || rt_cmp(rt, or) != 0 ||\n\t\t    (rt->rt_ifa.sa_family != AF_UNSPEC &&\n\t\t\tsa_cmp(&or->rt_ifa, &rt->rt_ifa) != 0) ||\n#ifdef HAVE_ROUTE_LIFETIME\n\t\t    rt_cmp_lifetime(rt, or) != 0 ||\n#endif\n\t\t    rt_cmp_mtu(rt, or) != 0) {\n\t\t\tif (!rt_add(kroutes, rt, or))\n\t\t\t\treturn false;\n\t\t}\n\t\trb_tree_remove_node(&ctx->routes, or);\n\t\trt_free(or);\n\t} else {\n\t\tif (rt->rt_dflags & RTDF_FAKE) {\n\t\t\tor = rb_tree_find_node(kroutes, rt);\n\t\t\tif (or == NULL)\n\t\t\t\treturn false;\n\t\t\tif (rt_cmp(rt, or) == 0)\n\t\t\t\treturn false;\n\t\t} else {\n\t\t\tif (!rt_add(kroutes, rt, NULL))\n\t\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nvoid\nrt_build(struct dhcpcd_ctx *ctx, int af)\n{\n\trb_tree_t routes, added, kroutes;\n\tstruct rt *rt, *rtn;\n\tunsigned long long o;\n\n\t/* When exiting with persistence, don't change any routing\n\t * which maybe affected by interfaces stopping. */\n\tif ((ctx->options & (DHCPCD_EXITING | DHCPCD_PERSISTENT)) ==\n\t    (DHCPCD_EXITING | DHCPCD_PERSISTENT))\n\t\treturn;\n\n\trb_tree_init(&routes, &rt_compare_proto_ops);\n\trb_tree_init(&added, &rt_compare_os_ops);\n\trb_tree_init(&kroutes, &rt_compare_os_ops);\n\tif (if_initrt(ctx, &kroutes, af) != 0)\n\t\tlogerr(\"%s: if_initrt\", __func__);\n\tctx->rt_order = 0;\n\tctx->options |= DHCPCD_RTBUILD;\n\n#ifdef INET\n\tif (!inet_getroutes(ctx, &routes))\n\t\tgoto getfail;\n#endif\n#ifdef INET6\n\tif (!inet6_getroutes(ctx, &routes))\n\t\tgoto getfail;\n#endif\n\n#ifdef BSD\n\t/* Rewind the miss filter */\n\tctx->rt_missfilterlen = 0;\n#endif\n\n\tRB_TREE_FOREACH_SAFE(rt, &routes, rtn)\n\t{\n\t\tif (rt->rt_ifp->active) {\n\t\t\tif (!(rt->rt_ifp->options->options & DHCPCD_CONFIGURE))\n\t\t\t\tcontinue;\n\t\t} else if (!(ctx->options & DHCPCD_CONFIGURE))\n\t\t\tcontinue;\n#ifdef BSD\n\t\tif (rt_is_default(rt) &&\n\t\t    if_missfilter(rt->rt_ifp, &rt->rt_gateway) == -1)\n\t\t\tlogerr(\"if_missfilter\");\n#endif\n\t\tif ((rt->rt_dest.sa_family != af &&\n\t\t\trt->rt_dest.sa_family != AF_UNSPEC) ||\n\t\t    (rt->rt_gateway.sa_family != af &&\n\t\t\trt->rt_gateway.sa_family != AF_UNSPEC))\n\t\t\tcontinue;\n\t\t/* Is this route already in our table? */\n\t\tif (rb_tree_find_node(&added, rt) != NULL)\n\t\t\tcontinue;\n\t\tif (rt_doroute(&kroutes, rt)) {\n\t\t\trb_tree_remove_node(&routes, rt);\n\t\t\tif (rb_tree_insert_node(&added, rt) != rt) {\n\t\t\t\terrno = EEXIST;\n\t\t\t\tlogerr(__func__);\n\t\t\t\trt_free(rt);\n\t\t\t}\n\t\t}\n\t}\n\n#ifdef BSD\n\tif (!(ctx->options & DHCPCD_EXITING) &&\n\t    if_missfilter_apply(ctx) == -1 && errno != ENOTSUP)\n\t\tlogerr(\"if_missfilter_apply\");\n#endif\n\n\t/* Remove old routes we used to manage. */\n\tRB_TREE_FOREACH_REVERSE_SAFE(rt, &ctx->routes, rtn)\n\t{\n\t\tif ((rt->rt_dest.sa_family != af &&\n\t\t\trt->rt_dest.sa_family != AF_UNSPEC) ||\n\t\t    (rt->rt_gateway.sa_family != af &&\n\t\t\trt->rt_gateway.sa_family != AF_UNSPEC))\n\t\t\tcontinue;\n\t\trb_tree_remove_node(&ctx->routes, rt);\n\t\tif (rb_tree_find_node(&added, rt) == NULL) {\n\t\t\to = rt->rt_ifp->options ? rt->rt_ifp->options->options :\n\t\t\t\t\t\t  ctx->options;\n\t\t\tif ((o & (DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=\n\t\t\t    (DHCPCD_EXITING | DHCPCD_PERSISTENT))\n\t\t\t\trt_delete(rt);\n\t\t}\n\t\trt_free(rt);\n\t}\n\n\t/* XXX This needs to be optimised. */\n\twhile ((rt = RB_TREE_MIN(&added)) != NULL) {\n\t\trb_tree_remove_node(&added, rt);\n\t\tif (rb_tree_insert_node(&ctx->routes, rt) != rt) {\n\t\t\terrno = EEXIST;\n\t\t\tlogerr(__func__);\n\t\t\trt_free(rt);\n\t\t}\n\t}\n\ngetfail:\n\trt_headclear(&routes, AF_UNSPEC);\n\trt_headclear(&kroutes, AF_UNSPEC);\n}\n"
  },
  {
    "path": "src/route.h",
    "content": "/*\n * dhcpcd - route management\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * rEDISTRIBUTION AND USE IN SOURCE AND BINARY FORMS, WITH OR WITHOUT\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef ROUTE_H\n#define ROUTE_H\n\n#ifdef HAVE_SYS_RBTREE_H\n#include <sys/rbtree.h>\n#else\n#include \"rbtree.h\"\n#endif\n\n#include <sys/socket.h>\n\n#include <net/route.h>\n\n#include <stdbool.h>\n\n#include \"dhcpcd.h\"\n#include \"sa.h\"\n\n/*\n * Enable the route free list by default as\n * memory usage is still reported as low/unchanged even\n * when dealing with millions of routes.\n */\n#if !defined(RT_FREE_ROUTE_TABLE)\n#define RT_FREE_ROUTE_TABLE 1\n#elif RT_FREE_ROUTE_TABLE == 0\n#undef RT_FREE_ROUTE_TABLE\n#endif\n\n/* Some systems have route metrics.\n * OpenBSD route priority is not this. */\n#ifndef HAVE_ROUTE_METRIC\n#if defined(__linux__)\n#define HAVE_ROUTE_METRIC 1\n#endif\n#endif\n\n#ifdef __linux__\n#include <linux/version.h> /* RTA_PREF is only an enum.... */\n#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)\n#define HAVE_ROUTE_PREF\n#endif\n#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)\n#define HAVE_ROUTE_LIFETIME /* For IPv6 routes only */\n#endif\n#endif\n\n#if defined(__OpenBSD__) || defined(__sun)\n#define ROUTE_PER_GATEWAY\n/* XXX dhcpcd doesn't really support this yet.\n * But that's generally OK if only dhcpcd is managing routes. */\n#endif\n\n/* OpenBSD defines this as a \"convienience\" ..... we work around it. */\n#ifdef __OpenBSD__\n#undef rt_mtu\n#endif\n\nstruct rt {\n\tunion sa_ss rt_ss_dest;\n#define rt_dest rt_ss_dest.sa\n\tunion sa_ss rt_ss_netmask;\n#define rt_netmask rt_ss_netmask.sa\n\tunion sa_ss rt_ss_gateway;\n#define rt_gateway rt_ss_gateway.sa\n\tstruct interface *rt_ifp;\n\tunion sa_ss rt_ss_ifa;\n#define rt_ifa rt_ss_ifa.sa\n\tunsigned int rt_flags;\n\tunsigned int rt_mtu;\n#ifdef HAVE_ROUTE_METRIC\n\tunsigned int rt_metric;\n#endif\n/* Maximum interface index is generally USHORT_MAX or 65535.\n * Add some padding for other stuff and we get offsets for the\n * below that should work automatically.\n * This is only an issue if the user defines higher metrics in\n * their configuration, but then they might wish to override also. */\n#define RTMETRIC_BASE\t  1000U\n#define RTMETRIC_WIRELESS 2000U\n#define RTMETRIC_IPV4LL\t  1000000U\n#define RTMETRIC_ROAM\t  2000000U\n#ifdef HAVE_ROUTE_PREF\n\tint rt_pref;\n#endif\n#define RTPREF_HIGH\t1\n#define RTPREF_MEDIUM\t0 /* has to be zero */\n#define RTPREF_LOW\t(-1)\n#define RTPREF_RESERVED (-2)\n#define RTPREF_INVALID\t(-3) /* internal */\n\tunsigned int rt_dflags;\n#define RTDF_IFA_ROUTE 0x01 /* Address generated route */\n#define RTDF_FAKE      0x02 /* Maybe us on lease reboot */\n#define RTDF_IPV4LL    0x04 /* IPv4LL route */\n#define RTDF_RA\t       0x08 /* Router Advertisement */\n#define RTDF_DHCP      0x10 /* DHCP route */\n#define RTDF_STATIC    0x20 /* Configured in dhcpcd */\n#define RTDF_GATELINK  0x40 /* Gateway is on link */\n\tsize_t rt_order;\n\trb_node_t rt_tree;\n#ifdef HAVE_ROUTE_LIFETIME\n\tstruct timespec rt_aquired; /* timestamp of aquisition */\n\tuint32_t rt_lifetime;\t    /* current lifetime of route */\n#define RTLIFETIME_DEV_MAX 2\t    /* max deviation for cmp */\n#endif\n};\n\nextern const rb_tree_ops_t rt_compare_list_ops;\nextern const rb_tree_ops_t rt_compare_proto_ops;\n\nvoid rt_init(struct dhcpcd_ctx *);\nvoid rt_dispose(struct dhcpcd_ctx *);\nvoid rt_free(struct rt *);\nvoid rt_freeif(struct interface *);\nbool rt_is_default(const struct rt *);\nvoid rt_headclear0(struct dhcpcd_ctx *, rb_tree_t *, int);\nvoid rt_headclear(rb_tree_t *, int);\nvoid rt_headfreeif(rb_tree_t *);\nstruct rt *rt_new0(struct dhcpcd_ctx *);\nvoid rt_setif(struct rt *, struct interface *);\nstruct rt *rt_new(struct interface *);\nstruct rt *rt_proto_add_ctx(rb_tree_t *, struct rt *, struct dhcpcd_ctx *);\nstruct rt *rt_proto_add(rb_tree_t *, struct rt *);\nint rt_cmp_dest(const struct rt *, const struct rt *);\nvoid rt_recvrt(int, const struct rt *, pid_t);\nvoid rt_build(struct dhcpcd_ctx *, int);\n\n#endif\n"
  },
  {
    "path": "src/sa.c",
    "content": "/*\n * Socket Address handling for dhcpcd\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2015-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/types.h>\n#include <sys/socket.h>\n\n#include <arpa/inet.h>\n#ifdef AF_LINK\n#include <net/if_dl.h>\n#elif defined(AF_PACKET)\n#include <linux/if_packet.h>\n#endif\n\n#include <assert.h>\n#include <errno.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"common.h\"\n#include \"config.h\"\n#include \"sa.h\"\n\n#ifndef NDEBUG\nstatic bool sa_inprefix;\n#endif\n\nsocklen_t\nsa_addroffset(const struct sockaddr *sa)\n{\n\tassert(sa != NULL);\n\tswitch (sa->sa_family) {\n#ifdef INET\n\tcase AF_INET:\n\t\treturn offsetof(struct sockaddr_in, sin_addr) +\n\t\t    offsetof(struct in_addr, s_addr);\n#endif /* INET */\n#ifdef INET6\n\tcase AF_INET6:\n\t\treturn offsetof(struct sockaddr_in6, sin6_addr) +\n\t\t    offsetof(struct in6_addr, s6_addr);\n#endif /* INET6 */\n\tdefault:\n\t\terrno = EAFNOSUPPORT;\n\t\treturn 0;\n\t}\n}\n\nsocklen_t\nsa_addrlen(const struct sockaddr *sa)\n{\n#define membersize(type, member) sizeof(((type *)0)->member)\n\tassert(sa != NULL);\n\tswitch (sa->sa_family) {\n#ifdef INET\n\tcase AF_INET:\n\t\treturn membersize(struct in_addr, s_addr);\n#endif /* INET */\n#ifdef INET6\n\tcase AF_INET6:\n\t\treturn membersize(struct in6_addr, s6_addr);\n#endif /* INET6 */\n\tdefault:\n\t\terrno = EAFNOSUPPORT;\n\t\treturn 0;\n\t}\n}\n\n#ifndef HAVE_SA_LEN\nsocklen_t\nsa_len(const struct sockaddr *sa)\n{\n\tswitch (sa->sa_family) {\n#ifdef AF_LINK\n\tcase AF_LINK:\n\t\treturn sizeof(struct sockaddr_dl);\n#endif\n#ifdef AF_PACKET\n\tcase AF_PACKET:\n\t\treturn sizeof(struct sockaddr_ll);\n#endif\n\tcase AF_INET:\n\t\treturn sizeof(struct sockaddr_in);\n\tcase AF_INET6:\n\t\treturn sizeof(struct sockaddr_in6);\n\tdefault:\n\t\treturn sizeof(struct sockaddr);\n\t}\n}\n#endif\n\nbool\nsa_is_unspecified(const struct sockaddr *sa)\n{\n\tassert(sa != NULL);\n\tswitch (sa->sa_family) {\n\tcase AF_UNSPEC:\n\t\treturn true;\n#ifdef INET\n\tcase AF_INET:\n\t\treturn satocsin(sa)->sin_addr.s_addr == INADDR_ANY;\n#endif /* INET */\n#ifdef INET6\n\tcase AF_INET6:\n\t\treturn IN6_IS_ADDR_UNSPECIFIED(&satocsin6(sa)->sin6_addr);\n#endif /* INET6 */\n\tdefault:\n\t\terrno = EAFNOSUPPORT;\n\t\treturn false;\n\t}\n}\n\n#ifdef INET6\n#ifndef IN6MASK128\n#define IN6MASK128                                                       \\\n\t{                                                                \\\n\t\t{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \\\n\t\t       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }} \\\n\t}\n#endif\nstatic const struct in6_addr in6allones = IN6MASK128;\n#endif\n\nbool\nsa_is_allones(const struct sockaddr *sa)\n{\n\tassert(sa != NULL);\n\tswitch (sa->sa_family) {\n\tcase AF_UNSPEC:\n\t\treturn false;\n#ifdef INET\n\tcase AF_INET: {\n\t\tconst struct sockaddr_in *sin;\n\n\t\tsin = satocsin(sa);\n\t\treturn sin->sin_addr.s_addr == INADDR_BROADCAST;\n\t}\n#endif /* INET */\n#ifdef INET6\n\tcase AF_INET6: {\n\t\tconst struct sockaddr_in6 *sin6;\n\n\t\tsin6 = satocsin6(sa);\n\t\treturn IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &in6allones);\n\t}\n#endif /* INET6 */\n\tdefault:\n\t\terrno = EAFNOSUPPORT;\n\t\treturn false;\n\t}\n}\n\nbool\nsa_is_loopback(const struct sockaddr *sa)\n{\n\tassert(sa != NULL);\n\tswitch (sa->sa_family) {\n\tcase AF_UNSPEC:\n\t\treturn false;\n#ifdef INET\n\tcase AF_INET: {\n\t\tconst struct sockaddr_in *sin;\n\n\t\tsin = satocsin(sa);\n\t\treturn sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK);\n\t}\n#endif /* INET */\n#ifdef INET6\n\tcase AF_INET6: {\n\t\tconst struct sockaddr_in6 *sin6;\n\n\t\tsin6 = satocsin6(sa);\n\t\treturn IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr);\n\t}\n#endif /* INET6 */\n\tdefault:\n\t\terrno = EAFNOSUPPORT;\n\t\treturn false;\n\t}\n}\n\nint\nsa_toprefix(const struct sockaddr *sa)\n{\n\tint prefix;\n\n\tassert(sa != NULL);\n\tswitch (sa->sa_family) {\n#ifdef INET\n\tcase AF_INET: {\n\t\tconst struct sockaddr_in *sin;\n\t\tuint32_t mask;\n\n\t\tsin = satocsin(sa);\n\t\tif (sin->sin_addr.s_addr == INADDR_ANY) {\n\t\t\tprefix = 0;\n\t\t\tbreak;\n\t\t}\n\t\tmask = ntohl(sin->sin_addr.s_addr);\n\t\tprefix = 33 - ffs((int)mask); /* 33 - (1 .. 32) -> 32 .. 1 */\n\t\tif (prefix < 32) {\t      /* more than 1 bit in mask */\n\t\t\t/* check for non-contig netmask */\n\t\t\tif ((mask ^ (((1U << prefix) - 1) << (32 - prefix))) !=\n\t\t\t    0) {\n\t\t\t\terrno = EINVAL;\n\t\t\t\treturn -1; /* noncontig, no pfxlen */\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n#endif\n#ifdef INET6\n\tcase AF_INET6: {\n\t\tconst struct sockaddr_in6 *sin6;\n\t\tint x, y;\n\t\tconst uint8_t *lim, *p;\n\n\t\tsin6 = satocsin6(sa);\n\t\tp = (const uint8_t *)sin6->sin6_addr.s6_addr;\n\t\tlim = p + sizeof(sin6->sin6_addr.s6_addr);\n\t\tfor (x = 0; p < lim; x++, p++) {\n\t\t\tif (*p != 0xff)\n\t\t\t\tbreak;\n\t\t}\n\t\ty = 0;\n\t\tif (p < lim) {\n\t\t\tfor (y = 0; y < NBBY; y++) {\n\t\t\t\tif ((*p & (0x80 >> y)) == 0)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * when the limit pointer is given, do a stricter check on the\n\t\t * remaining bits.\n\t\t */\n\t\tif (p < lim) {\n\t\t\tif (y != 0 && (*p & (0x00ff >> y)) != 0)\n\t\t\t\treturn 0;\n\t\t\tfor (p = p + 1; p < lim; p++)\n\t\t\t\tif (*p != 0)\n\t\t\t\t\treturn 0;\n\t\t}\n\n\t\tprefix = x * NBBY + y;\n\t\tbreak;\n\t}\n#endif\n\tdefault:\n\t\terrno = EAFNOSUPPORT;\n\t\treturn -1;\n\t}\n\n#ifndef NDEBUG\n\t/* Ensure the calculation is correct */\n\tif (!sa_inprefix) {\n\t\tunion sa_ss ss = { .sa = { .sa_family = sa->sa_family } };\n\n\t\tsa_inprefix = true;\n\t\tsa_fromprefix(&ss.sa, prefix);\n\t\tassert(sa_cmp(sa, &ss.sa) == 0);\n\t\tsa_inprefix = false;\n\t}\n#endif\n\n\treturn prefix;\n}\n\nstatic void\nipbytes_fromprefix(uint8_t *ap, int prefix, int max_prefix)\n{\n\tint bytes, bits, i;\n\n\tbytes = prefix / NBBY;\n\tbits = prefix % NBBY;\n\n\tfor (i = 0; i < bytes; i++)\n\t\t*ap++ = 0xff;\n\tif (bits) {\n\t\tuint8_t a;\n\n\t\ta = 0xff;\n\t\ta = (uint8_t)(a << (8 - bits));\n\t\t*ap++ = a;\n\t}\n\tbytes = (max_prefix - prefix) / NBBY;\n\tfor (i = 0; i < bytes; i++)\n\t\t*ap++ = 0x00;\n}\n\nvoid\nin6_addr_fromprefix(struct in6_addr *addr, int prefix)\n{\n\tipbytes_fromprefix((uint8_t *)addr, prefix, 128);\n}\n\nint\nsa_fromprefix(struct sockaddr *sa, int prefix)\n{\n\tuint8_t *ap;\n\tint max_prefix;\n\n\tswitch (sa->sa_family) {\n#ifdef INET\n\tcase AF_INET:\n\t\tmax_prefix = 32;\n#ifdef HAVE_SA_LEN\n\t\tsa->sa_len = sizeof(struct sockaddr_in);\n#endif\n\t\tbreak;\n#endif\n#ifdef INET6\n\tcase AF_INET6:\n\t\tmax_prefix = 128;\n#ifdef HAVE_SA_LEN\n\t\tsa->sa_len = sizeof(struct sockaddr_in6);\n#endif\n\t\tbreak;\n#endif\n\tdefault:\n\t\terrno = EAFNOSUPPORT;\n\t\treturn -1;\n\t}\n\n\tap = (uint8_t *)sa + sa_addroffset(sa);\n\tipbytes_fromprefix(ap, prefix, max_prefix);\n\n#ifndef NDEBUG\n\t/* Ensure the calculation is correct */\n\tif (!sa_inprefix) {\n\t\tsa_inprefix = true;\n\t\tassert(sa_toprefix(sa) == prefix);\n\t\tsa_inprefix = false;\n\t}\n#endif\n\treturn 0;\n}\n\n/* inet_ntop, but for sockaddr. */\nconst char *\nsa_addrtop(const struct sockaddr *sa, char *buf, socklen_t len)\n{\n\tconst void *addr;\n\n\tassert(buf != NULL);\n\tassert(len > 0);\n\n\tif (sa->sa_family == 0) {\n\t\t*buf = '\\0';\n\t\treturn NULL;\n\t}\n\n#ifdef AF_LINK\n#ifndef CLLADDR\n#define CLLADDR(sdl) (const void *)((sdl)->sdl_data + (sdl)->sdl_nlen)\n#endif\n\tif (sa->sa_family == AF_LINK) {\n\t\tconst struct sockaddr_dl *sdl;\n\n\t\tsdl = (const void *)sa;\n\t\tif (sdl->sdl_alen == 0) {\n\t\t\tif (snprintf(buf, len, \"link#%d\", sdl->sdl_index) == -1)\n\t\t\t\treturn NULL;\n\t\t\treturn buf;\n\t\t}\n\t\treturn hwaddr_ntoa(CLLADDR(sdl), sdl->sdl_alen, buf, len);\n\t}\n#elif defined(AF_PACKET)\n\tif (sa->sa_family == AF_PACKET) {\n\t\tconst struct sockaddr_ll *sll;\n\n\t\tsll = (const void *)sa;\n\t\treturn hwaddr_ntoa(sll->sll_addr, sll->sll_halen, buf, len);\n\t}\n#endif\n\taddr = (const char *)sa + sa_addroffset(sa);\n\treturn inet_ntop(sa->sa_family, addr, buf, len);\n}\n\nint\nsa_cmp(const struct sockaddr *sa1, const struct sockaddr *sa2)\n{\n\tsocklen_t offset, len;\n\n\tassert(sa1 != NULL);\n\tassert(sa2 != NULL);\n\n\tif (sa1->sa_family != sa2->sa_family)\n\t\treturn sa1->sa_family - sa2->sa_family;\n\n#ifdef HAVE_SA_LEN\n\tlen = MIN(sa1->sa_len, sa2->sa_len);\n#endif\n\n\tswitch (sa1->sa_family) {\n#ifdef INET\n\tcase AF_INET:\n\t\toffset = offsetof(struct sockaddr_in, sin_addr);\n#ifdef HAVE_SA_LEN\n\t\tlen -= offset;\n\t\tlen = MIN(len, sizeof(struct in_addr));\n#else\n\t\tlen = sizeof(struct in_addr);\n#endif\n\t\tbreak;\n#endif\n#ifdef INET6\n\tcase AF_INET6:\n\t\toffset = offsetof(struct sockaddr_in6, sin6_addr);\n#ifdef HAVE_SA_LEN\n\t\tlen -= offset;\n\t\tlen = MIN(len, sizeof(struct in6_addr));\n#else\n\t\tlen = sizeof(struct in6_addr);\n#endif\n\t\tbreak;\n#endif\n\tdefault:\n\t\toffset = 0;\n#ifndef HAVE_SA_LEN\n\t\tlen = sizeof(struct sockaddr);\n#endif\n\t\tbreak;\n\t}\n\n\treturn memcmp((const char *)sa1 + offset, (const char *)sa2 + offset,\n\t    len);\n}\n\n#ifdef INET\nvoid\nsa_in_init(struct sockaddr *sa, const struct in_addr *addr)\n{\n\tstruct sockaddr_in *sin;\n\n\tassert(sa != NULL);\n\tassert(addr != NULL);\n\tsin = satosin(sa);\n\tsin->sin_family = AF_INET;\n#ifdef HAVE_SA_LEN\n\tsin->sin_len = sizeof(*sin);\n#endif\n\tsin->sin_addr.s_addr = addr->s_addr;\n}\n#endif\n\n#ifdef INET6\nvoid\nsa_in6_init(struct sockaddr *sa, const struct in6_addr *addr)\n{\n\tstruct sockaddr_in6 *sin6;\n\n\tassert(sa != NULL);\n\tassert(addr != NULL);\n\tsin6 = satosin6(sa);\n\tsin6->sin6_family = AF_INET6;\n#ifdef HAVE_SA_LEN\n\tsin6->sin6_len = sizeof(*sin6);\n#endif\n\tmemcpy(&sin6->sin6_addr.s6_addr, &addr->s6_addr,\n\t    sizeof(sin6->sin6_addr.s6_addr));\n}\n#endif\n"
  },
  {
    "path": "src/sa.h",
    "content": "/*\n * Socket Address handling for dhcpcd\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2015-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef SA_H\n#define SA_H\n\n#include <sys/socket.h>\n\n#include <netinet/in.h>\n\nunion sa_ss {\n\tstruct sockaddr sa;\n\tstruct sockaddr_in sin;\n\tstruct sockaddr_in6 sin6;\n};\n\n#ifdef BSD\n#define HAVE_SA_LEN\n#endif\n\n/* Allow for a sockaddr_dl being printed too. */\n#define INET_MAX_ADDRSTRLEN (20 * 3)\n\n#ifdef INET\n#define satosin(sa)  ((struct sockaddr_in *)(void *)(sa))\n#define satocsin(sa) ((const struct sockaddr_in *)(const void *)(sa))\n#endif\n#ifdef INET6\n#define satosin6(sa)  ((struct sockaddr_in6 *)(void *)(sa))\n#define satocsin6(sa) ((const struct sockaddr_in6 *)(const void *)(sa))\n#endif\n\nsocklen_t sa_addroffset(const struct sockaddr *sa);\nsocklen_t sa_addrlen(const struct sockaddr *sa);\n#ifdef HAVE_SA_LEN\n#define sa_len(sa) ((sa)->sa_len)\n#else\nsocklen_t sa_len(const struct sockaddr *sa);\n#endif\nbool sa_is_unspecified(const struct sockaddr *);\nbool sa_is_allones(const struct sockaddr *);\nbool sa_is_loopback(const struct sockaddr *);\nvoid *sa_toaddr(struct sockaddr *);\nint sa_toprefix(const struct sockaddr *);\nint sa_fromprefix(struct sockaddr *, int);\nvoid in6_addr_fromprefix(struct in6_addr *, int);\nconst char *sa_addrtop(const struct sockaddr *, char *, socklen_t);\nint sa_cmp(const struct sockaddr *, const struct sockaddr *);\nvoid sa_in_init(struct sockaddr *, const struct in_addr *);\nvoid sa_in6_init(struct sockaddr *, const struct in6_addr *);\n\n#endif\n"
  },
  {
    "path": "src/script.c",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/stat.h>\n#include <sys/uio.h>\n#include <sys/wait.h>\n\n#include <netinet/in.h>\n\n#include <arpa/inet.h>\n#include <assert.h>\n#include <ctype.h>\n#include <errno.h>\n#include <pwd.h>\n#include <signal.h>\n#include <spawn.h>\n#include <stdarg.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include \"common.h\"\n#include \"config.h\"\n#include \"dhcp.h\"\n#include \"dhcp6.h\"\n#include \"eloop.h\"\n#include \"if-options.h\"\n#include \"if.h\"\n#include \"ipv4ll.h\"\n#include \"ipv6nd.h\"\n#include \"logerr.h\"\n#include \"privsep.h\"\n#include \"script.h\"\n\n#define DEFAULT_PATH \"/usr/bin:/usr/sbin:/bin:/sbin\"\n\nstatic const char *const if_params[] = { \"interface\", \"protocol\", \"reason\",\n\t\"pid\", \"ifcarrier\", \"ifmetric\", \"ifwireless\", \"ifflags\", \"ssid\",\n\t\"profile\", \"interface_order\", NULL };\n\nstatic const char *true_str = \"true\";\nstatic const char *false_str = \"false\";\n\nvoid\nif_printoptions(void)\n{\n\tconst char *const *p;\n\n\tfor (p = if_params; *p; p++)\n\t\tprintf(\" -  %s\\n\", *p);\n}\n\npid_t\nscript_exec(char *const *argv, char *const *env)\n{\n\tpid_t pid = 0;\n\tposix_spawnattr_t attr;\n\tint r;\n#ifdef USE_SIGNALS\n\tsize_t i;\n\tshort flags;\n\tsigset_t defsigs;\n#else\n\tUNUSED(ctx);\n#endif\n\n\t/* posix_spawn is a safe way of executing another image\n\t * and changing signals back to how they should be. */\n\tif (posix_spawnattr_init(&attr) == -1)\n\t\treturn -1;\n#ifdef USE_SIGNALS\n\tflags = POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF;\n\tposix_spawnattr_setflags(&attr, flags);\n\tsigemptyset(&defsigs);\n\tposix_spawnattr_setsigmask(&attr, &defsigs);\n\tfor (i = 0; i < dhcpcd_signals_len; i++)\n\t\tsigaddset(&defsigs, dhcpcd_signals[i]);\n\tfor (i = 0; i < dhcpcd_signals_ignore_len; i++)\n\t\tsigaddset(&defsigs, dhcpcd_signals_ignore[i]);\n\tposix_spawnattr_setsigdefault(&attr, &defsigs);\n#endif\n\terrno = 0;\n\tr = posix_spawn(&pid, argv[0], NULL, &attr, argv, env);\n\tposix_spawnattr_destroy(&attr);\n\tif (r) {\n\t\terrno = r;\n\t\treturn -1;\n\t}\n\treturn pid;\n}\n\n#ifdef INET\nstatic int\nappend_config(FILE *fp, const char *prefix, const char *const *config)\n{\n\tsize_t i;\n\n\tif (config == NULL)\n\t\treturn 0;\n\n\t/* Do we need to replace existing config rather than append? */\n\tfor (i = 0; config[i] != NULL; i++) {\n\t\tif (efprintf(fp, \"%s_%s\", prefix, config[i]) == -1)\n\t\t\treturn -1;\n\t}\n\treturn 1;\n}\n\n#endif\n\n#define PROTO_LINK    0\n#define PROTO_DHCP    1\n#define PROTO_IPV4LL  2\n#define PROTO_RA      3\n#define PROTO_DHCP6   4\n#define PROTO_STATIC6 5\nstatic const char *protocols[] = { \"link\", \"dhcp\", \"ipv4ll\", \"ra\", \"dhcp6\",\n\t\"static6\" };\n\nint\nefprintf(FILE *fp, const char *fmt, ...)\n{\n\tva_list args;\n\tint r;\n\n\tva_start(args, fmt);\n\tr = vfprintf(fp, fmt, args);\n\tva_end(args);\n\tif (r == -1)\n\t\treturn -1;\n\t/* Write a trailing NULL so we can easily create env strings. */\n\tif (fputc('\\0', fp) == EOF)\n\t\treturn -1;\n\treturn r;\n}\n\nchar **\nscript_buftoenv(struct dhcpcd_ctx *ctx, char *buf, size_t len)\n{\n\tchar **env, **envp, *bufp, *endp;\n\tsize_t nenv;\n\n\t/* Count the terminated env strings.\n\t * Assert that the terminations are correct. */\n\tnenv = 0;\n\tendp = buf + len;\n\tfor (bufp = buf; bufp < endp; bufp++) {\n\t\tif (*bufp == '\\0') {\n#ifndef NDEBUG\n\t\t\tif (bufp + 1 < endp)\n\t\t\t\tassert(*(bufp + 1) != '\\0');\n#endif\n\t\t\tnenv++;\n\t\t}\n\t}\n\tassert(*(bufp - 1) == '\\0');\n\tif (nenv == 0)\n\t\treturn NULL;\n\n\tif (ctx->script_envlen < nenv) {\n\t\tenv = reallocarray(ctx->script_env, nenv + 1, sizeof(*env));\n\t\tif (env == NULL)\n\t\t\treturn NULL;\n\t\tctx->script_env = env;\n\t\tctx->script_envlen = nenv;\n\t}\n\n\tbufp = buf;\n\tenvp = ctx->script_env;\n\t*envp++ = bufp++;\n\tendp--; /* Avoid setting the last \\0 to an invalid pointer */\n\tfor (; bufp < endp; bufp++) {\n\t\tif (*bufp == '\\0')\n\t\t\t*envp++ = bufp + 1;\n\t}\n\t*envp = NULL;\n\n\treturn ctx->script_env;\n}\n\nstatic long\nmake_env(struct dhcpcd_ctx *ctx, const struct interface *ifp,\n    const char *reason)\n{\n\tFILE *fp;\n\tlong buf_pos, i;\n\tchar *path;\n\tint protocol = PROTO_LINK;\n\tconst struct if_options *ifo;\n\tconst struct interface *ifp2;\n\tint af;\n\tbool is_stdin = ifp->name[0] == '\\0';\n\tchar if_name[sizeof(ifp->name) * 4];\n\tconst char *if_up, *if_down;\n\trb_tree_t ifaces;\n\tstruct rt *rt;\n#ifdef INET\n\tconst struct dhcp_state *state;\n#ifdef IPV4LL\n\tconst struct ipv4ll_state *istate;\n#endif\n#endif\n#ifdef DHCP6\n\tconst struct dhcp6_state *d6_state;\n#endif\n\n#ifdef HAVE_OPEN_MEMSTREAM\n\tif (ctx->script_fp == NULL) {\n\t\tfp = open_memstream(&ctx->script_buf, &ctx->script_buflen);\n\t\tif (fp == NULL)\n\t\t\tgoto eexit;\n\t\tctx->script_fp = fp;\n\t} else {\n\t\tfp = ctx->script_fp;\n\t\trewind(fp);\n\t}\n#else\n\tchar tmpfile[] = \"/tmp/dhcpcd-script-env-XXXXXX\";\n\tint tmpfd;\n\n\tfp = NULL;\n\ttmpfd = mkstemp(tmpfile);\n\tif (tmpfd == -1) {\n\t\tlogerr(\"%s: mkstemp\", __func__);\n\t\treturn -1;\n\t}\n\tunlink(tmpfile);\n\tfp = fdopen(tmpfd, \"w+\");\n\tif (fp == NULL) {\n\t\tclose(tmpfd);\n\t\tgoto eexit;\n\t}\n#endif\n\n\tif (!(ifp->ctx->options & DHCPCD_DUMPLEASE)) {\n\t\t/* Needed for scripts */\n\t\tpath = getenv(\"PATH\");\n\t\tif (efprintf(fp, \"PATH=%s\",\n\t\t\tpath == NULL ? DEFAULT_PATH : path) == -1)\n\t\t\tgoto eexit;\n\t\tif (efprintf(fp, \"pid=%d\", (int)getpid()) == -1)\n\t\t\tgoto eexit;\n\t}\n\n\tif (!is_stdin) {\n\t\tif (efprintf(fp, \"reason=%s\", reason) == -1)\n\t\t\tgoto eexit;\n\t}\n\n\tifo = ifp->options;\n#ifdef INET\n\tstate = D_STATE(ifp);\n#ifdef IPV4LL\n\tistate = IPV4LL_CSTATE(ifp);\n#endif\n#endif\n#ifdef DHCP6\n\td6_state = D6_CSTATE(ifp);\n#endif\n\tif (strcmp(reason, \"TEST\") == 0) {\n\t\tif (1 == 2) {\n\t\t\t/* This space left intentionally blank\n\t\t\t * as all the below statements are optional. */\n\t\t}\n#ifdef INET6\n#ifdef DHCP6\n\t\telse if (d6_state && d6_state->new)\n\t\t\tprotocol = PROTO_DHCP6;\n#endif\n\t\telse if (ipv6nd_hasra(ifp))\n\t\t\tprotocol = PROTO_RA;\n#endif\n#ifdef INET\n#ifdef IPV4LL\n\t\telse if (istate && istate->addr != NULL)\n\t\t\tprotocol = PROTO_IPV4LL;\n#endif\n\t\telse\n\t\t\tprotocol = PROTO_DHCP;\n#endif\n\t}\n#ifdef INET6\n\telse if (strcmp(reason, \"STATIC6\") == 0)\n\t\tprotocol = PROTO_STATIC6;\n#ifdef DHCP6\n\telse if (reason[strlen(reason) - 1] == '6')\n\t\tprotocol = PROTO_DHCP6;\n#endif\n\telse if (strcmp(reason, \"ROUTERADVERT\") == 0)\n\t\tprotocol = PROTO_RA;\n#endif\n\telse if (strcmp(reason, \"PREINIT\") == 0 ||\n\t    strcmp(reason, \"CARRIER\") == 0 ||\n\t    strcmp(reason, \"NOCARRIER\") == 0 ||\n\t    strcmp(reason, \"NOCARRIER_ROAMING\") == 0 ||\n\t    strcmp(reason, \"UNKNOWN\") == 0 || strcmp(reason, \"DEPARTED\") == 0 ||\n\t    strcmp(reason, \"STOPPED\") == 0)\n\t\tprotocol = PROTO_LINK;\n#ifdef INET\n#ifdef IPV4LL\n\telse if (strcmp(reason, \"IPV4LL\") == 0)\n\t\tprotocol = PROTO_IPV4LL;\n#endif\n\telse\n\t\tprotocol = PROTO_DHCP;\n#endif\n\n\tif (!is_stdin) {\n\t\tprint_string(if_name, sizeof(if_name), OT_ESCFILE, ifp->name,\n\t\t    strlen(ifp->name));\n\t\tif (efprintf(fp, \"interface=%s\", if_name) == -1)\n\t\t\tgoto eexit;\n\t\tif (protocols[protocol] != NULL) {\n\t\t\tif (efprintf(fp, \"protocol=%s\", protocols[protocol]) ==\n\t\t\t    -1)\n\t\t\t\tgoto eexit;\n\t\t}\n\t}\n\tif (ifp->ctx->options & DHCPCD_DUMPLEASE && protocol != PROTO_LINK)\n\t\tgoto dumplease;\n\tif (efprintf(fp, \"if_configured=%s\",\n\t\tifo->options & DHCPCD_CONFIGURE ? \"true\" : \"false\") == -1)\n\t\tgoto eexit;\n\tif (efprintf(fp, \"ifcarrier=%s\",\n\t\tifp->carrier == LINK_UNKNOWN ? \"unknown\" :\n\t\t    ifp->carrier == LINK_UP  ? \"up\" :\n\t\t\t\t\t       \"down\") == -1)\n\t\tgoto eexit;\n\tif (efprintf(fp, \"ifmetric=%d\", ifp->metric) == -1)\n\t\tgoto eexit;\n\tif (efprintf(fp, \"ifwireless=%d\", ifp->wireless) == -1)\n\t\tgoto eexit;\n\tif (efprintf(fp, \"ifflags=%u\", ifp->flags) == -1)\n\t\tgoto eexit;\n\tif (efprintf(fp, \"ifmtu=%d\", if_getmtu(ifp)) == -1)\n\t\tgoto eexit;\n\tif (ifp->wireless) {\n\t\tchar pssid[IF_SSIDLEN * 4];\n\n\t\tif (print_string(pssid, sizeof(pssid), OT_ESCSTRING, ifp->ssid,\n\t\t\tifp->ssid_len) != -1) {\n\t\t\tif (efprintf(fp, \"ifssid=%s\", pssid) == -1)\n\t\t\t\tgoto eexit;\n\t\t}\n\t}\n\tif (*ifp->profile != '\\0') {\n\t\tif (efprintf(fp, \"profile=%s\", ifp->profile) == -1)\n\t\t\tgoto eexit;\n\t}\n\tif (ifp->ctx->options & DHCPCD_DUMPLEASE)\n\t\tgoto dumplease;\n\n\tifp->ctx->rt_order = 0;\n\trb_tree_init(&ifaces, &rt_compare_proto_ops);\n\tTAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {\n\t\tif (!ifp2->active)\n\t\t\tcontinue;\n\t\trt = rt_new(UNCONST(ifp2));\n\t\tif (rt == NULL)\n\t\t\tgoto eexit;\n\t\tif (rt_proto_add(&ifaces, rt) != rt)\n\t\t\tgoto eexit;\n\t}\n\tif (fprintf(fp, \"interface_order=\") == -1)\n\t\tgoto eexit;\n\tRB_TREE_FOREACH(rt, &ifaces)\n\t{\n\t\tprint_string(if_name, sizeof(if_name), OT_ESCFILE,\n\t\t    rt->rt_ifp->name, strlen(rt->rt_ifp->name));\n\t\tif (rt != RB_TREE_MIN(&ifaces) && fprintf(fp, \"%s\", \" \") == -1)\n\t\t\tgoto eexit;\n\t\tif (fprintf(fp, \"%s\", if_name) == -1)\n\t\t\tgoto eexit;\n\t}\n\trt_headclear(&ifaces, AF_UNSPEC);\n\tif (fputc('\\0', fp) == EOF)\n\t\tgoto eexit;\n\n\tif (strcmp(reason, \"STOPPED\") == 0) {\n\t\tif_up = false_str;\n\t\tif_down = ifo->options & DHCPCD_RELEASE ? true_str : false_str;\n\t} else if (strcmp(reason, \"TEST\") == 0 ||\n\t    strcmp(reason, \"PREINIT\") == 0 || strcmp(reason, \"CARRIER\") == 0 ||\n\t    strcmp(reason, \"STOP\") == 0 || strcmp(reason, \"UNKNOWN\") == 0) {\n\t\tif_up = false_str;\n\t\tif_down = false_str;\n\t} else if (strcmp(reason, \"NOCARRIER\") == 0) {\n\t\tif_up = false_str;\n\t\tif_down = true_str;\n\t} else if (strcmp(reason, \"NOCARRIER_ROAMING\") == 0) {\n\t\tif_up = true_str;\n\t\tif_down = false_str;\n\t} else if (1 == 2 /* appease ifdefs */\n#ifdef INET\n\t    || (protocol == PROTO_DHCP && state && state->new)\n#ifdef IPV4LL\n\t    || (protocol == PROTO_IPV4LL && IPV4LL_STATE_RUNNING(ifp))\n#endif\n#endif\n#ifdef INET6\n\t    || (protocol == PROTO_STATIC6 && IPV6_STATE_RUNNING(ifp))\n#ifdef DHCP6\n\t    || (protocol == PROTO_DHCP6 && d6_state && d6_state->new)\n#endif\n\t    || (protocol == PROTO_RA && ipv6nd_hasra(ifp))\n#endif\n\t) {\n\t\tif_up = true_str;\n\t\tif_down = false_str;\n\t} else {\n\t\tif_up = false_str;\n\t\tif_down = true_str;\n\t}\n\tif (efprintf(fp, \"if_up=%s\", if_up) == -1)\n\t\tgoto eexit;\n\tif (efprintf(fp, \"if_down=%s\", if_down) == -1)\n\t\tgoto eexit;\n\n\tif ((af = dhcpcd_ifafwaiting(ifp)) != AF_MAX) {\n\t\tif (efprintf(fp, \"if_afwaiting=%d\", af) == -1)\n\t\t\tgoto eexit;\n\t}\n\tif ((af = dhcpcd_afwaiting(ifp->ctx)) != AF_MAX) {\n\t\tTAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {\n\t\t\tif ((af = dhcpcd_ifafwaiting(ifp2)) != AF_MAX)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\tif (af != AF_MAX) {\n\t\tif (efprintf(fp, \"af_waiting=%d\", af) == -1)\n\t\t\tgoto eexit;\n\t}\n\tif (loggetopts() & LOGERR_DEBUG) {\n\t\tif (efprintf(fp, \"syslog_debug=true\") == -1)\n\t\t\tgoto eexit;\n\t}\n#ifdef INET\n\tif (protocol == PROTO_DHCP && state && state->old) {\n\t\tif (dhcp_env(fp, \"old\", ifp, state->old, state->old_len) == -1)\n\t\t\tgoto eexit;\n\t\tif (append_config(fp, \"old\",\n\t\t\t(const char *const *)ifo->config) == -1)\n\t\t\tgoto eexit;\n\t}\n#endif\n#ifdef DHCP6\n\tif (protocol == PROTO_DHCP6 && d6_state && d6_state->old) {\n\t\tif (dhcp6_env(fp, \"old\", ifp, d6_state->old,\n\t\t\td6_state->old_len) == -1)\n\t\t\tgoto eexit;\n\t}\n#endif\n\ndumplease:\n#ifdef INET\n#ifdef IPV4LL\n\tif (protocol == PROTO_IPV4LL && istate) {\n\t\tif (ipv4ll_env(fp, istate->down ? \"old\" : \"new\", ifp) == -1)\n\t\t\tgoto eexit;\n\t}\n#endif\n\tif (protocol == PROTO_DHCP && state && state->new) {\n\t\tif (dhcp_env(fp, \"new\", ifp, state->new, state->new_len) == -1)\n\t\t\tgoto eexit;\n\t\tif (append_config(fp, \"new\",\n\t\t\t(const char *const *)ifo->config) == -1)\n\t\t\tgoto eexit;\n\t}\n#endif\n#ifdef INET6\n\tif (protocol == PROTO_STATIC6) {\n\t\tif (ipv6_env(fp, \"new\", ifp) == -1)\n\t\t\tgoto eexit;\n\t}\n#ifdef DHCP6\n\tif (protocol == PROTO_DHCP6 && D6_STATE_RUNNING(ifp)) {\n\t\tif (dhcp6_env(fp, \"new\", ifp, d6_state->new,\n\t\t\td6_state->new_len) == -1)\n\t\t\tgoto eexit;\n\t}\n#endif\n\tif (protocol == PROTO_RA) {\n\t\tif (ipv6nd_env(fp, ifp) == -1)\n\t\t\tgoto eexit;\n\t}\n#endif\n\n\t/* Add our base environment */\n\tif (ifo->environ) {\n\t\tfor (i = 0; ifo->environ[i] != NULL; i++)\n\t\t\tif (efprintf(fp, \"%s\", ifo->environ[i]) == -1)\n\t\t\t\tgoto eexit;\n\t}\n\n\t/* Convert buffer to argv */\n\tfflush(fp);\n\n\tbuf_pos = ftell(fp);\n\tif (buf_pos == -1) {\n\t\tlogerr(__func__);\n\t\tgoto eexit;\n\t}\n\n#ifndef HAVE_OPEN_MEMSTREAM\n\tsize_t buf_len = (size_t)buf_pos;\n\tif (ctx->script_buflen < buf_len) {\n\t\tchar *buf = realloc(ctx->script_buf, buf_len);\n\t\tif (buf == NULL)\n\t\t\tgoto eexit;\n\t\tctx->script_buf = buf;\n\t\tctx->script_buflen = buf_len;\n\t}\n\trewind(fp);\n\tif (fread(ctx->script_buf, sizeof(char), buf_len, fp) != buf_len)\n\t\tgoto eexit;\n\tfclose(fp);\n\tfp = NULL;\n#endif\n\n\tif (is_stdin)\n\t\treturn buf_pos;\n\n\tif (script_buftoenv(ctx, ctx->script_buf, (size_t)buf_pos) == NULL)\n\t\tgoto eexit;\n\n\treturn buf_pos;\n\neexit:\n\tlogerr(__func__);\n#ifndef HAVE_OPEN_MEMSTREAM\n\tif (fp != NULL)\n\t\tfclose(fp);\n#endif\n\treturn -1;\n}\n\nstatic int\nsend_interface1(struct fd_list *fd, const struct interface *ifp,\n    const char *reason)\n{\n\tstruct dhcpcd_ctx *ctx = ifp->ctx;\n\tlong len;\n\n\tlen = make_env(ifp->ctx, ifp, reason);\n\tif (len == -1)\n\t\treturn -1;\n\treturn control_queue(fd, ctx->script_buf, (size_t)len);\n}\n\nint\nsend_interface(struct fd_list *fd, const struct interface *ifp, int af)\n{\n\tint retval = 0;\n#ifdef INET\n\tconst struct dhcp_state *d;\n#endif\n#ifdef DHCP6\n\tconst struct dhcp6_state *d6;\n#endif\n\n#ifndef AF_LINK\n#define AF_LINK AF_PACKET\n#endif\n\n\tif (af == AF_UNSPEC || af == AF_LINK) {\n\t\tconst char *reason;\n\n\t\tswitch (ifp->carrier) {\n\t\tcase LINK_UP:\n\t\t\treason = \"CARRIER\";\n\t\t\tbreak;\n\t\tcase LINK_DOWN:\n\t\t\treason = \"NOCARRIER\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treason = \"UNKNOWN\";\n\t\t\tbreak;\n\t\t}\n\t\tif (fd != NULL) {\n\t\t\tif (send_interface1(fd, ifp, reason) == -1)\n\t\t\t\tretval = -1;\n\t\t} else\n\t\t\tretval++;\n\t}\n\n#ifdef INET\n\tif (af == AF_UNSPEC || af == AF_INET) {\n\t\tif (D_STATE_RUNNING(ifp)) {\n\t\t\td = D_CSTATE(ifp);\n\t\t\tif (fd != NULL) {\n\t\t\t\tif (send_interface1(fd, ifp, d->reason) == -1)\n\t\t\t\t\tretval = -1;\n\t\t\t} else\n\t\t\t\tretval++;\n\t\t}\n#ifdef IPV4LL\n\t\tif (IPV4LL_STATE_RUNNING(ifp)) {\n\t\t\tif (fd != NULL) {\n\t\t\t\tif (send_interface1(fd, ifp, \"IPV4LL\") == -1)\n\t\t\t\t\tretval = -1;\n\t\t\t} else\n\t\t\t\tretval++;\n\t\t}\n#endif\n\t}\n#endif\n\n#ifdef INET6\n\tif (af == AF_UNSPEC || af == AF_INET6) {\n\t\tif (IPV6_STATE_RUNNING(ifp)) {\n\t\t\tif (fd != NULL) {\n\t\t\t\tif (send_interface1(fd, ifp, \"STATIC6\") == -1)\n\t\t\t\t\tretval = -1;\n\t\t\t} else\n\t\t\t\tretval++;\n\t\t}\n\t\tif (RS_STATE_RUNNING(ifp)) {\n\t\t\tif (fd != NULL) {\n\t\t\t\tif (send_interface1(fd, ifp, \"ROUTERADVERT\") ==\n\t\t\t\t    -1)\n\t\t\t\t\tretval = -1;\n\t\t\t} else\n\t\t\t\tretval++;\n\t\t}\n#ifdef DHCP6\n\t\tif (D6_STATE_RUNNING(ifp)) {\n\t\t\td6 = D6_CSTATE(ifp);\n\t\t\tif (fd != NULL) {\n\t\t\t\tif (send_interface1(fd, ifp, d6->reason) == -1)\n\t\t\t\t\tretval = -1;\n\t\t\t} else\n\t\t\t\tretval++;\n\t\t}\n#endif\n\t}\n#endif\n\n\treturn retval;\n}\n\nstatic int\nscript_status(const char *script, int status)\n{\n\tif (WIFEXITED(status)) {\n\t\tif (WEXITSTATUS(status))\n\t\t\tlogerrx(\"%s: %s: WEXITSTATUS %d\", __func__, script,\n\t\t\t    WEXITSTATUS(status));\n\t} else if (WIFSIGNALED(status))\n\t\tlogerrx(\"%s: %s: %s\", __func__, script,\n\t\t    strsignal(WTERMSIG(status)));\n\n\treturn WEXITSTATUS(status);\n}\n\nstatic int\nscript_run(struct dhcpcd_ctx *ctx, char **argv)\n{\n\tpid_t pid;\n\tint status;\n\n\tpid = script_exec(argv, ctx->script_env);\n\tif (pid == -1) {\n\t\tlogerr(\"%s: %s\", __func__, argv[0]);\n\t\treturn -1;\n\t} else if (pid == 0)\n\t\treturn 0;\n\n\t/* Wait for the script to finish */\n\twhile (waitpid(pid, &status, 0) == -1) {\n\t\tif (errno != EINTR) {\n\t\t\tlogerr(\"%s: waitpid\", __func__);\n\t\t\tstatus = 0;\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn script_status(argv[0], status);\n}\n\nint\nscript_dump(const char *env, size_t len)\n{\n\tconst char *ep = env + len;\n\n\tif (len == 0)\n\t\treturn 0;\n\n\tif (*(ep - 1) != '\\0') {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\n\tfor (; env < ep; env += strlen(env) + 1) {\n\t\tif (strncmp(env, \"new_\", 4) == 0)\n\t\t\tenv += 4;\n\t\tprintf(\"%s\\n\", env);\n\t}\n\tfflush(stdout);\n\treturn 0;\n}\n\nint\nscript_runreason(const struct interface *ifp, const char *reason)\n{\n\tstruct dhcpcd_ctx *ctx = ifp->ctx;\n\tchar *argv[2];\n\tint status = 0;\n\tstruct fd_list *fd;\n\tlong buflen;\n\n\tif (ctx->script == NULL && TAILQ_FIRST(&ifp->ctx->control_fds) == NULL)\n\t\treturn 0;\n\n\t/* Make our env */\n\tif ((buflen = make_env(ifp->ctx, ifp, reason)) == -1) {\n\t\tlogerr(__func__);\n\t\treturn -1;\n\t}\n\n\tif (strncmp(reason, \"DUMP\", 4) == 0)\n\t\treturn script_dump(ctx->script_buf, (size_t)buflen);\n\n\tif (ctx->script == NULL)\n\t\tgoto send_listeners;\n\n\targv[0] = ctx->script;\n\targv[1] = NULL;\n\tlogdebugx(\"%s: executing: %s %s\", ifp->name, argv[0], reason);\n\n#ifdef PRIVSEP\n\tif (IN_PRIVSEP(ctx)) {\n\t\tssize_t err;\n\n\t\terr = ps_root_script(ctx, ctx->script_buf, (size_t)buflen);\n\t\tif (err == -1)\n\t\t\tlogerr(__func__);\n\t\telse\n\t\t\tscript_status(ctx->script, (int)err);\n\t\tgoto send_listeners;\n\t}\n#endif\n\n\tscript_run(ctx, argv);\n\nsend_listeners:\n\t/* Send to our listeners */\n\tstatus = 0;\n\tTAILQ_FOREACH(fd, &ctx->control_fds, next) {\n\t\tif (!(fd->flags & FD_LISTEN))\n\t\t\tcontinue;\n\t\tif (control_queue(fd, ctx->script_buf, ctx->script_buflen) ==\n\t\t    -1)\n\t\t\tlogerr(\"%s: control_queue\", __func__);\n\t\telse\n\t\t\tstatus = 1;\n\t}\n\n\treturn status;\n}\n"
  },
  {
    "path": "src/script.h",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2025 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef SCRIPT_H\n#define SCRIPT_H\n\n#include \"control.h\"\n\n__printflike(2, 3) int efprintf(FILE *, const char *, ...);\nvoid if_printoptions(void);\nchar **script_buftoenv(struct dhcpcd_ctx *, char *, size_t);\npid_t script_exec(char *const *, char *const *);\nint send_interface(struct fd_list *, const struct interface *, int);\nint script_dump(const char *, size_t);\nint script_runreason(const struct interface *, const char *);\n#endif\n"
  },
  {
    "path": "tests/Makefile",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2017-2019 Roy Marples <roy@marples.name>\n\nSUBDIRS=\tcrypt eloop-bench\n\nall: \n\tfor x in ${SUBDIRS}; do cd $$x; ${MAKE} $@ || exit $$?; cd ..; done\n\ninstall:\n\nproginstall:\n\nclean:\n\tfor x in ${SUBDIRS}; do cd $$x; ${MAKE} $@ || exit $$?; cd ..; done\n\ndistclean: clean\n\trm -f *.diff *.patch *.orig *.rej\n\tfor x in ${SUBDIRS}; do cd $$x; ${MAKE} $@ || exit $$?; cd ..; done\n\ntest:\n\tfor x in ${SUBDIRS}; do cd $$x; ${MAKE} $@ || exit $$?; cd ..; done\n\ntests: test\n"
  },
  {
    "path": "tests/crypt/.gitignore",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2017 Roy Marples <roy@marples.name>\n\nrun-test\n"
  },
  {
    "path": "tests/crypt/GNUmakefile",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2017 Roy Marples <roy@marples.name>\n\n# GNU Make does not automagically include .depend\n# Luckily it does read GNUmakefile over Makefile so we can work around it\n\ninclude Makefile\nifneq ($(wildcard .depend), )\ninclude .depend\nendif\n"
  },
  {
    "path": "tests/crypt/Makefile",
    "content": "# SPDX-License-Identifier: BSD-2-Clause\n# Copyright (c) 2017-2023 Roy Marples <roy@marples.name>\n\nTOP=\t../..\ninclude ${TOP}/iconfig.mk\n\nPROG=\t\trun-test\nSRCS=\t\trun-test.c\nSRCS+=\t\ttest_hmac_md5.c test_sha256.c\n\nCFLAGS?=\t-O2\nCSTD?=\t\tc99\nCFLAGS+=\t-std=${CSTD}\n\nCPPFLAGS+=\t-I${TOP} -I${TOP}/src\n\nPCRYPT_SRCS=\t${CRYPT_SRCS:compat/%=${TOP}/compat/%}\nOBJS+=\t\t${SRCS:.c=.o} ${PCRYPT_SRCS:.c=.o}\n\n.c.o:\n\t${CC} ${CFLAGS} ${CPPFLAGS} -c $< -o $@\n\nall: ${PROG}\n\nclean:\n\trm -f ${OBJS} ${PROG} ${PROG}.core ${CLEANFILES}\n\ndistclean: clean\n\trm -f .depend\n\trm -f *.diff *.patch *.orig *.rej\n\n.depend: ${SRCS} ${PCRYPT_SRCS}\n\t${CC} ${CPPFLAGS} -MM ${SRCS} ${PCRYPT_SRCS}\n\n${PROG}: ${DEPEND} ${OBJS}\n\t${CC} ${LDFLAGS} -o $@ ${OBJS} ${LDADD}\n\ntest: ${PROG}\n\t./${PROG}\n"
  },
  {
    "path": "tests/crypt/README.md",
    "content": "<!-- SPDX-License-Identifier: BSD-2-Clause -->\n<!-- Copyright (c) 2017 Roy Marples <roy@marples.name> -->\n\n# dhcpcd Test Suite\n\nCurrently this just tests the RFC2202 MD5 implementation in dhcpcd.\nThis is important, because dhcpcd will either use the system MD5\nimplementation if found, otherwise some compat code.\n\nThis test suit ensures that it works in accordance with known standards\non your platform.\n"
  },
  {
    "path": "tests/crypt/run-test.c",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include \"test.h\"\n\nint main(void)\n{\n\tint r = 0;\n\n\tif (test_hmac_md5())\n\t\tr = -1;\n\tif (test_sha256())\n\t\tr = -1;\n\n\treturn r;\n}\n"
  },
  {
    "path": "tests/crypt/test.h",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef TEST_H\n\nint test_hmac_md5(void);\nint test_sha256(void);\n\n#endif\n"
  },
  {
    "path": "tests/crypt/test_hmac_md5.c",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"config.h\"\n#include \"test.h\"\n\n#ifdef HAVE_HMAC_H\n#include <hmac.h>\n#endif\n\nstatic void\nprint_hmac(FILE *stream, const uint8_t *hmac)\n{\n\tint i;\n\n\tfprintf(stream, \"digest = 0x\");\n\tfor (i = 0; i < 16; i++)\n\t\tfprintf(stream, \"%02x\", *hmac++);\n\tfprintf(stream, \"\\n\");\n}\n\nstatic void\ntest_hmac(const uint8_t *hmac, const uint8_t *tst)\n{\n\tprint_hmac(stdout, hmac);\n\tif (memcmp(hmac, tst, 16) == 0)\n\t\treturn;\n\tfprintf(stderr, \"FAILED!\\nExpected\\t\\t\\t\");\n\tprint_hmac(stderr, tst);\n\texit(EXIT_FAILURE);\n}\n\nstatic void\nhmac_md5_test1(void)\n{\n\tconst uint8_t text[] = \"Hi There\";\n\tuint8_t key[16];\n\tconst uint8_t expect[16] = {\n\t    0x92, 0x94, 0x72, 0x7a, 0x36, 0x38, 0xbb, 0x1c,\n\t    0x13, 0xf4, 0x8e, 0xf8, 0x15, 0x8b, 0xfc, 0x9d,\n\t};\n\tuint8_t digest[16];\n\tint i;\n\n\tprintf (\"HMAC MD5 Test 1:\\t\\t\");\n\tfor (i = 0; i < 16; i++)\n\t\tkey[i] = 0x0b;\n\thmac(\"md5\", key, 16, text, 8, digest, sizeof(digest));\n\ttest_hmac(digest, expect);\n}\n\nstatic void\nhmac_md5_test2(void)\n{\n\tconst uint8_t text[] = \"what do ya want for nothing?\";\n\tconst uint8_t key[] = \"Jefe\";\n\tconst uint8_t expect[16] = {\n\t    0x75, 0x0c, 0x78, 0x3e, 0x6a, 0xb0, 0xb5, 0x03,\n\t    0xea, 0xa8, 0x6e, 0x31, 0x0a, 0x5d, 0xb7, 0x38,\n\t};\n\tuint8_t digest[16];\n\n\tprintf(\"HMAC MD5 Test 2:\\t\\t\");\n\thmac(\"md5\", key, 4, text, 28, digest, sizeof(digest));\n\ttest_hmac(digest, expect);\n}\n\nstatic void\nhmac_md5_test3(void)\n{\n\tconst uint8_t expect[16] = {\n\t    0x56, 0xbe, 0x34, 0x52, 0x1d, 0x14, 0x4c, 0x88,\n\t    0xdb, 0xb8, 0xc7, 0x33, 0xf0, 0xe8, 0xb3, 0xf6,\n\t};\n\tuint8_t digest[16];\n\tuint8_t text[50];\n\tuint8_t key[16];\n\tint i;\n\n\tprintf (\"HMAC MD5 Test 3:\\t\\t\");\n\tfor (i = 0; i < 50; i++)\n\t\ttext[i] = 0xdd;\n\tfor (i = 0; i < 16; i++)\n\t\tkey[i] = 0xaa;\n\thmac(\"md5\", key, 16, text, 50, digest, sizeof(digest));\n\ttest_hmac(digest, expect);\n}\n\nstatic void\nhmac_md5_test4(void)\n{\n\tconst uint8_t expect[16] = {\n\t    0x69, 0x7e, 0xaf, 0x0a, 0xca, 0x3a, 0x3a, 0xea,\n\t    0x3a, 0x75, 0x16, 0x47, 0x46, 0xff, 0xaa, 0x79,\n\t};\n\tuint8_t digest[16];\n\tuint8_t text[50];\n\tuint8_t key[25];\n\tuint8_t i;\n\n\tprintf (\"HMAC MD5 Test 4:\\t\\t\");\n\tfor (i = 0; i < 50; i++)\n\t\ttext[i] = 0xcd;\n\tfor (i = 0; i < 25; i++)\n\t\tkey[i] = (uint8_t)(i + 1);\n\thmac(\"md5\", key, 25, text, 50, digest, sizeof(digest));\n\ttest_hmac(digest, expect);\n}\n\nstatic void\nhmac_md5_test5(void)\n{\n\tconst uint8_t text[] = \"Test With Truncation\";\n\tconst uint8_t expect[] = {\n\t    0x56, 0x46, 0x1e, 0xf2, 0x34, 0x2e, 0xdc, 0x00,\n\t    0xf9, 0xba, 0xb9, 0x95, 0x69, 0x0e, 0xfd, 0x4c,\n\t};\n\tuint8_t digest[16];\n\tuint8_t key[16];\n\tint i;\n\n\tprintf (\"HMAC MD5 Test 5:\\t\\t\");\n\tfor (i = 0; i < 16; i++)\n\t\tkey[i] = 0x0c;\n\thmac(\"md5\", key, 16, text, 20, digest, sizeof(digest));\n\ttest_hmac(digest, expect);\n}\n\nstatic void\nhmac_md5_test6(void)\n{\n\tconst uint8_t text[] = \"Test Using Larger Than Block-Size Key - Hash Key First\";\n\tconst uint8_t expect[] = {\n\t    0x6b, 0x1a, 0xb7, 0xfe, 0x4b, 0xd7, 0xbf, 0x8f,\n\t    0x0b, 0x62, 0xe6, 0xce, 0x61, 0xb9, 0xd0, 0xcd,\n\t};\n\tuint8_t digest[16];\n\tuint8_t key[80];\n\tint i;\n\n\tprintf (\"HMAC MD5 Test 6:\\t\\t\");\n\tfor (i = 0; i < 80; i++)\n\t\tkey[i] = 0xaa;\n\thmac(\"md5\", key, 80, text, 54, digest, sizeof(digest));\n\ttest_hmac(digest, expect);\n}\n\nstatic void\nhmac_md5_test7(void)\n{\n\tconst uint8_t text[] = \"Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data\";\n\tconst uint8_t expect[] = {\n\t    0x6f, 0x63, 0x0f, 0xad, 0x67, 0xcd, 0xa0, 0xee,\n\t    0x1f, 0xb1, 0xf5, 0x62, 0xdb, 0x3a, 0xa5, 0x3e,\n\t};\n\tuint8_t digest[16];\n\tuint8_t key[80];\n\tint i;\n\n\tprintf (\"HMAC MD5 Test 7:\\t\\t\");\n\tfor (i = 0; i < 80; i++)\n\t\tkey[i] = 0xaa;\n\thmac(\"md5\", key, 80, text, 73, digest, sizeof(digest));\n\ttest_hmac(digest, expect);\n}\n\nint test_hmac_md5(void)\n{\n\n\tprintf (\"Starting RFC2202 HMAC MD5 tests...\\n\\n\");\n\thmac_md5_test1();\n\thmac_md5_test2();\n\thmac_md5_test3();\n\thmac_md5_test4();\n\thmac_md5_test5();\n\thmac_md5_test6();\n\thmac_md5_test7();\n\tprintf(\"\\nAll tests pass.\\n\");\n\treturn 0;\n}\n"
  },
  {
    "path": "tests/crypt/test_sha256.c",
    "content": "/*\n * dhcpcd - DHCP client daemon\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2023 Tobias Heider <tobias.heider@canonical.com>\n * Copyright (c) 2006-2018 Roy Marples <roy@marples.name>\n * All rights reserved\n\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"config.h\"\n#include \"test.h\"\n\n#ifdef SHA2_H\n#include SHA2_H\n#endif\n\n# ifndef SHA256_DIGEST_LENGTH\n#  define SHA256_DIGEST_LENGTH          32\n# endif\n\nstatic void\nprint_md(FILE *stream, const uint8_t *md)\n{\n\tint i;\n\n\tfprintf(stream, \"digest = 0x\");\n\tfor (i = 0; i < SHA256_DIGEST_LENGTH; i++)\n\t\tfprintf(stream, \"%02x\", *md++);\n\tfprintf(stream, \"\\n\");\n}\n\nstatic void\ntest_md(const uint8_t *md, const uint8_t *tst)\n{\n\tprint_md(stdout, md);\n\tif (memcmp(md, tst, SHA256_DIGEST_LENGTH) == 0)\n\t\treturn;\n\tfprintf(stderr, \"FAILED!\\nExpected\\t\\t\\t\");\n\tprint_md(stderr, tst);\n\texit(EXIT_FAILURE);\n}\n\nstatic void\nsha256_test1(void)\n{\n\tconst uint8_t text[] = \"Hi There\";\n\tconst uint8_t expect[SHA256_DIGEST_LENGTH] = {\n\t\t0xcc, 0x6d, 0x58, 0x96, 0xd7, 0x70, 0x10, 0x1e,\n\t\t0xf0, 0x28, 0x0c, 0x94, 0x3a, 0x2d, 0x3c, 0x3f,\n\t\t0x24, 0xcd ,0x5b, 0x11, 0x46, 0x4a, 0x51, 0x86,\n\t\t0xda, 0xf7, 0xa2, 0x38, 0x47, 0x71, 0x62, 0xac\n\t};\n\tuint8_t digest[SHA256_DIGEST_LENGTH];\n\tSHA256_CTX ctx;\n\n\tprintf (\"SHA256 Test 1:\\t\\t\");\n\tSHA256_Init(&ctx);\n\tSHA256_Update(&ctx, text, 8);\n\tSHA256_Final(digest, &ctx);\n\ttest_md(digest, expect);\n}\n\nstatic void\nsha256_test2(void)\n{\n\tconst uint8_t text[] = \"what do ya want for nothing?\";\n\tconst uint8_t expect[SHA256_DIGEST_LENGTH] = {\n\t\t0xb3, 0x81, 0xe7, 0xfe, 0xc6, 0x53, 0xfc, 0x3a,\n\t\t0xb9, 0xb1, 0x78, 0x27, 0x23, 0x66, 0xb8, 0xac,\n\t\t0x87, 0xfe, 0xd8, 0xd3, 0x1c, 0xb2, 0x5e, 0xd1,\n\t\t0xd0, 0xe1, 0xf3, 0x31, 0x86, 0x44, 0xc8, 0x9c,\n\t};\n\tuint8_t digest[SHA256_DIGEST_LENGTH];\n\tSHA256_CTX ctx;\n\n\tprintf (\"SHA256 Test 2:\\t\\t\");\n\tSHA256_Init(&ctx);\n\tSHA256_Update(&ctx, text, 28);\n\tSHA256_Final(digest, &ctx);\n\ttest_md(digest, expect);\n}\n\nstatic void\nsha256_test3(void)\n{\n\tconst uint8_t expect[SHA256_DIGEST_LENGTH] = {\n\t\t0x5c, 0xf6, 0x18, 0xb5, 0xb6, 0xd3, 0x8b, 0xd1,\n\t\t0x6c, 0x2e, 0x55, 0x8e, 0xef, 0x4d, 0x4b, 0x6d,\n\t\t0x52, 0x82, 0x84, 0x54, 0x7f, 0xd4, 0xa0, 0x9d,\n\t\t0xa2, 0xab, 0xb6, 0xf0, 0x98, 0xec, 0x61, 0x93,\n\t};\n\tuint8_t digest[SHA256_DIGEST_LENGTH];\n\tuint8_t text[50];\n\tint i;\n\tSHA256_CTX ctx;\n\n\tprintf (\"SHA256 Test 3:\\t\\t\");\n\tfor (i = 0; i < 50; i++)\n\t\ttext[i] = 0xdd;\n\tSHA256_Init(&ctx);\n\tSHA256_Update(&ctx, text, 50);\n\tSHA256_Final(digest, &ctx);\n\ttest_md(digest, expect);\n}\n\nint test_sha256(void)\n{\n\tprintf (\"Starting SHA256 tests...\\n\\n\");\n\tsha256_test1();\n\tsha256_test2();\n\tsha256_test3();\n\tprintf(\"\\nAll tests pass.\\n\");\n\treturn 0;\n}\n"
  },
  {
    "path": "vendor/README.md",
    "content": "<!-- SPDX-License-Identifier: BSD-2-Clause -->\n<!-- Copyright (c) 2025 Roy Marples <roy@marples.name> -->\n\nThis area is for 3rd party software we include directly.\nAll imports should be made on a branch vendor/NAME (initially orphaned) and\nmerged into the master branch.\n\nAny local changes we need (ideally none) should be done directly\non the master branch where we handle any fallout.\n\nThis makes updating vendor imports easy.\n\nVendor sources are imported from these locations:\n* queue.h     - https://cvsweb.netbsd.org/bsdweb.cgi/src/sys/sys/queue.h\n* rbtree.c    - https://cvsweb.netbsd.org/bsdweb.cgi/src/common/lib/libc/gen/rbtree.c\n* rbtree.h    - https://cvsweb.netbsd.org/bsdweb.cgi/src/sys/sys/rbtree.h\n"
  },
  {
    "path": "vendor/queue.h",
    "content": "/*\t$NetBSD: queue.h,v 1.77 2024/05/12 10:34:56 rillig Exp $\t*/\n\n/*\n * SPDX-License-Identifier: BSD-3-Clause\n * Copyright (c) 1991, 1993\n *\tThe Regents of the University of California.  All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the University nor the names of its contributors\n *    may be used to endorse or promote products derived from this software\n *    without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n *\n *\t@(#)queue.h\t8.5 (Berkeley) 8/20/94\n */\n\n#ifndef\t_SYS_QUEUE_H_\n#define\t_SYS_QUEUE_H_\n\n/*\n * This file defines five types of data structures: singly-linked lists,\n * lists, simple queues, tail queues, and circular queues.\n *\n * A singly-linked list is headed by a single forward pointer. The\n * elements are singly linked for minimum space and pointer manipulation\n * overhead at the expense of O(n) removal for arbitrary elements. New\n * elements can be added to the list after an existing element or at the\n * head of the list.  Elements being removed from the head of the list\n * should use the explicit macro for this purpose for optimum\n * efficiency. A singly-linked list may only be traversed in the forward\n * direction.  Singly-linked lists are ideal for applications with large\n * datasets and few or no removals or for implementing a LIFO queue.\n *\n * A list is headed by a single forward pointer (or an array of forward\n * pointers for a hash table header). The elements are doubly linked\n * so that an arbitrary element can be removed without a need to\n * traverse the list. New elements can be added to the list before\n * or after an existing element or at the head of the list. A list\n * may only be traversed in the forward direction.\n *\n * A simple queue is headed by a pair of pointers, one the head of the\n * list and the other to the tail of the list. The elements are singly\n * linked to save space, so elements can only be removed from the\n * head of the list. New elements can be added to the list after\n * an existing element, at the head of the list, or at the end of the\n * list. A simple queue may only be traversed in the forward direction.\n *\n * A tail queue is headed by a pair of pointers, one to the head of the\n * list and the other to the tail of the list. The elements are doubly\n * linked so that an arbitrary element can be removed without a need to\n * traverse the list. New elements can be added to the list before or\n * after an existing element, at the head of the list, or at the end of\n * the list. A tail queue may be traversed in either direction.\n *\n * For details on the use of these macros, see the queue(3) manual page.\n */\n\n/*\n * Include the definition of NULL only on NetBSD because sys/null.h\n * is not available elsewhere.  This conditional makes the header\n * portable and it can simply be dropped verbatim into any system.\n * The caveat is that on other systems some other header\n * must provide NULL before the macros can be used.\n */\n#ifdef __NetBSD__\n#include <sys/null.h>\n#endif\n\n#if defined(_KERNEL) && defined(DIAGNOSTIC)\n#define QUEUEDEBUG\t1\n#endif\n\n#if defined(QUEUEDEBUG)\n# if defined(_KERNEL)\n#  define QUEUEDEBUG_ABORT(...) panic(__VA_ARGS__)\n# else\n#  include <err.h>\n#  define QUEUEDEBUG_ABORT(...) err(1, __VA_ARGS__)\n# endif\n#endif\n\n/*\n * Singly-linked List definitions.\n */\n#define\tSLIST_HEAD(name, type)\t\t\t\t\t\t\\\nstruct name {\t\t\t\t\t\t\t\t\\\n\tstruct type *slh_first;\t/* first element */\t\t\t\\\n}\n\n#define\tSLIST_HEAD_INITIALIZER(head)\t\t\t\t\t\\\n\t{ NULL }\n\n#define\tSLIST_ENTRY(type)\t\t\t\t\t\t\\\nstruct {\t\t\t\t\t\t\t\t\\\n\tstruct type *sle_next;\t/* next element */\t\t\t\\\n}\n\n/*\n * Singly-linked List access methods.\n */\n#define\tSLIST_FIRST(head)\t((head)->slh_first)\n#define\tSLIST_END(head)\t\tNULL\n#define\tSLIST_EMPTY(head)\t((head)->slh_first == NULL)\n#define\tSLIST_NEXT(elm, field)\t((elm)->field.sle_next)\n\n#define\tSLIST_FOREACH(var, head, field)\t\t\t\t\t\\\n\tfor((var) = (head)->slh_first;\t\t\t\t\t\\\n\t    (var) != SLIST_END(head);\t\t\t\t\t\\\n\t    (var) = (var)->field.sle_next)\n\n#define\tSLIST_FOREACH_SAFE(var, head, field, tvar)\t\t\t\\\n\tfor ((var) = SLIST_FIRST((head));\t\t\t\t\\\n\t    (var) != SLIST_END(head) &&\t\t\t\t\t\\\n\t    ((tvar) = SLIST_NEXT((var), field), 1);\t\t\t\\\n\t    (var) = (tvar))\n\n/*\n * Singly-linked List functions.\n */\n#define\tSLIST_INIT(head) do {\t\t\t\t\t\t\\\n\t(head)->slh_first = SLIST_END(head);\t\t\t\t\\\n} while (0)\n\n#define\tSLIST_INSERT_AFTER(slistelm, elm, field) do {\t\t\t\\\n\t(elm)->field.sle_next = (slistelm)->field.sle_next;\t\t\\\n\t(slistelm)->field.sle_next = (elm);\t\t\t\t\\\n} while (0)\n\n#define\tSLIST_INSERT_HEAD(head, elm, field) do {\t\t\t\\\n\t(elm)->field.sle_next = (head)->slh_first;\t\t\t\\\n\t(head)->slh_first = (elm);\t\t\t\t\t\\\n} while (0)\n\n#define\tSLIST_REMOVE_AFTER(slistelm, field) do {\t\t\t\\\n\t(slistelm)->field.sle_next =\t\t\t\t\t\\\n\t    SLIST_NEXT(SLIST_NEXT((slistelm), field), field);\t\t\\\n} while (0)\n\n#define\tSLIST_REMOVE_HEAD(head, field) do {\t\t\t\t\\\n\t(head)->slh_first = (head)->slh_first->field.sle_next;\t\t\\\n} while (0)\n\n#define\tSLIST_REMOVE(head, elm, type, field) do {\t\t\t\\\n\tif ((head)->slh_first == (elm)) {\t\t\t\t\\\n\t\tSLIST_REMOVE_HEAD((head), field);\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n\telse {\t\t\t\t\t\t\t\t\\\n\t\tstruct type *curelm = (head)->slh_first;\t\t\\\n\t\twhile(curelm->field.sle_next != (elm))\t\t\t\\\n\t\t\tcurelm = curelm->field.sle_next;\t\t\\\n\t\tcurelm->field.sle_next =\t\t\t\t\\\n\t\t    curelm->field.sle_next->field.sle_next;\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n\n/*\n * List definitions.\n */\n#define\tLIST_HEAD(name, type)\t\t\t\t\t\t\\\nstruct name {\t\t\t\t\t\t\t\t\\\n\tstruct type *lh_first;\t/* first element */\t\t\t\\\n}\n\n#define\tLIST_HEAD_INITIALIZER(head)\t\t\t\t\t\\\n\t{ NULL }\n\n#define\tLIST_ENTRY(type)\t\t\t\t\t\t\\\nstruct {\t\t\t\t\t\t\t\t\\\n\tstruct type *le_next;\t/* next element */\t\t\t\\\n\tstruct type **le_prev;\t/* address of previous next element */\t\\\n}\n\n/*\n * List access methods.\n */\n#define\tLIST_FIRST(head)\t\t((head)->lh_first)\n#define\tLIST_END(head)\t\t\tNULL\n#define\tLIST_EMPTY(head)\t\t((head)->lh_first == LIST_END(head))\n#define\tLIST_NEXT(elm, field)\t\t((elm)->field.le_next)\n\n#define\tLIST_FOREACH(var, head, field)\t\t\t\t\t\\\n\tfor ((var) = ((head)->lh_first);\t\t\t\t\\\n\t    (var) != LIST_END(head);\t\t\t\t\t\\\n\t    (var) = ((var)->field.le_next))\n\n#define\tLIST_FOREACH_SAFE(var, head, field, tvar)\t\t\t\\\n\tfor ((var) = LIST_FIRST((head));\t\t\t\t\\\n\t    (var) != LIST_END(head) &&\t\t\t\t\t\\\n\t    ((tvar) = LIST_NEXT((var), field), 1);\t\t\t\\\n\t    (var) = (tvar))\n\n#define\tLIST_MOVE(head1, head2, field) do {\t\t\t\t\\\n\tLIST_INIT((head2));\t\t\t\t\t\t\\\n\tif (!LIST_EMPTY((head1))) {\t\t\t\t\t\\\n\t\t(head2)->lh_first = (head1)->lh_first;\t\t\t\\\n\t\t(head2)->lh_first->field.le_prev = &(head2)->lh_first;\t\\\n\t\tLIST_INIT((head1));\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n/*\n * List functions.\n */\n#if defined(QUEUEDEBUG)\n#define\tQUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field)\t\t\t\\\n\tif ((head)->lh_first &&\t\t\t\t\t\t\\\n\t    (head)->lh_first->field.le_prev != &(head)->lh_first)\t\\\n\t\tQUEUEDEBUG_ABORT(\"LIST_INSERT_HEAD %p %s:%d\", (head),\t\\\n\t\t    __FILE__, __LINE__);\n#define\tQUEUEDEBUG_LIST_OP(elm, field)\t\t\t\t\t\\\n\tif ((elm)->field.le_next &&\t\t\t\t\t\\\n\t    (elm)->field.le_next->field.le_prev !=\t\t\t\\\n\t    &(elm)->field.le_next)\t\t\t\t\t\\\n\t\tQUEUEDEBUG_ABORT(\"LIST_* forw %p %s:%d\", (elm),\t\t\\\n\t\t    __FILE__, __LINE__);\t\t\t\t\\\n\tif (*(elm)->field.le_prev != (elm))\t\t\t\t\\\n\t\tQUEUEDEBUG_ABORT(\"LIST_* back %p %s:%d\", (elm),\t\t\\\n\t\t    __FILE__, __LINE__);\n#define\tQUEUEDEBUG_LIST_POSTREMOVE(elm, field)\t\t\t\t\\\n\t(elm)->field.le_next = (void *)1L;\t\t\t\t\\\n\t(elm)->field.le_prev = (void *)1L;\n#else\n#define\tQUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field)\n#define\tQUEUEDEBUG_LIST_OP(elm, field)\n#define\tQUEUEDEBUG_LIST_POSTREMOVE(elm, field)\n#endif\n\n#define\tLIST_INIT(head) do {\t\t\t\t\t\t\\\n\t(head)->lh_first = LIST_END(head);\t\t\t\t\\\n} while (0)\n\n#define\tLIST_INSERT_AFTER(listelm, elm, field) do {\t\t\t\\\n\tQUEUEDEBUG_LIST_OP((listelm), field)\t\t\t\t\\\n\tif (((elm)->field.le_next = (listelm)->field.le_next) != \t\\\n\t    LIST_END(head))\t\t\t\t\t\t\\\n\t\t(listelm)->field.le_next->field.le_prev =\t\t\\\n\t\t    &(elm)->field.le_next;\t\t\t\t\\\n\t(listelm)->field.le_next = (elm);\t\t\t\t\\\n\t(elm)->field.le_prev = &(listelm)->field.le_next;\t\t\\\n} while (0)\n\n#define\tLIST_INSERT_BEFORE(listelm, elm, field) do {\t\t\t\\\n\tQUEUEDEBUG_LIST_OP((listelm), field)\t\t\t\t\\\n\t(elm)->field.le_prev = (listelm)->field.le_prev;\t\t\\\n\t(elm)->field.le_next = (listelm);\t\t\t\t\\\n\t*(listelm)->field.le_prev = (elm);\t\t\t\t\\\n\t(listelm)->field.le_prev = &(elm)->field.le_next;\t\t\\\n} while (0)\n\n#define\tLIST_INSERT_HEAD(head, elm, field) do {\t\t\t\t\\\n\tQUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field)\t\t\\\n\tif (((elm)->field.le_next = (head)->lh_first) != LIST_END(head))\\\n\t\t(head)->lh_first->field.le_prev = &(elm)->field.le_next;\\\n\t(head)->lh_first = (elm);\t\t\t\t\t\\\n\t(elm)->field.le_prev = &(head)->lh_first;\t\t\t\\\n} while (0)\n\n#define\tLIST_REMOVE(elm, field) do {\t\t\t\t\t\\\n\tQUEUEDEBUG_LIST_OP((elm), field)\t\t\t\t\\\n\tif ((elm)->field.le_next != NULL)\t\t\t\t\\\n\t\t(elm)->field.le_next->field.le_prev = \t\t\t\\\n\t\t    (elm)->field.le_prev;\t\t\t\t\\\n\t*(elm)->field.le_prev = (elm)->field.le_next;\t\t\t\\\n\tQUEUEDEBUG_LIST_POSTREMOVE((elm), field)\t\t\t\\\n} while (0)\n\n#define LIST_REPLACE(elm, elm2, field) do {\t\t\t\t\\\n\tif (((elm2)->field.le_next = (elm)->field.le_next) != NULL)\t\\\n\t\t(elm2)->field.le_next->field.le_prev =\t\t\t\\\n\t\t    &(elm2)->field.le_next;\t\t\t\t\\\n\t(elm2)->field.le_prev = (elm)->field.le_prev;\t\t\t\\\n\t*(elm2)->field.le_prev = (elm2);\t\t\t\t\\\n\tQUEUEDEBUG_LIST_POSTREMOVE((elm), field)\t\t\t\\\n} while (0)\n\n/*\n * Simple queue definitions.\n */\n#define\tSIMPLEQ_HEAD(name, type)\t\t\t\t\t\\\nstruct name {\t\t\t\t\t\t\t\t\\\n\tstruct type *sqh_first;\t/* first element */\t\t\t\\\n\tstruct type **sqh_last;\t/* addr of last next element */\t\t\\\n}\n\n#define\tSIMPLEQ_HEAD_INITIALIZER(head)\t\t\t\t\t\\\n\t{ NULL, &(head).sqh_first }\n\n#define\tSIMPLEQ_ENTRY(type)\t\t\t\t\t\t\\\nstruct {\t\t\t\t\t\t\t\t\\\n\tstruct type *sqe_next;\t/* next element */\t\t\t\\\n}\n\n/*\n * Simple queue access methods.\n */\n#define\tSIMPLEQ_FIRST(head)\t\t((head)->sqh_first)\n#define\tSIMPLEQ_END(head)\t\tNULL\n#define\tSIMPLEQ_EMPTY(head)\t\t((head)->sqh_first == SIMPLEQ_END(head))\n#define\tSIMPLEQ_NEXT(elm, field)\t((elm)->field.sqe_next)\n\n#define\tSIMPLEQ_FOREACH(var, head, field)\t\t\t\t\\\n\tfor ((var) = ((head)->sqh_first);\t\t\t\t\\\n\t    (var) != SIMPLEQ_END(head);\t\t\t\t\t\\\n\t    (var) = ((var)->field.sqe_next))\n\n#define\tSIMPLEQ_FOREACH_SAFE(var, head, field, next)\t\t\t\\\n\tfor ((var) = ((head)->sqh_first);\t\t\t\t\\\n\t    (var) != SIMPLEQ_END(head) &&\t\t\t\t\\\n\t    ((next = ((var)->field.sqe_next)), 1);\t\t\t\\\n\t    (var) = (next))\n\n/*\n * Simple queue functions.\n */\n#define\tSIMPLEQ_INIT(head) do {\t\t\t\t\t\t\\\n\t(head)->sqh_first = NULL;\t\t\t\t\t\\\n\t(head)->sqh_last = &(head)->sqh_first;\t\t\t\t\\\n} while (0)\n\n#define\tSIMPLEQ_INSERT_HEAD(head, elm, field) do {\t\t\t\\\n\tif (((elm)->field.sqe_next = (head)->sqh_first) == NULL)\t\\\n\t\t(head)->sqh_last = &(elm)->field.sqe_next;\t\t\\\n\t(head)->sqh_first = (elm);\t\t\t\t\t\\\n} while (0)\n\n#define\tSIMPLEQ_INSERT_TAIL(head, elm, field) do {\t\t\t\\\n\t(elm)->field.sqe_next = NULL;\t\t\t\t\t\\\n\t*(head)->sqh_last = (elm);\t\t\t\t\t\\\n\t(head)->sqh_last = &(elm)->field.sqe_next;\t\t\t\\\n} while (0)\n\n#define\tSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do {\t\t\\\n\tif (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\\\n\t\t(head)->sqh_last = &(elm)->field.sqe_next;\t\t\\\n\t(listelm)->field.sqe_next = (elm);\t\t\t\t\\\n} while (0)\n\n#define\tSIMPLEQ_REMOVE_HEAD(head, field) do {\t\t\t\t\\\n\tif (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \\\n\t\t(head)->sqh_last = &(head)->sqh_first;\t\t\t\\\n} while (0)\n\n#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do {\t\t\t\\\n\tif (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \\\n\t    == NULL)\t\t\t\t\t\t\t\\\n\t\t(head)->sqh_last = &(elm)->field.sqe_next;\t\t\\\n} while (0)\n\n#define\tSIMPLEQ_REMOVE(head, elm, type, field) do {\t\t\t\\\n\tif ((head)->sqh_first == (elm)) {\t\t\t\t\\\n\t\tSIMPLEQ_REMOVE_HEAD((head), field);\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tstruct type *curelm = (head)->sqh_first;\t\t\\\n\t\twhile (curelm->field.sqe_next != (elm))\t\t\t\\\n\t\t\tcurelm = curelm->field.sqe_next;\t\t\\\n\t\tif ((curelm->field.sqe_next =\t\t\t\t\\\n\t\t\tcurelm->field.sqe_next->field.sqe_next) == NULL) \\\n\t\t\t    (head)->sqh_last = &(curelm)->field.sqe_next; \\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define\tSIMPLEQ_CONCAT(head1, head2) do {\t\t\t\t\\\n\tif (!SIMPLEQ_EMPTY((head2))) {\t\t\t\t\t\\\n\t\t*(head1)->sqh_last = (head2)->sqh_first;\t\t\\\n\t\t(head1)->sqh_last = (head2)->sqh_last;\t\t\\\n\t\tSIMPLEQ_INIT((head2));\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define\tSIMPLEQ_LAST(head, type, field)\t\t\t\t\t\\\n\t(SIMPLEQ_EMPTY((head)) ?\t\t\t\t\t\t\\\n\t\tNULL :\t\t\t\t\t\t\t\\\n\t        ((struct type *)(void *)\t\t\t\t\\\n\t\t((char *)((head)->sqh_last) - offsetof(struct type, field))))\n\n/*\n * Tail queue definitions.\n */\n#define\t_TAILQ_HEAD(name, type, qual)\t\t\t\t\t\\\nstruct name {\t\t\t\t\t\t\t\t\\\n\tqual type *tqh_first;\t\t/* first element */\t\t\\\n\tqual type *qual *tqh_last;\t/* addr of last next element */\t\\\n}\n#define TAILQ_HEAD(name, type)\t_TAILQ_HEAD(name, struct type,)\n\n#define\tTAILQ_HEAD_INITIALIZER(head)\t\t\t\t\t\\\n\t{ TAILQ_END(head), &(head).tqh_first }\n\n#define\t_TAILQ_ENTRY(type, qual)\t\t\t\t\t\\\nstruct {\t\t\t\t\t\t\t\t\\\n\tqual type *tqe_next;\t\t/* next element */\t\t\\\n\tqual type *qual *tqe_prev;\t/* address of previous next element */\\\n}\n#define TAILQ_ENTRY(type)\t_TAILQ_ENTRY(struct type,)\n\n/*\n * Tail queue access methods.\n */\n#define\tTAILQ_FIRST(head)\t\t((head)->tqh_first)\n#define\tTAILQ_END(head)\t\t\t(NULL)\n#define\tTAILQ_NEXT(elm, field)\t\t((elm)->field.tqe_next)\n#define\tTAILQ_LAST(head, headname) \\\n\t(*(((struct headname *)(void *)((head)->tqh_last))->tqh_last))\n#define\tTAILQ_PREV(elm, headname, field) \\\n\t(*(((struct headname *)(void *)((elm)->field.tqe_prev))->tqh_last))\n#define\tTAILQ_EMPTY(head)\t\t(TAILQ_FIRST(head) == TAILQ_END(head))\n\n\n#define\tTAILQ_FOREACH(var, head, field)\t\t\t\t\t\\\n\tfor ((var) = ((head)->tqh_first);\t\t\t\t\\\n\t    (var) != TAILQ_END(head);\t\t\t\t\t\\\n\t    (var) = ((var)->field.tqe_next))\n\n#define\tTAILQ_FOREACH_SAFE(var, head, field, next)\t\t\t\\\n\tfor ((var) = ((head)->tqh_first);\t\t\t\t\\\n\t    (var) != TAILQ_END(head) &&\t\t\t\t\t\\\n\t    ((next) = TAILQ_NEXT(var, field), 1); (var) = (next))\n\n#define\tTAILQ_FOREACH_REVERSE(var, head, headname, field)\t\t\\\n\tfor ((var) = TAILQ_LAST((head), headname);\t\t\t\\\n\t    (var) != TAILQ_END(head);\t\t\t\t\t\\\n\t    (var) = TAILQ_PREV((var), headname, field))\n\n#define\tTAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, prev)\t\\\n\tfor ((var) = TAILQ_LAST((head), headname);\t\t\t\\\n\t    (var) != TAILQ_END(head) && \t\t\t\t\\\n\t    ((prev) = TAILQ_PREV((var), headname, field), 1); (var) = (prev))\n\n/*\n * Tail queue functions.\n */\n#if defined(QUEUEDEBUG)\n#define\tQUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field)\t\t\t\\\n\tif ((head)->tqh_first &&\t\t\t\t\t\\\n\t    (head)->tqh_first->field.tqe_prev != &(head)->tqh_first)\t\\\n\t\tQUEUEDEBUG_ABORT(\"TAILQ_INSERT_HEAD %p %s:%d\", (head),\t\\\n\t\t    __FILE__, __LINE__);\n#define\tQUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field)\t\t\t\\\n\tif (*(head)->tqh_last != NULL)\t\t\t\t\t\\\n\t\tQUEUEDEBUG_ABORT(\"TAILQ_INSERT_TAIL %p %s:%d\", (head),\t\\\n\t\t    __FILE__, __LINE__);\n#define\tQUEUEDEBUG_TAILQ_OP(elm, field)\t\t\t\t\t\\\n\tif ((elm)->field.tqe_next &&\t\t\t\t\t\\\n\t    (elm)->field.tqe_next->field.tqe_prev !=\t\t\t\\\n\t    &(elm)->field.tqe_next)\t\t\t\t\t\\\n\t\tQUEUEDEBUG_ABORT(\"TAILQ_* forw %p %s:%d\", (elm),\t\\\n\t\t    __FILE__, __LINE__);\t\t\t\t\\\n\tif (*(elm)->field.tqe_prev != (elm))\t\t\t\t\\\n\t\tQUEUEDEBUG_ABORT(\"TAILQ_* back %p %s:%d\", (elm),\t\\\n\t\t    __FILE__, __LINE__);\n#define\tQUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field)\t\t\t\\\n\tif ((elm)->field.tqe_next == NULL &&\t\t\t\t\\\n\t    (head)->tqh_last != &(elm)->field.tqe_next)\t\t\t\\\n\t\tQUEUEDEBUG_ABORT(\"TAILQ_PREREMOVE head %p elm %p %s:%d\",\\\n\t\t    (head), (elm), __FILE__, __LINE__);\n#define\tQUEUEDEBUG_TAILQ_POSTREMOVE(elm, field)\t\t\t\t\\\n\t(elm)->field.tqe_next = (void *)1L;\t\t\t\t\\\n\t(elm)->field.tqe_prev = (void *)1L;\n#else\n#define\tQUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field)\n#define\tQUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field)\n#define\tQUEUEDEBUG_TAILQ_OP(elm, field)\n#define\tQUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field)\n#define\tQUEUEDEBUG_TAILQ_POSTREMOVE(elm, field)\n#endif\n\n#define\tTAILQ_INIT(head) do {\t\t\t\t\t\t\\\n\t(head)->tqh_first = TAILQ_END(head);\t\t\t\t\\\n\t(head)->tqh_last = &(head)->tqh_first;\t\t\t\t\\\n} while (0)\n\n#define\tTAILQ_INSERT_HEAD(head, elm, field) do {\t\t\t\\\n\tQUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field)\t\t\\\n\tif (((elm)->field.tqe_next = (head)->tqh_first) != TAILQ_END(head))\\\n\t\t(head)->tqh_first->field.tqe_prev =\t\t\t\\\n\t\t    &(elm)->field.tqe_next;\t\t\t\t\\\n\telse\t\t\t\t\t\t\t\t\\\n\t\t(head)->tqh_last = &(elm)->field.tqe_next;\t\t\\\n\t(head)->tqh_first = (elm);\t\t\t\t\t\\\n\t(elm)->field.tqe_prev = &(head)->tqh_first;\t\t\t\\\n} while (0)\n\n#define\tTAILQ_INSERT_TAIL(head, elm, field) do {\t\t\t\\\n\tQUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field)\t\t\\\n\t(elm)->field.tqe_next = TAILQ_END(head);\t\t\t\\\n\t(elm)->field.tqe_prev = (head)->tqh_last;\t\t\t\\\n\t*(head)->tqh_last = (elm);\t\t\t\t\t\\\n\t(head)->tqh_last = &(elm)->field.tqe_next;\t\t\t\\\n} while (0)\n\n#define\tTAILQ_INSERT_AFTER(head, listelm, elm, field) do {\t\t\\\n\tQUEUEDEBUG_TAILQ_OP((listelm), field)\t\t\t\t\\\n\tif (((elm)->field.tqe_next = (listelm)->field.tqe_next) != \t\\\n\t    TAILQ_END(head))\t\t\t\t\t\t\\\n\t\t(elm)->field.tqe_next->field.tqe_prev = \t\t\\\n\t\t    &(elm)->field.tqe_next;\t\t\t\t\\\n\telse\t\t\t\t\t\t\t\t\\\n\t\t(head)->tqh_last = &(elm)->field.tqe_next;\t\t\\\n\t(listelm)->field.tqe_next = (elm);\t\t\t\t\\\n\t(elm)->field.tqe_prev = &(listelm)->field.tqe_next;\t\t\\\n} while (0)\n\n#define\tTAILQ_INSERT_BEFORE(listelm, elm, field) do {\t\t\t\\\n\tQUEUEDEBUG_TAILQ_OP((listelm), field)\t\t\t\t\\\n\t(elm)->field.tqe_prev = (listelm)->field.tqe_prev;\t\t\\\n\t(elm)->field.tqe_next = (listelm);\t\t\t\t\\\n\t*(listelm)->field.tqe_prev = (elm);\t\t\t\t\\\n\t(listelm)->field.tqe_prev = &(elm)->field.tqe_next;\t\t\\\n} while (0)\n\n#define\tTAILQ_REMOVE(head, elm, field) do {\t\t\t\t\\\n\tQUEUEDEBUG_TAILQ_PREREMOVE((head), (elm), field)\t\t\\\n\tQUEUEDEBUG_TAILQ_OP((elm), field)\t\t\t\t\\\n\tif (((elm)->field.tqe_next) != TAILQ_END(head))\t\t\t\\\n\t\t(elm)->field.tqe_next->field.tqe_prev = \t\t\\\n\t\t    (elm)->field.tqe_prev;\t\t\t\t\\\n\telse\t\t\t\t\t\t\t\t\\\n\t\t(head)->tqh_last = (elm)->field.tqe_prev;\t\t\\\n\t*(elm)->field.tqe_prev = (elm)->field.tqe_next;\t\t\t\\\n\tQUEUEDEBUG_TAILQ_POSTREMOVE((elm), field);\t\t\t\\\n} while (0)\n\n#define TAILQ_REPLACE(head, elm, elm2, field) do {\t\t\t\\\n        if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != \t\\\n\t    TAILQ_END(head))   \t\t\t\t\t\t\\\n                (elm2)->field.tqe_next->field.tqe_prev =\t\t\\\n                    &(elm2)->field.tqe_next;\t\t\t\t\\\n        else\t\t\t\t\t\t\t\t\\\n                (head)->tqh_last = &(elm2)->field.tqe_next;\t\t\\\n        (elm2)->field.tqe_prev = (elm)->field.tqe_prev;\t\t\t\\\n        *(elm2)->field.tqe_prev = (elm2);\t\t\t\t\\\n\tQUEUEDEBUG_TAILQ_POSTREMOVE((elm), field);\t\t\t\\\n} while (0)\n\n#define\tTAILQ_CONCAT(head1, head2, field) do {\t\t\t\t\\\n\tif (!TAILQ_EMPTY(head2)) {\t\t\t\t\t\\\n\t\t*(head1)->tqh_last = (head2)->tqh_first;\t\t\\\n\t\t(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last;\t\\\n\t\t(head1)->tqh_last = (head2)->tqh_last;\t\t\t\\\n\t\tTAILQ_INIT((head2));\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n/*\n * Singly-linked Tail queue declarations.\n */\n#define\tSTAILQ_HEAD(name, type)\t\t\t\t\t\t\\\nstruct name {\t\t\t\t\t\t\t\t\\\n\tstruct type *stqh_first;\t/* first element */\t\t\\\n\tstruct type **stqh_last;\t/* addr of last next element */\t\\\n}\n\n#define\tSTAILQ_HEAD_INITIALIZER(head)\t\t\t\t\t\\\n\t{ NULL, &(head).stqh_first }\n\n#define\tSTAILQ_ENTRY(type)\t\t\t\t\t\t\\\nstruct {\t\t\t\t\t\t\t\t\\\n\tstruct type *stqe_next;\t/* next element */\t\t\t\\\n}\n\n/*\n * Singly-linked Tail queue access methods.\n */\n#define\tSTAILQ_FIRST(head)\t((head)->stqh_first)\n#define\tSTAILQ_END(head)\tNULL\n#define\tSTAILQ_NEXT(elm, field)\t((elm)->field.stqe_next)\n#define\tSTAILQ_EMPTY(head)\t(STAILQ_FIRST(head) == STAILQ_END(head))\n\n/*\n * Singly-linked Tail queue functions.\n */\n#define\tSTAILQ_INIT(head) do {\t\t\t\t\t\t\\\n\t(head)->stqh_first = NULL;\t\t\t\t\t\\\n\t(head)->stqh_last = &(head)->stqh_first;\t\t\t\t\\\n} while (0)\n\n#define\tSTAILQ_INSERT_HEAD(head, elm, field) do {\t\t\t\\\n\tif (((elm)->field.stqe_next = (head)->stqh_first) == NULL)\t\\\n\t\t(head)->stqh_last = &(elm)->field.stqe_next;\t\t\\\n\t(head)->stqh_first = (elm);\t\t\t\t\t\\\n} while (0)\n\n#define\tSTAILQ_INSERT_TAIL(head, elm, field) do {\t\t\t\\\n\t(elm)->field.stqe_next = NULL;\t\t\t\t\t\\\n\t*(head)->stqh_last = (elm);\t\t\t\t\t\\\n\t(head)->stqh_last = &(elm)->field.stqe_next;\t\t\t\\\n} while (0)\n\n#define\tSTAILQ_INSERT_AFTER(head, listelm, elm, field) do {\t\t\\\n\tif (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\\\n\t\t(head)->stqh_last = &(elm)->field.stqe_next;\t\t\\\n\t(listelm)->field.stqe_next = (elm);\t\t\t\t\\\n} while (0)\n\n#define\tSTAILQ_REMOVE_HEAD(head, field) do {\t\t\t\t\\\n\tif (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \\\n\t\t(head)->stqh_last = &(head)->stqh_first;\t\t\t\\\n} while (0)\n\n#define\tSTAILQ_REMOVE(head, elm, type, field) do {\t\t\t\\\n\tif ((head)->stqh_first == (elm)) {\t\t\t\t\\\n\t\tSTAILQ_REMOVE_HEAD((head), field);\t\t\t\\\n\t} else {\t\t\t\t\t\t\t\\\n\t\tstruct type *curelm = (head)->stqh_first;\t\t\\\n\t\twhile (curelm->field.stqe_next != (elm))\t\t\t\\\n\t\t\tcurelm = curelm->field.stqe_next;\t\t\\\n\t\tif ((curelm->field.stqe_next =\t\t\t\t\\\n\t\t\tcurelm->field.stqe_next->field.stqe_next) == NULL) \\\n\t\t\t    (head)->stqh_last = &(curelm)->field.stqe_next; \\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define\tSTAILQ_FOREACH(var, head, field)\t\t\t\t\\\n\tfor ((var) = ((head)->stqh_first);\t\t\t\t\\\n\t\t(var);\t\t\t\t\t\t\t\\\n\t\t(var) = ((var)->field.stqe_next))\n\n#define\tSTAILQ_FOREACH_SAFE(var, head, field, tvar)\t\t\t\\\n\tfor ((var) = STAILQ_FIRST((head));\t\t\t\t\\\n\t    (var) && ((tvar) = STAILQ_NEXT((var), field), 1);\t\t\\\n\t    (var) = (tvar))\n\n#define\tSTAILQ_CONCAT(head1, head2) do {\t\t\t\t\\\n\tif (!STAILQ_EMPTY((head2))) {\t\t\t\t\t\\\n\t\t*(head1)->stqh_last = (head2)->stqh_first;\t\t\\\n\t\t(head1)->stqh_last = (head2)->stqh_last;\t\t\\\n\t\tSTAILQ_INIT((head2));\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} while (0)\n\n#define\tSTAILQ_LAST(head, type, field)\t\t\t\t\t\\\n\t(STAILQ_EMPTY((head)) ?\t\t\t\t\t\t\\\n\t\tNULL :\t\t\t\t\t\t\t\\\n\t        ((struct type *)(void *)\t\t\t\t\\\n\t\t((char *)((head)->stqh_last) - offsetof(struct type, field))))\n\n#endif\t/* !_SYS_QUEUE_H_ */\n"
  },
  {
    "path": "vendor/rbtree.c",
    "content": "/*\t$NetBSD: rbtree.c,v 1.2 2025/10/29 08:08:44 roy Exp $\t*/\n\n/*-\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2001 The NetBSD Foundation, Inc.\n * All rights reserved.\n *\n * This code is derived from software contributed to The NetBSD Foundation\n * by Matt Thomas <matt@3am-software.com>.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS\n * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\n * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS\n * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#if defined(HAVE_NBTOOL_CONFIG_H) && HAVE_NBTOOL_CONFIG_H\n#include \"nbtool_config.h\"\n#endif\n\n#if !defined(_KERNEL) && !defined(_STANDALONE)\n#include <sys/types.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdint.h>\n#ifndef __predict_false\n#define __predict_false(exp)\t(exp)\n#endif\n#ifdef RBDEBUG\n#include <assert.h>\n#define\tKASSERT(s)\tassert(s)\n#else\n#define\tKASSERT(s)\tdo { } while (/*CONSTCOND*/ 0)\n#ifdef __unused\n#define\t__rbt_unused\t__unused\n#endif\n#endif\n#ifdef __RCSID\n__RCSID(\"$NetBSD: rbtree.c,v 1.2 2025/10/29 08:08:44 roy Exp $\");\n#endif\n#else\n#include <lib/libkern/libkern.h>\n__KERNEL_RCSID(0, \"$NetBSD: rbtree.c,v 1.2 2025/10/29 08:08:44 roy Exp $\");\n#ifndef DIAGNOSTIC\n#define\t__rbt_unused\t__unused\n#endif\n#endif\n\n#ifndef __rbt_unused\n#define\t__rbt_unused\n#endif\n\n#ifdef _LIBC\n__weak_alias(rb_tree_init, _rb_tree_init)\n__weak_alias(rb_tree_find_node, _rb_tree_find_node)\n__weak_alias(rb_tree_find_node_geq, _rb_tree_find_node_geq)\n__weak_alias(rb_tree_find_node_leq, _rb_tree_find_node_leq)\n__weak_alias(rb_tree_insert_node, _rb_tree_insert_node)\n__weak_alias(rb_tree_remove_node, _rb_tree_remove_node)\n__weak_alias(rb_tree_iterate, _rb_tree_iterate)\n#ifdef RBDEBUG\n__weak_alias(rb_tree_check, _rb_tree_check)\n__weak_alias(rb_tree_depths, _rb_tree_depths)\n#endif\n\n#include \"namespace.h\"\n#endif\n\n#ifdef RBLOCAL\n#include \"rbtree.h\"\n#else\n#include <sys/rbtree.h>\n#endif\n\nstatic void rb_tree_insert_rebalance(struct rb_tree *, struct rb_node *);\nstatic void rb_tree_removal_rebalance(struct rb_tree *, struct rb_node *,\n\tunsigned int);\n#ifdef RBDEBUG\nstatic const struct rb_node *rb_tree_iterate_const(const struct rb_tree *,\n\tconst struct rb_node *, const unsigned int);\nstatic bool rb_tree_check_node(const struct rb_tree *, const struct rb_node *,\n\tconst struct rb_node *, bool);\n#else\n#define\trb_tree_check_node(a, b, c, d)\ttrue\n#endif\n\n#define\tRB_NODETOITEM(rbto, rbn)\t\\\n    ((void *)((uintptr_t)(rbn) - (rbto)->rbto_node_offset))\n#define\tRB_ITEMTONODE(rbto, rbn)\t\\\n    ((rb_node_t *)((uintptr_t)(rbn) + (rbto)->rbto_node_offset))\n\n#define\tRB_SENTINEL_NODE\tNULL\n\nvoid\nrb_tree_init(struct rb_tree *rbt, const rb_tree_ops_t *ops)\n{\n\n\trbt->rbt_ops = ops;\n\trbt->rbt_root = RB_SENTINEL_NODE;\n\tRB_TAILQ_INIT(&rbt->rbt_nodes);\n#ifndef RBSMALL\n\trbt->rbt_minmax[RB_DIR_LEFT] = rbt->rbt_root;\t/* minimum node */\n\trbt->rbt_minmax[RB_DIR_RIGHT] = rbt->rbt_root;\t/* maximum node */\n#endif\n#ifdef RBSTATS\n\trbt->rbt_count = 0;\n\trbt->rbt_insertions = 0;\n\trbt->rbt_removals = 0;\n\trbt->rbt_insertion_rebalance_calls = 0;\n\trbt->rbt_insertion_rebalance_passes = 0;\n\trbt->rbt_removal_rebalance_calls = 0;\n\trbt->rbt_removal_rebalance_passes = 0;\n#endif\n}\n\nvoid *\nrb_tree_find_node(struct rb_tree *rbt, const void *key)\n{\n\tconst rb_tree_ops_t *rbto = rbt->rbt_ops;\n\trbto_compare_key_fn compare_key = rbto->rbto_compare_key;\n\tstruct rb_node *parent = rbt->rbt_root;\n\n\twhile (!RB_SENTINEL_P(parent)) {\n\t\tvoid *pobj = RB_NODETOITEM(rbto, parent);\n\t\tconst signed int diff = (*compare_key)(rbto->rbto_context,\n\t\t    pobj, key);\n\t\tif (diff == 0)\n\t\t\treturn pobj;\n\t\tparent = parent->rb_nodes[diff < 0];\n\t}\n\n\treturn NULL;\n}\n\nvoid *\nrb_tree_find_node_geq(struct rb_tree *rbt, const void *key)\n{\n\tconst rb_tree_ops_t *rbto = rbt->rbt_ops;\n\trbto_compare_key_fn compare_key = rbto->rbto_compare_key;\n\tstruct rb_node *parent = rbt->rbt_root, *last = NULL;\n\n\twhile (!RB_SENTINEL_P(parent)) {\n\t\tvoid *pobj = RB_NODETOITEM(rbto, parent);\n\t\tconst signed int diff = (*compare_key)(rbto->rbto_context,\n\t\t    pobj, key);\n\t\tif (diff == 0)\n\t\t\treturn pobj;\n\t\tif (diff > 0)\n\t\t\tlast = parent;\n\t\tparent = parent->rb_nodes[diff < 0];\n\t}\n\n\treturn last == NULL ? NULL : RB_NODETOITEM(rbto, last);\n}\n\nvoid *\nrb_tree_find_node_leq(struct rb_tree *rbt, const void *key)\n{\n\tconst rb_tree_ops_t *rbto = rbt->rbt_ops;\n\trbto_compare_key_fn compare_key = rbto->rbto_compare_key;\n\tstruct rb_node *parent = rbt->rbt_root, *last = NULL;\n\n\twhile (!RB_SENTINEL_P(parent)) {\n\t\tvoid *pobj = RB_NODETOITEM(rbto, parent);\n\t\tconst signed int diff = (*compare_key)(rbto->rbto_context,\n\t\t    pobj, key);\n\t\tif (diff == 0)\n\t\t\treturn pobj;\n\t\tif (diff < 0)\n\t\t\tlast = parent;\n\t\tparent = parent->rb_nodes[diff < 0];\n\t}\n\n\treturn last == NULL ? NULL : RB_NODETOITEM(rbto, last);\n}\n\nvoid *\nrb_tree_insert_node(struct rb_tree *rbt, void *object)\n{\n\tconst rb_tree_ops_t *rbto = rbt->rbt_ops;\n\trbto_compare_nodes_fn compare_nodes = rbto->rbto_compare_nodes;\n\tstruct rb_node *parent, *tmp, *self = RB_ITEMTONODE(rbto, object);\n\tunsigned int position;\n\tbool rebalance;\n\n\tRBSTAT_INC(rbt->rbt_insertions);\n\n\ttmp = rbt->rbt_root;\n\t/*\n\t * This is a hack.  Because rbt->rbt_root is just a struct rb_node *,\n\t * just like rb_node->rb_nodes[RB_DIR_LEFT], we can use this fact to\n\t * avoid a lot of tests for root and know that even at root,\n\t * updating RB_FATHER(rb_node)->rb_nodes[RB_POSITION(rb_node)] will\n\t * update rbt->rbt_root.\n\t */\n\tparent = (struct rb_node *)(void *)&rbt->rbt_root;\n\tposition = RB_DIR_LEFT;\n\n\t/*\n\t * Find out where to place this new leaf.\n\t */\n\twhile (!RB_SENTINEL_P(tmp)) {\n\t\tvoid *tobj = RB_NODETOITEM(rbto, tmp);\n\t\tconst signed int diff = (*compare_nodes)(rbto->rbto_context,\n\t\t    tobj, object);\n\t\tif (__predict_false(diff == 0)) {\n\t\t\t/*\n\t\t\t * Node already exists; return it.\n\t\t\t */\n\t\t\treturn tobj;\n\t\t}\n\t\tparent = tmp;\n\t\tposition = (diff < 0);\n\t\ttmp = parent->rb_nodes[position];\n\t}\n\n#ifdef RBDEBUG\n\t{\n\t\tstruct rb_node *prev = NULL, *next = NULL;\n\n\t\tif (position == RB_DIR_RIGHT)\n\t\t\tprev = parent;\n\t\telse if (tmp != rbt->rbt_root)\n\t\t\tnext = parent;\n\n\t\t/*\n\t\t * Verify our sequential position\n\t\t */\n\t\tKASSERT(prev == NULL || !RB_SENTINEL_P(prev));\n\t\tKASSERT(next == NULL || !RB_SENTINEL_P(next));\n\t\tif (prev != NULL && next == NULL)\n\t\t\tnext = TAILQ_NEXT(prev, rb_link);\n\t\tif (prev == NULL && next != NULL)\n\t\t\tprev = TAILQ_PREV(next, rb_node_qh, rb_link);\n\t\tKASSERT(prev == NULL || !RB_SENTINEL_P(prev));\n\t\tKASSERT(next == NULL || !RB_SENTINEL_P(next));\n\t\tKASSERT(prev == NULL || (*compare_nodes)(rbto->rbto_context,\n\t\t    RB_NODETOITEM(rbto, prev), RB_NODETOITEM(rbto, self)) < 0);\n\t\tKASSERT(next == NULL || (*compare_nodes)(rbto->rbto_context,\n\t\t    RB_NODETOITEM(rbto, self), RB_NODETOITEM(rbto, next)) < 0);\n\t}\n#endif\n\n\t/*\n\t * Initialize the node and insert as a leaf into the tree.\n\t */\n\tRB_SET_FATHER(self, parent);\n\tRB_SET_POSITION(self, position);\n\tif (__predict_false(parent == (struct rb_node *)(void *)&rbt->rbt_root)) {\n\t\tRB_MARK_BLACK(self);\t\t/* root is always black */\n#ifndef RBSMALL\n\t\trbt->rbt_minmax[RB_DIR_LEFT] = self;\n\t\trbt->rbt_minmax[RB_DIR_RIGHT] = self;\n#endif\n\t\trebalance = false;\n\t} else {\n\t\tKASSERT(position == RB_DIR_LEFT || position == RB_DIR_RIGHT);\n#ifndef RBSMALL\n\t\t/*\n\t\t * Keep track of the minimum and maximum nodes.  If our\n\t\t * parent is a minmax node and we on their min/max side,\n\t\t * we must be the new min/max node.\n\t\t */\n\t\tif (parent == rbt->rbt_minmax[position])\n\t\t\trbt->rbt_minmax[position] = self;\n#endif /* !RBSMALL */\n\t\t/*\n\t\t * All new nodes are colored red.  We only need to rebalance\n\t\t * if our parent is also red.\n\t\t */\n\t\tRB_MARK_RED(self);\n\t\trebalance = RB_RED_P(parent);\n\t}\n\tKASSERT(RB_SENTINEL_P(parent->rb_nodes[position]));\n\tself->rb_left = parent->rb_nodes[position];\n\tself->rb_right = parent->rb_nodes[position];\n\tparent->rb_nodes[position] = self;\n\tKASSERT(RB_CHILDLESS_P(self));\n\n\t/*\n\t * Insert the new node into a sorted list for easy sequential access\n\t */\n\tRBSTAT_INC(rbt->rbt_count);\n#ifdef RBDEBUG\n\tif (RB_ROOT_P(rbt, self)) {\n\t\tRB_TAILQ_INSERT_HEAD(&rbt->rbt_nodes, self, rb_link);\n\t} else if (position == RB_DIR_LEFT) {\n\t\tKASSERT((*compare_nodes)(rbto->rbto_context,\n\t\t    RB_NODETOITEM(rbto, self),\n\t\t    RB_NODETOITEM(rbto, RB_FATHER(self))) < 0);\n\t\tRB_TAILQ_INSERT_BEFORE(RB_FATHER(self), self, rb_link);\n\t} else {\n\t\tKASSERT((*compare_nodes)(rbto->rbto_context,\n\t\t    RB_NODETOITEM(rbto, RB_FATHER(self)),\n\t\t    RB_NODETOITEM(rbto, self)) < 0);\n\t\tRB_TAILQ_INSERT_AFTER(&rbt->rbt_nodes, RB_FATHER(self),\n\t\t    self, rb_link);\n\t}\n#endif\n\tKASSERT(rb_tree_check_node(rbt, self, NULL, !rebalance));\n\n\t/*\n\t * Rebalance tree after insertion\n\t */\n\tif (rebalance) {\n\t\trb_tree_insert_rebalance(rbt, self);\n\t\tKASSERT(rb_tree_check_node(rbt, self, NULL, true));\n\t}\n\n\t/* Successfully inserted, return our node pointer. */\n\treturn object;\n}\n\n/*\n * Swap the location and colors of 'self' and its child @ which.  The child\n * can not be a sentinel node.  This is our rotation function.  However,\n * since it preserves coloring, it great simplifies both insertion and\n * removal since rotation almost always involves the exchanging of colors\n * as a separate step.\n */\nstatic void\nrb_tree_reparent_nodes(__rbt_unused struct rb_tree *rbt,\n\tstruct rb_node *old_father, const unsigned int which)\n{\n\tconst unsigned int other = which ^ RB_DIR_OTHER;\n\tstruct rb_node * const grandpa = RB_FATHER(old_father);\n\tstruct rb_node * const old_child = old_father->rb_nodes[which];\n\tstruct rb_node * const new_father = old_child;\n\tstruct rb_node * const new_child = old_father;\n\n\tKASSERT(which == RB_DIR_LEFT || which == RB_DIR_RIGHT);\n\n\tKASSERT(!RB_SENTINEL_P(old_child));\n\tKASSERT(RB_FATHER(old_child) == old_father);\n\n\tKASSERT(rb_tree_check_node(rbt, old_father, NULL, false));\n\tKASSERT(rb_tree_check_node(rbt, old_child, NULL, false));\n\tKASSERT(RB_ROOT_P(rbt, old_father) ||\n\t    rb_tree_check_node(rbt, grandpa, NULL, false));\n\n\t/*\n\t * Exchange descendant linkages.\n\t */\n\tgrandpa->rb_nodes[RB_POSITION(old_father)] = new_father;\n\tnew_child->rb_nodes[which] = old_child->rb_nodes[other];\n\tnew_father->rb_nodes[other] = new_child;\n\n\t/*\n\t * Update ancestor linkages\n\t */\n\tRB_SET_FATHER(new_father, grandpa);\n\tRB_SET_FATHER(new_child, new_father);\n\n\t/*\n\t * Exchange properties between new_father and new_child.  The only\n\t * change is that new_child's position is now on the other side.\n\t */\n#if 0\n\t{\n\t\tstruct rb_node tmp;\n\t\ttmp.rb_info = 0;\n\t\tRB_COPY_PROPERTIES(&tmp, old_child);\n\t\tRB_COPY_PROPERTIES(new_father, old_father);\n\t\tRB_COPY_PROPERTIES(new_child, &tmp);\n\t}\n#else\n\tRB_SWAP_PROPERTIES(new_father, new_child);\n#endif\n\tRB_SET_POSITION(new_child, other);\n\n\t/*\n\t * Make sure to reparent the new child to ourself.\n\t */\n\tif (!RB_SENTINEL_P(new_child->rb_nodes[which])) {\n\t\tRB_SET_FATHER(new_child->rb_nodes[which], new_child);\n\t\tRB_SET_POSITION(new_child->rb_nodes[which], which);\n\t}\n\n\tKASSERT(rb_tree_check_node(rbt, new_father, NULL, false));\n\tKASSERT(rb_tree_check_node(rbt, new_child, NULL, false));\n\tKASSERT(RB_ROOT_P(rbt, new_father) ||\n\t    rb_tree_check_node(rbt, grandpa, NULL, false));\n}\n\nstatic void\nrb_tree_insert_rebalance(struct rb_tree *rbt, struct rb_node *self)\n{\n\tstruct rb_node * father = RB_FATHER(self);\n\tstruct rb_node * grandpa = RB_FATHER(father);\n\tstruct rb_node * uncle;\n\tunsigned int which;\n\tunsigned int other;\n\n\tKASSERT(!RB_ROOT_P(rbt, self));\n\tKASSERT(RB_RED_P(self));\n\tKASSERT(RB_RED_P(father));\n\tRBSTAT_INC(rbt->rbt_insertion_rebalance_calls);\n\n\tfor (;;) {\n\t\tKASSERT(!RB_SENTINEL_P(self));\n\n\t\tKASSERT(RB_RED_P(self));\n\t\tKASSERT(RB_RED_P(father));\n\t\t/*\n\t\t * We are red and our parent is red, therefore we must have a\n\t\t * grandfather and he must be black.\n\t\t */\n\t\tgrandpa = RB_FATHER(father);\n\t\tKASSERT(RB_BLACK_P(grandpa));\n\t\tKASSERT(RB_DIR_RIGHT == 1 && RB_DIR_LEFT == 0);\n\t\twhich = (father == grandpa->rb_right);\n\t\tother = which ^ RB_DIR_OTHER;\n\t\tuncle = grandpa->rb_nodes[other];\n\n\t\tif (RB_BLACK_P(uncle))\n\t\t\tbreak;\n\n\t\tRBSTAT_INC(rbt->rbt_insertion_rebalance_passes);\n\t\t/*\n\t\t * Case 1: our uncle is red\n\t\t *   Simply invert the colors of our parent and\n\t\t *   uncle and make our grandparent red.  And\n\t\t *   then solve the problem up at his level.\n\t\t */\n\t\tRB_MARK_BLACK(uncle);\n\t\tRB_MARK_BLACK(father);\n\t\tif (__predict_false(RB_ROOT_P(rbt, grandpa))) {\n\t\t\t/*\n\t\t\t * If our grandpa is root, don't bother\n\t\t\t * setting him to red, just return.\n\t\t\t */\n\t\t\tKASSERT(RB_BLACK_P(grandpa));\n\t\t\treturn;\n\t\t}\n\t\tRB_MARK_RED(grandpa);\n\t\tself = grandpa;\n\t\tfather = RB_FATHER(self);\n\t\tKASSERT(RB_RED_P(self));\n\t\tif (RB_BLACK_P(father)) {\n\t\t\t/*\n\t\t\t * If our greatgrandpa is black, we're done.\n\t\t\t */\n\t\t\tKASSERT(RB_BLACK_P(rbt->rbt_root));\n\t\t\treturn;\n\t\t}\n\t}\n\n\tKASSERT(!RB_ROOT_P(rbt, self));\n\tKASSERT(RB_RED_P(self));\n\tKASSERT(RB_RED_P(father));\n\tKASSERT(RB_BLACK_P(uncle));\n\tKASSERT(RB_BLACK_P(grandpa));\n\t/*\n\t * Case 2&3: our uncle is black.\n\t */\n\tif (self == father->rb_nodes[other]) {\n\t\t/*\n\t\t * Case 2: we are on the same side as our uncle\n\t\t *   Swap ourselves with our parent so this case\n\t\t *   becomes case 3.  Basically our parent becomes our\n\t\t *   child.\n\t\t */\n\t\trb_tree_reparent_nodes(rbt, father, other);\n\t\tKASSERT(RB_FATHER(father) == self);\n\t\tKASSERT(self->rb_nodes[which] == father);\n\t\tKASSERT(RB_FATHER(self) == grandpa);\n\t\tself = father;\n\t\tfather = RB_FATHER(self);\n\t}\n\tKASSERT(RB_RED_P(self) && RB_RED_P(father));\n\tKASSERT(grandpa->rb_nodes[which] == father);\n\t/*\n\t * Case 3: we are opposite a child of a black uncle.\n\t *   Swap our parent and grandparent.  Since our grandfather\n\t *   is black, our father will become black and our new sibling\n\t *   (former grandparent) will become red.\n\t */\n\trb_tree_reparent_nodes(rbt, grandpa, which);\n\tKASSERT(RB_FATHER(self) == father);\n\tKASSERT(RB_FATHER(self)->rb_nodes[RB_POSITION(self) ^ RB_DIR_OTHER] == grandpa);\n\tKASSERT(RB_RED_P(self));\n\tKASSERT(RB_BLACK_P(father));\n\tKASSERT(RB_RED_P(grandpa));\n\n\t/*\n\t * Final step: Set the root to black.\n\t */\n\tRB_MARK_BLACK(rbt->rbt_root);\n}\n\nstatic void\nrb_tree_prune_node(struct rb_tree *rbt, struct rb_node *self, bool rebalance)\n{\n\tconst unsigned int which = RB_POSITION(self);\n\tstruct rb_node *father = RB_FATHER(self);\n#ifndef RBSMALL\n\tconst bool was_root = RB_ROOT_P(rbt, self);\n#endif\n\n\tKASSERT(rebalance || (RB_ROOT_P(rbt, self) || RB_RED_P(self)));\n\tKASSERT(!rebalance || RB_BLACK_P(self));\n\tKASSERT(RB_CHILDLESS_P(self));\n\tKASSERT(rb_tree_check_node(rbt, self, NULL, false));\n\n\t/*\n\t * Since we are childless, we know that self->rb_left is pointing\n\t * to the sentinel node.\n\t */\n\tfather->rb_nodes[which] = self->rb_left;\n\n\t/*\n\t * Remove ourselves from the node list, decrement the count,\n\t * and update min/max.\n\t */\n\tRB_TAILQ_REMOVE(&rbt->rbt_nodes, self, rb_link);\n\tRBSTAT_DEC(rbt->rbt_count);\n#ifndef RBSMALL\n\tif (__predict_false(rbt->rbt_minmax[RB_POSITION(self)] == self)) {\n\t\trbt->rbt_minmax[RB_POSITION(self)] = father;\n\t\t/*\n\t\t * When removing the root, rbt->rbt_minmax[RB_DIR_LEFT] is\n\t\t * updated automatically, but we also need to update \n\t\t * rbt->rbt_minmax[RB_DIR_RIGHT];\n\t\t */\n\t\tif (__predict_false(was_root)) {\n\t\t\trbt->rbt_minmax[RB_DIR_RIGHT] = father;\n\t\t}\n\t}\n\tRB_SET_FATHER(self, NULL);\n#endif\n\n\t/*\n\t * Rebalance if requested.\n\t */\n\tif (rebalance)\n\t\trb_tree_removal_rebalance(rbt, father, which);\n\tKASSERT(was_root || rb_tree_check_node(rbt, father, NULL, true));\n}\n\n/*\n * When deleting an interior node\n */\nstatic void\nrb_tree_swap_prune_and_rebalance(struct rb_tree *rbt, struct rb_node *self,\n\tstruct rb_node *standin)\n{\n\tconst unsigned int standin_which = RB_POSITION(standin);\n\tunsigned int standin_other = standin_which ^ RB_DIR_OTHER;\n\tstruct rb_node *standin_son;\n\tstruct rb_node *standin_father = RB_FATHER(standin);\n\tbool rebalance = RB_BLACK_P(standin);\n\n\tif (standin_father == self) {\n\t\t/*\n\t\t * As a child of self, any childen would be opposite of\n\t\t * our parent.\n\t\t */\n\t\tKASSERT(RB_SENTINEL_P(standin->rb_nodes[standin_other]));\n\t\tstandin_son = standin->rb_nodes[standin_which];\n\t} else {\n\t\t/*\n\t\t * Since we aren't a child of self, any childen would be\n\t\t * on the same side as our parent.\n\t\t */\n\t\tKASSERT(RB_SENTINEL_P(standin->rb_nodes[standin_which]));\n\t\tstandin_son = standin->rb_nodes[standin_other];\n\t}\n\n\t/*\n\t * the node we are removing must have two children.\n\t */\n\tKASSERT(RB_TWOCHILDREN_P(self));\n\t/*\n\t * If standin has a child, it must be red.\n\t */\n\tKASSERT(RB_SENTINEL_P(standin_son) || RB_RED_P(standin_son));\n\n\t/*\n\t * Verify things are sane.\n\t */\n\tKASSERT(rb_tree_check_node(rbt, self, NULL, false));\n\tKASSERT(rb_tree_check_node(rbt, standin, NULL, false));\n\n\tif (__predict_false(RB_RED_P(standin_son))) {\n\t\t/*\n\t\t * We know we have a red child so if we flip it to black\n\t\t * we don't have to rebalance.\n\t\t */\n\t\tKASSERT(rb_tree_check_node(rbt, standin_son, NULL, true));\n\t\tRB_MARK_BLACK(standin_son);\n\t\trebalance = false;\n\n\t\tif (standin_father == self) {\n\t\t\tKASSERT(RB_POSITION(standin_son) == standin_which);\n\t\t} else {\n\t\t\tKASSERT(RB_POSITION(standin_son) == standin_other);\n\t\t\t/*\n\t\t\t * Change the son's parentage to point to his grandpa.\n\t\t\t */\n\t\t\tRB_SET_FATHER(standin_son, standin_father);\n\t\t\tRB_SET_POSITION(standin_son, standin_which);\n\t\t}\n\t}\n\n\tif (standin_father == self) {\n\t\t/*\n\t\t * If we are about to delete the standin's father, then when\n\t\t * we call rebalance, we need to use ourselves as our father.\n\t\t * Otherwise remember our original father.  Also, sincef we are\n\t\t * our standin's father we only need to reparent the standin's\n\t\t * brother.\n\t\t *\n\t\t * |    R      -->     S    |\n\t\t * |  Q   S    -->   Q   T  |\n\t\t * |        t  -->          |\n\t\t */\n\t\tKASSERT(RB_SENTINEL_P(standin->rb_nodes[standin_other]));\n\t\tKASSERT(!RB_SENTINEL_P(self->rb_nodes[standin_other]));\n\t\tKASSERT(self->rb_nodes[standin_which] == standin);\n\t\t/*\n\t\t * Have our son/standin adopt his brother as his new son.\n\t\t */\n\t\tstandin_father = standin;\n\t} else {\n\t\t/*\n\t\t * |    R          -->    S       .  |\n\t\t * |   / \\  |   T  -->   / \\  |  /   |\n\t\t * |  ..... | S    -->  ..... | T    |\n\t\t *\n\t\t * Sever standin's connection to his father.\n\t\t */\n\t\tstandin_father->rb_nodes[standin_which] = standin_son;\n\t\t/*\n\t\t * Adopt the far son.\n\t\t */\n\t\tstandin->rb_nodes[standin_other] = self->rb_nodes[standin_other];\n\t\tRB_SET_FATHER(standin->rb_nodes[standin_other], standin);\n\t\tKASSERT(RB_POSITION(self->rb_nodes[standin_other]) == standin_other);\n\t\t/*\n\t\t * Use standin_other because we need to preserve standin_which\n\t\t * for the removal_rebalance.\n\t\t */\n\t\tstandin_other = standin_which;\n\t}\n\n\t/*\n\t * Move the only remaining son to our standin.  If our standin is our\n\t * son, this will be the only son needed to be moved.\n\t */\n\tKASSERT(standin->rb_nodes[standin_other] != self->rb_nodes[standin_other]);\n\tstandin->rb_nodes[standin_other] = self->rb_nodes[standin_other];\n\tRB_SET_FATHER(standin->rb_nodes[standin_other], standin);\n\n\t/*\n\t * Now copy the result of self to standin and then replace\n\t * self with standin in the tree.\n\t */\n\tRB_COPY_PROPERTIES(standin, self);\n\tRB_SET_FATHER(standin, RB_FATHER(self));\n\tRB_FATHER(standin)->rb_nodes[RB_POSITION(standin)] = standin;\n\n\t/*\n\t * Remove ourselves from the node list, decrement the count,\n\t * and update min/max.\n\t */\n\tRB_TAILQ_REMOVE(&rbt->rbt_nodes, self, rb_link);\n\tRBSTAT_DEC(rbt->rbt_count);\n#ifndef RBSMALL\n\tif (__predict_false(rbt->rbt_minmax[RB_POSITION(self)] == self))\n\t\trbt->rbt_minmax[RB_POSITION(self)] = RB_FATHER(self);\n\tRB_SET_FATHER(self, NULL);\n#endif\n\n\tKASSERT(rb_tree_check_node(rbt, standin, NULL, false));\n\tKASSERT(RB_FATHER_SENTINEL_P(standin)\n\t\t|| rb_tree_check_node(rbt, standin_father, NULL, false));\n\tKASSERT(RB_LEFT_SENTINEL_P(standin)\n\t\t|| rb_tree_check_node(rbt, standin->rb_left, NULL, false));\n\tKASSERT(RB_RIGHT_SENTINEL_P(standin)\n\t\t|| rb_tree_check_node(rbt, standin->rb_right, NULL, false));\n\n\tif (!rebalance)\n\t\treturn;\n\n\trb_tree_removal_rebalance(rbt, standin_father, standin_which);\n\tKASSERT(rb_tree_check_node(rbt, standin, NULL, true));\n}\n\n/*\n * We could do this by doing\n *\trb_tree_node_swap(rbt, self, which);\n *\trb_tree_prune_node(rbt, self, false);\n *\n * But it's more efficient to just evalate and recolor the child.\n */\nstatic void\nrb_tree_prune_blackred_branch(struct rb_tree *rbt, struct rb_node *self,\n\tunsigned int which)\n{\n\tstruct rb_node *father = RB_FATHER(self);\n\tstruct rb_node *son = self->rb_nodes[which];\n#ifndef RBSMALL\n\tconst bool was_root = RB_ROOT_P(rbt, self);\n#endif\n\n\tKASSERT(which == RB_DIR_LEFT || which == RB_DIR_RIGHT);\n\tKASSERT(RB_BLACK_P(self) && RB_RED_P(son));\n\tKASSERT(!RB_TWOCHILDREN_P(son));\n\tKASSERT(RB_CHILDLESS_P(son));\n\tKASSERT(rb_tree_check_node(rbt, self, NULL, false));\n\tKASSERT(rb_tree_check_node(rbt, son, NULL, false));\n\n\t/*\n\t * Remove ourselves from the tree and give our former child our\n\t * properties (position, color, root).\n\t */\n\tRB_COPY_PROPERTIES(son, self);\n\tfather->rb_nodes[RB_POSITION(son)] = son;\n\tRB_SET_FATHER(son, father);\n\n\t/*\n\t * Remove ourselves from the node list, decrement the count,\n\t * and update minmax.\n\t */\n\tRB_TAILQ_REMOVE(&rbt->rbt_nodes, self, rb_link);\n\tRBSTAT_DEC(rbt->rbt_count);\n#ifndef RBSMALL\n\tif (__predict_false(was_root)) {\n\t\tKASSERT(rbt->rbt_minmax[which] == son);\n\t\trbt->rbt_minmax[which ^ RB_DIR_OTHER] = son;\n\t} else if (rbt->rbt_minmax[RB_POSITION(self)] == self) {\n\t\trbt->rbt_minmax[RB_POSITION(self)] = son;\n\t}\n\tRB_SET_FATHER(self, NULL);\n#endif\n\n\tKASSERT(was_root || rb_tree_check_node(rbt, father, NULL, true));\n\tKASSERT(rb_tree_check_node(rbt, son, NULL, true));\n}\n\nvoid\nrb_tree_remove_node(struct rb_tree *rbt, void *object)\n{\n\tconst rb_tree_ops_t *rbto = rbt->rbt_ops;\n\tstruct rb_node *standin, *self = RB_ITEMTONODE(rbto, object);\n\tunsigned int which;\n\n\tKASSERT(!RB_SENTINEL_P(self));\n\tRBSTAT_INC(rbt->rbt_removals);\n\n\t/*\n\t * In the following diagrams, we (the node to be removed) are S.  Red\n\t * nodes are lowercase.  T could be either red or black.\n\t *\n\t * Remember the major axiom of the red-black tree: the number of\n\t * black nodes from the root to each leaf is constant across all\n\t * leaves, only the number of red nodes varies.\n\t *\n\t * Thus removing a red leaf doesn't require any other changes to a\n\t * red-black tree.  So if we must remove a node, attempt to rearrange\n\t * the tree so we can remove a red node.\n\t *\n\t * The simpliest case is a childless red node or a childless root node:\n\t *\n\t * |    T  -->    T  |    or    |  R  -->  *  |\n\t * |  s    -->  *    |\n\t */\n\tif (RB_CHILDLESS_P(self)) {\n\t\tconst bool rebalance = RB_BLACK_P(self) && !RB_ROOT_P(rbt, self);\n\t\trb_tree_prune_node(rbt, self, rebalance);\n\t\treturn;\n\t}\n\tKASSERT(!RB_CHILDLESS_P(self));\n\tif (!RB_TWOCHILDREN_P(self)) {\n\t\t/*\n\t\t * The next simpliest case is the node we are deleting is\n\t\t * black and has one red child.\n\t\t *\n\t\t * |      T  -->      T  -->      T  |\n\t\t * |    S    -->  R      -->  R      |\n\t\t * |  r      -->    s    -->    *    |\n\t\t */\n\t\twhich = RB_LEFT_SENTINEL_P(self) ? RB_DIR_RIGHT : RB_DIR_LEFT;\n\t\tKASSERT(RB_BLACK_P(self));\n\t\tKASSERT(RB_RED_P(self->rb_nodes[which]));\n\t\tKASSERT(RB_CHILDLESS_P(self->rb_nodes[which]));\n\t\trb_tree_prune_blackred_branch(rbt, self, which);\n\t\treturn;\n\t}\n\tKASSERT(RB_TWOCHILDREN_P(self));\n\n\t/*\n\t * We invert these because we prefer to remove from the inside of\n\t * the tree.\n\t */\n\twhich = RB_POSITION(self) ^ RB_DIR_OTHER;\n\n\t/*\n\t * Let's find the node closes to us opposite of our parent\n\t * Now swap it with ourself, \"prune\" it, and rebalance, if needed.\n\t */\n\tstandin = RB_ITEMTONODE(rbto, rb_tree_iterate(rbt, object, which));\n\trb_tree_swap_prune_and_rebalance(rbt, self, standin);\n}\n\nstatic void\nrb_tree_removal_rebalance(struct rb_tree *rbt, struct rb_node *parent,\n\tunsigned int which)\n{\n\tKASSERT(!RB_SENTINEL_P(parent));\n\tKASSERT(RB_SENTINEL_P(parent->rb_nodes[which]));\n\tKASSERT(which == RB_DIR_LEFT || which == RB_DIR_RIGHT);\n\tRBSTAT_INC(rbt->rbt_removal_rebalance_calls);\n\n\twhile (RB_BLACK_P(parent->rb_nodes[which])) {\n\t\tunsigned int other = which ^ RB_DIR_OTHER;\n\t\tstruct rb_node *brother = parent->rb_nodes[other];\n\n\t\tRBSTAT_INC(rbt->rbt_removal_rebalance_passes);\n\n\t\tKASSERT(!RB_SENTINEL_P(brother));\n\t\t/*\n\t\t * For cases 1, 2a, and 2b, our brother's children must\n\t\t * be black and our father must be black\n\t\t */\n\t\tif (RB_BLACK_P(parent)\n\t\t    && RB_BLACK_P(brother->rb_left)\n\t\t    && RB_BLACK_P(brother->rb_right)) {\n\t\t\tif (RB_RED_P(brother)) {\n\t\t\t\t/*\n\t\t\t\t * Case 1: Our brother is red, swap its\n\t\t\t\t * position (and colors) with our parent. \n\t\t\t\t * This should now be case 2b (unless C or E\n\t\t\t\t * has a red child which is case 3; thus no\n\t\t\t\t * explicit branch to case 2b).\n\t\t\t\t *\n\t\t\t\t *    B         ->        D\n\t\t\t\t *  A     d     ->    b     E\n\t\t\t\t *      C   E   ->  A   C\n\t\t\t\t */\n\t\t\t\tKASSERT(RB_BLACK_P(parent));\n\t\t\t\trb_tree_reparent_nodes(rbt, parent, other);\n\t\t\t\tbrother = parent->rb_nodes[other];\n\t\t\t\tKASSERT(!RB_SENTINEL_P(brother));\n\t\t\t\tKASSERT(RB_RED_P(parent));\n\t\t\t\tKASSERT(RB_BLACK_P(brother));\n\t\t\t\tKASSERT(rb_tree_check_node(rbt, brother, NULL, false));\n\t\t\t\tKASSERT(rb_tree_check_node(rbt, parent, NULL, false));\n\t\t\t} else {\n\t\t\t\t/*\n\t\t\t\t * Both our parent and brother are black.\n\t\t\t\t * Change our brother to red, advance up rank\n\t\t\t\t * and go through the loop again.\n\t\t\t\t *\n\t\t\t\t *    B         ->   *B\n\t\t\t\t * *A     D     ->  A     d\n\t\t\t\t *      C   E   ->      C   E\n\t\t\t\t */\n\t\t\t\tRB_MARK_RED(brother);\n\t\t\t\tKASSERT(RB_BLACK_P(brother->rb_left));\n\t\t\t\tKASSERT(RB_BLACK_P(brother->rb_right));\n\t\t\t\tif (RB_ROOT_P(rbt, parent))\n\t\t\t\t\treturn;\t/* root == parent == black */\n\t\t\t\tKASSERT(rb_tree_check_node(rbt, brother, NULL, false));\n\t\t\t\tKASSERT(rb_tree_check_node(rbt, parent, NULL, false));\n\t\t\t\twhich = RB_POSITION(parent);\n\t\t\t\tparent = RB_FATHER(parent);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\t/*\n\t\t * Avoid an else here so that case 2a above can hit either\n\t\t * case 2b, 3, or 4.\n\t\t */\n\t\tif (RB_RED_P(parent)\n\t\t    && RB_BLACK_P(brother)\n\t\t    && RB_BLACK_P(brother->rb_left)\n\t\t    && RB_BLACK_P(brother->rb_right)) {\n\t\t\tKASSERT(RB_RED_P(parent));\n\t\t\tKASSERT(RB_BLACK_P(brother));\n\t\t\tKASSERT(RB_BLACK_P(brother->rb_left));\n\t\t\tKASSERT(RB_BLACK_P(brother->rb_right));\n\t\t\t/*\n\t\t\t * We are black, our father is red, our brother and\n\t\t\t * both nephews are black.  Simply invert/exchange the\n\t\t\t * colors of our father and brother (to black and red\n\t\t\t * respectively).\n\t\t\t *\n\t\t\t *\t|    f        -->    F        |\n\t\t\t *\t|  *     B    -->  *     b    |\n\t\t\t *\t|      N   N  -->      N   N  |\n\t\t\t */\n\t\t\tRB_MARK_BLACK(parent);\n\t\t\tRB_MARK_RED(brother);\n\t\t\tKASSERT(rb_tree_check_node(rbt, brother, NULL, true));\n\t\t\tbreak;\t\t/* We're done! */\n\t\t} else {\n\t\t\t/*\n\t\t\t * Our brother must be black and have at least one\n\t\t\t * red child (it may have two).\n\t\t\t */\n\t\t\tKASSERT(RB_BLACK_P(brother));\n\t\t\tKASSERT(RB_RED_P(brother->rb_nodes[which]) ||\n\t\t\t\tRB_RED_P(brother->rb_nodes[other]));\n\t\t\tif (RB_BLACK_P(brother->rb_nodes[other])) {\n\t\t\t\t/*\n\t\t\t\t * Case 3: our brother is black, our near\n\t\t\t\t * nephew is red, and our far nephew is black.\n\t\t\t\t * Swap our brother with our near nephew.  \n\t\t\t\t * This result in a tree that matches case 4.\n\t\t\t\t * (Our father could be red or black).\n\t\t\t\t *\n\t\t\t\t *\t|    F      -->    F      |\n\t\t\t\t *\t|  x     B  -->  x   B    |\n\t\t\t\t *\t|      n    -->        n  |\n\t\t\t\t */\n\t\t\t\tKASSERT(RB_RED_P(brother->rb_nodes[which]));\n\t\t\t\trb_tree_reparent_nodes(rbt, brother, which);\n\t\t\t\tKASSERT(RB_FATHER(brother) == parent->rb_nodes[other]);\n\t\t\t\tbrother = parent->rb_nodes[other];\n\t\t\t\tKASSERT(RB_RED_P(brother->rb_nodes[other]));\n\t\t\t}\n\t\t\t/*\n\t\t\t * Case 4: our brother is black and our far nephew\n\t\t\t * is red.  Swap our father and brother locations and\n\t\t\t * change our far nephew to black.  (these can be\n\t\t\t * done in either order so we change the color first).\n\t\t\t * The result is a valid red-black tree and is a\n\t\t\t * terminal case.  (again we don't care about the\n\t\t\t * father's color)\n\t\t\t *\n\t\t\t * If the father is red, we will get a red-black-black\n\t\t\t * tree:\n\t\t\t *\t|  f      ->  f      -->    b    |\n\t\t\t *\t|    B    ->    B    -->  F   N  |\n\t\t\t *\t|      n  ->      N  -->         |\n\t\t\t *\n\t\t\t * If the father is black, we will get an all black\n\t\t\t * tree:\n\t\t\t *\t|  F      ->  F      -->    B    |\n\t\t\t *\t|    B    ->    B    -->  F   N  |\n\t\t\t *\t|      n  ->      N  -->         |\n\t\t\t *\n\t\t\t * If we had two red nephews, then after the swap,\n\t\t\t * our former father would have a red grandson. \n\t\t\t */\n\t\t\tKASSERT(RB_BLACK_P(brother));\n\t\t\tKASSERT(RB_RED_P(brother->rb_nodes[other]));\n\t\t\tRB_MARK_BLACK(brother->rb_nodes[other]);\n\t\t\trb_tree_reparent_nodes(rbt, parent, other);\n\t\t\tbreak;\t\t/* We're done! */\n\t\t}\n\t}\n\tKASSERT(rb_tree_check_node(rbt, parent, NULL, true));\n}\n\nvoid *\nrb_tree_iterate(struct rb_tree *rbt, void *object, const unsigned int direction)\n{\n\tconst rb_tree_ops_t *rbto = rbt->rbt_ops;\n\tconst unsigned int other = direction ^ RB_DIR_OTHER;\n\tstruct rb_node *self;\n\n\tKASSERT(direction == RB_DIR_LEFT || direction == RB_DIR_RIGHT);\n\n\tif (object == NULL) {\n#ifndef RBSMALL\n\t\tif (RB_SENTINEL_P(rbt->rbt_root))\n\t\t\treturn NULL;\n\t\treturn RB_NODETOITEM(rbto, rbt->rbt_minmax[direction]);\n#else\n\t\tself = rbt->rbt_root;\n\t\tif (RB_SENTINEL_P(self))\n\t\t\treturn NULL;\n\t\twhile (!RB_SENTINEL_P(self->rb_nodes[direction]))\n\t\t\tself = self->rb_nodes[direction];\n\t\treturn RB_NODETOITEM(rbto, self);\n#endif /* !RBSMALL */\n\t}\n\tself = RB_ITEMTONODE(rbto, object);\n\tKASSERT(!RB_SENTINEL_P(self));\n\t/*\n\t * We can't go any further in this direction.  We proceed up in the\n\t * opposite direction until our parent is in direction we want to go.\n\t */\n\tif (RB_SENTINEL_P(self->rb_nodes[direction])) {\n\t\twhile (!RB_ROOT_P(rbt, self)) {\n\t\t\tif (other == RB_POSITION(self))\n\t\t\t\treturn RB_NODETOITEM(rbto, RB_FATHER(self));\n\t\t\tself = RB_FATHER(self);\n\t\t}\n\t\treturn NULL;\n\t}\n\n\t/*\n\t * Advance down one in current direction and go down as far as possible\n\t * in the opposite direction.\n\t */\n\tself = self->rb_nodes[direction];\n\tKASSERT(!RB_SENTINEL_P(self));\n\twhile (!RB_SENTINEL_P(self->rb_nodes[other]))\n\t\tself = self->rb_nodes[other];\n\treturn RB_NODETOITEM(rbto, self);\n}\n\n#ifdef RBDEBUG\nstatic const struct rb_node *\nrb_tree_iterate_const(const struct rb_tree *rbt, const struct rb_node *self,\n\tconst unsigned int direction)\n{\n\tconst unsigned int other = direction ^ RB_DIR_OTHER;\n\tKASSERT(direction == RB_DIR_LEFT || direction == RB_DIR_RIGHT);\n\n\tif (self == NULL) {\n#ifndef RBSMALL\n\t\tif (RB_SENTINEL_P(rbt->rbt_root))\n\t\t\treturn NULL;\n\t\treturn rbt->rbt_minmax[direction];\n#else\n\t\tself = rbt->rbt_root;\n\t\tif (RB_SENTINEL_P(self))\n\t\t\treturn NULL;\n\t\twhile (!RB_SENTINEL_P(self->rb_nodes[direction]))\n\t\t\tself = self->rb_nodes[direction];\n\t\treturn self;\n#endif /* !RBSMALL */\n\t}\n\tKASSERT(!RB_SENTINEL_P(self));\n\t/*\n\t * We can't go any further in this direction.  We proceed up in the\n\t * opposite direction until our parent is in direction we want to go.\n\t */\n\tif (RB_SENTINEL_P(self->rb_nodes[direction])) {\n\t\twhile (!RB_ROOT_P(rbt, self)) {\n\t\t\tif (other == RB_POSITION(self))\n\t\t\t\treturn RB_FATHER(self);\n\t\t\tself = RB_FATHER(self);\n\t\t}\n\t\treturn NULL;\n\t}\n\n\t/*\n\t * Advance down one in current direction and go down as far as possible\n\t * in the opposite direction.\n\t */\n\tself = self->rb_nodes[direction];\n\tKASSERT(!RB_SENTINEL_P(self));\n\twhile (!RB_SENTINEL_P(self->rb_nodes[other]))\n\t\tself = self->rb_nodes[other];\n\treturn self;\n}\n\nstatic unsigned int\nrb_tree_count_black(const struct rb_node *self)\n{\n\tunsigned int left, right;\n\n\tif (RB_SENTINEL_P(self))\n\t\treturn 0;\n\n\tleft = rb_tree_count_black(self->rb_left);\n\tright = rb_tree_count_black(self->rb_right);\n\n\tKASSERT(left == right);\n\n\treturn left + RB_BLACK_P(self);\n}\n\nstatic bool\nrb_tree_check_node(const struct rb_tree *rbt, const struct rb_node *self,\n\tconst struct rb_node *prev, bool red_check)\n{\n\tconst rb_tree_ops_t *rbto = rbt->rbt_ops;\n\trbto_compare_nodes_fn compare_nodes = rbto->rbto_compare_nodes;\n\n\tKASSERT(!RB_SENTINEL_P(self));\n\tKASSERT(prev == NULL || (*compare_nodes)(rbto->rbto_context,\n\t    RB_NODETOITEM(rbto, prev), RB_NODETOITEM(rbto, self)) < 0);\n\n\t/*\n\t * Verify our relationship to our parent.\n\t */\n\tif (RB_ROOT_P(rbt, self)) {\n\t\tKASSERT(self == rbt->rbt_root);\n\t\tKASSERT(RB_POSITION(self) == RB_DIR_LEFT);\n\t\tKASSERT(RB_FATHER(self)->rb_nodes[RB_DIR_LEFT] == self);\n\t\tKASSERT(RB_FATHER(self) == (const struct rb_node *) &rbt->rbt_root);\n\t} else {\n\t\tint diff = (*compare_nodes)(rbto->rbto_context,\n\t\t    RB_NODETOITEM(rbto, self),\n\t\t    RB_NODETOITEM(rbto, RB_FATHER(self)));\n\n\t\tKASSERT(self != rbt->rbt_root);\n\t\tKASSERT(!RB_FATHER_SENTINEL_P(self));\n\t\tif (RB_POSITION(self) == RB_DIR_LEFT) {\n\t\t\tKASSERT(diff < 0);\n\t\t\tKASSERT(RB_FATHER(self)->rb_nodes[RB_DIR_LEFT] == self);\n\t\t} else {\n\t\t\tKASSERT(diff > 0);\n\t\t\tKASSERT(RB_FATHER(self)->rb_nodes[RB_DIR_RIGHT] == self);\n\t\t}\n\t}\n\n\t/*\n\t * Verify our position in the linked list against the tree itself.\n\t */\n\t{\n\t\tconst struct rb_node *prev0 = rb_tree_iterate_const(rbt, self, RB_DIR_LEFT);\n\t\tconst struct rb_node *next0 = rb_tree_iterate_const(rbt, self, RB_DIR_RIGHT);\n\t\tKASSERT(prev0 == TAILQ_PREV(self, rb_node_qh, rb_link));\n\t\tKASSERT(next0 == TAILQ_NEXT(self, rb_link));\n#ifndef RBSMALL\n\t\tKASSERT(prev0 != NULL || self == rbt->rbt_minmax[RB_DIR_LEFT]);\n\t\tKASSERT(next0 != NULL || self == rbt->rbt_minmax[RB_DIR_RIGHT]);\n#endif\n\t}\n\n\t/*\n\t * The root must be black.\n\t * There can never be two adjacent red nodes. \n\t */\n\tif (red_check) {\n\t\tKASSERT(!RB_ROOT_P(rbt, self) || RB_BLACK_P(self));\n\t\t(void) rb_tree_count_black(self);\n\t\tif (RB_RED_P(self)) {\n\t\t\tconst struct rb_node *brother;\n\t\t\tKASSERT(!RB_ROOT_P(rbt, self));\n\t\t\tbrother = RB_FATHER(self)->rb_nodes[RB_POSITION(self) ^ RB_DIR_OTHER];\n\t\t\tKASSERT(RB_BLACK_P(RB_FATHER(self)));\n\t\t\t/* \n\t\t\t * I'm red and have no children, then I must either\n\t\t\t * have no brother or my brother also be red and\n\t\t\t * also have no children.  (black count == 0)\n\t\t\t */\n\t\t\tKASSERT(!RB_CHILDLESS_P(self)\n\t\t\t\t|| RB_SENTINEL_P(brother)\n\t\t\t\t|| RB_RED_P(brother)\n\t\t\t\t|| RB_CHILDLESS_P(brother));\n\t\t\t/*\n\t\t\t * If I'm not childless, I must have two children\n\t\t\t * and they must be both be black.\n\t\t\t */\n\t\t\tKASSERT(RB_CHILDLESS_P(self)\n\t\t\t\t|| (RB_TWOCHILDREN_P(self)\n\t\t\t\t    && RB_BLACK_P(self->rb_left)\n\t\t\t\t    && RB_BLACK_P(self->rb_right)));\n\t\t\t/*\n\t\t\t * If I'm not childless, thus I have black children,\n\t\t\t * then my brother must either be black or have two\n\t\t\t * black children.\n\t\t\t */\n\t\t\tKASSERT(RB_CHILDLESS_P(self)\n\t\t\t\t|| RB_BLACK_P(brother)\n\t\t\t\t|| (RB_TWOCHILDREN_P(brother)\n\t\t\t\t    && RB_BLACK_P(brother->rb_left)\n\t\t\t\t    && RB_BLACK_P(brother->rb_right)));\n\t\t} else {\n\t\t\t/*\n\t\t\t * If I'm black and have one child, that child must\n\t\t\t * be red and childless.\n\t\t\t */\n\t\t\tKASSERT(RB_CHILDLESS_P(self)\n\t\t\t\t|| RB_TWOCHILDREN_P(self)\n\t\t\t\t|| (!RB_LEFT_SENTINEL_P(self)\n\t\t\t\t    && RB_RIGHT_SENTINEL_P(self)\n\t\t\t\t    && RB_RED_P(self->rb_left)\n\t\t\t\t    && RB_CHILDLESS_P(self->rb_left))\n\t\t\t\t|| (!RB_RIGHT_SENTINEL_P(self)\n\t\t\t\t    && RB_LEFT_SENTINEL_P(self)\n\t\t\t\t    && RB_RED_P(self->rb_right)\n\t\t\t\t    && RB_CHILDLESS_P(self->rb_right)));\n\n\t\t\t/*\n\t\t\t * If I'm a childless black node and my parent is\n\t\t\t * black, my 2nd closet relative away from my parent\n\t\t\t * is either red or has a red parent or red children.\n\t\t\t */\n\t\t\tif (!RB_ROOT_P(rbt, self)\n\t\t\t    && RB_CHILDLESS_P(self)\n\t\t\t    && RB_BLACK_P(RB_FATHER(self))) {\n\t\t\t\tconst unsigned int which = RB_POSITION(self);\n\t\t\t\tconst unsigned int other = which ^ RB_DIR_OTHER;\n\t\t\t\tconst struct rb_node *relative0, *relative;\n\n\t\t\t\trelative0 = rb_tree_iterate_const(rbt,\n\t\t\t\t    self, other);\n\t\t\t\tKASSERT(relative0 != NULL);\n\t\t\t\trelative = rb_tree_iterate_const(rbt,\n\t\t\t\t    relative0, other);\n\t\t\t\tKASSERT(relative != NULL);\n\t\t\t\tKASSERT(RB_SENTINEL_P(relative->rb_nodes[which]));\n#if 0\n\t\t\t\tKASSERT(RB_RED_P(relative)\n\t\t\t\t\t|| RB_RED_P(relative->rb_left)\n\t\t\t\t\t|| RB_RED_P(relative->rb_right)\n\t\t\t\t\t|| RB_RED_P(RB_FATHER(relative)));\n#endif\n\t\t\t}\n\t\t}\n\t\t/*\n\t\t * A grandparent's children must be real nodes and not\n\t\t * sentinels.  First check out grandparent.\n\t\t */\n\t\tKASSERT(RB_ROOT_P(rbt, self)\n\t\t\t|| RB_ROOT_P(rbt, RB_FATHER(self))\n\t\t\t|| RB_TWOCHILDREN_P(RB_FATHER(RB_FATHER(self))));\n\t\t/*\n\t\t * If we are have grandchildren on our left, then\n\t\t * we must have a child on our right.\n\t\t */\n\t\tKASSERT(RB_LEFT_SENTINEL_P(self)\n\t\t\t|| RB_CHILDLESS_P(self->rb_left)\n\t\t\t|| !RB_RIGHT_SENTINEL_P(self));\n\t\t/*\n\t\t * If we are have grandchildren on our right, then\n\t\t * we must have a child on our left.\n\t\t */\n\t\tKASSERT(RB_RIGHT_SENTINEL_P(self)\n\t\t\t|| RB_CHILDLESS_P(self->rb_right)\n\t\t\t|| !RB_LEFT_SENTINEL_P(self));\n\n\t\t/*\n\t\t * If we have a child on the left and it doesn't have two\n\t\t * children make sure we don't have great-great-grandchildren on\n\t\t * the right.\n\t\t */\n\t\tKASSERT(RB_TWOCHILDREN_P(self->rb_left)\n\t\t\t|| RB_CHILDLESS_P(self->rb_right)\n\t\t\t|| RB_CHILDLESS_P(self->rb_right->rb_left)\n\t\t\t|| RB_CHILDLESS_P(self->rb_right->rb_left->rb_left)\n\t\t\t|| RB_CHILDLESS_P(self->rb_right->rb_left->rb_right)\n\t\t\t|| RB_CHILDLESS_P(self->rb_right->rb_right)\n\t\t\t|| RB_CHILDLESS_P(self->rb_right->rb_right->rb_left)\n\t\t\t|| RB_CHILDLESS_P(self->rb_right->rb_right->rb_right));\n\n\t\t/*\n\t\t * If we have a child on the right and it doesn't have two\n\t\t * children make sure we don't have great-great-grandchildren on\n\t\t * the left.\n\t\t */\n\t\tKASSERT(RB_TWOCHILDREN_P(self->rb_right)\n\t\t\t|| RB_CHILDLESS_P(self->rb_left)\n\t\t\t|| RB_CHILDLESS_P(self->rb_left->rb_left)\n\t\t\t|| RB_CHILDLESS_P(self->rb_left->rb_left->rb_left)\n\t\t\t|| RB_CHILDLESS_P(self->rb_left->rb_left->rb_right)\n\t\t\t|| RB_CHILDLESS_P(self->rb_left->rb_right)\n\t\t\t|| RB_CHILDLESS_P(self->rb_left->rb_right->rb_left)\n\t\t\t|| RB_CHILDLESS_P(self->rb_left->rb_right->rb_right));\n\n\t\t/*\n\t\t * If we are fully interior node, then our predecessors and\n\t\t * successors must have no children in our direction.\n\t\t */\n\t\tif (RB_TWOCHILDREN_P(self)) {\n\t\t\tconst struct rb_node *prev0;\n\t\t\tconst struct rb_node *next0;\n\n\t\t\tprev0 = rb_tree_iterate_const(rbt, self, RB_DIR_LEFT);\n\t\t\tKASSERT(prev0 != NULL);\n\t\t\tKASSERT(RB_RIGHT_SENTINEL_P(prev0));\n\n\t\t\tnext0 = rb_tree_iterate_const(rbt, self, RB_DIR_RIGHT);\n\t\t\tKASSERT(next0 != NULL);\n\t\t\tKASSERT(RB_LEFT_SENTINEL_P(next0));\n\t\t}\n\t}\n\n\treturn true;\n}\n\nvoid\nrb_tree_check(const struct rb_tree *rbt, bool red_check)\n{\n\tconst struct rb_node *self;\n\tconst struct rb_node *prev;\n#ifdef RBSTATS\n\tunsigned int count = 0;\n#endif\n\n\tKASSERT(rbt->rbt_root != NULL);\n\tKASSERT(RB_LEFT_P(rbt->rbt_root));\n\n#if defined(RBSTATS) && !defined(RBSMALL)\n\tKASSERT(rbt->rbt_count > 1\n\t    || rbt->rbt_minmax[RB_DIR_LEFT] == rbt->rbt_minmax[RB_DIR_RIGHT]);\n#endif\n\n\tprev = NULL;\n\tTAILQ_FOREACH(self, &rbt->rbt_nodes, rb_link) {\n\t\trb_tree_check_node(rbt, self, prev, false);\n#ifdef RBSTATS\n\t\tcount++;\n#endif\n\t}\n#ifdef RBSTATS\n\tKASSERT(rbt->rbt_count == count);\n#endif\n\tif (red_check) {\n\t\tKASSERT(RB_BLACK_P(rbt->rbt_root));\n\t\tKASSERT(RB_SENTINEL_P(rbt->rbt_root)\n\t\t\t|| rb_tree_count_black(rbt->rbt_root));\n\n\t\t/*\n\t\t * The root must be black.\n\t\t * There can never be two adjacent red nodes. \n\t\t */\n\t\tTAILQ_FOREACH(self, &rbt->rbt_nodes, rb_link) {\n\t\t\trb_tree_check_node(rbt, self, NULL, true);\n\t\t}\n\t}\n}\n#endif /* RBDEBUG */\n\n#ifdef RBSTATS\nstatic void\nrb_tree_mark_depth(const struct rb_tree *rbt, const struct rb_node *self,\n\tsize_t *depths, size_t depth)\n{\n\tif (RB_SENTINEL_P(self))\n\t\treturn;\n\n\tif (RB_TWOCHILDREN_P(self)) {\n\t\trb_tree_mark_depth(rbt, self->rb_left, depths, depth + 1);\n\t\trb_tree_mark_depth(rbt, self->rb_right, depths, depth + 1);\n\t\treturn;\n\t}\n\tdepths[depth]++;\n\tif (!RB_LEFT_SENTINEL_P(self)) {\n\t\trb_tree_mark_depth(rbt, self->rb_left, depths, depth + 1);\n\t}\n\tif (!RB_RIGHT_SENTINEL_P(self)) {\n\t\trb_tree_mark_depth(rbt, self->rb_right, depths, depth + 1);\n\t}\n}\n\nvoid\nrb_tree_depths(const struct rb_tree *rbt, size_t *depths)\n{\n\trb_tree_mark_depth(rbt, rbt->rbt_root, depths, 1);\n}\n#endif /* RBSTATS */\n"
  },
  {
    "path": "vendor/rbtree.h",
    "content": "/*\t$NetBSD: rbtree.h,v 1.14 2025/10/29 08:08:44 roy Exp $\t*/\n\n/*-\n * SPDX-License-Identifier: BSD-2-Clause\n * Copyright (c) 2001 The NetBSD Foundation, Inc.\n * All rights reserved.\n *\n * This code is derived from software contributed to The NetBSD Foundation\n * by Matt Thomas <matt@3am-software.com>.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS\n * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\n * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS\n * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef _SYS_RBTREE_H_\n#define\t_SYS_RBTREE_H_\n\n#if !defined(_KERNEL) && !defined(_STANDALONE)\n#include <stddef.h>\n#include <stdint.h>\n#else\n#include <sys/types.h>\n#endif\n\n#ifdef RBDEBUG\n#include <sys/queue.h>\n#if !defined(_KERNEL) && !defined(_STANDALONE)\n#include <stdbool.h>\n#endif\n#endif\n\n#ifdef __BEGIN_DECLS\n__BEGIN_DECLS\n#endif\n\ntypedef struct rb_node {\n\tstruct rb_node *rb_nodes[2];\n#define\tRB_DIR_LEFT\t\t0\n#define\tRB_DIR_RIGHT\t\t1\n#define\tRB_DIR_OTHER\t\t1\n#define\trb_left\t\t\trb_nodes[RB_DIR_LEFT]\n#define\trb_right\t\trb_nodes[RB_DIR_RIGHT]\n\n\t/*\n\t * rb_info contains the two flags and the parent back pointer.\n\t * We put the two flags in the low two bits since we know that\n\t * rb_node will have an alignment of 4 or 8 bytes.\n\t */\n\tuintptr_t rb_info;\n#define\tRB_FLAG_POSITION\t(uintptr_t)0x2\n#define\tRB_FLAG_RED\t\t(uintptr_t)0x1\n#define\tRB_FLAG_MASK\t\t(RB_FLAG_POSITION|RB_FLAG_RED)\n#define\tRB_FATHER(rb) \\\n    ((struct rb_node *)((rb)->rb_info & ~RB_FLAG_MASK))\n#define\tRB_SET_FATHER(rb, father) \\\n    ((void)((rb)->rb_info = (uintptr_t)(father)|((rb)->rb_info & RB_FLAG_MASK)))\n\n#define\tRB_SENTINEL_P(rb)\t((rb) == NULL)\n#define\tRB_LEFT_SENTINEL_P(rb)\tRB_SENTINEL_P((rb)->rb_left)\n#define\tRB_RIGHT_SENTINEL_P(rb)\tRB_SENTINEL_P((rb)->rb_right)\n#define\tRB_FATHER_SENTINEL_P(rb) RB_SENTINEL_P(RB_FATHER((rb)))\n#define\tRB_CHILDLESS_P(rb) \\\n    (RB_SENTINEL_P(rb) || (RB_LEFT_SENTINEL_P(rb) && RB_RIGHT_SENTINEL_P(rb)))\n#define\tRB_TWOCHILDREN_P(rb) \\\n    (!RB_SENTINEL_P(rb) && !RB_LEFT_SENTINEL_P(rb) && !RB_RIGHT_SENTINEL_P(rb))\n\n#define\tRB_POSITION(rb)\t\\\n    (((rb)->rb_info & RB_FLAG_POSITION) ? RB_DIR_RIGHT : RB_DIR_LEFT)\n#define\tRB_RIGHT_P(rb)\t\t(RB_POSITION(rb) == RB_DIR_RIGHT)\n#define\tRB_LEFT_P(rb)\t\t(RB_POSITION(rb) == RB_DIR_LEFT)\n#define\tRB_RED_P(rb) \t\t(!RB_SENTINEL_P(rb) && ((rb)->rb_info & RB_FLAG_RED) != 0)\n#define\tRB_BLACK_P(rb) \t\t(RB_SENTINEL_P(rb) || ((rb)->rb_info & RB_FLAG_RED) == 0)\n#define\tRB_MARK_RED(rb) \t((void)((rb)->rb_info |= RB_FLAG_RED))\n#define\tRB_MARK_BLACK(rb) \t((void)((rb)->rb_info &= ~RB_FLAG_RED))\n#define\tRB_INVERT_COLOR(rb) \t((void)((rb)->rb_info ^= RB_FLAG_RED))\n#define\tRB_ROOT_P(rbt, rb)\t((rbt)->rbt_root == (rb))\n#define\tRB_SET_POSITION(rb, position) \\\n    ((void)((position) ? ((rb)->rb_info |= RB_FLAG_POSITION) : \\\n    ((rb)->rb_info &= ~RB_FLAG_POSITION)))\n#define\tRB_ZERO_PROPERTIES(rb)\t((void)((rb)->rb_info &= ~RB_FLAG_MASK))\n#define\tRB_COPY_PROPERTIES(dst, src) \\\n    ((void)((dst)->rb_info ^= ((dst)->rb_info ^ (src)->rb_info) & RB_FLAG_MASK))\n#define RB_SWAP_PROPERTIES(a, b) do { \\\n    uintptr_t xorinfo = ((a)->rb_info ^ (b)->rb_info) & RB_FLAG_MASK; \\\n    (a)->rb_info ^= xorinfo; \\\n    (b)->rb_info ^= xorinfo; \\\n  } while (0)\n#ifdef RBDEBUG\n\tTAILQ_ENTRY(rb_node) rb_link;\n#endif\n} rb_node_t;\n\n#define RB_TREE_MIN(T) rb_tree_iterate((T), NULL, RB_DIR_LEFT)\n#define RB_TREE_MAX(T) rb_tree_iterate((T), NULL, RB_DIR_RIGHT)\n#define RB_TREE_NEXT(T, N) rb_tree_iterate((T), (N), RB_DIR_RIGHT)\n#define RB_TREE_PREV(T, N) rb_tree_iterate((T), (N), RB_DIR_LEFT)\n#define RB_TREE_FOREACH(N, T) \\\n    for ((N) = RB_TREE_MIN(T); (N); (N) = RB_TREE_NEXT((T), (N)))\n#define RB_TREE_FOREACH_REVERSE(N, T) \\\n    for ((N) = RB_TREE_MAX(T); (N); (N) = RB_TREE_PREV((T), (N)))\n#define RB_TREE_FOREACH_SAFE(N, T, S) \\\n    for ((N) = RB_TREE_MIN(T); \\\n        (N) && ((S) = RB_TREE_NEXT((T), (N)), 1); \\\n        (N) = (S))\n#define RB_TREE_FOREACH_REVERSE_SAFE(N, T, S) \\\n    for ((N) = RB_TREE_MAX(T); \\\n        (N) && ((S) = RB_TREE_PREV((T), (N)), 1); \\\n        (N) = (S))\n\n#ifdef RBDEBUG\nTAILQ_HEAD(rb_node_qh, rb_node);\n\n#define\tRB_TAILQ_REMOVE(a, b, c)\t\tTAILQ_REMOVE(a, b, c)\n#define\tRB_TAILQ_INIT(a)\t\t\tTAILQ_INIT(a)\n#define\tRB_TAILQ_INSERT_HEAD(a, b, c)\t\tTAILQ_INSERT_HEAD(a, b, c)\n#define\tRB_TAILQ_INSERT_BEFORE(a, b, c)\t\tTAILQ_INSERT_BEFORE(a, b, c)\n#define\tRB_TAILQ_INSERT_AFTER(a, b, c, d)\tTAILQ_INSERT_AFTER(a, b, c, d)\n\n#define\tRBDEBUG_TREE_INITIALIZER(t)\t\t\t\t\t      \\\n\t.rbt_nodes = TAILQ_HEAD_INITIALIZER((t).rbt_nodes),\n#else\n#define\tRB_TAILQ_REMOVE(a, b, c)\t\tdo { } while (0)\n#define\tRB_TAILQ_INIT(a)\t\t\tdo { } while (0)\n#define\tRB_TAILQ_INSERT_HEAD(a, b, c)\t\tdo { } while (0)\n#define\tRB_TAILQ_INSERT_BEFORE(a, b, c)\t\tdo { } while (0)\n#define\tRB_TAILQ_INSERT_AFTER(a, b, c, d)\tdo { } while (0)\n\n#define\tRBDEBUG_TREE_INITIALIZER(t)\t\t/* nothing */\n#endif /* RBDEBUG */\n\n/*\n * rbto_compare_nodes_fn:\n *\treturn a positive value if the first node > the second node.\n *\treturn a negative value if the first node < the second node.\n *\treturn 0 if they are considered same.\n *\n * rbto_compare_key_fn:\n *\treturn a positive value if the node > the key.\n *\treturn a negative value if the node < the key.\n *\treturn 0 if they are considered same.\n */\n\ntypedef signed int (*rbto_compare_nodes_fn)(void *, const void *, const void *);\ntypedef signed int (*rbto_compare_key_fn)(void *, const void *, const void *);\n\ntypedef struct {\n\trbto_compare_nodes_fn rbto_compare_nodes;\n\trbto_compare_key_fn rbto_compare_key;\n\tsize_t rbto_node_offset;\n\tvoid *rbto_context;\n} rb_tree_ops_t;\n\ntypedef struct rb_tree {\n\tstruct rb_node *rbt_root;\n\tconst rb_tree_ops_t *rbt_ops;\n\tstruct rb_node *rbt_minmax[2];\n#ifdef RBDEBUG\n\tstruct rb_node_qh rbt_nodes;\n#endif\n#ifdef RBSTATS\n\tunsigned int rbt_count;\n\tunsigned int rbt_insertions;\n\tunsigned int rbt_removals;\n\tunsigned int rbt_insertion_rebalance_calls;\n\tunsigned int rbt_insertion_rebalance_passes;\n\tunsigned int rbt_removal_rebalance_calls;\n\tunsigned int rbt_removal_rebalance_passes;\n#endif\n} rb_tree_t;\n\n#ifdef RBSTATS\n#define\tRBSTAT_INC(v)\t((void)((v)++))\n#define\tRBSTAT_DEC(v)\t((void)((v)--))\n#else\n#define\tRBSTAT_INC(v)\tdo { } while (0)\n#define\tRBSTAT_DEC(v)\tdo { } while (0)\n#endif\n\n#define\tRB_TREE_INIT_TYPECHECK(t)\t\t\t\t\t      \\\n\t0*sizeof(&(t) - (struct rb_tree *)0)\n\n#define\tRB_TREE_INITIALIZER(t, ops)\t\t\t\t\t      \\\n{\t\t\t\t\t\t\t\t\t      \\\n\t.rbt_ops = (ops) + RB_TREE_INIT_TYPECHECK(t),\t\t\t      \\\n\tRBDEBUG_TREE_INITIALIZER(t)\t\t\t\t\t      \\\n}\n\nvoid\trb_tree_init(rb_tree_t *, const rb_tree_ops_t *);\nvoid *\trb_tree_insert_node(rb_tree_t *, void *);\nvoid *\trb_tree_find_node(rb_tree_t *, const void *);\nvoid *\trb_tree_find_node_geq(rb_tree_t *, const void *);\nvoid *\trb_tree_find_node_leq(rb_tree_t *, const void *);\nvoid\trb_tree_remove_node(rb_tree_t *, void *);\nvoid *\trb_tree_iterate(rb_tree_t *, void *, const unsigned int);\n#ifdef RBDEBUG\nvoid\trb_tree_check(const rb_tree_t *, bool);\n#endif\n#ifdef RBSTATS\nvoid\trb_tree_depths(const rb_tree_t *, size_t *);\n#endif\n\n#ifdef __END_DECLS\n__END_DECLS\n#endif\n\n#endif\t/* _SYS_RBTREE_H_*/\n"
  }
]