拉脱维亚公司MikroTik的路由器、交换机等硬件产品及其配套的操作系统RouterOS是对普通用户友好的准专业网络配置方案。
近日,为了调整VLAN而把路由器搞炸了(从任何接口都无法连进去改配置),懒得找串口线,直接重置路由。特此记录一下重新配置过程中的一些坑。
本文基于RouterOS 7.19.3
版本,仅针对家庭主路由用途,且假设路由器已经重置并适用了默认配置(例如防火墙设置了默认规则,已经建立了bridge并把物理LAN口添加了进去)。全部配置都可以使用WinBox或Web完成,本文列出命令主要是为了方便描述。
第一部分,PPPOE接口的注意事项
RouterOS的默认防火墙配置中,masquerade规则有一个out-interface-list=WAN
,而如果你没有使用Quick Set来做引导式配置的话,自己新建的pppoe interface是不会自动被加入WAN这个interface list的。因此你需要:
/interface list member
add interface=pppoe-out1 list=WAN
第二部分,关于VLAN
我使用VLAN的目的不是为了在二层隔离VLAN,这样自由度太低,适合子网需要严格隔离的大组织。另一方面,家庭网络结构混乱,又不可能分什么接入/汇聚/核心交换机,还不是随便找个网口插上,同在客厅的交换机上的几个设备可能就需要不同的VLAN,在家用路由器有限的接口上根本不可能划出专门用于哪个VLAN的物理接口。
因此我的方案是,通过交换机/AC+AP对某些特定的设备(比如从某个特定SSID连入WIFI的设备)打VLAN tag,然后对不同的VLAN分配不同的子网,通过三层防火墙来控制它们之间的互访,类似于(但不同于)RouterOS官方文档中的InterVLAN Routing by Bridge,也被称作Layer3 VLAN。
在下面的例子里,普通设备(比如电脑)通过无VLAN tag的流量接入路由器,这样配置最为简单;IoT设备根据特定的SSID被AP打上VLAN id=5的tag,以便后面配置防火墙和主要设备的子网隔离。
设置一个不包括在bridge内的lan口
这是一个保险动作,不是必须的,但我强烈推荐这么做。比如,我从bridge里面删掉ether5,平时也不使用这个物理接口。当你在bridge上错误配置VLAN而无法连入路由器(我就是因此而重置路由的),你就可以通过这个接口连入路由来修改。
为了通过IP连入,你需要给这个接口分配一个IP(当然连入的设备也要手动设定IP);为了通过MAC连入,你需要把这个接口加入interface list LAN,因为/tool mac-server默认allowed-interface-list=LAN。
/interface bridge port
remove [find interface=ether5]
/ip address
add address=192.168.250.1/24 interface=ether5 network=192.168.250.0
/interface list member
add interface=ether5 list=LAN
新建VLAN interface并在其上配置dhcp server
如果你只是像我一样,想为不同的VLAN配置不同的dhcp服务器,分配不同的子网,那么在interface中新建虚拟的VLAN接口,确保这些接口基于bridge(而不是物理接口),并在这些VLAN接口上分别新建dhcp服务器就好了。
按照如下设置,一个设备的流量如果被打上了VLAN id=5的tag,它就会被分配到192.168.5.0/24的地址,之后别忘了为限制它而配置防火墙,此处略过防火墙配置。
/interface vlan
add interface=bridge name=vlan5 vlan-id=5
/interface list member
add interface=vlan5 list=LAN
/ip pool
add name=vlan5 ranges=192.168.5.10-192.168.5.250
/ip dhcp-server
add add-arp=yes address-pool=vlan5 interface=vlan5 name=server5
/ip dhcp-server network
add address=192.168.5.0/24 dns-server=192.168.5.1 gateway=192.168.5.1 netmask=24
/ip address
add address=192.168.5.1/24 interface=vlan5 network=192.168.5.0
(可选,看完再配置!)打开bridge VLAN filtering
从字面意义上看,如果我们要在路由器上设置VLAN,就需要打开这个功能,但这实际上是个误解。你只需要按照上一节的内容设置VLAN接口,已经被打了VLAN tag的流量就会被正确处理。如果你的家庭网络规模较小,又不想踩坑,可以完全不打开bridge VLAN filtering。
但bridge VLAN filtering
确实是官方推荐的配置。RouterOS在v7版本支持了同时使用bridge VLAN filtering
和hardware offloading
之后,所有的官方文档都推荐你在配置VLAN时使用这个功能。
Bridge VLAN Filtering provides VLAN-aware Layer 2 forwarding and VLAN tag modifications within the bridge. This set of features makes bridge operation more similar to a traditional Ethernet switch and allows overcoming Spanning Tree compatibility issues compared to the configuration when VLAN interfaces are bridged.
但是,这个功能很容易导致你被锁在路由器外面……以下的代码块是我写的原理分析和吐槽,只想知道结论的人可以跳过:
在打开bridge VLAN filtering功能前,你需要知道:
- 打开bridge VLAN filtering,路由器的交换芯片将可以处理VLAN tag,工作在二层,因此本段中我们简称它为交换机
- 进入交换机的数据帧如果没有VLAN tag,会被相应的端口根据其PVID打上tag
- bridge自己也有PVID
- 你需要设置Bridge VLAN table。在这个table中,你需要对所有你会在该交换机中用到的VLAN id,分别设置tagged和untagged组
- tagged和untagged的意思如下:
# Bridge VLAN table represents per-VLAN port mapping with an egress VLAN tag action. The tagged ports send out frames with a corresponding VLAN ID tag. The untagged ports remove a VLAN tag before sending out frames.
# - tagged (interfaces; Default: none) Interfaces or interface list with a VLAN tag adding action in egress. This setting accepts comma-separated values. e.g. tagged=ether1,ether2.
# - untagged (interfaces; Default: none) Interfaces or interface list with a VLAN tag removing action in egress. This setting accepts comma-separated values. e.g. untagged=ether3,ether4.
- 如果你需要某个VLAN的流量或无标签流量访问bridge(管理路由器/访问网关/访问bridge上的VLAN接口等,基本上是家用路由的大部分用途),你需要把bridge interface(具体见下面吐槽)加入Bridge VLAN table
如果在Bridge VLAN table(简称table)中没有正确配置相应的VLAN id或其tagged和untagged组,会有如下情况:
1 如果一个数据帧在进入交换机之前已经打上了VLAN id=2的tag,进入的物理接口是ether2
1.1 table中没有VLAN id=2,帧会被丢弃
1.2 table中VLAN id=2的tagged和untagged组都不包含ether2,帧会被丢弃
1.3 table中VLAN id=2的untagged组包含ether2,帧会被删除VLAN tag,向客户端发送的帧会失去VLAN tag,无法到达客户端
1.4 接下来,如果没有出现前述错误,而这是一个访问bridge的帧
1.4.1 table中VLAN id=2的tagged和untagged组都不包含bridge interface,帧会被丢弃
1.4.2 table中VLAN id=2的untagged组包含bridge interface,帧会被删除VLAN tag,可能错误地允许某个VLAN访问bridge上的默认网关
2 如果一个数据帧在进入交换机之前没有VLAN tag,进入的物理接口是ether2,ether2和bridge的PVID=1
2.1 table中没有VLAN id=1,帧会被丢弃(因为这个无VLAN tag的帧在进入ether时会被按照接口的PVID加上tag)
2.2 table中VLAN id=1的tagged和untagged组都不包含ether2,帧会被丢弃
2.3 table中VLAN id=1的tagged组包含ether2,向客户端发送的帧会被保留VLAN id=1的tag,无法到达客户端
2.4 接下来,如果没有出现前述错误,而这是一个访问bridge的帧
2.4.1 table中VLAN id=1的tagged和untagged组都不包含bridge interface,帧会被丢弃
2.4.2 table中VLAN id=1的tagged组包含bridge interface,帧在离开bridge interface(而不是工作在三层的bridge,见下面吐槽)的时候会保留VLAN id=1的tag,无法访问无VLAN的bridge,只能访问bridge上VLAN id=1的VLAN interface,这违反了设计目的
吐槽:
- 1.1/1.2/2.1/2.2 体现在官方文档中Bridge Interface Setup的`ingress-filtering`设置中,不在`Bridge VLAN Filtering`条目下,虽然不好找但问题不大;
- 1.3/2.3 符合官方文档对于tagged和untagged的介绍,是最容易理解的部分;
- 1.4.1/2.4.1 体现在官方文档的警告中,还算明确;
- ---以下是最难理解的部分,也是核心原理---
- 1.4.2/2.4.2 虽然符合官方的例子(下面结论中的两个例子),但完全从文档中无法直接读出来。我能想到加入bridge VLAN table中的bridge interface指的是交换机芯片连接CPU的端口(文档里也提了一嘴,`Though it is possible in certain situations to allow a packet to be processed by the CPU, this is usually called a packet forwarding to the switch CPU port (or the bridge interface in bridge VLAN filtering scenario)`),但绝对想不到这个bridge和你要访问的、绑定了默认dhcp server和默认网关IP的bridge不是一回事(或者说是同一个东西的二层和三层)。
- bridge VLAN table中的bridge interface指的是交换机芯片连接路由器CPU的端口,这个端口的egress指的是访问路由器CPU,工作在二层,从而受tagged/untagged影响
- 绑定了默认网关IP的bridge指的是路由器CPU内部的网桥,绑定IP作为默认网关的功能工作在三层,不会传输而会终结VLAN tag
- bridge VLAN table中的bridge interface的PVID=1,但绑定了默认网关IP的bridge却还是只接受无VLAN tag的流量(这简直是故意误导用户),接受VLAN id=1流量(即终结VLAN tag)的是bridge.VLAN1
再思考:
ether2到bridge算不算egress?VLAN tag是否经历了添加-删除-添加-删除的过程?为什么bridge和untagged的物理接口的PVID要一样?官方文档没说。
我尝试将ether2设置为PVID 1,bridge设置为PVID 9,通过package sniffer来看,任何接口上都没有VLAN id=1的包,ether2上有VLAN id=9的tx包(即向lan设备发出的包,src ip是公网),这能说明从公网进入bridge的包被bridge加了VLAN id 9,但不能用于回答上面的问题,累了。
顺便一说,这样的情况下在某些物理接口上出现了不少跟它不可能有关系的包,不知道是package sniffer工具的混乱,还是路由本身乱了,总之建议保持bridge和untagged的物理接口的PVID一致。
结论:
- 对于已经打上VLAN tag的帧,应该将VLAN id加入
Bridge VLAN table
,并设置所有可能进入的物理接口和bridge为这个VLAN id的tagged
组成员 - 对于没有打上VLAN tag的帧,如果不需要访问bridge本身,而只需要访问bridge上的VLAN interface从而访问路由器(官方文档推荐配置),应该将物理接口和bridge的PVID(应该一样)作为VLAN id加入
Bridge VLAN table
,并设置所有可能进入的物理接口为这个VLAN id的untagged
组成员,bridge设为tagged
组成员,再添加正确的VLAN interface
。如官方文档例子1 - 对于没有打上VLAN tag的帧,如果需要访问bridge本身(在家庭网络中几乎是必然的),应该将物理接口和bridge的PVID(应该一样)作为VLAN id加入
Bridge VLAN table
,并设置所有可能进入的物理接口和bridge为这个VLAN id的untagged
组成员,如官方文档例子2
如果你配错了,没打VLAN tag的流量就没法通过IP或者MAC(因为VLAN tag在二层)连入路由器。注意,有很多网卡驱动中设置VLAN tag的功能并不可靠,可能和路由的VLAN并不兼容,因而你只能通过无标签流量访问路由器,我的电脑就是。
在本文的例子里,我的配置是:
/interface bridge VLAN
add bridge=bridge untagged=bridge,ether2,ether3,ether4 vlan-ids=1
add bridge=bridge tagged=bridge,ether2,ether3,ether4 vlan-ids=5
/interface bridge set bridge vlan-filtering=yes
请确保你一切都配置好了,再执行最后一步的set bridge vlan-filtering=yes。
bridge(三层意义上)是没有VLAN tag的,不要配置一个dhcp服务器来为VLAN 1服务
正如上文所述,绑定了默认网关IP的bridge处理的流量是不含VLAN tag的,如果你并没有把bridge设置在VLAN id=1的tagged组,就不需要在bridge上设置一个VLAN1 interface,更不要在这个接口上运行dhcp,我观测到明显的路由器性能下降(但是写这篇文章的时候又观测不到了,可能有其他原因)。
第三部分,关于IPv6
我对于将家庭设备暴露给公网还是有一定的担心,所以我违背IPv6的初衷,在IPv6上还是配置了NAT。
获取前缀
在我的电信宽带,只要开启dhcp client就能获得/60的IPv6前缀,记得request不要选address(选了address和prefix,电信不会下发任何东西)。
/ipv6 dhcp-client
add add-default-route=yes default-route-tables=main interface=pppoe-out1 pool-name=public-ipv6 request=prefix use-peer-dns=no
给PPPOE接口绑定地址
因为我要设置NAT,所以不希望在bridge或物理LAN接口上绑定公网IPv6地址,而且这个地址要设为advertise=no,即不会通过ND下发。在lo接口上绑定是没有用的(这和Linux不同),只能随便找个不属于bridge的接口绑定了,你可以绑在WAN口上,这样可以启用eui-64,也可以像我一样绑在pppoe口上,这样更符合逻辑。
/ipv6 address
add address=:: advertise=no eui-64=no from-pool=public-ipv6 interface=pppoe-out1
现在,你的路由本身应该能够ping通公网IPv6地址。
下发私有地址
我们要给局域网设备下发IPv6地址,但又不能下发从ISP获得的公网前缀,所以我们自定义一段,假设是fc00:fc00:fc00:1::/64(实际上我用的不是这个前缀)发给VLAN0,fc00:fc00:fc00:5::/64发给VLAN5。设置ND的interface=bridge,能够正常下发到bridge上的vlan接口上,不用担心。
/ipv6 address
add address=fc00:fc00:fc00:1:: eui-64=yes interface=bridge
add address=fc00:fc00:fc00:5:: eui-64=yes interface=vlan5
/ipv6 nd
set [ find default=yes ] advertise-dns=no advertise-mac-address=no interface=bridge
你的设备应该会获取到一个fc00开头的IPv6地址。
如果它也获取到了ISP给的公网前缀(原因不明,可能是因为ND默认是打开的,你在配置前几步的时候它已经开始错误地工作了),不用担心。一方面,重启路由后,这个前缀就会从局域网设备中消失;另一方面,由于我们把公网IP绑定在了pppoe口上,bridge或物理LAN口上没有公网IP的路由,所以即使不设置任何防火墙,路由器(以及根据/60前缀来访问你的路由器的公网用户)也无法根据下发给你的公网IP来路由到这个内网设备。
设置IPv6 NAT
/ipv6 firewall nat
add action=masquerade chain=srcnat ipsec-policy=out,none out-interface-list=WAN
现在,就像IPv4的NAT一样,我们在不让局域网设备获得公网IPv6地址的情况下,允许它们通过IPv6正常访问互联网。如果你还不放心,可以在防火墙上再限制一下。
/ipv6 firewall filter
add action=drop chain=forward comment="drop not from fc00 to WAN" out-interface-list=WAN src-address=!fc00:fc00:fc00::/48