0x01 术语
术语 | 解释 | 应用举例 |
---|---|---|
Action | OpenFlow中的动作,应用在报文上面 | 转发到某个端口,丢弃 |
List of actions | 有顺序的动作列表,允许重复,作用是累加的。如果有output动作,那么将拷贝一份报文及其pipeline fields,供后续处理,不影响原始的报文 | apply-actions,packet-out |
Set of actions | 无顺序的动作集,不允许有重复 | 组表,流表 |
Action set | 动作集,附加在报文之上,流表结束后执行 | 流表 |
Control channel | 控制通道,用于虚拟交换机和sdn控制器通信 | |
Counter | 计数器 | 报文统计,报文字节数统计 |
Datapath | 一个完整的虚拟交换机,包括流表,组表,计量表等,也即一个bridge | |
Flow entry | 流表项,在流表中 | |
Flow table | 流表,包含多个流表项 | |
Group | 组,包含在组表中,一个Group包含多个action bucket | 组播转发 |
Hybrid switch | 一种实现了openflow交换机和传统交换机的交换机 | |
Instructions | 指令,和动作的区别在于指令是立即执行的,每个流表被命中之后立即执行 | |
Instructions set | 指令集,和动作集类似 | |
Match field | 匹配域,用于匹配报文,flow entry的一部分 | |
Metadata | 一个带有掩码的寄存器 | 用于流表之间传输数据 |
Meter | 流量计量 | QoS |
Pipeline | 报文的流水线处理,即多个flow-tale构成一个pipeline | |
Pipeline field | 在pipeline处理过程中,除了报文头部之外的数据 | ingress port,metadata |
0x02 组件概览
0x03 Openflow Ports
OpenFlow的端口即报文转发的端口,不一定和物理端口是完全一致的,比如被openflow禁用,或者openflow自身的虚拟端口。一个报文的生命周期:
OpenFlow将端口分为以下几种类型:
- Physical ports:即真实的物理接口
- Logical ports:交换机自身定义的端口,和物理接口没有一一对应的关系,例如聚合组,隧道口
Reserved ports:包括以下几种: | 端口类型
(required,optional) | 含义 | 应用范围 | | | :—-: | :—-: | :—-: | —- | | | | Ingress port | Output port | | ALL | 所有交换机定义的standar ports |
| 该接口下报文将被转发到除了ingress port,OFPPC_NO_FWD port之外的所有标准端口 | | CONTROLLER | 和控制器连接的控制隧道 | 代表该报文来自控制器 | 将报文封装在packet-in消息里面通过隧道发送到控制器 | | TABLE | 指代pipeline的起点 | | 将该报文投递到pipeline的起点,从头开始处理 | | IN_PORT | 即ingress port | | 将报文发送到报文的入口 | | ANY | 任意端口,控制消息中使用 | | | | UNSET | 未设置的端口,匹配action set的出口中使用 | | | | LOCAL | 代表本地的网络栈 | | | | NORMAL | 表示使用传统交换机转发方式转发 | | | | FLOOD | 表示使用传统交换机转发方式泛洪 | | |Strandard ports:包括Physical Ports,Logical Ports,LOCAL Reserved Ports
0x04 OpenFlow Tables
Pipeline
OpenFlow Pipeline包括多个流表,每个流表包含多个流表项。报文从表0开始处理,根据命中的指令选择是否进入下一级流表。
流表及流表项
流表项组成
Match Fields | Priority | Counters | Instructions | Timeouts | Cookies | Flags |
---|---|---|---|---|---|---|
- Match Fields:匹配域,除了报文的头部以外,还包括Pipeline Fields
- Priority:优先级
- Counters:计数器
- Instructions:指令
- Timeouts:包含Hard timeout,Idle timeout
- Cookies:交换机处理报文的时候没有使用,由控制器下发,标识一条流表项
- FLags:表明表项的管理方式,比如表项被删除是否上报到控制器
每条流表项的key为:(Match Fields, priority)
匹配算法
根据控制器的配置,流表可以被分成两部分,Ingress和Egress,且Egress表的编号均在Ingress之后。
#OFP_EGRESS is defined by controller
if table_num > OFP_EGRESS
#This table is Egress
else
#This table is Ingress
相应的匹配也分两阶段,Ingress和Egress。 入口的报文先进行Ingress处理,之后根据在出口的上下文下进行Egress处理。匹配过程中按照优先级匹配,优先级高的先命中,一个Flow Table只会有一个Entry命中。如果同时存在优先级相同的表项两个同时命中的话,官方文档对这种情况定义为Undefined。
特殊流表项(Table-Miss)
流表项(Match Fileds, Priority) = (*, 0)
即为Table-Miss表项,用于流表的默认动作。例如发送给控制器以产生新的流表项,或者丢弃。该表项是控制器安装的表项,并非默认存在于流表中,如果控制器没有安装该表项,则未匹配中的报文就默认丢弃。
Instructions
Instructions是Flow Entry的一个成员,命中之后执行。总共包括以下几个,并且执行顺序也是按照以下的顺序,每个流表项的instruction为一个集合,即在同一个流表项中不允许出现相同的instructions。
Instruction | 含义 | 应用 |
---|---|---|
Apply-actions | 不改动动作集,动作立即执行,action为有顺序的list | 修改报文,在多个流表执行同一个动作 |
Clear-actions | 清除Action set里面的action | 在table-miss flow entry中使用 |
Write-actions | 添加动作到动作集,如果有相同的将覆盖原有的 | ANY |
Write-metadata | 修改metadata | |
stat-trigger | 如果计量值超过指定值,则发一个消息到控制器 | |
Goto-table | 跳转到某个流表,必须是编号比自己大的流表,不允许向后跳转 | ANY |
流表的输入包括三部分:Action Set,Pipeline Fields,Packet。经过每张流表的处理,每一部分都可能被修改,Action Set可能被Clear-actions/Wrige-actions修改,Pipeline Fields可能被Apply-actions修改,Packet可能被Apply-actions修改。被修改后的数据通过Goto-table指定传递给下一张流表。如果没有Goto-table指令,那么Action set将被立即执行。
Action set
正如前面介绍的,Action set是附加在报文上面的。Ingress的初始动作集为空,但是Egress的初始动作集包含一个Output current port
的动作,动作集的执行按照如下的顺序:
action | 含义 |
---|---|
copy TTL inwards | |
pop | 弹出标签(vlan/mpls/PBB) |
push MPLS | 压入MPLS标签 |
push PBB | 压入PBB标签 |
push VLAN | 压入Vlan标签 |
copy TTL outwards | |
decrememet TTL | 减少IP报文的TTL值 |
set | set-field动作 |
QoS | QoS相关,比如set-queue,meter |
group | 组动作 |
output | 转发报文 |
如果output和group有出口是一样的,那么group的优先执行,output则不会再执行。对于output,ingress和Egress有不同的处理方式。ingress的output会检查是否有egress-table,如果有的话初始化当前出口为动作集,继续执行Egress-table;egress的output则会立即执行。
Actions
Action | 含义 |
---|---|
Output port_no | 转发报文 |
Group group_id | 执行对应的组动作 |
Drop | 事实上不存在这个动作,这是个隐式的动作,没有出口即代表丢弃 |
Set-Queue queue_id | 设置队列的id,出口队列 |
Meter meter_id | 计量表,主要是限速 |
Push-Tag/Pop-Tag ethertype | 压/弹标签,有vlan/mpls/pbb |
Set-Field field_type value | 设置报文头部的字段值 |
Copy-Field src_field_typ dst_field_type | 在报文头部及pipeline域之间拷贝 |
Change-TTL ttl | 改变报文的ttl |
Counters
OpenFlow的白皮书定义了很多counter,包含在flow-table,flow-entry,port,queue, group,group bucket,meter,meter band。详情参考白皮书5.9的表格。
Group Table
Openflow仅定义了一个组表,里面包含多个组表项,供action-set里面引用。每个组表项包含以下几个字段:
字段名 | 含义 |
---|---|
group identifier | 标识符,引用组的时候使用 |
group type | 组类型 |
counters | 计数器 |
action buckets | 每个bucket即为一个action set,所有的action bucket为一个list,即action bucket间执行是按照配置的顺序,action bucket内部执行是按照action-set的顺序 |
OpenFlow定义的组类型包括以下4种:
- indirect:该组仅包含一个action bucket,和包含一个buckt的all类型是一样的。
- all:该类型可以包含多个bucket,命中后执行所有的action bucket。组播/广播中使用到。
- select:多选一的类型,该类型包含多个bucket,但是只选择一个bucket来执行。选择哪一个依赖于配置的算法。这种类型可以实现负载均衡作用。
- fat failover:多选一的类型,选择第一个可用的bucket。判断是否可以依赖于每个bucket附加的端口或者group,该类型需要实现可用性机制。
Meter Table
和Group Table类似,Meter Table也只有一张,包含了多个表项。表项的构成如下所示,其中一个表项可以包含多个Meter Band,meter Band之间是没有顺序的,但是一个报文只能有一个meter band被执行。能被执行的Meter Band其速率要超过配置的速率。
- Meter Identifier:标识符,被引用的时候使用。
- Meter Bands:由多个meter band组成。
- Counters:计数器
多个Meter band,到底那一个可以被执行,openflow没有明确的定义,只是规定了一些约束。最简单的可以根据Rate的大小,越大的优先级越高。
- Band Type:类型,包括Drop,Dscp remark。
- Rate:配置的速率大小,根据这个速率区分各个Meter band。
- Burst:定义该meter band的粒度。
- Counter:计数器。
- Parameters:和Band Type相关联,比如DSCP remark其参数就存放在这个字段。
0x05 OpenFlow Channel
OpenFlow的控制隧道是控制器和交换机的通信接口,可以基于TLS进行加密通信,或者直接使用TCP。一台交换机可以连接一到多个控制器。
通信消息
控制隧道的通信消息可以分为以下三类:
- Controller-to-Switch:由控制器发起的消息。用于管理,查询交换机。
- Asynchronous:由交换机发起的消息,上报交换机的状态,事件。
- Symmetric:对称消息,由控制器或者交换机发起。 | 消息类型 | 子消息 | 含义 | 用途 | | —- | —- | —- | —- | | Controller-to-Switch | Feature | 查询交换机的特性,能力 | 兼容性 | | | Configuration | 设置或者查询交换机配置 | | | | Modify-State | 修改交换机的状态 | 修改/添加/删除流表 | | | Read-State | 查询交换机的状态 | | | | Packet-out | 控制器指定交换机某个端口发送报文 | | | | Barrier | 消息栅栏 | 由于保证消息之前的依赖 | | | Role-Request | 请求或者设置隧道的角色 | 多个连接时使用 | | | Asynchronous-Configuration | 设置或者查询异步消息的过滤器 | 连接多个控制器时,过滤交换机上报的消息,只关注自己需要的,防止隧道中消息太多 | | Asynchronous | Packet-in | 转发报文到控制器,可以在交换机缓存报文,然后上报报文头部及buffer-id | | | | Flow-Removed | 发送流表项被删除到控制器 | | | | Port-status | 端口发生变化 | 包括配置变化,状态变化 | | | Role-status | 隧道角色变化 | | | | Controller-Status | 控制隧道变化,交换机通知其他控制器 | 通过交换机,控制器之间可以知道对端的状态 | | | Flow-monitor | 流变化消息 | | | Symmetric | Hello | 连接建立的握手 | | | | Echo | 探测报文,需要回复 | 心跳,测量延时 | | | Error | 通知另一端出错 | 交换机回复控制器请求失败 | | | Experimenter | 实验特性 | 在加入标准前试验新特性 |
连接管理
OpeFlow的连接包括两种:主连接,次连接。次连接依赖于主连接,主连接断开后,次连接也要断开连接。次连接主要用于优化数据转发消息(Packet-in),比如用udp来实现次连接,从而加快转发速率。按照网络位置,连接也可以分为带内(in-band),带外(out-of-band)。带内连接的报文也需要通过OpenFlow pipeline的处理,因此带内连接交换机需要预先初始化好流表,使得报文可以进来处理。带外连接指的是连接报文所处的网络不是由OpenFlow管理的。
连接URI
交换机通过URI:protocol:name-or-address[:port]
来标识一台控制器的隧道。
- 协议:对于主连接,可以为
TLS/TCP
;对于次连接,其协议可以为TLS/DTLS/TCP/UDP
。 - 端口号:可以配置指定也可以使用默认的
6653
。
控制器通过(Datapath ID,Auxiliary ID)
来标识一条连接,主连接的Auxiliary ID
为0,
连接建立
控制隧道的建立分为以下三步:
- 根据指定的协议建立传输层连接,
TCP
或者TLS
。 - 连接建立之后立马发送
Hello
报文给对端。收到对端的报文之后,根据报文中携带的OpenFlow版本号信息,选择双方都支持的最高版本进行通信。如果不支持对端的OpenFlow版本,需要给对端发送一个Error Message,并终止连接。 - 控制器发送
Feature
消息获取交换机的Datapath-id
,用于标识交换机。
连接维护
OpenFlow不处理连接的维护,连接的维护比如心跳,流量管理,拥塞管理应该由下层传输层协议维护。在连接因为网络的问题导致中断的情况下,发起连接的一端需要主动去重连,但是如果是因为收到Error消息导致的主动断开,则不需要重连。在连接状态发生变化的时候(中断或者重新连接上),OpenFlow交换机需要主动上报改连接的状态给其他控制器,使得其他控制器可以据此作出响应。如果所有的连接均中断,那么交换机可以运行在以下两种模式:
- fail secure mode:在这种模式下,唯一的变化是所有发给控制器的报文都将被丢弃,同时当前的流表还是会按照既定的时间老化。
fail standalone mode:在这种模式下,交换机将变成一个普通交换机,按照传统的方式转发报文,并且可以任意修改流表。一般在Hybrid Switch才有。
多控制器
一台交换机可以和多台控制器连接,多台控制器之间可以做备份或者负载。一台控制器挂掉之后,交换机可以连接另外一台控制器,提高网络的高可用性;相应地,控制器可以通过
Asynchronous Configuration
消息来配置从交换机收什么类型的消息,以达到负载的作用。如果一台交换机连接了多台控制器,那么对于Asynchronous消息,交换机需要发送给每台控制器。对于Controller-to-Switch的消息,则根据控制器隧道分开处理,在哪条隧道收到的消息则回复给那一台控制器。此时对于流表的修改可能存在冲突,但是交换机不处理该冲突情况,由控制器之间相互协商解决。这也体现了SDN的思想,将业务逻辑的复杂性放到控制器处理。对于交换机连接的所有控制器来说,他们之间通过角色选举决定如何由谁控制该交换机。控制器角色分为以下三种:Eqal:该角色可以读写交换机的状态,处于该角色的控制器是同等的地位。可以有多个控制器处于该角色。
- Mater:处于该角色的控制器只能有一个(由交换机保证),该角色的控制器也具有读写交换机状态的权限。
- Slave:处于该角色的控制器可以有多个,该角色的控制器只有读权限。
控制器的角色都是由控制器触发的,交换机无权干涉。控制器通过OFPT_ROLE_REQUEST
消息告知交换机当前控制器的角色,交换机无条件接受。唯一比较特殊的是:如果控制器设置为Mater,但是当前有一台为Master,那么之前那台主将被交换机切换为备,并且发送一个角色切换的消息给之前的主(一般情况下,是那台主掉线了,另外一台才升主的,所以这种情况下是无法通知到的)。为了防止主备切换过程中的乱序(比如连续切换两次,由于是多条连接,可能存在最后的那个主发的消息提前到达的情况,这个时候就会设置错误),在OFPT_ROLE_REQUEST
消息中会带上一个generation_id
,表明消息的顺序。generation_id
是一个单调递增的整数,在每次主设备变化的时候改变。交换机收到该消息的处理算法如下:
//On switch startup:
int64_t generation_is_defined = false;
//On receiving OFPT_ROLE_REQUEST with role equal to OFPCR_ROLE_MASTER or OFPCR_ROLE_SLAVE and with a given generation_id, say GEN_ID_X:
if (generation_is_defined &&
distance(GEN_ID_X, cached_generation_id) < 0) {
//<discard OFPT_ROLE_REQUEST message>;
//<send an error message with code OFPRRFC_STALE>;
} else {
cached_generation_id = GEN_ID_X;
generation_is_defined = true;
//<process the message normally>;
}
//where distance() is the Wrapping Sequence Number Distance operator defined as following:
distance(a, b) := (int64_t)(a - b)
//I.e. distance() is the unsigned difference between the sequence numbers, interpreted as a two’s complement signed value. This results in a positive distance if a is greater than b (in a circular sense) but less than “half the sequence number space” away from it. It results in a negative distance otherwise.(a < b).
次连接
次连接依赖于主连接,只有主连接存在,次连接才存在。也就是次连接在主连接之后建立,次连接在主连接断开时候端开。对于次连接有如下限制:
- 相同的源
IP
。 - 相同的目的
IP
。 - 相同的传输层目的端口。
对于某种类型的连接处理什么消息没有特别的约束,每一条连接都可以处理所有消息,但是一个连接收到的请求必须在这条连接上面回复。下面的处理方式是一种较优的建议:
- 对于控制器:
- 除
Packet-Out
的所有消息都在主连接上面发送。 - 连接维护消息
(hello/echo request/features request)
在所有的连接上面发送。 - 和
Packet-In
对应的消息在其收到的连接上面发送。 - 对于非
Packet-In
对应的Packet-Out
消息分散到各个次连接上面,但是需要保证同一条流映射到同一条连接。 - 以上如果对应的次连接不可用,则统一使用主连接。
- 除
- 对于交换机:
Packet-In
通过次连接发送,但是需要保证同一条流在同一条连接上面发送- 除
Packet-In
之外的所有消息在主连接上面发送。
次连接使用不可靠的传输层协议的时候(例如UDP),不支持所有类型的消息。支持的消息类型为OFPT_HELLO, OFPT_ERROR,OFPT_ECHO_REQUEST, OFPT_ECHO_REPLY, OFPT_FEATURES_REQUEST, OFPT_FEATURES_REPLY,OFPT_PACKET_IN, OFPT_PACKET_OUT and OFPT_EXPERIMENTER
。
流表管理
流表消息类型
流表的消息一共有5种类型,如下所示:
enum ofp_flow_mod_command {
OFPFC_ADD = 0, /* New flow. */
OFPFC_MODIFY = 1, /* Modify all matching flows. */
OFPFC_MODIFY_STRICT = 2, /* Modify entry strictly matching wildcards and priority. */
OFPFC_DELETE = 3, /* Delete all matching flows. */
OFPFC_DELETE_STRICT = 4, /* Delete entry strictly matching wildcards and priority. */
};
OFPFC_ADD
:添加流表项,如果该流表项已经存在,那么旧的流表项将被删除,新的流表项添加进去。- 设置
OFPFF_CHECK_OVERLA
flag:该添加需要检查是否存在overlap
的情况,即是否存在一条表项和当前的表项可以同时被命中;如果存在,那么该表项不能被插入,交换机返回一个overlap
错误消息。
- 设置
OFPFC_MODIFY/OFPFC_MODIFY_STRICT
:修改流表项,幂等操作。strict?- 存在该流表项,修改之。
- 不存在该流表项,无变化,不返回错误。
OFPFC_DELETE/OFPFC_DELETE_STRICT
:删除流表项,幂等操作。- 存在该流表项,删除。
- 不存在该流表项,无变化,不返回错误。
流表的删除有以下三种方式:
- 控制器下发的删除指令
OFPFC_DELETE
。 - 老化时间到期后被交换机删除。
-
流表同步机制
流表的同步机制即交换机上面的流表自同步,被同步的流表的表项的增删改将映射到同步的流表。可以分为两类:单向同步,双向同步。这种机制使得交换机可以根据需要执行多个匹配,同时对应到同一个操作。
OpenFlow
标准里面没有对这种机制作出规定,里面举了个mac地址的学习转发的例子。一个表(A表)用于学习查找,另一个表(B表)用于根据目的mac转发查找。 A表解析报文的源
MAC
地址,查找表项,没有的话通知控制器学习。同时设置表项的老化时间。- B表解析报文目的
MAC
地址,查找表项转发报文。A表新增删除表项的时候,B表也同步操作。
组表管理
enum ofp_group_mod_command {
OFPGC_ADD = 0, /* New group. */
OFPGC_MODIFY = 1, /* Modify all matching groups. */
OFPGC_DELETE = 2, /* Delete all matching groups. */
OFPGC_INSERT_BUCKET = 3,/* Insert action buckets to the already available list of action buckets in a matching group */
/* OFPGC_??? = 4, */ /* Reserved for future use. */
OFPGC_REMOVE_BUCKET = 5,/* Remove all action buckets or any specific action bucket from matching group */
};
OFPGC_ADD
:添加一个新的组,与流表不同的是,如果当前存在该组表,那么将返回错误OFPGC_MODIFY
:修改一个组,与流表不同的是,当前如果不存在该组表,那么将返回错误OFPGC_DELETE
:删除一个组,是一个幂等操作,如果当前不存在则不删除。OFPGC_INSERT_BUCKET
:插入一个action bucket
到指定的group
中。插入位置由command_bucket_id
指定。有以下几种类型:OFPG_BUCKET_FIRST
:插入到首部。OFPG_BUCKET_LAST
:插入到尾部。- 指定的
bucket id
:插入到bucket id
之后。
OFPGC_REMOVE_BUCKET
:在指定的group
中删除一个action bucket
,删除的位置与插入类型,由command_bucket_id
指定。有以下几种类型:OFPG_BUCKET_FIRST
:删除最后一个。OFPG_BUCKET_LAST
:删除第一个。OFPG_BUCKET_ALL
:删除所有。- 指定的
bucket id
:删除指定位置的bucket
。
meter表管理
/* Meter commands */
enum ofp_meter_mod_command {
OFPMC_ADD = 0, /* New meter. */
OFPMC_MODIFY = 1, /* Modify specified meter. */
OFPMC_DELETE = 2, /* Delete specified meter. */
};
OFPMC_ADD
:与组表类似,如果已经存在则返回错误。OFPMC_MODIFY
:与组表类型,如果不存在则返回错误。OFPMC_DELETE
:与组表类似,属于幂等
操作。
各个表之间对比
表类型/消息类型 | ADD | MODIFY | DELETE |
---|---|---|---|
Flow | 幂等 | 幂等 | 幂等 |
Group | 非幂等 | 非幂等 | 幂等 |
Meter | 非幂等 | 非幂等 | 幂等 |
- 为什么
Flow
表和Group/Meter
表设计不一致?Group/Meter
表由唯一的id
标识,而Flow
表则没有。Flow
表可以由交换机主动删除/新增。因此对于增删改设计成幂等操作,而Group/Meter则完全有控制器管理,和控制器视角不一致时应该告知控制器。连接多台控制器,需要告知控制器?
- 为什么在
Group/Meter
表中ADD/MODIFY
不是幂等,而DELETE
是幂等。ADD/MODIFY
需要通知控制器,不然后续交换机和控制器的数据就不一致了。ADD
:如果不告知控制器MODIFY
:由于数据可能不一致。
Bundle消息
Bundle
消息类似于一个事务,具有原子性。即将多个OpenFlow消息放在一起执行,同时成功或者同时失败。有以下两个用途:
- 将关联的消息作为一个事务执行,防止由于某些消息执行失败出现中间状态。
- 可以让整个网络里面的所有OpenFlow交换机同时更新状态。
交换机在处理Bundle
消息的时候,每收到一个Bundle ADD
消息都需要校验是否可以执行成功,如果不能执行成功,那么需要返回错误。虽然前面有校验,但是在执行Commit
的时候的还是可能失败,比如执行的时候资源正好不可用,这个时候仍然需要返回错误给控制器。
对于控制器来说,整个Bundle
消息必须是一个原子操作,也就是在还是执行commit的时候,控制器不能查询到交换机的中间状态。一种可能的实现是在收到Bundle open
的时候,保存当前的一份状态副本,在当前副本上面做校验,之后Commit
的时候再在原始环境上面执行。同时控制器可以指定Bundle
的执行方式,有以下几种:
OFPBF_ORDERED
:按照顺序执行。OFPBF_ATOMIC
:原子执行,也就是任何时刻的报文看到的状态是整个Bundle
执行前或者执行后的状态,不能是执行过程种的状态。Whether OFPBF_ATOMIC is supported would depend on the switch hardware and software architecture. Packets and messages can temporarily be enqueued while changes are applied. As the resulting increase in forwarding / processing latency may be unacceptable, double buffering techniques are often employed.
Bundle调度
- 控制器发送一个
OFPBCT_OPEN_REQUEST
消息到交换机,请求开始一个Bundle,消息中包含Bundle id
。交换机回复一个OFPBCT_OPEN_REPLY
。 - 控制发送
OFPT_BUNDLE_ADD_MESSAGE
消息,一个个发送到交换机,根据Bundle id
识别属于哪一个Bundle
。 - 控制器发送
OFPBCT_CLOSE_REQUEST
消息,表示后续不再有Bundle Add
消息,交换机回复OFPBCT_CLOSE_REPLY
。 - 控制器发送
OFPBCT_COMMIT_REQUEST
消息,表示要提交执行,同时会带上一个表示执行的时间。
控制器在发送OFPBCT_COMMIT_REQUEST
消息给交换机之后,在交换机执行之前,控制器可以通过发送OFPBCT_DISCARD_REQUEST
消息终止该Bundle
的执行。
交换机执行Bundle
有一个时间容忍度。如下图所示,时间容忍度包括参数sched_max_future
和sched_max_past
。默认为1S,可以由控制器配置。
时间容忍度对执行的影响如下算法:
#define Tl lower_bound
#define Tu upper_bound
if (Ts > Tu) {
//Execute the bundle as close as possiable to Ts.
} else if (Ts < Tl) {
//Abort the bundle,reply error message.
} else {
//Execute the bundle as soon as possiable.
}