Files
NE_YuR/openbox/openbox_1.txt
2025-11-21 01:38:25 +08:00

463 lines
22 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#set page(header: [
#set par(spacing: 6pt)
#align(center)[#text(size: 11pt)[《网络工程》实验报告]]
#v(-0.3em)
#line(length: 100%, stroke: (thickness: 1pt))
],)
#show heading: it => box(width: 100%)[
#v(0.50em)
#set text(font: hei)
#it.body
]
#outline(title: "目录",depth: 3, indent: 1em)
#pagebreak()
#outline(
title: [图目录],
target: figure.where(kind: image),
)
#show heading: it => box(width: 100%)[
#v(0.50em)
#set text(font: hei)
#counter(heading).display()
// #h(0.5em)
#it.body
]
#set enum(indent: 0.5em,body-indent: 0.5em,)
#pagebreak()
= 实验概述
== 实验内容
#para[
本次实验中有三个小实验:
+ 基于Openbox-S4测试SDN交换功能
+ 基于SDN 交换机源码处理openflow协议消息
+ 实验三 使用 REST API 接口查询交换机的相关功能数据
本实验报告重点分析第二个小实验的实现细节。
]
== 实验要求
#para[
1. 熟悉Openbox-S4的基本功能
2. 熟悉OpenFlow协议的基本消息格式
3. 熟悉OpenFlow协议的基本消息处理流程
4. 熟悉REST API接口的基本使用
]
// Display inline code in a small box
// that retains the correct baseline.
#show raw.where(block: false): it => box(
text(font: ("Consolas","FangSong_GB2312"), it),
fill: luma(240),
inset: (x: 3pt, y: 0pt),
outset: (y: 3pt),
radius: 2pt,
)
// Display block code in a larger block
// with more padding.
#show raw.where(block: true): it => block(
text(font: ("Consolas","FangSong_GB2312"), it),
fill: luma(240),
inset: 10pt,
radius: 4pt,
width: 100%,
)
= 编写SDN交换机源码处理OpenFlow协议消息
#para[
本次实验在`main_user_openflow.c`文件中实现了OpenFlow协议处理逻辑。代码通过多个函数处理不同类型的OpenFlow消息包括Hello消息、Features Request消息、Flow Stats Request消息等。每个函数都根据消息类型生成相应的回复消息并通过`send_openflow_message`函数发送给控制器。以下将逐一分析每个函数的实现细节。
]
== 本次实验所编写的协议
#para[
本次实验实现了OpenFlow协议中的大部分消息处理功能。实验代码涵盖了从交换机特性查询到流表统计、端口统计等多种消息类型的处理。以下是实验编写协议的总结。
]
=== 实现的协议
#para[
- OFPT_FEATURES_REQUEST用于获取交换机支持的流表数量、缓冲区大小等特性信息。对应的处理函数为`handle_opfmsg_features_request`。
- OFPT_GET_CONFIG_REQUEST用于查询交换机的配置信息如Miss Send Length等。对应的处理函数为`handle_ofpmsg_get_config_request`。
- OFPT_MULTIPART_REQUEST用于处理多种统计信息请求包括交换机描述信息、流表信息、端口统计信息等。对应的处理函数包括
- `handle_ofpmsg_desc`
- `handle_ofpmsg_flow_stats`
- `handle_ofpmsg_aggregate`
- `handle_ofpmsg_table`
- `handle_ofpmsg_port_stats`
- `handle_ofpmsg_group_features`
- `handle_ofpmsg_port_desc`
- OFPT_PACKET_OUT用于处理控制器发送的数据包并根据动作指示将数据包从指定端口发送出去。对应的处理函数为`handle_ofpmsg_packet_out`。
- OFPT_ROLE_REQUEST用于配置交换机的角色如主控制器、从控制器等。对应的处理函数为`handle__opfmsg_role_request`。
]
=== 组内分工
#para[
本次实验由组内成员共同完成,每个成员负责不同的消息处理函数。以下是每个成员负责的函数列表。
#align(center)[#table(
columns: (55pt, auto),
rows: 15pt,
inset: 3pt,
align: horizon+center,
table.header(
[分工], [函数]
),
table.cell(rowspan: 7)[王李烜],
"handle_opfmsg_features_request",
"handle_ofpmsg_desc",
"handle_ofpmsg_flow_stats",
"handle_ofpmsg_aggregate",
"handle_ofpmsg_table",
"handle_ofpmsg_port_desc",
"handle_opfmsg_role_request",
table.cell(rowspan: 2)[廖中煜],
"handle_ofpmsg_get_config_request",
"handle_ofpmsg_packet_out",
table.cell(rowspan: 2)[王誉潞],
"handle_ofpmsg_port_stats",
"handle_ofpmsg_group_features",
)]
]
== 相关数据结构
#para[
在`main_user_openflow.c`中定义了一些关键的数据结构和全局变量用于处理OpenFlow协议的消息。主要的数据结构包括`ofp_header`、`ofp_switch_features`、`ofp_flow_stats`和`ofp_port_stats`等。这些数据结构用于处理OpenFlow协议中的不同消息类型如交换机特性请求、流表统计请求、端口统计请求等。
]
#para[
`ofp_header`结构体用于表示OpenFlow消息头包含协议版本、消息类型、消息长度和事务ID等字段。`ofp_switch_features`结构体用于表示交换机的特性信息如数据路径ID、缓冲区数量、流表数量等。`ofp_flow_stats`结构体用于表示流表统计信息,如流表项长度、优先级、数据包计数等。`ofp_port_stats`结构体用于表示端口统计信息,如端口号、接收数据包数、发送数据包数等。
]
== 消息头构建
#para[
在OpenFlow协议中每个消息都有一个消息头用于标识消息的版本、类型、长度和事务ID。代码中实现了`build_opfmsg_header`函数用于构建OpenFlow消息头。该函数接收消息长度、消息类型和事务ID作为参数并填充消息头的各个字段。通过调用该函数可以确保消息能够被正确解析和处理。
]
== 消息处理函数
#para[
代码中实现了多个OpenFlow消息处理函数每个函数对应一种OpenFlow消息类型。以下是每个函数的详细分析。
]
=== 处理Hello消息
#para[
`handle_opfmsg_hello`函数用于处理控制器发送的Hello消息。Hello消息是OpenFlow协议中的基础消息用于交换控制器和交换机之间的协议版本信息。函数首先检查消息头中的`version`字段判断协议版本是否为1.3。如果版本匹配则打印接收到的Hello消息否则生成一个错误消息并发送给控制器。函数返回`HANDLE`表示消息已处理。
```c
static enum ofperr handle_opfmsg_hello(struct ofp_buffer *ofpbuf) {
if (ofpbuf->header.version == 0x04) {
printf("RECV HELLO!\n\n\n");
} else {
struct ofp_buffer *ofpbuf_reply = (struct ofp_buffer *)build_reply_ofpbuf(OFPT_ERROR, ofpbuf->header.xid, sizeof(struct ofp_header));
send_openflow_message(ofpbuf_reply, sizeof(struct ofp_header));
}
return HANDLE;
}
```
]
=== 处理Features Request消息
#para[
`handle_opfmsg_features_request`函数用于处理控制器发送的Features Request消息。该消息用于查询交换机的特性信息如支持的流表数量、缓冲区大小等。函数生成一个Features Reply消息并填充交换机的特性信息如数据路径ID、缓冲区数量、流表数量等。通过`send_openflow_message`函数将回复消息发送给控制器,并返回`HANDLE`表示消息已处理。
```c
static enum ofperr handle_opfmsg_features_request(struct ofp_buffer *ofpbuf) {
int feature_reply_len = sizeof(struct ofp_switch_features) + sizeof(struct ofp_header);
struct ofp_buffer *ofpbuf_reply = (struct ofp_buffer *)build_opfmsg_reply_ofpbuf(OFPT_FEATURES_REPLY, ofpbuf->header.xid, feature_reply_len);
struct ofp_switch_features *feature_reply_msg = (struct ofp_switch_features *)ofpbuf_reply->data;
feature_reply_msg->datapath_id = 0x0100000000000000;
feature_reply_msg->n_buffers = htonl(46);
feature_reply_msg->n_tables = 3;
feature_reply_msg->capabilities = 0x7;
send_openflow_message(ofpbuf_reply, feature_reply_len);
return HANDLE;
}
```
]
=== 处理Get Config Request消息
#para[
`handle_ofpmsg_get_config_request`函数用于处理控制器发送的Get Config Request消息。该消息用于查询交换机的配置信息如Miss Send Length等。函数生成一个Get Config Reply消息并填充交换机的配置信息。通过`send_openflow_message`函数将回复消息发送给控制器,并返回`HANDLE`表示消息已处理。
```c
static enum ofperr handle_ofpmsg_get_config_request(struct ofp_buffer *ofpbuf) {
int reply_len = sizeof(struct ofp_switch_config) + sizeof(struct ofp_header);
struct ofp_buffer *ofpbuf_reply = (struct ofp_buffer *)build_reply_ofpbuf(OFPT_GET_CONFIG_REPLY, ofpbuf->header.xid, reply_len);
struct ofp_switch_config *switch_config_reply = (struct ofp_switch_config *)ofpbuf_reply->data;
switch_config_reply->flags = htons(0x0000);
switch_config_reply->miss_send_len = htons(32);
send_openflow_message(ofpbuf_reply, reply_len);
return HANDLE;
}
```
]
=== 处理Description Request消息
#para[
`handle_ofpmsg_desc`函数用于处理控制器发送的Description Request消息。该消息用于查询交换机的描述信息如制造商、硬件版本等。函数生成一个Multipart Reply消息并填充交换机的描述信息。通过`send_openflow_message`函数将回复消息发送给控制器,并返回`HANDLE`表示消息已处理。
```c
static enum ofperr handle_ofpmsg_desc(struct ofp_buffer *ofpbuf) {
int reply_len = sizeof(struct ofp_header) + sizeof(struct ofp_multipart) + sizeof(struct ofp_desc_stats);
struct ofp_buffer *ofpbuf_reply = (struct ofp_buffer *)build_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_dp_desc = "None";
ofpmp_reply->type = htons(OFPMP_DESC);
ofpmp_reply->flags = htonl(OFPMP_REPLY_MORE_NO);
snprintf(ofpmp_reply->ofpmp_desc[0].mfr_desc, sizeof ofpmp_reply->ofpmp_desc[0].mfr_desc, "%s", default_mfr_desc);
snprintf(ofpmp_reply->ofpmp_desc[0].hw_desc, sizeof ofpmp_reply->ofpmp_desc[0].hw_desc, "%s", default_hw_desc);
snprintf(ofpmp_reply->ofpmp_desc[0].sw_desc, sizeof ofpmp_reply->ofpmp_desc[0].sw_desc, "%s", default_sw_desc);
snprintf(ofpmp_reply->ofpmp_desc[0].serial_num, sizeof ofpmp_reply->ofpmp_desc[0].serial_num, "%s", default_serial_desc);
snprintf(ofpmp_reply->ofpmp_desc[0].dp_desc, sizeof ofpmp_reply->ofpmp_desc[0].dp_desc, "%s", default_dp_desc);
send_openflow_message(ofpbuf_reply, reply_len);
return HANDLE;
}
```
]
=== 处理Flow Stats Request消息
#para[
`handle_ofpmsg_flow_stats`函数用于处理控制器发送的Flow Stats Request消息。该消息用于查询交换机的流表统计信息。函数生成一个Multipart Reply消息并设置消息类型为Flow Stats。通过`send_openflow_message`函数将回复消息发送给控制器,并返回`HANDLE`表示消息已处理。
```c
static enum ofperr handle_ofpmsg_flow_stats(struct ofp_buffer *ofpbuf) {
int reply_len = sizeof(struct ofp_header) + sizeof(struct ofp_multipart);
struct ofp_buffer *reply_buffer = (struct ofp_buffer *)build_reply_ofpbuf(OFPT_MULTIPART_REPLY, ofpbuf->header.xid, reply_len);
struct ofp_multipart *multipart_reply = (struct ofp_multipart *)reply_buffer->data;
multipart_reply->type = htons(OFPMP_FLOW);
multipart_reply->flags = htonl(OFPMP_REPLY_MORE_NO);
send_openflow_message(reply_buffer, reply_len);
return HANDLE;
}
```
]
=== 处理Aggregate Stats Request消息
#para[
`handle_ofpmsg_aggregate`函数用于处理控制器发送的Aggregate Stats Request消息。该消息用于查询交换机的聚合统计信息。函数生成一个Multipart Reply消息并填充聚合统计信息如数据包计数、字节计数等。通过`send_openflow_message`函数将回复消息发送给控制器,并返回`HANDLE`表示消息已处理。
```c
static enum ofperr handle_ofpmsg_aggregate(struct ofp_buffer *ofpbuf) {
int reply_len = sizeof(struct ofp_header) + sizeof(struct ofp_multipart) + sizeof(struct ofp_aggregate_stats_reply);
struct ofp_buffer *ofpbuf_reply = (struct ofp_buffer *)build_reply_ofpbuf(OFPT_MULTIPART_REPLY, ofpbuf->header.xid, reply_len);
struct ofp_multipart *ofpmp_reply = (struct ofp_multipart *)ofpbuf_reply->data;
ofpmp_reply->type = htons(OFPMP_AGGREGATE);
ofpmp_reply->flags = htonl(OFPMP_REPLY_MORE_NO);
ofpmp_reply->ofpmp_aggregate_reply[0].packet_count = htonll(46);
ofpmp_reply->ofpmp_aggregate_reply[0].byte_count = htonll(2025);
ofpmp_reply->ofpmp_aggregate_reply[0].flow_count = htonll(200);
send_openflow_message(ofpbuf_reply, reply_len);
return HANDLE;
}
```
]
=== 处理Table Stats Request消息
#para[
`handle_ofpmsg_table`函数用于处理控制器发送的Table Stats Request消息。该消息用于查询交换机的流表统计信息。函数生成一个Multipart Reply消息并填充流表统计信息如匹配计数、查找计数等。通过`send_openflow_message`函数将回复消息发送给控制器,并返回`HANDLE`表示消息已处理。
```c
static enum ofperr handle_ofpmsg_table(struct ofp_buffer *ofpbuf) {
int reply_len = sizeof(struct ofp_header) + sizeof(struct ofp_multipart) + sizeof(struct ofp_table_stats) * 1;
struct ofp_buffer *ofpbuf_reply = (struct ofp_buffer *)build_reply_ofpbuf(OFPT_MULTIPART_REPLY, ofpbuf->header.xid, reply_len);
struct ofp_multipart *ofpmp_reply = (struct ofp_multipart *)ofpbuf_reply->data;
ofpmp_reply->type = htons(OFPMP_TABLE);
ofpmp_reply->flags = htonl(OFPMP_REPLY_MORE_NO);
ofpmp_reply->table_stats[0].matched_count = htonll(2025);
ofpmp_reply->table_stats[0].table_id = 0;
ofpmp_reply->table_stats[0].lookup_count = htonll(46);
ofpmp_reply->table_stats[0].active_count = htonl(1);
send_openflow_message(ofpbuf_reply, reply_len);
return HANDLE;
}
```
]
=== 处理Port Stats Request消息
#para[
`handle_ofpmsg_port_stats`函数用于处理控制器发送的Port Stats Request消息。该消息用于查询交换机的端口统计信息。函数生成一个Multipart Reply消息并填充端口统计信息如持续时间、接收数据包数等。通过`send_openflow_message`函数将回复消息发送给控制器,并返回`HANDLE`表示消息已处理。
```c
static enum ofperr handle_ofpmsg_port_stats(struct ofp_buffer *ofpbuf) {
int reply_len = sizeof(struct ofp_header) + sizeof(struct ofp_multipart) + sizeof(struct ofp_port_stats) * nmps.cnt;
struct ofp_buffer *ofpbuf_reply = (struct ofp_buffer *)build_reply_ofpbuf(OFPT_MULTIPART_REPLY, ofpbuf->header.xid, reply_len);
struct ofp_multipart *ofpmp_reply = (struct ofp_multipart *)ofpbuf_reply->data;
ofpmp_reply->type = htons(OFPMP_PORT_STATS);
ofpmp_reply->flags = htonl(OFPMP_REPLY_MORE_NO);
for (int i = 0; i < nmps.cnt; i++) {
ofpmp_reply->ofpmp_port_stats[i] = nmps.ports[i].stats;
ofpmp_reply->ofpmp_port_stats[i].duration_sec = htonl(2025);
ofpmp_reply->ofpmp_port_stats[i].duration_nsec = htonl(51);
}
send_openflow_message(ofpbuf_reply, reply_len);
return HANDLE;
}
```
]
=== 处理Group Features Request消息
#para[
`handle_ofpmsg_group_features`函数用于处理控制器发送的Group Features Request消息。该消息用于查询交换机的组表特性信息。函数生成一个Multipart Reply消息并填充组表特性信息如最大组数等。通过`send_openflow_message`函数将回复消息发送给控制器,并返回`HANDLE`表示消息已处理。
```c
static enum ofperr handle_ofpmsg_group_features(struct ofp_buffer *ofpbuf) {
int reply_len = sizeof(struct ofp_header) + sizeof(struct ofp_group_features) + 8;
struct ofp_buffer *ofpbuf_reply = (struct ofp_buffer *)build_reply_ofpbuf(OFPT_MULTIPART_REPLY, ofpbuf->header.xid, reply_len);
struct ofp_group_features *group = (struct ofp_group_features *)ofpbuf_reply->data;
group->types = htons(OFPMP_GROUP_FEATURES);
group->max_groups[0] = htonl(0xdeadbeef);
send_openflow_message(ofpbuf_reply, reply_len);
return HANDLE;
}
```
]
=== 处理Port Description Request消息
#para[
`handle_ofpmsg_port_desc`函数用于处理控制器发送的Port Description Request消息。该消息用于查询交换机的端口描述信息。函数生成一个Multipart Reply消息并填充端口描述信息如端口状态等。通过`send_openflow_message`函数将回复消息发送给控制器,并返回`HANDLE`表示消息已处理。
```c
static enum ofperr handle_ofpmsg_port_desc(struct ofp_buffer *ofpbuf) {
int reply_len = sizeof(struct ofp_header) + sizeof(struct ofp_multipart) + sizeof(struct ofp_port) * nmps.cnt;
struct ofp_buffer *ofpbuf_reply = (struct ofp_buffer *)build_reply_ofpbuf(OFPT_MULTIPART_REPLY, ofpbuf->header.xid, reply_len);
struct ofp_multipart *ofpmp_reply = (struct ofp_multipart *)ofpbuf_reply->data;
ofpmp_reply->type = htons(OFPMP_PORT_DESC);
ofpmp_reply->flags = htonl(OFPMP_REPLY_MORE_NO);
for (int i = 0; i < nmps.cnt; i++) {
ofpmp_reply->ofpmp_port_desc[i] = nmps.ports[i].state;
}
send_openflow_message(ofpbuf_reply, reply_len);
return HANDLE;
}
```
]
=== 处理Packet Out消息
#para[
`handle_ofpmsg_packet_out`函数用于处理控制器发送的Packet Out消息。该消息用于指示交换机将数据包从指定端口发送出去。函数解析消息中的动作列表判断动作类型是否为`OFPAT_OUTPUT`,并根据动作指示的端口号调用`nms_exec_action`函数将数据包发送出去。函数返回`HANDLE`表示消息已处理。
```c
static enum ofperr handle_ofpmsg_packet_out(struct ofp_buffer *ofpbuf) {
struct ofp_packet_out *out = (struct ofp_packet_out *)ofpbuf;
struct ofp_action_output *action = (struct ofp_action_output *)&out->actions[0];
int action_len = ntohs(out->actions_len);
struct eth_header *eth = (struct eth_header *)&ofpbuf->data[sizeof(struct ofp_packet_out) - sizeof(struct ofp_header) + action_len];
int send_len = ntohs(ofpbuf->header.length) - sizeof(struct ofp_packet_out) - action_len;
if (action_len == 0) {
nms_exec_action(ntohl(out->in_port), OFPP_FLOOD, eth, send_len, -1);
} else {
while (action_len > 0) {
if (action->type == OFPAT_OUTPUT) {
nms_exec_action(ntohl(out->in_port), ntohl(action->port), eth, send_len, -1);
}
action_len -= sizeof(struct ofp_action_output);
action++;
}
}
return HANDLE;
}
```
]
=== 处理Role Request消息
#para[
`handle_opfmsg_role_request`函数用于处理控制器发送的Role Request消息。该消息用于配置交换机的角色如主控制器、从控制器等。函数生成一个Role Reply消息并填充角色配置信息。通过`send_openflow_message`函数将回复消息发送给控制器,并返回`HANDLE`表示消息已处理。
```c
static enum ofperr handle_opfmsg_role_request(struct ofp_buffer *ofpbuf) {
int reply_len = sizeof(struct ofp_header) + sizeof(struct ofp_role);
struct ofp_buffer *ofpbuf_reply = (struct ofp_buffer *)build_reply_ofpbuf(OFPT_ROLE_REPLY, ofpbuf->header.xid, reply_len);
memcpy(ofpbuf_reply->data, ofpbuf->data, sizeof(struct ofp_role));
ofpbuf_reply->header.type = OFPT_ROLE_REPLY;
send_openflow_message(ofpbuf_reply, reply_len);
return HANDLE;
}
```
]
== 实验结果
#para[
在控制器与交换机之间进行抓包分析报文的交互过程。控制器发送不同类型的OpenFlow消息给交换机交换机接收并处理消息并返回相应的回复消息。通过抓包分析可以看到消息的交互过程包括消息头、消息类型、消息长度等字段的解析和处理。用这种方式来验证协议编写的正确性。
实现的子协议种类较多,这里只展示部分报文内容。
]
=== OFPT_ROLE_REPLY
#para[
+ OFPT_ROLE_REPLY类型消息。此消息用于配置交换机的角色如主控制器、从控制器等。下面的@slave_request 是控制器发送的Role Request消息。
#figure(image("slave.png",fit:"stretch",format:"png"),caption:"OFPT_ROLE_REQUEST消息",)<slave_request>
如图@slave_reply 所示交换机接收并处理后返回的Role Reply消息。
#figure(image("slave_reply.png",fit:"stretch",format:"png"),caption:"OFPT_ROLE_REPLY消息",)<slave_reply>
这说明交换机已经接收到了控制器发送的Role Request消息并返回了Role Reply消息。
]
=== OFPT_MULTIPART_REPLY
#para[
+ OFPMP_DESC类型消息。此消息用于查询交换机的描述信息包括制造商、硬件版本、软件版本、序列号和数据路径描述等。
#figure(image("lixuanwang.png",fit:"stretch",format:"png"),caption:"OFPMP_DESC消息",)
可以看到图中的消息包含了制造商、硬件版本、软件版本、序列号和数据路径描述等信息(此处使用了自己的信息进行标记与区分)。
+ OFPMP_TABLE类型消息。此消息用于查询交换机的流表统计信息包括匹配计数、查找计数等。
#figure(image("table.png",fit:"stretch",format:"png"),caption:"OFPMP_TABLE消息",)
可以看到图中的消息包含了匹配计数、查找计数等信息(此处修改为了自己设置的值)。
]
= 实验总结
#para[
]
#show heading: it => box(width: 100%)[
#v(0.50em)
#set text(font: hei)
// #counter(heading).display()
// #h(0.5em)
#it.body
]
#pagebreak()
#bibliography("ref.yml",full: true,title: "参考文献",style:"gb-7714-2015-numeric")
/*
根据这个网站的格式示范https://github.com/typst/hayagriva/blob/main/docs/file-format.md
为这些网页生成.yml文件
https://opennetworking.org/wp-content/uploads/2014/10/openflow-spec-v1.3.0.pdf
https://www.cnblogs.com/goldsunshine/p/7262484.html
https://www.jianshu.com/p/acfeae1771b3
https://www.jianshu.com/p/82e238eb8d14
*/