blob: eb7e41505841208585b8b32c306e82da6ff9d387 [file] [log] [blame]
Zsolt Harasztied091602016-12-08 13:36:38 -08001#
Zsolt Haraszti3eb27a52017-01-03 21:56:48 -08002# Copyright 2017 the original author or authors.
Zsolt Harasztied091602016-12-08 13:36:38 -08003#
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
17"""
18Tibit OLT device adapter
19"""
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -080020import json
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -080021from uuid import uuid4
22
Zsolt Haraszti80175202016-12-24 00:17:51 -080023import structlog
24from scapy.fields import StrField
Zsolt Haraszti348d1932016-12-10 01:10:07 -080025from scapy.layers.l2 import Ether, Dot1Q
Zsolt Haraszti80175202016-12-24 00:17:51 -080026from scapy.packet import Packet, bind_layers
Zsolt Haraszti89a27302016-12-08 16:53:06 -080027from twisted.internet import reactor
Zsolt Haraszti80175202016-12-24 00:17:51 -080028from twisted.internet.defer import DeferredQueue, inlineCallbacks
Zsolt Harasztied091602016-12-08 13:36:38 -080029from zope.interface import implementer
30
Zsolt Haraszti313c4be2016-12-27 11:06:53 -080031from common.frameio.frameio import BpfProgramFilter, hexify
Zsolt Harasztied091602016-12-08 13:36:38 -080032from voltha.adapters.interface import IAdapterInterface
Nathan Knuth31c36962016-12-27 10:04:49 -080033from voltha.extensions.eoam.EOAM import EOAMPayload, DPoEOpcode_SetRequest
34from voltha.extensions.eoam.EOAM_TLV import DOLTObject, \
Nathan Knuthd8285e62017-01-11 14:18:43 -060035 NetworkToNetworkPortObject, OLTUnicastLogicalLink, \
36 PortIngressRuleClauseMatchLength01, AddStaticMacAddress, \
37 PortIngressRuleClauseMatchLength02, PortIngressRuleResultForward, \
38 PortIngressRuleResultSet, PortIngressRuleResultInsert, \
39 PortIngressRuleResultCopy, PortIngressRuleResultReplace, \
40 PortIngressRuleResultDelete, PortIngressRuleResultOLTQueue, \
41 PortIngressRuleTerminator, AddPortIngressRule, CablelabsOUI, PonPortObject
Nathan Knuth31c36962016-12-27 10:04:49 -080042from voltha.extensions.eoam.EOAM_TLV import PortIngressRuleHeader
Nathan Knuthd390ceb2017-01-07 15:38:58 -080043from voltha.extensions.eoam.EOAM_TLV import ClauseSubtypeEnum
44from voltha.extensions.eoam.EOAM_TLV import RuleOperatorEnum
Zsolt Haraszti80175202016-12-24 00:17:51 -080045from voltha.core.flow_decomposer import *
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -080046from voltha.core.logical_device_agent import mac_str_to_tuple
Zsolt Harasztied091602016-12-08 13:36:38 -080047from voltha.protos.adapter_pb2 import Adapter, AdapterConfig
Zsolt Haraszti80175202016-12-24 00:17:51 -080048from voltha.protos.common_pb2 import LogLevel, ConnectStatus
49from voltha.protos.common_pb2 import OperStatus, AdminState
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -080050from voltha.protos.device_pb2 import Device, Port
Zsolt Harasztied091602016-12-08 13:36:38 -080051from voltha.protos.device_pb2 import DeviceType, DeviceTypes
52from voltha.protos.health_pb2 import HealthStatus
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -080053from voltha.protos.logical_device_pb2 import LogicalDevice, LogicalPort
54from voltha.protos.openflow_13_pb2 import ofp_desc, ofp_port, OFPPF_10GB_FD, \
Nathan Knuthd8285e62017-01-11 14:18:43 -060055 OFPPF_FIBER, OFPPS_LIVE, ofp_switch_features, OFPC_PORT_STATS, \
56 OFPC_GROUP_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS
Zsolt Haraszti80175202016-12-24 00:17:51 -080057from voltha.registry import registry
Zsolt Haraszti348d1932016-12-10 01:10:07 -080058log = structlog.get_logger()
Zsolt Harasztied091602016-12-08 13:36:38 -080059
Nathan Knuth6e57f332016-12-22 15:49:20 -080060# Match on the MGMT VLAN, Priority 7
Zsolt Haraszti313c4be2016-12-27 11:06:53 -080061TIBIT_MGMT_VLAN = 4090
62TIBIT_MGMT_PRIORITY = 7
63frame_match_case1 = 'ether[14:2] = 0x{:01x}{:03x}'.format(
64 TIBIT_MGMT_PRIORITY << 1, TIBIT_MGMT_VLAN)
65
66TIBIT_PACKET_IN_VLAN = 4000
67frame_match_case2 = '(ether[14:2] & 0xfff) = 0x{:03x}'.format(
68 TIBIT_PACKET_IN_VLAN)
69
Nathan Knuthfe2b2e02017-01-06 07:29:02 -080070TIBIT_PACKET_OUT_VLAN = 4000
71
Zsolt Haraszti313c4be2016-12-27 11:06:53 -080072is_tibit_frame = BpfProgramFilter('{} or {}'.format(
73 frame_match_case1, frame_match_case2))
74
Nathan Knuth6e57f332016-12-22 15:49:20 -080075#is_tibit_frame = lambda x: True
Zsolt Haraszti89a27302016-12-08 16:53:06 -080076
Nathan Knuthfe2b2e02017-01-06 07:29:02 -080077# Extract OLT MAC address: This is a good
78# example of getting the OLT mac address
79
80#for mac, device in self.device_ids.iteritems():
81# if device == dev_id:
82# olt_mac_address = mac
83# log.info('packet-out', olt_mac_address=olt_mac_address)
Zsolt Haraszti85f12852016-12-24 08:30:58 -080084
Zsolt Haraszti348d1932016-12-10 01:10:07 -080085# To be removed in favor of OAM
Zsolt Haraszti89a27302016-12-08 16:53:06 -080086class TBJSON(Packet):
87 """ TBJSON 'packet' layer. """
88 name = "TBJSON"
89 fields_desc = [StrField("data", default="")]
90
Nathan Knuth6e57f332016-12-22 15:49:20 -080091bind_layers(Ether, TBJSON, type=0x9001)
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -080092
Nathan Knuthd8285e62017-01-11 14:18:43 -060093SUMITOMO_ELECTRIC_INDUSTRIES_OUI=u"0025DC"
Zsolt Haraszti85f12852016-12-24 08:30:58 -080094
Zsolt Harasztied091602016-12-08 13:36:38 -080095@implementer(IAdapterInterface)
96class TibitOltAdapter(object):
97
98 name = 'tibit_olt'
99
100 supported_device_types = [
101 DeviceType(
102 id='tibit_olt',
103 adapter=name,
104 accepts_bulk_flow_update=True
105 )
106 ]
107
108 def __init__(self, adapter_agent, config):
109 self.adapter_agent = adapter_agent
110 self.config = config
111 self.descriptor = Adapter(
112 id=self.name,
113 vendor='Tibit Communications Inc.',
114 version='0.1',
115 config=AdapterConfig(log_level=LogLevel.INFO)
116 )
Zsolt Harasztia17f3ec2016-12-08 14:55:49 -0800117 self.interface = registry('main').get_args().interface
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800118 self.io_port = None
Nathan Knuth6e57f332016-12-22 15:49:20 -0800119 self.incoming_queues = {} # OLT mac_address -> DeferredQueue()
120 self.device_ids = {} # OLT mac_address -> device_id
Zsolt Haraszti313c4be2016-12-27 11:06:53 -0800121 self.vlan_to_device_ids = {} # c-vid -> (device_id, logical_device_id)
Zsolt Harasztied091602016-12-08 13:36:38 -0800122
123 def start(self):
Zsolt Harasztia17f3ec2016-12-08 14:55:49 -0800124 log.debug('starting', interface=self.interface)
125 log.info('started', interface=self.interface)
Zsolt Harasztied091602016-12-08 13:36:38 -0800126
127 def stop(self):
128 log.debug('stopping')
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800129 if self.io_port is not None:
130 registry('frameio').del_interface(self.interface)
Zsolt Harasztied091602016-12-08 13:36:38 -0800131 log.info('stopped')
132
133 def adapter_descriptor(self):
134 return self.descriptor
135
136 def device_types(self):
137 return DeviceTypes(items=self.supported_device_types)
138
139 def health(self):
140 return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
141
142 def change_master_state(self, master):
143 raise NotImplementedError()
144
145 def adopt_device(self, device):
146 log.info('adopt-device', device=device)
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800147 self._activate_io_port()
148 reactor.callLater(0, self._launch_device_activation, device)
Zsolt Harasztied091602016-12-08 13:36:38 -0800149
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800150 def _activate_io_port(self):
151 if self.io_port is None:
152 self.io_port = registry('frameio').add_interface(
153 self.interface, self._rcv_io, is_tibit_frame)
154
155 @inlineCallbacks
156 def _launch_device_activation(self, device):
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800157 try:
158 log.debug('launch_dev_activation')
159 # prepare receive queue
160 self.incoming_queues[device.mac_address] = DeferredQueue(size=100)
161
Nathan Knuth6e57f332016-12-22 15:49:20 -0800162 # add mac_address to device_ids table
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800163 olt_mac = device.mac_address
Nathan Knuth6e57f332016-12-22 15:49:20 -0800164 self.device_ids[olt_mac] = device.id
165
166 # send out ping to OLT device
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800167 ping_frame = self._make_ping_frame(mac_address=olt_mac)
168 self.io_port.send(ping_frame)
169
170 # wait till we receive a response
Nathan Knuth6e57f332016-12-22 15:49:20 -0800171 ## TODO add timeout mechanism so we can signal if we cannot reach
172 ##device
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800173 while True:
174 response = yield self.incoming_queues[olt_mac].get()
175 # verify response and if not the expected response
176 if 1: # TODO check if it is really what we expect, and wait if not
177 break
178
179 except Exception, e:
180 log.exception('launch device failed', e=e)
181
182 # if we got response, we can fill out the device info, mark the device
183 # reachable
Zsolt Haraszti80175202016-12-24 00:17:51 -0800184 # jdev = json.loads(response.data[5:])
185 jdev = json.loads(response.payload.payload.body.load)
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800186 device.root = True
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -0800187 device.vendor = 'Tibit Communications, Inc.'
Nathan Knuth6e57f332016-12-22 15:49:20 -0800188 device.model = jdev.get('results', {}).get('device', 'DEVICE_UNKNOWN')
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -0800189 device.hardware_version = jdev['results']['datecode']
190 device.firmware_version = jdev['results']['firmware']
191 device.software_version = jdev['results']['modelversion']
192 device.serial_number = jdev['results']['manufacturer']
Nathan Knuth6e57f332016-12-22 15:49:20 -0800193
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800194 device.connect_status = ConnectStatus.REACHABLE
195 self.adapter_agent.update_device(device)
196
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -0800197 # then shortly after we create some ports for the device
198 log.info('create-port')
199 nni_port = Port(
200 port_no=2,
201 label='NNI facing Ethernet port',
202 type=Port.ETHERNET_NNI,
203 admin_state=AdminState.ENABLED,
204 oper_status=OperStatus.ACTIVE
205 )
206 self.adapter_agent.add_port(device.id, nni_port)
207 self.adapter_agent.add_port(device.id, Port(
208 port_no=1,
209 label='PON port',
210 type=Port.PON_OLT,
211 admin_state=AdminState.ENABLED,
212 oper_status=OperStatus.ACTIVE
213 ))
214
215 log.info('create-logical-device')
216 # then shortly after we create the logical device with one port
217 # that will correspond to the NNI port
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -0800218 ld = LogicalDevice(
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -0800219 desc=ofp_desc(
220 mfr_desc=device.vendor,
221 hw_desc=jdev['results']['device'],
222 sw_desc=jdev['results']['firmware'],
223 serial_num=uuid4().hex,
224 dp_desc='n/a'
225 ),
226 switch_features=ofp_switch_features(
227 n_buffers=256, # TODO fake for now
228 n_tables=2, # TODO ditto
229 capabilities=( # TODO and ditto
230 OFPC_FLOW_STATS
231 | OFPC_TABLE_STATS
232 | OFPC_PORT_STATS
233 | OFPC_GROUP_STATS
234 )
235 ),
236 root_device_id=device.id
237 )
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800238 ld_initialized = self.adapter_agent.create_logical_device(ld)
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -0800239 cap = OFPPF_10GB_FD | OFPPF_FIBER
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800240 self.adapter_agent.add_logical_port(ld_initialized.id, LogicalPort(
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -0800241 id='nni',
242 ofp_port=ofp_port(
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800243 port_no=0,
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -0800244 hw_addr=mac_str_to_tuple(device.mac_address),
245 name='nni',
246 config=0,
247 state=OFPPS_LIVE,
248 curr=cap,
249 advertised=cap,
250 peer=cap,
251 curr_speed=OFPPF_10GB_FD,
252 max_speed=OFPPF_10GB_FD
253 ),
254 device_id=device.id,
255 device_port_no=nni_port.port_no,
256 root_port=True
257 ))
258
259 # and finally update to active
260 device = self.adapter_agent.get_device(device.id)
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800261 device.parent_id = ld_initialized.id
Zsolt Harasztiaa4626e2016-12-08 16:53:06 -0800262 device.oper_status = OperStatus.ACTIVE
263 self.adapter_agent.update_device(device)
264
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800265 # Just transitioned to ACTIVE, wait a tenth of second
266 # before checking for ONUs
267 reactor.callLater(0.1, self._detect_onus, device)
268
269 @inlineCallbacks
270 def _detect_onus(self, device):
271 # send out get 'links' to the OLT device
272 olt_mac = device.mac_address
273 links_frame = self._make_links_frame(mac_address=olt_mac)
274 self.io_port.send(links_frame)
275 while True:
276 response = yield self.incoming_queues[olt_mac].get()
277 # verify response and if not the expected response
278 if 1: # TODO check if it is really what we expect, and wait if not
279 break
280
Zsolt Haraszti80175202016-12-24 00:17:51 -0800281 jdev = json.loads(response.payload.payload.body.load)
Nathan Knuthd8285e62017-01-11 14:18:43 -0600282 onu_mac = ''
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800283 for macid in jdev['results']:
284 if macid['macid'] is None:
285 log.info('MAC ID is NONE %s' % str(macid['macid']))
Nathan Knuthd8285e62017-01-11 14:18:43 -0600286 elif macid['macid'][:6].upper() == SUMITOMO_ELECTRIC_INDUSTRIES_OUI:
287 onu_mac = macid['macid']
288 log.info('SUMITOMO mac address %s' % str(macid['macid']))
289 log.info('activate-olt-for-onu-%s' % onu_mac)
290 # Convert from string to colon separated form
291 onu_mac = ':'.join(s.encode('hex') for s in onu_mac.decode('hex'))
292 vlan_id = self._olt_side_onu_activation(int(macid['macid'][-4:-2], 16))
293 self.adapter_agent.child_device_detected(
294 parent_device_id=device.id,
295 parent_port_no=1,
296 child_device_type='dpoe_onu',
297 mac_address = onu_mac,
298 proxy_address=Device.ProxyAddress(
299 device_id=device.id,
300 channel_id=vlan_id
301 ),
302 vlan=vlan_id
303 )
304
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800305 else:
Nathan Knuthd8285e62017-01-11 14:18:43 -0600306 onu_mac = '000c' + macid.get('macid', 'e2000000')[4:]
307 log.info('activate-olt-for-onu-%s' % onu_mac)
308 # Convert from string to colon separated form
309 onu_mac = ':'.join(s.encode('hex') for s in onu_mac.decode('hex'))
310 vlan_id = self._olt_side_onu_activation(int(macid['macid'][-4:-2], 16))
311 self.adapter_agent.child_device_detected(
312 parent_device_id=device.id,
313 parent_port_no=1,
314 child_device_type='tibit_onu',
315 mac_address = onu_mac,
316 proxy_address=Device.ProxyAddress(
317 device_id=device.id,
318 channel_id=vlan_id
319 ),
320 vlan=vlan_id
321 )
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800322
Zsolt Haraszti313c4be2016-12-27 11:06:53 -0800323 # also record the vlan_id -> (device_id, logical_device_id) for
324 # later use
325 self.vlan_to_device_ids[vlan_id] = (device.id, device.parent_id)
326
Nathan Knuth6e57f332016-12-22 15:49:20 -0800327 def _olt_side_onu_activation(self, serial):
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800328 """
329 This is where if this was a real OLT, the OLT-side activation for
330 the new ONU should be performed. By the time we return, the OLT shall
331 be able to provide tunneled (proxy) communication to the given ONU,
332 using the returned information.
333 """
Nathan Knuth6e57f332016-12-22 15:49:20 -0800334 vlan_id = serial + 200
Zsolt Haraszti313c4be2016-12-27 11:06:53 -0800335 return vlan_id
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800336
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800337 def _rcv_io(self, port, frame):
338
Zsolt Haraszti313c4be2016-12-27 11:06:53 -0800339 log.info('frame-received', frame=hexify(frame))
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800340
Zsolt Haraszti4ef0a9a2016-12-20 01:35:48 -0800341 # make into frame to extract source mac
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800342 response = Ether(frame)
343
Nathan Knuth6e57f332016-12-22 15:49:20 -0800344 if response.haslayer(Dot1Q):
Nathan Knuth6e57f332016-12-22 15:49:20 -0800345
Zsolt Haraszti313c4be2016-12-27 11:06:53 -0800346 # All OAM responses from the OLT should have a TIBIT_MGMT_VLAN.
347 # Responses from the ONUs should have a TIBIT_MGMT_VLAN followed by a ONU CTAG
348 # All packet-in frames will have the TIBIT_PACKET_IN_VLAN.
349 if response.getlayer(Dot1Q).type == 0x8100:
350
351 if response.getlayer(Dot1Q).vlan == TIBIT_PACKET_IN_VLAN:
352
353 inner_tag_and_rest = response.payload.payload
354
Zsolt Haraszti313c4be2016-12-27 11:06:53 -0800355 if isinstance(inner_tag_and_rest, Dot1Q):
356
357 cvid = inner_tag_and_rest.vlan
358
359 frame = Ether(src=response.src,
360 dst=response.dst,
361 type=inner_tag_and_rest.type) /\
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800362 inner_tag_and_rest.payload
Zsolt Haraszti313c4be2016-12-27 11:06:53 -0800363
364 _, logical_device_id = self.vlan_to_device_ids.get(cvid)
365 if logical_device_id is None:
366 log.error('invalid-cvid', cvid=cvid)
367 else:
368 self.adapter_agent.send_packet_in(
369 logical_device_id=logical_device_id,
370 logical_port_no=cvid, # C-VID encodes port no
371 packet=str(frame))
372
373 else:
374 log.error('packet-in-single-tagged',
375 frame=hexify(response))
376
377 else:
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800378 ## Mgmt responses received from the ONU
Zsolt Haraszti313c4be2016-12-27 11:06:53 -0800379 ## Since the type of the first layer is 0x8100,
380 ## then the frame must have an inner tag layer
381 olt_mac = response.src
382 device_id = self.device_ids[olt_mac]
383 channel_id = response[Dot1Q:2].vlan
384 log.info('received_channel_id', channel_id=channel_id,
385 device_id=device_id)
386
387 proxy_address=Device.ProxyAddress(
388 device_id=device_id,
389 channel_id=channel_id
390 )
391 # pop dot1q header(s)
392 msg = response.payload.payload
393 self.adapter_agent.receive_proxied_message(proxy_address, msg)
394
Nathan Knuth6e57f332016-12-22 15:49:20 -0800395 else:
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800396 ## Mgmt responses received from the OLT
Nathan Knuth6e57f332016-12-22 15:49:20 -0800397 ## enqueue incoming parsed frame to right device
Zsolt Haraszti313c4be2016-12-27 11:06:53 -0800398 log.info('received-dot1q-not-8100')
Nathan Knuth6e57f332016-12-22 15:49:20 -0800399 self.incoming_queues[response.src].put(response)
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800400
401 def _make_ping_frame(self, mac_address):
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800402 # Create a json packet
403 json_operation_str = '{\"operation\":\"version\"}'
Nathan Knuth6e57f332016-12-22 15:49:20 -0800404 frame = Ether(dst=mac_address)/Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY)/TBJSON(data='json %s' % json_operation_str)
Zsolt Haraszti89a27302016-12-08 16:53:06 -0800405 return str(frame)
406
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800407 def _make_links_frame(self, mac_address):
408 # Create a json packet
409 json_operation_str = '{\"operation\":\"links\"}'
Nathan Knuth6e57f332016-12-22 15:49:20 -0800410 frame = Ether(dst=mac_address)/Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY)/TBJSON(data='json %s' % json_operation_str)
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800411 return str(frame)
412
Zsolt Harasztied091602016-12-08 13:36:38 -0800413 def abandon_device(self, device):
414 raise NotImplementedError(0
415 )
416 def deactivate_device(self, device):
417 raise NotImplementedError()
418
419 def update_flows_bulk(self, device, flows, groups):
Nathan Knuthe69ceb12017-01-04 21:13:39 -0800420 log.info('########################################')
Zsolt Haraszti80175202016-12-24 00:17:51 -0800421 log.info('bulk-flow-update', device_id=device.id,
422 flows=flows, groups=groups)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800423 assert len(groups.items) == 0, "Cannot yet deal with groups"
424
Nathan Knuthf840dfb2017-01-12 18:15:14 -0800425 # extract ONU VID
426 vid_from_device_id = {v[0]: k for k,v in self.vlan_to_device_ids.iteritems()}
427 ONU_VID = vid_from_device_id[device.id]
Nathan Knuth5f4163e2017-01-11 18:21:10 -0600428
Nathan Knuthd390ceb2017-01-07 15:38:58 -0800429 Clause = {v: k for k, v in ClauseSubtypeEnum.iteritems()}
430 Operator = {v: k for k, v in RuleOperatorEnum.iteritems()}
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800431
Zsolt Haraszti80175202016-12-24 00:17:51 -0800432 for flow in flows.items:
Zsolt Haraszti80175202016-12-24 00:17:51 -0800433
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800434 try:
435 in_port = get_in_port(flow)
436 assert in_port is not None
Zsolt Haraszti80175202016-12-24 00:17:51 -0800437
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800438 precedence = 255 - min(flow.priority / 256, 255)
Nathan Knuthe69ceb12017-01-04 21:13:39 -0800439
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800440 if in_port == 2:
441 log.info('#### Downstream Rule ####')
442 dn_req = NetworkToNetworkPortObject()
443 dn_req /= PortIngressRuleHeader(precedence=precedence)
Nathan Knuthe69ceb12017-01-04 21:13:39 -0800444
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800445 for field in get_ofb_fields(flow):
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800446
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800447 if field.type == ETH_TYPE:
448 _type = field.eth_type
449 log.info('#### field.type == ETH_TYPE ####')
450 dn_req /= PortIngressRuleClauseMatchLength02(
451 fieldcode=Clause['L2 Type/Len'],
452 operator=Operator['=='],
453 match=_type)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800454
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800455 elif field.type == IP_PROTO:
456 _proto = field.ip_proto
457 log.info('#### field.type == IP_PROTO ####')
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800458
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800459 elif field.type == IN_PORT:
460 _port = field.port
461 log.info('#### field.type == IN_PORT ####', port=_port)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800462
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800463 elif field.type == VLAN_VID:
464 _vlan_vid = field.vlan_vid & 0xfff
465 log.info('#### field.type == VLAN_VID ####', vlan=_vlan_vid)
466 dn_req /= PortIngressRuleClauseMatchLength02(fieldcode=Clause['C-VLAN Tag'], fieldinstance=0,
467 operator=Operator['=='], match=_vlan_vid)
468 if (_vlan_vid != 140):
469 dn_req /= PortIngressRuleClauseMatchLength02(fieldcode=Clause['C-VLAN Tag'], fieldinstance=1,
Nathan Knuthf840dfb2017-01-12 18:15:14 -0800470 operator=Operator['=='], match=ONU_VID)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800471
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800472 elif field.type == VLAN_PCP:
473 _vlan_pcp = field.vlan_pcp
474 log.info('#### field.type == VLAN_PCP ####', pcp=_vlan_pcp)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800475
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800476 elif field.type == UDP_DST:
477 _udp_dst = field.udp_dst
478 log.info('#### field.type == UDP_DST ####')
Zsolt Haraszti6a5107c2017-01-09 23:42:41 -0800479
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800480 elif field.type == UDP_SRC:
481 _udp_src = field.udp_src
482 log.info('#### field.type == UDP_SRC ####')
Zsolt Haraszti80175202016-12-24 00:17:51 -0800483
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800484 elif field.type == IPV4_DST:
485 _ipv4_dst = field.ipv4_dst
486 log.info('#### field.type == IPV4_DST ####')
Zsolt Haraszti80175202016-12-24 00:17:51 -0800487
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800488 elif field.type == METADATA:
489 log.info('#### field.type == METADATA ####')
490 pass
Zsolt Haraszti80175202016-12-24 00:17:51 -0800491
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800492 else:
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800493 raise NotImplementedError('field.type={}'.format(
494 field.type))
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800495
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800496 for action in get_actions(flow):
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800497
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800498 if action.type == OUTPUT:
499 log.info('#### action.type == OUTPUT ####')
500 dn_req /= PortIngressRuleResultForward()
Nathan Knuthf840dfb2017-01-12 18:15:14 -0800501 serial = ONU_VID - 200
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800502 link = (0xe222 << 16) | (serial << 8)
503 dn_req /= PortIngressRuleResultOLTQueue(unicastvssn="TBIT",
504 unicastlink=link)
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800505
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800506 elif action.type == POP_VLAN:
507 log.info('#### action.type == POP_VLAN ####')
508 dn_req /= PortIngressRuleResultDelete(fieldcode=Clause['S-VLAN Tag'])
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800509
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800510 elif action.type == PUSH_VLAN:
511 log.info('#### action.type == PUSH_VLAN ####')
512 if action.push.ethertype != 0x8100:
513 log.error('unhandled-tpid',
514 ethertype=action.push.ethertype)
515 dn_req /= PortIngressRuleResultInsert(fieldcode=Clause['C-VLAN Tag'])
Nathan Knuthd8285e62017-01-11 14:18:43 -0600516
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800517 elif action.type == SET_FIELD:
518 log.info('#### action.type == SET_FIELD ####')
519 assert (action.set_field.field.oxm_class ==
520 ofp.OFPXMC_OPENFLOW_BASIC)
521 field = action.set_field.field.ofb_field
522 if field.type == VLAN_VID:
523 dn_req /= PortIngressRuleResultSet(
524 fieldcode=Clause['C-VLAN Tag'], value=field.vlan_vid & 0xfff)
525 else:
526 log.error('unsupported-action-set-field-type',
527 field_type=field.type)
Nathan Knuthd8285e62017-01-11 14:18:43 -0600528 else:
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800529 log.error('UNSUPPORTED-ACTION-TYPE',
530 action_type=action.type)
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800531
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800532 dn_req /= PortIngressRuleTerminator()
533 dn_req /= AddPortIngressRule()
534
535 msg = (
536 Ether(dst=device.mac_address) /
537 Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY) /
538 EOAMPayload(
539 body=CablelabsOUI() / DPoEOpcode_SetRequest() / dn_req)
540 )
541
542 self.io_port.send(str(msg))
543
544 elif in_port == 1:
545 # Upstream rule
546 log.info('#### Upstream Rule ####')
547
548 field_match_vlan_upstream_with_link = False
549 up_req_link = PortIngressRuleHeader(precedence=precedence)
550
551 up_req_pon = PonPortObject()
552 up_req_pon /= PortIngressRuleHeader(precedence=precedence)
553
554 for field in get_ofb_fields(flow):
555
556 if field.type == ETH_TYPE:
557 _type = field.eth_type
558 log.info('#### field.type == ETH_TYPE ####', in_port=in_port,
559 match=_type)
560 up_req_pon /= PortIngressRuleClauseMatchLength02(
561 fieldcode=Clause['L2 Type/Len'],
562 operator=Operator['=='],
563 match=_type)
564
565 up_req_link /= PortIngressRuleClauseMatchLength02(
566 fieldcode=Clause['L2 Type/Len'],
567 operator=Operator['=='],
568 match=_type)
569
570 elif field.type == IP_PROTO:
571 _proto = field.ip_proto
572 log.info('#### field.type == IP_PROTO ####', in_port=in_port,
573 ip_proto=_proto)
574
575 up_req_pon /= PortIngressRuleClauseMatchLength01(
576 fieldcode=Clause['IPv4/IPv6 Protocol Type'],
577 operator=Operator['=='], match=_proto)
578
579 up_req_link /= PortIngressRuleClauseMatchLength01(
580 fieldcode=Clause['IPv4/IPv6 Protocol Type'],
581 operator=Operator['=='], match=_proto)
582
583 elif field.type == IN_PORT:
584 _port = field.port
585 log.info('#### field.type == IN_PORT ####')
586
587 elif field.type == VLAN_VID:
588 _vlan_vid = field.vlan_vid & 0xfff
589 log.info('#### field.type == VLAN_VID ####')
590 up_req_pon /= PortIngressRuleClauseMatchLength02(
591 fieldcode=Clause['C-VLAN Tag'], fieldinstance=0,
592 operator=Operator['=='], match=_vlan_vid)
593
594 serial = _vlan_vid - 200
595 link = (0xe222 << 16) | (serial << 8)
596 up_req_link /= OLTUnicastLogicalLink(unicastvssn='TBIT', unicastlink=link)
597
598 up_req_link /= PortIngressRuleClauseMatchLength02(
599 fieldcode=Clause['C-VLAN Tag'], fieldinstance=0,
600 operator=Operator['=='], match=_vlan_vid)
601 field_match_vlan_upstream_with_link = True
602
603
604 elif field.type == VLAN_PCP:
605 _vlan_pcp = field.vlan_pcp
606 log.info('#### field.type == VLAN_PCP ####')
607
608 elif field.type == UDP_DST:
609 _udp_dst = field.udp_dst
610 log.info('#### field.type == UDP_DST ####')
611 up_req_pon /= (PortIngressRuleClauseMatchLength02(fieldcode=Clause['TCP/UDP source port'],
612 operator=Operator['=='], match=0x0044)/
613 PortIngressRuleClauseMatchLength02(fieldcode=Clause['TCP/UDP destination port'],
614 operator=Operator['=='], match=0x0043))
615
616 elif field.type == UDP_SRC:
617 _udp_src = field.udp_src
618 log.info('#### field.type == UDP_SRC ####')
619
620 else:
621 raise NotImplementedError('field.type={}'.format(
622 field.type))
623
624 for action in get_actions(flow):
625
626 if action.type == OUTPUT:
627 log.info('#### action.type == OUTPUT ####')
628 up_req_pon /= PortIngressRuleResultForward()
629 up_req_link /= PortIngressRuleResultForward()
630
631 elif action.type == POP_VLAN:
632 log.info('#### action.type == POP_VLAN ####')
633
634 elif action.type == PUSH_VLAN:
635 log.info('#### action.type == PUSH_VLAN ####')
636 if action.push.ethertype != 0x8100:
637 log.error('unhandled-ether-type',
638 ethertype=action.push.ethertype)
Nathan Knuthd8285e62017-01-11 14:18:43 -0600639 if field_match_vlan_upstream_with_link == True:
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800640 up_req_link /= PortIngressRuleResultInsert(fieldcode=Clause['C-VLAN Tag'],
641 fieldinstance=1)
642 else:
643 up_req_pon /= PortIngressRuleResultInsert(fieldcode=Clause['C-VLAN Tag'],
644 fieldinstance=0)
Nathan Knuthd8285e62017-01-11 14:18:43 -0600645
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800646 elif action.type == SET_FIELD:
647 log.info('#### action.type == SET_FIELD ####')
648 assert (action.set_field.field.oxm_class ==
649 ofp.OFPXMC_OPENFLOW_BASIC)
650 field = action.set_field.field.ofb_field
651 if field.type == VLAN_VID:
652 if field_match_vlan_upstream_with_link == True:
653 up_req_link /=(PortIngressRuleResultCopy(fieldcode=Clause['C-VLAN Tag'])/
654 PortIngressRuleResultReplace(fieldcode=Clause['C-VLAN Tag']))
655
656 up_req_pon /= PortIngressRuleResultSet(
657 fieldcode=Clause['C-VLAN Tag'], value=field.vlan_vid & 0xfff)
658 up_req_link /= PortIngressRuleResultSet(
659 fieldcode=Clause['C-VLAN Tag'], value=field.vlan_vid & 0xfff)
660 else:
661 log.error('unsupported-action-set-field-type',
662 field_type=field.type)
663
Zsolt Haraszti80175202016-12-24 00:17:51 -0800664 else:
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800665 log.error('UNSUPPORTED-ACTION-TYPE',
666 action_type=action.type)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800667
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800668 if (field_match_vlan_upstream_with_link == True):
669 up_req = up_req_link
Zsolt Haraszti80175202016-12-24 00:17:51 -0800670 else:
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800671 up_req = up_req_pon
Zsolt Haraszti80175202016-12-24 00:17:51 -0800672
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800673 up_req /= PortIngressRuleTerminator()
674 up_req /= AddPortIngressRule()
675
676 msg = (
677 Ether(dst=device.mac_address) /
678 Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY) /
679 EOAMPayload(
680 body=CablelabsOUI() / DPoEOpcode_SetRequest() / up_req)
681 )
682
683 self.io_port.send(str(msg))
684
Nathan Knuthd8285e62017-01-11 14:18:43 -0600685 else:
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800686 raise Exception('Port should be 1 or 2 by our convention')
Nathan Knuthd8285e62017-01-11 14:18:43 -0600687
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800688 except Exception, e:
689 log.exception('failed-to-install-flow', e=e, flow=flow)
Zsolt Harasztied091602016-12-08 13:36:38 -0800690
691 def update_flows_incrementally(self, device, flow_changes, group_changes):
692 raise NotImplementedError()
693
694 def send_proxied_message(self, proxy_address, msg):
Nathan Knuth6e57f332016-12-22 15:49:20 -0800695 log.info('send-proxied-message', proxy_address=proxy_address)
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800696 device = self.adapter_agent.get_device(proxy_address.device_id)
Nathan Knuthe69ceb12017-01-04 21:13:39 -0800697 frame = Ether(dst=device.mac_address) / \
Nathan Knuth6e57f332016-12-22 15:49:20 -0800698 Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY) / \
699 Dot1Q(vlan=proxy_address.channel_id, prio=TIBIT_MGMT_PRIORITY) / \
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800700 msg
Nathan Knuth6e57f332016-12-22 15:49:20 -0800701
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800702 self.io_port.send(str(frame))
Zsolt Harasztied091602016-12-08 13:36:38 -0800703
704 def receive_proxied_message(self, proxy_address, msg):
705 raise NotImplementedError()
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800706
707 def receive_packet_out(self, logical_device_id, egress_port_no, msg):
708 log.info('packet-out', logical_device_id=logical_device_id,
709 egress_port_no=egress_port_no, msg_len=len(msg))
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800710
711 dev_id, logical_dev_id = self.vlan_to_device_ids[egress_port_no]
712 if logical_dev_id != logical_device_id:
713 raise Exception('Internal table mismatch')
714
715 tmp = Ether(msg)
716
717 frame = Ether(dst=tmp.dst, src=tmp.src) / \
718 Dot1Q(vlan=TIBIT_PACKET_OUT_VLAN) / \
719 Dot1Q(vlan=egress_port_no) / \
720 tmp.payload
721
722 self.io_port.send(str(frame))