Hotspot customisation
通过覆盖存储在路由器 FTP 服务器上的默认 HTML 文件来定制 HotSpot 认证页面,并在 HotSpot 配置文件中通过 'html-directory-override' 指定自定义目录。可以从 login.html、status.html 和 logout.html 等预定义页面中选择,也可以使用 $(username) 和 $(password) 等变量创建自定义页面。
Hotspot 定制
简介
你可以为每个 HotSpot 服务器创建一套完全不同的 servlet 页面,只需在 HotSpot 服务器配置文件 /ip/hotspot/profile 的 "html-directory-override" 属性中指定目录即可。创建服务器配置文件后,默认的 servlet 页面会立即复制到 "hotspot" 目录中。可以通过 FTP 客户端连接到路由器来访问该目录。你可以复制该目录,并使用本节手册中的信息按需修改页面。请注意,建议手动编辑文件,因为自动化 HTML 编辑工具可能会因删除变量或其他关键部分而损坏页面。完成内容修改后,你需要将修改后的内容上传到 HotSpot 路由器上的某个自定义目录,并将前面提到的 "html-directory-override" 属性值指向这个新的自定义 HTML 目录路径。
注意: 如果 "html-directory-override" 的值路径缺失或为空,则 hotspot 服务器将恢复为默认 HTML 文件。
可用页面
向用户显示的主要 HTML servlet 页面:
- redirect.html - 将用户重定向到另一个 URL(例如,重定向到登录页面)。
- login.html - 向用户显示并要求输入用户名和密码的登录页面。此页面可以接受以下参数:
- username - 用户名。
- password - 明文密码(在 PAP 认证的情况下)或 chap-id 变量、密码和 CHAP 质询的 MD5 哈希(在 CHAP 认证的情况下)。对于 trial 用户,该值用作电子邮件地址。
- dst - 重定向之前请求的原始 URL。登录成功后会打开该 URL。
- popup - 登录成功后是否弹出状态窗口。
- radius<id> - 以文本字符串形式将 <id> 标识的属性发送到 RADIUS 服务器(在使用 RADIUS 认证时;否则会丢失)。
- radius<id>u - 以无符号整数形式将 <id> 标识的属性发送到 RADIUS 服务器(在使用 RADIUS 认证时;否则会丢失)。
- radius<id>-<vnd-id> - 以文本字符串形式将 <id> 和供应商 ID <vnd-id> 标识的属性发送到 RADIUS 服务器(在使用 RADIUS 认证时;否则会丢失)。
- radius<id>-<vnd-id>u - 以无符号整数形式将 <id> 和供应商 ID <vnd-id> 标识的属性发送到 RADIUS 服务器(在使用 RADIUS 认证时;否则会丢失)。
- md5.js - 用于 MD5 密码哈希的 JavaScript。与 http-chap 登录方式一起使用。
- alogin.html - 客户端登录后显示的页面。它会弹出状态页面,并将浏览器重定向到最初请求的页面(即在他/她被重定向到 HotSpot 登录页面之前请求的页面)。
- status.html - 状态页面,显示客户端的统计信息。它还能够自动显示广告。
- logout.html - 注销页面,在用户注销后显示。显示已完成会话的最终统计信息。此页面可以接受以下附加参数:
- erase-cookie - 注销时是否从 HotSpot 服务器擦除 cookie(这样下次从同一浏览器登录时无法使用 cookie,在多用户环境中可能有用)。
- error.html - 错误页面,仅在发生致命错误时显示。
如果需要更多控制,还提供了一些其他页面:
- rlogin.html - 如果访问某个 URL 需要客户端授权,则将该客户端从该 URL 重定向到登录页面的页面
- rstatus.html - 与 rlogin.html 类似,仅在客户端已登录且原始 URL 未知时使用
- radvert.html - 将客户端重定向到预定的广告链接
- flogin.html - 如果发生了某些错误(例如用户名或密码无效),则替代 login.html 显示
- fstatus.html - 如果请求状态页面但客户端未登录,则替代重定向显示
- flogout.html - 如果请求注销页面但客户端未登录,则替代重定向显示
提供 Servlet 页面
HotSpot servlet 可识别 5 种不同的请求类型:
- 对远程主机的请求
- 如果用户已登录且广告已到显示时间,则显示 radvert.html。该页面会重定向到预定的广告页面。
- 如果用户已登录且没有为该用户安排广告,则提供所请求的页面。
- 如果用户未登录,但目标主机被 walled garden 允许,则也会提供该请求。
- 如果用户未登录,且目标主机被 walled garden 禁止,则显示 rlogin.html;如果找不到 rlogin.html,则使用 redirect.html 重定向到登录页面。
- 对 HotSpot 主机上 "/" 的请求
- 如果用户已登录,则显示 rstatus.html;如果找不到 rstatus.html,则使用 redirect.html 重定向到状态页面。
- 如果用户未登录,则显示 rlogin.html;如果找不到 rlogin.html,则使用 redirect.html 重定向到登录页面。
- 对 "/login" 页面的请求
- 如果用户已成功登录(或已经登录),则显示 alogin.html;如果找不到 alogin.html,则使用 redirect.html 重定向到最初请求的页面或状态页面(在没有给出原始目标页面的情况下)。
- 如果用户未登录(未提供用户名,也没有出现错误消息),则显示 login.html。
- 如果登录过程失败(提供了错误消息),则显示 flogin.html;如果找不到 flogin.html,则使用 login.html。
- 如果发生致命错误,则显示 error.html。
- 对 "/status" 页面的请求
- 如果用户已登录,则显示 status.html。
- 如果用户未登录,则显示 fstatus.html;如果找不到 fstatus.html,则使用 redirect.html 重定向到登录页面。
- 对 '/logout' 页面的请求
- 如果用户已登录,则显示 logout.html。
- 如果用户未登录,则显示 flogout.html;如果找不到 flogout.html,则使用 redirect.html 重定向到登录页面。
注意: 如果无法使用存储在路由器 FTP 服务器上的页面满足请求,则会显示 Error 404。
自定义 HotSpot 认证页面外观的方法有很多:
- 这些页面很容易修改。它们存储在路由器 FTP 服务器上你为相应 HotSpot 服务器配置文件选择的目录中。
- 通过更改客户端发送到 HotSpot servlet 的变量,可以将关键字数量减少到一个(用户名或密码;例如,客户端的 MAC 地址可以用作另一个值),甚至减少到零(许可协议;一些对所有用户通用的预定义值或客户端的 MAC 地址可以用作用户名和密码)。
- 注册可以在另一台服务器上进行(例如,能够处理信用卡的服务器)。可以将客户端的 MAC 地址传递给它,这样就无需手动输入这些信息。注册后,服务器应更改 RADIUS 数据库,使客户端能够在某段时间内登录。
要在 HTML 文件中的某个位置插入变量,使用 $(var_name) 语法,其中 "var_name" 是变量名(不带引号)。此构造可用于任何以 '/', '/login', '/status' 或 '/logout' 访问的 HotSpot HTML 文件,以及存储在 HotSpot 服务器上的任何文本或 HTML(.txt、.htm 或 .html)文件(流量计数器除外,它们仅在状态页面可用;以及 error、error-orig、chap-id、chap-challenge 和 popup 变量,它们仅在登录页面可用)。例如,要显示指向登录页面的链接,可以使用以下构造:
login
变量
所有 Servlet HTML 页面都使用变量来显示用户特定的值。变量名只出现在 servlet 页面的 HTML 源码中——HotSpot Servlet 会自动将它们替换为相应的值。对于大多数变量,括号中给出了其可能值的示例。所有描述的变量在所有 servlet 页面中都有效,但其中一些在访问时可能为空(例如,用户登录前没有 uptime)。
可用变量列表
注意: 部分变量使用硬编码的 http URL;如果你使用的是 https,可以用其他方式构造链接,例如对于 $link-status,可以使用 https://$(hostname)/$(target-dir)status
通用服务器变量:
- hostname - HotSpot Servlet 的 DNS 名称或 IP 地址(如果未给出 DNS 名称)("hotspot.example.net")。
- identity - RouterOS 身份名称("MikroTik")。
- login-by - 用户使用的认证方法。
- plain-passwd - 表示是否允许 HTTP-PAP 登录方式的 "yes/no" 值("no")。
- server-address - HotSpot 服务器地址("10.5.50.1:80")。
- ssl-login - 表示访问该 servlet 页面是否使用了 HTTPS 方法的 "yes/no" 值("no")。
- server-name - HotSpot 服务器名称(在
/ip/hotspot菜单中作为 name 属性设置)。
链接:
- link-login - 指向登录页面的链接,包含请求的原始 URL("http://10.5.50.1/login?dst=http://www.example.com/")
- link-login-only - 指向登录页面的链接,不包含请求的原始 URL("http://10.5.50.1/login")
- link-logout - 指向注销页面的链接("http://10.5.50.1/logout")
- link-status - 指向状态页面的链接("http://10.5.50.1/status")
- link-orig - 请求的原始 URL("http://www.example.com/")
通用客户端信息:
- domain - 用户的域名("example.com")。
- interface-name - 物理 HotSpot 接口名称(对于网桥接口,将返回实际的网桥端口名称)。
- ip - 客户端的 IP 地址("10.5.50.2")。
- logged-in - 如果用户已登录则为 "yes",否则为 "no"("yes")。
- mac - 用户的 MAC 地址("01:23:45:67:89:AB")。
- trial - 表示用户是否有权使用 trial 时间的 "yes/no" 值。如果用户的 trial 时间已过期,则值为 "no"。
- username - 用户名("John")。
- host-ip - 来自
/ip/hotspot/host表的客户端 IP 地址。 - vlan-id - 表示客户端所连接的 VLAN 接口 ID。
用户状态信息:
- idle-timeout - 空闲超时("20m",如果没有则为 "")。
- idle-timeout-secs - 以秒为单位的空闲超时("88",如果没有此类超时则为 "0")。
- limit-bytes-in - 发送的字节限制("1000000",如果没有限制则为 "---")。
- limit-bytes-out - 接收的字节限制("1000000",如果没有限制则为 "---")。
- refresh-timeout - 状态页面刷新超时("1m30s",如果没有则为 "")。
- refresh-timeout-secs - 以秒为单位的状态页面刷新超时("90s",如果没有则为 "0")。
- session-timeout - 用户剩余的会话时间("5h",如果没有则为 "")。
- session-timeout-secs - 用户剩余的会话时间,以秒为单位("3475",如果没有此类超时则为 "0")。
- session-time-left - 用户剩余的会话时间("5h",如果没有则为 "")。
- session-time-left-secs - 用户剩余的会话时间,以秒为单位("3475",如果没有此类超时则为 "0")。
- uptime - 当前会话运行时长("10h2m33s")。
- uptime-secs - 以秒为单位的当前会话运行时长("125")。
流量计数器,仅在状态页面可用:
- bytes-in - 从用户接收的字节数("15423")。
- bytes-in-nice - 从用户接收的字节数的用户友好形式("15423")。
- bytes-out - 发送到用户的字节数("11352")。
- bytes-out-nice - 发送到用户的字节数的用户友好形式("11352")。
- packets-in - 从用户接收的数据包数("251")。
- packets-out - 发送到用户的数据包数("211")。
- remain-bytes-in - 达到 limit-bytes-in 之前的剩余字节数("337465",如果没有限制则为 "---")。
- remain-bytes-out - 达到 limit-bytes-out 之前的剩余字节数("124455",如果没有限制则为 "---")。
其他变量:
- session-id - 最后一次请求中 'session-id' 参数的值。
- var - 最后一次请求中 'var' 参数的值。
- error - 如果某处失败,显示错误消息("invalid username or password")。
- error-orig - 如果某处失败,显示原始错误消息(未从 errors.txt 中获取翻译)("invalid username or password")。
- chap-id - chap ID 的值("\371")。
- chap-challenge - chap challenge 的值("\357\015\330\013\021\234\145\245\303\253\142\246\133\175\375\316")。
- popup - 是否弹出复选框("true" 或 "false")。
- advert-pending - 是否有待显示的广告("yes" 或 "no")。
- http-status - 允许设置 http 状态码和消息。
- http-header - 允许设置 http 头。
与 RADIUS 相关的变量:
- radius<id> - 以文本字符串形式显示 <id> 标识的属性(在使用 RADIUS 认证时;否则为 "")
- radius<id>u - 以无符号整数形式显示 <id> 标识的属性(在使用 RADIUS 认证时;否则为 "0")
- radius<id>-<vnd-id> - 以文本字符串形式显示 <id> 和供应商 ID <vnd-id> 标识的属性(在使用 RADIUS 认证时;否则为 "")
- radius<id>-<vnd-id>u - 以无符号整数形式显示 <id> 和供应商 ID <vnd-id> 标识的属性(在使用 RADIUS 认证时;否则为 "0")
使用变量
$(if <var_name>) 语句可以在这些页面中使用。如果 <var_name> 的值不是空字符串,则会包含以下内容。它等价于 $(if <var_name> != "")。也可以进行等价比较:$(if <var_name> == <value>)。这些语句一直生效,直到 $(elif <var_name>)、$(else) 或 $(endif)。一般情况下看起来是这样的:
some content, which will always be displayed
$(if username == john)
Hey, your username is john
$(elif username == dizzy)
Hello, Dizzy! How are you? Your administrator.
$(elif ip == 10.1.2.3)
You are sitting at that old computer, which is so slow...
$(elif mac == 00:01:02:03:04:05)
This is an ethernet card, which was stolen few months ago...
$(else)
I don't know who you are, so lets live in peace.
$(endif)
other content, which will always be displayed
这些表达式中只会显示一个。显示哪一个取决于每个客户端的这些变量值。
重定向和自定义头
$(if http-status == 302)Hotspot login required$(endif)
$(if http-header == "Location")$(link-redirect)$(endif)
注意: 尽管上面的写法看起来使用了条件表达式 'if',但实际上它是在将 'http-status' 设置为 '302',而不是对其进行测试。变量 'http-header' 也是如此。再次强调,尽管它使用了 'if',但实际上它是在将该变量设置为 'Location',后跟由变量 'link-redirect' 设置的 URL。
例如,当 $(link-redirect) 的计算结果为 "http://192.168.88.1/login" 时,返回给客户端的 HTTP 响应将变为:
HTTP/1.0 302 Hotspot login required
<regular HTTP headers>
Location: http://192.168.88.1/login
http-status 语法:
$(if http-status == XYZ)HTTP_STATUS_MESSAGE$(endif)
- XYZ - 你希望返回的状态码。应为 3 位十进制数字,第一位不能为 0。
- HTTP_STATUS_MESSAGE - 你希望返回给客户端的任意文本,它将跟在 HTTP 回复中上述状态码之后。
在任何 HTTP 响应中,它将位于第一行,如下所示:
HTTP/1.0 XYZ HTTP_STATUS_MESSAGE
http-header 语法:
$(if http-header == HTTP_HEADER_NAME)HTTP_HEADER_VALUE$(endif)
- HTTP_HEADER_NAME - 响应中要发送的 HTTP 头名称
- HTTP_HEADER_VALUE - 响应中要发送的名为 HTTP_HEADER_NAME 的 HTTP 头的值
HTTP 响应将如下所示:
HTTP_HEADER_NAME: HTTP_HEADER_VALUE
HTTP_HEADER_VALUE 和 HTTP_STATUS_MESSAGE 中的所有变量和条件表达式都会按常规处理。
如果添加了多个同名头,只使用最后一个(之前的将被丢弃)。这允许系统覆盖常规 HTTP 头(例如 Content-Type 和 Cache-Control)。
自定义错误消息
所有错误消息都存储在相应 HotSpot servlet 目录中的 errors.txt 文件里。你可以更改所有这些消息并将其翻译成你的母语。为此,请编辑 errors.txt 文件。你也可以在消息中使用变量。该文件中给出了所有说明。
HotSpot 页面的多个版本
支持为同一 HotSpot 服务器设置多套 HotSpot 页面。它们可以由用户选择(以选择语言),或由 JavaScript 自动选择(以选择 PDA/常规版本的 HTML 页面)。
要使用此功能,请在 HotSpot HTML 目录中创建子目录,并将那些不同的 HTML 文件放入该子目录。例如,要将所有内容翻译成拉脱维亚语,可以创建子目录 "lv",其中包含翻译成拉脱维亚语的 login.html、logout.html、status.html、alogin.html、radvert.html 和 errors.txt 文件。如果在请求的子目录中找不到所请求的 HTML 页面,则将使用主目录中相应的 HTML 文件。主 login.html 文件将包含指向 "/lv/login?dst=$(link-orig-esc)" 的链接,随后显示拉脱维亚语版的登录页面:Latviski。而拉脱维亚语版本将包含指向英语版本的链接:English
引用目录的另一种方式是指定 'target' 变量:
Latviski
English
选择首选目录后(例如 "lv"),所有指向本地 HotSpot 页面的链接都将包含该路径(例如 $(link-status) = "http://hotspot.mt.lv/lv/status")。因此,如果所有 HotSpot 页面都使用 "$(link-xxx)" 变量引用链接,就无需再做其他更改——每个客户端将始终停留在所选的目录中。
其他
如果你想使用 HTTP-CHAP 认证方法,则需要在登录表单的 Submit action 之前包含 doLogin() 函数(它引用了必须已加载的 md5.js)。否则,CHAP 登录将失败。
在 HTTP-CHAP 方法中,要发送到 HotSpot 网关的最终密码由以下内容的拼接进行 MD5 哈希后形成:chap-id、用户的密码和 chap-challenge(按此顺序)
如果要在链接中直接使用变量,则必须对其进行相应的转义。例如,在登录页面中,如果用户名为 "123&456=1 2",则 link 无法按预期工作。在这种情况下,必须使用 $(user) 的转义版本:$(user-esc):link。现在,相同的用户名将被转换为 "123%26456%3D1+2",这是 "123&456=1 2" 在 URL 中的有效表示。此技巧可用于任何变量,而不仅是 $(username)。
注销页面有一个布尔参数 "erase-cookie",它可以为 "on" 或 "true",用于在注销时删除用户 cookie(这样用户下次打开浏览器时就不会被自动登录)。
示例
具备基本的 HTML 语言知识并参考以下示例,应该很容易实现上述想法:
- 要提供预定义的用户名值,请在 login.html 中将:
<input type="text" name="username" value="$(username)">
更改为:
<input type="hidden" name="username" value="hsuser">
(其中 hsuser 是你要提供的用户名)
- 要提供预定义的密码值,请在 login.html 中更改。
<input type="password">
更改为:
<input type="hidden" name="password" value="hspass">
(其中 hspass 是你要提供的密码)
- 以以下形式将客户端的 MAC 地址发送到注册服务器:
https://www.example.com/register.html?mac=XX:XX:XX:XX:XX:XX
将 login.html 中的登录按钮链接更改为:
https://www.example.com/register.html?mac=$(mac)
(你应将链接更正为指向你的服务器)
- 要在用户登录后显示横幅,请在 alogin.html 中,在
$(if popup == 'true') 之后添加以下行:
open('http://www.example.com/your-banner-page.html', 'my-banner-name','');
(你应将链接更正为指向你想要显示的页面)
- 要选择登录后显示的其他页面,请在 login.html 中将:
<input type="hidden" name="dst" value="$(link-orig)">
更改为:
<input type="hidden" name="dst" value="http://www.example.com">
(你应将链接更正为指向你的服务器)
- 要在注销时擦除 cookie,请在包含注销链接的页面(例如 status.html)中将。
open('$(link-logout)', 'hotspot_logout', ...
更改为:
open('$(link-logout)?erase-cookie=on', 'hotspot_logout', ...
或改为添加以下行:
<input type="hidden" name="erase-cookie" value="on">
在这一行之前:
<input type="submit" value="log off">
外部认证
另一个示例是让 HotSpot 在远程服务器上进行认证(例如,该服务器可以处理信用卡扣款):
- 在 walled-garden 中允许对外部服务器的直接访问(基于 HTTP 或基于 IP)。
- 修改 HotSpot servlet 的登录页面,以重定向到外部认证服务器。外部服务器应按需修改 RADIUS 数据库。
以下是要放在 HotSpot 路由器上的此类登录页面的示例(它重定向到 https://auth.example.com/login.php,请替换为外部认证服务器的实际地址):
<html>
<title>...</title>
<body>
<form name="redirect" action="https://auth.example.com/login.php" method="post">
<input type="hidden" name="mac" value="$(mac)">
<input type="hidden" name="ip" value="$(ip)">
<input type="hidden" name="username" value="$(username)">
<input type="hidden" name="link-login" value="$(link-login)">
<input type="hidden" name="link-orig" value="$(link-orig)">
<input type="hidden" name="error" value="$(error)">
</form>
<script language="JavaScript">
</script>
</body>
</html>
- 外部服务器可以通过将 HotSpot 客户端重定向回原始 HotSpot servlet 登录页面并指定正确的用户名和密码,来登录该客户端。
以下是此类页面的示例(它重定向到 https://hotspot.example.com/login,请将其替换为 HotSpot 路由器的实际地址;此外,它在登录成功后显示 www.mikrotik.com,请替换为所需内容):
<html>
<title>Hotspot login page</title>
<body>
<form name="login" action="https://hotspot.example.com/login" method="post">
<input type="text" name="username" value="demo">
<input type="password" name="password" value="none">
<input type="hidden" name="domain" value="">
<input type="hidden" name="dst" value="http://www.mikrotik.com/">
<input type="submit" name="login" value="log in">
</form>
</body>
</html>
- Hotspot 会询问 RADIUS 服务器是否允许登录。如果允许,将显示 alogin.html 页面(可以修改它以执行任何操作)。如果不允许,将显示 flogin.html(或 login.html)页面,该页面会将客户端重定向回外部认证服务器。
注意: 如这些示例所示,可以使用 HTTPS 协议和 POST 方法来保护通信。
HTTP 头检测
Hotspot 登录页面可以通过使用 $(http-header-name); 来访问 HTTP 头。
例如,可以检查用户代理(或浏览器),如果需要,它将返回任何其他内容而不是常规登录页面。例如,这可用于禁用手机中的自动弹窗。
例如,要为特定 Firefox 移动版本的用户输出 "SUCCESS" 而不是登录页面,你可以在 hotspot 目录中的 rlogin.html 页面顶部添加以下行:
$(if user-agent == "Mozilla/5.0 (Android; Mobile; rv:40.0) Gecko/40.0 Firefox/40.0" )
<HTML><HEAD><TITLE>Success</TITLE></HEAD><BODY>Success</BODY></HTML>
$(else)
---- regular content of rlogin.html page ----
$(endif)
这将为 Android Firefox 40 用户禁用登录弹窗。
一键登录
可以为不需要用户名或密码的场景创建一个用于快速一键登录的修改版 captive portal。
你需要做的是:
- 为此目的创建一个用户。例如,用户名为 "notsosecretuser",密码为 "notsosecretpass"。
- 将此用户分配到一个允许特定/不限数量并发活动用户的用户配置文件。
- 复制路由器文件菜单根目录下已生成的原始 hotspot 目录。
- 修改此副本目录的内容。
- 只需修改一个文件即可使其工作,即 "login.html"。
原始内容:
login
<input style={{width: '80px'}} name="username" type="text" value="$(username)"/>
password
<input style={{width: '80px'}} name="password" type="password"/>
<input type="submit" value="OK" />
修改后:
login
<input style={{width: '80px'}} name="username" type="text" value="notsosecretuser"/>
password
<input style={{width: '80px'}} name="password" type="password" value="notsosecretpass"/>
<input type="submit" value="Proceed to Internet!" />
更改的内容:
-
-
- 用户和密码 "" 字段被隐藏。
- 用户和密码字段值都包含预定义值。
- 将 "OK" 按钮的值(名称)更改为更合适的内容。
-
-
现在将这个新的 hotspot 文件夹上传回路由器,最好使用不同的名称。
-
更改 hotspot 服务器配置文件中的设置,以使用这个新的 html 目录。
/ip/hotspot/profile/set (profile number or name) html-directory-override=(dir path/name)
防火墙定制
概述
除了 /ip/hotspot 子菜单本身中明显的动态条目(如 hosts 和 active users)之外,激活 HotSpot 服务时还会在防火墙表中添加一些附加规则。
NAT
从 /ip/firewall/nat/print dynamic 命令中,你可以得到类似以下内容(每条规则后面都附有注释):
0 D chain=dstnat action=jump jump-target=hotspot hotspot=from-client
将所有与 HotSpot 相关的任务针对来自所有 HotSpot 客户端的数据包放入一个单独的链中。
1 I chain=hotspot action=jump jump-target=pre-hotspot
在 HotSpot 规则生效之前应执行的任何操作都应放在 pre-hotspot 链中。该链完全由管理员控制,不包含系统设置的任何规则,因此会出现无效的 jump 规则(因为默认情况下该链没有任何规则)。
2 D chain=hotspot action=redirect to-ports=64872 dst-port=53 protocol=udp
3 D chain=hotspot action=redirect to-ports=64872 dst-port=53 protocol=tcp
将所有 DNS 请求重定向到 HotSpot 服务。64872 端口为所有 HotSpot 用户提供 DNS 服务。如果你希望 HotSpot 服务器监听其他端口,请以相同方式在此处添加规则,并更改 dst-port 属性。
4 D chain=hotspot action=redirect to-ports=64873 hotspot=local-dst dst-port=80
protocol=tcp
将所有 HTTP 登录请求重定向到 HTTP 登录 servlet。64873 是 HotSpot HTTP servlet 端口。
5 D chain=hotspot action=redirect to-ports=64875 hotspot=local-dst dst-port=443
protocol=tcp
将所有 HTTPS 登录请求重定向到 HTTPS 登录 servlet。64875 是 HotSpot HTTPS servlet 端口。
6 D chain=hotspot action=jump jump-target=hs-unauth hotspot=!auth protocol=tcp
来自未授权客户端的除 DNS 和登录请求之外的所有其他数据包都应通过 hs-unauth 链。
7 D chain=hotspot action=jump jump-target=hs-auth hotspot=auth protocol=tcp
而来自已授权客户端的数据包则通过 hs-auth 链。
8 D ;;; www.mikrotik.com
chain=hs-unauth action=return dst-address=66.228.113.26 dst-port=80 protocol=tcp
hs-unauth 链中的第一项是放入 /ip/hotspot/walled-garden/ip 子菜单中所有影响 TCP 协议的内容(即协议未设置或设置为 TCP 的所有内容)。这里我们将 www.mikrotik.com 排除在重定向到登录页面之外。
9 D chain=hs-unauth action=redirect to-ports=64874 dst-port=80 protocol=tcp
所有其他 HTTP 请求都被重定向到监听端口 64874 的 Walled Garden 代理服务器。如果 /ip/hotspot/walled-garden 菜单中存在针对某个 HTTP 请求的 "allow" 条目,则该请求会被转发到目标地址。否则,请求将自动重定向到 HotSpot 登录 servlet(端口 64873)。
10 D chain=hs-unauth action=redirect to-ports=64874 dst-port=3128 protocol=tcp
11 D chain=hs-unauth action=redirect to-ports=64874 dst-port=8080 protocol=tcp
HotSpot 默认假设只有这些端口可用于 HTTP 代理请求。这两个条目用于"捕获"客户端对未知代理的请求(你可以在此处为其他端口添加更多规则),即让使用未知代理设置的客户端能够与 HotSpot 系统配合工作。此功能称为 "Universal Proxy"。如果检测到客户端正在使用某个代理服务器,系统将自动用 HTTP hotspot mark 标记这些数据包,以绕过未知代理问题,我们稍后会看到这一点。请注意,所使用的端口(64874)与规则 #9 中 HTTP 请求使用的端口相同(因此 HTTP 和 HTTP 代理请求由同一段代码处理)。
12 D chain=hs-unauth action=redirect to-ports=64875 dst-port=443 protocol=tcp
HTTPS 代理监听端口 64875。
13 I chain=hs-unauth action=jump jump-target=hs-smtp dst-port=25 protocol=tcp
SMTP 协议的重定向也可以在 HotSpot 配置中定义。如果定义了,则会在 hs-smtp 链中放入一条重定向规则。这样做是为了让使用未知 SMTP 配置的用户能够通过服务提供商(你)的 SMTP 服务器发送邮件,而不是发往用户在其计算机上配置的 [可能在其原始网络之外不可用的] SMTP 服务器。该链默认为空,因此会出现无效的 jump 规则。
14 D chain=hs-auth action=redirect to-ports=64874 hotspot=http protocol=tcp
为已授权用户提供 HTTP 代理服务。已认证用户的请求可能需要进行透明代理("Universal Proxy" 技术和广告功能)。此 HTTP mark 会自动添加到 HotSpot HTTP 代理(监听端口 64874 的那个)检测到的服务器的 HTTP 代理请求上,作为对未知代理服务器的 HTTP 代理请求。这样做是为了让具有某些代理设置的用户使用 HotSpot 网关,而不是发往用户在其计算机上配置的 [可能在其原始网络之外不可用的] 代理服务器。当需要向用户显示广告时,以及对于配置为透明代理其请求的用户发出的任何 HTTP 请求,也会应用此标记。
15 I chain=hs-auth action=jump jump-target=hs-smtp dst-port=25 protocol=tcp
为已授权用户提供 SMTP 代理(与规则 #13 相同)。
数据包过滤
从 /ip/firewall/filter/print dynamic 命令中,你可以得到类似以下内容(每条规则后面都附有注释):
0 D chain=forward action=jump jump-target=hs-unauth hotspot=from-client,!auth
任何从路由器穿越、来自未授权客户端的数据包都会被发送到 hs-unauth 链。hs-unauth 实现基于 IP 的 Walled Garden 过滤。
1 D chain=forward action=jump jump-target=hs-unauth-to hotspot=to-client,!auth
所有通过路由器到达客户端的内容都会被重定向到另一个称为 hs-unauth-to 的链。该链应拒绝未授权发往这些客户端的请求。
2 D chain=input action=jump jump-target=hs-input hotspot=from-client
所有从客户端发往路由器本身的内容都会到达另一个称为 hs-input 的链。
3 I chain=hs-input action=jump jump-target=pre-hs-input
在继续处理 [预定义的] 动态规则之前,数据包会先进入由管理员控制的 pre-hs-input 链,该链默认为空,因此该 jump 规则处于无效状态。
4 D chain=hs-input action=accept dst-port=64872 protocol=udp
5 D chain=hs-input action=accept dst-port=64872-64875 protocol=tcp
允许客户端访问本地认证和代理服务(如前所述)。
6 D chain=hs-input action=jump jump-target=hs-unauth hotspot=!auth
来自未授权客户端发往路由器本身的所有其他流量都将以与穿越路由器的流量相同的方式处理。
7 D chain=hs-unauth action=return protocol=icmp
8 D ;;; www.mikrotik.com
chain=hs-unauth action=return dst-address=66.228.113.26 dst-port=80 protocol=tcp
与仅在 NAT 表中添加与 TCP 协议相关的 Walled Garden 条目不同,在数据包过滤中,hs-unauth 链会添加到你在 /ip/hotspot/walled-garden/ip 菜单中设置的所有内容中。这就是为什么尽管你在 NAT 表中只看到一个条目,而这里却有两条规则。
9 D chain=hs-unauth action=reject reject-with=tcp-reset protocol=tcp
10 D chain=hs-unauth action=reject reject-with=icmp-net-prohibited
所有其他未被 Walled Garden 加入白名单的内容都将被拒绝。请注意使用 TCP Reset 来拒绝 TCP 连接。
11 D chain=hs-unauth-to action=return protocol=icmp
12 D ;;; www.mikrotik.com
chain=hs-unauth-to action=return src-address=66.228.113.26 src-port=80 protocol=tcp
对于发往客户端的数据包(hs-unauth-to 链)也执行与规则 #7 和 #8 相同的操作。
13 D chain=hs-unauth-to action=reject reject-with=icmp-host-prohibited
使用 ICMP reject 消息拒绝所有发往客户端的数据包。