zeek/scripts/base/frameworks/openflow/plugins/ryu.bro

148 lines
4.5 KiB
Text

@load ../main
@load ../utils/json
@load base/utils/exec
@load base/utils/active-http
module Openflow;
export {
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.
_type: string &default="OUTPUT";
# The output port
_port: count;
};
# The restAPI documentation can be found at
# https://media.readthedocs.org/pdf/ryu/latest/ryu.pdf
# on page 278-299
type ryu_flow_mod: record {
dpid: count;
cookie: count &optional;
cookie_mask: count &optional;
table_id: count &optional;
idle_timeout: count &optional;
hard_timeout: count &optional;
priority: count &optional;
buffer_id: count &optional;
flags: count &optional;
match: Openflow::ofp_match;
actions: vector of ryu_flow_action_output;
};
# register the ryu openflow plugin flow_mod function
hook register_openflow_plugin() {
register_openflow_mod_func(
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 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(
"%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 not %s flow, restAPI returned:\n%s", command_type, result);
return F;
}
}
# Add reverse flow because openflow only uses unidirectional flows.
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(
"%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 not %s flow, restAPI returned:\n%s", command_type, result2);
return F;
}
}
}
return T;
}
);
}