ryu---北向接口(利用socket对外通信)

Posted 楊木木8023

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ryu---北向接口(利用socket对外通信)相关的知识,希望对你有一定的参考价值。

一、原理

最近有人问ryu控制器如何实现与应用层(如web、app等)的通信,实际上,实现通信就需要利用SDN的北向接口,关于SDN的北向接口,各种文章的解释不一,难以理解。实质上,北向接口实现的功能就是和两个软件实体之间的通信无任何区别,也就是说,北向接口不需要任何新的协议,只要会写简单的socket编程就能轻易实现应用层与控制层之间的通信。下面我介绍一下如何使用python的socket编程实现控制器和应用层之间的通信。

关于python socket编程参考:https://blog.csdn.net/weixin_40042248/article/details/114779625?spm=1001.2014.3001.5501

二、网路拓扑及实施原理

这个实验,仅仅演示控制器与应用层如何通信,所以为了便于理解,本次实验以hub为例,也就是利用ryu实现hub,然后获取每次通信的dpid和port传输给应用层。拓扑结构如下。

 三、实验代码编写

本次实验,数据转发层面和控制层面都是在Ubuntu虚拟机下面的mininet进行拓扑仿真的,在ryu运行编写好的程序,在宿主机中运行编写好的server端程序。

(1)server程序编写

server端主要用于接收控制器的连接,并接收由控制器发送过来的信息,这里主要是dpid和port信息,也就是当主机h1、h2进行通信时,控制器收集通信时经过的端口和交换机id。

代码如下:

import socket
import re


def main():
    server1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # TCP

    host = '10.50.177.208'
    port = 12345
    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程序编写

本次实验以简单的hub为基础,在hub中写入client端程序实现控制器和应用层的通信。

代码如下:

from ryu.base import app_manager
from ryu.controller.handler import set_ev_cls
from ryu.controller.handler import MAIN_DISPATCHER, CONFIG_DISPATCHER
from ryu.controller import ofp_event
from ryu.ofproto import ofproto_v1_3
import socket


class L2Switch(app_manager.RyuApp):
    OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.dp_id = '0'
        self.in_port = '0'
        
        # 开启client,并连接server
        self.client1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        dst_host = '10.50.177.208'
        dst_port = 12345
        # 防止server端连接不上影响hub的使用
        try:
            self.client1.connect((dst_host, dst_port))
        except Exception as error:
            print('Connect error:', error)

    def doflow(self, datapath, command, priority, match, actions):
        ofp = datapath.ofproto
        ofp_parser = datapath.ofproto_parser
        inst = [ofp_parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions)]
        req = ofp_parser.OFPFlowMod(datapath=datapath, command=command,
                                    priority=priority, match=match, instructions=inst)
        datapath.send_msg(req)
    
    # 控制器和交换机握手时,向交换机下发默认流表项
    @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
    def switch_features_handler(self, ev):
        msg = ev.msg
        datapath = msg.datapath
        ofp = datapath.ofproto
        ofp_parser = datapath.ofproto_parser

        # add table-miss
        command = ofp.OFPFC_ADD
        match = ofp_parser.OFPMatch()
        actions = [ofp_parser.OFPActionOutput(ofp.OFPP_CONTROLLER, ofp.OFPCML_NO_BUFFER)]
        self.doflow(datapath, command, 0, match, actions)

    @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
        self.dp_id = dp.id

        # 计算出in_port
        start = str(msg).index('oxm_fields') + 11
        end = str(msg).index('),reason')
        inport_str = str(msg)[start:end]
        instr = eval(inport_str)
        self.in_port = instr['in_port']

        actions = [ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD)]

        data = None
        if msg.buffer_id == ofp.OFP_NO_BUFFER:
             data = msg.data
        print('id:0     in_port:1'.format(self.dp_id, self.in_port))
        # 每次有信息经过交换机时,控制器就将获取的dpid和port发送给server
        info = str(self.dp_id) + ',' + str(self.in_port)
        self.client1.send(info.encode())

        out = ofp_parser.OFPPacketOut(
            datapath=dp, buffer_id=msg.buffer_id, in_port=self.in_port,
            actions=actions, data=data)
        dp.send_msg(out)

 四、运行实验

 (1) 在mininet中构建完成拓扑后,运行拓扑。

 (2) 在宿主机中,运行server端,本次实验直接在win下的pycharm编写server端运行。

 (3) 启动ryu程序,如图所示。

 ryu启动之后,就可以在server端控制台看见输出连接成功。

 (4) 接下来,在mininet命令行输入h1 ping h2,此时可以发现server端输出了dpid和port信息,同时ryu也打印出了信息。

 

实验完成,可以发现实验实现了控制层和应用层的简单通信,原理非常简单。

GitHub源码地址:

https://github.com/Yang-Jianlin/ryu/blob/master/ryu/app/north_socket_yjl.py

https://github.com/Yang-Jianlin/ryu/blob/master/ryu/app/server.py

以上是关于ryu---北向接口(利用socket对外通信)的主要内容,如果未能解决你的问题,请参考以下文章

ryu--北向接口(流表的操作以及多控制器流表信息互通)

ryu--北向接口(流表的操作以及多控制器流表信息互通)

SDN实验---Ryu的应用开发北向接口RESTAPI

[FFH]openharmony南向研究 - 南北向接口Napi实现

基于REST API的SDN北向应用实践

Java网络通信-Socket初探