写在前面
只是作为一个入门p4的实验尝试,借用了一些即成的运行代码。
p4代码
/**p4_16,v1_model**/
1 #include2 #include 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 drop2 table_add ipv6_lpm ipv6_forward fe80::5678/128 => 00:04:00:00:00:02 23 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, UDP10 11 def get_if():12 ifs=get_if_list()13 iface=None14 for i in get_if_list():15 if "eth0" in i:16 iface=i17 break;18 if not iface:19 print "Cannot find eth0 interface"20 exit(1)21 return iface22 23 def main():24 25 if len(sys.argv)<3:26 print 'pass 3 arguments:
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_HDR10 11 def get_if():12 ifs=get_if_list()13 iface=None14 for i in get_if_list():15 if "eth0" in i:16 iface=i17 break;18 if not iface:19 print "Cannot find eth0 interface"20 exit(1)21 return iface22 23 class IPOption_MRI(IPOption):24 name = "MRI"25 option = 3126 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" % iface46 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.sh2 //在mininet cli中3 xterm h1 h24 //在h2中5 ./receive.py6 //在h1中7 ./send.py fe80::1234 fe80::5678 "Hello p4!"
结果如下:
牢骚
第一次尝试构建p4相关的实验,了解了一部分p4的语法,以及一些的工作原理,也发掘出一部分并未了解的知识内容待后续去琢磨学习。过程有很多不完美,结果也有很多不完美,虽然现在也不满足,但是寒假也快结束了。