a few small fixes to openflow

*rename module from Openflow to OpenFlow
*add match_conn function to convert conn_id to openflow match
*add a few things back into the openflow records like... table_id
*and - a test
This commit is contained in:
Johanna Amann 2015-04-10 11:20:36 -07:00
parent 70f6635cb1
commit 46058d0b02
6 changed files with 120 additions and 19 deletions

View file

@ -1,7 +1,7 @@
# All types/constants not specific to Openflow will be defined here # All types/constants not specific to Openflow will be defined here
# unitl they somehow get into bro. # unitl they somehow get into bro.
module Openflow; module OpenFlow;
# Some cookie specific constants. # Some cookie specific constants.
# first 24 bits # first 24 bits

View file

@ -8,7 +8,7 @@
##! for shunting, please look at the PACF framework, which provides higher ##! for shunting, please look at the PACF framework, which provides higher
##! level functions and can use the OpenFlow framework as a backend. ##! level functions and can use the OpenFlow framework as a backend.
module Openflow; module OpenFlow;
@load ./consts @load ./consts
@load ./types @load ./types
@ -50,6 +50,16 @@ export {
## msg: Message to describe the event. ## msg: Message to describe the event.
global flow_mod_failure: event(match: ofp_match, flow_mod: ofp_flow_mod, msg: string &default=""); global flow_mod_failure: event(match: ofp_match, flow_mod: ofp_flow_mod, msg: string &default="");
## Convert a conn_id record into an ofp_match record that can be used to
## create match objects for OpenFlow.
##
## id: the conn_id record that describes the record.
##
## reverse: reverse the sources and destinations when creating the match record (default F)
##
## Returns: ofp_match object for the conn_id record.
global match_conn: function(id: conn_id, reverse: bool &default=F): ofp_match;
# ### # ###
# ### Low-level functions for cookie handling. # ### Low-level functions for cookie handling.
# ### # ###
@ -76,6 +86,7 @@ export {
global generate_cookie: function(cookie: count &default=0): count; global generate_cookie: function(cookie: count &default=0): count;
} }
# the flow_mod function wrapper # the flow_mod function wrapper
function flow_mod(controller: Controller, match: ofp_match, flow_mod: ofp_flow_mod): bool function flow_mod(controller: Controller, match: ofp_match, flow_mod: ofp_flow_mod): bool
{ {
@ -93,6 +104,49 @@ function flow_clear(controller: Controller): bool
return F; return F;
} }
function match_conn(id: conn_id, reverse: bool &default=F): ofp_match
{
local dl_type = ETH_IPv4;
local proto = IP_TCP;
local orig_h: addr;
local orig_p: port;
local resp_h: addr;
local resp_p: port;
if ( reverse == F )
{
orig_h = id$orig_h;
orig_p = id$orig_p;
resp_h = id$resp_h;
resp_p = id$resp_p;
}
else
{
orig_h = id$resp_h;
orig_p = id$resp_p;
resp_h = id$orig_h;
resp_p = id$resp_p;
}
if ( is_v6_addr(orig_h) )
dl_type = ETH_IPv6;
if ( is_udp_port(orig_p) )
proto = IP_UDP;
else if ( is_icmp_port(orig_p) )
proto = IP_ICMP;
return ofp_match(
$dl_type=dl_type,
$nw_proto=proto,
$nw_src=orig_h,
$tp_src=orig_p,
$nw_dst=resp_h,
$tp_dst=resp_p
);
}
# local function to forge a flow_mod cookie for this framework. # local function to forge a flow_mod cookie for this framework.
# all flow entries from the openflow framework should have the # all flow entries from the openflow framework should have the
# 42 bit of the cookie set. # 42 bit of the cookie set.
@ -131,7 +185,7 @@ function get_cookie_gid(cookie: count): count
{ {
if( is_valid_cookie(cookie) ) if( is_valid_cookie(cookie) )
return ( return (
(cookie - (COOKIE_BID_START * BRO_COOKIE_ID) - (cookie - (COOKIE_BID_START * BRO_COOKIE_ID) -
(cookie - ((cookie / COOKIE_GID_START) * COOKIE_GID_START))) / (cookie - ((cookie / COOKIE_GID_START) * COOKIE_GID_START))) /
COOKIE_GID_START COOKIE_GID_START
); );

View file

@ -3,7 +3,7 @@
@load base/utils/exec @load base/utils/exec
@load base/utils/json @load base/utils/json
module Openflow; module OpenFlow;
export { export {
redef enum Plugin += { redef enum Plugin += {
@ -16,17 +16,17 @@ export {
## ##
## host_port: Controller listen port. ## host_port: Controller listen port.
## ##
## dpid: Openflow switch datapath id. ## dpid: OpenFlow switch datapath id.
## ##
## Returns: Openflow::Controller record ## Returns: OpenFlow::Controller record
global ryu_new: function(host: addr, host_port: count, dpid: count): Openflow::Controller; global ryu_new: function(host: addr, host_port: count, dpid: count): OpenFlow::Controller;
redef record ControllerState += { redef record ControllerState += {
## Controller ip. ## Controller ip.
ryu_host: addr &optional; ryu_host: addr &optional;
## Controller listen port. ## Controller listen port.
ryu_port: count &optional; ryu_port: count &optional;
## Openflow switch datapath id. ## OpenFlow switch datapath id.
ryu_dpid: count &optional; ryu_dpid: count &optional;
## Enable debug mode - output JSON to stdout; do not perform actions ## Enable debug mode - output JSON to stdout; do not perform actions
ryu_debug: bool &default=F; ryu_debug: bool &default=F;
@ -58,7 +58,7 @@ type ryu_ofp_flow_mod: record {
hard_timeout: count &optional; hard_timeout: count &optional;
priority: count &optional; priority: count &optional;
flags: count &optional; flags: count &optional;
match: Openflow::ofp_match; match: OpenFlow::ofp_match;
actions: vector of ryu_flow_action; actions: vector of ryu_flow_action;
}; };
@ -72,7 +72,7 @@ const ryu_url: table[ofp_flow_mod_command] of string = {
}; };
# Ryu flow_mod function # Ryu flow_mod function
function ryu_flow_mod(state: Openflow::ControllerState, match: ofp_match, flow_mod: Openflow::ofp_flow_mod): bool function ryu_flow_mod(state: OpenFlow::ControllerState, match: ofp_match, flow_mod: OpenFlow::ofp_flow_mod): bool
{ {
if ( state$_plugin != RYU ) if ( state$_plugin != RYU )
{ {
@ -89,7 +89,7 @@ function ryu_flow_mod(state: Openflow::ControllerState, match: ofp_match, flow_m
# Generate our ryu_flow_mod record for the ReST API call. # Generate our ryu_flow_mod record for the ReST API call.
local mod: ryu_ofp_flow_mod = ryu_ofp_flow_mod( local mod: ryu_ofp_flow_mod = ryu_ofp_flow_mod(
$dpid=state$ryu_dpid, $dpid=state$ryu_dpid,
$cookie=Openflow::generate_cookie(flow_mod$cookie), $cookie=OpenFlow::generate_cookie(flow_mod$cookie),
$idle_timeout=flow_mod$idle_timeout, $idle_timeout=flow_mod$idle_timeout,
$hard_timeout=flow_mod$hard_timeout, $hard_timeout=flow_mod$hard_timeout,
$priority=flow_mod$priority, $priority=flow_mod$priority,
@ -105,7 +105,7 @@ function ryu_flow_mod(state: Openflow::ControllerState, match: ofp_match, flow_m
command_type = ryu_url[flow_mod$command]; command_type = ryu_url[flow_mod$command];
else else
{ {
Reporter::warning(fmt("The given Openflow command type '%s' is not available", cat(flow_mod$command))); Reporter::warning(fmt("The given OpenFlow command type '%s' is not available", cat(flow_mod$command)));
return F; return F;
} }
@ -115,7 +115,7 @@ function ryu_flow_mod(state: Openflow::ControllerState, match: ofp_match, flow_m
{ {
print url; print url;
print to_json(mod); print to_json(mod);
event Openflow::flow_mod_success(match, flow_mod); event OpenFlow::flow_mod_success(match, flow_mod);
return T; return T;
} }
@ -130,11 +130,11 @@ function ryu_flow_mod(state: Openflow::ControllerState, match: ofp_match, flow_m
when ( local result = ActiveHTTP::request(request) ) when ( local result = ActiveHTTP::request(request) )
{ {
if(result$code == 200) if(result$code == 200)
event Openflow::flow_mod_success(match, flow_mod, result$body); event OpenFlow::flow_mod_success(match, flow_mod, result$body);
else else
{ {
Reporter::warning(fmt("Flow modification failed with error: %s", result$body)); Reporter::warning(fmt("Flow modification failed with error: %s", result$body));
event Openflow::flow_mod_failure(match, flow_mod, result$body); event OpenFlow::flow_mod_failure(match, flow_mod, result$body);
return F; return F;
} }
} }
@ -142,7 +142,7 @@ function ryu_flow_mod(state: Openflow::ControllerState, match: ofp_match, flow_m
return T; return T;
} }
function ryu_flow_clear(state: Openflow::ControllerState): bool 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); local url=cat("http://", cat(state$ryu_host), ":", cat(state$ryu_port), RYU_FLOWENTRY_PATH, "clear", "/", state$ryu_dpid);
@ -165,8 +165,8 @@ function ryu_flow_clear(state: Openflow::ControllerState): bool
} }
# Ryu controller constructor # Ryu controller constructor
function ryu_new(host: addr, host_port: count, dpid: count): Openflow::Controller function ryu_new(host: addr, host_port: count, dpid: count): OpenFlow::Controller
{ {
return [$state=[$ryu_host=host, $ryu_port=host_port, $ryu_dpid=dpid, $_plugin=Openflow::RYU], return [$state=[$ryu_host=host, $ryu_port=host_port, $ryu_dpid=dpid, $_plugin=OpenFlow::RYU],
$flow_mod=ryu_flow_mod, $flow_clear=ryu_flow_clear]; $flow_mod=ryu_flow_mod, $flow_clear=ryu_flow_clear];
} }

View file

@ -1,4 +1,4 @@
module Openflow; module OpenFlow;
@load ./consts @load ./consts
@ -39,6 +39,8 @@ export {
nw_tos: count &optional; nw_tos: count &optional;
# IP protocol or lower 8 bits of ARP opcode. # IP protocol or lower 8 bits of ARP opcode.
nw_proto: count &optional; nw_proto: count &optional;
# At the moment, we store both v4 and v6 in the same fields.
# This is not how OpenFlow does it, we might want to change that...
# IP source address. # IP source address.
nw_src: addr &optional; nw_src: addr &optional;
# IP destination address. # IP destination address.
@ -56,6 +58,9 @@ export {
# it so we always can identify our flows... # it so we always can identify our flows...
cookie: count; # &default=BRO_COOKIE_ID * COOKIE_BID_START; cookie: count; # &default=BRO_COOKIE_ID * COOKIE_BID_START;
# Flow actions # Flow actions
## Table to put the flow in. OFPTT_ALL can be used for delete,
## to delete flows from all matching tables.
table_id: count &optional;
## One of OFPFC_*. ## One of OFPFC_*.
command: ofp_flow_mod_command; # &default=OFPFC_ADD; command: ofp_flow_mod_command; # &default=OFPFC_ADD;
## Idle time before discarding (seconds). ## Idle time before discarding (seconds).
@ -64,6 +69,9 @@ export {
hard_timeout: count &default=0; hard_timeout: count &default=0;
## Priority level of flow entry. ## Priority level of flow entry.
priority: count &default=0; priority: count &default=0;
## For OFPFC_DELETE* commands, require matching entried to include
## this as an output port. OFPP_ANY means no restrictions.
out_group: count &optional;
## Bitmap of the OFPFF_* flags ## Bitmap of the OFPFF_* flags
flags: count &default=0; flags: count &default=0;
## Output ports to send data to. ## Output ports to send data to.

View file

@ -0,0 +1,7 @@
http://127.0.0.1:8080/stats/flowentry/clear/42
http://127.0.0.1:8080/stats/flowentry/add
{"match": {}, "dpid": 42, "flags": 0, "hard_timeout": 0, "priority": 0, "actions": [{"port": 3, "type": "OUTPUT"}, {"port": 7, "type": "OUTPUT"}], "cookie": 4398046511105, "idle_timeout": 0}
http://127.0.0.1:8080/stats/flowentry/add
{"match": {"tp_dst": 25, "nw_dst": "74.53.140.153", "nw_src": "10.10.1.4", "dl_type": 2048, "tp_src": 1470, "nw_proto": 6}, "dpid": 42, "flags": 0, "hard_timeout": 0, "priority": 5, "actions": [], "cookie": 4398046511146, "idle_timeout": 30}
http://127.0.0.1:8080/stats/flowentry/add
{"match": {"tp_dst": 25, "nw_dst": "10.10.1.4", "nw_src": "74.53.140.153", "dl_type": 2048, "tp_src": 25, "nw_proto": 6}, "dpid": 42, "flags": 0, "hard_timeout": 0, "priority": 5, "actions": [], "cookie": 4398046511146, "idle_timeout": 30}

View file

@ -0,0 +1,32 @@
# @TEST-EXEC: bro -r $TRACES/smtp.trace %INPUT
# @TEST-EXEC: btest-diff .stdout
@load base/protocols/conn
@load base/frameworks/openflow
global of_controller: OpenFlow::Controller;
event bro_init()
{
of_controller = OpenFlow::ryu_new(127.0.0.1, 8080, 42);
of_controller$state$ryu_debug=T;
OpenFlow::flow_clear(of_controller);
OpenFlow::flow_mod(of_controller, [], [$cookie=1, $command=OpenFlow::OFPFC_ADD, $out_ports=vector(3, 7)]);
}
event connection_established(c: connection)
{
local match = OpenFlow::match_conn(c$id);
local match_rev = OpenFlow::match_conn(c$id, T);
local flow_mod: OpenFlow::ofp_flow_mod = [
$cookie=42,
$command=OpenFlow::OFPFC_ADD,
$idle_timeout=30,
$priority=5
];
OpenFlow::flow_mod(of_controller, match, flow_mod);
OpenFlow::flow_mod(of_controller, match_rev, flow_mod);
}