1 # Copyright (C) 2016 Nippon Telegraph and Telephone Corporation.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
22 from ryu.lib import dpid
23 from ryu.lib import hub
24 from ryu.ofproto import ofproto_v1_2
27 LOG = logging.getLogger(__name__)
30 # NOTE(jkoelker) Constants for converting actions
32 COPY_TTL_OUT = 'COPY_TTL_OUT'
33 COPY_TTL_IN = 'COPY_TTL_IN'
34 SET_MPLS_TTL = 'SET_MPLS_TTL'
35 DEC_MPLS_TTL = 'DEC_MPLS_TTL'
36 PUSH_VLAN = 'PUSH_VLAN'
38 PUSH_MPLS = 'PUSH_MPLS'
40 SET_QUEUE = 'SET_QUEUE'
42 SET_NW_TTL = 'SET_NW_TTL'
43 DEC_NW_TTL = 'DEC_NW_TTL'
44 SET_FIELD = 'SET_FIELD'
45 PUSH_PBB = 'PUSH_PBB' # OpenFlow 1.3 or later
46 POP_PBB = 'POP_PBB' # OpenFlow 1.3 or later
47 COPY_FIELD = 'COPY_FIELD' # OpenFlow 1.5 or later
48 METER = 'METER' # OpenFlow 1.5 or later
49 EXPERIMENTER = 'EXPERIMENTER'
52 def get_logger(logger=None):
53 # NOTE(jkoelker) use the logger the calling code wants us to
54 if logger is not None:
60 def match_vid_to_str(value, mask, ofpvid_present):
62 return '0x%04x/0x%04x' % (value, mask)
64 if value & ofpvid_present:
65 return str(value & ~ofpvid_present)
67 return '0x%04x' % value
70 def to_action(dic, ofp, parser, action_type, util):
71 actions = {COPY_TTL_OUT: parser.OFPActionCopyTtlOut,
72 COPY_TTL_IN: parser.OFPActionCopyTtlIn,
73 DEC_MPLS_TTL: parser.OFPActionDecMplsTtl,
74 POP_VLAN: parser.OFPActionPopVlan,
75 DEC_NW_TTL: parser.OFPActionDecNwTtl}
76 if ofp.OFP_VERSION > ofproto_v1_2.OFP_VERSION:
77 actions[POP_PBB] = parser.OFPActionPopPbb
79 need_ethertype = {PUSH_VLAN: parser.OFPActionPushVlan,
80 PUSH_MPLS: parser.OFPActionPushMpls,
81 POP_MPLS: parser.OFPActionPopMpls}
82 if ofp.OFP_VERSION > ofproto_v1_2.OFP_VERSION:
83 need_ethertype[PUSH_PBB] = parser.OFPActionPushPbb
85 if action_type in actions:
86 return actions[action_type]()
88 elif action_type in need_ethertype:
89 ethertype = str_to_int(dic.get('ethertype'))
90 return need_ethertype[action_type](ethertype)
92 elif action_type == OUTPUT:
93 out_port = util.ofp_port_from_user(dic.get('port', ofp.OFPP_ANY))
94 max_len = util.ofp_cml_from_user(dic.get('max_len', ofp.OFPCML_MAX))
95 return parser.OFPActionOutput(out_port, max_len)
97 elif action_type == SET_MPLS_TTL:
98 mpls_ttl = str_to_int(dic.get('mpls_ttl'))
99 return parser.OFPActionSetMplsTtl(mpls_ttl)
101 elif action_type == SET_QUEUE:
102 queue_id = util.ofp_queue_from_user(dic.get('queue_id'))
103 return parser.OFPActionSetQueue(queue_id)
105 elif action_type == GROUP:
106 group_id = util.ofp_group_from_user(dic.get('group_id'))
107 return parser.OFPActionGroup(group_id)
109 elif action_type == SET_NW_TTL:
110 nw_ttl = str_to_int(dic.get('nw_ttl'))
111 return parser.OFPActionSetNwTtl(nw_ttl)
113 elif action_type == SET_FIELD:
114 field = dic.get('field')
115 value = dic.get('value')
116 return parser.OFPActionSetField(**{field: value})
118 elif action_type == 'COPY_FIELD':
119 n_bits = str_to_int(dic.get('n_bits'))
120 src_offset = str_to_int(dic.get('src_offset'))
121 dst_offset = str_to_int(dic.get('dst_offset'))
122 oxm_ids = [parser.OFPOxmId(str(dic.get('src_oxm_id'))),
123 parser.OFPOxmId(str(dic.get('dst_oxm_id')))]
124 return parser.OFPActionCopyField(
125 n_bits, src_offset, dst_offset, oxm_ids)
127 elif action_type == 'METER':
128 if hasattr(parser, 'OFPActionMeter'):
129 # OpenFlow 1.5 or later
130 meter_id = str_to_int(dic.get('meter_id'))
131 return parser.OFPActionMeter(meter_id)
133 # OpenFlow 1.4 or earlier
136 elif action_type == EXPERIMENTER:
137 experimenter = str_to_int(dic.get('experimenter'))
138 data_type = dic.get('data_type', 'ascii')
140 if data_type not in ('ascii', 'base64'):
141 LOG.error('Unknown data type: %s', data_type)
144 data = dic.get('data', '')
145 if data_type == 'base64':
146 data = base64.b64decode(data)
147 return parser.OFPActionExperimenterUnknown(experimenter, data)
152 def to_match_eth(value):
154 value = value.split('/')
155 return value[0], value[1]
160 def to_match_ip(value):
162 (ip_addr, ip_mask) = value.split('/')
164 if ip_mask.isdigit():
165 ip = netaddr.ip.IPNetwork(value)
167 ip_mask = str(ip.netmask)
169 return ip_addr, ip_mask
174 def to_match_vid(value, ofpvid_present):
175 # NOTE: If "vlan_id" field is described as decimal int value
176 # (and decimal string value), it is treated as values of
177 # VLAN tag, and OFPVID_PRESENT(0x1000) bit is automatically
178 # applied. OTOH, If it is described as hexadecimal string,
179 # treated as values of oxm_value (including OFPVID_PRESENT
180 # bit), and OFPVID_PRESENT bit is NOT automatically applied
181 if isinstance(value, six.integer_types):
182 # described as decimal int value
183 return value | ofpvid_present
187 val = value.split('/')
188 return str_to_int(val[0]), str_to_int(val[1])
192 # described as decimal string value
193 return int(value, 10) | ofpvid_present
195 return str_to_int(value)
198 def to_match_masked_int(value):
199 if isinstance(value, str) and '/' in value:
200 value = value.split('/')
201 return str_to_int(value[0]), str_to_int(value[1])
203 return str_to_int(value)
206 def to_match_packet_type(value):
207 if isinstance(value, (list, tuple)):
208 return str_to_int(value[0]) << 16 | str_to_int(value[1])
210 return str_to_int(value)
213 def send_experimenter(dp, exp, logger=None):
214 experimenter = exp.get('experimenter', 0)
215 exp_type = exp.get('exp_type', 0)
216 data_type = exp.get('data_type', 'ascii')
218 data = exp.get('data', '')
219 if data_type == 'base64':
220 data = base64.b64decode(data)
221 elif data_type == 'ascii':
222 data = data.encode('ascii')
224 get_logger(logger).error('Unknown data type: %s', data_type)
227 expmsg = dp.ofproto_parser.OFPExperimenter(
228 dp, experimenter, exp_type, data)
229 send_msg(dp, expmsg, logger)
232 def send_msg(dp, msg, logger=None):
236 log = get_logger(logger)
237 # NOTE(jkoelker) Prevent unnecessary string formating by including the
238 # format rules in the log_msg
239 log_msg = ('Sending message with xid(%x) to '
240 'datapath(' + dpid._DPID_FMT + '): %s')
241 log.debug(log_msg, msg.xid, dp.id, msg)
245 def send_stats_request(dp, stats, waiters, msgs, logger=None):
247 waiters_per_dp = waiters.setdefault(dp.id, {})
249 previous_msg_len = len(msgs)
250 waiters_per_dp[stats.xid] = (lock, msgs)
251 send_msg(dp, stats, logger)
253 lock.wait(timeout=DEFAULT_TIMEOUT)
254 current_msg_len = len(msgs)
256 while current_msg_len > previous_msg_len:
257 previous_msg_len = current_msg_len
258 lock.wait(timeout=DEFAULT_TIMEOUT)
259 current_msg_len = len(msgs)
261 if not lock.is_set():
262 del waiters_per_dp[stats.xid]
265 def str_to_int(str_num):
266 return int(str(str_num), 0)
269 def get_role(dp, waiters, to_user):
270 stats = dp.ofproto_parser.OFPRoleRequest(
271 dp, dp.ofproto.OFPCR_ROLE_NOCHANGE, generation_id=0)
273 send_stats_request(dp, stats, waiters, msgs, LOG)
277 d = msg.to_jsondict()[msg.__class__.__name__]
279 d['role'] = OFCtlUtil(dp.ofproto).ofp_role_to_user(d['role'])
282 return {str(dp.id): descs}
285 class OFCtlUtil(object):
287 def __init__(self, ofproto):
288 self.ofproto = ofproto
289 self.deprecated_value = [
290 'OFPTFPT_EXPERIMENTER_SLAVE',
291 'OFPTFPT_EXPERIMENTER_MASTER',
294 def _reserved_num_from_user(self, num, prefix):
296 return str_to_int(num)
299 if num.startswith(prefix):
300 return getattr(self.ofproto, num.upper())
302 return getattr(self.ofproto, prefix + num.upper())
303 except AttributeError:
305 "Cannot convert argument to reserved number: %s", num)
308 def _reserved_num_to_user(self, num, prefix):
309 for k, v in self.ofproto.__dict__.items():
310 if k not in self.deprecated_value and \
311 k.startswith(prefix) and v == num:
312 return k.replace(prefix, '')
315 def ofp_port_features_from_user(self, act):
316 return self._reserved_num_from_user(act, 'OFPPF_')
318 def ofp_port_features_to_user(self, act):
319 return self._reserved_num_to_user(act, 'OFPPF_')
321 def ofp_port_mod_prop_type_from_user(self, act):
322 return self._reserved_num_from_user(act, 'OFPPMPT_')
324 def ofp_port_mod_prop_type_to_user(self, act):
325 return self._reserved_num_to_user(act, 'OFPPMPT_')
327 def ofp_port_desc_prop_type_from_user(self, act):
328 return self._reserved_num_from_user(act, 'OFPPDPT_')
330 def ofp_port_desc_prop_type_to_user(self, act):
331 return self._reserved_num_to_user(act, 'OFPPDPT_')
333 def ofp_action_type_from_user(self, act):
334 return self._reserved_num_from_user(act, 'OFPAT_')
336 def ofp_action_type_to_user(self, act):
337 return self._reserved_num_to_user(act, 'OFPAT_')
339 def ofp_instruction_type_from_user(self, act):
340 return self._reserved_num_from_user(act, 'OFPIT_')
342 def ofp_instruction_type_to_user(self, act):
343 return self._reserved_num_to_user(act, 'OFPIT_')
345 def ofp_group_type_from_user(self, act):
346 return self._reserved_num_from_user(act, 'OFPGT_')
348 def ofp_group_type_to_user(self, act):
349 return self._reserved_num_to_user(act, 'OFPGT_')
351 def ofp_meter_band_type_from_user(self, act):
352 return self._reserved_num_from_user(act, 'OFPMBT_')
354 def ofp_meter_band_type_to_user(self, act):
355 return self._reserved_num_to_user(act, 'OFPMBT_')
357 def ofp_table_feature_prop_type_from_user(self, act):
358 return self._reserved_num_from_user(act, 'OFPTFPT_')
360 def ofp_table_feature_prop_type_to_user(self, act):
361 return self._reserved_num_to_user(act, 'OFPTFPT_')
363 def ofp_port_stats_prop_type_from_user(self, act):
364 return self._reserved_num_from_user(act, 'OFPPSPT_')
366 def ofp_port_stats_prop_type_to_user(self, act):
367 return self._reserved_num_to_user(act, 'OFPPSPT_')
369 def ofp_queue_desc_prop_type_from_user(self, act):
370 return self._reserved_num_from_user(act, 'OFPQDPT_')
372 def ofp_queue_desc_prop_type_to_user(self, act):
373 return self._reserved_num_to_user(act, 'OFPQDPT_')
375 def ofp_queue_stats_prop_type_from_user(self, act):
376 return self._reserved_num_from_user(act, 'OFPQSPT_')
378 def ofp_queue_stats_prop_type_to_user(self, act):
379 return self._reserved_num_to_user(act, 'OFPQSPT_')
381 def ofp_meter_flags_from_user(self, act):
382 return self._reserved_num_from_user(act, 'OFPMF_')
384 def ofp_meter_flags_to_user(self, act):
385 return self._reserved_num_to_user(act, 'OFPMF_')
387 def ofp_port_from_user(self, port):
388 return self._reserved_num_from_user(port, 'OFPP_')
390 def ofp_port_to_user(self, port):
391 return self._reserved_num_to_user(port, 'OFPP_')
393 def ofp_table_from_user(self, table):
394 return self._reserved_num_from_user(table, 'OFPTT_')
396 def ofp_table_to_user(self, table):
397 return self._reserved_num_to_user(table, 'OFPTT_')
399 def ofp_cml_from_user(self, max_len):
400 return self._reserved_num_from_user(max_len, 'OFPCML_')
402 def ofp_cml_to_user(self, max_len):
403 return self._reserved_num_to_user(max_len, 'OFPCML_')
405 def ofp_group_from_user(self, group):
406 return self._reserved_num_from_user(group, 'OFPG_')
408 def ofp_group_to_user(self, group):
409 return self._reserved_num_to_user(group, 'OFPG_')
411 def ofp_group_capabilities_from_user(self, group):
412 return self._reserved_num_from_user(group, 'OFPGFC_')
414 def ofp_group_capabilities_to_user(self, group):
415 return self._reserved_num_to_user(group, 'OFPGFC_')
417 def ofp_group_bucket_prop_type_from_user(self, group):
418 return self._reserved_num_from_user(group, 'OFPGBPT_')
420 def ofp_group_bucket_prop_type_to_user(self, group):
421 return self._reserved_num_to_user(group, 'OFPGBPT_')
423 def ofp_buffer_from_user(self, buffer):
424 if buffer in ['OFP_NO_BUFFER', 'NO_BUFFER']:
425 return self.ofproto.OFP_NO_BUFFER
429 def ofp_buffer_to_user(self, buffer):
430 if self.ofproto.OFP_NO_BUFFER == buffer:
435 def ofp_meter_from_user(self, meter):
436 return self._reserved_num_from_user(meter, 'OFPM_')
438 def ofp_meter_to_user(self, meter):
439 return self._reserved_num_to_user(meter, 'OFPM_')
441 def ofp_queue_from_user(self, queue):
442 return self._reserved_num_from_user(queue, 'OFPQ_')
444 def ofp_queue_to_user(self, queue):
445 return self._reserved_num_to_user(queue, 'OFPQ_')
447 def ofp_role_from_user(self, role):
448 return self._reserved_num_from_user(role, 'OFPCR_ROLE_')
450 def ofp_role_to_user(self, role):
451 return self._reserved_num_to_user(role, 'OFPCR_ROLE_')