..

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 數據包實現。故可得鏈路平均時延為:

\[delay=\frac{delay_{lldp}(s1,s2)+delay_{lldp}(s2,s1)-delay_{echo}(s1)-delay_{echo}(s2)}{2}\]

實現 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)的前提下,可以考慮如下删除策略:

據說此即為先生實験之原意,然而其實據此將帶來虚假的故障容忍。由於自始至終僅保留了一條 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 分別為 ivp4arp 數據报類型。

私以為該任務還有一些值得注意的點:

附加題

附加題意在優化 LLDP,当網路中端口很多時如何使控制器僅發送交換機數量的 LLDP 數據包來減少資源消耗。由於鏈路信息包含兩兩可達交換機之間通信的端口,所以在網絡中數據包的數量並不能大幅減少,或者說,其實數據包數量是「邊數」級別的。但可以嘗試使控制器下發的數據包減少,於是考慮如下策略:

如此一來,控制器对於接送到的 LLDPDU,可以得知其經由第一個交換機 S1 的 ID,以及第二個交換機 S2 的 ID 以及接收端口 port2,注意到控制器同样向 S2 發送了 LLDPDU,故 S1 亦會向控制器發送由 S2 收到的 LLDPDU,故此時控制得知 S1 与 S2 通信的端口 port1,由此方法即可得知全局鏈路信息。

參考