P4 simple switch安装&测试
P4 tutorials的实验都是通过mininet模拟网络环境进行测试。
你可能觉得mininet的环境不够真实,能不能直接用一台Linux虚拟机当作Switch呢?
答案当然是可以的!
今天这篇文章主要介绍如何搭建一台Linux虚拟机当作Switch使用。
参考官方https://github.com/p4lang
准备工作
准备一台Ubuntu20.4的虚拟机,作为Switch,完成IPv4 L3转发。
另外你也可以在准备两台虚拟机作为Host来做ping测试。
在VirtualBox或者VMware环境搭建网络如下:
不过这样准备3台虚拟机有点麻烦,我们只需要ping测试一下联通性,所以这里我简化了一下实验环境。
通过namespace来模拟:
新建两个namespace,ns1和ns2用来模拟Host1和Host2,
新建两对veth,分别是一头接Switch,一头接ns。
如下图,我们这次实验的拓扑
关于namespace,veth的介绍:namespace,veth介绍
https://blog.csdn.net/qq_33681684/article/details/120771949
安装 P4C
Ubuntu 20.04安装,直接用以下命令就可以
. /etc/os-release
echo "deb http://download.opensuse.org/repositories/home:/p4lang/xUbuntu_${VERSION_ID}/ /" | sudo tee /etc/apt/sources.list.d/home:p4lang.list
curl -L "http://download.opensuse.org/repositories/home:/p4lang/xUbuntu_${VERSION_ID}/Release.key" | sudo apt-key add -
sudo apt-get update
sudo apt install p4lang-p4c
安装 behavioral-model
Ubuntu 20.04安装,直接用以下命令就可以
sudo apt-get install -y automake cmake libgmp-dev \
libpcap-dev libboost-dev libboost-test-dev libboost-program-options-dev \
libboost-system-dev libboost-filesystem-dev libboost-thread-dev \
libevent-dev libtool flex bison pkg-config g++ libssl-dev
安装完后还需要安装thrift 0.11.0 和nanomsg 1.0.0
git clone https://github.com/p4lang/behavioral-model.git
下载下来代码后在ci目录下有install-thrift.sh和install-nanomsg.sh两个安装脚本可以直接运行安装。
Building the code
./autogen.sh
./configure
sudo make
sudo make install
sudo ldconfig
install bmv2后test一下
make check
这里截取一小部分,test会显示检查状态。
完成p4c和behavioral-model后,就可以搭建网络拓扑和配置Switch测试了。
搭建拓扑
按照上图,开始搭建实验拓扑
- 新建veth
sudo ip link add veth1-1 type veth peer name veth1-2
sudo ip link add veth2-1 type veth peer name veth2-2
- 新建namesp,ns1、ns2
sudo ip netns add ns1
sudo ip netns add ns2
- veth1-2放进ns1,veth2-2放进ns2
sudo ip link set veth1-2 netns ns1
sudo ip link set veth2-2 netns ns2
- 配置IP
sudo ip addr add 192.168.101.254/24 dev veth1-1
sudo ip addr add 192.168.102.254/24 dev veth2-1
sudo ip netns exec ns1 ip addr add 192.168.101.1/24 dev veth1-2
sudo ip netns exec ns2 ip addr add 192.168.102.1/24 dev veth2-2
打开接口
sudo ip link set up veth1-1
sudo ip link set up veth2-1
sudo ip netns exec ns1 ip link set up veth1-2
sudo ip netns exec ns2 ip link set up veth2-2
配置路由
sudo ip netns exec ns1 ip route add 0.0.0.0/0 via 192.168.101.254
sudo ip netns exec ns2 ip route add 0.0.0.0/0 via 192.168.102.254
配置完后,检查一下ip_forward功能没打开,ping是不通的
编译P4
输入以下命令,会生成一个basic.json的文件
p4c --target bmv2 --arch v1model --std p4-16 basic.p4
basic.p4文件basic.p4 for L3 forward https://download.csdn.net/download/qq_33681684/85101767
运行 simple_switch
运行Switch,绑定端口,basic.json送入simple_switch生成二进制。
simple_switch -i 1@veth1-1 -i 2@veth2-1 --thrift-port 9090 --nanolog ipc:///tmp/bm-1-log.ipc basic.json --log-console
如下截图Switch已经启动
手动配置Switch规则
我们再开一个终端窗口,进入Switch配置规则。
先查看一下veth1-2和veth2-2的mac地址。
然后进入Switch配置
simple_switch_CLI --thrift-port 9090
table_add ipv4_lpm ipv4_forward 192.168.101.1/32 => b2:8e:42:c6:c4:a9 1
table_add ipv4_lpm ipv4_forward 192.168.102.1/32 => 96:b9:54:d1:40:27 2
测试
ns1 ping ns2
在Switch的终端窗口可以看见数据包转发的log
[17:48:57.608] [bmv2] [D] [thread 43275] [19.0] [cxt 0] Processing packet received on port 1 #port1收到数据包
[17:48:57.608] [bmv2] [D] [thread 43275] [19.0] [cxt 0] Parser ‘parser’: start #开始解析
[17:48:57.608] [bmv2] [D] [thread 43275] [19.0] [cxt 0] Parser ‘parser’ entering state ‘start’
[17:48:57.608] [bmv2] [D] [thread 43275] [19.0] [cxt 0] Extracting header ‘ethernet’ #提取ethernet 报头
[17:48:57.609] [bmv2] [D] [thread 43275] [19.0] [cxt 0] Parser state ‘start’: key is 0800 #key 0800 表示ipv4类型
[17:48:57.609] [bmv2] [T] [thread 43275] [19.0] [cxt 0] Bytes parsed: 14
[17:48:57.609] [bmv2] [D] [thread 43275] [19.0] [cxt 0] Parser ‘parser’ entering state ‘parse_ipv4’ #下一个状态机解析ipv4
[17:48:57.609] [bmv2] [D] [thread 43275] [19.0] [cxt 0] Extracting header ‘ipv4’ #提取ipv4
[17:48:57.609] [bmv2] [D] [thread 43275] [19.0] [cxt 0] Parser state ‘parse_ipv4’ has no switch, going to default next state
[17:48:57.609] [bmv2] [T] [thread 43275] [19.0] [cxt 0] Bytes parsed: 34
[17:48:57.609] [bmv2] [D] [thread 43275] [19.0] [cxt 0] Parser ‘parser’: end #解析结束
[17:48:57.609] [bmv2] [D] [thread 43275] [19.0] [cxt 0] Pipeline ‘ingress’: start #进入pipeline
[17:48:57.609] [bmv2] [T] [thread 43275] [19.0] [cxt 0] basic.p4(114) Condition “hdr.ipv4.isValid()” (node_2) is true #判断报文有效
[17:48:57.609] [bmv2] [T] [thread 43275] [19.0] [cxt 0] Applying table ‘MyIngress.ipv4_lpm’ #使用最长匹配
[17:48:57.609] [bmv2] [D] [thread 43275] [19.0] [cxt 0] Looking up key:
- hdr.ipv4.dstAddr : c0a86601 #匹配目的IP192.168.102.1/32
[17:48:57.609] [bmv2] [D] [thread 43275] [19.0] [cxt 0] Table ‘MyIngress.ipv4_lpm’: hit with handle 1 #命中规则
[17:48:57.609] [bmv2] [D] [thread 43275] [19.0] [cxt 0] Dumping entry 1
Match key:
- hdr.ipv4.dstAddr : LPM c0a86601/32
Action entry: MyIngress.ipv4_forward - 96b954d14027,2, #action 封装目的MAC,从port2送出
[17:48:57.609] [bmv2] [D] [thread 43275] [19.0] [cxt 0] Action entry is MyIngress.ipv4_forward - 96b954d14027,2,
[17:48:57.609] [bmv2] [T] [thread 43275] [19.0] [cxt 0] Action MyIngress.ipv4_forward
[17:48:57.609] [bmv2] [T] [thread 43275] [19.0] [cxt 0] basic.p4(94) Primitive standard_metadata.egress_spec = port
[17:48:57.609] [bmv2] [T] [thread 43275] [19.0] [cxt 0] basic.p4(95) Primitive hdr.ethernet.srcAddr = hdr.ethernet.dstAddr
[17:48:57.609] [bmv2] [T] [thread 43275] [19.0] [cxt 0] basic.p4(96) Primitive hdr.ethernet.dstAddr = dstAddr
[17:48:57.609] [bmv2] [T] [thread 43275] [19.0] [cxt 0] basic.p4(97) Primitive hdr.ipv4.ttl = hdr.ipv4.ttl - 1
[17:48:57.609] [bmv2] [D] [thread 43275] [19.0] [cxt 0] Pipeline ‘ingress’: end
[17:48:57.609] [bmv2] [D] [thread 43275] [19.0] [cxt 0] Egress port is 2
[17:48:57.609] [bmv2] [D] [thread 43278] [19.0] [cxt 0] Pipeline ‘egress’: start
[17:48:57.609] [bmv2] [D] [thread 43278] [19.0] [cxt 0] Pipeline ‘egress’: end
[17:48:57.609] [bmv2] [D] [thread 43278] [19.0] [cxt 0] Deparser ‘deparser’: start
[17:48:57.609] [bmv2] [D] [thread 43278] [19.0] [cxt 0] Updating checksum ‘cksum’
[17:48:57.609] [bmv2] [D] [thread 43278] [19.0] [cxt 0] Deparsing header ‘ethernet’
[17:48:57.609] [bmv2] [D] [thread 43278] [19.0] [cxt 0] Deparsing header ‘ipv4’
[17:48:57.609] [bmv2] [D] [thread 43278] [19.0] [cxt 0] Deparser ‘deparser’: end
[17:48:57.609] [bmv2] [D] [thread 43280] [19.0] [cxt 0] Transmitting packet of size 98 out of port 2 #报文送出
[17:48:57.609] [bmv2] [D] [thread 43275] [20.0] [cxt 0] Processing packet received on port 2 #ns2到ns1回包
[17:48:57.609] [bmv2] [D] [thread 43275] [20.0] [cxt 0] Parser ‘parser’: start
[17:48:57.609] [bmv2] [D] [thread 43275] [20.0] [cxt 0] Parser ‘parser’ entering state ‘start’
[17:48:57.609] [bmv2] [D] [thread 43275] [20.0] [cxt 0] Extracting header ‘ethernet’
[17:48:57.609] [bmv2] [D] [thread 43275] [20.0] [cxt 0] Parser state ‘start’: key is 0800
[17:48:57.609] [bmv2] [T] [thread 43275] [20.0] [cxt 0] Bytes parsed: 14
[17:48:57.609] [bmv2] [D] [thread 43275] [20.0] [cxt 0] Parser ‘parser’ entering state ‘parse_ipv4’
[17:48:57.609] [bmv2] [D] [thread 43275] [20.0] [cxt 0] Extracting header ‘ipv4’
[17:48:57.609] [bmv2] [D] [thread 43275] [20.0] [cxt 0] Parser state ‘parse_ipv4’ has no switch, going to default next state
[17:48:57.609] [bmv2] [T] [thread 43275] [20.0] [cxt 0] Bytes parsed: 34
[17:48:57.609] [bmv2] [D] [thread 43275] [20.0] [cxt 0] Parser ‘parser’: end
[17:48:57.609] [bmv2] [D] [thread 43275] [20.0] [cxt 0] Pipeline ‘ingress’: start
[17:48:57.609] [bmv2] [T] [thread 43275] [20.0] [cxt 0] basic.p4(114) Condition “hdr.ipv4.isValid()” (node_2) is true
[17:48:57.609] [bmv2] [T] [thread 43275] [20.0] [cxt 0] Applying table ‘MyIngress.ipv4_lpm’
[17:48:57.609] [bmv2] [D] [thread 43275] [20.0] [cxt 0] Looking up key:
- hdr.ipv4.dstAddr : c0a86501
[17:48:57.609] [bmv2] [D] [thread 43275] [20.0] [cxt 0] Table ‘MyIngress.ipv4_lpm’: hit with handle 0
[17:48:57.609] [bmv2] [D] [thread 43275] [20.0] [cxt 0] Dumping entry 0
Match key:
- hdr.ipv4.dstAddr : LPM c0a86501/32
Action entry: MyIngress.ipv4_forward - b28e42c6c4a9,1,
[17:48:57.609] [bmv2] [D] [thread 43275] [20.0] [cxt 0] Action entry is MyIngress.ipv4_forward - b28e42c6c4a9,1,
[17:48:57.609] [bmv2] [T] [thread 43275] [20.0] [cxt 0] Action MyIngress.ipv4_forward
[17:48:57.609] [bmv2] [T] [thread 43275] [20.0] [cxt 0] basic.p4(94) Primitive standard_metadata.egress_spec = port
[17:48:57.609] [bmv2] [T] [thread 43275] [20.0] [cxt 0] basic.p4(95) Primitive hdr.ethernet.srcAddr = hdr.ethernet.dstAddr
[17:48:57.609] [bmv2] [T] [thread 43275] [20.0] [cxt 0] basic.p4(96) Primitive hdr.ethernet.dstAddr = dstAddr
[17:48:57.609] [bmv2] [T] [thread 43275] [20.0] [cxt 0] basic.p4(97) Primitive hdr.ipv4.ttl = hdr.ipv4.ttl - 1
[17:48:57.609] [bmv2] [D] [thread 43275] [20.0] [cxt 0] Pipeline ‘ingress’: end
[17:48:57.609] [bmv2] [D] [thread 43275] [20.0] [cxt 0] Egress port is 1
[17:48:57.609] [bmv2] [D] [thread 43277] [20.0] [cxt 0] Pipeline ‘egress’: start
[17:48:57.609] [bmv2] [D] [thread 43277] [20.0] [cxt 0] Pipeline ‘egress’: end
[17:48:57.609] [bmv2] [D] [thread 43277] [20.0] [cxt 0] Deparser ‘deparser’: start
[17:48:57.609] [bmv2] [D] [thread 43277] [20.0] [cxt 0] Updating checksum ‘cksum’
[17:48:57.609] [bmv2] [D] [thread 43277] [20.0] [cxt 0] Deparsing header ‘ethernet’
[17:48:57.609] [bmv2] [D] [thread 43277] [20.0] [cxt 0] Deparsing header ‘ipv4’
[17:48:57.609] [bmv2] [D] [thread 43277] [20.0] [cxt 0] Deparser ‘deparser’: end
[17:48:57.609] [bmv2] [D] [thread 43280] [20.0] [cxt 0] Transmitting packet of size 98 out of port 1