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 com.google.common.base.Optional;
11 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.Collections;
16 import java.util.Iterator;
17 import java.util.List;
18 import java.util.Objects;
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.Future;
22 import java.util.concurrent.locks.ReentrantLock;
23 import javax.annotation.PostConstruct;
24 import javax.inject.Inject;
25 import javax.inject.Singleton;
26 import org.eclipse.jdt.annotation.NonNull;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
29 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
30 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
31 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
32 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
33 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
34 import org.opendaylight.genius.infra.Datastore.Configuration;
35 import org.opendaylight.genius.infra.Datastore.Operational;
36 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
37 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
38 import org.opendaylight.genius.infra.TypedReadTransaction;
39 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
40 import org.opendaylight.genius.infra.TypedWriteTransaction;
41 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
42 import org.opendaylight.genius.mdsalutil.FlowEntity;
43 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
44 import org.opendaylight.genius.mdsalutil.NwConstants;
45 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
46 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
47 import org.opendaylight.genius.utils.JvmGlobalLocks;
48 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
49 import org.opendaylight.infrautils.utils.function.InterruptibleCheckedConsumer;
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.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
68 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.vpntargets.VpnTarget;
69 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
70 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
71 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
73 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInput;
74 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInputBuilder;
75 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolOutput;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
80 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
81 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.SubnetsAssociatedToRouteTargets;
82 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
83 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnets.associated.to.route.targets.RouteTarget;
84 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnets.associated.to.route.targets.RouteTargetKey;
85 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnets.associated.to.route.targets.route.target.AssociatedSubnet;
86 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnets.associated.to.route.targets.route.target.associated.subnet.AssociatedVpn;
87 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
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 } catch (InterruptedException | ExecutionException e) {
171 LOG.error("Failed to create idPool for VPN Service", e);
174 // Now an IdPool for InterVpnLink endpoint's pseudo ports
175 CreateIdPoolInput createPseudoLporTagPool =
176 new CreateIdPoolInputBuilder().setPoolName(VpnConstants.PSEUDO_LPORT_TAG_ID_POOL_NAME)
177 .setLow(VpnConstants.LOWER_PSEUDO_LPORT_TAG)
178 .setHigh(VpnConstants.UPPER_PSEUDO_LPORT_TAG)
181 Future<RpcResult<CreateIdPoolOutput>> result = idManager.createIdPool(createPseudoLporTagPool);
182 if (result.get().isSuccessful()) {
183 LOG.debug("Created IdPool for Pseudo Port tags");
185 Collection<RpcError> errors = result.get().getErrors();
186 StringBuilder errMsg = new StringBuilder();
187 for (RpcError err : errors) {
188 errMsg.append(err.getMessage()).append("\n");
190 LOG.error("IdPool creation for PseudoPort tags failed. Reasons: {}", errMsg);
192 } catch (InterruptedException | ExecutionException e) {
193 LOG.error("Failed to create idPool for Pseudo Port tags", e);
198 public void addExtraRoute(String vpnName, String destination, String nextHop, String rd, @Nullable String routerID,
199 Uint32 l3vni, RouteOrigin origin, @Nullable String intfName, @Nullable Adjacency operationalAdj,
200 VrfEntry.EncapType encapType, Set<String> prefixListForRefreshFib,
201 @NonNull TypedWriteTransaction<Configuration> confTx) {
202 //add extra route to vpn mapping; advertise with nexthop as tunnel ip
203 vpnUtil.syncUpdate(LogicalDatastoreType.OPERATIONAL,
204 VpnExtraRouteHelper.getVpnToExtrarouteVrfIdIdentifier(vpnName, rd != null ? rd : routerID,
206 VpnUtil.getVpnToExtraroute(destination, Collections.singletonList(nextHop)));
209 if (intfName != null && !intfName.isEmpty()) {
210 dpnId = InterfaceUtils.getDpnForInterface(ifaceMgrRpcService, intfName);
211 String nextHopIp = InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, dpnId);
212 if (nextHopIp == null || nextHopIp.isEmpty()) {
213 LOG.error("addExtraRoute: NextHop for interface {} is null / empty."
214 + " Failed advertising extra route for rd {} prefix {} dpn {}", intfName, rd, destination,
221 String primaryRd = vpnUtil.getPrimaryRd(vpnName);
223 // TODO: This is a limitation to be stated in docs. When configuring static route to go to
224 // another VPN, there can only be one nexthop or, at least, the nexthop to the interVpnLink should be in
226 Optional<InterVpnLinkDataComposite> optVpnLink = interVpnLinkCache.getInterVpnLinkByEndpoint(nextHop);
227 if (optVpnLink.isPresent() && optVpnLink.get().isActive()) {
228 InterVpnLinkDataComposite interVpnLink = optVpnLink.get();
229 // If the nexthop is the endpoint of Vpn2, then prefix must be advertised to Vpn1 in DC-GW, with nexthops
230 // pointing to the DPNs where Vpn1 is instantiated. LFIB in these DPNS must have a flow entry, with lower
231 // priority, where if Label matches then sets the lportTag of the Vpn2 endpoint and goes to LportDispatcher
232 // This is like leaking one of the Vpn2 routes towards Vpn1
233 String srcVpnUuid = interVpnLink.getVpnNameByIpAddress(nextHop);
234 String dstVpnUuid = interVpnLink.getOtherVpnNameByIpAddress(nextHop);
235 String dstVpnRd = vpnUtil.getVpnRd(dstVpnUuid);
236 Uint32 newLabel = vpnUtil.getUniqueId(VpnConstants.VPN_IDPOOL_NAME,
237 VpnUtil.getNextHopLabelKey(dstVpnRd, destination));
238 if (newLabel.longValue() == 0) {
239 LOG.error("addExtraRoute: Unable to fetch label from Id Manager. Bailing out of adding intervpnlink"
240 + " route for destination {}", destination);
243 ivpnLinkService.leakRoute(interVpnLink, srcVpnUuid, dstVpnUuid, destination, newLabel, RouteOrigin.STATIC);
245 Optional<Routes> optVpnExtraRoutes = VpnExtraRouteHelper
246 .getVpnExtraroutes(dataBroker, vpnName, rd != null ? rd : routerID, destination);
247 if (optVpnExtraRoutes.isPresent()) {
248 List<String> nhList = optVpnExtraRoutes.get().getNexthopIpList();
249 if (nhList != null && nhList.size() > 1) {
250 // If nhList is greater than one for vpnextraroute, a call to populatefib doesn't update vrfentry.
251 prefixListForRefreshFib.add(destination);
253 L3vpnInput input = new L3vpnInput().setNextHop(operationalAdj).setNextHopIp(nextHop)
254 .setL3vni(l3vni.longValue())
255 .setPrimaryRd(primaryRd).setVpnName(vpnName).setDpnId(dpnId)
256 .setEncapType(encapType).setRd(rd).setRouteOrigin(origin);
257 L3vpnRegistry.getRegisteredPopulator(encapType).populateFib(input, confTx);
264 public void delExtraRoute(String vpnName, String destination, String nextHop, String rd, @Nullable String routerID,
265 @Nullable String intfName, @NonNull TypedWriteTransaction<Configuration> confTx,
266 @NonNull TypedWriteTransaction<Operational> operTx) {
268 String tunnelIp = nextHop;
269 if (intfName != null && !intfName.isEmpty()) {
270 dpnId = InterfaceUtils.getDpnForInterface(ifaceMgrRpcService, intfName);
271 String nextHopIp = InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, dpnId);
272 if (nextHopIp == null || nextHopIp.isEmpty()) {
273 LOG.error("delExtraRoute: NextHop for interface {} is null / empty."
274 + " Failed advertising extra route for rd {} prefix {} dpn {}", intfName, rd, destination,
277 tunnelIp = nextHopIp;
280 String primaryRd = vpnUtil.getVpnRd(vpnName);
281 removePrefixFromBGP(vpnName, primaryRd, rd, intfName, destination,
282 nextHop, tunnelIp, dpnId, confTx, operTx);
283 LOG.info("delExtraRoute: Removed extra route {} from interface {} for rd {}", destination, intfName, rd);
285 // add FIB route directly
286 fibManager.removeOrUpdateFibEntry(routerID, destination, tunnelIp, confTx);
287 LOG.info("delExtraRoute: Removed extra route {} from interface {} for rd {}", destination, intfName,
293 @SuppressWarnings("checkstyle:IllegalCatch")
294 public void removePrefixFromBGP(String vpnName, String primaryRd, String extraRouteRd, String vpnInterfaceName,
295 String prefix, String nextHop, String nextHopTunnelIp, Uint64 dpnId,
296 TypedWriteTransaction<Configuration> confTx,
297 TypedWriteTransaction<Operational> operTx) {
298 String vpnNamePrefixKey = VpnUtil.getVpnNamePrefixKey(vpnName, prefix);
299 // FIXME: separate out to somehow?
300 final ReentrantLock lock = JvmGlobalLocks.getLockForString(vpnNamePrefixKey);
303 if (vpnUtil.removeOrUpdateDSForExtraRoute(vpnName, primaryRd, extraRouteRd, vpnInterfaceName, prefix,
304 nextHop, nextHopTunnelIp, operTx)) {
307 fibManager.removeOrUpdateFibEntry(primaryRd, prefix, nextHopTunnelIp, confTx);
308 if (VpnUtil.isEligibleForBgp(extraRouteRd, vpnName, dpnId, null /*networkName*/)) {
309 // TODO: Might be needed to include nextHop here
310 bgpManager.withdrawPrefix(extraRouteRd, prefix);
312 LOG.info("removePrefixFromBGP: VPN WITHDRAW: Removed Fib Entry rd {} prefix {} nexthop {}", extraRouteRd,
314 } catch (RuntimeException e) {
315 LOG.error("removePrefixFromBGP: Delete prefix {} rd {} nextHop {} failed", prefix, extraRouteRd, nextHop);
322 public boolean isVPNConfigured() {
323 InstanceIdentifier<VpnInstances> vpnsIdentifier = InstanceIdentifier.builder(VpnInstances.class).build();
325 Optional<VpnInstances> optionalVpns =
326 SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.CONFIGURATION,
328 if (!optionalVpns.isPresent()
329 || optionalVpns.get().getVpnInstance() == null
330 || optionalVpns.get().getVpnInstance().isEmpty()) {
331 LOG.trace("isVPNConfigured: No VPNs configured.");
334 } catch (ReadFailedException e) {
335 throw new RuntimeException("Error reading VPN " + vpnsIdentifier, e);
337 LOG.trace("isVPNConfigured: VPNs are configured on the system.");
342 public void addSubnetMacIntoVpnInstance(String vpnName, String subnetVpnName, String srcMacAddress,
343 Uint64 dpnId, TypedWriteTransaction<Configuration> confTx)
344 throws ExecutionException, InterruptedException {
345 setupSubnetMacInVpnInstance(vpnName, subnetVpnName, srcMacAddress, dpnId,
346 (vpnId, dpId, subnetVpnId) -> addGwMac(srcMacAddress, confTx, vpnId, dpId, subnetVpnId));
350 public void removeSubnetMacFromVpnInstance(String vpnName, String subnetVpnName, String srcMacAddress,
351 Uint64 dpnId, TypedReadWriteTransaction<Configuration> confTx)
352 throws ExecutionException, InterruptedException {
353 setupSubnetMacInVpnInstance(vpnName, subnetVpnName, srcMacAddress, dpnId,
354 (vpnId, dpId, subnetVpnId) -> removeGwMac(srcMacAddress, confTx, vpnId, dpId, subnetVpnId));
358 private interface VpnInstanceSubnetMacSetupMethod {
359 void process(Uint32 vpnId, Uint64 dpId, Uint32 subnetVpnId) throws InterruptedException, ExecutionException;
362 private void setupSubnetMacInVpnInstance(String vpnName, String subnetVpnName, String srcMacAddress,
363 Uint64 dpnId, VpnInstanceSubnetMacSetupMethod consumer)
364 throws ExecutionException, InterruptedException {
365 if (vpnName == null) {
366 LOG.warn("Cannot setup subnet MAC {} on DPN {}, null vpnName", srcMacAddress, dpnId);
370 Uint32 vpnId = vpnUtil.getVpnId(vpnName);
371 Uint32 subnetVpnId = vpnUtil.getVpnId(subnetVpnName);
372 if (dpnId.equals(Uint64.ZERO)) {
373 /* Apply the MAC on all DPNs in a VPN */
374 for (Uint64 dpId : vpnUtil.getDpnsOnVpn(vpnName)) {
375 consumer.process(vpnId, dpId, subnetVpnId);
378 consumer.process(vpnId, dpnId, subnetVpnId);
382 private void addGwMac(String srcMacAddress, TypedWriteTransaction<Configuration> tx, Uint32 vpnId, Uint64 dpId,
383 Uint32 subnetVpnId) {
384 FlowEntity flowEntity = vpnUtil.buildL3vpnGatewayFlow(dpId, srcMacAddress, vpnId, subnetVpnId);
385 mdsalManager.addFlow(tx, flowEntity);
388 // TODO skitt Fix the exception handling here
389 @SuppressWarnings("checkstyle:IllegalCatch")
390 @SuppressFBWarnings("REC_CATCH_EXCEPTION")
391 private void removeGwMac(String srcMacAddress, TypedReadWriteTransaction<Configuration> tx, Uint32 vpnId,
392 Uint64 dpId, Uint32 subnetVpnId) throws ExecutionException, InterruptedException {
393 mdsalManager.removeFlow(tx, dpId,
394 VpnUtil.getL3VpnGatewayFlowRef(NwConstants.L3_GW_MAC_TABLE, dpId, vpnId, srcMacAddress, subnetVpnId),
395 NwConstants.L3_GW_MAC_TABLE);
399 public void addRouterGwMacFlow(String routerName, String routerGwMac, Uint64 dpnId, Uuid extNetworkId,
400 String subnetVpnName, TypedWriteTransaction<Configuration> confTx)
401 throws ExecutionException, InterruptedException {
402 setupRouterGwMacFlow(routerName, routerGwMac, dpnId, extNetworkId,
403 vpnId -> addSubnetMacIntoVpnInstance(vpnId, subnetVpnName, routerGwMac, dpnId, confTx), "Installing");
407 public void removeRouterGwMacFlow(String routerName, String routerGwMac, Uint64 dpnId, Uuid extNetworkId,
408 String subnetVpnName, TypedReadWriteTransaction<Configuration> confTx)
409 throws ExecutionException, InterruptedException {
410 setupRouterGwMacFlow(routerName, routerGwMac, dpnId, extNetworkId,
411 vpnId -> removeSubnetMacFromVpnInstance(vpnId, subnetVpnName, routerGwMac, dpnId, confTx), "Removing");
414 private void setupRouterGwMacFlow(String routerName, String routerGwMac, Uint64 dpnId, Uuid extNetworkId,
415 InterruptibleCheckedConsumer<String, ExecutionException> consumer, String operation)
416 throws ExecutionException, InterruptedException {
417 if (routerGwMac == null) {
418 LOG.warn("Failed to handle router GW flow in GW-MAC table. MAC address is missing for router-id {}",
423 if (dpnId == null || Uint64.ZERO.equals(dpnId)) {
424 LOG.info("setupRouterGwMacFlow: DPN id is missing for router-id {}",
429 Uuid vpnId = vpnUtil.getExternalNetworkVpnId(extNetworkId);
431 LOG.warn("Network {} is not associated with VPN", extNetworkId.getValue());
435 LOG.info("{} router GW MAC flow for router-id {} on switch {}", operation, routerName, dpnId);
436 consumer.accept(vpnId.getValue());
440 public void addArpResponderFlowsToExternalNetworkIps(String id, Collection<String> fixedIps, String macAddress,
441 Uint64 dpnId, Uuid extNetworkId) {
443 if (dpnId == null || Uint64.ZERO.equals(dpnId)) {
444 LOG.warn("Failed to install arp responder flows for router {}. DPN id is missing.", id);
448 String extInterfaceName = elanService.getExternalElanInterface(extNetworkId.getValue(), dpnId);
449 if (extInterfaceName != null) {
450 doAddArpResponderFlowsToExternalNetworkIps(
451 id, fixedIps, macAddress, dpnId, extInterfaceName);
455 LOG.warn("Failed to install responder flows for {}. No external interface found for DPN id {}", id, dpnId);
457 if (!upgradeState.isUpgradeInProgress()) {
461 // The following through the end of the function deals with an upgrade scenario where the neutron configuration
462 // is restored before the OVS switches reconnect. In such a case, the elan-dpn-interfaces entries will be
463 // missing from the operational data store. In order to mitigate this we use DataTreeEventCallbackRegistrar
464 // to wait for the exact operational md-sal object we need to contain the external interface we need.
466 LOG.info("Upgrade in process, waiting for an external interface to appear on dpn {} for elan {}",
467 dpnId, extNetworkId.getValue());
469 InstanceIdentifier<DpnInterfaces> dpnInterfacesIid =
470 elanService.getElanDpnInterfaceOperationalDataPath(extNetworkId.getValue(), dpnId);
472 eventCallbacks.onAddOrUpdate(LogicalDatastoreType.OPERATIONAL, dpnInterfacesIid, (unused, alsoUnused) -> {
473 LOG.info("Reattempting write of arp responder for external interfaces for external network {}",
475 DpnInterfaces dpnInterfaces = elanService.getElanInterfaceInfoByElanDpn(extNetworkId.getValue(), dpnId);
476 if (dpnInterfaces == null) {
477 LOG.error("Could not retrieve DpnInterfaces for {}, {}", extNetworkId.getValue(), dpnId);
478 return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER;
481 String extIfc = null;
482 @Nullable List<String> interfaces = dpnInterfaces.getInterfaces();
483 if (interfaces != null) {
484 for (String dpnInterface : interfaces) {
485 if (interfaceManager.isExternalInterface(dpnInterface)) {
486 extIfc = dpnInterface;
492 if (extIfc == null) {
493 if (upgradeState.isUpgradeInProgress()) {
494 LOG.info("External interface not found yet in elan {} on dpn {}, keep waiting",
495 extNetworkId.getValue(), dpnInterfaces);
496 return DataTreeEventCallbackRegistrar.NextAction.CALL_AGAIN;
498 return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER;
502 final String extIfcFinal = extIfc;
503 doAddArpResponderFlowsToExternalNetworkIps(id, fixedIps, macAddress, dpnId, extIfcFinal);
505 return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER;
511 public void addArpResponderFlowsToExternalNetworkIps(String id, Collection<String> fixedIps, String macAddress,
512 Uint64 dpnId, String extInterfaceName, int lportTag) {
513 if (fixedIps == null || fixedIps.isEmpty()) {
514 LOG.debug("No external IPs defined for {}", id);
518 LOG.info("Installing ARP responder flows for {} fixed-ips {} on switch {}", id, fixedIps, dpnId);
520 for (String fixedIp : fixedIps) {
521 IpVersionChoice ipVersionChoice = VpnUtil.getIpVersionFromString(fixedIp);
522 if (ipVersionChoice == IpVersionChoice.IPV6) {
525 installArpResponderFlowsToExternalNetworkIp(macAddress, dpnId, extInterfaceName, lportTag,
530 private void doAddArpResponderFlowsToExternalNetworkIps(String id, Collection<String> fixedIps, String macAddress,
531 Uint64 dpnId, String extInterfaceName) {
532 Interface extInterfaceState = InterfaceUtils.getInterfaceStateFromOperDS(dataBroker, extInterfaceName);
533 if (extInterfaceState == null) {
534 LOG.debug("No interface state found for interface {}. Delaying responder flows for {}", extInterfaceName,
539 Integer lportTag = extInterfaceState.getIfIndex();
540 if (lportTag == null) {
541 LOG.debug("No Lport tag found for interface {}. Delaying flows for router-id {}", extInterfaceName, id);
545 if (macAddress == null) {
547 LOG.debug("Failed to install arp responder flows for router-gateway-ip {} for router {}."
548 + "External Gw MacAddress is missing.", fixedIps, id);
552 addArpResponderFlowsToExternalNetworkIps(id, fixedIps, macAddress, dpnId, extInterfaceName, lportTag);
556 public void removeArpResponderFlowsToExternalNetworkIps(String id, Collection<String> fixedIps, String macAddress,
557 Uint64 dpnId, Uuid extNetworkId) {
559 if (dpnId == null || Uint64.ZERO.equals(dpnId)) {
560 LOG.warn("Failed to remove arp responder flows for router {}. DPN id is missing.", id);
564 String extInterfaceName = elanService.getExternalElanInterface(extNetworkId.getValue(), dpnId);
565 if (extInterfaceName == null) {
566 LOG.warn("Failed to remove responder flows for {}. No external interface found for DPN id {}", id, dpnId);
570 Interface extInterfaceState = InterfaceUtils.getInterfaceStateFromOperDS(dataBroker, extInterfaceName);
571 if (extInterfaceState == null) {
572 LOG.debug("No interface state found for interface {}. Delaying responder flows for {}", extInterfaceName,
577 Integer lportTag = extInterfaceState.getIfIndex();
578 if (lportTag == null) {
579 LOG.debug("No Lport tag found for interface {}. Delaying flows for router-id {}", extInterfaceName, id);
583 if (macAddress == null) {
585 LOG.debug("Failed to remove arp responder flows for router-gateway-ip {} for router {}."
586 + "External Gw MacAddress is missing.", fixedIps, id);
590 removeArpResponderFlowsToExternalNetworkIps(id, fixedIps, dpnId,
591 extInterfaceName, lportTag);
595 public void removeArpResponderFlowsToExternalNetworkIps(String id, Collection<String> fixedIps,
596 Uint64 dpnId, String extInterfaceName, int lportTag) {
597 if (fixedIps == null || fixedIps.isEmpty()) {
598 LOG.debug("No external IPs defined for {}", id);
602 LOG.info("Removing ARP responder flows for {} fixed-ips {} on switch {}", id, fixedIps, dpnId);
604 for (String fixedIp : fixedIps) {
605 removeArpResponderFlowsToExternalNetworkIp(dpnId, lportTag, fixedIp, extInterfaceName);
610 public String getPrimaryRdFromVpnInstance(VpnInstance vpnInstance) {
611 return VpnUtil.getPrimaryRd(vpnInstance);
614 private void installArpResponderFlowsToExternalNetworkIp(String macAddress, Uint64 dpnId,
615 String extInterfaceName, int lportTag, String fixedIp) {
616 // reset the split-horizon bit to allow traffic to be sent back to the
618 List<Instruction> instructions = new ArrayList<>();
620 new InstructionWriteMetadata(Uint64.ZERO,
621 MetaDataUtil.METADATA_MASK_SH_FLAG).buildInstruction(1));
623 ArpResponderUtil.getExtInterfaceInstructions(interfaceManager, itmRpcService, extInterfaceName,
624 fixedIp, macAddress));
625 ArpReponderInputBuilder builder = new ArpReponderInputBuilder().setDpId(dpnId.toJava())
626 .setInterfaceName(extInterfaceName).setSpa(fixedIp).setSha(macAddress).setLportTag(lportTag);
627 builder.setInstructions(instructions);
628 elanService.addArpResponderFlow(builder.buildForInstallFlow());
631 private void removeArpResponderFlowsToExternalNetworkIp(Uint64 dpnId, Integer lportTag, String fixedIp,
632 String extInterfaceName) {
633 ArpResponderInput arpInput = new ArpReponderInputBuilder()
634 .setDpId(dpnId.toJava()).setInterfaceName(extInterfaceName)
635 .setSpa(fixedIp).setLportTag(lportTag).buildForRemoveFlow();
636 elanService.removeArpResponderFlow(arpInput);
640 public void onSubnetAddedToVpn(Subnetmap subnetmap, boolean isBgpVpn, Long elanTag) {
641 vpnSubnetRouteHandler.onSubnetAddedToVpn(subnetmap, isBgpVpn, elanTag);
645 public void onSubnetDeletedFromVpn(Subnetmap subnetmap, boolean isBgpVpn) {
646 vpnSubnetRouteHandler.onSubnetDeletedFromVpn(subnetmap, isBgpVpn);
651 public VpnInstance getVpnInstance(DataBroker broker, String vpnInstanceName) {
652 return vpnUtil.getVpnInstance(vpnInstanceName);
656 public String getVpnRd(TypedReadTransaction<Configuration> confTx, String vpnName) {
657 return VpnUtil.getVpnRd(confTx, vpnName);
661 public String getVpnRd(DataBroker broker, String vpnName) {
662 return vpnUtil.getVpnRd(vpnName);
666 public VpnPortipToPort getNeutronPortFromVpnPortFixedIp(TypedReadTransaction<Configuration> confTx, String vpnName,
668 return VpnUtil.getNeutronPortFromVpnPortFixedIp(confTx, vpnName, fixedIp);
673 public VpnPortipToPort getNeutronPortFromVpnPortFixedIp(DataBroker broker, String vpnName, String fixedIp) {
674 return vpnUtil.getNeutronPortFromVpnPortFixedIp(vpnName, fixedIp);
678 public Set<VpnTarget> getRtListForVpn(String vpnName) {
679 return vpnUtil.getRtListForVpn(vpnName);
683 public void updateRouteTargetsToSubnetAssociation(Set<VpnTarget> routeTargets, String cidr, String vpnName) {
684 ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
685 for (VpnTarget rt : routeTargets) {
686 String rtValue = rt.getVrfRTValue();
687 switch (rt.getVrfRTType()) {
688 case ImportExtcommunity:
689 addSubnetAssociationOperationToTx(rtValue, RouteTarget.RtType.IRT, cidr, vpnName,
690 tx, false/*isAssociationRemoved*/);
692 case ExportExtcommunity:
693 addSubnetAssociationOperationToTx(rtValue, RouteTarget.RtType.ERT, cidr, vpnName,
694 tx, false/*isAssociationRemoved*/);
697 addSubnetAssociationOperationToTx(rtValue, RouteTarget.RtType.IRT, cidr, vpnName,
698 tx, false/*isAssociationRemoved*/);
699 addSubnetAssociationOperationToTx(rtValue, RouteTarget.RtType.ERT, cidr, vpnName,
700 tx, false/*isAssociationRemoved*/);
703 LOG.error("updateRouteTargetsToSubnetAssociation: Invalid rt-type {} for vpn {}"
704 + " subnet-cidr {}", rt.getVrfRTType(), vpnName, cidr);
708 }), LOG, "updateRouteTargetsToSubnetAssociation: Failed for vpn {} subnet-cidr {}", vpnName, cidr);
709 LOG.info("updateRouteTargetsToSubnetAssociation: Updated RT to subnet association for vpn {} cidr {}",
714 public void removeRouteTargetsToSubnetAssociation(Set<VpnTarget> routeTargets, String cidr, String vpnName) {
715 ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(tx -> {
716 for (VpnTarget rt : routeTargets) {
717 String rtValue = rt.getVrfRTValue();
718 switch (rt.getVrfRTType()) {
719 case ImportExtcommunity:
720 addSubnetAssociationOperationToTx(rtValue, RouteTarget.RtType.IRT, cidr, vpnName, tx,
721 true/*isAssociationRemoved*/);
723 case ExportExtcommunity:
724 addSubnetAssociationOperationToTx(rtValue, RouteTarget.RtType.ERT, cidr, vpnName, tx,
725 true/*isAssociationRemoved*/);
728 addSubnetAssociationOperationToTx(rtValue, RouteTarget.RtType.IRT, cidr, vpnName, tx,
729 true/*isAssociationRemoved*/);
730 addSubnetAssociationOperationToTx(rtValue, RouteTarget.RtType.ERT, cidr, vpnName, tx,
731 true/*isAssociationRemoved*/);
734 LOG.error("removeRouteTargetsToSubnetAssociation: Invalid route-target type {} for vpn {}"
735 + " subnet-cidr {}", rt.getVrfRTType(), vpnName, cidr);
739 }), LOG, "removeRouteTargetsToSubnetAssociation: Failed for vpn {} subnet-cidr {}", vpnName, cidr);
740 LOG.info("removeRouteTargetsToSubnetAssociation: vpn {} cidr {}", vpnName, cidr);
743 private boolean doesExistingVpnsHaveConflictingSubnet(Set<VpnTarget> routeTargets, String subnetCidr) {
744 Set<RouteTarget> routeTargetSet = vpnUtil.getRouteTargetSet(routeTargets);
745 for (RouteTarget routerTarget : routeTargetSet) {
746 if (routerTarget.getAssociatedSubnet() != null) {
747 for (int i = 0; i < routerTarget.getAssociatedSubnet().size(); i++) {
748 AssociatedSubnet associatedSubnet = routerTarget.getAssociatedSubnet().get(i);
749 if (VpnUtil.areSubnetsOverlapping(associatedSubnet.getCidr(), subnetCidr)) {
752 if (routerTarget.getRtType() == RouteTarget.RtType.ERT) {
753 /* Check if there are multiple exports for the input iRT value (iRT in routeTargets)
754 * Example : (1) iRT=A eRT=B subnet-range=S1; OK
755 * (2) iRT=A eRT=B subnet-range=S1; OK
756 * (3) iRT=B eRT=A subnet-range=S2; NOK
757 * Check if (1) and (2) are importing the same subnet-range routes to (3) */
758 List<AssociatedVpn> multipleAssociatedVpn = associatedSubnet.getAssociatedVpn();
759 if (multipleAssociatedVpn != null && multipleAssociatedVpn.size() > 1) {
760 LOG.error("doesExistingVpnsHaveConflictingSubnet: There is an indirect complete overlap"
761 + " for subnet CIDR {} for rt {} rtType {}", subnetCidr, routerTarget.getRt(),
762 routerTarget.getRtType());
765 for (int j = i + 1; j < routerTarget.getAssociatedSubnet().size(); j++) {
766 if (VpnUtil.areSubnetsOverlapping(associatedSubnet.getCidr(),
767 routerTarget.getAssociatedSubnet().get(j).getCidr())) {
768 LOG.error("doesExistingVpnsHaveConflictingSubnet: There is an indirect paartial"
769 + " overlap for subnet CIDR {} for rt {} rtType {}", subnetCidr,
770 routerTarget.getRt(), routerTarget.getRtType());
776 /* Check if there are indirect EXPORTS for the eRT value (eRT in routeTargets)
777 * Example : (1) iRT=A eRT=B subnet-range=S1; OK
778 * (2) iRT=B eRT=A subnet-range=S2; OK
779 * (3) iRT=A eRT=B subnet-range=S1; NOK
780 * If associatedSubnet is non-null for a routeTarget in (2),
781 * it may have already imported routes from (1) */
782 if (routerTarget.getRtType() == RouteTarget.RtType.IRT) {
784 Optional<RouteTarget> indirectRts = SingleTransactionDataBroker.syncReadOptional(dataBroker,
785 LogicalDatastoreType.OPERATIONAL, VpnUtil.getRouteTargetsIdentifier(
786 routerTarget.getRt(), RouteTarget.RtType.ERT));
787 if (indirectRts.isPresent() && indirectRts.get().getAssociatedSubnet() != null
788 && routerTarget.getAssociatedSubnet() != null) {
789 for (AssociatedSubnet associatedSubnet : indirectRts.get().getAssociatedSubnet()) {
790 if (VpnUtil.areSubnetsOverlapping(associatedSubnet.getCidr(), subnetCidr)) {
791 LOG.error("doesExistingVpnsHaveConflictingSubnet: There is an indirect overlap for"
792 + " subnet CIDR {} for rt {} rtType {}", subnetCidr, routerTarget.getRt(),
793 routerTarget.getRtType());
798 } catch (ReadFailedException e) {
799 LOG.error("doesExistingVpnsHaveConflictingSubnet: Failed to read route targets to subnet"
800 + "association for rt {} type {} subnet-cidr {}", routerTarget.getRt(),
801 RouteTarget.RtType.ERT, subnetCidr);
802 return true; //Fail subnet association to avoid further damage to the data-stores
806 LOG.info("doesExistingVpnsHaveConflictingSubnet: No prior associated subnets for rtVal {} rtType {}",
807 routerTarget.getRt(), routerTarget.getRtType());
813 private void addSubnetAssociationOperationToTx(String rt, RouteTarget.RtType rtType, String cidr,
814 String vpnName, ReadWriteTransaction tx,
815 boolean isAssociationRemoved)
816 throws InterruptedException, ExecutionException {
817 if (isAssociationRemoved) {
818 //Remove RT-Subnet-Vpn Association
819 Optional<AssociatedSubnet> associatedSubnet = tx.read(LogicalDatastoreType.OPERATIONAL,
820 VpnUtil.getAssociatedSubnetIdentifier(rt, rtType, cidr)).get();
821 boolean deleteParent = false;
822 if (associatedSubnet.isPresent()) {
823 List<AssociatedVpn> associatedVpns = associatedSubnet.get().getAssociatedVpn();
824 if (associatedVpns == null || associatedVpns.isEmpty()) {
827 for (Iterator<AssociatedVpn> iterator = associatedVpns.iterator(); iterator.hasNext();) {
828 AssociatedVpn associatedVpn = iterator.next();
829 if (Objects.equals(associatedVpn.getName(), vpnName)) {
834 if (associatedVpns.isEmpty()) {
840 deleteParentForSubnetToVpnAssocication(rt, rtType, cidr, tx);
842 //Some other VPNs are also part of this rtVal, rtType and subnetCidr combination.
843 //Delete only this AssociatedVpn Object
844 tx.delete(LogicalDatastoreType.OPERATIONAL,
845 VpnUtil.getAssociatedSubnetAndVpnIdentifier(rt, rtType, cidr, vpnName));
846 LOG.debug("addSubnetAssocOperationToTx: Removed vpn {} from association rt {} rtType {} cidr {}",
847 vpnName, rt, rtType, cidr);
850 //Add RT-Subnet-Vpn Association
851 tx.put(LogicalDatastoreType.OPERATIONAL,
852 VpnUtil.getAssociatedSubnetAndVpnIdentifier(rt, rtType, cidr, vpnName),
853 VpnUtil.buildAssociatedSubnetAndVpn(vpnName), true);
857 private void deleteParentForSubnetToVpnAssocication(String rt, RouteTarget.RtType rtType,
858 String cidr, ReadWriteTransaction tx)
859 throws InterruptedException, ExecutionException {
860 //Check if you need to delete rtVal+rtType or just the subnetCidr
861 InstanceIdentifier<RouteTarget> rtIdentifier = InstanceIdentifier
862 .builder(SubnetsAssociatedToRouteTargets.class).child(RouteTarget.class,
863 new RouteTargetKey(rt, rtType)).build();
864 Optional<RouteTarget> rtToSubnetsAssociation = tx.read(LogicalDatastoreType.OPERATIONAL,
866 if (rtToSubnetsAssociation.isPresent()) {
867 List<AssociatedSubnet> associatedSubnets = rtToSubnetsAssociation.get().getAssociatedSubnet();
868 if (associatedSubnets != null && !associatedSubnets.isEmpty()) {
869 for (Iterator<AssociatedSubnet> iterator = associatedSubnets.iterator(); iterator.hasNext(); ) {
870 if (Objects.equals(iterator.next().getCidr(), cidr)) {
875 if (associatedSubnets.isEmpty()) {
876 //The entire rt to subnet association is empty
877 //Delete the RouteTarget object
878 tx.delete(LogicalDatastoreType.OPERATIONAL, rtIdentifier);
879 LOG.debug("deleteParentForSubnetToVpnAssocication: Removed rt {} rtType {} from association,",
882 //Some other VPNs are also part of this rtVal, rtType combination
883 //Delete only this AssociatedSubnet
884 tx.delete(LogicalDatastoreType.OPERATIONAL, VpnUtil.getAssociatedSubnetIdentifier(rt, rtType,
886 LOG.debug("deleteParentForSubnetToVpnAssocication: Removed cidr {} from association rt {}"
887 + " rtType {}", cidr, rt, rtType);
894 public boolean checkForOverlappingSubnets(Uuid network, List<Subnetmap> subnetmapList, Uuid vpn,
895 Set<VpnTarget> routeTargets, List<String> failedNwList) {
896 for (int i = 0; i < subnetmapList.size(); i++) {
897 //Check if any other subnet that is already part of a different vpn with same rt, has overlapping CIDR
898 if (checkExistingSubnetWithSameRoutTargets(routeTargets, vpn, subnetmapList.get(i), failedNwList)) {
905 private boolean checkExistingSubnetWithSameRoutTargets(Set<VpnTarget> routeTargets, Uuid vpn, Subnetmap subnetmap,
906 List<String> failedNwList) {
907 String cidr = String.valueOf(subnetmap.getSubnetIp());
908 boolean subnetExistsWithSameRt = doesExistingVpnsHaveConflictingSubnet(routeTargets, cidr);
909 if (subnetExistsWithSameRt) {
910 failedNwList.add(String.format("Another subnet with the same/overlapping cidr %s as subnet %s"
911 + " is already associated to a vpn with routeTargets %s. Ignoring subnet addition to vpn"
912 + " %s", cidr, subnetmap.getId().getValue(), routeTargets.toString(), vpn.getValue()));
914 return subnetExistsWithSameRt;