P4-utils: Monitor Link BW
[Topology]
In this example, h1 will send the traffic via s1,s3,s5 to h2. We will see the link bw.
[ip_forward.p4]
#include <core.p4> #include <v1model.p4> typedef bit<48> macAddr_t; typedef bit<9> egressSpec_t; const bit<4> MAX_PORT = 5; header arp_t { bit<16> htype; bit<16> ptype; bit<8> hlen; bit<8> plen; bit<16> opcode; bit<48> hwSrcAddr; bit<32> protoSrcAddr; bit<48> hwDstAddr; bit<32> protoDstAddr; } header ethernet_t
{ bit<48> dstAddr; bit<48> srcAddr; bit<16> etherType; } header ipv4_t { bit<4> version; bit<4> ihl; bit<8> diffserv; bit<16> totalLen; bit<16>
identification; bit<3> flags; bit<13> fragOffset; bit<8> ttl; bit<8> protocol; bit<16> hdrChecksum; bit<32> srcAddr; bit<32> dstAddr; } struct metadata { } struct headers { @name(".arp") arp_t arp; @name(".ethernet")
ethernet_t
ethernet; @name(".ipv4")
ipv4_t ipv4; } parser ParserImpl(packet_in packet, out headers hdr,
inout metadata meta, inout
standard_metadata_t standard_metadata)
{ @name(".parse_arp") state parse_arp { packet.extract(hdr.arp);
transition accept; } @name(".parse_ethernet")
state parse_ethernet { packet.extract(hdr.ethernet);
transition select(hdr.ethernet.etherType)
{
16w0x800: parse_ipv4;
16w0x806: parse_arp;
default: accept; } } @name(".parse_ipv4")
state parse_ipv4 { packet.extract(hdr.ipv4);
transition accept; } @name(".start")
state start {
transition parse_ethernet; } } control egress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata)
{ counter((bit<32>)MAX_PORT, CounterType.packets_and_bytes)
egressPortCounter; apply { egressPortCounter.count((bit<32>)standard_metadata.egress_port); } } control ingress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata)
{ @name(".set_nhop") action set_nhop(macAddr_t dstAddr, egressSpec_t port) {
//set the src mac address as the previous dst, this is not correct right? hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
//set the destination mac address that we got from the match in the
table hdr.ethernet.dstAddr = dstAddr;
//set the output port that we also get from the table standard_metadata.egress_spec =
port;
//decrease ttl by 1
hdr.ipv4.ttl = hdr.ipv4.ttl - 1; } @name("._drop")
action _drop() { mark_to_drop(standard_metadata); } @name(".ipv4_lpm")
table ipv4_lpm {
actions = {
set_nhop;
_drop; } key
= {
hdr.ipv4.dstAddr: lpm; }
size = 512;
const default_action = _drop(); } apply { if (hdr.ipv4.isValid()){
ipv4_lpm.apply(); } } } control DeparserImpl(packet_out packet, in headers hdr)
{ apply { packet.emit(hdr.ethernet); packet.emit(hdr.arp); packet.emit(hdr.ipv4); } } control verifyChecksum(inout headers hdr, inout metadata meta) { apply { verify_checksum(true,
{ hdr.ipv4.version, hdr.ipv4.ihl, hdr.ipv4.diffserv, 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); } } control computeChecksum(inout headers hdr, inout metadata meta) { apply { update_checksum(true, { hdr.ipv4.version, hdr.ipv4.ihl,
hdr.ipv4.diffserv, 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); } } V1Switch(ParserImpl(), verifyChecksum(), ingress(), egress(), computeChecksum(), DeparserImpl()) main; |
[p4app.json]
{
"program": "ip_forward.p4",
"switch": "simple_switch",
"compiler": "p4c",
"options": "--target bmv2 --arch v1model --std
p4-16",
"switch_cli": "simple_switch_CLI",
"cli": true,
"pcap_dump": true,
"enable_log": true,
"topo_module": { "file_path":
"", "module_name":
"p4utils.mininetlib.apptopo", "object_name":
"AppTopoStrategies"
},
"controller_module": null,
"topodb_module": { "file_path":
"", "module_name":
"p4utils.utils.topology", "object_name":
"Topology"
},
"mininet_module": { "file_path":
"", "module_name":
"p4utils.mininetlib.p4net", "object_name":
"P4Mininet"
},
"topology": { "assignment_strategy":
"mixed", "auto_arp_tables":
"true", "auto_gw_arp":
"true", "links":
[["h1", "s1"], ["s1", "s2"],
["s1", "s3"], ["s2", "s4"],
["s4", "s5"], ["s3", "s5"],
["s5", "h2"]], "hosts": {
"h1": { },
"h2": { } }, "switches": {
"s1": { },
"s2": { },
"s3": { },
"s4": { },
"s5": { } }
} } |
[routing-controller.p4]
from p4utils.utils.topology
import Topology from p4utils.utils.sswitch_API
import SimpleSwitchAPI import time import datetime from collections import defaultdict class RoutingController(object): def __init__(self): self.topo = Topology(db="topology.db") self.controllers = {} self.init() self.prev_time = 0 self.port_bytes = defaultdict(lambda:defaultdict(lambda:None)) def init(self): self.connect_to_switches() self.reset_states() self.set_table_defaults() def reset_states(self): [controller.reset_state() for
controller in self.controllers.values()] def connect_to_switches(self):
for p4switch in self.topo.get_p4switches():
thrift_port = self.topo.get_thrift_port(p4switch)
self.controllers[p4switch]
= SimpleSwitchAPI(thrift_port) def set_table_defaults(self):
for controller in self.controllers.values():
controller.table_set_default("ipv4_lpm",
"_drop", []) def addtwodimdict(thedict, key_a, key_b, val): if
key_a in adic:
thedict[key_a].update({key_b: val})
else:
thedict.update({key_a:{key_b: val}}) def monitor_links(self): now
= time.time() diff
= now - self.prev_time for
sw_name, controller in self.controllers.items():
for intf, node in self.topo.get_interfaces_to_node(sw_name).items(): sw_port = self.topo.node_to_node_port_num(sw_name, node) if
self.port_bytes[sw_name][sw_port] is not None: a=self.controllers[sw_name].counter_read("egressPortCounter",
sw_port) b=self.port_bytes[sw_name][sw_port] bw = (a.bytes - b.bytes)
* 8 /diff/1000.0; print "sw_name:",
sw_name, "intf:",
intf, "bw:", bw, "kbps" self.port_bytes[sw_name][sw_port]=self.controllers[sw_name].counter_read("egressPortCounter",
sw_port); self.prev_time=now def route(self):
switches = {sw_name:{} for sw_name in
self.topo.get_p4switches().keys()} print
"switches:", switches
print "==============================================================================="
print "self.controllers:",
self.controllers
print
"==============================================================================="
for sw_name, controller in self.controllers.items():
for sw_dst in self.topo.get_p4switches():
#if its ourselves we create direct connections
if sw_name == sw_dst:
for host in self.topo.get_hosts_connected_to(sw_name):
sw_port = self.topo.node_to_node_port_num(sw_name,
host)
host_ip = self.topo.get_host_ip(host)
+ "/32"
host_mac = self.topo.get_host_mac(host) print
host, "(", host_ip, host_mac,
")", "-->", sw_name,
"with port:", sw_port
#add rule
print "table_add at {}:".format(sw_name)
self.controllers[sw_name].table_add("ipv4_lpm",
"set_nhop", [str(host_ip)],
[str(host_mac), str(sw_port)])
#check if there are directly connected hosts
else:
if self.topo.get_hosts_connected_to(sw_dst):
paths = self.topo.get_shortest_paths_between_nodes(sw_name,
sw_dst) print
sw_name,"->",sw_dst,":",paths
for host in self.topo.get_hosts_connected_to(sw_dst): next_hop
= paths[0][1] #if there are more than one path,
choose the first path
host_ip = self.topo.get_host_ip(host)
+ "/24"
sw_port = self.topo.node_to_node_port_num(sw_name,
next_hop)
dst_sw_mac = self.topo.node_to_node_mac(next_hop,
sw_name)
#add rule
print "table_add at {}:".format(sw_name)
self.controllers[sw_name].table_add("ipv4_lpm",
"set_nhop", [str(host_ip)],
[str(dst_sw_mac), str(sw_port)]) def main(self): self.route() while
True: print
"==============================================================" print(datetime.datetime.now()) self.monitor_links() time.sleep(1) if __name__ == "__main__": controller = RoutingController().main() |
Execution
Open another terminal to run controller.
Run iperf in h1 and h2
In controller terminal (we can see that the packets go through s1-eth3, s3-eth2, and s5-eth3. The Link BW is around 107 kbps.)
p.s. You can try other topologies. I think the controller can still work.
Dr. Chih-Heng Ke
Department of Computer Science and
Information Engineering, National Quemoy University, Kinmen, Taiwan
Email: smallko@gmail.com