DN功能实现(七)---修改OVS源码实现ACK机制(控制器-交换机用户态-交换机内核态通信)

本文主要是介绍DN功能实现(七)---修改OVS源码实现ACK机制(控制器-交换机用户态-交换机内核态通信),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

(一)系统部分架构说明

如图所示,其中s1\s4是边缘交换机,s1直接与发送方相连,作为近接受方交换机存在,s4直接与接受方相连,作为近接受方交换机存在.

为了使目的终端获取得到唯一的\可靠\实时传输的UDP数据包,源终端主机发送的数据经由近接收方交换机s1,若流表中没有匹配项,则发送Pakcet-in消息给控制器,控制器获取rudp数据后,通过拓扑发现和时延探测模块获取网络全局拓扑结构,预选取两条节点不相关的主\备份路径{s1-s2-s3-s4,s1-s5-s6-s4}和关键节点{s1,s2,s4,s6},下发对应流表项.数据包在时延最优路径{s1-s2-s3-s4}中进行传输,经过关键节点{s1,s2,s4}时,需要触发ACK报文处理机制,各个关键节点通过控制器进行ACK报文的回送和确认,以保障数据传输的可靠性,减少由于路径故障导致的丢包问题.

其中若是关键节点之间的链路故障\拥塞导致数据包无法实时到达下一个关键节点,则通过内部定时机制或下一个关键节点回送的缺失类型ACK决定对丢失数据包的重传,重传包括原关键节点同时向主路径端口和备份路径端口进行转发,以提高数据传输的实时性和可靠性.如图所示,对于关键节点s2转发数据包{packet1,packet2,packet3}在链路{s2-s3-s4},其中packet2由于链路{s2-s3}故障导致丢失,因此在下一个关键节点s4中无法接收到packet2,通过发送ack报文,请求前一个关键节点进行数据的重传,在关键节点s2接受到重传ack后,从缓存队列中获取对应的数据包,通过端口分别向主路径进行重传和备份路径进行转发,同样关键节点s2内部的定时机制也可以触发重传机制.

(二)ACK机制

补充:RUDP数据报文字段如下图,通过对FrontDPID和PacketNumber字段数据的获取,用于支持ACK机制的实现.

其中各个字段属性如下:

在系统实现中,关键节点交换机(如S4)使用了数据缓存队列缓存所有需要可靠传输服务的RUDP数据包,同时通过开启内核线程实现定时检测队列,如果有新的数据达到,则对最新的数据的包序号进行ACK转发,从内核态通过Generic Netlink机制上传至用户态,用户态通过openflow协议传输至控制器,控制器通过获取的包序号和前一个关键节点交换机(S2)信息,将ACK报文通过OpenFlow协议转发至S2交换机用户空间,S2同样通过Generic Netlink转发至内核态,通过对ACK数据解析,S2获取得到S4中包接收的顺序,因此可以释放数据队列中已经确认的ACK序号前面的所有数据空间,对于丢失的数据,同样通过ACK可以实现重传和转发.

本文主要介绍ACK报文在两个交换机的内核态-用户态-控制器-用户态-内核态之间的转发过程.

其中Generic Netlink学习,可以看前面转载的几篇博客!!!

二:扩展OpenFlow协议,实现控制器和交换机用户态通信

(一)Ryu源码修改

1.修改ofproto/ofproto_v1_3.py,声明新的OpenFlow消息类型

OFPT_GET_ASYNC_REQUEST = 26 # Controller/switch message
OFPT_GET_ASYNC_REPLY = 27 # Controller/switch message
OFPT_SET_ASYNC = 28 # Controller/switch messageOFPT_METER_MOD = 29 # Controller/switch message#与前面的消息类型编码不一致(唯一即可)
OFPT_ACK_REQUEST = 32               # Symmetric message
OFPT_ACK_REPLY = 33                 # Symmetric message

2.修改ofproto/ofproto_v1_3_parser.py,实现对新的ACK消息支持

@_register_parser
@_set_msg_type(ofproto.OFPT_ACK_REQUEST)
class OFPACKRequest(MsgBase):def __init__(self, datapath, data=None):super(OFPACKRequest, self).__init__(datapath)self.data = data@classmethoddef parser(cls, datapath, version, msg_type, msg_len, xid, buf):msg = super(OFPACKRequest, cls).parser(datapath, version, msg_type,msg_len, xid, buf)msg.data = msg.buf[ofproto.OFP_HEADER_SIZE:]return msgdef _serialize_body(self):if self.data is not None:self.buf += self.data@_register_parser
@_set_msg_type(ofproto.OFPT_ACK_REPLY)
class OFPACKReply(MsgBase):def __init__(self, datapath, data=None):super(OFPACKReply, self).__init__(datapath)self.data = data@classmethoddef parser(cls, datapath, version, msg_type, msg_len, xid, buf):msg = super(OFPACKReply, cls).parser(datapath, version, msg_type,msg_len, xid, buf)msg.data = msg.buf[ofproto.OFP_HEADER_SIZE:]return msgdef _serialize_body(self):assert self.data is not Noneself.buf += self.data

3.完成1,2步骤后记得进行Ryu源码的重新编译!步骤在前面的SDN实现中

4.实现Ryu App应用对新的ACK消息进行转发

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 MAIN_DISPATCHER,CONFIG_DISPATCHER,DEAD_DISPATCHER,HANDSHAKE_DISPATCHER #只是表示datapath数据路径的状态
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
from ryu.lib.packet import udp
from ryu.lib.packet import ipv4
from struct import packRUDP_MAGIC = 38217class RudpTrans(app_manager.RyuApp):OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]def __init__(self, *args, **kwargs):super(RudpTrans, self).__init__(*args, **kwargs)self.mac_to_port = {}self.datapaths = {}self.initFlag = False@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)def switch_features_handler(self, ev):datapath = ev.msg.datapathself.datapaths[datapath.id] = datapathofproto = datapath.ofprotoparser = 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.ofprotoparser = datapath.ofproto_parserinst = [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)def _send_ack_request(self,dp_id,hash_id,packet_number,type_id):"""发送echo_time报文到datapath"""    print("===============_send_ack_request========start============")print("front dpid / hash id / packet number / type id : ",dp_id, hash_id, packet_number, type_id)datapath = self.datapaths.get(dp_id,None)if datapath == None:returnparser = datapath.ofproto_parserprint("========%d"%packet_number);ack_req = parser.OFPACKRequest(datapath,data=pack("BBBI",dp_id,hash_id,type_id,packet_number)) datapath.send_msg(ack_req)print("==========_send_ack_request=========end=====")@set_ev_cls(ofp_event.EventOFPACKReply,[MAIN_DISPATCHER,CONFIG_DISPATCHER,HANDSHAKE_DISPATCHER])def ack_reply_handler(self,ev):"""处理ACK响应报文,获取控制器到交换机的链路往返时延"""print("==================OFPT_ACK_REPLY============start===========")print(ev.msg.data)info = ev.msg.datafront_dpid = int.from_bytes(info[0:1],'little')hash_id = int.from_bytes(info[1:2],'little')packet_id = int.from_bytes(info[2:6],'little')type_id = int.from_bytes(info[6:7],'little')self._send_ack_request(front_dpid,hash_id,packet_id,type_id)  print("==================OFPT_ACK_REPLY===========end============") 

(免费订阅,永久学习)学习地址: Dpdk/网络协议栈/vpp/OvS/DDos/NFV/虚拟化/高性能专家-学习视频教程-腾讯课堂

更多DPDK相关学习资料有需要的可以自行报名学习,免费订阅,永久学习,或点击这里加qun免费
领取,关注我持续更新哦! ! 

(二)OVS源码修改,实现对新的OpenFlow消息支持

1.修改include/openvswitch/ofp-msgs.h下的enum ofptype和enum ofpraw枚举类型

enum ofptype {OFPTYPE_HELLO, /* OFPRAW_OFPT_HELLO. */OFPTYPE_ERROR, /* OFPRAW_OFPT_ERROR. ......OFPTYPE_ACK_REQUEST, /* OFPRAW_OFPT_ACK_REQUEST */OFPTYPE_ACK_REPLY, /* OFPRAW_OFPT_ACK_REPLY */......
}

注意:注释对应了下面ofpraw枚举的名称

enum ofpraw {....../* OFPT 1.1-1.3 (32): uint8_t[]. */OFPRAW_OFPT_ACK_REQUEST,/* OFPT 1.1-1.3 (33): uint8_t[]. */OFPRAW_OFPT_ACK_REPLY,......
}

注意:序号需要保持与Ryu中的消息序号一致!!!

2.修改在ofproto/ofproto.c文件中handle_flow函数下的handle_single_part_openflow方法,处理自定义的类型

static enum ofperr
handle_single_part_openflow(struct ofconn *ofconn, const struct ofp_header *oh,enum ofptype type)OVS_EXCLUDED(ofproto_mutex)
{switch (type) {/* OpenFlow requests. */......case OFPTYPE_ACK_REQUEST:// VLOG_INFO("--------------------OFPTYPE_ACK_REQUEST--------------------");return handle_ack_request(ofconn,oh);......case OFPTYPE_ACK_REPLY:return 0;......  
}

同样在该文件中实现handle_ack_request方法:

static enum ofperr
handle_ack_request(struct ofconn *ofconn, const struct ofp_header *oh)
{//不同的是,获取数据,发送消息给内核// VLOG_INFO("----handle_ack_request-------start----");if(!ack_init) return OFPERR_OFPAFC_INIT;if(!ack_oh || ack_oh != oh) ack_oh = oh;struct ofpbuf rq_buf = ofpbuf_const_initializer(oh, ntohs(oh->length));//rq_buf.data是我们的数据,但是前面8个字节是结构体ofp_header,所以我们获取真正的数据需要跳过这8个字节数据char *data = rq_buf.data;// uint32_t packetid = ack_parse_packet_number(data+8,rq_buf.size-8);uint8_t front_dpid = *(uint8_t *)(data + 8);uint8_t hash_id = *(uint8_t *)(data + 9);uint8_t typeid = *(uint8_t *)(data + 10);uint32_t packetid = *(uint32_t *)(data + 12);   //存在对齐问题,所以间隔2//通过以上操作,可以获取并解析出控制器转发的ACK消息数据// VLOG_INFO("--handle_ack_request---front dpid:%u--hash id:%u--packet id:%u- type:%u-------",front_dpid,hash_id,packetid,typeid);dpif_netlink_ack_reply(false,front_dpid,hash_id,packetid,typeid);return 0;
}

其中dpif_netlink_ack_reply方案用于将解析的数据通过Generic Netlink转发至内核态,将在下一章中进行详细介绍!

三:实现交换机内核态与用户态通信

(一)用户态主动与内核态通信

1.在内核态进行新的Generic Netlink套接字注册

(1)在datapath/linux/compat/include/linux/openvswitch.h中进行类型声明

/*     ACK.    */#define OVS_ACK_FAMILY     "ovs_ack"
#define OVS_ACK_VERSION 0x1enum ovs_ack_cmd {OVS_ACK_CMD_UNSPEC,OVS_ACK_CMD_ECHO,                          //用户发给内核,该命令将由内核进行处理OVS_ACK_CMD_SET,                           //用戶发送给内核,进行设置OVS_ACK_CMD_REPLY,                         //由内核发送响应给用户态,该命令将由用户态进行处理__OVS_ACK_CMD_MAX,
};
#define OVS_ACK_CMD_MAX (__OVS_ACK_CMD_MAX - 1)enum ovs_ack_attr {OVS_ACK_ATTR_UNSPEC = 0,OVS_ACK_ATTR_INIT,                        //flag标识初始化OVS_ACK_ATTR_VID,                        //8bits无符号整型OVS_ACK_ATTR_OUTPUT,                    //16bits无符号整型OVS_ACK_ATTR_TURNOUT,                    //16bits无符号整型OVS_ACK_ATTR_QUEUE_SIZE,                //16bits无符号整型OVS_ACK_ATTR_TIME_INTERVAL,                //16bits无符合整型OVS_ACK_ATTR_DEVSTATUS,                    //8bits无符号整型OVS_ACK_ATTR_HASH_ID,                    //8bits无符号整型OVS_ACK_ATTR_TYPE,                        //8bits无符号整型OVS_ACK_ATTR_NUMBER,                    //32bits无符号整型__OVS_ACK_ATTR_MAX,
};
#define OVS_ACK_ATTR_MAX (__OVS_ACK_ATTR_MAX - 1)

其中红色的字段与本次的ACK机制有关,其他的是关于其他功能的,这里不需要关心.

另外需要补充,datapath/linux/compat/include/linux/openvswitch.h在编译过程中会自动生成一个include/odp-netlink.h文件,我们在用户空间中使用这个头文件即可.

(2)在datapath/datapath.c中进行实现

Ⅰ.为模块添加Generic Netlink family信息

module_init(dp_init);
module_exit(dp_cleanup);MODULE_DESCRIPTION("Open vSwitch switching datapath");
MODULE_LICENSE("GPL");
MODULE_VERSION(VERSION);
MODULE_ALIAS_GENL_FAMILY(OVS_DATAPATH_FAMILY);
MODULE_ALIAS_GENL_FAMILY(OVS_VPORT_FAMILY);
MODULE_ALIAS_GENL_FAMILY(OVS_FLOW_FAMILY);
MODULE_ALIAS_GENL_FAMILY(OVS_PACKET_FAMILY);
MODULE_ALIAS_GENL_FAMILY(OVS_ACK_FAMILY);
MODULE_ALIAS_GENL_FAMILY(OVS_METER_FAMILY);
MODULE_ALIAS_GENL_FAMILY(OVS_CT_LIMIT_FAMILY);

Ⅱ.定义策略、操作集和family

static const struct nla_policy ack_policy[OVS_ACK_ATTR_MAX + 1] = {    [OVS_ACK_ATTR_INIT]            =    {.type = NLA_FLAG},[OVS_ACK_ATTR_VID]            =    {.type = NLA_U8},[OVS_ACK_ATTR_HASH_ID]        =    {.type = NLA_U8},[OVS_ACK_ATTR_NUMBER]        =    {.type = NLA_U32},[OVS_ACK_ATTR_TYPE]            =    {.type = NLA_U8},[OVS_ACK_ATTR_OUTPUT]        =    {.type = NLA_U16},[OVS_ACK_ATTR_TURNOUT]        =    {.type = NLA_U16},[OVS_ACK_ATTR_QUEUE_SIZE]        =    {.type = NLA_U16},[OVS_ACK_ATTR_TIME_INTERVAL]        =    {.type = NLA_U16},[OVS_ACK_ATTR_DEVSTATUS]    =    {.type = NLA_U8},
};                                                                        //其他的为默认,NLA_UNSPEC表示类型和长度未知static const struct genl_ops dp_ack_genl_ops[] = {{.cmd         =    OVS_ACK_CMD_ECHO,.doit         =     ovs_ack_cmd_execute,.policy     =     ack_policy,.flags         =    0,},{.cmd         =    OVS_ACK_CMD_SET,.doit         =     ovs_ack_cmd_set,.policy     =     ack_policy,.flags         =    0,},
};static struct genl_family dp_ack_genl_family __ro_after_init = {.id      =    0,                   

这篇关于DN功能实现(七)---修改OVS源码实现ACK机制(控制器-交换机用户态-交换机内核态通信)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/696710

相关文章

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

【Linux进阶】UNIX体系结构分解——操作系统,内核,shell

1.什么是操作系统? 从严格意义上说,可将操作系统定义为一种软件,它控制计算机硬件资源,提供程序运行环境。我们通常将这种软件称为内核(kerel),因为它相对较小,而且位于环境的核心。  从广义上说,操作系统包括了内核和一些其他软件,这些软件使得计算机能够发挥作用,并使计算机具有自己的特生。这里所说的其他软件包括系统实用程序(system utility)、应用程序、shell以及公用函数库等

通过SSH隧道实现通过远程服务器上外网

搭建隧道 autossh -M 0 -f -D 1080 -C -N user1@remotehost##验证隧道是否生效,查看1080端口是否启动netstat -tuln | grep 1080## 测试ssh 隧道是否生效curl -x socks5h://127.0.0.1:1080 -I http://www.github.com 将autossh 设置为服务,隧道开机启动

通信系统网络架构_2.广域网网络架构

1.概述          通俗来讲,广域网是将分布于相比局域网络更广区域的计算机设备联接起来的网络。广域网由通信子网于资源子网组成。通信子网可以利用公用分组交换网、卫星通信网和无线分组交换网构建,将分布在不同地区的局域网或计算机系统互连起来,实现资源子网的共享。 2.网络组成          广域网属于多级网络,通常由骨干网、分布网、接入网组成。在网络规模较小时,可仅由骨干网和接入网组成

ROS话题通信流程自定义数据格式

ROS话题通信流程自定义数据格式 需求流程实现步骤定义msg文件编辑配置文件编译 在 ROS 通信协议中,数据载体是一个较为重要组成部分,ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:String、Int32、Int64、Char、Bool、Empty… 但是,这些数据一般只包含一个 data 字段,结构的单一意味着功能上的局限性,当传输一些复杂的数据,比如:

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测 目录 时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测基本介绍程序设计参考资料 基本介绍 MATLAB实现LSTM时间序列未来多步预测-递归预测。LSTM是一种含有LSTM区块(blocks)或其他的一种类神经网络,文献或其他资料中LSTM区块可能被描述成智能网络单元,因为

vue项目集成CanvasEditor实现Word在线编辑器

CanvasEditor实现Word在线编辑器 官网文档:https://hufe.club/canvas-editor-docs/guide/schema.html 源码地址:https://github.com/Hufe921/canvas-editor 前提声明: 由于CanvasEditor目前不支持vue、react 等框架开箱即用版,所以需要我们去Git下载源码,拿到其中两个主

android 免费短信验证功能

没有太复杂的使用的话,功能实现比较简单粗暴。 在www.mob.com网站中可以申请使用免费短信验证功能。 步骤: 1.注册登录。 2.选择“短信验证码SDK” 3.下载对应的sdk包,我这是选studio的。 4.从头像那进入后台并创建短信验证应用,获取到key跟secret 5.根据技术文档操作(initSDK方法写在setContentView上面) 6.关键:在有用到的Mo

android一键分享功能部分实现

为什么叫做部分实现呢,其实是我只实现一部分的分享。如新浪微博,那还有没去实现的是微信分享。还有一部分奇怪的问题:我QQ分享跟QQ空间的分享功能,我都没配置key那些都是原本集成就有的key也可以实现分享,谁清楚的麻烦详解下。 实现分享功能我们可以去www.mob.com这个网站集成。免费的,而且还有短信验证功能。等这分享研究完后就研究下短信验证功能。 开始实现步骤(新浪分享,以下是本人自己实现

Android我的二维码扫描功能发展史(完整)

最近在研究下二维码扫描功能,跟据从网上查阅的资料到自己勉强已实现扫描功能来一一介绍我的二维码扫描功能实现的发展历程: 首页通过网络搜索发现做android二维码扫描功能看去都是基于google的ZXing项目开发。 2、搜索怎么使用ZXing实现自己的二维码扫描:从网上下载ZXing-2.2.zip以及core-2.2-source.jar文件,分别解压两个文件。然后把.jar解压出来的整个c