wifidog-ng: Update to 2.0.0
authorJianhui Zhao <redacted>
Thu, 26 Jul 2018 15:28:41 +0000 (23:28 +0800)
committerJianhui Zhao <redacted>
Thu, 26 Jul 2018 15:40:25 +0000 (23:40 +0800)
Signed-off-by: Jianhui Zhao <redacted>
17 files changed:
net/wifidog-ng/Makefile
net/wifidog-ng/files/ssl.crt [moved from net/wifidog-ng/files/wifidog-ng.crt with 100% similarity]
net/wifidog-ng/files/ssl.key [moved from net/wifidog-ng/files/wifidog-ng.key with 100% similarity]
net/wifidog-ng/files/wifidog-ng.config
net/wifidog-ng/files/wifidog-ng.init
net/wifidog-ng/files/wifidog-ng.lua [new file with mode: 0644]
net/wifidog-ng/files/wifidog-ng/auth.lua [new file with mode: 0644]
net/wifidog-ng/files/wifidog-ng/config.lua [new file with mode: 0644]
net/wifidog-ng/files/wifidog-ng/heartbeat.lua [new file with mode: 0644]
net/wifidog-ng/files/wifidog-ng/ubus.lua [new file with mode: 0644]
net/wifidog-ng/files/wifidog-ng/util.lua [new file with mode: 0644]
net/wifidog-ng/files/wifidog-ng/version.lua [new file with mode: 0644]
net/wifidog-ng/src/Makefile [new file with mode: 0644]
net/wifidog-ng/src/config.c [new file with mode: 0644]
net/wifidog-ng/src/config.h [new file with mode: 0644]
net/wifidog-ng/src/main.c [new file with mode: 0644]
net/wifidog-ng/src/utils.h [new file with mode: 0644]

index f7b6e4ee77478c340964b95ab0e94ea42f630693..ecaf540445daaca0a51c2261fb59fb8d59c13cea 100644 (file)
@@ -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.
 # See /LICENSE for more information.
@@ -8,15 +8,10 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=wifidog-ng
-PKG_VERSION:=1.5.6
+PKG_VERSION:=2.0.0
 PKG_RELEASE:=1
 
-PKG_SOURCE_PROTO:=git
-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_BUILD_DIR=$(BUILD_DIR)/$(PKG_NAME)-$(BUILD_VARIANT)
 
 PKG_LICENSE:=LGPL-2.1
 PKG_LICENSE_FILES:=LICENSE
@@ -24,15 +19,14 @@ PKG_LICENSE_FILES:=LICENSE
 PKG_MAINTAINER:=Jianhui Zhao <jianhuizhao329@gmail.com>
 
 include $(INCLUDE_DIR)/package.mk
-include $(INCLUDE_DIR)/cmake.mk
 
 define Package/wifidog-ng/default
   SUBMENU:=Captive Portals
   SECTION:=net
   CATEGORY:=Network
-  TITLE:=Next generation WifiDog
-  DEPENDS:=+kmod-wifidog-ng +libuci +libuclient +libblobmsg-json +libubus +libcares \
-         +ipset +libpcap
+  TITLE:=Next generation WifiDog implemented in Lua
+  DEPENDS:=+kmod-wifidog-ng +libubox-lua +libuci-lua +libubus-lua \
+         +ipset +dnsmasq-full +luasocket
 endef
 
 define Package/wifidog-ng-nossl
@@ -68,13 +62,14 @@ endef
 
 define Package/wifidog-ng/default/install
        $(INSTALL_DIR) $(1)/usr/bin $(1)/etc/init.d $(1)/etc/config \
-               $(1)/etc/wifidog-ng $(1)//etc/hotplug.d/dhcp
-       $(INSTALL_BIN) $(PKG_BUILD_DIR)/src/wifidog-ng $(1)/usr/bin
+               $(1)/etc/wifidog-ng $(1)//etc/hotplug.d/dhcp $(1)/usr/lib/lua
+       $(INSTALL_BIN) ./files//wifidog-ng.lua $(1)/usr/bin/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.key $(1)/etc/wifidog-ng
-       $(INSTALL_CONF) ./files/wifidog-ng.crt $(1)/etc/wifidog-ng
+       $(INSTALL_CONF) ./files/ssl.key $(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
+       $(CP) ./files/wifidog-ng $(1)/usr/lib/lua
 endef
 
 Package/wifidog-ng-nossl/install = $(Package/wifidog-ng/default/install)
@@ -88,14 +83,13 @@ define KernelPackage/wifidog-ng
   SUBMENU:=Other modules
   TITLE:=Kernel module for wifidog-ng
   DEPENDS:=+kmod-nf-nat +kmod-ipt-ipset
-  FILES:=$(PKG_BUILD_DIR)/kmod/wifidog-ng.ko
+  FILES:=$(PKG_BUILD_DIR)/wifidog-ng.ko
 endef
 
 include $(INCLUDE_DIR)/kernel-defaults.mk
 
 define Build/Compile
-       $(call Build/Compile/Default)
-       $(MAKE) $(KERNEL_MAKEOPTS) SUBDIRS="$(PKG_BUILD_DIR)"/kmod modules
+       $(MAKE) $(KERNEL_MAKEOPTS) SUBDIRS="$(PKG_BUILD_DIR)" modules
 endef
 
 $(eval $(call BuildPackage,wifidog-ng-nossl))
index b3002403062305e88777f45a94947bfbc1b0ac4a..c4c15039c12f882bc7eed3b0e6860ddec0a1cca0 100644 (file)
@@ -1,13 +1,14 @@
 config gateway
     option enabled 1
-    option ifname 'br-lan'
+    option interface 'lan'
     option port 2060
+    option dhcp_host_white 1
     option ssl_port 8443
     option checkinterval 30
     option client_timeout 5
     option temppass_time 30
  
-config authserver
+config server
     option host 'authserver.com'
     option port 80
     option ssl 0
@@ -16,10 +17,4 @@ config authserver
     option portal_path 'portal'
     option msg_path 'gw_message.php'
     option ping_path 'ping'
-    option auth_path 'auth'
-
-config popularserver
-    list server www.baidu.com
-    list server www.qq.com
-
-config whitelist
+    option auth_path 'auth'
\ No newline at end of file
index 145ee6dc5eb663d30d5af5b2ce4d7adc0a808c32..f39373278aea29071b895ada8c7ca826e6698ad9 100644 (file)
@@ -5,45 +5,117 @@ START=95
 
 BIN=/usr/bin/wifidog-ng
 
-parse_whitelist_mac() {
+dhcp_host_white=1
+
+start_wifidog() {
+    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 ] && {
+        echo "validation gateway failed" >&2
+        exit 1
+    }
+
+    [ $enabled -eq 1 ] || exit 0
+
+    # timeout = 49 days
+    ipset -! create wifidog-ng-mac hash:mac timeout 4294967
+    ipset -! create wifidog-ng-ip hash:ip
+
+    modprobe wifidog-ng
+    echo "enabled=1" > /proc/wifidog-ng/config
+    
+    procd_open_instance
+    procd_set_param command $BIN
+    procd_set_param respawn
+    procd_close_instance
+}
+
+parse_server() {
     local cfg="$1"
-    local mac
+    local host
 
-    uci_validate_section wifidog-ng whitelist "${1}" \
+    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 whitelist_mac failed" >&2
+        echo "validation validated_user failed" >&2
         exit 1
     }
+
+    [ -n "$mac" ] && ipset add wifidog-ng-mac $mac
 }
 
-parse_whitelist_domain() {
+parse_validated_domain() {
     local cfg="$1"
     local domain
 
-    uci_validate_section wifidog-ng whitelist "${1}" \
+    uci_validate_section wifidog-ng validated_domain "${1}" \
         'domain:host'
 
     [ $? -ne 0 ] && {
-        echo "validation whitelist_domain failed" >&2
+        echo "validation validated_domain failed" >&2
         exit 1
     }
+
+    [ -n "$domain" ] && echo "ipset=/$domain/wifidog-ng-ip" >> /tmp/dnsmasq.d/wifidog-ng
 }
 
-start_service() {
-    modprobe 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 parse_whitelist_mac whitelist_mac
-    config_foreach parse_whitelist_domain whitelist_domain
+    config_foreach start_wifidog gateway
 
-    procd_open_instance
-    procd_set_param command $BIN
-    procd_set_param respawn
-    procd_close_instance
+    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() {
     rmmod wifidog-ng
+
+    ipset destroy wifidog-ng-mac
+    ipset destroy wifidog-ng-ip
 }
diff --git a/net/wifidog-ng/files/wifidog-ng.lua b/net/wifidog-ng/files/wifidog-ng.lua
new file mode 100644 (file)
index 0000000..c6a63d6
--- /dev/null
@@ -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()
diff --git a/net/wifidog-ng/files/wifidog-ng/auth.lua b/net/wifidog-ng/files/wifidog-ng/auth.lua
new file mode 100644 (file)
index 0000000..63666a4
--- /dev/null
@@ -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
diff --git a/net/wifidog-ng/files/wifidog-ng/config.lua b/net/wifidog-ng/files/wifidog-ng/config.lua
new file mode 100644 (file)
index 0000000..04f3d00
--- /dev/null
@@ -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
diff --git a/net/wifidog-ng/files/wifidog-ng/heartbeat.lua b/net/wifidog-ng/files/wifidog-ng/heartbeat.lua
new file mode 100644 (file)
index 0000000..0649be2
--- /dev/null
@@ -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
diff --git a/net/wifidog-ng/files/wifidog-ng/ubus.lua b/net/wifidog-ng/files/wifidog-ng/ubus.lua
new file mode 100644 (file)
index 0000000..a2c57a2
--- /dev/null
@@ -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
diff --git a/net/wifidog-ng/files/wifidog-ng/util.lua b/net/wifidog-ng/files/wifidog-ng/util.lua
new file mode 100644 (file)
index 0000000..bf14e7d
--- /dev/null
@@ -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
diff --git a/net/wifidog-ng/files/wifidog-ng/version.lua b/net/wifidog-ng/files/wifidog-ng/version.lua
new file mode 100644 (file)
index 0000000..feb4819
--- /dev/null
@@ -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
diff --git a/net/wifidog-ng/src/Makefile b/net/wifidog-ng/src/Makefile
new file mode 100644 (file)
index 0000000..f829d5d
--- /dev/null
@@ -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
diff --git a/net/wifidog-ng/src/config.c b/net/wifidog-ng/src/config.c
new file mode 100644 (file)
index 0000000..3ccad84
--- /dev/null
@@ -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;
+}
diff --git a/net/wifidog-ng/src/config.h b/net/wifidog-ng/src/config.h
new file mode 100644 (file)
index 0000000..a7414af
--- /dev/null
@@ -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
diff --git a/net/wifidog-ng/src/main.c b/net/wifidog-ng/src/main.c
new file mode 100644 (file)
index 0000000..96da9a6
--- /dev/null
@@ -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");
diff --git a/net/wifidog-ng/src/utils.h b/net/wifidog-ng/src/utils.h
new file mode 100644 (file)
index 0000000..5f7aa08
--- /dev/null
@@ -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
git clone https://git.99rst.org/PROJECT