Passed
Pull Request — master (#117)
by Carlos
02:51
created

build.match.match10()   F

Complexity

Conditions 20

Size

Total Lines 38
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 388.4669

Importance

Changes 0
Metric Value
cc 20
eloc 37
nop 2
dl 0
loc 38
ccs 1
cts 37
cp 0.027
crap 388.4669
rs 0
c 0
b 0
f 0

How to fix   Complexity   

Complexity

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