blob: 7e6c25820eac19df626fcdb8226316562015fe52 [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
Girish Gowdruab836e92018-10-25 01:17:57 -070019from google.protobuf.json_format import MessageToDict
Shad Ansari2dda4f32018-05-17 07:16:07 +000020
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -040021from voltha.protos.openflow_13_pb2 import OFPXMC_OPENFLOW_BASIC, \
Jonathan Hart5b435642018-08-20 08:50:05 -070022 ofp_flow_stats, OFPMT_OXM, Flows, FlowGroups, OFPXMT_OFB_IN_PORT, \
23 OFPXMT_OFB_VLAN_VID
Nicolas Palpacuer5780e152018-09-05 17:25:42 -040024from voltha.protos.device_pb2 import Port
Shad Ansari2dda4f32018-05-17 07:16:07 +000025import voltha.core.flow_decomposer as fd
Shad Ansari2dda4f32018-05-17 07:16:07 +000026from voltha.adapters.openolt.protos import openolt_pb2
Nicolas Palpacuer61815162018-06-20 18:12:04 -040027from voltha.registry import registry
Shad Ansari2dda4f32018-05-17 07:16:07 +000028
Girish Gowdruab836e92018-10-25 01:17:57 -070029from common.tech_profile.tech_profile import DEFAULT_TECH_PROFILE_TABLE_ID
30
31# Flow categories
32HSIA_FLOW = "HSIA_FLOW"
33DHCP_FLOW = "DHCP_FLOW"
34EAPOL_PRIMARY_FLOW = "EAPOL_PRIMARY_FLOW"
35EAPOL_SECONDARY_FLOW = "EAPOL_SECONDARY_FLOW"
36IGMP_FLOW = "IGMP_FLOW"
37LLDP_FLOW = "LLDP_FLOW"
Nicolas Palpacuer61815162018-06-20 18:12:04 -040038
39EAP_ETH_TYPE = 0x888e
Jonathan Hart5b435642018-08-20 08:50:05 -070040LLDP_ETH_TYPE = 0x88cc
Shad Ansari2dda4f32018-05-17 07:16:07 +000041
Girish Gowdruab836e92018-10-25 01:17:57 -070042IGMP_PROTO = 2
43
Shad Ansari2dda4f32018-05-17 07:16:07 +000044# FIXME - see also BRDCM_DEFAULT_VLAN in broadcom_onu.py
45DEFAULT_MGMT_VLAN = 4091
46
Nicolas Palpacuer2789f042018-09-17 09:10:29 -040047# Openolt Flow
Girish Gowdruab836e92018-10-25 01:17:57 -070048UPSTREAM = "upstream"
49DOWNSTREAM = "downstream"
50PACKET_TAG_TYPE = "pkt_tag_type"
51UNTAGGED = "untagged"
52SINGLE_TAG = "single_tag"
53DOUBLE_TAG = "double_tag"
Nicolas Palpacuer2789f042018-09-17 09:10:29 -040054
55# Classifier
56ETH_TYPE = 'eth_type'
57TPID = 'tpid'
58IP_PROTO = 'ip_proto'
59IN_PORT = 'in_port'
60VLAN_VID = 'vlan_vid'
61VLAN_PCP = 'vlan_pcp'
62UDP_DST = 'udp_dst'
63UDP_SRC = 'udp_src'
64IPV4_DST = 'ipv4_dst'
65IPV4_SRC = 'ipv4_src'
66METADATA = 'metadata'
67OUTPUT = 'output'
68# Action
69POP_VLAN = 'pop_vlan'
70PUSH_VLAN = 'push_vlan'
71TRAP_TO_HOST = 'trap_to_host'
72
Girish Gowdruab836e92018-10-25 01:17:57 -070073KV_STORE_TECH_PROFILE_PATH_PREFIX = 'voltha/technology_profiles'
Nicolas Palpacuer2789f042018-09-17 09:10:29 -040074
75
Shad Ansari2dda4f32018-05-17 07:16:07 +000076class OpenOltFlowMgr(object):
77
Girish Gowdruab836e92018-10-25 01:17:57 -070078 def __init__(self, adapter_agent, log, stub, device_id, logical_device_id,
Girish Gowdru1e77ea02018-09-24 09:10:35 -070079 platform, resource_mgr):
Girish Gowdruab836e92018-10-25 01:17:57 -070080 self.adapter_agent = adapter_agent
Shad Ansari2dda4f32018-05-17 07:16:07 +000081 self.log = log
82 self.stub = stub
Nicolas Palpacuer61815162018-06-20 18:12:04 -040083 self.device_id = device_id
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -040084 self.logical_device_id = logical_device_id
Shad Ansaricd20a6d2018-10-02 14:36:33 +000085 self.platform = platform
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -040086 self.logical_flows_proxy = registry('core').get_proxy(
87 '/logical_devices/{}/flows'.format(self.logical_device_id))
88 self.flows_proxy = registry('core').get_proxy(
Nicolas Palpacuer61815162018-06-20 18:12:04 -040089 '/devices/{}/flows'.format(self.device_id))
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -040090 self.root_proxy = registry('core').get_proxy('/')
Girish Gowdru1e77ea02018-09-24 09:10:35 -070091 self.resource_mgr = resource_mgr
Girish Gowdruab836e92018-10-25 01:17:57 -070092 self.tech_profile = dict()
93 self._populate_tech_profile_per_pon_port()
Shad Ansari2dda4f32018-05-17 07:16:07 +000094
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -040095 def add_flow(self, flow):
96 self.log.debug('add flow', flow=flow)
Shad Ansari2dda4f32018-05-17 07:16:07 +000097 classifier_info = dict()
98 action_info = dict()
99
Shad Ansari2dda4f32018-05-17 07:16:07 +0000100 for field in fd.get_ofb_fields(flow):
101 if field.type == fd.ETH_TYPE:
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400102 classifier_info[ETH_TYPE] = field.eth_type
Shad Ansarie048aaa2018-05-18 18:27:21 +0000103 self.log.debug('field-type-eth-type',
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400104 eth_type=classifier_info[ETH_TYPE])
Shad Ansari2dda4f32018-05-17 07:16:07 +0000105 elif field.type == fd.IP_PROTO:
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400106 classifier_info[IP_PROTO] = field.ip_proto
Shad Ansarie048aaa2018-05-18 18:27:21 +0000107 self.log.debug('field-type-ip-proto',
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400108 ip_proto=classifier_info[IP_PROTO])
Shad Ansari2dda4f32018-05-17 07:16:07 +0000109 elif field.type == fd.IN_PORT:
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400110 classifier_info[IN_PORT] = field.port
Shad Ansarie048aaa2018-05-18 18:27:21 +0000111 self.log.debug('field-type-in-port',
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400112 in_port=classifier_info[IN_PORT])
Shad Ansari2dda4f32018-05-17 07:16:07 +0000113 elif field.type == fd.VLAN_VID:
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400114 classifier_info[VLAN_VID] = field.vlan_vid & 0xfff
Shad Ansarie048aaa2018-05-18 18:27:21 +0000115 self.log.debug('field-type-vlan-vid',
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400116 vlan=classifier_info[VLAN_VID])
Shad Ansari2dda4f32018-05-17 07:16:07 +0000117 elif field.type == fd.VLAN_PCP:
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400118 classifier_info[VLAN_PCP] = field.vlan_pcp
Shad Ansarie048aaa2018-05-18 18:27:21 +0000119 self.log.debug('field-type-vlan-pcp',
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400120 pcp=classifier_info[VLAN_PCP])
Shad Ansari2dda4f32018-05-17 07:16:07 +0000121 elif field.type == fd.UDP_DST:
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400122 classifier_info[UDP_DST] = field.udp_dst
Shad Ansarie048aaa2018-05-18 18:27:21 +0000123 self.log.debug('field-type-udp-dst',
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400124 udp_dst=classifier_info[UDP_DST])
Shad Ansari2dda4f32018-05-17 07:16:07 +0000125 elif field.type == fd.UDP_SRC:
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400126 classifier_info[UDP_SRC] = field.udp_src
Shad Ansarie048aaa2018-05-18 18:27:21 +0000127 self.log.debug('field-type-udp-src',
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400128 udp_src=classifier_info[UDP_SRC])
Shad Ansari2dda4f32018-05-17 07:16:07 +0000129 elif field.type == fd.IPV4_DST:
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400130 classifier_info[IPV4_DST] = field.ipv4_dst
Shad Ansarie048aaa2018-05-18 18:27:21 +0000131 self.log.debug('field-type-ipv4-dst',
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400132 ipv4_dst=classifier_info[IPV4_DST])
Shad Ansari2dda4f32018-05-17 07:16:07 +0000133 elif field.type == fd.IPV4_SRC:
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400134 classifier_info[IPV4_SRC] = field.ipv4_src
Shad Ansarie048aaa2018-05-18 18:27:21 +0000135 self.log.debug('field-type-ipv4-src',
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400136 ipv4_dst=classifier_info[IPV4_SRC])
Shad Ansari2dda4f32018-05-17 07:16:07 +0000137 elif field.type == fd.METADATA:
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400138 classifier_info[METADATA] = field.table_metadata
Shad Ansarie048aaa2018-05-18 18:27:21 +0000139 self.log.debug('field-type-metadata',
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400140 metadata=classifier_info[METADATA])
Shad Ansari2dda4f32018-05-17 07:16:07 +0000141 else:
142 raise NotImplementedError('field.type={}'.format(
143 field.type))
144
145 for action in fd.get_actions(flow):
146 if action.type == fd.OUTPUT:
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400147 action_info[OUTPUT] = action.output.port
Shad Ansarie048aaa2018-05-18 18:27:21 +0000148 self.log.debug('action-type-output',
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400149 output=action_info[OUTPUT],
150 in_port=classifier_info[IN_PORT])
Shad Ansari2dda4f32018-05-17 07:16:07 +0000151 elif action.type == fd.POP_VLAN:
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400152 if fd.get_goto_table_id(flow) is None:
153 self.log.debug('being taken care of by ONU', flow=flow)
154 return
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400155 action_info[POP_VLAN] = True
Jonathan Hart5b435642018-08-20 08:50:05 -0700156 self.log.debug('action-type-pop-vlan',
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400157 in_port=classifier_info[IN_PORT])
Shad Ansari2dda4f32018-05-17 07:16:07 +0000158 elif action.type == fd.PUSH_VLAN:
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400159 action_info[PUSH_VLAN] = True
160 action_info[TPID] = action.push.ethertype
Shad Ansarie048aaa2018-05-18 18:27:21 +0000161 self.log.debug('action-type-push-vlan',
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400162 push_tpid=action_info[TPID], in_port=classifier_info[IN_PORT])
Shad Ansari2dda4f32018-05-17 07:16:07 +0000163 if action.push.ethertype != 0x8100:
164 self.log.error('unhandled-tpid',
Shad Ansarif9d2d102018-06-13 02:15:26 +0000165 ethertype=action.push.ethertype)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000166 elif action.type == fd.SET_FIELD:
167 # action_info['action_type'] = 'set_field'
168 _field = action.set_field.field.ofb_field
169 assert (action.set_field.field.oxm_class ==
170 OFPXMC_OPENFLOW_BASIC)
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400171 self.log.debug('action-type-set-field',
172 field=_field, in_port=classifier_info[IN_PORT])
Shad Ansari2dda4f32018-05-17 07:16:07 +0000173 if _field.type == fd.VLAN_VID:
Shad Ansarie048aaa2018-05-18 18:27:21 +0000174 self.log.debug('set-field-type-vlan-vid',
Shad Ansarif9d2d102018-06-13 02:15:26 +0000175 vlan_vid=_field.vlan_vid & 0xfff)
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400176 action_info[VLAN_VID] = (_field.vlan_vid & 0xfff)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000177 else:
178 self.log.error('unsupported-action-set-field-type',
Shad Ansarif9d2d102018-06-13 02:15:26 +0000179 field_type=_field.type)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000180 else:
181 self.log.error('unsupported-action-type',
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400182 action_type=action.type, in_port=classifier_info[IN_PORT])
Shad Ansari2dda4f32018-05-17 07:16:07 +0000183
Girish Gowdruab836e92018-10-25 01:17:57 -0700184 if fd.get_goto_table_id(flow) is not None and POP_VLAN not in action_info:
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400185 self.log.debug('being taken care of by ONU', flow=flow)
Nicolas Palpacuer856d3af2018-09-12 15:04:51 -0400186 return
Shad Ansari2dda4f32018-05-17 07:16:07 +0000187
Girish Gowdruab836e92018-10-25 01:17:57 -0700188 if OUTPUT not in action_info and METADATA in classifier_info:
189 # find flow in the next table
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400190 next_flow = self.find_next_flow(flow)
191 if next_flow is None:
192 return
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400193 action_info[OUTPUT] = fd.get_out_port(next_flow)
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400194 for field in fd.get_ofb_fields(next_flow):
195 if field.type == fd.VLAN_VID:
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400196 classifier_info[METADATA] = field.vlan_vid & 0xfff
197
Shad Ansaricd20a6d2018-10-02 14:36:33 +0000198 (intf_id, onu_id) = self.platform.extract_access_from_flow(
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400199 classifier_info[IN_PORT], action_info[OUTPUT])
200
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400201 self.divide_and_add_flow(intf_id, onu_id, classifier_info,
202 action_info, flow)
203
Girish Gowdruab836e92018-10-25 01:17:57 -0700204 def _is_uni_port(self, port_no):
205 try:
206 port = self.adapter_agent.get_logical_port(self.logical_device_id,
207 'uni-{}'.format(port_no))
208 if port is not None:
209 return (not port.root_port), port.device_id
210 else:
211 return False, None
212 except Exception as e:
213 self.log.error("error-retrieving-port", e=e)
214 return False, None
215
216 def _clear_flow_id_from_rm(self, flow, flow_id, flow_direction):
217 uni_port_no = None
218 flow_category = HSIA_FLOW # default
219 child_device_id = None
220 if flow_direction == UPSTREAM:
221 for field in fd.get_ofb_fields(flow):
222 if field.type == fd.IN_PORT:
223 is_uni, child_device_id = self._is_uni_port(field.port)
224 if is_uni:
225 uni_port_no = field.port
226 elif field.type == fd.IP_PROTO:
227 if field.ip_proto == IGMP_PROTO:
228 flow_category = IGMP_FLOW
229 elif field.type == fd.ETH_TYPE:
230 if field.eth_type == EAP_ETH_TYPE:
231 flow_category = EAPOL_PRIMARY_FLOW
232 elif field.eth_type == LLDP_ETH_TYPE:
233 flow_category = LLDP_FLOW
234
235 elif flow_direction == DOWNSTREAM:
236 for field in fd.get_ofb_fields(flow):
237 if field.type == fd.METADATA:
238 uni_port = field.table_metadata & 0xFFFFFFFF
239 is_uni, child_device_id = self._is_uni_port(uni_port)
240 if is_uni:
241 uni_port_no = field.port
242
243 if uni_port_no is None:
244 for action in fd.get_actions(flow):
245 if action.type == fd.OUTPUT:
246 is_uni, child_device_id = \
247 self._is_uni_port(action.output.port)
248 if is_uni:
249 uni_port_no = action.output.port
250
251 if flow_category and child_device_id:
252 child_device = self.adapter_agent.get_device(child_device_id)
253 pon_intf = child_device.proxy_address.channel_id
254 onu_id = child_device.proxy_address.onu_id
255 flows = self.resource_mgr.get_flow_id_info(pon_intf, onu_id, flow_id)
256 assert (isinstance(flows, list))
257 self.log.debug("retrieved-flows", flows=flows)
258 for idx in range(len(flows)):
259 if flow_direction == flows[idx]['flow_type']:
260 flows.pop(idx)
261 self.update_flow_info_to_kv_store(pon_intf, onu_id,
262 flow_id, flows)
263 if len(flows) > 0:
264 # There are still flows referencing the same flow_id.
265 # So the flow should not be freed yet.
266 # For ex: Case of HSIA where same flow is shared
267 # between DS and US.
268 return
269
270 self.resource_mgr.free_flow_id(pon_intf, onu_id, flow_id)
271 else:
272 self.log.error("invalid-info", uni_port_no=uni_port_no,
273 flow_category=flow_category,
274 child_device_id=child_device_id)
275
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400276 def remove_flow(self, flow):
277 self.log.debug('trying to remove flows from logical flow :',
Jonathan Hart5b435642018-08-20 08:50:05 -0700278 logical_flow=flow)
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400279 device_flows_to_remove = []
280 device_flows = self.flows_proxy.get('/').items
281 for f in device_flows:
282 if f.cookie == flow.id:
283 device_flows_to_remove.append(f)
284
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400285 for f in device_flows_to_remove:
286 (id, direction) = self.decode_stored_id(f.id)
287 flow_to_remove = openolt_pb2.Flow(flow_id=id, flow_type=direction)
Nicolas Palpacuer3d0878d2018-08-17 11:29:42 -0400288 try:
289 self.stub.FlowRemove(flow_to_remove)
290 except grpc.RpcError as grpc_e:
291 if grpc_e.code() == grpc.StatusCode.NOT_FOUND:
292 self.log.debug('This flow does not exist on the switch, '
Jonathan Hart5b435642018-08-20 08:50:05 -0700293 'normal after an OLT reboot',
294 flow=flow_to_remove)
Nicolas Palpacuer3d0878d2018-08-17 11:29:42 -0400295 else:
296 raise grpc_e
297
Girish Gowdruab836e92018-10-25 01:17:57 -0700298 # once we have successfully deleted the flow on the device
299 # release the flow_id on resource pool and also clear any
300 # data associated with the flow_id on KV store.
301 self._clear_flow_id_from_rm(f, id, direction)
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400302 self.log.debug('flow removed from device', flow=f,
303 flow_key=flow_to_remove)
304
305 if len(device_flows_to_remove) > 0:
306 new_flows = []
307 flows_ids_to_remove = [f.id for f in device_flows_to_remove]
308 for f in device_flows:
309 if f.id not in flows_ids_to_remove:
310 new_flows.append(f)
311
312 self.flows_proxy.update('/', Flows(items=new_flows))
313 self.log.debug('flows removed from the data store',
314 flow_ids_removed=flows_ids_to_remove,
315 number_of_flows_removed=(len(device_flows) - len(
316 new_flows)), expected_flows_removed=len(
Girish Gowdruab836e92018-10-25 01:17:57 -0700317 device_flows_to_remove))
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400318 else:
319 self.log.debug('no device flow to remove for this flow (normal '
320 'for multi table flows)', flow=flow)
321
Girish Gowdruab836e92018-10-25 01:17:57 -0700322 def _get_ofp_port_name(self, intf_id, onu_id):
323 parent_port_no = self.platform.intf_id_to_port_no(intf_id, Port.PON_OLT)
324 child_device = self.adapter_agent.get_child_device(self.device_id,
325 parent_port_no=parent_port_no, onu_id=onu_id)
326 if child_device is None:
327 self.log.error("could-not-find-child-device",
328 parent_port_no=intf_id, onu_id=onu_id)
329 return None
330 # FIXME: Assumes single UNI for a ONU device which is visible at ONOS
331 ports = self.adapter_agent.get_ports(child_device.id, Port.ETHERNET_UNI)
332 logical_port = self.adapter_agent.get_logical_port(
333 self.logical_device_id, ports[0].label)
334 ofp_port_name = logical_port.ofp_port.name
335 return ofp_port_name
336
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400337 def divide_and_add_flow(self, intf_id, onu_id, classifier,
338 action, flow):
339
340 self.log.debug('sorting flow', intf_id=intf_id, onu_id=onu_id,
341 classifier=classifier, action=action)
342
Girish Gowdruab836e92018-10-25 01:17:57 -0700343 alloc_id, gem_ports = self.create_tcont_gemport(intf_id, onu_id,
344 flow.table_id)
345 if alloc_id is None or gem_ports is None:
346 self.log.error("alloc-id-gem-ports-unavailable", alloc_id=alloc_id,
347 gem_ports=gem_ports)
348 return
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400349
Girish Gowdruab836e92018-10-25 01:17:57 -0700350 self.log.debug('Generated required alloc and gemport ids',
351 alloc_id=alloc_id, gemports=gem_ports)
352
353 # Flows can't be added specific to gemport unless p-bits are received.
354 # Hence adding flows for all gemports
355 for gemport_id in gem_ports:
356 if IP_PROTO in classifier:
357 if classifier[IP_PROTO] == 17:
358 self.log.debug('dhcp flow add')
359 self.add_dhcp_trap(intf_id, onu_id, classifier,
360 action, flow, alloc_id, gemport_id)
361 elif classifier[IP_PROTO] == 2:
362 self.log.warn('igmp flow add ignored, not implemented yet')
363 else:
364 self.log.warn("Invalid-Classifier-to-handle",
365 classifier=classifier,
366 action=action)
367 elif ETH_TYPE in classifier:
368 if classifier[ETH_TYPE] == EAP_ETH_TYPE:
369 self.log.debug('eapol flow add')
370 self.add_eapol_flow(intf_id, onu_id, flow, alloc_id,
371 gemport_id)
372 vlan_id = self.get_subscriber_vlan(fd.get_in_port(flow))
373 if vlan_id is not None:
374 self.add_eapol_flow(
375 intf_id, onu_id, flow, alloc_id, gemport_id,
376 eapol_flow_category=EAPOL_SECONDARY_FLOW,
377 vlan_id=vlan_id)
378 parent_port_no = self.platform.intf_id_to_port_no(intf_id, Port.PON_OLT)
379 onu_device = self.adapter_agent.get_child_device(self.device_id,
380 onu_id=onu_id,
381 parent_port_no=parent_port_no)
382 ofp_port_name = self._get_ofp_port_name(intf_id, onu_id)
383 if ofp_port_name is None:
384 self.log.error("port-name-not-found")
385 return
386
387 # FIXME Should get Table id form the flow, as of now hardcoded to
388 # DEFAULT_TECH_PROFILE_TABLE_ID (64)
389 tp_path = KV_STORE_TECH_PROFILE_PATH_PREFIX + '/' + \
390 self.tech_profile[intf_id]. \
391 get_tp_path(DEFAULT_TECH_PROFILE_TABLE_ID,
392 ofp_port_name)
393
394 self.log.debug('Load-tech-profile-request-to-brcm-handler',
395 tp_path=tp_path)
396 msg = {'proxy_address': onu_device.proxy_address,
397 'event': 'download_tech_profile', 'event_data': tp_path}
398
399 # Send the event message to the ONU adapter
400 self.adapter_agent.publish_inter_adapter_message(onu_device.id,
401 msg)
402
403 if classifier[ETH_TYPE] == LLDP_ETH_TYPE:
404 self.log.debug('lldp flow add')
405 self.add_lldp_flow(flow)
406
407 elif PUSH_VLAN in action:
408 self.add_upstream_data_flow(intf_id, onu_id, classifier,
409 action, flow, alloc_id, gemport_id)
410 elif POP_VLAN in action:
411 self.add_downstream_data_flow(intf_id, onu_id, classifier,
412 action, flow, alloc_id, gemport_id)
413 else:
414 self.log.debug('Invalid-flow-type-to-handle',
415 classifier=classifier,
416 action=action, flow=flow)
417
418 def create_tcont_gemport(self, intf_id, onu_id, table_id):
419 alloc_id, gem_port_ids = None, None
420 try:
421 ofp_port_name = self._get_ofp_port_name(intf_id, onu_id)
422 if ofp_port_name is None:
423 self.log.error("port-name-not-found")
424 return alloc_id, gem_port_ids
425 # FIXME: If table id is <= 63 using 64 as table id
426 if table_id < DEFAULT_TECH_PROFILE_TABLE_ID:
427 table_id = DEFAULT_TECH_PROFILE_TABLE_ID
428
429 # Check tech profile instance already exists for derived port name
430 tech_profile_instance = self.tech_profile[intf_id]. \
431 get_tech_profile_instance(table_id, ofp_port_name)
432 self.log.debug('Get-tech-profile-instance-status', tech_profile_instance=tech_profile_instance)
433
434 if tech_profile_instance is None:
435 # create tech profile instance
436 tech_profile_instance = self.tech_profile[intf_id]. \
437 create_tech_profile_instance(table_id, ofp_port_name,
438 intf_id)
439 if tech_profile_instance is not None:
440
441 # upstream scheduler
442 us_scheduler = self.tech_profile[intf_id].get_us_scheduler(
443 tech_profile_instance)
444 # downstream scheduler
445 ds_scheduler = self.tech_profile[intf_id].get_ds_scheduler(
446 tech_profile_instance)
447 # create Tcont
448 tconts = self.tech_profile[intf_id].get_tconts(tech_profile_instance,
449 us_scheduler,
450 ds_scheduler)
451
452 self.stub.CreateTconts(openolt_pb2.Tconts(intf_id=intf_id,
453 onu_id=onu_id,
454 tconts=tconts))
455 else:
456 raise Exception('Tech-profile-instance-creation-failed')
457 else:
458 self.log.debug(
459 'Tech-profile-instance-already-exist-for-given port-name',
460 ofp_port_name=ofp_port_name)
461
462 # Fetch alloc id and gemports from tech profile instance
463 alloc_id = tech_profile_instance.us_scheduler.alloc_id
464 gem_port_ids = []
465 for i in range(len(
466 tech_profile_instance.upstream_gem_port_attribute_list)):
467 gem_port_ids.append(
468 tech_profile_instance.upstream_gem_port_attribute_list[i].
469 gemport_id)
470 except BaseException as e:
471 self.log.exception(exception=e)
472
473 # Update the allocated alloc_id and gem_port_id for the ONU to KV store
474 pon_intf_onu_id = (intf_id, onu_id)
475 self.resource_mgr.resource_mgrs[intf_id].update_alloc_ids_for_onu(
476 pon_intf_onu_id,
477 list([alloc_id])
478 )
479 self.resource_mgr.resource_mgrs[intf_id].update_gemport_ids_for_onu(
480 pon_intf_onu_id,
481 gem_port_ids
482 )
483
484 self.resource_mgr.update_gemports_ponport_to_onu_map_on_kv_store(
485 gem_port_ids, intf_id, onu_id
486 )
487
488 return alloc_id, gem_port_ids
Shad Ansari2dda4f32018-05-17 07:16:07 +0000489
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400490 def add_upstream_data_flow(self, intf_id, onu_id, uplink_classifier,
Girish Gowdruab836e92018-10-25 01:17:57 -0700491 uplink_action, logical_flow, alloc_id,
492 gemport_id):
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400493
494 uplink_classifier[PACKET_TAG_TYPE] = SINGLE_TAG
495
Girish Gowdru1a3b7042018-09-19 07:08:48 -0700496 self.add_hsia_flow(intf_id, onu_id, uplink_classifier,
Girish Gowdruab836e92018-10-25 01:17:57 -0700497 uplink_action, UPSTREAM,
498 logical_flow, alloc_id, gemport_id)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000499
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400500 # Secondary EAP on the subscriber vlan
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400501 (eap_active, eap_logical_flow) = self.is_eap_enabled(intf_id, onu_id)
Nicolas Palpacuer2e0fa582018-07-16 16:04:12 -0400502 if eap_active:
Girish Gowdruab836e92018-10-25 01:17:57 -0700503 self.add_eapol_flow(intf_id, onu_id, eap_logical_flow, alloc_id,
504 gemport_id, eapol_flow_category=EAPOL_SECONDARY_FLOW,
505 vlan_id=uplink_classifier[VLAN_VID])
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400506
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400507 def add_downstream_data_flow(self, intf_id, onu_id, downlink_classifier,
Girish Gowdruab836e92018-10-25 01:17:57 -0700508 downlink_action, flow, alloc_id, gemport_id):
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400509 downlink_classifier[PACKET_TAG_TYPE] = DOUBLE_TAG
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400510 # Needed ???? It should be already there
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400511 downlink_action[POP_VLAN] = True
512 downlink_action[VLAN_VID] = downlink_classifier[VLAN_VID]
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400513
Girish Gowdru1a3b7042018-09-19 07:08:48 -0700514 self.add_hsia_flow(intf_id, onu_id, downlink_classifier,
Girish Gowdruab836e92018-10-25 01:17:57 -0700515 downlink_action, DOWNSTREAM,
516 flow, alloc_id, gemport_id)
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400517
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400518 def add_hsia_flow(self, intf_id, onu_id, classifier, action,
Girish Gowdruab836e92018-10-25 01:17:57 -0700519 direction, logical_flow, alloc_id, gemport_id):
Shad Ansari2dda4f32018-05-17 07:16:07 +0000520
Girish Gowdruab836e92018-10-25 01:17:57 -0700521 flow_id = self.resource_mgr.get_hsia_flow_for_onu(intf_id, onu_id, gemport_id)
522 if flow_id is None:
523 self.log.error("hsia-flow-unavailable")
524 return
Shad Ansari2dda4f32018-05-17 07:16:07 +0000525
Shad Ansari2dda4f32018-05-17 07:16:07 +0000526 flow = openolt_pb2.Flow(
Girish Gowdruab836e92018-10-25 01:17:57 -0700527 access_intf_id=intf_id, onu_id=onu_id, flow_id=flow_id,
528 flow_type=direction, alloc_id=alloc_id, network_intf_id=0,
529 gemport_id=gemport_id,
530 classifier=self.mk_classifier(classifier),
531 action=self.mk_action(action),
532 priority=logical_flow.priority)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000533
Girish Gowdruab836e92018-10-25 01:17:57 -0700534 if self.add_flow_to_device(flow, logical_flow):
535 flow_info = self._get_flow_info_as_json_blob(flow, HSIA_FLOW)
536 self.update_flow_info_to_kv_store(flow.access_intf_id, flow.onu_id,
537 flow.flow_id, flow_info)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000538
Girish Gowdruab836e92018-10-25 01:17:57 -0700539 def add_dhcp_trap(self, intf_id, onu_id, classifier, action, logical_flow,
540 alloc_id, gemport_id):
Shad Ansari2dda4f32018-05-17 07:16:07 +0000541
Shad Ansari47e0c392018-07-17 23:55:07 +0000542 self.log.debug('add dhcp upstream trap', classifier=classifier,
Shad Ansari9712dc92018-09-26 17:46:00 +0000543 intf_id=intf_id, onu_id=onu_id, action=action)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000544
545 action.clear()
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400546 action[TRAP_TO_HOST] = True
Girish Gowdruab836e92018-10-25 01:17:57 -0700547 classifier[UDP_SRC] = 68
548 classifier[UDP_DST] = 67
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400549 classifier[PACKET_TAG_TYPE] = SINGLE_TAG
550 classifier.pop(VLAN_VID, None)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000551
Girish Gowdru1e77ea02018-09-24 09:10:35 -0700552 pon_intf_onu_id = (intf_id, onu_id)
Girish Gowdruab836e92018-10-25 01:17:57 -0700553 flow_id = self.resource_mgr.get_flow_id(pon_intf_onu_id)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000554
Matteo Scandolodf583282018-11-02 16:18:19 -0700555 dhcp_flow = openolt_pb2.Flow(
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400556 onu_id=onu_id, flow_id=flow_id, flow_type=UPSTREAM,
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400557 access_intf_id=intf_id, gemport_id=gemport_id,
Girish Gowdruab836e92018-10-25 01:17:57 -0700558 alloc_id=alloc_id, network_intf_id=0,
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400559 priority=logical_flow.priority,
560 classifier=self.mk_classifier(classifier),
Shad Ansarif9d2d102018-06-13 02:15:26 +0000561 action=self.mk_action(action))
Shad Ansari2dda4f32018-05-17 07:16:07 +0000562
Girish Gowdruab836e92018-10-25 01:17:57 -0700563 if self.add_flow_to_device(dhcp_flow, logical_flow):
564 flow_info = self._get_flow_info_as_json_blob(dhcp_flow, DHCP_FLOW)
565 self.update_flow_info_to_kv_store(dhcp_flow.access_intf_id,
566 dhcp_flow.onu_id,
567 dhcp_flow.flow_id,
568 flow_info)
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400569
Girish Gowdruab836e92018-10-25 01:17:57 -0700570 def add_eapol_flow(self, intf_id, onu_id, logical_flow, alloc_id,
571 gemport_id, eapol_flow_category=EAPOL_PRIMARY_FLOW,
Shad Ansarif9d2d102018-06-13 02:15:26 +0000572 vlan_id=DEFAULT_MGMT_VLAN):
Shad Ansari2dda4f32018-05-17 07:16:07 +0000573
Girish Gowdruab836e92018-10-25 01:17:57 -0700574 uplink_classifier = dict()
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400575 uplink_classifier[ETH_TYPE] = EAP_ETH_TYPE
576 uplink_classifier[PACKET_TAG_TYPE] = SINGLE_TAG
577 uplink_classifier[VLAN_VID] = vlan_id
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400578
Girish Gowdruab836e92018-10-25 01:17:57 -0700579 uplink_action = dict()
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400580 uplink_action[TRAP_TO_HOST] = True
Shad Ansari2dda4f32018-05-17 07:16:07 +0000581
Girish Gowdru1e77ea02018-09-24 09:10:35 -0700582 pon_intf_onu_id = (intf_id, onu_id)
Girish Gowdruab836e92018-10-25 01:17:57 -0700583 # Add Upstream EAPOL Flow.
584 if eapol_flow_category == EAPOL_PRIMARY_FLOW:
585 uplink_flow_id = self.resource_mgr.get_flow_id(pon_intf_onu_id)
586 else:
587 uplink_flow_id = self.resource_mgr.get_flow_id(pon_intf_onu_id)
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400588
Shad Ansari2dda4f32018-05-17 07:16:07 +0000589 upstream_flow = openolt_pb2.Flow(
Girish Gowdruab836e92018-10-25 01:17:57 -0700590 access_intf_id=intf_id, onu_id=onu_id, flow_id=uplink_flow_id,
591 flow_type=UPSTREAM, alloc_id=alloc_id, network_intf_id=0,
592 gemport_id=gemport_id,
Shad Ansarif9d2d102018-06-13 02:15:26 +0000593 classifier=self.mk_classifier(uplink_classifier),
Girish Gowdruab836e92018-10-25 01:17:57 -0700594 action=self.mk_action(uplink_action),
595 priority=logical_flow.priority)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000596
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400597 logical_flow = copy.deepcopy(logical_flow)
598 logical_flow.match.oxm_fields.extend(fd.mk_oxm_fields([fd.vlan_vid(
599 vlan_id | 0x1000)]))
600 logical_flow.match.type = OFPMT_OXM
601
Girish Gowdruab836e92018-10-25 01:17:57 -0700602 if self.add_flow_to_device(upstream_flow, logical_flow):
603 if eapol_flow_category == EAPOL_PRIMARY_FLOW:
604 flow_info = self._get_flow_info_as_json_blob(upstream_flow,
605 EAPOL_PRIMARY_FLOW)
606 self.update_flow_info_to_kv_store(upstream_flow.access_intf_id,
607 upstream_flow.onu_id,
608 upstream_flow.flow_id,
609 flow_info)
610 else:
611 flow_info = self._get_flow_info_as_json_blob(upstream_flow,
612 EAPOL_SECONDARY_FLOW)
613 self.update_flow_info_to_kv_store(upstream_flow.access_intf_id,
614 upstream_flow.onu_id,
615 upstream_flow.flow_id,
616 flow_info)
Shad Ansari2dda4f32018-05-17 07:16:07 +0000617
Nicolas Palpacuer6152a322018-09-05 10:52:15 -0400618 if vlan_id == DEFAULT_MGMT_VLAN:
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400619 # Add Downstream EAPOL Flow, Only for first EAP flow (BAL
620 # requirement)
621 special_vlan_downstream_flow = 4000 - onu_id
Shad Ansari2dda4f32018-05-17 07:16:07 +0000622
Girish Gowdruab836e92018-10-25 01:17:57 -0700623 downlink_classifier = dict()
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400624 downlink_classifier[PACKET_TAG_TYPE] = SINGLE_TAG
625 downlink_classifier[VLAN_VID] = special_vlan_downstream_flow
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400626
Girish Gowdruab836e92018-10-25 01:17:57 -0700627 downlink_action = dict()
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400628 downlink_action[PUSH_VLAN] = True
629 downlink_action[VLAN_VID] = vlan_id
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400630
Girish Gowdruab836e92018-10-25 01:17:57 -0700631 pon_intf_onu_id = (intf_id, onu_id)
632 downlink_flow_id = self.resource_mgr.get_flow_id(pon_intf_onu_id)
Nicolas Palpacuer6152a322018-09-05 10:52:15 -0400633
634 downstream_flow = openolt_pb2.Flow(
Girish Gowdruab836e92018-10-25 01:17:57 -0700635 access_intf_id=intf_id, onu_id=onu_id, flow_id=downlink_flow_id,
636 flow_type=DOWNSTREAM, alloc_id=alloc_id, network_intf_id=0,
637 gemport_id=gemport_id,
Nicolas Palpacuer6152a322018-09-05 10:52:15 -0400638 classifier=self.mk_classifier(downlink_classifier),
Girish Gowdruab836e92018-10-25 01:17:57 -0700639 action=self.mk_action(downlink_action),
640 priority=logical_flow.priority)
Nicolas Palpacuer6152a322018-09-05 10:52:15 -0400641
Shad Ansarifd0111d2018-09-13 21:33:06 +0000642 downstream_logical_flow = ofp_flow_stats(
643 id=logical_flow.id, cookie=logical_flow.cookie,
644 table_id=logical_flow.table_id, priority=logical_flow.priority,
645 flags=logical_flow.flags)
Nicolas Palpacuer6152a322018-09-05 10:52:15 -0400646
647 downstream_logical_flow.match.oxm_fields.extend(fd.mk_oxm_fields([
648 fd.in_port(fd.get_out_port(logical_flow)),
Girish Gowdruab836e92018-10-25 01:17:57 -0700649 fd.vlan_vid(special_vlan_downstream_flow | 0x1000)]))
Nicolas Palpacuer6152a322018-09-05 10:52:15 -0400650 downstream_logical_flow.match.type = OFPMT_OXM
651
652 downstream_logical_flow.instructions.extend(
653 fd.mk_instructions_from_actions([fd.output(
Shad Ansaricd20a6d2018-10-02 14:36:33 +0000654 self.platform.mk_uni_port_num(intf_id, onu_id))]))
Nicolas Palpacuer6152a322018-09-05 10:52:15 -0400655
Girish Gowdruab836e92018-10-25 01:17:57 -0700656 if self.add_flow_to_device(downstream_flow, downstream_logical_flow):
657 flow_info = self._get_flow_info_as_json_blob(downstream_flow,
658 EAPOL_PRIMARY_FLOW)
659 self.update_flow_info_to_kv_store(downstream_flow.access_intf_id,
660 downstream_flow.onu_id,
661 downstream_flow.flow_id,
662 flow_info)
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400663
Nicolas Palpacuer41141352018-08-31 14:11:38 -0400664 def repush_all_different_flows(self):
665 # Check if the device is supposed to have flows, if so add them
666 # Recover static flows after a reboot
667 logical_flows = self.logical_flows_proxy.get('/').items
668 devices_flows = self.flows_proxy.get('/').items
669 logical_flows_ids_provisioned = [f.cookie for f in devices_flows]
670 for logical_flow in logical_flows:
671 try:
672 if logical_flow.id not in logical_flows_ids_provisioned:
673 self.add_flow(logical_flow)
674 except Exception as e:
675 self.log.debug('Problem readding this flow', error=e)
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400676
Nicolas Palpacuer41141352018-08-31 14:11:38 -0400677 def reset_flows(self):
678 self.flows_proxy.update('/', Flows())
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400679
Shad Ansarifd0111d2018-09-13 21:33:06 +0000680 """ Add a downstream LLDP trap flow on the NNI interface
681 """
Girish Gowdruab836e92018-10-25 01:17:57 -0700682
Shad Ansarifd0111d2018-09-13 21:33:06 +0000683 def add_lldp_flow(self, logical_flow, network_intf_id=0):
Jonathan Hart5b435642018-08-20 08:50:05 -0700684
Girish Gowdruab836e92018-10-25 01:17:57 -0700685 classifier = dict()
Shad Ansarifd0111d2018-09-13 21:33:06 +0000686 classifier[ETH_TYPE] = LLDP_ETH_TYPE
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400687 classifier[PACKET_TAG_TYPE] = UNTAGGED
Girish Gowdruab836e92018-10-25 01:17:57 -0700688 action = dict()
Shad Ansarifd0111d2018-09-13 21:33:06 +0000689 action[TRAP_TO_HOST] = True
Jonathan Hart5b435642018-08-20 08:50:05 -0700690
Girish Gowdruab836e92018-10-25 01:17:57 -0700691 # LLDP flow is installed to trap LLDP packets on the NNI port.
692 # We manage flow_id resource pool on per PON port basis.
693 # Since this situation is tricky, as a hack, we pass the NNI port
694 # index (network_intf_id) as PON port Index for the flow_id resource
695 # pool. Also, there is no ONU Id available for trapping LLDP packets
696 # on NNI port, use onu_id as -1 (invalid)
697 # ****************** CAVEAT *******************
698 # This logic works if the NNI Port Id falls within the same valid
699 # range of PON Port Ids. If this doesn't work for some OLT Vendor
700 # we need to have a re-look at this.
701 # *********************************************
702 onu_id = -1
703 intf_id_onu_id = (network_intf_id, onu_id)
704 flow_id = self.resource_mgr.get_flow_id(intf_id_onu_id)
Jonathan Hart5b435642018-08-20 08:50:05 -0700705
706 downstream_flow = openolt_pb2.Flow(
Shad Ansarifd0111d2018-09-13 21:33:06 +0000707 access_intf_id=-1, # access_intf_id not required
Girish Gowdruab836e92018-10-25 01:17:57 -0700708 onu_id=onu_id, # onu_id not required
Shad Ansarifd0111d2018-09-13 21:33:06 +0000709 flow_id=flow_id,
710 flow_type=DOWNSTREAM,
Shad Ansarifd0111d2018-09-13 21:33:06 +0000711 network_intf_id=network_intf_id,
Girish Gowdruab836e92018-10-25 01:17:57 -0700712 gemport_id=-1, # gemport_id not required
Jonathan Hart5b435642018-08-20 08:50:05 -0700713 classifier=self.mk_classifier(classifier),
Girish Gowdruab836e92018-10-25 01:17:57 -0700714 action=self.mk_action(action),
715 priority=logical_flow.priority)
Jonathan Hart5b435642018-08-20 08:50:05 -0700716
Shad Ansarifd0111d2018-09-13 21:33:06 +0000717 self.log.debug('add lldp downstream trap', classifier=classifier,
718 action=action, flow=downstream_flow)
Girish Gowdruab836e92018-10-25 01:17:57 -0700719 if self.add_flow_to_device(downstream_flow, logical_flow):
720 self.update_flow_info_to_kv_store(network_intf_id, onu_id,
721 flow_id, downstream_flow)
Jonathan Hart5b435642018-08-20 08:50:05 -0700722
Shad Ansari2dda4f32018-05-17 07:16:07 +0000723 def mk_classifier(self, classifier_info):
724
725 classifier = openolt_pb2.Classifier()
726
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400727 if ETH_TYPE in classifier_info:
728 classifier.eth_type = classifier_info[ETH_TYPE]
729 if IP_PROTO in classifier_info:
730 classifier.ip_proto = classifier_info[IP_PROTO]
731 if VLAN_VID in classifier_info:
732 classifier.o_vid = classifier_info[VLAN_VID]
733 if METADATA in classifier_info:
734 classifier.i_vid = classifier_info[METADATA]
735 if VLAN_PCP in classifier_info:
736 classifier.o_pbits = classifier_info[VLAN_PCP]
737 if UDP_SRC in classifier_info:
738 classifier.src_port = classifier_info[UDP_SRC]
739 if UDP_DST in classifier_info:
740 classifier.dst_port = classifier_info[UDP_DST]
741 if IPV4_DST in classifier_info:
742 classifier.dst_ip = classifier_info[IPV4_DST]
743 if IPV4_SRC in classifier_info:
744 classifier.src_ip = classifier_info[IPV4_SRC]
745 if PACKET_TAG_TYPE in classifier_info:
746 if classifier_info[PACKET_TAG_TYPE] == SINGLE_TAG:
747 classifier.pkt_tag_type = SINGLE_TAG
748 elif classifier_info[PACKET_TAG_TYPE] == DOUBLE_TAG:
749 classifier.pkt_tag_type = DOUBLE_TAG
750 elif classifier_info[PACKET_TAG_TYPE] == UNTAGGED:
751 classifier.pkt_tag_type = UNTAGGED
Shad Ansari2dda4f32018-05-17 07:16:07 +0000752 else:
753 classifier.pkt_tag_type = 'none'
754
755 return classifier
756
757 def mk_action(self, action_info):
758 action = openolt_pb2.Action()
759
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400760 if POP_VLAN in action_info:
761 action.o_vid = action_info[VLAN_VID]
Shad Ansari2dda4f32018-05-17 07:16:07 +0000762 action.cmd.remove_outer_tag = True
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400763 elif PUSH_VLAN in action_info:
764 action.o_vid = action_info[VLAN_VID]
Shad Ansari2dda4f32018-05-17 07:16:07 +0000765 action.cmd.add_outer_tag = True
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400766 elif TRAP_TO_HOST in action_info:
Shad Ansari2dda4f32018-05-17 07:16:07 +0000767 action.cmd.trap_to_host = True
Shad Ansarif9d2d102018-06-13 02:15:26 +0000768 else:
Nicolas Palpacuer324dcae2018-08-02 11:12:22 -0400769 self.log.info('Invalid-action-field', action_info=action_info)
Shad Ansarif9d2d102018-06-13 02:15:26 +0000770 return
Shad Ansari2dda4f32018-05-17 07:16:07 +0000771 return action
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400772
773 def is_eap_enabled(self, intf_id, onu_id):
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400774 flows = self.logical_flows_proxy.get('/').items
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400775
776 for flow in flows:
777 eap_flow = False
778 eap_intf_id = None
779 eap_onu_id = None
780 for field in fd.get_ofb_fields(flow):
781 if field.type == fd.ETH_TYPE:
782 if field.eth_type == EAP_ETH_TYPE:
783 eap_flow = True
784 if field.type == fd.IN_PORT:
Shad Ansaricd20a6d2018-10-02 14:36:33 +0000785 eap_intf_id = self.platform.intf_id_from_uni_port_num(
Shad Ansari47e0c392018-07-17 23:55:07 +0000786 field.port)
Shad Ansaricd20a6d2018-10-02 14:36:33 +0000787 eap_onu_id = self.platform.onu_id_from_port_num(field.port)
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400788
789 if eap_flow:
790 self.log.debug('eap flow detected', onu_id=onu_id,
791 intf_id=intf_id, eap_intf_id=eap_intf_id,
792 eap_onu_id=eap_onu_id)
793 if eap_flow and intf_id == eap_intf_id and onu_id == eap_onu_id:
Girish Gowdruab836e92018-10-25 01:17:57 -0700794 return True, flow
Nicolas Palpacuer61815162018-06-20 18:12:04 -0400795
Girish Gowdruab836e92018-10-25 01:17:57 -0700796 return False, None
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400797
Nicolas Palpacuer41141352018-08-31 14:11:38 -0400798 def get_subscriber_vlan(self, port):
799 self.log.debug('looking from subscriber flow for port', port=port)
800
801 flows = self.logical_flows_proxy.get('/').items
802 for flow in flows:
803 in_port = fd.get_in_port(flow)
804 out_port = fd.get_out_port(flow)
Nicolas Palpacuer5780e152018-09-05 17:25:42 -0400805 if in_port == port and \
Girish Gowdruab836e92018-10-25 01:17:57 -0700806 self.platform.intf_id_to_port_type_name(out_port) \
Shad Ansarifd0111d2018-09-13 21:33:06 +0000807 == Port.ETHERNET_NNI:
Nicolas Palpacuer41141352018-08-31 14:11:38 -0400808 fields = fd.get_ofb_fields(flow)
809 self.log.debug('subscriber flow found', fields=fields)
810 for field in fields:
811 if field.type == OFPXMT_OFB_VLAN_VID:
812 self.log.debug('subscriber vlan found',
813 vlan_id=field.vlan_vid)
814 return field.vlan_vid & 0x0fff
815 self.log.debug('No subscriber flow found', port=port)
816 return None
817
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400818 def add_flow_to_device(self, flow, logical_flow):
819 self.log.debug('pushing flow to device', flow=flow)
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400820 try:
821 self.stub.FlowAdd(flow)
822 except grpc.RpcError as grpc_e:
823 if grpc_e.code() == grpc.StatusCode.ALREADY_EXISTS:
824 self.log.warn('flow already exists', e=grpc_e, flow=flow)
825 else:
826 self.log.error('failed to add flow',
827 logical_flow=logical_flow, flow=flow,
828 grpc_error=grpc_e)
Girish Gowdruab836e92018-10-25 01:17:57 -0700829 return False
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400830 else:
831 self.register_flow(logical_flow, flow)
Girish Gowdruab836e92018-10-25 01:17:57 -0700832 return True
833
834 def update_flow_info_to_kv_store(self, intf_id, onu_id, flow_id, flow):
835 pon_intf_onu_id = (intf_id, onu_id)
836 self.resource_mgr.update_flow_id_info_for_onu(pon_intf_onu_id,
837 flow_id, flow)
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400838
839 def register_flow(self, logical_flow, device_flow):
840 self.log.debug('registering flow in device',
841 logical_flow=logical_flow, device_flow=device_flow)
842 stored_flow = copy.deepcopy(logical_flow)
843 stored_flow.id = self.generate_stored_id(device_flow.flow_id,
844 device_flow.flow_type)
845 self.log.debug('generated device flow id', id=stored_flow.id,
846 flow_id=device_flow.flow_id,
847 direction=device_flow.flow_type)
848 stored_flow.cookie = logical_flow.id
849 flows = self.flows_proxy.get('/')
850 flows.items.extend([stored_flow])
851 self.flows_proxy.update('/', flows)
852
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400853 def find_next_flow(self, flow):
854 table_id = fd.get_goto_table_id(flow)
855 metadata = 0
Chip Boling41f795a2018-10-04 15:45:34 -0500856 # Prior to ONOS 1.13.5, Metadata contained the UNI output port number. In
857 # 1.13.5 and later, the lower 32-bits is the output port number and the
858 # upper 32-bits is the inner-vid we are looking for. Use just the lower 32
859 # bits. Allows this code to work with pre- and post-1.13.5 ONOS OltPipeline
860
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400861 for field in fd.get_ofb_fields(flow):
862 if field.type == fd.METADATA:
Chip Boling41f795a2018-10-04 15:45:34 -0500863 metadata = field.table_metadata & 0xFFFFFFFF
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400864 if table_id is None:
865 return None
866 flows = self.logical_flows_proxy.get('/').items
867 next_flows = []
868 for f in flows:
869 if f.table_id == table_id:
Jonathan Hart5b435642018-08-20 08:50:05 -0700870 # FIXME
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400871 if fd.get_in_port(f) == fd.get_in_port(flow) and \
872 fd.get_out_port(f) == metadata:
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400873 next_flows.append(f)
874
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400875 if len(next_flows) == 0:
876 self.log.warning('no next flow found, it may be a timing issue',
877 flow=flow, number_of_flows=len(flows))
878 reactor.callLater(5, self.add_flow, flow)
879 return None
880
Jonathan Hart5b435642018-08-20 08:50:05 -0700881 next_flows.sort(key=lambda f: f.priority, reverse=True)
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400882
883 return next_flows[0]
884
885 def update_children_flows(self, device_rules_map):
886
887 for device_id, (flows, groups) in device_rules_map.iteritems():
888 if device_id != self.device_id:
889 self.root_proxy.update('/devices/{}/flows'.format(device_id),
890 Flows(items=flows.values()))
891 self.root_proxy.update('/devices/{}/flow_groups'.format(
892 device_id), FlowGroups(items=groups.values()))
893
Girish Gowdruab836e92018-10-25 01:17:57 -0700894 def clear_flows_and_scheduler_for_logical_port(self, child_device, logical_port):
895 ofp_port_name = logical_port.ofp_port.name
896 pon_port = child_device.proxy_address.channel_id
897 onu_id = child_device.proxy_address.onu_id
898 # TODO: The DEFAULT_TECH_PROFILE_ID is assumed. Right way to do,
899 # is probably to maintain a list of Tech-profile table IDs associated
900 # with the UNI logical_port. This way, when the logical port is deleted,
901 # all the associated tech-profile configuration with the UNI logical_port
902 # can be cleared.
903 tech_profile_instance = self.tech_profile[pon_port]. \
904 get_tech_profile_instance(
905 DEFAULT_TECH_PROFILE_TABLE_ID,
906 ofp_port_name)
907 flow_ids = self.resource_mgr.get_current_flow_ids_for_onu(pon_port,
908 onu_id)
909 self.log.debug("outstanding-flows-to-be-cleared", flow_ids=flow_ids)
910 for flow_id in flow_ids:
911 flow_infos = self.resource_mgr.get_flow_id_info(pon_port,
912 onu_id,
913 flow_id)
914 for flow_info in flow_infos:
915 direction = flow_info['flow_type']
916 flow_to_remove = openolt_pb2.Flow(flow_id=flow_id,
917 flow_type=direction)
918 try:
919 self.stub.FlowRemove(flow_to_remove)
920 except grpc.RpcError as grpc_e:
921 if grpc_e.code() == grpc.StatusCode.NOT_FOUND:
922 self.log.debug('This flow does not exist on the switch, '
923 'normal after an OLT reboot',
924 flow=flow_to_remove)
925 else:
926 raise grpc_e
927
928 self.resource_mgr.free_flow_id(pon_port, onu_id, flow_id)
929
930 try:
931 tconts = self.tech_profile[pon_port].get_tconts(tech_profile_instance)
932 self.stub.RemoveTconts(openolt_pb2.Tconts(intf_id=pon_port,
933 onu_id=onu_id,
934 tconts=tconts))
935 except grpc.RpcError as grpc_e:
936 self.log.error('error-removing-tcont-scheduler-queues',
937 err=grpc_e)
938
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400939 def generate_stored_id(self, flow_id, direction):
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400940 if direction == UPSTREAM:
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400941 self.log.debug('upstream flow, shifting id')
942 return 0x1 << 15 | flow_id
Nicolas Palpacuer2789f042018-09-17 09:10:29 -0400943 elif direction == DOWNSTREAM:
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400944 self.log.debug('downstream flow, not shifting id')
945 return flow_id
946 else:
947 self.log.warn('Unrecognized direction', direction=direction)
948 return flow_id
949
950 def decode_stored_id(self, id):
951 if id >> 15 == 0x1:
Girish Gowdruab836e92018-10-25 01:17:57 -0700952 return id & 0x7fff, UPSTREAM
Nicolas Palpacuer0c7c3162018-08-08 11:27:57 -0400953 else:
Girish Gowdruab836e92018-10-25 01:17:57 -0700954 return id, DOWNSTREAM
955
956 def _populate_tech_profile_per_pon_port(self):
957 for arange in self.resource_mgr.device_info.ranges:
958 for intf_id in arange.intf_ids:
959 self.tech_profile[intf_id] = \
960 self.resource_mgr.resource_mgrs[intf_id].tech_profile
961
962 # Make sure we have as many tech_profiles as there are pon ports on
963 # the device
964 assert len(self.tech_profile) == self.resource_mgr.device_info.pon_ports
965
966 def _get_flow_info_as_json_blob(self, flow, flow_category):
967 json_blob = MessageToDict(message=flow,
968 preserving_proto_field_name=True)
969 self.log.debug("flow-info", json_blob=json_blob)
970 json_blob['flow_category'] = flow_category
971 flow_info = self.resource_mgr.get_flow_id_info(flow.access_intf_id,
972 flow.onu_id, flow.flow_id)
973
974 if flow_info is None:
975 flow_info = list()
976 flow_info.append(json_blob)
977 else:
978 assert (isinstance(flow_info, list))
979 flow_info.append(json_blob)
980
981 return flow_info