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.opendaylight.flow.inventory.rev130819.tables.table.Flow;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInput;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInputBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryOutput;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInput;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryOutput;
50 import org.opendaylight.yangtools.yang.common.RpcResult;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
55 public class EvpnSnatFlowProgrammer {
56 private static final Logger LOG = LoggerFactory.getLogger(EvpnSnatFlowProgrammer.class);
58 private static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
60 private final DataBroker dataBroker;
61 private final ManagedNewTransactionRunner txRunner;
62 private final IMdsalApiManager mdsalManager;
63 private final IBgpManager bgpManager;
64 private final IFibManager fibManager;
65 private final FibRpcService fibService;
66 private final IdManagerService idManager;
69 public EvpnSnatFlowProgrammer(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
70 final IBgpManager bgpManager,
71 final IFibManager fibManager,
72 final FibRpcService fibService,
73 final IdManagerService idManager) {
74 this.dataBroker = dataBroker;
75 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
76 this.mdsalManager = mdsalManager;
77 this.bgpManager = bgpManager;
78 this.fibManager = fibManager;
79 this.fibService = fibService;
80 this.idManager = idManager;
83 public void evpnAdvToBgpAndInstallFibAndTsFlows(final BigInteger dpnId, final short tableId,
84 final String externalIp, final String vpnName, final String rd, final String nextHopIp,
85 final long routerId, final String routerName, TypedWriteTransaction<Configuration> confTx) {
87 * 1) Install the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
88 * (FIP VM on DPN1 is responding back to external fixed IP on DPN2) {DNAT to SNAT traffic on
89 * different Hypervisor}
91 * 2) Install the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
92 * (FIP VM on DPN1 is responding back to external fixed IP on beyond DC-GW VM){DNAT to SNAT Inter DC traffic}
94 * 3) Install the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
95 * (If there is no FIP Match on table 25 (PDNAT_TABLE) then default flow to INBOUND_NAPT_TABLE (table=44))
97 * 4) Install the flow L3_FIB_TABLE (table=21)-> INBOUND_NAPT_TABLE (table=44)
98 * (FIP VM on DPN1 is responding back to external fixed Ip on DPN1 itself. ie. same Hypervisor)
99 * {DNAT to SNAT Intra DC traffic}
101 LOG.info("evpnAdvToBgpAndInstallFibAndTsFlows : Handling SNAT Reverse Traffic for External Fixed IP {} for "
102 + "RouterId {}", externalIp, routerId);
103 // Get the External Gateway MAC Address which is Router gateway MAC address for SNAT
104 String gwMacAddress = NatUtil.getExtGwMacAddFromRouterName(dataBroker, routerName);
105 if (gwMacAddress == null) {
106 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Unable to Retrieve External Gateway MAC address "
107 + "from Router ID {}", routerId);
110 //get l3Vni value for external VPN
111 long l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
112 if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
113 LOG.debug("evpnAdvToBgpAndInstallFibAndTsFlows : L3VNI value is not configured in Internet VPN {}"
114 + " and RD {} Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue with "
115 + "installing SNAT flows for External Fixed IP {}", vpnName, rd, externalIp);
116 l3Vni = NatOverVxlanUtil.getInternetVpnVni(idManager, vpnName, routerId).longValue();
119 long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
120 if (vpnId == NatConstants.INVALID_ID) {
121 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Invalid Vpn Id is found for Vpn Name {}",
125 /* As of now neither SNAT nor DNAT will use mac-address while advertising to FIB and BGP instead
126 * use only gwMacAddress. Hence default value of macAddress is null
129 NatEvpnUtil.addRoutesForVxLanProvType(dataBroker, bgpManager, fibManager, vpnName, rd, externalIp,
130 nextHopIp, l3Vni, null /*InterfaceName*/, gwMacAddress, confTx, RouteOrigin.STATIC, dpnId);
132 //Install custom FIB routes - FIB table.
133 List<Instruction> customInstructions = new ArrayList<>();
134 customInstructions.add(new InstructionGotoTable(tableId).buildInstruction(0));
135 final String externalFixedIp = NatUtil.validateAndAddNetworkMask(externalIp);
137 CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName)
138 .setSourceDpid(dpnId).setIpAddress(externalFixedIp)
139 .setServiceId(l3Vni).setIpAddressSource(CreateFibEntryInput.IpAddressSource.ExternalFixedIP)
140 .setInstruction(customInstructions).build();
141 LOG.debug("evpnAdvToBgpAndInstallFibAndTsFlows : Installing custom FIB table {} --> table {} flow on "
142 + "NAPT Switch {} with l3Vni {}, ExternalFixedIp {}, ExternalVpnName {} for RouterId {}",
143 NwConstants.L3_FIB_TABLE, tableId, dpnId, l3Vni, externalIp, vpnName, routerId);
145 ListenableFuture<RpcResult<CreateFibEntryOutput>> futureVxlan = fibService.createFibEntry(input);
147 final long finalL3Vni = l3Vni;
148 Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<CreateFibEntryOutput>>() {
150 public void onFailure(@Nonnull Throwable error) {
151 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Error in custom fib routes install process for "
152 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
153 externalIp, dpnId, finalL3Vni, vpnName, routerId, error);
157 public void onSuccess(@Nonnull RpcResult<CreateFibEntryOutput> result) {
158 if (result.isSuccessful()) {
159 LOG.info("evpnAdvToBgpAndInstallFibAndTsFlows : Successfully installed custom FIB routes for "
160 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
161 externalIp, dpnId, finalL3Vni, vpnName, routerId);
163 /* Install the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
164 * (SNAT to DNAT reverse Traffic: If traffic is Initiated from NAPT to FIP VM on different Hypervisor)
166 makeTunnelTableEntry(dpnId, finalL3Vni, customInstructions, tableId, confTx);
167 /* Install the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
168 * (SNAT reverse traffic: If the traffic is Initiated from DC-GW to VM (SNAT Reverse traffic))
170 NatEvpnUtil.makeL3GwMacTableEntry(dpnId, vpnId, gwMacAddress, customInstructions, mdsalManager,
173 /* Install the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
174 * If there is no FIP Match on table 25 (PDNAT_TABLE)
176 NatUtil.makePreDnatToSnatTableEntry(mdsalManager, dpnId, tableId, confTx);
179 }, MoreExecutors.directExecutor());
182 public void evpnDelFibTsAndReverseTraffic(final BigInteger dpnId, final long routerId, final String externalIp,
183 final String vpnName, String extGwMacAddress) {
185 * 1) Remove the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
186 * (FIP VM on DPN1 is responding back to external fixed IP on DPN2) {DNAT to SNAT traffic on
187 * different Hypervisor}
189 * 2) Remove the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
190 * (FIP VM on DPN1 is responding back to external fixed IP on DPN1 itself){DNAT to SNAT traffic on
193 * 3) Remove the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
194 * (If there is no FIP Match on table 25 (PDNAT_TABLE) then default flow to INBOUND_NAPT_TABLE (table=44))
196 * 4) Remove the flow L3_FIB_TABLE (table=21)-> INBOUND_NAPT_TABLE (table=44)
197 * (FIP VM on DPN1 is responding back to external fixed Ip on DPN1 itself. ie. same Hypervisor)
198 * {DNAT to SNAT Intra DC traffic}
200 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
202 LOG.error("evpnDelFibTsAndReverseTraffic : Could not retrieve RD value from VPN Name {}", vpnName);
205 long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
206 if (vpnId == NatConstants.INVALID_ID) {
207 LOG.error("evpnDelFibTsAndReverseTraffic : Invalid Vpn Id is found for Vpn Name {}", vpnName);
210 if (extGwMacAddress == null) {
211 LOG.error("evpnDelFibTsAndReverseTraffic : Unable to Get External Gateway MAC address for "
212 + "External Router ID {} ", routerId);
215 long l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
216 if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
217 LOG.debug("evpnDelFibTsAndReverseTraffic : L3VNI value is not configured in Internet VPN {} and RD {} "
218 + "Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue with installing "
219 + "SNAT flows for External Fixed IP {}", vpnName, rd, externalIp);
220 l3Vni = NatOverVxlanUtil.getInternetVpnVni(idManager, vpnName, routerId).longValue();
223 final String externalFixedIp = NatUtil.validateAndAddNetworkMask(externalIp);
224 RemoveFibEntryInput input = new RemoveFibEntryInputBuilder()
225 .setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalFixedIp)
226 .setIpAddressSource(RemoveFibEntryInput.IpAddressSource.ExternalFixedIP).setServiceId(l3Vni).build();
227 LOG.debug("evpnDelFibTsAndReverseTraffic : Removing custom FIB table {} --> table {} flow on "
228 + "NAPT Switch {} with l3Vni {}, ExternalFixedIp {}, ExternalVpnName {} for RouterId {}",
229 NwConstants.L3_FIB_TABLE, NwConstants.INBOUND_NAPT_TABLE, dpnId, l3Vni, externalIp, vpnName, routerId);
231 ListenableFuture<RpcResult<RemoveFibEntryOutput>> futureVxlan = fibService.removeFibEntry(input);
232 final long finalL3Vni = l3Vni;
233 Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<RemoveFibEntryOutput>>() {
235 public void onFailure(@Nonnull Throwable error) {
236 LOG.error("evpnDelFibTsAndReverseTraffic : Error in custom fib routes remove process for "
237 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
238 externalIp, dpnId, finalL3Vni, vpnName, routerId, error);
242 public void onSuccess(@Nonnull RpcResult<RemoveFibEntryOutput> result) {
243 if (result.isSuccessful()) {
244 ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
246 LOG.info("evpnDelFibTsAndReverseTraffic : Successfully removed custom FIB routes for "
247 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for "
248 + "RouterId {}", externalIp, dpnId, finalL3Vni, vpnName, routerId);
250 //remove INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44) flow
251 removeTunnelTableEntry(dpnId, finalL3Vni, innerConfTx);
252 //remove L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44) flow
253 NatUtil.removePreDnatToSnatTableEntry(innerConfTx, mdsalManager, dpnId);
254 //remove PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44) flow
255 NatEvpnUtil.removeL3GwMacTableEntry(dpnId, vpnId, extGwMacAddress, mdsalManager,
257 }), LOG, "Error removing EVPN SNAT table entries");
260 }, MoreExecutors.directExecutor());
263 public void makeTunnelTableEntry(BigInteger dpnId, long l3Vni, List<Instruction> customInstructions,
264 short tableId, TypedWriteTransaction<Configuration> confTx) {
265 LOG.debug("makeTunnelTableEntry : Create terminating service table {} --> table {} flow on NAPT DpnId {} "
266 + "with l3Vni {} as matching parameter", NwConstants.INTERNAL_TUNNEL_TABLE, tableId, dpnId, l3Vni);
267 List<MatchInfo> mkMatches = new ArrayList<>();
268 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(l3Vni)));
270 Flow terminatingServiceTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
271 NatEvpnUtil.getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, l3Vni, NatConstants.SNAT_FLOW_NAME), 5,
272 String.format("%s:%d", "TST Flow Entry ", l3Vni),
273 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(l3Vni)), mkMatches, customInstructions);
274 mdsalManager.addFlow(confTx, dpnId, terminatingServiceTableFlowEntity);
275 LOG.debug("makeTunnelTableEntry : Successfully installed terminating service table flow {} on DpnId {}",
276 terminatingServiceTableFlowEntity, dpnId);
279 // TODO skitt Fix the exception handling here
280 @SuppressWarnings("checkstyle:IllegalCatch")
281 @SuppressFBWarnings("REC_CATCH_EXCEPTION")
282 public void removeTunnelTableEntry(BigInteger dpnId, long l3Vni, TypedReadWriteTransaction<Configuration> confTx) {
283 LOG.debug("removeTunnelTableEntry : Remove terminating service table {} --> table {} flow on NAPT DpnId {} "
284 + "with l3Vni {} as matching parameter", NwConstants.INTERNAL_TUNNEL_TABLE,
285 NwConstants.INBOUND_NAPT_TABLE, dpnId, l3Vni);
286 List<MatchInfo> mkMatches = new ArrayList<>();
288 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(l3Vni)));
289 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
290 NatEvpnUtil.getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, l3Vni, NatConstants.SNAT_FLOW_NAME),
291 5, String.format("%s:%d", "TST Flow Entry ", l3Vni), 0, 0,
292 COOKIE_TUNNEL.add(BigInteger.valueOf(l3Vni)), mkMatches, null);
294 mdsalManager.removeFlow(confTx, dpnId, flowEntity);
295 } catch (Exception e) {
296 LOG.error("Error removing flow", e);
297 throw new RuntimeException("Error removing flow", e);
299 LOG.debug("removeTunnelTableEntry : Successfully removed terminating service table flow {} on DpnId {}",