0%

Linux虚拟网络设备-birdge

网络桥接

网桥是一种网络设备,它能够从多个通信网络创建一个单一的聚合网络,此功能被称为网络桥接。网络桥接不同于路由,路由允许多个独立网络互相通信,但各个网络仍然保持独立,而桥接是将多个独立网络连接成一个单一网络。

网桥工作于OSI模型的数据链路层,它基于以太网地址(而不是IP地址)转发数据包。交换机是一种常见的网络桥接设备,家用路由器内部一般也带有集成交换机。

Linux bridge

Linux bridge实现了802.1d标准的一个子集,Linux网桥比纯粹的硬件网桥更加强大,它不仅可以转发数据包,还可以进行过滤和流量调整等功能。

下面介绍一下Linux bridge的配置工具,然后利用树莓派上进行一个简单的桥接实验。

ip 和 bridge

iproute2程序包包含的 ipbridge 命令可用于管理网桥。

基本命令

ip命令

  • 创建bridge设备 br0

    1
    ip link add name br0 type bridge
  • 将interface eth0添加到bridge br0

    1
    ip link set eth0 master br0

    上面是以物理网卡 eth0 为例,但网桥中的设备不仅限于物理设备,tun/tap/veth/vlan/macvlan/macvtap/ipvlan/ipvtap 等各种虚拟设备均可添加到 bridge。

    eth0 原本一端连接着外界物理网络,另一端连接着网络协议栈。一旦它接入了 br0,由外部传入 eth0 的数据包将被直接传递到 br0,而不再是网络协议栈。这样一来,为 eth0 配置IP就失去了意义,因为 eth0 另一端连接的只是一个虚拟网桥 br0,这是一个2层设备,它工作于数据链路层,仅基于MAC地址进行数据包转发,形象地说,也就是 eth0 现在只是相当于 br0 的一个 Port,把 br0 比作以太交换机,那么 eth0 就是 br0 的一个网口。

    原本的网络结构:
    eth0 连接着网络协议栈与外界网络

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    +-------------------------------------------------+
    | |
    | +----------------------------------------+ |
    | | Network Protocol Stack | |
    | +----------------------------------------+ |
    | ↑ ↑ |
    |............|.....................|..............|
    | | | |
    | ↓ ↓ |
    | +--------------+ +--------+ |
    | | 192.168.2.10 | | | |
    | +--------------+ | br0 | |
    | | eth0 | | | |
    | +--------------+ +--------+ |
    | ↑ |
    | | |
    +------------|------------------------------------+

    Physical Network

    之后的网络结构:
    eth0 收到的外界数据包不再发给网络协议栈,而是虚拟2层设备 br0

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    +-------------------------------------------------+
    | |
    | +----------------------------------------+ |
    | | Network Protocol Stack | |
    | +----------------------------------------+ |
    | | ↑ |
    |............|.....................|..............|
    | | | |
    | ↓ ↓ |
    | +--------------+ +---------+ |
    | | 192.168.2.10 | | | |
    | +--------------+ | br0 | |
    | | eth0 |<------->| | |
    | +--------------+ +---------+ |
    | ↑ |
    | | |
    +------------|------------------------------------+

    Physical Network
  • 开启bridge br0

    1
    set link br0 up

bridge命令

  • bridge link:展示桥接到网桥的interface(相当于交换机的网口)
  • bridge fdb:展示网桥的转发表
  • bridge vlan:展示网桥的VLAN配置信息

由上面的分析,可以得知Linux虚拟网桥设备 bridge 可以将多个 interface 连接为一个网络,并且虚拟网桥设备有自己的MAC转发表,也可以在虚拟网桥中配置VLAN。

利用树莓派验证brdige的二层交换功能

实验设备

  • 树莓派3b+:包含一个以太网卡 eth0,一个无线网卡 wlan0
  • PC:有一个以太网口
  • 手机:可以连接WiFI

实验思路

  • 在树莓派上创建一个 bridge,称为 br0
  • PC通过网线接入 eth0,WiFi设备接入基于 wlan0 构建的无线局域网
  • eth0wlan0 添加到 br0,利用br0 将无线局域网和PC桥接在一起

这样一来,网络结构如下图所示(略去了Network Protocol Stack -> eth0 的单向连接):

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
32
33
            +-------------------------------------------------+
| +----------------------------------------+ |
| | Network Protocol Stack | |
| +----------------------------------------+ |
| ↑ |
|........................|........................|
| ↓ |
| +----------------------------------+ |
| | br0 | |
| +----------------------------------+ |
| ↑ ↑ |
| | | |
| ↓ ↓ |
| +--------------+ +--------------+ |
| | eth0 | | wlan0 |<---------> WiFi_Device
| +--------------+ +--------------+ |
| ↑ ↑ ↑ |
+------------|------------------|--------|--------+
↓ ↓ ↓
PC WiFi_Device WiFi_Device
```

根据其原理,只要合理地配置PC和无线设备的IP地址,它们理应可以借助虚拟网桥 `br0` 互相访问。

### 关键步骤

#### 树莓派配置AP

借助 `hostapd` 实现,大致步骤为:

1. 安装 `hostapd`
```shell
sudo apt install hostapd

  1. 配置 hostapd,创建 /etc/hostapd/hostapd.conf
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    interface=wlan0
    bridge=br0
    hw_mode=g
    channel=7
    wmm_enabled=0
    macaddr_acl=0
    auth_algs=1
    ignore_broadcast_ssid=0
    wpa=2
    wpa_key_mgmt=WPA-PSK
    wpa_pairwise=TKIP
    rsn_pairwise=CCMP
    ssid=RaspberryPi
    wpa_passphrase=password
  2. 关闭 dhcpcd,启用 hostapd
    1
    2
    3
    4
    sudo disable dhcpcd
    # References: /usr/share/doc/hostapd/README.Debian
    sudo systemctl unmask hostapd
    sudo systemctl enable hostapd
  3. 连接热点 RaspberryPi
    配置好之后重启树莓派应该就能看到热点 RaspberryPi 了,由于树莓派没有开启dhcpd,WiFI设备需要配置使用Static IP,比如 192.168.31.100/24

桥接

  1. 创建虚拟网桥 br0
    1
    sudo ip link add name br0 type bridge
  2. 添加设备 eth0wlan0br0
    1
    2
    sudo ip link set dev eth0 master br0
    sudo ip link set dev wlan0 master br0

连接网络

由于树莓派没有开启dhcpd,需要为各个连接到WiFI的设备配置静态IP,当然经网线连接的PC也需要配置,各个设备的IP需要处于同一网段才能互相访问,比如 192.168.31.0/24

故障排除

  1. PC和无线设备都配置好了却无法互相连接
    关掉Windows防火墙再试试,稍等一会儿再试试
    Networking: bridge - Linux Foundation DokuWiki中有这样一句话:

    The bridge will take a short amount of time when a device is added to learn the Ethernet addresses on the segment before starting to forward.

  2. 暂时没有2

测试结果

PC ping通了连接WiFI的手机,成功借助虚拟网桥连通了eth0wlan0无线局域网。

树莓派配置信息

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
Script started on 2021-07-30 20:07:21+08:00 [TERM="linux" TTY="/dev/tty1" COLUMNS="240" LINES="67"]


pi@rasp:~/projects/bridge $ ip -d addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0 minmtu 0 maxmtu 0 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master br0 state UP group default qlen 1000
link/ether b8:27:eb:98:76:96 brd ff:ff:ff:ff:ff:ff promiscuity 1 minmtu 68 maxmtu 9000
bridge_slave state forwarding priority 32 cost 4 hairpin off guard off root_block off fastleave off learning on flood on port_id 0x8002 port_no 0x2 designated_port 32770 designated_cost 0 designated_bridge 8000.b8:27:eb:98:76:96 designated_root 8000.b8:27:eb:98:76:96 hold_timer 0.00 message_age_timer 0.00 forward_delay_timer 0.00 topology_change_ack 0 config_pending 0 proxy_arp off proxy_arp_wifi off mcast_router 1 mcast_fast_leave off mcast_flood on neigh_suppress off group_fwd_mask 0 group_fwd_mask_str 0x0 vlan_tunnel off isolated off numtxqueues 1 numrxqueues 1 gso_max_size 8824 gso_max_segs 65535
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master br0 state UP group default qlen 1000
link/ether b8:27:eb:cd:23:c3 brd ff:ff:ff:ff:ff:ff promiscuity 1 minmtu 68 maxmtu 1500
bridge_slave state forwarding priority 32 cost 100 hairpin off guard off root_block off fastleave off learning on flood on port_id 0x8001 port_no 0x1 designated_port 32769 designated_cost 0 designated_bridge 8000.b8:27:eb:98:76:96 designated_root 8000.b8:27:eb:98:76:96 hold_timer 0.00 message_age_timer 0.00 forward_delay_timer 0.00 topology_change_ack 0 config_pending 0 proxy_arp off proxy_arp_wifi off mcast_router 1 mcast_fast_leave off mcast_flood on neigh_suppress off group_fwd_mask 0 group_fwd_mask_str 0x0 vlan_tunnel off isolated off numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
5: br0: <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 68 maxmtu 65535
bridge forward_delay 1500 hello_time 200 max_age 2000 ageing_time 30000 stp_state 0 priority 32768 vlan_filtering 0 bridge_id 8000.b8:27:eb:98:76:96 designated_root 8000.b8:27:eb:98:76:96 root_port 0 root_path_cost 0 topology_change 0 topology_change_detected 0 hello_timer 0.00 tcn_timer 0.00 topology_change_timer 0.00 gc_timer 220.78 group_fwd_mask 0 group_address 01:80:c2:00:00:00 mcast_snooping 1 mcast_router 1 mcast_query_use_ifaddr 0 mcast_querier 0 mcast_hash_elasticity 16 mcast_hash_max 4096 mcast_last_member_count 2 mcast_startup_query_count 2 mcast_last_member_interval 100 mcast_membership_interval 26000 mcast_querier_interval 25500 mcast_query_interval 12500 mcast_query_response_interval 1000 mcast_startup_query_interval 3125 mcast_stats_enabled 0 mcast_igmp_version 2 mcast_mld_version 1 nf_call_iptables 0 nf_call_ip6tables 0 nf_call_arptables 0 numtxqueues 1 numrxqueues 1 gso_max_size 8824 gso_max_segs 65535
inet6 fe80::ba27:ebff:fecd:23c3/64 scope link
valid_lft forever preferred_lft forever


pi@rasp:~/projects/bridge $ bridge -d link
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br0 state forwarding priority 32 cost 4
hairpin off guard off root_block off fastleave off learning on flood on mcast_flood on neigh_suppress off vlan_tunnel off isolated off
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br0 state forwarding priority 32 cost 100
hairpin off guard off root_block off fastleave off learning on flood on mcast_flood on neigh_suppress off vlan_tunnel off isolated off
5: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master br0


pi@rasp:~/projects/bridge $ bridge fdb
f4:30:b9:ad:56:eb dev eth0 master br0
b8:27:eb:98:76:96 dev eth0 master br0 permanent
01:00:5e:00:00:01 dev eth0 self permanent
33:33:00:00:00:01 dev eth0 self permanent
e0:cc:f8:ce:27:a9 dev wlan0 master br0
b8:27:eb:cd:23:c3 dev wlan0 master br0 permanent
01:00:5e:00:00:01 dev wlan0 self permanent
33:33:00:00:00:01 dev wlan0 self permanent
33:33:00:00:00:01 dev br0 self permanent
01:00:5e:00:00:6a dev br0 self permanent
33:33:00:00:00:6a dev br0 self permanent
01:00:5e:00:00:01 dev br0 self permanent
33:33:ff:cd:23:c3 dev br0 self permanent
33:33:00:00:00:fb dev br0 self permanent
pi@rasp:~/projects/bridge $ bridge -d fdb
f4:30:b9:ad:56:eb dev eth0 master br0
b8:27:eb:98:76:96 dev eth0 master br0 permanent
01:00:5e:00:00:01 dev eth0 self permanent
33:33:00:00:00:01 dev eth0 self permanent
e0:cc:f8:ce:27:a9 dev wlan0 master br0
b8:27:eb:cd:23:c3 dev wlan0 master br0 permanent
01:00:5e:00:00:01 dev wlan0 self permanent
33:33:00:00:00:01 dev wlan0 self permanent
33:33:00:00:00:01 dev br0 self permanent
01:00:5e:00:00:6a dev br0 self permanent
33:33:00:00:00:6a dev br0 self permanent
01:00:5e:00:00:01 dev br0 self permanent
33:33:ff:cd:23:c3 dev br0 self permanent
33:33:00:00:00:fb dev br0 self permanent


pi@rasp:~/projects/bridge $ bridge -d vlan
port vlan ids
eth0
wlan0
br0
pi@rasp:~/projects/bridge $ exit

Script done on 2021-07-30 20:08:17+08:00 [COMMAND_EXIT_CODE="0"]

设备IP配置

  • PC:192.168.100.100
  • 手机:192.168.100.102

tcpdump部分抓包结果

抓包命令:sudo tcpdump -i br0

可以看到两台设备之间的 ICMP echo request 和 ICMP echo reply,至于为什么没有互相询问ARP的消息,大概是因为在我截取消息的时候他们已经知道了对方的MAC。

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
19:56:53.340646 ARP, Request who-has 192.168.17.1 tell 192.168.17.100, length 28
19:56:53.577869 IP 192.168.17.100 > 192.168.17.102: ICMP echo request, id 2945, seq 279, length 64
19:56:53.578378 IP 192.168.17.102 > 192.168.17.100: ICMP echo reply, id 2945, seq 279, length 64
19:56:54.105795 ARP, Request who-has 192.168.17.1 tell 192.168.17.100, length 28
19:56:54.580512 IP 192.168.17.100 > 192.168.17.102: ICMP echo request, id 2945, seq 280, length 64
19:56:54.580882 IP 192.168.17.102 > 192.168.17.100: ICMP echo reply, id 2945, seq 280, length 64
19:56:54.875747 ARP, Request who-has 192.168.17.1 tell 192.168.17.100, length 28
19:56:55.582730 IP 192.168.17.100 > 192.168.17.102: ICMP echo request, id 2945, seq 281, length 64
19:56:55.583236 IP 192.168.17.102 > 192.168.17.100: ICMP echo reply, id 2945, seq 281, length 64
19:56:55.629983 ARP, Request who-has 192.168.17.1 tell 192.168.17.100, length 28
19:56:56.584221 IP 192.168.17.100 > 192.168.17.102: ICMP echo request, id 2945, seq 282, length 64
19:56:56.584775 IP 192.168.17.102 > 192.168.17.100: ICMP echo reply, id 2945, seq 282, length 64
19:56:57.079863 ARP, Request who-has 192.168.17.1 tell 192.168.17.100, length 28
19:56:57.585969 IP 192.168.17.100 > 192.168.17.102: ICMP echo request, id 2945, seq 283, length 64
19:56:57.586473 IP 192.168.17.102 > 192.168.17.100: ICMP echo reply, id 2945, seq 283, length 64
19:56:57.851101 ARP, Request who-has 192.168.17.1 tell 192.168.17.100, length 28
19:56:58.587740 IP 192.168.17.100 > 192.168.17.102: ICMP echo request, id 2945, seq 284, length 64
19:56:58.588376 IP 192.168.17.102 > 192.168.17.100: ICMP echo reply, id 2945, seq 284, length 64
19:56:58.606484 ARP, Request who-has 192.168.17.1 tell 192.168.17.100, length 28
19:56:59.589851 IP 192.168.17.100 > 192.168.17.102: ICMP echo request, id 2945, seq 285, length 64
19:56:59.590206 IP 192.168.17.102 > 192.168.17.100: ICMP echo reply, id 2945, seq 285, length 64
19:56:59.639003 IP 192.168.17.102.62171 > 239.255.255.250.1900: UDP, length 174
19:57:00.591333 IP 192.168.17.100 > 192.168.17.102: ICMP echo request, id 2945, seq 286, length 64
19:57:00.592043 IP 192.168.17.102 > 192.168.17.100: ICMP echo reply, id 2945, seq 286, length 64
19:57:00.642907 IP 192.168.17.102.62171 > 239.255.255.250.1900: UDP, length 174
19:57:01.593566 IP 192.168.17.100 > 192.168.17.102: ICMP echo request, id 2945, seq 287, length 64
19:57:01.593930 IP 192.168.17.102 > 192.168.17.100: ICMP echo reply, id 2945, seq 287, length 64
19:57:01.645152 IP 192.168.17.102.62171 > 239.255.255.250.1900: UDP, length 174
19:57:01.729974 ARP, Request who-has 192.168.17.1 tell 192.168.17.100, length 28
19:57:02.491623 ARP, Request who-has 192.168.17.1 tell 192.168.17.100, length 28
19:57:02.582445 IP 192.168.17.100 > 192.168.17.102: ICMP echo request, id 2945, seq 288, length 64
19:57:02.582814 IP 192.168.17.102 > 192.168.17.100: ICMP echo reply, id 2945, seq 288, length 64
19:57:02.646148 IP 192.168.17.102.62171 > 239.255.255.250.1900: UDP, length 174
19:57:03.258025 ARP, Request who-has 192.168.17.1 tell 192.168.17.100, length 28
19:57:03.595961 IP 192.168.17.100 > 192.168.17.102: ICMP echo request, id 2945, seq 289, length 64
19:57:03.596639 IP 192.168.17.102 > 192.168.17.100: ICMP echo reply, id 2945, seq 289, length 64
19:57:04.121192 ARP, Request who-has 192.168.17.1 tell 192.168.17.100, length 28
19:57:04.599132 IP 192.168.17.100 > 192.168.17.102: ICMP echo request, id 2945, seq 290, length 64
19:57:04.599516 IP 192.168.17.102 > 192.168.17.100: ICMP echo reply, id 2945, seq 290, length 64
19:57:04.881646 ARP, Request who-has 192.168.17.1 tell 192.168.17.100, length 28
19:57:05.592449 IP 192.168.17.100 > 192.168.17.102: ICMP echo request, id 2945, seq 291, length 64
19:57:05.593202 IP 192.168.17.102 > 192.168.17.100: ICMP echo reply, id 2945, seq 291, length 64
19:57:05.630129 ARP, Request who-has 192.168.17.1 tell 192.168.17.100, length 28
19:57:06.601837 IP 192.168.17.100 > 192.168.17.102: ICMP echo request, id 2945, seq 292, length 64
19:57:06.602326 IP 192.168.17.102 > 192.168.17.100: ICMP echo reply, id 2945, seq 292, length 64
19:57:06.735715 ARP, Request who-has 192.168.17.1 tell 192.168.17.100, length 28

参考资料