blob: b81460dc5317cf0a4fbd84dc4f63d9ca1b908596 [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 Knuth5f4163e2017-01-11 18:21:10 -0600425 ONU_VID_208=0xd0
426
Nathan Knuthd390ceb2017-01-07 15:38:58 -0800427 Clause = {v: k for k, v in ClauseSubtypeEnum.iteritems()}
428 Operator = {v: k for k, v in RuleOperatorEnum.iteritems()}
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800429
Zsolt Haraszti80175202016-12-24 00:17:51 -0800430 for flow in flows.items:
Zsolt Haraszti80175202016-12-24 00:17:51 -0800431
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800432 try:
433 in_port = get_in_port(flow)
434 assert in_port is not None
Zsolt Haraszti80175202016-12-24 00:17:51 -0800435
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800436 precedence = 255 - min(flow.priority / 256, 255)
Nathan Knuthe69ceb12017-01-04 21:13:39 -0800437
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800438 if in_port == 2:
439 log.info('#### Downstream Rule ####')
440 dn_req = NetworkToNetworkPortObject()
441 dn_req /= PortIngressRuleHeader(precedence=precedence)
Nathan Knuthe69ceb12017-01-04 21:13:39 -0800442
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800443 for field in get_ofb_fields(flow):
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800444
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800445 if field.type == ETH_TYPE:
446 _type = field.eth_type
447 log.info('#### field.type == ETH_TYPE ####')
448 dn_req /= PortIngressRuleClauseMatchLength02(
449 fieldcode=Clause['L2 Type/Len'],
450 operator=Operator['=='],
451 match=_type)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800452
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800453 elif field.type == IP_PROTO:
454 _proto = field.ip_proto
455 log.info('#### field.type == IP_PROTO ####')
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800456
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800457 elif field.type == IN_PORT:
458 _port = field.port
459 log.info('#### field.type == IN_PORT ####', port=_port)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800460
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800461 elif field.type == VLAN_VID:
462 _vlan_vid = field.vlan_vid & 0xfff
463 log.info('#### field.type == VLAN_VID ####', vlan=_vlan_vid)
464 dn_req /= PortIngressRuleClauseMatchLength02(fieldcode=Clause['C-VLAN Tag'], fieldinstance=0,
465 operator=Operator['=='], match=_vlan_vid)
466 if (_vlan_vid != 140):
467 dn_req /= PortIngressRuleClauseMatchLength02(fieldcode=Clause['C-VLAN Tag'], fieldinstance=1,
468 operator=Operator['=='], match=ONU_VID_208)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800469
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800470 elif field.type == VLAN_PCP:
471 _vlan_pcp = field.vlan_pcp
472 log.info('#### field.type == VLAN_PCP ####', pcp=_vlan_pcp)
Zsolt Haraszti85f12852016-12-24 08:30:58 -0800473
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800474 elif field.type == UDP_DST:
475 _udp_dst = field.udp_dst
476 log.info('#### field.type == UDP_DST ####')
Zsolt Haraszti6a5107c2017-01-09 23:42:41 -0800477
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800478 elif field.type == UDP_SRC:
479 _udp_src = field.udp_src
480 log.info('#### field.type == UDP_SRC ####')
Zsolt Haraszti80175202016-12-24 00:17:51 -0800481
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800482 elif field.type == IPV4_DST:
483 _ipv4_dst = field.ipv4_dst
484 log.info('#### field.type == IPV4_DST ####')
Zsolt Haraszti80175202016-12-24 00:17:51 -0800485
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800486 elif field.type == METADATA:
487 log.info('#### field.type == METADATA ####')
488 pass
Zsolt Haraszti80175202016-12-24 00:17:51 -0800489
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800490 else:
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800491 raise NotImplementedError('field.type={}'.format(
492 field.type))
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800493
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800494 for action in get_actions(flow):
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800495
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800496 if action.type == OUTPUT:
497 log.info('#### action.type == OUTPUT ####')
498 dn_req /= PortIngressRuleResultForward()
499 serial = ONU_VID_208 - 200
500 link = (0xe222 << 16) | (serial << 8)
501 dn_req /= PortIngressRuleResultOLTQueue(unicastvssn="TBIT",
502 unicastlink=link)
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800503
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800504 elif action.type == POP_VLAN:
505 log.info('#### action.type == POP_VLAN ####')
506 dn_req /= PortIngressRuleResultDelete(fieldcode=Clause['S-VLAN Tag'])
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800507
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800508 elif action.type == PUSH_VLAN:
509 log.info('#### action.type == PUSH_VLAN ####')
510 if action.push.ethertype != 0x8100:
511 log.error('unhandled-tpid',
512 ethertype=action.push.ethertype)
513 dn_req /= PortIngressRuleResultInsert(fieldcode=Clause['C-VLAN Tag'])
Nathan Knuthd8285e62017-01-11 14:18:43 -0600514
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800515 elif action.type == SET_FIELD:
516 log.info('#### action.type == SET_FIELD ####')
517 assert (action.set_field.field.oxm_class ==
518 ofp.OFPXMC_OPENFLOW_BASIC)
519 field = action.set_field.field.ofb_field
520 if field.type == VLAN_VID:
521 dn_req /= PortIngressRuleResultSet(
522 fieldcode=Clause['C-VLAN Tag'], value=field.vlan_vid & 0xfff)
523 else:
524 log.error('unsupported-action-set-field-type',
525 field_type=field.type)
Nathan Knuthd8285e62017-01-11 14:18:43 -0600526 else:
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800527 log.error('UNSUPPORTED-ACTION-TYPE',
528 action_type=action.type)
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800529
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800530 dn_req /= PortIngressRuleTerminator()
531 dn_req /= AddPortIngressRule()
532
533 msg = (
534 Ether(dst=device.mac_address) /
535 Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY) /
536 EOAMPayload(
537 body=CablelabsOUI() / DPoEOpcode_SetRequest() / dn_req)
538 )
539
540 self.io_port.send(str(msg))
541
542 elif in_port == 1:
543 # Upstream rule
544 log.info('#### Upstream Rule ####')
545
546 field_match_vlan_upstream_with_link = False
547 up_req_link = PortIngressRuleHeader(precedence=precedence)
548
549 up_req_pon = PonPortObject()
550 up_req_pon /= PortIngressRuleHeader(precedence=precedence)
551
552 for field in get_ofb_fields(flow):
553
554 if field.type == ETH_TYPE:
555 _type = field.eth_type
556 log.info('#### field.type == ETH_TYPE ####', in_port=in_port,
557 match=_type)
558 up_req_pon /= PortIngressRuleClauseMatchLength02(
559 fieldcode=Clause['L2 Type/Len'],
560 operator=Operator['=='],
561 match=_type)
562
563 up_req_link /= PortIngressRuleClauseMatchLength02(
564 fieldcode=Clause['L2 Type/Len'],
565 operator=Operator['=='],
566 match=_type)
567
568 elif field.type == IP_PROTO:
569 _proto = field.ip_proto
570 log.info('#### field.type == IP_PROTO ####', in_port=in_port,
571 ip_proto=_proto)
572
573 up_req_pon /= PortIngressRuleClauseMatchLength01(
574 fieldcode=Clause['IPv4/IPv6 Protocol Type'],
575 operator=Operator['=='], match=_proto)
576
577 up_req_link /= PortIngressRuleClauseMatchLength01(
578 fieldcode=Clause['IPv4/IPv6 Protocol Type'],
579 operator=Operator['=='], match=_proto)
580
581 elif field.type == IN_PORT:
582 _port = field.port
583 log.info('#### field.type == IN_PORT ####')
584
585 elif field.type == VLAN_VID:
586 _vlan_vid = field.vlan_vid & 0xfff
587 log.info('#### field.type == VLAN_VID ####')
588 up_req_pon /= PortIngressRuleClauseMatchLength02(
589 fieldcode=Clause['C-VLAN Tag'], fieldinstance=0,
590 operator=Operator['=='], match=_vlan_vid)
591
592 serial = _vlan_vid - 200
593 link = (0xe222 << 16) | (serial << 8)
594 up_req_link /= OLTUnicastLogicalLink(unicastvssn='TBIT', unicastlink=link)
595
596 up_req_link /= PortIngressRuleClauseMatchLength02(
597 fieldcode=Clause['C-VLAN Tag'], fieldinstance=0,
598 operator=Operator['=='], match=_vlan_vid)
599 field_match_vlan_upstream_with_link = True
600
601
602 elif field.type == VLAN_PCP:
603 _vlan_pcp = field.vlan_pcp
604 log.info('#### field.type == VLAN_PCP ####')
605
606 elif field.type == UDP_DST:
607 _udp_dst = field.udp_dst
608 log.info('#### field.type == UDP_DST ####')
609 up_req_pon /= (PortIngressRuleClauseMatchLength02(fieldcode=Clause['TCP/UDP source port'],
610 operator=Operator['=='], match=0x0044)/
611 PortIngressRuleClauseMatchLength02(fieldcode=Clause['TCP/UDP destination port'],
612 operator=Operator['=='], match=0x0043))
613
614 elif field.type == UDP_SRC:
615 _udp_src = field.udp_src
616 log.info('#### field.type == UDP_SRC ####')
617
618 else:
619 raise NotImplementedError('field.type={}'.format(
620 field.type))
621
622 for action in get_actions(flow):
623
624 if action.type == OUTPUT:
625 log.info('#### action.type == OUTPUT ####')
626 up_req_pon /= PortIngressRuleResultForward()
627 up_req_link /= PortIngressRuleResultForward()
628
629 elif action.type == POP_VLAN:
630 log.info('#### action.type == POP_VLAN ####')
631
632 elif action.type == PUSH_VLAN:
633 log.info('#### action.type == PUSH_VLAN ####')
634 if action.push.ethertype != 0x8100:
635 log.error('unhandled-ether-type',
636 ethertype=action.push.ethertype)
Nathan Knuthd8285e62017-01-11 14:18:43 -0600637 if field_match_vlan_upstream_with_link == True:
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800638 up_req_link /= PortIngressRuleResultInsert(fieldcode=Clause['C-VLAN Tag'],
639 fieldinstance=1)
640 else:
641 up_req_pon /= PortIngressRuleResultInsert(fieldcode=Clause['C-VLAN Tag'],
642 fieldinstance=0)
Nathan Knuthd8285e62017-01-11 14:18:43 -0600643
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800644 elif action.type == SET_FIELD:
645 log.info('#### action.type == SET_FIELD ####')
646 assert (action.set_field.field.oxm_class ==
647 ofp.OFPXMC_OPENFLOW_BASIC)
648 field = action.set_field.field.ofb_field
649 if field.type == VLAN_VID:
650 if field_match_vlan_upstream_with_link == True:
651 up_req_link /=(PortIngressRuleResultCopy(fieldcode=Clause['C-VLAN Tag'])/
652 PortIngressRuleResultReplace(fieldcode=Clause['C-VLAN Tag']))
653
654 up_req_pon /= PortIngressRuleResultSet(
655 fieldcode=Clause['C-VLAN Tag'], value=field.vlan_vid & 0xfff)
656 up_req_link /= PortIngressRuleResultSet(
657 fieldcode=Clause['C-VLAN Tag'], value=field.vlan_vid & 0xfff)
658 else:
659 log.error('unsupported-action-set-field-type',
660 field_type=field.type)
661
Zsolt Haraszti80175202016-12-24 00:17:51 -0800662 else:
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800663 log.error('UNSUPPORTED-ACTION-TYPE',
664 action_type=action.type)
Zsolt Haraszti80175202016-12-24 00:17:51 -0800665
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800666 if (field_match_vlan_upstream_with_link == True):
667 up_req = up_req_link
Zsolt Haraszti80175202016-12-24 00:17:51 -0800668 else:
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800669 up_req = up_req_pon
Zsolt Haraszti80175202016-12-24 00:17:51 -0800670
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800671 up_req /= PortIngressRuleTerminator()
672 up_req /= AddPortIngressRule()
673
674 msg = (
675 Ether(dst=device.mac_address) /
676 Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY) /
677 EOAMPayload(
678 body=CablelabsOUI() / DPoEOpcode_SetRequest() / up_req)
679 )
680
681 self.io_port.send(str(msg))
682
Nathan Knuthd8285e62017-01-11 14:18:43 -0600683 else:
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800684 raise Exception('Port should be 1 or 2 by our convention')
Nathan Knuthd8285e62017-01-11 14:18:43 -0600685
Nathan Knuth2976a3f2017-01-11 22:47:26 -0800686 except Exception, e:
687 log.exception('failed-to-install-flow', e=e, flow=flow)
Zsolt Harasztied091602016-12-08 13:36:38 -0800688
689 def update_flows_incrementally(self, device, flow_changes, group_changes):
690 raise NotImplementedError()
691
692 def send_proxied_message(self, proxy_address, msg):
Nathan Knuth6e57f332016-12-22 15:49:20 -0800693 log.info('send-proxied-message', proxy_address=proxy_address)
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800694 device = self.adapter_agent.get_device(proxy_address.device_id)
Nathan Knuthe69ceb12017-01-04 21:13:39 -0800695 frame = Ether(dst=device.mac_address) / \
Nathan Knuth6e57f332016-12-22 15:49:20 -0800696 Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY) / \
697 Dot1Q(vlan=proxy_address.channel_id, prio=TIBIT_MGMT_PRIORITY) / \
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800698 msg
Nathan Knuth6e57f332016-12-22 15:49:20 -0800699
Zsolt Haraszti348d1932016-12-10 01:10:07 -0800700 self.io_port.send(str(frame))
Zsolt Harasztied091602016-12-08 13:36:38 -0800701
702 def receive_proxied_message(self, proxy_address, msg):
703 raise NotImplementedError()
Zsolt Haraszti656ecc62016-12-28 15:08:23 -0800704
705 def receive_packet_out(self, logical_device_id, egress_port_no, msg):
706 log.info('packet-out', logical_device_id=logical_device_id,
707 egress_port_no=egress_port_no, msg_len=len(msg))
Nathan Knuthfe2b2e02017-01-06 07:29:02 -0800708
709 dev_id, logical_dev_id = self.vlan_to_device_ids[egress_port_no]
710 if logical_dev_id != logical_device_id:
711 raise Exception('Internal table mismatch')
712
713 tmp = Ether(msg)
714
715 frame = Ether(dst=tmp.dst, src=tmp.src) / \
716 Dot1Q(vlan=TIBIT_PACKET_OUT_VLAN) / \
717 Dot1Q(vlan=egress_port_no) / \
718 tmp.payload
719
720 self.io_port.send(str(frame))