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

32 篇文章 61 订阅
31 篇文章 2 订阅

实验目标:

(1)实现网络拓扑的各个主机之间的相互通信

(2)实现各个自治域的控制器的流表获取(各个控制器的流表信息互通)

(3)实现应用层对控制器的控制,进而实现对整个网络的控制

大致的实现效果如下:

 实验原理:

利用mininet进行拓扑的模拟,构建多个控制器组成的网络。由于SDN网络留有北向接口,所以利用北向接口就可以对网络的流表等信息进行获取,在ryu中,ofctl_rest.py即是restapi的实现。本次实验,通过在docker中运行ryu控制器,同时运行ofctl_rest.py和simple_switch_13.py,然后利用python程序的get()、post()等方法对控制器的流表信息进行获取和添加,这样就实现了对流表信息的操作,同时也可以实现多个控制器流表信息的互通(公用一块公共的存储空间,如数据库),但是本次只演示基本的获取和流表的基本添加删除等操作。

实验拓扑构建:

工具:mininet进行拓扑搭建,Docker作为控制器

mininet构建实验拓扑,模拟自治域AS1和AS2,r4和r5作为自治域的边界路由器,实现域间路由。每个自治域由一台控制器控制(远端控制器最终运行在Docker上),若干台主机组成。最终通过配置路由表,实现域间的通信,实现整个网络架构各个主机之间的通信即为拓扑搭建完成。如图所示。

 主机、交换机、控制器设置如下图所示。

 各项设置完成无误后,运行网络拓扑。

路由表和Docker ryu配置:

(1)Docker ryu配置

关于Docker镜像ymumu/ryu:0.1:

可以参考文章(Docker命令、基于Docker的SDN实验环境部署(1)_北风-CSDN博客

使用如下命令进行镜像ymumu/ryu的安装:

docker pull ymumu/ryu:0.1

新建两个Docker并且进行空间挂载,分别运行两个自治域的控制器,命令如下。

root@ymumu-VirtualBox:/home/ymumu# docker images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
ymumu/ryu    0.1       edb38c9e5a5c   3 weeks ago   719MB
root@ymumu-VirtualBox:/home/ymumu# docker run -it --name as1-ryu1 -v /home/ymumu/ryu/ryu/app:/home/ryu/ryu/app ymumu/ryu:0.1 /bin/bash

root@ymumu-VirtualBox:/home/ymumu# docker run -it --name as2-ryu1 -v /home/ymumu/ryu/ryu/app:/home/ryu/ryu/app ymumu/ryu:0.1 /bin/bash

以simple_switch_13为例,接下来,在各个Docker中开启ryu,命令步骤如下所示。其中,ofctl_rest.py是开启控制器的北向接口,simple_switch_13.py作为自学习交换机。

以上完成后,各个自治域内部的各个主机之间就可以通过自学习交换机进行通信,而不可以进行跨域通信,接下来就需要设置路由器和路由表。

(2)路由表配置

 首先,设置两个路由器各个端口ip,命令如下。

mininet> r4 ip addr add 10.0.1.101/24 dev r4-eth0
mininet> r4 ip addr add 10.0.1.102/24 dev r4-eth1
mininet> r4 ip addr add 10.0.3.1/24 dev r4-eth2
mininet> r5 ip addr add 10.0.2.101/24 dev r5-eth0
mininet> r5 ip addr add 10.0.3.2/24 dev r5-eth1

接下来,设置路由器的路由表,命令如下。

# 添加路由表
mininet> r4 ip route add 10.0.2.0/24 via 10.0.3.2 dev r4-eth2 
mininet> r5 ip route add 10.0.1.0/24 via 10.0.3.1 dev r5-eth1 
# 查看路由器中的路由表
mininet> r4 ip r
10.0.1.0/24 dev r4-eth0 proto kernel scope link src 10.0.1.101 
10.0.1.0/24 dev r4-eth1 proto kernel scope link src 10.0.1.102 
10.0.2.0/24 via 10.0.3.2 dev r4-eth2 
10.0.3.0/24 dev r4-eth2 proto kernel scope link src 10.0.3.1 
mininet> r5 ip r
10.0.1.0/24 via 10.0.3.1 dev r5-eth1 
10.0.2.0/24 dev r5-eth0 proto kernel scope link src 10.0.2.101 
10.0.3.0/24 dev r5-eth1 proto kernel scope link src 10.0.3.2 

然后,向各个主机添加路由表,为了简单起见,直接在各个主机中添加默认路由表,命令如下。

# 向as1-h1添加默认路由表
ip route add default via 10.0.1.101 dev as1-h1-eth0
# 向as1-h2添加默认路由表
ip route add default via 10.0.1.101 dev as1-h2-eth0
# 向as1-h3添加默认路由表
ip route add default via 10.0.1.102 dev as1-h3-eth0
# 向as2-h1添加默认路由表
ip route add default via 10.0.2.101 dev as2-h1-eth0
# 向as2-h2添加默认路由表
ip route add default via 10.0.2.101 dev as2-h2-eth0

以上所有路由表设置完成,网络中各个主机之间就可以相互通信了。

应用层编码

(1)原理

ryu控制器留有北向接口,即ofctl_rest.py,其通过restapi向应用层提供接口,应用层可以通过接口获取流表等信息并对流表进行操作。关于ryu的应用层接口的api使用,可以参考ryu docs:ryu.app.ofctl_rest — Ryu 4.34 documentation

通过文档可以知道,使用get()方法可以进行流表等信息的获取,使用post()方法可以实现流表等的下发操作,使用delete()方法可以实现流表等信息的删除。因此,使用pytho的get()等方法进行应用层程序的编写。

(2)获取流表

根据ryu docs中获取流表的方法为get(),url=http://ip:port/stats/flow/dpid,所以首先需要获取转发层面的控制器的dpid,获取dpid的url=http://ip:port/stats/switches,所以利用get()方法获取dpid,再根据dpid获取所有的流表,最后将流表显示出来。

    # 通过get()获取交换机的dpid,并将其转化为16进制存入列表
    def get_switch_id(self):
        url = 'http://' + self.ip + ':' + self.port + '/stats/switches'
        re_switch_id = requests.get(url=url).json()
        switch_id_hex = []
        for i in re_switch_id:
            switch_id_hex.append(hex(i))

        return switch_id_hex

    # 通过get()和获取的dpid得到每一个交换机的流表项
    def get_flow_table(self):
        url = 'http://' + self.ip + ':' + self.port + '/stats/flow/%d'
        list_switch = self.get_switch_id()
        all_flow = []
        for switch in list_switch:
            new_url = format(url % int(switch, 16))
            re_switch_flow = requests.get(url=new_url).json()
            all_flow.append(re_switch_flow)

        return all_flow

(3)下发流表操作(添加、删除、清空)

下发流表操作的方法使用post(),对于流表的添加,在ryu docs中使用的api的url=http://ip:port/stats/flowentry/add。然后,发送的信息包括,dpid,cookie,in_port在内的各项流表项信息。添加流表项的程序如下。

    # 向控制器发送添加流表项的请求
    def post_add_flow(self, dpid=None, cookie=0, priority=0, in_port=1, type='OUTPUT', port='CONTROLLER'):
        url = 'http://' + self.ip + ':' + self.port + '/stats/flowentry/add'
        if in_port == 'None':
            # 添加的默认流表项数据信息
            data = {
                "dpid": dpid,
                "cookie": cookie,
                "cookie_mask": 0,
                "table_id": 0,
                "priority": priority,
                "flags": 0,
                "actions": [
                    {
                        "type": type,
                        "port": port
                    }
                ]
            }
        else:
            in_port = int(in_port)
            data = {
                "dpid": dpid,
                "cookie": cookie,
                "cookie_mask": 0,
                "table_id": 0,
                "priority": priority,
                "flags": 0,
                "match": {
                    "in_port": in_port
                },
                "actions": [
                    {
                        "type": type,
                        "port": port
                    }
                ]
            }
        
        # 如果正确添加,则返回200OK
        response = requests.post(url=url, json=data)
        if response.status_code == 200:
            print('Successfully Add!')
        else:
            print('Fail!')

删除流表项,即通过发送匹配信息,删除和指令匹配的流表项,在ryu docs中的url=http://ip:port/stats/flowentry/delete_strict,然后,发送的信息包括,dpid,cookie,in_port在内的各项流表项匹配信息。删除流表项的程序如下。

    def post_del_flow(self, dpid=None, cookie=0, priority=0, in_port=1, type='OUTPUT', port='CONTROLLER'):
        url = url = 'http://' + self.ip + ':' + self.port + '/stats/flowentry/delete_strict'
        data = {
            "dpid": dpid,
            "cookie": cookie,
            "cookie_mask": 1,
            "table_id": 0,
            "priority": priority,
            "flags": 1,
            "match": {
                "in_port": in_port,
            },
            "actions": [
                {
                    "type": type,
                    "port": port
                }
            ]
        }

        response = requests.post(url=url, json=data)
        if response.status_code == 200:
            print('Successfully Delete!')
        else:
            print('Fail!')

对于清空流表项,在ryu docs中给出的例子,只需要用delete()方法向控制器请求特定url即可清空指定的交换机的流表项。程序如下。

    def post_clear_flow(self, dpid=None):
        url = 'http://' + self.ip + ':' + self.port + '/stats/flowentry/clear/' + str(dpid)
        response = requests.delete(url=url)
        if response.status_code == 200:
            print('Successfully Clear!')
        else:
            print('Fail!')

接下来,就是主函数的编写,调用各个方法实现目标,这里不再叙述,文末有完整源码链接。

实验效果:

实验演示和操作如视频所示。

ryu_operation_哔哩哔哩_bilibili

github源码地址:https://github.com/Yang-Jianlin/ryu/tree/master/ryu/app/ryu_restapi_operation

以上就是全部内容,如有疑问请私信或留言,谢谢。

  • 8
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

楊木木8023

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值