Test Failed
Pull Request — master (#117)
by Carlos
02:30
created

build.match.match_flows()   A

Complexity

Conditions 1

Size

Total Lines 14
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nop 3
dl 0
loc 14
rs 10
c 0
b 0
f 0
1
"""Switch match."""
2
3
import ipaddress
4
5
from pyof.v0x01.common.flow_match import FlowWildCards
6
7
IPV4_ETH_TYPE = 2048
8
9
10
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
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
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
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
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
def match13_no_strict(flow_to_install, stored_flow_dict):
151
    """Match a packet againts the stored flow (OF 1.3).
152
    Return the flow if any fields match, otherwise, return False.
153
    """
154
    if flow_to_install.get('cookie_mask') and 'cookie' in stored_flow_dict:
155
        cookie = flow_to_install['cookie_mask'] & flow_to_install['cookie']
156
        if cookie == stored_flow_dict['cookie']:
157
            return stored_flow_dict
158
        return False
159
160
    if 'match' not in flow_to_install:
161
        return False
162
163
    for key, value in flow_to_install.get('match').items():
164
        if 'match' in stored_flow_dict:
165
            if value == stored_flow_dict['match'].get(key):
166
                return stored_flow_dict
167
    return None
168
169
170
def match_flows(flow_to_install, version, stored_flow_dict):
171
    # pylint: disable=bad-staticmethod-argument
172
    """
173
    Match the packet in request against the flows installed in the switch.
174
175
    Try the match with each flow, in other. If many is True, tries the
176
    match with all flows, if False, tries until the first match.
177
    :param args: packet data
178
    :param many: Boolean, indicating whether to continue after matching the
179
            first flow or not
180
    :return: If many, the list of matched flows, or the matched flow
181
    """
182
    match = do_match(flow_to_install, version, stored_flow_dict)
183
    return match
184