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

build.match   B

Complexity

Total Complexity 51

Size/Duplication

Total Lines 139
Duplicated Lines 0 %

Test Coverage

Coverage 63.73%

Importance

Changes 0
Metric Value
eloc 109
dl 0
loc 139
rs 7.92
c 0
b 0
f 0
ccs 65
cts 102
cp 0.6373
wmc 51

5 Functions

Rating   Name   Duplication   Size   Complexity  
C match13_no_strict() 0 31 11
F _match_ipv4_10() 0 43 20
F match10() 0 26 14
A _get_match_fields() 0 7 3
A match_flow() 0 12 3

How to fix   Complexity   

Complexity

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