Compare commits
86 Commits
Author | SHA1 | Date |
---|---|---|
roy | 986d1710a8 | |
roy | e355904211 | |
roy | a7f052dc0d | |
roy | 0ea6469f35 | |
roy | 6d263e5669 | |
roy | 5f00958399 | |
roy | 4fce98e7fd | |
roy | 5ba815c7a7 | |
roy | b21ddb5395 | |
roy | 33445a79ae | |
roy | c9893f654b | |
roy | ee4e0574e8 | |
roy | b81737046b | |
roy | c5770ceb10 | |
roy | c44edc208c | |
roy | b1246fe72f | |
roy | b90aaaef64 | |
roy | cd40ae1fd5 | |
roy | 61b23c6195 | |
roy | 404637bd06 | |
roy | 83a86239b0 | |
roy | aac4420686 | |
roy | 55689a1efa | |
roy | 961d2a0db3 | |
roy | 603fb519dd | |
roy | 3d6c38e070 | |
roy | 98dee57b86 | |
roy | 173e555c9e | |
roy | 568a64c5ec | |
roy | d4128c3af8 | |
roy | f31da2ae25 | |
roy | 3ded1c87fc | |
roy | 8105b4b2ec | |
roy | 40ee987d84 | |
roy | 1cc3fcda30 | |
roy | 3191a42627 | |
roy | 8c55aad609 | |
roy | 5921161ee0 | |
roy | 7dfda7b4f5 | |
roy | b83bc1b66c | |
roy | 4dfdf2681a | |
roy | b6a3a94eb1 | |
roy | cb0b7d2b9f | |
roy | 141ff30ff1 | |
roy | 6964063b9b | |
roy | 3ed23301ff | |
roy | ec73a3e813 | |
roy | dc127b4bb4 | |
roy | dbd4cee32e | |
roy | a5e54c894f | |
roy | 2d8cdccce8 | |
roy | 0fda18bd0d | |
roy | 78f6e36f5b | |
roy | 3b33a0ebf3 | |
roy | 12f1dee606 | |
roy | afa3b9b888 | |
roy | 259182f30d | |
roy | 483cca5a38 | |
roy | 71c9531719 | |
roy | 6909e9fcde | |
roy | 19ee775ace | |
roy | 8f03b3ce8d | |
roy | 011165c38b | |
roy | 1f8b775ce0 | |
roy | f9f6f284e0 | |
roy | d0f9585b51 | |
roy | a0f00732f7 | |
roy | 5231d2c6ff | |
roy | 1d279116c4 | |
roy | 19127286b8 | |
roy | 917ddb683c | |
roy | 79169177cf | |
roy | 2837f4b649 | |
roy | 5418580eb9 | |
roy | b50127bf05 | |
roy | aa9a7cc616 | |
roy | 9f020fd04f | |
roy | 81cde76eff | |
roy | 8313ebff18 | |
roy | 62638c05f8 | |
roy | 45de6d39f8 | |
roy | 5adcf9c629 | |
roy | 9d96839845 | |
roy | 0165dbff35 | |
roy | 51e1959dcb | |
roy | 94d183a61c |
|
@ -0,0 +1,23 @@
|
|||
Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
|
@ -0,0 +1,96 @@
|
|||
# dhcpcd
|
||||
|
||||
dhcpcd is a
|
||||
[DHCP](https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol) and a
|
||||
[DHCPv6](https://en.wikipedia.org/wiki/DHCPv6) client.
|
||||
It's also an IPv4LL (aka [ZeroConf](https://en.wikipedia.org/wiki/Zeroconf))
|
||||
client.
|
||||
In layperson's terms, dhcpcd runs on your machine and silently configures your
|
||||
computer to work on the attached networks without trouble and mostly without
|
||||
configuration.
|
||||
|
||||
If you're a desktop user then you may also be interested in
|
||||
[Network Configurator (dhcpcd-ui)](http://roy.marples.name/projects/dhcpcd-ui)
|
||||
which sits in the notification area and monitors the state of the network via
|
||||
dhcpcd.
|
||||
It also has a nice configuration dialog and the ability to enter a pass phrase
|
||||
for wireless networks.
|
||||
|
||||
dhcpcd may not be the only daemon running that wants to configure DNS on the
|
||||
host, so it uses [openresolv](http://roy.marples.name/projects/openresolv)
|
||||
to ensure they can co-exist.
|
||||
|
||||
See [BUILDING.md](BUILDING.md) for how to build dhcpcd.
|
||||
|
||||
## Configuration
|
||||
|
||||
You should read the dhcpcd.conf man page
|
||||
and put your options into `/etc/dhcpcd.conf`.
|
||||
The default configuration file should work for most people just fine.
|
||||
Here it is, in case you lose it.
|
||||
|
||||
```
|
||||
# A sample configuration for dhcpcd.
|
||||
# See dhcpcd.conf(5) for details.
|
||||
|
||||
# Allow users of this group to interact with dhcpcd via the control socket.
|
||||
#controlgroup wheel
|
||||
|
||||
# Inform the DHCP server of our hostname for DDNS.
|
||||
hostname
|
||||
|
||||
# Use the hardware address of the interface for the Client ID.
|
||||
#clientid
|
||||
# or
|
||||
# Use the same DUID + IAID as set in DHCPv6 for DHCPv4 ClientID as per RFC4361.
|
||||
# Some non-RFC compliant DHCP servers do not reply with this set.
|
||||
# In this case, comment out duid and enable clientid above.
|
||||
duid
|
||||
|
||||
# Persist interface configuration when dhcpcd exits.
|
||||
persistent
|
||||
|
||||
# Rapid commit support.
|
||||
# Safe to enable by default because it requires the equivalent option set
|
||||
# on the server to actually work.
|
||||
option rapid_commit
|
||||
|
||||
# A list of options to request from the DHCP server.
|
||||
option domain_name_servers, domain_name, domain_search, host_name
|
||||
option classless_static_routes
|
||||
# Respect the network MTU. This is applied to DHCP routes.
|
||||
option interface_mtu
|
||||
|
||||
# Most distributions have NTP support.
|
||||
#option ntp_servers
|
||||
|
||||
# A ServerID is required by RFC2131.
|
||||
require dhcp_server_identifier
|
||||
|
||||
# Generate SLAAC address using the Hardware Address of the interface
|
||||
#slaac hwaddr
|
||||
# OR generate Stable Private IPv6 Addresses based from the DUID
|
||||
slaac private
|
||||
```
|
||||
|
||||
The dhcpcd man page has a lot of the same options and more,
|
||||
which only apply to calling dhcpcd from the command line.
|
||||
|
||||
|
||||
## Compatibility
|
||||
dhcpcd-5 is only fully command line compatible with dhcpcd-4.
|
||||
For compatibility with older versions, use dhcpcd-4.
|
||||
|
||||
## Upgrading
|
||||
dhcpcd-7 defaults the database directory to `/var/db/dhcpcd` instead of
|
||||
`/var/db` and now stores dhcpcd.duid and dhcpcd.secret in there instead of
|
||||
in /etc.
|
||||
|
||||
dhcpcd-9 defaults the run directory to `/var/run/dhcpcd` instead of
|
||||
`/var/run` and the prefix of dhcpcd has been removed from the files therein.
|
||||
|
||||
## ChangeLog
|
||||
We no longer supply a ChangeLog.
|
||||
However, you're more than welcome to read the
|
||||
[commit log](https://github.com/NetworkConfiguration/dhcpcd/commits) and
|
||||
[release announcements](https://github.com/NetworkConfiguration/dhcpcd/releases).
|
|
@ -0,0 +1,11 @@
|
|||
# setup chroot mounts
|
||||
|
||||
if [ "$reason" = CHROOT ] && [ -n "$chroot" ]; then
|
||||
for d in /dev /proc /sys /run/udev; do
|
||||
[ -d "$d" ] || continue
|
||||
if ! mountpoint -q "$chroot$d"; then
|
||||
mkdir -p "$chroot$d"
|
||||
mount --bind $d "$chroot$d"
|
||||
fi
|
||||
done
|
||||
fi
|
|
@ -0,0 +1,37 @@
|
|||
# Echo the interface flags, reason and message options
|
||||
|
||||
if [ "$reason" = "TEST" ]; then
|
||||
# General variables at the top
|
||||
set | while read line; do
|
||||
case "$line" in
|
||||
interface=*|pid=*|reason=*|protocol=*|profile=*|skip_hooks=*)
|
||||
echo "$line";;
|
||||
esac
|
||||
done
|
||||
# Interface flags
|
||||
set | while read line; do
|
||||
case "$line" in
|
||||
ifcarrier=*|ifflags=*|ifmetric=*|ifmtu=*|ifwireless=*|ifssid=*)
|
||||
echo "$line";;
|
||||
esac
|
||||
done
|
||||
# Old lease
|
||||
set | while read line; do
|
||||
case "$line" in
|
||||
old_*) echo "$line";;
|
||||
esac
|
||||
done
|
||||
# New lease
|
||||
set | while read line; do
|
||||
case "$line" in
|
||||
new_*) echo "$line";;
|
||||
esac
|
||||
done
|
||||
# Router Advertisements
|
||||
set | while read line; do
|
||||
case "$line" in
|
||||
nd[0-9]*_*) echo "$line";;
|
||||
esac
|
||||
done
|
||||
exit 0
|
||||
fi
|
|
@ -0,0 +1,8 @@
|
|||
# Just echo our DHCP options we have
|
||||
|
||||
case "$reason" in
|
||||
DUMP|DUMP6)
|
||||
set | sed -ne 's/^new_//p' | sort
|
||||
exit 0
|
||||
;;
|
||||
esac
|
|
@ -0,0 +1,113 @@
|
|||
# Start, reconfigure and stop wpa_supplicant per wireless interface.
|
||||
#
|
||||
# This is only needed when using wpa_supplicant-2.5 or older, OR
|
||||
# when wpa_supplicant has not been built with CONFIG_MATCH_IFACE, OR
|
||||
# wpa_supplicant was launched without the -M flag to activate
|
||||
# interface matching.
|
||||
|
||||
if [ -z "$wpa_supplicant_conf" ]; then
|
||||
for x in \
|
||||
/etc/wpa_supplicant/wpa_supplicant-"$interface".conf \
|
||||
/etc/wpa_supplicant/wpa_supplicant.conf \
|
||||
/etc/wpa_supplicant-"$interface".conf \
|
||||
/etc/wpa_supplicant.conf \
|
||||
; do
|
||||
if [ -s "$x" ]; then
|
||||
wpa_supplicant_conf="$x"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
: ${wpa_supplicant_conf:=/etc/wpa_supplicant.conf}
|
||||
|
||||
wpa_supplicant_ctrldir()
|
||||
{
|
||||
dir=$(key_get_value "[[:space:]]*ctrl_interface=" \
|
||||
"$wpa_supplicant_conf")
|
||||
dir=$(trim "$dir")
|
||||
case "$dir" in
|
||||
DIR=*)
|
||||
dir=${dir##DIR=}
|
||||
dir=${dir%%[[:space:]]GROUP=*}
|
||||
dir=$(trim "$dir")
|
||||
;;
|
||||
esac
|
||||
printf %s "$dir"
|
||||
}
|
||||
|
||||
wpa_supplicant_start()
|
||||
{
|
||||
# If the carrier is up, don't bother checking anything
|
||||
[ "$ifcarrier" = "up" ] && return 0
|
||||
|
||||
# Pre flight checks
|
||||
if [ ! -s "$wpa_supplicant_conf" ]; then
|
||||
syslog warn \
|
||||
"$wpa_supplicant_conf does not exist"
|
||||
syslog warn "not interacting with wpa_supplicant(8)"
|
||||
return 1
|
||||
fi
|
||||
dir=$(wpa_supplicant_ctrldir)
|
||||
if [ -z "$dir" ]; then
|
||||
syslog warn \
|
||||
"ctrl_interface not defined in $wpa_supplicant_conf"
|
||||
syslog warn "not interacting with wpa_supplicant(8)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
wpa_cli -p "$dir" -i "$interface" status >/dev/null 2>&1 && return 0
|
||||
syslog info "starting wpa_supplicant"
|
||||
driver=${wpa_supplicant_driver:+-D}$wpa_supplicant_driver
|
||||
err=$(wpa_supplicant -B -c"$wpa_supplicant_conf" -i"$interface" \
|
||||
"$driver" 2>&1)
|
||||
errn=$?
|
||||
if [ $errn != 0 ]; then
|
||||
syslog err "failed to start wpa_supplicant"
|
||||
syslog err "$err"
|
||||
fi
|
||||
return $errn
|
||||
}
|
||||
|
||||
wpa_supplicant_reconfigure()
|
||||
{
|
||||
dir=$(wpa_supplicant_ctrldir)
|
||||
[ -z "$dir" ] && return 1
|
||||
if ! wpa_cli -p "$dir" -i "$interface" status >/dev/null 2>&1; then
|
||||
wpa_supplicant_start
|
||||
return $?
|
||||
fi
|
||||
syslog info "reconfiguring wpa_supplicant"
|
||||
err=$(wpa_cli -p "$dir" -i "$interface" reconfigure 2>&1)
|
||||
errn=$?
|
||||
if [ $errn != 0 ]; then
|
||||
syslog err "failed to reconfigure wpa_supplicant"
|
||||
syslog err "$err"
|
||||
fi
|
||||
return $errn
|
||||
}
|
||||
|
||||
wpa_supplicant_stop()
|
||||
{
|
||||
dir=$(wpa_supplicant_ctrldir)
|
||||
[ -z "$dir" ] && return 1
|
||||
wpa_cli -p "$dir" -i "$interface" status >/dev/null 2>&1 || return 0
|
||||
syslog info "stopping wpa_supplicant"
|
||||
err=$(wpa_cli -i"$interface" terminate 2>&1)
|
||||
errn=$?
|
||||
if [ $errn != 0 ]; then
|
||||
syslog err "failed to stop wpa_supplicant"
|
||||
syslog err "$err"
|
||||
fi
|
||||
return $errn
|
||||
}
|
||||
|
||||
if [ "$ifwireless" = "1" ] && \
|
||||
command -v wpa_supplicant >/dev/null 2>&1 && \
|
||||
command -v wpa_cli >/dev/null 2>&1
|
||||
then
|
||||
case "$reason" in
|
||||
PREINIT) wpa_supplicant_start;;
|
||||
RECONFIGURE) wpa_supplicant_reconfigure;;
|
||||
DEPARTED|STOPPED) wpa_supplicant_stop;;
|
||||
esac
|
||||
fi
|
|
@ -0,0 +1,47 @@
|
|||
# Configure timezone
|
||||
|
||||
: ${localtime:=/etc/localtime}
|
||||
|
||||
set_zoneinfo()
|
||||
{
|
||||
[ -z "$new_tzdb_timezone" ] && return 0
|
||||
|
||||
zoneinfo_dir=
|
||||
for d in \
|
||||
/usr/share/zoneinfo \
|
||||
/usr/lib/zoneinfo \
|
||||
/var/share/zoneinfo \
|
||||
/var/zoneinfo \
|
||||
; do
|
||||
if [ -d "$d" ]; then
|
||||
zoneinfo_dir="$d"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$zoneinfo_dir" ]; then
|
||||
syslog warning "timezone directory not found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
zone_file="$zoneinfo_dir/$new_tzdb_timezone"
|
||||
if [ ! -e "$zone_file" ]; then
|
||||
syslog warning "no timezone definition for $new_tzdb_timezone"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if copy_file "$zone_file" "$localtime"; then
|
||||
syslog info "timezone changed to $new_tzdb_timezone"
|
||||
fi
|
||||
}
|
||||
|
||||
# For ease of use, map DHCP6 names onto our DHCP4 names
|
||||
case "$reason" in
|
||||
BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)
|
||||
new_tzdb_timezone="$new_dhcp6_tzdb_timezone"
|
||||
;;
|
||||
esac
|
||||
|
||||
if $if_configured && $if_up; then
|
||||
set_zoneinfo
|
||||
fi
|
|
@ -0,0 +1,224 @@
|
|||
# Generate /etc/resolv.conf
|
||||
# Support resolvconf(8) if available
|
||||
# We can merge other dhcpcd resolv.conf files into one like resolvconf,
|
||||
# but resolvconf is preferred as other applications like VPN clients
|
||||
# can readily hook into it.
|
||||
# Also, resolvconf can configure local nameservers such as bind
|
||||
# or dnsmasq. This is important as the libc resolver isn't that powerful.
|
||||
|
||||
resolv_conf_dir="$state_dir/resolv.conf"
|
||||
nocarrier_roaming_dir="$state_dir/roaming"
|
||||
NL="
|
||||
"
|
||||
: ${resolvconf:=resolvconf}
|
||||
if command -v "$resolvconf" >/dev/null 2>&1; then
|
||||
have_resolvconf=true
|
||||
else
|
||||
have_resolvconf=false
|
||||
fi
|
||||
|
||||
build_resolv_conf()
|
||||
{
|
||||
cf="$state_dir/resolv.conf.$ifname"
|
||||
|
||||
# Build a list of interfaces
|
||||
interfaces=$(list_interfaces "$resolv_conf_dir")
|
||||
|
||||
# Build the resolv.conf
|
||||
header=
|
||||
if [ -n "$interfaces" ]; then
|
||||
# Build the header
|
||||
for x in ${interfaces}; do
|
||||
header="$header${header:+, }$x"
|
||||
done
|
||||
|
||||
# Build the search list
|
||||
domain=$(cd "$resolv_conf_dir"; \
|
||||
key_get_value "domain " ${interfaces})
|
||||
search=$(cd "$resolv_conf_dir"; \
|
||||
key_get_value "search " ${interfaces})
|
||||
set -- ${domain}
|
||||
domain="$1"
|
||||
[ -n "$2" ] && search="$search $*"
|
||||
[ -n "$search" ] && search="$(uniqify $search)"
|
||||
[ "$domain" = "$search" ] && search=
|
||||
[ -n "$domain" ] && domain="domain $domain$NL"
|
||||
[ -n "$search" ] && search="search $search$NL"
|
||||
|
||||
# Build the nameserver list
|
||||
srvs=$(cd "$resolv_conf_dir"; \
|
||||
key_get_value "nameserver " ${interfaces})
|
||||
for x in $(uniqify $srvs); do
|
||||
servers="${servers}nameserver $x$NL"
|
||||
done
|
||||
fi
|
||||
header="$signature_base${header:+ $from }$header"
|
||||
|
||||
# Assemble resolv.conf using our head and tail files
|
||||
[ -f "$cf" ] && rm -f "$cf"
|
||||
[ -d "$resolv_conf_dir" ] || mkdir -p "$resolv_conf_dir"
|
||||
echo "$header" > "$cf"
|
||||
if [ -f /etc/resolv.conf.head ]; then
|
||||
cat /etc/resolv.conf.head >> "$cf"
|
||||
else
|
||||
echo "# /etc/resolv.conf.head can replace this line" >> "$cf"
|
||||
fi
|
||||
printf %s "$domain$search$servers" >> "$cf"
|
||||
if [ -f /etc/resolv.conf.tail ]; then
|
||||
cat /etc/resolv.conf.tail >> "$cf"
|
||||
else
|
||||
echo "# /etc/resolv.conf.tail can replace this line" >> "$cf"
|
||||
fi
|
||||
if change_file /etc/resolv.conf "$cf"; then
|
||||
chmod 644 /etc/resolv.conf
|
||||
fi
|
||||
rm -f "$cf"
|
||||
}
|
||||
|
||||
# Extract any ND DNS options from the RA
|
||||
# Obey the lifetimes
|
||||
eval_nd_dns()
|
||||
{
|
||||
|
||||
eval rdnsstime=\$nd${i}_rdnss${j}_lifetime
|
||||
[ -z "$rdnsstime" ] && return 1
|
||||
ltime=$(($rdnsstime - $offset))
|
||||
if [ "$ltime" -gt 0 ]; then
|
||||
eval rdnss=\$nd${i}_rdnss${j}_servers
|
||||
[ -n "$rdnss" ] && new_rdnss="$new_rdnss${new_rdnss:+ }$rdnss"
|
||||
fi
|
||||
|
||||
eval dnssltime=\$nd${i}_dnssl${j}_lifetime
|
||||
[ -z "$dnssltime" ] && return 1
|
||||
ltime=$(($dnssltime - $offset))
|
||||
if [ "$ltime" -gt 0 ]; then
|
||||
eval dnssl=\$nd${i}_dnssl${j}_search
|
||||
[ -n "$dnssl" ] && new_dnssl="$new_dnssl${new_dnssl:+ }$dnssl"
|
||||
fi
|
||||
|
||||
j=$(($j + 1))
|
||||
return 0
|
||||
}
|
||||
|
||||
add_resolv_conf()
|
||||
{
|
||||
conf="$signature$NL"
|
||||
warn=true
|
||||
|
||||
# Loop to extract the ND DNS options using our indexed shell values
|
||||
i=1
|
||||
j=1
|
||||
while true; do
|
||||
eval acquired=\$nd${i}_acquired
|
||||
[ -z "$acquired" ] && break
|
||||
eval now=\$nd${i}_now
|
||||
[ -z "$now" ] && break
|
||||
offset=$(($now - $acquired))
|
||||
while true; do
|
||||
eval_nd_dns || break
|
||||
done
|
||||
i=$(($i + 1))
|
||||
j=1
|
||||
done
|
||||
[ -n "$new_rdnss" ] && \
|
||||
new_domain_name_servers="$new_domain_name_servers${new_domain_name_servers:+ }$new_rdnss"
|
||||
[ -n "$new_dnssl" ] && \
|
||||
new_domain_search="$new_domain_search${new_domain_search:+ }$new_dnssl"
|
||||
|
||||
# Derive a new domain from our various hostname options
|
||||
if [ -z "$new_domain_name" ]; then
|
||||
if [ "$new_dhcp6_fqdn" != "${new_dhcp6_fqdn#*.}" ]; then
|
||||
new_domain_name="${new_dhcp6_fqdn#*.}"
|
||||
elif [ "$new_fqdn" != "${new_fqdn#*.}" ]; then
|
||||
new_domain_name="${new_fqdn#*.}"
|
||||
elif [ "$new_host_name" != "${new_host_name#*.}" ]; then
|
||||
new_domain_name="${new_host_name#*.}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# If we don't have any configuration, remove it
|
||||
if [ -z "$new_domain_name_servers" ] &&
|
||||
[ -z "$new_domain_name" ] &&
|
||||
[ -z "$new_domain_search" ]; then
|
||||
remove_resolv_conf
|
||||
return $?
|
||||
fi
|
||||
|
||||
if [ -n "$new_domain_name" ]; then
|
||||
set -- $new_domain_name
|
||||
if valid_domainname "$1"; then
|
||||
conf="${conf}domain $1$NL"
|
||||
else
|
||||
syslog err "Invalid domain name: $1"
|
||||
fi
|
||||
# If there is no search this, make this one
|
||||
if [ -z "$new_domain_search" ]; then
|
||||
new_domain_search="$new_domain_name"
|
||||
[ "$new_domain_name" = "$1" ] && warn=true
|
||||
fi
|
||||
fi
|
||||
if [ -n "$new_domain_search" ]; then
|
||||
new_domain_search=$(uniqify $new_domain_search)
|
||||
if valid_domainname_list $new_domain_search; then
|
||||
conf="${conf}search $new_domain_search$NL"
|
||||
elif ! $warn; then
|
||||
syslog err "Invalid domain name in list:" \
|
||||
"$new_domain_search"
|
||||
fi
|
||||
fi
|
||||
new_domain_name_servers=$(uniqify $new_domain_name_servers)
|
||||
for x in ${new_domain_name_servers}; do
|
||||
conf="${conf}nameserver $x$NL"
|
||||
done
|
||||
if $have_resolvconf; then
|
||||
[ -n "$ifmetric" ] && export IF_METRIC="$ifmetric"
|
||||
printf %s "$conf" | "$resolvconf" -a "$ifname"
|
||||
return $?
|
||||
fi
|
||||
|
||||
if [ -e "$resolv_conf_dir/$ifname" ]; then
|
||||
rm -f "$resolv_conf_dir/$ifname"
|
||||
fi
|
||||
[ -d "$resolv_conf_dir" ] || mkdir -p "$resolv_conf_dir"
|
||||
printf %s "$conf" > "$resolv_conf_dir/$ifname"
|
||||
build_resolv_conf
|
||||
}
|
||||
|
||||
remove_resolv_conf()
|
||||
{
|
||||
if $have_resolvconf; then
|
||||
"$resolvconf" -d "$ifname" -f
|
||||
else
|
||||
if [ -e "$resolv_conf_dir/$ifname" ]; then
|
||||
rm -f "$resolv_conf_dir/$ifname"
|
||||
fi
|
||||
build_resolv_conf
|
||||
fi
|
||||
}
|
||||
|
||||
# For ease of use, map DHCP6 names onto our DHCP4 names
|
||||
case "$reason" in
|
||||
BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)
|
||||
new_domain_name_servers="$new_dhcp6_name_servers"
|
||||
new_domain_search="$new_dhcp6_domain_search"
|
||||
;;
|
||||
esac
|
||||
|
||||
if $if_configured; then
|
||||
if $have_resolvconf && [ "$reason" = NOCARRIER_ROAMING ]; then
|
||||
# avoid calling resolvconf -c on CARRIER unless we roam
|
||||
mkdir -p "$nocarrier_roaming_dir"
|
||||
echo " " >"$nocarrier_roaming_dir/$interface"
|
||||
"$resolvconf" -C "$interface.*"
|
||||
elif $have_resolvconf && [ "$reason" = CARRIER ]; then
|
||||
# Not all resolvconf implementations support -c
|
||||
if [ -e "$nocarrier_roaming_dir/$interface" ]; then
|
||||
rm -f "$nocarrier_roaming_dir/$interface"
|
||||
"$resolvconf" -c "$interface.*"
|
||||
fi
|
||||
elif $if_up || [ "$reason" = ROUTERADVERT ]; then
|
||||
add_resolv_conf
|
||||
elif $if_down; then
|
||||
remove_resolv_conf
|
||||
fi
|
||||
fi
|
|
@ -0,0 +1,39 @@
|
|||
# Lookup the hostname in DNS if not set
|
||||
|
||||
lookup_hostname()
|
||||
{
|
||||
[ -z "$new_ip_address" ] && return 1
|
||||
# Silly ISC programs love to send error text to stdout
|
||||
if command -v dig >/dev/null 2>&1; then
|
||||
h=$(dig +short -x $new_ip_address)
|
||||
if [ $? = 0 ]; then
|
||||
echo "$h" | sed 's/\.$//'
|
||||
return 0
|
||||
fi
|
||||
elif command -v host >/dev/null 2>&1; then
|
||||
h=$(host $new_ip_address)
|
||||
if [ $? = 0 ]; then
|
||||
echo "$h" \
|
||||
| sed 's/.* domain name pointer \(.*\)./\1/'
|
||||
return 0
|
||||
fi
|
||||
elif command -v getent >/dev/null 2>&1; then
|
||||
h=$(getent hosts $new_ip_address)
|
||||
if [ $? = 0 ]; then
|
||||
echo "$h" | sed 's/[^ ]* *\([^ ]*\).*/\1/'
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
set_hostname()
|
||||
{
|
||||
if [ -z "${new_host_name}${new_fqdn_name}" ]; then
|
||||
export new_host_name="$(lookup_hostname)"
|
||||
fi
|
||||
}
|
||||
|
||||
if $if_up; then
|
||||
set_hostname
|
||||
fi
|
|
@ -0,0 +1,158 @@
|
|||
# Set the hostname from DHCP data if required
|
||||
|
||||
# A hostname can either be a short hostname or a FQDN.
|
||||
# hostname_fqdn=true
|
||||
# hostname_fqdn=false
|
||||
# hostname_fqdn=server
|
||||
|
||||
# A value of server means just what the server says, don't manipulate it.
|
||||
# This could lead to an inconsistent hostname on a DHCPv4 and DHCPv6 network
|
||||
# where the DHCPv4 hostname is short and the DHCPv6 has an FQDN.
|
||||
# DHCPv6 has no hostname option.
|
||||
# RFC4702 section 3.1 says FQDN should be prefered over hostname.
|
||||
#
|
||||
# As such, the default is hostname_fqdn=true so that a consistent hostname
|
||||
# is always assigned.
|
||||
: ${hostname_fqdn:=true}
|
||||
|
||||
# If we used to set the hostname, but relinquish control of it, we should
|
||||
# reset to the default value.
|
||||
: ${hostname_default=}
|
||||
|
||||
# Some systems don't have hostname(1)
|
||||
_hostname()
|
||||
{
|
||||
if [ -z "${1+x}" ]; then
|
||||
if [ -r /proc/sys/kernel/hostname ]; then
|
||||
read name </proc/sys/kernel/hostname && echo "$name"
|
||||
elif command -v hostname >/dev/null 2>/dev/null; then
|
||||
hostname
|
||||
elif sysctl kern.hostname >/dev/null 2>&1; then
|
||||
sysctl -n kern.hostname
|
||||
elif sysctl kernel.hostname >/dev/null 2>&1; then
|
||||
sysctl -n kernel.hostname
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
return $?
|
||||
fi
|
||||
|
||||
if [ -w /proc/sys/kernel/hostname ]; then
|
||||
echo "$1" >/proc/sys/kernel/hostname
|
||||
elif [ -n "$1" ] && command -v hostname >/dev/null 2>&1; then
|
||||
hostname "$1"
|
||||
elif sysctl kern.hostname >/dev/null 2>&1; then
|
||||
sysctl -w "kern.hostname=$1" >/dev/null
|
||||
elif sysctl kernel.hostname >/dev/null 2>&1; then
|
||||
sysctl -w "kernel.hostname=$1" >/dev/null
|
||||
else
|
||||
# May fail to set a blank hostname
|
||||
hostname "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
is_default_hostname()
|
||||
{
|
||||
case "$1" in
|
||||
""|"$hostname_default"|localhost|localhost.localdomain)
|
||||
return 0;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
need_hostname()
|
||||
{
|
||||
# Always load the hostname variable for future use
|
||||
hostname="$(_hostname)"
|
||||
is_default_hostname "$hostname" && return 0
|
||||
|
||||
case "$force_hostname" in
|
||||
[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|1) return 0;;
|
||||
esac
|
||||
|
||||
if [ -n "$old_fqdn" ]; then
|
||||
if ${hfqdn} || ! ${hshort}; then
|
||||
[ "$hostname" = "$old_fqdn" ]
|
||||
else
|
||||
[ "$hostname" = "${old_fqdn%%.*}" ]
|
||||
fi
|
||||
elif [ -n "$old_host_name" ]; then
|
||||
if ${hfqdn}; then
|
||||
if [ -n "$old_domain_name" ] &&
|
||||
[ "$old_host_name" = "${old_host_name#*.}" ]
|
||||
then
|
||||
[ "$hostname" = \
|
||||
"$old_host_name.$old_domain_name" ]
|
||||
else
|
||||
[ "$hostname" = "$old_host_name" ]
|
||||
fi
|
||||
elif ${hshort}; then
|
||||
[ "$hostname" = "${old_host_name%%.*}" ]
|
||||
else
|
||||
[ "$hostname" = "$old_host_name" ]
|
||||
fi
|
||||
else
|
||||
# No old hostname
|
||||
false
|
||||
fi
|
||||
}
|
||||
|
||||
try_hostname()
|
||||
{
|
||||
[ "$hostname" = "$1" ] && return 0
|
||||
if valid_domainname "$1"; then
|
||||
syslog info "Setting hostname: $1"
|
||||
_hostname "$1"
|
||||
else
|
||||
syslog err "Invalid hostname: $1"
|
||||
fi
|
||||
}
|
||||
|
||||
set_hostname()
|
||||
{
|
||||
hfqdn=false
|
||||
hshort=false
|
||||
case "$hostname_fqdn" in
|
||||
[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|1) hfqdn=true;;
|
||||
""|[Ss][Ee][Rr][Vv][Ee][Rr]) ;;
|
||||
*) hshort=true;;
|
||||
esac
|
||||
|
||||
need_hostname || return 0
|
||||
|
||||
if [ -n "$new_fqdn" ]; then
|
||||
if ${hfqdn} || ! ${hshort}; then
|
||||
try_hostname "$new_fqdn"
|
||||
else
|
||||
try_hostname "${new_fqdn%%.*}"
|
||||
fi
|
||||
elif [ -n "$new_host_name" ]; then
|
||||
if ${hfqdn}; then
|
||||
if [ -n "$new_domain_name" ] &&
|
||||
[ "$new_host_name" = "${new_host_name#*.}" ]
|
||||
then
|
||||
try_hostname "$new_host_name.$new_domain_name"
|
||||
else
|
||||
try_hostname "$new_host_name"
|
||||
fi
|
||||
elif ${hshort}; then
|
||||
try_hostname "${new_host_name%%.*}"
|
||||
else
|
||||
try_hostname "$new_host_name"
|
||||
fi
|
||||
elif ! is_default_hostname "$hostname"; then
|
||||
try_hostname "$hostname_default"
|
||||
fi
|
||||
}
|
||||
|
||||
# For ease of use, map DHCP6 names onto our DHCP4 names
|
||||
case "$reason" in
|
||||
BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)
|
||||
new_fqdn="$new_dhcp6_fqdn"
|
||||
old_fqdn="$old_dhcp6_fqdn"
|
||||
;;
|
||||
esac
|
||||
|
||||
if $if_configured && $if_up && [ "$reason" != ROUTERADVERT ]; then
|
||||
set_hostname
|
||||
fi
|
|
@ -0,0 +1,158 @@
|
|||
# Set the hostname from DHCP data if required
|
||||
|
||||
# A hostname can either be a short hostname or a FQDN.
|
||||
# hostname_fqdn=true
|
||||
# hostname_fqdn=false
|
||||
# hostname_fqdn=server
|
||||
|
||||
# A value of server means just what the server says, don't manipulate it.
|
||||
# This could lead to an inconsistent hostname on a DHCPv4 and DHCPv6 network
|
||||
# where the DHCPv4 hostname is short and the DHCPv6 has an FQDN.
|
||||
# DHCPv6 has no hostname option.
|
||||
# RFC4702 section 3.1 says FQDN should be prefered over hostname.
|
||||
#
|
||||
# As such, the default is hostname_fqdn=true so that a consistent hostname
|
||||
# is always assigned.
|
||||
: ${hostname_fqdn:=true}
|
||||
|
||||
# If we used to set the hostname, but relinquish control of it, we should
|
||||
# reset to the default value.
|
||||
: ${hostname_default=@DEFAULT_HOSTNAME@}
|
||||
|
||||
# Some systems don't have hostname(1)
|
||||
_hostname()
|
||||
{
|
||||
if [ -z "${1+x}" ]; then
|
||||
if [ -r /proc/sys/kernel/hostname ]; then
|
||||
read name </proc/sys/kernel/hostname && echo "$name"
|
||||
elif type hostname >/dev/null 2>/dev/null; then
|
||||
hostname
|
||||
elif sysctl kern.hostname >/dev/null 2>&1; then
|
||||
sysctl -n kern.hostname
|
||||
elif sysctl kernel.hostname >/dev/null 2>&1; then
|
||||
sysctl -n kernel.hostname
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
return $?
|
||||
fi
|
||||
|
||||
if [ -w /proc/sys/kernel/hostname ]; then
|
||||
echo "$1" >/proc/sys/kernel/hostname
|
||||
elif [ -n "$1" ] && type hostname >/dev/null 2>&1; then
|
||||
hostname "$1"
|
||||
elif sysctl kern.hostname >/dev/null 2>&1; then
|
||||
sysctl -w "kern.hostname=$1" >/dev/null
|
||||
elif sysctl kernel.hostname >/dev/null 2>&1; then
|
||||
sysctl -w "kernel.hostname=$1" >/dev/null
|
||||
else
|
||||
# May fail to set a blank hostname
|
||||
hostname "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
is_default_hostname()
|
||||
{
|
||||
case "$1" in
|
||||
""|"$hostname_default"|localhost|localhost.localdomain)
|
||||
return 0;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
need_hostname()
|
||||
{
|
||||
# Always load the hostname variable for future use
|
||||
hostname="$(_hostname)"
|
||||
is_default_hostname "$hostname" && return 0
|
||||
|
||||
case "$force_hostname" in
|
||||
[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|1) return 0;;
|
||||
esac
|
||||
|
||||
if [ -n "$old_fqdn" ]; then
|
||||
if ${hfqdn} || ! ${hshort}; then
|
||||
[ "$hostname" = "$old_fqdn" ]
|
||||
else
|
||||
[ "$hostname" = "${old_fqdn%%.*}" ]
|
||||
fi
|
||||
elif [ -n "$old_host_name" ]; then
|
||||
if ${hfqdn}; then
|
||||
if [ -n "$old_domain_name" ] &&
|
||||
[ "$old_host_name" = "${old_host_name#*.}" ]
|
||||
then
|
||||
[ "$hostname" = \
|
||||
"$old_host_name.$old_domain_name" ]
|
||||
else
|
||||
[ "$hostname" = "$old_host_name" ]
|
||||
fi
|
||||
elif ${hshort}; then
|
||||
[ "$hostname" = "${old_host_name%%.*}" ]
|
||||
else
|
||||
[ "$hostname" = "$old_host_name" ]
|
||||
fi
|
||||
else
|
||||
# No old hostname
|
||||
false
|
||||
fi
|
||||
}
|
||||
|
||||
try_hostname()
|
||||
{
|
||||
[ "$hostname" = "$1" ] && return 0
|
||||
if valid_domainname "$1"; then
|
||||
syslog info "Setting hostname: $1"
|
||||
_hostname "$1"
|
||||
else
|
||||
syslog err "Invalid hostname: $1"
|
||||
fi
|
||||
}
|
||||
|
||||
set_hostname()
|
||||
{
|
||||
hfqdn=false
|
||||
hshort=false
|
||||
case "$hostname_fqdn" in
|
||||
[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|1) hfqdn=true;;
|
||||
""|[Ss][Ee][Rr][Vv][Ee][Rr]) ;;
|
||||
*) hshort=true;;
|
||||
esac
|
||||
|
||||
need_hostname || return
|
||||
|
||||
if [ -n "$new_fqdn" ]; then
|
||||
if ${hfqdn} || ! ${hshort}; then
|
||||
try_hostname "$new_fqdn"
|
||||
else
|
||||
try_hostname "${new_fqdn%%.*}"
|
||||
fi
|
||||
elif [ -n "$new_host_name" ]; then
|
||||
if ${hfqdn}; then
|
||||
if [ -n "$new_domain_name" ] &&
|
||||
[ "$new_host_name" = "${new_host_name#*.}" ]
|
||||
then
|
||||
try_hostname "$new_host_name.$new_domain_name"
|
||||
else
|
||||
try_hostname "$new_host_name"
|
||||
fi
|
||||
elif ${hshort}; then
|
||||
try_hostname "${new_host_name%%.*}"
|
||||
else
|
||||
try_hostname "$new_host_name"
|
||||
fi
|
||||
elif ! is_default_hostname "$hostname"; then
|
||||
try_hostname "$hostname_default"
|
||||
fi
|
||||
}
|
||||
|
||||
# For ease of use, map DHCP6 names onto our DHCP4 names
|
||||
case "$reason" in
|
||||
BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)
|
||||
new_fqdn="$new_dhcp6_fqdn"
|
||||
old_fqdn="$old_dhcp6_fqdn"
|
||||
;;
|
||||
esac
|
||||
|
||||
if $if_configured && $if_up && [ "$reason" != ROUTERADVERT ]; then
|
||||
set_hostname
|
||||
fi
|
|
@ -0,0 +1,144 @@
|
|||
# Sample dhcpcd hook script for NTP
|
||||
# It will configure either one of NTP, OpenNTP or Chrony (in that order)
|
||||
# and will default to NTP if no default config is found.
|
||||
|
||||
# Like our resolv.conf hook script, we store a database of ntp.conf files
|
||||
# and merge into /etc/ntp.conf
|
||||
|
||||
# You can set the env var NTP_CONF to override the derived default on
|
||||
# systems with >1 NTP client installed.
|
||||
# Here is an example for OpenNTP
|
||||
# dhcpcd -e NTP_CONF=/usr/pkg/etc/ntpd.conf
|
||||
# or by adding this to /etc/dhcpcd.conf
|
||||
# env NTP_CONF=/usr/pkg/etc/ntpd.conf
|
||||
# or by adding this to /etc/dhcpcd.enter-hook
|
||||
# NTP_CONF=/usr/pkg/etc/ntpd.conf
|
||||
# To use Chrony instead, simply change ntpd.conf to chrony.conf in the
|
||||
# above examples.
|
||||
|
||||
: ${ntp_confs:=ntp.conf ntpd.conf chrony.conf}
|
||||
: ${ntp_conf_dirs=/etc /usr/pkg/etc /usr/local/etc}
|
||||
ntp_conf_dir="$state_dir/ntp.conf"
|
||||
|
||||
# If NTP_CONF is not set, work out a good default
|
||||
if [ -z "$NTP_CONF" ]; then
|
||||
for d in ${ntp_conf_dirs}; do
|
||||
for f in ${ntp_confs}; do
|
||||
if [ -e "$d/$f" ]; then
|
||||
NTP_CONF="$d/$f"
|
||||
break 2
|
||||
fi
|
||||
done
|
||||
done
|
||||
[ -e "$NTP_CONF" ] || NTP_CONF=/etc/ntp.conf
|
||||
fi
|
||||
|
||||
# Derive service name from configuration
|
||||
if [ -z "$ntp_service" ]; then
|
||||
case "$NTP_CONF" in
|
||||
*chrony.conf) ntp_service=chronyd;;
|
||||
*) ntp_service=ntpd;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Debian has a separate file for DHCP config to avoid stamping on
|
||||
# the master.
|
||||
if [ "$ntp_service" = ntpd ] && command -v invoke-rc.d >/dev/null 2>&1; then
|
||||
[ -e /var/lib/ntp ] || mkdir /var/lib/ntp
|
||||
: ${ntp_service:=ntp}
|
||||
: ${NTP_DHCP_CONF:=/var/lib/ntp/ntp.conf.dhcp}
|
||||
fi
|
||||
|
||||
: ${ntp_restart_cmd:=service_condcommand $ntp_service restart}
|
||||
|
||||
ntp_conf=${NTP_CONF}
|
||||
NL="
|
||||
"
|
||||
|
||||
build_ntp_conf()
|
||||
{
|
||||
cf="$state_dir/ntp.conf.$ifname"
|
||||
|
||||
# Build a list of interfaces
|
||||
interfaces=$(list_interfaces "$ntp_conf_dir")
|
||||
|
||||
header=
|
||||
servers=
|
||||
if [ -n "$interfaces" ]; then
|
||||
# Build the header
|
||||
for x in ${interfaces}; do
|
||||
header="$header${header:+, }$x"
|
||||
done
|
||||
|
||||
# Build a server list
|
||||
srvs=$(cd "$ntp_conf_dir";
|
||||
key_get_value "server " $interfaces)
|
||||
if [ -n "$srvs" ]; then
|
||||
for x in $(uniqify $srvs); do
|
||||
servers="${servers}server $x$NL"
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
# Merge our config into ntp.conf
|
||||
[ -e "$cf" ] && rm -f "$cf"
|
||||
[ -d "$ntp_conf_dir" ] || mkdir -p "$ntp_conf_dir"
|
||||
|
||||
if [ -n "$NTP_DHCP_CONF" ]; then
|
||||
[ -e "$ntp_conf" ] && cp "$ntp_conf" "$cf"
|
||||
ntp_conf="$NTP_DHCP_CONF"
|
||||
elif [ -e "$ntp_conf" ]; then
|
||||
remove_markers "$signature_base" "$signature_base_end" \
|
||||
"$ntp_conf" > "$cf"
|
||||
fi
|
||||
|
||||
if [ -n "$servers" ]; then
|
||||
echo "$signature_base${header:+ $from }$header" >> "$cf"
|
||||
printf %s "$servers" >> "$cf"
|
||||
echo "$signature_base_end${header:+ $from }$header" >> "$cf"
|
||||
else
|
||||
[ -e "$ntp_conf" ] && [ -e "$cf" ] || return
|
||||
fi
|
||||
|
||||
# If we changed anything, restart ntpd
|
||||
if change_file "$ntp_conf" "$cf"; then
|
||||
[ -n "$ntp_restart_cmd" ] && eval $ntp_restart_cmd
|
||||
fi
|
||||
}
|
||||
|
||||
add_ntp_conf()
|
||||
{
|
||||
cf="$ntp_conf_dir/$ifname"
|
||||
|
||||
[ -e "$cf" ] && rm "$cf"
|
||||
[ -d "$ntp_conf_dir" ] || mkdir -p "$ntp_conf_dir"
|
||||
if [ -n "$new_ntp_servers" ]; then
|
||||
for x in $(uniqify $new_ntp_servers); do
|
||||
echo "server $x" >> "$cf"
|
||||
done
|
||||
fi
|
||||
build_ntp_conf
|
||||
}
|
||||
|
||||
remove_ntp_conf()
|
||||
{
|
||||
if [ -e "$ntp_conf_dir/$ifname" ]; then
|
||||
rm "$ntp_conf_dir/$ifname"
|
||||
fi
|
||||
build_ntp_conf
|
||||
}
|
||||
|
||||
# For ease of use, map DHCP6 names onto our DHCP4 names
|
||||
case "$reason" in
|
||||
BOUND6|RENEW6|REBIND6|REBOOT6|INFORM6)
|
||||
new_ntp_servers="$new_dhcp6_sntp_servers $new_dhcp6_ntp_server_addr $new_dhcp6_ntp_server_fqdn"
|
||||
;;
|
||||
esac
|
||||
|
||||
if $if_configured; then
|
||||
if $if_up; then
|
||||
add_ntp_conf
|
||||
elif $if_down; then
|
||||
remove_ntp_conf
|
||||
fi
|
||||
fi
|
|
@ -0,0 +1,84 @@
|
|||
# Sample dhcpcd hook for ypbind
|
||||
# This script is only suitable for the BSD versions.
|
||||
|
||||
: ${ypbind_restart_cmd:=service_command ypbind restart}
|
||||
: ${ypbind_stop_cmd:=service_condcommand ypbind stop}
|
||||
ypbind_dir="$state_dir/ypbind"
|
||||
: ${ypdomain_dir:=/var/yp}
|
||||
: ${ypdomain_suffix:=.ypservers}
|
||||
|
||||
best_domain()
|
||||
{
|
||||
for i in "$ypbind_dir/$interface_order".*; do
|
||||
if [ -f "$i" ]; then
|
||||
cat "$i"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
make_yp_binding()
|
||||
{
|
||||
[ -d "$ypbind_dir" ] || mkdir -p "$ypbind_dir"
|
||||
echo "$new_nis_domain" >"$ypbind_dir/$ifname"
|
||||
|
||||
if [ -z "$ypdomain_dir" ]; then
|
||||
false
|
||||
else
|
||||
cf="$ypdomain_dir/$new_nis_domain$ypdomain_suffix"
|
||||
if [ -n "$new_nis_servers" ]; then
|
||||
ncf="$cf.$ifname"
|
||||
rm -f "$ncf"
|
||||
for x in $new_nis_servers; do
|
||||
echo "$x" >>"$ncf"
|
||||
done
|
||||
change_file "$cf" "$ncf"
|
||||
else
|
||||
[ -e "$cf" ] && rm "$cf"
|
||||
fi
|
||||
fi
|
||||
|
||||
nd="$(best_domain)"
|
||||
if [ $? = 0 ] && [ "$nd" != "$(domainname)" ]; then
|
||||
domainname "$nd"
|
||||
if [ -n "$ypbind_restart_cmd" ]; then
|
||||
eval $ypbind_restart_cmd
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
restore_yp_binding()
|
||||
{
|
||||
rm -f "$ypbind_dir/$ifname"
|
||||
nd="$(best_domain)"
|
||||
# We need to stop ypbind if there is no best domain
|
||||
# otherwise it will just stall as we cannot set domainname
|
||||
# to blank :/
|
||||
if [ -z "$nd" ]; then
|
||||
if [ -n "$ypbind_stop_cmd" ]; then
|
||||
eval $ypbind_stop_cmd
|
||||
fi
|
||||
elif [ "$nd" != "$(domainname)" ]; then
|
||||
domainname "$nd"
|
||||
if [ -n "$ypbind_restart_cmd" ]; then
|
||||
eval $ypbind_restart_cmd
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
if ! $if_configured; then
|
||||
;
|
||||
elif [ "$reason" = PREINIT ]; then
|
||||
rm -f "$ypbind_dir/$interface".*
|
||||
elif $if_up || $if_down; then
|
||||
if [ -n "$new_nis_domain" ]; then
|
||||
if valid_domainname "$new_nis_domain"; then
|
||||
make_yp_binding
|
||||
else
|
||||
syslog err "Invalid NIS domain name: $new_nis_domain"
|
||||
fi
|
||||
elif [ -n "$old_nis_domain" ]; then
|
||||
restore_yp_binding
|
||||
fi
|
||||
fi
|
|
@ -0,0 +1,85 @@
|
|||
# Sample dhcpcd hook for ypbind
|
||||
# This script is only suitable for the BSD versions.
|
||||
|
||||
: ${ypbind_restart_cmd:=service_command ypbind restart}
|
||||
: ${ypbind_stop_cmd:=service_condcommand ypbind stop}
|
||||
ypbind_dir="$state_dir/ypbind"
|
||||
: ${ypdomain_dir:=@YPDOMAIN_DIR@}
|
||||
: ${ypdomain_suffix:=@YPDOMAIN_SUFFIX@}
|
||||
|
||||
|
||||
best_domain()
|
||||
{
|
||||
for i in "$ypbind_dir/$interface_order".*; do
|
||||
if [ -f "$i" ]; then
|
||||
cat "$i"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
make_yp_binding()
|
||||
{
|
||||
[ -d "$ypbind_dir" ] || mkdir -p "$ypbind_dir"
|
||||
echo "$new_nis_domain" >"$ypbind_dir/$ifname"
|
||||
|
||||
if [ -z "$ypdomain_dir" ]; then
|
||||
false
|
||||
else
|
||||
cf="$ypdomain_dir/$new_nis_domain$ypdomain_suffix"
|
||||
if [ -n "$new_nis_servers" ]; then
|
||||
ncf="$cf.$ifname"
|
||||
rm -f "$ncf"
|
||||
for x in $new_nis_servers; do
|
||||
echo "$x" >>"$ncf"
|
||||
done
|
||||
change_file "$cf" "$ncf"
|
||||
else
|
||||
[ -e "$cf" ] && rm "$cf"
|
||||
fi
|
||||
fi
|
||||
|
||||
nd="$(best_domain)"
|
||||
if [ $? = 0 ] && [ "$nd" != "$(domainname)" ]; then
|
||||
domainname "$nd"
|
||||
if [ -n "$ypbind_restart_cmd" ]; then
|
||||
eval $ypbind_restart_cmd
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
restore_yp_binding()
|
||||
{
|
||||
rm -f "$ypbind_dir/$ifname"
|
||||
nd="$(best_domain)"
|
||||
# We need to stop ypbind if there is no best domain
|
||||
# otherwise it will just stall as we cannot set domainname
|
||||
# to blank :/
|
||||
if [ -z "$nd" ]; then
|
||||
if [ -n "$ypbind_stop_cmd" ]; then
|
||||
eval $ypbind_stop_cmd
|
||||
fi
|
||||
elif [ "$nd" != "$(domainname)" ]; then
|
||||
domainname "$nd"
|
||||
if [ -n "$ypbind_restart_cmd" ]; then
|
||||
eval $ypbind_restart_cmd
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
if ! $if_configured; then
|
||||
;
|
||||
elif [ "$reason" = PREINIT ]; then
|
||||
rm -f "$ypbind_dir/$interface".*
|
||||
elif $if_up || $if_down; then
|
||||
if [ -n "$new_nis_domain" ]; then
|
||||
if valid_domainname "$new_nis_domain"; then
|
||||
make_yp_binding
|
||||
else
|
||||
syslog err "Invalid NIS domain name: $new_nis_domain"
|
||||
fi
|
||||
elif [ -n "$old_nis_domain" ]; then
|
||||
restore_yp_binding
|
||||
fi
|
||||
fi
|
|
@ -0,0 +1,354 @@
|
|||
#!/bin/sh
|
||||
# dhcpcd client configuration script
|
||||
|
||||
# Handy variables and functions for our hooks to use
|
||||
ifname="$interface${protocol+.}$protocol"
|
||||
from=from
|
||||
signature_base="# Generated by dhcpcd"
|
||||
signature="$signature_base $from $ifname"
|
||||
signature_base_end="# End of dhcpcd"
|
||||
signature_end="$signature_base_end $from $ifname"
|
||||
state_dir=/var/run/dhcpcd/hook-state
|
||||
_detected_init=false
|
||||
|
||||
: ${if_up:=false}
|
||||
: ${if_down:=false}
|
||||
: ${syslog_debug:=false}
|
||||
|
||||
# Ensure that all arguments are unique
|
||||
uniqify()
|
||||
{
|
||||
result=
|
||||
for i do
|
||||
case " $result " in
|
||||
*" $i "*);;
|
||||
*) result="$result${result:+ }$i";;
|
||||
esac
|
||||
done
|
||||
echo "$result"
|
||||
}
|
||||
|
||||
# List interface config files in a directory.
|
||||
# If dhcpcd is running as a single instance then it will have a list of
|
||||
# interfaces in the preferred order.
|
||||
# Otherwise we just use what we have.
|
||||
list_interfaces()
|
||||
{
|
||||
ifaces=
|
||||
for i in $interface_order; do
|
||||
for x in "$1"/$i.*; do
|
||||
[ -f "$x" ] && ifaces="$ifaces${ifaces:+ }${x##*/}"
|
||||
done
|
||||
done
|
||||
for x in "$1"/*; do
|
||||
[ -f "$x" ] && ifaces="$ifaces${ifaces:+ }${x##*/}"
|
||||
done
|
||||
uniqify $ifaces
|
||||
}
|
||||
|
||||
# Trim function
|
||||
trim()
|
||||
{
|
||||
var="$*"
|
||||
var=${var#"${var%%[![:space:]]*}"}
|
||||
var=${var%"${var##*[![:space:]]}"}
|
||||
if [ -z "$var" ]; then
|
||||
# So it seems our shell doesn't support wctype(3) patterns
|
||||
# Fall back to sed
|
||||
var=$(echo "$*" | sed -e 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
||||
fi
|
||||
printf %s "$var"
|
||||
}
|
||||
|
||||
# We normally use sed to extract values using a key from a list of files
|
||||
# but sed may not always be available at the time.
|
||||
key_get_value()
|
||||
{
|
||||
key="$1"
|
||||
shift
|
||||
|
||||
if command -v sed >/dev/null 2>&1; then
|
||||
sed -n "s/^$key//p" $@
|
||||
else
|
||||
for x do
|
||||
while read line; do
|
||||
case "$line" in
|
||||
"$key"*) echo "${line##$key}";;
|
||||
esac
|
||||
done < "$x"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# We normally use sed to remove markers from a configuration file
|
||||
# but sed may not always be available at the time.
|
||||
remove_markers()
|
||||
{
|
||||
m1="$1"
|
||||
m2="$2"
|
||||
in_marker=0
|
||||
|
||||
shift; shift
|
||||
if command -v sed >/dev/null 2>&1; then
|
||||
sed "/^$m1/,/^$m2/d" $@
|
||||
else
|
||||
for x do
|
||||
while read line; do
|
||||
case "$line" in
|
||||
"$m1"*) in_marker=1;;
|
||||
"$m2"*) in_marker=0;;
|
||||
*) [ $in_marker = 0 ] && echo "$line";;
|
||||
esac
|
||||
done < "$x"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# Compare two files.
|
||||
comp_file()
|
||||
{
|
||||
[ -e "$1" ] && [ -e "$2" ] || return 1
|
||||
|
||||
if command -v cmp >/dev/null 2>&1; then
|
||||
cmp -s "$1" "$2"
|
||||
elif command -v diff >/dev/null 2>&1; then
|
||||
diff -q "$1" "$2" >/dev/null
|
||||
else
|
||||
# Hopefully we're only working on small text files ...
|
||||
[ "$(cat "$1")" = "$(cat "$2")" ]
|
||||
fi
|
||||
}
|
||||
|
||||
# Compare two files.
|
||||
# If different, replace first with second otherwise remove second.
|
||||
change_file()
|
||||
{
|
||||
if [ -e "$1" ]; then
|
||||
if comp_file "$1" "$2"; then
|
||||
rm -f "$2"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
cat "$2" > "$1"
|
||||
rm -f "$2"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Compare two files.
|
||||
# If different, copy or link depending on target type
|
||||
copy_file()
|
||||
{
|
||||
if [ -h "$2" ]; then
|
||||
[ "$(readlink "$2")" = "$1" ] && return 1
|
||||
ln -sf "$1" "$2"
|
||||
else
|
||||
comp_file "$1" "$2" && return 1
|
||||
cat "$1" >"$2"
|
||||
fi
|
||||
}
|
||||
|
||||
# Save a config file
|
||||
save_conf()
|
||||
{
|
||||
if [ -f "$1" ]; then
|
||||
rm -f "$1-pre.$interface"
|
||||
cat "$1" > "$1-pre.$interface"
|
||||
fi
|
||||
}
|
||||
|
||||
# Restore a config file
|
||||
restore_conf()
|
||||
{
|
||||
[ -f "$1-pre.$interface" ] || return 1
|
||||
cat "$1-pre.$interface" > "$1"
|
||||
rm -f "$1-pre.$interface"
|
||||
}
|
||||
|
||||
# Write a syslog entry
|
||||
syslog()
|
||||
{
|
||||
lvl="$1"
|
||||
|
||||
if [ "$lvl" = debug ]; then
|
||||
${syslog_debug} || return 0
|
||||
fi
|
||||
[ -n "$lvl" ] && shift
|
||||
[ -n "$*" ] || return 0
|
||||
case "$lvl" in
|
||||
err|error) echo "$interface: $*" >&2;;
|
||||
*) echo "$interface: $*";;
|
||||
esac
|
||||
if command -v logger >/dev/null 2>&1; then
|
||||
logger -i -p daemon."$lvl" -t dhcpcd-run-hooks "$interface: $*"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check for a valid name as per RFC952 and RFC1123 section 2.1
|
||||
valid_domainname()
|
||||
{
|
||||
name="$1"
|
||||
[ -z "$name" ] || [ ${#name} -gt 255 ] && return 1
|
||||
|
||||
while [ -n "$name" ]; do
|
||||
label="${name%%.*}"
|
||||
[ -z "$label" ] || [ ${#label} -gt 63 ] && return 1
|
||||
case "$label" in
|
||||
-*|_*|*-|*_) return 1;;
|
||||
*[![:alnum:]_-]*) return 1;;
|
||||
"$name") return 0;;
|
||||
esac
|
||||
name="${name#*.}"
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
valid_domainname_list()
|
||||
{
|
||||
for name do
|
||||
valid_domainname "$name" || return $?
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# With the advent of alternative init systems, it's possible to have
|
||||
# more than one installed. So we need to try to guess what one we're
|
||||
# using unless overridden by configure.
|
||||
detect_init()
|
||||
{
|
||||
_service_exists=""
|
||||
_service_cmd=""
|
||||
_service_status=""
|
||||
|
||||
[ -n "$_service_cmd" ] && return 0
|
||||
|
||||
if $_detected_init; then
|
||||
[ -n "$_service_cmd" ]
|
||||
return $?
|
||||
fi
|
||||
|
||||
# Detect the running init system.
|
||||
# As systemd and OpenRC can be installed on top of legacy init
|
||||
# systems we try to detect them first.
|
||||
status=""
|
||||
: ${status:=status}
|
||||
if [ -x /bin/systemctl ] && [ -S /run/systemd/private ]; then
|
||||
_service_exists="/bin/systemctl --quiet is-enabled \$1.service"
|
||||
_service_status="/bin/systemctl --quiet is-active \$1.service"
|
||||
_service_cmd="/bin/systemctl \$2 --no-block \$1.service"
|
||||
elif [ -x /usr/bin/systemctl ] && [ -S /run/systemd/private ]; then
|
||||
_service_exists="/usr/bin/systemctl --quiet is-enabled \$1.service"
|
||||
_service_status="/usr/bin/systemctl --quiet is-active \$1.service"
|
||||
_service_cmd="/usr/bin/systemctl \$2 --no-block \$1.service"
|
||||
elif [ -x /sbin/rc-service ] &&
|
||||
{ [ -s /libexec/rc/init.d/softlevel ] ||
|
||||
[ -s /run/openrc/softlevel ]; }
|
||||
then
|
||||
_service_exists="/sbin/rc-service -e \$1"
|
||||
_service_cmd="/sbin/rc-service \$1 -- -D \$2"
|
||||
elif [ -x /usr/sbin/invoke-rc.d ]; then
|
||||
_service_exists="/usr/sbin/invoke-rc.d --query --quiet \$1 start >/dev/null 2>&1 || [ \$? = 104 ]"
|
||||
_service_cmd="/usr/sbin/invoke-rc.d \$1 \$2"
|
||||
elif [ -x /sbin/service ]; then
|
||||
_service_exists="/sbin/service \$1 >/dev/null 2>&1"
|
||||
_service_cmd="/sbin/service \$1 \$2"
|
||||
elif [ -x /usr/sbin/service ]; then
|
||||
_service_exists="/usr/sbin/service \$1 $status >/dev/null 2>&1"
|
||||
_service_cmd="/usr/sbin/service \$1 \$2"
|
||||
elif [ -x /bin/sv ]; then
|
||||
_service_exists="/bin/sv status \$1 >/dev/null 2>&1"
|
||||
_service_cmd="/bin/sv \$2 \$1"
|
||||
elif [ -x /usr/bin/sv ]; then
|
||||
_service_exists="/usr/bin/sv status \$1 >/dev/null 2>&1"
|
||||
_service_cmd="/usr/bin/sv \$2 \$1"
|
||||
elif [ -e /etc/slackware-version ] && [ -d /etc/rc.d ]; then
|
||||
_service_exists="[ -x /etc/rc.d/rc.\$1 ]"
|
||||
_service_cmd="/etc/rc.d/rc.\$1 \$2"
|
||||
_service_status="/etc/rc.d/rc.\$1 status >/dev/null 2>&1"
|
||||
else
|
||||
for x in /etc/init.d/rc.d /etc/rc.d /etc/init.d; do
|
||||
if [ -d $x ]; then
|
||||
_service_exists="[ -x $x/\$1 ]"
|
||||
_service_cmd="$x/\$1 \$2"
|
||||
_service_status="$x/\$1 $status >/dev/null 2>&1"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ -e /etc/arch-release ]; then
|
||||
_service_status="[ -e /var/run/daemons/\$1 ]"
|
||||
elif [ "$x" = "/etc/rc.d" ] && [ -e /etc/rc.d/rc.subr ]; then
|
||||
_service_status="$x/\$1 check >/dev/null 2>&1"
|
||||
fi
|
||||
fi
|
||||
|
||||
_detected_init=true
|
||||
if [ -z "$_service_cmd" ]; then
|
||||
syslog err "could not detect a useable init system"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check a system service exists
|
||||
service_exists()
|
||||
{
|
||||
if [ -z "$_service_exists" ]; then
|
||||
detect_init || return 1
|
||||
fi
|
||||
eval $_service_exists
|
||||
}
|
||||
|
||||
# Send a command to a system service
|
||||
service_cmd()
|
||||
{
|
||||
if [ -z "$_service_cmd" ]; then
|
||||
detect_init || return 1
|
||||
fi
|
||||
eval $_service_cmd
|
||||
}
|
||||
|
||||
# Send a command to a system service if it is running
|
||||
service_status()
|
||||
{
|
||||
if [ -z "$_service_cmd" ]; then
|
||||
detect_init || return 1
|
||||
fi
|
||||
if [ -n "$_service_status" ]; then
|
||||
eval $_service_status
|
||||
else
|
||||
service_command $1 status >/dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
# Handy macros for our hooks
|
||||
service_command()
|
||||
{
|
||||
service_exists $1 && service_cmd $1 $2
|
||||
}
|
||||
service_condcommand()
|
||||
{
|
||||
service_exists $1 && service_status $1 && service_cmd $1 $2
|
||||
}
|
||||
|
||||
# We source each script into this one so that scripts run earlier can
|
||||
# remove variables from the environment so later scripts don't see them.
|
||||
# Thus, the user can create their dhcpcd.enter/exit-hook script to configure
|
||||
# /etc/resolv.conf how they want and stop the system scripts ever updating it.
|
||||
for hook in \
|
||||
/etc/dhcpcd.enter-hook \
|
||||
/libexec/dhcpcd-hooks/* \
|
||||
/etc/dhcpcd.exit-hook
|
||||
do
|
||||
case "$hook" in
|
||||
*/*~) continue;;
|
||||
esac
|
||||
for skip in $skip_hooks; do
|
||||
case "$hook" in
|
||||
*/"$skip") continue 2;;
|
||||
*/[0-9][0-9]"-$skip") continue 2;;
|
||||
*/[0-9][0-9]"-$skip.sh") continue 2;;
|
||||
esac
|
||||
done
|
||||
if [ -f "$hook" ]; then
|
||||
. "$hook"
|
||||
fi
|
||||
done
|
|
@ -0,0 +1,233 @@
|
|||
.\" Copyright (c) 2006-2023 Roy Marples
|
||||
.\" All rights reserved
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd August 31, 2022
|
||||
.Dt DHCPCD-RUN-HOOKS 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm dhcpcd-run-hooks
|
||||
.Nd DHCP client configuration script
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is used by
|
||||
.Xr dhcpcd 8
|
||||
to run any system and user defined hook scripts.
|
||||
System hook scripts are found in
|
||||
.Pa /libexec/dhcpcd-hooks
|
||||
and the user defined hooks are
|
||||
.Pa /etc/dhcpcd.enter-hook .
|
||||
and
|
||||
.Pa /etc/dhcpcd.exit-hook .
|
||||
The default install supplies hook scripts for configuring
|
||||
.Pa /etc/resolv.conf
|
||||
and the hostname.
|
||||
Your distribution may have included other hook scripts to say configure
|
||||
ntp or ypbind.
|
||||
A test hook is also supplied that simply echos the dhcp variables to the
|
||||
console from DISCOVER message.
|
||||
.Pp
|
||||
The hooks scripts are loaded into the current shell rather than executed
|
||||
in their own process.
|
||||
This allows each hook script, such as
|
||||
.Pa /etc/dhcpcd.enter-hook
|
||||
to customise environment variables or provide alternative functions to hooks
|
||||
further down the chain.
|
||||
As such, using the shell builtins
|
||||
.Ic exit ,
|
||||
.Ic exec
|
||||
or similar will cause
|
||||
.Nm
|
||||
to exit at that point.
|
||||
.Pp
|
||||
Each time
|
||||
.Nm
|
||||
is invoked,
|
||||
.Ev $interface
|
||||
is set to the interface that
|
||||
.Nm dhcpcd
|
||||
is run on and
|
||||
.Ev $reason
|
||||
is set to the reason why
|
||||
.Nm
|
||||
was invoked.
|
||||
DHCP information to be configured is held in variables starting with the word
|
||||
new_ and old DHCP information to be removed is held in variables starting with
|
||||
the word old_.
|
||||
.Nm dhcpcd
|
||||
can display the full list of variables it knows about by using the
|
||||
.Fl V , -variables
|
||||
argument.
|
||||
.Pp
|
||||
Here's a list of reasons why
|
||||
.Nm
|
||||
could be invoked:
|
||||
.Bl -tag -width EXPIREXXXEXPIRE6
|
||||
.It Dv PREINIT
|
||||
dhcpcd is starting up and any pre-initialisation required should be performed now.
|
||||
.It Dv CARRIER
|
||||
dhcpcd has detected the carrier is up.
|
||||
This is generally just a notification and no action need be taken.
|
||||
.It Dv NOCARRIER
|
||||
dhcpcd lost the carrier.
|
||||
The cable may have been unplugged or association to the wireless point lost.
|
||||
.It Dv NOCARRIER_ROAMING
|
||||
dhcpcd lost the carrier but the interface configuration is persisted.
|
||||
The OS has to support wireless roaming or IP Persistence for this to happen.
|
||||
.It Dv INFORM | Dv INFORM6
|
||||
dhcpcd informed a DHCP server about its address and obtained other
|
||||
configuration details.
|
||||
.It Dv BOUND | Dv BOUND6
|
||||
dhcpcd obtained a new lease from a DHCP server.
|
||||
.It Dv RENEW | Dv RENEW6
|
||||
dhcpcd renewed its lease.
|
||||
.It Dv REBIND | Dv REBIND6
|
||||
dhcpcd has rebound to a new DHCP server.
|
||||
.It Dv REBOOT | Dv REBOOT6
|
||||
dhcpcd successfully requested a lease from a DHCP server.
|
||||
.It Dv DELEGATED6
|
||||
dhcpcd assigned a delegated prefix to the interface.
|
||||
.It Dv IPV4LL
|
||||
dhcpcd obtained an IPV4LL address, or one was removed.
|
||||
.It Dv STATIC
|
||||
dhcpcd has been configured with a static configuration which has not been
|
||||
obtained from a DHCP server.
|
||||
.It Dv 3RDPARTY
|
||||
dhcpcd is monitoring the interface for a 3rd party to give it an IP address.
|
||||
.It Dv TIMEOUT
|
||||
dhcpcd failed to contact any DHCP servers but was able to use an old lease.
|
||||
.It Dv EXPIRE | EXPIRE6
|
||||
dhcpcd's lease or state expired and it failed to obtain a new one.
|
||||
.It Dv NAK
|
||||
dhcpcd received a NAK from the DHCP server.
|
||||
This should be treated as EXPIRE.
|
||||
.It Dv RECONFIGURE
|
||||
dhcpcd has been instructed to reconfigure an interface.
|
||||
.It Dv ROUTERADVERT
|
||||
dhcpcd has received an IPv6 Router Advertisement, or one has expired.
|
||||
.It Dv STOP | Dv STOP6
|
||||
dhcpcd stopped running on the interface.
|
||||
.It Dv STOPPED
|
||||
dhcpcd has stopped entirely.
|
||||
.It Dv DEPARTED
|
||||
The interface has been removed.
|
||||
.It Dv FAIL
|
||||
dhcpcd failed to operate on the interface.
|
||||
This normally happens when dhcpcd does not support the raw interface, which
|
||||
means it cannot work as a DHCP or ZeroConf client.
|
||||
Static configuration and DHCP INFORM is still allowed.
|
||||
.It Dv TEST
|
||||
dhcpcd received an OFFER from a DHCP server but will not configure the
|
||||
interface.
|
||||
This is primarily used to test the variables are filled correctly for the
|
||||
script to process them.
|
||||
.El
|
||||
.Sh ENVIRONMENT
|
||||
.Nm dhcpcd
|
||||
will clear the environment variables aside from
|
||||
.Ev $PATH .
|
||||
The following variables will then be set, along with any protocol supplied
|
||||
ones.
|
||||
.Bl -tag -width xnew_delegated_dhcp6_prefix
|
||||
.It Ev $interface
|
||||
the name of the interface.
|
||||
.It Ev $protocol
|
||||
the protocol that triggered the event.
|
||||
.It Ev $reason
|
||||
as described above.
|
||||
.It Ev $pid
|
||||
the pid of
|
||||
.Nm dhcpcd .
|
||||
.It Ev $ifcarrier
|
||||
the link status of
|
||||
.Ev $interface :
|
||||
.Dv unknown ,
|
||||
.Dv up
|
||||
or
|
||||
.Dv down .
|
||||
.It Ev $ifmetric
|
||||
.Ev $interface
|
||||
preference, lower is better.
|
||||
.It Ev $ifwireless
|
||||
.Dv 1 if
|
||||
.Ev $interface
|
||||
is wireless, otherwise
|
||||
.Dv 0 .
|
||||
.It Ev $ifflags
|
||||
.Ev $interface
|
||||
flags.
|
||||
.It Ev $ifmtu
|
||||
.Ev $interface
|
||||
MTU.
|
||||
.It Ev $ifssid
|
||||
the SSID the
|
||||
.Ev interface
|
||||
is connected to.
|
||||
.It Ev $interface_order
|
||||
A list of interfaces, in order of preference.
|
||||
.It Ev $if_up
|
||||
.Dv true
|
||||
if the
|
||||
.Ev interface
|
||||
is up, otherwise
|
||||
.Dv false .
|
||||
This is more than IFF_UP and may not be equal.
|
||||
.It Ev $if_down
|
||||
.Dv true
|
||||
if the
|
||||
.Ev interface
|
||||
is down, otherwise
|
||||
.Dv false .
|
||||
This is more than IFF_UP and may not be equal.
|
||||
.It Ev $af_waiting
|
||||
Address family waiting for, as defined in
|
||||
.Xr dhcpcd.conf 5 .
|
||||
.It Ev $profile
|
||||
the name of the profile selected from
|
||||
.Xr dhcpcd.conf 5 .
|
||||
.It Ev $new_delegated_dhcp6_prefix
|
||||
space-separated list of delegated prefixes.
|
||||
.El
|
||||
.Sh FILES
|
||||
When
|
||||
.Nm
|
||||
runs, it loads
|
||||
.Pa /etc/dhcpcd.enter-hook ,
|
||||
any scripts found in
|
||||
.Pa /libexec/dhcpcd-hooks
|
||||
in lexical order, then finally
|
||||
.Pa /etc/dhcpcd.exit-hook .
|
||||
.Sh SEE ALSO
|
||||
.Xr dhcpcd 8
|
||||
.Sh AUTHORS
|
||||
.An Roy Marples Aq Mt roy@marples.name
|
||||
.Sh BUGS
|
||||
Please report them to
|
||||
.Lk https://roy.marples.name/projects/dhcpcd
|
||||
.Sh SECURITY CONSIDERATIONS
|
||||
.Nm dhcpcd
|
||||
will validate the content of each option against its encoding.
|
||||
For string, ascii, raw or binhex encoding it's up to the user to validate it
|
||||
for the intended purpose.
|
||||
.Pp
|
||||
When used in a shell script, each variable must be quoted correctly.
|
|
@ -0,0 +1,234 @@
|
|||
.\" Copyright (c) 2006-2020 Roy Marples
|
||||
.\" All rights reserved
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd December 27, 2020
|
||||
.Dt DHCPCD-RUN-HOOKS 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm dhcpcd-run-hooks
|
||||
.Nd DHCP client configuration script
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is used by
|
||||
.Xr dhcpcd 8
|
||||
to run any system and user defined hook scripts.
|
||||
System hook scripts are found in
|
||||
.Pa @HOOKDIR@
|
||||
and the user defined hooks are
|
||||
.Pa @SYSCONFDIR@/dhcpcd.enter-hook .
|
||||
and
|
||||
.Pa @SYSCONFDIR@/dhcpcd.exit-hook .
|
||||
The default install supplies hook scripts for configuring
|
||||
.Pa /etc/resolv.conf
|
||||
and the hostname.
|
||||
Your distribution may have included other hook scripts to say configure
|
||||
ntp or ypbind.
|
||||
A test hook is also supplied that simply echos the dhcp variables to the
|
||||
console from DISCOVER message.
|
||||
.Pp
|
||||
The hooks scripts are loaded into the current shell rather than executed
|
||||
in their own process.
|
||||
This allows each hook script, such as
|
||||
.Pa @SYSCONFDIR@/dhcpcd.enter-hook
|
||||
to customise environment variables or provide alternative functions to hooks
|
||||
further down the chain.
|
||||
As such, using the shell builtins
|
||||
.Ic exit ,
|
||||
.Ic exec
|
||||
or similar will cause
|
||||
.Nm
|
||||
to exit at that point.
|
||||
.Pp
|
||||
Each time
|
||||
.Nm
|
||||
is invoked,
|
||||
.Ev $interface
|
||||
is set to the interface that
|
||||
.Nm dhcpcd
|
||||
is run on and
|
||||
.Ev $reason
|
||||
is to the reason why
|
||||
q
|
||||
.Nm
|
||||
was invoked.
|
||||
DHCP information to be configured is held in variables starting with the word
|
||||
new_ and old DHCP information to be removed is held in variables starting with
|
||||
the word old_.
|
||||
.Nm dhcpcd
|
||||
can display the full list of variables it knows how about by using the
|
||||
.Fl V , -variables
|
||||
argument.
|
||||
.Pp
|
||||
Here's a list of reasons why
|
||||
.Nm
|
||||
could be invoked:
|
||||
.Bl -tag -width EXPIREXXXEXPIRE6
|
||||
.It Dv PREINIT
|
||||
dhcpcd is starting up and any pre-initialisation should be done.
|
||||
.It Dv CARRIER
|
||||
dhcpcd has detected the carrier is up.
|
||||
This is generally just a notification and no action need be taken.
|
||||
.It Dv NOCARRIER
|
||||
dhcpcd lost the carrier.
|
||||
The cable may have been unplugged or association to the wireless point lost.
|
||||
.It Dv NOCARRIER_ROAMING
|
||||
dhcpcd lost the carrier but the interface configuration is persisted.
|
||||
The OS has to support wireless roaming or IP Persistance for this to happen.
|
||||
.It Dv INFORM | Dv INFORM6
|
||||
dhcpcd informed a DHCP server about its address and obtained other
|
||||
configuration details.
|
||||
.It Dv BOUND | Dv BOUND6
|
||||
dhcpcd obtained a new lease from a DHCP server.
|
||||
.It Dv RENEW | Dv RENEW6
|
||||
dhcpcd renewed it's lease.
|
||||
.It Dv REBIND | Dv REBIND6
|
||||
dhcpcd has rebound to a new DHCP server.
|
||||
.It Dv REBOOT | Dv REBOOT6
|
||||
dhcpcd successfully requested a lease from a DHCP server.
|
||||
.It Dv DELEGATED6
|
||||
dhcpcd assigned a delegated prefix to the interface.
|
||||
.It Dv IPV4LL
|
||||
dhcpcd obtained an IPV4LL address, or one was removed.
|
||||
.It Dv STATIC
|
||||
dhcpcd has been configured with a static configuration which has not been
|
||||
obtained from a DHCP server.
|
||||
.It Dv 3RDPARTY
|
||||
dhcpcd is monitoring the interface for a 3rd party to give it an IP address.
|
||||
.It Dv TIMEOUT
|
||||
dhcpcd failed to contact any DHCP servers but was able to use an old lease.
|
||||
.It Dv EXPIRE | EXPIRE6
|
||||
dhcpcd's lease or state expired and it failed to obtain a new one.
|
||||
.It Dv NAK
|
||||
dhcpcd received a NAK from the DHCP server.
|
||||
This should be treated as EXPIRE.
|
||||
.It Dv RECONFIGURE
|
||||
dhcpcd has been instructed to reconfigure an interface.
|
||||
.It Dv ROUTERADVERT
|
||||
dhcpcd has received an IPv6 Router Advertisement, or one has expired.
|
||||
.It Dv STOP | Dv STOP6
|
||||
dhcpcd stopped running on the interface.
|
||||
.It Dv STOPPED
|
||||
dhcpcd has stopped entirely.
|
||||
.It Dv DEPARTED
|
||||
The interface has been removed.
|
||||
.It Dv FAIL
|
||||
dhcpcd failed to operate on the interface.
|
||||
This normally happens when dhcpcd does not support the raw interface, which
|
||||
means it cannot work as a DHCP or ZeroConf client.
|
||||
Static configuration and DHCP INFORM is still allowed.
|
||||
.It Dv TEST
|
||||
dhcpcd received an OFFER from a DHCP server but will not configure the
|
||||
interface.
|
||||
This is primarily used to test the variables are filled correctly for the
|
||||
script to process them.
|
||||
.El
|
||||
.Sh ENVIRONMENT
|
||||
.Nm dhcpcd
|
||||
will clear the environment variables aside from
|
||||
.Ev $PATH .
|
||||
The following variables will then be set, along with any protocol supplied
|
||||
ones.
|
||||
.Bl -tag -width xnew_delegated_dhcp6_prefix
|
||||
.It Ev $interface
|
||||
the name of the interface.
|
||||
.It Ev $protocol
|
||||
the protocol that triggered the event.
|
||||
.It Ev $reason
|
||||
as described above.
|
||||
.It Ev $pid
|
||||
the pid of
|
||||
.Nm dhcpcd .
|
||||
.It Ev $ifcarrier
|
||||
the link status of
|
||||
.Ev $interface :
|
||||
.Dv unknown ,
|
||||
.Dv up
|
||||
or
|
||||
.Dv down .
|
||||
.It Ev $ifmetric
|
||||
.Ev $interface
|
||||
preference, lower is better.
|
||||
.It Ev $ifwireless
|
||||
.Dv 1 if
|
||||
.Ev $interface
|
||||
is wireless, otherwise
|
||||
.Dv 0 .
|
||||
.It Ev $ifflags
|
||||
.Ev $interface
|
||||
flags.
|
||||
.It Ev $ifmtu
|
||||
.Ev $interface
|
||||
MTU.
|
||||
.It Ev $ifssid
|
||||
the name of the SSID the
|
||||
.Ev interface
|
||||
is connected to.
|
||||
.It Ev $interface_order
|
||||
A list of interfaces, in order of preference.
|
||||
.It Ev $if_up
|
||||
.Dv true
|
||||
if the
|
||||
.Ev interface
|
||||
is up, otherwise
|
||||
.Dv false .
|
||||
This is more than IFF_UP and may not be equal.
|
||||
.It Ev $if_down
|
||||
.Dv true
|
||||
if the
|
||||
.Ev interface
|
||||
is down, otherwise
|
||||
.Dv false .
|
||||
This is more than IFF_UP and may not be equal.
|
||||
.It Ev $af_waiting
|
||||
Address family waiting for, as defined in
|
||||
.Xr dhcpcd.conf 5 .
|
||||
.It Ev $profile
|
||||
the name of the profile selected from
|
||||
.Xr dhcpcd.conf 5 .
|
||||
.It Ev $new_delegated_dhcp6_prefix
|
||||
space separated list of delegated prefixes.
|
||||
.El
|
||||
.Sh FILES
|
||||
When
|
||||
.Nm
|
||||
runs, it loads
|
||||
.Pa @SYSCONFDIR@/dhcpcd.enter-hook
|
||||
and any scripts found in
|
||||
.Pa @HOOKDIR@
|
||||
in a lexical order and then finally
|
||||
.Pa @SYSCONFDIR@/dhcpcd.exit-hook
|
||||
.Sh SEE ALSO
|
||||
.Xr dhcpcd 8
|
||||
.Sh AUTHORS
|
||||
.An Roy Marples Aq Mt roy@marples.name
|
||||
.Sh BUGS
|
||||
Please report them to
|
||||
.Lk http://roy.marples.name/projects/dhcpcd
|
||||
.Sh SECURITY CONSIDERATIONS
|
||||
.Nm dhcpcd
|
||||
will validate the content of each option against its encoding.
|
||||
For string, ascii, raw or binhex encoding it's up to the user to validate it
|
||||
for the intended purpose.
|
||||
.Pp
|
||||
When used in a shell script, each variable must be quoted correctly.
|
|
@ -0,0 +1,352 @@
|
|||
#!/bin/sh
|
||||
# dhcpcd client configuration script
|
||||
|
||||
# Handy variables and functions for our hooks to use
|
||||
ifname="$interface${protocol+.}$protocol"
|
||||
from=from
|
||||
signature_base="# Generated by dhcpcd"
|
||||
signature="$signature_base $from $ifname"
|
||||
signature_base_end="# End of dhcpcd"
|
||||
signature_end="$signature_base_end $from $ifname"
|
||||
state_dir=@RUNDIR@/hook-state
|
||||
_detected_init=false
|
||||
|
||||
: ${if_up:=false}
|
||||
: ${if_down:=false}
|
||||
: ${syslog_debug:=false}
|
||||
|
||||
# Ensure that all arguments are unique
|
||||
uniqify()
|
||||
{
|
||||
result=
|
||||
for i do
|
||||
case " $result " in
|
||||
*" $i "*);;
|
||||
*) result="$result${result:+ }$i";;
|
||||
esac
|
||||
done
|
||||
echo "$result"
|
||||
}
|
||||
|
||||
# List interface config files in a directory.
|
||||
# If dhcpcd is running as a single instance then it will have a list of
|
||||
# interfaces in the preferred order.
|
||||
# Otherwise we just use what we have.
|
||||
list_interfaces()
|
||||
{
|
||||
ifaces=
|
||||
for i in $interface_order; do
|
||||
for x in "$1"/$i.*; do
|
||||
[ -f "$x" ] && ifaces="$ifaces${ifaces:+ }${x##*/}"
|
||||
done
|
||||
done
|
||||
for x in "$1"/*; do
|
||||
[ -f "$x" ] && ifaces="$ifaces${ifaces:+ }${x##*/}"
|
||||
done
|
||||
uniqify $ifaces
|
||||
}
|
||||
|
||||
# Trim function
|
||||
trim()
|
||||
{
|
||||
var="$*"
|
||||
var=${var#"${var%%[![:space:]]*}"}
|
||||
var=${var%"${var##*[![:space:]]}"}
|
||||
if [ -z "$var" ]; then
|
||||
# So it seems our shell doesn't support wctype(3) patterns
|
||||
# Fall back to sed
|
||||
var=$(echo "$*" | sed -e 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
||||
fi
|
||||
printf %s "$var"
|
||||
}
|
||||
|
||||
# We normally use sed to extract values using a key from a list of files
|
||||
# but sed may not always be available at the time.
|
||||
key_get_value()
|
||||
{
|
||||
key="$1"
|
||||
shift
|
||||
|
||||
if type sed >/dev/null 2>&1; then
|
||||
sed -n "s/^$key//p" $@
|
||||
else
|
||||
for x do
|
||||
while read line; do
|
||||
case "$line" in
|
||||
"$key"*) echo "${line##$key}";;
|
||||
esac
|
||||
done < "$x"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# We normally use sed to remove markers from a configuration file
|
||||
# but sed may not always be available at the time.
|
||||
remove_markers()
|
||||
{
|
||||
m1="$1"
|
||||
m2="$2"
|
||||
in_marker=0
|
||||
|
||||
shift; shift
|
||||
if type sed >/dev/null 2>&1; then
|
||||
sed "/^$m1/,/^$m2/d" $@
|
||||
else
|
||||
for x do
|
||||
while read line; do
|
||||
case "$line" in
|
||||
"$m1"*) in_marker=1;;
|
||||
"$m2"*) in_marker=0;;
|
||||
*) [ $in_marker = 0 ] && echo "$line";;
|
||||
esac
|
||||
done < "$x"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# Compare two files.
|
||||
comp_file()
|
||||
{
|
||||
[ -e "$1" ] && [ -e "$2" ] || return 1
|
||||
|
||||
if type cmp >/dev/null 2>&1; then
|
||||
cmp -s "$1" "$2"
|
||||
elif type diff >/dev/null 2>&1; then
|
||||
diff -q "$1" "$2" >/dev/null
|
||||
else
|
||||
# Hopefully we're only working on small text files ...
|
||||
[ "$(cat "$1")" = "$(cat "$2")" ]
|
||||
fi
|
||||
}
|
||||
|
||||
# Compare two files.
|
||||
# If different, replace first with second otherwise remove second.
|
||||
change_file()
|
||||
{
|
||||
if [ -e "$1" ]; then
|
||||
if comp_file "$1" "$2"; then
|
||||
rm -f "$2"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
cat "$2" > "$1"
|
||||
rm -f "$2"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Compare two files.
|
||||
# If different, copy or link depending on target type
|
||||
copy_file()
|
||||
{
|
||||
if [ -h "$2" ]; then
|
||||
[ "$(readlink "$2")" = "$1" ] && return 1
|
||||
ln -sf "$1" "$2"
|
||||
else
|
||||
comp_file "$1" "$2" && return 1
|
||||
cat "$1" >"$2"
|
||||
fi
|
||||
}
|
||||
|
||||
# Save a config file
|
||||
save_conf()
|
||||
{
|
||||
if [ -f "$1" ]; then
|
||||
rm -f "$1-pre.$interface"
|
||||
cat "$1" > "$1-pre.$interface"
|
||||
fi
|
||||
}
|
||||
|
||||
# Restore a config file
|
||||
restore_conf()
|
||||
{
|
||||
[ -f "$1-pre.$interface" ] || return 1
|
||||
cat "$1-pre.$interface" > "$1"
|
||||
rm -f "$1-pre.$interface"
|
||||
}
|
||||
|
||||
# Write a syslog entry
|
||||
syslog()
|
||||
{
|
||||
lvl="$1"
|
||||
|
||||
if [ "$lvl" = debug ]; then
|
||||
${syslog_debug} || return 0
|
||||
fi
|
||||
[ -n "$lvl" ] && shift
|
||||
[ -n "$*" ] || return 0
|
||||
case "$lvl" in
|
||||
err|error) echo "$interface: $*" >&2;;
|
||||
*) echo "$interface: $*";;
|
||||
esac
|
||||
if type logger >/dev/null 2>&1; then
|
||||
logger -i -p daemon."$lvl" -t dhcpcd-run-hooks "$interface: $*"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check for a valid name as per RFC952 and RFC1123 section 2.1
|
||||
valid_domainname()
|
||||
{
|
||||
name="$1"
|
||||
[ -z "$name" ] || [ ${#name} -gt 255 ] && return 1
|
||||
|
||||
while [ -n "$name" ]; do
|
||||
label="${name%%.*}"
|
||||
[ -z "$label" ] || [ ${#label} -gt 63 ] && return 1
|
||||
case "$label" in
|
||||
-*|_*|*-|*_) return 1;;
|
||||
*[![:alnum:]_-]*) return 1;;
|
||||
"$name") return 0;;
|
||||
esac
|
||||
name="${name#*.}"
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
valid_domainname_list()
|
||||
{
|
||||
for name do
|
||||
valid_domainname "$name" || return $?
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# With the advent of alternative init systems, it's possible to have
|
||||
# more than one installed. So we need to try to guess what one we're
|
||||
# using unless overridden by configure.
|
||||
detect_init()
|
||||
{
|
||||
_service_exists="@SERVICEEXISTS@"
|
||||
_service_cmd="@SERVICECMD@"
|
||||
_service_status="@SERVICESTATUS@"
|
||||
|
||||
[ -n "$_service_cmd" ] && return 0
|
||||
|
||||
if $_detected_init; then
|
||||
[ -n "$_service_cmd" ]
|
||||
return $?
|
||||
fi
|
||||
|
||||
# Detect the running init system.
|
||||
# As systemd and OpenRC can be installed on top of legacy init
|
||||
# systems we try to detect them first.
|
||||
status="@STATUSARG@"
|
||||
: ${status:=status}
|
||||
if [ -x /bin/systemctl ] && [ -S /run/systemd/private ]; then
|
||||
_service_exists="/bin/systemctl --quiet is-enabled \$1.service"
|
||||
_service_status="/bin/systemctl --quiet is-active \$1.service"
|
||||
_service_cmd="/bin/systemctl \$2 \$1.service"
|
||||
elif [ -x /usr/bin/systemctl ] && [ -S /run/systemd/private ]; then
|
||||
_service_exists="/usr/bin/systemctl --quiet is-enabled \$1.service"
|
||||
_service_status="/usr/bin/systemctl --quiet is-active \$1.service"
|
||||
_service_cmd="/usr/bin/systemctl \$2 \$1.service"
|
||||
elif [ -x /sbin/rc-service ] &&
|
||||
{ [ -s /libexec/rc/init.d/softlevel ] ||
|
||||
[ -s /run/openrc/softlevel ]; }
|
||||
then
|
||||
_service_exists="/sbin/rc-service -e \$1"
|
||||
_service_cmd="/sbin/rc-service \$1 -- -D \$2"
|
||||
elif [ -x /usr/sbin/invoke-rc.d ]; then
|
||||
_service_exists="/usr/sbin/invoke-rc.d --query --quiet \$1 start >/dev/null 2>&1 || [ \$? = 104 ]"
|
||||
_service_cmd="/usr/sbin/invoke-rc.d \$1 \$2"
|
||||
elif [ -x /sbin/service ]; then
|
||||
_service_exists="/sbin/service \$1 >/dev/null 2>&1"
|
||||
_service_cmd="/sbin/service \$1 \$2"
|
||||
elif [ -x /usr/sbin/service ]; then
|
||||
_service_exists="/usr/sbin/service \$1 $status >/dev/null 2>&1"
|
||||
_service_cmd="/usr/sbin/service \$1 \$2"
|
||||
elif [ -x /bin/sv ]; then
|
||||
_service_exists="/bin/sv status \$1 >/dev/null 2>&1"
|
||||
_service_cmd="/bin/sv \$2 \$1"
|
||||
elif [ -x /usr/bin/sv ]; then
|
||||
_service_exists="/usr/bin/sv status \$1 >/dev/null 2>&1"
|
||||
_service_cmd="/usr/bin/sv \$2 \$1"
|
||||
elif [ -e /etc/slackware-version ] && [ -d /etc/rc.d ]; then
|
||||
_service_exists="[ -x /etc/rc.d/rc.\$1 ]"
|
||||
_service_cmd="/etc/rc.d/rc.\$1 \$2"
|
||||
_service_status="/etc/rc.d/rc.\$1 status >/dev/null 2>&1"
|
||||
else
|
||||
for x in /etc/init.d/rc.d /etc/rc.d /etc/init.d; do
|
||||
if [ -d $x ]; then
|
||||
_service_exists="[ -x $x/\$1 ]"
|
||||
_service_cmd="$x/\$1 \$2"
|
||||
_service_status="$x/\$1 $status >/dev/null 2>&1"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ -e /etc/arch-release ]; then
|
||||
_service_status="[ -e /var/run/daemons/\$1 ]"
|
||||
elif [ "$x" = "/etc/rc.d" ] && [ -e /etc/rc.d/rc.subr ]; then
|
||||
_service_status="$x/\$1 check >/dev/null 2>&1"
|
||||
fi
|
||||
fi
|
||||
|
||||
_detected_init=true
|
||||
if [ -z "$_service_cmd" ]; then
|
||||
syslog err "could not detect a useable init system"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check a system service exists
|
||||
service_exists()
|
||||
{
|
||||
if [ -z "$_service_exists" ]; then
|
||||
detect_init || return 1
|
||||
fi
|
||||
eval $_service_exists
|
||||
}
|
||||
|
||||
# Send a command to a system service
|
||||
service_cmd()
|
||||
{
|
||||
if [ -z "$_service_cmd" ]; then
|
||||
detect_init || return 1
|
||||
fi
|
||||
eval $_service_cmd
|
||||
}
|
||||
|
||||
# Send a command to a system service if it is running
|
||||
service_status()
|
||||
{
|
||||
if [ -z "$_service_cmd" ]; then
|
||||
detect_init || return 1
|
||||
fi
|
||||
if [ -n "$_service_status" ]; then
|
||||
eval $_service_status
|
||||
else
|
||||
service_command $1 status >/dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
# Handy macros for our hooks
|
||||
service_command()
|
||||
{
|
||||
service_exists $1 && service_cmd $1 $2
|
||||
}
|
||||
service_condcommand()
|
||||
{
|
||||
service_exists $1 && service_status $1 && service_cmd $1 $2
|
||||
}
|
||||
|
||||
# We source each script into this one so that scripts run earlier can
|
||||
# remove variables from the environment so later scripts don't see them.
|
||||
# Thus, the user can create their dhcpcd.enter/exit-hook script to configure
|
||||
# /etc/resolv.conf how they want and stop the system scripts ever updating it.
|
||||
for hook in \
|
||||
@SYSCONFDIR@/dhcpcd.enter-hook \
|
||||
@HOOKDIR@/* \
|
||||
@SYSCONFDIR@/dhcpcd.exit-hook
|
||||
do
|
||||
for skip in $skip_hooks; do
|
||||
case "$hook" in
|
||||
*/*~) continue 2;;
|
||||
*/"$skip") continue 2;;
|
||||
*/[0-9][0-9]"-$skip") continue 2;;
|
||||
*/[0-9][0-9]"-$skip.sh") continue 2;;
|
||||
esac
|
||||
done
|
||||
if [ -f "$hook" ]; then
|
||||
. "$hook"
|
||||
fi
|
||||
done
|
|
@ -0,0 +1,670 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - ARP handler
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/if_ether.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define ELOOP_QUEUE ELOOP_ARP
|
||||
#include "config.h"
|
||||
#include "arp.h"
|
||||
#include "bpf.h"
|
||||
#include "ipv4.h"
|
||||
#include "common.h"
|
||||
#include "dhcpcd.h"
|
||||
#include "eloop.h"
|
||||
#include "if.h"
|
||||
#include "if-options.h"
|
||||
#include "ipv4ll.h"
|
||||
#include "logerr.h"
|
||||
#include "privsep.h"
|
||||
|
||||
#if defined(ARP)
|
||||
#define ARP_LEN \
|
||||
(FRAMEHDRLEN_MAX + \
|
||||
sizeof(struct arphdr) + (2 * sizeof(uint32_t)) + (2 * HWADDR_LEN))
|
||||
|
||||
/* ARP debugging can be quite noisy. Enable this for more noise! */
|
||||
//#define ARP_DEBUG
|
||||
|
||||
/* Assert the correct structure size for on wire */
|
||||
__CTASSERT(sizeof(struct arphdr) == 8);
|
||||
|
||||
static ssize_t
|
||||
arp_request(const struct arp_state *astate,
|
||||
const struct in_addr *sip)
|
||||
{
|
||||
const struct interface *ifp = astate->iface;
|
||||
const struct in_addr *tip = &astate->addr;
|
||||
uint8_t arp_buffer[ARP_LEN];
|
||||
struct arphdr ar;
|
||||
size_t len;
|
||||
uint8_t *p;
|
||||
|
||||
ar.ar_hrd = htons(ifp->hwtype);
|
||||
ar.ar_pro = htons(ETHERTYPE_IP);
|
||||
ar.ar_hln = ifp->hwlen;
|
||||
ar.ar_pln = sizeof(tip->s_addr);
|
||||
ar.ar_op = htons(ARPOP_REQUEST);
|
||||
|
||||
p = arp_buffer;
|
||||
len = 0;
|
||||
|
||||
#define CHECK(fun, b, l) \
|
||||
do { \
|
||||
if (len + (l) > sizeof(arp_buffer)) \
|
||||
goto eexit; \
|
||||
fun(p, (b), (l)); \
|
||||
p += (l); \
|
||||
len += (l); \
|
||||
} while (/* CONSTCOND */ 0)
|
||||
#define APPEND(b, l) CHECK(memcpy, b, l)
|
||||
#define ZERO(l) CHECK(memset, 0, l)
|
||||
|
||||
APPEND(&ar, sizeof(ar));
|
||||
APPEND(ifp->hwaddr, ifp->hwlen);
|
||||
if (sip != NULL)
|
||||
APPEND(&sip->s_addr, sizeof(sip->s_addr));
|
||||
else
|
||||
ZERO(sizeof(tip->s_addr));
|
||||
ZERO(ifp->hwlen);
|
||||
APPEND(&tip->s_addr, sizeof(tip->s_addr));
|
||||
|
||||
#ifdef PRIVSEP
|
||||
if (ifp->ctx->options & DHCPCD_PRIVSEP)
|
||||
return ps_bpf_sendarp(ifp, tip, arp_buffer, len);
|
||||
#endif
|
||||
/* Note that well formed ethernet will add extra padding
|
||||
* to ensure that the packet is at least 60 bytes (64 including FCS). */
|
||||
return bpf_send(astate->bpf, ETHERTYPE_ARP, arp_buffer, len);
|
||||
|
||||
eexit:
|
||||
errno = ENOBUFS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
arp_report_conflicted(const struct arp_state *astate,
|
||||
const struct arp_msg *amsg)
|
||||
{
|
||||
char abuf[HWADDR_LEN * 3];
|
||||
char fbuf[HWADDR_LEN * 3];
|
||||
|
||||
if (amsg == NULL) {
|
||||
logerrx("%s: DAD detected %s",
|
||||
astate->iface->name, inet_ntoa(astate->addr));
|
||||
return;
|
||||
}
|
||||
|
||||
hwaddr_ntoa(amsg->sha, astate->iface->hwlen, abuf, sizeof(abuf));
|
||||
if (bpf_frame_header_len(astate->iface) == 0) {
|
||||
logwarnx("%s: %s claims %s",
|
||||
astate->iface->name, abuf, inet_ntoa(astate->addr));
|
||||
return;
|
||||
}
|
||||
|
||||
logwarnx("%s: %s(%s) claims %s",
|
||||
astate->iface->name, abuf,
|
||||
hwaddr_ntoa(amsg->fsha, astate->iface->hwlen, fbuf, sizeof(fbuf)),
|
||||
inet_ntoa(astate->addr));
|
||||
}
|
||||
|
||||
static void
|
||||
arp_found(struct arp_state *astate, const struct arp_msg *amsg)
|
||||
{
|
||||
struct interface *ifp;
|
||||
struct ipv4_addr *ia;
|
||||
#ifndef KERNEL_RFC5227
|
||||
struct timespec now;
|
||||
#endif
|
||||
|
||||
arp_report_conflicted(astate, amsg);
|
||||
ifp = astate->iface;
|
||||
|
||||
/* If we haven't added the address we're doing a probe. */
|
||||
ia = ipv4_iffindaddr(ifp, &astate->addr, NULL);
|
||||
if (ia == NULL) {
|
||||
if (astate->found_cb != NULL)
|
||||
astate->found_cb(astate, amsg);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef KERNEL_RFC5227
|
||||
/* RFC 3927 Section 2.5 says a defence should
|
||||
* broadcast an ARP announcement.
|
||||
* Because the kernel will also unicast a reply to the
|
||||
* hardware address which requested the IP address
|
||||
* the other IPv4LL client will receieve two ARP
|
||||
* messages.
|
||||
* If another conflict happens within DEFEND_INTERVAL
|
||||
* then we must drop our address and negotiate a new one.
|
||||
* If DHCPCD_ARP_PERSISTDEFENCE is set, that enables
|
||||
* RFC5227 section 2.4.c behaviour. Upon conflict
|
||||
* detection, the host records the time that the
|
||||
* conflicting ARP packet was received, and then
|
||||
* broadcasts one single ARP Announcement. The host then
|
||||
* continues to use the address normally. All further
|
||||
* conflict notifications within the DEFEND_INTERVAL are
|
||||
* ignored. */
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
if (timespecisset(&astate->defend) &&
|
||||
eloop_timespec_diff(&now, &astate->defend, NULL) < DEFEND_INTERVAL)
|
||||
{
|
||||
logwarnx("%s: %d second defence failed for %s",
|
||||
ifp->name, DEFEND_INTERVAL, inet_ntoa(astate->addr));
|
||||
if (ifp->options->options & DHCPCD_ARP_PERSISTDEFENCE)
|
||||
return;
|
||||
}
|
||||
else if (arp_request(astate, &astate->addr) == -1)
|
||||
logerr(__func__);
|
||||
else {
|
||||
logdebugx("%s: defended address %s",
|
||||
ifp->name, inet_ntoa(astate->addr));
|
||||
astate->defend = now;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (astate->defend_failed_cb != NULL)
|
||||
astate->defend_failed_cb(astate);
|
||||
}
|
||||
|
||||
static bool
|
||||
arp_validate(const struct interface *ifp, struct arphdr *arp)
|
||||
{
|
||||
|
||||
/* Address type must match */
|
||||
if (arp->ar_hrd != htons(ifp->hwtype))
|
||||
return false;
|
||||
|
||||
/* Protocol must be IP. */
|
||||
if (arp->ar_pro != htons(ETHERTYPE_IP))
|
||||
return false;
|
||||
|
||||
/* lladdr length matches */
|
||||
if (arp->ar_hln != ifp->hwlen)
|
||||
return false;
|
||||
|
||||
/* Protocol length must match in_addr_t */
|
||||
if (arp->ar_pln != sizeof(in_addr_t))
|
||||
return false;
|
||||
|
||||
/* Only these types are recognised */
|
||||
if (arp->ar_op != htons(ARPOP_REPLY) &&
|
||||
arp->ar_op != htons(ARPOP_REQUEST))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
arp_packet(struct interface *ifp, uint8_t *data, size_t len,
|
||||
unsigned int bpf_flags)
|
||||
{
|
||||
size_t fl = bpf_frame_header_len(ifp), falen;
|
||||
const struct interface *ifn;
|
||||
struct arphdr ar;
|
||||
struct arp_msg arm;
|
||||
const struct iarp_state *state;
|
||||
struct arp_state *astate, *astaten;
|
||||
uint8_t *hw_s, *hw_t;
|
||||
#ifndef KERNEL_RFC5227
|
||||
bool is_probe;
|
||||
#endif /* KERNEL_RFC5227 */
|
||||
|
||||
/* Copy the frame header source and destination out */
|
||||
memset(&arm, 0, sizeof(arm));
|
||||
if (fl != 0) {
|
||||
hw_s = bpf_frame_header_src(ifp, data, &falen);
|
||||
if (hw_s != NULL && falen <= sizeof(arm.fsha))
|
||||
memcpy(arm.fsha, hw_s, falen);
|
||||
hw_t = bpf_frame_header_dst(ifp, data, &falen);
|
||||
if (hw_t != NULL && falen <= sizeof(arm.ftha))
|
||||
memcpy(arm.ftha, hw_t, falen);
|
||||
|
||||
/* Skip past the frame header */
|
||||
data += fl;
|
||||
len -= fl;
|
||||
}
|
||||
|
||||
/* We must have a full ARP header */
|
||||
if (len < sizeof(ar))
|
||||
return;
|
||||
memcpy(&ar, data, sizeof(ar));
|
||||
|
||||
if (!arp_validate(ifp, &ar)) {
|
||||
#ifdef BPF_DEBUG
|
||||
logerrx("%s: ARP BPF validation failure", ifp->name);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get pointers to the hardware addresses */
|
||||
hw_s = data + sizeof(ar);
|
||||
hw_t = hw_s + ar.ar_hln + ar.ar_pln;
|
||||
/* Ensure we got all the data */
|
||||
if ((size_t)((hw_t + ar.ar_hln + ar.ar_pln) - data) > len)
|
||||
return;
|
||||
/* Ignore messages from ourself */
|
||||
TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) {
|
||||
if (ar.ar_hln == ifn->hwlen &&
|
||||
memcmp(hw_s, ifn->hwaddr, ifn->hwlen) == 0)
|
||||
break;
|
||||
}
|
||||
if (ifn) {
|
||||
#ifdef ARP_DEBUG
|
||||
logdebugx("%s: ignoring ARP from self", ifp->name);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
/* Copy out the HW and IP addresses */
|
||||
memcpy(&arm.sha, hw_s, ar.ar_hln);
|
||||
memcpy(&arm.sip.s_addr, hw_s + ar.ar_hln, ar.ar_pln);
|
||||
memcpy(&arm.tha, hw_t, ar.ar_hln);
|
||||
memcpy(&arm.tip.s_addr, hw_t + ar.ar_hln, ar.ar_pln);
|
||||
|
||||
#ifndef KERNEL_RFC5227
|
||||
/* During ARP probe the 'sender hardware address' MUST contain the hardware
|
||||
* address of the interface sending the packet. RFC5227, 1.1 */
|
||||
is_probe = ar.ar_op == htons(ARPOP_REQUEST) && IN_IS_ADDR_UNSPECIFIED(&arm.sip) &&
|
||||
bpf_flags & BPF_BCAST;
|
||||
if (is_probe && falen > 0 && (falen != ar.ar_hln ||
|
||||
memcmp(&arm.sha, &arm.fsha, ar.ar_hln))) {
|
||||
char abuf[HWADDR_LEN * 3];
|
||||
char fbuf[HWADDR_LEN * 3];
|
||||
hwaddr_ntoa(&arm.sha, ar.ar_hln, abuf, sizeof(abuf));
|
||||
hwaddr_ntoa(&arm.fsha, falen, fbuf, sizeof(fbuf));
|
||||
logwarnx("%s: invalid ARP probe, sender hw address mismatch (%s, %s)",
|
||||
ifp->name, abuf, fbuf);
|
||||
return;
|
||||
}
|
||||
#endif /* KERNEL_RFC5227 */
|
||||
|
||||
/* Match the ARP probe to our states.
|
||||
* Ignore Unicast Poll, RFC1122. */
|
||||
state = ARP_CSTATE(ifp);
|
||||
if (state == NULL)
|
||||
return;
|
||||
TAILQ_FOREACH_SAFE(astate, &state->arp_states, next, astaten) {
|
||||
if (IN_ARE_ADDR_EQUAL(&arm.sip, &astate->addr) ||
|
||||
(IN_IS_ADDR_UNSPECIFIED(&arm.sip) &&
|
||||
IN_ARE_ADDR_EQUAL(&arm.tip, &astate->addr) &&
|
||||
bpf_flags & BPF_BCAST))
|
||||
arp_found(astate, &arm);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
arp_read(void *arg, unsigned short events)
|
||||
{
|
||||
struct arp_state *astate = arg;
|
||||
struct bpf *bpf = astate->bpf;
|
||||
struct interface *ifp = astate->iface;
|
||||
uint8_t buf[ARP_LEN];
|
||||
ssize_t bytes;
|
||||
struct in_addr addr = astate->addr;
|
||||
|
||||
if (events != ELE_READ)
|
||||
logerrx("%s: unexpected event 0x%04x", __func__, events);
|
||||
|
||||
/* Some RAW mechanisms are generic file descriptors, not sockets.
|
||||
* This means we have no kernel call to just get one packet,
|
||||
* so we have to process the entire buffer. */
|
||||
bpf->bpf_flags &= ~BPF_EOF;
|
||||
while (!(bpf->bpf_flags & BPF_EOF)) {
|
||||
bytes = bpf_read(bpf, buf, sizeof(buf));
|
||||
if (bytes == -1) {
|
||||
logerr("%s: %s", __func__, ifp->name);
|
||||
arp_free(astate);
|
||||
return;
|
||||
}
|
||||
arp_packet(ifp, buf, (size_t)bytes, bpf->bpf_flags);
|
||||
/* Check we still have a state after processing. */
|
||||
if ((astate = arp_find(ifp, &addr)) == NULL)
|
||||
break;
|
||||
if ((bpf = astate->bpf) == NULL)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
arp_probed(void *arg)
|
||||
{
|
||||
struct arp_state *astate = arg;
|
||||
|
||||
timespecclear(&astate->defend);
|
||||
astate->not_found_cb(astate);
|
||||
}
|
||||
|
||||
static void
|
||||
arp_probe1(void *arg)
|
||||
{
|
||||
struct arp_state *astate = arg;
|
||||
struct interface *ifp = astate->iface;
|
||||
unsigned int delay;
|
||||
|
||||
if (++astate->probes < PROBE_NUM) {
|
||||
delay = (PROBE_MIN * MSEC_PER_SEC) +
|
||||
(arc4random_uniform(
|
||||
(PROBE_MAX - PROBE_MIN) * MSEC_PER_SEC));
|
||||
eloop_timeout_add_msec(ifp->ctx->eloop, delay, arp_probe1, astate);
|
||||
} else {
|
||||
delay = ANNOUNCE_WAIT * MSEC_PER_SEC;
|
||||
eloop_timeout_add_msec(ifp->ctx->eloop, delay, arp_probed, astate);
|
||||
}
|
||||
logdebugx("%s: ARP probing %s (%d of %d), next in %0.1f seconds",
|
||||
ifp->name, inet_ntoa(astate->addr),
|
||||
astate->probes ? astate->probes : PROBE_NUM, PROBE_NUM,
|
||||
(float)delay / MSEC_PER_SEC);
|
||||
if (arp_request(astate, NULL) == -1)
|
||||
logerr(__func__);
|
||||
}
|
||||
|
||||
void
|
||||
arp_probe(struct arp_state *astate)
|
||||
{
|
||||
|
||||
astate->probes = 0;
|
||||
logdebugx("%s: probing for %s",
|
||||
astate->iface->name, inet_ntoa(astate->addr));
|
||||
arp_probe1(astate);
|
||||
}
|
||||
#endif /* ARP */
|
||||
|
||||
struct arp_state *
|
||||
arp_find(struct interface *ifp, const struct in_addr *addr)
|
||||
{
|
||||
struct iarp_state *state;
|
||||
struct arp_state *astate;
|
||||
|
||||
if ((state = ARP_STATE(ifp)) == NULL)
|
||||
goto out;
|
||||
TAILQ_FOREACH(astate, &state->arp_states, next) {
|
||||
if (astate->addr.s_addr == addr->s_addr && astate->iface == ifp)
|
||||
return astate;
|
||||
}
|
||||
out:
|
||||
errno = ESRCH;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
arp_announced(void *arg)
|
||||
{
|
||||
struct arp_state *astate = arg;
|
||||
|
||||
if (astate->announced_cb) {
|
||||
astate->announced_cb(astate);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Keep the ARP state open to handle ongoing ACD. */
|
||||
}
|
||||
|
||||
static void
|
||||
arp_announce1(void *arg)
|
||||
{
|
||||
struct arp_state *astate = arg;
|
||||
struct interface *ifp = astate->iface;
|
||||
struct ipv4_addr *ia;
|
||||
|
||||
if (++astate->claims < ANNOUNCE_NUM)
|
||||
logdebugx("%s: ARP announcing %s (%d of %d), "
|
||||
"next in %d.0 seconds",
|
||||
ifp->name, inet_ntoa(astate->addr),
|
||||
astate->claims, ANNOUNCE_NUM, ANNOUNCE_WAIT);
|
||||
else
|
||||
logdebugx("%s: ARP announcing %s (%d of %d)",
|
||||
ifp->name, inet_ntoa(astate->addr),
|
||||
astate->claims, ANNOUNCE_NUM);
|
||||
|
||||
/* The kernel will send a Gratuitous ARP for newly added addresses.
|
||||
* So we can avoid sending the same.
|
||||
* Linux is special and doesn't send one. */
|
||||
ia = ipv4_iffindaddr(ifp, &astate->addr, NULL);
|
||||
#ifndef __linux__
|
||||
if (astate->claims == 1 && ia != NULL && ia->flags & IPV4_AF_NEW)
|
||||
goto skip_request;
|
||||
#endif
|
||||
|
||||
if (arp_request(astate, &astate->addr) == -1)
|
||||
logerr(__func__);
|
||||
|
||||
#ifndef __linux__
|
||||
skip_request:
|
||||
#endif
|
||||
/* No longer a new address. */
|
||||
if (ia != NULL)
|
||||
ia->flags |= ~IPV4_AF_NEW;
|
||||
|
||||
eloop_timeout_add_sec(ifp->ctx->eloop, ANNOUNCE_WAIT,
|
||||
astate->claims < ANNOUNCE_NUM ? arp_announce1 : arp_announced,
|
||||
astate);
|
||||
}
|
||||
|
||||
static void
|
||||
arp_announce(struct arp_state *astate)
|
||||
{
|
||||
struct iarp_state *state;
|
||||
struct interface *ifp;
|
||||
struct arp_state *a2;
|
||||
int r;
|
||||
|
||||
/* Cancel any other ARP announcements for this address. */
|
||||
TAILQ_FOREACH(ifp, astate->iface->ctx->ifaces, next) {
|
||||
state = ARP_STATE(ifp);
|
||||
if (state == NULL)
|
||||
continue;
|
||||
TAILQ_FOREACH(a2, &state->arp_states, next) {
|
||||
if (astate == a2 ||
|
||||
a2->addr.s_addr != astate->addr.s_addr)
|
||||
continue;
|
||||
r = eloop_timeout_delete(a2->iface->ctx->eloop,
|
||||
a2->claims < ANNOUNCE_NUM
|
||||
? arp_announce1 : arp_announced,
|
||||
a2);
|
||||
if (r == -1)
|
||||
logerr(__func__);
|
||||
else if (r != 0) {
|
||||
logdebugx("%s: ARP announcement "
|
||||
"of %s cancelled",
|
||||
a2->iface->name,
|
||||
inet_ntoa(a2->addr));
|
||||
arp_announced(a2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
astate->claims = 0;
|
||||
arp_announce1(astate);
|
||||
}
|
||||
|
||||
struct arp_state *
|
||||
arp_ifannounceaddr(struct interface *ifp, const struct in_addr *ia)
|
||||
{
|
||||
struct arp_state *astate;
|
||||
|
||||
if (ifp->flags & IFF_NOARP || !(ifp->options->options & DHCPCD_ARP))
|
||||
return NULL;
|
||||
|
||||
astate = arp_find(ifp, ia);
|
||||
if (astate == NULL) {
|
||||
astate = arp_new(ifp, ia);
|
||||
if (astate == NULL)
|
||||
return NULL;
|
||||
astate->announced_cb = arp_free;
|
||||
}
|
||||
arp_announce(astate);
|
||||
return astate;
|
||||
}
|
||||
|
||||
struct arp_state *
|
||||
arp_announceaddr(struct dhcpcd_ctx *ctx, const struct in_addr *ia)
|
||||
{
|
||||
struct interface *ifp, *iff = NULL;
|
||||
struct ipv4_addr *iap;
|
||||
|
||||
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
|
||||
if (!ifp->active || !if_is_link_up(ifp))
|
||||
continue;
|
||||
iap = ipv4_iffindaddr(ifp, ia, NULL);
|
||||
if (iap == NULL)
|
||||
continue;
|
||||
#ifdef IN_IFF_NOTUSEABLE
|
||||
if (iap->addr_flags & IN_IFF_NOTUSEABLE)
|
||||
continue;
|
||||
#endif
|
||||
if (iff != NULL && iff->metric < ifp->metric)
|
||||
continue;
|
||||
iff = ifp;
|
||||
}
|
||||
if (iff == NULL)
|
||||
return NULL;
|
||||
|
||||
return arp_ifannounceaddr(iff, ia);
|
||||
}
|
||||
|
||||
struct arp_state *
|
||||
arp_new(struct interface *ifp, const struct in_addr *addr)
|
||||
{
|
||||
struct iarp_state *state;
|
||||
struct arp_state *astate;
|
||||
|
||||
if ((state = ARP_STATE(ifp)) == NULL) {
|
||||
ifp->if_data[IF_DATA_ARP] = malloc(sizeof(*state));
|
||||
state = ARP_STATE(ifp);
|
||||
if (state == NULL) {
|
||||
logerr(__func__);
|
||||
return NULL;
|
||||
}
|
||||
TAILQ_INIT(&state->arp_states);
|
||||
} else {
|
||||
if ((astate = arp_find(ifp, addr)) != NULL)
|
||||
return astate;
|
||||
}
|
||||
|
||||
if ((astate = calloc(1, sizeof(*astate))) == NULL) {
|
||||
logerr(__func__);
|
||||
return NULL;
|
||||
}
|
||||
astate->iface = ifp;
|
||||
astate->addr = *addr;
|
||||
|
||||
#ifdef PRIVSEP
|
||||
if (IN_PRIVSEP(ifp->ctx)) {
|
||||
if (ps_bpf_openarp(ifp, addr) == -1) {
|
||||
logerr(__func__);
|
||||
free(astate);
|
||||
return NULL;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
astate->bpf = bpf_open(ifp, bpf_arp, addr);
|
||||
if (astate->bpf == NULL) {
|
||||
logerr(__func__);
|
||||
free(astate);
|
||||
return NULL;
|
||||
}
|
||||
if (eloop_event_add(ifp->ctx->eloop, astate->bpf->bpf_fd, ELE_READ,
|
||||
arp_read, astate) == -1)
|
||||
logerr("%s: eloop_event_add", __func__);
|
||||
}
|
||||
|
||||
|
||||
state = ARP_STATE(ifp);
|
||||
TAILQ_INSERT_TAIL(&state->arp_states, astate, next);
|
||||
return astate;
|
||||
}
|
||||
|
||||
void
|
||||
arp_free(struct arp_state *astate)
|
||||
{
|
||||
struct interface *ifp;
|
||||
struct dhcpcd_ctx *ctx;
|
||||
struct iarp_state *state;
|
||||
|
||||
if (astate == NULL)
|
||||
return;
|
||||
|
||||
ifp = astate->iface;
|
||||
ctx = ifp->ctx;
|
||||
eloop_timeout_delete(ctx->eloop, NULL, astate);
|
||||
|
||||
state = ARP_STATE(ifp);
|
||||
TAILQ_REMOVE(&state->arp_states, astate, next);
|
||||
if (astate->free_cb)
|
||||
astate->free_cb(astate);
|
||||
|
||||
#ifdef PRIVSEP
|
||||
if (IN_PRIVSEP(ctx) && ps_bpf_closearp(ifp, &astate->addr) == -1)
|
||||
logerr(__func__);
|
||||
#endif
|
||||
if (astate->bpf != NULL) {
|
||||
eloop_event_delete(ctx->eloop, astate->bpf->bpf_fd);
|
||||
bpf_close(astate->bpf);
|
||||
}
|
||||
|
||||
free(astate);
|
||||
|
||||
if (TAILQ_FIRST(&state->arp_states) == NULL) {
|
||||
free(state);
|
||||
ifp->if_data[IF_DATA_ARP] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
arp_freeaddr(struct interface *ifp, const struct in_addr *ia)
|
||||
{
|
||||
struct arp_state *astate;
|
||||
|
||||
astate = arp_find(ifp, ia);
|
||||
arp_free(astate);
|
||||
}
|
||||
|
||||
void
|
||||
arp_drop(struct interface *ifp)
|
||||
{
|
||||
struct iarp_state *state;
|
||||
struct arp_state *astate;
|
||||
|
||||
while ((state = ARP_STATE(ifp)) != NULL &&
|
||||
(astate = TAILQ_FIRST(&state->arp_states)) != NULL)
|
||||
arp_free(astate);
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef ARP_H
|
||||
#define ARP_H
|
||||
|
||||
/* ARP timings from RFC5227 */
|
||||
#define PROBE_WAIT 1
|
||||
#define PROBE_NUM 3
|
||||
#define PROBE_MIN 1
|
||||
#define PROBE_MAX 2
|
||||
#define ANNOUNCE_WAIT 2
|
||||
#define ANNOUNCE_NUM 2
|
||||
#define ANNOUNCE_INTERVAL 2
|
||||
#define MAX_CONFLICTS 10
|
||||
#define RATE_LIMIT_INTERVAL 60
|
||||
#define DEFEND_INTERVAL 10
|
||||
|
||||
#include "bpf.h"
|
||||
#include "dhcpcd.h"
|
||||
#include "if.h"
|
||||
|
||||
#ifdef IN_IFF_DUPLICATED
|
||||
/* NetBSD gained RFC 5227 support in the kernel.
|
||||
* This means dhcpcd doesn't need ARP except for ARPing support
|
||||
* and ARP announcing an address. */
|
||||
#if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 799003900
|
||||
#define KERNEL_RFC5227
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct arp_msg {
|
||||
uint16_t op;
|
||||
uint8_t sha[HWADDR_LEN];
|
||||
struct in_addr sip;
|
||||
uint8_t tha[HWADDR_LEN];
|
||||
struct in_addr tip;
|
||||
/* Frame header and sender to diagnose failures */
|
||||
uint8_t fsha[HWADDR_LEN];
|
||||
uint8_t ftha[HWADDR_LEN];
|
||||
};
|
||||
|
||||
struct arp_state {
|
||||
TAILQ_ENTRY(arp_state) next;
|
||||
struct interface *iface;
|
||||
struct in_addr addr;
|
||||
struct bpf *bpf;
|
||||
|
||||
int probes;
|
||||
int claims;
|
||||
struct timespec defend;
|
||||
|
||||
void (*found_cb)(struct arp_state *, const struct arp_msg *);
|
||||
void (*not_found_cb)(struct arp_state *);
|
||||
void (*announced_cb)(struct arp_state *);
|
||||
void (*defend_failed_cb)(struct arp_state *);
|
||||
void (*free_cb)(struct arp_state *);
|
||||
};
|
||||
TAILQ_HEAD(arp_statehead, arp_state);
|
||||
|
||||
struct iarp_state {
|
||||
struct arp_statehead arp_states;
|
||||
};
|
||||
|
||||
#define ARP_STATE(ifp) \
|
||||
((struct iarp_state *)(ifp)->if_data[IF_DATA_ARP])
|
||||
#define ARP_CSTATE(ifp) \
|
||||
((const struct iarp_state *)(ifp)->if_data[IF_DATA_ARP])
|
||||
|
||||
#ifdef ARP
|
||||
void arp_packet(struct interface *, uint8_t *, size_t, unsigned int);
|
||||
struct arp_state *arp_new(struct interface *, const struct in_addr *);
|
||||
void arp_probe(struct arp_state *);
|
||||
struct arp_state *arp_announceaddr(struct dhcpcd_ctx *, const struct in_addr *);
|
||||
struct arp_state *arp_ifannounceaddr(struct interface *, const struct in_addr *);
|
||||
struct arp_state * arp_find(struct interface *, const struct in_addr *);
|
||||
void arp_free(struct arp_state *);
|
||||
void arp_freeaddr(struct interface *, const struct in_addr *);
|
||||
void arp_drop(struct interface *);
|
||||
#endif /* ARP */
|
||||
#endif /* ARP_H */
|
|
@ -0,0 +1,738 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/file.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "auth.h"
|
||||
#include "dhcp.h"
|
||||
#include "dhcp6.h"
|
||||
#include "dhcpcd.h"
|
||||
#include "privsep-root.h"
|
||||
|
||||
#ifdef HAVE_HMAC_H
|
||||
#include <hmac.h>
|
||||
#endif
|
||||
|
||||
#ifdef __sun
|
||||
#define htonll
|
||||
#define ntohll
|
||||
#endif
|
||||
|
||||
#ifndef htonll
|
||||
#if (BYTE_ORDER == LITTLE_ENDIAN)
|
||||
#define htonll(x) ((uint64_t)htonl((uint32_t)((x) >> 32)) | \
|
||||
(uint64_t)htonl((uint32_t)((x) & 0x00000000ffffffffULL)) << 32)
|
||||
#else /* (BYTE_ORDER == LITTLE_ENDIAN) */
|
||||
#define htonll(x) (x)
|
||||
#endif
|
||||
#endif /* htonll */
|
||||
|
||||
#ifndef ntohll
|
||||
#if (BYTE_ORDER == LITTLE_ENDIAN)
|
||||
#define ntohll(x) ((uint64_t)ntohl((uint32_t)((x) >> 32)) | \
|
||||
(uint64_t)ntohl((uint32_t)((x) & 0x00000000ffffffffULL)) << 32)
|
||||
#else /* (BYTE_ORDER == LITTLE_ENDIAN) */
|
||||
#define ntohll(x) (x)
|
||||
#endif
|
||||
#endif /* ntohll */
|
||||
|
||||
#define HMAC_LENGTH 16
|
||||
|
||||
void
|
||||
dhcp_auth_reset(struct authstate *state)
|
||||
{
|
||||
|
||||
state->replay = 0;
|
||||
if (state->token) {
|
||||
free(state->token->key);
|
||||
free(state->token->realm);
|
||||
free(state->token);
|
||||
state->token = NULL;
|
||||
}
|
||||
if (state->reconf) {
|
||||
free(state->reconf->key);
|
||||
free(state->reconf->realm);
|
||||
free(state->reconf);
|
||||
state->reconf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Authenticate a DHCP message.
|
||||
* m and mlen refer to the whole message.
|
||||
* t is the DHCP type, pass it 4 or 6.
|
||||
* data and dlen refer to the authentication option within the message.
|
||||
*/
|
||||
const struct token *
|
||||
dhcp_auth_validate(struct authstate *state, const struct auth *auth,
|
||||
const void *vm, size_t mlen, int mp, int mt,
|
||||
const void *vdata, size_t dlen)
|
||||
{
|
||||
const uint8_t *m, *data;
|
||||
uint8_t protocol, algorithm, rdm, *mm, type;
|
||||
uint64_t replay;
|
||||
uint32_t secretid;
|
||||
const uint8_t *d, *realm;
|
||||
size_t realm_len;
|
||||
const struct token *t;
|
||||
time_t now;
|
||||
uint8_t hmac_code[HMAC_LENGTH];
|
||||
|
||||
if (dlen < 3 + sizeof(replay)) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
m = vm;
|
||||
data = vdata;
|
||||
/* Ensure that d is inside m which *may* not be the case for DHCPv4.
|
||||
* This can occur if the authentication option is split using
|
||||
* DHCP long option from RFC 3399. Section 9 which does infact note that
|
||||
* implementations should take this into account.
|
||||
* Fixing this would be problematic, patches welcome. */
|
||||
if (data < m || data > m + mlen || data + dlen > m + mlen) {
|
||||
errno = ERANGE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
d = data;
|
||||
protocol = *d++;
|
||||
algorithm = *d++;
|
||||
rdm = *d++;
|
||||
if (!(auth->options & DHCPCD_AUTH_SEND)) {
|
||||
/* If we didn't send any authorisation, it can only be a
|
||||
* reconfigure key */
|
||||
if (protocol != AUTH_PROTO_RECONFKEY) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
} else if (protocol != auth->protocol ||
|
||||
algorithm != auth->algorithm ||
|
||||
rdm != auth->rdm)
|
||||
{
|
||||
/* As we don't require authentication, we should still
|
||||
* accept a reconfigure key */
|
||||
if (protocol != AUTH_PROTO_RECONFKEY ||
|
||||
auth->options & DHCPCD_AUTH_REQUIRE)
|
||||
{
|
||||
errno = EPERM;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
dlen -= 3;
|
||||
|
||||
memcpy(&replay, d, sizeof(replay));
|
||||
replay = ntohll(replay);
|
||||
/*
|
||||
* Test for a replay attack.
|
||||
*
|
||||
* NOTE: Some servers always send a replay data value of zero.
|
||||
* This is strictly compliant with RFC 3315 and 3318 which say:
|
||||
* "If the RDM field contains 0x00, the replay detection field MUST be
|
||||
* set to the value of a monotonically increasing counter."
|
||||
* An example of a monotonically increasing sequence is:
|
||||
* 1, 2, 2, 2, 2, 2, 2
|
||||
* Errata 3474 updates RFC 3318 to say:
|
||||
* "If the RDM field contains 0x00, the replay detection field MUST be
|
||||
* set to the value of a strictly increasing counter."
|
||||
*
|
||||
* Taking the above into account, dhcpcd will only test for
|
||||
* strictly speaking replay attacks if it receives any non zero
|
||||
* replay data to validate against.
|
||||
*/
|
||||
if (state->token && state->replay != 0) {
|
||||
if (state->replay == (replay ^ 0x8000000000000000ULL)) {
|
||||
/* We don't know if the singular point is increasing
|
||||
* or decreasing. */
|
||||
errno = EPERM;
|
||||
return NULL;
|
||||
}
|
||||
if ((uint64_t)(replay - state->replay) <= 0) {
|
||||
/* Replay attack detected */
|
||||
errno = EPERM;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
d+= sizeof(replay);
|
||||
dlen -= sizeof(replay);
|
||||
|
||||
realm = NULL;
|
||||
realm_len = 0;
|
||||
|
||||
/* Extract realm and secret.
|
||||
* Rest of data is MAC. */
|
||||
switch (protocol) {
|
||||
case AUTH_PROTO_TOKEN:
|
||||
secretid = auth->token_rcv_secretid;
|
||||
break;
|
||||
case AUTH_PROTO_DELAYED:
|
||||
if (dlen < sizeof(secretid) + sizeof(hmac_code)) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
memcpy(&secretid, d, sizeof(secretid));
|
||||
secretid = ntohl(secretid);
|
||||
d += sizeof(secretid);
|
||||
dlen -= sizeof(secretid);
|
||||
break;
|
||||
case AUTH_PROTO_DELAYEDREALM:
|
||||
if (dlen < sizeof(secretid) + sizeof(hmac_code)) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
realm_len = dlen - (sizeof(secretid) + sizeof(hmac_code));
|
||||
if (realm_len) {
|
||||
realm = d;
|
||||
d += realm_len;
|
||||
dlen -= realm_len;
|
||||
}
|
||||
memcpy(&secretid, d, sizeof(secretid));
|
||||
secretid = ntohl(secretid);
|
||||
d += sizeof(secretid);
|
||||
dlen -= sizeof(secretid);
|
||||
break;
|
||||
case AUTH_PROTO_RECONFKEY:
|
||||
if (dlen != 1 + 16) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
type = *d++;
|
||||
dlen--;
|
||||
switch (type) {
|
||||
case 1:
|
||||
if ((mp == 4 && mt == DHCP_ACK) ||
|
||||
(mp == 6 && mt == DHCP6_REPLY))
|
||||
{
|
||||
if (state->reconf == NULL) {
|
||||
state->reconf =
|
||||
malloc(sizeof(*state->reconf));
|
||||
if (state->reconf == NULL)
|
||||
return NULL;
|
||||
state->reconf->key = malloc(16);
|
||||
if (state->reconf->key == NULL) {
|
||||
free(state->reconf);
|
||||
state->reconf = NULL;
|
||||
return NULL;
|
||||
}
|
||||
state->reconf->secretid = 0;
|
||||
state->reconf->expire = 0;
|
||||
state->reconf->realm = NULL;
|
||||
state->reconf->realm_len = 0;
|
||||
state->reconf->key_len = 16;
|
||||
}
|
||||
memcpy(state->reconf->key, d, 16);
|
||||
} else {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
if (state->reconf == NULL)
|
||||
errno = ENOENT;
|
||||
/* Free the old token so we log acceptance */
|
||||
if (state->token) {
|
||||
free(state->token);
|
||||
state->token = NULL;
|
||||
}
|
||||
/* Nothing to validate, just accepting the key */
|
||||
return state->reconf;
|
||||
case 2:
|
||||
if (!((mp == 4 && mt == DHCP_FORCERENEW) ||
|
||||
(mp == 6 && mt == DHCP6_RECONFIGURE)))
|
||||
{
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
if (state->reconf == NULL) {
|
||||
errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
t = state->reconf;
|
||||
goto gottoken;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
default:
|
||||
errno = ENOTSUP;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find a token for the realm and secret */
|
||||
TAILQ_FOREACH(t, &auth->tokens, next) {
|
||||
if (t->secretid == secretid &&
|
||||
t->realm_len == realm_len &&
|
||||
(t->realm_len == 0 ||
|
||||
memcmp(t->realm, realm, t->realm_len) == 0))
|
||||
break;
|
||||
}
|
||||
if (t == NULL) {
|
||||
errno = ESRCH;
|
||||
return NULL;
|
||||
}
|
||||
if (t->expire) {
|
||||
if (time(&now) == -1)
|
||||
return NULL;
|
||||
if (t->expire < now) {
|
||||
errno = EFAULT;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
gottoken:
|
||||
/* First message from the server */
|
||||
if (state->token &&
|
||||
(state->token->secretid != t->secretid ||
|
||||
state->token->realm_len != t->realm_len ||
|
||||
memcmp(state->token->realm, t->realm, t->realm_len)))
|
||||
{
|
||||
errno = EPERM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Special case as no hashing needs to be done. */
|
||||
if (protocol == AUTH_PROTO_TOKEN) {
|
||||
if (dlen != t->key_len || memcmp(d, t->key, dlen)) {
|
||||
errno = EPERM;
|
||||
return NULL;
|
||||
}
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Make a duplicate of the message, but zero out the MAC part */
|
||||
mm = malloc(mlen);
|
||||
if (mm == NULL)
|
||||
return NULL;
|
||||
memcpy(mm, m, mlen);
|
||||
memset(mm + (d - m), 0, dlen);
|
||||
|
||||
/* RFC3318, section 5.2 - zero giaddr and hops */
|
||||
if (mp == 4) {
|
||||
/* Assert the bootp structure is correct size. */
|
||||
__CTASSERT(sizeof(struct bootp) == 300);
|
||||
|
||||
*(mm + offsetof(struct bootp, hops)) = '\0';
|
||||
memset(mm + offsetof(struct bootp, giaddr), 0, 4);
|
||||
}
|
||||
|
||||
memset(hmac_code, 0, sizeof(hmac_code));
|
||||
switch (algorithm) {
|
||||
case AUTH_ALG_HMAC_MD5:
|
||||
hmac("md5", t->key, t->key_len, mm, mlen,
|
||||
hmac_code, sizeof(hmac_code));
|
||||
break;
|
||||
default:
|
||||
errno = ENOSYS;
|
||||
free(mm);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
free(mm);
|
||||
if (!consttime_memequal(d, &hmac_code, dlen)) {
|
||||
errno = EPERM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
finish:
|
||||
/* If we got here then authentication passed */
|
||||
state->replay = replay;
|
||||
if (state->token == NULL) {
|
||||
/* We cannot just save a pointer because a reconfigure will
|
||||
* recreate the token list. So we duplicate it. */
|
||||
state->token = malloc(sizeof(*state->token));
|
||||
if (state->token) {
|
||||
state->token->secretid = t->secretid;
|
||||
state->token->key = malloc(t->key_len);
|
||||
if (state->token->key) {
|
||||
state->token->key_len = t->key_len;
|
||||
memcpy(state->token->key, t->key, t->key_len);
|
||||
} else {
|
||||
free(state->token);
|
||||
state->token = NULL;
|
||||
return NULL;
|
||||
}
|
||||
if (t->realm_len) {
|
||||
state->token->realm = malloc(t->realm_len);
|
||||
if (state->token->realm) {
|
||||
state->token->realm_len = t->realm_len;
|
||||
memcpy(state->token->realm, t->realm,
|
||||
t->realm_len);
|
||||
} else {
|
||||
free(state->token->key);
|
||||
free(state->token);
|
||||
state->token = NULL;
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
state->token->realm = NULL;
|
||||
state->token->realm_len = 0;
|
||||
}
|
||||
}
|
||||
/* If we cannot save the token, we must invalidate */
|
||||
if (state->token == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
int
|
||||
auth_get_rdm_monotonic(uint64_t *rdm)
|
||||
{
|
||||
FILE *fp;
|
||||
int err;
|
||||
#ifdef LOCK_EX
|
||||
int flocked;
|
||||
#endif
|
||||
|
||||
fp = fopen(RDM_MONOFILE, "r+");
|
||||
if (fp == NULL) {
|
||||
if (errno != ENOENT)
|
||||
return -1;
|
||||
fp = fopen(RDM_MONOFILE, "w");
|
||||
if (fp == NULL)
|
||||
return -1;
|
||||
if (chmod(RDM_MONOFILE, 0400) == -1) {
|
||||
fclose(fp);
|
||||
unlink(RDM_MONOFILE);
|
||||
return -1;
|
||||
}
|
||||
#ifdef LOCK_EX
|
||||
flocked = flock(fileno(fp), LOCK_EX);
|
||||
#endif
|
||||
*rdm = 0;
|
||||
} else {
|
||||
#ifdef LOCK_EX
|
||||
flocked = flock(fileno(fp), LOCK_EX);
|
||||
#endif
|
||||
if (fscanf(fp, "0x%016" PRIu64, rdm) != 1) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
(*rdm)++;
|
||||
if (fseek(fp, 0, SEEK_SET) == -1 ||
|
||||
ftruncate(fileno(fp), 0) == -1 ||
|
||||
fprintf(fp, "0x%016" PRIu64 "\n", *rdm) != 19 ||
|
||||
fflush(fp) == EOF)
|
||||
err = -1;
|
||||
else
|
||||
err = 0;
|
||||
#ifdef LOCK_EX
|
||||
if (flocked == 0)
|
||||
flock(fileno(fp), LOCK_UN);
|
||||
#endif
|
||||
fclose(fp);
|
||||
return err;
|
||||
}
|
||||
|
||||
#define NTP_EPOCH 2208988800U /* 1970 - 1900 in seconds */
|
||||
#define NTP_SCALE_FRAC 4294967295.0 /* max value of the fractional part */
|
||||
static uint64_t
|
||||
get_next_rdm_monotonic_clock(struct auth *auth)
|
||||
{
|
||||
struct timespec ts;
|
||||
uint64_t secs, rdm;
|
||||
double frac;
|
||||
|
||||
if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
|
||||
return ++auth->last_replay; /* report error? */
|
||||
|
||||
secs = (uint64_t)ts.tv_sec + NTP_EPOCH;
|
||||
frac = ((double)ts.tv_nsec / 1e9 * NTP_SCALE_FRAC);
|
||||
rdm = (secs << 32) | (uint64_t)frac;
|
||||
return rdm;
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
get_next_rdm_monotonic(struct dhcpcd_ctx *ctx, struct auth *auth)
|
||||
{
|
||||
#ifndef PRIVSEP
|
||||
UNUSED(ctx);
|
||||
#endif
|
||||
|
||||
if (auth->options & DHCPCD_AUTH_RDM_COUNTER) {
|
||||
uint64_t rdm;
|
||||
int err;
|
||||
|
||||
#ifdef PRIVSEP
|
||||
if (IN_PRIVSEP(ctx)) {
|
||||
|
||||
err = ps_root_getauthrdm(ctx, &rdm);
|
||||
} else
|
||||
#endif
|
||||
err = auth_get_rdm_monotonic(&rdm);
|
||||
if (err == -1)
|
||||
return ++auth->last_replay;
|
||||
|
||||
auth->last_replay = rdm;
|
||||
return rdm;
|
||||
}
|
||||
return get_next_rdm_monotonic_clock(auth);
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode a DHCP message.
|
||||
* Either we know which token to use from the server response
|
||||
* or we are using a basic configuration token.
|
||||
* token is the token to encrypt with.
|
||||
* m and mlen refer to the whole message.
|
||||
* mp is the DHCP type, pass it 4 or 6.
|
||||
* mt is the DHCP message type.
|
||||
* data and dlen refer to the authentication option within the message.
|
||||
*/
|
||||
ssize_t
|
||||
dhcp_auth_encode(struct dhcpcd_ctx *ctx, struct auth *auth,
|
||||
const struct token *t,
|
||||
void *vm, size_t mlen, int mp, int mt,
|
||||
void *vdata, size_t dlen)
|
||||
{
|
||||
uint64_t rdm;
|
||||
uint8_t hmac_code[HMAC_LENGTH];
|
||||
time_t now;
|
||||
uint8_t hops, *p, *m, *data;
|
||||
uint32_t giaddr, secretid;
|
||||
bool auth_info;
|
||||
|
||||
/* Ignore the token argument given to us - always send using the
|
||||
* configured token. */
|
||||
if (auth->protocol == AUTH_PROTO_TOKEN) {
|
||||
TAILQ_FOREACH(t, &auth->tokens, next) {
|
||||
if (t->secretid == auth->token_snd_secretid)
|
||||
break;
|
||||
}
|
||||
if (t == NULL) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (t->expire) {
|
||||
if (time(&now) == -1)
|
||||
return -1;
|
||||
if (t->expire < now) {
|
||||
errno = EPERM;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(auth->protocol) {
|
||||
case AUTH_PROTO_TOKEN:
|
||||
case AUTH_PROTO_DELAYED:
|
||||
case AUTH_PROTO_DELAYEDREALM:
|
||||
/* We don't ever send a reconf key */
|
||||
break;
|
||||
default:
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch(auth->algorithm) {
|
||||
case AUTH_ALG_NONE:
|
||||
case AUTH_ALG_HMAC_MD5:
|
||||
break;
|
||||
default:
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch(auth->rdm) {
|
||||
case AUTH_RDM_MONOTONIC:
|
||||
break;
|
||||
default:
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* DISCOVER or INFORM messages don't write auth info */
|
||||
if ((mp == 4 && (mt == DHCP_DISCOVER || mt == DHCP_INFORM)) ||
|
||||
(mp == 6 && (mt == DHCP6_SOLICIT || mt == DHCP6_INFORMATION_REQ)))
|
||||
auth_info = false;
|
||||
else
|
||||
auth_info = true;
|
||||
|
||||
/* Work out the auth area size.
|
||||
* We only need to do this for DISCOVER messages */
|
||||
if (vdata == NULL) {
|
||||
dlen = 1 + 1 + 1 + 8;
|
||||
switch(auth->protocol) {
|
||||
case AUTH_PROTO_TOKEN:
|
||||
dlen += t->key_len;
|
||||
break;
|
||||
case AUTH_PROTO_DELAYEDREALM:
|
||||
if (auth_info && t)
|
||||
dlen += t->realm_len;
|
||||
/* FALLTHROUGH */
|
||||
case AUTH_PROTO_DELAYED:
|
||||
if (auth_info && t)
|
||||
dlen += sizeof(t->secretid) + sizeof(hmac_code);
|
||||
break;
|
||||
}
|
||||
return (ssize_t)dlen;
|
||||
}
|
||||
|
||||
if (dlen < 1 + 1 + 1 + 8) {
|
||||
errno = ENOBUFS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Ensure that d is inside m which *may* not be the case for DHPCPv4 */
|
||||
m = vm;
|
||||
data = vdata;
|
||||
if (data < m || data > m + mlen || data + dlen > m + mlen) {
|
||||
errno = ERANGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Write out our option */
|
||||
*data++ = auth->protocol;
|
||||
*data++ = auth->algorithm;
|
||||
/*
|
||||
* RFC 3315 21.4.4.1 says that SOLICIT in DELAYED authentication
|
||||
* should not set RDM or it's data.
|
||||
* An expired draft draft-ietf-dhc-dhcpv6-clarify-auth-01 suggets
|
||||
* this should not be set for INFORMATION REQ messages as well,
|
||||
* which is probably a good idea because both states start from zero.
|
||||
*/
|
||||
if (auth_info ||
|
||||
!(auth->protocol & (AUTH_PROTO_DELAYED | AUTH_PROTO_DELAYEDREALM)))
|
||||
{
|
||||
*data++ = auth->rdm;
|
||||
switch (auth->rdm) {
|
||||
case AUTH_RDM_MONOTONIC:
|
||||
rdm = get_next_rdm_monotonic(ctx, auth);
|
||||
break;
|
||||
default:
|
||||
/* This block appeases gcc, clang doesn't need it */
|
||||
rdm = get_next_rdm_monotonic(ctx, auth);
|
||||
break;
|
||||
}
|
||||
rdm = htonll(rdm);
|
||||
memcpy(data, &rdm, 8);
|
||||
} else {
|
||||
*data++ = 0; /* rdm */
|
||||
memset(data, 0, 8); /* replay detection data */
|
||||
}
|
||||
data += 8;
|
||||
dlen -= 1 + 1 + 1 + 8;
|
||||
|
||||
/* Special case as no hashing needs to be done. */
|
||||
if (auth->protocol == AUTH_PROTO_TOKEN) {
|
||||
/* Should be impossible, but still */
|
||||
if (t == NULL) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (dlen < t->key_len) {
|
||||
errno = ENOBUFS;
|
||||
return -1;
|
||||
}
|
||||
memcpy(data, t->key, t->key_len);
|
||||
return (ssize_t)(dlen - t->key_len);
|
||||
}
|
||||
|
||||
/* DISCOVER or INFORM messages don't write auth info */
|
||||
if (!auth_info)
|
||||
return (ssize_t)dlen;
|
||||
|
||||
/* Loading a saved lease without an authentication option */
|
||||
if (t == NULL)
|
||||
return 0;
|
||||
|
||||
/* Write out the Realm */
|
||||
if (auth->protocol == AUTH_PROTO_DELAYEDREALM) {
|
||||
if (dlen < t->realm_len) {
|
||||
errno = ENOBUFS;
|
||||
return -1;
|
||||
}
|
||||
memcpy(data, t->realm, t->realm_len);
|
||||
data += t->realm_len;
|
||||
dlen -= t->realm_len;
|
||||
}
|
||||
|
||||
/* Write out the SecretID */
|
||||
if (auth->protocol == AUTH_PROTO_DELAYED ||
|
||||
auth->protocol == AUTH_PROTO_DELAYEDREALM)
|
||||
{
|
||||
if (dlen < sizeof(t->secretid)) {
|
||||
errno = ENOBUFS;
|
||||
return -1;
|
||||
}
|
||||
secretid = htonl(t->secretid);
|
||||
memcpy(data, &secretid, sizeof(secretid));
|
||||
data += sizeof(secretid);
|
||||
dlen -= sizeof(secretid);
|
||||
}
|
||||
|
||||
/* Zero what's left, the MAC */
|
||||
memset(data, 0, dlen);
|
||||
|
||||
/* RFC3318, section 5.2 - zero giaddr and hops */
|
||||
if (mp == 4) {
|
||||
p = m + offsetof(struct bootp, hops);
|
||||
hops = *p;
|
||||
*p = '\0';
|
||||
p = m + offsetof(struct bootp, giaddr);
|
||||
memcpy(&giaddr, p, sizeof(giaddr));
|
||||
memset(p, 0, sizeof(giaddr));
|
||||
} else {
|
||||
/* appease GCC again */
|
||||
hops = 0;
|
||||
giaddr = 0;
|
||||
}
|
||||
|
||||
/* Create our hash and write it out */
|
||||
switch(auth->algorithm) {
|
||||
case AUTH_ALG_HMAC_MD5:
|
||||
hmac("md5", t->key, t->key_len, m, mlen,
|
||||
hmac_code, sizeof(hmac_code));
|
||||
memcpy(data, hmac_code, sizeof(hmac_code));
|
||||
break;
|
||||
}
|
||||
|
||||
/* RFC3318, section 5.2 - restore giaddr and hops */
|
||||
if (mp == 4) {
|
||||
p = m + offsetof(struct bootp, hops);
|
||||
*p = hops;
|
||||
p = m + offsetof(struct bootp, giaddr);
|
||||
memcpy(p, &giaddr, sizeof(giaddr));
|
||||
}
|
||||
|
||||
/* Done! */
|
||||
return (int)(dlen - sizeof(hmac_code)); /* should be zero */
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef AUTH_H
|
||||
#define AUTH_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_SYS_QUEUE_H
|
||||
#include <sys/queue.h>
|
||||
#endif
|
||||
|
||||
#define DHCPCD_AUTH_SEND (1 << 0)
|
||||
#define DHCPCD_AUTH_REQUIRE (1 << 1)
|
||||
#define DHCPCD_AUTH_RDM_COUNTER (1 << 2)
|
||||
|
||||
#define DHCPCD_AUTH_SENDREQUIRE (DHCPCD_AUTH_SEND | DHCPCD_AUTH_REQUIRE)
|
||||
|
||||
#define AUTH_PROTO_TOKEN 0
|
||||
#define AUTH_PROTO_DELAYED 1
|
||||
#define AUTH_PROTO_DELAYEDREALM 2
|
||||
#define AUTH_PROTO_RECONFKEY 3
|
||||
|
||||
#define AUTH_ALG_NONE 0
|
||||
#define AUTH_ALG_HMAC_MD5 1
|
||||
|
||||
#define AUTH_RDM_MONOTONIC 0
|
||||
|
||||
struct token {
|
||||
TAILQ_ENTRY(token) next;
|
||||
uint32_t secretid;
|
||||
size_t realm_len;
|
||||
unsigned char *realm;
|
||||
size_t key_len;
|
||||
unsigned char *key;
|
||||
time_t expire;
|
||||
};
|
||||
|
||||
TAILQ_HEAD(token_head, token);
|
||||
|
||||
struct auth {
|
||||
int options;
|
||||
#ifdef AUTH
|
||||
uint8_t protocol;
|
||||
uint8_t algorithm;
|
||||
uint8_t rdm;
|
||||
uint64_t last_replay;
|
||||
uint8_t last_replay_set;
|
||||
struct token_head tokens;
|
||||
uint32_t token_snd_secretid;
|
||||
uint32_t token_rcv_secretid;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct authstate {
|
||||
uint64_t replay;
|
||||
struct token *token;
|
||||
struct token *reconf;
|
||||
};
|
||||
|
||||
void dhcp_auth_reset(struct authstate *);
|
||||
|
||||
const struct token * dhcp_auth_validate(struct authstate *,
|
||||
const struct auth *,
|
||||
const void *, size_t, int, int,
|
||||
const void *, size_t);
|
||||
|
||||
struct dhcpcd_ctx;
|
||||
ssize_t dhcp_auth_encode(struct dhcpcd_ctx *, struct auth *,
|
||||
const struct token *,
|
||||
void *, size_t, int, int,
|
||||
void *, size_t);
|
||||
|
||||
int auth_get_rdm_monotonic(uint64_t *rdm);
|
||||
#endif
|
|
@ -0,0 +1,714 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd: BPF arp and bootp filtering
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/if_ether.h>
|
||||
|
||||
#ifdef __linux__
|
||||
/* Special BPF snowflake. */
|
||||
#include <linux/filter.h>
|
||||
#define bpf_insn sock_filter
|
||||
#else
|
||||
#include <net/bpf.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "arp.h"
|
||||
#include "bpf.h"
|
||||
#include "dhcp.h"
|
||||
#include "if.h"
|
||||
#include "logerr.h"
|
||||
|
||||
/* BPF helper macros */
|
||||
#ifdef __linux__
|
||||
#define BPF_WHOLEPACKET 0x7fffffff /* work around buggy LPF filters */
|
||||
#else
|
||||
#define BPF_WHOLEPACKET ~0U
|
||||
#endif
|
||||
|
||||
/* Macros to update the BPF structure */
|
||||
#define BPF_SET_STMT(insn, c, v) { \
|
||||
(insn)->code = (c); \
|
||||
(insn)->jt = 0; \
|
||||
(insn)->jf = 0; \
|
||||
(insn)->k = (uint32_t)(v); \
|
||||
}
|
||||
|
||||
#define BPF_SET_JUMP(insn, c, v, t, f) { \
|
||||
(insn)->code = (c); \
|
||||
(insn)->jt = (t); \
|
||||
(insn)->jf = (f); \
|
||||
(insn)->k = (uint32_t)(v); \
|
||||
}
|
||||
|
||||
size_t
|
||||
bpf_frame_header_len(const struct interface *ifp)
|
||||
{
|
||||
|
||||
switch (ifp->hwtype) {
|
||||
case ARPHRD_ETHER:
|
||||
return sizeof(struct ether_header);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void *
|
||||
bpf_frame_header_src(const struct interface *ifp, void *fh, size_t *len)
|
||||
{
|
||||
uint8_t *f = fh;
|
||||
|
||||
switch (ifp->hwtype) {
|
||||
case ARPHRD_ETHER:
|
||||
*len = sizeof(((struct ether_header *)0)->ether_shost);
|
||||
return f + offsetof(struct ether_header, ether_shost);
|
||||
default:
|
||||
*len = 0;
|
||||
errno = ENOTSUP;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void *
|
||||
bpf_frame_header_dst(const struct interface *ifp, void *fh, size_t *len)
|
||||
{
|
||||
uint8_t *f = fh;
|
||||
|
||||
switch (ifp->hwtype) {
|
||||
case ARPHRD_ETHER:
|
||||
*len = sizeof(((struct ether_header *)0)->ether_dhost);
|
||||
return f + offsetof(struct ether_header, ether_dhost);
|
||||
default:
|
||||
*len = 0;
|
||||
errno = ENOTSUP;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const uint8_t etherbcastaddr[] =
|
||||
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
|
||||
int
|
||||
bpf_frame_bcast(const struct interface *ifp, const void *frame)
|
||||
{
|
||||
|
||||
switch (ifp->hwtype) {
|
||||
case ARPHRD_ETHER:
|
||||
return memcmp((const char *)frame +
|
||||
offsetof(struct ether_header, ether_dhost),
|
||||
etherbcastaddr, sizeof(etherbcastaddr));
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef __linux__
|
||||
/* Linux is a special snowflake for opening, attaching and reading BPF.
|
||||
* See if-linux.c for the Linux specific BPF functions. */
|
||||
|
||||
const char *bpf_name = "Berkley Packet Filter";
|
||||
|
||||
struct bpf *
|
||||
bpf_open(const struct interface *ifp,
|
||||
int (*filter)(const struct bpf *, const struct in_addr *),
|
||||
const struct in_addr *ia)
|
||||
{
|
||||
struct bpf *bpf;
|
||||
struct bpf_version pv = { .bv_major = 0, .bv_minor = 0 };
|
||||
struct ifreq ifr = { .ifr_flags = 0 };
|
||||
int ibuf_len = 0;
|
||||
#ifdef O_CLOEXEC
|
||||
#define BPF_OPEN_FLAGS O_RDWR | O_NONBLOCK | O_CLOEXEC
|
||||
#else
|
||||
#define BPF_OPEN_FLAGS O_RDWR | O_NONBLOCK
|
||||
#endif
|
||||
#ifdef BIOCIMMEDIATE
|
||||
unsigned int flags;
|
||||
#endif
|
||||
#ifndef O_CLOEXEC
|
||||
int fd_opts;
|
||||
#endif
|
||||
|
||||
bpf = calloc(1, sizeof(*bpf));
|
||||
if (bpf == NULL)
|
||||
return NULL;
|
||||
bpf->bpf_ifp = ifp;
|
||||
|
||||
/* /dev/bpf is a cloner on modern kernels */
|
||||
bpf->bpf_fd = open("/dev/bpf", BPF_OPEN_FLAGS);
|
||||
|
||||
/* Support older kernels where /dev/bpf is not a cloner */
|
||||
if (bpf->bpf_fd == -1) {
|
||||
char device[32];
|
||||
int n = 0;
|
||||
|
||||
do {
|
||||
snprintf(device, sizeof(device), "/dev/bpf%d", n++);
|
||||
bpf->bpf_fd = open(device, BPF_OPEN_FLAGS);
|
||||
} while (bpf->bpf_fd == -1 && errno == EBUSY);
|
||||
}
|
||||
|
||||
if (bpf->bpf_fd == -1)
|
||||
goto eexit;
|
||||
|
||||
#ifndef O_CLOEXEC
|
||||
if ((fd_opts = fcntl(bpf->bpf_fd, F_GETFD)) == -1 ||
|
||||
fcntl(bpf->bpf_fd, F_SETFD, fd_opts | FD_CLOEXEC) == -1)
|
||||
goto eexit;
|
||||
#endif
|
||||
|
||||
if (ioctl(bpf->bpf_fd, BIOCVERSION, &pv) == -1)
|
||||
goto eexit;
|
||||
if (pv.bv_major != BPF_MAJOR_VERSION ||
|
||||
pv.bv_minor < BPF_MINOR_VERSION) {
|
||||
logerrx("BPF version mismatch - recompile");
|
||||
goto eexit;
|
||||
}
|
||||
|
||||
strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name));
|
||||
if (ioctl(bpf->bpf_fd, BIOCSETIF, &ifr) == -1)
|
||||
goto eexit;
|
||||
|
||||
#ifdef BIOCIMMEDIATE
|
||||
flags = 1;
|
||||
if (ioctl(bpf->bpf_fd, BIOCIMMEDIATE, &flags) == -1)
|
||||
goto eexit;
|
||||
#endif
|
||||
|
||||
if (filter(bpf, ia) != 0)
|
||||
goto eexit;
|
||||
|
||||
/* Get the required BPF buffer length from the kernel. */
|
||||
if (ioctl(bpf->bpf_fd, BIOCGBLEN, &ibuf_len) == -1)
|
||||
goto eexit;
|
||||
bpf->bpf_size = (size_t)ibuf_len;
|
||||
bpf->bpf_buffer = malloc(bpf->bpf_size);
|
||||
if (bpf->bpf_buffer == NULL)
|
||||
goto eexit;
|
||||
return bpf;
|
||||
|
||||
eexit:
|
||||
if (bpf->bpf_fd != -1)
|
||||
close(bpf->bpf_fd);
|
||||
free(bpf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* BPF requires that we read the entire buffer.
|
||||
* So we pass the buffer in the API so we can loop on >1 packet. */
|
||||
ssize_t
|
||||
bpf_read(struct bpf *bpf, void *data, size_t len)
|
||||
{
|
||||
ssize_t bytes;
|
||||
struct bpf_hdr packet;
|
||||
const char *payload;
|
||||
|
||||
bpf->bpf_flags &= ~BPF_EOF;
|
||||
for (;;) {
|
||||
if (bpf->bpf_len == 0) {
|
||||
bytes = read(bpf->bpf_fd, bpf->bpf_buffer,
|
||||
bpf->bpf_size);
|
||||
#if defined(__sun)
|
||||
/* After 2^31 bytes, the kernel offset overflows.
|
||||
* To work around this bug, lseek 0. */
|
||||
if (bytes == -1 && errno == EINVAL) {
|
||||
lseek(bpf->bpf_fd, 0, SEEK_SET);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (bytes == -1 || bytes == 0)
|
||||
return bytes;
|
||||
bpf->bpf_len = (size_t)bytes;
|
||||
bpf->bpf_pos = 0;
|
||||
}
|
||||
bytes = -1;
|
||||
payload = (const char *)bpf->bpf_buffer + bpf->bpf_pos;
|
||||
memcpy(&packet, payload, sizeof(packet));
|
||||
if (bpf->bpf_pos + packet.bh_caplen + packet.bh_hdrlen >
|
||||
bpf->bpf_len)
|
||||
goto next; /* Packet beyond buffer, drop. */
|
||||
payload += packet.bh_hdrlen;
|
||||
if (packet.bh_caplen > len)
|
||||
bytes = (ssize_t)len;
|
||||
else
|
||||
bytes = (ssize_t)packet.bh_caplen;
|
||||
if (bpf_frame_bcast(bpf->bpf_ifp, payload) == 0)
|
||||
bpf->bpf_flags |= BPF_BCAST;
|
||||
else
|
||||
bpf->bpf_flags &= ~BPF_BCAST;
|
||||
memcpy(data, payload, (size_t)bytes);
|
||||
next:
|
||||
bpf->bpf_pos += BPF_WORDALIGN(packet.bh_hdrlen +
|
||||
packet.bh_caplen);
|
||||
if (bpf->bpf_pos >= bpf->bpf_len) {
|
||||
bpf->bpf_len = bpf->bpf_pos = 0;
|
||||
bpf->bpf_flags |= BPF_EOF;
|
||||
}
|
||||
if (bytes != -1)
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
int
|
||||
bpf_attach(int fd, void *filter, unsigned int filter_len)
|
||||
{
|
||||
struct bpf_program pf = { .bf_insns = filter, .bf_len = filter_len };
|
||||
|
||||
/* Install the filter. */
|
||||
return ioctl(fd, BIOCSETF, &pf);
|
||||
}
|
||||
|
||||
#ifdef BIOCSETWF
|
||||
static int
|
||||
bpf_wattach(int fd, void *filter, unsigned int filter_len)
|
||||
{
|
||||
struct bpf_program pf = { .bf_insns = filter, .bf_len = filter_len };
|
||||
|
||||
/* Install the filter. */
|
||||
return ioctl(fd, BIOCSETWF, &pf);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef __sun
|
||||
/* SunOS is special too - sending via BPF goes nowhere. */
|
||||
ssize_t
|
||||
bpf_send(const struct bpf *bpf, uint16_t protocol,
|
||||
const void *data, size_t len)
|
||||
{
|
||||
struct iovec iov[2];
|
||||
struct ether_header eh;
|
||||
|
||||
switch(bpf->bpf_ifp->hwtype) {
|
||||
case ARPHRD_ETHER:
|
||||
memset(&eh.ether_dhost, 0xff, sizeof(eh.ether_dhost));
|
||||
memcpy(&eh.ether_shost, bpf->bpf_ifp->hwaddr,
|
||||
sizeof(eh.ether_shost));
|
||||
eh.ether_type = htons(protocol);
|
||||
iov[0].iov_base = &eh;
|
||||
iov[0].iov_len = sizeof(eh);
|
||||
break;
|
||||
default:
|
||||
iov[0].iov_base = NULL;
|
||||
iov[0].iov_len = 0;
|
||||
break;
|
||||
}
|
||||
iov[1].iov_base = UNCONST(data);
|
||||
iov[1].iov_len = len;
|
||||
return writev(bpf->bpf_fd, iov, 2);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
bpf_close(struct bpf *bpf)
|
||||
{
|
||||
|
||||
close(bpf->bpf_fd);
|
||||
free(bpf->bpf_buffer);
|
||||
free(bpf);
|
||||
}
|
||||
|
||||
#ifdef ARP
|
||||
#define BPF_CMP_HWADDR_LEN ((((HWADDR_LEN / 4) + 2) * 2) + 1)
|
||||
static unsigned int
|
||||
bpf_cmp_hwaddr(struct bpf_insn *bpf, size_t bpf_len, size_t off,
|
||||
bool equal, const uint8_t *hwaddr, size_t hwaddr_len)
|
||||
{
|
||||
struct bpf_insn *bp;
|
||||
size_t maclen, nlft, njmps;
|
||||
uint32_t mac32;
|
||||
uint16_t mac16;
|
||||
uint8_t jt, jf;
|
||||
|
||||
/* Calc the number of jumps */
|
||||
if ((hwaddr_len / 4) >= 128) {
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
njmps = (hwaddr_len / 4) * 2; /* 2 instructions per check */
|
||||
/* We jump after the 1st check. */
|
||||
if (njmps)
|
||||
njmps -= 2;
|
||||
nlft = hwaddr_len % 4;
|
||||
if (nlft) {
|
||||
njmps += (nlft / 2) * 2;
|
||||
nlft = nlft % 2;
|
||||
if (nlft)
|
||||
njmps += 2;
|
||||
|
||||
}
|
||||
|
||||
/* Skip to positive finish. */
|
||||
njmps++;
|
||||
if (equal) {
|
||||
jt = (uint8_t)njmps;
|
||||
jf = 0;
|
||||
} else {
|
||||
jt = 0;
|
||||
jf = (uint8_t)njmps;
|
||||
}
|
||||
|
||||
bp = bpf;
|
||||
for (; hwaddr_len > 0;
|
||||
hwaddr += maclen, hwaddr_len -= maclen, off += maclen)
|
||||
{
|
||||
if (bpf_len < 3) {
|
||||
errno = ENOBUFS;
|
||||
return 0;
|
||||
}
|
||||
bpf_len -= 3;
|
||||
|
||||
if (hwaddr_len >= 4) {
|
||||
maclen = sizeof(mac32);
|
||||
memcpy(&mac32, hwaddr, maclen);
|
||||
BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND, off);
|
||||
bp++;
|
||||
BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
|
||||
htonl(mac32), jt, jf);
|
||||
} else if (hwaddr_len >= 2) {
|
||||
maclen = sizeof(mac16);
|
||||
memcpy(&mac16, hwaddr, maclen);
|
||||
BPF_SET_STMT(bp, BPF_LD + BPF_H + BPF_IND, off);
|
||||
bp++;
|
||||
BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
|
||||
htons(mac16), jt, jf);
|
||||
} else {
|
||||
maclen = sizeof(*hwaddr);
|
||||
BPF_SET_STMT(bp, BPF_LD + BPF_B + BPF_IND, off);
|
||||
bp++;
|
||||
BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K,
|
||||
*hwaddr, jt, jf);
|
||||
}
|
||||
if (jt)
|
||||
jt = (uint8_t)(jt - 2);
|
||||
if (jf)
|
||||
jf = (uint8_t)(jf - 2);
|
||||
bp++;
|
||||
}
|
||||
|
||||
/* Last step is always return failure.
|
||||
* Next step is a positive finish. */
|
||||
BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
|
||||
bp++;
|
||||
|
||||
return (unsigned int)(bp - bpf);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ARP
|
||||
static const struct bpf_insn bpf_arp_ether [] = {
|
||||
/* Check this is an ARP packet. */
|
||||
BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
|
||||
offsetof(struct ether_header, ether_type)),
|
||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_ARP, 1, 0),
|
||||
BPF_STMT(BPF_RET + BPF_K, 0),
|
||||
|
||||
/* Load frame header length into X */
|
||||
BPF_STMT(BPF_LDX + BPF_W + BPF_IMM, sizeof(struct ether_header)),
|
||||
|
||||
/* Make sure the hardware type matches. */
|
||||
BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_hrd)),
|
||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPHRD_ETHER, 1, 0),
|
||||
BPF_STMT(BPF_RET + BPF_K, 0),
|
||||
|
||||
/* Make sure the hardware length matches. */
|
||||
BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct arphdr, ar_hln)),
|
||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K,
|
||||
sizeof(((struct ether_arp *)0)->arp_sha), 1, 0),
|
||||
BPF_STMT(BPF_RET + BPF_K, 0),
|
||||
};
|
||||
#define BPF_ARP_ETHER_LEN __arraycount(bpf_arp_ether)
|
||||
|
||||
static const struct bpf_insn bpf_arp_filter [] = {
|
||||
/* Make sure this is for IP. */
|
||||
BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_pro)),
|
||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0),
|
||||
BPF_STMT(BPF_RET + BPF_K, 0),
|
||||
/* Make sure this is an ARP REQUEST. */
|
||||
BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct arphdr, ar_op)),
|
||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REQUEST, 2, 0),
|
||||
/* or ARP REPLY. */
|
||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0),
|
||||
BPF_STMT(BPF_RET + BPF_K, 0),
|
||||
/* Make sure the protocol length matches. */
|
||||
BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct arphdr, ar_pln)),
|
||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, sizeof(in_addr_t), 1, 0),
|
||||
BPF_STMT(BPF_RET + BPF_K, 0),
|
||||
};
|
||||
#define BPF_ARP_FILTER_LEN __arraycount(bpf_arp_filter)
|
||||
|
||||
/* One address is two checks of two statements. */
|
||||
#define BPF_NADDRS 1
|
||||
#define BPF_ARP_ADDRS_LEN 5 + ((BPF_NADDRS * 2) * 2)
|
||||
|
||||
#define BPF_ARP_LEN BPF_ARP_ETHER_LEN + BPF_ARP_FILTER_LEN + \
|
||||
BPF_CMP_HWADDR_LEN + BPF_ARP_ADDRS_LEN
|
||||
|
||||
static int
|
||||
bpf_arp_rw(const struct bpf *bpf, const struct in_addr *ia, bool recv)
|
||||
{
|
||||
const struct interface *ifp = bpf->bpf_ifp;
|
||||
struct bpf_insn buf[BPF_ARP_LEN + 1];
|
||||
struct bpf_insn *bp;
|
||||
uint16_t arp_len;
|
||||
|
||||
bp = buf;
|
||||
/* Check frame header. */
|
||||
switch(ifp->hwtype) {
|
||||
case ARPHRD_ETHER:
|
||||
memcpy(bp, bpf_arp_ether, sizeof(bpf_arp_ether));
|
||||
bp += BPF_ARP_ETHER_LEN;
|
||||
arp_len = sizeof(struct ether_header)+sizeof(struct ether_arp);
|
||||
break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Copy in the main filter. */
|
||||
memcpy(bp, bpf_arp_filter, sizeof(bpf_arp_filter));
|
||||
bp += BPF_ARP_FILTER_LEN;
|
||||
|
||||
/* Ensure it's not from us. */
|
||||
bp += bpf_cmp_hwaddr(bp, BPF_CMP_HWADDR_LEN, sizeof(struct arphdr),
|
||||
!recv, ifp->hwaddr, ifp->hwlen);
|
||||
|
||||
/* Match sender protocol address */
|
||||
BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND,
|
||||
sizeof(struct arphdr) + ifp->hwlen);
|
||||
bp++;
|
||||
BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, htonl(ia->s_addr), 0, 1);
|
||||
bp++;
|
||||
BPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len);
|
||||
bp++;
|
||||
|
||||
/* If we didn't match sender, then we're only interested in
|
||||
* ARP probes to us, so check the null host sender. */
|
||||
BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, INADDR_ANY, 1, 0);
|
||||
bp++;
|
||||
BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
|
||||
bp++;
|
||||
|
||||
/* Match target protocol address */
|
||||
BPF_SET_STMT(bp, BPF_LD + BPF_W + BPF_IND, (sizeof(struct arphdr) +
|
||||
(size_t)(ifp->hwlen * 2) + sizeof(in_addr_t)));
|
||||
bp++;
|
||||
BPF_SET_JUMP(bp, BPF_JMP + BPF_JEQ + BPF_K, htonl(ia->s_addr), 0, 1);
|
||||
bp++;
|
||||
BPF_SET_STMT(bp, BPF_RET + BPF_K, arp_len);
|
||||
bp++;
|
||||
|
||||
/* No match, drop it */
|
||||
BPF_SET_STMT(bp, BPF_RET + BPF_K, 0);
|
||||
bp++;
|
||||
|
||||
#ifdef BIOCSETWF
|
||||
if (!recv)
|
||||
return bpf_wattach(bpf->bpf_fd, buf, (unsigned int)(bp - buf));
|
||||
#endif
|
||||
|
||||
return bpf_attach(bpf->bpf_fd, buf, (unsigned int)(bp - buf));
|
||||
}
|
||||
|
||||
int
|
||||
bpf_arp(const struct bpf *bpf, const struct in_addr *ia)
|
||||
{
|
||||
|
||||
#ifdef BIOCSETWF
|
||||
if (bpf_arp_rw(bpf, ia, true) == -1 ||
|
||||
bpf_arp_rw(bpf, ia, false) == -1 ||
|
||||
ioctl(bpf->bpf_fd, BIOCLOCK) == -1)
|
||||
return -1;
|
||||
return 0;
|
||||
#else
|
||||
return bpf_arp_rw(bpf, ia, true);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ARPHRD_NONE
|
||||
static const struct bpf_insn bpf_bootp_none[] = {
|
||||
};
|
||||
#define BPF_BOOTP_NONE_LEN __arraycount(bpf_bootp_none)
|
||||
#endif
|
||||
|
||||
static const struct bpf_insn bpf_bootp_ether[] = {
|
||||
/* Make sure this is an IP packet. */
|
||||
BPF_STMT(BPF_LD + BPF_H + BPF_ABS,
|
||||
offsetof(struct ether_header, ether_type)),
|
||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 1, 0),
|
||||
BPF_STMT(BPF_RET + BPF_K, 0),
|
||||
|
||||
/* Advance to the IP header. */
|
||||
BPF_STMT(BPF_LDX + BPF_K, sizeof(struct ether_header)),
|
||||
};
|
||||
#define BPF_BOOTP_ETHER_LEN __arraycount(bpf_bootp_ether)
|
||||
|
||||
static const struct bpf_insn bpf_bootp_base[] = {
|
||||
/* Make sure it's an IPv4 packet. */
|
||||
BPF_STMT(BPF_LD + BPF_B + BPF_IND, 0),
|
||||
BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0xf0),
|
||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x40, 1, 0),
|
||||
BPF_STMT(BPF_RET + BPF_K, 0),
|
||||
|
||||
/* Make sure it's a UDP packet. */
|
||||
BPF_STMT(BPF_LD + BPF_B + BPF_IND, offsetof(struct ip, ip_p)),
|
||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0),
|
||||
BPF_STMT(BPF_RET + BPF_K, 0),
|
||||
|
||||
/* Make sure this isn't a fragment. */
|
||||
BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct ip, ip_off)),
|
||||
BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 0, 1),
|
||||
BPF_STMT(BPF_RET + BPF_K, 0),
|
||||
|
||||
/* Advance to the UDP header. */
|
||||
BPF_STMT(BPF_LD + BPF_B + BPF_IND, 0),
|
||||
BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x0f),
|
||||
BPF_STMT(BPF_ALU + BPF_MUL + BPF_K, 4),
|
||||
BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0),
|
||||
BPF_STMT(BPF_MISC + BPF_TAX, 0),
|
||||
};
|
||||
#define BPF_BOOTP_BASE_LEN __arraycount(bpf_bootp_base)
|
||||
|
||||
static const struct bpf_insn bpf_bootp_read[] = {
|
||||
/* Make sure it's to the right port.
|
||||
* RFC2131 makes no mention of enforcing a source port. */
|
||||
BPF_STMT(BPF_LD + BPF_H + BPF_IND, offsetof(struct udphdr, uh_dport)),
|
||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTPC, 1, 0),
|
||||
BPF_STMT(BPF_RET + BPF_K, 0),
|
||||
};
|
||||
#define BPF_BOOTP_READ_LEN __arraycount(bpf_bootp_read)
|
||||
|
||||
#ifdef BIOCSETWF
|
||||
static const struct bpf_insn bpf_bootp_write[] = {
|
||||
/* Make sure it's from and to the right port.
|
||||
* RFC2131 makes no mention of encforcing a source port,
|
||||
* but dhcpcd does enforce it for sending. */
|
||||
BPF_STMT(BPF_LD + BPF_W + BPF_IND, 0),
|
||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (BOOTPC << 16) + BOOTPS, 1, 0),
|
||||
BPF_STMT(BPF_RET + BPF_K, 0),
|
||||
};
|
||||
#define BPF_BOOTP_WRITE_LEN __arraycount(bpf_bootp_write)
|
||||
#endif
|
||||
|
||||
#define BPF_BOOTP_CHADDR_LEN ((BOOTP_CHADDR_LEN / 4) * 3)
|
||||
#define BPF_BOOTP_XID_LEN 4 /* BOUND check is 4 instructions */
|
||||
|
||||
#define BPF_BOOTP_LEN BPF_BOOTP_ETHER_LEN + \
|
||||
BPF_BOOTP_BASE_LEN + BPF_BOOTP_READ_LEN + \
|
||||
BPF_BOOTP_XID_LEN + BPF_BOOTP_CHADDR_LEN + 4
|
||||
|
||||
static int
|
||||
bpf_bootp_rw(const struct bpf *bpf, bool read)
|
||||
{
|
||||
struct bpf_insn buf[BPF_BOOTP_LEN + 1];
|
||||
struct bpf_insn *bp;
|
||||
|
||||
bp = buf;
|
||||
/* Check frame header. */
|
||||
switch(bpf->bpf_ifp->hwtype) {
|
||||
#ifdef ARPHRD_NONE
|
||||
case ARPHRD_NONE:
|
||||
memcpy(bp, bpf_bootp_none, sizeof(bpf_bootp_none));
|
||||
bp += BPF_BOOTP_NONE_LEN;
|
||||
break;
|
||||
#endif
|
||||
case ARPHRD_ETHER:
|
||||
memcpy(bp, bpf_bootp_ether, sizeof(bpf_bootp_ether));
|
||||
bp += BPF_BOOTP_ETHER_LEN;
|
||||
break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Copy in the main filter. */
|
||||
memcpy(bp, bpf_bootp_base, sizeof(bpf_bootp_base));
|
||||
bp += BPF_BOOTP_BASE_LEN;
|
||||
|
||||
#ifdef BIOCSETWF
|
||||
if (!read) {
|
||||
memcpy(bp, bpf_bootp_write, sizeof(bpf_bootp_write));
|
||||
bp += BPF_BOOTP_WRITE_LEN;
|
||||
|
||||
/* All passed, return the packet. */
|
||||
BPF_SET_STMT(bp, BPF_RET + BPF_K, BPF_WHOLEPACKET);
|
||||
bp++;
|
||||
|
||||
return bpf_wattach(bpf->bpf_fd, buf, (unsigned int)(bp - buf));
|
||||
}
|
||||
#else
|
||||
UNUSED(read);
|
||||
#endif
|
||||
|
||||
memcpy(bp, bpf_bootp_read, sizeof(bpf_bootp_read));
|
||||
bp += BPF_BOOTP_READ_LEN;
|
||||
|
||||
/* All passed, return the packet. */
|
||||
BPF_SET_STMT(bp, BPF_RET + BPF_K, BPF_WHOLEPACKET);
|
||||
bp++;
|
||||
|
||||
return bpf_attach(bpf->bpf_fd, buf, (unsigned int)(bp - buf));
|
||||
}
|
||||
|
||||
int
|
||||
bpf_bootp(const struct bpf *bpf, __unused const struct in_addr *ia)
|
||||
{
|
||||
|
||||
#ifdef BIOCSETWF
|
||||
if (bpf_bootp_rw(bpf, true) == -1 ||
|
||||
bpf_bootp_rw(bpf, false) == -1 ||
|
||||
ioctl(bpf->bpf_fd, BIOCLOCK) == -1)
|
||||
return -1;
|
||||
return 0;
|
||||
#else
|
||||
#ifdef PRIVSEP
|
||||
#if defined(__sun) /* Solaris cannot send via BPF. */
|
||||
#elif defined(BIOCSETF)
|
||||
#warning No BIOCSETWF support - a compromised BPF can be used as a raw socket
|
||||
#else
|
||||
#warning A compromised PF_PACKET socket can be used as a raw socket
|
||||
#endif
|
||||
#endif
|
||||
return bpf_bootp_rw(bpf, true);
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd: BPF arp and bootp filtering
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef BPF_HEADER
|
||||
#define BPF_HEADER
|
||||
|
||||
#define BPF_EOF 0x01U
|
||||
#define BPF_PARTIALCSUM 0x02U
|
||||
#define BPF_BCAST 0x04U
|
||||
|
||||
/*
|
||||
* Even though we program the BPF filter should we trust it?
|
||||
* On Linux at least there is a window between opening the socket,
|
||||
* binding the interface and setting the filter where we receive data.
|
||||
* This data is NOT checked OR flushed and IS returned when reading.
|
||||
* We have no way of flushing it other than reading these packets!
|
||||
* But we don't know if they passed the filter or not ..... so we need
|
||||
* to validate each and every packet that comes through ourselves as well.
|
||||
* Even if Linux does fix this sorry state, who is to say other kernels
|
||||
* don't have bugs causing a similar effect?
|
||||
*
|
||||
* As such, let's strive to keep the filters just for pattern matching
|
||||
* to avoid waking dhcpcd up.
|
||||
*
|
||||
* If you want to be notified of any packet failing the BPF filter,
|
||||
* define BPF_DEBUG below.
|
||||
*/
|
||||
//#define BPF_DEBUG
|
||||
|
||||
#include "dhcpcd.h"
|
||||
|
||||
struct bpf {
|
||||
const struct interface *bpf_ifp;
|
||||
int bpf_fd;
|
||||
unsigned int bpf_flags;
|
||||
void *bpf_buffer;
|
||||
size_t bpf_size;
|
||||
size_t bpf_len;
|
||||
size_t bpf_pos;
|
||||
};
|
||||
|
||||
extern const char *bpf_name;
|
||||
size_t bpf_frame_header_len(const struct interface *);
|
||||
void *bpf_frame_header_src(const struct interface *, void *, size_t *);
|
||||
void *bpf_frame_header_dst(const struct interface *, void *, size_t *);
|
||||
int bpf_frame_bcast(const struct interface *, const void *);
|
||||
struct bpf * bpf_open(const struct interface *,
|
||||
int (*)(const struct bpf *, const struct in_addr *),
|
||||
const struct in_addr *);
|
||||
void bpf_close(struct bpf *);
|
||||
int bpf_attach(int, void *, unsigned int);
|
||||
ssize_t bpf_send(const struct bpf *, uint16_t, const void *, size_t);
|
||||
ssize_t bpf_read(struct bpf *, void *, size_t);
|
||||
int bpf_arp(const struct bpf *, const struct in_addr *);
|
||||
int bpf_bootp(const struct bpf *, const struct in_addr *);
|
||||
#endif
|
|
@ -0,0 +1,216 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "dhcpcd.h"
|
||||
#include "if-options.h"
|
||||
|
||||
const char *
|
||||
hwaddr_ntoa(const void *hwaddr, size_t hwlen, char *buf, size_t buflen)
|
||||
{
|
||||
const unsigned char *hp, *ep;
|
||||
char *p;
|
||||
|
||||
/* Allow a hwlen of 0 to be an empty string. */
|
||||
if (buf == NULL || buflen == 0) {
|
||||
errno = ENOBUFS;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (hwlen * 3 > buflen) {
|
||||
/* We should still terminate the string just in case. */
|
||||
buf[0] = '\0';
|
||||
errno = ENOBUFS;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hp = hwaddr;
|
||||
ep = hp + hwlen;
|
||||
p = buf;
|
||||
while (hp < ep) {
|
||||
if (hp != hwaddr)
|
||||
*p ++= ':';
|
||||
p += snprintf(p, 3, "%.2x", *hp++);
|
||||
}
|
||||
*p ++= '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
size_t
|
||||
hwaddr_aton(uint8_t *buffer, const char *addr)
|
||||
{
|
||||
char c[3];
|
||||
const char *p = addr;
|
||||
uint8_t *bp = buffer;
|
||||
size_t len = 0;
|
||||
|
||||
c[2] = '\0';
|
||||
while (*p != '\0') {
|
||||
/* Skip separators */
|
||||
c[0] = *p++;
|
||||
switch (c[0]) {
|
||||
case '\n': /* long duid split on lines */
|
||||
case ':': /* typical mac address */
|
||||
case '-': /* uuid */
|
||||
continue;
|
||||
}
|
||||
c[1] = *p++;
|
||||
/* Ensure that digits are hex */
|
||||
if (isxdigit((unsigned char)c[0]) == 0 ||
|
||||
isxdigit((unsigned char)c[1]) == 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
/* We should have at least two entries 00:01 */
|
||||
if (len == 0 && *p == '\0') {
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
if (bp)
|
||||
*bp++ = (uint8_t)strtol(c, NULL, 16);
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
readfile(const char *file, void *data, size_t len)
|
||||
{
|
||||
int fd;
|
||||
ssize_t bytes;
|
||||
|
||||
fd = open(file, O_RDONLY);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
bytes = read(fd, data, len);
|
||||
close(fd);
|
||||
if ((size_t)bytes == len) {
|
||||
errno = ENOBUFS;
|
||||
return -1;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
writefile(const char *file, mode_t mode, const void *data, size_t len)
|
||||
{
|
||||
int fd;
|
||||
ssize_t bytes;
|
||||
|
||||
fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
bytes = write(fd, data, len);
|
||||
close(fd);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
int
|
||||
filemtime(const char *file, time_t *time)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (stat(file, &st) == -1)
|
||||
return -1;
|
||||
*time = st.st_mtime;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Handy routine to read very long lines in text files.
|
||||
* This means we read the whole line and avoid any nasty buffer overflows.
|
||||
* We strip leading space and avoid comment lines, making the code that calls
|
||||
* us smaller. */
|
||||
char *
|
||||
get_line(char ** __restrict buf, ssize_t * __restrict buflen)
|
||||
{
|
||||
char *p, *c;
|
||||
bool quoted;
|
||||
|
||||
do {
|
||||
p = *buf;
|
||||
if (*buf == NULL)
|
||||
return NULL;
|
||||
c = memchr(*buf, '\n', (size_t)*buflen);
|
||||
if (c == NULL) {
|
||||
c = memchr(*buf, '\0', (size_t)*buflen);
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
*buflen = c - *buf;
|
||||
*buf = NULL;
|
||||
} else {
|
||||
*c++ = '\0';
|
||||
*buflen -= c - *buf;
|
||||
*buf = c;
|
||||
}
|
||||
for (; *p == ' ' || *p == '\t'; p++)
|
||||
;
|
||||
} while (*p == '\0' || *p == '\n' || *p == '#' || *p == ';');
|
||||
|
||||
/* Strip embedded comments unless in a quoted string or escaped */
|
||||
quoted = false;
|
||||
for (c = p; *c != '\0'; c++) {
|
||||
if (*c == '\\') {
|
||||
c++; /* escaped */
|
||||
continue;
|
||||
}
|
||||
if (*c == '"')
|
||||
quoted = !quoted;
|
||||
else if (*c == '#' && !quoted) {
|
||||
*c = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
is_root_local(void)
|
||||
{
|
||||
#ifdef ST_LOCAL
|
||||
struct statvfs vfs;
|
||||
|
||||
if (statvfs("/", &vfs) == -1)
|
||||
return -1;
|
||||
return vfs.f_flag & ST_LOCAL ? 1 : 0;
|
||||
#else
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* Define eloop queues here, as other apps share eloop.h */
|
||||
#define ELOOP_DHCPCD 1 /* default queue */
|
||||
#define ELOOP_DHCP 2
|
||||
#define ELOOP_ARP 3
|
||||
#define ELOOP_IPV4LL 4
|
||||
#define ELOOP_IPV6 5
|
||||
#define ELOOP_IPV6ND 6
|
||||
#define ELOOP_IPV6RA_EXPIRE 7
|
||||
#define ELOOP_DHCP6 8
|
||||
#define ELOOP_IF 9
|
||||
|
||||
#ifndef HOSTNAME_MAX_LEN
|
||||
#define HOSTNAME_MAX_LEN 250 /* 255 - 3 (FQDN) - 2 (DNS enc) */
|
||||
#endif
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a,b) ((/*CONSTCOND*/(a)<(b))?(a):(b))
|
||||
#define MAX(a,b) ((/*CONSTCOND*/(a)>(b))?(a):(b))
|
||||
#endif
|
||||
|
||||
#define UNCONST(a) ((void *)(unsigned long)(const void *)(a))
|
||||
#define STRINGIFY(a) #a
|
||||
#define TOSTRING(a) STRINGIFY(a)
|
||||
#define UNUSED(a) (void)(a)
|
||||
|
||||
#define ROUNDUP4(a) (1 + (((a) - 1) | 3))
|
||||
#define ROUNDUP8(a) (1 + (((a) - 1) | 7))
|
||||
|
||||
/* Some systems don't define timespec macros */
|
||||
#ifndef timespecclear
|
||||
#define timespecclear(tsp) (tsp)->tv_sec = (time_t)((tsp)->tv_nsec = 0L)
|
||||
#define timespecisset(tsp) ((tsp)->tv_sec || (tsp)->tv_nsec)
|
||||
#endif
|
||||
|
||||
#if __GNUC__ > 2 || defined(__INTEL_COMPILER)
|
||||
# ifndef __packed
|
||||
# define __packed __attribute__((__packed__))
|
||||
# endif
|
||||
# ifndef __unused
|
||||
# define __unused __attribute__((__unused__))
|
||||
# endif
|
||||
#else
|
||||
# ifndef __packed
|
||||
# define __packed
|
||||
# endif
|
||||
# ifndef __unused
|
||||
# define __unused
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Needed for rbtree(3) compat */
|
||||
#ifndef __RCSID
|
||||
#define __RCSID(a)
|
||||
#endif
|
||||
#ifndef __predict_false
|
||||
# if __GNUC__ > 2
|
||||
# define __predict_true(exp) __builtin_expect((exp) != 0, 1)
|
||||
# define __predict_false(exp) __builtin_expect((exp) != 0, 0)
|
||||
#else
|
||||
# define __predict_true(exp) (exp)
|
||||
# define __predict_false(exp) (exp)
|
||||
# endif
|
||||
#endif
|
||||
#ifndef __BEGIN_DECLS
|
||||
# if defined(__cplusplus)
|
||||
# define __BEGIN_DECLS extern "C" {
|
||||
# define __END_DECLS };
|
||||
# else /* __BEGIN_DECLS */
|
||||
# define __BEGIN_DECLS
|
||||
# define __END_DECLS
|
||||
# endif /* __BEGIN_DECLS */
|
||||
#endif /* __BEGIN_DECLS */
|
||||
|
||||
#ifndef __fallthrough
|
||||
# if __GNUC__ >= 7
|
||||
# define __fallthrough __attribute__((fallthrough))
|
||||
# else
|
||||
# define __fallthrough
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Compile Time Assertion.
|
||||
*/
|
||||
#ifndef __CTASSERT
|
||||
# ifdef __COUNTER__
|
||||
# define __CTASSERT(x) __CTASSERT0(x, __ctassert, __COUNTER__)
|
||||
# else
|
||||
# define __CTASSERT(x) __CTASSERT99(x, __INCLUDE_LEVEL__, __LINE__)
|
||||
# define __CTASSERT99(x, a, b) __CTASSERT0(x, __CONCAT(__ctassert,a), \
|
||||
__CONCAT(_,b))
|
||||
# endif
|
||||
# define __CTASSERT0(x, y, z) __CTASSERT1(x, y, z)
|
||||
# define __CTASSERT1(x, y, z) typedef char y ## z[/*CONSTCOND*/(x) ? 1 : -1] __unused
|
||||
#endif
|
||||
|
||||
#ifndef __arraycount
|
||||
# define __arraycount(__x) (sizeof(__x) / sizeof(__x[0]))
|
||||
#endif
|
||||
|
||||
/* We don't really need this as our supported systems define __restrict
|
||||
* automatically for us, but it is here for completeness. */
|
||||
#ifndef __restrict
|
||||
# if defined(__lint__)
|
||||
# define __restrict
|
||||
# elif __STDC_VERSION__ >= 199901L
|
||||
# define __restrict restrict
|
||||
# elif !(2 < __GNUC__ || (2 == __GNU_C && 95 <= __GNUC_VERSION__))
|
||||
# define __restrict
|
||||
# endif
|
||||
#endif
|
||||
|
||||
const char *hwaddr_ntoa(const void *, size_t, char *, size_t);
|
||||
size_t hwaddr_aton(uint8_t *, const char *);
|
||||
ssize_t readfile(const char *, void *, size_t);
|
||||
ssize_t writefile(const char *, mode_t, const void *, size_t);
|
||||
int filemtime(const char *, time_t *);
|
||||
char *get_line(char ** __restrict, ssize_t * __restrict);
|
||||
int is_root_local(void);
|
||||
#endif
|
|
@ -0,0 +1,634 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "common.h"
|
||||
#include "dhcpcd.h"
|
||||
#include "control.h"
|
||||
#include "eloop.h"
|
||||
#include "if.h"
|
||||
#include "logerr.h"
|
||||
#include "privsep.h"
|
||||
|
||||
#ifndef SUN_LEN
|
||||
#define SUN_LEN(su) \
|
||||
(sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
|
||||
#endif
|
||||
|
||||
static void control_handle_data(void *, unsigned short);
|
||||
|
||||
static void
|
||||
control_queue_free(struct fd_list *fd)
|
||||
{
|
||||
struct fd_data *fdp;
|
||||
|
||||
while ((fdp = TAILQ_FIRST(&fd->queue))) {
|
||||
TAILQ_REMOVE(&fd->queue, fdp, next);
|
||||
if (fdp->data_size != 0)
|
||||
free(fdp->data);
|
||||
free(fdp);
|
||||
}
|
||||
|
||||
#ifdef CTL_FREE_LIST
|
||||
while ((fdp = TAILQ_FIRST(&fd->free_queue))) {
|
||||
TAILQ_REMOVE(&fd->free_queue, fdp, next);
|
||||
if (fdp->data_size != 0)
|
||||
free(fdp->data);
|
||||
free(fdp);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
control_free(struct fd_list *fd)
|
||||
{
|
||||
|
||||
#ifdef PRIVSEP
|
||||
if (fd->ctx->ps_control_client == fd)
|
||||
fd->ctx->ps_control_client = NULL;
|
||||
#endif
|
||||
|
||||
eloop_event_delete(fd->ctx->eloop, fd->fd);
|
||||
close(fd->fd);
|
||||
TAILQ_REMOVE(&fd->ctx->control_fds, fd, next);
|
||||
control_queue_free(fd);
|
||||
free(fd);
|
||||
}
|
||||
|
||||
static void
|
||||
control_hangup(struct fd_list *fd)
|
||||
{
|
||||
|
||||
#ifdef PRIVSEP
|
||||
if (IN_PRIVSEP(fd->ctx)) {
|
||||
if (ps_ctl_sendeof(fd) == -1)
|
||||
logerr(__func__);
|
||||
}
|
||||
#endif
|
||||
control_free(fd);
|
||||
}
|
||||
|
||||
static int
|
||||
control_handle_read(struct fd_list *fd)
|
||||
{
|
||||
char buffer[1024];
|
||||
ssize_t bytes;
|
||||
|
||||
bytes = read(fd->fd, buffer, sizeof(buffer) - 1);
|
||||
if (bytes == -1)
|
||||
logerr(__func__);
|
||||
if (bytes == -1 || bytes == 0) {
|
||||
control_hangup(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef PRIVSEP
|
||||
if (IN_PRIVSEP(fd->ctx)) {
|
||||
ssize_t err;
|
||||
|
||||
fd->flags |= FD_SENDLEN;
|
||||
err = ps_ctl_handleargs(fd, buffer, (size_t)bytes);
|
||||
fd->flags &= ~FD_SENDLEN;
|
||||
if (err == -1) {
|
||||
logerr(__func__);
|
||||
return 0;
|
||||
}
|
||||
if (err == 1 &&
|
||||
ps_ctl_sendargs(fd, buffer, (size_t)bytes) == -1) {
|
||||
logerr(__func__);
|
||||
control_free(fd);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
control_recvdata(fd, buffer, (size_t)bytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
control_handle_write(struct fd_list *fd)
|
||||
{
|
||||
struct iovec iov[2];
|
||||
int iov_len;
|
||||
struct fd_data *data;
|
||||
|
||||
data = TAILQ_FIRST(&fd->queue);
|
||||
|
||||
if (data->data_flags & FD_SENDLEN) {
|
||||
iov[0].iov_base = &data->data_len;
|
||||
iov[0].iov_len = sizeof(size_t);
|
||||
iov[1].iov_base = data->data;
|
||||
iov[1].iov_len = data->data_len;
|
||||
iov_len = 2;
|
||||
} else {
|
||||
iov[0].iov_base = data->data;
|
||||
iov[0].iov_len = data->data_len;
|
||||
iov_len = 1;
|
||||
}
|
||||
|
||||
if (writev(fd->fd, iov, iov_len) == -1) {
|
||||
if (errno != EPIPE && errno != ENOTCONN) {
|
||||
// We don't get ELE_HANGUP for some reason
|
||||
logerr("%s: write", __func__);
|
||||
}
|
||||
control_hangup(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
TAILQ_REMOVE(&fd->queue, data, next);
|
||||
#ifdef CTL_FREE_LIST
|
||||
TAILQ_INSERT_TAIL(&fd->free_queue, data, next);
|
||||
#else
|
||||
if (data->data_size != 0)
|
||||
free(data->data);
|
||||
free(data);
|
||||
#endif
|
||||
|
||||
if (TAILQ_FIRST(&fd->queue) != NULL)
|
||||
return 0;
|
||||
|
||||
#ifdef PRIVSEP
|
||||
if (IN_PRIVSEP_SE(fd->ctx) && !(fd->flags & FD_LISTEN)) {
|
||||
if (ps_ctl_sendeof(fd) == -1)
|
||||
logerr(__func__);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Done sending data, stop watching write to fd */
|
||||
if (eloop_event_add(fd->ctx->eloop, fd->fd, ELE_READ,
|
||||
control_handle_data, fd) == -1)
|
||||
logerr("%s: eloop_event_add", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
control_handle_data(void *arg, unsigned short events)
|
||||
{
|
||||
struct fd_list *fd = arg;
|
||||
|
||||
if (!(events & (ELE_READ | ELE_WRITE | ELE_HANGUP)))
|
||||
logerrx("%s: unexpected event 0x%04x", __func__, events);
|
||||
|
||||
if (events & ELE_WRITE && !(events & ELE_HANGUP)) {
|
||||
if (control_handle_write(fd) == -1)
|
||||
return;
|
||||
}
|
||||
if (events & ELE_READ) {
|
||||
if (control_handle_read(fd) == -1)
|
||||
return;
|
||||
}
|
||||
if (events & ELE_HANGUP)
|
||||
control_hangup(fd);
|
||||
}
|
||||
|
||||
void
|
||||
control_recvdata(struct fd_list *fd, char *data, size_t len)
|
||||
{
|
||||
char *p = data, *e;
|
||||
char *argvp[255], **ap;
|
||||
int argc;
|
||||
|
||||
/* Each command is \n terminated
|
||||
* Each argument is NULL separated */
|
||||
while (len != 0) {
|
||||
argc = 0;
|
||||
ap = argvp;
|
||||
while (len != 0) {
|
||||
if (*p == '\0') {
|
||||
p++;
|
||||
len--;
|
||||
continue;
|
||||
}
|
||||
e = memchr(p, '\0', len);
|
||||
if (e == NULL) {
|
||||
errno = EINVAL;
|
||||
logerrx("%s: no terminator", __func__);
|
||||
return;
|
||||
}
|
||||
if ((size_t)argc >= sizeof(argvp) / sizeof(argvp[0])) {
|
||||
errno = ENOBUFS;
|
||||
logerrx("%s: no arg buffer", __func__);
|
||||
return;
|
||||
}
|
||||
*ap++ = p;
|
||||
argc++;
|
||||
e++;
|
||||
len -= (size_t)(e - p);
|
||||
p = e;
|
||||
e--;
|
||||
if (*(--e) == '\n') {
|
||||
*e = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (argc == 0) {
|
||||
logerrx("%s: no args", __func__);
|
||||
continue;
|
||||
}
|
||||
*ap = NULL;
|
||||
if (dhcpcd_handleargs(fd->ctx, fd, argc, argvp) == -1) {
|
||||
logerr(__func__);
|
||||
if (errno != EINTR && errno != EAGAIN) {
|
||||
control_free(fd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct fd_list *
|
||||
control_new(struct dhcpcd_ctx *ctx, int fd, unsigned int flags)
|
||||
{
|
||||
struct fd_list *l;
|
||||
|
||||
l = malloc(sizeof(*l));
|
||||
if (l == NULL)
|
||||
return NULL;
|
||||
|
||||
l->ctx = ctx;
|
||||
l->fd = fd;
|
||||
l->flags = flags;
|
||||
TAILQ_INIT(&l->queue);
|
||||
#ifdef CTL_FREE_LIST
|
||||
TAILQ_INIT(&l->free_queue);
|
||||
#endif
|
||||
TAILQ_INSERT_TAIL(&ctx->control_fds, l, next);
|
||||
return l;
|
||||
}
|
||||
|
||||
static void
|
||||
control_handle1(struct dhcpcd_ctx *ctx, int lfd, unsigned int fd_flags,
|
||||
unsigned short events)
|
||||
{
|
||||
struct sockaddr_un run;
|
||||
socklen_t len;
|
||||
struct fd_list *l;
|
||||
int fd, flags;
|
||||
|
||||
if (events != ELE_READ)
|
||||
logerrx("%s: unexpected event 0x%04x", __func__, events);
|
||||
|
||||
len = sizeof(run);
|
||||
if ((fd = accept(lfd, (struct sockaddr *)&run, &len)) == -1)
|
||||
goto error;
|
||||
if ((flags = fcntl(fd, F_GETFD, 0)) == -1 ||
|
||||
fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
|
||||
goto error;
|
||||
if ((flags = fcntl(fd, F_GETFL, 0)) == -1 ||
|
||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
|
||||
goto error;
|
||||
|
||||
#ifdef PRIVSEP
|
||||
if (IN_PRIVSEP(ctx) && !IN_PRIVSEP_SE(ctx))
|
||||
;
|
||||
else
|
||||
#endif
|
||||
fd_flags |= FD_SENDLEN;
|
||||
|
||||
l = control_new(ctx, fd, fd_flags);
|
||||
if (l == NULL)
|
||||
goto error;
|
||||
|
||||
if (eloop_event_add(ctx->eloop, l->fd, ELE_READ,
|
||||
control_handle_data, l) == -1)
|
||||
logerr("%s: eloop_event_add", __func__);
|
||||
return;
|
||||
|
||||
error:
|
||||
logerr(__func__);
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void
|
||||
control_handle(void *arg, unsigned short events)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = arg;
|
||||
|
||||
control_handle1(ctx, ctx->control_fd, 0, events);
|
||||
}
|
||||
|
||||
static void
|
||||
control_handle_unpriv(void *arg, unsigned short events)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = arg;
|
||||
|
||||
control_handle1(ctx, ctx->control_unpriv_fd, FD_UNPRIV, events);
|
||||
}
|
||||
|
||||
static int
|
||||
make_path(char *path, size_t len, const char *ifname, sa_family_t family,
|
||||
bool unpriv)
|
||||
{
|
||||
const char *per;
|
||||
const char *sunpriv;
|
||||
|
||||
switch(family) {
|
||||
case AF_INET:
|
||||
per = "-4";
|
||||
break;
|
||||
case AF_INET6:
|
||||
per = "-6";
|
||||
break;
|
||||
default:
|
||||
per = "";
|
||||
break;
|
||||
}
|
||||
if (unpriv)
|
||||
sunpriv = ifname ? ".unpriv" : "unpriv.";
|
||||
else
|
||||
sunpriv = "";
|
||||
return snprintf(path, len, CONTROLSOCKET,
|
||||
ifname ? ifname : "", ifname ? per : "",
|
||||
sunpriv, ifname ? "." : "");
|
||||
}
|
||||
|
||||
static int
|
||||
make_sock(struct sockaddr_un *sa, const char *ifname, sa_family_t family,
|
||||
bool unpriv)
|
||||
{
|
||||
int fd;
|
||||
|
||||
if ((fd = xsocket(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0)) == -1)
|
||||
return -1;
|
||||
memset(sa, 0, sizeof(*sa));
|
||||
sa->sun_family = AF_UNIX;
|
||||
make_path(sa->sun_path, sizeof(sa->sun_path), ifname, family, unpriv);
|
||||
return fd;
|
||||
}
|
||||
|
||||
#define S_PRIV (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
|
||||
#define S_UNPRIV (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
|
||||
|
||||
static int
|
||||
control_start1(struct dhcpcd_ctx *ctx, const char *ifname, sa_family_t family,
|
||||
mode_t fmode)
|
||||
{
|
||||
struct sockaddr_un sa;
|
||||
int fd;
|
||||
socklen_t len;
|
||||
|
||||
fd = make_sock(&sa, ifname, family, (fmode & S_UNPRIV) == S_UNPRIV);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
len = (socklen_t)SUN_LEN(&sa);
|
||||
unlink(sa.sun_path);
|
||||
if (bind(fd, (struct sockaddr *)&sa, len) == -1 ||
|
||||
chmod(sa.sun_path, fmode) == -1 ||
|
||||
(ctx->control_group &&
|
||||
chown(sa.sun_path, geteuid(), ctx->control_group) == -1) ||
|
||||
listen(fd, sizeof(ctx->control_fds)) == -1)
|
||||
{
|
||||
close(fd);
|
||||
unlink(sa.sun_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef PRIVSEP_RIGHTS
|
||||
if (IN_PRIVSEP(ctx) && ps_rights_limit_fd_fctnl(fd) == -1) {
|
||||
close(fd);
|
||||
unlink(sa.sun_path);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((fmode & S_UNPRIV) == S_UNPRIV)
|
||||
strlcpy(ctx->control_sock_unpriv, sa.sun_path,
|
||||
sizeof(ctx->control_sock_unpriv));
|
||||
else
|
||||
strlcpy(ctx->control_sock, sa.sun_path,
|
||||
sizeof(ctx->control_sock));
|
||||
return fd;
|
||||
}
|
||||
|
||||
int
|
||||
control_start(struct dhcpcd_ctx *ctx, const char *ifname, sa_family_t family)
|
||||
{
|
||||
int fd;
|
||||
|
||||
#ifdef PRIVSEP
|
||||
if (IN_PRIVSEP_SE(ctx)) {
|
||||
make_path(ctx->control_sock, sizeof(ctx->control_sock),
|
||||
ifname, family, false);
|
||||
make_path(ctx->control_sock_unpriv,
|
||||
sizeof(ctx->control_sock_unpriv),
|
||||
ifname, family, true);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((fd = control_start1(ctx, ifname, family, S_PRIV)) == -1)
|
||||
return -1;
|
||||
|
||||
ctx->control_fd = fd;
|
||||
if (eloop_event_add(ctx->eloop, fd, ELE_READ,
|
||||
control_handle, ctx) == -1)
|
||||
logerr("%s: eloop_event_add", __func__);
|
||||
|
||||
if ((fd = control_start1(ctx, ifname, family, S_UNPRIV)) != -1) {
|
||||
ctx->control_unpriv_fd = fd;
|
||||
if (eloop_event_add(ctx->eloop, fd, ELE_READ,
|
||||
control_handle_unpriv, ctx) == -1)
|
||||
logerr("%s: eloop_event_add", __func__);
|
||||
}
|
||||
return ctx->control_fd;
|
||||
}
|
||||
|
||||
static int
|
||||
control_unlink(struct dhcpcd_ctx *ctx, const char *file)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
errno = 0;
|
||||
#ifdef PRIVSEP
|
||||
if (IN_PRIVSEP(ctx))
|
||||
retval = (int)ps_root_unlink(ctx, file);
|
||||
else
|
||||
#else
|
||||
UNUSED(ctx);
|
||||
#endif
|
||||
retval = unlink(file);
|
||||
|
||||
return retval == -1 && errno != ENOENT ? -1 : 0;
|
||||
}
|
||||
|
||||
int
|
||||
control_stop(struct dhcpcd_ctx *ctx)
|
||||
{
|
||||
int retval = 0;
|
||||
struct fd_list *l;
|
||||
|
||||
while ((l = TAILQ_FIRST(&ctx->control_fds)) != NULL) {
|
||||
control_free(l);
|
||||
}
|
||||
|
||||
#ifdef PRIVSEP
|
||||
if (IN_PRIVSEP_SE(ctx)) {
|
||||
if (ctx->control_sock[0] != '\0' &&
|
||||
ps_root_unlink(ctx, ctx->control_sock) == -1)
|
||||
retval = -1;
|
||||
if (ctx->control_sock_unpriv[0] != '\0' &&
|
||||
ps_root_unlink(ctx, ctx->control_sock_unpriv) == -1)
|
||||
retval = -1;
|
||||
return retval;
|
||||
} else if (ctx->options & DHCPCD_FORKED)
|
||||
return retval;
|
||||
#endif
|
||||
|
||||
if (ctx->control_fd != -1) {
|
||||
eloop_event_delete(ctx->eloop, ctx->control_fd);
|
||||
close(ctx->control_fd);
|
||||
ctx->control_fd = -1;
|
||||
if (control_unlink(ctx, ctx->control_sock) == -1)
|
||||
retval = -1;
|
||||
}
|
||||
|
||||
if (ctx->control_unpriv_fd != -1) {
|
||||
eloop_event_delete(ctx->eloop, ctx->control_unpriv_fd);
|
||||
close(ctx->control_unpriv_fd);
|
||||
ctx->control_unpriv_fd = -1;
|
||||
if (control_unlink(ctx, ctx->control_sock_unpriv) == -1)
|
||||
retval = -1;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
control_open(const char *ifname, sa_family_t family, bool unpriv)
|
||||
{
|
||||
struct sockaddr_un sa;
|
||||
int fd;
|
||||
|
||||
if ((fd = make_sock(&sa, ifname, family, unpriv)) != -1) {
|
||||
socklen_t len;
|
||||
|
||||
len = (socklen_t)SUN_LEN(&sa);
|
||||
if (connect(fd, (struct sockaddr *)&sa, len) == -1) {
|
||||
close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
control_send(struct dhcpcd_ctx *ctx, int argc, char * const *argv)
|
||||
{
|
||||
char buffer[1024];
|
||||
int i;
|
||||
size_t len, l;
|
||||
|
||||
if (argc > 255) {
|
||||
errno = ENOBUFS;
|
||||
return -1;
|
||||
}
|
||||
len = 0;
|
||||
for (i = 0; i < argc; i++) {
|
||||
l = strlen(argv[i]) + 1;
|
||||
if (len + l > sizeof(buffer)) {
|
||||
errno = ENOBUFS;
|
||||
return -1;
|
||||
}
|
||||
memcpy(buffer + len, argv[i], l);
|
||||
len += l;
|
||||
}
|
||||
return write(ctx->control_fd, buffer, len);
|
||||
}
|
||||
|
||||
int
|
||||
control_queue(struct fd_list *fd, void *data, size_t data_len)
|
||||
{
|
||||
struct fd_data *d;
|
||||
unsigned short events;
|
||||
|
||||
if (data_len == 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef CTL_FREE_LIST
|
||||
struct fd_data *df;
|
||||
|
||||
d = NULL;
|
||||
TAILQ_FOREACH(df, &fd->free_queue, next) {
|
||||
if (d == NULL || d->data_size < df->data_size) {
|
||||
d = df;
|
||||
if (d->data_size <= data_len)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (d != NULL)
|
||||
TAILQ_REMOVE(&fd->free_queue, d, next);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
d = calloc(1, sizeof(*d));
|
||||
if (d == NULL)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (d->data_size == 0)
|
||||
d->data = NULL;
|
||||
if (d->data_size < data_len) {
|
||||
void *nbuf = realloc(d->data, data_len);
|
||||
if (nbuf == NULL) {
|
||||
free(d->data);
|
||||
free(d);
|
||||
return -1;
|
||||
}
|
||||
d->data = nbuf;
|
||||
d->data_size = data_len;
|
||||
}
|
||||
memcpy(d->data, data, data_len);
|
||||
d->data_len = data_len;
|
||||
d->data_flags = fd->flags & FD_SENDLEN;
|
||||
|
||||
TAILQ_INSERT_TAIL(&fd->queue, d, next);
|
||||
events = ELE_WRITE;
|
||||
if (fd->flags & FD_LISTEN)
|
||||
events |= ELE_READ;
|
||||
return eloop_event_add(fd->ctx->eloop, fd->fd, events,
|
||||
control_handle_data, fd);
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef CONTROL_H
|
||||
#define CONTROL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "dhcpcd.h"
|
||||
|
||||
#if !defined(CTL_FREE_LIST)
|
||||
#define CTL_FREE_LIST 1
|
||||
#elif CTL_FREE_LIST == 0
|
||||
#undef CTL_FREE_LIST
|
||||
#endif
|
||||
|
||||
/* Limit queue size per fd */
|
||||
#define CONTROL_QUEUE_MAX 100
|
||||
|
||||
struct fd_data {
|
||||
TAILQ_ENTRY(fd_data) next;
|
||||
void *data;
|
||||
size_t data_size;
|
||||
size_t data_len;
|
||||
unsigned int data_flags;
|
||||
};
|
||||
TAILQ_HEAD(fd_data_head, fd_data);
|
||||
|
||||
struct fd_list {
|
||||
TAILQ_ENTRY(fd_list) next;
|
||||
struct dhcpcd_ctx *ctx;
|
||||
int fd;
|
||||
unsigned int flags;
|
||||
struct fd_data_head queue;
|
||||
#ifdef CTL_FREE_LIST
|
||||
struct fd_data_head free_queue;
|
||||
#endif
|
||||
};
|
||||
TAILQ_HEAD(fd_list_head, fd_list);
|
||||
|
||||
#define FD_LISTEN 0x01U
|
||||
#define FD_UNPRIV 0x02U
|
||||
#define FD_SENDLEN 0x04U
|
||||
|
||||
int control_start(struct dhcpcd_ctx *, const char *, sa_family_t);
|
||||
int control_stop(struct dhcpcd_ctx *);
|
||||
int control_open(const char *, sa_family_t, bool);
|
||||
ssize_t control_send(struct dhcpcd_ctx *, int, char * const *);
|
||||
struct fd_list *control_new(struct dhcpcd_ctx *, int, unsigned int);
|
||||
void control_free(struct fd_list *);
|
||||
void control_delete(struct fd_list *);
|
||||
int control_queue(struct fd_list *, void *, size_t);
|
||||
void control_recvdata(struct fd_list *fd, char *, size_t);
|
||||
#endif
|
|
@ -0,0 +1,78 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef DEFS_H
|
||||
#define DEFS_H
|
||||
|
||||
#define PACKAGE "dhcpcd"
|
||||
#define VERSION "10.0.8"
|
||||
|
||||
#ifndef PRIVSEP_USER
|
||||
# define PRIVSEP_USER "_" PACKAGE
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG
|
||||
# define CONFIG SYSCONFDIR "/" PACKAGE ".conf"
|
||||
#endif
|
||||
#ifndef SCRIPT
|
||||
# define SCRIPT LIBEXECDIR "/" PACKAGE "-run-hooks"
|
||||
#endif
|
||||
#ifndef DEVDIR
|
||||
# define DEVDIR LIBDIR "/" PACKAGE "/dev"
|
||||
#endif
|
||||
#ifndef DUID
|
||||
# define DUID DBDIR "/duid"
|
||||
#endif
|
||||
#ifndef SECRET
|
||||
# define SECRET DBDIR "/secret"
|
||||
#endif
|
||||
#ifndef LEASEFILE
|
||||
# define LEASEFILE DBDIR "/%s%s.lease"
|
||||
#endif
|
||||
#ifndef LEASEFILE6
|
||||
# define LEASEFILE6 LEASEFILE "6"
|
||||
#endif
|
||||
#ifndef PIDFILE
|
||||
# define PIDFILE RUNDIR "/%s%s%spid"
|
||||
#endif
|
||||
#ifndef CONTROLSOCKET
|
||||
# define CONTROLSOCKET RUNDIR "/%s%s%s%ssock"
|
||||
#endif
|
||||
#ifndef RDM_MONOFILE
|
||||
# define RDM_MONOFILE DBDIR "/rdm_monotonic"
|
||||
#endif
|
||||
|
||||
#ifndef NO_SIGNALS
|
||||
# define USE_SIGNALS
|
||||
#endif
|
||||
#ifndef USE_SIGNALS
|
||||
# ifndef THERE_IS_NO_FORK
|
||||
# define THERE_IS_NO_FORK
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,56 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef DEV_H
|
||||
#define DEV_H
|
||||
|
||||
// dev plugin setup
|
||||
struct dev {
|
||||
const char *name;
|
||||
int (*initialised)(const char *);
|
||||
int (*listening)(void);
|
||||
int (*handle_device)(void *);
|
||||
int (*start)(void);
|
||||
void (*stop)(void);
|
||||
};
|
||||
|
||||
struct dev_dhcpcd {
|
||||
int (*handle_interface)(void *, int, const char *);
|
||||
};
|
||||
|
||||
int dev_init(struct dev *, const struct dev_dhcpcd *);
|
||||
|
||||
// hooks for dhcpcd
|
||||
#ifdef PLUGIN_DEV
|
||||
#include "dhcpcd.h"
|
||||
int dev_initialised(struct dhcpcd_ctx *, const char *);
|
||||
int dev_listening(struct dhcpcd_ctx *);
|
||||
int dev_start(struct dhcpcd_ctx *, int (*)(void *, int, const char *));
|
||||
void dev_stop(struct dhcpcd_ctx *);
|
||||
#endif
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,147 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef DHCPCOMMON_H
|
||||
#define DHCPCOMMON_H
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <arpa/nameser.h> /* after normal includes for sunos */
|
||||
|
||||
#include "common.h"
|
||||
#include "dhcpcd.h"
|
||||
|
||||
/* Support very old arpa/nameser.h as found in OpenBSD */
|
||||
#ifndef NS_MAXDNAME
|
||||
#define NS_MAXCDNAME MAXCDNAME
|
||||
#define NS_MAXDNAME MAXDNAME
|
||||
#define NS_MAXLABEL MAXLABEL
|
||||
#endif
|
||||
|
||||
/* Max MTU - defines dhcp option length */
|
||||
#define IP_UDP_SIZE 28
|
||||
#define MTU_MAX 1500 - IP_UDP_SIZE
|
||||
#define MTU_MIN 576 + IP_UDP_SIZE
|
||||
|
||||
#define OT_REQUEST (1 << 0)
|
||||
#define OT_UINT8 (1 << 1)
|
||||
#define OT_INT8 (1 << 2)
|
||||
#define OT_UINT16 (1 << 3)
|
||||
#define OT_INT16 (1 << 4)
|
||||
#define OT_UINT32 (1 << 5)
|
||||
#define OT_INT32 (1 << 6)
|
||||
#define OT_ADDRIPV4 (1 << 7)
|
||||
#define OT_STRING (1 << 8)
|
||||
#define OT_ARRAY (1 << 9)
|
||||
#define OT_RFC3361 (1 << 10)
|
||||
#define OT_RFC1035 (1 << 11)
|
||||
#define OT_RFC3442 (1 << 12)
|
||||
#define OT_OPTIONAL (1 << 13)
|
||||
#define OT_ADDRIPV6 (1 << 14)
|
||||
#define OT_BINHEX (1 << 15)
|
||||
#define OT_FLAG (1 << 16)
|
||||
#define OT_NOREQ (1 << 17)
|
||||
#define OT_EMBED (1 << 18)
|
||||
#define OT_ENCAP (1 << 19)
|
||||
#define OT_INDEX (1 << 20)
|
||||
#define OT_OPTION (1 << 21)
|
||||
#define OT_DOMAIN (1 << 22)
|
||||
#define OT_ASCII (1 << 23)
|
||||
#define OT_RAW (1 << 24)
|
||||
#define OT_ESCSTRING (1 << 25)
|
||||
#define OT_ESCFILE (1 << 26)
|
||||
#define OT_BITFLAG (1 << 27)
|
||||
#define OT_RESERVED (1 << 28)
|
||||
#define OT_URI (1 << 29)
|
||||
|
||||
#define DHC_REQ(r, n, o) \
|
||||
(has_option_mask((r), (o)) && !has_option_mask((n), (o)))
|
||||
|
||||
#define DHC_REQOPT(o, r, n) \
|
||||
(!((o)->type & OT_NOREQ) && \
|
||||
((o)->type & OT_REQUEST || has_option_mask((r), (o)->option)) && \
|
||||
!has_option_mask((n), (o)->option))
|
||||
|
||||
struct dhcp_opt {
|
||||
uint32_t option; /* Also used for IANA Enterpise Number */
|
||||
int type;
|
||||
size_t len;
|
||||
char *var;
|
||||
|
||||
int index; /* Index counter for many instances of the same option */
|
||||
char bitflags[8];
|
||||
|
||||
/* Embedded options.
|
||||
* The option code is irrelevant here. */
|
||||
struct dhcp_opt *embopts;
|
||||
size_t embopts_len;
|
||||
|
||||
/* Encapsulated options */
|
||||
struct dhcp_opt *encopts;
|
||||
size_t encopts_len;
|
||||
};
|
||||
|
||||
const char *dhcp_get_hostname(char *, size_t, const struct if_options *);
|
||||
struct dhcp_opt *vivso_find(uint32_t, const void *);
|
||||
|
||||
ssize_t dhcp_vendor(char *, size_t);
|
||||
|
||||
void dhcp_print_option_encoding(const struct dhcp_opt *opt, int cols);
|
||||
#define add_option_mask(var, val) \
|
||||
((var)[(val) >> 3] = (uint8_t)((var)[(val) >> 3] | 1 << ((val) & 7)))
|
||||
#define del_option_mask(var, val) \
|
||||
((var)[(val) >> 3] = (uint8_t)((var)[(val) >> 3] & ~(1 << ((val) & 7))))
|
||||
#define has_option_mask(var, val) \
|
||||
((var)[(val) >> 3] & (uint8_t)(1 << ((val) & 7)))
|
||||
int make_option_mask(const struct dhcp_opt *, size_t,
|
||||
const struct dhcp_opt *, size_t,
|
||||
uint8_t *, const char *, int);
|
||||
|
||||
size_t encode_rfc1035(const char *src, uint8_t *dst);
|
||||
ssize_t decode_rfc1035(char *, size_t, const uint8_t *, size_t);
|
||||
ssize_t print_string(char *, size_t, int, const uint8_t *, size_t);
|
||||
int dhcp_set_leasefile(char *, size_t, int, const struct interface *);
|
||||
|
||||
void dhcp_envoption(struct dhcpcd_ctx *,
|
||||
FILE *, const char *, const char *, struct dhcp_opt *,
|
||||
const uint8_t *(*dgetopt)(struct dhcpcd_ctx *,
|
||||
size_t *, unsigned int *, size_t *,
|
||||
const uint8_t *, size_t, struct dhcp_opt **),
|
||||
const uint8_t *od, size_t ol);
|
||||
void dhcp_zero_index(struct dhcp_opt *);
|
||||
|
||||
ssize_t dhcp_readfile(struct dhcpcd_ctx *, const char *, void *, size_t);
|
||||
ssize_t dhcp_writefile(struct dhcpcd_ctx *, const char *, mode_t,
|
||||
const void *, size_t);
|
||||
int dhcp_filemtime(struct dhcpcd_ctx *, const char *, time_t *);
|
||||
int dhcp_unlink(struct dhcpcd_ctx *, const char *);
|
||||
size_t dhcp_read_hwaddr_aton(struct dhcpcd_ctx *, uint8_t **, const char *);
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,285 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef DHCP_H
|
||||
#define DHCP_H
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <netinet/ip.h>
|
||||
#define __FAVOR_BSD /* Nasty glibc hack so we can use BSD semantics for UDP */
|
||||
#include <netinet/udp.h>
|
||||
#undef __FAVOR_BSD
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "arp.h"
|
||||
#include "bpf.h"
|
||||
#include "auth.h"
|
||||
#include "dhcp-common.h"
|
||||
|
||||
/* UDP port numbers for BOOTP */
|
||||
#define BOOTPS 67
|
||||
#define BOOTPC 68
|
||||
|
||||
#define MAGIC_COOKIE 0x63825363
|
||||
#define BROADCAST_FLAG 0x8000
|
||||
|
||||
/* BOOTP message OP code */
|
||||
#define BOOTREQUEST 1
|
||||
#define BOOTREPLY 2
|
||||
|
||||
/* DHCP message type */
|
||||
#define DHCP_DISCOVER 1
|
||||
#define DHCP_OFFER 2
|
||||
#define DHCP_REQUEST 3
|
||||
#define DHCP_DECLINE 4
|
||||
#define DHCP_ACK 5
|
||||
#define DHCP_NAK 6
|
||||
#define DHCP_RELEASE 7
|
||||
#define DHCP_INFORM 8
|
||||
#define DHCP_FORCERENEW 9
|
||||
|
||||
/* Constants taken from RFC 2131. */
|
||||
#define T1 0.5
|
||||
#define T2 0.875
|
||||
#define DHCP_BASE 4
|
||||
#define DHCP_MAX 64
|
||||
#define DHCP_RAND_MIN -1
|
||||
#define DHCP_RAND_MAX 1
|
||||
|
||||
#ifdef RFC2131_STRICT
|
||||
/* Be strictly conformant for section 4.1.1 */
|
||||
# define DHCP_MIN_DELAY 1
|
||||
# define DHCP_MAX_DELAY 10
|
||||
#else
|
||||
/* or mirror the more modern IPv6RS and DHCPv6 delays */
|
||||
# define DHCP_MIN_DELAY 0
|
||||
# define DHCP_MAX_DELAY 1
|
||||
#endif
|
||||
|
||||
/* DHCP options */
|
||||
enum DHO {
|
||||
DHO_PAD = 0,
|
||||
DHO_SUBNETMASK = 1,
|
||||
DHO_ROUTER = 3,
|
||||
DHO_DNSSERVER = 6,
|
||||
DHO_HOSTNAME = 12,
|
||||
DHO_DNSDOMAIN = 15,
|
||||
DHO_MTU = 26,
|
||||
DHO_BROADCAST = 28,
|
||||
DHO_STATICROUTE = 33,
|
||||
DHO_NISDOMAIN = 40,
|
||||
DHO_NISSERVER = 41,
|
||||
DHO_NTPSERVER = 42,
|
||||
DHO_VENDOR = 43,
|
||||
DHO_IPADDRESS = 50,
|
||||
DHO_LEASETIME = 51,
|
||||
DHO_OPTSOVERLOADED = 52,
|
||||
DHO_MESSAGETYPE = 53,
|
||||
DHO_SERVERID = 54,
|
||||
DHO_PARAMETERREQUESTLIST = 55,
|
||||
DHO_MESSAGE = 56,
|
||||
DHO_MAXMESSAGESIZE = 57,
|
||||
DHO_RENEWALTIME = 58,
|
||||
DHO_REBINDTIME = 59,
|
||||
DHO_VENDORCLASSID = 60,
|
||||
DHO_CLIENTID = 61,
|
||||
DHO_USERCLASS = 77, /* RFC 3004 */
|
||||
DHO_RAPIDCOMMIT = 80, /* RFC 4039 */
|
||||
DHO_FQDN = 81,
|
||||
DHO_AUTHENTICATION = 90, /* RFC 3118 */
|
||||
DHO_IPV6_PREFERRED_ONLY = 108, /* RFC 8925 */
|
||||
DHO_AUTOCONFIGURE = 116, /* RFC 2563 */
|
||||
DHO_DNSSEARCH = 119, /* RFC 3397 */
|
||||
DHO_CSR = 121, /* RFC 3442 */
|
||||
DHO_VIVCO = 124, /* RFC 3925 */
|
||||
DHO_VIVSO = 125, /* RFC 3925 */
|
||||
DHO_FORCERENEW_NONCE = 145, /* RFC 6704 */
|
||||
DHO_MUDURL = 161, /* draft-ietf-opsawg-mud */
|
||||
DHO_SIXRD = 212, /* RFC 5969 */
|
||||
DHO_MSCSR = 249, /* MS code for RFC 3442 */
|
||||
DHO_END = 255
|
||||
};
|
||||
|
||||
/* FQDN values - lsnybble used in flags
|
||||
* hsnybble to create order
|
||||
* and to allow 0x00 to mean disable
|
||||
*/
|
||||
enum FQDN {
|
||||
FQDN_DISABLE = 0x00,
|
||||
FQDN_NONE = 0x18,
|
||||
FQDN_PTR = 0x20,
|
||||
FQDN_BOTH = 0x31
|
||||
};
|
||||
|
||||
#define MIN_V6ONLY_WAIT 300 /* seconds, RFC 8925 */
|
||||
|
||||
/* Sizes for BOOTP options */
|
||||
#define BOOTP_CHADDR_LEN 16
|
||||
#define BOOTP_SNAME_LEN 64
|
||||
#define BOOTP_FILE_LEN 128
|
||||
#define BOOTP_VEND_LEN 64
|
||||
|
||||
/* DHCP is basically an extension to BOOTP */
|
||||
struct bootp {
|
||||
uint8_t op; /* message type */
|
||||
uint8_t htype; /* hardware address type */
|
||||
uint8_t hlen; /* hardware address length */
|
||||
uint8_t hops; /* should be zero in client message */
|
||||
uint32_t xid; /* transaction id */
|
||||
uint16_t secs; /* elapsed time in sec. from boot */
|
||||
uint16_t flags; /* such as broadcast flag */
|
||||
uint32_t ciaddr; /* (previously allocated) client IP */
|
||||
uint32_t yiaddr; /* 'your' client IP address */
|
||||
uint32_t siaddr; /* should be zero in client's messages */
|
||||
uint32_t giaddr; /* should be zero in client's messages */
|
||||
uint8_t chaddr[BOOTP_CHADDR_LEN]; /* client's hardware address */
|
||||
uint8_t sname[BOOTP_SNAME_LEN]; /* server host name */
|
||||
uint8_t file[BOOTP_FILE_LEN]; /* boot file name */
|
||||
uint8_t vend[BOOTP_VEND_LEN]; /* vendor specific area */
|
||||
/* DHCP allows a variable length vendor area */
|
||||
};
|
||||
|
||||
#define DHCP_MIN_LEN (offsetof(struct bootp, vend) + 4)
|
||||
|
||||
struct bootp_pkt
|
||||
{
|
||||
struct ip ip;
|
||||
struct udphdr udp;
|
||||
struct bootp bootp;
|
||||
};
|
||||
|
||||
struct dhcp_lease {
|
||||
struct in_addr addr;
|
||||
struct in_addr mask;
|
||||
struct in_addr brd;
|
||||
uint32_t leasetime;
|
||||
uint32_t renewaltime;
|
||||
uint32_t rebindtime;
|
||||
struct in_addr server;
|
||||
uint8_t frominfo;
|
||||
uint32_t cookie;
|
||||
};
|
||||
|
||||
#ifndef DHCP_INFINITE_LIFETIME
|
||||
# define DHCP_INFINITE_LIFETIME (~0U)
|
||||
#endif
|
||||
|
||||
enum DHS {
|
||||
DHS_NONE,
|
||||
DHS_INIT,
|
||||
DHS_DISCOVER,
|
||||
DHS_REQUEST,
|
||||
DHS_PROBE,
|
||||
DHS_BOUND,
|
||||
DHS_RENEW,
|
||||
DHS_REBIND,
|
||||
DHS_REBOOT,
|
||||
DHS_INFORM,
|
||||
DHS_RENEW_REQUESTED,
|
||||
DHS_RELEASE
|
||||
};
|
||||
|
||||
struct dhcp_state {
|
||||
enum DHS state;
|
||||
struct bootp *sent;
|
||||
size_t sent_len;
|
||||
struct bootp *offer;
|
||||
size_t offer_len;
|
||||
struct bootp *new;
|
||||
size_t new_len;
|
||||
struct bootp *old;
|
||||
size_t old_len;
|
||||
struct dhcp_lease lease;
|
||||
const char *reason;
|
||||
unsigned int interval;
|
||||
unsigned int nakoff;
|
||||
uint32_t xid;
|
||||
int socket;
|
||||
|
||||
struct bpf *bpf;
|
||||
int udp_rfd;
|
||||
struct ipv4_addr *addr;
|
||||
uint8_t added;
|
||||
|
||||
char leasefile[sizeof(LEASEFILE) + IF_NAMESIZE + (IF_SSIDLEN * 4)];
|
||||
struct timespec started;
|
||||
unsigned char *clientid;
|
||||
struct authstate auth;
|
||||
#ifdef ARPING
|
||||
ssize_t arping_index;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef INET
|
||||
#define D_STATE(ifp) \
|
||||
((struct dhcp_state *)(ifp)->if_data[IF_DATA_DHCP])
|
||||
#define D_CSTATE(ifp) \
|
||||
((const struct dhcp_state *)(ifp)->if_data[IF_DATA_DHCP])
|
||||
#define D_STATE_RUNNING(ifp) \
|
||||
(D_CSTATE((ifp)) && D_CSTATE((ifp))->new && D_CSTATE((ifp))->reason)
|
||||
|
||||
#define IS_DHCP(b) ((b)->vend[0] == 0x63 && \
|
||||
(b)->vend[1] == 0x82 && \
|
||||
(b)->vend[2] == 0x53 && \
|
||||
(b)->vend[3] == 0x63)
|
||||
|
||||
#include "dhcpcd.h"
|
||||
#include "if-options.h"
|
||||
|
||||
ssize_t print_rfc3361(FILE *, const uint8_t *, size_t);
|
||||
ssize_t print_rfc3442(FILE *, const uint8_t *, size_t);
|
||||
|
||||
int dhcp_openudp(struct in_addr *);
|
||||
void dhcp_packet(struct interface *, uint8_t *, size_t, unsigned int);
|
||||
void dhcp_recvmsg(struct dhcpcd_ctx *, struct msghdr *);
|
||||
void dhcp_printoptions(const struct dhcpcd_ctx *,
|
||||
const struct dhcp_opt *, size_t);
|
||||
uint16_t dhcp_get_mtu(const struct interface *);
|
||||
int dhcp_get_routes(rb_tree_t *, struct interface *);
|
||||
ssize_t dhcp_env(FILE *, const char *, const struct interface *,
|
||||
const struct bootp *, size_t);
|
||||
|
||||
struct ipv4_addr *dhcp_handleifa(int, struct ipv4_addr *, pid_t pid);
|
||||
void dhcp_drop(struct interface *, const char *);
|
||||
void dhcp_start(struct interface *);
|
||||
void dhcp_abort(struct interface *);
|
||||
void dhcp_discover(void *);
|
||||
void dhcp_inform(struct interface *);
|
||||
void dhcp_renew(struct interface *);
|
||||
void dhcp_bind(struct interface *);
|
||||
void dhcp_reboot_newopts(struct interface *, unsigned long long);
|
||||
void dhcp_close(struct interface *);
|
||||
void dhcp_free(struct interface *);
|
||||
int dhcp_dump(struct interface *);
|
||||
#endif /* INET */
|
||||
|
||||
#endif /* DHCP_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,254 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef DHCP6_H
|
||||
#define DHCP6_H
|
||||
|
||||
#include "dhcpcd.h"
|
||||
|
||||
#define IN6ADDR_LINKLOCAL_ALLDHCP_INIT \
|
||||
{{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 }}}
|
||||
|
||||
/* UDP port numbers for DHCP */
|
||||
#define DHCP6_CLIENT_PORT 546
|
||||
#define DHCP6_SERVER_PORT 547
|
||||
|
||||
/* DHCP message type */
|
||||
#define DHCP6_SOLICIT 1
|
||||
#define DHCP6_ADVERTISE 2
|
||||
#define DHCP6_REQUEST 3
|
||||
#define DHCP6_CONFIRM 4
|
||||
#define DHCP6_RENEW 5
|
||||
#define DHCP6_REBIND 6
|
||||
#define DHCP6_REPLY 7
|
||||
#define DHCP6_RELEASE 8
|
||||
#define DHCP6_DECLINE 9
|
||||
#define DHCP6_RECONFIGURE 10
|
||||
#define DHCP6_INFORMATION_REQ 11
|
||||
#define DHCP6_RELAY_FLOW 12
|
||||
#define DHCP6_RELAY_REPL 13
|
||||
#define DHCP6_RECONFIGURE_REQ 18
|
||||
#define DHCP6_RECONFIGURE_REPLY 19
|
||||
|
||||
#ifdef DHCP6
|
||||
|
||||
#define D6_OPTION_CLIENTID 1
|
||||
#define D6_OPTION_SERVERID 2
|
||||
#define D6_OPTION_IA_NA 3
|
||||
#define D6_OPTION_IA_TA 4
|
||||
#define D6_OPTION_ORO 6
|
||||
#define D6_OPTION_IA_ADDR 5
|
||||
#define D6_OPTION_PREFERENCE 7
|
||||
#define D6_OPTION_ELAPSED 8
|
||||
#define D6_OPTION_AUTH 11
|
||||
#define D6_OPTION_UNICAST 12
|
||||
#define D6_OPTION_STATUS_CODE 13
|
||||
#define D6_OPTION_RAPID_COMMIT 14
|
||||
#define D6_OPTION_USER_CLASS 15
|
||||
#define D6_OPTION_VENDOR_CLASS 16
|
||||
#define D6_OPTION_VENDOR_OPTS 17
|
||||
#define D6_OPTION_INTERFACE_ID 18
|
||||
#define D6_OPTION_RECONF_MSG 19
|
||||
#define D6_OPTION_RECONF_ACCEPT 20
|
||||
#define D6_OPTION_SIP_SERVERS_NAME 21
|
||||
#define D6_OPTION_SIP_SERVERS_ADDRESS 22
|
||||
#define D6_OPTION_DNS_SERVERS 23
|
||||
#define D6_OPTION_DOMAIN_LIST 24
|
||||
#define D6_OPTION_IA_PD 25
|
||||
#define D6_OPTION_IAPREFIX 26
|
||||
#define D6_OPTION_NIS_SERVERS 27
|
||||
#define D6_OPTION_NISP_SERVERS 28
|
||||
#define D6_OPTION_NIS_DOMAIN_NAME 29
|
||||
#define D6_OPTION_NISP_DOMAIN_NAME 30
|
||||
#define D6_OPTION_SNTP_SERVERS 31
|
||||
#define D6_OPTION_INFO_REFRESH_TIME 32
|
||||
#define D6_OPTION_BCMS_SERVER_D 33
|
||||
#define D6_OPTION_BCMS_SERVER_A 34
|
||||
#define D6_OPTION_FQDN 39
|
||||
#define D6_OPTION_POSIX_TIMEZONE 41
|
||||
#define D6_OPTION_TZDB_TIMEZONE 42
|
||||
#define D6_OPTION_NTP_SERVER 56
|
||||
#define D6_OPTION_PD_EXCLUDE 67
|
||||
#define D6_OPTION_SOL_MAX_RT 82
|
||||
#define D6_OPTION_INF_MAX_RT 83
|
||||
#define D6_OPTION_MUDURL 112
|
||||
|
||||
#define D6_FQDN_PTR 0x00
|
||||
#define D6_FQDN_BOTH 0x01
|
||||
#define D6_FQDN_NONE 0x04
|
||||
|
||||
#include "dhcp.h"
|
||||
#include "ipv6.h"
|
||||
|
||||
#define D6_STATUS_OK 0
|
||||
#define D6_STATUS_FAIL 1
|
||||
#define D6_STATUS_NOADDR 2
|
||||
#define D6_STATUS_NOBINDING 3
|
||||
#define D6_STATUS_NOTONLINK 4
|
||||
#define D6_STATUS_USEMULTICAST 5
|
||||
|
||||
#define SOL_MAX_DELAY 1
|
||||
#define SOL_TIMEOUT 1
|
||||
#define SOL_MAX_RT 3600 /* RFC7083 */
|
||||
#define SOL_MAX_RC 0
|
||||
#define REQ_MAX_DELAY 0
|
||||
#define REQ_TIMEOUT 1
|
||||
#define REQ_MAX_RT 30
|
||||
#define REQ_MAX_RC 10
|
||||
#define CNF_MAX_DELAY 1
|
||||
#define CNF_TIMEOUT 1
|
||||
#define CNF_MAX_RT 4
|
||||
#define CNF_MAX_RC 0
|
||||
#define CNF_MAX_RD 10
|
||||
#define REN_MAX_DELAY 0
|
||||
#define REN_TIMEOUT 10
|
||||
#define REN_MAX_RT 600
|
||||
#define REB_MAX_DELAY 0
|
||||
#define REB_TIMEOUT 10
|
||||
#define REB_MAX_RT 600
|
||||
#define INF_MAX_DELAY 1
|
||||
#define INF_TIMEOUT 1
|
||||
#define INF_MAX_RD CNF_MAX_RD /* NOT RFC defined */
|
||||
#define INF_MAX_RT 3600 /* RFC7083 */
|
||||
#define REL_MAX_DELAY 0
|
||||
#define REL_TIMEOUT 1
|
||||
#define REL_MAX_RT 0
|
||||
#define REL_MAX_RC 5
|
||||
#define DEC_MAX_DELAY 0
|
||||
#define DEC_TIMEOUT 1
|
||||
#define DEC_MAX_RC 5
|
||||
#define REC_MAX_DELAY 0
|
||||
#define REC_TIMEOUT 2
|
||||
#define REC_MAX_RC 8
|
||||
#define HOP_COUNT_LIMIT 32
|
||||
|
||||
/* RFC4242 3.1 */
|
||||
#define IRT_DEFAULT 86400
|
||||
#define IRT_MINIMUM 600
|
||||
|
||||
/* These should give -.1 to .1 randomness */
|
||||
#define DHCP6_RAND_MIN -100
|
||||
#define DHCP6_RAND_MAX 100
|
||||
#define DHCP6_RAND_DIV 1000.0f
|
||||
|
||||
enum DH6S {
|
||||
DH6S_INIT,
|
||||
DH6S_DISCOVER,
|
||||
DH6S_REQUEST,
|
||||
DH6S_BOUND,
|
||||
DH6S_RENEW,
|
||||
DH6S_REBIND,
|
||||
DH6S_CONFIRM,
|
||||
DH6S_INFORM,
|
||||
DH6S_INFORMED,
|
||||
DH6S_RENEW_REQUESTED,
|
||||
DH6S_PROBE,
|
||||
DH6S_DECLINE,
|
||||
DH6S_DELEGATED,
|
||||
DH6S_RELEASE,
|
||||
DH6S_RELEASED,
|
||||
};
|
||||
|
||||
struct dhcp6_state {
|
||||
enum DH6S state;
|
||||
struct timespec started;
|
||||
|
||||
/* Message retransmission timings in seconds */
|
||||
unsigned int IMD;
|
||||
unsigned int RTC;
|
||||
unsigned int IRT;
|
||||
unsigned int MRC;
|
||||
unsigned int MRT;
|
||||
void (*MRCcallback)(void *);
|
||||
unsigned int sol_max_rt;
|
||||
unsigned int inf_max_rt;
|
||||
unsigned int RT; /* retransmission timer in milliseconds
|
||||
* maximal RT is 1 day + RAND,
|
||||
* so should be enough */
|
||||
|
||||
struct dhcp6_message *send;
|
||||
size_t send_len;
|
||||
struct dhcp6_message *recv;
|
||||
size_t recv_len;
|
||||
struct dhcp6_message *new;
|
||||
size_t new_len;
|
||||
struct dhcp6_message *old;
|
||||
size_t old_len;
|
||||
|
||||
struct timespec acquired;
|
||||
uint32_t renew;
|
||||
uint32_t rebind;
|
||||
uint32_t expire;
|
||||
struct in6_addr unicast;
|
||||
struct ipv6_addrhead addrs;
|
||||
uint32_t lowpl;
|
||||
/* The +3 is for the possible .pd extension for prefix delegation */
|
||||
char leasefile[sizeof(LEASEFILE6) + IF_NAMESIZE + (IF_SSIDLEN * 4) +3];
|
||||
const char *reason;
|
||||
uint16_t lerror; /* Last error received from DHCPv6 reply. */
|
||||
bool has_no_binding;
|
||||
bool failed; /* Entered the failed state - used to rate limit log. */
|
||||
bool new_start; /* New external start, to determine log type. */
|
||||
#ifdef AUTH
|
||||
struct authstate auth;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define D6_STATE(ifp) \
|
||||
((struct dhcp6_state *)(ifp)->if_data[IF_DATA_DHCP6])
|
||||
#define D6_CSTATE(ifp) \
|
||||
((const struct dhcp6_state *)(ifp)->if_data[IF_DATA_DHCP6])
|
||||
#define D6_STATE_RUNNING(ifp) \
|
||||
(D6_CSTATE((ifp)) && \
|
||||
D6_CSTATE((ifp))->reason && dhcp6_dadcompleted((ifp)))
|
||||
|
||||
int dhcp6_openraw(void);
|
||||
int dhcp6_openudp(unsigned int, struct in6_addr *);
|
||||
void dhcp6_recvmsg(struct dhcpcd_ctx *, struct msghdr *, struct ipv6_addr *);
|
||||
void dhcp6_printoptions(const struct dhcpcd_ctx *,
|
||||
const struct dhcp_opt *, size_t);
|
||||
const struct ipv6_addr *dhcp6_iffindaddr(const struct interface *ifp,
|
||||
const struct in6_addr *addr, unsigned int flags);
|
||||
struct ipv6_addr *dhcp6_findaddr(struct dhcpcd_ctx *, const struct in6_addr *,
|
||||
unsigned int);
|
||||
size_t dhcp6_find_delegates(struct interface *);
|
||||
int dhcp6_start(struct interface *, enum DH6S);
|
||||
void dhcp6_reboot(struct interface *);
|
||||
void dhcp6_renew(struct interface *);
|
||||
ssize_t dhcp6_env(FILE *, const char *, const struct interface *,
|
||||
const struct dhcp6_message *, size_t);
|
||||
void dhcp6_free(struct interface *);
|
||||
void dhcp6_handleifa(int, struct ipv6_addr *, pid_t);
|
||||
bool dhcp6_dadcompleted(const struct interface *);
|
||||
void dhcp6_abort(struct interface *);
|
||||
void dhcp6_drop(struct interface *, const char *);
|
||||
int dhcp6_dump(struct interface *);
|
||||
#endif /* DHCP6 */
|
||||
|
||||
#endif /* DHCP6_H */
|
|
@ -0,0 +1,532 @@
|
|||
/*
|
||||
* DO NOT EDIT!
|
||||
* Automatically generated from dhcpcd-embedded.conf
|
||||
* Ths allows us to simply generate DHCP structure without any C programming.
|
||||
*/
|
||||
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
const char dhcpcd_embedded_conf[] =
|
||||
#ifdef SMALL
|
||||
"define 1 request ipaddress subnet_mask\n"
|
||||
"define 121 rfc3442 classless_static_routes\n"
|
||||
"define 3 request array ipaddress routers\n"
|
||||
"define 6 array ipaddress domain_name_servers\n"
|
||||
"define 12 dname host_name\n"
|
||||
"define 15 array dname domain_name\n"
|
||||
"define 26 uint16 interface_mtu\n"
|
||||
"define 28 request ipaddress broadcast_address\n"
|
||||
"define 33 request array ipaddress static_routes\n"
|
||||
"define 50 ipaddress dhcp_requested_address\n"
|
||||
"define 51 request uint32 dhcp_lease_time\n"
|
||||
"define 52 byte dhcp_option_overload\n"
|
||||
"define 53 byte dhcp_message_type\n"
|
||||
"define 54 ipaddress dhcp_server_identifier\n"
|
||||
"define 55 array byte dhcp_parameter_request_list\n"
|
||||
"define 56 string dhcp_message\n"
|
||||
"define 57 uint16 dhcp_max_message_size\n"
|
||||
"define 58 request uint32 dhcp_renewal_time\n"
|
||||
"define 59 request uint32 dhcp_rebinding_time\n"
|
||||
"define 60 string vendor_class_identifier\n"
|
||||
"define 61 binhex dhcp_client_identifier\n"
|
||||
"define 80 norequest flag rapid_commit\n"
|
||||
"define 81 embed fqdn\n"
|
||||
"embed bitflags=0000NEOS flags\n"
|
||||
"embed byte rcode1\n"
|
||||
"embed byte rcode2\n"
|
||||
"embed optional domain fqdn\n"
|
||||
"define 119 array domain domain_search\n"
|
||||
"define 249 rfc3442 ms_classless_static_routes\n"
|
||||
"definend 1 binhex source_address\n"
|
||||
"definend 2 binhex target_address\n"
|
||||
"definend 3 index embed prefix_information\n"
|
||||
"embed byte length\n"
|
||||
"embed bitflags=LAH flags\n"
|
||||
"embed uint32 vltime\n"
|
||||
"embed uint32 pltime\n"
|
||||
"embed uint32 reserved\n"
|
||||
"embed array ip6address prefix\n"
|
||||
"definend 5 embed mtu\n"
|
||||
"embed uint16 reserved\n"
|
||||
"embed uint32 mtu\n"
|
||||
"definend 25 index embed rdnss\n"
|
||||
"embed uint16 reserved\n"
|
||||
"embed uint32 lifetime\n"
|
||||
"embed array ip6address servers\n"
|
||||
"definend 31 index embed dnssl\n"
|
||||
"embed uint16 reserved\n"
|
||||
"embed uint32 lifetime\n"
|
||||
"embed domain search\n"
|
||||
"define6 1 binhex client_id\n"
|
||||
"define6 2 binhex server_id\n"
|
||||
"define6 3 norequest index embed ia_na\n"
|
||||
"embed binhex:4 iaid\n"
|
||||
"embed uint32 t1\n"
|
||||
"embed uint32 t2\n"
|
||||
"encap 5 option\n"
|
||||
"encap 13 option\n"
|
||||
"define6 4 norequest index embed ia_ta\n"
|
||||
"embed uint32 iaid\n"
|
||||
"encap 5 option\n"
|
||||
"encap 13 option\n"
|
||||
"define6 5 norequest index embed ia_addr\n"
|
||||
"embed ip6address ia_addr\n"
|
||||
"embed uint32 pltime\n"
|
||||
"embed uint32 vltime\n"
|
||||
"encap 13 option\n"
|
||||
"define6 12 ip6address unicast\n"
|
||||
"define6 13 norequest embed status_code\n"
|
||||
"embed uint16 status_code\n"
|
||||
"embed optional string message\n"
|
||||
"define6 18 binhex interface_id\n"
|
||||
"define6 19 byte reconfigure_msg\n"
|
||||
"define6 20 flag reconfigure_accept\n"
|
||||
"define6 23 array ip6address name_servers\n"
|
||||
"define6 24 array domain domain_search\n"
|
||||
"define6 39 embed fqdn\n"
|
||||
"embed bitflags=00000NOS flags\n"
|
||||
"embed optional domain fqdn\n"
|
||||
"define6 82 request uint32 sol_max_rt\n"
|
||||
"define6 83 request uint32 inf_max_rt\n"
|
||||
#else
|
||||
"define 1 request ipaddress subnet_mask\n"
|
||||
"define 121 rfc3442 classless_static_routes\n"
|
||||
"define 2 int32 time_offset\n"
|
||||
"define 3 request array ipaddress routers\n"
|
||||
"define 4 array ipaddress time_servers\n"
|
||||
"define 5 array ipaddress ien116_name_servers\n"
|
||||
"define 6 array ipaddress domain_name_servers\n"
|
||||
"define 7 array ipaddress log_servers\n"
|
||||
"define 8 array ipaddress cookie_servers\n"
|
||||
"define 9 array ipaddress lpr_servers\n"
|
||||
"define 10 array ipaddress impress_servers\n"
|
||||
"define 11 array ipaddress resource_location_servers\n"
|
||||
"define 12 dname host_name\n"
|
||||
"define 13 uint16 boot_size\n"
|
||||
"define 14 string merit_dump\n"
|
||||
"define 15 array dname domain_name\n"
|
||||
"define 16 ipaddress swap_server\n"
|
||||
"define 17 string root_path\n"
|
||||
"define 18 string extensions_path\n"
|
||||
"define 19 byte ip_forwarding\n"
|
||||
"define 20 byte non_local_source_routing\n"
|
||||
"define 21 array ipaddress policy_filter\n"
|
||||
"define 22 uint16 max_dgram_reassembly\n"
|
||||
"define 23 byte default_ip_ttl\n"
|
||||
"define 24 uint32 path_mtu_aging_timeout\n"
|
||||
"define 25 array uint16 path_mtu_plateau_table\n"
|
||||
"define 26 uint16 interface_mtu\n"
|
||||
"define 27 byte all_subnets_local\n"
|
||||
"define 28 request ipaddress broadcast_address\n"
|
||||
"define 29 byte perform_mask_discovery\n"
|
||||
"define 30 byte mask_supplier\n"
|
||||
"define 31 byte router_discovery\n"
|
||||
"define 32 ipaddress router_solicitation_address\n"
|
||||
"define 33 request array ipaddress static_routes\n"
|
||||
"define 34 byte trailer_encapsulation\n"
|
||||
"define 35 uint32 arp_cache_timeout\n"
|
||||
"define 36 uint16 ieee802_3_encapsulation\n"
|
||||
"define 37 byte default_tcp_ttl\n"
|
||||
"define 38 uint32 tcp_keepalive_interval\n"
|
||||
"define 39 byte tcp_keepalive_garbage\n"
|
||||
"define 40 string nis_domain\n"
|
||||
"define 41 array ipaddress nis_servers\n"
|
||||
"define 42 array ipaddress ntp_servers\n"
|
||||
"define 43 binhex vendor_encapsulated_options\n"
|
||||
"define 44 array ipaddress netbios_name_servers\n"
|
||||
"define 45 ipaddress netbios_dd_server\n"
|
||||
"define 46 byte netbios_node_type\n"
|
||||
"define 47 string netbios_scope\n"
|
||||
"define 48 array ipaddress font_servers\n"
|
||||
"define 49 array ipaddress x_display_manager\n"
|
||||
"define 50 ipaddress dhcp_requested_address\n"
|
||||
"define 51 request uint32 dhcp_lease_time\n"
|
||||
"define 52 byte dhcp_option_overload\n"
|
||||
"define 53 byte dhcp_message_type\n"
|
||||
"define 54 ipaddress dhcp_server_identifier\n"
|
||||
"define 55 array byte dhcp_parameter_request_list\n"
|
||||
"define 56 string dhcp_message\n"
|
||||
"define 57 uint16 dhcp_max_message_size\n"
|
||||
"define 58 request uint32 dhcp_renewal_time\n"
|
||||
"define 59 request uint32 dhcp_rebinding_time\n"
|
||||
"define 60 string vendor_class_identifier\n"
|
||||
"define 61 binhex dhcp_client_identifier\n"
|
||||
"define 64 string nisplus_domain\n"
|
||||
"define 65 array ipaddress nisplus_servers\n"
|
||||
"define 66 dname tftp_server_name\n"
|
||||
"define 67 string bootfile_name\n"
|
||||
"define 68 array ipaddress mobile_ip_home_agent\n"
|
||||
"define 69 array ipaddress smtp_server\n"
|
||||
"define 70 array ipaddress pop_server\n"
|
||||
"define 71 array ipaddress nntp_server\n"
|
||||
"define 72 array ipaddress www_server\n"
|
||||
"define 73 array ipaddress finger_server\n"
|
||||
"define 74 array ipaddress irc_server\n"
|
||||
"define 75 array ipaddress streettalk_server\n"
|
||||
"define 76 array ipaddress streettalk_directory_assistance_server\n"
|
||||
"define 77 binhex user_class\n"
|
||||
"define 78 embed slp_agent\n"
|
||||
"embed byte mandatory\n"
|
||||
"embed array ipaddress address\n"
|
||||
"define 79 embed slp_service\n"
|
||||
"embed byte mandatory\n"
|
||||
"embed ascii scope_list\n"
|
||||
"define 80 norequest flag rapid_commit\n"
|
||||
"define 81 embed fqdn\n"
|
||||
"embed bitflags=0000NEOS flags\n"
|
||||
"embed byte rcode1\n"
|
||||
"embed byte rcode2\n"
|
||||
"embed optional domain fqdn\n"
|
||||
"define 83 embed isns\n"
|
||||
"embed byte reserved1\n"
|
||||
"embed bitflags=00000SAE functions\n"
|
||||
"embed byte reserved2\n"
|
||||
"embed bitflags=00fFsSCE dd\n"
|
||||
"embed byte reserved3\n"
|
||||
"embed bitflags=0000DMHE admin\n"
|
||||
"embed uint16 reserved4\n"
|
||||
"embed byte reserved5\n"
|
||||
"embed bitflags=0TXPAMSE server_security\n"
|
||||
"embed array ipaddress servers\n"
|
||||
"define 85 array ipaddress nds_servers\n"
|
||||
"define 86 raw nds_tree_name\n"
|
||||
"define 87 raw nds_context\n"
|
||||
"define 88 array domain bcms_controller_names\n"
|
||||
"define 89 array ipaddress bcms_controller_address\n"
|
||||
"define 90 embed auth\n"
|
||||
"embed byte protocol\n"
|
||||
"embed byte algorithm\n"
|
||||
"embed byte rdm\n"
|
||||
"embed binhex:8 replay\n"
|
||||
"embed binhex information\n"
|
||||
"define 91 uint32 client_last_transaction_time\n"
|
||||
"define 92 array ipaddress associated_ip\n"
|
||||
"define 98 string uap_servers\n"
|
||||
"define 99 encap geoconf_civic\n"
|
||||
"embed byte what\n"
|
||||
"embed uint16 country_code\n"
|
||||
"define 100 string posix_timezone\n"
|
||||
"define 101 string tzdb_timezone\n"
|
||||
"define 108 uint32 ipv6_only_preferred\n"
|
||||
"define 114 string captive_portal_uri\n"
|
||||
"define 116 byte auto_configure\n"
|
||||
"define 117 array uint16 name_service_search\n"
|
||||
"define 118 ipaddress subnet_selection\n"
|
||||
"define 119 array domain domain_search\n"
|
||||
"define 120 rfc3361 sip_server\n"
|
||||
"define 122 encap tsp\n"
|
||||
"encap 1 ipaddress dhcp_server\n"
|
||||
"encap 2 ipaddress dhcp_secondary_server\n"
|
||||
"encap 3 rfc3361 provisioning_server\n"
|
||||
"encap 4 embed as_req_as_rep_backoff\n"
|
||||
"embed uint32 nominal\n"
|
||||
"embed uint32 maximum\n"
|
||||
"embed uint32 retry\n"
|
||||
"encap 5 embed ap_req_ap_rep_backoff\n"
|
||||
"embed uint32 nominal\n"
|
||||
"embed uint32 maximum\n"
|
||||
"embed uint32 retry\n"
|
||||
"encap 6 domain kerberos_realm\n"
|
||||
"encap 7 byte ticket_granting_server_utilization\n"
|
||||
"encap 8 byte provisioning_timer\n"
|
||||
"define 123 binhex geoconf\n"
|
||||
"define 124 binhex vivco\n"
|
||||
"define 125 embed vivso\n"
|
||||
"embed uint32 enterprise_number\n"
|
||||
"define 136 array ipaddress pana_agent\n"
|
||||
"define 137 domain lost_server\n"
|
||||
"define 138 array ipaddress capwap_ac\n"
|
||||
"define 139 encap mos_ip\n"
|
||||
"encap 1 array ipaddress is\n"
|
||||
"encap 2 array ipaddress cs\n"
|
||||
"encap 3 array ipaddress es\n"
|
||||
"define 140 encap mos_domain\n"
|
||||
"encap 1 domain is\n"
|
||||
"encap 2 domain cs\n"
|
||||
"encap 3 domain es\n"
|
||||
"define 141 array domain sip_ua_cs_list\n"
|
||||
"define 142 array ipaddress andsf\n"
|
||||
"define 143 array uri sztp_redirect\n"
|
||||
"define 144 binhex geoloc\n"
|
||||
"define 145 array byte forcerenew_nonce_capable\n"
|
||||
"define 146 embed rdnss_selection\n"
|
||||
"embed byte prf\n"
|
||||
"embed ipaddress primary\n"
|
||||
"embed ipaddress secondary\n"
|
||||
"embed array domain domains\n"
|
||||
"define 147 domain dots_ri\n"
|
||||
"define 148 array ipaddress dots_address\n"
|
||||
"define 150 array ipaddress tftp_servers\n"
|
||||
"define 161 string mudurl\n"
|
||||
"define 208 binhex pxelinux_magic\n"
|
||||
"define 209 string config_file\n"
|
||||
"define 210 string path_prefix\n"
|
||||
"define 211 uint32 reboot_time\n"
|
||||
"define 212 embed sixrd\n"
|
||||
"embed byte mask_len\n"
|
||||
"embed byte prefix_len\n"
|
||||
"embed ip6address prefix\n"
|
||||
"embed array ipaddress brip_address\n"
|
||||
"define 213 domain access_domain\n"
|
||||
"define 221 encap vss\n"
|
||||
"encap 0 string nvt\n"
|
||||
"encap 1 binhex vpn_id\n"
|
||||
"encap 255 flag global\n"
|
||||
"define 245 ipaddress azureendpoint\n"
|
||||
"define 249 rfc3442 ms_classless_static_routes\n"
|
||||
"define 252 uri wpad_url\n"
|
||||
"define 224 binhex site_specific_224\n"
|
||||
"define 225 binhex site_specific_225\n"
|
||||
"define 226 binhex site_specific_226\n"
|
||||
"define 227 binhex site_specific_227\n"
|
||||
"define 228 binhex site_specific_228\n"
|
||||
"define 229 binhex site_specific_229\n"
|
||||
"define 230 binhex site_specific_230\n"
|
||||
"define 231 binhex site_specific_231\n"
|
||||
"define 232 binhex site_specific_232\n"
|
||||
"define 233 binhex site_specific_233\n"
|
||||
"define 234 binhex site_specific_234\n"
|
||||
"define 235 binhex site_specific_235\n"
|
||||
"define 236 binhex site_specific_236\n"
|
||||
"define 237 binhex site_specific_237\n"
|
||||
"define 238 binhex site_specific_238\n"
|
||||
"define 239 binhex site_specific_239\n"
|
||||
"define 240 binhex site_specific_240\n"
|
||||
"define 241 binhex site_specific_241\n"
|
||||
"define 242 binhex site_specific_242\n"
|
||||
"define 243 binhex site_specific_243\n"
|
||||
"define 244 binhex site_specific_244\n"
|
||||
"define 246 binhex site_specific_246\n"
|
||||
"define 247 binhex site_specific_247\n"
|
||||
"define 248 binhex site_specific_248\n"
|
||||
"define 250 binhex site_specific_250\n"
|
||||
"define 251 binhex site_specific_251\n"
|
||||
"define 253 binhex site_specific_253\n"
|
||||
"define 254 binhex site_specific_254\n"
|
||||
"definend 1 binhex source_address\n"
|
||||
"definend 2 binhex target_address\n"
|
||||
"definend 3 index embed prefix_information\n"
|
||||
"embed byte length\n"
|
||||
"embed bitflags=LAH flags\n"
|
||||
"embed uint32 vltime\n"
|
||||
"embed uint32 pltime\n"
|
||||
"embed uint32 reserved\n"
|
||||
"embed array ip6address prefix\n"
|
||||
"definend 5 embed mtu\n"
|
||||
"embed uint16 reserved\n"
|
||||
"embed uint32 mtu\n"
|
||||
"definend 8 embed homeagent_information\n"
|
||||
"embed uint16 reserved\n"
|
||||
"embed uint16 preference\n"
|
||||
"embed uint16 lifetime\n"
|
||||
"definend 25 index embed rdnss\n"
|
||||
"embed uint16 reserved\n"
|
||||
"embed uint32 lifetime\n"
|
||||
"embed array ip6address servers\n"
|
||||
"definend 31 index embed dnssl\n"
|
||||
"embed uint16 reserved\n"
|
||||
"embed uint32 lifetime\n"
|
||||
"embed domain search\n"
|
||||
"define6 1 binhex client_id\n"
|
||||
"define6 2 binhex server_id\n"
|
||||
"define6 3 norequest index embed ia_na\n"
|
||||
"embed binhex:4 iaid\n"
|
||||
"embed uint32 t1\n"
|
||||
"embed uint32 t2\n"
|
||||
"encap 5 option\n"
|
||||
"encap 13 option\n"
|
||||
"define6 4 norequest index embed ia_ta\n"
|
||||
"embed uint32 iaid\n"
|
||||
"encap 5 option\n"
|
||||
"encap 13 option\n"
|
||||
"define6 5 norequest index embed ia_addr\n"
|
||||
"embed ip6address ia_addr\n"
|
||||
"embed uint32 pltime\n"
|
||||
"embed uint32 vltime\n"
|
||||
"encap 13 option\n"
|
||||
"define6 6 array uint16 option_request\n"
|
||||
"define6 7 byte preference\n"
|
||||
"define6 8 uint16 elased_time\n"
|
||||
"define6 9 binhex dhcp_relay_msg\n"
|
||||
"define6 11 embed auth\n"
|
||||
"embed byte protocol\n"
|
||||
"embed byte algorithm\n"
|
||||
"embed byte rdm\n"
|
||||
"embed binhex:8 replay\n"
|
||||
"embed binhex information\n"
|
||||
"define6 12 ip6address unicast\n"
|
||||
"define6 13 norequest embed status_code\n"
|
||||
"embed uint16 status_code\n"
|
||||
"embed optional string message\n"
|
||||
"define6 14 norequest flag rapid_commit\n"
|
||||
"define6 15 binhex user_class\n"
|
||||
"define6 16 binhex vivco\n"
|
||||
"define6 17 embed vivso\n"
|
||||
"embed uint32 enterprise_number\n"
|
||||
"define6 18 binhex interface_id\n"
|
||||
"define6 19 byte reconfigure_msg\n"
|
||||
"define6 20 flag reconfigure_accept\n"
|
||||
"define6 21 array domain sip_servers_names\n"
|
||||
"define6 22 array ip6address sip_servers_addresses\n"
|
||||
"define6 23 array ip6address name_servers\n"
|
||||
"define6 24 array domain domain_search\n"
|
||||
"define6 25 norequest index embed ia_pd\n"
|
||||
"embed binhex:4 iaid\n"
|
||||
"embed uint32 t1\n"
|
||||
"embed uint32 t2\n"
|
||||
"encap 26 option\n"
|
||||
"define6 26 index embed prefix\n"
|
||||
"embed uint32 pltime\n"
|
||||
"embed uint32 vltime\n"
|
||||
"embed byte length\n"
|
||||
"embed ip6address prefix\n"
|
||||
"encap 13 option\n"
|
||||
"encap 67 option\n"
|
||||
"define6 27 array ip6address nis_servers\n"
|
||||
"define6 28 array ip6address nisp_servers\n"
|
||||
"define6 29 string nis_domain_name\n"
|
||||
"define6 30 string nisp_domain_name\n"
|
||||
"define6 31 array ip6address sntp_servers\n"
|
||||
"define6 32 uint32 info_refresh_time\n"
|
||||
"define6 33 array domain bcms_server_d\n"
|
||||
"define6 34 array ip6address bcms_server_a\n"
|
||||
"define6 36 encap geoconf_civic\n"
|
||||
"embed byte what\n"
|
||||
"embed uint16 country_code\n"
|
||||
"define6 37 embed remote_id\n"
|
||||
"embed uint32 enterprise_number\n"
|
||||
"embed binhex remote_id\n"
|
||||
"define6 38 binhex subscriber_id\n"
|
||||
"define6 39 embed fqdn\n"
|
||||
"embed bitflags=00000NOS flags\n"
|
||||
"embed optional domain fqdn\n"
|
||||
"define6 40 array ip6address pana_agent\n"
|
||||
"define6 41 string posix_timezone\n"
|
||||
"define6 42 string tzdb_timezone\n"
|
||||
"define6 43 array uint16 ero\n"
|
||||
"define6 49 domain mip6_hnidf\n"
|
||||
"define6 50 encap mip6_vdinf\n"
|
||||
"encap 71 option\n"
|
||||
"encap 72 option\n"
|
||||
"encap 73 option\n"
|
||||
"define6 51 domain lost_server\n"
|
||||
"define6 52 array ip6address capwap_ac\n"
|
||||
"define6 53 binhex relay_id\n"
|
||||
"define6 54 encap mos_ip\n"
|
||||
"encap 1 array ip6address is\n"
|
||||
"encap 2 array ip6address cs\n"
|
||||
"encap 3 array ip6address es\n"
|
||||
"define6 55 encap mos_domain\n"
|
||||
"encap 1 domain is\n"
|
||||
"encap 2 domain cs\n"
|
||||
"encap 3 domain es\n"
|
||||
"define6 56 encap ntp_server\n"
|
||||
"encap 1 ip6address addr\n"
|
||||
"encap 2 ip6address mcast_addr\n"
|
||||
"encap 3 domain fqdn\n"
|
||||
"define6 57 domain access_domain\n"
|
||||
"define6 58 array domain sip_ua_cs_list\n"
|
||||
"define6 59 uri bootfile_url\n"
|
||||
"define6 60 binhex bootfile_param\n"
|
||||
"define6 61 array uint16 architecture_types\n"
|
||||
"define6 62 embed nii\n"
|
||||
"embed byte type\n"
|
||||
"embed byte major\n"
|
||||
"embed byte minor\n"
|
||||
"define6 63 binhex geoloc\n"
|
||||
"define6 64 domain aftr_name\n"
|
||||
"define6 67 embed pd_exclude\n"
|
||||
"embed byte prefix_len\n"
|
||||
"embed binhex subnetID\n"
|
||||
"define6 69 encap mip6_idinf\n"
|
||||
"encap 71 option\n"
|
||||
"encap 72 option\n"
|
||||
"encap 73 option\n"
|
||||
"define6 70 encap mip6_udinf\n"
|
||||
"encap 71 option\n"
|
||||
"encap 72 option\n"
|
||||
"encap 73 option\n"
|
||||
"define6 71 embed mip6_hnp\n"
|
||||
"embed byte prefix_len\n"
|
||||
"embed ip6address prefix\n"
|
||||
"define6 72 ip6address mip6_haa\n"
|
||||
"define6 73 domain mip6_haf\n"
|
||||
"define6 74 embed rdnss_selection\n"
|
||||
"embed ip6address server\n"
|
||||
"embed byte prf\n"
|
||||
"embed array domain domains\n"
|
||||
"define6 75 string krb_principal_name\n"
|
||||
"define6 76 string krb_realm_name\n"
|
||||
"define6 78 embed krb_kdc\n"
|
||||
"embed uint16 priority\n"
|
||||
"embed uint16 weight\n"
|
||||
"embed byte transport_type\n"
|
||||
"embed uint16 port\n"
|
||||
"embed ip6address address\n"
|
||||
"embed string realm_name\n"
|
||||
"define6 80 ip6address link_address\n"
|
||||
"define6 82 request uint32 sol_max_rt\n"
|
||||
"define6 83 request uint32 inf_max_rt\n"
|
||||
"define6 89 embed s46_rule\n"
|
||||
"embed bitflags=0000000F flags\n"
|
||||
"embed byte ea_len\n"
|
||||
"embed byte prefix4_len\n"
|
||||
"embed ipaddress ipv4_prefix\n"
|
||||
"embed ip6address ipv6_prefix\n"
|
||||
"define6 90 ip6address s64_br\n"
|
||||
"define6 91 embed s46_dmr\n"
|
||||
"embed byte prefix_len\n"
|
||||
"embed binhex prefix\n"
|
||||
"define6 92 embed s46_v4v6bind\n"
|
||||
"embed ipaddress ipv4_address\n"
|
||||
"embed byte ipv6_prefix_len\n"
|
||||
"embed binhex ipv6_prefix_and_options\n"
|
||||
"define6 93 embed s46_portparams\n"
|
||||
"embed byte offset\n"
|
||||
"embed byte psid_len\n"
|
||||
"embed uint16 psid\n"
|
||||
"define6 94 embed s46_cont_mape\n"
|
||||
"encap 89 option\n"
|
||||
"encap 90 option\n"
|
||||
"define6 95 embed s46_cont_mapt\n"
|
||||
"encap 89 option\n"
|
||||
"encap 91 option\n"
|
||||
"define6 96 embed s46_cont_lw\n"
|
||||
"encap 90 option\n"
|
||||
"encap 92 option\n"
|
||||
"define6 112 string mudurl\n"
|
||||
"define6 103 uri captive_portal_uri\n"
|
||||
"define6 136 array uri sztp_redirect\n"
|
||||
"define6 141 domain dots_ri\n"
|
||||
"define6 142 array ip6address dots_address\n"
|
||||
"define6 143 array ip6address andsf6\n"
|
||||
#endif
|
||||
"\0";
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef SMALL
|
||||
#define INITDEFINES 25
|
||||
#define INITDEFINENDS 6
|
||||
#define INITDEFINE6S 14
|
||||
#else
|
||||
#define INITDEFINES 157
|
||||
#define INITDEFINENDS 7
|
||||
#define INITDEFINE6S 74
|
||||
#endif
|
||||
|
||||
extern const char dhcpcd_embedded_conf[];
|
|
@ -0,0 +1,888 @@
|
|||
.\" SPDX-License-Identifier: BSD-2-Clause
|
||||
.\"
|
||||
.\" Copyright (c) 2006-2023 Roy Marples
|
||||
.\" All rights reserved
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd December 10, 2023
|
||||
.Dt DHCPCD 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm dhcpcd
|
||||
.Nd a DHCP client
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl 146ABbDdEGgHJKLMNPpqTV
|
||||
.Op Fl C , Fl Fl nohook Ar hook
|
||||
.Op Fl c , Fl Fl script Ar script
|
||||
.Op Fl e , Fl Fl env Ar value
|
||||
.Op Fl F , Fl Fl fqdn Ar FQDN
|
||||
.Op Fl f , Fl Fl config Ar file
|
||||
.Op Fl h , Fl Fl hostname Ar hostname
|
||||
.Op Fl I , Fl Fl clientid Ar clientid
|
||||
.Op Fl i , Fl Fl vendorclassid Ar vendorclassid
|
||||
.Op Fl j , Fl Fl logfile Ar logfile
|
||||
.Op Fl l , Fl Fl leasetime Ar seconds
|
||||
.Op Fl m , Fl Fl metric Ar metric
|
||||
.Op Fl O , Fl Fl nooption Ar option
|
||||
.Op Fl o , Fl Fl option Ar option
|
||||
.Op Fl Q , Fl Fl require Ar option
|
||||
.Op Fl r , Fl Fl request Ar address
|
||||
.Op Fl S , Fl Fl static Ar value
|
||||
.Op Fl s , Fl Fl inform Ar address Ns Op Ar /cidr Ns Op Ar /broadcast_address
|
||||
.Op Fl Fl inform6
|
||||
.Op Fl t , Fl Fl timeout Ar seconds
|
||||
.Op Fl u , Fl Fl userclass Ar class
|
||||
.Op Fl v , Fl Fl vendor Ar code , Ar value
|
||||
.Op Fl W , Fl Fl whitelist Ar address Ns Op Ar /cidr
|
||||
.Op Fl w
|
||||
.Op Fl Fl waitip Ns = Ns Op 4 | 6
|
||||
.Op Fl y , Fl Fl reboot Ar seconds
|
||||
.Op Fl X , Fl Fl blacklist Ar address Ns Op Ar /cidr
|
||||
.Op Fl Z , Fl Fl denyinterfaces Ar pattern
|
||||
.Op Fl z , Fl Fl allowinterfaces Ar pattern
|
||||
.Op Fl Fl inactive
|
||||
.Op Fl Fl configure
|
||||
.Op Fl Fl noconfigure
|
||||
.Op interface
|
||||
.Op ...
|
||||
.Nm
|
||||
.Fl n , Fl Fl rebind
|
||||
.Op interface
|
||||
.Nm
|
||||
.Fl k , Fl Fl release
|
||||
.Op interface
|
||||
.Nm
|
||||
.Fl U , Fl Fl dumplease
|
||||
.Op Ar interface
|
||||
.Nm
|
||||
.Fl Fl version
|
||||
.Nm
|
||||
.Fl x , Fl Fl exit
|
||||
.Op interface
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is an implementation of the DHCP client specified in
|
||||
.Li RFC 2131 .
|
||||
.Nm
|
||||
gets the host information
|
||||
.Po
|
||||
IP address, routes, etc
|
||||
.Pc
|
||||
from a DHCP server and configures the network
|
||||
.Ar interface
|
||||
of the
|
||||
machine on which it is running.
|
||||
.Nm
|
||||
then runs the configuration script which writes DNS information to
|
||||
.Xr resolvconf 8 ,
|
||||
if available, otherwise directly to
|
||||
.Pa /etc/resolv.conf .
|
||||
If the hostname is currently blank, (null) or localhost, or
|
||||
.Va force_hostname
|
||||
is YES or TRUE or 1 then
|
||||
.Nm
|
||||
sets the hostname to the one supplied by the DHCP server.
|
||||
.Nm
|
||||
then daemonises and waits for the lease renewal time to lapse.
|
||||
It will then attempt to renew its lease and reconfigure if the new lease
|
||||
changes when the lease begins to expire or the DHCP server sends a message
|
||||
to renew early.
|
||||
.Pp
|
||||
If any interface reports a working carrier then
|
||||
.Nm
|
||||
will try to obtain a lease before forking to the background,
|
||||
otherwise it will fork right away.
|
||||
This behaviour can be modified with the
|
||||
.Fl b , Fl Fl background
|
||||
and
|
||||
.Fl w , Fl Fl waitip
|
||||
options.
|
||||
.Pp
|
||||
.Nm
|
||||
is also an implementation of the BOOTP client specified in
|
||||
.Li RFC 951 .
|
||||
.Pp
|
||||
.Nm
|
||||
is also an implementation of the IPv6 Router Solicitor as specified in
|
||||
.Li RFC 4861
|
||||
and
|
||||
.Li RFC 6106 .
|
||||
.Pp
|
||||
.Nm
|
||||
is also an implementation of the IPv6 Privacy Extensions to AutoConf as
|
||||
specified in
|
||||
.Li RFC 4941 .
|
||||
This feature needs to be enabled in the kernel and
|
||||
.Nm
|
||||
will start using it.
|
||||
.Pp
|
||||
.Nm
|
||||
is also an implementation of the DHCPv6 client as specified in
|
||||
.Li RFC 3315 .
|
||||
By default,
|
||||
.Nm
|
||||
only starts DHCPv6 when instructed to do so by an IPV6 Router Advertisement.
|
||||
If no Identity Association is configured,
|
||||
then a Non-temporary Address is requested.
|
||||
.Ss Local Link configuration
|
||||
If
|
||||
.Nm
|
||||
failed to obtain a lease, it probes for a valid IPv4LL address
|
||||
.Po
|
||||
aka ZeroConf, aka APIPA
|
||||
.Pc .
|
||||
Once obtained it restarts the process of looking for a DHCP server to get a
|
||||
proper address.
|
||||
.Pp
|
||||
When using IPv4LL,
|
||||
.Nm
|
||||
nearly always succeeds and returns an exit code of 0.
|
||||
In the rare case it fails, it normally means that there is a reverse ARP proxy
|
||||
installed which always defeats IPv4LL probing.
|
||||
To disable this behaviour, you can use the
|
||||
.Fl L , Fl Fl noipv4ll
|
||||
option.
|
||||
.Ss Multiple interfaces
|
||||
If a list of interfaces are given on the command line, then
|
||||
.Nm
|
||||
only works with those interfaces, otherwise
|
||||
.Nm
|
||||
discovers available Ethernet interfaces that can be configured.
|
||||
When
|
||||
.Nm
|
||||
is not limited to one interface on the command line,
|
||||
it is running in Manager mode.
|
||||
The
|
||||
.Nm dhcpcd-ui
|
||||
project expects dhcpcd to be running this way.
|
||||
.Pp
|
||||
If a single interface is given then
|
||||
.Nm
|
||||
only works for that interface and runs as a separate instance to other
|
||||
.Nm
|
||||
processes.
|
||||
The
|
||||
.Fl w , Fl Fl waitip
|
||||
option is enabled in this instance to maintain compatibility with older
|
||||
versions.
|
||||
Using a single interface,
|
||||
optionally further limited to an address protocol,
|
||||
also affects the
|
||||
.Fl k ,
|
||||
.Fl N ,
|
||||
.Fl n
|
||||
and
|
||||
.Fl x
|
||||
options, where the same interface and any address protocol
|
||||
will need to be specified, as a lack of an
|
||||
interface will imply Manager mode which this is not.
|
||||
To force starting in Manager mode with only one interface, the
|
||||
.Fl M , Fl Fl manager
|
||||
option can be used.
|
||||
.Pp
|
||||
Interfaces are preferred by carrier, DHCP lease/IPv4LL and then lowest metric.
|
||||
For systems that support route metrics, each route will be tagged with the
|
||||
metric, otherwise
|
||||
.Nm
|
||||
changes the routes to use the interface with the same route and the lowest
|
||||
metric.
|
||||
See options below for controlling which interfaces we allow and deny through
|
||||
the use of patterns.
|
||||
.Pp
|
||||
Non-ethernet interfaces and some virtual ethernet interfaces
|
||||
such as TAP and bridge are ignored by default,
|
||||
as is the FireWire interface.
|
||||
To work with these devices they either need to be specified on the command line,
|
||||
be listed in
|
||||
.Fl Fl allowinterfaces
|
||||
or have an interface directive in
|
||||
.Pa /etc/dhcpcd.conf .
|
||||
.Ss Hooking into events
|
||||
.Nm
|
||||
runs
|
||||
.Pa /libexec/dhcpcd-run-hooks ,
|
||||
or the script specified by the
|
||||
.Fl c , Fl Fl script
|
||||
option.
|
||||
This script runs each script found in
|
||||
.Pa /libexec/dhcpcd-hooks
|
||||
in a lexical order.
|
||||
The default installation supplies the scripts
|
||||
.Pa 01-test ,
|
||||
.Pa 20-resolv.conf
|
||||
and
|
||||
.Pa 30-hostname .
|
||||
You can disable each script by using the
|
||||
.Fl C , Fl Fl nohook
|
||||
option.
|
||||
See
|
||||
.Xr dhcpcd-run-hooks 8
|
||||
for details on how these scripts work.
|
||||
.Nm
|
||||
currently ignores the exit code of the script.
|
||||
.Pp
|
||||
More scripts are supplied in
|
||||
.Pa /usr/share/dhcpcd/hooks
|
||||
and need to be copied to
|
||||
.Pa /libexec/dhcpcd-hooks
|
||||
if you intend to use them.
|
||||
For example, you could install
|
||||
.Pa 29-lookup-hostname
|
||||
so that
|
||||
.Nm
|
||||
can lookup the hostname of the IP address in DNS if no hostname
|
||||
is given by the lease and one is not already set.
|
||||
.Ss Fine tuning
|
||||
You can fine-tune the behaviour of
|
||||
.Nm
|
||||
with the following options:
|
||||
.Bl -tag -width indent
|
||||
.It Fl b , Fl Fl background
|
||||
Background immediately.
|
||||
This is useful for startup scripts which don't disable link messages for
|
||||
carrier status.
|
||||
.It Fl c , Fl Fl script Ar script
|
||||
Use this
|
||||
.Ar script
|
||||
instead of the default
|
||||
.Pa /libexec/dhcpcd-run-hooks .
|
||||
.It Fl D , Fl Fl duid Op Ar ll | lt | uuid | value
|
||||
Use a DHCP Unique Identifier.
|
||||
If a system UUID is available, that will be used to create a DUID-UUID,
|
||||
otherwise if persistent storage is available then a DUID-LLT
|
||||
(link local address + time) is generated,
|
||||
otherwise DUID-LL is generated (link local address).
|
||||
The DUID type can be hinted as an optional parameter if the file
|
||||
.Pa /var/db/dhcpcd/duid
|
||||
does not exist.
|
||||
If not
|
||||
.Va ll ,
|
||||
.Va lt
|
||||
or
|
||||
.Va uuid
|
||||
then
|
||||
.Va value
|
||||
will be converted from 00:11:22:33 format.
|
||||
This, plus the IAID will be used as the
|
||||
.Fl I , Fl Fl clientid .
|
||||
The DUID generated will be held in
|
||||
.Pa /var/db/dhcpcd/duid
|
||||
and should not be copied to other hosts.
|
||||
This file also takes precedence over the above rules except for setting a value.
|
||||
.It Fl d , Fl Fl debug
|
||||
Echo debug messages to the stderr and syslog.
|
||||
.It Fl E , Fl Fl lastlease
|
||||
If
|
||||
.Nm
|
||||
cannot obtain a lease, then try to use the last lease acquired for the
|
||||
interface.
|
||||
.It Fl Fl lastleaseextend
|
||||
Same as the above, but the lease will be retained even if it expires.
|
||||
.Nm
|
||||
will give it up if any other host tries to claim it for their own via ARP.
|
||||
This violates RFC 2131, section 3.7, which states the lease should be
|
||||
dropped once it has expired.
|
||||
.It Fl e , Fl Fl env Ar value
|
||||
Push
|
||||
.Ar value
|
||||
to the environment for use in
|
||||
.Xr dhcpcd-run-hooks 8 .
|
||||
For example, you can force the hostname hook to always set the hostname with
|
||||
.Fl e
|
||||
.Va force_hostname=YES .
|
||||
.It Fl g , Fl Fl reconfigure
|
||||
.Nm
|
||||
will re-apply IP address, routing and run
|
||||
.Xr dhcpcd-run-hooks 8
|
||||
for each interface.
|
||||
This is useful so that a 3rd party such as PPP or VPN can change the routing
|
||||
table and / or DNS, etc and then instruct
|
||||
.Nm
|
||||
to put things back afterwards.
|
||||
.Nm
|
||||
does not read a new configuration when this happens - you should rebind if you
|
||||
need that functionality.
|
||||
.It Fl F , Fl Fl fqdn Ar fqdn
|
||||
Requests that the DHCP server update DNS using FQDN instead of just a
|
||||
hostname.
|
||||
Valid values for
|
||||
.Ar fqdn
|
||||
are disable, none, ptr and both.
|
||||
.Nm
|
||||
itself never does any DNS updates.
|
||||
.Nm
|
||||
encodes the FQDN hostname as specified in
|
||||
.Li RFC 1035 .
|
||||
.It Fl f , Fl Fl config Ar file
|
||||
Specify a config to load instead of
|
||||
.Pa /etc/dhcpcd.conf .
|
||||
.Nm
|
||||
always processes the config file before any command line options.
|
||||
.It Fl h , Fl Fl hostname Ar hostname
|
||||
Sends
|
||||
.Ar hostname
|
||||
to the DHCP server so it can be registered in DNS.
|
||||
If
|
||||
.Ar hostname
|
||||
is an empty string then the current system hostname is sent.
|
||||
If
|
||||
.Ar hostname
|
||||
is a FQDN (i.e., contains a .) then it will be encoded as such.
|
||||
.It Fl I , Fl Fl clientid Ar clientid
|
||||
Send the
|
||||
.Ar clientid .
|
||||
If the string is of the format 01:02:03 then it is encoded as hex.
|
||||
For interfaces whose hardware address is longer than 8 bytes, or if the
|
||||
.Ar clientid
|
||||
is an empty string then
|
||||
.Nm
|
||||
sends a default
|
||||
.Ar clientid
|
||||
of the hardware family and the hardware address.
|
||||
.It Fl i , Fl Fl vendorclassid Ar vendorclassid
|
||||
Override the DHCPv4
|
||||
.Ar vendorclassid
|
||||
field sent.
|
||||
The default is
|
||||
dhcpcd-<version>:<os>:<machine>:<platform>.
|
||||
For example
|
||||
.D1 dhcpcd-5.5.6:NetBSD-6.99.5:i386:i386
|
||||
If not set then none is sent.
|
||||
Some badly configured DHCP servers reject unknown vendorclassids.
|
||||
To work around it, try and impersonate Windows by using the MSFT vendorclassid.
|
||||
.It Fl j , Fl Fl logfile Ar logfile
|
||||
Writes to the specified
|
||||
.Ar logfile .
|
||||
.Nm
|
||||
still writes to
|
||||
.Xr syslog 3 .
|
||||
The
|
||||
.Ar logfile
|
||||
is reopened when
|
||||
.Nm
|
||||
receives the
|
||||
.Dv SIGUSR2
|
||||
signal.
|
||||
.It Fl k , Fl Fl release Op Ar interface
|
||||
This causes an existing
|
||||
.Nm
|
||||
process running on the
|
||||
.Ar interface
|
||||
to release its lease and de-configure the
|
||||
.Ar interface
|
||||
regardless of the
|
||||
.Fl p , Fl Fl persistent
|
||||
option.
|
||||
If no
|
||||
.Ar interface
|
||||
is specified then this applies to all interfaces in Manager mode.
|
||||
If no interfaces are left running,
|
||||
.Nm
|
||||
will exit.
|
||||
.It Fl l , Fl Fl leasetime Ar seconds
|
||||
Request a lease time of
|
||||
.Ar seconds .
|
||||
.Ar -1
|
||||
represents an infinite lease time.
|
||||
By default
|
||||
.Nm
|
||||
does not request any lease time and leaves it in the hands of the
|
||||
DHCP server.
|
||||
.It Fl M , Fl Fl manager
|
||||
Start
|
||||
.Nm
|
||||
in Manager mode even if only one interface specified on the command line.
|
||||
See the Multiple Interfaces section above.
|
||||
.It Fl m , Fl Fl metric Ar metric
|
||||
Metrics are used to prefer an interface over another one, lowest wins.
|
||||
.Nm
|
||||
will supply a default metric of 1000 +
|
||||
.Xr if_nametoindex 3 .
|
||||
This will be offset by 2000 for wireless interfaces, with additional offsets
|
||||
of 1000000 for IPv4LL and 2000000 for roaming interfaces.
|
||||
.It Fl n , Fl Fl rebind Op Ar interface
|
||||
Notifies
|
||||
.Nm
|
||||
to reload its configuration and rebind the specified
|
||||
.Ar interface .
|
||||
If no
|
||||
.Ar interface
|
||||
is specified then this applies to all interfaces in Manager mode.
|
||||
If
|
||||
.Nm
|
||||
is not running, then it starts up as normal.
|
||||
.It Fl N , Fl Fl renew Op Ar interface
|
||||
Notifies
|
||||
.Nm
|
||||
to renew existing addresses on the specified
|
||||
.Ar interface .
|
||||
If no
|
||||
.Ar interface
|
||||
is specified then this applies to all interfaces in Manager mode.
|
||||
If
|
||||
.Nm
|
||||
is not running, then it starts up as normal.
|
||||
Unlike the
|
||||
.Fl n , Fl Fl rebind
|
||||
option above, the configuration for
|
||||
.Nm
|
||||
is not reloaded.
|
||||
.It Fl o , Fl Fl option Ar option
|
||||
Request the DHCP
|
||||
.Ar option
|
||||
variable for use in
|
||||
.Pa /libexec/dhcpcd-run-hooks .
|
||||
.It Fl p , Fl Fl persistent
|
||||
.Nm
|
||||
de-configures the
|
||||
.Ar interface
|
||||
when it exits unless this option is enabled.
|
||||
Sometimes, this isn't desirable if, for example, you have root mounted over
|
||||
NFS or SSH clients connect to this host and they need to be notified of
|
||||
the host shutting down.
|
||||
You can use this option to stop this from happening.
|
||||
.It Fl r , Fl Fl request Ar address
|
||||
Request the
|
||||
.Ar address
|
||||
in the DHCP DISCOVER message.
|
||||
There is no guarantee this is the address the DHCP server will actually give.
|
||||
If no
|
||||
.Ar address
|
||||
is given then the first address currently assigned to the
|
||||
.Ar interface
|
||||
is used.
|
||||
.It Fl s , Fl Fl inform Ar address Ns Op Ar /cidr Ns Op Ar /broadcast_address
|
||||
Behaves like
|
||||
.Fl r , Fl Fl request
|
||||
as above, but sends a DHCP INFORM instead of DISCOVER/REQUEST.
|
||||
This does not get a lease as such, just notifies the DHCP server of the
|
||||
.Ar address
|
||||
in use.
|
||||
You should also include the optional
|
||||
.Ar cidr
|
||||
network number in case the address is not already configured on the interface.
|
||||
.Nm
|
||||
remains running and pretends it has an infinite lease.
|
||||
.Nm
|
||||
will not de-configure the interface when it exits.
|
||||
If
|
||||
.Nm
|
||||
fails to contact a DHCP server then it returns a failure instead of falling
|
||||
back on IPv4LL.
|
||||
.It Fl Fl inform6
|
||||
Performs a DHCPv6 Information Request.
|
||||
No address is requested or specified, but all other DHCPv6 options are allowed.
|
||||
This is normally performed automatically when the IPv6 Router Advertises
|
||||
that the client should perform this operation.
|
||||
This option is only needed when
|
||||
.Nm
|
||||
is not processing IPv6RA messages and the need for DHCPv6 Information Request
|
||||
exists.
|
||||
.It Fl S , Fl Fl static Ar value
|
||||
Configures a static DHCP
|
||||
.Ar value .
|
||||
If you set
|
||||
.Ic ip_address
|
||||
then
|
||||
.Nm
|
||||
will not attempt to obtain a lease and just use the value for the address with
|
||||
an infinite lease time.
|
||||
.Pp
|
||||
Here is an example which configures a static address, routes and DNS.
|
||||
.D1 dhcpcd -S ip_address=192.168.0.10/24 \e
|
||||
.D1 -S routers=192.168.0.1 \e
|
||||
.D1 -S domain_name_servers=192.168.0.1 \e
|
||||
.D1 eth0
|
||||
.Pp
|
||||
You cannot presently set static DHCPv6 values.
|
||||
Use the
|
||||
.Fl e , Fl Fl env
|
||||
option instead.
|
||||
.It Fl t , Fl Fl timeout Ar seconds
|
||||
Timeout after
|
||||
.Ar seconds ,
|
||||
instead of the default 30.
|
||||
A setting of 0
|
||||
.Ar seconds
|
||||
causes
|
||||
.Nm
|
||||
to wait forever to get a lease.
|
||||
If
|
||||
.Nm
|
||||
is working on a single interface then
|
||||
.Nm
|
||||
will exit when a timeout occurs, otherwise
|
||||
.Nm
|
||||
will fork into the background.
|
||||
.It Fl u , Fl Fl userclass Ar class
|
||||
Tags the DHCPv4 message with the userclass
|
||||
.Ar class .
|
||||
DHCP servers use this to give members of the class DHCP options other than the
|
||||
default, without having to know things like hardware address or hostname.
|
||||
.It Fl v , Fl Fl vendor Ar code , Ns Ar value
|
||||
Add an encapsulated vendor option.
|
||||
.Ar code
|
||||
should be between 1 and 254 inclusive.
|
||||
To add a raw vendor string, omit
|
||||
.Ar code
|
||||
but keep the comma.
|
||||
Examples.
|
||||
.Pp
|
||||
Set the vendor option 01 with an IP address.
|
||||
.D1 dhcpcd \-v 01,192.168.0.2 eth0
|
||||
Set the vendor option 02 with a hex code.
|
||||
.D1 dhcpcd \-v 02,01:02:03:04:05 eth0
|
||||
Set the vendor option 03 with an IP address as a string.
|
||||
.D1 dhcpcd \-v 03,\e"192.168.0.2\e" eth0
|
||||
Set un-encapsulated vendor option to hello world.
|
||||
.D1 dhcpcd \-v ,"hello world" eth0
|
||||
.It Fl Fl version
|
||||
Display both program version and copyright information.
|
||||
.Nm
|
||||
then exits before doing any configuration.
|
||||
.It Fl w
|
||||
Wait for an address to be assigned before forking to the background.
|
||||
Does not take an argument, unlike the below option.
|
||||
.It Fl Fl waitip Ns = Ns Op 4 | 6
|
||||
Wait for an address to be assigned before forking to the background.
|
||||
4 means wait for an IPv4 address to be assigned.
|
||||
6 means wait for an IPv6 address to be assigned.
|
||||
If no argument is given,
|
||||
.Nm
|
||||
will wait for any address protocol to be assigned.
|
||||
It is possible to wait for more than one address protocol and
|
||||
.Nm
|
||||
will only fork to the background when all waiting conditions are satisfied.
|
||||
.It Fl x , Fl Fl exit Op Ar interface
|
||||
This will signal an existing
|
||||
.Nm
|
||||
process running on the
|
||||
.Ar interface
|
||||
to exit.
|
||||
If no
|
||||
.Ar interface
|
||||
is specified, then the above is applied to all interfaces in Manager mode.
|
||||
See the
|
||||
.Fl p , Fl Fl persistent
|
||||
option to control configuration persistence on exit,
|
||||
which is enabled by default in
|
||||
.Xr dhcpcd.conf 5 .
|
||||
.Nm
|
||||
then waits until this process has exited.
|
||||
.It Fl y , Fl Fl reboot Ar seconds
|
||||
Allow
|
||||
.Ar reboot
|
||||
seconds before moving to the discover phase if we have an old lease to use.
|
||||
Allow
|
||||
.Ar reboot
|
||||
seconds before starting fallback states from the discover phase.
|
||||
IPv4LL is started when the first
|
||||
.Ar reboot
|
||||
timeout is reached.
|
||||
The default is 5 seconds.
|
||||
A setting of 0 seconds causes
|
||||
.Nm
|
||||
to skip the reboot phase and go straight into discover.
|
||||
This has no effect on DHCPv6 other than skipping the reboot phase.
|
||||
.El
|
||||
.Ss Restricting behaviour
|
||||
.Nm
|
||||
will try to do as much as it can by default.
|
||||
However, there are sometimes situations where you don't want the things to be
|
||||
configured exactly how the DHCP server wants.
|
||||
Here are some options that deal with turning these bits off.
|
||||
.Pp
|
||||
Note that when
|
||||
.Nm
|
||||
is restricted to a single interface then the interface also needs to be
|
||||
specified when asking
|
||||
.Nm
|
||||
to exit using the commandline.
|
||||
If the protocol is restricted as well then the protocol needs to be included
|
||||
with the exit instruction.
|
||||
.Bl -tag -width indent
|
||||
.It Fl 1 , Fl Fl oneshot
|
||||
Exit after configuring an interface.
|
||||
Use the
|
||||
.Fl w , Fl Fl waitip
|
||||
option to specify which protocol(s) to configure before exiting.
|
||||
.It Fl 4 , Fl Fl ipv4only
|
||||
Configure IPv4 only.
|
||||
.It Fl 6 , Fl Fl ipv6only
|
||||
Configure IPv6 only.
|
||||
.It Fl A , Fl Fl noarp
|
||||
Don't request or claim the address by ARP.
|
||||
This also disables IPv4LL.
|
||||
.It Fl B , Fl Fl nobackground
|
||||
Don't run in the background when we acquire a lease.
|
||||
This is mainly useful for running under the control of another process, such
|
||||
as a debugger or a network manager.
|
||||
.It Fl C , Fl Fl nohook Ar script
|
||||
Don't run this hook script.
|
||||
Matches full name, or prefixed with 2 numbers optionally ending with
|
||||
.Pa .sh .
|
||||
.Pp
|
||||
So to stop
|
||||
.Nm
|
||||
from touching your DNS settings you would do:-
|
||||
.D1 dhcpcd -C resolv.conf eth0
|
||||
.It Fl G , Fl Fl nogateway
|
||||
Don't set any default routes.
|
||||
.It Fl H , Fl Fl xidhwaddr
|
||||
Use the last four bytes of the hardware address as the DHCP xid instead
|
||||
of a randomly generated number.
|
||||
.It Fl J , Fl Fl broadcast
|
||||
Instructs the DHCP server to broadcast replies back to the client.
|
||||
Normally this is only set for non-Ethernet interfaces,
|
||||
such as FireWire and InfiniBand.
|
||||
In most instances,
|
||||
.Nm
|
||||
will set this automatically.
|
||||
.It Fl K , Fl Fl nolink
|
||||
Don't receive link messages for carrier status.
|
||||
You should only have to use this with buggy device drivers or running
|
||||
.Nm
|
||||
through a network manager.
|
||||
.It Fl L , Fl Fl noipv4ll
|
||||
Don't use IPv4LL (aka APIPA, aka Bonjour, aka ZeroConf).
|
||||
.It Fl O , Fl Fl nooption Ar option
|
||||
Removes the
|
||||
.Ar option
|
||||
from the DHCP message before processing.
|
||||
.It Fl P , Fl Fl printpidfile
|
||||
Print the
|
||||
.Pa pidfile
|
||||
.Nm
|
||||
will use based on command-line arguments to stdout.
|
||||
.It Fl Q , Fl Fl require Ar option
|
||||
Requires the
|
||||
.Ar option
|
||||
to be present in all DHCP messages, otherwise the message is ignored.
|
||||
To enforce that
|
||||
.Nm
|
||||
only responds to DHCP servers and not BOOTP servers, you can
|
||||
.Fl Q
|
||||
.Ar dhcp_message_type .
|
||||
.It Fl q , Fl Fl quiet
|
||||
Quiet
|
||||
.Nm
|
||||
on the command line, only warnings and errors will be displayed.
|
||||
If this option is used another time then all console output is disabled.
|
||||
These messages are still logged via
|
||||
.Xr syslog 3 .
|
||||
.It Fl T , Fl Fl test
|
||||
On receipt of DHCP messages just call
|
||||
.Pa /libexec/dhcpcd-run-hooks
|
||||
with the reason of TEST which echos the DHCP variables found in the message
|
||||
to the console.
|
||||
The interface configuration isn't touched and neither are any configuration
|
||||
files.
|
||||
The
|
||||
.Ar rapid_commit
|
||||
option is not sent in TEST mode so that the server does not lease an address.
|
||||
To test INFORM the interface needs to be configured with the desired address
|
||||
before starting
|
||||
.Nm .
|
||||
.It Fl U , Fl Fl dumplease Op Ar interface
|
||||
Dumps the current lease for the
|
||||
.Ar interface
|
||||
to stdout.
|
||||
If no
|
||||
.Ar interface
|
||||
is given then all interfaces are dumped.
|
||||
Use the
|
||||
.Fl 4
|
||||
or
|
||||
.Fl 6
|
||||
flags to specify an address family.
|
||||
If a lease is piped in via standard input then that is dumped.
|
||||
In this case, specifying an address family is mandatory.
|
||||
.It Fl V , Fl Fl variables
|
||||
Display a list of option codes, the associated variable and encoding for use in
|
||||
.Xr dhcpcd-run-hooks 8 .
|
||||
Variables are prefixed with new_ and old_ unless the option number is -.
|
||||
Variables without an option are part of the DHCP message and cannot be
|
||||
directly requested.
|
||||
.It Fl W , Fl Fl whitelist Ar address Ns Op /cidr
|
||||
Only accept packets from
|
||||
.Ar address Ns Op /cidr .
|
||||
.Fl X , Fl Fl blacklist
|
||||
is ignored if
|
||||
.Fl W , Fl Fl whitelist
|
||||
is set.
|
||||
.It Fl X , Fl Fl blacklist Ar address Ns Op Ar /cidr
|
||||
Ignore all packets from
|
||||
.Ar address Ns Op Ar /cidr .
|
||||
.It Fl Z , Fl Fl denyinterfaces Ar pattern
|
||||
When discovering interfaces, the interface name must not match
|
||||
.Ar pattern
|
||||
which is a space or comma separated list of patterns passed to
|
||||
.Xr fnmatch 3 .
|
||||
.It Fl z , Fl Fl allowinterfaces Ar pattern
|
||||
When discovering interfaces, the interface name must match
|
||||
.Ar pattern
|
||||
which is a space or comma separated list of patterns passed to
|
||||
.Xr fnmatch 3 .
|
||||
If the same interface is matched in
|
||||
.Fl Z , Fl Fl denyinterfaces
|
||||
then it is still denied.
|
||||
.It Fl Fl inactive
|
||||
Don't start any interfaces other than those specified on the command line.
|
||||
This allows
|
||||
.Nm
|
||||
to be started in Manager mode and then wait for subsequent
|
||||
.Nm
|
||||
commands to start each interface as required.
|
||||
.It Fl Fl configure
|
||||
Allows
|
||||
.Nm
|
||||
to configure the system.
|
||||
This is the default behaviour and sets
|
||||
.Ev if_configured=true .
|
||||
.It Fl Fl noconfigure
|
||||
.Nm
|
||||
will not configure the system at all.
|
||||
This is only of use if the
|
||||
.Fl Fl script
|
||||
that
|
||||
.Nm
|
||||
calls at each network event configures the system instead.
|
||||
This is different from
|
||||
.Fl T , Fl Fl test
|
||||
mode in that it's not one shot and the only change to the environment is the
|
||||
addition of
|
||||
.Ev if_configured=false .
|
||||
.It Fl Fl nodev
|
||||
Don't load any
|
||||
.Pa /dev
|
||||
management modules.
|
||||
.El
|
||||
.Sh 3RDPARTY LINK MANAGEMENT
|
||||
Some interfaces require configuration by 3rd parties, such as PPP or VPN.
|
||||
When an interface configuration in
|
||||
.Nm
|
||||
is marked as STATIC or INFORM without an address then
|
||||
.Nm
|
||||
will monitor the interface until an address is added or removed from it and
|
||||
act accordingly.
|
||||
For point to point interfaces (like PPP), a default route to its
|
||||
destination is automatically added to the configuration.
|
||||
If the point to point interface is configured for INFORM, then
|
||||
.Nm
|
||||
unicasts INFORM to the destination, otherwise it defaults to STATIC.
|
||||
.Sh NOTES
|
||||
.Nm
|
||||
requires a Berkeley Packet Filter, or BPF device on BSD based systems and a
|
||||
Linux Socket Filter, or LPF device on Linux based systems for all IPv4
|
||||
configuration.
|
||||
.Pp
|
||||
If restricting
|
||||
.Nm
|
||||
to a single interface and optionally address family via the command-line
|
||||
then all further calls to
|
||||
.Nm
|
||||
to rebind, reconfigure or exit need to include the same restrictive flags
|
||||
so that
|
||||
.Nm
|
||||
knows which process to signal.
|
||||
.Pp
|
||||
Some DHCP servers implement ClientID filtering.
|
||||
If
|
||||
.Nm
|
||||
is replacing an in-use DHCP client then you might need to adjust the clientid
|
||||
option
|
||||
.Nm
|
||||
sends to match.
|
||||
If using a DUID in place of the ClientID, edit
|
||||
.Pa /var/db/dhcpcd/duid
|
||||
accordingly.
|
||||
.Sh FILES
|
||||
.Bl -ohang
|
||||
.It Pa /etc/dhcpcd.conf
|
||||
Configuration file for dhcpcd.
|
||||
If you always use the same options, put them here.
|
||||
.It Pa /libexec/dhcpcd-run-hooks
|
||||
Bourne shell script that is run to configure or de-configure an interface.
|
||||
.It Pa /lib/dhcpcd/dev
|
||||
Linux
|
||||
.Pa /dev
|
||||
management modules.
|
||||
.It Pa /libexec/dhcpcd-hooks
|
||||
A directory containing Bourne shell scripts that are run by the above script.
|
||||
Each script can be disabled by using the
|
||||
.Fl C , Fl Fl nohook
|
||||
option described above.
|
||||
.It Pa /var/db/dhcpcd/duid
|
||||
Text file that holds the DUID used to identify the host.
|
||||
.It Pa /var/db/dhcpcd/secret
|
||||
Text file that holds a secret key known only to the host.
|
||||
.It Pa /var/db/dhcpcd/ Ns Ar interface Ns Ar -ssid Ns .lease
|
||||
The actual DHCP message sent by the server.
|
||||
We use this when reading the last
|
||||
lease and use the file's mtime as when it was issued.
|
||||
.It Pa /var/db/dhcpcd/ Ns Ar interface Ns Ar -ssid Ns .lease6
|
||||
The actual DHCPv6 message sent by the server.
|
||||
We use this when reading the last
|
||||
lease and use the file's mtime as when it was issued.
|
||||
.It Pa /var/db/dhcpcd/rdm_monotonic
|
||||
Stores the monotonic counter used in the
|
||||
.Ar replay
|
||||
field in Authentication Options.
|
||||
.It Pa /var/run/dhcpcd/pid
|
||||
Stores the PID of
|
||||
.Nm
|
||||
running on all interfaces.
|
||||
.It Pa /var/run/dhcpcd/ Ns Ar interface Ns .pid
|
||||
Stores the PID of
|
||||
.Nm
|
||||
running on the
|
||||
.Ar interface .
|
||||
.It Pa /var/run/dhcpcd/sock
|
||||
Control socket to the manager daemon.
|
||||
.It Pa /var/run/dhcpcd/unpriv.sock
|
||||
Unprivileged socket to the manager daemon, only allows state retrieval.
|
||||
.It Pa /var/run/dhcpcd/ Ns Ar interface Ns .sock
|
||||
Control socket to per interface daemon.
|
||||
.It Pa /var/run/dhcpcd/ Ns Ar interface Ns .unpriv.sock
|
||||
Unprivileged socket to per interface daemon, only allows state retrieval.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr fnmatch 3 ,
|
||||
.Xr if_nametoindex 3 ,
|
||||
.Xr dhcpcd.conf 5 ,
|
||||
.Xr resolv.conf 5 ,
|
||||
.Xr dhcpcd-run-hooks 8 ,
|
||||
.Xr resolvconf 8
|
||||
.Sh STANDARDS
|
||||
RFC\ 951, RFC\ 1534, RFC\ 2104, RFC\ 2131, RFC\ 2132, RFC\ 2563, RFC\ 2855,
|
||||
RFC\ 3004, RFC\ 3118, RFC\ 3203, RFC\ 3315, RFC\ 3361, RFC\ 3633, RFC\ 3396,
|
||||
RFC\ 3397, RFC\ 3442, RFC\ 3495, RFC\ 3925, RFC\ 3927, RFC\ 4039, RFC\ 4075,
|
||||
RFC\ 4242, RFC\ 4361, RFC\ 4390, RFC\ 4702, RFC\ 4074, RFC\ 4861, RFC\ 4833,
|
||||
RFC\ 4941, RFC\ 5227, RFC\ 5942, RFC\ 5969, RFC\ 6106, RFC\ 6334, RFC\ 6355,
|
||||
RFC\ 6603, RFC\ 6704, RFC\ 7217, RFC\ 7550, RFC\ 7844.
|
||||
.Sh AUTHORS
|
||||
.An Roy Marples Aq Mt roy@marples.name
|
||||
.Sh BUGS
|
||||
Please report them to
|
||||
.Lk https://roy.marples.name/projects/dhcpcd
|
|
@ -0,0 +1,882 @@
|
|||
.\" SPDX-License-Identifier: BSD-2-Clause
|
||||
.\"
|
||||
.\" Copyright (c) 2006-2020 Roy Marples
|
||||
.\" All rights reserved
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd November 25, 2020
|
||||
.Dt DHCPCD 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm dhcpcd
|
||||
.Nd a DHCP client
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl 146ABbDdEGgHJKLMNPpqTV
|
||||
.Op Fl C , Fl Fl nohook Ar hook
|
||||
.Op Fl c , Fl Fl script Ar script
|
||||
.Op Fl e , Fl Fl env Ar value
|
||||
.Op Fl F , Fl Fl fqdn Ar FQDN
|
||||
.Op Fl f , Fl Fl config Ar file
|
||||
.Op Fl h , Fl Fl hostname Ar hostname
|
||||
.Op Fl I , Fl Fl clientid Ar clientid
|
||||
.Op Fl i , Fl Fl vendorclassid Ar vendorclassid
|
||||
.Op Fl j , Fl Fl logfile Ar logfile
|
||||
.Op Fl l , Fl Fl leasetime Ar seconds
|
||||
.Op Fl m , Fl Fl metric Ar metric
|
||||
.Op Fl O , Fl Fl nooption Ar option
|
||||
.Op Fl o , Fl Fl option Ar option
|
||||
.Op Fl Q , Fl Fl require Ar option
|
||||
.Op Fl r , Fl Fl request Ar address
|
||||
.Op Fl S , Fl Fl static Ar value
|
||||
.Op Fl s , Fl Fl inform Ar address Ns Op Ar /cidr Ns Op Ar /broadcast_address
|
||||
.Op Fl Fl inform6
|
||||
.Op Fl t , Fl Fl timeout Ar seconds
|
||||
.Op Fl u , Fl Fl userclass Ar class
|
||||
.Op Fl v , Fl Fl vendor Ar code , Ar value
|
||||
.Op Fl W , Fl Fl whitelist Ar address Ns Op Ar /cidr
|
||||
.Op Fl w
|
||||
.Op Fl Fl waitip Ns = Ns Op 4 | 6
|
||||
.Op Fl y , Fl Fl reboot Ar seconds
|
||||
.Op Fl X , Fl Fl blacklist Ar address Ns Op Ar /cidr
|
||||
.Op Fl Z , Fl Fl denyinterfaces Ar pattern
|
||||
.Op Fl z , Fl Fl allowinterfaces Ar pattern
|
||||
.Op Fl Fl inactive
|
||||
.Op Fl Fl configure
|
||||
.Op Fl Fl noconfigure
|
||||
.Op interface
|
||||
.Op ...
|
||||
.Nm
|
||||
.Fl n , Fl Fl rebind
|
||||
.Op interface
|
||||
.Nm
|
||||
.Fl k , Fl Fl release
|
||||
.Op interface
|
||||
.Nm
|
||||
.Fl U , Fl Fl dumplease
|
||||
.Op Ar interface
|
||||
.Nm
|
||||
.Fl Fl version
|
||||
.Nm
|
||||
.Fl x , Fl Fl exit
|
||||
.Op interface
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is an implementation of the DHCP client specified in
|
||||
.Li RFC 2131 .
|
||||
.Nm
|
||||
gets the host information
|
||||
.Po
|
||||
IP address, routes, etc
|
||||
.Pc
|
||||
from a DHCP server and configures the network
|
||||
.Ar interface
|
||||
of the
|
||||
machine on which it is running.
|
||||
.Nm
|
||||
then runs the configuration script which writes DNS information to
|
||||
.Xr resolvconf 8 ,
|
||||
if available, otherwise directly to
|
||||
.Pa /etc/resolv.conf .
|
||||
If the hostname is currently blank, (null) or localhost, or
|
||||
.Va force_hostname
|
||||
is YES or TRUE or 1 then
|
||||
.Nm
|
||||
sets the hostname to the one supplied by the DHCP server.
|
||||
.Nm
|
||||
then daemonises and waits for the lease renewal time to lapse.
|
||||
It will then attempt to renew its lease and reconfigure if the new lease
|
||||
changes when the lease begins to expire or the DHCP server sends a message
|
||||
to renew early.
|
||||
.Pp
|
||||
If any interface reports a working carrier then
|
||||
.Nm
|
||||
will try to obtain a lease before forking to the background,
|
||||
otherwise it will fork right away.
|
||||
This behaviour can be modified with the
|
||||
.Fl b , Fl Fl background
|
||||
and
|
||||
.Fl w , Fl Fl waitip
|
||||
options.
|
||||
.Pp
|
||||
.Nm
|
||||
is also an implementation of the BOOTP client specified in
|
||||
.Li RFC 951 .
|
||||
.Pp
|
||||
.Nm
|
||||
is also an implementation of the IPv6 Router Solicitor as specified in
|
||||
.Li RFC 4861
|
||||
and
|
||||
.Li RFC 6106 .
|
||||
.Pp
|
||||
.Nm
|
||||
is also an implementation of the IPv6 Privacy Extensions to AutoConf as
|
||||
specified in
|
||||
.Li RFC 4941 .
|
||||
This feature needs to be enabled in the kernel and
|
||||
.Nm
|
||||
will start using it.
|
||||
.Pp
|
||||
.Nm
|
||||
is also an implementation of the DHCPv6 client as specified in
|
||||
.Li RFC 3315 .
|
||||
By default,
|
||||
.Nm
|
||||
only starts DHCPv6 when instructed to do so by an IPV6 Router Advertisement.
|
||||
If no Identity Association is configured,
|
||||
then a Non-temporary Address is requested.
|
||||
.Ss Local Link configuration
|
||||
If
|
||||
.Nm
|
||||
failed to obtain a lease, it probes for a valid IPv4LL address
|
||||
.Po
|
||||
aka ZeroConf, aka APIPA
|
||||
.Pc .
|
||||
Once obtained it restarts the process of looking for a DHCP server to get a
|
||||
proper address.
|
||||
.Pp
|
||||
When using IPv4LL,
|
||||
.Nm
|
||||
nearly always succeeds and returns an exit code of 0.
|
||||
In the rare case it fails, it normally means that there is a reverse ARP proxy
|
||||
installed which always defeats IPv4LL probing.
|
||||
To disable this behaviour, you can use the
|
||||
.Fl L , Fl Fl noipv4ll
|
||||
option.
|
||||
.Ss Multiple interfaces
|
||||
If a list of interfaces are given on the command line, then
|
||||
.Nm
|
||||
only works with those interfaces, otherwise
|
||||
.Nm
|
||||
discovers available Ethernet interfaces that can be configured.
|
||||
When
|
||||
.Nm
|
||||
not limited to one interface on the command line,
|
||||
it is running in Master mode.
|
||||
The
|
||||
.Nm dhcpcd-ui
|
||||
project expects dhcpcd to be running this way.
|
||||
.Pp
|
||||
If a single interface is given then
|
||||
.Nm
|
||||
only works for that interface and runs as a separate instance to other
|
||||
.Nm
|
||||
processes.
|
||||
.Fl w , Fl Fl waitip
|
||||
option is enabled in this instance to maintain compatibility with older
|
||||
versions.
|
||||
Using a single interface also affects the
|
||||
.Fl k ,
|
||||
.Fl N ,
|
||||
.Fl n
|
||||
and
|
||||
.Fl x
|
||||
options, where the same interface will need to be specified, as a lack of an
|
||||
interface will imply Master mode which this is not.
|
||||
To force starting in Master mode with only one interface, the
|
||||
.Fl M , Fl Fl master
|
||||
option can be used.
|
||||
.Pp
|
||||
Interfaces are preferred by carrier, DHCP lease/IPv4LL and then lowest metric.
|
||||
For systems that support route metrics, each route will be tagged with the
|
||||
metric, otherwise
|
||||
.Nm
|
||||
changes the routes to use the interface with the same route and the lowest
|
||||
metric.
|
||||
See options below for controlling which interfaces we allow and deny through
|
||||
the use of patterns.
|
||||
.Pp
|
||||
Non-ethernet interfaces and some virtual ethernet interfaces
|
||||
such as TAP and bridge are ignored by default,
|
||||
as is the FireWire interface.
|
||||
To work with these devices they either need to be specified on the command line,
|
||||
be listed in
|
||||
.Fl Fl allowinterfaces
|
||||
or have an interface directive in
|
||||
.Pa @SYSCONFDIR@/dhcpcd.conf .
|
||||
.Ss Hooking into events
|
||||
.Nm
|
||||
runs
|
||||
.Pa @SCRIPT@ ,
|
||||
or the script specified by the
|
||||
.Fl c , Fl Fl script
|
||||
option.
|
||||
This script runs each script found in
|
||||
.Pa @HOOKDIR@
|
||||
in a lexical order.
|
||||
The default installation supplies the scripts
|
||||
.Pa 01-test ,
|
||||
.Pa 02-dump ,
|
||||
.Pa 20-resolv.conf
|
||||
and
|
||||
.Pa 30-hostname .
|
||||
You can disable each script by using the
|
||||
.Fl C , Fl Fl nohook
|
||||
option.
|
||||
See
|
||||
.Xr dhcpcd-run-hooks 8
|
||||
for details on how these scripts work.
|
||||
.Nm
|
||||
currently ignores the exit code of the script.
|
||||
.Pp
|
||||
More scripts are supplied in
|
||||
.Pa @DATADIR@/dhcpcd/hooks
|
||||
and need to be copied to
|
||||
.Pa @HOOKDIR@
|
||||
if you intend to use them.
|
||||
For example, you could install
|
||||
.Pa 29-lookup-hostname
|
||||
so that
|
||||
.Nm
|
||||
can lookup the hostname of the IP address in DNS if no hostname
|
||||
is given by the lease and one is not already set.
|
||||
.Ss Fine tuning
|
||||
You can fine-tune the behaviour of
|
||||
.Nm
|
||||
with the following options:
|
||||
.Bl -tag -width indent
|
||||
.It Fl b , Fl Fl background
|
||||
Background immediately.
|
||||
This is useful for startup scripts which don't disable link messages for
|
||||
carrier status.
|
||||
.It Fl c , Fl Fl script Ar script
|
||||
Use this
|
||||
.Ar script
|
||||
instead of the default
|
||||
.Pa @SCRIPT@ .
|
||||
.It Fl D , Fl Fl duid Op Ar ll | lt | uuid | value
|
||||
Use a DHCP Unique Identifier.
|
||||
If a system UUID is available, that will be used to create a DUID-UUID,
|
||||
otheriwse if persistent storage is available then a DUID-LLT
|
||||
(link local address + time) is generated,
|
||||
otherwise DUID-LL is generated (link local address).
|
||||
The DUID type can be hinted as an optional parameter if the file
|
||||
.Pa @DBDIR@/duid
|
||||
does not exist.
|
||||
If not
|
||||
.Va ll ,
|
||||
.Va lt
|
||||
or
|
||||
.Va uuid
|
||||
then
|
||||
.Va value
|
||||
will be converted from 00:11:22:33 format.
|
||||
This, plus the IAID will be used as the
|
||||
.Fl I , Fl Fl clientid .
|
||||
The DUID generated will be held in
|
||||
.Pa @DBDIR@/duid
|
||||
and should not be copied to other hosts.
|
||||
This file also takes precedence over the above rules except for setting a value.
|
||||
.It Fl d , Fl Fl debug
|
||||
Echo debug messages to the stderr and syslog.
|
||||
.It Fl E , Fl Fl lastlease
|
||||
If
|
||||
.Nm
|
||||
cannot obtain a lease, then try to use the last lease acquired for the
|
||||
interface.
|
||||
.It Fl Fl lastleaseextend
|
||||
Same as the above, but the lease will be retained even if it expires.
|
||||
.Nm
|
||||
will give it up if any other host tries to claim it for their own via ARP.
|
||||
This violates RFC 2131, section 3.7, which states the lease should be
|
||||
dropped once it has expired.
|
||||
.It Fl e , Fl Fl env Ar value
|
||||
Push
|
||||
.Ar value
|
||||
to the environment for use in
|
||||
.Xr dhcpcd-run-hooks 8 .
|
||||
For example, you can force the hostname hook to always set the hostname with
|
||||
.Fl e
|
||||
.Va force_hostname=YES .
|
||||
.It Fl g , Fl Fl reconfigure
|
||||
.Nm
|
||||
will re-apply IP address, routing and run
|
||||
.Xr dhcpcd-run-hooks 8
|
||||
for each interface.
|
||||
This is useful so that a 3rd party such as PPP or VPN can change the routing
|
||||
table and / or DNS, etc and then instruct
|
||||
.Nm
|
||||
to put things back afterwards.
|
||||
.Nm
|
||||
does not read a new configuration when this happens - you should rebind if you
|
||||
need that functionality.
|
||||
.It Fl F , Fl Fl fqdn Ar fqdn
|
||||
Requests that the DHCP server updates DNS using FQDN instead of just a
|
||||
hostname.
|
||||
Valid values for
|
||||
.Ar fqdn
|
||||
are disable, none, ptr and both.
|
||||
.Nm
|
||||
itself never does any DNS updates.
|
||||
.Nm
|
||||
encodes the FQDN hostname as specified in
|
||||
.Li RFC 1035 .
|
||||
.It Fl f , Fl Fl config Ar file
|
||||
Specify a config to load instead of
|
||||
.Pa @SYSCONFDIR@/dhcpcd.conf .
|
||||
.Nm
|
||||
always processes the config file before any command line options.
|
||||
.It Fl h , Fl Fl hostname Ar hostname
|
||||
Sends
|
||||
.Ar hostname
|
||||
to the DHCP server so it can be registered in DNS.
|
||||
If
|
||||
.Ar hostname
|
||||
is an empty string then the current system hostname is sent.
|
||||
If
|
||||
.Ar hostname
|
||||
is a FQDN (i.e., contains a .) then it will be encoded as such.
|
||||
.It Fl I , Fl Fl clientid Ar clientid
|
||||
Send the
|
||||
.Ar clientid .
|
||||
If the string is of the format 01:02:03 then it is encoded as hex.
|
||||
For interfaces whose hardware address is longer than 8 bytes, or if the
|
||||
.Ar clientid
|
||||
is an empty string then
|
||||
.Nm
|
||||
sends a default
|
||||
.Ar clientid
|
||||
of the hardware family and the hardware address.
|
||||
.It Fl i , Fl Fl vendorclassid Ar vendorclassid
|
||||
Override the DHCPv4
|
||||
.Ar vendorclassid
|
||||
field sent.
|
||||
The default is
|
||||
dhcpcd-<version>:<os>:<machine>:<platform>.
|
||||
For example
|
||||
.D1 dhcpcd-5.5.6:NetBSD-6.99.5:i386:i386
|
||||
If not set then none is sent.
|
||||
Some badly configured DHCP servers reject unknown vendorclassids.
|
||||
To work around it, try and impersonate Windows by using the MSFT vendorclassid.
|
||||
.It Fl j , Fl Fl logfile Ar logfile
|
||||
Writes to the specified
|
||||
.Ar logfile .
|
||||
.Nm
|
||||
still writes to
|
||||
.Xr syslog 3 .
|
||||
The
|
||||
.Ar logfile
|
||||
is reopened when
|
||||
.Nm
|
||||
receives the
|
||||
.Dv SIGUSR2
|
||||
signal.
|
||||
.It Fl k , Fl Fl release Op Ar interface
|
||||
This causes an existing
|
||||
.Nm
|
||||
process running on the
|
||||
.Ar interface
|
||||
to release its lease and de-configure the
|
||||
.Ar interface
|
||||
regardless of the
|
||||
.Fl p , Fl Fl persistent
|
||||
option.
|
||||
If no
|
||||
.Ar interface
|
||||
is specified then this applies to all interfaces in Master mode.
|
||||
If no interfaces are left running,
|
||||
.Nm
|
||||
will exit.
|
||||
.It Fl l , Fl Fl leasetime Ar seconds
|
||||
Request a lease time of
|
||||
.Ar seconds .
|
||||
.Ar -1
|
||||
represents an infinite lease time.
|
||||
By default
|
||||
.Nm
|
||||
does not request any lease time and leaves it in the hands of the
|
||||
DHCP server.
|
||||
.It Fl M , Fl Fl master
|
||||
Start
|
||||
.Nm
|
||||
in Master mode even if only one interface specified on the command line.
|
||||
See the Multiple Interfaces section above.
|
||||
.It Fl m , Fl Fl metric Ar metric
|
||||
Metrics are used to prefer an interface over another one, lowest wins.
|
||||
.Nm
|
||||
will supply a default metic of 200 +
|
||||
.Xr if_nametoindex 3 .
|
||||
An extra 100 will be added for wireless interfaces.
|
||||
.It Fl n , Fl Fl rebind Op Ar interface
|
||||
Notifies
|
||||
.Nm
|
||||
to reload its configuration and rebind the specified
|
||||
.Ar interface .
|
||||
If no
|
||||
.Ar interface
|
||||
is specified then this applies to all interfaces in Master mode.
|
||||
If
|
||||
.Nm
|
||||
is not running, then it starts up as normal.
|
||||
.It Fl N , Fl Fl renew Op Ar interface
|
||||
Notifies
|
||||
.Nm
|
||||
to renew existing addresses on the specified
|
||||
.Ar interface .
|
||||
If no
|
||||
.Ar interface
|
||||
is specified then this applies to all interfaces in Master mode.
|
||||
If
|
||||
.Nm
|
||||
is not running, then it starts up as normal.
|
||||
Unlike the
|
||||
.Fl n , Fl Fl rebind
|
||||
option above, the configuration for
|
||||
.Nm
|
||||
is not reloaded.
|
||||
.It Fl o , Fl Fl option Ar option
|
||||
Request the DHCP
|
||||
.Ar option
|
||||
variable for use in
|
||||
.Pa @SCRIPT@ .
|
||||
.It Fl p , Fl Fl persistent
|
||||
.Nm
|
||||
normally de-configures the
|
||||
.Ar interface
|
||||
and configuration when it exits.
|
||||
Sometimes, this isn't desirable if, for example, you have root mounted over
|
||||
NFS or SSH clients connect to this host and they need to be notified of
|
||||
the host shutting down.
|
||||
You can use this option to stop this from happening.
|
||||
.It Fl r , Fl Fl request Ar address
|
||||
Request the
|
||||
.Ar address
|
||||
in the DHCP DISCOVER message.
|
||||
There is no guarantee this is the address the DHCP server will actually give.
|
||||
If no
|
||||
.Ar address
|
||||
is given then the first address currently assigned to the
|
||||
.Ar interface
|
||||
is used.
|
||||
.It Fl s , Fl Fl inform Ar address Ns Op Ar /cidr Ns Op Ar /broadcast_address
|
||||
Behaves like
|
||||
.Fl r , Fl Fl request
|
||||
as above, but sends a DHCP INFORM instead of DISCOVER/REQUEST.
|
||||
This does not get a lease as such, just notifies the DHCP server of the
|
||||
.Ar address
|
||||
in use.
|
||||
You should also include the optional
|
||||
.Ar cidr
|
||||
network number in case the address is not already configured on the interface.
|
||||
.Nm
|
||||
remains running and pretends it has an infinite lease.
|
||||
.Nm
|
||||
will not de-configure the interface when it exits.
|
||||
If
|
||||
.Nm
|
||||
fails to contact a DHCP server then it returns a failure instead of falling
|
||||
back on IPv4LL.
|
||||
.It Fl Fl inform6
|
||||
Performs a DHCPv6 Information Request.
|
||||
No address is requested or specified, but all other DHCPv6 options are allowed.
|
||||
This is normally performed automatically when the IPv6 Router Advertises
|
||||
that the client should perform this operation.
|
||||
This option is only needed when
|
||||
.Nm
|
||||
is not processing IPv6RA messages and the need for DHCPv6 Information Request
|
||||
exists.
|
||||
.It Fl S , Fl Fl static Ar value
|
||||
Configures a static DHCP
|
||||
.Ar value .
|
||||
If you set
|
||||
.Ic ip_address
|
||||
then
|
||||
.Nm
|
||||
will not attempt to obtain a lease and just use the value for the address with
|
||||
an infinite lease time.
|
||||
.Pp
|
||||
Here is an example which configures a static address, routes and DNS.
|
||||
.D1 dhcpcd -S ip_address=192.168.0.10/24 \e
|
||||
.D1 -S routers=192.168.0.1 \e
|
||||
.D1 -S domain_name_servers=192.168.0.1 \e
|
||||
.D1 eth0
|
||||
.Pp
|
||||
You cannot presently set static DHCPv6 values.
|
||||
Use the
|
||||
.Fl e , Fl Fl env
|
||||
option instead.
|
||||
.It Fl t , Fl Fl timeout Ar seconds
|
||||
Timeout after
|
||||
.Ar seconds ,
|
||||
instead of the default 30.
|
||||
A setting of 0
|
||||
.Ar seconds
|
||||
causes
|
||||
.Nm
|
||||
to wait forever to get a lease.
|
||||
If
|
||||
.Nm
|
||||
is working on a single interface then
|
||||
.Nm
|
||||
will exit when a timeout occurs, otherwise
|
||||
.Nm
|
||||
will fork into the background.
|
||||
.It Fl u , Fl Fl userclass Ar class
|
||||
Tags the DHCPv4 message with the userclass
|
||||
.Ar class .
|
||||
DHCP servers use this to give members of the class DHCP options other than the
|
||||
default, without having to know things like hardware address or hostname.
|
||||
.It Fl v , Fl Fl vendor Ar code , Ns Ar value
|
||||
Add an encapsulated vendor option.
|
||||
.Ar code
|
||||
should be between 1 and 254 inclusive.
|
||||
To add a raw vendor string, omit
|
||||
.Ar code
|
||||
but keep the comma.
|
||||
Examples.
|
||||
.Pp
|
||||
Set the vendor option 01 with an IP address.
|
||||
.D1 dhcpcd \-v 01,192.168.0.2 eth0
|
||||
Set the vendor option 02 with a hex code.
|
||||
.D1 dhcpcd \-v 02,01:02:03:04:05 eth0
|
||||
Set the vendor option 03 with an IP address as a string.
|
||||
.D1 dhcpcd \-v 03,\e"192.168.0.2\e" eth0
|
||||
Set un-encapsulated vendor option to hello world.
|
||||
.D1 dhcpcd \-v ,"hello world" eth0
|
||||
.It Fl Fl version
|
||||
Display both program version and copyright information.
|
||||
.Nm
|
||||
then exits before doing any configuration.
|
||||
.It Fl w
|
||||
Wait for an address to be assigned before forking to the background.
|
||||
Does not take an argument, unlike the below option.
|
||||
.It Fl Fl waitip Ns = Ns Op 4 | 6
|
||||
Wait for an address to be assigned before forking to the background.
|
||||
4 means wait for an IPv4 address to be assigned.
|
||||
6 means wait for an IPv6 address to be assigned.
|
||||
If no argument is given,
|
||||
.Nm
|
||||
will wait for any address protocol to be assigned.
|
||||
It is possible to wait for more than one address protocol and
|
||||
.Nm
|
||||
will only fork to the background when all waiting conditions are satisfied.
|
||||
.It Fl x , Fl Fl exit Op Ar interface
|
||||
This will signal an existing
|
||||
.Nm
|
||||
process running on the
|
||||
.Ar interface
|
||||
to exit.
|
||||
If no
|
||||
.Ar interface
|
||||
is specified, then the above is applied to all interfaces in Master mode.
|
||||
See the
|
||||
.Fl p , Fl Fl persistent
|
||||
option to control configuration persistence on exit,
|
||||
which is enabled by default in
|
||||
.Xr dhcpcd.conf 5 .
|
||||
.Nm
|
||||
then waits until this process has exited.
|
||||
.It Fl y , Fl Fl reboot Ar seconds
|
||||
Allow
|
||||
.Ar reboot
|
||||
seconds before moving to the discover phase if we have an old lease to use.
|
||||
Allow
|
||||
.Ar reboot
|
||||
seconds before starting fallback states from the discover phase.
|
||||
IPv4LL is started when the first
|
||||
.Ar reboot
|
||||
timeout is reached.
|
||||
The default is 5 seconds.
|
||||
A setting of 0 seconds causes
|
||||
.Nm
|
||||
to skip the reboot phase and go straight into discover.
|
||||
This has no effect on DHCPv6 other than skipping the reboot phase.
|
||||
.El
|
||||
.Ss Restricting behaviour
|
||||
.Nm
|
||||
will try to do as much as it can by default.
|
||||
However, there are sometimes situations where you don't want the things to be
|
||||
configured exactly how the DHCP server wants.
|
||||
Here are some options that deal with turning these bits off.
|
||||
.Pp
|
||||
Note that when
|
||||
.Nm
|
||||
is restricted to a single interface then the interface also needs to be
|
||||
specified when asking
|
||||
.Nm
|
||||
to exit using the commandline.
|
||||
If the protocol is restricted as well then the protocol needs to be included
|
||||
with the exit instruction.
|
||||
.Bl -tag -width indent
|
||||
.It Fl 1 , Fl Fl oneshot
|
||||
Exit after configuring an interface.
|
||||
Use the
|
||||
.Fl w , Fl Fl waitip
|
||||
option to specify which protocol(s) to configure before exiting.
|
||||
.It Fl 4 , Fl Fl ipv4only
|
||||
Configure IPv4 only.
|
||||
.It Fl 6 , Fl Fl ipv6only
|
||||
Configure IPv6 only.
|
||||
.It Fl A , Fl Fl noarp
|
||||
Don't request or claim the address by ARP.
|
||||
This also disables IPv4LL.
|
||||
.It Fl B , Fl Fl nobackground
|
||||
Don't run in the background when we acquire a lease.
|
||||
This is mainly useful for running under the control of another process, such
|
||||
as a debugger or a network manager.
|
||||
.It Fl C , Fl Fl nohook Ar script
|
||||
Don't run this hook script.
|
||||
Matches full name, or prefixed with 2 numbers optionally ending with
|
||||
.Pa .sh .
|
||||
.Pp
|
||||
So to stop
|
||||
.Nm
|
||||
from touching your DNS settings you would do:-
|
||||
.D1 dhcpcd -C resolv.conf eth0
|
||||
.It Fl G , Fl Fl nogateway
|
||||
Don't set any default routes.
|
||||
.It Fl H , Fl Fl xidhwaddr
|
||||
Use the last four bytes of the hardware address as the DHCP xid instead
|
||||
of a randomly generated number.
|
||||
.It Fl J , Fl Fl broadcast
|
||||
Instructs the DHCP server to broadcast replies back to the client.
|
||||
Normally this is only set for non-Ethernet interfaces,
|
||||
such as FireWire and InfiniBand.
|
||||
In most instances,
|
||||
.Nm
|
||||
will set this automatically.
|
||||
.It Fl K , Fl Fl nolink
|
||||
Don't receive link messages for carrier status.
|
||||
You should only have to use this with buggy device drivers or running
|
||||
.Nm
|
||||
through a network manager.
|
||||
.It Fl L , Fl Fl noipv4ll
|
||||
Don't use IPv4LL (aka APIPA, aka Bonjour, aka ZeroConf).
|
||||
.It Fl O , Fl Fl nooption Ar option
|
||||
Removes the
|
||||
.Ar option
|
||||
from the DHCP message before processing.
|
||||
.It Fl P , Fl Fl printpidfile
|
||||
Print the
|
||||
.Pa pidfile
|
||||
.Nm
|
||||
will use based on commmand-line arguments to stdout.
|
||||
.It Fl Q , Fl Fl require Ar option
|
||||
Requires the
|
||||
.Ar option
|
||||
to be present in all DHCP messages, otherwise the message is ignored.
|
||||
To enforce that
|
||||
.Nm
|
||||
only responds to DHCP servers and not BOOTP servers, you can
|
||||
.Fl Q
|
||||
.Ar dhcp_message_type .
|
||||
.It Fl q , Fl Fl quiet
|
||||
Quiet
|
||||
.Nm
|
||||
on the command line, only warnings and errors will be displayed.
|
||||
If this option is used another time then all console output is disabled.
|
||||
These messages are still logged via
|
||||
.Xr syslog 3 .
|
||||
.It Fl T , Fl Fl test
|
||||
On receipt of DHCP messages just call
|
||||
.Pa @SCRIPT@
|
||||
with the reason of TEST which echos the DHCP variables found in the message
|
||||
to the console.
|
||||
The interface configuration isn't touched and neither are any configuration
|
||||
files.
|
||||
The
|
||||
.Ar rapid_commit
|
||||
option is not sent in TEST mode so that the server does not lease an address.
|
||||
To test INFORM the interface needs to be configured with the desired address
|
||||
before starting
|
||||
.Nm .
|
||||
.It Fl U , Fl Fl dumplease Op Ar interface
|
||||
Dumps the current lease for the
|
||||
.Ar interface
|
||||
to stdout.
|
||||
If no
|
||||
.Ar interface
|
||||
is given then all interfaces are dumped.
|
||||
Use the
|
||||
.Fl 4
|
||||
or
|
||||
.Fl 6
|
||||
flags to specify an address family.
|
||||
If a lease is piped in via standard input then that is dumped.
|
||||
In this case, specifying an address family is mandatory.
|
||||
.It Fl V , Fl Fl variables
|
||||
Display a list of option codes, the associated variable and encoding for use in
|
||||
.Xr dhcpcd-run-hooks 8 .
|
||||
Variables are prefixed with new_ and old_ unless the option number is -.
|
||||
Variables without an option are part of the DHCP message and cannot be
|
||||
directly requested.
|
||||
.It Fl W , Fl Fl whitelist Ar address Ns Op /cidr
|
||||
Only accept packets from
|
||||
.Ar address Ns Op /cidr .
|
||||
.Fl X , Fl Fl blacklist
|
||||
is ignored if
|
||||
.Fl W , Fl Fl whitelist
|
||||
is set.
|
||||
.It Fl X , Fl Fl blacklist Ar address Ns Op Ar /cidr
|
||||
Ignore all packets from
|
||||
.Ar address Ns Op Ar /cidr .
|
||||
.It Fl Z , Fl Fl denyinterfaces Ar pattern
|
||||
When discovering interfaces, the interface name must not match
|
||||
.Ar pattern
|
||||
which is a space or comma separated list of patterns passed to
|
||||
.Xr fnmatch 3 .
|
||||
.It Fl z , Fl Fl allowinterfaces Ar pattern
|
||||
When discovering interfaces, the interface name must match
|
||||
.Ar pattern
|
||||
which is a space or comma separated list of patterns passed to
|
||||
.Xr fnmatch 3 .
|
||||
If the same interface is matched in
|
||||
.Fl Z , Fl Fl denyinterfaces
|
||||
then it is still denied.
|
||||
.It Fl Fl inactive
|
||||
Don't start any interfaces other than those specified on the command line.
|
||||
This allows
|
||||
.Nm
|
||||
to be started in Master mode and then wait for subsequent
|
||||
.Nm
|
||||
commands to start each interface as required.
|
||||
.It Fl Fl configure
|
||||
Allows
|
||||
.Nm
|
||||
to configure the system.
|
||||
This is the default behaviour and sets
|
||||
.Ev if_configured=true .
|
||||
.It Fl Fl noconfigure
|
||||
.Nm
|
||||
will not configure the system at all.
|
||||
This is only of use if the
|
||||
.Fl Fl script
|
||||
that
|
||||
.Nm
|
||||
calls at each network event configures the system instead.
|
||||
This is different from
|
||||
.Fl T , Fl Fl test
|
||||
mode in that it's not one shot and the only change to the environment is the
|
||||
addition of
|
||||
.Ev if_configured=false .
|
||||
.It Fl Fl nodev
|
||||
Don't load any
|
||||
.Pa /dev
|
||||
management modules.
|
||||
.El
|
||||
.Sh 3RDPARTY LINK MANAGEMENT
|
||||
Some interfaces require configuration by 3rd parties, such as PPP or VPN.
|
||||
When an interface configuration in
|
||||
.Nm
|
||||
is marked as STATIC or INFORM without an address then
|
||||
.Nm
|
||||
will monitor the interface until an address is added or removed from it and
|
||||
act accordingly.
|
||||
For point to point interfaces (like PPP), a default route to its
|
||||
destination is automatically added to the configuration.
|
||||
If the point to point interface is configured for INFORM, then
|
||||
.Nm
|
||||
unicasts INFORM to the destination, otherwise it defaults to STATIC.
|
||||
.Sh NOTES
|
||||
.Nm
|
||||
requires a Berkley Packet Filter, or BPF device on BSD based systems and a
|
||||
Linux Socket Filter, or LPF device on Linux based systems for all IPv4
|
||||
configuration.
|
||||
.Pp
|
||||
If restricting
|
||||
.Nm
|
||||
to a single interface and optionally address family via the command-line
|
||||
then all further calls to
|
||||
.Nm
|
||||
to rebind, reconfigure or exit need to include the same restrictive flags
|
||||
so that
|
||||
.Nm
|
||||
knows which process to signal.
|
||||
.Pp
|
||||
Some DHCP servers implement ClientID filtering.
|
||||
If
|
||||
.Nm
|
||||
is replacing an in-use DHCP client then you might need to adjust the clientid
|
||||
option
|
||||
.Nm
|
||||
sends to match.
|
||||
If using a DUID in place of the ClientID, edit
|
||||
.Pa @DBDIR@/duid
|
||||
accordingly.
|
||||
.Sh FILES
|
||||
.Bl -ohang
|
||||
.It Pa @SYSCONFDIR@/dhcpcd.conf
|
||||
Configuration file for dhcpcd.
|
||||
If you always use the same options, put them here.
|
||||
.It Pa @SCRIPT@
|
||||
Bourne shell script that is run to configure or de-configure an interface.
|
||||
.It Pa @LIBDIR@/dhcpcd/dev
|
||||
Linux
|
||||
.Pa /dev
|
||||
management modules.
|
||||
.It Pa @HOOKDIR@
|
||||
A directory containing bourne shell scripts that are run by the above script.
|
||||
Each script can be disabled by using the
|
||||
.Fl C , Fl Fl nohook
|
||||
option described above.
|
||||
.It Pa @DBDIR@/duid
|
||||
Text file that holds the DUID used to identify the host.
|
||||
.It Pa @DBDIR@/secret
|
||||
Text file that holds a secret key known only to the host.
|
||||
.It Pa @DBDIR@/ Ns Ar interface Ns Ar -ssid Ns .lease
|
||||
The actual DHCP message sent by the server.
|
||||
We use this when reading the last
|
||||
lease and use the file's mtime as when it was issued.
|
||||
.It Pa @DBDIR@/ Ns Ar interface Ns Ar -ssid Ns .lease6
|
||||
The actual DHCPv6 message sent by the server.
|
||||
We use this when reading the last
|
||||
lease and use the file's mtime as when it was issued.
|
||||
.It Pa @DBDIR@/rdm_monotonic
|
||||
Stores the monotonic counter used in the
|
||||
.Ar replay
|
||||
field in Authentication Options.
|
||||
.It Pa @RUNDIR@/pid
|
||||
Stores the PID of
|
||||
.Nm
|
||||
running on all interfaces.
|
||||
.It Pa @RUNDIR@/ Ns Ar interface Ns .pid
|
||||
Stores the PID of
|
||||
.Nm
|
||||
running on the
|
||||
.Ar interface .
|
||||
.It Pa @RUNDIR@/sock
|
||||
Control socket to the master daemon.
|
||||
.It Pa @RUNDIR@/unpriv.sock
|
||||
Unprivileged socket to the master daemon, only allows state retrieval.
|
||||
.It Pa @RUNDIR@/ Ns Ar interface Ns .sock
|
||||
Control socket to per interface daemon.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr fnmatch 3 ,
|
||||
.Xr if_nametoindex 3 ,
|
||||
.Xr dhcpcd.conf 5 ,
|
||||
.Xr resolv.conf 5 ,
|
||||
.Xr dhcpcd-run-hooks 8 ,
|
||||
.Xr resolvconf 8
|
||||
.Sh STANDARDS
|
||||
RFC\ 951, RFC\ 1534, RFC\ 2104, RFC\ 2131, RFC\ 2132, RFC\ 2563, RFC\ 2855,
|
||||
RFC\ 3004, RFC\ 3118, RFC\ 3203, RFC\ 3315, RFC\ 3361, RFC\ 3633, RFC\ 3396,
|
||||
RFC\ 3397, RFC\ 3442, RFC\ 3495, RFC\ 3925, RFC\ 3927, RFC\ 4039, RFC\ 4075,
|
||||
RFC\ 4242, RFC\ 4361, RFC\ 4390, RFC\ 4702, RFC\ 4074, RFC\ 4861, RFC\ 4833,
|
||||
RFC\ 4941, RFC\ 5227, RFC\ 5942, RFC\ 5969, RFC\ 6106, RFC\ 6334, RFC\ 6355,
|
||||
RFC\ 6603, RFC\ 6704, RFC\ 7217, RFC\ 7550, RFC\ 7844.
|
||||
.Sh AUTHORS
|
||||
.An Roy Marples Aq Mt roy@marples.name
|
||||
.Sh BUGS
|
||||
Please report them to
|
||||
.Lk http://roy.marples.name/projects/dhcpcd
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,48 @@
|
|||
# A sample configuration for dhcpcd.
|
||||
# See dhcpcd.conf(5) for details.
|
||||
|
||||
# Allow users of this group to interact with dhcpcd via the control socket.
|
||||
#controlgroup wheel
|
||||
|
||||
# Inform the DHCP server of our hostname for DDNS.
|
||||
#hostname
|
||||
|
||||
# Use the hardware address of the interface for the Client ID.
|
||||
#clientid
|
||||
# or
|
||||
# Use the same DUID + IAID as set in DHCPv6 for DHCPv4 ClientID as per RFC4361.
|
||||
# Some non-RFC compliant DHCP servers do not reply with this set.
|
||||
# In this case, comment out duid and enable clientid above.
|
||||
duid
|
||||
|
||||
# Persist interface configuration when dhcpcd exits.
|
||||
persistent
|
||||
|
||||
# vendorclassid is set to blank to avoid sending the default of
|
||||
# dhcpcd-<version>:<os>:<machine>:<platform>
|
||||
vendorclassid
|
||||
|
||||
# A list of options to request from the DHCP server.
|
||||
option domain_name_servers, domain_name, domain_search
|
||||
option classless_static_routes
|
||||
# Respect the network MTU. This is applied to DHCP routes.
|
||||
option interface_mtu
|
||||
|
||||
# Request a hostname from the network
|
||||
option host_name
|
||||
|
||||
# Most distributions have NTP support.
|
||||
#option ntp_servers
|
||||
|
||||
# Rapid commit support.
|
||||
# Safe to enable by default because it requires the equivalent option set
|
||||
# on the server to actually work.
|
||||
option rapid_commit
|
||||
|
||||
# A ServerID is required by RFC2131.
|
||||
require dhcp_server_identifier
|
||||
|
||||
# Generate SLAAC address using the Hardware Address of the interface
|
||||
#slaac hwaddr
|
||||
# OR generate Stable Private IPv6 Addresses based from the DUID
|
||||
slaac private
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,280 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef DHCPCD_H
|
||||
#define DHCPCD_H
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "config.h"
|
||||
#ifdef HAVE_SYS_QUEUE_H
|
||||
#include <sys/queue.h>
|
||||
#endif
|
||||
|
||||
#include "defs.h"
|
||||
#include "control.h"
|
||||
#include "if-options.h"
|
||||
|
||||
#define HWADDR_LEN 20
|
||||
#define IF_SSIDLEN 32
|
||||
#define PROFILE_LEN 64
|
||||
#define SECRET_LEN 64
|
||||
|
||||
#define IF_INACTIVE 0
|
||||
#define IF_ACTIVE 1
|
||||
#define IF_ACTIVE_USER 2
|
||||
|
||||
#define LINK_UP 1
|
||||
#define LINK_UNKNOWN 0
|
||||
#define LINK_DOWN -1
|
||||
|
||||
#define IF_DATA_IPV4 0
|
||||
#define IF_DATA_ARP 1
|
||||
#define IF_DATA_IPV4LL 2
|
||||
#define IF_DATA_DHCP 3
|
||||
#define IF_DATA_IPV6 4
|
||||
#define IF_DATA_IPV6ND 5
|
||||
#define IF_DATA_DHCP6 6
|
||||
#define IF_DATA_MAX 7
|
||||
|
||||
#ifdef __QNX__
|
||||
/* QNX carries defines for, but does not actually support PF_LINK */
|
||||
#undef IFLR_ACTIVE
|
||||
#endif
|
||||
|
||||
struct interface {
|
||||
struct dhcpcd_ctx *ctx;
|
||||
TAILQ_ENTRY(interface) next;
|
||||
char name[IF_NAMESIZE];
|
||||
unsigned int index;
|
||||
unsigned int active;
|
||||
unsigned int flags;
|
||||
uint16_t hwtype; /* ARPHRD_ETHER for example */
|
||||
unsigned char hwaddr[HWADDR_LEN];
|
||||
uint8_t hwlen;
|
||||
unsigned short vlanid;
|
||||
unsigned int metric;
|
||||
int carrier;
|
||||
bool wireless;
|
||||
uint8_t ssid[IF_SSIDLEN];
|
||||
unsigned int ssid_len;
|
||||
|
||||
char profile[PROFILE_LEN];
|
||||
struct if_options *options;
|
||||
void *if_data[IF_DATA_MAX];
|
||||
};
|
||||
TAILQ_HEAD(if_head, interface);
|
||||
|
||||
#include "privsep.h"
|
||||
|
||||
/* dhcpcd requires CMSG_SPACE to evaluate to a compile time constant. */
|
||||
#if defined(__QNX) || \
|
||||
(defined(__NetBSD_Version__) && __NetBSD_Version__ < 600000000)
|
||||
#undef CMSG_SPACE
|
||||
#endif
|
||||
|
||||
#ifndef ALIGNBYTES
|
||||
#define ALIGNBYTES (sizeof(int) - 1)
|
||||
#endif
|
||||
#ifndef ALIGN
|
||||
#define ALIGN(p) (((unsigned int)(p) + ALIGNBYTES) & ~ALIGNBYTES)
|
||||
#endif
|
||||
#ifndef CMSG_SPACE
|
||||
#define CMSG_SPACE(len) (ALIGN(sizeof(struct cmsghdr)) + ALIGN(len))
|
||||
#endif
|
||||
|
||||
struct passwd;
|
||||
|
||||
struct dhcpcd_ctx {
|
||||
char pidfile[sizeof(PIDFILE) + IF_NAMESIZE + 1];
|
||||
char vendor[256];
|
||||
int fork_fd; /* FD for the fork init signal pipe */
|
||||
const char *cffile;
|
||||
unsigned long long options;
|
||||
char *logfile;
|
||||
int argc;
|
||||
char **argv;
|
||||
int ifac; /* allowed interfaces */
|
||||
char **ifav; /* allowed interfaces */
|
||||
int ifdc; /* denied interfaces */
|
||||
char **ifdv; /* denied interfaces */
|
||||
int ifc; /* listed interfaces */
|
||||
char **ifv; /* listed interfaces */
|
||||
int ifcc; /* configured interfaces */
|
||||
char **ifcv; /* configured interfaces */
|
||||
uint8_t duid_type;
|
||||
unsigned char *duid;
|
||||
size_t duid_len;
|
||||
struct if_head *ifaces;
|
||||
|
||||
char *ctl_buf;
|
||||
size_t ctl_buflen;
|
||||
size_t ctl_bufpos;
|
||||
size_t ctl_extra;
|
||||
|
||||
rb_tree_t routes; /* our routes */
|
||||
#ifdef RT_FREE_ROUTE_TABLE
|
||||
rb_tree_t froutes; /* free routes for re-use */
|
||||
#endif
|
||||
size_t rt_order; /* route order storage */
|
||||
|
||||
int pf_inet_fd;
|
||||
#ifdef PF_LINK
|
||||
int pf_link_fd;
|
||||
#endif
|
||||
void *priv;
|
||||
int link_fd;
|
||||
#ifndef SMALL
|
||||
int link_rcvbuf;
|
||||
#endif
|
||||
int seq; /* route message sequence no */
|
||||
int sseq; /* successful seq no sent */
|
||||
|
||||
#ifdef USE_SIGNALS
|
||||
sigset_t sigset;
|
||||
#endif
|
||||
struct eloop *eloop;
|
||||
|
||||
char *script;
|
||||
#ifdef HAVE_OPEN_MEMSTREAM
|
||||
FILE *script_fp;
|
||||
#endif
|
||||
char *script_buf;
|
||||
size_t script_buflen;
|
||||
char **script_env;
|
||||
size_t script_envlen;
|
||||
|
||||
int control_fd;
|
||||
int control_unpriv_fd;
|
||||
struct fd_list_head control_fds;
|
||||
char control_sock[sizeof(CONTROLSOCKET) + IF_NAMESIZE];
|
||||
char control_sock_unpriv[sizeof(CONTROLSOCKET) + IF_NAMESIZE + 7];
|
||||
gid_t control_group;
|
||||
|
||||
/* DHCP Enterprise options, RFC3925 */
|
||||
struct dhcp_opt *vivso;
|
||||
size_t vivso_len;
|
||||
|
||||
char *randomstate; /* original state */
|
||||
|
||||
/* For filtering RTM_MISS messages per router */
|
||||
#ifdef BSD
|
||||
uint8_t *rt_missfilter;
|
||||
size_t rt_missfilterlen;
|
||||
size_t rt_missfiltersize;
|
||||
#endif
|
||||
|
||||
#ifdef PRIVSEP
|
||||
struct passwd *ps_user; /* struct passwd for privsep user */
|
||||
struct ps_process_head ps_processes; /* List of spawned processes */
|
||||
struct ps_process *ps_root;
|
||||
struct ps_process *ps_inet;
|
||||
struct ps_process *ps_ctl;
|
||||
int ps_data_fd; /* data returned from processes */
|
||||
int ps_log_fd; /* chroot logging */
|
||||
int ps_log_root_fd; /* outside chroot log reader */
|
||||
struct eloop *ps_eloop; /* eloop for polling root data */
|
||||
struct fd_list *ps_control; /* Queue for the above */
|
||||
struct fd_list *ps_control_client; /* Queue for the above */
|
||||
#endif
|
||||
|
||||
#ifdef INET
|
||||
struct dhcp_opt *dhcp_opts;
|
||||
size_t dhcp_opts_len;
|
||||
|
||||
int udp_rfd;
|
||||
int udp_wfd;
|
||||
|
||||
/* Our aggregate option buffer.
|
||||
* We ONLY use this when options are split, which for most purposes is
|
||||
* practically never. See RFC3396 for details. */
|
||||
uint8_t *opt_buffer;
|
||||
size_t opt_buffer_len;
|
||||
#endif
|
||||
#ifdef INET6
|
||||
uint8_t *secret;
|
||||
size_t secret_len;
|
||||
|
||||
#ifndef __sun
|
||||
int nd_fd;
|
||||
#endif
|
||||
struct ra_head *ra_routers;
|
||||
|
||||
struct dhcp_opt *nd_opts;
|
||||
size_t nd_opts_len;
|
||||
#ifdef DHCP6
|
||||
int dhcp6_rfd;
|
||||
int dhcp6_wfd;
|
||||
struct dhcp_opt *dhcp6_opts;
|
||||
size_t dhcp6_opts_len;
|
||||
#endif
|
||||
|
||||
#ifndef __linux__
|
||||
int ra_global;
|
||||
#endif
|
||||
#endif /* INET6 */
|
||||
|
||||
#ifdef PLUGIN_DEV
|
||||
char *dev_load;
|
||||
int dev_fd;
|
||||
struct dev *dev;
|
||||
void *dev_handle;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef USE_SIGNALS
|
||||
extern const int dhcpcd_signals[];
|
||||
extern const size_t dhcpcd_signals_len;
|
||||
extern const int dhcpcd_signals_ignore[];
|
||||
extern const size_t dhcpcd_signals_ignore_len;
|
||||
#endif
|
||||
|
||||
extern const char *dhcpcd_default_script;
|
||||
|
||||
int dhcpcd_ifafwaiting(const struct interface *);
|
||||
int dhcpcd_afwaiting(const struct dhcpcd_ctx *);
|
||||
void dhcpcd_daemonised(struct dhcpcd_ctx *);
|
||||
void dhcpcd_daemonise(struct dhcpcd_ctx *);
|
||||
|
||||
void dhcpcd_signal_cb(int, void *);
|
||||
|
||||
void dhcpcd_linkoverflow(struct dhcpcd_ctx *);
|
||||
int dhcpcd_handleargs(struct dhcpcd_ctx *, struct fd_list *, int, char **);
|
||||
void dhcpcd_handlecarrier(struct interface *, int, unsigned int);
|
||||
int dhcpcd_handleinterface(void *, int, const char *);
|
||||
void dhcpcd_handlehwaddr(struct interface *, uint16_t, const void *, uint8_t);
|
||||
void dhcpcd_dropinterface(struct interface *, const char *);
|
||||
int dhcpcd_selectprofile(struct interface *, const char *);
|
||||
|
||||
void dhcpcd_startinterface(void *);
|
||||
void dhcpcd_activateinterface(struct interface *, unsigned long long);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,247 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define UUID_LEN 36
|
||||
#define DUID_TIME_EPOCH 946684800
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef BSD
|
||||
# include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_arp.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "dhcpcd.h"
|
||||
#include "duid.h"
|
||||
#include "logerr.h"
|
||||
|
||||
/*
|
||||
* Machine, system or product UUIDs are not guaranteed unique.
|
||||
* Let's not use them by default.
|
||||
*/
|
||||
#ifdef USE_MACHINE_UUID
|
||||
static size_t
|
||||
duid_machineuuid(char *uuid, size_t uuid_len)
|
||||
{
|
||||
int r;
|
||||
size_t len = uuid_len;
|
||||
|
||||
#if defined(HW_UUID) /* OpenBSD */
|
||||
int mib[] = { CTL_HW, HW_UUID };
|
||||
|
||||
r = sysctl(mib, sizeof(mib)/sizeof(mib[0]), uuid, &len, NULL, 0);
|
||||
#elif defined(KERN_HOSTUUID) /* FreeBSD */
|
||||
int mib[] = { CTL_KERN, KERN_HOSTUUID };
|
||||
|
||||
r = sysctl(mib, sizeof(mib)/sizeof(mib[0]), uuid, &len, NULL, 0);
|
||||
#elif defined(__NetBSD__)
|
||||
r = sysctlbyname("machdep.dmi.system-uuid", uuid, &len, NULL, 0);
|
||||
#elif defined(__linux__)
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/sys/class/dmi/id/product_uuid", "r");
|
||||
if (fp == NULL)
|
||||
return 0;
|
||||
if (fgets(uuid, (int)uuid_len, fp) == NULL) {
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
len = strlen(uuid) + 1;
|
||||
fclose(fp);
|
||||
r = len == 1 ? -1 : 0;
|
||||
#else
|
||||
UNUSED(uuid);
|
||||
r = -1;
|
||||
errno = ENOSYS;
|
||||
#endif
|
||||
|
||||
if (r == -1)
|
||||
return 0;
|
||||
return len;
|
||||
}
|
||||
|
||||
static size_t
|
||||
duid_make_uuid(uint8_t *d)
|
||||
{
|
||||
uint16_t type = htons(DUID_UUID);
|
||||
char uuid[UUID_LEN + 1];
|
||||
size_t l;
|
||||
|
||||
if (duid_machineuuid(uuid, sizeof(uuid)) != sizeof(uuid))
|
||||
return 0;
|
||||
|
||||
/* All zeros UUID is not valid */
|
||||
if (strcmp("00000000-0000-0000-0000-000000000000", uuid) == 0)
|
||||
return 0;
|
||||
|
||||
memcpy(d, &type, sizeof(type));
|
||||
l = sizeof(type);
|
||||
d += sizeof(type);
|
||||
l += hwaddr_aton(d, uuid);
|
||||
return l;
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t
|
||||
duid_make(void *d, const struct interface *ifp, uint16_t type)
|
||||
{
|
||||
uint8_t *p;
|
||||
uint16_t u16;
|
||||
time_t t;
|
||||
uint32_t u32;
|
||||
|
||||
if (ifp->hwlen == 0)
|
||||
return 0;
|
||||
|
||||
p = d;
|
||||
u16 = htons(type);
|
||||
memcpy(p, &u16, sizeof(u16));
|
||||
p += sizeof(u16);
|
||||
u16 = htons(ifp->hwtype);
|
||||
memcpy(p, &u16, sizeof(u16));
|
||||
p += sizeof(u16);
|
||||
if (type == DUID_LLT) {
|
||||
/* time returns seconds from jan 1 1970, but DUID-LLT is
|
||||
* seconds from jan 1 2000 modulo 2^32 */
|
||||
t = time(NULL) - DUID_TIME_EPOCH;
|
||||
u32 = htonl((uint32_t)t & 0xffffffff);
|
||||
memcpy(p, &u32, sizeof(u32));
|
||||
p += sizeof(u32);
|
||||
}
|
||||
/* Finally, add the MAC address of the interface */
|
||||
memcpy(p, ifp->hwaddr, ifp->hwlen);
|
||||
p += ifp->hwlen;
|
||||
return (size_t)(p - (uint8_t *)d);
|
||||
}
|
||||
|
||||
#define DUID_STRLEN DUID_LEN * 3
|
||||
static size_t
|
||||
duid_get(struct dhcpcd_ctx *ctx, const struct interface *ifp)
|
||||
{
|
||||
uint8_t *data;
|
||||
size_t len, slen;
|
||||
char line[DUID_STRLEN];
|
||||
const struct interface *ifp2;
|
||||
|
||||
/* If we already have a DUID then use it as it's never supposed
|
||||
* to change once we have one even if the interfaces do */
|
||||
if ((len = dhcp_read_hwaddr_aton(ctx, &data, DUID)) != 0) {
|
||||
if (len <= DUID_LEN) {
|
||||
ctx->duid = data;
|
||||
return len;
|
||||
}
|
||||
logerrx("DUID too big (max %u): %s", DUID_LEN, DUID);
|
||||
/* Keep the buffer, will assign below. */
|
||||
} else {
|
||||
if (errno != ENOENT)
|
||||
logerr("%s", DUID);
|
||||
if ((data = malloc(DUID_LEN)) == NULL) {
|
||||
logerr(__func__);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* No file? OK, lets make one based the machines UUID */
|
||||
if (ifp == NULL) {
|
||||
#ifdef USE_MACHINE_UUID
|
||||
if (ctx->duid_type != DUID_DEFAULT &&
|
||||
ctx->duid_type != DUID_UUID)
|
||||
len = 0;
|
||||
else
|
||||
len = duid_make_uuid(data);
|
||||
if (len == 0)
|
||||
free(data);
|
||||
else
|
||||
ctx->duid = data;
|
||||
return len;
|
||||
#else
|
||||
free(data);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Regardless of what happens we will create a DUID to use. */
|
||||
ctx->duid = data;
|
||||
|
||||
/* No UUID? OK, lets make one based on our interface */
|
||||
if (ifp->hwlen == 0) {
|
||||
logwarnx("%s: does not have hardware address", ifp->name);
|
||||
TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
|
||||
if (ifp2->hwlen != 0)
|
||||
break;
|
||||
}
|
||||
if (ifp2) {
|
||||
ifp = ifp2;
|
||||
logwarnx("picked interface %s to generate a DUID",
|
||||
ifp->name);
|
||||
} else {
|
||||
if (ctx->duid_type != DUID_LL)
|
||||
logwarnx("no interfaces have a fixed hardware "
|
||||
"address");
|
||||
return duid_make(data, ifp, DUID_LL);
|
||||
}
|
||||
}
|
||||
|
||||
len = duid_make(data, ifp,
|
||||
ctx->duid_type == DUID_LL ? DUID_LL : DUID_LLT);
|
||||
hwaddr_ntoa(data, len, line, sizeof(line));
|
||||
slen = strlen(line);
|
||||
if (slen < sizeof(line) - 2) {
|
||||
line[slen++] = '\n';
|
||||
line[slen] = '\0';
|
||||
}
|
||||
if (dhcp_writefile(ctx, DUID, 0640, line, slen) == -1) {
|
||||
logerr("%s: cannot write duid", __func__);
|
||||
if (ctx->duid_type != DUID_LL)
|
||||
return duid_make(data, ifp, DUID_LL);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t
|
||||
duid_init(struct dhcpcd_ctx *ctx, const struct interface *ifp)
|
||||
{
|
||||
|
||||
if (ctx->duid == NULL)
|
||||
ctx->duid_len = duid_get(ctx, ifp);
|
||||
return ctx->duid_len;
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef DUID_H
|
||||
#define DUID_H
|
||||
|
||||
#define DUID_LEN 128 + 2
|
||||
#define DUID_DEFAULT 0
|
||||
#define DUID_LLT 1
|
||||
#define DUID_LL 3
|
||||
#define DUID_UUID 4
|
||||
|
||||
size_t duid_make(void *, const struct interface *, uint16_t);
|
||||
size_t duid_init(struct dhcpcd_ctx *, const struct interface *);
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,97 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef ELOOP_H
|
||||
#define ELOOP_H
|
||||
|
||||
#include <time.h>
|
||||
|
||||
/* Handy macros to create subsecond timeouts */
|
||||
#define CSEC_PER_SEC 100
|
||||
#define MSEC_PER_SEC 1000
|
||||
#define NSEC_PER_CSEC 10000000
|
||||
#define NSEC_PER_MSEC 1000000
|
||||
#define NSEC_PER_SEC 1000000000
|
||||
|
||||
/* eloop queues are really only for deleting timeouts registered
|
||||
* for a function or object.
|
||||
* The idea being that one interface has different timeouts for
|
||||
* say DHCP and DHCPv6. */
|
||||
#ifndef ELOOP_QUEUE
|
||||
#define ELOOP_QUEUE 1
|
||||
#endif
|
||||
|
||||
/* Used for deleting a timeout for all queues. */
|
||||
#define ELOOP_QUEUE_ALL 0
|
||||
|
||||
/* Forward declare eloop - the content should be invisible to the outside */
|
||||
struct eloop;
|
||||
|
||||
#define ELE_READ 0x0001
|
||||
#define ELE_WRITE 0x0002
|
||||
#define ELE_ERROR 0x0100
|
||||
#define ELE_HANGUP 0x0200
|
||||
#define ELE_NVAL 0x0400
|
||||
|
||||
size_t eloop_event_count(const struct eloop *);
|
||||
int eloop_event_add(struct eloop *, int, unsigned short,
|
||||
void (*)(void *, unsigned short), void *);
|
||||
int eloop_event_delete(struct eloop *, int);
|
||||
|
||||
unsigned long long eloop_timespec_diff(const struct timespec *tsp,
|
||||
const struct timespec *usp, unsigned int *nsp);
|
||||
#define eloop_timeout_add_tv(eloop, tv, cb, ctx) \
|
||||
eloop_q_timeout_add_tv((eloop), ELOOP_QUEUE, (tv), (cb), (ctx))
|
||||
#define eloop_timeout_add_sec(eloop, tv, cb, ctx) \
|
||||
eloop_q_timeout_add_sec((eloop), ELOOP_QUEUE, (tv), (cb), (ctx))
|
||||
#define eloop_timeout_add_msec(eloop, ms, cb, ctx) \
|
||||
eloop_q_timeout_add_msec((eloop), ELOOP_QUEUE, (ms), (cb), (ctx))
|
||||
#define eloop_timeout_delete(eloop, cb, ctx) \
|
||||
eloop_q_timeout_delete((eloop), ELOOP_QUEUE, (cb), (ctx))
|
||||
int eloop_q_timeout_add_tv(struct eloop *, int,
|
||||
const struct timespec *, void (*)(void *), void *);
|
||||
int eloop_q_timeout_add_sec(struct eloop *, int,
|
||||
unsigned int, void (*)(void *), void *);
|
||||
int eloop_q_timeout_add_msec(struct eloop *, int,
|
||||
unsigned long, void (*)(void *), void *);
|
||||
int eloop_q_timeout_delete(struct eloop *, int, void (*)(void *), void *);
|
||||
|
||||
int eloop_signal_set_cb(struct eloop *, const int *, size_t,
|
||||
void (*)(int, void *), void *);
|
||||
int eloop_signal_mask(struct eloop *, sigset_t *oldset);
|
||||
|
||||
struct eloop * eloop_new(void);
|
||||
void eloop_clear(struct eloop *, ...);
|
||||
void eloop_free(struct eloop *);
|
||||
void eloop_exit(struct eloop *, int);
|
||||
void eloop_enter(struct eloop *);
|
||||
int eloop_forked(struct eloop *);
|
||||
int eloop_open(struct eloop *);
|
||||
int eloop_start(struct eloop *, sigset_t *);
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,306 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef IF_OPTIONS_H
|
||||
#define IF_OPTIONS_H
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "auth.h"
|
||||
#include "route.h"
|
||||
|
||||
/* Don't set any optional arguments here so we retain POSIX
|
||||
* compatibility with getopt */
|
||||
#define IF_OPTS "146bc:de:f:gh:i:j:kl:m:no:pqr:s:t:u:v:wxy:z:" \
|
||||
"ABC:DEF:GHI:JKLMNO:PQ:S:TUVW:X:Z:"
|
||||
#define NOERR_IF_OPTS ":" IF_OPTS
|
||||
|
||||
#define DEFAULT_TIMEOUT 30
|
||||
#define DEFAULT_REBOOT 5
|
||||
#define DEFAULT_REQUEST 180 /* secs to request, mirror DHCP6 */
|
||||
#define DEFAULT_FALLBACK 5 /* secs until fallback */
|
||||
#define DEFAULT_IPV4LL 5 /* secs until ipv4ll */
|
||||
|
||||
#ifndef HOSTNAME_MAX_LEN
|
||||
#define HOSTNAME_MAX_LEN 250 /* 255 - 3 (FQDN) - 2 (DNS enc) */
|
||||
#endif
|
||||
#define VENDORCLASSID_MAX_LEN 255
|
||||
#define CLIENTID_MAX_LEN 48
|
||||
#define USERCLASS_MAX_LEN 255
|
||||
#define VENDOR_MAX_LEN 255
|
||||
#define MUDURL_MAX_LEN 255
|
||||
|
||||
#define DHCPCD_ARP (1ULL << 0)
|
||||
#define DHCPCD_RELEASE (1ULL << 1)
|
||||
#define DHCPCD_RTBUILD (1ULL << 2)
|
||||
#define DHCPCD_GATEWAY (1ULL << 3)
|
||||
#define DHCPCD_STATIC (1ULL << 4)
|
||||
#define DHCPCD_DEBUG (1ULL << 5)
|
||||
#define DHCPCD_ARP_PERSISTDEFENCE (1ULL << 6)
|
||||
#define DHCPCD_LASTLEASE (1ULL << 7)
|
||||
#define DHCPCD_INFORM (1ULL << 8)
|
||||
#define DHCPCD_REQUEST (1ULL << 9)
|
||||
#define DHCPCD_IPV4LL (1ULL << 10)
|
||||
#define DHCPCD_DUID (1ULL << 11)
|
||||
#define DHCPCD_PERSISTENT (1ULL << 12)
|
||||
#define DHCPCD_DAEMONISE (1ULL << 14)
|
||||
#define DHCPCD_DAEMONISED (1ULL << 15)
|
||||
#define DHCPCD_TEST (1ULL << 16)
|
||||
#define DHCPCD_MANAGER (1ULL << 17)
|
||||
#define DHCPCD_HOSTNAME (1ULL << 18)
|
||||
#define DHCPCD_CLIENTID (1ULL << 19)
|
||||
#define DHCPCD_LINK (1ULL << 20)
|
||||
#define DHCPCD_ANONYMOUS (1ULL << 21)
|
||||
#define DHCPCD_BACKGROUND (1ULL << 22)
|
||||
#define DHCPCD_VENDORRAW (1ULL << 23)
|
||||
#define DHCPCD_NOWAITIP (1ULL << 24) /* To force daemonise */
|
||||
#define DHCPCD_WAITIP (1ULL << 25)
|
||||
#define DHCPCD_SLAACPRIVATE (1ULL << 26)
|
||||
#define DHCPCD_CSR_WARNED (1ULL << 27)
|
||||
#define DHCPCD_XID_HWADDR (1ULL << 28)
|
||||
#define DHCPCD_BROADCAST (1ULL << 29)
|
||||
#define DHCPCD_DUMPLEASE (1ULL << 30)
|
||||
#define DHCPCD_IPV6RS (1ULL << 31)
|
||||
#define DHCPCD_IPV6RA_REQRDNSS (1ULL << 32)
|
||||
#define DHCPCD_PRIVSEP (1ULL << 33)
|
||||
#define DHCPCD_CONFIGURE (1ULL << 34)
|
||||
#define DHCPCD_IPV4 (1ULL << 35)
|
||||
#define DHCPCD_FORKED (1ULL << 36)
|
||||
#define DHCPCD_IPV6 (1ULL << 37)
|
||||
#define DHCPCD_STARTED (1ULL << 38)
|
||||
#define DHCPCD_NOALIAS (1ULL << 39)
|
||||
#define DHCPCD_IA_FORCED (1ULL << 40)
|
||||
#define DHCPCD_STOPPING (1ULL << 41)
|
||||
#define DHCPCD_LAUNCHER (1ULL << 42)
|
||||
#define DHCPCD_HOSTNAME_SHORT (1ULL << 43)
|
||||
#define DHCPCD_EXITING (1ULL << 44)
|
||||
#define DHCPCD_WAITIP4 (1ULL << 45)
|
||||
#define DHCPCD_WAITIP6 (1ULL << 46)
|
||||
#define DHCPCD_DEV (1ULL << 47)
|
||||
#define DHCPCD_IAID (1ULL << 48)
|
||||
#define DHCPCD_DHCP (1ULL << 49)
|
||||
#define DHCPCD_DHCP6 (1ULL << 50)
|
||||
#define DHCPCD_IF_UP (1ULL << 51)
|
||||
#define DHCPCD_INFORM6 (1ULL << 52)
|
||||
#define DHCPCD_WANTDHCP (1ULL << 53)
|
||||
#define DHCPCD_IPV6RA_AUTOCONF (1ULL << 54)
|
||||
#define DHCPCD_ROUTER_HOST_ROUTE_WARNED (1ULL << 55)
|
||||
#define DHCPCD_LASTLEASE_EXTEND (1ULL << 56)
|
||||
#define DHCPCD_BOOTP (1ULL << 57)
|
||||
#define DHCPCD_INITIAL_DELAY (1ULL << 58)
|
||||
#define DHCPCD_PRINT_PIDFILE (1ULL << 59)
|
||||
#define DHCPCD_ONESHOT (1ULL << 60)
|
||||
#define DHCPCD_INACTIVE (1ULL << 61)
|
||||
#define DHCPCD_SLAACTEMP (1ULL << 62)
|
||||
#define DHCPCD_PRIVSEPROOT (1ULL << 63)
|
||||
|
||||
#define DHCPCD_NODROP (DHCPCD_EXITING | DHCPCD_PERSISTENT)
|
||||
|
||||
#define DHCPCD_WAITOPTS (DHCPCD_WAITIP | DHCPCD_WAITIP4 | DHCPCD_WAITIP6)
|
||||
|
||||
#define DHCPCD_WARNINGS (DHCPCD_CSR_WARNED | \
|
||||
DHCPCD_ROUTER_HOST_ROUTE_WARNED)
|
||||
|
||||
/* These options only make sense in the config file, so don't use any
|
||||
valid short options for them */
|
||||
#define O_BASE MAX('z', 'Z') + 1
|
||||
#define O_ARPING O_BASE + 1
|
||||
#define O_FALLBACK O_BASE + 2
|
||||
#define O_DESTINATION O_BASE + 3
|
||||
#define O_IPV6RS O_BASE + 4
|
||||
#define O_NOIPV6RS O_BASE + 5
|
||||
#define O_IPV6RA_FORK O_BASE + 6
|
||||
#define O_LINK_RCVBUF O_BASE + 7
|
||||
#define O_ANONYMOUS O_BASE + 8
|
||||
#define O_NOALIAS O_BASE + 9
|
||||
#define O_IA_NA O_BASE + 10
|
||||
#define O_IA_TA O_BASE + 11
|
||||
#define O_IA_PD O_BASE + 12
|
||||
#define O_HOSTNAME_SHORT O_BASE + 13
|
||||
#define O_DEV O_BASE + 14
|
||||
#define O_NODEV O_BASE + 15
|
||||
#define O_NOIPV4 O_BASE + 16
|
||||
#define O_NOIPV6 O_BASE + 17
|
||||
#define O_IAID O_BASE + 18
|
||||
#define O_DEFINE O_BASE + 19
|
||||
#define O_DEFINE6 O_BASE + 20
|
||||
#define O_EMBED O_BASE + 21
|
||||
#define O_ENCAP O_BASE + 22
|
||||
#define O_VENDOPT O_BASE + 23
|
||||
#define O_VENDCLASS O_BASE + 24
|
||||
#define O_AUTHPROTOCOL O_BASE + 25
|
||||
#define O_AUTHTOKEN O_BASE + 26
|
||||
#define O_AUTHNOTREQUIRED O_BASE + 27
|
||||
#define O_NODHCP O_BASE + 28
|
||||
#define O_NODHCP6 O_BASE + 29
|
||||
#define O_DHCP O_BASE + 30
|
||||
#define O_DHCP6 O_BASE + 31
|
||||
#define O_IPV4 O_BASE + 32
|
||||
#define O_IPV6 O_BASE + 33
|
||||
#define O_CONTROLGRP O_BASE + 34
|
||||
#define O_SLAAC O_BASE + 35
|
||||
#define O_GATEWAY O_BASE + 36
|
||||
#define O_NOUP O_BASE + 37
|
||||
#define O_IPV6RA_AUTOCONF O_BASE + 38
|
||||
#define O_IPV6RA_NOAUTOCONF O_BASE + 39
|
||||
#define O_REJECT O_BASE + 40
|
||||
#define O_BOOTP O_BASE + 42
|
||||
#define O_DEFINEND O_BASE + 43
|
||||
#define O_NODELAY O_BASE + 44
|
||||
#define O_INFORM6 O_BASE + 45
|
||||
#define O_LASTLEASE_EXTEND O_BASE + 46
|
||||
#define O_INACTIVE O_BASE + 47
|
||||
#define O_MUDURL O_BASE + 48
|
||||
#define O_MSUSERCLASS O_BASE + 49
|
||||
#define O_CONFIGURE O_BASE + 50
|
||||
#define O_NOCONFIGURE O_BASE + 51
|
||||
#define O_RANDOMISE_HWADDR O_BASE + 52
|
||||
#define O_ARP_PERSISTDEFENCE O_BASE + 53
|
||||
#define O_REQUEST_TIME O_BASE + 54
|
||||
#define O_FALLBACK_TIME O_BASE + 55
|
||||
#define O_IPV4LL_TIME O_BASE + 56
|
||||
|
||||
extern const struct option cf_options[];
|
||||
|
||||
struct if_sla {
|
||||
char ifname[IF_NAMESIZE];
|
||||
uint32_t sla;
|
||||
uint8_t prefix_len;
|
||||
uint64_t suffix;
|
||||
bool sla_set;
|
||||
};
|
||||
|
||||
struct if_ia {
|
||||
uint8_t iaid[4];
|
||||
#ifdef INET6
|
||||
uint16_t ia_type;
|
||||
uint8_t iaid_set;
|
||||
struct in6_addr addr;
|
||||
uint8_t prefix_len;
|
||||
#ifndef SMALL
|
||||
uint32_t sla_max;
|
||||
size_t sla_len;
|
||||
struct if_sla *sla;
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
struct vivco {
|
||||
size_t len;
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
struct if_options {
|
||||
time_t mtime;
|
||||
uint8_t iaid[4];
|
||||
int metric;
|
||||
uint8_t requestmask[256 / NBBY];
|
||||
uint8_t requiremask[256 / NBBY];
|
||||
uint8_t nomask[256 / NBBY];
|
||||
uint8_t rejectmask[256 / NBBY];
|
||||
uint8_t dstmask[256 / NBBY];
|
||||
uint8_t requestmasknd[(UINT16_MAX + 1) / NBBY];
|
||||
uint8_t requiremasknd[(UINT16_MAX + 1) / NBBY];
|
||||
uint8_t nomasknd[(UINT16_MAX + 1) / NBBY];
|
||||
uint8_t rejectmasknd[(UINT16_MAX + 1) / NBBY];
|
||||
uint8_t requestmask6[(UINT16_MAX + 1) / NBBY];
|
||||
uint8_t requiremask6[(UINT16_MAX + 1) / NBBY];
|
||||
uint8_t nomask6[(UINT16_MAX + 1) / NBBY];
|
||||
uint8_t rejectmask6[(UINT16_MAX + 1) / NBBY];
|
||||
uint32_t leasetime;
|
||||
uint32_t timeout;
|
||||
uint32_t reboot;
|
||||
uint32_t request_time;
|
||||
uint32_t fallback_time;
|
||||
uint32_t ipv4ll_time;
|
||||
unsigned long long options;
|
||||
bool randomise_hwaddr;
|
||||
|
||||
struct in_addr req_addr;
|
||||
struct in_addr req_mask;
|
||||
struct in_addr req_brd;
|
||||
rb_tree_t routes;
|
||||
struct in6_addr req_addr6;
|
||||
uint8_t req_prefix_len;
|
||||
unsigned int mtu;
|
||||
char **config;
|
||||
|
||||
char **environ;
|
||||
|
||||
char hostname[HOSTNAME_MAX_LEN + 1]; /* We don't store the length */
|
||||
uint8_t fqdn;
|
||||
uint8_t vendorclassid[VENDORCLASSID_MAX_LEN + 2];
|
||||
uint8_t clientid[CLIENTID_MAX_LEN + 2];
|
||||
uint8_t userclass[USERCLASS_MAX_LEN + 2];
|
||||
uint8_t vendor[VENDOR_MAX_LEN + 2];
|
||||
uint8_t mudurl[MUDURL_MAX_LEN + 2];
|
||||
|
||||
size_t blacklist_len;
|
||||
in_addr_t *blacklist;
|
||||
size_t whitelist_len;
|
||||
in_addr_t *whitelist;
|
||||
ssize_t arping_len;
|
||||
in_addr_t *arping;
|
||||
char *fallback;
|
||||
|
||||
struct if_ia *ia;
|
||||
size_t ia_len;
|
||||
#ifdef INET6
|
||||
struct in6_addr token;
|
||||
#endif
|
||||
|
||||
struct dhcp_opt *dhcp_override;
|
||||
size_t dhcp_override_len;
|
||||
struct dhcp_opt *nd_override;
|
||||
size_t nd_override_len;
|
||||
struct dhcp_opt *dhcp6_override;
|
||||
size_t dhcp6_override_len;
|
||||
uint32_t vivco_en;
|
||||
struct vivco *vivco;
|
||||
size_t vivco_len;
|
||||
struct dhcp_opt *vivso_override;
|
||||
size_t vivso_override_len;
|
||||
|
||||
struct auth auth;
|
||||
};
|
||||
|
||||
struct if_options *read_config(struct dhcpcd_ctx *,
|
||||
const char *, const char *, const char *);
|
||||
int add_options(struct dhcpcd_ctx *, const char *,
|
||||
struct if_options *, int, char **);
|
||||
void free_dhcp_opt_embenc(struct dhcp_opt *);
|
||||
void free_options(struct dhcpcd_ctx *, struct if_options *);
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,294 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef INTERFACE_H
|
||||
#define INTERFACE_H
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/route.h> /* for RTM_ADD et all */
|
||||
#include <netinet/in.h>
|
||||
#ifdef BSD
|
||||
#include <netinet/in_var.h> /* for IN_IFF_TENTATIVE et all */
|
||||
#endif
|
||||
|
||||
#include <ifaddrs.h>
|
||||
|
||||
/* If the interface does not support carrier status (ie PPP),
|
||||
* dhcpcd can poll it for the relevant flags periodically */
|
||||
#define IF_POLL_UP 100 /* milliseconds */
|
||||
|
||||
/*
|
||||
* Systems which handle 1 address per alias.
|
||||
* Currenly this is just Solaris.
|
||||
* While Linux can do aliased addresses, it is only useful for their
|
||||
* legacy ifconfig(8) tool which cannot display >1 IPv4 address
|
||||
* (it can display many IPv6 addresses which makes the limitation odd).
|
||||
* Linux has ip(8) which is a more feature rich tool, without the above
|
||||
* restriction.
|
||||
*/
|
||||
#ifndef ALIAS_ADDR
|
||||
# ifdef __sun
|
||||
# define ALIAS_ADDR
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
|
||||
/* POSIX defines ioctl request as an int, which Solaris and musl use.
|
||||
* Everyone else use an unsigned long, which happens to be the bigger one
|
||||
* so we use that in our on wire API. */
|
||||
#ifdef IOCTL_REQUEST_TYPE
|
||||
typedef IOCTL_REQUEST_TYPE ioctl_request_t;
|
||||
#else
|
||||
typedef unsigned long ioctl_request_t;
|
||||
#endif
|
||||
|
||||
#include "dhcpcd.h"
|
||||
#include "ipv4.h"
|
||||
#include "ipv6.h"
|
||||
#include "route.h"
|
||||
|
||||
#define EUI64_ADDR_LEN 8
|
||||
#define INFINIBAND_ADDR_LEN 20
|
||||
|
||||
/* Linux 2.4 doesn't define this */
|
||||
#ifndef ARPHRD_IEEE1394
|
||||
# define ARPHRD_IEEE1394 24
|
||||
#endif
|
||||
|
||||
/* The BSD's don't define this yet */
|
||||
#ifndef ARPHRD_INFINIBAND
|
||||
# define ARPHRD_INFINIBAND 32
|
||||
#endif
|
||||
|
||||
/* Maximum frame length.
|
||||
* Support jumbo frames and some extra. */
|
||||
#define FRAMEHDRLEN_MAX 14 /* only ethernet support */
|
||||
#define FRAMELEN_MAX (FRAMEHDRLEN_MAX + 9216)
|
||||
|
||||
#define UDPLEN_MAX 64 * 1024
|
||||
|
||||
/* Work out if we have a private address or not
|
||||
* 10/8
|
||||
* 172.16/12
|
||||
* 192.168/16
|
||||
*/
|
||||
#ifndef IN_PRIVATE
|
||||
# define IN_PRIVATE(addr) (((addr & IN_CLASSA_NET) == 0x0a000000) || \
|
||||
((addr & 0xfff00000) == 0xac100000) || \
|
||||
((addr & IN_CLASSB_NET) == 0xc0a80000))
|
||||
#endif
|
||||
|
||||
#ifndef CLLADDR
|
||||
#ifdef AF_LINK
|
||||
# define CLLADDR(sdl) (const void *)((sdl)->sdl_data + (sdl)->sdl_nlen)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __sun
|
||||
/* Solaris stupidly defines this for compat with BSD
|
||||
* but then ignores it. */
|
||||
#undef RTF_CLONING
|
||||
|
||||
/* This interface is busted on DilOS at least.
|
||||
* It used to work, but lukily Solaris can fall back to
|
||||
* IP_PKTINFO. */
|
||||
#undef IP_RECVIF
|
||||
#endif
|
||||
|
||||
/* Private structures specific to an OS */
|
||||
#ifdef BSD
|
||||
struct priv {
|
||||
#ifdef INET6
|
||||
int pf_inet6_fd;
|
||||
#endif
|
||||
#if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */
|
||||
int pf_link_fd;
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
struct priv {
|
||||
int route_fd;
|
||||
int generic_fd;
|
||||
uint32_t route_pid;
|
||||
};
|
||||
#endif
|
||||
#ifdef __sun
|
||||
struct priv {
|
||||
#ifdef INET6
|
||||
int pf_inet6_fd;
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef __sun
|
||||
/* Solaris getifaddrs is very un-suitable for dhcpcd.
|
||||
* See if-sun.c for details why. */
|
||||
struct ifaddrs;
|
||||
int if_getifaddrs(struct ifaddrs **);
|
||||
#define getifaddrs if_getifaddrs
|
||||
int if_getsubnet(struct dhcpcd_ctx *, const char *, int, void *, size_t);
|
||||
#endif
|
||||
|
||||
int if_ioctl(struct dhcpcd_ctx *, ioctl_request_t, void *, size_t);
|
||||
#ifdef HAVE_PLEDGE
|
||||
#define pioctl(ctx, req, data, len) if_ioctl((ctx), (req), (data), (len))
|
||||
#else
|
||||
#define pioctl(ctx, req, data, len) ioctl((ctx)->pf_inet_fd, (req),(data),(len))
|
||||
#endif
|
||||
int if_getflags(struct interface *);
|
||||
int if_setflag(struct interface *, short, short);
|
||||
#define if_up(ifp) if_setflag((ifp), (IFF_UP | IFF_RUNNING), 0)
|
||||
#define if_down(ifp) if_setflag((ifp), 0, IFF_UP);
|
||||
bool if_is_link_up(const struct interface *);
|
||||
bool if_valid_hwaddr(const uint8_t *, size_t);
|
||||
struct if_head *if_discover(struct dhcpcd_ctx *, struct ifaddrs **,
|
||||
int, char * const *);
|
||||
void if_freeifaddrs(struct dhcpcd_ctx *ctx, struct ifaddrs **);
|
||||
void if_markaddrsstale(struct if_head *);
|
||||
void if_learnaddrs(struct dhcpcd_ctx *, struct if_head *, struct ifaddrs **);
|
||||
void if_deletestaleaddrs(struct if_head *);
|
||||
struct interface *if_find(struct if_head *, const char *);
|
||||
struct interface *if_findindex(struct if_head *, unsigned int);
|
||||
struct interface *if_loopback(struct dhcpcd_ctx *);
|
||||
void if_free(struct interface *);
|
||||
int if_domtu(const struct interface *, short int);
|
||||
#define if_getmtu(ifp) if_domtu((ifp), 0)
|
||||
#define if_setmtu(ifp, mtu) if_domtu((ifp), (mtu))
|
||||
int if_carrier(struct interface *, const void *);
|
||||
bool if_roaming(struct interface *);
|
||||
|
||||
#ifdef ALIAS_ADDR
|
||||
int if_makealias(char *, size_t, const char *, int);
|
||||
#endif
|
||||
|
||||
int if_mtu_os(const struct interface *);
|
||||
|
||||
/*
|
||||
* Helper to decode an interface name of bge0:1 to
|
||||
* devname = bge0, drvname = bge0, ppa = 0, lun = 1.
|
||||
* If ppa or lun are invalid they are set to -1.
|
||||
*/
|
||||
struct if_spec {
|
||||
char ifname[IF_NAMESIZE];
|
||||
char devname[IF_NAMESIZE];
|
||||
char drvname[IF_NAMESIZE];
|
||||
int ppa;
|
||||
int vlid;
|
||||
int lun;
|
||||
};
|
||||
int if_nametospec(const char *, struct if_spec *);
|
||||
|
||||
/* The below functions are provided by if-KERNEL.c */
|
||||
int os_init(void);
|
||||
int if_conf(struct interface *);
|
||||
int if_init(struct interface *);
|
||||
int if_getssid(struct interface *);
|
||||
int if_ignoregroup(int, const char *);
|
||||
bool if_ignore(struct dhcpcd_ctx *, const char *);
|
||||
int if_vimaster(struct dhcpcd_ctx *ctx, const char *);
|
||||
unsigned short if_vlanid(const struct interface *);
|
||||
char * if_getnetworknamespace(char *, size_t);
|
||||
int if_opensockets(struct dhcpcd_ctx *);
|
||||
int if_opensockets_os(struct dhcpcd_ctx *);
|
||||
void if_closesockets(struct dhcpcd_ctx *);
|
||||
void if_closesockets_os(struct dhcpcd_ctx *);
|
||||
int if_handlelink(struct dhcpcd_ctx *);
|
||||
int if_randomisemac(struct interface *);
|
||||
int if_setmac(struct interface *ifp, void *, uint8_t);
|
||||
|
||||
/* dhcpcd uses the same routing flags as BSD.
|
||||
* If the platform doesn't use these flags,
|
||||
* map them in the platform interace file. */
|
||||
#ifndef RTM_ADD
|
||||
#define RTM_ADD 0x1 /* Add Route */
|
||||
#define RTM_DELETE 0x2 /* Delete Route */
|
||||
#define RTM_CHANGE 0x3 /* Change Metrics or flags */
|
||||
#define RTM_GET 0x4 /* Report Metrics */
|
||||
#endif
|
||||
|
||||
/* Define SOCK_CLOEXEC and SOCK_NONBLOCK for systems that lack it.
|
||||
* xsocket() in if.c will map them to fctnl FD_CLOEXEC and O_NONBLOCK. */
|
||||
#ifdef SOCK_CLOEXEC
|
||||
# define HAVE_SOCK_CLOEXEC
|
||||
#else
|
||||
# define SOCK_CLOEXEC 0x10000000
|
||||
#endif
|
||||
#ifdef SOCK_NONBLOCK
|
||||
# define HAVE_SOCK_NONBLOCK
|
||||
#else
|
||||
# define SOCK_NONBLOCK 0x20000000
|
||||
#endif
|
||||
#ifndef SOCK_CXNB
|
||||
#define SOCK_CXNB SOCK_CLOEXEC | SOCK_NONBLOCK
|
||||
#endif
|
||||
int xsocket(int, int, int);
|
||||
int xsocketpair(int, int, int, int[2]);
|
||||
|
||||
int if_route(unsigned char, const struct rt *rt);
|
||||
int if_initrt(struct dhcpcd_ctx *, rb_tree_t *, int);
|
||||
|
||||
int if_missfilter(struct interface *, struct sockaddr *);
|
||||
int if_missfilter_apply(struct dhcpcd_ctx *);
|
||||
|
||||
#ifdef INET
|
||||
int if_address(unsigned char, const struct ipv4_addr *);
|
||||
int if_addrflags(const struct interface *, const struct in_addr *,
|
||||
const char *);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef INET6
|
||||
void if_disable_rtadv(void);
|
||||
void if_setup_inet6(const struct interface *);
|
||||
int ip6_forwarding(const char *ifname);
|
||||
|
||||
struct ra;
|
||||
struct ipv6_addr;
|
||||
|
||||
int if_applyra(const struct ra *);
|
||||
int if_address6(unsigned char, const struct ipv6_addr *);
|
||||
int if_addrflags6(const struct interface *, const struct in6_addr *,
|
||||
const char *);
|
||||
int if_getlifetime6(struct ipv6_addr *);
|
||||
|
||||
#else
|
||||
#define if_checkipv6(a, b, c) (-1)
|
||||
#endif
|
||||
|
||||
int if_machinearch(char *, size_t);
|
||||
struct interface *if_findifpfromcmsg(struct dhcpcd_ctx *,
|
||||
struct msghdr *, int *);
|
||||
|
||||
#ifdef __linux__
|
||||
int if_linksocket(struct sockaddr_nl *, int, int);
|
||||
int if_getnetlink(struct dhcpcd_ctx *, struct iovec *, int, int,
|
||||
int (*)(struct dhcpcd_ctx *, void *, struct nlmsghdr *), void *);
|
||||
#endif
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,157 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef IPV4_H
|
||||
#define IPV4_H
|
||||
|
||||
#include "dhcpcd.h"
|
||||
|
||||
/* Prefer our macro */
|
||||
#ifdef HTONL
|
||||
#undef HTONL
|
||||
#endif
|
||||
|
||||
#ifndef BYTE_ORDER
|
||||
#define BIG_ENDIAN 1234
|
||||
#define LITTLE_ENDIAN 4321
|
||||
#if defined(_BIG_ENDIAN)
|
||||
#define BYTE_ORDER BIG_ENDIAN
|
||||
#elif defined(_LITTLE_ENDIAN)
|
||||
#define BYTE_ORDER LITTLE_ENDIAN
|
||||
#else
|
||||
#error Endian unknown
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
#define HTONL(A) (A)
|
||||
#elif BYTE_ORDER == LITTLE_ENDIAN
|
||||
#define HTONL(A) \
|
||||
((((uint32_t)(A) & 0xff000000) >> 24) | \
|
||||
(((uint32_t)(A) & 0x00ff0000) >> 8) | \
|
||||
(((uint32_t)(A) & 0x0000ff00) << 8) | \
|
||||
(((uint32_t)(A) & 0x000000ff) << 24))
|
||||
#endif /* BYTE_ORDER */
|
||||
|
||||
#ifdef __sun
|
||||
/* Solaris lacks these defines.
|
||||
* While it supports DaD, to seems to only expose IFF_DUPLICATE
|
||||
* so we have no way of knowing if it's tentative or not.
|
||||
* I don't even know if Solaris has any special treatment for tentative. */
|
||||
# define IN_IFF_TENTATIVE 0x01
|
||||
# define IN_IFF_DUPLICATED 0x02
|
||||
# define IN_IFF_DETACHED 0x00
|
||||
#endif
|
||||
|
||||
#ifdef IN_IFF_TENTATIVE
|
||||
#define IN_IFF_NOTUSEABLE \
|
||||
(IN_IFF_TENTATIVE | IN_IFF_DUPLICATED | IN_IFF_DETACHED)
|
||||
#endif
|
||||
|
||||
#define IN_ARE_ADDR_EQUAL(a, b) ((a)->s_addr == (b)->s_addr)
|
||||
#define IN_IS_ADDR_UNSPECIFIED(a) ((a)->s_addr == INADDR_ANY)
|
||||
|
||||
#ifdef __linux__
|
||||
#define IP_LIFETIME
|
||||
#endif
|
||||
|
||||
struct ipv4_addr {
|
||||
TAILQ_ENTRY(ipv4_addr) next;
|
||||
struct in_addr addr;
|
||||
struct in_addr mask;
|
||||
struct in_addr brd;
|
||||
struct interface *iface;
|
||||
int addr_flags;
|
||||
unsigned int flags;
|
||||
#ifdef IP_LIFETIME
|
||||
uint32_t vltime;
|
||||
uint32_t pltime;
|
||||
#endif
|
||||
char saddr[INET_ADDRSTRLEN + 3];
|
||||
#ifdef ALIAS_ADDR
|
||||
char alias[IF_NAMESIZE];
|
||||
#endif
|
||||
};
|
||||
TAILQ_HEAD(ipv4_addrhead, ipv4_addr);
|
||||
|
||||
#define IPV4_AF_STALE (1U << 0)
|
||||
#define IPV4_AF_NEW (1U << 1)
|
||||
|
||||
#define IPV4_ADDR_EQ(a1, a2) ((a1) && (a1)->addr.s_addr == (a2)->addr.s_addr)
|
||||
#define IPV4_MASK1_EQ(a1, a2) ((a1) && (a1)->mask.s_addr == (a2)->mask.s_addr)
|
||||
#define IPV4_MASK_EQ(a1, a2) (IPV4_ADDR_EQ(a1, a2) && IPV4_MASK1_EQ(a1, a2))
|
||||
#define IPV4_BRD1_EQ(a1, a2) ((a1) && (a1)->brd.s_addr == (a2)->brd.s_addr)
|
||||
#define IPV4_BRD_EQ(a1, a2) (IPV4_MASK_EQ(a1, a2) && IPV4_BRD1_EQ(a1, a2))
|
||||
|
||||
struct ipv4_state {
|
||||
struct ipv4_addrhead addrs;
|
||||
};
|
||||
|
||||
#define IPV4_STATE(ifp) \
|
||||
((struct ipv4_state *)(ifp)->if_data[IF_DATA_IPV4])
|
||||
#define IPV4_CSTATE(ifp) \
|
||||
((const struct ipv4_state *)(ifp)->if_data[IF_DATA_IPV4])
|
||||
|
||||
#ifdef INET
|
||||
struct ipv4_state *ipv4_getstate(struct interface *);
|
||||
int ipv4_ifcmp(const struct interface *, const struct interface *);
|
||||
uint8_t inet_ntocidr(struct in_addr);
|
||||
int inet_cidrtoaddr(int, struct in_addr *);
|
||||
uint32_t ipv4_getnetmask(uint32_t);
|
||||
int ipv4_hasaddr(const struct interface *);
|
||||
|
||||
bool inet_getroutes(struct dhcpcd_ctx *, rb_tree_t *);
|
||||
|
||||
#define STATE_ADDED 0x01
|
||||
#define STATE_FAKE 0x02
|
||||
#define STATE_EXPIRED 0x04
|
||||
|
||||
int ipv4_deladdr(struct ipv4_addr *, int);
|
||||
struct ipv4_addr *ipv4_addaddr(struct interface *,
|
||||
const struct in_addr *, const struct in_addr *, const struct in_addr *,
|
||||
uint32_t, uint32_t);
|
||||
struct ipv4_addr *ipv4_applyaddr(void *);
|
||||
|
||||
struct ipv4_addr *ipv4_iffindaddr(struct interface *,
|
||||
const struct in_addr *, const struct in_addr *);
|
||||
struct ipv4_addr *ipv4_iffindlladdr(struct interface *);
|
||||
struct ipv4_addr *ipv4_findaddr(struct dhcpcd_ctx *, const struct in_addr *);
|
||||
struct ipv4_addr *ipv4_findmaskaddr(struct dhcpcd_ctx *,
|
||||
const struct in_addr *);
|
||||
struct ipv4_addr *ipv4_findmaskbrd(struct dhcpcd_ctx *,
|
||||
const struct in_addr *);
|
||||
void ipv4_markaddrsstale(struct interface *);
|
||||
void ipv4_deletestaleaddrs(struct interface *);
|
||||
void ipv4_handleifa(struct dhcpcd_ctx *, int, struct if_head *, const char *,
|
||||
const struct in_addr *, const struct in_addr *, const struct in_addr *,
|
||||
int, pid_t);
|
||||
|
||||
void ipv4_free(struct interface *);
|
||||
#endif /* INET */
|
||||
|
||||
#endif /* IPV4_H */
|
|
@ -0,0 +1,561 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define ELOOP_QUEUE IPV4LL
|
||||
#include "config.h"
|
||||
#include "arp.h"
|
||||
#include "common.h"
|
||||
#include "dhcp.h"
|
||||
#include "eloop.h"
|
||||
#include "if.h"
|
||||
#include "if-options.h"
|
||||
#include "ipv4.h"
|
||||
#include "ipv4ll.h"
|
||||
#include "logerr.h"
|
||||
#include "sa.h"
|
||||
#include "script.h"
|
||||
|
||||
static const struct in_addr inaddr_llmask = {
|
||||
.s_addr = HTONL(LINKLOCAL_MASK)
|
||||
};
|
||||
static const struct in_addr inaddr_llbcast = {
|
||||
.s_addr = HTONL(LINKLOCAL_BCAST)
|
||||
};
|
||||
|
||||
static void
|
||||
ipv4ll_pickaddr(struct interface *ifp)
|
||||
{
|
||||
struct in_addr addr = { .s_addr = 0 };
|
||||
struct ipv4ll_state *state;
|
||||
|
||||
state = IPV4LL_STATE(ifp);
|
||||
setstate(state->randomstate);
|
||||
|
||||
do {
|
||||
long r;
|
||||
|
||||
again:
|
||||
/* RFC 3927 Section 2.1 states that the first 256 and
|
||||
* last 256 addresses are reserved for future use.
|
||||
* See ipv4ll_start for why we don't use arc4random. */
|
||||
/* coverity[dont_call] */
|
||||
r = random();
|
||||
addr.s_addr = ntohl(LINKLOCAL_ADDR |
|
||||
((uint32_t)(r % 0xFD00) + 0x0100));
|
||||
|
||||
/* No point using a failed address */
|
||||
if (IN_ARE_ADDR_EQUAL(&addr, &state->pickedaddr))
|
||||
goto again;
|
||||
/* Ensure we don't have the address on another interface */
|
||||
} while (ipv4_findaddr(ifp->ctx, &addr) != NULL);
|
||||
|
||||
/* Restore the original random state */
|
||||
setstate(ifp->ctx->randomstate);
|
||||
state->pickedaddr = addr;
|
||||
}
|
||||
|
||||
int
|
||||
ipv4ll_subnetroute(rb_tree_t *routes, struct interface *ifp)
|
||||
{
|
||||
struct ipv4ll_state *state;
|
||||
struct rt *rt;
|
||||
struct in_addr in;
|
||||
|
||||
assert(ifp != NULL);
|
||||
if ((state = IPV4LL_STATE(ifp)) == NULL ||
|
||||
state->addr == NULL)
|
||||
return 0;
|
||||
|
||||
if ((rt = rt_new(ifp)) == NULL)
|
||||
return -1;
|
||||
|
||||
in.s_addr = state->addr->addr.s_addr & state->addr->mask.s_addr;
|
||||
sa_in_init(&rt->rt_dest, &in);
|
||||
in.s_addr = state->addr->mask.s_addr;
|
||||
sa_in_init(&rt->rt_netmask, &in);
|
||||
in.s_addr = INADDR_ANY;
|
||||
sa_in_init(&rt->rt_gateway, &in);
|
||||
sa_in_init(&rt->rt_ifa, &state->addr->addr);
|
||||
rt->rt_dflags |= RTDF_IPV4LL;
|
||||
return rt_proto_add(routes, rt) ? 1 : 0;
|
||||
}
|
||||
|
||||
int
|
||||
ipv4ll_defaultroute(rb_tree_t *routes, struct interface *ifp)
|
||||
{
|
||||
struct ipv4ll_state *state;
|
||||
struct rt *rt;
|
||||
struct in_addr in;
|
||||
|
||||
assert(ifp != NULL);
|
||||
if ((state = IPV4LL_STATE(ifp)) == NULL ||
|
||||
state->addr == NULL)
|
||||
return 0;
|
||||
|
||||
if ((rt = rt_new(ifp)) == NULL)
|
||||
return -1;
|
||||
|
||||
in.s_addr = INADDR_ANY;
|
||||
sa_in_init(&rt->rt_dest, &in);
|
||||
sa_in_init(&rt->rt_netmask, &in);
|
||||
sa_in_init(&rt->rt_gateway, &in);
|
||||
sa_in_init(&rt->rt_ifa, &state->addr->addr);
|
||||
rt->rt_dflags |= RTDF_IPV4LL;
|
||||
#ifdef HAVE_ROUTE_METRIC
|
||||
rt->rt_metric += RTMETRIC_IPV4LL;
|
||||
#endif
|
||||
return rt_proto_add(routes, rt) ? 1 : 0;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ipv4ll_env(FILE *fp, const char *prefix, const struct interface *ifp)
|
||||
{
|
||||
const struct ipv4ll_state *state;
|
||||
const char *pf = prefix == NULL ? "" : "_";
|
||||
struct in_addr netnum;
|
||||
|
||||
assert(ifp != NULL);
|
||||
if ((state = IPV4LL_CSTATE(ifp)) == NULL || state->addr == NULL)
|
||||
return 0;
|
||||
|
||||
/* Emulate a DHCP environment */
|
||||
if (efprintf(fp, "%s%sip_address=%s",
|
||||
prefix, pf, inet_ntoa(state->addr->addr)) == -1)
|
||||
return -1;
|
||||
if (efprintf(fp, "%s%ssubnet_mask=%s",
|
||||
prefix, pf, inet_ntoa(state->addr->mask)) == -1)
|
||||
return -1;
|
||||
if (efprintf(fp, "%s%ssubnet_cidr=%d",
|
||||
prefix, pf, inet_ntocidr(state->addr->mask)) == -1)
|
||||
return -1;
|
||||
if (efprintf(fp, "%s%sbroadcast_address=%s",
|
||||
prefix, pf, inet_ntoa(state->addr->brd)) == -1)
|
||||
return -1;
|
||||
netnum.s_addr = state->addr->addr.s_addr & state->addr->mask.s_addr;
|
||||
if (efprintf(fp, "%s%snetwork_number=%s",
|
||||
prefix, pf, inet_ntoa(netnum)) == -1)
|
||||
return -1;
|
||||
return 5;
|
||||
}
|
||||
|
||||
static void
|
||||
ipv4ll_announced_arp(struct arp_state *astate)
|
||||
{
|
||||
struct ipv4ll_state *state = IPV4LL_STATE(astate->iface);
|
||||
|
||||
state->conflicts = 0;
|
||||
#ifdef KERNEL_RFC5227
|
||||
arp_free(astate);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef KERNEL_RFC5227
|
||||
/* This is the callback by ARP freeing */
|
||||
static void
|
||||
ipv4ll_free_arp(struct arp_state *astate)
|
||||
{
|
||||
struct ipv4ll_state *state;
|
||||
|
||||
state = IPV4LL_STATE(astate->iface);
|
||||
if (state != NULL && state->arp == astate)
|
||||
state->arp = NULL;
|
||||
}
|
||||
|
||||
/* This is us freeing any ARP state */
|
||||
static void
|
||||
ipv4ll_freearp(struct interface *ifp)
|
||||
{
|
||||
struct ipv4ll_state *state;
|
||||
|
||||
state = IPV4LL_STATE(ifp);
|
||||
if (state == NULL || state->arp == NULL)
|
||||
return;
|
||||
|
||||
eloop_timeout_delete(ifp->ctx->eloop, NULL, state->arp);
|
||||
arp_free(state->arp);
|
||||
state->arp = NULL;
|
||||
}
|
||||
#else
|
||||
#define ipv4ll_freearp(ifp)
|
||||
#endif
|
||||
|
||||
static void
|
||||
ipv4ll_not_found(struct interface *ifp)
|
||||
{
|
||||
struct ipv4ll_state *state;
|
||||
struct ipv4_addr *ia;
|
||||
struct arp_state *astate;
|
||||
|
||||
state = IPV4LL_STATE(ifp);
|
||||
ia = ipv4_iffindaddr(ifp, &state->pickedaddr, &inaddr_llmask);
|
||||
#ifdef IN_IFF_NOTREADY
|
||||
if (ia == NULL || ia->addr_flags & IN_IFF_NOTREADY)
|
||||
#endif
|
||||
loginfox("%s: using IPv4LL address %s",
|
||||
ifp->name, inet_ntoa(state->pickedaddr));
|
||||
if (!(ifp->options->options & DHCPCD_CONFIGURE))
|
||||
goto run;
|
||||
if (ia == NULL) {
|
||||
if (ifp->ctx->options & DHCPCD_TEST)
|
||||
goto test;
|
||||
ia = ipv4_addaddr(ifp, &state->pickedaddr,
|
||||
&inaddr_llmask, &inaddr_llbcast,
|
||||
DHCP_INFINITE_LIFETIME, DHCP_INFINITE_LIFETIME);
|
||||
}
|
||||
if (ia == NULL)
|
||||
return;
|
||||
#ifdef IN_IFF_NOTREADY
|
||||
if (ia->addr_flags & IN_IFF_NOTREADY)
|
||||
return;
|
||||
logdebugx("%s: DAD completed for %s", ifp->name, ia->saddr);
|
||||
#endif
|
||||
|
||||
test:
|
||||
state->addr = ia;
|
||||
state->down = false;
|
||||
if (ifp->ctx->options & DHCPCD_TEST) {
|
||||
script_runreason(ifp, "TEST");
|
||||
eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS);
|
||||
return;
|
||||
}
|
||||
rt_build(ifp->ctx, AF_INET);
|
||||
run:
|
||||
astate = arp_announceaddr(ifp->ctx, &ia->addr);
|
||||
if (astate != NULL)
|
||||
astate->announced_cb = ipv4ll_announced_arp;
|
||||
script_runreason(ifp, "IPV4LL");
|
||||
dhcpcd_daemonise(ifp->ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
ipv4ll_found(struct interface *ifp)
|
||||
{
|
||||
struct ipv4ll_state *state = IPV4LL_STATE(ifp);
|
||||
|
||||
ipv4ll_freearp(ifp);
|
||||
if (++state->conflicts == MAX_CONFLICTS)
|
||||
logerrx("%s: failed to acquire an IPv4LL address",
|
||||
ifp->name);
|
||||
ipv4ll_pickaddr(ifp);
|
||||
eloop_timeout_add_sec(ifp->ctx->eloop,
|
||||
state->conflicts >= MAX_CONFLICTS ?
|
||||
RATE_LIMIT_INTERVAL : PROBE_WAIT,
|
||||
ipv4ll_start, ifp);
|
||||
}
|
||||
|
||||
static void
|
||||
ipv4ll_defend_failed(struct interface *ifp)
|
||||
{
|
||||
struct ipv4ll_state *state = IPV4LL_STATE(ifp);
|
||||
|
||||
ipv4ll_freearp(ifp);
|
||||
if (ifp->options->options & DHCPCD_CONFIGURE)
|
||||
ipv4_deladdr(state->addr, 1);
|
||||
state->addr = NULL;
|
||||
rt_build(ifp->ctx, AF_INET);
|
||||
script_runreason(ifp, "IPV4LL");
|
||||
ipv4ll_pickaddr(ifp);
|
||||
ipv4ll_start(ifp);
|
||||
}
|
||||
|
||||
#ifndef KERNEL_RFC5227
|
||||
static void
|
||||
ipv4ll_not_found_arp(struct arp_state *astate)
|
||||
{
|
||||
|
||||
ipv4ll_not_found(astate->iface);
|
||||
}
|
||||
|
||||
static void
|
||||
ipv4ll_found_arp(struct arp_state *astate, __unused const struct arp_msg *amsg)
|
||||
{
|
||||
|
||||
ipv4ll_found(astate->iface);
|
||||
}
|
||||
|
||||
static void
|
||||
ipv4ll_defend_failed_arp(struct arp_state *astate)
|
||||
{
|
||||
|
||||
ipv4ll_defend_failed(astate->iface);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
ipv4ll_start(void *arg)
|
||||
{
|
||||
struct interface *ifp = arg;
|
||||
struct ipv4ll_state *state;
|
||||
struct ipv4_addr *ia;
|
||||
bool repick;
|
||||
#ifndef KERNEL_RFC5227
|
||||
struct arp_state *astate;
|
||||
#endif
|
||||
|
||||
if ((state = IPV4LL_STATE(ifp)) == NULL) {
|
||||
ifp->if_data[IF_DATA_IPV4LL] = calloc(1, sizeof(*state));
|
||||
if ((state = IPV4LL_STATE(ifp)) == NULL) {
|
||||
logerr(__func__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (state->running)
|
||||
return;
|
||||
state->running = true;
|
||||
|
||||
/* RFC 3927 Section 2.1 states that the random number generator
|
||||
* SHOULD be seeded with a value derived from persistent information
|
||||
* such as the IEEE 802 MAC address so that it usually picks
|
||||
* the same address without persistent storage. */
|
||||
if (!state->seeded) {
|
||||
unsigned int seed;
|
||||
char *orig;
|
||||
|
||||
if (sizeof(seed) > ifp->hwlen) {
|
||||
seed = 0;
|
||||
memcpy(&seed, ifp->hwaddr, ifp->hwlen);
|
||||
} else
|
||||
memcpy(&seed, ifp->hwaddr + ifp->hwlen - sizeof(seed),
|
||||
sizeof(seed));
|
||||
/* coverity[dont_call] */
|
||||
orig = initstate(seed,
|
||||
state->randomstate, sizeof(state->randomstate));
|
||||
|
||||
/* Save the original state. */
|
||||
if (ifp->ctx->randomstate == NULL)
|
||||
ifp->ctx->randomstate = orig;
|
||||
|
||||
/* Set back the original state until we need the seeded one. */
|
||||
setstate(ifp->ctx->randomstate);
|
||||
state->seeded = true;
|
||||
}
|
||||
|
||||
/* Find the previosuly used address. */
|
||||
if (state->pickedaddr.s_addr != INADDR_ANY)
|
||||
ia = ipv4_iffindaddr(ifp, &state->pickedaddr, NULL);
|
||||
else
|
||||
ia = NULL;
|
||||
|
||||
/* Find an existing IPv4LL address and ensure we can work with it. */
|
||||
if (ia == NULL)
|
||||
ia = ipv4_iffindlladdr(ifp);
|
||||
|
||||
repick = false;
|
||||
#ifdef IN_IFF_DUPLICATED
|
||||
if (ia != NULL && ia->addr_flags & IN_IFF_DUPLICATED) {
|
||||
state->pickedaddr = ia->addr; /* So it's not picked again. */
|
||||
repick = true;
|
||||
if (ifp->options->options & DHCPCD_CONFIGURE)
|
||||
ipv4_deladdr(ia, 0);
|
||||
ia = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
state->addr = ia;
|
||||
state->down = true;
|
||||
if (ia != NULL) {
|
||||
state->pickedaddr = ia->addr;
|
||||
#ifdef IN_IFF_TENTATIVE
|
||||
if (ia->addr_flags & (IN_IFF_TENTATIVE | IN_IFF_DETACHED)) {
|
||||
loginfox("%s: waiting for DAD to complete on %s",
|
||||
ifp->name, inet_ntoa(ia->addr));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef IN_IFF_DUPLICATED
|
||||
loginfox("%s: using IPv4LL address %s", ifp->name, ia->saddr);
|
||||
#endif
|
||||
} else {
|
||||
loginfox("%s: probing for an IPv4LL address", ifp->name);
|
||||
if (repick || state->pickedaddr.s_addr == INADDR_ANY)
|
||||
ipv4ll_pickaddr(ifp);
|
||||
}
|
||||
|
||||
#ifdef KERNEL_RFC5227
|
||||
ipv4ll_not_found(ifp);
|
||||
#else
|
||||
ipv4ll_freearp(ifp);
|
||||
state->arp = astate = arp_new(ifp, &state->pickedaddr);
|
||||
if (state->arp == NULL)
|
||||
return;
|
||||
|
||||
astate->found_cb = ipv4ll_found_arp;
|
||||
astate->not_found_cb = ipv4ll_not_found_arp;
|
||||
astate->announced_cb = ipv4ll_announced_arp;
|
||||
astate->defend_failed_cb = ipv4ll_defend_failed_arp;
|
||||
astate->free_cb = ipv4ll_free_arp;
|
||||
arp_probe(astate);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
ipv4ll_drop(struct interface *ifp)
|
||||
{
|
||||
struct ipv4ll_state *state;
|
||||
bool dropped = false;
|
||||
struct ipv4_state *istate;
|
||||
|
||||
assert(ifp != NULL);
|
||||
|
||||
ipv4ll_freearp(ifp);
|
||||
|
||||
if ((ifp->options->options & DHCPCD_NODROP) == DHCPCD_NODROP)
|
||||
return;
|
||||
|
||||
state = IPV4LL_STATE(ifp);
|
||||
if (state) {
|
||||
state->running = false;
|
||||
if (state->addr != NULL) {
|
||||
if (ifp->options->options & DHCPCD_CONFIGURE)
|
||||
ipv4_deladdr(state->addr, 1);
|
||||
state->addr = NULL;
|
||||
dropped = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free any other link local addresses that might exist. */
|
||||
if ((istate = IPV4_STATE(ifp)) != NULL) {
|
||||
struct ipv4_addr *ia, *ian;
|
||||
|
||||
TAILQ_FOREACH_SAFE(ia, &istate->addrs, next, ian) {
|
||||
if (IN_LINKLOCAL(ntohl(ia->addr.s_addr))) {
|
||||
if (ifp->options->options & DHCPCD_CONFIGURE)
|
||||
ipv4_deladdr(ia, 0);
|
||||
dropped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dropped) {
|
||||
rt_build(ifp->ctx, AF_INET);
|
||||
script_runreason(ifp, "IPV4LL");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ipv4ll_reset(struct interface *ifp)
|
||||
{
|
||||
struct ipv4ll_state *state = IPV4LL_STATE(ifp);
|
||||
|
||||
if (state == NULL)
|
||||
return;
|
||||
ipv4ll_freearp(ifp);
|
||||
state->pickedaddr.s_addr = INADDR_ANY;
|
||||
state->seeded = false;
|
||||
}
|
||||
|
||||
void
|
||||
ipv4ll_free(struct interface *ifp)
|
||||
{
|
||||
|
||||
assert(ifp != NULL);
|
||||
|
||||
ipv4ll_freearp(ifp);
|
||||
free(IPV4LL_STATE(ifp));
|
||||
ifp->if_data[IF_DATA_IPV4LL] = NULL;
|
||||
}
|
||||
|
||||
/* This may cause issues in BSD systems, where running as a single dhcpcd
|
||||
* daemon would solve this issue easily. */
|
||||
#ifdef HAVE_ROUTE_METRIC
|
||||
int
|
||||
ipv4ll_recvrt(__unused int cmd, const struct rt *rt)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx;
|
||||
struct interface *ifp;
|
||||
|
||||
/* Only interested in default route changes. */
|
||||
if (sa_is_unspecified(&rt->rt_dest))
|
||||
return 0;
|
||||
|
||||
/* If any interface is running IPv4LL, rebuild our routing table. */
|
||||
ctx = rt->rt_ifp->ctx;
|
||||
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
|
||||
if (IPV4LL_STATE_RUNNING(ifp)) {
|
||||
rt_build(ctx, AF_INET);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct ipv4_addr *
|
||||
ipv4ll_handleifa(int cmd, struct ipv4_addr *ia, pid_t pid)
|
||||
{
|
||||
struct interface *ifp;
|
||||
struct ipv4ll_state *state;
|
||||
|
||||
ifp = ia->iface;
|
||||
state = IPV4LL_STATE(ifp);
|
||||
if (state == NULL)
|
||||
return ia;
|
||||
|
||||
if (cmd == RTM_DELADDR &&
|
||||
state->addr != NULL &&
|
||||
IN_ARE_ADDR_EQUAL(&state->addr->addr, &ia->addr))
|
||||
{
|
||||
loginfox("%s: pid %d deleted IP address %s",
|
||||
ifp->name, pid, ia->saddr);
|
||||
ipv4ll_defend_failed(ifp);
|
||||
return ia;
|
||||
}
|
||||
|
||||
#ifdef IN_IFF_DUPLICATED
|
||||
if (cmd != RTM_NEWADDR)
|
||||
return ia;
|
||||
if (!IN_ARE_ADDR_EQUAL(&state->pickedaddr, &ia->addr))
|
||||
return ia;
|
||||
if (!(ia->addr_flags & IN_IFF_NOTUSEABLE))
|
||||
ipv4ll_not_found(ifp);
|
||||
else if (ia->addr_flags & IN_IFF_DUPLICATED) {
|
||||
logerrx("%s: DAD detected %s", ifp->name, ia->saddr);
|
||||
ipv4ll_freearp(ifp);
|
||||
if (ifp->options->options & DHCPCD_CONFIGURE)
|
||||
ipv4_deladdr(ia, 1);
|
||||
state->addr = NULL;
|
||||
rt_build(ifp->ctx, AF_INET);
|
||||
ipv4ll_found(ifp);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
return ia;
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef IPV4LL_H
|
||||
#define IPV4LL_H
|
||||
|
||||
#define LINKLOCAL_ADDR 0xa9fe0000
|
||||
#define LINKLOCAL_MASK IN_CLASSB_NET
|
||||
#define LINKLOCAL_BCAST (LINKLOCAL_ADDR | ~LINKLOCAL_MASK)
|
||||
|
||||
#ifndef IN_LINKLOCAL
|
||||
# define IN_LINKLOCAL(addr) ((addr & IN_CLASSB_NET) == LINKLOCAL_ADDR)
|
||||
#endif
|
||||
|
||||
#ifdef IPV4LL
|
||||
#include "arp.h"
|
||||
|
||||
struct ipv4ll_state {
|
||||
struct in_addr pickedaddr;
|
||||
struct ipv4_addr *addr;
|
||||
char randomstate[128];
|
||||
bool running;
|
||||
bool seeded;
|
||||
bool down;
|
||||
size_t conflicts;
|
||||
#ifndef KERNEL_RFC5227
|
||||
struct arp_state *arp;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define IPV4LL_STATE(ifp) \
|
||||
((struct ipv4ll_state *)(ifp)->if_data[IF_DATA_IPV4LL])
|
||||
#define IPV4LL_CSTATE(ifp) \
|
||||
((const struct ipv4ll_state *)(ifp)->if_data[IF_DATA_IPV4LL])
|
||||
#define IPV4LL_STATE_RUNNING(ifp) \
|
||||
(IPV4LL_CSTATE((ifp)) && !IPV4LL_CSTATE((ifp))->down && \
|
||||
(IPV4LL_CSTATE((ifp))->addr != NULL))
|
||||
|
||||
int ipv4ll_subnetroute(rb_tree_t *, struct interface *);
|
||||
int ipv4ll_defaultroute(rb_tree_t *,struct interface *);
|
||||
ssize_t ipv4ll_env(FILE *, const char *, const struct interface *);
|
||||
void ipv4ll_start(void *);
|
||||
void ipv4ll_claimed(void *);
|
||||
void ipv4ll_handle_failure(void *);
|
||||
struct ipv4_addr *ipv4ll_handleifa(int, struct ipv4_addr *, pid_t pid);
|
||||
#ifdef HAVE_ROUTE_METRIC
|
||||
int ipv4ll_recvrt(int, const struct rt *);
|
||||
#endif
|
||||
|
||||
void ipv4ll_reset(struct interface *);
|
||||
void ipv4ll_drop(struct interface *);
|
||||
void ipv4ll_free(struct interface *);
|
||||
#endif
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,316 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef IPV6_H
|
||||
#define IPV6_H
|
||||
|
||||
#include <sys/uio.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "if.h"
|
||||
|
||||
#ifndef __linux__
|
||||
# if !defined(__QNX__) && !defined(__sun)
|
||||
# include <sys/endian.h>
|
||||
# endif
|
||||
# include <net/if.h>
|
||||
# ifndef __sun
|
||||
# include <netinet6/in6_var.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define EUI64_GBIT 0x01
|
||||
#define EUI64_UBIT 0x02
|
||||
#define EUI64_TO_IFID(in6) do {(in6)->s6_addr[8] ^= EUI64_UBIT; } while (0)
|
||||
#define EUI64_GROUP(in6) ((in6)->s6_addr[8] & EUI64_GBIT)
|
||||
|
||||
#ifndef ND6_INFINITE_LIFETIME
|
||||
# define ND6_INFINITE_LIFETIME ((uint32_t)~0)
|
||||
#endif
|
||||
|
||||
/* RFC4941 constants */
|
||||
#define TEMP_VALID_LIFETIME 604800 /* 1 week */
|
||||
#define TEMP_PREFERRED_LIFETIME 86400 /* 1 day */
|
||||
#define REGEN_ADVANCE 5 /* seconds */
|
||||
#define MAX_DESYNC_FACTOR 600 /* 10 minutes */
|
||||
#define TEMP_IDGEN_RETRIES 3
|
||||
|
||||
/* RFC7217 constants */
|
||||
#define IDGEN_RETRIES 3
|
||||
#define IDGEN_DELAY 1 /* second */
|
||||
|
||||
/* Interface identifier length. Prefix + this == 128 for autoconf */
|
||||
#define ipv6_ifidlen(ifp) 64
|
||||
#define IA6_CANAUTOCONF(ia) \
|
||||
((ia)->prefix_len + ipv6_ifidlen((ia)->iface) == 128)
|
||||
|
||||
#ifndef IN6_ARE_MASKED_ADDR_EQUAL
|
||||
#define IN6_ARE_MASKED_ADDR_EQUAL(d, a, m) ( \
|
||||
(((d)->s6_addr32[0] ^ (a)->s6_addr32[0]) & (m)->s6_addr32[0]) == 0 && \
|
||||
(((d)->s6_addr32[1] ^ (a)->s6_addr32[1]) & (m)->s6_addr32[1]) == 0 && \
|
||||
(((d)->s6_addr32[2] ^ (a)->s6_addr32[2]) & (m)->s6_addr32[2]) == 0 && \
|
||||
(((d)->s6_addr32[3] ^ (a)->s6_addr32[3]) & (m)->s6_addr32[3]) == 0 )
|
||||
#endif
|
||||
|
||||
#ifndef IN6ADDR_LINKLOCAL_ALLNODES_INIT
|
||||
#define IN6ADDR_LINKLOCAL_ALLNODES_INIT \
|
||||
{{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}}
|
||||
#endif
|
||||
#ifndef IN6ADDR_LINKLOCAL_ALLROUTERS_INIT
|
||||
#define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT \
|
||||
{{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }}}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* BSD kernels don't inform userland of DAD results.
|
||||
* See the discussion here:
|
||||
* http://mail-index.netbsd.org/tech-net/2013/03/15/msg004019.html
|
||||
*/
|
||||
#ifndef __linux__
|
||||
/* We guard here to avoid breaking a compile on linux ppc-64 headers */
|
||||
# include <sys/param.h>
|
||||
#endif
|
||||
#ifdef BSD
|
||||
# define IPV6_POLLADDRFLAG
|
||||
#endif
|
||||
|
||||
/* This was fixed in NetBSD */
|
||||
#if (defined(__DragonFly_version) && __DragonFly_version >= 500704) || \
|
||||
(defined(__NetBSD_Version__) && __NetBSD_Version__ >= 699002000)
|
||||
# undef IPV6_POLLADDRFLAG
|
||||
#endif
|
||||
|
||||
/* Of course OpenBSD has their own special name. */
|
||||
#if !defined(IN6_IFF_TEMPORARY) && defined(IN6_IFF_PRIVACY)
|
||||
#define IN6_IFF_TEMPORARY IN6_IFF_PRIVACY
|
||||
#endif
|
||||
|
||||
#ifdef __sun
|
||||
/* Solaris lacks these defines.
|
||||
* While it supports DaD, to seems to only expose IFF_DUPLICATE
|
||||
* so we have no way of knowing if it's tentative or not.
|
||||
* I don't even know if Solaris has any special treatment for tentative. */
|
||||
# define IN6_IFF_TENTATIVE 0x02
|
||||
# define IN6_IFF_DUPLICATED 0x04
|
||||
# define IN6_IFF_DETACHED 0x00
|
||||
#endif
|
||||
|
||||
#define IN6_IFF_NOTUSEABLE \
|
||||
(IN6_IFF_TENTATIVE | IN6_IFF_DUPLICATED | IN6_IFF_DETACHED)
|
||||
|
||||
/*
|
||||
* If dhcpcd handles RA processing instead of the kernel, the kernel needs
|
||||
* to either allow userland to set temporary addresses or mark an address
|
||||
* for the kernel to manage temporary addresses from.
|
||||
* If the kernel allows the former, a global #define is needed, otherwise
|
||||
* the address marking will be handled in the platform specific address handler.
|
||||
*
|
||||
* Some BSDs do not allow userland to set temporary addresses.
|
||||
* Linux-3.18 allows the marking of addresses from which to manage temp addrs.
|
||||
*/
|
||||
#if defined(IN6_IFF_TEMPORARY) && !defined(__linux__)
|
||||
#define IPV6_MANAGETEMPADDR
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
/* Match Linux defines to BSD */
|
||||
# ifdef IFA_F_TEMPORARY
|
||||
# define IN6_IFF_TEMPORARY IFA_F_TEMPORARY
|
||||
# endif
|
||||
# ifdef IFA_F_OPTIMISTIC
|
||||
# define IN6_IFF_TENTATIVE (IFA_F_TENTATIVE | IFA_F_OPTIMISTIC)
|
||||
# else
|
||||
# define IN6_IFF_TENTATIVE (IFA_F_TENTATIVE | 0x04)
|
||||
# endif
|
||||
# ifdef IF_F_DADFAILED
|
||||
# define IN6_IFF_DUPLICATED IFA_F_DADFAILED
|
||||
# else
|
||||
# define IN6_IFF_DUPLICATED 0x08
|
||||
# endif
|
||||
# define IN6_IFF_DETACHED 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ND6 Advertising is only used for IP address sharing to prefer
|
||||
* the address on a specific interface.
|
||||
* This just fails to work on OpenBSD and causes erroneous duplicate
|
||||
* address messages on BSD's other then DragonFly and NetBSD.
|
||||
*/
|
||||
#if !defined(SMALL) && \
|
||||
((defined(__DragonFly_version) && __DragonFly_version >= 500703) || \
|
||||
(defined(__NetBSD_Version__) && __NetBSD_Version__ >= 899002800) || \
|
||||
defined(__linux__) || defined(__sun))
|
||||
# define ND6_ADVERTISE
|
||||
#endif
|
||||
|
||||
#ifdef INET6
|
||||
TAILQ_HEAD(ipv6_addrhead, ipv6_addr);
|
||||
struct ipv6_addr {
|
||||
TAILQ_ENTRY(ipv6_addr) next;
|
||||
struct interface *iface;
|
||||
struct in6_addr prefix;
|
||||
uint8_t prefix_len;
|
||||
uint32_t prefix_vltime;
|
||||
uint32_t prefix_pltime;
|
||||
struct timespec created;
|
||||
struct timespec acquired;
|
||||
struct in6_addr addr;
|
||||
int addr_flags;
|
||||
unsigned int flags;
|
||||
char saddr[INET6_ADDRSTRLEN];
|
||||
uint8_t iaid[4];
|
||||
uint16_t ia_type;
|
||||
int dhcp6_fd;
|
||||
|
||||
#ifndef SMALL
|
||||
struct ipv6_addr *delegating_prefix;
|
||||
struct ipv6_addrhead pd_pfxs;
|
||||
TAILQ_ENTRY(ipv6_addr) pd_next;
|
||||
|
||||
uint8_t prefix_exclude_len;
|
||||
struct in6_addr prefix_exclude;
|
||||
#endif
|
||||
|
||||
void (*dadcallback)(void *);
|
||||
int dadcounter;
|
||||
|
||||
struct nd_neighbor_advert *na;
|
||||
size_t na_len;
|
||||
int na_count;
|
||||
|
||||
#ifdef ALIAS_ADDR
|
||||
char alias[IF_NAMESIZE];
|
||||
#endif
|
||||
};
|
||||
|
||||
#define IPV6_AF_ONLINK (1U << 0)
|
||||
#define IPV6_AF_NEW (1U << 1)
|
||||
#define IPV6_AF_STALE (1U << 2)
|
||||
#define IPV6_AF_ADDED (1U << 3)
|
||||
#define IPV6_AF_AUTOCONF (1U << 4)
|
||||
#define IPV6_AF_DADCOMPLETED (1U << 5)
|
||||
#define IPV6_AF_DELEGATED (1U << 6)
|
||||
#define IPV6_AF_DELEGATEDPFX (1U << 7)
|
||||
#define IPV6_AF_NOREJECT (1U << 8)
|
||||
#define IPV6_AF_REQUEST (1U << 9)
|
||||
#define IPV6_AF_STATIC (1U << 10)
|
||||
#define IPV6_AF_DELEGATEDLOG (1U << 11)
|
||||
#define IPV6_AF_RAPFX (1U << 12)
|
||||
#define IPV6_AF_EXTENDED (1U << 13)
|
||||
#define IPV6_AF_REGEN (1U << 14)
|
||||
#define IPV6_AF_ROUTER (1U << 15)
|
||||
#ifdef IPV6_MANAGETEMPADDR
|
||||
#define IPV6_AF_TEMPORARY (1U << 16)
|
||||
#endif
|
||||
|
||||
struct ll_callback {
|
||||
TAILQ_ENTRY(ll_callback) next;
|
||||
void (*callback)(void *);
|
||||
void *arg;
|
||||
};
|
||||
TAILQ_HEAD(ll_callback_head, ll_callback);
|
||||
|
||||
struct ipv6_state {
|
||||
struct ipv6_addrhead addrs;
|
||||
struct ll_callback_head ll_callbacks;
|
||||
|
||||
#ifdef IPV6_MANAGETEMPADDR
|
||||
uint32_t desync_factor;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define IPV6_STATE(ifp) \
|
||||
((struct ipv6_state *)(ifp)->if_data[IF_DATA_IPV6])
|
||||
#define IPV6_CSTATE(ifp) \
|
||||
((const struct ipv6_state *)(ifp)->if_data[IF_DATA_IPV6])
|
||||
#define IPV6_STATE_RUNNING(ifp) ipv6_staticdadcompleted((ifp))
|
||||
|
||||
|
||||
int ipv6_init(struct dhcpcd_ctx *);
|
||||
int ipv6_makestableprivate(struct in6_addr *,
|
||||
const struct in6_addr *, int, const struct interface *, int *);
|
||||
int ipv6_makeaddr(struct in6_addr *, struct interface *,
|
||||
const struct in6_addr *, int, unsigned int);
|
||||
int ipv6_mask(struct in6_addr *, int);
|
||||
uint8_t ipv6_prefixlen(const struct in6_addr *);
|
||||
int ipv6_userprefix( const struct in6_addr *, short prefix_len,
|
||||
uint64_t user_number, struct in6_addr *result, short result_len);
|
||||
void ipv6_checkaddrflags(void *);
|
||||
void ipv6_markaddrsstale(struct interface *, unsigned int);
|
||||
void ipv6_deletestaleaddrs(struct interface *);
|
||||
int ipv6_addaddr(struct ipv6_addr *, const struct timespec *);
|
||||
int ipv6_doaddr(struct ipv6_addr *, struct timespec *);
|
||||
ssize_t ipv6_addaddrs(struct ipv6_addrhead *addrs);
|
||||
void ipv6_deleteaddr(struct ipv6_addr *);
|
||||
void ipv6_freedrop_addrs(struct ipv6_addrhead *, int,
|
||||
const struct interface *);
|
||||
void ipv6_handleifa(struct dhcpcd_ctx *ctx, int, struct if_head *,
|
||||
const char *, const struct in6_addr *, uint8_t, int, pid_t);
|
||||
int ipv6_handleifa_addrs(int, struct ipv6_addrhead *, const struct ipv6_addr *,
|
||||
pid_t);
|
||||
struct ipv6_addr *ipv6_iffindaddr(struct interface *,
|
||||
const struct in6_addr *, int);
|
||||
int ipv6_hasaddr(const struct interface *);
|
||||
struct ipv6_addr *ipv6_anyglobal(struct interface *);
|
||||
int ipv6_findaddrmatch(const struct ipv6_addr *, const struct in6_addr *,
|
||||
unsigned int);
|
||||
struct ipv6_addr *ipv6_findaddr(struct dhcpcd_ctx *,
|
||||
const struct in6_addr *, unsigned int);
|
||||
struct ipv6_addr *ipv6_findmaskaddr(struct dhcpcd_ctx *,
|
||||
const struct in6_addr *);
|
||||
#define ipv6_linklocal(ifp) ipv6_iffindaddr((ifp), NULL, IN6_IFF_NOTUSEABLE)
|
||||
int ipv6_addlinklocalcallback(struct interface *, void (*)(void *), void *);
|
||||
void ipv6_setscope(struct sockaddr_in6 *, unsigned int);
|
||||
unsigned int ipv6_getscope(const struct sockaddr_in6 *);
|
||||
struct ipv6_addr *ipv6_newaddr(struct interface *, const struct in6_addr *,
|
||||
uint8_t, unsigned int);
|
||||
void ipv6_freeaddr(struct ipv6_addr *);
|
||||
void ipv6_freedrop(struct interface *, int);
|
||||
#define ipv6_free(ifp) ipv6_freedrop((ifp), 0)
|
||||
#define ipv6_drop(ifp) ipv6_freedrop((ifp), 2)
|
||||
|
||||
#ifdef IPV6_MANAGETEMPADDR
|
||||
struct ipv6_addr *ipv6_createtempaddr(struct ipv6_addr *,
|
||||
const struct timespec *);
|
||||
struct ipv6_addr *ipv6_settemptime(struct ipv6_addr *, int);
|
||||
void ipv6_addtempaddrs(struct interface *, const struct timespec *);
|
||||
void ipv6_regentempaddrs(void *);
|
||||
#endif
|
||||
|
||||
int ipv6_start(struct interface *);
|
||||
int ipv6_staticdadcompleted(const struct interface *);
|
||||
int ipv6_startstatic(struct interface *);
|
||||
ssize_t ipv6_env(FILE *, const char *, const struct interface *);
|
||||
void ipv6_ctxfree(struct dhcpcd_ctx *);
|
||||
bool inet6_getroutes(struct dhcpcd_ctx *, rb_tree_t *);
|
||||
#endif /* INET6 */
|
||||
|
||||
#endif /* INET6_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,147 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - IPv6 ND handling
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef IPV6ND_H
|
||||
#define IPV6ND_H
|
||||
|
||||
#ifdef INET6
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "dhcpcd.h"
|
||||
#include "ipv6.h"
|
||||
|
||||
/* rfc4191 */
|
||||
struct routeinfo {
|
||||
TAILQ_ENTRY(routeinfo) next;
|
||||
struct in6_addr prefix;
|
||||
uint8_t prefix_len;
|
||||
uint32_t lifetime;
|
||||
uint8_t flags;
|
||||
struct timespec acquired;
|
||||
char sprefix[INET6_ADDRSTRLEN];
|
||||
};
|
||||
|
||||
TAILQ_HEAD(routeinfohead, routeinfo);
|
||||
|
||||
|
||||
struct ra {
|
||||
TAILQ_ENTRY(ra) next;
|
||||
struct interface *iface;
|
||||
struct in6_addr from;
|
||||
char sfrom[INET6_ADDRSTRLEN];
|
||||
uint8_t *data;
|
||||
size_t data_len;
|
||||
struct timespec acquired;
|
||||
uint8_t flags;
|
||||
uint32_t lifetime;
|
||||
uint32_t reachable;
|
||||
uint32_t retrans;
|
||||
uint32_t mtu;
|
||||
uint8_t hoplimit;
|
||||
struct ipv6_addrhead addrs;
|
||||
struct routeinfohead rinfos;
|
||||
bool hasdns;
|
||||
bool expired;
|
||||
bool willexpire;
|
||||
bool doexpire;
|
||||
bool isreachable;
|
||||
};
|
||||
|
||||
TAILQ_HEAD(ra_head, ra);
|
||||
|
||||
struct rs_state {
|
||||
struct nd_router_solicit *rs;
|
||||
size_t rslen;
|
||||
int rsprobes;
|
||||
uint32_t retrans;
|
||||
#ifdef __sun
|
||||
int nd_fd;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define RS_STATE(a) ((struct rs_state *)(ifp)->if_data[IF_DATA_IPV6ND])
|
||||
#define RS_CSTATE(a) ((const struct rs_state *)(ifp)->if_data[IF_DATA_IPV6ND])
|
||||
#define RS_STATE_RUNNING(a) (ipv6nd_hasra((a)) && ipv6nd_dadcompleted((a)))
|
||||
|
||||
#ifndef MAX_RTR_SOLICITATION_DELAY
|
||||
#define MAX_RTR_SOLICITATION_DELAY 1 /* seconds */
|
||||
#define MAX_UNICAST_SOLICIT 3 /* 3 transmissions */
|
||||
#define RTR_SOLICITATION_INTERVAL 4 /* seconds */
|
||||
#define MAX_RTR_SOLICITATIONS 3 /* times */
|
||||
#define MAX_NEIGHBOR_ADVERTISEMENT 3 /* 3 transmissions */
|
||||
|
||||
#ifndef IPV6_DEFHLIM
|
||||
#define IPV6_DEFHLIM 64
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* On carrier up, expire known routers after RTR_CARRIER_EXPIRE seconds. */
|
||||
#define RTR_CARRIER_EXPIRE \
|
||||
(MAX_RTR_SOLICITATION_DELAY + \
|
||||
(MAX_RTR_SOLICITATIONS + 1) * \
|
||||
RTR_SOLICITATION_INTERVAL)
|
||||
|
||||
#define MAX_REACHABLE_TIME 3600000 /* milliseconds */
|
||||
#define REACHABLE_TIME 30000 /* milliseconds */
|
||||
#define RETRANS_TIMER 1000 /* milliseconds */
|
||||
#define DELAY_FIRST_PROBE_TIME 5 /* seconds */
|
||||
|
||||
#define MIN_EXTENDED_VLTIME 7200 /* seconds */
|
||||
|
||||
int ipv6nd_open(bool);
|
||||
#ifdef __sun
|
||||
int ipv6nd_openif(struct interface *);
|
||||
#endif
|
||||
void ipv6nd_recvmsg(struct dhcpcd_ctx *, struct msghdr *);
|
||||
int ipv6nd_rtpref(uint8_t);
|
||||
void ipv6nd_printoptions(const struct dhcpcd_ctx *,
|
||||
const struct dhcp_opt *, size_t);
|
||||
void ipv6nd_startrs(struct interface *);
|
||||
ssize_t ipv6nd_env(FILE *, const struct interface *);
|
||||
const struct ipv6_addr *ipv6nd_iffindaddr(const struct interface *ifp,
|
||||
const struct in6_addr *addr, unsigned int flags);
|
||||
struct ipv6_addr *ipv6nd_findaddr(struct dhcpcd_ctx *,
|
||||
const struct in6_addr *, unsigned int);
|
||||
struct ipv6_addr *ipv6nd_iffindprefix(struct interface *,
|
||||
const struct in6_addr *, uint8_t);
|
||||
ssize_t ipv6nd_free(struct interface *);
|
||||
void ipv6nd_expirera(void *arg);
|
||||
bool ipv6nd_hasralifetime(const struct interface *, bool);
|
||||
#define ipv6nd_hasra(i) ipv6nd_hasralifetime((i), false)
|
||||
bool ipv6nd_hasradhcp(const struct interface *, bool);
|
||||
void ipv6nd_handleifa(int, struct ipv6_addr *, pid_t);
|
||||
int ipv6nd_dadcompleted(const struct interface *);
|
||||
void ipv6nd_advertise(struct ipv6_addr *);
|
||||
void ipv6nd_startexpire(struct interface *);
|
||||
void ipv6nd_drop(struct interface *);
|
||||
void ipv6nd_neighbour(struct dhcpcd_ctx *, struct in6_addr *, bool);
|
||||
#endif /* INET6 */
|
||||
|
||||
#endif /* IPV6ND_H */
|
|
@ -0,0 +1,499 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* logerr: errx with logging
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "logerr.h"
|
||||
|
||||
#ifndef LOGERR_SYSLOG_FACILITY
|
||||
#define LOGERR_SYSLOG_FACILITY LOG_DAEMON
|
||||
#endif
|
||||
|
||||
#ifdef SMALL
|
||||
#undef LOGERR_TAG
|
||||
#endif
|
||||
|
||||
/* syslog protocol is 1k message max, RFC 3164 section 4.1 */
|
||||
#define LOGERR_SYSLOGBUF 1024 + sizeof(int) + sizeof(pid_t)
|
||||
|
||||
#define UNUSED(a) (void)(a)
|
||||
|
||||
struct logctx {
|
||||
char log_buf[BUFSIZ];
|
||||
unsigned int log_opts;
|
||||
int log_fd;
|
||||
pid_t log_pid;
|
||||
#ifndef SMALL
|
||||
FILE *log_file;
|
||||
#ifdef LOGERR_TAG
|
||||
const char *log_tag;
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct logctx _logctx = {
|
||||
/* syslog style, but without the hostname or tag. */
|
||||
.log_opts = LOGERR_LOG | LOGERR_LOG_DATE | LOGERR_LOG_PID,
|
||||
.log_fd = -1,
|
||||
.log_pid = 0,
|
||||
};
|
||||
|
||||
#if defined(__linux__)
|
||||
/* Poor man's getprogname(3). */
|
||||
static char *_logprog;
|
||||
static const char *
|
||||
getprogname(void)
|
||||
{
|
||||
const char *p;
|
||||
|
||||
/* Use PATH_MAX + 1 to avoid truncation. */
|
||||
if (_logprog == NULL) {
|
||||
/* readlink(2) does not append a NULL byte,
|
||||
* so zero the buffer. */
|
||||
if ((_logprog = calloc(1, PATH_MAX + 1)) == NULL)
|
||||
return NULL;
|
||||
if (readlink("/proc/self/exe", _logprog, PATH_MAX + 1) == -1) {
|
||||
free(_logprog);
|
||||
_logprog = NULL;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (_logprog[0] == '[')
|
||||
return NULL;
|
||||
p = strrchr(_logprog, '/');
|
||||
if (p == NULL)
|
||||
return _logprog;
|
||||
return p + 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SMALL
|
||||
/* Write the time, syslog style. month day time - */
|
||||
static int
|
||||
logprintdate(FILE *stream)
|
||||
{
|
||||
struct timeval tv;
|
||||
time_t now;
|
||||
struct tm tmnow;
|
||||
char buf[32];
|
||||
|
||||
if (gettimeofday(&tv, NULL) == -1)
|
||||
return -1;
|
||||
|
||||
now = tv.tv_sec;
|
||||
if (localtime_r(&now, &tmnow) == NULL)
|
||||
return -1;
|
||||
if (strftime(buf, sizeof(buf), "%b %d %T ", &tmnow) == 0)
|
||||
return -1;
|
||||
return fprintf(stream, "%s", buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
__printflike(3, 0) static int
|
||||
vlogprintf_r(struct logctx *ctx, FILE *stream, const char *fmt, va_list args)
|
||||
{
|
||||
int len = 0, e;
|
||||
va_list a;
|
||||
#ifndef SMALL
|
||||
bool log_pid;
|
||||
#ifdef LOGERR_TAG
|
||||
bool log_tag;
|
||||
#endif
|
||||
|
||||
if ((stream == stderr && ctx->log_opts & LOGERR_ERR_DATE) ||
|
||||
(stream != stderr && ctx->log_opts & LOGERR_LOG_DATE))
|
||||
{
|
||||
if ((e = logprintdate(stream)) == -1)
|
||||
return -1;
|
||||
len += e;
|
||||
}
|
||||
|
||||
#ifdef LOGERR_TAG
|
||||
log_tag = ((stream == stderr && ctx->log_opts & LOGERR_ERR_TAG) ||
|
||||
(stream != stderr && ctx->log_opts & LOGERR_LOG_TAG));
|
||||
if (log_tag) {
|
||||
if (ctx->log_tag == NULL)
|
||||
ctx->log_tag = getprogname();
|
||||
if ((e = fprintf(stream, "%s", ctx->log_tag)) == -1)
|
||||
return -1;
|
||||
len += e;
|
||||
}
|
||||
#endif
|
||||
|
||||
log_pid = ((stream == stderr && ctx->log_opts & LOGERR_ERR_PID) ||
|
||||
(stream != stderr && ctx->log_opts & LOGERR_LOG_PID));
|
||||
if (log_pid) {
|
||||
pid_t pid;
|
||||
|
||||
if (ctx->log_pid == 0)
|
||||
pid = getpid();
|
||||
else
|
||||
pid = ctx->log_pid;
|
||||
if ((e = fprintf(stream, "[%d]", pid)) == -1)
|
||||
return -1;
|
||||
len += e;
|
||||
}
|
||||
|
||||
#ifdef LOGERR_TAG
|
||||
if (log_tag || log_pid)
|
||||
#else
|
||||
if (log_pid)
|
||||
#endif
|
||||
{
|
||||
if ((e = fprintf(stream, ": ")) == -1)
|
||||
return -1;
|
||||
len += e;
|
||||
}
|
||||
#else
|
||||
UNUSED(ctx);
|
||||
#endif
|
||||
|
||||
va_copy(a, args);
|
||||
e = vfprintf(stream, fmt, a);
|
||||
if (fputc('\n', stream) == EOF)
|
||||
e = -1;
|
||||
else if (e != -1)
|
||||
e++;
|
||||
va_end(a);
|
||||
|
||||
return e == -1 ? -1 : len + e;
|
||||
}
|
||||
|
||||
/*
|
||||
* NetBSD's gcc has been modified to check for the non standard %m in printf
|
||||
* like functions and warn noisily about it that they should be marked as
|
||||
* syslog like instead.
|
||||
* This is all well and good, but our logger also goes via vfprintf and
|
||||
* when marked as a sysloglike funcion, gcc will then warn us that the
|
||||
* function should be printflike instead!
|
||||
* This creates an infinte loop of gcc warnings.
|
||||
* Until NetBSD solves this issue, we have to disable a gcc diagnostic
|
||||
* for our fully standards compliant code in the logger function.
|
||||
*/
|
||||
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5))
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmissing-format-attribute"
|
||||
#endif
|
||||
__printflike(2, 0) static int
|
||||
vlogmessage(int pri, const char *fmt, va_list args)
|
||||
{
|
||||
struct logctx *ctx = &_logctx;
|
||||
int len = 0;
|
||||
|
||||
if (ctx->log_fd != -1) {
|
||||
char buf[LOGERR_SYSLOGBUF];
|
||||
pid_t pid;
|
||||
|
||||
memcpy(buf, &pri, sizeof(pri));
|
||||
pid = getpid();
|
||||
memcpy(buf + sizeof(pri), &pid, sizeof(pid));
|
||||
len = vsnprintf(buf + sizeof(pri) + sizeof(pid),
|
||||
sizeof(buf) - sizeof(pri) - sizeof(pid),
|
||||
fmt, args);
|
||||
if (len != -1)
|
||||
len = (int)write(ctx->log_fd, buf,
|
||||
((size_t)++len) + sizeof(pri) + sizeof(pid));
|
||||
return len;
|
||||
}
|
||||
|
||||
if (ctx->log_opts & LOGERR_ERR &&
|
||||
(pri <= LOG_ERR ||
|
||||
(!(ctx->log_opts & LOGERR_QUIET) && pri <= LOG_INFO) ||
|
||||
(ctx->log_opts & LOGERR_DEBUG && pri <= LOG_DEBUG)))
|
||||
len = vlogprintf_r(ctx, stderr, fmt, args);
|
||||
|
||||
#ifndef SMALL
|
||||
if (ctx->log_file != NULL &&
|
||||
(pri != LOG_DEBUG || (ctx->log_opts & LOGERR_DEBUG)))
|
||||
len = vlogprintf_r(ctx, ctx->log_file, fmt, args);
|
||||
#endif
|
||||
|
||||
if (ctx->log_opts & LOGERR_LOG)
|
||||
vsyslog(pri, fmt, args);
|
||||
|
||||
return len;
|
||||
}
|
||||
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5))
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
__printflike(2, 3) void
|
||||
logmessage(int pri, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vlogmessage(pri, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
__printflike(2, 0) static void
|
||||
vlogerrmessage(int pri, const char *fmt, va_list args)
|
||||
{
|
||||
int _errno = errno;
|
||||
char buf[1024];
|
||||
|
||||
vsnprintf(buf, sizeof(buf), fmt, args);
|
||||
logmessage(pri, "%s: %s", buf, strerror(_errno));
|
||||
errno = _errno;
|
||||
}
|
||||
|
||||
__printflike(2, 3) void
|
||||
logerrmessage(int pri, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vlogerrmessage(pri, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void
|
||||
log_debug(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vlogerrmessage(LOG_DEBUG, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void
|
||||
log_debugx(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vlogmessage(LOG_DEBUG, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void
|
||||
log_info(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vlogerrmessage(LOG_INFO, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void
|
||||
log_infox(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vlogmessage(LOG_INFO, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void
|
||||
log_warn(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vlogerrmessage(LOG_WARNING, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void
|
||||
log_warnx(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vlogmessage(LOG_WARNING, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void
|
||||
log_err(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vlogerrmessage(LOG_ERR, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void
|
||||
log_errx(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vlogmessage(LOG_ERR, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
int
|
||||
loggetfd(void)
|
||||
{
|
||||
struct logctx *ctx = &_logctx;
|
||||
|
||||
return ctx->log_fd;
|
||||
}
|
||||
|
||||
void
|
||||
logsetfd(int fd)
|
||||
{
|
||||
struct logctx *ctx = &_logctx;
|
||||
|
||||
ctx->log_fd = fd;
|
||||
if (fd != -1)
|
||||
closelog();
|
||||
#ifndef SMALL
|
||||
if (fd != -1 && ctx->log_file != NULL) {
|
||||
fclose(ctx->log_file);
|
||||
ctx->log_file = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
logreadfd(int fd)
|
||||
{
|
||||
struct logctx *ctx = &_logctx;
|
||||
char buf[LOGERR_SYSLOGBUF];
|
||||
int len, pri;
|
||||
|
||||
len = (int)read(fd, buf, sizeof(buf));
|
||||
if (len == -1)
|
||||
return -1;
|
||||
|
||||
/* Ensure we have pri, pid and a terminator */
|
||||
if (len < (int)(sizeof(pri) + sizeof(pid_t) + 1) ||
|
||||
buf[len - 1] != '\0')
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(&pri, buf, sizeof(pri));
|
||||
memcpy(&ctx->log_pid, buf + sizeof(pri), sizeof(ctx->log_pid));
|
||||
logmessage(pri, "%s", buf + sizeof(pri) + sizeof(ctx->log_pid));
|
||||
ctx->log_pid = 0;
|
||||
return len;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
loggetopts(void)
|
||||
{
|
||||
struct logctx *ctx = &_logctx;
|
||||
|
||||
return ctx->log_opts;
|
||||
}
|
||||
|
||||
void
|
||||
logsetopts(unsigned int opts)
|
||||
{
|
||||
struct logctx *ctx = &_logctx;
|
||||
|
||||
ctx->log_opts = opts;
|
||||
setlogmask(LOG_UPTO(opts & LOGERR_DEBUG ? LOG_DEBUG : LOG_INFO));
|
||||
}
|
||||
|
||||
#ifdef LOGERR_TAG
|
||||
void
|
||||
logsettag(const char *tag)
|
||||
{
|
||||
#if !defined(SMALL)
|
||||
struct logctx *ctx = &_logctx;
|
||||
|
||||
ctx->log_tag = tag;
|
||||
#else
|
||||
UNUSED(tag);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
logopen(const char *path)
|
||||
{
|
||||
struct logctx *ctx = &_logctx;
|
||||
int opts = 0;
|
||||
|
||||
/* Cache timezone */
|
||||
tzset();
|
||||
|
||||
(void)setvbuf(stderr, ctx->log_buf, _IOLBF, sizeof(ctx->log_buf));
|
||||
|
||||
#ifndef SMALL
|
||||
if (ctx->log_file != NULL) {
|
||||
fclose(ctx->log_file);
|
||||
ctx->log_file = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ctx->log_opts & LOGERR_LOG_PID)
|
||||
opts |= LOG_PID;
|
||||
openlog(getprogname(), opts, LOGERR_SYSLOG_FACILITY);
|
||||
if (path == NULL)
|
||||
return 1;
|
||||
|
||||
#ifndef SMALL
|
||||
if ((ctx->log_file = fopen(path, "ae")) == NULL)
|
||||
return -1;
|
||||
setlinebuf(ctx->log_file);
|
||||
return fileno(ctx->log_file);
|
||||
#else
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
logclose(void)
|
||||
{
|
||||
#ifndef SMALL
|
||||
struct logctx *ctx = &_logctx;
|
||||
#endif
|
||||
|
||||
closelog();
|
||||
#if defined(__linux__)
|
||||
free(_logprog);
|
||||
_logprog = NULL;
|
||||
#endif
|
||||
#ifndef SMALL
|
||||
if (ctx->log_file == NULL)
|
||||
return;
|
||||
fclose(ctx->log_file);
|
||||
ctx->log_file = NULL;
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* logerr: errx with logging
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef LOGERR_H
|
||||
#define LOGERR_H
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
#ifndef __printflike
|
||||
#if __GNUC__ > 2 || defined(__INTEL_COMPILER)
|
||||
#define __printflike(a, b) __attribute__((format(printf, a, b)))
|
||||
#else
|
||||
#define __printflike(a, b)
|
||||
#endif
|
||||
#endif /* !__printflike */
|
||||
|
||||
/* Please do not call log_* functions directly, use macros below */
|
||||
__printflike(1, 2) void log_debug(const char *, ...);
|
||||
__printflike(1, 2) void log_debugx(const char *, ...);
|
||||
__printflike(1, 2) void log_info(const char *, ...);
|
||||
__printflike(1, 2) void log_infox(const char *, ...);
|
||||
__printflike(1, 2) void log_warn(const char *, ...);
|
||||
__printflike(1, 2) void log_warnx(const char *, ...);
|
||||
__printflike(1, 2) void log_err(const char *, ...);
|
||||
__printflike(1, 2) void log_errx(const char *, ...);
|
||||
#define LOGERROR logerr("%s: %d", __FILE__, __LINE__)
|
||||
|
||||
__printflike(2, 3) void logmessage(int pri, const char *fmt, ...);
|
||||
__printflike(2, 3) void logerrmessage(int pri, const char *fmt, ...);
|
||||
|
||||
/*
|
||||
* These are macros to prevent taking address of them so
|
||||
* __FILE__, __LINE__, etc can easily be added.
|
||||
*
|
||||
* We should be using
|
||||
* #define loginfox(fmt, __VA_OPT__(,) __VA_ARGS__)
|
||||
* but that requires gcc-8 or clang-6 and we still have a need to support
|
||||
* old OS's without modern compilers.
|
||||
*
|
||||
* Likewise, ##__VA_ARGS__ can't be used as that's a gcc only extension.
|
||||
*
|
||||
* The solution is to put fmt into __VA_ARGS__.
|
||||
* It's not pretty but it's 100% portable.
|
||||
*/
|
||||
#define logdebug(...) log_debug(__VA_ARGS__)
|
||||
#define logdebugx(...) log_debugx(__VA_ARGS__)
|
||||
#define loginfo(...) log_info(__VA_ARGS__)
|
||||
#define loginfox(...) log_infox(__VA_ARGS__)
|
||||
#define logwarn(...) log_warn(__VA_ARGS__)
|
||||
#define logwarnx(...) log_warnx(__VA_ARGS__)
|
||||
#define logerr(...) log_err(__VA_ARGS__)
|
||||
#define logerrx(...) log_errx(__VA_ARGS__)
|
||||
|
||||
/* For logging in a chroot */
|
||||
int loggetfd(void);
|
||||
void logsetfd(int);
|
||||
int logreadfd(int);
|
||||
|
||||
unsigned int loggetopts(void);
|
||||
void logsetopts(unsigned int);
|
||||
#define LOGERR_DEBUG (1U << 6)
|
||||
#define LOGERR_QUIET (1U << 7)
|
||||
#define LOGERR_LOG (1U << 11)
|
||||
#define LOGERR_LOG_DATE (1U << 12)
|
||||
#define LOGERR_LOG_HOST (1U << 13)
|
||||
#define LOGERR_LOG_TAG (1U << 14)
|
||||
#define LOGERR_LOG_PID (1U << 15)
|
||||
#define LOGERR_ERR (1U << 21)
|
||||
#define LOGERR_ERR_DATE (1U << 22)
|
||||
#define LOGERR_ERR_HOST (1U << 23)
|
||||
#define LOGERR_ERR_TAG (1U << 24)
|
||||
#define LOGERR_ERR_PID (1U << 25)
|
||||
|
||||
/* To build tag support or not. */
|
||||
//#define LOGERR_TAG
|
||||
#if defined(LOGERR_TAG)
|
||||
void logsettag(const char *);
|
||||
#endif
|
||||
|
||||
/* Can be called more than once. */
|
||||
int logopen(const char *);
|
||||
|
||||
/* Should only be called at program exit. */
|
||||
void logclose(void);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,386 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* Privilege Separation BPF Initiator
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/* Need these headers just for if_ether on some OS. */
|
||||
#ifndef __NetBSD__
|
||||
#include <net/if.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
#include <netinet/if_ether.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <pwd.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "arp.h"
|
||||
#include "bpf.h"
|
||||
#include "dhcp.h"
|
||||
#include "dhcp6.h"
|
||||
#include "eloop.h"
|
||||
#include "ipv6nd.h"
|
||||
#include "logerr.h"
|
||||
#include "privsep.h"
|
||||
|
||||
/* We expect to have open 3 SEQPACKET and one RAW fd */
|
||||
|
||||
static void
|
||||
ps_bpf_recvbpf(void *arg, unsigned short events)
|
||||
{
|
||||
struct ps_process *psp = arg;
|
||||
struct bpf *bpf = psp->psp_bpf;
|
||||
uint8_t buf[FRAMELEN_MAX];
|
||||
ssize_t len;
|
||||
struct ps_msghdr psm = {
|
||||
.ps_id = psp->psp_id,
|
||||
.ps_cmd = psp->psp_id.psi_cmd,
|
||||
};
|
||||
|
||||
if (events != ELE_READ)
|
||||
logerrx("%s: unexpected event 0x%04x", __func__, events);
|
||||
|
||||
bpf->bpf_flags &= ~BPF_EOF;
|
||||
/* A BPF read can read more than one filtered packet at time.
|
||||
* This mechanism allows us to read each packet from the buffer. */
|
||||
while (!(bpf->bpf_flags & BPF_EOF)) {
|
||||
len = bpf_read(bpf, buf, sizeof(buf));
|
||||
if (len == -1) {
|
||||
int error = errno;
|
||||
|
||||
if (errno != ENETDOWN)
|
||||
logerr("%s: %s", psp->psp_ifname, __func__);
|
||||
if (error != ENXIO)
|
||||
break;
|
||||
/* If the interface has departed, close the BPF
|
||||
* socket. This stops log spam if RTM_IFANNOUNCE is
|
||||
* delayed in announcing the departing interface. */
|
||||
eloop_event_delete(psp->psp_ctx->eloop, bpf->bpf_fd);
|
||||
bpf_close(bpf);
|
||||
psp->psp_bpf = NULL;
|
||||
break;
|
||||
}
|
||||
if (len == 0)
|
||||
break;
|
||||
psm.ps_flags = bpf->bpf_flags;
|
||||
len = ps_sendpsmdata(psp->psp_ctx, psp->psp_ctx->ps_data_fd,
|
||||
&psm, buf, (size_t)len);
|
||||
if (len == -1)
|
||||
logerr(__func__);
|
||||
if (len == -1 || len == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ps_bpf_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
|
||||
{
|
||||
struct ps_process *psp = arg;
|
||||
struct iovec *iov = msg->msg_iov;
|
||||
|
||||
#ifdef PRIVSEP_DEBUG
|
||||
logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp);
|
||||
#endif
|
||||
|
||||
switch(psm->ps_cmd) {
|
||||
#ifdef ARP
|
||||
case PS_BPF_ARP: /* FALLTHROUGH */
|
||||
#endif
|
||||
case PS_BPF_BOOTP:
|
||||
break;
|
||||
default:
|
||||
/* IPC failure, we should not be processing any commands
|
||||
* at this point!/ */
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* We might have had an earlier ENXIO error. */
|
||||
if (psp->psp_bpf == NULL) {
|
||||
errno = ENXIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return bpf_send(psp->psp_bpf, psp->psp_proto,
|
||||
iov->iov_base, iov->iov_len);
|
||||
}
|
||||
|
||||
static void
|
||||
ps_bpf_recvmsg(void *arg, unsigned short events)
|
||||
{
|
||||
struct ps_process *psp = arg;
|
||||
|
||||
if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events,
|
||||
ps_bpf_recvmsgcb, arg) == -1)
|
||||
logerr(__func__);
|
||||
}
|
||||
|
||||
static int
|
||||
ps_bpf_start_bpf(struct ps_process *psp)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = psp->psp_ctx;
|
||||
char *addr;
|
||||
struct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr;
|
||||
|
||||
if (ia->s_addr == INADDR_ANY) {
|
||||
ia = NULL;
|
||||
addr = NULL;
|
||||
} else
|
||||
addr = inet_ntoa(*ia);
|
||||
setproctitle("[BPF %s] %s%s%s", psp->psp_protostr, psp->psp_ifname,
|
||||
addr != NULL ? " " : "", addr != NULL ? addr : "");
|
||||
ps_freeprocesses(ctx, psp);
|
||||
|
||||
psp->psp_bpf = bpf_open(&psp->psp_ifp, psp->psp_filter, ia);
|
||||
#ifdef DEBUG_FD
|
||||
logdebugx("pid %d bpf_fd=%d", getpid(), psp->psp_bpf->bpf_fd);
|
||||
#endif
|
||||
if (psp->psp_bpf == NULL)
|
||||
logerr("%s: bpf_open",__func__);
|
||||
#ifdef PRIVSEP_RIGHTS
|
||||
else if (ps_rights_limit_fd(psp->psp_bpf->bpf_fd) == -1)
|
||||
logerr("%s: ps_rights_limit_fd", __func__);
|
||||
#endif
|
||||
else if (eloop_event_add(ctx->eloop, psp->psp_bpf->bpf_fd, ELE_READ,
|
||||
ps_bpf_recvbpf, psp) == -1)
|
||||
logerr("%s: eloop_event_add", __func__);
|
||||
else {
|
||||
psp->psp_work_fd = psp->psp_bpf->bpf_fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
eloop_exit(ctx->eloop, EXIT_FAILURE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_bpf_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
|
||||
{
|
||||
uint16_t cmd;
|
||||
struct ps_process *psp;
|
||||
pid_t start;
|
||||
struct iovec *iov = msg->msg_iov;
|
||||
struct interface *ifp;
|
||||
struct in_addr *ia = &psm->ps_id.psi_addr.psa_in_addr;
|
||||
const char *addr;
|
||||
|
||||
cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
|
||||
psp = ps_findprocess(ctx, &psm->ps_id);
|
||||
|
||||
#ifdef PRIVSEP_DEBUG
|
||||
logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp);
|
||||
#endif
|
||||
|
||||
switch (cmd) {
|
||||
#ifdef ARP
|
||||
case PS_BPF_ARP: /* FALLTHROUGH */
|
||||
#endif
|
||||
case PS_BPF_BOOTP:
|
||||
break;
|
||||
default:
|
||||
logerrx("%s: unknown command %x", __func__, psm->ps_cmd);
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(psm->ps_cmd & PS_START)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (psp != NULL)
|
||||
return 1;
|
||||
|
||||
psp = ps_newprocess(ctx, &psm->ps_id);
|
||||
if (psp == NULL)
|
||||
return -1;
|
||||
|
||||
ifp = &psp->psp_ifp;
|
||||
assert(msg->msg_iovlen == 1);
|
||||
assert(iov->iov_len == sizeof(*ifp));
|
||||
memcpy(ifp, iov->iov_base, sizeof(*ifp));
|
||||
ifp->ctx = psp->psp_ctx;
|
||||
ifp->options = NULL;
|
||||
memset(ifp->if_data, 0, sizeof(ifp->if_data));
|
||||
|
||||
memcpy(psp->psp_ifname, ifp->name, sizeof(psp->psp_ifname));
|
||||
|
||||
switch (cmd) {
|
||||
#ifdef ARP
|
||||
case PS_BPF_ARP:
|
||||
psp->psp_proto = ETHERTYPE_ARP;
|
||||
psp->psp_protostr = "ARP";
|
||||
psp->psp_filter = bpf_arp;
|
||||
break;
|
||||
#endif
|
||||
case PS_BPF_BOOTP:
|
||||
psp->psp_proto = ETHERTYPE_IP;
|
||||
psp->psp_protostr = "BOOTP";
|
||||
psp->psp_filter = bpf_bootp;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ia->s_addr == INADDR_ANY)
|
||||
addr = NULL;
|
||||
else
|
||||
addr = inet_ntoa(*ia);
|
||||
snprintf(psp->psp_name, sizeof(psp->psp_name), "BPF %s%s%s",
|
||||
psp->psp_protostr,
|
||||
addr != NULL ? " " : "", addr != NULL ? addr : "");
|
||||
|
||||
start = ps_startprocess(psp, ps_bpf_recvmsg, NULL,
|
||||
ps_bpf_start_bpf, NULL, PSF_DROPPRIVS);
|
||||
switch (start) {
|
||||
case -1:
|
||||
ps_freeprocess(psp);
|
||||
return -1;
|
||||
case 0:
|
||||
ps_entersandbox("stdio", NULL);
|
||||
break;
|
||||
default:
|
||||
logdebugx("%s: spawned %s on PID %d",
|
||||
psp->psp_ifname, psp->psp_name, psp->psp_pid);
|
||||
break;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_bpf_dispatch(struct dhcpcd_ctx *ctx,
|
||||
struct ps_msghdr *psm, struct msghdr *msg)
|
||||
{
|
||||
struct iovec *iov = msg->msg_iov;
|
||||
struct interface *ifp;
|
||||
uint8_t *bpf;
|
||||
size_t bpf_len;
|
||||
|
||||
switch (psm->ps_cmd) {
|
||||
#ifdef ARP
|
||||
case PS_BPF_ARP:
|
||||
#endif
|
||||
case PS_BPF_BOOTP:
|
||||
break;
|
||||
default:
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ifp = if_findindex(ctx->ifaces, psm->ps_id.psi_ifindex);
|
||||
/* interface may have departed .... */
|
||||
if (ifp == NULL)
|
||||
return -1;
|
||||
|
||||
bpf = iov->iov_base;
|
||||
bpf_len = iov->iov_len;
|
||||
|
||||
switch (psm->ps_cmd) {
|
||||
#ifdef ARP
|
||||
case PS_BPF_ARP:
|
||||
arp_packet(ifp, bpf, bpf_len, (unsigned int)psm->ps_flags);
|
||||
break;
|
||||
#endif
|
||||
case PS_BPF_BOOTP:
|
||||
dhcp_packet(ifp, bpf, bpf_len, (unsigned int)psm->ps_flags);
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ps_bpf_send(const struct interface *ifp, const struct in_addr *ia,
|
||||
uint16_t cmd, const void *data, size_t len)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = ifp->ctx;
|
||||
struct ps_msghdr psm = {
|
||||
.ps_cmd = cmd,
|
||||
.ps_id = {
|
||||
.psi_ifindex = ifp->index,
|
||||
.psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),
|
||||
},
|
||||
};
|
||||
|
||||
if (ia != NULL)
|
||||
psm.ps_id.psi_addr.psa_in_addr = *ia;
|
||||
|
||||
return ps_sendpsmdata(ctx, PS_ROOT_FD(ctx), &psm, data, len);
|
||||
}
|
||||
|
||||
#ifdef ARP
|
||||
ssize_t
|
||||
ps_bpf_openarp(const struct interface *ifp, const struct in_addr *ia)
|
||||
{
|
||||
|
||||
assert(ia != NULL);
|
||||
return ps_bpf_send(ifp, ia, PS_BPF_ARP | PS_START,
|
||||
ifp, sizeof(*ifp));
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_bpf_closearp(const struct interface *ifp, const struct in_addr *ia)
|
||||
{
|
||||
|
||||
return ps_bpf_send(ifp, ia, PS_BPF_ARP | PS_STOP, NULL, 0);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_bpf_sendarp(const struct interface *ifp, const struct in_addr *ia,
|
||||
const void *data, size_t len)
|
||||
{
|
||||
|
||||
assert(ia != NULL);
|
||||
return ps_bpf_send(ifp, ia, PS_BPF_ARP, data, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
ssize_t
|
||||
ps_bpf_openbootp(const struct interface *ifp)
|
||||
{
|
||||
|
||||
return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP | PS_START,
|
||||
ifp, sizeof(*ifp));
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_bpf_closebootp(const struct interface *ifp)
|
||||
{
|
||||
|
||||
return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP | PS_STOP, NULL, 0);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_bpf_sendbootp(const struct interface *ifp, const void *data, size_t len)
|
||||
{
|
||||
|
||||
return ps_bpf_send(ifp, NULL, PS_BPF_BOOTP, data, len);
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* Privilege Separation for dhcpcd
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef PRIVSEP_BPF_H
|
||||
#define PRIVSEP_BPF_H
|
||||
|
||||
ssize_t ps_bpf_cmd(struct dhcpcd_ctx *,
|
||||
struct ps_msghdr *, struct msghdr *);
|
||||
ssize_t ps_bpf_dispatch(struct dhcpcd_ctx *,
|
||||
struct ps_msghdr *, struct msghdr *);
|
||||
|
||||
#ifdef ARP
|
||||
ssize_t ps_bpf_openarp(const struct interface *, const struct in_addr *);
|
||||
ssize_t ps_bpf_closearp(const struct interface *, const struct in_addr *);
|
||||
ssize_t ps_bpf_sendarp(const struct interface *, const struct in_addr *,
|
||||
const void *, size_t);
|
||||
#endif
|
||||
|
||||
ssize_t ps_bpf_openbootp(const struct interface *);
|
||||
ssize_t ps_bpf_closebootp(const struct interface *);
|
||||
ssize_t ps_bpf_sendbootp(const struct interface *, const void *, size_t);
|
||||
ssize_t ps_bpf_openbootpudp(const struct interface *);
|
||||
ssize_t ps_bpf_closebootpudp(const struct interface *);
|
||||
ssize_t ps_bpf_sendbootpudp(const struct interface *, const void *, size_t);
|
||||
#endif
|
|
@ -0,0 +1,421 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* Privilege Separation for dhcpcd, BSD driver
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
/* Need these for filtering the ioctls */
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/if_ether.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet6/in6_var.h>
|
||||
#include <netinet6/nd6.h>
|
||||
#ifdef __NetBSD__
|
||||
#include <netinet/if_ether.h>
|
||||
#include <net/if_vlanvar.h> /* Needs netinet/if_ether.h */
|
||||
#elif defined(__DragonFly__)
|
||||
#include <net/vlan/if_vlan_var.h>
|
||||
#else
|
||||
#include <net/if_vlan_var.h>
|
||||
#endif
|
||||
#ifdef __DragonFly__
|
||||
# include <netproto/802_11/ieee80211_ioctl.h>
|
||||
#else
|
||||
# include <net80211/ieee80211.h>
|
||||
# include <net80211/ieee80211_ioctl.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "dhcpcd.h"
|
||||
#include "if.h"
|
||||
#include "logerr.h"
|
||||
#include "privsep.h"
|
||||
|
||||
static ssize_t
|
||||
ps_root_doioctldom(struct dhcpcd_ctx *ctx, int domain, unsigned long req, void *data, size_t len)
|
||||
{
|
||||
#if defined(INET6) || (defined(SIOCALIFADDR) && defined(IFLR_ACTIVE))
|
||||
struct priv *priv = (struct priv *)ctx->priv;
|
||||
#endif
|
||||
int s;
|
||||
|
||||
switch(domain) {
|
||||
#ifdef INET
|
||||
case PF_INET:
|
||||
s = ctx->pf_inet_fd;
|
||||
break;
|
||||
#endif
|
||||
#ifdef INET6
|
||||
case PF_INET6:
|
||||
s = priv->pf_inet6_fd;
|
||||
break;
|
||||
#endif
|
||||
#if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE) /*NetBSD */
|
||||
case PF_LINK:
|
||||
s = priv->pf_link_fd;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
errno = EPFNOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Only allow these ioctls */
|
||||
switch(req) {
|
||||
#ifdef SIOCGIFDATA
|
||||
case SIOCGIFDATA: /* FALLTHROUGH */
|
||||
#endif
|
||||
#ifdef SIOCG80211NWID
|
||||
case SIOCG80211NWID: /* FALLTHROUGH */
|
||||
#endif
|
||||
#ifdef SIOCGETVLAN
|
||||
case SIOCGETVLAN: /* FALLTHROUGH */
|
||||
#endif
|
||||
#ifdef SIOCIFAFATTACH
|
||||
case SIOCIFAFATTACH: /* FALLTHROUGH */
|
||||
#endif
|
||||
#ifdef SIOCSIFXFLAGS
|
||||
case SIOCSIFXFLAGS: /* FALLTHROUGH */
|
||||
#endif
|
||||
#ifdef SIOCSIFINFO_FLAGS
|
||||
case SIOCSIFINFO_FLAGS: /* FALLTHROUGH */
|
||||
#endif
|
||||
#ifdef SIOCSRTRFLUSH_IN6
|
||||
case SIOCSRTRFLUSH_IN6: /* FALLTHROUGH */
|
||||
case SIOCSPFXFLUSH_IN6: /* FALLTHROUGH */
|
||||
#endif
|
||||
#if defined(SIOCALIFADDR) && defined(IFLR_ACTIVE)
|
||||
case SIOCALIFADDR: /* FALLTHROUGH */
|
||||
case SIOCDLIFADDR: /* FALLTHROUGH */
|
||||
#else
|
||||
case SIOCSIFLLADDR: /* FALLTHROUGH */
|
||||
#endif
|
||||
#ifdef SIOCSIFINFO_IN6
|
||||
case SIOCSIFINFO_IN6: /* FALLTHROUGH */
|
||||
#endif
|
||||
case SIOCAIFADDR_IN6: /* FALLTHROUGH */
|
||||
case SIOCDIFADDR_IN6:
|
||||
break;
|
||||
default:
|
||||
errno = EPERM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ioctl(s, req, data, len);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ps_root_doroute(struct dhcpcd_ctx *ctx, void *data, size_t len)
|
||||
{
|
||||
|
||||
return write(ctx->link_fd, data, len);
|
||||
}
|
||||
|
||||
#if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE)
|
||||
static ssize_t
|
||||
ps_root_doindirectioctl(struct dhcpcd_ctx *ctx,
|
||||
unsigned long req, void *data, size_t len)
|
||||
{
|
||||
char *p = data;
|
||||
struct ifreq ifr = { .ifr_flags = 0 };
|
||||
|
||||
/* ioctl filtering is done in ps_root_doioctldom */
|
||||
|
||||
if (len < IFNAMSIZ + 1) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
strlcpy(ifr.ifr_name, p, IFNAMSIZ);
|
||||
len -= IFNAMSIZ;
|
||||
memmove(data, p + IFNAMSIZ, len);
|
||||
ifr.ifr_data = data;
|
||||
|
||||
return ps_root_doioctldom(ctx, PF_INET, req, &ifr, sizeof(ifr));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PLEDGE
|
||||
static ssize_t
|
||||
ps_root_doifignoregroup(struct dhcpcd_ctx *ctx, void *data, size_t len)
|
||||
{
|
||||
|
||||
if (len == 0 || ((const char *)data)[len - 1] != '\0') {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return if_ignoregroup(ctx->pf_inet_fd, data);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CAPSICUM
|
||||
static ssize_t
|
||||
ps_root_dosysctl(unsigned long flags,
|
||||
void *data, size_t len, void **rdata, size_t *rlen)
|
||||
{
|
||||
char *p = data, *e = p + len;
|
||||
int name[10];
|
||||
unsigned int namelen;
|
||||
void *oldp;
|
||||
size_t *oldlenp, oldlen, nlen;
|
||||
void *newp;
|
||||
size_t newlen;
|
||||
int err;
|
||||
|
||||
if (sizeof(namelen) >= len) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
memcpy(&namelen, p, sizeof(namelen));
|
||||
p += sizeof(namelen);
|
||||
nlen = sizeof(*name) * namelen;
|
||||
if (namelen > __arraycount(name)) {
|
||||
errno = ENOBUFS;
|
||||
return -1;
|
||||
}
|
||||
if (p + nlen > e) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
memcpy(name, p, nlen);
|
||||
p += nlen;
|
||||
if (p + sizeof(oldlen) > e) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
memcpy(&oldlen, p, sizeof(oldlen));
|
||||
p += sizeof(oldlen);
|
||||
if (p + sizeof(newlen) > e) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
memcpy(&newlen, p, sizeof(newlen));
|
||||
p += sizeof(newlen);
|
||||
if (p + newlen > e) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
newp = newlen ? p : NULL;
|
||||
|
||||
if (flags & PS_SYSCTL_OLEN) {
|
||||
*rlen = sizeof(oldlen) + oldlen;
|
||||
*rdata = malloc(*rlen);
|
||||
if (*rdata == NULL)
|
||||
return -1;
|
||||
oldlenp = (size_t *)*rdata;
|
||||
*oldlenp = oldlen;
|
||||
if (flags & PS_SYSCTL_ODATA)
|
||||
oldp = (char *)*rdata + sizeof(oldlen);
|
||||
else
|
||||
oldp = NULL;
|
||||
} else {
|
||||
oldlenp = NULL;
|
||||
oldp = NULL;
|
||||
}
|
||||
|
||||
err = sysctl(name, namelen, oldp, oldlenp, newp, newlen);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
ssize_t
|
||||
ps_root_os(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg,
|
||||
void **rdata, size_t *rlen, bool *free_rdata)
|
||||
{
|
||||
struct iovec *iov = msg->msg_iov;
|
||||
void *data = iov->iov_base;
|
||||
size_t len = iov->iov_len;
|
||||
ssize_t err;
|
||||
|
||||
switch (psm->ps_cmd) {
|
||||
case PS_IOCTLLINK:
|
||||
err = ps_root_doioctldom(ctx, PF_LINK, psm->ps_flags, data, len);
|
||||
break;
|
||||
case PS_IOCTL6:
|
||||
err = ps_root_doioctldom(ctx, PF_INET6, psm->ps_flags, data, len);
|
||||
break;
|
||||
case PS_ROUTE:
|
||||
return ps_root_doroute(ctx, data, len);
|
||||
#if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE)
|
||||
case PS_IOCTLINDIRECT:
|
||||
err = ps_root_doindirectioctl(ctx, psm->ps_flags, data, len);
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_PLEDGE
|
||||
case PS_IFIGNOREGRP:
|
||||
return ps_root_doifignoregroup(ctx, data, len);
|
||||
#endif
|
||||
#ifdef HAVE_CAPSICUM
|
||||
case PS_SYSCTL:
|
||||
*free_rdata = true;
|
||||
return ps_root_dosysctl(psm->ps_flags, data, len, rdata, rlen);
|
||||
#else
|
||||
UNUSED(free_rdata);
|
||||
#endif
|
||||
default:
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (err != -1) {
|
||||
*rdata = data;
|
||||
*rlen = len;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ps_root_ioctldom(struct dhcpcd_ctx *ctx, uint16_t domain, unsigned long request,
|
||||
void *data, size_t len)
|
||||
{
|
||||
|
||||
if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), domain,
|
||||
request, data, len) == -1)
|
||||
return -1;
|
||||
return ps_root_readerror(ctx, data, len);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_root_ioctllink(struct dhcpcd_ctx *ctx, unsigned long request,
|
||||
void *data, size_t len)
|
||||
{
|
||||
|
||||
return ps_root_ioctldom(ctx, PS_IOCTLLINK, request, data, len);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_root_ioctl6(struct dhcpcd_ctx *ctx, unsigned long request,
|
||||
void *data, size_t len)
|
||||
{
|
||||
|
||||
return ps_root_ioctldom(ctx, PS_IOCTL6, request, data, len);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_root_route(struct dhcpcd_ctx *ctx, void *data, size_t len)
|
||||
{
|
||||
|
||||
if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_ROUTE, 0, data, len) == -1)
|
||||
return -1;
|
||||
return ps_root_readerror(ctx, data, len);
|
||||
}
|
||||
|
||||
#if defined(HAVE_CAPSICUM) || defined(HAVE_PLEDGE)
|
||||
ssize_t
|
||||
ps_root_indirectioctl(struct dhcpcd_ctx *ctx, unsigned long request,
|
||||
const char *ifname, void *data, size_t len)
|
||||
{
|
||||
char buf[PS_BUFLEN];
|
||||
|
||||
if (IFNAMSIZ + len > sizeof(buf)) {
|
||||
errno = ENOBUFS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
strlcpy(buf, ifname, IFNAMSIZ);
|
||||
memcpy(buf + IFNAMSIZ, data, len);
|
||||
if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_IOCTLINDIRECT,
|
||||
request, buf, IFNAMSIZ + len) == -1)
|
||||
return -1;
|
||||
return ps_root_readerror(ctx, data, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PLEDGE
|
||||
ssize_t
|
||||
ps_root_ifignoregroup(struct dhcpcd_ctx *ctx, const char *ifname)
|
||||
{
|
||||
|
||||
if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_IFIGNOREGRP, 0,
|
||||
ifname, strlen(ifname) + 1) == -1)
|
||||
return -1;
|
||||
return ps_root_readerror(ctx, NULL, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CAPSICUM
|
||||
ssize_t
|
||||
ps_root_sysctl(struct dhcpcd_ctx *ctx,
|
||||
const int *name, unsigned int namelen,
|
||||
void *oldp, size_t *oldlenp, const void *newp, size_t newlen)
|
||||
{
|
||||
char buf[PS_BUFLEN], *p = buf;
|
||||
unsigned long flags = 0;
|
||||
size_t olen = (oldp && oldlenp) ? *oldlenp : 0, nolen;
|
||||
|
||||
if (sizeof(namelen) + (sizeof(*name) * namelen) +
|
||||
sizeof(oldlenp) +
|
||||
sizeof(newlen) + newlen > sizeof(buf))
|
||||
{
|
||||
errno = ENOBUFS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (oldlenp)
|
||||
flags |= PS_SYSCTL_OLEN;
|
||||
if (oldp)
|
||||
flags |= PS_SYSCTL_ODATA;
|
||||
memcpy(p, &namelen, sizeof(namelen));
|
||||
p += sizeof(namelen);
|
||||
memcpy(p, name, sizeof(*name) * namelen);
|
||||
p += sizeof(*name) * namelen;
|
||||
memcpy(p, &olen, sizeof(olen));
|
||||
p += sizeof(olen);
|
||||
memcpy(p, &newlen, sizeof(newlen));
|
||||
p += sizeof(newlen);
|
||||
if (newlen) {
|
||||
memcpy(p, newp, newlen);
|
||||
p += newlen;
|
||||
}
|
||||
|
||||
if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_SYSCTL,
|
||||
flags, buf, (size_t)(p - buf)) == -1)
|
||||
return -1;
|
||||
|
||||
if (ps_root_readerror(ctx, buf, sizeof(buf)) == -1)
|
||||
return -1;
|
||||
|
||||
p = buf;
|
||||
memcpy(&nolen, p, sizeof(nolen));
|
||||
p += sizeof(nolen);
|
||||
if (oldlenp) {
|
||||
*oldlenp = nolen;
|
||||
if (oldp && nolen <= olen)
|
||||
memcpy(oldp, p, nolen);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,299 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* Privilege Separation for dhcpcd, control proxy
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dhcpcd.h"
|
||||
#include "control.h"
|
||||
#include "eloop.h"
|
||||
#include "logerr.h"
|
||||
#include "privsep.h"
|
||||
|
||||
/* We expect to have open 2 SEQPACKET, 2 STREAM and 2 file STREAM fds */
|
||||
|
||||
static int
|
||||
ps_ctl_startcb(struct ps_process *psp)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = psp->psp_ctx;
|
||||
sa_family_t af;
|
||||
|
||||
if (ctx->options & DHCPCD_MANAGER) {
|
||||
setproctitle("[control proxy]");
|
||||
af = AF_UNSPEC;
|
||||
} else {
|
||||
setproctitle("[control proxy] %s%s%s",
|
||||
ctx->ifv[0],
|
||||
ctx->options & DHCPCD_IPV4 ? " [ip4]" : "",
|
||||
ctx->options & DHCPCD_IPV6 ? " [ip6]" : "");
|
||||
if ((ctx->options &
|
||||
(DHCPCD_IPV4 | DHCPCD_IPV6)) == DHCPCD_IPV4)
|
||||
af = AF_INET;
|
||||
else if ((ctx->options &
|
||||
(DHCPCD_IPV4 | DHCPCD_IPV6)) == DHCPCD_IPV6)
|
||||
af = AF_INET6;
|
||||
else
|
||||
af = AF_UNSPEC;
|
||||
}
|
||||
|
||||
return control_start(ctx,
|
||||
ctx->options & DHCPCD_MANAGER ? NULL : *ctx->ifv, af);
|
||||
}
|
||||
|
||||
static void
|
||||
ps_ctl_recvmsg(void *arg, unsigned short events)
|
||||
{
|
||||
struct ps_process *psp = arg;
|
||||
|
||||
if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, NULL, NULL) == -1)
|
||||
logerr(__func__);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_ctl_handleargs(struct fd_list *fd, char *data, size_t len)
|
||||
{
|
||||
|
||||
/* Make any change here in dhcpcd.c as well. */
|
||||
if (strncmp(data, "--version",
|
||||
MIN(strlen("--version"), len)) == 0) {
|
||||
return control_queue(fd, UNCONST(VERSION),
|
||||
strlen(VERSION) + 1);
|
||||
} else if (strncmp(data, "--getconfigfile",
|
||||
MIN(strlen("--getconfigfile"), len)) == 0) {
|
||||
return control_queue(fd, UNCONST(fd->ctx->cffile),
|
||||
strlen(fd->ctx->cffile) + 1);
|
||||
} else if (strncmp(data, "--listen",
|
||||
MIN(strlen("--listen"), len)) == 0) {
|
||||
fd->flags |= FD_LISTEN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fd->ctx->ps_control_client != NULL &&
|
||||
fd->ctx->ps_control_client != fd)
|
||||
{
|
||||
logerrx("%s: cannot handle another client", __func__);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ps_ctl_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = arg;
|
||||
struct iovec *iov = msg->msg_iov;
|
||||
struct fd_list *fd;
|
||||
unsigned int fd_flags = FD_SENDLEN;
|
||||
|
||||
switch (psm->ps_flags) {
|
||||
case PS_CTL_PRIV:
|
||||
break;
|
||||
case PS_CTL_UNPRIV:
|
||||
fd_flags |= FD_UNPRIV;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (psm->ps_cmd) {
|
||||
case PS_CTL:
|
||||
if (msg->msg_iovlen != 1) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (ctx->ps_control_client != NULL) {
|
||||
logerrx("%s: cannot handle another client", __func__);
|
||||
return 0;
|
||||
}
|
||||
fd = control_new(ctx, ctx->ps_ctl->psp_work_fd, fd_flags);
|
||||
if (fd == NULL)
|
||||
return -1;
|
||||
ctx->ps_control_client = fd;
|
||||
control_recvdata(fd, iov->iov_base, iov->iov_len);
|
||||
break;
|
||||
case PS_CTL_EOF:
|
||||
ctx->ps_control_client = NULL;
|
||||
break;
|
||||
default:
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
ps_ctl_dodispatch(void *arg, unsigned short events)
|
||||
{
|
||||
struct ps_process *psp = arg;
|
||||
|
||||
if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events,
|
||||
ps_ctl_dispatch, psp->psp_ctx) == -1)
|
||||
logerr(__func__);
|
||||
}
|
||||
|
||||
static void
|
||||
ps_ctl_recv(void *arg, unsigned short events)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = arg;
|
||||
char buf[BUFSIZ];
|
||||
ssize_t len;
|
||||
|
||||
if (!(events & (ELE_READ | ELE_HANGUP)))
|
||||
logerrx("%s: unexpected event 0x%04x", __func__, events);
|
||||
|
||||
if (events & ELE_READ) {
|
||||
len = read(ctx->ps_ctl->psp_work_fd, buf, sizeof(buf));
|
||||
if (len == -1)
|
||||
logerr("%s: read", __func__);
|
||||
else if (len == 0)
|
||||
// FIXME: Why does this happen?
|
||||
;
|
||||
else if (ctx->ps_control_client == NULL)
|
||||
logerrx("%s: clientfd #%d disconnected (len=%zd)",
|
||||
__func__, ctx->ps_ctl->psp_work_fd, len);
|
||||
else {
|
||||
errno = 0;
|
||||
if (control_queue(ctx->ps_control_client,
|
||||
buf, (size_t)len) == -1)
|
||||
logerr("%s: control_queue", __func__);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ps_ctl_listen(void *arg, unsigned short events)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = arg;
|
||||
char buf[BUFSIZ];
|
||||
ssize_t len;
|
||||
struct fd_list *fd;
|
||||
|
||||
if (!(events & ELE_READ))
|
||||
logerrx("%s: unexpected event 0x%04x", __func__, events);
|
||||
|
||||
len = read(ctx->ps_control->fd, buf, sizeof(buf));
|
||||
if (len == -1) {
|
||||
logerr("%s: read", __func__);
|
||||
eloop_exit(ctx->eloop, EXIT_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Send to our listeners */
|
||||
TAILQ_FOREACH(fd, &ctx->control_fds, next) {
|
||||
if (!(fd->flags & FD_LISTEN))
|
||||
continue;
|
||||
if (control_queue(fd, buf, (size_t)len)== -1)
|
||||
logerr("%s: control_queue", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
pid_t
|
||||
ps_ctl_start(struct dhcpcd_ctx *ctx)
|
||||
{
|
||||
struct ps_id id = {
|
||||
.psi_ifindex = 0,
|
||||
.psi_cmd = PS_CTL,
|
||||
};
|
||||
struct ps_process *psp;
|
||||
int work_fd[2], listen_fd[2];
|
||||
pid_t pid;
|
||||
|
||||
if_closesockets(ctx);
|
||||
|
||||
if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, work_fd) == -1 ||
|
||||
xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, listen_fd) == -1)
|
||||
return -1;
|
||||
#ifdef PRIVSEP_RIGHTS
|
||||
if (ps_rights_limit_fdpair(work_fd) == -1 ||
|
||||
ps_rights_limit_fdpair(listen_fd) == -1)
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
psp = ctx->ps_ctl = ps_newprocess(ctx, &id);
|
||||
strlcpy(psp->psp_name, "control proxy", sizeof(psp->psp_name));
|
||||
pid = ps_startprocess(psp, ps_ctl_recvmsg, ps_ctl_dodispatch,
|
||||
ps_ctl_startcb, NULL, PSF_DROPPRIVS);
|
||||
|
||||
if (pid == -1)
|
||||
return -1;
|
||||
else if (pid != 0) {
|
||||
psp->psp_work_fd = work_fd[0];
|
||||
close(work_fd[1]);
|
||||
close(listen_fd[1]);
|
||||
ctx->ps_control = control_new(ctx,
|
||||
listen_fd[0], FD_SENDLEN | FD_LISTEN);
|
||||
if (ctx->ps_control == NULL)
|
||||
return -1;
|
||||
return pid;
|
||||
}
|
||||
|
||||
close(work_fd[0]);
|
||||
close(listen_fd[0]);
|
||||
|
||||
psp->psp_work_fd = work_fd[1];
|
||||
if (eloop_event_add(ctx->eloop, psp->psp_work_fd, ELE_READ,
|
||||
ps_ctl_recv, ctx) == -1)
|
||||
return -1;
|
||||
|
||||
ctx->ps_control = control_new(ctx, listen_fd[1], 0);
|
||||
if (ctx->ps_control == NULL)
|
||||
return -1;
|
||||
if (eloop_event_add(ctx->eloop, ctx->ps_control->fd, ELE_READ,
|
||||
ps_ctl_listen, ctx) == -1)
|
||||
return -1;
|
||||
|
||||
ps_entersandbox("stdio inet", NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ps_ctl_stop(struct dhcpcd_ctx *ctx)
|
||||
{
|
||||
|
||||
return ps_stopprocess(ctx->ps_ctl);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_ctl_sendargs(struct fd_list *fd, void *data, size_t len)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = fd->ctx;
|
||||
|
||||
if (ctx->ps_control_client != NULL && ctx->ps_control_client != fd)
|
||||
logerrx("%s: cannot deal with another client", __func__);
|
||||
ctx->ps_control_client = fd;
|
||||
return ps_sendcmd(ctx, ctx->ps_ctl->psp_fd, PS_CTL,
|
||||
fd->flags & FD_UNPRIV ? PS_CTL_UNPRIV : PS_CTL_PRIV,
|
||||
data, len);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_ctl_sendeof(struct fd_list *fd)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = fd->ctx;
|
||||
|
||||
return ps_sendcmd(ctx, ctx->ps_ctl->psp_fd, PS_CTL_EOF, 0, NULL, 0);
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* Privilege Separation for dhcpcd
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef PRIVSEP_CTL_H
|
||||
#define PRIVSEP_CTL_H
|
||||
|
||||
#define IN_PRIVSEP_CONTROLLER(ctx) \
|
||||
(IN_PRIVSEP((ctx)) && (ctx)->ps_control_pid == getpid())
|
||||
|
||||
pid_t ps_ctl_start(struct dhcpcd_ctx *);
|
||||
int ps_ctl_stop(struct dhcpcd_ctx *);
|
||||
ssize_t ps_ctl_handleargs(struct fd_list *, char *, size_t);
|
||||
ssize_t ps_ctl_sendargs(struct fd_list *, void *, size_t);
|
||||
ssize_t ps_ctl_sendeof(struct fd_list *fd);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,741 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* Privilege Separation for dhcpcd, network proxy
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/icmp6.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "arp.h"
|
||||
#include "bpf.h"
|
||||
#include "dhcp.h"
|
||||
#include "dhcp6.h"
|
||||
#include "eloop.h"
|
||||
#include "ipv6nd.h"
|
||||
#include "logerr.h"
|
||||
#include "privsep.h"
|
||||
|
||||
/* We expect to have open 2 SEQPACKET, 1 udp, 1 udp6 and 1 raw6 fds */
|
||||
|
||||
#ifdef INET
|
||||
static void
|
||||
ps_inet_recvbootp(void *arg, unsigned short events)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = arg;
|
||||
|
||||
if (ps_recvmsg(ctx->udp_rfd, events,
|
||||
PS_BOOTP, ctx->ps_inet->psp_fd) == -1)
|
||||
logerr(__func__);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef INET6
|
||||
static void
|
||||
ps_inet_recvra(void *arg, unsigned short events)
|
||||
{
|
||||
#ifdef __sun
|
||||
struct interface *ifp = arg;
|
||||
struct rs_state *state = RS_STATE(ifp);
|
||||
struct dhcpcd_ctx *ctx = ifp->ctx;
|
||||
|
||||
if (ps_recvmsg(state->nd_fd, events,
|
||||
PS_ND, ctx->ps_inet->psp_fd) == -1)
|
||||
logerr(__func__);
|
||||
#else
|
||||
struct dhcpcd_ctx *ctx = arg;
|
||||
|
||||
if (ps_recvmsg(ctx->nd_fd, events,
|
||||
PS_ND, ctx->ps_inet->psp_fd) == -1)
|
||||
logerr(__func__);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DHCP6
|
||||
static void
|
||||
ps_inet_recvdhcp6(void *arg, unsigned short events)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = arg;
|
||||
|
||||
if (ps_recvmsg(ctx->dhcp6_rfd, events,
|
||||
PS_DHCP6, ctx->ps_inet->psp_fd) == -1)
|
||||
logerr(__func__);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
ps_inet_canstart(const struct dhcpcd_ctx *ctx)
|
||||
{
|
||||
|
||||
#ifdef INET
|
||||
if ((ctx->options & (DHCPCD_IPV4 | DHCPCD_MANAGER)) ==
|
||||
(DHCPCD_IPV4 | DHCPCD_MANAGER))
|
||||
return true;
|
||||
#endif
|
||||
#if defined(INET6) && !defined(__sun)
|
||||
if (ctx->options & DHCPCD_IPV6)
|
||||
return true;
|
||||
#endif
|
||||
#ifdef DHCP6
|
||||
if ((ctx->options & (DHCPCD_IPV6 | DHCPCD_MANAGER)) ==
|
||||
(DHCPCD_IPV6 | DHCPCD_MANAGER))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int
|
||||
ps_inet_startcb(struct ps_process *psp)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = psp->psp_ctx;
|
||||
int ret = 0;
|
||||
|
||||
if (ctx->options & DHCPCD_MANAGER)
|
||||
setproctitle("[network proxy]");
|
||||
else
|
||||
setproctitle("[network proxy] %s%s%s",
|
||||
ctx->ifc != 0 ? ctx->ifv[0] : "",
|
||||
ctx->options & DHCPCD_IPV4 ? " [ip4]" : "",
|
||||
ctx->options & DHCPCD_IPV6 ? " [ip6]" : "");
|
||||
|
||||
/* This end is the main engine, so it's useless for us. */
|
||||
close(ctx->ps_data_fd);
|
||||
ctx->ps_data_fd = -1;
|
||||
|
||||
errno = 0;
|
||||
|
||||
#ifdef INET
|
||||
if ((ctx->options & (DHCPCD_IPV4 | DHCPCD_MANAGER)) ==
|
||||
(DHCPCD_IPV4 | DHCPCD_MANAGER))
|
||||
{
|
||||
ctx->udp_rfd = dhcp_openudp(NULL);
|
||||
if (ctx->udp_rfd == -1)
|
||||
logerr("%s: dhcp_open", __func__);
|
||||
#ifdef PRIVSEP_RIGHTS
|
||||
else if (ps_rights_limit_fd_rdonly(ctx->udp_rfd) == -1) {
|
||||
logerr("%s: ps_rights_limit_fd_rdonly", __func__);
|
||||
close(ctx->udp_rfd);
|
||||
ctx->udp_rfd = -1;
|
||||
}
|
||||
#endif
|
||||
else if (eloop_event_add(ctx->eloop, ctx->udp_rfd, ELE_READ,
|
||||
ps_inet_recvbootp, ctx) == -1)
|
||||
{
|
||||
logerr("%s: eloop_event_add DHCP", __func__);
|
||||
close(ctx->udp_rfd);
|
||||
ctx->udp_rfd = -1;
|
||||
} else
|
||||
ret++;
|
||||
}
|
||||
#endif
|
||||
#if defined(INET6) && !defined(__sun)
|
||||
if (ctx->options & DHCPCD_IPV6) {
|
||||
ctx->nd_fd = ipv6nd_open(true);
|
||||
if (ctx->nd_fd == -1)
|
||||
logerr("%s: ipv6nd_open", __func__);
|
||||
#ifdef PRIVSEP_RIGHTS
|
||||
else if (ps_rights_limit_fd_rdonly(ctx->nd_fd) == -1) {
|
||||
logerr("%s: ps_rights_limit_fd_rdonly", __func__);
|
||||
close(ctx->nd_fd);
|
||||
ctx->nd_fd = -1;
|
||||
}
|
||||
#endif
|
||||
else if (eloop_event_add(ctx->eloop, ctx->nd_fd, ELE_READ,
|
||||
ps_inet_recvra, ctx) == -1)
|
||||
{
|
||||
logerr("%s: eloop_event_add RA", __func__);
|
||||
close(ctx->nd_fd);
|
||||
ctx->nd_fd = -1;
|
||||
} else
|
||||
ret++;
|
||||
}
|
||||
#endif
|
||||
#ifdef DHCP6
|
||||
if ((ctx->options & (DHCPCD_IPV6 | DHCPCD_MANAGER)) ==
|
||||
(DHCPCD_IPV6 | DHCPCD_MANAGER))
|
||||
{
|
||||
ctx->dhcp6_rfd = dhcp6_openudp(0, NULL);
|
||||
if (ctx->dhcp6_rfd == -1)
|
||||
logerr("%s: dhcp6_open", __func__);
|
||||
#ifdef PRIVSEP_RIGHTS
|
||||
else if (ps_rights_limit_fd_rdonly(ctx->dhcp6_rfd) == -1) {
|
||||
logerr("%s: ps_rights_limit_fd_rdonly", __func__);
|
||||
close(ctx->dhcp6_rfd);
|
||||
ctx->dhcp6_rfd = -1;
|
||||
}
|
||||
#endif
|
||||
else if (eloop_event_add(ctx->eloop, ctx->dhcp6_rfd, ELE_READ,
|
||||
ps_inet_recvdhcp6, ctx) == -1)
|
||||
{
|
||||
logerr("%s: eloop_event_add DHCP6", __func__);
|
||||
close(ctx->dhcp6_rfd);
|
||||
ctx->dhcp6_rfd = -1;
|
||||
} else
|
||||
ret++;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ret == 0 && errno == 0) {
|
||||
errno = ENXIO;
|
||||
return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool
|
||||
ps_inet_validudp(struct msghdr *msg, uint16_t sport, uint16_t dport)
|
||||
{
|
||||
struct udphdr udp;
|
||||
struct iovec *iov = msg->msg_iov;
|
||||
|
||||
if (msg->msg_iovlen == 0 || iov->iov_len < sizeof(udp)) {
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(&udp, iov->iov_base, sizeof(udp));
|
||||
if (udp.uh_sport != htons(sport) || udp.uh_dport != htons(dport)) {
|
||||
errno = EPERM;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef INET6
|
||||
static bool
|
||||
ps_inet_validnd(struct msghdr *msg)
|
||||
{
|
||||
struct icmp6_hdr icmp6;
|
||||
struct iovec *iov = msg->msg_iov;
|
||||
|
||||
if (msg->msg_iovlen == 0 || iov->iov_len < sizeof(icmp6)) {
|
||||
errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(&icmp6, iov->iov_base, sizeof(icmp6));
|
||||
switch(icmp6.icmp6_type) {
|
||||
case ND_ROUTER_SOLICIT:
|
||||
case ND_NEIGHBOR_ADVERT:
|
||||
break;
|
||||
default:
|
||||
errno = EPERM;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static ssize_t
|
||||
ps_inet_sendmsg(struct dhcpcd_ctx *ctx,
|
||||
struct ps_msghdr *psm, struct msghdr *msg)
|
||||
{
|
||||
struct ps_process *psp;
|
||||
int s;
|
||||
|
||||
psp = ps_findprocess(ctx, &psm->ps_id);
|
||||
if (psp != NULL) {
|
||||
s = psp->psp_work_fd;
|
||||
goto dosend;
|
||||
}
|
||||
|
||||
switch (psm->ps_cmd) {
|
||||
#ifdef INET
|
||||
case PS_BOOTP:
|
||||
if (!ps_inet_validudp(msg, BOOTPC, BOOTPS))
|
||||
return -1;
|
||||
s = ctx->udp_wfd;
|
||||
break;
|
||||
#endif
|
||||
#if defined(INET6) && !defined(__sun)
|
||||
case PS_ND:
|
||||
if (!ps_inet_validnd(msg))
|
||||
return -1;
|
||||
s = ctx->nd_fd;
|
||||
break;
|
||||
#endif
|
||||
#ifdef DHCP6
|
||||
case PS_DHCP6:
|
||||
if (!ps_inet_validudp(msg, DHCP6_CLIENT_PORT,DHCP6_SERVER_PORT))
|
||||
return -1;
|
||||
s = ctx->dhcp6_wfd;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
dosend:
|
||||
return sendmsg(s, msg, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
ps_inet_recvmsg(void *arg, unsigned short events)
|
||||
{
|
||||
struct ps_process *psp = arg;
|
||||
|
||||
/* Receive shutdown */
|
||||
if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, NULL, NULL) == -1)
|
||||
logerr(__func__);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_inet_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = arg;
|
||||
|
||||
switch (psm->ps_cmd) {
|
||||
#ifdef INET
|
||||
case PS_BOOTP:
|
||||
dhcp_recvmsg(ctx, msg);
|
||||
break;
|
||||
#endif
|
||||
#ifdef INET6
|
||||
case PS_ND:
|
||||
ipv6nd_recvmsg(ctx, msg);
|
||||
break;
|
||||
#endif
|
||||
#ifdef DHCP6
|
||||
case PS_DHCP6:
|
||||
dhcp6_recvmsg(ctx, msg, NULL);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
ps_inet_dodispatch(void *arg, unsigned short events)
|
||||
{
|
||||
struct ps_process *psp = arg;
|
||||
|
||||
if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events,
|
||||
ps_inet_dispatch, psp->psp_ctx) == -1)
|
||||
logerr(__func__);
|
||||
}
|
||||
|
||||
pid_t
|
||||
ps_inet_start(struct dhcpcd_ctx *ctx)
|
||||
{
|
||||
struct ps_id id = {
|
||||
.psi_ifindex = 0,
|
||||
.psi_cmd = PS_INET,
|
||||
};
|
||||
struct ps_process *psp;
|
||||
pid_t pid;
|
||||
|
||||
psp = ctx->ps_inet = ps_newprocess(ctx, &id);
|
||||
if (psp == NULL)
|
||||
return -1;
|
||||
|
||||
strlcpy(psp->psp_name, "network proxy", sizeof(psp->psp_name));
|
||||
pid = ps_startprocess(psp, ps_inet_recvmsg, ps_inet_dodispatch,
|
||||
ps_inet_startcb, NULL, PSF_DROPPRIVS);
|
||||
|
||||
if (pid == 0)
|
||||
ps_entersandbox("stdio", NULL);
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
int
|
||||
ps_inet_stop(struct dhcpcd_ctx *ctx)
|
||||
{
|
||||
|
||||
return ps_stopprocess(ctx->ps_inet);
|
||||
}
|
||||
|
||||
#ifdef INET
|
||||
static void
|
||||
ps_inet_recvinbootp(void *arg, unsigned short events)
|
||||
{
|
||||
struct ps_process *psp = arg;
|
||||
|
||||
if (ps_recvmsg(psp->psp_work_fd, events,
|
||||
PS_BOOTP, psp->psp_ctx->ps_data_fd) == -1)
|
||||
logerr(__func__);
|
||||
}
|
||||
|
||||
static int
|
||||
ps_inet_listenin(struct ps_process *psp)
|
||||
{
|
||||
struct in_addr *ia = &psp->psp_id.psi_addr.psa_in_addr;
|
||||
char buf[INET_ADDRSTRLEN];
|
||||
|
||||
inet_ntop(AF_INET, ia, buf, sizeof(buf));
|
||||
setproctitle("[%s proxy] %s", psp->psp_protostr, buf);
|
||||
|
||||
psp->psp_work_fd = dhcp_openudp(ia);
|
||||
if (psp->psp_work_fd == -1) {
|
||||
logerr(__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef PRIVSEP_RIGHTS
|
||||
if (ps_rights_limit_fd_rdonly(psp->psp_work_fd) == -1) {
|
||||
logerr("%s: ps_rights_limit_fd_rdonly", __func__);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd, ELE_READ,
|
||||
ps_inet_recvinbootp, psp) == -1)
|
||||
{
|
||||
logerr("%s: eloop_event_add DHCP", __func__);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(INET6) && defined(__sun)
|
||||
static void
|
||||
ps_inet_recvin6nd(void *arg)
|
||||
{
|
||||
struct ps_process *psp = arg;
|
||||
|
||||
if (ps_recvmsg(psp->psp_work_fd,
|
||||
PS_ND, psp->psp_ctx->ps_data_fd) == -1)
|
||||
logerr(__func__);
|
||||
}
|
||||
|
||||
static int
|
||||
ps_inet_listennd(struct ps_process *psp)
|
||||
{
|
||||
|
||||
setproctitle("[ND network proxy]");
|
||||
|
||||
psp->psp_work_fd = ipv6nd_open(&psp->psp_ifp);
|
||||
if (psp->psp_work_fd == -1) {
|
||||
logerr(__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef PRIVSEP_RIGHTS
|
||||
if (ps_rights_limit_fd_rdonly(psp->psp_work_fd) == -1) {
|
||||
logerr("%s: ps_rights_limit_fd_rdonly", __func__);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd,
|
||||
ps_inet_recvin6nd, psp) == -1)
|
||||
{
|
||||
logerr(__func__);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DHCP6
|
||||
static void
|
||||
ps_inet_recvin6dhcp6(void *arg, unsigned short events)
|
||||
{
|
||||
struct ps_process *psp = arg;
|
||||
|
||||
if (ps_recvmsg(psp->psp_work_fd, events,
|
||||
PS_DHCP6, psp->psp_ctx->ps_data_fd) == -1)
|
||||
logerr(__func__);
|
||||
}
|
||||
|
||||
static int
|
||||
ps_inet_listenin6(struct ps_process *psp)
|
||||
{
|
||||
struct in6_addr *ia = &psp->psp_id.psi_addr.psa_in6_addr;
|
||||
char buf[INET6_ADDRSTRLEN];
|
||||
|
||||
inet_ntop(AF_INET6, ia, buf, sizeof(buf));
|
||||
setproctitle("[%s proxy] %s", psp->psp_protostr, buf);
|
||||
|
||||
psp->psp_work_fd = dhcp6_openudp(psp->psp_id.psi_ifindex, ia);
|
||||
if (psp->psp_work_fd == -1) {
|
||||
logerr(__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef PRIVSEP_RIGHTS
|
||||
if (ps_rights_limit_fd_rdonly(psp->psp_work_fd) == -1) {
|
||||
logerr("%s: ps_rights_limit_fd_rdonly", __func__);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (eloop_event_add(psp->psp_ctx->eloop, psp->psp_work_fd, ELE_READ,
|
||||
ps_inet_recvin6dhcp6, psp) == -1)
|
||||
{
|
||||
logerr("%s: eloop_event_add DHCP", __func__);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
ps_inet_recvmsgpsp(void *arg, unsigned short events)
|
||||
{
|
||||
struct ps_process *psp = arg;
|
||||
|
||||
/* Receive shutdown. */
|
||||
if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, NULL, NULL) == -1)
|
||||
logerr(__func__);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_inet_cmd(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
|
||||
{
|
||||
uint16_t cmd;
|
||||
struct ps_process *psp;
|
||||
int (*start_func)(struct ps_process *);
|
||||
pid_t start;
|
||||
struct ps_addr *psa = &psm->ps_id.psi_addr;
|
||||
void *ia;
|
||||
char buf[INET_MAX_ADDRSTRLEN];
|
||||
|
||||
cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
|
||||
if (cmd == psm->ps_cmd)
|
||||
return ps_inet_sendmsg(ctx, psm, msg);
|
||||
|
||||
psp = ps_findprocess(ctx, &psm->ps_id);
|
||||
|
||||
#ifdef PRIVSEP_DEBUG
|
||||
logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp);
|
||||
#endif
|
||||
|
||||
if (psm->ps_cmd & PS_STOP) {
|
||||
assert(psp == NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(psm->ps_cmd & PS_START)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (psp != NULL)
|
||||
return 1;
|
||||
|
||||
psp = ps_newprocess(ctx, &psm->ps_id);
|
||||
if (psp == NULL)
|
||||
return -1;
|
||||
|
||||
|
||||
switch (cmd) {
|
||||
#ifdef INET
|
||||
case PS_BOOTP:
|
||||
start_func = ps_inet_listenin;
|
||||
psp->psp_protostr = "BOOTP";
|
||||
ia = &psa->psa_in_addr;
|
||||
break;
|
||||
#endif
|
||||
#ifdef INET6
|
||||
#ifdef __sun
|
||||
case PS_ND:
|
||||
start_func = ps_inet_listennd;
|
||||
psp->psp_protostr = "ND";
|
||||
ia = &psa->psa_in6_addr;
|
||||
break;
|
||||
#endif
|
||||
#ifdef DHCP6
|
||||
case PS_DHCP6:
|
||||
start_func = ps_inet_listenin6;
|
||||
psp->psp_protostr = "DHCP6";
|
||||
ia = &psa->psa_in6_addr;
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
default:
|
||||
logerrx("%s: unknown command %x", __func__, psm->ps_cmd);
|
||||
errno = ENOTSUP;
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(psp->psp_name, sizeof(psp->psp_name),
|
||||
"%s proxy %s", psp->psp_protostr,
|
||||
inet_ntop(psa->psa_family, ia, buf, sizeof(buf)));
|
||||
start = ps_startprocess(psp, ps_inet_recvmsgpsp, NULL,
|
||||
start_func, NULL, PSF_DROPPRIVS);
|
||||
switch (start) {
|
||||
case -1:
|
||||
ps_freeprocess(psp);
|
||||
return -1;
|
||||
case 0:
|
||||
ps_entersandbox("stdio", NULL);
|
||||
break;
|
||||
default:
|
||||
logdebugx("%s: spawned %s on PID %d",
|
||||
psp->psp_ifname, psp->psp_name, psp->psp_pid);
|
||||
break;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
#ifdef INET
|
||||
static ssize_t
|
||||
ps_inet_in_docmd(struct ipv4_addr *ia, uint16_t cmd, const struct msghdr *msg)
|
||||
{
|
||||
assert(ia != NULL);
|
||||
struct dhcpcd_ctx *ctx = ia->iface->ctx;
|
||||
struct ps_msghdr psm = {
|
||||
.ps_cmd = cmd,
|
||||
.ps_id = {
|
||||
.psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),
|
||||
.psi_ifindex = ia->iface->index,
|
||||
.psi_addr.psa_family = AF_INET,
|
||||
.psi_addr.psa_in_addr = ia->addr,
|
||||
},
|
||||
};
|
||||
|
||||
return ps_sendpsmmsg(ctx, PS_ROOT_FD(ctx), &psm, msg);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_inet_openbootp(struct ipv4_addr *ia)
|
||||
{
|
||||
|
||||
return ps_inet_in_docmd(ia, PS_START | PS_BOOTP, NULL);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_inet_closebootp(struct ipv4_addr *ia)
|
||||
{
|
||||
|
||||
return ps_inet_in_docmd(ia, PS_STOP | PS_BOOTP, NULL);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_inet_sendbootp(struct interface *ifp, const struct msghdr *msg)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = ifp->ctx;
|
||||
|
||||
return ps_sendmsg(ctx, PS_ROOT_FD(ctx), PS_BOOTP, 0, msg);
|
||||
}
|
||||
#endif /* INET */
|
||||
|
||||
#ifdef INET6
|
||||
#ifdef __sun
|
||||
static ssize_t
|
||||
ps_inet_ifp_docmd(struct interface *ifp, uint16_t cmd, const struct msghdr *msg)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = ifp->ctx;
|
||||
struct ps_msghdr psm = {
|
||||
.ps_cmd = cmd,
|
||||
.ps_id = {
|
||||
.psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),
|
||||
.psi_ifindex = ifp->index,
|
||||
.psi_addr.psa_family = AF_INET6,
|
||||
},
|
||||
};
|
||||
|
||||
return ps_sendpsmmsg(ctx, PS_ROOT_FD(ctx), &psm, msg);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_inet_opennd(struct interface *ifp)
|
||||
{
|
||||
|
||||
return ps_inet_ifp_docmd(ifp, PS_ND | PS_START, NULL);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_inet_closend(struct interface *ifp)
|
||||
{
|
||||
|
||||
return ps_inet_ifp_docmd(ifp, PS_ND | PS_STOP, NULL);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_inet_sendnd(struct interface *ifp, const struct msghdr *msg)
|
||||
{
|
||||
|
||||
return ps_inet_ifp_docmd(ifp, PS_ND, msg);
|
||||
}
|
||||
#else
|
||||
ssize_t
|
||||
ps_inet_sendnd(struct interface *ifp, const struct msghdr *msg)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = ifp->ctx;
|
||||
|
||||
return ps_sendmsg(ctx, PS_ROOT_FD(ctx), PS_ND, 0, msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DHCP6
|
||||
static ssize_t
|
||||
ps_inet_in6_docmd(struct ipv6_addr *ia, uint16_t cmd, const struct msghdr *msg)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = ia->iface->ctx;
|
||||
struct ps_msghdr psm = {
|
||||
.ps_cmd = cmd,
|
||||
.ps_id = {
|
||||
.psi_cmd = (uint8_t)(cmd & ~(PS_START | PS_STOP)),
|
||||
.psi_ifindex = ia->iface->index,
|
||||
.psi_addr.psa_family = AF_INET6,
|
||||
.psi_addr.psa_in6_addr = ia->addr,
|
||||
},
|
||||
};
|
||||
|
||||
return ps_sendpsmmsg(ctx, PS_ROOT_FD(ctx), &psm, msg);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_inet_opendhcp6(struct ipv6_addr *ia)
|
||||
{
|
||||
|
||||
return ps_inet_in6_docmd(ia, PS_DHCP6 | PS_START, NULL);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_inet_closedhcp6(struct ipv6_addr *ia)
|
||||
{
|
||||
|
||||
return ps_inet_in6_docmd(ia, PS_DHCP6 | PS_STOP, NULL);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
ps_inet_senddhcp6(struct interface *ifp, const struct msghdr *msg)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = ifp->ctx;
|
||||
|
||||
return ps_sendmsg(ctx, PS_ROOT_FD(ctx), PS_DHCP6, 0, msg);
|
||||
}
|
||||
#endif /* DHCP6 */
|
||||
#endif /* INET6 */
|
|
@ -0,0 +1,58 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* Privilege Separation for dhcpcd
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef PRIVSEP_INET_H
|
||||
#define PRIVSEP_INET_H
|
||||
|
||||
bool ps_inet_canstart(const struct dhcpcd_ctx *);
|
||||
pid_t ps_inet_start(struct dhcpcd_ctx *);
|
||||
int ps_inet_stop(struct dhcpcd_ctx *);
|
||||
ssize_t ps_inet_cmd(struct dhcpcd_ctx *, struct ps_msghdr *, struct msghdr *);
|
||||
ssize_t ps_inet_dispatch(void *, struct ps_msghdr *, struct msghdr *);
|
||||
|
||||
#ifdef INET
|
||||
struct ipv4_addr;
|
||||
ssize_t ps_inet_openbootp(struct ipv4_addr *);
|
||||
ssize_t ps_inet_closebootp(struct ipv4_addr *);
|
||||
ssize_t ps_inet_sendbootp(struct interface *, const struct msghdr *);
|
||||
#endif
|
||||
|
||||
#ifdef INET6
|
||||
struct ipv6_addr;
|
||||
#ifdef __sun
|
||||
ssize_t ps_inet_opennd(struct interface *);
|
||||
ssize_t ps_inet_closend(struct interface *);
|
||||
#endif
|
||||
ssize_t ps_inet_sendnd(struct interface *, const struct msghdr *);
|
||||
#ifdef DHCP6
|
||||
ssize_t ps_inet_opendhcp6(struct ipv6_addr *);
|
||||
ssize_t ps_inet_closedhcp6(struct ipv6_addr *);
|
||||
ssize_t ps_inet_senddhcp6(struct interface *, const struct msghdr *);
|
||||
#endif /* DHCP6 */
|
||||
#endif /* INET6 */
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,81 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* Privilege Separation for dhcpcd
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef PRIVSEP_ROOT_H
|
||||
#define PRIVSEP_ROOT_H
|
||||
|
||||
#include "if.h"
|
||||
|
||||
#if defined(PRIVSEP) && (defined(HAVE_CAPSICUM) || defined(__linux__))
|
||||
#define PRIVSEP_GETIFADDRS
|
||||
#endif
|
||||
|
||||
pid_t ps_root_start(struct dhcpcd_ctx *ctx);
|
||||
void ps_root_close(struct dhcpcd_ctx *ctx);
|
||||
int ps_root_stop(struct dhcpcd_ctx *ctx);
|
||||
void ps_root_signalcb(int, void *);
|
||||
|
||||
ssize_t ps_root_readerror(struct dhcpcd_ctx *, void *, size_t);
|
||||
ssize_t ps_root_mreaderror(struct dhcpcd_ctx *, void **, size_t *);
|
||||
ssize_t ps_root_ioctl(struct dhcpcd_ctx *, ioctl_request_t, void *, size_t);
|
||||
ssize_t ps_root_ip6forwarding(struct dhcpcd_ctx *, const char *);
|
||||
ssize_t ps_root_unlink(struct dhcpcd_ctx *, const char *);
|
||||
ssize_t ps_root_filemtime(struct dhcpcd_ctx *, const char *, time_t *);
|
||||
ssize_t ps_root_readfile(struct dhcpcd_ctx *, const char *, void *, size_t);
|
||||
ssize_t ps_root_writefile(struct dhcpcd_ctx *, const char *, mode_t,
|
||||
const void *, size_t);
|
||||
ssize_t ps_root_logreopen(struct dhcpcd_ctx *);
|
||||
ssize_t ps_root_script(struct dhcpcd_ctx *, const void *, size_t);
|
||||
ssize_t ps_root_stopprocesses(struct dhcpcd_ctx *);
|
||||
int ps_root_getauthrdm(struct dhcpcd_ctx *, uint64_t *);
|
||||
#ifdef PRIVSEP_GETIFADDRS
|
||||
int ps_root_getifaddrs(struct dhcpcd_ctx *, struct ifaddrs **);
|
||||
#endif
|
||||
|
||||
ssize_t ps_root_os(struct dhcpcd_ctx *, struct ps_msghdr *, struct msghdr *,
|
||||
void **, size_t *, bool *);
|
||||
#if defined(BSD) || defined(__sun)
|
||||
ssize_t ps_root_route(struct dhcpcd_ctx *, void *, size_t);
|
||||
ssize_t ps_root_ioctllink(struct dhcpcd_ctx *, unsigned long, void *, size_t);
|
||||
ssize_t ps_root_ioctl6(struct dhcpcd_ctx *, unsigned long, void *, size_t);
|
||||
ssize_t ps_root_indirectioctl(struct dhcpcd_ctx *, unsigned long, const char *,
|
||||
void *, size_t);
|
||||
ssize_t ps_root_ifignoregroup(struct dhcpcd_ctx *, const char *);
|
||||
ssize_t ps_root_sysctl(struct dhcpcd_ctx *, const int *, unsigned int,
|
||||
void *, size_t *, const void *, size_t);
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
ssize_t ps_root_sendnetlink(struct dhcpcd_ctx *, int, struct msghdr *);
|
||||
#endif
|
||||
|
||||
#ifdef PLUGIN_DEV
|
||||
int ps_root_dev_initialised(struct dhcpcd_ctx *, const char *);
|
||||
int ps_root_dev_listening(struct dhcpcd_ctx *);
|
||||
#endif
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,251 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* Privilege Separation for dhcpcd
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef PRIVSEP_H
|
||||
#define PRIVSEP_H
|
||||
|
||||
//#define PRIVSEP_DEBUG
|
||||
|
||||
/* Start flags */
|
||||
#define PSF_DROPPRIVS 0x01
|
||||
#define PSF_ELOOP 0x02
|
||||
|
||||
/* Protocols */
|
||||
#define PS_BOOTP 0x0001
|
||||
#define PS_ND 0x0002
|
||||
#define PS_DHCP6 0x0003
|
||||
#define PS_BPF_BOOTP 0x0004
|
||||
#define PS_BPF_ARP 0x0005
|
||||
|
||||
/* Generic commands */
|
||||
#define PS_IOCTL 0x0010
|
||||
#define PS_ROUTE 0x0011 /* Also used for NETLINK */
|
||||
#define PS_SCRIPT 0x0012
|
||||
#define PS_UNLINK 0x0013
|
||||
#define PS_READFILE 0x0014
|
||||
#define PS_WRITEFILE 0x0015
|
||||
#define PS_FILEMTIME 0x0016
|
||||
#define PS_AUTH_MONORDM 0x0017
|
||||
#define PS_CTL 0x0018
|
||||
#define PS_CTL_EOF 0x0019
|
||||
#define PS_LOGREOPEN 0x0020
|
||||
#define PS_STOPPROCS 0x0021
|
||||
#define PS_DAEMONISED 0x0022
|
||||
|
||||
/* Domains */
|
||||
#define PS_ROOT 0x0101
|
||||
#define PS_INET 0x0102
|
||||
#define PS_CONTROL 0x0103
|
||||
|
||||
/* BSD Commands */
|
||||
#define PS_IOCTLLINK 0x0201
|
||||
#define PS_IOCTL6 0x0202
|
||||
#define PS_IOCTLINDIRECT 0x0203
|
||||
#define PS_IP6FORWARDING 0x0204
|
||||
#define PS_GETIFADDRS 0x0205
|
||||
#define PS_IFIGNOREGRP 0x0206
|
||||
#define PS_SYSCTL 0x0207
|
||||
|
||||
/* Dev Commands */
|
||||
#define PS_DEV_LISTENING 0x1001
|
||||
#define PS_DEV_INITTED 0x1002
|
||||
#define PS_DEV_IFCMD 0x1003
|
||||
|
||||
/* Dev Interface Commands (via flags) */
|
||||
#define PS_DEV_IFADDED 0x0001
|
||||
#define PS_DEV_IFREMOVED 0x0002
|
||||
#define PS_DEV_IFUPDATED 0x0003
|
||||
|
||||
/* Control Type (via flags) */
|
||||
#define PS_CTL_PRIV 0x0004
|
||||
#define PS_CTL_UNPRIV 0x0005
|
||||
|
||||
/* Sysctl Needs (via flags) */
|
||||
#define PS_SYSCTL_OLEN 0x0001
|
||||
#define PS_SYSCTL_ODATA 0x0002
|
||||
|
||||
/* Process commands */
|
||||
#define PS_START 0x4000
|
||||
#define PS_STOP 0x8000
|
||||
|
||||
/* Max INET message size + meta data for IPC */
|
||||
#define PS_BUFLEN ((64 * 1024) + \
|
||||
sizeof(struct ps_msghdr) + \
|
||||
sizeof(struct msghdr) + \
|
||||
CMSG_SPACE(sizeof(struct in6_pktinfo) + \
|
||||
sizeof(int)))
|
||||
|
||||
#define PSP_NAMESIZE 16 + INET_MAX_ADDRSTRLEN
|
||||
|
||||
/* Handy macro to work out if in the privsep engine or not. */
|
||||
#define IN_PRIVSEP(ctx) \
|
||||
((ctx)->options & DHCPCD_PRIVSEP)
|
||||
#define IN_PRIVSEP_SE(ctx) \
|
||||
(((ctx)->options & (DHCPCD_PRIVSEP | DHCPCD_FORKED)) == DHCPCD_PRIVSEP)
|
||||
|
||||
#define PS_PROCESS_TIMEOUT 5 /* seconds to stop all processes */
|
||||
|
||||
#if defined(PRIVSEP) && defined(HAVE_CAPSICUM)
|
||||
#define PRIVSEP_RIGHTS
|
||||
#endif
|
||||
|
||||
#define PS_ROOT_FD(ctx) ((ctx)->ps_root ? (ctx)->ps_root->psp_fd : -1)
|
||||
|
||||
#if !defined(DISABLE_SECCOMP) && defined(__linux__)
|
||||
# include <linux/version.h>
|
||||
# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
|
||||
# define HAVE_SECCOMP
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#include "arp.h"
|
||||
#include "dhcp.h"
|
||||
#include "dhcpcd.h"
|
||||
|
||||
struct ps_addr {
|
||||
sa_family_t psa_family;
|
||||
uint8_t psa_pad[4 - sizeof(sa_family_t)];
|
||||
union {
|
||||
struct in_addr psau_in_addr;
|
||||
struct in6_addr psau_in6_addr;
|
||||
} psa_u;
|
||||
#define psa_in_addr psa_u.psau_in_addr
|
||||
#define psa_in6_addr psa_u.psau_in6_addr
|
||||
};
|
||||
|
||||
/* Uniquely identify a process */
|
||||
struct ps_id {
|
||||
struct ps_addr psi_addr;
|
||||
unsigned int psi_ifindex;
|
||||
uint16_t psi_cmd;
|
||||
uint8_t psi_pad[2];
|
||||
};
|
||||
|
||||
struct ps_msghdr {
|
||||
uint16_t ps_cmd;
|
||||
uint8_t ps_pad[sizeof(unsigned long) - sizeof(uint16_t)];
|
||||
unsigned long ps_flags;
|
||||
struct ps_id ps_id;
|
||||
socklen_t ps_namelen;
|
||||
socklen_t ps_controllen;
|
||||
uint8_t ps_pad2[sizeof(size_t) - sizeof(socklen_t)];
|
||||
size_t ps_datalen;
|
||||
};
|
||||
|
||||
struct ps_msg {
|
||||
struct ps_msghdr psm_hdr;
|
||||
uint8_t psm_data[PS_BUFLEN];
|
||||
};
|
||||
|
||||
struct bpf;
|
||||
|
||||
struct ps_process {
|
||||
TAILQ_ENTRY(ps_process) next;
|
||||
struct dhcpcd_ctx *psp_ctx;
|
||||
struct ps_id psp_id;
|
||||
pid_t psp_pid;
|
||||
int psp_fd;
|
||||
int psp_work_fd;
|
||||
unsigned int psp_ifindex;
|
||||
char psp_ifname[IF_NAMESIZE];
|
||||
char psp_name[PSP_NAMESIZE];
|
||||
uint16_t psp_proto;
|
||||
const char *psp_protostr;
|
||||
bool psp_started;
|
||||
|
||||
#ifdef INET
|
||||
int (*psp_filter)(const struct bpf *, const struct in_addr *);
|
||||
struct interface psp_ifp; /* Move BPF gubbins elsewhere */
|
||||
struct bpf *psp_bpf;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CAPSICUM
|
||||
int psp_pfd;
|
||||
#endif
|
||||
};
|
||||
TAILQ_HEAD(ps_process_head, ps_process);
|
||||
|
||||
#include "privsep-control.h"
|
||||
#include "privsep-inet.h"
|
||||
#include "privsep-root.h"
|
||||
#ifdef INET
|
||||
#include "privsep-bpf.h"
|
||||
#endif
|
||||
|
||||
int ps_init(struct dhcpcd_ctx *);
|
||||
int ps_start(struct dhcpcd_ctx *);
|
||||
int ps_stop(struct dhcpcd_ctx *);
|
||||
int ps_stopwait(struct dhcpcd_ctx *);
|
||||
int ps_entersandbox(const char *, const char **);
|
||||
int ps_managersandbox(struct dhcpcd_ctx *, const char *);
|
||||
ssize_t ps_daemonised(struct dhcpcd_ctx *);
|
||||
|
||||
int ps_unrollmsg(struct msghdr *, struct ps_msghdr *, const void *, size_t);
|
||||
ssize_t ps_sendpsmmsg(struct dhcpcd_ctx *, int,
|
||||
struct ps_msghdr *, const struct msghdr *);
|
||||
ssize_t ps_sendpsmdata(struct dhcpcd_ctx *, int,
|
||||
struct ps_msghdr *, const void *, size_t);
|
||||
ssize_t ps_sendmsg(struct dhcpcd_ctx *, int, uint16_t, unsigned long,
|
||||
const struct msghdr *);
|
||||
ssize_t ps_sendcmd(struct dhcpcd_ctx *, int, uint16_t, unsigned long,
|
||||
const void *data, size_t len);
|
||||
ssize_t ps_recvmsg(int, unsigned short, uint16_t, int);
|
||||
ssize_t ps_recvpsmsg(struct dhcpcd_ctx *, int, unsigned short,
|
||||
ssize_t (*callback)(void *, struct ps_msghdr *, struct msghdr *), void *);
|
||||
|
||||
/* Internal privsep functions. */
|
||||
int ps_setbuf_fdpair(int []);
|
||||
|
||||
#ifdef PRIVSEP_RIGHTS
|
||||
int ps_rights_limit_ioctl(int);
|
||||
int ps_rights_limit_fd_fctnl(int);
|
||||
int ps_rights_limit_fd_rdonly(int);
|
||||
int ps_rights_limit_fd_sockopt(int);
|
||||
int ps_rights_limit_fd(int);
|
||||
int ps_rights_limit_fdpair(int []);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SECCOMP
|
||||
int ps_seccomp_enter(void);
|
||||
#endif
|
||||
|
||||
pid_t ps_startprocess(struct ps_process *,
|
||||
void (*recv_msg)(void *, unsigned short),
|
||||
void (*recv_unpriv_msg)(void *, unsigned short),
|
||||
int (*callback)(struct ps_process *), void (*)(int, void *),
|
||||
unsigned int);
|
||||
int ps_stopprocess(struct ps_process *);
|
||||
struct ps_process *ps_findprocess(struct dhcpcd_ctx *, struct ps_id *);
|
||||
struct ps_process *ps_findprocesspid(struct dhcpcd_ctx *, pid_t);
|
||||
struct ps_process *ps_newprocess(struct dhcpcd_ctx *, struct ps_id *);
|
||||
bool ps_waitforprocs(struct dhcpcd_ctx *ctx);
|
||||
void ps_process_timeout(void *);
|
||||
void ps_freeprocess(struct ps_process *);
|
||||
void ps_freeprocesses(struct dhcpcd_ctx *, struct ps_process *);
|
||||
#endif
|
|
@ -0,0 +1,800 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - route management
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "common.h"
|
||||
#include "dhcpcd.h"
|
||||
#include "if.h"
|
||||
#include "if-options.h"
|
||||
#include "ipv4.h"
|
||||
#include "ipv4ll.h"
|
||||
#include "ipv6.h"
|
||||
#include "logerr.h"
|
||||
#include "route.h"
|
||||
#include "sa.h"
|
||||
|
||||
/* Needed for NetBSD-6, 7 and 8. */
|
||||
#ifndef RB_TREE_FOREACH_SAFE
|
||||
#ifndef RB_TREE_PREV
|
||||
#define RB_TREE_NEXT(T, N) rb_tree_iterate((T), (N), RB_DIR_RIGHT)
|
||||
#define RB_TREE_PREV(T, N) rb_tree_iterate((T), (N), RB_DIR_LEFT)
|
||||
#endif
|
||||
#define RB_TREE_FOREACH_SAFE(N, T, S) \
|
||||
for ((N) = RB_TREE_MIN(T); \
|
||||
(N) && ((S) = RB_TREE_NEXT((T), (N)), 1); \
|
||||
(N) = (S))
|
||||
#define RB_TREE_FOREACH_REVERSE_SAFE(N, T, S) \
|
||||
for ((N) = RB_TREE_MAX(T); \
|
||||
(N) && ((S) = RB_TREE_PREV((T), (N)), 1); \
|
||||
(N) = (S))
|
||||
#endif
|
||||
|
||||
#ifdef RT_FREE_ROUTE_TABLE_STATS
|
||||
static size_t croutes;
|
||||
static size_t nroutes;
|
||||
static size_t froutes;
|
||||
static size_t mroutes;
|
||||
#endif
|
||||
|
||||
static void
|
||||
rt_maskedaddr(struct sockaddr *dst,
|
||||
const struct sockaddr *addr, const struct sockaddr *netmask)
|
||||
{
|
||||
const char *addrp = addr->sa_data, *netmaskp = netmask->sa_data;
|
||||
char *dstp = dst->sa_data;
|
||||
const char *addre = (char *)dst + sa_len(addr);
|
||||
const char *netmaske = (char *)dst + MIN(sa_len(addr), sa_len(netmask));
|
||||
|
||||
dst->sa_family = addr->sa_family;
|
||||
#ifdef HAVE_SA_LEN
|
||||
dst->sa_len = addr->sa_len;
|
||||
#endif
|
||||
|
||||
if (sa_is_unspecified(netmask)) {
|
||||
if (addre > dstp)
|
||||
memcpy(dstp, addrp, (size_t)(addre - dstp));
|
||||
return;
|
||||
}
|
||||
|
||||
while (dstp < netmaske)
|
||||
*dstp++ = *addrp++ & *netmaskp++;
|
||||
if (dstp < addre)
|
||||
memset(dstp, 0, (size_t)(addre - dstp));
|
||||
}
|
||||
|
||||
/*
|
||||
* On some systems, host routes have no need for a netmask.
|
||||
* However DHCP specifies host routes using an all-ones netmask.
|
||||
* This handy function allows easy comparison when the two
|
||||
* differ.
|
||||
*/
|
||||
static int
|
||||
rt_cmp_netmask(const struct rt *rt1, const struct rt *rt2)
|
||||
{
|
||||
|
||||
if (rt1->rt_flags & RTF_HOST && rt2->rt_flags & RTF_HOST)
|
||||
return 0;
|
||||
return sa_cmp(&rt1->rt_netmask, &rt2->rt_netmask);
|
||||
}
|
||||
|
||||
int
|
||||
rt_cmp_dest(const struct rt *rt1, const struct rt *rt2)
|
||||
{
|
||||
union sa_ss ma1 = { .sa.sa_family = AF_UNSPEC };
|
||||
union sa_ss ma2 = { .sa.sa_family = AF_UNSPEC };
|
||||
int c;
|
||||
|
||||
rt_maskedaddr(&ma1.sa, &rt1->rt_dest, &rt1->rt_netmask);
|
||||
rt_maskedaddr(&ma2.sa, &rt2->rt_dest, &rt2->rt_netmask);
|
||||
c = sa_cmp(&ma1.sa, &ma2.sa);
|
||||
if (c != 0)
|
||||
return c;
|
||||
|
||||
return rt_cmp_netmask(rt1, rt2);
|
||||
}
|
||||
|
||||
static int
|
||||
rt_compare_os(__unused void *context, const void *node1, const void *node2)
|
||||
{
|
||||
const struct rt *rt1 = node1, *rt2 = node2;
|
||||
int c;
|
||||
|
||||
/* Sort by masked destination. */
|
||||
c = rt_cmp_dest(rt1, rt2);
|
||||
if (c != 0)
|
||||
return c;
|
||||
|
||||
#ifdef HAVE_ROUTE_METRIC
|
||||
c = (int)(rt1->rt_ifp->metric - rt2->rt_ifp->metric);
|
||||
#endif
|
||||
return c;
|
||||
}
|
||||
|
||||
static int
|
||||
rt_compare_list(__unused void *context, const void *node1, const void *node2)
|
||||
{
|
||||
const struct rt *rt1 = node1, *rt2 = node2;
|
||||
|
||||
if (rt1->rt_order > rt2->rt_order)
|
||||
return 1;
|
||||
if (rt1->rt_order < rt2->rt_order)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
rt_compare_proto(void *context, const void *node1, const void *node2)
|
||||
{
|
||||
const struct rt *rt1 = node1, *rt2 = node2;
|
||||
int c;
|
||||
struct interface *ifp1, *ifp2;
|
||||
|
||||
assert(rt1->rt_ifp != NULL);
|
||||
assert(rt2->rt_ifp != NULL);
|
||||
ifp1 = rt1->rt_ifp;
|
||||
ifp2 = rt2->rt_ifp;
|
||||
|
||||
/* Prefer interfaces with a carrier. */
|
||||
c = ifp1->carrier - ifp2->carrier;
|
||||
if (c != 0)
|
||||
return -c;
|
||||
|
||||
/* Prefer roaming over non roaming if both carriers are down. */
|
||||
if (ifp1->carrier == LINK_DOWN && ifp2->carrier == LINK_DOWN) {
|
||||
bool roam1 = if_roaming(ifp1);
|
||||
bool roam2 = if_roaming(ifp2);
|
||||
|
||||
if (roam1 != roam2)
|
||||
return roam1 ? 1 : -1;
|
||||
}
|
||||
|
||||
#ifdef INET
|
||||
/* IPv4LL routes always come last */
|
||||
if (rt1->rt_dflags & RTDF_IPV4LL && !(rt2->rt_dflags & RTDF_IPV4LL))
|
||||
return -1;
|
||||
else if (!(rt1->rt_dflags & RTDF_IPV4LL) && rt2->rt_dflags & RTDF_IPV4LL)
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
/* Lower metric interfaces come first. */
|
||||
c = (int)(ifp1->metric - ifp2->metric);
|
||||
if (c != 0)
|
||||
return c;
|
||||
|
||||
/* Finally the order in which the route was given to us. */
|
||||
return rt_compare_list(context, rt1, rt2);
|
||||
}
|
||||
|
||||
static const rb_tree_ops_t rt_compare_os_ops = {
|
||||
.rbto_compare_nodes = rt_compare_os,
|
||||
.rbto_compare_key = rt_compare_os,
|
||||
.rbto_node_offset = offsetof(struct rt, rt_tree),
|
||||
.rbto_context = NULL
|
||||
};
|
||||
|
||||
const rb_tree_ops_t rt_compare_list_ops = {
|
||||
.rbto_compare_nodes = rt_compare_list,
|
||||
.rbto_compare_key = rt_compare_list,
|
||||
.rbto_node_offset = offsetof(struct rt, rt_tree),
|
||||
.rbto_context = NULL
|
||||
};
|
||||
|
||||
const rb_tree_ops_t rt_compare_proto_ops = {
|
||||
.rbto_compare_nodes = rt_compare_proto,
|
||||
.rbto_compare_key = rt_compare_proto,
|
||||
.rbto_node_offset = offsetof(struct rt, rt_tree),
|
||||
.rbto_context = NULL
|
||||
};
|
||||
|
||||
#ifdef RT_FREE_ROUTE_TABLE
|
||||
static int
|
||||
rt_compare_free(__unused void *context, const void *node1, const void *node2)
|
||||
{
|
||||
|
||||
return node1 == node2 ? 0 : node1 < node2 ? -1 : 1;
|
||||
}
|
||||
|
||||
static const rb_tree_ops_t rt_compare_free_ops = {
|
||||
.rbto_compare_nodes = rt_compare_free,
|
||||
.rbto_compare_key = rt_compare_free,
|
||||
.rbto_node_offset = offsetof(struct rt, rt_tree),
|
||||
.rbto_context = NULL
|
||||
};
|
||||
#endif
|
||||
|
||||
void
|
||||
rt_init(struct dhcpcd_ctx *ctx)
|
||||
{
|
||||
|
||||
rb_tree_init(&ctx->routes, &rt_compare_os_ops);
|
||||
#ifdef RT_FREE_ROUTE_TABLE
|
||||
rb_tree_init(&ctx->froutes, &rt_compare_free_ops);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
rt_is_default(const struct rt *rt)
|
||||
{
|
||||
|
||||
return sa_is_unspecified(&rt->rt_dest) &&
|
||||
sa_is_unspecified(&rt->rt_netmask);
|
||||
}
|
||||
|
||||
static void
|
||||
rt_desc(const char *cmd, const struct rt *rt)
|
||||
{
|
||||
char dest[INET_MAX_ADDRSTRLEN], gateway[INET_MAX_ADDRSTRLEN];
|
||||
int prefix;
|
||||
const char *ifname;
|
||||
bool gateway_unspec;
|
||||
|
||||
assert(cmd != NULL);
|
||||
assert(rt != NULL);
|
||||
|
||||
sa_addrtop(&rt->rt_dest, dest, sizeof(dest));
|
||||
prefix = sa_toprefix(&rt->rt_netmask);
|
||||
sa_addrtop(&rt->rt_gateway, gateway, sizeof(gateway));
|
||||
gateway_unspec = sa_is_unspecified(&rt->rt_gateway);
|
||||
ifname = rt->rt_ifp == NULL ? "(null)" : rt->rt_ifp->name;
|
||||
|
||||
if (rt->rt_flags & RTF_HOST) {
|
||||
if (gateway_unspec)
|
||||
loginfox("%s: %s host route to %s",
|
||||
ifname, cmd, dest);
|
||||
else
|
||||
loginfox("%s: %s host route to %s via %s",
|
||||
ifname, cmd, dest, gateway);
|
||||
} else if (rt_is_default(rt)) {
|
||||
if (gateway_unspec)
|
||||
loginfox("%s: %s default route",
|
||||
ifname, cmd);
|
||||
else
|
||||
loginfox("%s: %s default route via %s",
|
||||
ifname, cmd, gateway);
|
||||
} else if (gateway_unspec)
|
||||
loginfox("%s: %s%s route to %s/%d",
|
||||
ifname, cmd,
|
||||
rt->rt_flags & RTF_REJECT ? " reject" : "",
|
||||
dest, prefix);
|
||||
else
|
||||
loginfox("%s: %s%s route to %s/%d via %s",
|
||||
ifname, cmd,
|
||||
rt->rt_flags & RTF_REJECT ? " reject" : "",
|
||||
dest, prefix, gateway);
|
||||
}
|
||||
|
||||
void
|
||||
rt_headclear0(struct dhcpcd_ctx *ctx, rb_tree_t *rts, int af)
|
||||
{
|
||||
struct rt *rt, *rtn;
|
||||
|
||||
if (rts == NULL)
|
||||
return;
|
||||
assert(ctx != NULL);
|
||||
#ifdef RT_FREE_ROUTE_TABLE
|
||||
assert(&ctx->froutes != rts);
|
||||
#endif
|
||||
|
||||
RB_TREE_FOREACH_SAFE(rt, rts, rtn) {
|
||||
if (af != AF_UNSPEC &&
|
||||
rt->rt_dest.sa_family != af &&
|
||||
rt->rt_gateway.sa_family != af)
|
||||
continue;
|
||||
rb_tree_remove_node(rts, rt);
|
||||
rt_free(rt);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rt_headclear(rb_tree_t *rts, int af)
|
||||
{
|
||||
struct rt *rt;
|
||||
|
||||
if (rts == NULL || (rt = RB_TREE_MIN(rts)) == NULL)
|
||||
return;
|
||||
rt_headclear0(rt->rt_ifp->ctx, rts, af);
|
||||
}
|
||||
|
||||
static void
|
||||
rt_headfree(rb_tree_t *rts)
|
||||
{
|
||||
struct rt *rt;
|
||||
|
||||
while ((rt = RB_TREE_MIN(rts)) != NULL) {
|
||||
rb_tree_remove_node(rts, rt);
|
||||
free(rt);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rt_dispose(struct dhcpcd_ctx *ctx)
|
||||
{
|
||||
|
||||
assert(ctx != NULL);
|
||||
rt_headfree(&ctx->routes);
|
||||
#ifdef RT_FREE_ROUTE_TABLE
|
||||
rt_headfree(&ctx->froutes);
|
||||
#ifdef RT_FREE_ROUTE_TABLE_STATS
|
||||
logdebugx("free route list used %zu times", froutes);
|
||||
logdebugx("new routes from route free list %zu", nroutes);
|
||||
logdebugx("maximum route free list size %zu", mroutes);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
struct rt *
|
||||
rt_new0(struct dhcpcd_ctx *ctx)
|
||||
{
|
||||
struct rt *rt;
|
||||
|
||||
assert(ctx != NULL);
|
||||
#ifdef RT_FREE_ROUTE_TABLE
|
||||
if ((rt = RB_TREE_MIN(&ctx->froutes)) != NULL) {
|
||||
rb_tree_remove_node(&ctx->froutes, rt);
|
||||
#ifdef RT_FREE_ROUTE_TABLE_STATS
|
||||
croutes--;
|
||||
nroutes++;
|
||||
#endif
|
||||
} else
|
||||
#endif
|
||||
if ((rt = malloc(sizeof(*rt))) == NULL) {
|
||||
logerr(__func__);
|
||||
return NULL;
|
||||
}
|
||||
memset(rt, 0, sizeof(*rt));
|
||||
return rt;
|
||||
}
|
||||
|
||||
void
|
||||
rt_setif(struct rt *rt, struct interface *ifp)
|
||||
{
|
||||
|
||||
assert(rt != NULL);
|
||||
assert(ifp != NULL);
|
||||
rt->rt_ifp = ifp;
|
||||
#ifdef HAVE_ROUTE_METRIC
|
||||
rt->rt_metric = ifp->metric;
|
||||
if (if_roaming(ifp))
|
||||
rt->rt_metric += RTMETRIC_ROAM;
|
||||
#endif
|
||||
}
|
||||
|
||||
struct rt *
|
||||
rt_new(struct interface *ifp)
|
||||
{
|
||||
struct rt *rt;
|
||||
|
||||
assert(ifp != NULL);
|
||||
if ((rt = rt_new0(ifp->ctx)) == NULL)
|
||||
return NULL;
|
||||
rt_setif(rt, ifp);
|
||||
return rt;
|
||||
}
|
||||
|
||||
struct rt *
|
||||
rt_proto_add_ctx(rb_tree_t *tree, struct rt *rt, struct dhcpcd_ctx *ctx)
|
||||
{
|
||||
|
||||
rt->rt_order = ctx->rt_order++;
|
||||
if (rb_tree_insert_node(tree, rt) == rt)
|
||||
return rt;
|
||||
|
||||
rt_free(rt);
|
||||
errno = EEXIST;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct rt *
|
||||
rt_proto_add(rb_tree_t *tree, struct rt *rt)
|
||||
{
|
||||
|
||||
assert (rt->rt_ifp != NULL);
|
||||
return rt_proto_add_ctx(tree, rt, rt->rt_ifp->ctx);
|
||||
}
|
||||
|
||||
void
|
||||
rt_free(struct rt *rt)
|
||||
{
|
||||
#ifdef RT_FREE_ROUTE_TABLE
|
||||
struct dhcpcd_ctx *ctx;
|
||||
|
||||
assert(rt != NULL);
|
||||
if (rt->rt_ifp == NULL) {
|
||||
free(rt);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx = rt->rt_ifp->ctx;
|
||||
rb_tree_insert_node(&ctx->froutes, rt);
|
||||
#ifdef RT_FREE_ROUTE_TABLE_STATS
|
||||
croutes++;
|
||||
froutes++;
|
||||
if (croutes > mroutes)
|
||||
mroutes = croutes;
|
||||
#endif
|
||||
#else
|
||||
free(rt);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
rt_freeif(struct interface *ifp)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx;
|
||||
struct rt *rt, *rtn;
|
||||
|
||||
if (ifp == NULL)
|
||||
return;
|
||||
ctx = ifp->ctx;
|
||||
RB_TREE_FOREACH_SAFE(rt, &ctx->routes, rtn) {
|
||||
if (rt->rt_ifp == ifp) {
|
||||
rb_tree_remove_node(&ctx->routes, rt);
|
||||
rt_free(rt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If something other than dhcpcd removes a route,
|
||||
* we need to remove it from our internal table. */
|
||||
void
|
||||
rt_recvrt(int cmd, const struct rt *rt, pid_t pid)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx;
|
||||
struct rt *f;
|
||||
|
||||
assert(rt != NULL);
|
||||
assert(rt->rt_ifp != NULL);
|
||||
assert(rt->rt_ifp->ctx != NULL);
|
||||
|
||||
ctx = rt->rt_ifp->ctx;
|
||||
|
||||
switch(cmd) {
|
||||
case RTM_DELETE:
|
||||
f = rb_tree_find_node(&ctx->routes, rt);
|
||||
if (f != NULL) {
|
||||
char buf[32];
|
||||
|
||||
rb_tree_remove_node(&ctx->routes, f);
|
||||
snprintf(buf, sizeof(buf), "pid %d deleted", pid);
|
||||
rt_desc(buf, f);
|
||||
rt_free(f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
#if defined(IPV4LL) && defined(HAVE_ROUTE_METRIC)
|
||||
if (rt->rt_dest.sa_family == AF_INET)
|
||||
ipv4ll_recvrt(cmd, rt);
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool
|
||||
rt_add(rb_tree_t *kroutes, struct rt *nrt, struct rt *ort)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx;
|
||||
bool change, kroute, result;
|
||||
|
||||
assert(nrt != NULL);
|
||||
ctx = nrt->rt_ifp->ctx;
|
||||
|
||||
/*
|
||||
* Don't install a gateway if not asked to.
|
||||
* This option is mainly for VPN users who want their VPN to be the
|
||||
* default route.
|
||||
* Because VPN's generally don't care about route management
|
||||
* beyond their own, a longer term solution would be to remove this
|
||||
* and get the VPN to inject the default route into dhcpcd somehow.
|
||||
*/
|
||||
if (((nrt->rt_ifp->active &&
|
||||
!(nrt->rt_ifp->options->options & DHCPCD_GATEWAY)) ||
|
||||
(!nrt->rt_ifp->active && !(ctx->options & DHCPCD_GATEWAY))) &&
|
||||
sa_is_unspecified(&nrt->rt_dest) &&
|
||||
sa_is_unspecified(&nrt->rt_netmask))
|
||||
return false;
|
||||
|
||||
rt_desc(ort == NULL ? "adding" : "changing", nrt);
|
||||
|
||||
change = kroute = result = false;
|
||||
if (ort == NULL) {
|
||||
ort = rb_tree_find_node(kroutes, nrt);
|
||||
if (ort != NULL &&
|
||||
((ort->rt_flags & RTF_REJECT &&
|
||||
nrt->rt_flags & RTF_REJECT) ||
|
||||
(ort->rt_ifp == nrt->rt_ifp &&
|
||||
#ifdef HAVE_ROUTE_METRIC
|
||||
ort->rt_metric == nrt->rt_metric &&
|
||||
#endif
|
||||
sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0)))
|
||||
{
|
||||
if (ort->rt_mtu == nrt->rt_mtu)
|
||||
return true;
|
||||
change = true;
|
||||
kroute = true;
|
||||
}
|
||||
} else if (ort->rt_dflags & RTDF_FAKE &&
|
||||
!(nrt->rt_dflags & RTDF_FAKE) &&
|
||||
ort->rt_ifp == nrt->rt_ifp &&
|
||||
#ifdef HAVE_ROUTE_METRIC
|
||||
ort->rt_metric == nrt->rt_metric &&
|
||||
#endif
|
||||
sa_cmp(&ort->rt_dest, &nrt->rt_dest) == 0 &&
|
||||
rt_cmp_netmask(ort, nrt) == 0 &&
|
||||
sa_cmp(&ort->rt_gateway, &nrt->rt_gateway) == 0)
|
||||
{
|
||||
if (ort->rt_mtu == nrt->rt_mtu)
|
||||
return true;
|
||||
change = true;
|
||||
}
|
||||
|
||||
#ifdef RTF_CLONING
|
||||
/* BSD can set routes to be cloning routes.
|
||||
* Cloned routes inherit the parent flags.
|
||||
* As such, we need to delete and re-add the route to flush children
|
||||
* to correct the flags. */
|
||||
if (change && ort != NULL && ort->rt_flags & RTF_CLONING)
|
||||
change = false;
|
||||
#endif
|
||||
|
||||
if (change) {
|
||||
if (if_route(RTM_CHANGE, nrt) != -1) {
|
||||
result = true;
|
||||
goto out;
|
||||
}
|
||||
if (errno != ESRCH)
|
||||
logerr("if_route (CHG)");
|
||||
}
|
||||
|
||||
#ifdef HAVE_ROUTE_METRIC
|
||||
/* With route metrics, we can safely add the new route before
|
||||
* deleting the old route. */
|
||||
if (if_route(RTM_ADD, nrt) != -1) {
|
||||
if (ort != NULL) {
|
||||
if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
|
||||
logerr("if_route (DEL)");
|
||||
}
|
||||
result = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If the kernel claims the route exists we need to rip out the
|
||||
* old one first. */
|
||||
if (errno != EEXIST || ort == NULL)
|
||||
goto logerr;
|
||||
#endif
|
||||
|
||||
/* No route metrics, we need to delete the old route before
|
||||
* adding the new one. */
|
||||
#ifdef ROUTE_PER_GATEWAY
|
||||
errno = 0;
|
||||
#endif
|
||||
if (ort != NULL) {
|
||||
if (if_route(RTM_DELETE, ort) == -1 && errno != ESRCH)
|
||||
logerr("if_route (DEL)");
|
||||
else
|
||||
kroute = false;
|
||||
}
|
||||
#ifdef ROUTE_PER_GATEWAY
|
||||
/* The OS allows many routes to the same dest with different gateways.
|
||||
* dhcpcd does not support this yet, so for the time being just keep on
|
||||
* deleting the route until there is an error. */
|
||||
if (ort != NULL && errno == 0) {
|
||||
for (;;) {
|
||||
if (if_route(RTM_DELETE, ort) == -1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Shouldn't need to check for EEXIST, but some kernels don't
|
||||
* dump the subnet route just after we added the address. */
|
||||
if (if_route(RTM_ADD, nrt) != -1 || errno == EEXIST) {
|
||||
result = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef HAVE_ROUTE_METRIC
|
||||
logerr:
|
||||
#endif
|
||||
logerr("if_route (ADD)");
|
||||
|
||||
out:
|
||||
if (kroute) {
|
||||
rb_tree_remove_node(kroutes, ort);
|
||||
rt_free(ort);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool
|
||||
rt_delete(struct rt *rt)
|
||||
{
|
||||
int retval;
|
||||
|
||||
rt_desc("deleting", rt);
|
||||
retval = if_route(RTM_DELETE, rt) == -1 ? false : true;
|
||||
if (!retval && errno != ENOENT && errno != ESRCH)
|
||||
logerr(__func__);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static bool
|
||||
rt_cmp(const struct rt *r1, const struct rt *r2)
|
||||
{
|
||||
|
||||
return (r1->rt_ifp == r2->rt_ifp &&
|
||||
#ifdef HAVE_ROUTE_METRIC
|
||||
r1->rt_metric == r2->rt_metric &&
|
||||
#endif
|
||||
sa_cmp(&r1->rt_gateway, &r2->rt_gateway) == 0);
|
||||
}
|
||||
|
||||
static bool
|
||||
rt_doroute(rb_tree_t *kroutes, struct rt *rt)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx;
|
||||
struct rt *or;
|
||||
|
||||
ctx = rt->rt_ifp->ctx;
|
||||
/* Do we already manage it? */
|
||||
or = rb_tree_find_node(&ctx->routes, rt);
|
||||
if (or != NULL) {
|
||||
if (rt->rt_dflags & RTDF_FAKE)
|
||||
return true;
|
||||
if (or->rt_dflags & RTDF_FAKE ||
|
||||
!rt_cmp(rt, or) ||
|
||||
(rt->rt_ifa.sa_family != AF_UNSPEC &&
|
||||
sa_cmp(&or->rt_ifa, &rt->rt_ifa) != 0) ||
|
||||
or->rt_mtu != rt->rt_mtu)
|
||||
{
|
||||
if (!rt_add(kroutes, rt, or))
|
||||
return false;
|
||||
}
|
||||
rb_tree_remove_node(&ctx->routes, or);
|
||||
rt_free(or);
|
||||
} else {
|
||||
if (rt->rt_dflags & RTDF_FAKE) {
|
||||
or = rb_tree_find_node(kroutes, rt);
|
||||
if (or == NULL)
|
||||
return false;
|
||||
if (!rt_cmp(rt, or))
|
||||
return false;
|
||||
} else {
|
||||
if (!rt_add(kroutes, rt, NULL))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
rt_build(struct dhcpcd_ctx *ctx, int af)
|
||||
{
|
||||
rb_tree_t routes, added, kroutes;
|
||||
struct rt *rt, *rtn;
|
||||
unsigned long long o;
|
||||
|
||||
rb_tree_init(&routes, &rt_compare_proto_ops);
|
||||
rb_tree_init(&added, &rt_compare_os_ops);
|
||||
rb_tree_init(&kroutes, &rt_compare_os_ops);
|
||||
if (if_initrt(ctx, &kroutes, af) != 0)
|
||||
logerr("%s: if_initrt", __func__);
|
||||
ctx->rt_order = 0;
|
||||
ctx->options |= DHCPCD_RTBUILD;
|
||||
|
||||
#ifdef INET
|
||||
if (!inet_getroutes(ctx, &routes))
|
||||
goto getfail;
|
||||
#endif
|
||||
#ifdef INET6
|
||||
if (!inet6_getroutes(ctx, &routes))
|
||||
goto getfail;
|
||||
#endif
|
||||
|
||||
#ifdef BSD
|
||||
/* Rewind the miss filter */
|
||||
ctx->rt_missfilterlen = 0;
|
||||
#endif
|
||||
|
||||
RB_TREE_FOREACH_SAFE(rt, &routes, rtn) {
|
||||
if (rt->rt_ifp->active) {
|
||||
if (!(rt->rt_ifp->options->options & DHCPCD_CONFIGURE))
|
||||
continue;
|
||||
} else if (!(ctx->options & DHCPCD_CONFIGURE))
|
||||
continue;
|
||||
#ifdef BSD
|
||||
if (rt_is_default(rt) &&
|
||||
if_missfilter(rt->rt_ifp, &rt->rt_gateway) == -1)
|
||||
logerr("if_missfilter");
|
||||
#endif
|
||||
if ((rt->rt_dest.sa_family != af &&
|
||||
rt->rt_dest.sa_family != AF_UNSPEC) ||
|
||||
(rt->rt_gateway.sa_family != af &&
|
||||
rt->rt_gateway.sa_family != AF_UNSPEC))
|
||||
continue;
|
||||
/* Is this route already in our table? */
|
||||
if (rb_tree_find_node(&added, rt) != NULL)
|
||||
continue;
|
||||
if (rt_doroute(&kroutes, rt)) {
|
||||
rb_tree_remove_node(&routes, rt);
|
||||
if (rb_tree_insert_node(&added, rt) != rt) {
|
||||
errno = EEXIST;
|
||||
logerr(__func__);
|
||||
rt_free(rt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef BSD
|
||||
if (if_missfilter_apply(ctx) == -1 && errno != ENOTSUP)
|
||||
logerr("if_missfilter_apply");
|
||||
#endif
|
||||
|
||||
/* Remove old routes we used to manage. */
|
||||
RB_TREE_FOREACH_REVERSE_SAFE(rt, &ctx->routes, rtn) {
|
||||
if ((rt->rt_dest.sa_family != af &&
|
||||
rt->rt_dest.sa_family != AF_UNSPEC) ||
|
||||
(rt->rt_gateway.sa_family != af &&
|
||||
rt->rt_gateway.sa_family != AF_UNSPEC))
|
||||
continue;
|
||||
rb_tree_remove_node(&ctx->routes, rt);
|
||||
if (rb_tree_find_node(&added, rt) == NULL) {
|
||||
o = rt->rt_ifp->options ?
|
||||
rt->rt_ifp->options->options :
|
||||
ctx->options;
|
||||
if ((o &
|
||||
(DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
|
||||
(DHCPCD_EXITING | DHCPCD_PERSISTENT))
|
||||
rt_delete(rt);
|
||||
}
|
||||
rt_free(rt);
|
||||
}
|
||||
|
||||
/* XXX This needs to be optimised. */
|
||||
while ((rt = RB_TREE_MIN(&added)) != NULL) {
|
||||
rb_tree_remove_node(&added, rt);
|
||||
if (rb_tree_insert_node(&ctx->routes, rt) != rt) {
|
||||
errno = EEXIST;
|
||||
logerr(__func__);
|
||||
rt_free(rt);
|
||||
}
|
||||
}
|
||||
|
||||
getfail:
|
||||
rt_headclear(&routes, AF_UNSPEC);
|
||||
rt_headclear(&kroutes, AF_UNSPEC);
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - route management
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* rEDISTRIBUTION AND USE IN SOURCE AND BINARY FORMS, WITH OR WITHOUT
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef ROUTE_H
|
||||
#define ROUTE_H
|
||||
|
||||
#ifdef HAVE_SYS_RBTREE_H
|
||||
#include <sys/rbtree.h>
|
||||
#endif
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <net/route.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "dhcpcd.h"
|
||||
#include "sa.h"
|
||||
|
||||
/*
|
||||
* Enable the route free list by default as
|
||||
* memory usage is still reported as low/unchanged even
|
||||
* when dealing with millions of routes.
|
||||
*/
|
||||
#if !defined(RT_FREE_ROUTE_TABLE)
|
||||
#define RT_FREE_ROUTE_TABLE 1
|
||||
#elif RT_FREE_ROUTE_TABLE == 0
|
||||
#undef RT_FREE_ROUTE_TABLE
|
||||
#endif
|
||||
|
||||
/* Some systems have route metrics.
|
||||
* OpenBSD route priority is not this. */
|
||||
#ifndef HAVE_ROUTE_METRIC
|
||||
# if defined(__linux__)
|
||||
# define HAVE_ROUTE_METRIC 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
# include <linux/version.h> /* RTA_PREF is only an enum.... */
|
||||
# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
|
||||
# define HAVE_ROUTE_PREF
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__OpenBSD__) || defined (__sun)
|
||||
# define ROUTE_PER_GATEWAY
|
||||
/* XXX dhcpcd doesn't really support this yet.
|
||||
* But that's generally OK if only dhcpcd is managing routes. */
|
||||
#endif
|
||||
|
||||
/* OpenBSD defines this as a "convienience" ..... we work around it. */
|
||||
#ifdef __OpenBSD__
|
||||
#undef rt_mtu
|
||||
#endif
|
||||
|
||||
struct rt {
|
||||
union sa_ss rt_ss_dest;
|
||||
#define rt_dest rt_ss_dest.sa
|
||||
union sa_ss rt_ss_netmask;
|
||||
#define rt_netmask rt_ss_netmask.sa
|
||||
union sa_ss rt_ss_gateway;
|
||||
#define rt_gateway rt_ss_gateway.sa
|
||||
struct interface *rt_ifp;
|
||||
union sa_ss rt_ss_ifa;
|
||||
#define rt_ifa rt_ss_ifa.sa
|
||||
unsigned int rt_flags;
|
||||
unsigned int rt_mtu;
|
||||
#ifdef HAVE_ROUTE_METRIC
|
||||
unsigned int rt_metric;
|
||||
#endif
|
||||
/* Maximum interface index is generally USHORT_MAX or 65535.
|
||||
* Add some padding for other stuff and we get offsets for the
|
||||
* below that should work automatically.
|
||||
* This is only an issue if the user defines higher metrics in
|
||||
* their configuration, but then they might wish to override also. */
|
||||
#define RTMETRIC_BASE 1000U
|
||||
#define RTMETRIC_WIRELESS 2000U
|
||||
#define RTMETRIC_IPV4LL 1000000U
|
||||
#define RTMETRIC_ROAM 2000000U
|
||||
#ifdef HAVE_ROUTE_PREF
|
||||
int rt_pref;
|
||||
#endif
|
||||
#define RTPREF_HIGH 1
|
||||
#define RTPREF_MEDIUM 0 /* has to be zero */
|
||||
#define RTPREF_LOW (-1)
|
||||
#define RTPREF_RESERVED (-2)
|
||||
#define RTPREF_INVALID (-3) /* internal */
|
||||
unsigned int rt_dflags;
|
||||
#define RTDF_IFA_ROUTE 0x01 /* Address generated route */
|
||||
#define RTDF_FAKE 0x02 /* Maybe us on lease reboot */
|
||||
#define RTDF_IPV4LL 0x04 /* IPv4LL route */
|
||||
#define RTDF_RA 0x08 /* Router Advertisement */
|
||||
#define RTDF_DHCP 0x10 /* DHCP route */
|
||||
#define RTDF_STATIC 0x20 /* Configured in dhcpcd */
|
||||
#define RTDF_GATELINK 0x40 /* Gateway is on link */
|
||||
size_t rt_order;
|
||||
rb_node_t rt_tree;
|
||||
};
|
||||
|
||||
extern const rb_tree_ops_t rt_compare_list_ops;
|
||||
extern const rb_tree_ops_t rt_compare_proto_ops;
|
||||
|
||||
void rt_init(struct dhcpcd_ctx *);
|
||||
void rt_dispose(struct dhcpcd_ctx *);
|
||||
void rt_free(struct rt *);
|
||||
void rt_freeif(struct interface *);
|
||||
bool rt_is_default(const struct rt *);
|
||||
void rt_headclear0(struct dhcpcd_ctx *, rb_tree_t *, int);
|
||||
void rt_headclear(rb_tree_t *, int);
|
||||
void rt_headfreeif(rb_tree_t *);
|
||||
struct rt * rt_new0(struct dhcpcd_ctx *);
|
||||
void rt_setif(struct rt *, struct interface *);
|
||||
struct rt * rt_new(struct interface *);
|
||||
struct rt * rt_proto_add_ctx(rb_tree_t *, struct rt *, struct dhcpcd_ctx *);
|
||||
struct rt * rt_proto_add(rb_tree_t *, struct rt *);
|
||||
int rt_cmp_dest(const struct rt *, const struct rt *);
|
||||
void rt_recvrt(int, const struct rt *, pid_t);
|
||||
void rt_build(struct dhcpcd_ctx *, int);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,503 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* Socket Address handling for dhcpcd
|
||||
* Copyright (c) 2015-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#ifdef AF_LINK
|
||||
#include <net/if_dl.h>
|
||||
#elif defined(AF_PACKET)
|
||||
#include <linux/if_packet.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "common.h"
|
||||
#include "sa.h"
|
||||
|
||||
#ifndef NDEBUG
|
||||
static bool sa_inprefix;
|
||||
#endif
|
||||
|
||||
socklen_t
|
||||
sa_addroffset(const struct sockaddr *sa)
|
||||
{
|
||||
|
||||
assert(sa != NULL);
|
||||
switch(sa->sa_family) {
|
||||
#ifdef INET
|
||||
case AF_INET:
|
||||
return offsetof(struct sockaddr_in, sin_addr) +
|
||||
offsetof(struct in_addr, s_addr);
|
||||
#endif /* INET */
|
||||
#ifdef INET6
|
||||
case AF_INET6:
|
||||
return offsetof(struct sockaddr_in6, sin6_addr) +
|
||||
offsetof(struct in6_addr, s6_addr);
|
||||
#endif /* INET6 */
|
||||
default:
|
||||
errno = EAFNOSUPPORT;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
socklen_t
|
||||
sa_addrlen(const struct sockaddr *sa)
|
||||
{
|
||||
#define membersize(type, member) sizeof(((type *)0)->member)
|
||||
assert(sa != NULL);
|
||||
switch(sa->sa_family) {
|
||||
#ifdef INET
|
||||
case AF_INET:
|
||||
return membersize(struct in_addr, s_addr);
|
||||
#endif /* INET */
|
||||
#ifdef INET6
|
||||
case AF_INET6:
|
||||
return membersize(struct in6_addr, s6_addr);
|
||||
#endif /* INET6 */
|
||||
default:
|
||||
errno = EAFNOSUPPORT;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef HAVE_SA_LEN
|
||||
socklen_t
|
||||
sa_len(const struct sockaddr *sa)
|
||||
{
|
||||
|
||||
switch (sa->sa_family) {
|
||||
#ifdef AF_LINK
|
||||
case AF_LINK:
|
||||
return sizeof(struct sockaddr_dl);
|
||||
#endif
|
||||
#ifdef AF_PACKET
|
||||
case AF_PACKET:
|
||||
return sizeof(struct sockaddr_ll);
|
||||
#endif
|
||||
case AF_INET:
|
||||
return sizeof(struct sockaddr_in);
|
||||
case AF_INET6:
|
||||
return sizeof(struct sockaddr_in6);
|
||||
default:
|
||||
return sizeof(struct sockaddr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
sa_is_unspecified(const struct sockaddr *sa)
|
||||
{
|
||||
|
||||
assert(sa != NULL);
|
||||
switch(sa->sa_family) {
|
||||
case AF_UNSPEC:
|
||||
return true;
|
||||
#ifdef INET
|
||||
case AF_INET:
|
||||
return satocsin(sa)->sin_addr.s_addr == INADDR_ANY;
|
||||
#endif /* INET */
|
||||
#ifdef INET6
|
||||
case AF_INET6:
|
||||
return IN6_IS_ADDR_UNSPECIFIED(&satocsin6(sa)->sin6_addr);
|
||||
#endif /* INET6 */
|
||||
default:
|
||||
errno = EAFNOSUPPORT;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef INET6
|
||||
#ifndef IN6MASK128
|
||||
#define IN6MASK128 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}}
|
||||
#endif
|
||||
static const struct in6_addr in6allones = IN6MASK128;
|
||||
#endif
|
||||
|
||||
bool
|
||||
sa_is_allones(const struct sockaddr *sa)
|
||||
{
|
||||
|
||||
assert(sa != NULL);
|
||||
switch(sa->sa_family) {
|
||||
case AF_UNSPEC:
|
||||
return false;
|
||||
#ifdef INET
|
||||
case AF_INET:
|
||||
{
|
||||
const struct sockaddr_in *sin;
|
||||
|
||||
sin = satocsin(sa);
|
||||
return sin->sin_addr.s_addr == INADDR_BROADCAST;
|
||||
}
|
||||
#endif /* INET */
|
||||
#ifdef INET6
|
||||
case AF_INET6:
|
||||
{
|
||||
const struct sockaddr_in6 *sin6;
|
||||
|
||||
sin6 = satocsin6(sa);
|
||||
return IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &in6allones);
|
||||
}
|
||||
#endif /* INET6 */
|
||||
default:
|
||||
errno = EAFNOSUPPORT;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
sa_is_loopback(const struct sockaddr *sa)
|
||||
{
|
||||
|
||||
assert(sa != NULL);
|
||||
switch(sa->sa_family) {
|
||||
case AF_UNSPEC:
|
||||
return false;
|
||||
#ifdef INET
|
||||
case AF_INET:
|
||||
{
|
||||
const struct sockaddr_in *sin;
|
||||
|
||||
sin = satocsin(sa);
|
||||
return sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK);
|
||||
}
|
||||
#endif /* INET */
|
||||
#ifdef INET6
|
||||
case AF_INET6:
|
||||
{
|
||||
const struct sockaddr_in6 *sin6;
|
||||
|
||||
sin6 = satocsin6(sa);
|
||||
return IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr);
|
||||
}
|
||||
#endif /* INET6 */
|
||||
default:
|
||||
errno = EAFNOSUPPORT;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
sa_toprefix(const struct sockaddr *sa)
|
||||
{
|
||||
int prefix;
|
||||
|
||||
assert(sa != NULL);
|
||||
switch(sa->sa_family) {
|
||||
#ifdef INET
|
||||
case AF_INET:
|
||||
{
|
||||
const struct sockaddr_in *sin;
|
||||
uint32_t mask;
|
||||
|
||||
sin = satocsin(sa);
|
||||
if (sin->sin_addr.s_addr == INADDR_ANY) {
|
||||
prefix = 0;
|
||||
break;
|
||||
}
|
||||
mask = ntohl(sin->sin_addr.s_addr);
|
||||
prefix = 33 - ffs((int)mask); /* 33 - (1 .. 32) -> 32 .. 1 */
|
||||
if (prefix < 32) { /* more than 1 bit in mask */
|
||||
/* check for non-contig netmask */
|
||||
if ((mask^(((1U << prefix)-1) << (32 - prefix))) != 0) {
|
||||
errno = EINVAL;
|
||||
return -1; /* noncontig, no pfxlen */
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef INET6
|
||||
case AF_INET6:
|
||||
{
|
||||
const struct sockaddr_in6 *sin6;
|
||||
int x, y;
|
||||
const uint8_t *lim, *p;
|
||||
|
||||
sin6 = satocsin6(sa);
|
||||
p = (const uint8_t *)sin6->sin6_addr.s6_addr;
|
||||
lim = p + sizeof(sin6->sin6_addr.s6_addr);
|
||||
for (x = 0; p < lim; x++, p++) {
|
||||
if (*p != 0xff)
|
||||
break;
|
||||
}
|
||||
y = 0;
|
||||
if (p < lim) {
|
||||
for (y = 0; y < NBBY; y++) {
|
||||
if ((*p & (0x80 >> y)) == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* when the limit pointer is given, do a stricter check on the
|
||||
* remaining bits.
|
||||
*/
|
||||
if (p < lim) {
|
||||
if (y != 0 && (*p & (0x00ff >> y)) != 0)
|
||||
return 0;
|
||||
for (p = p + 1; p < lim; p++)
|
||||
if (*p != 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
prefix = x * NBBY + y;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
errno = EAFNOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
/* Ensure the calculation is correct */
|
||||
if (!sa_inprefix) {
|
||||
union sa_ss ss = { .sa = { .sa_family = sa->sa_family } };
|
||||
|
||||
sa_inprefix = true;
|
||||
sa_fromprefix(&ss.sa, prefix);
|
||||
assert(sa_cmp(sa, &ss.sa) == 0);
|
||||
sa_inprefix = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return prefix;
|
||||
}
|
||||
|
||||
static void
|
||||
ipbytes_fromprefix(uint8_t *ap, int prefix, int max_prefix)
|
||||
{
|
||||
int bytes, bits, i;
|
||||
|
||||
bytes = prefix / NBBY;
|
||||
bits = prefix % NBBY;
|
||||
|
||||
for (i = 0; i < bytes; i++)
|
||||
*ap++ = 0xff;
|
||||
if (bits) {
|
||||
uint8_t a;
|
||||
|
||||
a = 0xff;
|
||||
a = (uint8_t)(a << (8 - bits));
|
||||
*ap++ = a;
|
||||
}
|
||||
bytes = (max_prefix - prefix) / NBBY;
|
||||
for (i = 0; i < bytes; i++)
|
||||
*ap++ = 0x00;
|
||||
}
|
||||
|
||||
void
|
||||
in6_addr_fromprefix(struct in6_addr *addr, int prefix)
|
||||
{
|
||||
ipbytes_fromprefix((uint8_t *)addr, prefix, 128);
|
||||
}
|
||||
|
||||
int
|
||||
sa_fromprefix(struct sockaddr *sa, int prefix)
|
||||
{
|
||||
uint8_t *ap;
|
||||
int max_prefix;
|
||||
|
||||
switch (sa->sa_family) {
|
||||
#ifdef INET
|
||||
case AF_INET:
|
||||
max_prefix = 32;
|
||||
#ifdef HAVE_SA_LEN
|
||||
sa->sa_len = sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
#ifdef INET6
|
||||
case AF_INET6:
|
||||
max_prefix = 128;
|
||||
#ifdef HAVE_SA_LEN
|
||||
sa->sa_len = sizeof(struct sockaddr_in6);
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
errno = EAFNOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ap = (uint8_t *)sa + sa_addroffset(sa);
|
||||
ipbytes_fromprefix(ap, prefix, max_prefix);
|
||||
|
||||
#ifndef NDEBUG
|
||||
/* Ensure the calculation is correct */
|
||||
if (!sa_inprefix) {
|
||||
sa_inprefix = true;
|
||||
assert(sa_toprefix(sa) == prefix);
|
||||
sa_inprefix = false;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* inet_ntop, but for sockaddr. */
|
||||
const char *
|
||||
sa_addrtop(const struct sockaddr *sa, char *buf, socklen_t len)
|
||||
{
|
||||
const void *addr;
|
||||
|
||||
assert(buf != NULL);
|
||||
assert(len > 0);
|
||||
|
||||
if (sa->sa_family == 0) {
|
||||
*buf = '\0';
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef AF_LINK
|
||||
#ifndef CLLADDR
|
||||
#define CLLADDR(sdl) (const void *)((sdl)->sdl_data + (sdl)->sdl_nlen)
|
||||
#endif
|
||||
if (sa->sa_family == AF_LINK) {
|
||||
const struct sockaddr_dl *sdl;
|
||||
|
||||
sdl = (const void *)sa;
|
||||
if (sdl->sdl_alen == 0) {
|
||||
if (snprintf(buf, len, "link#%d", sdl->sdl_index) == -1)
|
||||
return NULL;
|
||||
return buf;
|
||||
}
|
||||
return hwaddr_ntoa(CLLADDR(sdl), sdl->sdl_alen, buf, len);
|
||||
}
|
||||
#elif defined(AF_PACKET)
|
||||
if (sa->sa_family == AF_PACKET) {
|
||||
const struct sockaddr_ll *sll;
|
||||
|
||||
sll = (const void *)sa;
|
||||
return hwaddr_ntoa(sll->sll_addr, sll->sll_halen, buf, len);
|
||||
}
|
||||
#endif
|
||||
addr = (const char *)sa + sa_addroffset(sa);
|
||||
return inet_ntop(sa->sa_family, addr, buf, len);
|
||||
}
|
||||
|
||||
int
|
||||
sa_cmp(const struct sockaddr *sa1, const struct sockaddr *sa2)
|
||||
{
|
||||
socklen_t offset, len;
|
||||
|
||||
assert(sa1 != NULL);
|
||||
assert(sa2 != NULL);
|
||||
|
||||
/* Treat AF_UNSPEC as the unspecified address. */
|
||||
if ((sa1->sa_family == AF_UNSPEC || sa2->sa_family == AF_UNSPEC) &&
|
||||
sa_is_unspecified(sa1) && sa_is_unspecified(sa2))
|
||||
return 0;
|
||||
|
||||
if (sa1->sa_family != sa2->sa_family)
|
||||
return sa1->sa_family - sa2->sa_family;
|
||||
|
||||
#ifdef HAVE_SA_LEN
|
||||
len = MIN(sa1->sa_len, sa2->sa_len);
|
||||
#endif
|
||||
|
||||
switch (sa1->sa_family) {
|
||||
#ifdef INET
|
||||
case AF_INET:
|
||||
offset = offsetof(struct sockaddr_in, sin_addr);
|
||||
#ifdef HAVE_SA_LEN
|
||||
len -= offset;
|
||||
len = MIN(len, sizeof(struct in_addr));
|
||||
#else
|
||||
len = sizeof(struct in_addr);
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
#ifdef INET6
|
||||
case AF_INET6:
|
||||
offset = offsetof(struct sockaddr_in6, sin6_addr);
|
||||
#ifdef HAVE_SA_LEN
|
||||
len -= offset;
|
||||
len = MIN(len, sizeof(struct in6_addr));
|
||||
#else
|
||||
len = sizeof(struct in6_addr);
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
offset = 0;
|
||||
#ifndef HAVE_SA_LEN
|
||||
len = sizeof(struct sockaddr);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
return memcmp((const char *)sa1 + offset,
|
||||
(const char *)sa2 + offset,
|
||||
len);
|
||||
}
|
||||
|
||||
#ifdef INET
|
||||
void
|
||||
sa_in_init(struct sockaddr *sa, const struct in_addr *addr)
|
||||
{
|
||||
struct sockaddr_in *sin;
|
||||
|
||||
assert(sa != NULL);
|
||||
assert(addr != NULL);
|
||||
sin = satosin(sa);
|
||||
sin->sin_family = AF_INET;
|
||||
#ifdef HAVE_SA_LEN
|
||||
sin->sin_len = sizeof(*sin);
|
||||
#endif
|
||||
sin->sin_addr.s_addr = addr->s_addr;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef INET6
|
||||
void
|
||||
sa_in6_init(struct sockaddr *sa, const struct in6_addr *addr)
|
||||
{
|
||||
struct sockaddr_in6 *sin6;
|
||||
|
||||
assert(sa != NULL);
|
||||
assert(addr != NULL);
|
||||
sin6 = satosin6(sa);
|
||||
sin6->sin6_family = AF_INET6;
|
||||
#ifdef HAVE_SA_LEN
|
||||
sin6->sin6_len = sizeof(*sin6);
|
||||
#endif
|
||||
memcpy(&sin6->sin6_addr.s6_addr, &addr->s6_addr,
|
||||
sizeof(sin6->sin6_addr.s6_addr));
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,76 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* Socket Address handling for dhcpcd
|
||||
* Copyright (c) 2015-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef SA_H
|
||||
#define SA_H
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
union sa_ss {
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_in sin;
|
||||
struct sockaddr_in6 sin6;
|
||||
};
|
||||
|
||||
#ifdef BSD
|
||||
#define HAVE_SA_LEN
|
||||
#endif
|
||||
|
||||
/* Allow for a sockaddr_dl being printed too. */
|
||||
#define INET_MAX_ADDRSTRLEN (20 * 3)
|
||||
|
||||
#ifdef INET
|
||||
#define satosin(sa) ((struct sockaddr_in *)(void *)(sa))
|
||||
#define satocsin(sa) ((const struct sockaddr_in *)(const void *)(sa))
|
||||
#endif
|
||||
#ifdef INET6
|
||||
#define satosin6(sa) ((struct sockaddr_in6 *)(void *)(sa))
|
||||
#define satocsin6(sa) ((const struct sockaddr_in6 *)(const void *)(sa))
|
||||
#endif
|
||||
|
||||
socklen_t sa_addroffset(const struct sockaddr *sa);
|
||||
socklen_t sa_addrlen(const struct sockaddr *sa);
|
||||
#ifdef HAVE_SA_LEN
|
||||
#define sa_len(sa) ((sa)->sa_len)
|
||||
#else
|
||||
socklen_t sa_len(const struct sockaddr *sa);
|
||||
#endif
|
||||
bool sa_is_unspecified(const struct sockaddr *);
|
||||
bool sa_is_allones(const struct sockaddr *);
|
||||
bool sa_is_loopback(const struct sockaddr *);
|
||||
void *sa_toaddr(struct sockaddr *);
|
||||
int sa_toprefix(const struct sockaddr *);
|
||||
int sa_fromprefix(struct sockaddr *, int);
|
||||
void in6_addr_fromprefix(struct in6_addr *, int);
|
||||
const char *sa_addrtop(const struct sockaddr *, char *, socklen_t);
|
||||
int sa_cmp(const struct sockaddr *, const struct sockaddr *);
|
||||
void sa_in_init(struct sockaddr *, const struct in_addr *);
|
||||
void sa_in6_init(struct sockaddr *, const struct in6_addr *);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,801 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <spawn.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "common.h"
|
||||
#include "dhcp.h"
|
||||
#include "dhcp6.h"
|
||||
#include "eloop.h"
|
||||
#include "if.h"
|
||||
#include "if-options.h"
|
||||
#include "ipv4ll.h"
|
||||
#include "ipv6nd.h"
|
||||
#include "logerr.h"
|
||||
#include "privsep.h"
|
||||
#include "script.h"
|
||||
|
||||
#define DEFAULT_PATH "/usr/bin:/usr/sbin:/bin:/sbin"
|
||||
|
||||
static const char * const if_params[] = {
|
||||
"interface",
|
||||
"protocol",
|
||||
"reason",
|
||||
"pid",
|
||||
"ifcarrier",
|
||||
"ifmetric",
|
||||
"ifwireless",
|
||||
"ifflags",
|
||||
"ssid",
|
||||
"profile",
|
||||
"interface_order",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char * true_str = "true";
|
||||
static const char * false_str = "false";
|
||||
|
||||
void
|
||||
if_printoptions(void)
|
||||
{
|
||||
const char * const *p;
|
||||
|
||||
for (p = if_params; *p; p++)
|
||||
printf(" - %s\n", *p);
|
||||
}
|
||||
|
||||
pid_t
|
||||
script_exec(char *const *argv, char *const *env)
|
||||
{
|
||||
pid_t pid = 0;
|
||||
posix_spawnattr_t attr;
|
||||
int r;
|
||||
#ifdef USE_SIGNALS
|
||||
size_t i;
|
||||
short flags;
|
||||
sigset_t defsigs;
|
||||
#else
|
||||
UNUSED(ctx);
|
||||
#endif
|
||||
|
||||
/* posix_spawn is a safe way of executing another image
|
||||
* and changing signals back to how they should be. */
|
||||
if (posix_spawnattr_init(&attr) == -1)
|
||||
return -1;
|
||||
#ifdef USE_SIGNALS
|
||||
flags = POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF;
|
||||
posix_spawnattr_setflags(&attr, flags);
|
||||
sigemptyset(&defsigs);
|
||||
posix_spawnattr_setsigmask(&attr, &defsigs);
|
||||
for (i = 0; i < dhcpcd_signals_len; i++)
|
||||
sigaddset(&defsigs, dhcpcd_signals[i]);
|
||||
for (i = 0; i < dhcpcd_signals_ignore_len; i++)
|
||||
sigaddset(&defsigs, dhcpcd_signals_ignore[i]);
|
||||
posix_spawnattr_setsigdefault(&attr, &defsigs);
|
||||
#endif
|
||||
errno = 0;
|
||||
r = posix_spawn(&pid, argv[0], NULL, &attr, argv, env);
|
||||
posix_spawnattr_destroy(&attr);
|
||||
if (r) {
|
||||
errno = r;
|
||||
return -1;
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
#ifdef INET
|
||||
static int
|
||||
append_config(FILE *fp, const char *prefix, const char *const *config)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (config == NULL)
|
||||
return 0;
|
||||
|
||||
/* Do we need to replace existing config rather than append? */
|
||||
for (i = 0; config[i] != NULL; i++) {
|
||||
if (efprintf(fp, "%s_%s", prefix, config[i]) == -1)
|
||||
return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#define PROTO_LINK 0
|
||||
#define PROTO_DHCP 1
|
||||
#define PROTO_IPV4LL 2
|
||||
#define PROTO_RA 3
|
||||
#define PROTO_DHCP6 4
|
||||
#define PROTO_STATIC6 5
|
||||
static const char *protocols[] = {
|
||||
"link",
|
||||
"dhcp",
|
||||
"ipv4ll",
|
||||
"ra",
|
||||
"dhcp6",
|
||||
"static6"
|
||||
};
|
||||
|
||||
int
|
||||
efprintf(FILE *fp, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int r;
|
||||
|
||||
va_start(args, fmt);
|
||||
r = vfprintf(fp, fmt, args);
|
||||
va_end(args);
|
||||
if (r == -1)
|
||||
return -1;
|
||||
/* Write a trailing NULL so we can easily create env strings. */
|
||||
if (fputc('\0', fp) == EOF)
|
||||
return -1;
|
||||
return r;
|
||||
}
|
||||
|
||||
char **
|
||||
script_buftoenv(struct dhcpcd_ctx *ctx, char *buf, size_t len)
|
||||
{
|
||||
char **env, **envp, *bufp, *endp;
|
||||
size_t nenv;
|
||||
|
||||
/* Count the terminated env strings.
|
||||
* Assert that the terminations are correct. */
|
||||
nenv = 0;
|
||||
endp = buf + len;
|
||||
for (bufp = buf; bufp < endp; bufp++) {
|
||||
if (*bufp == '\0') {
|
||||
#ifndef NDEBUG
|
||||
if (bufp + 1 < endp)
|
||||
assert(*(bufp + 1) != '\0');
|
||||
#endif
|
||||
nenv++;
|
||||
}
|
||||
}
|
||||
assert(*(bufp - 1) == '\0');
|
||||
if (nenv == 0)
|
||||
return NULL;
|
||||
|
||||
if (ctx->script_envlen < nenv) {
|
||||
env = reallocarray(ctx->script_env, nenv + 1, sizeof(*env));
|
||||
if (env == NULL)
|
||||
return NULL;
|
||||
ctx->script_env = env;
|
||||
ctx->script_envlen = nenv;
|
||||
}
|
||||
|
||||
bufp = buf;
|
||||
envp = ctx->script_env;
|
||||
*envp++ = bufp++;
|
||||
endp--; /* Avoid setting the last \0 to an invalid pointer */
|
||||
for (; bufp < endp; bufp++) {
|
||||
if (*bufp == '\0')
|
||||
*envp++ = bufp + 1;
|
||||
}
|
||||
*envp = NULL;
|
||||
|
||||
return ctx->script_env;
|
||||
}
|
||||
|
||||
static long
|
||||
make_env(struct dhcpcd_ctx *ctx, const struct interface *ifp,
|
||||
const char *reason)
|
||||
{
|
||||
FILE *fp;
|
||||
long buf_pos, i;
|
||||
char *path;
|
||||
int protocol = PROTO_LINK;
|
||||
const struct if_options *ifo;
|
||||
const struct interface *ifp2;
|
||||
int af;
|
||||
bool is_stdin = ifp->name[0] == '\0';
|
||||
const char *if_up, *if_down;
|
||||
rb_tree_t ifaces;
|
||||
struct rt *rt;
|
||||
#ifdef INET
|
||||
const struct dhcp_state *state;
|
||||
#ifdef IPV4LL
|
||||
const struct ipv4ll_state *istate;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef DHCP6
|
||||
const struct dhcp6_state *d6_state;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPEN_MEMSTREAM
|
||||
if (ctx->script_fp == NULL) {
|
||||
fp = open_memstream(&ctx->script_buf, &ctx->script_buflen);
|
||||
if (fp == NULL)
|
||||
goto eexit;
|
||||
ctx->script_fp = fp;
|
||||
} else {
|
||||
fp = ctx->script_fp;
|
||||
rewind(fp);
|
||||
}
|
||||
#else
|
||||
char tmpfile[] = "/tmp/dhcpcd-script-env-XXXXXX";
|
||||
int tmpfd;
|
||||
|
||||
fp = NULL;
|
||||
tmpfd = mkstemp(tmpfile);
|
||||
if (tmpfd == -1) {
|
||||
logerr("%s: mkstemp", __func__);
|
||||
return -1;
|
||||
}
|
||||
unlink(tmpfile);
|
||||
fp = fdopen(tmpfd, "w+");
|
||||
if (fp == NULL) {
|
||||
close(tmpfd);
|
||||
goto eexit;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!(ifp->ctx->options & DHCPCD_DUMPLEASE)) {
|
||||
/* Needed for scripts */
|
||||
path = getenv("PATH");
|
||||
if (efprintf(fp, "PATH=%s",
|
||||
path == NULL ? DEFAULT_PATH : path) == -1)
|
||||
goto eexit;
|
||||
if (efprintf(fp, "pid=%d", getpid()) == -1)
|
||||
goto eexit;
|
||||
}
|
||||
|
||||
if (!is_stdin) {
|
||||
if (efprintf(fp, "reason=%s", reason) == -1)
|
||||
goto eexit;
|
||||
}
|
||||
|
||||
ifo = ifp->options;
|
||||
#ifdef INET
|
||||
state = D_STATE(ifp);
|
||||
#ifdef IPV4LL
|
||||
istate = IPV4LL_CSTATE(ifp);
|
||||
#endif
|
||||
#endif
|
||||
#ifdef DHCP6
|
||||
d6_state = D6_CSTATE(ifp);
|
||||
#endif
|
||||
if (strcmp(reason, "TEST") == 0) {
|
||||
if (1 == 2) {
|
||||
/* This space left intentionally blank
|
||||
* as all the below statements are optional. */
|
||||
}
|
||||
#ifdef INET6
|
||||
#ifdef DHCP6
|
||||
else if (d6_state && d6_state->new)
|
||||
protocol = PROTO_DHCP6;
|
||||
#endif
|
||||
else if (ipv6nd_hasra(ifp))
|
||||
protocol = PROTO_RA;
|
||||
#endif
|
||||
#ifdef INET
|
||||
#ifdef IPV4LL
|
||||
else if (istate && istate->addr != NULL)
|
||||
protocol = PROTO_IPV4LL;
|
||||
#endif
|
||||
else
|
||||
protocol = PROTO_DHCP;
|
||||
#endif
|
||||
}
|
||||
#ifdef INET6
|
||||
else if (strcmp(reason, "STATIC6") == 0)
|
||||
protocol = PROTO_STATIC6;
|
||||
#ifdef DHCP6
|
||||
else if (reason[strlen(reason) - 1] == '6')
|
||||
protocol = PROTO_DHCP6;
|
||||
#endif
|
||||
else if (strcmp(reason, "ROUTERADVERT") == 0)
|
||||
protocol = PROTO_RA;
|
||||
#endif
|
||||
else if (strcmp(reason, "PREINIT") == 0 ||
|
||||
strcmp(reason, "CARRIER") == 0 ||
|
||||
strcmp(reason, "NOCARRIER") == 0 ||
|
||||
strcmp(reason, "NOCARRIER_ROAMING") == 0 ||
|
||||
strcmp(reason, "UNKNOWN") == 0 ||
|
||||
strcmp(reason, "DEPARTED") == 0 ||
|
||||
strcmp(reason, "STOPPED") == 0)
|
||||
protocol = PROTO_LINK;
|
||||
#ifdef INET
|
||||
#ifdef IPV4LL
|
||||
else if (strcmp(reason, "IPV4LL") == 0)
|
||||
protocol = PROTO_IPV4LL;
|
||||
#endif
|
||||
else
|
||||
protocol = PROTO_DHCP;
|
||||
#endif
|
||||
|
||||
if (!is_stdin) {
|
||||
if (efprintf(fp, "interface=%s", ifp->name) == -1)
|
||||
goto eexit;
|
||||
if (protocols[protocol] != NULL) {
|
||||
if (efprintf(fp, "protocol=%s",
|
||||
protocols[protocol]) == -1)
|
||||
goto eexit;
|
||||
}
|
||||
}
|
||||
if (ifp->ctx->options & DHCPCD_DUMPLEASE && protocol != PROTO_LINK)
|
||||
goto dumplease;
|
||||
if (efprintf(fp, "if_configured=%s",
|
||||
ifo->options & DHCPCD_CONFIGURE ? "true" : "false") == -1)
|
||||
goto eexit;
|
||||
if (efprintf(fp, "ifcarrier=%s",
|
||||
ifp->carrier == LINK_UNKNOWN ? "unknown" :
|
||||
ifp->carrier == LINK_UP ? "up" : "down") == -1)
|
||||
goto eexit;
|
||||
if (efprintf(fp, "ifmetric=%d", ifp->metric) == -1)
|
||||
goto eexit;
|
||||
if (efprintf(fp, "ifwireless=%d", ifp->wireless) == -1)
|
||||
goto eexit;
|
||||
if (efprintf(fp, "ifflags=%u", ifp->flags) == -1)
|
||||
goto eexit;
|
||||
if (efprintf(fp, "ifmtu=%d", if_getmtu(ifp)) == -1)
|
||||
goto eexit;
|
||||
if (ifp->wireless) {
|
||||
char pssid[IF_SSIDLEN * 4];
|
||||
|
||||
if (print_string(pssid, sizeof(pssid), OT_ESCSTRING,
|
||||
ifp->ssid, ifp->ssid_len) != -1)
|
||||
{
|
||||
if (efprintf(fp, "ifssid=%s", pssid) == -1)
|
||||
goto eexit;
|
||||
}
|
||||
}
|
||||
if (*ifp->profile != '\0') {
|
||||
if (efprintf(fp, "profile=%s", ifp->profile) == -1)
|
||||
goto eexit;
|
||||
}
|
||||
if (ifp->ctx->options & DHCPCD_DUMPLEASE)
|
||||
goto dumplease;
|
||||
|
||||
ifp->ctx->rt_order = 0;
|
||||
rb_tree_init(&ifaces, &rt_compare_proto_ops);
|
||||
TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
|
||||
if (!ifp2->active)
|
||||
continue;
|
||||
rt = rt_new(UNCONST(ifp2));
|
||||
if (rt == NULL)
|
||||
goto eexit;
|
||||
if (rt_proto_add(&ifaces, rt) != rt)
|
||||
goto eexit;
|
||||
}
|
||||
if (fprintf(fp, "interface_order=") == -1)
|
||||
goto eexit;
|
||||
RB_TREE_FOREACH(rt, &ifaces) {
|
||||
if (rt != RB_TREE_MIN(&ifaces) &&
|
||||
fprintf(fp, "%s", " ") == -1)
|
||||
goto eexit;
|
||||
if (fprintf(fp, "%s", rt->rt_ifp->name) == -1)
|
||||
goto eexit;
|
||||
}
|
||||
rt_headclear(&ifaces, AF_UNSPEC);
|
||||
if (fputc('\0', fp) == EOF)
|
||||
goto eexit;
|
||||
|
||||
if (strcmp(reason, "STOPPED") == 0) {
|
||||
if_up = false_str;
|
||||
if_down = ifo->options & DHCPCD_RELEASE ? true_str : false_str;
|
||||
} else if (strcmp(reason, "TEST") == 0 ||
|
||||
strcmp(reason, "PREINIT") == 0 ||
|
||||
strcmp(reason, "CARRIER") == 0 ||
|
||||
strcmp(reason, "UNKNOWN") == 0)
|
||||
{
|
||||
if_up = false_str;
|
||||
if_down = false_str;
|
||||
} else if (strcmp(reason, "NOCARRIER") == 0) {
|
||||
if_up = false_str;
|
||||
if_down = true_str;
|
||||
} else if (strcmp(reason, "NOCARRIER_ROAMING") == 0) {
|
||||
if_up = true_str;
|
||||
if_down = false_str;
|
||||
} else if (1 == 2 /* appease ifdefs */
|
||||
#ifdef INET
|
||||
|| (protocol == PROTO_DHCP && state && state->new)
|
||||
#ifdef IPV4LL
|
||||
|| (protocol == PROTO_IPV4LL && IPV4LL_STATE_RUNNING(ifp))
|
||||
#endif
|
||||
#endif
|
||||
#ifdef INET6
|
||||
|| (protocol == PROTO_STATIC6 && IPV6_STATE_RUNNING(ifp))
|
||||
#ifdef DHCP6
|
||||
|| (protocol == PROTO_DHCP6 && d6_state && d6_state->new)
|
||||
#endif
|
||||
|| (protocol == PROTO_RA && ipv6nd_hasra(ifp))
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if_up = true_str;
|
||||
if_down = false_str;
|
||||
} else {
|
||||
if_up = false_str;
|
||||
if_down = true_str;
|
||||
}
|
||||
if (efprintf(fp, "if_up=%s", if_up) == -1)
|
||||
goto eexit;
|
||||
if (efprintf(fp, "if_down=%s", if_down) == -1)
|
||||
goto eexit;
|
||||
|
||||
if ((af = dhcpcd_ifafwaiting(ifp)) != AF_MAX) {
|
||||
if (efprintf(fp, "if_afwaiting=%d", af) == -1)
|
||||
goto eexit;
|
||||
}
|
||||
if ((af = dhcpcd_afwaiting(ifp->ctx)) != AF_MAX) {
|
||||
TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
|
||||
if ((af = dhcpcd_ifafwaiting(ifp2)) != AF_MAX)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (af != AF_MAX) {
|
||||
if (efprintf(fp, "af_waiting=%d", af) == -1)
|
||||
goto eexit;
|
||||
}
|
||||
if (ifo->options & DHCPCD_DEBUG) {
|
||||
if (efprintf(fp, "syslog_debug=true") == -1)
|
||||
goto eexit;
|
||||
}
|
||||
#ifdef INET
|
||||
if (protocol == PROTO_DHCP && state && state->old) {
|
||||
if (dhcp_env(fp, "old", ifp,
|
||||
state->old, state->old_len) == -1)
|
||||
goto eexit;
|
||||
if (append_config(fp, "old",
|
||||
(const char *const *)ifo->config) == -1)
|
||||
goto eexit;
|
||||
}
|
||||
#endif
|
||||
#ifdef DHCP6
|
||||
if (protocol == PROTO_DHCP6 && d6_state && d6_state->old) {
|
||||
if (dhcp6_env(fp, "old", ifp,
|
||||
d6_state->old, d6_state->old_len) == -1)
|
||||
goto eexit;
|
||||
}
|
||||
#endif
|
||||
|
||||
dumplease:
|
||||
#ifdef INET
|
||||
#ifdef IPV4LL
|
||||
if (protocol == PROTO_IPV4LL && istate) {
|
||||
if (ipv4ll_env(fp, istate->down ? "old" : "new", ifp) == -1)
|
||||
goto eexit;
|
||||
}
|
||||
#endif
|
||||
if (protocol == PROTO_DHCP && state && state->new) {
|
||||
if (dhcp_env(fp, "new", ifp,
|
||||
state->new, state->new_len) == -1)
|
||||
goto eexit;
|
||||
if (append_config(fp, "new",
|
||||
(const char *const *)ifo->config) == -1)
|
||||
goto eexit;
|
||||
}
|
||||
#endif
|
||||
#ifdef INET6
|
||||
if (protocol == PROTO_STATIC6) {
|
||||
if (ipv6_env(fp, "new", ifp) == -1)
|
||||
goto eexit;
|
||||
}
|
||||
#ifdef DHCP6
|
||||
if (protocol == PROTO_DHCP6 && D6_STATE_RUNNING(ifp)) {
|
||||
if (dhcp6_env(fp, "new", ifp,
|
||||
d6_state->new, d6_state->new_len) == -1)
|
||||
goto eexit;
|
||||
}
|
||||
#endif
|
||||
if (protocol == PROTO_RA) {
|
||||
if (ipv6nd_env(fp, ifp) == -1)
|
||||
goto eexit;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Add our base environment */
|
||||
if (ifo->environ) {
|
||||
for (i = 0; ifo->environ[i] != NULL; i++)
|
||||
if (efprintf(fp, "%s", ifo->environ[i]) == -1)
|
||||
goto eexit;
|
||||
}
|
||||
|
||||
/* Convert buffer to argv */
|
||||
fflush(fp);
|
||||
|
||||
buf_pos = ftell(fp);
|
||||
if (buf_pos == -1) {
|
||||
logerr(__func__);
|
||||
goto eexit;
|
||||
}
|
||||
|
||||
#ifndef HAVE_OPEN_MEMSTREAM
|
||||
size_t buf_len = (size_t)buf_pos;
|
||||
if (ctx->script_buflen < buf_len) {
|
||||
char *buf = realloc(ctx->script_buf, buf_len);
|
||||
if (buf == NULL)
|
||||
goto eexit;
|
||||
ctx->script_buf = buf;
|
||||
ctx->script_buflen = buf_len;
|
||||
}
|
||||
rewind(fp);
|
||||
if (fread(ctx->script_buf, sizeof(char), buf_len, fp) != buf_len)
|
||||
goto eexit;
|
||||
fclose(fp);
|
||||
fp = NULL;
|
||||
#endif
|
||||
|
||||
if (is_stdin)
|
||||
return buf_pos;
|
||||
|
||||
if (script_buftoenv(ctx, ctx->script_buf, (size_t)buf_pos) == NULL)
|
||||
goto eexit;
|
||||
|
||||
return buf_pos;
|
||||
|
||||
eexit:
|
||||
logerr(__func__);
|
||||
#ifndef HAVE_OPEN_MEMSTREAM
|
||||
if (fp != NULL)
|
||||
fclose(fp);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
send_interface1(struct fd_list *fd, const struct interface *ifp,
|
||||
const char *reason)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = ifp->ctx;
|
||||
long len;
|
||||
|
||||
len = make_env(ifp->ctx, ifp, reason);
|
||||
if (len == -1)
|
||||
return -1;
|
||||
return control_queue(fd, ctx->script_buf, (size_t)len);
|
||||
}
|
||||
|
||||
int
|
||||
send_interface(struct fd_list *fd, const struct interface *ifp, int af)
|
||||
{
|
||||
int retval = 0;
|
||||
#ifdef INET
|
||||
const struct dhcp_state *d;
|
||||
#endif
|
||||
#ifdef DHCP6
|
||||
const struct dhcp6_state *d6;
|
||||
#endif
|
||||
|
||||
#ifndef AF_LINK
|
||||
#define AF_LINK AF_PACKET
|
||||
#endif
|
||||
|
||||
if (af == AF_UNSPEC || af == AF_LINK) {
|
||||
const char *reason;
|
||||
|
||||
switch (ifp->carrier) {
|
||||
case LINK_UP:
|
||||
reason = "CARRIER";
|
||||
break;
|
||||
case LINK_DOWN:
|
||||
reason = "NOCARRIER";
|
||||
break;
|
||||
default:
|
||||
reason = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
if (fd != NULL) {
|
||||
if (send_interface1(fd, ifp, reason) == -1)
|
||||
retval = -1;
|
||||
} else
|
||||
retval++;
|
||||
}
|
||||
|
||||
#ifdef INET
|
||||
if (af == AF_UNSPEC || af == AF_INET) {
|
||||
if (D_STATE_RUNNING(ifp)) {
|
||||
d = D_CSTATE(ifp);
|
||||
if (fd != NULL) {
|
||||
if (send_interface1(fd, ifp, d->reason) == -1)
|
||||
retval = -1;
|
||||
} else
|
||||
retval++;
|
||||
}
|
||||
#ifdef IPV4LL
|
||||
if (IPV4LL_STATE_RUNNING(ifp)) {
|
||||
if (fd != NULL) {
|
||||
if (send_interface1(fd, ifp, "IPV4LL") == -1)
|
||||
retval = -1;
|
||||
} else
|
||||
retval++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef INET6
|
||||
if (af == AF_UNSPEC || af == AF_INET6) {
|
||||
if (IPV6_STATE_RUNNING(ifp)) {
|
||||
if (fd != NULL) {
|
||||
if (send_interface1(fd, ifp, "STATIC6") == -1)
|
||||
retval = -1;
|
||||
} else
|
||||
retval++;
|
||||
}
|
||||
if (RS_STATE_RUNNING(ifp)) {
|
||||
if (fd != NULL) {
|
||||
if (send_interface1(fd, ifp,
|
||||
"ROUTERADVERT") == -1)
|
||||
retval = -1;
|
||||
} else
|
||||
retval++;
|
||||
}
|
||||
#ifdef DHCP6
|
||||
if (D6_STATE_RUNNING(ifp)) {
|
||||
d6 = D6_CSTATE(ifp);
|
||||
if (fd != NULL) {
|
||||
if (send_interface1(fd, ifp, d6->reason) == -1)
|
||||
retval = -1;
|
||||
} else
|
||||
retval++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int
|
||||
script_status(const char *script, int status)
|
||||
{
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
if (WEXITSTATUS(status))
|
||||
logerrx("%s: %s: WEXITSTATUS %d",
|
||||
__func__, script, WEXITSTATUS(status));
|
||||
} else if (WIFSIGNALED(status))
|
||||
logerrx("%s: %s: %s",
|
||||
__func__, script, strsignal(WTERMSIG(status)));
|
||||
|
||||
return WEXITSTATUS(status);
|
||||
}
|
||||
|
||||
static int
|
||||
script_run(struct dhcpcd_ctx *ctx, char **argv)
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
|
||||
pid = script_exec(argv, ctx->script_env);
|
||||
if (pid == -1) {
|
||||
logerr("%s: %s", __func__, argv[0]);
|
||||
return -1;
|
||||
} else if (pid == 0)
|
||||
return 0;
|
||||
|
||||
/* Wait for the script to finish */
|
||||
while (waitpid(pid, &status, 0) == -1) {
|
||||
if (errno != EINTR) {
|
||||
logerr("%s: waitpid", __func__);
|
||||
status = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return script_status(argv[0], status);
|
||||
}
|
||||
|
||||
int
|
||||
script_dump(const char *env, size_t len)
|
||||
{
|
||||
const char *ep = env + len;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
if (*(ep - 1) != '\0') {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (; env < ep; env += strlen(env) + 1) {
|
||||
if (strncmp(env, "new_", 4) == 0)
|
||||
env += 4;
|
||||
printf("%s\n", env);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
script_runreason(const struct interface *ifp, const char *reason)
|
||||
{
|
||||
struct dhcpcd_ctx *ctx = ifp->ctx;
|
||||
char *argv[2];
|
||||
int status = 0;
|
||||
struct fd_list *fd;
|
||||
long buflen;
|
||||
|
||||
if (ctx->script == NULL &&
|
||||
TAILQ_FIRST(&ifp->ctx->control_fds) == NULL)
|
||||
return 0;
|
||||
|
||||
/* Make our env */
|
||||
if ((buflen = make_env(ifp->ctx, ifp, reason)) == -1) {
|
||||
logerr(__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strncmp(reason, "DUMP", 4) == 0)
|
||||
return script_dump(ctx->script_buf, (size_t)buflen);
|
||||
|
||||
if (ctx->script == NULL)
|
||||
goto send_listeners;
|
||||
|
||||
argv[0] = ctx->script;
|
||||
argv[1] = NULL;
|
||||
logdebugx("%s: executing: %s %s", ifp->name, argv[0], reason);
|
||||
|
||||
#ifdef PRIVSEP
|
||||
if (IN_PRIVSEP(ctx)) {
|
||||
ssize_t err;
|
||||
|
||||
err = ps_root_script(ctx, ctx->script_buf, (size_t)buflen);
|
||||
if (err == -1)
|
||||
logerr(__func__);
|
||||
else
|
||||
script_status(ctx->script, (int)err);
|
||||
goto send_listeners;
|
||||
}
|
||||
#endif
|
||||
|
||||
script_run(ctx, argv);
|
||||
|
||||
send_listeners:
|
||||
/* Send to our listeners */
|
||||
status = 0;
|
||||
TAILQ_FOREACH(fd, &ctx->control_fds, next) {
|
||||
if (!(fd->flags & FD_LISTEN))
|
||||
continue;
|
||||
if (control_queue(fd, ctx->script_buf, ctx->script_buflen)== -1)
|
||||
logerr("%s: control_queue", __func__);
|
||||
else
|
||||
status = 1;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* dhcpcd - DHCP client daemon
|
||||
* Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
|
||||
* All rights reserved
|
||||
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef SCRIPT_H
|
||||
#define SCRIPT_H
|
||||
|
||||
#include "control.h"
|
||||
|
||||
__printflike(2, 3) int efprintf(FILE *, const char *, ...);
|
||||
void if_printoptions(void);
|
||||
char ** script_buftoenv(struct dhcpcd_ctx *, char *, size_t);
|
||||
pid_t script_exec(char *const *, char *const *);
|
||||
int send_interface(struct fd_list *, const struct interface *, int);
|
||||
int script_dump(const char *, size_t);
|
||||
int script_runreason(const struct interface *, const char *);
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
Copyright (c) 2007-2020 Roy Marples <roy@marples.name>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
|
@ -0,0 +1,68 @@
|
|||
# openresolv
|
||||
|
||||
openresolv is a [resolvconf](https://en.wikipedia.org/wiki/Resolvconf)
|
||||
implementation which manages `/etc/resolv.conf`.
|
||||
|
||||
`/etc/resolv.conf` is a file that holds the configuration for the local
|
||||
resolution of domain names.
|
||||
Normally this file is either static or maintained by a local daemon,
|
||||
normally a DHCP daemon. But what happens if more than one thing wants to
|
||||
control the file?
|
||||
Say you have wired and wireless interfaces to different subnets and run a VPN
|
||||
or two on top of that, how do you say which one controls the file?
|
||||
It's also not as easy as just adding and removing the nameservers each client
|
||||
knows about as different clients could add the same nameservers.
|
||||
|
||||
Enter resolvconf, the middleman between the network configuration services and
|
||||
`/etc/resolv.conf`.
|
||||
resolvconf itself is just a script that stores, removes and lists a full
|
||||
`resolv.conf` generated for the interface. It then calls all the helper scripts
|
||||
it knows about so it can configure the real `/etc/resolv.conf` and optionally
|
||||
any local nameservers other than libc.
|
||||
|
||||
## Reasons for using openresolv
|
||||
|
||||
Why openresolv over the
|
||||
[Debian implementation](http://qref.sourceforge.net/Debian/reference/ch-gateway.en.html#s-dns-resolvconf)?
|
||||
Here's some reasons:
|
||||
* Works with
|
||||
[POSIX shell and userland](http://www.opengroup.org/onlinepubs/009695399)
|
||||
* Does not need awk, grep or sed which means we can work without `/usr`
|
||||
mounted
|
||||
* Works with other init systems than Debians' out of the box
|
||||
* Available as a 2 clause
|
||||
[BSD license](http://www.freebsd.org/copyright/freebsd-license.html)
|
||||
* Prefer configs via IF_METRIC for dynamic ordering
|
||||
* Configures zones for local resolvers other than libc
|
||||
|
||||
The last point is quite important, especially when running VPN systems.
|
||||
Take the following resolv.conf files which have been generated by a
|
||||
[DHCP client](https://github.com/NetworkConfiguration/dhcpcd) and sent to resolvconf:
|
||||
|
||||
```
|
||||
# resolv.conf from bge0
|
||||
search foo.com
|
||||
nameserver 1.2.3.4
|
||||
|
||||
# resolv.conf from tap0
|
||||
domain bar.org
|
||||
nameserver 5.6.7.8
|
||||
```
|
||||
|
||||
In this instance, queries for foo.com will go to 1.2.3.4 and queries for
|
||||
bar.org will go to 5.6.7.8.
|
||||
This does require the resolvers to be configured to pickup the resolvconf
|
||||
generated configuration for them though.
|
||||
openresolv ships with helpers for:
|
||||
* [unbound](http://www.unbound.net/)
|
||||
* [dnsmasq](http://www.thekelleys.org.uk/dnsmasq/doc.html)
|
||||
* [ISC BIND](http://www.isc.org/software/bind)
|
||||
* [PowerDNS Recursor](http://wiki.powerdns.com/trac)
|
||||
|
||||
See the
|
||||
[configuration section](https://roy.marples.name/projects/openresolv/configuration)
|
||||
for more details.
|
||||
|
||||
If openresolv updates `/etc/resolv.conf` it can notify the following of this:
|
||||
* [Bonjour (mdnsd)](https://developer.apple.com/bonjour/)
|
||||
* [avahi](http://www.avahi.org/)
|
|
@ -0,0 +1,32 @@
|
|||
#!/bin/sh
|
||||
# Copyright (c) 2007-2023 Roy Marples
|
||||
# All rights reserved
|
||||
|
||||
# avahi-daemon notifier for resolvconf libc subscriber
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * 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 COPYRIGHT HOLDERS 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 COPYRIGHT
|
||||
# OWNER 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.
|
||||
|
||||
: ${avahi_daemon_pidfile:=/var/run/avahi-daemon/pid}
|
||||
if [ -s "$avahi_daemon_pidfile" ]; then
|
||||
kill -HUP $(cat "$avahi_daemon_pidfile")
|
||||
fi
|
|
@ -0,0 +1,211 @@
|
|||
#!/bin/sh
|
||||
# Copyright (c) 2007-2023 Roy Marples
|
||||
# All rights reserved
|
||||
|
||||
# dnsmasq subscriber for resolvconf
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * 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 COPYRIGHT HOLDERS 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 COPYRIGHT
|
||||
# OWNER 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.
|
||||
|
||||
[ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0
|
||||
. "@SYSCONFDIR@/resolvconf.conf" || exit 1
|
||||
[ -z "${dnsmasq_conf}${dnsmasq_resolv}" ] && exit 0
|
||||
[ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)"
|
||||
NL="
|
||||
"
|
||||
|
||||
: ${dnsmasq_pid:=/var/run/dnsmasq.pid}
|
||||
[ -s "$dnsmasq_pid" ] || dnsmasq_pid=/var/run/dnsmasq/dnsmasq.pid
|
||||
[ -s "$dnsmasq_pid" ] || unset dnsmasq_pid
|
||||
: ${dnsmasq_service:=dnsmasq}
|
||||
newconf="# Generated by resolvconf$NL"
|
||||
newresolv="$newconf"
|
||||
|
||||
# Using dbus means that we never have to restart the daemon
|
||||
# This is important as it means we should not drop DNS queries
|
||||
# whilst changing DNS options around. However, dbus support is optional
|
||||
# so we need to validate a few things first.
|
||||
# Check for DBus support in the binary
|
||||
dbus=false
|
||||
dbus_ex=false
|
||||
dbus_introspect=$(dbus-send --print-reply --system \
|
||||
--dest=uk.org.thekelleys.dnsmasq \
|
||||
/uk/org/thekelleys/dnsmasq \
|
||||
org.freedesktop.DBus.Introspectable.Introspect \
|
||||
2>/dev/null)
|
||||
if [ $? = 0 ]; then
|
||||
dbus=true
|
||||
if printf %s "$dbus_introspect" | \
|
||||
grep -q '<method name="SetDomainServers">'
|
||||
then
|
||||
dbus_ex=true
|
||||
fi
|
||||
fi
|
||||
|
||||
for n in $NAMESERVERS; do
|
||||
newresolv="${newresolv}nameserver $n$NL"
|
||||
done
|
||||
|
||||
dbusdest=
|
||||
dbusdest_ex=
|
||||
conf=
|
||||
for d in $DOMAINS; do
|
||||
dn="${d%%:*}"
|
||||
ns="${d#*:}"
|
||||
while [ -n "$ns" ]; do
|
||||
n="${ns%%,*}"
|
||||
if $dbus && ! $dbus_ex; then
|
||||
case "$n" in
|
||||
*.*.*.*)
|
||||
SIFS=${IFS-y} OIFS=$IFS
|
||||
IFS=.
|
||||
set -- $n
|
||||
num="0x$(printf %02x $1 $2 $3 $4)"
|
||||
if [ "$SIFS" = y ]; then
|
||||
unset IFS
|
||||
else
|
||||
IFS=$OIFS
|
||||
fi
|
||||
dbusdest="$dbusdest uint32:$(printf %u $num)"
|
||||
dbusdest="$dbusdest string:$dn"
|
||||
;;
|
||||
*:*%*)
|
||||
# This version of dnsmasq won't accept
|
||||
# scoped IPv6 addresses
|
||||
dbus=false
|
||||
;;
|
||||
*:*)
|
||||
SIFS=${IFS-y} OIFS=$IFS bytes= front= back=
|
||||
empty=false i=0
|
||||
IFS=:
|
||||
set -- $n
|
||||
while [ -n "$1" ] || [ -n "$2" ]; do
|
||||
addr="$1"
|
||||
shift
|
||||
if [ -z "$addr" ]; then
|
||||
empty=true
|
||||
continue
|
||||
fi
|
||||
i=$((i + 1))
|
||||
while [ ${#addr} -lt 4 ]; do
|
||||
addr="0${addr}"
|
||||
done
|
||||
byte1="$(printf %d 0x${addr%??})"
|
||||
byte2="$(printf %d 0x${addr#??})"
|
||||
if $empty; then
|
||||
back="$back byte:$byte1 byte:$byte2"
|
||||
else
|
||||
front="$front byte:$byte1 byte:$byte2"
|
||||
fi
|
||||
done
|
||||
while [ $i != 8 ]; do
|
||||
i=$((i + 1))
|
||||
front="$front byte:0 byte:0"
|
||||
done
|
||||
front="${front}$back"
|
||||
if [ "$SIFS" = y ]; then
|
||||
unset IFS
|
||||
else
|
||||
IFS=$OIFS
|
||||
fi
|
||||
dbusdest="${dbusdest}$front string:$dn"
|
||||
;;
|
||||
*)
|
||||
if ! $dbus_ex; then
|
||||
dbus=false
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
dbusdest_ex="$dbusdest_ex${dbusdest_ex:+,}/$dn/$n"
|
||||
conf="${conf}server=/$dn/$n$NL"
|
||||
[ "$ns" = "${ns#*,}" ] && break
|
||||
ns="${ns#*,}"
|
||||
done
|
||||
done
|
||||
|
||||
if $dbus; then
|
||||
newconf="$newconf$NL# Domain specific servers will"
|
||||
newconf="$newconf be sent over dbus${NL}"
|
||||
else
|
||||
newconf="$newconf$conf"
|
||||
fi
|
||||
|
||||
# Try to ensure that config dirs exist
|
||||
if command -v config_mkdirs >/dev/null 2>&1; then
|
||||
config_mkdirs "$dnsmasq_conf" "$dnsmasq_resolv"
|
||||
else
|
||||
@SBINDIR@/resolvconf -D "$dnsmasq_conf" "$dnsmasq_resolv"
|
||||
fi
|
||||
|
||||
changed=false
|
||||
if [ -n "$dnsmasq_conf" ]; then
|
||||
if [ ! -f "$dnsmasq_conf" ] || \
|
||||
[ "$(cat "$dnsmasq_conf")" != "$(printf %s "$newconf")" ]
|
||||
then
|
||||
changed=true
|
||||
printf %s "$newconf" >"$dnsmasq_conf"
|
||||
fi
|
||||
fi
|
||||
if [ -n "$dnsmasq_resolv" ]; then
|
||||
# dnsmasq polls this file so no need to set changed=true
|
||||
if [ -f "$dnsmasq_resolv" ]; then
|
||||
if [ "$(cat "$dnsmasq_resolv")" != "$(printf %s "$newresolv")" ]
|
||||
then
|
||||
printf %s "$newresolv" >"$dnsmasq_resolv"
|
||||
fi
|
||||
else
|
||||
printf %s "$newresolv" >"$dnsmasq_resolv"
|
||||
fi
|
||||
fi
|
||||
|
||||
if $changed; then
|
||||
# dnsmasq does not re-read the configuration file on SIGHUP
|
||||
if [ -n "$dnsmasq_restart" ]; then
|
||||
eval $dnsmasq_restart
|
||||
elif [ -n "$RESTARTCMD" ]; then
|
||||
set -- ${dnsmasq_service}
|
||||
eval "$RESTARTCMD"
|
||||
else
|
||||
@SBINDIR@/resolvconf -r ${dnsmasq_service}
|
||||
fi
|
||||
fi
|
||||
if $dbus; then
|
||||
if [ -s "$dnsmasq_pid" ]; then
|
||||
$changed || kill -HUP $(cat "$dnsmasq_pid")
|
||||
fi
|
||||
# Send even if empty so old servers are cleared
|
||||
if $dbus_ex; then
|
||||
method=SetDomainServers
|
||||
if [ -n "$dbusdest_ex" ]; then
|
||||
dbusdest_ex="array:string:$dbusdest_ex"
|
||||
fi
|
||||
dbusdest="$dbusdest_ex"
|
||||
else
|
||||
method=SetServers
|
||||
fi
|
||||
dbus-send --system --dest=uk.org.thekelleys.dnsmasq \
|
||||
/uk/org/thekelleys/dnsmasq uk.org.thekelleys.$method \
|
||||
$dbusdest
|
||||
dbus-send --system --dest=uk.org.thekelleys.dnsmasq \
|
||||
/uk/org/thekelleys/dnsmasq uk.org.thekelleys.ClearCache
|
||||
fi
|
|
@ -0,0 +1,271 @@
|
|||
#!/bin/sh
|
||||
# Copyright (c) 2007-2023 Roy Marples
|
||||
# All rights reserved
|
||||
|
||||
# libc subscriber for resolvconf
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * 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 COPYRIGHT HOLDERS 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 COPYRIGHT
|
||||
# OWNER 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.
|
||||
|
||||
SYSCONFDIR=@SYSCONFDIR@
|
||||
LIBEXECDIR=@LIBEXECDIR@
|
||||
VARDIR=@VARDIR@
|
||||
IFACEDIR="$VARDIR/interfaces"
|
||||
NL="
|
||||
"
|
||||
|
||||
# sed may not be available, and this is faster on small files
|
||||
key_get_value()
|
||||
{
|
||||
key="$1"
|
||||
shift
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
while read -r line; do
|
||||
case "$line" in
|
||||
"$key"*) echo "${line##$key}";;
|
||||
esac
|
||||
done
|
||||
else
|
||||
for x do
|
||||
while read -r line; do
|
||||
case "$line" in
|
||||
"$key"*) echo "${line##$key}";;
|
||||
esac
|
||||
done < "$x"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
keys_remove()
|
||||
{
|
||||
while read -r line; do
|
||||
found=false
|
||||
for key do
|
||||
case "$line" in
|
||||
"$key"*|"#"*|" "*|" "*|"") found=true;;
|
||||
esac
|
||||
$found && break
|
||||
done
|
||||
$found || echo "$line"
|
||||
done
|
||||
}
|
||||
|
||||
local_nameservers="127.* 0.0.0.0 255.255.255.255 ::1"
|
||||
|
||||
# Support original resolvconf configuration layout
|
||||
# as well as the openresolv config file
|
||||
if [ -f "$SYSCONFDIR"/resolvconf.conf ]; then
|
||||
. "$SYSCONFDIR"/resolvconf.conf
|
||||
elif [ -d "$SYSCONFDIR"/resolvconf ]; then
|
||||
SYSCONFDIR="$SYSCONFDIR/resolvconf"
|
||||
base="$SYSCONFDIR/resolv.conf.d/base"
|
||||
if [ -f "$base" ]; then
|
||||
prepend_nameservers="$(key_get_value "nameserver " "$base")"
|
||||
domain="$(key_get_value "domain " "$base")"
|
||||
prepend_search="$(key_get_value "search " "$base")"
|
||||
resolv_conf_options="$(key_get_value "options " "$base")"
|
||||
resolv_conf_sortlist="$(key_get_value "sortlist " "$base")"
|
||||
fi
|
||||
if [ -f "$SYSCONFDIR"/resolv.conf.d/head ]; then
|
||||
resolv_conf_head="$(cat "${SYSCONFDIR}"/resolv.conf.d/head)"
|
||||
fi
|
||||
if [ -f "$SYSCONFDIR"/resolv.conf.d/tail ]; then
|
||||
resolv_conf_tail="$(cat "$SYSCONFDIR"/resolv.conf.d/tail)"
|
||||
fi
|
||||
fi
|
||||
: ${resolv_conf:=/etc/resolv.conf}
|
||||
: ${resolv_conf_tmp:="$resolv_conf.$$.openresolv"}
|
||||
: ${libc_service:=nscd}
|
||||
: ${list_resolv:=@SBINDIR@/resolvconf -l}
|
||||
if [ "${resolv_conf_head-x}" = x ] && [ -f "$SYSCONFDIR"/resolv.conf.head ]
|
||||
then
|
||||
resolv_conf_head="$(cat "${SYSCONFDIR}"/resolv.conf.head)"
|
||||
fi
|
||||
if [ "${resolv_conf_tail-x}" = x ] && [ -f "$SYSCONFDIR"/resolv.conf.tail ]
|
||||
then
|
||||
resolv_conf_tail="$(cat "$SYSCONFDIR"/resolv.conf.tail)"
|
||||
fi
|
||||
|
||||
backup=true
|
||||
signature="# Generated by resolvconf"
|
||||
|
||||
uniqify()
|
||||
{
|
||||
result=
|
||||
while [ -n "$1" ]; do
|
||||
case " $result " in
|
||||
*" $1 "*);;
|
||||
*) result="$result $1";;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
echo "${result# *}"
|
||||
}
|
||||
|
||||
case "${resolv_conf_passthrough:-NO}" in
|
||||
[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
|
||||
backup=false
|
||||
newest=
|
||||
for conf in "$IFACEDIR"/*; do
|
||||
if [ -z "$newest" ] || [ "$conf" -nt "$newest" ]; then
|
||||
newest="$conf"
|
||||
fi
|
||||
done
|
||||
[ -z "$newest" ] && exit 0
|
||||
newconf="$(cat "$newest")$NL"
|
||||
;;
|
||||
/dev/null|[Nn][Uu][Ll][Ll])
|
||||
: ${resolv_conf_local_only:=NO}
|
||||
if [ "$local_nameservers" = "127.* 0.0.0.0 255.255.255.255 ::1" ]; then
|
||||
local_nameservers=
|
||||
fi
|
||||
# Need to overwrite our variables.
|
||||
eval "$(@SBINDIR@/resolvconf -V)"
|
||||
;;
|
||||
|
||||
*)
|
||||
[ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)"
|
||||
;;
|
||||
esac
|
||||
case "${resolv_conf_passthrough:-NO}" in
|
||||
[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;;
|
||||
*)
|
||||
: ${domain:=$DOMAIN}
|
||||
newsearch="$(uniqify $prepend_search $SEARCH $append_search)"
|
||||
NS="$LOCALNAMESERVERS $NAMESERVERS"
|
||||
newns=
|
||||
gotlocal=false
|
||||
for n in $(uniqify $prepend_nameservers $NS $append_nameservers); do
|
||||
add=true
|
||||
islocal=false
|
||||
for l in $local_nameservers; do
|
||||
case "$n" in
|
||||
$l) islocal=true; gotlocal=true; break;;
|
||||
esac
|
||||
done
|
||||
if ! $islocal; then
|
||||
case "${resolv_conf_local_only:-YES}" in
|
||||
[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
|
||||
$gotlocal && add=false;;
|
||||
esac
|
||||
fi
|
||||
$add && newns="$newns $n"
|
||||
done
|
||||
|
||||
# Hold our new resolv.conf in a variable to save on temporary files
|
||||
newconf="$signature$NL"
|
||||
if [ -n "$resolv_conf_head" ]; then
|
||||
newconf="$newconf$resolv_conf_head$NL"
|
||||
fi
|
||||
|
||||
[ -n "$domain" ] && newconf="${newconf}domain $domain$NL"
|
||||
if [ -n "$newsearch" ] && [ "$newsearch" != "$domain" ]; then
|
||||
newconf="${newconf}search $newsearch$NL"
|
||||
fi
|
||||
for n in $newns; do
|
||||
newconf="${newconf}nameserver $n$NL"
|
||||
done
|
||||
|
||||
# Now add anything we don't care about such as sortlist and options
|
||||
stuff="$($list_resolv | keys_remove nameserver domain search)"
|
||||
if [ -n "$stuff" ]; then
|
||||
newconf="$newconf$stuff$NL"
|
||||
fi
|
||||
|
||||
# Append any user defined ones
|
||||
if [ -n "$resolv_conf_options" ]; then
|
||||
newconf="${newconf}options $resolv_conf_options$NL"
|
||||
fi
|
||||
if [ -n "$resolv_conf_sortlist" ]; then
|
||||
newconf="${newconf}sortlist $resolv_conf_sortlist$NL"
|
||||
fi
|
||||
|
||||
if [ -n "$resolv_conf_tail" ]; then
|
||||
newconf="$newconf$resolv_conf_tail$NL"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# Check if the file has actually changed or not
|
||||
if [ -e "$resolv_conf" ]; then
|
||||
[ "$(cat "$resolv_conf")" = "$(printf %s "$newconf")" ] && exit 0
|
||||
fi
|
||||
|
||||
# Change is good.
|
||||
# If the old file does not have our signature, back it up.
|
||||
# If the new file just has our signature, restore the backup.
|
||||
if $backup; then
|
||||
if [ "$newconf" = "$signature$NL" ]; then
|
||||
if [ -e "$resolv_conf.bak" ]; then
|
||||
newconf="$(cat "$resolv_conf.bak")$NL"
|
||||
fi
|
||||
elif [ -e "$resolv_conf" ]; then
|
||||
read line <"$resolv_conf"
|
||||
if [ "$line" != "$signature" ]; then
|
||||
cp "$resolv_conf" "$resolv_conf.bak"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# There are pros and cons for writing directly to resolv.conf
|
||||
# instead of a temporary file and then moving it over.
|
||||
# The default is to write to resolv.conf as it has the least
|
||||
# issues and has been the long standing default behaviour.
|
||||
case "${resolv_conf_mv:-NO}" in
|
||||
[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
|
||||
# Protect against symlink attack, ensure new file does not exist
|
||||
rm -f "$resolv_conf_tmp"
|
||||
# Keep original file owner, group and mode
|
||||
[ -r "$resolv_conf" ] && cp -p "$resolv_conf" "$resolv_conf_tmp"
|
||||
# Create our resolv.conf now
|
||||
if (umask 022; printf %s "$newconf" >"$resolv_conf_tmp"); then
|
||||
mv "$resolv_conf_tmp" "$resolv_conf"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
(umask 022; printf %s "$newconf" >"$resolv_conf")
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -n "$libc_restart" ]; then
|
||||
eval $libc_restart
|
||||
elif [ -n "$RESTARTCMD" ]; then
|
||||
set -- ${libc_service}
|
||||
eval "$RESTARTCMD"
|
||||
else
|
||||
@SBINDIR@/resolvconf -r ${libc_service}
|
||||
fi
|
||||
|
||||
retval=0
|
||||
# Notify users of the resolver
|
||||
for script in "$LIBEXECDIR"/libc.d/*; do
|
||||
if [ -f "$script" ]; then
|
||||
if [ -x "$script" ]; then
|
||||
"$script" "$@"
|
||||
else
|
||||
(. "$script")
|
||||
fi
|
||||
retval=$(($retval + $?))
|
||||
fi
|
||||
done
|
||||
exit $retval
|
|
@ -0,0 +1,32 @@
|
|||
#!/bin/sh
|
||||
# Copyright (c) 2007-2023 Roy Marples
|
||||
# All rights reserved
|
||||
|
||||
# mdnsd notifier for resolvconf libc subscriber
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * 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 COPYRIGHT HOLDERS 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 COPYRIGHT
|
||||
# OWNER 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.
|
||||
|
||||
: ${mdnsd_pidfile:=/var/run/mdnsd/mdnsd.pid}
|
||||
if [ -s "$mdnsd_pidfile" ]; then
|
||||
kill -HUP $(cat "$mdnsd_pidfile")
|
||||
fi
|
|
@ -0,0 +1,118 @@
|
|||
#!/bin/sh
|
||||
# Copyright (c) 2007-2023 Roy Marples
|
||||
# All rights reserved
|
||||
|
||||
# named subscriber for resolvconf
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * 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 COPYRIGHT HOLDERS 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 COPYRIGHT
|
||||
# OWNER 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.
|
||||
|
||||
[ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0
|
||||
. "@SYSCONFDIR@/resolvconf.conf" || exit 1
|
||||
[ -z "${named_zones}${named_options}" ] && exit 0
|
||||
[ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)"
|
||||
NL="
|
||||
"
|
||||
|
||||
# Platform specific kludges
|
||||
if [ -z "${named_service}${named_restart}" ] &&
|
||||
[ -d "$RCDIR" ] && ! [ -x "$RCDIR"/named ]
|
||||
then
|
||||
if [ -x "$RCDIR"/bind9 ]; then
|
||||
# Debian and derivatives
|
||||
named_service=bind9
|
||||
elif [ -x "$RCDIR"/rc.bind ]; then
|
||||
# Slackware
|
||||
named_service=rc.bind
|
||||
fi
|
||||
fi
|
||||
: ${named_service:=named}
|
||||
|
||||
: ${named_pid:=/var/run/$named_service.pid}
|
||||
[ -s "$named_pid" ] || named_pid=/var/run/$named_service/$named_service.pid
|
||||
[ -s "$named_pid" ] || unset named_pid
|
||||
|
||||
newoptions="# Generated by resolvconf$NL"
|
||||
newzones="$newoptions"
|
||||
|
||||
forward=
|
||||
for n in $NAMESERVERS; do
|
||||
case "$forward" in
|
||||
*"$NL $n;"*);;
|
||||
*) forward="$forward$NL $n;";;
|
||||
esac
|
||||
done
|
||||
if [ -n "$forward" ]; then
|
||||
newoptions="${newoptions}forward first;${NL}forwarders {$forward${NL}};$NL"
|
||||
fi
|
||||
|
||||
for d in $DOMAINS; do
|
||||
newzones="${newzones}zone \"${d%%:*}\" {$NL"
|
||||
newzones="$newzones type forward;$NL"
|
||||
newzones="$newzones forward first;$NL forwarders {$NL"
|
||||
ns="${d#*:}"
|
||||
while [ -n "$ns" ]; do
|
||||
newzones="$newzones ${ns%%,*};$NL"
|
||||
[ "$ns" = "${ns#*,}" ] && break
|
||||
ns="${ns#*,}"
|
||||
done
|
||||
newzones="$newzones };$NL};$NL"
|
||||
done
|
||||
|
||||
# Try to ensure that config dirs exist
|
||||
if command -v config_mkdirs >/dev/null 2>&1; then
|
||||
config_mkdirs "$named_options" "$named_zones"
|
||||
else
|
||||
@SBINDIR@/resolvconf -D "$named_options" "$named_zones"
|
||||
fi
|
||||
|
||||
# No point in changing files or reloading bind if the end result has not
|
||||
# changed
|
||||
changed=false
|
||||
if [ -n "$named_options" ]; then
|
||||
if [ ! -f "$named_options" ] || \
|
||||
[ "$(cat "$named_options")" != "$(printf %s "$newoptions")" ]
|
||||
then
|
||||
printf %s "$newoptions" >"$named_options"
|
||||
changed=true
|
||||
fi
|
||||
fi
|
||||
if [ -n "$named_zones" ]; then
|
||||
if [ ! -f "$named_zones" ] || \
|
||||
[ "$(cat "$named_zones")" != "$(printf %s "$newzones")" ]
|
||||
then
|
||||
printf %s "$newzones" >"$named_zones"
|
||||
changed=true
|
||||
fi
|
||||
fi
|
||||
|
||||
# named does not seem to work with SIGHUP which is a same
|
||||
if $changed; then
|
||||
if [ -n "$named_restart" ]; then
|
||||
eval $named_restart
|
||||
elif [ -n "$RESTARTCMD" ]; then
|
||||
set -- ${named_service}
|
||||
eval "$RESTARTCMD"
|
||||
else
|
||||
@SBINDIR@/resolvconf -r ${named_service}
|
||||
fi
|
||||
fi
|
|
@ -0,0 +1,75 @@
|
|||
#!/bin/sh
|
||||
# Copyright (c) 2009-2023 Roy Marples
|
||||
# All rights reserved
|
||||
|
||||
# PowerDNS Recursor subscriber for resolvconf
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * 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 COPYRIGHT HOLDERS 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 COPYRIGHT
|
||||
# OWNER 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.
|
||||
|
||||
[ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0
|
||||
. "@SYSCONFDIR@/resolvconf.conf" || exit 1
|
||||
[ -z "$pdns_zones" ] && exit 0
|
||||
[ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)"
|
||||
NL="
|
||||
"
|
||||
|
||||
: ${pdns_service:=pdns-recursor}
|
||||
|
||||
newzones=
|
||||
|
||||
for n in $NAMESERVERS; do
|
||||
newzones="$newzones${newzones:+,}$n"
|
||||
done
|
||||
[ -n "$newzones" ] && newzones="+.=$newzones$NL"
|
||||
|
||||
for d in $DOMAINS; do
|
||||
newns=
|
||||
ns="${d#*:}"
|
||||
while [ -n "$ns" ]; do
|
||||
newns="$newns${newns:+,}${ns%%,*}"
|
||||
[ "$ns" = "${ns#*,}" ] && break
|
||||
ns="${ns#*,}"
|
||||
done
|
||||
[ -n "$newns" ] && newzones="$newzones${d%%:*}=$newns$NL"
|
||||
done
|
||||
|
||||
# Try to ensure that config dirs exist
|
||||
if command -v config_mkdirs >/dev/null 2>&1; then
|
||||
config_mkdirs "$pdnsd_zones"
|
||||
else
|
||||
@SBINDIR@/resolvconf -D "$pdnsd_zones"
|
||||
fi
|
||||
|
||||
if [ ! -f "$pdns_zones" ] || \
|
||||
[ "$(cat "$pdns_zones")" != "$(printf %s "$newzones")" ]
|
||||
then
|
||||
printf %s "$newzones" >"$pdns_zones"
|
||||
if [ -n "$pdns_restart" ]; then
|
||||
eval $pdns_restart
|
||||
elif [ -n "$RESTARTCMD" ]; then
|
||||
set -- ${pdns_service}
|
||||
eval "$RESTARTCMD"
|
||||
else
|
||||
@SBINDIR@/resolvconf -r ${pdns_service}
|
||||
fi
|
||||
fi
|
|
@ -0,0 +1,165 @@
|
|||
#!/bin/sh
|
||||
# Copyright (c) 2010-2023 Roy Marples
|
||||
# All rights reserved
|
||||
|
||||
# pdnsd subscriber for resolvconf
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * 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 COPYRIGHT HOLDERS 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 COPYRIGHT
|
||||
# OWNER 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.
|
||||
|
||||
[ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0
|
||||
. "@SYSCONFDIR@/resolvconf.conf" || exit 1
|
||||
[ -z "${pdnsd_conf}${pdnsd_resolv}" ] && exit 0
|
||||
[ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)"
|
||||
NL="
|
||||
"
|
||||
|
||||
: ${pdnsd_restart:=pdnsd-ctl config $pdnsd_conf}
|
||||
signature="# Generated by resolvconf"
|
||||
signature_end="# End of resolvconf"
|
||||
|
||||
# We normally use sed to remove markers from a configuration file
|
||||
# but sed may not always be available at the time.
|
||||
remove_markers()
|
||||
{
|
||||
m1="$1"
|
||||
m2="$2"
|
||||
in_marker=0
|
||||
|
||||
shift; shift
|
||||
if command -v sed >/dev/null 2>&1; then
|
||||
sed "/^$m1/,/^$m2/d" $@
|
||||
else
|
||||
for x do
|
||||
while read line; do
|
||||
case "$line" in
|
||||
"$m1"*) in_marker=1;;
|
||||
"$m2"*) in_marker=0;;
|
||||
*) [ $in_marker = 0 ] && echo "$line";;
|
||||
esac
|
||||
done < "$x"
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# Compare two files
|
||||
# If different, replace first with second otherwise remove second
|
||||
change_file()
|
||||
{
|
||||
if [ -e "$1" ]; then
|
||||
if command -v cmp >/dev/null 2>&1; then
|
||||
cmp -s "$1" "$2"
|
||||
elif command -v diff >/dev/null 2>&1; then
|
||||
diff -q "$1" "$2" >/dev/null
|
||||
else
|
||||
# Hopefully we're only working on small text files ...
|
||||
[ "$(cat "$1")" = "$(cat "$2")" ]
|
||||
fi
|
||||
if [ $? -eq 0 ]; then
|
||||
rm -f "$2"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
cat "$2" > "$1"
|
||||
rm -f "$2"
|
||||
return 0
|
||||
}
|
||||
|
||||
newresolv="# Generated by resolvconf$NL"
|
||||
changed=false
|
||||
|
||||
# Try to ensure that config dirs exist
|
||||
if command -v config_mkdirs >/dev/null 2>&1; then
|
||||
config_mkdirs "$pdnsd_resolv" "$pdnsd_conf"
|
||||
else
|
||||
@SBINDIR@/resolvconf -D "$pdnsd_resolv" "$pdnsd_conf"
|
||||
fi
|
||||
|
||||
if [ -n "$pdnsd_resolv" ]; then
|
||||
for n in $NAMESERVERS; do
|
||||
newresolv="${newresolv}nameserver $n$NL"
|
||||
done
|
||||
fi
|
||||
|
||||
# Only modify the configuration if it exists and we can write to it
|
||||
if [ -w "$pdnsd_conf" ]; then
|
||||
cf="$pdnsd_conf.new"
|
||||
newconf=
|
||||
|
||||
if [ -z "$pdnsd_resolv" ]; then
|
||||
newconf="${newconf}server {$NL"
|
||||
newconf="${newconf} label=resolvconf;$NL"
|
||||
if [ -n "$NAMESERVERS" ]; then
|
||||
newconf="${newconf} ip="
|
||||
first=true
|
||||
for n in $NAMESERVERS; do
|
||||
if $first; then
|
||||
first=false
|
||||
else
|
||||
newconf="${newconf},"
|
||||
fi
|
||||
newconf="$newconf$n"
|
||||
done
|
||||
newconf="${newconf};$NL"
|
||||
fi
|
||||
newconf="${newconf}}$NL"
|
||||
fi
|
||||
|
||||
for d in $DOMAINS; do
|
||||
newconf="${newconf}server {$NL"
|
||||
newconf="${newconf} include=.${d%%:*}.;$NL"
|
||||
newconf="${newconf} policy=excluded;$NL"
|
||||
newconf="${newconf} ip="
|
||||
ns="${d#*:}"
|
||||
while [ -n "$ns" ]; do
|
||||
newconf="${newconf}${ns%%,*}"
|
||||
[ "$ns" = "${ns#*,}" ] && break
|
||||
ns="${ns#*,}"
|
||||
newconf="${newconf},"
|
||||
done
|
||||
newconf="${newconf};$NL}$NL"
|
||||
done
|
||||
|
||||
rm -f "$cf"
|
||||
remove_markers "$signature" "$signature_end" "$pdnsd_conf" > "$cf"
|
||||
if [ -n "$newconf" ]; then
|
||||
echo "$signature" >> "$cf"
|
||||
printf %s "$newconf" >> "$cf"
|
||||
echo "$signature_end" >> "$cf"
|
||||
fi
|
||||
if change_file "$pdnsd_conf" "$cf"; then
|
||||
changed=true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$pdnsd_resolv" ]; then
|
||||
if [ ! -f "$pdnsd_resolv" ] || \
|
||||
[ "$(cat "$pdnsd_resolv")" != "$(printf %s "$newresolv")" ]
|
||||
then
|
||||
changed=true
|
||||
printf %s "$newresolv" >"$pdnsd_resolv"
|
||||
fi
|
||||
fi
|
||||
|
||||
if $changed; then
|
||||
eval $pdnsd_restart
|
||||
fi
|
|
@ -0,0 +1,336 @@
|
|||
.\" Copyright (c) 2007-2023 Roy Marples
|
||||
.\" All rights reserved
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd December 23, 2016
|
||||
.Dt RESOLVCONF 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm resolvconf
|
||||
.Nd a framework for managing multiple DNS configurations
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Fl I
|
||||
.Nm
|
||||
.Op Fl m Ar metric
|
||||
.Op Fl p
|
||||
.Op Fl x
|
||||
.Fl a Ar interface Ns Op Ar .protocol
|
||||
.No < Ns Pa file
|
||||
.Nm
|
||||
.Fl C Ar pattern
|
||||
.Nm
|
||||
.Fl c Ar pattern
|
||||
.Nm
|
||||
.Op Fl f
|
||||
.Fl d Ar interface Ns Op Ar .protocol
|
||||
.Nm
|
||||
.Op Fl x
|
||||
.Fl il Ar pattern
|
||||
.Nm
|
||||
.Fl u
|
||||
.Nm
|
||||
.Fl Fl version
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
manages
|
||||
.Xr resolv.conf 5
|
||||
files from multiple sources, such as DHCP and VPN clients.
|
||||
Traditionally, the host runs just one client and that updates
|
||||
.Pa /etc/resolv.conf .
|
||||
More modern systems frequently have wired and wireless interfaces and there is
|
||||
no guarantee both are on the same network.
|
||||
With the advent of VPN and other
|
||||
types of networking daemons, many things now contend for the contents of
|
||||
.Pa /etc/resolv.conf .
|
||||
.Pp
|
||||
.Nm
|
||||
solves this by letting the daemon send their
|
||||
.Xr resolv.conf 5
|
||||
file to
|
||||
.Nm
|
||||
via
|
||||
.Xr stdin 4
|
||||
with the argument
|
||||
.Fl a Ar interface Ns Op Ar .protocol
|
||||
instead of the filesystem.
|
||||
.Nm
|
||||
then updates
|
||||
.Pa /etc/resolv.conf
|
||||
as it thinks best.
|
||||
When a local resolver other than libc is installed, such as
|
||||
.Xr dnsmasq 8
|
||||
or
|
||||
.Xr named 8 ,
|
||||
then
|
||||
.Nm
|
||||
will supply files that the resolver should be configured to include.
|
||||
.Pp
|
||||
.Nm
|
||||
assumes it has a job to do.
|
||||
In some situations
|
||||
.Nm
|
||||
needs to act as a deterrent to writing to
|
||||
.Pa /etc/resolv.conf .
|
||||
Where this file cannot be made immutable or you just need to toggle this
|
||||
behaviour,
|
||||
.Nm
|
||||
can be disabled by adding
|
||||
.Sy resolvconf Ns = Ns NO
|
||||
to
|
||||
.Xr resolvconf.conf 5 .
|
||||
.Pp
|
||||
.Nm
|
||||
can mark an interfaces
|
||||
.Pa resolv.conf
|
||||
as private.
|
||||
This means that the name servers listed in that
|
||||
.Pa resolv.conf
|
||||
are only used for queries against the domain/search listed in the same file.
|
||||
This only works when a local resolver other than libc is installed.
|
||||
See
|
||||
.Xr resolvconf.conf 5
|
||||
for how to configure
|
||||
.Nm
|
||||
to use a local name server and how to remove the private marking.
|
||||
.Pp
|
||||
.Nm
|
||||
can mark an interfaces
|
||||
.Pa resolv.conf
|
||||
as exclusive.
|
||||
Only the latest exclusive interface is used for processing, otherwise all are.
|
||||
.Pp
|
||||
When an interface goes down, it should then call
|
||||
.Nm
|
||||
with
|
||||
.Fl d Ar interface.*
|
||||
arguments to delete the
|
||||
.Pa resolv.conf
|
||||
file(s) for all the
|
||||
.Ar protocols
|
||||
on the
|
||||
.Ar interface .
|
||||
For systems that support the concept of persisting configuration when
|
||||
the carrier goes down, then it should instead call
|
||||
.Nm
|
||||
with
|
||||
.Fl C Ar interface.*
|
||||
arguments to deprecate the matching interfaces and
|
||||
.Fl c Ar interface.*
|
||||
to activate the matching interfaces when the carrier comes up.
|
||||
This only affects the order in which interfaces are processed.
|
||||
.Pp
|
||||
Here are some options for the above commands:-
|
||||
.Bl -tag -width pattern_opt
|
||||
.It Fl f
|
||||
Ignore non existent interfaces.
|
||||
Only really useful for deleting interfaces.
|
||||
.It Fl m Ar metric
|
||||
Set the metric of the interface when adding it, default of 0.
|
||||
Lower metrics take precedence.
|
||||
This affects the default order of interfaces when listed.
|
||||
.It Fl p
|
||||
Marks the interface
|
||||
.Pa resolv.conf
|
||||
as private.
|
||||
.It Fl x
|
||||
Mark the interface
|
||||
.Pa resolv.conf
|
||||
as exclusive when adding, otherwise only use the latest exclusive interface.
|
||||
.El
|
||||
.Pp
|
||||
.Nm
|
||||
has some more commands for general usage:-
|
||||
.Bl -tag -width pattern_opt
|
||||
.It Fl i Ar pattern
|
||||
List the interfaces and protocols, optionally matching
|
||||
.Ar pattern ,
|
||||
we have
|
||||
.Pa resolv.conf
|
||||
files for.
|
||||
.It Fl l Ar pattern
|
||||
List the
|
||||
.Pa resolv.conf
|
||||
files we have.
|
||||
If
|
||||
.Ar pattern
|
||||
is specified then we list the files for the interfaces and protocols
|
||||
that match it.
|
||||
.It Fl u
|
||||
Force
|
||||
.Nm
|
||||
to update all its subscribers.
|
||||
.Nm
|
||||
does not update the subscribers when adding a resolv.conf that matches
|
||||
what it already has for that interface.
|
||||
.It Fl Fl version
|
||||
Echo the resolvconf version to
|
||||
.Em stdout .
|
||||
.El
|
||||
.Pp
|
||||
.Nm
|
||||
also has some commands designed to be used by its subscribers and
|
||||
system startup:-
|
||||
.Bl -tag -width pattern_opt
|
||||
.It Fl I
|
||||
Initialise the state directory
|
||||
.Pa @VARDIR@ .
|
||||
This only needs to be called if the initial system boot sequence does not
|
||||
automatically clean it out; for example the state directory is moved
|
||||
somewhere other than
|
||||
.Pa /var/run .
|
||||
If used, it should only be called once as early in the system boot sequence
|
||||
as possible and before
|
||||
.Nm
|
||||
is used to add interfaces.
|
||||
.It Fl R
|
||||
Echo the command used to restart a service.
|
||||
.It Fl r Ar service
|
||||
If the
|
||||
.Ar service
|
||||
is running then restart it.
|
||||
If the service does not exist or is not running then zero is returned,
|
||||
otherwise the result of restarting the service.
|
||||
.It Fl v
|
||||
Echo variables DOMAINS, SEARCH and NAMESERVERS so that the subscriber can
|
||||
configure the resolver easily.
|
||||
.It Fl V
|
||||
Same as
|
||||
.Fl v
|
||||
except that only the information configured in
|
||||
.Xr resolvconf.conf 5
|
||||
is set.
|
||||
.El
|
||||
.Sh INTERFACE ORDERING
|
||||
For
|
||||
.Nm
|
||||
to work effectively, it has to process the resolv.confs for the interfaces
|
||||
in the correct order.
|
||||
.Nm
|
||||
first processes interfaces from the
|
||||
.Sy interface_order
|
||||
list, then interfaces without a metric and that match the
|
||||
.Sy dynamic_order
|
||||
list, then interfaces with a metric in order and finally the rest in
|
||||
the operating systems lexical order.
|
||||
See
|
||||
.Xr resolvconf.conf 5
|
||||
for details on these lists.
|
||||
.Sh PROTOCOLS
|
||||
Here are some suggested protocol tags to use for each
|
||||
.Pa resolv.conf
|
||||
file registered on an
|
||||
.Ar interface Ns No :-
|
||||
.Bl -tag -width pattern_opt
|
||||
.It dhcp
|
||||
Dynamic Host Configuration Protocol.
|
||||
Initial versions of
|
||||
.Nm
|
||||
did not recommend a
|
||||
.Ar protocol
|
||||
tag be appended to the
|
||||
.Ar interface
|
||||
name.
|
||||
When the protocol is absent, it is assumed to be the DHCP protocol.
|
||||
.It ppp
|
||||
Point-to-Point Protocol.
|
||||
.It ra
|
||||
IPv6 Router Advertisement.
|
||||
.It dhcp6
|
||||
Dynamic Host Configuration Protocol, version 6.
|
||||
.El
|
||||
.Sh IMPLEMENTATION NOTES
|
||||
If a subscriber has the executable bit then it is executed otherwise it is
|
||||
assumed to be a shell script and sourced into the current environment in a
|
||||
subshell.
|
||||
This is done so that subscribers can remain fast, but are also not limited
|
||||
to the shell language.
|
||||
.Pp
|
||||
Portable subscribers should not use anything outside of
|
||||
.Pa /bin
|
||||
and
|
||||
.Pa /sbin
|
||||
because
|
||||
.Pa /usr
|
||||
and others may not be available when booting.
|
||||
Also, it would be unwise to assume any shell specific features.
|
||||
.Sh ENVIRONMENT
|
||||
.Bl -ohang
|
||||
.It Va IF_METRIC
|
||||
If the
|
||||
.Fl m
|
||||
option is not present then we use
|
||||
.Va IF_METRIC
|
||||
for the metric.
|
||||
.It Va IF_PRIVATE
|
||||
Marks the interface
|
||||
.Pa resolv.conf
|
||||
as private.
|
||||
.It Va IF_EXCLUSIVE
|
||||
Marks the interface
|
||||
.Pa resolv.conf
|
||||
as exclusive.
|
||||
.El
|
||||
.Sh FILES
|
||||
.Bl -ohang
|
||||
.It Pa /etc/resolv.conf.bak
|
||||
Backup file of the original resolv.conf.
|
||||
.It Pa @SYSCONFDIR@/resolvconf.conf
|
||||
Configuration file for
|
||||
.Nm .
|
||||
.It Pa @LIBEXECDIR@
|
||||
Directory of subscribers which are run every time
|
||||
.Nm
|
||||
adds, deletes or updates.
|
||||
.It Pa @LIBEXECDIR@/libc.d
|
||||
Directory of subscribers which are run after the libc subscriber is run.
|
||||
.It Pa @VARDIR@
|
||||
State directory for
|
||||
.Nm .
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr resolver 3 ,
|
||||
.Xr stdin 4 ,
|
||||
.Xr resolv.conf 5 ,
|
||||
.Xr resolvconf.conf 5
|
||||
.Sh HISTORY
|
||||
This implementation of
|
||||
.Nm
|
||||
is called openresolv and is fully command line compatible with Debian's
|
||||
resolvconf, as written by Thomas Hood.
|
||||
.Sh AUTHORS
|
||||
.An Roy Marples Aq Mt roy@marples.name
|
||||
.Sh BUGS
|
||||
Please report them to
|
||||
.Lk http://roy.marples.name/projects/openresolv
|
||||
.Pp
|
||||
.Nm
|
||||
does not validate any of the files given to it.
|
||||
.Pp
|
||||
When running a local resolver other than libc, you will need to configure it
|
||||
to include files that
|
||||
.Nm
|
||||
will generate.
|
||||
You should consult
|
||||
.Xr resolvconf.conf 5
|
||||
for instructions on how to configure your resolver.
|
|
@ -0,0 +1,7 @@
|
|||
# Configuration for resolvconf(8)
|
||||
# See resolvconf.conf(5) for details
|
||||
|
||||
resolv_conf=/etc/resolv.conf
|
||||
# If you run a local name server, you should uncomment the below line and
|
||||
# configure your subscribers configuration files below.
|
||||
#name_servers=127.0.0.1
|
|
@ -0,0 +1,397 @@
|
|||
.\" Copyright (c) 2009-2023 Roy Marples
|
||||
.\" All rights reserved
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd May 23, 2023
|
||||
.Dt RESOLVCONF.CONF 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm resolvconf.conf
|
||||
.Nd resolvconf configuration file
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is the configuration file for
|
||||
.Xr resolvconf 8 .
|
||||
The
|
||||
.Nm
|
||||
file is a shell script that is sourced by
|
||||
.Xr resolvconf 8 ,
|
||||
meaning that
|
||||
.Nm
|
||||
must contain valid shell commands.
|
||||
Listed below are the standard
|
||||
.Nm
|
||||
variables that may be set.
|
||||
If the values contain whitespace, wildcards or other special shell characters,
|
||||
ensure they are quoted and escaped correctly.
|
||||
See the
|
||||
.Sy replace
|
||||
variable for an example on quoting.
|
||||
.Pp
|
||||
After updating this file, you may wish to run
|
||||
.Nm resolvconf -u
|
||||
to apply the new configuration.
|
||||
.Pp
|
||||
When a dynamically generated list is appended or prepended to, the whole
|
||||
is made unique where left-most wins.
|
||||
.Sh RESOLVCONF OPTIONS
|
||||
.Bl -tag -width indent
|
||||
.It Sy resolvconf
|
||||
Set to NO to disable
|
||||
.Nm resolvconf
|
||||
from running any subscribers.
|
||||
Defaults to YES.
|
||||
.It Sy allow_interfaces
|
||||
If set, only these interfaces will be processed.
|
||||
.It Sy deny_interfaces
|
||||
If set, these interfaces will not be processed.
|
||||
.It Sy interface_order
|
||||
These interfaces will always be processed first.
|
||||
If unset, defaults to the following:-
|
||||
.Bd -compact -literal -offset indent
|
||||
lo lo[0-9]*
|
||||
.Ed
|
||||
.It Sy dynamic_order
|
||||
These interfaces will be processed next, unless they have a metric.
|
||||
If unset, defaults to the following:-
|
||||
.Bd -compact -literal -offset indent
|
||||
tap[0-9]* tun[0-9]* vpn vpn[0-9]* wg[0-9]* ppp[0-9]* ippp[0-9]*
|
||||
.Ed
|
||||
.It Sy inclusive_interfaces
|
||||
Ignore any exclusive marking for these interfaces.
|
||||
This is handy when 3rd party integrations force the
|
||||
.Nm resolvconf -x
|
||||
option and you want to disable it easily.
|
||||
.It Sy local_nameservers
|
||||
If unset, defaults to the following:-
|
||||
.Bd -compact -literal -offset indent
|
||||
127.* 0.0.0.0 255.255.255.255 ::1
|
||||
.Ed
|
||||
.It Sy search_domains
|
||||
Prepend search domains to the dynamically generated list.
|
||||
.It Sy search_domains_append
|
||||
Append search domains to the dynamically generated list.
|
||||
.It Sy domain_blacklist
|
||||
A list of domains to be removed from consideration.
|
||||
To remove a domain, you can use foo.*
|
||||
To remove a sub domain, you can use *.bar
|
||||
.It Sy name_servers
|
||||
Prepend name servers to the dynamically generated list.
|
||||
You should set this to 127.0.0.1 if you use a local name server other than
|
||||
libc.
|
||||
.It Sy name_servers_append
|
||||
Append name servers to the dynamically generated list.
|
||||
.It Sy name_server_blacklist
|
||||
A list of name servers to be removed from consideration.
|
||||
The default is 0.0.0.0 as some faulty routers send it via DHCP.
|
||||
To remove a block, you can use 192.168.*
|
||||
.It Sy private_interfaces
|
||||
These interfaces name servers will only be queried for the domains listed
|
||||
in their resolv.conf.
|
||||
Useful for VPN domains.
|
||||
Setting
|
||||
.Sy private_interfaces Ns ="*"
|
||||
will stop the forwarding of the root zone and allows the local resolver to
|
||||
recursively query the root servers directly.
|
||||
Requires a local nameserver other than libc.
|
||||
This is equivalent to the
|
||||
.Nm resolvconf -p
|
||||
option.
|
||||
.It Sy public_interfaces
|
||||
Force these interface to be public, overriding the private marking.
|
||||
This is handy when 3rd party integrations force the
|
||||
.Nm resolvconf -p
|
||||
option and you want to disable it easily.
|
||||
.It Sy replace
|
||||
Is a space separated list of replacement keywords.
|
||||
The syntax is this:
|
||||
.Va $keyword Ns / Ns Va $match Ns / Ns Va $replacement
|
||||
.Pp
|
||||
Example, given this resolv.conf:
|
||||
.Bd -compact -literal -offset indent
|
||||
domain foo.org
|
||||
search foo.org dead.beef
|
||||
nameserver 1.2.3.4
|
||||
nameserver 2.3.4.5
|
||||
.Ed
|
||||
and this configuration:
|
||||
.Bd -compact -literal -offset indent
|
||||
replace="search/foo*/bar.com"
|
||||
replace="$replace nameserver/1.2.3.4/5.6.7.8"
|
||||
replace="$replace nameserver/2.3.4.5/"
|
||||
.Ed
|
||||
you would get this resolv.conf instead:
|
||||
.Bd -compact -literal -offset indent
|
||||
domain foo.org
|
||||
search bar.com
|
||||
nameserver 5.6.7.8
|
||||
.Ed
|
||||
.It Sy replace_sub
|
||||
Works the same way as
|
||||
.Sy replace
|
||||
except it works on each space separated value rather than the whole line,
|
||||
so it's useful for the replacing a single domain within the search directive.
|
||||
Using the same example resolv.conf and changing
|
||||
.Sy replace
|
||||
to
|
||||
.Sy replace_sub ,
|
||||
you would get this resolv.conf instead:
|
||||
.Bd -compact -literal -offset indent
|
||||
domain foo.org
|
||||
search bar.com dead.beef
|
||||
nameserver 5.6.7.8
|
||||
.Ed
|
||||
.It Sy state_dir
|
||||
Override the default state directory of
|
||||
.Pa @VARDIR@ .
|
||||
This should not be changed once
|
||||
.Nm resolvconf
|
||||
is in use unless the old directory is copied to the new one.
|
||||
.El
|
||||
.Sh LIBC OPTIONS
|
||||
The following variables affect
|
||||
.Xr resolv.conf 5
|
||||
directly:-
|
||||
.Bl -tag -width indent
|
||||
.It Sy resolv_conf
|
||||
Defaults to
|
||||
.Pa /etc/resolv.conf
|
||||
if not set.
|
||||
.It Sy resolv_conf_options
|
||||
A list of libc resolver options, as specified in
|
||||
.Xr resolv.conf 5 .
|
||||
.It Sy resolv_conf_passthrough
|
||||
When set to YES the latest resolv.conf is written to
|
||||
.Sy resolv_conf
|
||||
without any alteration.
|
||||
When set to /dev/null or NULL,
|
||||
.Sy resolv_conf_local_only
|
||||
is defaulted to NO,
|
||||
.Sy local_nameservers
|
||||
is unset unless overridden and only the information set in
|
||||
.Nm
|
||||
is written to
|
||||
.Sy resolv_conf .
|
||||
.It Sy resolv_conf_sortlist
|
||||
A libc resolver sortlist, as specified in
|
||||
.Xr resolv.conf 5 .
|
||||
.It Sy resolv_conf_local_only
|
||||
If a local name server is configured then the default is just to specify that
|
||||
and ignore all other entries as they will be configured for the local
|
||||
name server.
|
||||
Set this to NO to also list non-local nameservers.
|
||||
This will give you working DNS even if the local nameserver stops functioning
|
||||
at the expense of duplicated server queries.
|
||||
.It Sy append_nameservers
|
||||
Append name servers to the dynamically generated list.
|
||||
.It Sy prepend_nameservers
|
||||
Prepend name servers to the dynamically generated list.
|
||||
.It Sy append_search
|
||||
Append search domains to the dynamically generated list.
|
||||
.It Sy prepend_search
|
||||
Prepend search domains to the dynamically generated list.
|
||||
.It Sy resolv_conf_mv
|
||||
Defaults to NO.
|
||||
Defines if
|
||||
.Pa /etc/resolv.conf
|
||||
is updated by writing to a temporary file and then moving it
|
||||
vs writing directly to it.
|
||||
.El
|
||||
.Sh SUBSCRIBER OPTIONS
|
||||
openresolv ships with subscribers for the name servers
|
||||
.Xr dnsmasq 8 ,
|
||||
.Xr named 8 ,
|
||||
.Xr pdnsd 8 ,
|
||||
.Xr pdns_recursor 1 ,
|
||||
and
|
||||
.Xr unbound 8 .
|
||||
Each subscriber can create configuration files which should be included in
|
||||
the subscribers main configuration file.
|
||||
.Pp
|
||||
To disable a subscriber, simply set its name to NO.
|
||||
For example, to disable the libc subscriber you would set:
|
||||
.Bd -compact -literal -offset indent
|
||||
libc=NO
|
||||
.Ed
|
||||
.Bl -tag -width indent
|
||||
.It Sy dnsmasq_conf
|
||||
This file tells dnsmasq which name servers to use for specific domains.
|
||||
.It Sy dnsmasq_resolv
|
||||
This file tells dnsmasq which name servers to use for global lookups.
|
||||
.Pp
|
||||
Example resolvconf.conf for dnsmasq:
|
||||
.Bd -compact -literal -offset indent
|
||||
name_servers=127.0.0.1
|
||||
dnsmasq_conf=/etc/dnsmasq-conf.conf
|
||||
dnsmasq_resolv=/etc/dnsmasq-resolv.conf
|
||||
.Ed
|
||||
.Pp
|
||||
Example dnsmasq.conf:
|
||||
.Bd -compact -literal -offset indent
|
||||
listen-address=127.0.0.1
|
||||
# If dnsmasq is compiled for DBus then we can take
|
||||
# advantage of not having to restart dnsmasq.
|
||||
enable-dbus
|
||||
conf-file=/etc/dnsmasq-conf.conf
|
||||
resolv-file=/etc/dnsmasq-resolv.conf
|
||||
.Ed
|
||||
.It Sy named_options
|
||||
Include this file in the named options block.
|
||||
This file tells named which name servers to use for global lookups.
|
||||
.It Sy named_zones
|
||||
Include this file in the named global scope, after the options block.
|
||||
This file tells named which name servers to use for specific domains.
|
||||
.Pp
|
||||
Example resolvconf.conf for named:
|
||||
.Bd -compact -literal -offset indent
|
||||
name_servers=127.0.0.1
|
||||
named_options=/etc/named-options.conf
|
||||
named_zones=/etc/named-zones.conf
|
||||
.Ed
|
||||
.Pp
|
||||
Example named.conf:
|
||||
.Bd -compact -literal -offset indent
|
||||
options {
|
||||
listen-on { 127.0.0.1; };
|
||||
include "/etc/named-options.conf";
|
||||
};
|
||||
|
||||
include "/etc/named-zones.conf";
|
||||
.Ed
|
||||
.It Sy pdnsd_conf
|
||||
This is the main pdnsd configuration file which we modify to add our
|
||||
forward domains to.
|
||||
If this variable is not set then we rely on the pdnsd configuration file
|
||||
setup to read
|
||||
.Pa pdnsd_resolv
|
||||
as documented below.
|
||||
.It Sy pdnsd_resolv
|
||||
This file tells pdnsd about global name servers.
|
||||
If this variable is not set then it's written to
|
||||
.Pa pdnsd_conf .
|
||||
.Pp
|
||||
Example resolvconf.conf for pdnsd:
|
||||
.Bd -compact -literal -offset indent
|
||||
name_servers=127.0.0.1
|
||||
pdnsd_conf=/etc/pdnsd.conf
|
||||
# pdnsd_resolv=/etc/pdnsd-resolv.conf
|
||||
.Ed
|
||||
.Pp
|
||||
Example pdnsd.conf:
|
||||
.Bd -compact -literal -offset indent
|
||||
global {
|
||||
server_ip = 127.0.0.1;
|
||||
status_ctl = on;
|
||||
}
|
||||
server {
|
||||
# A server definition is required, even if empty.
|
||||
label="empty";
|
||||
proxy_only=on;
|
||||
# file="/etc/pdnsd-resolv.conf";
|
||||
}
|
||||
.Ed
|
||||
.It Sy pdns_zones
|
||||
This file tells pdns_recursor about specific and global name servers.
|
||||
.Pp
|
||||
Example resolvconf.conf for pdns_recursor:
|
||||
.Bd -compact -literal -offset indent
|
||||
name_servers=127.0.0.1
|
||||
pdns_zones=/etc/pdns/recursor-zones.conf
|
||||
.Ed
|
||||
.Pp
|
||||
Example recursor.conf:
|
||||
.Bd -compact -literal -offset indent
|
||||
allow-from=127.0.0.0/8, ::1/128
|
||||
forward-zones-file=/etc/pdns/recursor-zones.conf
|
||||
.Ed
|
||||
.It Sy unbound_conf
|
||||
This file tells unbound about specific and global name servers.
|
||||
.It Sy unbound_insecure
|
||||
When set to YES, unbound marks the domains as insecure, thus ignoring DNSSEC.
|
||||
.It Sy unbound_forward_zone_options
|
||||
Options appended to each forward zone.
|
||||
Each option should be separated by an embedded new line.
|
||||
.Pp
|
||||
Example resolvconf.conf for unbound:
|
||||
.Bd -compact -literal -offset indent
|
||||
name_servers=127.0.0.1
|
||||
unbound_conf=/etc/unbound-resolvconf.conf
|
||||
.Ed
|
||||
.Pp
|
||||
Example unbound.conf:
|
||||
.Bd -compact -literal -offset indent
|
||||
include: /etc/unbound-resolvconf.conf
|
||||
.Ed
|
||||
.El
|
||||
.Sh SUBSCRIBER INTEGRATION
|
||||
Not all distributions store the files the subscribers need in the same
|
||||
locations.
|
||||
For example, named service scripts have been called named, bind and rc.bind
|
||||
and they could be located in a directory called /etc/rc.d, /etc/init.d or
|
||||
similar.
|
||||
Each subscriber attempts to automatically configure itself, but not every
|
||||
distribution has been catered for.
|
||||
Also, users could equally want to use a different version from the one
|
||||
installed by default, such as bind8 and bind9.
|
||||
To accommodate this, the subscribers have these files in configurable
|
||||
variables, documented below.
|
||||
.Bl -tag -width indent
|
||||
.It Sy dnsmasq_service
|
||||
Name of the dnsmasq service.
|
||||
.It Sy dnsmasq_restart
|
||||
Command to restart the dnsmasq service.
|
||||
.It Sy dnsmasq_pid
|
||||
Location of the dnsmasq pidfile.
|
||||
.It Sy libc_service
|
||||
Name of the libc service.
|
||||
.It Sy libc_restart
|
||||
Command to restart the libc service.
|
||||
.It Sy named_service
|
||||
Name of the named service.
|
||||
.It Sy named_restart
|
||||
Command to restart the named service.
|
||||
.It Sy pdnsd_restart
|
||||
Command to restart the pdnsd service.
|
||||
.It Sy pdns_service
|
||||
Command to restart the pdns_recursor service.
|
||||
.It Sy pdns_restart
|
||||
Command to restart the pdns_recursor service.
|
||||
.It Sy unbound_service
|
||||
Name of the unbound service.
|
||||
.It Sy unbound_restart
|
||||
Command to restart the unbound service.
|
||||
.It Sy unbound_pid
|
||||
Location of the unbound pidfile.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr sh 1 ,
|
||||
.Xr resolv.conf 5 ,
|
||||
.Xr resolvconf 8
|
||||
.Sh AUTHORS
|
||||
.An Roy Marples Aq Mt roy@marples.name
|
||||
.Sh BUGS
|
||||
Each distribution is a special snowflake and likes to name the same thing
|
||||
differently, namely the named service script.
|
||||
.Pp
|
||||
Please report them to
|
||||
.Lk https://roy.marples.name/projects/openresolv
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,103 @@
|
|||
#!/bin/sh
|
||||
# Copyright (c) 2009-2023 Roy Marples
|
||||
# All rights reserved
|
||||
|
||||
# unbound subscriber for resolvconf
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * 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 COPYRIGHT HOLDERS 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 COPYRIGHT
|
||||
# OWNER 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.
|
||||
|
||||
unbound_insecure=
|
||||
|
||||
[ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0
|
||||
. "@SYSCONFDIR@/resolvconf.conf" || exit 1
|
||||
[ -z "$unbound_conf" ] && exit 0
|
||||
[ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)"
|
||||
NL="
|
||||
"
|
||||
|
||||
: ${unbound_pid:=/var/run/unbound.pid}
|
||||
: ${unbound_service:=unbound}
|
||||
newconf="# Generated by resolvconf$NL"
|
||||
|
||||
for d in $DOMAINS; do
|
||||
dn="${d%%:*}"
|
||||
ns="${d#*:}"
|
||||
case "$unbound_insecure" in
|
||||
[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
|
||||
newconf="$newconf${NL}server:$NL"
|
||||
newconf="$newconf domain-insecure: \"$dn\"$NL"
|
||||
;;
|
||||
esac
|
||||
newconf="$newconf${NL}forward-zone:$NL name: \"$dn\"$NL"
|
||||
if [ -n "$unbound_forward_zone_options" ]; then
|
||||
newconf="$newconf $unbound_forward_zone_options${NL}"
|
||||
fi
|
||||
while [ -n "$ns" ]; do
|
||||
newconf="$newconf forward-addr: ${ns%%,*}$NL"
|
||||
[ "$ns" = "${ns#*,}" ] && break
|
||||
ns="${ns#*,}"
|
||||
done
|
||||
done
|
||||
|
||||
if [ -n "$NAMESERVERS" ]; then
|
||||
newconf="$newconf${NL}forward-zone:$NL name: \".\"$NL"
|
||||
if [ -n "$unbound_forward_zone_options" ]; then
|
||||
newconf="$newconf $unbound_forward_zone_options${NL}"
|
||||
fi
|
||||
for n in $NAMESERVERS; do
|
||||
newconf="$newconf forward-addr: $n$NL"
|
||||
done
|
||||
fi
|
||||
|
||||
# Try to ensure that config dirs exist
|
||||
if command -v config_mkdirs >/dev/null 2>&1; then
|
||||
config_mkdirs "$unbound_conf"
|
||||
else
|
||||
@SBINDIR@/resolvconf -D "$unbound_conf"
|
||||
fi
|
||||
|
||||
restart_unbound()
|
||||
{
|
||||
if [ -n "$unbound_restart" ]; then
|
||||
eval $unbound_restart
|
||||
elif [ -n "$RESTARTCMD" ]; then
|
||||
set -- ${unbound_service}
|
||||
eval "$RESTARTCMD"
|
||||
else
|
||||
@SBINDIR@/resolvconf -r ${unbound_service}
|
||||
fi
|
||||
}
|
||||
|
||||
if [ ! -f "$unbound_conf" ] || \
|
||||
[ "$(cat "$unbound_conf")" != "$(printf %s "$newconf")" ]
|
||||
then
|
||||
printf %s "$newconf" >"$unbound_conf"
|
||||
# If we can't sent a HUP then force a restart
|
||||
if [ -s "$unbound_pid" ]; then
|
||||
if ! kill -HUP $(cat "$unbound_pid") 2>/dev/null; then
|
||||
restart_unbound
|
||||
fi
|
||||
else
|
||||
restart_unbound
|
||||
fi
|
||||
fi
|
Loading…
Reference in New Issue