SDN Exp#1 log
第一次實験任務在於完成在仅有四個結點的 1969 年 ARPANET 網絡中的自學習交換機以及解決網絡中環路引起的ARP廣播風暴。實験通過 Mininet
構建網絡拓扑,Ryu app
編程實現控制器。
自學習交換機
正如 RIP 協議一般,此處交換機轉發數據包必須依據一定的規則,否則祇使用洪泛的方式將產生許多不必要的數據包,浪費帶寛。本實験的實現也非常容易,其思想在於對於每個到達的數據包,交換機都根據該包的源地址 $src$ 學習,若下次有目的地為 $src$ 的數據包就能直接通過此次記錄的端口進行發送。
但是需要注意的是,SDN 網絡不同於 RIP,對於每次非洪泛發包需要更新流表。因為在 SDN 中,數據包通過在流表中進行匹配而確定轉發規則,若匹配失敗,需要向控制器發送一個名為 packet-in
的信息請求控制器來處理當前包。
在 packet_in_handler()
中實現如下:
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def packet_in_handler(self, ev):
msg = ev.msg
dp = msg.datapath
ofp = dp.ofproto
parser = dp.ofproto_parser
# the identity of switch
dpid = dp.id
self.mac_to_port.setdefault(dpid,{})
# the port that receive the packet
in_port = msg.match['in_port']
pkt = packet.Packet(msg.data)
eth_pkt = pkt.get_protocol(ethernet.ethernet)
# get the mac
dst = eth_pkt.dst
src = eth_pkt.src
# we can use the logger to print some useful information
self.logger.info('packet: %s %s %s %s', dpid, src, dst, in_port)
# you need to code here to avoid the direct flooding
# having fun
# :)
self.mac_to_port[dpid][src] = in_port
if dst in self.mac_to_port[dpid]:
# out_port = ofp.OFPP_FLOOD
out_port = self.mac_to_port[dpid][dst]
else:
out_port = ofp.OFPP_FLOOD
if out_port != ofp.OFPP_FLOOD:
match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
self.add_flow(dp, 1, match, actions)
data = None
if msg.buffer_id == ofp.OFP_NO_BUFFER:
data = msg.data
actions = [dp.ofproto_parser.OFPActionOutput(out_port)]
out = dp.ofproto_parser.OFPPacketOut(datapath=dp, buffer_id=msg.buffer_id, in_port=in_port, actions=actions, data=msg.data)
dp.send_msg(out)
正如上方所示,我需要補充的實際也祇有 TODO
部分。
廣播風暴問題
問题在於拓扑中出現了環路,而這將導致廣播的 ARP 請求包肆虐無度。指導書中提到了傳統的生成樹協議,並給出一種待實現的策略:強制使某个源主機發出的 ARP 請求包仅能由交換機的某個特定端口接收(例如:首次由洪泛接收的端口),稍微一想就能發現,對於特定的 ARP 請求包,其在拓扑中能傳播的途徑顯然構成了一棵生成樹,不過其不同於傳統的生成樹,在不同的數據包發出时會動熊生成可能不同的樹,魯棒性較強。
實現如下:
if dst == ETHERNET_MULTICAST and ARP in header_list:
# you need to code here to avoid broadcast loop to finish mission 2
self.sw.setdefault((dpid, src, dst), None)
if self.sw.get((dpid, src, dst)) == None:
self.sw[(dpid, src, dst)] = in_port
elif self.sw[(dpid, src, dst)] != in_port:
return
另外,實験附加題要求實現另一種方法。根據 ARP 緩存在相當長一段時間不會清除,可以對 ARP 數據包標記一个過期時間 TIMEOUT
,在過期時間未到之時段,不在接收相同包。
實現如下:
if dst == ETHERNET_MULTICAST and ARP in header_list:
ticks = time.time()
self.sw.setdefault((dpid, src, dst), 0)
if self.sw[(dpid, src, dst)] < ticks:
self.sw[(dpid, src, dst)] = ticks + TIMEOUT
else:
return
注意,這樣考慮基於網絡中不會發生丢包,若丢包發生,則源主機重發的有效包可能被拒收,直到記錄過期,是有概率降低效率的。
参考
- STP(Spaning Tree Protocol)
- Performance evaluation of OpenFlow-based software-defined networks based on queueing model
- RyuBook 1.0 說明文件