second try
[vsorcdistro/.git] / ryu / ryu / controller / ofp_handler.py
1 # Copyright (C) 2011, 2012 Nippon Telegraph and Telephone Corporation.
2 # Copyright (C) 2011, 2012 Isaku Yamahata <yamahata at valinux co jp>
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
13 # implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 """
18 Basic OpenFlow handling including negotiation.
19 """
20
21 import itertools
22 import logging
23 import warnings
24
25 import ryu.base.app_manager
26
27 from ryu.lib import hub
28 from ryu import utils
29 from ryu.controller import ofp_event
30 from ryu.controller.controller import OpenFlowController
31 from ryu.controller.handler import set_ev_handler
32 from ryu.controller.handler import HANDSHAKE_DISPATCHER, CONFIG_DISPATCHER,\
33     MAIN_DISPATCHER
34 from ryu.ofproto import ofproto_parser
35
36
37 # The state transition: HANDSHAKE -> CONFIG -> MAIN
38 #
39 # HANDSHAKE: if it receives HELLO message with the valid OFP version,
40 # sends Features Request message, and moves to CONFIG.
41 #
42 # CONFIG: it receives Features Reply message and moves to MAIN
43 #
44 # MAIN: it does nothing. Applications are expected to register their
45 # own handlers.
46 #
47 # Note that at any state, when we receive Echo Request message, send
48 # back Echo Reply message.
49
50
51 class OFPHandler(ryu.base.app_manager.RyuApp):
52     def __init__(self, *args, **kwargs):
53         super(OFPHandler, self).__init__(*args, **kwargs)
54         self.name = ofp_event.NAME
55         self.controller = None
56
57     def start(self):
58         super(OFPHandler, self).start()
59         self.controller = OpenFlowController()
60         return hub.spawn(self.controller)
61
62     def _hello_failed(self, datapath, error_desc):
63         self.logger.error('%s on datapath %s', error_desc, datapath.address)
64         error_msg = datapath.ofproto_parser.OFPErrorMsg(
65             datapath=datapath,
66             type_=datapath.ofproto.OFPET_HELLO_FAILED,
67             code=datapath.ofproto.OFPHFC_INCOMPATIBLE,
68             data=error_desc)
69         datapath.send_msg(error_msg, close_socket=True)
70
71     @set_ev_handler(ofp_event.EventOFPHello, HANDSHAKE_DISPATCHER)
72     def hello_handler(self, ev):
73         self.logger.debug('hello ev %s', ev)
74         msg = ev.msg
75         datapath = msg.datapath
76
77         # check if received version is supported.
78         # pre 1.0 is not supported
79         elements = getattr(msg, 'elements', None)
80         if elements:
81             switch_versions = set()
82             for version in itertools.chain.from_iterable(
83                     element.versions for element in elements):
84                 switch_versions.add(version)
85             usable_versions = switch_versions & set(
86                 datapath.supported_ofp_version)
87
88             # We didn't send our supported versions for interoperability as
89             # most switches would not understand elements at the moment.
90             # So the switch would think that the negotiated version would
91             # be max(negotiated_versions), but actual usable version is
92             # max(usable_versions).
93             negotiated_versions = set(
94                 version for version in switch_versions
95                 if version <= max(datapath.supported_ofp_version))
96             if negotiated_versions and not usable_versions:
97                 # e.g.
98                 # versions of OF 1.0 and 1.1 from switch
99                 # max of OF 1.2 from Ryu and supported_ofp_version = (1.2, )
100                 # negotiated version = 1.1
101                 # usable version = None
102                 error_desc = (
103                     'no compatible version found: '
104                     'switch versions %s controller version 0x%x, '
105                     'the negotiated version is 0x%x, '
106                     'but no usable version found. '
107                     'If possible, set the switch to use one of OF version %s'
108                     % (switch_versions, max(datapath.supported_ofp_version),
109                        max(negotiated_versions),
110                        sorted(datapath.supported_ofp_version)))
111                 self._hello_failed(datapath, error_desc)
112                 return
113             if (negotiated_versions and usable_versions and
114                     max(negotiated_versions) != max(usable_versions)):
115                 # e.g.
116                 # versions of OF 1.0 and 1.1 from switch
117                 # max of OF 1.2 from Ryu and supported_ofp_version = (1.0, 1.2)
118                 # negotiated version = 1.1
119                 # usable version = 1.0
120                 #
121                 # TODO: In order to get the version 1.0, Ryu need to send
122                 # supported verions.
123                 error_desc = (
124                     'no compatible version found: '
125                     'switch versions 0x%x controller version 0x%x, '
126                     'the negotiated version is %s but found usable %s. '
127                     'If possible, '
128                     'set the switch to use one of OF version %s' % (
129                         max(switch_versions),
130                         max(datapath.supported_ofp_version),
131                         sorted(negotiated_versions),
132                         sorted(usable_versions), sorted(usable_versions)))
133                 self._hello_failed(datapath, error_desc)
134                 return
135         else:
136             usable_versions = set(version for version
137                                   in datapath.supported_ofp_version
138                                   if version <= msg.version)
139             if (usable_versions and
140                 max(usable_versions) != min(msg.version,
141                                             datapath.ofproto.OFP_VERSION)):
142                 # The version of min(msg.version, datapath.ofproto.OFP_VERSION)
143                 # should be used according to the spec. But we can't.
144                 # So log it and use max(usable_versions) with the hope that
145                 # the switch is able to understand lower version.
146                 # e.g.
147                 # OF 1.1 from switch
148                 # OF 1.2 from Ryu and supported_ofp_version = (1.0, 1.2)
149                 # In this case, 1.1 should be used according to the spec,
150                 # but 1.1 can't be used.
151                 #
152                 # OF1.3.1 6.3.1
153                 # Upon receipt of this message, the recipient must
154                 # calculate the OpenFlow protocol version to be used. If
155                 # both the Hello message sent and the Hello message
156                 # received contained a OFPHET_VERSIONBITMAP hello element,
157                 # and if those bitmaps have some common bits set, the
158                 # negotiated version must be the highest version set in
159                 # both bitmaps. Otherwise, the negotiated version must be
160                 # the smaller of the version number that was sent and the
161                 # one that was received in the version fields.  If the
162                 # negotiated version is supported by the recipient, then
163                 # the connection proceeds. Otherwise, the recipient must
164                 # reply with an OFPT_ERROR message with a type field of
165                 # OFPET_HELLO_FAILED, a code field of OFPHFC_INCOMPATIBLE,
166                 # and optionally an ASCII string explaining the situation
167                 # in data, and then terminate the connection.
168                 version = max(usable_versions)
169                 error_desc = (
170                     'no compatible version found: '
171                     'switch 0x%x controller 0x%x, but found usable 0x%x. '
172                     'If possible, set the switch to use OF version 0x%x' % (
173                         msg.version, datapath.ofproto.OFP_VERSION,
174                         version, version))
175                 self._hello_failed(datapath, error_desc)
176                 return
177
178         if not usable_versions:
179             error_desc = (
180                 'unsupported version 0x%x. '
181                 'If possible, set the switch to use one of the versions %s' % (
182                     msg.version, sorted(datapath.supported_ofp_version)))
183             self._hello_failed(datapath, error_desc)
184             return
185         datapath.set_version(max(usable_versions))
186
187         # Move on to config state
188         self.logger.debug('move onto config mode')
189         datapath.set_state(CONFIG_DISPATCHER)
190
191         # Finally, send feature request
192         features_request = datapath.ofproto_parser.OFPFeaturesRequest(datapath)
193         datapath.send_msg(features_request)
194
195     @set_ev_handler(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
196     def switch_features_handler(self, ev):
197         msg = ev.msg
198         datapath = msg.datapath
199         self.logger.debug('switch features ev %s', msg)
200
201         datapath.id = msg.datapath_id
202
203         # hacky workaround, will be removed. OF1.3 doesn't have
204         # ports. An application should not depend on them. But there
205         # might be such bad applications so keep this workaround for
206         # while.
207         if datapath.ofproto.OFP_VERSION < 0x04:
208             datapath.ports = msg.ports
209         else:
210             datapath.ports = {}
211
212         if datapath.ofproto.OFP_VERSION < 0x04:
213             self.logger.debug('move onto main mode')
214             ev.msg.datapath.set_state(MAIN_DISPATCHER)
215         else:
216             port_desc = datapath.ofproto_parser.OFPPortDescStatsRequest(
217                 datapath, 0)
218             datapath.send_msg(port_desc)
219
220     @set_ev_handler(ofp_event.EventOFPPortDescStatsReply, CONFIG_DISPATCHER)
221     def multipart_reply_handler(self, ev):
222         msg = ev.msg
223         datapath = msg.datapath
224         with warnings.catch_warnings():
225             warnings.simplefilter('ignore')
226             for port in msg.body:
227                 datapath.ports[port.port_no] = port
228
229         if msg.flags & datapath.ofproto.OFPMPF_REPLY_MORE:
230             return
231         self.logger.debug('move onto main mode')
232         ev.msg.datapath.set_state(MAIN_DISPATCHER)
233
234     @set_ev_handler(ofp_event.EventOFPEchoRequest,
235                     [HANDSHAKE_DISPATCHER, CONFIG_DISPATCHER, MAIN_DISPATCHER])
236     def echo_request_handler(self, ev):
237         msg = ev.msg
238         datapath = msg.datapath
239         echo_reply = datapath.ofproto_parser.OFPEchoReply(datapath)
240         echo_reply.xid = msg.xid
241         echo_reply.data = msg.data
242         datapath.send_msg(echo_reply)
243
244     @set_ev_handler(ofp_event.EventOFPEchoReply,
245                     [HANDSHAKE_DISPATCHER, CONFIG_DISPATCHER, MAIN_DISPATCHER])
246     def echo_reply_handler(self, ev):
247         msg = ev.msg
248         datapath = msg.datapath
249         datapath.acknowledge_echo_reply(msg.xid)
250
251     @set_ev_handler(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER)
252     def port_status_handler(self, ev):
253         msg = ev.msg
254         datapath = msg.datapath
255         ofproto = datapath.ofproto
256
257         if msg.reason in [ofproto.OFPPR_ADD, ofproto.OFPPR_MODIFY]:
258             datapath.ports[msg.desc.port_no] = msg.desc
259         elif msg.reason == ofproto.OFPPR_DELETE:
260             datapath.ports.pop(msg.desc.port_no, None)
261         else:
262             return
263
264         self.send_event_to_observers(
265             ofp_event.EventOFPPortStateChange(
266                 datapath, msg.reason, msg.desc.port_no),
267             datapath.state)
268
269     @set_ev_handler(ofp_event.EventOFPErrorMsg,
270                     [HANDSHAKE_DISPATCHER, CONFIG_DISPATCHER, MAIN_DISPATCHER])
271     def error_msg_handler(self, ev):
272         msg = ev.msg
273         ofp = msg.datapath.ofproto
274         self.logger.debug(
275             "EventOFPErrorMsg received.\n"
276             "version=%s, msg_type=%s, msg_len=%s, xid=%s\n"
277             " `-- msg_type: %s",
278             hex(msg.version), hex(msg.msg_type), hex(msg.msg_len),
279             hex(msg.xid),
280             ofp.ofp_msg_type_to_str(msg.msg_type))
281         if msg.type == ofp.OFPET_EXPERIMENTER:
282             self.logger.debug(
283                 "OFPErrorExperimenterMsg(type=%s, exp_type=%s,"
284                 " experimenter=%s, data=b'%s')",
285                 hex(msg.type), hex(msg.exp_type),
286                 hex(msg.experimenter), utils.binary_str(msg.data))
287         else:
288             self.logger.debug(
289                 "OFPErrorMsg(type=%s, code=%s, data=b'%s')\n"
290                 " |-- type: %s\n"
291                 " |-- code: %s",
292                 hex(msg.type), hex(msg.code), utils.binary_str(msg.data),
293                 ofp.ofp_error_type_to_str(msg.type),
294                 ofp.ofp_error_code_to_str(msg.type, msg.code))
295         if msg.type == ofp.OFPET_HELLO_FAILED:
296             self.logger.debug(
297                 " `-- data: %s", msg.data.decode('ascii'))
298         elif len(msg.data) >= ofp.OFP_HEADER_SIZE:
299             (version, msg_type, msg_len, xid) = ofproto_parser.header(msg.data)
300             self.logger.debug(
301                 " `-- data: version=%s, msg_type=%s, msg_len=%s, xid=%s\n"
302                 "     `-- msg_type: %s",
303                 hex(version), hex(msg_type), hex(msg_len), hex(xid),
304                 ofp.ofp_msg_type_to_str(msg_type))
305         else:
306             self.logger.warning(
307                 "The data field sent from the switch is too short: "
308                 "len(msg.data) < OFP_HEADER_SIZE\n"
309                 "The OpenFlow Spec says that the data field should contain "
310                 "at least 64 bytes of the failed request.\n"
311                 "Please check the settings or implementation of your switch.")