2 * Copyright © 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.fibmanager;
10 import static java.util.Objects.requireNonNull;
11 import static java.util.stream.Collectors.toList;
13 import java.util.ArrayList;
14 import java.util.List;
16 import java.util.Optional;
17 import java.util.concurrent.BlockingQueue;
18 import java.util.concurrent.LinkedBlockingQueue;
19 import java.util.function.Consumer;
20 import javax.annotation.PostConstruct;
21 import javax.inject.Inject;
22 import javax.inject.Singleton;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.opendaylight.genius.datastoreutils.listeners.DataTreeEventCallbackRegistrar;
25 import org.opendaylight.genius.mdsalutil.ActionInfo;
26 import org.opendaylight.genius.mdsalutil.InstructionInfo;
27 import org.opendaylight.genius.mdsalutil.NwConstants;
28 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
29 import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadInPort;
30 import org.opendaylight.genius.mdsalutil.actions.ActionPushMpls;
31 import org.opendaylight.genius.mdsalutil.actions.ActionRegLoad;
32 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
33 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldMplsLabel;
34 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
35 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
36 import org.opendaylight.genius.utils.batching.ActionableResource;
37 import org.opendaylight.genius.utils.batching.ActionableResources;
38 import org.opendaylight.genius.utils.batching.ResourceBatchingManager;
39 import org.opendaylight.genius.utils.batching.ResourceHandler;
40 import org.opendaylight.genius.utils.batching.SubTransaction;
41 import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
42 import org.opendaylight.mdsal.binding.api.DataBroker;
43 import org.opendaylight.mdsal.binding.api.WriteTransaction;
44 import org.opendaylight.mdsal.binding.util.Datastore;
45 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunner;
46 import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunnerImpl;
47 import org.opendaylight.mdsal.binding.util.TransactionAdapter;
48 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
49 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
50 import org.opendaylight.serviceutils.upgrade.UpgradeState;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeMplsOverGre;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnListKey;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.to.extraroutes.vpn.extra.routes.Routes;
64 import org.opendaylight.yangtools.concepts.Identifier;
65 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
66 import org.opendaylight.yangtools.yang.common.Uint32;
67 import org.opendaylight.yangtools.yang.common.Uint64;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
73 public class BgpRouteVrfEntryHandler extends BaseVrfEntryHandler implements ResourceHandler {
74 private static final class ActionableResourceIdentifier implements Identifier {
75 private static final long serialVersionUID = 1L;
77 private final String routeDistinguisher;
78 private final String destPrefix;
80 ActionableResourceIdentifier(final String routeDistinguisher, final String destPrefix) {
81 this.routeDistinguisher = requireNonNull(routeDistinguisher);
82 this.destPrefix = requireNonNull(destPrefix);
86 public int hashCode() {
87 return routeDistinguisher.hashCode() * 31 + destPrefix.hashCode();
91 public boolean equals(final Object obj) {
95 if (!(obj instanceof ActionableResourceIdentifier)) {
98 final ActionableResourceIdentifier other = (ActionableResourceIdentifier) obj;
99 return routeDistinguisher.equals(other.routeDistinguisher) && destPrefix.equals(other.destPrefix);
103 public String toString() {
104 return routeDistinguisher + destPrefix;
108 private static final Logger LOG = LoggerFactory.getLogger(BgpRouteVrfEntryHandler.class);
109 private static final int BATCH_INTERVAL = 500;
110 private static final int BATCH_SIZE = 1000;
112 private final DataBroker dataBroker;
113 private final ManagedNewTransactionRunner txRunner;
114 private final BlockingQueue<ActionableResource<?>> vrfEntryBufferQ = new LinkedBlockingQueue<>();
115 private final ResourceBatchingManager resourceBatchingManager;
116 private final NexthopManager nexthopManager;
119 public BgpRouteVrfEntryHandler(final DataBroker dataBroker,
120 final NexthopManager nexthopManager,
121 final FibUtil fibUtil,
122 final UpgradeState upgradeState,
123 final DataTreeEventCallbackRegistrar eventCallbacks) {
124 super(dataBroker, nexthopManager, null, fibUtil, upgradeState, eventCallbacks);
125 this.dataBroker = dataBroker;
126 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
127 this.nexthopManager = nexthopManager;
129 resourceBatchingManager = ResourceBatchingManager.getInstance();
130 resourceBatchingManager.registerBatchableResource("FIB-VRFENTRY", vrfEntryBufferQ, this);
135 LOG.info("{} start", getClass().getSimpleName());
139 public void close() {
140 LOG.info("{} close", getClass().getSimpleName());
144 public DataBroker getResourceBroker() {
149 public int getBatchSize() {
154 public int getBatchInterval() {
155 return BATCH_INTERVAL;
159 public LogicalDatastoreType getDatastoreType() {
160 return LogicalDatastoreType.CONFIGURATION;
164 public void update(WriteTransaction tx, LogicalDatastoreType datastoreType, InstanceIdentifier identifier,
165 Object original, Object update, List<SubTransaction> subTxns) {
166 if (original instanceof VrfEntry && update instanceof VrfEntry) {
167 createFibEntries(tx, identifier, (VrfEntry) update, subTxns);
172 public void updateContainer(WriteTransaction tx, LogicalDatastoreType datastoreType, InstanceIdentifier identifier,
173 Object original, Object update, List<SubTransaction> transactionObjects) {
177 public void create(WriteTransaction tx, LogicalDatastoreType datastoreType, InstanceIdentifier identifier,
178 Object vrfEntry, List<SubTransaction> subTxns) {
179 if (vrfEntry instanceof VrfEntry) {
180 createFibEntries(tx, identifier, (VrfEntry) vrfEntry, subTxns);
185 public void delete(WriteTransaction tx, LogicalDatastoreType datastoreType, InstanceIdentifier identifier,
186 Object vrfEntry, List<SubTransaction> subTxns) {
187 if (vrfEntry instanceof VrfEntry) {
188 deleteFibEntries(tx, identifier, (VrfEntry) vrfEntry, subTxns);
192 void createFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
193 vrfEntryBufferQ.add(ActionableResources.create(new ActionableResourceIdentifier(rd, vrfEntry.getDestPrefix()),
194 identifier, vrfEntry));
197 void removeFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry vrfEntry, String rd) {
198 vrfEntryBufferQ.add(ActionableResources.delete(new ActionableResourceIdentifier(rd, vrfEntry.getDestPrefix()),
199 identifier, vrfEntry));
202 void updateFlows(InstanceIdentifier<VrfEntry> identifier, VrfEntry original, VrfEntry update, String rd) {
203 vrfEntryBufferQ.add(ActionableResources.update(new ActionableResourceIdentifier(rd, update.getDestPrefix()),
204 identifier, update, original));
208 Please note that the following createFibEntries will be invoked only for BGP Imported Routes.
209 The invocation of the following method is via create() callback from the MDSAL Batching Infrastructure
210 provided by ResourceBatchingManager
212 private void createFibEntries(WriteTransaction writeTx, final InstanceIdentifier<VrfEntry> vrfEntryIid,
213 final VrfEntry vrfEntry, List<SubTransaction> subTxns) {
214 final VrfTablesKey vrfTableKey = vrfEntryIid.firstKeyOf(VrfTables.class);
215 LOG.trace("Creating fib entry for vrfEntry with destPrefix{}, rd {}",
216 vrfEntry.getDestPrefix(), vrfTableKey.getRouteDistinguisher());
217 final VpnInstanceOpDataEntry vpnInstance =
218 getFibUtil().getVpnInstance(vrfTableKey.getRouteDistinguisher());
219 if (vpnInstance == null || vpnInstance.getVpnId() == null) {
220 LOG.error("Vpn Instance not availabe {}", vrfTableKey.getRouteDistinguisher());
223 final Map<VpnToDpnListKey, VpnToDpnList> keyVpnToDpnListMap = vpnInstance.nonnullVpnToDpnList();
224 if (keyVpnToDpnListMap != null) {
225 for (VpnToDpnList vpnDpn : keyVpnToDpnListMap.values()) {
226 LOG.trace("Dpnstate is {} for dpn {} in vpn {}", vpnDpn.getDpnState(), vpnDpn.getDpnId(),
227 vpnInstance.getVpnId());
228 if (vpnDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
229 createRemoteFibEntry(vpnDpn.getDpnId(), vpnInstance.getVpnId(),
230 vrfTableKey.getRouteDistinguisher(), vrfEntry, writeTx, subTxns);
234 LOG.trace("Created fib entry for vrfEntry with destPrefix{}, rd {}",
235 vrfEntry.getDestPrefix(), vrfTableKey.getRouteDistinguisher());
239 Please note that the following deleteFibEntries will be invoked only for BGP Imported Routes.
240 The invocation of the following method is via delete() callback from the MDSAL Batching Infrastructure
241 provided by ResourceBatchingManager
243 private void deleteFibEntries(WriteTransaction writeTx, final InstanceIdentifier<VrfEntry> identifier,
244 final VrfEntry vrfEntry, List<SubTransaction> subTxns) {
245 final VrfTablesKey vrfTableKey = identifier.firstKeyOf(VrfTables.class);
246 String rd = vrfTableKey.getRouteDistinguisher();
247 final VpnInstanceOpDataEntry vpnInstance =
248 getFibUtil().getVpnInstance(vrfTableKey.getRouteDistinguisher());
249 if (vpnInstance == null) {
250 LOG.debug("VPN Instance for rd {} is not available from VPN Op Instance Datastore", rd);
253 String vpnName = getFibUtil().getVpnNameFromId(vpnInstance.getVpnId());
254 final Map<VpnToDpnListKey, VpnToDpnList> keyVpnToDpnListMap = vpnInstance.nonnullVpnToDpnList();
255 if (keyVpnToDpnListMap != null) {
256 List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker,
257 vpnInstance.getVpnId(), vrfEntry.getDestPrefix());
258 Optional<Routes> extraRouteOptional;
259 //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
260 if (usedRds != null && !usedRds.isEmpty()) {
261 if (usedRds.size() > 1) {
262 LOG.error("The extra route prefix is still present in some DPNs");
265 extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker, vpnName,
266 usedRds.get(0), vrfEntry.getDestPrefix());
269 extraRouteOptional = Optional.empty();
271 for (VpnToDpnList curDpn : keyVpnToDpnListMap.values()) {
272 if (curDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
273 deleteRemoteRoute(Uint64.ZERO, curDpn.getDpnId(), vpnInstance.getVpnId(),
274 vrfTableKey, vrfEntry, extraRouteOptional, writeTx, subTxns);
280 public void programRemoteFibForBgpRoutes(final Uint64 remoteDpnId,
282 final VrfEntry vrfEntry,
285 List<NexthopManager.AdjacencyResult> adjacencyResults,
286 List<SubTransaction> subTxns) {
287 if (vrfEntry.nonnullRoutePaths().size() > 2) {
288 LOG.error("DC-GW can advertise only 2 bestPaths for prefix {}", vrfEntry.getDestPrefix());
291 LOG.trace("Start programming remote fib for destPrefix {}, vpnId {}, dpnId {}",
292 vrfEntry.getDestPrefix(), vpnId, remoteDpnId);
293 if (adjacencyResults.size() == 1) {
294 programRemoteFib(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
297 // ECMP Use case, point to LB group. Move the mpls label accordingly.
298 List<String> tunnelList =
299 adjacencyResults.stream()
300 .map(NexthopManager.AdjacencyResult::getNextHopIp)
301 .sorted().collect(toList());
302 String lbGroupKey = FibUtil.getGreLbGroupKey(tunnelList);
303 long groupId = nexthopManager.createNextHopPointer(lbGroupKey);
305 List<ActionInfo> actionInfos = new ArrayList<>();
306 for (NexthopManager.AdjacencyResult adjResult : adjacencyResults) {
307 String nextHopIp = adjResult.getNextHopIp();
308 java.util.Optional<Uint32> optionalLabel = FibUtil.getLabelForNextHop(vrfEntry, nextHopIp);
309 if (!optionalLabel.isPresent()) {
310 LOG.warn("NextHopIp {} not found in vrfEntry {}", nextHopIp, vrfEntry);
313 long label = optionalLabel.get().toJava();
315 actionInfos.add(new ActionRegLoad(index, FibConstants.NXM_REG_MAPPING.get(index++), 0,
318 List<InstructionInfo> instructions = new ArrayList<>();
319 actionInfos.add(new ActionGroup(index, groupId));
320 instructions.add(new InstructionApplyActions(actionInfos));
321 makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions, NwConstants.ADD_FLOW, tx, subTxns);
322 LOG.trace("End programming remote fib for destPrefix {}, vpnId {}, dpnId {}",
323 vrfEntry.getDestPrefix(), vpnId, remoteDpnId);
326 // Allow deprecated TransactionRunner calls for now
327 @SuppressWarnings("ForbidCertainMethod")
328 public void createRemoteFibEntry(final Uint64 remoteDpnId,
331 final VrfEntry vrfEntry,
333 List<SubTransaction> subTxns) {
335 LoggingFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(Datastore.CONFIGURATION,
336 newTx -> createRemoteFibEntry(remoteDpnId, vpnId, rd, vrfEntry,
337 TransactionAdapter.toWriteTransaction(newTx), subTxns)), LOG,
338 "Error creating remote FIB entry");
342 LOG.debug("createRemoteFibEntry: adding route {} for rd {} on remoteDpnId {}",
343 vrfEntry.getDestPrefix(), rd, remoteDpnId);
345 List<NexthopManager.AdjacencyResult> adjacencyResults =
346 resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
347 if (adjacencyResults.isEmpty()) {
348 LOG.error("Could not get interface for route-paths: {} in vpn {}", vrfEntry.getRoutePaths(), rd);
349 LOG.warn("Failed to add Route: {} in vpn: {}", vrfEntry.getDestPrefix(), rd);
353 programRemoteFibForBgpRoutes(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
355 LOG.debug("Successfully added FIB entry for prefix {} in vpnId {}", vrfEntry.getDestPrefix(), vpnId);
358 private void deleteFibEntryForBgpRoutes(Uint64 remoteDpnId, Uint32 vpnId, VrfEntry vrfEntry,
359 String rd, WriteTransaction tx, List<SubTransaction> subTxns) {
360 // When the tunnel is removed the fib entries should be reprogrammed/deleted depending on
361 // the adjacencyResults.
362 List<NexthopManager.AdjacencyResult> adjacencyResults = resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
363 if (!adjacencyResults.isEmpty()) {
364 programRemoteFibForBgpRoutes(remoteDpnId, vpnId, vrfEntry, tx, rd, adjacencyResults, subTxns);
368 // Allow deprecated TransactionRunner calls for now
369 @SuppressWarnings("ForbidCertainMethod")
370 public void deleteRemoteRoute(@Nullable final Uint64 localDpnId, final Uint64 remoteDpnId,
371 final Uint32 vpnId, final VrfTablesKey vrfTableKey,
372 final VrfEntry vrfEntry, Optional<Routes> extraRouteOptional,
373 @Nullable WriteTransaction tx, List<SubTransaction> subTxns) {
375 LoggingFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(Datastore.CONFIGURATION,
376 newTx -> deleteRemoteRoute(localDpnId, remoteDpnId, vpnId, vrfTableKey, vrfEntry,
377 extraRouteOptional, TransactionAdapter.toWriteTransaction(newTx))),
378 LOG, "Error deleting remote route");
382 LOG.debug("deleting remote route: prefix={}, vpnId={} localDpnId {} remoteDpnId {}",
383 vrfEntry.getDestPrefix(), vpnId, localDpnId, remoteDpnId);
384 String rd = vrfTableKey.getRouteDistinguisher();
386 if (localDpnId != null && !Uint64.ZERO.equals(localDpnId)) {
387 // localDpnId is not known when clean up happens for last vm for a vpn on a dpn
388 if (extraRouteOptional.isPresent()) {
389 nexthopManager.deleteLoadBalancingNextHop(vpnId, remoteDpnId, vrfEntry.getDestPrefix());
391 deleteFibEntryForBgpRoutes(remoteDpnId, vpnId, vrfEntry, rd, tx, subTxns);
395 // below two reads are kept as is, until best way is found to identify dpnID
396 VpnNexthop localNextHopInfo = nexthopManager.getVpnNexthop(vpnId, vrfEntry.getDestPrefix());
397 if (extraRouteOptional.isPresent()) {
398 nexthopManager.deleteLoadBalancingNextHop(vpnId, remoteDpnId, vrfEntry.getDestPrefix());
400 checkDpnDeleteFibEntry(localNextHopInfo, remoteDpnId, vpnId, vrfEntry, rd, tx, subTxns);
404 public Consumer<? super VrfEntry> getConsumerForCreatingRemoteFib(
405 final Uint64 dpnId, final Uint32 vpnId, final String rd,
406 final String remoteNextHopIp, final Optional<VrfTables> vrfTable,
407 WriteTransaction writeCfgTxn, List<SubTransaction> subTxns) {
408 return vrfEntry -> vrfEntry.nonnullRoutePaths().values().stream()
409 .filter(routes -> !routes.getNexthopAddress().isEmpty()
410 && remoteNextHopIp.trim().equals(routes.getNexthopAddress().trim()))
412 .ifPresent(routes -> {
413 LOG.trace("creating remote FIB entry for prefix {} rd {} on Dpn {}",
414 vrfEntry.getDestPrefix(), rd, dpnId);
415 createRemoteFibEntry(dpnId, vpnId, vrfTable.get().getRouteDistinguisher(),
416 vrfEntry, writeCfgTxn, subTxns);
420 public Consumer<? super VrfEntry> getConsumerForDeletingRemoteFib(
421 final Uint64 dpnId, final Uint32 vpnId,
422 final String remoteNextHopIp, final Optional<VrfTables> vrfTable,
423 WriteTransaction writeCfgTxn, List<SubTransaction> subTxns) {
424 return vrfEntry -> vrfEntry.nonnullRoutePaths().values().stream()
425 .filter(routes -> !routes.getNexthopAddress().isEmpty()
426 && remoteNextHopIp.trim().equals(routes.getNexthopAddress().trim()))
428 .ifPresent(routes -> {
429 LOG.trace(" deleting remote FIB entry {}", vrfEntry);
430 deleteRemoteRoute(null, dpnId, vpnId, vrfTable.get().key(), vrfEntry,
431 Optional.empty(), writeCfgTxn, subTxns);
436 protected void addTunnelInterfaceActions(NexthopManager.AdjacencyResult adjacencyResult, Uint32 vpnId,
437 VrfEntry vrfEntry, List<ActionInfo> actionInfos, String rd) {
438 Class<? extends TunnelTypeBase> tunnelType = VpnExtraRouteHelper
439 .getTunnelType(getNextHopManager().getItmManager(), adjacencyResult.getInterfaceName());
440 if (tunnelType == null) {
441 LOG.debug("Tunnel type not found for vrfEntry {}", vrfEntry);
444 String nextHopIp = adjacencyResult.getNextHopIp();
445 if (tunnelType.equals(TunnelTypeMplsOverGre.class)) {
446 java.util.Optional<Uint32> optionalLabel = FibUtil.getLabelForNextHop(vrfEntry, nextHopIp);
447 if (!optionalLabel.isPresent()) {
448 LOG.warn("NextHopIp {} not found in vrfEntry {}", nextHopIp, vrfEntry);
451 long label = optionalLabel.get().toJava();
452 LOG.debug("addTunnelInterfaceActions: Push label action for prefix {} rd {} l3vni {} nextHop {}",
453 vrfEntry.getDestPrefix(), rd, vrfEntry.getL3vni(), nextHopIp);
454 actionInfos.add(new ActionPushMpls());
455 actionInfos.add(new ActionSetFieldMplsLabel(label));
456 actionInfos.add(new ActionNxLoadInPort(Uint64.ZERO));
457 } else if (tunnelType.equals(TunnelTypeVxlan.class)) {
458 actionInfos.add(new ActionSetFieldTunnelId(Uint64.valueOf(vrfEntry.getL3vni().longValue())));
459 LOG.debug("addTunnelInterfaceActions: adding set tunnel id action for prefix {} rd {} l3vni {}"
460 + " nextHop {} ", vrfEntry.getDestPrefix(), rd, vrfEntry.getL3vni(), nextHopIp);
461 addRewriteDstMacAction(vpnId, vrfEntry, null /*prefixInfo*/, actionInfos);
466 protected void addRewriteDstMacAction(Uint32 vpnId, VrfEntry vrfEntry, @Nullable Prefixes prefixInfo,
467 List<ActionInfo> actionInfos) {
468 if (vrfEntry.getGatewayMacAddress() != null) {
469 actionInfos.add(new ActionSetFieldEthernetDestination(actionInfos.size(),
470 new MacAddress(vrfEntry.getGatewayMacAddress())));