软件定义网络实验六

实验6:开源控制器实践——RYU

第一部分:基本实验

实验步骤1

  • 步骤内容:完成Ryu控制器的安装。

实验步骤2

  • 步骤内容:搭建下图所示SDN拓扑,协议使用Open Flow 1.0,并连接Ryu控制器。
    sudo mn --topo=single,3 --mac --controller=remote,ip=127.0.0.1,port=6633 --switch ovsk,protocols=OpenFlow10建立拓扑

实验步骤3

  • 步骤内容:通过Ryu的图形界面查看网络拓扑。
    首先在存放有ryu的文件目录内运行终端,并输入命令:ryu-manager ryu/ryu/app/gui_topology/gui_topology.py --observe-links连接ryu控制器
    在浏览器中输入地址http://127.0.0.1:8080即可打开ryu的图形界面
    查看到的网络拓扑如下:

实验步骤4

  • 步骤内容:阅读Ryu文档的The First Application一节,运行并使用 tcpdump 验证L2Switch,分析和POX的Hub模块有何不同。
  • 编写以下代码,命名为L2Switch.py,存放于ryu/ryu/app文件夹内,终端运行命令:ryu-manager L2Switch.py
    执行结果

h1 ping h2

h1 ping h3

  • 通过mininet命令行输入命令:dpctl dump-flows分别查看L2Switch模块及Hub模块下控制器流表信息

  • L2Switch

  • Hub

不同点:POX的下发流表可以查看,L2Switch的下发流表无法查看。

第二部分:进阶实验

实验内容:阅读Ryu关于simple_switch.py和simple_switch_1x.py的实现,以simple_switch_13.py为例,完成其代码的注释工作,并回答下列问题:
注释结果如下:

Copyright (C) 2011 Nippon Telegraph and Telephone Corporation.

Licensed under the Apache License, Version 2.0 (the "License");

you may not use this file except in compliance with the License.

You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software

distributed under the License is distributed on an "AS IS" BASIS,

WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or

implied.

See the License for the specific language governing permissions and

limitations under the License.

引入数据包

from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.ofproto import ofproto_v1_3
from ryu.lib.packet import packet
from ryu.lib.packet import ethernet
from ryu.lib.packet import ether_types

class SimpleSwitch13(app_manager.RyuApp): #定义openflow版本
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]

def init(self, args, **kwargs):
super(SimpleSwitch13, self).init(
args, **kwargs)
# 定义保存mac地址到端口的映射
self.mac_to_port = {}

@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
def switch_features_handler(self, ev):
datapath = ev.msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser

install table-miss flow entry

We specify NO BUFFER to max_len of the output action due to

OVS bug. At this moment, if we specify a lesser number, e.g.,

128, OVS will send Packet-In with invalid buffer_id and

truncated packet data. In that case, we cannot output packets

correctly. The bug has been fixed in OVS v2.1.0.

match = parser.OFPMatch()
actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER,
ofproto.OFPCML_NO_BUFFER)]
self.add_flow(datapath, 0, match, actions)

def add_flow(self, datapath, priority, match, actions, buffer_id=None):
#增加流表项
# 获取交换机信息
ofproto = datapath.ofproto
parser = datapath.ofproto_parser

inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS,actions)]
if buffer_id:
mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id,priority=priority, match=match,instructions=inst)
else:
mod = parser.OFPFlowMod(datapath=datapath, priority=priority,
match=match, instructions=inst)
datapath.send_msg(mod)

触发Packet_In事件时调用_packet_in_handler函数

@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def _packet_in_handler(self, ev):
# If you hit this you might want to increase
# the "miss_send_length" of your switch
if ev.msg.msg_len < ev.msg.total_len:
self.logger.debug("packet truncated: only %s of %s bytes",
ev.msg.msg_len, ev.msg.total_len)
msg = ev.msg
datapath = msg.datapath
ofproto = datapath.ofproto
parser = datapath.ofproto_parser
in_port = msg.match['in_port']

pkt = packet.Packet(msg.data)
eth = pkt.get_protocols(ethernet.ethernet)[0]

忽略LLDP类型

if eth.ethertype == ether_types.ETH_TYPE_LLDP:
# ignore lldp packet
return
dst = eth.dst
src = eth.src

dpid = format(datapath.id, "d").zfill(16)
self.mac_to_port.setdefault(dpid, {})

self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port)

了解mac地址以避免下次泛滥。

self.mac_to_port[dpid][src] = 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

actions = [parser.OFPActionOutput(out_port)]

安装一个流以避免下次出现数据包_

if out_port != ofproto.OFPP_FLOOD:
match = parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src)
# 验证是否有有效的缓冲区id,如果是,则避免同时发送这两个id
# 流量模块和数据包输出
if msg.buffer_id != ofproto.OFP_NO_BUFFER:
self.add_flow(datapath, 1, match, actions, msg.buffer_id)
return
else:
self.add_flow(datapath, 1, match, actions)
data = None
if msg.buffer_id == ofproto.OFP_NO_BUFFER:
data = msg.data

out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id,
in_port=in_port, actions=actions, data=data)
datapath.send_msg(out) #发送流表

a) 代码当中的mac_to_port的作用是什么?
保存mac地址到交换机端口的映射,为交换机自学习功能提供数据结构进行mac端口的存储

b) simple_switch和simple_switch_13在dpid的输出上有何不同
可见在simple_switch_13中,会在前端加上0以填充至16位,simple_switch直接输出dpid

c) 相比simple_switch,simple_switch_13增加的switch_feature_handler实现了什么功能?
实现了交换机以特性应答消息响应特性请求

d) simple_switch_13是如何实现流规则下发的?
在接收到packetin事件后,首先获取包学习,交换机信息,以太网信息,协议信息等。如果以太网类型是LLDP类型,则不予处理。如果不是,则获取源端口目的端口,以及交换机id,先学习源地址对应的交换机的入端口,再查看是否已经学习目的mac地址,如果没有则进行洪泛转发。如果学习过该mac地址,则查看是否有buffer_id,如果有的话,则在添加流动作时加上buffer_id,向交换机发送流表

e) switch_features_handler和_packet_in_handler两个事件在发送流规则的优先级上有何不同?
switch_features_handler下发流表的优先级相对较高

第三部分:反思与总结

  • 实验难度:相较于前几次实验较难。

  • 实验过程遇到的困难及解决方法
    首先是在ryu图形界面下查看网络拓扑过程中,ryu打开失败了好几次,多次尝试才知道打开的目录出错,需要在包含ryu的文件目录内打开才能成功。
    还有一点就是验证L2Switch模块的步骤,h1,h2,h3没有相互连接,通过百度一直没法解决,最后通过请教同学,才知道需要先开启ryu,再建立拓扑,才能相互连通。在这个问题上被困了很久。
    注释问题上,对于理解代码有着一定的困难,文件里有了一定的英文注释了,通过百度翻译,和对代码的查询,才完成对代码的注释,了解了代码的功能。

  • 个人感想
    通过本次实验,对ryu的使用有了初步的了解认识,对L2Wwitch模块的洪泛转发功能也有了一定的了解,并且也能够区分和Hub模块的不同之处。对于sdn这门课有了更深的认识,通过注释,对python也得到了一定的学习。

posted @ 2021-10-17 17:52  fzux  阅读(197)  评论(0)    收藏  举报