若是想要了解 SDN OpenFlow 以及 P4 請不要吝嗇點擊喜歡或是訂閱我喔!(訂閱又不收費XD)
未來有機會也可以跟大家分享我當網管的辛酸血淚史,可以讓人解決問題時可以參考我的文章。
話不多說,我們就累狗!!
What's link Monitor?
甚麼是流量監測?
A:流量監測就是 switch 與 switch 之間的 link 的 loading , controller 需要時時刻刻去紀錄
該loading 才能計算出有效的 routing path,傳統 OpenFlow 的作法是 controller 下指令到 switch去要
該 switch 的流量資訊。
傳統 OpenFlow 的作法造成什麼問題,因為由 controller 下指令去跟 switch 拿資訊,那會造成 control plane以及 data plane 的大量溝通,這對於 controller來說,這是冗餘的。
若是採取 定期的 polling,則會有容易抓不準流量的問題,舉例來說: 這條流量有沒有被處理過?哪些流量是這次要被controller 所蒐集的? 這些都是常見的問題。那市面上有提出很多 polling 的方法,包含 polling single 、 polling some 、 polling all 。Polling 的週期則是根據你問題需要所訂。
P4就可以很有效的去解決我們上述所提到的問題 "Control plane 以及 Data plane的大量溝通", 我們可以透過 P4指令在 Data plane 做好資料蒐集的動作,並且深度的去分析整個網路的狀態,也就是所謂的 Network State Imformation(NSI)。
這是我們所使用到的 topology
那我們應該要先準備好兩個資料欄位,讓他們可以存入資料。
分別是:
byte_cnt_reg: 封包/流量的大小
last_time_reg:最後一個 HOP所記錄的時間
那有興趣的同學們,可以先去研究有甚麼好的方式可以蒐集流量。
解析器
解析器已擴展為支持源路由探查包的解析。解析器是設計中最複雜的部分,因此花一些時間閱讀它。請注意,它不包含任何TODO註釋,因此您無需在此處進行任何更改。
為了解析探測數據包,我們使用hdr.probe.hop_cnt來確定數據包在到達交換機之前經過了多少跳。如果這是第一跳,則數據包中將沒有任何probe_data,因此我們跳過該狀態並直接轉換為parse_probe_fwd狀態。在parse_probe_fwd狀態下,我們使用hdr.probe.hop_cnt字段來確定使用哪個egress_spec標頭字段執行轉發,並將該端口值保存到元數據字段中,該字段隨後用於執行轉發。
入口控制
入口控制塊看起來與基本練習非常相似。唯一的區別是apply塊包含另一個條件,以使用解析器提取的egress_spec字段轉發探測數據包。它還會增加hdr.probe.hop_cnt字段。
出口控制
這是發生有趣的有狀態處理的地方。它使用byte_cnt_reg寄存器來計算自最後一個探測數據包通過端口以來已通過每個端口的字節數。
它將新的probe_data標頭添加到數據包,並填寫了bos(堆棧底部)字段以及swid(交換機ID)字段。
待辦事項:您的工作是填寫其餘的探測數據包字段,以確保您可以正確地測量鏈路利用率。
拆裝機
只需以正確的順序發出所有標頭即可。
請注意,發出標頭堆棧僅會發出堆棧中實際標記為有效的標頭。
/* -*- P4_16 -*- */
#include <core.p4>
#include <v1model.p4>
const bit<16> TYPE_IPV4 = 0x800;
const bit<16> TYPE_PROBE = 0x812;
#define MAX_HOPS 10
#define MAX_PORTS 8
/*************************************************************************
*********************** H E A D E R S ***********************************
*************************************************************************/
typedef bit<9> egressSpec_t;
typedef bit<48> macAddr_t;
typedef bit<32> ip4Addr_t;
typedef bit<48> time_t;
header ethernet_t {
macAddr_t dstAddr;
macAddr_t 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;
ip4Addr_t srcAddr;
ip4Addr_t dstAddr;
}
// Top-level probe header, indicates how many hops this probe
// packet has traversed so far.
header probe_t {
bit<8> hop_cnt;
}
// The data added to the probe by each switch at each hop.
header probe_data_t {
bit<1> bos;
bit<7> swid;
bit<8> port;
bit<32> byte_cnt;
time_t last_time;
time_t cur_time;
}
// Indicates the egress port the switch should send this probe
// packet out of. There is one of these headers for each hop.
header probe_fwd_t {
bit<8> egress_spec;
}
struct parser_metadata_t {
bit<8> remaining;
}
struct metadata {
bit<8> egress_spec;
parser_metadata_t parser_metadata;
}
struct headers {
ethernet_t ethernet;
ipv4_t ipv4;
probe_t probe;
probe_data_t[MAX_HOPS] probe_data;
probe_fwd_t[MAX_HOPS] probe_fwd;
}
/*************************************************************************
*********************** 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;
TYPE_PROBE: parse_probe;
default: accept;
}
}
state parse_ipv4 {
packet.extract(hdr.ipv4);
transition accept;
}
state parse_probe {
packet.extract(hdr.probe);
meta.parser_metadata.remaining = hdr.probe.hop_cnt + 1;
transition select(hdr.probe.hop_cnt) {
0: parse_probe_fwd;
default: parse_probe_data;
}
}
state parse_probe_data {
packet.extract(hdr.probe_data.next);
transition select(hdr.probe_data.last.bos) {
1: parse_probe_fwd;
default: parse_probe_data;
}
}
state parse_probe_fwd {
packet.extract(hdr.probe_fwd.next);
meta.parser_metadata.remaining = meta.parser_metadata.remaining - 1;
// extract the forwarding data
meta.egress_spec = hdr.probe_fwd.last.egress_spec;
transition select(meta.parser_metadata.remaining) {
0: accept;
default: parse_probe_fwd;
}
}
}
/*************************************************************************
************ 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) {
standard_metadata.egress_spec = port;
hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
hdr.ethernet.dstAddr = dstAddr;
hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
}
table ipv4_lpm {
key = {
hdr.ipv4.dstAddr: lpm;
}
actions = {
ipv4_forward;
drop;
NoAction;
}
size = 1024;
default_action = drop();
}
apply {
if (hdr.ipv4.isValid()) {
ipv4_lpm.apply();
}
else if (hdr.probe.isValid()) {
standard_metadata.egress_spec = (bit<9>)meta.egress_spec;
hdr.probe.hop_cnt = hdr.probe.hop_cnt + 1;
}
}
}
/*************************************************************************
**************** 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) {
// count the number of bytes seen since the last probe
register<bit<32>>(MAX_PORTS) byte_cnt_reg;
// remember the time of the last probe
register<time_t>(MAX_PORTS) last_time_reg;
action set_swid(bit<7> swid) {
hdr.probe_data[0].swid = swid;
}
table swid {
actions = {
set_swid;
NoAction;
}
default_action = NoAction();
}
apply {
bit<32> byte_cnt;
bit<32> new_byte_cnt;
time_t last_time;
time_t cur_time = standard_metadata.egress_global_timestamp;
// increment byte cnt for this packet's port
byte_cnt_reg.read(byte_cnt, (bit<32>)standard_metadata.egress_port);
byte_cnt = byte_cnt + standard_metadata.packet_length;
// reset the byte count when a probe packet passes through
new_byte_cnt = (hdr.probe.isValid()) ? 0 : byte_cnt;
byte_cnt_reg.write((bit<32>)standard_metadata.egress_port, new_byte_cnt);
if (hdr.probe.isValid()) {
// fill out probe fields
hdr.probe_data.push_front(1);
hdr.probe_data[0].setValid();
if (hdr.probe.hop_cnt == 1) {
hdr.probe_data[0].bos = 1;
}
else {
hdr.probe_data[0].bos = 0;
}
// set switch ID field
swid.apply();
hdr.probe_data[0].port = (bit<8>)standard_metadata.egress_port;
hdr.probe_data[0].byte_cnt = byte_cnt;
// read / update the last_time_reg
last_time_reg.read(last_time, (bit<32>)standard_metadata.egress_port);
last_time_reg.write((bit<32>)standard_metadata.egress_port, cur_time);
hdr.probe_data[0].last_time = last_time;
hdr.probe_data[0].cur_time = cur_time;
}
}
}
/*************************************************************************
************* 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.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);
}
}
/*************************************************************************
*********************** D E P A R S E R *******************************
*************************************************************************/
control MyDeparser(packet_out packet, in headers hdr) {
apply {
packet.emit(hdr.ethernet);
packet.emit(hdr.ipv4);
packet.emit(hdr.probe);
packet.emit(hdr.probe_data);
packet.emit(hdr.probe_fwd);
}
}
/*************************************************************************
*********************** S W I T C H *******************************
*************************************************************************/
V1Switch(
MyParser(),
MyVerifyChecksum(),
MyIngress(),
MyEgress(),
MyComputeChecksum(),
MyDeparser()
) main;
在此付上我的 Reference,我將會以簡短白話的方式來講解 P4 這套語言,若是你/妳不嫌棄可以訂閱我的發文
每天就根據我自己了解的程度來做發文的動作,如果自己對於 P4也有興趣可以先來預習,那我們明天見!
Reference :
P4_turtorial
[(http://docs.google.com/presentation/d/1zliBqsS8IOD4nQUboRRmF_19poeLLDLadD5zLzrTkVc/edit#slide=id.g37fca2850e_6_1802)]