P4 02-Repeater
P4示例程序-02 中继器
功能:实现了简单的双端口交换机,使得两个主机能够进行通信,即类似于中继器的功能
拓扑结构
代码
p4app.json的拓扑结构1
2
3
4
5
6
7
8
9
10
11
12
13
14"topology": {
"assignment_strategy": "l2",
"links": [["h1", "s1"], ["h2", "s1"]],
"hosts": {
"h1": {
},
"h2": {
}
},
"switches": {
"s1": {
}
}
}- 原代码
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90/* -*- P4_16 -*- */
#include <core.p4>
#include <v1model.p4>
/*************************************************************************
*********************** H E A D E R S ***********************************
*************************************************************************/
struct metadata {
}
struct headers {
}
/*************************************************************************
*********************** P A R S E R ***********************************
*************************************************************************/
parser MyParser(packet_in packet,
out headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
state start{
transition accept;
}
}
/*************************************************************************
************ C H E C K S U M V E R I F I C A T I O N *************
*************************************************************************/
control MyVerifyChecksum(inout headers hdr, inout metadata meta) {
apply { }
}
/*************************************************************************
************** I N G R E S S P R O C E S S I N G *******************
*************************************************************************/
control MyIngress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
/* TODO 1: For solution 2 -> define a table that matches standard_metadata.ingress_port */
/* TODO 2: For solution 2 -> define an action that modifies the egress_port */
apply {
/* TODO 3:*/
/* Solution 1: Without tables, write the algorithm directly here*/
/* Solution 2: Apply the table you use */
}
}
/*************************************************************************
**************** E G R E S S P R O C E S S I N G *******************
*************************************************************************/
control MyEgress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
apply { }
}
/*************************************************************************
************* C H E C K S U M C O M P U T A T I O N **************
*************************************************************************/
control MyComputeChecksum(inout headers hdr, inout metadata meta) {
apply { }
}
/*************************************************************************
*********************** D E P A R S E R *******************************
*************************************************************************/
control MyDeparser(packet_out packet, in headers hdr) {
apply {
/* Deparser not needed */
}
}
/*************************************************************************
*********************** S W I T C H *******************************
*************************************************************************/
V1Switch(
MyParser(),
MyVerifyChecksum(),
MyIngress(),
MyEgress(),
MyComputeChecksum(),
MyDeparser()
) main; - Solution 1 静态配置方案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// solution 1
control MyIngress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
apply {
// 如果入口是 1 号端口 => 从 2 号端口发出
if (standard_metadata.ingress_port == 1){
standard_metadata.egress_spec = 2;
}
// 如果入口是 2 号端口 => 从 1 号端口发出
else if (standard_metadata.ingress_port == 2){
standard_metadata.egress_spec = 1;
}
}
}ingress_port:数据包的入端口,解析之前设置,只读egress_spec:在入端口流水线的匹配-动作过程之后设置,指定数据包出端口,可以是物理端口、逻辑端口或者多播组
- Solution 2 基于表的配置方案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24control MyIngress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
// 定义一个 action,确定出口端
action forward(bit<9> egress_port){ // 这里的参数是无方向的,因为是来查表得到的
standard_metadata.egress_spec = egress_port;
}
table repeater {
key = {
standard_metadata.ingress_port: exact; // 完全 match 才可以
}
actions = { // 两种动作
forward; // 一定是之前已经声明的 action
NoAction;
}
size = 2; // 只需要两个端口的规则,所以表里只需要两个 entries
default_action = NoAction;
}
apply {
repeater.apply();
}
- 实现该solution还要新建命令表
s1-commands.txt1
2table_add repeater forward 1 => 2
table_add repeater forward 2 => 1 - 语法
1
tabel_add <table_name> <action_name> <match_fields> => <action_parameters> - 另外还要修改
p4app.json1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16"topology": {
"assignment_strategy": "l2",
"links": [
["h1", "s1"],
["h2", "s1"]
],
"hosts": {
"h1": {},
"h2": {}
},
"switches": {
"s1": {
"cli_input": "s1-commands.txt"
}
}
}
- P4仿真
启动网络拓扑
1
sudo p4run
打开
h1,h2命令窗口1
2xterm h1
xterm h2
打开 h2 的终端,运行
python receive.py
打开一个 h1 的终端,运行
python send.py 10.0.0.2 "hello world"
h2端接收到消息并成功解码
问题
- h2接收端msg无法显示发送端内容
截图
原因 Scapy 会自动尝试根据上下文推断下一层协议类型。字符串
str类型的 payload,默认被包装为Raw类型。解决方案
Send.py1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24def main():
if len(sys.argv)<3:
print('pass 2 arguments: <destination> "<message>"')
exit(1)
addr = socket.gethostbyname(sys.argv[1])
iface = get_if()
if len(sys.argv) > 3:
tos = int(sys.argv[3]) % 256
else:
tos = 0
ether_dst = get_dst_mac(addr)
if not ether_dst:
print("Mac address for %s was not found in the ARP table" % addr)
exit(1)
print("Sending on interface %s to %s" % (iface, str(addr)))
pkt = Ether(src=get_if_hwaddr(iface), dst=ether_dst)
pkt = pkt / IP(dst=addr, tos=tos) / Raw(load=sys.argv[2])
sendp(pkt, iface=iface, verbose=False)Rceiver.py1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18def handle_pkt(pkt):
print("Packet Received:")
ether = pkt.getlayer(Ether)
ip = pkt.getlayer(IP)
msg = ip.payload
raw_data = pkt[Raw].load
decoded = raw_data.decode(errors='ignore')
print("###[ Ethernet ]###")
print(" src: {}".format(ether.src))
print(" dst: {}".format(ether.dst))
print("###[ IP ]###")
print(" src: {}".format(ip.src))
print(" dst: {}".format(ip.dst))
print("###[ MESSAGE ]###")
print(f" msg: {decoded}")
print()