Passed
Pull Request — master (#117)
by Carlos
03:27 queued 40s
created

build.match._get_match_fields()   A

Complexity

Conditions 3

Size

Total Lines 7
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 8.2077

Importance

Changes 0
Metric Value
cc 3
eloc 6
nop 1
dl 0
loc 7
ccs 1
cts 6
cp 0.1666
crap 8.2077
rs 10
c 0
b 0
f 0
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 do_match(flow_to_install, version, stored_flow_dict):
19
    """Match a packet against this flow."""
20 1
    if version == 0x01:
21
        return match10(stored_flow_dict, flow_to_install)
22 1
    elif version == 0x04:
23 1
        return match13_no_strict(flow_to_install, stored_flow_dict)
24
    raise NotImplementedError(f'Unsupported OpenFlow version {version}')
25
26
27 1
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 1
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 1
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 1
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 1
    if flow_to_install.get('cookie_mask') and 'cookie' in stored_flow_dict:
136 1
        cookie = flow_to_install['cookie_mask'] & flow_to_install['cookie']
137 1
        stored_cookie = stored_flow_dict['cookie'] & flow_to_install['cookie']
138 1
        if cookie == stored_cookie:
139 1
            return stored_flow_dict
140 1
        return False
141
142
    if 'match' not in flow_to_install:
143
        return False
144
145
    for key, value in flow_to_install.get('match').items():
146
        if 'match' not in stored_flow_dict:
147
            return False
148
        if key not in ('ipv4_src', 'ipv4_dst', 'ipv6_src', 'ipv6_dst'):
149
            if value == stored_flow_dict['match'].get(key):
150
                return stored_flow_dict
151
        else:
152
            field = flow_to_install.get(key)
153
            packet_ip = int(ipaddress.ip_address(field))
154
            ip_addr = value
155
            if packet_ip & ip_addr.netmask == ip_addr.address:
156
                return stored_flow_dict
157
158
    return False
159
160
161 1
def match_flows(flow_to_install, version, stored_flow_dict):
162
    # pylint: disable=bad-staticmethod-argument
163
    """
164
    Match the packet in request against the flows installed in the switch.
165
166
    Try the match with each flow, in other. If many is True, tries the
167
    match with all flows, if False, tries until the first match.
168
    :param args: packet data
169
    :param many: Boolean, indicating whether to continue after matching the
170
            first flow or not
171
    :return: If many, the list of matched flows, or the matched flow
172
    """
173 1
    match = do_match(flow_to_install, version, stored_flow_dict)
174
    return match
175