diff --git a/scripts/base/frameworks/openflow/main.bro b/scripts/base/frameworks/openflow/main.bro index f1f69ce613..11152d2b67 100644 --- a/scripts/base/frameworks/openflow/main.bro +++ b/scripts/base/frameworks/openflow/main.bro @@ -84,7 +84,7 @@ export { type ofp_match: record { # Wildcard fields. - wildcards: count &optional; + #wildcards: count &optional; # Input switch port. in_port: count &optional; # Ethernet source address. @@ -112,53 +112,63 @@ export { }; 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; - _len: count &default=8; + #_len: count &default=8; + # Output port. _port: count &default=OFPP_FLOOD; - _max_len: count &optional; + #_max_len: count &optional; }; -# TODO: -# type ofp_flow_mod: record { -# header: ofp_header; -# # Fields to match -# match: ofp_match; -# # Opaque controller-issued identifier. -# cookie: count &optional; -# -# # Flow actions -# -# # 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; -# }; +#type ofp_flow_mod_flags: enum { + # Send flow removed message when flow + # expires or is deleted. + const OFPFF_SEND_FLOW_REM = 0x1; + # Check for overlapping entries first. + const OFPFF_CHECK_OVERLAP = 0x2; + # Remark this is for emergency. + # Flows added with this are only used + # when the controller is disconnected. + const OFPFF_EMERG = 0x4; +#}; - global flow_mod: function( - dpid: count, cookie: count, idle_timeout: count, hard_timeout: count, - actions: vector of ofp_action_output, match: ofp_match): bool; + type ofp_flow_mod: record { + # header: ofp_header; + # 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 -type FlowModFunc: function( - dpid: count, cookie: count, idle_timeout:count, hard_timeout: count, - actions: vector of ofp_action_output, match: ofp_match): bool; +type FlowModFunc: function(dpid: count, flow_mod: ofp_flow_mod): bool; # Flow Modification function global FlowMod: FlowModFunc; @@ -170,10 +180,8 @@ function register_openflow_mod_func(func: FlowModFunc) { FlowMod = func; } -function flow_mod( - dpid: count, cookie: count, idle_timeout:count, hard_timeout:count, - actions: vector of ofp_action_output, match: ofp_match): bool { - return FlowMod(dpid, cookie, idle_timeout, hard_timeout, actions, match); +function flow_mod(dpid: count, flow_mod: ofp_flow_mod): bool { + return FlowMod(dpid, flow_mod); } event bro_init() &priority=100000 { diff --git a/scripts/base/frameworks/openflow/plugins/ryu.bro b/scripts/base/frameworks/openflow/plugins/ryu.bro index 745dcb4e04..a0905ba1a1 100644 --- a/scripts/base/frameworks/openflow/plugins/ryu.bro +++ b/scripts/base/frameworks/openflow/plugins/ryu.bro @@ -6,9 +6,13 @@ module Openflow; 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 { # The type should be never changed... # 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 # https://media.readthedocs.org/pdf/ryu/latest/ryu.pdf # on page 278-299 -type ryu_flow_add: record { +type ryu_flow_mod: record { dpid: count; cookie: count &optional; cookie_mask: count &optional; @@ -37,42 +41,103 @@ type ryu_flow_add: record { # register the ryu openflow plugin flow_mod function hook register_openflow_plugin() { register_openflow_mod_func( - function( - dpid: count, cookie: count, idle_timeout: count, hard_timeout: count, - actions: vector of ofp_action_output, match: ofp_match): bool { - local ryu_flow_actions: vector of ryu_flow_action_output; - for(i in actions) { - if(actions[i]$_type == Openflow::OFPAT_OUTPUT) { - ryu_flow_actions[|ryu_flow_actions|] = ryu_flow_action_output($_port=actions[i]$_port); + 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 record for the restAPI. - 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); - # Create the ActiveHTTP request and convert the record to a JSON string - local request: ActiveHTTP::Request = ActiveHTTP::Request($url=controller_uri, $method="POST", $client_data=JSON::convert(ryu_flow_mod)); - # Execute call to RyuRestAPI + # 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("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 { - 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; } } # Add reverse flow because openflow only uses unidirectional flows. - if(|actions| == 1 && (match$dl_type == ETH_IPv4 || match$dl_type == ETH_IPv6)) { - local reverse_match: ofp_match; - local reverse_actions: vector of ryu_flow_action_output; - reverse_actions[|reverse_actions|] = ryu_flow_action_output($_port=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); - 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); - local reverse_request: ActiveHTTP::Request = ActiveHTTP::Request($url=controller_uri, $method="POST", $addl_curl_args=fmt("-d '%s'", JSON::convert(reverse_flow_mod))); + 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("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 { - 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; } } diff --git a/scripts/site/openflow-shunt.bro b/scripts/site/openflow-shunt.bro index ecb14815d3..92fab94e65 100644 --- a/scripts/site/openflow-shunt.bro +++ b/scripts/site/openflow-shunt.bro @@ -19,6 +19,8 @@ const hard_timeout = 0; const in_port = 3; const out_port = 1; +global delete_flow: bool = F; + export { ## Number of bytes transferred before shunting a flow. const size_threshold = 1024000 &redef; @@ -33,27 +35,62 @@ export { } 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 ) { + # create openflow flow_mod add records from connection data and given default constants local actions: vector of Openflow::ofp_action_output; actions[|actions|] = Openflow::ofp_action_output($_port=out_port); - + # flow layer 4 protocol local nw_proto = Openflow::IP_TCP; if(is_udp_port(c$id$orig_p)) { nw_proto = Openflow::IP_UDP; } else if(is_icmp_port(c$id$orig_p)) { 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); - when ( local result = Openflow::flow_mod(dpid, cookie, idle_timeout, hard_timeout, actions, match) ) { + # call openflow framework + when ( local result = Openflow::flow_mod(dpid, flow_mod) ) { if(result) { event OpenflowShunt::shunt_triggered(c); } } - return -1sec; + if(delete_flow) { + return -1sec; + } else { + delete_flow = T; + return 15sec; + } } return poll_interval;