通过 Docker 运行 OpenWRT 并实现软路由的一次尝试记录

SteveXMH大约 8 分钟DockerOpenWRT编程

自己买了一个 Rock Pi 5,想着它的性能如此强劲,宿舍里却还在用着性能比它慢不止一星半点的路由器来承载宿舍的网络。想着让自己的 Pi 通过 Docker 运行 OpenWRT,然后通过虚拟网卡将所有流量都往 OpenWRT 容器中传递来实现软路由,于是便有了本文的记录。

本流程基本上参考自 双网口主机通过 docker 安装 openwrt 实现软路由功能_docker 软路由_qq_38251691 的博客-CSDN 博客open in new window,在这里非常感谢作者的文章!

步骤大纲

  1. 准备 Docker 和 OpenWRT 镜像
  2. 配置 Docker 的虚拟网络(作为软路由中的 Lan 口和 Wan 口)
  3. 启动 OpenWRT 容器并连接虚拟网络
  4. 配置 OpenWRT 中各个网口的行为
  5. 配置宿主机和 OpenWRT 容器之间的网络连接(通过 ip 命令)
  6. 配置宿主机的网口自动设置其启用状态、Mac 地址和混杂模式(依情况可选)

正式开始

零:需要准备的东西

  • 一个能跑的 Linux 机器(此处演示使用的是 Rock Pi 5B,运行的是 Ubuntu 20.04 LTS)
  • 机器需要具备两个或以上的物理网口,形式可以是板载的,也可以是外置的(USB 网卡均可)

为了避免操作中途因为网络环境变化导致无法进入 Linux 机器继续操作,推荐使用非网络 SSH 方式连接你的 Linux 机器,可以是直接物理屏幕键盘或者串口通信。

本文为了方便操作 Docker,将会全程处于 Root 用户下,故将会省略相关的提权代码。如果你的 Linux 系统不允许你直接登录 Root 用户的话,你可能需要在每个 Docker 指令前添加 sudo 以获得 Root 权限运行指令。

一:准备 OpenWRT 的根文件系统(Rootfs)归档并导入到 Docker 中

这个还是很好找的,根据自己的宿主机器架构,在 OpenWRT 的固件官网open in new window上选择自己的架构的文件夹,然后从中下载形如 default-rootfs.tar.gz 的根文件系统归档文件到宿主机器里。考虑到我的机器是 ARM64 架构的,所以我选择了 armvirt/x64 的架构。

随后使用 docker image import <default-rootfs.tar.gz> openwrt-rootfs 命令将根文件系统导入到 Docker 中。注意这里后面的 openwrt-rootfs,这个将会是你这个镜像的名称,之后要使用它的时候会用到。

导入成功后,输入 docker images 查看镜像,看是不是已经正确导入了:

root@rock-5b:~# docker images
REPOSITORY          TAG         IMAGE ID       CREATED        SIZE
openwrt-rootfs      latest      8c374f019165   7 weeks ago    10.5MB # 我们导入的镜像

二:准备 Docker 虚拟网络

在创建之前,我们先确认一下我们需要基于哪些物理网口创建我们需要的虚拟网络:

root@rock-5b:~# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: enP4p65s0: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:e0:4c:03:00:cd brd ff:ff:ff:ff:ff:ff
    inet6 fe80::2b2:c459:68e9:bcbd/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
3: enx6045bdfb0bd2: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 60:45:bd:fb:0b:d2 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::6245:bdff:fefb:bd2/64 scope link
       valid_lft forever preferred_lft forever
# 省略一部分输出...

定位每一个项目的 ID,确认一下各个网口之后的用途,这里我的 enP4p65s0 网口将被用于 LAN 口,而 enx6045bdfb0bd2 将被用于 WAN 口。

首先创建用于 LAN 口的虚拟网卡,创建前先想好自己需要创建什么范围的子网和网关地址(路由器的访问 IP),我这里则选择最普通的 192.168.1.0/24 作为子网,路由器的 IP 为 192.168.1.1

想好之后即可开始输入指令创建网口,留意 --subnet, --gateway-o parent 参数要修改成上文所述的内容,最后一个参数是虚拟网络名称,这里我写了 openwrt-lan

root@rock-5b:~# docker network create -d macvlan --subnet=192.168.1.0/24 --gateway=192.168.1.1 -o parent=enP4p65s0 openwrt-lan

然后再创建 WAN 口的虚拟网卡,这个除了要指定所属物理网口外不需要额外设置,这里我将其命名为 openwrt-wan

root@rock-5b:~# docker network create -d macvlan -o parent=enx6045bdfb0bd2 openwrt-wan

接下来列出当前已有虚拟网络列表以确认我们的虚拟网络是否都成功创建了:

root@rock-5b:~# docker network ls
NETWORK ID     NAME          DRIVER    SCOPE
9064ec779a5b   bridge        bridge    local
b69e3d110f3e   host          host      local
515920225be1   none          null      local
6876eb036fea   openwrt-lan   macvlan   local
5313d119f916   openwrt-wan   macvlan   local

三:创建 Docker 容器

一开始创建容器的时候只能先连接一个虚拟网络,所以我们先连接 LAN 口网络,为后面连接路由器控制台做准备。

此处我将容器的名称命名为 openwrt-router,并确保自动重启,连接 openwrt-lan 虚拟网络,提高容器 Root 权限。

root@rock-5b:~# docker run -d --name openwrt-router --restart always --network openwrt-lan --privileged openwrt-rootfs:latest /sbin/init

启动成功后,将电脑和你的 Linux 机器用网线连接,并设置你的网络设置为同一子网下的不同 IP,然后设置其网关为我们最初配置 LAN 口网络的网关地址。

然后尝试用浏览器访问路由器的控制台(此处即 192.168.1.1),至此大部分流程已经成功了一部分了。

接下来连接 WAN 口:

root@rock-5b:~# docker network connect openwrt-wan openwrt-router

连接完成后即可直接在路由器控制台内配置网口行为,也就是将 LAN 口设置静态地址使其符合我们 LAN 口虚拟网络的配置,开启 LAN 口的 DHCP 服务器功能,将 WAN 口协议设置成 DHCP 客户端即可。

至此你的 LAN 口下属所有设备应该可以访问互联网了。

四:让宿主机连接到 OpenWRT 网络

由于 Docker 的隔离特性,目前你的容器宿主还是没有和 OpenWRT 相连的,因此需要在宿主机创建隶属于物理网口中用于 LAN 口的虚拟网卡(非 Docker 的虚拟网卡)(此处为 enP4p65s0)并桥接到 OpenWRT 网络方可连接。

此处我将这个虚拟网卡命名为 openwrtlan,注意和上面的 openwrt-lan 区分。

root@rock-5b:~# nmcli connection add type macvlan dev enP4p65s0 mode bridge ifname openwrtlan autoconnect yes save yes

不出意外的话,虚拟网卡就会自己获取到 IP 地址,并且可以访问互联网了。

至此你便获得了一个性能极高扩展性极强且不用浪费一整个 Linux 机器做路由器的路由器了,畅快享受网络吧!

五:设置开启自动配置网络设备(依情况可选)

因为本人的网口是使用的 USB 网卡,所以似乎每次启动都需要手动打开一次 WAN 网口。 而且每次重启机器的时候 LAN 口的 MAC 地址都会变动,导致每次 DHCP 的 IP 地址不固定。因此这里也记录一下以防之后又需要处理这样的情况。

以 Root 用户下新建一个脚本,这里我将其放在 /root 并命名为 iplinkset.sh

依你的情况写入以下内容:

#!/bin/sh
ip link set dev enP4p65s0 up # 启动 LAN 网络设备
ip link set dev enx6045bdfb0bd2 up # 启动 WAN 网络设备
ip link set dev enP4p65s0 promisc on # 启动 LAN 混杂模式
ip link set dev enx6045bdfb0bd2 promisc on # 启动 WAN 混杂模式

ip link set dev openwrtlan down # 暂时关闭宿主机和 OpenWRT 之间的连接
ip link set dev openwrtlan address "11:22:33:44:55:66" # 指定一个固定的 MAC 地址,此处可随意设置
ip link set dev openwrtlan up # 重新打开

为脚本文件赋予可执行权限,然后在 /etc/systemd/system 下新建一个服务描述文件,名字不限,只要后缀是 .service,此处我将其命名为 iplinkset.service

写入以下内容:

[Unit]
Description=Set up network interfaces # 服务简介,内容不限
Requires=network-online.target
After=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/root/iplinkset.sh # 此处改为你的脚本路径

[Install]
WantedBy=multi-user.target

保存后执行以下内容启用该服务:

systemctl reload-daemon
systemctl enable iplinkset.service
systemctl start iplinkset.service

至此已完成所有工作,就可以真正享受网络了!

后记

其实看过我博客的都知道,在这之前我都是一直在用 GL.iNet 的路由器,随后需求逐渐增大,路由器已经不能很好的满足需求了,所以想过要换掉它。而目前还剩下一个无线网络需要导入过来,所以无线网卡和天线都在路上了,我想加入新网口的方法也是和上述的步骤类似,创建虚拟网口然后和 OpenWRT 连接配置即可。

参考资料