/* * Copyright 2016-present Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onosproject.vpls; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.onlab.packet.MacAddress; import org.onlab.packet.VlanId; import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; import org.onosproject.incubator.net.intf.Interface; import org.onosproject.incubator.net.intf.InterfaceEvent; import org.onosproject.incubator.net.intf.InterfaceListener; import org.onosproject.incubator.net.intf.InterfaceService; import org.onosproject.incubator.net.neighbour.NeighbourMessageContext; import org.onosproject.incubator.net.neighbour.NeighbourMessageHandler; import org.onosproject.incubator.net.neighbour.NeighbourResolutionService; import org.onosproject.net.ConnectPoint; import org.onosproject.net.Host; import org.onosproject.net.config.NetworkConfigEvent; import org.onosproject.net.config.NetworkConfigListener; import org.onosproject.net.config.NetworkConfigService; import org.onosproject.net.host.HostService; import org.onosproject.vpls.api.VplsData; import org.onosproject.vpls.api.VplsStore; import org.slf4j.Logger; import java.util.Collection; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import static org.slf4j.LoggerFactory.getLogger; /** * Handles neighbour messages for on behalf of the VPLS application. Handlers * will be changed automatically by interface or network configuration events. */ @Component(immediate = true) public class VplsNeighbourHandler { private static final String UNKNOWN_CONTEXT = "Unknown context type: {}"; private static final String CAN_NOT_FIND_VPLS = "Cannot find VPLS for port {} with VLAN Id {}."; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected CoreService coreService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected InterfaceService interfaceService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected NeighbourResolutionService neighbourService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected VplsStore vplsStore; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected NetworkConfigService configService; private VplsInterfaceListener interfaceListener = new VplsInterfaceListener(); protected VplsNeighbourMessageHandler neighbourHandler = new VplsNeighbourMessageHandler(); protected VplsConfigListener configListener = new VplsConfigListener(); private final Logger log = getLogger(getClass()); private ApplicationId appId; @Activate protected void activate() { appId = coreService.registerApplication(VplsManager.VPLS_APP); interfaceService.addListener(interfaceListener); configService.addListener(configListener); configNeighbourHandler(); } @Deactivate protected void deactivate() { interfaceService.removeListener(interfaceListener); configService.removeListener(configListener); neighbourService.unregisterNeighbourHandlers(appId); } /** * Registers neighbour handler to all available interfaces. */ protected void configNeighbourHandler() { neighbourService.unregisterNeighbourHandlers(appId); interfaceService .getInterfaces() .forEach(intf -> neighbourService.registerNeighbourHandler(intf, neighbourHandler, appId)); } /** * Handler for neighbour messages. */ private class VplsNeighbourMessageHandler implements NeighbourMessageHandler { @Override public void handleMessage(NeighbourMessageContext context, HostService hostService) { switch (context.type()) { case REQUEST: handleRequest(context); break; case REPLY: handleReply(context, hostService); break; default: log.warn(UNKNOWN_CONTEXT, context.type()); break; } } } /** * Handles request messages. * * @param context the message context */ protected void handleRequest(NeighbourMessageContext context) { // Find target VPLS first, then broadcast to all interface of this VPLS VplsData vplsData = findVpls(context); if (vplsData != null) { vplsData.interfaces().stream() .filter(intf -> !context.inPort().equals(intf.connectPoint())) .forEach(context::forward); } else { log.warn(CAN_NOT_FIND_VPLS, context.inPort(), context.vlan()); context.drop(); } } /** * Handles reply messages between VLAN tagged interfaces. * * @param context the message context * @param hostService the host service */ protected void handleReply(NeighbourMessageContext context, HostService hostService) { // Find target VPLS, then reply to the host VplsData vplsData = findVpls(context); if (vplsData != null) { MacAddress dstMac = context.dstMac(); Set<Host> hosts = hostService.getHostsByMac(dstMac); hosts = hosts.stream() .filter(host -> vplsData.interfaces().contains(getHostInterface(host))) .collect(Collectors.toSet()); // reply to all host in same VPLS hosts.stream() .map(this::getHostInterface) .filter(Objects::nonNull) .forEach(context::forward); } else { // this might be happened when we remove an interface from VPLS // just ignore this message log.warn(CAN_NOT_FIND_VPLS, context.inPort(), context.vlan()); context.drop(); } } /** * Finds the VPLS with given neighbour message context. * * @param context the neighbour message context * @return the VPLS for specific neighbour message context */ private VplsData findVpls(NeighbourMessageContext context) { Collection<VplsData> vplses = vplsStore.getAllVpls(); for (VplsData vplsData : vplses) { Set<Interface> interfaces = vplsData.interfaces(); ConnectPoint port = context.inPort(); VlanId vlanId = context.vlan(); boolean match = interfaces.stream() .anyMatch(iface -> iface.connectPoint().equals(port) && iface.vlan().equals(vlanId)); if (match) { return vplsData; } } return null; } /** * Finds the network interface related to the host. * * @param host the host * @return the interface related to the host */ private Interface getHostInterface(Host host) { Set<Interface> interfaces = interfaceService.getInterfaces(); return interfaces.stream() .filter(iface -> iface.connectPoint().equals(host.location()) && iface.vlan().equals(host.vlan())) .findFirst() .orElse(null); } /** * Listener for interface configuration events. */ private class VplsInterfaceListener implements InterfaceListener { @Override public void event(InterfaceEvent event) { configNeighbourHandler(); } } /** * Listener for network configuration events. */ private class VplsConfigListener implements NetworkConfigListener { @Override public void event(NetworkConfigEvent event) { configNeighbourHandler(); } } }