0%

树莓派拨号上网并作为网关

设备

  • Newifi D2
  • RaspberryPi 3B+
  • 笔记本电脑

网络拓扑

  • Raspberry Pi 3B+Newifi D2 CPU(Openwrt) 的地位是类似的,它们都同时属于 VLAN-1VLAN-2eth0.1 处理 VLAN-1 的数据包,eth0.2 处理 VLAN-2 的数据包
  • Raspberry Pi 3B+Newifi D2 CPU(Openwrt) 的不同之处仅仅在于,Raspberry Pi 3B+ 通过外部接口LAN1接入交换机,而Newifi D2 CPU(Openwrt)通过内部接口Internal Port接入交换机
  • Newifi D2 CPU(Openwrt) 内部通过网桥 br-laneth0.1wlan0wlan1 桥接在一起,使得 wlan0wlan1 可以和 eth0.1 所属的 vlan1 互通

配置过程

VLAN配置

交换机VLAN配置

  • 路由器内嵌可编程交换机的 LAN2/LAN3/LAN4 属于 untagged VLAN-1
  • 路由器内嵌可编程交换机的 WAN 属于 untagged VLAN-2
  • 路由器内嵌可编程交换机的 LAN1Internal Port 属于 tagged VLAN-1 & tagged VLAN-2

树莓派VLAN接口配置

树莓派的 eth0.1 属于 VLAN 1eth0.2 属于 VLAN 2,到达 eth0 的数据包会根据其中包含的 VLAN ID 传递到对应的VLAN虚拟接口,而从VLAN虚拟接口发出的数据包也会打上对应的 VLAN ID (后两句是我的猜测,然而由于我并不了解Linux协议栈以及虚拟接口的工作原理,无法保证准确性,大致工作原理可能类似)。其实路由器的 eth0.1eth0.2 也是这样的。

配置方法

  1. 通过IP命令配置
    1
    2
    sudo ip link add link eth0 name eth0.1 type vlan id 1
    sudo ip link add link eth0 name eth0.2 type vlan id 2
  2. /etc/network/interfaces 中配置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # interfaces(5) file used by ifup(8) and ifdown(8)

    # Please note that this file is written to be used with dhcpcd
    # For static IP, consult /etc/dhcpcd.conf and 'man dhcpcd.conf'

    # Include files from /etc/network/interfaces.d:
    source-directory /etc/network/interfaces.d

    auto eth0.1
    iface eth0.1 inet manual

    auto eth0.2
    iface eth0.2 inet manual

为树莓派的 eth0.1 配置静态IP

树莓派默认会通过 dhcpcd 为各个接口配置IP,可以为树莓派的eth0.1配置静态IP便于进行局域网访问并进一步作为网关。可以在 /etc/dhcpcd.conf 中配置:

1
2
interface eth0.1
static ip_address=192.168.2.10/24

如果仅仅希望阻止 dhcpcd 自动为某些接口通过局域网中的DHCP服务器配置IP,可以这样配置:

1
2
# deny
denyinterfaces eth0 eth0.2

树莓派利用PPPoE连接网络

Raspberry Pi 3B+ 可以通过 eth0.2 与经 WAN 连接的运营商通信,就像 Newifi D2 CPU(Openwrt) 一样。

连接步骤

  1. 安装 pppoe 相关软件包
    1
    sudo apt install pppoe pppoeconf pppstatus
  2. 配置 pppoe 连接信息
    1
    sudo pppoeconf eth0.2
    然后根据提示输入账号和密码即可,账号和密码只需配置一次
  3. 连接管理
    1
    2
    3
    sudo pon dsl-provider #建立连接
    sudo poff #断开连接
    sudo plog #查看连接日志

连接成功之后,通过 ip -d addr 就能看到PPPoE接口 ppp0,下面的内容还顺便展示了 eth0.1eth0.2 的接口信息,可以忽略其中的 wlan0,树莓派的 wlan0 其实连接到了路由器的无线网络 Openwrt 当中,与本次实验关系不大。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether b8:27:eb:98:76:96 brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 68 maxmtu 9000 numtxqueues 1 numrxqueues 1 gso_max_size 8824 gso_max_segs 65535

3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether b8:27:eb:cd:23:c3 brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 68 maxmtu 1500 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
inet 192.168.2.151/24 brd 192.168.2.255 scope global dynamic noprefixroute wlan0
valid_lft 27475sec preferred_lft 22075sec

4: eth0.1@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether b8:27:eb:98:76:96 brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 0 maxmtu 65535
vlan protocol 802.1Q id 1 <REORDER_HDR> numtxqueues 1 numrxqueues 1 gso_max_size 8824 gso_max_segs 65535
inet 192.168.2.10/24 brd 192.168.2.255 scope global noprefixroute eth0.1
valid_lft forever preferred_lft forever

5: eth0.2@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether b8:27:eb:98:76:96 brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 0 maxmtu 65535
vlan protocol 802.1Q id 2 <REORDER_HDR> numtxqueues 1 numrxqueues 1 gso_max_size 8824 gso_max_segs 65535

6: ppp0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1492 qdisc pfifo_fast state UNKNOWN group default qlen 3
link/ppp promiscuity 0 minmtu 0 maxmtu 0
ppp numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
inet 100.82.232.18 peer 100.82.0.1/32 scope global ppp0
valid_lft forever preferred_lft forever

树莓派配置路由表

如果树莓派在建立PPPoE之前不存在默认路由,那么在PPPoE连接过程中应该会自动添加至新建的 PPPoE接口 ppp0 的默认路由,否则,它将不会覆盖原本就存在的默认路由。
由于我的树莓派还同时经树莓派的无线网卡 wlan0 连接了路由器的无线网络,原本存在默认路由,故在连接PPPoE之后需要配置默认路由,从而确保树莓派通过其自身建立的 ppp0 连接互联网,而非路由器。

配置好之后的默认路由为PPPoE接口 ppp0

1
2
3
4
5
6
pi@rasp:~ $ ip route

default dev ppp0 scope link
100.82.0.1 dev ppp0 proto kernel scope link src 100.82.232.18
192.168.2.0/24 dev eth0.1 proto dhcp scope link src 192.168.2.10 metric 204
192.168.2.0/24 dev wlan0 proto dhcp scope link src 192.168.2.151 metric 303

网络测试1

在完成上述配置之后,如果不出意外的话,树莓派应该就可以连接互联网了,可以随便ping一下百度进行测试,或者ping一个知名如 114.114.114.114,如果能ping IP却不能ping 域名,可以检查下DNS设置,在此不再赘述。

配置NAT(Network Address Translation)

完成上面的配置之后,树莓派本身可以通过其建立的PPPoE连接互联网,但局域网中的其他设备却仍然无法访问互联网,那么如何使其他设备可以借助树莓派的PPPoE访问互联网呢?答案是NAT。

NAT的中文名称是网络地址转换,配置好NAT之后,局域网其他设备就可以将树莓派作为网关,然后该设备(称为设备A)的网络协议栈就会将目的地址为外部网络的数据包发往树莓派(IP数据包目的地址仍然是真实目的IP,MAC数据包的目的地址是树莓派,准确地说是树莓派的eth0.1),树莓派会将原始IP数据包的 src 改为自身PPPoE接口ppp0的IP地址,然后发往目标设备,并且,会将目标设备返回的IP数据包的 dst 改为设备A的IP地址,使得局域网设备可以透明地与互联网中的设备进行通信。
如果想要深入了解NAT,可以参考Network address translation - WikipediaNAT - Network Address Translation,前一篇文章侧重于NAT的概念和实现,后一篇文章侧重于Linux系统中NAT的配置和用途。

配置方法
配置NAT需要使用*nux的防火墙工具,比如 iptables。关于防火墙可以参考Linux防火墙配置(iptables, firewalld)

在本次实验当中,我使用iptables,并且配置为masquerade,除了masquerade还可以采用snat。他们的区别在于,snat需要指定要改写为哪个IP地址,这种情况适用于静态IP用户,而PPPoE分配的IP一般为动态IP,这时一般应该采用masquerade,在这种情况下,无需指定要改写为哪个IP,内核会自动读取对应网络接口的IP地址进行改写。

不过在配置NAT之前,需要先开启Linux内核的数据包转发功能,默认情况下会禁止转发数据包。配置命令如下:

1
sudo bash -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'

下面是NAT配置命令:

1
sudo iptables -t nat -A POSTROUTING -s 192.168.2.0/24 -j MASQUERADE

上面的命令的意思是这样的,在nat表中的POSTROUTING链中添加一条规则:如果IP数据包的 src 属于 192.168.2.0/24 网段,则在发送出去之前,改写其 src 为出口网络接口的IP地址。同时,该规则隐含了一条反向规则,将应答数据包的 dst 改为真实的 dst(即被改掉的 src )。实际实现过程中,只改IP应该是不够的,可能还需要修改sport,并对应修改应答数据包的dport

网络测试2

完成上述配置之后,为局域网设备设置合适的IP地址,网关设为树莓派的局域网IP 192.168.2.10,配置好DNS,应该就可以访问互联网了。

我的测试设备IP是这样的:

  • 笔记本电脑:无线网卡连接至路由器的无线网 Openwrt-5G,IP为 192.168.2.51Openwrt-5G经路由器的br-lan与路由器的eth0.1桥接在一起,从而能够接入vlan1
  • 树莓派:通过 LAN1 接入 vlan-1vlan-2eth0.1 的IP为 192.168.2.10,经eth0.2建立的PPPoE连接 ppp0 的IP为 100.82.232.18

测试方法为,在笔记本电脑上 ping 8.8.4.4,然后在树莓派的 eth0.1ppp0 上抓包。测试结果如下:

ping 8.8.4.4

1
2
3
4
5
6
7
8
9
10
11
12
PS C:\Users\lpy> ping -n 4 8.8.4.4

正在 Ping 8.8.4.4 具有 32 字节的数据:
来自 8.8.4.4 的回复: 字节=32 时间=208ms TTL=113
来自 8.8.4.4 的回复: 字节=32 时间=209ms TTL=113
来自 8.8.4.4 的回复: 字节=32 时间=209ms TTL=113
来自 8.8.4.4 的回复: 字节=32 时间=209ms TTL=113

8.8.4.4 的 Ping 统计信息:
数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
最短 = 208ms,最长 = 209ms,平均 = 208ms

抓包结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
pi@rasp:~ $ sudo tcpdump -i eth0.1 dst 8.8.4.4
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0.1, link-type EN10MB (Ethernet), capture size 262144 bytes
14:20:42.281074 IP 192.168.2.51 > dns.google: ICMP echo request, id 1, seq 346, length 40
14:20:43.292657 IP 192.168.2.51 > dns.google: ICMP echo request, id 1, seq 347, length 40
14:20:44.311231 IP 192.168.2.51 > dns.google: ICMP echo request, id 1, seq 348, length 40
14:20:45.333757 IP 192.168.2.51 > dns.google: ICMP echo request, id 1, seq 349, length 40

pi@rasp:~ $ sudo tcpdump -i eth0.1 src 8.8.4.4
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0.1, link-type EN10MB (Ethernet), capture size 262144 bytes
14:20:42.487554 IP dns.google > 192.168.2.51: ICMP echo reply, id 1, seq 346, length 40
14:20:43.499652 IP dns.google > 192.168.2.51: ICMP echo reply, id 1, seq 347, length 40
14:20:44.518266 IP dns.google > 192.168.2.51: ICMP echo reply, id 1, seq 348, length 40
14:20:45.540728 IP dns.google > 192.168.2.51: ICMP echo reply, id 1, seq 349, length 40

pi@rasp:~ $ sudo tcpdump -i ppp0 dst 8.8.4.4
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ppp0, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
14:20:42.281130 IP 100.82.232.18 > dns.google: ICMP echo request, id 1, seq 346, length 40
14:20:43.292714 IP 100.82.232.18 > dns.google: ICMP echo request, id 1, seq 347, length 40
14:20:44.311320 IP 100.82.232.18 > dns.google: ICMP echo request, id 1, seq 348, length 40
14:20:45.333840 IP 100.82.232.18 > dns.google: ICMP echo request, id 1, seq 349, length 40

pi@rasp:~ $ sudo tcpdump -i ppp0 src 8.8.4.4
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ppp0, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
14:20:42.487470 IP dns.google > 100.82.232.18: ICMP echo reply, id 1, seq 346, length 40
14:20:43.499578 IP dns.google > 100.82.232.18: ICMP echo reply, id 1, seq 347, length 40
14:20:44.518194 IP dns.google > 100.82.232.18: ICMP echo reply, id 1, seq 348, length 40
14:20:45.540656 IP dns.google > 100.82.232.18: ICMP echo reply, id 1, seq 349, length 40

测试结果是:

  • 笔记本电脑ping通8.8.4.4
  • ppp0 上可以看到 dns.google(即8.8.4.4) 与 ppp0(即100.82.232.18)之间的往来数据包,ppp0 发出 ICMP echo request,然后 dns.google 回应 ICMP echo reply
  • eth0.1 上可以看到 dns.google(即8.8.4.4) 与笔记本电脑(即192.168.2.51)之间的往来数据包,笔记本电脑发出 ICMP echo request,然后 dns.google 回应 ICMP echo reply

数据交换流程如下所示,由于这个例子是ICMP协议,不涉及端口号,如果是TCP/UDP还需要考虑端口号的转换:

需要再次强调一下,本例只是选用了最简单的ICMP协议,而应用程序大部分情况下使用的是TCP和UDP协议,在这种情况下,NAT是同时涉及IP和Port的,此图展示了两次对NTP服务器的访问,其中就涉及到了端口号。

扩展玩法

  • 进一步在树莓派上安装 dnsmasq,提供DNS Server以及DNS缓存服务
  • 利用树莓派的wlan0开启无线热点,并通过Linux虚拟网桥桥接wlan0和eth0.1,将网关IP配置在虚拟网桥接口上,使wlan0所创建的WLAN也能通过树莓派的PPPoE接口经NAT访问互联网

参考资料