赞
踩
在SDN三层结构中,我们通过OpenFlow 协议可以控制数据转发设备的相关行为(包括收集设备的信息),那么控制器上的数据能否通过应用层的程序进行管理调用呢?
SDN(软件定义网络)的北向开发是指通过编写应用程序或API来与SDN控制器进行交互,以实现网络管理、配置和控制性。实际上就是解决:ryu控制器如何实现与应用层(如web、app等)的通信,利用SDN的北向接口的功能就是和其他软件实体之间的通信。
本文将展示控制器与应用层如何通信,所以为了便于理解,本次以上博文中编写的控制器为例子(packet_statics.py)结合mininet模块创建的网络拓扑(3_2_topo.py),然后在另外一个应用程序中开启数据收集(另外一个应用进程app_server.py),获取在SDN环境中每次通信的dpid和port,并传输给应用层(app_server.p),开发环境结构如下。
图1 框架
数据转发层面和控制层面都是在Ubuntu虚拟机下面开发,在ryu环境中运行编写好的程序,在宿主机(本文也放在ubuntu)中运行编写好的server端程序。
(1)server程序编写
server端主要用于接收控制器的连接,并接收由控制器发送过来的信息(如dpid和port信息),也可以收集控制器其他的数据,原理一样。
import socket import re def main(): server1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # TCP host = '127.0.0.1' # ubuntu地址,开启服务功能,用来给ryu主动连接提交数据 port = 12345 # 开放端口,自主定义一般大于1024 server1.bind((host, port)) server1.listen(5) while True: conn, addr = server1.accept() print("----------------------------") print("Success connect from ", addr) try: count = 0 while True: data = conn.recv(1024) data = re.split(r'[, :]', data.decode('utf-8')) # 对收到的信息进行解析,包括dpid和port count += 1 print("from {0}:dpid={1}, in_port={2}".format(addr, data[0], data[1])) conn.close() except Exception as error: # 当控制器和应用层断开连接后,输出统计信息 print('共接收{}条信息。'.format(count - 1)) print(error) exit() if __name__ == '__main__': main()
(2)ryu控制器程序
from ryu.base import app_manager from ryu.controller import ofp_event from ryu.controller.handler import set_ev_cls from ryu.controller.handler import MAIN_DISPATCHER, CONFIG_DISPATCHER from ryu.ofproto import ofproto_v1_3 from ryu.lib.packet import packet, ethernet, ipv6, icmp, icmpv6 import socket class L2Switch(app_manager.RyuApp): def __init__(self, *args, **kwargs): super(L2Switch, self).__init__(*args, **kwargs) self.mac_port_table = {} self.protocol_stats = {} # 开启client,并连接server self.client1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) dst_host = '127.0.0.1' dst_port = 12345 # 防止server端连接不上影响hub的使用 try: self.client1.connect((dst_host, dst_port)) except Exception as error: print('Connect error:', error) @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 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): ofproto = datapath.ofproto parser = datapath.ofproto_parser inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, actions)] mod = parser.OFPFlowMod(datapath=datapath, priority=priority, match=match, instructions=inst) datapath.send_msg(mod) @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def packet_in_handler(self, ev): msg = ev.msg dp = msg.datapath ofp = dp.ofproto ofp_parser = dp.ofproto_parser in_port = msg.match['in_port'] dpid = dp.id print(dpid) pkt = packet.Packet(msg.data) icmp_pkt = pkt.get_protocol(icmp.icmp) icmp6_pkt = pkt.get_protocol(icmpv6.icmpv6) if not icmp_pkt and not icmp6_pkt: self.mac_port_table.setdefault(dpid, {}) pkt = packet.Packet(msg.data) eth_pkt = pkt.get_protocols(ethernet.ethernet)[0] dst = eth_pkt.dst src = eth_pkt.src self.mac_port_table[dpid][src] = in_port if dst in self.mac_port_table[dpid]: out_port = self.mac_port_table[dpid][dst] else: out_port = ofp.OFPP_FLOOD actions = [ofp_parser.OFPActionOutput(out_port)] if out_port != ofp.OFPP_FLOOD: match = ofp_parser.OFPMatch(in_port=in_port, eth_dst=dst, eth_src=src) if msg.buffer_id != ofp.OFP_NO_BUFFER: self.add_flow(dp, 1, match, actions, msg.buffer_id) return else: self.add_flow(dp, 1, match, actions) data = None if msg.buffer_id == ofp.OFP_NO_BUFFER: data = msg.data out = ofp_parser.OFPPacketOut(datapath=dp, buffer_id=msg.buffer_id, in_port=in_port, actions=actions, data=data) dp.send_msg(out) # send to server info = str(dpid) + ',' + str(in_port) self.client1.send(info.encode()) #把信息传送到应用程序端
(3) 转发层程序
转发层层序主要是负责网络拓扑环境搭建
from mininet.net import Mininet from mininet.node import OVSSwitch, Host from mininet.cli import CLI from mininet.link import Link from mininet.node import RemoteController #import networkx as nx #import matplotlib.pyplot as plt class NoIPv6Host(Host): def config(self, **kwargs): super(NoIPv6Host, self).config(**kwargs) self.cmd('sysctl -w net.ipv6.conf.all.disable_ipv6=1') self.cmd('sysctl -w net.ipv6.conf.default.disable_ipv6=1') def create_network(): net = Mininet(host=NoIPv6Host) # 创建单个OVS交换机 switch1 = net.addSwitch('s1', cls=OVSSwitch,protocols='OpenFlow13') switch2 = net.addSwitch('s2', cls=OVSSwitch,protocols='OpenFlow13') switch3 = net.addSwitch('s3', cls=OVSSwitch,protocols='OpenFlow13') switch4 = net.addSwitch('s4', cls=OVSSwitch,protocols='OpenFlow13') switch5 = net.addSwitch('s5', cls=OVSSwitch,protocols='OpenFlow13') # 创建2个主机 host1 = net.addHost('h1', cls=Host, ip='192.168.0.1/24', defaultRoute='via 192.168.0.254') host2 = net.addHost('h2', cls=Host, ip='192.168.0.2/24', defaultRoute='via 192.168.0.254') host3 = net.addHost('h3', cls=Host, ip='192.168.0.3/24', defaultRoute='via 192.168.0.254') host4 = net.addHost('h4', cls=Host, ip='192.168.0.4/24', defaultRoute='via 192.168.0.254') # 连接主机到交换机 net.addLink(host1, switch1) net.addLink(host2, switch5) net.addLink(host3, switch5) net.addLink(host4, switch5) #交换机连接交换机 net.addLink(switch1, switch2) net.addLink(switch2, switch5) net.addLink(switch1, switch3) net.addLink(switch3, switch5) net.addLink(switch1, switch4) net.addLink(switch4, switch5) # 指定控制器的IP地址和端口 #可以先使用ss -tlnp | grep ryu-manager查看ryu运行后的监听端口 controller_ip = '127.0.0.1' controller_port = 6633 # 创建Mininet网络,并指定控制器和OpenFlow协议版本 net.addController('controller', controller=RemoteController, ip=controller_ip, port=controller_port,protocols='OpenFlow13') # 启动网络 net.start() # 打开命令行界面 CLI(net) # 关闭网络 net.stop() if __name__ == '__main__': create_network()
(4)实验测试
按照以下顺序:
1) 运行app_server.py 应用程序监听
2)运行RYU控制器,观察与app_server.py 应用连接
3)运行转发层网络
可以观察到数据之间的交互
图2 app_server 数据接收
图3 packet_statics控制器运行后,数据转发层面截图
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。