2 * Copyright (c) 2016 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 com.google.common.util.concurrent.AsyncFunction;
11 import com.google.common.util.concurrent.FutureCallback;
12 import com.google.common.util.concurrent.Futures;
13 import com.google.common.util.concurrent.JdkFutureAdapters;
14 import com.google.common.util.concurrent.ListenableFuture;
16 import java.math.BigInteger;
17 import java.util.ArrayList;
18 import java.util.List;
19 import java.util.concurrent.Future;
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
23 import org.opendaylight.genius.mdsalutil.ActionInfo;
24 import org.opendaylight.genius.mdsalutil.ActionType;
25 import org.opendaylight.genius.mdsalutil.InstructionInfo;
26 import org.opendaylight.genius.mdsalutil.InstructionType;
27 import org.opendaylight.genius.mdsalutil.MDSALUtil;
28 import org.opendaylight.genius.mdsalutil.MatchFieldType;
29 import org.opendaylight.genius.mdsalutil.MatchInfo;
30 import org.opendaylight.genius.mdsalutil.NwConstants;
31 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
32 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
33 import org.opendaylight.netvirt.elanmanager.api.IElanService;
34 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
35 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
36 import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronConstants;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
41 import org.opendaylight.netvirt.vpnmanager.api.IVpnManager;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.OdlArputilService;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpRequestInput;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpRequestInputBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.interfaces.InterfaceAddress;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.interfaces.InterfaceAddressBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInput;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInputBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInput;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelInput;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelInputBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelOutput;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveVpnLabelInput;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveVpnLabelInputBuilder;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.VpnRpcService;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
62 import org.opendaylight.yangtools.yang.common.RpcResult;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
66 public class VpnFloatingIpHandler implements FloatingIPHandler {
67 private static final Logger LOG = LoggerFactory.getLogger(VpnFloatingIpHandler.class);
68 private final DataBroker dataBroker;
69 private final IMdsalApiManager mdsalManager;
70 private final VpnRpcService vpnService;
71 private final IBgpManager bgpManager;
72 private final FibRpcService fibService;
73 private final FloatingIPListener floatingIPListener;
74 private final IVpnManager vpnManager;
75 private final IFibManager fibManager;
76 private final OdlArputilService arpUtilService;
77 private final IElanService elanService;
79 static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
80 static final String FLOWID_PREFIX = "NAT.";
82 public VpnFloatingIpHandler(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
83 final VpnRpcService vpnService,
84 final IBgpManager bgpManager,
85 final FibRpcService fibService,
86 final FloatingIPListener floatingIPListener,
87 final IFibManager fibManager,
88 final OdlArputilService arputilService,
89 final IVpnManager vpnManager,
90 final IElanService elanService) {
91 this.dataBroker = dataBroker;
92 this.mdsalManager = mdsalManager;
93 this.vpnService = vpnService;
94 this.bgpManager = bgpManager;
95 this.fibService = fibService;
96 this.floatingIPListener = floatingIPListener;
97 this.fibManager = fibManager;
98 this.arpUtilService = arputilService;
99 this.vpnManager = vpnManager;
100 this.elanService = elanService;
104 public void onAddFloatingIp(final BigInteger dpnId, final String routerId,
105 Uuid networkId, final String interfaceName, final String externalIp,
106 final String internalIp) {
107 final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG);
108 if (vpnName == null) {
109 LOG.info("No VPN associated with ext nw {} to handle add floating ip configuration {} in router {}",
110 networkId, externalIp, routerId);
114 GenerateVpnLabelInput labelInput = new GenerateVpnLabelInputBuilder().setVpnName(vpnName)
115 .setIpPrefix(externalIp).build();
116 Future<RpcResult<GenerateVpnLabelOutput>> labelFuture = vpnService.generateVpnLabel(labelInput);
118 ListenableFuture<RpcResult<Void>> future = Futures.transform(JdkFutureAdapters.listenInPoolThread(labelFuture),
119 new AsyncFunction<RpcResult<GenerateVpnLabelOutput>, RpcResult<Void>>() {
122 public ListenableFuture<RpcResult<Void>> apply(RpcResult<GenerateVpnLabelOutput> result) throws Exception {
123 if(result.isSuccessful()) {
124 GenerateVpnLabelOutput output = result.getResult();
125 long label = output.getLabel();
126 LOG.debug("Generated label {} for prefix {}", label, externalIp);
127 floatingIPListener.updateOperationalDS(routerId, interfaceName, label, internalIp, externalIp);
130 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
131 String nextHopIp = NatUtil.getEndpointIpAddressForDPN(dataBroker, dpnId);
132 LOG.debug("Nexthop ip for prefix {} is {}", externalIp, nextHopIp);
133 NatUtil.addPrefixToBGP(dataBroker, bgpManager, fibManager, rd, externalIp + "/32", nextHopIp,
134 label, LOG, RouteOrigin.STATIC);
136 List<Instruction> instructions = new ArrayList<>();
137 List<ActionInfo> actionsInfos = new ArrayList<>();
138 actionsInfos.add(new ActionInfo(ActionType.nx_resubmit, new String[] { Integer.toString(NwConstants.PDNAT_TABLE) }));
139 instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos).buildInstruction(0));
140 makeTunnelTableEntry(dpnId, label, instructions);
142 //Install custom FIB routes
143 List<Instruction> customInstructions = new ArrayList<>();
144 customInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NwConstants.PDNAT_TABLE }).buildInstruction(0));
145 makeLFibTableEntry(dpnId, label, NwConstants.PDNAT_TABLE);
146 CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setInstruction(customInstructions)
147 .setIpAddress(externalIp + "/32").setServiceId(label).setInstruction(customInstructions).build();
148 //Future<RpcResult<java.lang.Void>> createFibEntry(CreateFibEntryInput input);
149 Future<RpcResult<Void>> future = fibService.createFibEntry(input);
150 WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
151 IpAddress externalIpAddress = new IpAddress(new Ipv4Address(externalIp));
152 Port neutronPort = NatUtil.getNeutronPortForFloatingIp(dataBroker, externalIpAddress);
153 if (neutronPort != null && neutronPort.getMacAddress() != null) {
154 vpnManager.setupSubnetMacIntoVpnInstance(vpnName, neutronPort.getMacAddress().getValue(), writeTx, NwConstants.ADD_FLOW);
157 return JdkFutureAdapters.listenInPoolThread(future);
159 String errMsg = String.format("Could not retrieve the label for prefix %s in VPN %s, %s", externalIp, vpnName, result.getErrors());
161 return Futures.immediateFailedFuture(new RuntimeException(errMsg));
166 Futures.addCallback(future, new FutureCallback<RpcResult<Void>>() {
169 public void onFailure(Throwable error) {
170 LOG.error("Error in generate label or fib install process", error);
174 public void onSuccess(RpcResult<Void> result) {
175 if(result.isSuccessful()) {
176 LOG.info("Successfully installed custom FIB routes for prefix {}", externalIp);
178 LOG.error("Error in rpc call to create custom Fib entries for prefix {} in DPN {}, {}", externalIp, dpnId, result.getErrors());
183 // Handle GARP transmission
184 final IpAddress extrenalAddress = IpAddressBuilder.getDefaultInstance(externalIp);
185 sendGarpOnInterface(dpnId, networkId, routerId, extrenalAddress);
190 public void onRemoveFloatingIp(final BigInteger dpnId, String routerId, Uuid networkId, final String externalIp,
191 String internalIp, final long label) {
192 final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG);
193 if (vpnName == null) {
194 LOG.info("No VPN associated with ext nw {} to handle remove floating ip configuration {} in router {}",
195 networkId, externalIp, routerId);
198 //Remove floating mac from mymac table
199 WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
200 IpAddress externalIpAddress = new IpAddress(new Ipv4Address(externalIp));
201 Port neutronPort = NatUtil.getNeutronPortForFloatingIp(dataBroker, externalIpAddress);
202 if (neutronPort != null && neutronPort.getMacAddress() != null) {
203 vpnManager.setupSubnetMacIntoVpnInstance(vpnName, neutronPort.getMacAddress().getValue(), writeTx, NwConstants.DEL_FLOW);
206 //Remove Prefix from BGP
207 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
208 NatUtil.removePrefixFromBGP(dataBroker, bgpManager, fibManager, rd, externalIp + "/32", LOG);
210 //Remove custom FIB routes
211 //Future<RpcResult<java.lang.Void>> removeFibEntry(RemoveFibEntryInput input);
212 RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalIp + "/32").setServiceId(label).build();
213 Future<RpcResult<Void>> future = fibService.removeFibEntry(input);
215 ListenableFuture<RpcResult<Void>> labelFuture = Futures.transform(JdkFutureAdapters.listenInPoolThread(future), new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
218 public ListenableFuture<RpcResult<Void>> apply(RpcResult<Void> result) throws Exception {
220 if(result.isSuccessful()) {
221 removeTunnelTableEntry(dpnId, label);
222 removeLFibTableEntry(dpnId, label);
223 RemoveVpnLabelInput labelInput = new RemoveVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
224 Future<RpcResult<Void>> labelFuture = vpnService.removeVpnLabel(labelInput);
225 return JdkFutureAdapters.listenInPoolThread(labelFuture);
227 String errMsg = String.format("RPC call to remove custom FIB entries on dpn %s for prefix %s Failed - %s", dpnId, externalIp, result.getErrors());
229 return Futures.immediateFailedFuture(new RuntimeException(errMsg));
234 Futures.addCallback(labelFuture, new FutureCallback<RpcResult<Void>>() {
237 public void onFailure(Throwable error) {
238 LOG.error("Error in removing the label or custom fib entries", error);
242 public void onSuccess(RpcResult<Void> result) {
243 if(result.isSuccessful()) {
244 LOG.debug("Successfully removed the label for the prefix {} from VPN {}", externalIp, vpnName);
246 LOG.error("Error in removing the label for prefix {} from VPN {}, {}", externalIp, vpnName, result.getErrors());
252 void cleanupFibEntries(final BigInteger dpnId, final String vpnName, final String externalIp, final long label ) {
253 //Remove Prefix from BGP
254 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
255 NatUtil.removePrefixFromBGP(dataBroker, bgpManager, fibManager, rd, externalIp + "/32", LOG);
257 //Remove custom FIB routes
259 //Future<RpcResult<java.lang.Void>> removeFibEntry(RemoveFibEntryInput input);
260 RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalIp + "/32").setServiceId(label).build();
261 Future<RpcResult<Void>> future = fibService.removeFibEntry(input);
263 ListenableFuture<RpcResult<Void>> labelFuture = Futures.transform(JdkFutureAdapters.listenInPoolThread(future),
264 new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
267 public ListenableFuture<RpcResult<Void>> apply(RpcResult<Void> result) throws Exception {
269 if(result.isSuccessful()) {
270 removeTunnelTableEntry(dpnId, label);
271 removeLFibTableEntry(dpnId, label);
272 RemoveVpnLabelInput labelInput = new RemoveVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
273 Future<RpcResult<Void>> labelFuture = vpnService.removeVpnLabel(labelInput);
274 return JdkFutureAdapters.listenInPoolThread(labelFuture);
276 String errMsg = String.format("RPC call to remove custom FIB entries on dpn %s for prefix %s Failed - %s", dpnId, externalIp, result.getErrors());
278 return Futures.immediateFailedFuture(new RuntimeException(errMsg));
283 Futures.addCallback(labelFuture, new FutureCallback<RpcResult<Void>>() {
286 public void onFailure(Throwable error) {
287 LOG.error("Error in removing the label or custom fib entries", error);
291 public void onSuccess(RpcResult<Void> result) {
292 if(result.isSuccessful()) {
293 LOG.debug("Successfully removed the label for the prefix {} from VPN {}", externalIp, vpnName);
295 LOG.error("Error in removing the label for prefix {} from VPN {}, {}", externalIp, vpnName, result.getErrors());
301 private String getFlowRef(BigInteger dpnId, short tableId, long id, String ipAddress) {
302 return new StringBuilder(64).append(FLOWID_PREFIX).append(dpnId).append(NwConstants.FLOWID_SEPARATOR)
303 .append(tableId).append(NwConstants.FLOWID_SEPARATOR)
304 .append(id).append(NwConstants.FLOWID_SEPARATOR).append(ipAddress).toString();
307 private void removeTunnelTableEntry(BigInteger dpnId, long serviceId) {
308 LOG.info("remove terminatingServiceActions called with DpnId = {} and label = {}", dpnId , serviceId);
309 List<MatchInfo> mkMatches = new ArrayList<>();
311 mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)}));
312 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
313 getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""),
314 5, String.format("%s:%d","TST Flow Entry ",serviceId), 0, 0,
315 COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)), mkMatches, null);
316 mdsalManager.removeFlow(dpnId, flowEntity);
317 LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully {}",dpnId, serviceId);
320 private void makeTunnelTableEntry(BigInteger dpnId, long serviceId, List<Instruction> customInstructions) {
321 List<MatchInfo> mkMatches = new ArrayList<>();
323 LOG.info("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}", dpnId , serviceId);
325 mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)}));
327 Flow terminatingServiceTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
328 getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""), 5, String.format("%s:%d","TST Flow Entry ",serviceId),
329 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)),mkMatches, customInstructions);
331 mdsalManager.installFlow(dpnId, terminatingServiceTableFlowEntity);
334 private void makeLFibTableEntry(BigInteger dpId, long serviceId, long tableId) {
335 List<MatchInfo> matches = new ArrayList<>();
336 matches.add(new MatchInfo(MatchFieldType.eth_type,
337 new long[] { 0x8847L }));
338 matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
340 List<Instruction> instructions = new ArrayList<>();
341 List<ActionInfo> actionsInfos = new ArrayList<>();
342 actionsInfos.add(new ActionInfo(ActionType.pop_mpls, new String[]{}));
343 Instruction writeInstruction = new InstructionInfo(InstructionType.apply_actions, actionsInfos).buildInstruction(0);
344 instructions.add(writeInstruction);
345 instructions.add(new InstructionInfo(InstructionType.goto_table, new long[]{tableId}).buildInstruction(1));
347 // Install the flow entry in L3_LFIB_TABLE
348 String flowRef = getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, serviceId, "");
350 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef,
352 NwConstants.COOKIE_VM_LFIB_TABLE, matches, instructions);
354 mdsalManager.installFlow(dpId, flowEntity);
356 LOG.debug("LFIB Entry for dpID {} : label : {} modified successfully {}",dpId, serviceId );
359 private void removeLFibTableEntry(BigInteger dpnId, long serviceId) {
360 List<MatchInfo> matches = new ArrayList<>();
361 matches.add(new MatchInfo(MatchFieldType.eth_type,
362 new long[] { 0x8847L }));
363 matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
365 String flowRef = getFlowRef(dpnId, NwConstants.L3_LFIB_TABLE, serviceId, "");
367 LOG.debug("removing LFib entry with flow ref {}", flowRef);
369 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef,
371 NwConstants.COOKIE_VM_LFIB_TABLE, matches, null);
373 mdsalManager.removeFlow(dpnId, flowEntity);
375 LOG.debug("LFIB Entry for dpID : {} label : {} removed successfully {}",dpnId, serviceId);
378 private void sendGarpOnInterface(final BigInteger dpnId, Uuid networkId, final String routerId,
379 final IpAddress floatingIpAddress) {
380 if (floatingIpAddress.getIpv4Address() == null) {
381 LOG.info("Failed to send GARP for IP. recieved IPv6.");
382 NatServiceCounters.garp_failed_ipv6.inc();
386 String interfaceName = elanService.getExternalElanInterface(networkId.getValue(), dpnId);
387 if (interfaceName == null) {
388 LOG.warn("Failed to send GARP for IP. Failed to retrieve interface name from network {} and dpn id {}.",
389 networkId.getValue(), dpnId);
390 NatServiceCounters.garp_failed_missing_interface.inc();
394 // find the external network interface name for dpn
395 Port floatingPort = NatUtil.getNeutronPortForFloatingIp(dataBroker, floatingIpAddress);
396 PhysAddress floatingPortMac = new PhysAddress(floatingPort.getMacAddress().getValue());
397 List<InterfaceAddress> interfaceAddresses = new ArrayList<>();
398 interfaceAddresses.add(new InterfaceAddressBuilder()
399 .setInterface(interfaceName)
400 .setIpAddress(floatingIpAddress)
401 .setMacaddress(floatingPortMac).build());
403 SendArpRequestInput sendArpRequestInput = new SendArpRequestInputBuilder().setIpaddress(floatingIpAddress)
404 .setInterfaceAddress(interfaceAddresses).build();
405 arpUtilService.sendArpRequest(sendArpRequestInput);
406 NatServiceCounters.garp_sent.inc();
407 } catch (Exception e) {
408 LOG.error("Failed to send GARP request for floating ip {} from interface {}",
409 floatingIpAddress.getIpv4Address().getValue(), interfaceName, e);
410 NatServiceCounters.garp_failed_send.inc();