diff --git a/scripts/base/frameworks/openflow/consts.bro b/scripts/base/frameworks/openflow/consts.bro index 776af5ad46..c2e890ea48 100644 --- a/scripts/base/frameworks/openflow/consts.bro +++ b/scripts/base/frameworks/openflow/consts.bro @@ -1,7 +1,7 @@ # All types/constants not specific to Openflow will be defined here # unitl they somehow get into bro. -module Openflow; +module OpenFlow; # Some cookie specific constants. # first 24 bits diff --git a/scripts/base/frameworks/openflow/main.bro b/scripts/base/frameworks/openflow/main.bro index 6132731f03..3e2f3eca36 100644 --- a/scripts/base/frameworks/openflow/main.bro +++ b/scripts/base/frameworks/openflow/main.bro @@ -8,7 +8,7 @@ ##! for shunting, please look at the PACF framework, which provides higher ##! level functions and can use the OpenFlow framework as a backend. -module Openflow; +module OpenFlow; @load ./consts @load ./types @@ -50,6 +50,16 @@ export { ## msg: Message to describe the event. 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. # ### @@ -76,6 +86,7 @@ export { global generate_cookie: function(cookie: count &default=0): count; } + # the flow_mod function wrapper 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; } +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. # all flow entries from the openflow framework should have the # 42 bit of the cookie set. @@ -131,7 +185,7 @@ function get_cookie_gid(cookie: count): count { if( is_valid_cookie(cookie) ) return ( - (cookie - (COOKIE_BID_START * BRO_COOKIE_ID) - + (cookie - (COOKIE_BID_START * BRO_COOKIE_ID) - (cookie - ((cookie / COOKIE_GID_START) * COOKIE_GID_START))) / COOKIE_GID_START ); diff --git a/scripts/base/frameworks/openflow/plugins/ryu.bro b/scripts/base/frameworks/openflow/plugins/ryu.bro index feab410c86..181c9d89be 100644 --- a/scripts/base/frameworks/openflow/plugins/ryu.bro +++ b/scripts/base/frameworks/openflow/plugins/ryu.bro @@ -3,7 +3,7 @@ @load base/utils/exec @load base/utils/json -module Openflow; +module OpenFlow; export { redef enum Plugin += { @@ -16,17 +16,17 @@ export { ## ## host_port: Controller listen port. ## - ## dpid: Openflow switch datapath id. + ## dpid: OpenFlow switch datapath id. ## - ## Returns: Openflow::Controller record - global ryu_new: function(host: addr, host_port: count, dpid: count): Openflow::Controller; + ## 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. + ## OpenFlow switch datapath id. ryu_dpid: count &optional; ## Enable debug mode - output JSON to stdout; do not perform actions ryu_debug: bool &default=F; @@ -58,7 +58,7 @@ type ryu_ofp_flow_mod: record { hard_timeout: count &optional; priority: count &optional; flags: count &optional; - match: Openflow::ofp_match; + match: OpenFlow::ofp_match; actions: vector of ryu_flow_action; }; @@ -72,7 +72,7 @@ const ryu_url: table[ofp_flow_mod_command] of string = { }; # 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 ) { @@ -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. local mod: ryu_ofp_flow_mod = ryu_ofp_flow_mod( $dpid=state$ryu_dpid, - $cookie=Openflow::generate_cookie(flow_mod$cookie), + $cookie=OpenFlow::generate_cookie(flow_mod$cookie), $idle_timeout=flow_mod$idle_timeout, $hard_timeout=flow_mod$hard_timeout, $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]; 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; } @@ -115,7 +115,7 @@ function ryu_flow_mod(state: Openflow::ControllerState, match: ofp_match, flow_m { print url; print to_json(mod); - event Openflow::flow_mod_success(match, flow_mod); + event OpenFlow::flow_mod_success(match, flow_mod); return T; } @@ -130,11 +130,11 @@ function ryu_flow_mod(state: Openflow::ControllerState, match: ofp_match, flow_m when ( local result = ActiveHTTP::request(request) ) { 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 { 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; } } @@ -142,7 +142,7 @@ function ryu_flow_mod(state: Openflow::ControllerState, match: ofp_match, flow_m 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); @@ -165,8 +165,8 @@ function ryu_flow_clear(state: Openflow::ControllerState): bool } # 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]; } diff --git a/scripts/base/frameworks/openflow/types.bro b/scripts/base/frameworks/openflow/types.bro index 24f4e49562..e979ec75a5 100644 --- a/scripts/base/frameworks/openflow/types.bro +++ b/scripts/base/frameworks/openflow/types.bro @@ -1,4 +1,4 @@ -module Openflow; +module OpenFlow; @load ./consts @@ -39,6 +39,8 @@ export { nw_tos: count &optional; # IP protocol or lower 8 bits of ARP opcode. 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. nw_src: addr &optional; # IP destination address. @@ -56,6 +58,9 @@ export { # it so we always can identify our flows... cookie: count; # &default=BRO_COOKIE_ID * COOKIE_BID_START; # 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_*. command: ofp_flow_mod_command; # &default=OFPFC_ADD; ## Idle time before discarding (seconds). @@ -64,6 +69,9 @@ export { hard_timeout: count &default=0; ## Priority level of flow entry. 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 flags: count &default=0; ## Output ports to send data to. diff --git a/testing/btest/Baseline/scripts.base.frameworks.openflow.ryu-basic/.stdout b/testing/btest/Baseline/scripts.base.frameworks.openflow.ryu-basic/.stdout new file mode 100644 index 0000000000..5ef044f707 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.frameworks.openflow.ryu-basic/.stdout @@ -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} diff --git a/testing/btest/scripts/base/frameworks/openflow/ryu-basic.bro b/testing/btest/scripts/base/frameworks/openflow/ryu-basic.bro new file mode 100644 index 0000000000..319567fa6a --- /dev/null +++ b/testing/btest/scripts/base/frameworks/openflow/ryu-basic.bro @@ -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); + }