Files
NE_YuR/openflow/include/main_user_openflow-wlx.c
2025-11-18 14:46:26 +08:00

620 lines
21 KiB
C
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.

/***************************************************************************
* 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 = "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++) // 关于为什么写成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