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 logging
17import netaddr
18
19from ryu.ofproto import ether
20from ryu.ofproto import inet
21from ryu.ofproto import ofproto_v1_2
22from ryu.ofproto import ofproto_v1_2_parser
23from ryu.lib import ofctl_utils
24
25
26LOG = logging.getLogger('ryu.lib.ofctl_v1_2')
27
28DEFAULT_TIMEOUT = 1.0
29
30UTIL = ofctl_utils.OFCtlUtil(ofproto_v1_2)
31str_to_int = ofctl_utils.str_to_int
32
33
34def to_action(dp, dic):
35    ofp = dp.ofproto
36    parser = dp.ofproto_parser
37    action_type = dic.get('type')
38    return ofctl_utils.to_action(dic, ofp, parser, action_type, UTIL)
39
40
41def to_actions(dp, acts):
42    inst = []
43    actions = []
44    ofp = dp.ofproto
45    parser = dp.ofproto_parser
46
47    for a in acts:
48        action = to_action(dp, a)
49        if action is not None:
50            actions.append(action)
51        else:
52            action_type = a.get('type')
53            if action_type == 'WRITE_ACTIONS':
54                write_actions = []
55                write_acts = a.get('actions')
56                for act in write_acts:
57                    action = to_action(dp, act)
58                    if action is not None:
59                        write_actions.append(action)
60                    else:
61                        LOG.error('Unknown action type: %s', action_type)
62                if write_actions:
63                    inst.append(
64                        parser.OFPInstructionActions(ofp.OFPIT_WRITE_ACTIONS,
65                                                     write_actions))
66            elif action_type == 'CLEAR_ACTIONS':
67                inst.append(
68                    parser.OFPInstructionActions(ofp.OFPIT_CLEAR_ACTIONS, []))
69            elif action_type == 'GOTO_TABLE':
70                table_id = UTIL.ofp_table_from_user(a.get('table_id'))
71                inst.append(parser.OFPInstructionGotoTable(table_id))
72            elif action_type == 'WRITE_METADATA':
73                metadata = str_to_int(a.get('metadata'))
74                metadata_mask = (str_to_int(a['metadata_mask'])
75                                 if 'metadata_mask' in a
76                                 else parser.UINT64_MAX)
77                inst.append(
78                    parser.OFPInstructionWriteMetadata(
79                        metadata, metadata_mask))
80            else:
81                LOG.error('Unknown action type: %s', action_type)
82
83    if actions:
84        inst.append(parser.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS,
85                                                 actions))
86    return inst
87
88
89def action_to_str(act):
90    action_type = act.cls_action_type
91
92    if action_type == ofproto_v1_2.OFPAT_OUTPUT:
93        port = UTIL.ofp_port_to_user(act.port)
94        buf = 'OUTPUT:' + str(port)
95    elif action_type == ofproto_v1_2.OFPAT_COPY_TTL_OUT:
96        buf = 'COPY_TTL_OUT'
97    elif action_type == ofproto_v1_2.OFPAT_COPY_TTL_IN:
98        buf = 'COPY_TTL_IN'
99    elif action_type == ofproto_v1_2.OFPAT_SET_MPLS_TTL:
100        buf = 'SET_MPLS_TTL:' + str(act.mpls_ttl)
101    elif action_type == ofproto_v1_2.OFPAT_DEC_MPLS_TTL:
102        buf = 'DEC_MPLS_TTL'
103    elif action_type == ofproto_v1_2.OFPAT_PUSH_VLAN:
104        buf = 'PUSH_VLAN:' + str(act.ethertype)
105    elif action_type == ofproto_v1_2.OFPAT_POP_VLAN:
106        buf = 'POP_VLAN'
107    elif action_type == ofproto_v1_2.OFPAT_PUSH_MPLS:
108        buf = 'PUSH_MPLS:' + str(act.ethertype)
109    elif action_type == ofproto_v1_2.OFPAT_POP_MPLS:
110        buf = 'POP_MPLS:' + str(act.ethertype)
111    elif action_type == ofproto_v1_2.OFPAT_SET_QUEUE:
112        queue_id = UTIL.ofp_queue_to_user(act.queue_id)
113        buf = 'SET_QUEUE:' + str(queue_id)
114    elif action_type == ofproto_v1_2.OFPAT_GROUP:
115        group_id = UTIL.ofp_group_to_user(act.group_id)
116        buf = 'GROUP:' + str(group_id)
117    elif action_type == ofproto_v1_2.OFPAT_SET_NW_TTL:
118        buf = 'SET_NW_TTL:' + str(act.nw_ttl)
119    elif action_type == ofproto_v1_2.OFPAT_DEC_NW_TTL:
120        buf = 'DEC_NW_TTL'
121    elif action_type == ofproto_v1_2.OFPAT_SET_FIELD:
122        buf = 'SET_FIELD: {%s:%s}' % (act.key, act.value)
123    else:
124        buf = 'UNKNOWN'
125    return buf
126
127
128def actions_to_str(instructions):
129    actions = []
130
131    for instruction in instructions:
132        if isinstance(instruction,
133                      ofproto_v1_2_parser.OFPInstructionActions):
134            if instruction.type == ofproto_v1_2.OFPIT_APPLY_ACTIONS:
135                for a in instruction.actions:
136                    actions.append(action_to_str(a))
137            elif instruction.type == ofproto_v1_2.OFPIT_WRITE_ACTIONS:
138                write_actions = []
139                for a in instruction.actions:
140                    write_actions.append(action_to_str(a))
141                if write_actions:
142                    actions.append({'WRITE_ACTIONS': write_actions})
143            elif instruction.type == ofproto_v1_2.OFPIT_CLEAR_ACTIONS:
144                actions.append('CLEAR_ACTIONS')
145            else:
146                actions.append('UNKNOWN')
147        elif isinstance(instruction,
148                        ofproto_v1_2_parser.OFPInstructionGotoTable):
149            table_id = UTIL.ofp_table_to_user(instruction.table_id)
150            buf = 'GOTO_TABLE:' + str(table_id)
151            actions.append(buf)
152
153        elif isinstance(instruction,
154                        ofproto_v1_2_parser.OFPInstructionWriteMetadata):
155            buf = ('WRITE_METADATA:0x%x/0x%x' % (instruction.metadata,
156                                                 instruction.metadata_mask)
157                   if instruction.metadata_mask
158                   else 'WRITE_METADATA:0x%x' % instruction.metadata)
159            actions.append(buf)
160
161        else:
162            continue
163
164    return actions
165
166
167def to_match(dp, attrs):
168    convert = {'in_port': UTIL.ofp_port_from_user,
169               'in_phy_port': str_to_int,
170               'metadata': ofctl_utils.to_match_masked_int,
171               'dl_dst': ofctl_utils.to_match_eth,
172               'dl_src': ofctl_utils.to_match_eth,
173               'eth_dst': ofctl_utils.to_match_eth,
174               'eth_src': ofctl_utils.to_match_eth,
175               'dl_type': str_to_int,
176               'eth_type': str_to_int,
177               'dl_vlan': to_match_vid,
178               'vlan_vid': to_match_vid,
179               'vlan_pcp': str_to_int,
180               'ip_dscp': str_to_int,
181               'ip_ecn': str_to_int,
182               'nw_proto': str_to_int,
183               'ip_proto': str_to_int,
184               'nw_src': ofctl_utils.to_match_ip,
185               'nw_dst': ofctl_utils.to_match_ip,
186               'ipv4_src': ofctl_utils.to_match_ip,
187               'ipv4_dst': ofctl_utils.to_match_ip,
188               'tp_src': str_to_int,
189               'tp_dst': str_to_int,
190               'tcp_src': str_to_int,
191               'tcp_dst': str_to_int,
192               'udp_src': str_to_int,
193               'udp_dst': str_to_int,
194               'sctp_src': str_to_int,
195               'sctp_dst': str_to_int,
196               'icmpv4_type': str_to_int,
197               'icmpv4_code': str_to_int,
198               'arp_op': str_to_int,
199               'arp_spa': ofctl_utils.to_match_ip,
200               'arp_tpa': ofctl_utils.to_match_ip,
201               'arp_sha': ofctl_utils.to_match_eth,
202               'arp_tha': ofctl_utils.to_match_eth,
203               'ipv6_src': ofctl_utils.to_match_ip,
204               'ipv6_dst': ofctl_utils.to_match_ip,
205               'ipv6_flabel': str_to_int,
206               'icmpv6_type': str_to_int,
207               'icmpv6_code': str_to_int,
208               'ipv6_nd_target': ofctl_utils.to_match_ip,
209               'ipv6_nd_sll': ofctl_utils.to_match_eth,
210               'ipv6_nd_tll': ofctl_utils.to_match_eth,
211               'mpls_label': str_to_int,
212               'mpls_tc': str_to_int}
213
214    keys = {'dl_dst': 'eth_dst',
215            'dl_src': 'eth_src',
216            'dl_type': 'eth_type',
217            'dl_vlan': 'vlan_vid',
218            'nw_src': 'ipv4_src',
219            'nw_dst': 'ipv4_dst',
220            'nw_proto': 'ip_proto'}
221
222    if attrs.get('dl_type') == ether.ETH_TYPE_ARP or \
223            attrs.get('eth_type') == ether.ETH_TYPE_ARP:
224        if 'nw_src' in attrs and 'arp_spa' not in attrs:
225            attrs['arp_spa'] = attrs['nw_src']
226            del attrs['nw_src']
227        if 'nw_dst' in attrs and 'arp_tpa' not in attrs:
228            attrs['arp_tpa'] = attrs['nw_dst']
229            del attrs['nw_dst']
230
231    kwargs = {}
232    for key, value in attrs.items():
233        if key in keys:
234            # For old field name
235            key = keys[key]
236        if key in convert:
237            value = convert[key](value)
238            if key == 'tp_src' or key == 'tp_dst':
239                # TCP/UDP port
240                conv = {inet.IPPROTO_TCP: {'tp_src': 'tcp_src',
241                                           'tp_dst': 'tcp_dst'},
242                        inet.IPPROTO_UDP: {'tp_src': 'udp_src',
243                                           'tp_dst': 'udp_dst'}}
244                ip_proto = attrs.get('nw_proto', attrs.get('ip_proto', 0))
245                key = conv[ip_proto][key]
246                kwargs[key] = value
247            else:
248                # others
249                kwargs[key] = value
250        else:
251            LOG.error('Unknown match field: %s', key)
252
253    return dp.ofproto_parser.OFPMatch(**kwargs)
254
255
256def to_match_vid(value):
257    return ofctl_utils.to_match_vid(value, ofproto_v1_2.OFPVID_PRESENT)
258
259
260def match_to_str(ofmatch):
261
262    keys = {'eth_src': 'dl_src',
263            'eth_dst': 'dl_dst',
264            'eth_type': 'dl_type',
265            'vlan_vid': 'dl_vlan',
266            'ipv4_src': 'nw_src',
267            'ipv4_dst': 'nw_dst',
268            'ip_proto': 'nw_proto',
269            'tcp_src': 'tp_src',
270            'tcp_dst': 'tp_dst',
271            'udp_src': 'tp_src',
272            'udp_dst': 'tp_dst'}
273
274    match = {}
275
276    ofmatch = ofmatch.to_jsondict()['OFPMatch']
277    ofmatch = ofmatch['oxm_fields']
278    for match_field in ofmatch:
279        key = match_field['OXMTlv']['field']
280        if key in keys:
281            key = keys[key]
282        mask = match_field['OXMTlv']['mask']
283        value = match_field['OXMTlv']['value']
284        if key == 'dl_vlan':
285            value = match_vid_to_str(value, mask)
286        elif key == 'in_port':
287            value = UTIL.ofp_port_to_user(value)
288        else:
289            if mask is not None:
290                value = str(value) + '/' + str(mask)
291        match.setdefault(key, value)
292
293    return match
294
295
296def match_vid_to_str(value, mask):
297    return ofctl_utils.match_vid_to_str(
298        value, mask, ofproto_v1_2.OFPVID_PRESENT)
299
300
301def get_desc_stats(dp, waiters):
302    stats = dp.ofproto_parser.OFPDescStatsRequest(dp)
303    msgs = []
304    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
305
306    s = {}
307    for msg in msgs:
308        stats = msg.body
309        s = stats.to_jsondict()[stats.__class__.__name__]
310
311    return {str(dp.id): s}
312
313
314def get_queue_stats(dp, waiters, port=None, queue_id=None):
315    ofp = dp.ofproto
316
317    if port is None:
318        port = ofp.OFPP_ANY
319    else:
320        port = str_to_int(port)
321
322    if queue_id is None:
323        queue_id = ofp.OFPQ_ALL
324    else:
325        queue_id = str_to_int(queue_id)
326
327    stats = dp.ofproto_parser.OFPQueueStatsRequest(dp, port,
328                                                   queue_id, 0)
329    msgs = []
330    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
331
332    s = []
333    for msg in msgs:
334        stats = msg.body
335        for stat in stats:
336            s.append({'port_no': stat.port_no,
337                      'queue_id': stat.queue_id,
338                      'tx_bytes': stat.tx_bytes,
339                      'tx_errors': stat.tx_errors,
340                      'tx_packets': stat.tx_packets})
341
342    return {str(dp.id): s}
343
344
345def get_queue_config(dp, waiters, port=None):
346    ofp = dp.ofproto
347    if port is None:
348        port = ofp.OFPP_ANY
349    else:
350        port = UTIL.ofp_port_from_user(str_to_int(port))
351    stats = dp.ofproto_parser.OFPQueueGetConfigRequest(dp, port)
352    msgs = []
353    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
354
355    prop_type = {
356        dp.ofproto.OFPQT_MIN_RATE: 'MIN_RATE',
357        dp.ofproto.OFPQT_MAX_RATE: 'MAX_RATE',
358        dp.ofproto.OFPQT_EXPERIMENTER: 'EXPERIMENTER',
359    }
360
361    configs = []
362    for config in msgs:
363        queue_list = []
364        for queue in config.queues:
365            prop_list = []
366            for prop in queue.properties:
367                p = {'property': prop_type.get(prop.property, 'UNKNOWN')}
368                if prop.property == dp.ofproto.OFPQT_MIN_RATE or \
369                   prop.property == dp.ofproto.OFPQT_MAX_RATE:
370                    p['rate'] = prop.rate
371                elif prop.property == dp.ofproto.OFPQT_EXPERIMENTER:
372                    p['experimenter'] = prop.experimenter
373                    p['data'] = prop.data
374                prop_list.append(p)
375            q = {'port': UTIL.ofp_port_to_user(queue.port),
376                 'properties': prop_list,
377                 'queue_id': UTIL.ofp_queue_to_user(queue.queue_id)}
378            queue_list.append(q)
379        c = {'port': UTIL.ofp_port_to_user(config.port),
380             'queues': queue_list}
381        configs.append(c)
382
383    return {str(dp.id): configs}
384
385
386def get_flow_stats(dp, waiters, flow=None):
387    flow = flow if flow else {}
388    table_id = UTIL.ofp_table_from_user(
389        flow.get('table_id', dp.ofproto.OFPTT_ALL))
390    out_port = UTIL.ofp_port_from_user(
391        flow.get('out_port', dp.ofproto.OFPP_ANY))
392    out_group = UTIL.ofp_group_from_user(
393        flow.get('out_group', dp.ofproto.OFPG_ANY))
394    cookie = str_to_int(flow.get('cookie', 0))
395    cookie_mask = str_to_int(flow.get('cookie_mask', 0))
396    match = to_match(dp, flow.get('match', {}))
397    # Note: OpenFlow does not allow to filter flow entries by priority,
398    # but for efficiency, ofctl provides the way to do it.
399    priority = str_to_int(flow.get('priority', -1))
400
401    stats = dp.ofproto_parser.OFPFlowStatsRequest(
402        dp, table_id, out_port, out_group, cookie, cookie_mask, match)
403
404    msgs = []
405    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
406
407    flows = []
408    for msg in msgs:
409        for stats in msg.body:
410            if 0 <= priority != stats.priority:
411                continue
412
413            actions = actions_to_str(stats.instructions)
414            match = match_to_str(stats.match)
415            s = {'priority': stats.priority,
416                 'cookie': stats.cookie,
417                 'idle_timeout': stats.idle_timeout,
418                 'hard_timeout': stats.hard_timeout,
419                 'actions': actions,
420                 'match': match,
421                 'byte_count': stats.byte_count,
422                 'duration_sec': stats.duration_sec,
423                 'duration_nsec': stats.duration_nsec,
424                 'packet_count': stats.packet_count,
425                 'table_id': UTIL.ofp_table_to_user(stats.table_id),
426                 'length': stats.length}
427            flows.append(s)
428
429    return {str(dp.id): flows}
430
431
432def get_aggregate_flow_stats(dp, waiters, flow=None):
433    flow = flow if flow else {}
434    table_id = UTIL.ofp_table_from_user(
435        flow.get('table_id', dp.ofproto.OFPTT_ALL))
436    out_port = UTIL.ofp_port_from_user(
437        flow.get('out_port', dp.ofproto.OFPP_ANY))
438    out_group = UTIL.ofp_group_from_user(
439        flow.get('out_group', dp.ofproto.OFPG_ANY))
440    cookie = str_to_int(flow.get('cookie', 0))
441    cookie_mask = str_to_int(flow.get('cookie_mask', 0))
442    match = to_match(dp, flow.get('match', {}))
443
444    stats = dp.ofproto_parser.OFPAggregateStatsRequest(
445        dp, table_id, out_port, out_group, cookie, cookie_mask, match)
446
447    msgs = []
448    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
449
450    flows = []
451    for msg in msgs:
452        stats = msg.body
453        s = {'packet_count': stats.packet_count,
454             'byte_count': stats.byte_count,
455             'flow_count': stats.flow_count}
456        flows.append(s)
457
458    return {str(dp.id): flows}
459
460
461def get_table_stats(dp, waiters):
462    stats = dp.ofproto_parser.OFPTableStatsRequest(dp)
463    ofp = dp.ofproto
464    msgs = []
465    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
466
467    oxm_type_convert = {ofp.OFPXMT_OFB_IN_PORT: 'IN_PORT',
468                        ofp.OFPXMT_OFB_IN_PHY_PORT: 'IN_PHY_PORT',
469                        ofp.OFPXMT_OFB_METADATA: 'METADATA',
470                        ofp.OFPXMT_OFB_ETH_DST: 'ETH_DST',
471                        ofp.OFPXMT_OFB_ETH_SRC: 'ETH_SRC',
472                        ofp.OFPXMT_OFB_ETH_TYPE: 'ETH_TYPE',
473                        ofp.OFPXMT_OFB_VLAN_VID: 'VLAN_VID',
474                        ofp.OFPXMT_OFB_VLAN_PCP: 'VLAN_PCP',
475                        ofp.OFPXMT_OFB_IP_DSCP: 'IP_DSCP',
476                        ofp.OFPXMT_OFB_IP_ECN: 'IP_ECN',
477                        ofp.OFPXMT_OFB_IP_PROTO: 'IP_PROTO',
478                        ofp.OFPXMT_OFB_IPV4_SRC: 'IPV4_SRC',
479                        ofp.OFPXMT_OFB_IPV4_DST: 'IPV4_DST',
480                        ofp.OFPXMT_OFB_TCP_SRC: 'TCP_SRC',
481                        ofp.OFPXMT_OFB_TCP_DST: 'TCP_DST',
482                        ofp.OFPXMT_OFB_UDP_SRC: 'UDP_SRC',
483                        ofp.OFPXMT_OFB_UDP_DST: 'UDP_DST',
484                        ofp.OFPXMT_OFB_SCTP_SRC: 'SCTP_SRC',
485                        ofp.OFPXMT_OFB_SCTP_DST: 'SCTP_DST',
486                        ofp.OFPXMT_OFB_ICMPV4_TYPE: 'ICMPV4_TYPE',
487                        ofp.OFPXMT_OFB_ICMPV4_CODE: 'ICMPV4_CODE',
488                        ofp.OFPXMT_OFB_ARP_OP: 'ARP_OP',
489                        ofp.OFPXMT_OFB_ARP_SPA: 'ARP_SPA',
490                        ofp.OFPXMT_OFB_ARP_TPA: 'ARP_TPA',
491                        ofp.OFPXMT_OFB_ARP_SHA: 'ARP_SHA',
492                        ofp.OFPXMT_OFB_ARP_THA: 'ARP_THA',
493                        ofp.OFPXMT_OFB_IPV6_SRC: 'IPV6_SRC',
494                        ofp.OFPXMT_OFB_IPV6_DST: 'IPV6_DST',
495                        ofp.OFPXMT_OFB_IPV6_FLABEL: 'IPV6_FLABEL',
496                        ofp.OFPXMT_OFB_ICMPV6_TYPE: 'ICMPV6_TYPE',
497                        ofp.OFPXMT_OFB_ICMPV6_CODE: 'ICMPV6_CODE',
498                        ofp.OFPXMT_OFB_IPV6_ND_TARGET: 'IPV6_ND_TARGET',
499                        ofp.OFPXMT_OFB_IPV6_ND_SLL: 'IPV6_ND_SLL',
500                        ofp.OFPXMT_OFB_IPV6_ND_TLL: 'IPV6_ND_TLL',
501                        ofp.OFPXMT_OFB_MPLS_LABEL: 'MPLS_LABEL',
502                        ofp.OFPXMT_OFB_MPLS_TC: 'MPLS_TC'}
503
504    act_convert = {ofp.OFPAT_OUTPUT: 'OUTPUT',
505                   ofp.OFPAT_COPY_TTL_OUT: 'COPY_TTL_OUT',
506                   ofp.OFPAT_COPY_TTL_IN: 'COPY_TTL_IN',
507                   ofp.OFPAT_SET_MPLS_TTL: 'SET_MPLS_TTL',
508                   ofp.OFPAT_DEC_MPLS_TTL: 'DEC_MPLS_TTL',
509                   ofp.OFPAT_PUSH_VLAN: 'PUSH_VLAN',
510                   ofp.OFPAT_POP_VLAN: 'POP_VLAN',
511                   ofp.OFPAT_PUSH_MPLS: 'PUSH_MPLS',
512                   ofp.OFPAT_POP_MPLS: 'POP_MPLS',
513                   ofp.OFPAT_SET_QUEUE: 'SET_QUEUE',
514                   ofp.OFPAT_GROUP: 'GROUP',
515                   ofp.OFPAT_SET_NW_TTL: 'SET_NW_TTL',
516                   ofp.OFPAT_DEC_NW_TTL: 'DEC_NW_TTL',
517                   ofp.OFPAT_SET_FIELD: 'SET_FIELD'}
518
519    inst_convert = {ofp.OFPIT_GOTO_TABLE: 'GOTO_TABLE',
520                    ofp.OFPIT_WRITE_METADATA: 'WRITE_METADATA',
521                    ofp.OFPIT_WRITE_ACTIONS: 'WRITE_ACTIONS',
522                    ofp.OFPIT_APPLY_ACTIONS: 'APPLY_ACTIONS',
523                    ofp.OFPIT_CLEAR_ACTIONS: 'CLEAR_ACTIONS',
524                    ofp.OFPIT_EXPERIMENTER: 'EXPERIMENTER'}
525
526    table_conf_convert = {
527        ofp.OFPTC_TABLE_MISS_CONTROLLER: 'TABLE_MISS_CONTROLLER',
528        ofp.OFPTC_TABLE_MISS_CONTINUE: 'TABLE_MISS_CONTINUE',
529        ofp.OFPTC_TABLE_MISS_DROP: 'TABLE_MISS_DROP',
530        ofp.OFPTC_TABLE_MISS_MASK: 'TABLE_MISS_MASK'}
531
532    tables = []
533    for msg in msgs:
534        stats = msg.body
535        for stat in stats:
536            match = []
537            wildcards = []
538            write_setfields = []
539            apply_setfields = []
540            for k, v in oxm_type_convert.items():
541                if (1 << k) & stat.match:
542                    match.append(v)
543                if (1 << k) & stat.wildcards:
544                    wildcards.append(v)
545                if (1 << k) & stat.write_setfields:
546                    write_setfields.append(v)
547                if (1 << k) & stat.apply_setfields:
548                    apply_setfields.append(v)
549            write_actions = []
550            apply_actions = []
551            for k, v in act_convert.items():
552                if (1 << k) & stat.write_actions:
553                    write_actions.append(v)
554                if (1 << k) & stat.apply_actions:
555                    apply_actions.append(v)
556            instructions = []
557            for k, v in inst_convert.items():
558                if (1 << k) & stat.instructions:
559                    instructions.append(v)
560            config = []
561            for k, v in table_conf_convert.items():
562                if (1 << k) & stat.config:
563                    config.append(v)
564            s = {'table_id': UTIL.ofp_table_to_user(stat.table_id),
565                 'name': stat.name.decode('utf-8'),
566                 'match': match,
567                 'wildcards': wildcards,
568                 'write_actions': write_actions,
569                 'apply_actions': apply_actions,
570                 'write_setfields': write_setfields,
571                 'apply_setfields': apply_setfields,
572                 'metadata_match': stat.metadata_match,
573                 'metadata_write': stat.metadata_write,
574                 'instructions': instructions,
575                 'config': config,
576                 'max_entries': stat.max_entries,
577                 'active_count': stat.active_count,
578                 'lookup_count': stat.lookup_count,
579                 'matched_count': stat.matched_count}
580            tables.append(s)
581
582    return {str(dp.id): tables}
583
584
585def get_port_stats(dp, waiters, port=None):
586    if port is None:
587        port = dp.ofproto.OFPP_ANY
588    else:
589        port = str_to_int(port)
590
591    stats = dp.ofproto_parser.OFPPortStatsRequest(
592        dp, port, 0)
593    msgs = []
594    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
595
596    ports = []
597    for msg in msgs:
598        for stats in msg.body:
599            s = {'port_no': UTIL.ofp_port_to_user(stats.port_no),
600                 'rx_packets': stats.rx_packets,
601                 'tx_packets': stats.tx_packets,
602                 'rx_bytes': stats.rx_bytes,
603                 'tx_bytes': stats.tx_bytes,
604                 'rx_dropped': stats.rx_dropped,
605                 'tx_dropped': stats.tx_dropped,
606                 'rx_errors': stats.rx_errors,
607                 'tx_errors': stats.tx_errors,
608                 'rx_frame_err': stats.rx_frame_err,
609                 'rx_over_err': stats.rx_over_err,
610                 'rx_crc_err': stats.rx_crc_err,
611                 'collisions': stats.collisions}
612            ports.append(s)
613
614    return {str(dp.id): ports}
615
616
617def get_group_stats(dp, waiters, group_id=None):
618    if group_id is None:
619        group_id = dp.ofproto.OFPG_ALL
620    else:
621        group_id = str_to_int(group_id)
622
623    stats = dp.ofproto_parser.OFPGroupStatsRequest(
624        dp, group_id, 0)
625    msgs = []
626    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
627
628    groups = []
629    for msg in msgs:
630        for stats in msg.body:
631            bucket_counters = []
632            for bucket_counter in stats.bucket_counters:
633                c = {'packet_count': bucket_counter.packet_count,
634                     'byte_count': bucket_counter.byte_count}
635                bucket_counters.append(c)
636            g = {'length': stats.length,
637                 'group_id': UTIL.ofp_group_to_user(stats.group_id),
638                 'ref_count': stats.ref_count,
639                 'packet_count': stats.packet_count,
640                 'byte_count': stats.byte_count,
641                 'bucket_stats': bucket_counters}
642            groups.append(g)
643
644    return {str(dp.id): groups}
645
646
647def get_group_features(dp, waiters):
648
649    ofp = dp.ofproto
650    type_convert = {ofp.OFPGT_ALL: 'ALL',
651                    ofp.OFPGT_SELECT: 'SELECT',
652                    ofp.OFPGT_INDIRECT: 'INDIRECT',
653                    ofp.OFPGT_FF: 'FF'}
654    cap_convert = {ofp.OFPGFC_SELECT_WEIGHT: 'SELECT_WEIGHT',
655                   ofp.OFPGFC_SELECT_LIVENESS: 'SELECT_LIVENESS',
656                   ofp.OFPGFC_CHAINING: 'CHAINING',
657                   ofp.OFPGFC_CHAINING_CHECKS: 'CHAINING_CHECKS'}
658    act_convert = {ofp.OFPAT_OUTPUT: 'OUTPUT',
659                   ofp.OFPAT_COPY_TTL_OUT: 'COPY_TTL_OUT',
660                   ofp.OFPAT_COPY_TTL_IN: 'COPY_TTL_IN',
661                   ofp.OFPAT_SET_MPLS_TTL: 'SET_MPLS_TTL',
662                   ofp.OFPAT_DEC_MPLS_TTL: 'DEC_MPLS_TTL',
663                   ofp.OFPAT_PUSH_VLAN: 'PUSH_VLAN',
664                   ofp.OFPAT_POP_VLAN: 'POP_VLAN',
665                   ofp.OFPAT_PUSH_MPLS: 'PUSH_MPLS',
666                   ofp.OFPAT_POP_MPLS: 'POP_MPLS',
667                   ofp.OFPAT_SET_QUEUE: 'SET_QUEUE',
668                   ofp.OFPAT_GROUP: 'GROUP',
669                   ofp.OFPAT_SET_NW_TTL: 'SET_NW_TTL',
670                   ofp.OFPAT_DEC_NW_TTL: 'DEC_NW_TTL',
671                   ofp.OFPAT_SET_FIELD: 'SET_FIELD'}
672
673    stats = dp.ofproto_parser.OFPGroupFeaturesStatsRequest(dp, 0)
674    msgs = []
675    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
676
677    features = []
678    for msg in msgs:
679        feature = msg.body
680        types = []
681        for k, v in type_convert.items():
682            if (1 << k) & feature.types:
683                types.append(v)
684        capabilities = []
685        for k, v in cap_convert.items():
686            if k & feature.capabilities:
687                capabilities.append(v)
688        max_groups = []
689        for k, v in type_convert.items():
690            max_groups.append({v: feature.max_groups[k]})
691        actions = []
692        for k1, v1 in type_convert.items():
693            acts = []
694            for k2, v2 in act_convert.items():
695                if (1 << k2) & feature.actions[k1]:
696                    acts.append(v2)
697            actions.append({v1: acts})
698        f = {'types': types,
699             'capabilities': capabilities,
700             'max_groups': max_groups,
701             'actions': actions}
702        features.append(f)
703
704    return {str(dp.id): features}
705
706
707def get_group_desc(dp, waiters):
708
709    type_convert = {dp.ofproto.OFPGT_ALL: 'ALL',
710                    dp.ofproto.OFPGT_SELECT: 'SELECT',
711                    dp.ofproto.OFPGT_INDIRECT: 'INDIRECT',
712                    dp.ofproto.OFPGT_FF: 'FF'}
713
714    stats = dp.ofproto_parser.OFPGroupDescStatsRequest(dp, 0)
715    msgs = []
716    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
717
718    descs = []
719    for msg in msgs:
720        for stats in msg.body:
721            buckets = []
722            for bucket in stats.buckets:
723                actions = []
724                for action in bucket.actions:
725                    actions.append(action_to_str(action))
726                b = {'weight': bucket.weight,
727                     'watch_port': bucket.watch_port,
728                     'watch_group': bucket.watch_group,
729                     'actions': actions}
730                buckets.append(b)
731            d = {'type': type_convert.get(stats.type),
732                 'group_id': UTIL.ofp_group_to_user(stats.group_id),
733                 'buckets': buckets}
734            descs.append(d)
735
736    return {str(dp.id): descs}
737
738
739def get_port_desc(dp, waiters):
740
741    stats = dp.ofproto_parser.OFPFeaturesRequest(dp)
742    msgs = []
743    ofctl_utils.send_stats_request(dp, stats, waiters, msgs, LOG)
744
745    descs = []
746
747    for msg in msgs:
748        stats = msg.ports
749        for stat in stats.values():
750            d = {'port_no': UTIL.ofp_port_to_user(stat.port_no),
751                 'hw_addr': stat.hw_addr,
752                 'name': stat.name.decode('utf-8'),
753                 'config': stat.config,
754                 'state': stat.state,
755                 'curr': stat.curr,
756                 'advertised': stat.advertised,
757                 'supported': stat.supported,
758                 'peer': stat.peer,
759                 'curr_speed': stat.curr_speed,
760                 'max_speed': stat.max_speed}
761            descs.append(d)
762
763    return {str(dp.id): descs}
764
765
766def get_role(dp, waiters, to_user=True):
767    return ofctl_utils.get_role(dp, waiters, to_user)
768
769
770def mod_flow_entry(dp, flow, cmd):
771    cookie = str_to_int(flow.get('cookie', 0))
772    cookie_mask = str_to_int(flow.get('cookie_mask', 0))
773    table_id = UTIL.ofp_table_from_user(flow.get('table_id', 0))
774    idle_timeout = str_to_int(flow.get('idle_timeout', 0))
775    hard_timeout = str_to_int(flow.get('hard_timeout', 0))
776    priority = str_to_int(flow.get('priority', 0))
777    buffer_id = UTIL.ofp_buffer_from_user(
778        flow.get('buffer_id', dp.ofproto.OFP_NO_BUFFER))
779    out_port = UTIL.ofp_port_from_user(
780        flow.get('out_port', dp.ofproto.OFPP_ANY))
781    out_group = UTIL.ofp_group_from_user(
782        flow.get('out_group', dp.ofproto.OFPG_ANY))
783    flags = str_to_int(flow.get('flags', 0))
784    match = to_match(dp, flow.get('match', {}))
785    inst = to_actions(dp, flow.get('actions', []))
786
787    flow_mod = dp.ofproto_parser.OFPFlowMod(
788        dp, cookie, cookie_mask, table_id, cmd, idle_timeout,
789        hard_timeout, priority, buffer_id, out_port, out_group,
790        flags, match, inst)
791
792    ofctl_utils.send_msg(dp, flow_mod, LOG)
793
794
795def mod_group_entry(dp, group, cmd):
796
797    type_convert = {'ALL': dp.ofproto.OFPGT_ALL,
798                    'SELECT': dp.ofproto.OFPGT_SELECT,
799                    'INDIRECT': dp.ofproto.OFPGT_INDIRECT,
800                    'FF': dp.ofproto.OFPGT_FF}
801
802    type_ = type_convert.get(group.get('type', 'ALL'))
803    if type_ is None:
804        LOG.error('Unknown group type: %s', group.get('type'))
805
806    group_id = UTIL.ofp_group_from_user(group.get('group_id', 0))
807
808    buckets = []
809    for bucket in group.get('buckets', []):
810        weight = str_to_int(bucket.get('weight', 0))
811        watch_port = str_to_int(
812            bucket.get('watch_port', dp.ofproto.OFPP_ANY))
813        watch_group = str_to_int(
814            bucket.get('watch_group', dp.ofproto.OFPG_ANY))
815        actions = []
816        for dic in bucket.get('actions', []):
817            action = to_action(dp, dic)
818            if action is not None:
819                actions.append(action)
820        buckets.append(dp.ofproto_parser.OFPBucket(
821            weight, watch_port, watch_group, actions))
822
823    group_mod = dp.ofproto_parser.OFPGroupMod(
824        dp, cmd, type_, group_id, buckets)
825
826    ofctl_utils.send_msg(dp, group_mod, LOG)
827
828
829def mod_port_behavior(dp, port_config):
830    port_no = UTIL.ofp_port_from_user(port_config.get('port_no', 0))
831    hw_addr = str(port_config.get('hw_addr'))
832    config = str_to_int(port_config.get('config', 0))
833    mask = str_to_int(port_config.get('mask', 0))
834    advertise = str_to_int(port_config.get('advertise'))
835
836    port_mod = dp.ofproto_parser.OFPPortMod(
837        dp, port_no, hw_addr, config, mask, advertise)
838
839    ofctl_utils.send_msg(dp, port_mod, LOG)
840
841
842def set_role(dp, role):
843    r = UTIL.ofp_role_from_user(role.get('role', dp.ofproto.OFPCR_ROLE_EQUAL))
844    role_request = dp.ofproto_parser.OFPRoleRequest(dp, r, 0)
845    ofctl_utils.send_msg(dp, role_request, LOG)
846
847
848# NOTE(jkoelker) Alias common funcitons
849send_experimenter = ofctl_utils.send_experimenter
850