diff --git a/distrib/amd64/iso/Makefile b/distrib/amd64/iso/Makefile index 8fbdef7e8..a8d18d9ad 100644 --- a/distrib/amd64/iso/Makefile +++ b/distrib/amd64/iso/Makefile @@ -1,7 +1,7 @@ -# $OpenBSD: Makefile,v 1.47 2023/12/15 06:03:00 jmatthew Exp $ +# $OpenBSD: Makefile,v 1.48 2024/06/02 16:00:07 deraadt Exp $ FS= install${OSrev}.img -FSSIZE= 1359872 +FSSIZE= 1425408 CDROM= install${OSrev}.iso MOUNT_POINT= /mnt diff --git a/distrib/sets/lists/base/mi b/distrib/sets/lists/base/mi index ddda8ed63..f4cb4f658 100644 --- a/distrib/sets/lists/base/mi +++ b/distrib/sets/lists/base/mi @@ -222,6 +222,7 @@ ./etc/rc.d/bgplgd ./etc/rc.d/bootparamd ./etc/rc.d/cron +./etc/rc.d/dhcp6leased ./etc/rc.d/dhcpd ./etc/rc.d/dhcpleased ./etc/rc.d/dhcrelay @@ -329,6 +330,7 @@ ./sbin/chown ./sbin/clri ./sbin/dhclient +./sbin/dhcp6leased ./sbin/dhcpleased ./sbin/disklabel ./sbin/dmesg @@ -6560,6 +6562,7 @@ ./var/cron/tabs ./var/db ./var/db/acpi +./var/db/dhcp6leased ./var/db/dhcpleased ./var/db/ldap ./var/db/ns diff --git a/distrib/sets/lists/man/mi b/distrib/sets/lists/man/mi index 711eaba7d..944da9dbc 100644 --- a/distrib/sets/lists/man/mi +++ b/distrib/sets/lists/man/mi @@ -2236,6 +2236,7 @@ ./usr/share/man/man5/dhclient.conf.5 ./usr/share/man/man5/dhclient.leases.5 ./usr/share/man/man5/dhcp-options.5 +./usr/share/man/man5/dhcp6leased.conf.5 ./usr/share/man/man5/dhcpd.conf.5 ./usr/share/man/man5/dhcpd.leases.5 ./usr/share/man/man5/dhcpleased.conf.5 @@ -2436,6 +2437,7 @@ ./usr/share/man/man8/daily.8 ./usr/share/man/man8/dev_mkdb.8 ./usr/share/man/man8/dhclient.8 +./usr/share/man/man8/dhcp6leased.8 ./usr/share/man/man8/dhcpd.8 ./usr/share/man/man8/dhcpleasectl.8 ./usr/share/man/man8/dhcpleased.8 diff --git a/distrib/special/Makefile.inc b/distrib/special/Makefile.inc index 99547e82a..83835578d 100644 --- a/distrib/special/Makefile.inc +++ b/distrib/special/Makefile.inc @@ -4,7 +4,7 @@ COPTS+=-Oz -fno-stack-protector COPTS+=-fno-unwind-tables -fno-asynchronous-unwind-tables .if ${MACHINE} == "amd64" -COPTS+=-fcf-protection=none +COPTS+=-fcf-protection=none -fno-ret-clean .endif .if ${MACHINE} == "arm64" COPTS+=-mbranch-protection=none diff --git a/etc/Makefile b/etc/Makefile index b17c7a678..e1851fd60 100644 --- a/etc/Makefile +++ b/etc/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.488 2024/02/29 22:21:21 bluhm Exp $ +# $OpenBSD: Makefile,v 1.489 2024/06/03 10:06:35 florian Exp $ .include @@ -58,12 +58,12 @@ EXAMPLES_600=bgpd.conf doas.conf dvmrpd.conf eigrpd.conf hostapd.conf \ snmpd.conf vm.conf ypldap.conf # -r-xr-xr-x -RCDAEMONS=amd apmd bgpd bgplgd bootparamd cron dhcpd dhcpleased dhcrelay \ - dhcrelay6 dvmrpd eigrpd ftpd ftpproxy ftpproxy6 hostapd hotplugd \ - httpd identd ifstated iked inetd isakmpd iscsid ldapd ldattach ldomd \ - ldpd lockd lpd mopd mountd mrouted nfsd npppd nsd ntpd ospf6d ospfd \ - pflogd portmap rad radiusd rarpd rbootd relayd resolvd ripd route6d \ - sasyncd sensorsd slowcgi slaacd smtpd sndiod snmpd spamd \ +RCDAEMONS=amd apmd bgpd bgplgd bootparamd cron dhcpd dhcpleased dhcp6leased \ + dhcrelay dhcrelay6 dvmrpd eigrpd ftpd ftpproxy ftpproxy6 hostapd \ + hotplugd httpd identd ifstated iked inetd isakmpd iscsid ldapd \ + ldattach ldomd ldpd lockd lpd mopd mountd mrouted nfsd npppd nsd ntpd \ + ospf6d ospfd pflogd portmap rad radiusd rarpd rbootd relayd resolvd \ + ripd route6d sasyncd sensorsd slowcgi slaacd smtpd sndiod snmpd spamd \ spamlogd sshd statd syslogd tftpd tftpproxy unbound \ unwind vmd watchdogd wsmoused xenodm ypbind ypldap ypserv diff --git a/etc/changelist b/etc/changelist index f5639b660..6f933d05a 100644 --- a/etc/changelist +++ b/etc/changelist @@ -1,4 +1,4 @@ -# $OpenBSD: changelist,v 1.138 2023/12/13 11:34:56 job Exp $ +# $OpenBSD: changelist,v 1.139 2024/06/03 10:07:27 florian Exp $ # # List of files which the security script backs up and checks # for modifications. @@ -25,6 +25,7 @@ /etc/daily.local /etc/defaultdomain /etc/dhcpd.conf +/etc/dhcp6leased.conf /etc/dhcpleased.conf /etc/disktab /etc/distfile diff --git a/etc/group b/etc/group index 55db71f90..0abe53580 100644 --- a/etc/group +++ b/etc/group @@ -80,6 +80,7 @@ _ftp_proxy:*:109: _sndiop:*:110: _syspatch:*:112: _slaacd:*:115: +_dhcp6leased:*:116: dialer:*:117: _shutdown:*:118: nogroup:*:32766: diff --git a/etc/master.passwd b/etc/master.passwd index d31ef5770..1668f5b52 100644 --- a/etc/master.passwd +++ b/etc/master.passwd @@ -62,4 +62,5 @@ _ftp_proxy:*:109:109::0:0:ftp proxy daemon:/nonexistent:/sbin/nologin _sndiop:*:110:110::0:0:sndio privileged user:/var/empty:/sbin/nologin _syspatch:*:112:112::0:0:syspatch unprivileged user:/var/empty:/sbin/nologin _slaacd:*:115:115::0:0:SLAAC Daemon:/var/empty:/sbin/nologin +_dhcp6leased:*:116:116::0:0:DHCP6Lease Daemon:/var/empty:/sbin/nologin nobody:*:32767:32767::0:0:Unprivileged user:/nonexistent:/sbin/nologin diff --git a/etc/mtree/4.4BSD.dist b/etc/mtree/4.4BSD.dist index 9a4a9e8a2..ede7cb6a3 100644 --- a/etc/mtree/4.4BSD.dist +++ b/etc/mtree/4.4BSD.dist @@ -1,4 +1,4 @@ -# $OpenBSD: 4.4BSD.dist,v 1.324 2023/10/26 19:28:30 naddy Exp $ +# $OpenBSD: 4.4BSD.dist,v 1.325 2024/06/02 12:32:33 florian Exp $ /set type=dir uname=root gname=wheel mode=0755 @@ -567,6 +567,8 @@ var db acpi .. + dhcp6leased + .. dhcpleased .. ldap mode=0700 diff --git a/etc/rc b/etc/rc index 537a2345d..85250cceb 100644 --- a/etc/rc +++ b/etc/rc @@ -1,4 +1,4 @@ -# $OpenBSD: rc,v 1.575 2024/05/17 00:33:43 deraadt Exp $ +# $OpenBSD: rc,v 1.576 2024/06/03 10:06:35 florian Exp $ # System startup script run by init on autoboot or after single-user. # Output and error are redirected to console by init, and the console is the @@ -633,7 +633,7 @@ run_upgrade_script sysmerge echo -n 'starting network daemons:' start_daemon ldomd sshd snmpd ldpd ripd ospfd ospf6d bgpd ifstated start_daemon relayd dhcpd dhcrelay mrouted dvmrpd radiusd eigrpd route6d -start_daemon rad hostapd lpd smtpd slowcgi bgplgd httpd ftpd +start_daemon dhcp6leased rad hostapd lpd smtpd slowcgi bgplgd httpd ftpd start_daemon ftpproxy ftpproxy6 tftpd tftpproxy identd inetd rarpd bootparamd start_daemon rbootd mopd vmd spamd spamlogd sndiod echo '.' diff --git a/etc/rc.conf b/etc/rc.conf index d1326019e..163aac16f 100644 --- a/etc/rc.conf +++ b/etc/rc.conf @@ -1,4 +1,4 @@ -# $OpenBSD: rc.conf,v 1.227 2022/09/26 00:29:55 kn Exp $ +# $OpenBSD: rc.conf,v 1.228 2024/06/03 10:06:35 florian Exp $ # DO NOT EDIT THIS FILE!! # @@ -19,6 +19,7 @@ bgplgd_flags=NO bootparamd_flags=NO cron_flags= dhcpd_flags=NO +dhcp6leased_flags=NO dhcpleased_flags= dhcrelay_flags=NO # for normal use: "-i interface [server]" dvmrpd_flags=NO diff --git a/etc/rc.d/dhcp6leased b/etc/rc.d/dhcp6leased new file mode 100644 index 000000000..99a29867f --- /dev/null +++ b/etc/rc.d/dhcp6leased @@ -0,0 +1,16 @@ +#!/bin/ksh +# +# $OpenBSD: dhcp6leased,v 1.1 2024/06/03 10:06:35 florian Exp $ + +daemon="/sbin/dhcp6leased" + +. /etc/rc.d/rc.subr + +rc_configtest() { + # use rc_exec here since daemon_flags may contain arguments with spaces + rc_exec "${daemon} -n ${daemon_flags}" +} + +rc_reload=NO + +rc_cmd $1 diff --git a/games/cribbage/io.c b/games/cribbage/io.c index ec9ce54ba..7dfa94de8 100644 --- a/games/cribbage/io.c +++ b/games/cribbage/io.c @@ -1,4 +1,4 @@ -/* $OpenBSD: io.c,v 1.22 2016/01/10 13:35:09 mestre Exp $ */ +/* $OpenBSD: io.c,v 1.23 2024/06/03 09:43:10 otto Exp $ */ /* $NetBSD: io.c,v 1.9 1997/07/09 06:25:47 phil Exp $ */ /*- @@ -505,14 +505,12 @@ get_line(void) { size_t pos; int c, oy, ox; - WINDOW *oscr; - oscr = stdscr; - stdscr = Msgwin; - getyx(stdscr, oy, ox); - refresh(); + getyx(Msgwin, oy, ox); + wrefresh(Msgwin); /* loop reading in the string, and put it in a temporary buffer */ - for (pos = 0; (c = readchar()) != '\n'; clrtoeol(), refresh()) { + for (pos = 0; (c = readchar()) != '\n'; wclrtoeol(Msgwin), + wrefresh(Msgwin)) { if (c == -1) continue; if (c == ' ' && (pos == 0 || linebuf[pos - 1] == ' ')) @@ -522,13 +520,13 @@ get_line(void) int i; pos--; for (i = strlen(unctrl(linebuf[pos])); i; i--) - addch('\b'); + waddch(Msgwin, '\b'); } continue; } if (c == killchar()) { pos = 0; - move(oy, ox); + wmove(Msgwin, oy, ox); continue; } if (pos >= LINESIZE - 1 || !(isalnum(c) || c == ' ')) { @@ -538,12 +536,11 @@ get_line(void) if (islower(c)) c = toupper(c); linebuf[pos++] = c; - addstr(unctrl(c)); + waddstr(Msgwin, unctrl(c)); Mpos++; } while (pos < sizeof(linebuf)) linebuf[pos++] = '\0'; - stdscr = oscr; return (linebuf); } diff --git a/gnu/llvm/clang/include/clang/Driver/Options.td b/gnu/llvm/clang/include/clang/Driver/Options.td index 7a2995109..a5df86eba 100644 --- a/gnu/llvm/clang/include/clang/Driver/Options.td +++ b/gnu/llvm/clang/include/clang/Driver/Options.td @@ -2857,6 +2857,10 @@ def fno_fixup_gadgets : Flag<["-"], "fno-fixup-gadgets">, Group, Flags< HelpText<"Disable FixupGadgets pass (x86 only)">; def ffixup_gadgets : Flag<["-"], "ffixup-gadgets">, Group, Flags<[CoreOption]>, HelpText<"Replace ROP friendly instructions with safe alternatives (x86 only)">; +def fno_ret_clean : Flag<["-"], "fno-ret-clean">, Group, Flags<[CoreOption]>, + HelpText<"Disable ret-clean pass">; +def fret_clean : Flag<["-"], "fret-clean">, Group, Flags<[CoreOption]>, + HelpText<"Clean return address from stack after call">; def ftrivial_auto_var_init_stop_after : Joined<["-"], "ftrivial-auto-var-init-stop-after=">, Group, Flags<[CC1Option, CoreOption]>, HelpText<"Stop initializing trivial automatic stack variables after the specified number of instances">, MarshallingInfoInt>; diff --git a/gnu/llvm/clang/lib/Driver/ToolChains/Clang.cpp b/gnu/llvm/clang/lib/Driver/ToolChains/Clang.cpp index 1d073428f..6eaa99183 100644 --- a/gnu/llvm/clang/lib/Driver/ToolChains/Clang.cpp +++ b/gnu/llvm/clang/lib/Driver/ToolChains/Clang.cpp @@ -6402,6 +6402,16 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back(Args.MakeArgString(Twine("-x86-fixup-gadgets=true"))); } + // -ret-clean + if (Arg *A = Args.getLastArg(options::OPT_fno_ret_clean, + options::OPT_fret_clean)) { + CmdArgs.push_back(Args.MakeArgString(Twine("-mllvm"))); + if (A->getOption().matches(options::OPT_fno_ret_clean)) + CmdArgs.push_back(Args.MakeArgString(Twine("-x86-ret-clean=false"))); + else if (A->getOption().matches(options::OPT_fret_clean)) + CmdArgs.push_back(Args.MakeArgString(Twine("-x86-ret-clean=true"))); + } + RenderSCPOptions(TC, Args, CmdArgs); RenderTrivialAutoVarInitOptions(D, TC, Args, CmdArgs); diff --git a/gnu/llvm/llvm/lib/Target/X86/X86.h b/gnu/llvm/llvm/lib/Target/X86/X86.h index 3d0eef8d8..bf4503dd6 100644 --- a/gnu/llvm/llvm/lib/Target/X86/X86.h +++ b/gnu/llvm/llvm/lib/Target/X86/X86.h @@ -132,6 +132,10 @@ FunctionPass *createX86DomainReassignmentPass(); /// ROP friendly instructions with alternatives. FunctionPass *createX86FixupGadgetsPass(); +/// Return a Machine Function pass that attempts to replace +/// RET instructions with a cleaning sequence +FunctionPass *createX86RetCleanPass(); + /// This pass replaces EVEX encoded of AVX-512 instructiosn by VEX /// encoding when possible in order to reduce code size. FunctionPass *createX86EvexToVexInsts(); diff --git a/gnu/llvm/llvm/lib/Target/X86/X86RetClean.cpp b/gnu/llvm/llvm/lib/Target/X86/X86RetClean.cpp new file mode 100644 index 000000000..623bfede5 --- /dev/null +++ b/gnu/llvm/llvm/lib/Target/X86/X86RetClean.cpp @@ -0,0 +1,115 @@ +//===-- X86RetClean.cpp - Clean Retaddr off stack upon function return ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file +/// This file defines a function pass that clears the ret-address from +/// the top of the stack, immediately upon return to the caller, the goal +/// is remove this subtle but powerful info-leak which hints at the +/// address space location of the lower level libraries. +/// +//===----------------------------------------------------------------------===// + +#include "X86.h" +#include "X86InstrBuilder.h" +#include "X86InstrInfo.h" +#include "X86MachineFunctionInfo.h" +#include "X86Subtarget.h" +#include "X86TargetMachine.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +using namespace llvm; + +#define RETCLEAN_DESC "X86 Ret Clean" +#define RETCLEAN_NAME "x86-ret-clean" + +#define DEBUG_TYPE RETCLEAN_NAME + +// Toggle with cc1 option: -mllvm -x86-ret-clean= +static cl::opt RetClean( + "x86-ret-clean", cl::Hidden, + cl::desc("clean return address off stack after call"), + cl::init(false)); + +namespace { +class RetCleanPass : public MachineFunctionPass { + +public: + static char ID; + + StringRef getPassName() const override { return RETCLEAN_DESC; } + + RetCleanPass() + : MachineFunctionPass(ID) {} + + /// Loop over all the instructions and replace ret with ret+clean + bool runOnMachineFunction(MachineFunction &MF) override; + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::NoVRegs); + } + +private: + bool fixupInstruction(MachineFunction &MF, MachineBasicBlock &MBB, + MachineInstr &MI); +}; +char RetCleanPass::ID = 0; +} // namespace + +FunctionPass *llvm::createX86RetCleanPass() { + return new RetCleanPass(); +} + +bool RetCleanPass::fixupInstruction(MachineFunction &MF, + MachineBasicBlock &MBB, + MachineInstr &MI) { + + const X86InstrInfo *TII = MF.getSubtarget().getInstrInfo(); + bool Is64Bit = MF.getTarget().getTargetTriple().getArch() == Triple::x86_64; + unsigned Opc = Is64Bit ? X86::MOV64mi32 : X86::MOV32mi; + unsigned Offset = Is64Bit ? -8 : -4; + Register SPReg = Is64Bit ? X86::RSP : X86::ESP; + + // add "movq $0, -8(%rsp)" (or similar) in caller, to clear the + // ret-addr info-leak off the stack + addRegOffset(BuildMI(MBB, MI, MI.getDebugLoc(), TII->get(Opc)), + SPReg, false, Offset) + .addImm(0); + return true; +} + +bool RetCleanPass::runOnMachineFunction(MachineFunction &MF) { + if (!RetClean) + return false; + + bool modified = false; + + for (auto &MBB : MF) { + std::vector fixups; + bool foundcall = false; + + for (auto &MI : MBB) { + if (MI.isCall()) { + foundcall = true; // queue the insert before the next MI + } else if (foundcall) { + fixups.push_back(&MI); + foundcall = false; + } + } + for (auto *fixup : fixups) + modified |= fixupInstruction(MF, MBB, *fixup); + } + return modified; +} diff --git a/gnu/llvm/llvm/lib/Target/X86/X86TargetMachine.cpp b/gnu/llvm/llvm/lib/Target/X86/X86TargetMachine.cpp index 306a53e94..5d4b1b9d0 100644 --- a/gnu/llvm/llvm/lib/Target/X86/X86TargetMachine.cpp +++ b/gnu/llvm/llvm/lib/Target/X86/X86TargetMachine.cpp @@ -596,6 +596,8 @@ void X86PassConfig::addPreEmitPass2() { addPass(createX86IndirectThunksPass()); addPass(createX86ReturnThunksPass()); + addPass(createX86RetCleanPass()); + // Insert extra int3 instructions after trailing call instructions to avoid // issues in the unwinder. if (TT.isOSWindows() && TT.getArch() == Triple::x86_64) diff --git a/gnu/usr.bin/clang/libLLVMX86CodeGen/Makefile b/gnu/usr.bin/clang/libLLVMX86CodeGen/Makefile index 0dd32c5c2..143e37b43 100644 --- a/gnu/usr.bin/clang/libLLVMX86CodeGen/Makefile +++ b/gnu/usr.bin/clang/libLLVMX86CodeGen/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.18 2023/11/11 18:35:38 robert Exp $ +# $OpenBSD: Makefile,v 1.19 2024/06/02 15:40:42 deraadt Exp $ LIB= LLVMX86CodeGen NOPROFILE= @@ -25,6 +25,7 @@ SRCS+= X86AsmPrinter.cpp \ X86FastISel.cpp \ X86FixupBWInsts.cpp \ X86FixupGadgets.cpp \ + X86RetClean.cpp \ X86FixupLEAs.cpp \ X86AvoidStoreForwardingBlocks.cpp \ X86DynAllocaExpander.cpp \ diff --git a/regress/sys/kern/pledge/generic/Makefile b/regress/sys/kern/pledge/generic/Makefile index 6f7deff57..0db296034 100644 --- a/regress/sys/kern/pledge/generic/Makefile +++ b/regress/sys/kern/pledge/generic/Makefile @@ -1,6 +1,6 @@ -# $OpenBSD: Makefile,v 1.10 2020/04/01 15:26:53 claudio Exp $ +# $OpenBSD: Makefile,v 1.11 2024/06/03 08:02:22 anton Exp $ PROG= generic -SRCS= main.c manager.c test_stdio.c test_tty.c +SRCS= main.c manager.c test_stdio.c test_tty.c pty.c NOMAN= yes LDADD+= -lutil diff --git a/regress/sys/kern/pledge/generic/manager.c b/regress/sys/kern/pledge/generic/manager.c index 207923bc2..5ed58fb00 100644 --- a/regress/sys/kern/pledge/generic/manager.c +++ b/regress/sys/kern/pledge/generic/manager.c @@ -1,4 +1,4 @@ -/* $OpenBSD: manager.c,v 1.8 2024/04/26 04:44:43 jsg Exp $ */ +/* $OpenBSD: manager.c,v 1.9 2024/06/03 08:02:22 anton Exp $ */ /* * Copyright (c) 2015 Sebastien Marie * @@ -32,6 +32,7 @@ #include #include "manager.h" +#include "pty.h" extern char *__progname; @@ -89,20 +90,18 @@ clear_coredump(int *ret, const char *test_name) static int -grab_syscall(pid_t pid) +grab_syscall(pid_t pid, char *output) { int ret = -1; char *pattern; regex_t regex; regmatch_t matches[2]; - FILE *fd; - char line[1024]; int error; const char *errstr; /* build searched string */ error = asprintf(&pattern, - "^%s\\[%d\\]: pledge \"[a-z]+\", syscall ([0-9]+)\n?$", + "%s\\[%d\\]: pledge \"[a-z]+\", syscall ([0-9]+)", __progname, pid); if (error <= 0) { warn("asprintf pattern"); @@ -119,56 +118,28 @@ grab_syscall(pid_t pid) goto out; } - /* call dmesg */ - if ((fd = popen("/sbin/dmesg", "r")) == NULL) { - warn("popen /sbin/dmesg"); - goto out; - } - /* search the string */ - while (1) { - /* read a line */ - fgets(line, sizeof(line), fd); - - /* error checking */ - if (ferror(fd)) { - ret = -1; - goto out; - } - - /* quit */ - if (feof(fd)) - break; - - /* check if found */ - error = regexec(®ex, line, 2, matches, 0); - if (error == REG_NOMATCH) - continue; - if (error) { - warnx("regexec pattern=%s line=%s error=%d", - pattern, line, error); - ret = -1; - goto out; - } - - /* convert it */ - line[matches[1].rm_eo] = '\0'; - ret = strtonum(&line[matches[1].rm_so], 0, 255, &errstr); - if (errstr) { - warnx("strtonum: number=%s error=%s", - &line[matches[1].rm_so], errstr); - ret = -1; - goto out; - } + error = regexec(®ex, output, 2, matches, 0); + if (error == REG_NOMATCH) { + ret = 0; + goto out; + } + if (error) { + warnx("regexec pattern=%s output=%s error=%d", + pattern, output, error); + ret = -1; + goto out; } - /* cleanup */ - if (pclose(fd) == -1) + /* convert it */ + output[matches[1].rm_eo] = '\0'; + ret = strtonum(&output[matches[1].rm_so], 0, 255, &errstr); + if (errstr) { + warnx("strtonum: number=%s error=%s", + &output[matches[1].rm_so], errstr); + ret = -1; goto out; - - /* not found */ - if (ret == -1) - ret = 0; + } out: free(pattern); @@ -198,6 +169,7 @@ void _start_test(int *ret, const char *test_name, const char *request, void (*test_func)(void)) { + struct pty pty = {0}; int fildes[2]; pid_t pid; int status; @@ -228,6 +200,11 @@ _start_test(int *ret, const char *test_name, const char *request, return; } + if (pty_open(&pty)) { + *ret = EXIT_FAILURE; + return; + } + /* fork and launch the test */ switch (pid = fork()) { case -1: @@ -245,9 +222,19 @@ _start_test(int *ret, const char *test_name, const char *request, if (errno != EINTR) err(errno, "dup2"); + if (pty_detach(&pty)) { + *ret = EXIT_FAILURE; + return; + } + /* create a new session (for kill) */ setsid(); + if (pty_attach(&pty)) { + *ret = EXIT_FAILURE; + return; + } + /* set pledge policy */ if (request && pledge(request, NULL) != 0) err(errno, "pledge"); @@ -263,6 +250,11 @@ _start_test(int *ret, const char *test_name, const char *request, /* NOTREACHED */ } + if (pty_drain(&pty)) { + *ret = EXIT_FAILURE; + return; + } + /* copy pipe to output */ (void)close(fildes[1]); if (drainfd(fildes[0], STDOUT_FILENO) != 0) { @@ -331,7 +323,7 @@ _start_test(int *ret, const char *test_name, const char *request, /* grab pledged syscall from dmesg */ if (signal == SIGKILL || signal == SIGABRT) { - int syscall = grab_syscall(pid); + int syscall = grab_syscall(pid, pty_buffer(&pty)); switch (syscall) { case -1: /* error */ warn("test(%s): grab_syscall pid=%d", test_name, @@ -352,5 +344,7 @@ _start_test(int *ret, const char *test_name, const char *request, if (WIFSTOPPED(status)) printf(" stop=%d", WSTOPSIG(status)); + pty_close(&pty); + printf("\n"); } diff --git a/regress/sys/kern/pledge/generic/pty.c b/regress/sys/kern/pledge/generic/pty.c new file mode 100644 index 000000000..3651713b6 --- /dev/null +++ b/regress/sys/kern/pledge/generic/pty.c @@ -0,0 +1,103 @@ +/* $OpenBSD: pty.c,v 1.1 2024/06/03 08:02:22 anton Exp $ */ + +#include + +#include +#include +#include +#include + +#include "pty.h" + +int +pty_open(struct pty *pty) +{ + int master, slave; + + master = posix_openpt(O_RDWR); + if (master == -1) { + warn("posix_openpt"); + return 1; + } + if (grantpt(master) == -1) { + warn("grantpt"); + return 1; + } + if (unlockpt(master) == -1) { + warn("unlockpt"); + return 1; + } + + slave = open(ptsname(master), O_RDWR); + if (slave == -1) { + warn("%s", ptsname(master)); + return 1; + } + + pty->master = master; + pty->slave = slave; + return 0; +} + +void +pty_close(struct pty *pty) +{ + close(pty->slave); + close(pty->master); +} + +/* + * Disconnect the controlling tty, if present. + */ +int +pty_detach(struct pty *pty) +{ + int fd; + + fd = open("/dev/tty", O_RDWR | O_NOCTTY); + if (fd >= 0) { + (void)ioctl(fd, TIOCNOTTY, NULL); + close(fd); + } + return 0; +} + +/* + * Connect the slave as the controlling tty. + */ +int +pty_attach(struct pty *pty) +{ + if (ioctl(pty->slave, TIOCSCTTY, NULL) == -1) { + warn("TIOCSCTTY"); + return 1; + } + return 0; +} + +int +pty_drain(struct pty *pty) +{ + for (;;) { + char *buf = &pty->buf.storage[pty->buf.len]; + size_t bufsize = sizeof(pty->buf.storage) - pty->buf.len; + ssize_t n; + + n = read(pty->master, buf, bufsize); + if (n == -1) { + warn("read"); + return 1; + } + if (n == 0) + break; + + /* Ensure space for NUL-terminator. */ + if ((size_t)n >= bufsize) { + warnx("pty buffer too small"); + return 1; + } + pty->buf.len += n; + } + + return 0; +} diff --git a/regress/sys/kern/pledge/generic/pty.h b/regress/sys/kern/pledge/generic/pty.h new file mode 100644 index 000000000..6776f4869 --- /dev/null +++ b/regress/sys/kern/pledge/generic/pty.h @@ -0,0 +1,22 @@ +/* $OpenBSD: pty.h,v 1.1 2024/06/03 08:02:22 anton Exp $ */ + +struct pty { + struct { + char storage[1024]; + size_t len; + } buf; + int master; + int slave; +}; + +int pty_open(struct pty *); +void pty_close(struct pty *); +int pty_detach(struct pty *); +int pty_attach(struct pty *); +int pty_drain(struct pty *pty); + +static inline char * +pty_buffer(struct pty *pty) +{ + return pty->buf.storage; +} diff --git a/sbin/Makefile b/sbin/Makefile index bd65cf735..81ac089fa 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -1,6 +1,6 @@ -# $OpenBSD: Makefile,v 1.110 2021/02/26 17:17:03 florian Exp $ +# $OpenBSD: Makefile,v 1.111 2024/06/03 10:05:18 florian Exp $ -SUBDIR= atactl badsect bioctl clri dhclient dhcpleased \ +SUBDIR= atactl badsect bioctl clri dhclient dhcp6leased dhcpleased \ disklabel dmesg dump dumpfs fdisk fsck fsck_ext2fs fsck_ffs \ fsck_msdos fsdb fsirand growfs ifconfig iked init ipsecctl \ isakmpd kbd ldattach mknod mount \ diff --git a/sbin/dhcp6leased/Makefile b/sbin/dhcp6leased/Makefile new file mode 100644 index 000000000..4574a9a61 --- /dev/null +++ b/sbin/dhcp6leased/Makefile @@ -0,0 +1,23 @@ +# $OpenBSD: Makefile,v 1.1 2024/06/02 12:28:05 florian Exp $ + +PROG= dhcp6leased +SRCS= control.c dhcp6leased.c engine.c frontend.c log.c +SRCS+= parse.y printconf.c + +MAN= dhcp6leased.8 dhcp6leased.conf.5 + +#DEBUG= -g -DDEBUG=3 -O0 + +CFLAGS+= -Wall -I${.CURDIR} +CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes +CFLAGS+= -Wmissing-declarations +CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual +CFLAGS+= -Wsign-compare +YFLAGS= +LDADD+= -levent -lutil +DPADD+= ${LIBEVENT} ${LIBUTIL} + +.include + +# Don't compile dhcp6leased as static binary by default +LDSTATIC= diff --git a/sbin/dhcp6leased/control.c b/sbin/dhcp6leased/control.c new file mode 100644 index 000000000..5bc63c11a --- /dev/null +++ b/sbin/dhcp6leased/control.c @@ -0,0 +1,306 @@ +/* $OpenBSD: control.c,v 1.2 2024/06/02 13:35:52 florian Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "dhcp6leased.h" +#include "control.h" +#include "frontend.h" + +#define CONTROL_BACKLOG 5 + +struct { + struct event ev; + struct event evt; + int fd; +} control_state = {.fd = -1}; + +struct ctl_conn { + TAILQ_ENTRY(ctl_conn) entry; + struct imsgev iev; +}; + +struct ctl_conn *control_connbyfd(int); +struct ctl_conn *control_connbypid(pid_t); +void control_close(int); + +TAILQ_HEAD(ctl_conns, ctl_conn) ctl_conns = TAILQ_HEAD_INITIALIZER(ctl_conns); + +int +control_init(char *path) +{ + struct sockaddr_un sun; + int fd; + mode_t old_umask; + + if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, + 0)) == -1) { + log_warn("%s: socket", __func__); + return (-1); + } + + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_UNIX; + strlcpy(sun.sun_path, path, sizeof(sun.sun_path)); + + if (unlink(path) == -1) + if (errno != ENOENT) { + log_warn("%s: unlink %s", __func__, path); + close(fd); + return (-1); + } + + old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); + if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { + log_warn("%s: bind: %s", __func__, path); + close(fd); + umask(old_umask); + return (-1); + } + umask(old_umask); + + if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) { + log_warn("%s: chmod", __func__); + close(fd); + (void)unlink(path); + return (-1); + } + + return (fd); +} + +int +control_listen(int fd) +{ + if (control_state.fd != -1) + fatalx("%s: received unexpected controlsock", __func__); + + control_state.fd = fd; + if (listen(control_state.fd, CONTROL_BACKLOG) == -1) { + log_warn("%s: listen", __func__); + return (-1); + } + + event_set(&control_state.ev, control_state.fd, EV_READ, + control_accept, NULL); + event_add(&control_state.ev, NULL); + evtimer_set(&control_state.evt, control_accept, NULL); + + return (0); +} + +void +control_accept(int listenfd, short event, void *bula) +{ + int connfd; + socklen_t len; + struct sockaddr_un sun; + struct ctl_conn *c; + + event_add(&control_state.ev, NULL); + if ((event & EV_TIMEOUT)) + return; + + len = sizeof(sun); + if ((connfd = accept4(listenfd, (struct sockaddr *)&sun, &len, + SOCK_CLOEXEC | SOCK_NONBLOCK)) == -1) { + /* + * Pause accept if we are out of file descriptors, or + * libevent will haunt us here too. + */ + if (errno == ENFILE || errno == EMFILE) { + struct timeval evtpause = { 1, 0 }; + + event_del(&control_state.ev); + evtimer_add(&control_state.evt, &evtpause); + } else if (errno != EWOULDBLOCK && errno != EINTR && + errno != ECONNABORTED) + log_warn("%s: accept4", __func__); + return; + } + + if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) { + log_warn("%s: calloc", __func__); + close(connfd); + return; + } + + imsg_init(&c->iev.ibuf, connfd); + c->iev.handler = control_dispatch_imsg; + c->iev.events = EV_READ; + event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events, + c->iev.handler, &c->iev); + event_add(&c->iev.ev, NULL); + + TAILQ_INSERT_TAIL(&ctl_conns, c, entry); +} + +struct ctl_conn * +control_connbyfd(int fd) +{ + struct ctl_conn *c; + + TAILQ_FOREACH(c, &ctl_conns, entry) { + if (c->iev.ibuf.fd == fd) + break; + } + + return (c); +} + +struct ctl_conn * +control_connbypid(pid_t pid) +{ + struct ctl_conn *c; + + TAILQ_FOREACH(c, &ctl_conns, entry) { + if (c->iev.ibuf.pid == pid) + break; + } + + return (c); +} + +void +control_close(int fd) +{ + struct ctl_conn *c; + + if ((c = control_connbyfd(fd)) == NULL) { + log_warnx("%s: fd %d: not found", __func__, fd); + return; + } + + msgbuf_clear(&c->iev.ibuf.w); + TAILQ_REMOVE(&ctl_conns, c, entry); + + event_del(&c->iev.ev); + close(c->iev.ibuf.fd); + + /* Some file descriptors are available again. */ + if (evtimer_pending(&control_state.evt, NULL)) { + evtimer_del(&control_state.evt); + event_add(&control_state.ev, NULL); + } + + free(c); +} + +void +control_dispatch_imsg(int fd, short event, void *bula) +{ + struct ctl_conn *c; + struct imsg imsg; + ssize_t n; + int verbose; + + if ((c = control_connbyfd(fd)) == NULL) { + log_warnx("%s: fd %d: not found", __func__, fd); + return; + } + + if (event & EV_READ) { + if (((n = imsg_read(&c->iev.ibuf)) == -1 && errno != EAGAIN) || + n == 0) { + control_close(fd); + return; + } + } + if (event & EV_WRITE) { + if (msgbuf_write(&c->iev.ibuf.w) <= 0 && errno != EAGAIN) { + control_close(fd); + return; + } + } + + for (;;) { + if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) { + control_close(fd); + return; + } + if (n == 0) + break; + + switch (imsg.hdr.type) { + case IMSG_CTL_RELOAD: + frontend_imsg_compose_main(imsg.hdr.type, 0, NULL, 0); + break; + case IMSG_CTL_LOG_VERBOSE: + if (IMSG_DATA_SIZE(imsg) != sizeof(verbose)) + break; + c->iev.ibuf.pid = imsg.hdr.pid; + /* Forward to all other processes. */ + frontend_imsg_compose_main(imsg.hdr.type, imsg.hdr.pid, + imsg.data, IMSG_DATA_SIZE(imsg)); + frontend_imsg_compose_engine(imsg.hdr.type, 0, + imsg.hdr.pid, imsg.data, IMSG_DATA_SIZE(imsg)); + + memcpy(&verbose, imsg.data, sizeof(verbose)); + log_setverbose(verbose); + break; + case IMSG_CTL_SHOW_INTERFACE_INFO: + if (IMSG_DATA_SIZE(imsg) != sizeof(uint32_t)) + break; + c->iev.ibuf.pid = imsg.hdr.pid; + frontend_imsg_compose_engine(imsg.hdr.type, 0, + imsg.hdr.pid, imsg.data, IMSG_DATA_SIZE(imsg)); + break; + case IMSG_CTL_SEND_REQUEST: + if (IMSG_DATA_SIZE(imsg) != sizeof(uint32_t)) + break; + c->iev.ibuf.pid = imsg.hdr.pid; + frontend_imsg_compose_engine(IMSG_REQUEST_REBOOT, 0, + imsg.hdr.pid, imsg.data, IMSG_DATA_SIZE(imsg)); + break; + default: + log_debug("%s: error handling imsg %d", __func__, + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + + imsg_event_add(&c->iev); +} + +int +control_imsg_relay(struct imsg *imsg) +{ + struct ctl_conn *c; + + if ((c = control_connbypid(imsg->hdr.pid)) == NULL) + return (0); + + return (imsg_compose_event(&c->iev, imsg->hdr.type, 0, imsg->hdr.pid, + -1, imsg->data, IMSG_DATA_SIZE(*imsg))); +} diff --git a/sbin/dhcp6leased/control.h b/sbin/dhcp6leased/control.h new file mode 100644 index 000000000..b1fdef8ca --- /dev/null +++ b/sbin/dhcp6leased/control.h @@ -0,0 +1,23 @@ +/* $OpenBSD: control.h,v 1.1 2024/06/02 12:28:05 florian Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +int control_init(char *); +int control_listen(int); +void control_accept(int, short, void *); +void control_dispatch_imsg(int, short, void *); +int control_imsg_relay(struct imsg *); diff --git a/sbin/dhcp6leased/dhcp6leased.8 b/sbin/dhcp6leased/dhcp6leased.8 new file mode 100644 index 000000000..762fa4344 --- /dev/null +++ b/sbin/dhcp6leased/dhcp6leased.8 @@ -0,0 +1,102 @@ +.\" $OpenBSD: dhcp6leased.8,v 1.1 2024/06/02 12:28:05 florian Exp $ +.\" +.\" Copyright (c) 2024 Florian Obser +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: June 2 2024 $ +.Dt DHCP6LEASED 8 +.Os +.Sh NAME +.Nm dhcp6leased +.Nd Dynamic Host Configuration Protocol (DHCPv6) client daemon for IPv6 prefix delegation +.Sh SYNOPSIS +.Nm +.Op Fl dnv +.Op Fl f Ar file +.Op Fl s Ar socket +.Sh DESCRIPTION +.Nm +is an IPv6 dynamic host configuration protocol (DHCPv6) daemon for clients. +It requests IPv6 prefix delegations from DHCPv6 servers for assignment +to downstream interfaces. +.Pp +A running +.Nm +can be controlled with the +.Xr dhcp6leasectl 8 +utility. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl d +Do not daemonize. +If this option is specified, +.Nm +will run in the foreground and log to +.Em stderr . +.It Fl f Ar file +Specify an alternative configuration file. +.It Fl n +Configtest mode. +Only check the configuration file for validity. +.It Fl s Ar socket +Use an alternate location for the default control socket. +.It Fl v +Produce more verbose output. +Multiple +.Fl v +options increase the verbosity. +.El +.Sh FILES +.Bl -tag -width "/var/db/dhcp6leased/" -compact +.It Pa /dev/dhcp6leased.sock +.Ux Ns -domain +socket used for communication with +.Xr dhcp6leasectl 8 . +.It Pa /etc/dhcp6leased.conf +Default +.Nm +configuration file. +.It Pa /var/db/dhcp6leased/ Ns Aq Ar if +Interface specific lease files. +.El +.Sh SEE ALSO +.Xr dhcp6leased.conf 5 , +.Xr dhcp6leasectl 8 , +.Xr ifconfig 8 +.Sh STANDARDS +.Rs +.%A T. Mrugalski +.%A M. Siodelski +.%A B. Volz +.%A A. Yourtchenko +.%A M. Richardson +.%A S. Jiang +.%A T. Lemon +.%A T. Winters +.%D November 2018 +.%R RFC 8415 +.%T Dynamic Host Configuration Protocol for IPv6 (DHCPv6) +.Re +.Sh HISTORY +The +.Nm +program first appeared in +.Ox 7.6 . +.Sh AUTHORS +.An -nosplit +The +.Nm +program was written by +.An Florian Obser Aq Mt florian@openbsd.org . diff --git a/sbin/dhcp6leased/dhcp6leased.c b/sbin/dhcp6leased/dhcp6leased.c new file mode 100644 index 000000000..09478a29f --- /dev/null +++ b/sbin/dhcp6leased/dhcp6leased.c @@ -0,0 +1,1000 @@ +/* $OpenBSD: dhcp6leased.c,v 1.9 2024/06/03 15:53:26 deraadt Exp $ */ + +/* + * Copyright (c) 2017, 2021, 2024 Florian Obser + * Copyright (c) 2005 Claudio Jeker + * Copyright (c) 2004 Esben Norby + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "dhcp6leased.h" +#include "frontend.h" +#include "engine.h" +#include "control.h" + +enum dhcp6leased_process { + PROC_MAIN, + PROC_ENGINE, + PROC_FRONTEND +}; + +__dead void usage(void); +__dead void main_shutdown(void); + +void main_sig_handler(int, short, void *); + +static pid_t start_child(enum dhcp6leased_process, char *, int, int, int); + +void main_dispatch_frontend(int, short, void *); +void main_dispatch_engine(int, short, void *); +void open_udpsock(uint32_t); +void configure_address(struct imsg_configure_address *); +void deconfigure_address(struct imsg_configure_address *); +void read_lease_file(struct imsg_ifinfo *); +uint8_t *get_uuid(void); + +int main_imsg_send_ipc_sockets(struct imsgbuf *, struct imsgbuf *); +int main_imsg_compose_frontend(int, int, void *, uint16_t); +int main_imsg_compose_engine(int, int, void *, uint16_t); +int main_imsg_send_config(struct dhcp6leased_conf *); +int main_reload(void); + +static struct imsgev *iev_frontend; +static struct imsgev *iev_engine; + +struct dhcp6leased_conf *main_conf; +const char default_conffile[] = _PATH_CONF_FILE; +const char *conffile = default_conffile; +pid_t frontend_pid; +pid_t engine_pid; + +int routesock, ioctl_sock, rtm_seq, no_lease_files; + +void +main_sig_handler(int sig, short event, void *arg) +{ + /* + * Normal signal handler rules don't apply because libevent + * decouples for us. + */ + + switch (sig) { + case SIGTERM: + case SIGINT: + main_shutdown(); + case SIGHUP: + if (main_reload() == -1) + log_warnx("configuration reload failed"); + else + log_debug("configuration reloaded"); + break; + default: + fatalx("unexpected signal"); + } +} + +__dead void +usage(void) +{ + extern char *__progname; + + fprintf(stderr, "usage: %s [-dnv] [-f file] [-s socket]\n", + __progname); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + struct event ev_sigint, ev_sigterm, ev_sighup; + int ch; + int debug = 0, engine_flag = 0, frontend_flag = 0; + int verbose = 0, no_action = 0; + char *saved_argv0; + int pipe_main2frontend[2]; + int pipe_main2engine[2]; + int frontend_routesock, rtfilter, lockfd; + int rtable_any = RTABLE_ANY; + char *csock = _PATH_CTRL_SOCKET; + int control_fd; + uint8_t *uuid; + + log_init(1, LOG_DAEMON); /* Log to stderr until daemonized. */ + log_setverbose(1); + + saved_argv0 = argv[0]; + if (saved_argv0 == NULL) + saved_argv0 = "dhcp6leased"; + + while ((ch = getopt(argc, argv, "dEFf:ns:v")) != -1) { + switch (ch) { + case 'd': + debug = 1; + break; + case 'E': + engine_flag = 1; + break; + case 'F': + frontend_flag = 1; + break; + case 'f': + conffile = optarg; + break; + case 'n': + no_action = 1; + break; + case 's': + csock = optarg; + break; + case 'v': + verbose++; + break; + default: + usage(); + } + } + + argc -= optind; + argv += optind; + if (argc > 0 || (engine_flag && frontend_flag)) + usage(); + + if (engine_flag) + engine(debug, verbose); + else if (frontend_flag) + frontend(debug, verbose); + + /* parse config file */ + if ((main_conf = parse_config(conffile)) == NULL) + exit(1); + + if (no_action) { + if (verbose) + print_config(main_conf, verbose); + else + fprintf(stderr, "configuration OK\n"); + exit(0); + } + + /* Check for root privileges. */ + if (geteuid()) + errx(1, "need root privileges"); + + lockfd = open(_PATH_LOCKFILE, O_CREAT|O_RDWR|O_EXLOCK|O_NONBLOCK, 0600); + if (lockfd == -1) { + if (errno == EAGAIN) + errx(1, "already running"); + err(1, "%s", _PATH_LOCKFILE); + } + + /* Check for assigned daemon user */ + if (getpwnam(DHCP6LEASED_USER) == NULL) + errx(1, "unknown user %s", DHCP6LEASED_USER); + + log_init(debug, LOG_DAEMON); + log_setverbose(verbose); + + if (!debug) + daemon(0, 0); + + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, + PF_UNSPEC, pipe_main2frontend) == -1) + fatal("main2frontend socketpair"); + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, + PF_UNSPEC, pipe_main2engine) == -1) + fatal("main2engine socketpair"); + + /* Start children. */ + engine_pid = start_child(PROC_ENGINE, saved_argv0, pipe_main2engine[1], + debug, verbose); + frontend_pid = start_child(PROC_FRONTEND, saved_argv0, + pipe_main2frontend[1], debug, verbose); + + log_procinit("main"); + + if ((routesock = socket(AF_ROUTE, SOCK_RAW | SOCK_CLOEXEC | + SOCK_NONBLOCK, AF_INET)) == -1) + fatal("route socket"); + shutdown(routesock, SHUT_RD); + + event_init(); + + /* Setup signal handler. */ + signal_set(&ev_sigint, SIGINT, main_sig_handler, NULL); + signal_set(&ev_sigterm, SIGTERM, main_sig_handler, NULL); + signal_set(&ev_sighup, SIGHUP, main_sig_handler, NULL); + signal_add(&ev_sigint, NULL); + signal_add(&ev_sigterm, NULL); + signal_add(&ev_sighup, NULL); + signal(SIGPIPE, SIG_IGN); + + /* Setup pipes to children. */ + + if ((iev_frontend = malloc(sizeof(struct imsgev))) == NULL || + (iev_engine = malloc(sizeof(struct imsgev))) == NULL) + fatal(NULL); + imsg_init(&iev_frontend->ibuf, pipe_main2frontend[0]); + iev_frontend->handler = main_dispatch_frontend; + imsg_init(&iev_engine->ibuf, pipe_main2engine[0]); + iev_engine->handler = main_dispatch_engine; + + /* Setup event handlers for pipes to engine & frontend. */ + iev_frontend->events = EV_READ; + event_set(&iev_frontend->ev, iev_frontend->ibuf.fd, + iev_frontend->events, iev_frontend->handler, iev_frontend); + event_add(&iev_frontend->ev, NULL); + + iev_engine->events = EV_READ; + event_set(&iev_engine->ev, iev_engine->ibuf.fd, iev_engine->events, + iev_engine->handler, iev_engine); + event_add(&iev_engine->ev, NULL); + + if (main_imsg_send_ipc_sockets(&iev_frontend->ibuf, &iev_engine->ibuf)) + fatal("could not establish imsg links"); + + if ((ioctl_sock = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1) + fatal("socket"); + + if ((frontend_routesock = socket(AF_ROUTE, SOCK_RAW | SOCK_CLOEXEC, + AF_INET)) == -1) + fatal("route socket"); + + rtfilter = ROUTE_FILTER(RTM_IFINFO) | ROUTE_FILTER(RTM_IFANNOUNCE); + if (setsockopt(frontend_routesock, AF_ROUTE, ROUTE_MSGFILTER, + &rtfilter, sizeof(rtfilter)) == -1) + fatal("setsockopt(ROUTE_MSGFILTER)"); + if (setsockopt(frontend_routesock, AF_ROUTE, ROUTE_TABLEFILTER, + &rtable_any, sizeof(rtable_any)) == -1) + fatal("setsockopt(ROUTE_TABLEFILTER)"); + + uuid = get_uuid(); + + if ((control_fd = control_init(csock)) == -1) + warnx("control socket setup failed"); + + if (unveil(conffile, "r") == -1) + fatal("unveil %s", conffile); + + if (unveil(_PATH_LEASE, "rwc") == -1) { + no_lease_files = 1; + log_warn("disabling lease files, unveil " _PATH_LEASE); + } + + if (unveil(NULL, NULL) == -1) + fatal("unveil"); + + if (pledge("stdio inet rpath wpath sendfd wroute", NULL) == -1) + fatal("pledge"); + + main_imsg_compose_frontend(IMSG_ROUTESOCK, frontend_routesock, NULL, 0); + + main_imsg_compose_frontend(IMSG_UUID, -1, uuid, UUID_SIZE); + main_imsg_compose_engine(IMSG_UUID, -1, uuid, UUID_SIZE); + + if (control_fd != -1) + main_imsg_compose_frontend(IMSG_CONTROLFD, control_fd, NULL, 0); + main_imsg_send_config(main_conf); + + main_imsg_compose_frontend(IMSG_STARTUP, -1, NULL, 0); + + event_dispatch(); + + main_shutdown(); + return (0); +} + +__dead void +main_shutdown(void) +{ + pid_t pid; + int status; + + /* Close pipes. */ + msgbuf_clear(&iev_frontend->ibuf.w); + close(iev_frontend->ibuf.fd); + msgbuf_clear(&iev_engine->ibuf.w); + close(iev_engine->ibuf.fd); + + config_clear(main_conf); + + log_debug("waiting for children to terminate"); + do { + pid = wait(&status); + if (pid == -1) { + if (errno != EINTR && errno != ECHILD) + fatal("wait"); + } else if (WIFSIGNALED(status)) + log_warnx("%s terminated; signal %d", + (pid == engine_pid) ? "engine" : + "frontend", WTERMSIG(status)); + } while (pid != -1 || (pid == -1 && errno == EINTR)); + + free(iev_frontend); + free(iev_engine); + + log_info("terminating"); + exit(0); +} + +static pid_t +start_child(enum dhcp6leased_process p, char *argv0, int fd, int debug, int + verbose) +{ + char *argv[7]; + int argc = 0; + pid_t pid; + + switch (pid = fork()) { + case -1: + fatal("cannot fork"); + case 0: + break; + default: + close(fd); + return (pid); + } + + if (fd != 3) { + if (dup2(fd, 3) == -1) + fatal("cannot setup imsg fd"); + } else if (fcntl(fd, F_SETFD, 0) == -1) + fatal("cannot setup imsg fd"); + + argv[argc++] = argv0; + switch (p) { + case PROC_MAIN: + fatalx("Can not start main process"); + case PROC_ENGINE: + argv[argc++] = "-E"; + break; + case PROC_FRONTEND: + argv[argc++] = "-F"; + break; + } + if (debug) + argv[argc++] = "-d"; + if (verbose) + argv[argc++] = "-v"; + if (verbose > 1) + argv[argc++] = "-v"; + argv[argc++] = NULL; + + execvp(argv0, argv); + fatal("execvp"); +} + +void +main_dispatch_frontend(int fd, short event, void *bula) +{ + struct imsgev *iev = bula; + struct imsgbuf *ibuf; + struct imsg imsg; + struct imsg_ifinfo imsg_ifinfo; + ssize_t n; + int shut = 0; + uint32_t if_index; + int verbose; + + ibuf = &iev->ibuf; + + if (event & EV_READ) { + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* Connection closed. */ + shut = 1; + } + if (event & EV_WRITE) { + if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) + fatal("msgbuf_write"); + if (n == 0) /* Connection closed. */ + shut = 1; + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + if (n == 0) /* No more messages. */ + break; + + switch (imsg.hdr.type) { + case IMSG_OPEN_UDPSOCK: + if (IMSG_DATA_SIZE(imsg) != sizeof(if_index)) + fatalx("%s: IMSG_OPEN_UDPSOCK wrong length: " + "%lu", __func__, IMSG_DATA_SIZE(imsg)); + memcpy(&if_index, imsg.data, sizeof(if_index)); + open_udpsock(if_index); + break; + case IMSG_CTL_RELOAD: + if (main_reload() == -1) + log_warnx("configuration reload failed"); + else + log_warnx("configuration reloaded"); + break; + case IMSG_CTL_LOG_VERBOSE: + if (IMSG_DATA_SIZE(imsg) != sizeof(verbose)) + fatalx("%s: IMSG_CTL_LOG_VERBOSE wrong length: " + "%lu", __func__, IMSG_DATA_SIZE(imsg)); + memcpy(&verbose, imsg.data, sizeof(verbose)); + log_setverbose(verbose); + break; + case IMSG_UPDATE_IF: + if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_ifinfo)) + fatalx("%s: IMSG_UPDATE_IF wrong length: %lu", + __func__, IMSG_DATA_SIZE(imsg)); + memcpy(&imsg_ifinfo, imsg.data, sizeof(imsg_ifinfo)); + read_lease_file(&imsg_ifinfo); + main_imsg_compose_engine(IMSG_UPDATE_IF, -1, + &imsg_ifinfo, sizeof(imsg_ifinfo)); + break; + default: + log_debug("%s: error handling imsg %d", __func__, + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(iev); + else { + /* This pipe is dead. Remove its event handler */ + event_del(&iev->ev); + event_loopexit(NULL); + } +} + +void +main_dispatch_engine(int fd, short event, void *bula) +{ + struct imsgev *iev = bula; + struct imsgbuf *ibuf; + struct imsg imsg; + ssize_t n; + int shut = 0; + + ibuf = &iev->ibuf; + + if (event & EV_READ) { + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* Connection closed. */ + shut = 1; + } + if (event & EV_WRITE) { + if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) + fatal("msgbuf_write"); + if (n == 0) /* Connection closed. */ + shut = 1; + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("imsg_get"); + if (n == 0) /* No more messages. */ + break; + + switch (imsg.hdr.type) { + case IMSG_CONFIGURE_ADDRESS: { + struct imsg_configure_address imsg_configure_address; + if (IMSG_DATA_SIZE(imsg) != + sizeof(imsg_configure_address)) + fatalx("%s: IMSG_CONFIGURE_ADDRESS wrong " + "length: %lu", __func__, + IMSG_DATA_SIZE(imsg)); + memcpy(&imsg_configure_address, imsg.data, + sizeof(imsg_configure_address)); + configure_address(&imsg_configure_address); + break; + } + case IMSG_DECONFIGURE_ADDRESS: { + struct imsg_configure_address imsg_configure_address; + if (IMSG_DATA_SIZE(imsg) != + sizeof(imsg_configure_address)) + fatalx("%s: IMSG_CONFIGURE_ADDRESS wrong " + "length: %lu", __func__, + IMSG_DATA_SIZE(imsg)); + memcpy(&imsg_configure_address, imsg.data, + sizeof(imsg_configure_address)); + deconfigure_address(&imsg_configure_address); + break; + } + default: + log_debug("%s: error handling imsg %d", __func__, + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(iev); + else { + /* This pipe is dead. Remove its event handler. */ + event_del(&iev->ev); + event_loopexit(NULL); + } +} + +int +main_imsg_compose_frontend(int type, int fd, void *data, uint16_t datalen) +{ + if (iev_frontend) + return (imsg_compose_event(iev_frontend, type, 0, 0, fd, data, + datalen)); + else + return (-1); +} + +int +main_imsg_compose_engine(int type, int fd, void *data, uint16_t datalen) +{ + if (iev_engine) + return(imsg_compose_event(iev_engine, type, 0, 0, fd, data, + datalen)); + else + return (-1); +} + +void +imsg_event_add(struct imsgev *iev) +{ + iev->events = EV_READ; + if (iev->ibuf.w.queued) + iev->events |= EV_WRITE; + + event_del(&iev->ev); + event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev); + event_add(&iev->ev, NULL); +} + +int +imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid, + pid_t pid, int fd, void *data, uint16_t datalen) +{ + int ret; + + if ((ret = imsg_compose(&iev->ibuf, type, peerid, pid, fd, data, + datalen)) != -1) + imsg_event_add(iev); + + return (ret); +} + +int +main_imsg_send_ipc_sockets(struct imsgbuf *frontend_buf, + struct imsgbuf *engine_buf) +{ + int pipe_frontend2engine[2]; + + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, + PF_UNSPEC, pipe_frontend2engine) == -1) + return (-1); + + if (imsg_compose(frontend_buf, IMSG_SOCKET_IPC, 0, 0, + pipe_frontend2engine[0], NULL, 0) == -1) + return (-1); + imsg_flush(frontend_buf); + if (imsg_compose(engine_buf, IMSG_SOCKET_IPC, 0, 0, + pipe_frontend2engine[1], NULL, 0) == -1) + return (-1); + imsg_flush(engine_buf); + return (0); +} + +int +main_reload(void) +{ + struct dhcp6leased_conf *xconf; + + if ((xconf = parse_config(conffile)) == NULL) + return (-1); + + if (main_imsg_send_config(xconf) == -1) + return (-1); + + merge_config(main_conf, xconf); + + return (0); +} + +int +main_imsg_send_config(struct dhcp6leased_conf *xconf) +{ + struct iface_conf *iface_conf; + struct iface_ia_conf *ia_conf; + struct iface_pd_conf *pd_conf; + + main_imsg_compose_frontend(IMSG_RECONF_CONF, -1, xconf, sizeof(*xconf)); + main_imsg_compose_engine(IMSG_RECONF_CONF, -1, xconf, sizeof(*xconf)); + + /* Send the interface list to the frontend & engine. */ + SIMPLEQ_FOREACH(iface_conf, &xconf->iface_list, entry) { + main_imsg_compose_frontend(IMSG_RECONF_IFACE, -1, iface_conf, + sizeof(*iface_conf)); + main_imsg_compose_engine(IMSG_RECONF_IFACE, -1, iface_conf, + sizeof(*iface_conf)); + SIMPLEQ_FOREACH(ia_conf, &iface_conf->iface_ia_list, + entry) { + main_imsg_compose_frontend(IMSG_RECONF_IFACE_IA, -1, + ia_conf, sizeof(*ia_conf)); + main_imsg_compose_engine(IMSG_RECONF_IFACE_IA, -1, + ia_conf, sizeof(*ia_conf)); + SIMPLEQ_FOREACH(pd_conf, &ia_conf->iface_pd_list, + entry) { + main_imsg_compose_frontend(IMSG_RECONF_IFACE_PD, + -1, pd_conf, sizeof(*pd_conf)); + main_imsg_compose_engine(IMSG_RECONF_IFACE_PD, + -1, pd_conf, sizeof(*pd_conf)); + } + main_imsg_compose_frontend(IMSG_RECONF_IFACE_IA_END, + -1, NULL, 0); + main_imsg_compose_engine(IMSG_RECONF_IFACE_IA_END, + -1, NULL, 0); + } + main_imsg_compose_frontend(IMSG_RECONF_IFACE_END, -1, NULL, 0); + main_imsg_compose_engine(IMSG_RECONF_IFACE_END, -1, NULL, 0); + + } + + /* Config is now complete. */ + main_imsg_compose_frontend(IMSG_RECONF_END, -1, NULL, 0); + main_imsg_compose_engine(IMSG_RECONF_END, -1, NULL, 0); + + return (0); +} + +void +configure_address(struct imsg_configure_address *address) +{ + struct in6_aliasreq in6_addreq; + time_t t; + char *if_name; + + memset(&in6_addreq, 0, sizeof(in6_addreq)); + + if_name = if_indextoname(address->if_index, in6_addreq.ifra_name); + if (if_name == NULL) { + log_warnx("%s: cannot find interface %d", __func__, + address->if_index); + return; + } + + memcpy(&in6_addreq.ifra_addr, &address->addr, + sizeof(in6_addreq.ifra_addr)); + memcpy(&in6_addreq.ifra_prefixmask.sin6_addr, &address->mask, + sizeof(in6_addreq.ifra_prefixmask.sin6_addr)); + in6_addreq.ifra_prefixmask.sin6_family = AF_INET6; + in6_addreq.ifra_prefixmask.sin6_len = + sizeof(in6_addreq.ifra_prefixmask); + + t = time(NULL); + + in6_addreq.ifra_lifetime.ia6t_expire = t + address->vltime; + in6_addreq.ifra_lifetime.ia6t_vltime = address->vltime; + + in6_addreq.ifra_lifetime.ia6t_preferred = t + address->pltime; + in6_addreq.ifra_lifetime.ia6t_pltime = address->pltime; + + log_debug("%s: %s", __func__, if_name); + + if (ioctl(ioctl_sock, SIOCAIFADDR_IN6, &in6_addreq) == -1) + log_warn("SIOCAIFADDR_IN6"); +} + +void +deconfigure_address(struct imsg_configure_address *imsg) +{ + fatalx("%s: not implemented", __func__); /* XXX */ +} + +const char* +sin6_to_str(struct sockaddr_in6 *sin6) +{ + static char hbuf[NI_MAXHOST]; + int error; + + error = getnameinfo((struct sockaddr *)sin6, sin6->sin6_len, hbuf, + sizeof(hbuf), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV); + if (error) { + log_warnx("%s", gai_strerror(error)); + strlcpy(hbuf, "unknown", sizeof(hbuf)); + } + return hbuf; +} + +void +open_udpsock(uint32_t if_index) +{ + struct ifaddrs *ifap, *ifa; + struct sockaddr_in6 *sin6 = NULL; + int udpsock = -1, rdomain = -1, opt = 1; + char if_name[IF_NAMESIZE]; + + if (if_indextoname(if_index, if_name) == NULL) { + log_warnx("%s: cannot find interface %d", __func__, if_index); + return; + } + + if (getifaddrs(&ifap) != 0) + fatal("getifaddrs"); + for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { + if (strcmp(if_name, ifa->ifa_name) != 0) + continue; + if (ifa->ifa_addr == NULL) + continue; + switch (ifa->ifa_addr->sa_family) { + case AF_LINK: { + struct if_data *if_data; + + if_data = (struct if_data *)ifa->ifa_data; + rdomain = if_data->ifi_rdomain; + break; + } + case AF_INET6: { + struct sockaddr_in6 *s6; + s6 = (struct sockaddr_in6 *)ifa->ifa_addr; +#ifdef __KAME__ + if (IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr) && + s6->sin6_scope_id == 0) { + s6->sin6_scope_id = ntohs(*(u_int16_t *) + &s6->sin6_addr.s6_addr[2]); + s6->sin6_addr.s6_addr[2] = + s6->sin6_addr.s6_addr[3] = 0; + } +#endif + if (IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr)) + sin6 = s6; + break; + } + default: + break; + + } + } + + if (sin6 == NULL) { + log_warnx("%s: missing link-local address on %s", __func__, + if_name); + goto out; + } + + if (rdomain == -1) { + log_warnx("%s: cannot find rdomain for %s", __func__, + if_name); + goto out; + } + + sin6->sin6_port = htons(CLIENT_PORT); + log_debug("%s: %s rdomain: %d", __func__, sin6_to_str(sin6), + rdomain); + + if ((udpsock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) { + log_warn("socket"); + goto out; + } + if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR, &opt, + sizeof(opt)) == -1) + log_warn("setting SO_REUSEADDR on socket"); + + if (setsockopt(udpsock, SOL_SOCKET, SO_RTABLE, &rdomain, + sizeof(rdomain)) == -1) { + /* we might race against removal of the rdomain */ + log_warn("setsockopt SO_RTABLE"); + close(udpsock); + goto out; + } + + if (bind(udpsock, (struct sockaddr *)sin6, sizeof(*sin6)) == -1) { + close(udpsock); + goto out; + } + + main_imsg_compose_frontend(IMSG_UDPSOCK, udpsock, &if_index, + sizeof(if_index)); + out: + freeifaddrs(ifap); +} + +void +read_lease_file(struct imsg_ifinfo *imsg_ifinfo) +{ + int len, fd; + char if_name[IF_NAMESIZE]; + char lease_file_buf[sizeof(_PATH_LEASE) + IF_NAMESIZE]; + + if (no_lease_files) + return; + + memset(imsg_ifinfo->lease, 0, sizeof(imsg_ifinfo->lease)); + + if (if_indextoname(imsg_ifinfo->if_index, if_name) == NULL) { + log_warnx("%s: cannot find interface %d", __func__, + imsg_ifinfo->if_index); + return; + } + + len = snprintf(lease_file_buf, sizeof(lease_file_buf), "%s%s", + _PATH_LEASE, if_name); + if ( len == -1 || (size_t) len >= sizeof(lease_file_buf)) { + log_warnx("%s: failed to encode lease path for %s", __func__, + if_name); + return; + } + + if ((fd = open(lease_file_buf, O_RDONLY)) == -1) + return; + + /* no need for error handling, we'll just do a DHCP discover */ + read(fd, imsg_ifinfo->lease, sizeof(imsg_ifinfo->lease) - 1); + close(fd); +} + +void +merge_config(struct dhcp6leased_conf *conf, struct dhcp6leased_conf *xconf) +{ + struct iface_conf *iface_conf; + struct iface_ia_conf *ia_conf; + struct iface_pd_conf *pd_conf; + + /* Remove & discard existing interfaces. */ + while ((iface_conf = SIMPLEQ_FIRST(&conf->iface_list)) != NULL) { + SIMPLEQ_REMOVE_HEAD(&conf->iface_list, entry); + while ((ia_conf = + SIMPLEQ_FIRST(&iface_conf->iface_ia_list)) != NULL) { + SIMPLEQ_REMOVE_HEAD(&iface_conf->iface_ia_list, + entry); + while ((pd_conf = + SIMPLEQ_FIRST(&ia_conf->iface_pd_list)) != NULL) { + SIMPLEQ_REMOVE_HEAD(&ia_conf->iface_pd_list, + entry); + free(pd_conf); + } + free(ia_conf); + } + free(iface_conf); + } + + conf->rapid_commit = xconf->rapid_commit; + + /* Add new interfaces. */ + SIMPLEQ_CONCAT(&conf->iface_list, &xconf->iface_list); + + free(xconf); +} + +struct dhcp6leased_conf * +config_new_empty(void) +{ + struct dhcp6leased_conf *xconf; + + xconf = calloc(1, sizeof(*xconf)); + if (xconf == NULL) + fatal(NULL); + + SIMPLEQ_INIT(&xconf->iface_list); + + return (xconf); +} + +void +config_clear(struct dhcp6leased_conf *conf) +{ + struct dhcp6leased_conf *xconf; + + /* Merge current config with an empty config. */ + xconf = config_new_empty(); + merge_config(conf, xconf); + + free(conf); +} + +uint8_t* +get_uuid(void) { + static uint8_t uuid_buf[UUID_SIZE]; + uuid_t uuid; + uint32_t status; + int fd, len; + char *str; + char strbuf[UUID_STR_SIZE]; + char tmpl[] = _PATH_UUID"XXXXXXXXXX"; + + if ((fd = open(_PATH_UUID, O_RDONLY)) == -1) { + gen: + uuid_create(&uuid, NULL); + uuid_to_string(&uuid, &str, &status); + if (status != uuid_s_ok) + fatalx("failed to generate uuid string representation"); + + len = snprintf(strbuf, sizeof(strbuf), "%s\n", str); + if (len < 0 || (size_t)len >= sizeof(strbuf)) + fatalx("uuid string too long"); + free(str); + + if ((fd = mkstemp(tmpl)) == -1) { + log_warn("mkstemp"); + goto err; + } + if (write(fd, strbuf, len) < len) { + log_warn("write"); + goto err; + } + if (fchmod(fd, 0644) == -1) { + log_warn("fchmod"); + goto err; + } + + if (close(fd) == -1) { + log_warn("fchmod"); + goto err; + } + fd = -1; + if (rename(tmpl, _PATH_UUID) == -1) { + log_warn("rename"); + goto err; + } + } else { + read(fd, strbuf, sizeof(strbuf)); + close(fd); + strbuf[sizeof(strbuf) - 2] = '\0'; + + uuid_from_string(strbuf, &uuid, &status); + + if (status != uuid_s_ok) { + log_warnx("failed to convert string to uuid: %s - %d", + strbuf, status); + goto gen; + } + } + uuid_enc_be(uuid_buf, &uuid); + return (uuid_buf); + + err: + if (fd != -1) + close(fd); + unlink(tmpl); + fatal("Could not read or create UUID"); +} diff --git a/sbin/dhcp6leased/dhcp6leased.conf.5 b/sbin/dhcp6leased/dhcp6leased.conf.5 new file mode 100644 index 000000000..ca5f3fbf7 --- /dev/null +++ b/sbin/dhcp6leased/dhcp6leased.conf.5 @@ -0,0 +1,131 @@ +.\" $OpenBSD: dhcp6leased.conf.5,v 1.3 2024/06/03 11:08:31 florian Exp $ +.\" +.\" Copyright (c) 2018, 2021, 2024 Florian Obser +.\" Copyright (c) 2005 Esben Norby +.\" Copyright (c) 2004 Claudio Jeker +.\" Copyright (c) 2003, 2004 Henning Brauer +.\" Copyright (c) 2002 Daniel Hartmeier +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: June 3 2024 $ +.Dt DHCP6LEASED.CONF 5 +.Os +.Sh NAME +.Nm dhcp6leased.conf +.Nd DHCPv6 client configuration file +.Sh DESCRIPTION +The +.Xr dhcp6leased 8 +daemon is a dynamic host configuration protocol client daemon for IPv6 prefix +delegation. +.Pp +The +.Nm +config file is divided into the following main sections: +.Bl -tag -width xxxx +.It Sy Macros +User-defined variables may be defined and used later, simplifying the +configuration file. +.It Sy Global Configuration +Global settings for +.Xr dhcp6leased 8 . +.It Sy Prefix delegation +.Xr dhcp6leased 8 +requests prefix delegation from a DHCPv6 server and assigns prefixes +to interfaces. +This section defines on which interfaces prefix delegation should be +requested and to which interfaces prefixes should be assigned. +.El +.Sh MACROS +Macros can be defined that will later be expanded in context. +Macro names must start with a letter, digit, or underscore, +and may contain any of those characters. +Macro names may not be reserved words (for example, +.Ic interface ) . +Macros are not expanded inside quotes. +.Sh GLOBAL CONFIGURATION +These settings affect the operation of the +.Xr dhcp6leased 8 +daemon as a whole. +.Bl -tag -width Ds +.It Ic request rapid commit +Send the rapid commit DHCPv6 option, requesting a two-message exchange +from the server instead of the normal four-message exchange. +.El +.Sh PREFIX DELEGATION +A list of interfaces on which to request prefix delegation: +.Bd -unfilled -offset indent +.Ic request prefix delegation on Ar name Ic for Ar { name/prefix Oo Ar name/prefix ... Oc } +.Ed +.Pp +This requests a prefix delegation on +.Ar name +upstream network interface for the list of +.Ar name/prefix +network interfaces. +If +.Ar prefix +is omitted a default of /64 is used. +.Pp +.Xr dhcp6leased 8 +will calculate the prefix length needed to cover all interfaces in the list. +When a prefix is delegated by a DHCPv6 server, +.Xr dhcp6leased 8 +splits the prefix into smaller prefixes and assigns them to the interfaces +in the order they are listed. +This might create unassigned gaps in the delegated prefix. +.Pp +For example if a /64 and /60 prefix are to be assigned to network interfaces, +.Xr dhcp6leased 8 +requests a /59 prefix. +The prefix is then split into two /60 prefixes and the first /64 out of the +first /60 prefix is assigned to the first interface. +The second /60 prefix from the delegated /59 is assigned to the +second interface. +This leaves 15 unused /64 prefixes in the first /60. +.Pp +Care should be taken to avoid renumbering of existing interfaces +when new interfaces are added or existing interfaces are removed. +New interfaces can be added to the end of the list or in places +where unassigned gaps were present. +.Pp +The special name +.Cm reserve +can be used to reserve space in the delegated prefix for later use or +when an interface is removed. +.Pp +Running +.Xr dhcp6leased 8 +in configtest mode with a verbosity of two or more will print the +configuration file with comments indicated how prefixes would be +assigned to network interfaces. +This can be used to check that existing interface are not renumbered. +.Pp +More than one prefix can be requested from a DHCPv6 server, however most ISP +DHCPv6 servers will only delegate a single prefix. +Therefore it is better to let +.Xr dhcp6leased 8 +request a single larger prefix and split it up. +.Xr dhcp6leased 8 +has a compile time limit on how many prefix requests per interface it can +handle. +.Sh FILES +.Bl -tag -width /etc/dhcp6leased.conf -compact +.It Pa /etc/dhcp6leased.conf +.Xr dhcp6leased 8 +configuration file. +.El +.Sh SEE ALSO +.Xr dhcp6leasectl 8 , +.Xr dhcp6leased 8 diff --git a/sbin/dhcp6leased/dhcp6leased.h b/sbin/dhcp6leased/dhcp6leased.h new file mode 100644 index 000000000..ae85aa92a --- /dev/null +++ b/sbin/dhcp6leased/dhcp6leased.h @@ -0,0 +1,276 @@ +/* $OpenBSD: dhcp6leased.h,v 1.4 2024/06/03 11:08:31 florian Exp $ */ + +/* + * Copyright (c) 2017, 2021 Florian Obser + * Copyright (c) 2004 Esben Norby + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define _PATH_LOCKFILE "/dev/dhcp6leased.lock" +#define _PATH_CONF_FILE "/etc/dhcp6leased.conf" +#define _PATH_CTRL_SOCKET "/dev/dhcp6leased.sock" +#define DHCP6LEASED_USER "_dhcp6leased" +#define DHCP6LEASED_RTA_LABEL "dhcp6leased" +#define CLIENT_PORT 546 +#define SERVER_PORT 547 +#define _PATH_LEASE "/var/db/dhcp6leased/" +#define _PATH_UUID _PATH_LEASE"uuid" +#define UUID_SIZE 16 +#define UUID_STR_SIZE sizeof("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\n") +#define DUID_UUID_TYPE 4 +#define XID_SIZE 3 +#define SERVERID_SIZE 130 /* 2 octet type, max 128 octets data */ +#define MAX_IA 32 +#define LEASE_VERSION "version: 2" +#define LEASE_IP_PREFIX "ip: " +#define LEASE_NEXTSERVER_PREFIX "next-server: " +#define LEASE_BOOTFILE_PREFIX "filename: " +#define LEASE_HOSTNAME_PREFIX "host-name: " +#define LEASE_DOMAIN_PREFIX "domain-name: " +#define LEASE_SIZE 4096 +/* MAXDNAME from arpa/namesr.h */ +#define DHCP6LEASED_MAX_DNSSL 1025 +#define MAX_RDNS_COUNT 8 /* max nameserver in a RTM_PROPOSAL */ + +/* A 1500 bytes packet can hold less than 300 classless static routes */ +#define MAX_DHCP_ROUTES 256 + +#define OPENBSD_ENTERPRISENO 30155 + +/* DHCP message types. */ +#define DHCPSOLICIT 1 +#define DHCPADVERTISE 2 +#define DHCPREQUEST 3 +#define DHCPCONFIRM 4 +#define DHCPRENEW 5 +#define DHCPREBIND 6 +#define DHCPREPLY 7 +#define DHCPRELEASE 8 +#define DHCPDECLINE 9 +#define DHCPRECONFIGURE 10 +#define DHCPINFORMATIONREQUEST 11 +#define DHCPRELAYFORW 12 +#define DHCPRELAYREPL 13 + +/* DHCP options */ +#define DHO_CLIENTID 1 +#define DHO_SERVERID 2 +#define DHO_ORO 6 +#define DHO_ELAPSED_TIME 8 +#define DHO_STATUS_CODE 13 +#define DHO_RAPID_COMMIT 14 +#define DHO_VENDOR_CLASS 16 +#define DHO_IA_PD 25 +#define DHO_IA_PREFIX 26 +#define DHO_SOL_MAX_RT 82 +#define DHO_INF_MAX_RT 83 + +/* Status Code Option status codes */ +#define DHCP_STATUS_SUCCESS 0 +#define DHCP_STATUS_UNSPECFAIL 1 +#define DHCP_STATUS_NOADDRSAVAIL 2 +#define DHCP_STATUS_NOBINDING 3 +#define DHCP_STATUS_NOTONLINK 4 +#define DHCP_STATUS_USEMULTICAST 5 +#define DHCP_STATUS_NOPREFIXAVAIL 6 + +/* Ignore parts of DHCP lease */ +#define IGN_ROUTES 1 +#define IGN_DNS 2 + +#define MAX_SERVERS 16 /* max servers that can be ignored per if */ + +#define IMSG_DATA_SIZE(imsg) ((imsg).hdr.len - IMSG_HEADER_SIZE) +#define DHCP_SNAME_LEN 64 +#define DHCP_FILE_LEN 128 + +struct dhcp_hdr { + uint8_t msg_type; /* Message opcode/type */ + uint8_t xid[XID_SIZE]; /* Transaction ID */ +} __packed; + +struct dhcp_option_hdr { + uint16_t code; + uint16_t len; +} __packed; + +struct dhcp_duid { + uint16_t type; + uint8_t uuid[UUID_SIZE]; +} __packed; + +struct dhcp_iapd { + uint32_t iaid; + uint32_t t1; + uint32_t t2; +} __packed; + +struct dhcp_vendor_class { + uint32_t enterprise_number; + uint16_t vendor_class_len; +} __packed; + +struct dhcp_iaprefix { + uint32_t pltime; + uint32_t vltime; + uint8_t prefix_len; + struct in6_addr prefix; +} __packed; + +struct imsgev { + struct imsgbuf ibuf; + void (*handler)(int, short, void *); + struct event ev; + short events; +}; + +struct dhcp_route { + struct in_addr dst; + struct in_addr mask; + struct in_addr gw; +}; + +enum imsg_type { + IMSG_NONE, + IMSG_CTL_LOG_VERBOSE, + IMSG_CTL_SHOW_INTERFACE_INFO, + IMSG_CTL_SEND_REQUEST, + IMSG_CTL_RELOAD, + IMSG_CTL_END, + IMSG_RECONF_CONF, + IMSG_RECONF_IFACE, + IMSG_RECONF_IFACE_IA, + IMSG_RECONF_IFACE_PD, + IMSG_RECONF_IFACE_IA_END, + IMSG_RECONF_IFACE_END, + IMSG_RECONF_END, + IMSG_SEND_SOLICIT, + IMSG_SEND_REQUEST, + IMSG_SEND_RENEW, + IMSG_SEND_REBIND, + IMSG_SOCKET_IPC, + IMSG_OPEN_UDPSOCK, + IMSG_UDPSOCK, + IMSG_ROUTESOCK, + IMSG_UUID, + IMSG_CONTROLFD, + IMSG_STARTUP, + IMSG_UPDATE_IF, + IMSG_REMOVE_IF, + IMSG_DHCP, + IMSG_CONFIGURE_ADDRESS, + IMSG_DECONFIGURE_ADDRESS, + IMSG_REQUEST_REBOOT, +}; + +struct ctl_engine_info { + uint32_t if_index; + int running; + int link_state; + char state[sizeof("IF_INIT_REBOOT")]; + struct timespec request_time; + uint32_t lease_time; + uint32_t t1; + uint32_t t2; +}; + +struct iface_pd_conf { + SIMPLEQ_ENTRY(iface_pd_conf) entry; + char name[IF_NAMESIZE]; + struct in6_addr prefix_mask; + int prefix_len; +}; + +struct iface_ia_conf { + SIMPLEQ_ENTRY(iface_ia_conf) entry; + SIMPLEQ_HEAD(iface_pd_conf_head, iface_pd_conf) iface_pd_list; + int id; + int prefix_len; +}; + +struct iface_conf { + SIMPLEQ_ENTRY(iface_conf) entry; + SIMPLEQ_HEAD(iface_ia_conf_head, + iface_ia_conf) iface_ia_list; + uint32_t ia_count; + char name[IF_NAMESIZE]; +}; + +struct dhcp6leased_conf { + SIMPLEQ_HEAD(iface_conf_head, iface_conf) iface_list; + int rapid_commit; +}; + +struct imsg_ifinfo { + uint32_t if_index; + int rdomain; + int running; + int link_state; + char lease[LEASE_SIZE]; +}; + +struct imsg_propose_rdns { + uint32_t if_index; + int rdomain; + int rdns_count; + struct in_addr rdns[MAX_RDNS_COUNT]; +}; + +struct imsg_dhcp { + uint32_t if_index; + ssize_t len; + uint8_t packet[1500]; +}; + +struct prefix { + struct in6_addr prefix; + int prefix_len; + uint32_t vltime; + uint32_t pltime; +}; + +struct imsg_req_dhcp { + uint32_t if_index; + int elapsed_time; + uint8_t xid[XID_SIZE]; + int serverid_len; + uint8_t serverid[SERVERID_SIZE]; + struct prefix pds[MAX_IA]; +}; + +/* dhcp6leased.c */ +void imsg_event_add(struct imsgev *); +int imsg_compose_event(struct imsgev *, uint16_t, uint32_t, + pid_t, int, void *, uint16_t); +void config_clear(struct dhcp6leased_conf *); +struct dhcp6leased_conf *config_new_empty(void); +void merge_config(struct dhcp6leased_conf *, struct + dhcp6leased_conf *); +const char *sin6_to_str(struct sockaddr_in6 *); + +/* engine.c */ +const char *dhcp_message_type2str(uint8_t); + +/* frontend.c */ +struct iface_conf *find_iface_conf(struct iface_conf_head *, char *); +int *changed_ifaces(struct dhcp6leased_conf *, struct + dhcp6leased_conf *); +/* printconf.c */ +void print_config(struct dhcp6leased_conf *, int); + +/* parse.y */ +struct dhcp6leased_conf *parse_config(const char *); +int cmdline_symset(char *); + diff --git a/sbin/dhcp6leased/engine.c b/sbin/dhcp6leased/engine.c new file mode 100644 index 000000000..0e059c19b --- /dev/null +++ b/sbin/dhcp6leased/engine.c @@ -0,0 +1,1505 @@ +/* $OpenBSD: engine.c,v 1.7 2024/06/03 15:53:26 deraadt Exp $ */ + +/* + * Copyright (c) 2017, 2021, 2024 Florian Obser + * Copyright (c) 2004, 2005 Claudio Jeker + * Copyright (c) 2004 Esben Norby + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "dhcp6leased.h" +#include "engine.h" + +/* + * RFC 2131 4.1 p23 has "SHOULD be 4 seconds", we are a bit more aggressive, + * networks are faster these days. + */ +#define START_EXP_BACKOFF 1 +#define MAX_EXP_BACKOFF_SLOW 64 /* RFC 2131 4.1 p23 */ +#define MAX_EXP_BACKOFF_FAST 2 +#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) + +enum if_state { + IF_DOWN, + IF_INIT, + /* IF_SELECTING, */ + IF_REQUESTING, + IF_BOUND, + IF_RENEWING, + IF_REBINDING, + /* IF_INIT_REBOOT, */ + IF_REBOOTING, +}; + +const char* if_state_name[] = { + "Down", + "Init", + /* "Selecting", */ + "Requesting", + "Bound", + "Renewing", + "Rebinding", + /* "Init-Reboot", */ + "Rebooting", + "IPv6 only", +}; + +struct dhcp6leased_iface { + LIST_ENTRY(dhcp6leased_iface) entries; + enum if_state state; + struct event timer; + struct timeval timo; + uint32_t if_index; + int rdomain; + int running; + int link_state; + uint8_t xid[XID_SIZE]; + int serverid_len; + uint8_t serverid[SERVERID_SIZE]; + struct prefix pds[MAX_IA]; + struct timespec request_time; + struct timespec elapsed_time_start; + uint32_t lease_time; + uint32_t t1; + uint32_t t2; +}; + +LIST_HEAD(, dhcp6leased_iface) dhcp6leased_interfaces; + +__dead void engine_shutdown(void); +void engine_sig_handler(int sig, short, void *); +void engine_dispatch_frontend(int, short, void *); +void engine_dispatch_main(int, short, void *); +void send_interface_info(struct dhcp6leased_iface *, pid_t); +void engine_showinfo_ctl(struct imsg *, uint32_t); +void engine_update_iface(struct imsg_ifinfo *); +struct dhcp6leased_iface *get_dhcp6leased_iface_by_id(uint32_t); +void remove_dhcp6leased_iface(uint32_t); +void parse_dhcp(struct dhcp6leased_iface *, + struct imsg_dhcp *); +void parse_ia_pd_options(uint8_t *, size_t, struct prefix *); +void state_transition(struct dhcp6leased_iface *, enum + if_state); +void iface_timeout(int, short, void *); +void request_dhcp_discover(struct dhcp6leased_iface *); +void request_dhcp_request(struct dhcp6leased_iface *); +void log_lease(struct dhcp6leased_iface *, int); +void configure_interfaces(struct dhcp6leased_iface *); +void send_configure_interface(struct iface_pd_conf *, + struct prefix *); +void send_deconfigure_interface(struct dhcp6leased_iface *); +void parse_lease(struct dhcp6leased_iface *, + struct imsg_ifinfo *); +int engine_imsg_compose_main(int, pid_t, void *, uint16_t); +const char *dhcp_message_type2str(uint8_t); +const char *dhcp_option_type2str(uint16_t); +const char *dhcp_duid2str(int, uint8_t *); +const char *dhcp_status2str(uint8_t); +void in6_prefixlen2mask(struct in6_addr *, int len); + +struct dhcp6leased_conf *engine_conf; + +static struct imsgev *iev_frontend; +static struct imsgev *iev_main; +int64_t proposal_id; +static struct dhcp_duid duid; + +void +engine_sig_handler(int sig, short event, void *arg) +{ + /* + * Normal signal handler rules don't apply because libevent + * decouples for us. + */ + + switch (sig) { + case SIGINT: + case SIGTERM: + engine_shutdown(); + default: + fatalx("unexpected signal"); + } +} + +void +engine(int debug, int verbose) +{ + struct event ev_sigint, ev_sigterm; + struct passwd *pw; + + engine_conf = config_new_empty(); + + log_init(debug, LOG_DAEMON); + log_setverbose(verbose); + + if ((pw = getpwnam(DHCP6LEASED_USER)) == NULL) + fatal("getpwnam"); + + if (chdir("/") == -1) + fatal("chdir(\"/\")"); + + if (unveil("/", "") == -1) + fatal("unveil /"); + if (unveil(NULL, NULL) == -1) + fatal("unveil"); + + setproctitle("%s", "engine"); + log_procinit("engine"); + + if (setgroups(1, &pw->pw_gid) || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) + fatal("can't drop privileges"); + + if (pledge("stdio recvfd", NULL) == -1) + fatal("pledge"); + + event_init(); + + /* Setup signal handler(s). */ + signal_set(&ev_sigint, SIGINT, engine_sig_handler, NULL); + signal_set(&ev_sigterm, SIGTERM, engine_sig_handler, NULL); + signal_add(&ev_sigint, NULL); + signal_add(&ev_sigterm, NULL); + signal(SIGPIPE, SIG_IGN); + signal(SIGHUP, SIG_IGN); + + /* Setup pipe and event handler to the main process. */ + if ((iev_main = malloc(sizeof(struct imsgev))) == NULL) + fatal(NULL); + + imsg_init(&iev_main->ibuf, 3); + iev_main->handler = engine_dispatch_main; + + /* Setup event handlers. */ + iev_main->events = EV_READ; + event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events, + iev_main->handler, iev_main); + event_add(&iev_main->ev, NULL); + + LIST_INIT(&dhcp6leased_interfaces); + + event_dispatch(); + + engine_shutdown(); +} + +__dead void +engine_shutdown(void) +{ + /* Close pipes. */ + msgbuf_clear(&iev_frontend->ibuf.w); + close(iev_frontend->ibuf.fd); + msgbuf_clear(&iev_main->ibuf.w); + close(iev_main->ibuf.fd); + + free(iev_frontend); + free(iev_main); + + log_info("engine exiting"); + exit(0); +} + +int +engine_imsg_compose_frontend(int type, pid_t pid, void *data, + uint16_t datalen) +{ + return (imsg_compose_event(iev_frontend, type, 0, pid, -1, + data, datalen)); +} + +int +engine_imsg_compose_main(int type, pid_t pid, void *data, + uint16_t datalen) +{ + return (imsg_compose_event(iev_main, type, 0, pid, -1, + data, datalen)); +} + +void +engine_dispatch_frontend(int fd, short event, void *bula) +{ + struct imsgev *iev = bula; + struct imsgbuf *ibuf = &iev->ibuf; + struct imsg imsg; + struct dhcp6leased_iface *iface; + ssize_t n; + int shut = 0; + int verbose; + uint32_t if_index; + + if (event & EV_READ) { + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* Connection closed. */ + shut = 1; + } + if (event & EV_WRITE) { + if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) + fatal("msgbuf_write"); + if (n == 0) /* Connection closed. */ + shut = 1; + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("%s: imsg_get error", __func__); + if (n == 0) /* No more messages. */ + break; + + switch (imsg.hdr.type) { + case IMSG_CTL_LOG_VERBOSE: + if (IMSG_DATA_SIZE(imsg) != sizeof(verbose)) + fatalx("%s: IMSG_CTL_LOG_VERBOSE wrong length: " + "%lu", __func__, IMSG_DATA_SIZE(imsg)); + memcpy(&verbose, imsg.data, sizeof(verbose)); + log_setverbose(verbose); + break; + case IMSG_CTL_SHOW_INTERFACE_INFO: + if (IMSG_DATA_SIZE(imsg) != sizeof(if_index)) + fatalx("%s: IMSG_CTL_SHOW_INTERFACE_INFO wrong " + "length: %lu", __func__, + IMSG_DATA_SIZE(imsg)); + memcpy(&if_index, imsg.data, sizeof(if_index)); + engine_showinfo_ctl(&imsg, if_index); + break; + case IMSG_REQUEST_REBOOT: + if (IMSG_DATA_SIZE(imsg) != sizeof(if_index)) + fatalx("%s: IMSG_CTL_SEND_DISCOVER wrong " + "length: %lu", __func__, + IMSG_DATA_SIZE(imsg)); + memcpy(&if_index, imsg.data, sizeof(if_index)); + iface = get_dhcp6leased_iface_by_id(if_index); + if (iface != NULL) { + switch (iface->state) { + case IF_DOWN: + break; + case IF_INIT: + case IF_REQUESTING: + state_transition(iface, iface->state); + break; + case IF_RENEWING: + case IF_REBINDING: + case IF_REBOOTING: + case IF_BOUND: + state_transition(iface, IF_REBOOTING); + break; + } + } + break; + case IMSG_REMOVE_IF: + if (IMSG_DATA_SIZE(imsg) != sizeof(if_index)) + fatalx("%s: IMSG_REMOVE_IF wrong length: %lu", + __func__, IMSG_DATA_SIZE(imsg)); + memcpy(&if_index, imsg.data, sizeof(if_index)); + remove_dhcp6leased_iface(if_index); + break; + case IMSG_DHCP: { + struct imsg_dhcp imsg_dhcp; + if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_dhcp)) + fatalx("%s: IMSG_DHCP wrong length: %lu", + __func__, IMSG_DATA_SIZE(imsg)); + memcpy(&imsg_dhcp, imsg.data, sizeof(imsg_dhcp)); + iface = get_dhcp6leased_iface_by_id(imsg_dhcp.if_index); + if (iface != NULL) + parse_dhcp(iface, &imsg_dhcp); + break; + } + default: + log_debug("%s: unexpected imsg %d", __func__, + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(iev); + else { + /* This pipe is dead. Remove its event handler. */ + event_del(&iev->ev); + event_loopexit(NULL); + } +} + +void +engine_dispatch_main(int fd, short event, void *bula) +{ + static struct dhcp6leased_conf *nconf; + static struct iface_conf *iface_conf; + static struct iface_ia_conf *iface_ia_conf; + struct iface_pd_conf *iface_pd_conf; + struct imsg imsg; + struct imsgev *iev = bula; + struct imsgbuf *ibuf = &iev->ibuf; + struct imsg_ifinfo imsg_ifinfo; + ssize_t n; + int shut = 0; + + if (event & EV_READ) { + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* Connection closed. */ + shut = 1; + } + if (event & EV_WRITE) { + if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) + fatal("msgbuf_write"); + if (n == 0) /* Connection closed. */ + shut = 1; + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("%s: imsg_get error", __func__); + if (n == 0) /* No more messages. */ + break; + + switch (imsg.hdr.type) { + case IMSG_SOCKET_IPC: + /* + * Setup pipe and event handler to the frontend + * process. + */ + if (iev_frontend) + fatalx("%s: received unexpected imsg fd " + "to engine", __func__); + + if ((fd = imsg_get_fd(&imsg)) == -1) + fatalx("%s: expected to receive imsg fd to " + "engine but didn't receive any", __func__); + + iev_frontend = malloc(sizeof(struct imsgev)); + if (iev_frontend == NULL) + fatal(NULL); + + imsg_init(&iev_frontend->ibuf, fd); + iev_frontend->handler = engine_dispatch_frontend; + iev_frontend->events = EV_READ; + + event_set(&iev_frontend->ev, iev_frontend->ibuf.fd, + iev_frontend->events, iev_frontend->handler, + iev_frontend); + event_add(&iev_frontend->ev, NULL); + + if (pledge("stdio", NULL) == -1) + fatal("pledge"); + + break; + case IMSG_UUID: + if (IMSG_DATA_SIZE(imsg) != sizeof(duid.uuid)) + fatalx("%s: IMSG_UUID wrong length: " + "%lu", __func__, IMSG_DATA_SIZE(imsg)); + duid.type = htons(DUID_UUID_TYPE); + memcpy(duid.uuid, imsg.data, sizeof(duid.uuid)); + break; + case IMSG_UPDATE_IF: + if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_ifinfo)) + fatalx("%s: IMSG_UPDATE_IF wrong length: %lu", + __func__, IMSG_DATA_SIZE(imsg)); + memcpy(&imsg_ifinfo, imsg.data, sizeof(imsg_ifinfo)); + if (imsg_ifinfo.lease[LEASE_SIZE - 1] != '\0') + fatalx("Invalid lease"); + engine_update_iface(&imsg_ifinfo); + break; + case IMSG_RECONF_CONF: + if (nconf != NULL) + fatalx("%s: IMSG_RECONF_CONF already in " + "progress", __func__); + if (IMSG_DATA_SIZE(imsg) != + sizeof(struct dhcp6leased_conf)) + fatalx("%s: IMSG_RECONF_CONF wrong length: %lu", + __func__, IMSG_DATA_SIZE(imsg)); + if ((nconf = malloc(sizeof(struct dhcp6leased_conf))) == + NULL) + fatal(NULL); + memcpy(nconf, imsg.data, + sizeof(struct dhcp6leased_conf)); + SIMPLEQ_INIT(&nconf->iface_list); + break; + case IMSG_RECONF_IFACE: + if (IMSG_DATA_SIZE(imsg) != sizeof(struct + iface_conf)) + fatalx("%s: IMSG_RECONF_IFACE wrong length: " + "%lu", __func__, IMSG_DATA_SIZE(imsg)); + if ((iface_conf = malloc(sizeof(struct iface_conf))) + == NULL) + fatal(NULL); + memcpy(iface_conf, imsg.data, sizeof(struct + iface_conf)); + SIMPLEQ_INIT(&iface_conf->iface_ia_list); + SIMPLEQ_INSERT_TAIL(&nconf->iface_list, + iface_conf, entry); + iface_conf->ia_count = 0; + break; + case IMSG_RECONF_IFACE_IA: + if (IMSG_DATA_SIZE(imsg) != sizeof(struct + iface_ia_conf)) + fatalx("%s: IMSG_RECONF_IFACE_IA wrong " + "length: %lu", __func__, + IMSG_DATA_SIZE(imsg)); + if ((iface_ia_conf = + malloc(sizeof(struct iface_ia_conf))) == NULL) + fatal(NULL); + memcpy(iface_ia_conf, imsg.data, sizeof(struct + iface_ia_conf)); + SIMPLEQ_INIT(&iface_ia_conf->iface_pd_list); + SIMPLEQ_INSERT_TAIL(&iface_conf->iface_ia_list, + iface_ia_conf, entry); + iface_conf->ia_count++; + if (iface_conf->ia_count > MAX_IA) + fatalx("Too many prefix delegation requests."); + break; + case IMSG_RECONF_IFACE_PD: + if (IMSG_DATA_SIZE(imsg) != sizeof(struct + iface_pd_conf)) + fatalx("%s: IMSG_RECONF_IFACE_PD wrong length: " + "%lu", __func__, IMSG_DATA_SIZE(imsg)); + if ((iface_pd_conf = + malloc(sizeof(struct iface_pd_conf))) == NULL) + fatal(NULL); + memcpy(iface_pd_conf, imsg.data, sizeof(struct + iface_pd_conf)); + SIMPLEQ_INSERT_TAIL(&iface_ia_conf->iface_pd_list, + iface_pd_conf, entry); + break; + case IMSG_RECONF_IFACE_IA_END: + iface_ia_conf = NULL; + break; + case IMSG_RECONF_IFACE_END: + iface_conf = NULL; + break; + case IMSG_RECONF_END: { + struct dhcp6leased_iface *iface; + int *ifaces; + int i, if_index; + char *if_name; + char ifnamebuf[IF_NAMESIZE]; + + if (nconf == NULL) + fatalx("%s: IMSG_RECONF_END without " + "IMSG_RECONF_CONF", __func__); + ifaces = changed_ifaces(engine_conf, nconf); + merge_config(engine_conf, nconf); + nconf = NULL; + for (i = 0; ifaces[i] != 0; i++) { + if_index = ifaces[i]; + if_name = if_indextoname(if_index, ifnamebuf); + iface = get_dhcp6leased_iface_by_id(if_index); + if (if_name == NULL || iface == NULL) + continue; + iface_conf = find_iface_conf( + &engine_conf->iface_list, if_name); + if (iface_conf == NULL) + continue; + } + free(ifaces); + break; + } + default: + log_debug("%s: unexpected imsg %d", __func__, + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(iev); + else { + /* This pipe is dead. Remove its event handler. */ + event_del(&iev->ev); + event_loopexit(NULL); + } +} + +void +send_interface_info(struct dhcp6leased_iface *iface, pid_t pid) +{ + struct ctl_engine_info cei; + + memset(&cei, 0, sizeof(cei)); + cei.if_index = iface->if_index; + cei.running = iface->running; + cei.link_state = iface->link_state; + strlcpy(cei.state, if_state_name[iface->state], sizeof(cei.state)); + memcpy(&cei.request_time, &iface->request_time, + sizeof(cei.request_time)); + cei.lease_time = iface->lease_time; + cei.t1 = iface->t1; + cei.t2 = iface->t2; + engine_imsg_compose_frontend(IMSG_CTL_SHOW_INTERFACE_INFO, pid, &cei, + sizeof(cei)); +} + +void +engine_showinfo_ctl(struct imsg *imsg, uint32_t if_index) +{ + struct dhcp6leased_iface *iface; + + switch (imsg->hdr.type) { + case IMSG_CTL_SHOW_INTERFACE_INFO: + if ((iface = get_dhcp6leased_iface_by_id(if_index)) != NULL) + send_interface_info(iface, imsg->hdr.pid); + else + engine_imsg_compose_frontend(IMSG_CTL_END, + imsg->hdr.pid, NULL, 0); + break; + default: + log_debug("%s: error handling imsg", __func__); + break; + } +} + +void +engine_update_iface(struct imsg_ifinfo *imsg_ifinfo) +{ + struct dhcp6leased_iface *iface; + int need_refresh = 0; + + iface = get_dhcp6leased_iface_by_id(imsg_ifinfo->if_index); + + if (iface == NULL) { + if ((iface = calloc(1, sizeof(*iface))) == NULL) + fatal("calloc"); + iface->state = IF_DOWN; + arc4random_buf(iface->xid, sizeof(iface->xid)); + iface->timo.tv_usec = arc4random_uniform(1000000); + evtimer_set(&iface->timer, iface_timeout, iface); + iface->if_index = imsg_ifinfo->if_index; + iface->rdomain = imsg_ifinfo->rdomain; + iface->running = imsg_ifinfo->running; + iface->link_state = imsg_ifinfo->link_state; + LIST_INSERT_HEAD(&dhcp6leased_interfaces, iface, entries); + need_refresh = 1; + } else { + if (imsg_ifinfo->rdomain != iface->rdomain) { + iface->rdomain = imsg_ifinfo->rdomain; + need_refresh = 1; + } + if (imsg_ifinfo->running != iface->running) { + iface->running = imsg_ifinfo->running; + need_refresh = 1; + } + + if (imsg_ifinfo->link_state != iface->link_state) { + iface->link_state = imsg_ifinfo->link_state; + need_refresh = 1; + } + } + + if (!need_refresh) + return; + + if (iface->running && LINK_STATE_IS_UP(iface->link_state)) { +#if 0 +XXXX + if (iface->requested_ip.s_addr == INADDR_ANY) + parse_lease(iface, imsg_ifinfo); + + if (iface->requested_ip.s_addr == INADDR_ANY) + state_transition(iface, IF_INIT); + else + state_transition(iface, IF_REBOOTING); +#endif + state_transition(iface, IF_INIT); + } else + state_transition(iface, IF_DOWN); +} +struct dhcp6leased_iface* +get_dhcp6leased_iface_by_id(uint32_t if_index) +{ + struct dhcp6leased_iface *iface; + LIST_FOREACH (iface, &dhcp6leased_interfaces, entries) { + if (iface->if_index == if_index) + return (iface); + } + + return (NULL); +} + +void +remove_dhcp6leased_iface(uint32_t if_index) +{ + struct dhcp6leased_iface *iface; + + iface = get_dhcp6leased_iface_by_id(if_index); + + if (iface == NULL) + return; + + send_deconfigure_interface(iface); + LIST_REMOVE(iface, entries); + evtimer_del(&iface->timer); + free(iface); +} + +void +parse_dhcp(struct dhcp6leased_iface *iface, struct imsg_dhcp *dhcp) +{ + struct iface_conf *iface_conf; + struct iface_ia_conf *ia_conf; + struct dhcp_hdr hdr; + struct dhcp_option_hdr opt_hdr; + struct dhcp_iapd iapd; + struct prefix *pds = NULL; + size_t rem; + uint32_t t1, t2, lease_time; + int serverid_len, rapid_commit = 0; + uint8_t serverid[SERVERID_SIZE]; + uint8_t *p; + char ifnamebuf[IF_NAMESIZE], *if_name; + char ntopbuf[INET6_ADDRSTRLEN]; + + if ((if_name = if_indextoname(iface->if_index, ifnamebuf)) == NULL) { + log_debug("%s: unknown interface %d", __func__, + iface->if_index); + goto out; + } + if ((iface_conf = find_iface_conf(&engine_conf->iface_list, if_name)) + == NULL) { + log_debug("%s: no interface configuration for %d", __func__, + iface->if_index); + goto out; + } + + log_debug("%s: %s ia_count: %d", __func__, if_name, + iface_conf->ia_count); + + pds = calloc(iface_conf->ia_count, sizeof(struct prefix)); + if (pds == NULL) + fatal("%s: calloc", __func__); + + serverid_len = t1 = t2 = lease_time = 0; + + p = dhcp->packet; + rem = dhcp->len; + + if (rem < sizeof(struct dhcp_hdr)) { + log_warnx("%s: message too short", __func__); + goto out; + } + memcpy(&hdr, p, sizeof(struct dhcp_hdr)); + p += sizeof(struct dhcp_hdr); + rem -= sizeof(struct dhcp_hdr); + + if (log_getverbose() > 1) + log_debug("%s: %s, xid: 0x%02x%02x%02x", __func__, + dhcp_message_type2str(hdr.msg_type), hdr.xid[0], hdr.xid[1], + hdr.xid[2]); + + while (rem >= sizeof(struct dhcp_option_hdr)) { + memcpy(&opt_hdr, p, sizeof(struct dhcp_option_hdr)); + opt_hdr.code = ntohs(opt_hdr.code); + opt_hdr.len = ntohs(opt_hdr.len); + p += sizeof(struct dhcp_option_hdr); + rem -= sizeof(struct dhcp_option_hdr); + if (log_getverbose() > 1) + log_debug("%s: %s, len: %u", __func__, + dhcp_option_type2str(opt_hdr.code), opt_hdr.len); + + if (rem < opt_hdr.len) { + log_warnx("%s: malformed packet, ignoring", __func__); + goto out; + } + + switch (opt_hdr.code) { + case DHO_CLIENTID: + if (opt_hdr.len != sizeof(struct dhcp_duid) || + memcmp(&duid, p, sizeof(struct dhcp_duid)) != 0) { + log_debug("%s: message not for us", __func__); + goto out; + } + break; + case DHO_SERVERID: + /* + * RFC 8415, 11.1: + * The length of the DUID (not including the type code) + * is at least 1 octet and at most 128 octets. + */ + if (opt_hdr.len < 2 + 1) { + log_warnx("%s: SERVERID too short", __func__); + goto out; + } + if (opt_hdr.len > SERVERID_SIZE) { + log_warnx("%s: SERVERID too long", __func__); + goto out; + } + log_debug("%s: SERVERID: %s", __func__, + dhcp_duid2str(opt_hdr.len, p)); + if (serverid_len != 0) { + log_warnx("%s: duplicate SERVERID option", + __func__); + goto out; + } + serverid_len = opt_hdr.len; + memcpy(serverid, p, serverid_len); + break; + case DHO_IA_PD: + if (opt_hdr.len < sizeof(struct dhcp_iapd)) { + log_warnx("%s: IA_PD too short", __func__); + goto out; + } + memcpy(&iapd, p, sizeof(struct dhcp_iapd)); + + if (t1 == 0 || t1 > ntohl(iapd.t1)) + t1 = ntohl(iapd.t1); + if (t2 == 0 || t2 > ntohl(iapd.t2)) + t2 = ntohl(iapd.t2); + + log_debug("%s: IA_PD, IAID: %08x, T1: %u, T2: %u", + __func__, ntohl(iapd.iaid), ntohl(iapd.t1), + ntohl(iapd.t2)); + if (ntohl(iapd.iaid) <= iface_conf->ia_count) + parse_ia_pd_options(p + + sizeof(struct dhcp_iapd), opt_hdr.len - + sizeof(struct dhcp_iapd), + &pds[ntohl(iapd.iaid) -1]); + break; + case DHO_RAPID_COMMIT: + if (opt_hdr.len != 0) { + log_warnx("%s: invalid rapid commit option", + __func__); + goto out; + } + rapid_commit = 1; + break; + default: + log_debug("unhandled option: %u", opt_hdr.code); + break; + } + + p += opt_hdr.len; + rem -= opt_hdr.len; + } + + /* check that we got all the information we need */ + if (serverid_len == 0) { + log_warnx("%s: Did not receive server identifier", __func__); + goto out; + } + + + SIMPLEQ_FOREACH(ia_conf, &iface_conf->iface_ia_list, entry) { + struct prefix *pd = &pds[ia_conf->id - 1]; + + if (pd->prefix_len == 0) { + log_warnx("%s: no IA for IAID %d found", __func__, + ia_conf->id); + goto out; + } + if (pd->prefix_len > ia_conf->prefix_len) { + log_warnx("%s: prefix for IAID %d too small: %d > %d", + __func__, ia_conf->id, pd->prefix_len, + ia_conf->prefix_len); + goto out; + } + + if (lease_time < pd->vltime) + lease_time = pd->vltime; + + log_debug("%s: pltime: %u, vltime: %u, prefix: %s/%u", + __func__, pd->pltime, pd->vltime, inet_ntop(AF_INET6, + &pd->prefix, ntopbuf, INET6_ADDRSTRLEN), pd->prefix_len); + } + + switch (hdr.msg_type) { + case DHCPSOLICIT: + case DHCPREQUEST: + case DHCPCONFIRM: + case DHCPRENEW: + case DHCPREBIND: + case DHCPRELEASE: + case DHCPDECLINE: + case DHCPINFORMATIONREQUEST: + log_warnx("%s: Ignoring client-only message (%s) from server", + __func__, dhcp_message_type2str(hdr.msg_type)); + goto out; + case DHCPRELAYFORW: + case DHCPRELAYREPL: + log_warnx("%s: Ignoring relay-agent-only message (%s) from " + "server", __func__, dhcp_message_type2str(hdr.msg_type)); + goto out; + case DHCPADVERTISE: + if (iface->state != IF_INIT) { + log_debug("%s: ignoring unexpected %s", __func__, + dhcp_message_type2str(hdr.msg_type)); + goto out; + } + iface->serverid_len = serverid_len; + memcpy(iface->serverid, serverid, SERVERID_SIZE); + memset(iface->pds, 0, sizeof(iface->pds)); + memcpy(iface->pds, pds, + iface_conf->ia_count * sizeof(struct prefix)); + state_transition(iface, IF_REQUESTING); + break; + case DHCPREPLY: + switch (iface->state) { + case IF_REQUESTING: + case IF_RENEWING: + case IF_REBINDING: + break; + case IF_INIT: + if (rapid_commit && engine_conf->rapid_commit) + break; + /* fall through */ + default: + log_debug("%s: ignoring unexpected %s", __func__, + dhcp_message_type2str(hdr.msg_type)); + goto out; + } + iface->serverid_len = serverid_len; + memcpy(iface->serverid, serverid, SERVERID_SIZE); + memset(iface->pds, 0, sizeof(iface->pds)); + memcpy(iface->pds, pds, + iface_conf->ia_count * sizeof(struct prefix)); + /* XXX handle t1 = 0 or t2 = 0 */ + iface->t1 = t1; + iface->t2 = t2; + iface->lease_time = lease_time; + clock_gettime(CLOCK_MONOTONIC, &iface->request_time); + state_transition(iface, IF_BOUND); + break; + case DHCPRECONFIGURE: + log_warnx("%s: Ignoring %s from server", + __func__, dhcp_message_type2str(hdr.msg_type)); + goto out; + default: + fatalx("%s: %s unhandled", + __func__, dhcp_message_type2str(hdr.msg_type)); + break; + } + out: + free(pds); +} + +void +parse_ia_pd_options(uint8_t *p, size_t len, struct prefix *prefix) +{ + struct dhcp_option_hdr opt_hdr; + struct dhcp_iaprefix iaprefix; + struct in6_addr mask; + int i; + uint16_t status_code; + char ntopbuf[INET6_ADDRSTRLEN], *visbuf; + + while (len >= sizeof(struct dhcp_option_hdr)) { + memcpy(&opt_hdr, p, sizeof(struct dhcp_option_hdr)); + opt_hdr.code = ntohs(opt_hdr.code); + opt_hdr.len = ntohs(opt_hdr.len); + p += sizeof(struct dhcp_option_hdr); + len -= sizeof(struct dhcp_option_hdr); + if (log_getverbose() > 1) + log_debug("%s: %s, len: %u", __func__, + dhcp_option_type2str(opt_hdr.code), opt_hdr.len); + if (len < opt_hdr.len) { + log_warnx("%s: malformed packet, ignoring", __func__); + return; + } + + switch (opt_hdr.code) { + case DHO_IA_PREFIX: + if (len < sizeof(struct dhcp_iaprefix)) { + log_warnx("%s: malformed packet, ignoring", + __func__); + return; + } + + memcpy(&iaprefix, p, sizeof(struct dhcp_iaprefix)); + log_debug("%s: pltime: %u, vltime: %u, prefix: %s/%u", + __func__, ntohl(iaprefix.pltime), + ntohl(iaprefix.vltime), inet_ntop(AF_INET6, + &iaprefix.prefix, ntopbuf, INET6_ADDRSTRLEN), + iaprefix.prefix_len); + if (ntohl(iaprefix.vltime) < ntohl(iaprefix.pltime)) { + log_warnx("%s: vltime < pltime, ignoring IA_PD", + __func__); + break; + } + + prefix->prefix = iaprefix.prefix; + prefix->prefix_len = iaprefix.prefix_len; + prefix->vltime = ntohl(iaprefix.vltime); + prefix->pltime = ntohl(iaprefix.pltime); + + /* make sure prefix is mask correctly */ + memset(&mask, 0, sizeof(mask)); + in6_prefixlen2mask(&mask, prefix->prefix_len); + for (i = 0; i < 16; i++) + prefix->prefix.s6_addr[i] &= mask.s6_addr[i]; + + break; + case DHO_STATUS_CODE: + /* + * XXX handle STATUS_CODE if not success + * STATUS_CODE can also appear in other parts of + * the packet. + */ + if (len < 2) { + log_warnx("%s: malformed packet, ignoring", + __func__); + return; + } + memcpy(&status_code, p, sizeof(uint16_t)); + status_code = ntohs(status_code); + visbuf = calloc(4, len - 2); + strvisx(visbuf, p + 2, len - 2, VIS_SAFE); + log_debug("%s: %s - %s", __func__, + dhcp_status2str(status_code), visbuf); + break; + default: + log_debug("unhandled option: %u", opt_hdr.code); + } + p += opt_hdr.len; + len -= opt_hdr.len; + } +} + +/* XXX check valid transitions */ +void +state_transition(struct dhcp6leased_iface *iface, enum if_state new_state) +{ + enum if_state old_state = iface->state; + char ifnamebuf[IF_NAMESIZE], *if_name; + + iface->state = new_state; + + switch (new_state) { + case IF_DOWN: +#if 0 +XXXX + if (iface->requested_ip.s_addr == INADDR_ANY) { + /* nothing to do until iface comes up */ + iface->timo.tv_sec = -1; + break; + } + if (old_state == IF_DOWN) { + /* nameservers already withdrawn when if went down */ + send_deconfigure_interface(iface); + /* nothing more to do until iface comes back */ + iface->timo.tv_sec = -1; + } else { + clock_gettime(CLOCK_MONOTONIC, &now); + timespecsub(&now, &iface->request_time, &res); + iface->timo.tv_sec = iface->lease_time - res.tv_sec; + if (iface->timo.tv_sec < 0) + iface->timo.tv_sec = 0; /* deconfigure now */ + } +#endif + /* nothing to do until iface comes up */ + iface->timo.tv_sec = -1; + break; + case IF_INIT: + switch (old_state) { + case IF_INIT: + if (iface->timo.tv_sec < MAX_EXP_BACKOFF_SLOW) + iface->timo.tv_sec *= 2; + break; + case IF_REQUESTING: + case IF_RENEWING: + case IF_REBINDING: + case IF_REBOOTING: + /* lease expired, got DHCPNAK or timeout: delete IP */ + send_deconfigure_interface(iface); + /* fall through */ + case IF_DOWN: + iface->timo.tv_sec = START_EXP_BACKOFF; + clock_gettime(CLOCK_MONOTONIC, + &iface->elapsed_time_start); + break; + case IF_BOUND: + fatal("invalid transition Bound -> Init"); + break; + } + request_dhcp_discover(iface); + break; + case IF_REBOOTING: + if (old_state == IF_REBOOTING) + iface->timo.tv_sec *= 2; + else { + iface->timo.tv_sec = START_EXP_BACKOFF; + arc4random_buf(iface->xid, sizeof(iface->xid)); + } + request_dhcp_request(iface); + break; + case IF_REQUESTING: + if (old_state == IF_REQUESTING) + iface->timo.tv_sec *= 2; + else { + iface->timo.tv_sec = START_EXP_BACKOFF; + clock_gettime(CLOCK_MONOTONIC, + &iface->elapsed_time_start); + } + request_dhcp_request(iface); + break; + case IF_BOUND: + iface->timo.tv_sec = iface->t1; + switch (old_state) { + case IF_REQUESTING: + case IF_RENEWING: + case IF_REBINDING: + case IF_REBOOTING: + configure_interfaces(iface); + break; + case IF_INIT: + if (engine_conf->rapid_commit) + configure_interfaces(iface); + else + fatal("invalid transition Init -> Bound"); + break; + default: + break; + } + break; + case IF_RENEWING: + if (old_state == IF_BOUND) { + iface->timo.tv_sec = (iface->t2 - + iface->t1) / 2; /* RFC 2131 4.4.5 */ + arc4random_buf(iface->xid, sizeof(iface->xid)); + } else + iface->timo.tv_sec /= 2; + + if (iface->timo.tv_sec < 60) + iface->timo.tv_sec = 60; + request_dhcp_request(iface); + break; + case IF_REBINDING: + if (old_state == IF_RENEWING) { + iface->timo.tv_sec = (iface->lease_time - + iface->t2) / 2; /* RFC 2131 4.4.5 */ + } else + iface->timo.tv_sec /= 2; + request_dhcp_request(iface); + break; + } + + if_name = if_indextoname(iface->if_index, ifnamebuf); + log_debug("%s[%s] %s -> %s, timo: %lld", __func__, if_name == NULL ? + "?" : if_name, if_state_name[old_state], if_state_name[new_state], + iface->timo.tv_sec); + + if (iface->timo.tv_sec == -1) { + if (evtimer_pending(&iface->timer, NULL)) + evtimer_del(&iface->timer); + } else + evtimer_add(&iface->timer, &iface->timo); +} + +void +iface_timeout(int fd, short events, void *arg) +{ + struct dhcp6leased_iface *iface = (struct dhcp6leased_iface *)arg; + struct timespec now, res; + + log_debug("%s[%d]: %s", __func__, iface->if_index, + if_state_name[iface->state]); + + switch (iface->state) { + case IF_DOWN: + state_transition(iface, IF_DOWN); + break; + case IF_INIT: + state_transition(iface, IF_INIT); + break; + case IF_REBOOTING: + if (iface->timo.tv_sec >= MAX_EXP_BACKOFF_FAST) + state_transition(iface, IF_INIT); + else + state_transition(iface, IF_REBOOTING); + break; + case IF_REQUESTING: + if (iface->timo.tv_sec >= MAX_EXP_BACKOFF_SLOW) + state_transition(iface, IF_INIT); + else + state_transition(iface, IF_REQUESTING); + break; + case IF_BOUND: + state_transition(iface, IF_RENEWING); + break; + case IF_RENEWING: + clock_gettime(CLOCK_MONOTONIC, &now); + timespecsub(&now, &iface->request_time, &res); + log_debug("%s: res.tv_sec: %lld, t2: %u", __func__, + res.tv_sec, iface->t2); + if (res.tv_sec >= iface->t2) + state_transition(iface, IF_REBINDING); + else + state_transition(iface, IF_RENEWING); + break; + case IF_REBINDING: + clock_gettime(CLOCK_MONOTONIC, &now); + timespecsub(&now, &iface->request_time, &res); + log_debug("%s: res.tv_sec: %lld, lease_time: %u", __func__, + res.tv_sec, iface->lease_time); + if (res.tv_sec > iface->lease_time) + state_transition(iface, IF_INIT); + else + state_transition(iface, IF_REBINDING); + break; + } +} + +/* XXX can this be merged into dhcp_request()? */ +void +request_dhcp_discover(struct dhcp6leased_iface *iface) +{ + struct imsg_req_dhcp imsg; + struct timespec now, res; + + memset(&imsg, 0, sizeof(imsg)); + imsg.if_index = iface->if_index; + memcpy(imsg.xid, iface->xid, sizeof(imsg.xid)); + clock_gettime(CLOCK_MONOTONIC, &now); + timespecsub(&now, &iface->elapsed_time_start, &res); + if (res.tv_sec * 100 > 0xffff) + imsg.elapsed_time = 0xffff; + else + imsg.elapsed_time = res.tv_sec * 100; + engine_imsg_compose_frontend(IMSG_SEND_SOLICIT, 0, &imsg, sizeof(imsg)); +} + +void +request_dhcp_request(struct dhcp6leased_iface *iface) +{ + struct imsg_req_dhcp imsg; + struct timespec now, res; + + memset(&imsg, 0, sizeof(imsg)); + imsg.if_index = iface->if_index; + memcpy(imsg.xid, iface->xid, sizeof(imsg.xid)); + + clock_gettime(CLOCK_MONOTONIC, &now); + timespecsub(&now, &iface->elapsed_time_start, &res); + if (res.tv_sec * 100 > 0xffff) + imsg.elapsed_time = 0xffff; + else + imsg.elapsed_time = res.tv_sec * 100; + + switch (iface->state) { + case IF_DOWN: + fatalx("invalid state IF_DOWN in %s", __func__); + break; + case IF_INIT: + fatalx("invalid state IF_INIT in %s", __func__); + break; + case IF_BOUND: + fatalx("invalid state IF_BOUND in %s", __func__); + break; + case IF_REBOOTING: + fatalx("XXX state IF_REBOOTING in %s not IMPL", __func__); + break; + case IF_REQUESTING: + case IF_RENEWING: + case IF_REBINDING: + imsg.serverid_len = iface->serverid_len; + memcpy(imsg.serverid, iface->serverid, SERVERID_SIZE); + memcpy(imsg.pds, iface->pds, sizeof(iface->pds)); + break; + } + switch (iface->state) { + case IF_REQUESTING: + engine_imsg_compose_frontend(IMSG_SEND_REQUEST, 0, &imsg, + sizeof(imsg)); + break; + case IF_RENEWING: + engine_imsg_compose_frontend(IMSG_SEND_RENEW, 0, &imsg, + sizeof(imsg)); + break; + case IF_REBINDING: + engine_imsg_compose_frontend(IMSG_SEND_REBIND, 0, &imsg, + sizeof(imsg)); + break; + default: + fatalx("%s: wrong state", __func__); + } +} + +void +log_lease(struct dhcp6leased_iface *iface, int deconfigure) +{ + fatalx("%s: not implemented", __func__); /* XXX */ +} + +/* XXX we need to install a reject route for the delegated prefix */ +void +configure_interfaces(struct dhcp6leased_iface *iface) +{ + struct iface_conf *iface_conf; + struct iface_ia_conf *ia_conf; + struct iface_pd_conf *pd_conf; + char ifnamebuf[IF_NAMESIZE], *if_name; + + + if ((if_name = if_indextoname(iface->if_index, ifnamebuf)) == NULL) { + log_debug("%s: unknown interface %d", __func__, + iface->if_index); + return; + } + if ((iface_conf = find_iface_conf(&engine_conf->iface_list, if_name)) + == NULL) { + log_debug("%s: no interface configuration for %d", __func__, + iface->if_index); + return; + } + + SIMPLEQ_FOREACH(ia_conf, &iface_conf->iface_ia_list, entry) { + struct prefix *pd = &iface->pds[ia_conf->id - 1]; + + SIMPLEQ_FOREACH(pd_conf, &ia_conf->iface_pd_list, entry) { + send_configure_interface(pd_conf, pd); + } + } +} + +void +send_configure_interface(struct iface_pd_conf *pd_conf, struct prefix *pd) +{ + struct imsg_configure_address address; + uint32_t if_index; + int i; + char ntopbuf[INET6_ADDRSTRLEN]; + + if (strcmp(pd_conf->name, "reserve") == 0) + return; + + if ((if_index = if_nametoindex(pd_conf->name)) == 0) + return; + + memset(&address, 0, sizeof(address)); + + address.if_index = if_index; + address.addr.sin6_family = AF_INET6; + address.addr.sin6_len = sizeof(address.addr); + address.addr.sin6_addr = pd->prefix; + + for (i = 0; i < 16; i++) + address.addr.sin6_addr.s6_addr[i] |= + pd_conf->prefix_mask.s6_addr[i]; + + /* XXX make this configurable & use SOII */ + address.addr.sin6_addr.s6_addr[15] |= 1; + + in6_prefixlen2mask(&address.mask, pd_conf->prefix_len); + + log_debug("%s: %s: %s/%d", __func__, pd_conf->name, + inet_ntop(AF_INET6, &address.addr.sin6_addr, ntopbuf, + INET6_ADDRSTRLEN), pd_conf->prefix_len); + + address.vltime = pd->vltime; + address.pltime = pd->pltime; + + engine_imsg_compose_main(IMSG_CONFIGURE_ADDRESS, 0, &address, + sizeof(address)); +} + +void +send_deconfigure_interface(struct dhcp6leased_iface *iface) +{ + fatalx("%s: not implemented", __func__); /* XXX */ +} + +void +parse_lease(struct dhcp6leased_iface *iface, struct imsg_ifinfo *imsg_ifinfo) +{ + fatalx("%s: not implemented", __func__); /* XXX */ +} + +const char * +dhcp_message_type2str(uint8_t type) +{ + static char buf[sizeof("Unknown [255]")]; + + switch (type) { + case DHCPSOLICIT: + return "DHCPSOLICIT"; + case DHCPADVERTISE: + return "DHCPADVERTISE"; + case DHCPREQUEST: + return "DHCPREQUEST"; + case DHCPCONFIRM: + return "DHCPCONFIRM"; + case DHCPRENEW: + return "DHCPRENEW"; + case DHCPREBIND: + return "DHCPREBIND"; + case DHCPREPLY: + return "DHCPREPLY"; + case DHCPRELEASE: + return "DHCPRELEASE"; + case DHCPDECLINE: + return "DHCPDECLINE"; + case DHCPRECONFIGURE: + return "DHCPRECONFIGURE"; + case DHCPINFORMATIONREQUEST: + return "DHCPINFORMATIONREQUEST"; + case DHCPRELAYFORW: + return "DHCPRELAYFORW"; + case DHCPRELAYREPL: + return "DHCPRELAYREPL"; + default: + snprintf(buf, sizeof(buf), "Unknown [%u]", type); + return buf; + } +} + +const char * +dhcp_option_type2str(uint16_t code) +{ + static char buf[sizeof("Unknown [65535]")]; + switch (code) { + case DHO_CLIENTID: + return "DHO_CLIENTID"; + case DHO_SERVERID: + return "DHO_SERVERID"; + case DHO_ORO: + return "DHO_ORO"; + case DHO_ELAPSED_TIME: + return "DHO_ELAPSED_TIME"; + case DHO_STATUS_CODE: + return "DHO_STATUS_CODE"; + case DHO_RAPID_COMMIT: + return "DHO_RAPID_COMMIT"; + case DHO_VENDOR_CLASS: + return "DHO_VENDOR_CLASS"; + case DHO_IA_PD: + return "DHO_IA_PD"; + case DHO_IA_PREFIX: + return "DHO_IA_PREFIX"; + case DHO_SOL_MAX_RT: + return "DHO_SOL_MAX_RT"; + case DHO_INF_MAX_RT: + return "DHO_INF_MAX_RT"; + default: + snprintf(buf, sizeof(buf), "Unknown [%u]", code); + return buf; + } +} + +const char* +dhcp_duid2str(int len, uint8_t *p) +{ + static char buf[2 * 130]; + int i, rem; + char *pbuf; + + if (len > 130) + return "invalid"; + + pbuf = buf; + rem = sizeof(buf); + for (i = 0; i < len && rem > 0; i++, pbuf += 2, rem -=2) + snprintf(pbuf, rem, "%02x", p[i]); + + return buf; +} + +const char* +dhcp_status2str(uint8_t status) +{ + static char buf[sizeof("Unknown [255]")]; + + switch (status) { + case DHCP_STATUS_SUCCESS: + return "Success"; + case DHCP_STATUS_UNSPECFAIL: + return "UnspecFail"; + case DHCP_STATUS_NOADDRSAVAIL: + return "NoAddrsAvail"; + case DHCP_STATUS_NOBINDING: + return "NoBinding"; + case DHCP_STATUS_NOTONLINK: + return "NotOnLink"; + case DHCP_STATUS_USEMULTICAST: + return "UseMulticast"; + case DHCP_STATUS_NOPREFIXAVAIL: + return "NoPrefixAvail"; + default: + snprintf(buf, sizeof(buf), "Unknown [%u]", status); + return buf; + } +} + +/* from sys/netinet6/in6.c */ +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +void +in6_prefixlen2mask(struct in6_addr *maskp, int len) +{ + u_char maskarray[8] = {0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff}; + int bytelen, bitlen, i; + + if (0 > len || len > 128) + fatalx("%s: invalid prefix length(%d)\n", __func__, len); + + bzero(maskp, sizeof(*maskp)); + bytelen = len / 8; + bitlen = len % 8; + for (i = 0; i < bytelen; i++) + maskp->s6_addr[i] = 0xff; + /* len == 128 is ok because bitlen == 0 then */ + if (bitlen) + maskp->s6_addr[bytelen] = maskarray[bitlen - 1]; +} diff --git a/sbin/dhcp6leased/engine.h b/sbin/dhcp6leased/engine.h new file mode 100644 index 000000000..61c0fd15f --- /dev/null +++ b/sbin/dhcp6leased/engine.h @@ -0,0 +1,28 @@ +/* $OpenBSD: engine.h,v 1.1 2024/06/02 12:28:05 florian Exp $ */ + +/* + * Copyright (c) 2021 Florian Obser + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +struct imsg_configure_address { + uint32_t if_index; + struct sockaddr_in6 addr; + struct in6_addr mask; + uint32_t vltime; + uint32_t pltime; +}; + +void engine(int, int); +int engine_imsg_compose_frontend(int, pid_t, void *, uint16_t); diff --git a/sbin/dhcp6leased/frontend.c b/sbin/dhcp6leased/frontend.c new file mode 100644 index 000000000..d683a6823 --- /dev/null +++ b/sbin/dhcp6leased/frontend.c @@ -0,0 +1,1092 @@ +/* $OpenBSD: frontend.c,v 1.8 2024/06/03 15:53:26 deraadt Exp $ */ + +/* + * Copyright (c) 2017, 2021, 2024 Florian Obser + * Copyright (c) 2005 Claudio Jeker + * Copyright (c) 2004 Esben Norby + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "dhcp6leased.h" +#include "frontend.h" +#include "control.h" + +#define ALL_DHCP_RELAY_AGENTS_AND_SERVERS "ff02::1:2" +#define ROUTE_SOCKET_BUF_SIZE 16384 + +struct iface { + LIST_ENTRY(iface) entries; + struct event udpev; + struct imsg_ifinfo ifinfo; + int send_solicit; + int elapsed_time; + uint8_t xid[XID_SIZE]; + int serverid_len; + uint8_t serverid[SERVERID_SIZE]; + struct prefix pds[MAX_IA]; +}; + +__dead void frontend_shutdown(void); +void frontend_sig_handler(int, short, void *); +void rtsock_update_iface(struct if_msghdr *, struct sockaddr_dl *); +void frontend_startup(void); +void update_iface(uint32_t); +void route_receive(int, short, void *); +void handle_route_message(struct rt_msghdr *, struct sockaddr **); +void get_rtaddrs(int, struct sockaddr *, struct sockaddr **); +void udp_receive(int, short, void *); +int get_flags(char *); +struct iface *get_iface_by_id(uint32_t); +struct iface *get_iface_by_name(const char *); +void remove_iface(uint32_t); +void set_udpsock(int, uint32_t); +void iface_data_from_imsg(struct iface*, struct imsg_req_dhcp *); +ssize_t build_packet(uint8_t, struct iface *, char *); +void send_packet(uint8_t, struct iface *); +int iface_conf_cmp(struct iface_conf *, struct iface_conf *); + +LIST_HEAD(, iface) interfaces; +struct dhcp6leased_conf *frontend_conf; +static struct imsgev *iev_main; +static struct imsgev *iev_engine; +struct event ev_route; +struct sockaddr_in6 dst; +int ioctlsock; + +uint8_t dhcp_packet[1500]; +static struct dhcp_duid duid; +char *vendor_class_data; +int vendor_class_len; + +void +frontend_sig_handler(int sig, short event, void *bula) +{ + /* + * Normal signal handler rules don't apply because libevent + * decouples for us. + */ + + switch (sig) { + case SIGINT: + case SIGTERM: + frontend_shutdown(); + default: + fatalx("unexpected signal"); + } +} + +void +frontend(int debug, int verbose) +{ + struct event ev_sigint, ev_sigterm; + struct passwd *pw; + struct utsname utsname; + + frontend_conf = config_new_empty(); + + log_init(debug, LOG_DAEMON); + log_setverbose(verbose); + + if ((pw = getpwnam(DHCP6LEASED_USER)) == NULL) + fatal("getpwnam"); + + if (chdir("/") == -1) + fatal("chdir(\"/\")"); + + if (unveil("/", "") == -1) + fatal("unveil /"); + if (unveil(NULL, NULL) == -1) + fatal("unveil"); + + setproctitle("%s", "frontend"); + log_procinit("frontend"); + + if ((ioctlsock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) == -1) + fatal("socket"); + + if (setgroups(1, &pw->pw_gid) || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) + fatal("can't drop privileges"); + + if (pledge("stdio unix recvfd route", NULL) == -1) + fatal("pledge"); + + if (uname(&utsname) == -1) + fatal("uname"); + vendor_class_len = asprintf(&vendor_class_data, "%s %s %s", + utsname.sysname, utsname.release, utsname.machine); + if (vendor_class_len == -1) + fatal("Cannot generate vendor-class-data"); + + memset(&dst, 0, sizeof(dst)); + dst.sin6_family = AF_INET6; + if (inet_pton(AF_INET6, ALL_DHCP_RELAY_AGENTS_AND_SERVERS, + &dst.sin6_addr.s6_addr) != 1) + fatal("inet_pton"); + + dst.sin6_port = ntohs(SERVER_PORT); + + event_init(); + + /* Setup signal handler. */ + signal_set(&ev_sigint, SIGINT, frontend_sig_handler, NULL); + signal_set(&ev_sigterm, SIGTERM, frontend_sig_handler, NULL); + signal_add(&ev_sigint, NULL); + signal_add(&ev_sigterm, NULL); + signal(SIGPIPE, SIG_IGN); + signal(SIGHUP, SIG_IGN); + + /* Setup pipe and event handler to the parent process. */ + if ((iev_main = malloc(sizeof(struct imsgev))) == NULL) + fatal(NULL); + imsg_init(&iev_main->ibuf, 3); + iev_main->handler = frontend_dispatch_main; + iev_main->events = EV_READ; + event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events, + iev_main->handler, iev_main); + event_add(&iev_main->ev, NULL); + + LIST_INIT(&interfaces); + event_dispatch(); + + frontend_shutdown(); +} + +__dead void +frontend_shutdown(void) +{ + /* Close pipes. */ + msgbuf_write(&iev_engine->ibuf.w); + msgbuf_clear(&iev_engine->ibuf.w); + close(iev_engine->ibuf.fd); + msgbuf_write(&iev_main->ibuf.w); + msgbuf_clear(&iev_main->ibuf.w); + close(iev_main->ibuf.fd); + + config_clear(frontend_conf); + + free(iev_engine); + free(iev_main); + + log_info("frontend exiting"); + exit(0); +} + +int +frontend_imsg_compose_main(int type, pid_t pid, void *data, + uint16_t datalen) +{ + return (imsg_compose_event(iev_main, type, 0, pid, -1, data, + datalen)); +} + +int +frontend_imsg_compose_engine(int type, uint32_t peerid, pid_t pid, + void *data, uint16_t datalen) +{ + return (imsg_compose_event(iev_engine, type, peerid, pid, -1, + data, datalen)); +} + +void +frontend_dispatch_main(int fd, short event, void *bula) +{ + static struct dhcp6leased_conf *nconf; + static struct iface_conf *iface_conf; + static struct iface_ia_conf *iface_ia_conf; + struct iface_pd_conf *iface_pd_conf; + struct imsg imsg; + struct imsgev *iev = bula; + struct imsgbuf *ibuf = &iev->ibuf; + ssize_t n; + int shut = 0, udpsock, if_index; + + if (event & EV_READ) { + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* Connection closed. */ + shut = 1; + } + if (event & EV_WRITE) { + if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) + fatal("msgbuf_write"); + if (n == 0) /* Connection closed. */ + shut = 1; + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("%s: imsg_get error", __func__); + if (n == 0) /* No more messages. */ + break; + + switch (imsg.hdr.type) { + case IMSG_SOCKET_IPC: + /* + * Setup pipe and event handler to the engine + * process. + */ + if (iev_engine) + fatalx("%s: received unexpected imsg fd " + "to frontend", __func__); + + if ((fd = imsg_get_fd(&imsg)) == -1) + fatalx("%s: expected to receive imsg fd to " + "frontend but didn't receive any", + __func__); + + iev_engine = malloc(sizeof(struct imsgev)); + if (iev_engine == NULL) + fatal(NULL); + + imsg_init(&iev_engine->ibuf, fd); + iev_engine->handler = frontend_dispatch_engine; + iev_engine->events = EV_READ; + + event_set(&iev_engine->ev, iev_engine->ibuf.fd, + iev_engine->events, iev_engine->handler, iev_engine); + event_add(&iev_engine->ev, NULL); + break; + case IMSG_UDPSOCK: + if ((udpsock = imsg_get_fd(&imsg)) == -1) + fatalx("%s: expected to receive imsg " + "udp fd but didn't receive any", + __func__); + if (IMSG_DATA_SIZE(imsg) != sizeof(if_index)) + fatalx("%s: IMSG_UDPSOCK wrong length: " + "%lu", __func__, IMSG_DATA_SIZE(imsg)); + memcpy(&if_index, imsg.data, sizeof(if_index)); + set_udpsock(udpsock, if_index); + break; + case IMSG_ROUTESOCK: + if ((fd = imsg_get_fd(&imsg)) == -1) + fatalx("%s: expected to receive imsg " + "routesocket fd but didn't receive any", + __func__); + event_set(&ev_route, fd, EV_READ | EV_PERSIST, + route_receive, NULL); + break; + case IMSG_UUID: + if (IMSG_DATA_SIZE(imsg) != sizeof(duid.uuid)) + fatalx("%s: IMSG_UUID wrong length: " + "%lu", __func__, IMSG_DATA_SIZE(imsg)); + duid.type = htons(DUID_UUID_TYPE); + memcpy(duid.uuid, imsg.data, sizeof(duid.uuid)); + break; + case IMSG_STARTUP: + frontend_startup(); + break; + case IMSG_RECONF_CONF: + if (nconf != NULL) + fatalx("%s: IMSG_RECONF_CONF already in " + "progress", __func__); + if (IMSG_DATA_SIZE(imsg) != + sizeof(struct dhcp6leased_conf)) + fatalx("%s: IMSG_RECONF_CONF wrong length: %lu", + __func__, IMSG_DATA_SIZE(imsg)); + if ((nconf = malloc(sizeof(struct dhcp6leased_conf))) == + NULL) + fatal(NULL); + memcpy(nconf, imsg.data, + sizeof(struct dhcp6leased_conf)); + SIMPLEQ_INIT(&nconf->iface_list); + break; + case IMSG_RECONF_IFACE: + if (IMSG_DATA_SIZE(imsg) != sizeof(struct + iface_conf)) + fatalx("%s: IMSG_RECONF_IFACE wrong length: " + "%lu", __func__, IMSG_DATA_SIZE(imsg)); + if ((iface_conf = malloc(sizeof(struct iface_conf))) + == NULL) + fatal(NULL); + memcpy(iface_conf, imsg.data, sizeof(struct + iface_conf)); + SIMPLEQ_INIT(&iface_conf->iface_ia_list); + SIMPLEQ_INSERT_TAIL(&nconf->iface_list, + iface_conf, entry); + iface_conf->ia_count = 0; + break; + case IMSG_RECONF_IFACE_IA: + if (IMSG_DATA_SIZE(imsg) != sizeof(struct + iface_ia_conf)) + fatalx("%s: IMSG_RECONF_IFACE_IA wrong " + "length: %lu", __func__, + IMSG_DATA_SIZE(imsg)); + if ((iface_ia_conf = + malloc(sizeof(struct iface_ia_conf))) == NULL) + fatal(NULL); + memcpy(iface_ia_conf, imsg.data, sizeof(struct + iface_ia_conf)); + SIMPLEQ_INIT(&iface_ia_conf->iface_pd_list); + SIMPLEQ_INSERT_TAIL(&iface_conf->iface_ia_list, + iface_ia_conf, entry); + iface_conf->ia_count++; + if (iface_conf->ia_count > MAX_IA) + fatalx("Too many prefix delegation requests."); + break; + case IMSG_RECONF_IFACE_PD: + if (IMSG_DATA_SIZE(imsg) != sizeof(struct + iface_pd_conf)) + fatalx("%s: IMSG_RECONF_IFACE_PD wrong length: " + "%lu", __func__, IMSG_DATA_SIZE(imsg)); + if ((iface_pd_conf = + malloc(sizeof(struct iface_pd_conf))) == NULL) + fatal(NULL); + memcpy(iface_pd_conf, imsg.data, sizeof(struct + iface_pd_conf)); + SIMPLEQ_INSERT_TAIL(&iface_ia_conf->iface_pd_list, + iface_pd_conf, entry); + break; + case IMSG_RECONF_IFACE_IA_END: + iface_ia_conf = NULL; + break; + case IMSG_RECONF_IFACE_END: + iface_conf = NULL; + break; + case IMSG_RECONF_END: { + int i; + int *ifaces; + char ifnamebuf[IF_NAMESIZE], *if_name; + + if (nconf == NULL) + fatalx("%s: IMSG_RECONF_END without " + "IMSG_RECONF_CONF", __func__); + + ifaces = changed_ifaces(frontend_conf, nconf); + merge_config(frontend_conf, nconf); + nconf = NULL; + for (i = 0; ifaces[i] != 0; i++) { + if_index = ifaces[i]; + if_name = if_indextoname(if_index, ifnamebuf); + log_debug("changed iface: %s[%d]", if_name != + NULL ? if_name : "", if_index); + update_iface(if_index); + frontend_imsg_compose_engine( + IMSG_REQUEST_REBOOT, 0, 0, &if_index, + sizeof(if_index)); + } + free(ifaces); + break; + } + case IMSG_CONTROLFD: + if ((fd = imsg_get_fd(&imsg)) == -1) + fatalx("%s: expected to receive imsg " + "control fd but didn't receive any", + __func__); + /* Listen on control socket. */ + control_listen(fd); + break; + case IMSG_CTL_END: + control_imsg_relay(&imsg); + break; + default: + log_debug("%s: error handling imsg %d", __func__, + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(iev); + else { + /* This pipe is dead. Remove its event handler. */ + event_del(&iev->ev); + event_loopexit(NULL); + } +} + +void +frontend_dispatch_engine(int fd, short event, void *bula) +{ + struct imsgev *iev = bula; + struct imsgbuf *ibuf = &iev->ibuf; + struct imsg imsg; + struct iface *iface; + ssize_t n; + int shut = 0; + + if (event & EV_READ) { + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) /* Connection closed. */ + shut = 1; + } + if (event & EV_WRITE) { + if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) + fatal("msgbuf_write"); + if (n == 0) /* Connection closed. */ + shut = 1; + } + + for (;;) { + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("%s: imsg_get error", __func__); + if (n == 0) /* No more messages. */ + break; + + switch (imsg.hdr.type) { + case IMSG_CTL_END: + case IMSG_CTL_SHOW_INTERFACE_INFO: + control_imsg_relay(&imsg); + break; + case IMSG_SEND_SOLICIT: + case IMSG_SEND_REQUEST: + case IMSG_SEND_RENEW: + case IMSG_SEND_REBIND: { + struct imsg_req_dhcp imsg_req_dhcp; + if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_req_dhcp)) + fatalx("%s: IMSG_SEND_DISCOVER wrong " + "length: %lu", __func__, + IMSG_DATA_SIZE(imsg)); + memcpy(&imsg_req_dhcp, imsg.data, + sizeof(imsg_req_dhcp)); + + iface = get_iface_by_id(imsg_req_dhcp.if_index); + + if (iface == NULL) + break; + + iface_data_from_imsg(iface, &imsg_req_dhcp); + switch (imsg.hdr.type) { + case IMSG_SEND_SOLICIT: + send_packet(DHCPSOLICIT, iface); + break; + case IMSG_SEND_REQUEST: + send_packet(DHCPREQUEST, iface); + break; + case IMSG_SEND_RENEW: + send_packet(DHCPRENEW, iface); + break; + case IMSG_SEND_REBIND: + send_packet(DHCPREBIND, iface); + break; + } + break; + } + default: + log_debug("%s: error handling imsg %d", __func__, + imsg.hdr.type); + break; + } + imsg_free(&imsg); + } + if (!shut) + imsg_event_add(iev); + else { + /* This pipe is dead. Remove its event handler. */ + event_del(&iev->ev); + event_loopexit(NULL); + } +} + +int +get_flags(char *if_name) +{ + struct ifreq ifr; + + strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)); + if (ioctl(ioctlsock, SIOCGIFFLAGS, (caddr_t)&ifr) == -1) { + log_warn("SIOCGIFFLAGS"); + return -1; + } + return ifr.ifr_flags; +} + +void +update_iface(uint32_t if_index) +{ + struct ifaddrs *ifap, *ifa; + struct iface *iface; + struct imsg_ifinfo ifinfo; + int flags; + char ifnamebuf[IF_NAMESIZE], *if_name; + + if (getifaddrs(&ifap) != 0) + fatal("getifaddrs"); + + if ((if_name = if_indextoname(if_index, ifnamebuf)) == NULL) + return; + + if ((flags = get_flags(if_name)) == -1) + return; + + memset(&ifinfo, 0, sizeof(ifinfo)); + ifinfo.if_index = if_index; + ifinfo.link_state = -1; + ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) == + (IFF_UP | IFF_RUNNING); + + for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { + if (strcmp(if_name, ifa->ifa_name) != 0) + continue; + if (ifa->ifa_addr == NULL) + continue; + + switch (ifa->ifa_addr->sa_family) { + case AF_LINK: { + struct if_data *if_data; + + if_data = (struct if_data *)ifa->ifa_data; + ifinfo.link_state = if_data->ifi_link_state; + ifinfo.rdomain = if_data->ifi_rdomain; + goto out; + } + default: + break; + } + } + out: + freeifaddrs(ifap); + iface = get_iface_by_id(if_index); + if (iface == NULL) { + if ((iface = calloc(1, sizeof(*iface))) == NULL) + fatal("calloc"); + memcpy(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo)); + LIST_INSERT_HEAD(&interfaces, iface, entries); + frontend_imsg_compose_main(IMSG_OPEN_UDPSOCK, 0, + &if_index, sizeof(if_index)); + } else + /* XXX check rdomain changed ?*/ + memcpy(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo)); + + frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &iface->ifinfo, + sizeof(iface->ifinfo)); +} + +void +rtsock_update_iface(struct if_msghdr *ifm, struct sockaddr_dl *sdl) +{ +#if 0 +XXX + struct iface *iface; + struct imsg_ifinfo ifinfo; + uint32_t if_index; + int flags; + char ifnamebuf[IF_NAMESIZE], *if_name; + + if_index = ifm->ifm_index; + + flags = ifm->ifm_flags; + + iface = get_iface_by_id(if_index); + if_name = if_indextoname(if_index, ifnamebuf); + + if (if_name == NULL) { + if (iface != NULL) { + log_debug("interface with idx %d removed", if_index); + frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, + &if_index, sizeof(if_index)); + remove_iface(if_index); + } + return; + } + + memset(&ifinfo, 0, sizeof(ifinfo)); + ifinfo.if_index = if_index; + ifinfo.link_state = ifm->ifm_data.ifi_link_state; + ifinfo.rdomain = ifm->ifm_tableid; + ifinfo.running = (flags & (IFF_UP | IFF_RUNNING)) == + (IFF_UP | IFF_RUNNING); + + if (iface == NULL) { + if ((iface = calloc(1, sizeof(*iface))) == NULL) + fatal("calloc"); + iface->udpsock = -1; + LIST_INSERT_HEAD(&interfaces, iface, entries); + frontend_imsg_compose_main(IMSG_OPEN_UDPSOCK, 0, + &if_index, sizeof(if_index)); + } else { + if (iface->ifinfo.rdomain != ifinfo.rdomain && + iface->udpsock != -1) { + close(iface->udpsock); + iface->udpsock = -1; + } + } + + if (memcmp(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo)) != 0) { + memcpy(&iface->ifinfo, &ifinfo, sizeof(iface->ifinfo)); + frontend_imsg_compose_main(IMSG_UPDATE_IF, 0, &iface->ifinfo, + sizeof(iface->ifinfo)); + } +#endif +} + +void +frontend_startup(void) +{ + if (!event_initialized(&ev_route)) + fatalx("%s: did not receive a route socket from the main " + "process", __func__); + + if (pledge("stdio unix recvfd", NULL) == -1) + fatal("pledge"); + event_add(&ev_route, NULL); +} + +void +route_receive(int fd, short events, void *arg) +{ + static uint8_t *buf; + + struct rt_msghdr *rtm; + struct sockaddr *sa, *rti_info[RTAX_MAX]; + ssize_t n; + + if (buf == NULL) { + buf = malloc(ROUTE_SOCKET_BUF_SIZE); + if (buf == NULL) + fatal("malloc"); + } + rtm = (struct rt_msghdr *)buf; + if ((n = read(fd, buf, ROUTE_SOCKET_BUF_SIZE)) == -1) { + if (errno == EAGAIN || errno == EINTR) + return; + log_warn("dispatch_rtmsg: read error"); + return; + } + + if (n == 0) + fatal("routing socket closed"); + + if (n < (ssize_t)sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen) { + log_warnx("partial rtm of %zd in buffer", n); + return; + } + + if (rtm->rtm_version != RTM_VERSION) + return; + + sa = (struct sockaddr *)(buf + rtm->rtm_hdrlen); + get_rtaddrs(rtm->rtm_addrs, sa, rti_info); + + handle_route_message(rtm, rti_info); +} + +void +handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info) +{ + struct sockaddr_dl *sdl = NULL; + struct if_announcemsghdr *ifan; + uint32_t if_index; + + switch (rtm->rtm_type) { + case RTM_IFINFO: + if (rtm->rtm_addrs & RTA_IFP && rti_info[RTAX_IFP]->sa_family + == AF_LINK) + sdl = (struct sockaddr_dl *)rti_info[RTAX_IFP]; + rtsock_update_iface((struct if_msghdr *)rtm, sdl); + break; + case RTM_IFANNOUNCE: + ifan = (struct if_announcemsghdr *)rtm; + if_index = ifan->ifan_index; + if (ifan->ifan_what == IFAN_DEPARTURE) { + frontend_imsg_compose_engine(IMSG_REMOVE_IF, 0, 0, + &if_index, sizeof(if_index)); + remove_iface(if_index); + } + break; + default: + log_debug("unexpected RTM: %d", rtm->rtm_type); + break; + } +} + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) + +void +get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info) +{ + int i; + + for (i = 0; i < RTAX_MAX; i++) { + if (addrs & (1 << i)) { + rti_info[i] = sa; + sa = (struct sockaddr *)((char *)(sa) + + ROUNDUP(sa->sa_len)); + } else + rti_info[i] = NULL; + } +} + +void +udp_receive(int fd, short events, void *arg) +{ + struct imsg_dhcp imsg_dhcp; + struct iface *iface; + ssize_t len; + + iface = (struct iface *)arg; + memset(&imsg_dhcp, 0, sizeof(imsg_dhcp)); + + if ((len = read(fd, imsg_dhcp.packet, 1500)) == -1) { + log_warn("%s: read", __func__); + return; + } + + if (len == 0) + fatal("%s len == 0", __func__); + + imsg_dhcp.if_index = iface->ifinfo.if_index; + imsg_dhcp.len = len; + frontend_imsg_compose_engine(IMSG_DHCP, 0, 0, &imsg_dhcp, + sizeof(imsg_dhcp)); +} + +void +iface_data_from_imsg(struct iface* iface, struct imsg_req_dhcp *imsg) +{ + memcpy(iface->xid, imsg->xid, sizeof(iface->xid)); + iface->elapsed_time = imsg->elapsed_time; + iface->serverid_len = imsg->serverid_len; + memcpy(iface->serverid, imsg->serverid, SERVERID_SIZE); + memcpy(iface->pds, imsg->pds, sizeof(iface->pds)); +} + +ssize_t +build_packet(uint8_t message_type, struct iface *iface, char *if_name) +{ + struct iface_conf *iface_conf; + struct iface_ia_conf *ia_conf; + struct dhcp_hdr hdr; + struct dhcp_option_hdr opt_hdr; + struct dhcp_iapd iapd; + struct dhcp_iaprefix iaprefix; + struct dhcp_vendor_class vendor_class; + ssize_t len; + uint16_t request_option_code, elapsed_time; + uint8_t *p; + + switch (message_type) { + case DHCPSOLICIT: + case DHCPREQUEST: + case DHCPRENEW: + case DHCPREBIND: + break; + default: + fatalx("%s: %s not implemented", __func__, + dhcp_message_type2str(message_type)); + } + + iface_conf = find_iface_conf(&frontend_conf->iface_list, if_name); + + memset(dhcp_packet, 0, sizeof(dhcp_packet)); + + p = dhcp_packet; + hdr.msg_type = message_type; + memcpy(hdr.xid, iface->xid, sizeof(hdr.xid)); + memcpy(p, &hdr, sizeof(struct dhcp_hdr)); + p += sizeof(struct dhcp_hdr); + + opt_hdr.code = htons(DHO_CLIENTID); + opt_hdr.len = htons(sizeof(struct dhcp_duid)); + memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr)); + p += sizeof(struct dhcp_option_hdr); + memcpy(p, &duid, sizeof(struct dhcp_duid)); + p += sizeof(struct dhcp_duid); + + switch (message_type) { + case DHCPSOLICIT: + case DHCPREBIND: + break; + case DHCPREQUEST: + case DHCPRENEW: + opt_hdr.code = htons(DHO_SERVERID); + opt_hdr.len = htons(iface->serverid_len); + memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr)); + p += sizeof(struct dhcp_option_hdr); + memcpy(p, iface->serverid, iface->serverid_len); + p += iface->serverid_len; + break; + default: + fatalx("%s: %s not implemented", __func__, + dhcp_message_type2str(message_type)); + } + SIMPLEQ_FOREACH(ia_conf, &iface_conf->iface_ia_list, entry) { + struct prefix *pd; + + opt_hdr.code = htons(DHO_IA_PD); + opt_hdr.len = htons(sizeof(struct dhcp_iapd) + + sizeof(struct dhcp_option_hdr) + + sizeof(struct dhcp_iaprefix)); + memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr)); + p += sizeof(struct dhcp_option_hdr); + iapd.iaid = htonl(ia_conf->id); + iapd.t1 = 0; + iapd.t2 = 0; + memcpy(p, &iapd, sizeof(struct dhcp_iapd)); + p += sizeof(struct dhcp_iapd); + + opt_hdr.code = htons(DHO_IA_PREFIX); + opt_hdr.len = htons(sizeof(struct dhcp_iaprefix)); + memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr)); + p += sizeof(struct dhcp_option_hdr); + + memset(&iaprefix, 0, sizeof(struct dhcp_iaprefix)); + + switch (message_type) { + case DHCPSOLICIT: + iaprefix.prefix_len = ia_conf->prefix_len; + break; + case DHCPREQUEST: + case DHCPRENEW: + case DHCPREBIND: + pd = &iface->pds[ia_conf->id - 1]; + iaprefix.prefix_len = pd->prefix_len; + memcpy(&iaprefix.prefix, &pd->prefix, + sizeof(struct in6_addr)); + break; + default: + fatalx("%s: %s not implemented", __func__, + dhcp_message_type2str(message_type)); + } + memcpy(p, &iaprefix, sizeof(struct dhcp_iaprefix)); + p += sizeof(struct dhcp_iaprefix); + } + + opt_hdr.code = htons(DHO_ORO); + opt_hdr.len = htons(2 * 2); + memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr)); + p += sizeof(struct dhcp_option_hdr); + request_option_code = htons(DHO_SOL_MAX_RT); + memcpy(p, &request_option_code, sizeof(uint16_t)); + p += sizeof(uint16_t); + request_option_code = htons(DHO_INF_MAX_RT); + memcpy(p, &request_option_code, sizeof(uint16_t)); + p += sizeof(uint16_t); + + opt_hdr.code = htons(DHO_ELAPSED_TIME); + opt_hdr.len = htons(2); + memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr)); + p += sizeof(struct dhcp_option_hdr); + elapsed_time = htons(iface->elapsed_time); + memcpy(p, &elapsed_time, sizeof(uint16_t)); + p += sizeof(uint16_t); + + if (message_type == DHCPSOLICIT && frontend_conf->rapid_commit) { + opt_hdr.code = htons(DHO_RAPID_COMMIT); + opt_hdr.len = htons(0); + memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr)); + p += sizeof(struct dhcp_option_hdr); + } + + opt_hdr.code = htons(DHO_VENDOR_CLASS); + opt_hdr.len = htons(sizeof(struct dhcp_vendor_class) + + vendor_class_len); + memcpy(p, &opt_hdr, sizeof(struct dhcp_option_hdr)); + p += sizeof(struct dhcp_option_hdr); + vendor_class.enterprise_number = htonl(OPENBSD_ENTERPRISENO); + vendor_class.vendor_class_len = htons(vendor_class_len); + memcpy(p, &vendor_class, sizeof(struct dhcp_vendor_class)); + p += sizeof(struct dhcp_vendor_class); + /* Not a C-string, leave out \0 */ + memcpy(p, vendor_class_data, vendor_class_len); + p += vendor_class_len; + + len = p - dhcp_packet; + return (len); +} + +void +send_packet(uint8_t message_type, struct iface *iface) +{ + ssize_t pkt_len; + char ifnamebuf[IF_NAMESIZE], *if_name; + + if (!event_initialized(&iface->udpev)) { + iface->send_solicit = 1; + return; + } + + iface->send_solicit = 0; + + if ((if_name = if_indextoname(iface->ifinfo.if_index, ifnamebuf)) + == NULL) + return; /* iface went away, nothing to do */ + + log_debug("%s on %s", dhcp_message_type2str(message_type), if_name); + + pkt_len = build_packet(message_type, iface, if_name); + + dst.sin6_scope_id = iface->ifinfo.if_index; + + if (sendto(EVENT_FD(&iface->udpev), dhcp_packet, pkt_len, 0, + (struct sockaddr *)&dst, sizeof(dst)) == -1) + log_warn("sendto"); +} + +struct iface* +get_iface_by_id(uint32_t if_index) +{ + struct iface *iface; + + LIST_FOREACH (iface, &interfaces, entries) { + if (iface->ifinfo.if_index == if_index) + return (iface); + } + + return (NULL); +} + +struct iface* +get_iface_by_name(const char *if_name) +{ + uint32_t ifidx = if_nametoindex(if_name); + + if (ifidx == 0) + return (NULL); + return get_iface_by_id(ifidx); +} + +void +remove_iface(uint32_t if_index) +{ + struct iface *iface; + + iface = get_iface_by_id(if_index); + + if (iface == NULL) + return; + + LIST_REMOVE(iface, entries); + if (event_initialized(&iface->udpev)) { + event_del(&iface->udpev); + close(EVENT_FD(&iface->udpev)); + } + free(iface); +} + +void +set_udpsock(int udpsock, uint32_t if_index) +{ + struct iface *iface; + + iface = get_iface_by_id(if_index); + + if (iface == NULL) { + /* + * The interface disappeared while we were waiting for the + * parent process to open the udp socket. + */ + close(udpsock); + } else if (event_initialized(&iface->udpev)) { + /* + * XXX + * The autoconf flag is flapping and we have multiple udp + * sockets in flight. We don't need this one because we already + * got one. + */ + close(udpsock); + } else { + event_set(&iface->udpev, udpsock, EV_READ | + EV_PERSIST, udp_receive, iface); + event_add(&iface->udpev, NULL); + if (iface->send_solicit) + send_packet(DHCPSOLICIT, iface); + } +} + +struct iface_conf* +find_iface_conf(struct iface_conf_head *head, char *if_name) +{ + struct iface_conf *iface_conf; + + if (if_name == NULL) + return (NULL); + + SIMPLEQ_FOREACH(iface_conf, head, entry) { + if (strcmp(iface_conf->name, if_name) == 0) + return iface_conf; + } + return (NULL); +} + +int* +changed_ifaces(struct dhcp6leased_conf *oconf, struct dhcp6leased_conf *nconf) +{ + struct iface_conf *iface_conf, *oiface_conf; + int *ret, if_index, count = 0, i = 0; + + /* + * Worst case: All old interfaces replaced with new interfaces. + * This should still be a small number + */ + SIMPLEQ_FOREACH(iface_conf, &oconf->iface_list, entry) + count++; + SIMPLEQ_FOREACH(iface_conf, &nconf->iface_list, entry) + count++; + + ret = calloc(count + 1, sizeof(int)); + + SIMPLEQ_FOREACH(iface_conf, &nconf->iface_list, entry) { + if ((if_index = if_nametoindex(iface_conf->name)) == 0) + continue; + oiface_conf = find_iface_conf(&oconf->iface_list, + iface_conf->name); + if (oiface_conf == NULL) { + /* new interface added to config */ + ret[i++] = if_index; + } else if (iface_conf_cmp(iface_conf, oiface_conf) != 0) { + /* interface conf changed */ + ret[i++] = if_index; + } + } + SIMPLEQ_FOREACH(oiface_conf, &oconf->iface_list, entry) { + if ((if_index = if_nametoindex(oiface_conf->name)) == 0) + continue; + if (find_iface_conf(&nconf->iface_list, oiface_conf->name) == + NULL) { + /* interface removed from config */ + ret[i++] = if_index; + } + } + return ret; +} + +int +iface_conf_cmp(struct iface_conf *a, struct iface_conf *b) +{ + return 0; +} diff --git a/sbin/dhcp6leased/frontend.h b/sbin/dhcp6leased/frontend.h new file mode 100644 index 000000000..7694907cd --- /dev/null +++ b/sbin/dhcp6leased/frontend.h @@ -0,0 +1,24 @@ +/* $OpenBSD: frontend.h,v 1.1 2024/06/02 12:28:05 florian Exp $ */ + +/* + * Copyright (c) 2004, 2005 Esben Norby + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +void frontend(int, int); +void frontend_dispatch_main(int, short, void *); +void frontend_dispatch_engine(int, short, void *); +int frontend_imsg_compose_main(int, pid_t, void *, uint16_t); +int frontend_imsg_compose_engine(int, uint32_t, pid_t, void *, + uint16_t); diff --git a/sbin/dhcp6leased/log.c b/sbin/dhcp6leased/log.c new file mode 100644 index 000000000..8cbf4162f --- /dev/null +++ b/sbin/dhcp6leased/log.c @@ -0,0 +1,199 @@ +/* $OpenBSD: log.c,v 1.1 2024/06/02 12:28:05 florian Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" + +static int debug; +static int verbose; +static const char *log_procname; + +void +log_init(int n_debug, int facility) +{ + extern char *__progname; + + debug = n_debug; + verbose = n_debug; + log_procinit(__progname); + + if (!debug) + openlog(__progname, LOG_PID | LOG_NDELAY, facility); + + tzset(); +} + +void +log_procinit(const char *procname) +{ + if (procname != NULL) + log_procname = procname; +} + +void +log_setverbose(int v) +{ + verbose = v; +} + +int +log_getverbose(void) +{ + return (verbose); +} + +void +logit(int pri, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vlog(pri, fmt, ap); + va_end(ap); +} + +void +vlog(int pri, const char *fmt, va_list ap) +{ + char *nfmt; + int saved_errno = errno; + + if (debug) { + /* best effort in out of mem situations */ + if (asprintf(&nfmt, "%s\n", fmt) == -1) { + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + } else { + vfprintf(stderr, nfmt, ap); + free(nfmt); + } + fflush(stderr); + } else + vsyslog(pri, fmt, ap); + + errno = saved_errno; +} + +void +log_warn(const char *emsg, ...) +{ + char *nfmt; + va_list ap; + int saved_errno = errno; + + /* best effort to even work in out of memory situations */ + if (emsg == NULL) + logit(LOG_ERR, "%s", strerror(saved_errno)); + else { + va_start(ap, emsg); + + if (asprintf(&nfmt, "%s: %s", emsg, + strerror(saved_errno)) == -1) { + /* we tried it... */ + vlog(LOG_ERR, emsg, ap); + logit(LOG_ERR, "%s", strerror(saved_errno)); + } else { + vlog(LOG_ERR, nfmt, ap); + free(nfmt); + } + va_end(ap); + } + + errno = saved_errno; +} + +void +log_warnx(const char *emsg, ...) +{ + va_list ap; + + va_start(ap, emsg); + vlog(LOG_ERR, emsg, ap); + va_end(ap); +} + +void +log_info(const char *emsg, ...) +{ + va_list ap; + + va_start(ap, emsg); + vlog(LOG_INFO, emsg, ap); + va_end(ap); +} + +void +log_debug(const char *emsg, ...) +{ + va_list ap; + + if (verbose) { + va_start(ap, emsg); + vlog(LOG_DEBUG, emsg, ap); + va_end(ap); + } +} + +static void +vfatalc(int code, const char *emsg, va_list ap) +{ + static char s[BUFSIZ]; + const char *sep; + + if (emsg != NULL) { + (void)vsnprintf(s, sizeof(s), emsg, ap); + sep = ": "; + } else { + s[0] = '\0'; + sep = ""; + } + if (code) + logit(LOG_CRIT, "fatal in %s: %s%s%s", + log_procname, s, sep, strerror(code)); + else + logit(LOG_CRIT, "fatal in %s%s%s", log_procname, sep, s); +} + +void +fatal(const char *emsg, ...) +{ + va_list ap; + + va_start(ap, emsg); + vfatalc(errno, emsg, ap); + va_end(ap); + exit(1); +} + +void +fatalx(const char *emsg, ...) +{ + va_list ap; + + va_start(ap, emsg); + vfatalc(0, emsg, ap); + va_end(ap); + exit(1); +} diff --git a/sbin/dhcp6leased/log.h b/sbin/dhcp6leased/log.h new file mode 100644 index 000000000..18db5fd2d --- /dev/null +++ b/sbin/dhcp6leased/log.h @@ -0,0 +1,46 @@ +/* $OpenBSD: log.h,v 1.1 2024/06/02 12:28:05 florian Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef LOG_H +#define LOG_H + +#include +#include + +void log_init(int, int); +void log_procinit(const char *); +void log_setverbose(int); +int log_getverbose(void); +void log_warn(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_warnx(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_info(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void log_debug(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +void logit(int, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void vlog(int, const char *, va_list) + __attribute__((__format__ (printf, 2, 0))); +__dead void fatal(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +__dead void fatalx(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); + +#endif /* LOG_H */ diff --git a/sbin/dhcp6leased/parse.y b/sbin/dhcp6leased/parse.y new file mode 100644 index 000000000..0e8a50c7d --- /dev/null +++ b/sbin/dhcp6leased/parse.y @@ -0,0 +1,922 @@ +/* $OpenBSD: parse.y,v 1.6 2024/06/03 15:53:26 deraadt Exp $ */ + +/* + * Copyright (c) 2018, 2024 Florian Obser + * Copyright (c) 2004, 2005 Esben Norby + * Copyright (c) 2004 Ryan McBride + * Copyright (c) 2002, 2003, 2004 Henning Brauer + * Copyright (c) 2001 Markus Friedl. All rights reserved. + * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. + * Copyright (c) 2001 Theo de Raadt. All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +%{ +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "dhcp6leased.h" +#include "frontend.h" + +TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); +static struct file { + TAILQ_ENTRY(file) entry; + FILE *stream; + char *name; + size_t ungetpos; + size_t ungetsize; + u_char *ungetbuf; + int eof_reached; + int lineno; + int errors; +} *file, *topfile; +struct file *pushfile(const char *, int); +int popfile(void); +int check_file_secrecy(int, const char *); +int yyparse(void); +int yylex(void); +int yyerror(const char *, ...) + __attribute__((__format__ (printf, 1, 2))) + __attribute__((__nonnull__ (1))); +int kw_cmp(const void *, const void *); +int lookup(char *); +int igetc(void); +int lgetc(int); +void lungetc(int); +int findeol(void); + +TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); +struct sym { + TAILQ_ENTRY(sym) entry; + int used; + int persist; + char *nam; + char *val; +}; + +int symset(const char *, const char *, int); +char *symget(const char *); + +static struct dhcp6leased_conf *conf; +static int errors; + +static struct iface_conf *iface_conf; +static struct iface_ia_conf *iface_ia_conf; + +struct iface_conf *conf_get_iface(char *); +struct iface_pd_conf *conf_get_pd_iface(char *, int); +void addressing_plan(struct iface_ia_conf *); +int fls64(uint64_t); + +typedef struct { + union { + int64_t number; + char *string; + } v; + int lineno; +} YYSTYPE; + +%} + +%token ERROR DELEGATION FOR ON PREFIX REQUEST RAPID COMMIT + +%token STRING +%token NUMBER +%type string + +%% + +grammar : /* empty */ + | grammar '\n' + | grammar varset '\n' + | grammar conf_main '\n' + | grammar ia_pd '\n' + | grammar error '\n' { file->errors++; } + ; + +string : string STRING { + if (asprintf(&$$, "%s %s", $1, $2) == -1) { + free($1); + free($2); + yyerror("string: asprintf"); + YYERROR; + } + free($1); + free($2); + } + | STRING + ; + +varset : STRING '=' string { + char *s = $1; + if (log_getverbose() == 1) + printf("%s = \"%s\"\n", $1, $3); + while (*s++) { + if (isspace((unsigned char)*s)) { + yyerror("macro name cannot contain " + "whitespace"); + free($1); + free($3); + YYERROR; + } + } + if (symset($1, $3, 0) == -1) + fatal("cannot store variable"); + free($1); + free($3); + } + ; + +optnl : '\n' optnl /* zero or more newlines */ + | /*empty*/ + ; + +nl : '\n' optnl /* one or more newlines */ + ; +conf_main : REQUEST RAPID COMMIT { + conf->rapid_commit = 1; + } + ; + +ia_pd : REQUEST PREFIX DELEGATION ON STRING FOR { + iface_conf = conf_get_iface($5); + iface_ia_conf = calloc(1, sizeof(*iface_ia_conf)); + if (iface_ia_conf == NULL) + err(1, "%s: calloc", __func__); + iface_ia_conf->id = ++iface_conf->ia_count; + if (iface_ia_conf->id > MAX_IA) { + yyerror("Too many prefix delegation requests"); + YYERROR; + } + SIMPLEQ_INIT(&iface_ia_conf->iface_pd_list); + SIMPLEQ_INSERT_TAIL(&iface_conf->iface_ia_list, + iface_ia_conf, entry); + } '{' iface_block '}' { + iface_conf = NULL; + iface_ia_conf = NULL; + } + ; + +iface_block : optnl ifaceopts_l + | optnl + ; + +ifaceopts_l : ifaceopts_l ifaceoptsl nl + | ifaceoptsl optnl + ; + +ifaceoptsl : STRING { + struct iface_pd_conf *iface_pd_conf; + int prefixlen; + char *p; + const char *errstr; + + p = strchr($1, '/'); + if (p != NULL) { + *p++ = '\0'; + prefixlen = strtonum(p, 0, 128, &errstr); + if (errstr != NULL) { + yyerror("error parsing interface " + "\"%s/%s\"", $1, p); + free($1); + YYERROR; + } + } else + prefixlen = 64; + if ((iface_pd_conf = conf_get_pd_iface($1, prefixlen)) + == NULL) { + yyerror("duplicate interface %s", $1); + free($1); + YYERROR; + } + } + ; +%% + +struct keywords { + const char *k_name; + int k_val; +}; + +int +yyerror(const char *fmt, ...) +{ + va_list ap; + char *msg; + + file->errors++; + va_start(ap, fmt); + if (vasprintf(&msg, fmt, ap) == -1) + fatalx("yyerror vasprintf"); + va_end(ap); + logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg); + free(msg); + return (0); +} + +int +kw_cmp(const void *k, const void *e) +{ + return (strcmp(k, ((const struct keywords *)e)->k_name)); +} + +int +lookup(char *s) +{ + /* This has to be sorted always. */ + static const struct keywords keywords[] = { + {"commit", COMMIT}, + {"delegation", DELEGATION}, + {"for", FOR}, + {"on", ON}, + {"prefix", PREFIX}, + {"rapid", RAPID}, + {"request", REQUEST}, + }; + const struct keywords *p; + + p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), + sizeof(keywords[0]), kw_cmp); + + if (p) + return (p->k_val); + else + return (STRING); +} + +#define START_EXPAND 1 +#define DONE_EXPAND 2 + +static int expanding; + +int +igetc(void) +{ + int c; + + while (1) { + if (file->ungetpos > 0) + c = file->ungetbuf[--file->ungetpos]; + else + c = getc(file->stream); + + if (c == START_EXPAND) + expanding = 1; + else if (c == DONE_EXPAND) + expanding = 0; + else + break; + } + return (c); +} + +int +lgetc(int quotec) +{ + int c, next; + + if (quotec) { + if ((c = igetc()) == EOF) { + yyerror("reached end of file while parsing " + "quoted string"); + if (file == topfile || popfile() == EOF) + return (EOF); + return (quotec); + } + return (c); + } + + while ((c = igetc()) == '\\') { + next = igetc(); + if (next != '\n') { + c = next; + break; + } + yylval.lineno = file->lineno; + file->lineno++; + } + + if (c == EOF) { + /* + * Fake EOL when hit EOF for the first time. This gets line + * count right if last line in included file is syntactically + * invalid and has no newline. + */ + if (file->eof_reached == 0) { + file->eof_reached = 1; + return ('\n'); + } + while (c == EOF) { + if (file == topfile || popfile() == EOF) + return (EOF); + c = igetc(); + } + } + return (c); +} + +void +lungetc(int c) +{ + if (c == EOF) + return; + + if (file->ungetpos >= file->ungetsize) { + void *p = reallocarray(file->ungetbuf, file->ungetsize, 2); + if (p == NULL) + err(1, "lungetc"); + file->ungetbuf = p; + file->ungetsize *= 2; + } + file->ungetbuf[file->ungetpos++] = c; +} + +int +findeol(void) +{ + int c; + + /* Skip to either EOF or the first real EOL. */ + while (1) { + c = lgetc(0); + if (c == '\n') { + file->lineno++; + break; + } + if (c == EOF) + break; + } + return (ERROR); +} + +int +yylex(void) +{ + char buf[8096]; + char *p, *val; + int quotec, next, c; + int token; + +top: + p = buf; + while ((c = lgetc(0)) == ' ' || c == '\t') + ; /* nothing */ + + yylval.lineno = file->lineno; + if (c == '#') + while ((c = lgetc(0)) != '\n' && c != EOF) + ; /* nothing */ + if (c == '$' && !expanding) { + while (1) { + if ((c = lgetc(0)) == EOF) + return (0); + + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + if (isalnum(c) || c == '_') { + *p++ = c; + continue; + } + *p = '\0'; + lungetc(c); + break; + } + val = symget(buf); + if (val == NULL) { + yyerror("macro '%s' not defined", buf); + return (findeol()); + } + p = val + strlen(val) - 1; + lungetc(DONE_EXPAND); + while (p >= val) { + lungetc((unsigned char)*p); + p--; + } + lungetc(START_EXPAND); + goto top; + } + + switch (c) { + case '\'': + case '"': + quotec = c; + while (1) { + if ((c = lgetc(quotec)) == EOF) + return (0); + if (c == '\n') { + file->lineno++; + continue; + } else if (c == '\\') { + if ((next = lgetc(quotec)) == EOF) + return (0); + if (next == quotec || next == ' ' || + next == '\t') + c = next; + else if (next == '\n') { + file->lineno++; + continue; + } else + lungetc(next); + } else if (c == quotec) { + *p = '\0'; + break; + } else if (c == '\0') { + yyerror("syntax error"); + return (findeol()); + } + if (p + 1 >= buf + sizeof(buf) - 1) { + yyerror("string too long"); + return (findeol()); + } + *p++ = c; + } + yylval.v.string = strdup(buf); + if (yylval.v.string == NULL) + err(1, "yylex: strdup"); + return (STRING); + } + +#define allowed_to_end_number(x) \ + (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') + + if (c == '-' || isdigit(c)) { + do { + *p++ = c; + if ((size_t)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(0)) != EOF && isdigit(c)); + lungetc(c); + if (p == buf + 1 && buf[0] == '-') + goto nodigits; + if (c == EOF || allowed_to_end_number(c)) { + const char *errstr = NULL; + + *p = '\0'; + yylval.v.number = strtonum(buf, LLONG_MIN, + LLONG_MAX, &errstr); + if (errstr) { + yyerror("\"%s\" invalid number: %s", + buf, errstr); + return (findeol()); + } + return (NUMBER); + } else { +nodigits: + while (p > buf + 1) + lungetc((unsigned char)*--p); + c = (unsigned char)*--p; + if (c == '-') + return (c); + } + } + +#define allowed_in_string(x) \ + (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ + x != '{' && x != '}' && \ + x != '!' && x != '=' && x != '#' && \ + x != ',')) + + if (isalnum(c) || c == ':' || c == '_') { + do { + *p++ = c; + if ((size_t)(p-buf) >= sizeof(buf)) { + yyerror("string too long"); + return (findeol()); + } + } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); + lungetc(c); + *p = '\0'; + if ((token = lookup(buf)) == STRING) + if ((yylval.v.string = strdup(buf)) == NULL) + err(1, "yylex: strdup"); + return (token); + } + if (c == '\n') { + yylval.lineno = file->lineno; + file->lineno++; + } + if (c == EOF) + return (0); + return (c); +} + +int +check_file_secrecy(int fd, const char *fname) +{ + struct stat st; + + if (fstat(fd, &st)) { + log_warn("cannot stat %s", fname); + return (-1); + } + if (st.st_uid != 0 && st.st_uid != getuid()) { + log_warnx("%s: owner not root or current user", fname); + return (-1); + } + if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) { + log_warnx("%s: group writable or world read/writable", fname); + return (-1); + } + return (0); +} + +struct file * +pushfile(const char *name, int secret) +{ + struct file *nfile; + + if ((nfile = calloc(1, sizeof(struct file))) == NULL) { + log_warn("calloc"); + return (NULL); + } + if ((nfile->name = strdup(name)) == NULL) { + log_warn("strdup"); + free(nfile); + return (NULL); + } + if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { + free(nfile->name); + free(nfile); + return (NULL); + } else if (secret && + check_file_secrecy(fileno(nfile->stream), nfile->name)) { + fclose(nfile->stream); + free(nfile->name); + free(nfile); + return (NULL); + } + nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0; + nfile->ungetsize = 16; + nfile->ungetbuf = malloc(nfile->ungetsize); + if (nfile->ungetbuf == NULL) { + log_warn("malloc"); + fclose(nfile->stream); + free(nfile->name); + free(nfile); + return (NULL); + } + TAILQ_INSERT_TAIL(&files, nfile, entry); + return (nfile); +} + +int +popfile(void) +{ + struct file *prev; + + if ((prev = TAILQ_PREV(file, files, entry)) != NULL) + prev->errors += file->errors; + + TAILQ_REMOVE(&files, file, entry); + fclose(file->stream); + free(file->name); + free(file->ungetbuf); + free(file); + file = prev; + return (file ? 0 : EOF); +} + +struct dhcp6leased_conf * +parse_config(const char *filename) +{ + struct sym *sym, *next; + struct iface_conf *iface; + struct iface_ia_conf *ia_conf; + + conf = config_new_empty(); + + file = pushfile(filename, 0); + if (file == NULL) { + free(conf); + return (NULL); + } + topfile = file; + + yyparse(); + errors = file->errors; + popfile(); + + /* Free macros and check which have not been used. */ + TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) { + if ((log_getverbose() == 2) && !sym->used) + fprintf(stderr, "warning: macro '%s' not used\n", + sym->nam); + if (!sym->persist) { + free(sym->nam); + free(sym->val); + TAILQ_REMOVE(&symhead, sym, entry); + free(sym); + } + } + + if (errors) { + config_clear(conf); + return (NULL); + } + + SIMPLEQ_FOREACH(iface, &conf->iface_list, entry) { + SIMPLEQ_FOREACH(ia_conf, &iface->iface_ia_list, entry) { + addressing_plan(ia_conf); + } + } + return (conf); +} + +int +symset(const char *nam, const char *val, int persist) +{ + struct sym *sym; + + TAILQ_FOREACH(sym, &symhead, entry) { + if (strcmp(nam, sym->nam) == 0) + break; + } + + if (sym != NULL) { + if (sym->persist == 1) + return (0); + else { + free(sym->nam); + free(sym->val); + TAILQ_REMOVE(&symhead, sym, entry); + free(sym); + } + } + if ((sym = calloc(1, sizeof(*sym))) == NULL) + return (-1); + + sym->nam = strdup(nam); + if (sym->nam == NULL) { + free(sym); + return (-1); + } + sym->val = strdup(val); + if (sym->val == NULL) { + free(sym->nam); + free(sym); + return (-1); + } + sym->used = 0; + sym->persist = persist; + TAILQ_INSERT_TAIL(&symhead, sym, entry); + return (0); +} + +int +cmdline_symset(char *s) +{ + char *sym, *val; + int ret; + + if ((val = strrchr(s, '=')) == NULL) + return (-1); + sym = strndup(s, val - s); + if (sym == NULL) + errx(1, "%s: strndup", __func__); + ret = symset(sym, val + 1, 1); + free(sym); + + return (ret); +} + +char * +symget(const char *nam) +{ + struct sym *sym; + + TAILQ_FOREACH(sym, &symhead, entry) { + if (strcmp(nam, sym->nam) == 0) { + sym->used = 1; + return (sym->val); + } + } + return (NULL); +} + +struct iface_conf * +conf_get_iface(char *name) +{ + struct iface_conf *iface; + size_t n; + + SIMPLEQ_FOREACH(iface, &conf->iface_list, entry) { + if (strcmp(name, iface->name) == 0) + return (iface); + } + + iface = calloc(1, sizeof(*iface)); + if (iface == NULL) + errx(1, "%s: calloc", __func__); + n = strlcpy(iface->name, name, sizeof(iface->name)); + if (n >= sizeof(iface->name)) + errx(1, "%s: name too long", __func__); + SIMPLEQ_INIT(&iface->iface_ia_list); + + SIMPLEQ_INSERT_TAIL(&conf->iface_list, iface, entry); + + return (iface); +} + +struct iface_pd_conf * +conf_get_pd_iface(char *name, int prefixlen) +{ + struct iface_ia_conf *iface_ia; + struct iface_pd_conf *iface_pd; + size_t n; + + if (strcmp(name, "reserve") != 0) { + SIMPLEQ_FOREACH(iface_ia, &iface_conf->iface_ia_list, + entry) { + SIMPLEQ_FOREACH(iface_pd, &iface_ia->iface_pd_list, + entry) { + if (strcmp(name, iface_pd->name) == 0) + return NULL; + } + } + } + + iface_pd = calloc(1, sizeof(*iface_pd)); + if (iface_pd == NULL) + err(1, "%s: calloc", __func__); + n = strlcpy(iface_pd->name, name, sizeof(iface_pd->name)); + if (n >= sizeof(iface_pd->name)) + errx(1, "%s: name too long", __func__); + iface_pd->prefix_len = prefixlen; + + SIMPLEQ_INSERT_TAIL(&iface_ia_conf->iface_pd_list, iface_pd, entry); + + return (iface_pd); +} + +static inline uint64_t +get_shift(int plen) +{ + if (plen > 64) + plen -= 64; + + return 1ULL << (64 - plen); +} + +void +addressing_plan(struct iface_ia_conf *ia_conf) +{ + struct iface_pd_conf *pd_conf; + uint64_t *p, lo_counter, hi_counter, lo_shift, hi_shift; + int prev_plen = -1; + + lo_counter = hi_counter = 0; + + SIMPLEQ_FOREACH(pd_conf, &ia_conf->iface_pd_list, entry) { + /* not the first prefix */ + if (ia_conf->prefix_len != 0) { + lo_shift = hi_shift = 0; + if (prev_plen > pd_conf->prefix_len) { + if (pd_conf->prefix_len > 64) + lo_shift = + get_shift(pd_conf->prefix_len); + else + hi_shift = + get_shift(pd_conf->prefix_len); + } else { + if (prev_plen > 64) + lo_shift = get_shift(prev_plen); + else + hi_shift = get_shift(prev_plen); + } + + if (lo_shift != 0) { + if (lo_counter > UINT64_MAX - lo_shift) { + /* overflow */ + hi_counter++; + lo_counter = 0; + } else { + lo_counter += lo_shift; + /* remove all lower bits */ + lo_counter &= ~(lo_shift - 1); + } + } else { + hi_counter += hi_shift; + /* remove all lower bits */ + hi_counter &= ~(hi_shift - 1); + lo_counter = 0; + } + + } else + ia_conf->prefix_len = pd_conf->prefix_len; + + p = (uint64_t *)&pd_conf->prefix_mask.s6_addr; + *p |= htobe64(hi_counter); + + p = (uint64_t *)&pd_conf->prefix_mask.s6_addr[8]; + *p |= htobe64(lo_counter); + + prev_plen = pd_conf->prefix_len; + } + + if (hi_counter != 0) + ia_conf->prefix_len = 64 - fls64(hi_counter); + else if (lo_counter != 0) + ia_conf->prefix_len = 128 - fls64(lo_counter); +} + +/* from NetBSD's sys/sys/bitops.h */ +/*- + * Copyright (c) 2007, 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas and Joerg Sonnenberger. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +int +fls64(uint64_t _n) +{ + int _v; + + if (!_n) + return 0; + + _v = 64; + if ((_n & 0xFFFFFFFF00000000ULL) == 0) { + _n <<= 32; + _v -= 32; + } + if ((_n & 0xFFFF000000000000ULL) == 0) { + _n <<= 16; + _v -= 16; + } + if ((_n & 0xFF00000000000000ULL) == 0) { + _n <<= 8; + _v -= 8; + } + if ((_n & 0xF000000000000000ULL) == 0) { + _n <<= 4; + _v -= 4; + } + if ((_n & 0xC000000000000000ULL) == 0) { + _n <<= 2; + _v -= 2; + } + if ((_n & 0x8000000000000000ULL) == 0) { + //_n <<= 1; + _v -= 1; + } + return _v; +} diff --git a/sbin/dhcp6leased/printconf.c b/sbin/dhcp6leased/printconf.c new file mode 100644 index 000000000..fe8bb623a --- /dev/null +++ b/sbin/dhcp6leased/printconf.c @@ -0,0 +1,108 @@ +/* $OpenBSD: printconf.c,v 1.3 2024/06/03 11:08:31 florian Exp $ */ + +/* + * Copyright (c) 2024 Florian Obser + * Copyright (c) 2004, 2005 Esben Norby + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#include "dhcp6leased.h" +#include "log.h" + +void print_iface_conf(struct iface_conf *, int); +void print_iface_ia_conf(struct iface_ia_conf *, int); +void print_iface_pd_conf(char *, struct iface_pd_conf *, int); + +void +print_iface_pd_conf(char *indent, struct iface_pd_conf *pd_conf, int verbose) +{ + if (verbose > 1) { + struct in6_addr ia6; + int i; + char ntopbuf[INET6_ADDRSTRLEN]; + + memset(&ia6, 0, sizeof(ia6)); + inet_pton(AF_INET6, "2001:db8::", &ia6); + + for (i = 0; i < 16; i++) + ia6.s6_addr[i] |= pd_conf->prefix_mask.s6_addr[i]; + + inet_ntop(AF_INET6, &ia6, ntopbuf, INET6_ADDRSTRLEN); + printf("%s%s/%d\t# %s/%d\n", indent, pd_conf->name, + pd_conf->prefix_len, ntopbuf, pd_conf->prefix_len); + } else + printf("%s%s/%d\n", indent, pd_conf->name, pd_conf->prefix_len); +} + +void +print_iface_ia_conf(struct iface_ia_conf *ia_conf, int verbose) +{ + struct iface_pd_conf *pd_conf; + + SIMPLEQ_FOREACH(pd_conf, &ia_conf->iface_pd_list, entry) + print_iface_pd_conf("\t", pd_conf, + ia_conf->prefix_len >= 32 ? verbose : 1); +} + +void +print_iface_conf(struct iface_conf *iface, int verbose) +{ + struct iface_ia_conf *ia_conf; + int first = 1; + + SIMPLEQ_FOREACH(ia_conf, &iface->iface_ia_list, entry) { + if (!first) + printf("\n"); + first = 0; + + if (verbose > 1) { + printf("request prefix delegation on %s for {" + "\t# prefix length = %d\n", iface->name, + ia_conf->prefix_len); + } else { + printf("request prefix delegation on %s for {\n", + iface->name); + } + print_iface_ia_conf(ia_conf, verbose); + printf("}\n"); + } +} +void +print_config(struct dhcp6leased_conf *conf, int verbose) +{ + struct iface_conf *iface; + + if (conf->rapid_commit) + printf("request rapid commit\n\n"); + + SIMPLEQ_FOREACH(iface, &conf->iface_list, entry) + print_iface_conf(iface, verbose); +} diff --git a/sbin/dhcpleased/engine.c b/sbin/dhcpleased/engine.c index 84d02cc12..99433ed05 100644 --- a/sbin/dhcpleased/engine.c +++ b/sbin/dhcpleased/engine.c @@ -1,4 +1,4 @@ -/* $OpenBSD: engine.c,v 1.43 2024/02/13 12:53:05 florian Exp $ */ +/* $OpenBSD: engine.c,v 1.44 2024/06/02 12:39:26 florian Exp $ */ /* * Copyright (c) 2017, 2021 Florian Obser @@ -1544,7 +1544,7 @@ iface_timeout(int fd, short events, void *arg) timespecsub(&now, &iface->request_time, &res); log_debug("%s: res.tv_sec: %lld, rebinding_time: %u", __func__, res.tv_sec, iface->rebinding_time); - if (res.tv_sec > iface->rebinding_time) + if (res.tv_sec >= iface->rebinding_time) state_transition(iface, IF_REBINDING); else state_transition(iface, IF_RENEWING); diff --git a/share/man/man1/clang-local.1 b/share/man/man1/clang-local.1 index 9e02e6972..6cc7e0d6b 100644 --- a/share/man/man1/clang-local.1 +++ b/share/man/man1/clang-local.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: clang-local.1,v 1.23 2022/02/18 00:39:18 jca Exp $ +.\" $OpenBSD: clang-local.1,v 1.24 2024/06/02 15:40:43 deraadt Exp $ .\" .\" Copyright (c) 2016 Pascal Stumpf .\" @@ -15,7 +15,7 @@ .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" -.Dd $Mdocdate: February 18 2022 $ +.Dd $Mdocdate: June 2 2024 $ .Dt CLANG-LOCAL 1 .Os .Sh NAME @@ -119,6 +119,13 @@ This can be disabled with the option. .It .Nm clang +includes a security pass that can clear the return address on the +stack upon return from calling a function, on i386 and amd64. +This can be enabled with the +.Fl fret-clean +option. +.It +.Nm clang includes the retguard security feature on amd64, arm64, mips64, powerpc and powerpc64. This feature can be disabled with the diff --git a/share/man/man4/qwx.4 b/share/man/man4/qwx.4 index db12ee130..96b15a308 100644 --- a/share/man/man4/qwx.4 +++ b/share/man/man4/qwx.4 @@ -1,4 +1,4 @@ -.\" $OpenBSD: qwx.4,v 1.5 2024/05/29 09:04:12 stsp Exp $ +.\" $OpenBSD: qwx.4,v 1.6 2024/06/03 14:59:22 jsg Exp $ .\" .\" Copyright (c) 2022 Martin Pieuchot .\" Copyright (c) 2024 Stefan Sperling @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: May 29 2024 $ +.Dd $Mdocdate: June 3 2024 $ .Dt QWX 4 .Os .Sh NAME @@ -96,5 +96,5 @@ capabilities offered by the hardware. .Sh BUGS Broadcast and Multicast frames are only received on networks which do not use encryption or which use WPA2 with group cipher CCMP. -This prevents ARP and IPv6 from working correcly on other types of +This prevents ARP and IPv6 from working correctly on other types of networks. diff --git a/sys/dev/acpi/acpi.c b/sys/dev/acpi/acpi.c index 71c30b59a..09c4b56d3 100644 --- a/sys/dev/acpi/acpi.c +++ b/sys/dev/acpi/acpi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: acpi.c,v 1.429 2024/05/29 12:21:33 kettenis Exp $ */ +/* $OpenBSD: acpi.c,v 1.430 2024/06/02 11:08:41 kettenis Exp $ */ /* * Copyright (c) 2005 Thorsten Lockert * Copyright (c) 2005 Jordan Hargrave @@ -611,6 +611,10 @@ acpi_getpci(struct aml_node *node, void *arg) aml_nodename(node)); /* Collect device power state information. */ + if (aml_evalinteger(sc, node, "_S0W", 0, NULL, &val) == 0) + pci->_s0w = val; + else + pci->_s0w = -1; if (aml_evalinteger(sc, node, "_S3D", 0, NULL, &val) == 0) pci->_s3d = val; else @@ -721,6 +725,10 @@ acpi_pci_min_powerstate(pci_chipset_tag_t pc, pcitag_t tag) TAILQ_FOREACH(pdev, &acpi_pcidevs, next) { if (pdev->bus == bus && pdev->dev == dev && pdev->fun == fun) { switch (acpi_softc->sc_state) { + case ACPI_STATE_S0: + defaultstate = PCI_PMCSR_STATE_D3; + state = pdev->_s0w; + break; case ACPI_STATE_S3: defaultstate = PCI_PMCSR_STATE_D3; state = MAX(pdev->_s3d, pdev->_s3w); diff --git a/sys/dev/acpi/amltypes.h b/sys/dev/acpi/amltypes.h index 974a252f7..4c72f2517 100644 --- a/sys/dev/acpi/amltypes.h +++ b/sys/dev/acpi/amltypes.h @@ -1,4 +1,4 @@ -/* $OpenBSD: amltypes.h,v 1.49 2022/09/12 17:42:31 kettenis Exp $ */ +/* $OpenBSD: amltypes.h,v 1.50 2024/06/02 11:08:41 kettenis Exp $ */ /* * Copyright (c) 2005 Jordan Hargrave * @@ -358,6 +358,7 @@ struct acpi_pci { int dev; int fun; + int _s0w; int _s3d; int _s3w; int _s4d; diff --git a/sys/dev/ic/nvme.c b/sys/dev/ic/nvme.c index 106889186..4f0f2a336 100644 --- a/sys/dev/ic/nvme.c +++ b/sys/dev/ic/nvme.c @@ -1,4 +1,4 @@ -/* $OpenBSD: nvme.c,v 1.115 2024/05/28 00:24:44 jsg Exp $ */ +/* $OpenBSD: nvme.c,v 1.116 2024/06/03 12:01:57 mglocker Exp $ */ /* * Copyright (c) 2014 David Gwynne @@ -1742,8 +1742,6 @@ nvme_hibernate_io(dev_t dev, daddr_t blkno, vaddr_t addr, size_t size, my->cq_head = 0; my->cqe_phase = NVME_CQE_PHASE; - pmap_extract(pmap_kernel(), (vaddr_t)page, &page_phys); - memset(&qsqe, 0, sizeof(qsqe)); qsqe.opcode = NVM_ADMIN_ADD_IOCQ; htolem64(&qsqe.prp1, diff --git a/sys/dev/pci/drm/drm_linux.c b/sys/dev/pci/drm/drm_linux.c index eefd2325e..a75d8e881 100644 --- a/sys/dev/pci/drm/drm_linux.c +++ b/sys/dev/pci/drm/drm_linux.c @@ -1,4 +1,4 @@ -/* $OpenBSD: drm_linux.c,v 1.112 2024/03/30 13:33:20 mpi Exp $ */ +/* $OpenBSD: drm_linux.c,v 1.113 2024/06/03 12:48:25 claudio Exp $ */ /* * Copyright (c) 2013 Jonathan Gray * Copyright (c) 2015, 2016 Mark Kettenis @@ -114,14 +114,13 @@ void __set_current_state(int state) { struct proc *p = curproc; - int s; KASSERT(state == TASK_RUNNING); - SCHED_LOCK(s); + SCHED_LOCK(); unsleep(p); p->p_stat = SONPROC; atomic_clearbits_int(&p->p_flag, P_WSLEEP); - SCHED_UNLOCK(s); + SCHED_UNLOCK(); } void @@ -159,11 +158,11 @@ schedule_timeout_uninterruptible(long timeout) int wake_up_process(struct proc *p) { - int s, rv; + int rv; - SCHED_LOCK(s); + SCHED_LOCK(); rv = wakeup_proc(p, 0); - SCHED_UNLOCK(s); + SCHED_UNLOCK(); return rv; } diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c index 029b59309..af34d278d 100644 --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_exit.c,v 1.221 2024/05/20 10:32:20 claudio Exp $ */ +/* $OpenBSD: kern_exit.c,v 1.222 2024/06/03 12:48:25 claudio Exp $ */ /* $NetBSD: kern_exit.c,v 1.39 1996/04/22 01:38:25 christos Exp $ */ /* @@ -119,7 +119,6 @@ exit1(struct proc *p, int xexit, int xsig, int flags) struct process *pr, *qr, *nqr; struct rusage *rup; struct timespec ts; - int s; atomic_setbits_int(&p->p_flag, P_WEXIT); @@ -329,9 +328,9 @@ exit1(struct proc *p, int xexit, int xsig, int flags) timespecclear(&ts); else timespecsub(&ts, &curcpu()->ci_schedstate.spc_runtime, &ts); - SCHED_LOCK(s); + SCHED_LOCK(); tuagg_locked(pr, p, &ts); - SCHED_UNLOCK(s); + SCHED_UNLOCK(); /* * clear %cpu usage during swap diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index 7d9188261..884dcd64d 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_fork.c,v 1.259 2024/05/29 18:55:45 claudio Exp $ */ +/* $OpenBSD: kern_fork.c,v 1.260 2024/06/03 12:48:25 claudio Exp $ */ /* $NetBSD: kern_fork.c,v 1.29 1996/02/09 18:59:34 christos Exp $ */ /* @@ -329,14 +329,13 @@ static inline void fork_thread_start(struct proc *p, struct proc *parent, int flags) { struct cpu_info *ci; - int s; - SCHED_LOCK(s); + SCHED_LOCK(); ci = sched_choosecpu_fork(parent, flags); TRACEPOINT(sched, fork, p->p_tid + THREAD_PID_OFFSET, p->p_p->ps_pid, CPU_INFO_UNIT(ci)); setrunqueue(ci, p, p->p_usrpri); - SCHED_UNLOCK(s); + SCHED_UNLOCK(); } int diff --git a/sys/kern/kern_pledge.c b/sys/kern/kern_pledge.c index 487df5dc2..6ee112430 100644 --- a/sys/kern/kern_pledge.c +++ b/sys/kern/kern_pledge.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_pledge.c,v 1.314 2024/05/18 05:20:22 guenther Exp $ */ +/* $OpenBSD: kern_pledge.c,v 1.316 2024/06/03 03:41:47 deraadt Exp $ */ /* * Copyright (c) 2015 Nicholas Marriott @@ -574,7 +574,7 @@ pledge_fail(struct proc *p, int error, uint64_t code) return (ENOSYS); KERNEL_LOCK(); - log(LOG_ERR, "%s[%d]: pledge \"%s\", syscall %d\n", + uprintf("%s[%d]: pledge \"%s\", syscall %d\n", p->p_p->ps_comm, p->p_p->ps_pid, codes, p->p_pledge_syscall); p->p_p->ps_acflag |= APLEDGE; @@ -1002,10 +1002,10 @@ pledge_sysctl(struct proc *p, int miblen, int *mib, void *new) snprintf(buf, sizeof(buf), "%s(%d): pledge sysctl %d:", p->p_p->ps_comm, p->p_p->ps_pid, miblen); for (i = 0; i < miblen; i++) { - char *p = buf + strlen(buf); - snprintf(p, sizeof(buf) - (p - buf), " %d", mib[i]); + char *s = buf + strlen(buf); + snprintf(s, sizeof(buf) - (s - buf), " %d", mib[i]); } - log(LOG_ERR, "%s\n", buf); + uprintf("%s\n", buf); return pledge_fail(p, EINVAL, 0); } diff --git a/sys/kern/kern_resource.c b/sys/kern/kern_resource.c index d589948a5..bfacce9ad 100644 --- a/sys/kern/kern_resource.c +++ b/sys/kern/kern_resource.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_resource.c,v 1.83 2024/05/22 09:20:22 claudio Exp $ */ +/* $OpenBSD: kern_resource.c,v 1.84 2024/06/03 12:48:25 claudio Exp $ */ /* $NetBSD: kern_resource.c,v 1.38 1996/10/23 07:19:38 matthias Exp $ */ /*- @@ -198,7 +198,6 @@ donice(struct proc *curp, struct process *chgpr, int n) { struct ucred *ucred = curp->p_ucred; struct proc *p; - int s; if (ucred->cr_uid != 0 && ucred->cr_ruid != 0 && ucred->cr_uid != chgpr->ps_ucred->cr_uid && @@ -213,11 +212,11 @@ donice(struct proc *curp, struct process *chgpr, int n) return (EACCES); chgpr->ps_nice = n; mtx_enter(&chgpr->ps_mtx); - SCHED_LOCK(s); + SCHED_LOCK(); TAILQ_FOREACH(p, &chgpr->ps_threads, p_thr_link) { setpriority(p, p->p_estcpu, n); } - SCHED_UNLOCK(s); + SCHED_UNLOCK(); mtx_leave(&chgpr->ps_mtx); return (0); } @@ -396,11 +395,9 @@ tuagg_locked(struct process *pr, struct proc *p, const struct timespec *ts) void tuagg(struct process *pr, struct proc *p) { - int s; - - SCHED_LOCK(s); + SCHED_LOCK(); tuagg_locked(pr, p, NULL); - SCHED_UNLOCK(s); + SCHED_UNLOCK(); } /* @@ -537,13 +534,12 @@ rucheck(void *arg) struct rlimit rlim; struct process *pr = arg; time_t runtime; - int s; KERNEL_ASSERT_LOCKED(); - SCHED_LOCK(s); + SCHED_LOCK(); runtime = pr->ps_tu.tu_runtime.tv_sec; - SCHED_UNLOCK(s); + SCHED_UNLOCK(); mtx_enter(&pr->ps_mtx); rlim = pr->ps_limit->pl_rlimit[RLIMIT_CPU]; diff --git a/sys/kern/kern_sched.c b/sys/kern/kern_sched.c index 290672dd2..1877d5730 100644 --- a/sys/kern/kern_sched.c +++ b/sys/kern/kern_sched.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_sched.c,v 1.95 2024/02/28 13:43:44 mpi Exp $ */ +/* $OpenBSD: kern_sched.c,v 1.96 2024/06/03 12:48:25 claudio Exp $ */ /* * Copyright (c) 2007, 2008 Artur Grabowski * @@ -137,7 +137,6 @@ sched_idle(void *v) struct schedstate_percpu *spc; struct proc *p = curproc; struct cpu_info *ci = v; - int s; KERNEL_UNLOCK(); @@ -147,14 +146,14 @@ sched_idle(void *v) * First time we enter here, we're not supposed to idle, * just go away for a while. */ - SCHED_LOCK(s); + SCHED_LOCK(); cpuset_add(&sched_idle_cpus, ci); p->p_stat = SSLEEP; p->p_cpu = ci; atomic_setbits_int(&p->p_flag, P_CPUPEG); mi_switch(); cpuset_del(&sched_idle_cpus, ci); - SCHED_UNLOCK(s); + SCHED_UNLOCK(); KASSERT(ci == curcpu()); KASSERT(curproc == spc->spc_idleproc); @@ -163,10 +162,10 @@ sched_idle(void *v) while (!cpu_is_idle(curcpu())) { struct proc *dead; - SCHED_LOCK(s); + SCHED_LOCK(); p->p_stat = SSLEEP; mi_switch(); - SCHED_UNLOCK(s); + SCHED_UNLOCK(); while ((dead = LIST_FIRST(&spc->spc_deadproc))) { LIST_REMOVE(dead, p_hash); @@ -185,10 +184,10 @@ sched_idle(void *v) if (spc->spc_schedflags & SPCF_SHOULDHALT && (spc->spc_schedflags & SPCF_HALTED) == 0) { cpuset_del(&sched_idle_cpus, ci); - SCHED_LOCK(s); + SCHED_LOCK(); atomic_setbits_int(&spc->spc_schedflags, spc->spc_whichqs ? 0 : SPCF_HALTED); - SCHED_UNLOCK(s); + SCHED_UNLOCK(); wakeup(spc); } #endif @@ -226,7 +225,6 @@ sched_toidle(void) { struct schedstate_percpu *spc = &curcpu()->ci_schedstate; struct proc *idle; - int s; #ifdef MULTIPROCESSOR /* This process no longer needs to hold the kernel lock. */ @@ -245,8 +243,7 @@ sched_toidle(void) atomic_clearbits_int(&spc->spc_schedflags, SPCF_SWITCHCLEAR); - SCHED_LOCK(s); - + SCHED_LOCK(); idle = spc->spc_idleproc; idle->p_stat = SRUN; @@ -627,14 +624,13 @@ void sched_peg_curproc(struct cpu_info *ci) { struct proc *p = curproc; - int s; - SCHED_LOCK(s); + SCHED_LOCK(); atomic_setbits_int(&p->p_flag, P_CPUPEG); setrunqueue(ci, p, p->p_usrpri); p->p_ru.ru_nvcsw++; mi_switch(); - SCHED_UNLOCK(s); + SCHED_UNLOCK(); } #ifdef MULTIPROCESSOR diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index 6b7e341ca..7089d7446 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_sig.c,v 1.329 2024/05/22 09:22:55 claudio Exp $ */ +/* $OpenBSD: kern_sig.c,v 1.330 2024/06/03 12:48:25 claudio Exp $ */ /* $NetBSD: kern_sig.c,v 1.54 1996/04/22 01:38:32 christos Exp $ */ /* @@ -843,14 +843,12 @@ trapsignal(struct proc *p, int signum, u_long trapno, int code, */ if (((pr->ps_flags & (PS_TRACED | PS_PPWAIT)) == PS_TRACED) && signum != SIGKILL && (p->p_sigmask & mask) != 0) { - int s; - single_thread_set(p, SINGLE_SUSPEND | SINGLE_NOWAIT); pr->ps_xsig = signum; - SCHED_LOCK(s); + SCHED_LOCK(); proc_stop(p, 1); - SCHED_UNLOCK(s); + SCHED_UNLOCK(); signum = pr->ps_xsig; single_thread_clear(p, 0); @@ -920,7 +918,7 @@ psignal(struct proc *p, int signum) void ptsignal(struct proc *p, int signum, enum signal_type type) { - int s, prop; + int prop; sig_t action, altaction = SIG_DFL; sigset_t mask, sigmask; int *siglist; @@ -1063,7 +1061,7 @@ ptsignal(struct proc *p, int signum, enum signal_type type) if (q != p) ptsignal(q, signum, SPROPAGATED); - SCHED_LOCK(s); + SCHED_LOCK(); switch (p->p_stat) { @@ -1252,7 +1250,7 @@ out: atomic_clearbits_int(&p->p_flag, P_CONTINUED); } - SCHED_UNLOCK(s); + SCHED_UNLOCK(); if (wakeparent) wakeup(pr->ps_pptr); } @@ -1299,7 +1297,6 @@ cursig(struct proc *p, struct sigctx *sctx) struct process *pr = p->p_p; int signum, mask, prop; sigset_t ps_siglist; - int s; KASSERT(p == curproc); @@ -1340,9 +1337,9 @@ cursig(struct proc *p, struct sigctx *sctx) single_thread_set(p, SINGLE_SUSPEND | SINGLE_NOWAIT); pr->ps_xsig = signum; - SCHED_LOCK(s); + SCHED_LOCK(); proc_stop(p, 1); - SCHED_UNLOCK(s); + SCHED_UNLOCK(); /* * re-take the signal before releasing @@ -1415,9 +1412,9 @@ cursig(struct proc *p, struct sigctx *sctx) prop & SA_TTYSTOP)) break; /* == ignore */ pr->ps_xsig = signum; - SCHED_LOCK(s); + SCHED_LOCK(); proc_stop(p, 1); - SCHED_UNLOCK(s); + SCHED_UNLOCK(); break; } else if (prop & SA_IGNORE) { /* @@ -2064,7 +2061,6 @@ int single_thread_check_locked(struct proc *p, int deep) { struct process *pr = p->p_p; - int s; MUTEX_ASSERT_LOCKED(&pr->ps_mtx); @@ -2093,10 +2089,10 @@ single_thread_check_locked(struct proc *p, int deep) /* not exiting and don't need to unwind, so suspend */ mtx_leave(&pr->ps_mtx); - SCHED_LOCK(s); + SCHED_LOCK(); p->p_stat = SSTOP; mi_switch(); - SCHED_UNLOCK(s); + SCHED_UNLOCK(); mtx_enter(&pr->ps_mtx); } while (pr->ps_single != NULL); @@ -2129,7 +2125,7 @@ single_thread_set(struct proc *p, int flags) { struct process *pr = p->p_p; struct proc *q; - int error, s, mode = flags & SINGLE_MASK; + int error, mode = flags & SINGLE_MASK; KASSERT(curproc == p); @@ -2161,7 +2157,7 @@ single_thread_set(struct proc *p, int flags) TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) { if (q == p) continue; - SCHED_LOCK(s); + SCHED_LOCK(); atomic_setbits_int(&q->p_flag, P_SUSPSINGLE); switch (q->p_stat) { case SIDL: @@ -2194,7 +2190,7 @@ single_thread_set(struct proc *p, int flags) case SRUN: break; } - SCHED_UNLOCK(s); + SCHED_UNLOCK(); } /* count ourselfs out */ @@ -2235,7 +2231,6 @@ single_thread_clear(struct proc *p, int flag) { struct process *pr = p->p_p; struct proc *q; - int s; KASSERT(pr->ps_single == p); KASSERT(curproc == p); @@ -2254,7 +2249,7 @@ single_thread_clear(struct proc *p, int flag) * then clearing that either makes it runnable or puts * it back into some sleep queue */ - SCHED_LOCK(s); + SCHED_LOCK(); if (q->p_stat == SSTOP && (q->p_flag & flag) == 0) { if (q->p_wchan == NULL) setrunnable(q); @@ -2263,7 +2258,7 @@ single_thread_clear(struct proc *p, int flag) q->p_stat = SSLEEP; } } - SCHED_UNLOCK(s); + SCHED_UNLOCK(); } mtx_leave(&pr->ps_mtx); } diff --git a/sys/kern/kern_synch.c b/sys/kern/kern_synch.c index 21ee0c6fb..16de8ad5d 100644 --- a/sys/kern/kern_synch.c +++ b/sys/kern/kern_synch.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_synch.c,v 1.204 2024/05/22 09:24:11 claudio Exp $ */ +/* $OpenBSD: kern_synch.c,v 1.205 2024/06/03 12:48:25 claudio Exp $ */ /* $NetBSD: kern_synch.c,v 1.37 1996/04/22 01:38:37 christos Exp $ */ /* @@ -332,7 +332,6 @@ void sleep_setup(const volatile void *ident, int prio, const char *wmesg) { struct proc *p = curproc; - int s; #ifdef DIAGNOSTIC if (p->p_flag & P_CANTSLEEP) @@ -346,7 +345,7 @@ sleep_setup(const volatile void *ident, int prio, const char *wmesg) if (p->p_flag & P_WEXIT) CLR(prio, PCATCH); - SCHED_LOCK(s); + SCHED_LOCK(); TRACEPOINT(sched, sleep, NULL); @@ -360,14 +359,14 @@ sleep_setup(const volatile void *ident, int prio, const char *wmesg) atomic_setbits_int(&p->p_flag, P_SINTR); p->p_stat = SSLEEP; - SCHED_UNLOCK(s); + SCHED_UNLOCK(); } int sleep_finish(int timo, int do_sleep) { struct proc *p = curproc; - int s, catch, error = 0, error1 = 0; + int catch, error = 0, error1 = 0; catch = p->p_flag & P_SINTR; @@ -392,7 +391,7 @@ sleep_finish(int timo, int do_sleep) } } - SCHED_LOCK(s); + SCHED_LOCK(); /* * If the wakeup happens while going to sleep, p->p_wchan * will be NULL. In that case unwind immediately but still @@ -419,7 +418,7 @@ sleep_finish(int timo, int do_sleep) #endif p->p_cpu->ci_schedstate.spc_curpriority = p->p_usrpri; - SCHED_UNLOCK(s); + SCHED_UNLOCK(); /* * Even though this belongs to the signal handling part of sleep, @@ -503,11 +502,10 @@ void endtsleep(void *arg) { struct proc *p = arg; - int s; - SCHED_LOCK(s); + SCHED_LOCK(); wakeup_proc(p, P_TIMEOUT); - SCHED_UNLOCK(s); + SCHED_UNLOCK(); } /* @@ -536,11 +534,10 @@ wakeup_n(const volatile void *ident, int n) struct slpque *qp, wakeq; struct proc *p; struct proc *pnext; - int s; TAILQ_INIT(&wakeq); - SCHED_LOCK(s); + SCHED_LOCK(); qp = &slpque[LOOKUP(ident)]; for (p = TAILQ_FIRST(qp); p != NULL && n != 0; p = pnext) { pnext = TAILQ_NEXT(p, p_runq); @@ -564,7 +561,7 @@ wakeup_n(const volatile void *ident, int n) if (p->p_stat == SSLEEP) setrunnable(p); } - SCHED_UNLOCK(s); + SCHED_UNLOCK(); } /* @@ -581,7 +578,6 @@ sys_sched_yield(struct proc *p, void *v, register_t *retval) { struct proc *q; uint8_t newprio; - int s; /* * If one of the threads of a multi-threaded process called @@ -594,11 +590,11 @@ sys_sched_yield(struct proc *p, void *v, register_t *retval) newprio = max(newprio, q->p_runpri); mtx_leave(&p->p_p->ps_mtx); - SCHED_LOCK(s); + SCHED_LOCK(); setrunqueue(p->p_cpu, p, newprio); p->p_ru.ru_nvcsw++; mi_switch(); - SCHED_UNLOCK(s); + SCHED_UNLOCK(); return (0); } diff --git a/sys/kern/sched_bsd.c b/sys/kern/sched_bsd.c index 1cfd32746..54ce3173e 100644 --- a/sys/kern/sched_bsd.c +++ b/sys/kern/sched_bsd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sched_bsd.c,v 1.92 2024/05/29 18:55:45 claudio Exp $ */ +/* $OpenBSD: sched_bsd.c,v 1.93 2024/06/03 12:48:25 claudio Exp $ */ /* $NetBSD: kern_synch.c,v 1.37 1996/04/22 01:38:37 christos Exp $ */ /*- @@ -230,7 +230,6 @@ schedcpu(void *unused) static struct timeout to = TIMEOUT_INITIALIZER(schedcpu, NULL); fixpt_t loadfac = loadfactor(averunnable.ldavg[0]); struct proc *p; - int s; unsigned int newcpu; LIST_FOREACH(p, &allproc, p_list) { @@ -253,7 +252,7 @@ schedcpu(void *unused) */ if (p->p_slptime > 1) continue; - SCHED_LOCK(s); + SCHED_LOCK(); /* * p_pctcpu is only for diagnostic tools such as ps. */ @@ -275,7 +274,7 @@ schedcpu(void *unused) remrunqueue(p); setrunqueue(p->p_cpu, p, p->p_usrpri); } - SCHED_UNLOCK(s); + SCHED_UNLOCK(); } wakeup(&lbolt); timeout_add_sec(&to, 1); @@ -313,13 +312,12 @@ void yield(void) { struct proc *p = curproc; - int s; - SCHED_LOCK(s); + SCHED_LOCK(); setrunqueue(p->p_cpu, p, p->p_usrpri); p->p_ru.ru_nvcsw++; mi_switch(); - SCHED_UNLOCK(s); + SCHED_UNLOCK(); } /* @@ -332,13 +330,12 @@ void preempt(void) { struct proc *p = curproc; - int s; - SCHED_LOCK(s); + SCHED_LOCK(); setrunqueue(p->p_cpu, p, p->p_usrpri); p->p_ru.ru_nivcsw++; mi_switch(); - SCHED_UNLOCK(s); + SCHED_UNLOCK(); } void @@ -349,7 +346,7 @@ mi_switch(void) struct proc *nextproc; struct process *pr = p->p_p; struct timespec ts; - int oldipl, s; + int oldipl; #ifdef MULTIPROCESSOR int hold_count; #endif @@ -427,7 +424,7 @@ mi_switch(void) /* Restore proc's IPL. */ MUTEX_OLDIPL(&sched_lock) = oldipl; - SCHED_UNLOCK(s); + SCHED_UNLOCK(); SCHED_ASSERT_UNLOCKED(); @@ -463,7 +460,7 @@ mi_switch(void) if (hold_count) __mp_acquire_count(&kernel_lock, hold_count); #endif - SCHED_LOCK(s); + SCHED_LOCK(); } /* @@ -551,15 +548,14 @@ schedclock(struct proc *p) struct cpu_info *ci = curcpu(); struct schedstate_percpu *spc = &ci->ci_schedstate; uint32_t newcpu; - int s; if (p == spc->spc_idleproc || spc->spc_spinning) return; - SCHED_LOCK(s); + SCHED_LOCK(); newcpu = ESTCPULIM(p->p_estcpu + 1); setpriority(p, newcpu, p->p_p->ps_nice); - SCHED_UNLOCK(s); + SCHED_UNLOCK(); } void (*cpu_setperf)(int); diff --git a/sys/kern/subr_witness.c b/sys/kern/subr_witness.c index b8d208313..9f0919013 100644 --- a/sys/kern/subr_witness.c +++ b/sys/kern/subr_witness.c @@ -1,4 +1,4 @@ -/* $OpenBSD: subr_witness.c,v 1.52 2024/05/03 13:47:31 visa Exp $ */ +/* $OpenBSD: subr_witness.c,v 1.53 2024/06/03 14:34:19 claudio Exp $ */ /*- * Copyright (c) 2008 Isilon Systems, Inc. @@ -438,11 +438,6 @@ static struct lock_class lock_class_kernel_lock = { .lc_flags = LC_SLEEPLOCK | LC_RECURSABLE | LC_SLEEPABLE }; -static struct lock_class lock_class_sched_lock = { - .lc_name = "sched_lock", - .lc_flags = LC_SPINLOCK | LC_RECURSABLE -}; - static struct lock_class lock_class_mutex = { .lc_name = "mutex", .lc_flags = LC_SPINLOCK @@ -461,7 +456,6 @@ static struct lock_class lock_class_rrwlock = { static struct lock_class *lock_classes[] = { &lock_class_kernel_lock, - &lock_class_sched_lock, &lock_class_mutex, &lock_class_rwlock, &lock_class_rrwlock, diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c index a310b123d..3e2b4586f 100644 --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sys_process.c,v 1.97 2024/04/02 08:27:22 deraadt Exp $ */ +/* $OpenBSD: sys_process.c,v 1.98 2024/06/03 12:48:25 claudio Exp $ */ /* $NetBSD: sys_process.c,v 1.55 1996/05/15 06:17:47 tls Exp $ */ /*- @@ -283,7 +283,6 @@ ptrace_ctrl(struct proc *p, int req, pid_t pid, caddr_t addr, int data) struct proc *t; /* target thread */ struct process *tr; /* target process */ int error = 0; - int s; switch (req) { case PT_TRACE_ME: @@ -492,10 +491,10 @@ ptrace_ctrl(struct proc *p, int req, pid_t pid, caddr_t addr, int data) /* Finally, deliver the requested signal (or none). */ if (t->p_stat == SSTOP) { tr->ps_xsig = data; - SCHED_LOCK(s); + SCHED_LOCK(); unsleep(t); setrunnable(t); - SCHED_UNLOCK(s); + SCHED_UNLOCK(); } else { if (data != 0) psignal(t, data); diff --git a/sys/sys/_lock.h b/sys/sys/_lock.h index fbcfb0ada..a55b8378d 100644 --- a/sys/sys/_lock.h +++ b/sys/sys/_lock.h @@ -1,4 +1,4 @@ -/* $OpenBSD: _lock.h,v 1.4 2019/04/23 13:35:12 visa Exp $ */ +/* $OpenBSD: _lock.h,v 1.5 2024/06/03 12:46:59 claudio Exp $ */ /*- * Copyright (c) 1997 Berkeley Software Design, Inc. All rights reserved. @@ -50,7 +50,6 @@ enum lock_class_index { LO_CLASS_KERNEL_LOCK, - LO_CLASS_SCHED_LOCK, LO_CLASS_MUTEX, LO_CLASS_RWLOCK, LO_CLASS_RRWLOCK diff --git a/sys/sys/sched.h b/sys/sys/sched.h index 593bd3e60..dcc9b6366 100644 --- a/sys/sys/sched.h +++ b/sys/sys/sched.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sched.h,v 1.71 2024/05/29 18:55:45 claudio Exp $ */ +/* $OpenBSD: sched.h,v 1.72 2024/06/03 12:48:25 claudio Exp $ */ /* $NetBSD: sched.h,v 1.2 1999/02/28 18:14:58 ross Exp $ */ /*- @@ -201,28 +201,12 @@ void remrunqueue(struct proc *); extern struct mutex sched_lock; -#define SCHED_ASSERT_LOCKED() \ -do { \ - MUTEX_ASSERT_LOCKED(&sched_lock); \ -} while (0) -#define SCHED_ASSERT_UNLOCKED() \ -do { \ - MUTEX_ASSERT_UNLOCKED(&sched_lock); \ -} while (0) +#define SCHED_ASSERT_LOCKED() MUTEX_ASSERT_LOCKED(&sched_lock) +#define SCHED_ASSERT_UNLOCKED() MUTEX_ASSERT_UNLOCKED(&sched_lock) #define SCHED_LOCK_INIT() mtx_init(&sched_lock, IPL_SCHED) - -#define SCHED_LOCK(s) \ -do { \ - (s) = 0; /* XXX cleanup useless argument */ \ - mtx_enter(&sched_lock); \ -} while (/* CONSTCOND */ 0) - -#define SCHED_UNLOCK(s) \ -do { \ - (void)s; /* XXX cleanup useless argument */ \ - mtx_leave(&sched_lock); \ -} while (/* CONSTCOND */ 0) +#define SCHED_LOCK() mtx_enter(&sched_lock) +#define SCHED_UNLOCK() mtx_leave(&sched_lock) #endif /* _KERNEL */ #endif /* _SYS_SCHED_H_ */ diff --git a/sys/sys/syscall_mi.h b/sys/sys/syscall_mi.h index 480dc05b0..c83ad99a3 100644 --- a/sys/sys/syscall_mi.h +++ b/sys/sys/syscall_mi.h @@ -1,4 +1,4 @@ -/* $OpenBSD: syscall_mi.h,v 1.33 2024/04/01 12:00:15 deraadt Exp $ */ +/* $OpenBSD: syscall_mi.h,v 1.34 2024/06/02 15:31:57 deraadt Exp $ */ /* * Copyright (c) 1982, 1986, 1989, 1993 @@ -105,9 +105,8 @@ die: ktrpinsyscall(p, error, code, addr); #endif KERNEL_LOCK(); - /* XXX remove or simplify this log() call after SecBSD 1.5 release */ - log(LOG_ERR, - "%s[%d]: pinsyscalls addr %lx code %ld, pinoff 0x%x " + /* XXX remove or simplify this uprintf() call after OpenBSD 7.5 release */ + uprintf("%s[%d]: pinsyscalls addr %lx code %ld, pinoff 0x%x " "(pin%s %d %lx-%lx %lx) (libcpin%s %d %lx-%lx %lx) error %d\n", p->p_p->ps_comm, p->p_p->ps_pid, addr, code, (pin && code < pin->pn_npins) ? pin->pn_pins[code] : -1, diff --git a/sys/uvm/uvm_map.c b/sys/uvm/uvm_map.c index f19a690df..de0e7d247 100644 --- a/sys/uvm/uvm_map.c +++ b/sys/uvm/uvm_map.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_map.c,v 1.328 2024/04/02 08:39:17 deraadt Exp $ */ +/* $OpenBSD: uvm_map.c,v 1.329 2024/06/02 15:31:57 deraadt Exp $ */ /* $NetBSD: uvm_map.c,v 1.86 2000/11/27 08:40:03 chs Exp $ */ /* @@ -1659,7 +1659,7 @@ uvm_map_inentry(struct proc *p, struct p_inentry *ie, vaddr_t addr, ok = uvm_map_inentry_fix(p, ie, addr, fn, serial); if (!ok) { KERNEL_LOCK(); - printf(fmt, p->p_p->ps_comm, p->p_p->ps_pid, p->p_tid, + uprintf(fmt, p->p_p->ps_comm, p->p_p->ps_pid, p->p_tid, addr, ie->ie_start, ie->ie_end-1); p->p_p->ps_acflag |= AMAP; sv.sival_ptr = (void *)PROC_PC(p); @@ -1685,11 +1685,8 @@ uvm_map_is_stack_remappable(struct vm_map *map, vaddr_t addr, vaddr_t sz, vm_map_assert_anylock(map); - if (!uvm_map_lookup_entry(map, addr, &first)) { - printf("map stack 0x%lx-0x%lx of map %p failed: no mapping\n", - addr, end, map); + if (!uvm_map_lookup_entry(map, addr, &first)) return FALSE; - } /* * Check that the address range exists and is contiguous. @@ -1707,16 +1704,10 @@ uvm_map_is_stack_remappable(struct vm_map *map, vaddr_t addr, vaddr_t sz, } #endif - if (prev != NULL && prev->end != iter->start) { - printf("map stack 0x%lx-0x%lx of map %p failed: " - "hole in range\n", addr, end, map); + if (prev != NULL && prev->end != iter->start) return FALSE; - } - if (iter->start == iter->end || UVM_ET_ISHOLE(iter)) { - printf("map stack 0x%lx-0x%lx of map %p failed: " - "hole in range\n", addr, end, map); + if (iter->start == iter->end || UVM_ET_ISHOLE(iter)) return FALSE; - } if (sigaltstack_check) { if (iter->protection != (PROT_READ | PROT_WRITE)) return FALSE; @@ -1740,7 +1731,6 @@ uvm_map_remap_as_stack(struct proc *p, vaddr_t addr, vaddr_t sz) { vm_map_t map = &p->p_vmspace->vm_map; vaddr_t start, end; - int error; int flags = UVM_MAPFLAG(PROT_READ | PROT_WRITE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_INHERIT_COPY, MADV_NORMAL, @@ -1767,11 +1757,7 @@ uvm_map_remap_as_stack(struct proc *p, vaddr_t addr, vaddr_t sz) * placed upon the region, which prevents an attacker from pivoting * into pre-placed MAP_STACK space. */ - error = uvm_mapanon(map, &start, end - start, 0, flags); - if (error != 0) - printf("map stack for pid %d failed\n", p->p_p->ps_pid); - - return error; + return uvm_mapanon(map, &start, end - start, 0, flags); } /* diff --git a/usr.bin/awk/FIXES b/usr.bin/awk/FIXES index 15c463067..5bfc3cada 100644 --- a/usr.bin/awk/FIXES +++ b/usr.bin/awk/FIXES @@ -25,6 +25,10 @@ THIS SOFTWARE. This file lists all bug fixes, changes, etc., made since the second edition of the AWK book was published in September 2023. +May 27, 2024 + Spelling fixes and removal of unneeded prototypes and extern. + Thanks to Jonathan Gray. + May 4, 2024 Fixed a use-after-free bug with ARGV for "delete ARGV". Also ENVtab is no longer global. Thanks to Benjamin Sturz diff --git a/usr.bin/awk/FIXES.1e b/usr.bin/awk/FIXES.1e index c35c71d8f..3c66a3b89 100644 --- a/usr.bin/awk/FIXES.1e +++ b/usr.bin/awk/FIXES.1e @@ -224,7 +224,7 @@ January 9, 2020: mere warnings. Thanks to Martijn Dekker . January 5, 2020: - Fix a bug in the concatentation of two string constants into + Fix a bug in the concatenation of two string constants into one done in the grammar. Fixes GitHub issue #61. Thanks to GitHub user awkfan77 for pointing out the direction for the fix. New test T.concat added to the test suite. @@ -866,7 +866,7 @@ Jan 13, 1999: added a few (int) casts to silence useless compiler warnings. e.g., errorflag= in run.c jump(). - added proctab.c to the bundle outout; one less thing + added proctab.c to the bundle output; one less thing to have to compile out of the box. added calls to _popen and _pclose to the win95 stub for diff --git a/usr.bin/awk/Makefile b/usr.bin/awk/Makefile index ae4e3c285..54f62d002 100644 --- a/usr.bin/awk/Makefile +++ b/usr.bin/awk/Makefile @@ -1,5 +1,6 @@ -# $OpenBSD: Makefile,v 1.18 2020/07/30 17:45:44 millert Exp $ +# $OpenBSD: Makefile,v 1.19 2024/06/03 00:55:05 millert Exp $ +WARNINGS=Yes PROG= awk SRCS= awkgram.tab.c lex.c b.c main.c parse.c proctab.c tran.c lib.c run.c LDADD= -lm diff --git a/usr.bin/awk/README.md b/usr.bin/awk/README.md index ea232e876..bb1a185e8 100644 --- a/usr.bin/awk/README.md +++ b/usr.bin/awk/README.md @@ -27,7 +27,7 @@ this affects `length`, `substr`, `index`, `match`, `split`, points are not necessarily characters. UTF-8 sequences may appear in literal strings and regular expressions. -Aribtrary characters may be included with `\u` followed by 1 to 8 hexadecimal digits. +Arbitrary characters may be included with `\u` followed by 1 to 8 hexadecimal digits. ### Regular expressions ### diff --git a/usr.bin/awk/awk.h b/usr.bin/awk/awk.h index 3b5c67b60..2848d343c 100644 --- a/usr.bin/awk/awk.h +++ b/usr.bin/awk/awk.h @@ -1,4 +1,4 @@ -/* $OpenBSD: awk.h,v 1.31 2023/11/25 16:31:33 millert Exp $ */ +/* $OpenBSD: awk.h,v 1.32 2024/06/03 00:58:04 millert Exp $ */ /**************************************************************** Copyright (C) Lucent Technologies 1997 All Rights Reserved @@ -179,7 +179,6 @@ typedef struct Node { #define NIL ((Node *) 0) extern Node *winner; -extern Node *nullstat; extern Node *nullnode; /* ctypes */ diff --git a/usr.bin/awk/b.c b/usr.bin/awk/b.c index 82f652216..53a36d27b 100644 --- a/usr.bin/awk/b.c +++ b/usr.bin/awk/b.c @@ -1,4 +1,4 @@ -/* $OpenBSD: b.c,v 1.52 2024/05/04 22:59:21 millert Exp $ */ +/* $OpenBSD: b.c,v 1.53 2024/06/03 00:55:05 millert Exp $ */ /**************************************************************** Copyright (C) Lucent Technologies 1997 All Rights Reserved @@ -81,9 +81,6 @@ int patlen; fa *fatab[NFA]; int nfatab = 0; /* entries in fatab */ -extern int u8_nextlen(const char *s); - - /* utf-8 mechanism: For most of Awk, utf-8 strings just "work", since they look like @@ -117,7 +114,6 @@ static int entry_cmp(const void *l, const void *r); static int get_gototab(fa*, int, int); static int set_gototab(fa*, int, int, int); static void clear_gototab(fa*, int); -extern int u8_rune(int *, const char *); static int * intalloc(size_t n, const char *f) @@ -347,7 +343,8 @@ void freetr(Node *p) /* free parse tree */ /* in the parsing of regular expressions, metacharacters like . have */ /* to be seen literally; \056 is not a metacharacter. */ -int hexstr(const uschar **pp, int max) /* find and eval hex string at pp, return new p */ +static int +hexstr(const uschar **pp, int max) /* find and eval hex string at pp, return new p */ { /* only pick up one 8-bit byte (2 chars) */ const uschar *p; int n = 0; diff --git a/usr.bin/awk/lex.c b/usr.bin/awk/lex.c index f1b57e38e..2a9dba2b3 100644 --- a/usr.bin/awk/lex.c +++ b/usr.bin/awk/lex.c @@ -1,4 +1,4 @@ -/* $OpenBSD: lex.c,v 1.32 2023/11/25 16:31:33 millert Exp $ */ +/* $OpenBSD: lex.c,v 1.34 2024/06/03 00:58:04 millert Exp $ */ /**************************************************************** Copyright (C) Lucent Technologies 1997 All Rights Reserved @@ -227,7 +227,7 @@ int yylex(void) ; unput(c); /* - * Next line is a hack, itcompensates for + * Next line is a hack, it compensates for * unput's treatment of \n. */ lineno++; @@ -379,8 +379,6 @@ int yylex(void) } } -extern int runetochar(char *str, int c); - int string(void) { int c, n; diff --git a/usr.bin/awk/lib.c b/usr.bin/awk/lib.c index e6a22bc6c..8ddd935e8 100644 --- a/usr.bin/awk/lib.c +++ b/usr.bin/awk/lib.c @@ -1,4 +1,4 @@ -/* $OpenBSD: lib.c,v 1.57 2024/05/05 02:55:34 jsg Exp $ */ +/* $OpenBSD: lib.c,v 1.58 2024/06/03 00:55:05 millert Exp $ */ /**************************************************************** Copyright (C) Lucent Technologies 1997 All Rights Reserved @@ -35,8 +35,6 @@ THIS SOFTWARE. #include #include "awk.h" -extern int u8_nextlen(const char *s); - char EMPTY[] = { '\0' }; FILE *infile = NULL; bool innew; /* true = infile has not been read by readrec */ @@ -757,7 +755,7 @@ void WARNING(const char *fmt, ...) error(); } -void error() +void error(void) { extern Node *curnode; diff --git a/usr.bin/awk/main.c b/usr.bin/awk/main.c index ef5a724a8..5c4241d5a 100644 --- a/usr.bin/awk/main.c +++ b/usr.bin/awk/main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: main.c,v 1.70 2024/05/04 22:59:21 millert Exp $ */ +/* $OpenBSD: main.c,v 1.71 2024/06/03 00:58:04 millert Exp $ */ /**************************************************************** Copyright (C) Lucent Technologies 1997 All Rights Reserved @@ -23,7 +23,7 @@ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ****************************************************************/ -const char *version = "version 20240504"; +const char *version = "version 20240527"; #define DEBUG #include diff --git a/usr.bin/awk/proto.h b/usr.bin/awk/proto.h index 4c2fafd08..37882294f 100644 --- a/usr.bin/awk/proto.h +++ b/usr.bin/awk/proto.h @@ -1,4 +1,4 @@ -/* $OpenBSD: proto.h,v 1.23 2023/11/22 01:01:21 millert Exp $ */ +/* $OpenBSD: proto.h,v 1.25 2024/06/03 00:58:04 millert Exp $ */ /**************************************************************** Copyright (C) Lucent Technologies 1997 All Rights Reserved @@ -35,9 +35,6 @@ extern void startreg(void); extern int input(void); extern void unput(int); extern void unputstr(const char *); -extern int yylook(void); -extern int yyback(int *, int); -extern int yyinput(void); extern fa *makedfa(const char *, bool); extern fa *mkdfa(const char *, bool); @@ -170,7 +167,6 @@ extern Cell *boolop(Node **, int); extern Cell *relop(Node **, int); extern void tfree(Cell *); extern Cell *gettemp(void); -extern Cell *field(Node **, int); extern Cell *indirect(Node **, int); extern Cell *substr(Node **, int); extern Cell *sindex(Node **, int); @@ -205,4 +201,8 @@ extern Cell *gensub(Node **, int); extern FILE *popen(const char *, const char *); extern int pclose(FILE *); +extern int u8_nextlen(const char *s); +extern int u8_rune(int *, const char *); +extern int runetochar(char *str, int c); + extern const char *flags2str(int flags); diff --git a/usr.bin/awk/run.c b/usr.bin/awk/run.c index 81bf4bd29..04903e121 100644 --- a/usr.bin/awk/run.c +++ b/usr.bin/awk/run.c @@ -1,4 +1,4 @@ -/* $OpenBSD: run.c,v 1.86 2024/05/04 22:59:21 millert Exp $ */ +/* $OpenBSD: run.c,v 1.87 2024/06/03 00:55:05 millert Exp $ */ /**************************************************************** Copyright (C) Lucent Technologies 1997 All Rights Reserved @@ -600,7 +600,7 @@ Cell *intest(Node **a, int n) /* a[0] is index (list), a[1] is symtab */ /* is s the beginning of a valid utf-8 string? */ /* return length 1..4 if yes, 0 if no */ -int u8_isutf(const char *s) +static int u8_isutf(const char *s) { int n, ret; unsigned char c; @@ -671,7 +671,7 @@ int u8_nextlen(const char *s) } /* return number of utf characters or single non-utf bytes */ -int u8_strlen(const char *s) +static int u8_strlen(const char *s) { int i, len, n, totlen; unsigned char c; @@ -693,7 +693,7 @@ int u8_strlen(const char *s) } /* convert utf-8 char number in a string to its byte offset */ -int u8_char2byte(const char *s, int charnum) +static int u8_char2byte(const char *s, int charnum) { int n; int bytenum = 0; @@ -708,7 +708,7 @@ int u8_char2byte(const char *s, int charnum) } /* convert byte offset in s to utf-8 char number that starts there */ -int u8_byte2char(const char *s, int bytenum) +static int u8_byte2char(const char *s, int bytenum) { int i, len, b; int charnum = 0; /* BUG: what origin? */ @@ -1062,7 +1062,7 @@ Cell *sindex(Node **a, int nnn) /* index(a[0], a[1]) */ return(z); } -int has_utf8(char *s) /* return 1 if s contains any utf-8 (2 bytes or more) character */ +static int has_utf8(char *s) /* return 1 if s contains any utf-8 (2 bytes or more) character */ { int n; @@ -1248,13 +1248,13 @@ int format(char **pbuf, int *pbufsize, const char *s, Node *a) /* printf-like co if (prec > u8_strlen(t)) prec = u8_strlen(t); pad = wid>prec ? wid - prec : 0; // has to be >= 0 - int i, k, n; + int i, precb; if (ljust) { // print prec chars from t, then pad blanks - n = u8_char2byte(t, prec); - for (k = 0; k < n; k++) { - //putchar(t[k]); - *p++ = t[k]; + precb = u8_char2byte(t, prec); + for (i = 0; i < precb; i++) { + //putchar(t[i]); + *p++ = t[i]; } for (i = 0; i < pad; i++) { //printf(" "); @@ -1265,10 +1265,10 @@ int format(char **pbuf, int *pbufsize, const char *s, Node *a) /* printf-like co //printf(" "); *p++ = ' '; } - n = u8_char2byte(t, prec); - for (k = 0; k < n; k++) { - //putchar(t[k]); - *p++ = t[k]; + precb = u8_char2byte(t, prec); + for (i = 0; i < precb; i++) { + //putchar(t[i]); + *p++ = t[i]; } } *p = 0; @@ -1986,7 +1986,6 @@ static char *nawk_convert(const char *s, int (*fun_c)(int), size_t n = 0; wchar_t wc; const size_t sz = awk_mb_cur_max; - int unused; if (sz == 1) { buf = tostring(s); @@ -1999,15 +1998,9 @@ static char *nawk_convert(const char *s, int (*fun_c)(int), /* upper/lower character may be shorter/longer */ buf = tostringN(s, strlen(s) * sz + 1); - (void) mbtowc(NULL, NULL, 0); /* reset internal state */ - /* - * Reset internal state here too. - * Assign result to avoid a compiler warning. (Casting to void - * doesn't work.) - * Increment said variable to avoid a different warning. - */ - unused = wctomb(NULL, L'\0'); - unused++; + /* reset internal state */ + if (mbtowc(NULL, NULL, 0) == -1 || wctomb(NULL, L'\0') == -1) + FATAL("unable to reset character conversion state"); ps = s; pbuf = buf; diff --git a/usr.bin/awk/tran.c b/usr.bin/awk/tran.c index ba31583e9..315735bf7 100644 --- a/usr.bin/awk/tran.c +++ b/usr.bin/awk/tran.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tran.c,v 1.39 2024/05/04 22:59:21 millert Exp $ */ +/* $OpenBSD: tran.c,v 1.40 2024/06/03 00:55:05 millert Exp $ */ /**************************************************************** Copyright (C) Lucent Technologies 1997 All Rights Reserved @@ -644,7 +644,7 @@ const char *flags2str(int flags) if ((flags & flagtab[i].value) != 0) { len = snprintf(cp, sizeof(buf) - (cp - buf), "%s%s", cp > buf ? "|" : "", flagtab[i].name); - if (len < 0 || len >= sizeof(buf) - (cp - buf)) + if (len < 0 || (size_t)len >= sizeof(buf) - (cp - buf)) FATAL("out of space in flags2str"); cp += len; } diff --git a/usr.bin/uname/uname.1 b/usr.bin/uname/uname.1 index a6b48bb88..626e02a73 100644 --- a/usr.bin/uname/uname.1 +++ b/usr.bin/uname/uname.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: uname.1,v 1.19 2023/10/09 19:28:42 schwarze Exp $ +.\" $OpenBSD: uname.1,v 1.20 2024/06/03 15:55:52 florian Exp $ .\" .\" Copyright (c) 1990 The Regents of the University of California. .\" All rights reserved. @@ -29,7 +29,7 @@ .\" .\" from: @(#)du.1 6.13 (Berkeley) 6/20/91 .\" -.Dd $Mdocdate: October 9 2023 $ +.Dd $Mdocdate: June 3 2024 $ .Dt UNAME 1 .Os .Sh NAME @@ -73,34 +73,8 @@ On .Ox , the format is .Sm off -.Ar digit . digit Bq \- Ar flavor . +.Ar digit . digit . .Sm on -.Pp -The meaning of the -.Pf \- Ar flavor -suffix is as follows: -.Bl -tag -width "no suffix" -.It \-beta -A development version preceding the -.Ar digit . Ns Ar digit -release. -.It no suffix -A public release, -or a development version very closely preceding that release. -.It \-stable -A branch based on the -.Ar digit . Ns Ar digit -release that only contains patches to fix very important bugs. -.It \-current -A development version coming after the -.Ar digit . Ns Ar digit -release. -Some time before the subsequent release, the second -.Ar digit -is incremented and the -.Pf \- Ar flavor -switched to \-beta. -.El .It Fl s Print the operating system name. On diff --git a/usr.sbin/crunchgen/crunchgen.c b/usr.sbin/crunchgen/crunchgen.c index e0337d556..3eafcaeee 100644 --- a/usr.sbin/crunchgen/crunchgen.c +++ b/usr.sbin/crunchgen/crunchgen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: crunchgen.c,v 1.27 2023/09/14 16:39:00 jca Exp $ */ +/* $OpenBSD: crunchgen.c,v 1.30 2024/06/02 18:47:17 deraadt Exp $ */ /* * Copyright (c) 1994 University of Maryland @@ -897,6 +897,7 @@ top_makefile_rules(FILE * outmk) fprintf(outmk, "CFLAGS+=-fno-unwind-tables\n"); fprintf(outmk, ".if ${MACHINE} == \"amd64\"\n"); fprintf(outmk, "CFLAGS+=-fcf-protection=none\n"); + fprintf(outmk, "CFLAGS+=-fno-ret-clean\n"); fprintf(outmk, ".endif\n"); fprintf(outmk, ".if ${MACHINE} == \"arm64\"\n"); fprintf(outmk, "CFLAGS+=-mbranch-protection=none\n"); diff --git a/usr.sbin/httpd/css.h.in b/usr.sbin/httpd/css.h.in index e57b0aebd..a54406d26 100644 --- a/usr.sbin/httpd/css.h.in +++ b/usr.sbin/httpd/css.h.in @@ -3,6 +3,7 @@ body { background-color: white; color: black; font-family: sans-serif; + font-variant-emoji: text; } table { border-collapse: collapse; @@ -18,7 +19,6 @@ tr.sort th.sorted { font-weight: bold; } tr.sort th::after { content: "\a0\2195"; } tr.dir td:nth-child(2n+1) { font-weight: bold; - font-style: italic; } td, th { padding: 2pt 2em; } td:first-child, th:first-child { padding-left: 5pt; } diff --git a/usr.sbin/rpki-client/cert.c b/usr.sbin/rpki-client/cert.c index d180c6817..625f04793 100644 --- a/usr.sbin/rpki-client/cert.c +++ b/usr.sbin/rpki-client/cert.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cert.c,v 1.132 2024/05/31 02:45:15 tb Exp $ */ +/* $OpenBSD: cert.c,v 1.133 2024/06/03 12:58:39 tb Exp $ */ /* * Copyright (c) 2022 Theo Buehler * Copyright (c) 2021 Job Snijders @@ -495,7 +495,8 @@ sbgp_ipaddrblk(const char *fn, struct cert *cert, X509_EXTENSION *ext) } /* - * Parse "Subject Information Access" extension, RFC 6487 4.8.8. + * Parse "Subject Information Access" extension for a CA cert, + * RFC 6487, section 4.8.8.1 and RFC 8182, section 3.2. * Returns zero on failure, non-zero on success. */ static int @@ -505,8 +506,11 @@ sbgp_sia(const char *fn, struct cert *cert, X509_EXTENSION *ext) ACCESS_DESCRIPTION *ad; ASN1_OBJECT *oid; const char *mftfilename; + char *carepo = NULL, *rpkimft = NULL; int i, rc = 0; + assert(cert->repo == NULL && cert->mft == NULL && cert->notify == NULL); + if (X509_EXTENSION_get_critical(ext)) { warnx("%s: RFC 6487 section 4.8.8: SIA: " "extension not non-critical", fn); @@ -525,13 +529,35 @@ sbgp_sia(const char *fn, struct cert *cert, X509_EXTENSION *ext) oid = ad->method; if (OBJ_cmp(oid, carepo_oid) == 0) { - if (!x509_location(fn, "SIA: caRepository", - RSYNC_PROTO, ad->location, &cert->repo)) + if (!x509_location(fn, "SIA: caRepository", NULL, + ad->location, &carepo)) goto out; + if (cert->repo == NULL && strncasecmp(carepo, + RSYNC_PROTO, RSYNC_PROTO_LEN) == 0) { + cert->repo = carepo; + carepo = NULL; + continue; + } + if (verbose) + warnx("%s: RFC 6487 section 4.8.8: SIA: " + "ignoring location %s", fn, carepo); + free(carepo); + carepo = NULL; } else if (OBJ_cmp(oid, manifest_oid) == 0) { - if (!x509_location(fn, "SIA: rpkiManifest", - RSYNC_PROTO, ad->location, &cert->mft)) + if (!x509_location(fn, "SIA: rpkiManifest", NULL, + ad->location, &rpkimft)) goto out; + if (cert->mft == NULL && strncasecmp(rpkimft, + RSYNC_PROTO, RSYNC_PROTO_LEN) == 0) { + cert->mft = rpkimft; + rpkimft = NULL; + continue; + } + if (verbose) + warnx("%s: RFC 6487 section 4.8.8: SIA: " + "ignoring location %s", fn, rpkimft); + free(rpkimft); + rpkimft = NULL; } else if (OBJ_cmp(oid, notify_oid) == 0) { if (!x509_location(fn, "SIA: rpkiNotify", HTTPS_PROTO, ad->location, &cert->notify)) @@ -844,6 +870,10 @@ cert_parse_pre(const char *fn, const unsigned char *der, size_t len) case NID_sinfo_access: if (sia++ > 0) goto dup; + /* + * This will fail for BGPsec certs, but they must omit + * this extension anyway (RFC 8209, section 3.1.3.3). + */ if (!sbgp_sia(fn, cert, ext)) goto out; break; diff --git a/usr.sbin/rpki-client/x509.c b/usr.sbin/rpki-client/x509.c index 0b28d6ee4..1aad594f3 100644 --- a/usr.sbin/rpki-client/x509.c +++ b/usr.sbin/rpki-client/x509.c @@ -1,4 +1,4 @@ -/* $OpenBSD: x509.c,v 1.90 2024/05/31 11:27:34 tb Exp $ */ +/* $OpenBSD: x509.c,v 1.91 2024/06/03 12:58:39 tb Exp $ */ /* * Copyright (c) 2022 Theo Buehler * Copyright (c) 2021 Claudio Jeker @@ -467,8 +467,8 @@ out: } /* - * Parse the Subject Information Access (SIA) extension - * See RFC 6487, section 4.8.8 for details. + * Parse the Subject Information Access (SIA) extension for an EE cert. + * See RFC 6487, section 4.8.8.2 for details. * Returns NULL on failure, on success returns the SIA signedObject URI * (which has to be freed after use). */ diff --git a/usr.sbin/smtpd/smtp.h b/usr.sbin/smtpd/smtp.h index 11ab6cbed..5321abcd7 100644 --- a/usr.sbin/smtpd/smtp.h +++ b/usr.sbin/smtpd/smtp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: smtp.h,v 1.4 2021/03/05 12:37:32 eric Exp $ */ +/* $OpenBSD: smtp.h,v 1.5 2024/06/02 23:26:39 jsg Exp $ */ /* * Copyright (c) 2018 Eric Faurot @@ -87,7 +87,6 @@ void smtp_quit(struct smtp_client *); void smtp_sendmail(struct smtp_client *, struct smtp_mail *); /* callbacks */ -void smtp_verify_server_cert(void *, struct smtp_client *, void *); void smtp_require_tls(void *, struct smtp_client *); void smtp_ready(void *, struct smtp_client *); void smtp_failed(void *, struct smtp_client *, int, const char *); diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h index e7ff0e3ca..6eddff3c7 100644 --- a/usr.sbin/smtpd/smtpd.h +++ b/usr.sbin/smtpd/smtpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.h,v 1.685 2024/05/28 07:10:30 op Exp $ */ +/* $OpenBSD: smtpd.h,v 1.686 2024/06/02 23:26:39 jsg Exp $ */ /* * Copyright (c) 2008 Gilles Chehade @@ -1261,11 +1261,6 @@ struct rule { /* aliases.c */ int aliases_get(struct expand *, const char *); int aliases_virtual_get(struct expand *, const struct mailaddr *); -int alias_parse(struct expandnode *, const char *); - - -/* auth.c */ -struct auth_backend *auth_backend_lookup(enum auth_type); /* bounce.c */ @@ -1402,7 +1397,6 @@ void lka_filter_end(uint64_t); void lka_filter_protocol(uint64_t, enum filter_phase, const char *); void lka_filter_data_begin(uint64_t); void lka_filter_data_end(uint64_t); -int lka_filter_response(uint64_t, const char *, const char *); /* lka_session.c */ @@ -1609,12 +1603,6 @@ int smtp_session(struct listener *, int, const struct sockaddr_storage *, void smtp_session_imsg(struct mproc *, struct imsg *); -/* smtpf_session.c */ -int smtpf_session(struct listener *, int, const struct sockaddr_storage *, - const char *); -void smtpf_session_imsg(struct mproc *, struct imsg *); - - /* smtpd.c */ void imsg_dispatch(struct mproc *, struct imsg *); const char *proc_name(enum smtp_proc_type); @@ -1670,7 +1658,6 @@ void table_close_all(struct smtpd *); /* to.c */ -int email_to_mailaddr(struct mailaddr *, char *); int text_to_netaddr(struct netaddr *, const char *); int text_to_mailaddr(struct mailaddr *, const char *); int text_to_relayhost(struct relayhost *, const char *); @@ -1714,7 +1701,6 @@ int lowercase(char *, const char *, size_t); void xlowercase(char *, const char *, size_t); int uppercase(char *, const char *, size_t); uint64_t generate_uid(void); -int availdesc(void); int ckdir(const char *, mode_t, uid_t, gid_t, int); int rmtree(char *, int); int mvpurge(char *, char *); @@ -1730,8 +1716,6 @@ char *strip(char *); int io_xprint(struct io *, const char *); int io_xprintf(struct io *, const char *, ...) __attribute__((__format__ (printf, 2, 3))); -void log_envelope(const struct envelope *, const char *, const char *, - const char *); int session_socket_error(int); int getmailname(char *, size_t); int base64_encode(unsigned char const *, size_t, char *, size_t); diff --git a/usr.sbin/smtpd/util.c b/usr.sbin/smtpd/util.c index 5c08cd49e..f7df1a6d5 100644 --- a/usr.sbin/smtpd/util.c +++ b/usr.sbin/smtpd/util.c @@ -1,4 +1,4 @@ -/* $OpenBSD: util.c,v 1.158 2024/05/13 06:48:26 jsg Exp $ */ +/* $OpenBSD: util.c,v 1.159 2024/06/02 23:26:39 jsg Exp $ */ /* * Copyright (c) 2000,2001 Markus Friedl. All rights reserved. @@ -38,8 +38,6 @@ #include "smtpd.h" #include "log.h" -const char *log_in6addr(const struct in6_addr *); -const char *log_sockaddr(struct sockaddr *); static int parse_mailname_file(char *, size_t); int tracing = 0; diff --git a/usr.sbin/snmpd/parse.y b/usr.sbin/snmpd/parse.y index 2a702e8db..f1aeb995c 100644 --- a/usr.sbin/snmpd/parse.y +++ b/usr.sbin/snmpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.90 2024/02/20 12:32:48 martijn Exp $ */ +/* $OpenBSD: parse.y,v 1.91 2024/06/03 06:14:32 anton Exp $ */ /* * Copyright (c) 2007, 2008, 2012 Reyk Floeter @@ -142,6 +142,8 @@ static uint8_t engineid[SNMPD_MAXENGINEIDLEN]; static int32_t enginepen; static size_t engineidlen; +static unsigned char sha256[SHA256_DIGEST_LENGTH]; + int resolve_oid(struct ber_oid *, struct oid_sym *); int resolve_oids(void); int host(const char *, const char *, int, int, @@ -708,7 +710,7 @@ enginefmt : IP4 STRING { } engineid[engineidlen++] = SNMP_ENGINEID_FMT_HH; memcpy(engineid + engineidlen, - SHA256($2, strlen($2), NULL), + SHA256($2, strlen($2), sha256), sizeof(engineid) - engineidlen); engineidlen = sizeof(engineid); engineid[0] |= SNMP_ENGINEID_NEW; @@ -761,7 +763,7 @@ enginefmt_local : enginefmt engineid[engineidlen++] = SNMP_ENGINEID_FMT_HH; memcpy(engineid + engineidlen, - SHA256(hostname, strlen(hostname), NULL), + SHA256(hostname, strlen(hostname), sha256), sizeof(engineid) - engineidlen); engineidlen = sizeof(engineid); engineid[0] |= SNMP_ENGINEID_NEW; @@ -1861,7 +1863,7 @@ parse_config(const char *filename, u_int flags) conf->sc_engineid[conf->sc_engineid_len++] |= SNMP_ENGINEID_FMT_HH; memcpy(conf->sc_engineid + conf->sc_engineid_len, - SHA256(hostname, strlen(hostname), NULL), + SHA256(hostname, strlen(hostname), sha256), sizeof(conf->sc_engineid) - conf->sc_engineid_len); conf->sc_engineid_len = sizeof(conf->sc_engineid); conf->sc_engineid[0] |= SNMP_ENGINEID_NEW;