mirror of
https://github.com/zeek/zeek.git
synced 2025-10-04 07:38:19 +00:00
[ADD] the possibility to remove flows and refactored the flow_mod function to fit the new capabilities. Also started to comment more of the code
This commit is contained in:
parent
6c2a8cdff4
commit
bf6dc12be4
3 changed files with 185 additions and 75 deletions
|
@ -84,7 +84,7 @@ export {
|
|||
|
||||
type ofp_match: record {
|
||||
# Wildcard fields.
|
||||
wildcards: count &optional;
|
||||
#wildcards: count &optional;
|
||||
# Input switch port.
|
||||
in_port: count &optional;
|
||||
# Ethernet source address.
|
||||
|
@ -112,53 +112,63 @@ export {
|
|||
};
|
||||
|
||||
type ofp_action_output: record {
|
||||
# this should never change, but there are not
|
||||
# constants available in records
|
||||
# defaults to OFPAT_OUTPUT
|
||||
_type: ofp_action_type &default=OFPAT_OUTPUT;
|
||||
_len: count &default=8;
|
||||
#_len: count &default=8;
|
||||
# Output port.
|
||||
_port: count &default=OFPP_FLOOD;
|
||||
_max_len: count &optional;
|
||||
#_max_len: count &optional;
|
||||
};
|
||||
|
||||
# TODO:
|
||||
# type ofp_flow_mod: record {
|
||||
# header: ofp_header;
|
||||
# # Fields to match
|
||||
# match: ofp_match;
|
||||
# # Opaque controller-issued identifier.
|
||||
# cookie: count &optional;
|
||||
#
|
||||
# # Flow actions
|
||||
#
|
||||
# # One of OFPFC_*.
|
||||
# command: #TBD
|
||||
# # Idle time befor discarding (seconds).
|
||||
# idle_timeout: count &optional;
|
||||
# # Max time before discarding (seconds).
|
||||
# hard_timeout: count &optional;
|
||||
# # Priority level of flow entry.
|
||||
# priority: count &optional;
|
||||
# # Buffered packet to apply to (or -1).
|
||||
# # Not meaningful for OFPFC_DELETE*.
|
||||
# buffer_id: count &optional;
|
||||
# # For OFPFC_DELETE* commands, require
|
||||
# # matching entries to include this as an
|
||||
# # output port. A value of OFPP_NONE
|
||||
# # indicates no restrictions
|
||||
# out_port: count &optional;
|
||||
# # One of OFPFF_*.
|
||||
# flags: count &optional;
|
||||
#
|
||||
# actions: vector of ofp_action_header;
|
||||
# };
|
||||
#type ofp_flow_mod_flags: enum {
|
||||
# Send flow removed message when flow
|
||||
# expires or is deleted.
|
||||
const OFPFF_SEND_FLOW_REM = 0x1;
|
||||
# Check for overlapping entries first.
|
||||
const OFPFF_CHECK_OVERLAP = 0x2;
|
||||
# Remark this is for emergency.
|
||||
# Flows added with this are only used
|
||||
# when the controller is disconnected.
|
||||
const OFPFF_EMERG = 0x4;
|
||||
#};
|
||||
|
||||
global flow_mod: function(
|
||||
dpid: count, cookie: count, idle_timeout: count, hard_timeout: count,
|
||||
actions: vector of ofp_action_output, match: ofp_match): bool;
|
||||
type ofp_flow_mod: record {
|
||||
# header: ofp_header;
|
||||
# Fields to match
|
||||
match: ofp_match;
|
||||
# Opaque controller-issued identifier.
|
||||
cookie: count &optional;
|
||||
|
||||
# Flow actions
|
||||
|
||||
# One of OFPFC_*.
|
||||
command: ofp_flow_mod_command &default=OFPFC_ADD;
|
||||
# Idle time befor discarding (seconds).
|
||||
idle_timeout: count &optional;
|
||||
# Max time before discarding (seconds).
|
||||
hard_timeout: count &optional;
|
||||
# Priority level of flow entry.
|
||||
priority: count &optional;
|
||||
# Buffered packet to apply to (or -1).
|
||||
# Not meaningful for OFPFC_DELETE*.
|
||||
buffer_id: count &optional;
|
||||
# For OFPFC_DELETE* commands, require
|
||||
# matching entries to include this as an
|
||||
# output port. A value of OFPP_NONE
|
||||
# indicates no restrictions
|
||||
out_port: count &optional;
|
||||
# One of OFPFF_*.
|
||||
flags: count &optional;
|
||||
actions: vector of ofp_action_output;
|
||||
};
|
||||
|
||||
global flow_mod: function(dpid: count, flow_mod: ofp_flow_mod): bool;
|
||||
}
|
||||
|
||||
# Flow Modification function prototype
|
||||
type FlowModFunc: function(
|
||||
dpid: count, cookie: count, idle_timeout:count, hard_timeout: count,
|
||||
actions: vector of ofp_action_output, match: ofp_match): bool;
|
||||
type FlowModFunc: function(dpid: count, flow_mod: ofp_flow_mod): bool;
|
||||
|
||||
# Flow Modification function
|
||||
global FlowMod: FlowModFunc;
|
||||
|
@ -170,10 +180,8 @@ function register_openflow_mod_func(func: FlowModFunc) {
|
|||
FlowMod = func;
|
||||
}
|
||||
|
||||
function flow_mod(
|
||||
dpid: count, cookie: count, idle_timeout:count, hard_timeout:count,
|
||||
actions: vector of ofp_action_output, match: ofp_match): bool {
|
||||
return FlowMod(dpid, cookie, idle_timeout, hard_timeout, actions, match);
|
||||
function flow_mod(dpid: count, flow_mod: ofp_flow_mod): bool {
|
||||
return FlowMod(dpid, flow_mod);
|
||||
}
|
||||
|
||||
event bro_init() &priority=100000 {
|
||||
|
|
|
@ -6,9 +6,13 @@
|
|||
module Openflow;
|
||||
|
||||
export {
|
||||
const controller_uri = "http://10.255.0.20:8080/stats/flowentry/add" &redef;
|
||||
const controller_ip = "10.255.0.20" &redef;
|
||||
const controller_port = "8080" &redef;
|
||||
}
|
||||
|
||||
const OFP_NO_BUFFER = 0xffffffff;
|
||||
const RYU_FLOWENTRY_PATH = "/stats/flowentry/";
|
||||
|
||||
type ryu_flow_action_output: record {
|
||||
# The type should be never changed...
|
||||
# but constants are not possible in a record.
|
||||
|
@ -20,7 +24,7 @@ type ryu_flow_action_output: record {
|
|||
# The restAPI documentation can be found at
|
||||
# https://media.readthedocs.org/pdf/ryu/latest/ryu.pdf
|
||||
# on page 278-299
|
||||
type ryu_flow_add: record {
|
||||
type ryu_flow_mod: record {
|
||||
dpid: count;
|
||||
cookie: count &optional;
|
||||
cookie_mask: count &optional;
|
||||
|
@ -37,42 +41,103 @@ type ryu_flow_add: record {
|
|||
# register the ryu openflow plugin flow_mod function
|
||||
hook register_openflow_plugin() {
|
||||
register_openflow_mod_func(
|
||||
function(
|
||||
dpid: count, cookie: count, idle_timeout: count, hard_timeout: count,
|
||||
actions: vector of ofp_action_output, match: ofp_match): bool {
|
||||
local ryu_flow_actions: vector of ryu_flow_action_output;
|
||||
for(i in actions) {
|
||||
if(actions[i]$_type == Openflow::OFPAT_OUTPUT) {
|
||||
ryu_flow_actions[|ryu_flow_actions|] = ryu_flow_action_output($_port=actions[i]$_port);
|
||||
function(dpid: count, flow_mod: ofp_flow_mod): bool {
|
||||
# Generate ryu_flow_actions because their type differs (using strings as type).
|
||||
local _flow_actions: vector of ryu_flow_action_output;
|
||||
for(i in flow_mod$actions) {
|
||||
switch(flow_mod$actions[i]$_type) {
|
||||
case OFPAT_OUTPUT:
|
||||
_flow_actions[|_flow_actions|] = ryu_flow_action_output($_port=flow_mod$actions[i]$_port);
|
||||
break;
|
||||
default:
|
||||
print fmt("Error: flow action '%s' not available", flow_mod$actions[i]$_type);
|
||||
return F;
|
||||
}
|
||||
}
|
||||
# Generate our record for the restAPI.
|
||||
local ryu_flow_mod: ryu_flow_add = ryu_flow_add($dpid=dpid, $cookie=cookie, $idle_timeout=idle_timeout, $hard_timeout=hard_timeout, $match=match, $actions=ryu_flow_actions);
|
||||
# Create the ActiveHTTP request and convert the record to a JSON string
|
||||
local request: ActiveHTTP::Request = ActiveHTTP::Request($url=controller_uri, $method="POST", $client_data=JSON::convert(ryu_flow_mod));
|
||||
# Execute call to RyuRestAPI
|
||||
# Generate our ryu_flow_mod record for the restAPI call.
|
||||
local _flow_mod: ryu_flow_mod = ryu_flow_mod(
|
||||
$dpid=dpid,
|
||||
$cookie=flow_mod$cookie,
|
||||
$idle_timeout=flow_mod$idle_timeout,
|
||||
$hard_timeout=flow_mod$hard_timeout,
|
||||
$match=flow_mod$match,
|
||||
$actions=_flow_actions
|
||||
);
|
||||
# Type of the command
|
||||
local command_type: string;
|
||||
switch(flow_mod$command) {
|
||||
case OFPFC_ADD:
|
||||
command_type = "add";
|
||||
break;
|
||||
case OFPFC_DELETE:
|
||||
command_type = "delete";
|
||||
break;
|
||||
default:
|
||||
print fmt("Error: command type '%s' not available", flow_mod$command);
|
||||
return F;
|
||||
}
|
||||
# Create the ActiveHTTP request and convert the record to a ryu restAPI JSON string
|
||||
local request: ActiveHTTP::Request = ActiveHTTP::Request(
|
||||
$url=cat("http://", controller_ip, ":", controller_port, RYU_FLOWENTRY_PATH, command_type),
|
||||
$method="POST",
|
||||
$client_data=JSON::convert(_flow_mod)
|
||||
);
|
||||
# Execute call to ryu's restAPI
|
||||
when(local result = ActiveHTTP::request(request)) {
|
||||
if(result$code == 200) {
|
||||
print fmt("Flow %s:%s -> %s:%s removed from monitor", match$nw_src, match$tp_src, match$nw_dst, match$tp_dst);
|
||||
print fmt(
|
||||
"%sed flow %s:%s -> %s:%s",
|
||||
command_type,
|
||||
flow_mod$match$nw_src,
|
||||
flow_mod$match$tp_src,
|
||||
flow_mod$match$nw_dst,
|
||||
flow_mod$match$tp_dst
|
||||
);
|
||||
} else {
|
||||
print fmt("Error: could no add shunt flow, restAPI returned:\n%s", result);
|
||||
print fmt("Error: could not %s flow, restAPI returned:\n%s", command_type, result);
|
||||
return F;
|
||||
}
|
||||
}
|
||||
|
||||
# Add reverse flow because openflow only uses unidirectional flows.
|
||||
if(|actions| == 1 && (match$dl_type == ETH_IPv4 || match$dl_type == ETH_IPv6)) {
|
||||
local reverse_match: ofp_match;
|
||||
local reverse_actions: vector of ryu_flow_action_output;
|
||||
reverse_actions[|reverse_actions|] = ryu_flow_action_output($_port=match$in_port);
|
||||
reverse_match = ofp_match($in_port=actions[0]$_port, $dl_type=match$dl_type, $nw_proto=match$nw_proto, $nw_src=match$nw_dst, $nw_dst=match$nw_src, $tp_src=match$tp_dst, $tp_dst=match$tp_src);
|
||||
local reverse_flow_mod: ryu_flow_add = ryu_flow_add($dpid=dpid, $cookie=cookie, $idle_timeout=idle_timeout, $hard_timeout=hard_timeout, $match=reverse_match, $actions=reverse_actions);
|
||||
local reverse_request: ActiveHTTP::Request = ActiveHTTP::Request($url=controller_uri, $method="POST", $addl_curl_args=fmt("-d '%s'", JSON::convert(reverse_flow_mod)));
|
||||
if(|flow_mod$actions| == 1 && (flow_mod$match$dl_type == ETH_IPv4 || flow_mod$match$dl_type == ETH_IPv6)) {
|
||||
local reverse_flow_match: ofp_match;
|
||||
local reverse_flow_actions: vector of ryu_flow_action_output;
|
||||
reverse_flow_actions[|reverse_flow_actions|] = ryu_flow_action_output($_port=flow_mod$match$in_port);
|
||||
reverse_flow_match = ofp_match(
|
||||
$in_port=flow_mod$actions[0]$_port,
|
||||
$dl_type=flow_mod$match$dl_type,
|
||||
$nw_proto=flow_mod$match$nw_proto,
|
||||
$nw_src=flow_mod$match$nw_dst,
|
||||
$nw_dst=flow_mod$match$nw_src,
|
||||
$tp_src=flow_mod$match$tp_dst,
|
||||
$tp_dst=flow_mod$match$tp_src
|
||||
);
|
||||
local reverse_flow_mod: ryu_flow_mod = ryu_flow_mod(
|
||||
$dpid=dpid,
|
||||
$cookie=flow_mod$cookie,
|
||||
$idle_timeout=flow_mod$idle_timeout,
|
||||
$hard_timeout=flow_mod$hard_timeout,
|
||||
$match=reverse_flow_match,
|
||||
$actions=reverse_flow_actions
|
||||
);
|
||||
local reverse_request: ActiveHTTP::Request = ActiveHTTP::Request(
|
||||
$url=cat("http://", controller_ip, ":", controller_port, RYU_FLOWENTRY_PATH, command_type),
|
||||
$method="POST",
|
||||
$client_data=JSON::convert(reverse_flow_mod)
|
||||
);
|
||||
when(local result2 = ActiveHTTP::request(reverse_request)) {
|
||||
if(result2$code == 200) {
|
||||
print fmt("Flow %s:%s -> %s:%s removed from monitor", reverse_match$nw_src, reverse_match$tp_src, reverse_match$nw_dst, reverse_match$tp_dst);
|
||||
print fmt(
|
||||
"%sed flow %s:%s -> %s:%s",
|
||||
command_type,
|
||||
reverse_flow_match$nw_src,
|
||||
reverse_flow_match$tp_src,
|
||||
reverse_flow_match$nw_dst,
|
||||
reverse_flow_match$tp_dst
|
||||
);
|
||||
} else {
|
||||
print fmt("Error: could no add shunt flow, restAPI returned:\n%s", result2);
|
||||
print fmt("Error: could not %s flow, restAPI returned:\n%s", command_type, result2);
|
||||
return F;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ const hard_timeout = 0;
|
|||
const in_port = 3;
|
||||
const out_port = 1;
|
||||
|
||||
global delete_flow: bool = F;
|
||||
|
||||
export {
|
||||
## Number of bytes transferred before shunting a flow.
|
||||
const size_threshold = 1024000 &redef;
|
||||
|
@ -33,27 +35,62 @@ export {
|
|||
}
|
||||
|
||||
function size_callback(c: connection, cnt: count): interval {
|
||||
print fmt("%s:%s <-> %s:%s reached %s/%s", c$id$orig_h, port_to_count(c$id$orig_p), c$id$resp_h, port_to_count(c$id$resp_p), c$orig$num_bytes_ip + c$resp$num_bytes_ip, size_threshold);
|
||||
# print flow traffic.
|
||||
print fmt(
|
||||
"%s:%s <-> %s:%s reached %s/%s",
|
||||
c$id$orig_h,
|
||||
port_to_count(c$id$orig_p),
|
||||
c$id$resp_h,
|
||||
port_to_count(c$id$resp_p),
|
||||
c$orig$num_bytes_ip + c$resp$num_bytes_ip,
|
||||
size_threshold
|
||||
);
|
||||
# if traffic exceeds the given threshold, remove flow.
|
||||
if ( c$orig$num_bytes_ip + c$resp$num_bytes_ip >= size_threshold ) {
|
||||
# create openflow flow_mod add records from connection data and given default constants
|
||||
local actions: vector of Openflow::ofp_action_output;
|
||||
actions[|actions|] = Openflow::ofp_action_output($_port=out_port);
|
||||
|
||||
# flow layer 4 protocol
|
||||
local nw_proto = Openflow::IP_TCP;
|
||||
if(is_udp_port(c$id$orig_p)) {
|
||||
nw_proto = Openflow::IP_UDP;
|
||||
} else if(is_icmp_port(c$id$orig_p)) {
|
||||
nw_proto = Openflow::IP_ICMP;
|
||||
}
|
||||
local match: Openflow::ofp_match = [$in_port=in_port, $nw_src=c$id$orig_h, $nw_dst=c$id$resp_h, $nw_proto=nw_proto, $tp_src=c$id$orig_p, $tp_dst=c$id$resp_p];
|
||||
local match: Openflow::ofp_match = [
|
||||
$in_port=in_port,
|
||||
$nw_src=c$id$orig_h,
|
||||
$nw_dst=c$id$resp_h,
|
||||
$nw_proto=nw_proto,
|
||||
$tp_src=c$id$orig_p,
|
||||
$tp_dst=c$id$resp_p
|
||||
];
|
||||
local command = Openflow::OFPFC_ADD;
|
||||
if(delete_flow) {
|
||||
command = Openflow::OFPFC_DELETE;
|
||||
}
|
||||
local flow_mod: Openflow::ofp_flow_mod = [
|
||||
$match=match,
|
||||
$cookie=cookie,
|
||||
$command=command,
|
||||
$idle_timeout=idle_timeout,
|
||||
$hard_timeout=hard_timeout,
|
||||
$actions=actions
|
||||
];
|
||||
|
||||
# print fmt(cmd, param_dpid, param_port, "",of_ctrl_uri);
|
||||
when ( local result = Openflow::flow_mod(dpid, cookie, idle_timeout, hard_timeout, actions, match) ) {
|
||||
# call openflow framework
|
||||
when ( local result = Openflow::flow_mod(dpid, flow_mod) ) {
|
||||
if(result) {
|
||||
event OpenflowShunt::shunt_triggered(c);
|
||||
}
|
||||
}
|
||||
|
||||
if(delete_flow) {
|
||||
return -1sec;
|
||||
} else {
|
||||
delete_flow = T;
|
||||
return 15sec;
|
||||
}
|
||||
}
|
||||
|
||||
return poll_interval;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue