Adding EVPN App code
Change-Id: Id3b2192f56f054cadcd8384092245b8757a781a9
diff --git a/apps/evpnopenflow/src/main/java/org/onosproject/evpnopenflow/manager/impl/EvpnManager.java b/apps/evpnopenflow/src/main/java/org/onosproject/evpnopenflow/manager/impl/EvpnManager.java
new file mode 100755
index 0000000..7f7314b
--- /dev/null
+++ b/apps/evpnopenflow/src/main/java/org/onosproject/evpnopenflow/manager/impl/EvpnManager.java
@@ -0,0 +1,1132 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.evpnopenflow.manager.impl;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.collect.Sets;
+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.apache.felix.scr.annotations.Service;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onlab.packet.EthType;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MplsLabel;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.evpnopenflow.manager.EvpnService;
+import org.onosproject.evpnopenflow.rsc.VpnAfConfig;
+import org.onosproject.evpnopenflow.rsc.VpnInstance;
+import org.onosproject.evpnopenflow.rsc.VpnInstanceId;
+import org.onosproject.evpnopenflow.rsc.VpnPort;
+import org.onosproject.evpnopenflow.rsc.VpnPortId;
+import org.onosproject.evpnopenflow.rsc.baseport.BasePortService;
+import org.onosproject.evpnopenflow.rsc.vpnafconfig.VpnAfConfigEvent;
+import org.onosproject.evpnopenflow.rsc.vpnafconfig.VpnAfConfigListener;
+import org.onosproject.evpnopenflow.rsc.vpnafconfig.VpnAfConfigService;
+import org.onosproject.evpnopenflow.rsc.vpninstance.VpnInstanceService;
+import org.onosproject.evpnopenflow.rsc.vpnport.VpnPortEvent;
+import org.onosproject.evpnopenflow.rsc.vpnport.VpnPortListener;
+import org.onosproject.evpnopenflow.rsc.vpnport.VpnPortService;
+import org.onosproject.gluon.rsc.GluonConfig;
+import org.onosproject.incubator.net.resource.label.LabelResource;
+import org.onosproject.incubator.net.resource.label.LabelResourceAdminService;
+import org.onosproject.incubator.net.resource.label.LabelResourceId;
+import org.onosproject.incubator.net.resource.label.LabelResourceService;
+import org.onosproject.incubator.net.routing.EvpnInstanceName;
+import org.onosproject.incubator.net.routing.EvpnInstanceNextHop;
+import org.onosproject.incubator.net.routing.EvpnInstancePrefix;
+import org.onosproject.incubator.net.routing.EvpnInstanceRoute;
+import org.onosproject.incubator.net.routing.EvpnNextHop;
+import org.onosproject.incubator.net.routing.EvpnRoute;
+import org.onosproject.incubator.net.routing.EvpnRoute.Source;
+import org.onosproject.incubator.net.routing.EvpnRouteAdminService;
+import org.onosproject.incubator.net.routing.EvpnRouteEvent;
+import org.onosproject.incubator.net.routing.EvpnRouteListener;
+import org.onosproject.incubator.net.routing.EvpnRouteService;
+import org.onosproject.incubator.net.routing.EvpnRouteSet;
+import org.onosproject.incubator.net.routing.EvpnRouteStore;
+import org.onosproject.incubator.net.routing.Label;
+import org.onosproject.incubator.net.routing.RouteDistinguisher;
+import org.onosproject.incubator.net.routing.VpnRouteTarget;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.flowobjective.Objective.Operation;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import static com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type.Reference;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.APP_ID;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.ARP_PRIORITY;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.ARP_RESPONSE;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.BASEPORT;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.BGP_EVPN_ROUTE_DELETE_START;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.BGP_EVPN_ROUTE_UPDATE_START;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.BOTH;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.CANNOT_FIND_TUNNEL_PORT_DEVICE;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.CANT_FIND_CONTROLLER_DEVICE;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.CANT_FIND_VPN_INSTANCE;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.CANT_FIND_VPN_PORT;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.DELETE;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.EVPN_OPENFLOW_START;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.EVPN_OPENFLOW_STOP;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.EXPORT_EXTCOMMUNITY;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.FAILED_TO_SET_TUNNEL_DST;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.GET_PRIVATE_LABEL;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.HOST_DETECT;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.HOST_VANISHED;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.IFACEID;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.IFACEID_OF_HOST_IS_NULL;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.IMPORT_EXTCOMMUNITY;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.INVALID_EVENT_RECEIVED;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.INVALID_ROUTE_TARGET_TYPE;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.INVALID_TARGET_RECEIVED;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.MPLS_OUT_FLOWS;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.NETWORK_CONFIG_EVENT_IS_RECEIVED;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.NOT_MASTER_FOR_SPECIFIC_DEVICE;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.RELEASE_LABEL_FAILED;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.ROUTE_ADD_ARP_RULES;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.ROUTE_REMOVE_ARP_RULES;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.SET;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.SLASH;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.SWITCH_CHANNEL_ID;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.TUNNEL_DST;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.UPDATE;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.VPN_AF_TARGET;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.VPN_INSTANCE_TARGET;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.VPN_PORT_BIND;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.VPN_PORT_TARGET;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.VPN_PORT_UNBIND;
+import static org.onosproject.evpnopenflow.rsc.EvpnConstants.VXLAN;
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the EVPN service.
+ */
+@Component(immediate = true)
+@Service
+public class EvpnManager implements EvpnService {
+ private final Logger log = getLogger(getClass());
+ private static final EthType.EtherType ARP_TYPE = EthType.EtherType.ARP;
+
+ protected ApplicationId appId;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected EvpnRouteService evpnRouteService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected EvpnRouteStore evpnRouteStore;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected EvpnRouteAdminService evpnRouteAdminService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LabelResourceAdminService labelAdminService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LabelResourceService labelService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected VpnInstanceService vpnInstanceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowObjectiveService flowObjectiveService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DriverService driverService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected VpnPortService vpnPortService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected VpnAfConfigService vpnAfConfigService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigService configService;
+
+ public Set<EvpnInstanceRoute> evpnInstanceRoutes = new HashSet<>();
+ private final HostListener hostListener = new InnerHostListener();
+ private final VpnPortListener vpnPortListner = new InnerVpnPortListener();
+ private final VpnAfConfigListener vpnAfConfigListener = new
+ InnerVpnAfConfigListener();
+ private final InternalRouteEventListener routeListener = new
+ InternalRouteEventListener();
+
+ private final NetworkConfigListener configListener = new
+ InternalNetworkConfigListener();
+
+ @Activate
+ public void activate() {
+ appId = coreService.registerApplication(APP_ID);
+ hostService.addListener(hostListener);
+ vpnPortService.addListener(vpnPortListner);
+ vpnAfConfigService.addListener(vpnAfConfigListener);
+ configService.addListener(configListener);
+ evpnRouteService.addListener(routeListener);
+
+ labelAdminService
+ .createGlobalPool(LabelResourceId.labelResourceId(1),
+ LabelResourceId.labelResourceId(1000));
+ log.info(EVPN_OPENFLOW_START);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ hostService.removeListener(hostListener);
+ vpnPortService.removeListener(vpnPortListner);
+ vpnAfConfigService.removeListener(vpnAfConfigListener);
+ configService.removeListener(configListener);
+ log.info(EVPN_OPENFLOW_STOP);
+ }
+
+ @Override
+ public void onBgpEvpnRouteUpdate(EvpnRoute route) {
+ if (EvpnRoute.Source.LOCAL.equals(route.source())) {
+ return;
+ }
+ log.info(BGP_EVPN_ROUTE_UPDATE_START, route);
+ // deal with public route and transfer to private route
+ if (vpnInstanceService.getInstances().isEmpty()) {
+ log.info("unable to get instnaces from vpninstance");
+ return;
+ }
+
+ vpnInstanceService.getInstances().forEach(vpnInstance -> {
+ log.info("got instnaces from vpninstance but not entered here");
+ List<VpnRouteTarget> vpnImportRouteRt = new
+ LinkedList<>(vpnInstance.getImportRouteTargets());
+ List<VpnRouteTarget> expRt = route.exportRouteTarget();
+ List<VpnRouteTarget> similar = new LinkedList<>(expRt);
+ similar.retainAll(vpnImportRouteRt);
+
+ if (!similar.isEmpty()) {
+ EvpnInstancePrefix evpnPrefix = EvpnInstancePrefix
+ .evpnPrefix(route.prefixMac(), route.prefixIp());
+
+ EvpnInstanceNextHop evpnNextHop = EvpnInstanceNextHop
+ .evpnNextHop(route.ipNextHop(), route.label());
+
+ EvpnInstanceRoute evpnPrivateRoute = new
+ EvpnInstanceRoute(vpnInstance.vpnInstanceName(),
+ route.routeDistinguisher(),
+ vpnImportRouteRt,
+ route.exportRouteTarget(),
+ evpnPrefix,
+ evpnNextHop,
+ route.prefixIp(),
+ route.ipNextHop(),
+ route.label());
+
+ //update route in route subsystem
+ //TODO: added by shahid
+ evpnInstanceRoutes.add(evpnPrivateRoute);
+
+ }
+ });
+
+ deviceService.getAvailableDevices(Device.Type.SWITCH)
+ .forEach(device -> {
+ log.info("switch device is found");
+ Set<Host> hosts = getHostsByVpn(device, route);
+ for (Host h : hosts) {
+ addArpFlows(device.id(),
+ route,
+ Objective.Operation.ADD,
+ h);
+ ForwardingObjective.Builder objective =
+ getMplsOutBuilder(device.id(),
+ route,
+ h);
+ log.info(MPLS_OUT_FLOWS, h);
+ flowObjectiveService.forward(device.id(),
+ objective.add());
+ }
+ });
+ log.info("no switch device is found");
+ }
+
+ @Override
+ public void onBgpEvpnRouteDelete(EvpnRoute route) {
+ if (EvpnRoute.Source.LOCAL.equals(route.source())) {
+ return;
+ }
+ log.info(BGP_EVPN_ROUTE_DELETE_START, route);
+ // deal with public route deleted and transfer to private route
+ vpnInstanceService.getInstances().forEach(vpnInstance -> {
+ List<VpnRouteTarget> vpnRouteRt = new
+ LinkedList<>(vpnInstance.getImportRouteTargets());
+ List<VpnRouteTarget> localRt = route.exportRouteTarget();
+ List<VpnRouteTarget> similar = new LinkedList<>(localRt);
+ similar.retainAll(vpnRouteRt);
+
+ if (!similar.isEmpty()) {
+ EvpnInstancePrefix evpnPrefix = EvpnInstancePrefix
+ .evpnPrefix(route.prefixMac(), route.prefixIp());
+
+ EvpnInstanceNextHop evpnNextHop = EvpnInstanceNextHop
+ .evpnNextHop(route.ipNextHop(), route.label());
+
+ EvpnInstanceRoute evpnPrivateRoute = new
+ EvpnInstanceRoute(vpnInstance.vpnInstanceName(),
+ route.routeDistinguisher(),
+ vpnRouteRt,
+ route.exportRouteTarget(),
+ evpnPrefix,
+ evpnNextHop,
+ route.prefixIp(),
+ route.ipNextHop(),
+ route.label());
+ //TODO: Added by Shahid
+ //evpnRouteAdminService.withdraw(Sets.newHashSet
+ // (evpnPrivateRoute));
+
+ }
+ });
+ deviceService.getAvailableDevices(Device.Type.SWITCH)
+ .forEach(device -> {
+ Set<Host> hosts = getHostsByVpn(device, route);
+ for (Host h : hosts) {
+ addArpFlows(device.id(),
+ route,
+ Objective.Operation.REMOVE,
+ h);
+ ForwardingObjective.Builder objective
+ = getMplsOutBuilder(device.id(),
+ route,
+ h);
+ flowObjectiveService.forward(device.id(),
+ objective.remove());
+ }
+ });
+ }
+
+ private void addArpFlows(DeviceId deviceId,
+ EvpnRoute route,
+ Operation type,
+ Host host) {
+ DriverHandler handler = driverService.createHandler(deviceId);
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(ARP_TYPE.ethType().toShort())
+ .matchArpTpa(route.prefixIp().address().getIp4Address())
+ .matchInPort(host.location().port()).build();
+
+ ExtensionTreatmentResolver resolver = handler
+ .behaviour(ExtensionTreatmentResolver.class);
+ ExtensionTreatment ethSrcToDst = resolver
+ .getExtensionInstruction(ExtensionTreatmentType
+ .ExtensionTreatmentTypes
+ .NICIRA_MOV_ETH_SRC_TO_DST
+ .type());
+ ExtensionTreatment arpShaToTha = resolver
+ .getExtensionInstruction(ExtensionTreatmentType
+ .ExtensionTreatmentTypes
+ .NICIRA_MOV_ARP_SHA_TO_THA
+ .type());
+ ExtensionTreatment arpSpaToTpa = resolver
+ .getExtensionInstruction(ExtensionTreatmentType
+ .ExtensionTreatmentTypes
+ .NICIRA_MOV_ARP_SPA_TO_TPA
+ .type());
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .extension(ethSrcToDst, deviceId).setEthSrc(route.prefixMac())
+ .setArpOp(ARP_RESPONSE).extension(arpShaToTha, deviceId)
+ .extension(arpSpaToTpa, deviceId).setArpSha(route.prefixMac())
+ .setArpSpa(route.prefixIp().address().getIp4Address())
+ .setOutput(PortNumber.IN_PORT)
+ .build();
+
+ ForwardingObjective.Builder objective = DefaultForwardingObjective
+ .builder().withTreatment(treatment).withSelector(selector)
+ .fromApp(appId).withFlag(ForwardingObjective.Flag.SPECIFIC)
+ .withPriority(ARP_PRIORITY);
+ if (type.equals(Objective.Operation.ADD)) {
+ log.info(ROUTE_ADD_ARP_RULES);
+ flowObjectiveService.forward(deviceId, objective.add());
+ } else {
+ log.info(ROUTE_REMOVE_ARP_RULES);
+ flowObjectiveService.forward(deviceId, objective.remove());
+ }
+ }
+
+ private Set<Host> getHostsByVpn(Device device, EvpnRoute route) {
+ Set<Host> vpnHosts = Sets.newHashSet();
+ Set<Host> hosts = hostService.getConnectedHosts(device.id());
+ for (Host h : hosts) {
+ String ifaceId = h.annotations().value(IFACEID);
+ if (!vpnPortService.exists(VpnPortId.vpnPortId(ifaceId))) {
+ continue;
+ }
+
+ VpnPort vpnPort = vpnPortService
+ .getPort(VpnPortId.vpnPortId(ifaceId));
+ VpnInstanceId vpnInstanceId = vpnPort.vpnInstanceId();
+
+ VpnInstance vpnInstance = vpnInstanceService
+ .getInstance(vpnInstanceId);
+
+ List<VpnRouteTarget> expRt = route.exportRouteTarget();
+ List<VpnRouteTarget> similar = new LinkedList<>(expRt);
+ similar.retainAll(vpnInstance.getImportRouteTargets());
+ //TODO: currently checking for RT comparision.
+ //TODO: Need to check about RD comparision is really required.
+ //if (route.routeDistinguisher()
+ //.equals(vpnInstance.routeDistinguisher())) {
+ if (!similar.isEmpty()) {
+ vpnHosts.add(h);
+ }
+ }
+ return vpnHosts;
+ }
+
+ private ForwardingObjective.Builder getMplsOutBuilder(DeviceId deviceId,
+ EvpnRoute route,
+ Host h) {
+ DriverHandler handler = driverService.createHandler(deviceId);
+ ExtensionTreatmentResolver resolver = handler
+ .behaviour(ExtensionTreatmentResolver.class);
+ ExtensionTreatment treatment = resolver
+ .getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
+ try {
+ treatment.setPropertyValue(TUNNEL_DST, route.ipNextHop());
+ } catch (Exception e) {
+ log.error(FAILED_TO_SET_TUNNEL_DST, deviceId);
+ }
+ TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
+ builder.extension(treatment, deviceId);
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchInPort(h.location().port()).matchEthSrc(h.mac())
+ .matchEthDst(route.prefixMac()).build();
+
+ TrafficTreatment build = builder.pushMpls()
+ .setMpls(MplsLabel.mplsLabel(route.label().getLabel()))
+ .setOutput(getTunnlePort(deviceId)).build();
+
+ return DefaultForwardingObjective
+ .builder().withTreatment(build).withSelector(selector)
+ .fromApp(appId).withFlag(ForwardingObjective.Flag.SPECIFIC)
+ .withPriority(60000);
+
+ }
+
+ private ForwardingObjective.Builder getMplsInBuilder(DeviceId deviceId,
+ Host host,
+ Label label) {
+ TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchInPort(getTunnlePort(deviceId))
+ .matchEthType(EthType.EtherType.MPLS_UNICAST.ethType()
+ .toShort())
+ .matchMplsBos(true)
+ .matchMplsLabel(MplsLabel.mplsLabel(label.getLabel())).build();
+ TrafficTreatment treatment = builder.popMpls(EthType
+ .EtherType
+ .IPV4.ethType())
+ .setOutput(host.location().port()).build();
+ return DefaultForwardingObjective
+ .builder().withTreatment(treatment).withSelector(selector)
+ .fromApp(appId).withFlag(ForwardingObjective.Flag.SPECIFIC)
+ .withPriority(60000);
+ }
+
+ /**
+ * Get local tunnel ports.
+ *
+ * @param ports Iterable of Port
+ * @return Collection of PortNumber
+ */
+ private Collection<PortNumber> getLocalTunnelPorts(Iterable<Port>
+ ports) {
+ Collection<PortNumber> localTunnelPorts = new ArrayList<>();
+ if (ports != null) {
+ log.info("port value is not null {}", ports);
+ Sets.newHashSet(ports).stream()
+ .filter(p -> !p.number().equals(PortNumber.LOCAL))
+ .forEach(p -> {
+ log.info("number is not matched but no vxlan port");
+ if (p.annotations().value(AnnotationKeys.PORT_NAME)
+ .startsWith(VXLAN)) {
+ localTunnelPorts.add(p.number());
+ }
+ });
+ }
+ return localTunnelPorts;
+ }
+
+ private PortNumber getTunnlePort(DeviceId deviceId) {
+ Iterable<Port> ports = deviceService.getPorts(deviceId);
+ Collection<PortNumber> localTunnelPorts = getLocalTunnelPorts(ports);
+ if (localTunnelPorts.isEmpty()) {
+ log.error(CANNOT_FIND_TUNNEL_PORT_DEVICE, deviceId);
+ return null;
+ }
+ return localTunnelPorts.iterator().next();
+ }
+
+ private void setFlows(DeviceId deviceId, Host host, Label label,
+ List<VpnRouteTarget> rtImport,
+ Operation type) {
+ log.info("Set the flows to OVS");
+ ForwardingObjective.Builder objective = getMplsInBuilder(deviceId,
+ host,
+ label);
+ if (type.equals(Objective.Operation.ADD)) {
+ flowObjectiveService.forward(deviceId, objective.add());
+ } else {
+ flowObjectiveService.forward(deviceId, objective.remove());
+ }
+
+ // download remote flows if and only routes are present.
+ evpnRouteStore.getRouteTables().forEach(routeTableId -> {
+ Collection<EvpnRouteSet> routes
+ = evpnRouteStore.getRoutes(routeTableId);
+ if (routes != null) {
+ routes.forEach(route -> {
+ Collection<EvpnRoute> evpnRoutes = route.routes();
+ for (EvpnRoute evpnRoute : evpnRoutes) {
+ EvpnRoute evpnRouteTem = evpnRoute;
+ Set<Host> hostByMac = hostService
+ .getHostsByMac(evpnRouteTem
+ .prefixMac());
+
+ if (!hostByMac.isEmpty()
+ || (!(compareLists(rtImport, evpnRouteTem
+ .exportRouteTarget())))) {
+ log.info("Route target import/export is not matched");
+ continue;
+ }
+ log.info("Set the ARP flows");
+ addArpFlows(deviceId, evpnRouteTem, type, host);
+ ForwardingObjective.Builder build = getMplsOutBuilder(deviceId,
+ evpnRouteTem,
+ host);
+ log.info("Set the MPLS flows");
+ if (type.equals(Objective.Operation.ADD)) {
+ flowObjectiveService.forward(deviceId, build.add());
+ } else {
+ flowObjectiveService.forward(deviceId, build.remove());
+ }
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * comparison for tow lists.
+ *
+ * @param list1 import list
+ * @param list2 export list
+ * @return true or false
+ */
+ public static boolean compareLists(List<VpnRouteTarget> list1,
+ List<VpnRouteTarget> list2) {
+ if (list1 == null && list2 == null) {
+ return true;
+ }
+
+ if (list1 != null && list2 != null) {
+ if (list1.size() == list2.size()) {
+ for (VpnRouteTarget li1Long : list1) {
+ boolean isEqual = false;
+ for (VpnRouteTarget li2Long : list2) {
+ if (li1Long.equals(li2Long)) {
+ isEqual = true;
+ break;
+ }
+ }
+ if (!isEqual) {
+ return false;
+ }
+ }
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void onHostDetected(Host host) {
+ log.info(HOST_DETECT, host);
+ DeviceId deviceId = host.location().deviceId();
+ if (!mastershipService.isLocalMaster(deviceId)) {
+ log.info(NOT_MASTER_FOR_SPECIFIC_DEVICE);
+ return;
+ }
+
+ String ifaceId = host.annotations().value(IFACEID);
+ if (ifaceId == null) {
+ log.error(IFACEID_OF_HOST_IS_NULL);
+ return;
+ }
+ VpnPortId vpnPortId = VpnPortId.vpnPortId(ifaceId);
+ // Get VPN port id from EVPN app store
+ if (!vpnPortService.exists(vpnPortId)) {
+ log.info(CANT_FIND_VPN_PORT, ifaceId);
+ return;
+ }
+
+ VpnPort vpnPort = vpnPortService.getPort(vpnPortId);
+ VpnInstanceId vpnInstanceId = vpnPort.vpnInstanceId();
+ if (!vpnInstanceService.exists(vpnInstanceId)) {
+ log.info(CANT_FIND_VPN_INSTANCE, vpnInstanceId);
+ return;
+ }
+
+ Label privateLabel = applyLabel();
+ // create private route and get label
+ setPrivateRoute(host, vpnInstanceId, privateLabel,
+ Objective.Operation.ADD);
+ VpnInstance vpnInstance = vpnInstanceService.getInstance(vpnInstanceId);
+
+ List<VpnRouteTarget> rtImport
+ = new LinkedList<>(vpnInstance.getImportRouteTargets());
+ List<VpnRouteTarget> rtExport
+ = new LinkedList<>(vpnInstance.getExportRouteTargets());
+ //download flows
+ setFlows(deviceId, host, privateLabel, rtImport,
+ Objective.Operation.ADD);
+ }
+
+ /**
+ * update or withdraw evpn route from route admin service.
+ *
+ * @param host host
+ * @param vpnInstanceId vpn instance id
+ * @param privateLabel private label
+ * @param type operation type
+ */
+ private void setPrivateRoute(Host host, VpnInstanceId vpnInstanceId,
+ Label privateLabel,
+ Operation type) {
+ DeviceId deviceId = host.location().deviceId();
+ Device device = deviceService.getDevice(deviceId);
+ VpnInstance vpnInstance = vpnInstanceService.getInstance(vpnInstanceId);
+ RouteDistinguisher rd = vpnInstance.routeDistinguisher();
+ Set<VpnRouteTarget> importRouteTargets
+ = vpnInstance.getImportRouteTargets();
+ Set<VpnRouteTarget> exportRouteTargets
+ = vpnInstance.getExportRouteTargets();
+ EvpnInstanceName instanceName = vpnInstance.vpnInstanceName();
+ String url = device.annotations().value(SWITCH_CHANNEL_ID);
+ String controllerIp = url.substring(0, url.lastIndexOf(":"));
+
+ if (controllerIp == null) {
+ log.error(CANT_FIND_CONTROLLER_DEVICE, device.id().toString());
+ return;
+ }
+ IpAddress ipAddress = IpAddress.valueOf(controllerIp);
+ // create private route
+ EvpnInstanceNextHop evpnNextHop = EvpnInstanceNextHop
+ .evpnNextHop(ipAddress, privateLabel);
+ EvpnInstancePrefix evpnPrefix = EvpnInstancePrefix
+ .evpnPrefix(host.mac(), IpPrefix.valueOf(host.ipAddresses()
+ .iterator()
+ .next()
+ .getIp4Address(), 32));
+ EvpnInstanceRoute evpnPrivateRoute
+ = new EvpnInstanceRoute(instanceName,
+ rd,
+ new LinkedList<>(importRouteTargets),
+ new LinkedList<>(exportRouteTargets),
+ evpnPrefix,
+ evpnNextHop,
+ IpPrefix.valueOf(host.ipAddresses()
+ .iterator()
+ .next()
+ .getIp4Address(), 32),
+ ipAddress,
+ privateLabel);
+
+ // change to public route
+ EvpnRoute evpnRoute
+ = new EvpnRoute(Source.LOCAL,
+ host.mac(),
+ IpPrefix.valueOf(host.ipAddresses()
+ .iterator()
+ .next()
+ .getIp4Address(), 32),
+ ipAddress,
+ rd,
+ new LinkedList<>(importRouteTargets),
+ new LinkedList<>(exportRouteTargets),
+ privateLabel);
+ if (type.equals(Objective.Operation.ADD)) {
+ //evpnRouteAdminService.update(Sets.newHashSet(evpnPrivateRoute));
+ evpnInstanceRoutes.add(evpnPrivateRoute);
+ evpnRouteAdminService.update(Sets.newHashSet(evpnRoute));
+
+ } else {
+ //evpnRouteAdminService.withdraw(Sets.newHashSet(evpnPrivateRoute));
+ evpnInstanceRoutes.remove(evpnPrivateRoute);
+ evpnRouteAdminService.withdraw(Sets.newHashSet(evpnRoute));
+ }
+ }
+
+ /**
+ * Generate the label for evpn route from global pool.
+ */
+ private Label applyLabel() {
+ Collection<LabelResource> privateLabels = labelService
+ .applyFromGlobalPool(1);
+ Label privateLabel = Label.label(0);
+ if (!privateLabels.isEmpty()) {
+ privateLabel = Label.label(Integer.parseInt(
+ privateLabels.iterator().next()
+ .labelResourceId().toString()));
+ }
+ log.info(GET_PRIVATE_LABEL, privateLabel);
+ return privateLabel;
+ }
+
+ @Override
+ public void onHostVanished(Host host) {
+ log.info(HOST_VANISHED, host);
+ DeviceId deviceId = host.location().deviceId();
+ if (!mastershipService.isLocalMaster(deviceId)) {
+ return;
+ }
+ String ifaceId = host.annotations().value(IFACEID);
+ if (ifaceId == null) {
+ log.error(IFACEID_OF_HOST_IS_NULL);
+ return;
+ }
+ // Get info from Gluon Shim
+ VpnPort vpnPort = vpnPortService.getPort(VpnPortId.vpnPortId(ifaceId));
+ VpnInstanceId vpnInstanceId = vpnPort.vpnInstanceId();
+ if (!vpnInstanceService.exists(vpnInstanceId)) {
+ log.info(CANT_FIND_VPN_INSTANCE, vpnInstanceId);
+ return;
+ }
+ VpnInstance vpnInstance = vpnInstanceService.getInstance(vpnInstanceId);
+
+ Label label = releaseLabel(vpnInstance, host);
+ // create private route and get label
+ setPrivateRoute(host, vpnInstanceId, label, Objective.Operation.REMOVE);
+ // download flows
+ List<VpnRouteTarget> rtImport
+ = new LinkedList<>(vpnInstance.getImportRouteTargets());
+ List<VpnRouteTarget> rtExport
+ = new LinkedList<>(vpnInstance.getExportRouteTargets());
+ setFlows(deviceId, host, label, rtImport,
+ Objective.Operation.REMOVE);
+ }
+
+ /**
+ * Release the label from the evpn route.
+ *
+ * @param vpnInstance vpn instance
+ * @param host host
+ */
+ private Label releaseLabel(VpnInstance vpnInstance, Host host) {
+ EvpnInstanceName instanceName = vpnInstance.vpnInstanceName();
+
+ //Get all vpn-instance routes and check for label.
+ Label label = null;
+ for (EvpnInstanceRoute evpnInstanceRoute : evpnInstanceRoutes) {
+ if (evpnInstanceRoute.evpnInstanceName().equals(instanceName)) {
+ label = evpnInstanceRoute.getLabel();
+ // delete private route and get label ,change to public route
+ boolean isRelease
+ = labelService
+ .releaseToGlobalPool(
+ Sets.newHashSet(
+ LabelResourceId
+ .labelResourceId(label.getLabel())));
+ if (!isRelease) {
+ log.error(RELEASE_LABEL_FAILED, label.getLabel());
+ }
+ break;
+ }
+ }
+ return label;
+ }
+
+ private class InternalRouteEventListener implements EvpnRouteListener {
+
+ @Override
+ public void event(EvpnRouteEvent event) {
+ if (!(event.subject() instanceof EvpnRoute)) {
+ return;
+ }
+ EvpnRoute route = (EvpnRoute) event.subject();
+ if (EvpnRouteEvent.Type.ROUTE_ADDED == event.type()) {
+ onBgpEvpnRouteUpdate(route);
+ } else if (EvpnRouteEvent.Type.ROUTE_REMOVED == event.type()) {
+ onBgpEvpnRouteDelete(route);
+ }
+ }
+ }
+
+ private class InnerHostListener implements HostListener {
+
+ @Override
+ public void event(HostEvent event) {
+ Host host = event.subject();
+ if (HostEvent.Type.HOST_ADDED == event.type()) {
+ onHostDetected(host);
+ } else if (HostEvent.Type.HOST_REMOVED == event.type()) {
+ onHostVanished(host);
+ }
+ }
+
+ }
+
+ private class InnerVpnPortListener implements VpnPortListener {
+
+ @Override
+ public void event(VpnPortEvent event) {
+ VpnPort vpnPort = event.subject();
+ if (VpnPortEvent.Type.VPN_PORT_DELETE == event.type()) {
+ onVpnPortDelete(vpnPort);
+ } else if (VpnPortEvent.Type.VPN_PORT_SET == event.type()) {
+ onVpnPortSet(vpnPort);
+ }
+ }
+ }
+
+ @Override
+ public void onVpnPortDelete(VpnPort vpnPort) {
+ // delete the flows of this vpn
+ hostService.getHosts().forEach(host -> {
+ VpnPortId vpnPortId = vpnPort.id();
+ VpnInstanceId vpnInstanceId = vpnPort.vpnInstanceId();
+ if (!vpnInstanceService.exists(vpnInstanceId)) {
+ log.error(CANT_FIND_VPN_INSTANCE, vpnInstanceId);
+ return;
+ }
+ VpnInstance vpnInstance = vpnInstanceService
+ .getInstance(vpnInstanceId);
+ List<VpnRouteTarget> rtImport
+ = new LinkedList<>(vpnInstance.getImportRouteTargets());
+ List<VpnRouteTarget> rtExport
+ = new LinkedList<>(vpnInstance.getExportRouteTargets());
+
+ if (vpnPortId.vpnPortId()
+ .equals(host.annotations().value(IFACEID))) {
+ log.info(VPN_PORT_UNBIND);
+ Label label = releaseLabel(vpnInstance, host);
+ // create private route and get label
+ DeviceId deviceId = host.location().deviceId();
+ setPrivateRoute(host, vpnInstanceId, label,
+ Objective.Operation.REMOVE);
+ // download flows
+ setFlows(deviceId, host, label, rtImport,
+ Objective.Operation.REMOVE);
+ }
+ });
+ }
+
+ @Override
+ public void onVpnPortSet(VpnPort vpnPort) {
+ // delete the flows of this vpn
+ hostService.getHosts().forEach(host -> {
+ VpnPortId vpnPortId = vpnPort.id();
+ VpnInstanceId vpnInstanceId = vpnPort.vpnInstanceId();
+ VpnInstance vpnInstance = vpnInstanceService
+ .getInstance(vpnInstanceId);
+ if (vpnInstance == null) {
+ log.info("why vpn instance is null");
+ return;
+ }
+ List<VpnRouteTarget> rtImport
+ = new LinkedList<>(vpnInstance.getImportRouteTargets());
+/* List<VpnRouteTarget> rtExport
+ = new LinkedList<>(vpnInstance.getExportRouteTargets());*/
+
+ if (!vpnInstanceService.exists(vpnInstanceId)) {
+ log.error(CANT_FIND_VPN_INSTANCE, vpnInstanceId);
+ return;
+ }
+
+ if (vpnPortId.vpnPortId()
+ .equals(host.annotations().value(IFACEID))) {
+ log.info(VPN_PORT_BIND);
+ Label label = applyLabel();
+ // create private route and get label
+ DeviceId deviceId = host.location().deviceId();
+ setPrivateRoute(host, vpnInstanceId, label,
+ Objective.Operation.ADD);
+ // download flows
+ setFlows(deviceId, host, label, rtImport,
+ Objective.Operation.ADD);
+ }
+ });
+ }
+
+ /**
+ * process the gluon configuration and will update the configuration into
+ * vpn port service.
+ *
+ * @param action action
+ * @param key key
+ * @param value json node
+ */
+ private void processEtcdResponse(String action, String key, JsonNode
+ value) {
+ String[] list = key.split(SLASH);
+ String target = list[list.length - 2];
+ switch (target) {
+ case VPN_INSTANCE_TARGET:
+ VpnInstanceService vpnInstanceService
+ = DefaultServiceDirectory
+ .getService(VpnInstanceService.class);
+ vpnInstanceService.processGluonConfig(action, key, value);
+ break;
+ case VPN_PORT_TARGET:
+ VpnPortService vpnPortService = DefaultServiceDirectory
+ .getService(VpnPortService.class);
+ vpnPortService.processGluonConfig(action, key, value);
+ break;
+ case VPN_AF_TARGET:
+ VpnAfConfigService vpnAfConfigService =
+ DefaultServiceDirectory.getService(VpnAfConfigService
+ .class);
+ vpnAfConfigService.processGluonConfig(action, key, value);
+ break;
+ case BASEPORT:
+ BasePortService basePortService =
+ DefaultServiceDirectory.getService(BasePortService
+ .class);
+ basePortService.processGluonConfig(action, key, value);
+ break;
+ default:
+ log.info("why target type is invalid {}", target);
+ log.info(INVALID_TARGET_RECEIVED);
+ break;
+ }
+ }
+
+ /**
+ * parse the gluon configuration received from network config system.
+ *
+ * @param jsonNode json node
+ * @param key key
+ * @param action action
+ */
+ private void parseEtcdResponse(JsonNode jsonNode,
+ String key,
+ String action) {
+ JsonNode modifyValue = null;
+ if (action.equals(SET)) {
+ modifyValue = jsonNode.get(key);
+ }
+ processEtcdResponse(action, key, modifyValue);
+ }
+
+ /**
+ * Listener for network config events.
+ */
+ private class InternalNetworkConfigListener implements
+ NetworkConfigListener {
+
+ @Override
+ public void event(NetworkConfigEvent event) {
+ String subject;
+ log.info(NETWORK_CONFIG_EVENT_IS_RECEIVED, event.type());
+ if (!event.configClass().equals(GluonConfig.class)) {
+ return;
+ }
+ log.info("Event is received from network configuration {}", event
+ .type());
+ switch (event.type()) {
+ case CONFIG_UPDATED:
+ subject = (String) event.subject();
+ GluonConfig gluonConfig = configService
+ .getConfig(subject, GluonConfig.class);
+ JsonNode jsonNode = gluonConfig.node();
+ parseEtcdResponse(jsonNode, subject, SET);
+ break;
+ case CONFIG_REMOVED:
+ subject = (String) event.subject();
+ parseEtcdResponse(null, subject, DELETE);
+ break;
+ default:
+ log.info(INVALID_EVENT_RECEIVED);
+ break;
+ }
+ }
+ }
+
+ /**
+ * update import and export route target information in route admin service.
+ *
+ * @param evpnInstanceName evpn instance name
+ * @param exportRouteTargets export route targets
+ * @param importRouteTargets import route targets
+ * @param action action holds update or delete
+ */
+ private void updateImpExpRtInRoute(EvpnInstanceName evpnInstanceName,
+ Set<VpnRouteTarget> exportRouteTargets,
+ Set<VpnRouteTarget> importRouteTargets,
+ String action) {
+
+ for (EvpnInstanceRoute evpnInstanceRoute : evpnInstanceRoutes) {
+ if (evpnInstanceRoute.evpnInstanceName().equals(evpnInstanceName)) {
+ evpnInstanceRoute
+ .setExportRtList(new LinkedList<>(exportRouteTargets));
+ evpnInstanceRoute
+ .setImportRtList(new LinkedList<>(importRouteTargets));
+ if (action.equals(UPDATE)) {
+ evpnInstanceRoutes.add(evpnInstanceRoute);
+ } else if (action.equals(DELETE)) {
+ evpnInstanceRoutes.remove(evpnInstanceRoute);
+ }
+ //Get the public route and update route targets.
+ EvpnNextHop evpnNextHop = EvpnNextHop
+ .evpnNextHop(evpnInstanceRoute.getNextHopl(),
+ evpnInstanceRoute.importRouteTarget(),
+ evpnInstanceRoute.exportRouteTarget(),
+ evpnInstanceRoute.getLabel());
+ Collection<EvpnRoute> evpnPublicRoutes
+ = evpnRouteStore.getRoutesForNextHop(evpnNextHop.nextHop());
+ for (EvpnRoute pubRoute : evpnPublicRoutes) {
+ EvpnRoute evpnPubRoute = pubRoute;
+ if (evpnPubRoute.label().equals(evpnInstanceRoute
+ .getLabel())) {
+ evpnPubRoute
+ .setExportRtList(new LinkedList<>(exportRouteTargets));
+ evpnPubRoute
+ .setImportRtList(new LinkedList<>(importRouteTargets));
+ if (action.equals(UPDATE)) {
+ evpnRouteAdminService.update(Sets.newHashSet(evpnPubRoute));
+ } else if (action.equals(DELETE)) {
+ evpnRouteAdminService
+ .withdraw(Sets.newHashSet(evpnPubRoute));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * update or withdraw evpn route based on vpn af configuration.
+ *
+ * @param vpnAfConfig vpn af configuration
+ * @param action action holds update or delete
+ */
+
+ private void processEvpnRouteUpdate(VpnAfConfig vpnAfConfig,
+ String action) {
+ Collection<VpnInstance> instances
+ = vpnInstanceService.getInstances();
+ for (VpnInstance vpnInstance : instances) {
+ Set<VpnRouteTarget> configRouteTargets
+ = vpnInstance.getConfigRouteTargets();
+ for (VpnRouteTarget vpnRouteTarget : configRouteTargets) {
+ if (vpnRouteTarget.equals(vpnAfConfig.routeTarget())) {
+ Set<VpnRouteTarget> exportRouteTargets
+ = vpnInstance.getExportRouteTargets();
+ Set<VpnRouteTarget> importRouteTargets
+ = vpnInstance.getImportRouteTargets();
+ String routeTargetType = vpnAfConfig.routeTargetType();
+ if (action.equals(UPDATE)) {
+ vpnInstanceService
+ .updateImpExpRouteTargets(routeTargetType,
+ exportRouteTargets,
+ importRouteTargets,
+ vpnRouteTarget);
+ } else if (action.equals(DELETE)) {
+ switch (routeTargetType) {
+ case EXPORT_EXTCOMMUNITY:
+ exportRouteTargets.remove(vpnRouteTarget);
+ break;
+ case IMPORT_EXTCOMMUNITY:
+ importRouteTargets.remove(vpnRouteTarget);
+ break;
+ case BOTH:
+ exportRouteTargets.remove(vpnRouteTarget);
+ importRouteTargets.remove(vpnRouteTarget);
+ break;
+ default:
+ log.info(INVALID_ROUTE_TARGET_TYPE);
+ break;
+ }
+ }
+ updateImpExpRtInRoute(vpnInstance.vpnInstanceName(),
+ exportRouteTargets,
+ importRouteTargets,
+ action);
+ }
+ }
+ }
+ }
+
+ private class InnerVpnAfConfigListener implements VpnAfConfigListener {
+
+ @Override
+ public void event(VpnAfConfigEvent event) {
+ VpnAfConfig vpnAfConfig = event.subject();
+ if (VpnAfConfigEvent.Type.VPN_AF_CONFIG_DELETE == event.type()) {
+ processEvpnRouteUpdate(vpnAfConfig, DELETE);
+ } else if (VpnAfConfigEvent.Type.VPN_AF_CONFIG_SET
+ == event.type() || VpnAfConfigEvent.Type
+ .VPN_AF_CONFIG_UPDATE == event.type()) {
+ processEvpnRouteUpdate(vpnAfConfig, UPDATE);
+ }
+ }
+ }
+}