标签: OpenFlow

  • OpenFlow协议学习

    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。

    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
    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。
    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表示要完全匹配。如果有错误发生,那么将回复错误信息。如果在流表中存在相同项,那么会将原有计数器清零。
    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。

    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即为控制器向交换机发送的消息。

    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控制器。

    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消息。

    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。

    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地址。

    -----------------------------------------------------
    | 组播地址 | 设备的以太网地址 | 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对流表的设置来决定,具体的方法有发送至控制器、前进到下一流表和丢弃。