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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
18 import java.math.BigInteger;
19 import java.util.ArrayList;
20 import java.util.List;
21 import javax.annotation.Nonnull;
22 import javax.inject.Inject;
23 import javax.inject.Singleton;
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.genius.infra.Datastore.Configuration;
26 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
27 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
28 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
29 import org.opendaylight.genius.infra.TypedWriteTransaction;
30 import org.opendaylight.genius.mdsalutil.MDSALUtil;
31 import org.opendaylight.genius.mdsalutil.MatchInfo;
32 import org.opendaylight.genius.mdsalutil.NwConstants;
33 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
34 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
35 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
36 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
37 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
38 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
39 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInput;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInputBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryOutput;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInput;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryOutput;
51 import org.opendaylight.yangtools.yang.common.RpcResult;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
56 public class EvpnSnatFlowProgrammer {
57 private static final Logger LOG = LoggerFactory.getLogger(EvpnSnatFlowProgrammer.class);
59 private static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
61 private final DataBroker dataBroker;
62 private final ManagedNewTransactionRunner txRunner;
63 private final IMdsalApiManager mdsalManager;
64 private final IBgpManager bgpManager;
65 private final IFibManager fibManager;
66 private final FibRpcService fibService;
67 private final IdManagerService idManager;
70 public EvpnSnatFlowProgrammer(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
71 final IBgpManager bgpManager,
72 final IFibManager fibManager,
73 final FibRpcService fibService,
74 final IdManagerService idManager) {
75 this.dataBroker = dataBroker;
76 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
77 this.mdsalManager = mdsalManager;
78 this.bgpManager = bgpManager;
79 this.fibManager = fibManager;
80 this.fibService = fibService;
81 this.idManager = idManager;
84 public void evpnAdvToBgpAndInstallFibAndTsFlows(final BigInteger dpnId, final short tableId,
85 final String externalIp, final String vpnName, final String rd, final String nextHopIp,
86 final long routerId, final String routerName, final Uuid extNetworkId,
87 TypedWriteTransaction<Configuration> confTx) {
89 * 1) Install the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
90 * (FIP VM on DPN1 is responding back to external fixed IP on DPN2) {DNAT to SNAT traffic on
91 * different Hypervisor}
93 * 2) Install the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
94 * (FIP VM on DPN1 is responding back to external fixed IP on beyond DC-GW VM){DNAT to SNAT Inter DC traffic}
96 * 3) Install the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
97 * (If there is no FIP Match on table 25 (PDNAT_TABLE) then default flow to INBOUND_NAPT_TABLE (table=44))
99 * 4) Install the flow L3_FIB_TABLE (table=21)-> INBOUND_NAPT_TABLE (table=44)
100 * (FIP VM on DPN1 is responding back to external fixed Ip on DPN1 itself. ie. same Hypervisor)
101 * {DNAT to SNAT Intra DC traffic}
103 LOG.info("evpnAdvToBgpAndInstallFibAndTsFlows : Handling SNAT Reverse Traffic for External Fixed IP {} for "
104 + "RouterId {}", externalIp, routerId);
105 // Get the External Gateway MAC Address which is Router gateway MAC address for SNAT
106 String gwMacAddress = NatUtil.getExtGwMacAddFromRouterName(dataBroker, routerName);
107 if (gwMacAddress == null) {
108 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Unable to Retrieve External Gateway MAC address "
109 + "from Router ID {}", routerId);
112 //get l3Vni value for external VPN
113 long l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
114 if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
115 LOG.debug("evpnAdvToBgpAndInstallFibAndTsFlows : L3VNI value is not configured in Internet VPN {}"
116 + " and RD {} Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue with "
117 + "installing SNAT flows for External Fixed IP {}", vpnName, rd, externalIp);
118 l3Vni = NatOverVxlanUtil.getInternetVpnVni(idManager, vpnName, routerId).longValue();
121 long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
122 if (vpnId == NatConstants.INVALID_ID) {
123 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Invalid Vpn Id is found for Vpn Name {}",
127 /* As of now neither SNAT nor DNAT will use mac-address while advertising to FIB and BGP instead
128 * use only gwMacAddress. Hence default value of macAddress is null
131 NatEvpnUtil.addRoutesForVxLanProvType(dataBroker, bgpManager, fibManager, vpnName, rd, externalIp,
132 nextHopIp, l3Vni, null /*InterfaceName*/, gwMacAddress, confTx, RouteOrigin.STATIC,
133 dpnId, extNetworkId);
135 //Install custom FIB routes - FIB table.
136 List<Instruction> customInstructions = new ArrayList<>();
137 customInstructions.add(new InstructionGotoTable(tableId).buildInstruction(0));
138 final String externalFixedIp = NatUtil.validateAndAddNetworkMask(externalIp);
140 CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName)
141 .setSourceDpid(dpnId).setIpAddress(externalFixedIp)
142 .setServiceId(l3Vni).setIpAddressSource(CreateFibEntryInput.IpAddressSource.ExternalFixedIP)
143 .setInstruction(customInstructions).build();
144 LOG.debug("evpnAdvToBgpAndInstallFibAndTsFlows : Installing custom FIB table {} --> table {} flow on "
145 + "NAPT Switch {} with l3Vni {}, ExternalFixedIp {}, ExternalVpnName {} for RouterId {}",
146 NwConstants.L3_FIB_TABLE, tableId, dpnId, l3Vni, externalIp, vpnName, routerId);
148 ListenableFuture<RpcResult<CreateFibEntryOutput>> futureVxlan = fibService.createFibEntry(input);
150 final long finalL3Vni = l3Vni;
151 Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<CreateFibEntryOutput>>() {
153 public void onFailure(@Nonnull Throwable error) {
154 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Error in custom fib routes install process for "
155 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
156 externalIp, dpnId, finalL3Vni, vpnName, routerId, error);
160 public void onSuccess(@Nonnull RpcResult<CreateFibEntryOutput> result) {
161 if (result.isSuccessful()) {
162 LOG.info("evpnAdvToBgpAndInstallFibAndTsFlows : Successfully installed custom FIB routes for "
163 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
164 externalIp, dpnId, finalL3Vni, vpnName, routerId);
166 /* Install the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
167 * (SNAT to DNAT reverse Traffic: If traffic is Initiated from NAPT to FIP VM on different Hypervisor)
169 makeTunnelTableEntry(dpnId, finalL3Vni, customInstructions, tableId, confTx);
170 /* Install the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
171 * (SNAT reverse traffic: If the traffic is Initiated from DC-GW to VM (SNAT Reverse traffic))
173 NatEvpnUtil.makeL3GwMacTableEntry(dpnId, vpnId, gwMacAddress, customInstructions, mdsalManager,
176 /* Install the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
177 * If there is no FIP Match on table 25 (PDNAT_TABLE)
179 NatUtil.makePreDnatToSnatTableEntry(mdsalManager, dpnId, tableId, confTx);
182 }, MoreExecutors.directExecutor());
185 public void evpnDelFibTsAndReverseTraffic(final BigInteger dpnId, final long routerId, final String externalIp,
186 final String vpnName, String extGwMacAddress) {
188 * 1) Remove the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
189 * (FIP VM on DPN1 is responding back to external fixed IP on DPN2) {DNAT to SNAT traffic on
190 * different Hypervisor}
192 * 2) Remove the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
193 * (FIP VM on DPN1 is responding back to external fixed IP on DPN1 itself){DNAT to SNAT traffic on
196 * 3) Remove the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
197 * (If there is no FIP Match on table 25 (PDNAT_TABLE) then default flow to INBOUND_NAPT_TABLE (table=44))
199 * 4) Remove the flow L3_FIB_TABLE (table=21)-> INBOUND_NAPT_TABLE (table=44)
200 * (FIP VM on DPN1 is responding back to external fixed Ip on DPN1 itself. ie. same Hypervisor)
201 * {DNAT to SNAT Intra DC traffic}
203 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
205 LOG.error("evpnDelFibTsAndReverseTraffic : Could not retrieve RD value from VPN Name {}", vpnName);
208 long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
209 if (vpnId == NatConstants.INVALID_ID) {
210 LOG.error("evpnDelFibTsAndReverseTraffic : Invalid Vpn Id is found for Vpn Name {}", vpnName);
213 if (extGwMacAddress == null) {
214 LOG.error("evpnDelFibTsAndReverseTraffic : Unable to Get External Gateway MAC address for "
215 + "External Router ID {} ", routerId);
218 long l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
219 if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
220 LOG.debug("evpnDelFibTsAndReverseTraffic : L3VNI value is not configured in Internet VPN {} and RD {} "
221 + "Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue with installing "
222 + "SNAT flows for External Fixed IP {}", vpnName, rd, externalIp);
223 l3Vni = NatOverVxlanUtil.getInternetVpnVni(idManager, vpnName, routerId).longValue();
226 final String externalFixedIp = NatUtil.validateAndAddNetworkMask(externalIp);
227 RemoveFibEntryInput input = new RemoveFibEntryInputBuilder()
228 .setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalFixedIp)
229 .setIpAddressSource(RemoveFibEntryInput.IpAddressSource.ExternalFixedIP).setServiceId(l3Vni).build();
230 LOG.debug("evpnDelFibTsAndReverseTraffic : Removing custom FIB table {} --> table {} flow on "
231 + "NAPT Switch {} with l3Vni {}, ExternalFixedIp {}, ExternalVpnName {} for RouterId {}",
232 NwConstants.L3_FIB_TABLE, NwConstants.INBOUND_NAPT_TABLE, dpnId, l3Vni, externalIp, vpnName, routerId);
234 ListenableFuture<RpcResult<RemoveFibEntryOutput>> futureVxlan = fibService.removeFibEntry(input);
235 final long finalL3Vni = l3Vni;
236 Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<RemoveFibEntryOutput>>() {
238 public void onFailure(@Nonnull Throwable error) {
239 LOG.error("evpnDelFibTsAndReverseTraffic : Error in custom fib routes remove process for "
240 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
241 externalIp, dpnId, finalL3Vni, vpnName, routerId, error);
245 public void onSuccess(@Nonnull RpcResult<RemoveFibEntryOutput> result) {
246 if (result.isSuccessful()) {
247 ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
249 LOG.info("evpnDelFibTsAndReverseTraffic : Successfully removed custom FIB routes for "
250 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for "
251 + "RouterId {}", externalIp, dpnId, finalL3Vni, vpnName, routerId);
253 //remove INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44) flow
254 removeTunnelTableEntry(dpnId, finalL3Vni, innerConfTx);
255 //remove L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44) flow
256 NatUtil.removePreDnatToSnatTableEntry(innerConfTx, mdsalManager, dpnId);
257 //remove PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44) flow
258 NatEvpnUtil.removeL3GwMacTableEntry(dpnId, vpnId, extGwMacAddress, mdsalManager,
260 }), LOG, "Error removing EVPN SNAT table entries");
263 }, MoreExecutors.directExecutor());
266 public void makeTunnelTableEntry(BigInteger dpnId, long l3Vni, List<Instruction> customInstructions,
267 short tableId, TypedWriteTransaction<Configuration> confTx) {
268 LOG.debug("makeTunnelTableEntry : Create terminating service table {} --> table {} flow on NAPT DpnId {} "
269 + "with l3Vni {} as matching parameter", NwConstants.INTERNAL_TUNNEL_TABLE, tableId, dpnId, l3Vni);
270 List<MatchInfo> mkMatches = new ArrayList<>();
271 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(l3Vni)));
273 Flow terminatingServiceTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
274 NatEvpnUtil.getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, l3Vni, NatConstants.SNAT_FLOW_NAME), 5,
275 String.format("%s:%d", "TST Flow Entry ", l3Vni),
276 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(l3Vni)), mkMatches, customInstructions);
277 mdsalManager.addFlow(confTx, dpnId, terminatingServiceTableFlowEntity);
278 LOG.debug("makeTunnelTableEntry : Successfully installed terminating service table flow {} on DpnId {}",
279 terminatingServiceTableFlowEntity, dpnId);
282 // TODO skitt Fix the exception handling here
283 @SuppressWarnings("checkstyle:IllegalCatch")
284 @SuppressFBWarnings("REC_CATCH_EXCEPTION")
285 public void removeTunnelTableEntry(BigInteger dpnId, long l3Vni, TypedReadWriteTransaction<Configuration> confTx) {
286 LOG.debug("removeTunnelTableEntry : Remove terminating service table {} --> table {} flow on NAPT DpnId {} "
287 + "with l3Vni {} as matching parameter", NwConstants.INTERNAL_TUNNEL_TABLE,
288 NwConstants.INBOUND_NAPT_TABLE, dpnId, l3Vni);
289 List<MatchInfo> mkMatches = new ArrayList<>();
291 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(l3Vni)));
292 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
293 NatEvpnUtil.getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, l3Vni, NatConstants.SNAT_FLOW_NAME),
294 5, String.format("%s:%d", "TST Flow Entry ", l3Vni), 0, 0,
295 COOKIE_TUNNEL.add(BigInteger.valueOf(l3Vni)), mkMatches, null);
297 mdsalManager.removeFlow(confTx, dpnId, flowEntity);
298 } catch (Exception e) {
299 LOG.error("Error removing flow", e);
300 throw new RuntimeException("Error removing flow", e);
302 LOG.debug("removeTunnelTableEntry : Successfully removed terminating service table flow {} on DpnId {}",