[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:
Christian Struck 2014-10-23 18:20:40 -07:00
parent 6c2a8cdff4
commit bf6dc12be4
3 changed files with 185 additions and 75 deletions

View file

@ -84,7 +84,7 @@ export {
type ofp_match: record { type ofp_match: record {
# Wildcard fields. # Wildcard fields.
wildcards: count &optional; #wildcards: count &optional;
# Input switch port. # Input switch port.
in_port: count &optional; in_port: count &optional;
# Ethernet source address. # Ethernet source address.
@ -112,53 +112,63 @@ export {
}; };
type ofp_action_output: record { 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; _type: ofp_action_type &default=OFPAT_OUTPUT;
_len: count &default=8; #_len: count &default=8;
# Output port.
_port: count &default=OFPP_FLOOD; _port: count &default=OFPP_FLOOD;
_max_len: count &optional; #_max_len: count &optional;
}; };
# TODO: #type ofp_flow_mod_flags: enum {
# type ofp_flow_mod: record { # Send flow removed message when flow
# header: ofp_header; # expires or is deleted.
# # Fields to match const OFPFF_SEND_FLOW_REM = 0x1;
# match: ofp_match; # Check for overlapping entries first.
# # Opaque controller-issued identifier. const OFPFF_CHECK_OVERLAP = 0x2;
# cookie: count &optional; # Remark this is for emergency.
# # Flows added with this are only used
# # Flow actions # when the controller is disconnected.
# const OFPFF_EMERG = 0x4;
# # 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;
#}; #};
global flow_mod: function( type ofp_flow_mod: record {
dpid: count, cookie: count, idle_timeout: count, hard_timeout: count, # header: ofp_header;
actions: vector of ofp_action_output, match: ofp_match): bool; # 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 # Flow Modification function prototype
type FlowModFunc: function( type FlowModFunc: function(dpid: count, flow_mod: ofp_flow_mod): bool;
dpid: count, cookie: count, idle_timeout:count, hard_timeout: count,
actions: vector of ofp_action_output, match: ofp_match): bool;
# Flow Modification function # Flow Modification function
global FlowMod: FlowModFunc; global FlowMod: FlowModFunc;
@ -170,10 +180,8 @@ function register_openflow_mod_func(func: FlowModFunc) {
FlowMod = func; FlowMod = func;
} }
function flow_mod( function flow_mod(dpid: count, flow_mod: ofp_flow_mod): bool {
dpid: count, cookie: count, idle_timeout:count, hard_timeout:count, return FlowMod(dpid, flow_mod);
actions: vector of ofp_action_output, match: ofp_match): bool {
return FlowMod(dpid, cookie, idle_timeout, hard_timeout, actions, match);
} }
event bro_init() &priority=100000 { event bro_init() &priority=100000 {

View file

@ -6,9 +6,13 @@
module Openflow; module Openflow;
export { 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 { type ryu_flow_action_output: record {
# The type should be never changed... # The type should be never changed...
# but constants are not possible in a record. # 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 # The restAPI documentation can be found at
# https://media.readthedocs.org/pdf/ryu/latest/ryu.pdf # https://media.readthedocs.org/pdf/ryu/latest/ryu.pdf
# on page 278-299 # on page 278-299
type ryu_flow_add: record { type ryu_flow_mod: record {
dpid: count; dpid: count;
cookie: count &optional; cookie: count &optional;
cookie_mask: count &optional; cookie_mask: count &optional;
@ -37,42 +41,103 @@ type ryu_flow_add: record {
# register the ryu openflow plugin flow_mod function # register the ryu openflow plugin flow_mod function
hook register_openflow_plugin() { hook register_openflow_plugin() {
register_openflow_mod_func( register_openflow_mod_func(
function( function(dpid: count, flow_mod: ofp_flow_mod): bool {
dpid: count, cookie: count, idle_timeout: count, hard_timeout: count, # Generate ryu_flow_actions because their type differs (using strings as type).
actions: vector of ofp_action_output, match: ofp_match): bool { local _flow_actions: vector of ryu_flow_action_output;
local ryu_flow_actions: vector of ryu_flow_action_output; for(i in flow_mod$actions) {
for(i in actions) { switch(flow_mod$actions[i]$_type) {
if(actions[i]$_type == Openflow::OFPAT_OUTPUT) { case OFPAT_OUTPUT:
ryu_flow_actions[|ryu_flow_actions|] = ryu_flow_action_output($_port=actions[i]$_port); _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. # Generate our ryu_flow_mod record for the restAPI call.
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); local _flow_mod: ryu_flow_mod = ryu_flow_mod(
# Create the ActiveHTTP request and convert the record to a JSON string $dpid=dpid,
local request: ActiveHTTP::Request = ActiveHTTP::Request($url=controller_uri, $method="POST", $client_data=JSON::convert(ryu_flow_mod)); $cookie=flow_mod$cookie,
# Execute call to RyuRestAPI $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)) { when(local result = ActiveHTTP::request(request)) {
if(result$code == 200) { 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 { } 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; return F;
} }
} }
# Add reverse flow because openflow only uses unidirectional flows. # Add reverse flow because openflow only uses unidirectional flows.
if(|actions| == 1 && (match$dl_type == ETH_IPv4 || match$dl_type == ETH_IPv6)) { if(|flow_mod$actions| == 1 && (flow_mod$match$dl_type == ETH_IPv4 || flow_mod$match$dl_type == ETH_IPv6)) {
local reverse_match: ofp_match; local reverse_flow_match: ofp_match;
local reverse_actions: vector of ryu_flow_action_output; local reverse_flow_actions: vector of ryu_flow_action_output;
reverse_actions[|reverse_actions|] = ryu_flow_action_output($_port=match$in_port); reverse_flow_actions[|reverse_flow_actions|] = ryu_flow_action_output($_port=flow_mod$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); reverse_flow_match = ofp_match(
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); $in_port=flow_mod$actions[0]$_port,
local reverse_request: ActiveHTTP::Request = ActiveHTTP::Request($url=controller_uri, $method="POST", $addl_curl_args=fmt("-d '%s'", JSON::convert(reverse_flow_mod))); $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)) { when(local result2 = ActiveHTTP::request(reverse_request)) {
if(result2$code == 200) { 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 { } 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; return F;
} }
} }

View file

@ -19,6 +19,8 @@ const hard_timeout = 0;
const in_port = 3; const in_port = 3;
const out_port = 1; const out_port = 1;
global delete_flow: bool = F;
export { export {
## Number of bytes transferred before shunting a flow. ## Number of bytes transferred before shunting a flow.
const size_threshold = 1024000 &redef; const size_threshold = 1024000 &redef;
@ -33,27 +35,62 @@ export {
} }
function size_callback(c: connection, cnt: count): interval { 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 ) { 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; local actions: vector of Openflow::ofp_action_output;
actions[|actions|] = Openflow::ofp_action_output($_port=out_port); actions[|actions|] = Openflow::ofp_action_output($_port=out_port);
# flow layer 4 protocol
local nw_proto = Openflow::IP_TCP; local nw_proto = Openflow::IP_TCP;
if(is_udp_port(c$id$orig_p)) { if(is_udp_port(c$id$orig_p)) {
nw_proto = Openflow::IP_UDP; nw_proto = Openflow::IP_UDP;
} else if(is_icmp_port(c$id$orig_p)) { } else if(is_icmp_port(c$id$orig_p)) {
nw_proto = Openflow::IP_ICMP; 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); # call openflow framework
when ( local result = Openflow::flow_mod(dpid, cookie, idle_timeout, hard_timeout, actions, match) ) { when ( local result = Openflow::flow_mod(dpid, flow_mod) ) {
if(result) { if(result) {
event OpenflowShunt::shunt_triggered(c); event OpenflowShunt::shunt_triggered(c);
} }
} }
if(delete_flow) {
return -1sec; return -1sec;
} else {
delete_flow = T;
return 15sec;
}
} }
return poll_interval; return poll_interval;