RyuBook1.0案例一:SwitchingHub專案原始碼分析

劉建甌發表於2018-08-01

開發目標

實現一個帶MAC地址學習功能的二層交換機

Openflow交換機與Openflow控制器安全通道建立步驟

  • switch and controller建立未加密TCP連線或者加密的TLS連線
  • 確定連線通道的Openflow版本
  • 握手
  • 其他操作

建立連線通道後,二者發生Hello包,進行協商Openflow版本號

完成交換Hello訊息之後建立安全通道,執行握手。Controller發生Features請求,並處理Features響應

接收到Features響應,控制器可以向交換機傳送SET_CONFIG或者GET_CONFIG請求訊息,進行設定交換機預設配置或者查詢交換機配置。

之後,可以進行OpenFlow的其他操作

Flow-Mod訊息

Flow-Mod(Modify Flow Entry Message)由控制器向交換機下發的設定流表項的資訊

其中 ofp_match結構體為資料包匹配部分。

程式分析

設定想要向交換機協商的OpenFlow版本號

OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]

下發Table-miss流表項

設定完成該項引數配置,控制器自動執行第一步操作,即交換Hello包,協商版本號。協商完成之後,自動執行交換Features包,進行握手。

握手完成後,使用set_ev_cls函式處理Features響應包

@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)

第二項引數詳情如下:

Defination Explanation
HANDSHAKE_DISPATCHER 交換HELLO訊息
CONFIG_DISPATCHER 等待接收SwitchFeatures訊息
MAIN_DISPATCHER 正常狀態
DEAD_DISPATCHER 連線斷開

定義處理函式,並解析返回包的欄位

def switch_features_handler(self, ev):
    datapath = ev.msg.data
    ofproto = datapath.ofproto
    parser = datapath.ofproto_parser

安裝table-miss entry

match = parser.OFPMatch()

table-miss:

OpenFlow1.3版本為處理table miss事件專門引入的條目。並規定每一個flow table必須要支援table-miss flow entry去處理table miss情況。table-miss flow entry具備最低的優先順序(0);必須至少能夠支援使用CONTROLLER保留埠傳送包,使用Clear-Actions指令丟包;table-miss flow entry和其他flow entry具有相同的特性:預設不存在,控制器可以隨時新增或丟棄該條目,也可以到期;如果使用CONTROLLER保留埠發生資料包,Packet-In傳送原因必須標明table-miss。

actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
                                  ofproto.OFPCML_NO_BUFFER)]

OFPActionOutput() :使用一個packet_out 訊息去指定你想從交換機的哪個埠傳送出資料包。在該應用中,按照標準指定通過CONTROLLER保留埠發生,所以選擇了OFPP_CONTROLLER埠。第二個引數OFPCML_NO_BUFFER,指明:訊息中必須包含完整的包,而不會被緩衝。

指定完成actions,使用類的add_flow向控制器新增流表項

self.add_flow(self, datapath, 0, match, actions)

接下來分析add_flow的具體實現過程:

構建OpenFlow流表訊息

inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)]
mod = parser.OFPFlowMod(datapath=datapath, priority=priority,
                                match=match, instructions=inst)

OpenFlow目前主流版本為Openflow1.0,1.1,1.3。相比1.0版本,1.3版本流表項結構變化很大,在這裡我們用到的Instruction可以說是1.0版本中Actions的擴充。Instruction主要負責將流錶轉發到其他Table,流水線,或者進行其他轉發操作。

  • OFPIT_APPLY_ACTIONS: 立即應用actions操作到交換機

函式OFPFlowMod()負責構建Flow-Mod訊息

使用send()函式傳送flow-mod訊息

datapath.send(mod)

以上步驟完成下發配置table-miss流表項

MAC地址學習功能實現

MAC地址學習功能,主要要處理接收到的Packet-In資料包,所以重寫set_cls_ev函式

@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
  • ofp_event.EventOFPPacketIn: 指處理PacketIn訊息
  • MAIN_DISPATCHER: ???

解析資料包

def _package_in_handler(self, ev):
    msg = ev.msg
    datapath = msg.datapath
    ofproto = datapath.ofproto
    parser = datapath.ofproto_parser

獲取datapath.id

dpid = datapath.id

datapath是交換機的唯一標識,即dpid

新增dpid: {}到mac地址、port字典中

self.mac_to_port.setdefault(dpid, {})

從datapath中解析dst,src

pkt = packet.Packet(msg.data)
eth_pkt = pkt.get_protocol(ethernet.ethernet)
dst = eth_pkt.dst
src = eth_pkt.src

從datapath中匹配出in_port

in_port = msg.match[`in_port`]

儲存in_port和mac

self.mac_to_port[dpid][src] = in_port

檢查,如果已經儲存過port和mac,如果存在,則配置out_port為指定埠;如果不存在,則使用OFPP_FLOOD埠,向全部埠進行泛洪。

if dst in self.mac_to_port[dpid]:
    out_port = self.mac_to_port[dpid][dst]
else:
    out_port = ofproto.OFPP_FLOOD

構建Actions

actions = [parser.OFPActionOutput(out_port)]

如果out_port非Flood埠,則下發流表到交換機

if out_port != ofproto.OFPP_FLOOD:
        match = parser.OFPMatch(in_port=in_port, eth_dst=dst)
        self.add_flow(datapath, 1, match, actions)

構建Packet_Out訊息,併傳送到交換機

out = parser.OFPPacketOut(datapath=datapath,
                          buffer_id=ofproto.OFP_NO_BUFFER,
                          in_port=in_port, actions=actions,
                          data=msg.data)
datapath.send_msg(out)


相關文章