1# Copyright (C) 2013 Nippon Telegraph and Telephone Corporation.
2#
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
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
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
12# implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import base64
17import logging
18
19from ryu.ofproto import ether
20from ryu.ofproto import inet
21from ryu.ofproto import ofproto_common
22from ryu.ofproto import ofproto_v1_3
23from ryu.ofproto import ofproto_v1_3_parser
24from ryu.lib import ofctl_nicira_ext
25from ryu.lib import ofctl_utils
26
27
28LOG = logging.getLogger('ryu.lib.ofctl_v1_3')
29
30DEFAULT_TIMEOUT = 1.0
31
32UTIL = ofctl_utils.OFCtlUtil(ofproto_v1_3)
33str_to_int = ofctl_utils.str_to_int
34
35
36def to_action(dp, dic):
37    ofp = dp.ofproto
38    parser = dp.ofproto_parser
39    action_type = dic.get('type')
40    return ofctl_utils.to_action(dic, ofp, parser, action_type, UTIL)
41
42
43def to_actions(dp, acts):
44    inst = []
45    actions = []
46    ofp = dp.ofproto
47    parser = dp.ofproto_parser
48
49    for a in acts:
50        action = to_action(dp, a)
51
52        if action is not None:
53            actions.append(action)
54        else:
55            action_type = a.get('type')
56            if action_type == 'WRITE_ACTIONS':
57                write_actions = []
58                write_acts = a.get('actions')
59                for act in write_acts:
60                    action = to_action(dp, act)
61                    if action is not None:
62                        write_actions.append(action)
63                    else:
64                        LOG.error('Unknown action type: %s', action_type)
65                if write_actions:
66                    inst.append(
67                        parser.OFPInstructionActions(ofp.OFPIT_WRITE_ACTIONS,
68                                                     write_actions))
69            elif action_type == 'CLEAR_ACTIONS':
70                inst.append(
71                    parser.OFPInstructionActions(ofp.OFPIT_CLEAR_ACTIONS, []))
72            elif action_type == 'GOTO_TABLE':
73                table_id = UTIL.ofp_table_from_user(a.get('table_id'))
74                inst.append(parser.OFPInstructionGotoTable(table_id))
75            elif action_type == 'WRITE_METADATA':
76                metadata = str_to_int(a.get('metadata'))
77                metadata_mask = (str_to_int(a['metadata_mask'])
78                                 if 'metadata_mask' in a
79                                 else parser.UINT64_MAX)
80                inst.append(
81                    parser.OFPInstructionWriteMetadata(
82                        metadata, metadata_mask))
83            elif action_type == 'METER':
84                meter_id = UTIL.ofp_meter_from_user(a.get('meter_id'))
85                inst.append(parser.OFPInstructionMeter(meter_id))
86            else:
87                LOG.error('Unknown action type: %s', action_type)
88
89    if actions:
90        inst.append(parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS,
91                                                 actions))
92    return inst
93
94
95def action_to_str(act):
96    action_type = act.cls_action_type
97
98    if action_type == ofproto_v1_3.OFPAT_OUTPUT:
99        port = UTIL.ofp_port_to_user(act.port)
100        buf = 'OUTPUT:' + str(port)
101    elif action_type == ofproto_v1_3.OFPAT_COPY_TTL_OUT:
102        buf = 'COPY_TTL_OUT'
103    elif action_type == ofproto_v1_3.OFPAT_COPY_TTL_IN:
104        buf = 'COPY_TTL_IN'
105    elif action_type == ofproto_v1_3.OFPAT_SET_MPLS_TTL:
106        buf = 'SET_MPLS_TTL:' + str(act.mpls_ttl)
107    elif action_type == ofproto_v1_3.OFPAT_DEC_MPLS_TTL:
108        buf = 'DEC_MPLS_TTL'
109    elif action_type == ofproto_v1_3.OFPAT_PUSH_VLAN:
110        buf = 'PUSH_VLAN:' + str(act.ethertype)
111    elif action_type == ofproto_v1_3.OFPAT_POP_VLAN:
112        buf = 'POP_VLAN'
113    elif action_type == ofproto_v1_3.OFPAT_PUSH_MPLS:
114        buf = 'PUSH_MPLS:' + str(act.ethertype)
115    elif action_type == ofproto_v1_3.OFPAT_POP_MPLS:
116        buf = 'POP_MPLS:' + str(act.ethertype)
117    elif action_type == ofproto_v1_3.OFPAT_SET_QUEUE:
118        queue_id = UTIL.ofp_queue_to_user(act.queue_id)
119        buf = 'SET_QUEUE:' + str(queue_id)
120    elif action_type == ofproto_v1_3.OFPAT_GROUP:
121        group_id = UTIL.ofp_group_to_user(act.group_id)
122        buf = 'GROUP:' + str(group_id)
123    elif action_type == ofproto_v1_3.OFPAT_SET_NW_TTL:
124        buf = 'SET_NW_TTL:' + str(act.nw_ttl)
125    elif action_type == ofproto_v1_3.OFPAT_DEC_NW_TTL:
126        buf = 'DEC_NW_TTL'
127    elif action_type == ofproto_v1_3.OFPAT_SET_FIELD:
128        buf = 'SET_FIELD: {%s:%s}' % (act.key, act.value)
129    elif action_type == ofproto_v1_3.OFPAT_PUSH_PBB:
130        buf = 'PUSH_PBB:' + str(act.ethertype)
131    elif action_type == ofproto_v1_3.OFPAT_POP_PBB:
132        buf = 'POP_PBB'
133    elif action_type == ofproto_v1_3.OFPAT_EXPERIMENTER:
134        if act.experimenter == ofproto_common.NX_EXPERIMENTER_ID:
135            try:
136                return ofctl_nicira_ext.action_to_str(act, action_to_str)
137            except Exception:
138                LOG.debug('Error parsing NX_ACTION(%s)',
139                          act.__class__.__name__, exc_info=True)
140
141        data_str = base64.b64encode(act.data)
142        buf = 'EXPERIMENTER: {experimenter:%s, data:%s}' % \
143            (act.experimenter, data_str.decode('utf-8'))
144    else:
145        buf = 'UNKNOWN'
146    return buf
147
148
149def actions_to_str(instructions):
150    actions = []
151
152    for instruction in instructions:
153        if isinstance(instruction,
154                      ofproto_v1_3_parser.OFPInstructionActions):
155            if instruction.type == ofproto_v1_3.OFPIT_APPLY_ACTIONS:
156                for a in instruction.actions:
157                    actions.append(action_to_str(a))
158            elif instruction.type == ofproto_v1_3.OFPIT_WRITE_ACTIONS:
159                write_actions = []
160                for a in instruction.actions:
161                    write_actions.append(action_to_str(a))
162                if write_actions:
163                    actions.append({'WRITE_ACTIONS': write_actions})
164            elif instruction.type == ofproto_v1_3.OFPIT_CLEAR_ACTIONS:
165                actions.append('CLEAR_ACTIONS')
166            else:
167                actions.append('UNKNOWN')
168        elif isinstance(instruction,
169                        ofproto_v1_3_parser.OFPInstructionGotoTable):
170            table_id = UTIL.ofp_table_to_user(instruction.table_id)
171            buf = 'GOTO_TABLE:' + str(table_id)
172            actions.append(buf)
173
174        elif isinstance(instruction,
175                        ofproto_v1_3_parser.OFPInstructionWriteMetadata):
176            buf = ('WRITE_METADATA:0x%x/0x%x' % (instruction.metadata,
177                                                 instruction.metadata_mask)
178                   if instruction.metadata_mask
179                   else 'WRITE_METADATA:0x%x' % instruction.metadata)
180            actions.append(buf)
181
182        elif isinstance(instruction,
183                        ofproto_v1_3_parser.OFPInstructionMeter):
184            meter_id = UTIL.ofp_meter_to_user(instruction.meter_id)
185            buf = 'METER:' + str(meter_id)
186            actions.append(buf)
187
188        else:
189            continue
190
191    return actions
192
193
194def to_match(dp, attrs):
195    convert = {'in_port': UTIL.ofp_port_from_user,
196               'in_phy_port': str_to_int,
197               'metadata': ofctl_utils.to_match_masked_int,
198               'dl_dst': ofctl_utils.to_match_eth,
199               'dl_src': ofctl_utils.to_match_eth,
200               'eth_dst': ofctl_utils.to_match_eth,
201               'eth_src': ofctl_utils.to_match_eth,
202               'dl_type': str_to_int,
203               'eth_type': str_to_int,
204               'dl_vlan': to_match_vid,
205               'vlan_vid': to_match_vid,
206               'vlan_pcp': str_to_int,
207               'ip_dscp': str_to_int,
208               'ip_ecn': str_to_int,
209               'nw_proto': str_to_int,
210               'ip_proto': str_to_int,
211               'nw_src': ofctl_utils.to_match_ip,
212               'nw_dst': ofctl_utils.to_match_ip,
213               'ipv4_src': ofctl_utils.to_match_ip,
214               'ipv4_dst': ofctl_utils.to_match_ip,
215               'tp_src': str_to_int,
216               'tp_dst': str_to_int,
217               'tcp_src': str_to_int,
218               'tcp_dst': str_to_int,
219               'udp_src': str_to_int,
220               'udp_dst': str_to_int,
221               'sctp_src': str_to_int,
222               'sctp_dst': str_to_int,
223               'icmpv4_type': str_to_int,
224               'icmpv4_code': str_to_int,
225               'arp_op': str_to_int,
226               'arp_spa': ofctl_utils.to_match_ip,
227               'arp_tpa': ofctl_utils.to_match_ip,
228               'arp_sha': ofctl_utils.to_match_eth,
229               'arp_tha': ofctl_utils.to_match_eth,
230               'ipv6_src': ofctl_utils.to_match_ip,
231               'ipv6_dst': ofctl_utils.to_match_ip,
232               'ipv6_flabel': str_to_int,
233               'icmpv6_type': str_to_int,
234               'icmpv6_code': str_to_int,
235               'ipv6_nd_target': ofctl_utils.to_match_ip,
236               'ipv6_nd_sll': ofctl_utils.to_match_eth,
237               'ipv6_nd_tll': ofctl_utils.to_match_eth,
238               'mpls_label': str_to_int,
239               'mpls_tc': str_to_int,
240               'mpls_bos': str_to_int,
241               'pbb_isid': ofctl_utils.to_match_masked_int,
242               'tunnel_id': ofctl_utils.to_match_masked_int,
243               'ipv6_exthdr': ofctl_utils.to_match_masked_int}
244
245    keys = {'dl_dst': 'eth_dst',
246            'dl_src': 'eth_src',
247            'dl_type': 'eth_type',
248            'dl_vlan': 'vlan_vid',
249            'nw_src': 'ipv4_src',
250            'nw_dst': 'ipv4_dst',
251            'nw_proto': 'ip_proto'}
252
253    if attrs.get('dl_type') == ether.ETH_TYPE_ARP or \
254            attrs.get('eth_type') == ether.ETH_TYPE_ARP:
255        if 'nw_src' in attrs and 'arp_spa' not in attrs:
256            attrs['arp_spa'] = attrs['nw_src']
257            del attrs['nw_src']
258        if 'nw_dst' in attrs and 'arp_tpa' not in attrs:
259            attrs['arp_tpa'] = attrs['nw_dst']
260            del attrs['nw_dst']
261
262    kwargs = {}
263    for key, value in attrs.items():
264        if key in keys:
265            # For old field name
266            key = keys[key]
267        if key in convert:
268            value = convert[key](value)
269            if key == 'tp_src' or key == 'tp_dst':
270                # TCP/UDP port
271                conv = {inet.IPPROTO_TCP: {'tp_src': 'tcp_src',
272                                           'tp_dst': 'tcp_dst'},
273                        inet.IPPROTO_UDP: {'tp_src': 'udp_src',
274                                           'tp_dst': 'udp_dst'}}
275                ip_proto = attrs.get('nw_proto', attrs.get('ip_proto', 0))
276                key = conv[ip_proto][key]
277                kwargs[key] = value
278            else:
279                # others
280                kwargs[key] = value
281        else:
282            LOG.error('Unknown match field: %s', key)
283
284    return dp.ofproto_parser.OFPMatch(**kwargs)
285
286
287def to_match_vid(value):
288    return ofctl_utils.to_match_vid(value, ofproto_v1_3.OFPVID_PRESENT)
289
290
291def match_to_str(ofmatch):
292
293    keys = {'eth_src': 'dl_src',
294            'eth_dst': 'dl_dst',
295            'eth_type': 'dl_type',
296            'vlan_vid': 'dl_vlan',
297            'ipv4_src': 'nw_src',
298            'ipv4_dst': 'nw_dst',
299            'ip_proto': 'nw_proto',
300            'tcp_src': 'tp_src',
301            'tcp_dst': 'tp_dst',
302            'udp_src': 'tp_src',
303            'udp_dst': 'tp_dst'}
304
305    match = {}
306
307    ofmatch = ofmatch.to_jsondict()['OFPMatch']
308    ofmatch = ofmatch['oxm_fields']
309
310    for match_field in ofmatch:
311        key = match_field['OXMTlv']['field']
312        if key in keys:
313            key = keys[key]
314        mask = match_field['OXMTlv']['mask']
315        value = match_field['OXMTlv']['value']
316        if key == 'dl_vlan':
317            value = match_vid_to_str(value, mask)
318        elif key == 'in_port':
319            value = UTIL.ofp_port_to_user(value)
320        else:
321            if mask is not None:
322                value = str(value) + '/' + str(mask)
323        match.setdefault(key, value)
324
325    return match
326
327
328def match_vid_to_str(value, mask):
329    return ofctl_utils.match_vid_to_str(
330        value, mask, ofproto_v1_3.OFPVID_PRESENT)
331
332
333def wrap_dpid_dict(dp, value, to_user=True):
334    if to_user:
335        return {str(dp.id): value}
336
337    return {dp.id: value}
338
339
340def get_desc_stats(dp, waiters, to_user=True):
341    stats = dp.ofproto_parser.OFPDescStatsRequest(dp, 0)
342    msgs = []
343    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
344    s = {}
345
346    for msg in msgs:
347        stats = msg.body
348        s = stats.to_jsondict()[stats.__class__.__name__]
349
350    return wrap_dpid_dict(dp, s, to_user)
351
352
353def get_queue_stats(dp, waiters, port=None, queue_id=None, to_user=True):
354    ofp = dp.ofproto
355
356    if port is None:
357        port = ofp.OFPP_ANY
358    else:
359        port = str_to_int(port)
360
361    if queue_id is None:
362        queue_id = ofp.OFPQ_ALL
363    else:
364        queue_id = str_to_int(queue_id)
365
366    stats = dp.ofproto_parser.OFPQueueStatsRequest(dp, 0, port,
367                                                   queue_id)
368    msgs = []
369    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
370
371    s = []
372    for msg in msgs:
373        stats = msg.body
374        for stat in stats:
375            s.append({'duration_nsec': stat.duration_nsec,
376                      'duration_sec': stat.duration_sec,
377                      'port_no': stat.port_no,
378                      'queue_id': stat.queue_id,
379                      'tx_bytes': stat.tx_bytes,
380                      'tx_errors': stat.tx_errors,
381                      'tx_packets': stat.tx_packets})
382
383    return wrap_dpid_dict(dp, s, to_user)
384
385
386def get_queue_config(dp, waiters, port=None, to_user=True):
387    ofp = dp.ofproto
388    if port is None:
389        port = ofp.OFPP_ANY
390    else:
391        port = UTIL.ofp_port_from_user(str_to_int(port))
392    stats = dp.ofproto_parser.OFPQueueGetConfigRequest(dp, port)
393    msgs = []
394    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
395
396    prop_type = {dp.ofproto.OFPQT_MIN_RATE: 'MIN_RATE',
397                 dp.ofproto.OFPQT_MAX_RATE: 'MAX_RATE',
398                 dp.ofproto.OFPQT_EXPERIMENTER: 'EXPERIMENTER'}
399
400    configs = []
401    for config in msgs:
402        queue_list = []
403        for queue in config.queues:
404            prop_list = []
405            for prop in queue.properties:
406                p = {'property': prop_type.get(prop.property, 'UNKNOWN')}
407                if prop.property == dp.ofproto.OFPQT_MIN_RATE or \
408                   prop.property == dp.ofproto.OFPQT_MAX_RATE:
409                    p['rate'] = prop.rate
410                elif prop.property == dp.ofproto.OFPQT_EXPERIMENTER:
411                    p['experimenter'] = prop.experimenter
412                    p['data'] = prop.data
413                prop_list.append(p)
414
415            q = {'properties': prop_list}
416
417            if to_user:
418                q['port'] = UTIL.ofp_port_to_user(queue.port)
419                q['queue_id'] = UTIL.ofp_queue_to_user(queue.queue_id)
420
421            else:
422                q['port'] = queue.port
423                q['queue_id'] = queue.queue_id
424
425            queue_list.append(q)
426
427        c = {'queues': queue_list}
428
429        if to_user:
430            c['port'] = UTIL.ofp_port_to_user(config.port)
431
432        else:
433            c['port'] = config.port
434
435        configs.append(c)
436
437    return wrap_dpid_dict(dp, configs, to_user)
438
439
440def get_flow_stats(dp, waiters, flow=None, to_user=True):
441    flow = flow if flow else {}
442    table_id = UTIL.ofp_table_from_user(
443        flow.get('table_id', dp.ofproto.OFPTT_ALL))
444    flags = str_to_int(flow.get('flags', 0))
445    out_port = UTIL.ofp_port_from_user(
446        flow.get('out_port', dp.ofproto.OFPP_ANY))
447    out_group = UTIL.ofp_group_from_user(
448        flow.get('out_group', dp.ofproto.OFPG_ANY))
449    cookie = str_to_int(flow.get('cookie', 0))
450    cookie_mask = str_to_int(flow.get('cookie_mask', 0))
451    match = to_match(dp, flow.get('match', {}))
452    # Note: OpenFlow does not allow to filter flow entries by priority,
453    # but for efficiency, ofctl provides the way to do it.
454    priority = str_to_int(flow.get('priority', -1))
455
456    stats = dp.ofproto_parser.OFPFlowStatsRequest(
457        dp, flags, table_id, out_port, out_group, cookie, cookie_mask,
458        match)
459
460    msgs = []
461    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
462
463    flows = []
464    for msg in msgs:
465        for stats in msg.body:
466            if 0 <= priority != stats.priority:
467                continue
468
469            s = {'priority': stats.priority,
470                 'cookie': stats.cookie,
471                 'idle_timeout': stats.idle_timeout,
472                 'hard_timeout': stats.hard_timeout,
473                 'byte_count': stats.byte_count,
474                 'duration_sec': stats.duration_sec,
475                 'duration_nsec': stats.duration_nsec,
476                 'packet_count': stats.packet_count,
477                 'length': stats.length,
478                 'flags': stats.flags}
479
480            if to_user:
481                s['actions'] = actions_to_str(stats.instructions)
482                s['match'] = match_to_str(stats.match)
483                s['table_id'] = UTIL.ofp_table_to_user(stats.table_id)
484
485            else:
486                s['actions'] = stats.instructions
487                s['instructions'] = stats.instructions
488                s['match'] = stats.match
489                s['table_id'] = stats.table_id
490
491            flows.append(s)
492
493    return wrap_dpid_dict(dp, flows, to_user)
494
495
496def get_aggregate_flow_stats(dp, waiters, flow=None, to_user=True):
497    flow = flow if flow else {}
498    table_id = UTIL.ofp_table_from_user(
499        flow.get('table_id', dp.ofproto.OFPTT_ALL))
500    flags = str_to_int(flow.get('flags', 0))
501    out_port = UTIL.ofp_port_from_user(
502        flow.get('out_port', dp.ofproto.OFPP_ANY))
503    out_group = UTIL.ofp_group_from_user(
504        flow.get('out_group', dp.ofproto.OFPG_ANY))
505    cookie = str_to_int(flow.get('cookie', 0))
506    cookie_mask = str_to_int(flow.get('cookie_mask', 0))
507    match = to_match(dp, flow.get('match', {}))
508
509    stats = dp.ofproto_parser.OFPAggregateStatsRequest(
510        dp, flags, table_id, out_port, out_group, cookie, cookie_mask,
511        match)
512
513    msgs = []
514    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
515
516    flows = []
517    for msg in msgs:
518        stats = msg.body
519        s = {'packet_count': stats.packet_count,
520             'byte_count': stats.byte_count,
521             'flow_count': stats.flow_count}
522        flows.append(s)
523
524    return wrap_dpid_dict(dp, flows, to_user)
525
526
527def get_table_stats(dp, waiters, to_user=True):
528    stats = dp.ofproto_parser.OFPTableStatsRequest(dp, 0)
529    msgs = []
530    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
531
532    tables = []
533    for msg in msgs:
534        stats = msg.body
535        for stat in stats:
536            s = {'active_count': stat.active_count,
537                 'lookup_count': stat.lookup_count,
538                 'matched_count': stat.matched_count}
539
540            if to_user:
541                s['table_id'] = UTIL.ofp_table_to_user(stat.table_id)
542
543            else:
544                s['table_id'] = stat.table_id
545
546            tables.append(s)
547
548    return wrap_dpid_dict(dp, tables, to_user)
549
550
551def get_table_features(dp, waiters, to_user=True):
552    stats = dp.ofproto_parser.OFPTableFeaturesStatsRequest(dp, 0, [])
553    msgs = []
554    ofproto = dp.ofproto
555    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
556
557    prop_type = {ofproto.OFPTFPT_INSTRUCTIONS: 'INSTRUCTIONS',
558                 ofproto.OFPTFPT_INSTRUCTIONS_MISS: 'INSTRUCTIONS_MISS',
559                 ofproto.OFPTFPT_NEXT_TABLES: 'NEXT_TABLES',
560                 ofproto.OFPTFPT_NEXT_TABLES_MISS: 'NEXT_TABLES_MISS',
561                 ofproto.OFPTFPT_WRITE_ACTIONS: 'WRITE_ACTIONS',
562                 ofproto.OFPTFPT_WRITE_ACTIONS_MISS: 'WRITE_ACTIONS_MISS',
563                 ofproto.OFPTFPT_APPLY_ACTIONS: 'APPLY_ACTIONS',
564                 ofproto.OFPTFPT_APPLY_ACTIONS_MISS: 'APPLY_ACTIONS_MISS',
565                 ofproto.OFPTFPT_MATCH: 'MATCH',
566                 ofproto.OFPTFPT_WILDCARDS: 'WILDCARDS',
567                 ofproto.OFPTFPT_WRITE_SETFIELD: 'WRITE_SETFIELD',
568                 ofproto.OFPTFPT_WRITE_SETFIELD_MISS: 'WRITE_SETFIELD_MISS',
569                 ofproto.OFPTFPT_APPLY_SETFIELD: 'APPLY_SETFIELD',
570                 ofproto.OFPTFPT_APPLY_SETFIELD_MISS: 'APPLY_SETFIELD_MISS',
571                 ofproto.OFPTFPT_EXPERIMENTER: 'EXPERIMENTER',
572                 ofproto.OFPTFPT_EXPERIMENTER_MISS: 'EXPERIMENTER_MISS'}
573
574    if not to_user:
575        prop_type = dict((k, k) for k in prop_type.keys())
576
577    p_type_instructions = [ofproto.OFPTFPT_INSTRUCTIONS,
578                           ofproto.OFPTFPT_INSTRUCTIONS_MISS]
579
580    p_type_next_tables = [ofproto.OFPTFPT_NEXT_TABLES,
581                          ofproto.OFPTFPT_NEXT_TABLES_MISS]
582
583    p_type_actions = [ofproto.OFPTFPT_WRITE_ACTIONS,
584                      ofproto.OFPTFPT_WRITE_ACTIONS_MISS,
585                      ofproto.OFPTFPT_APPLY_ACTIONS,
586                      ofproto.OFPTFPT_APPLY_ACTIONS_MISS]
587
588    p_type_oxms = [ofproto.OFPTFPT_MATCH,
589                   ofproto.OFPTFPT_WILDCARDS,
590                   ofproto.OFPTFPT_WRITE_SETFIELD,
591                   ofproto.OFPTFPT_WRITE_SETFIELD_MISS,
592                   ofproto.OFPTFPT_APPLY_SETFIELD,
593                   ofproto.OFPTFPT_APPLY_SETFIELD_MISS]
594
595    p_type_experimenter = [ofproto.OFPTFPT_EXPERIMENTER,
596                           ofproto.OFPTFPT_EXPERIMENTER_MISS]
597
598    tables = []
599    for msg in msgs:
600        stats = msg.body
601        for stat in stats:
602            properties = []
603            for prop in stat.properties:
604                p = {'type': prop_type.get(prop.type, 'UNKNOWN')}
605                if prop.type in p_type_instructions:
606                    instruction_ids = []
607                    for i in prop.instruction_ids:
608                        inst = {'len': i.len,
609                                'type': i.type}
610                        instruction_ids.append(inst)
611                    p['instruction_ids'] = instruction_ids
612                elif prop.type in p_type_next_tables:
613                    table_ids = []
614                    for i in prop.table_ids:
615                        table_ids.append(i)
616                    p['table_ids'] = table_ids
617                elif prop.type in p_type_actions:
618                    action_ids = []
619                    for i in prop.action_ids:
620                        act = {'len': i.len,
621                               'type': i.type}
622                        action_ids.append(act)
623                    p['action_ids'] = action_ids
624                elif prop.type in p_type_oxms:
625                    oxm_ids = []
626                    for i in prop.oxm_ids:
627                        oxm = {'hasmask': i.hasmask,
628                               'length': i.length,
629                               'type': i.type}
630                        oxm_ids.append(oxm)
631                    p['oxm_ids'] = oxm_ids
632                elif prop.type in p_type_experimenter:
633                    pass
634                properties.append(p)
635            s = {
636                'name': stat.name.decode('utf-8'),
637                'metadata_match': stat.metadata_match,
638                'metadata_write': stat.metadata_write,
639                'config': stat.config,
640                'max_entries': stat.max_entries,
641                'properties': properties,
642            }
643
644            if to_user:
645                s['table_id'] = UTIL.ofp_table_to_user(stat.table_id)
646
647            else:
648                s['table_id'] = stat.table_id
649
650            tables.append(s)
651
652    return wrap_dpid_dict(dp, tables, to_user)
653
654
655def get_port_stats(dp, waiters, port=None, to_user=True):
656    if port is None:
657        port = dp.ofproto.OFPP_ANY
658    else:
659        port = str_to_int(port)
660
661    stats = dp.ofproto_parser.OFPPortStatsRequest(
662        dp, 0, port)
663    msgs = []
664    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
665
666    ports = []
667    for msg in msgs:
668        for stats in msg.body:
669            s = {'rx_packets': stats.rx_packets,
670                 'tx_packets': stats.tx_packets,
671                 'rx_bytes': stats.rx_bytes,
672                 'tx_bytes': stats.tx_bytes,
673                 'rx_dropped': stats.rx_dropped,
674                 'tx_dropped': stats.tx_dropped,
675                 'rx_errors': stats.rx_errors,
676                 'tx_errors': stats.tx_errors,
677                 'rx_frame_err': stats.rx_frame_err,
678                 'rx_over_err': stats.rx_over_err,
679                 'rx_crc_err': stats.rx_crc_err,
680                 'collisions': stats.collisions,
681                 'duration_sec': stats.duration_sec,
682                 'duration_nsec': stats.duration_nsec}
683
684            if to_user:
685                s['port_no'] = UTIL.ofp_port_to_user(stats.port_no)
686
687            else:
688                s['port_no'] = stats.port_no
689
690            ports.append(s)
691
692    return wrap_dpid_dict(dp, ports, to_user)
693
694
695def get_meter_stats(dp, waiters, meter_id=None, to_user=True):
696    if meter_id is None:
697        meter_id = dp.ofproto.OFPM_ALL
698    else:
699        meter_id = str_to_int(meter_id)
700
701    stats = dp.ofproto_parser.OFPMeterStatsRequest(
702        dp, 0, meter_id)
703    msgs = []
704    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
705
706    meters = []
707    for msg in msgs:
708        for stats in msg.body:
709            bands = []
710            for band in stats.band_stats:
711                b = {'packet_band_count': band.packet_band_count,
712                     'byte_band_count': band.byte_band_count}
713                bands.append(b)
714            s = {'len': stats.len,
715                 'flow_count': stats.flow_count,
716                 'packet_in_count': stats.packet_in_count,
717                 'byte_in_count': stats.byte_in_count,
718                 'duration_sec': stats.duration_sec,
719                 'duration_nsec': stats.duration_nsec,
720                 'band_stats': bands}
721
722            if to_user:
723                s['meter_id'] = UTIL.ofp_meter_to_user(stats.meter_id)
724
725            else:
726                s['meter_id'] = stats.meter_id
727
728            meters.append(s)
729
730    return wrap_dpid_dict(dp, meters, to_user)
731
732
733def get_meter_features(dp, waiters, to_user=True):
734
735    ofp = dp.ofproto
736    type_convert = {ofp.OFPMBT_DROP: 'DROP',
737                    ofp.OFPMBT_DSCP_REMARK: 'DSCP_REMARK'}
738
739    capa_convert = {ofp.OFPMF_KBPS: 'KBPS',
740                    ofp.OFPMF_PKTPS: 'PKTPS',
741                    ofp.OFPMF_BURST: 'BURST',
742                    ofp.OFPMF_STATS: 'STATS'}
743
744    stats = dp.ofproto_parser.OFPMeterFeaturesStatsRequest(dp, 0)
745    msgs = []
746    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
747
748    features = []
749    for msg in msgs:
750        for feature in msg.body:
751            band_types = []
752            for k, v in type_convert.items():
753                if (1 << k) & feature.band_types:
754
755                    if to_user:
756                        band_types.append(v)
757
758                    else:
759                        band_types.append(k)
760
761            capabilities = []
762            for k, v in sorted(capa_convert.items()):
763                if k & feature.capabilities:
764
765                    if to_user:
766                        capabilities.append(v)
767
768                    else:
769                        capabilities.append(k)
770
771            f = {'max_meter': feature.max_meter,
772                 'band_types': band_types,
773                 'capabilities': capabilities,
774                 'max_bands': feature.max_bands,
775                 'max_color': feature.max_color}
776            features.append(f)
777
778    return wrap_dpid_dict(dp, features, to_user)
779
780
781def get_meter_config(dp, waiters, meter_id=None, to_user=True):
782    flags = {dp.ofproto.OFPMF_KBPS: 'KBPS',
783             dp.ofproto.OFPMF_PKTPS: 'PKTPS',
784             dp.ofproto.OFPMF_BURST: 'BURST',
785             dp.ofproto.OFPMF_STATS: 'STATS'}
786
787    band_type = {dp.ofproto.OFPMBT_DROP: 'DROP',
788                 dp.ofproto.OFPMBT_DSCP_REMARK: 'DSCP_REMARK',
789                 dp.ofproto.OFPMBT_EXPERIMENTER: 'EXPERIMENTER'}
790
791    if meter_id is None:
792        meter_id = dp.ofproto.OFPM_ALL
793    else:
794        meter_id = str_to_int(meter_id)
795
796    stats = dp.ofproto_parser.OFPMeterConfigStatsRequest(
797        dp, 0, meter_id)
798    msgs = []
799    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
800
801    configs = []
802    for msg in msgs:
803        for config in msg.body:
804            bands = []
805            for band in config.bands:
806                b = {'rate': band.rate,
807                     'burst_size': band.burst_size}
808
809                if to_user:
810                    b['type'] = band_type.get(band.type, '')
811
812                else:
813                    b['type'] = band.type
814
815                if band.type == dp.ofproto.OFPMBT_DSCP_REMARK:
816                    b['prec_level'] = band.prec_level
817                elif band.type == dp.ofproto.OFPMBT_EXPERIMENTER:
818                    b['experimenter'] = band.experimenter
819                bands.append(b)
820            c_flags = []
821            for k, v in sorted(flags.items()):
822                if k & config.flags:
823                    if to_user:
824                        c_flags.append(v)
825
826                    else:
827                        c_flags.append(k)
828
829            c = {'flags': c_flags,
830                 'bands': bands}
831
832            if to_user:
833                c['meter_id'] = UTIL.ofp_meter_to_user(config.meter_id)
834
835            else:
836                c['meter_id'] = config.meter_id
837
838            configs.append(c)
839
840    return wrap_dpid_dict(dp, configs, to_user)
841
842
843def get_group_stats(dp, waiters, group_id=None, to_user=True):
844    if group_id is None:
845        group_id = dp.ofproto.OFPG_ALL
846    else:
847        group_id = str_to_int(group_id)
848
849    stats = dp.ofproto_parser.OFPGroupStatsRequest(
850        dp, 0, group_id)
851    msgs = []
852    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
853
854    groups = []
855    for msg in msgs:
856        for stats in msg.body:
857            bucket_stats = []
858            for bucket_stat in stats.bucket_stats:
859                c = {'packet_count': bucket_stat.packet_count,
860                     'byte_count': bucket_stat.byte_count}
861                bucket_stats.append(c)
862            g = {'length': stats.length,
863                 'ref_count': stats.ref_count,
864                 'packet_count': stats.packet_count,
865                 'byte_count': stats.byte_count,
866                 'duration_sec': stats.duration_sec,
867                 'duration_nsec': stats.duration_nsec,
868                 'bucket_stats': bucket_stats}
869
870            if to_user:
871                g['group_id'] = UTIL.ofp_group_to_user(stats.group_id)
872
873            else:
874                g['group_id'] = stats.group_id
875
876            groups.append(g)
877
878    return wrap_dpid_dict(dp, groups, to_user)
879
880
881def get_group_features(dp, waiters, to_user=True):
882
883    ofp = dp.ofproto
884    type_convert = {ofp.OFPGT_ALL: 'ALL',
885                    ofp.OFPGT_SELECT: 'SELECT',
886                    ofp.OFPGT_INDIRECT: 'INDIRECT',
887                    ofp.OFPGT_FF: 'FF'}
888    cap_convert = {ofp.OFPGFC_SELECT_WEIGHT: 'SELECT_WEIGHT',
889                   ofp.OFPGFC_SELECT_LIVENESS: 'SELECT_LIVENESS',
890                   ofp.OFPGFC_CHAINING: 'CHAINING',
891                   ofp.OFPGFC_CHAINING_CHECKS: 'CHAINING_CHECKS'}
892    act_convert = {ofp.OFPAT_OUTPUT: 'OUTPUT',
893                   ofp.OFPAT_COPY_TTL_OUT: 'COPY_TTL_OUT',
894                   ofp.OFPAT_COPY_TTL_IN: 'COPY_TTL_IN',
895                   ofp.OFPAT_SET_MPLS_TTL: 'SET_MPLS_TTL',
896                   ofp.OFPAT_DEC_MPLS_TTL: 'DEC_MPLS_TTL',
897                   ofp.OFPAT_PUSH_VLAN: 'PUSH_VLAN',
898                   ofp.OFPAT_POP_VLAN: 'POP_VLAN',
899                   ofp.OFPAT_PUSH_MPLS: 'PUSH_MPLS',
900                   ofp.OFPAT_POP_MPLS: 'POP_MPLS',
901                   ofp.OFPAT_SET_QUEUE: 'SET_QUEUE',
902                   ofp.OFPAT_GROUP: 'GROUP',
903                   ofp.OFPAT_SET_NW_TTL: 'SET_NW_TTL',
904                   ofp.OFPAT_DEC_NW_TTL: 'DEC_NW_TTL',
905                   ofp.OFPAT_SET_FIELD: 'SET_FIELD',
906                   ofp.OFPAT_PUSH_PBB: 'PUSH_PBB',
907                   ofp.OFPAT_POP_PBB: 'POP_PBB'}
908
909    stats = dp.ofproto_parser.OFPGroupFeaturesStatsRequest(dp, 0)
910    msgs = []
911    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
912
913    features = []
914    for msg in msgs:
915        feature = msg.body
916        types = []
917        for k, v in type_convert.items():
918            if (1 << k) & feature.types:
919                if to_user:
920                    types.append(v)
921
922                else:
923                    types.append(k)
924
925        capabilities = []
926        for k, v in cap_convert.items():
927            if k & feature.capabilities:
928                if to_user:
929                    capabilities.append(v)
930
931                else:
932                    capabilities.append(k)
933
934        if to_user:
935            max_groups = []
936            for k, v in type_convert.items():
937                max_groups.append({v: feature.max_groups[k]})
938
939        else:
940            max_groups = feature.max_groups
941
942        actions = []
943        for k1, v1 in type_convert.items():
944            acts = []
945            for k2, v2 in act_convert.items():
946                if (1 << k2) & feature.actions[k1]:
947                    if to_user:
948                        acts.append(v2)
949
950                    else:
951                        acts.append(k2)
952
953            if to_user:
954                actions.append({v1: acts})
955
956            else:
957                actions.append({k1: acts})
958
959        f = {'types': types,
960             'capabilities': capabilities,
961             'max_groups': max_groups,
962             'actions': actions}
963        features.append(f)
964
965    return wrap_dpid_dict(dp, features, to_user)
966
967
968def get_group_desc(dp, waiters, to_user=True):
969
970    type_convert = {dp.ofproto.OFPGT_ALL: 'ALL',
971                    dp.ofproto.OFPGT_SELECT: 'SELECT',
972                    dp.ofproto.OFPGT_INDIRECT: 'INDIRECT',
973                    dp.ofproto.OFPGT_FF: 'FF'}
974
975    stats = dp.ofproto_parser.OFPGroupDescStatsRequest(dp, 0)
976    msgs = []
977    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
978
979    descs = []
980    for msg in msgs:
981        for stats in msg.body:
982            buckets = []
983            for bucket in stats.buckets:
984                actions = []
985                for action in bucket.actions:
986                    if to_user:
987                        actions.append(action_to_str(action))
988
989                    else:
990                        actions.append(action)
991
992                b = {'weight': bucket.weight,
993                     'watch_port': bucket.watch_port,
994                     'watch_group': bucket.watch_group,
995                     'actions': actions}
996                buckets.append(b)
997
998            d = {'buckets': buckets}
999            if to_user:
1000                d['group_id'] = UTIL.ofp_group_to_user(stats.group_id)
1001                d['type'] = type_convert.get(stats.type)
1002
1003            else:
1004                d['group_id'] = stats.group_id
1005                d['type'] = stats.type
1006
1007            descs.append(d)
1008
1009    return wrap_dpid_dict(dp, descs, to_user)
1010
1011
1012def get_port_desc(dp, waiters, to_user=True):
1013
1014    stats = dp.ofproto_parser.OFPPortDescStatsRequest(dp, 0)
1015    msgs = []
1016    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
1017
1018    descs = []
1019
1020    for msg in msgs:
1021        stats = msg.body
1022        for stat in stats:
1023            d = {'hw_addr': stat.hw_addr,
1024                 'name': stat.name.decode('utf-8', errors='replace'),
1025                 'config': stat.config,
1026                 'state': stat.state,
1027                 'curr': stat.curr,
1028                 'advertised': stat.advertised,
1029                 'supported': stat.supported,
1030                 'peer': stat.peer,
1031                 'curr_speed': stat.curr_speed,
1032                 'max_speed': stat.max_speed}
1033
1034            if to_user:
1035                d['port_no'] = UTIL.ofp_port_to_user(stat.port_no)
1036
1037            else:
1038                d['port_no'] = stat.port_no
1039
1040            descs.append(d)
1041
1042    return wrap_dpid_dict(dp, descs, to_user)
1043
1044
1045def get_role(dp, waiters, to_user=True):
1046    return ofctl_utils.get_role(dp, waiters, to_user)
1047
1048
1049def mod_flow_entry(dp, flow, cmd):
1050    cookie = str_to_int(flow.get('cookie', 0))
1051    cookie_mask = str_to_int(flow.get('cookie_mask', 0))
1052    table_id = UTIL.ofp_table_from_user(flow.get('table_id', 0))
1053    idle_timeout = str_to_int(flow.get('idle_timeout', 0))
1054    hard_timeout = str_to_int(flow.get('hard_timeout', 0))
1055    priority = str_to_int(flow.get('priority', 0))
1056    buffer_id = UTIL.ofp_buffer_from_user(
1057        flow.get('buffer_id', dp.ofproto.OFP_NO_BUFFER))
1058    out_port = UTIL.ofp_port_from_user(
1059        flow.get('out_port', dp.ofproto.OFPP_ANY))
1060    out_group = UTIL.ofp_group_from_user(
1061        flow.get('out_group', dp.ofproto.OFPG_ANY))
1062    flags = str_to_int(flow.get('flags', 0))
1063    match = to_match(dp, flow.get('match', {}))
1064    inst = to_actions(dp, flow.get('actions', []))
1065
1066    flow_mod = dp.ofproto_parser.OFPFlowMod(
1067        dp, cookie, cookie_mask, table_id, cmd, idle_timeout,
1068        hard_timeout, priority, buffer_id, out_port, out_group,
1069        flags, match, inst)
1070
1071    ofctl_utils.send_msg(dp, flow_mod, LOG)
1072
1073
1074def mod_meter_entry(dp, meter, cmd):
1075
1076    flags_convert = {'KBPS': dp.ofproto.OFPMF_KBPS,
1077                     'PKTPS': dp.ofproto.OFPMF_PKTPS,
1078                     'BURST': dp.ofproto.OFPMF_BURST,
1079                     'STATS': dp.ofproto.OFPMF_STATS}
1080
1081    flags = 0
1082    if 'flags' in meter:
1083        meter_flags = meter['flags']
1084        if not isinstance(meter_flags, list):
1085            meter_flags = [meter_flags]
1086        for flag in meter_flags:
1087            if flag not in flags_convert:
1088                LOG.error('Unknown meter flag: %s', flag)
1089                continue
1090            flags |= flags_convert.get(flag)
1091
1092    meter_id = UTIL.ofp_meter_from_user(meter.get('meter_id', 0))
1093
1094    bands = []
1095    for band in meter.get('bands', []):
1096        band_type = band.get('type')
1097        rate = str_to_int(band.get('rate', 0))
1098        burst_size = str_to_int(band.get('burst_size', 0))
1099        if band_type == 'DROP':
1100            bands.append(
1101                dp.ofproto_parser.OFPMeterBandDrop(rate, burst_size))
1102        elif band_type == 'DSCP_REMARK':
1103            prec_level = str_to_int(band.get('prec_level', 0))
1104            bands.append(
1105                dp.ofproto_parser.OFPMeterBandDscpRemark(
1106                    rate, burst_size, prec_level))
1107        elif band_type == 'EXPERIMENTER':
1108            experimenter = str_to_int(band.get('experimenter', 0))
1109            bands.append(
1110                dp.ofproto_parser.OFPMeterBandExperimenter(
1111                    rate, burst_size, experimenter))
1112        else:
1113            LOG.error('Unknown band type: %s', band_type)
1114
1115    meter_mod = dp.ofproto_parser.OFPMeterMod(
1116        dp, cmd, flags, meter_id, bands)
1117
1118    ofctl_utils.send_msg(dp, meter_mod, LOG)
1119
1120
1121def mod_group_entry(dp, group, cmd):
1122
1123    type_convert = {'ALL': dp.ofproto.OFPGT_ALL,
1124                    'SELECT': dp.ofproto.OFPGT_SELECT,
1125                    'INDIRECT': dp.ofproto.OFPGT_INDIRECT,
1126                    'FF': dp.ofproto.OFPGT_FF}
1127
1128    type_ = type_convert.get(group.get('type', 'ALL'))
1129    if type_ is None:
1130        LOG.error('Unknown group type: %s', group.get('type'))
1131
1132    group_id = UTIL.ofp_group_from_user(group.get('group_id', 0))
1133
1134    buckets = []
1135    for bucket in group.get('buckets', []):
1136        weight = str_to_int(bucket.get('weight', 0))
1137        watch_port = str_to_int(
1138            bucket.get('watch_port', dp.ofproto.OFPP_ANY))
1139        watch_group = str_to_int(
1140            bucket.get('watch_group', dp.ofproto.OFPG_ANY))
1141        actions = []
1142        for dic in bucket.get('actions', []):
1143            action = to_action(dp, dic)
1144            if action is not None:
1145                actions.append(action)
1146        buckets.append(dp.ofproto_parser.OFPBucket(
1147            weight, watch_port, watch_group, actions))
1148
1149    group_mod = dp.ofproto_parser.OFPGroupMod(
1150        dp, cmd, type_, group_id, buckets)
1151
1152    ofctl_utils.send_msg(dp, group_mod, LOG)
1153
1154
1155def mod_port_behavior(dp, port_config):
1156    port_no = UTIL.ofp_port_from_user(port_config.get('port_no', 0))
1157    hw_addr = str(port_config.get('hw_addr'))
1158    config = str_to_int(port_config.get('config', 0))
1159    mask = str_to_int(port_config.get('mask', 0))
1160    advertise = str_to_int(port_config.get('advertise'))
1161
1162    port_mod = dp.ofproto_parser.OFPPortMod(
1163        dp, port_no, hw_addr, config, mask, advertise)
1164
1165    ofctl_utils.send_msg(dp, port_mod, LOG)
1166
1167
1168def set_role(dp, role):
1169    r = UTIL.ofp_role_from_user(role.get('role', dp.ofproto.OFPCR_ROLE_EQUAL))
1170    role_request = dp.ofproto_parser.OFPRoleRequest(dp, r, 0)
1171    ofctl_utils.send_msg(dp, role_request, LOG)
1172
1173
1174# NOTE(jkoelker) Alias common funcitons
1175send_experimenter = ofctl_utils.send_experimenter
1176