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 com.google.common.util.concurrent.FutureCallback;
12 import com.google.common.util.concurrent.Futures;
13 import com.google.common.util.concurrent.JdkFutureAdapters;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import com.google.common.util.concurrent.MoreExecutors;
16 import java.math.BigInteger;
17 import java.util.ArrayList;
18 import java.util.List;
19 import java.util.concurrent.Future;
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.controller.md.sal.binding.api.WriteTransaction;
25 import org.opendaylight.genius.mdsalutil.MDSALUtil;
26 import org.opendaylight.genius.mdsalutil.MatchInfo;
27 import org.opendaylight.genius.mdsalutil.NwConstants;
28 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
29 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
30 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
31 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
32 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
33 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInput;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInputBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInput;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
42 import org.opendaylight.yangtools.yang.common.RpcResult;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
47 public class EvpnSnatFlowProgrammer {
48 private static final Logger LOG = LoggerFactory.getLogger(EvpnSnatFlowProgrammer.class);
50 private static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
52 private final DataBroker dataBroker;
53 private final IMdsalApiManager mdsalManager;
54 private final IBgpManager bgpManager;
55 private final IFibManager fibManager;
56 private final FibRpcService fibService;
57 private final IdManagerService idManager;
60 public EvpnSnatFlowProgrammer(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
61 final IBgpManager bgpManager,
62 final IFibManager fibManager,
63 final FibRpcService fibService,
64 final IdManagerService idManager) {
65 this.dataBroker = dataBroker;
66 this.mdsalManager = mdsalManager;
67 this.bgpManager = bgpManager;
68 this.fibManager = fibManager;
69 this.fibService = fibService;
70 this.idManager = idManager;
73 public void evpnAdvToBgpAndInstallFibAndTsFlows(final BigInteger dpnId, final short tableId,
74 final String externalIp, final String vpnName, final String rd,
75 final String nextHopIp, final WriteTransaction writeTx,
76 final long routerId, final String routerName,
77 WriteTransaction writeFlowInvTx) {
79 * 1) Install the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
80 * (FIP VM on DPN1 is responding back to external fixed IP on DPN2) {DNAT to SNAT traffic on
81 * different Hypervisor}
83 * 2) Install the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
84 * (FIP VM on DPN1 is responding back to external fixed IP on beyond DC-GW VM){DNAT to SNAT Inter DC traffic}
86 * 3) Install the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
87 * (If there is no FIP Match on table 25 (PDNAT_TABLE) then default flow to INBOUND_NAPT_TABLE (table=44))
89 * 4) Install the flow L3_FIB_TABLE (table=21)-> INBOUND_NAPT_TABLE (table=44)
90 * (FIP VM on DPN1 is responding back to external fixed Ip on DPN1 itself. ie. same Hypervisor)
91 * {DNAT to SNAT Intra DC traffic}
93 LOG.info("evpnAdvToBgpAndInstallFibAndTsFlows : Handling SNAT Reverse Traffic for External Fixed IP {} for "
94 + "RouterId {}", externalIp, routerId);
95 // Get the External Gateway MAC Address which is Router gateway MAC address for SNAT
96 String gwMacAddress = NatUtil.getExtGwMacAddFromRouterName(dataBroker, routerName);
97 if (gwMacAddress == null) {
98 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Unable to Retrieve External Gateway MAC address "
99 + "from Router ID {}", routerId);
102 //get l3Vni value for external VPN
103 long l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
104 if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
105 LOG.debug("evpnAdvToBgpAndInstallFibAndTsFlows : L3VNI value is not configured in Internet VPN {}"
106 + " and RD {} Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue with "
107 + "installing SNAT flows for External Fixed IP {}", vpnName, rd, externalIp);
108 l3Vni = NatOverVxlanUtil.getInternetVpnVni(idManager, vpnName, routerId).longValue();
111 long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
112 if (vpnId == NatConstants.INVALID_ID) {
113 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Invalid Vpn Id is found for Vpn Name {}",
117 /* As of now neither SNAT nor DNAT will use mac-address while advertising to FIB and BGP instead
118 * use only gwMacAddress. Hence default value of macAddress is null
121 NatEvpnUtil.addRoutesForVxLanProvType(dataBroker, bgpManager, fibManager, vpnName, rd, externalIp,
122 nextHopIp, l3Vni, null /*InterfaceName*/, gwMacAddress, writeTx, RouteOrigin.STATIC, dpnId);
124 //Install custom FIB routes - FIB table.
125 List<Instruction> customInstructions = new ArrayList<>();
126 customInstructions.add(new InstructionGotoTable(tableId).buildInstruction(0));
127 final String externalFixedIp = NatUtil.validateAndAddNetworkMask(externalIp);
129 CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName)
130 .setSourceDpid(dpnId).setIpAddress(externalFixedIp)
131 .setServiceId(l3Vni).setIpAddressSource(CreateFibEntryInput.IpAddressSource.ExternalFixedIP)
132 .setInstruction(customInstructions).build();
133 LOG.debug("evpnAdvToBgpAndInstallFibAndTsFlows : Installing custom FIB table {} --> table {} flow on "
134 + "NAPT Switch {} with l3Vni {}, ExternalFixedIp {}, ExternalVpnName {} for RouterId {}",
135 NwConstants.L3_FIB_TABLE, tableId, dpnId, l3Vni, externalIp, vpnName, routerId);
137 Future<RpcResult<Void>> future1 = fibService.createFibEntry(input);
138 ListenableFuture<RpcResult<Void>> futureVxlan = JdkFutureAdapters.listenInPoolThread(future1);
140 final long finalL3Vni = l3Vni;
141 Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<Void>>() {
143 public void onFailure(@Nonnull Throwable error) {
144 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Error in custom fib routes install process for "
145 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
146 externalIp, dpnId, finalL3Vni, vpnName, routerId, error);
150 public void onSuccess(@Nonnull RpcResult<Void> result) {
151 if (result.isSuccessful()) {
152 LOG.info("evpnAdvToBgpAndInstallFibAndTsFlows : Successfully installed custom FIB routes for "
153 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
154 externalIp, dpnId, finalL3Vni, vpnName, routerId);
156 /* Install the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
157 * (SNAT to DNAT reverse Traffic: If traffic is Initiated from NAPT to FIP VM on different Hypervisor)
159 makeTunnelTableEntry(dpnId, finalL3Vni, customInstructions, tableId, writeFlowInvTx);
160 /* Install the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
161 * (SNAT reverse traffic: If the traffic is Initiated from DC-GW to VM (SNAT Reverse traffic))
163 NatEvpnUtil.makeL3GwMacTableEntry(dpnId, vpnId, gwMacAddress, customInstructions, mdsalManager,
166 /* Install the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
167 * If there is no FIP Match on table 25 (PDNAT_TABLE)
169 NatUtil.makePreDnatToSnatTableEntry(mdsalManager, dpnId, tableId, writeFlowInvTx);
172 }, MoreExecutors.directExecutor());
175 public void evpnDelFibTsAndReverseTraffic(final BigInteger dpnId, final long routerId, final String externalIp,
176 final String vpnName, String extGwMacAddress,
177 WriteTransaction removeFlowInvTx) {
179 * 1) Remove the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
180 * (FIP VM on DPN1 is responding back to external fixed IP on DPN2) {DNAT to SNAT traffic on
181 * different Hypervisor}
183 * 2) Remove the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
184 * (FIP VM on DPN1 is responding back to external fixed IP on DPN1 itself){DNAT to SNAT traffic on
187 * 3) Remove the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
188 * (If there is no FIP Match on table 25 (PDNAT_TABLE) then default flow to INBOUND_NAPT_TABLE (table=44))
190 * 4) Remove the flow L3_FIB_TABLE (table=21)-> INBOUND_NAPT_TABLE (table=44)
191 * (FIP VM on DPN1 is responding back to external fixed Ip on DPN1 itself. ie. same Hypervisor)
192 * {DNAT to SNAT Intra DC traffic}
194 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
196 LOG.error("evpnDelFibTsAndReverseTraffic : Could not retrieve RD value from VPN Name {}", vpnName);
199 long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
200 if (vpnId == NatConstants.INVALID_ID) {
201 LOG.error("evpnDelFibTsAndReverseTraffic : Invalid Vpn Id is found for Vpn Name {}", vpnName);
204 if (extGwMacAddress == null) {
205 LOG.error("evpnDelFibTsAndReverseTraffic : Unable to Get External Gateway MAC address for "
206 + "External Router ID {} ", routerId);
209 long l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
210 if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
211 LOG.debug("evpnDelFibTsAndReverseTraffic : L3VNI value is not configured in Internet VPN {} and RD {} "
212 + "Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue with installing "
213 + "SNAT flows for External Fixed IP {}", vpnName, rd, externalIp);
214 l3Vni = NatOverVxlanUtil.getInternetVpnVni(idManager, vpnName, routerId).longValue();
217 final String externalFixedIp = NatUtil.validateAndAddNetworkMask(externalIp);
218 RemoveFibEntryInput input = new RemoveFibEntryInputBuilder()
219 .setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalFixedIp)
220 .setIpAddressSource(RemoveFibEntryInput.IpAddressSource.ExternalFixedIP).setServiceId(l3Vni).build();
221 LOG.debug("evpnDelFibTsAndReverseTraffic : Removing custom FIB table {} --> table {} flow on "
222 + "NAPT Switch {} with l3Vni {}, ExternalFixedIp {}, ExternalVpnName {} for RouterId {}",
223 NwConstants.L3_FIB_TABLE, NwConstants.INBOUND_NAPT_TABLE, dpnId, l3Vni, externalIp, vpnName, routerId);
225 Future<RpcResult<Void>> future = fibService.removeFibEntry(input);
226 ListenableFuture<RpcResult<Void>> futureVxlan = JdkFutureAdapters.listenInPoolThread(future);
227 final long finalL3Vni = l3Vni;
228 Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<Void>>() {
230 public void onFailure(@Nonnull Throwable error) {
231 LOG.error("evpnDelFibTsAndReverseTraffic : Error in custom fib routes remove process for "
232 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
233 externalIp, dpnId, finalL3Vni, vpnName, routerId, error);
237 public void onSuccess(@Nonnull RpcResult<Void> result) {
238 if (result.isSuccessful()) {
239 LOG.info("evpnDelFibTsAndReverseTraffic : Successfully removed custom FIB routes for "
240 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for "
241 + "RouterId {}", externalIp, dpnId, finalL3Vni, vpnName, routerId);
243 //remove INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44) flow
244 removeTunnelTableEntry(dpnId, finalL3Vni, removeFlowInvTx);
245 //remove L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44) flow
246 NatUtil.removePreDnatToSnatTableEntry(mdsalManager, dpnId, removeFlowInvTx);
247 //remove PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44) flow
248 NatEvpnUtil.removeL3GwMacTableEntry(dpnId, vpnId, extGwMacAddress, mdsalManager, removeFlowInvTx);
251 }, MoreExecutors.directExecutor());
254 public void makeTunnelTableEntry(BigInteger dpnId, long l3Vni, List<Instruction> customInstructions,
255 short tableId, WriteTransaction writeFlowTx) {
256 LOG.debug("makeTunnelTableEntry : Create terminating service table {} --> table {} flow on NAPT DpnId {} "
257 + "with l3Vni {} as matching parameter", NwConstants.INTERNAL_TUNNEL_TABLE, tableId, dpnId, l3Vni);
258 List<MatchInfo> mkMatches = new ArrayList<>();
259 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(l3Vni)));
261 Flow terminatingServiceTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
262 NatEvpnUtil.getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, l3Vni, NatConstants.SNAT_FLOW_NAME), 5,
263 String.format("%s:%d", "TST Flow Entry ", l3Vni),
264 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(l3Vni)), mkMatches, customInstructions);
265 mdsalManager.addFlowToTx(dpnId, terminatingServiceTableFlowEntity, writeFlowTx);
266 LOG.debug("makeTunnelTableEntry : Successfully installed terminating service table flow {} on DpnId {}",
267 terminatingServiceTableFlowEntity, dpnId);
270 public void removeTunnelTableEntry(BigInteger dpnId, long l3Vni, WriteTransaction removeFlowInvTx) {
271 LOG.debug("removeTunnelTableEntry : Remove terminating service table {} --> table {} flow on NAPT DpnId {} "
272 + "with l3Vni {} as matching parameter", NwConstants.INTERNAL_TUNNEL_TABLE,
273 NwConstants.INBOUND_NAPT_TABLE, dpnId, l3Vni);
274 List<MatchInfo> mkMatches = new ArrayList<>();
276 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(l3Vni)));
277 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
278 NatEvpnUtil.getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, l3Vni, NatConstants.SNAT_FLOW_NAME),
279 5, String.format("%s:%d", "TST Flow Entry ", l3Vni), 0, 0,
280 COOKIE_TUNNEL.add(BigInteger.valueOf(l3Vni)), mkMatches, null);
281 mdsalManager.removeFlowToTx(dpnId, flowEntity, removeFlowInvTx);
282 LOG.debug("removeTunnelTableEntry : Successfully removed terminating service table flow {} on DpnId {}",