Files
NE_YuR/openflow/main_user_openflow_analysis.md
2025-11-12 09:01:03 +08:00

14 KiB
Raw Permalink Blame History

main_user_openflow.c 函数功能详解

本文档详细介绍了 main_user_openflow.c 文件中每个函数的功能、参数和实现逻辑。该文件主要负责处理和响应来自控制器的各种 OpenFlow 1.3 (OFP13) 消息。


目录


1. 辅助函数

htonll

static inline uint64_t htonll(uint64_t n)
  • 功能: 将64位无符号整数从主机字节序Host Byte Order转换网络字节序Network Byte Order
  • 参数:
    • n: 需要转换的64位无符号整数。
  • 返回值: 转换后的网络字节序64位整数。
  • 逻辑: 通过检查 htonl(1) 的结果来判断当前系统是否为大端序。如果是小端序则将高32位和低32位分别转换后交换位置。

ntohll

static inline uint64_t ntohll(uint64_t n)
  • 功能: 将64位无符号整数从网络字节序转换为主机字节序。
  • 参数:
    • n: 需要转换的64位网络字节序整数。
  • 返回值: 转换后的主机字节序64位整数。
  • 逻辑: 与 htonll 类似,根据系统的大小端情况进行相应的转换。

2. 报文构建函数

build_opfmsg_header

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

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_secduration_nsec,并为 packet_countbyte_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

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

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.chandle_openflow_callback 函数中并无直接处理 OFPT_FLOW_MOD 消息的 case 分支。这表明该功能可能未完全实现、在其他地方处理,或者 README.txt 中的文档领先于当前文件中的具体实现。

根据 main_user_openflow.c 中的可执行代码路径,只有 A 部分列出的消息类型被主回调循环主动处理。