packages: sync with 19.07-rc2

This commit is contained in:
LEAN-ESX 2019-12-04 02:45:43 -08:00
parent 13f3fbaedb
commit eeea03f457
46 changed files with 5989 additions and 1163 deletions

65
net/banip/Makefile Normal file
View File

@ -0,0 +1,65 @@
#
# Copyright (c) 2018-2019 Dirk Brenken (dev@brenken.org)
# This is free software, licensed under the GNU General Public License v3.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=banip
PKG_VERSION:=0.3.10
PKG_RELEASE:=1
PKG_LICENSE:=GPL-3.0-or-later
PKG_MAINTAINER:=Dirk Brenken <dev@brenken.org>
include $(INCLUDE_DIR)/package.mk
define Package/banip
SECTION:=net
CATEGORY:=Network
TITLE:=Ban incoming and/or outgoing ip adresses via ipsets
DEPENDS:=+jshn +jsonfilter +ip +ipset +iptables +ca-bundle +logd
PKGARCH:=all
endef
define Package/banip/description
Powerful banIP script to block ip addresses via ipsets.
The script supports many ip blacklist sites plus manual black- and whitelist overrides.
Please see https://github.com/openwrt/packages/blob/master/net/banip/files/README.md for further information.
endef
define Package/banip/conffiles
/etc/config/banip
/etc/banip/banip.whitelist
/etc/banip/banip.blacklist
endef
define Build/Prepare
endef
define Build/Configure
endef
define Build/Compile
endef
define Package/banip/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) ./files/banip.sh $(1)/usr/bin
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./files/banip.init $(1)/etc/init.d/banip
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) ./files/banip.conf $(1)/etc/config/banip
$(INSTALL_DIR) $(1)/etc/banip
$(INSTALL_BIN) ./files/banip.service $(1)/etc/banip
$(INSTALL_CONF) ./files/banip.blacklist $(1)/etc/banip
$(INSTALL_CONF) ./files/banip.whitelist $(1)/etc/banip
$(INSTALL_DIR) $(1)/etc/hotplug.d/firewall
$(INSTALL_DATA) ./files/banip.hotplug $(1)/etc/hotplug.d/firewall/30-banip
endef
$(eval $(call BuildPackage,banip))

96
net/banip/files/README.md Normal file
View File

@ -0,0 +1,96 @@
# banIP - ban incoming and/or outgoing ip adresses via ipsets
## Description
IP address blocking is commonly used to protect against brute force attacks, prevent disruptive or unauthorized address(es) from access or it can be used to restrict access to or from a particular geographic area — for example.
## Main Features
* support many IP blocklist sources (free for private usage, for commercial use please check their individual licenses):
* zero-conf like automatic installation & setup, usually no manual changes needed
* automatically selects one of the following download utilities: aria2c, curl, uclient-fetch, wget
* Really fast downloads & list processing as they are handled in parallel as background jobs in a configurable 'Download Queue'
* full IPv4 and IPv6 support
* ipsets (one per source) are used to ban a large number of IP addresses
* supports blocking by ASN numbers
* supports blocking by iso country codes
* supports local white & blacklist (IPv4, IPv6 & CIDR notation), located by default in /etc/banip/banip.whitelist and /etc/banip/banip.blacklist
* auto-add unsuccessful LuCI and ssh login attempts via 'dropbear' or 'sshd' to local blacklist (see 'ban_autoblacklist' option)
* auto-add the uplink subnet to local whitelist (see 'ban_autowhitelist' option)
* provides a small background log monitor to ban unsuccessful login attempts in real-time
* per source configuration of SRC (incoming) and DST (outgoing)
* integrated IPSet-Lookup
* integrated RIPE-Lookup
* blocklist source parsing by fast & flexible regex rulesets
* minimal status & error logging to syslog, enable debug logging to receive more output
* procd based init system support (start/stop/restart/reload/refresh/status)
* procd network interface trigger support
* automatic blocklist backup & restore, they will be used in case of download errors or during startup
* output comprehensive runtime information via LuCI or via 'status' init command
* strong LuCI support
* optional: add new banIP sources on your own
## Prerequisites
* [OpenWrt](https://openwrt.org), tested with the stable release series (19.07) and with the latest snapshot
* download utility: 'uclient-fetch' with one of the 'libustream-*' ssl libraries, 'wget', 'aria2c' or 'curl' is required
## Installation & Usage
* install 'banip' (_opkg install banip_)
* at minimum configure the needed IP blocklist sources, the download utility and enable the banIP service in _/etc/config/banip_
* control the banip service manually with _/etc/init.d/banip_ start/stop/restart/reload/refresh/status or use the LuCI frontend
## LuCI banIP companion package
* it's recommended to use the provided LuCI frontend to control all aspects of banIP
* install 'luci-app-banip' (_opkg install luci-app-banip_)
* the application is located in LuCI under 'Services' menu
## banIP config options
* usually the pre-configured banIP setup works quite well and no manual overrides are needed
* the following options apply to the 'global' config section:
* ban\_enabled => main switch to enable/disable banIP service (bool/default: '0', disabled)
* ban\_automatic => determine the L2/L3 WAN network device automatically (bool/default: '1', enabled)
* ban\_iface => space separated list of WAN network interface(s)/device(s) used by banIP (default: not set, automatically detected)
* ban\_realtime => a small log/banIP background monitor to block SSH/LuCI brute force attacks in realtime (bool/default: 'false', disabled)
* the following options apply to the 'extra' config section:
* ban\_debug => enable/disable banIP debug output (bool/default: '0', disabled)
* ban\_nice => set the nice level of the banIP process and all sub-processes (int/default: '0', standard priority)
* ban\_triggerdelay => additional trigger delay in seconds before banIP processing begins (int/default: '2')
* ban\_backupdir => target directory for banIP backups (default: '/tmp')
* ban\_sshdaemon => select the SSH daemon for logfile parsing, 'dropbear' or 'sshd' (default: 'dropbear')
* ban\_starttype => select the used start type during boot, 'start', 'refresh' or 'reload' (default: 'start')
* ban\_maxqueue => size of the download queue to handle downloads & IPSet processing in parallel (int/default: '4')
* ban\_fetchutil => name of the used download utility: 'uclient-fetch', 'wget', 'curl', 'aria2c' (default: not set, automatically detected)
* ban\_fetchparm => special config options for the download utility (default: not set)
* ban\_autoblacklist => store auto-addons temporary in ipset and permanently in local blacklist as well (bool/default: '1', enabled)
* ban\_autowhitelist => store auto-addons temporary in ipset and permanently in local whitelist as well (bool/default: '1', enabled)
## Examples
**receive banIP runtime information:**
<pre><code>
/etc/init.d/banip status
::: banIP runtime information
+ status : enabled
+ version : 0.3.0
+ util_info : /usr/bin/aria2c, true
+ ipset_info : 10 IPSets with overall 106729 IPs/Prefixes
+ backup_dir : /tmp
+ last_run : 03.10.2019 19:15:25
+ system : UBNT-ERX, OpenWrt SNAPSHOT r11102-ced4c0e635
</code></pre>
**cronjob for a regular IPSet blocklist update (/etc/crontabs/root):**
<pre><code>
0 06 * * * /etc/init.d/banip reload
</code></pre>
## Support
Please join the banIP discussion in this [forum thread](https://forum.openwrt.org/t/banip-support-thread/16985) or contact me by mail <dev@brenken.org>
## Removal
* stop all banIP related services with _/etc/init.d/banip stop_
* optional: remove the banip package (_opkg remove banip_)
Have fun!
Dirk

View File

224
net/banip/files/banip.conf Normal file
View File

@ -0,0 +1,224 @@
config banip 'global'
option ban_enabled '0'
option ban_basever '0.3'
option ban_automatic '1'
option ban_realtime 'false'
config banip 'extra'
option ban_debug '0'
option ban_maxqueue '4'
config source 'whitelist'
option ban_src '/etc/banip/banip.whitelist'
option ban_src_6 '/etc/banip/banip.whitelist'
option ban_src_desc 'Always allow these IPs (IPv4/IPv6)'
option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,2})?)([[:space:]]|$)/{print \"add whitelist \"\$1}'
option ban_src_rset_6 '/^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}(:\/[0-9]{1,2})?([[:space:]]|$)/{print \"add whitelist_6 \"\$1}'
option ban_src_settype 'net'
option ban_src_ruletype 'src+dst'
option ban_src_on '1'
option ban_src_on_6 '0'
config source 'blacklist'
option ban_src '/etc/banip/banip.blacklist'
option ban_src_6 '/etc/banip/banip.blacklist'
option ban_src_desc 'Always deny these IPs (IPv4/IPv6)'
option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,2})?)([[:space:]]|$)/{print \"add blacklist \"\$1}'
option ban_src_rset_6 '/^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}(:\/[0-9]{1,2})?([[:space:]]|$)/{print \"add blacklist_6 \"\$1}'
option ban_src_settype 'net'
option ban_src_ruletype 'src+dst'
option ban_src_on '0'
option ban_src_on_6 '0'
config source 'bogon'
option ban_src 'https://www.team-cymru.org/Services/Bogons/fullbogons-ipv4.txt'
option ban_src_6 'https://www.team-cymru.org/Services/Bogons/fullbogons-ipv6.txt'
option ban_src_desc 'Bogon prefixes, plus prefixes that have been allocated to RIRs but not yet assigned to ISPs (IPv4/IPv6)'
option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,2})?)([[:space:]]|$)/{print \"add bogon \"\$1}'
option ban_src_rset_6 '/^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}(:\/[0-9]{1,2})?([[:space:]]|$)/{print \"add bogon_6 \"\$1}'
option ban_src_settype 'net'
option ban_src_ruletype 'src+dst'
option ban_src_on '0'
option ban_src_on_6 '0'
config source 'DoH'
option ban_src 'https://raw.githubusercontent.com/dibdot/DoH-IP-blocklists/master/doh-ipv4.txt'
option ban_src_6 'https://raw.githubusercontent.com/dibdot/DoH-IP-blocklists/master/doh-ipv6.txt'
option ban_src_desc 'List of public DoH providers (DNS over HTTPS) (IPv4/IPv6)'
option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,2})?)([[:space:]]|$)/{print \"add DoH \"\$1}'
option ban_src_rset_6 '/^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}(:\/[0-9]{1,2})?([[:space:]]|$)/{print \"add DoH_6 \"\$1}'
option ban_src_settype 'net'
option ban_src_ruletype 'src+dst'
option ban_src_on '0'
option ban_src_on_6 '0'
config source 'tor'
option ban_src 'https://check.torproject.org/exit-addresses'
option ban_src_desc 'List of Tor Exit Nodes (IPv4)'
option ban_src_rset '/^(ExitAddress ([0-9]{1,3}\.){3}[0-9]{1,3})([[:space:]]|$)/{print \"add tor \"\$2}'
option ban_src_settype 'ip'
option ban_src_ruletype 'src'
option ban_src_on '0'
option ban_src_on_6 '0'
config source 'threat'
option ban_src 'https://rules.emergingthreats.net/fwrules/emerging-Block-IPs.txt'
option ban_src_desc 'Emerging Threats (IPv4)'
option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,2})?)([[:space:]]|$)/{print \"add threat \"\$1}'
option ban_src_settype 'net'
option ban_src_ruletype 'src'
option ban_src_on '0'
config source 'debl'
option ban_src 'https://www.blocklist.de/downloads/export-ips_all.txt'
option ban_src_6 'https://www.blocklist.de/downloads/export-ips_all.txt'
option ban_src_desc 'Fail2ban reporting service (IPv4/IPv6)'
option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3})([[:space:]]|$)/{print \"add debl \"\$1}'
option ban_src_rset_6 '/^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}(:\/[0-9]{1,2})?([[:space:]]|$)/{print \"add debl_6 \"\$1}'
option ban_src_settype 'ip'
option ban_src_ruletype 'src'
option ban_src_on '0'
option ban_src_on_6 '0'
config source 'myip'
option ban_src 'https://www.myip.ms/files/blacklist/general/latest_blacklist.txt'
option ban_src_6 'https://www.myip.ms/files/blacklist/general/latest_blacklist.txt'
option ban_src_desc 'IP blacklist provided by myip.ms (IPv4/IPv6)'
option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3})([[:space:]]|$)/{print \"add myip \"\$1}'
option ban_src_rset_6 '/^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}(:\/[0-9]{1,2})?([[:space:]]|$)/{print \"add myip_6 \"\$1}'
option ban_src_settype 'ip'
option ban_src_ruletype 'src'
option ban_src_on '0'
option ban_src_on_6 '0'
config source 'yoyo'
option ban_src 'https://pgl.yoyo.org/adservers/iplist.php?ipformat=plain&showintro=0&mimetype=plaintext'
option ban_src_desc 'IP blocklist provided by Peter Lowe (IPv4)'
option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3})([[:space:]]|$)/{print \"add yoyo \"\$1}'
option ban_src_settype 'ip'
option ban_src_ruletype 'src'
option ban_src_on '0'
config source 'sslbl'
option ban_src 'https://sslbl.abuse.ch/blacklist/sslipblacklist.csv'
option ban_src_desc 'SSL Blacklist by abuse.ch (IPv4)'
option ban_src_rset 'BEGIN{FS=\",\"}/(([0-9]{1,3}\.){3}[0-9]{1,3},).*/{print \"add sslbl \"\$2}'
option ban_src_settype 'ip'
option ban_src_ruletype 'src'
option ban_src_on '0'
config source 'ransomware'
option ban_src 'https://ransomwaretracker.abuse.ch/downloads/RW_IPBL.txt'
option ban_src_desc 'Ransomware Tracker by abuse.ch (IPv4)'
option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3})([[:space:]]|$)/{print \"add ransomware \"\$1}'
option ban_src_settype 'ip'
option ban_src_ruletype 'src'
option ban_src_on '0'
config source 'feodo'
option ban_src 'https://feodotracker.abuse.ch/downloads/ipblocklist.txt'
option ban_src_desc 'Feodo Tracker by abuse.ch (IPv4)'
option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3})([[:space:]]|$)/{print \"add feodo \"\$1}'
option ban_src_settype 'ip'
option ban_src_ruletype 'src'
option ban_src_on '0'
config source 'dshield'
option ban_src 'https://feeds.dshield.org/block.txt'
option ban_src_desc 'Dshield recommended IP blocklist. Contains top 20 attacking class C subnets (IPv4)'
option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3})([[:space:]]|$)/{print \"add dshield \"\$1 \"/\"\$3}'
option ban_src_settype 'net'
option ban_src_ruletype 'src'
option ban_src_on '0'
config source 'proxy'
option ban_src 'https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/proxylists.ipset'
option ban_src_desc 'List of Open Proxies (IPv4)'
option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3})([[:space:]]|$)/{print \"add proxy \"\$1}'
option ban_src_settype 'ip'
option ban_src_ruletype 'src'
option ban_src_on '0'
config source 'iblocklist'
option ban_src 'https://list.iblocklist.com/?list=dgxtneitpuvgqqcpfulq&fileformat=cidr&archiveformat=gz'
option ban_src_desc 'Contains advertising trackers and a short list of bad/intrusive porn sites (IPv4)'
option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,2})?)([[:space:]]|$)/{print \"add iblocklist \"\$1}'
option ban_src_settype 'net'
option ban_src_ruletype 'src'
option ban_src_on '0'
config source 'drop'
option ban_src 'https://www.spamhaus.org/drop/drop.txt'
option ban_src_6 'https://www.spamhaus.org/drop/dropv6.txt'
option ban_src_desc 'Spamhaus drop compilation (IPv4/IPv6)'
option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,2})?)([[:space:]]|$)/{print \"add drop \"\$1}'
option ban_src_rset_6 '/^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}(:\/[0-9]{1,2})?([[:space:]]|$)/{print \"add drop_6 \"\$1}'
option ban_src_settype 'net'
option ban_src_ruletype 'src'
option ban_src_on '0'
option ban_src_on_6 '0'
config source 'edrop'
option ban_src 'https://www.spamhaus.org/drop/edrop.txt'
option ban_src_desc 'Spamhaus edrop compilation (IPv4)'
option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,2})?)([[:space:]]|$)/{print \"add edrop \"\$1}'
option ban_src_settype 'net'
option ban_src_ruletype 'src'
option ban_src_on '0'
config source 'firehol1'
option ban_src 'https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level1.netset'
option ban_src_desc 'Firehol Level 1 compilation. Contains bogons, spamhaus drop and edrop, dshield and malware lists (IPv4)'
option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,2})?)([[:space:]]|$)/{print \"add firehol1 \"\$1}'
option ban_src_settype 'net'
option ban_src_ruletype 'src'
option ban_src_on '0'
config source 'firehol2'
option ban_src 'https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level2.netset'
option ban_src_desc 'Firehol Level 2 compilation. Contains blocklists that track attacks, during the last 48 hours (IPv4)'
option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,2})?)([[:space:]]|$)/{print \"add firehol2 \"\$1}'
option ban_src_settype 'net'
option ban_src_ruletype 'src'
option ban_src_on '0'
config source 'firehol3'
option ban_src 'https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level3.netset'
option ban_src_desc 'Firehol Level 3 compilation. Contains blocklists that track attacks, spyware and viruses (IPv4)'
option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,2})?)([[:space:]]|$)/{print \"add firehol3 \"\$1}'
option ban_src_settype 'net'
option ban_src_ruletype 'src'
option ban_src_on '0'
config source 'firehol4'
option ban_src 'https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level4.netset'
option ban_src_desc 'Firehol Level 4 compilation. May include a large number of false positives (IPv4)'
option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,2})?)([[:space:]]|$)/{print \"add firehol4 \"\$1}'
option ban_src_settype 'net'
option ban_src_ruletype 'src'
option ban_src_on '0'
config source 'country'
option ban_src 'https://stat.ripe.net/data/country-resource-list/data.json?resource='
option ban_src_6 'https://stat.ripe.net/data/country-resource-list/data.json?resource='
option ban_src_desc 'Build a dynamic IPSet by country iso codes based on RIPE data (IPv4/IPv6)'
option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,2})?)([[:space:]]|$)/{print \"add country \"\$1}'
option ban_src_rset_6 '/^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}(:\/[0-9]{1,2})?([[:space:]]|$)/{print \"add country_6 \"\$1}'
list ban_src_cat 'de'
option ban_src_settype 'net'
option ban_src_ruletype 'src'
option ban_src_on '0'
option ban_src_on_6 '0'
config source 'asn'
option ban_src 'https://stat.ripe.net/data/announced-prefixes/data.json?resource='
option ban_src_6 'https://stat.ripe.net/data/announced-prefixes/data.json?resource='
option ban_src_desc 'Build a dynamic IPSet by ASN numbers based on RIPE data (IPv4/IPv6)'
option ban_src_rset '/^(([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,2})?)([[:space:]]|$)/{print \"add asn \"\$1}'
option ban_src_rset_6 '/^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}(:\/[0-9]{1,2})?([[:space:]]|$)/{print \"add asn_6 \"\$1}'
list ban_src_cat '32934'
option ban_src_settype 'net'
option ban_src_ruletype 'src'
option ban_src_on '0'
option ban_src_on_6 '0'

View File

@ -0,0 +1,15 @@
#!/bin/sh
#
[ "${ACTION}" != "add" ] && exit 0
ban_iface="wan"
[ -r "/lib/functions/network.sh" ] && { . "/lib/functions/network.sh"; network_find_wan ban_iface; }
[ "${INTERFACE}" != "${ban_iface}" ] && exit 0
ban_pidfile="/var/run/banip.pid"
ban_enabled="$(/etc/init.d/banip enabled; printf "%u" "${?}")"
if [ "${ban_enabled}" = "0" ] && [ ! -s "${ban_pidfile}" ]
then
/etc/init.d/banip refresh
fi
exit 0

109
net/banip/files/banip.init Executable file
View File

@ -0,0 +1,109 @@
#!/bin/sh /etc/rc.common
#
START=30
USE_PROCD=1
EXTRA_COMMANDS="refresh"
EXTRA_HELP=" refresh Refresh ipsets without new list downloads"
ban_init="/etc/init.d/banip"
ban_script="/usr/bin/banip.sh"
ban_pidfile="/var/run/banip.pid"
if [ -s "${ban_pidfile}" ] && { [ "${action}" = "start" ] || [ "${action}" = "stop" ] || \
[ "${action}" = "restart" ] || [ "${action}" = "reload" ] || [ "${action}" = "refresh" ]; }
then
exit 0
fi
boot()
{
[ -s "${ban_pidfile}" ] && > "${ban_pidfile}"
rc_procd start_service
}
start_service()
{
if [ "$("${ban_init}" enabled; printf "%u" ${?})" -eq 0 ]
then
if [ "${action}" = "boot" ]
then
return 0
fi
procd_open_instance "banip"
procd_set_param command "${ban_script}" "${@}"
procd_set_param pidfile "${ban_pidfile}"
procd_set_param nice "$(uci_get banip extra ban_nice "0")"
procd_set_param stdout 1
procd_set_param stderr 1
procd_close_instance
fi
}
refresh()
{
rc_procd start_service refresh
}
reload_service()
{
rc_procd start_service reload
}
stop_service()
{
rc_procd "${ban_script}" stop
}
restart()
{
rc_procd start_service restart
}
status_service()
{
local key keylist value
local rtfile="$(uci_get banip global ban_rtfile "/tmp/ban_runtime.json")"
json_load_file "${rtfile}" >/dev/null 2>&1
json_select data >/dev/null 2>&1
if [ "${?}" -eq 0 ]
then
printf "%s\\n" "::: banIP runtime information"
json_get_keys keylist
for key in ${keylist}
do
json_get_var value "${key}"
printf " + %-10s : %s\\n" "${key}" "${value}"
done
else
printf "%s\\n" "::: no banIP runtime information available"
fi
}
service_triggers()
{
local trigger trigger_list="$(uci_get banip global ban_trigger)"
local delay="$(uci_get banip extra ban_triggerdelay "2")"
local type="$(uci_get banip extra ban_starttype "start")"
PROCD_RELOAD_DELAY=$((${delay}*1000))
if [ -z "${trigger_list}" ] && [ -r "/lib/functions/network.sh" ]
then
. "/lib/functions/network.sh"
network_find_wan trigger_list
fi
if [ -n "${trigger_list}" ]
then
for trigger in ${trigger_list}
do
procd_add_interface_trigger "interface.*.up" "${trigger}" "${ban_init}" "${type}"
done
else
procd_add_raw_trigger "interface.*.up" ${PROCD_RELOAD_DELAY} "${ban_init}" "${type}"
fi
procd_add_reload_trigger "banip"
}

35
net/banip/files/banip.service Executable file
View File

@ -0,0 +1,35 @@
#!/bin/sh
# log service to trace failed ssh/luci logins and conditionally refresh banIP
# written by Dirk Brenken (dev@brenken.org)
# This is free software, licensed under the GNU General Public License v3.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
LC_ALL=C
PATH="/usr/sbin:/usr/bin:/sbin:/bin"
ban_ver="${1}"
ban_sshdaemon="${2}"
ban_logger="$(command -v logger)"
ban_logread="$(command -v logread)"
f_log()
{
local class="${1}" log_msg="${2}"
if [ -x "${ban_logger}" ]
then
"${ban_logger}" -p "${class}" -t "banIP-${ban_ver}[${$}]" "${log_msg}"
else
printf "%s %s %s\\n" "${class}" "banIP-${ban_ver}[${$}]" "${log_msg}"
fi
}
if [ -x "${ban_logread}" ]
then
f_log "info" "log/banIP service started"
"${ban_logread}" -f -e "${ban_sshdaemon}\|luci: failed login" | \
{ grep -qE "Exit before auth|luci: failed login|[0-9]+ \[preauth\]$"; [ $? -eq 0 ] && /etc/init.d/banip refresh; }
else
f_log "err" "can't start log/banIP service"
fi

958
net/banip/files/banip.sh Executable file
View File

@ -0,0 +1,958 @@
#!/bin/sh
# banIP - ban incoming and outgoing ip adresses/subnets via ipset
# written by Dirk Brenken (dev@brenken.org)
# This is free software, licensed under the GNU General Public License v3.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# (s)hellcheck exceptions
# shellcheck disable=1091 disable=2039 disable=2143 disable=2181 disable=2188
# set initial defaults
#
LC_ALL=C
PATH="/usr/sbin:/usr/bin:/sbin:/bin"
ban_ver="0.3.10"
ban_basever=""
ban_enabled=0
ban_automatic="1"
ban_sources=""
ban_iface=""
ban_debug=0
ban_backupdir="/mnt"
ban_maxqueue=4
ban_autoblacklist=1
ban_autowhitelist=1
ban_realtime="false"
ban_fetchutil=""
ban_ipt="$(command -v iptables)"
ban_ipt_save="$(command -v iptables-save)"
ban_ipt_restore="$(command -v iptables-restore)"
ban_ipt6="$(command -v ip6tables)"
ban_ipt6_save="$(command -v ip6tables-save)"
ban_ipt6_restore="$(command -v ip6tables-restore)"
ban_ipset="$(command -v ipset)"
ban_logger="$(command -v logger)"
ban_chain="banIP"
ban_action="${1:-"start"}"
ban_pidfile="/var/run/banip.pid"
ban_rtfile="/tmp/ban_runtime.json"
ban_logservice="/etc/banip/banip.service"
ban_sshdaemon=""
ban_setcnt=0
ban_cnt=0
# load environment
#
f_envload()
{
# get system information
#
ban_sysver="$(ubus -S call system board 2>/dev/null | jsonfilter -e '@.model' -e '@.release.description' | \
awk 'BEGIN{ORS=", "}{print $0}' | awk '{print substr($0,1,length($0)-2)}')"
# parse 'global' and 'extra' section by callback
#
config_cb()
{
local type="${1}"
if [ "${type}" = "banip" ]
then
option_cb()
{
local option="${1}"
local value="${2}"
eval "${option}=\"${value}\""
}
else
reset_cb
fi
}
# parse 'source' typed sections
#
parse_config()
{
local value opt section="${1}" options="ban_src ban_src_6 ban_src_rset ban_src_rset_6 ban_src_settype ban_src_ruletype ban_src_on ban_src_on_6 ban_src_cat"
for opt in ${options}
do
config_get value "${section}" "${opt}"
if [ -n "${value}" ]
then
eval "${opt}_${section}=\"${value}\""
if [ "${opt}" = "ban_src" ]
then
eval "ban_sources=\"${ban_sources} ${section}\""
elif [ "${opt}" = "ban_src_6" ]
then
eval "ban_sources=\"${ban_sources} ${section}_6\""
fi
fi
done
}
# load config
#
config_load banip
config_foreach parse_config source
# log daemon check
#
if [ "$(/etc/init.d/log running; printf "%u" "${?}")" -eq 1 ]
then
unset ban_logger
f_log "info" "your log daemon 'logd' is not running, please enable 'logd' to use this service"
f_rmtemp
exit 1
fi
# version check
#
if [ -z "${ban_basever}" ] || [ "${ban_ver%.*}" != "${ban_basever}" ]
then
f_log "info" "your banIP config seems to be too old, please update your config with the '--force-maintainer' opkg option"
f_rmtemp
exit 0
fi
# create temp directory & files
#
f_temp
# check status
#
if [ "${ban_enabled}" -eq 0 ]
then
f_bgserv "stop"
f_jsnup disabled
f_ipset destroy
f_rmbackup
f_rmtemp
f_log "info" "banIP is currently disabled, please set ban_enabled to '1' to use this service"
exit 0
fi
}
# check environment
#
f_envcheck()
{
local util utils packages iface tmp cnt=0 cnt_max=0
f_jsnup "running"
f_log "info" "start banIP processing (${ban_action})"
# check backup directory
#
if [ ! -d "${ban_backupdir}" ]
then
f_log "err" "the backup directory '${ban_backupdir}' does not exist or has not been mounted yet, please create the directory or raise the 'ban_triggerdelay' to defer the banIP start"
fi
# get wan devices and wan subnets
#
if [ "${ban_automatic}" = "1" ]
then
while [ "${cnt}" -le 30 ]
do
network_find_wan iface
if [ -n "${iface}" ] && [ -z "$(printf "%s\\n" "${ban_iface}" | grep -F "${iface}")" ]
then
ban_iface="${ban_iface} ${iface}"
if [ "${cnt_max}" -eq 0 ]
then
cnt_max=$((cnt+5))
fi
fi
network_find_wan6 iface
if [ -n "${iface}" ] && [ -z "$(printf "%s\\n" "${ban_iface}" | grep -F "${iface}")" ]
then
ban_iface="${ban_iface} ${iface}"
if [ "${cnt_max}" -eq 0 ]
then
cnt_max=$((cnt+5))
fi
fi
if [ -z "${ban_iface}" ] || [ "${cnt}" -le "${cnt_max}" ]
then
network_flush_cache
cnt=$((cnt+1))
sleep 1
else
break
fi
done
fi
for iface in ${ban_iface}
do
network_get_device tmp "${iface}"
if [ -n "${tmp}" ] && [ -z "$(printf "%s\\n" "${ban_dev}" | grep -F "${tmp}")" ]
then
ban_dev="${ban_dev} ${tmp}"
else
network_get_physdev tmp "${iface}"
if [ -n "${tmp}" ] && [ -z "$(printf "%s\\n" "${ban_dev}" | grep -F "${tmp}")" ]
then
ban_dev="${ban_dev} ${tmp}"
fi
fi
network_get_subnets tmp "${iface}"
if [ -n "${tmp}" ] && [ -z "$(printf "%s\\n" "${ban_subnets}" | grep -F "${tmp}")" ]
then
ban_subnets="${ban_subnets} ${tmp}"
fi
network_get_subnets6 tmp "${iface}"
if [ -n "${tmp}" ] && [ -z "$(printf "%s\\n" "${ban_subnets6}" | grep -F "${tmp}")" ]
then
ban_subnets6="${ban_subnets6} ${tmp}"
fi
done
ban_dev_all="$(ip link show 2>/dev/null | awk 'BEGIN{FS="[@: ]"}/^[0-9:]/{if($3!="lo"){print $3}}')"
if [ -z "${ban_iface}" ] || [ -z "${ban_dev}" ] || [ -z "${ban_dev_all}" ]
then
f_log "err" "wan interface(s)/device(s) (${ban_iface:-"-"}/${ban_dev:-"-"}) not found, please please check your configuration"
fi
# check fetch utility
#
if [ -z "${ban_fetchutil}" ]
then
cnt_max=$((cnt+5))
while [ -z "${packages}" ]
do
packages="$(opkg list-installed 2>/dev/null)"
if [ "${cnt}" -gt "${cnt_max}" ]
then
break
fi
cnt=$((cnt+1))
sleep 1
done
if [ -n "${packages}" ]
then
utils="aria2c curl wget uclient-fetch"
for util in ${utils}
do
if { [ "${util}" = "uclient-fetch" ] && [ -n "$(printf "%s\\n" "${packages}" | grep "^libustream-")" ]; } || \
{ [ "${util}" = "wget" ] && [ -n "$(printf "%s\\n" "${packages}" | grep "^wget -")" ]; } || \
{ [ "${util}" != "uclient-fetch" ] && [ "${util}" != "wget" ]; }
then
ban_fetchutil="$(command -v "${util}")"
if [ -x "${ban_fetchutil}" ]
then
break
fi
fi
unset ban_fetchutil util
done
fi
else
util="${ban_fetchutil}"
ban_fetchutil="$(command -v "${util}")"
if [ ! -x "${ban_fetchutil}" ]
then
unset ban_fetchutil util
fi
fi
case "${util}" in
"aria2c")
ban_fetchparm="${ban_fetchparm:-"--timeout=20 --allow-overwrite=true --auto-file-renaming=false --check-certificate=true --dir=" " -o"}"
;;
"curl")
ban_fetchparm="${ban_fetchparm:-"--connect-timeout 20 -o"}"
;;
"uclient-fetch")
ban_fetchparm="${ban_fetchparm:-"--timeout=20 -O"}"
;;
"wget")
ban_fetchparm="${ban_fetchparm:-"--no-cache --no-cookies --max-redirect=0 --timeout=20 -O"}"
;;
esac
if [ -z "${ban_fetchutil}" ] || [ -z "${ban_fetchparm}" ]
then
f_log "err" "download utility with SSL support not found, please install 'uclient-fetch' with a 'libustream-*' variant or another download utility like 'wget', 'curl' or 'aria2'"
fi
# check ssh daemon
#
if [ -z "${ban_sshdaemon}" ]
then
utils="dropbear sshd"
for util in ${utils}
do
if [ -x "$(command -v "${util}")" ]
then
ban_sshdaemon="${util}"
break
fi
done
fi
if [ -z "${ban_sshdaemon}" ]
then
f_log "err" "ssh daemon not found, please install 'dropbear' or 'sshd'"
fi
}
# create temporary files and directories
#
f_temp()
{
if [ -d "/tmp" ] && [ -z "${ban_tmpdir}" ]
then
ban_tmpdir="$(mktemp -p /tmp -d)"
ban_tmpfile="$(mktemp -p "${ban_tmpdir}" -tu)"
elif [ ! -d "/tmp" ]
then
f_log "err" "the temp directory '/tmp' does not exist or has not been mounted yet, please create the directory or raise the 'ban_triggerdelay' to defer the banIP start"
fi
if [ ! -f "${ban_pidfile}" ] || [ ! -s "${ban_pidfile}" ]
then
printf "%s" "${$}" > "${ban_pidfile}"
fi
}
# remove temporary files and directories
#
f_rmtemp()
{
if [ -d "${ban_tmpdir}" ]
then
rm -rf "${ban_tmpdir}"
fi
> "${ban_pidfile}"
}
# remove backup files
#
f_rmbackup()
{
if [ -d "${ban_backupdir}" ]
then
rm -f "${ban_backupdir}"/banIP.*.gz
fi
}
# iptables rules engine
#
f_iptrule()
{
local rc timeout="-w 5" action="${1}" rule="${2}"
if [ "${src_name##*_}" = "6" ]
then
if [ -x "${ban_ipt6}" ]
then
rc="$("${ban_ipt6}" "${timeout}" -C ${rule} 2>/dev/null; printf "%u" ${?})"
if { [ "${rc}" -ne 0 ] && { [ "${action}" = "-A" ] || [ "${action}" = "-I" ]; } } || \
{ [ "${rc}" -eq 0 ] && [ "${action}" = "-D" ]; }
then
"${ban_ipt6}" "${timeout}" "${action}" ${rule} 2>/dev/null
fi
fi
else
if [ -x "${ban_ipt}" ]
then
rc="$("${ban_ipt}" "${timeout}" -C ${rule} 2>/dev/null; printf "%u" ${?})"
if { [ "${rc}" -ne 0 ] && { [ "${action}" = "-A" ] || [ "${action}" = "-I" ]; } } || \
{ [ "${rc}" -eq 0 ] && [ "${action}" = "-D" ]; }
then
"${ban_ipt}" "${timeout}" "${action}" ${rule} 2>/dev/null
fi
fi
fi
if [ "${?}" -ne 0 ]
then
> "${tmp_err}"
f_log "info" "can't create iptables rule: action: '${action:-"-"}', rule: '${rule:-"-"}'"
fi
}
# remove/add iptables rules
#
f_iptadd()
{
local rm="${1}" dev
for dev in ${ban_dev_all}
do
f_iptrule "-D" "${ban_chain} -i ${dev} -m conntrack --ctstate NEW -m set --match-set ${src_name} src -j ${target_src}"
f_iptrule "-D" "${ban_chain} -o ${dev} -m conntrack --ctstate NEW -m set --match-set ${src_name} dst -j ${target_dst}"
done
if [ -z "${rm}" ] && [ "${cnt}" -gt 0 ]
then
if [ "${src_ruletype}" != "dst" ]
then
if [ "${src_name##*_}" = "6" ]
then
# dummy, special IPv6 rules
/bin/true
else
f_iptrule "-I" "${wan_input} -p udp --dport 67:68 --sport 67:68 -j RETURN"
fi
f_iptrule "-A" "${wan_input} -j ${ban_chain}"
f_iptrule "-A" "${wan_forward} -j ${ban_chain}"
for dev in ${ban_dev}
do
f_iptrule "${action:-"-A"}" "${ban_chain} -i ${dev} -m conntrack --ctstate NEW -m set --match-set ${src_name} src -j ${target_src}"
done
fi
if [ "${src_ruletype}" != "src" ]
then
if [ "${src_name##*_}" = "6" ]
then
# dummy, special IPv6 rules
/bin/true
else
f_iptrule "-I" "${lan_input} -p udp --dport 67:68 --sport 67:68 -j RETURN"
fi
f_iptrule "-A" "${lan_input} -j ${ban_chain}"
f_iptrule "-A" "${lan_forward} -j ${ban_chain}"
for dev in ${ban_dev}
do
f_iptrule "${action:-"-A"}" "${ban_chain} -o ${dev} -m conntrack --ctstate NEW -m set --match-set ${src_name} dst -j ${target_dst}"
done
fi
else
if [ -x "${ban_ipset}" ] && [ -n "$("${ban_ipset}" -q -n list "${src_name}")" ]
then
"${ban_ipset}" -q destroy "${src_name}"
fi
fi
}
# ipset/iptables actions
#
f_ipset()
{
local out_rc source action ruleset ruleset_6 rule cnt=0 cnt_ip=0 cnt_cidr=0 timeout="-w 5" mode="${1}" in_rc="${src_rc:-0}"
if [ "${src_name%_6*}" = "whitelist" ]
then
target_src="RETURN"
target_dst="RETURN"
action="-I"
fi
case "${mode}" in
"backup")
gzip -cf "${tmp_load}" 2>/dev/null > "${ban_backupdir}/banIP.${src_name}.gz"
out_rc="${?:-"${in_rc}"}"
f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, out_rc: ${out_rc}"
return "${out_rc}"
;;
"restore")
if [ -f "${ban_backupdir}/banIP.${src_name}.gz" ]
then
zcat "${ban_backupdir}/banIP.${src_name}.gz" 2>/dev/null > "${tmp_load}"
out_rc="${?}"
fi
out_rc="${out_rc:-"${in_rc}"}"
f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, out_rc: ${out_rc}"
return "${out_rc}"
;;
"remove")
if [ -f "${ban_backupdir}/banIP.${src_name}.gz" ]
then
rm -f "${ban_backupdir}/banIP.${src_name}.gz"
out_rc="${?}"
fi
out_rc="${out_rc:-"${in_rc}"}"
f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, out_rc: ${out_rc}"
return "${out_rc}"
;;
"initial")
if [ -x "${ban_ipt}" ] && [ -z "$("${ban_ipt}" "${timeout}" -nL "${ban_chain}" 2>/dev/null)" ]
then
"${ban_ipt}" "${timeout}" -N "${ban_chain}" 2>/dev/null
out_rc="${?}"
elif [ -x "${ban_ipt}" ]
then
src_name="ruleset"
ruleset="${ban_wan_input_chain:-"input_wan_rule"} ${ban_wan_forward_chain:-"forwarding_wan_rule"} ${ban_lan_input_chain:-"input_lan_rule"} ${ban_lan_forward_chain:-"forwarding_lan_rule"}"
for rule in ${ruleset}
do
f_iptrule "-D" "${rule} -j ${ban_chain}"
done
fi
if [ -x "${ban_ipt6}" ] && [ -z "$("${ban_ipt6}" "${timeout}" -nL "${ban_chain}" 2>/dev/null)" ]
then
"${ban_ipt6}" "${timeout}" -N "${ban_chain}" 2>/dev/null
out_rc="${?}"
elif [ -x "${ban_ipt6}" ]
then
src_name="ruleset_6"
ruleset_6="${ban_wan_input_chain_6:-"input_wan_rule"} ${ban_wan_forward_chain_6:-"forwarding_wan_rule"} ${ban_lan_input_chain_6:-"input_lan_rule"} ${ban_lan_forward_chain_6:-"forwarding_lan_rule"}"
for rule in ${ruleset_6}
do
f_iptrule "-D" "${rule} -j ${ban_chain}"
done
fi
out_rc="${out_rc:-"${in_rc}"}"
f_log "debug" "f_ipset ::: name: -, mode: ${mode:-"-"}, chain: ${ban_chain:-"-"}, ruleset: ${ruleset:-"-"}, ruleset_6: ${ruleset_6:-"-"}, out_rc: ${out_rc}"
return "${out_rc}"
;;
"create")
if [ -x "${ban_ipset}" ]
then
if [ -s "${tmp_file}" ] && [ -z "$("${ban_ipset}" -q -n list "${src_name}")" ]
then
"${ban_ipset}" -q create "${src_name}" hash:"${src_settype}" hashsize 64 maxelem 262144 family "${src_setipv}" counters
out_rc="${?}"
else
"${ban_ipset}" -q flush "${src_name}"
out_rc="${?}"
fi
if [ -s "${tmp_file}" ] && [ "${out_rc}" -eq 0 ]
then
"${ban_ipset}" -q -! restore < "${tmp_file}"
out_rc="${?}"
if [ "${out_rc}" -eq 0 ]
then
"${ban_ipset}" -q save "${src_name}" > "${tmp_file}"
cnt="$(($(wc -l 2>/dev/null < "${tmp_file}")-1))"
cnt_cidr="$(grep -cF "/" "${tmp_file}")"
cnt_ip="$((cnt-cnt_cidr))"
printf "%s\\n" "${cnt}" > "${tmp_cnt}"
fi
fi
f_iptadd
fi
end_ts="$(date +%s)"
out_rc="${out_rc:-"${in_rc}"}"
f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, settype: ${src_settype:-"-"}, setipv: ${src_setipv:-"-"}, ruletype: ${src_ruletype:-"-"}, count(sum/ip/cidr): ${cnt}/${cnt_ip}/${cnt_cidr}, time: $((end_ts-start_ts)), out_rc: ${out_rc}"
return "${out_rc}"
;;
"refresh")
if [ -x "${ban_ipset}" ] && [ -n "$("${ban_ipset}" -q -n list "${src_name}")" ]
then
"${ban_ipset}" -q save "${src_name}" > "${tmp_file}"
out_rc="${?}"
if [ -s "${tmp_file}" ] && [ "${out_rc}" -eq 0 ]
then
cnt="$(($(wc -l 2>/dev/null < "${tmp_file}")-1))"
cnt_cidr="$(grep -cF "/" "${tmp_file}")"
cnt_ip="$((cnt-cnt_cidr))"
printf "%s\\n" "${cnt}" > "${tmp_cnt}"
fi
f_iptadd
fi
end_ts="$(date +%s)"
out_rc="${out_rc:-"${in_rc}"}"
f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}, count: ${cnt}/${cnt_ip}/${cnt_cidr}, time: $((end_ts-start_ts)), out_rc: ${out_rc}"
return "${out_rc}"
;;
"flush")
f_iptadd "remove"
if [ -x "${ban_ipset}" ] && [ -n "$("${ban_ipset}" -q -n list "${src_name}")" ]
then
"${ban_ipset}" -q flush "${src_name}"
"${ban_ipset}" -q destroy "${src_name}"
fi
f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}"
;;
"destroy")
if [ -x "${ban_ipt}" ] && [ -x "${ban_ipt_save}" ] && [ -x "${ban_ipt_restore}" ] && \
[ -n "$("${ban_ipt}" "${timeout}" -nL "${ban_chain}" 2>/dev/null)" ]
then
"${ban_ipt_save}" | grep -v -- "-j ${ban_chain}" | "${ban_ipt_restore}"
"${ban_ipt}" "${timeout}" -F "${ban_chain}" 2>/dev/null
"${ban_ipt}" "${timeout}" -X "${ban_chain}" 2>/dev/null
fi
if [ -x "${ban_ipt6}" ] && [ -x "${ban_ipt6_save}" ] && [ -x "${ban_ipt6_restore}" ] && \
[ -n "$("${ban_ipt6}" "${timeout}" -nL "${ban_chain}" 2>/dev/null)" ]
then
"${ban_ipt6_save}" | grep -v -- "-j ${ban_chain}" | "${ban_ipt6_restore}"
"${ban_ipt6}" "${timeout}" -F "${ban_chain}" 2>/dev/null
"${ban_ipt6}" "${timeout}" -X "${ban_chain}" 2>/dev/null
fi
for source in ${ban_sources}
do
if [ -x "${ban_ipset}" ] && [ -n "$("${ban_ipset}" -q -n list "${source}")" ]
then
"${ban_ipset}" -q destroy "${source}"
fi
done
f_log "debug" "f_ipset ::: name: ${src_name:-"-"}, mode: ${mode:-"-"}"
;;
esac
}
# write to syslog
#
f_log()
{
local class="${1}" log_msg="${2}"
if [ -n "${log_msg}" ] && { [ "${class}" != "debug" ] || [ "${ban_debug}" -eq 1 ]; }
then
if [ -x "${ban_logger}" ]
then
"${ban_logger}" -p "${class}" -t "banIP-${ban_ver}[${$}]" "${log_msg}"
else
printf "%s %s %s\\n" "${class}" "banIP-${ban_ver}[${$}]" "${log_msg}"
fi
if [ "${class}" = "err" ]
then
f_jsnup error
f_ipset destroy
f_rmbackup
f_rmtemp
exit 1
fi
fi
}
# start log service to trace failed ssh/luci logins
#
f_bgserv()
{
local bg_pid status="${1}"
bg_pid="$(pgrep -f "^/bin/sh ${ban_logservice}.*|^logread -f -e ${ban_sshdaemon}\|luci: failed login|^grep -qE Exit before auth|luci: failed login|[0-9]+ \[preauth\]$" | awk '{ORS=" "; print $1}')"
if [ -z "${bg_pid}" ] && [ "${status}" = "start" ] \
&& [ -x "${ban_logservice}" ] && [ "${ban_realtime}" = "true" ]
then
( "${ban_logservice}" "${ban_ver}" "${ban_sshdaemon}" & )
elif [ -n "${bg_pid}" ] && [ "${status}" = "stop" ]
then
kill -HUP "${bg_pid}" 2>/dev/null
fi
f_log "debug" "f_bgserv ::: status: ${status:-"-"}, bg_pid: ${bg_pid:-"-"}, ban_realtime: ${ban_realtime:-"-"}, log_service: ${ban_logservice:-"-"}"
}
# main function for banIP processing
#
f_main()
{
local pid pid_list start_ts end_ts ip tmp_raw tmp_cnt tmp_load tmp_file mem_total mem_free cnt=1
local src_name src_on src_url src_rset src_setipv src_settype src_ruletype src_cat src_log src_addon src_ts src_rc
local wan_input wan_forward lan_input lan_forward target_src target_dst ssh_log luci_log
ssh_log="$(logread -e "${ban_sshdaemon}" | grep -o "${ban_sshdaemon}.*" | sed 's/:[0-9]*$//g')"
luci_log="$(logread -e "luci: failed login" | grep -o "luci:.*")"
mem_total="$(awk '/^MemTotal/ {print int($2/1000)}' "/proc/meminfo" 2>/dev/null)"
mem_free="$(awk '/^MemFree/ {print int($2/1000)}' "/proc/meminfo" 2>/dev/null)"
f_log "debug" "f_main ::: fetch_util: ${ban_fetchutil:-"-"}, fetch_parm: ${ban_fetchparm:-"-"}, ssh_daemon: ${ban_sshdaemon}, interface(s): ${ban_iface:-"-"}, device(s): ${ban_dev:-"-"}, all_devices: ${ban_dev_all:-"-"}, backup_dir: ${ban_backupdir:-"-"}, mem_total: ${mem_total:-0}, mem_free: ${mem_free:-0}, max_queue: ${ban_maxqueue}"
# chain creation
#
f_ipset initial
if [ "${?}" -ne 0 ]
then
f_log "err" "banIP processing failed, fatal error during iptables chain creation (${ban_sysver})"
fi
# main loop
#
for src_name in ${ban_sources}
do
unset src_on
if [ "${src_name##*_}" = "6" ]
then
if [ -x "${ban_ipt6}" ]
then
src_on="$(eval printf "%s" \"\$\{ban_src_on_6_${src_name%_6*}\}\")"
src_url="$(eval printf "%s" \"\$\{ban_src_6_${src_name%_6*}\}\")"
src_rset="$(eval printf "%s" \"\$\{ban_src_rset_6_${src_name%_6*}\}\")"
src_setipv="inet6"
wan_input="${ban_wan_input_chain_6:-"input_wan_rule"}"
wan_forward="${ban_wan_forward_chain_6:-"forwarding_wan_rule"}"
lan_input="${ban_lan_input_chain_6:-"input_lan_rule"}"
lan_forward="${ban_lan_forward_chain_6:-"forwarding_lan_rule"}"
target_src="${ban_target_src_6:-"DROP"}"
target_dst="${ban_target_dst_6:-"REJECT"}"
fi
else
if [ -x "${ban_ipt}" ]
then
src_on="$(eval printf "%s" \"\$\{ban_src_on_${src_name}\}\")"
src_url="$(eval printf "%s" \"\$\{ban_src_${src_name}\}\")"
src_rset="$(eval printf "%s" \"\$\{ban_src_rset_${src_name}\}\")"
src_setipv="inet"
wan_input="${ban_wan_input_chain:-"input_wan_rule"}"
wan_forward="${ban_wan_forward_chain:-"forwarding_wan_rule"}"
lan_input="${ban_lan_input_chain:-"input_lan_rule"}"
lan_forward="${ban_lan_forward_chain:-"forwarding_lan_rule"}"
target_src="${ban_target_src:-"DROP"}"
target_dst="${ban_target_dst:-"REJECT"}"
fi
fi
src_settype="$(eval printf "%s" \"\$\{ban_src_settype_${src_name%_6*}\}\")"
src_ruletype="$(eval printf "%s" \"\$\{ban_src_ruletype_${src_name%_6*}\}\")"
src_cat="$(eval printf "%s" \"\$\{ban_src_cat_${src_name%_6*}\}\")"
src_addon=""
src_rc=4
tmp_load="${ban_tmpfile}.${src_name}.load"
tmp_file="${ban_tmpfile}.${src_name}.file"
tmp_raw="${tmp_file}.raw"
tmp_cnt="${tmp_file}.cnt"
tmp_err="${tmp_file}.err"
# basic pre-checks
#
f_log "debug" "f_main ::: name: ${src_name}, src_on: ${src_on:-"-"}"
if [ -z "${src_on}" ] || [ "${src_on}" != "1" ] || [ -z "${src_url}" ] || \
[ -z "${src_rset}" ] || [ -z "${src_settype}" ] || [ -z "${src_ruletype}" ]
then
f_ipset flush
f_ipset remove
continue
elif [ "${ban_action}" = "refresh" ] && [ ! -f "${src_url}" ]
then
start_ts="$(date +%s)"
f_ipset refresh
if [ "${?}" -eq 0 ]
then
continue
fi
fi
# download queue processing
#
(
start_ts="$(date +%s)"
if [ "${ban_action}" = "start" ] && [ ! -f "${src_url}" ]
then
f_ipset restore
fi
src_rc="${?}"
if [ "${src_rc}" -ne 0 ] || [ ! -s "${tmp_load}" ]
then
if [ -f "${src_url}" ]
then
src_log="$(cat "${src_url}" 2>/dev/null > "${tmp_load}")"
src_rc="${?}"
case "${src_name}" in
"whitelist")
src_addon="${ban_subnets}"
;;
"whitelist_6")
src_addon="${ban_subnets6}"
;;
"blacklist")
if [ "${ban_sshdaemon}" = "dropbear" ]
then
pid_list="$(printf "%s\\n" "${ssh_log}" | grep -F "Exit before auth" | awk 'match($0,/(\[[0-9]+\])/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
for pid in ${pid_list}
do
src_addon="${src_addon} $(printf "%s\\n" "${ssh_log}" | grep -F "${pid}" | awk 'match($0,/([0-9]{1,3}\.){3}[0-9]{1,3}$/){ORS=" ";print substr($0,RSTART,RLENGTH);exit}')"
done
elif [ "${ban_sshdaemon}" = "sshd" ]
then
src_addon="$(printf "%s\\n" "${ssh_log}" | grep -E "[0-9]+ \[preauth\]$" | awk 'match($0,/([0-9]{1,3}\.){3}[0-9]{1,3}$/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
fi
src_addon="${src_addon} $(printf "%s\\n" "${luci_log}" | awk 'match($0,/([0-9]{1,3}\.){3}[0-9]{1,3}$/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
;;
"blacklist_6")
if [ "${ban_sshdaemon}" = "dropbear" ]
then
pid_list="$(printf "%s\\n" "${ssh_log}" | grep -F "Exit before auth" | awk 'match($0,/(\[[0-9]+\])/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
for pid in ${pid_list}
do
src_addon="${src_addon} $(printf "%s\\n" "${ssh_log}" | grep -F "${pid}" | awk 'match($0,/(([0-9A-f]{0,4}::?){1,7}[0-9A-f]{0,4}$)/){ORS=" ";print substr($0,RSTART,RLENGTH);exit}')"
done
elif [ "${ban_sshdaemon}" = "sshd" ]
then
src_addon="$(printf "%s\\n" "${ssh_log}" | grep -E "[0-9]+ \[preauth\]$" | awk 'match($0,/(([0-9A-f]{0,4}::?){1,7}[0-9A-f]{0,4}$)/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
fi
src_addon="${src_addon} $(printf "%s\\n" "${luci_log}" | awk 'match($0,/(([0-9A-f]{0,4}::?){1,7}[0-9A-f]{0,4}$)/){ORS=" ";print substr($0,RSTART,RLENGTH)}')"
;;
esac
for ip in ${src_addon}
do
if [ -z "$(grep -F "${ip}" "${src_url}")" ]
then
printf "%s\\n" "${ip}" >> "${tmp_load}"
if { [ "${src_name//_*/}" = "blacklist" ] && [ "${ban_autoblacklist}" -eq 1 ]; } || \
{ [ "${src_name//_*/}" = "whitelist" ] && [ "${ban_autowhitelist}" -eq 1 ]; }
then
src_ts="# auto-added $(date "+%d.%m.%Y %H:%M:%S")"
printf "%s %s\\n" "${ip}" "${src_ts}" >> "${src_url}"
fi
fi
done
elif [ -n "${src_cat}" ]
then
if [ "${src_cat//[0-9]/}" != "${src_cat}" ]
then
for as in ${src_cat}
do
src_log="$("${ban_fetchutil}" ${ban_fetchparm} "${tmp_raw}" "${src_url}AS${as}" 2>&1)"
src_rc="${?}"
if [ "${src_rc}" -eq 0 ]
then
jsonfilter -i "${tmp_raw}" -e '@.data.prefixes.*.prefix' 2>/dev/null >> "${tmp_load}"
else
break
fi
done
if [ "${src_rc}" -eq 0 ]
then
f_ipset backup
elif [ "${ban_action}" != "start" ]
then
f_ipset restore
fi
else
for co in ${src_cat}
do
src_log="$("${ban_fetchutil}" ${ban_fetchparm} "${tmp_raw}" "${src_url}${co}&v4_format=prefix" 2>&1)"
src_rc="${?}"
if [ "${src_rc}" -eq 0 ]
then
if [ "${src_name##*_}" = "6" ]
then
jsonfilter -i "${tmp_raw}" -e '@.data.resources.ipv6.*' 2>/dev/null >> "${tmp_load}"
else
jsonfilter -i "${tmp_raw}" -e '@.data.resources.ipv4.*' 2>/dev/null >> "${tmp_load}"
fi
else
break
fi
done
if [ "${src_rc}" -eq 0 ]
then
f_ipset backup
elif [ "${ban_action}" != "start" ]
then
f_ipset restore
fi
fi
else
src_log="$("${ban_fetchutil}" ${ban_fetchparm} "${tmp_raw}" "${src_url}" 2>&1)"
src_rc="${?}"
if [ "${src_rc}" -eq 0 ]
then
zcat "${tmp_raw}" 2>/dev/null > "${tmp_load}"
src_rc="${?}"
if [ "${src_rc}" -ne 0 ]
then
mv -f "${tmp_raw}" "${tmp_load}"
src_rc="${?}"
fi
if [ "${src_rc}" -eq 0 ]
then
f_ipset backup
src_rc="${?}"
fi
elif [ "${ban_action}" != "start" ]
then
f_ipset restore
src_rc="${?}"
fi
fi
fi
if [ "${src_rc}" -eq 0 ]
then
awk "${src_rset}" "${tmp_load}" 2>/dev/null > "${tmp_file}"
src_rc="${?}"
if [ "${src_rc}" -eq 0 ]
then
f_ipset create
src_rc="${?}"
elif [ "${ban_action}" != "refresh" ]
then
f_ipset refresh
src_rc="${?}"
fi
else
src_log="$(printf "%s" "${src_log}" | awk '{ORS=" ";print $0}')"
if [ "${ban_action}" != "refresh" ]
then
f_ipset refresh
src_rc="${?}"
fi
f_log "debug" "f_main ::: name: ${src_name}, url: ${src_url}, rc: ${src_rc}, log: ${src_log:-"-"}"
fi
)&
hold="$((cnt%ban_maxqueue))"
if [ "${hold}" -eq 0 ]
then
wait
fi
cnt="$((cnt+1))"
done
wait
if [ -z "$(ls "${ban_tmpfile}".*.err 2>/dev/null)" ]
then
for cnt in $(cat "${ban_tmpfile}".*.cnt 2>/dev/null)
do
ban_cnt="$((ban_cnt+cnt))"
done
if [ "${ban_cnt}" -gt 0 ]
then
ban_setcnt="$(ls "${ban_tmpfile}".*.cnt 2>/dev/null | wc -l)"
fi
f_log "info" "${ban_setcnt} IPSets with overall ${ban_cnt} IPs/Prefixes loaded successfully (${ban_sysver})"
f_bgserv "start"
f_jsnup
f_rmtemp
else
f_log "err" "banIP processing failed, fatal iptables error(s) during subshell processing (${ban_sysver})"
fi
}
# update runtime information
#
f_jsnup()
{
local rundate status="${1:-"enabled"}"
rundate="$(date "+%d.%m.%Y %H:%M:%S")"
ban_cntinfo="${ban_setcnt} IPSets with overall ${ban_cnt} IPs/Prefixes"
> "${ban_rtfile}"
json_load_file "${ban_rtfile}" >/dev/null 2>&1
json_init
json_add_object "data"
json_add_string "status" "${status}"
json_add_string "version" "${ban_ver}"
json_add_string "util_info" "${ban_fetchutil:-"-"}, ${ban_realtime:-"-"}"
json_add_string "ipset_info" "${ban_cntinfo:-"-"}"
json_add_string "backup_dir" "${ban_backupdir}"
json_add_string "last_run" "${rundate:-"-"}"
json_add_string "system" "${ban_sysver}"
json_close_object
json_dump > "${ban_rtfile}"
f_log "debug" "f_jsnup ::: status: ${status}, setcnt: ${ban_setcnt}, cnt: ${ban_cnt}"
}
# source required system libraries
#
if [ -r "/lib/functions.sh" ] && [ -r "/lib/functions/network.sh" ] && [ -r "/usr/share/libubox/jshn.sh" ]
then
. "/lib/functions.sh"
. "/lib/functions/network.sh"
. "/usr/share/libubox/jshn.sh"
else
f_log "err" "system libraries not found"
fi
# handle different banIP actions
#
f_envload
case "${ban_action}" in
"stop")
f_bgserv "stop"
f_jsnup stopped
f_ipset destroy
f_rmbackup
f_rmtemp
;;
"start"|"restart"|"reload"|"refresh")
f_bgserv "stop"
f_envcheck
f_main
;;
esac

View File

69
net/dcwapd/Makefile Normal file
View File

@ -0,0 +1,69 @@
#
# Copyright (C) 2019 EWSI
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=dcwapd
PKG_VERSION:=1.1.0
PKG_RELEASE:=2
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/ewsi/$(PKG_NAME)/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=58e52bf4e7526b2f26319740549dbcc6f6ab505f587815ee8731e40f7fecb625
PKG_MAINTAINER:=Carey Sonsino <careys@edgewaterwireless.com>
PKG_LICENSE:=Apache-2.0
PKG_LICENSE_FILES:=COPYING
PKG_FIXUP:=autoreconf
PKG_INSTALL:=1
PKG_BUILD_PARALLEL:=1
include $(INCLUDE_DIR)/uclibc++.mk
include $(INCLUDE_DIR)/package.mk
define Package/dcwapd
SECTION:=net
CATEGORY:=Network
SUBMENU:=Routing and Redirection
TITLE:=Dual-Channel WiFi AP daemon
URL:=https://www.edgewaterwireless.com
DEPENDS:=$(CXX_DEPENDS) +kmod-macremapper +libdcwsocket +libdcwproto +mrmctl +libuci
endef
define Package/dcwapd/description
Implementation of the Dual-Channel WiFi AP daemon
endef
CONFIGURE_ARGS += \
--enable-platform=linuxjsonstatic \
--enable-shared
TARGET_CXXFLAGS += -std=c++11 -DRAPIDJSON_HAS_CXX11_RVALUE_REFS=0 -ffunction-sections -fdata-sections -flto
TARGET_LDFLAGS += -ldcwproto -ldcwsocket -lmrmfilterparser -luci -Wl,--gc-sections,--as-needed
define Build/InstallDev
$(INSTALL_DIR) $(1)/usr/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/* $(1)/usr/lib/
endef
define Package/dcwapd/install
$(INSTALL_DIR) $(1)/bin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/$(PKG_NAME) $(1)/bin/
$(INSTALL_DIR) $(1)/usr/lib
# Note: $(INSTALL_BIN) does not keep symlinks, so use $(CP)
$(CP) $(PKG_INSTALL_DIR)/usr/lib/*.so* $(1)/usr/lib/
# UCI config file
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_DATA) ./files/dcwapd.uci $(1)/etc/config/dcwapd
# Init script
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./files/dcwapd.init $(1)/etc/init.d/dcwapd
endef
$(eval $(call BuildPackage,dcwapd))

View File

@ -0,0 +1,326 @@
#!/bin/sh /etc/rc.common
USE_PROCD=1
START=99
STOP=01
CONFIGURATION=dcwapd
VERBOSE=1
# NOTE: all functions write the result to the $result variable
result=
get_channelsets()
{
# default to empty
result=
channelsets=$(uci show $CONFIGURATION | grep "=channel-set$")
for channelset in $channelsets; do
channelset=$(echo "$channelset" | sed -rn "s/$CONFIGURATION\.(.*)=.*/\1/p")
result="$result $channelset"
done
if [ $VERBOSE -eq 1 ]; then
echo "Channel Sets: $result" 2>&1 | logger
fi
}
# $1 : the channel set name
get_channelset_enabled()
{
# default to disabled
result=0
if [ -n "$1" ]; then
result=$(uci get $CONFIGURATION."$1".enabled)
fi
if [ $VERBOSE -eq 1 ]; then
echo "Channel Set \"$1\" Enabled: $result" 2>&1 | logger
fi
}
# $1 : the channel set name
get_primary_bridge()
{
result=
if [ -n "$1" ]; then
result=$(uci get $CONFIGURATION."$1".bridge)
fi
if [ $VERBOSE -eq 1 ]; then
echo "Channel Set \"$1\" Primary Bridge: $result" 2>&1 | logger
fi
}
# $1 : the channel set name
get_datachannels()
{
# default to empty
result=
if [ -n "$1" ]; then
result=$(uci get $CONFIGURATION."$1".data_channels)
fi
if [ $VERBOSE -eq 1 ]; then
echo "Channel Set \"$1\" Data Channels: $result" 2>&1 | logger
fi
}
# $1 : the wlan interface name
get_wifi_iface_num()
{
result=
if [ -n "$1" ];then
#result=$(echo "$1" | sed -n "s/wlan//p")
result=$(echo "$1" | sed -rn "s/wlan([0-9]*).*/\1/p")
fi
}
# $1 : the bridge name
get_bridge_network_name()
{
result=
if [ -n "$1" ];then
result=$(echo "$1" | sed -n "s/br-//p")
fi
}
# $1 : the wlan interface name
set_iface_init_state()
{
result=
if [ -n "$1" ]; then
iface=$1
# need to extract the "X" from wlanX
get_wifi_iface_num "$iface"
iface_num=$result
if [ -n "$iface_num" ]; then
# get the iface network
init_net=$(uci get wireless.@wifi-iface[$iface_num].network)
if [ -n "$init_net" ]; then
# if the iface network is a bridge, but doesn't start with "br-"
# I think we need to prepend it?
net_type=$(uci get network."$init_net".type)
if [ -n "$net_type" ] && [ "$net_type" = "bridge" ]; then
prefix_ok=$(echo "$init_net" | grep "^br-")
if [ -z "$prefix_ok" ]; then
init_net="br-$init_net"
fi
fi
fi
# make sure that the init_net section exists
init_net_section=$(uci get dcwapd.init_net)
if [ "$init_net_section" != "init_net" ]; then
# the section did not exist
uci set dcwapd.init_net=init_net
fi
# save the initial network
if [ $VERBOSE -eq 1 ]; then
echo "Saving '$iface' initial network '$init_net'" 2>&1 | logger
fi
uci set $CONFIGURATION.init_net."$iface"="$init_net"
uci commit
# save the initial network in the result variable
result=$init_net
fi
fi
}
# $1 : the wlan interface name
get_iface_init_state()
{
result=
if [ -n "$1" ];then
init_net=$(uci get $CONFIGURATION.init_net."$iface")
# if the response starts with "uci: ", it was an error not the real result
err=$(echo "$init_net" | grep "^uci: ")
if [ -z "$err" ]; then
# no error, set the result
result=$init_net
if [ $VERBOSE -eq 1 ]; then
echo "Got '$iface' initial network '$init_net'" 2>&1 | logger
fi
fi
fi
}
# $1 : the name of the data channel name to bring up
datachannel_up()
{
if [ -n "$1" ]; then
bridge=$(uci get $CONFIGURATION."$1".bridge)
interfaces=$(uci get $CONFIGURATION."$1".interfaces)
if [ $VERBOSE -eq 1 ]; then
echo "Creating Data Channel Bridge: $bridge" 2>&1 | logger
fi
get_bridge_network_name "$bridge"
netname=$result
if [ -n "$netname" ]; then
uci set network."$netname"=interface
uci set network."$netname".type=bridge
uci set network."$netname".proto=static
uci set network."$netname".bridge_empty='1'
fi
# create the bridge
uci commit
/etc/init.d/network reload
for iface in $interfaces; do
# if iface is in a bridge, the bridge name should be stored in result
set_iface_init_state "$iface"
init_bridge=$result
# update uci with the new bridge info
get_wifi_iface_num "$iface"
iface_num=$result
if [ -n "$iface_num" ]; then
uci set wireless.@wifi-iface[$iface_num].network="$netname"
fi
# manually put the interface into the data bridge
# if iface is in a bridge, remove it before adding it to the data bridge
if [ -n "$init_bridge" ]; then
brctl delif "$init_bridge" "$iface" 2>&1 | logger
fi
brctl addif "$bridge" "$iface" 2>&1 | logger
done
# commit uci changes and reload the network
uci commit
/etc/init.d/network reload
#/etc/init.d/network restart
# while [ 1 ]; do
# ifconfig "$bridge" > /dev/null 2>&1
# if [ $? == 0 ]; then
# break;
# fi
# sleep 1
# done
fi
}
# $1 : the name of the data channel to bring down
datachannel_down()
{
if [ -n "$1" ]; then
bridge=$(uci get $CONFIGURATION."$1".bridge)
interfaces=$(uci get $CONFIGURATION."$1".interfaces)
for iface in $interfaces; do
if [ $VERBOSE -eq 1 ]; then
echo "Deconfiguring Data Channel Interface: $iface" 2>&1 | logger
fi
# manually remove the interface from the data bridge
brctl delif "$bridge" "$iface" 2>&1 | logger
get_iface_init_state "$iface"
init_bridge=$result
if [ -n "$init_bridge" ]; then
# manually move the interface back to the original bridge
brctl addif "$init_bridge" "$iface" 2>&1 | logger
# update uci with the new bridge and interface configuration
get_wifi_iface_num "$iface"
iface_num=$result
get_bridge_network_name "$init_bridge"
netname=$result
if [ -n "$iface_num" ] && [ -n "$netname" ]; then
uci set wireless.@wifi-iface[$iface_num].network="$netname"
fi
fi
done
if [ $VERBOSE -eq 1 ]; then
echo "Deconfiguring Data Channel Bridge: $bridge" 2>&1 | logger
fi
# delete the bridge from uci
get_bridge_network_name "$bridge"
netname=$result
if [ -n "$netname" ]; then
uci delete network."$netname"
fi
# commit uci changes and reload the network
uci commit
/etc/init.d/network reload
#`/etc/init.d/network restart`
fi
}
start_service() {
config_load "$CONFIGURATION"
local enabled
config_get enabled general enabled
if [ "$enabled" != "1" ]; then
echo "dcwapd is disabled in UCI"
return 1
fi
get_channelsets
# get the list of channel sets
channelsets=$result
for channelset in $channelsets; do
if [ -n "$channelset" ]; then
get_channelset_enabled "$channelset"
enabled=$result
if [ "$enabled" = "1" ]; then
# the channel set is enabled
# get the list of data channels used by the channel set
get_datachannels "$channelset"
datachannels=$result
for datachannel in $datachannels; do
datachannel_up "$datachannel"
done
fi
fi
done
procd_open_instance
procd_set_param file /etc/config/dcwapd
procd_set_param command dcwapd
procd_set_param stdout 1
procd_set_param stderr 1
procd_close_instance
}
stop_service() {
get_channelsets
# get the list of channel sets
channelsets=$result
for channelset in $channelsets; do
if [ -n "$channelset" ]; then
# we don't care if it is enabled, tear it down
# get_channelset_enabled $channelset
# enabled=$result
# if [ $enabled = "1" ]; then
# # the channel set is enabled
# get the list of data channels used by the channel set
get_datachannels "$channelset"
datachannels=$result
for datachannel in $datachannels; do
datachannel_down "$datachannel"
done
# fi
fi
done
sleep 1
}
service_triggers()
{
procd_add_reload_trigger dcwapd
}
reload_service() {
stop
start
}

View File

@ -0,0 +1,32 @@
config general 'general'
option enabled 0
option tmpdir '/tmp/dcwapd'
config channel-set 'channelset0'
option enabled 0
option ssid 'OpenWrt'
option bridge 'br-lan'
option data_channels 'datachannel0'
config datachannel 'datachannel0'
option ssid 'OpenWrt-DCW'
option bridge 'br-dc0'
option interfaces ''
config filter-set 'TFP_Default'
option mac '*'
option filters 'filter0 filter1'
config filter 'filter0'
option packet_size '*'
option source_ip '*'
option source_port '80'
option protocol 'tcp'
option dest_port '*'
config filter 'filter1'
option packet_size '*'
option source_ip '*'
option source_port '443'
option protocol 'tcp'
option dest_port '*'

View File

@ -0,0 +1,475 @@
--- a/dev/null
+++ b/dcwlinux/uci_configuration_provider.h
@@ -0,0 +1,104 @@
+#ifndef UCI_CONFIGURATION_PROVIDER_H_INCLUDED
+#define UCI_CONFIGURATION_PROVIDER_H_INCLUDED
+
+#include "./ap_configuration.h"
+
+namespace dcwlinux {
+
+class UciConfigurationProvider : public APConfigurationProvider {
+
+ static const char *SECTION_TYPE_GENERAL;
+ static const char *SECTION_TYPE_CHANNEL_SET;
+ static const char *SECTION_TYPE_DATA_CHANNEL;
+ static const char *SECTION_TYPE_FILTER_SET;
+ static const char *SECTION_TYPE_FILTER;
+ static const char *DEFAULT_FILTER_SET_NAME;
+
+ static const char *OPTION_TMPDIR;
+ static const char *OPTION_ENABLED;
+ static const char *OPTION_SSID;
+ static const char *OPTION_BRIDGE;
+ static const char *OPTION_DATA_CHANNELS;
+ static const char *OPTION_INTERFACES;
+ static const char *OPTION_MAC_ADDRESS;
+ static const char *OPTION_FILTERS;
+ static const char *OPTION_PACKET_SIZE;
+ static const char *OPTION_SOURCE_IP;
+ static const char *OPTION_SOURCE_PORT;
+ static const char *OPTION_PROTOCOL;
+ static const char *OPTION_DEST_PORT;
+
+ static const char *FILTER_FILE_EXTENSION;
+
+ UciConfigurationProvider(const UciConfigurationProvider&); //no copy
+
+ typedef std::map<std::string, std::string> DataChannelBridgeMap;
+ struct PrimaryChannel {
+ std::string bridgeName;
+ DataChannelBridgeMap dataChannels;
+ };
+ typedef std::map<std::string, PrimaryChannel> PrimaryChannelMap;
+ typedef std::map<dcw::MacAddress, std::string> StationFilterMap;
+
+ struct uci_context *_uciContext;
+ struct uci_package *_uciPackage;
+ const char *_uciConfig;
+
+ std::string _filterDirectory;
+ PrimaryChannelMap _primaryChannels;
+ StationFilterMap _stationFilters;
+
+public:
+ UciConfigurationProvider(const char * const uciConfig); // the "config" part of UCI commands
+ virtual ~UciConfigurationProvider();
+
+ virtual void InstanciateCFileTrafficFilterProfiles(CFTFPList& output) const;
+ virtual void GetPrimarySsids(SsidSet& output) const;
+ virtual void GetDataSsids(SsidSet& output, const char * const primarySsid) const;
+ virtual const char *GetSsidIfname(const char * const ssid) const;
+ virtual void GetStationTrafficFilterProfiles(StationTFPMap& output) const;
+};
+
+}; //namespace dcwlinux {
+
+#endif //#ifndef UCI_CONFIGURATION_PROVIDER_H_INCLUDED
+#ifndef UCI_CONFIGURATION_PROVIDER_H_INCLUDED
+#define UCI_CONFIGURATION_PROVIDER_H_INCLUDED
+
+#include "./ap_configuration.h"
+
+namespace dcwlinux {
+
+class UciConfigurationProvider : public APConfigurationProvider {
+ UciConfigurationProvider(const UciConfigurationProvider&); //no copy
+
+ typedef std::map<std::string, std::string> DataChannelBridgeMap;
+ struct PrimaryChannel {
+ std::string bridgeName;
+ DataChannelBridgeMap dataChannels;
+ };
+ typedef std::map<std::string, PrimaryChannel> PrimaryChannelMap;
+ typedef std::map<dcw::MacAddress, std::string> StationFilterMap;
+
+ struct uci_context *_uciContext;
+ struct uci_package *_uciPackage;
+ const char *_uciConfig;
+
+ PrimaryChannelMap _primaryChannels;
+ StationFilterMap _stationFilters;
+ CFTFPList _defaultFilters;
+
+public:
+ UciConfigurationProvider(const char * const uciConfig); // the "config" part of UCI commands
+ virtual ~UciConfigurationProvider();
+
+ virtual void InstanciateCFileTrafficFilterProfiles(CFTFPList& output) const;
+ virtual void GetPrimarySsids(SsidSet& output) const;
+ virtual void GetDataSsids(SsidSet& output, const char * const primarySsid) const;
+ virtual const char *GetSsidIfname(const char * const ssid) const;
+ virtual void GetStationTrafficFilterProfiles(StationTFPMap& output) const;
+};
+
+}; //namespace dcwlinux {
+
+#endif //#ifndef UCI_CONFIGURATION_PROVIDER_H_INCLUDED
--- a/dev/null
+++ b/dcwlinux/uci_configuration_provider.cxx
@@ -0,0 +1,365 @@
+
+#include <uci.h>
+#include <string.h>
+
+#include <stdlib.h>
+#include <stdexcept>
+#include <sys/stat.h>
+#include <cerrno>
+#include <iostream>
+#include <fstream>
+
+#include "./uci_configuration_provider.h"
+
+#include "dcwposix/filterdirscanner.h"
+#include "dcw/macaddress.h"
+#include "dcw/dcwlog.h"
+
+using namespace dcwlinux;
+
+ const char *UciConfigurationProvider::SECTION_TYPE_GENERAL = "general";
+ const char *UciConfigurationProvider::SECTION_TYPE_CHANNEL_SET = "channel-set";
+ const char *UciConfigurationProvider::SECTION_TYPE_DATA_CHANNEL = "datachannel";
+ const char *UciConfigurationProvider::SECTION_TYPE_FILTER_SET = "filter-set";
+ const char *UciConfigurationProvider::SECTION_TYPE_FILTER = "filter";
+ const char *UciConfigurationProvider::DEFAULT_FILTER_SET_NAME = "TFP_Default";
+
+ const char *UciConfigurationProvider::OPTION_TMPDIR = "tmpdir";
+ const char *UciConfigurationProvider::OPTION_ENABLED = "enabled";
+ const char *UciConfigurationProvider::OPTION_SSID = "ssid";
+ const char *UciConfigurationProvider::OPTION_BRIDGE = "bridge";
+ const char *UciConfigurationProvider::OPTION_DATA_CHANNELS = "data_channels";
+ const char *UciConfigurationProvider::OPTION_INTERFACES = "interfaces";
+ const char *UciConfigurationProvider::OPTION_MAC_ADDRESS = "mac";
+ const char *UciConfigurationProvider::OPTION_FILTERS = "filters";
+ const char *UciConfigurationProvider::OPTION_PACKET_SIZE = "packet_size";
+ const char *UciConfigurationProvider::OPTION_SOURCE_IP = "source_ip";
+ const char *UciConfigurationProvider::OPTION_SOURCE_PORT = "source_port";
+ const char *UciConfigurationProvider::OPTION_PROTOCOL = "protocol";
+ const char *UciConfigurationProvider::OPTION_DEST_PORT = "dest_port";
+
+ const char *UciConfigurationProvider::FILTER_FILE_EXTENSION = ".tfp";
+
+ UciConfigurationProvider::UciConfigurationProvider(const char * const uciConfig) : _uciConfig(uciConfig) {
+
+ //printf("*** Start UciConfigurationProvider(%s)\n", _uciConfig);
+ //printf("*** About to uci_alloc_context()\n");
+
+ _uciContext = uci_alloc_context();
+
+ //printf("*** uci_alloc_context() complete\n");
+ //printf("*** About to uci_load()\n");
+
+ if (_uciContext == NULL)
+ {
+ std::string err = "Error creating UCI context ";
+ throw std::runtime_error(err);
+ }
+
+ uci_load(_uciContext, _uciConfig, &_uciPackage);
+
+ //printf("*** uci_load complete()\n");
+
+ if (_uciPackage == NULL)
+ {
+ std::string err = "Error loading UCI package " + std::string(_uciConfig);
+ throw std::runtime_error(err);
+ }
+
+ uci_section *generalSection = uci_lookup_section(_uciContext, _uciPackage, UciConfigurationProvider::SECTION_TYPE_GENERAL);
+ if (generalSection == NULL)
+ {
+ std::string err = "Error: A general section (" + std::string(UciConfigurationProvider::SECTION_TYPE_GENERAL) + ") must be specified!";
+ throw std::runtime_error(err);
+ }
+
+ uci_option *opt_tmpdir = uci_lookup_option(_uciContext, generalSection, UciConfigurationProvider::OPTION_TMPDIR);
+ if (opt_tmpdir == NULL)
+ {
+ std::string err = "Error: A temporary directory (" + std::string(UciConfigurationProvider::OPTION_TMPDIR) + ") must be specified!";
+ throw std::runtime_error(err);
+ }
+ char *tmpdir = opt_tmpdir->v.string;
+ //printf(" *** Set tmpdir: %s\n", tmpdir);
+
+ // make sure that tmpdir exists
+ int status = mkdir(tmpdir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+ if ((status != 0) && // failure
+ (errno != EEXIST)) // the failure was not that the directory already existed
+ {
+ std::string err = "Error: Unable to create the temporary directory (tmpdir), error # " + errno;
+ throw std::runtime_error(err);
+ }
+ _filterDirectory = std::string(tmpdir);
+
+ if (uci_lookup_section(_uciContext, _uciPackage, UciConfigurationProvider::DEFAULT_FILTER_SET_NAME) == NULL)
+ {
+ std::string err = "Error: A default traffic filter profile named " + std::string(UciConfigurationProvider::DEFAULT_FILTER_SET_NAME) + " MUST exist!";
+ throw std::runtime_error(err);
+ }
+
+ // iterate over all of the sections in the package
+ uci_element *elem;
+ uci_foreach_element(&_uciPackage->sections, elem)
+ {
+ //printf("--==-- element.type: %d\n", elem->type);
+ //printf("--==-- element.name: %s\n", elem->name);
+
+ if (elem->type == UCI_TYPE_SECTION)
+ {
+ // look up the section and get it's type
+
+ uci_section *section = NULL;
+ //printf("*** Looking up section: %s\n", elem->name);
+
+ section = uci_lookup_section(_uciContext, _uciPackage, elem->name);
+
+ if ((section != NULL) && (section->type != NULL))
+ {
+ //printf(" *** Section type: %s\n", section->type);
+ if (strcmp(elem->name, UciConfigurationProvider::SECTION_TYPE_GENERAL) == 0)
+ {
+ // we already processed the general section for the tmpdir
+ }
+ else if (strcmp(section->type, UciConfigurationProvider::SECTION_TYPE_CHANNEL_SET) == 0)
+ {
+ // the section is a channel set, populate it with the specified values
+
+ uci_option *enabled = uci_lookup_option(_uciContext, section, UciConfigurationProvider::OPTION_ENABLED);
+ if ((enabled == NULL) || (strcmp(enabled->v.string, "1") != 0))
+ {
+ // found a disabled channel set, ignore it
+ continue;
+ }
+
+ uci_option *ssid = uci_lookup_option(_uciContext, section, UciConfigurationProvider::OPTION_SSID);
+ uci_option *bridge = uci_lookup_option(_uciContext, section, UciConfigurationProvider::OPTION_BRIDGE);
+ uci_option *dataChannels = uci_lookup_option(_uciContext, section, UciConfigurationProvider::OPTION_DATA_CHANNELS);
+
+ if ((ssid != NULL) && (bridge != NULL) && (dataChannels != NULL))
+ {
+ PrimaryChannel &pc = _primaryChannels[ssid->v.string];
+ pc.bridgeName = bridge->v.string;
+
+ char dataChannels_list[255];
+ // The dataChannels option is not a list
+ //if (dataChannels->type == UCI_TYPE_LIST)
+ if (dataChannels->v.string != NULL)
+ {
+ strcpy(dataChannels_list, dataChannels->v.string);
+ std::string str_dataChannels = dataChannels->v.string;
+ size_t start_pos = 0;
+ size_t pos = 0;
+ while(start_pos != std::string::npos)
+ {
+ pos = str_dataChannels.find(" ", start_pos);
+ //printf("****** start_pos: %u, pos: %u\n", start_pos, pos);
+ std::string str_dataChannel = str_dataChannels.substr(start_pos,
+ pos == std::string::npos ? pos : pos-start_pos);
+ //printf("*** dataChannel: %s\n", str_dataChannel.c_str());
+
+ // update the start position for next loop
+ start_pos = (pos == std::string::npos ? pos : pos+1);
+
+ uci_section *dcSection = uci_lookup_section(_uciContext, _uciPackage, str_dataChannel.c_str());
+ if (dcSection != NULL)
+ {
+ uci_option *dcSsid = uci_lookup_option(_uciContext, dcSection, UciConfigurationProvider::OPTION_SSID);
+ uci_option *dcBridge = uci_lookup_option(_uciContext, dcSection, UciConfigurationProvider::OPTION_BRIDGE);
+
+ // TODO: configure dcBridge and dcInterfaces
+ //uci_option *dcInterfaces = uci_lookup_option(_uciContext, dcSection, UciConfigurationProvider::OPTION_INTERFACES);
+
+ if ((dcSsid != NULL) && (dcBridge != NULL))
+ {
+ pc.dataChannels[dcSsid->v.string];
+ pc.dataChannels[dcSsid->v.string] = dcBridge->v.string;
+ }
+ }
+ }
+ }
+
+ //printf("Section: %s, SSID: %s, Bridge: %s, Data Channels: %s\n", section->e.name, ssid->v.string, bridge->v.string, dataChannels_list);
+ }
+ }
+ else if (strcmp(section->type, UciConfigurationProvider::SECTION_TYPE_DATA_CHANNEL) == 0)
+ {
+ // data channels are processed by the channel set
+ }
+ else if (strcmp(section->type, UciConfigurationProvider::SECTION_TYPE_FILTER_SET) == 0)
+ {
+ // the section is a filter set, populate it with the specified values
+ //printf("*** filter set: %s\n", elem->name);
+
+ // create a tfp file for the sectionName
+ std::ofstream tfpFile;
+ std::string tfpFilePath =
+ tmpdir + std::string("/") +
+ std::string(elem->name) +
+ std::string(UciConfigurationProvider::FILTER_FILE_EXTENSION);
+ tfpFile.open(tfpFilePath.c_str(), std::ios::out | std::ios::trunc);
+ if (!tfpFile.is_open())
+ {
+ std::string err = "Error: Unable to open the filter file: " + tfpFilePath;
+ throw std::runtime_error(err);
+ }
+
+ const char *filterDelimiter = "\n";
+ char sFilterContents[2048];
+ sFilterContents[0] = '\0';
+
+ uci_option *filters = uci_lookup_option(_uciContext, section, UciConfigurationProvider::OPTION_FILTERS);
+ // The filters option is not a list
+ //if ((filters != NULL) && (filters->type == UCI_TYPE_LIST))
+ if (filters != NULL)
+ {
+ //printf("*** %s.filters is a list.\n", elem->name);
+ //struct uci_element *e;
+ //uci_foreach_element(&filters->v.list, e)
+
+ std::string str_filters = filters->v.string;
+ //printf("*** STR_FILTERS: %s\n", str_filters.c_str());
+ size_t start_pos = 0;
+ size_t pos = 0;
+ while(start_pos != std::string::npos)
+ {
+ pos = str_filters.find(" ", start_pos);
+ //printf("****** start_pos: %u, pos: %u\n", start_pos, pos);
+ std::string str_filter = str_filters.substr(start_pos,
+ pos == std::string::npos ? pos : pos-start_pos);
+ //printf("*** Looking for filter section named: %s ...\n", str_filter.c_str());
+
+ // update the start position for next loop
+ start_pos = (pos == std::string::npos ? pos : pos+1);
+
+ uci_section *fSection = uci_lookup_section(_uciContext, _uciPackage, str_filter.c_str());
+ if (fSection != NULL)
+ {
+ uci_option *fPacketSize = uci_lookup_option(_uciContext, fSection, UciConfigurationProvider::OPTION_PACKET_SIZE);
+ uci_option *fSourceIp = uci_lookup_option(_uciContext, fSection, UciConfigurationProvider::OPTION_SOURCE_IP);
+ uci_option *fSourcePort = uci_lookup_option(_uciContext, fSection, UciConfigurationProvider::OPTION_SOURCE_PORT);
+ uci_option *fProtocol = uci_lookup_option(_uciContext, fSection, UciConfigurationProvider::OPTION_PROTOCOL);
+ uci_option *fDestPort = uci_lookup_option(_uciContext, fSection, UciConfigurationProvider::OPTION_DEST_PORT);
+
+ if ((fPacketSize != NULL) &&
+ (fSourceIp != NULL) &&
+ (fSourcePort != NULL) &&
+ (fProtocol != NULL) &&
+ (fDestPort != NULL))
+ {
+ //printf("*** filter: %s %s:%s:%s:%s:%s\n", e->name,
+ // fPacketSize->v.string, fSourceIp->v.string, fSourcePort->v.string,
+ // fProtocol->v.string, fDestPort->v.string);
+
+ strcpy(sFilterContents, fPacketSize->v.string);
+ strcat(sFilterContents, ":");
+ strcat(sFilterContents, fSourceIp->v.string);
+ strcat(sFilterContents, ":");
+ strcat(sFilterContents, fSourcePort->v.string);
+ strcat(sFilterContents, ":");
+ strcat(sFilterContents, fProtocol->v.string);
+ strcat(sFilterContents, ":");
+ strcat(sFilterContents, fDestPort->v.string);
+ strcat(sFilterContents, filterDelimiter);
+
+ //printf("*** Writing filter contents to file: %s\n", sFilterContents);
+ tfpFile << sFilterContents;
+ }
+ else
+ {
+ std::string err = "Error parsing filter: " + str_filter;
+ throw std::runtime_error(err);
+ }
+ }
+ }
+ }
+ tfpFile.close();
+
+ // if there is a MAC address for the filter set, we need to add it to the station filters list
+ uci_option *mac = uci_lookup_option(_uciContext, section, UciConfigurationProvider::OPTION_MAC_ADDRESS);
+ if (mac != NULL)
+ {
+ // ignore wildcard MAC address
+ if (strcmp(mac->v.string,"*") != 0)
+ {
+ //printf(" *** MAC Address: %s\n", mac->v.string);
+ _stationFilters[::dcw::MacAddress(mac->v.string)] = elem->name;
+ }
+ }
+ }
+ else if (strcmp(section->type, UciConfigurationProvider::SECTION_TYPE_FILTER) == 0)
+ {
+ // filters are processed by the filter set
+ }
+ else
+ {
+ //std::string err = "Error: Unknown UCI section type: " + std::string(section->type);
+ //throw std::runtime_error(err);
+
+ // Don't throw an exception. It is fine for UCI to contain things that we do not know about
+ // that it may use for other purposes, like UI or internal state
+ dcwlogdbgf("Ignoring UCI section type: %s\n", section->type);
+ }
+ }
+ }
+ }
+ }
+
+ UciConfigurationProvider::~UciConfigurationProvider() {
+ uci_free_context(_uciContext);
+ }
+
+ void UciConfigurationProvider::InstanciateCFileTrafficFilterProfiles(CFTFPList& output) const {
+ ::dcwposix::FilterdirScanner::FileFilterProfileList ffpl;
+ ::dcwposix::FilterdirScanner dirScanner(_filterDirectory.c_str());
+ dirScanner.Scan(ffpl);
+
+ for (::dcwposix::FilterdirScanner::FileFilterProfileList::const_iterator i = ffpl.begin(); i != ffpl.end(); i++) {
+ output.push_back(new ::dcw::FileTrafficFilterProfile(*i));
+ }
+ }
+
+
+ void UciConfigurationProvider::GetPrimarySsids(SsidSet& output) const {
+ for (PrimaryChannelMap::const_iterator i = _primaryChannels.begin(); i != _primaryChannels.end(); i++) {
+ output.insert(i->first);
+ }
+ }
+
+ void UciConfigurationProvider::GetDataSsids(SsidSet& output, const char * const primarySsid) const {
+ const PrimaryChannelMap::const_iterator pssid = _primaryChannels.find(primarySsid);
+ if (pssid == _primaryChannels.end()) return;
+
+ for (DataChannelBridgeMap::const_iterator i = pssid->second.dataChannels.begin(); i != pssid->second.dataChannels.end(); i++) {
+ output.insert(i->first);
+ }
+ }
+
+ const char *UciConfigurationProvider::GetSsidIfname(const char * const ssid) const {
+ PrimaryChannelMap::const_iterator pssid = _primaryChannels.find(ssid);
+ if (pssid != _primaryChannels.end()) {
+ if (pssid->second.bridgeName.empty()) {
+ return NULL;
+ }
+ return pssid->second.bridgeName.c_str();
+ }
+
+ for (pssid = _primaryChannels.begin(); pssid != _primaryChannels.end(); pssid++) {
+ const DataChannelBridgeMap& dataChannels = pssid->second.dataChannels;
+ const DataChannelBridgeMap::const_iterator dc = dataChannels.find(ssid);
+ if (dc == dataChannels.end()) continue;
+ if (dc->second.empty()) {
+ return NULL;
+ }
+ return dc->second.c_str();
+ }
+
+ return NULL;
+ }
+
+ void UciConfigurationProvider::GetStationTrafficFilterProfiles(StationTFPMap& output) const {
+ for (StationFilterMap::const_iterator i = _stationFilters.begin(); i != _stationFilters.end(); i++) {
+ output[i->first] = i->second;
+ }
+
+ }

View File

@ -0,0 +1,20 @@
--- a/dcwapd.linuxjsonstatic/main.cxx
+++ b/dcwapd.linuxjsonstatic/main.cxx
@@ -10,6 +10,7 @@
#include "dcwlinux/ap_configuration.h"
#include "dcwlinux/vap_manager.h"
#include "dcwlinux/json_configuration_provider.h"
+#include "dcwlinux/uci_configuration_provider.h"
#include "dcw/dcwlog.h"
@@ -19,7 +20,8 @@ int
main( void ) {
try {
- dcwlinux::JsonConfigurationProvider configProvider("./dcwapdconf.json");
+ //dcwlinux::JsonConfigurationProvider configProvider("./dcwapdconf.json");
+ dcwlinux::UciConfigurationProvider configProvider("dcwapd");
dcwposix::ProcessSignalManager sigman;
dcwposix::SelectEventReactor eventReactor;

View File

@ -0,0 +1,10 @@
--- a/dcwlinux/Makefile.am
+++ b/dcwlinux/Makefile.am
@@ -6,6 +6,7 @@ libdcwlinux_la_SOURCES =
ap_configuration.cxx \
brctlnetwork.cxx \
json_configuration_provider.cxx \
+ uci_configuration_provider.cxx \
macremapper_driver.cxx \
vap_manager.cxx \
virtual_ap.cxx

View File

@ -0,0 +1,15 @@
menu "Configuration"
depends on PACKAGE_modemmanager
config MODEMMANAGER_WITH_MBIM
bool "Include MBIM support"
default y
help
Compile ModemManager with MBIM support
config MODEMMANAGER_WITH_QMI
bool "Include QMI support"
default y
help
Compile ModemManager with QMI support
endmenu

140
net/modemmanager/Makefile Normal file
View File

@ -0,0 +1,140 @@
#
# Copyright (C) 2016 Velocloud Inc.
# Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
#
# This is free software, licensed under the GNU General Public License v2.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=modemmanager
PKG_VERSION:=1.12.0
PKG_RELEASE:=8
PKG_SOURCE:=ModemManager-$(PKG_VERSION).tar.xz
PKG_SOURCE_URL:=https://www.freedesktop.org/software/ModemManager
PKG_HASH:=3daca86164145fffb589939433f596c13fa077c9a187c0d5820fdd5b4e4a6424
PKG_BUILD_DIR:=$(BUILD_DIR)/ModemManager-$(PKG_VERSION)
PKG_MAINTAINER:=Nicholas Smith <nicholas.smith@telcoantennas.com.au>
LICENSE:=GPL-2.0-or-later
LICENSE_FILES:=COPYING
PKG_INSTALL:=1
PKG_BUILD_PARALLEL:=1
PKG_BUILD_DEPENDS:=glib2/host libxslt/host
PKG_FIXUP:=autoreconf
include $(INCLUDE_DIR)/package.mk
define Package/modemmanager/config
source "$(SOURCE)/Config.in"
endef
define Package/modemmanager
SECTION:=net
CATEGORY:=Network
TITLE:=Control utility for any kind of mobile broadband modem
URL:=https://www.freedesktop.org/wiki/Software/ModemManager
DEPENDS:= \
$(INTL_DEPENDS) \
+glib2 \
+dbus \
+MODEMMANAGER_WITH_MBIM:libmbim \
+MODEMMANAGER_WITH_QMI:libqmi
endef
define Package/modemmanager/description
ModemManager is a D-Bus-activated service which allows controlling mobile
broadband modems. Add kernel modules for your modems as needed.
Select Utilities/usb-modeswitch if needed.
endef
CONFIGURE_ARGS += \
--without-polkit \
--without-udev \
--without-suspend-resume \
--with-systemdsystemunitdir=no \
--disable-rpath \
--disable-gtk-doc
ifdef CONFIG_MODEMMANAGER_WITH_MBIM
CONFIGURE_ARGS += --with-mbim
else
CONFIGURE_ARGS += --without-mbim
endif
ifdef CONFIG_MODEMMANAGER_WITH_QMI
CONFIGURE_ARGS += --with-qmi
else
CONFIGURE_ARGS += --without-qmi
endif
define Build/Prepare
$(call Build/Prepare/Default)
( cd "$(PKG_BUILD_DIR)"; \
printf "all:\ninstall:\n" >po/Makefile.in.in; \
)
$(SED) 's|^\(GLIB_MKENUMS\)=.*|\1=$(STAGING_DIR_HOSTPKG)/bin/glib-mkenums|' \
$(PKG_BUILD_DIR)/configure.ac
$(SED) 's|^\(GDBUS_CODEGEN\)=.*|\1=$(STAGING_DIR_HOSTPKG)/bin/gdbus-codegen|' \
$(PKG_BUILD_DIR)/configure.ac
endef
define Build/InstallDev
$(INSTALL_DIR) $(1)/usr/include/ModemManager
$(CP) $(PKG_INSTALL_DIR)/usr/include/ModemManager/*.h $(1)/usr/include/ModemManager
$(INSTALL_DIR) $(1)/usr/include/libmm-glib
$(CP) $(PKG_INSTALL_DIR)/usr/include/libmm-glib/*.h $(1)/usr/include/libmm-glib
$(INSTALL_DIR) $(1)/usr/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libmm-glib.so* $(1)/usr/lib
$(INSTALL_DIR) $(1)/usr/lib/pkgconfig
$(CP) $(PKG_INSTALL_DIR)/usr/lib/pkgconfig/ModemManager.pc $(1)/usr/lib/pkgconfig
$(CP) $(PKG_INSTALL_DIR)/usr/lib/pkgconfig/mm-glib.pc $(1)/usr/lib/pkgconfig
endef
define Package/modemmanager/install
$(INSTALL_DIR) $(1)/lib/udev/rules.d
$(INSTALL_DATA) $(PKG_INSTALL_DIR)/lib/udev/rules.d/*.rules $(1)/lib/udev/rules.d
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ModemManager $(1)/usr/sbin
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/mmcli $(1)/usr/bin
$(INSTALL_DIR) $(1)/usr/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libmm-glib.so.* $(1)/usr/lib
$(INSTALL_DIR) $(1)/usr/lib/ModemManager
$(CP) $(PKG_INSTALL_DIR)/usr/lib/ModemManager/libmm-plugin-*.so* $(1)/usr/lib/ModemManager
$(INSTALL_DIR) $(1)/etc/dbus-1/system.d
$(INSTALL_CONF) $(PKG_INSTALL_DIR)/etc/dbus-1/system.d/org.freedesktop.ModemManager1.conf $(1)/etc/dbus-1/system.d
$(INSTALL_DIR) $(1)/usr/share/dbus-1/system-services
$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/share/dbus-1/system-services/org.freedesktop.ModemManager1.service $(1)/usr/share/dbus-1/system-services
$(INSTALL_DIR) $(1)/usr/share/ModemManager
$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/share/ModemManager/*.conf $(1)/usr/share/ModemManager
$(INSTALL_DATA) ./files/modemmanager.common $(1)/usr/share/ModemManager
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./files/modemmanager.init $(1)/etc/init.d/modemmanager
$(INSTALL_DIR) $(1)/etc/hotplug.d/usb
$(INSTALL_DATA) ./files/25-modemmanager-usb $(1)/etc/hotplug.d/usb
$(INSTALL_DIR) $(1)/etc/hotplug.d/net
$(INSTALL_DATA) ./files/25-modemmanager-net $(1)/etc/hotplug.d/net
$(INSTALL_DIR) $(1)/etc/hotplug.d/tty
$(INSTALL_DATA) ./files/25-modemmanager-tty $(1)/etc/hotplug.d/tty
$(INSTALL_DIR) $(1)/lib/netifd/proto
$(INSTALL_BIN) ./files/modemmanager.proto $(1)/lib/netifd/proto/modemmanager.sh
endef
$(eval $(call BuildPackage,modemmanager))

View File

@ -0,0 +1,22 @@
# OpenWrt ModemManager
## Description
Cellular modem control and connectivity
Optional libraries libmbim and libqmi are available. Optional mbim-utils and qmi-utils are available.
Your modem may require additional kernel modules.
## Usage
# Once installed, you can configure the 2G/3G/4G modem connections directly in
/etc/config/network as in the following example:
config interface 'broadband'
option device '/sys/devices/platform/soc/20980000.usb/usb1/1-1/1-1.2/1-1.2.1'
option proto 'modemmanager'
option apn 'ac.vodafone.es'
option username 'vodafone'
option password 'vodafone'
option pincode '7423'
option lowpower '1'

View File

@ -0,0 +1,31 @@
#!/bin/sh
# Copyright (C) 2016 Velocloud Inc
# Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
# Load common utilities
. /usr/share/ModemManager/modemmanager.common
# We require a interface name
[ -n "${INTERFACE}" ] || exit
# Always make sure the rundir exists
mkdir -m 0755 -p "${MODEMMANAGER_RUNDIR}"
# Report network interface
mm_log "${ACTION} network interface ${INTERFACE}: event processed"
mm_report_event "${ACTION}" "${INTERFACE}" "net" "/sys${DEVPATH}"
# Look for an associated cdc-wdm interface
cdcwdm=""
case "${ACTION}" in
"add") cdcwdm=$(mm_track_cdcwdm "${INTERFACE}") ;;
"remove") cdcwdm=$(mm_untrack_cdcwdm "${INTERFACE}") ;;
esac
# Report cdc-wdm device, if any
[ -n "${cdcwdm}" ] && {
mm_log "${ACTION} cdc interface ${cdcwdm}: custom event processed"
mm_report_event "${ACTION}" "${cdcwdm}" "usbmisc" "/sys${DEVPATH}"
}

View File

@ -0,0 +1,16 @@
#!/bin/sh
# Copyright (C) 2016 Velocloud Inc
# Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
# Load hotplug common utilities
. /usr/share/ModemManager/modemmanager.common
# We require a device name
[ -n "$DEVNAME" ] || exit
# Always make sure the rundir exists
mkdir -m 0755 -p "${MODEMMANAGER_RUNDIR}"
# Report TTY
mm_log "${ACTION} serial interface ${DEVNAME}: event processed"
mm_report_event "${ACTION}" "${DEVNAME}" "tty" "/sys${DEVPATH}"

View File

@ -0,0 +1,13 @@
#!/bin/sh
# Copyright (C) 2019 Aleksander Morgado <aleksander@aleksander.es>
# We need to process only full USB device removal events, we don't
# want to process specific interface removal events.
[ "$ACTION" = remove ] || exit
[ -z "${INTERFACE}" ] || exit
# Load common utilities
. /usr/share/ModemManager/modemmanager.common
mm_clear_modem_wait_status "/sys${DEVPATH}"
mm_cleanup_interface_by_sysfspath "/sys${DEVPATH}"

View File

@ -0,0 +1,332 @@
#!/bin/sh
# Copyright (C) 2016 Velocloud Inc
# Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
################################################################################
. /lib/functions.sh
. /lib/netifd/netifd-proto.sh
################################################################################
# Runtime state
MODEMMANAGER_RUNDIR="/var/run/modemmanager"
MODEMMANAGER_PID_FILE="${MODEMMANAGER_RUNDIR}/modemmanager.pid"
MODEMMANAGER_CDCWDM_CACHE="${MODEMMANAGER_RUNDIR}/cdcwdm.cache"
MODEMMANAGER_SYSFS_CACHE="${MODEMMANAGER_RUNDIR}/sysfs.cache"
MODEMMANAGER_EVENTS_CACHE="${MODEMMANAGER_RUNDIR}/events.cache"
################################################################################
# Common logging
mm_log() {
logger -t "ModemManager" "hotplug: $*"
}
################################################################################
# Receives as input argument the full sysfs path of the device
# Returns the physical device sysfs path
#
# NOTE: this method only works when the device exists, i.e. it cannot be used
# on removal hotplug events
mm_find_physdev_sysfs_path() {
local tmp_path="$1"
while true; do
tmp_path=$(dirname "${tmp_path}")
# avoid infinite loops iterating
[ -z "${tmp_path}" ] || [ "${tmp_path}" = "/" ] && return
# the physical device will be that with a idVendor and idProduct pair of files
[ -f "${tmp_path}"/idVendor ] && [ -f "${tmp_path}"/idProduct ] && {
tmp_path=$(readlink -f "$tmp_path")
echo "${tmp_path}"
return
}
done
}
################################################################################
# Returns the cdc-wdm name retrieved from sysfs
mm_track_cdcwdm() {
local wwan="$1"
local cdcwdm
cdcwdm=$(ls "/sys/class/net/${wwan}/device/usbmisc/")
[ -n "${cdcwdm}" ] || return
# We have to cache it for later, as we won't be able to get the
# associated cdc-wdm device on a remove event
echo "${wwan} ${cdcwdm}" >> "${MODEMMANAGER_CDCWDM_CACHE}"
echo "${cdcwdm}"
}
# Returns the cdc-wdm name retrieved from the cache
mm_untrack_cdcwdm() {
local wwan="$1"
local cdcwdm
# Look for the cached associated cdc-wdm device
[ -f "${MODEMMANAGER_CDCWDM_CACHE}" ] || return
cdcwdm=$(awk -v wwan="${wwan}" '!/^#/ && $0 ~ wwan { print $2 }' "${MODEMMANAGER_CDCWDM_CACHE}")
[ -n "${cdcwdm}" ] || return
# Remove from cache
sed -i "/${wwan} ${cdcwdm}/d" "${MODEMMANAGER_CDCWDM_CACHE}"
echo "${cdcwdm}"
}
################################################################################
# ModemManager needs some time from the ports being added until a modem object
# is exposed in DBus. With the logic here we do an explicit wait of N seconds
# for ModemManager to expose the new modem object, making sure that the wait is
# unique per device (i.e. per physical device sysfs path).
# Gets the modem wait status as retrieved from the cache
mm_get_modem_wait_status() {
local sysfspath="$1"
# If no sysfs cache file, we're done
[ -f "${MODEMMANAGER_SYSFS_CACHE}" ] || return
# Get status of the sysfs path
awk -v sysfspath="${sysfspath}" '!/^#/ && $0 ~ sysfspath { print $2 }' "${MODEMMANAGER_SYSFS_CACHE}"
}
# Clear the modem wait status from the cache, if any
mm_clear_modem_wait_status() {
local sysfspath="$1"
local escaped_sysfspath
[ -f "${MODEMMANAGER_SYSFS_CACHE}" ] && {
# escape '/', '\' and '&' for sed...
escaped_sysfspath=$(echo "$sysfspath" | sed -e 's/[\/&]/\\&/g')
sed -i "/${escaped_sysfspath}/d" "${MODEMMANAGER_SYSFS_CACHE}"
}
}
# Sets the modem wait status in the cache
mm_set_modem_wait_status() {
local sysfspath="$1"
local status="$2"
# Remove sysfs line before adding the new one with the new state
mm_clear_modem_wait_status "${sysfspath}"
# Add the new status
echo "${sysfspath} ${status}" >> "${MODEMMANAGER_SYSFS_CACHE}"
}
# Callback for config_foreach()
mm_get_modem_config_foreach_cb() {
local cfg="$1"
local sysfspath="$2"
local proto
config_get proto "${cfg}" proto
[ "${proto}" = modemmanager ] || return 0
local dev
dev=$(uci_get network "${cfg}" device)
[ "${dev}" = "${sysfspath}" ] || return 0
echo "${cfg}"
}
# Returns the name of the interface configured for this device
mm_get_modem_config() {
local sysfspath="$1"
# Look for configuration for the given sysfs path
config_load network
config_foreach mm_get_modem_config_foreach_cb interface "${sysfspath}"
}
# Wait for a modem in the specified sysfspath
mm_wait_for_modem() {
local cfg="$1"
local sysfspath="$2"
# TODO: config max wait
local n=45
local step=5
while [ $n -ge 0 ]; do
[ -d "${sysfspath}" ] || {
mm_log "error: ignoring modem detection request: no device at ${sysfspath}"
proto_set_available "${cfg}" 0
return 1
}
# Check if the modem exists at the given sysfs path
if ! mmcli -m "${sysfspath}" > /dev/null 2>&1
then
mm_log "error: modem not detected at sysfs path"
else
mm_log "modem exported successfully at ${sysfspath}"
mm_log "setting interface '${cfg}' as available"
proto_set_available "${cfg}" 1
return 0
fi
sleep $step
n=$((n-step))
done
mm_log "error: timed out waiting for the modem to get exported at ${sysfspath}"
proto_set_available "${cfg}" 0
return 2
}
mm_report_modem_wait() {
local sysfspath=$1
local parent_sysfspath status
parent_sysfspath=$(mm_find_physdev_sysfs_path "$sysfspath")
[ -n "${parent_sysfspath}" ] || {
mm_log "error: parent device sysfspath not found"
return
}
status=$(mm_get_modem_wait_status "${parent_sysfspath}")
case "${status}" in
"")
local cfg
cfg=$(mm_get_modem_config "${parent_sysfspath}")
if [ -n "${cfg}" ]; then
mm_log "interface '${cfg}' is set to configure device '${parent_sysfspath}'"
mm_log "now waiting for modem at sysfs path ${parent_sysfspath}"
mm_set_modem_wait_status "${parent_sysfspath}" "processed"
# Launch subshell for the explicit wait
( mm_wait_for_modem "${cfg}" "${parent_sysfspath}" ) > /dev/null 2>&1 &
else
mm_log "no need to wait for modem at sysfs path ${parent_sysfspath}"
mm_set_modem_wait_status "${parent_sysfspath}" "ignored"
fi
;;
"processed")
mm_log "already waiting for modem at sysfs path ${parent_sysfspath}"
;;
"ignored")
;;
*)
mm_log "error: unknown status read for device at sysfs path ${parent_sysfspath}"
;;
esac
}
################################################################################
# Cleanup interfaces
mm_cleanup_interface_cb() {
local cfg="$1"
local proto
config_get proto "${cfg}" proto
[ "${proto}" = modemmanager ] || return 0
proto_set_available "${cfg}" 0
}
mm_cleanup_interfaces() {
config_load network
config_foreach mm_cleanup_interface_cb interface
}
mm_cleanup_interface_by_sysfspath() {
local dev="$1"
local cfg
cfg=$(mm_get_modem_config "$dev")
[ -n "${cfg}" ] || return
mm_log "setting interface '$cfg' as unavailable"
proto_set_available "${cfg}" 0
}
################################################################################
# Event reporting
# Receives as input the action, the device name and the subsystem
mm_report_event() {
local action="$1"
local name="$2"
local subsystem="$3"
local sysfspath="$4"
# Track/untrack events in cache
case "${action}" in
"add")
# On add events, store event details in cache (if not exists yet)
grep -qs "${name},${subsystem}" "${MODEMMANAGER_EVENTS_CACHE}" || \
echo "${action},${name},${subsystem},${sysfspath}" >> "${MODEMMANAGER_EVENTS_CACHE}"
;;
"remove")
# On remove events, remove old events from cache (match by subsystem+name)
sed -i "/${name},${subsystem}/d" "${MODEMMANAGER_EVENTS_CACHE}"
;;
esac
# Report the event
mm_log "event reported: action=${action}, name=${name}, subsystem=${subsystem}"
mmcli --report-kernel-event="action=${action},name=${name},subsystem=${subsystem}" 1>/dev/null 2>&1 &
# Wait for added modem if a sysfspath is given
[ -n "${sysfspath}" ] && [ "$action" = "add" ] && mm_report_modem_wait "${sysfspath}"
}
mm_report_event_from_cache_line() {
local event_line="$1"
local action name subsystem sysfspath
action=$(echo "${event_line}" | awk -F ',' '{ print $1 }')
name=$(echo "${event_line}" | awk -F ',' '{ print $2 }')
subsystem=$(echo "${event_line}" | awk -F ',' '{ print $3 }')
sysfspath=$(echo "${event_line}" | awk -F ',' '{ print $4 }')
mm_log "cached event found: action=${action}, name=${name}, subsystem=${subsystem}, sysfspath=${sysfspath}"
mm_report_event "${action}" "${name}" "${subsystem}" "${sysfspath}"
}
mm_report_events_from_cache() {
# Remove the sysfs cache
rm -f "${MODEMMANAGER_SYSFS_CACHE}"
local n=10
local step=1
local mmrunning=0
# Wait for ModemManager to be available in the bus
while [ $n -ge 0 ]; do
sleep $step
mm_log "checking if ModemManager is available..."
if ! mmcli -L >/dev/null 2>&1
then
mm_log "ModemManager not yet available"
else
mmrunning=1
break
fi
n=$((n-step))
done
[ ${mmrunning} -eq 1 ] || {
mm_log "error: couldn't report initial kernel events: ModemManager not running"
return
}
# Report cached kernel events
while IFS= read -r event_line; do
mm_report_event_from_cache_line "${event_line}"
done < ${MODEMMANAGER_EVENTS_CACHE}
}

View File

@ -0,0 +1,33 @@
#!/bin/sh /etc/rc.common
# Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
USE_PROCD=1
START=70
stop_service() {
# Load common utils
. /usr/share/ModemManager/modemmanager.common
# Set all configured interfaces as unavailable
mm_cleanup_interfaces
}
start_service() {
# Load common utils
. /usr/share/ModemManager/modemmanager.common
# Always make sure the rundir exists
mkdir -m 0755 -p "${MODEMMANAGER_RUNDIR}"
# Initially set all configured interfaces as unavailable
mm_cleanup_interfaces
# Report cached events (will wait for MM to be launched)
( mm_report_events_from_cache ) >/dev/null 2>&1 &
# Setup ModemManager service
procd_open_instance
procd_set_param command /usr/sbin/ModemManager
procd_set_param respawn "${respawn_threshold:-3600}" "${respawn_timeout:-5}" "${respawn_retry:-5}"
procd_set_param pidfile "${MODEMMANAGER_PID_FILE}"
procd_close_instance
}

View File

@ -0,0 +1,501 @@
#!/bin/sh
# Copyright (C) 2016-2019 Aleksander Morgado <aleksander@aleksander.es>
[ -x /usr/bin/mmcli ] || exit 0
[ -x /usr/sbin/pppd ] || exit 0
[ -n "$INCLUDE_ONLY" ] || {
. /lib/functions.sh
. ../netifd-proto.sh
. ./ppp.sh
init_proto "$@"
}
cdr2mask ()
{
# Number of args to shift, 255..255, first non-255 byte, zeroes
set -- $(( 5 - ($1 / 8) )) 255 255 255 255 $(( (255 << (8 - ($1 % 8))) & 255 )) 0 0 0
if [ "$1" -gt 1 ]
then
shift "$1"
else
shift
fi
echo "${1-0}"."${2-0}"."${3-0}"."${4-0}"
}
# This method expects as first argument a list of key-value pairs, as returned by mmcli --output-keyvalue
# The second argument must be exactly the name of the field to read
#
# Sample output:
# $ mmcli -m 0 -K
# modem.dbus-path : /org/freedesktop/ModemManager1/Modem/0
# modem.generic.device-identifier : ed6eff2e3e0f90463da1c2a755b2acacd1335752
# modem.generic.manufacturer : Dell Inc.
# modem.generic.model : DW5821e Snapdragon X20 LTE
# modem.generic.revision : T77W968.F1.0.0.4.0.GC.009\n026
# modem.generic.carrier-configuration : GCF
# modem.generic.carrier-configuration-revision : 08E00009
# modem.generic.hardware-revision : DW5821e Snapdragon X20 LTE
# ....
modemmanager_get_field() {
local list=$1
local field=$2
local value=""
[ -z "${list}" ] || [ -z "${field}" ] && return
# there is always at least a whitespace after each key, and we use that as part of the
# key matching we do (e.g. to avoid getting 'modem.generic.state-failed-reason' as a result
# when grepping for 'modem.generic.state'.
line=$(echo "${list}" | grep "${field} ")
value=$(echo ${line#*:})
# not found?
[ -n "${value}" ] || return 2
# only print value if set
[ "${value}" != "--" ] && echo "${value}"
return 0
}
# build a comma-separated list of values from the list
modemmanager_get_multivalue_field() {
local list=$1
local field=$2
local value=""
local length idx item
[ -z "${list}" ] || [ -z "${field}" ] && return
length=$(modemmanager_get_field "${list}" "${field}.length")
[ -n "${length}" ] || return 0
[ "$length" -ge 1 ] || return 0
idx=1
while [ $idx -le "$length" ]; do
item=$(modemmanager_get_field "${list}" "${field}.value\[$idx\]")
[ -n "${item}" ] && [ "${item}" != "--" ] && {
[ -n "${value}" ] && value="${value}, "
value="${value}${item}"
}
idx=$((idx + 1))
done
# nothing built?
[ -n "${value}" ] || return 2
# only print value if set
echo "${value}"
return 0
}
modemmanager_cleanup_connection() {
local modemstatus="$1"
local bearercount idx bearerpath
bearercount=$(modemmanager_get_field "${modemstatus}" "modem.generic.bearers.length")
# do nothing if no bearers reported
[ -n "${bearercount}" ] && [ "$bearercount" -ge 1 ] && {
# explicitly disconnect just in case
mmcli --modem="${device}" --simple-disconnect >/dev/null 2>&1
# and remove all bearer objects, if any found
idx=1
while [ $idx -le "$bearercount" ]; do
bearerpath=$(modemmanager_get_field "${modemstatus}" "modem.generic.bearers.value\[$idx\]")
mmcli --modem "${device}" --delete-bearer="${bearerpath}" >/dev/null 2>&1
idx=$((idx + 1))
done
}
}
modemmanager_connected_method_ppp_ipv4() {
local interface="$1"
local ttyname="$2"
local username="$3"
local password="$4"
proto_run_command "${interface}" /usr/sbin/pppd \
"${ttyname}" \
115200 \
nodetach \
noaccomp \
nobsdcomp \
nopcomp \
novj \
noauth \
${username:+ user $username} \
${password:+ password $password} \
lcp-echo-failure 5 \
lcp-echo-interval 15 \
lock \
crtscts \
nodefaultroute \
usepeerdns \
ipparam "${interface}" \
ip-up-script /lib/netifd/ppp-up \
ip-down-script /lib/netifd/ppp-down
}
modemmanager_disconnected_method_ppp_ipv4() {
local interface="$1"
echo "running disconnection (ppp method)"
[ -n "${ERROR}" ] && {
local errorstring
errorstring=$(ppp_exitcode_tostring "${ERROR}")
case "$ERROR" in
0)
;;
2)
proto_notify_error "$interface" "$errorstring"
proto_block_restart "$interface"
;;
*)
proto_notify_error "$interface" "$errorstring"
;;
esac
} || echo "pppd result code not given"
proto_kill_command "$interface"
}
modemmanager_connected_method_dhcp_ipv4() {
local interface="$1"
local wwan="$2"
local metric="$3"
proto_init_update "${wwan}" 1
proto_set_keep 1
proto_send_update "${interface}"
json_init
json_add_string name "${interface}_4"
json_add_string ifname "@${interface}"
json_add_string proto "dhcp"
proto_add_dynamic_defaults
[ -n "$metric" ] && json_add_int metric "${metric}"
json_close_object
ubus call network add_dynamic "$(json_dump)"
}
modemmanager_connected_method_static_ipv4() {
local interface="$1"
local wwan="$2"
local address="$3"
local prefix="$4"
local gateway="$5"
local mtu="$6"
local dns1="$7"
local dns2="$8"
local metric="$9"
local mask=""
[ -n "${address}" ] || {
proto_notify_error "${interface}" ADDRESS_MISSING
return
}
[ -n "${prefix}" ] || {
proto_notify_error "${interface}" PREFIX_MISSING
return
}
mask=$(cdr2mask "${prefix}")
# TODO: mtu reporting in proto handler
proto_init_update "${wwan}" 1
proto_set_keep 1
echo "adding IPv4 address ${address}, netmask ${mask}"
proto_add_ipv4_address "${address}" "${mask}"
[ -n "${gateway}" ] && {
echo "adding default IPv4 route via ${gateway}"
proto_add_ipv4_route "0.0.0.0" "0" "${gateway}" "${address}"
}
[ -n "${dns1}" ] && {
echo "adding primary DNS at ${dns1}"
proto_add_dns_server "${dns1}"
}
[ -n "${dns2}" ] && {
echo "adding secondary DNS at ${dns2}"
proto_add_dns_server "${dns2}"
}
[ -n "$metric" ] && json_add_int metric "${metric}"
proto_send_update "${interface}"
}
modemmanager_connected_method_dhcp_ipv6() {
local interface="$1"
local wwan="$2"
local metric="$3"
proto_init_update "${wwan}" 1
proto_set_keep 1
proto_send_update "${interface}"
json_init
json_add_string name "${interface}_6"
json_add_string ifname "@${interface}"
json_add_string proto "dhcpv6"
proto_add_dynamic_defaults
json_add_string extendprefix 1 # RFC 7278: Extend an IPv6 /64 Prefix to LAN
[ -n "$metric" ] && json_add_int metric "${metric}"
json_close_object
ubus call network add_dynamic "$(json_dump)"
}
modemmanager_connected_method_static_ipv6() {
local interface="$1"
local wwan="$2"
local address="$3"
local prefix="$4"
local gateway="$5"
local mtu="$6"
local dns1="$7"
local dns2="$8"
local metric="$9"
[ -n "${address}" ] || {
proto_notify_error "${interface}" ADDRESS_MISSING
return
}
[ -n "${prefix}" ] || {
proto_notify_error "${interface}" PREFIX_MISSING
return
}
# TODO: mtu reporting in proto handler
proto_init_update "${wwan}" 1
proto_set_keep 1
echo "adding IPv6 address ${address}, prefix ${prefix}"
proto_add_ipv6_address "${address}" "128"
proto_add_ipv6_prefix "${address}/${prefix}"
[ -n "${gateway}" ] && {
echo "adding default IPv6 route via ${gateway}"
proto_add_ipv6_route "${gateway}" "128"
proto_add_ipv6_route "::0" "0" "${gateway}" "" "" "${address}/${prefix}"
}
[ -n "${dns1}" ] && {
echo "adding primary DNS at ${dns1}"
proto_add_dns_server "${dns1}"
}
[ -n "${dns2}" ] && {
echo "adding secondary DNS at ${dns2}"
proto_add_dns_server "${dns2}"
}
[ -n "$metric" ] && json_add_int metric "${metric}"
proto_send_update "${interface}"
}
modemmanager_disconnected_method_common() {
local interface="$1"
echo "running disconnection (common)"
proto_init_update "*" 0
proto_send_update "${interface}"
}
proto_modemmanager_init_config() {
available=1
no_device=1
proto_config_add_string device
proto_config_add_string apn
proto_config_add_string username
proto_config_add_string password
proto_config_add_string pincode
proto_config_add_string iptype
proto_config_add_boolean lowpower
proto_config_add_defaults
}
proto_modemmanager_setup() {
local interface="$1"
local modempath modemstatus bearercount bearerpath connectargs bearerstatus beareriface
local bearermethod_ipv4 bearermethod_ipv6
local operatorname operatorid registration accesstech signalquality
local device apn username password pincode iptype metric
local address prefix gateway mtu dns1 dns2
json_get_vars device apn username password pincode iptype metric
# validate sysfs path given in config
[ -n "${device}" ] || {
echo "No device specified"
proto_notify_error "${interface}" NO_DEVICE
proto_set_available "${interface}" 0
return 1
}
[ -e "${device}" ] || {
echo "Device not found in sysfs"
proto_set_available "${interface}" 0
return 1
}
# validate that ModemManager is handling the modem at the sysfs path
modemstatus=$(mmcli --modem="${device}" --output-keyvalue)
modempath=$(modemmanager_get_field "${modemstatus}" "modem.dbus-path")
[ -n "${modempath}" ] || {
echo "Device not managed by ModemManager"
proto_notify_error "${interface}" DEVICE_NOT_MANAGED
proto_set_available "${interface}" 0
return 1
}
echo "modem available at ${modempath}"
# always cleanup before attempting a new connection, just in case
modemmanager_cleanup_connection "${modemstatus}"
# setup connect args; APN mandatory (even if it may be empty)
echo "starting connection with apn '${apn}'..."
connectargs="apn=${apn}${iptype:+,ip-type=${iptype}}${username:+,user=${username}}${password:+,password=${password}}${pincode:+,pin=${pincode}}"
mmcli --modem="${device}" --timeout 120 --simple-connect="${connectargs}" || {
proto_notify_error "${interface}" CONNECT_FAILED
proto_block_restart "${interface}"
return 1
}
# log additional useful information
modemstatus=$(mmcli --modem="${device}" --output-keyvalue)
operatorname=$(modemmanager_get_field "${modemstatus}" "modem.3gpp.operator-name")
[ -n "${operatorname}" ] && echo "network operator name: ${operatorname}"
operatorid=$(modemmanager_get_field "${modemstatus}" "modem.3gpp.operator-code")
[ -n "${operatorid}" ] && echo "network operator MCCMNC: ${operatorid}"
registration=$(modemmanager_get_field "${modemstatus}" "modem.3gpp.registration-state")
[ -n "${registration}" ] && echo "registration type: ${registration}"
accesstech=$(modemmanager_get_multivalue_field "${modemstatus}" "modem.generic.access-technologies")
[ -n "${accesstech}" ] && echo "access technology: ${accesstech}"
signalquality=$(modemmanager_get_field "${modemstatus}" "modem.generic.signal-quality.value")
[ -n "${signalquality}" ] && echo "signal quality: ${signalquality}%"
# we won't like it if there are more than one bearers, as that would mean the
# user manually created them, and that's unsupported by this proto
bearercount=$(modemmanager_get_field "${modemstatus}" "modem.generic.bearers.length")
[ -n "${bearercount}" ] && [ "$bearercount" -eq 1 ] || {
proto_notify_error "${interface}" INVALID_BEARER_LIST
return 1
}
# load connected bearer information
bearerpath=$(modemmanager_get_field "${modemstatus}" "modem.generic.bearers.value\[1\]")
bearerstatus=$(mmcli --bearer "${bearerpath}" --output-keyvalue)
# load network interface and method information
beareriface=$(modemmanager_get_field "${bearerstatus}" "bearer.status.interface")
bearermethod_ipv4=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.method")
bearermethod_ipv6=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.method")
# setup IPv4
[ -n "${bearermethod_ipv4}" ] && {
echo "IPv4 connection setup required in interface ${interface}: ${bearermethod_ipv4}"
case "${bearermethod_ipv4}" in
"dhcp")
modemmanager_connected_method_dhcp_ipv4 "${interface}" "${beareriface}" "${metric}"
;;
"static")
address=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.address")
prefix=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.prefix")
gateway=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.gateway")
mtu=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.mtu")
dns1=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.dns.value\[1\]")
dns2=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.dns.value\[2\]")
modemmanager_connected_method_static_ipv4 "${interface}" "${beareriface}" "${address}" "${prefix}" "${gateway}" "${mtu}" "${dns1}" "${dns2}" "${metric}"
;;
"ppp")
modemmanager_connected_method_ppp_ipv4 "${interface}" "${beareriface}" "${username}" "${password}"
;;
*)
proto_notify_error "${interface}" UNKNOWN_METHOD
return 1
;;
esac
}
# setup IPv6
# note: if using ipv4v6, both IPv4 and IPv6 settings will have the same MTU and metric values reported
[ -n "${bearermethod_ipv6}" ] && {
echo "IPv6 connection setup required in interface ${interface}: ${bearermethod_ipv6}"
case "${bearermethod_ipv6}" in
"dhcp")
modemmanager_connected_method_dhcp_ipv6 "${interface}" "${beareriface}" "${metric}"
;;
"static")
address=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.address")
prefix=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.prefix")
gateway=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.gateway")
mtu=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.mtu")
dns1=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.dns.value\[1\]")
dns2=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.dns.value\[2\]")
modemmanager_connected_method_static_ipv6 "${interface}" "${beareriface}" "${address}" "${prefix}" "${gateway}" "${mtu}" "${dns1}" "${dns2}" "${metric}"
;;
"ppp")
proto_notify_error "${interface}" "unsupported method"
return 1
;;
*)
proto_notify_error "${interface}" UNKNOWN_METHOD
return 1
;;
esac
}
return 0
}
proto_modemmanager_teardown() {
local interface="$1"
local modemstatus bearerpath errorstring
local bearermethod_ipv4 bearermethod_ipv6
local device lowpower iptype
json_get_vars device lowpower iptype
echo "stopping network"
# load connected bearer information, just the first one should be ok
modemstatus=$(mmcli --modem="${device}" --output-keyvalue)
bearerpath=$(modemmanager_get_field "${modemstatus}" "modem.generic.bearers.value\[1\]")
[ -n "${bearerpath}" ] || {
echo "couldn't load bearer path"
return
}
# load bearer connection methods
bearerstatus=$(mmcli --bearer "${bearerpath}" --output-keyvalue)
bearermethod_ipv4=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv4-config.method")
[ -n "${bearermethod_ipv4}" ] &&
echo "IPv4 connection teardown required in interface ${interface}: ${bearermethod_ipv4}"
bearermethod_ipv6=$(modemmanager_get_field "${bearerstatus}" "bearer.ipv6-config.method")
[ -n "${bearermethod_ipv6}" ] &&
echo "IPv6 connection teardown required in interface ${interface}: ${bearermethod_ipv6}"
# disconnection handling only requires special treatment in IPv4/PPP
[ "${bearermethod_ipv4}" = "ppp" ] && modemmanager_disconnected_method_ppp_ipv4 "${interface}"
modemmanager_disconnected_method_common "${interface}"
# disconnect
mmcli --modem="${device}" --simple-disconnect ||
proto_notify_error "${interface}" DISCONNECT_FAILED
# disable
mmcli --modem="${device}" --disable
# low power, only if requested
[ "${lowpower:-0}" -lt 1 ] ||
mmcli --modem="${device}" --set-power-state-low
}
[ -n "$INCLUDE_ONLY" ] || {
add_protocol modemmanager
}

75
net/nextdns/Makefile Normal file
View File

@ -0,0 +1,75 @@
#
# Copyright (C) 2019 NextDNS Inc
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=nextdns
PKG_VERSION:=1.1.5
PKG_RELEASE:=3
PKG_SOURCE_PROTO:=git
PKG_SOURCE_VERSION:=v$(PKG_VERSION)
PKG_SOURCE_URL:=https://github.com/nextdns/nextdns.git
PKG_MIRROR_HASH:=01ff61771bcf076f1659167b8676234fdefefac9cd0a05aa1d491a7c5f0145fa
PKG_MAINTAINER:=Olivier Poitrey <rs@nextdns.io>
PKG_LICENSE:=MIT
PKG_LICENSE_FILES:=LICENSE
PKG_BUILD_DEPENDS:=golang/host
PKG_BUILD_PARALLEL:=1
PKG_USE_MIPS16:=0
GO_PKG:=github.com/nextdns/nextdns
GO_PKG_LDFLAGS:=-s -w
GO_PKG_LDFLAGS_X:=main.version=$(PKG_VERSION)
include $(INCLUDE_DIR)/package.mk
include ../../lang/golang/golang-package.mk
define Package/nextdns
SECTION:=net
CATEGORY:=Network
TITLE:=NextDNS DNS over HTTPS Proxy
URL:=https://github.com/nextdns/nextdns
DEPENDS:=$(GO_ARCH_DEPENDS) +ca-bundle
endef
define Package/nextdns/install
$(call GoPackage/Package/Install/Bin,$(PKG_INSTALL_DIR))
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/nextdns $(1)/usr/sbin/
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./files/nextdns.init $(1)/etc/init.d/nextdns
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_CONF) ./files/nextdns.config $(1)/etc/config/nextdns
endef
define Package/nextdns/description
Official NextDNS DNS over HTTPS Proxy.
endef
define Package/golang-github-nextdns-nextdns-dev
$(call Package/nextdns)
$(call GoPackage/GoSubMenu)
TITLE+= (source files)
PKGARCH:=all
endef
define Package/golang-github-nextdns-nextdns-dev/description
$(call Package/nextdns/description)
This package provides the source files for the client/bridge program.
endef
$(eval $(call GoBinPackage,nextdns))
$(eval $(call BuildPackage,nextdns))
$(eval $(call GoSrcPackage,golang-github-nextdns-nextdns-dev))
$(eval $(call BuildPackage,golang-github-nextdns-nextdns-dev))

View File

@ -0,0 +1,31 @@
config nextdns main
option enabled '1'
# NextDNS custom configuration id (create on on https://nextdns.io).
# If not defined, this package will act as a non-logging, non-filtering
# DNS over HTTPS resolver.
#
#option config abcdef
# Custom configurations can also be conditionally assigned to LAN hosts based
# their MAC address or subnet. The first matching host_config wins. If both
# host_config and config are defined, config is always placed last, as default
# option.
#
#list host_config 'da:c8:6d:b6:93:78=fedcba'
#list host_config '10.0.1.2/32=abc123'
#list host_config '10.0.3.0/24=def321'
# Listen on a custom local port so a DNS front (like dnsmasq) can use us as
# a forwarder.
option listen '127.0.0.1:5342'
# Expose LAN clients information in NextDNS analytics.
option report_client_info '1'
# When enabled, use DNS servers located in jurisdictions with strong privacy laws.
# Available locations are: Switzerland, Iceland, Finland, Panama and Hong Kong.
option hardened_privacy '0'
# Log individual queries to system log.
option log_queries '0'

View File

@ -0,0 +1,76 @@
#!/bin/sh /etc/rc.common
# shellcheck disable=SC2034 disable=SC2154
USE_PROCD=1
# starts after network starts
START=21
# stops before networking stops
STOP=89
PROG=/usr/sbin/nextdns
add_dnsmasq_opt() {
mkdir -p /tmp/dnsmasq.d
echo "$1" >> /tmp/dnsmasq.d/nextdns.conf
}
dnsmasq_reload() {
# Reload dnsmasq is already running.
if /etc/init.d/dnsmasq running; then
/etc/init.d/dnsmasq reload
fi
}
handle_host_config() {
host_config_args="$host_config_args -config=$1"
}
start_service() {
config_load nextdns
config_get_bool enabled main enabled "1"
rm -f /tmp/dnsmasq.d/nextdns.conf
if [ "$enabled" = "1" ]; then
config_get config main config ""
config_list_foreach main host_config handle_host_config
config_get listen main listen "127.0.0.1:5342"
config_get_bool report_client_info main report_client_info "1"
config_get_bool hardened_privacy main hardened_privacy "0"
config_get_bool log_queries main log_queries "0"
# Add a custom configuration for dnsmasq.
server=$(echo "$listen" | sed -e 's/:/#/')
add_dnsmasq_opt "server=$server"
add_dnsmasq_opt "no-resolv"
if [ "$report_client_info" = "1" ]; then
add_dnsmasq_opt "add-mac"
add_dnsmasq_opt "add-subnet=32,128"
fi
procd_open_instance
# shellcheck disable=SC2086
procd_set_param command "$PROG" run \
-listen="$listen" \
$host_config_args \
-config="$config" \
-report-client-info="$report_client_info" \
-hardened-privacy="$hardened_privacy" \
-log-queries="$log_queries"
procd_set_param stdout 1
procd_set_param stderr 1
procd_set_param respawn "${respawn_threshold:-3600}" "${respawn_timeout:-5}" "${respawn_retry:-5}"
procd_close_instance
fi
dnsmasq_reload
}
stop_service() {
rm -f /tmp/dnsmasq.d/nextdns.conf
dnsmasq_reload
}
service_triggers() {
procd_add_reload_trigger "nextdns"
}

107
net/radicale2/Makefile Normal file
View File

@ -0,0 +1,107 @@
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
include $(TOPDIR)/rules.mk
PKG_NAME:=radicale2
PKG_VERSION:=2.1.11
PKG_RELEASE:=1
PKG_MAINTAINER:=Daniel Dickinson <cshored@thecshore.com>
PKG_LICENSE:=GPL-3.0
PKG_LICENSE_FILES:=COPYING
PKG_CPE_ID:=cpe:/a:radicale:radicale
PKG_SOURCE:=Radicale-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://files.pythonhosted.org/packages/source/R/Radicale/
PKG_HASH:=02273fcc6ae10e0f74aa12652e24d0001eec8dbf467d54ddb4dfcc2af7d7a5db
PKG_BUILD_DIR:=$(BUILD_DIR)/radicale2-$(BUILD_VARIANT)-$(PKG_VERSION)
include $(INCLUDE_DIR)/package.mk
include ../../lang/python/python3-package.mk
PKG_UNPACK:=$(HOST_TAR) -C $(PKG_BUILD_DIR) --strip-components=1 -xzf $(DL_DIR)/$(PKG_SOURCE)
define Package/radicale2/Default
SECTION:=net
CATEGORY:=Network
SUBMENU:=Web Servers/Proxies
URL:=http://radicale.org/
TITLE:=Radicale 2.x CalDAV/CardDAV server
MAINTAINER:=Daniel Dickinson <cshored@thecshore.com>
endef
define Package/radicale2
$(call Package/radicale2/Default)
USERID:=radicale2=225:radicale2=225
DEPENDS:=+python3 +python3-dateutil +python3-vobject +python3-setuptools
CONFLICTS:=radicale
VARIANT:=python3
endef
define Package/radicale2-examples
$(call Package/radicale2/Default)
TITLE:=Radicale v2 example configs
endef
define Package/radicale2-meta/description
The Radicale Project is a CalDAV (calendar) and CardDAV (contact) server. It aims to be a light solution, easy to use, easy to install, easy to configure. As a consequence, it requires few software dependances and is pre-configured to work out-of-the-box.
The Radicale Project runs on most of the UNIX-like platforms (Linux, BSD, MacOS X) and Windows. It is known to work with Evolution, Lightning, iPhone and Android clients. It is free and open-source software, released under GPL version 3.
endef
define Package/radicale2/description
$(call Package/radicale2-meta/description)
.
This package contains the python files.
endef
define Package/radicale2-examples/description
$(call Package/radicale2-meta/description)
.
This package contains upstream configs for example purposes.
endef
define Package/radicale2/conffiles
/etc/config/radicale2
/etc/radicale2/config
/etc/radicale2/users
/etc/radicale2/rights
/etc/radicale2/logging
endef
define Package/radicale2/preinst
#!/bin/sh
[ -n "$${IPKG_INSTROOT}" ] && exit 0 # if run within buildroot exit
# stop service if PKG_UPGRADE
[ "$${PKG_UPGRADE}" = "1" ] && /etc/init.d/radicale2 stop >/dev/null 2>&1
exit 0 # suppress errors from stop command
endef
define Py3Package/radicale2/filespec
+|$(PYTHON3_PKG_DIR)
+|/usr/bin/radicale2|0755
endef
define Py3Package/radicale2/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/radicale $(PKG_INSTALL_DIR)/usr/bin/radicale2
$(SED) 's,^#!.*python.*,#!/usr/bin/python$(PYTHON3_VERSION),' $(PKG_INSTALL_DIR)/usr/bin/radicale2
$(INSTALL_DIR) $(1)/etc/config $(1)/etc/init.d
$(INSTALL_CONF) ./files/radicale2.config $(1)/etc/config/radicale2
$(INSTALL_BIN) ./files/radicale2.init $(1)/etc/init.d/radicale2
endef
define Package/radicale2-examples/install
$(INSTALL_DIR) $(1)/usr/share/radicale2
$(INSTALL_DATA) $(PKG_BUILD_DIR)/config $(1)/usr/share/radicale2/config.example
$(INSTALL_DATA) $(PKG_BUILD_DIR)/rights $(1)/usr/share/radicale2/rights.example
$(INSTALL_DATA) $(PKG_BUILD_DIR)/logging $(1)/usr/share/radicale2/logging.example
endef
$(eval $(call Py3Package,radicale2))
$(eval $(call BuildPackage,radicale2))
$(eval $(call BuildPackage,radicale2-src))
$(eval $(call BuildPackage,radicale2-examples))

View File

@ -0,0 +1,7 @@
#config section server
# list host 127.0.0.1:5232
# list host ::1:5232
#config user
#option name user1
#option password password1

View File

@ -0,0 +1,278 @@
#!/bin/sh /etc/rc.common
START=80
STOP=10
CFGDIR=/var/etc/radicale2
SYSCFG=$CFGDIR/config
USRCFG=$CFGDIR/users
DATADIR="/srv/radicale2/data"
LOGDIR=""
USE_PROCD=1
# we could start with empty configuration file using defaults
[ -f ${IPKG_INSTROOT}/etc/config/radicale2 ] || touch ${IPKG_INSTROOT}/etc/config/radicale2
conf_line() {
local cfgfile="$1"
local option="$2"
local value="$3"
if [ -n "$value" ]; then
eval "echo '$2' = '$value' >>'$cfgfile'"
fi
}
conf_getline() {
local cfg="$1"
local cfgfile="$2"
local option="$3"
local defval="$4"
local flag="$5"
unset value
if [ "$flag" != "1" ]; then
config_get value "$cfg" "$option" "$defval"
conf_line "$cfgfile" "$option" "$value"
else
config_get_bool value "$cfg" "$option" "$defval"
[ -z "$defval" ] && defval=1
if [ "$value" -ne "$defval" ]; then
if [ "$value" -ne 0 ]; then
conf_line "$cfgfile" "$option" "True"
else
conf_line "$cfgfile" "$option" "False"
fi
fi
fi
}
build_hosts_line() {
local val="$1"
append hostlist "$val" ", "
}
conf_section() {
local cfg="$1"
local cfgfile="$2"
local hostlist=""
local value
echo "[$cfg]
" >>$cfgfile
case $cfg in
server)
config_list_foreach "$cfg" host build_hosts_line
conf_line "$tmpfile" hosts "$hostlist"
conf_getline "$cfg" $tmpfile max_connections
conf_getline "$cfg" $tmpfile max_conntent_length
conf_getline "$cfg" $tmpfile timeout
conf_getline "$cfg" $tmpfile ssl 0 1
if [ "$value" -eq 1 ]; then
conf_getline "$cfg" $tmpfile certificate
conf_getline "$cfg" $tmpfile key
conf_getline "$cfg" $tmpfile certificate_authority
conf_getline "$cfg" $tmpfile protocol
conf_getline "$cfg" $tmpfile ciphers
fi
conf_getline "$cfg" $tmpfile dns_lookup 1 1
conf_getline "$cfg" $tmpfile realm
;;
encoding)
conf_getline "$cfg" $tmpfile request
conf_getline "$cfg" $tmpfile stock
;;
auth)
conf_getline "$cfg" $tmpfile "type" htpasswd
if [ "$value" = "htpasswd" ]; then
conf_getline "$cfg" $tmpfile htpasswd_filename $CFGDIR/users
conf_getline "$cfg" "$tmpfile" htpasswd_encryption plain
fi
conf_getline "$cfg" "$tmpfile" delay
;;
rights)
conf_getline "$cfg" "$tmpfile" "type"
if [ "$value" = "from_file" ]; then
conf_getline "$cfg" "$tmpfile" "file"
fi
;;
storage)
conf_getline "$cfg" $tmpfile filesystem_folder "$DATADIR"
DATADIR="$value"
conf_getline "$cfg" $tmpfile filesystem_locking 1 1
conf_getline "$cfg" $tmpfile max_sync_token_age
conf_getline "$cfg" $tmpfile filesystem_close_lock_file 0 1
conf_getline "$cfg" $tmpfile hook
;;
web)
conf_getline "$cfg" $tmpfile "type"
;;
logging)
conf_getline "$cfg" "$tmpfile" config
conf_getline "$cfg" "$tmpfile" debug 0 1
conf_getline "$cfg" "$tmpfile" full_environment 0 1
conf_getline "$cfg" "$tmpfile" mask_passwords 1 1
;;
headers)
config_get "$cfg" "$tmpfile" cors
if [ -n "$cors" ]; then
echo "Access-Control-Allow-Origin = $cors" >>$tmpfile
fi
;;
esac
echo "
" >>$cfgfile
}
add_missing_sections() {
local cfgfile="$1"
for section in server encoding auth rights storage web logging headers; do
if [ "$section" = "server" ]; then
grep -q "\[$section\]" $cfgfile || echo "
[$section]
hosts = 0.0.0.0:5232, [::]:5232
" >>$cfgfile
elif [ "$section" = "auth" ]; then
grep -q "\[$section\]" $cfgfile || echo "
[$section]
type = htpasswd
htpasswd_filename = $CFGDIR/users
htpasswd_encryption = plain
" >>$cfgfile
elif [ "$section" = "storage" ]; then
grep -q "\[$section\]" $cfgfile || echo "
[$section]
filesystem_folder = $DATADIR
" >>$cfgfile
else
grep -q "\[$section\]" $cfgfile || echo "
[$section]
" >>$cfgfile
fi
done
}
add_user() {
local cfg="$1"
local tmpfile="$2"
local name password
config_get name "$cfg" name
config_get password "$cfg" password
[ -n "$name" ] && echo "$name:$password" >>$tmpfile
}
build_users() {
local tmpfile="$1"
# temporary config file
# radicale2 needs read access
chmod 0640 $tmpfile
config_foreach add_user user "$tmpfile"
}
build_config() {
local tmpfile=$(mktemp)
local tmpfile2=$(mktemp)
# temporary config file
# radicale2 need read access
chmod 0640 $tmpfile
config_load radicale2
config_foreach conf_section section $tmpfile
add_missing_sections $tmpfile
build_users $tmpfile2
# move tmp to final
mkdir -m0750 -p $CFGDIR
cat $tmpfile >$SYSCFG
rm -f $tmpfile
cat $tmpfile2 >$USRCFG
rm -f $tmpfile2
}
set_permission() {
# config file permissions (read access for group)
chmod 0750 $CFGDIR
chmod 0640 $SYSCFG
chmod 0640 $USRCFG
chgrp -R radicale2 $CFGDIR
# data directory does not exist
[ -d $DATADIR ] || {
logger -p user.error -t "radicale2[----]" "Data directory '$DATADIR' does not exist. Startup failed !!!"
}
}
interface_triggers() {
local action="$1"
local triggerlist trigger
config_load radicale2
config_get triggerlist server triggerlist
. /lib/functions/network.sh
if [ -n "$triggerlist" ]; then
for trigger in $triggerlist; do
if [ "$action" = "add_trigger" ]; then
procd_add_interface_trigger "interface.*" "$trigger" /etc/init.d/radicale2 reload
else
network_is_up "$trigger" && return 0
fi
done
else
if [ "$action" = "add_trigger" ]; then
procd_add_raw_trigger "interface.*.up" 2000 /etc/init.d/radicale2 reload
else
ubus call network.device status | grep -q '"up": true' && return 0
fi
fi
[ "$action" = "add_trigger" ] || return 1
}
start_service() {
local haveinterface
if [ ! -r /etc/radicale2/config ]; then
build_config
set_permission
fi
interface_triggers "check_interface_up" || return
procd_open_instance "radicale2"
procd_set_param respawn
procd_set_param stderr 1
procd_set_param stdout 1
if [ ! -r /etc/radicale2/config ]; then
procd_set_param command /usr/bin/radicale2 --config="$SYSCFG"
else
procd_set_param command /usr/bin/radicale2 --config="/etc/radicale2/config"
fi
procd_set_param user radicale2
procd_close_instance
return 0
}
service_triggers() {
interface_triggers "add_trigger"
procd_add_reload_trigger "radicale2"
}

View File

@ -0,0 +1,13 @@
Index: radicale2-python3-2.1.11/setup.py
===================================================================
--- radicale2-python3-2.1.11.orig/setup.py
+++ radicale2-python3-2.1.11/setup.py
@@ -67,7 +67,7 @@ setup(
package_data={"radicale": WEB_FILES},
entry_points={"console_scripts": ["radicale = radicale.__main__:run"]},
install_requires=["vobject>=0.9.6", "python-dateutil>=2.7.3"],
- setup_requires=pytest_runner,
+ setup_requires=[],
tests_require=tests_require,
extras_require={
"test": tests_require,

View File

@ -0,0 +1,52 @@
#
# Copyright (C) 2019 rosysong@rosinson.com
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=rosy-file-server
PKG_VERSION:=1.0.0
PKG_RELEASE:=1
PKG_LICENSE:=GPL-2.0
PKG_MAINTAINER:=Rosy Song <rosysong@rosinson.com>
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
include $(INCLUDE_DIR)/package.mk
define Package/rosy-file-server
SUBMENU:=File Transfer
SECTION:=net
CATEGORY:=Network
TITLE:=Rosy File Server over HTTP
PKGARCH:=all
endef
define Package/rosy-file-server/description
This package is a configuration management for luci-app-rosy-file-server.
endef
define Package/rosy-file-server/conffiles
/etc/config/rosy-file-server
endef
define Build/Prepare
endef
define Build/Configure
endef
define Build/Compile
endef
define Package/rosy-file-server/install
$(INSTALL_DIR) $(1)/etc/init.d $(1)/etc/config
$(INSTALL_BIN) ./files/rosyfs.init $(1)/etc/init.d/rosyfs
$(INSTALL_CONF) ./files/rosyfs.config $(1)/etc/config/rosyfs
endef
$(eval $(call BuildPackage,rosy-file-server))

View File

@ -0,0 +1,13 @@
#
# Copyright (C) 2019 rosysong@rosinson.com
#
config rosyfs default
# Web title
option title 'Rosy File Server'
# Path to share
option target '/www'
option disabled '0'

View File

@ -0,0 +1,29 @@
#!/bin/sh /etc/rc.common
#
# Copyright (C) 2019 rosysong@rosinson.com
#
START=99
USE_PROCD=1
SHARE_PATH=/www/rosyfs-share
service_triggers() {
procd_add_reload_trigger rosyfs
}
start_service() {
config_load rosyfs
config_get disabled default disabled '0'
config_get target default target ''
[ $disabled -eq 1 ] && return
[ -n "$target" -a ! "$(readlink $SHARE_PATH)" = "$target" ] && {
rm -f $SHARE_PATH
ln -s $target $SHARE_PATH
}
}
stop_service() {
rm -f $SHARE_PATH
}

View File

@ -8,63 +8,117 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=unbound PKG_NAME:=unbound
PKG_VERSION:=1.8.1 PKG_VERSION:=1.9.5
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://nlnetlabs.nl/downloads/unbound
PKG_HASH:=8a8d400f697c61d73d109c250743a1b6b79848297848026d82b43e831045db57
PKG_MAINTAINER:=Eric Luehrsen <ericluehrsen@gmail.com>
PKG_LICENSE:=BSD-3-Clause PKG_LICENSE:=BSD-3-Clause
PKG_LICENSE_FILES:=LICENSE PKG_LICENSE_FILES:=LICENSE
PKG_MAINTAINER:=Eric Luehrsen <ericluehrsen@gmail.com> PKG_CPE_ID:=cpe:/a:nlnetlabs:unbound
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=http://www.unbound.net/downloads
PKG_HASH:=c362b3b9c35d1b8c1918da02cdd5528d729206c14c767add89ae95acae363c5d
PKG_BUILD_PARALLEL:=1 PKG_BUILD_PARALLEL:=1
PKG_FIXUP:=autoreconf PKG_FIXUP:=autoreconf
PKG_INSTALL:=1 PKG_INSTALL:=1
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(BUILD_VARIANT)/$(PKG_NAME)-$(PKG_VERSION)
include $(INCLUDE_DIR)/package.mk include $(INCLUDE_DIR)/package.mk
define Package/unbound/Default define Package/unbound/Default
TITLE:=Validating Recursive DNS Server
URL:=http://www.unbound.net/
DEPENDS:=+libopenssl
endef
define Package/unbound
$(call Package/unbound/Default)
SECTION:=net SECTION:=net
CATEGORY:=Network CATEGORY:=Network
SUBMENU:=IP Addresses and Names SUBMENU:=IP Addresses and Names
USERID:=unbound=553:unbound=553 USERID:=unbound:unbound
TITLE+= (daemon) TITLE:=Recursive DNS Server
DEPENDS+= +libunbound URL:=https://nlnetlabs.nl/projects/unbound/about
DEPENDS:=+libopenssl +@OPENSSL_WITH_EC
endef endef
define Package/unbound/description define Package/unbound-daemon
This package contains the Unbound daemon. $(call Package/unbound/Default)
TITLE+= (daemon, light traffic)
DEPENDS+= +libunbound
VARIANT:=light
endef
define Package/unbound-daemon/description
This package contains the Unbound daemon with basic includes
necessary to meet the needs of UCI/LuCI configuration optoins.
endef
define Package/unbound-daemon-heavy
$(call Package/unbound/Default)
TITLE+= (daemon, heavy traffic)
URL:=https://nlnetlabs.nl/documentation/unbound/howto-optimise
DEPENDS+= +libunbound-heavy +libpthread +libevent2 +libevent2-pthreads
VARIANT:=heavy
PROVIDES:=unbound-daemon
endef
define Package/unbound-daemon-heavy/description
This package contains the Unbound daemon including 'libevent' and
'libpthread' to better handle large networks with heavy query loads.
endef
define Package/libunbound
$(call Package/unbound/Default)
SECTION:=libs
CATEGORY:=Libraries
SUBMENU:=Networking
TITLE+= (library, light traffic)
VARIANT:=light
DEFAULT_VARIANT:=1
endef
define Package/libunbound/description
This package contains the Unbound shared library with basic includes
necessary to meet the needs of UCI/LuCI configuration optoins.
endef
define Package/libunbound-heavy
$(call Package/unbound/Default)
SECTION:=libs
CATEGORY:=Libraries
SUBMENU:=Networking
TITLE+= (library, heavy traffic)
URL:=https://nlnetlabs.nl/documentation/unbound/howto-optimise
DEPENDS+= +libpthread +libevent2 +libevent2-pthreads
VARIANT:=heavy
PROVIDES:=libunbound
endef
define Package/libunbound-heavy/description
This package contains the Unbound shared library including 'libevent' and
'libpthread' to better handle large networks with heavy query loads.
endef endef
define Package/unbound-anchor define Package/unbound-anchor
$(call Package/unbound/Default) $(call Package/unbound/Default)
SECTION:=net TITLE+= (root DSKEY)
CATEGORY:=Network DEPENDS+= +unbound-daemon +libexpat
SUBMENU:=IP Addresses and Names
TITLE+= (DSKEY utility)
DEPENDS+= +unbound +libexpat
endef endef
define Package/unbound-anchor/description define Package/unbound-anchor/description
This package contains the Unbound anchor utility. This package contains the Unbound anchor utility.
endef endef
define Package/unbound-checkconf
$(call Package/unbound/Default)
TITLE+= (config checker)
DEPENDS+= +unbound-daemon
endef
define Package/unbound-checkconf/description
This package contains the Unbound DNS configuration checker utility.
endef
define Package/unbound-control define Package/unbound-control
$(call Package/unbound/Default) $(call Package/unbound/Default)
SECTION:=net TITLE+= (remote control)
CATEGORY:=Network DEPENDS+= +unbound-daemon
SUBMENU:=IP Addresses and Names
TITLE+= (control utility)
DEPENDS+= +unbound
endef endef
define Package/unbound-control/description define Package/unbound-control/description
@ -73,9 +127,6 @@ endef
define Package/unbound-control-setup define Package/unbound-control-setup
$(call Package/unbound/Default) $(call Package/unbound/Default)
SECTION:=net
CATEGORY:=Network
SUBMENU:=IP Addresses and Names
TITLE+= (control setup) TITLE+= (control setup)
DEPENDS+= +unbound-control +openssl-util DEPENDS+= +unbound-control +openssl-util
endef endef
@ -86,10 +137,7 @@ endef
define Package/unbound-host define Package/unbound-host
$(call Package/unbound/Default) $(call Package/unbound/Default)
SECTION:=net TITLE+= (DNS lookup)
CATEGORY:=Network
SUBMENU:=IP Addresses and Names
TITLE+= (lookup utility)
DEPENDS+= +libunbound DEPENDS+= +libunbound
endef endef
@ -97,52 +145,58 @@ define Package/unbound-host/description
This package contains the Unbound DNS lookup utility. This package contains the Unbound DNS lookup utility.
endef endef
define Package/libunbound
$(call Package/unbound/Default)
SECTION:=libs
CATEGORY:=Libraries
TITLE+= (library)
DEPENDS+= +libpthread
endef
define Package/libunbound/description
This package contains the Unbound shared library.
endef
CONFIGURE_ARGS += \ CONFIGURE_ARGS += \
--disable-dsa \ --disable-dsa \
--disable-gost \ --disable-gost \
--enable-allsymbols \ --enable-allsymbols \
--enable-ecdsa \
--enable-tfo-client \ --enable-tfo-client \
--enable-tfo-server \ --enable-tfo-server \
--with-libexpat="$(STAGING_DIR)/usr" \ --with-libexpat="$(STAGING_DIR)/usr" \
--with-ssl="$(STAGING_DIR)/usr" \ --with-ssl="$(STAGING_DIR)/usr" \
--with-pidfile=/var/run/unbound.pid \ --with-user=unbound \
--with-user=unbound --with-run-dir=/var/lib/unbound \
--with-conf-file=/var/lib/unbound/unbound.conf \
--with-pidfile=/var/run/unbound.pid
define Package/unbound/conffiles ifeq ($(BUILD_VARIANT),heavy)
CONFIGURE_ARGS += \
--with-pthreads \
--with-libevent="$(STAGING_DIR)/usr" \
--enable-event-api
else
CONFIGURE_ARGS += \
--without-pthreads \
--without-solaris-threads \
--without-libevent
endif
define Package/unbound-daemon/conffiles
/etc/config/unbound /etc/config/unbound
/etc/unbound/unbound.conf /etc/unbound/unbound.conf
/etc/unbound/unbound_ext.conf /etc/unbound/unbound_ext.conf
/etc/unbound/unbound_srv.conf /etc/unbound/unbound_srv.conf
endef endef
Package/unbound-daemon-heavy/conffiles = $(Package/unbound-daemon/conffiles)
define Build/InstallDev define Build/InstallDev
$(INSTALL_DIR) $(1)/usr/include
$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/include/unbound.h $(1)/usr/include/
$(INSTALL_DIR) $(1)/usr/lib $(INSTALL_DIR) $(1)/usr/lib
$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/lib/libunbound.{so*,a,la} $(1)/usr/lib/ $(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/lib/libunbound.{so*,a,la} $(1)/usr/lib/
$(INSTALL_DIR) $(1)/usr/include
$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/include/unbound.h $(1)/usr/include/
ifeq ($(BUILD_VARIANT),heavy)
$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/include/unbound-event.h $(1)/usr/include/
endif
endef endef
define Package/unbound/install define Package/unbound-daemon/install
$(INSTALL_DIR) $(1)/usr/sbin $(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) \ $(INSTALL_BIN) \
$(PKG_INSTALL_DIR)/usr/sbin/unbound \ $(PKG_INSTALL_DIR)/usr/sbin/unbound $(1)/usr/sbin/
$(PKG_INSTALL_DIR)/usr/sbin/unbound-checkconf \
$(1)/usr/sbin/
$(INSTALL_DIR) $(1)/etc/unbound $(INSTALL_DIR) $(1)/etc/unbound
$(INSTALL_DATA) \ $(INSTALL_DATA) \
$(PKG_INSTALL_DIR)/etc/unbound/unbound.conf \ $(PKG_INSTALL_DIR)/var/lib/unbound/unbound.conf \
$(1)/etc/unbound/unbound.conf $(1)/etc/unbound/unbound.conf
$(INSTALL_DATA) ./files/root.key $(1)/etc/unbound/root.key $(INSTALL_DATA) ./files/root.key $(1)/etc/unbound/root.key
$(INSTALL_DATA) ./files/unbound_ext.conf $(1)/etc/unbound/unbound_ext.conf $(INSTALL_DATA) ./files/unbound_ext.conf $(1)/etc/unbound/unbound_ext.conf
@ -159,15 +213,29 @@ define Package/unbound/install
$(INSTALL_DATA) ./files/iptools.sh $(1)/usr/lib/unbound/iptools.sh $(INSTALL_DATA) ./files/iptools.sh $(1)/usr/lib/unbound/iptools.sh
$(INSTALL_BIN) ./files/odhcpd.sh $(1)/usr/lib/unbound/odhcpd.sh $(INSTALL_BIN) ./files/odhcpd.sh $(1)/usr/lib/unbound/odhcpd.sh
$(INSTALL_DATA) ./files/odhcpd.awk $(1)/usr/lib/unbound/odhcpd.awk $(INSTALL_DATA) ./files/odhcpd.awk $(1)/usr/lib/unbound/odhcpd.awk
$(INSTALL_DATA) ./files/rootzone.sh $(1)/usr/lib/unbound/rootzone.sh $(INSTALL_DATA) ./files/stopping.sh $(1)/usr/lib/unbound/stopping.sh
$(INSTALL_DATA) ./files/unbound.sh $(1)/usr/lib/unbound/unbound.sh $(INSTALL_DATA) ./files/unbound.sh $(1)/usr/lib/unbound/unbound.sh
endef endef
Package/unbound-daemon-heavy/install = $(Package/unbound-daemon/install)
define Package/libunbound/install
$(INSTALL_DIR) $(1)/usr/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/libunbound.so.* $(1)/usr/lib/
endef
Package/libunbound-heavy/install = $(Package/libunbound/install)
define Package/unbound-anchor/install define Package/unbound-anchor/install
$(INSTALL_DIR) $(1)/usr/sbin $(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/unbound-anchor $(1)/usr/sbin/ $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/unbound-anchor $(1)/usr/sbin/
endef endef
define Package/unbound-checkconf/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/unbound-checkconf $(1)/usr/sbin/
endef
define Package/unbound-control/install define Package/unbound-control/install
$(INSTALL_DIR) $(1)/usr/sbin $(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/unbound-control $(1)/usr/sbin/ $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/unbound-control $(1)/usr/sbin/
@ -183,15 +251,13 @@ define Package/unbound-host/install
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/unbound-host $(1)/usr/sbin/ $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/unbound-host $(1)/usr/sbin/
endef endef
define Package/libunbound/install $(eval $(call BuildPackage,unbound-daemon))
$(INSTALL_DIR) $(1)/usr/lib $(eval $(call BuildPackage,unbound-daemon-heavy))
$(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/lib/libunbound.so.* $(1)/usr/lib/ $(eval $(call BuildPackage,libunbound))
endef $(eval $(call BuildPackage,libunbound-heavy))
$(eval $(call BuildPackage,unbound))
$(eval $(call BuildPackage,unbound-anchor)) $(eval $(call BuildPackage,unbound-anchor))
$(eval $(call BuildPackage,unbound-checkconf))
$(eval $(call BuildPackage,unbound-control)) $(eval $(call BuildPackage,unbound-control))
$(eval $(call BuildPackage,unbound-control-setup)) $(eval $(call BuildPackage,unbound-control-setup))
$(eval $(call BuildPackage,unbound-host)) $(eval $(call BuildPackage,unbound-host))
$(eval $(call BuildPackage,libunbound))

View File

@ -6,7 +6,7 @@
## Package Overview ## Package Overview
OpenWrt default build uses [dnsmasq](http://www.thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html) for DNS forwarding and DHCP. With a forward only resolver, dependence on the upstream recursors may be cause for concern. They are often provided by the ISP, and some users have switched to public DNS providers. Either way may result in problems due to performance, "snoop-vertising", hijacking (MiM), and other causes. Running a recursive resolver or resolver capable of TLS may be a solution. OpenWrt default build uses [dnsmasq](http://www.thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html) for DNS forwarding and DHCP. With a forward only resolver, dependence on the upstream recursors may be cause for concern. They are often provided by the ISP, and some users have switched to public DNS providers. Either way may result in problems due to performance, "snoop-vertising", hijacking (MiM), and other causes. Running a recursive resolver or resolver capable of TLS may be a solution.
Unbound may be useful on consumer grade embedded hardware. It is fully DNSSEC and TLS capable. It is _intended_ to be a recursive resolver only. [NLnet Labs NSD](https://www.nlnetlabs.nl/projects/nsd/) is _intended_ for the authoritative task. This is different than [ISC Bind](https://www.isc.org/downloads/bind/) and its inclusive functions. Unbound configuration effort and memory consumption may be easier to control. A consumer could have their own recursive resolver with 8/64 MB router, and remove potential issues from forwarding resolvers outside of their control. Unbound may be useful on consumer grade embedded hardware. It is fully DNSSEC and TLS capable. It is _intended_ to be a recursive resolver only. NLnet Labs [NSD](https://www.nlnetlabs.nl/projects/nsd/) is _intended_ for the authoritative task. This is different than [ISC Bind](https://www.isc.org/downloads/bind/) and its inclusive functions. Unbound configuration effort and memory consumption may be easier to control. A consumer could have their own recursive resolver with 8/64 MB router, and remove potential issues from forwarding resolvers outside of their control.
This package builds on Unbounds capabilities with OpenWrt UCI. Not every Unbound option is in UCI, but rather, UCI simplifies the combination of related options. Unbounds native options are bundled and balanced within a smaller set of choices. Options include resources, DNSSEC, access control, and some TTL tweaking. The UCI also provides an escape option and works at the raw "unbound.conf" level. This package builds on Unbounds capabilities with OpenWrt UCI. Not every Unbound option is in UCI, but rather, UCI simplifies the combination of related options. Unbounds native options are bundled and balanced within a smaller set of choices. Options include resources, DNSSEC, access control, and some TTL tweaking. The UCI also provides an escape option and works at the raw "unbound.conf" level.
@ -18,21 +18,21 @@ A few tweaks may be needed to enhance the realiability and effectiveness. Ad Blo
**/etc/config/firewall**: **/etc/config/firewall**:
``` ```
config rule config rule
option name 'Block-Public-DNS' option name 'Block-Public-DNS'
option enabled '1' option enabled '1'
option src 'lan' option src 'lan'
option dest 'wan' option dest 'wan'
option dest_port '53 853 5353' option dest_port '53 853 5353'
option proto 'tcpudp' option proto 'tcpudp'
option family 'any' option family 'any'
option target 'REJECT' option target 'REJECT'
``` ```
## HOW TO: Integrate with DHCP ## HOW TO: Integrate with DHCP
Some UCI options and scripts help Unbound to work with DHCP servers to load the local DNS. The examples provided here are serial dnsmasq-unbound, parallel dnsmasq-unbound, and unbound scripted with odhcpd. Some UCI options and scripts help Unbound to work with DHCP servers to load the local DNS. The examples provided here are serial dnsmasq-unbound, parallel dnsmasq-unbound, and unbound scripted with odhcpd.
### Serial dnsmasq ### Serial dnsmasq
In this case, dnsmasq is not changed *much* with respect to the default [OpenWrt configuration](https://openwrt.org/docs/guide-user/base-system/dns_configuration). Here dnsmasq is forced to use the local Unbound instance as the lone upstream DNS server, instead of your ISP. This may be the easiest implementation, but performance degradation can occur in high volume networks. dnsmasq and Unbound effectively have the same information in memory, and all transfers are double handled. In this case, dnsmasq is not changed *much* with respect to the default [OpenWrt](https://openwrt.org/docs/guide-user/base-system/dns_configuration) configuration. Here dnsmasq is forced to use the local Unbound instance as the lone upstream DNS server, instead of your ISP. This may be the easiest implementation, but performance degradation can occur in high volume networks. Unbound and dnsmasq effectively have the same information in memory, and all transfers are double handled.
**/etc/config/unbound**: **/etc/config/unbound**:
``` ```
@ -148,26 +148,44 @@ config unbound
### Hybrid Manual/UCI ### Hybrid Manual/UCI
You like the UCI. Yet, you need to add some difficult to standardize options, or just are not ready to make a UCI request yet. The files `/etc/unbound/unbound_srv.conf` and `/etc/unbound/unbound_ext.conf` will be copied to Unbounds chroot directory and included during auto generation. You like the UCI. Yet, you need to add some difficult to standardize options, or just are not ready to make a UCI request yet. The files `/etc/unbound/unbound_srv.conf` and `/etc/unbound/unbound_ext.conf` will be copied to Unbounds chroot directory and included during auto generation.
The former will be added to the end of the `server:` clause. The later will be added to the end of the file for extended `forward:` and `view:` clauses. You can also disable unbound-control in the UCI which only allows "localhost" connections unencrypted, and then add an encrypted remote `control:` clause. The file `unbound_srv.conf` will be added into the `server:` clause. The file `unbound_ext.conf` will be added to the end of all configuration. It is for extended `forward-zone:`, `stub-zone:`, `auth-zone:`, and `view:` clauses. You can also disable unbound-control in the UCI which only allows "localhost" connections unencrypted, and then add an encrypted remote `control:` clause.
#### DNS over TLS ## HOW TO: Cache Zone Files
Some public servers are now offering DNS over TLS. Unbound supports acting as DNS over TLS forwarding client. You can use the override files to enable this funciton. Unbound will connect TLS without verifying keys unless you install `ca-bundle` package. Do **not** however forget to maintain the certification bundle. No connection or connection without verification will occur unless you use complete syntax with "@" and "#". See [Cloudflare](https://www.cloudflare.com/) DNS [1.1.1.1](https://1.1.1.1/) for example. Unbound makes a new TLS connection for each query. You limit this effect using large resource and aggressive recursion setting (big cache and prefetching). You can also set memory and recursion to default and edit `unbound_srv.conf` to suit your needs. UCI improvements are in progress but not ready in OpenWrt 18.06. Unbound has the ability to AXFR a whole zone from an authoritative server to prefetch the zone. This can speed up access to common zones. Some may have special bandwidth concerns for DNSSEC overhead. The following is a generic example. UCI defaults include the [root](https://www.internic.net/domain/) zone, but it is disabled as a ready to go example.
**/etc/unbound/unbound_srv.conf**: **/etc/config/unbound**:
``` ```
tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt config zone
option enabled '1'
option fallback '1'
option url_dir 'https://asset-management.it.example.com/zones/'
option zone_type 'auth_zone'
list server 'ns1.it.example.com'
list server 'ns2.it.example.com'
list zone_name 'example.com'
``` ```
**/etc/unbound/unbound_ext.conf**: ## HOW TO: TLS Over DNS
Unbound can use TLS as a client or server. UCI supports Unbound as a forwarding client with TLS. Servers are more complex and need manual configuration. This may be desired for privacy against stealth tracking. Some public DNS servers seem to advertise help in this quest. If your looking for a better understanding, then some information can be found at [Cloudflare](https://www.cloudflare.com/) DNS [1.1.1.1](https://1.1.1.1/). The following is a generic example. You can mix providers by using complete server specificaiton to override the zones common port and certificate domain index.
Update as of Unbound 1.9.1, all TLS functions work correctly with either OpenSSL 1.0.2 or 1.1.0. Please be sure to install `ca-bundle` package and use `opkg` to get updates regularly.
**/etc/config/unbound**:
``` ```
forward-zone: config zone
# example for Cloudflare about July 2018 option enabled '1'
name: . # question: do you want to recurse when TLS fails or not?
forward-addr: 1.1.1.1@853#cloudflare-dns.com option fallback '0'
forward-addr: 1.0.0.1@853#cloudflare-dns.com option tls_index 'dns.example.net'
forward-addr: 2606:4700:4700::1111@853#cloudflare-dns.com option tls_port '853'
forward-addr: 2606:4700:4700::1001@853#cloudflare-dns.com option tls_upstream '1'
forward-tls-upstream: yes option zone_type 'forward_zone'
# these servers assume a common TLS port/index
list server '192.0.2.53'
list server '2001:db8::53'
# this alternate server is fully specified inline
list server '192.0.2.153@443#dns.alternate.example.org'
list zone_name '.'
``` ```
## Complete List of UCI Options ## Complete List of UCI Options
@ -194,18 +212,17 @@ config unbound
4 - Above and interfaces named <iface>.<hostname>.<domain> 4 - Above and interfaces named <iface>.<hostname>.<domain>
option add_wan_fqdn '0' option add_wan_fqdn '0'
Level. Same as previous option only this applies to the WAN. WAN Level. Same as previous option only this applies to the WAN. WAN are
are inferred by a UCI `config dhcp` entry that contains the line inferred by a UCI `config dhcp` entry that contains the 'option ignore 1'.
option ignore '1'.
option dns64 '0' option dns64 '0'
Boolean. Enable DNS64 through Unbound in order to bridge networks Boolean. Enable DNS64 through Unbound in order to bridge networks that are
that are IPV6 only and IPV4 only (see RFC6052). IPV6 only and IPV4 only (see RFC6052).
option dns64_prefix '64:ff9b::/96' option dns64_prefix '64:ff9b::/96'
IPV6 Prefix. The IPV6 prefix wrapped on the IPV4 address for DNS64. IPV6 Prefix. The IPV6 prefix wrapped on the IPV4 address for DNS64. You
You should use RFC6052 "well known" address, unless you also should use RFC6052 "well known" address, unless you also redirect to a proxy
redirect to a proxy or gateway for your NAT64. or gateway for your NAT64.
option dhcp_link 'none' option dhcp_link 'none'
Program Name. Link to one of the supported programs we have scripts Program Name. Link to one of the supported programs we have scripts
@ -224,16 +241,16 @@ config unbound
option domain_type 'static' option domain_type 'static'
Unbound local-zone: <domain> <type>. This allows you to lock Unbound local-zone: <domain> <type>. This allows you to lock
down or allow forwarding of your domain, your router host name down or allow forwarding of the local zone. Notable types:
without suffix, and leakage of RFC6762 "local." static - typical single router setup much like OpenWrt dnsmasq default
refuse - to answer overtly with DNS code REFUSED
deny - to drop queries for the local zone
transparent - to use your manually added forward-zone: or stub-zone: clause
option edns_size '1280' option edns_size '1280'
Bytes. Extended DNS is necessary for DNSSEC. However, it can run Bytes. Extended DNS is necessary for DNSSEC. However, it can run
into MTU issues. Use this size in bytes to manage drop outs. into MTU issues. Use this size in bytes to manage drop outs.
option extended_luci '0'
Boolean. Extends a tab hierarchy in LuCI for advanced configuration.
option extended_stats '0' option extended_stats '0'
Boolean. extended statistics are printed from unbound-control. Boolean. extended statistics are printed from unbound-control.
Keeping track of more statistics takes time. Keeping track of more statistics takes time.
@ -253,28 +270,34 @@ config unbound
Boolean. Skip all this UCI nonsense. Manually edit the Boolean. Skip all this UCI nonsense. Manually edit the
configuration. Make changes to /etc/unbound/unbound.conf. configuration. Make changes to /etc/unbound/unbound.conf.
option num_threads '1'
Count. Enable multithreading with the "heavy traffic" variant. Base variant
spins each as whole proces and is not efficient. Two threads may be used,
but they use one shared cache slab. More edges into an industrial setup,
and UCI simplificaitons may not be appropriate.
option protocol 'mixed' option protocol 'mixed'
Unbound can limit its protocol used for recursive queries. Unbound can limit its protocol used for recursive queries.
ip4_only - limit issues if you do not have native IPv6 ip4_only - old fashioned IPv4 upstream and downstream
ip6_only - test environment only; could cauase problems ip6_only - test environment only; could cauase problems
ip6_local - upstream IPv4 only and local network IPv4 and IPv6
ip6_prefer - both IPv4 and IPv6 but try IPv6 first ip6_prefer - both IPv4 and IPv6 but try IPv6 first
mixed - both IPv4 and IPv6 mixed - both IPv4 and IPv6
default - Unbound built-in defaults default - Unbound built-in defaults
option query_minimize '0' option query_minimize '0'
Boolean. Enable a minor privacy option. Don't let each server know Boolean. Enable a minor privacy option. Don't let each server know the next
the next recursion. Query one piece at a time. recursion. Query one piece at a time.
option query_min_strict '0' option query_min_strict '0'
Boolean. Query minimize is best effort and will fall back to normal Boolean. Query minimize is best effort and will fall back to normal when it
when it must. This option prevents the fall back, but less than must. This option prevents the fall back, but less than standard name
standard name servers will fail to resolve their domains. servers will fail to resolve their domains.
option rebind_localhost '0' option rebind_localhost '0'
Boolean. Prevent loopback "127.0.0.0/8" or "::1/128" responses. Boolean. Prevent loopback "127.0.0.0/8" or "::1/128" responses. These may
These may used by black hole servers for good purposes like used by black hole servers for good purposes like ad-blocking or parental
ad-blocking or parental access control. Obviously these responses access control. Obviously these responses may be used to for bad purposes.
also can be used to for bad purposes.
option rebind_protection '1' option rebind_protection '1'
Level. Block your local address responses from global DNS. A poisoned Level. Block your local address responses from global DNS. A poisoned
@ -300,16 +323,16 @@ config unbound
large - about double of medium large - about double of medium
option root_age '9' option root_age '9'
Days. >90 Disables. Age limit for Unbound root data like root Days. >90 Disables. Age limit for Unbound root data like root DNSSEC key.
DNSSEC key. Unbound uses RFC 5011 to manage root key. This could Unbound uses RFC 5011 to manage root key. This could harm flash ROM. This
harm flash ROM. This activity is mapped to "tmpfs," but every so activity is mapped to "tmpfs," but every so often it needs to be copied back
often it needs to be copied back to flash for the next reboot. to flash for the next reboot.
option ttl_min '120' option ttl_min '120'
Seconds. Minimum TTL in cache. Recursion can be expensive without Seconds. Minimum TTL in cache. Recursion can be expensive without cache. A
cache. A low TTL is normal for server migration. A low TTL can be low TTL is normal for server migration. A low TTL can be abused for snoop-
abused for snoop-vertising (DNS hit counts; recording query IP). vertising (DNS hit counts; recording query IP). Typical to configure maybe
Typical to configure maybe 0~300, but 1800 is the maximum accepted. 0~300, but 1800 is the maximum accepted.
option unbound_control '0' option unbound_control '0'
Level. Enables unbound-control application access ports. Level. Enables unbound-control application access ports.
@ -323,28 +346,87 @@ config unbound
Boolean. Enable DNSSEC. Unbound names this the "validator" module. Boolean. Enable DNSSEC. Unbound names this the "validator" module.
option validator_ntp '1' option validator_ntp '1'
Boolean. Disable DNSSEC time checks at boot. Once NTP confirms Boolean. Disable DNSSEC time checks at boot. Once NTP confirms global real
global real time, then DNSSEC is restarted at full strength. Many time, then DNSSEC is restarted at full strength. Many embedded devices don't
embedded devices don't have a real time power off clock. NTP needs have a real time power off clock. NTP needs DNS to resolve servers. This
DNS to resolve servers. This works around the chicken-and-egg. works around the chicken-and-egg.
list domain_forward 'mail.my-isp.com' option verbosity '1'
Domain. Do not recurse, but rather forward the domains to given DNS Level. Sets Unbounds logging intensity.
servers found in resolve.conf.auto from WAN DHCP client. This may
provide better access to mirror servers in 'your neigborhood.' This
may be useful in keeping local organization lookups on local subnets.
list domain_insecure 'ntp.somewhere.org' list domain_insecure 'ntp.somewhere.org'
Domain. Domains that you wish to skip DNSSEC. It is one way around NTP Domain. Domains that you wish to skip DNSSEC. It is one way around NTP
chicken and egg. Your DHCP servered domains are automatically included. chicken and egg. Your DHCP servered domains are automatically included.
list rebind_interface 'lan'
Interface (logical). Works with 'rebind_protection' options 2 and 3.
list trigger_interface 'lan' 'wan' list trigger_interface 'lan' 'wan'
Interface (logical). This option is a work around for netifd/procd Interface (logical). This option is a work around for netifd/procd
interaction with WAN DHCPv6. Minor RA or DHCP changes in IP6 can interaction with WAN DHCPv6. Minor RA or DHCP changes in IP6 can cause
cause netifd to execute procd interface reload. Limit Unbound procd netifd to execute procd interface reload. Limit Unbound procd triggers to
triggers to LAN and WAN (IP4 only) to prevent restart @2-3 minutes. LAN and WAN (IP4 only) to prevent restart @2-3 minutes.
config zone
Create Unbounds forward-zone:, stub-zone:, or auth-zone: clauses
option enabled 1
Boolean. Enable the zone clause.
option fallback 1
Boolean. Permit normal recursion when the narrowly selected servers in this
zone are unresponsive or return empty responses. Disable, if there are
security concerns (forward only internal to organization).
option port 53
Port. Servers are contact on this port for plain DNS operations.
option resolv_conf 0
Boolean. Use "resolv.conf" as it was filled by the DHCP client. This can be
used to forward zones within your ISP (mail.example.net) or that have co-
located services (streamed-movies.example.com). Recursion may not yield the
most local result, but forwarding may instead.
option tls_index (n/a)
Domain. Name TLS certificates are signed for (dns.example.net). If this
option is ommitted, then Unbound will make connections but not validate.
option tls_port 853
Port. Servers are contact on this port for DNS over TLS operations.
option tls_upstream 0
Boolean. Use TLS to contact the zone server.
option url_dir
String. http or https path, directory part only, to the zone file for
auth_zone type only. Files "${zone_name}.zone" are expect in this path.
option zone_type (n/a)
State. Required field or the clause is effectively disabled. Check Unbound
documentation for clarity (unbound-conf).
auth_zone - prefetch whole zones from authoritative server (ICANN)
forward_zone - forward queries in these domains to the listed servers
stub_zone - force recursion of these domains to the listed servers
list server (n/a)
IP. Every zone must have one server. Stub and forward require IP to prevent
chicken and egg (due to UCI simplicity). Authoritative prefetch may use a
server name.
list zone_name
Domain. Every zone must represent some part of the DNS tree. It can be all
of it "." or you internal organization domain "example.com." Within each
zone clause all zone names will be matched to all servers.
``` ```
## Replaced Options
config unbound / option prefetch_root
List the domains in a zone with type auth_zone and fill in the server or url
fields. Root zones are ready but disabled in default install UCI.
config unbound / list domain_forward
List the domains in a zone with type forward_zone and enable the
resolv_conf option.
config unbound / list rebind_interface
Enable rebind_protection at 2 and all DHCP interfaces are also protected for
IPV6 GLA (parallel to subnets in add_local_fqdn).

View File

@ -14,30 +14,53 @@
# #
############################################################################## ##############################################################################
UNBOUND_LIBDIR=/usr/lib/unbound # where are we?
UNBOUND_VARDIR=/var/lib/unbound UB_LIBDIR=/usr/lib/unbound
UB_VARDIR=/var/lib/unbound
UB_PIDFILE=/var/run/unbound.pid
UNBOUND_PIDFILE=/var/run/unbound.pid # conf deconstructed
UB_TOTAL_CONF=$UB_VARDIR/unbound.conf
UB_CORE_CONF=$UB_VARDIR/server.conf.tmp
UB_HOST_CONF=$UB_VARDIR/host.conf.tmp
UB_DHCP_CONF=$UB_VARDIR/dhcp.conf
UB_ZONE_CONF=$UB_VARDIR/zone.conf.tmp
UB_CTRL_CONF=$UB_VARDIR/ctrl.conf.tmp
UB_SRVMASQ_CONF=$UB_VARDIR/dnsmasq_srv.conf.tmp
UB_EXTMASQ_CONF=$UB_VARDIR/dnsmasq_ext.conf.tmp
UB_SRV_CONF=$UB_VARDIR/unbound_srv.conf
UB_EXT_CONF=$UB_VARDIR/unbound_ext.conf
UNBOUND_SRV_CONF=$UNBOUND_VARDIR/unbound_srv.conf # TLS keys
UNBOUND_EXT_CONF=$UNBOUND_VARDIR/unbound_ext.conf UB_TLS_KEY_FILE="TLS server UCI not implemented"
UNBOUND_DHCP_CONF=$UNBOUND_VARDIR/unbound_dhcp.conf UB_TLS_PEM_FILE="TLS server UCI not implemented"
UNBOUND_CONFFILE=$UNBOUND_VARDIR/unbound.conf UB_TLS_FWD_FILE=$UB_VARDIR/ca-certificates.crt
UB_TLS_ETC_FILE=/etc/ssl/certs/ca-certificates.crt
UNBOUND_KEYFILE=$UNBOUND_VARDIR/root.key # start files
UNBOUND_HINTFILE=$UNBOUND_VARDIR/root.hints UB_RKEY_FILE=$UB_VARDIR/root.key
UNBOUND_TIMEFILE=$UNBOUND_VARDIR/hotplug.time UB_RHINT_FILE=$UB_VARDIR/root.hints
UB_TIME_FILE=$UB_VARDIR/hotplug.time
UB_SKIP_FILE=$UB_VARDIR/skip.time
UNBOUND_CTLKEY_FILE=$UNBOUND_VARDIR/unbound_control.key # control app keys
UNBOUND_CTLPEM_FILE=$UNBOUND_VARDIR/unbound_control.pem UB_CTLKEY_FILE=$UB_VARDIR/unbound_control.key
UNBOUND_SRVKEY_FILE=$UNBOUND_VARDIR/unbound_server.key UB_CTLPEM_FILE=$UB_VARDIR/unbound_control.pem
UNBOUND_SRVPEM_FILE=$UNBOUND_VARDIR/unbound_server.pem UB_SRVKEY_FILE=$UB_VARDIR/unbound_server.key
UB_SRVPEM_FILE=$UB_VARDIR/unbound_server.pem
##############################################################################
# similar default SOA / NS RR as Unbound uses for private ARPA zones
UNBOUND_ANCHOR=/usr/sbin/unbound-anchor UB_XSER=$(( $( date +%s ) / 60 ))
UNBOUND_CONTROL=/usr/sbin/unbound-control UB_XSOA="7200 IN SOA localhost. nobody.invalid. $UB_XSER 3600 1200 9600 300"
UNBOUND_CONTROL_CFG="$UNBOUND_CONTROL -c $UNBOUND_CONFFILE" UB_XNS="7200 IN NS localhost."
UB_XTXT="7200 IN TXT \"comment=local intranet dns zone\""
UB_MTXT="7200 IN TXT \"comment=masked internet dns zone\""
UB_LTXT="7200 IN TXT \"comment=rfc6762 multicast dns zone\""
# helper apps
UB_ANCHOR=/usr/sbin/unbound-anchor
UB_CONTROL=/usr/sbin/unbound-control
UB_CONTROL_CFG="$UB_CONTROL -c $UB_TOTAL_CONF"
############################################################################## ##############################################################################

View File

@ -23,6 +23,140 @@
# #
############################################################################## ##############################################################################
DM_D_WAN_FQDN=0
DM_LIST_KNOWN_ZONES="invalid"
DM_LIST_TRN_ZONES=""
DM_LIST_LOCAL_DATA=""
DM_LIST_LOCAL_PTR=""
DM_LIST_FWD_PORTS=""
DM_LIST_FWD_ZONES=""
##############################################################################
create_local_zone() {
local target="$1"
local partial domain found
case $DM_LIST_TRN_ZONES in
*"${target}"*)
found=1
;;
*)
case $target in
[A-Za-z0-9]*.[A-Za-z0-9]*)
found=0
;;
*) # no dots
found=1
;;
esac
esac
if [ $found -eq 0 ] ; then
# New Zone! Bundle local-zones: by first two name tiers "abcd.tld."
partial=$( echo "$target" | awk -F. '{ j=NF ; i=j-1; print $i"."$j }' )
DM_LIST_TRN_ZONES="$DM_LIST_TRN_ZONES $partial"
DM_LIST_KNOWN_ZONES="$DM_LIST_KNOWN_ZONES $partial"
fi
}
##############################################################################
create_host_record() {
local cfg="$1"
local ip name debug_ip
# basefiles dhcp "domain" clause which means host A, AAAA, and PRT record
config_get ip "$cfg" ip
config_get name "$cfg" name
if [ -n "$name" ] && [ -n "$ip" ] ; then
create_local_zone "$name"
case $ip in
fe[89ab][0-9a-f]:*|169.254.*)
debug_ip="$ip@$name"
;;
[1-9a-f]*:*[0-9a-f])
DM_LIST_LOCAL_DATA="$DM_LIST_LOCAL_DATA $name.@@300@@IN@@AAAA@@$ip"
DM_LIST_LOCAL_PTR="$DM_LIST_LOCAL_PTR $ip@@300@@$name"
;;
[1-9]*.*[0-9])
DM_LIST_LOCAL_DATA="$DM_LIST_LOCAL_DATA $name.@@300@@IN@@A@@$ip"
DM_LIST_LOCAL_PTR="$DM_LIST_LOCAL_PTR $ip@@300@@$name"
;;
esac
fi
}
##############################################################################
create_mx_record() {
local cfg="$1"
local domain relay pref record
# Insert a static MX record
config_get domain "$cfg" domain
config_get relay "$cfg" relay
config_get pref "$cfg" pref 10
if [ -n "$domain" ] && [ -n "$relay" ] ; then
create_local_zone "$domain"
record="$domain.@@300@@IN@@MX@@$pref@@$relay."
DM_LIST_LOCAL_DATA="$DM_LIST_LOCAL_DATA $record"
fi
}
##############################################################################
create_srv_record() {
local cfg="$1"
local srv target port class weight record
# Insert a static SRV record such as SIP server
config_get srv "$cfg" srv
config_get target "$cfg" target
config_get port "$cfg" port
config_get class "$cfg" class 10
config_get weight "$cfg" weight 10
if [ -n "$srv" ] && [ -n "$target" ] && [ -n "$port" ] ; then
create_local_zone "$srv"
record="$srv.@@300@@IN@@SRV@@$class@@$weight@@$port@@$target."
DM_LIST_LOCAL_DATA="$DM_LIST_LOCAL_DATA $record"
fi
}
##############################################################################
create_cname_record() {
local cfg="$1"
local cname target record
# Insert static CNAME record
config_get cname "$cfg" cname
config_get target "$cfg" target
if [ -n "$cname" ] && [ -n "$target" ] ; then
create_local_zone "$cname"
record="$cname.@@300@@IN@@CNAME@@$target."
DM_LIST_LOCAL_DATA="$DM_LIST_LOCAL_DATA $record"
fi
}
##############################################################################
dnsmasq_local_zone() { dnsmasq_local_zone() {
local cfg="$1" local cfg="$1"
local fwd_port fwd_domain wan_fqdn local fwd_port fwd_domain wan_fqdn
@ -34,130 +168,129 @@ dnsmasq_local_zone() {
if [ -n "$wan_fqdn" ] ; then if [ -n "$wan_fqdn" ] ; then
UNBOUND_D_WAN_FQDN=$wan_fqdn DM_D_WAN_FQDN=$wan_fqdn
fi fi
if [ -n "$fwd_domain" -a -n "$fwd_port" -a ! "${fwd_port:-53}" -eq 53 ] ; then
# dnsmasq localhost listening ports (possible multiple instances)
UNBOUND_N_FWD_PORTS="$UNBOUND_N_FWD_PORTS $fwd_port"
UNBOUND_TXT_FWD_ZONE="$UNBOUND_TXT_FWD_ZONE $fwd_domain"
{ if [ -n "$fwd_domain" ] && [ -n "$fwd_port" ] \
# This creates DOMAIN local privledges && [ ! "${fwd_port:-53}" -eq 53 ] ; then
echo " private-domain: \"$fwd_domain\"" # dnsmasq localhost listening ports (possible multiple instances)
echo " local-zone: \"$fwd_domain.\" transparent" DM_LIST_FWD_PORTS="$DM_LIST_FWD_PORTS $fwd_port"
echo " domain-insecure: \"$fwd_domain\"" DM_LIST_FWD_ZONES="$DM_LIST_FWD_ZONES $fwd_domain"
echo
} >> $UNBOUND_CONFFILE
fi fi
} }
############################################################################## ##############################################################################
dnsmasq_local_arpa() { dnsmasq_local_arpa() {
local cfg="$1" local ifarpa ifsubnet
local logint dhcpv4 dhcpv6 ignore
local subnets subnets4 subnets6
local forward arpa
local validip4 validip6 privateip
config_get logint "$cfg" interface
config_get dhcpv4 "$cfg" dhcpv4
config_get dhcpv6 "$cfg" dhcpv6
config_get_bool ignore "$cfg" ignore 0
# Find the list of addresses assigned to a logical interface
# Its typical to have a logical gateway split NAME and NAME6
network_get_subnets subnets4 "$logint"
network_get_subnets6 subnets6 "$logint"
subnets="$subnets4 $subnets6"
network_get_subnets subnets4 "${logint}6"
network_get_subnets6 subnets6 "${logint}6"
subnets="$subnets $subnets4 $subnets6"
if [ -z "$subnets" ] ; then if [ -n "$UB_LIST_NETW_LAN" ] ; then
forward="" for ifsubnet in $UB_LIST_NETW_LAN ; do
ifarpa=$( domain_ptr_any "${ifsubnet#*@}" )
elif [ -z "$UNBOUND_N_FWD_PORTS" ] ; then DM_LIST_FWD_ZONES="$DM_LIST_FWD_ZONES $ifarpa"
forward="" done
elif [ "$ignore" -gt 0 ] ; then
if [ "$UNBOUND_D_WAN_FQDN" -gt 0 ] ; then
# Only forward the one gateway host.
forward="host"
else
forward=""
fi
else
# Forward the entire private subnet.
forward="domain"
fi fi
if [ -n "$forward" ] ; then if [ -n "$UB_LIST_NETW_WAN" ] && [ "$DM_D_WAN_FQDN" -gt 0 ] ; then
for subnet in $subnets ; do for ifsubnet in $UB_LIST_NETW_WAN ; do
validip4=$( valid_subnet4 $subnet ) ifarpa=$( domain_ptr_any "${ifsubnet#*@}" )
validip6=$( valid_subnet6 $subnet ) DM_LIST_FWD_ZONES="$DM_LIST_FWD_ZONES $ifarpa"
privateip=$( private_subnet $subnet )
if [ "$validip4" = "ok" -a "$dhcpv4" != "disable" ] ; then
if [ "$forward" = "domain" ] ; then
arpa=$( domain_ptr_ip4 "$subnet" )
else
arpa=$( host_ptr_ip4 "$subnet" )
fi
elif [ "$validip6" = "ok" -a "$dhcpv6" != "disable" ] ; then
if [ "$forward" = "domain" ] ; then
arpa=$( domain_ptr_ip6 "$subnet" )
else
arpa=$( host_ptr_ip6 "$subnet" )
fi
else
arpa=""
fi
if [ -n "$arpa" ] ; then
if [ "$privateip" = "ok" ] ; then
{
# This creates ARPA local zone privledges
echo " local-zone: \"$arpa.\" transparent"
echo " domain-insecure: \"$arpa\""
echo
} >> $UNBOUND_CONFFILE
fi
UNBOUND_TXT_FWD_ZONE="$UNBOUND_TXT_FWD_ZONE $arpa"
fi
done done
fi fi
} }
############################################################################## ##############################################################################
dnsmasq_forward_zone() { dnsmasq_inactive() {
if [ -n "$UNBOUND_N_FWD_PORTS" -a -n "$UNBOUND_TXT_FWD_ZONE" ] ; then local record
for fwd_domain in $UNBOUND_TXT_FWD_ZONE ; do
{
# This is derived of dnsmasq_local_zone/arpa
# but forward: clauses need to be seperate
echo "forward-zone:"
echo " name: \"$fwd_domain.\""
for port in $UNBOUND_N_FWD_PORTS ; do
if [ "$UB_D_EXTRA_DNS" -gt 0 ] ; then
# Parasite from the uci.dhcp.domain clauses
DM_LIST_KNOWN_ZONES="$DM_LIST_KNOWN_ZONES $UB_TXT_DOMAIN"
config_load dhcp
config_foreach create_host_record domain
if [ "$UB_D_EXTRA_DNS" -gt 1 ] ; then
config_foreach create_srv_record srvhost
config_foreach create_mx_record mxhost
fi
if [ "$UB_D_EXTRA_DNS" -gt 2 ] ; then
config_foreach create_cname_record cname
fi
{
echo "# $UB_SRVMASQ_CONF generated by UCI $( date -Is )"
if [ -n "$DM_LIST_TRN_ZONES" ] ; then
for record in $DM_LIST_TRN_ZONES ; do
echo " local-zone: $record transparent"
done
echo
fi
if [ -n "$DM_LIST_LOCAL_DATA" ] ; then
for record in $DM_LIST_LOCAL_DATA ; do
echo " local-data: \"${record//@@/ }\""
done
echo
fi
if [ -n "$DM_LIST_LOCAL_PTR" ] ; then
for record in $DM_LIST_LOCAL_PTR ; do
echo " local-data-ptr: \"${record//@@/ }\""
done
echo
fi
} > $UB_SRVMASQ_CONF
fi
}
##############################################################################
dnsmasq_active() {
# Look at dnsmasq settings
config_load dhcp
# Zone for DHCP / SLAAC-PING DOMAIN
config_foreach dnsmasq_local_zone dnsmasq
# Zone for DHCP / SLAAC-PING ARPA
dnsmasq_local_arpa
if [ -n "$DM_LIST_FWD_PORTS" ] && [ -n "$DM_LIST_FWD_ZONES" ] ; then
{
# Forward to dnsmasq on same host for DHCP lease hosts
echo "# $UB_SRVMASQ_CONF generated by UCI $( date -Is )"
echo " do-not-query-localhost: no"
echo
} > $UB_SRVMASQ_CONF
echo "# $UB_EXTMASQ_CONF generated by UCI $( date -Is )" > $UB_EXTMASQ_CONF
for fwd_domain in $DM_LIST_FWD_ZONES ; do
{
# This creates a domain with local privledges
echo " domain-insecure: $fwd_domain"
echo " private-domain: $fwd_domain"
echo " local-zone: $fwd_domain transparent"
echo
} >> $UB_SRVMASQ_CONF
{
# This is derived from dnsmasq local domain and dhcp service subnets
echo "forward-zone:"
echo " name: $fwd_domain"
echo " forward-first: no"
for port in $DM_LIST_FWD_PORTS ; do
echo " forward-addr: 127.0.0.1@$port" echo " forward-addr: 127.0.0.1@$port"
done done
echo echo
} >> $UNBOUND_CONFFILE } >> $UB_EXTMASQ_CONF
done done
fi fi
} }
@ -165,16 +298,12 @@ dnsmasq_forward_zone() {
############################################################################## ##############################################################################
dnsmasq_link() { dnsmasq_link() {
# Forward to dnsmasq on same host for DHCP lease hosts if [ "$UB_D_DHCP_LINK" = "dnsmasq" ] ; then
echo " do-not-query-localhost: no" >> $UNBOUND_CONFFILE dnsmasq_active
# Look at dnsmasq settings
config_load dhcp else
# Zone for DHCP / SLAAC-PING DOMAIN dnsmasq_inactive
config_foreach dnsmasq_local_zone dnsmasq fi
# Zone for DHCP / SLAAC-PING ARPA
config_foreach dnsmasq_local_arpa dhcp
# Now create ALL seperate forward: clauses
dnsmasq_forward_zone
} }
############################################################################## ##############################################################################

View File

@ -124,6 +124,20 @@ valid_subnet4() {
############################################################################## ##############################################################################
valid_subnet_any() {
local subnet=$1
local validip4=$( valid_subnet4 $subnet )
local validip6=$( valid_subnet6 $subnet )
if [ "$validip4" = "ok" ] || [ "$validip6" = "ok" ] ; then
echo "ok"
else
echo "not"
fi
}
##############################################################################
private_subnet() { private_subnet() {
case "$1" in case "$1" in
10"."*) echo "ok" ;; 10"."*) echo "ok" ;;
@ -138,3 +152,47 @@ private_subnet() {
############################################################################## ##############################################################################
domain_ptr_any() {
local subnet=$1
local arpa validip4 validip6
validip4=$( valid_subnet4 $subnet )
validip6=$( valid_subnet6 $subnet )
if [ "$validip4" = "ok" ] ; then
arpa=$( domain_ptr_ip4 "$subnet" )
elif [ "$validip6" = "ok" ] ; then
arpa=$( domain_ptr_ip6 "$subnet" )
fi
if [ -n "$arpa" ] ; then
echo $arpa
fi
}
##############################################################################
host_ptr_any() {
local subnet=$1
local arpa validip4 validip6
validip4=$( valid_subnet4 $subnet )
validip6=$( valid_subnet6 $subnet )
if [ "$validip4" = "ok" ] ; then
arpa=$( host_ptr_ip4 "$subnet" )
elif [ "$validip6" = "ok" ] ; then
arpa=$( host_ptr_ip6 "$subnet" )
fi
if [ -n "$arpa" ] ; then
echo $arpa
fi
}
##############################################################################

View File

@ -18,11 +18,12 @@
# function from dnsmasq and use DHCPv4 MAC to find IPV6 SLAAC hosts. # function from dnsmasq and use DHCPv4 MAC to find IPV6 SLAAC hosts.
# #
# External Parameters # External Parameters
# "hostfile" = where this script will cache host DNS data # "conffile" = Unbound configuration left for a restart
# "pipefile" = DNS entries for unbound-control standard input
# "domain" = text domain suffix # "domain" = text domain suffix
# "bslaac" = boolean, use DHCPv4 MAC to find GA and ULA IPV6 SLAAC # "bslaac" = boolean, use DHCPv4 MAC to find GA and ULA IPV6 SLAAC
# "bisolt" = boolean, format <host>.<network>.<domain>. so you can isolate # "bisolt" = boolean, format <host>.<network>.<domain>. so you can isolate
# "bconf" = boolean, write conf file format rather than pipe records # "bconf" = boolean, write conf file with pipe records
# #
############################################################################## ##############################################################################
@ -35,10 +36,12 @@
sub( /.*\//, "", cdr ) ; sub( /.*\//, "", cdr ) ;
sub( /\/.*/, "", adr2 ) ; sub( /\/.*/, "", adr2 ) ;
sub( /.*\//, "", cdr2 ) ; sub( /.*\//, "", cdr2 ) ;
gsub( /_/, "-", hst ) ;
if ( hst !~ /^[[:alnum:]]([-[:alnum:]]*[[:alnum:]])?$/ ) { if ( hst !~ /^[[:alnum:]]([-[:alnum:]]*[[:alnum:]])?$/ ) {
# that is not a valid host name (RFC1123) # that is not a valid host name (RFC1123)
# above replaced common error of "_" in host name with "-"
hst = "-" ; hst = "-" ;
} }
@ -47,7 +50,7 @@
# TODO: this might be better with a substituion option, # TODO: this might be better with a substituion option,
# or per DHCP pool do-not-DNS option, but its getting busy here. # or per DHCP pool do-not-DNS option, but its getting busy here.
fqdn = net fqdn = net
fqdn = sub( /\./, "-", fqdn ) ; gsub( /\./, "-", fqdn ) ;
fqdn = tolower( hst "." fqdn "." domain ) ; fqdn = tolower( hst "." fqdn "." domain ) ;
} }
@ -64,17 +67,17 @@
if ( bconf == 1 ) { if ( bconf == 1 ) {
x = ( "local-data: \"" fqdn ". 120 IN A " adr "\"" ) ; x = ( "local-data: \"" fqdn ". 300 IN A " adr "\"" ) ;
y = ( "local-data-ptr: \"" adr " 120 " fqdn "\"" ) ; y = ( "local-data-ptr: \"" adr " 300 " fqdn "\"" ) ;
print ( x "\n" y "\n" ) > hostfile ; print ( x "\n" y "\n" ) > conffile ;
} }
else {
for( i=1; i<=4; i++ ) { qpr = ( ptr[i] "." qpr) ; } # always create the pipe file
x = ( fqdn ". 120 IN A " adr ) ; for( i=1; i<=4; i++ ) { qpr = ( ptr[i] "." qpr) ; }
y = ( qpr "in-addr.arpa. 120 IN PTR " fqdn ) ; x = ( fqdn ". 300 IN A " adr ) ;
print ( x "\n" y ) > hostfile ; y = ( qpr "in-addr.arpa. 300 IN PTR " fqdn ) ;
} print ( x "\n" y ) > pipefile ;
if (( bslaac == 1 ) && ( slaac != 0 )) { if (( bslaac == 1 ) && ( slaac != 0 )) {
@ -85,6 +88,7 @@
while ( ( cmd | getline adr ) > 0 ) { while ( ( cmd | getline adr ) > 0 ) {
if (( substr( adr, 1, 5 ) <= "fdff:" ) \ if (( substr( adr, 1, 5 ) <= "fdff:" ) \
&& ( index( adr, "::/" ) != 0 ) \
&& ( index( adr, "anycast" ) == 0 ) \ && ( index( adr, "anycast" ) == 0 ) \
&& ( index( adr, "via" ) == 0 )) { && ( index( adr, "via" ) == 0 )) {
# GA or ULA routed addresses only (not LL or MC) # GA or ULA routed addresses only (not LL or MC)
@ -98,17 +102,17 @@
if ( bconf == 1 ) { if ( bconf == 1 ) {
x = ( "local-data: \"" fqdn ". 120 IN AAAA " adr "\"" ) ; x = ( "local-data: \"" fqdn ". 300 IN AAAA " adr "\"" ) ;
y = ( "local-data-ptr: \"" adr " 120 " fqdn "\"" ) ; y = ( "local-data-ptr: \"" adr " 300 " fqdn "\"" ) ;
print ( x "\n" y "\n" ) > hostfile ; print ( x "\n" y "\n" ) > conffile ;
} }
else {
qpr = ipv6_ptr( adr ) ; # always create the pipe file
x = ( fqdn ". 120 IN AAAA " adr ) ; qpr = ipv6_ptr( adr ) ;
y = ( qpr ". 120 IN PTR " fqdn ) ; x = ( fqdn ". 300 IN AAAA " adr ) ;
print ( x "\n" y ) > hostfile ; y = ( qpr ". 300 IN PTR " fqdn ) ;
} print ( x "\n" y ) > pipefile ;
} }
} }
@ -120,34 +124,32 @@
else if ((cls != "ipv4") && (hst != "-") && (9 <= NF) && (NF <= 10)) { else if ((cls != "ipv4") && (hst != "-") && (9 <= NF) && (NF <= 10)) {
if (cdr == 128) { if (cdr == 128) {
if ( bconf == 1 ) { if ( bconf == 1 ) {
x = ( "local-data: \"" fqdn ". 120 IN AAAA " adr "\"" ) ; x = ( "local-data: \"" fqdn ". 300 IN AAAA " adr "\"" ) ;
y = ( "local-data-ptr: \"" adr " 120 " fqdn "\"" ) ; y = ( "local-data-ptr: \"" adr " 300 " fqdn "\"" ) ;
print ( x "\n" y "\n" ) > hostfile ; print ( x "\n" y "\n" ) > conffile ;
} }
else {
# only for provided hostnames and full /128 assignments # only for provided hostnames and full /128 assignments
qpr = ipv6_ptr( adr ) ; qpr = ipv6_ptr( adr ) ;
x = ( fqdn ". 120 IN AAAA " adr ) ; x = ( fqdn ". 300 IN AAAA " adr ) ;
y = ( qpr ". 120 IN PTR " fqdn ) ; y = ( qpr ". 300 IN PTR " fqdn ) ;
print ( x "\n" y ) > hostfile ; print ( x "\n" y ) > pipefile ;
}
} }
if (cdr2 == 128) { if (cdr2 == 128) {
if ( bconf == 1 ) { if ( bconf == 1 ) {
x = ( "local-data: \"" fqdn ". 120 IN AAAA " adr2 "\"" ) ; x = ( "local-data: \"" fqdn ". 300 IN AAAA " adr2 "\"" ) ;
y = ( "local-data-ptr: \"" adr2 " 120 " fqdn "\"" ) ; y = ( "local-data-ptr: \"" adr2 " 300 " fqdn "\"" ) ;
print ( x "\n" y "\n" ) > hostfile ; print ( x "\n" y "\n" ) > conffile ;
} }
else {
# odhcp puts GA and ULA on the same line (position 9 and 10) # odhcp puts GA and ULA on the same line (position 9 and 10)
qpr2 = ipv6_ptr( adr2 ) ; qpr2 = ipv6_ptr( adr2 ) ;
x = ( fqdn ". 120 IN AAAA " adr2 ) ; x = ( fqdn ". 300 IN AAAA " adr2 ) ;
y = ( qpr2 ". 120 IN PTR " fqdn ) ; y = ( qpr2 ". 300 IN PTR " fqdn ) ;
print ( x "\n" y ) > hostfile ; print ( x "\n" y ) > pipefile ;
}
} }
} }

View File

@ -29,82 +29,90 @@
############################################################################## ##############################################################################
odhcpd_zonedata() { odhcpd_zonedata() {
local longconf dateconf
local dns_ls_add=$UNBOUND_VARDIR/dhcp_dns.add
local dns_ls_del=$UNBOUND_VARDIR/dhcp_dns.del
local dhcp_ls_new=$UNBOUND_VARDIR/dhcp_lease.new
local dhcp_ls_old=$UNBOUND_VARDIR/dhcp_lease.old
local dhcp_ls_add=$UNBOUND_VARDIR/dhcp_lease.add
local dhcp_ls_del=$UNBOUND_VARDIR/dhcp_lease.del
local dhcp_link=$( uci_get unbound.@unbound[0].dhcp_link ) local dhcp_link=$( uci_get unbound.@unbound[0].dhcp_link )
local dhcp4_slaac6=$( uci_get unbound.@unbound[0].dhcp4_slaac6 ) local dhcp4_slaac6=$( uci_get unbound.@unbound[0].dhcp4_slaac6 )
local dhcp_domain=$( uci_get unbound.@unbound[0].domain ) local dhcp_domain=$( uci_get unbound.@unbound[0].domain )
local dhcp_origin=$( uci_get dhcp.@odhcpd[0].leasefile ) local dhcp_origin=$( uci_get dhcp.@odhcpd[0].leasefile )
if [ "$dhcp_link" = "odhcpd" -a -f "$dhcp_origin" ] ; then if [ -f "$UB_TOTAL_CONF" ] && [ -f "$dhcp_origin" ] \
&& [ "$dhcp_link" = "odhcpd" ] && [ -n "$dhcp_domain" ] ; then
local longconf dateconf
local dns_ls_add=$UB_VARDIR/dhcp_dns.add
local dns_ls_del=$UB_VARDIR/dhcp_dns.del
local dns_ls_new=$UB_VARDIR/dhcp_dns.new
local dns_ls_old=$UB_VARDIR/dhcp_dns.old
local dhcp_ls_new=$UB_VARDIR/dhcp_lease.new
# Capture the lease file which could be changing often # Capture the lease file which could be changing often
sort $dhcp_origin > $dhcp_ls_new sort $dhcp_origin > $dhcp_ls_new
if [ ! -f $UNBOUND_DHCP_CONF -o ! -f $dhcp_ls_old ] ; then if [ ! -f $UB_DHCP_CONF ] || [ ! -f $dns_ls_old ] ; then
longconf=2 # no old files laying around
longconf=freshstart
else else
dateconf=$(( $( date +%s ) - $( date -r $UNBOUND_DHCP_CONF +%s ) )) # incremental at high load or full refresh about each 5 minutes
dateconf=$(( $( date +%s ) - $( date -r $UB_DHCP_CONF +%s ) ))
if [ $dateconf > 150 ] ; then if [ $dateconf -gt 300 ] ; then
longconf=1 longconf=longtime
else else
longconf=0 longconf=increment
fi fi
fi fi
if [ $longconf -gt 0 ] ; then case $longconf in
# Go through the messy business of coding up A, AAAA, and PTR records freshstart)
# This static conf will be available if Unbound restarts asynchronously awk -v conffile=$UB_DHCP_CONF -v pipefile=$dns_ls_new \
awk -v hostfile=$UNBOUND_DHCP_CONF -v domain=$dhcp_domain \ -v domain=$dhcp_domain -v bslaac=$dhcp4_slaac6 \
-v bslaac=$dhcp4_slaac6 -v bisolt=0 -v bconf=1 \ -v bisolt=0 -v bconf=1 \
-f /usr/lib/unbound/odhcpd.awk $dhcp_ls_new -f /usr/lib/unbound/odhcpd.awk $dhcp_ls_new
fi
cp $dns_ls_new $dns_ls_add
cp $dns_ls_new $dns_ls_old
;;
if [ $longconf -lt 2 ] ; then longtime)
# Deleting and adding all records into Unbound can be a burden in a awk -v conffile=$UB_DHCP_CONF -v pipefile=$dns_ls_new \
# high density environment. Use unbound-control incrementally. -v domain=$dhcp_domain -v bslaac=$dhcp4_slaac6 \
sort $dhcp_ls_old $dhcp_ls_new $dhcp_ls_new | uniq -u > $dhcp_ls_del -v bisolt=0 -v bconf=1 \
awk -v hostfile=$dns_ls_del -v domain=$dhcp_domain \
-v bslaac=$dhcp4_slaac6 -v bisolt=0 -v bconf=0 \
-f /usr/lib/unbound/odhcpd.awk $dhcp_ls_del
sort $dhcp_ls_new $dhcp_ls_old $dhcp_ls_old | uniq -u > $dhcp_ls_add
awk -v hostfile=$dns_ls_add -v domain=$dhcp_domain \
-v bslaac=$dhcp4_slaac6 -v bisolt=0 -v bconf=0 \
-f /usr/lib/unbound/odhcpd.awk $dhcp_ls_add
else
awk -v hostfile=$dns_ls_add -v domain=$dhcp_domain \
-v bslaac=$dhcp4_slaac6 -v bisolt=0 -v bconf=0 \
-f /usr/lib/unbound/odhcpd.awk $dhcp_ls_new -f /usr/lib/unbound/odhcpd.awk $dhcp_ls_new
fi
awk '{ print $1 }' $dns_ls_old | sort | uniq > $dns_ls_del
cp $dns_ls_new $dns_ls_add
cp $dns_ls_new $dns_ls_old
;;
*)
# incremental add and prepare the old list for delete later
# unbound-control can be slow so high DHCP rates cannot run a full list
awk -v conffile=$UB_DHCP_CONF -v pipefile=$dns_ls_new \
-v domain=$dhcp_domain -v bslaac=$dhcp4_slaac6 \
-v bisolt=0 -v bconf=0 \
-f /usr/lib/unbound/odhcpd.awk $dhcp_ls_new
sort $dns_ls_new $dns_ls_old $dns_ls_old | uniq -u > $dns_ls_add
sort $dns_ls_new $dns_ls_old | uniq > $dns_ls_old
;;
esac
if [ -f "$dns_ls_del" ] ; then if [ -f "$dns_ls_del" ] ; then
cat $dns_ls_del | $UNBOUND_CONTROL_CFG local_datas_remove cat $dns_ls_del | $UB_CONTROL_CFG local_datas_remove
fi fi
if [ -f "$dns_ls_add" ] ; then if [ -f "$dns_ls_add" ] ; then
cat $dns_ls_add | $UNBOUND_CONTROL_CFG local_datas cat $dns_ls_add | $UB_CONTROL_CFG local_datas
fi fi
# prepare next round # prepare next round
mv $dhcp_ls_new $dhcp_ls_old rm -f $dns_ls_new $dns_ls_del $dns_ls_add $dhcp_ls_new
rm -f $dns_ls_del $dns_ls_add $dhcp_ls_del $dhcp_ls_add
fi fi
} }

View File

@ -19,6 +19,10 @@
# #
############################################################################## ##############################################################################
. /usr/lib/unbound/defaults.sh
##############################################################################
roothints_update() { roothints_update() {
# TODO: Might not be implemented. Unbound doesn't natively update hints. # TODO: Might not be implemented. Unbound doesn't natively update hints.
# Unbound philosophy is built in root hints are good for machine life. # Unbound philosophy is built in root hints are good for machine life.
@ -29,17 +33,21 @@ roothints_update() {
rootkey_update() { rootkey_update() {
local basekey_date rootkey_date rootkey_age filestuff local basekey_date rootkey_date rootkey_age filestuff
local dnssec=$( uci_get unbound.@unbound[0].validator ) local dnssec=$( uci_get unbound.@unbound[0].validator )
local dnssec_ntp=$( uci_get unbound.@unbound[0].validator_ntp ) local dnssec_ntp=$( uci_get unbound.@unbound[0].validator_ntp )
local dnssec_age=$( uci_get unbound.@unbound[0].root_age ) local dnssec_age=$( uci_get unbound.@unbound[0].root_age )
# fix empty
[ -z "$dnssec" ] && dnssec=0
[ -z "$dnssec_ntp" ] && dnssec_ntp=1
[ -z "$dnssec_age" ] && dnssec_age=9
if [ "$dnssec_age" -gt 90 -o "$dnssec" -lt 1 ] ; then
if [ "$dnssec_age" -gt 90 ] || [ "$dnssec" -lt 1 ] ; then
# Feature disabled # Feature disabled
return 0 return 0
elif [ "$dnssec_ntp" -gt 0 -a ! -f "$UNBOUND_TIMEFILE" ] ; then elif [ "$dnssec_ntp" -gt 0 ] && [ ! -f "$UB_TIME_FILE" ] ; then
# We don't have time yet # We don't have time yet
return 0 return 0
fi fi
@ -54,16 +62,16 @@ rootkey_update() {
fi fi
if [ -f "$UNBOUND_KEYFILE" ] ; then if [ -f "$UB_RKEY_FILE" ] ; then
# Unbound maintains it itself # Unbound maintains it itself
rootkey_date=$( date -r $UNBOUND_KEYFILE +%s ) rootkey_date=$( date -r $UB_RKEY_FILE +%s )
rootkey_age=$(( (rootkey_date - basekey_date) / 86440 )) rootkey_age=$(( (rootkey_date - basekey_date) / 86440 ))
elif [ -x "$UNBOUND_ANCHOR" ] ; then elif [ -x "$UB_ANCHOR" ] ; then
# No tmpfs key - use unbound-anchor # No tmpfs key - use unbound-anchor
rootkey_date=$( date -I +%s ) rootkey_date=$( date -I +%s )
rootkey_age=$(( (rootkey_date - basekey_date) / 86440 )) rootkey_age=$(( (rootkey_date - basekey_date) / 86440 ))
$UNBOUND_ANCHOR -a $UNBOUND_KEYFILE $UB_ANCHOR -a $UB_RKEY_FILE
else else
# give up # give up
@ -72,20 +80,20 @@ rootkey_update() {
if [ "$rootkey_age" -gt "$dnssec_age" ] ; then if [ "$rootkey_age" -gt "$dnssec_age" ] ; then
filestuff=$( cat $UNBOUND_KEYFILE ) filestuff=$( cat $UB_RKEY_FILE )
case "$filestuff" in case "$filestuff" in
*NOERROR*) *NOERROR*)
# Header comment for drill and dig # Header comment for drill and dig
logger -t unbound -s "root.key updated after $rootkey_age days" logger -t unbound -s "root.key updated after $rootkey_age days"
cp -p $UNBOUND_KEYFILE /etc/unbound/root.key cp -p $UB_RKEY_FILE /etc/unbound/root.key
;; ;;
*"state=2 [ VALID ]"*) *"state=2 [ VALID ]"*)
# Comment inline to key for unbound-anchor # Comment inline to key for unbound-anchor
logger -t unbound -s "root.key updated after $rootkey_age days" logger -t unbound -s "root.key updated after $rootkey_age days"
cp -p $UNBOUND_KEYFILE /etc/unbound/root.key cp -p $UB_RKEY_FILE /etc/unbound/root.key
;; ;;
*) *)
@ -97,7 +105,20 @@ rootkey_update() {
############################################################################## ##############################################################################
rootzone_update() { resolv_teardown() {
case $( cat /tmp/resolv.conf ) in
*"generated by Unbound UCI"*)
# our resolver file, reset to auto resolver file.
rm -f /tmp/resolv.conf
ln -s /tmp/resolv.conf.auto /tmp/resolv.conf
;;
esac
}
##############################################################################
unbound_stop() {
resolv_teardown
roothints_update roothints_update
rootkey_update rootkey_update
} }

View File

@ -17,14 +17,14 @@ PROG=/usr/sbin/unbound
############################################################################## ##############################################################################
boot() { boot() {
UNBOUND_BOOT=1 UB_BOOT=1
start "$@" start "$@"
} }
############################################################################## ##############################################################################
start_service() { start_service() {
if [ -n "$UNBOUND_BOOT" ] ; then if [ -n "$UB_BOOT" ] ; then
# Load procd triggers (rc) and use event IFUP to really start # Load procd triggers (rc) and use event IFUP to really start
return 0 return 0
fi fi
@ -35,7 +35,7 @@ start_service() {
# standard procd clause # standard procd clause
procd_open_instance "unbound" procd_open_instance "unbound"
procd_set_param command $PROG -d -c $UNBOUND_CONFFILE procd_set_param command $PROG -d -c $UB_TOTAL_CONF
procd_set_param respawn procd_set_param respawn
procd_close_instance procd_close_instance
} }
@ -44,7 +44,7 @@ start_service() {
stop_service() { stop_service() {
# clean up # clean up
. /usr/lib/unbound/unbound.sh . /usr/lib/unbound/stopping.sh
unbound_stop unbound_stop
# Wait! on restart Unbound may take time writing closure stats to syslog # Wait! on restart Unbound may take time writing closure stats to syslog
@ -54,22 +54,29 @@ stop_service() {
############################################################################## ##############################################################################
service_triggers() { service_triggers() {
local trigger
local legacy=$( uci_get unbound.@unbound[0].trigger ) local legacy=$( uci_get unbound.@unbound[0].trigger )
local triggers=$( uci_get unbound.@unbound[0].trigger_interface ) local triggers=$( uci_get unbound.@unbound[0].trigger_interface )
local trigger="$triggers $legacy"
triggers="$triggers $legacy" . /usr/lib/unbound/defaults.sh
PROCD_RELOAD_DELAY=2000
procd_add_reload_trigger "unbound"
if [ ! -f "$UB_TOTAL_CONF" ] || [ -n "$UB_BOOT" ] ; then
# Unbound can be a bit heavy, so wait some on first start. Any interface
# up affects the trigger delay and will guarantee start.
procd_add_raw_trigger "interface.*.up" 3000 /etc/init.d/unbound restart
elif [ -n "$triggers" ] ; then
procd_add_reload_trigger "unbound" "dhcp"
if [ -n "$triggers" ] ; then
for trigger in $triggers ; do for trigger in $triggers ; do
# due to some netifd/procd interactions with IP6, limit interfaces # User selected triggers to restart at any other time
procd_add_reload_interface_trigger "$trigger" procd_add_reload_interface_trigger "$trigger"
done done
else else
procd_add_raw_trigger "interface.*.up" 2000 /etc/init.d/unbound reload procd_add_reload_trigger "unbound" "dhcp"
fi fi
} }

View File

@ -13,12 +13,12 @@
############################################################################## ##############################################################################
# Common file location definitions # Common file location definitions
. /usr/lib/unbound/unbound.sh . /usr/lib/unbound/defaults.sh
############################################################################## ##############################################################################
if [ "$ACTION" = stratum -a ! -f "$UNBOUND_TIMEFILE" ] ; then if [ ! -f "$UB_TIME_FILE" -a "$ACTION" = stratum ] ; then
echo "ntpd: $( date )" > $UNBOUND_TIMEFILE date -Is > $UB_TIME_FILE
/etc/init.d/unbound enabled && /etc/init.d/unbound restart /etc/init.d/unbound enabled && /etc/init.d/unbound restart
# Yes, hard RESTART. We need to be absolutely sure to enable DNSSEC. # Yes, hard RESTART. We need to be absolutely sure to enable DNSSEC.
fi fi

File diff suppressed because it is too large Load Diff

View File

@ -9,12 +9,12 @@ config unbound
option domain 'lan' option domain 'lan'
option domain_type 'static' option domain_type 'static'
option edns_size '1280' option edns_size '1280'
option extended_luci '0'
option extended_stats '0' option extended_stats '0'
option hide_binddata '1' option hide_binddata '1'
option listen_port '53' option listen_port '53'
option localservice '1' option localservice '1'
option manual_conf '0' option manual_conf '0'
option num_threads '1'
option protocol 'default' option protocol 'default'
option query_minimize '0' option query_minimize '0'
option query_min_strict '0' option query_min_strict '0'
@ -27,9 +27,28 @@ config unbound
option unbound_control '0' option unbound_control '0'
option validator '0' option validator '0'
option validator_ntp '1' option validator_ntp '1'
option verbosity '1'
list trigger_interface 'lan' list trigger_interface 'lan'
list trigger_interface 'wan' list trigger_interface 'wan'
#list rebind_interface 'lan'
#list domain_insecure 'ntp.example.com' #list domain_insecure 'ntp.example.com'
#list domain_forward 'mail.example.com'
config zone
option enabled '0'
option fallback '1'
option url_dir 'https://www.internic.net/domain/'
option zone_type 'auth_zone'
list server 'lax.xfr.dns.icann.org'
list server 'iad.xfr.dns.icann.org'
list zone_name '.'
list zone_name 'arpa.'
list zone_name 'in-addr.arpa.'
list zone_name 'ip6.arpa.'
config zone
option enabled '0'
option fallback '1'
option resolv_conf '1'
option zone_type 'forward_zone'
list zone_name 'isp-bill.example.com.'
list zone_name 'isp-mail.example.net.'