#!/bin/sh /etc/rc.common START=19 USE_PROCD=1 PROG=/usr/sbin/dhcpd lease_file=/tmp/dhcpd.leases config_file=/tmp/run/dhcpd.conf time2seconds() { local timestring=$1 local multiplier number suffix suffix="${timestring//[0-9 ]}" number="${timestring%%$suffix}" [ "$number$suffix" != "$timestring" ] && return 1 case "$suffix" in "" | s) multiplier=1 ;; m) multiplier=60 ;; h) multiplier=3600 ;; d) multiplier=86400 ;; w) multiplier=604800 ;; *) return 1 ;; esac echo $(( number * multiplier )) } # duplicated from dnsmasq init script hex_to_hostid() { local var="$1" local hex="${2#0x}" # strip optional "0x" prefix if [ -n "${hex//[0-9a-fA-F]/}" ]; then # is invalid hex literal return 1 fi # convert into host id export "$var=$( printf "%0x:%0x" \ $(((0x$hex >> 16) % 65536)) \ $(( 0x$hex % 65536)) )" return 0 } typeof() { echo "$1" | awk ' /^\d+\.\d+\.\d+\.\d+$/ { print "ip\n"; next; } /^(true|false)$/ { print "bool\n"; next; } /^\d+$/ { print "integer\n"; next; } /^"[^"]*"$/ { print "string\n"; next; } { print "other\n"; next; } ' } explode() { local arg="$1" echo "$arg" | sed -e 's/\./, /g' } append_routes() { local tuple tuples="$1" local string= local IFS=',' for tuple in $tuples; do local network prefix router save octets compacted save="${tuple% *}" router="${tuple#${save} }" network="${save%/[0-9]*}" prefix="${save##${network}}" prefix="${prefix:1}" octets=$((($prefix + 7) / 8)) compacted="$(echo "$network" | cut -d. -f1-$octets)" string="${string:+, }$(explode "$prefix.$compacted.$router")" done echo " option classless-ipv4-route $string;" } append_dhcp_options() { local tuple="$1" # strip redundant "option:" prefix tuple="${tuple#option:}" local tag="${tuple%%,*}" local values="${tuple#$tag,}" local formatted value local IFS=$', \n' for value in $values; do # detect type of $value and quote if necessary case $(typeof "$value") in ip|bool|integer|string) ;; other) value="\"$value\"" ;; esac formatted="$formatted${formatted:+, }$value" done echo " option $tag $formatted;" } static_host_add() { local cfg="$1" local broadcast hostid macn macs mac name ip leasetime config_get macs "$cfg" "mac" [ -n "$macs" ] || return 0 config_get name "$cfg" "name" [ -n "$name" ] || return 0 config_get ip "$cfg" "ip" [ -n "$ip" ] || return 0 config_get_bool broadcast "$cfg" "broadcast" 0 config_get dns "$cfg" "dns" config_get gateway "$cfg" "gateway" config_get leasetime "$cfg" "leasetime" if [ -n "$leasetime" ] ; then leasetime="$(time2seconds "$leasetime")" [ "$?" -ne 0 ] && return 1 fi config_get hostid "$cfg" "hostid" if [ -n "$hostid" ] ; then hex_to_hostid hostid "$hostid" || return 1 fi macn=0 for mac in $macs; do macn=$(( macn + 1 )) done for mac in $macs; do local secname="$name" if [ $macn -gt 1 ] ; then secname="${name}-${mac//:}" fi echo "host $secname {" echo " hardware ethernet $mac;" echo " fixed-address $ip;" echo " option host-name \"$name\";" if [ "$broadcast" -eq 1 ] ; then echo " always-broadcast true;" fi if [ -n "$leasetime" ] ; then echo " default-lease-time $leasetime;" echo " max-lease-time $leasetime;" fi if [ -n "$hostid" ] ; then echo " option dhcp-client-identifier $hostid;" fi if [ -n "$dns" ] ; then echo " option domain-name-servers $dns;" fi if [ -n "$gateway" ] ; then echo " option routers $gateway;" fi config_list_foreach "$cfg" "routes" append_routes config_list_foreach "$cfg" "dhcp_option" append_dhcp_options echo "}" done } static_hosts() { config_foreach static_host_add host "$@" } gen_dhcp_subnet() { local cfg="$1" echo "subnet $NETWORK netmask $NETMASK {" echo " range $START $END;" echo " option subnet-mask $netmask;" if [ "$BROADCAST" != "0.0.0.0" ] ; then echo " option broadcast-address $BROADCAST;" fi if [ "$dynamicdhcp" -eq 0 ] ; then if [ "$authoritative" -eq 1 ] ; then echo " deny unknown-clients;" else echo " ignore unknown-clients;" fi fi if [ -n "$leasetime" ] ; then echo " default-lease-time $leasetime;" echo " max-lease-time $leasetime;" fi echo " option routers $gateway;" echo " option domain-name-servers $DNS;" config_list_foreach "$cfg" "routes" append_routes config_list_foreach "$cfg" "dhcp_option" append_dhcp_options echo "}" } dhcpd_add() { local cfg="$1" local dhcp6range="::" local dynamicdhcp end gateway ifname ignore leasetime limit net netmask local proto networkid start subnet local IP NETMASK BROADCAST NETWORK PREFIX DNS START END config_get_bool ignore "$cfg" "ignore" 0 [ "$ignore" = "0" ] || return 0 config_get net "$cfg" "interface" [ -n "$net" ] || return 0 config_get start "$cfg" "start" [ -n "$start" ] || return 0 config_get limit "$cfg" "limit" [ -n "$limit" ] || return 0 network_get_subnet subnet "$net" || return 0 network_get_device ifname "$net" || return 0 network_get_protocol proto "$net" || return 0 [ static = "$proto" ] || return 0 config_get_bool dynamicdhcp "$cfg" "dynamicdhcp" 1 dhcp_ifs="$dhcp_ifs $ifname" eval "$(ipcalc.sh $subnet $start $limit)" config_get netmask "$cfg" "netmask" "$NETMASK" config_get leasetime "$cfg" "leasetime" if [ -n "$leasetime" ] ; then leasetime="$(time2seconds "$leasetime")" [ "$?" -ne 0 ] && return 1 fi if network_get_dnsserver dnsserver "$net" ; then for dnsserv in $dnsserver ; do DNS="$DNS${DNS:+, }$dnsserv" done else DNS="$IP" fi if ! network_get_gateway gateway "$net" ; then gateway="$IP" fi gen_dhcp_subnet "$cfg" >> $config_file } general_config() { local always_broadcast boot_unknown_clients log_facility local default_lease_time max_lease_time config_get_bool always_broadcast "isc_dhcpd" "always_broadcast" 0 config_get_bool authoritative "isc_dhcpd" "authoritative" 1 config_get_bool boot_unknown_clients "isc_dhcpd" "boot_unknown_clients" 1 config_get default_lease_time "isc_dhcpd" "default_lease_time" 3600 config_get max_lease_time "isc_dhcpd" "max_lease_time" 86400 config_get log_facility "isc_dhcpd" "log_facility" config_get domain "isc_dhcpd" "domain" [ $always_broadcast -eq 1 ] && echo "always-broadcast true;" [ $authoritative -eq 1 ] && echo "authoritative;" [ $boot_unknown_clients -eq 0 ] && echo "boot-unknown-clients false;" default_lease_time="$(time2seconds "$default_lease_time")" [ "$?" -ne 0 ] && return 1 max_lease_time="$(time2seconds "$max_lease_time")" [ "$?" -ne 0 ] && return 1 if [ -n "$log_facility" ] ; then echo "log-facility $log_facility;" fi echo "default-lease-time $default_lease_time;" echo "max-lease-time $max_lease_time;" [ -n "$domain" ] && echo "option domain-name \"$domain\";" echo -e "\n# additional codes\noption classless-ipv4-route code 121 = array of { unsigned integer 8 };\n" rm -f /tmp/resolv.conf echo "# This file is generated by the DHCPD service" > /tmp/resolv.conf [ -n "$domain" ] && echo "domain $domain" >> /tmp/resolv.conf echo "nameserver 127.0.0.1" >> /tmp/resolv.conf } # base procd hooks boot() { DHCPD_BOOT=1 start "$@" } start_service() { local domain dhcp_ifs authoritative if [ -n "$DHCPD_BOOT" ] ; then return 0 fi if [ ! -e $lease_file ] ; then touch $lease_file fi if [ -e "/etc/dhcpd.conf" ] ; then config_file="/etc/dhcpd.conf" else . /lib/functions/network.sh config_load dhcp general_config > $config_file config_foreach dhcpd_add dhcp static_hosts >> $config_file [ -z "$dhcp_ifs" ] && return 0 fi procd_open_instance procd_set_param command $PROG -q -f -cf $config_file -lf $lease_file $dhcp_ifs procd_close_instance } reload_service() { rc_procd start_service "$@" prodcd_send_signal dhcpd "$@" } add_interface_trigger() { local cfg=$1 local trigger ignore config_get trigger "$cfg" interface config_get_bool ignore "$cfg" ignore 0 if [ -n "$trigger" -a $ignore -eq 0 ] ; then procd_add_reload_interface_trigger "$trigger" fi } service_triggers() { if [ -n "$DHCPD_BOOT" ] ; then # Make the first start robust to slow interfaces; wait a while procd_add_raw_trigger "interface.*.up" 5000 /etc/init.d/dhcpd restart else # reload with normal parameters procd_add_reload_trigger "network" "dhcp" config_load dhcp config_foreach add_interface_trigger dhcp fi }