blob: c840efcdcb3c359e4a3690fada5f9fcd9b56d412 [file] [log] [blame]
Daniele Moro94660a02019-12-02 12:02:07 -08001/*
2 * Copyright 2019-present Open Networking Foundation
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
Daniele Moroad7057d2020-01-15 10:54:37 -080017package org.opencord.bng.impl;
Daniele Moro94660a02019-12-02 12:02:07 -080018
Daniele Moro59903172020-02-20 19:23:11 -080019import com.google.common.cache.CacheBuilder;
20import com.google.common.cache.CacheLoader;
21import com.google.common.cache.LoadingCache;
Daniele Moro94660a02019-12-02 12:02:07 -080022import com.google.common.collect.Maps;
Daniele Moro59903172020-02-20 19:23:11 -080023import org.apache.commons.lang3.tuple.ImmutableTriple;
Daniele Moro94660a02019-12-02 12:02:07 -080024import org.onlab.packet.Data;
25import org.onlab.packet.DeserializationException;
26import org.onlab.packet.Ethernet;
27import org.onlab.packet.IpAddress;
28import org.onlab.packet.MacAddress;
29import org.onlab.packet.VlanId;
30import org.onlab.util.ItemNotFoundException;
Daniele Moro59903172020-02-20 19:23:11 -080031import org.onlab.util.SharedExecutors;
Daniele Moro94660a02019-12-02 12:02:07 -080032import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreService;
34import org.onosproject.event.AbstractListenerManager;
35import org.onosproject.net.ConnectPoint;
Daniele Moro59903172020-02-20 19:23:11 -080036import org.onosproject.net.Port;
Daniele Moro94660a02019-12-02 12:02:07 -080037import org.onosproject.net.config.ConfigFactory;
38import org.onosproject.net.config.NetworkConfigEvent;
39import org.onosproject.net.config.NetworkConfigListener;
40import org.onosproject.net.config.NetworkConfigRegistry;
41import org.onosproject.net.device.DeviceService;
42import org.onosproject.net.driver.DriverService;
43import org.onosproject.net.flow.DefaultTrafficTreatment;
44import org.onosproject.net.flow.TrafficTreatment;
45import org.onosproject.net.intf.Interface;
46import org.onosproject.net.intf.InterfaceService;
47import org.onosproject.net.link.LinkService;
48import org.onosproject.net.packet.DefaultOutboundPacket;
49import org.onosproject.net.packet.OutboundPacket;
50import org.onosproject.net.packet.PacketContext;
51import org.onosproject.net.packet.PacketProcessor;
52import org.onosproject.net.packet.PacketService;
Daniele Moroad7057d2020-01-15 10:54:37 -080053import org.opencord.bng.BngAttachment;
54import org.opencord.bng.PppoeBngAttachment;
55import org.opencord.bng.PppoeBngControlHandler;
56import org.opencord.bng.PppoeEvent;
57import org.opencord.bng.PppoeEventListener;
58import org.opencord.bng.PppoeEventSubject;
Daniele Moro94660a02019-12-02 12:02:07 -080059import org.opencord.bng.config.PppoeRelayConfig;
60import org.opencord.bng.packets.GenericPpp;
61import org.opencord.bng.packets.Ipcp;
62import org.opencord.bng.packets.PppProtocolType;
63import org.opencord.bng.packets.Pppoe;
64import org.opencord.sadis.SadisService;
65import org.osgi.service.component.annotations.Activate;
66import org.osgi.service.component.annotations.Component;
67import org.osgi.service.component.annotations.Deactivate;
68import org.osgi.service.component.annotations.Reference;
69import org.osgi.service.component.annotations.ReferenceCardinality;
70import org.slf4j.Logger;
71import org.slf4j.LoggerFactory;
72
73import java.nio.ByteBuffer;
74import java.util.Map;
75import java.util.Objects;
76import java.util.Optional;
77import java.util.Set;
Daniele Moro59903172020-02-20 19:23:11 -080078import java.util.concurrent.ExecutionException;
79import java.util.concurrent.TimeUnit;
Daniele Moro94660a02019-12-02 12:02:07 -080080import java.util.stream.Collectors;
81
82import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
83
Daniele Moroad7057d2020-01-15 10:54:37 -080084@Component(immediate = true)
Daniele Moro94660a02019-12-02 12:02:07 -080085public class PppoeHandlerRelay
86 extends AbstractListenerManager<PppoeEvent, PppoeEventListener>
87 implements PppoeBngControlHandler {
88
89 private static final IpAddress IP_ADDRESS_ZERO = IpAddress.valueOf(0);
90
91 private final Logger log = LoggerFactory.getLogger(getClass());
92 private final InternalConfigListener cfgListener = new InternalConfigListener();
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY)
95 protected NetworkConfigRegistry cfgService;
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY)
98 protected InterfaceService interfaceService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY)
101 protected CoreService coreService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY)
104 protected PacketService packetService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY)
107 protected SadisService sadisService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY)
110 protected DeviceService deviceService;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY)
113 protected LinkService linkService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY)
116 protected DriverService driverService;
117
118 private ConfigFactory<ApplicationId, PppoeRelayConfig> cfgFactory = new ConfigFactory<>(
119 APP_SUBJECT_FACTORY,
120 PppoeRelayConfig.class,
121 PppoeRelayConfig.KEY) {
122 @Override
123 public PppoeRelayConfig createConfig() {
124 return new PppoeRelayConfig();
125 }
126 };
127 private ApplicationId appId;
128 private InternalPacketProcessor internalPacketProcessor;
129 private PppoeRelayConfig pppoeRelayConfig;
Daniele Morof8a28ce2019-12-12 09:26:25 -0800130 private MacAddress macPppoeServer;
Daniele Moro94660a02019-12-02 12:02:07 -0800131
132 /**
133 * Ephemeral internal map to trace the attachment information. This map is
134 * mainly used to modify the packet towards the PPPoE server or towards the
135 * attachment. FIXME: this map should be cleaned after some time.
136 */
137 private Map<MacAddress, BngAttachment> mapSrcMacToAttInfo;
138
Daniele Moro59903172020-02-20 19:23:11 -0800139 /**
140 * Cache to cache Sadis results during PPPoE connection establishment.
141 */
142 private final LoadingCache<ImmutableTriple<VlanId, VlanId, ConnectPoint>, ConnectPoint>
143 oltCpCache = CacheBuilder.newBuilder()
144 .expireAfterWrite(30, TimeUnit.SECONDS)
145 .build(new CacheLoader<>() {
146 @Override
147 public ConnectPoint load(ImmutableTriple<VlanId, VlanId, ConnectPoint> key) throws Exception {
148 return getOltConnectPoint(key.left, key.middle, key.right).orElseThrow();
149 }
150 });
151
Daniele Moro94660a02019-12-02 12:02:07 -0800152 @Activate
153 protected void activate() {
Daniele Moro59903172020-02-20 19:23:11 -0800154 mapSrcMacToAttInfo = Maps.newConcurrentMap();
Daniele Moro94660a02019-12-02 12:02:07 -0800155 appId = coreService.getAppId(BngManager.BNG_APP);
156 cfgService.addListener(cfgListener);
157 cfgService.registerConfigFactory(cfgFactory);
158
159 eventDispatcher.addSink(PppoeEvent.class, listenerRegistry);
160
161 updateConfig();
162
163 internalPacketProcessor = new InternalPacketProcessor();
164 packetService.addProcessor(internalPacketProcessor, PacketProcessor.director(0));
165
166 log.info("PPPoE Handler Relay activated");
167 }
168
169 @Deactivate
170 protected void deactivate() {
171 eventDispatcher.removeSink(PppoeEvent.class);
172 packetService.removeProcessor(internalPacketProcessor);
173 cfgService.unregisterConfigFactory(cfgFactory);
Daniele Moro59903172020-02-20 19:23:11 -0800174 oltCpCache.invalidateAll();
Daniele Moro94660a02019-12-02 12:02:07 -0800175 pppoeRelayConfig = null;
176 mapSrcMacToAttInfo = null;
177 internalPacketProcessor = null;
Daniele Morof8a28ce2019-12-12 09:26:25 -0800178 macPppoeServer = null;
Daniele Moro94660a02019-12-02 12:02:07 -0800179
180 log.info("PPPoE Handler Relay deactivated");
181 }
182
183
184 private void updateConfig() {
185 PppoeRelayConfig newPppoeRelayConfig = cfgService.getConfig(appId, PppoeRelayConfig.class);
186 log.info("{}", newPppoeRelayConfig);
187 if (this.pppoeRelayConfig == null &&
188 newPppoeRelayConfig != null &&
189 newPppoeRelayConfig.isValid()) {
190 // TODO: what happens if this is triggered in the middle of a session auth/packet relay?
191 this.pppoeRelayConfig = newPppoeRelayConfig;
192 }
193 }
194
195 private void processPppoePacket(PacketContext context) {
196 if (!isConfigured()) {
197 log.warn("Missing BNG PPPoE handler relay config. Abort packet processing");
198 return;
199 }
200 Ethernet eth = context.inPacket().parsed();
201 log.debug("Parsing the PPPoE header");
202 //FIXME: PPPoE and above headers are extracted from the ethernet
203 // payload. In case we want to modify the PPPoE/upper-layer header, remember to
204 // serialize it back on the Ethernet payload.
205 Pppoe pppoe = parsePppoeHeader(eth);
206 if (pppoe == null) {
207 return;
208 }
209
210 log.debug("Processing PPPoE header");
211
212 // Check from where the packet is received and if the interface is configured
213 ConnectPoint heardOn = context.inPacket().receivedFrom();
214 if (!heardOn.equals(pppoeRelayConfig.getPppoeServerConnectPoint()) &&
215 !heardOn.equals(pppoeRelayConfig.getAsgToOltConnectPoint()) &&
216 !interfaceService.getInterfacesByPort(heardOn).isEmpty()) {
217 log.info("PPPoE packet from unregistered port {}", heardOn);
218 return;
219 }
220
221 // Retrieve the MAC address of the device that intercepted the packet.
222 // This MAC address is the actual PPPoE server MAC address seen by the attachment
223 MacAddress bnguMac = interfaceService.getInterfacesByPort(heardOn).iterator().next().mac();
224
225 VlanId cTag = VlanId.vlanId(eth.getVlanID());
226 VlanId sTag = VlanId.vlanId(eth.getQinQVID());
227
228 // --------------------------------------- DEBUG ----------------------------------------------
229 if (Pppoe.isPPPoED(eth)) {
230 log.info("Received {} packet from {}",
231 pppoe.getPacketType(),
232 heardOn);
233 }
234
235 StringBuilder logPacketPppoes = new StringBuilder();
236 if (Pppoe.isPPPoES(eth)) {
237 logPacketPppoes.append("Received PPPoES ")
238 .append(PppProtocolType.lookup(pppoe.getPppProtocol()).type())
239 .append(" packet from ").append(heardOn).append(".");
240 }
241 if (logPacketPppoes.length() > 0) {
242 log.debug(logPacketPppoes.toString());
243 }
244 log.debug(eth.toString());
245 // --------------------------------------------------------------------------------------------
246
247 if (heardOn.equals(pppoeRelayConfig.getPppoeServerConnectPoint())) {
248 // DOWNSTREAM PACKET: from the PPPoE server to the attachment.
Daniele Morof8a28ce2019-12-12 09:26:25 -0800249
250 // Learn the MAC address of the PPPoE server
251 if (macPppoeServer == null) {
252 macPppoeServer = eth.getSourceMAC();
253 }
254
Daniele Moro94660a02019-12-02 12:02:07 -0800255 MacAddress dstMac = eth.getDestinationMAC();
256 log.debug("Packet to the attachment: {}", eth);
257 if (!mapSrcMacToAttInfo.containsKey(dstMac)) {
258 BngAttachment newAttInfo = PppoeBngAttachment.builder()
259 .withMacAddress(dstMac)
260 .withSTag(sTag)
261 .withCTag(cTag)
262 .withQinqTpid(eth.getQinQTPID())
263 .build();
264 mapSrcMacToAttInfo.put(dstMac, newAttInfo);
265 }
266 // Retrieve the information about the attachment from the internal MAP
267 BngAttachment attInfo = mapSrcMacToAttInfo.get(dstMac);
268
269 // Generate the events for this attachment
270 manageAttachmentStateDownstream(eth, pppoe, attInfo);
Daniele Moro94660a02019-12-02 12:02:07 -0800271 modPacketForAttachment(eth, attInfo, bnguMac);
272
273 log.debug("Packet modified as: {}", eth);
274 // Send out the packet towards the OLT
275 forwardPacket(pppoeRelayConfig.getAsgToOltConnectPoint(), eth);
276 } else {
277 // UPSTREAM DIRECTION: from the attachment to the PPPoE server
278 MacAddress srcMac = eth.getSourceMAC();
279 if (!mapSrcMacToAttInfo.containsKey(srcMac)) {
280 BngAttachment newAttInfo = PppoeBngAttachment.builder()
281 .withMacAddress(srcMac)
282 .withSTag(sTag)
283 .withCTag(cTag)
284 .withQinqTpid(eth.getQinQTPID())
285 .build();
286 mapSrcMacToAttInfo.put(srcMac, newAttInfo);
287 }
288
289 manageAttachmentStateUpstream(eth, pppoe);
290
291 modPacketForPPPoEServer(eth);
292 log.debug("Packet modified as: {}", eth);
293 // Forward packet to the PPPoE server connect point
294 forwardPacket(pppoeRelayConfig.getPppoeServerConnectPoint(), eth);
295 }
296 }
297
298 /**
299 * Generate an event related to PPPoE or IPCP state change.
300 *
301 * @param bngAppEventType Event type
302 * @param ip IP Address if it has been assigned, otherwise
303 * 0.0.0.0
304 * @param attInfo Local attachment information
305 */
306 private void generateEventPppoe(PppoeEvent.EventType bngAppEventType,
307 BngAttachment attInfo, short pppoeSessionId,
308 IpAddress ip) {
309 // Retrive the NNI connect point
Daniele Moro59903172020-02-20 19:23:11 -0800310 ConnectPoint oltConnectPoint;
311 try {
312 oltConnectPoint = oltCpCache.get(ImmutableTriple.of(attInfo.sTag(), attInfo.cTag(),
313 pppoeRelayConfig.getAsgToOltConnectPoint()));
314 } catch (ExecutionException e) {
315 // If unable to retrieve the OLT Connect Point log error and return.
316 // In this way we do NOT propagate the event and eventually create an
317 // inconsistent BNG Attachment.
318 log.error("Unable to retrieve the OLT Connect Point (\"NNI\" Connect Point)", e);
319 return;
320 }
Daniele Moro94660a02019-12-02 12:02:07 -0800321 log.info("Generating event of type {}", bngAppEventType);
322 post(new PppoeEvent(
323 bngAppEventType,
324 new PppoeEventSubject(
Daniele Moro59903172020-02-20 19:23:11 -0800325 oltConnectPoint,
Daniele Moro94660a02019-12-02 12:02:07 -0800326 ip,
327 attInfo.macAddress(),
Daniele Moro59903172020-02-20 19:23:11 -0800328 getPortNameAnnotation(oltConnectPoint),
Daniele Moro94660a02019-12-02 12:02:07 -0800329 pppoeSessionId,
330 attInfo.sTag(),
331 attInfo.cTag())
332 )
333 );
334 }
335
336 /**
337 * Generate attachment related state for the upstream direction.
338 *
339 * @param eth The ethernet packet
340 * @param pppoe PPPoE header
341 */
342 private void manageAttachmentStateUpstream(Ethernet eth, Pppoe pppoe) {
343 PppoeEvent.EventType eventType = null;
344 MacAddress srcMac = eth.getSourceMAC();
345 VlanId cTag = VlanId.vlanId(eth.getVlanID());
346 VlanId sTag = VlanId.vlanId(eth.getQinQVID());
347 BngAttachment attInfo = mapSrcMacToAttInfo.get(srcMac);
348 switch (PppProtocolType.lookup(pppoe.getPppProtocol())) {
349 case IPCP:
350 // Attachment information should be already present
351 Ipcp ipcp = (Ipcp) pppoe.getPayload();
352 if (ipcp.getCode() == Ipcp.CONF_REQ) {
353 log.debug("IPCP configuration request from attachment");
354 eventType = PppoeEvent.EventType.IPCP_CONF_REQUEST;
355 }
356 break;
357 case NO_PROTOCOL:
358 if (Pppoe.isPPPoED(eth) &&
359 pppoe.getPacketType() == Pppoe.PppoeType.PADI) {
360 log.info("PADI received from attachment {}/{}. Saved in internal store",
361 srcMac, sTag);
362 eventType = PppoeEvent.EventType.SESSION_INIT;
363 }
364 break;
365 default:
366 }
367 if (eventType != null) {
368 generateEventPppoe(eventType, attInfo, pppoe.getSessionId(), IP_ADDRESS_ZERO);
369 }
370 }
371
372 private String getPortNameAnnotation(ConnectPoint oltConnectPoint) {
373 return deviceService.getPort(oltConnectPoint.deviceId(),
374 oltConnectPoint.port()).annotations().value("portName");
375 }
376
377 /**
378 * Generate attachment related state for the downstream direction.
379 *
380 * @param eth The ethernet packet
381 * @param pppoe PPPoE header
382 * @param attInfo Attachment info stored in the internal store
383 */
384 private void manageAttachmentStateDownstream(Ethernet eth, Pppoe pppoe,
385 BngAttachment attInfo) {
386 PppoeEvent.EventType eventType = null;
387 IpAddress assignedIpAddress = IP_ADDRESS_ZERO;
388 switch (PppProtocolType.lookup(pppoe.getPppProtocol())) {
389 case IPCP:
390 Ipcp ipcp = (Ipcp) pppoe.getPayload();
391 if (ipcp.getCode() == Ipcp.ACK) {
392 log.info("Received a IPCP ACK from Server. Assigned IP Address {}",
393 ipcp.getIpAddress());
394 assignedIpAddress = ipcp.getIpAddress();
395 eventType = PppoeEvent.EventType.IPCP_CONF_ACK;
396 }
397 break;
398
399 case CHAP:
400 // Check if server has correctly authenticated the attachment
401 GenericPpp chap = (GenericPpp) pppoe.getPayload();
402 if (chap.getCode() == GenericPpp.CHAP_CODE_SUCCESS) {
403 log.info("CHAP authentication success: {}", attInfo.macAddress());
404 eventType = PppoeEvent.EventType.AUTH_SUCCESS;
405 }
406 if (chap.getCode() == GenericPpp.CHAP_CODE_FAILURE) {
407 log.info("CHAP authentication failed: {}", attInfo.macAddress());
408 eventType = PppoeEvent.EventType.AUTH_FAILURE;
409 }
410 break;
411
412 case PAP:
413 // Check if server has correctly authenticated the attachment
414 GenericPpp pap = (GenericPpp) pppoe.getPayload();
415 if (pap.getCode() == GenericPpp.PAP_AUTH_ACK) {
416 log.info("PAP authentication success: {}", attInfo.macAddress());
417 eventType = PppoeEvent.EventType.AUTH_SUCCESS;
418 }
419 if (pap.getCode() == GenericPpp.PAP_AUTH_NACK) {
420 log.info("PAP authentication failed: {}", attInfo.macAddress());
421 eventType = PppoeEvent.EventType.AUTH_FAILURE;
422 }
423 break;
424
425 case LCP:
426 GenericPpp lcp = (GenericPpp) pppoe.getPayload();
427 if (lcp.getCode() == GenericPpp.CODE_TERM_REQ) {
428 log.info("LCP Termination request from PPPoE server");
429 eventType = PppoeEvent.EventType.SESSION_TERMINATION;
430 }
431 break;
432
433 case NO_PROTOCOL:
434 if (Pppoe.isPPPoED(eth)) {
435 switch (pppoe.getPacketType()) {
436 case PADS:
437 // Set the current PPPoE session ID
438 eventType = PppoeEvent.EventType.SESSION_CONFIRMATION;
439 break;
440 case PADT:
441 log.info("PADT received from PPPoE server");
442 eventType = PppoeEvent.EventType.SESSION_TERMINATION;
443 break;
444 default:
445 }
446 }
447 break;
448 default:
449 }
450 // Generate and event if needed
451 if (eventType != null) {
452 generateEventPppoe(eventType, attInfo, pppoe.getSessionId(), assignedIpAddress);
453 }
454 }
455
456 private Pppoe parsePppoeHeader(Ethernet eth) {
457 try {
458 return Pppoe.deserializer().deserialize(((Data) eth.getPayload()).getData(),
459 0,
460 ((Data) eth.getPayload()).getData().length);
461 } catch (DeserializationException e) {
462 log.error("Error parsing the PPPoE Headers, packet skipped. \n" + e.getMessage());
463 return null;
464 }
465 }
466
467
468 /**
469 * Apply the modification to the packet to send it to the attachment.
470 *
471 * @param eth Packet to be modified
472 * @param attInfo Attachment information store in the internal map
473 */
474 private void modPacketForAttachment(Ethernet eth,
475 BngAttachment attInfo,
476 MacAddress newSourceMac) {
477 eth.setVlanID(attInfo.cTag().toShort());
478 eth.setQinQVID(attInfo.sTag().toShort());
479 eth.setQinQTPID(attInfo.qinqTpid());
480 eth.setSourceMACAddress(newSourceMac);
481 }
482
483 /**
484 * Apply the modification to the packet to send it to the PPPoE Server.
485 *
486 * @param eth Packet to be modified
487 */
488 private void modPacketForPPPoEServer(Ethernet eth) {
Daniele Moro94660a02019-12-02 12:02:07 -0800489 Set<Interface> interfaces = interfaceService.getInterfacesByPort(pppoeRelayConfig.getPppoeServerConnectPoint());
490 if (interfaces != null &&
491 interfaces.iterator().hasNext() &&
492 interfaces.iterator().next().vlanTagged() != null &&
493 interfaces.iterator().next().vlanTagged().iterator().hasNext()) {
494 VlanId vlanId = interfaces.iterator().next().vlanTagged().iterator().next();
495 if (vlanId != null && vlanId != VlanId.NONE) {
496 eth.setVlanID(vlanId.toShort());
497 eth.setQinQVID(Ethernet.VLAN_UNTAGGED);
498 } else {
499 eth.setVlanID(Ethernet.VLAN_UNTAGGED);
500 eth.setQinQVID(Ethernet.VLAN_UNTAGGED);
501 }
502 } else {
503 eth.setVlanID(Ethernet.VLAN_UNTAGGED);
504 eth.setQinQVID(Ethernet.VLAN_UNTAGGED);
505 }
506 // Modify DST Mac Address with the one of the PPPoE Server
507 if (!eth.getDestinationMAC().isBroadcast()) {
Daniele Morof8a28ce2019-12-12 09:26:25 -0800508 if (macPppoeServer == null) {
509 log.warn("NO Mac address for PPPoE server available! Dropping packet");
510 return;
511 }
512 eth.setDestinationMACAddress(macPppoeServer);
Daniele Moro94660a02019-12-02 12:02:07 -0800513 }
514 }
515
516 /**
517 * Retrieve the NNI Connect Point given the S-Tag, C-Tag and the OLT facing
518 * ASG connect point.
519 *
520 * @param sTag The S-Tag VLAN tag
521 * @param cTag The C-Tag VLAN tag
522 * @param asgToOltConnectPoint Connect point from ASG to OLT.
523 * @return
524 */
525 private Optional<ConnectPoint> getOltConnectPoint(
526 VlanId sTag, VlanId cTag, ConnectPoint asgToOltConnectPoint) {
527 // Retrieve the UNI port where this attachment is attached to. We assume
528 // an attachment is uniquely identified by its c-tag and s-tag in the
529 // scope of an OLT. In lack of a better API in SADIS, we retrieve info
530 // for all OLT ports and match those that have same c-tag and s-tag as
531 // the given attachemnt info.
Daniele Moro94660a02019-12-02 12:02:07 -0800532 var oltDeviceIds = linkService.getIngressLinks(asgToOltConnectPoint)
533 .stream()
534 .map(link -> link.src().deviceId())
535 .filter(deviceId -> {
536 try {
537 return driverService.getDriver(deviceId)
538 .name().contains("voltha");
539 } catch (ItemNotFoundException e) {
540 log.warn("Unable to find driver for {}", deviceId);
541 return false;
542 }
543 })
544 .collect(Collectors.toSet());
545
546 var oltConnectPoints = oltDeviceIds.stream()
547 .flatMap(deviceId -> deviceService.getPorts(deviceId).stream())
Daniele Moro59903172020-02-20 19:23:11 -0800548 .filter(Port::isEnabled)
Daniele Moro94660a02019-12-02 12:02:07 -0800549 .filter(port -> {
550 var portName = port.annotations().value("portName");
Daniele Moro59903172020-02-20 19:23:11 -0800551 // FIXME: here we support a single UNI per ONU port
552 if (portName == null ||
553 (portName.contains("-") && !portName.endsWith("-1"))) {
Daniele Moro94660a02019-12-02 12:02:07 -0800554 return false;
555 }
556 var info = sadisService.getSubscriberInfoService()
557 .get(portName);
558 return info != null &&
559 Objects.equals(cTag, info.cTag()) &&
560 Objects.equals(sTag, info.sTag());
561 })
562 .map(port -> new ConnectPoint(port.element().id(), port.number()))
563 .collect(Collectors.toSet());
564
565 if (oltConnectPoints.isEmpty()) {
566 log.error("Unable to find a connect point for attachment with S-Tag {} C-Tag {} on OLTs {}",
567 sTag, cTag, oltDeviceIds);
568 return Optional.empty();
569 } else if (oltConnectPoints.size() > 1) {
570 log.error("Multiple OLT connect points found for attachment S-Tag {} C-Tag {}," +
571 "aborting discovery as this is NOT supported (yet)..." +
572 "oltConnectPoints={}",
573 sTag, cTag, oltConnectPoints);
574 return Optional.empty();
575 }
576
577 return Optional.of(oltConnectPoints.iterator().next());
578 }
579
580 /**
581 * Send the specified packet, out to the specified connect point.
582 *
583 * @param toPort Output port to send the packet
584 * @param packet Packet to be sent
585 */
586 private void forwardPacket(ConnectPoint toPort, Ethernet packet) {
587 TrafficTreatment toPortTreatment = DefaultTrafficTreatment.builder()
588 .setOutput(toPort.port()).build();
589 OutboundPacket outboundPacket = new DefaultOutboundPacket(
590 toPort.deviceId(), toPortTreatment, ByteBuffer.wrap(packet.serialize()));
591 packetService.emit(outboundPacket);
592 }
593
594 /**
595 * Check if the handler is correctly configured.
596 *
597 * @return True if it is correctly configure, False otherwise
598 */
599 private boolean isConfigured() {
600 return pppoeRelayConfig != null;
601 }
602
603 /**
604 * The internal packet processor for PPPoE packets.
605 */
606 private class InternalPacketProcessor implements PacketProcessor {
607
608 @Override
609 public void process(PacketContext context) {
610 processPacketInternal(context);
611 }
612
613 private void processPacketInternal(PacketContext context) {
614 if (context == null || context.isHandled()) {
615 return;
616 }
617 Ethernet eth = context.inPacket().parsed();
618 if (eth == null) {
619 return;
620 }
621 if (!Pppoe.isPPPoES(eth) && !Pppoe.isPPPoED(eth)) {
622 return;
623 }
Daniele Moro59903172020-02-20 19:23:11 -0800624 SharedExecutors.getPoolThreadExecutor().submit(() -> {
625 try {
626 processPppoePacket(context);
627 } catch (Throwable e) {
628 log.error("Exception while processing packet", e);
629 }
630 });
Daniele Moro94660a02019-12-02 12:02:07 -0800631 }
632 }
633
634 /**
635 * Listener for network config events.
636 */
637 private class InternalConfigListener implements NetworkConfigListener {
638 @Override
639 public void event(NetworkConfigEvent event) {
640 switch (event.type()) {
641 case CONFIG_ADDED:
642 log.info("CONFIG_ADDED");
643 event.config().ifPresent(config -> {
644 pppoeRelayConfig = ((PppoeRelayConfig) config);
645 log.info("{} added", config.getClass().getSimpleName());
646 });
647 break;
648 // TODO: support at least updated and removed events
649 case CONFIG_UPDATED:
650 case CONFIG_REGISTERED:
651 case CONFIG_UNREGISTERED:
652 case CONFIG_REMOVED:
653 default:
654 log.warn("Unsupported event type {}", event.type());
655 break;
656 }
657 }
658
659 @Override
660 public boolean isRelevant(NetworkConfigEvent event) {
661 if (event.configClass().equals(PppoeRelayConfig.class)) {
662 return true;
663 }
664 log.debug("Ignore irrelevant event class {}", event.configClass().getName());
665 return false;
666 }
667 }
668}