Bump odlparent->6.0.0,mdsal->5.0.3
[netvirt.git] / fibmanager / impl / src / main / java / org / opendaylight / netvirt / fibmanager / BgpRouteVrfEntryHandler.java
1 /*
2  * Copyright © 2017 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.netvirt.fibmanager;
9
10 import static java.util.stream.Collectors.toList;
11
12 import com.google.common.base.Optional;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.List;
16 import java.util.concurrent.BlockingQueue;
17 import java.util.concurrent.LinkedBlockingQueue;
18 import java.util.function.Consumer;
19 import javax.annotation.PostConstruct;
20 import javax.inject.Inject;
21 import javax.inject.Singleton;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
27 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
28 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
29 import org.opendaylight.genius.mdsalutil.ActionInfo;
30 import org.opendaylight.genius.mdsalutil.InstructionInfo;
31 import org.opendaylight.genius.mdsalutil.NwConstants;
32 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
33 import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadInPort;
34 import org.opendaylight.genius.mdsalutil.actions.ActionPushMpls;
35 import org.opendaylight.genius.mdsalutil.actions.ActionRegLoad;
36 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
37 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldMplsLabel;
38 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
39 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
40 import org.opendaylight.genius.utils.batching.ActionableResource;
41 import org.opendaylight.genius.utils.batching.ActionableResourceImpl;
42 import org.opendaylight.genius.utils.batching.ResourceBatchingManager;
43 import org.opendaylight.genius.utils.batching.ResourceHandler;
44 import org.opendaylight.genius.utils.batching.SubTransaction;
45 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
46 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
47 import org.opendaylight.serviceutils.upgrade.UpgradeState;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeMplsOverGre;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
60 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
61 import org.opendaylight.yangtools.yang.common.Uint32;
62 import org.opendaylight.yangtools.yang.common.Uint64;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65
66
67 @Singleton
68 public class BgpRouteVrfEntryHandler extends BaseVrfEntryHandler implements ResourceHandler {
69
70     private static final Logger LOG = LoggerFactory.getLogger(BgpRouteVrfEntryHandler.class);
71     private static final int BATCH_INTERVAL = 500;
72     private static final int BATCH_SIZE = 1000;
73
74     private final DataBroker dataBroker;
75     private final ManagedNewTransactionRunner txRunner;
76     private final BlockingQueue<ActionableResource> vrfEntryBufferQ = new LinkedBlockingQueue<>();
77     private final ResourceBatchingManager resourceBatchingManager;
78     private final NexthopManager nexthopManager;
79
80     @Inject
81     public BgpRouteVrfEntryHandler(final DataBroker dataBroker,
82                                    final NexthopManager nexthopManager,
83                                    final FibUtil fibUtil,
84                                    final UpgradeState upgradeState,
85                                    final DataTreeEventCallbackRegistrar eventCallbacks) {
86         super(dataBroker, nexthopManager, null, fibUtil, upgradeState, eventCallbacks);
87         this.dataBroker = dataBroker;
88         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
89         this.nexthopManager = nexthopManager;
90
91         resourceBatchingManager = ResourceBatchingManager.getInstance();
92         resourceBatchingManager.registerBatchableResource("FIB-VRFENTRY", vrfEntryBufferQ, this);
93     }
94
95     @PostConstruct
96     public void init() {
97         LOG.info("{} start", getClass().getSimpleName());
98     }
99
100     @Override
101     public void close() {
102         LOG.info("{} close", getClass().getSimpleName());
103     }
104
105     @Override
106     public DataBroker getResourceBroker() {
107         return dataBroker;
108     }
109
110     @Override
111     public int getBatchSize() {
112         return BATCH_SIZE;
113     }
114
115     @Override
116     public int getBatchInterval() {
117         return BATCH_INTERVAL;
118     }
119
120     @Override
121     public LogicalDatastoreType getDatastoreType() {
122         return LogicalDatastoreType.CONFIGURATION;
123     }
124
125     @Override
126     public void update(WriteTransaction tx, LogicalDatastoreType datastoreType, InstanceIdentifier identifier,
127                        Object original, Object update, List<SubTransaction> subTxns) {
128         if (original instanceof VrfEntry && update instanceof VrfEntry) {
129             createFibEntries(tx, identifier, (VrfEntry) update, subTxns);
130         }
131     }
132
133     @Override
134     public void create(WriteTransaction tx, LogicalDatastoreType datastoreType, InstanceIdentifier identifier,
135                        Object vrfEntry, List<SubTransaction> subTxns) {
136         if (vrfEntry instanceof VrfEntry) {
137             createFibEntries(tx, identifier, (VrfEntry) vrfEntry, subTxns);
138         }
139     }
140
141     @Override
142     public void delete(WriteTransaction tx, LogicalDatastoreType datastoreType, InstanceIdentifier identifier,
143                        Object vrfEntry, List<SubTransaction> subTxns) {
144         if (vrfEntry instanceof VrfEntry) {
145             deleteFibEntries(tx, identifier, (VrfEntry) vrfEntry, subTxns);
146         }
147     }
148
149     void createFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
150         ActionableResource actResource = new ActionableResourceImpl(rd + vrfEntry.getDestPrefix());
151         actResource.setAction(ActionableResource.CREATE);
152         actResource.setInstanceIdentifier(identifier);
153         actResource.setInstance(vrfEntry);
154         vrfEntryBufferQ.add(actResource);
155     }
156
157     void removeFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
158         ActionableResource actResource = new ActionableResourceImpl(rd + vrfEntry.getDestPrefix());
159         actResource.setAction(ActionableResource.DELETE);
160         actResource.setInstanceIdentifier(identifier);
161         actResource.setInstance(vrfEntry);
162         vrfEntryBufferQ.add(actResource);
163     }
164
165     void updateFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry original, VrfEntry update, String rd) {
166         ActionableResource actResource = new ActionableResourceImpl(rd + update.getDestPrefix());
167         actResource.setAction(ActionableResource.UPDATE);
168         actResource.setInstanceIdentifier(identifier);
169         actResource.setInstance(update);
170         actResource.setOldInstance(original);
171         vrfEntryBufferQ.add(actResource);
172     }
173
174     /*
175       Please note that the following createFibEntries will be invoked only for BGP Imported Routes.
176       The invocation of the following method is via create() callback from the MDSAL Batching Infrastructure
177       provided by ResourceBatchingManager
178      */
179     private void createFibEntries(WriteTransaction writeTx, final InstanceIdentifier<VrfEntry> vrfEntryIid,
180                                   final VrfEntry vrfEntry, List<SubTransaction> subTxns) {
181         final VrfTablesKey vrfTableKey = vrfEntryIid.firstKeyOf(VrfTables.class);
182         LOG.trace("Creating fib entry for vrfEntry with destPrefix{}, rd {}",
183             vrfEntry.getDestPrefix(), vrfTableKey.getRouteDistinguisher());
184         final VpnInstanceOpDataEntry vpnInstance =
185                 getFibUtil().getVpnInstance(vrfTableKey.getRouteDistinguisher());
186         if (vpnInstance == null || vpnInstance.getVpnId() == null) {
187             LOG.error("Vpn Instance not availabe {}", vrfTableKey.getRouteDistinguisher());
188             return;
189         }
190         final Collection<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
191         if (vpnToDpnList != null) {
192             for (VpnToDpnList vpnDpn : vpnToDpnList) {
193                 LOG.trace("Dpnstate is {} for dpn {} in vpn {}", vpnDpn.getDpnState(), vpnDpn.getDpnId(),
194                     vpnInstance.getVpnId());
195                 if (vpnDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
196                     createRemoteFibEntry(vpnDpn.getDpnId(), vpnInstance.getVpnId(),
197                             vrfTableKey.getRouteDistinguisher(), vrfEntry, writeTx, subTxns);
198                 }
199             }
200         }
201         LOG.trace("Created fib entry for vrfEntry with destPrefix{}, rd {}",
202             vrfEntry.getDestPrefix(), vrfTableKey.getRouteDistinguisher());
203     }
204
205     /*
206       Please note that the following deleteFibEntries will be invoked only for BGP Imported Routes.
207       The invocation of the following method is via delete() callback from the MDSAL Batching Infrastructure
208       provided by ResourceBatchingManager
209      */
210     private void deleteFibEntries(WriteTransaction writeTx, final InstanceIdentifier<VrfEntry> identifier,
211                                   final VrfEntry vrfEntry, List<SubTransaction> subTxns) {
212         final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
213         String rd = vrfTableKey.getRouteDistinguisher();
214         final VpnInstanceOpDataEntry vpnInstance =
215                 getFibUtil().getVpnInstance(vrfTableKey.getRouteDistinguisher());
216         if (vpnInstance == null) {
217             LOG.debug("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
218             return;
219         }
220         String vpnName = getFibUtil().getVpnNameFromId(vpnInstance.getVpnId());
221         final Collection<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
222         if (vpnToDpnList != null) {
223             List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
224                     vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
225             Optional<Routes> extraRouteOptional;
226             //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
227             if (usedRds != null && !usedRds.isEmpty()) {
228                 if (usedRds.size() > 1) {
229                     LOG.error("The extra route prefix is still present in some DPNs");
230                     return ;
231                 } else {
232                     extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker, vpnName,
233                             usedRds.get(0), vrfEntry.getDestPrefix());
234                 }
235             } else {
236                 extraRouteOptional = Optional.absent();
237             }
238             for (VpnToDpnList curDpn : vpnToDpnList) {
239                 if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
240                     deleteRemoteRoute(Uint64.ZERO, curDpn.getDpnId(), vpnInstance.getVpnId(),
241                             vrfTableKey, vrfEntry, extraRouteOptional, writeTx, subTxns);
242                 }
243             }
244         }
245     }
246
247     public void programRemoteFibForBgpRoutes(final Uint64 remoteDpnId,
248                                              final Uint32 vpnId,
249                                              final VrfEntry vrfEntry,
250                                              WriteTransaction tx,
251                                              String rd,
252                                              List<NexthopManager.AdjacencyResult> adjacencyResults,
253                                              List<SubTransaction> subTxns) {
254         if (vrfEntry.nonnullRoutePaths().size() > 2) {
255             LOG.error("DC-GW can advertise only 2 bestPaths for prefix {}", vrfEntry.getDestPrefix());
256             return;
257         }
258         LOG.trace("Start programming remote fib for destPrefix {}, vpnId {}, dpnId {}",
259             vrfEntry.getDestPrefix(), vpnId, remoteDpnId);
260         if (adjacencyResults.size() == 1) {
261             programRemoteFib(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
262             return;
263         }
264         // ECMP Use case, point to LB group. Move the mpls label accordingly.
265         List<String> tunnelList =
266                 adjacencyResults.stream()
267                         .map(NexthopManager.AdjacencyResult::getNextHopIp)
268                         .sorted().collect(toList());
269         String lbGroupKey = FibUtil.getGreLbGroupKey(tunnelList);
270         long groupId = nexthopManager.createNextHopPointer(lbGroupKey);
271         int index = 0;
272         List<ActionInfo> actionInfos = new ArrayList<>();
273         for (NexthopManager.AdjacencyResult adjResult : adjacencyResults) {
274             String nextHopIp = adjResult.getNextHopIp();
275             java.util.Optional<Uint32> optionalLabel = FibUtil.getLabelForNextHop(vrfEntry, nextHopIp);
276             if (!optionalLabel.isPresent()) {
277                 LOG.warn("NextHopIp {} not found in vrfEntry {}", nextHopIp, vrfEntry);
278                 continue;
279             }
280             long label = optionalLabel.get().toJava();
281
282             actionInfos.add(new ActionRegLoad(index, FibConstants.NXM_REG_MAPPING.get(index++), 0,
283                     31, label));
284         }
285         List<InstructionInfo> instructions = new ArrayList<>();
286         actionInfos.add(new ActionGroup(index, groupId));
287         instructions.add(new InstructionApplyActions(actionInfos));
288         makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions, NwConstants.ADD_FLOW, tx, subTxns);
289         LOG.trace("End programming remote fib for destPrefix {}, vpnId {}, dpnId {}",
290                 vrfEntry.getDestPrefix(), vpnId, remoteDpnId);
291     }
292
293     public void createRemoteFibEntry(final Uint64 remoteDpnId,
294                                      final Uint32 vpnId,
295                                      final String rd,
296                                      final VrfEntry vrfEntry,
297                                      WriteTransaction tx,
298                                      List<SubTransaction> subTxns) {
299         if (tx == null) {
300             ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
301                 newTx -> createRemoteFibEntry(remoteDpnId, vpnId, rd, vrfEntry, newTx, subTxns)), LOG,
302                 "Error creating remote FIB entry");
303             return;
304         }
305
306         LOG.debug("createRemoteFibEntry: adding route {} for rd {} on remoteDpnId {}",
307                 vrfEntry.getDestPrefix(), rd, remoteDpnId);
308
309         List<NexthopManager.AdjacencyResult> adjacencyResults =
310                 resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
311         if (adjacencyResults.isEmpty()) {
312             LOG.error("Could not get interface for route-paths: {} in vpn {}", vrfEntry.getRoutePaths(), rd);
313             LOG.warn("Failed to add Route: {} in vpn: {}", vrfEntry.getDestPrefix(), rd);
314             return;
315         }
316
317         programRemoteFibForBgpRoutes(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
318
319         LOG.debug("Successfully added FIB entry for prefix {} in vpnId {}", vrfEntry.getDestPrefix(), vpnId);
320     }
321
322     private void deleteFibEntryForBgpRoutes(Uint64 remoteDpnId, Uint32 vpnId, VrfEntry vrfEntry,
323                                              String rd, WriteTransaction tx, List<SubTransaction> subTxns) {
324         // When the tunnel is removed the fib entries should be reprogrammed/deleted depending on
325         // the adjacencyResults.
326         List<NexthopManager.AdjacencyResult> adjacencyResults = resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
327         if (!adjacencyResults.isEmpty()) {
328             programRemoteFibForBgpRoutes(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
329         }
330     }
331
332     public void deleteRemoteRoute(@Nullable final Uint64 localDpnId, final Uint64 remoteDpnId,
333                                   final Uint32 vpnId, final VrfTablesKey vrfTableKey,
334                                   final VrfEntry vrfEntry, Optional<Routes> extraRouteOptional,
335                                   @Nullable WriteTransaction tx, List<SubTransaction> subTxns) {
336         if (tx == null) {
337             ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(
338                 newTx -> deleteRemoteRoute(localDpnId, remoteDpnId, vpnId, vrfTableKey, vrfEntry,
339                         extraRouteOptional, newTx)), LOG, "Error deleting remote route");
340             return;
341         }
342
343         LOG.debug("deleting remote route: prefix={}, vpnId={} localDpnId {} remoteDpnId {}",
344                 vrfEntry.getDestPrefix(), vpnId, localDpnId, remoteDpnId);
345         String rd = vrfTableKey.getRouteDistinguisher();
346
347         if (localDpnId != null && !Uint64.ZERO.equals(localDpnId)) {
348             // localDpnId is not known when clean up happens for last vm for a vpn on a dpn
349             if (extraRouteOptional.isPresent()) {
350                 nexthopManager.deleteLoadBalancingNextHop(vpnId, remoteDpnId, vrfEntry.getDestPrefix());
351             }
352             deleteFibEntryForBgpRoutes(remoteDpnId, vpnId, vrfEntry, rd, tx, subTxns);
353             return;
354         }
355
356         // below two reads are kept as is, until best way is found to identify dpnID
357         VpnNexthop localNextHopInfo = nexthopManager.getVpnNexthop(vpnId, vrfEntry.getDestPrefix());
358         if (extraRouteOptional.isPresent()) {
359             nexthopManager.deleteLoadBalancingNextHop(vpnId, remoteDpnId, vrfEntry.getDestPrefix());
360         } else {
361             checkDpnDeleteFibEntry(localNextHopInfo, remoteDpnId, vpnId, vrfEntry, rd, tx, subTxns);
362         }
363     }
364
365     public Consumer<? super VrfEntry> getConsumerForCreatingRemoteFib(
366             final Uint64 dpnId, final Uint32 vpnId, final String rd,
367             final String remoteNextHopIp, final Optional<VrfTables> vrfTable,
368             WriteTransaction writeCfgTxn, List<SubTransaction> subTxns) {
369         return vrfEntry -> vrfEntry.nonnullRoutePaths().stream()
370                 .filter(routes -> !routes.getNexthopAddress().isEmpty()
371                         && remoteNextHopIp.trim().equals(routes.getNexthopAddress().trim()))
372                 .findFirst()
373                 .ifPresent(routes -> {
374                     LOG.trace("creating remote FIB entry for prefix {} rd {} on Dpn {}",
375                             vrfEntry.getDestPrefix(), rd, dpnId);
376                     createRemoteFibEntry(dpnId, vpnId, vrfTable.get().getRouteDistinguisher(),
377                             vrfEntry, writeCfgTxn, subTxns);
378                 });
379     }
380
381     public Consumer<? super VrfEntry> getConsumerForDeletingRemoteFib(
382             final Uint64 dpnId, final Uint32 vpnId,
383             final String remoteNextHopIp, final Optional<VrfTables> vrfTable,
384             WriteTransaction writeCfgTxn, List<SubTransaction> subTxns) {
385         return vrfEntry -> vrfEntry.nonnullRoutePaths().stream()
386                 .filter(routes -> !routes.getNexthopAddress().isEmpty()
387                         && remoteNextHopIp.trim().equals(routes.getNexthopAddress().trim()))
388                 .findFirst()
389                 .ifPresent(routes -> {
390                     LOG.trace(" deleting remote FIB entry {}", vrfEntry);
391                     deleteRemoteRoute(null, dpnId, vpnId, vrfTable.get().key(), vrfEntry,
392                             Optional.absent(), writeCfgTxn, subTxns);
393                 });
394     }
395
396     @Override
397     protected void addTunnelInterfaceActions(NexthopManager.AdjacencyResult adjacencyResult, Uint32 vpnId,
398             VrfEntry vrfEntry, List<ActionInfo> actionInfos, String rd) {
399         Class<? extends TunnelTypeBase> tunnelType = VpnExtraRouteHelper
400                 .getTunnelType(getNextHopManager().getItmManager(), adjacencyResult.getInterfaceName());
401         if (tunnelType == null) {
402             LOG.debug("Tunnel type not found for vrfEntry {}", vrfEntry);
403             return;
404         }
405         String nextHopIp = adjacencyResult.getNextHopIp();
406         if (tunnelType.equals(TunnelTypeMplsOverGre.class)) {
407             java.util.Optional<Uint32> optionalLabel = FibUtil.getLabelForNextHop(vrfEntry, nextHopIp);
408             if (!optionalLabel.isPresent()) {
409                 LOG.warn("NextHopIp {} not found in vrfEntry {}", nextHopIp, vrfEntry);
410                 return;
411             }
412             long label = optionalLabel.get().toJava();
413             LOG.debug("addTunnelInterfaceActions: Push label action for prefix {} rd {} l3vni {} nextHop {}",
414                     vrfEntry.getDestPrefix(), rd, vrfEntry.getL3vni(), nextHopIp);
415             actionInfos.add(new ActionPushMpls());
416             actionInfos.add(new ActionSetFieldMplsLabel(label));
417             actionInfos.add(new ActionNxLoadInPort(Uint64.ZERO));
418         } else if (tunnelType.equals(TunnelTypeVxlan.class)) {
419             actionInfos.add(new ActionSetFieldTunnelId(Uint64.valueOf(vrfEntry.getL3vni().longValue())));
420             LOG.debug("addTunnelInterfaceActions: adding set tunnel id action for prefix {} rd {} l3vni {}"
421                     + " nextHop {} ", vrfEntry.getDestPrefix(), rd, vrfEntry.getL3vni(), nextHopIp);
422             addRewriteDstMacAction(vpnId, vrfEntry, null /*prefixInfo*/, actionInfos);
423         }
424     }
425
426     @Override
427     protected void addRewriteDstMacAction(Uint32 vpnId, VrfEntry vrfEntry, @Nullable Prefixes prefixInfo,
428                                           List<ActionInfo> actionInfos) {
429         if (vrfEntry.getGatewayMacAddress() != null) {
430             actionInfos.add(new ActionSetFieldEthernetDestination(actionInfos.size(),
431                     new MacAddress(vrfEntry.getGatewayMacAddress())));
432         }
433     }
434
435 }