Passed
Pull Request — master (#117)
by Carlos
03:31
created

build.match.match_flows()   A

Complexity

Conditions 1

Size

Total Lines 14
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1.2963

Importance

Changes 0
Metric Value
cc 1
eloc 3
nop 3
dl 0
loc 14
ccs 1
cts 3
cp 0.3333
crap 1.2963
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
    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 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(match_fields, args, wildcards):
38
    """Match IPV4."""
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
55
    flow_ip_int = int(ipaddress.IPv4Address(match_fields['ipv4_dst']))
56
    if flow_ip_int != 0:
57
        mask = (wildcards
58
                & FlowWildCards.OFPFW_NW_DST_MASK) >> \
59
                FlowWildCards.OFPFW_NW_DST_SHIFT
60
        if mask > 32:
61
            mask = 32
62
        if mask != 32 and 'ipv4_dst' not in args:
63
            return False
64
        mask = (0xffffffff << mask) & 0xffffffff
65
        ip_int = int(ipaddress.IPv4Address(args.get('ipv4_dst')))
66
        if ip_int & mask != flow_ip_int & mask:
67
            return False
68
    if not wildcards & FlowWildCards.OFPFW_NW_TOS:
69
        if 'ip_tos' not in args:
70
            return False
71
        if match_fields.get('ip_tos') != int(args.get('ip_tos')):
72
            return False
73
    if not wildcards & FlowWildCards.OFPFW_NW_PROTO:
74
        if 'ip_proto' not in args:
75
            return False
76
        if match_fields.get('ip_proto') != int(args.get('ip_proto')):
77
            return False
78
    if not wildcards & FlowWildCards.OFPFW_TP_SRC:
79
        if 'tp_src' not in args:
80
            return False
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 'tp_dst' not in args:
85
            return False
86
        if match_fields.get('tcp_dst') != int(args.get('tp_dst')):
87
            return False
88
    return True
89
90
91
# pylint: disable=too-many-return-statements, too-many-statements, R0912
92 1
def match10(flow_dict, args):
93
    """Match a packet against this flow (OF1.0)."""
94
    match_fields = _get_match_fields(flow_dict)
95
    wildcards = match_fields.get('wildcards')
96
    if not wildcards & FlowWildCards.OFPFW_IN_PORT:
97
        if 'in_port' not in args:
98
            return False
99
        if match_fields.get('in_port') != int(args.get('in_port')):
100
            return False
101
    if not wildcards & FlowWildCards.OFPFW_DL_VLAN_PCP:
102
        if 'vlan_pcp' not in args:
103
            return False
104
        if match_fields.get('vlan_pcp') != int(args.get('vlan_pcp')):
105
            return False
106
    if not wildcards & FlowWildCards.OFPFW_DL_VLAN:
107
        if 'vlan_vid' not in args:
108
            return False
109
        if match_fields.get('vlan_vid') != args.get('vlan_vid')[-1]:
110
            return False
111
    if not wildcards & FlowWildCards.OFPFW_DL_SRC:
112
        if 'eth_src' not in args:
113
            return False
114
        if match_fields.get('eth_src') != args.get('eth_src'):
115
            return False
116
    if not wildcards & FlowWildCards.OFPFW_DL_DST:
117
        if 'eth_dst' not in args:
118
            return False
119
        if match_fields.get('eth_dst') != args.get('eth_dst'):
120
            return False
121
    if not wildcards & FlowWildCards.OFPFW_DL_TYPE:
122
        if 'eth_type' not in args:
123
            return False
124
        if match_fields.get('eth_type') != int(args.get('eth_type')):
125
            return False
126
    if not _match_ipv4(match_fields, args, wildcards):
127
        return False
128
    return flow_dict
129
130
    # def match13(self, args):
131
    #     """Match a packet against this flow (OF1.3)."""
132
    #     for name in self.match:
133
    #         if name not in args:
134
    #             return False
135
    #         if name == 'vlan_vid':
136
    #             field = args[name][-1]
137
    #         else:
138
    #             field = args[name]
139
    #         if name not in ('ipv4_src', 'ipv4_dst', 'ipv6_src', 'ipv6_dst'):
140
    #             if self.match[name].value != field:
141
    #                 return False
142
    #         else:
143
    #             packet_ip = int(ipaddress.ip_address(field))
144
    #             ip_addr = self.match[name].value
145
    #             if packet_ip & ip_addr.netmask != ip_addr.address:
146
    #                 return False
147
    #     return self
148
149
150 1
def match13_no_strict(flow_to_install, stored_flow_dict):
151
    """Match a packet againts the stored flow (OF 1.3).
152
153
    Return the flow if any fields match, otherwise, return False.
154
    """
155
    if flow_to_install.get('cookie_mask') and 'cookie' in stored_flow_dict:
156
        cookie = flow_to_install['cookie_mask'] & flow_to_install['cookie']
157
        if cookie == stored_flow_dict['cookie']:
158
            return stored_flow_dict
159
        return False
160
161
    if 'match' not in flow_to_install:
162
        return False
163
164
    for key, value in flow_to_install.get('match').items():
165
        if 'match' not in stored_flow_dict:
166
            return False
167
        if key not in ('ipv4_src', 'ipv4_dst', 'ipv6_src', 'ipv6_dst'):
168
            if value == stored_flow_dict['match'].get(key):
169
                return stored_flow_dict
170
        else:
171
            field = flow_to_install.get(key)
172
            packet_ip = int(ipaddress.ip_address(field))
173
            ip_addr = value
174
            if packet_ip & ip_addr.netmask == ip_addr.address:
175
                return stored_flow_dict
176
177
    return False
178
179
180 1
def match_flows(flow_to_install, version, stored_flow_dict):
181
    # pylint: disable=bad-staticmethod-argument
182
    """
183
    Match the packet in request against the flows installed in the switch.
184
185
    Try the match with each flow, in other. If many is True, tries the
186
    match with all flows, if False, tries until the first match.
187
    :param args: packet data
188
    :param many: Boolean, indicating whether to continue after matching the
189
            first flow or not
190
    :return: If many, the list of matched flows, or the matched flow
191
    """
192
    match = do_match(flow_to_install, version, stored_flow_dict)
193
    return match
194