mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
190 lines
5 KiB
Text
190 lines
5 KiB
Text
##! OpenFlow plugin for the Ryu controller.
|
|
|
|
@load base/frameworks/openflow
|
|
@load base/utils/active-http
|
|
@load base/utils/exec
|
|
@load base/utils/json
|
|
|
|
module OpenFlow;
|
|
|
|
export {
|
|
redef enum Plugin += {
|
|
RYU,
|
|
};
|
|
|
|
## Ryu controller constructor.
|
|
##
|
|
## host: Controller ip.
|
|
##
|
|
## host_port: Controller listen port.
|
|
##
|
|
## dpid: OpenFlow switch datapath id.
|
|
##
|
|
## Returns: OpenFlow::Controller record.
|
|
global ryu_new: function(host: addr, host_port: count, dpid: count): OpenFlow::Controller;
|
|
|
|
redef record ControllerState += {
|
|
## Controller ip.
|
|
ryu_host: addr &optional;
|
|
## Controller listen port.
|
|
ryu_port: count &optional;
|
|
## OpenFlow switch datapath id.
|
|
ryu_dpid: count &optional;
|
|
## Enable debug mode - output JSON to stdout; do not perform actions.
|
|
ryu_debug: bool &default=F;
|
|
};
|
|
}
|
|
|
|
# Ryu ReST API flow_mod URL-path
|
|
const RYU_FLOWENTRY_PATH = "/stats/flowentry/";
|
|
# Ryu ReST API flow_stats URL-path
|
|
#const RYU_FLOWSTATS_PATH = "/stats/flow/";
|
|
|
|
# Ryu ReST API action_output type.
|
|
type ryu_flow_action: record {
|
|
# Ryu uses strings as its ReST API output action.
|
|
_type: string;
|
|
# The output port for type OUTPUT
|
|
_port: count &optional;
|
|
};
|
|
|
|
# The ReST API documentation can be found at
|
|
# https://media.readthedocs.org/pdf/ryu/latest/ryu.pdf
|
|
# Ryu ReST API flow_mod type.
|
|
type ryu_ofp_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;
|
|
flags: count &optional;
|
|
match: OpenFlow::ofp_match;
|
|
actions: vector of ryu_flow_action;
|
|
out_port: count &optional;
|
|
out_group: count &optional;
|
|
};
|
|
|
|
# Mapping between ofp flow mod commands and ryu urls
|
|
const ryu_url: table[ofp_flow_mod_command] of string = {
|
|
[OFPFC_ADD] = "add",
|
|
[OFPFC_MODIFY] = "modify",
|
|
[OFPFC_MODIFY_STRICT] = "modify_strict",
|
|
[OFPFC_DELETE] = "delete",
|
|
[OFPFC_DELETE_STRICT] = "delete_strict",
|
|
};
|
|
|
|
# Ryu flow_mod function
|
|
function ryu_flow_mod(state: OpenFlow::ControllerState, match: ofp_match, flow_mod: OpenFlow::ofp_flow_mod): bool
|
|
{
|
|
if ( state$_plugin != RYU )
|
|
{
|
|
Reporter::error("Ryu openflow plugin was called with state of non-ryu plugin");
|
|
return F;
|
|
}
|
|
|
|
# Generate ryu_flow_actions because their type differs (using strings as type).
|
|
local flow_actions: vector of ryu_flow_action = vector();
|
|
|
|
for ( i in flow_mod$actions$out_ports )
|
|
flow_actions += ryu_flow_action($_type="OUTPUT", $_port=flow_mod$actions$out_ports[i]);
|
|
|
|
# Generate our ryu_flow_mod record for the ReST API call.
|
|
local mod: ryu_ofp_flow_mod = ryu_ofp_flow_mod(
|
|
$dpid=state$ryu_dpid,
|
|
$cookie=flow_mod$cookie,
|
|
$idle_timeout=flow_mod$idle_timeout,
|
|
$hard_timeout=flow_mod$hard_timeout,
|
|
$priority=flow_mod$priority,
|
|
$flags=flow_mod$flags,
|
|
$match=match,
|
|
$actions=flow_actions
|
|
);
|
|
|
|
if ( flow_mod?$out_port )
|
|
mod$out_port = flow_mod$out_port;
|
|
if ( flow_mod?$out_group )
|
|
mod$out_group = flow_mod$out_group;
|
|
|
|
# Type of the command
|
|
local command_type: string;
|
|
|
|
if ( flow_mod$command in ryu_url )
|
|
command_type = ryu_url[flow_mod$command];
|
|
else
|
|
{
|
|
Reporter::warning(fmt("The given OpenFlow command type '%s' is not available", cat(flow_mod$command)));
|
|
return F;
|
|
}
|
|
|
|
local url=cat("http://", cat(state$ryu_host), ":", cat(state$ryu_port), RYU_FLOWENTRY_PATH, command_type);
|
|
|
|
if ( state$ryu_debug )
|
|
{
|
|
print url;
|
|
print to_json(mod);
|
|
event OpenFlow::flow_mod_success(state$_name, match, flow_mod);
|
|
return T;
|
|
}
|
|
|
|
# Create the ActiveHTTP request and convert the record to a Ryu ReST API JSON string
|
|
local request: ActiveHTTP::Request = ActiveHTTP::Request(
|
|
$url=url,
|
|
$method="POST",
|
|
$client_data=to_json(mod)
|
|
);
|
|
|
|
# Execute call to Ryu's ReST API
|
|
when ( local result = ActiveHTTP::request(request) )
|
|
{
|
|
if(result$code == 200)
|
|
event OpenFlow::flow_mod_success(state$_name, match, flow_mod, result$body);
|
|
else
|
|
{
|
|
Reporter::warning(fmt("Flow modification failed with error: %s", result$body));
|
|
event OpenFlow::flow_mod_failure(state$_name, match, flow_mod, result$body);
|
|
return F;
|
|
}
|
|
}
|
|
|
|
return T;
|
|
}
|
|
|
|
function ryu_flow_clear(state: OpenFlow::ControllerState): bool
|
|
{
|
|
local url=cat("http://", cat(state$ryu_host), ":", cat(state$ryu_port), RYU_FLOWENTRY_PATH, "clear", "/", state$ryu_dpid);
|
|
|
|
if ( state$ryu_debug )
|
|
{
|
|
print url;
|
|
return T;
|
|
}
|
|
|
|
local request: ActiveHTTP::Request = ActiveHTTP::Request(
|
|
$url=url,
|
|
$method="DELETE"
|
|
);
|
|
|
|
when ( local result = ActiveHTTP::request(request) )
|
|
{
|
|
}
|
|
|
|
return T;
|
|
}
|
|
|
|
function ryu_describe(state: ControllerState): string
|
|
{
|
|
return fmt("Ryu-%d-http://%s:%d", state$ryu_dpid, state$ryu_host, state$ryu_port);
|
|
}
|
|
|
|
# Ryu controller constructor
|
|
function ryu_new(host: addr, host_port: count, dpid: count): OpenFlow::Controller
|
|
{
|
|
local c = OpenFlow::Controller($state=OpenFlow::ControllerState($ryu_host=host, $ryu_port=host_port, $ryu_dpid=dpid),
|
|
$flow_mod=ryu_flow_mod, $flow_clear=ryu_flow_clear, $describe=ryu_describe, $supports_flow_removed=F);
|
|
|
|
register_controller(OpenFlow::RYU, cat(host,host_port,dpid), c);
|
|
|
|
return c;
|
|
}
|