Test Failed
Pull Request — master (#117)
by Carlos
63:01 queued 14:44
created

build.match.match13_no_strict()   C

Complexity

Conditions 11

Size

Total Lines 30
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 24
nop 2
dl 0
loc 30
rs 5.4
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like build.match.match13_no_strict() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
"""Switch match."""
2
3
import ipaddress
4
5
from pyof.v0x01.common.flow_match import FlowWildCards
6
7
IPV4_ETH_TYPE = 2048
8
9
10
def format_request(request):
11
    """Format user request to match function format."""
12
    args = {}
13
    for key, value in request.items():
14
        args[key] = value
15
    return args
16
17
18
def do_match(flow_to_install, version, stored_flow_dict):
19
    """Match a packet against this flow."""
20
    if version == 0x01:
21
        return match10(stored_flow_dict, flow_to_install)
22
    elif version == 0x04:
23
        return match13_no_strict(flow_to_install, stored_flow_dict)
24
    raise NotImplementedError(f'Unsupported OpenFlow version {version}')
25
26
27
def _get_match_fields(flow_dict):
28
    """Generate match fields."""
29
    match_fields = {}
30
    if 'match' in flow_dict:
31
        for key, value in flow_dict['match'].items():
32
            match_fields[key] = value
33
    return match_fields
34
35
36
# pylint: disable=too-many-return-statements, too-many-statements, R0912
37
def _match_ipv4_10(match_fields, args, wildcards):
38
    """Match IPV4 fields against packet with Flow (OF1.0)."""
39
    if not match_fields['eth_type'] == IPV4_ETH_TYPE:
40
        return False
41
    flow_ip_int = int(ipaddress.IPv4Address(match_fields.get('ipv4_src')))
42
    if flow_ip_int != 0:
43
        mask = (wildcards
44
                & FlowWildCards.OFPFW_NW_SRC_MASK) >> \
45
                FlowWildCards.OFPFW_NW_SRC_SHIFT
46
        if mask > 32:
47
            mask = 32
48
        if mask != 32 and 'ipv4_src' not in args:
49
            return False
50
        mask = (0xffffffff << mask) & 0xffffffff
51
        ip_int = int(ipaddress.IPv4Address(args.get('ipv4_src')))
52
        if ip_int & mask != flow_ip_int & mask:
53
            return False
54
    flow_ip_int = int(ipaddress.IPv4Address(match_fields['ipv4_dst']))
55
    if flow_ip_int != 0:
56
        mask = (wildcards
57
                & FlowWildCards.OFPFW_NW_DST_MASK) >> \
58
                FlowWildCards.OFPFW_NW_DST_SHIFT
59
        if mask > 32:
60
            mask = 32
61
        if mask != 32 and 'ipv4_dst' not in args:
62
            return False
63
        mask = (0xffffffff << mask) & 0xffffffff
64
        ip_int = int(ipaddress.IPv4Address(args.get('ipv4_dst')))
65
        if ip_int & mask != flow_ip_int & mask:
66
            return False
67
    if not wildcards & FlowWildCards.OFPFW_NW_TOS:
68
        if 'ip_tos' not in args:
69
            return False
70
        if match_fields.get('ip_tos') != int(args.get('ip_tos')):
71
            return False
72
    if not wildcards & FlowWildCards.OFPFW_NW_PROTO:
73
        if 'ip_proto' not in args:
74
            return False
75
        if match_fields.get('ip_proto') != int(args.get('ip_proto')):
76
            return False
77
    if not wildcards & FlowWildCards.OFPFW_TP_SRC:
78
        if 'tp_src' not in args:
79
            return False
80
        if match_fields.get('tcp_src') != int(args.get('tp_src')):
81
            return False
82
    if not wildcards & FlowWildCards.OFPFW_TP_DST:
83
        if 'tp_dst' not in args:
84
            return False
85
        if match_fields.get('tcp_dst') != int(args.get('tp_dst')):
86
            return False
87
    return True
88
89
90
# pylint: disable=too-many-return-statements, too-many-statements, R0912
91
def match10(flow_dict, args):
92
    """Match a packet against this flow (OF1.0)."""
93
    match_fields = _get_match_fields(flow_dict)
94
    wildcards = match_fields.get('wildcards')
95
    if not wildcards & FlowWildCards.OFPFW_IN_PORT:
96
        if 'in_port' not in args:
97
            return False
98
        if match_fields.get('in_port') != int(args.get('in_port')):
99
            return False
100
    if not wildcards & FlowWildCards.OFPFW_DL_VLAN_PCP:
101
        if 'vlan_pcp' not in args:
102
            return False
103
        if match_fields.get('vlan_pcp') != int(args.get('vlan_pcp')):
104
            return False
105
    if not wildcards & FlowWildCards.OFPFW_DL_VLAN:
106
        if 'vlan_vid' not in args:
107
            return False
108
        if match_fields.get('vlan_vid') != args.get('vlan_vid')[-1]:
109
            return False
110
    if not wildcards & FlowWildCards.OFPFW_DL_SRC:
111
        if 'eth_src' not in args:
112
            return False
113
        if match_fields.get('eth_src') != args.get('eth_src'):
114
            return False
115
    if not wildcards & FlowWildCards.OFPFW_DL_DST:
116
        if 'eth_dst' not in args:
117
            return False
118
        if match_fields.get('eth_dst') != args.get('eth_dst'):
119
            return False
120
    if not wildcards & FlowWildCards.OFPFW_DL_TYPE:
121
        if 'eth_type' not in args:
122
            return False
123
        if match_fields.get('eth_type') != int(args.get('eth_type')):
124
            return False
125
    if not _match_ipv4_10(match_fields, args, wildcards):
126
        return False
127
    return flow_dict
128
129
130
def match13_no_strict(flow_to_install, stored_flow_dict):
131
    """Match a packet againts the stored flow (OF 1.3).
132
133
    Return the flow if any fields match, otherwise, return False.
134
    """
135
    if flow_to_install.get('cookie_mask') and 'cookie' in stored_flow_dict:
136
        cookie = flow_to_install['cookie'] & flow_to_install['cookie_mask']
137
        stored_cookie = stored_flow_dict['cookie'] & flow_to_install['cookie_mask']
138
        if cookie == stored_cookie:
139
            return stored_flow_dict
140
        return False
141
    if 'match' not in flow_to_install:
142
        return False
143
144
    for key, value in flow_to_install.get('match').items():
145
        if 'match' not in stored_flow_dict:
146
            return False
147
        if key not in ('ipv4_src', 'ipv4_dst', 'ipv6_src', 'ipv6_dst'):
148
            if value == stored_flow_dict['match'].get(key):
149
                return stored_flow_dict
150
        else:
151
            field = stored_flow_dict['match'].get(key)
152
            if not field:
153
                return False
154
            masked_ip_addr = ipaddress.ip_network(value, False)
155
            field_mask = field + "/" + str(masked_ip_addr.netmask)
156
            masked_stored_ip = ipaddress.ip_network(field_mask, False)
157
            if masked_ip_addr == masked_stored_ip:
158
                return stored_flow_dict
159
    return False
160
161
162
def match_flows(flow_to_install, version, stored_flow_dict):
163
    # pylint: disable=bad-staticmethod-argument
164
    """
165
    Match the packet in request against the flows installed in the switch.
166
167
    Try the match with each flow, in other. If many is True, tries the
168
    match with all flows, if False, tries until the first match.
169
    :param args: packet data
170
    :param many: Boolean, indicating whether to continue after matching the
171
            first flow or not
172
    :return: If many, the list of matched flows, or the matched flow
173
    """
174
    match = do_match(flow_to_install, version, stored_flow_dict)
175
    return match
176