添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

基于OpenFlow的SDN网络中,控制器可以通过OpenFlow的消息”OFPT_PACKET_OUT”,直接发送任意报文到网络的数据通路中。本文介绍了在ODL环境下如何直接通过控制器发送任意报文的两种方法。第一种使用控制器的RESTCONF接口packet-processing(2013-07-09),采用HTTP协议和JSON格式直接发送任意的报文到网络中;第二种方法编写内嵌于ODL的APP应用,采用PacketProcessingService服务接口发送任意报文。两种方法在ODL的版本Boron(硼)和Beryllium(铍)均测试通过。

OpenDaylight作为一款开源SDN网络控制器,依托于强大的社区支持以及功能特性,成为了目前主流的SDN网络控制器开发平台。不仅为开发者提供了大量的网络管理功能,而且藉由AD-SAL(API驱动的服务层)和MD-SAL(模型驱动的服务层), 给独立的网络应用提供了完善的二次开发接口。尤其是其提供的REST API接口, 基于HTTP协议及JSON/XML格式进行接口的调用和数据的获取,独立于具体的开发语言,其使用非常的方便。

1.1 YANG模型和NETCONF协议

YANG模型是一种数据建模语言,用来建模由NETCONF协议定义的配置数据和状态数据、远端过程调用(RPCs)、和NETCONF通知(notification), 具有良好的可读性和可扩展性。设备端和客户端都可以使用YANG进行数据的建模, 设备侧提供了YANG数据模型后,客户端可依据工具自动生成对应的访问模型代码,大大的节省开发工作量。其具体的定义可以参看RFC 6020: “YANG - A Data Modeling Language for the Network Configuration Protocol (NETCONF)” 。

NETCONF(Network Configuration Protocol,网络配置协议)是一种基于XML的网络管理协议,它提供了一种可编程的、对网络设备进行配置和管理的方法。用户可以通过该协议设置参数、获取参数值、获取统计信息等。NETCONF报文使用XML格式,具有强大的过滤能力,而且每一个数据项都有一个固定的元素名称和位置,这使得同一厂商的不同设备具有相同的访问方式和结果呈现方式,不同厂商之间的设备也可以经过映射XML得到相同的效果,这使得它在第三方软件的开发上非常便利,很容易开发出在混合不同厂商、不同设备的环境下的通用的管理软件。在管理软件的协助下,使用NETCONF功能会使网络设备的配置管理工作,变得更简单更高效。

NETCONF协议采用了分层结构,分成四层:内容层、操作层、RPC(Remote Procedure Call,远程调用)层和通信协议层。
其具体定义可看RFC 6241:” Network Configuration Protocol (NETCONF)”。

最初的网络管理协议SNMP也有对应的建模语言SMI。而操作ODL的应用采用的RPC接口、HTTP协议和XML格式数据,就构成了SOAP协议内容。

在ODL中,通过YANG模型来建模应用的配置数据和状态数据,以及RPC和Notification.
NETCONF/SNMP/YANG/SOAD/REST其对比关系如下图:

图 11 SNMP/NETCONF/SOAD/REST对比

1.2 报文发送接口packet-processing

在ODL中,安装了bundle” odl-restconf”和” odl-netconf-connector-all”后,打开浏览器的地址:
http://:8181/ apidoc/explorer/index.html (以ODL版本Boron-SR1为例),就能看到相应的REST 公开的接口,其中RPC调用接口” packet-processing”提供了发送任意报文的功能, 此功能由openflowplugin提供, 如下图:
该接口的yang文件定义在ODL的bundle“openflowplugin”里面:openflowplugin/model/model-flow-service/src/main/yang/packet-processing.yang

此接口的yang RPC定义内容如下:

description "Sending packet out through openflow device."; input { uses inv:node-context-ref; leaf connection-cookie { type connection-cookie; leaf egress { type inv:node-connector-ref; leaf buffer-id { type uint32; uses raw-packet; uses action-type:action-list;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
rpc transmit - packet {
description "Sending packet out through openflow device." ;
input {
uses inv : node - context - ref ;
leaf connection - cookie {
type connection - cookie ;
}
leaf egress {
type inv : node - connector - ref ;
}
leaf buffer - id {
type uint32 ;
}
uses raw - packet ;
uses action - type : action - list ;
}
}

使用此接口时,需要构造:

  • raw-packet: 整个原始报文。报文内容包含数据链路层。Yang的定义为binary类型,采用HTTP协议发送时,此二进制内容以Base64编码为ASCII字符后发送
  • egress: 报文出口,这里指的是网络中交换机的端口,此端口格式为ODL中的node-connector-ref
  • node: 发送此报文的交换机,为ODL中的node-ref
  • action-list: 可选功能。如果提供action, 那么action-list的最后一个动作是output-action来发送报文,否则报文不能从交换机发送出去
  • Url: 发送的url为 http://:8181/restconf/operations/packet-processing:transmit-packet

    1.3 PacketProcessingService服务

    此服务由openflowplugin的模块model-flow-service提供。只有一个接口transmitPacket。定义如下:

    2.2 REST接口之packet-processing

    根据yang定义文件构造相应的请求报文即可。其中的payload原始数据为二进制数据,需要采用Base64编码为ASCII字符串序列。

    构造JSON请求报文的java代码如下(采用Nayuki’s library for JSON):

    public String buildInput_transmit_packet(){ byte[] rawPacket=getRawByte(xxx); String nodeid="openflow:11"; String egress="openflow:11:2"; Map<String,Object> input = new TreeMap<String,Object>(); input.put("connection-cookie", 123456); String strNode = "/opendaylight-inventory:nodes/opendaylight-inventory:node[opendaylight-inventory:id='"+nodeid+"']"; input.put("node",strNode); input.put("egress",strNode + "/opendaylight-inventory:node-connector[opendaylight-inventory:id='"+ egress+"']"); //payload input.put("payload", Base64Util.encode(rawPacket)); Map<String,Object> packet = new TreeMap<String,Object>(); packet.put("input", (Object)input); return Json.serialize(packet);
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public String buildInput_transmit_packet ( ) {
    byte [ ] rawPacket = getRawByte ( xxx ) ;
    String nodeid = "openflow:11" ;
    String egress = "openflow:11:2" ;
    Map < String , Object > input = new TreeMap < String , Object > ( ) ;
    input . put ( "connection-cookie" , 123456 ) ;
    String strNode = "/opendaylight-inventory:nodes/opendaylight-inventory:node[opendaylight-inventory:id='" + nodeid + "']" ;
    input . put ( "node" , strNode ) ;
    input . put ( "egress" , strNode + "/opendaylight-inventory:node-connector[opendaylight-inventory:id='" + egress + "']" ) ;
    //payload
    input . put ( "payload" , Base64Util . encode ( rawPacket ) ) ;
    Map < String , Object > packet = new TreeMap < String , Object > ( ) ;
    packet . put ( "input" , ( Object ) input ) ;
    return Json . serialize ( packet ) ;
    }
    public void doTransmitPacket() throws Exception{ String strUrl = "http://<IP>:8181/restconf/operations/packet-processing:transmit-packet"; HttpUtil http = new HttpUtil(strUrl); http.addBasicAuth("admin","admin"); http.addHeaderField("Accept","application/json"); http.addHeaderField("Content-type","application/json"); String strEntity = buildInput_transmit_packet(); System.out.println(strEntity); http.addEntity(strEntity); http.sendRequest(HttpUtil.Method.POST);
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public void doTransmitPacket ( ) throws Exception {
    String strUrl = "http://<IP>:8181/restconf/operations/packet-processing:transmit-packet" ;
    HttpUtil http = new HttpUtil ( strUrl ) ;
    http . addBasicAuth ( "admin" , "admin" ) ;
    http . addHeaderField ( "Accept" , "application/json" ) ;
    http . addHeaderField ( "Content-type" , "application/json" ) ;
    String strEntity = buildInput_transmit_packet ( ) ;
    System . out . println ( strEntity ) ;
    http . addEntity ( strEntity ) ;
    http . sendRequest ( HttpUtil . Method . POST ) ;
    }
    mvn archetype:generate -DarchetypeGroupId=org.opendaylight.controller \ -DarchetypeArtifactId=opendaylight-startup-archetype\ -DarchetypeVersion=1.2.1-Boron-SR1 \ -DarchetypeRepository=https://nexus.opendaylight.org/content/repositories/public/\ -DarchetypeCatalog=https://nexus.opendaylight.org/content/repositories/public/archetype-catalog.xml
    1
    2
    3
    4
    5
    mvn archetype : generate - DarchetypeGroupId = org . opendaylight . controller \
    - DarchetypeArtifactId = opendaylight - startup - archetype \
    - DarchetypeVersion = 1.2.1 - Boron - SR1 \
    - DarchetypeRepository = https : //nexus.opendaylight.org/content/repositories/public/\
    - DarchetypeCatalog = https : //nexus.opendaylight.org/content/repositories/public/archetype-catalog.xml
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    rpc send - packet {
    input {
    leaf rawpacket {
    type binary ;
    }
    leaf egress {
    type inv : node - connector - ref ;
    }
    leaf node {
    type inv : node - ref ;
    }
    }
    output {
    leaf result {
    type string ;
    }
    }
    }
    private void packetOut(NodeRef egressNodeRef, NodeConnectorRef egressNodeConnectorRef, byte[] payload) { // Construct input for RPC call to packet processing service TransmitPacketInput input = new TransmitPacketInputBuilder().setPayload(payload).setNode(egressNodeRef) .setEgress(egressNodeConnectorRef).build(); packetProcessingService.transmitPacket(input);
    1
    2
    3
    4
    5
    6
    private void packetOut ( NodeRef egressNodeRef , NodeConnectorRef egressNodeConnectorRef , byte [ ] payload ) {
    // Construct input for RPC call to packet processing service
    TransmitPacketInput input = new TransmitPacketInputBuilder ( ) . setPayload ( payload ) . setNode ( egressNodeRef )
    . setEgress ( egressNodeConnectorRef ) . build ( ) ;
    packetProcessingService . transmitPacket ( input ) ;
    }
    @Override public Future<RpcResult<SendPacketOutput>> sendPacket(SendPacketInput input) { SendPacketOutputBuilder packetBuilder = new SendPacketOutputBuilder(); NodeConnectorRef egress = input.getEgress(); NodeRef node = input.getNode(); byte[] payload = input.getRawpacket(); packetOut(node,egress, payload); //send packet!! packetBuilder.setResult("OK"); return RpcResultBuilder.success(packetBuilder.build()).buildFuture();
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Override
    public Future < RpcResult <SendPacketOutput> > sendPacket ( SendPacketInput input ) {
    SendPacketOutputBuilder packetBuilder = new SendPacketOutputBuilder ( ) ;
    NodeConnectorRef egress = input . getEgress ( ) ;
    NodeRef node = input . getNode ( ) ;
    byte [ ] payload = input . getRawpacket ( ) ;
    packetOut ( node , egress , payload ) ; //send packet!!
    packetBuilder . setResult ( "OK" ) ;
    return RpcResultBuilder . success ( packetBuilder . build ( ) ) . buildFuture ( ) ;
    }

    4) 添加相应的依赖, 并编译运行
    在api/pom.xml文件中添加依赖:model-inventory
    在impl/pom.xml文件中添加依赖:openflowplugin-model的model-flow-service依赖
    在feature/pom.xml添加上述2个依赖,
    在feature/ src/main/features/features.xml文件中分别添加如下的内容:

    <repository>mvn:org.opendaylight.openflowplugin/features-openflowplugin/${openflowplugin.version}/xml/features</repository> <feature version='${openflowplugin.version}'>odl-openflowplugin-southbound</feature>

    5) 运行测试
    浏览器打开" http://:8181/apidoc/explorer/index.html "
    找到” mypacket(2015-01-05)”,其中mypacket为第一步定义的项目名。
    构造如下的JSON输入串,然后点击”try it out!”. 可以在Test-PC上运行wireshark看到我们发送的自定义报文。

    {"input": {"egress": "/opendaylight-inventory:nodes/opendaylight-inventory:node[opendaylight-inventory:id='openflow:10']/opendaylight-inventory:node-connector[opendaylight-inventory:id='openflow:10:2']", "node": "/opendaylight-inventory:nodes/opendaylight-inventory:node[opendaylight-inventory:id='openflow:10']", "rawpacket": "QAwpyJZqAAwp06cxCABFAAA8fR0AAEABaCjAqAoVwKgKFggATJcCAP7EYWJjZGVmZ2hpamtsbW5vcHFyc3R1dndhYmNkZWZnaGk="}}
    { "input" : { "egress" : "/opendaylight-inventory:nodes/opendaylight-inventory:node[opendaylight-inventory:id='openflow:10']/opendaylight-inventory:node-connector[opendaylight-inventory:id='openflow:10:2']" , "node" : "/opendaylight-inventory:nodes/opendaylight-inventory:node[opendaylight-inventory:id='openflow:10']" , "rawpacket" : "QAwpyJZqAAwp06cxCABFAAA8fR0AAEABaCjAqAoVwKgKFggATJcCAP7EYWJjZGVmZ2hpamtsbW5vcHFyc3R1dndhYmNkZWZnaGk=" } }

    发送报文是网络设备的最基本功能。本文我们介绍了ODL环境中的两种直接发送任意原始报文的方法。并且在ODL的版本Boron(硼)和Beryllium(铍)都测试通过。需要注意的是:通过HTTP构造请求时,二进制类型的原始报文需要编码为Base64类型,ODL系统会自动进行解码。

    本文完整的代码: https://github.com/siwind/mypacket

    作者简介: 王寅庆, 目前在北京邮电大学网络技术研究院学习。 曾经供职于行业内某通信技术公司, 做过软件研发, 带领过项目团队。 关注于网络、SDN、大数据和信息安全等方面研究。

  • 本站原创文章仅代表作者观点,不代表SDNLAB立场。所有原创内容版权均属SDNLAB,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用,转载须注明来自 SDNLAB并附上本文链接。 本站中所有编译类文章仅用于学习和交流目的,编译工作遵照 CC 协议,如果有侵犯到您权益的地方,请及时联系我们。
  • 本文链接 https://www.sdnlab.com/18092.html
  • Viagra 5 mg funziona https://chanchuoi.com/community/profile/canadianpharmacy/ Whoa plenty of beneficial advice! (2023/05/21 04:25)
  • canadian pharmacies https://chanchuoi.com/community/profile/canadianpharmacy/ Perfectly expressed indeed. ! (2023/05/21 03:40)
  • canadian pharmaceuticals online https://www.careerstek.com/forum/profile/canadianpharmacy/ Awesome facts. Thanks a lot! (2023/05/20 20:29)
  • 详解:VirtIO Networking 虚…

    VirtIO 由 Rusty Russell 开发,最初是为…

  • HTTP/3 + QUIC:性能有余,安…

    超文本传输协议(HTTP)全称Hyper Text Tr…

  • EVPN VxLAN Overlay SDN 技术…

    2011 年 8 月,主要由 VMware 与 Cisco 公…

  • 图解IPsec协议:剖析IP网络加…

    互联网协议以TCP/IP最为人们所熟知,大多…

  • 爱立信与诺基亚的重大分歧:I…

    软硬件解耦的概念已提出多年,但如今的大…

  • comment reply
  • comment reply
  • comment reply
  • comment reply
  • comment reply

    fnedu_201558d518

  • comment reply
  • comment reply

    weibo_1401760347

  • comment reply
  • comment reply
  • comment reply
  •