2 * Copyright (c) 2017 Ericsson India Global Services Pvt Ltd. and others. All rights reserved.
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
9 package org.opendaylight.netvirt.natservice.internal;
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;
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;
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);
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;
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,
96 final String nextHopIp, final WriteTransaction writeTx) {
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}
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}
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})
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);
113 long routerId = NatUtil.getVpnId(dataBroker, routerName);
114 if (routerId == NatConstants.INVALID_ID) {
115 LOG.error("Unable to get RouterId from RouterName {}", routerName);
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();
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);
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 )
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));
142 CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName)
143 .setSourceDpid(dpnId).setIpAddress(fibExternalIp)
144 .setServiceId(l3Vni).setIpAddressSource(CreateFibEntryInput.IpAddressSource.FloatingIP)
145 .setInstruction(instructionsFib).build();
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);
158 final long finalL3Vni = l3Vni;
159 Futures.addCallback(futureVxlan, new FutureCallback<RpcResult<Void>>() {
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);
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
182 if (!NatUtil.isFloatingIpPresentForDpn(dataBroker, dpnId, rd, vpnName, externalIp, true)) {
183 makeTunnelTableEntry(dpnId, finalL3Vni, instructions);
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))
188 NatEvpnUtil.makeL3GwMacTableEntry(dpnId, vpnId, floatingIpPortMacAddress, customInstructions,
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);
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);
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();
215 LOG.debug("NAT Service : No vpnInterface {} found in Configuration l3vpn:vpn-interfaces ",
216 floatingIpInterface);
220 public void onRemoveFloatingIp(final BigInteger dpnId, final String vpnName, final String externalIp,
221 final String floatingIpInterface, final String floatingIpPortMacAddress,
222 final String routerName) {
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}
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}
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})
234 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
236 LOG.error("NAT Service : Could not retrieve RD value from VPN Name {} ", vpnName);
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);
244 long routerId = NatUtil.getVpnId(dataBroker, routerName);
245 if (routerId == NatConstants.INVALID_ID) {
246 LOG.error("Unable to get RouterId from RouterName {}", routerName);
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();
256 String fibExternalIp = NatUtil.validateAndAddNetworkMask(externalIp);
258 //Remove Prefix from BGP
259 NatUtil.removePrefixFromBGP(dataBroker, bgpManager, fibManager, rd, fibExternalIp, vpnName, LOG);
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>>() {
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);
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.
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);
289 //Remove the flow for L3_GW_MAC_TABLE (table=19)-> PDNAT_TABLE (table=25)
290 NatEvpnUtil.removeL3GwMacTableEntry(dpnId, vpnId, floatingIpPortMacAddress, mdsalManager);
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);
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();
309 LOG.debug("NAT Service : No vpnInterface {} found in Operational l3vpn:vpn-interfaces ",
310 floatingIpInterface);
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,
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);
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,
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,