├── Makefile ├── README.md └── files ├── 40-portal-clear ├── 40-portal-join ├── config └── portal-detect ├── init.d └── portal-detect ├── luci ├── cbi │ └── portal-detect.lua └── control │ └── portal-detect.lua └── portal-detect /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2019 GL.iNet 3 | # 4 | # This is free software, licensed under the GNU General Public License v3. 5 | # See /LICENSE for more information. 6 | # 7 | 8 | include $(TOPDIR)/rules.mk 9 | 10 | PKG_NAME:=gl-portal-detect 11 | PKG_VERSION:=3.0.14 12 | PKG_RELEASE:=1 13 | 14 | include $(INCLUDE_DIR)/package.mk 15 | 16 | define Package/gl-portal-detect 17 | SECTION:=base 18 | CATEGORY:=gl-inet 19 | TITLE+=Repeater automatically detect portal AP 20 | DEPENDS:=+dnsmasq-full +libcurl +libcares 21 | endef 22 | 23 | define Build/Compile 24 | endef 25 | 26 | define Package/gl-portal-detect/install 27 | $(INSTALL_DIR) $(1)/usr/bin 28 | $(CP) ./files/portal-detect $(1)/usr/bin 29 | $(INSTALL_DIR) $(1)/etc/hotplug.d/iface 30 | $(CP) ./files/40-portal-join $(1)/etc/hotplug.d/iface 31 | $(INSTALL_DIR) $(1)/etc/hotplug.d/net 32 | $(CP) ./files/40-portal-clear $(1)/etc/hotplug.d/net 33 | $(INSTALL_DIR) $(1)/etc/init.d 34 | $(CP) ./files/init.d/* $(1)/etc/init.d 35 | $(INSTALL_DIR) $(1)/etc/config 36 | $(CP) ./files/config/* $(1)/etc/config 37 | $(INSTALL_DIR) $(1)/usr/lib/lua/luci/controller/admin/ 38 | $(CP) ./files/luci/control/* $(1)/usr/lib/lua/luci/controller/admin/ 39 | $(INSTALL_DIR) $(1)/usr/lib/lua/luci/model/cbi/admin_network/ 40 | $(CP) ./files/luci/cbi/* $(1)/usr/lib/lua/luci/model/cbi/admin_network/ 41 | endef 42 | 43 | $(eval $(call BuildPackage,gl-portal-detect)) 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WiFi Captive Portal detection program 2 | 3 | ## Background 4 | 5 | When you connect to a WiFi network e.g. hotels or in public places, there will generally be a Captive portal page asking you for authorization information. If you don't get authorized in the portal page, you will not be able to use the Internet. 6 | 7 | When you use a WiFi router to connect to such networks, there are more things to deal with in order to let the portal page pop up correctly. You need to: 8 | 9 | * Disable all VPN connections in the router. 10 | * Disable DNS rebind protection. This is generally true but not for all portal. 11 | * Disable DNS encryption. In GL.iNet routers there are CloudFlare DNS. 12 | 13 | But if you use the router to protect data privacies, when you do the above, your data will be leaked. 14 | 15 | This program is used to solve this problem by manipulating the firewall without disabling your VPN and DNS protection manually while not leaking your data. 16 | 17 | ## How it works 18 | 19 | The program tries to detect if there is any WiFi portal page. If there is, it will allow the portal page to pass firewall and DNS encryption so that it will pop up in your client device connected to the router. After you authorize via the portal the firewall policy will be restored. 20 | 21 | ## Testing firmware URL 22 | 23 | [GL.iNet AR750S](http://download.gl-inet.com.s3-website.us-east-2.amazonaws.com/firmware/ar750s/testing/gl-ar750s-portal-detect.tar) http://download.gl-inet.com.s3-website.us-east-2.amazonaws.com/firmware/ar750s/testing/gl-ar750s-portal-detect.tar 24 | 25 | [GL.iNet MIFI](http://download.gl-inet.com.s3-website.us-east-2.amazonaws.com/firmware/mifi/testing/gl-mifi-portal-detect.bin) http://download.gl-inet.com.s3-website.us-east-2.amazonaws.com/firmware/mifi/testing/gl-mifi-portal-detect.bin 26 | 27 | 28 | ## How to use 29 | 30 | You can enable the service by executing the following command from the command line 31 | 32 | ``` 33 | uci set portal-detect.global.enable='1' 34 | uci commit portal-detect 35 | /etc/init.d/portal-detect restart 36 | ``` 37 | You can also enable it in the luci page.Its location is in admin-->network-->portal-detect. 38 | 39 | ## Pre-requisite 40 | 41 | This program requires the following three pakages. 42 | 43 | ``` 44 | libcares libcurl dnsmasq-full 45 | ``` 46 | 47 | ### How to compile 48 | 49 | First, we need curl to use libcares for asynchronous DNS resolution.[patch](https://github.com/gl-inet/openwrt/commit/2e032d245a642a3bdaa88d830edb063204be979f) 50 | ``` 51 | diff --git a/package/network/utils/curl/Makefile b/package/network/utils/curl/Makefile 52 | index db72640..ba857b8 100644 53 | --- a/package/network/utils/curl/Makefile 54 | +++ b/package/network/utils/curl/Makefile 55 | @@ -89,6 +89,7 @@ define Package/libcurl 56 | DEPENDS:= +LIBCURL_WOLFSSL:libwolfssl +LIBCURL_OPENSSL:libopenssl +LIBCURL_GNUTLS:libgnutls +LIBCURL_MBEDTLS:libmbedtls 57 | DEPENDS += +LIBCURL_ZLIB:zlib +LIBCURL_THREADED_RESOLVER:libpthread +LIBCURL_LDAP:libopenldap +LIBCURL_LIBIDN2:libidn2 58 | DEPENDS += +LIBCURL_SSH2:libssh2 +LIBCURL_NGHTTP2:libnghttp2 59 | + DEPENDS += libcares 60 | TITLE:=A client-side URL transfer library 61 | MENU:=1 62 | endef 63 | @@ -104,7 +105,7 @@ TARGET_LDFLAGS += -Wl,--gc-sections 64 | 65 | CONFIGURE_ARGS += \ 66 | --disable-debug \ 67 | - --disable-ares \ 68 | + --enable-ares \ 69 | --enable-shared \ 70 | --enable-static \ 71 | --disable-manual \ 72 | ``` 73 | 74 | Then,select packages 75 | ``` 76 | -*- libcares.. Library for asyncronous DNS Requests (including name resolves) 77 | -*- curl.................................. A client-side URL transfer utility 78 | <*> dnsmasq-full 79 | <*> gl-portal-detect................. Repeater automatically detect portal AP 80 | ``` 81 | 82 | Then compile the package 83 | 84 | ``` 85 | make ./package/portal-detect/compile 86 | ``` 87 | ## Install 88 | 89 | ``` 90 | opkg install gl-portal-detect 91 | ``` 92 | 93 | ## Note 94 | 95 | If that doesn't work, you might want to recompile curl and install it because it needs to support libares 96 | -------------------------------------------------------------------------------- /files/40-portal-clear: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (C) 2019 GL.iNet 3 | # 4 | # This is free software, licensed under the GNU General Public License v3. 5 | # See /LICENSE for more information. 6 | # 7 | SERVICE_DEV="" 8 | SERVICE_IF="" 9 | 10 | init_var() 11 | { 12 | local ifname 13 | config_get ifname global ifname 14 | SERVICE_IF=${ifname} 15 | } 16 | 17 | config_load portal-detect 18 | config_foreach init_var 19 | 20 | [ "$SERVICE_IF" = "" ] && exit 0 21 | 22 | [ "$ACTION" = "remove" -a "$DEVICENAME" = "$SERVICE_IF" ] && { 23 | for i in $(fuser /usr/bin/portal-detect);do 24 | kill $i 25 | done 26 | } 27 | -------------------------------------------------------------------------------- /files/40-portal-join: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (C) 2019 GL.iNet 3 | # 4 | # This is free software, licensed under the GNU General Public License v3. 5 | # See /LICENSE for more information. 6 | # 7 | SERVICE_EN="" 8 | SERVICE_IF="" 9 | 10 | init_var() 11 | { 12 | local enable ifname 13 | config_get ifname global ifname 14 | config_get enable global enable 15 | SERVICE_EN=${enable:-"0" "$enable"} 16 | SERVICE_IF=${ifname} 17 | } 18 | 19 | config_load portal-detect 20 | config_foreach init_var 21 | 22 | [ "$SERVICE_EN" = "0" -o "$SERVICE_IF" = "" ] && exit 0 23 | 24 | [ "$ACTION" = "ifup" -a "$DEVICE" = "$SERVICE_IF" ] && { 25 | /usr/bin/portal-detect join $DEVICE & 26 | } 27 | -------------------------------------------------------------------------------- /files/config/portal-detect: -------------------------------------------------------------------------------- 1 | 2 | config service 'global' 3 | option ifname 'wlan-sta' 4 | option testpoint '17.253.85.204' 5 | option enable '0' 6 | option timeout '5' 7 | option debug '1' 8 | option interval '10' 9 | 10 | -------------------------------------------------------------------------------- /files/init.d/portal-detect: -------------------------------------------------------------------------------- 1 | #!/bin/sh /etc/rc.common 2 | # Copyright (C) 2019 GL.iNet 3 | # 4 | # This is free software, licensed under the GNU General Public License v3. 5 | # See /LICENSE for more information. 6 | # 7 | 8 | START=99 9 | 10 | SERVICE_EN="" 11 | SERVICE_IF="" 12 | 13 | init_var() 14 | { 15 | local enable ifname 16 | config_get ifname global ifname 17 | config_get enable global enable 18 | SERVICE_EN=${enable:-"0" "$enable"} 19 | SERVICE_IF=${ifname} 20 | } 21 | 22 | config_load portal-detect 23 | config_foreach init_var 24 | 25 | start() 26 | { 27 | local status track 28 | track=$(uci get ucitrack.@portal-detect[0].init 2>/dev/null) 29 | [ -z "$track" ] && { 30 | uci add ucitrack portal-detect 31 | uci set ucitrack.@portal-detect[0]=portal-detect 32 | uci set ucitrack.@portal-detect[0].init=portal-detect 33 | uci commit ucitrack 34 | /etc/init.d/ucitrack restart 35 | } 36 | [ "$SERVICE_EN" = "1" -a "$SERVICE_IF" != "" ] && { 37 | INTERFACE=$(ubus call network.interface dump|jsonfilter -e "@.interface[@.l3_device='${SERVICE_IF}'].interface") 38 | status=$(ifstatus ${INTERFACE}|jsonfilter -e "@.up") 39 | [ "$status" = "true" ] && { 40 | env -i ACTION="ifup" DEVICE="${SERVICE_IF}" /sbin/hotplug-call iface 41 | } 42 | } 43 | } 44 | 45 | stop() 46 | { 47 | for i in $(fuser /usr/bin/portal-detect);do 48 | kill $i 49 | done 50 | while [ true ];do 51 | [ "$(fuser /usr/bin/portal-detect)" = "" ] && break 52 | sleep 1 53 | done 54 | } 55 | 56 | restart() 57 | { 58 | stop 59 | start 60 | } 61 | -------------------------------------------------------------------------------- /files/luci/cbi/portal-detect.lua: -------------------------------------------------------------------------------- 1 | m = Map("portal-detect", "Portal detect") 2 | 3 | s = m:section(NamedSection,"global","service") 4 | 5 | s:option(Flag, "enable", "Enable","Enable service") 6 | s:option(Value, "ifname","Device", "Device that need to be monitored.") 7 | s:option(Value, "testpoint","Test point","Server IP for probing.").datatype = "ip4addr" 8 | s:option(Value, "timeout","Timeout","Probe timeout,Effective range 1-60.").datatype = "range(1,60)" 9 | s:option(Value, "interval","Interval","Probe interval,Effective range 5-60.").datatype = "range(5,60)" 10 | s:option(Flag, "debug", "Debug","Enable debug mode") 11 | 12 | 13 | return m -- Returns the map 14 | -------------------------------------------------------------------------------- /files/luci/control/portal-detect.lua: -------------------------------------------------------------------------------- 1 | module("luci.controller.admin.portal-detect", package.seeall) 2 | 3 | function index() 4 | entry({"admin", "network", "portal-detect"}, cbi("admin_network/portal-detect"), _("PortalDetect"), 1) 5 | end 6 | -------------------------------------------------------------------------------- /files/portal-detect: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (C) 2019 GL.iNet 3 | # 4 | # This is free software, licensed under the GNU General Public License v3. 5 | # See /LICENSE for more information. 6 | # 7 | 8 | trap "clear_portal" INT TERM QUIT KILL 9 | [ -f /var/run/portal-detect.lock ] && exit 0 10 | 11 | . /lib/functions.sh 12 | 13 | ACTION=$1 14 | DEVICE=$2 15 | GATEWAY=$3 16 | INTERFACE="" 17 | DNS1="" 18 | DNS2="" 19 | 20 | LAN_IP="" 21 | AUTH_IP="" 22 | REDICT_URL="" 23 | RESULT=0 24 | DEBUG="" 25 | INTERVAL="" 26 | TIMEOUT="" 27 | PROBE_IP="" 28 | JOIN_FLAG=0 29 | 30 | LOGGER() 31 | { 32 | [ "$DEBUG" = "1" ] && logger $@ 33 | } 34 | 35 | init_var() 36 | { 37 | local testpoint debug timeout interval 38 | config_get testpoint global testpoint 39 | config_get debug global debug 40 | config_get timeout global timeout 41 | config_get interval global interval 42 | PROBE_IP=${testpoint:-"17.253.85.204" "$testpoint"} 43 | DEBUG=${debug:-"0" "$debug"} 44 | INTERVAL=${interval:-"10" "$interval"} 45 | TIMEOUT=${timeout:-"0" "$timeout"} 46 | LOGGER -t portal-init "probe-ip:$PROBE_IP debug:$DEBUG interval:$INTERVAL timeout:$TIMEOUT" 47 | } 48 | 49 | config_load portal-detect 50 | config_foreach init_var 51 | 52 | #To avoid long waits at exit time, we need to split the sleep 53 | split_sleep() 54 | { 55 | local counter=$1 56 | while [ true ];do 57 | [ "$counter" -le 0 ] && break 58 | sleep 1 59 | let counter=counter-1 60 | done 61 | } 62 | 63 | [ -z "$DEVICE" ] && { 64 | LOGGER portal-detect "Need a valid network device" 65 | exit 0 66 | } 67 | 68 | INTERFACE=$(ubus call network.interface dump|jsonfilter -e "@.interface[@.l3_device='${DEVICE}'].interface") 69 | 70 | [ -z "$INTERFACE" ] && { 71 | LOGGER portal-detect "Need a valid interface" 72 | exit 0 73 | } 74 | 75 | [ -z "$GATEWAY" ] && { 76 | GATEWAY=$(ifstatus ${INTERFACE}|jsonfilter -e @.route[0].nexthop) 77 | } 78 | 79 | [ -z "$DEVICE" -o -z "$GATEWAY" -o -z "$ACTION" ] && { 80 | LOGGER -t portal-prarm "$ACTION $DEVICE $GATEWAY" 81 | exit 0 82 | } 83 | 84 | touch /var/run/portal-detect.lock 85 | 86 | DNS1=$(ifstatus ${INTERFACE}|jsonfilter -e '@["dns-server"][0]') 87 | DNS2=$(ifstatus ${INTERFACE}|jsonfilter -e '@["dns-server"][1]') 88 | [ -z "$DNS1" ] && { 89 | DNS1="$GATEWAY" 90 | } 91 | [ -z "$DNS2" ] && { 92 | DNS2="$GATEWAY" 93 | } 94 | 95 | ipt_safe() 96 | { 97 | $@ 98 | [ ! $? = 0 ] && { 99 | LOGGER "iptables command execute failed, try again!" 100 | sleep 0.5 101 | $@ 102 | } 103 | 104 | } 105 | 106 | #Check the VPN and cloudflare and rebind protection 107 | check_vpn_dns() 108 | { 109 | local ov wg cf rb ss 110 | ov=$(uci get glconfig.openvpn.enable) 111 | wg=$( uci get wireguard.@proxy[0].enable) 112 | cf=$(uci get glconfig.general.cloudflare_dns) 113 | rb=$(uci get dhcp.@dnsmasq[0].rebind_protection) 114 | ss=$(pidof ss-redir) 115 | [ ! "$ss" = "" ] && { 116 | ipset add ss_spec_dst_bp ${PROBE_IP} 2>/dev/null 117 | } 118 | [ "$ov" != "1" -a "$wg" != "1" -a "$cf" != "1" -a "$rb" != "1" -a "$ss" = "" ] && return 0 119 | return 1 120 | } 121 | 122 | #Probe the portal AP 123 | probe_portal() 124 | { 125 | local wan_status 126 | LOGGER -t portal-probe "probe" 127 | check_vpn_dns 128 | [ $? = 0 ] && { 129 | LOGGER -t portal-probe "not any VPN or DNS setings, just return" 130 | RESULT=0 131 | return 0; 132 | } 133 | wan_status=$(cat /var/run/mwan3/iface_state/wan 2>/dev/null) 134 | [ "$wan_status" = "online" ] && { 135 | LOGGER -t portal-probe "wan is online, just return" 136 | RESULT=0 137 | return 0 138 | } 139 | tmp=$(curl --connect-timeout ${TIMEOUT} --interface $DEVICE --dns-interface $DEVICE --dns-servers $DNS1 ${PROBE_IP} \ 140 | -s -w "\n\t%{http_code}\t@@:@@%{redirect_url}" |grep -e http-equiv=\"refresh\" -e href=\"http -e @@:@@ >/tmp/portal_probe) 141 | [ $? = 0 ] || { 142 | RESULT=0 143 | return 0 144 | } 145 | http_code=$(cat /tmp/portal_probe|tail -n 1|awk -F '\t' '{print $2}') 146 | [ "$http_code" = 302 ] && { 147 | REDICT_URL=$(cat /tmp/portal_probe|tail -n 1|awk -F '/' '{print $3}') 148 | LOGGER -t portal-probe "httpcode:$http_code URL:$REDICT_URL" 149 | RESULT=2 150 | return 0 151 | } 152 | 153 | [ "$http_code" = 200 ] && { 154 | REDICT_URL=$(cat /tmp/portal_probe|head -n 1|awk -F '/' '{print $3}') 155 | LOGGER -t portal-probe "httpcode:$http_code URL:$REDICT_URL" 156 | RESULT=1 157 | [ -z "$REDICT_URL" ] && RESULT=0 158 | return 0 159 | } 160 | 161 | RESULT=0 162 | return 0 163 | } 164 | 165 | #Cycle check for authentication status 166 | poll_portal() 167 | { 168 | local wan_status 169 | LOGGER -t portal-probe "poll" 170 | while [ true ];do 171 | check_vpn_dns 172 | [ $? = 0 ] && { 173 | LOGGER -t portal-poll "not any VPN or DNS setings, just return" 174 | remove_portal 175 | break; 176 | } 177 | wan_status=$(cat /var/run/mwan3/iface_state/wan 2>/dev/null) 178 | [ "$wan_status" = "online" ] && { 179 | LOGGER -t portal-poll "wan online,stop portal poll" 180 | remove_portal 181 | break 182 | } 183 | 184 | tmp=$(curl --connect-timeout ${TIMEOUT} --interface $DEVICE --dns-interface $DEVICE --dns-servers $DNS1 ${PROBE_IP} \ 185 | -s -w "\n\t%{http_code}\t@@:@@" |grep -e http-equiv=\"refresh\" -e @@:@@ -e href=\"http >/tmp/portal_probe ) 186 | [ $? = 0 ] || continue 187 | http_code=$(cat /tmp/portal_probe|tail -n 1|awk -F '\t' '{print $2}') 188 | [ "$http_code" = 302 ] && { 189 | split_sleep 2 190 | continue 191 | } 192 | 193 | [ "$http_code" = 200 ] && { 194 | REDICT_URL=$(cat /tmp/portal_probe|head -n 1|awk -F '/' '{print $3}') 195 | [ -n "$REDICT_URL" ] && { 196 | split_sleep 2 197 | continue 198 | } 199 | } 200 | 201 | LOGGER -t portal-poll "authenticated" 202 | split_sleep 5 203 | remove_portal 204 | break 205 | 206 | done 207 | } 208 | 209 | join_portal() 210 | { 211 | 212 | #Use curl's DNS asynchronous resolution feature,which requires curl to support libcares 213 | [ $RESULT = 2 ] && { 214 | AUTH_IP=$(curl --connect-timeout ${TIMEOUT} --interface $DEVICE --dns-interface $DEVICE --dns-server $DNS1 -Ls -w "%{remote_ip}" ${PROBE_IP} -o /dev/null) 215 | } 216 | [ $RESULT = 1 ] && { 217 | AUTH_IP=$(curl --connect-timeout ${TIMEOUT} --interface $DEVICE --dns-interface $DEVICE --dns-server $DNS1 -Ls -w "%{remote_ip}" ${REDICT_URL} -o /dev/null) 218 | } 219 | [ -z "$AUTH_IP" ] && return 220 | 221 | LAN_IP=$(ifstatus lan | jsonfilter -e '@["ipv4-address"][0].address') 222 | [ -z "$LAN_IP" ] && { 223 | LOGGER -t portal-join "lan ip is null" 224 | return 225 | } 226 | JOIN_FLAG=1 227 | 228 | ipset create gl_portal_whitelist hash:net 229 | #Add the required IP for authentication to the white list 230 | ipset add gl_portal_whitelist ${GATEWAY}/32 231 | ipset add gl_portal_whitelist ${AUTH_IP}/32 232 | ipset add gl_portal_whitelist ${PROBE_IP}/32 233 | ipset add gl_portal_whitelist ${LAN_IP}/32 234 | ipset add gl_portal_whitelist ${DNS1}/32 235 | ipset add gl_portal_whitelist ${DNS2}/32 236 | 237 | echo "#temporary config" > /tmp/dnsmasq.d/portal-detect.conf 238 | #To prevent data leaks, we need to hijack the URL locally 239 | echo address=/.com/${PROBE_IP} >>/tmp/dnsmasq.d/portal-detect.conf 240 | echo address=/.net/${PROBE_IP} >>/tmp/dnsmasq.d/portal-detect.conf 241 | echo address=/.org/${PROBE_IP} >>/tmp/dnsmasq.d/portal-detect.conf 242 | echo address=/.top/${PROBE_IP} >>/tmp/dnsmasq.d/portal-detect.conf 243 | echo address=/.cn/${PROBE_IP} >>/tmp/dnsmasq.d/portal-detect.conf 244 | 245 | #Add commonly used portal explorer urls to the white list 246 | echo server=/${REDICT_URL}/$DNS1 >>/tmp/dnsmasq.d/portal-detect.conf 247 | echo ipset=/${REDICT_URL}/gl_portal_whitelist >>/tmp/dnsmasq.d/portal-detect.conf 248 | echo server=/apple.com/$DNS1 >>/tmp/dnsmasq.d/portal-detect.conf 249 | echo ipset=/apple.com/gl_portal_whitelist >>/tmp/dnsmasq.d/portal-detect.conf 250 | echo server=/${PROBE_IP}/$DNS1 >>/tmp/dnsmasq.d/portal-detect.conf 251 | echo ipset=/${PROBE_IP}/gl_portal_whitelist >>/tmp/dnsmasq.d/portal-detect.conf 252 | echo server=/ssl.google-analytics.com/$DNS1 >>/tmp/dnsmasq.d/portal-detect.conf 253 | echo ipset=/ssl.google-analytics.com/gl_portal_whitelist >>/tmp/dnsmasq.d/portal-detect.conf 254 | echo server=/captive.g.aaplimg.com/$DNS1 >>/tmp/dnsmasq.d/portal-detect.conf 255 | echo ipset=/captive.g.aaplimg.com/gl_portal_whitelist >>/tmp/dnsmasq.d/portal-detect.conf 256 | 257 | #Prevent authentication pages from being rejected, which can happen on some lans 258 | echo rebind-domain-ok=${REDICT_URL} >>/tmp/dnsmasq.d/portal-detect.conf 259 | 260 | #Read the Linux connection track mark associated with incoming DNS queries and set the same mark value on upstream traffic used to answer those queries. 261 | echo conntrack >>/tmp/dnsmasq.d/portal-detect.conf 262 | 263 | /etc/init.d/dnsmasq restart 264 | 265 | while [ true ];do 266 | [ ! -f /var/run/fw3.lock ] && break 267 | sleep 1 268 | LOGGER -t portal-join "wait for firewall free" 269 | done 270 | 271 | #Allows forwarding data that the IP address in the whitelist 272 | iptables -N GL_PORTAL_DETECT 273 | iptables -I FORWARD -j GL_PORTAL_DETECT 274 | iptables -I GL_PORTAL_DETECT -m set --match-set gl_portal_whitelist dst -j ACCEPT 275 | 276 | #Force local DNS resolution 277 | iptables -t nat -N GL_PORTAL_DETECT 278 | iptables -t nat -I PREROUTING -j GL_PORTAL_DETECT 279 | iptables -t nat -A GL_PORTAL_DETECT -p udp -m udp --dport 53 -j DNAT --to-destination $LAN_IP 280 | iptables -t nat -A GL_PORTAL_DETECT -p tcp -m tcp --dport 53 -j DNAT --to-destination $LAN_IP 281 | 282 | #The IP in the whitelist does not through SS 283 | iptables -t nat -C SS_SPEC_LAN_DG -m mark --mark 0x80/0x80 -j RETURN 284 | [ ! "$?" = "0" ] && iptables -t nat -I SS_SPEC_LAN_DG -m mark --mark 0x80/0x80 -j RETURN 285 | 286 | #Mark the IP address in the whitelist 287 | iptables -t mangle -N GL_PORTAL_DETECT 288 | iptables -t mangle -A PREROUTING -j GL_PORTAL_DETECT 289 | iptables -t mangle -A GL_PORTAL_DETECT -m set --match-set gl_portal_whitelist dst -m conntrack --ctstate NEW -j MARK --set-mark 0x80/0x80 290 | iptables -t mangle -A GL_PORTAL_DETECT -m mark --mark 0x80/0x80 -j CONNMARK --save-mark --nfmask 0x80 --ctmask 0x80 291 | iptables -t mangle -A GL_PORTAL_DETECT -m mark --mark 0x80/0x80 -j CONNMARK --restore-mark --nfmask 0x80 --ctmask 0x80 292 | 293 | #Let the IP address in the whitelist go to station device 294 | ip route add via $GATEWAY 0.0.0.0/1 dev ${DEVICE} table 50 295 | ip route add via $GATEWAY 128.0.0.0/1 dev ${DEVICE} table 50 296 | ip rule add fwmark 0x80/0x80 table 50 297 | 298 | 299 | } 300 | 301 | #Remove the rules 302 | remove_portal() 303 | { 304 | LOGGER -t portal-remove "remove" 305 | [ $JOIN_FLAG = 1 ] || return 306 | ip rule del fwmark 0x80/0x80 table 50 307 | ip route flush table 50 308 | 309 | while [ true ];do 310 | [ ! -f /var/run/fw3.lock ] && break 311 | sleep 1 312 | LOGGER -t portal-remove "wait for firewall free" 313 | done 314 | ipt_safe iptables -t mangle -D PREROUTING -j GL_PORTAL_DETECT 315 | ipt_safe iptables -t mangle -F GL_PORTAL_DETECT 316 | ipt_safe iptables -t mangle -X GL_PORTAL_DETECT 317 | 318 | ipt_safe iptables -t nat -D PREROUTING -j GL_PORTAL_DETECT 319 | ipt_safe iptables -t nat -F GL_PORTAL_DETECT 320 | ipt_safe iptables -t nat -X GL_PORTAL_DETECT 321 | 322 | iptables -t nat -C SS_SPEC_LAN_DG -m mark --mark 0x80/0x80 -j RETURN 323 | [ "$?" = "0" ] && iptables -t nat -D SS_SPEC_LAN_DG -m mark --mark 0x80/0x80 -j RETURN 324 | 325 | ipt_safe iptables -D FORWARD -j GL_PORTAL_DETECT 326 | ipt_safe iptables -F GL_PORTAL_DETECT 327 | ipt_safe iptables -X GL_PORTAL_DETECT 328 | 329 | [ -f /tmp/dnsmasq.d/portal-detect.conf ] && rm /tmp/dnsmasq.d/portal-detect.conf 330 | /etc/init.d/dnsmasq restart 331 | 332 | ipset destroy gl_portal_whitelist 333 | JOIN_FLAG=0 334 | } 335 | 336 | clear_portal() { 337 | remove_portal 338 | [ -f /var/run/portal-detect.lock ] && rm /var/run/portal-detect.lock 339 | exit 0 340 | } 341 | 342 | 343 | [ "$ACTION" = "join" ] && { 344 | while [ true ];do 345 | probe_portal 346 | [ $RESULT != 0 ] && { 347 | join_portal 348 | poll_portal 349 | } 350 | split_sleep ${INTERVAL} 351 | done 352 | } 353 | 354 | [ -f /var/run/portal-detect.lock ] && rm /var/run/portal-detect.lock 355 | exit $RESULT 356 | 357 | 358 | 359 | --------------------------------------------------------------------------------