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