1adb620ae0d26f61c507dcdec6dcbb45424d9e59
[netvirt.git] / natservice / impl / src / main / java / org / opendaylight / netvirt / natservice / internal / EvpnSnatFlowProgrammer.java
1 /*
2  * Copyright (c) 2017 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.netvirt.natservice.internal;
10
11 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
12
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.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.concurrent.ExecutionException;
23 import javax.inject.Inject;
24 import javax.inject.Singleton;
25 import org.eclipse.jdt.annotation.NonNull;
26 import org.opendaylight.genius.infra.Datastore.Configuration;
27 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
28 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
29 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
30 import org.opendaylight.genius.infra.TypedWriteTransaction;
31 import org.opendaylight.genius.mdsalutil.MDSALUtil;
32 import org.opendaylight.genius.mdsalutil.MatchInfo;
33 import org.opendaylight.genius.mdsalutil.NwConstants;
34 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
35 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
36 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
37 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
38 import org.opendaylight.mdsal.binding.api.DataBroker;
39 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
40 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
41 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInput;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInputBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryOutput;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInput;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryOutput;
53 import org.opendaylight.yangtools.yang.common.RpcResult;
54 import org.opendaylight.yangtools.yang.common.Uint32;
55 import org.opendaylight.yangtools.yang.common.Uint64;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59 @Singleton
60 public class EvpnSnatFlowProgrammer {
61     private static final Logger LOG = LoggerFactory.getLogger(EvpnSnatFlowProgrammer.class);
62
63     private static final Uint64 COOKIE_TUNNEL = Uint64.valueOf("9000000", 16).intern();
64
65     private final DataBroker dataBroker;
66     private final ManagedNewTransactionRunner txRunner;
67     private final IMdsalApiManager mdsalManager;
68     private final IBgpManager bgpManager;
69     private final IFibManager fibManager;
70     private final FibRpcService fibService;
71     private final NatOverVxlanUtil natOverVxlanUtil;
72
73     @Inject
74     public EvpnSnatFlowProgrammer(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
75                            final IBgpManager bgpManager,
76                            final IFibManager fibManager,
77                            final FibRpcService fibService,
78                            final NatOverVxlanUtil natOverVxlanUtil) {
79         this.dataBroker = dataBroker;
80         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
81         this.mdsalManager = mdsalManager;
82         this.bgpManager = bgpManager;
83         this.fibManager = fibManager;
84         this.fibService = fibService;
85         this.natOverVxlanUtil = natOverVxlanUtil;
86     }
87
88     public void evpnAdvToBgpAndInstallFibAndTsFlows(final Uint64 dpnId, final short tableId,
89         final String externalIp, final String vpnName, final String rd, final String nextHopIp,
90         final Uint32 routerId, final String routerName, final Uuid extNetworkId,
91         TypedWriteTransaction<Configuration> confTx) {
92      /*
93       * 1) Install the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
94       *    (FIP VM on DPN1 is responding back to external fixed IP on DPN2) {DNAT to SNAT traffic on
95       *     different Hypervisor}
96       *
97       * 2) Install the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
98       *    (FIP VM on DPN1 is responding back to external fixed IP on beyond DC-GW VM){DNAT to SNAT Inter DC traffic}
99       *
100       * 3) Install the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
101       *    (If there is no FIP Match on table 25 (PDNAT_TABLE) then default flow to INBOUND_NAPT_TABLE (table=44))
102       *
103       * 4) Install the flow L3_FIB_TABLE (table=21)-> INBOUND_NAPT_TABLE (table=44)
104       *    (FIP VM on DPN1 is responding back to external fixed Ip on DPN1 itself. ie. same Hypervisor)
105       *    {DNAT to SNAT Intra DC traffic}
106       */
107         LOG.info("evpnAdvToBgpAndInstallFibAndTsFlows : Handling SNAT Reverse Traffic for External Fixed IP {} for "
108                 + "RouterId {}", externalIp, routerId);
109         // Get the External Gateway MAC Address which is Router gateway MAC address for SNAT
110         String gwMacAddress = NatUtil.getExtGwMacAddFromRouterName(dataBroker, routerName);
111         if (gwMacAddress == null) {
112             LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Unable to Retrieve External Gateway MAC address "
113                     + "from Router ID {}", routerId);
114             return;
115         }
116         //get l3Vni value for external VPN
117         Uint32 l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
118         if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
119             LOG.debug("evpnAdvToBgpAndInstallFibAndTsFlows : L3VNI value is not configured in Internet VPN {}"
120                     + " and RD {} Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue with "
121                     + "installing SNAT flows for External Fixed IP {}", vpnName, rd, externalIp);
122             l3Vni = natOverVxlanUtil.getInternetVpnVni(vpnName, routerId);
123         }
124
125         Uint32 vpnId = NatUtil.getVpnId(dataBroker, vpnName);
126         if (vpnId == NatConstants.INVALID_ID) {
127             LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Invalid Vpn Id is found for Vpn Name {}",
128                     vpnName);
129             return;
130         }
131         /* As of now neither SNAT nor DNAT will use mac-address while advertising to FIB and BGP instead
132          * use only gwMacAddress. Hence default value of macAddress is null
133          */
134         //Inform to BGP
135         NatEvpnUtil.addRoutesForVxLanProvType(dataBroker, bgpManager, fibManager, vpnName, rd, externalIp,
136                 nextHopIp, l3Vni, null /*InterfaceName*/, gwMacAddress, confTx, RouteOrigin.STATIC,
137                 dpnId, extNetworkId);
138
139         //Install custom FIB routes - FIB table.
140         List<Instruction> customInstructions = new ArrayList<>();
141         customInstructions.add(new InstructionGotoTable(tableId).buildInstruction(0));
142         final String externalFixedIp = NatUtil.validateAndAddNetworkMask(externalIp);
143
144         CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName)
145                 .setSourceDpid(dpnId).setIpAddress(externalFixedIp)
146                 .setServiceId(l3Vni).setIpAddressSource(CreateFibEntryInput.IpAddressSource.ExternalFixedIP)
147                 .setInstruction(customInstructions).build();
148         LOG.debug("evpnAdvToBgpAndInstallFibAndTsFlows : Installing custom FIB table {} --> table {} flow on "
149                 + "NAPT Switch {} with l3Vni {}, ExternalFixedIp {}, ExternalVpnName {} for RouterId {}",
150                 NwConstants.L3_FIB_TABLE, tableId, dpnId, l3Vni, externalIp, vpnName, routerId);
151
152         ListenableFuture<RpcResult<CreateFibEntryOutput>> futureVxlan = fibService.createFibEntry(input);
153
154         final Uint32 finalL3Vni = l3Vni;
155         Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<CreateFibEntryOutput>>() {
156             @Override
157             public void onFailure(@NonNull Throwable error) {
158                 LOG.error("evpnAdvToBgpAndInstallFibAndTsFlows : Error in custom fib routes install process for "
159                         + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
160                         externalIp, dpnId, finalL3Vni, vpnName, routerId, error);
161             }
162
163             @Override
164             public void onSuccess(@NonNull RpcResult<CreateFibEntryOutput> result) {
165                 if (result.isSuccessful()) {
166                     LOG.info("evpnAdvToBgpAndInstallFibAndTsFlows : Successfully installed custom FIB routes for "
167                             + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
168                             externalIp, dpnId, finalL3Vni, vpnName, routerId);
169
170                  /* Install the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
171                   * (SNAT to DNAT reverse Traffic: If traffic is Initiated from NAPT to FIP VM on different Hypervisor)
172                   */
173                     makeTunnelTableEntry(dpnId, finalL3Vni, customInstructions, tableId, confTx);
174                  /* Install the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
175                   * (SNAT reverse traffic: If the traffic is Initiated from DC-GW to VM (SNAT Reverse traffic))
176                   */
177                     NatEvpnUtil.makeL3GwMacTableEntry(dpnId, vpnId, gwMacAddress, customInstructions, mdsalManager,
178                             confTx);
179
180                  /* Install the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
181                   * If there is no FIP Match on table 25 (PDNAT_TABLE)
182                   */
183                     NatUtil.makePreDnatToSnatTableEntry(mdsalManager, dpnId, tableId, confTx);
184                 }
185             }
186         }, MoreExecutors.directExecutor());
187     }
188
189     public void evpnDelFibTsAndReverseTraffic(final Uint64 dpnId, final Uint32 routerId, final String externalIp,
190                                               final String vpnName, String extGwMacAddress) {
191      /*
192       * 1) Remove the flow INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44)
193       *    (FIP VM on DPN1 is responding back to external fixed IP on DPN2) {DNAT to SNAT traffic on
194       *     different Hypervisor}
195       *
196       * 2) Remove the flow L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44)
197       *    (FIP VM on DPN1 is responding back to external fixed IP on DPN1 itself){DNAT to SNAT traffic on
198       *     Same Hypervisor}
199       *
200       * 3) Remove the flow PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44)
201       *    (If there is no FIP Match on table 25 (PDNAT_TABLE) then default flow to INBOUND_NAPT_TABLE (table=44))
202       *
203       * 4) Remove the flow L3_FIB_TABLE (table=21)-> INBOUND_NAPT_TABLE (table=44)
204       *    (FIP VM on DPN1 is responding back to external fixed Ip on DPN1 itself. ie. same Hypervisor)
205       *    {DNAT to SNAT Intra DC traffic}
206       */
207         String rd = NatUtil.getVpnRd(dataBroker, vpnName);
208         if (rd == null) {
209             LOG.error("evpnDelFibTsAndReverseTraffic : Could not retrieve RD value from VPN Name {}", vpnName);
210             return;
211         }
212         Uint32 vpnId = NatUtil.getVpnId(dataBroker, vpnName);
213         if (vpnId == NatConstants.INVALID_ID) {
214             LOG.error("evpnDelFibTsAndReverseTraffic : Invalid Vpn Id is found for Vpn Name {}", vpnName);
215             return;
216         }
217         if (extGwMacAddress == null) {
218             LOG.error("evpnDelFibTsAndReverseTraffic : Unable to Get External Gateway MAC address for "
219                     + "External Router ID {} ", routerId);
220             return;
221         }
222         Uint32 l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
223         if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
224             LOG.debug("evpnDelFibTsAndReverseTraffic : L3VNI value is not configured in Internet VPN {} and RD {} "
225                     + "Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue with installing "
226                     + "SNAT flows for External Fixed IP {}", vpnName, rd, externalIp);
227             l3Vni = natOverVxlanUtil.getInternetVpnVni(vpnName, routerId);
228         }
229
230         final String externalFixedIp = NatUtil.validateAndAddNetworkMask(externalIp);
231         RemoveFibEntryInput input = new RemoveFibEntryInputBuilder()
232                 .setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalFixedIp)
233                 .setIpAddressSource(RemoveFibEntryInput.IpAddressSource.ExternalFixedIP).setServiceId(l3Vni).build();
234         LOG.debug("evpnDelFibTsAndReverseTraffic : Removing custom FIB table {} --> table {} flow on "
235                         + "NAPT Switch {} with l3Vni {}, ExternalFixedIp {}, ExternalVpnName {} for RouterId {}",
236                 NwConstants.L3_FIB_TABLE, NwConstants.INBOUND_NAPT_TABLE, dpnId, l3Vni, externalIp, vpnName, routerId);
237
238         ListenableFuture<RpcResult<RemoveFibEntryOutput>> futureVxlan = fibService.removeFibEntry(input);
239         final Uint32 finalL3Vni = l3Vni;
240         Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<RemoveFibEntryOutput>>() {
241             @Override
242             public void onFailure(@NonNull Throwable error) {
243                 LOG.error("evpnDelFibTsAndReverseTraffic : Error in custom fib routes remove process for "
244                         + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for RouterId {}",
245                         externalIp, dpnId, finalL3Vni, vpnName, routerId, error);
246             }
247
248             @Override
249             public void onSuccess(@NonNull RpcResult<RemoveFibEntryOutput> result) {
250                 if (result.isSuccessful()) {
251                     ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
252                         innerConfTx -> {
253                             LOG.info("evpnDelFibTsAndReverseTraffic : Successfully removed custom FIB routes for "
254                                 + "External Fixed IP {} on DPN {} with l3Vni {}, ExternalVpnName {} for "
255                                 + "RouterId {}", externalIp, dpnId, finalL3Vni, vpnName, routerId);
256
257                             //remove INTERNAL_TUNNEL_TABLE (table=36)-> INBOUND_NAPT_TABLE (table=44) flow
258                             removeTunnelTableEntry(dpnId, finalL3Vni, innerConfTx);
259                             //remove L3_GW_MAC_TABLE (table=19)-> INBOUND_NAPT_TABLE (table=44) flow
260                             NatUtil.removePreDnatToSnatTableEntry(innerConfTx, mdsalManager, dpnId);
261                             //remove PDNAT_TABLE (table=25)-> INBOUND_NAPT_TABLE (table=44) flow
262                             NatEvpnUtil.removeL3GwMacTableEntry(dpnId, vpnId, extGwMacAddress, mdsalManager,
263                                 innerConfTx);
264                         }), LOG, "Error removing EVPN SNAT table entries");
265                 }
266             }
267         }, MoreExecutors.directExecutor());
268     }
269
270     public void makeTunnelTableEntry(Uint64 dpnId, Uint32 l3Vni, List<Instruction> customInstructions,
271                                      short tableId, TypedWriteTransaction<Configuration> confTx) {
272         LOG.debug("makeTunnelTableEntry : Create terminating service table {} --> table {} flow on NAPT DpnId {} "
273                 + "with l3Vni {} as matching parameter", NwConstants.INTERNAL_TUNNEL_TABLE, tableId, dpnId, l3Vni);
274         List<MatchInfo> mkMatches = new ArrayList<>();
275         mkMatches.add(new MatchTunnelId(Uint64.valueOf(l3Vni)));
276
277         Map<InstructionKey, Instruction> customInstructionsMap = new HashMap<InstructionKey, Instruction>();
278         int instructionKey = 0;
279         for (Instruction instructionObj : customInstructions) {
280             customInstructionsMap.put(new InstructionKey(++instructionKey), instructionObj);
281         }
282
283         Flow terminatingServiceTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
284                 NatEvpnUtil.getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, l3Vni, NatConstants.SNAT_FLOW_NAME),
285                 NatConstants.DEFAULT_VPN_INTERNAL_TUNNEL_TABLE_PRIORITY,
286                 String.format("%s:%s", "TST Flow Entry ", l3Vni),
287                 0, 0, Uint64.valueOf(COOKIE_TUNNEL.toJava().add(BigInteger.valueOf(l3Vni.longValue()))),
288                 mkMatches, customInstructionsMap);
289         mdsalManager.addFlow(confTx, dpnId, terminatingServiceTableFlowEntity);
290         LOG.debug("makeTunnelTableEntry : Successfully installed terminating service table flow {} on DpnId {}",
291                 terminatingServiceTableFlowEntity, dpnId);
292     }
293
294     public void removeTunnelTableEntry(Uint64 dpnId, Uint32 l3Vni, TypedReadWriteTransaction<Configuration> confTx)
295             throws ExecutionException, InterruptedException {
296         LOG.debug("removeTunnelTableEntry : Remove terminating service table {} --> table {} flow on NAPT DpnId {} "
297                 + "with l3Vni {} as matching parameter", NwConstants.INTERNAL_TUNNEL_TABLE,
298                 NwConstants.INBOUND_NAPT_TABLE, dpnId, l3Vni);
299         List<MatchInfo> mkMatches = new ArrayList<>();
300         // Matching metadata
301         mkMatches.add(new MatchTunnelId(Uint64.valueOf(l3Vni)));
302         Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
303                 NatEvpnUtil.getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, l3Vni, NatConstants.SNAT_FLOW_NAME),
304                 NatConstants.DEFAULT_VPN_INTERNAL_TUNNEL_TABLE_PRIORITY,
305                 String.format("%s:%s", "TST Flow Entry ", l3Vni), 0, 0,
306                 Uint64.valueOf(COOKIE_TUNNEL.toJava().add(BigInteger.valueOf(l3Vni.longValue()))), mkMatches, null);
307         mdsalManager.removeFlow(confTx, dpnId, flowEntity);
308         LOG.debug("removeTunnelTableEntry : Successfully removed terminating service table flow {} on DpnId {}",
309                 flowEntity, dpnId);
310     }
311 }