SDN Exp#2 log
第二次實験欲在擁有九個結點的拓扑網絡實現最少延時路徑轉發以及對鏈路故障的容忍。
最小時延路徑
該任務關鍵並不在於算法層面上如何求解最短路,基本框架本已實現好,需要理解並完成的是鏈路時延的計算。該計算主要依據 LLDP 以及 Echo 數據包。
当帶有時間戳的 LLDP 經由交換機 S1 至 S2 而後返囬控制器時,控制器會根據接收時間以及發送時間計算出 控制器 $\rightarrow$ S1$\rightarrow$ S2$\rightarrow$ 控制器 的時延 lldp_delay_s12
,同理可以計算 lldp_delay_s21
。為獲取 S1 至 S2 的時延,還需控制器至 S1 以及 S2 的時延,這步可經由 Echo 數據包實現。故可得鏈路平均時延為:
實現 lldp_delay
的函數如下:
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def packet_in_handler(self, ev):
msg = ev.msg
dpid = msg.datapath.id
try:
src_dpid, src_port_no = LLDPPacket.lldp_parse(msg.data)
if self.switches is None:
self.switches = lookup_service_brick('switches')
for port in self.switches.ports.keys():
if src_dpid == port.dpid and src_port_no == port.port_no:
self.lldp_delay[(src_dpid, dpid)] = self.switches.ports[port].delay
except:
return
對於 echo_delay
,需要多開一個線程來不停發送 echo_request
:
def _measure(self):
while True:
self._send_echo_request()
hub.sleep(SEND_ECHO_REQUEST_INTERVAL)
def _send_echo_request(self):
switches = None
switches = get_switch(self)
for switch in switches:
dp = switch.dp
parser = dp.ofproto_parser
echo_req = parser.OFPEchoRequest(dp, data='{:.10f}'.format(time.time()).encode("utf-8"))
dp.send_msg(echo_req)
@set_ev_cls(ofp_event.EventOFPEchoReply, MAIN_DISPATCHER)
def echo_reply_handler(self, ev):
try:
latency = time.time() - eval(ev.msg.data)
self.echo_latency[ev.msg.datapath.id] = latency
except:
return
最後需要在生成拓扑的函數中計算結点(交換機)間的時延即可:
delay = (self.lldp_delay.get((link.src.dpid, link.dst.dpid), INF) + \
self.lldp_delay.get((link.dst.dpid, link.src.dpid), INF) - \
self.echo_latency.get(link.src.dpid, 0) - \
self.echo_latency.get(link.dst.dpid, 0)) / 2
if delay < 0:
delay = 0
self.topo_map.add_edge(link.src.dpid, link.dst.dpid, hop=1, \
delay=delay, is_host=False)
故障容忍鏈路時延
關鍵在於實現流表的删除。
對於每次 link s1 s2 down
事件,都需要將相关路徑上的交換機的流表删除。
實験中祇处理了 arp
以及 ipv4
包的 packet-in
,筆者在处理時全部下發了流表,於是在 link down
事件發生時必須將這兩類的流表項删除。
在指導書上僅 ` SDC ping MIT` 且祇删除該路徑上的某兩個点(S1, S2)的前提下,可以考慮如下删除策略:
- 查找路徑上所有 datapath,按其中
mac_to_port
所有可達的eth_dst
進行OFPMatch
,進一步按此標準删除相關流表; - 查找路徑上所有 datapath,按該路徑起點
src_ip
以及終點dst_ip
進行OFPMatch
,進一步按此標準删除相關流表。
據說此即為先生實験之原意,然而其實據此將帶來虚假的故障容忍。由於自始至終僅保留了一條 path
,若 link down
的两个交換機都不在這條路上的話將删去零條流表項,以致 ping
不通的事件發生。實際上筆者在實験中嘗試了 pingall
後再 link s8 s9 down
就發生此類情況。所以筆者認為真正正确的做法就是删除網路中所有匹配 arp
以及 ipv4
包的流表項。
for dp in self.network_awareness.switch_info.values():
match = parser.OFPMatch(eth_type=0x0800)
self.sup_del_flow(dp, match)
match = parser.OFPMatch(eth_type=0x0806)
self.sup_del_flow(dp, match)
0x0800
以及 0x0806
分別為 ivp4
和 arp
數據报類型。
私以為該任務還有一些值得注意的點:
network_awareness.py
中的_get_topology()
實現有誤,每次檢測到拓扑結構改變應當删去舊圖,否則將致誤。這是因為網絡拓扑獲得是經由add_edge
實現的,所以不清空實際就會變成圖中多了不少莫名其妙的邊。- 在流表删除步驟中,
OFPFlowMod
規則較為嚴格,參數幾乎都要指定,不然可能無法删除(建議先將流表項設為永不 expired,這樣可以用於确認是否真正删除了指空下來相關流表)。 - 由於鏈路狀熊改變,故
mac_to_port
以及sw
須更新,不然可能導致轉發錯誤(筆者直接將其清空)。 - 若下發了
arp
的流表,需要指定eth_type
,否則即使無ipv4
相關流表亦能匹配成功,且結果正确。這是由於按實験一解決arp
廣播風暴的方法得到的轉發規則即為時延最短路。
附加題
附加題意在優化 LLDP,当網路中端口很多時如何使控制器僅發送交換機數量的 LLDP 數據包來減少資源消耗。由於鏈路信息包含兩兩可達交換機之間通信的端口,所以在網絡中數據包的數量並不能大幅減少,或者說,其實數據包數量是「邊數」級別的。但可以嘗試使控制器下發的數據包減少,於是考慮如下策略:
- 控制器向每個交換機的某个端口發送 LLDPDU,並標記
Chassis ID
為該交換機的 ID。 - 對每個交換機下發送流表,其規則為:對控制器發送的 LLDPDU,洪泛;對非控制器發送的 LLDPDU,由接送端口發送給控制器。
如此一來,控制器对於接送到的 LLDPDU,可以得知其經由第一個交換機 S1 的 ID,以及第二個交換機 S2 的 ID 以及接收端口 port2,注意到控制器同样向 S2 發送了 LLDPDU,故 S1 亦會向控制器發送由 S2 收到的 LLDPDU,故此時控制得知 S1 与 S2 通信的端口 port1,由此方法即可得知全局鏈路信息。