在网上看到一篇文章

The Ars guide to building a Linux router from scratch

很有意思,不仅过程很有趣,还能学到不少东西(文章封面就是作者的路由器照片)。这里简单总结记录一下。

打造路由器整个过程,分硬件和软件两部分。

硬件就是买一台mini PC,然后作者还加了个SSD硬盘。

软件部分需要懂得我们平常买的路由器里具体包含哪些功能,除了UI以外,有

  • Routing

  • NAT

  • DHCP

  • DNS

很神奇的是,上面这些功能只需要在一个普通的linux系统上就能配置和实现,作者用的ubuntu。

首先,作者的mini PC上有2个网口,一个LAN口,和一个WAN口,网线插到WAN口上。

先手动配置这2个网口,编辑文件 /etc/network/interfaces

# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

# The loopback network interface
auto lo
iface lo inet loopback

# The WAN interface, marked Lan1 on the case
auto p4p1
iface p4p1 inet dhcp

# The LAN interface, marked Lan2 on the case
auto p1p1
iface p1p1 inet static
  address 192.168.99.1
  netmask 255.255.255.0

可以看到这里p4p1是WAN口,p1p1是LAN口。

路由

配置路由,就是配置IP packet转发,非常简单,编辑文件 /etc/sysctl.conf

# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1

# Uncomment the next line to enable packet forwarding for IPv6
#  Enabling this option disables Stateless Address Autoconfiguration
#  based on Router Advertisements for this host
#net.ipv6.conf.all.forwarding=1

如上,设置 net.ipv4.ip_forward=1 即可。

创建 iptables 规则文件

为了方便管理,作者建了一个 iptables 规则文件 /etc/network/if-pre-up.d/iptables ,之后对 iptables的修改都在这个文件里,每次修改完,只需要执行 /etc/network/if-pre-up.d/iptables 即可生效,注意设立需要设置该文件的访问权限为755,root可以可读可写可执行,其他用户可读可执行。如下:

#!/bin/sh
/sbin/iptables-restore < /etc/network/iptables

可以看到它执行了 /etc/network/iptables 里的内容,如下:

*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]

COMMIT

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]

# Service rules
-A INPUT -j DROP

# Forwarding rules
-A FORWARD -j DROP

COMMIT

这个文件里有2种 iptable 规则,一个是nat,一个是filter。后续看到具体的用途。注意,这里filter规则里,所有的input和forward请求都会drop,所以我们的路由规则实际上是非常严格的,后面会按照需求逐渐放开权限。

NAT

nat其实就在nat规则链里添加一条命令的事。

*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]

# p4p1 is WAN interface, #p1p1 is LAN interface
-A POSTROUTING -o p4p1 -j MASQUERADE

COMMIT

所有去到WAN口的packet都执行masquerade。

DHCP 和 DNS

因为实在太简单,这连个步骤合成一个。

安装dhcp和dns对应的软件。

sudo apt-get install isc-dhcp-server
sudo apt-get install bind9

编辑文件 /etc/dhcp/dhcpd.conf:

subnet 192.168.99.0 netmask 255.255.255.0 {
  range 192.168.99.100 192.168.99.199;
  option routers 192.168.99.1;
  option domain-name-servers 192.168.99.1;
  option broadcast-address 192.168.99.255;
}

很简单,就不解释了。

这里虽然安装好了,但是还不够,需要配置iptables规则。允许局域网里的其他节点访问我们的路由器。因为目前为止,我们的路由器仅仅自己可以访问公网,但它不能接收局域网的请求,也不会转发数据包。

# Service rules

# basic global accept rules - ICMP, loopback, traceroute, established all accepted
-A INPUT -s 127.0.0.0/8 -d 127.0.0.0/8 -i lo -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -m state --state ESTABLISHED -j ACCEPT

# enable traceroute rejections to get sent out
-A INPUT -p udp -m udp --dport 33434:33523 -j REJECT --reject-with icmp-port-unreachable

# DNS - accept from LAN
-A INPUT -i p1p1 -p tcp --dport 53 -j ACCEPT
-A INPUT -i p1p1 -p udp --dport 53 -j ACCEPT

# SSH - accept from LAN
-A INPUT -i p1p1 -p tcp --dport 22 -j ACCEPT

# DHCP client requests - accept from LAN
-A INPUT -i p1p1 -p udp --dport 67:68 -j ACCEPT

# drop all other inbound traffic
-A INPUT -j DROP

访问外网

这是一个基本功能,需要2条规则。一个是conntrack,一个是把LAN

口的请求都转发到WAN口上。

# Forwarding rules

# forward packets along established/related connections
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# forward from LAN (p1p1) to WAN (p4p1)
-A FORWARD -i p1p1 -o p4p1 -j ACCEPT

# drop all other forwarded traffic
-A FORWARD -j DROP

到这一步,一个基本路由器的功能都实现了。

端口转发

实现端口转发的目的是可以让公网直接连局域网的服务器,一般用户实际上没有这个需求。在普通的路由器里可以通过UI设置,但在我们自己打造的路由器,每一条转发规则,需要在路由表里修改两项。

nat规则:

*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]

# p4p1 is WAN interface, #p1p1 is LAN interface
-A POSTROUTING -o p4p1 -j MASQUERADE

# NAT pinhole: HTTP from WAN to LAN
-A PREROUTING -p tcp -m tcp -i p4p1 --dport 80 -j DNAT --to-destination 192.168.99.100:80

COMMIT

filter规则:

# Forwarding rules

# forward packets along established/related connections
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# forward from LAN (p1p1) to WAN (p4p1)
-A FORWARD -i p1p1 -o p4p1 -j ACCEPT

# allow traffic from our NAT pinhole
-A FORWARD -p tcp -d 192.168.99.100 --dport 80 -j ACCEPT

# drop all other forwarded traffic
-A FORWARD -j DROP

最终,完整的 iptables 规则文件 /etc/network/iptables:

*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]

# p4p1 is WAN interface, #p1p1 is LAN interface
-A POSTROUTING -o p4p1 -j MASQUERADE

# NAT pinhole: HTTP from WAN to LAN
-A PREROUTING -p tcp -m tcp -i p4p1 --dport 80 -j DNAT --to-destination 192.168.99.100:80

COMMIT

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]

# Service rules

# basic global accept rules - ICMP, loopback, traceroute, established all accepted
-A INPUT -s 127.0.0.0/8 -d 127.0.0.0/8 -i lo -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -m state --state ESTABLISHED -j ACCEPT

# enable traceroute rejections to get sent out
-A INPUT -p udp -m udp --dport 33434:33523 -j REJECT --reject-with icmp-port-unreachable

# DNS - accept from LAN
-A INPUT -i p1p1 -p tcp --dport 53 -j ACCEPT
-A INPUT -i p1p1 -p udp --dport 53 -j ACCEPT

# SSH - accept from LAN
-A INPUT -i p1p1 -p tcp --dport 22 -j ACCEPT

# DHCP client requests - accept from LAN
-A INPUT -i p1p1 -p udp --dport 67:68 -j ACCEPT

# drop all other inbound traffic
-A INPUT -j DROP

# Forwarding rules

# forward packets along established/related connections
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# forward from LAN (p1p1) to WAN (p4p1)
-A FORWARD -i p1p1 -o p4p1 -j ACCEPT

# allow traffic from our NAT pinhole
-A FORWARD -p tcp -d 192.168.99.100 --dport 80 -j ACCEPT

# drop all other forwarded traffic
-A FORWARD -j DROP

COMMIT

最后,作者还做了一些性能测试,我不太关注,就不记录了。

Refer:

https://arstechnica.com/gadgets/2016/04/the-ars-guide-to-building-a-linux-router-from-scratch/


original refer