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