Contents

P4-P4 Learning

github nsg-ethz/p4-learning

examples

simple switch examples

基本示例

数据包反射

交换机将所有接收的数据包发回给发送者

network.py

 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

"""
+----+       +----+
| h1 +-------+ s1 +
+----+       +----+
"""

from p4utils.mininetlib.network_API import NetworkAPI

net = NetworkAPI()

# 网络配置选项
net.setLogLevel('info') # 设置日志等级
net.enableCli() # 启动mininet CLI

# 添加网络节点和链路
net.addP4Switch('s1') # 添加s1 p4交换机
net.setP4Source('s1','reflector.p4') # 给s1 添加p4文件
net.addHost('h1') # 添加主机h1
net.addLink('s1', 'h1') # 添加s1到h1的链路

# 设置分配策略
net.l2() # 2层分配策略

# 网络节点配置选项
net.enablePcapDumpAll() # 允许所有节点记录PcapDump
net.enableLogAll() # 允许所有节点记录日志

# 启动网络
net.startNetwork()

reflector.p4

  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
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/* -*- P4_16 -*- */
#include <core.p4>
#include <v1model.p4>

/*************************************************************************
*********************** H E A D E R S  ***********************************
*************************************************************************/

//mac地址类型
typedef bit<48> macAddr_t;

//以太头
header ethernet_t {
    macAddr_t dstAddr;
    macAddr_t srcAddr;
    bit<16>   etherType;
}

//元数据
struct metadata {
    /* empty */
}

//数据包头
struct headers {
    ethernet_t   ethernet;
}

/*************************************************************************
*********************** 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{
          //读取以太头
  	      packet.extract(hdr.ethernet);
          //转换状态为接受
          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) {

    //定义swap_mac动作,用于交换以太头的源mac和目的mac
    action swap_mac(){
        macAddr_t tmp;
        tmp = hdr.ethernet.srcAddr;
        hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
        hdr.ethernet.dstAddr = tmp;
    }

    //管道流程
    apply {
       //交换源mac和目的mac
       swap_mac();

       //将出口设置为和入口所在端口
       standard_metadata.egress_spec = standard_metadata.ingress_port;
    }
}

/*************************************************************************
****************  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 {
		// 将修改后的以太头添加给数据包载荷
		packet.emit(hdr.ethernet);
	}
}

/*************************************************************************
***********************  S W I T C H  *******************************
*************************************************************************/

//交换机流程
V1Switch(
	MyParser(),
	MyVerifyChecksum(),
	MyIngress(),
	MyEgress(),
	MyComputeChecksum(),
	MyDeparser()
) main;

send_receive.py

 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
91
#!/usr/bin/env python3
import sys
import socket
import random
import time
from threading import Thread, Event
from scapy.all import *

# 嗅探类,继承线程
class Sniffer(Thread):
    def __init__(self, interface="eth0"):
        super(Sniffer, self).__init__()

        self.interface = interface
        self.my_mac = get_if_hwaddr(interface)
        self.daemon = True

        self.socket = None
        self.stop_sniffer = Event()

    def isNotOutgoing(self, pkt):
        return pkt[Ether].src != self.my_mac

    def run(self):
        self.socket = conf.L2listen(
            type=ETH_P_ALL,
            iface=self.interface,
            filter="ip"
        )
        sniff(opened_socket=self.socket, prn=self.print_packet, lfilter=self.isNotOutgoing,
              stop_filter=self.should_stop_sniffer)

    def join(self, timeout=None):
        self.stop_sniffer.set()
        super(Sniffer, self).join(timeout)

    def should_stop_sniffer(self, packet):
        return self.stop_sniffer.isSet()

    def print_packet(self, packet):
        print("[!] A packet was reflected from the switch: ")
        # packet.show()
        ether_layer = packet.getlayer(Ether)
        print(("[!] Info: {src} -> {dst}\n".format(src=ether_layer.src, dst=ether_layer.dst)))


def get_if():
    ifs = get_if_list()
    iface = None  # "h1-eth0"
    for i in get_if_list():
        if "eth0" in i:
            iface = i
            break
    if not iface:
        print("Cannot find eth0 interface")
        exit(1)
    return iface


def send_packet(iface, addr):
    input("Press the return key to send a packet:")
    print("Sending on interface %s to %s\n" % (iface, str(addr)))
    pkt = Ether(src=get_if_hwaddr(iface), dst='00:01:02:03:04:05')
    pkt = pkt / IP(dst=addr)
    sendp(pkt, iface=iface, verbose=False)


def main():
    addr = "10.0.0.2"
    addr = socket.gethostbyname(addr)
    iface = get_if()

    listener = Sniffer(iface)
    listener.start()
    time.sleep(0.1)

    try:
        while True:
            send_packet(iface, addr)
            time.sleep(0.5)

    except KeyboardInterrupt:
        print("[*] Stop sniffing")
        listener.join(2.0)

        if listener.isAlive():
            listener.socket.close()


if __name__ == '__main__':
    main()
 |