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

build.match.match13_no_strict()   C

Complexity

Conditions 11

Size

Total Lines 31
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 11.5605

Importance

Changes 0
Metric Value
cc 11
eloc 25
nop 2
dl 0
loc 31
ccs 20
cts 24
cp 0.8333
crap 11.5605
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 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 'eth_type' in (match_fields, wildcards):
45
    #     return False
46
    # if not match_fields['eth_type'] == IPV4_ETH_TYPE:
47
    #     return False
48
    flow_ip_int = int(ipaddress.IPv4Address(match_fields.get('ipv4_src')))
49
    if flow_ip_int != 0:
50
        mask = (wildcards
51
                & FlowWildCards.OFPFW_NW_SRC_MASK) >> \
52
                FlowWildCards.OFPFW_NW_SRC_SHIFT
53
        if mask > 32:
54
            mask = 32
55
        if mask != 32 and 'ipv4_src' not in args:
56
            return False
57
        mask = (0xffffffff << mask) & 0xffffffff
58
        ip_int = int(ipaddress.IPv4Address(args.get('ipv4_src')))
59
        if ip_int & mask != flow_ip_int & mask:
60
            return False
61
    flow_ip_int = int(ipaddress.IPv4Address(match_fields['ipv4_dst']))
62
    if flow_ip_int != 0:
63
        mask = (wildcards
64
                & FlowWildCards.OFPFW_NW_DST_MASK) >> \
65
                FlowWildCards.OFPFW_NW_DST_SHIFT
66
        if mask > 32:
67
            mask = 32
68
        if mask != 32 and 'ipv4_dst' not in args:
69
            return False
70
        mask = (0xffffffff << mask) & 0xffffffff
71
        ip_int = int(ipaddress.IPv4Address(args.get('ipv4_dst')))
72
        if ip_int & mask != flow_ip_int & mask:
73
            return False
74
    if not wildcards & FlowWildCards.OFPFW_NW_TOS:
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 match_fields.get('ip_proto') != int(args.get('ip_proto')):
79
            return False
80
    if not wildcards & FlowWildCards.OFPFW_TP_SRC:
81
        if match_fields.get('tcp_src') != int(args.get('tp_src')):
82
            return False
83
    if not wildcards & FlowWildCards.OFPFW_TP_DST:
84
        if match_fields.get('tcp_dst') != int(args.get('tp_dst')):
85
            return False
86
    return True
87
88
89
# pylint: disable=too-many-return-statements, too-many-statements, R0912
90 1
def match10(flow_dict, args):
91
    """Match a packet against this flow (OF1.0)."""
92
    args = _get_match_fields(args)
93
    match_fields = _get_match_fields(flow_dict)
94
    wildcards = match_fields.get('wildcards', 0)
95
    if not wildcards & FlowWildCards.OFPFW_IN_PORT:
96
        if match_fields.get('in_port') != args.get('in_port'):
97
            return False
98
    if not wildcards & FlowWildCards.OFPFW_DL_VLAN_PCP:
99
        if match_fields.get('vlan_pcp') != args.get('vlan_pcp'):
100
            return False
101
    if not wildcards & FlowWildCards.OFPFW_DL_VLAN:
102
        if match_fields.get('vlan_vid') != args.get('vlan_vid'):
103
            return False
104
    if not wildcards & FlowWildCards.OFPFW_DL_SRC:
105
        if match_fields.get('eth_src') != args.get('eth_src'):
106
            return False
107
    if not wildcards & FlowWildCards.OFPFW_DL_DST:
108
        if match_fields.get('eth_dst') != args.get('eth_dst'):
109
            return False
110
    if not wildcards & FlowWildCards.OFPFW_DL_TYPE:
111
        if match_fields.get('eth_type') != args.get('eth_type'):
112
            return False
113
    if not _match_ipv4_10(match_fields, args, wildcards):
114
        return False
115
    return flow_dict
116
117
118 1
def match13_no_strict(flow_to_install, stored_flow_dict):
119
    """Match a packet againts the stored flow (OF 1.3).
120
121
    Return the flow if any fields match, otherwise, return False.
122
    """
123 1
    if flow_to_install.get('cookie_mask') and 'cookie' in stored_flow_dict:
124 1
        cookie = flow_to_install['cookie'] & flow_to_install['cookie_mask']
125 1
        stored_cookie = (stored_flow_dict['cookie'] &
126
                         flow_to_install['cookie_mask'])
127 1
        if cookie == stored_cookie:
128 1
            return stored_flow_dict
129 1
        return False
130 1
    if 'match' not in flow_to_install:
131
        return False
132
133 1
    for key, value in flow_to_install.get('match').items():
134 1
        if 'match' not in stored_flow_dict:
135
            return False
136 1
        if key not in ('ipv4_src', 'ipv4_dst', 'ipv6_src', 'ipv6_dst'):
137
            if value == stored_flow_dict['match'].get(key):
138
                return stored_flow_dict
139
        else:
140 1
            field = stored_flow_dict['match'].get(key)
141 1
            if not field:
142 1
                return False
143 1
            masked_ip_addr = ipaddress.ip_network(value, False)
144 1
            field_mask = field + "/" + str(masked_ip_addr.netmask)
145 1
            masked_stored_ip = ipaddress.ip_network(field_mask, False)
146 1
            if masked_ip_addr == masked_stored_ip:
147 1
                return stored_flow_dict
148
    return False
149