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