2 * Copyright (c) 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
9 package org.opendaylight.netvirt.natservice.internal;
11 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
13 import com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.Futures;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import com.google.common.util.concurrent.MoreExecutors;
17 import java.math.BigInteger;
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 import java.util.List;
22 import java.util.concurrent.ExecutionException;
23 import javax.inject.Inject;
24 import javax.inject.Singleton;
25 import org.eclipse.jdt.annotation.NonNull;
26 import org.opendaylight.genius.infra.Datastore.Configuration;
27 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
28 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
29 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
30 import org.opendaylight.genius.infra.TypedWriteTransaction;
31 import org.opendaylight.genius.mdsalutil.MDSALUtil;
32 import org.opendaylight.genius.mdsalutil.MatchInfo;
33 import org.opendaylight.genius.mdsalutil.NwConstants;
34 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
35 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
36 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
37 import org.opendaylight.infrautils.utils.concurrent.LoggingFutures;
38 import org.opendaylight.mdsal.binding.api.DataBroker;
39 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
40 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
41 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInput;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInputBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryOutput;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInput;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryOutput;
53 import org.opendaylight.yangtools.yang.common.RpcResult;
54 import org.opendaylight.yangtools.yang.common.Uint32;
55 import org.opendaylight.yangtools.yang.common.Uint64;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
60 public class EvpnSnatFlowProgrammer {
61 private static final Logger LOG = LoggerFactory.getLogger(EvpnSnatFlowProgrammer.class);
63 private static final Uint64 COOKIE_TUNNEL = Uint64.valueOf("9000000", 16).intern();
65 private final DataBroker dataBroker;
66 private final ManagedNewTransactionRunner txRunner;
67 private final IMdsalApiManager mdsalManager;
68 private final IBgpManager bgpManager;
69 private final IFibManager fibManager;
70 private final FibRpcService fibService;
71 private final NatOverVxlanUtil natOverVxlanUtil;
74 public EvpnSnatFlowProgrammer(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
75 final IBgpManager bgpManager,
76 final IFibManager fibManager,
77 final FibRpcService fibService,
78 final NatOverVxlanUtil natOverVxlanUtil) {
79 this.dataBroker = dataBroker;
80 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
81 this.mdsalManager = mdsalManager;
82 this.bgpManager = bgpManager;
83 this.fibManager = fibManager;
84 this.fibService = fibService;
85 this.natOverVxlanUtil = natOverVxlanUtil;
88 public void evpnAdvToBgpAndInstallFibAndTsFlows(final Uint64 dpnId, final short tableId,
89 final String externalIp, final String vpnName, final String rd, final String nextHopIp,
90 final Uint32 routerId, final String routerName, final Uuid extNetworkId,
91 TypedWriteTransaction<Configuration> confTx) {
93 * 1) Install the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
94 * (FIP VM on DPN1 is responding back to external fixed IP on DPN2) {DNAT to SNAT traffic on
95 * different Hypervisor}
97 * 2) Install the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
98 * (FIP VM on DPN1 is responding back to external fixed IP on beyond DC-GW VM){DNAT to SNAT Inter DC traffic}
100 * 3) Install the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
101 * (If there is no FIP Match on table 25 (PDNAT_TABLE) then default flow to INBOUND_NAPT_TABLE (table=44))
103 * 4) Install the flow L3_FIB_TABLE (table=21)-> INBOUND_NAPT_TABLE (table=44)
104 * (FIP VM on DPN1 is responding back to external fixed Ip on DPN1 itself. ie. same Hypervisor)
105 * {DNAT to SNAT Intra DC traffic}
107 LOG.info("evpnAdvToBgpAndInstallFibAndTsFlows : Handling SNAT Reverse Traffic for External Fixed IP {} for "
108 + "RouterId {}", externalIp, routerId);
109 // Get the External Gateway MAC Address which is Router gateway MAC address for SNAT
110 String gwMacAddress = NatUtil.getExtGwMacAddFromRouterName(dataBroker, routerName);
111 if (gwMacAddress == null) {
112 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Unable to Retrieve External Gateway MAC address "
113 + "from Router ID {}", routerId);
116 //get l3Vni value for external VPN
117 Uint32 l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
118 if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
119 LOG.debug("evpnAdvToBgpAndInstallFibAndTsFlows : L3VNI value is not configured in Internet VPN {}"
120 + " and RD {} Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue with "
121 + "installing SNAT flows for External Fixed IP {}", vpnName, rd, externalIp);
122 l3Vni = natOverVxlanUtil.getInternetVpnVni(vpnName, routerId);
125 Uint32 vpnId = NatUtil.getVpnId(dataBroker, vpnName);
126 if (vpnId == NatConstants.INVALID_ID) {
127 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Invalid Vpn Id is found for Vpn Name {}",
131 /* As of now neither SNAT nor DNAT will use mac-address while advertising to FIB and BGP instead
132 * use only gwMacAddress. Hence default value of macAddress is null
135 NatEvpnUtil.addRoutesForVxLanProvType(dataBroker, bgpManager, fibManager, vpnName, rd, externalIp,
136 nextHopIp, l3Vni, null /*InterfaceName*/, gwMacAddress, confTx, RouteOrigin.STATIC,
137 dpnId, extNetworkId);
139 //Install custom FIB routes - FIB table.
140 List<Instruction> customInstructions = new ArrayList<>();
141 customInstructions.add(new InstructionGotoTable(tableId).buildInstruction(0));
142 final String externalFixedIp = NatUtil.validateAndAddNetworkMask(externalIp);
144 CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName)
145 .setSourceDpid(dpnId).setIpAddress(externalFixedIp)
146 .setServiceId(l3Vni).setIpAddressSource(CreateFibEntryInput.IpAddressSource.ExternalFixedIP)
147 .setInstruction(customInstructions).build();
148 LOG.debug("evpnAdvToBgpAndInstallFibAndTsFlows : Installing custom FIB table {} --> table {} flow on "
149 + "NAPT Switch {} with l3Vni {}, ExternalFixedIp {}, ExternalVpnName {} for RouterId {}",
150 NwConstants.L3_FIB_TABLE, tableId, dpnId, l3Vni, externalIp, vpnName, routerId);
152 ListenableFuture<RpcResult<CreateFibEntryOutput>> futureVxlan = fibService.createFibEntry(input);
154 final Uint32 finalL3Vni = l3Vni;
155 Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<CreateFibEntryOutput>>() {
157 public void onFailure(@NonNull Throwable error) {
158 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Error in custom fib routes install process for "
159 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
160 externalIp, dpnId, finalL3Vni, vpnName, routerId, error);
164 public void onSuccess(@NonNull RpcResult<CreateFibEntryOutput> result) {
165 if (result.isSuccessful()) {
166 LOG.info("evpnAdvToBgpAndInstallFibAndTsFlows : Successfully installed custom FIB routes for "
167 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
168 externalIp, dpnId, finalL3Vni, vpnName, routerId);
170 /* Install the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
171 * (SNAT to DNAT reverse Traffic: If traffic is Initiated from NAPT to FIP VM on different Hypervisor)
173 makeTunnelTableEntry(dpnId, finalL3Vni, customInstructions, tableId, confTx);
174 /* Install the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
175 * (SNAT reverse traffic: If the traffic is Initiated from DC-GW to VM (SNAT Reverse traffic))
177 NatEvpnUtil.makeL3GwMacTableEntry(dpnId, vpnId, gwMacAddress, customInstructions, mdsalManager,
180 /* Install the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
181 * If there is no FIP Match on table 25 (PDNAT_TABLE)
183 NatUtil.makePreDnatToSnatTableEntry(mdsalManager, dpnId, tableId, confTx);
186 }, MoreExecutors.directExecutor());
189 public void evpnDelFibTsAndReverseTraffic(final Uint64 dpnId, final Uint32 routerId, final String externalIp,
190 final String vpnName, String extGwMacAddress) {
192 * 1) Remove the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
193 * (FIP VM on DPN1 is responding back to external fixed IP on DPN2) {DNAT to SNAT traffic on
194 * different Hypervisor}
196 * 2) Remove the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
197 * (FIP VM on DPN1 is responding back to external fixed IP on DPN1 itself){DNAT to SNAT traffic on
200 * 3) Remove the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
201 * (If there is no FIP Match on table 25 (PDNAT_TABLE) then default flow to INBOUND_NAPT_TABLE (table=44))
203 * 4) Remove the flow L3_FIB_TABLE (table=21)-> INBOUND_NAPT_TABLE (table=44)
204 * (FIP VM on DPN1 is responding back to external fixed Ip on DPN1 itself. ie. same Hypervisor)
205 * {DNAT to SNAT Intra DC traffic}
207 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
209 LOG.error("evpnDelFibTsAndReverseTraffic : Could not retrieve RD value from VPN Name {}", vpnName);
212 Uint32 vpnId = NatUtil.getVpnId(dataBroker, vpnName);
213 if (vpnId == NatConstants.INVALID_ID) {
214 LOG.error("evpnDelFibTsAndReverseTraffic : Invalid Vpn Id is found for Vpn Name {}", vpnName);
217 if (extGwMacAddress == null) {
218 LOG.error("evpnDelFibTsAndReverseTraffic : Unable to Get External Gateway MAC address for "
219 + "External Router ID {} ", routerId);
222 Uint32 l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
223 if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
224 LOG.debug("evpnDelFibTsAndReverseTraffic : L3VNI value is not configured in Internet VPN {} and RD {} "
225 + "Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue with installing "
226 + "SNAT flows for External Fixed IP {}", vpnName, rd, externalIp);
227 l3Vni = natOverVxlanUtil.getInternetVpnVni(vpnName, routerId);
230 final String externalFixedIp = NatUtil.validateAndAddNetworkMask(externalIp);
231 RemoveFibEntryInput input = new RemoveFibEntryInputBuilder()
232 .setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalFixedIp)
233 .setIpAddressSource(RemoveFibEntryInput.IpAddressSource.ExternalFixedIP).setServiceId(l3Vni).build();
234 LOG.debug("evpnDelFibTsAndReverseTraffic : Removing custom FIB table {} --> table {} flow on "
235 + "NAPT Switch {} with l3Vni {}, ExternalFixedIp {}, ExternalVpnName {} for RouterId {}",
236 NwConstants.L3_FIB_TABLE, NwConstants.INBOUND_NAPT_TABLE, dpnId, l3Vni, externalIp, vpnName, routerId);
238 ListenableFuture<RpcResult<RemoveFibEntryOutput>> futureVxlan = fibService.removeFibEntry(input);
239 final Uint32 finalL3Vni = l3Vni;
240 Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<RemoveFibEntryOutput>>() {
242 public void onFailure(@NonNull Throwable error) {
243 LOG.error("evpnDelFibTsAndReverseTraffic : Error in custom fib routes remove process for "
244 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
245 externalIp, dpnId, finalL3Vni, vpnName, routerId, error);
249 public void onSuccess(@NonNull RpcResult<RemoveFibEntryOutput> result) {
250 if (result.isSuccessful()) {
251 LoggingFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
253 LOG.info("evpnDelFibTsAndReverseTraffic : Successfully removed custom FIB routes for "
254 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for "
255 + "RouterId {}", externalIp, dpnId, finalL3Vni, vpnName, routerId);
257 //remove INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44) flow
258 removeTunnelTableEntry(dpnId, finalL3Vni, innerConfTx);
259 //remove L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44) flow
260 NatUtil.removePreDnatToSnatTableEntry(innerConfTx, mdsalManager, dpnId);
261 //remove PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44) flow
262 NatEvpnUtil.removeL3GwMacTableEntry(dpnId, vpnId, extGwMacAddress, mdsalManager,
264 }), LOG, "Error removing EVPN SNAT table entries");
267 }, MoreExecutors.directExecutor());
270 public void makeTunnelTableEntry(Uint64 dpnId, Uint32 l3Vni, List<Instruction> customInstructions,
271 short tableId, TypedWriteTransaction<Configuration> confTx) {
272 LOG.debug("makeTunnelTableEntry : Create terminating service table {} --> table {} flow on NAPT DpnId {} "
273 + "with l3Vni {} as matching parameter", NwConstants.INTERNAL_TUNNEL_TABLE, tableId, dpnId, l3Vni);
274 List<MatchInfo> mkMatches = new ArrayList<>();
275 mkMatches.add(new MatchTunnelId(Uint64.valueOf(l3Vni)));
277 Map<InstructionKey, Instruction> customInstructionsMap = new HashMap<InstructionKey, Instruction>();
278 int instructionKey = 0;
279 for (Instruction instructionObj : customInstructions) {
280 customInstructionsMap.put(new InstructionKey(++instructionKey), instructionObj);
283 Flow terminatingServiceTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
284 NatEvpnUtil.getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, l3Vni, NatConstants.SNAT_FLOW_NAME),
285 NatConstants.DEFAULT_VPN_INTERNAL_TUNNEL_TABLE_PRIORITY,
286 String.format("%s:%s", "TST Flow Entry ", l3Vni),
287 0, 0, Uint64.valueOf(COOKIE_TUNNEL.toJava().add(BigInteger.valueOf(l3Vni.longValue()))),
288 mkMatches, customInstructionsMap);
289 mdsalManager.addFlow(confTx, dpnId, terminatingServiceTableFlowEntity);
290 LOG.debug("makeTunnelTableEntry : Successfully installed terminating service table flow {} on DpnId {}",
291 terminatingServiceTableFlowEntity, dpnId);
294 public void removeTunnelTableEntry(Uint64 dpnId, Uint32 l3Vni, TypedReadWriteTransaction<Configuration> confTx)
295 throws ExecutionException, InterruptedException {
296 LOG.debug("removeTunnelTableEntry : Remove terminating service table {} --> table {} flow on NAPT DpnId {} "
297 + "with l3Vni {} as matching parameter", NwConstants.INTERNAL_TUNNEL_TABLE,
298 NwConstants.INBOUND_NAPT_TABLE, dpnId, l3Vni);
299 List<MatchInfo> mkMatches = new ArrayList<>();
301 mkMatches.add(new MatchTunnelId(Uint64.valueOf(l3Vni)));
302 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
303 NatEvpnUtil.getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, l3Vni, NatConstants.SNAT_FLOW_NAME),
304 NatConstants.DEFAULT_VPN_INTERNAL_TUNNEL_TABLE_PRIORITY,
305 String.format("%s:%s", "TST Flow Entry ", l3Vni), 0, 0,
306 Uint64.valueOf(COOKIE_TUNNEL.toJava().add(BigInteger.valueOf(l3Vni.longValue()))), mkMatches, null);
307 mdsalManager.removeFlow(confTx, dpnId, flowEntity);
308 LOG.debug("removeTunnelTableEntry : Successfully removed terminating service table flow {} on DpnId {}",