OpenWrt实现智能路由器

Posted by LvKouKou on December 23, 2023




image-20230327104636895-1679885221095

Computer Networking


OpenWrt实现智能路由器


Date: 2023.12.23

Lectured by: Shuyuan Jin
Computer Networking Course
Sun Yat-sen University






计算机网络实验-OpenWrt实现智能路由器

首先,需要感谢其他两位小组成员的共同努力,才有这份Blog。

1. Section 1 实验概述

组建路由器实验环境

image-20231208145201933

如图所示,将虚拟机A当作智能路由器,安装OpenWrt软件,并创建两个网卡,NAT网卡用于连接互联网,内部网络用于连接家庭网个人计算机。虚拟机B当作家庭PC使用,自动从OpenWrt网关处分配IP地址。这样我们就可以模拟常见的路由器场景,例如上网、防火墙和DNS代理等功能,任何家庭网的数据流量均通过路由器来转发到外部网络。

Helloworld

H公司想了解已售出路由器的使用情况,如路由器的启动次数。期望在路由器每次启动时将访问指定服务器(实体机)通过访问记录来保存路由器启动次数,可以通过修改路由器的配置文件实现。

为了防止某些小区在断电恢复后自动启动所有路由器同时立即访问服务器,对服务器产生瞬间流量冲击,因此需要对路由器启动后提供一个随机延迟时间,然后再访问服务器,以减少服务器压力。

实现路由器启动时向服务器发送消息(你的学号),并在服务器端保存该访问记录。

路由器基本功能

(每种基本功能中选择2个子功能实现即可)

  • 网络互连:路由器支持各种局域网和广域网接口,主要用于互连局域网和广域网,实现不同网络互相通信;

  • 数据处理:提供包括分组过滤、分组转发、优先级、复用、加密、压缩和防火墙等功能;

  • 网络管理:路由器提供包括路由器配置管理、性能管理、容错管理和流量控制等功能。

1.1 预备知识与实验环境

该节总结实验需要用到的基本知识,以及主机型号、代码编译工具、重要三方库的版本号信息等。

  • 预备知识:计算机网络
  • 实验环境:VirtualBox7.0.12,ubuntu20.04,OpenWRT15.05
  • 处理器型号: Intel Core i7-10875H @ 2.30GHz

2. Section 2 实验任务

2.1 任务一:搭建路由器实验环境

编译openwrt,并将其部署到虚拟机上。

2.2 任务二:Helloworld

使得openwrt开机时会自动向目标服务器发送消息。

2.3 任务三:智能路由器基本功能

配置以下网络环境,并实现,

  • 网络互连:路由器支持各种局域网和广域网接口,主要用于互连局域网和广域网,实现不同网络互相通信;

  • 数据处理:提供包括分组过滤、分组转发、优先级、复用、加密、压缩和防火墙等功能;

  • 网络管理:路由器提供包括路由器配置管理、性能管理、容错管理和流量控制等功能。

image-20231208145201933

3. Section 3 实验步骤与实验结果

3.1 任务一:组建路由器实验环境

1. 下载并编译OpenWRT

另一种方案是直接下载映像文件,因为OpenWRT本质上就是一个系统,类似ubuntu。

做到后面发现还是要自己编译的,因为要编译自己的hello组件。

安装编译环境依赖

1
sudo apt-get -y install build-essential asciidoc binutils bzip2 gawk gettext git libncurses5-dev libz-dev patch python3.5 unzip zlib1g-dev lib32gcc1 libc6-dev-i386 subversion flex uglifyjs git-core gcc-multilib p7zip p7zip-full msmtp libssl-dev texinfo libglib2.0-dev xmlto qemu-utils upx libelf-dev autoconf automake libtool autopoint device-tree-compiler g++-multilib antlr3 gperf

下载19.07分支的代码

1
git clone -b openwrt-19.07 https://github.com/openwrt/openwrt.git openwrt1907

更新源代码

1
2
./scripts/feeds update -a
./scripts/feeds install -a
配置编译环境
1
make menuconfig

由于使用的是vmware,所以make menuconfig的时候有两个地方要配置

  1. 选择Target System 为 x86
  2. Root filesystem archives 选择 VMDK(也就是vmware的格式,可在vmware上直接加载)
  3. LuCI->Collections->luci要选择(如果没有这一个会没有Web页面)

如果一开始没选luci也没关系,之后进入openwrt后可用以下指令安装:

1
2
3
4
5
opkg update
opkg install luci
/etc/init.d/uhttpd enable
/etc/init.d/uhttpd start
/etc/init.d/firewall stop

开始编译

详细的编译输出信息,多线程编译

1
make -j4 V=99

编译完成:

image-20231201155832659

之后就可以在bin//target/x86/generic/ 下看到对应的vmdk文件。

并且此时会有很多类似的img.gz文件,因为配置编译环境时Root filesystem archives 中有很多选项,不仅选了vmdk,还有其他的很多。

2. 部署到VMware

如何在vmware虚拟机中安装OpenWrt系统?

创建一个虚拟机,然后进入设置将原来设置的硬盘删掉,新建一个硬盘用编译生成的VMDK文件(因为创建虚拟机的时候没有出现选择虚拟硬盘的选项,所以只能之后修改)。

image-20231201204355395

然后进去就可以看到如下界面:

image-20231201170114238

使用的是桥接模式,修改lan口ip地址获取方式为dhcp,动态获取地址:

1
uci set network.lan.proto=dhcp

保存网络配置

1
uci commit network

查看是否修改成功可以通过 uci show network 命令查看

修改协议后:

image-20231201172104779

重启网络,使得配置生效:

1
/etc/init.d/network restart

此时通过ip add查看地址,然后再本地浏览器即输入br-lan的ip即可进入openWRT配置页面(如果不可以,很大概率是没有luci):

image-20231201210138629

3. 部署到virtual box

后来发现VMware网上教程较少,做了很久都没有探索出来,所以改用和《智能路由器开发指南》相同的VirtualBox和openwrt15.05版本。

编译步骤同上,同样在make menuconfig的时候在Root filesystem archives 选择 VDI(也就是VirtualBox的格式),但是由于距离上次编译太久cache中的编译缓存被删掉了要重新开始,由于一开始忘记选用VDI,所以选用编译好的img文件,将其转化为VDI文件:

进入VirtualBox目录,打开powershell输入

1
.\VBoxManage.exe convertfromraw -format VDI E:\ComNetFinalProject\openwrt\openwrt-x86-generic-combined-ext4.img E:\ComNetFinalProject\openwrt\openwrt15.vdi

执行成功:

image-20231206235751589


然后按照教程部署到VirtualBox即可

教程:VirtualBox虚拟机安装openwrt供本机使用

image-20231207001906240

对openwrt15这个虚拟机两个网卡分别设置为仅主机和桥接:

image-20231208110823765

image-20231208110837262

然后到虚拟机的网络管理器配置网卡和DHCP:

image-20231208110926411

image-20231208111019699

进入虚拟机后用vi etc/resolv.conf命令打开这个文件,更改如下:

image-20231207003642689

此时就联网成功了:

image-20231207003544526

此时在浏览器输入192.168.1.1即可进入Openwrt界面:

image-20231207004411111

初始时没有密码,按一下enter即可进入

image-20231207004426319


3.2 任务二:启动HelloWorld

要求:H公司想了解已售出路由器的使用情况,如路由器的启动次数。期望在路由器每次启动时将访问指定服务器(实体机)通过访问记录来保存路由器启动次数,可以通过修改路由器的配置文件实现。

为了防止某些小区在断电恢复后自动启动所有路由器同时立即访问服务器,对服务器产生瞬间流量冲击,因此需要对路由器启动后提供一个随机延迟时间,然后再访问服务器,以减少服务器压力。

实现路由器启动时向服务器发送消息(你的学号),并在服务器端保存该访问记录。

方法一:

为了实现要求,参考书上的Hello编写工程文件:

image-20231208101755036

然后将文件传到ubuntu我们一开始编译openwrt的文件下的package下,再次使用make menuconfig进入设置菜单,然后在Network中找到hello,按M将其选定,之后再openwrt目录下输入make package/hello/compile编译。(这一步遇到了比较多报错问题,没有的库要一一安装,不知道是不是距离前一次编译时间太久,本次实验参考教程几乎只有《智能路由器开发指南》,要自己慢慢理解,所以实验周期拉的比较长)

image-20231208112455459

编译成功后即可获得一个后缀为.ipk的文件,移动到window改名后如下图所示:

image-20231208112636443

由于openwrt的虚拟机没有安装增强功能没有图形界面,所以需要下载一个WinScp来将我们的hello_21307381.ipk移动到openwrt中。

主机名输入openwrt的IP地址,账号root,密码password

image-20231208113109784

进入后即可看到下面的页面,左边是window的文件目录,右边是openwrt。

image-20231208113237348

将hello_21307381.ipk移动到openwrt。

image-20231208150237850

到Openwrt的虚拟机,使用命令opkg install openssl-util安装一个openssl,安装成功应该会如下所示。

image-20231208102046168

最后使用命令opkg install hello_21307381.ipk安装我们的HelloWorld软件包,可以看到成功的界面如下。

发送学号:21307381

image-20231208105159825

此时查看目录,可以看到index.html已经下载下来了。这样就可以让每次开机都发送一条信息到我们目标服务器了。

image-20231208105521938

为了验证是不是每次开机重启都会执行一遍:reboot后发现开机自动下载了文件 ,实验成功。

image-20231208105709942

为了保证确实每次都会执行一次,而不是上次运行后留下来的文件,输入192.168.1.1访问luci,依次进入System-Startup,滑到最下面在Local Startupexit 0之前加上我们的opkg install hello_21307381.ipk,点击Submit,这样每次开机都会执行opkg install hello_21307381.ipk,下载我们编译出来的包。

image-20231208114133684


根据任务二操作步骤,我成功完成了一个名为 “helloworld” 的实验。在该实验中,每次路由器启动时,它会访问指定的服务器 www.baidu.com。通过该网站的后台管理,可以追踪记录路由器的启动次数。此外,在路由器启动后,还会存在一个随机的延迟时间,防止断电重启后的瞬间流量冲击,该延迟时间是通过在 hello.c 文件中使用 rand() 函数实现的,并在此之后再次访问服务器。在路由器启动时,它会向服务器发送一条包含学号 “21307381” 的消息。这个消息可以在服务器端被记录和查看。


方法二:

  1. 修改 rc.local 文件以在 OpenWrt 启动时执行命令,该命令会往目的ip和端口发送get请求。 Alt text Alt text Alt text

  2. 编写一个后端程序来监听目标 IP 地址的 HTTP GET 请求,并验证是否成功收到请求。启动后端,Reboot虚拟机,可以发现成功收到请求 Alt text

  3. 添加随机延迟 为了解决在启动时命令执行过快导致无法每次收到信息的问题,可以在命令执行之前添加一个随机延迟。由于 OpenWrt 中没有 RANDOM 变量,我们可以生成一个伪随机的数字作为延迟时间。

修改 rc.local 文件: Alt text

head会取出dev/urandom文件中的第一行数据并取出0-9的数字。 然后通过%10+20生成(20,30)内的随机数,并将延迟设置成该数字。 严格按照格式来不要有多余的空格或少空格 参考如下: https://www.jianshu.com/p/513c7019c2a1 如果命令not found,将编译器改成bash,参考如下: https://www.openwrt.pro/post-598.html

  1. 发送学号
    1. 修改wget语句,发送get请求时传参id=21307383_21307371_21307381 Alt text

    2. 修改后端: Alt text

    3. 取出学号并打印。Reboot一下,可以发现成功输出了学号 Alt text

3.3 任务三:智能路由器基本功能

1. 搭建路由器环境

前面的实验中都是路由器自己通过宿主机的网络上网,接下来我们搭建一个如下图所示的环境,实现将我们虚拟机上的openwrt当作智能路由器。

image-20231208145201933

为了搭建上述环境,openwrt的虚拟机(上图中gateway)要拥有两个网卡,NAT网卡用于连接互联网,内部网络用于连接家庭网个人计算机(上图中client)。Ubuntu虚拟机当作家庭PC使用,自动从OpenWrt网关处分配IP地址。这样我们就可以模拟常见的路由器场景,例如上网、防火墙和DNS代理等功能,任何家庭网的数据流量均通过路由器来转发到外部网络。

首先我们修改openwrt中网卡,由于openwrt的eth0默认为lan,所以将第一张网卡设置为内部网络,第二张网卡设置为NAT:

image-20231208151443816

image-20231208151459555

接下来启动openwrt,查看此时openwrt可以连接上互联网:

image-20231208151413586

然后再VirtualBox中将client的网络设置为内部网络:

image-20231208152333204

开启client,可以发现此时client自动从OpenWrt网关处分配IP地址(左图),此时自动分配的IP地址为192.168.1.190。这样我们就可以模拟常见的路由器场景,例如上网、防火墙和DNS代理等功能,任何家庭网的数据流量均通过路由器来转发到外部网络(也就是可以上网了,如登陆中大官网)。

image-20231208153208533

2. 网络互联

由上面可以知道,已经实现了网络互联,

client ping window,可以看到是可以ping 通的。

image-20231208153608289


在前面,我们已经成功实现了从客户机通过广域网(WAN)访问到外网。现在我们要实现通过主机连接到虚拟机 OpenWrt。

为了实现这个功能,我们利用 端口转发功能。

  1. 添加两条规则,一条将子系统端口设置为80,即 LuCI 默认端口;另一条将子系统端口设置为22,即 SSH 端口。 Alt text

  2. 下载并安装 SSH 服务器。 $ opkg install openssh-server
  3. 检验 SSH 连接是否正常工作。如果连接成功,则表示 SSH 连接正常。 $ ssh -l root -p 2222 127.0.0.1 Alt text
  4. 检验 HTTP 连接是否正常工作。在浏览器中输入 localhost:8080,然后成功进入 LuCI 界面。 Alt text

通过以上步骤,我们成功实现了网络互连的功能。需要注意的是,此时可能无法通过 Ping 命令进行通信。 参考链接:https://blog.csdn.net/qq_32077121/article/details/125908114

3. 数据处理

  • 数据处理:提供包括分组过滤、分组转发、优先级、复用、加密、压缩和防火墙等功能;

防火墙:

防火墙的实现可以直接用Client 登录路由器的 Web 管理界面来设置找到Network->Firewall:

image-20231208155240486

General Settings 中可以在一个范围内设置防火墙。 如接下来演示在局域网中设置防火墙, 将局域网内的主机和广域网相隔开。未开启防火墙时,wan 端口的output是 accept 的状态:

image-20231208155020840

如果将output更改为reject,不允许转发到广域网,那么就无法与外界相连,只可以在同一局域网内互连了:

image-20231208160024034

分组过滤

dnsmasq+ipset+iptables实现分组过滤 在luci页面的software可以看到可以下载的packages

image-20231208165351388

依次下载上述三个包,然后按照下面步骤配置:

1、配置dnsmasq.conf 修改/etc/dnsmasq.conf conf-dir=/etc/dnsmasq.d

image-20231208165643670

2、新建目录和文件 mkdir /etc/dnsmasq.d touch /etc/dnsmasq.d/domain.conf

image-20231208165757110

3、修改防火墙 ipset -N domain iphash 黑名单 iptables -t filter -A FORWARD -m set –match-set domain dst -j DROP iptables -t filter -A FORWARD -m set –match-set domain src -j DROP 白名单 iptables -t filter -A FORWARD -m set –match-set domain dst -j ACCEPT iptables -t filter -A FORWARD -m set –match-set domain src -j ACCEPT iptables -t filter -A FORWARD -j DROP

4、添加过滤 修改’/etc/dnsmasq.d/domain.conf ipset=/www.baidu.com/domain ipset=/www.qq.com/domain

整个原理:dnsmasq会将/etc/dnsmasq.d/domain.conf的ur转换成ip丢给ipset集合,防火墙的配置决定了这些ip是否放行

注意:openwrt默认的dnsmasq不支持ipset,修改修改配置make menuconfig ,使用dnsmasq-full,并开启ipser支持才行, 另外,需要本地设备将url的地址访问一下比如ping一下才能加进ipset集合里面,局域网设备访问不会自动加

可以访问中大官网,不可以访问加入过滤domain的网站ip:

image-20231208170318455

分组转发

  1. 进入 /etc/config/firewall 文件。在文件中找到初始配置,如下所示。 Alt text
  2. masq 设置为 1。这表示对该区域的出站连接进行 NAT 转换。 将 mtu_fix 设置为 1。这表示启用 MTU 修复。 将 input 设置为 ACCEPT。这表示该区域允许接受传入连接。也就是说,允许从该区域外部(如互联网)发送的连接请求进入该区域。 将 output 设置为 ACCEPT。这表示该区域允许传出连接。也就是说,允许从该区域内部发送连接请求到该区域外部(如互联网)。 将 forward 设置为 ACCEPT。这表示该区域允许转发连接。也就是说,允许从该区域接收到的连接请求转发到其他区域。 Alt text

  3. defaults 中的 forward 改为 REJECT。这样做后,如果从客户机尝试 Ping 外网,将会显示无法连接。 Alt text

  4. 添加转发规则,将来自 LAN 口且源 IP 地址为 192.168.1.30 的数据包发送到 WAN 口。 Alt text

  5. 现在,如果在客户端使用 Ping 命令,可以成功连接。
Alt text Alt text

4.网络管理

网络管理:路由器提供包括路由器配置管理、性能管理、容错管理和流量控制等功能。

路由器配置管理

LuCI即为路由器配置管理页面,可以在浏览器输入路由器的ip来访问。

容错管理

主要是数据备份和恢复

第一方法是通过定时使用scp命令将etc/config文件拷贝到当前主机

拷贝:

image-20231216213903005

恢复:

img

另外OpenWrt提供了 sysupgrade 命令和 LuCI 界面进行备份,

进入luci界面中system处找到backup/Flash new firmware,界面如下

img

在该界面,可以进行备份,点击generate archive即可下载当前配置的tar压缩包并通过upload archive来恢复配置。

还提供了快照功能,生成硬件快照.img文件。

流量控制

安装qos模块,opkg install luci-app-qos,然后使用qos来进行流量控制。

进入luci界面,在network处找到SQM QoS如下图所示:

img

SQM中我们可以设定下载速度和上传速度,如上图我们设定了下载速度为85000kbit/s,上传速度为10000kbit/s

勾上enable选项,按save,再按apply,即可应用sqm。

下图表明了apply后会产生的配置文件的改变

img