Hello everyone,
I am new to P4 and i am trying to implement priority queues on my p4 app so that i am able to prioritize BitTorrent traffic over other kinds of traffic.
Here is my p4 code:
/* -- P4_16 -- */
#include <core.p4>
#include <v1model.p4>
const bit<16> TYPE_IPV4 = 0x800;
const bit<8> TYPE_TCP = 6;
/*************************************************************************
*********************** H E A D E R S ***********************************
*************************************************************************/
typedef bit<9> egressSpec_t;
typedef bit<48> macAddr_t;
typedef bit<32> ip4Addr_t;
typedef bit<3> priority_t;
header ethernet_t {
macAddr_t dstAddr;
macAddr_t srcAddr;
bit<16> etherType;
}
header ipv4_t {
bit<4> version;
bit<4> ihl;
bit<8> tos;
bit<16> totalLen;
bit<16> identification;
bit<3> flags;
bit<13> fragOffset;
bit<8> ttl;
bit<8> protocol;
bit<16> hdrChecksum;
ip4Addr_t srcAddr;
ip4Addr_t dstAddr;
}
header tcp_t{
bit<16> srcPort;
bit<16> dstPort;
bit<32> seqNo;
bit<32> ackNo;
bit<4> dataOffset;
bit<4> res;
bit<1> cwr;
bit<1> ece;
bit<1> urg;
bit<1> ack;
bit<1> psh;
bit<1> rst;
bit<1> syn;
bit<1> fin;
bit<16> window;
bit<16> checksum;
bit<16> urgentPtr;
}
header cpu_t {
bit<16> srcPort;
bit<16> dstPort;
}
struct metadata {
@field_list(0)
bit<9> ingress_port;
/* empty */
}
struct headers {
ethernet_t ethernet;
ipv4_t ipv4;
tcp_t tcp;
cpu_t cpu;
}
/*************************************************************************
*********************** 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 {
transition parse_ethernet;
}
state parse_ethernet {
packet.extract(hdr.ethernet);
transition select(hdr.ethernet.etherType){
TYPE_IPV4: parse_ipv4;
default: accept;
}
}
state parse_ipv4 {
packet.extract(hdr.ipv4);
transition select(hdr.ipv4.protocol){
TYPE_TCP: parce_tcp;
default: accept;
}
}
state parce_tcp {
packet.extract(hdr.tcp);
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) {
action drop() {
mark_to_drop(standard_metadata);
}
action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) {
hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
hdr.ethernet.dstAddr = dstAddr;
standard_metadata.egress_spec = port;
hdr.ipv4.ttl = hdr.ipv4.ttl -1;
}
action learn_tcp_port() {
clone_preserving_field_list(CloneType.I2E, 100, 0);
}
action set_priority(priority_t priority) {
standard_metadata.priority = priority;
}
table ipv4_lpm {
key = {
hdr.ipv4.dstAddr: lpm;
}
actions = {
ipv4_forward;
drop;
NoAction;
}
size = 1024;
default_action = NoAction();
}
table tcp_port_learn {
key = {
hdr.tcp.srcPort: exact;
}
actions = {
learn_tcp_port;
NoAction;
}
size = 1024;
default_action = learn_tcp_port;
}
table priority_set {
key = {
hdr.tcp.srcPort: exact;
}
actions = {
set_priority;
NoAction;
}
default_action = NoAction();
}
apply {
// - ipv4_lpm should be applied only when IPv4 header is valid
if (hdr.ipv4.isValid()) {
ipv4_lpm.apply();
if (hdr.ipv4.protocol == TYPE_TCP){
tcp_port_learn.apply();
if (priority_set.apply().hit){
//
}
else {
standard_metadata.priority = (bit<3>)0;
}
}
else {
standard_metadata.priority = (bit<3>)0;
}
}
}
}
/*************************************************************************
**************** 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 {
hdr.ipv4.tos = (bit<8>)standard_metadata.qid;
// Non-cloned packets have an instance_type of 0, so then we clone them
// using the mirror ID = 100. That, in combination with the control plane, will
// select to which port the packet has to be cloned to.
if (standard_metadata.instance_type != 0){
hdr.cpu.setValid();
hdr.cpu.srcPort = hdr.tcp.srcPort;
hdr.cpu.dstPort = hdr.tcp.dstPort;
// Disable other headers
hdr.ethernet.setInvalid();
hdr.ipv4.setInvalid();
}
}
}
/*************************************************************************
************* 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 {
update_checksum(
hdr.ipv4.isValid(),
{ hdr.ipv4.version,
hdr.ipv4.ihl,
hdr.ipv4.tos,
hdr.ipv4.totalLen,
hdr.ipv4.identification,
hdr.ipv4.flags,
hdr.ipv4.fragOffset,
hdr.ipv4.ttl,
hdr.ipv4.protocol,
hdr.ipv4.srcAddr,
hdr.ipv4.dstAddr },
hdr.ipv4.hdrChecksum,
HashAlgorithm.csum16);
}
}
/*************************************************************************
*********************** D E P A R S E R *******************************
*************************************************************************/
control MyDeparser(packet_out packet, in headers hdr) {
apply {
packet.emit(hdr.cpu);
packet.emit(hdr.ethernet);
packet.emit(hdr.ipv4);
packet.emit(hdr.tcp);
}
}
/*************************************************************************
*********************** S W I T C H *******************************
*************************************************************************/
V1Switch(
MyParser(),
MyVerifyChecksum(),
MyIngress(),
MyEgress(),
MyComputeChecksum(),
MyDeparser()
) main;
and here is the code for the controller i am using:
#!/usr/bin/env python3
import sys
import struct
import os
from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr, bind_layers
from scapy.all import Packet, IPOption, Ether, IP, raw, TCP
from scapy.all import ShortField, IntField, LongField, BitField, FieldListField, FieldLenField
from p4utils.utils.helper import load_topo
from p4utils.utils.sswitch_thrift_API import SimpleSwitchThriftAPI
from scapy.layers.inet import _IPOption_HDR
import binascii
class CpuHeader(Packet):
name = βCpuPacketβ
fields_desc = [BitField(βsrcPortβ,0,16), BitField(βdstPortβ, 0, 16)]
bind_layers(CpuHeader, TCP)
class PrController(object):
tcp_ports_table = []
def __init__(self, sw_name):
self.topo = load_topo('topology.json')
self.sw_name = sw_name
self.thrift_port = self.topo.get_thrift_port(sw_name)
self.cpu_port = self.topo.get_cpu_port_index(self.sw_name)
self.controller = SimpleSwitchThriftAPI(self.thrift_port)
self.init()
def init(self):
self.controller.reset_state()
self.add_mirror()
self.ipv4_forward_fill()
self.controller.set_queue_rate(1000,2)
self.controller.set_queue_rate(1000,1)
def add_mirror(self):
if self.cpu_port:
self.controller.mirroring_add(100, self.cpu_port)
def ipv4_forward_fill(self):
self.controller.table_add("ipv4_lpm", "ipv4_forward", ['10.0.0.1/32'], ['00:00:00:00:00:01','1'])
self.controller.table_add("ipv4_lpm", "ipv4_forward", ['10.0.0.2/32'], ['00:00:00:00:00:02','2'])
def handle_pkt(self, pkt):
print("Controller got a packet")
cpu_packet = CpuHeader(bytes(pkt))
tcp_data = bytes(cpu_packet.payload.payload)
print(cpu_packet.srcPort, cpu_packet.dstPort)
#print(tcp_data)
self.handshake_check(cpu_packet, tcp_data)
#print(self.tcp_ports_table)
sys.stdout.flush()
def handshake_check(self, cpu_header, data):
try:
hex_data = binascii.hexlify(data)
except:
return
if hex_data.startswith(b'13426974546f7272656e742070726f746f636f6c'):
print("\n\n ---- HANDSHAKE ---- \n")
pr = '7'
self.learn_priority([cpu_header.srcPort, cpu_header.dstPort],pr)
#else:
#self.learn_priority([cpu_header.srcPort, cpu_header.dstPort],0)
def learn_priority(self, learning_data, priority):
for tcp_port in learning_data:
s_tcp_port = str(tcp_port)
if s_tcp_port not in self.tcp_ports_table:
self.tcp_ports_table.append(s_tcp_port)
self.controller.table_add("tcp_port_learn", "NoAction", [str(tcp_port)])
self.controller.table_add("priority_set", "set_priority", [str(tcp_port)], [priority])
def run_cpu_port(self):
cpu_port_intf = str(self.topo.get_cpu_port_intf(self.sw_name).replace("eth0", "eth1"))
sniff(iface=cpu_port_intf, prn=self.handle_pkt)
if name == βmainβ:
sw_name = sys.argv[1]
controller = PrController(sw_name).run_cpu_port()
Does this code work? How can i check it? If not, what do i have to do to make it work?