diff --git a/ryu/app/ofctl_rest.py b/ryu/app/ofctl_rest.py index edef9a75..a79cec9d 100644 --- a/ryu/app/ofctl_rest.py +++ b/ryu/app/ofctl_rest.py @@ -433,10 +433,12 @@ def __init__(self, *args, **kwargs): controller=StatsController, action='get_desc_stats', conditions=dict(method=['GET'])) + ''' uri = path + '/flow/{dpid}' mapper.connect('stats', uri, controller=StatsController, action='get_flow_stats', conditions=dict(method=['GET'])) + ''' uri = path + '/port/{dpid}' mapper.connect('stats', uri, @@ -472,7 +474,7 @@ def __init__(self, *args, **kwargs): mapper.connect('stats', uri, controller=StatsController, action='get_group_stats', conditions=dict(method=['GET'])) - + uri = path + '/flowentry/{cmd}' mapper.connect('stats', uri, controller=StatsController, action='mod_flow_entry', diff --git a/ryu/app/openstate/echo_server.py b/ryu/app/openstate/echo_server.py deleted file mode 100644 index af098341..00000000 --- a/ryu/app/openstate/echo_server.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python - -""" -An echo server that uses select to handle multiple clients at a time. -Command-line parameter: port=listening port -""" - -import select -import socket -import sys - - -if len(sys.argv)!=2: - print("You need to specify a listening port!") - sys.exit() - -host = '' -port = int(sys.argv[1]) -backlog = 5 # maximum number of queued connections -size = 1024 # buffer size -server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -server.bind((host,port)) -server.listen(backlog) -input = [server,sys.stdin] -running = 1 -print("Press any key to stop the server...") -while running: - # The Python select module allows an application to wait for input from multiple sockets at a time. - inputready,outputready,exceptready = select.select(input,[],[]) - - for s in inputready: - - if s == server: - # handle the server socket - client, address = server.accept() - print("New client at "+address[0]+":"+str(address[1])) - input.append(client) - - elif s == sys.stdin: - # handle standard input - junk = sys.stdin.readline() - running = 0 - - else: - # handle all other sockets - data = "[from h"+sys.argv[1][0]+"]: " - data = data+s.recv(size) - if data: - try: - s.send(data) - except socket.error, e: - s.close() - input.remove(s) - break - else: - s.close() - input.remove(s) -server.close() diff --git a/ryu/app/openstate/forw_cons_m_to_m_topo.py b/ryu/app/openstate/forw_cons_m_to_m_topo.py deleted file mode 100644 index 0c60eeda..00000000 --- a/ryu/app/openstate/forw_cons_m_to_m_topo.py +++ /dev/null @@ -1,39 +0,0 @@ -from mininet.topo import Topo - -class MyTopo( Topo ): - "Simple topology example." - - def __init__( self): - "Create custom topo." - - # Add default members to class. - Topo.__init__(self) - - # Add nodes - - Host1=self.addHost('h1', ip='10.0.0.1/24') - Host2=self.addHost('h2', ip='10.0.0.2/24') - switch1=self.addSwitch('s1') - switch2=self.addSwitch('s2') - switch3=self.addSwitch('s3') - switch4=self.addSwitch('s4') - switch5=self.addSwitch('s5') - switch6=self.addSwitch('s6') - switch7=self.addSwitch('s7') - - # Add edges - self.addLink( Host1, switch1, 1, 1) - self.addLink( switch1, switch2, 2, 1) - self.addLink( switch1, switch3, 4, 1) - self.addLink( switch1, switch4, 3, 2) - self.addLink( switch2, switch4, 2, 1) - self.addLink( switch3, switch4, 2, 3) - self.addLink( switch4, switch5, 4, 1) - self.addLink( switch4, switch7, 5, 2) - self.addLink( switch4, switch6, 6, 1) - self.addLink( switch5, switch7, 2, 1) - self.addLink( switch6, switch7, 2, 3) - self.addLink( switch7, Host2, 4, 1) - - -topos = { 'mytopo': ( lambda: MyTopo() ) } \ No newline at end of file diff --git a/ryu/app/openstate/forw_cons_topo.py b/ryu/app/openstate/forw_cons_topo.py deleted file mode 100644 index 9c29bd33..00000000 --- a/ryu/app/openstate/forw_cons_topo.py +++ /dev/null @@ -1,33 +0,0 @@ -from mininet.topo import Topo - -class MyTopo( Topo ): - "Simple topology example." - - def __init__( self): - "Create custom topo." - - # Add default members to class. - Topo.__init__(self) - - # Add nodes - - Host1=self.addHost('h1', ip='10.0.0.1/24') - Host2=self.addHost('h2', ip='10.0.0.2/24') - switch1=self.addSwitch('s1') - switch2=self.addSwitch('s2') - switch3=self.addSwitch('s3') - switch4=self.addSwitch('s4') - switch5=self.addSwitch('s5') - - # Add edges - self.addLink( Host1, switch1, 1, 1) - self.addLink( switch1, switch2, 2, 1) - self.addLink( switch1, switch3, 3, 1) - self.addLink( switch1, switch4, 4, 1) - self.addLink( switch2, switch5, 2, 1) - self.addLink( switch3, switch5, 2, 2) - self.addLink( switch4, switch5, 2, 3) - self.addLink( switch5, Host2, 4, 1) - - -topos = { 'mytopo': ( lambda: MyTopo() ) } \ No newline at end of file diff --git a/ryu/app/openstate/forwarding_consistency_1_to_many.py b/ryu/app/openstate/forwarding_consistency_1_to_many.py deleted file mode 100644 index 7d1396f1..00000000 --- a/ryu/app/openstate/forwarding_consistency_1_to_many.py +++ /dev/null @@ -1,233 +0,0 @@ -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging -import struct - -from ryu.base import app_manager -from ryu.controller import ofp_event -from ryu.controller.handler import MAIN_DISPATCHER, CONFIG_DISPATCHER, \ - HANDSHAKE_DISPATCHER -from ryu.controller.handler import set_ev_cls -from ryu.ofproto import ofproto_v1_3 -from ryu.lib.packet import packet -from ryu.lib.packet import ethernet -from ryu.topology import event - -LOG = logging.getLogger('app.openstate.forwarding_consistency_1_to_many') - -SWITCH_PORTS = 4 - -class OSLoadBalancing(app_manager.RyuApp): - OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] - - def __init__(self, *args, **kwargs): - LOG.info("OpenState Forwarding Consistency sample app initialized") - LOG.info("Supporting MAX %d ports per switch" % SWITCH_PORTS) - super(OSLoadBalancing, self).__init__(*args, **kwargs) - self.mac_to_port = {} - - @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) - def switch_features_handler(self, ev): - msg = ev.msg - datapath = msg.datapath - ofproto = datapath.ofproto - - self.send_features_request(datapath) - self.send_group_mod(datapath) - self.send_table_mod(datapath) - - self.send_key_lookup(datapath) - self.send_key_update(datapath) - - # install table-miss flow entry (if no rule matching, send it to controller) - # self.add_flow(datapath, True) - - # install other flow entries - self.add_flow(datapath, False) - - ''' - STATEFUL TABLE 0 - - Lookup-scope=IPV4_DST,IPV4_SRC,TCP_DST,TCP_SRC - Update-scope=IPV4_DST,IPV4_SRC,TCP_DST,TCP_SRC - - _______ - | |--h2 - h1--| S1 |--h3 - |_______|--h4 - - h1 is the client 10.0.0.1 - h1 connects to an EchoServer 10.0.0.2:80 - h2, h3, h4 are 3 replicas of the server: - h2 is listening at 10.0.0.2:200 - h3 is listening at 10.0.0.3:300 - h4 is listening at 10.0.0.4:400 - - $ ryu-manager ryu/ryu/app/openstate/forwarding_consistency_1_to_many.py - $ sudo mn --topo single,4 --switch user --mac --controller remote - mininet> xterm h1 h1 h1 h2 h3 h4 - h2# python ryu/ryu/app/openstate/echo_server.py 200 - h3# python ryu/ryu/app/openstate/echo_server.py 300 - h4# python ryu/ryu/app/openstate/echo_server.py 400 - - Let's try to connect from h1 to the EchoServer and send some message: - h1# nc 10.0.0.2 80 - If we keep the connection open, the responding EchoServer is always the same. - If we open another connection (from the 2nd terminal of h1) maybe we get connected to another replica. - If we close it and re-connect, maybe we are connected to another replica. - ''' - - def add_flow(self, datapath, table_miss=False): - ofproto = datapath.ofproto - parser = datapath.ofproto_parser - LOG.info("Configuring flow table for switch %d" % datapath.id) - - if table_miss: - LOG.debug("Installing table miss...") - actions = [parser.OFPActionOutput( - ofproto.OFPP_CONTROLLER, ofproto.OFPCML_NO_BUFFER)] - match = parser.OFPMatch() - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=0, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - - datapath.send_msg(mod) - - else: - - # ARP packets flooding - match = parser.OFPMatch(eth_type=0x0806) - actions = [ - parser.OFPActionOutput(ofproto.OFPP_FLOOD)] - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32768, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - - # Reverse path flow - for in_port in range(2, SWITCH_PORTS + 1): - src_ip="10.0.0."+str(in_port) - src_eth="00:00:00:00:00:0"+str(in_port) - src_tcp=in_port*100 - # we need to match an IPv4 (0x800) TCP (6) packet to do SetField() - match = parser.OFPMatch(in_port=in_port, eth_type=0x800, ip_proto=6, ipv4_src=src_ip,eth_src=src_eth,tcp_src=src_tcp) - actions = [parser.OFPActionSetField(ipv4_src="10.0.0.2"), - parser.OFPActionSetField(eth_src="00:00:00:00:00:02"), - parser.OFPActionSetField(tcp_src=80), - parser.OFPActionOutput(1,0)] - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32767, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - # the state of a flow is the selected output port for that flow - for state in range(SWITCH_PORTS): - if state == 0: - # if state=DEFAULT => send it to the first group entry in the group table - actions = [ - parser.OFPActionGroup(1)] - match = parser.OFPMatch( - in_port=1, state=state, eth_type=0x800, ip_proto=6) - else: - # state x means output port x+1 - dest_ip="10.0.0."+str(state+1) - dest_eth="00:00:00:00:00:0"+str(state+1) - dest_tcp=(state+1)*100 - actions = [ - parser.OFPActionSetField(ipv4_dst=dest_ip), - parser.OFPActionSetField(eth_dst=dest_eth), - parser.OFPActionSetField(tcp_dst=dest_tcp), - parser.OFPActionOutput(state+1, 0), - parser.OFPActionSetState(state, 0)] - match = parser.OFPMatch( - in_port=1, state=state, eth_type=0x800, ip_proto=6) - inst = [ - parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, - hard_timeout=0, priority=32767, - buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - def send_group_mod(self, datapath): - ofp = datapath.ofproto - ofp_parser = datapath.ofproto_parser - buckets = [] - # Action Bucket: xterm h1 h1 h1 h2 h3 h4 - h2# python ryu/ryu/app/openstate/echo_server.py 200 - h3# python ryu/ryu/app/openstate/echo_server.py 300 - h4# python ryu/ryu/app/openstate/echo_server.py 400 - - Let's try to connect from h1 to the EchoServer and send some message: - h1# nc 10.0.0.2 80 - If we keep the connection open, the responding EchoServer is always the same. - If we open another connection (from the 2nd terminal of h1) maybe we get connected to another replica. - If we close it and re-connect, maybe we are connected to another replica. - - With respect to basic application, the first table write the action set, while the second apply it. - We want to test the SetState() parametrization. - ''' - - def add_flow(self, datapath, table_miss=False): - ofproto = datapath.ofproto - parser = datapath.ofproto_parser - LOG.info("Configuring flow table for switch %d" % datapath.id) - - if table_miss: - LOG.debug("Installing table miss...") - actions = [parser.OFPActionOutput( - ofproto.OFPP_CONTROLLER, ofproto.OFPCML_NO_BUFFER)] - match = parser.OFPMatch() - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=0, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - - datapath.send_msg(mod) - - else: - - # ARP packets flooding - match = parser.OFPMatch(eth_type=0x0806) - actions = [ - parser.OFPActionOutput(ofproto.OFPP_FLOOD)] - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32768, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - - # Reverse path flow - for in_port in range(2, SWITCH_PORTS + 1): - src_ip="10.0.0."+str(in_port) - src_eth="00:00:00:00:00:0"+str(in_port) - src_tcp=in_port*100 - # we need to match an IPv4 (0x800) TCP (6) packet to do SetField() - match = parser.OFPMatch(in_port=in_port, eth_type=0x800, ip_proto=6, ipv4_src=src_ip,eth_src=src_eth,tcp_src=src_tcp) - actions = [parser.OFPActionSetField(ipv4_src="10.0.0.2"), - parser.OFPActionSetField(eth_src="00:00:00:00:00:02"), - parser.OFPActionSetField(tcp_src=80), - parser.OFPActionOutput(1,0)] - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32767, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - # the state of a flow is the selected output port for that flow - for state in range(SWITCH_PORTS): - if state == 0: - # if state=DEFAULT => send it to the first group entry in the group table - actions = [ - parser.OFPActionGroup(1)] - match = parser.OFPMatch( - in_port=1, state=state, eth_type=0x800, ip_proto=6) - else: - # state x means output port x+1 - dest_ip="10.0.0."+str(state+1) - dest_eth="00:00:00:00:00:0"+str(state+1) - dest_tcp=(state+1)*100 - actions = [ - parser.OFPActionSetField(ipv4_dst=dest_ip), - parser.OFPActionSetField(eth_dst=dest_eth), - parser.OFPActionSetField(tcp_dst=dest_tcp), - parser.OFPActionOutput(state+1, 0), - parser.OFPActionSetState(state, 0)] - match = parser.OFPMatch( - in_port=1, state=state, eth_type=0x800, ip_proto=6) - inst = [ - parser.OFPInstructionActions( - ofproto.OFPIT_WRITE_ACTIONS, actions), - parser.OFPInstructionGotoTable(1)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, - hard_timeout=0, priority=32767, - buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - # Table 1 entry - match = parser.OFPMatch() - inst = [] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=1, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32767, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - def send_group_mod(self, datapath): - ofp = datapath.ofproto - ofp_parser = datapath.ofproto_parser - buckets = [] - # Action Bucket: xterm h1 h1 h1 h2 h3 h4 - h2# python ryu/ryu/app/openstate/echo_server.py 200 - h3# python ryu/ryu/app/openstate/echo_server.py 300 - h4# python ryu/ryu/app/openstate/echo_server.py 400 - - Let's try to connect from h1 to the EchoServer and send some message: - h1# nc 10.0.0.2 80 - If we keep the connection open, the responding EchoServer is always the same. - If we open another connection (from the 2nd terminal of h1) maybe we get connected to another replica. - If we close it and re-connect, maybe we are connected to another replica. - - With respect to basic application, here we want to test if we can put many SetState() actions in the same Instruction. - ''' - - def add_flow(self, datapath, table_miss=False): - ofproto = datapath.ofproto - parser = datapath.ofproto_parser - LOG.info("Configuring flow table for switch %d" % datapath.id) - - if table_miss: - LOG.debug("Installing table miss...") - actions = [parser.OFPActionOutput( - ofproto.OFPP_CONTROLLER, ofproto.OFPCML_NO_BUFFER)] - match = parser.OFPMatch() - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=0, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - - datapath.send_msg(mod) - - else: - - # ARP packets flooding - match = parser.OFPMatch(eth_type=0x0806) - actions = [ - parser.OFPActionOutput(ofproto.OFPP_FLOOD)] - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32768, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - - # Reverse path flow - for in_port in range(2, SWITCH_PORTS + 1): - src_ip="10.0.0."+str(in_port) - src_eth="00:00:00:00:00:0"+str(in_port) - src_tcp=in_port*100 - # we need to match an IPv4 (0x800) TCP (6) packet to do SetField() - match = parser.OFPMatch(in_port=in_port, eth_type=0x800, ip_proto=6, ipv4_src=src_ip,eth_src=src_eth,tcp_src=src_tcp) - actions = [parser.OFPActionSetField(ipv4_src="10.0.0.2"), - parser.OFPActionSetField(eth_src="00:00:00:00:00:02"), - parser.OFPActionSetField(tcp_src=80), - parser.OFPActionOutput(1,0)] - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32767, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - # the state of a flow is the selected output port for that flow - for state in range(SWITCH_PORTS): - if state == 0: - # if state=DEFAULT => send it to the first group entry in the group table - actions = [ - parser.OFPActionGroup(1)] - match = parser.OFPMatch( - in_port=1, state=state, eth_type=0x800, ip_proto=6) - else: - # state x means output port x+1 - dest_ip="10.0.0."+str(state+1) - dest_eth="00:00:00:00:00:0"+str(state+1) - dest_tcp=(state+1)*100 - actions = [ - parser.OFPActionSetField(ipv4_dst=dest_ip), - parser.OFPActionSetField(eth_dst=dest_eth), - parser.OFPActionSetField(tcp_dst=dest_tcp), - parser.OFPActionOutput(state+1, 0), - parser.OFPActionSetState(state, 0), - parser.OFPActionSetState(0, 0), - parser.OFPActionSetState(state, 0)] - match = parser.OFPMatch( - in_port=1, state=state, eth_type=0x800, ip_proto=6) - inst = [ - parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, - hard_timeout=0, priority=32767, - buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - def send_group_mod(self, datapath): - ofp = datapath.ofproto - ofp_parser = datapath.ofproto_parser - buckets = [] - # Action Bucket: send it to the first group entry in the group table - actions = [ - parser.OFPActionGroup(1)] - match = parser.OFPMatch( - in_port=1, state=state, eth_type=0x800) - else: - # state x means output port x+1 - actions = [ - parser.OFPActionOutput(state+1, 0), - parser.OFPActionSetState(state, 0)] - match = parser.OFPMatch( - in_port=1, state=state, eth_type=0x800) - inst = [ - parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, - hard_timeout=0, priority=32767, - buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - - def add_flow_2(self, datapath, table_miss=False): - ofproto = datapath.ofproto - parser = datapath.ofproto_parser - LOG.info("Configuring flow table for switch %d" % datapath.id) - - if table_miss: - LOG.debug("Installing table miss...") - actions = [parser.OFPActionOutput( - ofproto.OFPP_CONTROLLER, ofproto.OFPCML_NO_BUFFER)] - match = parser.OFPMatch() - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=0, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - - datapath.send_msg(mod) - - else: - - match = parser.OFPMatch(in_port=1) - actions = [ - parser.OFPActionOutput(2,0)] - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32768, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - match = parser.OFPMatch(in_port=2) - actions = [ - parser.OFPActionOutput(1,0)] - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32768, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - def add_flow_3(self, datapath, table_miss=False): - ofproto = datapath.ofproto - parser = datapath.ofproto_parser - LOG.info("Configuring flow table for switch %d" % datapath.id) - - if table_miss: - LOG.debug("Installing table miss...") - actions = [parser.OFPActionOutput( - ofproto.OFPP_CONTROLLER, ofproto.OFPCML_NO_BUFFER)] - match = parser.OFPMatch() - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=0, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - - datapath.send_msg(mod) - - else: - - # ARP packets flooding - match = parser.OFPMatch(eth_type=0x0806) - actions = [ - parser.OFPActionOutput(ofproto.OFPP_FLOOD)] - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32768, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - - # Reverse path flow - for state in range(1,SWITCH_PORTS): - match = parser.OFPMatch(in_port=4, state=state, eth_type=0x800) - actions = [ - parser.OFPActionOutput(state,0)] - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32767, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - # the state of a flow is the selected output port for that flow - for in_port in range(1,SWITCH_PORTS): - # state x means output port x+1 - actions = [ - parser.OFPActionOutput(4, 0), - parser.OFPActionSetState(in_port, 0)] - match = parser.OFPMatch( - in_port=in_port, eth_type=0x800) - inst = [ - parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, - hard_timeout=0, priority=32767, - buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - def send_group_mod(self, datapath): - ofp = datapath.ofproto - ofp_parser = datapath.ofproto_parser - buckets = [] - # Action Bucket: send it to the first group entry in the group table - actions = [ - parser.OFPActionGroup(1)] - match = parser.OFPMatch( - in_port=1, state=state, eth_type=0x800) - else: - # state x means output port x+1 - actions = [ - parser.OFPActionOutput(state+1, 0), - parser.OFPActionSetState(state, 0)] - match = parser.OFPMatch( - in_port=1, state=state, eth_type=0x800) - inst = [ - parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, - hard_timeout=0, priority=32767, - buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - - def add_flow_2(self, datapath, table_miss=False): - ofproto = datapath.ofproto - parser = datapath.ofproto_parser - LOG.info("Configuring flow table for switch %d" % datapath.id) - - if table_miss: - LOG.debug("Installing table miss...") - actions = [parser.OFPActionOutput( - ofproto.OFPP_CONTROLLER, ofproto.OFPCML_NO_BUFFER)] - match = parser.OFPMatch() - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=0, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - - datapath.send_msg(mod) - - else: - - match = parser.OFPMatch(in_port=1) - actions = [ - parser.OFPActionOutput(2,0)] - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32768, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - match = parser.OFPMatch(in_port=2) - actions = [ - parser.OFPActionOutput(1,0)] - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32768, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - def add_flow_3(self, datapath, table_miss=False): - ofproto = datapath.ofproto - parser = datapath.ofproto_parser - LOG.info("Configuring flow table for switch %d" % datapath.id) - - if table_miss: - LOG.debug("Installing table miss...") - actions = [parser.OFPActionOutput( - ofproto.OFPP_CONTROLLER, ofproto.OFPCML_NO_BUFFER)] - match = parser.OFPMatch() - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=0, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - - datapath.send_msg(mod) - - else: - - # ARP packets flooding - match = parser.OFPMatch(eth_type=0x0806) - actions = [ - parser.OFPActionOutput(ofproto.OFPP_FLOOD)] - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32768, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - - # forward path flow - for in_port in range(1, SWITCH_PORTS): - match = parser.OFPMatch(in_port=in_port) - actions = [ - parser.OFPActionOutput(4,0)] - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32767, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - # the state of a flow is the selected output port for that flow - for state in range(SWITCH_PORTS): - if state == 0: - # if state=DEFAULT => send it to the first group entry in the group table - actions = [ - parser.OFPActionGroup(1)] - match = parser.OFPMatch( - in_port=4, state=state, eth_type=0x800) - else: - # state x means output port x+1 - actions = [ - parser.OFPActionOutput(state, 0), - parser.OFPActionSetState(state, 0)] - match = parser.OFPMatch( - in_port=4, state=state, eth_type=0x800) - inst = [ - parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, - hard_timeout=0, priority=32767, - buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - def send_group_mod_1(self, datapath): - ofp = datapath.ofproto - ofp_parser = datapath.ofproto_parser - buckets = [] - # Action Bucket: send it to the first group entry in the group table actions = [ - parser.OFPActionOutput(1,0)] - inst = [parser.OFPInstructionActions( + parser.OFPActionOutput(1), + parser.OFPActionSetState(in_port-1, 0, bw_flag=1)] + match = parser.OFPMatch( + in_port=in_port, state=0, eth_type=0x800) + inst = [ + parser.OFPInstructionActions( ofproto.OFPIT_APPLY_ACTIONS, actions)] mod = parser.OFPFlowMod( datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32767, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, + command=ofproto.OFPFC_ADD, idle_timeout=0, + hard_timeout=0, priority=32767, + buffer_id=ofproto.OFP_NO_BUFFER, + out_port=ofproto.OFPP_ANY, out_group=ofproto.OFPG_ANY, flags=0, match=match, instructions=inst) datapath.send_msg(mod) @@ -278,10 +283,17 @@ def add_flow_3(self, datapath, table_miss=False): # Reverse path flow - for state in range(1,SWITCH_PORTS): - match = parser.OFPMatch(in_port=4, state=state, eth_type=0x800) - actions = [ - parser.OFPActionOutput(state,0)] + for state in range(SWITCH_PORTS): + if state == 0: + # if state=DEFAULT => send it to the first group entry in the group table + actions = [ + parser.OFPActionGroup(1)] + match = parser.OFPMatch( + in_port=4, state=state, eth_type=0x800) + else: + match = parser.OFPMatch(in_port=4, state=state, eth_type=0x800) + actions = [ + parser.OFPActionOutput(state,0)] inst = [parser.OFPInstructionActions( ofproto.OFPIT_APPLY_ACTIONS, actions)] mod = parser.OFPFlowMod( @@ -298,7 +310,7 @@ def add_flow_3(self, datapath, table_miss=False): # state x means output port x+1 actions = [ parser.OFPActionOutput(4, 0), - parser.OFPActionSetState(in_port, 0)] + parser.OFPActionSetState(in_port, 0, bw_flag=1)] match = parser.OFPMatch( in_port=in_port, eth_type=0x800) inst = [ @@ -354,27 +366,12 @@ def add_flow_4(self, datapath, table_miss=False): flags=0, match=match, instructions=inst) datapath.send_msg(mod) - for in_port in range(4, 7): - match = parser.OFPMatch(in_port=in_port) - actions = [] - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions), - parser.OFPInstructionGotoTable(1)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32767, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - # the state of a flow is the selected output port for that flow for in_port in range(1,4): # if state=DEFAULT => send it to the first group entry in the group table actions = [ parser.OFPActionGroup(1), - parser.OFPActionSetState(in_port, 1)] + parser.OFPActionSetState(in_port, 0, bw_flag=1)] match = parser.OFPMatch( in_port=in_port, state=0, eth_type=0x800) inst = [ @@ -390,7 +387,7 @@ def add_flow_4(self, datapath, table_miss=False): datapath.send_msg(mod) for state in range(4,7): - # state x means output port x+1 + # state x means output port x actions = [ parser.OFPActionOutput(state, 0)] match = parser.OFPMatch( @@ -407,10 +404,25 @@ def add_flow_4(self, datapath, table_miss=False): flags=0, match=match, instructions=inst) datapath.send_msg(mod) - #SETUP TABLE 1 - # the state of a flow is the selected output port for that flow for in_port in range(4,7): + # if state=DEFAULT => send it to the first group entry in the group table + actions = [ + parser.OFPActionGroup(2), + parser.OFPActionSetState(in_port, 0, bw_flag=1)] + match = parser.OFPMatch( + in_port=in_port, state=0, eth_type=0x800) + inst = [ + parser.OFPInstructionActions( + ofproto.OFPIT_APPLY_ACTIONS, actions)] + mod = parser.OFPFlowMod( + datapath=datapath, cookie=0, cookie_mask=0, table_id=0, + command=ofproto.OFPFC_ADD, idle_timeout=0, + hard_timeout=0, priority=32767, + buffer_id=ofproto.OFP_NO_BUFFER, + out_port=ofproto.OFPP_ANY, out_group=ofproto.OFPG_ANY, + flags=0, match=match, instructions=inst) + datapath.send_msg(mod) for state in range(1,4): # state x means output port x+1 @@ -422,7 +434,7 @@ def add_flow_4(self, datapath, table_miss=False): parser.OFPInstructionActions( ofproto.OFPIT_APPLY_ACTIONS, actions)] mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=1, + datapath=datapath, cookie=0, cookie_mask=0, table_id=0, command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, priority=32767, buffer_id=ofproto.OFP_NO_BUFFER, @@ -472,14 +484,14 @@ def send_group_mod_2(self, datapath): req = ofp_parser.OFPGroupMod(datapath, ofp.OFPGC_ADD, ofp.OFPGT_RANDOM, group_id, buckets) datapath.send_msg(req) - # second entry + buckets = [] # Action Bucket: action: set_state(i) & flood() - match: state=j & in_port=i => action: set_state(i) & output(j) - - ''' - - for in_port in range(1, SWITCH_PORTS + 1): # for each port (from 1 to #ports) - LOG.info("Installing flow rule for port %d..." % in_port) - for state in range(SWITCH_PORTS + 1): # for each state (from 0 to #ports) - - if state == 0: # DEFAULT state - actions = [ - parser.OFPActionOutput( - ofproto.OFPP_FLOOD), - parser.OFPActionSetState(in_port,0)] - match = parser.OFPMatch( - in_port=in_port, state=state) - - else: - actions = [ - parser.OFPActionOutput(state, 0), - parser.OFPActionSetState(in_port,0)] - match = parser.OFPMatch( - in_port=in_port, state=state) - - inst = [parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, - hard_timeout=0, priority=32768, - buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - def send_table_mod(self, datapath): - ofp = datapath.ofproto - ofp_parser = datapath.ofproto_parser - - req = ofp_parser.OFPTableMod(datapath, 0, ofp.OFPTC_TABLE_STATEFUL) - datapath.send_msg(req) - - def add_state_entry(self, datapath): - ofproto = datapath.ofproto - state = datapath.ofproto_parser.OFPStateEntry( - datapath, ofproto.OFPSC_ADD_FLOW_STATE, 6, 4, [0,0,0,0,0,2], - cookie=0, cookie_mask=0, table_id=0) - datapath.send_msg(state) - - def send_features_request(self, datapath): - ofp_parser = datapath.ofproto_parser - - req = ofp_parser.OFPFeaturesRequest(datapath) - datapath.send_msg(req) - - def send_key_lookup(self, datapath): - ofp = datapath.ofproto - - key_lookup_extractor = datapath.ofproto_parser.OFPKeyExtract( - datapath, ofp.OFPSC_SET_L_EXTRACTOR, 1, [ofp.OXM_OF_ETH_DST]) - datapath.send_msg(key_lookup_extractor) - - def send_key_update(self, datapath): - ofp = datapath.ofproto - - key_update_extractor = datapath.ofproto_parser.OFPKeyExtract( - datapath, ofp.OFPSC_SET_U_EXTRACTOR, 1, [ofp.OXM_OF_ETH_SRC]) - datapath.send_msg(key_update_extractor) - diff --git a/ryu/app/openstate/portknock.py b/ryu/app/openstate/portknock.py deleted file mode 100644 index c7e66aa3..00000000 --- a/ryu/app/openstate/portknock.py +++ /dev/null @@ -1,160 +0,0 @@ - -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging -import struct - -from ryu.base import app_manager -from ryu.controller import ofp_event -from ryu.controller.handler import MAIN_DISPATCHER, CONFIG_DISPATCHER, HANDSHAKE_DISPATCHER -from ryu.controller.handler import set_ev_cls -from ryu.ofproto import ofproto_v1_3 -from ryu.lib.packet import packet -from ryu.lib.packet import ethernet -from ryu.topology import event - -LOG = logging.getLogger('app.openstate.portknock_') - -# Last port is the one to be opened after knoking all the others -PORT_LIST = [5123, 6234, 7345, 8456, 2000] - -class OSPortKnocking(app_manager.RyuApp): - OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] - - def __init__(self, *args, **kwargs): - super(OSPortKnocking, self).__init__(*args, **kwargs) - self.mac_to_port = {} - LOG.info("OpenState Port Knocking sample app initialized") - LOG.info("Port knock sequence is %s" % PORT_LIST) - - @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) - def switch_features_handler(self, ev): - msg = ev.msg - datapath = msg.datapath - ofproto = datapath.ofproto - - self.send_features_request(datapath) - self.send_table_mod(datapath) - - self.send_key_lookup(datapath) - self.send_key_update(datapath) - - # install the xfsm machine rules: - self.add_flow(datapath) - - ''' - STATEFUL TABLE 0 - - Lookup-scope=IPV4_SRC - Update-scope=IPV4_SRC - - $ sudo mn --topo single,4 --switch user --mac --controller remote - - h2# nc -ul 2000 - - h1# ./ryu/ryu/app/openstate/test_port_knocking.sh - - ''' - - def add_flow(self, datapath, table_miss=False): - ofproto = datapath.ofproto - parser = datapath.ofproto_parser - LOG.info("Configuring XFSM for switch %d" % datapath.id) - - # ARP packets flooding - match = parser.OFPMatch(eth_type=0x0806) - actions = [ - parser.OFPActionOutput(ofproto.OFPP_FLOOD)] - inst = [parser.OFPInstructionActions( - datapath.ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32760, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - # Flow entries for port knocking (UDP ports) - ''' - state=0 (DEFAULT) - state=1,2,3 (Stage 1,2,3) - state=4 (OPEN) - - eth_type=0x0800, ip_proto=17 --> IP+UDP packet - - match: state=j => action: set_state(j+1) - match: state=4 => action: set_state(4),output(2) - - ''' - for state in range(len(PORT_LIST)): - match = parser.OFPMatch( - state=state, eth_type=0x0800, ip_proto=17, udp_dst=PORT_LIST[state]) - if not state == 4: - actions = [parser.OFPActionSetState(state +1,0)] - inst = [parser.OFPInstructionActions( - datapath.ofproto.OFPIT_APPLY_ACTIONS, actions)] - else: - actions = [parser.OFPActionOutput(2, 0), - parser.OFPActionSetState(state,0)] - inst = [ parser.OFPInstructionActions( - datapath.ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32768, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - # se sbaglio sequenza, torno allo stato DEFAULT - actions = [parser.OFPActionSetState(0,0)] - match = parser.OFPMatch() - inst = [parser.OFPInstructionActions( - datapath.ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=0, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - - datapath.send_msg(mod) - - def send_table_mod(self, datapath): - ofp = datapath.ofproto - ofp_parser = datapath.ofproto_parser - req = ofp_parser.OFPTableMod(datapath, 0, ofp.OFPTC_TABLE_STATEFUL) - datapath.send_msg(req) - - def send_features_request(self, datapath): - ofp_parser = datapath.ofproto_parser - req = ofp_parser.OFPFeaturesRequest(datapath) - datapath.send_msg(req) - - def send_key_lookup(self, datapath): - ofp = datapath.ofproto - key_lookup_extractor = datapath.ofproto_parser.OFPKeyExtract( - datapath, ofp.OFPSC_SET_L_EXTRACTOR, 1, [ofp.OXM_OF_IPV4_SRC]) - datapath.send_msg(key_lookup_extractor) - - def send_key_update(self, datapath): - ofp = datapath.ofproto - key_update_extractor = datapath.ofproto_parser.OFPKeyExtract( - datapath, ofp.OFPSC_SET_U_EXTRACTOR, 1, [ofp.OXM_OF_IPV4_SRC]) - datapath.send_msg(key_update_extractor) diff --git a/ryu/app/openstate/start_1_to_many.py b/ryu/app/openstate/start_1_to_many.py deleted file mode 100644 index f4752499..00000000 --- a/ryu/app/openstate/start_1_to_many.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/python - -from mininet.net import Mininet -from mininet.topo import Topo,SingleSwitchTopo -from mininet.cli import CLI -from mininet.node import UserSwitch,RemoteController -from mininet.term import makeTerm -import os, time -######Starting controller - - -os.system("xterm -e 'ryu-manager ~/ryu/ryu/app/openstate/forwarding_consistency_1_to_many.py'&") - - - -######Starting mininet - -mytopo=SingleSwitchTopo(4) -time.sleep(1) -print("\n********************************** HELP *********************************************") -print("\nType \"python ~/ryu/ryu/app/openstate/echo_server.py 200\" in h2's xterm") -print("Type \"python ~/ryu/ryu/app/openstate/echo_server.py 300\" in h3's xterm") -print("Type \"python ~/ryu/ryu/app/openstate/echo_server.py 400\" in h4's xterm") -print("Type \"nc 10.0.0.2 80\" in all h1's xterms\n") -print("In order to test new path selection, close and reopen netcat") -print("\nTo exit type \"ctrl+D\" or exit") -print("*************************************************************************************") -net = Mininet(topo=mytopo,switch=UserSwitch,controller=RemoteController,cleanup=True,autoSetMacs=True,listenPort=6634) -net.start() -h1,h2,h3,h4 = net.hosts[0], net.hosts[1], net.hosts[2], net.hosts[3] -for i in range(3): - makeTerm(h1) -makeTerm(h2) -makeTerm(h3) -makeTerm(h4) -CLI(net) -net.stop() -os.system("sudo mn -c") -os.system("kill -9 $(pidof -x ryu-manager)") diff --git a/ryu/app/openstate/start_link_protection.py b/ryu/app/openstate/start_link_protection.py deleted file mode 100644 index 86a93f5f..00000000 --- a/ryu/app/openstate/start_link_protection.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/python - -from mininet.net import Mininet -from mininet.topo import Topo,SingleSwitchTopo -from mininet.cli import CLI -from mininet.node import UserSwitch,RemoteController -from mininet.term import makeTerm -import os, time -######Starting controller - - -os.system("xterm -e 'ryu-manager ~/ryu/ryu/app/openstate/link_protection.py'&") - -######Starting mininet - -mytopo=SingleSwitchTopo(4) -time.sleep(1) -print("\n********************************** HELP *********************************************") -print("\nType \"ping 10.0.0.2\" in h1's first xterm") -print("Type \"ping 10.0.0.3\" in h1's second xterm") -print("In order to change the outport from 2 to 3 and viceversa\n") -print("Type \"nc -w 1 10.0.0.1 33333\" in h2's xterm or \"nc -w 1 10.0.0.1 22222\" in h3's xterm") -print("\nTo exit type \"ctrl+D\" or exit") -print("*************************************************************************************") -net = Mininet(topo=mytopo,switch=UserSwitch,controller=RemoteController,cleanup=True,autoSetMacs=True,listenPort=6634,autoStaticArp=True) -net.start() -h1,h2,h3,h4 = net.hosts[0], net.hosts[1], net.hosts[2], net.hosts[3] - -makeTerm(h1) -makeTerm(h1) -makeTerm(h2) -makeTerm(h3) - -CLI(net) -net.stop() -os.system("sudo mn -c") -os.system("kill -9 $(pidof -x ryu-manager)") diff --git a/ryu/app/openstate/start_many_to_1.py b/ryu/app/openstate/start_many_to_1.py deleted file mode 100644 index 002277da..00000000 --- a/ryu/app/openstate/start_many_to_1.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/python - -from mininet.net import Mininet -from mininet.topo import Topo -from mininet.cli import CLI -from mininet.node import UserSwitch,RemoteController -from mininet.term import makeTerm -import os, time - -class MyTopo( Topo ): - "Simple topology example." - - def __init__( self): - "Create custom topo." - - # Add default members to class. - Topo.__init__(self) - - # Add nodes - - Host1=self.addHost('h1', ip='10.0.0.1/24') - Host2=self.addHost('h2', ip='10.0.0.2/24') - switch1=self.addSwitch('s1') - switch2=self.addSwitch('s2') - switch3=self.addSwitch('s3') - switch4=self.addSwitch('s4') - switch5=self.addSwitch('s5') - - # Add edges - self.addLink( Host1, switch1, 1, 1) - self.addLink( switch1, switch2, 2, 1) - self.addLink( switch1, switch3, 3, 1) - self.addLink( switch1, switch4, 4, 1) - self.addLink( switch2, switch5, 2, 1) - self.addLink( switch3, switch5, 2, 2) - self.addLink( switch4, switch5, 2, 3) - self.addLink( switch5, Host2, 4, 1) - -######Starting controller - - -os.system("xterm -e 'ryu-manager ~/ryu/ryu/app/openstate/forwarding_consistency_many_to_1.py'&") - - - -######Starting mininet -topos = { 'mytopo': ( lambda: MyTopo() ) } -mytopo=MyTopo() -time.sleep(1) -print("\n********************************** HELP *********************************************") -print("Type \"python ~/ryu/ryu/app/openstate/echo_server.py 200\" in h2's xterm") -print("Type \"nc 10.0.0.2 200\" in h1's xterm") -print("Watching the tcpdump results, it is possible to see that forwarding consistency is guaranteed\n" - "In order to test new path selection, close and reopen netcat") -print("\nTo exit type \"ctrl+D\" or exit") -print("*************************************************************************************") -net = Mininet(topo=mytopo,switch=UserSwitch,controller=RemoteController,cleanup=True,autoSetMacs=True,autoStaticArp=True,listenPort=6634) -net.start() -os.system("xterm -e 'tcpdump -i s2-eth1'&") -os.system("xterm -e 'tcpdump -i s3-eth1'&") -os.system("xterm -e 'tcpdump -i s4-eth1'&") -h1,h2 = net.hosts[0], net.hosts[1] -makeTerm(h1) -makeTerm(h2) -CLI(net) -net.stop() -os.system("sudo mn -c") -os.system("kill -9 $(pidof -x ryu-manager)") diff --git a/ryu/app/openstate/start_many_to_1_alternative.py b/ryu/app/openstate/start_many_to_1_alternative.py deleted file mode 100644 index 9023eb3d..00000000 --- a/ryu/app/openstate/start_many_to_1_alternative.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/python - -from mininet.net import Mininet -from mininet.topo import Topo -from mininet.cli import CLI -from mininet.node import UserSwitch,RemoteController -from mininet.term import makeTerm -import os, time - -class MyTopo( Topo ): - "Simple topology example." - - def __init__( self): - "Create custom topo." - - # Add default members to class. - Topo.__init__(self) - - # Add nodes - - Host1=self.addHost('h1', ip='10.0.0.1/24') - Host2=self.addHost('h2', ip='10.0.0.2/24') - switch1=self.addSwitch('s1') - switch2=self.addSwitch('s2') - switch3=self.addSwitch('s3') - switch4=self.addSwitch('s4') - switch5=self.addSwitch('s5') - - # Add edges - self.addLink( Host1, switch1, 1, 1) - self.addLink( switch1, switch2, 2, 1) - self.addLink( switch1, switch3, 3, 1) - self.addLink( switch1, switch4, 4, 1) - self.addLink( switch2, switch5, 2, 1) - self.addLink( switch3, switch5, 2, 2) - self.addLink( switch4, switch5, 2, 3) - self.addLink( switch5, Host2, 4, 1) - -######Starting controller - - -os.system("xterm -e 'ryu-manager ~/ryu/ryu/app/openstate/forwarding_consistency_many_to_1_alternative.py'&") - - - -######Starting mininet -topos = { 'mytopo': ( lambda: MyTopo() ) } -mytopo=MyTopo() -time.sleep(1) -print("\n********************************** HELP *********************************************") -print("Type \"python ~/ryu/ryu/app/openstate/echo_server.py 200\" in h2's xterm") -print("Type \"nc 10.0.0.2 200\" in h1's xterm") -print("Watching the tcpdump results, it is possible to see that forwarding consistency is guaranteed IN EACH DIRECTION.\n" - "In order to test new path selection, close and reopen netcat.") -print("\nTo exit type \"ctrl+D\" or exit") -print("*************************************************************************************") -net = Mininet(topo=mytopo,switch=UserSwitch,controller=RemoteController,cleanup=True,autoSetMacs=True,autoStaticArp=True,listenPort=6634) -net.start() -os.system("xterm -e 'tcpdump -i s2-eth1'&") -os.system("xterm -e 'tcpdump -i s3-eth1'&") -os.system("xterm -e 'tcpdump -i s4-eth1'&") -h1,h2 = net.hosts[0], net.hosts[1] -makeTerm(h1) -makeTerm(h2) -CLI(net) -net.stop() -os.system("sudo mn -c") -os.system("kill -9 $(pidof -x ryu-manager)") diff --git a/ryu/app/openstate/start_many_to_many.py b/ryu/app/openstate/start_many_to_many_BW.py similarity index 93% rename from ryu/app/openstate/start_many_to_many.py rename to ryu/app/openstate/start_many_to_many_BW.py index fe265ecc..2a6f103b 100644 --- a/ryu/app/openstate/start_many_to_many.py +++ b/ryu/app/openstate/start_many_to_many_BW.py @@ -45,7 +45,7 @@ def __init__( self): ######Starting controller -os.system("xterm -e 'ryu-manager ~/ryu/ryu/app/openstate/forwarding_consistency_many_to_many.py'&") +os.system("xterm -e 'ryu-manager ~/ryu_bw/ryu/app/openstate/forwarding_consistency_many_to_many_BW.py'&") @@ -60,7 +60,7 @@ def __init__( self): "In order to test new path selection, close and reopen netcat") print("\nTo exit type \"ctrl+D\" or exit") print("*************************************************************************************") -net = Mininet(topo=mytopo,switch=UserSwitch,controller=RemoteController,cleanup=True,autoSetMacs=True,autoStaticArp=True) +net = Mininet(topo=mytopo,switch=UserSwitch,controller=RemoteController,cleanup=True,autoSetMacs=True,autoStaticArp=True,listenPort=6634) net.start() os.system("xterm -e 'tcpdump -i s4-eth1'&") os.system("xterm -e 'tcpdump -i s4-eth2'&") diff --git a/ryu/app/openstate/test_FFSM.py b/ryu/app/openstate/test_FFSM.py deleted file mode 100644 index bdb1fbe8..00000000 --- a/ryu/app/openstate/test_FFSM.py +++ /dev/null @@ -1,219 +0,0 @@ - -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import logging -import struct - -from ryu.base import app_manager -from ryu.controller import ofp_event -from ryu.controller.handler import MAIN_DISPATCHER, CONFIG_DISPATCHER, HANDSHAKE_DISPATCHER -from ryu.controller.handler import set_ev_cls -from ryu.ofproto import ofproto_v1_3 -from ryu.lib.packet import packet -from ryu.lib.packet import ethernet -from ryu.topology import event -import time - -''' -Applicazione di test che fa uso di Global States (flags), Flow States e Metadata contemporaneamente e dei comandi OFPSC_ADD_FLOW_STATE e OFPSC_DEL_FLOW_STATE - -Ci sono 4 host: -h1 e h2 si pingano sempre -h3 e h4 si pingano per 5 secondi, poi non riescono per altri 5 e infine riescono sempre - -TABLE 0 (stateless) - -ipv4_src=10.0.0.1, in_port=1 ---> SetState(state=0xfffffffa,stage_id=1), SetFlag("1*01********"), WriteMetadata(64954), GotoTable(1) -ipv4_src=10.0.0.2, in_port=2 ---> forward(1) -ipv4_src=10.0.0.3, in_port=3 ---> GotoTable(1) -ipv4_src=10.0.0.4, in_port=4 ---> forward(3) - -TABLE 1 (stateful) Lookup-scope=Update-scope=OXM_OF_IPV4_SRC) - -ipv4_src=10.0.0.1, metadata=64954, flags="1*01********", state=0xfffffffa ---> forward(2) -ipv4_src=10.0.0.3, state=2 ---> forward(4) -''' - -class OSTestFFSM(app_manager.RyuApp): - OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] - - def __init__(self, *args, **kwargs): - super(OSTestFFSM, self).__init__(*args, **kwargs) - - @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) - def switch_features_handler(self, ev): - msg = ev.msg - datapath = msg.datapath - ofproto = datapath.ofproto - - self.send_features_request(datapath) - self.send_table_mod(datapath) - - self.send_key_lookup(datapath) - self.send_key_update(datapath) - - self.add_flow(datapath) - self.add_state_entry(datapath) - time.sleep(5) - self.del_state_entry(datapath) - time.sleep(5) - self.add_state_entry(datapath) - - - def add_flow(self, datapath, table_miss=False): - ofproto = datapath.ofproto - parser = datapath.ofproto_parser - - # ARP packets flooding - match = parser.OFPMatch(eth_type=0x0806) - actions = [ - parser.OFPActionOutput(ofproto.OFPP_FLOOD)] - inst = [parser.OFPInstructionActions( - datapath.ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32760, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - - match = parser.OFPMatch( - ipv4_src="10.0.0.1", in_port=1, eth_type=0x0800) - (flag, flag_mask) = parser.maskedflags("1*01",8) - actions = [parser.OFPActionSetState(state=0xfffffffa,stage_id=1), - parser.OFPActionSetFlag(flag, flag_mask)] - inst = [parser.OFPInstructionActions( - datapath.ofproto.OFPIT_APPLY_ACTIONS, actions), - parser.OFPInstructionGotoTable(1), - parser.OFPInstructionWriteMetadata(64954, 0xffffffffffffffff) - ] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32768, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - match = parser.OFPMatch( - ipv4_src="10.0.0.1", eth_type=0x0800, metadata=64954, state=0xfffffffa, flags=parser.maskedflags("1*01",8)) - actions = [parser.OFPActionOutput(2)] - inst = [parser.OFPInstructionActions( - datapath.ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=1, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32768, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - match = parser.OFPMatch( - ipv4_src="10.0.0.3", in_port=3, eth_type=0x0800) - - inst = [parser.OFPInstructionGotoTable(1)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32768, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - match = parser.OFPMatch( - ipv4_src="10.0.0.4", in_port=4, eth_type=0x0800) - actions = [parser.OFPActionOutput(3)] - inst = [parser.OFPInstructionActions( - datapath.ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32768, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - match = parser.OFPMatch( - ipv4_src="10.0.0.3", eth_type=0x0800, state=2) - actions = [parser.OFPActionOutput(4)] - inst = [parser.OFPInstructionActions( - datapath.ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=1, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32768, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - match = parser.OFPMatch( - ipv4_src="10.0.0.2", in_port=2, eth_type=0x0800) - actions = [parser.OFPActionOutput(1)] - inst = [parser.OFPInstructionActions( - datapath.ofproto.OFPIT_APPLY_ACTIONS, actions)] - mod = parser.OFPFlowMod( - datapath=datapath, cookie=0, cookie_mask=0, table_id=0, - command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, - priority=32768, buffer_id=ofproto.OFP_NO_BUFFER, - out_port=ofproto.OFPP_ANY, - out_group=ofproto.OFPG_ANY, - flags=0, match=match, instructions=inst) - datapath.send_msg(mod) - - - def send_table_mod(self, datapath): - ofp = datapath.ofproto - ofp_parser = datapath.ofproto_parser - req = ofp_parser.OFPTableMod(datapath, 1, ofp.OFPTC_TABLE_STATEFUL) - datapath.send_msg(req) - - def send_features_request(self, datapath): - ofp_parser = datapath.ofproto_parser - req = ofp_parser.OFPFeaturesRequest(datapath) - datapath.send_msg(req) - - def add_state_entry(self, datapath): - ofproto = datapath.ofproto - state = datapath.ofproto_parser.OFPStateEntry( - datapath, ofproto.OFPSC_ADD_FLOW_STATE, 4, 2, [10,0,0,3], - cookie=0, cookie_mask=0, table_id=1) - datapath.send_msg(state) - - def del_state_entry(self, datapath): - ofproto = datapath.ofproto - state = datapath.ofproto_parser.OFPStateEntry( - datapath, ofproto.OFPSC_DEL_FLOW_STATE, 4, 2, [10,0,0,3], - cookie=0, cookie_mask=0, table_id=1) - datapath.send_msg(state) - - def send_key_lookup(self, datapath): - ofp = datapath.ofproto - key_lookup_extractor = datapath.ofproto_parser.OFPKeyExtract( - datapath, ofp.OFPSC_SET_L_EXTRACTOR, 1, [ofp.OXM_OF_IPV4_SRC],table_id=1) - datapath.send_msg(key_lookup_extractor) - - def send_key_update(self, datapath): - ofp = datapath.ofproto - key_update_extractor = datapath.ofproto_parser.OFPKeyExtract( - datapath, ofp.OFPSC_SET_U_EXTRACTOR, 1, [ofp.OXM_OF_IPV4_SRC],table_id=1) - datapath.send_msg(key_update_extractor) diff --git a/ryu/app/openstate/test_port_knocking.sh b/ryu/app/openstate/test_port_knocking.sh deleted file mode 100755 index e7cac5a4..00000000 --- a/ryu/app/openstate/test_port_knocking.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -#Sequence: 5123, 6234, 7345, 8456, 2000 - -# Wrong sequence: -echo -n "*" | nc -q1 -u 10.0.0.2 5123 -echo -n "*" | nc -q1 -u 10.0.0.2 6234 -echo -n "*" | nc -q1 -u 10.0.0.2 73 - -# Correct Sequence -echo -n "*" | nc -q1 -u 10.0.0.2 5123 -echo -n "*" | nc -q1 -u 10.0.0.2 6234 -echo -n "*" | nc -q1 -u 10.0.0.2 7345 -echo -n "*" | nc -q1 -u 10.0.0.2 8456 -nc -u 10.0.0.2 2000 \ No newline at end of file diff --git a/ryu/gui/controller.py b/ryu/gui/controller.py new file mode 100644 index 00000000..56b07512 --- /dev/null +++ b/ryu/gui/controller.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python +# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from argparse import ArgumentParser +import sys +import logging +import inspect +from gevent import pywsgi +from geventwebsocket.handler import WebSocketHandler +from flask import Flask, request, abort +from views.view_base import ViewBase + + +parser = ArgumentParser() +parser.add_argument('--host', dest='host', default='0.0.0.0') +parser.add_argument('--port', dest='port', type=int, default=8000) +args = parser.parse_args() + +app = Flask(__name__.split('.')[0]) +logging.basicConfig(level=logging.DEBUG, + stream=sys.stderr, + format="%(asctime)-15s [%(levelname)-4s] %(message)s") +#handler = logging.FileHandler("/tmp/ryu_gui.log", encoding="utf8") +#app.logger.addHandler(handler) + + +@app.before_request +def before_request_trigger(): + pass + + +@app.after_request +def after_request_trigger(response): + return response + + +@app.route('/') +def index(): + return _view('topology') + + +@app.route('/stats/flow', methods=['POST']) +def flow_mod(): + return _view('flow', request.form.get('host'), request.form.get('port'), + request.form.get('dpid'), request.form.get('flows')) + + +@app.route('/websocket') +def websocket(): + if request.environ.get('wsgi.websocket'): + ws = request.environ['wsgi.websocket'] + return _view('websocket', ws) + abort(404) + + +def _view(view_name, *args, **kwargs): + view_name = 'views.' + view_name + try: + __import__(view_name) + except ImportError: + app.logger.error('ImportError (%s)', view_name) + abort(500) + + mod = sys.modules.get(view_name) + clases = inspect.getmembers(mod, lambda cls: (inspect.isclass(cls) and + issubclass(cls, ViewBase))) + try: + view = clases[0][1](*args, **kwargs) + except IndexError: + app.logger.error('has not View class (%s)', view_name) + abort(500) + app.logger.debug('view loaded. %s', view_name) + return view.run() + + +if __name__ == '__main__': + server = pywsgi.WSGIServer((args.host, args.port), + app, handler_class=WebSocketHandler) + app.logger.info('Running on %s', server.address) + server.serve_forever() diff --git a/ryu/gui/models/__init__.py b/ryu/gui/models/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ryu/gui/models/proxy.py b/ryu/gui/models/proxy.py new file mode 100644 index 00000000..25bb29ef --- /dev/null +++ b/ryu/gui/models/proxy.py @@ -0,0 +1,56 @@ +# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import json +import httplib + +LOG = logging.getLogger('ryu.gui') + +_FLOW_PATH_BASE = '/stats/flow/' + + +def get_flows(address, dpid): + assert type(dpid) == int + + flows = [] + try: + path = '%s%d' % (_FLOW_PATH_BASE, dpid) + flows = json.loads(_do_request(address, path).read())[str(dpid)] + except IOError as e: + LOG.error('REST API(%s) is not available.', address) + raise + except httplib.HTTPException as e: + if e[0].status == httplib.NOT_FOUND: + pass # switch already deleted + else: + LOG.error('REST API(%s, path=%s) request error.', address, path) + raise + return flows + + +def _do_request(address, path): + conn = httplib.HTTPConnection(address) + conn.request('GET', path) + res = conn.getresponse() + if res.status in (httplib.OK, + httplib.CREATED, + httplib.ACCEPTED, + httplib.NO_CONTENT): + return res + + raise httplib.HTTPException( + res, 'code %d reason %s' % (res.status, res.reason), + res.getheaders(), res.read()) diff --git a/ryu/gui/models/topology.py b/ryu/gui/models/topology.py new file mode 100644 index 00000000..d44243ee --- /dev/null +++ b/ryu/gui/models/topology.py @@ -0,0 +1,277 @@ +# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import json +from socket import error as SocketError +from httplib import HTTPException + +import gevent +import gevent.monkey +gevent.monkey.patch_all() + +from ryu.lib.dpid import str_to_dpid +from ryu.lib.port_no import str_to_port_no +from ryu.app.client import TopologyClient + +LOG = logging.getLogger('ryu.gui') + + +class Port(object): + def __init__(self, dpid, port_no, hw_addr, name): + assert type(dpid) == int + assert type(port_no) == int + assert type(hw_addr) == str or type(hw_addr) == unicode + assert type(name) == str or type(name) == unicode + + self.dpid = dpid + self.port_no = port_no + self.hw_addr = hw_addr + self.name = name + + def to_dict(self): + return {'dpid': self.dpid, + 'port_no': self.port_no, + 'hw_addr': self.hw_addr, + 'name': self.name} + + @classmethod + def from_rest_dict(cls, p): + return cls(str_to_dpid(p['dpid']), + str_to_port_no(p['port_no']), + p['hw_addr'], + p['name']) + + def __eq__(self, other): + return self.dpid == other.dpid and self.port_no == other.port_no + + def __ne__(self, other): + return not self.__eq__(other) + + def __hash__(self): + return hash((self.dpid, self.port_no)) + + def __str__(self): + return 'Port' % \ + (self.dpid, self.port_no, self.hw_addr, self.name) + + +class Switch(object): + def __init__(self, dpid, ports): + assert type(dpid) == int + assert type(ports) == list + + self.dpid = dpid + self.ports = ports + + def to_dict(self): + return {'dpid': self.dpid, + 'ports': [port.to_dict() for port in self.ports]} + + def __eq__(self, other): + return self.dpid == other.dpid + + def __ne__(self, other): + return not self.__eq__(other) + + def __hash__(self): + return hash(self.dpid) + + def __str__(self): + return 'Switch' % (self.dpid) + + +class Link(object): + def __init__(self, src, dst): + assert type(src) == Port + assert type(dst) == Port + + self.src = src + self.dst = dst + + def to_dict(self): + return {'src': self.src.to_dict(), + 'dst': self.dst.to_dict()} + + def __eq__(self, other): + return self.src == other.src and self.dst == other.dst + + def __ne__(self, other): + return not self.__eq__(other) + + def __hash__(self): + return hash((self.src, self.dst)) + + def __str__(self): + return 'Link<%s to %s>' % (self.src, self.dst) + + +class Topology(dict): + def __init__(self, switches_json=None, links_json=None): + super(Topology, self).__init__() + + self['switches'] = [] + if switches_json: + for s in json.loads(switches_json): + ports = [] + for p in s['ports']: + ports.append(Port.from_rest_dict(p)) + switch = Switch(str_to_dpid(s['dpid']), ports) + self['switches'].append(switch) + + self['links'] = [] + if links_json: + for l in json.loads(links_json): + link = Link(Port.from_rest_dict(l['src']), + Port.from_rest_dict(l['dst'])) + self['links'].append(link) + + self['ports'] = [] + for switch in self['switches']: + self['ports'].extend(switch.ports) + + def peer(self, port): + for link in self['links']: + if link.src == port: + return link.dst + elif link.dst == port: + return link.src + + return None + + def attached(self, port): + for switch in self['switches']: + if port in switch.port: + return switch + + return None + + def neighbors(self, switch): + ns = [] + for port in switch.port: + ns.append(self.attached(self.peer(port))) + + return ns + + # TopologyDelta = new_Topology - old_Topology + def __sub__(self, old): + assert type(old) == Topology + + added = Topology() + deleted = Topology() + for k in self.iterkeys(): + new_set = set(self[k]) + old_set = set(old[k]) + + added[k] = list(new_set - old_set) + deleted[k] = list(old_set - new_set) + + return TopologyDelta(added, deleted) + + def __str__(self): + return 'Topology' % ( + len(self['switches']), + len(self['ports']), + len(self['links'])) + + +class TopologyDelta(object): + def __init__(self, added, deleted): + self.added = added + self.deleted = deleted + + def __str__(self): + return 'TopologyDelta' % \ + (self.added, self.deleted) + + +class TopologyWatcher(object): + _LOOP_WAIT = 3 + _REST_RETRY_WAIT = 10 + + def __init__(self, update_handler=None, rest_error_handler=None): + self.update_handler = update_handler + self.rest_error_handler = rest_error_handler + self.address = None + self.tc = None + + self.threads = [] + self.topo = Topology() + self.prev_switches_json = '' + self.prev_links_json = '' + + def start(self, address): + LOG.debug('TopologyWatcher: start') + self.address = address + self.tc = TopologyClient(address) + self.is_active = True + self.threads.append(gevent.spawn(self._polling_loop)) + + def stop(self): + LOG.debug('TopologyWatcher: stop') + self.is_active = False + + def _polling_loop(self): + LOG.debug('TopologyWatcher: Enter polling loop') + while self.is_active: + try: + switches_json = self.tc.list_switches().read() + links_json = self.tc.list_links().read() + except (SocketError, HTTPException) as e: + LOG.debug('TopologyWatcher: REST API(%s) is not avaliable.' % + self.address) + LOG.debug(' wait %d secs...' % + self._REST_RETRY_WAIT) + self._call_rest_error_handler(e) + gevent.sleep(self._REST_RETRY_WAIT) + continue + + if self._is_updated(switches_json, links_json): + LOG.debug('TopologyWatcher: topology updated') + new_topo = Topology(switches_json, links_json) + delta = new_topo - self.topo + self.topo = new_topo + + self._call_update_handler(delta) + + gevent.sleep(self._LOOP_WAIT) + + def _is_updated(self, switches_json, links_json): + updated = ( + self.prev_switches_json != switches_json or + self.prev_links_json != links_json) + + self.prev_switches_json = switches_json + self.prev_links_json = links_json + + return updated + + def _call_rest_error_handler(self, e): + if self.rest_error_handler: + self.rest_error_handler(self.address, e) + + def _call_update_handler(self, delta): + if self.update_handler: + self.update_handler(self.address, delta) + + +def handler(address, delta): + print delta + +if __name__ == '__main__': + logging.basicConfig(level=logging.DEBUG) + watcher = TopologyWatcher(handler) + watcher.start('127.0.0.1:8080') + gevent.joinall(watcher.threads) diff --git a/ryu/gui/static/css/ryu.css b/ryu/gui/static/css/ryu.css new file mode 100644 index 00000000..209f64ee --- /dev/null +++ b/ryu/gui/static/css/ryu.css @@ -0,0 +1,367 @@ +/** + * Copyright (C) 2013 Nippon Telegraph and Telephone Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **/ + +* { + color: #808080; + font-size: 12px; +} +html { + width: 100%; + height: 100%; +} +body { + margin: 0; + padding: 0; + width: 100%; + height: 100%; + text-align: center; + font-family: Arial, Helvetica,"メイリオ",Meiryo,"ヒラギノ角ゴ Pro W3","Hiragino Kaku Gothic Pro","HiraKakuPro-W3",">MS Pゴシック","MS PGothic",sans-serif; +} +body a { + text-decoration: none; +} + + +/** + * header footer + **/ +#header { + width: 100%; + text-align: left; + font-size: 20px; + margin: 0px; + padding: 0px; + border-bottom: solid 2px #808080; +} +#header #ryu-logo { + width: 100px; + height: 50px; +} +#header #page-title { + font-weight: bold; + margin-left: 10px; + vertical-align: 15px; + font-size: 20px; +} +#footer { + position: fixed; + right: 10px; + bottom: 5px; + color: #808080; + font-size: 12px; + z-index: 100px; +} + + +/** + * main + **/ +#main { +} + +/** + * input form + */ +#jquery-ui-dialog-table { +} +#jquery-ui-dialog-table th , #jquery-ui-dialog-table td { + border: 1px solid gray; +} +#input-err-msg { + color: red; + display: none; + -webkit-border-radius: 5px; +} + + +/** + * contents + **/ +.content { + position: absolute; + border: solid 1px #808080; + color: #808080; + padding-bottom: 1.2em; + -webkit-border-radius: 5px; +} +.content .content-title { + height: 15px; +/* font-size: 1.1em; */ + margin: 0px; + padding: auto; + padding-left: 1.1em; + -webkit-border-radius: 5px; + background: #0070c0 url(../img/ui-bg_org_0070c0.png) 50% 50% repeat-x; + letter-spacing: 0.15em; + cursor: move; + color: #FFF; +} +.content .content-title .content-title-text { + color: #FFF; + float: left; +} +.content .content-title .content-title-close { + float: right; + width: 20px; + cursor: pointer; +} +.content .content-body{ + position: relative; + overflow: hidden; + width: 100%; + height: 95%; + white-space: nowrap; + -webkit-border-radius: 5px; +} +.content .content-end { + height: 0px; + display: none; +} +.content .watching { + margin-left: 5px; + color: #FFF; +} + + +/** + * list table + **/ +.content-body table { + margin-top: 2px; + padding-right: 5px; + width: 100%; + border-collapse:separate; + border-spacing:1px; +} +.content-body table th { + border:0.25px solid #FFFFFF; + background-color: #483D8B; + color: #FFF; + text-align: center; + font-weight: bold; + letter-spacing: 0.15em; +} +.content-body table td { + border:0.25px solid #FFFFFF; + background-color: #EEEEEE; + white-space: nowrap; +} + + +/** + * Menu + **/ +#menu { + top: 60px; + left: 5px; + width: 150px; + height: 100px; + text-align: left; + background-color: #EEE; + z-index: 100; +} +#menu .content-body { + background-color: #EEE; +} +#menu .menu-item { + text-align: left; + padding-left: 5%; /** width + padding-left = #demomenu.width **/ + padding-top: 1px; + padding-bottom: 1px; + margin-top: 1px; + margin-bottom: 1px; + font-weight: bold; + color: #0070c0; + background-color: #EEE; + -webkit-border-radius: 5px; +} + + +/** + * link-list + **/ +#link-list { + top: 250px; + left: 5px; + width: 200px; + height: 100px; + text-align: left; +} +#link-list td { + text-align: center; +} +#link-list .peer-port-name { +} +#link-list .peer-switch-name { + padding-left: 5px; +} + + +/** + * flow-list + */ +#flow-list { + top: 540px; + left: 5px; + width: 825px; + height: 100px; + text-align: left; +} +#flow-list td { + padding-left: 5px; + line-height: 1.4; +/* white-space: normal; */ +/* letter-spacing: 0.15em; */ +} +#flow-list .flow-item-line{ + width: 100%; + border-bottom: dashed 1px #808080; +} +#flow-list .flow-item-line .flow-item-title{ +/** + float: left; + width: 5em; +**/ + color: #000; + font-weight: bold; + padding-left: 0.5em; +} +#flow-list .flow-item-line .flow-item-value{ +/** + float: right; + width: auto; +**/ + padding-left: 0.5em; +} +/** + * Topology + **/ +#topology { + top: 60px; + left: 230px; + width: 800px; + height: 600px; +} +#topology .rest-status { + position: relative; + top: 0; + left: 0; + text-align: left; + font-weight:bold; + padding-left: 5px; +} +#topology .content-body{ + height: 100%; +} +#topology .rest-status .rest-url{ + margin-left: 10px; + font-size: 12px; + color: #808080; + letter-spacing: 0.15em; + font-weight: normal; +} +#topology .switch { + z-index: 1; + border: 0px solid #FFF; + +} +#topology .switch img { + z-index: 2; +} +#topology .switch-label { + position: relative; +/* font-weight:bold; */ +/* background-color: red; */ +/* color: white; */ + text-align: center; + border: 0px solid #FFF; +/* height: 15px; */ + z-index: 3; +} +#topology .port-no { + -webkit-border-radius: 10px; + text-align: center; + font-weight:bold; + background-color: #E6FFE9; + border: 1px solid #808080; + width: 14px; + height: 14px; + z-index: 4; + margin: auto auto; +} + +/** + * scrollbar + **/ +.ps-container .ps-scrollbar-x { + position: absolute; /* please don't change 'position' */ + bottom: 3px; /* there must be 'bottom' for ps-scrollbar-x */ + height: 8px; + background-color: #aaa; + border-radius: 4px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + opacity: 0; + filter: alpha(opacity = 0); + -webkit-transition: opacity.2s linear; + -moz-transition: opacity .2s linear; + transition: opacity .2s linear; +} +.ps-container:hover .ps-scrollbar-x { + opacity: 0.6; + filter: alpha(opacity = 60); +} +.ps-container .ps-scrollbar-x:hover { + opacity: 0.9; + filter: alpha(opacity = 90); +/* cursor:default; */ + cursor: pointer; +} +.ps-container .ps-scrollbar-x.in-scrolling { + opacity: 0.9; + filter: alpha(opacity = 90); +} + +.ps-container .ps-scrollbar-y { + position: absolute; /* please don't change 'position' */ + right: 3px; /* there must be 'right' for ps-scrollbar-y */ + width: 8px; + background-color: #aaa; + border-radius: 4px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + opacity: 0; + filter: alpha(opacity = 0); + -webkit-transition: opacity.2s linear; + -moz-transition: opacity .2s linear; + transition: opacity .2s linear; +} +.ps-container:hover .ps-scrollbar-y { + opacity: 0.6; + filter: alpha(opacity = 60); +} +.ps-container .ps-scrollbar-y:hover { + opacity: 0.9; + filter: alpha(opacity = 90); +/* cursor: default; */ + cursor: pointer; +} +.ps-container .ps-scrollbar-y.in-scrolling { + opacity: 0.9; + filter: alpha(opacity = 90); +} diff --git a/ryu/gui/static/img/ryu_logo.gif b/ryu/gui/static/img/ryu_logo.gif new file mode 100644 index 00000000..8bc7cc35 Binary files /dev/null and b/ryu/gui/static/img/ryu_logo.gif differ diff --git a/ryu/gui/static/img/switch.png b/ryu/gui/static/img/switch.png new file mode 100644 index 00000000..38868d43 Binary files /dev/null and b/ryu/gui/static/img/switch.png differ diff --git a/ryu/gui/static/img/ui-bg_org_0070c0.png b/ryu/gui/static/img/ui-bg_org_0070c0.png new file mode 100644 index 00000000..4f584f38 Binary files /dev/null and b/ryu/gui/static/img/ui-bg_org_0070c0.png differ diff --git a/ryu/gui/static/img/wireshark-logo.png b/ryu/gui/static/img/wireshark-logo.png new file mode 100644 index 00000000..2e2458fc Binary files /dev/null and b/ryu/gui/static/img/wireshark-logo.png differ diff --git a/ryu/gui/static/js/contrib/jquery.mousewheel.js b/ryu/gui/static/js/contrib/jquery.mousewheel.js new file mode 100644 index 00000000..38b60951 --- /dev/null +++ b/ryu/gui/static/js/contrib/jquery.mousewheel.js @@ -0,0 +1,84 @@ +/*! Copyright (c) 2011 Brandon Aaron (http://brandonaaron.net) + * Licensed under the MIT License (LICENSE.txt). + * + * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers. + * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix. + * Thanks to: Seamus Leahy for adding deltaX and deltaY + * + * Version: 3.0.6 + * + * Requires: 1.2.2+ + */ + +(function($) { + +var types = ['DOMMouseScroll', 'mousewheel']; + +if ($.event.fixHooks) { + for ( var i=types.length; i; ) { + $.event.fixHooks[ types[--i] ] = $.event.mouseHooks; + } +} + +$.event.special.mousewheel = { + setup: function() { + if ( this.addEventListener ) { + for ( var i=types.length; i; ) { + this.addEventListener( types[--i], handler, false ); + } + } else { + this.onmousewheel = handler; + } + }, + + teardown: function() { + if ( this.removeEventListener ) { + for ( var i=types.length; i; ) { + this.removeEventListener( types[--i], handler, false ); + } + } else { + this.onmousewheel = null; + } + } +}; + +$.fn.extend({ + mousewheel: function(fn) { + return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel"); + }, + + unmousewheel: function(fn) { + return this.unbind("mousewheel", fn); + } +}); + + +function handler(event) { + var orgEvent = event || window.event, args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true, deltaX = 0, deltaY = 0; + event = $.event.fix(orgEvent); + event.type = "mousewheel"; + + // Old school scrollwheel delta + if ( orgEvent.wheelDelta ) { delta = orgEvent.wheelDelta/120; } + if ( orgEvent.detail ) { delta = -orgEvent.detail/3; } + + // New school multidimensional scroll (touchpads) deltas + deltaY = delta; + + // Gecko + if ( orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) { + deltaY = 0; + deltaX = -1*delta; + } + + // Webkit + if ( orgEvent.wheelDeltaY !== undefined ) { deltaY = orgEvent.wheelDeltaY/120; } + if ( orgEvent.wheelDeltaX !== undefined ) { deltaX = -1*orgEvent.wheelDeltaX/120; } + + // Add event and delta to the front of the arguments + args.unshift(event, delta, deltaX, deltaY); + + return ($.event.dispatch || $.event.handle).apply(this, args); +} + +})(jQuery); diff --git a/ryu/gui/static/js/contrib/perfect-scrollbar.js b/ryu/gui/static/js/contrib/perfect-scrollbar.js new file mode 100644 index 00000000..be056ddf --- /dev/null +++ b/ryu/gui/static/js/contrib/perfect-scrollbar.js @@ -0,0 +1,313 @@ +/* Copyright (c) 2012 HyeonJe Jun (http://github.com/noraesae) + * Licensed under the MIT License + */ +((function($) { + + // The default settings for the plugin + var defaultSettings = { + wheelSpeed: 10, + wheelPropagation: false + }; + + $.fn.perfectScrollbar = function(suppliedSettings, option) { + + // Use the default settings + var settings = $.extend( true, {}, defaultSettings ); + if (typeof suppliedSettings === "object") { + // But over-ride any supplied + $.extend( true, settings, suppliedSettings ); + } else { + // If no settings were supplied, then the first param must be the option + option = suppliedSettings; + } + + if(option === 'update') { + if($(this).data('perfect_scrollbar_update')) { + $(this).data('perfect_scrollbar_update')(); + } + return $(this); + } + else if(option === 'destroy') { + if($(this).data('perfect_scrollbar_destroy')) { + $(this).data('perfect_scrollbar_destroy')(); + } + return $(this); + } + + if($(this).data('perfect_scrollbar')) { + // if there's already perfect_scrollbar + return $(this).data('perfect_scrollbar'); + } + + var $this = $(this).addClass('ps-container'), + $content = $(this).children(), + $scrollbar_x = $("
").appendTo($this), + $scrollbar_y = $("
").appendTo($this), + container_width, + container_height, + content_width, + content_height, + scrollbar_x_width, + scrollbar_x_left, + scrollbar_x_bottom = parseInt($scrollbar_x.css('bottom'), 10), + scrollbar_y_height, + scrollbar_y_top, + scrollbar_y_right = parseInt($scrollbar_y.css('right'), 10); + + var updateContentScrollTop = function() { + var scroll_top = parseInt(scrollbar_y_top * content_height / container_height, 10); + $this.scrollTop(scroll_top); + $scrollbar_x.css({bottom: scrollbar_x_bottom - scroll_top}); + }; + + var updateContentScrollLeft = function() { + var scroll_left = parseInt(scrollbar_x_left * content_width / container_width, 10); + $this.scrollLeft(scroll_left); + $scrollbar_y.css({right: scrollbar_y_right - scroll_left}); + }; + + var updateBarSizeAndPosition = function() { + container_width = $this.width(); + container_height = $this.height(); + content_width = $content.outerWidth(false); + content_height = $content.outerHeight(false); + if(container_width < content_width) { + scrollbar_x_width = parseInt(container_width * container_width / content_width, 10); + scrollbar_x_left = parseInt($this.scrollLeft() * container_width / content_width, 10); + } + else { + scrollbar_x_width = 0; + scrollbar_x_left = 0; + $this.scrollLeft(0); + } + if(container_height < content_height) { + scrollbar_y_height = parseInt(container_height * container_height / content_height, 10); + scrollbar_y_top = parseInt($this.scrollTop() * container_height / content_height, 10); + } + else { + scrollbar_y_height = 0; + scrollbar_y_left = 0; + $this.scrollTop(0); + } + + $scrollbar_x.css({left: scrollbar_x_left + $this.scrollLeft(), bottom: scrollbar_x_bottom - $this.scrollTop(), width: scrollbar_x_width}); + $scrollbar_y.css({top: scrollbar_y_top + $this.scrollTop(), right: scrollbar_y_right - $this.scrollLeft(), height: scrollbar_y_height}); + }; + + var moveBarX = function(current_left, delta_x) { + var new_left = current_left + delta_x, + max_left = container_width - scrollbar_x_width; + + if(new_left < 0) { + scrollbar_x_left = 0; + } + else if(new_left > max_left) { + scrollbar_x_left = max_left; + } + else { + scrollbar_x_left = new_left; + } + $scrollbar_x.css({left: scrollbar_x_left + $this.scrollLeft()}); + }; + + var moveBarY = function(current_top, delta_y) { + var new_top = current_top + delta_y, + max_top = container_height - scrollbar_y_height; + + if(new_top < 0) { + scrollbar_y_top = 0; + } + else if(new_top > max_top) { + scrollbar_y_top = max_top; + } + else { + scrollbar_y_top = new_top; + } + $scrollbar_y.css({top: scrollbar_y_top + $this.scrollTop()}); + }; + + var bindMouseScrollXHandler = function() { + var current_left, + current_page_x; + + $scrollbar_x.bind('mousedown.perfect-scroll', function(e) { + current_page_x = e.pageX; + current_left = $scrollbar_x.position().left; + $scrollbar_x.addClass('in-scrolling'); + e.stopPropagation(); + e.preventDefault(); + }); + + $(document).bind('mousemove.perfect-scroll', function(e) { + if($scrollbar_x.hasClass('in-scrolling')) { + moveBarX(current_left, e.pageX - current_page_x); + updateContentScrollLeft(); + e.stopPropagation(); + e.preventDefault(); + } + }); + + $(document).bind('mouseup.perfect-scroll', function(e) { + if($scrollbar_x.hasClass('in-scrolling')) { + $scrollbar_x.removeClass('in-scrolling'); + } + }); + }; + + var bindMouseScrollYHandler = function() { + var current_top, + current_page_y; + + $scrollbar_y.bind('mousedown.perfect-scroll', function(e) { + current_page_y = e.pageY; + current_top = $scrollbar_y.position().top; + $scrollbar_y.addClass('in-scrolling'); + e.stopPropagation(); + e.preventDefault(); + }); + + $(document).bind('mousemove.perfect-scroll', function(e) { + if($scrollbar_y.hasClass('in-scrolling')) { + moveBarY(current_top, e.pageY - current_page_y); + updateContentScrollTop(); + e.stopPropagation(); + e.preventDefault(); + } + }); + + $(document).bind('mouseup.perfect-scroll', function(e) { + if($scrollbar_y.hasClass('in-scrolling')) { + $scrollbar_y.removeClass('in-scrolling'); + } + }); + }; + + // bind handlers + var bindMouseWheelHandler = function() { + var shouldPreventDefault = function(deltaX, deltaY) { + var scrollTop = $this.scrollTop(); + if(scrollTop === 0 && deltaY > 0 && deltaX === 0) { + return !settings.wheelPropagation; + } + else if(scrollTop >= content_height - container_height && deltaY < 0 && deltaX === 0) { + return !settings.wheelPropagation; + } + + var scrollLeft = $this.scrollLeft(); + if(scrollLeft === 0 && deltaX < 0 && deltaY === 0) { + return !settings.wheelPropagation; + } + else if(scrollLeft >= content_width - container_width && deltaX > 0 && deltaY === 0) { + return !settings.wheelPropagation; + } + return true; + }; + + $this.mousewheel(function(e, delta, deltaX, deltaY) { + $this.scrollTop($this.scrollTop() - (deltaY * settings.wheelSpeed)); + $this.scrollLeft($this.scrollLeft() + (deltaX * settings.wheelSpeed)); + + // update bar position + updateBarSizeAndPosition(); + + if(shouldPreventDefault(deltaX, deltaY)) { + e.preventDefault(); + } + }); + }; + + // bind mobile touch handler + var bindMobileTouchHandler = function() { + var applyTouchMove = function(difference_x, difference_y) { + $this.scrollTop($this.scrollTop() - difference_y); + $this.scrollLeft($this.scrollLeft() - difference_x); + + // update bar position + updateBarSizeAndPosition(); + }; + + var start_coords = {}, + start_time = 0, + speed = {}, + breaking_process = null; + + $this.bind("touchstart.perfect-scroll", function(e) { + var touch = e.originalEvent.targetTouches[0]; + + start_coords.pageX = touch.pageX; + start_coords.pageY = touch.pageY; + + start_time = (new Date()).getTime(); + + if (breaking_process !== null) { + clearInterval(breaking_process); + } + }); + $this.bind("touchmove.perfect-scroll", function(e) { + var touch = e.originalEvent.targetTouches[0]; + + var current_coords = {}; + current_coords.pageX = touch.pageX; + current_coords.pageY = touch.pageY; + + var difference_x = current_coords.pageX - start_coords.pageX, + difference_y = current_coords.pageY - start_coords.pageY; + + applyTouchMove(difference_x, difference_y); + start_coords = current_coords; + + var current_time = (new Date()).getTime(); + speed.x = difference_x / (current_time - start_time); + speed.y = difference_y / (current_time - start_time); + start_time = current_time; + + e.preventDefault(); + }); + $this.bind("touchend.perfect-scroll", function(e) { + breaking_process = setInterval(function() { + if(Math.abs(speed.x) < 0.01 && Math.abs(speed.y) < 0.01) { + clearInterval(breaking_process); + return; + } + + applyTouchMove(speed.x * 30, speed.y * 30); + + speed.x *= 0.8; + speed.y *= 0.8; + }, 10); + }); + }; + + var destroy = function() { + $scrollbar_x.remove(); + $scrollbar_y.remove(); + $this.unbind('mousewheel'); + $this.unbind('touchstart.perfect-scroll'); + $this.unbind('touchmove.perfect-scroll'); + $this.unbind('touchend.perfect-scroll'); + $(window).unbind('mousemove.perfect-scroll'); + $(window).unbind('mouseup.perfect-scroll'); + $this.data('perfect_scrollbar', null); + $this.data('perfect_scrollbar_update', null); + $this.data('perfect_scrollbar_destroy', null); + }; + + var isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent); + + var initialize = function() { + updateBarSizeAndPosition(); + bindMouseScrollXHandler(); + bindMouseScrollYHandler(); + if(isMobile) bindMobileTouchHandler(); + if($this.mousewheel) bindMouseWheelHandler(); + $this.data('perfect_scrollbar', $this); + $this.data('perfect_scrollbar_update', updateBarSizeAndPosition); + $this.data('perfect_scrollbar_destroy', destroy); + }; + + // initialize + initialize(); + + return $this; + }; +})(jQuery)); diff --git a/ryu/gui/static/js/ryu.js b/ryu/gui/static/js/ryu.js new file mode 100644 index 00000000..4cb53278 --- /dev/null +++ b/ryu/gui/static/js/ryu.js @@ -0,0 +1,819 @@ +/** + * Copyright (C) 2013 Nippon Telegraph and Telephone Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + **/ +var conf = { + URL_GET_FLOWS: 'stats/flow', + LABEL_FONT_SIZE: 10, + EVENT_LOOP_INTERVAL: 10, + REPLACE_FLOW_INTERVAL: 5000, + CONNECTION_REPAINT_INTERVAL: 500, + IMG_SW: {"x": 50, "y": 30, "img": "static/img/switch.png"}, + DEFAULT_REST_PORT: '8080', + ID_PRE_SW: 'node-switch-', + ID_PRE_LINK_LIST: 'link-list-item-', + ID_PRE_FLOW_LIST: 'flow-list-item-' +}; + + +var _EVENTS = []; // [fnc, arg] + + +var _DATA = { + timer: {}, // ids of setTimeout() and setInterval() + watching: null, // dpid of select switch + input: {}, + switches: {} // topology data + // switches[].dpid + // .ports[].dpid + // .port_no + // .name + // .peer.dpid + // .port_no + // .name +}; + + + +/////////////////////////////////// +// topo +/////////////////////////////////// +var topo = { + init: function(){ + utils.restDisconnected(); + utils.event_loop(); + + // scrollbar update + setInterval(function(){ + $("#link-list-body").perfectScrollbar('update'); + $("#flow-list-body").perfectScrollbar('update'); + }, 100); + + // connections repaint + setInterval(function(){ + jsPlumb.repaint($("div .switch")) + }, conf.CONNECTION_REPAINT_INTERVAL); + + // open dialog + topo.setInput({'port': conf.DEFAULT_REST_PORT}); + $('#jquery-ui-dialog').dialog('open'); + }, + + registerHandler: function(){ + $('#jquery-ui-dialog').dialog({ + autoOpen: false, + width: 450, + show: 'explode', + hide: 'explode', + modal: true, + buttons: { + 'Launch': function() { + topo.restConnect(); + $(this).dialog('close'); + }, + 'cancel': function(){ + $(this).dialog('close'); + }, + }, + open: function(){ + topo.openInputForm(); + }, + }); + + // Contents draggable + $('#menu').draggable({ handle: '#menu, .content-title' }); + $('#link-list').draggable({ handle: '#link-list, .content-title' }); + $('#flow-list').draggable({ handle: '#flow-list, .content-title' }); + $('#topology').draggable({ handle: '#topology, .content-title' }); + + // Contents resize + $("#menu").resizable( { autoHide : true } ); + $("#topology").resizable( { autoHide : true } ); + $("#flow-list").resizable( { autoHide : true } ); + $("#link-list").resizable( { autoHide : true } ); + + // Contents scrollbar + $("#link-list-body").perfectScrollbar(); + $("#flow-list-body").perfectScrollbar(); + + // Contents active + $(".content").click(function(){topo.contentActive(this.id)}); + + // Contents close + $(".content-title-close").click(function(){ + return topo.contentClose($(this).closest("div .content").attr("id")) + }); + $(".content-title-close").hover(function(){topo.closeMouseOver(this)}, function(){topo.closeMouseOut(this)}); + + // Menu mouseouver/mouseout + $('#menu a div').hover(function(){ topo.menuMouseOver(this); }, function(){ topo.menuMouseOut(this); }); + + // Menu action + $('#jquery-ui-dialog-opener').click(function(){$('#jquery-ui-dialog').dialog('open');}); + $("#menu-flow-entries").click(function(){topo.contentActive('flow-list');}); + $("#menu-link-status").click(function(){topo.contentActive('link-list');}); + $("#menu-redesign").click(function(){topo.redesignTopology();}); + }, + + setInput: function(input) { + if (typeof input.host !== "undefined") _DATA.input.host = input.host; + if (typeof input.port !== "undefined") _DATA.input.port = input.port; + if (typeof input.err !== "undefined") _DATA.input.err = input.err; + }, + + openInputForm: function() { + if (_DATA.input.host) $('#jquery-ui-dialog-form-host').val(_DATA.input.host); + if (_DATA.input.port) $('#jquery-ui-dialog-form-port').val(_DATA.input.port); + if (_DATA.input.err) { + $("#input-err-msg").text(_DATA.input.err).css('display', 'block'); + } else { + $("#input-err-msg").css('display', 'none'); + } + }, + + restConnect: function() { + var input = {}; + input.host = $('#jquery-ui-dialog-form-host').val(); + input.port = conf.DEFAULT_REST_PORT; + if ($('#jquery-ui-dialog-form-port').val()) input.port = $('#jquery-ui-dialog-form-port').val(); + + // not changed + if (_DATA.input.host == input.host + && _DATA.input.port == input.port + && !_DATA.timer.restStatus) return; + + input.err = ''; + topo.setInput(input); + _EVENTS = []; + utils.restDisconnected(); + + // topology cleanup + utils.topologyCleanup(); + websocket.sendRestUpdate(input.host, input.port); + }, + + menuMouseOver: function(el) { + el.style.backgroundColor = "#0070c0"; + el.style.color = "#FFF"; + }, + + menuMouseOut: function(el) { + el.style.backgroundColor = "#EEE"; + el.style.color = "#0070c0"; + }, + + closeMouseOver: function(id) { + }, + + closeMouseOut: function(id) { + }, + + contentClose: function(id) { + $("#" + id).hide(); + return false; + }, + + contentActive: function(id) { + if (id == "menu") return; + var contents = $("#main").find(".content"); + for (var i=0; i < contents.length; i++) { + var content = contents[i]; + if (content.id != "menu") { + if (content.id == id) { + content.style.display = 'block'; + content.style.zIndex = $("#main").children(".content").length * 10; + } else if (content.style.zIndex > 10) { + content.style.zIndex = content.style.zIndex - 1 * 10; + } + } + } + }, + + watchingSwitch: function(dpid) { + if (typeof dpid === "undefined") dpid = ""; + else if (! dpid in _DATA.switches) return; + else if (dpid == _DATA.watching) return; + + $("#topology div").find(".switch").css("border", "0px solid #FFF"); + $("#" + utils._switchId(dpid)).css("border", "3px solid red"); + _DATA.watching = dpid; + utils.refreshLinkList(); + utils.clearFlowList(); + + if (dpid) { + // loop for flowList update + if (_DATA.timer.replaceFlowList) clearInterval(_DATA.timer.replaceFlowList) + var intervalfnc = function() { + if (_DATA.watching == dpid) { + rest.getFlows(_DATA.input.host, _DATA.input.port, _DATA.watching, function(data) { + if (data.host != _DATA.input.host || data.port != _DATA.input.port) return; + utils.replaceFlowList(data.dpid, data.flows); + }, function(data){utils.replaceFlowList(false)}); + } else { + clearInterval(_DATA.timer.replaceFlowList); + } + }; + intervalfnc(); + _DATA.timer.replaceFlowList = setInterval(intervalfnc, conf.REPLACE_FLOW_INTERVAL); + } +// websocket.sendWatchingSwitch(dpid); + }, + + redesignTopology: function(){ + var base = {x: $("#topology").width() / 2, + y: $("#topology").height() / 2} + var radii = {x: $("#topology").width() / 4, + y: $("#topology").height() / 4} + + var max_w,max_h; + max_w=$("#topology").width(); + max_h=$("#topology").height() + + for (var i in _DATA.switches) { + var sw = _DATA.switches[i]; + var p = {}; + p['x'] = _DATA.switches[sw.dpid].pos['x']; + p['y'] = _DATA.switches[sw.dpid].pos['y']; + if (p['x']>max_w) max_w=p['x']+60; + if (p['y']>max_h) max_h=p['y']+10; + } + $("#topology").width(max_w); + $("#topology").height(max_h); + + var cnt = 0; + var len = 0; + for (var i in _DATA.switches) len ++; + + for (var i in _DATA.switches) { + var sw = _DATA.switches[i]; + var position = utils._calTh(cnt, len, base, radii); + var p = {}; + p['x'] = _DATA.switches[sw.dpid].pos['x']; + p['y'] = $("#topology").height() -_DATA.switches[sw.dpid].pos['y']; + if (p['x']==-1 && _DATA.switches[sw.dpid].pos['y']==-1){ + utils.addSwitch(sw, position) + } else{ + utils.addSwitch(sw, p) + } + cnt ++; + } + }, + + emphasisList: function(conn) { + // TODO: + return; + } +}; + + +/////////////////////////////////// +// utils +/////////////////////////////////// +var utils = { + topologyCleanup: function() { + topo.watchingSwitch(); + jsPlumb.reset(); + $("#topology .switch").remove(); + _DATA.switches = {}; + }, + + _switchId: function(dpid) { + return conf.ID_PRE_SW + dpid; + }, + + _linkListId: function(dpid, port_no) { + return conf.ID_PRE_LINK_LIST + dpid + '-' + port_no; + }, + + _flowListId: function(dpid, no) { + return conf.ID_PRE_FLOW_LIST + dpid + '-' + no; + }, + + _connectionUUID: function(dpid, port_no) { + return utils._switchId(dpid) + '-' + port_no; + }, + + ///// + // Event + ///// + event_loop: function() { + if (_EVENTS.length) { + var ev = _EVENTS.shift(); + if (ev.length == 1) ev[0]() + else ev[0](ev[1]); + } + setTimeout(utils.event_loop, conf.EVENT_LOOP_INTERVAL); + }, + + registerEvent: function(func, arg){ + if (typeof arg === "undefined") _EVENTS.push([func]) + else _EVENTS.push([func, arg]) + }, + + ///// + // Rest + ///// + restDisconnected: function(host, port) { + $("#topology").find(".rest-status").css('color', 'red').text('Disconnected'); + if (typeof host !== "undefined" && typeof port !== "undefined") { + var rest = '(' + host + ':' + port + ')'; + $("#topology").find(".rest-status").append(rest); + } + if (_DATA.timer.restStatus) return; + _DATA.timer.restStatus = setInterval(function(){ + $("#topology").find(".rest-status").fadeTo(1000, 0.25).fadeTo(1000, 1) + }, 1500) + }, + + restConnected: function(host, port) { + if (_DATA.timer.restStatus) { + clearInterval(_DATA.timer.restStatus); + _DATA.timer.restStatus = null; + $("#topology").find(".rest-status").css('color', '#808080').text('Connected'); + var rest = '(' + host + ':' + port + ')'; + $("#topology").find(".rest-status").append(rest); + } + }, + + ///// + // Node + ///// + _addNode: function(id, position, img, className) { + var topo_div = $("#topology .content-body")[0]; + var node_div = document.createElement('div'); + var node_img = document.createElement('img'); + node_div.appendChild(node_img); + topo_div.appendChild(node_div); + + node_div.style.position = 'absolute'; + node_div.id = id; + if (typeof className !== 'undefined') node_div.className = className; + node_div.style.width = img.x; + node_div.style.height = img.y; + node_div.style.left = position.x; + node_div.style.top = position.y; + + node_img.id = id + "-img"; + node_img.src = img.img; + node_img.style.width = img.x; + node_img.style.height = img.y; + + // jsPlumb drag + jsPlumb.draggable(node_div, {"containment": "parent"}); + }, + + _moveNode: function(id, position) { + // move position + $("#" + id).animate({left: position.x, top: position.y}, 100, 'swing'); + }, + + _delNode: function(id) { + var points = jsPlumb.getEndpoints(id); + for (var i in points) { + jsPlumb.deleteEndpoint(points[i]); + } + $("#" + id).remove(); + }, + + _calTh: function(no, len, base, radii) { + var th = 3.14159; + var p = {}; + p['x'] = base.x + radii.x * Math.sin(th * 2 * (len - no) / len); + p['y'] = base.y + radii.y * Math.cos(th * 2 * (len - no) / len); + return p + }, + + ///// + // Node (switch) + ///// + addSwitch: function(sw, position) { + var id = utils._switchId(sw.dpid); + if (document.getElementById(id)) { + utils._moveNode(id, position); + return + } + + var img = conf.IMG_SW; + utils._addNode(id, position, img, 'switch'); + var node_div = document.getElementById(id) + node_div.setAttribute("onClick","topo.watchingSwitch('" + sw.dpid + "')"); + +// var labelStr = 'dpid:' + ("0000000000000000" + sw.dpid.toString(16)).slice(-16); + var labelStr = 'dpid: ' + sw.dpid.toString(); + $(node_div).find("img").attr('title', labelStr); + var fontSize = conf.LABEL_FONT_SIZE; + var label_div = document.createElement('div'); + label_div.className = "switch-label"; + label_div.id = id + "-label"; + label_div.style.width = labelStr.length * fontSize; +// label_div.style.marginTop = 0 - (img.y + fontSize) / 2; + label_div.style.marginLeft = (img.x - labelStr.length * fontSize) / 2; + var label_text = document.createTextNode(labelStr); + label_div.appendChild(label_text); + node_div.appendChild(label_div); + }, + + delSwitch: function(dpid) { + utils._delNode(utils._switchId(dpid)); + }, + + ///// + // List + ///// + _repainteRows: function(list_table_id) { + var rows = $("#main").find(".content"); + for (var i=0; i < $("#" + list_table_id).find(".content-table-item").length; i++) { + var tr = $("#" + list_table_id).find(".content-table-item")[i]; + if (i % 2) { + $(tr).find("td").css('background-color', '#D6D6D6'); + $(tr).find("td").css('color', '#535353'); + } else { + $(tr).find("td").css('background-color', '#EEEEEE'); + $(tr).find("td").css('color', '#808080'); + } + } + }, + + ///// + // List (links) + ///// + appendLinkList: function(link){ + var list_table = document.getElementById('link-list-table'); + var tr = list_table.insertRow(-1); + tr.className = 'content-table-item'; + tr.id = utils._linkListId(link.dpid, link.port_no); + + // port-no + var no_td = tr.insertCell(-1); + no_td.className = 'port-no'; + no_td.innerHTML = link.port_no; + + // name + var name_td = tr.insertCell(-1); + name_td.className = 'port-name'; + name_td.innerHTML = link.name; + + // peer + var peer_td = tr.insertCell(-1); + var peer_port_span = document.createElement('span'); + peer_td.className = 'port-peer'; + peer_port_span.className = 'peer-port-name'; + peer_td.appendChild(peer_port_span); + + var peer_port = ''; + if (link.peer) { + if (link.peer.dpid) { + var peer = _DATA.switches[link.peer.dpid]; + if (peer) { + if (peer.ports[link.peer.port_no]) { + peer_port = peer.ports[link.peer.port_no].name; + } + } + } + } + peer_port_span.innerHTML = peer_port; + utils._repainteRows('link-list-table'); + + // wireshark + var wireshark_td = tr.insertCell(-1); + wireshark_td.className = 'wireshark'; + wireshark_td.innerHTML = ""; + }, + + refreshLinkList: function() { + utils.clearLinkList(); + if (_DATA.watching) { + var sw = _DATA.switches[_DATA.watching]; + for (var i in sw.ports) utils.appendLinkList(sw.ports[i]); + } + }, + + clearLinkList: function(){ + $('#link-list tr').remove('.content-table-item'); + }, + + ///// + // List (flows) + ///// + clearFlowList: function(){ + $('#flow-list tr').remove('.content-table-item'); + }, + + replaceFlowList: function(dpid, flows){ + if (dpid === false) { + utils.clearFlowList(); + return + } + if (dpid != _DATA.watching) return; + utils.clearFlowList() + + // sorted duration + flows.sort(function(a, b){ + if (a.stats.table_id < b.stats.table_id) return -1; + if (a.stats.table_id > b.stats.table_id) return 1; + if (a.stats.priority > b.stats.priority) return -1; + if (a.stats.priority < b.stats.priority) return 1; + if (a.stats.duration_sec > b.stats.duration_sec) return -1; + if (a.stats.duration_sec < b.stats.duration_sec) return 1; + if (a.stats.duration_nsec > b.stats.duration_nsec) return -1; + if (a.stats.duration_nsec < b.stats.duration_nsec) return 1; + return 0; + }); + + var list_table = document.getElementById("flow-list-table"); + for (var i in flows) { + var tr = list_table.insertRow(-1); + tr.className = 'content-table-item'; + tr.id = utils._flowListId(dpid, i); + var td = tr.insertCell(-1); + td.className = 'flow'; + + // stats + var stats = document.createElement('div'); + stats.className = 'flow-item-line'; + td.appendChild(stats); + var statsTitle = document.createElement('span'); + statsTitle.className = 'flow-item-title'; + statsTitle.innerHTML = 'stats:'; + stats.appendChild(statsTitle); + var statsVal = document.createElement('span'); + statsVal.className = 'flow-item-value'; + // sort key + var texts = []; + var sortKey = ['table_id', 'priority', 'duration_sec', 'duration_nsec']; + for (var k in sortKey) { + texts.push(sortKey[k] + '=' + flows[i].stats[sortKey[k]]); + delete flows[i].stats[sortKey[k]]; + } + for (var key in flows[i].stats) { + texts.push(key + '=' + flows[i].stats[key]); + } + statsVal.innerHTML = texts.join(', '); + stats.appendChild(statsVal); + + // rules + var rules = document.createElement('div'); + rules.className = 'flow-item-line'; + td.appendChild(rules); + var rulesTitle = document.createElement('span'); + rulesTitle.className = 'flow-item-title'; + rulesTitle.innerHTML = 'rules:'; + rules.appendChild(rulesTitle); + var rulesVal = document.createElement('span'); + rulesVal.className = 'flow-item-value'; + var texts = []; + for (var key in flows[i].rules) { + texts.push(key + '=' + flows[i].rules[key]); + } + rulesVal.innerHTML = texts.join(', '); + rules.appendChild(rulesVal); + + // actions + var actions = document.createElement('div'); + actions.className = 'flow-item-line'; + td.appendChild(actions); + var actionsTitle = document.createElement('span'); + actionsTitle.className = 'flow-item-title'; + actionsTitle.innerHTML = 'actions:'; + actions.appendChild(actionsTitle); + var actionsVal = document.createElement('span'); + actionsVal.className = 'flow-item-value'; + actionsVal.innerHTML = flows[i].actions.join(', '); + actions.appendChild(actionsVal); + + utils._repainteRows('flow-list-table'); + } + }, + + ///// + // Connections + ///// + addConnect: function(p1, p2) { + var endpoint1 = utils._switchId(p1.dpid); + var endpoint2 = utils._switchId(p2.dpid); + var uuids = [utils._connectionUUID(p1.dpid, p1.port_no), + utils._connectionUUID(p2.dpid, p2.port_no)] + + var overlays = [["Label", {label: p1.port_no.toString(), location: 0.02, cssClass: "port-no"}], + ["Label", {label: p2.port_no.toString(), location: 0.98, cssClass: "port-no"}]]; + + var connector = 'Straight'; + var endpoint = 'Blank'; + var anchors = ["Continuous", "Continuous"]; + var paintStyle = {"lineWidth": 3, + "strokeStyle": '#35FF35', + "outlineWidth": 0.5, + "outlineColor": '#AAA', + "dashstyle": "0 0 0 0"} + + var conn = jsPlumb.connect({source: endpoint1, + target: endpoint2, + uuids: uuids, + endpoint: endpoint, + paintStyle: paintStyle, + connector: connector, + anchors: anchors, + overlays: overlays}); + + var click = function(c) { topo.emphasisList(c) } + conn.bind('click', click); + }, + + delConnect: function(dpid, port_no) { + jsPlumb.deleteEndpoint(utils._connectionUUID(dpid, port_no)) + } +}; + + +/////////////////////////////////// +// rest +/////////////////////////////////// +var rest = { + getFlows: function(host, port, dpid, successfnc, errorfnc) { + if (typeof errorfnc === "undefined") errorfnc = function(){return false} + $.ajax({ + 'type': 'POST', + 'url': conf.URL_GET_FLOWS, + 'data': {"host": host, "port": port, "dpid": dpid}, + 'dataType': 'json', + 'success': successfnc, + 'error': errorfnc + }); + } +}; + +/////////////////////////////////// +// websocket +/////////////////////////////////// +var websocket = { + _sendMessage: function(msg) { + ws.send(JSON.stringify(msg)); + }, + + onMessage: function(msg) { + var msg = JSON.parse(msg); + + // user already updated to URL + if (msg.host != _DATA.input.host || msg.port != _DATA.input.port) return; + + if (msg.message == 'rest_disconnected') { + utils.restDisconnected(msg.host, msg.port); + return; + } + + utils.restConnected(msg.host, msg.port); + if (msg.message == 'add_switches') { + for (var i in msg.body) { + utils.registerEvent(websocket._addSwitch, msg.body[i]); + }; + + } else if (msg.message == 'del_switches') { + for (var i in msg.body) { + utils.registerEvent(websocket._delSwitch, msg.body[i]); + }; + + } else if (msg.message == 'add_ports') { + utils.registerEvent(function(ports){ + for (var i in ports) websocket._addPort(ports[i]); + }, msg.body) + + } else if (msg.message == 'del_ports') { + utils.registerEvent(function(ports){ + for (var i in ports) websocket._delPort(ports[i]); + }, msg.body) + + } else if (msg.message == 'add_links') { + for (var i in msg.body) { + utils.registerEvent(websocket._addLink, msg.body[i]); + }; + + } else if (msg.message == 'del_links') { + for (var i in msg.body) { + utils.registerEvent(websocket._delLink, msg.body[i]); + }; + + } else if (msg.message == 'replace_flows') { + utils.registerEvent(websocket._replaceFlows, msg.body); + } else { + // unknown message + return; + } + }, + + //// + // send messages + //// + sendRestUpdate: function(host, port){ + var msg = {}; + msg.message = 'rest_update'; + msg.body = {}; + msg.body.host = host; + msg.body.port = port; + websocket._sendMessage(msg); + }, + + sendWatchingSwitch: function(dpid){ + msg = {}; + msg.message = "watching_switch_update"; + msg.body = {}; + msg.body.dpid = dpid; + websocket._sendMessage(msg); + }, + + sendOpenWireshark: function(interface){ + msg = {}; + msg.message = "open_wireshark"; + msg.body = {}; + msg.body.interface = interface; + websocket._sendMessage(msg); + }, + + //// + // recive messages + //// + _addSwitch: function(sw) { + if (_DATA.switches[sw.dpid]) return; + _DATA.switches[sw.dpid] = sw; + topo.redesignTopology(); + }, + + _delSwitch: function(sw) { + if (_DATA.watching == sw.dpid) topo.watchingSwitch(); + + // connections + for (var p in _DATA.switches[sw.dpid].ports) { + websocket._delPort(_DATA.switches[sw.dpid].ports[p]); + } + + // node + utils.delSwitch(sw.dpid) + delete _DATA.switches[sw.dpid] + topo.redesignTopology(); + }, + + _addPort: function(port) { + if (_DATA.switches[port.dpid]) _DATA.switches[port.dpid].ports[port.port_no] = port; + utils.refreshLinkList(); + }, + + _delPort: function(port) { + // delConnection + utils.delConnect(port.dpid, port.port_no); + + // delete memory + for (var dpid in _DATA.switches) { + for (var port_no in _DATA.switches[dpid].ports) { + var target = _DATA.switches[dpid].ports[port_no]; + if (target.peer) { + if (target.peer.dpid == port.dpid && target.peer.port_no == port.port_no) { + _DATA.switches[dpid].ports[port_no].peer = {}; + break; + } + } + } + } + delete _DATA.switches[port.dpid].ports[port.port_no]; + + // refreshLinkList + utils.refreshLinkList(); + }, + + _addLink: function(link) { + _DATA.switches[link.p1.dpid].ports[link.p1.port_no].peer = link.p2; + _DATA.switches[link.p2.dpid].ports[link.p2.port_no].peer = link.p1; + utils.addConnect(link.p1, link.p2); + utils.refreshLinkList(); + }, + + _delLink: function(link) { + if (_DATA.switches[link.p1.dpid]) { + if (_DATA.switches[link.p1.dpid].ports[link.p1.port_no]) { + _DATA.switches[link.p1.dpid].ports[link.p1.port_no].peer = {}; + } + } + if (_DATA.switches[link.p2.dpid]) { + if (_DATA.switches[link.p2.dpid].ports[link.p2.port_no]) { + _DATA.switches[link.p2.dpid].ports[link.p2.port_no].peer = {}; + } + } + utils.delConnect(link.p1.dpid, link.p1.port_no); + utils.refreshLinkList(); + }, + + _replaceFlows: function(data) { + utils.replaceFlowList(data.dpid, data.flows); + } +}; diff --git a/ryu/gui/templates/base.html b/ryu/gui/templates/base.html new file mode 100644 index 00000000..52ed0303 --- /dev/null +++ b/ryu/gui/templates/base.html @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + {% block head %}{% endblock %} + + + +
+ {% block body %}{% endblock %} +
+ + + diff --git a/ryu/gui/templates/topology.html b/ryu/gui/templates/topology.html new file mode 100644 index 00000000..5d2e0786 --- /dev/null +++ b/ryu/gui/templates/topology.html @@ -0,0 +1,82 @@ +{% extends "base.html" %} +{% block head %} + +{% endblock%} + +{% block body %} + +
+
+
+

Enter the address of the controller to begin.

+ + +

+

+ + +

+

+
+
+
+ + + +
+
+
Flow entries
+
close
+
+
+ +
+
+
+
+ +
+
Topology
+
+
Unconnected
+
+
+ + +{% endblock %} diff --git a/ryu/gui/views/__init__.py b/ryu/gui/views/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ryu/gui/views/flow.py b/ryu/gui/views/flow.py new file mode 100644 index 00000000..2bdd1b76 --- /dev/null +++ b/ryu/gui/views/flow.py @@ -0,0 +1,88 @@ +# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import re +import logging +import httplib + +import view_base +from models import proxy + + +LOG = logging.getLogger('ryu.gui') + + +class FlowView(view_base.ViewBase): + def __init__(self, host, port, dpid, flows=None): + super(FlowView, self).__init__() + self.host = host + self.port = port + self.dpid = dpid + self.flows = flows + + def run(self): + if not self.flows: + # dump flows + return self._dump_flows() + + # TODO: flow-mod + return self.null_response() + + def _dump_flows(self): + address = '%s:%s' % (self.host, self.port) + res = {'host': self.host, + 'port': self.port, + 'dpid': self.dpid, + 'flows': []} + + flows = proxy.get_flows(address, int(self.dpid)) + for flow in flows: + actions = self._to_client_actions(flow.pop('actions')) + rules = self._to_client_rules(flow.pop('match')) + stats = self._to_client_stats(flow) + res['flows'].append({'stats': stats, + 'rules': rules, + 'actions': actions}) + return self.json_response(res) + + def _to_client_actions(self, actions): + # TODO:XXX + return actions + + def _to_client_rules(self, rules): + for name, val in rules.items(): + # hide default values for GUI + if name in ['in_port', 'dl_type', 'nw_proto', 'tp_src', 'tp_dst', + 'dl_vlan', 'dl_vlan_pcp']: + if val == 0: + del rules[name] + + if name in ['nw_dst', 'nw_src']: + if val == '0.0.0.0': + del rules[name] + + if name in ['dl_dst', 'dl_src']: + if val == '00:00:00:00:00:00': + del rules[name] + return rules + + def _to_client_stats(self, stats): + for name, val in stats.items(): + # hide default values for GUI + if name in ['hard_timeout', 'idle_timeout', + 'cookie']: + if val == 0: + del stats[name] + return stats diff --git a/ryu/gui/views/topology.py b/ryu/gui/views/topology.py new file mode 100644 index 00000000..c403737a --- /dev/null +++ b/ryu/gui/views/topology.py @@ -0,0 +1,26 @@ +# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from flask import render_template, request +import view_base + + +class IndexView(view_base.ViewBase): + def __init__(self): + super(IndexView, self).__init__() + + def run(self): + host, port = request.host.split(':') + return render_template('topology.html', host=host, port=port) diff --git a/ryu/gui/views/view_base.py b/ryu/gui/views/view_base.py new file mode 100644 index 00000000..0d5aa14b --- /dev/null +++ b/ryu/gui/views/view_base.py @@ -0,0 +1,37 @@ +# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +from flask import abort, make_response, jsonify + + +LOG = logging.getLogger('ryu_gui') + + +class ViewBase(object): + def __init__(self, *args, **kwargs): + pass + + def run(self): + LOG.error('run() is not defined.') + abort(500) + + def json_response(self, data): + return jsonify(**data) + + def null_response(self): + res = make_response() + res.headers['Content-type'] = 'text/plain' + return res diff --git a/ryu/gui/views/websocket.py b/ryu/gui/views/websocket.py new file mode 100644 index 00000000..f6829233 --- /dev/null +++ b/ryu/gui/views/websocket.py @@ -0,0 +1,204 @@ +# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import json + +import view_base +from models.topology import TopologyWatcher +from xml.dom import minidom + +LOG = logging.getLogger('ryu.gui') + + +class WebsocketView(view_base.ViewBase): + def __init__(self, ws): + super(WebsocketView, self).__init__() + self.ws = ws + self.address = None + self.watcher = None + + def run(self): + while True: + msg = self.ws.receive() + if msg is not None: + try: + msg = json.loads(msg) + except: + LOG.debug("json parse error: %s", msg) + continue + self._recv_message(msg) + else: + if self.watcher: + self.watcher.stop() + break + + self.ws.close() + LOG.info('Websocket: closed.') + return self.null_response() + + def _send_message(self, msg_name, address, body=None): + message = {} + message['message'] = msg_name + message['host'], message['port'] = address.split(':') + message['body'] = body + LOG.debug("Websocket: send msg.\n%s", json.dumps(message, indent=2)) + self.ws.send(json.dumps(message)) + + def _recv_message(self, msg): + LOG.debug('Websocket: recv msg.\n%s', json.dumps(msg, indent=2)) + + message = msg.get('message') + body = msg.get('body') + + if message == 'rest_update': + self._watcher_start(body) + elif message == 'watching_switch_update': + self._watching_switch_update(body) + elif message == 'open_wireshark': + self._open_wireshark(body) + else: + return + + def _watcher_start(self, body): + address = '%s:%s' % (body['host'], body['port']) + self.address = address + if self.watcher: + self.watcher.stop() + + self.watcher = TopologyWatcher( + update_handler=self.update_handler, + rest_error_handler=self.rest_error_handler) + self.watcher.start(address) + + def _watching_switch_update(self, body): + pass + + def _open_wireshark(self, body): + interface = '%s' % (body['interface']) + import os + os.system("sudo wireshark -i "+interface+" -k &") + + # called by watcher when topology update + def update_handler(self, address, delta): + if self.address != address: + # user be watching the another controller already + return + + LOG.debug(delta) + self._send_message('rest_connected', address) + self._send_del_links(address, delta.deleted) + self._send_del_ports(address, delta.deleted) + self._send_del_switches(address, delta.deleted) + self._send_add_switches(address, delta.added) + self._send_add_ports(address, delta.added) + self._send_add_links(address, delta.added) + + def _send_add_switches(self, address, topo): + body = self._build_switches_message(topo) + if body: + self._send_message('add_switches', address, body) + + def _send_del_switches(self, address, topo): + body = self._build_switches_message(topo) + if body: + self._send_message('del_switches', address, body) + + def _build_switches_message(self, topo): + body = [] + + # Read switch positions from network.xml + from xml.dom import minidom + position={} + xmldoc = None + try: + xmldoc = minidom.parse('network.xml') + itemlist = xmldoc.getElementsByTagName('node') + for s in itemlist: + n = s.attributes['id'].value + # Remove the N char at the beginning + n = int(n[1:]) + x = s.getElementsByTagName('x')[0].firstChild.data + y = s.getElementsByTagName('y')[0].firstChild.data + position[n]=(int(float(x)), int(float(y))) + except Exception, detail: + print("Failure to parse network.xml: %s" % str(detail)) + for s in topo['switches']: + position[int(float(s.dpid))]=(-1,-1) + + # Nodes creation + for s in topo['switches']: + pos = {} + pos['x']=position[int(float(s.dpid))][0] + pos['y']=position[int(float(s.dpid))][1] + S = {'dpid': s.dpid, 'ports': {}, 'pos': pos} + for p in s.ports: + S['ports'][p.port_no] = p.to_dict() + body.append(S) + return body + + def _send_add_ports(self, address, topo): + body = self._build_ports_message(topo) + if body: + self._send_message('add_ports', address, body) + + def _send_del_ports(self, address, topo): + body = self._build_ports_message(topo) + if body: + self._send_message('del_ports', address, body) + + def _build_ports_message(self, topo): + # send only except new added switches + ports = set(topo['ports']) + for s in topo['switches']: + ports -= set(s.ports) + + body = [] + for p in ports: + body.append(p.to_dict()) + + return body + + def _send_add_links(self, address, topo): + body = self._build_links_message(topo) + if body: + self._send_message('add_links', address, body) + + def _send_del_links(self, address, topo): + body = self._build_links_message(topo) + if body: + self._send_message('del_links', address, body) + + def _build_links_message(self, topo): + body = [] + for link in topo['links']: + # handle link as undirected + if link.src.dpid > link.dst.dpid: + continue + + p1 = link.src.to_dict() + p2 = link.dst.to_dict() + L = {'p1': p1.copy(), 'p2': p2.copy()} + L['p1']['peer'] = p2.copy() + L['p2']['peer'] = p1.copy() + + body.append(L) + + return body + + # called by watcher when rest api error + def rest_error_handler(self, address, e): + LOG.debug('REST API Error: %s', e) + self._send_message('rest_disconnected', address) diff --git a/ryu/ofproto/ofproto_v1_3.py b/ryu/ofproto/ofproto_v1_3.py index ab195c65..182eddc2 100644 --- a/ryu/ofproto/ofproto_v1_3.py +++ b/ryu/ofproto/ofproto_v1_3.py @@ -312,7 +312,7 @@ OFP_ACTION_EXPERIMENTER_HEADER_SIZE) #struct ofp_action_set_state -OFP_ACTION_SET_STATE_PACK_STR = '!HHIB7x' +OFP_ACTION_SET_STATE_PACK_STR = '!HHIBB6x' OFP_ACTION_SET_STATE_SIZE = 16 assert calcsize(OFP_ACTION_SET_STATE_PACK_STR) == OFP_ACTION_SET_STATE_SIZE diff --git a/ryu/ofproto/ofproto_v1_3_parser.py b/ryu/ofproto/ofproto_v1_3_parser.py index 085cbcc6..7f64112a 100644 --- a/ryu/ofproto/ofproto_v1_3_parser.py +++ b/ryu/ofproto/ofproto_v1_3_parser.py @@ -3267,25 +3267,27 @@ class OFPActionSetState(OFPAction): ================ ====================================================== state State instance stage_id Stage ID + bw_flag Backward Flag ================ ====================================================== """ - def __init__(self, state=0,stage_id=0,type_=None, len_=None): + def __init__(self, state=0, stage_id=0, bw_flag=0, type_=None, len_=None): super(OFPActionSetState, self).__init__() self.type = ofproto.OFPAT_SET_STATE self.len = ofproto.OFP_ACTION_SET_STATE_SIZE self.state= state self.stage_id= stage_id + self.bw_flag= bw_flag @classmethod def parser(cls, buf, offset): - (type_, len_, state, stage_id) = struct.unpack_from( + (type_, len_, state, stage_id, bw_flag) = struct.unpack_from( ofproto.OFP_ACTION_SET_STATE_PACK_STR, buf, offset) - return cls(state, stage_id) + return cls(state, stage_id, bw_flag) def serialize(self, buf, offset): msg_pack_into(ofproto.OFP_ACTION_SET_STATE_PACK_STR, - buf, offset, self.type, self.len, self.state, self.stage_id) + buf, offset, self.type, self.len, self.state, self.stage_id, self.bw_flag) @OFPAction.register_action_type(ofproto.OFPAT_SET_FLAG,ofproto.OFP_ACTION_SET_FLAG_SIZE) class OFPActionSetFlag(OFPAction): diff --git a/ryu/topology/switches.py b/ryu/topology/switches.py index 70226f19..b7dd3235 100644 --- a/ryu/topology/switches.py +++ b/ryu/topology/switches.py @@ -674,8 +674,7 @@ def _drop_packet(msg): if dp.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: dp.send_packet_out(buffer_id, msg.in_port, []) else: - LOG.error('cannot drop_packet. unsupported version. %x', - dp.ofproto.OFP_VERSION) + dp.send_packet_out(buffer_id, msg.match['in_port'], []) @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) def packet_in_handler(self, ev):