Merge pull request #6583 from zhaojh329/wifidog-ng
wifidog-ng: Update to 2.0.0
This commit is contained in:
commit
430b7a4e90
17 changed files with 1276 additions and 53 deletions
|
@ -1,5 +1,5 @@
|
||||||
#
|
|
||||||
# Copyright (C) 2014-2017 OpenWrt.org
|
# Copyright (C) 2018 Jianhui Zhao
|
||||||
#
|
#
|
||||||
# This is free software, licensed under the GNU General Public License v2.
|
# This is free software, licensed under the GNU General Public License v2.
|
||||||
# See /LICENSE for more information.
|
# See /LICENSE for more information.
|
||||||
|
@ -8,15 +8,10 @@
|
||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=wifidog-ng
|
PKG_NAME:=wifidog-ng
|
||||||
PKG_VERSION:=1.5.6
|
PKG_VERSION:=2.0.0
|
||||||
PKG_RELEASE:=1
|
PKG_RELEASE:=1
|
||||||
|
|
||||||
PKG_SOURCE_PROTO:=git
|
PKG_BUILD_DIR=$(BUILD_DIR)/$(PKG_NAME)-$(BUILD_VARIANT)
|
||||||
PKG_SOURCE_VERSION:=v$(PKG_VERSION)
|
|
||||||
PKG_SOURCE_URL=https://github.com/zhaojh329/wifidog-ng.git
|
|
||||||
PKG_MIRROR_HASH:=a81e9a4d5feb3facbe1b2b55d2d813944b569865f53421680efbfc6876aa3f5d
|
|
||||||
|
|
||||||
PKG_BUILD_DIR=$(BUILD_DIR)/$(PKG_NAME)-$(BUILD_VARIANT)/$(PKG_SOURCE_SUBDIR)
|
|
||||||
|
|
||||||
PKG_LICENSE:=LGPL-2.1
|
PKG_LICENSE:=LGPL-2.1
|
||||||
PKG_LICENSE_FILES:=LICENSE
|
PKG_LICENSE_FILES:=LICENSE
|
||||||
|
@ -24,15 +19,14 @@ PKG_LICENSE_FILES:=LICENSE
|
||||||
PKG_MAINTAINER:=Jianhui Zhao <jianhuizhao329@gmail.com>
|
PKG_MAINTAINER:=Jianhui Zhao <jianhuizhao329@gmail.com>
|
||||||
|
|
||||||
include $(INCLUDE_DIR)/package.mk
|
include $(INCLUDE_DIR)/package.mk
|
||||||
include $(INCLUDE_DIR)/cmake.mk
|
|
||||||
|
|
||||||
define Package/wifidog-ng/default
|
define Package/wifidog-ng/default
|
||||||
SUBMENU:=Captive Portals
|
SUBMENU:=Captive Portals
|
||||||
SECTION:=net
|
SECTION:=net
|
||||||
CATEGORY:=Network
|
CATEGORY:=Network
|
||||||
TITLE:=Next generation WifiDog
|
TITLE:=Next generation WifiDog implemented in Lua
|
||||||
DEPENDS:=+kmod-wifidog-ng +libuci +libuclient +libblobmsg-json +libubus +libcares \
|
DEPENDS:=+kmod-wifidog-ng +libubox-lua +libuci-lua +libubus-lua \
|
||||||
+ipset +libpcap
|
+ipset +dnsmasq-full +luasocket
|
||||||
endef
|
endef
|
||||||
|
|
||||||
define Package/wifidog-ng-nossl
|
define Package/wifidog-ng-nossl
|
||||||
|
@ -68,13 +62,14 @@ endef
|
||||||
|
|
||||||
define Package/wifidog-ng/default/install
|
define Package/wifidog-ng/default/install
|
||||||
$(INSTALL_DIR) $(1)/usr/bin $(1)/etc/init.d $(1)/etc/config \
|
$(INSTALL_DIR) $(1)/usr/bin $(1)/etc/init.d $(1)/etc/config \
|
||||||
$(1)/etc/wifidog-ng $(1)//etc/hotplug.d/dhcp
|
$(1)/etc/wifidog-ng $(1)//etc/hotplug.d/dhcp $(1)/usr/lib/lua
|
||||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/src/wifidog-ng $(1)/usr/bin
|
$(INSTALL_BIN) ./files//wifidog-ng.lua $(1)/usr/bin/wifidog-ng
|
||||||
$(INSTALL_BIN) ./files/wifidog-ng.init $(1)/etc/init.d/wifidog-ng
|
$(INSTALL_BIN) ./files/wifidog-ng.init $(1)/etc/init.d/wifidog-ng
|
||||||
$(INSTALL_CONF) ./files/wifidog-ng.config $(1)/etc/config/wifidog-ng
|
$(INSTALL_CONF) ./files/wifidog-ng.config $(1)/etc/config/wifidog-ng
|
||||||
$(INSTALL_CONF) ./files/wifidog-ng.key $(1)/etc/wifidog-ng
|
$(INSTALL_CONF) ./files/ssl.key $(1)/etc/wifidog-ng
|
||||||
$(INSTALL_CONF) ./files/wifidog-ng.crt $(1)/etc/wifidog-ng
|
$(INSTALL_CONF) ./files/ssl.crt $(1)/etc/wifidog-ng
|
||||||
$(INSTALL_DATA) ./files/wifidog-ng.hotplug $(1)/etc/hotplug.d/dhcp/00-wifidog-ng
|
$(INSTALL_DATA) ./files/wifidog-ng.hotplug $(1)/etc/hotplug.d/dhcp/00-wifidog-ng
|
||||||
|
$(CP) ./files/wifidog-ng $(1)/usr/lib/lua
|
||||||
endef
|
endef
|
||||||
|
|
||||||
Package/wifidog-ng-nossl/install = $(Package/wifidog-ng/default/install)
|
Package/wifidog-ng-nossl/install = $(Package/wifidog-ng/default/install)
|
||||||
|
@ -88,14 +83,13 @@ define KernelPackage/wifidog-ng
|
||||||
SUBMENU:=Other modules
|
SUBMENU:=Other modules
|
||||||
TITLE:=Kernel module for wifidog-ng
|
TITLE:=Kernel module for wifidog-ng
|
||||||
DEPENDS:=+kmod-nf-nat +kmod-ipt-ipset
|
DEPENDS:=+kmod-nf-nat +kmod-ipt-ipset
|
||||||
FILES:=$(PKG_BUILD_DIR)/kmod/wifidog-ng.ko
|
FILES:=$(PKG_BUILD_DIR)/wifidog-ng.ko
|
||||||
endef
|
endef
|
||||||
|
|
||||||
include $(INCLUDE_DIR)/kernel-defaults.mk
|
include $(INCLUDE_DIR)/kernel-defaults.mk
|
||||||
|
|
||||||
define Build/Compile
|
define Build/Compile
|
||||||
$(call Build/Compile/Default)
|
$(MAKE) $(KERNEL_MAKEOPTS) SUBDIRS="$(PKG_BUILD_DIR)" modules
|
||||||
$(MAKE) $(KERNEL_MAKEOPTS) SUBDIRS="$(PKG_BUILD_DIR)"/kmod modules
|
|
||||||
endef
|
endef
|
||||||
|
|
||||||
$(eval $(call BuildPackage,wifidog-ng-nossl))
|
$(eval $(call BuildPackage,wifidog-ng-nossl))
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
config gateway
|
config gateway
|
||||||
option enabled 1
|
option enabled 1
|
||||||
option ifname 'br-lan'
|
option interface 'lan'
|
||||||
option port 2060
|
option port 2060
|
||||||
|
option dhcp_host_white 1
|
||||||
option ssl_port 8443
|
option ssl_port 8443
|
||||||
option checkinterval 30
|
option checkinterval 30
|
||||||
option client_timeout 5
|
option client_timeout 5
|
||||||
option temppass_time 30
|
option temppass_time 30
|
||||||
|
|
||||||
config authserver
|
config server
|
||||||
option host 'authserver.com'
|
option host 'authserver.com'
|
||||||
option port 80
|
option port 80
|
||||||
option ssl 0
|
option ssl 0
|
||||||
|
@ -16,10 +17,4 @@ config authserver
|
||||||
option portal_path 'portal'
|
option portal_path 'portal'
|
||||||
option msg_path 'gw_message.php'
|
option msg_path 'gw_message.php'
|
||||||
option ping_path 'ping'
|
option ping_path 'ping'
|
||||||
option auth_path 'auth'
|
option auth_path 'auth'
|
||||||
|
|
||||||
config popularserver
|
|
||||||
list server www.baidu.com
|
|
||||||
list server www.qq.com
|
|
||||||
|
|
||||||
config whitelist
|
|
|
@ -5,45 +5,117 @@ START=95
|
||||||
|
|
||||||
BIN=/usr/bin/wifidog-ng
|
BIN=/usr/bin/wifidog-ng
|
||||||
|
|
||||||
parse_whitelist_mac() {
|
dhcp_host_white=1
|
||||||
local cfg="$1"
|
|
||||||
local mac
|
|
||||||
|
|
||||||
uci_validate_section wifidog-ng whitelist "${1}" \
|
start_wifidog() {
|
||||||
'mac:macaddr'
|
local cfg="$1"
|
||||||
|
local enabled interface
|
||||||
|
|
||||||
|
uci_validate_section wifidog-ng gateway "${1}" \
|
||||||
|
'enabled:bool:0' \
|
||||||
|
'interface:uci("network", "@interface"):lan' \
|
||||||
|
'dhcp_host_white:bool:1'
|
||||||
|
|
||||||
[ $? -ne 0 ] && {
|
[ $? -ne 0 ] && {
|
||||||
echo "validation whitelist_mac failed" >&2
|
echo "validation gateway failed" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
parse_whitelist_domain() {
|
[ $enabled -eq 1 ] || exit 0
|
||||||
local cfg="$1"
|
|
||||||
local domain
|
|
||||||
|
|
||||||
uci_validate_section wifidog-ng whitelist "${1}" \
|
# timeout = 49 days
|
||||||
'domain:host'
|
ipset -! create wifidog-ng-mac hash:mac timeout 4294967
|
||||||
|
ipset -! create wifidog-ng-ip hash:ip
|
||||||
|
|
||||||
[ $? -ne 0 ] && {
|
|
||||||
echo "validation whitelist_domain failed" >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
start_service() {
|
|
||||||
modprobe wifidog-ng
|
modprobe wifidog-ng
|
||||||
|
echo "enabled=1" > /proc/wifidog-ng/config
|
||||||
config_load wifidog-ng
|
|
||||||
config_foreach parse_whitelist_mac whitelist_mac
|
|
||||||
config_foreach parse_whitelist_domain whitelist_domain
|
|
||||||
|
|
||||||
procd_open_instance
|
procd_open_instance
|
||||||
procd_set_param command $BIN
|
procd_set_param command $BIN
|
||||||
procd_set_param respawn
|
procd_set_param respawn
|
||||||
procd_close_instance
|
procd_close_instance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parse_server() {
|
||||||
|
local cfg="$1"
|
||||||
|
local host
|
||||||
|
|
||||||
|
config_get host $cfg host
|
||||||
|
validate_data ip4addr "$host" 2> /dev/null
|
||||||
|
if [ $? -eq 0 ];
|
||||||
|
then
|
||||||
|
ipset add wifidog-ng-ip $host
|
||||||
|
else
|
||||||
|
echo "ipset=/$host/wifidog-ng-ip" >> /tmp/dnsmasq.d/wifidog-ng
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_validated_user() {
|
||||||
|
local cfg="$1"
|
||||||
|
local mac ip
|
||||||
|
|
||||||
|
uci_validate_section wifidog-ng validated_user "${1}" \
|
||||||
|
'mac:macaddr'
|
||||||
|
|
||||||
|
[ $? -ne 0 ] && {
|
||||||
|
echo "validation validated_user failed" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[ -n "$mac" ] && ipset add wifidog-ng-mac $mac
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_validated_domain() {
|
||||||
|
local cfg="$1"
|
||||||
|
local domain
|
||||||
|
|
||||||
|
uci_validate_section wifidog-ng validated_domain "${1}" \
|
||||||
|
'domain:host'
|
||||||
|
|
||||||
|
[ $? -ne 0 ] && {
|
||||||
|
echo "validation validated_domain failed" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[ -n "$domain" ] && echo "ipset=/$domain/wifidog-ng-ip" >> /tmp/dnsmasq.d/wifidog-ng
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_dhcp_host() {
|
||||||
|
local cfg="$1"
|
||||||
|
local mac ip
|
||||||
|
|
||||||
|
uci_validate_section dhcp host "${1}" \
|
||||||
|
'mac:macaddr'
|
||||||
|
|
||||||
|
[ $? -ne 0 ] && {
|
||||||
|
echo "validation validated dhcp host failed" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[ -n "$mac" ] && ipset add wifidog-ng-mac $mac
|
||||||
|
}
|
||||||
|
|
||||||
|
start_service() {
|
||||||
|
config_load wifidog-ng
|
||||||
|
config_foreach start_wifidog gateway
|
||||||
|
|
||||||
|
echo -n > /tmp/dnsmasq.d/wifidog-ng
|
||||||
|
|
||||||
|
config_foreach parse_server server
|
||||||
|
config_foreach parse_validated_user validated_user
|
||||||
|
config_foreach parse_validated_domain validated_domain
|
||||||
|
|
||||||
|
[ $dhcp_host_white -eq 1 ] && {
|
||||||
|
config_load dhcp
|
||||||
|
config_foreach parse_dhcp_host host
|
||||||
|
}
|
||||||
|
|
||||||
|
/etc/init.d/dnsmasq restart &
|
||||||
|
}
|
||||||
|
|
||||||
stop_service() {
|
stop_service() {
|
||||||
rmmod wifidog-ng
|
rmmod wifidog-ng
|
||||||
|
|
||||||
|
ipset destroy wifidog-ng-mac
|
||||||
|
ipset destroy wifidog-ng-ip
|
||||||
}
|
}
|
||||||
|
|
37
net/wifidog-ng/files/wifidog-ng.lua
Normal file
37
net/wifidog-ng/files/wifidog-ng.lua
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#!/usr/bin/env lua
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Copyright (C) 2018 Jianhui Zhao <jianhuizhao329@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||||
|
USA
|
||||||
|
--]]
|
||||||
|
|
||||||
|
local uloop = require "uloop"
|
||||||
|
local uh = require "uhttpd"
|
||||||
|
local auth = require "wifidog-ng.auth"
|
||||||
|
local ubus = require "wifidog-ng.ubus"
|
||||||
|
local version = require "wifidog-ng.version"
|
||||||
|
local heartbeat = require "wifidog-ng.heartbeat"
|
||||||
|
|
||||||
|
uh.log(uh.LOG_INFO, "Version: " .. version.string())
|
||||||
|
|
||||||
|
uloop.init()
|
||||||
|
|
||||||
|
ubus.init()
|
||||||
|
auth.init()
|
||||||
|
heartbeat.start()
|
||||||
|
|
||||||
|
uloop.run()
|
221
net/wifidog-ng/files/wifidog-ng/auth.lua
Normal file
221
net/wifidog-ng/files/wifidog-ng/auth.lua
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
--[[
|
||||||
|
Copyright (C) 2018 Jianhui Zhao <jianhuizhao329@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||||
|
USA
|
||||||
|
--]]
|
||||||
|
|
||||||
|
local uh = require "uhttpd"
|
||||||
|
local http = require "socket.http"
|
||||||
|
local util = require "wifidog-ng.util"
|
||||||
|
local config = require "wifidog-ng.config"
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local apple_host = {
|
||||||
|
["captive.apple.com"] = true,
|
||||||
|
["www.apple.com"] = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
local terms = {}
|
||||||
|
|
||||||
|
local function is_authed_user(mac)
|
||||||
|
local r = os.execute("ipset test wifidog-ng-mac " .. mac .. " 2> /dev/null")
|
||||||
|
return r == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local function allow_user(mac, temppass)
|
||||||
|
if not temppass then
|
||||||
|
terms[mac].authed = true
|
||||||
|
os.execute("ipset add wifidog-ng-mac " .. mac)
|
||||||
|
else
|
||||||
|
local cfg = config.get()
|
||||||
|
os.execute("ipset add wifidog-ng-mac " .. mac .. " timeout " .. cfg.temppass_time)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function deny_user(mac)
|
||||||
|
os.execute("ipset del wifidog-ng-mac " .. mac)
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.get_terms()
|
||||||
|
local r = {}
|
||||||
|
for k, v in pairs(terms) do
|
||||||
|
if v.authed then
|
||||||
|
r[k] = {ip = v.ip}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return r
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.new_term(ip, mac, token)
|
||||||
|
terms[mac] = {ip = ip, token = token}
|
||||||
|
if token then
|
||||||
|
terms[mac].authed = true
|
||||||
|
allow_user(mac)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function http_callback_auth(cl)
|
||||||
|
local cfg = config.get()
|
||||||
|
local token = cl:get_var("token")
|
||||||
|
local ip = cl:get_remote_addr()
|
||||||
|
local mac = util.arp_get(cfg.gw_ifname, ip)
|
||||||
|
|
||||||
|
if not mac then
|
||||||
|
uh.log(uh.LOG_ERR, "Not found macaddr for " .. ip)
|
||||||
|
cl:send_error(401, "Unauthorized", "Not found your macaddr")
|
||||||
|
return uh.REQUEST_DONE
|
||||||
|
end
|
||||||
|
|
||||||
|
if token and #token > 0 then
|
||||||
|
if cl:get_var("logout") then
|
||||||
|
local url = string.format("%s&stage=logout&ip=%s&mac=%s&token=%s", cfg.auth_url, ip, mac, token)
|
||||||
|
http.request(url)
|
||||||
|
deny_user(mac)
|
||||||
|
else
|
||||||
|
local url = string.format("%s&stage=login&ip=%s&mac=%s&token=%s", cfg.auth_url, ip, mac, token)
|
||||||
|
local r = http.request(url)
|
||||||
|
|
||||||
|
if not r then
|
||||||
|
cl:send_error(401, "Unauthorized")
|
||||||
|
return uh.REQUEST_DONE
|
||||||
|
end
|
||||||
|
|
||||||
|
local auth = r:match("Auth: (%d)")
|
||||||
|
if auth == "1" then
|
||||||
|
allow_user(mac)
|
||||||
|
cl:redirect(302, string.format("%s&mac=%s", cfg.portal_url, mac))
|
||||||
|
else
|
||||||
|
cl:redirect(302, string.format("%s&mac=%s", cfg.msg_url, mac))
|
||||||
|
return uh.REQUEST_DONE
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
cl:send_error(401, "Unauthorized")
|
||||||
|
return uh.REQUEST_DONE
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function http_callback_temppass(cl)
|
||||||
|
local cfg = config.get()
|
||||||
|
local ip = cl:get_remote_addr()
|
||||||
|
local mac = util.arp_get(cfg.gw_ifname, ip)
|
||||||
|
|
||||||
|
if not mac then
|
||||||
|
uh.log(uh.LOG_ERR, "Not found macaddr for " .. ip)
|
||||||
|
cl:send_error(401, "Unauthorized", "Not found your macaddr")
|
||||||
|
return uh.REQUEST_DONE
|
||||||
|
end
|
||||||
|
|
||||||
|
local script = cl:get_var("script") or ""
|
||||||
|
|
||||||
|
cl:send_header(200, "OK", -1)
|
||||||
|
cl:header_end()
|
||||||
|
allow_user(mac, true)
|
||||||
|
cl:chunk_send(cl:get_var("script") or "");
|
||||||
|
cl:request_done()
|
||||||
|
|
||||||
|
return uh.REQUEST_DONE
|
||||||
|
end
|
||||||
|
|
||||||
|
local function http_callback_404(cl, path)
|
||||||
|
local cfg = config.get()
|
||||||
|
|
||||||
|
if cl:get_http_method() ~= uh.HTTP_METHOD_GET then
|
||||||
|
cl:send_error(401, "Unauthorized")
|
||||||
|
return uh.REQUEST_DONE
|
||||||
|
end
|
||||||
|
|
||||||
|
local ip = cl:get_remote_addr()
|
||||||
|
local mac = util.arp_get(cfg.gw_ifname, ip)
|
||||||
|
if not mac then
|
||||||
|
uh.log(uh.LOG_ERR, "Not found macaddr for " .. ip)
|
||||||
|
cl:send_error(401, "Unauthorized", "Not found your macaddr")
|
||||||
|
return uh.REQUEST_DONE
|
||||||
|
end
|
||||||
|
|
||||||
|
term = terms[mac]
|
||||||
|
if not term then
|
||||||
|
terms[mac] = {ip = ip}
|
||||||
|
end
|
||||||
|
|
||||||
|
term = terms[mac]
|
||||||
|
|
||||||
|
if is_authed_user(mac) then
|
||||||
|
cl:redirect(302, "%s&mac=%s", cfg.portal_url, mac)
|
||||||
|
return uh.REQUEST_DONE
|
||||||
|
end
|
||||||
|
|
||||||
|
cl:send_header(200, "OK", -1)
|
||||||
|
cl:header_end()
|
||||||
|
|
||||||
|
local header_host = cl:get_header("host")
|
||||||
|
if apple_host[header_host] then
|
||||||
|
local http_ver = cl:get_http_version()
|
||||||
|
if http_ver == uh.HTTP_VER_10 then
|
||||||
|
if not term.apple then
|
||||||
|
cl:chunk_send("fuck you")
|
||||||
|
term.apple = true
|
||||||
|
cl:request_done()
|
||||||
|
return uh.REQUEST_DONE
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local redirect_html = [[
|
||||||
|
<!doctype html><html><head><title>Success</title>
|
||||||
|
<script type="text/javascript">
|
||||||
|
setTimeout(function() {location.replace('%s&ip=%s&mac=%s');}, 1);</script>
|
||||||
|
<style type="text/css">body {color:#FFF}</style></head>
|
||||||
|
<body>Success</body></html>
|
||||||
|
]]
|
||||||
|
|
||||||
|
cl:chunk_send(string.format(redirect_html, cfg.login_url, ip, mac))
|
||||||
|
cl:request_done()
|
||||||
|
|
||||||
|
return uh.REQUEST_DONE
|
||||||
|
end
|
||||||
|
|
||||||
|
local function on_request(cl, path)
|
||||||
|
if path == "/wifidog/auth" then
|
||||||
|
return http_callback_auth(cl)
|
||||||
|
elseif path == "/wifidog/temppass" then
|
||||||
|
return http_callback_temppass(cl)
|
||||||
|
end
|
||||||
|
|
||||||
|
return uh.REQUEST_CONTINUE
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.init()
|
||||||
|
local cfg = config.get()
|
||||||
|
|
||||||
|
local srv = uh.new(cfg.gw_address, cfg.gw_port)
|
||||||
|
|
||||||
|
srv:on_request(on_request)
|
||||||
|
srv:on_error404(http_callback_404)
|
||||||
|
|
||||||
|
if uh.SSL_SUPPORTED then
|
||||||
|
local srv_ssl = uh.new(cfg.gw_address, cfg.gw_ssl_port)
|
||||||
|
|
||||||
|
srv_ssl:ssl_init("/etc/wifidog-ng/ssl.crt", "/etc/wifidog-ng/ssl.key")
|
||||||
|
|
||||||
|
srv_ssl:on_request(on_request)
|
||||||
|
srv_ssl:on_error404(http_callback_404)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
158
net/wifidog-ng/files/wifidog-ng/config.lua
Normal file
158
net/wifidog-ng/files/wifidog-ng/config.lua
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
--[[
|
||||||
|
Copyright (C) 2018 Jianhui Zhao <jianhuizhao329@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||||
|
USA
|
||||||
|
--]]
|
||||||
|
|
||||||
|
local uci = require "uci"
|
||||||
|
local util = require "wifidog-ng.util"
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local cfg = {}
|
||||||
|
|
||||||
|
function M.parse()
|
||||||
|
local c = uci.cursor()
|
||||||
|
|
||||||
|
c:foreach('wifidog-ng', 'gateway', function(s)
|
||||||
|
local port = s.port or 2060
|
||||||
|
local ssl_port = s.ssl_port or 8443
|
||||||
|
local interface = s.interface or "lan"
|
||||||
|
local checkinterval = s.checkinterval or 30
|
||||||
|
local client_timeout = s.client_timeout or 5
|
||||||
|
local temppass_time = s.temppass_time or 30
|
||||||
|
local id = s.id
|
||||||
|
local address = s.address
|
||||||
|
|
||||||
|
cfg.gw_port = tonumber(port)
|
||||||
|
cfg.gw_ssl_port = tonumber(ssl_port)
|
||||||
|
cfg.checkinterval = tonumber(checkinterval)
|
||||||
|
cfg.client_timeout = tonumber(client_timeout)
|
||||||
|
cfg.temppass_time = tonumber(temppass_time)
|
||||||
|
cfg.gw_address = s.address
|
||||||
|
cfg.gw_id = s.id
|
||||||
|
|
||||||
|
local st = util.ubus("network.interface." .. interface, "status")
|
||||||
|
cfg.gw_ifname = st.device
|
||||||
|
|
||||||
|
if not cfg.gw_address then
|
||||||
|
cfg.gw_address = st["ipv4-address"][1].address
|
||||||
|
end
|
||||||
|
|
||||||
|
if not cfg.gw_id then
|
||||||
|
local devst = util.ubus("network.device", "status", {name = st.device})
|
||||||
|
local macaddr = devst.macaddr
|
||||||
|
cfg.gw_id = macaddr:gsub(":", ""):upper()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
c:foreach('wifidog-ng', 'server', function(s)
|
||||||
|
local host = s.host
|
||||||
|
local path = s.path or "/wifidog/"
|
||||||
|
local gw_port = cfg.gw_port
|
||||||
|
local gw_id = cfg.gw_id
|
||||||
|
local gw_address = cfg.gw_address
|
||||||
|
local ssid = cfg.ssid or ""
|
||||||
|
local proto, port = "http", ""
|
||||||
|
|
||||||
|
|
||||||
|
if s.port ~= "80" and s.port ~= "443" then
|
||||||
|
port = ":" .. s.port
|
||||||
|
end
|
||||||
|
|
||||||
|
if s.ssl == "1" then
|
||||||
|
proto = "https"
|
||||||
|
end
|
||||||
|
|
||||||
|
cfg.login_url = string.format("%s://%s%s%s%s?gw_address=%s&gw_port=%d&gw_id=%s&ssid=%s",
|
||||||
|
proto, host, port, path, s.login_path, gw_address, gw_port, gw_id, ssid)
|
||||||
|
|
||||||
|
cfg.auth_url = string.format("%s://%s%s%s%s?gw_id=%s",
|
||||||
|
proto, host, port, path, s.auth_path, gw_id)
|
||||||
|
|
||||||
|
cfg.ping_url = string.format("%s://%s%s%s%s?gw_id=%s",
|
||||||
|
proto, host, port, path, s.ping_path, gw_id)
|
||||||
|
|
||||||
|
cfg.portal_url = string.format("%s://%s%s%s%s?gw_id=%s",
|
||||||
|
proto, host, port, path, s.portal_path, gw_id)
|
||||||
|
|
||||||
|
cfg.msg_url = string.format("%s://%s%s%s%s?gw_id=%s",
|
||||||
|
proto, host, port, path, s.msg_path, gw_id)
|
||||||
|
end)
|
||||||
|
|
||||||
|
cfg.parsed = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.get()
|
||||||
|
if not cfg.parsed then
|
||||||
|
M.parse()
|
||||||
|
end
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.add_whitelist(typ, value)
|
||||||
|
local c = uci.cursor()
|
||||||
|
local opt
|
||||||
|
|
||||||
|
if typ == "mac" then
|
||||||
|
typ = "validated_user"
|
||||||
|
opt = "mac"
|
||||||
|
elseif typ == "domain" then
|
||||||
|
typ = "validated_domain"
|
||||||
|
opt = "domain"
|
||||||
|
else
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local exist = false
|
||||||
|
c:foreach("wifidog-ng", typ, function(s)
|
||||||
|
if s[opt] == value then
|
||||||
|
exist = true
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
if not exist then
|
||||||
|
local s = c:add("wifidog-ng", typ)
|
||||||
|
c:set("wifidog-ng", s, opt, value)
|
||||||
|
c:commit("wifidog-ng")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.del_whitelist(typ, value)
|
||||||
|
local c = uci.cursor()
|
||||||
|
local opt
|
||||||
|
|
||||||
|
if typ == "mac" then
|
||||||
|
typ = "validated_user"
|
||||||
|
opt = "mac"
|
||||||
|
elseif typ == "domain" then
|
||||||
|
typ = "validated_domain"
|
||||||
|
opt = "domain"
|
||||||
|
else
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
c:foreach("wifidog-ng", typ, function(s)
|
||||||
|
if s[opt] == value then
|
||||||
|
c:delete("wifidog-ng", s[".name"])
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
c:commit("wifidog-ng")
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
46
net/wifidog-ng/files/wifidog-ng/heartbeat.lua
Normal file
46
net/wifidog-ng/files/wifidog-ng/heartbeat.lua
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
--[[
|
||||||
|
Copyright (C) 2018 Jianhui Zhao <jianhuizhao329@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||||
|
USA
|
||||||
|
--]]
|
||||||
|
|
||||||
|
local uloop = require "uloop"
|
||||||
|
local http = require "socket.http"
|
||||||
|
local util = require "wifidog-ng.util"
|
||||||
|
local config = require "wifidog-ng.config"
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local timer = nil
|
||||||
|
local start_time = os.time()
|
||||||
|
|
||||||
|
local function heartbeat()
|
||||||
|
local cfg = config.get()
|
||||||
|
|
||||||
|
timer:set(1000 * cfg.checkinterval)
|
||||||
|
|
||||||
|
local sysinfo = util.ubus("system", "info")
|
||||||
|
|
||||||
|
local url = string.format("%s&sys_uptime=%d&sys_memfree=%d&sys_load=%d&wifidog_uptime=%d",
|
||||||
|
cfg.ping_url, sysinfo.uptime, sysinfo.memory.free, sysinfo.load[1], os.time() - start_time)
|
||||||
|
http.request(url)
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.start()
|
||||||
|
timer = uloop.timer(heartbeat, 1000)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
128
net/wifidog-ng/files/wifidog-ng/ubus.lua
Normal file
128
net/wifidog-ng/files/wifidog-ng/ubus.lua
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
--[[
|
||||||
|
Copyright (C) 2018 Jianhui Zhao <jianhuizhao329@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||||
|
USA
|
||||||
|
--]]
|
||||||
|
|
||||||
|
local uci = require "uci"
|
||||||
|
local ubus = require "ubus"
|
||||||
|
local http = require "socket.http"
|
||||||
|
local auth = require "wifidog-ng.auth"
|
||||||
|
local config = require "wifidog-ng.config"
|
||||||
|
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local conn = nil
|
||||||
|
|
||||||
|
local ubus_codes = {
|
||||||
|
["INVALID_COMMAND"] = 1,
|
||||||
|
["INVALID_ARGUMENT"] = 2,
|
||||||
|
["METHOD_NOT_FOUND"] = 3,
|
||||||
|
["NOT_FOUND"] = 4,
|
||||||
|
["NO_DATA"] = 5,
|
||||||
|
["PERMISSION_DENIED"] = 6,
|
||||||
|
["TIMEOUT"] = 7,
|
||||||
|
["NOT_SUPPORTED"] = 8,
|
||||||
|
["UNKNOWN_ERROR"] = 9,
|
||||||
|
["CONNECTION_FAILED"] = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
local function reload_validated_domain()
|
||||||
|
local c = uci.cursor()
|
||||||
|
|
||||||
|
local file = io.open("/tmp/dnsmasq.d/wifidog-ng", "w")
|
||||||
|
|
||||||
|
c:foreach("wifidog-ng", "validated_domain", function(s)
|
||||||
|
file:write("ipset=/" .. s.domain .. "/wifidog-ng-ip\n")
|
||||||
|
end)
|
||||||
|
file:close()
|
||||||
|
|
||||||
|
os.execute("/etc/init.d/dnsmasq restart &")
|
||||||
|
end
|
||||||
|
|
||||||
|
local methods = {
|
||||||
|
["wifidog-ng"] = {
|
||||||
|
roam = {
|
||||||
|
function(req, msg)
|
||||||
|
local cfg = config.get()
|
||||||
|
|
||||||
|
if not msg.ip or not msg.mac then
|
||||||
|
return ubus_codes["INVALID_ARGUMENT"]
|
||||||
|
end
|
||||||
|
|
||||||
|
local url = string.format("%s&stage=roam&ip=%s&mac=%s", cfg.auth_url, msg.ip, msg.mac)
|
||||||
|
local r = http.request(url) or ""
|
||||||
|
local token = r:match("token=(%w+)")
|
||||||
|
if token then
|
||||||
|
auth.new_term(msg.ip, msg.mac, token)
|
||||||
|
end
|
||||||
|
end, {ip = ubus.STRING, mac = ubus.STRING }
|
||||||
|
},
|
||||||
|
term = {
|
||||||
|
function(req, msg)
|
||||||
|
if msg.action == "show" then
|
||||||
|
conn:reply(req, {terms = auth.get_terms()});
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not msg.action or not msg.mac then
|
||||||
|
return ubus_codes["INVALID_ARGUMENT"]
|
||||||
|
end
|
||||||
|
|
||||||
|
if msg.action == "add" then
|
||||||
|
auth.allow_user(mac)
|
||||||
|
elseif msg.action == "del" then
|
||||||
|
auth.deny_user(mac)
|
||||||
|
end
|
||||||
|
end, {action = ubus.STRING, mac = ubus.STRING }
|
||||||
|
},
|
||||||
|
whitelist = {
|
||||||
|
function(req, msg)
|
||||||
|
if not msg.action or not msg.type or not msg.value then
|
||||||
|
return ubus_codes["INVALID_ARGUMENT"]
|
||||||
|
end
|
||||||
|
|
||||||
|
if msg.action == "add" then
|
||||||
|
config.add_whitelist(msg.type, msg.value)
|
||||||
|
if msg.type == "mac" then
|
||||||
|
auth.allow_user(msg.value)
|
||||||
|
end
|
||||||
|
elseif msg.action == "del" then
|
||||||
|
config.del_whitelist(msg.type, msg.value)
|
||||||
|
if msg.type == "mac" then
|
||||||
|
auth.deny_user(msg.value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if msg.type == "domain" then
|
||||||
|
reload_validated_domain()
|
||||||
|
end
|
||||||
|
end, {action = ubus.STRING, type = ubus.STRING, value = ubus.STRING }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function M.init()
|
||||||
|
conn = ubus.connect()
|
||||||
|
if not conn then
|
||||||
|
error("Failed to connect to ubus")
|
||||||
|
end
|
||||||
|
|
||||||
|
conn:add(methods)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
83
net/wifidog-ng/files/wifidog-ng/util.lua
Normal file
83
net/wifidog-ng/files/wifidog-ng/util.lua
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
--[[
|
||||||
|
Copyright (C) 2018 Jianhui Zhao <jianhuizhao329@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||||
|
USA
|
||||||
|
--]]
|
||||||
|
|
||||||
|
local _ubus = require "ubus"
|
||||||
|
local _ubus_connection = nil
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.arp_get(ifname, ipaddr)
|
||||||
|
for l in io.lines("/proc/net/arp") do
|
||||||
|
local f = {}
|
||||||
|
|
||||||
|
for e in string.gmatch(l, "%S+") do
|
||||||
|
f[#f + 1] = e
|
||||||
|
end
|
||||||
|
|
||||||
|
if f[1] == ipaddr and f[6] == ifname then
|
||||||
|
return f[4]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.read_file(path, len)
|
||||||
|
local file = io.open(path, "r")
|
||||||
|
if not file then return nil end
|
||||||
|
|
||||||
|
if not len then len = "*a" end
|
||||||
|
|
||||||
|
local data = file:read(len)
|
||||||
|
file:close()
|
||||||
|
|
||||||
|
return data
|
||||||
|
end
|
||||||
|
|
||||||
|
local ubus_codes = {
|
||||||
|
"INVALID_COMMAND",
|
||||||
|
"INVALID_ARGUMENT",
|
||||||
|
"METHOD_NOT_FOUND",
|
||||||
|
"NOT_FOUND",
|
||||||
|
"NO_DATA",
|
||||||
|
"PERMISSION_DENIED",
|
||||||
|
"TIMEOUT",
|
||||||
|
"NOT_SUPPORTED",
|
||||||
|
"UNKNOWN_ERROR",
|
||||||
|
"CONNECTION_FAILED"
|
||||||
|
}
|
||||||
|
|
||||||
|
function M.ubus(object, method, data)
|
||||||
|
if not _ubus_connection then
|
||||||
|
_ubus_connection = _ubus.connect()
|
||||||
|
assert(_ubus_connection, "Unable to establish ubus connection")
|
||||||
|
end
|
||||||
|
|
||||||
|
if object and method then
|
||||||
|
if type(data) ~= "table" then
|
||||||
|
data = { }
|
||||||
|
end
|
||||||
|
local rv, err = _ubus_connection:call(object, method, data)
|
||||||
|
return rv, err, ubus_codes[err]
|
||||||
|
elseif object then
|
||||||
|
return _ubus_connection:signatures(object)
|
||||||
|
else
|
||||||
|
return _ubus_connection:objects()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
30
net/wifidog-ng/files/wifidog-ng/version.lua
Normal file
30
net/wifidog-ng/files/wifidog-ng/version.lua
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
--[[
|
||||||
|
Copyright (C) 2018 Jianhui Zhao <jianhuizhao329@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||||
|
USA
|
||||||
|
--]]
|
||||||
|
|
||||||
|
local M = {
|
||||||
|
MAJOR = 2,
|
||||||
|
MINOR = 0,
|
||||||
|
PATCH = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function M.string()
|
||||||
|
return string.format("%d.%d.%d", M.MAJOR, M.MINOR, M.PATCH)
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
18
net/wifidog-ng/src/Makefile
Normal file
18
net/wifidog-ng/src/Makefile
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
ifeq ($(findstring openwrt, $(CC)),)
|
||||||
|
ifneq ($(KERNELRELEASE),)
|
||||||
|
wifidog-ng-objs := main.o config.o
|
||||||
|
obj-m := wifidog-ng.o
|
||||||
|
else
|
||||||
|
KDIR = /lib/modules/$(shell uname -r)/build
|
||||||
|
|
||||||
|
all:
|
||||||
|
make -C $(KDIR) M=$(PWD) modules
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.o *.ko *.mod.c Module.* modules.* .*.cmd .tmp*
|
||||||
|
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
wifidog-ng-objs := main.o config.o
|
||||||
|
obj-m := wifidog-ng.o
|
||||||
|
endif
|
174
net/wifidog-ng/src/config.c
Normal file
174
net/wifidog-ng/src/config.c
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 jianhui zhao <jianhuizhao329@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/inetdevice.h>
|
||||||
|
#include <linux/seq_file.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
static struct proc_dir_entry *proc;
|
||||||
|
static struct config conf;
|
||||||
|
|
||||||
|
static int update_gw_interface(const char *interface)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
struct net_device *dev;
|
||||||
|
struct in_device *in_dev;
|
||||||
|
|
||||||
|
dev = dev_get_by_name(&init_net, interface);
|
||||||
|
if (!dev) {
|
||||||
|
pr_err("Not found interface: %s\n", interface);
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf.interface_ifindex = dev->ifindex;
|
||||||
|
|
||||||
|
in_dev = inetdev_by_index(dev_net(dev), conf.interface_ifindex);
|
||||||
|
if (!in_dev) {
|
||||||
|
pr_err("Not found in_dev on %s\n", interface);
|
||||||
|
ret = -ENOENT;
|
||||||
|
goto QUIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
for_primary_ifa(in_dev) {
|
||||||
|
conf.interface_ipaddr = ifa->ifa_local;
|
||||||
|
conf.interface_mask = ifa->ifa_mask;
|
||||||
|
conf.interface_broadcast = ifa->ifa_broadcast;
|
||||||
|
|
||||||
|
pr_info("Found ip from %s: %pI4\n", interface, &conf.interface_ipaddr);
|
||||||
|
break;
|
||||||
|
} endfor_ifa(in_dev)
|
||||||
|
|
||||||
|
QUIT:
|
||||||
|
dev_put(dev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int proc_config_show(struct seq_file *s, void *v)
|
||||||
|
{
|
||||||
|
seq_printf(s, "enabled(RW) = %d\n", conf.enabled);
|
||||||
|
seq_printf(s, "interface(RW) = %s\n", conf.interface);
|
||||||
|
seq_printf(s, "ipaddr(RO) = %pI4\n", &conf.interface_ipaddr);
|
||||||
|
seq_printf(s, "netmask(RO) = %pI4\n", &conf.interface_mask);
|
||||||
|
seq_printf(s, "broadcast(RO) = %pI4\n", &conf.interface_broadcast);
|
||||||
|
seq_printf(s, "port(RW) = %d\n", conf.port);
|
||||||
|
seq_printf(s, "ssl_port(RW) = %d\n", conf.ssl_port);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t proc_config_write(struct file *file, const char __user *buf, size_t size, loff_t *ppos)
|
||||||
|
{
|
||||||
|
char data[128];
|
||||||
|
char *delim, *key;
|
||||||
|
const char *value;
|
||||||
|
int update = 0;
|
||||||
|
|
||||||
|
if (size == 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (size > sizeof(data))
|
||||||
|
size = sizeof(data);
|
||||||
|
|
||||||
|
if (copy_from_user(data, buf, size))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
data[size - 1] = 0;
|
||||||
|
|
||||||
|
key = data;
|
||||||
|
while (key && *key) {
|
||||||
|
while (*key && (*key == ' '))
|
||||||
|
key++;
|
||||||
|
|
||||||
|
delim = strchr(key, '=');
|
||||||
|
if (!delim)
|
||||||
|
break;
|
||||||
|
|
||||||
|
*delim++ = 0;
|
||||||
|
value = delim;
|
||||||
|
|
||||||
|
delim = strchr(value, '\n');
|
||||||
|
if (delim)
|
||||||
|
*delim++ = 0;
|
||||||
|
|
||||||
|
if (!strcmp(key, "enabled")) {
|
||||||
|
conf.enabled = simple_strtol(value, NULL, 0);
|
||||||
|
if (conf.enabled)
|
||||||
|
update = 1;
|
||||||
|
pr_info("wifidog %s\n", conf.enabled ? "enabled" : "disabled");
|
||||||
|
} else if (!strcmp(key, "interface")) {
|
||||||
|
strncpy(conf.interface, value, sizeof(conf.interface) - 1);
|
||||||
|
update = 1;
|
||||||
|
} else if (!strcmp(key, "port")) {
|
||||||
|
conf.port = simple_strtol(value, NULL, 0);
|
||||||
|
} else if (!strcmp(key, "ssl_port")) {
|
||||||
|
conf.ssl_port = simple_strtol(value, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
key = delim;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update)
|
||||||
|
update_gw_interface(conf.interface);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int proc_config_open(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
return single_open(file, proc_config_show, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
const static struct file_operations proc_config_ops = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.open = proc_config_open,
|
||||||
|
.read = seq_read,
|
||||||
|
.write = proc_config_write,
|
||||||
|
.llseek = seq_lseek,
|
||||||
|
.release = single_release
|
||||||
|
};
|
||||||
|
|
||||||
|
int init_config(void)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
conf.interface_ifindex= -1;
|
||||||
|
conf.port = 2060;
|
||||||
|
conf.ssl_port = 8443;
|
||||||
|
strcpy(conf.interface, "br-lan");
|
||||||
|
|
||||||
|
proc = proc_mkdir(PROC_DIR_NAME, NULL);
|
||||||
|
if (!proc) {
|
||||||
|
pr_err("can't create dir /proc/"PROC_DIR_NAME"/\n");
|
||||||
|
return -ENODEV;;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!proc_create("config", 0644, proc, &proc_config_ops)) {
|
||||||
|
pr_err("can't create file /proc/"PROC_DIR_NAME"/config\n");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto remove;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
remove:
|
||||||
|
remove_proc_entry(PROC_DIR_NAME, NULL);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deinit_config(void)
|
||||||
|
{
|
||||||
|
remove_proc_entry("config", proc);
|
||||||
|
remove_proc_entry(PROC_DIR_NAME, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct config *get_config(void)
|
||||||
|
{
|
||||||
|
return &conf;
|
||||||
|
}
|
31
net/wifidog-ng/src/config.h
Normal file
31
net/wifidog-ng/src/config.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 jianhui zhao <jianhuizhao329@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __CONFIG_H_
|
||||||
|
#define __CONFIG_H_
|
||||||
|
|
||||||
|
#include <linux/proc_fs.h>
|
||||||
|
|
||||||
|
#define PROC_DIR_NAME "wifidog-ng"
|
||||||
|
|
||||||
|
struct config {
|
||||||
|
int enabled;
|
||||||
|
char interface[32];
|
||||||
|
int interface_ifindex;
|
||||||
|
__be32 interface_ipaddr;
|
||||||
|
__be32 interface_mask;
|
||||||
|
__be32 interface_broadcast;
|
||||||
|
int port;
|
||||||
|
int ssl_port;
|
||||||
|
};
|
||||||
|
|
||||||
|
int init_config(void);
|
||||||
|
void deinit_config(void);
|
||||||
|
struct config *get_config(void);
|
||||||
|
|
||||||
|
#endif
|
176
net/wifidog-ng/src/main.c
Normal file
176
net/wifidog-ng/src/main.c
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 jianhui zhao <jianhuizhao329@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#include <linux/ip.h>
|
||||||
|
#include <linux/tcp.h>
|
||||||
|
#include <linux/udp.h>
|
||||||
|
#include <net/netfilter/nf_nat.h>
|
||||||
|
#include <net/netfilter/nf_nat_l3proto.h>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#define IPS_HIJACKED (1 << 31)
|
||||||
|
#define IPS_ALLOWED (1 << 30)
|
||||||
|
|
||||||
|
static u32 wd_nf_nat_setup_info(void *priv, struct sk_buff *skb,
|
||||||
|
const struct nf_hook_state *state, struct nf_conn *ct)
|
||||||
|
{
|
||||||
|
struct config *conf = get_config();
|
||||||
|
struct tcphdr *tcph = tcp_hdr(skb);
|
||||||
|
union nf_conntrack_man_proto proto;
|
||||||
|
struct nf_nat_range newrange;
|
||||||
|
static uint16_t PORT_80 = htons(80);
|
||||||
|
|
||||||
|
proto.tcp.port = (tcph->dest == PORT_80) ? htons(conf->port) : htons(conf->ssl_port);
|
||||||
|
newrange.flags = NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED;
|
||||||
|
newrange.min_addr.ip = newrange.max_addr.ip = conf->interface_ipaddr;
|
||||||
|
newrange.min_proto = newrange.max_proto = proto;
|
||||||
|
|
||||||
|
ct->status |= IPS_HIJACKED;
|
||||||
|
|
||||||
|
return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 wifidog_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
|
||||||
|
{
|
||||||
|
struct config *conf = get_config();
|
||||||
|
struct iphdr *iph = ip_hdr(skb);
|
||||||
|
struct nf_conn *ct;
|
||||||
|
struct tcphdr *tcph;
|
||||||
|
struct udphdr *udph;
|
||||||
|
enum ip_conntrack_info ctinfo;
|
||||||
|
static uint16_t PORT_80 = htons(80); /* http */
|
||||||
|
static uint16_t PORT_443 = htons(443); /* https */
|
||||||
|
static uint16_t PORT_67 = htons(67); /* dhcp */
|
||||||
|
static uint16_t PORT_53 = htons(53); /* dns */
|
||||||
|
|
||||||
|
if (unlikely(!conf->enabled))
|
||||||
|
return NF_ACCEPT;
|
||||||
|
|
||||||
|
if (state->in->ifindex != conf->interface_ifindex)
|
||||||
|
return NF_ACCEPT;
|
||||||
|
|
||||||
|
/* Accept broadcast */
|
||||||
|
if (skb->pkt_type == PACKET_BROADCAST || skb->pkt_type == PACKET_MULTICAST)
|
||||||
|
return NF_ACCEPT;
|
||||||
|
|
||||||
|
/* Accept all to local area networks */
|
||||||
|
if ((iph->daddr | ~conf->interface_mask) == conf->interface_broadcast)
|
||||||
|
return NF_ACCEPT;
|
||||||
|
|
||||||
|
ct = nf_ct_get(skb, &ctinfo);
|
||||||
|
if (!ct || (ct->status & IPS_ALLOWED))
|
||||||
|
return NF_ACCEPT;
|
||||||
|
|
||||||
|
if (ct->status & IPS_HIJACKED) {
|
||||||
|
if (is_allowed_mac(skb, state)) {
|
||||||
|
/* Avoid duplication of authentication */
|
||||||
|
nf_reset(skb);
|
||||||
|
nf_ct_kill(ct);
|
||||||
|
}
|
||||||
|
return NF_ACCEPT;
|
||||||
|
} else if (ctinfo == IP_CT_NEW && (is_allowed_dest_ip(skb, state) || is_allowed_mac(skb, state))) {
|
||||||
|
ct->status |= IPS_ALLOWED;
|
||||||
|
return NF_ACCEPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (iph->protocol) {
|
||||||
|
case IPPROTO_TCP:
|
||||||
|
tcph = tcp_hdr(skb);
|
||||||
|
if(tcph->dest == PORT_53 || tcph->dest == PORT_67) {
|
||||||
|
ct->status |= IPS_ALLOWED;
|
||||||
|
return NF_ACCEPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tcph->dest == PORT_80 || tcph->dest == PORT_443)
|
||||||
|
goto redirect;
|
||||||
|
else
|
||||||
|
return NF_DROP;
|
||||||
|
|
||||||
|
case IPPROTO_UDP:
|
||||||
|
udph = udp_hdr(skb);
|
||||||
|
if(udph->dest == PORT_53 || udph->dest == PORT_67) {
|
||||||
|
ct->status |= IPS_ALLOWED;
|
||||||
|
return NF_ACCEPT;
|
||||||
|
}
|
||||||
|
return NF_DROP;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ct->status |= IPS_ALLOWED;
|
||||||
|
return NF_ACCEPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
redirect:
|
||||||
|
/* all packets from unknown client are dropped */
|
||||||
|
if (ctinfo != IP_CT_NEW || (ct->status & IPS_DST_NAT_DONE)) {
|
||||||
|
pr_debug("dropping packets of suspect stream, src:%pI4, dst:%pI4\n", &iph->saddr, &iph->daddr);
|
||||||
|
return NF_DROP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nf_nat_ipv4_in(priv, skb, state, wd_nf_nat_setup_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct nf_hook_ops wifidog_ops[] __read_mostly = {
|
||||||
|
{
|
||||||
|
.hook = wifidog_hook,
|
||||||
|
.pf = PF_INET,
|
||||||
|
.hooknum = NF_INET_PRE_ROUTING,
|
||||||
|
.priority = NF_IP_PRI_CONNTRACK + 1 /* after conntrack */
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init wifidog_init(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = init_config();
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 12, 14)
|
||||||
|
ret = nf_register_net_hooks(&init_net, wifidog_ops, ARRAY_SIZE(wifidog_ops));
|
||||||
|
#else
|
||||||
|
ret = nf_register_hooks(wifidog_ops, ARRAY_SIZE(wifidog_ops));
|
||||||
|
#endif
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("can't register hook\n");
|
||||||
|
goto remove_config;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("kmod of wifidog is started\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
remove_config:
|
||||||
|
deinit_config();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit wifidog_exit(void)
|
||||||
|
{
|
||||||
|
deinit_config();
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 12, 14)
|
||||||
|
nf_unregister_net_hooks(&init_net, wifidog_ops, ARRAY_SIZE(wifidog_ops));
|
||||||
|
#else
|
||||||
|
nf_unregister_hooks(wifidog_ops, ARRAY_SIZE(wifidog_ops));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pr_info("kmod of wifidog-ng is stop\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(wifidog_init);
|
||||||
|
module_exit(wifidog_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("jianhui zhao <jianhuizhao329@gmail.com>");
|
||||||
|
MODULE_LICENSE("GPL");
|
60
net/wifidog-ng/src/utils.h
Normal file
60
net/wifidog-ng/src/utils.h
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 jianhui zhao <jianhuizhao329@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __UTILS_H_
|
||||||
|
#define __UTILS_H_
|
||||||
|
|
||||||
|
#include <linux/netfilter/ipset/ip_set.h>
|
||||||
|
|
||||||
|
static inline int wd_ip_set_test(const char *name, const struct sk_buff *skb,
|
||||||
|
struct ip_set_adt_opt *opt, const struct nf_hook_state *state)
|
||||||
|
{
|
||||||
|
static struct xt_action_param par = { };
|
||||||
|
struct ip_set *set = NULL;
|
||||||
|
ip_set_id_t index;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
index = ip_set_get_byname(state->net, name, &set);
|
||||||
|
if (!set)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
|
||||||
|
par.net = state->net;
|
||||||
|
#else
|
||||||
|
par.state = state;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ret = ip_set_test(index, skb, &par, opt);
|
||||||
|
ip_set_put_byindex(state->net, index);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int is_allowed_mac(struct sk_buff *skb, const struct nf_hook_state *state)
|
||||||
|
{
|
||||||
|
static struct ip_set_adt_opt opt = {
|
||||||
|
.family = NFPROTO_IPV4,
|
||||||
|
.dim = IPSET_DIM_ONE,
|
||||||
|
.flags = IPSET_DIM_ONE_SRC,
|
||||||
|
.ext.timeout = UINT_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
return wd_ip_set_test("wifidog-ng-mac", skb, &opt, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int is_allowed_dest_ip(struct sk_buff *skb, const struct nf_hook_state *state)
|
||||||
|
{
|
||||||
|
static struct ip_set_adt_opt opt = {
|
||||||
|
.family = NFPROTO_IPV4,
|
||||||
|
.dim = IPSET_DIM_ONE,
|
||||||
|
.ext.timeout = UINT_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
return wd_ip_set_test("wifidog-ng-ip", skb, &opt, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in a new issue