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 javax.annotation.Nonnull;
21 import javax.inject.Inject;
22 import javax.inject.Singleton;
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.genius.infra.Datastore.Configuration;
25 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
26 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
27 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
28 import org.opendaylight.genius.infra.TypedWriteTransaction;
29 import org.opendaylight.genius.mdsalutil.MDSALUtil;
30 import org.opendaylight.genius.mdsalutil.MatchInfo;
31 import org.opendaylight.genius.mdsalutil.NwConstants;
32 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
33 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
34 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
35 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
36 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
37 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
38 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInput;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInputBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryOutput;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInput;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryOutput;
49 import org.opendaylight.yangtools.yang.common.RpcResult;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
54 public class EvpnSnatFlowProgrammer {
55 private static final Logger LOG = LoggerFactory.getLogger(EvpnSnatFlowProgrammer.class);
57 private static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
59 private final DataBroker dataBroker;
60 private final ManagedNewTransactionRunner txRunner;
61 private final IMdsalApiManager mdsalManager;
62 private final IBgpManager bgpManager;
63 private final IFibManager fibManager;
64 private final FibRpcService fibService;
65 private final IdManagerService idManager;
68 public EvpnSnatFlowProgrammer(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
69 final IBgpManager bgpManager,
70 final IFibManager fibManager,
71 final FibRpcService fibService,
72 final IdManagerService idManager) {
73 this.dataBroker = dataBroker;
74 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
75 this.mdsalManager = mdsalManager;
76 this.bgpManager = bgpManager;
77 this.fibManager = fibManager;
78 this.fibService = fibService;
79 this.idManager = idManager;
82 public void evpnAdvToBgpAndInstallFibAndTsFlows(final BigInteger dpnId, final short tableId,
83 final String externalIp, final String vpnName, final String rd, final String nextHopIp,
84 final long routerId, final String routerName, TypedWriteTransaction<Configuration> confTx) {
86 * 1) Install the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
87 * (FIP VM on DPN1 is responding back to external fixed IP on DPN2) {DNAT to SNAT traffic on
88 * different Hypervisor}
90 * 2) Install the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
91 * (FIP VM on DPN1 is responding back to external fixed IP on beyond DC-GW VM){DNAT to SNAT Inter DC traffic}
93 * 3) Install the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
94 * (If there is no FIP Match on table 25 (PDNAT_TABLE) then default flow to INBOUND_NAPT_TABLE (table=44))
96 * 4) Install the flow L3_FIB_TABLE (table=21)-> INBOUND_NAPT_TABLE (table=44)
97 * (FIP VM on DPN1 is responding back to external fixed Ip on DPN1 itself. ie. same Hypervisor)
98 * {DNAT to SNAT Intra DC traffic}
100 LOG.info("evpnAdvToBgpAndInstallFibAndTsFlows : Handling SNAT Reverse Traffic for External Fixed IP {} for "
101 + "RouterId {}", externalIp, routerId);
102 // Get the External Gateway MAC Address which is Router gateway MAC address for SNAT
103 String gwMacAddress = NatUtil.getExtGwMacAddFromRouterName(dataBroker, routerName);
104 if (gwMacAddress == null) {
105 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Unable to Retrieve External Gateway MAC address "
106 + "from Router ID {}", routerId);
109 //get l3Vni value for external VPN
110 long l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
111 if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
112 LOG.debug("evpnAdvToBgpAndInstallFibAndTsFlows : L3VNI value is not configured in Internet VPN {}"
113 + " and RD {} Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue with "
114 + "installing SNAT flows for External Fixed IP {}", vpnName, rd, externalIp);
115 l3Vni = NatOverVxlanUtil.getInternetVpnVni(idManager, vpnName, routerId).longValue();
118 long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
119 if (vpnId == NatConstants.INVALID_ID) {
120 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Invalid Vpn Id is found for Vpn Name {}",
124 /* As of now neither SNAT nor DNAT will use mac-address while advertising to FIB and BGP instead
125 * use only gwMacAddress. Hence default value of macAddress is null
128 NatEvpnUtil.addRoutesForVxLanProvType(dataBroker, bgpManager, fibManager, vpnName, rd, externalIp,
129 nextHopIp, l3Vni, null /*InterfaceName*/, gwMacAddress, confTx, RouteOrigin.STATIC, dpnId);
131 //Install custom FIB routes - FIB table.
132 List<Instruction> customInstructions = new ArrayList<>();
133 customInstructions.add(new InstructionGotoTable(tableId).buildInstruction(0));
134 final String externalFixedIp = NatUtil.validateAndAddNetworkMask(externalIp);
136 CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName)
137 .setSourceDpid(dpnId).setIpAddress(externalFixedIp)
138 .setServiceId(l3Vni).setIpAddressSource(CreateFibEntryInput.IpAddressSource.ExternalFixedIP)
139 .setInstruction(customInstructions).build();
140 LOG.debug("evpnAdvToBgpAndInstallFibAndTsFlows : Installing custom FIB table {} --> table {} flow on "
141 + "NAPT Switch {} with l3Vni {}, ExternalFixedIp {}, ExternalVpnName {} for RouterId {}",
142 NwConstants.L3_FIB_TABLE, tableId, dpnId, l3Vni, externalIp, vpnName, routerId);
144 ListenableFuture<RpcResult<CreateFibEntryOutput>> futureVxlan = fibService.createFibEntry(input);
146 final long finalL3Vni = l3Vni;
147 Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<CreateFibEntryOutput>>() {
149 public void onFailure(@Nonnull Throwable error) {
150 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Error in custom fib routes install process for "
151 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
152 externalIp, dpnId, finalL3Vni, vpnName, routerId, error);
156 public void onSuccess(@Nonnull RpcResult<CreateFibEntryOutput> result) {
157 if (result.isSuccessful()) {
158 LOG.info("evpnAdvToBgpAndInstallFibAndTsFlows : Successfully installed custom FIB routes for "
159 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
160 externalIp, dpnId, finalL3Vni, vpnName, routerId);
162 /* Install the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
163 * (SNAT to DNAT reverse Traffic: If traffic is Initiated from NAPT to FIP VM on different Hypervisor)
165 makeTunnelTableEntry(dpnId, finalL3Vni, customInstructions, tableId, confTx);
166 /* Install the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
167 * (SNAT reverse traffic: If the traffic is Initiated from DC-GW to VM (SNAT Reverse traffic))
169 NatEvpnUtil.makeL3GwMacTableEntry(dpnId, vpnId, gwMacAddress, customInstructions, mdsalManager,
172 /* Install the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
173 * If there is no FIP Match on table 25 (PDNAT_TABLE)
175 NatUtil.makePreDnatToSnatTableEntry(mdsalManager, dpnId, tableId, confTx);
178 }, MoreExecutors.directExecutor());
181 public void evpnDelFibTsAndReverseTraffic(final BigInteger dpnId, final long routerId, final String externalIp,
182 final String vpnName, String extGwMacAddress) {
184 * 1) Remove the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
185 * (FIP VM on DPN1 is responding back to external fixed IP on DPN2) {DNAT to SNAT traffic on
186 * different Hypervisor}
188 * 2) Remove the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
189 * (FIP VM on DPN1 is responding back to external fixed IP on DPN1 itself){DNAT to SNAT traffic on
192 * 3) Remove the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
193 * (If there is no FIP Match on table 25 (PDNAT_TABLE) then default flow to INBOUND_NAPT_TABLE (table=44))
195 * 4) Remove the flow L3_FIB_TABLE (table=21)-> INBOUND_NAPT_TABLE (table=44)
196 * (FIP VM on DPN1 is responding back to external fixed Ip on DPN1 itself. ie. same Hypervisor)
197 * {DNAT to SNAT Intra DC traffic}
199 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
201 LOG.error("evpnDelFibTsAndReverseTraffic : Could not retrieve RD value from VPN Name {}", vpnName);
204 long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
205 if (vpnId == NatConstants.INVALID_ID) {
206 LOG.error("evpnDelFibTsAndReverseTraffic : Invalid Vpn Id is found for Vpn Name {}", vpnName);
209 if (extGwMacAddress == null) {
210 LOG.error("evpnDelFibTsAndReverseTraffic : Unable to Get External Gateway MAC address for "
211 + "External Router ID {} ", routerId);
214 long l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
215 if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
216 LOG.debug("evpnDelFibTsAndReverseTraffic : L3VNI value is not configured in Internet VPN {} and RD {} "
217 + "Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue with installing "
218 + "SNAT flows for External Fixed IP {}", vpnName, rd, externalIp);
219 l3Vni = NatOverVxlanUtil.getInternetVpnVni(idManager, vpnName, routerId).longValue();
222 final String externalFixedIp = NatUtil.validateAndAddNetworkMask(externalIp);
223 RemoveFibEntryInput input = new RemoveFibEntryInputBuilder()
224 .setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalFixedIp)
225 .setIpAddressSource(RemoveFibEntryInput.IpAddressSource.ExternalFixedIP).setServiceId(l3Vni).build();
226 LOG.debug("evpnDelFibTsAndReverseTraffic : Removing custom FIB table {} --> table {} flow on "
227 + "NAPT Switch {} with l3Vni {}, ExternalFixedIp {}, ExternalVpnName {} for RouterId {}",
228 NwConstants.L3_FIB_TABLE, NwConstants.INBOUND_NAPT_TABLE, dpnId, l3Vni, externalIp, vpnName, routerId);
230 ListenableFuture<RpcResult<RemoveFibEntryOutput>> futureVxlan = fibService.removeFibEntry(input);
231 final long finalL3Vni = l3Vni;
232 Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<RemoveFibEntryOutput>>() {
234 public void onFailure(@Nonnull Throwable error) {
235 LOG.error("evpnDelFibTsAndReverseTraffic : Error in custom fib routes remove process for "
236 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
237 externalIp, dpnId, finalL3Vni, vpnName, routerId, error);
241 public void onSuccess(@Nonnull RpcResult<RemoveFibEntryOutput> result) {
242 if (result.isSuccessful()) {
243 ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
245 LOG.info("evpnDelFibTsAndReverseTraffic : Successfully removed custom FIB routes for "
246 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for "
247 + "RouterId {}", externalIp, dpnId, finalL3Vni, vpnName, routerId);
249 //remove INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44) flow
250 removeTunnelTableEntry(dpnId, finalL3Vni, innerConfTx);
251 //remove L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44) flow
252 NatUtil.removePreDnatToSnatTableEntry(innerConfTx, mdsalManager, dpnId);
253 //remove PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44) flow
254 NatEvpnUtil.removeL3GwMacTableEntry(dpnId, vpnId, extGwMacAddress, mdsalManager,
256 }), LOG, "Error removing EVPN SNAT table entries");
259 }, MoreExecutors.directExecutor());
262 public void makeTunnelTableEntry(BigInteger dpnId, long l3Vni, List<Instruction> customInstructions,
263 short tableId, TypedWriteTransaction<Configuration> confTx) {
264 LOG.debug("makeTunnelTableEntry : Create terminating service table {} --> table {} flow on NAPT DpnId {} "
265 + "with l3Vni {} as matching parameter", NwConstants.INTERNAL_TUNNEL_TABLE, tableId, dpnId, l3Vni);
266 List<MatchInfo> mkMatches = new ArrayList<>();
267 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(l3Vni)));
269 Flow terminatingServiceTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
270 NatEvpnUtil.getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, l3Vni, NatConstants.SNAT_FLOW_NAME), 5,
271 String.format("%s:%d", "TST Flow Entry ", l3Vni),
272 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(l3Vni)), mkMatches, customInstructions);
273 mdsalManager.addFlow(confTx, dpnId, terminatingServiceTableFlowEntity);
274 LOG.debug("makeTunnelTableEntry : Successfully installed terminating service table flow {} on DpnId {}",
275 terminatingServiceTableFlowEntity, dpnId);
278 public void removeTunnelTableEntry(BigInteger dpnId, long l3Vni, TypedReadWriteTransaction<Configuration> confTx) {
279 LOG.debug("removeTunnelTableEntry : Remove terminating service table {} --> table {} flow on NAPT DpnId {} "
280 + "with l3Vni {} as matching parameter", NwConstants.INTERNAL_TUNNEL_TABLE,
281 NwConstants.INBOUND_NAPT_TABLE, dpnId, l3Vni);
282 List<MatchInfo> mkMatches = new ArrayList<>();
284 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(l3Vni)));
285 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
286 NatEvpnUtil.getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, l3Vni, NatConstants.SNAT_FLOW_NAME),
287 5, String.format("%s:%d", "TST Flow Entry ", l3Vni), 0, 0,
288 COOKIE_TUNNEL.add(BigInteger.valueOf(l3Vni)), mkMatches, null);
289 mdsalManager.removeFlow(confTx, dpnId, flowEntity);
290 LOG.debug("removeTunnelTableEntry : Successfully removed terminating service table flow {} on DpnId {}",