Switch to JDT annotations for Nullable and NonNull
[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.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;
53
54 @Singleton
55 public class EvpnSnatFlowProgrammer {
56     private static final Logger LOG = LoggerFactory.getLogger(EvpnSnatFlowProgrammer.class);
57
58     private static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
59
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;
67
68     @Inject
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;
81     }
82
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) {
87      /*
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}
91       *
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}
94       *
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))
97       *
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}
101       */
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);
109             return;
110         }
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();
118         }
119
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 {}",
123                     vpnName);
124             return;
125         }
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
128          */
129         //Inform to BGP
130         NatEvpnUtil.addRoutesForVxLanProvType(dataBroker, bgpManager, fibManager, vpnName, rd, externalIp,
131                 nextHopIp, l3Vni, null /*InterfaceName*/, gwMacAddress, confTx, RouteOrigin.STATIC,
132                 dpnId, extNetworkId);
133
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);
138
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);
146
147         ListenableFuture<RpcResult<CreateFibEntryOutput>> futureVxlan = fibService.createFibEntry(input);
148
149         final long finalL3Vni = l3Vni;
150         Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<CreateFibEntryOutput>>() {
151             @Override
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);
156             }
157
158             @Override
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);
164
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)
167                   */
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))
171                   */
172                     NatEvpnUtil.makeL3GwMacTableEntry(dpnId, vpnId, gwMacAddress, customInstructions, mdsalManager,
173                             confTx);
174
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)
177                   */
178                     NatUtil.makePreDnatToSnatTableEntry(mdsalManager, dpnId, tableId, confTx);
179                 }
180             }
181         }, MoreExecutors.directExecutor());
182     }
183
184     public void evpnDelFibTsAndReverseTraffic(final BigInteger dpnId, final long routerId, final String externalIp,
185         final String vpnName, String extGwMacAddress) {
186      /*
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}
190       *
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
193       *     Same Hypervisor}
194       *
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))
197       *
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}
201       */
202         String rd = NatUtil.getVpnRd(dataBroker, vpnName);
203         if (rd == null) {
204             LOG.error("evpnDelFibTsAndReverseTraffic : Could not retrieve RD value from VPN Name {}", vpnName);
205             return;
206         }
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);
210             return;
211         }
212         if (extGwMacAddress == null) {
213             LOG.error("evpnDelFibTsAndReverseTraffic : Unable to Get External Gateway MAC address for "
214                     + "External Router ID {} ", routerId);
215             return;
216         }
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();
223         }
224
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);
232
233         ListenableFuture<RpcResult<RemoveFibEntryOutput>> futureVxlan = fibService.removeFibEntry(input);
234         final long finalL3Vni = l3Vni;
235         Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<RemoveFibEntryOutput>>() {
236             @Override
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);
241             }
242
243             @Override
244             public void onSuccess(@NonNull RpcResult<RemoveFibEntryOutput> result) {
245                 if (result.isSuccessful()) {
246                     ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
247                         innerConfTx -> {
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);
251
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,
258                                 innerConfTx);
259                         }), LOG, "Error removing EVPN SNAT table entries");
260                 }
261             }
262         }, MoreExecutors.directExecutor());
263     }
264
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)));
271
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);
279     }
280
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<>();
287         // Matching metadata
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 {}",
295                 flowEntity, dpnId);
296     }
297 }