ecc38de53a351e8ee48f900b23bb08f968c60735
[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.opendaylight.yangtools.yang.common.Uint32;
52 import org.opendaylight.yangtools.yang.common.Uint64;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55
56 @Singleton
57 public class EvpnSnatFlowProgrammer {
58     private static final Logger LOG = LoggerFactory.getLogger(EvpnSnatFlowProgrammer.class);
59
60     private static final Uint64 COOKIE_TUNNEL = Uint64.valueOf("9000000", 16).intern();
61
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;
69
70     @Inject
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;
83     }
84
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) {
89      /*
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}
93       *
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}
96       *
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))
99       *
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}
103       */
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);
111             return;
112         }
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);
120         }
121
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 {}",
125                     vpnName);
126             return;
127         }
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
130          */
131         //Inform to BGP
132         NatEvpnUtil.addRoutesForVxLanProvType(dataBroker, bgpManager, fibManager, vpnName, rd, externalIp,
133                 nextHopIp, l3Vni, null /*InterfaceName*/, gwMacAddress, confTx, RouteOrigin.STATIC,
134                 dpnId, extNetworkId);
135
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);
140
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);
148
149         ListenableFuture<RpcResult<CreateFibEntryOutput>> futureVxlan = fibService.createFibEntry(input);
150
151         final Uint32 finalL3Vni = l3Vni;
152         Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<CreateFibEntryOutput>>() {
153             @Override
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);
158             }
159
160             @Override
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);
166
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)
169                   */
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))
173                   */
174                     NatEvpnUtil.makeL3GwMacTableEntry(dpnId, vpnId, gwMacAddress, customInstructions, mdsalManager,
175                             confTx);
176
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)
179                   */
180                     NatUtil.makePreDnatToSnatTableEntry(mdsalManager, dpnId, tableId, confTx);
181                 }
182             }
183         }, MoreExecutors.directExecutor());
184     }
185
186     public void evpnDelFibTsAndReverseTraffic(final Uint64 dpnId, final Uint32 routerId, final String externalIp,
187                                               final String vpnName, String extGwMacAddress) {
188      /*
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}
192       *
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
195       *     Same Hypervisor}
196       *
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))
199       *
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}
203       */
204         String rd = NatUtil.getVpnRd(dataBroker, vpnName);
205         if (rd == null) {
206             LOG.error("evpnDelFibTsAndReverseTraffic : Could not retrieve RD value from VPN Name {}", vpnName);
207             return;
208         }
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);
212             return;
213         }
214         if (extGwMacAddress == null) {
215             LOG.error("evpnDelFibTsAndReverseTraffic : Unable to Get External Gateway MAC address for "
216                     + "External Router ID {} ", routerId);
217             return;
218         }
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);
225         }
226
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);
234
235         ListenableFuture<RpcResult<RemoveFibEntryOutput>> futureVxlan = fibService.removeFibEntry(input);
236         final Uint32 finalL3Vni = l3Vni;
237         Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<RemoveFibEntryOutput>>() {
238             @Override
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);
243             }
244
245             @Override
246             public void onSuccess(@NonNull RpcResult<RemoveFibEntryOutput> result) {
247                 if (result.isSuccessful()) {
248                     ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
249                         innerConfTx -> {
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);
253
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,
260                                 innerConfTx);
261                         }), LOG, "Error removing EVPN SNAT table entries");
262                 }
263             }
264         }, MoreExecutors.directExecutor());
265     }
266
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)));
273
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);
283     }
284
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<>();
291         // Matching metadata
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 {}",
300                 flowEntity, dpnId);
301     }
302 }