写在前面
只是作为一个入门p4的实验尝试,借用了一些即成的运行代码。
p4代码
/**p4_16,v1_model**/
1 #include<core.p4>
2 #include<v1model.p4>
3
4 const bit<16> TYPE_IPV6 = 0x08DD;//ipv6在以太网中的id
5
6 /*HEADERS*/
7
8 typedef bit<9> egressSpec_t;
9 typedef bit<48> macAddr_t;
10
11 header ethernet_t{
12 macAddr_t dstAddr;
13 macAddr_t srcAddr;
14 bit<16> etherType;
15 }
16
17 header ipv6_t{
18 bit<4> version;
19 bit<8> trafficClass;
20 bit<20> flowLabel;
21 bit<16> payLoadLen;
22 bit<8> nextHdr;
23 bit<8> hopLimit;
24 bit<128> srcAddr;
25 bit<128> dstAddr;
26 }
27
28 struct metadata{
29 }
30
31 struct headers{
32 ethernet_t ethernet;
33 ipv6_t ipv6;
34 }
35
36 /*PARSER*/
37
38 parser MyParser(packet_in packet,out headers hdr,inout metadata meta,inout standard_metadata_t standard_metadata){
39 state start{
40 transition parse_ethernet;//start开始先以底层eth解析
41 }
42
43 state parse_ethernet{
44 packet.extract(hdr.ethernet);
45 transition select(hdr.ethernet.etherType){
46 TYPE_IPV6:parse_ipv6;//转至ipv6解析
47 default:accept;
48 }
49 }
50
51 state parse_ipv6{
52 packet.extract(hdr.ipv6);
53 transition accept;
54 }
55 }
56
57 /*CHECKSUM VERIFICATION*/
58
59 control MyVerifyChecksum(inout headers hdr,inout metadata meta){
60 apply{}
61 }
62
63 /*INGRESS PROCESSING*/
64
65 control MyIngress(inout headers hdr,inout metadata meta,inout standard_metadata_t standard_metadata){
66 action drop(){
67 mark_to_drop();//将要丢弃的包标记为丢弃
68 }
69
70 action ipv6_forward(macAddr_t dstAddr,egressSpec_t port){
71 standard_metadata.egress_spec = port;
72 hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
73 hdr.ethernet.dstAddr = dstAddr;
74 hdr.ipv6.hopLimit = hdr.ipv6.hopLimit - 1;//这个类似ipv4中ttl,为0时就超时
75 }
76
77 table ipv6_lpm{
78 key = {
79 hdr.ipv6.dstAddr: lpm;//lpm是最长前缀匹配,exact完全匹配,ternary三元匹配
80 }
81
82 actions = {
83 ipv6_forward;//转发
84 drop;//丢弃
85 NoAction;//空动作
86 }
87
88 size = 1024;//流表项容量
89
90 default_action = drop();//table miss则丢弃
91 }
92
93 apply{
94 if(hdr.ipv6.isValid()){
95 ipv6_lpm.apply();
96 }
97 }
98 }
99
100 /*EGRESS PROCESSING*/
101
102 control MyEgress(inout headers hdr,inout metadata meta,inout standard_metadata_t standard_metadata){
103 apply{}
104 }
105
106 /*CHECKSUM COMPUTATION*/
107
108 control MyComputeChecksum(inout headers hdr,inout metadata meta){
109 apply{}
110 }
111
112 /*DEPARSER*/
113
114 control MyDeparser(packet_out packet,in headers hdr){
115 apply{
116 packet.emit(hdr.ethernet);
117 packet.emit(hdr.ipv6);
118 }
119 }
120
121 /*SWITCH*/
122
123 V1Switch(
124 MyParser(),
125 MyVerifyChecksum(),
126 MyIngress(),
127 MyEgress(),
128 MyComputeChecksum(),
129 MyDeparser()
130 )main;
实验拓扑
这里实验的拓扑用来最简单的一个交换机下挂两个主机,交换机是bmv2。其json文件书写如下: 交换机s1的流表项通过s1-commands.txt文件定义。
1 {
2 "program": "ipv6_forward.p4",
3 "language": "p4-16",
4 "targets": {
5 "multiswitch": {
6 "auto-control-plane": true,
7 "cli": true,
8 "pcap_dump": true,
9 "bmv2_log": true,
10 "links": [["h1", "s1"], ["h2", "s1"]],
11 "hosts": {
12 "h1": {
13 },
14 "h2": {
15 }
16 },
17 "switches": {
18 "s1": {
19 "entries": "s1-commands.txt"
20 }
21 }
22 }
23 }
24 }
在s1-commans.txt文件中做如下定义:
1 table_set_default ipv6_lpm drop
2 table_add ipv6_lpm ipv6_forward fe80::5678/128 => 00:04:00:00:00:02 2
3 table_add ipv6_lpm ipv6_forward fe80::1234/128 => 00:04:00:00:00:01 1
将其流表项画出来如下:
收发包脚本代码
鉴于对python socket发包代码不是那么了解,所以套用了一部分模板做了修改。
send.py
1 import argparse
2 import sys
3 import socket
4 import random
5 import struct
6
7 from scapy.all import sendp, send, get_if_list, get_if_hwaddr
8 from scapy.all import Packet
9 from scapy.all import Ether, IPv6, UDP
10
11 def get_if():
12 ifs=get_if_list()
13 iface=None
14 for i in get_if_list():
15 if "eth0" in i:
16 iface=i
17 break;
18 if not iface:
19 print "Cannot find eth0 interface"
20 exit(1)
21 return iface
22
23 def main():
24
25 if len(sys.argv)<3:
26 print 'pass 3 arguments:<source> <destination> "<message>"'
27 exit(1)
28 saddr = sys.argv[1]
29 addr = sys.argv[2]
30 iface = get_if()
31
32 print "sending on interface %s to %s" % (iface, str(addr))
33 pkt = Ether(src=get_if_hwaddr(iface), dst='ff:ff:ff:ff:ff:ff') / IPv6(src=saddr,dst=addr) / UDP(dport=4321, sport=1234) / sys.argv[3]
34 pkt.show2()
35 sendp(pkt, iface=iface, verbose=False)
36
37
38 if __name__ == '__main__':
39 main()
receive.py
1 import sys
2 import struct
3 import os
4
5 from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr
6 from scapy.all import Packet, IPOption
7 from scapy.all import ShortField, IntField, LongField, BitField, FieldListField, FieldLenField
8 from scapy.all import IPv6, TCP, UDP, Raw
9 from scapy.layers.inet import _IPOption_HDR
10
11 def get_if():
12 ifs=get_if_list()
13 iface=None
14 for i in get_if_list():
15 if "eth0" in i:
16 iface=i
17 break;
18 if not iface:
19 print "Cannot find eth0 interface"
20 exit(1)
21 return iface
22
23 class IPOption_MRI(IPOption):
24 name = "MRI"
25 option = 31
26 fields_desc = [ _IPOption_HDR,
27 FieldLenField("length", None, fmt="B",
28 length_of="swids",
29 adjust=lambda pkt,l:l+4),
30 ShortField("count", 0),
31 FieldListField("swids",
32 [],
33 IntField("", 0),
34 length_from=lambda pkt:pkt.count*4) ]
35 def handle_pkt(pkt):
36 print "got a packet"
37 pkt.show2()
38 #hexdump(pkt)
39 sys.stdout.flush()
40
41
42 def main():
43 ifaces = filter(lambda i: 'eth' in i, os.listdir('/sys/class/net/'))
44 iface = ifaces[0]
45 print "sniffing on %s" % iface
46 sys.stdout.flush()
47 sniff(filter="udp and port 4321",iface = iface,
48 prn = lambda x: handle_pkt(x))
49
50 if __name__ == '__main__':
51 main()
测试
1 ./run.sh
2 //在mininet cli中
3 xterm h1 h2
4 //在h2中
5 ./receive.py
6 //在h1中
7 ./send.py fe80::1234 fe80::5678 "Hello p4!"
结果如下:
牢骚
第一次尝试构建p4相关的实验,了解了一部分p4的语法,以及一些的工作原理,也发掘出一部分并未了解的知识内容待后续去琢磨学习。过程有很多不完美,结果也有很多不完美,虽然现在也不满足,但是寒假也快结束了。
来源:oschina
链接:https://my.oschina.net/u/4407852/blog/4496937