利用p4实现ipv6转发实验

写在前面

只是作为一个入门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的语法,以及一些的工作原理,也发掘出一部分并未了解的知识内容待后续去琢磨学习。过程有很多不完美,结果也有很多不完美,虽然现在也不满足,但是寒假也快结束了。

原文地址:https://www.cnblogs.com/pullself/p/10418743.html