diff --git a/openflow.pcap b/openflow.pcap new file mode 100644 index 0000000..4863863 Binary files /dev/null and b/openflow.pcap differ diff --git a/openflow/main_user_openflow.c b/openflow/main_user_openflow.c index 2441613..3158bbb 100644 --- a/openflow/main_user_openflow.c +++ b/openflow/main_user_openflow.c @@ -26,11 +26,11 @@ #include #include #include - -void nms_exec_action(u32 inport,u32 outport,struct eth_header *eth,int len,int hit_idx); -void pkt_print(u8 *data, u16 len); #include +extern void nms_exec_action(u32 inport,u32 outport,struct eth_header *eth,int len,int hit_idx); +extern void pkt_print(u8 *data, u16 len); + extern uintptr_t *flow_stats_addr; @@ -221,10 +221,10 @@ handle_ofpmsg_desc(struct ofp_buffer *ofpbuf) (struct ofp_buffer *)build_opfmsg_reply_ofpbuf(OFPT_MULTIPART_REPLY,ofpbuf->header.xid,reply_len); struct ofp_multipart *ofpmp_reply = (struct ofp_multipart *)ofpbuf_reply->data; - static const char *default_mfr_desc = "Wanglixuan"; - static const char *default_hw_desc = "Lixuan_OpenBox"; - static const char *default_sw_desc = "Lixuan_Driver"; - static const char *default_serial_desc = "Lixuan OpenBox Series"; + static const char *default_mfr_desc = "Chengjingyu_Networks"; + static const char *default_hw_desc = "Jingyu_OpenBox"; + static const char *default_sw_desc = "Jingyu_Driver"; + static const char *default_serial_desc = "Jingyu OpenBox Series"; static const char *default_dp_desc = "None"; ofpmp_reply->type = htons(OFPMP_DESC); diff --git a/openflow/main_user_openflow_analysis.md b/openflow/main_user_openflow_analysis.md new file mode 100644 index 0000000..62607d6 --- /dev/null +++ b/openflow/main_user_openflow_analysis.md @@ -0,0 +1,253 @@ +# `main_user_openflow.c` 函数功能详解 + +本文档详细介绍了 `main_user_openflow.c` 文件中每个函数的功能、参数和实现逻辑。该文件主要负责处理和响应来自控制器的各种 OpenFlow 1.3 (OFP13) 消息。 + +--- + +## 目录 +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) + +--- + +## 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. 程序将在此处循环等待和处理消息,直到被终止。 diff --git a/openflow/user_openflow b/openflow/user_openflow index 3207c72..b665453 100755 Binary files a/openflow/user_openflow and b/openflow/user_openflow differ