openflow-controller 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/controller.rb +138 -0
- data/lib/openflow-controller.rb +1 -0
- data/lib/switch.rb +52 -0
- metadata +47 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d03f70b6b79ae5f78ceea79beb46a1c0231c66b5
|
4
|
+
data.tar.gz: 175302796e704286f52912508d8e8c3bedbd947f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2c327dc1711de1ffaa374458189dc7999c9c8db40035be47adf2c3d37add1470df5a514bcfb86f938c256c9f9fad4fd7a9e6925d042e851ccd3e31b3f5bf9bdf
|
7
|
+
data.tar.gz: 34465269b491f1ad763d1811624968c9edb9548a77bbcedffdbd9cd77c17705949ef583ef3d1548960ee1aa17376788338cf9536ad0db76b68ee4e62ac93aeb2
|
data/lib/controller.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'logger'
|
3
|
+
require_relative 'switch'
|
4
|
+
|
5
|
+
class Numeric
|
6
|
+
def sec; self end
|
7
|
+
end
|
8
|
+
|
9
|
+
class OFController
|
10
|
+
DEFAULT_IP_ADDRESS = '0.0.0.0'
|
11
|
+
DEFAULT_TCP_PORT = 6633
|
12
|
+
|
13
|
+
def self.inherited(subclass)
|
14
|
+
@controller_class = subclass
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.create(*args)
|
18
|
+
@controller_class.new(*args)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.timer_event(handler, options)
|
22
|
+
@timer_handlers ||= {}
|
23
|
+
@timer_handlers[handler] = options.fetch(:interval)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.timer_handlers
|
27
|
+
@timer_handlers || {}
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_reader :logger
|
31
|
+
|
32
|
+
def initialize(level = Logger::INFO)
|
33
|
+
@switches = {}
|
34
|
+
@logger = Logger.new($stdout).tap do |logger|
|
35
|
+
logger.formatter = proc do |severity, datetime, _progname, msg|
|
36
|
+
"#{datetime} (#{severity}) -- #{msg}\n"
|
37
|
+
end
|
38
|
+
logger.level = level
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def run(ip, port, *args)
|
43
|
+
maybe_send_handler :start, *args
|
44
|
+
socket = TCPServer.open(ip, port)
|
45
|
+
logger.info "Controller running on #{ip}:#{port}."
|
46
|
+
start_timers
|
47
|
+
loop { start_switch_thread socket.accept }
|
48
|
+
end
|
49
|
+
|
50
|
+
def send_message(datapath_id, msg)
|
51
|
+
@switches.fetch(datapath_id).send(msg)
|
52
|
+
end
|
53
|
+
|
54
|
+
def start(*_args) end
|
55
|
+
def switch_ready(_datapath_id) end
|
56
|
+
def echo_request(datapath_id, msg)
|
57
|
+
send_message datapath_id, OFEchoReply.new(xid: msg.xid)
|
58
|
+
end
|
59
|
+
def packet_in(_datapath_id, _msg) end
|
60
|
+
def port_add(_datapath_id, _msg) end
|
61
|
+
def port_delete(_datapath_id, _msg) end
|
62
|
+
def port_modify(_datapath_id, _msg) end
|
63
|
+
def flow_removed(_datapath_id, _msg) end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def maybe_send_handler(handler, *args)
|
68
|
+
@handler_mutex ||= Mutex.new
|
69
|
+
@handler_mutex.synchronize do
|
70
|
+
__send__(handler, *args) if respond_to?(handler)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def start_timers
|
75
|
+
self.class.timer_handlers.each do |handler, interval|
|
76
|
+
Thread.new do
|
77
|
+
loop do
|
78
|
+
maybe_send_handler handler
|
79
|
+
sleep interval
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def start_switch_thread(socket)
|
86
|
+
logger.debug 'Socket accepted.'
|
87
|
+
Thread.new do
|
88
|
+
switch = create_and_register_new_switch(socket)
|
89
|
+
start_switch_main(switch.datapath_id)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def create_and_register_new_switch(socket)
|
94
|
+
switch = OFSwitch.new(self, socket)
|
95
|
+
@switches[switch.datapath_id] = switch
|
96
|
+
end
|
97
|
+
|
98
|
+
def start_switch_main(datapath_id)
|
99
|
+
logger.info "Switch #{datapath_id} is ready."
|
100
|
+
maybe_send_handler :switch_ready, datapath_id
|
101
|
+
loop { handle_openflow_message(datapath_id) }
|
102
|
+
rescue => exception
|
103
|
+
logger.debug "Switch #{datapath_id} error: #{exception}."
|
104
|
+
unregister_switch(datapath_id)
|
105
|
+
end
|
106
|
+
|
107
|
+
def unregister_switch(datapath_id)
|
108
|
+
@switches.delete(datapath_id)
|
109
|
+
logger.info "Switch #{datapath_id} is disconnected."
|
110
|
+
maybe_send_handler :switch_disconnected, datapath_id
|
111
|
+
end
|
112
|
+
|
113
|
+
def handle_openflow_message(datapath_id)
|
114
|
+
msg = @switches.fetch(datapath_id).receive
|
115
|
+
|
116
|
+
case msg
|
117
|
+
when OFEchoRequest
|
118
|
+
maybe_send_handler :echo_request, datapath_id, msg
|
119
|
+
when OFFeaturesReply
|
120
|
+
maybe_send_handler :features_reply, datapath_id, msg
|
121
|
+
when OFPacketIn
|
122
|
+
maybe_send_handler :packet_in, datapath_id, msg
|
123
|
+
when OFPortStatus
|
124
|
+
case msg.reason
|
125
|
+
when :add
|
126
|
+
maybe_send_handler :port_add, datapath_id, msg
|
127
|
+
when :delete
|
128
|
+
maybe_send_handler :port_delete, datapath_id, msg
|
129
|
+
when :modify
|
130
|
+
maybe_send_handler :port_modify, datapath_id, msg
|
131
|
+
# else
|
132
|
+
end
|
133
|
+
when OFFlowRemoved
|
134
|
+
maybe_send_handler :flow_removed, datapath_id, msg
|
135
|
+
# else
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'controller'
|
data/lib/switch.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'openflow-protocol'
|
2
|
+
|
3
|
+
class OFSwitch
|
4
|
+
attr_reader :controller
|
5
|
+
|
6
|
+
def initialize(controller, socket)
|
7
|
+
@controller = controller
|
8
|
+
@socket = socket
|
9
|
+
begin
|
10
|
+
exchange_hello_messages
|
11
|
+
exchange_echo_messages
|
12
|
+
exchange_features_messages
|
13
|
+
rescue => exception
|
14
|
+
controller.logger.debug "Switch error: #{exception}."
|
15
|
+
raise exception
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def send(msg)
|
20
|
+
@socket.write msg.to_binary_s
|
21
|
+
end
|
22
|
+
|
23
|
+
def receive
|
24
|
+
OFParser.read @socket
|
25
|
+
end
|
26
|
+
|
27
|
+
def datapath_id
|
28
|
+
@features_reply.datapath_id
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def exchange_hello_messages
|
34
|
+
controller.logger.debug 'Wait OFPT_HELLO.'
|
35
|
+
fail unless receive.is_a?(OFHello)
|
36
|
+
send OFHello.new
|
37
|
+
end
|
38
|
+
|
39
|
+
def exchange_echo_messages
|
40
|
+
send OFEchoRequest.new
|
41
|
+
controller.logger.debug 'Wait OFPT_ECHO_REPLY.'
|
42
|
+
fail unless receive.is_a?(OFEchoReply)
|
43
|
+
end
|
44
|
+
|
45
|
+
def exchange_features_messages
|
46
|
+
send OFFeaturesRequest.new
|
47
|
+
controller.logger.debug 'Wait OFPT_FEATURES_REPLY.'
|
48
|
+
@features_reply = receive
|
49
|
+
controller.logger.debug "OFPT_FEATURES_REPLY.datapath_id: #{datapath_id}."
|
50
|
+
fail unless @features_reply.is_a?(OFFeaturesReply)
|
51
|
+
end
|
52
|
+
end
|
metadata
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: openflow-controller
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jérémy Pagé
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-21 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: An OpenFlow Controller.
|
14
|
+
email:
|
15
|
+
- contact@jeremypage.me
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/controller.rb
|
21
|
+
- lib/openflow-controller.rb
|
22
|
+
- lib/switch.rb
|
23
|
+
homepage: https://github.com/jejepage/openflow-controller
|
24
|
+
licenses:
|
25
|
+
- MIT
|
26
|
+
metadata: {}
|
27
|
+
post_install_message:
|
28
|
+
rdoc_options: []
|
29
|
+
require_paths:
|
30
|
+
- lib
|
31
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
requirements: []
|
42
|
+
rubyforge_project:
|
43
|
+
rubygems_version: 2.4.8
|
44
|
+
signing_key:
|
45
|
+
specification_version: 4
|
46
|
+
summary: OpenFlow Controller
|
47
|
+
test_files: []
|