2 * Copyright © 2016, 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
8 package org.opendaylight.netvirt.natservice.internal;
10 import static org.opendaylight.netvirt.natservice.internal.NatUtil.buildfloatingIpIdToPortMappingIdentifier;
12 import com.google.common.base.Optional;
13 import com.google.common.util.concurrent.AsyncFunction;
14 import com.google.common.util.concurrent.FutureCallback;
15 import com.google.common.util.concurrent.Futures;
16 import com.google.common.util.concurrent.JdkFutureAdapters;
17 import com.google.common.util.concurrent.ListenableFuture;
18 import java.math.BigInteger;
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.List;
22 import java.util.concurrent.Future;
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.genius.mdsalutil.ActionInfo;
27 import org.opendaylight.genius.mdsalutil.MDSALUtil;
28 import org.opendaylight.genius.mdsalutil.MatchInfo;
29 import org.opendaylight.genius.mdsalutil.NwConstants;
30 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
31 import org.opendaylight.genius.mdsalutil.actions.ActionPopMpls;
32 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
33 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
34 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
35 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
36 import org.opendaylight.genius.mdsalutil.matches.MatchMplsLabel;
37 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
38 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
39 import org.opendaylight.netvirt.elanmanager.api.IElanService;
40 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
41 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
42 import org.opendaylight.netvirt.vpnmanager.api.IVpnManager;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.OdlArputilService;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpRequestInput;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpRequestInputBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.interfaces.InterfaceAddress;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.interfaces.InterfaceAddressBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInput;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInputBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInput;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.info.router.ports.ports.InternalToExternalPortMap;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.floating.ip.port.info.FloatingIpIdToPortMapping;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelInput;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelInputBuilder;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelOutput;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveVpnLabelInput;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveVpnLabelInputBuilder;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.VpnRpcService;
67 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
68 import org.opendaylight.yangtools.yang.common.RpcResult;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
72 public class VpnFloatingIpHandler implements FloatingIPHandler {
73 private static final Logger LOG = LoggerFactory.getLogger(VpnFloatingIpHandler.class);
74 private final DataBroker dataBroker;
75 private final IMdsalApiManager mdsalManager;
76 private final VpnRpcService vpnService;
77 private final IBgpManager bgpManager;
78 private final FibRpcService fibService;
79 private final FloatingIPListener floatingIPListener;
80 private final IVpnManager vpnManager;
81 private final IFibManager fibManager;
82 private final OdlArputilService arpUtilService;
83 private final IElanService elanService;
85 static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
86 static final String FLOWID_PREFIX = "NAT.";
88 public VpnFloatingIpHandler(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
89 final VpnRpcService vpnService,
90 final IBgpManager bgpManager,
91 final FibRpcService fibService,
92 final FloatingIPListener floatingIPListener,
93 final IFibManager fibManager,
94 final OdlArputilService arputilService,
95 final IVpnManager vpnManager,
96 final IElanService elanService
98 this.dataBroker = dataBroker;
99 this.mdsalManager = mdsalManager;
100 this.vpnService = vpnService;
101 this.bgpManager = bgpManager;
102 this.fibService = fibService;
103 this.floatingIPListener = floatingIPListener;
104 this.fibManager = fibManager;
105 this.arpUtilService = arputilService;
106 this.vpnManager = vpnManager;
107 this.elanService = elanService;
111 public void onAddFloatingIp(final BigInteger dpnId, final String routerId,
112 final Uuid networkId, final String interfaceName,
113 final InternalToExternalPortMap mapping) {
114 String internalIp = mapping.getInternalIp();
115 String externalIp = mapping.getExternalIp();
116 Uuid floatingIpId = mapping.getExternalId();
117 String floatingIpPortMacAddress = NatUtil.getFloatingIpPortMacFromFloatingIpId(dataBroker, floatingIpId);
118 final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG);
119 if (vpnName == null) {
120 LOG.info("No VPN associated with ext nw {} to handle add floating ip configuration {} in router {}",
121 networkId, externalIp, routerId);
125 GenerateVpnLabelInput labelInput = new GenerateVpnLabelInputBuilder().setVpnName(vpnName)
126 .setIpPrefix(externalIp).build();
127 Future<RpcResult<GenerateVpnLabelOutput>> labelFuture = vpnService.generateVpnLabel(labelInput);
129 ListenableFuture<RpcResult<Void>> future = Futures.transform(JdkFutureAdapters.listenInPoolThread(labelFuture),
130 (AsyncFunction<RpcResult<GenerateVpnLabelOutput>, RpcResult<Void>>) result -> {
131 if (result.isSuccessful()) {
132 GenerateVpnLabelOutput output = result.getResult();
133 long label = output.getLabel();
134 LOG.debug("Generated label {} for prefix {}", label, externalIp);
135 floatingIPListener.updateOperationalDS(routerId, interfaceName, label, internalIp, externalIp);
138 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
139 String nextHopIp = NatUtil.getEndpointIpAddressForDPN(dataBroker, dpnId);
140 LOG.debug("Nexthop ip for prefix {} is {}", externalIp, nextHopIp);
141 NatUtil.addPrefixToBGP(dataBroker, bgpManager, fibManager, vpnName, rd, externalIp + "/32",
142 nextHopIp, label, LOG, RouteOrigin.STATIC, dpnId);
144 List<Instruction> instructions = new ArrayList<>();
145 List<ActionInfo> actionsInfos = new ArrayList<>();
146 actionsInfos.add(new ActionNxResubmit(NwConstants.PDNAT_TABLE));
147 instructions.add(new InstructionApplyActions(actionsInfos).buildInstruction(0));
148 makeTunnelTableEntry(dpnId, label, instructions);
150 //Install custom FIB routes
151 List<Instruction> customInstructions = new ArrayList<>();
152 customInstructions.add(new InstructionGotoTable(NwConstants.PDNAT_TABLE).buildInstruction(0));
153 makeLFibTableEntry(dpnId, label, NwConstants.PDNAT_TABLE);
154 CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName)
155 .setSourceDpid(dpnId).setInstruction(customInstructions)
156 .setIpAddress(externalIp + "/32").setServiceId(label)
157 .setInstruction(customInstructions).build();
158 //Future<RpcResult<java.lang.Void>> createFibEntry(CreateFibEntryInput input);
159 Future<RpcResult<Void>> future1 = fibService.createFibEntry(input);
160 LOG.debug("Add Floating Ip {} , found associated to fixed port {}", externalIp, interfaceName);
161 if (floatingIpPortMacAddress != null) {
162 WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
163 vpnManager.setupSubnetMacIntoVpnInstance(vpnName, floatingIpPortMacAddress, dpnId, writeTx,
164 NwConstants.ADD_FLOW);
165 vpnManager.setupArpResponderFlowsToExternalNetworkIps(routerId,
166 Collections.singleton(externalIp),
167 floatingIpPortMacAddress, dpnId, networkId, writeTx, NwConstants.ADD_FLOW);
170 return JdkFutureAdapters.listenInPoolThread(future1);
172 String errMsg = String.format("Could not retrieve the label for prefix %s in VPN %s, %s",
173 externalIp, vpnName, result.getErrors());
175 return Futures.immediateFailedFuture(new RuntimeException(errMsg));
179 Futures.addCallback(future, new FutureCallback<RpcResult<Void>>() {
182 public void onFailure(Throwable error) {
183 LOG.error("Error in generate label or fib install process", error);
187 public void onSuccess(RpcResult<Void> result) {
188 if (result.isSuccessful()) {
189 LOG.info("Successfully installed custom FIB routes for prefix {}", externalIp);
191 LOG.error("Error in rpc call to create custom Fib entries for prefix {} in DPN {}, {}",
192 externalIp, dpnId, result.getErrors());
197 // Handle GARP transmission
198 final IpAddress extrenalAddress = IpAddressBuilder.getDefaultInstance(externalIp);
199 sendGarpOnInterface(dpnId, networkId, extrenalAddress, floatingIpPortMacAddress);
204 public void onRemoveFloatingIp(final BigInteger dpnId, String routerId, final Uuid networkId,
205 InternalToExternalPortMap mapping, final long label) {
206 final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG);
207 String externalIp = mapping.getExternalIp();
208 Uuid floatingIpId = mapping.getExternalId();
211 if (vpnName == null) {
212 LOG.info("No VPN associated with ext nw {} to handle remove floating ip configuration {} in router {}",
213 networkId, externalIp, routerId);
217 //Remove floating mac from mymac table
218 LOG.debug("Removing FloatingIp {}", externalIp);
219 String floatingIpPortMacAddress = NatUtil.getFloatingIpPortMacFromFloatingIpId(dataBroker, floatingIpId);
220 if (floatingIpPortMacAddress != null) {
221 WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
222 vpnManager.setupSubnetMacIntoVpnInstance(vpnName, floatingIpPortMacAddress, dpnId, writeTx,
223 NwConstants.DEL_FLOW);
224 vpnManager.setupArpResponderFlowsToExternalNetworkIps(routerId, Collections.singletonList(externalIp),
225 floatingIpPortMacAddress, dpnId, networkId, writeTx, NwConstants.DEL_FLOW);
228 removeFromFloatingIpPortInfo(floatingIpId);
229 cleanupFibEntries(dpnId, vpnName, externalIp, label);
233 public void cleanupFibEntries(final BigInteger dpnId, final String vpnName, final String externalIp,
235 //Remove Prefix from BGP
236 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
237 NatUtil.removePrefixFromBGP(dataBroker, bgpManager, fibManager, rd, externalIp + "/32", LOG);
239 //Remove custom FIB routes
241 //Future<RpcResult<java.lang.Void>> removeFibEntry(RemoveFibEntryInput input);
242 RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName)
243 .setSourceDpid(dpnId).setIpAddress(externalIp + "/32").setServiceId(label).build();
244 Future<RpcResult<Void>> future = fibService.removeFibEntry(input);
246 ListenableFuture<RpcResult<Void>> labelFuture = Futures.transform(JdkFutureAdapters.listenInPoolThread(future),
247 (AsyncFunction<RpcResult<Void>, RpcResult<Void>>) result -> {
249 if (result.isSuccessful()) {
250 removeTunnelTableEntry(dpnId, label);
251 removeLFibTableEntry(dpnId, label);
252 RemoveVpnLabelInput labelInput = new RemoveVpnLabelInputBuilder()
253 .setVpnName(vpnName).setIpPrefix(externalIp).build();
254 Future<RpcResult<Void>> labelFuture1 = vpnService.removeVpnLabel(labelInput);
255 return JdkFutureAdapters.listenInPoolThread(labelFuture1);
257 String errMsg = String.format("RPC call to remove custom FIB entries on dpn %s for "
258 + "prefix %s Failed - %s", dpnId, externalIp, result.getErrors());
260 return Futures.immediateFailedFuture(new RuntimeException(errMsg));
264 Futures.addCallback(labelFuture, new FutureCallback<RpcResult<Void>>() {
267 public void onFailure(Throwable error) {
268 LOG.error("Error in removing the label or custom fib entries", error);
272 public void onSuccess(RpcResult<Void> result) {
273 if (result.isSuccessful()) {
274 LOG.debug("Successfully removed the label for the prefix {} from VPN {}", externalIp, vpnName);
276 LOG.error("Error in removing the label for prefix {} from VPN {}, {}",
277 externalIp, vpnName, result.getErrors());
283 private String getFlowRef(BigInteger dpnId, short tableId, long id, String ipAddress) {
284 return FLOWID_PREFIX + dpnId + NwConstants.FLOWID_SEPARATOR + tableId + NwConstants.FLOWID_SEPARATOR + id
285 + NwConstants.FLOWID_SEPARATOR + ipAddress;
288 private void removeTunnelTableEntry(BigInteger dpnId, long serviceId) {
289 LOG.info("remove terminatingServiceActions called with DpnId = {} and label = {}", dpnId, serviceId);
290 List<MatchInfo> mkMatches = new ArrayList<>();
292 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(serviceId)));
293 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
294 getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""),
295 5, String.format("%s:%d", "TST Flow Entry ", serviceId), 0, 0,
296 COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)), mkMatches, null);
297 mdsalManager.removeFlow(dpnId, flowEntity);
298 LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully {}", dpnId, serviceId);
301 private void makeTunnelTableEntry(BigInteger dpnId, long serviceId, List<Instruction> customInstructions) {
302 List<MatchInfo> mkMatches = new ArrayList<>();
304 LOG.info("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}", dpnId, serviceId);
306 mkMatches.add(new MatchTunnelId(BigInteger.valueOf(serviceId)));
308 Flow terminatingServiceTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
309 getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""), 5,
310 String.format("%s:%d", "TST Flow Entry ", serviceId),
311 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)), mkMatches, customInstructions);
313 mdsalManager.installFlow(dpnId, terminatingServiceTableFlowEntity);
316 private void makeLFibTableEntry(BigInteger dpId, long serviceId, short tableId) {
317 List<MatchInfo> matches = new ArrayList<>();
318 matches.add(MatchEthernetType.MPLS_UNICAST);
319 matches.add(new MatchMplsLabel(serviceId));
321 List<Instruction> instructions = new ArrayList<>();
322 List<ActionInfo> actionsInfos = new ArrayList<>();
323 actionsInfos.add(new ActionPopMpls());
324 Instruction writeInstruction = new InstructionApplyActions(actionsInfos).buildInstruction(0);
325 instructions.add(writeInstruction);
326 instructions.add(new InstructionGotoTable(tableId).buildInstruction(1));
328 // Install the flow entry in L3_LFIB_TABLE
329 String flowRef = getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, serviceId, "");
331 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef,
333 NwConstants.COOKIE_VM_LFIB_TABLE, matches, instructions);
335 mdsalManager.installFlow(dpId, flowEntity);
337 LOG.debug("LFIB Entry for dpID {} : label : {} modified successfully {}", dpId, serviceId);
340 private void removeLFibTableEntry(BigInteger dpnId, long serviceId) {
341 List<MatchInfo> matches = new ArrayList<>();
342 matches.add(MatchEthernetType.MPLS_UNICAST);
343 matches.add(new MatchMplsLabel(serviceId));
345 String flowRef = getFlowRef(dpnId, NwConstants.L3_LFIB_TABLE, serviceId, "");
347 LOG.debug("removing LFib entry with flow ref {}", flowRef);
349 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef,
351 NwConstants.COOKIE_VM_LFIB_TABLE, matches, null);
353 mdsalManager.removeFlow(dpnId, flowEntity);
355 LOG.debug("LFIB Entry for dpID : {} label : {} removed successfully {}", dpnId, serviceId);
358 // TODO Clean up the exception handling
359 @SuppressWarnings("checkstyle:IllegalCatch")
360 private void sendGarpOnInterface(final BigInteger dpnId, Uuid networkId, final IpAddress floatingIpAddress,
361 String floatingIpPortMacAddress) {
362 if (floatingIpAddress.getIpv4Address() == null) {
363 LOG.info("Failed to send GARP for IP. recieved IPv6.");
364 NatServiceCounters.garp_failed_ipv6.inc();
368 String interfaceName = elanService.getExternalElanInterface(networkId.getValue(), dpnId);
369 if (interfaceName == null) {
370 LOG.warn("Failed to send GARP for IP. Failed to retrieve interface name from network {} and dpn id {}.",
371 networkId.getValue(), dpnId);
372 NatServiceCounters.garp_failed_missing_interface.inc();
376 // find the external network interface name for dpn
377 List<InterfaceAddress> interfaceAddresses = new ArrayList<>();
378 interfaceAddresses.add(new InterfaceAddressBuilder()
379 .setInterface(interfaceName)
380 .setIpAddress(floatingIpAddress)
381 .setMacaddress(new PhysAddress(floatingIpPortMacAddress)).build());
383 SendArpRequestInput sendArpRequestInput = new SendArpRequestInputBuilder().setIpaddress(floatingIpAddress)
384 .setInterfaceAddress(interfaceAddresses).build();
385 arpUtilService.sendArpRequest(sendArpRequestInput);
386 NatServiceCounters.garp_sent.inc();
387 } catch (Exception e) {
388 LOG.error("Failed to send GARP request for floating ip {} from interface {}",
389 floatingIpAddress.getIpv4Address().getValue(), interfaceName, e);
390 NatServiceCounters.garp_failed_send.inc();
394 // TODO Clean up the exception handling
395 @SuppressWarnings("checkstyle:IllegalCatch")
396 private void removeFromFloatingIpPortInfo(Uuid floatingIpId) {
397 InstanceIdentifier id = buildfloatingIpIdToPortMappingIdentifier(floatingIpId);
399 Optional<FloatingIpIdToPortMapping> optFloatingIpIdToPortMapping =
400 NatUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
401 if (optFloatingIpIdToPortMapping.isPresent() && optFloatingIpIdToPortMapping.get().isFloatingIpDeleted()) {
402 LOG.debug("Deleting floating IP UUID {} to Floating IP neutron port mapping from Floating "
403 + "IP Port Info Config DS", floatingIpId.getValue());
404 MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
406 } catch (Exception e) {
407 LOG.error("Deleting floating IP UUID {} to Floating IP neutron port mapping from Floating "
408 + "IP Port Info Config DS failed with exception {}", floatingIpId.getValue(), e);