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.UpgradeState;
34 import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
35 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
36 import org.opendaylight.genius.mdsalutil.nxmatches.NxMatchRegister;
37 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
38 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
39 import org.opendaylight.netvirt.elan.arp.responder.ArpResponderInput;
40 import org.opendaylight.netvirt.elan.arp.responder.ArpResponderInput.ArpReponderInputBuilder;
41 import org.opendaylight.netvirt.elan.arp.responder.ArpResponderUtil;
42 import org.opendaylight.netvirt.elanmanager.api.IElanService;
43 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
44 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
45 import org.opendaylight.netvirt.vpnmanager.api.IVpnManager;
46 import org.opendaylight.netvirt.vpnmanager.api.InterfaceUtils;
47 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
48 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.IVpnLinkService;
49 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkCache;
50 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkDataComposite;
51 import org.opendaylight.netvirt.vpnmanager.populator.input.L3vpnInput;
52 import org.opendaylight.netvirt.vpnmanager.populator.registry.L3vpnRegistry;
53 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
54 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
55 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
56 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInput;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInputBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.elan.rev150602.elan.dpn.interfaces.elan.dpn.interfaces.list.DpnInterfaces;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.portip.port.data.VpnPortipToPort;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
70 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
71 import org.opendaylight.yangtools.yang.common.RpcError;
72 import org.opendaylight.yangtools.yang.common.RpcResult;
73 import org.slf4j.Logger;
74 import org.slf4j.LoggerFactory;
77 public class VpnManagerImpl implements IVpnManager {
79 private static final Logger LOG = LoggerFactory.getLogger(VpnManagerImpl.class);
80 private final DataBroker dataBroker;
81 private final ManagedNewTransactionRunner txRunner;
82 private final IdManagerService idManager;
83 private final IMdsalApiManager mdsalManager;
84 private final IElanService elanService;
85 private final IInterfaceManager interfaceManager;
86 private final VpnSubnetRouteHandler vpnSubnetRouteHandler;
87 private final OdlInterfaceRpcService ifaceMgrRpcService;
88 private final IVpnLinkService ivpnLinkService;
89 private final IFibManager fibManager;
90 private final IBgpManager bgpManager;
91 private final InterVpnLinkCache interVpnLinkCache;
92 private final DataTreeEventCallbackRegistrar eventCallbacks;
93 private final UpgradeState upgradeState;
96 public VpnManagerImpl(final DataBroker dataBroker,
97 final IdManagerService idManagerService,
98 final IMdsalApiManager mdsalManager,
99 final IElanService elanService,
100 final IInterfaceManager interfaceManager,
101 final VpnSubnetRouteHandler vpnSubnetRouteHandler,
102 final OdlInterfaceRpcService ifaceMgrRpcService,
103 final IVpnLinkService ivpnLinkService,
104 final IFibManager fibManager,
105 final IBgpManager bgpManager,
106 final InterVpnLinkCache interVpnLinkCache,
107 final DataTreeEventCallbackRegistrar dataTreeEventCallbackRegistrar,
108 final UpgradeState upgradeState) {
109 this.dataBroker = dataBroker;
110 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
111 this.idManager = idManagerService;
112 this.mdsalManager = mdsalManager;
113 this.elanService = elanService;
114 this.interfaceManager = interfaceManager;
115 this.vpnSubnetRouteHandler = vpnSubnetRouteHandler;
116 this.ifaceMgrRpcService = ifaceMgrRpcService;
117 this.ivpnLinkService = ivpnLinkService;
118 this.fibManager = fibManager;
119 this.bgpManager = bgpManager;
120 this.interVpnLinkCache = interVpnLinkCache;
121 this.eventCallbacks = dataTreeEventCallbackRegistrar;
122 this.upgradeState = upgradeState;
126 public void start() {
127 LOG.info("{} start", getClass().getSimpleName());
131 private void createIdPool() {
132 CreateIdPoolInput createPool = new CreateIdPoolInputBuilder()
133 .setPoolName(VpnConstants.VPN_IDPOOL_NAME)
134 .setLow(VpnConstants.VPN_IDPOOL_LOW)
135 .setHigh(VpnConstants.VPN_IDPOOL_HIGH)
138 Future<RpcResult<Void>> result = idManager.createIdPool(createPool);
139 if (result != null && result.get().isSuccessful()) {
140 LOG.info("Created IdPool for VPN Service");
142 } catch (InterruptedException | ExecutionException e) {
143 LOG.error("Failed to create idPool for VPN Service", e);
146 // Now an IdPool for InterVpnLink endpoint's pseudo ports
147 CreateIdPoolInput createPseudoLporTagPool =
148 new CreateIdPoolInputBuilder().setPoolName(VpnConstants.PSEUDO_LPORT_TAG_ID_POOL_NAME)
149 .setLow(VpnConstants.LOWER_PSEUDO_LPORT_TAG)
150 .setHigh(VpnConstants.UPPER_PSEUDO_LPORT_TAG)
153 Future<RpcResult<Void>> result = idManager.createIdPool(createPseudoLporTagPool);
154 if (result.get().isSuccessful()) {
155 LOG.debug("Created IdPool for Pseudo Port tags");
157 Collection<RpcError> errors = result.get().getErrors();
158 StringBuilder errMsg = new StringBuilder();
159 for (RpcError err : errors) {
160 errMsg.append(err.getMessage()).append("\n");
162 LOG.error("IdPool creation for PseudoPort tags failed. Reasons: {}", errMsg);
164 } catch (InterruptedException | ExecutionException e) {
165 LOG.error("Failed to create idPool for Pseudo Port tags", e);
170 public void addExtraRoute(String vpnName, String destination, String nextHop, String rd, String routerID,
171 int label,RouteOrigin origin) {
172 LOG.info("Adding extra route with destination {}, nextHop {}, label{} and origin {}",
173 destination, nextHop, label, origin);
174 VpnInstanceOpDataEntry vpnOpEntry = VpnUtil.getVpnInstanceOpData(dataBroker, rd);
175 Boolean isVxlan = VpnUtil.isL3VpnOverVxLan(vpnOpEntry.getL3vni());
176 VrfEntry.EncapType encapType = VpnUtil.getEncapType(isVxlan);
177 addExtraRoute(vpnName, destination, nextHop, rd, routerID, vpnOpEntry.getL3vni(),
178 origin,/*intfName*/ null, null /*Adjacency*/, encapType, null);
182 public void addExtraRoute(String vpnName, String destination, String nextHop, String rd, String routerID,
183 Long l3vni, RouteOrigin origin, String intfName, Adjacency operationalAdj,
184 VrfEntry.EncapType encapType, WriteTransaction writeConfigTxn) {
186 Boolean writeConfigTxnPresent = true;
187 if (writeConfigTxn == null) {
188 writeConfigTxnPresent = false;
189 writeConfigTxn = dataBroker.newWriteOnlyTransaction();
192 //add extra route to vpn mapping; advertise with nexthop as tunnel ip
195 LogicalDatastoreType.OPERATIONAL,
196 VpnExtraRouteHelper.getVpnToExtrarouteVrfIdIdentifier(vpnName, rd != null ? rd : routerID,
198 VpnUtil.getVpnToExtraroute(destination, Collections.singletonList(nextHop)));
200 BigInteger dpnId = null;
201 if (intfName != null && !intfName.isEmpty()) {
202 dpnId = InterfaceUtils.getDpnForInterface(ifaceMgrRpcService, intfName);
203 String nextHopIp = InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, dpnId);
204 if (nextHopIp == null || nextHopIp.isEmpty()) {
205 LOG.error("addExtraRoute: NextHop for interface {} is null / empty."
206 + " Failed advertising extra route for rd {} prefix {} dpn {}", intfName, rd, destination,
213 String primaryRd = VpnUtil.getPrimaryRd(dataBroker, vpnName);
215 // TODO: This is a limitation to be stated in docs. When configuring static route to go to
216 // another VPN, there can only be one nexthop or, at least, the nexthop to the interVpnLink should be in
218 Optional<InterVpnLinkDataComposite> optVpnLink = interVpnLinkCache.getInterVpnLinkByEndpoint(nextHop);
219 if (optVpnLink.isPresent() && optVpnLink.get().isActive()) {
220 InterVpnLinkDataComposite interVpnLink = optVpnLink.get();
221 // If the nexthop is the endpoint of Vpn2, then prefix must be advertised to Vpn1 in DC-GW, with nexthops
222 // pointing to the DPNs where Vpn1 is instantiated. LFIB in these DPNS must have a flow entry, with lower
223 // priority, where if Label matches then sets the lportTag of the Vpn2 endpoint and goes to LportDispatcher
224 // This is like leaking one of the Vpn2 routes towards Vpn1
225 String srcVpnUuid = interVpnLink.getVpnNameByIpAddress(nextHop);
226 String dstVpnUuid = interVpnLink.getOtherVpnNameByIpAddress(nextHop);
227 String dstVpnRd = VpnUtil.getVpnRd(dataBroker, dstVpnUuid);
228 long newLabel = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME,
229 VpnUtil.getNextHopLabelKey(dstVpnRd, destination));
231 LOG.error("addExtraRoute: Unable to fetch label from Id Manager. Bailing out of adding intervpnlink"
232 + " route for destination {}", destination);
235 ivpnLinkService.leakRoute(interVpnLink, srcVpnUuid, dstVpnUuid, destination, newLabel, RouteOrigin.STATIC);
237 Optional<Routes> optVpnExtraRoutes = VpnExtraRouteHelper
238 .getVpnExtraroutes(dataBroker, vpnName, rd != null ? rd : routerID, destination);
239 if (optVpnExtraRoutes.isPresent()) {
240 List<String> nhList = optVpnExtraRoutes.get().getNexthopIpList();
241 if (nhList != null && nhList.size() > 1) {
242 // If nhList is greater than one for vpnextraroute, a call to populatefib doesn't update vrfentry.
243 fibManager.refreshVrfEntry(primaryRd, destination);
245 L3vpnInput input = new L3vpnInput().setNextHop(operationalAdj).setNextHopIp(nextHop).setL3vni(l3vni)
246 .setPrimaryRd(primaryRd).setVpnName(vpnName).setDpnId(dpnId)
247 .setEncapType(encapType).setRd(rd).setRouteOrigin(origin);
248 L3vpnRegistry.getRegisteredPopulator(encapType).populateFib(input, writeConfigTxn);
253 if (!writeConfigTxnPresent) {
254 writeConfigTxn.submit();
259 public void delExtraRoute(String vpnName, String destination, String nextHop, String rd, String routerID) {
260 LOG.info("Deleting extra route with destination {} and nextHop {}", destination, nextHop);
261 delExtraRoute(vpnName, destination, nextHop, rd, routerID, null, null);
265 public void delExtraRoute(String vpnName, String destination, String nextHop, String rd, String routerID,
266 String intfName, WriteTransaction writeConfigTxn) {
267 Boolean writeConfigTxnPresent = true;
268 BigInteger dpnId = null;
269 if (writeConfigTxn == null) {
270 writeConfigTxnPresent = false;
271 writeConfigTxn = dataBroker.newWriteOnlyTransaction();
273 String tunnelIp = nextHop;
274 if (intfName != null && !intfName.isEmpty()) {
275 dpnId = InterfaceUtils.getDpnForInterface(ifaceMgrRpcService, intfName);
276 String nextHopIp = InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, dpnId);
277 if (nextHopIp == null || nextHopIp.isEmpty()) {
278 LOG.error("delExtraRoute: NextHop for interface {} is null / empty."
279 + " Failed advertising extra route for rd {} prefix {} dpn {}", intfName, rd, destination,
282 tunnelIp = nextHopIp;
285 String primaryRd = VpnUtil.getVpnRd(dataBroker, vpnName);
286 removePrefixFromBGP(primaryRd, rd, vpnName, destination, nextHop, tunnelIp, dpnId, writeConfigTxn);
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, writeConfigTxn);
291 LOG.info("delExtraRoute: Removed extra route {} from interface {} for rd {}", destination, intfName,
294 if (!writeConfigTxnPresent) {
295 writeConfigTxn.submit();
299 // TODO Clean up the exception handling
301 @SuppressWarnings("checkstyle:IllegalCatch")
302 public void removePrefixFromBGP(String primaryRd, String rd, String vpnName, String prefix, String nextHop,
303 String tunnelIp, BigInteger dpnId, WriteTransaction writeConfigTxn) {
305 LOG.info("removePrefixFromBGP: VPN WITHDRAW: Removing Fib Entry rd {} prefix {} nexthop {}", rd, prefix,
307 String vpnNamePrefixKey = VpnUtil.getVpnNamePrefixKey(vpnName, prefix);
308 synchronized (vpnNamePrefixKey.intern()) {
309 Optional<Routes> optVpnExtraRoutes = VpnExtraRouteHelper
310 .getVpnExtraroutes(dataBroker, vpnName, rd, prefix);
311 if (optVpnExtraRoutes.isPresent()) {
312 List<String> nhList = optVpnExtraRoutes.get().getNexthopIpList();
313 if (nhList != null && nhList.size() > 1) {
314 // If nhList is more than 1, just update vpntoextraroute and prefixtointerface DS
315 // For other cases, remove the corresponding tep ip from fibentry and withdraw prefix
316 nhList.remove(nextHop);
317 VpnUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
318 VpnExtraRouteHelper.getVpnToExtrarouteVrfIdIdentifier(vpnName, rd, prefix),
319 VpnUtil.getVpnToExtraroute(prefix, nhList));
320 MDSALUtil.syncDelete(dataBroker,
321 LogicalDatastoreType.CONFIGURATION, VpnExtraRouteHelper.getUsedRdsIdentifier(
322 VpnUtil.getVpnId(dataBroker, vpnName), prefix, nextHop));
323 LOG.debug("removePrefixFromBGP: Removed vpn-to-extraroute with rd {} prefix {} nexthop {}",
324 rd, prefix, nextHop);
325 fibManager.refreshVrfEntry(primaryRd, prefix);
326 long vpnId = VpnUtil.getVpnId(dataBroker, vpnName);
327 Optional<Prefixes> prefixToInterface = VpnUtil.getPrefixToInterface(dataBroker, vpnId, nextHop);
328 if (prefixToInterface.isPresent()) {
329 writeConfigTxn.delete(LogicalDatastoreType.OPERATIONAL,
330 VpnUtil.getAdjacencyIdentifier(prefixToInterface.get().getVpnInterfaceName(),
333 LOG.info("VPN WITHDRAW: removePrefixFromBGP: Removed Fib Entry rd {} prefix {} nexthop {}",
334 rd, prefix, tunnelIp);
338 fibManager.removeOrUpdateFibEntry(primaryRd, prefix, tunnelIp, writeConfigTxn);
339 if (VpnUtil.isEligibleForBgp(rd, vpnName, dpnId, null /*networkName*/)) {
340 // TODO: Might be needed to include nextHop here
341 bgpManager.withdrawPrefix(rd, prefix);
344 LOG.info("removePrefixFromBGP: VPN WITHDRAW: Removed Fib Entry rd {} prefix {} nexthop {}", rd, prefix,
346 } catch (RuntimeException e) {
347 LOG.error("removePrefixFromBGP: Delete prefix {} rd {} nextHop {} failed", prefix, rd, nextHop);
352 public boolean isVPNConfigured() {
353 InstanceIdentifier<VpnInstances> vpnsIdentifier = InstanceIdentifier.builder(VpnInstances.class).build();
354 Optional<VpnInstances> optionalVpns = TransactionUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
356 if (!optionalVpns.isPresent()
357 || optionalVpns.get().getVpnInstance() == null
358 || optionalVpns.get().getVpnInstance().isEmpty()) {
359 LOG.trace("isVPNConfigured: No VPNs configured.");
362 LOG.trace("isVPNConfigured: VPNs are configured on the system.");
367 public List<BigInteger> getDpnsOnVpn(String vpnInstanceName) {
368 return VpnUtil.getDpnsOnVpn(dataBroker, vpnInstanceName);
372 public boolean existsVpn(String vpnName) {
373 return VpnUtil.getVpnInstance(dataBroker, vpnName) != null;
377 public void addSubnetMacIntoVpnInstance(String vpnName, String subnetVpnName, String srcMacAddress,
378 BigInteger dpnId, WriteTransaction tx) {
379 setupSubnetMacInVpnInstance(vpnName, subnetVpnName, srcMacAddress, dpnId,
380 (vpnId, dpId, subnetVpnId) -> addGwMac(srcMacAddress, tx, vpnId, dpId, subnetVpnId));
384 public void removeSubnetMacFromVpnInstance(String vpnName, String subnetVpnName, String srcMacAddress,
385 BigInteger dpnId, WriteTransaction tx) {
386 setupSubnetMacInVpnInstance(vpnName, subnetVpnName, srcMacAddress, dpnId,
387 (vpnId, dpId, subnetVpnId) -> removeGwMac(srcMacAddress, tx, vpnId, dpId, subnetVpnId));
391 private interface VpnInstanceSubnetMacSetupMethod {
392 void process(long vpnId, BigInteger dpId, long subnetVpnId);
395 private void setupSubnetMacInVpnInstance(String vpnName, String subnetVpnName, String srcMacAddress,
396 BigInteger dpnId, VpnInstanceSubnetMacSetupMethod consumer) {
397 if (vpnName == null) {
398 LOG.warn("Cannot setup subnet MAC {} on DPN {}, null vpnName", srcMacAddress, dpnId);
402 long vpnId = VpnUtil.getVpnId(dataBroker, vpnName);
403 long subnetVpnId = VpnUtil.getVpnId(dataBroker, subnetVpnName);
404 if (dpnId.equals(BigInteger.ZERO)) {
405 /* Apply the MAC on all DPNs in a VPN */
406 for (BigInteger dpId : VpnUtil.getDpnsOnVpn(dataBroker, vpnName)) {
407 consumer.process(vpnId, dpId, subnetVpnId);
410 consumer.process(vpnId, dpnId, subnetVpnId);
414 private void addGwMac(String srcMacAddress, WriteTransaction tx, long vpnId, BigInteger dpId, long subnetVpnId) {
415 FlowEntity flowEntity = VpnUtil.buildL3vpnGatewayFlow(dpId, srcMacAddress, vpnId, subnetVpnId);
416 mdsalManager.addFlowToTx(flowEntity, tx);
419 private void removeGwMac(String srcMacAddress, WriteTransaction tx, long vpnId, BigInteger dpId, long subnetVpnId) {
420 FlowEntity flowEntity = VpnUtil.buildL3vpnGatewayFlow(dpId, srcMacAddress, vpnId, subnetVpnId);
421 mdsalManager.removeFlowToTx(flowEntity, tx);
425 public void addRouterGwMacFlow(String routerName, String routerGwMac, BigInteger dpnId, Uuid extNetworkId,
426 String subnetVpnName, WriteTransaction writeTx) {
427 setupRouterGwMacFlow(routerName, routerGwMac, dpnId, extNetworkId, writeTx,
428 (vpnId, tx) -> addSubnetMacIntoVpnInstance(vpnId, subnetVpnName, routerGwMac, dpnId, tx), "Installing");
432 public void removeRouterGwMacFlow(String routerName, String routerGwMac, BigInteger dpnId, Uuid extNetworkId,
433 String subnetVpnName, WriteTransaction writeTx) {
434 setupRouterGwMacFlow(routerName, routerGwMac, dpnId, extNetworkId, writeTx,
435 (vpnId, tx) -> removeSubnetMacFromVpnInstance(vpnId, subnetVpnName, routerGwMac, dpnId, tx), "Removing");
439 private interface RouterGwMacFlowSetupMethod {
440 void process(String vpnId, WriteTransaction tx);
443 private void setupRouterGwMacFlow(String routerName, String routerGwMac, BigInteger dpnId, Uuid extNetworkId,
444 WriteTransaction writeTx, RouterGwMacFlowSetupMethod consumer, String operation) {
445 if (routerGwMac == null) {
446 LOG.warn("Failed to handle router GW flow in GW-MAC table. MAC address is missing for router-id {}",
451 if (dpnId == null || BigInteger.ZERO.equals(dpnId)) {
452 LOG.info("setupRouterGwMacFlow: DPN id is missing for router-id {}",
457 Uuid vpnId = VpnUtil.getExternalNetworkVpnId(dataBroker, extNetworkId);
459 LOG.warn("Network {} is not associated with VPN", extNetworkId.getValue());
463 LOG.info("{} router GW MAC flow for router-id {} on switch {}", operation, routerName, dpnId);
464 if (writeTx == null) {
465 ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
466 tx -> consumer.process(vpnId.getValue(), tx)), LOG, "Commit transaction");
468 consumer.process(vpnId.getValue(), writeTx);
473 public void addArpResponderFlowsToExternalNetworkIps(String id, Collection<String> fixedIps, String macAddress,
474 BigInteger dpnId, Uuid extNetworkId, WriteTransaction writeTx) {
476 if (dpnId == null || BigInteger.ZERO.equals(dpnId)) {
477 LOG.warn("Failed to install arp responder flows for router {}. DPN id is missing.", id);
481 String extInterfaceName = elanService.getExternalElanInterface(extNetworkId.getValue(), dpnId);
482 if (extInterfaceName != null) {
483 doAddArpResponderFlowsToExternalNetworkIps(
484 id, fixedIps, macAddress, dpnId, extNetworkId, writeTx, extInterfaceName);
488 LOG.warn("Failed to install responder flows for {}. No external interface found for DPN id {}", id, dpnId);
490 if (!upgradeState.isUpgradeInProgress()) {
494 // The following through the end of the function deals with an upgrade scenario where the neutron configuration
495 // is restored before the OVS switches reconnect. In such a case, the elan-dpn-interfaces entries will be
496 // missing from the operational data store. In order to mitigate this we use DataTreeEventCallbackRegistrar
497 // to wait for the exact operational md-sal object we need to contain the external interface we need.
499 LOG.info("Upgrade in process, waiting for an external interface to appear on dpn {} for elan {}",
500 dpnId, extNetworkId.getValue());
502 InstanceIdentifier<DpnInterfaces> dpnInterfacesIid =
503 elanService.getElanDpnInterfaceOperationalDataPath(extNetworkId.getValue(), dpnId);
505 eventCallbacks.onAddOrUpdate(LogicalDatastoreType.OPERATIONAL, dpnInterfacesIid, (unused, alsoUnused) -> {
506 LOG.info("Reattempting write of arp responder for external interfaces for external network {}",
508 DpnInterfaces dpnInterfaces = elanService.getElanInterfaceInfoByElanDpn(extNetworkId.getValue(), dpnId);
509 if (dpnInterfaces == null) {
510 LOG.error("Could not retrieve DpnInterfaces for {}, {}", extNetworkId.getValue(), dpnId);
511 return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER;
514 String extIfc = null;
515 for (String dpnInterface : dpnInterfaces.getInterfaces()) {
516 if (interfaceManager.isExternalInterface(dpnInterface)) {
517 extIfc = dpnInterface;
522 if (extIfc == null) {
523 if (upgradeState.isUpgradeInProgress()) {
524 LOG.info("External interface not found yet in elan {} on dpn {}, keep waiting",
525 extNetworkId.getValue(), dpnInterfaces);
526 return DataTreeEventCallbackRegistrar.NextAction.CALL_AGAIN;
528 return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER;
532 final String extIfcFinal = extIfc;
533 ListenableFuture<Void> listenableFuture = txRunner.callWithNewWriteOnlyTransactionAndSubmit(tx -> {
534 doAddArpResponderFlowsToExternalNetworkIps(
535 id, fixedIps, macAddress, dpnId, extNetworkId, tx, extIfcFinal);
537 ListenableFutures.addErrorLogging(listenableFuture, LOG,
538 "Error while configuring arp responder for ext. interface");
540 return DataTreeEventCallbackRegistrar.NextAction.UNREGISTER; });
545 public void addArpResponderFlowsToExternalNetworkIps(String id, Collection<String> fixedIps, String macAddress,
546 BigInteger dpnId, String extInterfaceName, int lportTag, WriteTransaction writeTx) {
547 if (fixedIps == null || fixedIps.isEmpty()) {
548 LOG.debug("No external IPs defined for {}", id);
552 LOG.info("Installing ARP responder flows for {} fixed-ips {} on switch {}", id, fixedIps, dpnId);
554 if (writeTx == null) {
555 ListenableFuture<Void> future = txRunner.callWithNewWriteOnlyTransactionAndSubmit(
557 for (String fixedIp : fixedIps) {
558 installArpResponderFlowsToExternalNetworkIp(macAddress, dpnId, extInterfaceName, lportTag,
562 ListenableFutures.addErrorLogging(future, LOG, "Commit transaction");
564 for (String fixedIp : fixedIps) {
565 installArpResponderFlowsToExternalNetworkIp(macAddress, dpnId, extInterfaceName, lportTag,
571 private void doAddArpResponderFlowsToExternalNetworkIps(String id, Collection<String> fixedIps, String macAddress,
572 BigInteger dpnId, Uuid extNetworkId, WriteTransaction writeTx, String extInterfaceName) {
573 Interface extInterfaceState = InterfaceUtils.getInterfaceStateFromOperDS(dataBroker, extInterfaceName);
574 if (extInterfaceState == null) {
575 LOG.debug("No interface state found for interface {}. Delaying responder flows for {}", extInterfaceName,
580 Integer lportTag = extInterfaceState.getIfIndex();
581 if (lportTag == null) {
582 LOG.debug("No Lport tag found for interface {}. Delaying flows for router-id {}", extInterfaceName, id);
586 if (macAddress == null) {
588 LOG.debug("Failed to install arp responder flows for router-gateway-ip {} for router {}."
589 + "External Gw MacAddress is missing.", fixedIps, id);
593 addArpResponderFlowsToExternalNetworkIps(id, fixedIps, macAddress, dpnId, extInterfaceName, lportTag,
598 public void removeArpResponderFlowsToExternalNetworkIps(String id, Collection<String> fixedIps, String macAddress,
599 BigInteger dpnId, Uuid extNetworkId) {
601 if (dpnId == null || BigInteger.ZERO.equals(dpnId)) {
602 LOG.warn("Failed to remove arp responder flows for router {}. DPN id is missing.", id);
606 String extInterfaceName = elanService.getExternalElanInterface(extNetworkId.getValue(), dpnId);
607 if (extInterfaceName == null) {
608 LOG.warn("Failed to remove responder flows for {}. No external interface found for DPN id {}", id, dpnId);
612 Interface extInterfaceState = InterfaceUtils.getInterfaceStateFromOperDS(dataBroker, extInterfaceName);
613 if (extInterfaceState == null) {
614 LOG.debug("No interface state found for interface {}. Delaying responder flows for {}", extInterfaceName,
619 Integer lportTag = extInterfaceState.getIfIndex();
620 if (lportTag == null) {
621 LOG.debug("No Lport tag found for interface {}. Delaying flows for router-id {}", extInterfaceName, id);
625 if (macAddress == null) {
627 LOG.debug("Failed to remove arp responder flows for router-gateway-ip {} for router {}."
628 + "External Gw MacAddress is missing.", fixedIps, id);
632 removeArpResponderFlowsToExternalNetworkIps(id, fixedIps, dpnId,
633 extInterfaceName, lportTag);
637 public void removeArpResponderFlowsToExternalNetworkIps(String id, Collection<String> fixedIps,
638 BigInteger dpnId, String extInterfaceName, int lportTag) {
639 if (fixedIps == null || fixedIps.isEmpty()) {
640 LOG.debug("No external IPs defined for {}", id);
644 LOG.info("Removing ARP responder flows for {} fixed-ips {} on switch {}", id, fixedIps, dpnId);
646 for (String fixedIp : fixedIps) {
647 removeArpResponderFlowsToExternalNetworkIp(dpnId, lportTag, fixedIp, extInterfaceName);
652 public String getPrimaryRdFromVpnInstance(VpnInstance vpnInstance) {
653 return VpnUtil.getPrimaryRd(vpnInstance);
657 public List<MatchInfoBase> getEgressMatchesForVpn(String vpnName) {
658 long vpnId = VpnUtil.getVpnId(dataBroker, vpnName);
659 if (vpnId == VpnConstants.INVALID_ID) {
660 LOG.warn("No VPN id found for {}", vpnName);
661 return Collections.emptyList();
665 .singletonList(new NxMatchRegister(VpnConstants.VPN_REG_ID, vpnId, MetaDataUtil.getVpnIdMaskForReg()));
668 private void installArpResponderFlowsToExternalNetworkIp(String macAddress, BigInteger dpnId,
669 String extInterfaceName, int lportTag, String fixedIp) {
670 // reset the split-horizon bit to allow traffic to be sent back to the
672 List<Instruction> instructions = new ArrayList<>();
674 new InstructionWriteMetadata(BigInteger.ZERO, MetaDataUtil.METADATA_MASK_SH_FLAG).buildInstruction(1));
676 ArpResponderUtil.getExtInterfaceInstructions(interfaceManager, extInterfaceName, fixedIp, macAddress));
677 ArpReponderInputBuilder builder = new ArpReponderInputBuilder().setDpId(dpnId)
678 .setInterfaceName(extInterfaceName).setSpa(fixedIp).setSha(macAddress).setLportTag(lportTag);
679 builder.setInstructions(instructions);
680 elanService.addArpResponderFlow(builder.buildForInstallFlow());
683 private void removeArpResponderFlowsToExternalNetworkIp(BigInteger dpnId, Integer lportTag, String fixedIp,
684 String extInterfaceName) {
685 ArpResponderInput arpInput = new ArpReponderInputBuilder().setDpId(dpnId).setInterfaceName(extInterfaceName)
686 .setSpa(fixedIp).setLportTag(lportTag).buildForRemoveFlow();
687 elanService.removeArpResponderFlow(arpInput);
691 public void onSubnetAddedToVpn(Subnetmap subnetmap, boolean isBgpVpn, Long elanTag) {
692 vpnSubnetRouteHandler.onSubnetAddedToVpn(subnetmap, isBgpVpn, elanTag);
696 public void onSubnetDeletedFromVpn(Subnetmap subnetmap, boolean isBgpVpn) {
697 vpnSubnetRouteHandler.onSubnetDeletedFromVpn(subnetmap, isBgpVpn);
701 public VpnInstance getVpnInstance(DataBroker broker, String vpnInstanceName) {
702 return VpnUtil.getVpnInstance(broker, vpnInstanceName);
706 public String getVpnRd(DataBroker broker, String vpnName) {
707 return VpnUtil.getVpnRd(broker, vpnName);
711 public VpnPortipToPort getNeutronPortFromVpnPortFixedIp(DataBroker broker, String vpnName, String fixedIp) {
712 return VpnUtil.getNeutronPortFromVpnPortFixedIp(broker, vpnName, fixedIp);