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

build.match.format_request()   A

Complexity

Conditions 2

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 4.048

Importance

Changes 0
Metric Value
cc 2
eloc 5
nop 1
dl 0
loc 6
ccs 1
cts 5
cp 0.2
crap 4.048
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'] & flow_to_install['cookie_mask']
137 1
        stored_cookie = (stored_flow_dict['cookie'] &
138
                         flow_to_install['cookie_mask'])
139 1
        if cookie == stored_cookie:
140 1
            return stored_flow_dict
141 1
        return False
142 1
    if 'match' not in flow_to_install:
143
        return False
144
145 1
    for key, value in flow_to_install.get('match').items():
146 1
        if 'match' not in stored_flow_dict:
147
            return False
148 1
        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 1
            field = stored_flow_dict['match'].get(key)
153 1
            if not field:
154 1
                return False
155 1
            masked_ip_addr = ipaddress.ip_network(value, False)
156 1
            field_mask = field + "/" + str(masked_ip_addr.netmask)
157 1
            masked_stored_ip = ipaddress.ip_network(field_mask, False)
158 1
            if masked_ip_addr == masked_stored_ip:
159 1
                return stored_flow_dict
160 1
    return False
161
162
163 1
def match_flows(flow_to_install, version, stored_flow_dict):
164
    # pylint: disable=bad-staticmethod-argument
165
    """
166
    Match the packet in request against the flows installed in the switch.
167
168
    Try the match with each flow, in other. If many is True, tries the
169
    match with all flows, if False, tries until the first match.
170
    :param args: packet data
171
    :param many: Boolean, indicating whether to continue after matching the
172
            first flow or not
173
    :return: If many, the list of matched flows, or the matched flow
174
    """
175 1
    match = do_match(flow_to_install, version, stored_flow_dict)
176
    return match
177