blob: 54d77e976a3f6dc2514c5e2a9dc15c9ce5b5b9be [file] [log] [blame]
Shad Ansari2dda4f32018-05-17 07:16:07 +00001#
2# Copyright 2018 the original author or authors.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -040016import copy
17from twisted.internet import reactor
Nicolas Palpacuer3d0878d2018-08-17 11:29:42 -040018import grpc
Shad Ansari2dda4f32018-05-17 07:16:07 +000019
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -040020from voltha.protos.openflow_13_pb2 import OFPXMC_OPENFLOW_BASIC, \
21 ofp_flow_stats, ofp_match, OFPMT_OXM, Flows, FlowGroups, OFPXMT_OFB_IN_PORT
Shad Ansari2dda4f32018-05-17 07:16:07 +000022import voltha.core.flow_decomposer as fd
23import openolt_platform as platform
24from voltha.adapters.openolt.protos import openolt_pb2
Nicolas Palpacuer61815162018-06-20 18:12:04 -040025from voltha.registry import registry
Shad Ansari2dda4f32018-05-17 07:16:07 +000026
Shad Ansarif9d2d102018-06-13 02:15:26 +000027HSIA_FLOW_INDEX = 0 # FIXME
28DHCP_FLOW_INDEX = 1 # FIXME
Shad Ansari47e0c392018-07-17 23:55:07 +000029DHCP_DOWNLINK_FLOW_INDEX = 6 # FIXME
Shad Ansarif9d2d102018-06-13 02:15:26 +000030EAPOL_FLOW_INDEX = 2 # FIXME
31EAPOL_DOWNLINK_FLOW_INDEX = 3 # FIXME
Nicolas Palpacuer61815162018-06-20 18:12:04 -040032EAPOL_DOWNLINK_SECONDARY_FLOW_INDEX = 4 # FIXME
33EAPOL_UPLINK_SECONDARY_FLOW_INDEX = 5 # FIXME
34
35
36EAP_ETH_TYPE = 0x888e
Shad Ansari2dda4f32018-05-17 07:16:07 +000037
38# FIXME - see also BRDCM_DEFAULT_VLAN in broadcom_onu.py
39DEFAULT_MGMT_VLAN = 4091
40
Shad Ansarif9d2d102018-06-13 02:15:26 +000041
Shad Ansari2dda4f32018-05-17 07:16:07 +000042class OpenOltFlowMgr(object):
43
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -040044 def __init__(self, log, stub, device_id, logical_device_id):
Shad Ansari2dda4f32018-05-17 07:16:07 +000045 self.log = log
46 self.stub = stub
Nicolas Palpacuer61815162018-06-20 18:12:04 -040047 self.device_id = device_id
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -040048 self.logical_device_id = logical_device_id
49 self.logical_flows_proxy = registry('core').get_proxy(
50 '/logical_devices/{}/flows'.format(self.logical_device_id))
51 self.flows_proxy = registry('core').get_proxy(
Nicolas Palpacuer61815162018-06-20 18:12:04 -040052 '/devices/{}/flows'.format(self.device_id))
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -040053 self.root_proxy = registry('core').get_proxy('/')
54 self.fd_object = fd.FlowDecomposer()
Shad Ansari2dda4f32018-05-17 07:16:07 +000055
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -040056 def add_flow(self, flow):
57 self.log.debug('add flow', flow=flow)
Shad Ansari2dda4f32018-05-17 07:16:07 +000058 classifier_info = dict()
59 action_info = dict()
60
Shad Ansari2dda4f32018-05-17 07:16:07 +000061
62 for field in fd.get_ofb_fields(flow):
63 if field.type == fd.ETH_TYPE:
64 classifier_info['eth_type'] = field.eth_type
Shad Ansarie048aaa2018-05-18 18:27:21 +000065 self.log.debug('field-type-eth-type',
Shad Ansarif9d2d102018-06-13 02:15:26 +000066 eth_type=classifier_info['eth_type'])
Shad Ansari2dda4f32018-05-17 07:16:07 +000067 elif field.type == fd.IP_PROTO:
68 classifier_info['ip_proto'] = field.ip_proto
Shad Ansarie048aaa2018-05-18 18:27:21 +000069 self.log.debug('field-type-ip-proto',
Shad Ansarif9d2d102018-06-13 02:15:26 +000070 ip_proto=classifier_info['ip_proto'])
Shad Ansari2dda4f32018-05-17 07:16:07 +000071 elif field.type == fd.IN_PORT:
72 classifier_info['in_port'] = field.port
Shad Ansarie048aaa2018-05-18 18:27:21 +000073 self.log.debug('field-type-in-port',
Shad Ansarif9d2d102018-06-13 02:15:26 +000074 in_port=classifier_info['in_port'])
Shad Ansari2dda4f32018-05-17 07:16:07 +000075 elif field.type == fd.VLAN_VID:
76 classifier_info['vlan_vid'] = field.vlan_vid & 0xfff
Shad Ansarie048aaa2018-05-18 18:27:21 +000077 self.log.debug('field-type-vlan-vid',
Shad Ansarif9d2d102018-06-13 02:15:26 +000078 vlan=classifier_info['vlan_vid'])
Shad Ansari2dda4f32018-05-17 07:16:07 +000079 elif field.type == fd.VLAN_PCP:
80 classifier_info['vlan_pcp'] = field.vlan_pcp
Shad Ansarie048aaa2018-05-18 18:27:21 +000081 self.log.debug('field-type-vlan-pcp',
Shad Ansarif9d2d102018-06-13 02:15:26 +000082 pcp=classifier_info['vlan_pcp'])
Shad Ansari2dda4f32018-05-17 07:16:07 +000083 elif field.type == fd.UDP_DST:
84 classifier_info['udp_dst'] = field.udp_dst
Shad Ansarie048aaa2018-05-18 18:27:21 +000085 self.log.debug('field-type-udp-dst',
Shad Ansarif9d2d102018-06-13 02:15:26 +000086 udp_dst=classifier_info['udp_dst'])
Shad Ansari2dda4f32018-05-17 07:16:07 +000087 elif field.type == fd.UDP_SRC:
88 classifier_info['udp_src'] = field.udp_src
Shad Ansarie048aaa2018-05-18 18:27:21 +000089 self.log.debug('field-type-udp-src',
Shad Ansarif9d2d102018-06-13 02:15:26 +000090 udp_src=classifier_info['udp_src'])
Shad Ansari2dda4f32018-05-17 07:16:07 +000091 elif field.type == fd.IPV4_DST:
92 classifier_info['ipv4_dst'] = field.ipv4_dst
Shad Ansarie048aaa2018-05-18 18:27:21 +000093 self.log.debug('field-type-ipv4-dst',
Shad Ansarif9d2d102018-06-13 02:15:26 +000094 ipv4_dst=classifier_info['ipv4_dst'])
Shad Ansari2dda4f32018-05-17 07:16:07 +000095 elif field.type == fd.IPV4_SRC:
96 classifier_info['ipv4_src'] = field.ipv4_src
Shad Ansarie048aaa2018-05-18 18:27:21 +000097 self.log.debug('field-type-ipv4-src',
Shad Ansarif9d2d102018-06-13 02:15:26 +000098 ipv4_dst=classifier_info['ipv4_src'])
Shad Ansari2dda4f32018-05-17 07:16:07 +000099 elif field.type == fd.METADATA:
100 classifier_info['metadata'] = field.table_metadata
Shad Ansarie048aaa2018-05-18 18:27:21 +0000101 self.log.debug('field-type-metadata',
Shad Ansarif9d2d102018-06-13 02:15:26 +0000102 metadata=classifier_info['metadata'])
Shad Ansari2dda4f32018-05-17 07:16:07 +0000103 else:
104 raise NotImplementedError('field.type={}'.format(
105 field.type))
106
107 for action in fd.get_actions(flow):
108 if action.type == fd.OUTPUT:
109 action_info['output'] = action.output.port
Shad Ansarie048aaa2018-05-18 18:27:21 +0000110 self.log.debug('action-type-output',
Shad Ansarif9d2d102018-06-13 02:15:26 +0000111 output=action_info['output'],
112 in_port=classifier_info['in_port'])
Shad Ansari2dda4f32018-05-17 07:16:07 +0000113 elif action.type == fd.POP_VLAN:
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400114 if fd.get_goto_table_id(flow) is None:
115 self.log.debug('being taken care of by ONU', flow=flow)
116 return
Shad Ansari2dda4f32018-05-17 07:16:07 +0000117 action_info['pop_vlan'] = True
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400118 self.log.debug('action-type-pop-vlan', in_port=classifier_info['in_port'])
Shad Ansari2dda4f32018-05-17 07:16:07 +0000119 elif action.type == fd.PUSH_VLAN:
120 action_info['push_vlan'] = True
121 action_info['tpid'] = action.push.ethertype
Shad Ansarie048aaa2018-05-18 18:27:21 +0000122 self.log.debug('action-type-push-vlan',
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400123 push_tpid=action_info['tpid'], in_port=classifier_info['in_port'])
Shad Ansari2dda4f32018-05-17 07:16:07 +0000124 if action.push.ethertype != 0x8100:
125 self.log.error('unhandled-tpid',
Shad Ansarif9d2d102018-06-13 02:15:26 +0000126 ethertype=action.push.ethertype)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000127 elif action.type == fd.SET_FIELD:
128 # action_info['action_type'] = 'set_field'
129 _field = action.set_field.field.ofb_field
130 assert (action.set_field.field.oxm_class ==
131 OFPXMC_OPENFLOW_BASIC)
Shad Ansarie048aaa2018-05-18 18:27:21 +0000132 self.log.debug('action-type-set-field',
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400133 field=_field, in_port=classifier_info['in_port'])
Shad Ansari2dda4f32018-05-17 07:16:07 +0000134 if _field.type == fd.VLAN_VID:
Shad Ansarie048aaa2018-05-18 18:27:21 +0000135 self.log.debug('set-field-type-vlan-vid',
Shad Ansarif9d2d102018-06-13 02:15:26 +0000136 vlan_vid=_field.vlan_vid & 0xfff)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000137 action_info['vlan_vid'] = (_field.vlan_vid & 0xfff)
138 else:
139 self.log.error('unsupported-action-set-field-type',
Shad Ansarif9d2d102018-06-13 02:15:26 +0000140 field_type=_field.type)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000141 else:
142 self.log.error('unsupported-action-type',
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400143 action_type=action.type, in_port=classifier_info['in_port'])
Shad Ansari2dda4f32018-05-17 07:16:07 +0000144
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400145 if fd.get_goto_table_id(flow) is not None and not 'pop_vlan' in \
146 action_info:
147 self.log.debug('being taken care of by ONU', flow=flow)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000148
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400149 if not 'output' in action_info and 'metadata' in classifier_info:
150 #find flow in the next table
151 next_flow = self.find_next_flow(flow)
152 if next_flow is None:
153 return
154 action_info['output'] = fd.get_out_port(next_flow)
155 for field in fd.get_ofb_fields(next_flow):
156 if field.type == fd.VLAN_VID:
157 classifier_info['metadata'] = field.vlan_vid & 0xfff
158
159
160 (intf_id, onu_id) = platform.extract_access_from_flow(
161 classifier_info['in_port'], action_info['output'])
162
163
164 self.divide_and_add_flow(intf_id, onu_id, classifier_info,
165 action_info, flow)
166
167 def remove_flow(self, flow):
168 self.log.debug('trying to remove flows from logical flow :',
169 logical_flow=flow)
170 device_flows_to_remove = []
171 device_flows = self.flows_proxy.get('/').items
172 for f in device_flows:
173 if f.cookie == flow.id:
174 device_flows_to_remove.append(f)
175
176
177 for f in device_flows_to_remove:
178 (id, direction) = self.decode_stored_id(f.id)
179 flow_to_remove = openolt_pb2.Flow(flow_id=id, flow_type=direction)
Nicolas Palpacuer3d0878d2018-08-17 11:29:42 -0400180 try:
181 self.stub.FlowRemove(flow_to_remove)
182 except grpc.RpcError as grpc_e:
183 if grpc_e.code() == grpc.StatusCode.NOT_FOUND:
184 self.log.debug('This flow does not exist on the switch, '
185 'normal after an OLT reboot', flow=flow_to_remove)
186 else:
187 raise grpc_e
188
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400189 self.log.debug('flow removed from device', flow=f,
190 flow_key=flow_to_remove)
191
192 if len(device_flows_to_remove) > 0:
193 new_flows = []
194 flows_ids_to_remove = [f.id for f in device_flows_to_remove]
195 for f in device_flows:
196 if f.id not in flows_ids_to_remove:
197 new_flows.append(f)
198
199 self.flows_proxy.update('/', Flows(items=new_flows))
200 self.log.debug('flows removed from the data store',
201 flow_ids_removed=flows_ids_to_remove,
202 number_of_flows_removed=(len(device_flows) - len(
203 new_flows)), expected_flows_removed=len(
204 device_flows_to_remove))
205 else:
206 self.log.debug('no device flow to remove for this flow (normal '
207 'for multi table flows)', flow=flow)
208
209
210 def divide_and_add_flow(self, intf_id, onu_id, classifier,
211 action, flow):
212
213 self.log.debug('sorting flow', intf_id=intf_id, onu_id=onu_id,
214 classifier=classifier, action=action)
215
Shad Ansari2dda4f32018-05-17 07:16:07 +0000216 if 'ip_proto' in classifier:
217 if classifier['ip_proto'] == 17:
218 self.log.debug('dhcp flow add')
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400219 self.add_dhcp_trap(intf_id, onu_id, classifier,
220 action, flow)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000221 elif classifier['ip_proto'] == 2:
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400222 self.log.warn('igmp flow add ignored, not implemented yet')
Shad Ansari2dda4f32018-05-17 07:16:07 +0000223 else:
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400224 self.log.warn("Invalid-Classifier-to-handle",
Shad Ansarif9d2d102018-06-13 02:15:26 +0000225 classifier=classifier,
226 action=action)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000227 elif 'eth_type' in classifier:
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400228 if classifier['eth_type'] == EAP_ETH_TYPE:
Shad Ansarie048aaa2018-05-18 18:27:21 +0000229 self.log.debug('eapol flow add')
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400230 self.add_eapol_flow(intf_id, onu_id, flow)
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400231
Shad Ansari2dda4f32018-05-17 07:16:07 +0000232 elif 'push_vlan' in action:
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400233 self.add_upstream_data_flow(intf_id, onu_id, classifier, action,
234 flow)
235 elif 'pop_vlan' in action:
236 self.add_downstream_data_flow(intf_id, onu_id, classifier,
237 action, flow)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000238 else:
Shad Ansarif9d2d102018-06-13 02:15:26 +0000239 self.log.debug('Invalid-flow-type-to-handle',
240 classifier=classifier,
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400241 action=action, flow=flow)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000242
Shad Ansari2dda4f32018-05-17 07:16:07 +0000243
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400244 def add_upstream_data_flow(self, intf_id, onu_id, uplink_classifier,
245 uplink_action, logical_flow):
246
Shad Ansari2dda4f32018-05-17 07:16:07 +0000247
248 uplink_classifier['pkt_tag_type'] = 'single_tag'
249
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400250 self.add_hsia_flow(intf_id, onu_id, uplink_classifier,
251 uplink_action, 'upstream', HSIA_FLOW_INDEX,
252 logical_flow)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000253
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400254 # Secondary EAP on the subscriber vlan
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400255 (eap_active, eap_logical_flow) = self.is_eap_enabled(intf_id, onu_id)
Nicolas Palpacuer2e0fa582018-07-16 16:04:12 -0400256 if eap_active:
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400257 self.add_eapol_flow(intf_id, onu_id, eap_logical_flow,
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400258 uplink_eapol_id=EAPOL_UPLINK_SECONDARY_FLOW_INDEX,
259 downlink_eapol_id=EAPOL_DOWNLINK_SECONDARY_FLOW_INDEX,
260 vlan_id=uplink_classifier['vlan_vid'])
261
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400262
263 def add_downstream_data_flow(self, intf_id, onu_id, downlink_classifier,
264 downlink_action, flow):
265 downlink_classifier['pkt_tag_type'] = 'double_tag'
266 # Needed ???? It should be already there
267 downlink_action['pop_vlan'] = True
268 downlink_action['vlan_vid'] = downlink_classifier['vlan_vid']
269
270 self.add_hsia_flow(intf_id, onu_id, downlink_classifier,
271 downlink_action, 'downstream', HSIA_FLOW_INDEX,
272 flow)
273
274 # To-Do right now only one GEM port is supported, so below method
275 # will take care of handling all the p bits.
276 # We need to revisit when mulitple gem port per p bits is needed.
277 def add_hsia_flow(self, intf_id, onu_id, classifier, action,
278 direction, hsia_id, logical_flow):
Shad Ansari2dda4f32018-05-17 07:16:07 +0000279
Nicolas Palpacuere9bf83c2018-08-16 14:53:14 -0400280 gemport_id = platform.mk_gemport_id(intf_id, onu_id)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000281 flow_id = platform.mk_flow_id(intf_id, onu_id, hsia_id)
282
Shad Ansari2dda4f32018-05-17 07:16:07 +0000283 flow = openolt_pb2.Flow(
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400284 onu_id=onu_id, flow_id=flow_id, flow_type=direction,
Shad Ansari2dda4f32018-05-17 07:16:07 +0000285 access_intf_id=intf_id, gemport_id=gemport_id,
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400286 priority=logical_flow.priority,
287 classifier=self.mk_classifier(classifier),
288 action=self.mk_action(action))
Shad Ansari2dda4f32018-05-17 07:16:07 +0000289
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400290 self.add_flow_to_device(flow, logical_flow)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000291
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400292 def add_dhcp_trap(self, intf_id, onu_id, classifier, action, logical_flow):
Shad Ansari2dda4f32018-05-17 07:16:07 +0000293
Shad Ansari47e0c392018-07-17 23:55:07 +0000294 self.log.debug('add dhcp upstream trap', classifier=classifier,
295 action=action)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000296
297 action.clear()
298 action['trap_to_host'] = True
299 classifier['pkt_tag_type'] = 'single_tag'
300 classifier.pop('vlan_vid', None)
301
Nicolas Palpacuere9bf83c2018-08-16 14:53:14 -0400302 gemport_id = platform.mk_gemport_id(intf_id, onu_id)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000303 flow_id = platform.mk_flow_id(intf_id, onu_id, DHCP_FLOW_INDEX)
304
305 upstream_flow = openolt_pb2.Flow(
Shad Ansarif9d2d102018-06-13 02:15:26 +0000306 onu_id=onu_id, flow_id=flow_id, flow_type="upstream",
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400307 access_intf_id=intf_id, gemport_id=gemport_id,
308 priority=logical_flow.priority,
309 classifier=self.mk_classifier(classifier),
Shad Ansarif9d2d102018-06-13 02:15:26 +0000310 action=self.mk_action(action))
Shad Ansari2dda4f32018-05-17 07:16:07 +0000311
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400312 self.add_flow_to_device(upstream_flow, logical_flow)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000313
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400314
Shad Ansari47e0c392018-07-17 23:55:07 +0000315 # FIXME - ONOS should send explicit upstream and downstream
316 # exact dhcp trap flow.
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400317
318 downstream_logical_flow = copy.deepcopy(logical_flow)
319 for oxm_field in downstream_logical_flow.match.oxm_fields:
320 if oxm_field.ofb_field.type == OFPXMT_OFB_IN_PORT:
321 oxm_field.ofb_field.port = 128
322
Shad Ansari47e0c392018-07-17 23:55:07 +0000323 classifier['udp_src'] = 67
324 classifier['udp_dst'] = 68
325 classifier['pkt_tag_type'] = 'double_tag'
326 action.pop('push_vlan', None)
327
328 flow_id = platform.mk_flow_id(intf_id, onu_id,
329 DHCP_DOWNLINK_FLOW_INDEX)
330
331 downstream_flow = openolt_pb2.Flow(
332 onu_id=onu_id, flow_id=flow_id, flow_type="downstream",
333 access_intf_id=intf_id, network_intf_id=0, gemport_id=gemport_id,
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400334 priority=logical_flow.priority, classifier=self.mk_classifier(
335 classifier),
Shad Ansari47e0c392018-07-17 23:55:07 +0000336 action=self.mk_action(action))
337
Shad Ansari47e0c392018-07-17 23:55:07 +0000338
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400339 self.add_flow_to_device(downstream_flow, downstream_logical_flow)
340
341 def add_eapol_flow(self, intf_id, onu_id, logical_flow,
Shad Ansarif9d2d102018-06-13 02:15:26 +0000342 uplink_eapol_id=EAPOL_FLOW_INDEX,
343 downlink_eapol_id=EAPOL_DOWNLINK_FLOW_INDEX,
344 vlan_id=DEFAULT_MGMT_VLAN):
Shad Ansari2dda4f32018-05-17 07:16:07 +0000345
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400346 downlink_classifier = {}
347 downlink_classifier['eth_type'] = EAP_ETH_TYPE
348 downlink_classifier['pkt_tag_type'] = 'single_tag'
349 downlink_classifier['vlan_vid'] = vlan_id
Shad Ansari2dda4f32018-05-17 07:16:07 +0000350
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400351 downlink_action = {}
352 downlink_action['push_vlan'] = True
353 downlink_action['vlan_vid'] = vlan_id
Shad Ansari2dda4f32018-05-17 07:16:07 +0000354
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400355 uplink_classifier = {}
356 uplink_classifier['eth_type'] = EAP_ETH_TYPE
Shad Ansari2dda4f32018-05-17 07:16:07 +0000357 uplink_classifier['pkt_tag_type'] = 'single_tag'
358 uplink_classifier['vlan_vid'] = vlan_id
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400359
360 uplink_action = {}
Shad Ansari2dda4f32018-05-17 07:16:07 +0000361 uplink_action['trap_to_host'] = True
362
Nicolas Palpacuere9bf83c2018-08-16 14:53:14 -0400363 gemport_id = platform.mk_gemport_id(intf_id, onu_id)
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400364
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400365 # Add Upstream EAPOL Flow.
366
367 uplink_flow_id = platform.mk_flow_id(intf_id, onu_id, uplink_eapol_id)
368
Shad Ansari2dda4f32018-05-17 07:16:07 +0000369 upstream_flow = openolt_pb2.Flow(
Shad Ansarif9d2d102018-06-13 02:15:26 +0000370 onu_id=onu_id, flow_id=uplink_flow_id, flow_type="upstream",
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400371 access_intf_id=intf_id, gemport_id=gemport_id,
372 priority=logical_flow.priority,
Shad Ansarif9d2d102018-06-13 02:15:26 +0000373 classifier=self.mk_classifier(uplink_classifier),
374 action=self.mk_action(uplink_action))
Shad Ansari2dda4f32018-05-17 07:16:07 +0000375
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400376 logical_flow = copy.deepcopy(logical_flow)
377 logical_flow.match.oxm_fields.extend(fd.mk_oxm_fields([fd.vlan_vid(
378 vlan_id | 0x1000)]))
379 logical_flow.match.type = OFPMT_OXM
380
381 self.add_flow_to_device(upstream_flow, logical_flow)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000382
383 # Add Downstream EAPOL Flow.
Shad Ansarif9d2d102018-06-13 02:15:26 +0000384 downlink_flow_id = platform.mk_flow_id(intf_id, onu_id,
385 downlink_eapol_id)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000386
387 downstream_flow = openolt_pb2.Flow(
Shad Ansarif9d2d102018-06-13 02:15:26 +0000388 onu_id=onu_id, flow_id=downlink_flow_id, flow_type="downstream",
389 access_intf_id=intf_id, gemport_id=gemport_id,
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400390 priority=logical_flow.priority,
Shad Ansarif9d2d102018-06-13 02:15:26 +0000391 classifier=self.mk_classifier(downlink_classifier),
392 action=self.mk_action(downlink_action))
Shad Ansari2dda4f32018-05-17 07:16:07 +0000393
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400394 downstream_logical_flow = ofp_flow_stats(id=logical_flow.id,
395 cookie=logical_flow.cookie, table_id=logical_flow.table_id,
396 priority=logical_flow.priority, flags=logical_flow.flags)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000397
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400398 downstream_logical_flow.match.oxm_fields.extend(fd.mk_oxm_fields([
399 fd.in_port(fd.get_out_port(logical_flow)),
400 fd.eth_type(EAP_ETH_TYPE), fd.vlan_vid(vlan_id | 0x1000)]))
401 downstream_logical_flow.match.type = OFPMT_OXM
402
403 downstream_logical_flow.instructions.extend(
404 fd.mk_instructions_from_actions([fd.output(
405 platform.mk_uni_port_num(intf_id, onu_id))]))
406
407 self.add_flow_to_device(downstream_flow, downstream_logical_flow)
408
409
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400410
Shad Ansari2dda4f32018-05-17 07:16:07 +0000411 def mk_classifier(self, classifier_info):
412
413 classifier = openolt_pb2.Classifier()
414
415 if 'eth_type' in classifier_info:
416 classifier.eth_type = classifier_info['eth_type']
417 if 'ip_proto' in classifier_info:
418 classifier.ip_proto = classifier_info['ip_proto']
419 if 'vlan_vid' in classifier_info:
420 classifier.o_vid = classifier_info['vlan_vid']
421 if 'metadata' in classifier_info:
422 classifier.i_vid = classifier_info['metadata']
423 if 'vlan_pcp' in classifier_info:
424 classifier.o_pbits = classifier_info['vlan_pcp']
425 if 'udp_src' in classifier_info:
426 classifier.src_port = classifier_info['udp_src']
427 if 'udp_dst' in classifier_info:
428 classifier.dst_port = classifier_info['udp_dst']
429 if 'ipv4_dst' in classifier_info:
430 classifier.dst_ip = classifier_info['ipv4_dst']
431 if 'ipv4_src' in classifier_info:
432 classifier.src_ip = classifier_info['ipv4_src']
433 if 'pkt_tag_type' in classifier_info:
434 if classifier_info['pkt_tag_type'] == 'single_tag':
435 classifier.pkt_tag_type = 'single_tag'
436 elif classifier_info['pkt_tag_type'] == 'double_tag':
437 classifier.pkt_tag_type = 'double_tag'
438 elif classifier_info['pkt_tag_type'] == 'untagged':
439 classifier.pkt_tag_type = 'untagged'
440 else:
441 classifier.pkt_tag_type = 'none'
442
443 return classifier
444
445 def mk_action(self, action_info):
446 action = openolt_pb2.Action()
447
Shad Ansarif9d2d102018-06-13 02:15:26 +0000448 if 'pop_vlan' in action_info:
449 action.o_vid = action_info['vlan_vid']
Shad Ansari2dda4f32018-05-17 07:16:07 +0000450 action.cmd.remove_outer_tag = True
Shad Ansarif9d2d102018-06-13 02:15:26 +0000451 elif 'push_vlan' in action_info:
452 action.o_vid = action_info['vlan_vid']
Shad Ansari2dda4f32018-05-17 07:16:07 +0000453 action.cmd.add_outer_tag = True
Shad Ansarif9d2d102018-06-13 02:15:26 +0000454 elif 'trap_to_host' in action_info:
Shad Ansari2dda4f32018-05-17 07:16:07 +0000455 action.cmd.trap_to_host = True
Shad Ansarif9d2d102018-06-13 02:15:26 +0000456 else:
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400457 self.log.info('Invalid-action-field', action_info=action_info)
Shad Ansarif9d2d102018-06-13 02:15:26 +0000458 return
Shad Ansari2dda4f32018-05-17 07:16:07 +0000459 return action
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400460
461 def is_eap_enabled(self, intf_id, onu_id):
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400462 flows = self.logical_flows_proxy.get('/').items
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400463
464 for flow in flows:
465 eap_flow = False
466 eap_intf_id = None
467 eap_onu_id = None
468 for field in fd.get_ofb_fields(flow):
469 if field.type == fd.ETH_TYPE:
470 if field.eth_type == EAP_ETH_TYPE:
471 eap_flow = True
472 if field.type == fd.IN_PORT:
Shad Ansari47e0c392018-07-17 23:55:07 +0000473 eap_intf_id = platform.intf_id_from_uni_port_num(
474 field.port)
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400475 eap_onu_id = platform.onu_id_from_port_num(field.port)
476
477 if eap_flow:
478 self.log.debug('eap flow detected', onu_id=onu_id,
479 intf_id=intf_id, eap_intf_id=eap_intf_id,
480 eap_onu_id=eap_onu_id)
481 if eap_flow and intf_id == eap_intf_id and onu_id == eap_onu_id:
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400482 return (True, flow)
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400483
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400484 return (False, None)
485
486 def add_flow_to_device(self, flow, logical_flow):
487 self.log.debug('pushing flow to device', flow=flow)
488 self.stub.FlowAdd(flow)
489 self.register_flow(logical_flow, flow)
490
491 def register_flow(self, logical_flow, device_flow):
492 self.log.debug('registering flow in device',
493 logical_flow=logical_flow, device_flow=device_flow)
494 stored_flow = copy.deepcopy(logical_flow)
495 stored_flow.id = self.generate_stored_id(device_flow.flow_id,
496 device_flow.flow_type)
497 self.log.debug('generated device flow id', id=stored_flow.id,
498 flow_id=device_flow.flow_id,
499 direction=device_flow.flow_type)
500 stored_flow.cookie = logical_flow.id
501 flows = self.flows_proxy.get('/')
502 flows.items.extend([stored_flow])
503 self.flows_proxy.update('/', flows)
504
505
506 def find_next_flow(self, flow):
507 table_id = fd.get_goto_table_id(flow)
508 metadata = 0
509 for field in fd.get_ofb_fields(flow):
510 if field.type == fd.METADATA:
511 metadata = field.table_metadata
512 if table_id is None:
513 return None
514 flows = self.logical_flows_proxy.get('/').items
515 next_flows = []
516 for f in flows:
517 if f.table_id == table_id:
518 #FIXME:
519 if fd.get_in_port(f) == fd.get_in_port(flow) and \
520 fd.get_out_port(f) == metadata:
521
522 next_flows.append(f)
523
524
525 if len(next_flows) == 0:
526 self.log.warning('no next flow found, it may be a timing issue',
527 flow=flow, number_of_flows=len(flows))
528 reactor.callLater(5, self.add_flow, flow)
529 return None
530
531 next_flows.sort(key=lambda f:f.priority, reverse=True)
532
533 return next_flows[0]
534
535 def update_children_flows(self, device_rules_map):
536
537 for device_id, (flows, groups) in device_rules_map.iteritems():
538 if device_id != self.device_id:
539 self.root_proxy.update('/devices/{}/flows'.format(device_id),
540 Flows(items=flows.values()))
541 self.root_proxy.update('/devices/{}/flow_groups'.format(
542 device_id), FlowGroups(items=groups.values()))
543
544 def generate_stored_id(self, flow_id, direction):
545 if direction == 'upstream':
546 self.log.debug('upstream flow, shifting id')
547 return 0x1 << 15 | flow_id
548 elif direction == 'downstream':
549 self.log.debug('downstream flow, not shifting id')
550 return flow_id
551 else:
552 self.log.warn('Unrecognized direction', direction=direction)
553 return flow_id
554
555 def decode_stored_id(self, id):
556 if id >> 15 == 0x1:
557 return (id & 0x7fff, 'upstream')
558 else:
559 return (id, 'downstream')