写在前面
只是作为一个入门p4的实验尝试,借用了一些即成的运行代码。
p4代码
/p4_16,v1_model/
#include<core.p4>
#include<v1model.p4>
const bit<16> TYPE_IPV6 = 0x08DD;//ipv6在以太网中的id
/*HEADERS*/
typedef bit<9> egressSpec_t;
typedef bit<48> macAddr_t;
header ethernet_t{
macAddr_t dstAddr;
macAddr_t srcAddr;
bit<16> etherType;
}
header ipv6_t{
bit<4> version;
bit<8> trafficClass;
bit<20> flowLabel;
bit<16> payLoadLen;
bit<8> nextHdr;
bit<8> hopLimit;
bit<128> srcAddr;
bit<128> dstAddr;
}
struct metadata{
}
struct headers{
ethernet_t ethernet;
ipv6_t ipv6;
}
/*PARSER*/
parser MyParser(packet_in packet,out headers hdr,inout metadata meta,inout standard_metadata_t standard_metadata){
state start{
transition parse_ethernet;//start开始先以底层eth解析
}
state parse_ethernet{
packet.extract(hdr.ethernet);
transition select(hdr.ethernet.etherType){
TYPE_IPV6:parse_ipv6;//转至ipv6解析
default:accept;
}
}
state parse_ipv6{
packet.extract(hdr.ipv6);
transition accept;
}
}
/*CHECKSUM VERIFICATION*/
control MyVerifyChecksum(inout headers hdr,inout metadata meta){
apply{}
}
/*INGRESS PROCESSING*/
control MyIngress(inout headers hdr,inout metadata meta,inout standard_metadata_t standard_metadata){
action drop(){
mark_to_drop();//将要丢弃的包标记为丢弃
}
action ipv6_forward(macAddr_t dstAddr,egressSpec_t port){
standard_metadata.egress_spec = port;
hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
hdr.ethernet.dstAddr = dstAddr;
hdr.ipv6.hopLimit = hdr.ipv6.hopLimit - 1;//这个类似ipv4中ttl,为0时就超时
}
table ipv6_lpm{
key = {
hdr.ipv6.dstAddr: lpm;//lpm是最长前缀匹配,exact完全匹配,ternary三元匹配
}
actions = {
ipv6_forward;//转发
drop;//丢弃
NoAction;//空动作
}
size = 1024;//流表项容量
default_action = drop();//table miss则丢弃
}
apply{
if(hdr.ipv6.isValid()){
ipv6_lpm.apply();
}
}
}
/*EGRESS PROCESSING*/
control MyEgress(inout headers hdr,inout metadata meta,inout standard_metadata_t standard_metadata){
apply{}
}
/*CHECKSUM COMPUTATION*/
control MyComputeChecksum(inout headers hdr,inout metadata meta){
apply{}
}
/*DEPARSER*/
control MyDeparser(packet_out packet,in headers hdr){
apply{
packet.emit(hdr.ethernet);
packet.emit(hdr.ipv6);
}
}
/*SWITCH*/
V1Switch(
MyParser(),
MyVerifyChecksum(),
MyIngress(),
MyEgress(),
MyComputeChecksum(),
MyDeparser()
)main;
实验拓扑
这里实验的拓扑用来最简单的一个交换机下挂两个主机,交换机是bmv2。其json文件书写如下: 交换机s1的流表项通过s1-commands.txt文件定义。
{
"program": "ipv6_forward.p4",
"language": "p4-16",
"targets": {
"multiswitch": {
"auto-control-plane": true,
"cli": true,
"pcap_dump": true,
"bmv2_log": true,
"links": [["h1", "s1"], ["h2", "s1"]],
"hosts": {
"h1": {
},
"h2": {
}
},
"switches": {
"s1": {
"entries": "s1-commands.txt"
}
}
}
}
}
在s1-commans.txt文件中做如下定义:
table_set_default ipv6_lpm drop
table_add ipv6_lpm ipv6_forward fe80::5678/128 => 00:04:00:00:00:02 2
table_add ipv6_lpm ipv6_forward fe80::1234/128 => 00:04:00:00:00:01 1
将其流表项画出来如下:
收发包脚本代码
鉴于对python socket发包代码不是那么了解,所以套用了一部分模板做了修改。
send.py
import argparse
import sys
import socket
import random
import struct
from scapy.all import sendp, send, get_if_list, get_if_hwaddr
from scapy.all import Packet
from scapy.all import Ether, IPv6, UDP
def get_if():
ifs=get_if_list()[20230912P4语法三.md](20230912P4%D3%EF%B7%A8%C8%FD.md)
iface=None
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 main():
if len(sys.argv)<3:
print 'pass 3 arguments:<source> <destination> "<message>"'
exit(1)
saddr = sys.argv[1]
addr = sys.argv[2]
iface = get_if()
print "sending on interface %s to %s" % (iface, str(addr))
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]
pkt.show2()
sendp(pkt, iface=iface, verbose=False)
if __name__ == '__main__':
main()
receive.py
import sys
import struct
import os
from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr
from scapy.all import Packet, IPOption
from scapy.all import ShortField, IntField, LongField, BitField, FieldListField, FieldLenField
from scapy.all import IPv6, TCP, UDP, Raw
from scapy.layers.inet import _IPOption_HDR
def get_if():
ifs=get_if_list()
iface=None
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
class IPOption_MRI(IPOption):
name = "MRI"
option = 31
fields_desc = [ _IPOption_HDR,
FieldLenField("length", None, fmt="B",
length_of="swids",
adjust=lambda pkt,l:l+4),
ShortField("count", 0),
FieldListField("swids",
[],
IntField("", 0),
length_from=lambda pkt:pkt.count*4) ]
def handle_pkt(pkt):
print "got a packet"
pkt.show2()
#hexdump(pkt)
sys.stdout.flush()
def main():
ifaces = filter(lambda i: 'eth' in i, os.listdir('/sys/class/net/'))
iface = ifaces[0]
print "sniffing on %s" % iface
sys.stdout.flush()
sniff(filter="udp and port 4321",iface = iface,
prn = lambda x: handle_pkt(x))
if __name__ == '__main__':
main()
测试
./run.sh
//在mininet cli中
xterm h1 h2
//在h2中
./receive.py
//在h1中
./send.py fe80::1234 fe80::5678 "Hello p4!"
结果如下:
牢骚
第一次尝试构建p4相关的实验,了解了一部分p4的语法,以及一些的工作原理,也发掘出一部分并未了解的知识内容待后续去琢磨学习。过程有很多不完美,结果也有很多不完美,虽然现在也不满足,但是寒假也快结束了。