blob: 35ef09f63ba00ae2f09e8a714ed6899f8a81e75c [file] [log] [blame]
Shad Ansari2825d012018-02-22 23:57:46 +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#
16
17import structlog
18import threading
19import grpc
20import collections
21import time
22
Shad Ansari15928d12018-04-17 02:42:13 +000023from twisted.internet import reactor
Shad Ansari0346f0d2018-04-26 06:54:09 +000024from scapy.layers.l2 import Ether, Dot1Q
25import binascii
Shad Ansari15928d12018-04-17 02:42:13 +000026
Shad Ansari2825d012018-02-22 23:57:46 +000027from voltha.protos.device_pb2 import Port, Device
28from voltha.protos.common_pb2 import OperStatus, AdminState, ConnectStatus
29from voltha.protos.logical_device_pb2 import LogicalDevice
30from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, OFPPS_LINK_DOWN, \
31 OFPPF_1GB_FD, OFPC_GROUP_STATS, OFPC_PORT_STATS, OFPC_TABLE_STATS, \
32 OFPC_FLOW_STATS, ofp_switch_features, ofp_desc, ofp_port, \
33 OFPXMC_OPENFLOW_BASIC
34from voltha.protos.logical_device_pb2 import LogicalPort, LogicalDevice
35from voltha.core.logical_device_agent import mac_str_to_tuple
36from voltha.registry import registry
37from voltha.adapters.openolt.protos import openolt_pb2_grpc, openolt_pb2
38from voltha.protos.bbf_fiber_tcont_body_pb2 import TcontsConfigData
39import voltha.core.flow_decomposer as fd
40
41ASFVOLT_HSIA_ID = 13 # FIXME
Shad Ansari8f1b2532018-04-21 07:51:39 +000042ASFVOLT_DHCP_TAGGED_ID = 5 # FIXME
Shad Ansari2825d012018-02-22 23:57:46 +000043
44Onu = collections.namedtuple("Onu", ["intf_id", "onu_id"])
45
46"""
Shad Ansari42db7342018-04-25 21:39:46 +000047GEM port id
48
49 12 5 0
50 +----------------+----------+
51 | onu id | svc id |
52 +----------------+----------+
53
54- onu id field is 8 bits (256 ONUs per PON).
55- svc id is used to differentiate multiple GEM ports of an ONU.
56 This could be a LAN/UNI port index, GEM ID, queue ID,
57 traffic class ID, or some other service profile information.
58 svc id is 5 bits (32 GEM ports per ONU)
59
60Logical (OF) UNI port number
61
Shad Ansari0346f0d2018-04-26 06:54:09 +000062 15 8 0
Shad Ansari42db7342018-04-25 21:39:46 +000063 +--+------------+----------------+
64 |0 | pon id | onu id |
65 +--+------------+----------------+
66
67- pon id is a zero based id representing a pon interface
68 This is usually the pon interface id assigned by the hardware
69- Note that no LAN (UNI) port information is included.
70 Do we represent physical LAN/UNI ports as OF ports?
71 Or does it make more sense to represent GEM ports as OF ports?
72
73Logical (OF) NNI port number
74
75 15 0
76 +--+------------------------------+
77 |1 | nni id |
78 +--+------------------------------+
79"""
80
81"""
Shad Ansari2825d012018-02-22 23:57:46 +000082OpenoltDevice represents an OLT.
83"""
84class OpenoltDevice(object):
85
86 def __init__(self, **kwargs):
87 super(OpenoltDevice, self).__init__()
88
89 self.adapter_agent = kwargs['adapter_agent']
90 device = kwargs['device']
91 self.device_id = device.id
92 self.host_and_port = device.host_and_port
93 self.log = structlog.get_logger(id=self.device_id, ip=self.host_and_port)
94 self.oper_state = 'unknown'
95 self.nni_oper_state = dict() #intf_id -> oper_state
96 self.onus = {} # Onu -> serial_number
Shad Ansari2825d012018-02-22 23:57:46 +000097
98 # Create logical device
99 ld = LogicalDevice(
100 desc=ofp_desc(
101 mfr_desc='FIXME', hw_desc='FIXME',
102 sw_desc='FIXME', serial_num='FIXME',
103 dp_desc='n/a'),
104 switch_features=ofp_switch_features(
105 n_buffers=256, n_tables=2,
106 capabilities=(
107 OFPC_FLOW_STATS | OFPC_TABLE_STATS |
108 OFPC_GROUP_STATS | OFPC_PORT_STATS)),
109 root_device_id=self.device_id)
110 # FIXME
111 ld_initialized = self.adapter_agent.create_logical_device(ld, dpid='de:ad:be:ef:fe:ed') # FIXME
112 self.logical_device_id = ld_initialized.id
113
114 # Update device
115 device.root = True
116 device.vendor = 'Edgecore'
117 device.model = 'ASFvOLT16'
118 device.serial_number = self.host_and_port # FIXME
119 device.parent_id = self.logical_device_id
120 device.connect_status = ConnectStatus.REACHABLE
121 device.oper_status = OperStatus.ACTIVATING
122 self.adapter_agent.update_device(device)
123
Shad Ansari8f1b2532018-04-21 07:51:39 +0000124
Shad Ansari2825d012018-02-22 23:57:46 +0000125 # Initialize gRPC
126 self.channel = grpc.insecure_channel(self.host_and_port)
Shad Ansari8f1b2532018-04-21 07:51:39 +0000127 self.channel_ready_future = grpc.channel_ready_future(self.channel)
Shad Ansari2825d012018-02-22 23:57:46 +0000128
129 # Start indications thread
Shad Ansari8f1b2532018-04-21 07:51:39 +0000130 self.indications_thread = threading.Thread(target=self.process_indications)
Shad Ansari2825d012018-02-22 23:57:46 +0000131 self.indications_thread.daemon = True
132 self.indications_thread.start()
133
Shad Ansari8f1b2532018-04-21 07:51:39 +0000134 def process_indications(self):
135 self.channel_ready_future.result() # blocks till gRPC connection is complete
136 self.stub = openolt_pb2_grpc.OpenoltStub(self.channel)
Shad Ansari15928d12018-04-17 02:42:13 +0000137 self.indications = self.stub.EnableIndication(openolt_pb2.Empty())
Shad Ansari8f1b2532018-04-21 07:51:39 +0000138 while True:
Shad Ansari15928d12018-04-17 02:42:13 +0000139 # get the next indication from olt
Shad Ansari2825d012018-02-22 23:57:46 +0000140 ind = next(self.indications)
141 self.log.debug("rx indication", indication=ind)
Shad Ansari15928d12018-04-17 02:42:13 +0000142 # schedule indication handlers to be run in the main event loop
Shad Ansari2825d012018-02-22 23:57:46 +0000143 if ind.HasField('olt_ind'):
Shad Ansari15928d12018-04-17 02:42:13 +0000144 reactor.callFromThread(self.olt_indication, ind.olt_ind)
Shad Ansari2825d012018-02-22 23:57:46 +0000145 elif ind.HasField('intf_ind'):
Shad Ansari15928d12018-04-17 02:42:13 +0000146 reactor.callFromThread(self.intf_indication, ind.intf_ind)
Shad Ansari2825d012018-02-22 23:57:46 +0000147 elif ind.HasField('intf_oper_ind'):
Shad Ansari15928d12018-04-17 02:42:13 +0000148 reactor.callFromThread(self.intf_oper_indication, ind.intf_oper_ind)
Shad Ansari2825d012018-02-22 23:57:46 +0000149 elif ind.HasField('onu_disc_ind'):
Shad Ansari15928d12018-04-17 02:42:13 +0000150 reactor.callFromThread(self.onu_discovery_indication, ind.onu_disc_ind)
Shad Ansari2825d012018-02-22 23:57:46 +0000151 elif ind.HasField('onu_ind'):
Shad Ansari15928d12018-04-17 02:42:13 +0000152 reactor.callFromThread(self.onu_indication, ind.onu_ind)
Shad Ansari2825d012018-02-22 23:57:46 +0000153 elif ind.HasField('omci_ind'):
Shad Ansari15928d12018-04-17 02:42:13 +0000154 reactor.callFromThread(self.omci_indication, ind.omci_ind)
Shad Ansari42db7342018-04-25 21:39:46 +0000155 elif ind.HasField('pkt_ind'):
156 reactor.callFromThread(self.packet_indication, ind.pkt_ind)
Shad Ansari2825d012018-02-22 23:57:46 +0000157
158 def olt_indication(self, olt_indication):
159 self.log.debug("olt indication", olt_ind=olt_indication)
160 self.set_oper_state(olt_indication.oper_state)
161
162 def intf_indication(self, intf_indication):
163 self.log.debug("intf indication", intf_id=intf_indication.intf_id,
164 oper_state=intf_indication.oper_state)
165
166 if intf_indication.oper_state == "up":
167 oper_status = OperStatus.ACTIVE
168 else:
169 oper_status = OperStatus.DISCOVERED
170
171 # FIXME - If port exists, update oper state
172 self.add_port(intf_indication.intf_id, Port.PON_OLT, oper_status)
173
174 def intf_oper_indication(self, intf_oper_indication):
175 self.log.debug("Received interface oper state change indication", intf_id=intf_oper_indication.intf_id,
176 type=intf_oper_indication.type, oper_state=intf_oper_indication.oper_state)
177
178 if intf_oper_indication.oper_state == "up":
179 oper_state = OperStatus.ACTIVE
180 else:
181 oper_state = OperStatus.DISCOVERED
182
183 if intf_oper_indication.type == "nni":
184
Shad Ansari0346f0d2018-04-26 06:54:09 +0000185 # FIXME - creating logical port for 2nd interface throws exception!
Shad Ansari2825d012018-02-22 23:57:46 +0000186 if intf_oper_indication.intf_id != 0:
187 return
188
189 if intf_oper_indication.intf_id not in self.nni_oper_state:
190 self.nni_oper_state[intf_oper_indication.intf_id] = oper_state
191 port_no, label = self.add_port(intf_oper_indication.intf_id, Port.ETHERNET_NNI, oper_state)
192 self.log.debug("int_oper_indication", port_no=port_no, label=label)
193 self.add_logical_port(port_no) # FIXME - add oper_state
194 elif intf_oper_indication.intf_id != self.nni_oper_state:
195 # FIXME - handle subsequent NNI oper state change
196 pass
197
198 elif intf_oper_indication.type == "pon":
199 # FIXME - handle PON oper state change
200 pass
201
202 def onu_discovery_indication(self, onu_disc_indication):
203 self.log.debug("onu discovery indication", intf_id=onu_disc_indication.intf_id,
204 serial_number=onu_disc_indication.serial_number)
205
206 onu_id = self.lookup_onu(serial_number=onu_disc_indication.serial_number)
207
208 if onu_id is None:
209 onu_id = self.new_onu_id(onu_disc_indication.intf_id)
Shad Ansari15928d12018-04-17 02:42:13 +0000210 try:
211 self.add_onu_device(
212 onu_disc_indication.intf_id,
213 self.intf_id_to_port_no(onu_disc_indication.intf_id, Port.PON_OLT),
214 onu_id,
215 onu_disc_indication.serial_number)
216 except Exception as e:
217 self.log.exception('onu activation failed', e=e)
218 else:
219 self.activate_onu(
220 onu_disc_indication.intf_id, onu_id,
221 serial_number=onu_disc_indication.serial_number)
Shad Ansari2825d012018-02-22 23:57:46 +0000222 else:
223 # FIXME - handle discovery of already activated onu
224 self.log.info("onu activation in progress",
225 intf_id=onu_disc_indication.intf_id, onu_id=onu_id)
226
Shad Ansari0346f0d2018-04-26 06:54:09 +0000227 def mk_uni_port_num(self, intf_id, onu_id):
228 return intf_id << 8 | onu_id
229
230 def onu_id_from_port_num(self, port_num):
231 return port_num & 0xFF
232
233 def intf_id_from_port_num(self, port_num):
234 return (port_num >> 8) & 0x7F
Shad Ansari2825d012018-02-22 23:57:46 +0000235
236 def onu_indication(self, onu_indication):
237
238 self.log.debug("onu indication", intf_id=onu_indication.intf_id,
239 onu_id=onu_indication.onu_id)
240
241 # FIXME - handle onu_id/serial_number mismatch
242 assert onu_indication.onu_id == self.lookup_onu(serial_number=onu_indication.serial_number)
243
244 onu_device = self.adapter_agent.get_child_device(
245 self.device_id, onu_id=onu_indication.onu_id)
246 assert onu_device is not None
247
248 msg = {'proxy_address':onu_device.proxy_address,
249 'event':'activation-completed',
250 'event_data':{'activation_successful':True}}
251 self.adapter_agent.publish_inter_adapter_message(onu_device.id, msg)
252
253 #
254 # tcont create (onu)
255 #
256 alloc_id = self.mk_alloc_id(onu_indication.onu_id)
257 msg = {'proxy_address':onu_device.proxy_address,
258 'event':'create-tcont',
259 'event_data':{'alloc_id':alloc_id}}
260 self.adapter_agent.publish_inter_adapter_message(onu_device.id, msg)
261
262 #
263 # v_enet create (olt)
264 #
Shad Ansari0346f0d2018-04-26 06:54:09 +0000265 uni_no = self.mk_uni_port_num(onu_indication.intf_id, onu_indication.onu_id)
Shad Ansari2825d012018-02-22 23:57:46 +0000266 uni_name = self.port_name(uni_no, Port.ETHERNET_UNI)
267 self.adapter_agent.add_port(
268 self.device_id,
269 Port(
270 port_no=uni_no,
271 label=uni_name,
272 type=Port.ETHERNET_UNI,
273 admin_state=AdminState.ENABLED,
274 oper_status=OperStatus.ACTIVE))
275
276 #
277 # v_enet create (onu)
278 #
279 interface_name = self.port_name(onu_indication.intf_id, Port.PON_OLT)
280 msg = {'proxy_address':onu_device.proxy_address,
281 'event':'create-venet',
282 'event_data':{'uni_name':uni_name, 'interface_name':uni_name}}
283 self.adapter_agent.publish_inter_adapter_message(onu_device.id, msg)
284
285 #
286 # gem port create
287 #
288 gemport_id = self.mk_gemport_id(onu_indication.onu_id)
289 msg = {'proxy_address':onu_device.proxy_address,
290 'event':'create-gemport',
291 'event_data':{'gemport_id':gemport_id}}
292 self.adapter_agent.publish_inter_adapter_message(onu_device.id, msg)
293
Shad Ansari42db7342018-04-25 21:39:46 +0000294 def mk_gemport_id(self, onu_id, uni_idx=0):
295 # FIXME - driver should do prefixing 1 << 13 as its Maple specific
296 return 1<<13 | onu_id<<5 | uni_idx
297
298 def onu_id_from_gemport_id(self, gemport_id):
299 # FIXME - driver should remove the (1 << 13) prefix as its Maple specific
300 return (gemport_id & ~(1<<13)) >> 5
Shad Ansari2825d012018-02-22 23:57:46 +0000301
302 def mk_alloc_id(self, onu_id):
303 return 1023 + onu_id # FIXME
304
305 def omci_indication(self, omci_indication):
306
307 self.log.debug("omci indication", intf_id=omci_indication.intf_id,
308 onu_id=omci_indication.onu_id)
309
310 onu_device = self.adapter_agent.get_child_device(
311 self.device_id,
312 onu_id=omci_indication.onu_id)
313 self.adapter_agent.receive_proxied_message(
314 onu_device.proxy_address,
315 omci_indication.pkt)
316
Shad Ansari42db7342018-04-25 21:39:46 +0000317 def packet_indication(self, pkt_indication):
318
319 self.log.debug("packet indication", intf_id=pkt_indication.intf_id,
320 gemport_id=pkt_indication.gemport_id,
321 flow_id=pkt_indication.flow_id)
322
323 onu_id = self.onu_id_from_gemport_id(pkt_indication.gemport_id)
Shad Ansari0346f0d2018-04-26 06:54:09 +0000324 logical_port_num = self.mk_uni_port_num(pkt_indication.intf_id, onu_id)
Shad Ansari42db7342018-04-25 21:39:46 +0000325
326 pkt = Ether(pkt_indication.pkt)
327 kw = dict(
328 logical_device_id=self.logical_device_id,
329 logical_port_no=logical_port_num,
330 )
331 self.adapter_agent.send_packet_in(packet=str(pkt), **kw)
332
333 def packet_out(self, egress_port, msg):
Shad Ansari0346f0d2018-04-26 06:54:09 +0000334 pkt = Ether(msg)
335 self.log.info('packet out', egress_port=egress_port,
336 packet=str(pkt).encode("HEX"))
337
338 if pkt.haslayer(Dot1Q):
339 outer_shim = pkt.getlayer(Dot1Q)
340 if isinstance(outer_shim.payload, Dot1Q):
341 payload = (
342 Ether(src=pkt.src, dst=pkt.dst, type=outer_shim.type) /
343 outer_shim.payload
344 )
345 else:
346 payload = pkt
347 else:
348 payload = pkt
349
350 self.log.info('sending-packet-to-device', egress_port=egress_port,
351 packet=str(payload).encode("HEX"))
352
353 send_pkt = binascii.unhexlify(str(payload).encode("HEX"))
354
355 onu_pkt = openolt_pb2.OnuPacket(intf_id=intf_id_from_port_num(egress_port),
356 onu_id=onu_id_from_port_num(egress_port), pkt=send_pkt)
357
358 self.stub.OnuPacketOut(onu_packet)
Shad Ansari42db7342018-04-25 21:39:46 +0000359
Shad Ansari2825d012018-02-22 23:57:46 +0000360 def activate_onu(self, intf_id, onu_id, serial_number):
361
362 self.log.info("activate onu", intf_id=intf_id, onu_id=onu_id,
363 serial_number=serial_number)
364
365 self.onus[Onu(intf_id=intf_id, onu_id=onu_id)] = serial_number
366
367 onu = openolt_pb2.Onu(
368 intf_id=intf_id, onu_id=onu_id, serial_number=serial_number)
369
370 self.stub.ActivateOnu(onu)
371
372
373 def send_proxied_message(self, proxy_address, msg):
374 omci = openolt_pb2.OmciMsg(
375 intf_id=proxy_address.channel_id, # intf_id
376 onu_id=proxy_address.onu_id,
377 pkt=str(msg))
378 self.stub.OmciMsgOut(omci)
379
380 def add_onu_device(self, intf_id, port_no, onu_id, serial_number):
381
382 self.log.info("Adding ONU", port_no=port_no, onu_id=onu_id,
383 serial_number=serial_number)
384
385 # NOTE - channel_id of onu is set to intf_id
386 proxy_address = Device.ProxyAddress(
387 device_id=self.device_id,
388 channel_id=intf_id,
389 onu_id=onu_id,
390 onu_session_id=onu_id)
391
392 self.log.info("Adding ONU", proxy_address=proxy_address)
393
394 serial_number_str = ''.join([
395 serial_number.vendor_id,
396 self.stringify_vendor_specific(serial_number.vendor_specific)])
397
398 self.adapter_agent.add_onu_device(
399 parent_device_id=self.device_id, parent_port_no=port_no,
400 vendor_id=serial_number.vendor_id, proxy_address=proxy_address,
401 root=True, serial_number=serial_number_str,
402 admin_state=AdminState.ENABLED) # FIXME
403
404 def intf_id_to_port_no(self, intf_id, intf_type):
405 if intf_type is Port.ETHERNET_NNI:
406 # FIXME - Remove hardcoded '129'
407 return intf_id + 129
408 elif intf_type is Port.PON_OLT:
409 # Interface Ids (reported by device) are zero-based indexed
410 # OpenFlow port numbering is one-based.
411 return intf_id + 1
412 else:
413 raise Exception('Invalid port type')
414
415
416 def port_name(self, port_no, port_type):
417 if port_type is Port.ETHERNET_NNI:
418 prefix = "nni"
419 elif port_type is Port.PON_OLT:
420 prefix = "pon"
421 elif port_type is Port.ETHERNET_UNI:
422 prefix = "uni"
423 return prefix + "-" + str(port_no)
424
425 def update_device_status(self, connect_status=None, oper_status=None, reason=None):
426 device = self.adapter_agent.get_device(self.device_id)
427 if connect_status is not None:
428 device.connect_status = connect_status
429 if oper_status is not None:
430 device.oper_status = oper_status
431 if reason is not None:
432 device.reason = reason
433 self.adapter_agent.update_device(device)
434
435 def add_logical_port(self, port_no):
436 self.log.info('adding-logical-port', port_no=port_no)
437
438 label = self.port_name(port_no, Port.ETHERNET_NNI)
439
440 cap = OFPPF_1GB_FD | OFPPF_FIBER
441 curr_speed = OFPPF_1GB_FD
442 max_speed = OFPPF_1GB_FD
443
444 ofp = ofp_port(
445 port_no=port_no,
446 hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % port_no),
447 name=label,
448 config=0,
449 state=OFPPS_LIVE,
450 curr=cap,
451 advertised=cap,
452 peer=cap,
453 curr_speed=curr_speed,
454 max_speed=max_speed)
455
456 logical_port = LogicalPort(
457 id=label,
458 ofp_port=ofp,
459 device_id=self.device_id,
460 device_port_no=port_no,
461 root_port=True
462 )
463
464 self.adapter_agent.add_logical_port(self.logical_device_id, logical_port)
465
466 def add_port(self, intf_id, port_type, oper_status):
467 port_no = self.intf_id_to_port_no(intf_id, port_type)
468
469 label = self.port_name(port_no, port_type)
470
471 self.log.info('adding-port', port_no=port_no, label=label, port_type=port_type)
472 port = Port(
473 port_no=port_no,
474 label=label,
475 type=port_type,
476 admin_state=AdminState.ENABLED,
477 oper_status=oper_status
478 )
479 self.adapter_agent.add_port(self.device_id, port)
480 return port_no, label
481
482 def set_oper_state(self, new_state):
483 if self.oper_state != new_state:
484 if new_state == 'up':
485 self.update_device_status(
486 connect_status=ConnectStatus.REACHABLE,
487 oper_status=OperStatus.ACTIVE,
488 reason='OLT indication - operation state up')
489 elif new_state == 'down':
490 self.update_device_status(
491 connect_status=ConnectStatus.REACHABLE,
492 oper_status=OperStatus.FAILED,
493 reason='OLT indication - operation state down')
494 else:
495 raise ValueError('Invalid oper_state in olt_indication')
496
497 self.oper_state = new_state
498
499 def new_onu_id(self, intf_id):
500 onu_id = None
501 # onu_id is unique per PON.
502 # FIXME - Remove hardcoded limit on ONUs per PON (64)
503 for i in range(1, 64):
504 onu = Onu(intf_id=intf_id, onu_id=i)
505 if onu not in self.onus:
506 onu_id = i
507 break
508 return onu_id
509
510 def stringify_vendor_specific(self, vendor_specific):
511 return ''.join(str(i) for i in [
512 ord(vendor_specific[0])>>4 & 0x0f,
513 ord(vendor_specific[0]) & 0x0f,
514 ord(vendor_specific[1])>>4 & 0x0f,
515 ord(vendor_specific[1]) & 0x0f,
516 ord(vendor_specific[2])>>4 & 0x0f,
517 ord(vendor_specific[2]) & 0x0f,
518 ord(vendor_specific[3])>>4 & 0x0f,
519 ord(vendor_specific[3]) & 0x0f])
520
521 def lookup_onu(self, serial_number):
522 onu_id = None
523 for onu, s in self.onus.iteritems():
524 if s.vendor_id == serial_number.vendor_id:
525 str1 = self.stringify_vendor_specific(s.vendor_specific)
526 str2 = self.stringify_vendor_specific(serial_number.vendor_specific)
527 if str1 == str2:
528 onu_id = onu.onu_id
529 break
530 return onu_id
531
532 def update_flow_table(self, flows):
533 device = self.adapter_agent.get_device(self.device_id)
534 self.log.info('update flow table', flows=flows)
535
536 for flow in flows:
537 self.log.info('flow-details', device_id=self.device_id, flow=flow)
538 classifier_info = dict()
539 action_info = dict()
540 is_down_stream = None
541 _in_port = None
542 try:
543 _in_port = fd.get_in_port(flow)
544 assert _in_port is not None
545 # Right now there is only one NNI port. Get the NNI PORT and compare
546 # with IN_PUT port number. Need to find better way.
547 ports = self.adapter_agent.get_ports(device.id, Port.ETHERNET_NNI)
548
549 for port in ports:
550 if (port.port_no == _in_port):
551 self.log.info('downstream-flow')
552 is_down_stream = True
553 break
554 if is_down_stream is None:
555 is_down_stream = False
556 self.log.info('upstream-flow')
557
558 _out_port = fd.get_out_port(flow) # may be None
559 self.log.info('out-port', out_port=_out_port)
560
561 for field in fd.get_ofb_fields(flow):
562
563 if field.type == fd.ETH_TYPE:
564 classifier_info['eth_type'] = field.eth_type
565 self.log.info('field-type-eth-type',
566 eth_type=classifier_info['eth_type'])
567
568 elif field.type == fd.IP_PROTO:
569 classifier_info['ip_proto'] = field.ip_proto
570 self.log.info('field-type-ip-proto',
571 ip_proto=classifier_info['ip_proto'])
572
573 elif field.type == fd.IN_PORT:
574 classifier_info['in_port'] = field.port
575 self.log.info('field-type-in-port',
576 in_port=classifier_info['in_port'])
577
578 elif field.type == fd.VLAN_VID:
579 classifier_info['vlan_vid'] = field.vlan_vid & 0xfff
580 self.log.info('field-type-vlan-vid',
581 vlan=classifier_info['vlan_vid'])
582
583 elif field.type == fd.VLAN_PCP:
584 classifier_info['vlan_pcp'] = field.vlan_pcp
585 self.log.info('field-type-vlan-pcp',
586 pcp=classifier_info['vlan_pcp'])
587
588 elif field.type == fd.UDP_DST:
589 classifier_info['udp_dst'] = field.udp_dst
590 self.log.info('field-type-udp-dst',
591 udp_dst=classifier_info['udp_dst'])
592
593 elif field.type == fd.UDP_SRC:
594 classifier_info['udp_src'] = field.udp_src
595 self.log.info('field-type-udp-src',
596 udp_src=classifier_info['udp_src'])
597
598 elif field.type == fd.IPV4_DST:
599 classifier_info['ipv4_dst'] = field.ipv4_dst
600 self.log.info('field-type-ipv4-dst',
601 ipv4_dst=classifier_info['ipv4_dst'])
602
603 elif field.type == fd.IPV4_SRC:
604 classifier_info['ipv4_src'] = field.ipv4_src
605 self.log.info('field-type-ipv4-src',
606 ipv4_dst=classifier_info['ipv4_src'])
607
608 elif field.type == fd.METADATA:
609 classifier_info['metadata'] = field.table_metadata
610 self.log.info('field-type-metadata',
611 metadata=classifier_info['metadata'])
612
613 else:
614 raise NotImplementedError('field.type={}'.format(
615 field.type))
616
617 for action in fd.get_actions(flow):
618
619 if action.type == fd.OUTPUT:
620 action_info['output'] = action.output.port
621 self.log.info('action-type-output',
622 output=action_info['output'],
623 in_port=classifier_info['in_port'])
624
625 elif action.type == fd.POP_VLAN:
626 action_info['pop_vlan'] = True
627 self.log.info('action-type-pop-vlan',
628 in_port=_in_port)
629
630 elif action.type == fd.PUSH_VLAN:
631 action_info['push_vlan'] = True
632 action_info['tpid'] = action.push.ethertype
633 self.log.info('action-type-push-vlan',
634 push_tpid=action_info['tpid'],
635 in_port=_in_port)
636 if action.push.ethertype != 0x8100:
637 self.log.error('unhandled-tpid',
638 ethertype=action.push.ethertype)
639
640 elif action.type == fd.SET_FIELD:
641 # action_info['action_type'] = 'set_field'
642 _field = action.set_field.field.ofb_field
643 assert (action.set_field.field.oxm_class ==
644 OFPXMC_OPENFLOW_BASIC)
645 self.log.info('action-type-set-field',
646 field=_field, in_port=_in_port)
647 if _field.type == fd.VLAN_VID:
648 self.log.info('set-field-type-vlan-vid',
649 vlan_vid=_field.vlan_vid & 0xfff)
650 action_info['vlan_vid'] = (_field.vlan_vid & 0xfff)
651 else:
652 self.log.error('unsupported-action-set-field-type',
653 field_type=_field.type)
654 else:
655 self.log.error('unsupported-action-type',
656 action_type=action.type, in_port=_in_port)
657
Shad Ansari42db7342018-04-25 21:39:46 +0000658 # FIXME - Why ignore downstream flows?
Shad Ansari2825d012018-02-22 23:57:46 +0000659 if is_down_stream is False:
660 intf_id, onu_id = self.parse_port_no(classifier_info['in_port'])
661 self.divide_and_add_flow(onu_id, intf_id, classifier_info, action_info)
662 except Exception as e:
663 self.log.exception('failed-to-install-flow', e=e, flow=flow)
664
665 def parse_port_no(self, port_no):
666 return 0, 1 # FIXME
667
Shad Ansari42db7342018-04-25 21:39:46 +0000668 # FIXME - No need for divide_and_add_flow if
669 # both upstream and downstream flows
670 # are acted upon (not just upstream flows).
Shad Ansari2825d012018-02-22 23:57:46 +0000671 def divide_and_add_flow(self, onu_id, intf_id, classifier, action):
672 if 'ip_proto' in classifier:
673 if classifier['ip_proto'] == 17:
Shad Ansari8f1b2532018-04-21 07:51:39 +0000674 self.log.info('dhcp flow add')
675 self.add_dhcp_trap(classifier, action, onu_id, intf_id)
Shad Ansari2825d012018-02-22 23:57:46 +0000676 elif classifier['ip_proto'] == 2:
677 self.log.info('igmp flow add ignored')
678 else:
Shad Ansari8f1b2532018-04-21 07:51:39 +0000679 self.log.info("Invalid-Classifier-to-handle", classifier=classifier,
680 action=action)
Shad Ansari2825d012018-02-22 23:57:46 +0000681 elif 'eth_type' in classifier:
682 if classifier['eth_type'] == 0x888e:
683 self.log.error('epol flow add ignored')
684 elif 'push_vlan' in action:
685 self.add_data_flow(onu_id, intf_id, classifier, action)
686 else:
Shad Ansari8f1b2532018-04-21 07:51:39 +0000687 self.log.info('Invalid-flow-type-to-handle', classifier=classifier,
688 action=action)
Shad Ansari2825d012018-02-22 23:57:46 +0000689
690 def add_data_flow(self, onu_id, intf_id, uplink_classifier, uplink_action):
691
692 downlink_classifier = dict(uplink_classifier)
693 downlink_action = dict(uplink_action)
694
695 uplink_classifier['pkt_tag_type'] = 'single_tag'
696
697 downlink_classifier['pkt_tag_type'] = 'double_tag'
698 downlink_classifier['vlan_vid'] = uplink_action['vlan_vid']
699 downlink_classifier['metadata'] = uplink_classifier['vlan_vid']
700 del downlink_action['push_vlan']
701 downlink_action['pop_vlan'] = True
702
703 # To-Do right now only one GEM port is supported, so below method
704 # will take care of handling all the p bits.
705 # We need to revisit when mulitple gem port per p bits is needed.
706 self.add_hsia_flow(onu_id, intf_id, uplink_classifier, uplink_action,
Shad Ansari8f1b2532018-04-21 07:51:39 +0000707 downlink_classifier, downlink_action, ASFVOLT_HSIA_ID)
Shad Ansari2825d012018-02-22 23:57:46 +0000708
709 def mk_classifier(self, classifier_info):
710
711 classifier = openolt_pb2.Classifier()
712
713 if 'eth_type' in classifier_info:
714 classifier.eth_type = classifier_info['eth_type']
715 if 'ip_proto' in classifier_info:
716 classifier.ip_proto = classifier_info['ip_proto']
717 if 'vlan_vid' in classifier_info:
718 classifier.o_vid = classifier_info['vlan_vid']
719 if 'metadata' in classifier_info:
720 classifier.i_vid = classifier_info['metadata']
721 if 'vlan_pcp' in classifier_info:
722 classifier.o_pbits = classifier_info['vlan_pcp']
723 if 'udp_src' in classifier_info:
724 classifier.src_port = classifier_info['udp_src']
725 if 'udp_dst' in classifier_info:
726 classifier.dst_port = classifier_info['udp_dst']
727 if 'ipv4_dst' in classifier_info:
728 classifier.dst_ip = classifier_info['ipv4_dst']
729 if 'ipv4_src' in classifier_info:
730 classifier.src_ip = classifier_info['ipv4_src']
731 if 'pkt_tag_type' in classifier_info:
732 if classifier_info['pkt_tag_type'] == 'single_tag':
733 classifier.pkt_tag_type = 'single_tag'
734 elif classifier_info['pkt_tag_type'] == 'double_tag':
735 classifier.pkt_tag_type = 'double_tag'
736 elif classifier_info['pkt_tag_type'] == 'untagged':
737 classifier.pkt_tag_type = 'untagged'
738 else:
739 classifier.pkt_tag_type = 'none'
740
741 return classifier
742
743 def mk_action(self, action_info):
744 action = openolt_pb2.Action()
745
746 if 'pop_vlan' in action_info:
747 action.o_vid = action_info['vlan_vid']
748 action.cmd.remove_outer_tag = True
749 elif 'push_vlan' in action_info:
750 action.o_vid = action_info['vlan_vid']
751 action.cmd.add_outer_tag = True
752 elif 'trap_to_host' in action_info:
753 action.cmd.trap_to_host = True
754 else:
755 self.log.info('Invalid-action-field')
756 return
757 return action
758
759 def add_hsia_flow(self, onu_id, intf_id, uplink_classifier, uplink_action,
Shad Ansari8f1b2532018-04-21 07:51:39 +0000760 downlink_classifier, downlink_action, hsia_id):
Shad Ansari2825d012018-02-22 23:57:46 +0000761
762 gemport_id = self.mk_gemport_id(onu_id)
Shad Ansari2825d012018-02-22 23:57:46 +0000763 flow_id = self.mk_flow_id(onu_id, intf_id, hsia_id)
764
Shad Ansari8f1b2532018-04-21 07:51:39 +0000765 self.log.info('add upstream flow', onu_id=onu_id, classifier=uplink_classifier,
766 action=uplink_action, gemport_id=gemport_id, flow_id=flow_id)
Shad Ansari2825d012018-02-22 23:57:46 +0000767
768 flow = openolt_pb2.Flow(
Shad Ansari8f1b2532018-04-21 07:51:39 +0000769 onu_id=onu_id, flow_id=flow_id, flow_type="upstream",
770 gemport_id=gemport_id, classifier=self.mk_classifier(uplink_classifier),
771 action=self.mk_action(uplink_action))
772
Shad Ansari2825d012018-02-22 23:57:46 +0000773 self.stub.FlowAdd(flow)
774 time.sleep(0.1) # FIXME
775
Shad Ansari8f1b2532018-04-21 07:51:39 +0000776 self.log.info('add downstream flow', classifier=downlink_classifier,
777 action=downlink_action, gemport_id=gemport_id, flow_id=flow_id)
Shad Ansari2825d012018-02-22 23:57:46 +0000778
779 flow = openolt_pb2.Flow(
Shad Ansari8f1b2532018-04-21 07:51:39 +0000780 onu_id=onu_id, flow_id=flow_id, flow_type="downstream",
781 access_intf_id=intf_id, gemport_id=gemport_id,
782 classifier=self.mk_classifier(downlink_classifier),
783 action=self.mk_action(downlink_action))
784
Shad Ansari2825d012018-02-22 23:57:46 +0000785 self.stub.FlowAdd(flow)
786 time.sleep(0.1) # FIXME
787
Shad Ansari8f1b2532018-04-21 07:51:39 +0000788 def add_dhcp_trap(self, classifier, action, onu_id, intf_id):
789
790 self.log.info('add dhcp trap', classifier=classifier, action=action)
791
792 action.clear()
793 action['trap_to_host'] = True
794 classifier['pkt_tag_type'] = 'single_tag'
795 classifier.pop('vlan_vid', None)
796
797 gemport_id = self.mk_gemport_id(onu_id)
798 flow_id = self.mk_flow_id(onu_id, intf_id, ASFVOLT_DHCP_TAGGED_ID)
799
800 flow = openolt_pb2.Flow(
801 onu_id=onu_id, flow_id=flow_id, flow_type="upstream",
802 gemport_id=gemport_id, classifier=self.mk_classifier(classifier),
803 action=self.mk_action(action))
804
805 self.stub.FlowAdd(flow)
806
Shad Ansari2825d012018-02-22 23:57:46 +0000807 def mk_flow_id(self, onu_id, intf_id, id):
808 # Tp-Do Need to generate unique flow ID using
809 # OnuID, IntfId, id
810 # BAL accepts flow_id till 16384. So we are
811 # using only onu_id and id to generate flow ID.
812 return ((onu_id << 5) | id)