2 * Copyright (c) 2015 - 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netvirt.vpnmanager;
10 import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
11 import static org.opendaylight.mdsal.binding.api.WriteTransaction.CREATE_MISSING_PARENTS;
13 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Objects;
20 import java.util.Optional;
22 import java.util.concurrent.ExecutionException;
23 import java.util.concurrent.Future;
24 import java.util.concurrent.locks.ReentrantLock;
25 import javax.annotation.PostConstruct;
26 import javax.inject.Inject;
27 import javax.inject.Singleton;
28 import org.eclipse.jdt.annotation.NonNull;
29 import org.eclipse.jdt.annotation.Nullable;
30 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
31 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
32 import org.opendaylight.genius.infra.Datastore.Configuration;
33 import org.opendaylight.genius.infra.Datastore.Operational;
34 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
35 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
36 import org.opendaylight.genius.infra.TypedReadTransaction;
37 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
38 import org.opendaylight.genius.infra.TypedWriteTransaction;
39 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
40 import org.opendaylight.genius.mdsalutil.FlowEntity;
41 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
42 import org.opendaylight.genius.mdsalutil.NwConstants;
43 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
44 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
45 import org.opendaylight.genius.utils.JvmGlobalLocks;
46 import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
47 import org.opendaylight.infrautils.utils.function.InterruptibleCheckedConsumer;
48 import org.opendaylight.mdsal.binding.api.DataBroker;
49 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
50 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
51 import org.opendaylight.netvirt.elan.arp.responder.ArpResponderInput;
52 import org.opendaylight.netvirt.elan.arp.responder.ArpResponderInput.ArpReponderInputBuilder;
53 import org.opendaylight.netvirt.elan.arp.responder.ArpResponderUtil;
54 import org.opendaylight.netvirt.elanmanager.api.IElanService;
55 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
56 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
57 import org.opendaylight.netvirt.neutronvpn.api.enums.IpVersionChoice;
58 import org.opendaylight.netvirt.vpnmanager.api.IVpnManager;
59 import org.opendaylight.netvirt.vpnmanager.api.InterfaceUtils;
60 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
61 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.IVpnLinkService;
62 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkCache;
63 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkDataComposite;
64 import org.opendaylight.netvirt.vpnmanager.populator.input.L3vpnInput;
65 import org.opendaylight.netvirt.vpnmanager.populator.registry.L3vpnRegistry;
66 import org.opendaylight.serviceutils.upgrade.UpgradeState;
67 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
68 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInput;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInputBuilder;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolOutput;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.SubnetsAssociatedToRouteTargets;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnets.associated.to.route.targets.RouteTarget;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnets.associated.to.route.targets.RouteTargetKey;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnets.associated.to.route.targets.route.target.AssociatedSubnet;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnets.associated.to.route.targets.route.target.associated.subnet.AssociatedVpn;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.VpnInstances;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.adjacency.list.Adjacency;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.vpn.instances.VpnInstance;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.l3vpn.rev200204.vpn.instances.vpn.instance.vpntargets.VpnTarget;
88 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.portip.port.data.VpnPortipToPort;
89 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
90 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
91 import org.opendaylight.yangtools.yang.common.RpcError;
92 import org.opendaylight.yangtools.yang.common.RpcResult;
93 import org.opendaylight.yangtools.yang.common.Uint32;
94 import org.opendaylight.yangtools.yang.common.Uint64;
95 import org.slf4j.Logger;
96 import org.slf4j.LoggerFactory;
99 public class VpnManagerImpl implements IVpnManager {
101 private static final Logger LOG = LoggerFactory.getLogger(VpnManagerImpl.class);
102 private final DataBroker dataBroker;
103 private final ManagedNewTransactionRunner txRunner;
104 private final IdManagerService idManager;
105 private final IMdsalApiManager mdsalManager;
106 private final IElanService elanService;
107 private final IInterfaceManager interfaceManager;
108 private final VpnSubnetRouteHandler vpnSubnetRouteHandler;
109 private final OdlInterfaceRpcService ifaceMgrRpcService;
110 private final IVpnLinkService ivpnLinkService;
111 private final IFibManager fibManager;
112 private final IBgpManager bgpManager;
113 private final InterVpnLinkCache interVpnLinkCache;
114 private final DataTreeEventCallbackRegistrar eventCallbacks;
115 private final UpgradeState upgradeState;
116 private final ItmRpcService itmRpcService;
117 private final VpnUtil vpnUtil;
120 public VpnManagerImpl(final DataBroker dataBroker,
121 final IdManagerService idManagerService,
122 final IMdsalApiManager mdsalManager,
123 final IElanService elanService,
124 final IInterfaceManager interfaceManager,
125 final VpnSubnetRouteHandler vpnSubnetRouteHandler,
126 final OdlInterfaceRpcService ifaceMgrRpcService,
127 final IVpnLinkService ivpnLinkService,
128 final IFibManager fibManager,
129 final IBgpManager bgpManager,
130 final InterVpnLinkCache interVpnLinkCache,
131 final DataTreeEventCallbackRegistrar dataTreeEventCallbackRegistrar,
132 final UpgradeState upgradeState,
133 final ItmRpcService itmRpcService,
134 final VpnUtil vpnUtil) {
135 this.dataBroker = dataBroker;
136 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
137 this.idManager = idManagerService;
138 this.mdsalManager = mdsalManager;
139 this.elanService = elanService;
140 this.interfaceManager = interfaceManager;
141 this.vpnSubnetRouteHandler = vpnSubnetRouteHandler;
142 this.ifaceMgrRpcService = ifaceMgrRpcService;
143 this.ivpnLinkService = ivpnLinkService;
144 this.fibManager = fibManager;
145 this.bgpManager = bgpManager;
146 this.interVpnLinkCache = interVpnLinkCache;
147 this.eventCallbacks = dataTreeEventCallbackRegistrar;
148 this.upgradeState = upgradeState;
149 this.itmRpcService = itmRpcService;
150 this.vpnUtil = vpnUtil;
154 public void start() {
155 LOG.info("{} start", getClass().getSimpleName());
159 private void createIdPool() {
160 CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
161 .setPoolName(VpnConstants.VPN_IDPOOL_NAME)
162 .setLow(VpnConstants.VPN_IDPOOL_LOW)
163 .setHigh(VpnConstants.VPN_IDPOOL_HIGH)
166 Future<RpcResult<CreateIdPoolOutput>> result = idManager.createIdPool(createPool);
167 if (result != null && result.get().isSuccessful()) {
168 LOG.info("Created IdPool for VPN Service");
170 LOG.error("createIdPool: Unable to create ID pool for VPNService");
172 } catch (InterruptedException | ExecutionException e) {
173 LOG.error("Failed to create idPool for VPN Service", e);
176 // Now an IdPool for InterVpnLink endpoint's pseudo ports
177 CreateIdPoolInput createPseudoLporTagPool =
178 new CreateIdPoolInputBuilder().setPoolName(VpnConstants.PSEUDO_LPORT_TAG_ID_POOL_NAME)
179 .setLow(VpnConstants.LOWER_PSEUDO_LPORT_TAG)
180 .setHigh(VpnConstants.UPPER_PSEUDO_LPORT_TAG)
183 Future<RpcResult<CreateIdPoolOutput>> result = idManager.createIdPool(createPseudoLporTagPool);
184 if (result != null && result.get().isSuccessful()) {
185 LOG.debug("Created IdPool for Pseudo Port tags");
187 StringBuilder errMsg = new StringBuilder();
188 if (result != null && result.get() != null) {
189 Collection<RpcError> errors = result.get().getErrors();
190 for (RpcError err : errors) {
191 errMsg.append(err.getMessage()).append("\n");
194 LOG.error("IdPool creation for PseudoPort tags failed. Reasons: {}", errMsg);
196 } catch (InterruptedException | ExecutionException e) {
197 LOG.error("Failed to create idPool for Pseudo Port tags", e);
202 public void addExtraRoute(String vpnName, String destination, String nextHop, String rd, @Nullable String routerID,
203 Uint32 l3vni, RouteOrigin origin, @Nullable String intfName, @Nullable Adjacency operationalAdj,
204 VrfEntry.EncapType encapType, Set<String> prefixListForRefreshFib,
205 @NonNull TypedWriteTransaction<Configuration> confTx) {
206 //add extra route to vpn mapping; advertise with nexthop as tunnel ip
207 vpnUtil.syncUpdate(LogicalDatastoreType.OPERATIONAL,
208 VpnExtraRouteHelper.getVpnToExtrarouteVrfIdIdentifier(vpnName, rd != null ? rd : routerID,
210 VpnUtil.getVpnToExtraroute(destination, Collections.singletonList(nextHop)));
213 if (intfName != null && !intfName.isEmpty()) {
214 dpnId = InterfaceUtils.getDpnForInterface(ifaceMgrRpcService, intfName);
215 String nextHopIp = InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, dpnId);
216 if (nextHopIp == null || nextHopIp.isEmpty()) {
217 LOG.error("addExtraRoute: NextHop for interface {} is null / empty."
218 + " Failed advertising extra route for rd {} prefix {} dpn {}", intfName, rd, destination,
225 String primaryRd = vpnUtil.getPrimaryRd(vpnName);
227 // TODO: This is a limitation to be stated in docs. When configuring static route to go to
228 // another VPN, there can only be one nexthop or, at least, the nexthop to the interVpnLink should be in
230 Optional<InterVpnLinkDataComposite> optVpnLink = interVpnLinkCache.getInterVpnLinkByEndpoint(nextHop);
231 if (optVpnLink.isPresent() && optVpnLink.get().isActive()) {
232 InterVpnLinkDataComposite interVpnLink = optVpnLink.get();
233 // If the nexthop is the endpoint of Vpn2, then prefix must be advertised to Vpn1 in DC-GW, with nexthops
234 // pointing to the DPNs where Vpn1 is instantiated. LFIB in these DPNS must have a flow entry, with lower
235 // priority, where if Label matches then sets the lportTag of the Vpn2 endpoint and goes to LportDispatcher
236 // This is like leaking one of the Vpn2 routes towards Vpn1
237 String srcVpnUuid = interVpnLink.getVpnNameByIpAddress(nextHop);
238 String dstVpnUuid = interVpnLink.getOtherVpnNameByIpAddress(nextHop);
239 String dstVpnRd = vpnUtil.getVpnRd(dstVpnUuid);
240 Uint32 newLabel = vpnUtil.getUniqueId(VpnConstants.VPN_IDPOOL_NAME,
241 VpnUtil.getNextHopLabelKey(dstVpnRd, destination));
242 if (newLabel.longValue() == VpnConstants.INVALID_LABEL) {
243 LOG.error("addExtraRoute: Unable to fetch label from Id Manager. Bailing out of adding intervpnlink"
244 + " route for destination {}", destination);
247 ivpnLinkService.leakRoute(interVpnLink, srcVpnUuid, dstVpnUuid, destination, newLabel, RouteOrigin.STATIC);
249 Optional<Routes> optVpnExtraRoutes = VpnExtraRouteHelper
250 .getVpnExtraroutes(dataBroker, vpnName, rd != null ? rd : routerID, destination);
251 if (optVpnExtraRoutes.isPresent()) {
252 List<String> nhList = optVpnExtraRoutes.get().getNexthopIpList();
253 if (nhList != null && nhList.size() > 1) {
254 // If nhList is greater than one for vpnextraroute, a call to populatefib doesn't update vrfentry.
255 prefixListForRefreshFib.add(destination);
257 L3vpnInput input = new L3vpnInput().setNextHop(operationalAdj).setNextHopIp(nextHop)
258 .setL3vni(l3vni.longValue())
259 .setPrimaryRd(primaryRd).setVpnName(vpnName).setDpnId(dpnId)
260 .setEncapType(encapType).setRd(rd).setRouteOrigin(origin);
261 L3vpnRegistry.getRegisteredPopulator(encapType).populateFib(input, confTx);
268 public void delExtraRoute(String vpnName, String destination, String nextHop, String rd, @Nullable String routerID,
269 @Nullable String intfName, @NonNull TypedWriteTransaction<Configuration> confTx,
270 @NonNull TypedWriteTransaction<Operational> operTx) {
272 String tunnelIp = nextHop;
273 if (intfName != null && !intfName.isEmpty()) {
274 dpnId = InterfaceUtils.getDpnForInterface(ifaceMgrRpcService, intfName);
275 String nextHopIp = InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, dpnId);
276 if (nextHopIp == null || nextHopIp.isEmpty()) {
277 LOG.error("delExtraRoute: NextHop for interface {} is null / empty."
278 + " Failed advertising extra route for rd {} prefix {} dpn {}", intfName, rd, destination,
281 tunnelIp = nextHopIp;
284 String primaryRd = vpnUtil.getVpnRd(vpnName);
285 removePrefixFromBGP(vpnName, primaryRd, rd, intfName, destination,
286 nextHop, tunnelIp, dpnId, confTx, operTx);
287 LOG.info("delExtraRoute: Removed extra route {} from interface {} for rd {}", destination, intfName, rd);
289 // add FIB route directly
290 fibManager.removeOrUpdateFibEntry(routerID, destination, tunnelIp, confTx);
291 LOG.info("delExtraRoute: Removed extra route {} from interface {} for rd {}", destination, intfName,
297 @SuppressWarnings("checkstyle:IllegalCatch")
298 public void removePrefixFromBGP(String vpnName, String primaryRd, String extraRouteRd, String vpnInterfaceName,
299 String prefix, String nextHop, String nextHopTunnelIp, Uint64 dpnId,
300 TypedWriteTransaction<Configuration> confTx,
301 TypedWriteTransaction<Operational> operTx) {
302 String vpnNamePrefixKey = VpnUtil.getVpnNamePrefixKey(vpnName, prefix);
303 // FIXME: separate out to somehow?
304 final ReentrantLock lock = JvmGlobalLocks.getLockForString(vpnNamePrefixKey);
305 LOG.info("removing prefix {} for nexthop {} in VPN {} rd {}", prefix, nextHop, vpnName, extraRouteRd);
308 if (vpnUtil.removeOrUpdateDSForExtraRoute(vpnName, primaryRd, extraRouteRd, vpnInterfaceName, prefix,
309 nextHop, nextHopTunnelIp, operTx)) {
312 fibManager.removeOrUpdateFibEntry(primaryRd, prefix, nextHopTunnelIp, confTx);
313 if (VpnUtil.isEligibleForBgp(extraRouteRd, vpnName, dpnId, null /*networkName*/)) {
314 // TODO: Might be needed to include nextHop here
315 bgpManager.withdrawPrefix(extraRouteRd, prefix);
317 LOG.info("removePrefixFromBGP: VPN WITHDRAW: Removed Fib Entry rd {} prefix {} nexthop {}", extraRouteRd,
319 } catch (RuntimeException e) {
320 LOG.error("removePrefixFromBGP: Delete prefix {} rd {} nextHop {} failed", prefix, extraRouteRd, nextHop);
327 public boolean isVPNConfigured() {
328 InstanceIdentifier<VpnInstances> vpnsIdentifier = InstanceIdentifier.builder(VpnInstances.class).build();
330 Optional<VpnInstances> optionalVpns =
331 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
333 if (!optionalVpns.isPresent()
334 || optionalVpns.get().getVpnInstance() == null
335 || optionalVpns.get().getVpnInstance().isEmpty()) {
336 LOG.trace("isVPNConfigured: No VPNs configured.");
339 } catch (InterruptedException | ExecutionException e) {
340 throw new RuntimeException("Error reading VPN " + vpnsIdentifier, e);
342 LOG.trace("isVPNConfigured: VPNs are configured on the system.");
347 public void addSubnetMacIntoVpnInstance(String vpnName, String subnetVpnName, String srcMacAddress,
348 Uint64 dpnId, TypedWriteTransaction<Configuration> confTx)
349 throws ExecutionException, InterruptedException {
350 setupSubnetMacInVpnInstance(vpnName, subnetVpnName, srcMacAddress, dpnId,
351 (vpnId, dpId, subnetVpnId) -> addGwMac(srcMacAddress, confTx, vpnId, dpId, subnetVpnId));
355 public void removeSubnetMacFromVpnInstance(String vpnName, String subnetVpnName, String srcMacAddress,
356 Uint64 dpnId, TypedReadWriteTransaction<Configuration> confTx)
357 throws ExecutionException, InterruptedException {
358 setupSubnetMacInVpnInstance(vpnName, subnetVpnName, srcMacAddress, dpnId,
359 (vpnId, dpId, subnetVpnId) -> removeGwMac(srcMacAddress, confTx, vpnId, dpId, subnetVpnId));
363 private interface VpnInstanceSubnetMacSetupMethod {
364 void process(Uint32 vpnId, Uint64 dpId, Uint32 subnetVpnId) throws InterruptedException, ExecutionException;
367 private void setupSubnetMacInVpnInstance(String vpnName, String subnetVpnName, String srcMacAddress,
368 Uint64 dpnId, VpnInstanceSubnetMacSetupMethod consumer)
369 throws ExecutionException, InterruptedException {
370 if (vpnName == null) {
371 LOG.warn("Cannot setup subnet MAC {} on DPN {}, null vpnName", srcMacAddress, dpnId);
375 Uint32 vpnId = vpnUtil.getVpnId(vpnName);
376 Uint32 subnetVpnId = vpnUtil.getVpnId(subnetVpnName);
377 if (dpnId.equals(Uint64.ZERO)) {
378 /* Apply the MAC on all DPNs in a VPN */
379 for (Uint64 dpId : vpnUtil.getDpnsOnVpn(vpnName)) {
380 consumer.process(vpnId, dpId, subnetVpnId);
383 consumer.process(vpnId, dpnId, subnetVpnId);
387 private void addGwMac(String srcMacAddress, TypedWriteTransaction<Configuration> tx, Uint32 vpnId, Uint64 dpId,
388 Uint32 subnetVpnId) {
389 FlowEntity flowEntity = vpnUtil.buildL3vpnGatewayFlow(dpId, srcMacAddress, vpnId, subnetVpnId);
390 mdsalManager.addFlow(tx, flowEntity);
393 // TODO skitt Fix the exception handling here
394 @SuppressWarnings("checkstyle:IllegalCatch")
395 @SuppressFBWarnings("REC_CATCH_EXCEPTION")
396 private void removeGwMac(String srcMacAddress, TypedReadWriteTransaction<Configuration> tx, Uint32 vpnId,
397 Uint64 dpId, Uint32 subnetVpnId) throws ExecutionException, InterruptedException {
398 mdsalManager.removeFlow(tx, dpId,
399 VpnUtil.getL3VpnGatewayFlowRef(NwConstants.L3_GW_MAC_TABLE, dpId, vpnId, srcMacAddress, subnetVpnId),
400 NwConstants.L3_GW_MAC_TABLE);
404 public void addRouterGwMacFlow(String routerName, String routerGwMac, Uint64 dpnId, Uuid extNetworkId,
405 String subnetVpnName, TypedWriteTransaction<Configuration> confTx)
406 throws ExecutionException, InterruptedException {
407 setupRouterGwMacFlow(routerName, routerGwMac, dpnId, extNetworkId,
408 vpnId -> addSubnetMacIntoVpnInstance(vpnId, subnetVpnName, routerGwMac, dpnId, confTx), "Installing");
412 public void removeRouterGwMacFlow(String routerName, String routerGwMac, Uint64 dpnId, Uuid extNetworkId,
413 String subnetVpnName, TypedReadWriteTransaction<Configuration> confTx)
414 throws ExecutionException, InterruptedException {
415 setupRouterGwMacFlow(routerName, routerGwMac, dpnId, extNetworkId,
416 vpnId -> removeSubnetMacFromVpnInstance(vpnId, subnetVpnName, routerGwMac, dpnId, confTx), "Removing");
419 private void setupRouterGwMacFlow(String routerName, String routerGwMac, Uint64 dpnId, Uuid extNetworkId,
420 InterruptibleCheckedConsumer<String, ExecutionException> consumer, String operation)
421 throws ExecutionException, InterruptedException {
422 if (routerGwMac == null) {
423 LOG.warn("Failed to handle router GW flow in GW-MAC table. MAC address is missing for router-id {}",
428 if (dpnId == null || Uint64.ZERO.equals(dpnId)) {
429 LOG.info("setupRouterGwMacFlow: DPN id is missing for router-id {}",
434 Uuid vpnId = vpnUtil.getExternalNetworkVpnId(extNetworkId);
436 LOG.warn("Network {} is not associated with VPN", extNetworkId.getValue());
440 LOG.info("{} router GW MAC flow for router-id {} on switch {}", operation, routerName, dpnId);
441 consumer.accept(vpnId.getValue());
445 public void addArpResponderFlowsToExternalNetworkIps(String id, Collection<String> fixedIps, String macAddress,
446 Uint64 dpnId, Uuid extNetworkId) {
448 if (dpnId == null || Uint64.ZERO.equals(dpnId)) {
449 LOG.warn("Failed to install arp responder flows for router {}. DPN id is missing.", id);
453 String extInterfaceName = elanService.getExternalElanInterface(extNetworkId.getValue(), dpnId);
454 if (extInterfaceName != null) {
455 doAddArpResponderFlowsToExternalNetworkIps(
456 id, fixedIps, macAddress, dpnId, extInterfaceName);
460 LOG.warn("Failed to install responder flows for {}. No external interface found for DPN id {}", id, dpnId);
462 if (!upgradeState.isUpgradeInProgress()) {
466 // The following through the end of the function deals with an upgrade scenario where the neutron configuration
467 // is restored before the OVS switches reconnect. In such a case, the elan-dpn-interfaces entries will be
468 // missing from the operational data store. In order to mitigate this we use DataTreeEventCallbackRegistrar
469 // to wait for the exact operational md-sal object we need to contain the external interface we need.
471 LOG.info("Upgrade in process, waiting for an external interface to appear on dpn {} for elan {}",
472 dpnId, extNetworkId.getValue());
474 InstanceIdentifier<DpnInterfaces> dpnInterfacesIid =
475 elanService.getElanDpnInterfaceOperationalDataPath(extNetworkId.getValue(), dpnId);
477 eventCallbacks.onAddOrUpdate(LogicalDatastoreType.OPERATIONAL, dpnInterfacesIid, (unused, alsoUnused) -> {
478 LOG.info("Reattempting write of arp responder for external interfaces for external network {}",
480 DpnInterfaces dpnInterfaces = elanService.getElanInterfaceInfoByElanDpn(extNetworkId.getValue(), dpnId);
481 if (dpnInterfaces == null) {
482 LOG.error("Could not retrieve DpnInterfaces for {}, {}", extNetworkId.getValue(), dpnId);
483 return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER;
486 String extIfc = null;
487 @Nullable List<String> interfaces = dpnInterfaces.getInterfaces();
488 if (interfaces != null) {
489 for (String dpnInterface : interfaces) {
490 if (interfaceManager.isExternalInterface(dpnInterface)) {
491 extIfc = dpnInterface;
497 if (extIfc == null) {
498 if (upgradeState.isUpgradeInProgress()) {
499 LOG.info("External interface not found yet in elan {} on dpn {}, keep waiting",
500 extNetworkId.getValue(), dpnInterfaces);
501 return DataTreeEventCallbackRegistrar.NextAction.CALL_AGAIN;
503 return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER;
507 final String extIfcFinal = extIfc;
508 doAddArpResponderFlowsToExternalNetworkIps(id, fixedIps, macAddress, dpnId, extIfcFinal);
510 return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER;
516 public void addArpResponderFlowsToExternalNetworkIps(String id, Collection<String> fixedIps, String macAddress,
517 Uint64 dpnId, String extInterfaceName, int lportTag) {
518 if (fixedIps == null || fixedIps.isEmpty()) {
519 LOG.debug("No external IPs defined for {}", id);
523 LOG.info("Installing ARP responder flows for {} fixed-ips {} on switch {}", id, fixedIps, dpnId);
525 for (String fixedIp : fixedIps) {
526 IpVersionChoice ipVersionChoice = VpnUtil.getIpVersionFromString(fixedIp);
527 if (ipVersionChoice == IpVersionChoice.IPV6) {
530 installArpResponderFlowsToExternalNetworkIp(macAddress, dpnId, extInterfaceName, lportTag,
535 private void doAddArpResponderFlowsToExternalNetworkIps(String id, Collection<String> fixedIps, String macAddress,
536 Uint64 dpnId, String extInterfaceName) {
537 Interface extInterfaceState = InterfaceUtils.getInterfaceStateFromOperDS(dataBroker, extInterfaceName);
538 if (extInterfaceState == null) {
539 LOG.debug("No interface state found for interface {}. Delaying responder flows for {}", extInterfaceName,
544 Integer lportTag = extInterfaceState.getIfIndex();
545 if (lportTag == null) {
546 LOG.debug("No Lport tag found for interface {}. Delaying flows for router-id {}", extInterfaceName, id);
550 if (macAddress == null) {
552 LOG.debug("Failed to install arp responder flows for router-gateway-ip {} for router {}."
553 + "External Gw MacAddress is missing.", fixedIps, id);
557 addArpResponderFlowsToExternalNetworkIps(id, fixedIps, macAddress, dpnId, extInterfaceName, lportTag);
561 public void removeArpResponderFlowsToExternalNetworkIps(String id, Collection<String> fixedIps, String macAddress,
562 Uint64 dpnId, Uuid extNetworkId) {
564 if (dpnId == null || Uint64.ZERO.equals(dpnId)) {
565 LOG.warn("Failed to remove arp responder flows for router {}. DPN id is missing.", id);
569 String extInterfaceName = elanService.getExternalElanInterface(extNetworkId.getValue(), dpnId);
570 if (extInterfaceName == null) {
571 LOG.warn("Failed to remove responder flows for {}. No external interface found for DPN id {}", id, dpnId);
575 Interface extInterfaceState = InterfaceUtils.getInterfaceStateFromOperDS(dataBroker, extInterfaceName);
576 if (extInterfaceState == null) {
577 LOG.debug("No interface state found for interface {}. Delaying responder flows for {}", extInterfaceName,
582 Integer lportTag = extInterfaceState.getIfIndex();
583 if (lportTag == null) {
584 LOG.debug("No Lport tag found for interface {}. Delaying flows for router-id {}", extInterfaceName, id);
588 if (macAddress == null) {
590 LOG.debug("Failed to remove arp responder flows for router-gateway-ip {} for router {}."
591 + "External Gw MacAddress is missing.", fixedIps, id);
595 removeArpResponderFlowsToExternalNetworkIps(id, fixedIps, dpnId,
596 extInterfaceName, lportTag);
600 public void removeArpResponderFlowsToExternalNetworkIps(String id, Collection<String> fixedIps,
601 Uint64 dpnId, String extInterfaceName, int lportTag) {
602 if (fixedIps == null || fixedIps.isEmpty()) {
603 LOG.debug("No external IPs defined for {}", id);
607 LOG.info("Removing ARP responder flows for {} fixed-ips {} on switch {}", id, fixedIps, dpnId);
609 for (String fixedIp : fixedIps) {
610 removeArpResponderFlowsToExternalNetworkIp(dpnId, lportTag, fixedIp, extInterfaceName);
615 public String getPrimaryRdFromVpnInstance(VpnInstance vpnInstance) {
616 return VpnUtil.getPrimaryRd(vpnInstance);
619 private void installArpResponderFlowsToExternalNetworkIp(String macAddress, Uint64 dpnId,
620 String extInterfaceName, int lportTag, String fixedIp) {
621 // reset the split-horizon bit to allow traffic to be sent back to the
623 List<Instruction> instructions = new ArrayList<>();
625 new InstructionWriteMetadata(Uint64.ZERO,
626 MetaDataUtil.METADATA_MASK_SH_FLAG).buildInstruction(1));
628 ArpResponderUtil.getExtInterfaceInstructions(interfaceManager, itmRpcService, extInterfaceName,
629 fixedIp, macAddress));
630 ArpReponderInputBuilder builder = new ArpReponderInputBuilder().setDpId(dpnId.toJava())
631 .setInterfaceName(extInterfaceName).setSpa(fixedIp).setSha(macAddress).setLportTag(lportTag);
632 builder.setInstructions(instructions);
633 elanService.addArpResponderFlow(builder.buildForInstallFlow());
636 private void removeArpResponderFlowsToExternalNetworkIp(Uint64 dpnId, Integer lportTag, String fixedIp,
637 String extInterfaceName) {
638 ArpResponderInput arpInput = new ArpReponderInputBuilder()
639 .setDpId(dpnId.toJava()).setInterfaceName(extInterfaceName)
640 .setSpa(fixedIp).setLportTag(lportTag).buildForRemoveFlow();
641 elanService.removeArpResponderFlow(arpInput);
645 public void onSubnetAddedToVpn(Subnetmap subnetmap, boolean isBgpVpn, Long elanTag) {
646 vpnSubnetRouteHandler.onSubnetAddedToVpn(subnetmap, isBgpVpn, elanTag);
650 public void onSubnetDeletedFromVpn(Subnetmap subnetmap, boolean isBgpVpn) {
651 vpnSubnetRouteHandler.onSubnetDeletedFromVpn(subnetmap, isBgpVpn);
656 public VpnInstance getVpnInstance(DataBroker broker, String vpnInstanceName) {
657 return vpnUtil.getVpnInstance(vpnInstanceName);
661 public String getVpnRd(TypedReadTransaction<Configuration> confTx, String vpnName) {
662 return VpnUtil.getVpnRd(confTx, vpnName);
666 public String getVpnRd(DataBroker broker, String vpnName) {
667 return vpnUtil.getVpnRd(vpnName);
671 public VpnPortipToPort getNeutronPortFromVpnPortFixedIp(TypedReadTransaction<Configuration> confTx, String vpnName,
673 return VpnUtil.getNeutronPortFromVpnPortFixedIp(confTx, vpnName, fixedIp);
678 public VpnPortipToPort getNeutronPortFromVpnPortFixedIp(DataBroker broker, String vpnName, String fixedIp) {
679 return vpnUtil.getNeutronPortFromVpnPortFixedIp(vpnName, fixedIp);
683 public Set<VpnTarget> getRtListForVpn(String vpnName) {
684 return vpnUtil.getRtListForVpn(dataBroker, vpnName);
688 public void updateRouteTargetsToSubnetAssociation(Set<VpnTarget> routeTargets, String cidr, String vpnName) {
689 LoggingFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx -> {
690 for (VpnTarget rt : routeTargets) {
691 String rtValue = rt.getVrfRTValue();
692 switch (rt.getVrfRTType()) {
693 case ImportExtcommunity:
694 addSubnetAssociationOperationToTx(rtValue, RouteTarget.RtType.IRT, cidr, vpnName,
695 tx, false/*isAssociationRemoved*/);
697 case ExportExtcommunity:
698 addSubnetAssociationOperationToTx(rtValue, RouteTarget.RtType.ERT, cidr, vpnName,
699 tx, false/*isAssociationRemoved*/);
702 addSubnetAssociationOperationToTx(rtValue, RouteTarget.RtType.IRT, cidr, vpnName,
703 tx, false/*isAssociationRemoved*/);
704 addSubnetAssociationOperationToTx(rtValue, RouteTarget.RtType.ERT, cidr, vpnName,
705 tx, false/*isAssociationRemoved*/);
708 LOG.error("updateRouteTargetsToSubnetAssociation: Invalid rt-type {} for vpn {}"
709 + " subnet-cidr {}", rt.getVrfRTType(), vpnName, cidr);
713 }), LOG, "updateRouteTargetsToSubnetAssociation: Failed for vpn {} subnet-cidr {}", vpnName, cidr);
714 LOG.info("updateRouteTargetsToSubnetAssociation: Updated RT to subnet association for vpn {} cidr {}",
719 public void removeRouteTargetsToSubnetAssociation(Set<VpnTarget> routeTargets, String cidr, String vpnName) {
720 LoggingFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, tx -> {
721 for (VpnTarget rt : routeTargets) {
722 String rtValue = rt.getVrfRTValue();
723 switch (rt.getVrfRTType()) {
724 case ImportExtcommunity:
725 addSubnetAssociationOperationToTx(rtValue, RouteTarget.RtType.IRT, cidr, vpnName, tx,
726 true/*isAssociationRemoved*/);
728 case ExportExtcommunity:
729 addSubnetAssociationOperationToTx(rtValue, RouteTarget.RtType.ERT, cidr, vpnName, tx,
730 true/*isAssociationRemoved*/);
733 addSubnetAssociationOperationToTx(rtValue, RouteTarget.RtType.IRT, cidr, vpnName, tx,
734 true/*isAssociationRemoved*/);
735 addSubnetAssociationOperationToTx(rtValue, RouteTarget.RtType.ERT, cidr, vpnName, tx,
736 true/*isAssociationRemoved*/);
739 LOG.error("removeRouteTargetsToSubnetAssociation: Invalid route-target type {} for vpn {}"
740 + " subnet-cidr {}", rt.getVrfRTType(), vpnName, cidr);
744 }), LOG, "removeRouteTargetsToSubnetAssociation: Failed for vpn {} subnet-cidr {}", vpnName, cidr);
745 LOG.info("removeRouteTargetsToSubnetAssociation: vpn {} cidr {}", vpnName, cidr);
748 private boolean doesExistingVpnsHaveConflictingSubnet(Set<VpnTarget> routeTargets, String subnetCidr) {
749 Set<RouteTarget> routeTargetSet = vpnUtil.getRouteTargetSet(routeTargets);
750 for (RouteTarget routerTarget : routeTargetSet) {
751 if (routerTarget.getAssociatedSubnet() != null) {
752 for (int i = 0; i < routerTarget.getAssociatedSubnet().size(); i++) {
753 AssociatedSubnet associatedSubnet = routerTarget.getAssociatedSubnet().get(i);
754 if (VpnUtil.areSubnetsOverlapping(associatedSubnet.getCidr(), subnetCidr)) {
757 if (routerTarget.getRtType() == RouteTarget.RtType.ERT) {
758 /* Check if there are multiple exports for the input iRT value (iRT in routeTargets)
759 * Example : (1) iRT=A eRT=B subnet-range=S1; OK
760 * (2) iRT=A eRT=B subnet-range=S1; OK
761 * (3) iRT=B eRT=A subnet-range=S2; NOK
762 * Check if (1) and (2) are importing the same subnet-range routes to (3) */
763 List<AssociatedVpn> multipleAssociatedVpn = associatedSubnet.getAssociatedVpn();
764 if (multipleAssociatedVpn != null && multipleAssociatedVpn.size() > 1) {
765 LOG.error("doesExistingVpnsHaveConflictingSubnet: There is an indirect complete overlap"
766 + " for subnet CIDR {} for rt {} rtType {}", subnetCidr, routerTarget.getRt(),
767 routerTarget.getRtType());
770 for (int j = i + 1; j < routerTarget.getAssociatedSubnet().size(); j++) {
771 if (VpnUtil.areSubnetsOverlapping(associatedSubnet.getCidr(),
772 routerTarget.getAssociatedSubnet().get(j).getCidr())) {
773 LOG.error("doesExistingVpnsHaveConflictingSubnet: There is an indirect paartial"
774 + " overlap for subnet CIDR {} for rt {} rtType {}", subnetCidr,
775 routerTarget.getRt(), routerTarget.getRtType());
781 /* Check if there are indirect EXPORTS for the eRT value (eRT in routeTargets)
782 * Example : (1) iRT=A eRT=B subnet-range=S1; OK
783 * (2) iRT=B eRT=A subnet-range=S2; OK
784 * (3) iRT=A eRT=B subnet-range=S1; NOK
785 * If associatedSubnet is non-null for a routeTarget in (2),
786 * it may have already imported routes from (1) */
787 if (routerTarget.getRtType() == RouteTarget.RtType.IRT) {
789 Optional<RouteTarget> indirectRts = SingleTransactionDataBroker.syncReadOptional(dataBroker,
790 LogicalDatastoreType.OPERATIONAL, VpnUtil.getRouteTargetsIdentifier(
791 routerTarget.getRt(), RouteTarget.RtType.ERT));
792 if (indirectRts.isPresent() && indirectRts.get().getAssociatedSubnet() != null
793 && routerTarget.getAssociatedSubnet() != null) {
794 for (AssociatedSubnet associatedSubnet : indirectRts.get().getAssociatedSubnet()) {
795 if (VpnUtil.areSubnetsOverlapping(associatedSubnet.getCidr(), subnetCidr)) {
796 LOG.error("doesExistingVpnsHaveConflictingSubnet: There is an indirect overlap for"
797 + " subnet CIDR {} for rt {} rtType {}", subnetCidr, routerTarget.getRt(),
798 routerTarget.getRtType());
803 } catch (InterruptedException | ExecutionException e) {
804 LOG.error("doesExistingVpnsHaveConflictingSubnet: Failed to read route targets to subnet"
805 + "association for rt {} type {} subnet-cidr {}", routerTarget.getRt(),
806 RouteTarget.RtType.ERT, subnetCidr);
807 return true; //Fail subnet association to avoid further damage to the data-stores
811 LOG.info("doesExistingVpnsHaveConflictingSubnet: No prior associated subnets for rtVal {} rtType {}",
812 routerTarget.getRt(), routerTarget.getRtType());
818 private void addSubnetAssociationOperationToTx(String rt, RouteTarget.RtType rtType, String cidr,
819 String vpnName, TypedReadWriteTransaction<Operational> tx,
820 boolean isAssociationRemoved)
821 throws InterruptedException, ExecutionException {
822 if (isAssociationRemoved) {
823 //Remove RT-Subnet-Vpn Association
824 Optional<AssociatedSubnet> associatedSubnet =
825 tx.read(VpnUtil.getAssociatedSubnetIdentifier(rt, rtType, cidr)).get();
826 boolean deleteParent = false;
827 if (associatedSubnet.isPresent()) {
828 List<AssociatedVpn> associatedVpns = new ArrayList<>(associatedSubnet.get().nonnullAssociatedVpn());
829 if (associatedVpns == null || associatedVpns.isEmpty()) {
832 for (Iterator<AssociatedVpn> iterator = associatedVpns.iterator(); iterator.hasNext();) {
833 AssociatedVpn associatedVpn = iterator.next();
834 if (Objects.equals(associatedVpn.getName(), vpnName)) {
839 if (associatedVpns.isEmpty()) {
845 deleteParentForSubnetToVpnAssociation(rt, rtType, cidr, tx);
847 //Some other VPNs are also part of this rtVal, rtType and subnetCidr combination.
848 //Delete only this AssociatedVpn Object
849 tx.delete(VpnUtil.getAssociatedSubnetAndVpnIdentifier(rt, rtType, cidr, vpnName));
850 LOG.debug("addSubnetAssocOperationToTx: Removed vpn {} from association rt {} rtType {} cidr {}",
851 vpnName, rt, rtType, cidr);
854 //Add RT-Subnet-Vpn Association
855 tx.put(VpnUtil.getAssociatedSubnetAndVpnIdentifier(rt, rtType, cidr, vpnName),
856 VpnUtil.buildAssociatedSubnetAndVpn(vpnName), CREATE_MISSING_PARENTS);
860 private void deleteParentForSubnetToVpnAssociation(String rt, RouteTarget.RtType rtType,
861 String cidr, TypedReadWriteTransaction<Operational> tx)
862 throws InterruptedException, ExecutionException {
863 //Check if you need to delete rtVal+rtType or just the subnetCidr
864 InstanceIdentifier<RouteTarget> rtIdentifier = InstanceIdentifier
865 .builder(SubnetsAssociatedToRouteTargets.class).child(RouteTarget.class,
866 new RouteTargetKey(rt, rtType)).build();
867 Optional<RouteTarget> rtToSubnetsAssociation = tx.read(rtIdentifier).get();
868 if (rtToSubnetsAssociation.isPresent()) {
869 List<AssociatedSubnet> associatedSubnets = new ArrayList<>(rtToSubnetsAssociation.get()
870 .nonnullAssociatedSubnet());
871 if (associatedSubnets != null && !associatedSubnets.isEmpty()) {
872 for (Iterator<AssociatedSubnet> iterator = associatedSubnets.iterator(); iterator.hasNext(); ) {
873 if (Objects.equals(iterator.next().getCidr(), cidr)) {
878 if (associatedSubnets.isEmpty()) {
879 //The entire rt to subnet association is empty
880 //Delete the RouteTarget object
881 tx.delete(rtIdentifier);
882 LOG.debug("deleteParentForSubnetToVpnAssociation: Removed rt {} rtType {} from association,",
885 //Some other VPNs are also part of this rtVal, rtType combination
886 //Delete only this AssociatedSubnet
887 tx.delete(VpnUtil.getAssociatedSubnetIdentifier(rt, rtType, cidr));
888 LOG.debug("deleteParentForSubnetToVpnAssociation: Removed cidr {} from association rt {}"
889 + " rtType {}", cidr, rt, rtType);
896 public boolean checkForOverlappingSubnets(Uuid network, List<Subnetmap> subnetmapList, Uuid vpn,
897 Set<VpnTarget> routeTargets, List<String> failedNwList) {
898 for (Subnetmap subnetmap : subnetmapList) {
899 //Check if any other subnet that is already part of a different vpn with same rt, has overlapping CIDR
900 if (checkExistingSubnetWithSameRoutTargets(routeTargets, vpn, subnetmap, failedNwList)) {
907 private boolean checkExistingSubnetWithSameRoutTargets(Set<VpnTarget> routeTargets, Uuid vpn, Subnetmap subnetmap,
908 List<String> failedNwList) {
909 String cidr = String.valueOf(subnetmap.getSubnetIp());
910 boolean subnetExistsWithSameRt = doesExistingVpnsHaveConflictingSubnet(routeTargets, cidr);
911 if (subnetExistsWithSameRt) {
912 failedNwList.add(String.format("Another subnet with the same/overlapping cidr %s as subnet %s"
913 + " is already associated to a vpn with routeTargets %s. Ignoring subnet addition to vpn"
914 + " %s", cidr, subnetmap.getId().getValue(), routeTargets.toString(), vpn.getValue()));
916 return subnetExistsWithSameRt;