系列链接:
- 土制 Linux 路由器(Chapter 1):设置一个简单的家用 Linux 路由器
- 土制 Linux 路由器(Chapter 2):策略路由
- Work in progress…
写在前面的话
因为跟着群友们玩了一段时间的网络,以及自己的开源软件洁癖,于是自己开始学习如何用常见的发行版例如 Debian 来制作纯正的开源路由器(?
前段时间有群友想知道如何把一个Linux变成路由器,再加上自己本身也想把自己折腾的东西整理出来,于是本人还是决定开坑了。
此系列文章主要记录个人配置 Linux 路由器的心得,注定也会比较干。
本篇主要内容则是基础上网加上简单的防火墙配置,可以达成接路由器下面的设备可以上网的成果。
需要注意的是,在写这篇文章的时候,作者已经尽量采用较新的软件,但技术总是更新的,可能过时的东西会越来越多(笑
通过阅读此篇,你可以做到: 路由器通过 PPPoE 上网,下面设备通过路由器也可以上网,并可获得双栈的连接。
如果文中出现错误,还请看到的各位大佬不吝指正。
文中已经尽量压缩了内容,但可能废话还是会很多
那么就开始正片吧(雾
环境介绍
使用的软件和发行版
- Linux 发行版采用 Alpine Linux
- 防火墙前端采用 nftables
- DHCP 服务器采用 Kea DHCP Server
- 递归 DNS 服务器采用 Knot Resolver
- PPPoE 拨号采用 PPP
- DHCPv6 客户端采用 DHCPCD
- IPv6 对局域网发送路由通告采用 radvd
使用的网络接口和连接信息
路由器采用 eth0
接口来拨号。上网用户名为 test
,密码是 123456
。
路由器的下游接口为 eth1
,内网网段为 192.168.10.0/24
,路由器在内网的 IP 为 192.168.10.1/24
上游设备 PPPoE Server 和 DHCPv6 Server 由 RouterOS 提供。
注意事项
- 以上软件可以在 AlpineLinux 上通过
apk add nftables kea-dhcp4 kea-dhcp6 ppp knot dhcpcd radvd
来取得。 - AlpineLinux 安装过程中需要网络,真实环境下操作请预先安装以上包,正片中不再赘述以上安装过程。
正文
Part 1:让 Alpine Linux 先使用上网络吧
一、 通过 PPPoE 连线
- 在
/etc/modules
中添加pppoe
以在开机时自动加载pppoe
这个内核模块。
可以执行echo pppoe >> /etc/modules
来添加。
如果是首次配置并且未重启,需要手动启用 pppoe 模块,执行modprobe pppoe
即可。 - 在
/etc/ppp/peers/dsl-provider
中添加如下内容:
|
|
此处假定用户名是 test
密码是 123456
,并且假定使用 eth0
这个接口进行拨号。 dsl-provider
只是一个名称,可以随自己喜好更改。
3. 测试拨号是否成功。
执行 pppd call dsl-provider
来使用 dsl-provider
这个配置来拨号。
如果没有问题,大致会输出以下信息:
|
|
执行 ip address show dev ppp0
可以看到ppp0接口的话,就算拨号成功了。:
|
|
也可以在 RouterOS 上的 Active Connections 看到 PPPoE 连接已经启用了。
此时IPv4连接就建好了。
等等,what about ipv6?
不要急,我们将其持久化之后再来配置。
- 使 PPPoE 持久化
编辑
/etc/network/interfaces
添加eth0
和ppp0
的配置:
|
|
- 注意 provider 选项填写在
/etc/ppp/peers/
内配置文件的名称。 重新启动之后,用ip link list
应该就能看到ppp
接口啦
|
|
- 检查网络是否联通,一般可以使用ping,traceroute等方法,如果路由器通了就没有问题了。
让 Linux 当路由器,我们还需要启用它的转发功能:
执行以下命令:
sysctl net.ipv4.ip_forward=1
sysctl net.ipv6.conf.all.forwarding=1
即可。如果需要持久化,则执行以下命令:
echo net.ipv4.ip_forward = 1 >> /etc/sysctl.d/90-forwarding.conf
echo net.ipv6.conf.all.forwarding = 1 >> /etc/sysctl.d/90-forwarding.conf
这样每次启动之后就会自动保存了。
二、 通过静态或者动态的 DHCP 连线
如果是一般的静态地址上网或者使用DHCP,在 /etc/network/interfaces
写入配置就可以了
- 此处假定是静态地址119.108.233.2/24,并且网关为 119.108.233.1 大概格式如下:
|
|
- 如果是 DHCP 获取地址,将上面的 static 换成 dhcp ,并去掉下面的 address 和 gateway 即可。
- IPv6 地址如果是使用 SLAAC 获取的话就在下面加上一行:
iface eth0 inet6 auto
即可。
Part 2: 让下面的设备也能上网吧~
一、给局域网分配地址
此处我们做实验的这台机器的 eth0 给PPPoE使用了,接下来就给 eth1 分一个内网的网关地址吧(将下面一段写入 /etc/network/interfaces
即可)
auto eth1
iface eth1 inet static
address 192.168.10.1/24
iface eth1 inet6 auto
二、NAT 简述
不过在配置 NAT 之前,还是需要读者知道 NAT 的含义,可以在这里去了解,其中也讲述了回环 NAT 的配置问题,不过此篇不会覆盖到,不过还是推荐读者去阅读它。
以下是关于它的简单的解释:
- SNAT(Source NAT) 是对从内部(一般是保留的)地址转到外部的时候所做的操作,让源 IP 从内部地址变成外部的地址,也就是把从里面来的东西改一下,之后这个数据包就可以在外部被转发了
- DNAT(Destination NAT)则相反,是外界访问内部数据时,改变它的目标IP地址(也可以一并改变它的目标端口),即更改发到私有 IP 地址的目标地址,也就是把从外面要发到里面的东西改一下,让其转发到内部的主机上去,端口转发(port forward)的原理也是这个。
- 一个源 IP 地址为保留地址的数据包在公网上基本也只会被丢弃。
三、nftables 的配置
既然已经2022年了,不用下 nftables 怎么行呢?nftables 相比 iptables 也有更多好玩的新特性
- 它的链和表都是完全可调整的,初始状态下规则(rule)、链(chain)和表(table )都不存在,可以通过自己的需求来加减,hook 则可以在链中配置,但链可以不带任何 hook ;
- 一条规则可以执行多个操作,有 Maps 和 Verdict Maps
- ipv4 和 ipv6 可以合并管理,比如对于丢弃到某个端口的连接,可以在 inet(即ip和ip6) 地址族中写一条规则即可,而不是像 iptables 那样需要同时通过 iptables 和 ip6tables 加这条规则。
- 在流控方面,nftables 可以替代 tc filter,可以对于不同的数据流或数据包进行分组设置priority,将其塞到特定的class里面。
- 数据操作也是可行的!
- 等等….(喵?)
- 关于 nftables 的新特性,以及详细用法,这里先不细讲,可以通过这里去了解。
关于nftables配置文件的位置:
Debian系在 /etc/nftables.conf
AlpineLinux在 /etc/nftables.nft
配置文件位置可能因发行版有所不同,但是语法都是一样的。
- 一些说明:
- 表(table)是用来装链(chain)的,链是用来装规则(rule)的,
- 每个链的挂载点(hook)是可选的,可以不挂载任何netfilter hooks,链可以拥有一个优先级(priority),和一个默认的操作(policy)。
nftables 的 NAT 配置
- 我们先建立一个表(table).
nft add table ip nat
Note1: 其中 “ip” 是 ipv4 这个地址族(address family),“nat” 则是这个表(table)的名称,名称可以自己随意定,此处使用 nat 是为了表明这个表内的链和规则是为了 nat 而写的,不过对于实际上 netfilter 怎样处理没有什么影响。
Note2: 对于nat类型的chain,我们只能将其塞进地址族为 ip 或者 ip6 的 table 内,想要偷懒使用 inet 是不可以的。 ,参考此处
- 之后我们在这个表(table)内建立一个链(chain)
nft 'add chain nat postrouting { type nat hook postrouting priority 100 ; }'
Note: 我们在这里需要做到的是 SNAT 操作,而对于变动的外部地址,最方便的一个操作是 masquerade,自动根据外部接口地址来进行 SNAT。而这个操作需要在数据包被路由之后,在被送出本地系统之前进行,所以建立一个挂载postrouting的chain。(需要知道被SNAT到的地址是什么,则需要在被路由之后才能知道出接口的地址才能进行自动 SNAT)
- 我们在postrouting链中塞一个 masquerade 的规则。(假定我们的出接口是ppp0)
nft add rule nat postrouting oifname "ppp0" masquerade
- 执行
nft list ruleset
就可以看到完整的规则啦:
|
|
这一段也可以写进/etc/nftables.nft里面,之后使用rc-update add nftables
就可以让其开机自动保存啦
nftables 的 NAT 加上过滤的配置文件
这里我直接放一个最终的配置文件吧,就不多写了。 (假定上网用接口是ppp0,局域网接口是eth1)
|
|
执行 nft -f /etc/nftables.nft
即可保存。
PPPoE 下的 PMTUD 问题的缓解措施
- 对于 IPv4:
如果你在使用过程中遇见了连接到网络超时的现象,并且抓包可以看到 previous segment not captured 和一堆连接重置,可以尝试在 nftables 配置文件中加上以下的规则:
|
|
- 对于 IPv6:
在 IPv6 下,已经有了通过 ICMPv6 实现的 PMTU Discovery 机制。
但对于一些配置不当的防火墙,IPv6也可能会出现 Path MTU Discovery 不工作的问题,原因一般是没有放行 Packet Too Big 这个类型的 ICMPv6 的数据包。即使本地放行了这种类型的ICMPv6,而对方没有放行,IPv6的 PMTUD 也是不能工作的。
检测 MTU 问题的时候,最好的诊断方法就是 ping 配合 size 参数,但是对方把 ICMP echo-request/echo-reply 给橄榄之后这种方法也不会起作用。
在此呼吁各位网络管理者不要随便就把 ICMP(v6) 直接给干掉,如果担心被 flood ping,可以设定一个频率限制,而 ICMPv6 中作为 IPv6 的重要组成部分,更不要轻易忽略它甚至把它给干掉,这样的话一般设定情况下,客户可是可能不能访问你的网站的喔
在对方把 ICMPv6 一刀切的时候,你可以通过在 RADVD 中设定 AdvLinkMTU 为 1492(1500-8) 或者更小来缓解,这样的话,下面的设备获取的默认路由的 MTU 就是你设定的 MTU 了。
不过还有一种解决方式就是 clamp mss to pmtu,这里就不多讲述了。(感觉用这种方法超坏的!)
nft 命令的一些常用用法(cheatsheet)
列出表和它们的地址族: nft list tables
列出所有规则和统计数据: nft list ruleset
清空所有规则: nft flush ruleset
检查配置文件的语法: nft -c -f <file path>
载入配置文件: nft -f <file path>
更多的可以去这里找到。
这样,我们的基本的路由器配置就结束了,不过为了下面的设备可以正常上网,你还需要一个 DHCP Server 来给下面的地址分配网关,DNS,地址等信息。
四、Kea-DHCP 的配置
Kea DHCPv4 Server 对应的配置文件在 /etc/kea/kea-dhcp4.conf
直接放配置文件吧:
{
# DHCPv4 configuration starts on the next line
"Dhcp4": {
# First we set up global values
"valid-lifetime": 4000,
"renew-timer": 1000,
"rebind-timer": 2000,
# Next we set up the interfaces to be used by the server.
"interfaces-config": {
"interfaces": [ "eth1" ]
},
# And we specify the type of lease database
"lease-database": {
"type": "memfile",
"persist": true,
"lfc-interval": 1800,
"name": "/var/lib/kea/dhcp4.leases"
},
# Finally, we list the subnets from which we will be leasing addresses.
"subnet4": [
{
"subnet": "192.168.10.0/24",
"pools": [
{
"pool": "192.168.10.20 - 192.168.10.200"
}
],
"option-data": [
{
"name": "domain-name-servers",
"data": "223.5.5.5"
},
{
"name": "routers",
"data": "192.168.10.1"
}
],
"reservations": [
{
"hw-address": "58:11:22:33:44:55",
"ip-address": "192.168.10.190"
}
]
}
]
# DHCPv4 configuration ends with the next line
}
}
之后 service kea-dhcp4 start
就可以开启 Kea DHCPv4 Server 了。
同样地,我们也可以用rc-update add kea-dhcp4
将其加入自启动列表。
五、IPv6 的配置
- 使用 dhcpcd 作为 IPv6 DHCP 客户端来获取地址和地址块。
废话不多说,直接上配置吧:
|
|
之后执行dhcpcd ppp0
就可以了。
我们只需要通过这个从 ppp0 获取 IPv6 地址,所以加上了 ipv6only
,
其中,我们需要默认在其他接口上不进行 IPv6 Router Solicit,只在 ppp0 这个上网的接口上进行 Router Solicit。
并且我们需要从运营商获取地址块之后,分一段 /64 到 eth1,并且将加上去的地址块的 :1 分到 eth1 上,于是就有了 ia_pd 1 eth1/0/64/1
Note: 如果需要加入自动启动的话,可以在
/etc/ppp/ip-up
以及/etc/ppp/ip-down
文件中的 ppp0 接口下分别加入dhcpcd ppp0
和dhcpcd ppp0 -k
,来让其随 ppp0 接口开启和关闭。
不出意外的话,我们可以在 ppp0 接口上看到 IPv6 地址(通过SLAAC),在 eth1 上也可以看到一个前缀长度为 64 的 IPv6 地址(通过DHCPv6 Prefix Delegation)。
|
|
在执行ip -6 route show
我们也可以看到一个 unreachable 的路由,这就是运营商给我们分配的IPv6地址池。这个地址池大小/56,/60不等,具体取决于当地的运营商。
我们也可以看到指向局域网的一个/64的路由,这就是这个接口下局域网的 IPv6 地址范围了。
|
|
- 配置 RADVD 让下面的设备可以通过 SLAAC 获取 IPv6 地址:
配置文件在/etc/radvd.conf
|
|
随后通过service radvd start
和rc-update add radvd
来启用并加入自启名单。
对于其中的一些flags:
- AdvAutonomous 是允许设备通过这个宣告的前缀来用 SLAAC 的方式为自己分配地址。
- OtherConfig 是指,让设备在 SLAAC 获取地址的方式下,通过 DHCPv6 服务器来获取其他的信息(例如 DNS 服务器等)。
- Managed 是指允许设备通过 DHCPv6 服务器来获取地址和 DNS 等信息。
通过一系列组合,读者可以尝试用 DHCPv6 Stateful + SLAAC 或者分别以 DHCPv6 Stateful 和 SLAAC 的方式让下面的设备获取地址。
Note 1:
与 IPv4 获取网关的方式不同, 在 IPv6 下,默认路由不是通过 DHCPv6 服务器来发放的,而是通过路由宣告(Router Advertisement)来发放的。
并且有状态的 DHCPv6 服务器也不是必要选项,因为本身路由通告(Router Advertisement)就可以直接发放 IPv6 的前缀和 DNS 解析服务器的地址了。
此外,单独的 DHCPv6 服务器是不会有任何作用的 ,需要配合 Router Advertisement ,并且设定正确的 Flags 才能令设备从 DHCPv6 服务器获取相关的信息。
Note 2:
SLAAC 下设备一般是 EUI64 生成地址(特点是中间会带 ff:fe ),而这种方式是通过接口 MAC 来直接计算的,反推也非常容易,较为隐私不友好。
不过后来又推出了 IPv6 隐私扩展(IPv6 Privacy Extension)解决了这个痛点。
如果读者使用 Linux,也可以通过 sysctl 的相关参数来开启隐私扩展。
六、开启 knot-resolver(可选)
直接通过 service kresd start
和 rc-update add kresd
就可以开启并让其自动启动了。
它的默认监听是在localhost,需要在 /etc/knot-resolver/kresd.conf
中修改它的监听地址为路由器在局域网中的地址即可。
如果需要让它作为默认的 DNS 服务器的话,可以在 Kea-DHCPv4 的配置文件中修改 DNS 服务器的相关设定。
这样就配置完毕啦。
资料引用
- 感谢下面的资料:
- nftables: Performing Network Address Translation (NAT)
- dhcpcd(8) manpages
- IPv6 RFCs(system.de)
- Kea Administrator Reference Manual
- ifupdown-ng manpages
- 题外补充:
- 有关端口转发的配置:
Multiple NATs using nftables maps - 关于路由器之间的 IPv6 子网大小是否应该使用 /127 :
[RFC 3627] Use of /127 Prefix Length Between Routers Considered Harmful
[RFC 6164] Using 127-Bit IPv6 Prefixes on Inter-Router Links - Misc:
LARTC: Linux Advanced Routing & Traffic Control
写在最后
- 吐槽1:之前看到 Google 的报告,提到互联网上大部分东西都是重复的,这体现了人类的本性(作者乃复读机是也!),不过本文也是如此,只是自己从其他地方(指一堆用户手册和一些 RFC ,以及博主个人总结的经验)学到了之后又在这里记录下来了而已。希望能对读者有所帮助,不过如果读者发现本文中出现了失误,也非常欢迎读者指出w。
- 吐槽2: 这些配置都比较通用,如果自己配置路由器时希望路由器同时干一些别的活的话,此处还是推荐 Debian。
- 吐槽3: 感觉是一个技术力超级低的文章呜呜呜。。。(已经尽力了,咕…)
更新
- 2023.01.09 补充了 pppoe 模块加载的细节以及 IPv6 部分,修正了部分错误
- 2022.12.10 整理了格式并补充说明了 dhcpcd。
- 2022.12.1 初次发表