blob: 3561d9d6824e9ed3bab8c5446c3da963c0cf55ea [file] [log] [blame]
Zsolt Haraszti023ea7c2016-10-16 19:30:34 -07001#
2# Copyright 2016 the original author or authors.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16import structlog
Zsolt Haraszti2bdb6b32016-11-03 16:56:17 -070017from twisted.internet.defer import inlineCallbacks, returnValue
Zsolt Haraszti023ea7c2016-10-16 19:30:34 -070018
19import loxi.of13 as ofp
20from converter import to_loxi, pb2dict, to_grpc
21
22log = structlog.get_logger()
23
24
25class OpenFlowProtocolError(Exception): pass
26
27
28class OpenFlowProtocolHandler(object):
29
Zsolt Haraszticd22adc2016-10-25 00:13:06 -070030 def __init__(self, datapath_id, device_id, agent, cxn, rpc):
Zsolt Haraszti023ea7c2016-10-16 19:30:34 -070031 """
32 The upper half of the OpenFlow protocol, focusing on message
33 exchanges.
34 :param agent: Reference to the Agent() instance, can be used to
35 indicate critical errors to break the connection.
36 :param cxn: The lower level message serdes part of the OF protocol.
37 :param rpc: The application level stub on which RPC calls
38 are made as result of processing incoming OpenFlow request messages.
39 """
40 self.datapath_id = datapath_id
Zsolt Haraszticd22adc2016-10-25 00:13:06 -070041 self.device_id = device_id
Zsolt Haraszti023ea7c2016-10-16 19:30:34 -070042 self.agent = agent
43 self.cxn = cxn
44 self.rpc = rpc
45
46 @inlineCallbacks
Zsolt Haraszti2bdb6b32016-11-03 16:56:17 -070047 def start(self):
Zsolt Haraszti023ea7c2016-10-16 19:30:34 -070048 """A new call is made after a fresh reconnect"""
49
Zsolt Haraszti2bdb6b32016-11-03 16:56:17 -070050 log.debug('starting')
51
Zsolt Haraszti023ea7c2016-10-16 19:30:34 -070052 try:
53 # send initial hello message
54 self.cxn.send(ofp.message.hello())
55
56 # expect to receive a hello message
57 msg = yield self.cxn.recv_class(ofp.message.hello)
58 # TODO verify version compatibility (must list version 1.3)
59
60 while True:
61 req = yield self.cxn.recv_any()
62 handler = self.main_handlers.get(req.type, None)
63 if handler:
64 handler(self, req)
65 else:
66 log.error('cannot-handle',
67 request=req, xid=req.xid, type=req.type)
68
69 except Exception, e:
70 log.exception('exception', e=e)
71
Zsolt Haraszti2bdb6b32016-11-03 16:56:17 -070072 log.info('started')
73 returnValue(self)
74
75 def stop(self):
76 log.debug('stopping')
77 pass # nothing to do yet
78 log.info('stopped')
79
Zsolt Haraszti023ea7c2016-10-16 19:30:34 -070080 def handle_echo_request(self, req):
81 self.cxn.send(ofp.message.echo_reply(xid=req.xid))
82
83 @inlineCallbacks
84 def handle_feature_request(self, req):
Zsolt Haraszticd22adc2016-10-25 00:13:06 -070085 device_info = yield self.rpc.get_device_info(self.device_id)
Zsolt Haraszti023ea7c2016-10-16 19:30:34 -070086 kw = pb2dict(device_info.switch_features)
87 self.cxn.send(ofp.message.features_reply(
88 xid=req.xid,
89 datapath_id=self.datapath_id,
90 **kw))
91
92 def handle_stats_request(self, req):
93 handler = self.stats_handlers.get(req.stats_type, None)
94 if handler:
95 handler(self, req)
96 else:
97 raise OpenFlowProtocolError(
98 'Cannot handle stats request type "{}"'.format(req.stats_type))
99
100 def handle_barrier_request(self, req):
101 # TODO not really doing barrier yet, but we respond
102 self.cxn.send(ofp.message.barrier_reply(xid=req.xid))
103
104 def handle_experimenter_request(self, req):
105 raise NotImplementedError()
106
107 @inlineCallbacks
108 def handle_flow_mod_request(self, req):
Zsolt Haraszticd22adc2016-10-25 00:13:06 -0700109 yield self.rpc.update_flow_table(self.device_id, to_grpc(req))
Zsolt Haraszti023ea7c2016-10-16 19:30:34 -0700110
111 def handle_get_async_request(self, req):
112 raise NotImplementedError()
113
114 def handle_get_config_request(self, req):
115 self.cxn.send(ofp.message.get_config_reply(
116 xid=req.xid,
117 miss_send_len=ofp.OFPCML_NO_BUFFER
118 ))
119
Zsolt Haraszti8a774382016-10-24 18:25:54 -0700120 @inlineCallbacks
Zsolt Haraszti023ea7c2016-10-16 19:30:34 -0700121 def handle_group_mod_request(self, req):
Zsolt Haraszticd22adc2016-10-25 00:13:06 -0700122 yield self.rpc.update_group_table(self.device_id, to_grpc(req))
Zsolt Haraszti023ea7c2016-10-16 19:30:34 -0700123
124 def handle_meter_mod_request(self, req):
125 raise NotImplementedError()
126
127 def handle_role_request(self, req):
128 # TODO this is where we need to manage which connection is active
129 if req.role != ofp.OFPCR_ROLE_MASTER:
130 raise NotImplementedError()
131 self.cxn.send(ofp.message.role_reply(
132 xid=req.xid, role=req.role, generation_id=req.generation_id))
133
134 def handle_packet_out_request(self, req):
Zsolt Haraszticd22adc2016-10-25 00:13:06 -0700135 self.rpc.send_packet_out(self.device_id, to_grpc(req))
Zsolt Haraszti023ea7c2016-10-16 19:30:34 -0700136
137 def handle_set_config_request(self, req):
138 # TODO ignore for now
139 pass
140
141 def handle_port_mod_request(self, req):
142 raise NotImplementedError()
143
144 def handle_table_mod_request(self, req):
145 raise NotImplementedError()
146
147 def handle_queue_get_config_request(self, req):
148 raise NotImplementedError()
149
150 def handle_set_async_request(self, req):
151 raise NotImplementedError()
152
153 def handle_aggregate_request(self, req):
154 raise NotImplementedError
155
156 @inlineCallbacks
157 def handle_device_description_request(self, req):
Zsolt Haraszticd22adc2016-10-25 00:13:06 -0700158 device_info = yield self.rpc.get_device_info(self.device_id)
Zsolt Haraszti023ea7c2016-10-16 19:30:34 -0700159 kw = pb2dict(device_info.desc)
160 self.cxn.send(ofp.message.desc_stats_reply(xid=req.xid, **kw))
161
162 def handle_experimenter_stats_request(self, req):
163 raise NotImplementedError()
164
165 @inlineCallbacks
166 def handle_flow_stats_request(self, req):
Zsolt Haraszticd22adc2016-10-25 00:13:06 -0700167 flow_stats = yield self.rpc.list_flows(self.device_id)
Zsolt Haraszti023ea7c2016-10-16 19:30:34 -0700168 self.cxn.send(ofp.message.flow_stats_reply(
169 xid=req.xid, entries=[to_loxi(f) for f in flow_stats]))
170
Zsolt Haraszti8a774382016-10-24 18:25:54 -0700171 @inlineCallbacks
Zsolt Haraszti023ea7c2016-10-16 19:30:34 -0700172 def handle_group_stats_request(self, req):
Zsolt Haraszticd22adc2016-10-25 00:13:06 -0700173 group_stats = yield self.rpc.list_groups(self.device_id)
Zsolt Haraszti023ea7c2016-10-16 19:30:34 -0700174 self.cxn.send(ofp.message.group_stats_reply(
Zsolt Haraszti8a774382016-10-24 18:25:54 -0700175 xid=req.xid, entries=[to_loxi(g) for g in group_stats]))
Zsolt Haraszti023ea7c2016-10-16 19:30:34 -0700176
177 def handle_group_descriptor_request(self, req):
178 group_list = [] # TODO
179 self.cxn.send(ofp.message.group_desc_stats_reply(
180 xid=req.xid, entries=group_list))
181
182 def handle_group_features_request(self, req):
183 raise NotImplementedError()
184
185 def handle_meter_stats_request(self, req):
186 meter_stats = [] # TODO
187 self.cxn.send(ofp.message.meter_stats_reply(
188 xid=req.xid, entries=meter_stats))
189
190 def handle_meter_config_request(self, req):
191 raise NotImplementedError()
192
193 def handle_meter_features_request(self, req):
alshabib81824e32016-12-21 21:43:45 -0800194 self.cxn.send(ofp.message.bad_request_error_msg())
Zsolt Haraszti023ea7c2016-10-16 19:30:34 -0700195
196 def handle_port_stats_request(self, req):
197 port_stats = [] # TODO
198 self.cxn.send(ofp.message.port_stats_reply(
199 xid=req.xid,entries=port_stats))
200
201 @inlineCallbacks
202 def handle_port_desc_request(self, req):
Zsolt Haraszticd22adc2016-10-25 00:13:06 -0700203 port_list = yield self.rpc.get_port_list(self.device_id)
Zsolt Haraszti023ea7c2016-10-16 19:30:34 -0700204 self.cxn.send(ofp.message.port_desc_stats_reply(
205 xid=req.xid,
206 #flags=None,
Zsolt Haraszti66862032016-11-28 14:28:39 -0800207 entries=[to_loxi(port.ofp_port) for port in port_list]
Zsolt Haraszti023ea7c2016-10-16 19:30:34 -0700208 ))
209
210 def handle_queue_stats_request(self, req):
211 raise NotImplementedError()
212
213 def handle_table_stats_request(self, req):
214 table_stats = [] # TODO
215 self.cxn.send(ofp.message.table_stats_reply(
216 xid=req.xid, entries=table_stats))
217
218 def handle_table_features_request(self, req):
219 raise NotImplementedError()
220
221 stats_handlers = {
222 ofp.OFPST_AGGREGATE: handle_aggregate_request,
223 ofp.OFPST_DESC: handle_device_description_request,
224 ofp.OFPST_EXPERIMENTER: handle_experimenter_stats_request,
225 ofp.OFPST_FLOW: handle_flow_stats_request,
226 ofp.OFPST_GROUP: handle_group_stats_request,
227 ofp.OFPST_GROUP_DESC: handle_group_descriptor_request,
228 ofp.OFPST_GROUP_FEATURES: handle_group_features_request,
229 ofp.OFPST_METER: handle_meter_stats_request,
230 ofp.OFPST_METER_CONFIG: handle_meter_config_request,
231 ofp.OFPST_METER_FEATURES: handle_meter_features_request,
232 ofp.OFPST_PORT: handle_port_stats_request,
233 ofp.OFPST_PORT_DESC: handle_port_desc_request,
234 ofp.OFPST_QUEUE: handle_queue_stats_request,
235 ofp.OFPST_TABLE: handle_table_stats_request,
236 ofp.OFPST_TABLE_FEATURES: handle_table_features_request
237 }
238
239 main_handlers = {
240 ofp.OFPT_BARRIER_REQUEST: handle_barrier_request,
241 ofp.OFPT_ECHO_REQUEST: handle_echo_request,
242 ofp.OFPT_FEATURES_REQUEST: handle_feature_request,
243 ofp.OFPT_EXPERIMENTER: handle_experimenter_request,
244 ofp.OFPT_FLOW_MOD: handle_flow_mod_request,
245 ofp.OFPT_GET_ASYNC_REQUEST: handle_get_async_request,
246 ofp.OFPT_GET_CONFIG_REQUEST: handle_get_config_request,
247 ofp.OFPT_GROUP_MOD: handle_group_mod_request,
248 ofp.OFPT_METER_MOD: handle_meter_mod_request,
249 ofp.OFPT_PACKET_OUT: handle_packet_out_request,
250 ofp.OFPT_PORT_MOD: handle_port_mod_request,
251 ofp.OFPT_QUEUE_GET_CONFIG_REQUEST: handle_queue_get_config_request,
252 ofp.OFPT_ROLE_REQUEST: handle_role_request,
253 ofp.OFPT_SET_ASYNC: handle_set_async_request,
254 ofp.OFPT_SET_CONFIG: handle_set_config_request,
255 ofp.OFPT_STATS_REQUEST: handle_stats_request,
256 ofp.OFPT_TABLE_MOD: handle_table_mod_request,
257 }
258
Zsolt Haraszticd22adc2016-10-25 00:13:06 -0700259 def forward_packet_in(self, ofp_packet_in):
Zsolt Haraszti8925d1f2016-12-21 00:45:19 -0800260 log.info('sending-packet-in', ofp_packet_in=ofp_packet_in)
Zsolt Haraszticd22adc2016-10-25 00:13:06 -0700261 self.cxn.send(to_loxi(ofp_packet_in))
Zsolt Haraszti217a12e2016-12-19 16:37:55 -0800262
263 def forward_port_status(self, ofp_port_status):
264 self.cxn.send(to_loxi(ofp_port_status))