9b2e092bb4d052283a33b050f8eb7bcb8babb828
[netvirt.git] / vpnservice / natservice / natservice-impl / src / main / java / org / opendaylight / netvirt / natservice / internal / EvpnDnatFlowProgrammer.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 com.google.common.base.Optional;
12 import com.google.common.util.concurrent.FutureCallback;
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.JdkFutureAdapters;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import java.math.BigInteger;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.List;
20 import java.util.concurrent.Future;
21
22 import javax.inject.Inject;
23 import javax.inject.Singleton;
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
26 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
27 import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
28 import org.opendaylight.genius.mdsalutil.ActionInfo;
29 import org.opendaylight.genius.mdsalutil.MDSALUtil;
30 import org.opendaylight.genius.mdsalutil.MatchInfo;
31 import org.opendaylight.genius.mdsalutil.NwConstants;
32 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
33 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetDestination;
34 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
35 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
36 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
37 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
38 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
39 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
40 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
41 import org.opendaylight.netvirt.vpnmanager.api.IVpnManager;
42 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
43 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceBuilder;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInput;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInputBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInput;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
57 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
58 import org.opendaylight.yangtools.yang.common.RpcResult;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62 @Singleton
63 public class EvpnDnatFlowProgrammer {
64     private static final Logger LOG = LoggerFactory.getLogger(EvpnDnatFlowProgrammer.class);
65     private final DataBroker dataBroker;
66     private final IMdsalApiManager mdsalManager;
67     private final IBgpManager bgpManager;
68     private final IFibManager fibManager;
69     private final FibRpcService fibService;
70     private final IVpnManager vpnManager;
71     private final IdManagerService idManager;
72     private static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
73
74     @Inject
75     public EvpnDnatFlowProgrammer(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
76                            final IBgpManager bgpManager,
77                            final IFibManager fibManager,
78                            final FibRpcService fibService,
79                            final IVpnManager vpnManager,
80                            final IdManagerService idManager) {
81         this.dataBroker = dataBroker;
82         this.mdsalManager = mdsalManager;
83         this.bgpManager = bgpManager;
84         this.fibManager = fibManager;
85         this.fibService = fibService;
86         this.vpnManager = vpnManager;
87         this.idManager = idManager;
88     }
89
90     public void onAddFloatingIp(final BigInteger dpnId, final String routerName, final String vpnName,
91                                 final String internalIp, final String externalIp, final Uuid networkId,
92                                 final String interfaceName,
93                                 final String floatingIpInterface,
94                                 final String floatingIpPortMacAddress,
95                                 final String rd,
96                                 final String nextHopIp, final WriteTransaction writeTx) {
97     /*
98      *  1) Install the flow INTERNAL_TUNNEL_TABLE (table=36)-> PDNAT_TABLE (table=25) (SNAT VM on DPN1 is
99      *     responding back to FIP VM on DPN2) {SNAT to DNAT traffic on different Hypervisor}
100      *
101      *  2) Install the flow L3_FIB_TABLE (table=21)-> PDNAT_TABLE (table=25) (FIP VM1 to FIP VM2
102      *    Traffic on Same Hypervisor) {DNAT to DNAT on Same Hypervisor}
103      *
104      *  3) Install the flow L3_GW_MAC_TABLE (table=19)-> PDNAT_TABLE (table=25)
105      *    (DC-GW is responding back to FIP VM) {DNAT Reverse traffic})
106      *
107      */
108         long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
109         if (vpnId == NatConstants.INVALID_ID) {
110             LOG.error("NAT Service : Invalid Vpn Id is found for Vpn Name {}", vpnName);
111             return;
112         }
113         long routerId = NatUtil.getVpnId(dataBroker, routerName);
114         if (routerId == NatConstants.INVALID_ID) {
115             LOG.error("Unable to get RouterId from RouterName {}", routerName);
116             return;
117         }
118         long l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
119         if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
120             LOG.debug("NAT Service : L3VNI value is not configured in Internet VPN {} and RD {} "
121                     + "Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue with installing "
122                     + "DNAT flows for FloatingIp {}", vpnName, rd, externalIp);
123             l3Vni = NatOverVxlanUtil.getInternetVpnVni(idManager, vpnName, routerId).longValue();
124         }
125         FloatingIPListener.updateOperationalDS(dataBroker, routerName, interfaceName, NatConstants.DEFAULT_LABEL_VALUE,
126                 internalIp, externalIp);
127         String fibExternalIp = NatUtil.validateAndAddNetworkMask(externalIp);
128         //Inform to FIB and BGP
129         NatEvpnUtil.addRoutesForVxLanProvType(dataBroker, bgpManager, fibManager, vpnName, rd, fibExternalIp,
130                 nextHopIp, l3Vni, floatingIpInterface, floatingIpPortMacAddress,
131                 writeTx, RouteOrigin.STATIC, dpnId);
132
133         /* Install the flow table L3_FIB_TABLE (table=21)-> PDNAT_TABLE (table=25)
134          * (SNAT to DNAT reverse traffic: If the DPN has both SNAT and  DNAT configured )
135          */
136         List<ActionInfo> actionInfoFib = new ArrayList<>();
137         actionInfoFib.add(new ActionSetFieldEthernetDestination(new MacAddress(floatingIpPortMacAddress)));
138         List<Instruction> instructionsFib = new ArrayList<>();
139         instructionsFib.add(new InstructionApplyActions(actionInfoFib).buildInstruction(0));
140         instructionsFib.add(new InstructionGotoTable(NwConstants.PDNAT_TABLE).buildInstruction(1));
141
142         CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName)
143                 .setSourceDpid(dpnId).setIpAddress(fibExternalIp)
144                 .setServiceId(l3Vni).setIpAddressSource(CreateFibEntryInput.IpAddressSource.FloatingIP)
145                 .setInstruction(instructionsFib).build();
146
147         Future<RpcResult<Void>> future1 = fibService.createFibEntry(input);
148         ListenableFuture<RpcResult<Void>> futureVxlan = JdkFutureAdapters.listenInPoolThread(future1);
149         LOG.debug("NAT Service : Add Floating Ip {} , found associated to fixed port {}",
150                 externalIp, interfaceName);
151         if (floatingIpPortMacAddress != null) {
152             vpnManager.setupSubnetMacIntoVpnInstance(vpnName, null /* subnet-vpn-name */, floatingIpPortMacAddress,
153                     dpnId, writeTx, NwConstants.ADD_FLOW);
154             vpnManager.setupArpResponderFlowsToExternalNetworkIps(routerName,
155                     Collections.singleton(externalIp),
156                     floatingIpPortMacAddress, dpnId, networkId, writeTx, NwConstants.ADD_FLOW);
157         }
158         final long finalL3Vni = l3Vni;
159         Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<Void>>() {
160
161             @Override
162             public void onFailure(Throwable error) {
163                 LOG.error("NAT Service : Error {} in custom fib routes install process for Floating "
164                         + "IP Prefix {} on DPN {}", error, externalIp, dpnId);
165             }
166
167             @Override
168             public void onSuccess(RpcResult<Void> result) {
169                 if (result.isSuccessful()) {
170                     LOG.info("NAT Service : Successfully installed custom FIB routes for Floating "
171                             + "IP Prefix {} on DPN {}", externalIp, dpnId);
172                     List<Instruction> instructions = new ArrayList<>();
173                     List<ActionInfo> actionsInfos = new ArrayList<>();
174                     List<Instruction> customInstructions = new ArrayList<>();
175                     customInstructions.add(new InstructionGotoTable(NwConstants.PDNAT_TABLE).buildInstruction(0));
176                     actionsInfos.add(new ActionNxResubmit(NwConstants.PDNAT_TABLE));
177                     instructions.add(new InstructionApplyActions(actionsInfos).buildInstruction(0));
178                  /* If more than one floatingIp is available in vpn-to-dpn-list for given dpn id, do not call for
179                   * installing INTERNAL_TUNNEL_TABLE (table=36) -> PDNAT_TABLE (table=25) flow entry with same tunnel_id
180                   * again and again.
181                   */
182                     if (!NatUtil.isFloatingIpPresentForDpn(dataBroker, dpnId, rd, vpnName, externalIp, true)) {
183                         makeTunnelTableEntry(dpnId, finalL3Vni, instructions);
184                     }
185                  /* Install the flow L3_GW_MAC_TABLE (table=19)-> PDNAT_TABLE (table=25)
186                   * (DNAT reverse traffic: If the traffic is Initiated from DC-GW to FIP VM (DNAT forward traffic))
187                   */
188                     NatEvpnUtil.makeL3GwMacTableEntry(dpnId, vpnId, floatingIpPortMacAddress, customInstructions,
189                             mdsalManager);
190                 } else {
191                     LOG.error("NAT Service : Error {} in rpc call to create custom Fib entries for Floating "
192                             + "IP Prefix {} on DPN {}, {}", result.getErrors(), externalIp, dpnId);
193                 }
194             }
195         });
196
197         //Read the FIP vpn-interface details from Configuration l3vpn:vpn-interfaces model and write into Operational DS
198         InstanceIdentifier<VpnInterface> vpnIfIdentifier = NatUtil.getVpnInterfaceIdentifier(floatingIpInterface);
199         Optional<VpnInterface> optionalVpnInterface =
200                 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
201                         LogicalDatastoreType.CONFIGURATION, vpnIfIdentifier);
202         if (optionalVpnInterface.isPresent()) {
203             VpnInterfaceBuilder vpnIfBuilder = new VpnInterfaceBuilder(optionalVpnInterface.get());
204             Adjacencies adjs = vpnIfBuilder.getAugmentation(Adjacencies.class);
205             List<Adjacency> adjacencyList = (adjs != null) ? adjs.getAdjacency() : new ArrayList<>();
206             Adjacencies adjacencies = new AdjacenciesBuilder().setAdjacency(adjacencyList).build();
207             vpnIfBuilder.addAugmentation(Adjacencies.class, adjacencies);
208
209             WriteTransaction writeOperTxn = dataBroker.newWriteOnlyTransaction();
210             LOG.debug("NAT Service : Add vpnInterface {} to Operational l3vpn:vpn-interfaces ", floatingIpInterface);
211             writeOperTxn.put(LogicalDatastoreType.OPERATIONAL, vpnIfIdentifier, vpnIfBuilder.build(),
212                     WriteTransaction.CREATE_MISSING_PARENTS);
213             writeOperTxn.submit();
214         } else {
215             LOG.debug("NAT Service : No vpnInterface {} found in Configuration l3vpn:vpn-interfaces ",
216                     floatingIpInterface);
217         }
218     }
219
220     public void onRemoveFloatingIp(final BigInteger dpnId, final String vpnName, final String externalIp,
221                                    final String floatingIpInterface, final String floatingIpPortMacAddress,
222                                    final String routerName) {
223     /*
224      *  1) Remove the flow INTERNAL_TUNNEL_TABLE (table=36)-> PDNAT_TABLE (table=25) (SNAT VM on DPN1 is
225      *     responding back to FIP VM on DPN2) {SNAT to DNAT traffic on different Hypervisor}
226      *
227      *  2) Remove the flow L3_FIB_TABLE (table=21)-> PDNAT_TABLE (table=25) (FIP VM1 to FIP VM2
228      *    Traffic on Same Hypervisor) {DNAT to DNAT on Same Hypervisor}
229      *
230      *  3) Remove the flow L3_GW_MAC_TABLE (table=19)-> PDNAT_TABLE (table=25)
231      *    (DC-GW is responding back to FIP VM) {DNAT Reverse traffic})
232      *
233      */
234         String rd = NatUtil.getVpnRd(dataBroker, vpnName);
235         if (rd == null) {
236             LOG.error("NAT Service : Could not retrieve RD value from VPN Name {}  ", vpnName);
237             return;
238         }
239         long vpnId = NatUtil.getVpnId(dataBroker, vpnName);
240         if (vpnId == NatConstants.INVALID_ID) {
241             LOG.error("NAT Service : Invalid Vpn Id is found for Vpn Name {}", vpnName);
242             return;
243         }
244         long routerId = NatUtil.getVpnId(dataBroker, routerName);
245         if (routerId == NatConstants.INVALID_ID) {
246             LOG.error("Unable to get RouterId from RouterName {}", routerName);
247             return;
248         }
249         long l3Vni = NatEvpnUtil.getL3Vni(dataBroker, rd);
250         if (l3Vni == NatConstants.DEFAULT_L3VNI_VALUE) {
251             LOG.debug("NAT Service : L3VNI value is not configured in Internet VPN {} and RD {} "
252                     + "Carve-out L3VNI value from OpenDaylight VXLAN VNI Pool and continue with installing "
253                     + "DNAT flows for FloatingIp {}", vpnName, rd, externalIp);
254             l3Vni = NatOverVxlanUtil.getInternetVpnVni(idManager, vpnName, routerId).longValue();
255         }
256         String fibExternalIp = NatUtil.validateAndAddNetworkMask(externalIp);
257
258         //Remove Prefix from BGP
259         NatUtil.removePrefixFromBGP(dataBroker, bgpManager, fibManager, rd, fibExternalIp, vpnName, LOG);
260
261         //Remove custom FIB routes flow for L3_FIB_TABLE (table=21)-> PDNAT_TABLE (table=25)
262         RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName)
263                 .setSourceDpid(dpnId).setIpAddress(fibExternalIp).setServiceId(l3Vni)
264                 .setIpAddressSource(RemoveFibEntryInput.IpAddressSource.FloatingIP).build();
265         Future<RpcResult<Void>> future = fibService.removeFibEntry(input);
266         ListenableFuture<RpcResult<Void>> futureVxlan = JdkFutureAdapters.listenInPoolThread(future);
267         final long finalL3Vni = l3Vni;
268         Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<Void>>() {
269
270             @Override
271             public void onFailure(Throwable error) {
272                 LOG.error("NAT Service : Error {} in custom fib routes remove process for Floating "
273                         + "IP Prefix {} on DPN {}", error, externalIp, dpnId);
274             }
275
276             @Override
277             public void onSuccess(RpcResult<Void> result) {
278                 if (result.isSuccessful()) {
279                     LOG.info("NAT Service : Successfully removed custom FIB routes for Floating "
280                             + "IP Prefix {} on DPN {}", externalIp, dpnId);
281                      /*  check if any floating IP information is available in vpn-to-dpn-list for given dpn id.
282                       *  If exist any floating IP then do not remove
283                       *  INTERNAL_TUNNEL_TABLE (table=36) -> PDNAT_TABLE (table=25) flow entry.
284                       */
285                     if (!NatUtil.isFloatingIpPresentForDpn(dataBroker, dpnId, rd, vpnName, externalIp, false)) {
286                         //Remove the flow for INTERNAL_TUNNEL_TABLE (table=36)-> PDNAT_TABLE (table=25)
287                         removeTunnelTableEntry(dpnId, finalL3Vni);
288                     }
289                     //Remove the flow for L3_GW_MAC_TABLE (table=19)-> PDNAT_TABLE (table=25)
290                     NatEvpnUtil.removeL3GwMacTableEntry(dpnId, vpnId, floatingIpPortMacAddress, mdsalManager);
291
292                 } else {
293                     LOG.error("NAT Service : Error {} in rpc call to remove custom Fib entries for Floating "
294                             + "IP Prefix {} on DPN {}, {}", result.getErrors(), externalIp, dpnId);
295                 }
296             }
297         });
298         //Read the FIP vpn-interface details from Operational l3vpn:vpn-interfaces model and delete from Operational DS
299         InstanceIdentifier<VpnInterface> vpnIfIdentifier = NatUtil.getVpnInterfaceIdentifier(floatingIpInterface);
300         Optional<VpnInterface> optionalVpnInterface =
301                 SingleTransactionDataBroker.syncReadOptionalAndTreatReadFailedExceptionAsAbsentOptional(dataBroker,
302                         LogicalDatastoreType.OPERATIONAL, vpnIfIdentifier);
303         if (optionalVpnInterface.isPresent()) {
304             WriteTransaction writeOperTxn = dataBroker.newWriteOnlyTransaction();
305             LOG.debug("NAT Service : Remove vpnInterface {} to Operational l3vpn:vpn-interfaces ", floatingIpInterface);
306             writeOperTxn.delete(LogicalDatastoreType.OPERATIONAL, vpnIfIdentifier);
307             writeOperTxn.submit();
308         } else {
309             LOG.debug("NAT Service : No vpnInterface {} found in Operational l3vpn:vpn-interfaces ",
310                     floatingIpInterface);
311         }
312     }
313
314     private void makeTunnelTableEntry(BigInteger dpnId, long l3Vni, List<Instruction> customInstructions) {
315         LOG.debug("NAT Service : Create terminating service table {} --> table {} flow on DpnId {} with l3Vni {} "
316                         + "as matching parameter", NwConstants.INTERNAL_TUNNEL_TABLE, NwConstants.PDNAT_TABLE, dpnId,
317                 l3Vni);
318         List<MatchInfo> mkMatches = new ArrayList<>();
319         mkMatches.add(new MatchTunnelId(BigInteger.valueOf(l3Vni)));
320         Flow terminatingServiceTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
321                 NatEvpnUtil.getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, l3Vni, NatConstants.DNAT_FLOW_NAME), 6,
322                 String.format("%s:%d", "TST Flow Entry ", l3Vni),
323                 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(l3Vni)), mkMatches, customInstructions);
324         mdsalManager.installFlow(dpnId, terminatingServiceTableFlowEntity);
325         LOG.debug("NAT Service : Successfully installed terminating service table flow {} on DpnId {}",
326                 terminatingServiceTableFlowEntity, dpnId);
327     }
328
329     private void removeTunnelTableEntry(BigInteger dpnId, long l3Vni) {
330         LOG.debug("NAT Service : Remove terminating service table {} --> table {} flow on DpnId {} with l3Vni {} "
331                         + "as matching parameter", NwConstants.INTERNAL_TUNNEL_TABLE, NwConstants.PDNAT_TABLE,
332                 dpnId, l3Vni);
333         List<MatchInfo> mkMatches = new ArrayList<>();
334         mkMatches.add(new MatchTunnelId(BigInteger.valueOf(l3Vni)));
335         Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
336                 NatEvpnUtil.getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, l3Vni, NatConstants.DNAT_FLOW_NAME),
337                 6, String.format("%s:%d", "TST Flow Entry ", l3Vni), 0, 0,
338                 COOKIE_TUNNEL.add(BigInteger.valueOf(l3Vni)), mkMatches, null);
339         mdsalManager.removeFlow(dpnId, flowEntity);
340         LOG.debug("NAT Service : Successfully removed terminating service table flow {} on DpnId {}", flowEntity,
341                 dpnId);
342     }
343 }