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_no_strict(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 | if match_fields.get('dl_type') == IPV4_ETH_TYPE: |
|
37 | return False |
||
38 | 1 | flow_ip_int = int(ipaddress.IPv4Address(match_fields.get('nw_src', 0))) |
|
39 | 1 | if flow_ip_int != 0: |
|
40 | mask = (wildcards |
||
41 | & FlowWildCards.OFPFW_NW_SRC_MASK) >> \ |
||
42 | FlowWildCards.OFPFW_NW_SRC_SHIFT |
||
43 | if mask > 32: |
||
44 | mask = 32 |
||
45 | if mask != 32 and 'nw_src' not in args: |
||
46 | return False |
||
47 | mask = (0xffffffff << mask) & 0xffffffff |
||
48 | ip_int = int(ipaddress.IPv4Address(args.get('nw_src'))) |
||
49 | if ip_int & mask != flow_ip_int & mask: |
||
50 | return False |
||
51 | 1 | flow_ip_int = int(ipaddress.IPv4Address(match_fields.get('nw_dst', 0))) |
|
52 | 1 | if flow_ip_int != 0: |
|
53 | mask = (wildcards |
||
54 | & FlowWildCards.OFPFW_NW_DST_MASK) >> \ |
||
55 | FlowWildCards.OFPFW_NW_DST_SHIFT |
||
56 | if mask > 32: |
||
57 | mask = 32 |
||
58 | if mask != 32 and 'nw_dst' not in args: |
||
59 | return False |
||
60 | mask = (0xffffffff << mask) & 0xffffffff |
||
61 | ip_int = int(ipaddress.IPv4Address(args.get('nw_dst'))) |
||
62 | if ip_int & mask != flow_ip_int & mask: |
||
63 | return False |
||
64 | 1 | if not wildcards & FlowWildCards.OFPFW_NW_TOS: |
|
65 | if ('nw_tos', 'nw_proto', 'tp_src', 'tp_dst') not in args: |
||
66 | return True |
||
67 | if match_fields.get('nw_tos') != int(args.get('nw_tos')): |
||
68 | return False |
||
69 | 1 | if not wildcards & FlowWildCards.OFPFW_NW_PROTO: |
|
70 | if match_fields.get('nw_proto') != int(args.get('nw_proto')): |
||
71 | return False |
||
72 | 1 | if not wildcards & FlowWildCards.OFPFW_TP_SRC: |
|
73 | if match_fields.get('tp_src') != int(args.get('tp_src')): |
||
74 | return False |
||
75 | 1 | if not wildcards & FlowWildCards.OFPFW_TP_DST: |
|
76 | if match_fields.get('tp_dst') != int(args.get('tp_dst')): |
||
77 | return False |
||
78 | 1 | return True |
|
79 | |||
80 | |||
81 | # pylint: disable=too-many-return-statements, too-many-statements, R0912 |
||
82 | 1 | def match10_no_strict(flow_dict, args): |
|
83 | """Match a packet against this flow (OF1.0).""" |
||
84 | 1 | args = _get_match_fields(args) |
|
85 | 1 | match_fields = _get_match_fields(flow_dict) |
|
86 | 1 | wildcards = match_fields.get('wildcards', 0) |
|
87 | 1 | if not wildcards & FlowWildCards.OFPFW_IN_PORT: |
|
88 | if match_fields.get('in_port') != args.get('in_port'): |
||
89 | return False |
||
90 | 1 | if not wildcards & FlowWildCards.OFPFW_DL_VLAN_PCP: |
|
91 | if match_fields.get('dl_vlan_pcp') != args.get('dl_vlan_pcp'): |
||
92 | return False |
||
93 | 1 | if not wildcards & FlowWildCards.OFPFW_DL_VLAN: |
|
94 | if match_fields.get('dl_vlan') != args.get('dl_vlan'): |
||
95 | return False |
||
96 | 1 | if not wildcards & FlowWildCards.OFPFW_DL_SRC: |
|
97 | if match_fields.get('dl_src') != args.get('dl_src'): |
||
98 | return False |
||
99 | 1 | if not wildcards & FlowWildCards.OFPFW_DL_DST: |
|
100 | if match_fields.get('dl_dst') != args.get('dl_dst'): |
||
101 | return False |
||
102 | 1 | if not wildcards & FlowWildCards.OFPFW_DL_TYPE: |
|
103 | if match_fields.get('dl_type') != args.get('dl_type'): |
||
104 | return False |
||
105 | 1 | if not _match_ipv4_10(match_fields, args, wildcards): |
|
106 | return False |
||
107 | 1 | return flow_dict |
|
108 | |||
109 | |||
110 | 1 | def match13_no_strict(flow_to_install, stored_flow_dict): |
|
111 | """Match a packet againts the stored flow (OF 1.3). |
||
112 | |||
113 | Return the flow if any fields match, otherwise, return False. |
||
114 | """ |
||
115 | 1 | if flow_to_install.get('cookie_mask') and 'cookie' in stored_flow_dict: |
|
116 | 1 | cookie = flow_to_install['cookie'] & flow_to_install['cookie_mask'] |
|
117 | 1 | stored_cookie = (stored_flow_dict['cookie'] & |
|
118 | flow_to_install['cookie_mask']) |
||
119 | 1 | if cookie == stored_cookie: |
|
120 | 1 | return stored_flow_dict |
|
121 | 1 | return False |
|
122 | 1 | if 'match' not in flow_to_install: |
|
123 | return False |
||
124 | |||
125 | 1 | for key, value in flow_to_install.get('match').items(): |
|
126 | 1 | if 'match' not in stored_flow_dict: |
|
127 | return False |
||
128 | 1 | if key not in ('ipv4_src', 'ipv4_dst', 'ipv6_src', 'ipv6_dst'): |
|
129 | if value == stored_flow_dict['match'].get(key): |
||
130 | return stored_flow_dict |
||
131 | else: |
||
132 | 1 | field = stored_flow_dict['match'].get(key) |
|
133 | 1 | if not field: |
|
134 | 1 | return False |
|
135 | 1 | masked_ip_addr = ipaddress.ip_network(value, False) |
|
136 | 1 | field_mask = field + "/" + str(masked_ip_addr.netmask) |
|
137 | 1 | masked_stored_ip = ipaddress.ip_network(field_mask, False) |
|
138 | 1 | if masked_ip_addr == masked_stored_ip: |
|
139 | 1 | return stored_flow_dict |
|
140 | return False |
||
141 |