写在开始

使用Hyper-V的时候他会自动分配一个Default Switch这么一个网卡用于NAT, 然而这个网卡的ip似乎每次重启都会自动更换, 所以这个就挺令人烦恼了, 毕竟如果Hyper-V虚拟机里面是一堆linux的话那每次都是自动更换ip的, 连接起来还是很麻烦的。 所以我打算在内网给解决掉这个问题, 把IP地址至少可以接近静态的, 之前那个连IP段都要变确实有点受不了。

技术选型

其实这个可以直接在虚拟机里跑一个OpenWRT之类的东西的, 但是鉴于我有一个这样子的虚拟机, 而且还被我莫名其妙搞坏了, 这次我就手动搭建一个路由器系统了。

至于选哪个系统, Linux用的比FreeBSD之类的熟悉一点, Windows太占资源。 然后基于节约资源的角度考虑, 我用了 Alpine Linux。 毕竟这个系统的体积小。 而且这个系统我在玩Docker镜像的时候经常用, 所以也不算陌生。

其他的东西也就是用vagrant来操作hyper-v了, 这样子会简单点。

那开始吧

下载 Alpine Linux 的 Box

先去找找有没有现成的Vagrant的Box, 然后我发现了这个Box, 这个Alpine Linux的Box支持Hyper-V的版本。 所以就用这个了。

那首先随便找个目录, 然后输入

vagrant init generic/alpine310
sudo vagrant up --provider=hyperv

因为不是用管理员打开的这个终端, 然后如果操作Hyper-V的虚拟机的话需要管理员权限, 所以需要提权。 这里推荐一下这个人的sudo脚本。 感觉还是挺好用的。

顺便一提, vagrant是用curl来下载box的。

Hyper-V部分的设置

在vagrant下载box的这一会去设置一下Hyper-V那边的东西。

打开Hyper-V管理器, 在右侧找到虚拟交换机管理器

点开之后创建一个内部的交换机

名称随意, 注意是要设置成内部网络, 就像这样

然后点确定就好。

Vagrant Box 下载完成后

vagrant up执行过程中会打印出类似以下的输出

这个时候选择 Default Switch就好然后接下来会出来一堆输出, 其实也不用在意是啥。

然后sudo vagrant halt关闭这个虚拟机, 接下来往里面添加一个给内网用户使用的网卡。

首先要在Hyper-V控制台里面找到这个虚拟机, 然后点击它, 右边会出来一排按钮

选择设置。 然后选择添加硬件

里面有一个网络适配器, 点击之后选择添加。

然后选择之前创建的那个内部网络。

选择完成之后大概长这个样子

然后点击确定, 回到这里

点击启动重启开机。

接下来就sudo vagrant ssh进入这个虚拟机里操作就好。

构建一个简易的软路由器的基本思路

首先要有一个DHCP服务器给内网设备分配IP用, 然后要有一个DNS服务器给内网设备提供DNS服务, 最重要的还要有一个NAT和转发来让内网设备可以通过NAT来连接到外部网络, 然后还需要能够回来。 这样子一个路由器就完成了。

那么我们一步一步做就好

软路由构成的基本选型

使用Dnsmasq来给内网设备提供DHCP和DNS, 然后用iptables创建一个NAT和处理请求转发。

曾经还考虑过使用AdGuardHome来提供DHCP和DNS服务来着, 奈何它的DHCP看起来不工作的样子, 也很不稳定的样子, 所以我就抛弃了这个选择。

那么就进入软路由里进行操作吧

毕竟是个国外的Box, 所以自带的软件源自然不会很快, 这里换成中科大的源。

首先看一下这个源是啥

cat /etc/apk/repositories

然后他输出了

https://dl-4.alpinelinux.org/alpine/v3.10/main
https://dl-4.alpinelinux.org/alpine/v3.10/community

中科大官方提供的一键脚本是这样的

sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories

这样子很明显不能成功替换, 所以要修改成这样子

sudo sed -i 's/dl-4.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories

替换完成之后执行

sudo apk update

更新完成之后就可以准备安装软件了。

需要安装的软件

因为这个Box非常精简, 精简到连iptables都没有, 所以要装这些东西:

  1. dnsmasq
  2. iptables
  3. ip6tables

那么就一条命令加上去吧

sudo apk add dnsmasq iptables ip6tables

Dnsmasq 配置

接下来配置一下Dnsmasq。 在这之前先确认一下自己的网卡

ip addr

然后输出了三个网卡, 一个是lo, 一个是eth0, 一个是eth1。

eth0是走了Default Swtich的, 算是路由器的WAN口, eth1是走了内部网络的, 算是路由器的LAN口, 确定了这个之后开始配置Dnsmasq。

sudo vim /etc/dnsmasq.conf

接下来我就贴一下我改了的配置, 然后一条一条说好了。

resolv-file=/etc/resolv.conf
interface=eth1
dhcp-range=192.168.1.50,192.168.1.150,255.255.255.0,12h
dhcp-option=option:router,192.168.1.1
cache-size=500
conf-dir=/etc/dnsmasq.d/,*.conf

这些是整个dnsmasq.conf里面使用的参数。

resolv-file 是声明了上游DNS服务器的地方, 这个我直接用了系统自动配置的DNS, 反正能上网, 所以问题不大。

interface 是声明DHCP服务器启动的接口, 这里要设置LAN口的, 所以是eth1

dhcp-range 这个就挺好懂了, 就是设置DHCP自动分配的一些设置, 第一个是起始IP, 第二个是结束IP, 第三个是子网掩码, 第四个是DHCP过期时间。

dhcp-option这个是提供了一些别的参数, 这个是为了给下游设备提供网关的一个设置, 最后面是设置网关IP的, 我设置为192.168.1.1, 过会我会把路由器的 LAN IP 设置为这个的。

cache-size 这个好像是DHCP缓存, 会让你缓存之前DHCP分配的历史记录, 下一次这个设备重新获取IP的时候会先走缓存找到上次的IP。 这个后面的数字随缘设置就好, 算是个可以优化的点吧。 我就随便设置了个500, 不改也可以。

conf-dir 这个默认就带着的, 会读取这个目录下面的conf文件, 来自定义配置Dnsmasq, 我也没改, 就放上去了。

配置完之后保存就好, 接下来设置LAN口的静态IP。

配置 LAN 口的静态 IP

那就直接进interface文件里加上这个接口的静态IP就好了

sudo vim /etc/network/interfaces

打开之后你会看到这样的东西

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet dhcp
    hostname alpine310.localdomain

然后在后面添加上

auto eth1
iface eth1 inet static
    address 192.168.1.1
    network 192.168.1.0
    netmask 255.255.255.0
    broadcast 192.168.1.255

这算是一个很经典的配置方式了吧。

address 设置这个接口的静态IP

network 这玩意其实我也不知道是啥, 但是应当是设置为这个网络的起始IP的前一个, 也就是常见的监听整个网络的那个IP。

netmask 这个是子网掩码

broadcast 这个是广播地址

这些配置完之后重启一下network服务

sudo service network restart

然后你就可以启动Dnsmasq了

sudo service dnsmasq start

接下来配置 iptables

因为只配置IPv4, 所以我就没有管IPv6的转发,

直接执行下面四条命令就可以了

sudo iptables -A FORWARD -i eth1 -s 192.168.1.0/255.255.255.0 -j ACCEPT
sudo iptables -A FORWARD -i eth0 -d 192.168.1.0/255.255.255.0 -j ACCEPT
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
sudo /etc/init.d/iptables save

第一条是允许从eth1进来的来源于那个子网的连接

第二条是允许从eth0的数据到那个子网里

就是一条允许出一条允许进

第三条就是把这个挂到POSTROUTING链上

第四条是保存这个iptables记录。

这个东西多少还是有点晕。 其实我自己也说不太清楚。

所以引用一句这位老哥的话

第三条表示把数据最终的POSTROUTING定向到eth0也就是WAN口上,使用MASQUERADE转译是为了保证NAT出去的地址是固定的。

(其实好多细节也是从这里看到的, 还有这里

然后启动开内核的IP转发, 这个版本的 Alpine Linux 有一个/etc/sysctl.d文件夹, 里面有一个00-alpine.conf的文件, 所以你可以仿照着这种文件格式添加一个01-router.conf之类的文件添加上这些东西, 或者也可以直接添加到00-alpine.conf里面, 看你的个人爱好了。

然后在那个配置文件里添加上这么一条

net.ipv4.ip_forward=1

似乎这时候sysctl -p并不生效, 所以还是要重启一下比较好。

给这两个东西加一个开机自启

Alpine Linux 的服务管理器是OpenRC, 一个比较轻量而且经典的进程管理器。

rc-update add dnsmasq
rc-update add iptables

给自己的宿主机设置一个静态IP

这个就随缘设置一个就好, 右键托盘里的网络

打开"网络和 Internet"设置

找到里面的更改适配器选项。

然后找到内部网路右键属性

双击 Internet 协议版本 4

简单配置一下就好了, 这个时候不建议设置网关和DNS, 设置了DNS的话可能会导致系统DNS走了这个, 导致出现循环。 网关也是。

其实到这里就结束了, 但是我还需要另一个东西

接下来装一下vlmcsd, 懂得都懂, 不知道的就不用管了。

直接抄一下人家的Dockerfile其实就好, 直接在终端输入

sudo su
apk update \
    && apk upgrade \
    && apk add --no-cache build-base gcc abuild binutils cmake git \
    && cd / \
    && git clone https://github.com/Wind4/vlmcsd.git vlmgit \
    && cd vlmgit \
    && make \
    && chmod +x bin/vlmcsd \
    && mv bin/vlmcsd / \
    && cd / \
    && apk del build-base gcc abuild binutils cmake git \
    && rm -rf /vlmgit  \
    && rm -rf /var/cache/apk/*

等着结束之后会在根目录下出现一个vlmcsd, 然后mv /vlmcsd /usr/local/bin/放到一个比较正常的目录下。 配置一下service。

于是就进入/etc/init.d里面创建一个文件, 内容大概这样子就好。

#!/sbin/openrc-run

description="vlmcsd Server"

pidfile="/run/$RC_SVCNAME.pid"
command="/usr/local/bin/vlmcsd"
command_args=" -D -d -t 3 -e -v"
command_background=true

extra_commands="checkconfig"

depend() {
    need net
    after firewall
}

stop() {
    if [ "${RC_CMD}" = "restart" ] ; then
        checkconfig || return 1
    fi

    ebegin "Stopping $RC_SVCNAME"
    start-stop-daemon --stop --exec "$command" \
        --pidfile "$pidfile" --quiet
    eend $?
}

创建完成之后给他加个可执行权限

chmod +x vlmcsd

然后启动

service vlmcsd start

添加开机自启

rc-update add vlmcsd

就完工了。

写在最后

之前一直以为人工做一个软路由很麻烦来着, 结果这么操作了一下发现还是很简单的, 至少现在思路明确了, 整体来说就简单好多了。

Last modification:March 7, 2020
如果觉得我的文章对你有用,请随意赞赏