# `main_user_openflow.c` 函数功能详解 本文档详细介绍了 `main_user_openflow.c` 文件中每个函数的功能、参数和实现逻辑。该文件主要负责处理和响应来自控制器的各种 OpenFlow 1.3 (OFP13) 消息。 --- ## 目录 - [`main_user_openflow.c` 函数功能详解](#main_user_openflowc-函数功能详解) - [目录](#目录) - [1. 辅助函数](#1-辅助函数) - [`htonll`](#htonll) - [`ntohll`](#ntohll) - [2. 报文构建函数](#2-报文构建函数) - [`build_opfmsg_header`](#build_opfmsg_header) - [`build_opfmsg_reply_ofpbuf`](#build_opfmsg_reply_ofpbuf) - [3. OpenFlow 消息处理函数](#3-openflow-消息处理函数) - [`handle_opfmsg_hello`](#handle_opfmsg_hello) - [`handle_opfmsg_features_request`](#handle_opfmsg_features_request) - [`handle_ofpmsg_get_config_request`](#handle_ofpmsg_get_config_request) - [`handle_ofpmsg_desc`](#handle_ofpmsg_desc) - [`handle_ofpmsg_flow_stats`](#handle_ofpmsg_flow_stats) - [`handle_ofpmsg_aggregate`](#handle_ofpmsg_aggregate) - [`handle_ofpmsg_table`](#handle_ofpmsg_table) - [`handle_ofpmsg_port_stats`](#handle_ofpmsg_port_stats) - [`handle_ofpmsg_group_features`](#handle_ofpmsg_group_features) - [`handle_ofpmsg_port_desc`](#handle_ofpmsg_port_desc) - [`handle_ofpmsg_packet_out`](#handle_ofpmsg_packet_out) - [`handle__opfmsg_role_request`](#handle__opfmsg_role_request) - [4. 核心回调与主函数](#4-核心回调与主函数) - [`handle_openflow_callback`](#handle_openflow_callback) - [`main`](#main) - [5. 已实现的协议类型总结](#5-已实现的协议类型总结) - [A. `handle_openflow_callback` 中处理的类型](#a-handle_openflow_callback-中处理的类型) - [B. 与 `README.txt` 的比较](#b-与-readmetxt-的比较) --- ## 1. 辅助函数 ### `htonll` ```c static inline uint64_t htonll(uint64_t n) ``` - **功能**: 将64位无符号整数从主机字节序(Host Byte Order)转换网络字节序(Network Byte Order)。 - **参数**: - `n`: 需要转换的64位无符号整数。 - **返回值**: 转换后的网络字节序64位整数。 - **逻辑**: 通过检查 `htonl(1)` 的结果来判断当前系统是否为大端序。如果是小端序,则将高32位和低32位分别转换后交换位置。 ### `ntohll` ```c static inline uint64_t ntohll(uint64_t n) ``` - **功能**: 将64位无符号整数从网络字节序转换为主机字节序。 - **参数**: - `n`: 需要转换的64位网络字节序整数。 - **返回值**: 转换后的主机字节序64位整数。 - **逻辑**: 与 `htonll` 类似,根据系统的大小端情况进行相应的转换。 --- ## 2. 报文构建函数 ### `build_opfmsg_header` ```c void build_opfmsg_header(struct ofp_header *ofpbuf_header, uint16_t len, uint8_t type, uint32_t xid) ``` - **功能**: 构建一个标准的 OpenFlow 报文头。 - **参数**: - `ofpbuf_header`: 指向 `ofp_header` 结构体的指针,用于填充报文头信息。 - `len`: 整个 OpenFlow 报文的总长度。 - `type`: OpenFlow 报文的类型 (例如 `OFPT_HELLO`, `OFPT_FEATURES_REPLY` 等)。 - `xid`: 事务ID (Transaction ID),用于匹配请求和响应。 - **逻辑**: 1. 设置版本号为 `OFP13_VERSION` (0x04)。 2. 使用 `htons` 将长度 `len` 转换网络字节序后填充。 3. 设置报文类型 `type`。 4. 设置事务ID `xid`。 ### `build_opfmsg_reply_ofpbuf` ```c u8 *build_opfmsg_reply_ofpbuf(uint8_t type, uint32_t xid, uint16_t len) ``` - **功能**: 分配内存并构建一个用于回复的 OpenFlow 报文缓冲区。 - **参数**: - `type`: 回复报文的类型。 - `xid`: 对应请求报文的事务ID。 - `len`: 回复报文的总长度。 - **返回值**: 指向新分配和初始化的报文缓冲区的指针 (`u8 *`)。 - **逻辑**: 1. 使用 `malloc` 分配指定长度 `len` 的内存。 2. 使用 `memset` 将分配的内存清零。 3. 调用 `build_opfmsg_header` 函数填充报文的头部信息。 4. 返回指向该缓冲区的指针。 --- ## 3. OpenFlow 消息处理函数 这些函数遵循 `handle_openflow_callback` 的规范,返回 `HANDLE` (0x1) 表示消息已处理,返回 `CONTINUE` (0x2) 表示未处理。 ### `handle_opfmsg_hello` - **对应消息**: `OFPT_HELLO` - **功能**: 处理与控制器建立连接时的 `HELLO` 消息。 - **逻辑**: 1. 检查接收到的 `HELLO` 消息中的 OpenFlow 版本号。 2. 如果版本号是 `0x04` (OpenFlow 1.3),则打印 "RECV HELLO!"。 3. 如果版本号不匹配,则构建一个 `OFPT_ERROR` 消息并发送给控制器,表明版本不兼容。 4. 返回 `HANDLE` 表示消息已处理。 ### `handle_opfmsg_features_request` - **对应消息**: `OFPT_FEATURES_REQUEST` - **功能**: 响应控制器查询交换机功能的请求。 - **逻辑**: 1. 构建一个 `OFPT_FEATURES_REPLY` 类型的回复报文。 2. 填充 `ofp_switch_features` 结构体,包含交换机的功能信息,如: - `datapath_id`: 交换机的唯一标识符。 - `n_buffers`: 交换机可以缓存的数据包数量。 - `n_tables`: 支持的流表数量(此处硬编码为1)。 - `capabilities`: 支持的特性(如流统计、端口统计等)。 3. 发送该回复报文给控制器。 4. 返回 `HANDLE`。 ### `handle_ofpmsg_get_config_request` - **对应消息**: `OFPT_GET_CONFIG_REQUEST` - **功能**: 响应控制器查询交换机配置的请求。 - **逻辑**: 1. 构建一个 `OFPT_GET_CONFIG_REPLY` 类型的回复报文。 2. 填充 `ofp_switch_config` 结构体,包含交换机的配置信息,如: - `flags`: 配置标志。 - `miss_send_len`: 当发生 table-miss 时,发送到控制器的报文最大长度。 3. 发送该回复报文。 4. 返回 `HANDLE`。 ### `handle_ofpmsg_desc` - **对应消息**: `OFPT_MULTIPART_REQUEST` (子类型 `OFPMP_DESC`) - **功能**: 响应控制器查询交换机描述信息的请求。 - **逻辑**: 1. 构建一个 `OFPT_MULTIPART_REPLY` 类型的回复报文。 2. 设置 multipart 类型为 `OFPMP_DESC`。 3. 填充 `ofp_desc_stats` 结构体,包含制造商、硬件、软件、序列号等描述信息(此处使用硬编码的字符串)。 4. 发送该回复报文。 5. 返回 `HANDLE`。 ### `handle_ofpmsg_flow_stats` - **对应消息**: `OFPT_MULTIPART_REQUEST` (子类型 `OFPMP_FLOW`) - **功能**: 响应控制器查询流表统计信息的请求。 - **逻辑**: 1. 遍历全局流表统计地址数组 `flow_stats_addr`,计算所有有效流表项的总长度。 2. 构建一个足够大的 `OFPT_MULTIPART_REPLY` 回复报文。 3. 设置 multipart 类型为 `OFPMP_FLOW`。 4. 再次遍历 `flow_stats_addr`,将每个有效的流表统计信息 (`ofp_flow_stats`) 拷贝到回复报文中。 5. 发送该回复报文。 6. 返回 `HANDLE`。 ### `handle_ofpmsg_aggregate` - **对应消息**: `OFPT_MULTIPART_REQUEST` (子类型 `OFPMP_AGGREGATE`) - **功能**: 响应控制器查询聚合统计信息(总流数、总包数、总字节数)的请求。 - **逻辑**: 1. 构建一个 `OFPT_MULTIPART_REPLY` 回复报文,类型为 `OFPMP_AGGREGATE`。 2. 初始化 `flow_count`, `packet_count`, `byte_count` 为 0。 3. 遍历 `flow_stats_addr`,累加每个流的包计数和字节计数,并统计流的数量。 - **注意**: 此处代码会更新每个流的 `duration_sec` 和 `duration_nsec`,并为 `packet_count` 和 `byte_count` 填充了硬编码的示例值。 4. 将聚合统计结果填充到 `ofp_aggregate_stats_reply` 结构体中。 5. 发送该回复报文。 6. 返回 `HANDLE`。 ### `handle_ofpmsg_table` - **对应消息**: `OFPT_MULTIPART_REQUEST` (子类型 `OFPMP_TABLE`) - **功能**: 响应控制器查询流表自身统计信息的请求。 - **逻辑**: 1. 构建一个 `OFPT_MULTIPART_REPLY` 回复报文,类型为 `OFPMP_TABLE`。 2. 填充 `ofp_table_stats` 结构体,包含活动流表项数量、查询次数、匹配次数等信息(此处硬编码为1个流表,活动数量为1)。 3. 发送该回复报文。 4. 返回 `HANDLE`。 ### `handle_ofpmsg_port_stats` - **对应消息**: `OFPT_MULTIPART_REQUEST` (子类型 `OFPMP_PORT_STATS`) - **功能**: 响应控制器查询端口统计信息的请求。 - **逻辑**: 1. 根据全局端口信息结构体 `nmps` 中的端口数量 `cnt`,构建一个 `OFPT_MULTIPART_REPLY` 回复报文。 2. 设置 multipart 类型为 `OFPMP_PORT_STATS`。 3. 遍历所有端口,将每个端口的统计信息 `nmps.ports[i].stats` 拷贝到回复报文中,并计算和填充端口的活动时间。 4. 发送该回复报文。 5. 返回 `HANDLE`。 ### `handle_ofpmsg_group_features` - **对应消息**: `OFPT_MULTIPART_REQUEST` (子类型 `OFPMP_GROUP_FEATURES`) - **功能**: 响应控制器查询交换机组表(Group Table)特性的请求。 - **逻辑**: 1. 构建一个 `OFPT_MULTIPART_REPLY` 回复报文。 2. 填充 `ofp_group_features` 结构体,设置其类型为 `OFPMP_GROUP_FEATURES`。 3. 发送该回复报文。 4. 返回 `HANDLE`。 ### `handle_ofpmsg_port_desc` - **对应消息**: `OFPT_MULTIPART_REQUEST` (子类型 `OFPMP_PORT_DESC`) - **功能**: 响应控制器查询端口描述信息的请求。 - **逻辑**: 1. 根据全局端口信息结构体 `nmps` 中的端口数量 `cnt`,构建一个 `OFPT_MULTIPART_REPLY` 回复报文。 2. 设置 multipart 类型为 `OFPMP_PORT_DESC`。 3. 遍历所有端口,将每个端口的状态描述信息 `nmps.ports[i].state` 拷贝到回复报文中。 4. 发送该回复报文。 5. 返回 `HANDLE`。 ### `handle_ofpmsg_packet_out` - **对应消息**: `OFPT_PACKET_OUT` - **功能**: 处理控制器下发的 `Packet Out` 消息,该消息指示交换机将特定数据包通过指定端口发送出去。 - **逻辑**: 1. 解析 `ofp_packet_out` 消息,提取出 `in_port`(入端口)、`actions_len`(动作列表长度)以及附带的数据包(`eth`)。 2. 检查 `actions_len`: - 如果为 0,表示没有指定动作,默认执行泛洪(`OFPP_FLOOD`)。 - 如果不为 0,则遍历动作列表。 3. 对于 `OFPAT_OUTPUT` 类型的动作,提取 `port`(出端口),并调用 `nms_exec_action` 函数将数据包从指定端口发送出去。 4. 返回 `HANDLE`。 ### `handle__opfmsg_role_request` - **对应消息**: `OFPT_ROLE_REQUEST` - **功能**: 处理控制器设置或查询交换机角色的请求(如 Master, Slave)。 - **逻辑**: 1. 构建一个 `OFPT_ROLE_REPLY` 类型的回复报文。 2. 将请求中的角色信息 (`ofp_role`) 直接拷贝到回复报文中。 3. 发送该回复报文。 4. 返回 `HANDLE`。 --- ## 4. 核心回调与主函数 ### `handle_openflow_callback` ```c int handle_openflow_callback(struct ofp_buffer *ofpbuf, int len) ``` - **功能**: 这是注册给 OpenFlow 库的核心回调函数,作为所有进入的 OpenFlow 消息的分发中枢。 - **参数**: - `ofpbuf`: 指向包含 OpenFlow 消息的缓冲区的指针。 - `len`: 消息的总长度。 - **返回值**: - `HANDLE` (0x1): 如果消息被此回调中的某个函数处理了。 - `CONTINUE` (0x2): 如果消息类型不被支持或未被处理,交由后续处理。 - **逻辑**: 1. 从报文头中提取消息类型 `oftype`。 2. 使用 `switch` 语句根据 `oftype` 将消息分发给对应的 `handle_...` 函数。 3. 对于 `OFPT_MULTIPART_REQUEST` 类型的消息,会进一步检查其子类型(`multipart->type`),并分发给相应的处理函数(如 `handle_ofpmsg_desc`, `handle_ofpmsg_flow_stats` 等)。 4. 如果 `switch` 语句中没有匹配的类型,则打印未处理信息并返回 `CONTINUE`。 ### `main` ```c int main(int argc, char* argv[]) ``` - **功能**: 程序的主入口点。 - **逻辑**: 1. 调用 `ofp_init(argc, argv)` 初始化 OpenFlow 环境。 2. 定义一个 `mask` 变量,用于指定程序希望监听和处理哪些类型的 OpenFlow 消息。 3. 通过位或运算 (`|`) 将多个消息类型的掩码(如 `MASK_HELLO`, `MASK_FEATURES_REQUEST` 等)组合起来,赋值给 `mask`。 4. 调用 `openflow_hook_init(mask, handle_openflow_callback)`,将 `mask` 和核心回调函数 `handle_openflow_callback` 注册到 OpenFlow 库。这样,只有 `mask` 中指定类型的消息到达时,`handle_openflow_callback` 才会被调用。 5. 调用 `pause()`,使程序进入挂起状态,等待网络事件(即等待 OpenFlow 消息的到来)。 6. 程序将在此处循环等待和处理消息,直到被终止。 --- ## 5. 已实现的协议类型总结 ### A. `handle_openflow_callback` 中处理的类型 `main_user_openflow.c` 文件中的 `handle_openflow_callback` 函数是主要的消息分发器。它直接处理以下消息类型: - **`OFPT_HELLO`**: 管理与控制器的初始握手过程。 - **`OFPT_FEATURES_REQUEST`**: 响应控制器,提供交换机的功能特性。 - **`OFPT_GET_CONFIG_REQUEST`**: 响应控制器,提供交换机的当前配置。 - **`OFPT_PACKET_OUT`**: 处理控制器下发的数据包,并从交换机指定端口发出。 - **`OFPT_ROLE_REQUEST`**: 处理控制器的角色变更请求(如 Master/Slave)。 - **`OFPT_MULTIPART_REQUEST`**: 用于请求各种统计和状态信息的复合类型。已实现的子类型包括: - **`OFPMP_DESC`**: 交换机的硬件/软件描述信息。 - **`OFPMP_FLOW`**: 单个流的统计信息。 - **`OFPMP_AGGREGATE`**: 聚合统计信息(总流数、包数、字节数)。 - **`OFPMP_TABLE`**: 流表的统计信息。 - **`OFPMP_PORT_STATS`**: 物理或逻辑端口的统计信息。 - **`OFPMP_GROUP_FEATURES`**: 组表的特性。 - **`OFPMP_PORT_DESC`**: 端口的描述信息。 ### B. 与 `README.txt` 的比较 `README.txt` 文件提供了一个相似的列表,但存在一个显著差异: - **`OFPT_FLOW_MOD` (类型 14)**: `README.txt` 文件列出了用于添加流规则的 `OFPT_FLOW_MOD`(记录为 `OFPT_FLOW=14`)。虽然 `README` 提到了用于此目的的 `fast_add_rule` 函数,但在 `main_user_openflow.c` 的 `handle_openflow_callback` 函数中**并无**直接处理 `OFPT_FLOW_MOD` 消息的 `case` 分支。这表明该功能可能未完全实现、在其他地方处理,或者 `README.txt` 中的文档领先于当前文件中的具体实现。 根据 `main_user_openflow.c` 中的可执行代码路径,只有 A 部分列出的消息类型被主回调循环主动处理。