├── .gitignore ├── ArpTest ├── ArpTest.py └── README.md ├── AsciiTopo ├── .gitignore ├── README.md ├── ascii_topo.py ├── hosts.json ├── links.json └── switches.json ├── BFSPath ├── BFSCalculator.py ├── BFSController.py ├── README.md └── test.py ├── COSCUP2015Workshop ├── README.md ├── hub_app.py ├── packet_in.py └── route_app.py ├── D3Topo ├── MultiForce │ ├── app.css │ ├── app.js │ ├── data.json │ ├── floor.png │ ├── host.png │ ├── host.svg │ ├── index.html │ ├── reset.css │ ├── switch.png │ └── switch.svg └── README.md ├── FastFailover ├── README.md ├── ff.py └── topology.py ├── GetSwitches ├── GetSwitches.py └── README.md ├── LICENSE ├── ModifyRyuBehavior └── app.py ├── MultiControl ├── ModStatusApp.py ├── hierarchy │ ├── README.md │ ├── api.md │ ├── global.py │ ├── local.py │ ├── local_lib.py │ ├── run1.sh │ ├── run2.sh │ ├── run3.sh │ └── test_net.py ├── load_balance │ ├── global.py │ ├── lb.py │ └── liblb.py └── ms │ ├── MasterApp.py │ ├── SlaveApp.py │ ├── run_master.sh │ └── run_slave.sh ├── PacketGenerate └── pg.py ├── PortStatistics ├── README.md ├── mininet.sh ├── port_static_app.py └── proto.sh ├── README.md ├── RyuTest └── RyuTest.py ├── StaticFlow └── static_flow.py ├── TopologyEventTest ├── host_lib.py └── topology_event_test.py ├── mininet ├── README.md ├── bgp-3as │ ├── as.py │ ├── r1.conf │ ├── r2.conf │ ├── r3.conf │ ├── zebra-r1.conf │ ├── zebra-r2.conf │ └── zebra-r3.conf ├── bgp │ ├── README.md │ ├── configs │ │ ├── quagga1.conf │ │ ├── quagga2.conf │ │ └── zebra.conf │ └── topo.py ├── custom │ ├── README.md │ ├── custom.py │ ├── custom1.py │ ├── custom2.py │ └── custom3.py ├── fat tree │ ├── README.md │ └── fat_tree.py ├── leaf-spine │ └── leaf-spine.py ├── two controller │ ├── README.md │ └── two_controller.py └── vxlan │ ├── insert_flows.sh │ ├── s1_flows.txt │ ├── s2_flows.txt │ └── topology.py ├── p4 └── mininet │ ├── README.md │ ├── fat_tree.py │ └── p4_and_ovs.py ├── qos └── create_queue.sh ├── rest_event_test ├── README.md ├── rest_server │ ├── db.sqlite3 │ ├── manage.py │ ├── rest_app │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── tests.py │ │ ├── urls.py │ │ └── views.py │ └── rest_server │ │ ├── __init__.py │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py ├── ryu_app │ ├── simple_event_app.py │ └── simple_event_lib.py └── structure.png └── tinyrpc ├── client.py └── server.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /ArpTest/ArpTest.py: -------------------------------------------------------------------------------- 1 | from ryu.base import app_manager 2 | from ryu.controller import ofp_event 3 | from ryu.controller.handler import MAIN_DISPATCHER 4 | from ryu.controller.handler import set_ev_cls 5 | from ryu.controller import dpset 6 | from ryu.topology import api 7 | from ryu.lib.packet import ethernet 8 | from ryu.lib.packet import packet 9 | from ryu.lib.packet import arp 10 | from ryu.lib.packet import ipv6 11 | from ryu.ofproto import ether 12 | from ryu.ofproto import ofproto_v1_3 13 | 14 | 15 | class ArpTest(app_manager.RyuApp): 16 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 17 | 18 | dps = {} 19 | 20 | def __init__(self, *args, **kwargs): 21 | super(ArpTest, self).__init__(*args, **kwargs) 22 | 23 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 24 | def packet_in_handler(self, ev): 25 | msg = ev.msg 26 | datapath = msg.datapath 27 | datapath_id = datapath.id 28 | ofproto = datapath.ofproto 29 | parser = datapath.ofproto_parser 30 | in_port = msg.match['in_port'] 31 | 32 | pkt = packet.Packet(msg.data) 33 | eth = pkt.get_protocols(ethernet.ethernet)[0] 34 | 35 | print 'Ether type : %X' % eth.ethertype 36 | 37 | if eth.ethertype == ether.ETH_TYPE_IPV6: 38 | print 'Receive an ipv6 packet' 39 | ipv6_pkt = pkt.get_protocols(ipv6.ipv6)[0] 40 | print 'Src: %s, Dst: %s' % (ipv6_pkt.src, ipv6_pkt.dst) 41 | 42 | if eth.ethertype == ether.ETH_TYPE_ARP: 43 | print 'Receive an ARP packet' 44 | arp_pkt = pkt.get_protocols(arp.arp)[0] 45 | self.process_arp(datapath, in_port, arp_pkt) 46 | 47 | dst_mac = eth.dst 48 | src_mac = eth.src 49 | 50 | # print "From %s to %s, datapath_id : %s" % (str(src_mac), str(dst_mac), str(datapath_id)) 51 | self.set_default_dp_port(datapath_id, port=in_port, mac=src_mac) 52 | 53 | @set_ev_cls(dpset.EventDP, MAIN_DISPATCHER) 54 | def datapath_change_handler(self, ev): 55 | 56 | if ev.enter: 57 | print "Datapath entered, id %s" % ev.dp.id 58 | # switch = api.get_switch(self, ev.dp.id)[0] 59 | dpid = ev.dp.id 60 | self.set_default_dp_port(dpid) 61 | # self.send_arp(datapath=ev.dp) 62 | 63 | def set_default_dp_port(self, dpid, port=None, mac=None): 64 | 65 | if dpid not in self.dps: 66 | self.dps[dpid] = {'dpid': dpid, 'ports': []} 67 | 68 | if port != None and mac != None: 69 | ports = self.dps[dpid]['ports'] 70 | if mac not in ports: 71 | ports.append({'port': port, 'mac': mac}) 72 | 73 | def process_arp(self, datapath, port, arp_pkt): 74 | src_mac = arp_pkt.src_mac 75 | src_ip = arp_pkt.src_ip 76 | dst_mac = arp_pkt.dst_mac 77 | dst_ip = arp_pkt.dst_ip 78 | print 'src : %s %s, dst : %s %s' % (src_mac, src_ip, dst_mac, dst_ip) 79 | 80 | def send_arp(self, datapath, port=None, src_mac=None, src_ip=None, dst_mac=None, dst_ip=None): 81 | eth_pkt = ethernet.ethernet(ether.ETH_TYPE_ARP) 82 | arp_pkt = arp.arp() 83 | ofp_parser = datapath.ofproto_parser 84 | ofp = datapath.ofproto 85 | flood = ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD) 86 | actions = [flood] 87 | p = packet.Packet() 88 | p.add_protocol(arp_pkt) 89 | p.add_protocol(eth_pkt) 90 | p.serialize() 91 | out = ofp_parser.OFPPacketOut( 92 | datapath=datapath, 93 | in_port=datapath.ofproto.OFPP_CONTROLLER, 94 | actions=actions, 95 | buffer_id=0xffffffff, 96 | data=buffer(p.data)) 97 | 98 | print 'sending arp' 99 | datapath.send_msg(out) 100 | print 'sent!' 101 | -------------------------------------------------------------------------------- /ArpTest/README.md: -------------------------------------------------------------------------------- 1 | Arp Test 2 | ==== 3 | 4 | ### 用途/What is this?: 5 | 6 | 取得 Arp 封包並解析以及印出 src 跟 dst 7 | 8 | Receive, process arp packet and print src and dst from this packet 9 | 10 | ### 用法/How to use?: 11 | 12 | ``` shell 13 | ryu-manager ArpTest.py 14 | ``` 15 | -------------------------------------------------------------------------------- /AsciiTopo/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | output.txt 4 | -------------------------------------------------------------------------------- /AsciiTopo/README.md: -------------------------------------------------------------------------------- 1 | ASCII Topology Viewer 2 | ==== 3 | 4 | ### 用途/What is this?: 5 | 6 | 使用 switches.json, links.json 以及 hosts.json 去顯示文字版本的拓樸 7 | 8 | Use switches.json, links.json, hosts.json to display topology with ascii style. 9 | 10 | ### 用法/How to use?: 11 | 12 | ``` shell 13 | chmod +x ascii_topo.py 14 | ./ascii_topo.py 15 | ``` 16 | -------------------------------------------------------------------------------- /AsciiTopo/ascii_topo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf8 -*- 3 | import json 4 | 5 | 6 | # By John-Lin 7 | colors = [ 8 | '\033[90m', 9 | '\033[91m', 10 | '\033[92m', 11 | '\033[93m', 12 | '\033[94m', 13 | '\033[95m', 14 | '\033[96m', 15 | ] 16 | end_color = '\033[0m' 17 | num_colors = len(colors) 18 | 19 | 20 | def print_topo(switches=[], links=[], hosts=[]): 21 | tlinks = [] 22 | thosts = [] 23 | 24 | for switch in switches: 25 | print('{}┐{:>8}'.format(switch['dpid'], ' '), end='') 26 | 27 | for t in tlinks: 28 | cindex = tlinks.index(t) 29 | if t is None: 30 | print(' ', end='') 31 | else: 32 | print('{}|{}'.format(colors[cindex % num_colors], end_color), end='') 33 | 34 | print() 35 | 36 | for port in switch['ports']: 37 | if port != switch['ports'][-1]: 38 | print('{:>17}{}'.format('├', port['port_no']), end='') 39 | 40 | else: 41 | print('{:>17}{}'.format('└', port['port_no']), end='') 42 | 43 | for link in links: 44 | if port['hw_addr'] == link['src']['hw_addr'] and\ 45 | int(link['src']['dpid']) < int(link['dst']['dpid']): 46 | 47 | if None in tlinks: 48 | nindex = tlinks.index(None) 49 | tlinks[nindex] = link 50 | 51 | else: 52 | tlinks.append(link) 53 | 54 | for host in hosts: 55 | if port['hw_addr'] == host['port']['hw_addr']: 56 | thosts.append(host) 57 | 58 | for t in tlinks: 59 | if len(thosts) > 0: 60 | print('-' * (len(tlinks) + 1), end='') 61 | print('-'.join([th['mac'] for th in thosts]), end='') 62 | thosts = [] 63 | break 64 | 65 | cindex = tlinks.index(t) 66 | src_ports = [l['src'] if l is not None else None for l in tlinks] 67 | dst_ports = [l['dst'] if l is not None else None for l in tlinks] 68 | 69 | if t is None and (port in dst_ports): 70 | dindex = dst_ports.index(port) 71 | print('{}-{}'.format(colors[dindex % num_colors], end_color), end='') 72 | 73 | elif t is None and (port in src_ports): 74 | sindex = src_ports.index(port) 75 | print('{}-{}'.format(colors[sindex % num_colors], end_color), end='') 76 | 77 | elif t is None: 78 | print(' ', end='') 79 | 80 | elif port['hw_addr'] == t['src']['hw_addr']: 81 | print('{}┐{}'.format(colors[cindex % num_colors], end_color), end='') 82 | 83 | elif port['hw_addr'] == t['dst']['hw_addr']: 84 | print('{}┘{}'.format(colors[cindex % num_colors], end_color), end='') 85 | tlinks[cindex] = None 86 | 87 | while len(tlinks) > 0 and tlinks[-1] is None: 88 | del tlinks[-1] 89 | 90 | else: 91 | print('{}|{}'.format(colors[cindex % num_colors], end_color), end='') 92 | 93 | print() 94 | 95 | 96 | def main(): 97 | switches = json.load(open('switches.json', 'r')) 98 | links = json.load(open('links.json', 'r')) 99 | hosts = json.load(open('hosts.json', 'r')) 100 | print_topo(switches, links, hosts) 101 | 102 | 103 | if __name__ == '__main__': 104 | main() 105 | -------------------------------------------------------------------------------- /AsciiTopo/hosts.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "port": { 4 | "hw_addr": "fe:24:e3:ef:db:94", 5 | "name": "s6-eth2", 6 | "port_no": "00000002", 7 | "dpid": "0000000000000006" 8 | }, 9 | "mac": "da:21:4b:34:7f:30", 10 | "ipv4": [ 11 | "10.0.0.6" 12 | ], 13 | "ipv6": [] 14 | }, 15 | { 16 | "port": { 17 | "hw_addr": "f2:d1:4b:cd:0c:50", 18 | "name": "s4-eth1", 19 | "port_no": "00000001", 20 | "dpid": "0000000000000004" 21 | }, 22 | "mac": "be:83:91:ff:45:cf", 23 | "ipv4": [ 24 | "10.0.0.3" 25 | ], 26 | "ipv6": [] 27 | }, 28 | { 29 | "port": { 30 | "hw_addr": "d2:5c:0f:d9:b3:60", 31 | "name": "s3-eth2", 32 | "port_no": "00000002", 33 | "dpid": "0000000000000003" 34 | }, 35 | "mac": "96:4a:f4:27:4f:db", 36 | "ipv4": [ 37 | "10.0.0.2" 38 | ], 39 | "ipv6": [] 40 | }, 41 | { 42 | "port": { 43 | "hw_addr": "c6:34:2f:95:df:69", 44 | "name": "s7-eth1", 45 | "port_no": "00000001", 46 | "dpid": "0000000000000007" 47 | }, 48 | "mac": "36:f3:d6:30:4e:6d", 49 | "ipv4": [ 50 | "10.0.0.7" 51 | ], 52 | "ipv6": [] 53 | }, 54 | { 55 | "port": { 56 | "hw_addr": "82:29:47:b5:f7:41", 57 | "name": "s3-eth1", 58 | "port_no": "00000001", 59 | "dpid": "0000000000000003" 60 | }, 61 | "mac": "56:fa:1c:d1:66:93", 62 | "ipv4": [ 63 | "10.0.0.1" 64 | ], 65 | "ipv6": [] 66 | }, 67 | { 68 | "port": { 69 | "hw_addr": "46:08:5b:c3:8f:a1", 70 | "name": "s4-eth2", 71 | "port_no": "00000002", 72 | "dpid": "0000000000000004" 73 | }, 74 | "mac": "da:9a:a8:f3:e4:1d", 75 | "ipv4": [ 76 | "10.0.0.4" 77 | ], 78 | "ipv6": [] 79 | }, 80 | { 81 | "port": { 82 | "hw_addr": "42:4f:a6:cb:f4:a5", 83 | "name": "s6-eth1", 84 | "port_no": "00000001", 85 | "dpid": "0000000000000006" 86 | }, 87 | "mac": "7e:0c:83:ad:c6:f2", 88 | "ipv4": [ 89 | "10.0.0.5" 90 | ], 91 | "ipv6": [] 92 | }, 93 | { 94 | "port": { 95 | "hw_addr": "22:ca:d3:35:6c:7f", 96 | "name": "s7-eth2", 97 | "port_no": "00000002", 98 | "dpid": "0000000000000007" 99 | }, 100 | "mac": "6a:68:20:b8:2f:e6", 101 | "ipv4": [ 102 | "10.0.0.8" 103 | ], 104 | "ipv6": [] 105 | } 106 | ] 107 | -------------------------------------------------------------------------------- /AsciiTopo/links.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "src": { 4 | "hw_addr": "a2:7a:c3:11:87:58", 5 | "name": "s2-eth3", 6 | "port_no": "00000003", 7 | "dpid": "0000000000000002" 8 | }, 9 | "dst": { 10 | "hw_addr": "8e:ff:5c:da:40:e2", 11 | "name": "s1-eth1", 12 | "port_no": "00000001", 13 | "dpid": "0000000000000001" 14 | } 15 | }, 16 | { 17 | "src": { 18 | "hw_addr": "f2:0f:21:6c:39:9b", 19 | "name": "s5-eth1", 20 | "port_no": "00000001", 21 | "dpid": "0000000000000005" 22 | }, 23 | "dst": { 24 | "hw_addr": "fe:ef:ec:7a:c9:e0", 25 | "name": "s6-eth3", 26 | "port_no": "00000003", 27 | "dpid": "0000000000000006" 28 | } 29 | }, 30 | { 31 | "src": { 32 | "hw_addr": "6a:39:9f:8e:3c:c5", 33 | "name": "s4-eth3", 34 | "port_no": "00000003", 35 | "dpid": "0000000000000004" 36 | }, 37 | "dst": { 38 | "hw_addr": "e2:e9:f6:09:42:a4", 39 | "name": "s2-eth2", 40 | "port_no": "00000002", 41 | "dpid": "0000000000000002" 42 | } 43 | }, 44 | { 45 | "src": { 46 | "hw_addr": "72:4b:9d:ef:86:bb", 47 | "name": "s7-eth3", 48 | "port_no": "00000003", 49 | "dpid": "0000000000000007" 50 | }, 51 | "dst": { 52 | "hw_addr": "2a:87:2a:b3:99:9b", 53 | "name": "s5-eth2", 54 | "port_no": "00000002", 55 | "dpid": "0000000000000005" 56 | } 57 | }, 58 | { 59 | "src": { 60 | "hw_addr": "1e:f7:18:7f:8e:73", 61 | "name": "s2-eth1", 62 | "port_no": "00000001", 63 | "dpid": "0000000000000002" 64 | }, 65 | "dst": { 66 | "hw_addr": "66:00:34:b1:bc:24", 67 | "name": "s3-eth3", 68 | "port_no": "00000003", 69 | "dpid": "0000000000000003" 70 | } 71 | }, 72 | { 73 | "src": { 74 | "hw_addr": "8e:ff:5c:da:40:e2", 75 | "name": "s1-eth1", 76 | "port_no": "00000001", 77 | "dpid": "0000000000000001" 78 | }, 79 | "dst": { 80 | "hw_addr": "a2:7a:c3:11:87:58", 81 | "name": "s2-eth3", 82 | "port_no": "00000003", 83 | "dpid": "0000000000000002" 84 | } 85 | }, 86 | { 87 | "src": { 88 | "hw_addr": "66:00:34:b1:bc:24", 89 | "name": "s3-eth3", 90 | "port_no": "00000003", 91 | "dpid": "0000000000000003" 92 | }, 93 | "dst": { 94 | "hw_addr": "1e:f7:18:7f:8e:73", 95 | "name": "s2-eth1", 96 | "port_no": "00000001", 97 | "dpid": "0000000000000002" 98 | } 99 | }, 100 | { 101 | "src": { 102 | "hw_addr": "2a:87:2a:b3:99:9b", 103 | "name": "s5-eth2", 104 | "port_no": "00000002", 105 | "dpid": "0000000000000005" 106 | }, 107 | "dst": { 108 | "hw_addr": "72:4b:9d:ef:86:bb", 109 | "name": "s7-eth3", 110 | "port_no": "00000003", 111 | "dpid": "0000000000000007" 112 | } 113 | }, 114 | { 115 | "src": { 116 | "hw_addr": "b6:46:4f:59:62:23", 117 | "name": "s1-eth2", 118 | "port_no": "00000002", 119 | "dpid": "0000000000000001" 120 | }, 121 | "dst": { 122 | "hw_addr": "92:d9:ba:00:4d:15", 123 | "name": "s5-eth3", 124 | "port_no": "00000003", 125 | "dpid": "0000000000000005" 126 | } 127 | }, 128 | { 129 | "src": { 130 | "hw_addr": "fe:ef:ec:7a:c9:e0", 131 | "name": "s6-eth3", 132 | "port_no": "00000003", 133 | "dpid": "0000000000000006" 134 | }, 135 | "dst": { 136 | "hw_addr": "f2:0f:21:6c:39:9b", 137 | "name": "s5-eth1", 138 | "port_no": "00000001", 139 | "dpid": "0000000000000005" 140 | } 141 | }, 142 | { 143 | "src": { 144 | "hw_addr": "92:d9:ba:00:4d:15", 145 | "name": "s5-eth3", 146 | "port_no": "00000003", 147 | "dpid": "0000000000000005" 148 | }, 149 | "dst": { 150 | "hw_addr": "b6:46:4f:59:62:23", 151 | "name": "s1-eth2", 152 | "port_no": "00000002", 153 | "dpid": "0000000000000001" 154 | } 155 | }, 156 | { 157 | "src": { 158 | "hw_addr": "e2:e9:f6:09:42:a4", 159 | "name": "s2-eth2", 160 | "port_no": "00000002", 161 | "dpid": "0000000000000002" 162 | }, 163 | "dst": { 164 | "hw_addr": "6a:39:9f:8e:3c:c5", 165 | "name": "s4-eth3", 166 | "port_no": "00000003", 167 | "dpid": "0000000000000004" 168 | } 169 | } 170 | ] 171 | -------------------------------------------------------------------------------- /AsciiTopo/switches.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ports": [ 4 | { 5 | "hw_addr": "8e:ff:5c:da:40:e2", 6 | "name": "s1-eth1", 7 | "port_no": "00000001", 8 | "dpid": "0000000000000001" 9 | }, 10 | { 11 | "hw_addr": "b6:46:4f:59:62:23", 12 | "name": "s1-eth2", 13 | "port_no": "00000002", 14 | "dpid": "0000000000000001" 15 | } 16 | ], 17 | "dpid": "0000000000000001" 18 | }, 19 | { 20 | "ports": [ 21 | { 22 | "hw_addr": "1e:f7:18:7f:8e:73", 23 | "name": "s2-eth1", 24 | "port_no": "00000001", 25 | "dpid": "0000000000000002" 26 | }, 27 | { 28 | "hw_addr": "e2:e9:f6:09:42:a4", 29 | "name": "s2-eth2", 30 | "port_no": "00000002", 31 | "dpid": "0000000000000002" 32 | }, 33 | { 34 | "hw_addr": "a2:7a:c3:11:87:58", 35 | "name": "s2-eth3", 36 | "port_no": "00000003", 37 | "dpid": "0000000000000002" 38 | } 39 | ], 40 | "dpid": "0000000000000002" 41 | }, 42 | { 43 | "ports": [ 44 | { 45 | "hw_addr": "82:29:47:b5:f7:41", 46 | "name": "s3-eth1", 47 | "port_no": "00000001", 48 | "dpid": "0000000000000003" 49 | }, 50 | { 51 | "hw_addr": "d2:5c:0f:d9:b3:60", 52 | "name": "s3-eth2", 53 | "port_no": "00000002", 54 | "dpid": "0000000000000003" 55 | }, 56 | { 57 | "hw_addr": "66:00:34:b1:bc:24", 58 | "name": "s3-eth3", 59 | "port_no": "00000003", 60 | "dpid": "0000000000000003" 61 | } 62 | ], 63 | "dpid": "0000000000000003" 64 | }, 65 | { 66 | "ports": [ 67 | { 68 | "hw_addr": "f2:d1:4b:cd:0c:50", 69 | "name": "s4-eth1", 70 | "port_no": "00000001", 71 | "dpid": "0000000000000004" 72 | }, 73 | { 74 | "hw_addr": "46:08:5b:c3:8f:a1", 75 | "name": "s4-eth2", 76 | "port_no": "00000002", 77 | "dpid": "0000000000000004" 78 | }, 79 | { 80 | "hw_addr": "6a:39:9f:8e:3c:c5", 81 | "name": "s4-eth3", 82 | "port_no": "00000003", 83 | "dpid": "0000000000000004" 84 | } 85 | ], 86 | "dpid": "0000000000000004" 87 | }, 88 | { 89 | "ports": [ 90 | { 91 | "hw_addr": "f2:0f:21:6c:39:9b", 92 | "name": "s5-eth1", 93 | "port_no": "00000001", 94 | "dpid": "0000000000000005" 95 | }, 96 | { 97 | "hw_addr": "2a:87:2a:b3:99:9b", 98 | "name": "s5-eth2", 99 | "port_no": "00000002", 100 | "dpid": "0000000000000005" 101 | }, 102 | { 103 | "hw_addr": "92:d9:ba:00:4d:15", 104 | "name": "s5-eth3", 105 | "port_no": "00000003", 106 | "dpid": "0000000000000005" 107 | } 108 | ], 109 | "dpid": "0000000000000005" 110 | }, 111 | { 112 | "ports": [ 113 | { 114 | "hw_addr": "42:4f:a6:cb:f4:a5", 115 | "name": "s6-eth1", 116 | "port_no": "00000001", 117 | "dpid": "0000000000000006" 118 | }, 119 | { 120 | "hw_addr": "fe:24:e3:ef:db:94", 121 | "name": "s6-eth2", 122 | "port_no": "00000002", 123 | "dpid": "0000000000000006" 124 | }, 125 | { 126 | "hw_addr": "fe:ef:ec:7a:c9:e0", 127 | "name": "s6-eth3", 128 | "port_no": "00000003", 129 | "dpid": "0000000000000006" 130 | } 131 | ], 132 | "dpid": "0000000000000006" 133 | }, 134 | { 135 | "ports": [ 136 | { 137 | "hw_addr": "c6:34:2f:95:df:69", 138 | "name": "s7-eth1", 139 | "port_no": "00000001", 140 | "dpid": "0000000000000007" 141 | }, 142 | { 143 | "hw_addr": "22:ca:d3:35:6c:7f", 144 | "name": "s7-eth2", 145 | "port_no": "00000002", 146 | "dpid": "0000000000000007" 147 | }, 148 | { 149 | "hw_addr": "72:4b:9d:ef:86:bb", 150 | "name": "s7-eth3", 151 | "port_no": "00000003", 152 | "dpid": "0000000000000007" 153 | } 154 | ], 155 | "dpid": "0000000000000007" 156 | } 157 | ] 158 | -------------------------------------------------------------------------------- /BFSPath/BFSCalculator.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | 4 | class BFSCalculator(object): 5 | ''' 6 | BFS Calculator 7 | ''' 8 | 9 | def __init__(self, ryu_links): 10 | self.graph = {} 11 | if ryu_links != None: 12 | self.ryu_links = ryu_links 13 | for link in ryu_links: 14 | src = link.src 15 | dst = link.dst 16 | self.graph[src.dpid][dst.dpid] = True 17 | 18 | def add_link(self, ryu_link): 19 | ''' 20 | add link by ryu link 21 | param: 22 | ryu_link: Link object from ryu.topology.switch.Link 23 | ''' 24 | 25 | assert ryu_link != None 26 | src = ryu_link.src 27 | dst = ryu_link.dst 28 | 29 | assert src != None and dst != None 30 | 31 | if src.dpid not in self.graph: 32 | self.graph[src.dpid] = {} 33 | 34 | self.graph[src.dpid][dst.dpid] = True 35 | 36 | if dst.dpid not in self.graph: 37 | self.graph[dst.dpid] = {} 38 | 39 | self.graph[dst.dpid][src.dpid] = True 40 | 41 | def remove_link(self, src, dst): 42 | ''' 43 | remove link from this graph 44 | param: 45 | src: dpid from source datapath 46 | dst: dpid from destination datapath 47 | ''' 48 | 49 | assert src != None and dst != None 50 | if src in self.graph: 51 | if dst in self.graph[src]: 52 | self.graph[src][dst] = False 53 | 54 | if dst in self.graph: 55 | if src in self.graph[dst]: 56 | self.graph[dst][src] = False 57 | 58 | def is_linked(self, src, dst): 59 | ''' 60 | check if two datapath is linked 61 | param: 62 | src: dpid from source datapath 63 | dst: dpid from destination datapath 64 | ''' 65 | 66 | assert src != None and dst != None 67 | res = True 68 | res = res and (src in self.graph) 69 | 70 | if not res: 71 | return res 72 | 73 | res = res and (dst in self.graph[src]) 74 | 75 | if not res: 76 | return res 77 | 78 | res = res and self.graph[src][dst] 79 | return res 80 | 81 | def get_port_to_port_info(self, src, dst): 82 | ''' 83 | get port to port information 84 | for example: 85 | s1-port1 linked to s4-port5 86 | it will return 87 | {'src_port':'00000001', 'dst_port':'00000005'} 88 | param: 89 | src: dpid from source datapath 90 | dst: dpid from destination datapath 91 | ''' 92 | 93 | result = [] 94 | if not self.is_linked(src, dst): 95 | return result 96 | 97 | # filter to get links that source and dstination is matched 98 | def f(l): 99 | return l.src.dpid == src and l.dst.dpid == dst 100 | 101 | filtered_link = fliter(f, self.ryu_links) 102 | for fl in filtered_link: 103 | s_port = fl.src.port_no 104 | d_port = fl.dst.port_no 105 | result.append({'src_port': s_port, 'dst_port': d_port}) 106 | 107 | return result 108 | 109 | def get_links(self, dpid): 110 | ''' 111 | get links from datapath 112 | param: 113 | dpid: id from datapath 114 | return: 115 | a list that contains it's links 116 | ''' 117 | 118 | assert dpid != None 119 | if dpid not in self.graph: 120 | return [] 121 | 122 | links = [] 123 | for n in self.graph[dpid]: 124 | if self.graph[dpid][n]: 125 | links.append(n) 126 | 127 | return links 128 | 129 | def get_short_path(self, src, dst): 130 | ''' 131 | get shortest path by BFS 132 | param: 133 | src: dpid from source datapath 134 | dst: dpid from destination datapath 135 | return: 136 | a list of path, contains ports, for example: 137 | [ 138 | {'dpid':'dpid1', 'in_port':None, 'out_port':'outport1'}, 139 | {'dpid':'dpid2', 'in_port':'in_port2', 'out_port':'outport2'}, 140 | ...... 141 | {'dpid':'dpidn', 'in_port':'in_portn', 'out_port':None} 142 | ] 143 | if destination is not reachable, it'll return an empty list 144 | ''' 145 | records = {src: 0} 146 | node_queue = deque() 147 | node_queue.append(src) 148 | while len(node_queue) != 0: 149 | node = node_queue.popleft() 150 | 151 | if node == dst: 152 | break 153 | 154 | links = self.get_links(node) 155 | if node in records: 156 | for _dst in links: 157 | if _dst not in records: 158 | records[_dst] = records[node] + 1 159 | node_queue.append(_dst) 160 | else: 161 | break 162 | 163 | if dst not in records or records[dst] == -1: 164 | return [] 165 | 166 | # get shortst path... 167 | sorted_res = sorted(records.items(), key=lambda x: x[1], reverse=True) 168 | 169 | # remove unnecessary nodes 170 | dst_deepth = records[dst] 171 | need_to_remove = [] 172 | for node in sorted_res: 173 | if node[0] != dst and node[1] == dst_deepth: 174 | need_to_remove.append(node) 175 | 176 | for node in need_to_remove: 177 | sorted_res.remove(node) 178 | 179 | deepth_set = {} 180 | for item in sorted_res: 181 | if item[1] not in deepth_set: 182 | deepth_set[item[1]] = [] 183 | deepth_set[item[1]].append(item[0]) 184 | 185 | result = [deepth_set[records[dst]][0]] 186 | self.get_short_path_rec(deepth_set, records[dst], result) 187 | 188 | return result 189 | 190 | def get_short_path_rec(self, deepth_set, deepth, result): 191 | '''get shotest path by using recursive method''' 192 | 193 | if deepth == 0: 194 | result = result.reverse() 195 | return True 196 | 197 | else: 198 | current_node = result[len(result) - 1] 199 | pos_nodes = deepth_set[deepth - 1] 200 | 201 | for node in pos_nodes: 202 | res = False 203 | 204 | if self.is_linked(current_node, node): 205 | result.append(node) 206 | res = self.get_short_path_rec(deepth_set, deepth - 1, result) 207 | if res: 208 | return True 209 | result.pop() 210 | 211 | return False 212 | -------------------------------------------------------------------------------- /BFSPath/BFSController.py: -------------------------------------------------------------------------------- 1 | ''' 2 | OF Controller 3 | ''' 4 | from ryu.base import app_manager 5 | from ryu.controller import ofp_event 6 | from ryu.controller.handler import MAIN_DISPATCHER 7 | from ryu.controller.handler import set_ev_cls 8 | from ryu.controller import dpset 9 | from ryu.lib.packet import ethernet 10 | from ryu.lib.packet import packet 11 | from ryu.lib.packet import arp 12 | from ryu.lib.packet import ipv6 13 | from ryu.ofproto import ether 14 | from ryu.ofproto import ofproto_v1_3 15 | from ryu.topology import api 16 | from BFSCalculator import BFSCalculator 17 | 18 | 19 | class BFSSwitch(app_manager.RyuApp): 20 | ''' 21 | Using BFS to generate path. 22 | ''' 23 | 24 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 25 | 26 | dps = {} 27 | 28 | def __init__(self, *args, **kwargs): 29 | super(BFSSwitch, self).__init__(*args, **kwargs) 30 | 31 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 32 | def packet_in_handler(self, ev): 33 | ''' 34 | process input packet 35 | ''' 36 | msg = ev.msg 37 | datapath = msg.datapath 38 | datapath_id = datapath.id 39 | ofproto = datapath.ofproto 40 | parser = datapath.ofproto_parser 41 | in_port = msg.match['in_port'] 42 | 43 | pkt = packet.Packet(msg.data) 44 | eth = pkt.get_protocols(ethernet.ethernet)[0] 45 | 46 | # print 'Ether type : %X' % eth.ethertype 47 | 48 | if eth.ethertype == ether.ETH_TYPE_IPV6: 49 | # print 'Receive an ipv6 packet' 50 | ipv6_pkt = pkt.get_protocols(ipv6.ipv6)[0] 51 | # print 'Src: %s, Dst: %s' % (ipv6_pkt.src, ipv6_pkt.dst) 52 | 53 | if eth.ethertype == ether.ETH_TYPE_ARP: 54 | print 'Receive an ARP packet' 55 | arp_pkt = pkt.get_protocols(arp.arp)[0] 56 | self.process_arp(datapath, in_port, arp_pkt) 57 | 58 | @set_ev_cls(dpset.EventDP, MAIN_DISPATCHER) 59 | def datapath_change_handler(self, ev): 60 | 61 | if ev.enter: 62 | print "Datapath entered, id %s" % ev.dp.id 63 | 64 | def process_arp(self, datapath, in_port, arp_pkt): 65 | src_mac = arp_pkt.src_mac 66 | src_ip = arp_pkt.src_ip 67 | dst_mac = arp_pkt.dst_mac 68 | dst_ip = arp_pkt.dst_ip 69 | print '[arp] %s %s -> %s %s' % (src_mac, src_ip, dst_mac, dst_ip) 70 | -------------------------------------------------------------------------------- /BFSPath/README.md: -------------------------------------------------------------------------------- 1 | BFS Path 2 | ==== 3 | 4 | ### 用途/What is this?: 5 | 6 | 透過 BFS 演算法計算最短路徑,並且透過最短路徑傳遞封包 7 | 8 | Use BFS algorithm to calculate shortest path, and route the packet over this path 9 | 10 | ### 用法/How to use?: 11 | 12 | 目前尚未完成,完成後預計使用方式如下: 13 | 14 | This project is not finish yet, when finished you can use is by: 15 | 16 | ``` shell 17 | ryu-manager --observe-links BFSController 18 | ``` 19 | -------------------------------------------------------------------------------- /BFSPath/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- encoding: utf-8 -*- 3 | # file: test.py 4 | 5 | '''BFSCalculator test''' 6 | 7 | from BFSCalculator import BFSCalculator 8 | 9 | 10 | class DP: 11 | ''' fake Ryu Datapath, just for test''' 12 | def __init__(self, dpid): 13 | self.dpid = dpid 14 | 15 | class RyuLink: 16 | ''' fake RyuLink, just for test''' 17 | 18 | def __init__(self, src, dst): 19 | self.src = DP(src) 20 | self.dst = DP(dst) 21 | 22 | 23 | def main(): 24 | '''main function''' 25 | links = [] 26 | calculator = BFSCalculator(links) 27 | calculator.add_link(RyuLink('a', 'b')) 28 | calculator.add_link(RyuLink('a', 'c')) 29 | calculator.add_link(RyuLink('b', 'd')) 30 | calculator.add_link(RyuLink('b', 'f')) 31 | calculator.add_link(RyuLink('c', 'f')) 32 | calculator.add_link(RyuLink('c', 'g')) 33 | calculator.add_link(RyuLink('d', 'e')) 34 | calculator.add_link(RyuLink('e', 'f')) 35 | calculator.add_link(RyuLink('f', 'g')) 36 | calculator.add_link(RyuLink('f', 'h')) 37 | 38 | from_node = 'a' 39 | to_node = 'h' 40 | res = calculator.get_short_path(from_node, to_node) 41 | print 'From %s to %s: ' % (from_node, to_node) 42 | print res 43 | 44 | if __name__ == '__main__': 45 | main() 46 | -------------------------------------------------------------------------------- /COSCUP2015Workshop/README.md: -------------------------------------------------------------------------------- 1 | COSCUP 215 Workshop 2 | ==== 3 | 4 | ### 用途/What is this?: 5 | 6 | 三個檔案是在 COSCUP 2015 Open network Workshop 中所使用,功能分別是: 7 | 8 | These files are used in COSCUP 2015 Open network Workshop, functions of 9 | 10 | these apps are: 11 | 12 | 1. simple hub 13 | 2. packet in analyzer 14 | 3. routing 15 | 16 | ### 用法/How to use?: 17 | 18 | 只需使用 ryu-manager 啟動即可,要注意的是 route app 需要啟動 lldp 功能才可以正常運作 19 | 20 | ``` shell 21 | ryu-manager --observe-links [App name] 22 | ``` 23 | -------------------------------------------------------------------------------- /COSCUP2015Workshop/hub_app.py: -------------------------------------------------------------------------------- 1 | from ryu.base import app_manager 2 | from ryu.controller import ofp_event 3 | from ryu.controller.handler import MAIN_DISPATCHER 4 | from ryu.controller.handler import set_ev_cls 5 | from ryu.ofproto import ofproto_v1_0 6 | from ryu.lib.packet import packet 7 | from ryu.lib.packet import ethernet 8 | from ryu.lib.packet import ether_types 9 | 10 | class HubApp(app_manager.RyuApp): 11 | OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION] 12 | 13 | def __init__(self, *args, **kwargs): 14 | super(HubApp, self).__init__(*args, **kwargs) 15 | 16 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 17 | def packet_in_handler(self, ev): 18 | msg = ev.msg 19 | 20 | pkt = packet.Packet(msg.data) 21 | eth = pkt.get_protocols(ethernet.ethernet)[0] 22 | if eth.ethertype == ether_types.ETH_TYPE_LLDP: 23 | # ignore lldp packet 24 | return 25 | num_protocols = len(pkt.protocols) 26 | print('num protocols : {}'.format(num_protocols)) 27 | for protocol in pkt.protocols: 28 | print(protocol) 29 | 30 | datapath = msg.datapath 31 | ofproto = datapath.ofproto 32 | out_port = ofproto.OFPP_FLOOD 33 | 34 | actions = [datapath.ofproto_parser.OFPActionOutput(out_port)] 35 | 36 | data = None 37 | if msg.buffer_id == ofproto.OFP_NO_BUFFER: 38 | data = msg.data 39 | 40 | out = datapath.ofproto_parser.OFPPacketOut( 41 | datapath=datapath, buffer_id=msg.buffer_id, in_port=msg.in_port, 42 | actions=actions, data=data) 43 | datapath.send_msg(out) 44 | -------------------------------------------------------------------------------- /COSCUP2015Workshop/packet_in.py: -------------------------------------------------------------------------------- 1 | from ryu.base import app_manager 2 | from ryu.controller import ofp_event 3 | from ryu.controller.handler import MAIN_DISPATCHER 4 | from ryu.controller.handler import set_ev_cls 5 | from ryu.ofproto import ofproto_v1_0 6 | from ryu.lib.packet import packet 7 | from ryu.lib.packet import ethernet 8 | from ryu.lib.packet import ether_types 9 | 10 | 11 | class PkInApp(app_manager.RyuApp): 12 | OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION] 13 | 14 | def __init__(self, *args, **kwargs): 15 | super(PkInApp, self).__init__(*args, **kwargs) 16 | 17 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 18 | def packet_in_handler(self, ev): 19 | msg = ev.msg 20 | 21 | pkt = packet.Packet(msg.data) 22 | eth = pkt.get_protocols(ethernet.ethernet)[0] 23 | if eth.ethertype == ether_types.ETH_TYPE_LLDP: 24 | # ignore lldp packet 25 | return 26 | 27 | num_protocols = len(pkt.protocols) 28 | print('num protocols : {}'.format(num_protocols)) 29 | for protocol in pkt.protocols: 30 | print(protocol) 31 | -------------------------------------------------------------------------------- /COSCUP2015Workshop/route_app.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | 3 | from ryu.base import app_manager 4 | from ryu.controller import ofp_event 5 | from ryu.controller.handler import MAIN_DISPATCHER 6 | from ryu.controller.handler import set_ev_cls 7 | from ryu.ofproto import ofproto_v1_0 8 | from ryu.lib.mac import haddr_to_bin 9 | from ryu.lib.packet import packet 10 | from ryu.lib.packet import ethernet 11 | from ryu.lib.packet import ether_types 12 | from ryu.topology import api as ryu_api 13 | 14 | 15 | class RouteApp(app_manager.RyuApp): 16 | OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION] 17 | 18 | def __init__(self, *args, **kwargs): 19 | super(RouteApp, self).__init__(*args, **kwargs) 20 | 21 | def add_flow(self, datapath, match, actions): 22 | ofproto = datapath.ofproto 23 | 24 | mod = datapath.ofproto_parser.OFPFlowMod( 25 | datapath=datapath, match=match, cookie=0, 26 | command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, 27 | priority=ofproto.OFP_DEFAULT_PRIORITY, 28 | flags=ofproto.OFPFF_SEND_FLOW_REM, actions=actions) 29 | datapath.send_msg(mod) 30 | 31 | def find_host(self, mac_addr): 32 | hosts = ryu_api.get_all_host(self) 33 | for host in hosts: 34 | if host.mac == mac_addr: 35 | return host 36 | 37 | return None 38 | 39 | def flood_packet(self, dp, msg): 40 | ofproto = dp.ofproto 41 | out_port = ofproto.OFPP_FLOOD 42 | actions = [dp.ofproto_parser.OFPActionOutput(out_port)] 43 | data = None 44 | 45 | if msg.buffer_id == ofproto.OFP_NO_BUFFER: 46 | data = msg.data 47 | 48 | out = dp.ofproto_parser.OFPPacketOut( 49 | datapath=dp, buffer_id=msg.buffer_id, in_port=msg.in_port, 50 | actions=actions, data=data) 51 | dp.send_msg(out) 52 | 53 | def get_all_links(self): 54 | ''' 55 | link form format: 56 | [(a.1, b.1), (b.2, c.1), (b.3, d.1)] 57 | x.k means dpid x and port no k 58 | a.1 means first port in switch "a" 59 | this topology should looks like: 60 | a.1 - 1.b.2 - 1.c 61 | 3 62 | | 63 | 1 64 | d 65 | 66 | ''' 67 | 68 | all_links = ryu_api.get_all_link(self) 69 | result = [] 70 | 71 | for link in all_links: 72 | src = '{}.{}'.format(link.src.dpid, link.src.port_no) 73 | dst = '{}.{}'.format(link.dst.dpid, link.dst.port_no) 74 | result.append((src, dst)) 75 | 76 | # internal switch links 77 | all_switches = ryu_api.get_all_switch(self) 78 | link_to_add = [] 79 | 80 | # O(n^3), such dirty!! 81 | for switch in all_switches: 82 | ports = switch.ports 83 | 84 | for port in ports: 85 | for _port in ports: 86 | if port != _port: 87 | src = '{}.{}'.format(port.dpid, port.port_no) 88 | dst = '{}.{}'.format(_port.dpid, _port.port_no) 89 | link_to_add.append((src, dst)) 90 | 91 | result.extend(link_to_add) 92 | return result 93 | 94 | def cal_shortest_path(self, src_host, dst_host): 95 | src_port = src_host.port 96 | dst_port = dst_host.port 97 | 98 | all_links = self.get_all_links() 99 | 100 | graph = nx.Graph() 101 | graph.add_edges_from(all_links) 102 | 103 | src = '{}.{}'.format(src_port.dpid, src_port.port_no) 104 | dst = '{}.{}'.format(dst_port.dpid, dst_port.port_no) 105 | 106 | if nx.has_path(graph, src, dst): 107 | return nx.shortest_path(graph, src, dst) 108 | 109 | return None 110 | 111 | def get_dp(self, dpid): 112 | switch = ryu_api.get_switch(self, dpid)[0] 113 | return switch.dp 114 | 115 | def packet_out(self, dp, msg, out_port): 116 | ofproto = dp.ofproto 117 | actions = [dp.ofproto_parser.OFPActionOutput(out_port)] 118 | data = None 119 | 120 | if msg.buffer_id == ofproto.OFP_NO_BUFFER: 121 | data = msg.data 122 | 123 | out = dp.ofproto_parser.OFPPacketOut( 124 | datapath=dp, buffer_id=msg.buffer_id, in_port=msg.in_port, 125 | actions=actions, data=data) 126 | dp.send_msg(out) 127 | 128 | def install_path(self, match, path): 129 | for node in path: 130 | dpid = int(node.split('.')[0]) 131 | port_no = int(node.split('.')[1]) 132 | dp = self.get_dp(dpid) 133 | actions = [dp.ofproto_parser.OFPActionOutput(port_no)] 134 | self.add_flow(dp, match, actions) 135 | 136 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 137 | def packet_in_handler(self, ev): 138 | msg = ev.msg 139 | dp = msg.datapath 140 | dpid = dp.id 141 | ofproto = dp.ofproto 142 | 143 | pkt = packet.Packet(msg.data) 144 | eth = pkt.get_protocol(ethernet.ethernet) 145 | 146 | if eth.ethertype == ether_types.ETH_TYPE_LLDP: 147 | # ignore lldp packet 148 | return 149 | 150 | src = eth.src 151 | dst = eth.dst 152 | 153 | if dst.startswith('33:33'): 154 | # IP multicast, flood it.... 155 | self.flood_packet(dp, msg) 156 | return 157 | 158 | if eth.ethertype == ether_types.ETH_TYPE_ARP: 159 | # arp, flood it 160 | self.flood_packet(dp, msg) 161 | return 162 | 163 | if dst == 'ff:ff:ff:ff:ff:ff': 164 | self.flood_packet(dp, msg) 165 | return 166 | 167 | self.logger.info('From {} to {}'.format(src, dst)) 168 | 169 | # find dst host location(dpid, port) 170 | dst_host = self.find_host(dst) 171 | 172 | # can't find dst, flood it. 173 | if not dst_host: 174 | self.logger.info('Can\'t find host {}'.format(dst)) 175 | self.flood_packet(dp, msg) 176 | return 177 | 178 | src_host = self.find_host(src) 179 | 180 | # calculate shortest path 181 | shortest_path = self.cal_shortest_path(src_host, dst_host) 182 | 183 | # can't find path, flood it! 184 | if not shortest_path: 185 | self.logger.info('Can\'t find path') 186 | self.flood_packet(dp, msg) 187 | return 188 | 189 | self.logger.info('Shortest path : ') 190 | self.logger.info(shortest_path) 191 | 192 | # Now, insert flows to switches! 193 | # shortest_path example: 194 | # from dpid 7, port 2 to dpid 3 port 1 195 | # ['7.2', '7.3', '5.2', '5.3', '1.2', '1.1', '2.3', '2.1', '3.3', '3.1'] 196 | 197 | # create match 198 | match = dp.ofproto_parser.OFPMatch( 199 | dl_dst=haddr_to_bin(dst)) 200 | 201 | self.install_path(match, shortest_path[1::2]) 202 | 203 | # create reverse path 204 | match = dp.ofproto_parser.OFPMatch( 205 | dl_dst=haddr_to_bin(src)) 206 | 207 | self.install_path(match, shortest_path[2::2]) 208 | 209 | # packet out this packet! 210 | node = shortest_path[1] 211 | dpid = int(node.split('.')[0]) 212 | port_no = int(node.split('.')[1]) 213 | self.packet_out(dp, msg, port_no) 214 | 215 | -------------------------------------------------------------------------------- /D3Topo/MultiForce/app.css: -------------------------------------------------------------------------------- 1 | @import url("reset.css"); 2 | 3 | .link { 4 | fill: none; 5 | stroke: #000; 6 | cursor: default; 7 | z-index: 1; 8 | } 9 | 10 | .node { 11 | z-index: 2; 12 | } -------------------------------------------------------------------------------- /D3Topo/MultiForce/app.js: -------------------------------------------------------------------------------- 1 | /*jslint plusplus: true */ 2 | 'use strict'; 3 | 4 | var topo_view = $('#topology'); 5 | var width = topo_view.width(); 6 | var height = topo_view.height() - 60; 7 | var allSVGElem = {}; 8 | var svg = d3.select('#topology').append("svg") 9 | .attr("width", width) 10 | .attr("height", height); 11 | var d3_nodes = [], 12 | d3_links = []; 13 | 14 | var forceCenter = [ 15 | {'x': width * 0.5, 'y': height * 0}, 16 | {'x': width * 0.5, 'y': height * 1}, 17 | {'x': width * 0.5, 'y': height * 2} 18 | ]; 19 | 20 | var layerCenter = [ 21 | {'x': width * 0.5, 'y': height * 0.4}, 22 | {'x': width * 0.5, 'y': height * 0.6}, 23 | {'x': width * 0.5, 'y': height * 0.8} 24 | ]; 25 | 26 | var force = d3.layout.force() 27 | .gravity(0.4) 28 | .charge(-3000) 29 | .linkDistance(function (d) { 30 | // XXX: I can't change link distance..... 31 | if(d === 'c') { 32 | return 100; 33 | } else { 34 | return 100; 35 | } 36 | }) 37 | .linkStrength(function (d) { 38 | // XXX: no use? 39 | if(d === 'c') { 40 | return 1.5; 41 | } else { 42 | return 1; 43 | } 44 | }) 45 | .friction(0.7) 46 | .theta(0.3) 47 | .size([width, height]); 48 | 49 | 50 | 51 | function linkExist(src, dst, links) { 52 | var index; 53 | for (index = 0; index < links.length; index++) { 54 | if (links[index].source === src && links[index].target === dst) { 55 | return true; 56 | } 57 | if (links[index].source === dst && links[index].target === src) { 58 | return true; 59 | } 60 | } 61 | return false; 62 | } 63 | 64 | function searchSwitchIndex(dpid, domain, nodes) { 65 | var index; 66 | for (index = 0; index < nodes.length; index++) { 67 | if (nodes[index].dpid === dpid && nodes[index].domain === domain) { 68 | return index; 69 | } 70 | } 71 | return -1; 72 | } 73 | 74 | function forceTick(e) { 75 | var k = 0.1 * e.alpha; 76 | allSVGElem.links 77 | .attr('x1', function (d) { return d.source.x; }) 78 | .attr('y1', function (d) { 79 | if (d.source.domain === 0) { 80 | if (d.source.y + 20 > height / 2) { 81 | return height / 2 - 20; 82 | } else { 83 | return d.source.y; 84 | } 85 | } else { 86 | if (d.source.y - 20 < height / 2) { 87 | return height / 2 + 20; 88 | } else { 89 | return d.source.y; 90 | } 91 | } 92 | }) 93 | .attr('x2', function (d) { return d.target.x; }) 94 | .attr('y2', function (d) { 95 | if (d.target.domain === 0) { 96 | if (d.target.y > height / 2) { 97 | return height / 2; 98 | } else { 99 | return d.target.y; 100 | } 101 | } else { 102 | if (d.target.y < height / 2) { 103 | return height / 2; 104 | } else { 105 | return d.target.y; 106 | } 107 | } 108 | }); 109 | 110 | // for multi force 111 | d3_nodes.forEach(function (o, i) { 112 | o.x += (forceCenter[o.domain].x - o.x) * k; 113 | o.y += (forceCenter[o.domain].y - o.y) * k; 114 | }); 115 | 116 | allSVGElem.nodes 117 | .attr('x', function (d) { return d.x - 20; }) 118 | .attr('y', function (d) { 119 | // give it limit! 120 | if (d.domain === 0) { 121 | if (d.y - 20 > height / 2) { 122 | return height / 2; 123 | } else { 124 | return d.y - 20; 125 | } 126 | } else { 127 | if (d.y - 20 < height / 2) { 128 | return height / 2; 129 | } else { 130 | return d.y - 20; 131 | } 132 | } 133 | 134 | }); 135 | 136 | } 137 | 138 | function loadData(err, data) { 139 | 140 | if (err) { 141 | console.log(err); 142 | console.log('Error on loading data!'); 143 | return; 144 | } 145 | 146 | var topos = [data.domain0, data.domain1, data.domain2], 147 | index, 148 | domain; 149 | 150 | for (domain = 0; domain < topos.length; domain++) { 151 | 152 | var switches = topos[domain].switches, 153 | links = topos[domain].links, 154 | hosts = topos[domain].hosts; 155 | 156 | for (index = 0; index < switches.length; index++) { 157 | switches[index].type = 's'; 158 | switches[index].domain = domain; 159 | d3_nodes.push(switches[index]); 160 | } 161 | 162 | for (index = 0; index < hosts.length; index++) { 163 | hosts[index].type = 'h'; 164 | hosts[index].domain = domain; 165 | 166 | // get index of host before push it. 167 | var host_index = d3_nodes.length, 168 | switch_index = searchSwitchIndex(hosts[index].dpid, domain, d3_nodes); 169 | d3_nodes.push(hosts[index]); 170 | 171 | // add host to switch link. 172 | d3_links.push({source: host_index, target: switch_index, type: 'h'}); 173 | } 174 | 175 | for (index = 0; index < links.length; index++) { 176 | var src_dpid = links[index].src.dpid, 177 | dst_dpid = links[index].dst.dpid, 178 | src_index = searchSwitchIndex(src_dpid, domain, d3_nodes), 179 | dst_index = searchSwitchIndex(dst_dpid, domain, d3_nodes); 180 | if (!linkExist(src_index, dst_index, d3_links)) { 181 | d3_links.push({source: src_index, target: dst_index, type: 's'}); 182 | } 183 | } 184 | } 185 | 186 | var crossDoaminLinks = data.crossDomainLinks; 187 | for (index = 0; index < crossDoaminLinks.length; index++) { 188 | var link = crossDoaminLinks[index], 189 | src_index = searchSwitchIndex(link.src.dpid, link.src.domain, d3_nodes), 190 | dst_index = searchSwitchIndex(link.dst.dpid, link.dst.domain, d3_nodes); 191 | 192 | d3_links.push({source: src_index, target: dst_index, type: 'c'}); 193 | } 194 | 195 | for (domain = 0; domain < topos.length; domain++) { 196 | 197 | var layer = svg.append('image') 198 | .attr('xlink:href', 'floor.png') 199 | .attr('x', layerCenter[domain]['x'] - 750) 200 | .attr('y', layerCenter[domain]['y'] - 110) 201 | .attr('width', 1500) 202 | .attr('height', 220) 203 | } 204 | 205 | force.nodes(d3_nodes) 206 | .links(d3_links) 207 | .start(); 208 | force.on('tick', forceTick); 209 | 210 | allSVGElem.links = svg.selectAll('.link') 211 | .data(d3_links) 212 | .enter() 213 | .append('line') 214 | .attr('class', 'link') 215 | .style("stroke-width", function (d) { 216 | if (d.type === 'c') { 217 | return 5; 218 | } else { 219 | return 3; 220 | } 221 | }) 222 | .style("stroke", function (d) { 223 | if (d.type === 'h') { 224 | // host to switch link 225 | return '#F00'; 226 | } else if (d.type === 's') { 227 | // switch to switch link 228 | return '#00F'; 229 | } else { 230 | // cross domain link 231 | return '#0F0'; 232 | } 233 | }); 234 | 235 | allSVGElem.nodes = svg.selectAll('.node') 236 | .data(d3_nodes) 237 | .enter() 238 | .append('image') 239 | .attr('xlink:href', function (d) { 240 | if (d.type === 's') { 241 | return 'switch.png'; 242 | } else { 243 | return 'host.png'; 244 | } 245 | }) 246 | .attr('x', -20) 247 | .attr('y', -20) 248 | .attr('width', 40) 249 | .attr('height', 40) 250 | .attr('px', function (d) { return d.x; }) 251 | .attr('py', function (d) { return d.y; }) 252 | .attr('class', 'node') 253 | .on("dblclick", function(d) { d3.select(this).classed("fixed", d.fixed = true); }) 254 | .call(force.drag); 255 | 256 | 257 | } 258 | 259 | 260 | d3.json('data.json', loadData); 261 | -------------------------------------------------------------------------------- /D3Topo/MultiForce/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "domain0": { 3 | "switches": [{ 4 | "dpid": 1 5 | }], 6 | "hosts": [{ 7 | "port": 1, 8 | "mac": "4e:d2:db:e2:10:13", 9 | "dpid": 1 10 | }, { 11 | "port": 2, 12 | "mac": "6a:34:c8:67:07:d8", 13 | "dpid": 1 14 | }], 15 | "links": [] 16 | }, 17 | "domain1": { 18 | "switches": [{ 19 | "dpid": 1 20 | }, { 21 | "dpid": 2 22 | }], 23 | "hosts": [{ 24 | "port": 1, 25 | "mac": "00:00:00:00:00:01", 26 | "dpid": 1 27 | }, { 28 | "port": 1, 29 | "mac": "00:00:00:00:00:02", 30 | "dpid": 2 31 | }], 32 | "links": [{ 33 | "src": { 34 | "port": 2, 35 | "dpid": 1 36 | }, 37 | "dst": { 38 | "port": 2, 39 | "dpid": 2 40 | } 41 | }] 42 | }, 43 | "domain2": { 44 | "switches": [{ 45 | "dpid": 1 46 | }, { 47 | "dpid": 2 48 | }, { 49 | "dpid": 3 50 | }, { 51 | "dpid": 4 52 | }, { 53 | "dpid": 5 54 | }], 55 | "hosts": [{ 56 | "port": 1, 57 | "mac": "00:00:00:00:00:01", 58 | "dpid": 2 59 | }, { 60 | "port": 1, 61 | "mac": "00:00:00:00:00:02", 62 | "dpid": 4 63 | }], 64 | "links": [{ 65 | "src": { 66 | "port": 2, 67 | "dpid": 1 68 | }, 69 | "dst": { 70 | "port": 1, 71 | "dpid": 3 72 | } 73 | }, { 74 | "src": { 75 | "port": 2, 76 | "dpid": 2 77 | }, 78 | "dst": { 79 | "port": 2, 80 | "dpid": 3 81 | } 82 | }, { 83 | "src": { 84 | "port": 2, 85 | "dpid": 4 86 | }, 87 | "dst": { 88 | "port": 3, 89 | "dpid": 3 90 | } 91 | }, { 92 | "src": { 93 | "port": 2, 94 | "dpid": 5 95 | }, 96 | "dst": { 97 | "port": 4, 98 | "dpid": 3 99 | } 100 | }] 101 | }, 102 | "crossDomainLinks": [ 103 | {"src": {"domain": 0, "dpid": 1}, "dst": {"domain": 1, "dpid": 1}}, 104 | {"src": {"domain": 0, "dpid": 1}, "dst": {"domain": 1, "dpid": 2}}, 105 | {"src": {"domain": 1, "dpid": 1}, "dst": {"domain": 2, "dpid": 2}}, 106 | {"src": {"domain": 1, "dpid": 1}, "dst": {"domain": 2, "dpid": 3}}, 107 | {"src": {"domain": 1, "dpid": 2}, "dst": {"domain": 2, "dpid": 4}} 108 | ] 109 | } 110 | -------------------------------------------------------------------------------- /D3Topo/MultiForce/floor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yi-Tseng/SDN-Work/cb0e6d8680bfb625b7735089a19aac73fc262693/D3Topo/MultiForce/floor.png -------------------------------------------------------------------------------- /D3Topo/MultiForce/host.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yi-Tseng/SDN-Work/cb0e6d8680bfb625b7735089a19aac73fc262693/D3Topo/MultiForce/host.png -------------------------------------------------------------------------------- /D3Topo/MultiForce/host.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 124 | -------------------------------------------------------------------------------- /D3Topo/MultiForce/index.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /D3Topo/MultiForce/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } 49 | -------------------------------------------------------------------------------- /D3Topo/MultiForce/switch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yi-Tseng/SDN-Work/cb0e6d8680bfb625b7735089a19aac73fc262693/D3Topo/MultiForce/switch.png -------------------------------------------------------------------------------- /D3Topo/MultiForce/switch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 58 | -------------------------------------------------------------------------------- /D3Topo/README.md: -------------------------------------------------------------------------------- 1 | D3Topo 2 | ==== 3 | 4 | ### 用途/What is this?: 5 | 6 | 用於練習 d3.js 來呈現網路唾撲用 7 | 8 | For displaying network topology by using d3.js 9 | 10 | ### 用法/How to use?: 11 | 12 | 開啟 index.html 即可 13 | 14 | just open index.html 15 | -------------------------------------------------------------------------------- /FastFailover/README.md: -------------------------------------------------------------------------------- 1 | FastFailover 2 | ---- 3 | 4 | Use group table feature to handle link failure 5 | -------------------------------------------------------------------------------- /FastFailover/ff.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | from ryu.base import app_manager 3 | from ryu.controller import ofp_event 4 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 5 | from ryu.controller.handler import set_ev_cls 6 | from ryu.ofproto import ofproto_v1_3 7 | from ryu.lib.packet import packet 8 | from ryu.lib.packet import ethernet 9 | from ryu.lib.packet import ether_types 10 | from ryu.topology import api as ryu_api 11 | 12 | DEFAULT_FLOW_PRIORITY = 32767 13 | DEFAULT_BUCKET_WEIGHT = 10 14 | 15 | 16 | class FastFailover(app_manager.RyuApp): 17 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 18 | 19 | def __init__(self, *args, **kwargs): 20 | super(FastFailover, self).__init__() 21 | self.hosts = {} 22 | self.num_groups = 1 23 | self.mac_to_gid = {} 24 | self.gid_to_mac = {} 25 | self.default_group_installed = [] 26 | 27 | def add_flow(self, datapath, priority, match, actions): 28 | self.logger.info('add flow with dpid:%d, match: %s, actions: %s', datapath.id, match, actions) 29 | ofproto = datapath.ofproto 30 | parser = datapath.ofproto_parser 31 | 32 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, 33 | actions)] 34 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, 35 | match=match, instructions=inst) 36 | datapath.send_msg(mod) 37 | 38 | @set_ev_cls(ofp_event.EventOFPErrorMsg, MAIN_DISPATCHER) 39 | def error_msg_handler(self, ev): 40 | from ryu import utils 41 | msg = ev.msg 42 | self.logger.info('OFPErrorMsg received: type=0x%02x code=0x%02x message=%s', msg.type, msg.code, utils.hex_array(msg.data)) 43 | 44 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 45 | def switch_features_handler(self, ev): 46 | datapath = ev.msg.datapath 47 | ofproto = datapath.ofproto 48 | parser = datapath.ofproto_parser 49 | 50 | match = parser.OFPMatch() 51 | actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, 52 | ofproto.OFPCML_NO_BUFFER)] 53 | self.add_flow(datapath, 0, match, actions) 54 | 55 | # install default groups 56 | if datapath.id in self.default_group_installed: 57 | return 58 | 59 | for gid in range(1, 255): 60 | gmod = parser.OFPGroupMod(datapath, ofproto.OFPGC_ADD, ofproto.OFPGT_FF, gid, []) 61 | datapath.send_msg(gmod) 62 | 63 | self.default_group_installed.append(datapath.id) 64 | 65 | def get_dp(self, dpid): 66 | switches = ryu_api.get_all_switch(self) 67 | 68 | for switch in switches: 69 | if switch.dp.id == dpid: 70 | return switch.dp 71 | 72 | return None 73 | 74 | def get_nx_graph(self): 75 | links = ryu_api.get_all_link(self) 76 | g = nx.DiGraph() 77 | 78 | for link in links: 79 | src = link.src 80 | dst = link.dst 81 | g.add_edge(src.dpid, dst.dpid, src_port=src.port_no, dst_port=dst.port_no) 82 | 83 | return g 84 | 85 | def is_edge_port(self, dpid, port_no): 86 | links = ryu_api.get_all_link(self) 87 | 88 | for link in links: 89 | if link.src.dpid == dpid and link.src.port_no == port_no: 90 | return False 91 | 92 | return True 93 | 94 | def install_group_to_all_dp(self, gid): 95 | switches = ryu_api.get_all_switch(self) 96 | 97 | if not gid: 98 | return 99 | 100 | for switch in switches: 101 | dp = switch.dp 102 | ofproto = dp.ofproto 103 | of_parser = dp.ofproto_parser 104 | gmod = of_parser.OFPGroupMod(dp, ofproto.OFPGC_ADD, ofproto.OFPGT_FF, gid, []) 105 | dp.send_msg(gmod) 106 | 107 | def modify_group_bucket_list(self, dp, gid, new_bucket_list): 108 | ofproto = dp.ofproto 109 | parser = dp.ofproto_parser 110 | gp_mod_msg = parser.OFPGroupMod(datapath, ofproto.OFPGC_ADD, ofproto.OFPGT_FF, group_id, new_bucket_list) 111 | 112 | 113 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 114 | def packet_in_handler(self, ev): 115 | ''' 116 | L2 shortest path routing 117 | ''' 118 | msg = ev.msg 119 | dp = msg.datapath 120 | dpid = dp.id 121 | ofproto = dp.ofproto 122 | of_parser = dp.ofproto_parser 123 | in_port = msg.match['in_port'] 124 | 125 | pkt = packet.Packet(msg.data) 126 | eth = pkt.get_protocol(ethernet.ethernet) 127 | 128 | if eth.ethertype == ether_types.ETH_TYPE_LLDP: 129 | # ignore lldp packet 130 | return 131 | 132 | src = eth.src 133 | dst = eth.dst 134 | 135 | _dbg_hosts = ['00:00:00:00:00:01', '00:00:00:00:00:02'] 136 | if src not in _dbg_hosts or dst not in _dbg_hosts: 137 | return 138 | 139 | self.logger.info("%s -> %s", src, dst) 140 | if self.is_edge_port(dpid, in_port): 141 | # add host record 142 | self.hosts[src] = (dpid, in_port) 143 | 144 | if src not in self.mac_to_gid: 145 | self.mac_to_gid[src] = self.num_groups 146 | self.gid_to_mac[self.num_groups] = src 147 | gid = self.num_groups 148 | self.logger.info("mac: %s, gid: %d", src, gid) 149 | self.num_groups += 1 150 | 151 | 152 | if src in self.mac_to_gid: 153 | # add a group with empty bucket 154 | gid = self.mac_to_gid[src] 155 | match = of_parser.OFPMatch(eth_dst=src) 156 | actions = [of_parser.OFPActionGroup(group_id=gid)] 157 | self.add_flow(dp, DEFAULT_FLOW_PRIORITY, match, actions) 158 | 159 | if dst not in self.hosts: 160 | # can't find host, drop it 161 | return 162 | 163 | dst_dpid, dst_port = self.hosts[dst] 164 | 165 | dst_gid = self.mac_to_gid.get(dst, -1) 166 | 167 | if dst_gid == -1: 168 | # can't find gid, drop 169 | return 170 | 171 | g = self.get_nx_graph() 172 | all_paths = nx.all_shortest_paths(g, dpid, dst_dpid) 173 | 174 | ''' 175 | to_install: 176 | for example, topology like 177 | /2\ /5\ 178 | h1-1 4 7-h2 179 | \3/ \6/ 180 | all path from h1 to h2 181 | [1, 2, 4, 5, 7] 182 | [1, 2, 4, 6, 7] 183 | [1, 3, 4, 5, 7] 184 | [1, 3, 4, 6, 7] 185 | data of to_install will be: 186 | { 187 | 1: [2, 3], 188 | 2: [4], 189 | 3: [4], 190 | 4: [5, 6], 191 | 5: [7], 192 | 6: [7], 193 | 7: [] 194 | } 195 | ''' 196 | to_install = {} 197 | 198 | for path in all_paths: 199 | for _i in range(0, len(path)): 200 | to_install.setdefault(path[_i], set()) 201 | 202 | if path[_i] == dst_dpid: 203 | continue 204 | 205 | to_install[path[_i]].add(path[_i + 1]) 206 | 207 | self.logger.info(to_install) 208 | dst_match = of_parser.OFPMatch(eth_dst=dst) 209 | dst_actions = [of_parser.OFPActionGroup(group_id=dst_gid)] 210 | # gnerate all buckets and actions and install to switch 211 | for _dpid, _next_dpid_list in to_install.items(): 212 | _dp = self.get_dp(_dpid) 213 | 214 | if not _dp: 215 | continue 216 | 217 | if _dpid == dst_dpid: 218 | actions = [of_parser.OFPActionOutput(port=dst_port)] 219 | buckets = [of_parser.OFPBucket(DEFAULT_BUCKET_WEIGHT, dst_port, dst_gid, actions)] 220 | gmod = of_parser.OFPGroupMod(dp, ofproto.OFPGC_MODIFY, ofproto.OFPGT_FF, dst_gid, buckets) 221 | _dp.send_msg(gmod) 222 | self.add_flow(_dp, DEFAULT_FLOW_PRIORITY, dst_match, dst_actions) 223 | 224 | else: 225 | buckets = [] 226 | 227 | for _next_dpid in _next_dpid_list: 228 | _out_port = g.edge[_dpid][_next_dpid]['src_port'] 229 | actions = [of_parser.OFPActionOutput(port=_out_port)] 230 | buckets.append(of_parser.OFPBucket(DEFAULT_BUCKET_WEIGHT, _out_port, dst_gid, actions)) 231 | 232 | gmod = of_parser.OFPGroupMod(dp, ofproto.OFPGC_MODIFY, ofproto.OFPGT_FF, dst_gid, buckets) 233 | _dp.send_msg(gmod) 234 | self.add_flow(_dp, DEFAULT_FLOW_PRIORITY, dst_match, dst_actions) 235 | 236 | -------------------------------------------------------------------------------- /FastFailover/topology.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ''' 4 | /2\ /5\ 5 | h1-1 4 7-h2 6 | \3/ \6/ 7 | ''' 8 | 9 | 10 | from mininet.cli import CLI 11 | from mininet.node import Link 12 | from mininet.net import Mininet 13 | from mininet.node import RemoteController 14 | from mininet.term import makeTerm 15 | from functools import partial 16 | 17 | 18 | def ofp_version(switch, protocols): 19 | protocols_str = ','.join(protocols) 20 | command = 'ovs-vsctl set Bridge %s protocols=%s' % (switch, protocols) 21 | switch.cmd(command.split(' ')) 22 | 23 | if '__main__' == __name__: 24 | net = Mininet(controller=RemoteController, autoStaticArp=True, autoSetMacs=True) 25 | c0 = net.addController('c0') 26 | 27 | h1 = net.addHost('h1') 28 | h2 = net.addHost('h2') 29 | 30 | s1 = net.addSwitch('s1') 31 | s2 = net.addSwitch('s2') 32 | s3 = net.addSwitch('s3') 33 | s4 = net.addSwitch('s4') 34 | s5 = net.addSwitch('s5') 35 | s6 = net.addSwitch('s6') 36 | s7 = net.addSwitch('s7') 37 | 38 | net.addLink(h1, s1) 39 | 40 | net.addLink(s1, s2) 41 | net.addLink(s1, s3) 42 | 43 | net.addLink(s2, s4) 44 | net.addLink(s3, s4) 45 | 46 | net.addLink(s4, s5) 47 | net.addLink(s4, s6) 48 | 49 | net.addLink(s5, s7) 50 | net.addLink(s6, s7) 51 | 52 | net.addLink(s7, h2) 53 | 54 | net.build() 55 | c0.start() 56 | s1.start([c0]) 57 | s2.start([c0]) 58 | s3.start([c0]) 59 | s4.start([c0]) 60 | s5.start([c0]) 61 | s6.start([c0]) 62 | s7.start([c0]) 63 | 64 | 65 | ofp_version(s1, ['OpenFlow13']) 66 | ofp_version(s2, ['OpenFlow13']) 67 | ofp_version(s3, ['OpenFlow13']) 68 | ofp_version(s4, ['OpenFlow13']) 69 | ofp_version(s5, ['OpenFlow13']) 70 | ofp_version(s6, ['OpenFlow13']) 71 | ofp_version(s7, ['OpenFlow13']) 72 | 73 | CLI(net) 74 | net.stop() 75 | -------------------------------------------------------------------------------- /GetSwitches/GetSwitches.py: -------------------------------------------------------------------------------- 1 | from ryu.base import app_manager 2 | from ryu.controller import ofp_event 3 | from ryu.controller.handler import MAIN_DISPATCHER 4 | from ryu.controller.handler import set_ev_cls 5 | from ryu.controller import dpset 6 | from ryu.topology import api 7 | from ryu.lib.packet import arp 8 | from ryu.ofproto import ofproto_v1_3 9 | 10 | 11 | class GetSwitches(app_manager.RyuApp): 12 | switches = [] 13 | 14 | def __init__(self, *args, **kwargs): 15 | super(GetSwitches, self).__init__(*args, **kwargs) 16 | 17 | @set_ev_cls(dpset.EventDP, MAIN_DISPATCHER) 18 | def datapath_change_handler(self, ev): 19 | 20 | if ev.enter: 21 | print "Datapath entered, id %s" % ev.dp.id 22 | switch = api.get_switch(self, ev.dp.id)[0] 23 | self.switches.append(switch) 24 | ports = switch.ports 25 | 26 | print "Switch : %s" % switch 27 | print "Ports :" 28 | for port in ports: 29 | print port.to_dict() 30 | 31 | print "Links :" 32 | links = api.get_link(self, ev.dp.id) 33 | for link in links: 34 | print link.to_dict() 35 | -------------------------------------------------------------------------------- /GetSwitches/README.md: -------------------------------------------------------------------------------- 1 | GetSwitches 2 | ==== 3 | 4 | ### 用途/What is this?: 5 | 6 | 用於顯示每一個 Switch 的相關資訊以及跟他有關的 Link 資料 7 | 8 | Display every switch data and link data which connected to it 9 | 10 | ### 用法/How to use?: 11 | 12 | ``` shell 13 | ryu-manager --observe-links GetSwitches 14 | ``` 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Yi Tseng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /ModifyRyuBehavior/app.py: -------------------------------------------------------------------------------- 1 | from ryu.base import app_manager 2 | from ryu.controller import ofp_event 3 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 4 | from ryu.controller.handler import set_ev_cls 5 | from ryu.ofproto import ofproto_v1_0 6 | from ryu.lib.packet import packet 7 | from ryu.lib.packet import ethernet 8 | 9 | 10 | def warper(func): 11 | 12 | def new_handler(msg): 13 | print("Msg : {}".format(msg)) 14 | func(msg) 15 | 16 | return new_handler 17 | 18 | 19 | class ChangeSendMsg(app_manager.RyuApp): 20 | OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION] 21 | 22 | def __init__(self, *args, **kwargs): 23 | super(ChangeSendMsg, self).__init__(*args, **kwargs) 24 | 25 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 26 | def switch_features_handler(self, ev): 27 | datapath = ev.msg.datapath 28 | datapath.send_msg = warper(datapath.send_msg) 29 | 30 | -------------------------------------------------------------------------------- /MultiControl/ModStatusApp.py: -------------------------------------------------------------------------------- 1 | from ryu.base import app_manager 2 | from ryu.controller import ofp_event 3 | from ryu.controller.handler import MAIN_DISPATCHER 4 | from ryu.controller.handler import HANDSHAKE_DISPATCHER 5 | from ryu.controller.handler import CONFIG_DISPATCHER 6 | from ryu.controller.handler import set_ev_cls 7 | from ryu.lib.packet import packet 8 | from ryu.controller import dpset 9 | from ryu.ofproto import ofproto_v1_3 10 | 11 | import random 12 | 13 | 14 | class ModStatusApp(app_manager.RyuApp): 15 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 16 | 17 | def __init__(self, *args, **kwargs): 18 | super(ModStatusApp, self).__init__(*args, **kwargs) 19 | self.gen_id = 0 20 | self.role_string_list = ['nochange', 'equal', 'master', 'slave', 'unknown'] 21 | 22 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 23 | def on_packet_in(self, ev): 24 | msg = ev.msg 25 | pkt = packet.Packet(msg.data) 26 | 27 | print 'get a packet: %s' % (pkt) 28 | 29 | @set_ev_cls(dpset.EventDP, MAIN_DISPATCHER) 30 | def on_dp_change(self, ev): 31 | 32 | if ev.enter: 33 | dp = ev.dp 34 | dpid = dp.id 35 | ofp = dp.ofproto 36 | ofp_parser = dp.ofproto_parser 37 | 38 | print 'dp entered, id is %s' % (dpid) 39 | self.send_role_request(dp, ofp.OFPCR_ROLE_EQUAL, self.gen_id) 40 | 41 | @set_ev_cls(ofp_event.EventOFPErrorMsg, 42 | [HANDSHAKE_DISPATCHER, CONFIG_DISPATCHER, MAIN_DISPATCHER]) 43 | def on_error_msg(self, ev): 44 | msg = ev.msg 45 | print 'receive a error message: %s' % (msg) 46 | 47 | @set_ev_cls(ofp_event.EventOFPRoleReply, MAIN_DISPATCHER) 48 | def on_role_reply(self, ev): 49 | msg = ev.msg 50 | dp = msg.datapath 51 | ofp = dp.ofproto 52 | role = msg.role 53 | 54 | # unknown role 55 | if role < 0 or role > 3: 56 | role = 4 57 | print '' 58 | print 'get a role reply: %s, generation: %d' % (self.role_string_list[role], msg.generation_id) 59 | 60 | # generate new generation id 61 | self.gen_id = random.randint(0, 10000) 62 | 63 | if role == ofp.OFPCR_ROLE_EQUAL: 64 | print 'now is equal, change to master' 65 | self.send_role_request(dp, ofp.OFPCR_ROLE_MASTER, self.gen_id) 66 | elif role == ofp.OFPCR_ROLE_MASTER: 67 | print 'now is master, change to slave' 68 | self.send_role_request(dp, ofp.OFPCR_ROLE_SLAVE, self.gen_id) 69 | elif role == ofp.OFPCR_ROLE_SLAVE: 70 | print 'now is slave, change to equal' 71 | self.send_role_request(dp, ofp.OFPCR_ROLE_EQUAL, self.gen_id) 72 | print '' 73 | 74 | def send_role_request(self, datapath, role, gen_id): 75 | ofp_parser = datapath.ofproto_parser 76 | print 'send a role change request' 77 | print 'role: %s, gen_id: %d' % (self.role_string_list[role], gen_id) 78 | msg = ofp_parser.OFPRoleRequest(datapath, role, gen_id) 79 | datapath.send_msg(msg) 80 | -------------------------------------------------------------------------------- /MultiControl/hierarchy/README.md: -------------------------------------------------------------------------------- 1 | Hierarchy SDN routing control for multiple domain 2 | ---- 3 | 4 | This project is the final project for SDN course in NCTU 5 | 6 | Author: 7 | 8 | Tseng Yi(a86487817[at]gmail.com) 9 | 10 | naoh (kennynaoh[at]gmail.com) 11 | 12 | ## How to use with test environment (mininet): 13 | 14 | 1. Start global controller 15 | ``` 16 | $ python global.py 17 | ``` 18 | 19 | 2. Modify global ip address in local controller (local.py#L31) 20 | ```python 21 | self.local_lib.start_serve('127.0.0.1', 10807) 22 | ``` 23 | 24 | 3. Start local controllers (example is 3), I suggest start from different bash session. 25 | ``` 26 | $ run1.sh # this start controller with port 6633 27 | $ run2.sh # port 6634 28 | $ run3.sh # port 6635 29 | ``` 30 | 31 | 4. Modify controller ip addresses from mininet script (test_net.py#L17-L19) 32 | ```python 33 | c0 = RemoteController('c0', '10.10.10.10', 6633) 34 | c1 = RemoteController('c1', '10.10.10.10', 6634) 35 | c2 = RemoteController('c2', '10.10.10.10', 6635) 36 | ``` 37 | 38 | 5. Start mininet 39 | ``` 40 | # python test_net.py 41 | ``` 42 | 43 | -------------------------------------------------------------------------------- /MultiControl/hierarchy/api.md: -------------------------------------------------------------------------------- 1 | API define 2 | 3 | Global to local 4 | ---- 5 | - Set agent id 6 | 7 | `` 8 | { 9 | cmd: "set_agent_id", 10 | agent_id: 1 11 | } 12 | 13 | 14 | - Ask host(cmd = 'ask_host') 15 | 16 | `` 17 | { 18 | "cmd": "ask_host", 19 | host: "00:00:00:00:00:01", 20 | } 21 | `` 22 | 23 | - Ask dpid 24 | 25 | `` 26 | { 27 | cmd: 'ask_dpid', 28 | dpid: 1 29 | } 30 | `` 31 | 32 | - Route result 33 | 34 | `` 35 | { 36 | cmd: "route_result", 37 | dpid: 1, 38 | port: 1, 39 | host: '00:00:00:00:00:01' 40 | } 41 | `` 42 | 43 | P.S. if dpid = -1, global route query failed 44 | 45 | 46 | Local to global 47 | ---- 48 | - Response host(cmd = 'reponse_host') 49 | 50 | `` 51 | { 52 | cmd: "response_host", 53 | host: "00:00:00:00:00:01" 54 | } 55 | `` 56 | 57 | - Get route 58 | 59 | `` 60 | { 61 | cmd: "get_route", 62 | dst:"00:00:00:00:00:03" 63 | } 64 | `` 65 | - Add cross domain link 66 | 67 | `` 68 | { 69 | cmd: "add_cross_domain_link", 70 | src: {dpid: 4, port: 3}, # this should from local controller 71 | dst: {dpid: 1, port: 1} 72 | } 73 | `` 74 | 75 | - Response dpid 76 | 77 | `` 78 | { 79 | cmd: 'response_dpid', 80 | dpid: 1, 81 | } 82 | `` 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /MultiControl/hierarchy/global.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import logging 4 | import contextlib 5 | import json 6 | import networkx as nx 7 | from ryu.lib import hub 8 | from ryu.lib.hub import StreamServer 9 | 10 | logging.basicConfig() 11 | LOG = logging.getLogger(__name__) 12 | LOG.setLevel(logging.INFO) 13 | 14 | MAX_AGENTS = 1024 15 | 16 | 17 | class GlobalController(object): 18 | 19 | def __init__(self, *args): 20 | super(GlobalController, self).__init__() 21 | self.agents = {} 22 | self.server = StreamServer(('0.0.0.0', 10807), self._connection_factory) 23 | self.cross_domain_links = [] # ex: [{src: {dpid: 4, port: 3}, dst: {dpid: 1, port: 1} }] 24 | self.hosts = {} # host -> domain number 25 | 26 | def _connection_factory(self, socket, address): 27 | print 'connected socket:%s address:%s' % (socket, address) 28 | 29 | with contextlib.closing(GlobalAgent(socket, address)) as agent: 30 | agent.global_ctrn = self 31 | agent_id = len(self.agents) 32 | 33 | while agent_id in self.agents: 34 | agent_id = (agent_id + 1) % MAX_AGENTS 35 | 36 | agent.set_agent_id(agent_id) 37 | self.agents[agent_id] = agent 38 | agent.serve() 39 | LOG.info('Remove agent %d', agent_id) 40 | del self.agents[agent_id] 41 | 42 | def start(self): 43 | 44 | LOG.info('Waiting for connection.....') 45 | self.server.serve_forever() 46 | 47 | def print_agents_status(self): 48 | 49 | for agent in self.agents: 50 | print "%s:%s" % (agent.address, agent.__str__(), ) 51 | 52 | def add_cross_domain_link(self, src, dst, agent_id): 53 | src['agent_id'] = agent_id 54 | link = {'src': src, 'dst': dst} 55 | link_rev = {'src': dst, 'dst': src} 56 | 57 | # ask for dpid 58 | msg = json.dumps({ 59 | 'cmd': 'ask_dpid', 60 | 'dpid': dst['dpid'] 61 | }) 62 | self.broad_cast(msg) 63 | 64 | for _link in self.cross_domain_links: 65 | 66 | if _link['src']['dpid'] == src['dpid'] and \ 67 | _link['src']['port'] == src['port']: 68 | _link['src']['agent_id'] = agent_id 69 | break 70 | 71 | if _link['dst']['dpid'] == src['dpid'] and \ 72 | _link['dst']['port'] == src['port']: 73 | _link['dst']['agent_id'] = agent_id 74 | break 75 | 76 | else: 77 | self.cross_domain_links.append(link) 78 | self.cross_domain_links.append(link_rev) 79 | 80 | def broad_cast(self, msg): 81 | 82 | for agent in self.agents.itervalues(): 83 | agent.send(msg) 84 | 85 | def get_route(self, dst_host, agent): 86 | ''' 87 | dst_host: mac address 88 | agent: source domain(lc) agent 89 | ''' 90 | 91 | if dst_host not in self.hosts: 92 | msg = json.dumps({ 93 | 'cmd': 'route_result', 94 | 'dpid': -1, 95 | 'port': -1, 96 | 'host': dst_host 97 | }) 98 | LOG.debug('Unknown host %s', dst_host) 99 | agent.send(msg) 100 | return 101 | 102 | # get source and destination 103 | # from a? to a? (cross doamin) 104 | dst_agent = self.hosts[dst_host] 105 | src_agent_id = agent.agent_id 106 | src = 'a%d' % (src_agent_id, ) 107 | dst = 'a%d' % (dst_agent.agent_id, ) 108 | 109 | # generate link between agents 110 | links = self._get_agent_links() 111 | 112 | # generate graph 113 | g = nx.Graph() 114 | g.add_edges_from(links) 115 | 116 | path = [] 117 | if nx.has_path(g, src, dst): 118 | path = nx.shortest_path(g, src, dst) 119 | 120 | # we only need first two element and get output port 121 | glink = self._get_agent_link(path[0], path[1]) 122 | 123 | # find output dpid and port 124 | output_dpid = glink['src']['dpid'] 125 | output_port = glink['src']['port'] 126 | 127 | # send route result 128 | msg = json.dumps({ 129 | 'cmd': 'route_result', 130 | 'dpid': output_dpid, 131 | 'port': output_port, 132 | 'host': dst_host 133 | }) 134 | LOG.debug('send route result to agent %d, %d:%d %s', 135 | agent.agent_id, output_dpid, output_port, dst_host) 136 | agent.send(msg) 137 | 138 | def _get_agent_link(self, src, dst): 139 | # convert a? to ? 140 | src_agent_id = int(src[1:]) 141 | dst_agent_id = int(dst[1:]) 142 | 143 | for glink in self.cross_domain_links: 144 | src = glink['src'] 145 | dst = glink['dst'] 146 | if src['agent_id'] == src_agent_id and \ 147 | dst['agent_id'] == dst_agent_id: 148 | return glink 149 | 150 | return None 151 | 152 | def _get_agent_links(self): 153 | ''' 154 | link: ('a1', 'a2') 155 | ''' 156 | links = [] 157 | 158 | for glink in self.cross_domain_links: 159 | src = glink['src'] 160 | dst = glink['dst'] 161 | 162 | if 'agent_id' in src and 'agent_id' in dst: 163 | src = 'a%d' % (src['agent_id'], ) 164 | dst = 'a%d' % (dst['agent_id'], ) 165 | links.append((src, dst)) 166 | 167 | return links 168 | 169 | def response_host(self, host, agent): 170 | ''' 171 | actually, it use for get route 172 | ''' 173 | self.hosts[host] = agent 174 | LOG.debug('Add host %s to self.hosts', host) 175 | 176 | def response_dpid(self, dpid, agent_id): 177 | 178 | for link in self.cross_domain_links: 179 | 180 | if link['src']['dpid'] == dpid: 181 | link['src']['agent_id'] = agent_id 182 | 183 | if link['dst']['dpid'] == dpid: 184 | link['dst']['agent_id'] = agent_id 185 | 186 | 187 | class GlobalAgent(object): 188 | 189 | def __init__(self, socket, address): 190 | super(GlobalAgent, self).__init__() 191 | self.socket = socket 192 | self.address = address 193 | self.send_q = hub.Queue(32) 194 | self.is_active = True 195 | self.global_ctrn = None 196 | self.agent_id = -1 197 | 198 | def set_agent_id(self, agent_id): 199 | self.agent_id = agent_id 200 | msg = json.dumps({ 201 | 'cmd': 'set_agent_id', 202 | 'agent_id': agent_id 203 | }) 204 | self.send(msg) 205 | 206 | def send(self, msg): 207 | 208 | if self.send_q: 209 | self.send_q.put(msg) 210 | 211 | def send_loop(self): 212 | 213 | try: 214 | 215 | while self.is_active: 216 | buf = self.send_q.get() 217 | self.socket.sendall(buf) 218 | hub.sleep(0.1) 219 | 220 | finally: 221 | q = self.send_q 222 | self.send_q = None 223 | 224 | try: 225 | 226 | while q.get(block=False): 227 | pass 228 | 229 | except hub.QueueEmpty: 230 | pass 231 | 232 | def recv_loop(self): 233 | 234 | while self.is_active: 235 | try: 236 | _buf = self.socket.recv(128) 237 | 238 | if len(_buf) == 0: 239 | LOG.info('connection fail, close') 240 | self.is_active = False 241 | break 242 | 243 | while '\n' != _buf[-1]: 244 | _buf += self.socket.recv(128) 245 | 246 | bufs = _buf.split('\n') 247 | 248 | for buf in bufs: 249 | 250 | if len(buf) == 0: 251 | continue 252 | msg = json.loads(buf) 253 | LOG.debug('receive : %s', msg) 254 | if msg['cmd'] == 'add_cross_domain_link': 255 | LOG.debug('receive cross domain link message') 256 | src = msg['src'] 257 | dst = msg['dst'] 258 | LOG.debug('src: %d, dst: %d', src, dst) 259 | self.global_ctrn.add_cross_domain_link(src, dst, self.agent_id) 260 | 261 | elif msg['cmd'] == 'response_host': 262 | host = msg['host'] 263 | self.global_ctrn.response_host(host, self) 264 | 265 | elif msg['cmd'] == 'get_route': 266 | dst_host = msg['dst'] 267 | self.global_ctrn.get_route(dst_host, self) 268 | 269 | elif msg['cmd'] == 'response_dpid': 270 | dpid = msg['dpid'] 271 | self.global_ctrn.response_dpid(dpid, self.agent_id) 272 | 273 | hub.sleep(0.1) 274 | except ValueError: 275 | LOG.warning('Value error for %s, len: %d', buf, len(buf)) 276 | 277 | def serve(self): 278 | thr = hub.spawn(self.send_loop) 279 | thr2 = hub.spawn(self.recv_loop) 280 | hub.joinall([thr, thr2]) 281 | 282 | def have_host(self, mac='00:00:00:00:00:00', ip='0.0.0.0'): 283 | ''' 284 | for cache 285 | ''' 286 | pass 287 | 288 | def close(self): 289 | self.is_active = False 290 | self.socket.close() 291 | 292 | 293 | # main function of global controller 294 | def main(): 295 | GlobalController().start() 296 | 297 | if __name__ == '__main__': 298 | main() 299 | -------------------------------------------------------------------------------- /MultiControl/hierarchy/local.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import logging 3 | import networkx as nx 4 | from ryu.base import app_manager 5 | from ryu.ofproto import ofproto_v1_3 6 | from ryu.topology import api 7 | from ryu.topology.switches import LLDPPacket 8 | from ryu.lib.packet import packet, ethernet 9 | from ryu.controller import ofp_event 10 | from ryu.controller.handler import set_ev_cls 11 | from ryu.controller.handler import MAIN_DISPATCHER 12 | 13 | import local_lib 14 | 15 | LOG = logging.getLogger(__name__) 16 | OFPPC_NO_FLOOD = 1 << 4 17 | mDNS = ['33:33:00:00:00:fb', '01:00:5E:00:00:FB'] 18 | multicast_list = ["33:33:00:00:00:%02X" % x for x in range(0, 256)] 19 | 20 | 21 | class LocalControllerApp(app_manager.RyuApp): 22 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 23 | _CONTEXTS = { 24 | 'local_lib': local_lib.LocalControllerLib 25 | } 26 | 27 | def __init__(self, *args, **kwargs): 28 | super(LocalControllerApp, self).__init__(*args, **kwargs) 29 | self.local_lib = kwargs['local_lib'] 30 | # self.local_lib = local_lib.LocalControllerLib('127.0.0.1', 10807) 31 | self.local_lib.start_serve('127.0.0.1', 10807) 32 | self.global_port = {} 33 | self.route_list = [] 34 | self.graph = None 35 | 36 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 37 | def packet_in_handler(self, ev): 38 | msg = ev.msg 39 | datapath = msg.datapath 40 | 41 | try: 42 | # src: from other switch 43 | src_dpid, src_port_no = LLDPPacket.lldp_parse(msg.data) 44 | # dst: this switch 45 | dst_dpid, dst_port_no = datapath.id, msg.match['in_port'] 46 | 47 | if src_dpid > 1024 or dst_dpid > 1024: 48 | # hack: ignote illegal switch 49 | return 50 | 51 | switch = api.get_switch(self, src_dpid) 52 | 53 | # not this topology switch 54 | if len(switch) != 0: 55 | return 56 | 57 | # send cross domain link add 58 | self.local_lib.send_cross_domain_link(dst_dpid, dst_port_no, src_dpid, src_port_no) 59 | 60 | # add global port 61 | self.global_port.setdefault(dst_dpid, []) 62 | self.global_port[dst_dpid].append(dst_port_no) 63 | 64 | return 65 | except LLDPPacket.LLDPUnknownFormat: 66 | # This handler can receive all the packtes which can be 67 | # not-LLDP packet. Ignore it silently 68 | pass 69 | 70 | # non-LLDP 71 | # local routing 72 | dpid = datapath.id 73 | pkt = packet.Packet(msg.data) 74 | eth = pkt.get_protocols(ethernet.ethernet)[0] 75 | src = eth.src 76 | dst = eth.dst 77 | 78 | if dst not in self.local_lib.hosts: 79 | # can't find host in local topology 80 | # ask global and let this msg queued 81 | if dst in mDNS or dst in multicast_list: 82 | return 83 | 84 | if dst == 'ff:ff:ff:ff:ff:ff': 85 | self._flood_packet(msg) 86 | return 87 | self.route_list.append((dst, msg)) 88 | self.local_lib.get_route(dst) 89 | return 90 | 91 | LOG.debug('Packet in, from %s, to %s', src, dst) 92 | # found in local, do local routing 93 | # two case: 94 | # 1. In same switch 95 | # 2. Not in same switch 96 | host = self.local_lib.hosts[dst] 97 | 98 | # host[0] -> dpid 99 | # host[1] -> port 100 | if host[0] == dpid: 101 | # same switch 102 | out_port = host[1] 103 | self._packet_out(msg, out_port) 104 | 105 | else: 106 | # not same switch 107 | # calculate path 108 | self._packet_out_to(msg, host[0], host[1]) 109 | 110 | def _get_out_port(self, src, dst): 111 | ''' 112 | get output port from src switch to dst switch 113 | return 114 | ''' 115 | links = api.get_all_link(self) 116 | 117 | for link in links: 118 | 119 | if link.src.dpid == src and \ 120 | link.dst.dpid == dst: 121 | return link.src.port_no 122 | 123 | def _flood_packet(self, msg): 124 | datapath = msg.datapath 125 | ofproto = datapath.ofproto 126 | parser = datapath.ofproto_parser 127 | in_port = msg.match['in_port'] 128 | 129 | data = None 130 | 131 | if msg.buffer_id == ofproto.OFP_NO_BUFFER: 132 | data = msg.data 133 | 134 | out_port = ofproto.OFPP_FLOOD 135 | actions = [parser.OFPActionOutput(out_port)] 136 | out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, 137 | in_port=in_port, actions=actions, 138 | data=data) 139 | datapath.send_msg(out) 140 | 141 | def _packet_out(self, msg, out_port): 142 | datapath = msg.datapath 143 | ofproto = datapath.ofproto 144 | parser = datapath.ofproto_parser 145 | in_port = msg.match['in_port'] 146 | 147 | data = None 148 | 149 | if msg.buffer_id == ofproto.OFP_NO_BUFFER: 150 | data = msg.data 151 | 152 | actions = [parser.OFPActionOutput(out_port)] 153 | out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, 154 | in_port=in_port, actions=actions, 155 | data=data) 156 | LOG.debug('packet out to %d:%d', datapath.id, out_port) 157 | datapath.send_msg(out) 158 | 159 | # add flow 160 | pkt = packet.Packet(msg.data) 161 | eth = pkt.get_protocols(ethernet.ethernet)[0] 162 | dst = eth.dst 163 | match = parser.OFPMatch(in_port=in_port, eth_dst=dst) 164 | 165 | if msg.buffer_id != ofproto.OFP_NO_BUFFER: 166 | self._add_flow(datapath, 1, match, actions, msg.buffer_id) 167 | else: 168 | self._add_flow(datapath, 1, match, actions) 169 | 170 | def _packet_out_to(self, msg, dst_dpid, dst_out_port): 171 | dp = msg.datapath 172 | src_dpid = dp.id 173 | 174 | # same dp 175 | if src_dpid == dst_dpid: 176 | self._packet_out(msg, dst_out_port) 177 | 178 | else: 179 | g = nx.Graph() 180 | links = api.get_all_link(self) 181 | 182 | for link in links: 183 | src = link.src.dpid 184 | dst = link.dst.dpid 185 | g.add_edge(src, dst) 186 | src = src_dpid 187 | dst = dst_dpid 188 | path = None 189 | 190 | if nx.has_path(g, src, dst): 191 | path = nx.shortest_path(g, src, dst) 192 | 193 | if path == None: 194 | return 195 | out_port = self._get_out_port(path[0], path[1]) 196 | self._packet_out(msg, out_port) 197 | 198 | def _add_flow(self, datapath, priority, match, actions, buffer_id=None): 199 | ofproto = datapath.ofproto 200 | parser = datapath.ofproto_parser 201 | 202 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, 203 | actions)] 204 | if buffer_id: 205 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, 206 | priority=priority, match=match, 207 | instructions=inst) 208 | else: 209 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, 210 | match=match, instructions=inst) 211 | datapath.send_msg(mod) 212 | 213 | @set_ev_cls(local_lib.EventRouteResult, MAIN_DISPATCHER) 214 | def route_result_handler(self, ev): 215 | dpid = ev.dpid 216 | port = ev.port 217 | host = ev.host 218 | LOG.info('Receive route result, %d:%d %s', dpid, port, host) 219 | remove_list = [] 220 | if dpid == -1: 221 | # global routing failed 222 | # do broad cast 223 | LOG.debug('Unknown hsot %s', host) 224 | for route_req in self.route_list: 225 | 226 | if route_req[0] != host: 227 | continue 228 | self._flood_packet(route_req[1]) 229 | remove_list.append(route_req) 230 | 231 | else: 232 | # global routing not failed 233 | for route_req in self.route_list: 234 | 235 | if route_req[0] != host: 236 | continue 237 | self._packet_out_to(route_req[1], dpid, port) 238 | remove_list.append(route_req) 239 | 240 | for remove_item in remove_list: 241 | 242 | try: 243 | index = self.route_list.index(remove_item) 244 | del self.route_list[index] 245 | 246 | except ValueError: 247 | pass 248 | 249 | @set_ev_cls(local_lib.EventAskDpid, MAIN_DISPATCHER) 250 | def ask_dpid_handler(self, ev): 251 | dpid = ev.dpid 252 | switch = api.get_switch(self, dpid) 253 | 254 | if len(switch) != 0: 255 | self.local_lib.response_dpid(dpid) 256 | 257 | @set_ev_cls(local_lib.EventAskHost, MAIN_DISPATCHER) 258 | def ask_host_handler(self, ev): 259 | host_mac = ev.host 260 | 261 | if host_mac in self.local_lib.hosts: 262 | self.local_lib.response_host(host_mac) 263 | 264 | @set_ev_cls(local_lib.EventHostDiscovery, MAIN_DISPATCHER) 265 | def host_discovery_handler(self, ev): 266 | LOG.debug('Discover host %s on %s, port %d', ev.host, ev.dpid, ev.port) 267 | -------------------------------------------------------------------------------- /MultiControl/hierarchy/local_lib.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import socket 3 | import logging 4 | import json 5 | 6 | 7 | from ryu.base import app_manager 8 | from ryu.controller import event 9 | from ryu.controller import ofp_event 10 | from ryu.controller.handler import set_ev_cls 11 | from ryu.controller.handler import MAIN_DISPATCHER 12 | from ryu.topology import api 13 | from ryu.lib.packet import packet, ethernet 14 | from ryu.ofproto import ofproto_v1_0, ofproto_v1_2, ofproto_v1_3 15 | from ryu.lib import hub 16 | from ryu.topology.switches import LLDPPacket 17 | 18 | LOG = logging.getLogger(__name__) 19 | 20 | 21 | class EventRouteResult(event.EventBase): 22 | 23 | def __init__(self, dpid, port, host): 24 | super(EventRouteResult, self).__init__() 25 | self.dpid = dpid 26 | self.port = port 27 | self.host = host 28 | 29 | 30 | class EventAskDpid(event.EventBase): 31 | 32 | def __init__(self, dpid): 33 | super(EventAskDpid, self).__init__() 34 | self.dpid = dpid 35 | 36 | 37 | class EventAskHost(event.EventBase): 38 | 39 | def __init__(self, host): 40 | ''' 41 | host: host mac address 42 | ''' 43 | super(EventAskHost, self).__init__() 44 | self.host = host 45 | 46 | 47 | class EventHostDiscovery(event.EventBase): 48 | 49 | def __init__(self, dpid, port, host): 50 | super(EventHostDiscovery, self).__init__() 51 | self.dpid = dpid 52 | self.port = port 53 | self.host = host 54 | 55 | LOG = logging.getLogger('local_lib') 56 | 57 | 58 | class LocalControllerLib(app_manager.RyuApp): 59 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 60 | 61 | def __init__(self, *args, **kwargs): 62 | super(LocalControllerLib, self).__init__(*args, **kwargs) 63 | self.name = 'local_lib' 64 | self.server_addr = None 65 | self.server_port = None 66 | self.is_active = False 67 | self.agent_id = -1 68 | self.send_q = hub.Queue(16) 69 | self.hosts = {} 70 | self.cross_domain_links = [] 71 | 72 | self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 73 | 74 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 75 | def _packet_in_handler(self, ev): 76 | try: 77 | msg = ev.msg 78 | LLDPPacket.lldp_parse(msg.data) 79 | return 80 | except LLDPPacket.LLDPUnknownFormat: 81 | # it's host 82 | dpid = msg.datapath.id 83 | port = -1 84 | 85 | if msg.datapath.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: 86 | port = msg.in_port 87 | elif msg.datapath.ofproto.OFP_VERSION >= ofproto_v1_2.OFP_VERSION: 88 | port = msg.match['in_port'] 89 | 90 | if port >= 1024: 91 | # hack: ignore some strange port 92 | return 93 | 94 | # ignore global host 95 | for link in self.cross_domain_links: 96 | 97 | if link['src']['dpid'] == dpid and \ 98 | link['src']['port'] == port: 99 | return 100 | 101 | # TODO, check again if host already in global port 102 | 103 | pkt = packet.Packet(msg.data) 104 | eth = pkt.get_protocols(ethernet.ethernet)[0] 105 | mac = eth.src 106 | 107 | if mac not in self.hosts and port != -1 and \ 108 | not self._host_exist_in_port(dpid, port) and \ 109 | not self._is_switch_port_to_port(dpid, port): 110 | LOG.debug('Add host %s to %d:%d', mac, dpid, port) 111 | self.hosts[mac] = (dpid, port) 112 | self.response_host(mac) 113 | ev = EventHostDiscovery(dpid, port, mac) 114 | self.send_event_to_observers(ev) 115 | 116 | def _is_switch_port_to_port(self, dpid, port): 117 | links = api.get_all_link(self) 118 | 119 | for link in links: 120 | 121 | if link.src.dpid == dpid and \ 122 | link.src.port_no == port: 123 | return True 124 | 125 | return False 126 | 127 | def _host_exist_in_port(self, dpid, port): 128 | 129 | for (d, p) in self.hosts.itervalues(): 130 | if d == dpid and p == port: 131 | return True 132 | 133 | return False 134 | 135 | def start_serve(self, server_addr, server_port): 136 | 137 | try: 138 | self.server_addr = server_addr 139 | self.server_port = server_port 140 | self.socket.connect((self.server_addr, self.server_port)) 141 | self.is_active = True 142 | hub.spawn(self._serve_loop) 143 | hub.spawn(self._send_loop) 144 | 145 | except Exception, ex: 146 | raise ex 147 | 148 | def _send_loop(self): 149 | 150 | try: 151 | 152 | while self.is_active: 153 | buf = self.send_q.get() 154 | buf += '\n' 155 | self.socket.sendall(buf) 156 | 157 | finally: 158 | q = self.send_q 159 | self.send_q = None 160 | 161 | try: 162 | 163 | while q.get(block=False): 164 | pass 165 | 166 | except hub.QueueEmpty: 167 | pass 168 | 169 | def _serve_loop(self): 170 | 171 | while self.is_active: 172 | buf = self.socket.recv(128) 173 | 174 | if len(buf) == 0: 175 | LOG.info('connection fail, close') 176 | self.is_active = False 177 | break 178 | LOG.debug('Receive: %s', buf) 179 | try: 180 | msg = json.loads(buf) 181 | except ValueError: 182 | LOG.warning('Error to decode to json: %s', buf) 183 | continue 184 | 185 | ev = None 186 | 187 | if msg['cmd'] == 'set_agent_id': 188 | self.agent_id = msg['agent_id'] 189 | 190 | elif msg['cmd'] == 'ask_host': 191 | host = msg['host'] 192 | ev = EventAskHost(host) 193 | 194 | elif msg['cmd'] == 'ask_dpid': 195 | dpid = msg['dpid'] 196 | ev = EventAskDpid(dpid) 197 | 198 | elif msg['cmd'] == 'route_result': 199 | dpid = msg['dpid'] 200 | port = msg['port'] 201 | host = msg['host'] 202 | ev = EventRouteResult(dpid, port, host) 203 | 204 | if ev != None: 205 | self.send_event_to_observers(ev) 206 | 207 | def send(self, msg): 208 | 209 | if self.send_q != None: 210 | self.send_q.put(msg) 211 | 212 | def send_cross_domain_link(self, local_dpid, local_port, out_dpid, out_port): 213 | 214 | link = { 215 | 'src': {'dpid': local_dpid, 'port': local_port}, 216 | 'dst': {'dpid': out_dpid, 'port': out_port} 217 | } 218 | 219 | if link in self.cross_domain_links: 220 | return 221 | self.cross_domain_links.append(link) 222 | msg = json.dumps({ 223 | 'cmd': 'add_cross_domain_link', 224 | 'src': {'dpid': local_dpid, 'port': local_port}, 225 | 'dst': {'dpid': out_dpid, 'port': out_port} 226 | }) 227 | LOG.info('Sending cross doamin link from %s:%d to %s:%d', local_dpid, local_port, out_dpid, out_port) 228 | self.send(msg) 229 | 230 | def response_host(self, host_mac): 231 | msg = json.dumps({ 232 | 'cmd': 'response_host', 233 | 'host': host_mac 234 | }) 235 | LOG.debug('Sending response host %s', host_mac) 236 | self.send(msg) 237 | 238 | def response_dpid(self, dpid): 239 | msg = json.dumps({ 240 | 'cmd': 'response_dpid', 241 | 'dpid': dpid 242 | }) 243 | LOG.debug('Sending response dpid %d', dpid) 244 | self.send(msg) 245 | 246 | def get_route(self, dst_mac): 247 | msg = json.dumps({ 248 | 'cmd': 'get_route', 249 | 'dst': dst_mac 250 | }) 251 | LOG.debug('Sending get route %s', dst_mac) 252 | self.send(msg) 253 | -------------------------------------------------------------------------------- /MultiControl/hierarchy/run1.sh: -------------------------------------------------------------------------------- 1 | ryu-manager --observe-links --ofp-tcp-listen-port 6633 local.py 2 | -------------------------------------------------------------------------------- /MultiControl/hierarchy/run2.sh: -------------------------------------------------------------------------------- 1 | ryu-manager --observe-links --ofp-tcp-listen-port 6634 local.py 2 | -------------------------------------------------------------------------------- /MultiControl/hierarchy/run3.sh: -------------------------------------------------------------------------------- 1 | ryu-manager --observe-links --ofp-tcp-listen-port 6635 local.py 2 | -------------------------------------------------------------------------------- /MultiControl/hierarchy/test_net.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from mininet.cli import CLI 4 | from mininet.net import Mininet 5 | from mininet.node import RemoteController, OVSSwitch 6 | 7 | 8 | def ofp_version(switch, protocols): 9 | protocols_str = ','.join(protocols) 10 | command = 'ovs-vsctl set Bridge %s protocols=%s' % (switch, protocols_str) 11 | switch.cmd(command.split(' ')) 12 | 13 | 14 | if '__main__' == __name__: 15 | net = Mininet(switch=OVSSwitch) 16 | controllers = [] 17 | c0 = RemoteController('c0', '10.10.10.10', 6633) 18 | c1 = RemoteController('c1', '10.10.10.10', 6634) 19 | c2 = RemoteController('c2', '10.10.10.10', 6635) 20 | controllers.append(c0) 21 | controllers.append(c1) 22 | controllers.append(c2) 23 | 24 | net.addController(c0) 25 | net.addController(c1) 26 | net.addController(c2) 27 | 28 | switches = [] 29 | for domain in range(0, 3): 30 | s1 = net.addSwitch('s%d' % (domain * 3 + 1, )) 31 | s2 = net.addSwitch('s%d' % (domain * 3 + 2, )) 32 | s3 = net.addSwitch('s%d' % (domain * 3 + 3, )) 33 | net.addLink(s1, s2) 34 | net.addLink(s1, s3) 35 | h1 = net.addHost('h%d' % (domain * 2 + 1, )) 36 | h2 = net.addHost('h%d' % (domain * 2 + 2, )) 37 | net.addLink(s2, h1) 38 | net.addLink(s3, h2) 39 | switches.append(s1) 40 | switches.append(s2) 41 | switches.append(s3) 42 | 43 | # link gw sw 44 | net.addLink(switches[0], switches[3]) 45 | net.addLink(switches[3], switches[6]) 46 | 47 | net.build() 48 | 49 | c0.start() 50 | c1.start() 51 | c2.start() 52 | 53 | for domain in range(0, 3): 54 | switches[domain * 3 + 0].start([controllers[domain]]) 55 | switches[domain * 3 + 1].start([controllers[domain]]) 56 | switches[domain * 3 + 2].start([controllers[domain]]) 57 | 58 | for sw in switches: 59 | ofp_version(sw, ['OpenFlow13']) 60 | 61 | CLI(net) 62 | 63 | net.stop() 64 | -------------------------------------------------------------------------------- /MultiControl/load_balance/global.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import logging 4 | import contextlib 5 | import liblb 6 | import pdb 7 | 8 | from ryu.lib import hub 9 | from ryu.lib.hub import StreamServer 10 | 11 | LOG = logging.getLogger('load_balance_global') 12 | 13 | 14 | # define role 15 | ROLE_SLAVE = 1 16 | ROLE_MASTER = 2 17 | 18 | # for calculate load score 19 | ALPHA = 0.5 20 | BETA = 1 - ALPHA 21 | 22 | # overload threshold(0~1) 23 | THRESHOLD = 0.7 24 | 25 | 26 | class GlobalController(object): 27 | 28 | def __init__(self, *args): 29 | super(GlobalController, self).__init__() 30 | self.agents = [] 31 | self.server = StreamServer(('0.0.0.0', 10807), self._connection_factory) 32 | 33 | def _find_free_agent(self, busy_agent): 34 | ''' 35 | find free agent 36 | rule: 37 | less loading score 38 | less switch control 39 | ''' 40 | free_agent = None 41 | agents = sorted(self.agents, key=lambda x: (x.load_score, x.controlled_switch)) 42 | 43 | for agent in agents: 44 | 45 | if agent.load_score < THRESHOLD and agent is not busy_agent: 46 | free_agent = agent 47 | break 48 | 49 | return free_agent 50 | 51 | def _balance_agents(self, busy_agent, free_agent): 52 | ''' 53 | move one switch from busy to free 54 | ''' 55 | # find one dpid for move 56 | for dpid in busy_agent.dpid_to_role: 57 | 58 | if busy_agent.dpid_to_role[dpid] == ROLE_MASTER and dpid in free_agent.dpid_to_role: 59 | # move it 60 | # TODO: not finish. 61 | pass 62 | 63 | def _serve_loop(self): 64 | # calculate load for each agent and send role to them. 65 | while True: 66 | 67 | self.print_agents_status() 68 | 69 | for agent in self.agents: 70 | 71 | if not agent.is_active: 72 | self.agents.remove(agent) 73 | 74 | # local controller is overloaded 75 | free_agent = None 76 | 77 | if agent.load_score > THRESHOLD: 78 | free_agent = self._find_free_agent(agent) 79 | 80 | if free_agent != None: 81 | # move some switch to free agent 82 | self._balance_agents(agent, free_agent) 83 | 84 | hub.sleep(1) 85 | 86 | def _connection_factory(self, socket, address): 87 | print('connected socket:%s address:%s', socket, address) 88 | 89 | with contextlib.closing(GlobalAgent(socket, address)) as agent: 90 | self.agents.append(agent) 91 | agent.serve() 92 | 93 | def start(self): 94 | thr = hub.spawn(self._serve_loop) 95 | print 'Waiting for connection.....' 96 | self.server.serve_forever() 97 | 98 | hub.joinall([thr]) 99 | 100 | def print_agents_status(self): 101 | 102 | for agent in self.agents: 103 | print "%s:%s" % (agent.address, agent.__str__(), ) 104 | 105 | 106 | class GlobalAgent(object): 107 | 108 | def __init__(self, socket, address): 109 | super(GlobalAgent, self).__init__() 110 | self.socket = socket 111 | self.address = address 112 | self.dpid_to_role = {} 113 | self.send_q = hub.Queue(32) 114 | self.cpu_load = 0 115 | self.mem_load = 0 116 | self.load_score = 0 117 | self.is_active = True 118 | 119 | def send(self, msg): 120 | 121 | if self.send_q: 122 | self.send_q.put(msg) 123 | 124 | def send_loop(self): 125 | 126 | try: 127 | 128 | while True: 129 | buf = self.send_q.get() 130 | self.socket.sendall(buf) 131 | 132 | finally: 133 | q = self.send_q 134 | self.send_q = None 135 | 136 | try: 137 | 138 | while q.get(block=False): 139 | pass 140 | 141 | except hub.QueueEmpty: 142 | pass 143 | 144 | def recv_loop(self): 145 | while True: 146 | header_data = self.socket.recv(liblb.HeaderStruct.size) 147 | header = liblb.HeaderStruct.unpack(header_data)[0] 148 | 149 | # add new dpid 150 | if header == 1: 151 | dp_data = self.socket.recv(liblb.DPStruct.size) 152 | dpid = liblb.DPStruct.unpack(dp_data)[0] 153 | 154 | if dpid not in self.dpid_to_role: 155 | self.dpid_to_role[dpid] = ROLE_SLAVE 156 | 157 | # update loading 158 | else: 159 | load_data = self.socket.recv(liblb.UtilStruct.size) 160 | self.cpu_load, self.mem_load = liblb.UtilStruct.unpack(load_data) 161 | self.load_score = ALPHA * self.cpu_load + BETA * self.mem_load 162 | 163 | def serve(self): 164 | thr = hub.spawn(self.send_loop) 165 | self.recv_loop() 166 | hub.joinall([thr]) 167 | 168 | def is_controling_dp(self, dpid): 169 | role = self.dpid_to_role.get(dpid, None) 170 | 171 | if role: 172 | return role == ROLE_MASTER 173 | 174 | return False 175 | 176 | def controlled_switch(self): 177 | ''' 178 | number of master control 179 | ''' 180 | result = 0 181 | 182 | for k in self.dpid_to_role: 183 | result += 1 if self.dpid_to_role[k] == ROLE_MASTER else 0 184 | 185 | return result 186 | 187 | def close(self): 188 | self.is_active = False 189 | self.socket.close() 190 | 191 | def __str__(self): 192 | return self.dpid_to_role.__str__() 193 | 194 | 195 | # main function of global controller 196 | def main(): 197 | # pdb.set_trace() 198 | GlobalController().start() 199 | 200 | if __name__ == '__main__': 201 | main() 202 | -------------------------------------------------------------------------------- /MultiControl/load_balance/lb.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import logging 3 | import random 4 | 5 | from ryu.base import app_manager 6 | from ryu.ofproto import ofproto_v1_3 7 | from ryu.topology import event 8 | from ryu.topology import api 9 | from ryu.controller.handler import set_ev_cls 10 | from ryu.controller.handler import MAIN_DISPATCHER 11 | from liblb import LBEventRoleChange, LoadBalancer 12 | 13 | LOG = logging.getLogger('load_balance_app') 14 | 15 | 16 | class LoadBalanceApp(app_manager.RyuApp): 17 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 18 | _CONTEXTS = {'load_balancer': LoadBalancer} 19 | 20 | def __init__(self, *args, **kwargs): 21 | super(LoadBalanceApp, self).__init__(*args, **kwargs) 22 | self.load_balancer = kwargs['load_balancer'] 23 | switches = api.get_all_switch(self) 24 | 25 | # change to slave first 26 | for switch in switches: 27 | dp = switch.dp 28 | ofp = dp.ofproto 29 | ofp_parser = dp.ofproto_parser 30 | role = ofp.OFPCR_ROLE_SLAVE 31 | # generate new generation id 32 | gen_id = random.randint(0, 10000) 33 | msg = ofp_parser.OFPRoleRequest(dp, role, gen_id) 34 | dp.send_msg(msg) 35 | 36 | self.load_balancer.start_serve() 37 | 38 | @set_ev_cls(event.EventSwitchEnter, MAIN_DISPATCHER) 39 | def _event_switch_enter_handler(self, ev): 40 | dpid = ev.dp.id 41 | self.load_balancer.add_dpid(dpid) 42 | 43 | @set_ev_cls(LBEventRoleChange, MAIN_DISPATCHER) 44 | def _role_change_handler(self, ev): 45 | dpid = ev.dpid 46 | role = ev.role 47 | # Role: 48 | # 1: master 49 | # 2: slave 50 | 51 | switch = api.get_switch(self, dpid) 52 | 53 | if switch != None: 54 | dp = switch.dp 55 | ofp = dp.ofproto 56 | ofp_parser = dp.ofproto_parser 57 | 58 | if role == 1: 59 | role = ofp.OFPCR_ROLE_MASTER 60 | 61 | else: 62 | role = ofp.OFPCR_ROLE_SLAVE 63 | # generate new generation id 64 | gen_id = random.randint(0, 10000) 65 | msg = ofp_parser.OFPRoleRequest(dp, role, gen_id) 66 | dp.send_msg(msg) 67 | 68 | app_manager.require_app('liblb.LoadBalancer') 69 | -------------------------------------------------------------------------------- /MultiControl/load_balance/liblb.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import socket 3 | import struct 4 | import psutil 5 | import logging 6 | 7 | from ryu.base import app_manager 8 | from ryu.ofproto import ofproto_v1_3 9 | from ryu.lib import hub 10 | from ryu.controller import event 11 | 12 | HeaderStruct = struct.Struct('!B') 13 | DPStruct = struct.Struct('!I') 14 | UtilStruct = struct.Struct('!BB') 15 | RoleStruct = struct.Struct('!IB') 16 | 17 | LOG = logging.getLogger('load_balance_lib') 18 | 19 | 20 | def get_cpu_ultilization(): 21 | return psutil.cpu_percent(interval=1) 22 | 23 | 24 | def get_ram_ultilization(): 25 | ram = psutil.virtual_memory() 26 | return ram.percent 27 | 28 | 29 | class LBEventRoleChange(event.EventBase): 30 | def __init__(self, dpid, role): 31 | super(LBEventRoleChange, self).__init__() 32 | self.dpid = dpid 33 | self.role = role 34 | 35 | 36 | class LoadBalancer(app_manager.RyuApp): 37 | 38 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 39 | 40 | def __init__(self, server_addr='127.0.0.1', server_port=10807, *args, **kwargs): 41 | super(LoadBalancer, self).__init__(*args, **kwargs) 42 | 43 | self.server_addr = kwargs['server_addr'] 44 | self.server_port = kwargs['server_port'] 45 | self.global_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 46 | 47 | def start_serve(self): 48 | try: 49 | self.global_socket.connect((self.server_addr, self.server_port)) 50 | hub.spawn(self._balance_loop) 51 | except Exception, e: 52 | raise e 53 | 54 | def add_dpid(self, dpid): 55 | # send header, 1 for adding dpid 56 | header_data = HeaderStruct.pack(1) 57 | self.global_socket.sendall(header_data) 58 | 59 | dp_data = DPStruct.pack(dpid) 60 | self.global_socket.sendall(dp_data) 61 | 62 | def _balance_loop(self): 63 | ''' 64 | keep sending cpu usage and memory usage 65 | and receive global controller decision 66 | ''' 67 | while True: 68 | cpu_util = get_cpu_ultilization() 69 | mem_util = get_ram_ultilization() 70 | 71 | # send header, 0 for load 72 | header_data = HeaderStruct.pack(0) 73 | self.global_socket.sendall(header_data) 74 | 75 | load_data = UtilStruct.pack(cpu_util << 8 | mem_util) 76 | self.global_socket.sendall(load_data) 77 | role_data = self.global_socket.recv(RoleStruct.size) 78 | dpid, role = RoleStruct.unpack(role_data) 79 | 80 | # Role: 81 | # [dpid][role] 82 | # 0: no change 83 | # 1: master 84 | # 2: slave 85 | 86 | if role == 0: 87 | LOG.debug('no need to change role.') 88 | continue 89 | 90 | else: 91 | role_event = LBEventRoleChange(dpid, role) 92 | self.send_event_to_observers(role_event) 93 | -------------------------------------------------------------------------------- /MultiControl/ms/MasterApp.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Master App, testing for master controller 3 | ''' 4 | # -*- encoding: utf-8 -*- 5 | # file: MasterApp.py 6 | 7 | from ryu.base import app_manager 8 | from ryu.controller import ofp_event 9 | from ryu.controller.handler import MAIN_DISPATCHER 10 | from ryu.controller.handler import HANDSHAKE_DISPATCHER 11 | from ryu.controller.handler import CONFIG_DISPATCHER 12 | from ryu.controller.handler import set_ev_cls 13 | from ryu.lib.packet import packet 14 | from ryu.controller import dpset 15 | from ryu.ofproto import ofproto_v1_3 16 | from threading import Thread 17 | import socket 18 | import time 19 | 20 | class MasterApp(app_manager.RyuApp): 21 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 22 | def __init__(self, *args, **kwargs): 23 | super(MasterApp, self).__init__(*args, **kwargs) 24 | print 'prepare server' 25 | master_server = MasterServer(7999) 26 | print 'starting server' 27 | master_server.start() 28 | print 'echo server started' 29 | 30 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 31 | def on_packet_in(self, ev): 32 | msg = ev.msg 33 | pkt = packet.Packet(msg.data) 34 | print 'get a packet: %s' % (pkt) 35 | 36 | @set_ev_cls(dpset.EventDP, MAIN_DISPATCHER) 37 | def on_dp_change(self, ev): 38 | 39 | if ev.enter: 40 | dp = ev.dp 41 | dpid = dp.id 42 | ofp = dp.ofproto 43 | ofp_parser = dp.ofproto_parser 44 | 45 | print 'dp entered, id is %s' % (dpid) 46 | self.send_role_request(dp, ofp.OFPCR_ROLE_MASTER, 0) 47 | 48 | 49 | @set_ev_cls(ofp_event.EventOFPErrorMsg, 50 | [HANDSHAKE_DISPATCHER, CONFIG_DISPATCHER, MAIN_DISPATCHER]) 51 | def on_error_msg(self, ev): 52 | msg = ev.msg 53 | print 'receive a error message: %s' % (msg) 54 | 55 | @set_ev_cls(ofp_event.EventOFPRoleReply, MAIN_DISPATCHER) 56 | def on_role_reply(self, ev): 57 | msg = ev.msg 58 | dp = msg.datapath 59 | ofp = dp.ofproto 60 | role = msg.role 61 | gen_id = msg.generation_id 62 | 63 | if role == ofp.OFPCR_ROLE_EQUAL: 64 | print 'now is equal' 65 | elif role == ofp.OFPCR_ROLE_MASTER: 66 | print 'now is master' 67 | elif role == ofp.OFPCR_ROLE_SLAVE: 68 | print 'now is slave' 69 | print '' 70 | 71 | def send_role_request(self, datapath, role, gen_id): 72 | ofp_parser = datapath.ofproto_parser 73 | msg = ofp_parser.OFPRoleRequest(datapath, role, gen_id) 74 | datapath.send_msg(msg) 75 | 76 | 77 | class ServerTask(Thread): 78 | 79 | def __init__(self, server, client_socket): 80 | Thread.__init__(self) 81 | self.server = server 82 | self.client_socket = client_socket 83 | 84 | def run(self): 85 | try: 86 | while True: 87 | self.client_socket.send('hello') 88 | client_data = self.client_socket.recv(1024) 89 | # print 'from client: %s' % (client_data) 90 | time.sleep(1) 91 | except Exception, e: 92 | print 'client break' 93 | client_sockets = self.server.client_sockets 94 | client_socket = self.client_socket 95 | client_sockets.remove(client_socket) 96 | print e 97 | 98 | class MasterServer(Thread): 99 | ''' 100 | server for master, shows that it is alive 101 | ''' 102 | def __init__(self, port): 103 | Thread.__init__(self) 104 | self.client_sockets = [] 105 | self.port = port 106 | self.server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 107 | self.server_socket.bind(('127.0.0.1', port)) 108 | 109 | def run(self): 110 | 111 | self.server_socket.listen(5) 112 | while True: 113 | (client_socket, addr) = self.server_socket.accept() 114 | self.client_sockets.append(client_socket) 115 | server_task = ServerTask(self, client_socket) 116 | server_task.start() 117 | 118 | -------------------------------------------------------------------------------- /MultiControl/ms/SlaveApp.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Slave App, testing for slave controller 3 | ''' 4 | # -*- encoding: utf-8 -*- 5 | # file: MasterApp.py 6 | 7 | from ryu.base import app_manager 8 | from ryu.controller import ofp_event 9 | from ryu.controller.handler import MAIN_DISPATCHER 10 | from ryu.controller.handler import HANDSHAKE_DISPATCHER 11 | from ryu.controller.handler import CONFIG_DISPATCHER 12 | from ryu.controller.handler import set_ev_cls 13 | from ryu.lib.packet import packet 14 | from ryu.controller import dpset 15 | from ryu.ofproto import ofproto_v1_3 16 | import socket 17 | from threading import Thread 18 | 19 | 20 | class SlaveApp(app_manager.RyuApp): 21 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 22 | def __init__(self, *args, **kwargs): 23 | super(SlaveApp, self).__init__(*args, **kwargs) 24 | self.datapaths = [] 25 | print 'preparing echo client' 26 | echo_client = ClientThread('127.0.0.1', 7999, self) 27 | print 'starting echo client...' 28 | echo_client.start() 29 | print 'echo client started' 30 | 31 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 32 | def on_packet_in(self, ev): 33 | msg = ev.msg 34 | pkt = packet.Packet(msg.data) 35 | #print 'get a packet: %s' % (pkt) 36 | 37 | @set_ev_cls(dpset.EventDP, MAIN_DISPATCHER) 38 | def on_dp_change(self, ev): 39 | 40 | if ev.enter: 41 | dp = ev.dp 42 | dpid = dp.id 43 | ofp = dp.ofproto 44 | ofp_parser = dp.ofproto_parser 45 | self.datapaths.append(dp) 46 | 47 | print 'dp entered, id is %s' % (dpid) 48 | self.send_role_request(dp, ofp.OFPCR_ROLE_SLAVE, 0) 49 | 50 | 51 | @set_ev_cls(ofp_event.EventOFPErrorMsg, 52 | [HANDSHAKE_DISPATCHER, CONFIG_DISPATCHER, MAIN_DISPATCHER]) 53 | def on_error_msg(self, ev): 54 | msg = ev.msg 55 | print 'receive a error message: %s' % (msg) 56 | 57 | @set_ev_cls(ofp_event.EventOFPRoleReply, MAIN_DISPATCHER) 58 | def on_role_reply(self, ev): 59 | msg = ev.msg 60 | dp = msg.datapath 61 | ofp = dp.ofproto 62 | role = msg.role 63 | gen_id = msg.generation_id 64 | 65 | if role == ofp.OFPCR_ROLE_EQUAL: 66 | print 'now is equal' 67 | elif role == ofp.OFPCR_ROLE_MASTER: 68 | print 'now is master' 69 | elif role == ofp.OFPCR_ROLE_SLAVE: 70 | print 'now is slave' 71 | print '' 72 | 73 | def send_role_request(self, datapath, role, gen_id): 74 | ofp_parser = datapath.ofproto_parser 75 | msg = ofp_parser.OFPRoleRequest(datapath, role, gen_id) 76 | datapath.send_msg(msg) 77 | 78 | def on_master_down(self): 79 | print 'master is down, trying to change priority to master' 80 | for dp in self.datapaths: 81 | ofp = dp.ofproto 82 | self.send_role_request(dp, ofp.OFPCR_ROLE_MASTER, 0) 83 | 84 | 85 | 86 | class ClientThread(Thread): 87 | 88 | def __init__(self, master_ip, master_port, slave_app): 89 | Thread.__init__(self) 90 | self.master_ip = master_ip 91 | self.master_port = master_port 92 | self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 93 | self.slave_app = slave_app 94 | 95 | def run(self): 96 | c_socket = self.client_socket 97 | ip = self.master_ip 98 | port = self.master_port 99 | c_socket.connect((ip, port)) 100 | try: 101 | while True: 102 | master_data = c_socket.recv(1024) 103 | # print 'receive master message: %s' % (master_data) 104 | c_socket.send('hello') 105 | except Exception, e: 106 | self.slave_app.on_master_down() 107 | -------------------------------------------------------------------------------- /MultiControl/ms/run_master.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ryu-manager --observe-links --ofp-tcp-listen-port 6633 MasterApp.py 3 | -------------------------------------------------------------------------------- /MultiControl/ms/run_slave.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ryu-manager --observe-links --ofp-tcp-listen-port 6632 SlaveApp.py 3 | -------------------------------------------------------------------------------- /PacketGenerate/pg.py: -------------------------------------------------------------------------------- 1 | from ryu.base import app_manager 2 | from ryu.controller import ofp_event 3 | from ryu.controller.handler import MAIN_DISPATCHER, CONFIG_DISPATCHER 4 | from ryu.controller.handler import set_ev_cls 5 | from ryu.ofproto import ofproto_v1_3 6 | from ryu.lib.packet import packet 7 | from ryu.lib.packet import ethernet, ipv4, udp 8 | from ryu.lib.packet import ether_types 9 | 10 | ''' 11 | To test this code 12 | 13 | 1. Create "single" topology 14 | 15 | h1 -- s1 -- h2 16 | 17 | 2. start xterm for both h1 and h2 and run nc: 18 | 19 | h1: 20 | nc -u 10.0.0.2 8000 21 | 22 | h2: 23 | nc -u -l 8000 24 | 25 | 3. send packet from h1 to h2, and h2 will 26 | receive "Hellooooooooo~~~~~~~~~" string 27 | ''' 28 | 29 | 30 | class PacketGenApp(app_manager.RyuApp): 31 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 32 | 33 | def __init__(self, *args, **kwargs): 34 | super(PacketGenApp, self).__init__(*args, **kwargs) 35 | 36 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 37 | def switch_features_handler(self, ev): 38 | datapath = ev.msg.datapath 39 | ofproto = datapath.ofproto 40 | parser = datapath.ofproto_parser 41 | 42 | match = parser.OFPMatch() 43 | actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, 44 | ofproto.OFPCML_NO_BUFFER)] 45 | self.add_flow(datapath, 0, match, actions) 46 | 47 | def add_flow(self, datapath, priority, match, actions, buffer_id=None): 48 | ofproto = datapath.ofproto 49 | parser = datapath.ofproto_parser 50 | 51 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, 52 | actions)] 53 | if buffer_id: 54 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, 55 | priority=priority, match=match, 56 | instructions=inst) 57 | else: 58 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, 59 | match=match, instructions=inst) 60 | datapath.send_msg(mod) 61 | 62 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 63 | def packet_in_handler(self, ev): 64 | pkt = packet.Packet() 65 | 66 | pkt.add_protocol(ethernet.ethernet(ethertype=0x0800, dst='00:00:00:00:00:02', src='00:00:00:00:00:01')) 67 | 68 | pkt.add_protocol(ipv4.ipv4(dst='10.0.0.2', src='10.0.0.1', proto=17)) 69 | pkt.add_protocol(udp.udp(src_port=1000, dst_port=8000)) 70 | 71 | payload = b'Hellooooooooo~~~~~~~~~' 72 | pkt.add_protocol(payload) 73 | 74 | # Packet serializing 75 | pkt.serialize() 76 | 77 | data = pkt.data 78 | 79 | msg = ev.msg 80 | dp = msg.datapath 81 | ofproto = dp.ofproto 82 | actions = [dp.ofproto_parser.OFPActionOutput(2)] 83 | 84 | out = dp.ofproto_parser.OFPPacketOut( 85 | datapath=dp, buffer_id=ofproto.OFP_NO_BUFFER, in_port=msg.match['in_port'], 86 | actions=actions, data=data) 87 | 88 | dp.send_msg(out) 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /PortStatistics/README.md: -------------------------------------------------------------------------------- 1 | Port statics updater 2 | ==== 3 | 4 | Let user know current bandwidth of every port. -------------------------------------------------------------------------------- /PortStatistics/mininet.sh: -------------------------------------------------------------------------------- 1 | sudo mn --topo=tree,depth=3 --controller=remote 2 | 3 | -------------------------------------------------------------------------------- /PortStatistics/port_static_app.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from ryu.base import app_manager 4 | from ryu.controller import ofp_event 5 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 6 | from ryu.controller.handler import set_ev_cls 7 | from ryu.ofproto import ofproto_v1_3 8 | from ryu.lib.packet import packet 9 | from ryu.lib.packet import ethernet 10 | from ryu.topology import api as topo_api 11 | from ryu.lib import hub 12 | 13 | class PortStaticApp(app_manager.RyuApp): 14 | 15 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 16 | 17 | def __init__(self, *args, **kwargs): 18 | super(PortStaticApp, self).__init__(*args, **kwargs) 19 | self.mac_to_port = {} 20 | self.port_infos = {} 21 | hub.spawn(self.port_request_loop) 22 | 23 | 24 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 25 | def switch_features_handler(self, ev): 26 | datapath = ev.msg.datapath 27 | ofproto = datapath.ofproto 28 | parser = datapath.ofproto_parser 29 | 30 | # install table-miss flow entry 31 | # 32 | # We specify NO BUFFER to max_len of the output action due to 33 | # OVS bug. At this moment, if we specify a lesser number, e.g., 34 | # 128, OVS will send Packet-In with invalid buffer_id and 35 | # truncated packet data. In that case, we cannot output packets 36 | # correctly. The bug has been fixed in OVS v2.1.0. 37 | match = parser.OFPMatch() 38 | actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, 39 | ofproto.OFPCML_NO_BUFFER)] 40 | self.add_flow(datapath, 0, match, actions) 41 | 42 | def add_flow(self, datapath, priority, match, actions, buffer_id=None): 43 | ofproto = datapath.ofproto 44 | parser = datapath.ofproto_parser 45 | 46 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, 47 | actions)] 48 | if buffer_id: 49 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, 50 | priority=priority, match=match, 51 | instructions=inst) 52 | else: 53 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, 54 | match=match, instructions=inst) 55 | datapath.send_msg(mod) 56 | 57 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 58 | def _packet_in_handler(self, ev): 59 | # If you hit this you might want to increase 60 | # the "miss_send_length" of your switch 61 | if ev.msg.msg_len < ev.msg.total_len: 62 | self.logger.debug("packet truncated: only %s of %s bytes", 63 | ev.msg.msg_len, ev.msg.total_len) 64 | msg = ev.msg 65 | datapath = msg.datapath 66 | ofproto = datapath.ofproto 67 | parser = datapath.ofproto_parser 68 | in_port = msg.match['in_port'] 69 | 70 | pkt = packet.Packet(msg.data) 71 | eth = pkt.get_protocols(ethernet.ethernet)[0] 72 | 73 | dst = eth.dst 74 | src = eth.src 75 | 76 | dpid = datapath.id 77 | self.mac_to_port.setdefault(dpid, {}) 78 | 79 | # self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port) 80 | 81 | # learn a mac address to avoid FLOOD next time. 82 | self.mac_to_port[dpid][src] = in_port 83 | 84 | if dst in self.mac_to_port[dpid]: 85 | out_port = self.mac_to_port[dpid][dst] 86 | else: 87 | out_port = ofproto.OFPP_FLOOD 88 | 89 | actions = [parser.OFPActionOutput(out_port)] 90 | 91 | # install a flow to avoid packet_in next time 92 | if out_port != ofproto.OFPP_FLOOD: 93 | match = parser.OFPMatch(in_port=in_port, eth_dst=dst) 94 | # verify if we have a valid buffer_id, if yes avoid to send both 95 | # flow_mod & packet_out 96 | if msg.buffer_id != ofproto.OFP_NO_BUFFER: 97 | self.add_flow(datapath, 1, match, actions, msg.buffer_id) 98 | return 99 | else: 100 | self.add_flow(datapath, 1, match, actions) 101 | data = None 102 | if msg.buffer_id == ofproto.OFP_NO_BUFFER: 103 | data = msg.data 104 | 105 | out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, 106 | in_port=in_port, actions=actions, data=data) 107 | datapath.send_msg(out) 108 | 109 | 110 | @set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER) 111 | def port_stats_event_handler(self, ev): 112 | 113 | print "Handling port stats event" 114 | 115 | for stat in ev.msg.body: 116 | dpid = ev.msg.datapath.id 117 | port_no = stat.port_no 118 | name = "%X-%d" % (dpid, port_no, ) 119 | current_time = time.time() 120 | 121 | self.port_infos.setdefault(name, {"last_update":current_time, "rx_bytes": 0, "tx_bytes": 0, "rx_band": 0, "tx_band": 0}) 122 | port_info = self.port_infos[name] 123 | 124 | if port_info["last_update"] == current_time: 125 | port_info["rx_bytes"] = stat.rx_bytes 126 | port_info["tx_bytes"] = stat.tx_bytes 127 | 128 | else: 129 | delta_time = current_time - port_info["last_update"] 130 | port_info["rx_band"] = (stat.rx_bytes - port_info["rx_bytes"]) / delta_time 131 | port_info["tx_band"] = (stat.tx_bytes - port_info["tx_bytes"]) / delta_time 132 | port_info["rx_bytes"] = stat.rx_bytes 133 | port_info["tx_bytes"] = stat.tx_bytes 134 | port_info["last_update"] = current_time 135 | 136 | for name in self.port_infos: 137 | port_info = self.port_infos[name] 138 | print "[%s] rx: %f, tx: %f" % (name, port_info["rx_band"], port_info["tx_band"]) 139 | 140 | 141 | def port_request_loop(self): 142 | time.sleep(5) 143 | 144 | while True: 145 | switches = topo_api.get_all_switch(self) 146 | dps = [switch.dp for switch in switches] 147 | for dp in dps: 148 | parser = dp.ofproto_parser 149 | ofproto = dp.ofproto 150 | msg = parser.OFPPortStatsRequest(dp, 0, ofproto.OFPP_ANY) 151 | dp.send_msg(msg) 152 | 153 | time.sleep(1) 154 | 155 | -------------------------------------------------------------------------------- /PortStatistics/proto.sh: -------------------------------------------------------------------------------- 1 | sudo ovs-vsctl set Bridge s1 protocols=OpenFlow13 2 | sudo ovs-vsctl set Bridge s2 protocols=OpenFlow13 3 | sudo ovs-vsctl set Bridge s3 protocols=OpenFlow13 4 | sudo ovs-vsctl set Bridge s4 protocols=OpenFlow13 5 | sudo ovs-vsctl set Bridge s5 protocols=OpenFlow13 6 | sudo ovs-vsctl set Bridge s6 protocols=OpenFlow13 7 | sudo ovs-vsctl set Bridge s7 protocols=OpenFlow13 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Takeshi's SDN 開發筆記 2 | 3 | 本資料夾下不定期跟新有關我在 SDN Controller 開發上的一箱相關程式,目前有的東西如下: 4 | 5 | - Ryu 6 | - mininet topology with python 7 | 8 | ## How to use 9 | 10 | **作業系統** 11 | 12 | Ubuntu 14.04 或更高 13 | 14 | **需要安裝的工具及軟體** 15 | 16 | 1. [mininet 2.1.0+][1] 17 | 2. [OpenFlow 1.0(optional) / 1.3][2] 18 | 3. [python 3][3] 19 | 20 | **可裝可不裝的工具/輔助設定** 21 | 22 | 1. [Sublime Text][4] 23 | 2. [Atom][7] 24 | 3. [tmux][5] 25 | 4. [Mosky's vimrc][6] 26 | 27 | ## License 28 | 29 | MIT 30 | 31 | 32 | 33 | [1]: http://mininet.org/ 34 | [2]: https://www.opennetworking.org/ 35 | [3]: https://www.python.org/ 36 | [4]: http://www.sublimetext.com/ 37 | [5]: http://tmux.sourceforge.net/ 38 | [6]: https://github.com/moskytw/mosky.vim 39 | [7]: https://atom.io/ 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /RyuTest/RyuTest.py: -------------------------------------------------------------------------------- 1 | from ryu.base import app_manager 2 | from ryu.controller import ofp_event 3 | from ryu.controller.handler import MAIN_DISPATCHER 4 | from ryu.controller.handler import set_ev_cls 5 | 6 | class L2Switch(app_manager.RyuApp): 7 | def __init__(self, *args, **kwargs): 8 | super(L2Switch, self).__init__(*args, **kwargs) 9 | 10 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 11 | def packet_in_handler(self, ev): 12 | msg = ev.msg 13 | dp = msg.datapath 14 | ofp = dp.ofproto 15 | ofp_parser = dp.ofproto_parser 16 | in_port = msg.match['in_port'] 17 | actions = [ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD)] 18 | out = ofp_parser.OFPPacketOut( 19 | datapath=dp, buffer_id=msg.buffer_id, 20 | actions=actions, in_port = in_port) 21 | dp.send_msg(out) 22 | -------------------------------------------------------------------------------- /StaticFlow/static_flow.py: -------------------------------------------------------------------------------- 1 | from ryu.base import app_manager 2 | from ryu.controller import ofp_event 3 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 4 | from ryu.controller.handler import set_ev_cls 5 | from ryu.ofproto import ofproto_v1_3 6 | from ryu.lib.packet import packet 7 | from ryu.lib.packet import ethernet 8 | 9 | 10 | # h1 -> s1 -> h2 : drop 11 | # h1 <- s1 <- h2 : pass 12 | 13 | class StaticFlow(app_manager.RyuApp): 14 | 15 | def __init__(self, *args, **kwargs): 16 | super(StaticFlow, self).__init__(*args, **kwargs) 17 | self.mac_to_port = {} 18 | 19 | 20 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 21 | def switch_features_handler(self, ev): 22 | datapath = ev.msg.datapath 23 | ofproto = datapath.ofproto 24 | parser = datapath.ofproto_parser 25 | 26 | # install table-miss flow entry 27 | match = parser.OFPMatch() 28 | actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, 29 | ofproto.OFPCML_NO_BUFFER)] 30 | self.add_flow(datapath, 0, match, actions) 31 | 32 | h1_ip = '10.0.0.1' 33 | h2_ip = '10.0.0.2' 34 | 35 | h1_mac = '00:00:00:00:00:01' 36 | h2_mac = '00:00:00:00:00:02' 37 | 38 | h1_port = 1 39 | 40 | # install static rule 41 | match_h1_to_h2_ip = parser.OFPMatch(eth_type=2048, ipv4_src=h1_ip, ipv4_dst=h2_ip) 42 | match_h2_to_h1_ip = parser.OFPMatch(eth_type=2048, ipv4_src=h2_ip, ipv4_dst=h1_ip) 43 | 44 | match_h1_to_h2_mac = parser.OFPMatch(eth_src=h1_mac, eth_dst=h2_mac) 45 | match_h2_to_h1_mac = parser.OFPMatch(eth_src=h2_mac, eth_dst=h1_mac) 46 | 47 | actions_forward = [parser.OFPActionOutput(h1_port)] 48 | actions_drop = [] # no actions to drop 49 | 50 | self.add_flow(datapath, 32767, match_h1_to_h2_mac, actions_drop) 51 | self.add_flow(datapath, 32767, match_h2_to_h1_mac, actions_forward) 52 | 53 | self.add_flow(datapath, 32767, match_h1_to_h2_ip, actions_drop) 54 | self.add_flow(datapath, 32767, match_h2_to_h1_ip, actions_forward) 55 | 56 | def add_flow(self, datapath, priority, match, actions, buffer_id=None): 57 | ofproto = datapath.ofproto 58 | parser = datapath.ofproto_parser 59 | 60 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, 61 | actions)] 62 | if buffer_id: 63 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, 64 | priority=priority, match=match, 65 | instructions=inst) 66 | else: 67 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, 68 | match=match, instructions=inst) 69 | datapath.send_msg(mod) 70 | -------------------------------------------------------------------------------- /TopologyEventTest/host_lib.py: -------------------------------------------------------------------------------- 1 | from ryu.base import app_manager 2 | from ryu.controller import ofp_event 3 | from ryu.controller import event 4 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 5 | from ryu.controller.handler import set_ev_cls 6 | from ryu.ofproto import ofproto_v1_3 7 | from ryu.lib.packet import packet 8 | from ryu.lib.packet import ethernet 9 | from ryu.lib.packet import ether_types 10 | 11 | 12 | class EventHostTimeout(event.EventBase): 13 | def __init__(self, host): 14 | super(EventHostTimeout, self).__init__() 15 | self.host = host 16 | 17 | class HostLib(app_manager.RyuApp): 18 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 19 | _EVENTS = [EventHostTimeout] 20 | 21 | def __init__(self, *args, **kwargs): 22 | super(HostLib, self).__init__(*args, **kwargs) 23 | self.port_infos = {} 24 | hub.spawn(self.port_request_loop) 25 | 26 | 27 | @set_ev_cls(ofp_event.EventOFPPortStatsReply, MAIN_DISPATCHER) 28 | def port_stats_event_handler(self, ev): 29 | pass 30 | 31 | def port_request_loop(self): 32 | time.sleep(5) 33 | 34 | while True: 35 | hosts = topo_api.get_all_host(self) 36 | ports = [host.port for host in hosts] 37 | 38 | for port in ports: 39 | dpid = port.dpid 40 | switch = topo_api.get_switch(self, dpid) 41 | dp = switch.dp 42 | parser = dp.ofproto_parser 43 | ofproto = dp.ofproto 44 | msg = parser.OFPPortStatsRequest(dp, 0, port.port_no) 45 | dp.send_msg(msg) 46 | 47 | time.sleep(1) 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /TopologyEventTest/topology_event_test.py: -------------------------------------------------------------------------------- 1 | from ryu.base import app_manager 2 | from ryu.controller import ofp_event 3 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 4 | from ryu.controller.handler import set_ev_cls 5 | from ryu.ofproto import ofproto_v1_3 6 | from ryu.lib.packet import packet 7 | from ryu.lib.packet import ethernet 8 | from ryu.lib.packet import ether_types 9 | from ryu.topology import event as topo_event 10 | 11 | ''' 12 | Test these topology events 13 | 14 | EventHostAdd 15 | EventLinkDelete 16 | EventLinkAdd 17 | EventPortModify 18 | EventPortDelete 19 | EventPortAdd 20 | EventSwitchLeave 21 | EventSwitchEnter 22 | 23 | ''' 24 | 25 | class TopologyEventTestApp(app_manager.RyuApp): 26 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 27 | 28 | def __init__(self, *args, **kwargs): 29 | super(TopologyEventTestApp, self).__init__(*args, **kwargs) 30 | 31 | @set_ev_cls(topo_event.EventHostAdd, MAIN_DISPATCHER) 32 | def EventHostAdd_handler(self, ev): 33 | self.logger.info('Event %s', ev) 34 | 35 | @set_ev_cls(topo_event.EventLinkDelete, MAIN_DISPATCHER) 36 | def EventLinkDelete_handler(self, ev): 37 | self.logger.info('Event %s', ev) 38 | 39 | @set_ev_cls(topo_event.EventLinkAdd, MAIN_DISPATCHER) 40 | def EventLinkAdd_handler(self, ev): 41 | self.logger.info('Event %s', ev) 42 | 43 | @set_ev_cls(topo_event.EventPortModify, MAIN_DISPATCHER) 44 | def EventPortModify_handler(self, ev): 45 | self.logger.info('Event %s', ev) 46 | 47 | @set_ev_cls(topo_event.EventPortDelete, MAIN_DISPATCHER) 48 | def EventPortDelete_handler(self, ev): 49 | self.logger.info('Event %s', ev) 50 | 51 | @set_ev_cls(topo_event.EventPortAdd, MAIN_DISPATCHER) 52 | def EventPortAdd_handler(self, ev): 53 | self.logger.info('Event %s', ev) 54 | 55 | @set_ev_cls(topo_event.EventSwitchLeave, MAIN_DISPATCHER) 56 | def EventSwitchLeave_handler(self, ev): 57 | self.logger.info('Event %s', ev) 58 | 59 | @set_ev_cls(topo_event.EventSwitchEnter, MAIN_DISPATCHER) 60 | def EventSwitchEnter_handler(self, ev): 61 | self.logger.info('Event %s', ev) 62 | 63 | -------------------------------------------------------------------------------- /mininet/README.md: -------------------------------------------------------------------------------- 1 | mininet 2 | ==== 3 | 4 | ### 用途/What is this?: 5 | 6 | 練習各種 mininet 拓樸寫法 7 | 8 | For practice mininet script 9 | 10 | ### 用法/How to use?: 11 | 12 | See README in every directory 13 | -------------------------------------------------------------------------------- /mininet/bgp-3as/as.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from mininet.net import Mininet 3 | from mininet.cli import CLI 4 | from mininet.log import setLogLevel 5 | 6 | ''' 7 | h1 -- r1 -- r2 -- r3 -- h3 8 | | 9 | h2 10 | h1 - r1 : 10.0.1.0/24 11 | h2 - r2 : 10.0.2.0/24 12 | h3 - r3 : 10.0.3.0/24 13 | 14 | r1 - r2 : 192.168.1.0/24 15 | r2 - r3 : 192.168.2.0/24 16 | ''' 17 | 18 | if '__main__' == __name__: 19 | setLogLevel('debug') 20 | net = Mininet(controller=None) 21 | h1 = net.addHost('h1', ip="10.0.1.1/24") 22 | h2 = net.addHost('h2', ip="10.0.2.1/24") 23 | h3 = net.addHost('h3', ip="10.0.3.1/24") 24 | 25 | r1 = net.addHost('r1') 26 | r2 = net.addHost('r2') 27 | r3 = net.addHost('r3') 28 | 29 | net.addLink(r1, r2) 30 | net.addLink(r2, r3) 31 | 32 | net.addLink(h1, r1) 33 | net.addLink(h2, r2) 34 | net.addLink(h3, r3) 35 | 36 | net.build() 37 | # default route for hosts 38 | h1.cmd('ip r add 0.0.0.0/0 via 10.0.1.254') 39 | h2.cmd('ip r add 0.0.0.0/0 via 10.0.2.254') 40 | h3.cmd('ip r add 0.0.0.0/0 via 10.0.3.254') 41 | 42 | # remove default ip address 43 | r1.cmd('ip a del 10.0.0.4/8 dev r1-eth0') 44 | r2.cmd('ip a del 10.0.0.5/8 dev r2-eth0') 45 | r3.cmd('ip a del 10.0.0.6/8 dev r3-eth0') 46 | 47 | # ip for router facing hosts 48 | r1.cmd('ip a add 10.0.1.254/24 dev r1-eth1') 49 | r2.cmd('ip a add 10.0.2.254/24 dev r2-eth2') 50 | r3.cmd('ip a add 10.0.3.254/24 dev r3-eth1') 51 | 52 | # subnet between r1 and r2 53 | r1.cmd('ip a add 192.168.1.1/24 dev r1-eth0') 54 | r2.cmd('ip a add 192.168.1.2/24 dev r2-eth0') 55 | 56 | # subnet between r2 and r3 57 | r2.cmd('ip a add 192.168.2.1/24 dev r2-eth1') 58 | r3.cmd('ip a add 192.168.2.2/24 dev r3-eth0') 59 | 60 | # quagga 61 | r1.cmd('/usr/lib/quagga/zebra -d -f zebra-r1.conf -z /var/run/quagga/zebra-r1.api -i /var/run/quagga/zebra-r1.pid') 62 | r1.cmd('/usr/lib/quagga/bgpd -d -f r1.conf -z /var/run/quagga/zebra-r1.api -i /var/run/quagga/bgpd-r1.pid') 63 | 64 | r2.cmd('/usr/lib/quagga/zebra -d -f zebra-r2.conf -z /var/run/quagga/zebra-r2.api -i /var/run/quagga/zebra-r2.pid') 65 | r2.cmd('/usr/lib/quagga/bgpd -d -f r2.conf -z /var/run/quagga/zebra-r2.api -i /var/run/quagga/bgpd-r2.pid') 66 | 67 | r3.cmd('/usr/lib/quagga/zebra -d -f zebra-r3.conf -z /var/run/quagga/zebra-r3.api -i /var/run/quagga/zebra-r3.pid') 68 | r3.cmd('/usr/lib/quagga/bgpd -d -f r3.conf -z /var/run/quagga/zebra-r3.api -i /var/run/quagga/bgpd-r3.pid') 69 | 70 | CLI(net) 71 | 72 | # kill bgpd and zebra 73 | r1.cmd('killall bgpd zebra') 74 | r2.cmd('killall bgpd zebra') 75 | r3.cmd('killall bgpd zebra') 76 | net.stop() 77 | -------------------------------------------------------------------------------- /mininet/bgp-3as/r1.conf: -------------------------------------------------------------------------------- 1 | hostname r1 2 | password r1 3 | log file /var/log/quagga/bgpd-r1.log 4 | ! 5 | router bgp 10001 6 | bgp router-id 192.168.1.1 7 | timers bgp 3 10 8 | network 10.0.1.0/24 9 | 10 | neighbor 192.168.1.2 remote-as 10002 11 | neighbor 192.168.1.2 ebgp-multihop 12 | neighbor 192.168.1.2 timers connect 5 13 | neighbor 192.168.1.2 advertisement-interval 5 14 | -------------------------------------------------------------------------------- /mininet/bgp-3as/r2.conf: -------------------------------------------------------------------------------- 1 | hostname r2 2 | password r2 3 | log file /var/log/quagga/bgpd-r2.log 4 | ! 5 | router bgp 10002 6 | bgp router-id 192.168.1.2 7 | timers bgp 3 10 8 | network 10.0.2.0/24 9 | 10 | neighbor 192.168.1.1 remote-as 10001 11 | neighbor 192.168.1.1 ebgp-multihop 12 | neighbor 192.168.1.1 timers connect 5 13 | neighbor 192.168.1.1 advertisement-interval 5 14 | 15 | neighbor 192.168.2.2 remote-as 10003 16 | neighbor 192.168.2.2 ebgp-multihop 17 | neighbor 192.168.2.2 timers connect 5 18 | neighbor 192.168.2.2 advertisement-interval 5 19 | -------------------------------------------------------------------------------- /mininet/bgp-3as/r3.conf: -------------------------------------------------------------------------------- 1 | hostname r3 2 | password r3 3 | log file /var/log/quagga/bgpd-r3.log 4 | ! 5 | router bgp 10003 6 | bgp router-id 192.168.2.2 7 | timers bgp 3 10 8 | network 10.0.3.0/24 9 | 10 | neighbor 192.168.2.1 remote-as 10002 11 | neighbor 192.168.2.1 ebgp-multihop 12 | neighbor 192.168.2.1 timers connect 5 13 | neighbor 192.168.2.1 advertisement-interval 5 14 | -------------------------------------------------------------------------------- /mininet/bgp-3as/zebra-r1.conf: -------------------------------------------------------------------------------- 1 | hostname zebra 2 | password zebra 3 | log stdout 4 | ! 5 | interface r1-eth0 6 | ip address 192.168.1.1/24 7 | ! 8 | interface r1-eth1 9 | ip address 10.0.1.254/24 10 | ! 11 | ip forwarding 12 | -------------------------------------------------------------------------------- /mininet/bgp-3as/zebra-r2.conf: -------------------------------------------------------------------------------- 1 | hostname zebra 2 | password zebra 3 | log stdout 4 | ! 5 | interface r2-eth0 6 | ip address 192.168.1.2/24 7 | ! 8 | interface r2-eth1 9 | ip address 192.168.2.1/24 10 | ! 11 | interface r2-eth2 12 | ip address 10.0.2.254/24 13 | ! 14 | ip forwarding 15 | -------------------------------------------------------------------------------- /mininet/bgp-3as/zebra-r3.conf: -------------------------------------------------------------------------------- 1 | hostname zebra 2 | password zebra 3 | log stdout 4 | ! 5 | interface r3-eth0 6 | ip address 192.168.2.2/24 7 | ! 8 | interface r3-eth1 9 | ip address 10.0.3.254/24 10 | ! 11 | ip forwarding 12 | -------------------------------------------------------------------------------- /mininet/bgp/README.md: -------------------------------------------------------------------------------- 1 | Quagga testbed 2 | ---- 3 | 4 | Topology 5 | 6 | Quagga1 -- switch -- Quagga2 7 | 8 | -------------------------------------------------------------------------------- /mininet/bgp/configs/quagga1.conf: -------------------------------------------------------------------------------- 1 | ! BGP configuration for quagga1 2 | ! 3 | hostname quagga1 4 | password quagga1 5 | ! 6 | ! 7 | router bgp 65001 8 | bgp router-id 10.0.1.1 9 | timers bgp 3 9 10 | ! krenet 11 | neighbor 10.0.2.1 remote-as 65002 12 | neighbor 10.0.2.1 ebgp-multihop 13 | neighbor 10.0.2.1 timers connect 5 14 | neighbor 10.0.2.1 advertisement-interval 5 15 | ! 16 | log stdout 17 | -------------------------------------------------------------------------------- /mininet/bgp/configs/quagga2.conf: -------------------------------------------------------------------------------- 1 | ! BGP configuration for quagga2 2 | ! 3 | hostname quagga2 4 | password quagga2 5 | ! 6 | ! 7 | router bgp 65002 8 | bgp router-id 10.0.2.1 9 | timers bgp 3 9 10 | ! krenet 11 | neighbor 10.0.1.1 remote-as 65001 12 | neighbor 10.0.1.1 ebgp-multihop 13 | neighbor 10.0.1.1 timers connect 5 14 | neighbor 10.0.1.1 advertisement-interval 5 15 | ! 16 | log stdout 17 | -------------------------------------------------------------------------------- /mininet/bgp/configs/zebra.conf: -------------------------------------------------------------------------------- 1 | ! Configuration for zebra (NB: it is the same for all routers) 2 | ! 3 | hostname zebra 4 | password zebra 5 | log stdout 6 | -------------------------------------------------------------------------------- /mininet/bgp/topo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from mininet.topo import Topo 4 | from mininet.net import Mininet 5 | from mininet.cli import CLI 6 | from mininet.log import setLogLevel, info, debug 7 | from mininet.node import Host, RemoteController, OVSSwitch 8 | 9 | # Must exist and be owned by quagga user (quagga:quagga by default on Ubuntu) 10 | QUAGGA_RUN_DIR = '/var/run/quagga' 11 | QCONFIG_DIR = 'configs' 12 | ZCONFIG_DIR = 'configs' 13 | 14 | 15 | class SdnIpHost(Host): 16 | def __init__(self, name, ip, route, *args, **kwargs): 17 | Host.__init__(self, name, ip=ip, *args, **kwargs) 18 | 19 | self.route = route 20 | 21 | def config(self, **kwargs): 22 | Host.config(self, **kwargs) 23 | 24 | debug("configuring route %s" % self.route) 25 | 26 | self.cmd('ip route add default via %s' % self.route) 27 | 28 | 29 | class Router(Host): 30 | def __init__(self, name, quaggaConfFile, zebraConfFile, intfDict, *args, **kwargs): 31 | Host.__init__(self, name, *args, **kwargs) 32 | 33 | self.quaggaConfFile = quaggaConfFile 34 | self.zebraConfFile = zebraConfFile 35 | self.intfDict = intfDict 36 | 37 | def config(self, **kwargs): 38 | Host.config(self, **kwargs) 39 | self.cmd('sysctl net.ipv4.ip_forward=1') 40 | 41 | for intf, attrs in self.intfDict.items(): 42 | self.cmd('ip addr flush dev %s' % intf) 43 | 44 | # setup mac address to specific interface 45 | if 'mac' in attrs: 46 | self.cmd('ip link set %s down' % intf) 47 | self.cmd('ip link set %s address %s' % (intf, attrs['mac'])) 48 | self.cmd('ip link set %s up ' % intf) 49 | 50 | # setup address to interfaces 51 | for addr in attrs['ipAddrs']: 52 | self.cmd('ip addr add %s dev %s' % (addr, intf)) 53 | 54 | self.cmd('zebra -d -f %s -z %s/zebra%s.api -i %s/zebra%s.pid' % (self.zebraConfFile, QUAGGA_RUN_DIR, self.name, QUAGGA_RUN_DIR, self.name)) 55 | self.cmd('bgpd -d -f %s -z %s/zebra%s.api -i %s/bgpd%s.pid' % (self.quaggaConfFile, QUAGGA_RUN_DIR, self.name, QUAGGA_RUN_DIR, self.name)) 56 | 57 | def terminate(self): 58 | self.cmd("ps ax | egrep 'bgpd%s.pid|zebra%s.pid' | awk '{print $1}' | xargs kill" % (self.name, self.name)) 59 | 60 | Host.terminate(self) 61 | 62 | 63 | class SdnIpTopo(Topo): 64 | 65 | def build(self): 66 | zebraConf = '{}/zebra.conf'.format(ZCONFIG_DIR) 67 | 68 | s1 = self.addSwitch('s1', dpid='0000000000000001', cls=OVSSwitch, failMode="standalone") 69 | 70 | # Quagga 1 71 | bgpEth0 = { 72 | 'mac': '00:00:00:00:00:01', 73 | 'ipAddrs': [ 74 | '10.0.1.1/24', 75 | ] 76 | } 77 | 78 | bgpIntfs = { 79 | 'bgpq1-eth0': bgpEth0 80 | } 81 | 82 | bgpq1 = self.addHost("bgpq1", cls=Router, 83 | quaggaConfFile='{}/quagga1.conf'.format(QCONFIG_DIR), 84 | zebraConfFile=zebraConf, 85 | intfDict=bgpIntfs) 86 | 87 | self.addLink(bgpq1, s1) 88 | 89 | # Quagga 2 90 | bgpEth0 = { 91 | 'mac': '00:00:00:00:00:02', 92 | 'ipAddrs': [ 93 | '10.0.2.1/24', 94 | ] 95 | } 96 | 97 | bgpIntfs = { 98 | 'bgpq2-eth0': bgpEth0 99 | } 100 | 101 | bgpq2 = self.addHost("bgpq2", cls=Router, 102 | quaggaConfFile='{}/quagga2.conf'.format(QCONFIG_DIR), 103 | zebraConfFile=zebraConf, 104 | intfDict=bgpIntfs) 105 | 106 | self.addLink(bgpq2, s1) 107 | 108 | 109 | topos = {'sdnip': SdnIpTopo} 110 | 111 | if __name__ == '__main__': 112 | setLogLevel('debug') 113 | topo = SdnIpTopo() 114 | 115 | net = Mininet(topo=topo, controller=RemoteController) 116 | 117 | net.start() 118 | 119 | CLI(net) 120 | 121 | net.stop() 122 | 123 | info("done\n") 124 | -------------------------------------------------------------------------------- /mininet/custom/README.md: -------------------------------------------------------------------------------- 1 | # Custom Topology 2 | 3 | #### 如何使用/How to use? 4 | 5 | 針對 custom~custom2 只需要執行他們即可 6 | 7 | For custom~custom2, just start it with execute it. 8 | 9 | ``` shell 10 | chmod +x ./custom.py 11 | sudo ./custom.py 12 | ``` 13 | 14 | 15 | 16 | 針對 custom3 需要輸入以下指令: 17 | 18 | For custom3: 19 | 20 | ``` shell 21 | sudo mn --custom ./custom3.py --topo square 22 | ``` 23 | 24 | -------------------------------------------------------------------------------- /mininet/custom/custom.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from mininet.cli import CLI 4 | from mininet.node import Link 5 | from mininet.net import Mininet 6 | from mininet.node import RemoteController 7 | from mininet.term import makeTerm 8 | from functools import partial 9 | 10 | 11 | def ofp_version(switch, protocols): 12 | protocols_str = ','.join(protocols) 13 | command = 'ovs-vsctl set Bridge %s protocols=%s' % (switch, protocols) 14 | switch.cmd(command.split(' ')) 15 | 16 | if '__main__' == __name__: 17 | net = Mininet(controller=RemoteController) 18 | c0 = net.addController('c0') 19 | 20 | s1 = net.addSwitch('s1') 21 | s2 = net.addSwitch('s2') 22 | s3 = net.addSwitch('s3') 23 | s4 = net.addSwitch('s4') 24 | s5 = net.addSwitch('s5') 25 | s6 = net.addSwitch('s6') 26 | s7 = net.addSwitch('s7') 27 | s8 = net.addSwitch('s8') 28 | 29 | net.addLink(s1, s2) 30 | net.addLink(s2, s3) 31 | net.addLink(s3, s4) 32 | net.addLink(s4, s1) 33 | 34 | net.addLink(s2, s5) 35 | net.addLink(s3, s6) 36 | net.addLink(s5, s6) 37 | 38 | net.addLink(s5, s7) 39 | net.addLink(s6, s8) 40 | net.addLink(s7, s8) 41 | 42 | net.build() 43 | c0.start() 44 | s1.start([c0]) 45 | s2.start([c0]) 46 | s3.start([c0]) 47 | s4.start([c0]) 48 | s5.start([c0]) 49 | s6.start([c0]) 50 | s7.start([c0]) 51 | s8.start([c0]) 52 | 53 | ofp_version(s1, ['OpenFlow13']) 54 | ofp_version(s2, ['OpenFlow13']) 55 | ofp_version(s3, ['OpenFlow13']) 56 | ofp_version(s4, ['OpenFlow13']) 57 | ofp_version(s5, ['OpenFlow13']) 58 | ofp_version(s6, ['OpenFlow13']) 59 | ofp_version(s7, ['OpenFlow13']) 60 | ofp_version(s8, ['OpenFlow13']) 61 | 62 | CLI(net) 63 | net.stop() 64 | -------------------------------------------------------------------------------- /mininet/custom/custom1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from mininet.cli import CLI 4 | from mininet.node import Link 5 | from mininet.net import Mininet 6 | from mininet.node import RemoteController 7 | from mininet.term import makeTerm 8 | from functools import partial 9 | 10 | 11 | def ofp_version(switch, protocols): 12 | protocols_str = ','.join(protocols) 13 | command = 'ovs-vsctl set Bridge %s protocols=%s' % (switch, protocols) 14 | switch.cmd(command.split(' ')) 15 | 16 | if '__main__' == __name__: 17 | net = Mininet(controller=partial(RemoteController, ip='10.42.0.27', port=6633)) 18 | c0 = net.addController('c0') 19 | s1 = net.addSwitch('s1') 20 | s2 = net.addSwitch('s2') 21 | s3 = net.addSwitch('s3') 22 | s4 = net.addSwitch('s4') 23 | s5 = net.addSwitch('s5') 24 | s6 = net.addSwitch('s6') 25 | 26 | h1 = net.addHost('h1') 27 | h2 = net.addHost('h2') 28 | h3 = net.addHost('h3') 29 | h4 = net.addHost('h4') 30 | 31 | net.addLink(s1, h1) 32 | net.addLink(s2, h2) 33 | net.addLink(s5, h3) 34 | net.addLink(s6, h4) 35 | 36 | net.addLink(s1, s2) 37 | net.addLink(s2, s3) 38 | net.addLink(s2, s4) 39 | net.addLink(s4, s5) 40 | net.addLink(s4, s6) 41 | 42 | net.build() 43 | c0.start() 44 | s1.start([c0]) 45 | s2.start([c0]) 46 | s3.start([c0]) 47 | s4.start([c0]) 48 | s5.start([c0]) 49 | s6.start([c0]) 50 | 51 | ofp_version(s1, ['OpenFlow13']) 52 | ofp_version(s2, ['OpenFlow13']) 53 | ofp_version(s3, ['OpenFlow13']) 54 | ofp_version(s4, ['OpenFlow13']) 55 | ofp_version(s5, ['OpenFlow13']) 56 | ofp_version(s6, ['OpenFlow13']) 57 | 58 | CLI(net) 59 | 60 | net.stop() 61 | -------------------------------------------------------------------------------- /mininet/custom/custom2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from mininet.cli import CLI 4 | from mininet.link import Link 5 | from mininet.net import Mininet 6 | from mininet.node import RemoteController, OVSSwitch 7 | 8 | 9 | def ofp_version(switch, protocols): 10 | protocols_str = ','.join(protocols) 11 | command = 'ovs-vsctl set Bridge %s protocols=%s' % (switch, protocols_str) 12 | switch.cmd(command.split(' ')) 13 | 14 | 15 | if '__main__' == __name__: 16 | net = Mininet(switch=OVSSwitch) 17 | 18 | c0 = RemoteController('c0') 19 | net.addController(c0) 20 | 21 | s1 = net.addSwitch('s1') 22 | 23 | h1 = net.addHost('h1') 24 | h2 = net.addHost('h2') 25 | h3 = net.addHost('h3') 26 | 27 | Link(s1, h1) 28 | Link(s1, h2) 29 | Link(s1, h3) 30 | 31 | net.build() 32 | 33 | c0.start() 34 | s1.start([c0]) 35 | 36 | ofp_version(s1, ['OpenFlow13']) 37 | 38 | CLI(net) 39 | 40 | net.stop() 41 | -------------------------------------------------------------------------------- /mininet/custom/custom3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # file: MyTopo.py 4 | 5 | from mininet.topo import Topo 6 | 7 | class Square(Topo): 8 | ''' Simple topology example ''' 9 | 10 | def __init__(self): 11 | Topo.__init__(self) 12 | s1 = self.addSwitch('s1') 13 | s2 = self.addSwitch('s2') 14 | s3 = self.addSwitch('s3') 15 | s4 = self.addSwitch('s4') 16 | 17 | h1 = self.addHost('h1') 18 | h2 = self.addHost('h2') 19 | 20 | self.addLink(s1, s2) 21 | self.addLink(s1, s3) 22 | self.addLink(s2, s4) 23 | self.addLink(s2, h1) 24 | self.addLink(s3, s4) 25 | self.addLink(s4, h2) 26 | 27 | class Router(Topo): 28 | '''test''' 29 | 30 | def __init__(self): 31 | Topo.__init__(self) 32 | s1 = self.addSwitch('s1') 33 | h1 = self.addHost('h1') 34 | h2 = self.addHost('h2') 35 | h3 = self.addHost('h3') 36 | 37 | self.addLink(s1, h1) 38 | self.addLink(s1, h2) 39 | self.addLink(s1, h3) 40 | 41 | 42 | 43 | topos = {'square': (lambda: Square()), 'router': (lambda: Router())} 44 | 45 | -------------------------------------------------------------------------------- /mininet/fat tree/README.md: -------------------------------------------------------------------------------- 1 | # Fat Tree 2 | 3 | ### 用途/What is this?: 4 | 5 | 用來產生 Fat tree 唾撲 6 | 7 | For generating fat tree topology 8 | 9 | ### 用法/How to use?: 10 | 11 | 1.設定 Controller: 12 | 13 | 1.Setup controller: 14 | 15 | ``` python 16 | fat_tree.py:158 net.addController('Controller', controller=RemoteController, ip='127.0.0.1', port=6653) 17 | ``` 18 | 19 | 2.(選擇性的)設定: 20 | 21 | - k 值 22 | - Aggregation 到 Core 的頻寬 23 | - Pod 內的頻寬 24 | - Aggregation 到 Core 的封包遺失率 25 | - Pod 內的封包遺失率 26 | 27 | 2.(Optional)setup: 28 | 29 | - k value, bandwidth between aggregation and core(Default 1Gbps) 30 | - bandwidth between switches in pod(Default 100Mbps) 31 | - packet lost between aggregation and core(Default: 5%) 32 | - packet lost between switches in pod(Default: 0%) 33 | 34 | ``` python 35 | fat_tree.py:155 fat_tree = FatTreeTopo(k=4, ac_bw=1000, pod_bw=100, ac_pkt_lost=5, pod_pkt_lost=0) 36 | ``` 37 | 38 | 3.執行 fat_tree.py 39 | 40 | 3.Start fat_tree.py 41 | 42 | ``` shell 43 | chmod +x ./fat_tree.py 44 | sudo ./fat_tree.py 45 | ``` 46 | -------------------------------------------------------------------------------- /mininet/fat tree/fat_tree.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ''' 3 | Fat tree topology create by mininet 4 | Author : Yi Tseng 5 | ''' 6 | import logging 7 | import pdb 8 | import os 9 | from functools import partial 10 | 11 | from mininet.net import Mininet 12 | from mininet.node import Controller, RemoteController 13 | from mininet.node import OVSSwitch 14 | from mininet.cli import CLI 15 | from mininet.log import setLogLevel, info 16 | from mininet.link import Link, Intf, TCLink 17 | from mininet.topo import Topo 18 | from mininet.util import dumpNodeConnections 19 | 20 | logging.basicConfig(level=logging.DEBUG) 21 | LOG = logging.getLogger('FatTreeTopo') 22 | 23 | 24 | class FatTreeTopo(Topo): 25 | core_switches = [] 26 | pods = [] 27 | 28 | def __init__(self, k=4, ac_bw=1000, pod_bw=100, ac_pkt_lost=5, pod_pkt_lost=0): 29 | ''' 30 | Param: 31 | k: k value for this fat tree(Default: 4) 32 | ac_bw: bandwidth between aggregation and core(Default 1Gbps) 33 | pod_bw: bandwidth between switches in pod(Default 100Mbps) 34 | ac_pkt_lost: packet lost between aggregation and core(Default: 5%) 35 | pod_pkt_lost: packet lost between switches in pod(Default: 0%) 36 | ''' 37 | self.num_pods = k 38 | self.num_cores = (k / 2) ** 2 39 | self.num_aggres = (k ** 2) / 2 40 | self.num_aggres_per_pod = self.num_aggres / self.num_pods 41 | self.num_edges = (k ** 2) / 2 42 | self.num_edges_per_pod = self.num_edges / self.num_pods 43 | self.num_host_per_edge = k / 2 44 | self.ac_bw = ac_bw 45 | self.pod_bw = pod_bw 46 | self.ac_pkt_lost = ac_pkt_lost 47 | self.pod_pkt_lost = pod_pkt_lost 48 | 49 | Topo.__init__(self) 50 | 51 | def create_core(self): 52 | ''' 53 | Create core switches 54 | ''' 55 | for i in range(0, self.num_cores): 56 | switch_name = 's1%d' % (i, ) 57 | self.core_switches.append(self.addSwitch(switch_name)) 58 | 59 | def create_pods(self): 60 | ''' 61 | Create pods 62 | ''' 63 | for pi in range(0, self.num_pods): 64 | self.pods.append({'aggr': [], 'edge': []}) 65 | 66 | # create aggregation layer 67 | for ai in range(0, self.num_aggres_per_pod): 68 | switch_name = 's2%d%d' % (pi, ai, ) 69 | self.pods[pi]['aggr'].append(self.addSwitch(switch_name)) 70 | 71 | # create edge layer 72 | for ei in range(0, self.num_edges_per_pod): 73 | switch_name = 's3%d%d' % (pi, ei, ) 74 | edge_switch = self.addSwitch(switch_name) 75 | self.pods[pi]['edge'].append(edge_switch) 76 | 77 | # create hosts 78 | for hi in range(0, self.num_host_per_edge): 79 | host_name = 'h%d%d%d' % (pi, ei, hi, ) 80 | host = self.addHost(host_name) 81 | self.addLink(edge_switch, host) 82 | 83 | # link aggregation and edge 84 | for aggr_switch in self.pods[pi]['aggr']: 85 | 86 | for edge_switch in self.pods[pi]['edge']: 87 | LOG.info('add link from %s to %s', aggr_switch, edge_switch) 88 | self.addLink(aggr_switch, 89 | edge_switch, 90 | bw=self.pod_bw, 91 | loss=self.pod_pkt_lost) 92 | 93 | def link_core_to_pods(self): 94 | ''' 95 | link aggregation layer to core layer 96 | aggregation switch can link to core switch directly 97 | pod number doesn't matter 98 | ex: 99 | k = 8 100 | 4 aggregation switch in a pod 101 | A0 -> C0~C3 102 | A1 -> C4~C7 103 | A2 -> C8~C11 104 | A3 -> C12~C15 105 | ''' 106 | num_cores_per_aggr = self.num_cores / self.num_aggres_per_pod 107 | 108 | for pod in self.pods: 109 | 110 | for ai in range(0, self.num_aggres_per_pod): # ai for aggregation index 111 | cis = ai * num_cores_per_aggr # start index 112 | cie = ai * num_cores_per_aggr + num_cores_per_aggr # end index 113 | 114 | for ci in range(cis, cie): # ci for core index 115 | aggr_switch = pod['aggr'][ai] 116 | core_switch = self.core_switches[ci] 117 | LOG.info('add link from %s to %s', aggr_switch, core_switch) 118 | self.addLink(aggr_switch, 119 | core_switch, 120 | bw=self.ac_bw, 121 | loss=self.ac_pkt_lost) 122 | 123 | def set_protocols_to_all_switch(self, protocols): 124 | pdb.set_trace() 125 | for core_switch in self.core_switches: 126 | set_protocol(core_switch) 127 | 128 | for pod in self.pods: 129 | 130 | for aggr_switch in pod['aggr']: 131 | set_protocol(aggr_switch) 132 | 133 | for edge_switch in pod['edge']: 134 | set_protocol(edge_switch) 135 | 136 | def init_fat_tree(self): 137 | self.create_core() 138 | self.create_pods() 139 | self.link_core_to_pods() 140 | # self.set_protocols_to_all_switch(['OpenFlow13']) 141 | 142 | 143 | def set_protocol(switch, protocols): 144 | protocols_str = ','.join(protocols) 145 | command = 'ovs-vsctl set Bridge %s protocols=%s' % (switch, protocols_str) 146 | switch.cmd(command.split(' ')) 147 | 148 | 149 | def set_stp(switch): 150 | cmd = "ovs-vsctl set Bridge %s stp_enable=true" % (switch.name, ) 151 | LOG.info(cmd) 152 | os.system(cmd) 153 | 154 | if __name__ == '__main__': 155 | fat_tree = FatTreeTopo(4) 156 | fat_tree.init_fat_tree() 157 | net = Mininet(topo=fat_tree, link=TCLink, controller=None) 158 | net.addController('Controller', controller=RemoteController, ip='127.0.0.1', port=6653) 159 | net.start() 160 | 161 | ''' 162 | switches = [net.get(sw) for sw in fat_tree.switches()] 163 | for sw in switches: 164 | set_stp(sw) 165 | ''' 166 | dumpNodeConnections(net.hosts) 167 | LOG.info('Ping all') 168 | net.pingAll() 169 | 170 | # iperf test 171 | LOG.info('iperf test') 172 | client_host = net.get('h000') # form pod 0, edge 0, host 0 173 | server_host1 = net.get('h011') # from pod 0, edge 1, host 1 174 | server_host2 = net.get('h301') # from pod 3, edge 0, host 1 175 | 176 | # start iperf server 1 177 | server_host1.popen('iperf -s -u -i 1 > iperf_server_1_report.txt', shell=True) 178 | 179 | # start iperf server 2 180 | server_host2.popen('iperf -s -u -i 1 > iperf_server_2_report.txt', shell=True) 181 | 182 | # start iperf client to server 1 183 | client_host.cmdPrint('iperf -c ' + server_host1.IP() + ' -u -t 10 -i 1 -b 100m') 184 | 185 | # start iperf client to server 1 186 | client_host.cmdPrint('iperf -c ' + server_host2.IP() + ' -u -t 10 -i 1 -b 100m') 187 | 188 | LOG.info('end iperf test') 189 | CLI(net) 190 | net.stop() 191 | -------------------------------------------------------------------------------- /mininet/leaf-spine/leaf-spine.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from mininet.cli import CLI 4 | from mininet.node import Link 5 | from mininet.net import Mininet 6 | from mininet.node import RemoteController 7 | from mininet.term import makeTerm 8 | from functools import partial 9 | 10 | 11 | 12 | if '__main__' == __name__: 13 | net = Mininet() 14 | num_spine = 2 15 | num_leaf = 2 16 | 17 | spine_switches = [] 18 | leaf_switches = [] 19 | 20 | for i in range(num_spine): 21 | name = 's1%02d' % (i) 22 | sw = net.addSwitch(name) 23 | spine_switches.append(sw) 24 | 25 | for i in range(num_leaf): 26 | name = 's2%02d' % (i) 27 | sw = net.addSwitch(name) 28 | leaf_switches.append(sw) 29 | 30 | for ss in spine_switches: 31 | for ls in leaf_switches: 32 | net.addLink(ss, ls) 33 | 34 | hid = 0 35 | for ls in leaf_switches: 36 | h1 = net.addHost('h%03d' % (hid, )) 37 | h2 = net.addHost('h%03d' % (hid + 1, )) 38 | hid += 2 39 | net.addLink(ls, h1) 40 | net.addLink(ls, h2) 41 | 42 | c0 = RemoteController('c0', '127.0.0.1', 6653) 43 | net.build() 44 | c0.start() 45 | 46 | for ss in spine_switches: 47 | ss.start([c0]) 48 | 49 | for ls in leaf_switches: 50 | ls.start([c0]) 51 | 52 | CLI(net) 53 | 54 | net.stop() 55 | -------------------------------------------------------------------------------- /mininet/two controller/README.md: -------------------------------------------------------------------------------- 1 | # Two Controller 2 | 3 | #### 如何使用?/How to use? 4 | 5 | 設定兩台 Controller 6 | 7 | Setup two controller 8 | 9 | ``` python 10 | two_controller.py:18 c0 = RemoteController('c0', '127.0.0.1', 6633) 11 | two_controller.py:19 c1 = RemoteController('c1', '127.0.0.1', 6632) 12 | ``` 13 | 14 | 15 | 16 | 然後直接執行即可 17 | 18 | And run the script 19 | 20 | ``` shell 21 | chmod +x ./two_controller.py 22 | sudo ./two_controller.py 23 | ``` -------------------------------------------------------------------------------- /mininet/two controller/two_controller.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from mininet.cli import CLI 4 | from mininet.link import Link 5 | from mininet.net import Mininet 6 | from mininet.node import RemoteController, OVSSwitch 7 | 8 | 9 | def ofp_version(switch, protocols): 10 | protocols_str = ','.join(protocols) 11 | command = 'ovs-vsctl set Bridge %s protocols=%s' % (switch, protocols_str) 12 | switch.cmd(command.split(' ')) 13 | 14 | 15 | if '__main__' == __name__: 16 | net = Mininet(switch=OVSSwitch) 17 | 18 | c0 = RemoteController('c0', '127.0.0.1', 6633) 19 | c1 = RemoteController('c1', '127.0.0.1', 6632) 20 | net.addController(c0) 21 | net.addController(c1) 22 | 23 | s1 = net.addSwitch('s1') 24 | 25 | h1 = net.addHost('h1') 26 | h2 = net.addHost('h2') 27 | h3 = net.addHost('h3') 28 | 29 | Link(s1, h1) 30 | Link(s1, h2) 31 | Link(s1, h3) 32 | 33 | net.build() 34 | 35 | c0.start() 36 | c1.start() 37 | s1.start([c0, c1]) 38 | 39 | ofp_version(s1, ['OpenFlow13']) 40 | 41 | CLI(net) 42 | 43 | net.stop() 44 | -------------------------------------------------------------------------------- /mininet/vxlan/insert_flows.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo ovs-ofctl add-flows s1 s1_flows.txt 4 | sudo ovs-ofctl add-flows s2 s2_flows.txt 5 | -------------------------------------------------------------------------------- /mininet/vxlan/s1_flows.txt: -------------------------------------------------------------------------------- 1 | table=0,in_port=1,actions=set_field:100->tun_id,resubmit(,1) 2 | table=0,in_port=2,actions=set_field:200->tun_id,resubmit(,1) 3 | table=0,actions=resubmit(,1) 4 | 5 | table=1,tun_id=100,dl_dst=00:00:00:00:00:01,actions=output:1 6 | table=1,tun_id=200,dl_dst=00:00:00:00:00:01,actions=output:2 7 | table=1,tun_id=100,dl_dst=00:00:00:00:00:02,actions=output:10 8 | table=1,tun_id=200,dl_dst=00:00:00:00:00:02,actions=output:10 9 | table=1,tun_id=100,arp,nw_dst=10.0.0.1,actions=output:1 10 | table=1,tun_id=200,arp,nw_dst=10.0.0.1,actions=output:2 11 | table=1,tun_id=100,arp,nw_dst=10.0.0.2,actions=output:10 12 | table=1,tun_id=200,arp,nw_dst=10.0.0.2,actions=output:10 13 | table=1,priority=100,actions=drop 14 | -------------------------------------------------------------------------------- /mininet/vxlan/s2_flows.txt: -------------------------------------------------------------------------------- 1 | table=0,in_port=1,actions=set_field:100->tun_id,resubmit(,1) 2 | table=0,in_port=2,actions=set_field:200->tun_id,resubmit(,1) 3 | table=0,actions=resubmit(,1) 4 | 5 | table=1,tun_id=100,dl_dst=00:00:00:00:00:01,actions=output:10 6 | table=1,tun_id=200,dl_dst=00:00:00:00:00:01,actions=output:10 7 | table=1,tun_id=100,dl_dst=00:00:00:00:00:02,actions=output:1 8 | table=1,tun_id=200,dl_dst=00:00:00:00:00:02,actions=output:2 9 | table=1,tun_id=100,arp,nw_dst=10.0.0.1,actions=output:10 10 | table=1,tun_id=200,arp,nw_dst=10.0.0.1,actions=output:10 11 | table=1,tun_id=100,arp,nw_dst=10.0.0.2,actions=output:1 12 | table=1,tun_id=200,arp,nw_dst=10.0.0.2,actions=output:2 13 | table=1,priority=100,actions=drop 14 | -------------------------------------------------------------------------------- /mininet/vxlan/topology.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | from mininet.net import Mininet 4 | from mininet.cli import CLI 5 | from mininet.log import setLogLevel, info, debug 6 | from mininet.topo import Topo 7 | from mininet.node import Host, RemoteController, OVSSwitch 8 | 9 | ''' 10 | Topology: 11 | red1 -` ,- red2 12 | +--- ovs1 -- tor -- ovs2 ---+ 13 | blue1 -, `- blue2 14 | 15 | IP of ovs1 is 192.168.10.1, ovs2 is 192.168.20.1 16 | Use ovs as vtep 17 | ''' 18 | 19 | 20 | class VXLANTopo(Topo): 21 | 22 | def __init__(self): 23 | Topo.__init__(self) 24 | tor = self.addSwitch('tor', 25 | dpid='0000000000000010', 26 | cls=OVSSwitch, failMode="standalone") 27 | s1 = self.addSwitch('s1') 28 | s2 = self.addSwitch('s2') 29 | red1 = self.addHost('red1', ip='10.0.0.1/24') 30 | red2 = self.addHost('red2', ip='10.0.0.2/24') 31 | blue1 = self.addHost('blue1', ip='10.0.0.1/24') 32 | blue2 = self.addHost('blue2', ip='10.0.0.2/24') 33 | 34 | self.addLink(red1, s1) 35 | self.addLink(blue1, s1) 36 | 37 | self.addLink(red2, s2) 38 | self.addLink(blue2, s2) 39 | 40 | self.addLink(s1, tor, port1=10, port2=1) 41 | self.addLink(s2, tor, port1=10, port2=2) 42 | 43 | if __name__ == '__main__': 44 | setLogLevel('debug') 45 | topo = VXLANTopo() 46 | 47 | net = Mininet(topo=topo, controller=None) 48 | 49 | net.start() 50 | c0 = net.addController(name='c0', 51 | controller=RemoteController, 52 | ip='127.0.0.1', port=6633) 53 | 54 | net.get('red1').setMAC('00:00:00:00:00:01') 55 | net.get('red2').setMAC('00:00:00:00:00:02') 56 | net.get('blue1').setMAC('00:00:00:00:00:01') 57 | net.get('blue2').setMAC('00:00:00:00:00:02') 58 | 59 | net.get('s1').start([c0]) 60 | net.get('s2').start([c0]) 61 | os.popen('ip addr add 192.168.10.1/16 dev s1-eth10') 62 | os.popen('ip addr add 192.168.20.1/16 dev s2-eth10') 63 | os.popen('ovs-vsctl set interface s1-eth10 type=vxlan option:remote_ip=192.168.20.1 option:key=flow') 64 | os.popen('ovs-vsctl set interface s2-eth10 type=vxlan option:remote_ip=192.168.10.1 option:key=flow') 65 | 66 | CLI(net) 67 | net.stop() 68 | -------------------------------------------------------------------------------- /p4/mininet/README.md: -------------------------------------------------------------------------------- 1 | P4 2 | ==== 3 | 4 | ### Requirements: 5 | 6 | - p4_mininet.py 7 | - docker 8 | - p4 docker switch image 9 | - mininet python api 10 | 11 | ### Examples: 12 | - p4 fat tree 13 | - p4 + ovs example 14 | -------------------------------------------------------------------------------- /p4/mininet/fat_tree.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ''' 3 | Fat tree topology create by mininet 4 | Author : Yi Tseng 5 | ''' 6 | import logging 7 | import pdb 8 | import os 9 | from functools import partial 10 | 11 | from mininet.net import Mininet 12 | from mininet.node import Controller, RemoteController 13 | from mininet.node import OVSSwitch 14 | from mininet.cli import CLI 15 | from mininet.log import setLogLevel, info 16 | from mininet.link import Link, Intf, TCLink 17 | from mininet.topo import Topo 18 | from mininet.util import dumpNodeConnections 19 | from p4_mininet import P4DockerSwitch 20 | 21 | logging.basicConfig(level=logging.DEBUG) 22 | LOG = logging.getLogger('FatTreeTopo') 23 | lib_path = os.path.abspath(os.path.join('..', 'targets', 'load_balance', 'tests', 'pd_thrift')) 24 | 25 | class FatTreeTopo(Topo): 26 | core_switches = [] 27 | pods = [] 28 | 29 | def __init__(self, k=4, ac_bw=1000, pod_bw=100, ac_pkt_lost=5, pod_pkt_lost=0): 30 | ''' 31 | Param: 32 | k: k value for this fat tree(Default: 4) 33 | ac_bw: bandwidth between aggregation and core(Default 1Gbps) 34 | pod_bw: bandwidth between switches in pod(Default 100Mbps) 35 | ac_pkt_lost: packet lost between aggregation and core(Default: 5%) 36 | pod_pkt_lost: packet lost between switches in pod(Default: 0%) 37 | ''' 38 | self.num_pods = k 39 | self.num_cores = (k / 2) ** 2 40 | self.num_aggres = (k ** 2) / 2 41 | self.num_aggres_per_pod = self.num_aggres / self.num_pods 42 | self.num_edges = (k ** 2) / 2 43 | self.num_edges_per_pod = self.num_edges / self.num_pods 44 | self.num_host_per_edge = k / 2 45 | self.ac_bw = ac_bw 46 | self.pod_bw = pod_bw 47 | self.ac_pkt_lost = ac_pkt_lost 48 | self.pod_pkt_lost = pod_pkt_lost 49 | 50 | Topo.__init__(self) 51 | 52 | def create_core(self): 53 | ''' 54 | Create core switches 55 | ''' 56 | for i in range(0, self.num_cores): 57 | switch_name = 's1%d' % (i, ) 58 | self.core_switches.append(self.addSwitch(switch_name, target_name='p4dockerswitch', cls=P4DockerSwitch, sai_port=self.global_sai_port_count, pcap_dump=False)) 59 | self.global_sai_port_count = self.global_sai_port_count + 1 60 | 61 | def create_pods(self): 62 | ''' 63 | Create pods 64 | ''' 65 | for pi in range(0, self.num_pods): 66 | self.pods.append({'aggr': [], 'edge': []}) 67 | 68 | # create aggregation layer 69 | for ai in range(0, self.num_aggres_per_pod): 70 | switch_name = 's2%d%d' % (pi, ai, ) 71 | self.pods[pi]['aggr'].append(self.addSwitch(switch_name, target_name='p4dockerswitch', cls=P4DockerSwitch, sai_port=self.global_sai_port_count, pcap_dump=False)) 72 | self.global_sai_port_count = self.global_sai_port_count + 1 73 | 74 | # create edge layer 75 | for ei in range(0, self.num_edges_per_pod): 76 | switch_name = 's3%d%d' % (pi, ei, ) 77 | edge_switch = self.addSwitch(switch_name, target_name='p4dockerswitch', cls=P4DockerSwitch, sai_port=self.global_sai_port_count, pcap_dump=False) 78 | self.global_sai_port_count = self.global_sai_port_count + 1 79 | 80 | # create hosts 81 | for hi in range(0, self.num_host_per_edge): 82 | host_name = 'h%d%d%d' % (pi, ei, hi, ) 83 | host = self.addHost(host_name) 84 | self.addLink(edge_switch, host) 85 | 86 | # link aggregation and edge 87 | for aggr_switch in self.pods[pi]['aggr']: 88 | 89 | for edge_switch in self.pods[pi]['edge']: 90 | LOG.info('add link from %s to %s', aggr_switch, edge_switch) 91 | self.addLink(aggr_switch, 92 | edge_switch, 93 | bw=self.pod_bw, 94 | loss=self.pod_pkt_lost) 95 | 96 | def link_core_to_pods(self): 97 | ''' 98 | link aggregation layer to core layer 99 | aggregation switch can link to core switch directly 100 | pod number doesn't matter 101 | ex: 102 | k = 8 103 | 4 aggregation switch in a pod 104 | A0 -> C0~C3 105 | A1 -> C4~C7 106 | A2 -> C8~C11 107 | A3 -> C12~C15 108 | ''' 109 | num_cores_per_aggr = self.num_cores / self.num_aggres_per_pod 110 | 111 | for pod in self.pods: 112 | 113 | for ai in range(0, self.num_aggres_per_pod): # ai for aggregation index 114 | cis = ai * num_cores_per_aggr # start index 115 | cie = ai * num_cores_per_aggr + num_cores_per_aggr # end index 116 | 117 | for ci in range(cis, cie): # ci for core index 118 | aggr_switch = pod['aggr'][ai] 119 | core_switch = self.core_switches[ci] 120 | LOG.info('add link from %s to %s', aggr_switch, core_switch) 121 | self.addLink(aggr_switch, 122 | core_switch, 123 | bw=self.ac_bw, 124 | loss=self.ac_pkt_lost) 125 | 126 | def set_protocols_to_all_switch(self, protocols): 127 | pdb.set_trace() 128 | for core_switch in self.core_switches: 129 | set_protocol(core_switch) 130 | 131 | for pod in self.pods: 132 | 133 | for aggr_switch in pod['aggr']: 134 | set_protocol(aggr_switch) 135 | 136 | for edge_switch in pod['edge']: 137 | set_protocol(edge_switch) 138 | 139 | def init_fat_tree(self): 140 | self.global_sai_port_count = 25000 141 | self.create_core() 142 | self.create_pods() 143 | self.link_core_to_pods() 144 | 145 | if __name__ == '__main__': 146 | fat_tree = FatTreeTopo(4) 147 | fat_tree.init_fat_tree() 148 | net = Mininet(topo=fat_tree, controller=None) 149 | net.start() 150 | 151 | CLI(net) 152 | net.stop() 153 | 154 | -------------------------------------------------------------------------------- /p4/mininet/p4_and_ovs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # This file is fork from p4factory/mininet/swl_l2.py 4 | 5 | ############################################################################## 6 | # Topology with two switches and two hosts (static macs, no loops, no STP) 7 | # 8 | # 172.16.10.0/24 9 | # h1 -------- sw1(p4) -------------- sw2(ovs) --------h2 10 | # .1 .2 11 | ############################################################################## 12 | 13 | from mininet.net import Mininet, VERSION 14 | from mininet.log import setLogLevel, info 15 | from mininet.cli import CLI 16 | from distutils.version import StrictVersion 17 | from p4_mininet import P4DockerSwitch 18 | from time import sleep 19 | import sys 20 | 21 | def main(): 22 | net = Mininet( controller = None ) 23 | 24 | # add hosts 25 | h1 = net.addHost( 'h1', ip = '172.16.10.1/24' ) 26 | h2 = net.addHost( 'h2', ip = '172.16.10.2/24' ) 27 | 28 | # add switch 1 29 | sw1 = net.addSwitch( 'sw1', target_name = "p4dockerswitch", 30 | cls = P4DockerSwitch, pcap_dump = False ) 31 | 32 | # add switch 2 33 | sw2 = net.addSwitch('sw2') 34 | 35 | # add links 36 | if StrictVersion(VERSION) <= StrictVersion('2.2.0') : 37 | net.addLink( sw1, h1, port1 = 1 ) 38 | net.addLink( sw1, sw2, port1 = 2, port2 = 2 ) 39 | net.addLink( sw2, h2, port1 = 1 ) 40 | else: 41 | net.addLink( sw1, h1, port1 = 1, fast=False ) 42 | net.addLink( sw1, sw2, port1 = 2, port2 = 2, fast=False ) 43 | net.addLink( sw2, h2, port1 = 1, fast=False ) 44 | 45 | net.start() 46 | CLI( net ) 47 | net.stop() 48 | 49 | if __name__ == '__main__': 50 | setLogLevel( 'info' ) 51 | main() 52 | 53 | -------------------------------------------------------------------------------- /qos/create_queue.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # https://github.com/PeterDaveHello/ColorEchoForShell 3 | if [[ ! -s "ColorEcho.bash" ]]; then 4 | alias echo.BoldRed='echo' 5 | alias echo.BoldGreen='echo' 6 | alias echo.BoldYellow='echo' 7 | else 8 | . ColorEcho.bash 9 | fi 10 | 11 | if [ "$(id -u)" != "0" ]; then 12 | echo.Red "This script must be run as root" 1>&2 13 | exit 1 14 | fi 15 | 16 | ovs-vsctl -- get Port eth0 qos | grep -e '\[\]' &> /dev/null 17 | 18 | if [ $? != 0 ]; then 19 | echo.Red "Qos settings is not empty, please clear it first!" 20 | exit 1 21 | fi 22 | 23 | echo.Yellow "Setting up queue to $PORTS" 24 | PORTS="eth0 eth1 eth2 eth3" 25 | CMD="ovs-vsctl" 26 | 27 | for PORT in $PORTS; do 28 | CMD="$CMD -- set Port $PORT qos=@newqos" 29 | done 30 | 31 | CMD="$CMD -- --id=@newqos create QoS type=linux-htb other-config:max-rate=1000000000 queues=0=@q0,1=@q1" 32 | CMD="$CMD -- --id=@q0 create Queue other-config:min-rate=100000000 other-config:max-rate=100000000" 33 | CMD="$CMD -- --id=@q1 create Queue other-config:min-rate=500000000" 34 | 35 | $CMD 36 | 37 | echo.Green "Done" 38 | -------------------------------------------------------------------------------- /rest_event_test/README.md: -------------------------------------------------------------------------------- 1 | RESTful event call test 2 | =================== 3 | 4 | What is this? 5 | ------------- 6 | This is a test project for learning ryu event handling by using RESTful 7 | interface. 8 | RESTful service is create by [Django REST 9 | Framework](http://www.django-rest-framework.org/) 10 | 11 | Structure 12 | --------- 13 |  14 | 15 | Install 16 | ------- 17 | Before use it, you need to install: 18 | 19 | > 1. [Python 2.7.6 or higher](https://www.python.org/) 20 | > 2. [Ryu SDN Framework](https://osrg.github.io/ryu/) 21 | > 3. [Django](https://www.djangoproject.com/) 22 | > 4. [Django REST Framework](http://www.django-rest-framework.org/) 23 | 24 | How to use it? 25 | -------------- 26 | Start ryu application 27 | > $ ryu-manager simple_event_app.py 28 | 29 | Start restful server 30 | > $ python manager runserver 0.0.0.0:8000 31 | 32 | Send request to RESTful service 33 | > $ curl localhost:8000/hello 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /rest_event_test/rest_server/db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yi-Tseng/SDN-Work/cb0e6d8680bfb625b7735089a19aac73fc262693/rest_event_test/rest_server/db.sqlite3 -------------------------------------------------------------------------------- /rest_event_test/rest_server/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "rest_server.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /rest_event_test/rest_server/rest_app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yi-Tseng/SDN-Work/cb0e6d8680bfb625b7735089a19aac73fc262693/rest_event_test/rest_server/rest_app/__init__.py -------------------------------------------------------------------------------- /rest_event_test/rest_server/rest_app/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /rest_event_test/rest_server/rest_app/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yi-Tseng/SDN-Work/cb0e6d8680bfb625b7735089a19aac73fc262693/rest_event_test/rest_server/rest_app/migrations/__init__.py -------------------------------------------------------------------------------- /rest_event_test/rest_server/rest_app/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /rest_event_test/rest_server/rest_app/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /rest_event_test/rest_server/rest_app/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from rest_app.views import HelloView 3 | 4 | urlpatterns = [ 5 | url(r'^hello$', HelloView.as_view()), 6 | ] 7 | -------------------------------------------------------------------------------- /rest_event_test/rest_server/rest_app/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework.response import Response 2 | from rest_framework.views import APIView 3 | import os 4 | import socket 5 | 6 | # Create your views here. 7 | 8 | SOCKFILE = '/tmp/hello_sock' 9 | 10 | class HelloView(APIView): 11 | 12 | def get(self, format=None): 13 | # send event by using Unix Domain Socket 14 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) 15 | 16 | try: 17 | sock.connect(SOCKFILE) 18 | sock.sendall('{"Hello": "World"}') 19 | except Exception, ex: 20 | print ex 21 | print 'connect error' 22 | 23 | return Response({}) 24 | 25 | -------------------------------------------------------------------------------- /rest_event_test/rest_server/rest_server/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yi-Tseng/SDN-Work/cb0e6d8680bfb625b7735089a19aac73fc262693/rest_event_test/rest_server/rest_server/__init__.py -------------------------------------------------------------------------------- /rest_event_test/rest_server/rest_server/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for rest_server project. 3 | 4 | For more information on this file, see 5 | https://docs.djangoproject.com/en/1.7/topics/settings/ 6 | 7 | For the full list of settings and their values, see 8 | https://docs.djangoproject.com/en/1.7/ref/settings/ 9 | """ 10 | 11 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 12 | import os 13 | BASE_DIR = os.path.dirname(os.path.dirname(__file__)) 14 | 15 | 16 | # Quick-start development settings - unsuitable for production 17 | # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ 18 | 19 | # SECURITY WARNING: keep the secret key used in production secret! 20 | SECRET_KEY = '%wkvyxw23h-q=yg+ukis7!tbkk9b#4psn#8(r0-kd23x=+j(74' 21 | 22 | # SECURITY WARNING: don't run with debug turned on in production! 23 | DEBUG = True 24 | 25 | TEMPLATE_DEBUG = True 26 | 27 | ALLOWED_HOSTS = [] 28 | 29 | 30 | # Application definition 31 | 32 | INSTALLED_APPS = ( 33 | 'django.contrib.admin', 34 | 'django.contrib.auth', 35 | 'django.contrib.contenttypes', 36 | 'django.contrib.sessions', 37 | 'django.contrib.messages', 38 | 'django.contrib.staticfiles', 39 | 'rest_framework', 40 | 'rest_app', 41 | ) 42 | 43 | MIDDLEWARE_CLASSES = ( 44 | 'django.contrib.sessions.middleware.SessionMiddleware', 45 | 'django.middleware.common.CommonMiddleware', 46 | 'django.middleware.csrf.CsrfViewMiddleware', 47 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 48 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 49 | 'django.contrib.messages.middleware.MessageMiddleware', 50 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 51 | ) 52 | 53 | ROOT_URLCONF = 'rest_server.urls' 54 | 55 | WSGI_APPLICATION = 'rest_server.wsgi.application' 56 | 57 | 58 | # Database 59 | # https://docs.djangoproject.com/en/1.7/ref/settings/#databases 60 | 61 | DATABASES = { 62 | 'default': { 63 | 'ENGINE': 'django.db.backends.sqlite3', 64 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 65 | } 66 | } 67 | 68 | # Internationalization 69 | # https://docs.djangoproject.com/en/1.7/topics/i18n/ 70 | 71 | LANGUAGE_CODE = 'en-us' 72 | 73 | TIME_ZONE = 'UTC' 74 | 75 | USE_I18N = True 76 | 77 | USE_L10N = True 78 | 79 | USE_TZ = True 80 | 81 | 82 | # Static files (CSS, JavaScript, Images) 83 | # https://docs.djangoproject.com/en/1.7/howto/static-files/ 84 | 85 | STATIC_URL = '/static/' 86 | -------------------------------------------------------------------------------- /rest_event_test/rest_server/rest_server/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, include, url 2 | from django.contrib import admin 3 | 4 | urlpatterns = patterns('', 5 | # Examples: 6 | # url(r'^$', 'rest_server.views.home', name='home'), 7 | # url(r'^blog/', include('blog.urls')), 8 | url(r'^', include('rest_app.urls')), 9 | ) 10 | -------------------------------------------------------------------------------- /rest_event_test/rest_server/rest_server/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for rest_server project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "rest_server.settings") 12 | 13 | from django.core.wsgi import get_wsgi_application 14 | application = get_wsgi_application() 15 | -------------------------------------------------------------------------------- /rest_event_test/ryu_app/simple_event_app.py: -------------------------------------------------------------------------------- 1 | from ryu.controller import ofp_event 2 | from ryu.base import app_manager 3 | import simple_event_lib 4 | from ryu.controller.handler import MAIN_DISPATCHER 5 | from ryu.controller.handler import set_ev_cls 6 | from ryu.ofproto import ofproto_v1_3 7 | 8 | 9 | class SimpleEventApp(app_manager.RyuApp): 10 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 11 | _CONTEXTS = {'evlib': simple_event_lib.SimpleEventLib} 12 | 13 | def __init__(self, *args, **kwargs): 14 | super(SimpleEventApp, self).__init__(*args, **kwargs) 15 | self.evlib = kwargs['evlib'] 16 | self.evlib.config = {} 17 | self.evlib.start_sock_server() 18 | 19 | @set_ev_cls(simple_event_lib.SimpleEvent, MAIN_DISPATCHER) 20 | def simple_event_handler(self, ev): 21 | print "Get simple event" 22 | print "Message:", ev.msg 23 | 24 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 25 | def _packet_in_handler(self, ev): 26 | pass 27 | -------------------------------------------------------------------------------- /rest_event_test/ryu_app/simple_event_lib.py: -------------------------------------------------------------------------------- 1 | import os 2 | from ryu.lib import hub 3 | from ryu.base import app_manager 4 | from ryu.controller import event 5 | import json 6 | 7 | SOCKFILE = '/tmp/hello_sock' 8 | 9 | class SimpleEvent(event.EventBase): 10 | 11 | def __init__(self, msg): 12 | super(SimpleEvent, self).__init__() 13 | self.msg = msg 14 | 15 | class SimpleEventLib(app_manager.RyuApp): 16 | def __init__(self): 17 | super(SimpleEventLib, self).__init__() 18 | self.config = {} 19 | self.sock = None 20 | 21 | def recv_loop(self): 22 | 23 | print "start loop" 24 | while True: 25 | print 'wait for recev' 26 | data = self.sock.recv(1024) 27 | print 'get data:', data 28 | msg = json.loads(data) 29 | print 'msg is:', msg 30 | if msg: 31 | self.send_event_to_observers(SimpleEvent(msg)) 32 | print 'sent to ovservers' 33 | 34 | def start_sock_server(self): 35 | if os.path.exists(SOCKFILE): 36 | os.unlink(SOCKFILE) 37 | 38 | self.sock = hub.socket.socket(hub.socket.AF_UNIX, hub.socket.SOCK_DGRAM) 39 | self.sock.bind(SOCKFILE) 40 | hub.spawn(self.recv_loop) 41 | -------------------------------------------------------------------------------- /rest_event_test/structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yi-Tseng/SDN-Work/cb0e6d8680bfb625b7735089a19aac73fc262693/rest_event_test/structure.png -------------------------------------------------------------------------------- /tinyrpc/client.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | from ryu.contrib.tinyrpc.client import RPCClient 5 | from ryu.contrib.tinyrpc.protocols.jsonrpc import JSONRPCProtocol 6 | from ryu.contrib.tinyrpc.transports.http import HttpPostClientTransport 7 | 8 | def main(): 9 | protocol = JSONRPCProtocol() 10 | transport = HttpPostClientTransport('127.0.0.1') 11 | rpc_client = RPCClient(protocol, transport) 12 | 13 | print rpc_client.call('hello', None, None) 14 | 15 | 16 | if __name__ == '__main__': 17 | main() 18 | -------------------------------------------------------------------------------- /tinyrpc/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | from ryu.contrib.tinyrpc.transports.wsgi import WsgiServerTransport 5 | from ryu.contrib.tinyrpc.server import RPCServer 6 | from ryu.contrib.tinyrpc.protocols.jsonrpc import JSONRPCProtocol 7 | from ryu.contrib.tinyrpc.dispatch import RPCDispatcher 8 | 9 | def hello(): 10 | return 'Hello world' 11 | 12 | def main(): 13 | transport = WsgiServerTransport() 14 | protocol = JSONRPCProtocol() 15 | dispatcher = RPCDispatcher() 16 | dispatcher.add_method(hello) 17 | 18 | rpc_server = RPCServer(transport, protocol, dispatcher) 19 | rpc_server.serve_forever() 20 | 21 | 22 | 23 | if __name__ == '__main__': 24 | main() 25 | --------------------------------------------------------------------------------