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