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 com.google.common.util.concurrent.ListenableFuture;
12 import java.math.BigInteger;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.Collections;
16 import java.util.List;
17 import java.util.concurrent.ExecutionException;
18 import java.util.concurrent.Future;
19 import javax.annotation.PostConstruct;
20 import javax.inject.Inject;
21 import javax.inject.Singleton;
22 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
23 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
24 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
25 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
26 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
27 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
28 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
29 import org.opendaylight.genius.mdsalutil.FlowEntity;
30 import org.opendaylight.genius.mdsalutil.MDSALUtil;
31 import org.opendaylight.genius.mdsalutil.MatchInfoBase;
32 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
33 import org.opendaylight.genius.mdsalutil.NwConstants;
34 import org.opendaylight.genius.mdsalutil.UpgradeState;
35 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
36 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
37 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchRegister;
38 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
39 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
40 import org.opendaylight.netvirt.elan.arp.responder.ArpResponderInput;
41 import org.opendaylight.netvirt.elan.arp.responder.ArpResponderInput.ArpReponderInputBuilder;
42 import org.opendaylight.netvirt.elan.arp.responder.ArpResponderUtil;
43 import org.opendaylight.netvirt.elanmanager.api.IElanService;
44 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
45 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
46 import org.opendaylight.netvirt.vpnmanager.api.IVpnManager;
47 import org.opendaylight.netvirt.vpnmanager.api.InterfaceUtils;
48 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
49 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.IVpnLinkService;
50 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkCache;
51 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkDataComposite;
52 import org.opendaylight.netvirt.vpnmanager.populator.input.L3vpnInput;
53 import org.opendaylight.netvirt.vpnmanager.populator.registry.L3vpnRegistry;
54 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
55 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
56 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
57 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInput;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInputBuilder;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.portip.port.data.VpnPortipToPort;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
71 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
72 import org.opendaylight.yangtools.yang.common.RpcError;
73 import org.opendaylight.yangtools.yang.common.RpcResult;
74 import org.slf4j.Logger;
75 import org.slf4j.LoggerFactory;
78 public class VpnManagerImpl implements IVpnManager {
80 private static final Logger LOG = LoggerFactory.getLogger(VpnManagerImpl.class);
81 private final DataBroker dataBroker;
82 private final ManagedNewTransactionRunner txRunner;
83 private final IdManagerService idManager;
84 private final IMdsalApiManager mdsalManager;
85 private final IElanService elanService;
86 private final IInterfaceManager interfaceManager;
87 private final VpnSubnetRouteHandler vpnSubnetRouteHandler;
88 private final OdlInterfaceRpcService ifaceMgrRpcService;
89 private final IVpnLinkService ivpnLinkService;
90 private final IFibManager fibManager;
91 private final IBgpManager bgpManager;
92 private final InterVpnLinkCache interVpnLinkCache;
93 private final DataTreeEventCallbackRegistrar eventCallbacks;
94 private final UpgradeState upgradeState;
97 public VpnManagerImpl(final DataBroker dataBroker,
98 final IdManagerService idManagerService,
99 final IMdsalApiManager mdsalManager,
100 final VpnFootprintService vpnFootprintService,
101 final IElanService elanService,
102 final IInterfaceManager interfaceManager,
103 final VpnSubnetRouteHandler vpnSubnetRouteHandler,
104 final OdlInterfaceRpcService ifaceMgrRpcService,
105 final IVpnLinkService ivpnLinkService,
106 final IFibManager fibManager,
107 final IBgpManager bgpManager,
108 final InterVpnLinkCache interVpnLinkCache,
109 final DataTreeEventCallbackRegistrar dataTreeEventCallbackRegistrar,
110 final UpgradeState upgradeState) {
111 this.dataBroker = dataBroker;
112 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
113 this.idManager = idManagerService;
114 this.mdsalManager = mdsalManager;
115 this.elanService = elanService;
116 this.interfaceManager = interfaceManager;
117 this.vpnSubnetRouteHandler = vpnSubnetRouteHandler;
118 this.ifaceMgrRpcService = ifaceMgrRpcService;
119 this.ivpnLinkService = ivpnLinkService;
120 this.fibManager = fibManager;
121 this.bgpManager = bgpManager;
122 this.interVpnLinkCache = interVpnLinkCache;
123 this.eventCallbacks = dataTreeEventCallbackRegistrar;
124 this.upgradeState = upgradeState;
128 public void start() {
129 LOG.info("{} start", getClass().getSimpleName());
133 private void createIdPool() {
134 CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
135 .setPoolName(VpnConstants.VPN_IDPOOL_NAME)
136 .setLow(VpnConstants.VPN_IDPOOL_LOW)
137 .setHigh(VpnConstants.VPN_IDPOOL_HIGH)
140 Future<RpcResult<Void>> result = idManager.createIdPool(createPool);
141 if (result != null && result.get().isSuccessful()) {
142 LOG.info("Created IdPool for VPN Service");
144 } catch (InterruptedException | ExecutionException e) {
145 LOG.error("Failed to create idPool for VPN Service", e);
148 // Now an IdPool for InterVpnLink endpoint's pseudo ports
149 CreateIdPoolInput createPseudoLporTagPool =
150 new CreateIdPoolInputBuilder().setPoolName(VpnConstants.PSEUDO_LPORT_TAG_ID_POOL_NAME)
151 .setLow(VpnConstants.LOWER_PSEUDO_LPORT_TAG)
152 .setHigh(VpnConstants.UPPER_PSEUDO_LPORT_TAG)
155 Future<RpcResult<Void>> result = idManager.createIdPool(createPseudoLporTagPool);
156 if (result.get().isSuccessful()) {
157 LOG.debug("Created IdPool for Pseudo Port tags");
159 Collection<RpcError> errors = result.get().getErrors();
160 StringBuilder errMsg = new StringBuilder();
161 for (RpcError err : errors) {
162 errMsg.append(err.getMessage()).append("\n");
164 LOG.error("IdPool creation for PseudoPort tags failed. Reasons: {}", errMsg);
166 } catch (InterruptedException | ExecutionException e) {
167 LOG.error("Failed to create idPool for Pseudo Port tags", e);
172 public void addExtraRoute(String vpnName, String destination, String nextHop, String rd, String routerID,
173 int label,RouteOrigin origin) {
174 LOG.info("Adding extra route with destination {}, nextHop {}, label{} and origin {}",
175 destination, nextHop, label, origin);
176 VpnInstanceOpDataEntry vpnOpEntry = VpnUtil.getVpnInstanceOpData(dataBroker, rd);
177 Boolean isVxlan = VpnUtil.isL3VpnOverVxLan(vpnOpEntry.getL3vni());
178 VrfEntry.EncapType encapType = VpnUtil.getEncapType(isVxlan);
179 addExtraRoute(vpnName, destination, nextHop, rd, routerID, label, vpnOpEntry.getL3vni(),
180 origin,/*intfName*/ null, null /*Adjacency*/, encapType, null);
184 public void addExtraRoute(String vpnName, String destination, String nextHop, String rd, String routerID,
185 int label, Long l3vni, RouteOrigin origin, String intfName, Adjacency operationalAdj,
186 VrfEntry.EncapType encapType, WriteTransaction writeConfigTxn) {
188 Boolean writeConfigTxnPresent = true;
189 if (writeConfigTxn == null) {
190 writeConfigTxnPresent = false;
191 writeConfigTxn = dataBroker.newWriteOnlyTransaction();
194 //add extra route to vpn mapping; advertise with nexthop as tunnel ip
197 LogicalDatastoreType.OPERATIONAL,
198 VpnExtraRouteHelper.getVpnToExtrarouteVrfIdIdentifier(vpnName, rd != null ? rd : routerID,
200 VpnUtil.getVpnToExtraroute(destination, Collections.singletonList(nextHop)));
202 BigInteger dpnId = null;
203 if (intfName != null && !intfName.isEmpty()) {
204 dpnId = InterfaceUtils.getDpnForInterface(ifaceMgrRpcService, intfName);
205 String nextHopIp = InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, dpnId);
206 if (nextHopIp == null || nextHopIp.isEmpty()) {
207 LOG.error("addExtraRoute: NextHop for interface {} is null / empty."
208 + " Failed advertising extra route for rd {} prefix {} dpn {}", intfName, rd, destination,
215 String primaryRd = VpnUtil.getPrimaryRd(dataBroker, vpnName);
217 // TODO: This is a limitation to be stated in docs. When configuring static route to go to
218 // another VPN, there can only be one nexthop or, at least, the nexthop to the interVpnLink should be in
220 Optional<InterVpnLinkDataComposite> optVpnLink = interVpnLinkCache.getInterVpnLinkByEndpoint(nextHop);
221 if (optVpnLink.isPresent() && optVpnLink.get().isActive()) {
222 InterVpnLinkDataComposite interVpnLink = optVpnLink.get();
223 // If the nexthop is the endpoint of Vpn2, then prefix must be advertised to Vpn1 in DC-GW, with nexthops
224 // pointing to the DPNs where Vpn1 is instantiated. LFIB in these DPNS must have a flow entry, with lower
225 // priority, where if Label matches then sets the lportTag of the Vpn2 endpoint and goes to LportDispatcher
226 // This is like leaking one of the Vpn2 routes towards Vpn1
227 String srcVpnUuid = interVpnLink.getVpnNameByIpAddress(nextHop);
228 String dstVpnUuid = interVpnLink.getOtherVpnNameByIpAddress(nextHop);
229 String dstVpnRd = VpnUtil.getVpnRd(dataBroker, dstVpnUuid);
230 long newLabel = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME,
231 VpnUtil.getNextHopLabelKey(dstVpnRd, destination));
233 LOG.error("addExtraRoute: Unable to fetch label from Id Manager. Bailing out of adding intervpnlink"
234 + " route for destination {}", destination);
237 ivpnLinkService.leakRoute(interVpnLink, srcVpnUuid, dstVpnUuid, destination, newLabel, RouteOrigin.STATIC,
238 NwConstants.ADD_FLOW);
240 Optional<Routes> optVpnExtraRoutes = VpnExtraRouteHelper
241 .getVpnExtraroutes(dataBroker, vpnName, rd != null ? rd : routerID, destination);
242 if (optVpnExtraRoutes.isPresent()) {
243 List<String> nhList = optVpnExtraRoutes.get().getNexthopIpList();
244 if (nhList != null && nhList.size() > 1) {
245 // If nhList is greater than one for vpnextraroute, a call to populatefib doesn't update vrfentry.
246 fibManager.refreshVrfEntry(primaryRd, destination);
248 L3vpnInput input = new L3vpnInput().setNextHop(operationalAdj).setNextHopIp(nextHop).setL3vni(l3vni)
249 .setPrimaryRd(primaryRd).setVpnName(vpnName).setDpnId(dpnId)
250 .setEncapType(encapType).setRd(rd).setRouteOrigin(origin);
251 L3vpnRegistry.getRegisteredPopulator(encapType).populateFib(input, writeConfigTxn, null);
256 if (!writeConfigTxnPresent) {
257 writeConfigTxn.submit();
262 public void delExtraRoute(String vpnName, String destination, String nextHop, String rd, String routerID) {
263 LOG.info("Deleting extra route with destination {} and nextHop {}", destination, nextHop);
264 delExtraRoute(vpnName, destination, nextHop, rd, routerID, null, null);
268 public void delExtraRoute(String vpnName, String destination, String nextHop, String rd, String routerID,
269 String intfName, WriteTransaction writeConfigTxn) {
270 Boolean writeConfigTxnPresent = true;
271 BigInteger dpnId = null;
272 if (writeConfigTxn == null) {
273 writeConfigTxnPresent = false;
274 writeConfigTxn = dataBroker.newWriteOnlyTransaction();
276 String tunnelIp = nextHop;
277 if (intfName != null && !intfName.isEmpty()) {
278 dpnId = InterfaceUtils.getDpnForInterface(ifaceMgrRpcService, intfName);
279 String nextHopIp = InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, dpnId);
280 if (nextHopIp == null || nextHopIp.isEmpty()) {
281 LOG.error("delExtraRoute: NextHop for interface {} is null / empty."
282 + " Failed advertising extra route for rd {} prefix {} dpn {}", intfName, rd, destination,
285 tunnelIp = nextHopIp;
288 String primaryRd = VpnUtil.getVpnRd(dataBroker, vpnName);
289 removePrefixFromBGP(primaryRd, rd, vpnName, destination, nextHop, tunnelIp, dpnId, writeConfigTxn);
290 LOG.info("delExtraRoute: Removed extra route {} from interface {} for rd {}", destination, intfName, rd);
292 // add FIB route directly
293 fibManager.removeOrUpdateFibEntry(dataBroker, routerID, destination, tunnelIp, writeConfigTxn);
294 LOG.info("delExtraRoute: Removed extra route {} from interface {} for rd {}", destination, intfName,
297 if (!writeConfigTxnPresent) {
298 writeConfigTxn.submit();
302 // TODO Clean up the exception handling
304 @SuppressWarnings("checkstyle:IllegalCatch")
305 public void removePrefixFromBGP(String primaryRd, String rd, String vpnName, String prefix, String nextHop,
306 String tunnelIp, BigInteger dpnId, WriteTransaction writeConfigTxn) {
308 LOG.info("removePrefixFromBGP: VPN WITHDRAW: Removing Fib Entry rd {} prefix {} nexthop {}", rd, prefix,
310 String vpnNamePrefixKey = VpnUtil.getVpnNamePrefixKey(vpnName, prefix);
311 synchronized (vpnNamePrefixKey.intern()) {
312 Optional<Routes> optVpnExtraRoutes = VpnExtraRouteHelper
313 .getVpnExtraroutes(dataBroker, vpnName, rd, prefix);
314 if (optVpnExtraRoutes.isPresent()) {
315 List<String> nhList = optVpnExtraRoutes.get().getNexthopIpList();
316 if (nhList != null && nhList.size() > 1) {
317 // If nhList is more than 1, just update vpntoextraroute and prefixtointerface DS
318 // For other cases, remove the corresponding tep ip from fibentry and withdraw prefix
319 nhList.remove(nextHop);
320 VpnUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
321 VpnExtraRouteHelper.getVpnToExtrarouteVrfIdIdentifier(vpnName, rd, prefix),
322 VpnUtil.getVpnToExtraroute(prefix, nhList));
323 MDSALUtil.syncDelete(dataBroker,
324 LogicalDatastoreType.CONFIGURATION, VpnExtraRouteHelper.getUsedRdsIdentifier(
325 VpnUtil.getVpnId(dataBroker, vpnName), prefix, nextHop));
326 LOG.debug("removePrefixFromBGP: Removed vpn-to-extraroute with rd {} prefix {} nexthop {}",
327 rd, prefix, nextHop);
328 fibManager.refreshVrfEntry(primaryRd, prefix);
329 long vpnId = VpnUtil.getVpnId(dataBroker, vpnName);
330 Optional<Prefixes> prefixToInterface = VpnUtil.getPrefixToInterface(dataBroker, vpnId, nextHop);
331 if (prefixToInterface.isPresent()) {
332 writeConfigTxn.delete(LogicalDatastoreType.OPERATIONAL,
333 VpnUtil.getAdjacencyIdentifier(prefixToInterface.get().getVpnInterfaceName(),
336 LOG.info("VPN WITHDRAW: removePrefixFromBGP: Removed Fib Entry rd {} prefix {} nexthop {}",
337 rd, prefix, tunnelIp);
341 fibManager.removeOrUpdateFibEntry(dataBroker, primaryRd, prefix, tunnelIp, writeConfigTxn);
342 if (VpnUtil.isEligibleForBgp(rd, vpnName, dpnId, null /*networkName*/)) {
343 // TODO: Might be needed to include nextHop here
344 bgpManager.withdrawPrefix(rd, prefix);
347 LOG.info("removePrefixFromBGP: VPN WITHDRAW: Removed Fib Entry rd {} prefix {} nexthop {}", rd, prefix,
349 } catch (RuntimeException e) {
350 LOG.error("removePrefixFromBGP: Delete prefix {} rd {} nextHop {} failed", prefix, rd, nextHop);
355 public boolean isVPNConfigured() {
356 InstanceIdentifier<VpnInstances> vpnsIdentifier = InstanceIdentifier.builder(VpnInstances.class).build();
357 Optional<VpnInstances> optionalVpns = TransactionUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
359 if (!optionalVpns.isPresent()
360 || optionalVpns.get().getVpnInstance() == null
361 || optionalVpns.get().getVpnInstance().isEmpty()) {
362 LOG.trace("isVPNConfigured: No VPNs configured.");
365 LOG.trace("isVPNConfigured: VPNs are configured on the system.");
370 public List<BigInteger> getDpnsOnVpn(String vpnInstanceName) {
371 return VpnUtil.getDpnsOnVpn(dataBroker, vpnInstanceName);
375 public boolean existsVpn(String vpnName) {
376 return VpnUtil.getVpnInstance(dataBroker, vpnName) != null;
380 public void addSubnetMacIntoVpnInstance(String vpnName, String subnetVpnName, String srcMacAddress,
381 BigInteger dpnId, WriteTransaction tx) {
382 setupSubnetMacInVpnInstance(vpnName, subnetVpnName, srcMacAddress, dpnId,
383 (vpnId, dpId, subnetVpnId) -> addGwMac(srcMacAddress, tx, vpnId, dpId, subnetVpnId));
387 public void removeSubnetMacFromVpnInstance(String vpnName, String subnetVpnName, String srcMacAddress,
388 BigInteger dpnId, WriteTransaction tx) {
389 setupSubnetMacInVpnInstance(vpnName, subnetVpnName, srcMacAddress, dpnId,
390 (vpnId, dpId, subnetVpnId) -> removeGwMac(srcMacAddress, tx, vpnId, dpId, subnetVpnId));
394 private interface VpnInstanceSubnetMacSetupMethod {
395 void process(long vpnId, BigInteger dpId, long subnetVpnId);
398 private void setupSubnetMacInVpnInstance(String vpnName, String subnetVpnName, String srcMacAddress,
399 BigInteger dpnId, VpnInstanceSubnetMacSetupMethod consumer) {
400 if (vpnName == null) {
401 LOG.warn("Cannot setup subnet MAC {} on DPN {}, null vpnName", srcMacAddress, dpnId);
405 long vpnId = VpnUtil.getVpnId(dataBroker, vpnName);
406 long subnetVpnId = VpnUtil.getVpnId(dataBroker, subnetVpnName);
407 if (dpnId.equals(BigInteger.ZERO)) {
408 /* Apply the MAC on all DPNs in a VPN */
409 for (BigInteger dpId : VpnUtil.getDpnsOnVpn(dataBroker, vpnName)) {
410 consumer.process(vpnId, dpId, subnetVpnId);
413 consumer.process(vpnId, dpnId, subnetVpnId);
417 private void addGwMac(String srcMacAddress, WriteTransaction tx, long vpnId, BigInteger dpId, long subnetVpnId) {
418 FlowEntity flowEntity = VpnUtil.buildL3vpnGatewayFlow(dpId, srcMacAddress, vpnId, subnetVpnId);
419 mdsalManager.addFlowToTx(flowEntity, tx);
422 private void removeGwMac(String srcMacAddress, WriteTransaction tx, long vpnId, BigInteger dpId, long subnetVpnId) {
423 FlowEntity flowEntity = VpnUtil.buildL3vpnGatewayFlow(dpId, srcMacAddress, vpnId, subnetVpnId);
424 mdsalManager.removeFlowToTx(flowEntity, tx);
428 public void addRouterGwMacFlow(String routerName, String routerGwMac, BigInteger dpnId, Uuid extNetworkId,
429 String subnetVpnName, WriteTransaction writeTx) {
430 setupRouterGwMacFlow(routerName, routerGwMac, dpnId, extNetworkId, writeTx,
431 (vpnId, tx) -> addSubnetMacIntoVpnInstance(vpnId, subnetVpnName, routerGwMac, dpnId, tx), "Installing");
435 public void removeRouterGwMacFlow(String routerName, String routerGwMac, BigInteger dpnId, Uuid extNetworkId,
436 String subnetVpnName, WriteTransaction writeTx) {
437 setupRouterGwMacFlow(routerName, routerGwMac, dpnId, extNetworkId, writeTx,
438 (vpnId, tx) -> removeSubnetMacFromVpnInstance(vpnId, subnetVpnName, routerGwMac, dpnId, tx), "Removing");
442 private interface RouterGwMacFlowSetupMethod {
443 void process(String vpnId, WriteTransaction tx);
446 private void setupRouterGwMacFlow(String routerName, String routerGwMac, BigInteger dpnId, Uuid extNetworkId,
447 WriteTransaction writeTx, RouterGwMacFlowSetupMethod consumer, String operation) {
448 if (routerGwMac == null) {
449 LOG.warn("Failed to handle router GW flow in GW-MAC table. MAC address is missing for router-id {}",
454 if (dpnId == null || BigInteger.ZERO.equals(dpnId)) {
455 LOG.info("setupRouterGwMacFlow: DPN id is missing for router-id {}",
460 Uuid vpnId = VpnUtil.getExternalNetworkVpnId(dataBroker, extNetworkId);
462 LOG.warn("Network {} is not associated with VPN", extNetworkId.getValue());
466 LOG.info("{} router GW MAC flow for router-id {} on switch {}", operation, routerName, dpnId);
467 if (writeTx == null) {
468 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
469 tx -> consumer.process(vpnId.getValue(), tx)), LOG, "Commit transaction");
471 consumer.process(vpnId.getValue(), writeTx);
476 public void addArpResponderFlowsToExternalNetworkIps(String id, Collection<String> fixedIps, String macAddress,
477 BigInteger dpnId, Uuid extNetworkId, WriteTransaction writeTx) {
479 if (dpnId == null || BigInteger.ZERO.equals(dpnId)) {
480 LOG.warn("Failed to install arp responder flows for router {}. DPN id is missing.", id);
484 String extInterfaceName = elanService.getExternalElanInterface(extNetworkId.getValue(), dpnId);
485 if (extInterfaceName != null) {
486 doAddArpResponderFlowsToExternalNetworkIps(
487 id, fixedIps, macAddress, dpnId, extNetworkId, writeTx, extInterfaceName);
491 LOG.warn("Failed to install responder flows for {}. No external interface found for DPN id {}", id, dpnId);
493 if (!upgradeState.isUpgradeInProgress()) {
497 // The following through the end of the function deals with an upgrade scenario where the neutron configuration
498 // is restored before the OVS switches reconnect. In such a case, the elan-dpn-interfaces entries will be
499 // missing from the operational data store. In order to mitigate this we use DataTreeEventCallbackRegistrar
500 // to wait for the exact operational md-sal object we need to contain the external interface we need.
502 LOG.info("Upgrade in process, waiting for an external interface to appear on dpn {} for elan {}",
503 dpnId, extNetworkId.getValue());
505 InstanceIdentifier<DpnInterfaces> dpnInterfacesIid =
506 elanService.getElanDpnInterfaceOperationalDataPath(extNetworkId.getValue(), dpnId);
508 eventCallbacks.onAddOrUpdate(LogicalDatastoreType.OPERATIONAL, dpnInterfacesIid, (unused, alsoUnused) -> {
509 LOG.info("Reattempting write of arp responder for external interfaces for external network {}",
511 DpnInterfaces dpnInterfaces = elanService.getElanInterfaceInfoByElanDpn(extNetworkId.getValue(), dpnId);
512 if (dpnInterfaces == null) {
513 LOG.error("Could not retrieve DpnInterfaces for {}, {}", extNetworkId.getValue(), dpnId);
514 return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER;
517 String extIfc = null;
518 for (String dpnInterface : dpnInterfaces.getInterfaces()) {
519 if (interfaceManager.isExternalInterface(dpnInterface)) {
520 extIfc = dpnInterface;
525 if (extIfc == null) {
526 if (upgradeState.isUpgradeInProgress()) {
527 LOG.info("External interface not found yet in elan {} on dpn {}, keep waiting",
528 extNetworkId.getValue(), dpnInterfaces);
529 return DataTreeEventCallbackRegistrar.NextAction.CALL_AGAIN;
531 return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER;
535 final String extIfcFinal = extIfc;
536 ListenableFuture<Void> listenableFuture = txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
537 doAddArpResponderFlowsToExternalNetworkIps(
538 id, fixedIps, macAddress, dpnId, extNetworkId, tx, extIfcFinal);
540 ListenableFutures.addErrorLogging(listenableFuture, LOG,
541 "Error while configuring arp responder for ext. interface");
543 return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER; });
548 public void addArpResponderFlowsToExternalNetworkIps(String id, Collection<String> fixedIps, String macAddress,
549 BigInteger dpnId, long vpnId, String extInterfaceName, int lportTag, WriteTransaction writeTx) {
550 if (fixedIps == null || fixedIps.isEmpty()) {
551 LOG.debug("No external IPs defined for {}", id);
555 LOG.info("Installing ARP responder flows for {} fixed-ips {} on switch {}", id, fixedIps, dpnId);
557 if (writeTx == null) {
558 ListenableFuture<Void> future = txRunner.callWithNewWriteOnlyTransactionAndSubmit(
560 for (String fixedIp : fixedIps) {
561 installArpResponderFlowsToExternalNetworkIp(macAddress, dpnId, extInterfaceName, lportTag,
565 ListenableFutures.addErrorLogging(future, LOG, "Commit transaction");
567 for (String fixedIp : fixedIps) {
568 installArpResponderFlowsToExternalNetworkIp(macAddress, dpnId, extInterfaceName, lportTag, vpnId,
574 private void doAddArpResponderFlowsToExternalNetworkIps(String id, Collection<String> fixedIps, String macAddress,
575 BigInteger dpnId, Uuid extNetworkId, WriteTransaction writeTx, String extInterfaceName) {
576 Interface extInterfaceState = InterfaceUtils.getInterfaceStateFromOperDS(dataBroker, extInterfaceName);
577 if (extInterfaceState == null) {
578 LOG.debug("No interface state found for interface {}. Delaying responder flows for {}", extInterfaceName,
583 Integer lportTag = extInterfaceState.getIfIndex();
584 if (lportTag == null) {
585 LOG.debug("No Lport tag found for interface {}. Delaying flows for router-id {}", extInterfaceName, id);
589 if (macAddress == null) {
591 LOG.debug("Failed to install arp responder flows for router-gateway-ip {} for router {}."
592 + "External Gw MacAddress is missing.", fixedIps, id);
596 long vpnId = getVpnIdFromExtNetworkId(extNetworkId);
597 addArpResponderFlowsToExternalNetworkIps(id, fixedIps, macAddress, dpnId, vpnId, extInterfaceName, lportTag,
602 public void removeArpResponderFlowsToExternalNetworkIps(String id, Collection<String> fixedIps, String macAddress,
603 BigInteger dpnId, Uuid extNetworkId, WriteTransaction writeTx) {
605 if (dpnId == null || BigInteger.ZERO.equals(dpnId)) {
606 LOG.warn("Failed to remove arp responder flows for router {}. DPN id is missing.", id);
610 String extInterfaceName = elanService.getExternalElanInterface(extNetworkId.getValue(), dpnId);
611 if (extInterfaceName == null) {
612 LOG.warn("Failed to remove responder flows for {}. No external interface found for DPN id {}", id, dpnId);
616 Interface extInterfaceState = InterfaceUtils.getInterfaceStateFromOperDS(dataBroker, extInterfaceName);
617 if (extInterfaceState == null) {
618 LOG.debug("No interface state found for interface {}. Delaying responder flows for {}", extInterfaceName,
623 Integer lportTag = extInterfaceState.getIfIndex();
624 if (lportTag == null) {
625 LOG.debug("No Lport tag found for interface {}. Delaying flows for router-id {}", extInterfaceName, id);
629 if (macAddress == null) {
631 LOG.debug("Failed to remove arp responder flows for router-gateway-ip {} for router {}."
632 + "External Gw MacAddress is missing.", fixedIps, id);
636 removeArpResponderFlowsToExternalNetworkIps(id, fixedIps, dpnId,
637 extInterfaceName, lportTag, writeTx);
641 public void removeArpResponderFlowsToExternalNetworkIps(String id, Collection<String> fixedIps,
642 BigInteger dpnId, String extInterfaceName, int lportTag, WriteTransaction writeTx) {
643 if (fixedIps == null || fixedIps.isEmpty()) {
644 LOG.debug("No external IPs defined for {}", id);
648 LOG.info("Removing ARP responder flows for {} fixed-ips {} on switch {}", id, fixedIps, dpnId);
650 for (String fixedIp : fixedIps) {
651 removeArpResponderFlowsToExternalNetworkIp(dpnId, lportTag, fixedIp, extInterfaceName);
656 public String getPrimaryRdFromVpnInstance(VpnInstance vpnInstance) {
657 return VpnUtil.getPrimaryRd(vpnInstance);
661 public List<MatchInfoBase> getEgressMatchesForVpn(String vpnName) {
662 long vpnId = VpnUtil.getVpnId(dataBroker, vpnName);
663 if (vpnId == VpnConstants.INVALID_ID) {
664 LOG.warn("No VPN id found for {}", vpnName);
665 return Collections.emptyList();
669 .singletonList(new NxMatchRegister(VpnConstants.VPN_REG_ID, vpnId, MetaDataUtil.getVpnIdMaskForReg()));
672 private void installArpResponderFlowsToExternalNetworkIp(String macAddress, BigInteger dpnId,
673 String extInterfaceName, int lportTag, long vpnId, String fixedIp, WriteTransaction writeTx) {
674 // reset the split-horizon bit to allow traffic to be sent back to the
676 List<Instruction> instructions = new ArrayList<>();
678 new InstructionWriteMetadata(BigInteger.ZERO, MetaDataUtil.METADATA_MASK_SH_FLAG).buildInstruction(1));
680 ArpResponderUtil.getExtInterfaceInstructions(interfaceManager, extInterfaceName, fixedIp, macAddress));
681 ArpReponderInputBuilder builder = new ArpReponderInputBuilder().setDpId(dpnId)
682 .setInterfaceName(extInterfaceName).setSpa(fixedIp).setSha(macAddress).setLportTag(lportTag);
683 builder.setInstructions(instructions);
684 elanService.addArpResponderFlow(builder.buildForInstallFlow());
687 private void removeArpResponderFlowsToExternalNetworkIp(BigInteger dpnId, Integer lportTag, String fixedIp,
688 String extInterfaceName) {
689 ArpResponderInput arpInput = new ArpReponderInputBuilder().setDpId(dpnId).setInterfaceName(extInterfaceName)
690 .setSpa(fixedIp).setLportTag(lportTag).buildForRemoveFlow();
691 elanService.removeArpResponderFlow(arpInput);
694 private long getVpnIdFromExtNetworkId(Uuid extNetworkId) {
695 Uuid vpnInstanceId = VpnUtil.getExternalNetworkVpnId(dataBroker, extNetworkId);
696 if (vpnInstanceId == null) {
697 LOG.debug("Network {} is not associated with VPN", extNetworkId.getValue());
698 return VpnConstants.INVALID_ID;
701 return VpnUtil.getVpnId(dataBroker, vpnInstanceId.getValue());
705 public void onSubnetAddedToVpn(Subnetmap subnetmap, boolean isBgpVpn, Long elanTag) {
706 vpnSubnetRouteHandler.onSubnetAddedToVpn(subnetmap, isBgpVpn, elanTag);
710 public void onSubnetDeletedFromVpn(Subnetmap subnetmap, boolean isBgpVpn) {
711 vpnSubnetRouteHandler.onSubnetDeletedFromVpn(subnetmap, isBgpVpn);
715 public VpnInstance getVpnInstance(DataBroker broker, String vpnInstanceName) {
716 return VpnUtil.getVpnInstance(broker, vpnInstanceName);
720 public String getVpnRd(DataBroker broker, String vpnName) {
721 return VpnUtil.getVpnRd(broker, vpnName);
725 public VpnPortipToPort getNeutronPortFromVpnPortFixedIp(DataBroker broker, String vpnName, String fixedIp) {
726 return VpnUtil.getNeutronPortFromVpnPortFixedIp(broker, vpnName, fixedIp);