相信大家家里都有路由器吧,你有没有想过折腾一下路由器让他变得更加强大,更或者说……自己做一个路由器呢?这篇指南就带你走进 $Openwrt$ 的世界。
前期准备
软件的下载和配置
VB&Ubuntu
我们本次的实验需要在 $Ubuntu$ 中完成,我们使用 $14.04.3$ 版本的 $Ubuntu$ 和 $6.1.22$ 版本的 $VirtualBox$ 完成。下载完成 $VirtualBox$ 后,我们依次点击新建-在虚拟电脑名称和系统类型中选择 $Linux$和$Ubuntu$-内存大小选择 $4096 MB$-现在创建虚拟硬盘-虚拟硬盘文件类型选择$VDI$-动态分配-虚拟硬盘大小选择$20G$-点击创建。
接下来我们选择设置-存储,在属性栏看到一个长得像光盘的东西(见下图),点击选择虚拟盘,在里面选择之前下载下来的 $Ubuntu$ 的 $iso$,点击确定完成配置。
完成配置后,我们点击启动,之后我们在启动生成的界面的上面栏选择设备-分配光驱-点击我们的 $Ubuntu$ 的 $iso$ 然后重启。
重启后我们就进入了 $Ubuntu$ 的安装设置,我们选择简体中文-继续-卸载整个磁盘并安装 $Ubuntu$ - 点击现在安装。地区和键盘布局都可以任意,接下来就是设置账号密码,这里建议不要选择登陆时需要密码毕竟是虚拟机,不需要那么麻烦。点击继续后就安静等待安装就可以啦(记得选择不升级)。
增强功能
我们点击设备-安装增强功能-点击运行-输入密码开始安装。
接下来我们先弄一个 $root$ 用户权限,我们用 $Ctrl+Alt+T$ 唤醒出命令窗口,在里面输入sudo passwd root
,在里面设置 $root$ 用户的密码,一般为了方便记忆可以和 $Ubuntu$ 本身设置的密码一致。
我们输入 su root
,我们输入刚刚设置的 $root$ 用户密码就可以进入 $root$ 权限模式了。我们接着通过 cd /media/你的linux名字/VBox_GAs_6.1.2.2
进入我们的增强工具驱动内,这在我们的文件-设备中是可以看到的,再通过sudo ./VBoxLinuxAdditions.run
运行增强功能的安装脚本。等待安装完成,我们输入sudo reboot
重启虚拟机即可。
其他小组件设置
依次单击“设备-共享文件夹-固定分配确定”,进行共享文件夹设置,这样虚拟机将宿主机的文件目录挂载在自己的根目录下,两个机器可以相互传递文件。依次单击“设备-共享剪切板-双向”, 这样虚拟机和宿主机之间就可以相互复制粘贴了。
由于后期要进行文件的编辑,方便起见我们先下载一个 $vim$ ,我们在 $root$ 模式下使用命令apt-get install vim
进行对 $vim$ 的安装。而自动挂载由于本次实验也用不到,在这里就不进行设置了。
安装编译工具
众所周知, $Ubuntu$是使用 apt 进行软件的下载和安装的,但是在很多情况下,$Ubuntu$ 自带的下载地址非常的慢,所以我们就要换源。以防万一,我们要先备份需要修改的文件$sources.list$,它在目录$/etc/apt/$下,$sources.list$ 是包管理工具$apt$所用的记录软件包仓库位置的配置文件,同样类型的还有位于 同目录下$sources.list.d$文件下的各种$.list$后缀的各文件,我们使用命令sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
进行备份。接下来我们使用命令 sudo vim /etc/apt/sources.list
用 vim 去修改我们的 $apt$ 源,我们在文件的最后加上阿里的源:
deb http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse
接着用sudo apt-get update
更新软件列表就可以了。
完成后,我们使用下列命令安装一些需要用到的软件环境:
sudo apt-get install subversion
sudo apt-get install g++ flex patch
sudo apt-get install libncurses5-dev zlib1g-dev
sudo apt-get install git-core
sudo apt-get install libssl-dev
sudo apt-get install gawk
sudo apt-get install xz-util
其中 $Subversion$ 是一个版本管理系统,可以跟踪文件和目录的历史信息 ,$g++$是 GNU 工程的 $C/C++$编译工具,用于将 $C$ 语言及 $C++$ 语言编译为动态链接库或二进制可执行程序,$FLEX$一个快速词法分析工具 ,$patch$ 是将 $diff$ 文件应用到原始文件的工具,用于在程序开发过程中提交代码,是应用差异文件的工具,$libncurses5-dev$ 用于屏幕终端控制,$zlib1g-dev$ 是压缩及解压缩开发库,$git-core$ 是设计用于大型工程的分布式版本管理工具, 是另外一种代码管理工具软件,$libssl-dev$ 是 $openssl$ 开发库,用于加密解密、计算哈希和数据签名等,$gawk$ 是 $GNU$ 工程实现的 $AWK$ 语言工具,是文本模式扫描和处理的工具,$xz-util$ 是 $xz$ 格式的压缩工具集。
Openwrt的安装
下载Openwrt
我们使用命令 git clone https://git.openwrt.org/15.05/openwrt.git cc
,使用 $git$ 命令来下载 $Openwrt$ 的代码到我们的 $cc$ 文件夹当中。接下来我们通过 cd cc
进入到 $cc$ 文件夹中,并使用 ./scripts/feeds update
更新最新的包定义,但会发现 failed 了。
这时我们就要去修改 $cc$ 中的配置文件,路径是 $cc/feeds.conf.default$,我们用 $vim$ 进行编辑,将这些 $https$ 全部替换为 $git$。接下来我们再使用 ./scripts/feeds update
更新最新的包定义就可以顺利完成了。
我们继续使用./scripts/feeds install -a
安装所有的包。我们输入make defconfig
,在这里会检查所需的编译工具是否齐
备,并生成默认的编译配置文件“.config”。期间可能显示错误fatal: No tags can describe 'e6fbf31baae41b618ff333f3ae55ff032333bd6a'.Try --always, or create some tags. fatal: No tags can describe 'e6fbf31baae41b618ff333f3ae55ff032333bd6a'. Try --always, or create some tags.
我们可以不用理会。
这里我们可以使用 make menuconfig
进入配置工具选项菜单来配置编译固件的内容,其配置选项和 Linux 内核的编译配置非常相似,使用上下左右箭头按键来在编译选项菜单上导航。 在这里可以做很多高级的编译设置,篇幅问题就留给之后慢慢探索了。
编译Openwrt
ARM版本编译
完成以上步骤后,我们使用 make -j2 V=s
表示我们用双线程去编译我们的 $Openwrt$,并且出编译过程中每一步的执行动作,出错后显示详细的错误信息。但是经过一晚上的等待, 我发现我的编译卡在了 HTTP request sent, awaiting response...
这一步。
那么这是为什么呢?查阅资料后,发现理由大致有二:
- openwrt 编译过程会检查依赖库在本地是否缓存,如果没有就会下载,这是首次编译超级慢的主要原因
- 依赖库镜像都是在国外,我们使用国内网络访问不通畅,下载很慢,经常下载失败,即使有重试机制,但挂一个晚上也未必编译成功
- 上面的错误可能是我们将https换成git导致的
我们的解决方法大概分成三步:
1.https://gitee.com/harvey520/openwrt.git
每天自动从官方源拉取更新一次,不会存在更新不及时问题,我们使用命令git clone https://gitee.com/harvey520/openwrt.git
把 openwrt
源码克隆到本地。
2.修改 openwrt
源码目录的 feeds.conf.default
文件中的镜像源。
- 将
https://git.openwrt.org/feed/packages.git
改为https://gitee.com/harvey520/packages.git
- 将
https://git.openwrt.org/project/luci.git
改为https://gitee.com/harvey520/luci.git
- 将
https://git.openwrt.org/feed/routing.git
改为https://gitee.com/harvey520/routing.git
- 将
https://git.openwrt.org/feed/telephony.git
改为https://gitee.com/harvey520/telephony.git
? 3.在 cc 目录下使用rm -rf dl
删除掉原有的 dl 文件夹,然后使用 git clone https://e.coding.net/yao7778899/openwrt-dependent-dl.git dl
提前下载依赖库源码放到 openwrt
源码目录中。
? 4.开始安装和编译,我们在 cc 目录下使用命令:
./scripts/feeds update -a
./scripts/feeds install -a
make V=99
悲伤的是,我们还是卡在了 HTTP request sent, awaiting response...
这一步,这次我们看到了卡住的包是在 https://udomain.dl.sourceforge.net/project/flex/flex-2.5.39.tar.bz2
,我们直接复制出网址,在windows下下载好这个包。现在的问题是,我们如何让将这个包运送到Linux下呢,这就要使用我们的共享文件夹功能了。
我们在 $VirtualBox$ 的 设备-共享文件夹-共享文件夹中,选中固定分配点击右边的新建,在里面选择Windows下的一个文件夹并取个名,选择自动挂载和固定分配。
? 我们回到$Linux$,在$root$用户权限下,打入 sudo mkdir /myshare
在$linux$下创建一个文件夹,然后输入 sudo mount -t vboxsf myshare(VB上设置的共享文件夹名称) /myshare
创建我们的共享挂载,这时我们就可以在我们的 Ubuntu的媒体处看到我们的共享文件夹了。
但是这时我们点击会发现,进不去!没有权限!太弱小了!没有力量!
我们尝试 sudo chmod 777 /myshare
更改文件夹读写权限,但还是进不去,这时就需要一点小技巧了。我们使用 sudo adduser jvruo vboxsf
,其中 $jvruo$ 是我的用户名,把我的用户加入到 $vboxsf$ 的用户组当中。这时我们再点击看看我们的共享文件夹,就发现可以访问我们放入到$windows$的文件了。
我们对传来的这个文件右键,选择发送到,发送到主文件夹,然后在主文件夹下使用命令 sudo cp -r flex-2.5.39.tar.bz2 cc/dl
将我们的文件放到 $dl$ 里面就好啦。我们再开始一轮编译。只要发现有文件下载不下来卡住,我们就故技重施,现在windows下下载,再通过共享文件夹传输到我们的 $Ubuntu$ 当中的 $cc/dl$ 下。
在重复上述过程添加了五个包后,终于进入正常的编译了。长久的等待后,又出现了错误*** error: 'OpenWrtunknown' is too long, max firware version length is 13
在Google上搜索一番后,发现将cc/scripts/getver.sh
脚本第33行替换成try_version || try_git || try_hg || REV="Dev"
,重新编译就OK了。
到此我们再等待一番,终于……终于,终于!完成编译了!
X86版本编译
如果你编译完Arm版本还意犹未尽,想编译个X86版本的话,也可以回到我们的设置部分,我们在 cc 文件夹下使用 make menuconfig
进入配置工具选项菜单来配置编译固件的内容,将第一行改成 X86:
接下来就和$Arm$差不多,在 $cc$ 目录下使用 make V=99
进行编译,期间也可能会出现HTTP request sent, awaiting response...
错误,我们还是故技重施,复制出网址,在$windows$下下载好这个包,然后通过共享的文件夹传输回来。在经过大概三个小时后就完成编译了,完成编译的界面和 arm 版的差不多。
那有的同学可能就要问了,我怎么知道自己的编译正不正常呢?这个很简单,主要有两个方面,一个是你的编译是正常结束的,没有什么$Error$之类的,第二个就是你的 $X86$ 编译结束后 在 cc/bin
下有 openwrt-x86-generic-combined-ext4.img.gz
这个文件就可以啦。
这里附上你可能在编译中会缺少的包,有的包确实不好找。我放在了我的蒟蒻网盘里,有需要的同学可以按需下载:链接:https://pan.jvruo.com/s/0xj7tvcd 密码:Jvruo.com
编译脚本分析
我们上面用了很长很复杂的步骤编译和完善了我们的 cc 文件夹,那你有没有好奇里面到底是些什么东西呢?其实我们的文件分为原始目录和生成目录两种,原始目录就是我们的 cc 文件夹下载下来就有的目录,而生成目录是在编译过程中逐渐补全的目录。
文件夹名 | 功能和作用 |
---|---|
bin(生成目录) | 保存编译完成后的二进制文件,包括:完整的bin文件,所有的ipk文件. |
build_dir(生成目录) | 编译主机使用的工具软件、交叉工具链和内核文件. |
config (原始目录) | 存放着整个系统的配置文件. |
dl(生成目录) | 在编译过程中使用的很多软件,刚开始下载源码并没有包含,而是在编译过程中从其他服务器下载的. |
docs (原始目录) | 包含了整个宿主机的文件源码的介绍, 里面还有Makefile为目标系统生成docs.使用make -C docs/ 可以为目标系统生成文档. |
feeds(生成目录) | openwrt的附加软件包管理器的扩展包索引目录.有点绕,简单来说就是下载管理软件包的.默认的feeds 下载有packages、management、luci、routing、telephony 。如要下载其他的软件包,需打开源码根目录下面的feeds.conf.default文件,去掉相应软件包前面的#号,然后更新源: ./scripts/feeds update -a 安装下载好的包: ./scripts/feeds install -a ,这在我们之后的步骤会用到. |
include(原始目录) | openwrt的Makefile都存放在这里。文件名为 *.mk 。这里的文件上是在Makefile里被include的,类似于库文件.这些文件定义了编译过程. |
package(原始目录) | 存放了openwrt系统中适用的软件包,包含针对各个软件包的Makefile。openwrt定义了一套Makefile模板.各软件参照这个模板定义了自己的信息,如软件包的版本、下载地址、编译方式、安装地址等。在二次开发过程中,这个文件夹我们会经常打交道. 事实上,通过./scripts/feed update -a和./scripts/feed install -a 的软件包也会存放在这个目录之中. |
scripts(原始目录) | 存放了一些脚本,使用了bash,Python,perl等多种脚本语言.编译过程中,用于第三方软件包管理的feeds文件也是在这个目录当中.在编译过程中,使用到的脚本也统一放在这个目录中. |
staging_dir(生成目录) | 编译安装目录。文件安装到这里,并由这里的文件生成最终的编译成果. |
target(原始目录) | openwrt的源码可以编译出各个平台适用的二进制文件,各平台在这个目录里定义了firmware和kernel的编译过程。 |
tmp(生成目录) | 从名字来看,是临时文件夹.在编译过程中,有大量中间临时文件需要保存,都是在这里. |
toolchain(原始目录) | 嵌入式的童鞋应该都知道交叉编译链,这个文件中存放的就是编译交叉编译链的软件包.包括:binutils,gcc,libc 等等. |
tools(原始目录) | 编译时,主机需要使用一些工具软件,tools 里包含了获取和编译这些工具的命令.软件包里面有Makefile文件,有的还包含了patch.每个Makefile当中都有一句$(eval $(call HostBuild)) ,这表明编译这个工具是为了在主机上使用的. |
我们接下来,就要编译脚本了。我们在 $root$ 权限下,先 $cd$ 到 $cc$ 文件夹使用命令 ./scripts/feeds update –a
更新我们已经下载的脚本,然后使用 ./scripts/feeds install -a
安装我们的脚本就可以了。
部署Openwrt
我们接下来的讲解,都将以 $x86$ 的编译结果开始,arm 版的话有兴趣的同学可以自行探究,这里就不展开了。我们先 $cd$ 到 cc/bin/x86
下,使用命令 gunzip openwrt-x86-generic-combined-ext4.img.gz
将我们的编译文件解压,得到openwrt-x86-generic-combined-ext4.img
,然后右键将解压得到的这个文件复制到myshare也就是我们的共享文件夹当中。
这里也放出来贡大家下载:链接:https://pan.jvruo.com/s/y78llzp4 密码:Jvruo.com
? 接下来我们切换到$Windows$系统,我们在$d$盘创建一个叫$openwrt$的文件夹,将我们的openwrt-x86-generic-combined-ext4.img
复制进去,接下来$cd$ 到 $VirtualBox$ 的目录下,使用命令VBoxManage.exe convertfromraw -format VDI D:\openwrt\openwrt-x86-generic-combined-squashfs.img D:\openwrt\openwrt15.vdi
,在openwrt下将其转化为 vdi 文件。如果顺利的话,命令窗会提示 Converting from raw image file="D:\openwrt\openwrt-x86-generic-combined-ext4.img" to file="D:\openwrt\openwrt15.vdi"...Creating dynamic image with size 55050240 bytes (53MB)...
。我们由此所获到了一个 vdi 文件。
我们接下来转战$VirtualBox$,我们新建一个虚拟机叫$openwrt15$,我们选择文件夹到我们之前存放虚拟机的文件夹,类型选择$Linux$,版本选择$Linux 2.6/3.x/4.x(32-bit)$。
我们点击下一步,分配$256MB$的内存。
点击下一步,这时我们的D:\VirtualBox VM
下就已经有个叫$openwrt15$的文件夹了,我们把之前转换好的openwrt15.vdi
复制进去,然后在$VirtualBox$中选择使用已有的虚拟硬盘文件,点击右边的小文件夹。
进入后,我们点击注册,然后在里面选中我们的openwrt15.vdi
就可以正常创建虚拟机啦。
这时我们直接启动,会发现卡在了random: nonblocking pool is initialized
:
这时我们直接回车就能进入$openwrt$了。
如果你卡在了其他地方,尝试打开串口1这样大概率是能打开的:
这时你去ping一下jvruo.com
发现ping不通,这就是没联网的问题。
我们这时先关闭电脑的防火墙,并且在虚拟机中,对openwrt15这个虚拟机分别设置为仅主机和桥接,注意顺序不要反了:
然后在$VirtualBox$左上角属性-主机网络管理器点击属性,按照下图来配置网卡和$DHCP$服务器:
然后我们进入$openwrt15$,用vi etc/resolv.conf
命令打开这个文件,更改如下:
这样我们就能$ping$通百度了!!!!
通常默认编译安装的$OpenWrt$路由器固件没有$Web$管理界面,因此这时我们就可以通过opkg命令进行安装,实验证明在早上进行opkg update
速度比晚上快不少。
opkg update
opkg install luci
/etc/init.d/uhttpd enable
/etc/init.d/uhttpd start
/etc/init.d/firewall stop
上面五个命令成功后界面分别如下,可以对照看看。
到这里我们就完成了对$Openwrt$的部署。
另外这时我们在浏览器输入 192.168.1.1
就能进入我们的$Openwrt$界面:
使用默认账号 root
密码 password
就能登录:
这样我们的前期准备就完成了。
(额外科技)VirtualBox扩容指南
Windows操作
我们的VirtualBox本身是不支持扩容操作了,刚开始你的虚拟容量分配了多少,虚拟机里的空间最大就是多少。这就很容易导致你编译着编译着,呀硬盘炸了,编译出错了。
这时不要慌张,我们先拓展我们的虚拟容量。我们先在Windows cmd下通过 cd 进入到我们 VirtualBox的目录下,然后使用命令 VBoxManage list hdds
查询我们现用虚拟盘的 UUID,我们发现现在有三个有父子关系的虚拟盘,我们用下列命令将他们都扩容到 50G:
VBoxManage modifyhd 6b049aa1-4f0a-4c11-838a-2e144446e38d --resize 51200
VBoxManage modifyhd 63d1a3e1-5256-416a-b649-57b56c14c267 --resize 51200
VBoxManage modifyhd 078ebb0d-9312-4464-a25a-99353a4c5b87 --resize 51200
其中中间那一串就是我们每个虚拟盘的 UUID。
Gparted操作
接下来我们从官网上下载gparted-live-0.25.0-3-i686.iso最新版本:点我进入下载链接 ,然后我们通过修改虚拟光盘文件启动。进入gparted我们发现报错,无法正常启动,查阅资料后发现是我们gparted版本太高==,无法适配我们低版本的Ubuntu。所以我们重新下载gparted-live-0.21.0-1-i586.iso版本:点我进入下载链接,再次修改拟光盘文件启动,正常进入。
这里直接Enter。
这里也直接Enter。
这里选择语言,直接Enter。
这里直接Enter。就终于进入图像化界面了。
在图像化界面中,我们看到未分配的空间是在最后的。而在 gparted 中,我们每次操作只能影响相邻的两个区间,所以我们先将蓝色的区间和我们未分配空间合并,我们需要点击蓝色的空间,进入 resize/move,然后把蓝色拉到最后面,再点击apply。
这样我们的未分配空间就在我们蓝色当中了,接下来我们交换红色空间和未分配空间,我们点击红色空间,进入 resize/move,然后把红色拉到最后面,再点击apply。
这样我们的未分配空间就到我们的蓝色的前面了,我们对蓝色再次 resize/move,把蓝色的左边界拉到最后,再点击apply,将未分配空间从蓝色当中分离出去。
我们最后对前面的深蓝色区间,我们 resize/move,将右边界拉到最后,让未分配空间全部进入它,最后Apply,实现扩容。这时我们的深蓝色就已经把未分配的空间全部吸收进去了。
VirtualBox操作
我们最后需要对 VirtualBox进行一点点操作,那就是进入到设置-存储-控制器,在光驱中移除虚拟盘,要不然是不能进去我们的Ubuntu系统。
HelloWorld
工程简介
为了防止某些小区在断电并自动启动后,均立即访问服务器,对服务器产生瞬间流量冲击,因此路由器启动后产生一个随机延迟时间, 然后再访问服务器。这个时间可以通过配置文件设置, 假如设置为$ 100 $秒, 则访问服务器时间就为 $1~ 100 $秒的随机值。 我们将使用$Openwrt$虚拟机作为路由器来完成这个功能。
创建工程
首先我们的$SDK$工程是有个目录规范的,我们这次的$HelloWorld$工程的目录如下:
|-- files
| |-- hello.conf
| |-- hello.init
|-- Makefile
|-- src
| |-- hello.c
| |-- Makefile
我们下面一个一个文件进行讲解。src/hello.c
这是我们工程的主体完成了我们工程的所有功能,src/Makefile
这是我们源文件的编译文件,而大文件夹下的Makefile
是我们整个工程的编译文件,千万不要弄混了。file
文件夹下的两个文件都是些配置文件,这里附上代码工程文件,在这里就不过多赘述了。
hello.rar
编译工程
我们在Windows下创建好我们的工程软件包后,将它通过共享文件夹传入$Linux$中并复制到主文件夹下,取名叫$hello$,然后通过cp hello cc/package/ -r
将我们的$helloworld$文件包移动到我们熟悉的cc/package
目录下。
完成之后我们 cd cc
进入到我们的目录下,使用make menuconfig
进入我们的设置界面。确保目标系统是X86。
然后我们找到$Network$选项进入。
在里面我们可以看到$ hello $我们用空格将前面调整成 $M$。
这时我们再在$ cc $目录下使用make package/hello/compile
编译我们的$ HelloWorld$软件包。编译成功如下图所示。
我们打开cc/bin/x86/packages/base
就能看到我们的hello_1.0_x86
了。这样我们的编译就完成了。这边也放出来贡大家参考学习:hello_1.0_x86.ipk
部署工程
我们先将刚刚编译生成的hello_1.0_x86
通过共享文件夹移动到$Windows$上,然后我们打开$Openwrt$的虚拟机。再次使用 opkg update
先更新一下我们的opkg
。然后我们去Windows下下载一个$WinScp$,下载完成后我们就可以通过我们$Openwrt$的$IP$也就是192.169.1.1
连接上我们的$Openwrt$了,端口是$22$,账号是$root$,密码是$password$。
连接上后我们可以看到左边是我们电脑的目录,右边是我们$Openwrt$的目录,我们点击进入$Openwrt$的目录。
然后我们在左边点击找到我们的桌面,把我们的hello_1.0_x86
拖到左边,选择确定。
我们回到Openwrt的虚拟机,使用命令opkg install openssl-util
安装一个openssl
,安装成功应该会如下所示。
这时我们已经万事具备了,我们使用命令opkg install hello_1.0_x86.ipk
安装我们的HelloWorld软件包,可以看到成功的界面如下。
这时我们去看我们的$Openwrt$的目录,发现Index.php
确实已经被下载下来了。
并且我们进入jvruo.com
的后台,发现收到了带自己学号的访问信息。
到这里我们整个工程的部署工作就完成了。
更进一步
我们完成了工程的部署,接下来的问题就是我们的工程简介要求我们在路由器重启的时候运行我们的HelloWorld工程,这个我们如何做到呢?这个其实也不难。
我们在浏览器输入192.168.1.1
访问luci,依次进入System-Startup
,在Local Startup
的exit 0
之前加上我们的opkg install hello_1.0_x86.ipk
,点击Submit。然后我们使用poweroff
关闭我们的openwrt
虚拟机,在VirtualBox中再次启动。当我们的启动完成时,我们可以看到我们的网站后台多了两条带我们学号的访问记录,并且我们也成功将我们的Index.php
下载了下来。
因此开机自启动这个部分我们就完美完成了。
再进一步
我们现在还是路由器自己在上网,但路由器嘛总归还是要服务于电脑的。我们接下来实现将我们$Openwrt$虚拟机当作智能路由器,并拥有两个网卡,$NAT$网卡用于连接互联网,内部网络用于连接家庭网个人计算机。$Ubuntu$虚拟机当作家庭PC使用,自动从$OpenWrt$网关处分配$IP$地址。这样我们就可以模拟常见的路由器场景,例如上网、防火墙和$DNS$代理等功能,任何家庭网的数据流量均通过路由器来转发到外部网络。
之前我们$Openwrt$的网卡采取的是桥接加上仅主机的的方式,所以我们首先到设置中将我们$Openwrt$的第一块网卡改成内部网络,第二块网卡改成$NAT$。一定要记得关闭电脑的防火墙!!
接着我们重启我们路由器,通过ping jvruo.com
确认还能上网。
然后我们在$VirtualBox$中将我们的$Ubuntu$的网络设置成内部网络。
这时我们在$Ubuntu$的浏览器中输入192.168.1.1
可以登录我们Openwrt
路由器的Luci
。
同时我们也可以访问外网。这样我们路由器的任务就顺利完了。
所以我们的$Openwrt$作为路由器的$HelloWorld$工程实验就圆满完成了!
网络互连
在实际工作中,经常遇到要查看客户路由器,帮助客户解决问题的情况,但是我们不可能每次都在客户现场,因此反向连接的存在就非常必要。在这一部分当中,我们将要实现互连局域网和广域网,实现不同网络互相通信。其主要的原理就是内网穿透,即因为客户路由器除了拨号当主路由的以外,对我们来说基本上都是局域网的IP我们不可能直接访问到,那么我们就只能让路由器主动去连接到一个公网服务器,然后我们通过公网服务器再连接到路由器上就实现了反向连接。
由于内网穿透要使用没有搭建网站的服务器,所以jvruo.com
指定是派不上用场了,所以我们这次使用花生壳这个软件来进行免费的内网穿透。我们首先进入花生壳官网下载花生壳。
然后我们点击右下角加号进入后新增映射,进行配置。其中内网主机的ip就是我们的$192.168.1.1$就是我们$Openwrt$的$ip$,后面的$80$端口也是我们访问$luci$时使用的端口。记得应用类型要选$HTTP$。配置和配置好的界面如下。
配置完成后,我们就可以通过外网域名http://w4109830o2.qicp.vip
来访问我们的路由器了。我们使用手机进行测试,发现在没有连接内网的情况下,确实完成了内网渗透,访问到了内网的路由器,实现了不同网络互相通信。
数据压缩
在这一部分我将简单介绍一下数据的压缩这一部分。说到数据压缩,那肯定少不了哈夫曼编码。哈夫曼编码$(Huffman Coding)$,又称霍夫曼编码,是一种编码方式,可变字长编码$(VLC)$的一种。$Huffman$于$1952$年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,一般就叫做$Huffman$编码(有时也称为霍夫曼编码)。哈夫曼编码,主要目的是根据使用频率来最大化节省字符(编码)的存储空间。换句话说,我们的哈夫曼编码可以让出现多的字符的编码尽量短。
对于哈夫曼编码,在数据结构和信息论相关课程都已经提到过了,下面直接给出代码:
编码代码:
#include <map>
#include <queue>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
struct node {
int sum;
char maxchar;
int leaf;
int site;
int l, r;
bool operator < (const node &v) const {
if(sum > v.sum) return(true);
else if(sum == v.sum && maxchar > v.maxchar) return(true);
else return(false);
}//重载<号的定义
}l, r, a[1100000], root;
int tot = 0, len = 0;
char s[1100000], ch;
map<char, int> num;
map<char, string> haff;
priority_queue<node> q;
string ss = "";
void find(node u) {
if(u.leaf == 1) {
haff[u.maxchar] = ss;
return ;
}
if(u.l) {
ss += '0';
find(a[u.l]);
ss = ss.substr(0, ss.length() - 1);
}
if(u.r) {
ss += '1';
find(a[u.r]);
ss = ss.substr(0, ss.length() - 1);
}
return ;
}
int main() {
while(scanf("%c", &ch) != EOF) {
if(ch == '\n' || ch == '\r') break;
s[++len] = ch;
num[ch]++;
}
map<char, int>::iterator iter;
for(iter = num.begin(); iter != num.end(); iter++){
//if(iter -> first == 10) continue;
tot++;
a[tot].leaf = 1;
a[tot].site = tot;
a[tot].l = a[tot].r = 0;
a[tot].maxchar = iter -> first;
a[tot].sum = iter -> second;
q.push(a[tot]);
}
int op = tot;
while(q.size() >= 2) {
l = q.top(); q.pop();
r = q.top(); q.pop();
tot++;
a[tot].l = l.site;
a[tot].r = r.site;
a[tot].site = tot;
a[tot].leaf = 0;
a[tot].maxchar = max(l.maxchar, r.maxchar);
a[tot].sum = l.sum + r.sum;
q.push(a[tot]);
}
root = q.top();
find(root);
printf("%d\n", op);
map<char, string>::iterator iterr;
for(iterr = haff.begin(); iterr != haff.end(); iterr++){
printf("%c ", iterr -> first);
cout << iterr -> second << endl;
}
for(int i = 1; i <= len; i++) cout << haff[s[i]];
return 0;
}
译码代码:
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
struct node {
int son[2];
char c;
}tr[1100000];
int n, tot = 0, len, now;
char ch, s[1100000];
void build(int u, int x, char ch) {
if(x == len + 1) {
tr[u].c = ch;
return ;
}
int num = s[x] - '0';
if(tr[u].son[num] == 0) {
tot++;
tr[u].son[num] = tot;
tr[tot].c = tr[tot].son[0] = tr[tot].son[1] = 0;
}
build(tr[u].son[num], x + 1, ch);
return ;
}
int main() {
tr[0].c = tr[0].son[0] = tr[0].son[1] = 0;
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%c", &ch); //换行符
scanf("%c", &ch);
scanf("%s", s + 1);
len = strlen(s + 1);
build(0, 1, ch);
}
scanf("%s", s + 1);
len = strlen(s + 1);
now = 0;
for(int i = 1; i <= len; i++) {
int num = s[i] - '0';
now = tr[now].son[num];
if(tr[now].c != 0) {
printf("%c", tr[now].c);
now = 0;
}
}
return 0;
}
那我们可不可以把这个压缩代码部署到我们的$Openwrt$上面呢。当然可以,但是没必要。经过压缩,我们的文件已经变得直接不可用,每次要用文件的时候都要先进行解压,使用完后又要压缩,这会让我们的路由器变得非常非常非常慢!所以我个人的建议是中国数据压缩用到我们路由器备份的时候,当下载到我们本地电脑的时候,可以进行压缩。要使用的时候我们再解压就可以了。
网络管理
我们的$Openwrt$的$luci$界面有非常强大的配置功能,我们进入通过浏览器访问$192.168.1.1$访问我们的$luci$,点击进入$Network$-$Firework$就能看到我们能根据自己的需求进行相关配置。
其中包括但不限于常用配置、端口转发和流量控制。我们可以根据需求自己加入或者删除策略。
个人总结
在这次的$Openwrt$实验中,我历时近一个月终于完成了整个实验。回首做实验的那段日子,由于我和大部分同学包括宿舍的舍友走的道路不太一样,他们直接用的是最新的$Openwrt$,我坚持使用指南上的$Openwrt15.05$,但也正是因为这个原因,我们遇到的问题非常不一样,不少问题甚至百度都难以寻觅到答案要去啃当时的官方文档。但就算这样也非常感谢身边的同学和老师给予的鼎力相助,让我完成了这个实验。
说实话,花这么长的时间去做一个实验,完成的那一刻说没有一种满足感和自豪感是不可能的。在这段做实验的时光里,每次取得哪怕是一点点突破都可以让我高兴大半天,这是我们平常理论教学所难以收获到的那种自己动手丰衣足食的感觉。同时由于也是第一次做$Openwrt$的实验,在实验过程中也走了不少的弯路,实验报告中可能短短一行字背后也许是我几天不眠不休探索的结果。也希望这份实验报告能够给后来的同学一些少走弯路的启发吧。
你们居然做openwrt的实验?惊了
那是,我们可是技术流派呢