目录

ryu源码解读——simple_switch.py


总览

文件位置:ryu/ryu/app/simple_switch.py simple_switch.py共110行
1-18:注释
21-29:引库
32-110:继承类RyuApp

32-37

继承类RyuApp,并调用构造函数

class SimpleSwitch(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION]   # 声明支持的Open Flow版本
    # 继承,调用构造函数并添加一个新属性 mac_to_port
    def __init__(self, *args, **kwargs):
        super(SimpleSwitch, self).__init__(*args, **kwargs)
        self.mac_to_port = {}
  • 编写的程序实际上是Ryu控制器的APP,需要继承类RyuApp。
  • 自学习交换机需要控制器来维护交换机的帧交换表,帧交换便就是mac地址与端口号的对应关系,所以添加一个新属性mac_to_port。

39-51

实现一个添加流表的函数

# 函数:添加流表
def add_flow(self, datapath, in_port, dst, src, actions):
    ofproto = datapath.ofproto

    match = datapath.ofproto_parser.OFPMatch(
        in_port=in_port,
        dl_dst=haddr_to_bin(dst), dl_src=haddr_to_bin(src)# 源和目的mac地址
        )

    mod = datapath.ofproto_parser.OFPFlowMod(
        datapath=datapath, match=match, cookie=0,
        command=ofproto.OFPFC_ADD, # 命令:添加新流表
        idle_timeout=0, hard_timeout=0,
        priority=ofproto.OFP_DEFAULT_PRIORITY, # 优先级:默认
        flags=ofproto.OFPFF_SEND_FLOW_REM, 
        actions=actions)
    datapath.send_msg(mod)      #控制器下发消息

datapath

控制器与交换机之间是一条Open Flow数据通路,所以控制器通过datapath来区分不同的交换机,datapath具有一个ofproto参数指示OpenFlow协议内容。ofproto的ofproto_parser定义了协议相关的数据结构。

协议细节

OFPFlowMod:修改流表消息,控制器发送此消息来修改流表。
OFPMatch:流匹配规则。
flags:以下三个值之一
| OFPFF_SEND_FLOW_REM 当流过期或删除时,发送删除流消息。
| OFPFF_CHECK_OVERLAP 首先检查重叠的条目。
| OFPFF_EMERG 标记为紧急情况。

53-94

PacketIn消息的处理逻辑:

@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def _packet_in_handler(self, ev):
    # 解析数据包
    msg = ev.msg
    datapath = msg.datapath
    ofproto = datapath.ofproto

    pkt = packet.Packet(msg.data)
    eth = pkt.get_protocol(ethernet.ethernet)

    if eth.ethertype == ether_types.ETH_TYPE_LLDP:
        # 忽略LLDP类型的包
        return
    dst = eth.dst
    src = eth.src

    # 初始化mac_port对应规则
    dpid = datapath.id
    self.mac_to_port.setdefault(dpid, {})  

    # 打印消息
    self.logger.info("packet in %s %s %s %s", dpid, src, dst, msg.in_port) 

    # 记录此包对应的mac_port对应规则
    self.mac_to_port[dpid][src] = msg.in_port

    # 如果目的mac的对应端口已经知道,就直接设置为输出端口,否则就洪泛
    if dst in self.mac_to_port[dpid]:
        out_port = self.mac_to_port[dpid][dst]
    else:
        out_port = ofproto.OFPP_FLOOD

    # 封装一个OFPActionOutput类型动作:从out_port端口输出
    actions = [datapath.ofproto_parser.OFPActionOutput(out_port)]

    # 如果已经明确了目的mac的输出端口,那么就下发一条流表
    if out_port != ofproto.OFPP_FLOOD:
        self.add_flow(datapath, msg.in_port, dst, src, actions)

    # 如果交换机没有缓存该包,那么就把该包还回去
    data = None
    if msg.buffer_id == ofproto.OFP_NO_BUFFER:
        data = msg.data

    # 发送PacketOut消息
    out = datapath.ofproto_parser.OFPPacketOut(
        datapath=datapath, buffer_id=msg.buffer_id, in_port=msg.in_port,
        actions=actions, data=data)
    datapath.send_msg(out)

细节分析:

LLDP

PacketIn与PacketOut

当交换机收到某个包之后,没有对应的流表,就向控制器发送PacketIn消息,控制器收到之后,进行一些处理,然后发送PacketOut消息给交换机,指示交换机该如何处理这个包。
所以PacketIn消息应当包含这个包,在控制器处理逻辑里面首先就是解析出这个包。
PacketOut消息应当包含一个action,当交换机收到PacketOut时执行这个action。

处理逻辑

  • 解析出数据包,根据数据包的mac和输入端口,绑定这个mac和交换机端口。
  • 如果目的mac对应的交换机端口已知,那么就把输出端口赋这个值。如果未知,就指示交换机洪泛这个包。
  • 如果输出不是洪泛,那么就可以下发流表,绑定目的mac和源mac的转发关系。
  • 封装PacketOut消息,下发。

buffer_id与data

交换机具有缓存,不知道如何处理某个包时,它可以选择是否暂存这个包。

  • 如果没有暂存,那么就应当由控制器通过PacketOut消息把该包传回来,通过OFPPacketOut类的data参数。
  • 如果暂存了,data参数就是None,PacketOut消息指示这个包暂存的位置,也就是buffer_id。

96-110

如果端口发生了一些变化,比如端口增加或者删除,那就在命令行打印相关的消息。 这个部分不是自学习交换机必需的。

@set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER)
def _port_status_handler(self, ev):
    # 解析消息
    msg = ev.msg
    reason = msg.reason
    port_no = msg.desc.port_no

    # 打印
    ofproto = msg.datapath.ofproto
    if reason == ofproto.OFPPR_ADD:
        self.logger.info("port added %s", port_no)
    elif reason == ofproto.OFPPR_DELETE:
        self.logger.info("port deleted %s", port_no)
    elif reason == ofproto.OFPPR_MODIFY:
        self.logger.info("port modified %s", port_no)
    else:
        self.logger.info("Illeagal port state %s %s", port_no, reason)

结束语

参考文献

RYU入门教程

备注

ryu官方:https://github.com/faucetsdn/ryu
本人注释版:https://github.com/leeshy-tech/ryu