blob: 7f7314b46582ab6942fb5bd70d6488659f0d4d0c [file] [log] [blame]
/*
* 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);
}
}
}
}