为什么 Flow Mod 消息需要使用 out_port 字段

如何灵活操纵 Flow Mod 消息,对掌握 OpenFlow 协议至关重要。在实验中使用最多是通过 flow_add 消息用来添加一条流表。在使用的时候很容易产生疑惑,为何需要填写 out_port 字段。

我第一次产生这样的疑问是在使用 Floodlight 的 J-Loxigen 操作生成流表的时候,当时的代码如下所示

OFFlowMod flowMod = sw.getOFFactory().buildFlowAdd()
.setIdleTimeout(0) //ForwardingBase.FLOWMOD_DEFAULT_IDLE_TIMEOUT)
.setHardTimeout(ForwardingBase.FLOWMOD_DEFAULT_HARD_TIMEOUT)
.setPriority(ForwardingBase.FLOWMOD_DEFAULT_PRIORITY + 1)
.setMatch(match)
.setActions(actionList)
//.setOutPort(outport)
.build();

为了生成一条流表,需要填写的 Time, Priority,Match,Actions 这些参数都很好理解,但是为何独独需要一个 OutPort 参数。如果这个参数指的是 packet 转发的 port 的话,那么和 Actions 里面的setOutPort 字段是什么关系?

我们回到 OpenFlow 协议来, Flow_Mod 消息需要的全部字段

/* Flow setup and teardown (controller -> datapath). */
struct ofp_flow_mod {
struct ofp_header header;
uint64_t cookie; /* Opaque controller-issued identifier. */
uint64_t cookie_mask; /* Mask used to restrict the cookie bits
that must match when the command is
OFPFC_MODIFY* or OFPFC_DELETE*. A value
of 0 indicates no restriction. */
uint8_t table_id; /* ID of the table to put the flow in.
For OFPFC_DELETE_* commands, OFPTT_ALL
can also be used to delete matching
flows from all tables. */
uint8_t command; /* One of OFPFC_*. */
uint16_t idle_timeout; /* Idle time before discarding (seconds). */
uint16_t hard_timeout; /* Max time before discarding (seconds). */
uint16_t priority; /* Priority level of flow entry. */
uint32_t buffer_id; /* Buffered packet to apply to, or
OFP_NO_BUFFER.
Not meaningful for OFPFC_DELETE*. */
uint32_t out_port; /* For OFPFC_DELETE* commands, require
matching entries to include this as an
output port. A value of OFPP_ANY
indicates no restriction. */
uint32_t out_group; /* For OFPFC_DELETE* commands, require
matching entries to include this as an
output group. A value of OFPG_ANY
indicates no restriction. */
uint16_t flags; /* Bitmap of OFPFF_* flags. */
uint16_t importance; /* Eviction precedence (optional). */
struct ofp_match match; /* Fields to match. Variable size. */
/* The variable size and padded match is always followed by instructions. */
//struct ofp_instruction_header instructions[0];
/* Instruction set – 0 or more. The length
of the instruction set is inferred from
the length field in the header. */
};
OFP_ASSERT(sizeof(struct ofp_flow_mod) == 56);

看了也不是太明白。我们看一下协议关于它的详细描述:

The out_port and out_group fields optionally filter the scope of OFPFC_DELETE and OFPFC_DELETE_STRICT messages by output port and group. If either out_port or out_group contains a value other than OFPP_ANY or OFPG_ANY respectively, it introduces a constraint when matching. This constraint is that the flow entry must contain an output action directed at that port or group. Other constraints such as ofp_match structs and priorities are still used; this is purely an additional constraint. Note that to disable output filtering, both out_port and out_group must be set to OFPP_ANY and OFPG_ANY respectively. These fields are ignored by OFPFC_ADD, OFPFC_MODIFY or OFPFC_MODIFY_STRICT messages.

其实看到这里,就应该很明白了。这个字段是专门为 flow_del 设计的。在使用 flow_add 或者 flow_modify 的时候会被忽略。当填写了这个字段的时候,flow_del 只会删除最终 action 会被转发到这个 port 的流表项

那么为何会这么设计哩?这样感觉一点也不优雅。

我们知道,在 OpenFlow 中,通过 优先级 priority 和 匹配域 match 唯一的确定一条流表,flow_del通过它们找到并删除对应的流表。但是当某个 port down 的时候,所有 action 转发到这个 port 的流表项已经都失去意义了,但是 match 中并没有需要转发到的 port 的信息,通过 port 在控制器端反查所有对应到这个 port 的流表项(优先级 priority 和 匹配域 match) 需要一定的计算,并且会生成的 flow_del 消息的聚合性不太好。所以通过 out_port 字段,可以实现快速处理 port down 的事件。

但是一点都不优雅。这个设计真是差。

感谢 谢磊 对本文做出的贡献