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.opendaylight.yangtools.yang.common.Uint32;
52 import org.opendaylight.yangtools.yang.common.Uint64;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
57 public class EvpnSnatFlowProgrammer {
58 private static final Logger LOG = LoggerFactory.getLogger(EvpnSnatFlowProgrammer.class);
60 private static final Uint64 COOKIE_TUNNEL = Uint64.valueOf("9000000", 16).intern();
62 private final DataBroker dataBroker;
63 private final ManagedNewTransactionRunner txRunner;
64 private final IMdsalApiManager mdsalManager;
65 private final IBgpManager bgpManager;
66 private final IFibManager fibManager;
67 private final FibRpcService fibService;
68 private final NatOverVxlanUtil natOverVxlanUtil;
71 public EvpnSnatFlowProgrammer(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
72 final IBgpManager bgpManager,
73 final IFibManager fibManager,
74 final FibRpcService fibService,
75 final NatOverVxlanUtil natOverVxlanUtil) {
76 this.dataBroker = dataBroker;
77 this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
78 this.mdsalManager = mdsalManager;
79 this.bgpManager = bgpManager;
80 this.fibManager = fibManager;
81 this.fibService = fibService;
82 this.natOverVxlanUtil = natOverVxlanUtil;
85 public void evpnAdvToBgpAndInstallFibAndTsFlows(final Uint64 dpnId, final short tableId,
86 final String externalIp, final String vpnName, final String rd, final String nextHopIp,
87 final Uint32 routerId, final String routerName, final Uuid extNetworkId,
88 TypedWriteTransaction<Configuration> confTx) {
90 * 1) Install the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
91 * (FIP VM on DPN1 is responding back to external fixed IP on DPN2) {DNAT to SNAT traffic on
92 * different Hypervisor}
94 * 2) Install the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
95 * (FIP VM on DPN1 is responding back to external fixed IP on beyond DC-GW VM){DNAT to SNAT Inter DC traffic}
97 * 3) Install the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
98 * (If there is no FIP Match on table 25 (PDNAT_TABLE) then default flow to INBOUND_NAPT_TABLE (table=44))
100 * 4) Install the flow L3_FIB_TABLE (table=21)-> INBOUND_NAPT_TABLE (table=44)
101 * (FIP VM on DPN1 is responding back to external fixed Ip on DPN1 itself. ie. same Hypervisor)
102 * {DNAT to SNAT Intra DC traffic}
104 LOG.info("evpnAdvToBgpAndInstallFibAndTsFlows : Handling SNAT Reverse Traffic for External Fixed IP {} for "
105 + "RouterId {}", externalIp, routerId);
106 // Get the External Gateway MAC Address which is Router gateway MAC address for SNAT
107 String gwMacAddress = NatUtil.getExtGwMacAddFromRouterName(dataBroker, routerName);
108 if (gwMacAddress == null) {
109 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Unable to Retrieve External Gateway MAC address "
110 + "from Router ID {}", routerId);
113 //get l3Vni value for external VPN
114 Uint32 l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
115 if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
116 LOG.debug("evpnAdvToBgpAndInstallFibAndTsFlows : L3VNI value is not configured in Internet VPN {}"
117 + " and RD {} Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue with "
118 + "installing SNAT flows for External Fixed IP {}", vpnName, rd, externalIp);
119 l3Vni = natOverVxlanUtil.getInternetVpnVni(vpnName, routerId);
122 Uint32 vpnId = NatUtil.getVpnId(dataBroker, vpnName);
123 if (vpnId == NatConstants.INVALID_ID) {
124 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Invalid Vpn Id is found for Vpn Name {}",
128 /* As of now neither SNAT nor DNAT will use mac-address while advertising to FIB and BGP instead
129 * use only gwMacAddress. Hence default value of macAddress is null
132 NatEvpnUtil.addRoutesForVxLanProvType(dataBroker, bgpManager, fibManager, vpnName, rd, externalIp,
133 nextHopIp, l3Vni, null /*InterfaceName*/, gwMacAddress, confTx, RouteOrigin.STATIC,
134 dpnId, extNetworkId);
136 //Install custom FIB routes - FIB table.
137 List<Instruction> customInstructions = new ArrayList<>();
138 customInstructions.add(new InstructionGotoTable(tableId).buildInstruction(0));
139 final String externalFixedIp = NatUtil.validateAndAddNetworkMask(externalIp);
141 CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName)
142 .setSourceDpid(dpnId).setIpAddress(externalFixedIp)
143 .setServiceId(l3Vni).setIpAddressSource(CreateFibEntryInput.IpAddressSource.ExternalFixedIP)
144 .setInstruction(customInstructions).build();
145 LOG.debug("evpnAdvToBgpAndInstallFibAndTsFlows : Installing custom FIB table {} --> table {} flow on "
146 + "NAPT Switch {} with l3Vni {}, ExternalFixedIp {}, ExternalVpnName {} for RouterId {}",
147 NwConstants.L3_FIB_TABLE, tableId, dpnId, l3Vni, externalIp, vpnName, routerId);
149 ListenableFuture<RpcResult<CreateFibEntryOutput>> futureVxlan = fibService.createFibEntry(input);
151 final Uint32 finalL3Vni = l3Vni;
152 Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<CreateFibEntryOutput>>() {
154 public void onFailure(@NonNull Throwable error) {
155 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Error in custom fib routes install process for "
156 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
157 externalIp, dpnId, finalL3Vni, vpnName, routerId, error);
161 public void onSuccess(@NonNull RpcResult<CreateFibEntryOutput> result) {
162 if (result.isSuccessful()) {
163 LOG.info("evpnAdvToBgpAndInstallFibAndTsFlows : Successfully installed custom FIB routes for "
164 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
165 externalIp, dpnId, finalL3Vni, vpnName, routerId);
167 /* Install the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
168 * (SNAT to DNAT reverse Traffic: If traffic is Initiated from NAPT to FIP VM on different Hypervisor)
170 makeTunnelTableEntry(dpnId, finalL3Vni, customInstructions, tableId, confTx);
171 /* Install the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
172 * (SNAT reverse traffic: If the traffic is Initiated from DC-GW to VM (SNAT Reverse traffic))
174 NatEvpnUtil.makeL3GwMacTableEntry(dpnId, vpnId, gwMacAddress, customInstructions, mdsalManager,
177 /* Install the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
178 * If there is no FIP Match on table 25 (PDNAT_TABLE)
180 NatUtil.makePreDnatToSnatTableEntry(mdsalManager, dpnId, tableId, confTx);
183 }, MoreExecutors.directExecutor());
186 public void evpnDelFibTsAndReverseTraffic(final Uint64 dpnId, final Uint32 routerId, final String externalIp,
187 final String vpnName, String extGwMacAddress) {
189 * 1) Remove the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
190 * (FIP VM on DPN1 is responding back to external fixed IP on DPN2) {DNAT to SNAT traffic on
191 * different Hypervisor}
193 * 2) Remove the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
194 * (FIP VM on DPN1 is responding back to external fixed IP on DPN1 itself){DNAT to SNAT traffic on
197 * 3) Remove the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
198 * (If there is no FIP Match on table 25 (PDNAT_TABLE) then default flow to INBOUND_NAPT_TABLE (table=44))
200 * 4) Remove the flow L3_FIB_TABLE (table=21)-> INBOUND_NAPT_TABLE (table=44)
201 * (FIP VM on DPN1 is responding back to external fixed Ip on DPN1 itself. ie. same Hypervisor)
202 * {DNAT to SNAT Intra DC traffic}
204 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
206 LOG.error("evpnDelFibTsAndReverseTraffic : Could not retrieve RD value from VPN Name {}", vpnName);
209 Uint32 vpnId = NatUtil.getVpnId(dataBroker, vpnName);
210 if (vpnId == NatConstants.INVALID_ID) {
211 LOG.error("evpnDelFibTsAndReverseTraffic : Invalid Vpn Id is found for Vpn Name {}", vpnName);
214 if (extGwMacAddress == null) {
215 LOG.error("evpnDelFibTsAndReverseTraffic : Unable to Get External Gateway MAC address for "
216 + "External Router ID {} ", routerId);
219 Uint32 l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
220 if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
221 LOG.debug("evpnDelFibTsAndReverseTraffic : L3VNI value is not configured in Internet VPN {} and RD {} "
222 + "Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue with installing "
223 + "SNAT flows for External Fixed IP {}", vpnName, rd, externalIp);
224 l3Vni = natOverVxlanUtil.getInternetVpnVni(vpnName, routerId);
227 final String externalFixedIp = NatUtil.validateAndAddNetworkMask(externalIp);
228 RemoveFibEntryInput input = new RemoveFibEntryInputBuilder()
229 .setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalFixedIp)
230 .setIpAddressSource(RemoveFibEntryInput.IpAddressSource.ExternalFixedIP).setServiceId(l3Vni).build();
231 LOG.debug("evpnDelFibTsAndReverseTraffic : Removing custom FIB table {} --> table {} flow on "
232 + "NAPT Switch {} with l3Vni {}, ExternalFixedIp {}, ExternalVpnName {} for RouterId {}",
233 NwConstants.L3_FIB_TABLE, NwConstants.INBOUND_NAPT_TABLE, dpnId, l3Vni, externalIp, vpnName, routerId);
235 ListenableFuture<RpcResult<RemoveFibEntryOutput>> futureVxlan = fibService.removeFibEntry(input);
236 final Uint32 finalL3Vni = l3Vni;
237 Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<RemoveFibEntryOutput>>() {
239 public void onFailure(@NonNull Throwable error) {
240 LOG.error("evpnDelFibTsAndReverseTraffic : Error in custom fib routes remove process for "
241 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
242 externalIp, dpnId, finalL3Vni, vpnName, routerId, error);
246 public void onSuccess(@NonNull RpcResult<RemoveFibEntryOutput> result) {
247 if (result.isSuccessful()) {
248 ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
250 LOG.info("evpnDelFibTsAndReverseTraffic : Successfully removed custom FIB routes for "
251 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for "
252 + "RouterId {}", externalIp, dpnId, finalL3Vni, vpnName, routerId);
254 //remove INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44) flow
255 removeTunnelTableEntry(dpnId, finalL3Vni, innerConfTx);
256 //remove L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44) flow
257 NatUtil.removePreDnatToSnatTableEntry(innerConfTx, mdsalManager, dpnId);
258 //remove PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44) flow
259 NatEvpnUtil.removeL3GwMacTableEntry(dpnId, vpnId, extGwMacAddress, mdsalManager,
261 }), LOG, "Error removing EVPN SNAT table entries");
264 }, MoreExecutors.directExecutor());
267 public void makeTunnelTableEntry(Uint64 dpnId, Uint32 l3Vni, List<Instruction> customInstructions,
268 short tableId, TypedWriteTransaction<Configuration> confTx) {
269 LOG.debug("makeTunnelTableEntry : Create terminating service table {} --> table {} flow on NAPT DpnId {} "
270 + "with l3Vni {} as matching parameter", NwConstants.INTERNAL_TUNNEL_TABLE, tableId, dpnId, l3Vni);
271 List<MatchInfo> mkMatches = new ArrayList<>();
272 mkMatches.add(new MatchTunnelId(Uint64.valueOf(l3Vni)));
274 Flow terminatingServiceTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
275 NatEvpnUtil.getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, l3Vni, NatConstants.SNAT_FLOW_NAME),
276 NatConstants.DEFAULT_VPN_INTERNAL_TUNNEL_TABLE_PRIORITY,
277 String.format("%s:%s", "TST Flow Entry ", l3Vni),
278 0, 0, Uint64.valueOf(COOKIE_TUNNEL.toJava().add(BigInteger.valueOf(l3Vni.longValue()))),
279 mkMatches, customInstructions);
280 mdsalManager.addFlow(confTx, dpnId, terminatingServiceTableFlowEntity);
281 LOG.debug("makeTunnelTableEntry : Successfully installed terminating service table flow {} on DpnId {}",
282 terminatingServiceTableFlowEntity, dpnId);
285 public void removeTunnelTableEntry(Uint64 dpnId, Uint32 l3Vni, TypedReadWriteTransaction<Configuration> confTx)
286 throws ExecutionException, InterruptedException {
287 LOG.debug("removeTunnelTableEntry : Remove terminating service table {} --> table {} flow on NAPT DpnId {} "
288 + "with l3Vni {} as matching parameter", NwConstants.INTERNAL_TUNNEL_TABLE,
289 NwConstants.INBOUND_NAPT_TABLE, dpnId, l3Vni);
290 List<MatchInfo> mkMatches = new ArrayList<>();
292 mkMatches.add(new MatchTunnelId(Uint64.valueOf(l3Vni)));
293 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
294 NatEvpnUtil.getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, l3Vni, NatConstants.SNAT_FLOW_NAME),
295 NatConstants.DEFAULT_VPN_INTERNAL_TUNNEL_TABLE_PRIORITY,
296 String.format("%s:%s", "TST Flow Entry ", l3Vni), 0, 0,
297 Uint64.valueOf(COOKIE_TUNNEL.toJava().add(BigInteger.valueOf(l3Vni.longValue()))), mkMatches, null);
298 mdsalManager.removeFlow(confTx, dpnId, flowEntity);
299 LOG.debug("removeTunnelTableEntry : Successfully removed terminating service table flow {} on DpnId {}",