#!/bin/sh /etc/rc.common # Copyright (C) 2007-2012 OpenWrt.org START=19 USE_PROCD=1 PROG=/usr/sbin/dnsmasq DNS_SERVERS="" DOMAIN="" ADD_LOCAL_DOMAIN=1 ADD_LOCAL_HOSTNAME=1 CONFIGFILE="/var/etc/dnsmasq.conf" CONFIGFILE_TMP="/var/etc/dnsmasq.conf.$$" HOSTFILE="/tmp/hosts/dhcp" TRUSTANCHORSFILE="/usr/share/dnsmasq/trust-anchors.conf" TIMESTAMPFILE="/etc/dnsmasq.time" TIMEVALIDFILE="/var/state/dnsmasqsec" xappend() { local value="$1" echo "${value#--}" >> $CONFIGFILE_TMP } 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 % 256)) )" return 0 } dhcp_calc() { local ip="$1" local res=0 while [ -n "$ip" ]; do part="${ip%%.*}" res="$(($res * 256))" res="$(($res + $part))" [ "${ip%.*}" != "$ip" ] && ip="${ip#*.}" || ip= done echo "$res" } dhcp_check() { local ifname="$1" local stamp="/var/run/dnsmasq.$ifname.dhcp" local rv=0 [ -s "$stamp" ] && return $(cat "$stamp") # If there's no carrier yet, skip this interface. # The init script will be called again once the link is up case "$(devstatus "$ifname" | jsonfilter -e @.carrier)" in false) return 1;; esac udhcpc -n -q -s /bin/true -t 1 -i "$ifname" >&- && rv=1 || rv=0 [ $rv -eq 1 ] && \ logger -t dnsmasq \ "found already running DHCP-server on interface '$ifname'" \ "refusing to start, use 'option force 1' to override" echo $rv > "$stamp" return $rv } log_once() { pidof dnsmasq >/dev/null || \ logger -t dnsmasq "$@" } append_bool() { local section="$1" local option="$2" local value="$3" local _loctmp config_get_bool _loctmp "$section" "$option" 0 [ $_loctmp -gt 0 ] && xappend "$value" } append_parm() { local section="$1" local option="$2" local switch="$3" local default="$4" local _loctmp config_get _loctmp "$section" "$option" "$default" [ -z "$_loctmp" ] && return 0 xappend "$switch=$_loctmp" } append_server() { xappend "--server=$1" } append_address() { xappend "--address=$1" } append_ipset() { xappend "--ipset=$1" } append_interface() { local ifname=$(uci_get_state network "$1" ifname "$1") xappend "--interface=$ifname" } append_notinterface() { local ifname=$(uci_get_state network "$1" ifname "$1") xappend "--except-interface=$ifname" } append_addnhosts() { xappend "--addn-hosts=$1" } append_bogusnxdomain() { xappend "--bogus-nxdomain=$1" } append_pxe_service() { xappend "--pxe-service=$1" } dnsmasq() { local cfg="$1" append_bool "$cfg" authoritative "--dhcp-authoritative" append_bool "$cfg" nodaemon "--no-daemon" append_bool "$cfg" domainneeded "--domain-needed" append_bool "$cfg" filterwin2k "--filterwin2k" append_bool "$cfg" nohosts "--no-hosts" append_bool "$cfg" nonegcache "--no-negcache" append_bool "$cfg" strictorder "--strict-order" append_bool "$cfg" logqueries "--log-queries=extra" append_bool "$cfg" noresolv "--no-resolv" append_bool "$cfg" localise_queries "--localise-queries" append_bool "$cfg" readethers "--read-ethers" append_bool "$cfg" dbus "--enable-dbus" append_bool "$cfg" boguspriv "--bogus-priv" append_bool "$cfg" expandhosts "--expand-hosts" config_get tftp_root "$cfg" "tftp_root" [ -d "$tftp_root" ] && append_bool "$cfg" enable_tftp "--enable-tftp" append_bool "$cfg" tftp_no_fail "--tftp-no-fail" append_bool "$cfg" nonwildcard "--bind-dynamic" append_bool "$cfg" fqdn "--dhcp-fqdn" append_bool "$cfg" proxydnssec "--proxy-dnssec" append_bool "$cfg" localservice "--local-service" append_bool "$cfg" quietdhcp "--quiet-dhcp" append_bool "$cfg" sequential_ip "--dhcp-sequential-ip" append_bool "$cfg" allservers "--all-servers" append_bool "$cfg" noping "--no-ping" append_parm "$cfg" dhcpscript "--dhcp-script" append_parm "$cfg" cachesize "--cache-size" append_parm "$cfg" dnsforwardmax "--dns-forward-max" append_parm "$cfg" port "--port" append_parm "$cfg" ednspacket_max "--edns-packet-max" append_parm "$cfg" dhcpleasemax "--dhcp-lease-max" append_parm "$cfg" "queryport" "--query-port" append_parm "$cfg" "minport" "--min-port" append_parm "$cfg" "maxport" "--max-port" append_parm "$cfg" "domain" "--domain" append_parm "$cfg" "local" "--server" config_list_foreach "$cfg" "server" append_server config_list_foreach "$cfg" "address" append_address config_list_foreach "$cfg" "ipset" append_ipset config_list_foreach "$cfg" "interface" append_interface config_list_foreach "$cfg" "notinterface" append_notinterface config_list_foreach "$cfg" "addnhosts" append_addnhosts config_list_foreach "$cfg" "bogusnxdomain" append_bogusnxdomain append_parm "$cfg" "leasefile" "--dhcp-leasefile" "/tmp/dhcp.leases" append_parm "$cfg" "resolvfile" "--resolv-file" "/tmp/resolv.conf.auto" append_parm "$cfg" "serversfile" "--servers-file" append_parm "$cfg" "tftp_root" "--tftp-root" append_parm "$cfg" "dhcp_boot" "--dhcp-boot" append_parm "$cfg" "local_ttl" "--local-ttl" append_parm "$cfg" "pxe_prompt" "--pxe-prompt" config_list_foreach "$cfg" "pxe_service" append_pxe_service config_get DOMAIN "$cfg" domain config_get_bool ADD_LOCAL_DOMAIN "$cfg" add_local_domain 1 config_get_bool ADD_LOCAL_HOSTNAME "$cfg" add_local_hostname 1 config_get_bool readethers "$cfg" readethers [ "$readethers" = "1" -a \! -e "/etc/ethers" ] && touch /etc/ethers config_get leasefile $cfg leasefile "/tmp/dhcp.leases" [ -n "$leasefile" -a \! -e "$leasefile" ] && touch "$leasefile" config_get_bool cachelocal "$cfg" cachelocal 1 config_get_bool noresolv "$cfg" noresolv 0 if [ "$noresolv" != "1" ]; then config_get resolvfile "$cfg" resolvfile "/tmp/resolv.conf.auto" # So jail doesn't complain if file missing [ -n "$resolvfile" -a \! -e "$resolvfile" ] && touch "$resolvfile" fi config_get hostsfile "$cfg" dhcphostsfile [ -e "$hostsfile" ] && xappend "--dhcp-hostsfile=$hostsfile" local rebind config_get_bool rebind "$cfg" rebind_protection 1 [ $rebind -gt 0 ] && { log_once \ "DNS rebinding protection is active," \ "will discard upstream RFC1918 responses!" xappend "--stop-dns-rebind" local rebind_localhost config_get_bool rebind_localhost "$cfg" rebind_localhost 0 [ $rebind_localhost -gt 0 ] && { log_once "Allowing 127.0.0.0/8 responses" xappend "--rebind-localhost-ok" } append_rebind_domain() { log_once "Allowing RFC1918 responses for domain $1" xappend "--rebind-domain-ok=$1" } config_list_foreach "$cfg" rebind_domain append_rebind_domain } config_get_bool dnssec "$cfg" dnssec 0 [ "$dnssec" -gt 0 ] && { xappend "--conf-file=$TRUSTANCHORSFILE" xappend "--dnssec" [ -x /etc/init.d/sysntpd ] && { /etc/init.d/sysntpd enabled [ "$?" -ne 0 -o "$(uci_get system.ntp.enabled)" = "1" ] && { [ -f "$TIMEVALIDFILE" ] || xappend "--dnssec-no-timecheck" } } append_bool "$cfg" dnsseccheckunsigned "--dnssec-check-unsigned" } dhcp_option_add "$cfg" "" 0 xappend "--dhcp-broadcast=tag:needs-broadcast" mkdir -p /tmp/hosts /tmp/dnsmasq.d xappend "--addn-hosts=/tmp/hosts" xappend "--conf-dir=/tmp/dnsmasq.d" xappend "--user=dnsmasq" xappend "--group=dnsmasq" echo >> $CONFIGFILE_TMP config_get_bool enable_tftp "$cfg" enable_tftp 0 [ "$enable_tftp" -gt 0 ] && { config_get tftp_root "$cfg" tftp_root append EXTRA_MOUNT $tftp_root } } dhcp_subscrid_add() { local cfg="$1" config_get networkid "$cfg" networkid [ -n "$networkid" ] || return 0 config_get subscriberid "$cfg" subscriberid [ -n "$subscriberid" ] || return 0 xappend "--dhcp-subscrid=$networkid,$subscriberid" config_get_bool force "$cfg" force 0 dhcp_option_add "$cfg" "$networkid" "$force" } dhcp_remoteid_add() { local cfg="$1" config_get networkid "$cfg" networkid [ -n "$networkid" ] || return 0 config_get remoteid "$cfg" remoteid [ -n "$remoteid" ] || return 0 xappend "--dhcp-remoteid=$networkid,$remoteid" config_get_bool force "$cfg" force 0 dhcp_option_add "$cfg" "$networkid" "$force" } dhcp_circuitid_add() { local cfg="$1" config_get networkid "$cfg" networkid [ -n "$networkid" ] || return 0 config_get circuitid "$cfg" circuitid [ -n "$circuitid" ] || return 0 xappend "--dhcp-circuitid=$networkid,$circuitid" config_get_bool force "$cfg" force 0 dhcp_option_add "$cfg" "$networkid" "$force" } dhcp_userclass_add() { local cfg="$1" config_get networkid "$cfg" networkid [ -n "$networkid" ] || return 0 config_get userclass "$cfg" userclass [ -n "$userclass" ] || return 0 xappend "--dhcp-userclass=$networkid,$userclass" config_get_bool force "$cfg" force 0 dhcp_option_add "$cfg" "$networkid" "$force" } dhcp_vendorclass_add() { local cfg="$1" config_get networkid "$cfg" networkid [ -n "$networkid" ] || return 0 config_get vendorclass "$cfg" vendorclass [ -n "$vendorclass" ] || return 0 xappend "--dhcp-vendorclass=$networkid,$vendorclass" config_get_bool force "$cfg" force 0 dhcp_option_add "$cfg" "$networkid" "$force" } dhcp_host_add() { local cfg="$1" config_get_bool force "$cfg" force 0 config_get networkid "$cfg" networkid [ -n "$networkid" ] && dhcp_option_add "$cfg" "$networkid" "$force" config_get_bool enable "$cfg" enable 1 [ "$enable" = "0" ] && return 0 config_get name "$cfg" name config_get ip "$cfg" ip [ -n "$ip" -o -n "$name" ] || return 0 config_get_bool dns "$cfg" dns 0 [ "$dns" = "1" -a -n "$ip" -a -n "$name" ] && { echo "$ip $name${DOMAIN:+.$DOMAIN}" >> $HOSTFILE } config_get mac "$cfg" mac if [ -n "$mac" ]; then # --dhcp-host=00:20:e0:3b:13:af,192.168.0.199,lap macs="" for m in $mac; do append macs "$m" ","; done else # --dhcp-host=lap,192.168.0.199 [ -n "$name" ] || return 0 macs="$name" name="" fi config_get tag "$cfg" tag if [ "$DHCPv6CAPABLE" -eq 1 ]; then config_get hostid "$cfg" hostid if [ -n "$hostid" ]; then hex_to_hostid hostid "$hostid" fi fi config_get_bool broadcast "$cfg" broadcast 0 [ "$broadcast" = "0" ] && broadcast= config_get leasetime "$cfg" leasetime xappend "--dhcp-host=$macs${networkid:+,net:$networkid}${broadcast:+,set:needs-broadcast}${tag:+,set:$tag}${ip:+,$ip${hostid:+,[::$hostid]}}${name:+,$name}${leasetime:+,$leasetime}" } dhcp_tag_add() { local cfg="$1" tag="$cfg" [ -n "$tag" ] || return 0 config_get_bool force "$cfg" force 0 [ "$force" = "0" ] && force= config_get option "$cfg" dhcp_option for o in $option; do xappend "--dhcp-option${force:+-force}=tag:$tag,$o" done } dhcp_mac_add() { local cfg="$1" config_get networkid "$cfg" networkid [ -n "$networkid" ] || return 0 config_get mac "$cfg" mac [ -n "$mac" ] || return 0 xappend "--dhcp-mac=$networkid,$mac" dhcp_option_add "$cfg" "$networkid" } dhcp_boot_add() { local cfg="$1" config_get networkid "$cfg" networkid config_get filename "$cfg" filename [ -n "$filename" ] || return 0 config_get servername "$cfg" servername config_get serveraddress "$cfg" serveraddress [ -n "$serveraddress" -a ! -n "$servername" ] && return 0 xappend "--dhcp-boot=${networkid:+net:$networkid,}${filename}${servername:+,$servername}${serveraddress:+,$serveraddress}" config_get_bool force "$cfg" force 0 dhcp_option_add "$cfg" "$networkid" "$force" } dhcp_add() { local cfg="$1" config_get net "$cfg" interface [ -n "$net" ] || return 0 config_get dhcpv4 "$cfg" dhcpv4 [ "$dhcpv4" != "disabled" ] || return 0 config_get networkid "$cfg" networkid [ -n "$networkid" ] || networkid="$net" network_get_subnet subnet "$net" || return 0 network_get_device ifname "$net" || return 0 network_get_protocol proto "$net" || return 0 [ "$cachelocal" = "0" ] && network_get_dnsserver dnsserver "$net" && { DNS_SERVERS="$DNS_SERVERS $dnsserver" } append_bool "$cfg" ignore "--no-dhcp-interface=$ifname" && return 0 # Do not support non-static interfaces for now [ static = "$proto" ] || return 0 # Override interface netmask with dhcp config if applicable config_get netmask "$cfg" netmask "${subnet##*/}" #check for an already active dhcp server on the interface, unless 'force' is set config_get_bool force "$cfg" force 0 [ $force -gt 0 ] || dhcp_check "$ifname" || return 0 config_get start "$cfg" start config_get limit "$cfg" limit config_get leasetime "$cfg" leasetime config_get options "$cfg" options config_get_bool dynamicdhcp "$cfg" dynamicdhcp 1 leasetime="${leasetime:-12h}" start="$(dhcp_calc "${start:-100}")" limit="${limit:-150}" [ "$limit" -gt 0 ] && limit=$((limit-1)) eval "$(ipcalc.sh "${subnet%%/*}" $netmask $start $limit)" if [ "$dynamicdhcp" = "0" ]; then END="static"; fi xappend "--dhcp-range=$networkid,$START,$END,$NETMASK,$leasetime${options:+ $options}" dhcp_option_add "$cfg" "$networkid" } dhcp_option_add() { local cfg="$1" local networkid="$2" local force="$3" [ "$force" = "0" ] && force= config_get dhcp_option "$cfg" dhcp_option for o in $dhcp_option; do xappend "--dhcp-option${force:+-force}=${networkid:+$networkid,}$o" done } dhcp_domain_add() { local cfg="$1" local ip name names record config_get names "$cfg" name "$2" [ -n "$names" ] || return 0 config_get ip "$cfg" ip "$3" [ -n "$ip" ] || return 0 for name in $names; do record="${record:+$record }$name" done echo "$ip $record" >> $HOSTFILE } dhcp_srv_add() { local cfg="$1" config_get srv "$cfg" srv [ -n "$srv" ] || return 0 config_get target "$cfg" target [ -n "$target" ] || return 0 config_get port "$cfg" port [ -n "$port" ] || return 0 config_get class "$cfg" class config_get weight "$cfg" weight local service="$srv,$target,$port${class:+,$class${weight:+,$weight}}" xappend "--srv-host=$service" } dhcp_mx_add() { local cfg="$1" local domain relay pref config_get domain "$cfg" domain [ -n "$domain" ] || return 0 config_get relay "$cfg" relay [ -n "$relay" ] || return 0 config_get pref "$cfg" pref 0 local service="$domain,$relay,$pref" xappend "--mx-host=$service" } dhcp_cname_add() { local cfg="$1" local cname target config_get cname "$cfg" cname [ -n "$cname" ] || return 0 config_get target "$cfg" target [ -n "$target" ] || return 0 xappend "--cname=${cname},${target}" } dhcp_hostrecord_add() { local cfg="$1" local names addresses record val config_get names "$cfg" name "$2" if [ -z "$names" ]; then return 0 fi config_get addresses "$cfg" ip "$3" if [ -z "$addresses" ]; then return 0 fi for val in $names $addresses; do record="${record:+$record,}$val" done xappend "--host-record=$record" } dhcp_relay_add() { local cfg="$1" local local_addr server_addr interface config_get local_addr "$cfg" local_addr [ -n "$local_addr" ] || return 0 config_get server_addr "$cfg" server_addr [ -n "$server_addr" ] || return 0 config_get interface "$cfg" interface if [ -z "$interface" ]; then xappend "--dhcp-relay=$local_addr,$server_addr" else xappend "--dhcp-relay=$local_addr,$server_addr,$interface" fi } service_triggers() { procd_add_reload_trigger "dhcp" procd_add_raw_trigger "interface.*" 2000 /etc/init.d/dnsmasq reload } boot() { BOOT=1 start "$@" } start_service() { [ -n "$BOOT" ] && return include /lib/functions config_load dhcp # before we can call xappend mkdir -p /var/run/dnsmasq/ mkdir -p $(dirname $CONFIGFILE) mkdir -p /var/lib/misc touch /tmp/dhcp.leases [ -f "$TIMESTAMPFILE" ] && rm -f "$TIMESTAMPFILE" echo "# auto-generated config file from /etc/config/dhcp" > $CONFIGFILE_TMP echo "# auto-generated config file from /etc/config/dhcp" > $HOSTFILE # if we did this last, we could override auto-generated config [ -f /etc/dnsmasq.conf ] && { xappend "--conf-file=/etc/dnsmasq.conf" } $PROG --version | grep -osqE "^Compile time options:.* DHCPv6( |$)" && DHCPv6CAPABLE=1 || DHCPv6CAPABLE=0 args="" config_foreach dnsmasq dnsmasq config_foreach dhcp_host_add host echo >> $CONFIGFILE_TMP config_foreach dhcp_boot_add boot config_foreach dhcp_mac_add mac config_foreach dhcp_tag_add tag config_foreach dhcp_vendorclass_add vendorclass config_foreach dhcp_userclass_add userclass config_foreach dhcp_circuitid_add circuitid config_foreach dhcp_remoteid_add remoteid config_foreach dhcp_subscrid_add subscrid config_foreach dhcp_domain_add domain config_foreach dhcp_hostrecord_add hostrecord config_foreach dhcp_relay_add relay # add own hostname [ $ADD_LOCAL_HOSTNAME -eq 1 ] && { local lanaddr lanaddr6 local ulaprefix="$(uci_get network @globals[0] ula_prefix)" local hostname="$(uci_get system @system[0] hostname Lede)" network_get_ipaddr lanaddr "lan" && { dhcp_domain_add "" "$hostname" "$lanaddr" } [ -n "$ulaprefix" ] && network_get_ipaddrs6 lanaddr6 "lan" && { for lanaddr6 in $lanaddr6; do case "$lanaddr6" in "${ulaprefix%%:/*}"*) dhcp_domain_add "" "$hostname" "$lanaddr6" ;; esac done } } echo >> $CONFIGFILE_TMP config_foreach dhcp_srv_add srvhost config_foreach dhcp_mx_add mxhost echo >> $CONFIGFILE_TMP config_get odhcpd_is_active odhcpd maindhcp if [ "$odhcpd_is_active" != "1" ]; then config_foreach dhcp_add dhcp fi echo >> $CONFIGFILE_TMP config_foreach dhcp_cname_add cname echo >> $CONFIGFILE_TMP mv -f $CONFIGFILE_TMP $CONFIGFILE rm -f /tmp/resolv.conf [ $ADD_LOCAL_DOMAIN -eq 1 ] && [ -n "$DOMAIN" ] && { echo "search $DOMAIN" >> /tmp/resolv.conf } DNS_SERVERS="$DNS_SERVERS 127.0.0.1" for DNS_SERVER in $DNS_SERVERS ; do echo "nameserver $DNS_SERVER" >> /tmp/resolv.conf done procd_open_instance procd_set_param command $PROG -C $CONFIGFILE -k -x /var/run/dnsmasq/dnsmasq.pid procd_set_param file $CONFIGFILE procd_set_param respawn procd_add_jail dnsmasq ubus log procd_add_jail_mount $CONFIGFILE $TRUSTANCHORSFILE $HOSTFILE /etc/passwd /etc/group /etc/TZ /dev/null /dev/urandom /etc/dnsmasq.conf /tmp/dnsmasq.d /tmp/resolv.conf.auto /etc/hosts /etc/ethers $EXTRA_MOUNT procd_add_jail_mount_rw /var/run/dnsmasq/ /tmp/dhcp.leases procd_close_instance } reload_service() { rc_procd start_service "$@" return 0 } stop_service() { [ -f /tmp/resolv.conf ] && { rm -f /tmp/resolv.conf ln -s /tmp/resolv.conf.auto /tmp/resolv.conf } rm -f /var/run/dnsmasq.*.dhcp }