OpenFlow消息中buffer_Id是什么?
Openflow中buffer_id分别在三类消息中定义,并且起到的作用均是不同的。
✔ Packetin消息:用于标记缓存在交换机中的数据报文id,如报文被action上送到控制器中maxlen字段或者table_miss消息限制长度,而通过bufferid将报文缓存在交换机中,以便被另外两种消息来调用;
✔ Packetout消息:用于控制器将原先buffer在交换机中的报文,通过Packetout个形式从交换机的某个物理口送出去;
✔ Flowmod消息:如果flowmod中带有bufferid,那么说明这个flowmod需要做两件事情,第一是正常下发一条flow,其次是把交换机中先前buffer的那个数据报文,Packetout到table来匹配一次下的这条flow;注意以上两个指令都是通过这个带有bufferid的消息执行的,不需要控制器另外下packet_out消息,这种设计思路是非常巧妙的。
为什么要引入buffer_Id?
优点没必要多说,Packet_in到控制器的报文决策之后,大部分报文必然还是要扔回交换机的,为何不在交换机上缓存起来,只送个tag上去呢?
Openflow针对buffer_id的设计思想
✔ 协议针对packetin/packetout中buffer_id的设计思想很简单,一个上去,一个下来,具体思路参考下面例子。
✔ 协议针对Flowmod中bufferid设计思想就非常巧妙了,前面我已经提过,如果flowmod中带有bufferid,那么说明这个flowmod需要做两件事情,第一是正常下发一条flow,其次是把交换机中先前buffer的那个数据报文,Packetout到table来匹配一次下的这条flow;我这里举个例子大家就明白为什么这么设计了:
举例
✔ 假设现在从交换机的某个口进来一个arp请求的报文,通过tablemiss直接Packetin(bufferid是0x100)上送到controller了,controller收到之后这个arp请求之后肯定会通过Packetout(bufferid填0x100)把这个报文给广播出去,这个过程数据报文不用真正整个都送到控制器,只要送上去指定长度部分,并通过bufferid来交互数据报文内容
✔ 现在又假设交换机上某个口挂的host响应了这个arp请求,并回复了arp reply,交换机收到依然果断Packetin(bufferid是0x100)送控制器,这个时候控制器已经知道了往哪里回复,那控制器这个时候不Packetout了,干脆下个flowmod(bufferid是0x100)吧,反正以后肯定还要有报文交互,那flowmod下下去了来匹配转发来个host的报文,那怎么来保证你本次这个arp应答也被转发呢?所以这个时候设计者非常巧妙地又让这个报文通过Packet_out到table再次进入交换机pipline通道一次,以保证本次交互不需要其他动作也能成功
Ryu测试buffer_id完整app代码
from ryu.base import app_manager
from ryu.controller.handler import CONFIG_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryu.controller import ofp_event
import logging
import time
import itertools
from ryu.ofproto import ofproto_v1_3
from ryu.lib import mac
LOG = logging.getLogger("buffer_id_test")
class buffer_id_test(app_manager.RyuApp):
def __init__(self, *args, **kwargs):
self.__name__ = "buffer_id_test"
super(buffer_id_test, self).__init__(*args, **kwargs)
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
def buffer_id_test(self, ev):
datapath = ev.msg.datapath
ofp = datapath.ofproto
parser = datapath.ofproto_parser
print "#######"
print '''Test Start'''
print "#######"
print "#######"
print '''step 1. + Add table_miss_flow'''
print "#######"
match = parser.OFPMatch()
oas = []
oa = parser.OFPActionOutput(ofproto_v1_3.OFPP_CONTROLLER, ofproto_v1_3.OFPCML_MAX)
oas.append(oa)
inst = parser.OFPInstructionActions(ofproto_v1_3.OFPIT_APPLY_ACTIONS, oas)
insts = []
insts.append(inst)
fm = parser.OFPFlowMod(datapath,
0, 0,
0, # table 0
ofproto_v1_3.OFPFC_ADD,
0, 0,
0, # must have higher priority.
0xffffffff,
ofproto_v1_3.OFPP_ANY,
0xffffffff,
0,
match,
insts)
datapath.send_msg(fm)
print "#######"
print '''step 2. + Send pkt ip_dst=1.1.1.1,send to controller'''
print "#######"
###这里报文自己拿工具去发,推荐scapy
time.sleep(10)
print "#######"
print '''step 3_1.(Flow_mod) + Add Flow with buffer_id of 0x100'''
print "#######"
match = parser.OFPMatch()
match.set_dl_type(int(int(0x800)))
match.set_ipv4_dst(int(16843009))
oas = []
oa = parser.OFPActionOutput(6, ofproto_v1_3.OFPCML_MAX)
oas.append(oa)
inst = parser.OFPInstructionActions(ofproto_v1_3.OFPIT_APPLY_ACTIONS, oas)
insts = []
insts.append(inst)
fm = parser.OFPFlowMod(datapath,
0, 0,
0, # table 0
ofproto_v1_3.OFPFC_ADD,
0, 0,
10, # must have higher priority.
0x100,
ofproto_v1_3.OFPP_ANY,
0xffffffff,
0,
match,
insts)
datapath.send_msg(fm)
print "#######"
print '''step 3_2.(Packet_out) + Add Packet_out message with buffer_id of 0x100'''
print "#######"
oa = parser.OFPActionOutput(6, ofproto_v1_3.OFPCML_MAX)
oas = []
oas.append(oa)
#pkt="\x00\x00\x00\x01\x01\x01\x00\x00\x00\x02\x02\x02\x08\x00\x45\x111111111111111111111111111111111111"
po = parser.OFPPacketOut(datapath, 0x100, 0,
oas
)
datapath.send_msg(po)
总结思考
目前ovs针对报文buffer的做法是一个报文就对应一个buffer,不管是不是同一个报文,这样的坏处浪费了交换机buffer的资源,更加优化的做法是交换机收到相同报文只buffer一次并且只上送同一个buffer_id给控制器。