Sa1ka's Shelter

OpenFlow 协议学习

Word count: 2.4kReading time: 9 min
2019/12/06 Share

OpenFlow 1.0 协议总结

OpenFlow交换机中包含了1个以上的流表,流表又包含多个流表项。对于接收到的数据包,OpenFlow交换机在流表中选择一个最合适的流表项,并按流表项的内容对数据包进行处理。

流表中包含的流表项由以下3个基本要素组成:Head Field, Counter, Action。Head Field即匹配规则,Counter即匹配次数,Action即匹配后所需要采取的操作。

流表项

Head Field

  • Ingress Port
  • Ethernet source address
  • Ethernet destination address
  • Ethernet type
  • VLAN id
  • VLAN Priority(802.1q PCP)
  • IP source address
  • IP destination address
  • ToS
  • Transport source port/ICMP type
  • Transport destination port/ICMP code

在1.0版本中,STP(802.1d)数据包不执行流表匹配。完成STP处理后,执行“头字段解析之后”,会在此基础上进行数据包与流表的匹配。当数据包与刘表内设置的多条流表项匹配时,优先级最高的流表项即为匹配结果。OpenFlow中可以存在多个流表,但必须从流表0开始匹配。若编号为0的流表中不存在与数据包相匹配的流表项,而在其他流表中存在的话,则前进到下一流表中执行匹配。
数据包与OpenFlow交换机中任何一个流表项都不匹配的情况称之为Table-miss,这种情况下会利用Packet-In消息将数据包转发至控制器,或者丢弃。具体情况会根据设置决定。

Counter

4种计数器
Per Table, Per Port, Per Flow, Per Queue。

Per Table: Active Entries, Packet Lookups, Packet Matches

Per Flow: Received Packets/Bytes, Duration(msec/usec)

Per Queue: Transmit Packets/Bytes, Transmit Overrun Errors

Per Port: Received Packets/Bytes/Drops/Errors, Transmit Packets/Bytes/Drops/Errors, Receive Frame Alignment Errors, Received Overrun Errors, Received CRC Errors, Collisions

Action

Forward, Drop, Enqueue(Optional), Modify-Field(Optional)

Forward

名称 说明 虚拟端口
(port) 转发至指定端口 port
ALL 除接收端口以外的所有端口 0xfffc
CONTROLLER 控制器 0xfffd
LOCAL 发送至本地网络栈 0xfffe
TABLE 执行流表中行动(仅Packet-Out) 0xfff9
IN_PORT 从输入端口发出 0xfff8
NORMAL(optional) 传统L2或L3交换机动作 0xfffa
FLOOD(optional) 按照STP发送 0xfffb

Drop

未在协议中明确说明

Enqueue

在等待队列的末尾添加数据包

Modify-Field

  • 设置VLAN ID
  • 设置VLAN priority
  • 去掉VLAN头
  • 修改源MAC
  • 修改目的MAC
  • 修改源IP
  • 修改目的IP
  • 修改ToS
  • 修改源端口
  • 修改目的端口

控制器和交换机之间的安全通道

安全通道通过控制面网络来建立,不受OpenFlow交换机中的流表项的影响。规范中安全通道应该使用TLS实现,但在OpenFlow 1.1开始添加了TCP明文的方式,默认的TCP端口为6653。

1
2
3
4
5
6
struct openflow_header {
uint8_t version; // 版本,OpenFlow 1.0-1.3分别对应0x01-0x04
uint8_t type; // 消息类型,表明OpenFlow消息的类型
uint16_t length; // Byte数
uint32_t xid; // 事务ID
}

安全通道的建立过程

  1. 建立TCP/TLS连接
  2. 确定使用的OpenFlow版本,type字段值为OFPT_HELLO,在version中放入各自支持的最大版本号。
  3. 握手,type字段值为OFPT_FEATURES_REQUEST和OFPT_FEATURES_RESPONSE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct features_response {
uint64_t datapath_id; // datapath id唯一标识OpenFlow交换机
uint32_t n_buffer; // 缓存数据包的最大个数
uint8_t n_tables; // 支持的流表个数
char pad[24];
uint32_t capabilities; // 支持的容量
uint32_t actions; // 支持的行动
struct ofp_phy_port ports[??]; // 物理端口信息
}

struct ofp_phy_port {
uint16_t port_no; // 物理端口号
uint8_t hw_addr[OFP_ETH_LENGTH]; // 48位的以太网地址
uint8_t name[OFP_MAX_PORT_NAME_LEN]; // 128位的端口名称
uint32_t config; // 端口设置bitmap
uint32_t state; // 端口状态bitmap
uint32_t curr; // 当前功能
uint32_t advertised; // 广播功能
uint32_t supported; // 支持功能
uint32_t peer; // 连接方广播功能
}
  1. 交换设置(optional)。type为OFPT_SET_CONFIG/OFPT_GET_CONFIG。
1
2
3
4
5
6
7
8
9
10
11
struct set_config_message {
uint16_t flags; // IP碎片处理方法
uint16_t miss_send_len; // Table-miss数据包个数
}

enum set_config_flag {
OFPC_FRAG_NORMAL = 0, // 依据流表处理
OFPC_FRAG_DROP, // 丢弃
OFPC_FRAG_REASM, // 重组
OFPC_FRAG_MASK // unknown
}
  1. 其他可能交换的内容
    STATS,QUEUE_GET_CONFIG,Vendor, …

Flow-Mod

对于流表进行修改的消息,可以进行添加、删除和变更设置等操作。
种类包括以下几种

  1. OFPFC_ADD
  2. OFPFC_MODIFY
  3. OFPFC_MODIFY_STRICT
  4. OFPFC_DELETE
  5. OFPFC_DELETE_STRICT
    后面加上STRICT表示要完全匹配。如果有错误发生,那么将回复错误信息。如果在流表中存在相同项,那么会将原有计数器清零。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
struct flow_mod_message {
struct ofp_match match; // 数据包匹配信息
uint8_t cookie[64];
uint8_t action[??];

}

struct ofp_match {
uint32_t wildcards; // 无视哪个字段的通配符
uint16_t in_port; // 输入物理端口
uint8_t dl_src[OFP_ETH_ALEN]; // 源MAC地址
uint8_t dl_dst[OFP_ETH_ALEN]; // 目的MAC地址
uint16_t dl_vlan; // VLAN id
uint8_t dl_vlan_pcp; // VLAN 优先级
uint8_t pad1;
uint16_t dl_type; // 以太网帧类型
uint8_t nw_tos; // ToS字段
uint8_t nw_proto; // IP协议号
uint16_t pad2;
uint32_t nw_src; // 源IPv4地址
uint32_t nw_dst; // 目的IPv4地址
uint16_t tp_src; // 源端口或ICMP类型
uint16_t tp_dst; // 目的端口或ICMP代码
}

struct flow_mod_action {
uint16_t command; // 即上面提到的action种类
uint16_t idle_timeout; // 如果本条规则在idle_timeout时间内没有应用成功,那么将删除该规则
uint16_t hard_timeout; // 如果本条规则在hard_timeout时间内还未添加成功,那么将取消添加该规则。
uint16_t priority; // 规则优先级
uint32_t buffer_id; // 缓存ID
uint16_t out_port; // 输出端口号
uint16_t flags;
uint8_t actions[??]; // TLV结构,一个头部包含修改种类,后面接上具体的数据
}

Packet-In

Packet-In消息用于将到达OpenFlow交换机的数据包发送至OpenFlow交换机。根据交换机是否缓存数据包来设置buffer_id的值:不缓存则设置为-1,将整个数据包发送;缓存则会根据SET_CONFIG消息设置的miss_send_len为最大值的数据包发送,默认值为128。

1
2
3
4
5
6
7
8
struct packet_in_message {
uint32_t buffer_id; // 缓存ID
uint16_t total_len; // 帧长度
uint16_t in_port; // 接受帧端口
uint8_t reason; // 发送原因(不匹配0,流表指定1)
uint8_t pad;
uint8_t data[??]; // 具体数据包
}

Packet-Out

Packet-Out即为控制器向交换机发送的消息。

1
2
3
4
5
6
7
struct packet_out_message {
uint32_t buffer_id; // 缓存ID
uint16_t in_port; // 输入端口,用OFPP_NONE表示未指定,用OFPP_CONTROLLER表示是控制器创建的数据包
uint16_t actions_len;
uint8_t actions[actions_len]; // 类似于Flow-Mod时的action
uint8_t data[??]; // 当buffer_id为-1的时候,即不指定交换器缓存时
}

Port-Status

在OpenFlow交换机添加、删除或修改物理端口时,需要发送Port-Status消息来通知OpenFlow控制器。

1
2
3
4
5
struct port_status_message {
uint8_t reason; // OFPPR_ADD(0)、OFPPR_DELETE(1)、OFPPR_MODIFY(2)
uint8_t pad[56];
struct ofp_phy_port;
}

Flow-Removed

当OpenFlow交换机设置的流表项超时时,会向控制器发送Flow-Removed消息。

1
2
3
4
5
6
7
8
9
10
11
12
13
struct flow_remove_message {
struct ofp_match match;
uint8_t cookie[64];
uint16_t priority;
uint8_t reason; // OFPRR_IDLE_TIMEOUT(0)、OFPRR_HARD_TIMEOUT(1)、OFPRR_DELETE(2)
uint8_t pad;
uint32_t duration_sec; // 有效时间(秒)
uint32_t duration_nsec; // 纳秒
uint16_t idle_timeout;
uint8_t pad2;
uint64_t packet_count; // 数据包数
uint64_t byte_count; // 总字节数
}

大部分字段可以直接复制于Flow-Mod的信息。

Error

当在处理过程中出现错误的时候发送,控制器和交换机均可使用,type为OFPT_ERROR_MSG。

1
2
3
4
5
6
7
8
9
10
11
12
13
struct error_message {
uint16_t type;
uint16_t code;
uint8_t data[??];
}

enum error_type {
OFPET_HELLO_FAILED,
OFPET_BAD_REQUEST,
OFPET_BAD_ACTION,
OFPET_FLOW_MOD_FAILED,
OFPET_QUEUE_OP_FAILED
}

具体code参考OpenFlow说明书。

Barrier

用于双方对于事务的完成程度的通信,避免发生有顺序的事务因执行程度未达到而造成错误的情况。例如在发送了三条Flow-Mod信息,xid分别为1、2、3后,可以发送xid为4的Barrier请求,如果可以得到对方xid为4的Barrier响应,则表示前面的消息已经处理完毕。

Echo

用于测试两方的连接情况、通信延迟和通信带宽等。

LLDP

Link Layer Discovery Protocol

IEEE 802.1ab

许多的OpenFlow控制器利用LLDP来检测网络拓扑结构。一般来说,OpenFlow控制器会利用Packet-Out消息向OpenFlow交换机下达发送LLDP帧的命令,接下来利用接收到LLDP帧的OpenFlow交换机向控制器发送的Packet-In消息中得到拓扑消息,构建整个网络拓扑结构。

LLDP的机制

  • 每隔一段时间(标准中建议是30秒)向L2的组播地址发送LLDP帧,这个特性无视链路状态。
  • 发送源地址为发送设备的MAC地址。
  • 单向发送设备的识别号、端口识别号,这样接收方就可以得知发送设备和端口的信息。

LLDP使用三种全局性组播群地址

名称 数值 说明
Nearest Bridge 01-80-C2-00-00-0E 所有的桥和路由设备不转发
Nearest non-TPMR Bridge 01-80-C2-00-00-C3 跨越TPMR桥
Nearest Customer Bridge 01-80-C2-00-00-00 跨越TPMR和S-VLAN桥

一般来说,仅OpenFlow交换机构成的网络拓扑,会使用Nearest Bridge地址,而如果在中间存在有非OpenFlow的普通L2交换机则使用Nearest non-TPMR Bridge地址,借助WAN远程使用OpenFlow交换机则使用Nearest Customer Bridge地址。

1
2
3
-----------------------------------------------------
| 组播地址 | 设备的以太网地址 | 0x88cc(LLDP) | LLDPDU |
-----------------------------------------------------

OpenFlow 1.1 更新

头字段变更为匹配字段

在匹配字段中添加了MPLS标签、MPLS流量类别、元数据

多级流表

OpenFlow交换机可以设置多个流表,还可为一个数据包匹配多个流表项,但是在流表内还是只选择一个流表项

采用流水线处理的方式,新定义了“行动集”的概念,即将所有的行动统一添加到行动集中,但是执行顺序与计入顺序不相同,采用copy TTL inwards->pop->push->copy TTL outwards->decrement TTL->set->qos->group->output的方式,这里面每种类型仅能设置一个。

如果发生了Table-miss的情况,那么将根据OFPT_TABLE_MOD对流表的设置来决定,具体的方法有发送至控制器、前进到下一流表和丢弃。

CATALOG
  1. 1. OpenFlow 1.0 协议总结
    1. 1.1. 流表项
      1. 1.1.1. Head Field
      2. 1.1.2. Counter
      3. 1.1.3. Action
        1. 1.1.3.1. Forward
        2. 1.1.3.2. Drop
        3. 1.1.3.3. Enqueue
        4. 1.1.3.4. Modify-Field
    2. 1.2. 控制器和交换机之间的安全通道
      1. 1.2.1. 安全通道的建立过程
      2. 1.2.2. Flow-Mod
      3. 1.2.3. Packet-In
      4. 1.2.4. Packet-Out
      5. 1.2.5. Port-Status
      6. 1.2.6. Flow-Removed
      7. 1.2.7. Error
      8. 1.2.8. Barrier
      9. 1.2.9. Echo
  2. 2. LLDP
    1. 2.1. LLDP的机制
  3. 3. OpenFlow 1.1 更新
    1. 3.1. 头字段变更为匹配字段
    2. 3.2. 多级流表