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.List;
20 import java.util.concurrent.ExecutionException;
21 import javax.inject.Inject;
22 import javax.inject.Singleton;
23 import org.eclipse.jdt.annotation.NonNull;
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.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 NatOverVxlanUtil natOverVxlanUtil;
69 public EvpnSnatFlowProgrammer(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
70 final IBgpManager bgpManager,
71 final IFibManager fibManager,
72 final FibRpcService fibService,
73 final NatOverVxlanUtil natOverVxlanUtil) {
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.natOverVxlanUtil = natOverVxlanUtil;
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, final Uuid extNetworkId,
86 TypedWriteTransaction<Configuration> confTx) {
88 * 1) Install the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
89 * (FIP VM on DPN1 is responding back to external fixed IP on DPN2) {DNAT to SNAT traffic on
90 * different Hypervisor}
92 * 2) Install the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
93 * (FIP VM on DPN1 is responding back to external fixed IP on beyond DC-GW VM){DNAT to SNAT Inter DC traffic}
95 * 3) Install the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
96 * (If there is no FIP Match on table 25 (PDNAT_TABLE) then default flow to INBOUND_NAPT_TABLE (table=44))
98 * 4) Install the flow L3_FIB_TABLE (table=21)-> INBOUND_NAPT_TABLE (table=44)
99 * (FIP VM on DPN1 is responding back to external fixed Ip on DPN1 itself. ie. same Hypervisor)
100 * {DNAT to SNAT Intra DC traffic}
102 LOG.info("evpnAdvToBgpAndInstallFibAndTsFlows : Handling SNAT Reverse Traffic for External Fixed IP {} for "
103 + "RouterId {}", externalIp, routerId);
104 // Get the External Gateway MAC Address which is Router gateway MAC address for SNAT
105 String gwMacAddress = NatUtil.getExtGwMacAddFromRouterName(dataBroker, routerName);
106 if (gwMacAddress == null) {
107 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Unable to Retrieve External Gateway MAC address "
108 + "from Router ID {}", routerId);
111 //get l3Vni value for external VPN
112 long l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
113 if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
114 LOG.debug("evpnAdvToBgpAndInstallFibAndTsFlows : L3VNI value is not configured in Internet VPN {}"
115 + " and RD {} Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue with "
116 + "installing SNAT flows for External Fixed IP {}", vpnName, rd, externalIp);
117 l3Vni = natOverVxlanUtil.getInternetVpnVni(vpnName, routerId).longValue();
120 long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
121 if (vpnId == NatConstants.INVALID_ID) {
122 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Invalid Vpn Id is found for Vpn Name {}",
126 /* As of now neither SNAT nor DNAT will use mac-address while advertising to FIB and BGP instead
127 * use only gwMacAddress. Hence default value of macAddress is null
130 NatEvpnUtil.addRoutesForVxLanProvType(dataBroker, bgpManager, fibManager, vpnName, rd, externalIp,
131 nextHopIp, l3Vni, null /*InterfaceName*/, gwMacAddress, confTx, RouteOrigin.STATIC,
132 dpnId, extNetworkId);
134 //Install custom FIB routes - FIB table.
135 List<Instruction> customInstructions = new ArrayList<>();
136 customInstructions.add(new InstructionGotoTable(tableId).buildInstruction(0));
137 final String externalFixedIp = NatUtil.validateAndAddNetworkMask(externalIp);
139 CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName)
140 .setSourceDpid(dpnId).setIpAddress(externalFixedIp)
141 .setServiceId(l3Vni).setIpAddressSource(CreateFibEntryInput.IpAddressSource.ExternalFixedIP)
142 .setInstruction(customInstructions).build();
143 LOG.debug("evpnAdvToBgpAndInstallFibAndTsFlows : Installing custom FIB table {} --> table {} flow on "
144 + "NAPT Switch {} with l3Vni {}, ExternalFixedIp {}, ExternalVpnName {} for RouterId {}",
145 NwConstants.L3_FIB_TABLE, tableId, dpnId, l3Vni, externalIp, vpnName, routerId);
147 ListenableFuture<RpcResult<CreateFibEntryOutput>> futureVxlan = fibService.createFibEntry(input);
149 final long finalL3Vni = l3Vni;
150 Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<CreateFibEntryOutput>>() {
152 public void onFailure(@NonNull Throwable error) {
153 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Error in custom fib routes install process for "
154 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
155 externalIp, dpnId, finalL3Vni, vpnName, routerId, error);
159 public void onSuccess(@NonNull RpcResult<CreateFibEntryOutput> result) {
160 if (result.isSuccessful()) {
161 LOG.info("evpnAdvToBgpAndInstallFibAndTsFlows : Successfully installed custom FIB routes for "
162 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
163 externalIp, dpnId, finalL3Vni, vpnName, routerId);
165 /* Install the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
166 * (SNAT to DNAT reverse Traffic: If traffic is Initiated from NAPT to FIP VM on different Hypervisor)
168 makeTunnelTableEntry(dpnId, finalL3Vni, customInstructions, tableId, confTx);
169 /* Install the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
170 * (SNAT reverse traffic: If the traffic is Initiated from DC-GW to VM (SNAT Reverse traffic))
172 NatEvpnUtil.makeL3GwMacTableEntry(dpnId, vpnId, gwMacAddress, customInstructions, mdsalManager,
175 /* Install the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
176 * If there is no FIP Match on table 25 (PDNAT_TABLE)
178 NatUtil.makePreDnatToSnatTableEntry(mdsalManager, dpnId, tableId, confTx);
181 }, MoreExecutors.directExecutor());
184 public void evpnDelFibTsAndReverseTraffic(final BigInteger dpnId, final long routerId, final String externalIp,
185 final String vpnName, String extGwMacAddress) {
187 * 1) Remove the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
188 * (FIP VM on DPN1 is responding back to external fixed IP on DPN2) {DNAT to SNAT traffic on
189 * different Hypervisor}
191 * 2) Remove the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
192 * (FIP VM on DPN1 is responding back to external fixed IP on DPN1 itself){DNAT to SNAT traffic on
195 * 3) Remove the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
196 * (If there is no FIP Match on table 25 (PDNAT_TABLE) then default flow to INBOUND_NAPT_TABLE (table=44))
198 * 4) Remove the flow L3_FIB_TABLE (table=21)-> INBOUND_NAPT_TABLE (table=44)
199 * (FIP VM on DPN1 is responding back to external fixed Ip on DPN1 itself. ie. same Hypervisor)
200 * {DNAT to SNAT Intra DC traffic}
202 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
204 LOG.error("evpnDelFibTsAndReverseTraffic : Could not retrieve RD value from VPN Name {}", vpnName);
207 long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
208 if (vpnId == NatConstants.INVALID_ID) {
209 LOG.error("evpnDelFibTsAndReverseTraffic : Invalid Vpn Id is found for Vpn Name {}", vpnName);
212 if (extGwMacAddress == null) {
213 LOG.error("evpnDelFibTsAndReverseTraffic : Unable to Get External Gateway MAC address for "
214 + "External Router ID {} ", routerId);
217 long l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
218 if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
219 LOG.debug("evpnDelFibTsAndReverseTraffic : L3VNI value is not configured in Internet VPN {} and RD {} "
220 + "Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue with installing "
221 + "SNAT flows for External Fixed IP {}", vpnName, rd, externalIp);
222 l3Vni = natOverVxlanUtil.getInternetVpnVni(vpnName, routerId).longValue();
225 final String externalFixedIp = NatUtil.validateAndAddNetworkMask(externalIp);
226 RemoveFibEntryInput input = new RemoveFibEntryInputBuilder()
227 .setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalFixedIp)
228 .setIpAddressSource(RemoveFibEntryInput.IpAddressSource.ExternalFixedIP).setServiceId(l3Vni).build();
229 LOG.debug("evpnDelFibTsAndReverseTraffic : Removing custom FIB table {} --> table {} flow on "
230 + "NAPT Switch {} with l3Vni {}, ExternalFixedIp {}, ExternalVpnName {} for RouterId {}",
231 NwConstants.L3_FIB_TABLE, NwConstants.INBOUND_NAPT_TABLE, dpnId, l3Vni, externalIp, vpnName, routerId);
233 ListenableFuture<RpcResult<RemoveFibEntryOutput>> futureVxlan = fibService.removeFibEntry(input);
234 final long finalL3Vni = l3Vni;
235 Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<RemoveFibEntryOutput>>() {
237 public void onFailure(@NonNull Throwable error) {
238 LOG.error("evpnDelFibTsAndReverseTraffic : Error in custom fib routes remove process for "
239 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
240 externalIp, dpnId, finalL3Vni, vpnName, routerId, error);
244 public void onSuccess(@NonNull RpcResult<RemoveFibEntryOutput> result) {
245 if (result.isSuccessful()) {
246 ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
248 LOG.info("evpnDelFibTsAndReverseTraffic : Successfully removed custom FIB routes for "
249 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for "
250 + "RouterId {}", externalIp, dpnId, finalL3Vni, vpnName, routerId);
252 //remove INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44) flow
253 removeTunnelTableEntry(dpnId, finalL3Vni, innerConfTx);
254 //remove L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44) flow
255 NatUtil.removePreDnatToSnatTableEntry(innerConfTx, mdsalManager, dpnId);
256 //remove PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44) flow
257 NatEvpnUtil.removeL3GwMacTableEntry(dpnId, vpnId, extGwMacAddress, mdsalManager,
259 }), LOG, "Error removing EVPN SNAT table entries");
262 }, MoreExecutors.directExecutor());
265 public void makeTunnelTableEntry(BigInteger dpnId, long l3Vni, List<Instruction> customInstructions,
266 short tableId, TypedWriteTransaction<Configuration> confTx) {
267 LOG.debug("makeTunnelTableEntry : Create terminating service table {} --> table {} flow on NAPT DpnId {} "
268 + "with l3Vni {} as matching parameter", NwConstants.INTERNAL_TUNNEL_TABLE, tableId, dpnId, l3Vni);
269 List<MatchInfo> mkMatches = new ArrayList<>();
270 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(l3Vni)));
272 Flow terminatingServiceTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
273 NatEvpnUtil.getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, l3Vni, NatConstants.SNAT_FLOW_NAME), 5,
274 String.format("%s:%d", "TST Flow Entry ", l3Vni),
275 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(l3Vni)), mkMatches, customInstructions);
276 mdsalManager.addFlow(confTx, dpnId, terminatingServiceTableFlowEntity);
277 LOG.debug("makeTunnelTableEntry : Successfully installed terminating service table flow {} on DpnId {}",
278 terminatingServiceTableFlowEntity, dpnId);
281 public void removeTunnelTableEntry(BigInteger dpnId, long l3Vni, TypedReadWriteTransaction<Configuration> confTx)
282 throws ExecutionException, InterruptedException {
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);
293 mdsalManager.removeFlow(confTx, dpnId, flowEntity);
294 LOG.debug("removeTunnelTableEntry : Successfully removed terminating service table flow {} on DpnId {}",