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;
15 import java.math.BigInteger;
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.concurrent.Future;
19 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
20 import org.opendaylight.genius.mdsalutil.ActionInfo;
21 import org.opendaylight.genius.mdsalutil.ActionType;
22 import org.opendaylight.genius.mdsalutil.InstructionInfo;
23 import org.opendaylight.genius.mdsalutil.InstructionType;
24 import org.opendaylight.genius.mdsalutil.MDSALUtil;
25 import org.opendaylight.genius.mdsalutil.MatchFieldType;
26 import org.opendaylight.genius.mdsalutil.MatchInfo;
27 import org.opendaylight.genius.mdsalutil.NwConstants;
28 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
29 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
30 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
31 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInput;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInputBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelInput;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelInputBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelOutput;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveVpnLabelInput;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveVpnLabelInputBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.VpnRpcService;
46 import org.opendaylight.yangtools.yang.common.RpcResult;
47 import org.osgi.framework.BundleContext;
48 import org.slf4j.Logger;
49 import org.slf4j.LoggerFactory;
51 public class VpnFloatingIpHandler implements FloatingIPHandler {
52 private static final Logger LOG = LoggerFactory.getLogger(VpnFloatingIpHandler.class);
53 private final DataBroker dataBroker;
54 private final IMdsalApiManager mdsalManager;
55 private final VpnRpcService vpnService;
56 private final IBgpManager bgpManager;
57 private final FibRpcService fibService;
58 private final FloatingIPListener floatingIPListener;
59 private final IFibManager fibManager;
60 static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
61 static final String FLOWID_PREFIX = "NAT.";
63 public VpnFloatingIpHandler(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
64 final VpnRpcService vpnService,
65 final IBgpManager bgpManager,
66 final FibRpcService fibService,
67 final FloatingIPListener floatingIPListener,
68 final IFibManager fibManager) {
69 this.dataBroker = dataBroker;
70 this.mdsalManager = mdsalManager;
71 this.vpnService = vpnService;
72 this.bgpManager = bgpManager;
73 this.fibService = fibService;
74 this.floatingIPListener = floatingIPListener;
75 this.fibManager = fibManager;
79 public void onAddFloatingIp(final BigInteger dpnId, final String routerId,
80 Uuid networkId, final String interfaceName, final String externalIp, final String internalIp) {
81 final String vpnName = getAssociatedVPN(networkId, routerId);
83 LOG.info("No VPN associated with ext nw {} to handle add floating ip configuration {} in router {}",
84 networkId, externalIp, routerId);
88 GenerateVpnLabelInput labelInput = new GenerateVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
89 Future<RpcResult<GenerateVpnLabelOutput>> labelFuture = vpnService.generateVpnLabel(labelInput);
91 ListenableFuture<RpcResult<Void>> future = Futures.transform(JdkFutureAdapters.listenInPoolThread(labelFuture), new AsyncFunction<RpcResult<GenerateVpnLabelOutput>, RpcResult<Void>>() {
94 public ListenableFuture<RpcResult<Void>> apply(RpcResult<GenerateVpnLabelOutput> result) throws Exception {
95 if(result.isSuccessful()) {
96 GenerateVpnLabelOutput output = result.getResult();
97 long label = output.getLabel();
98 LOG.debug("Generated label {} for prefix {}", label, externalIp);
99 floatingIPListener.updateOperationalDS(routerId, interfaceName, label, internalIp, externalIp);
102 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
103 String nextHopIp = NatUtil.getEndpointIpAddressForDPN(dataBroker, dpnId);
104 LOG.debug("Nexthop ip for prefix {} is {}", externalIp, nextHopIp);
105 NatUtil.addPrefixToBGP(dataBroker, bgpManager, fibManager, rd, externalIp + "/32", nextHopIp,
106 label, LOG, RouteOrigin.STATIC);
108 List<Instruction> instructions = new ArrayList<>();
109 List<ActionInfo> actionsInfos = new ArrayList<>();
110 actionsInfos.add(new ActionInfo(ActionType.nx_resubmit, new String[] { Integer.toString(NwConstants.PDNAT_TABLE) }));
111 instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos).buildInstruction(0));
112 makeTunnelTableEntry(dpnId, label, instructions);
114 //Install custom FIB routes
115 List<Instruction> customInstructions = new ArrayList<>();
116 customInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NwConstants.PDNAT_TABLE }).buildInstruction(0));
117 makeLFibTableEntry(dpnId, label, NwConstants.PDNAT_TABLE);
118 CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setInstruction(customInstructions)
119 .setIpAddress(externalIp + "/32").setServiceId(label).setInstruction(customInstructions).build();
120 //Future<RpcResult<java.lang.Void>> createFibEntry(CreateFibEntryInput input);
121 Future<RpcResult<Void>> future = fibService.createFibEntry(input);
122 return JdkFutureAdapters.listenInPoolThread(future);
124 String errMsg = String.format("Could not retrieve the label for prefix %s in VPN %s, %s", externalIp, vpnName, result.getErrors());
126 return Futures.immediateFailedFuture(new RuntimeException(errMsg));
131 Futures.addCallback(future, new FutureCallback<RpcResult<Void>>() {
134 public void onFailure(Throwable error) {
135 LOG.error("Error in generate label or fib install process", error);
139 public void onSuccess(RpcResult<Void> result) {
140 if(result.isSuccessful()) {
141 LOG.info("Successfully installed custom FIB routes for prefix {}", externalIp);
143 LOG.error("Error in rpc call to create custom Fib entries for prefix {} in DPN {}, {}", externalIp, dpnId, result.getErrors());
150 public void onRemoveFloatingIp(final BigInteger dpnId, String routerId, Uuid networkId, final String externalIp,
151 String internalIp, final long label) {
152 final String vpnName = getAssociatedVPN(networkId, routerId);
153 if(vpnName == null) {
154 LOG.info("No VPN associated with ext nw {} to handle remove floating ip configuration {} in router {}",
155 networkId, externalIp, routerId);
158 //Remove Prefix from BGP
159 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
160 NatUtil.removePrefixFromBGP(dataBroker, bgpManager, fibManager, rd, externalIp + "/32", LOG);
162 //Remove custom FIB routes
163 //Future<RpcResult<java.lang.Void>> removeFibEntry(RemoveFibEntryInput input);
164 RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalIp + "/32").setServiceId(label).build();
165 Future<RpcResult<Void>> future = fibService.removeFibEntry(input);
167 ListenableFuture<RpcResult<Void>> labelFuture = Futures.transform(JdkFutureAdapters.listenInPoolThread(future), new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
170 public ListenableFuture<RpcResult<Void>> apply(RpcResult<Void> result) throws Exception {
172 if(result.isSuccessful()) {
173 removeTunnelTableEntry(dpnId, label);
174 removeLFibTableEntry(dpnId, label);
175 RemoveVpnLabelInput labelInput = new RemoveVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
176 Future<RpcResult<Void>> labelFuture = vpnService.removeVpnLabel(labelInput);
177 return JdkFutureAdapters.listenInPoolThread(labelFuture);
179 String errMsg = String.format("RPC call to remove custom FIB entries on dpn %s for prefix %s Failed - %s", dpnId, externalIp, result.getErrors());
181 return Futures.immediateFailedFuture(new RuntimeException(errMsg));
186 Futures.addCallback(labelFuture, new FutureCallback<RpcResult<Void>>() {
189 public void onFailure(Throwable error) {
190 LOG.error("Error in removing the label or custom fib entries", error);
194 public void onSuccess(RpcResult<Void> result) {
195 if(result.isSuccessful()) {
196 LOG.debug("Successfully removed the label for the prefix {} from VPN {}", externalIp, vpnName);
198 LOG.error("Error in removing the label for prefix {} from VPN {}, {}", externalIp, vpnName, result.getErrors());
204 void cleanupFibEntries(final BigInteger dpnId, final String vpnName, final String externalIp, final long label ) {
205 //Remove Prefix from BGP
206 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
207 NatUtil.removePrefixFromBGP(dataBroker, bgpManager, fibManager, rd, externalIp + "/32", LOG);
209 //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),
216 new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
219 public ListenableFuture<RpcResult<Void>> apply(RpcResult<Void> result) throws Exception {
221 if(result.isSuccessful()) {
222 removeTunnelTableEntry(dpnId, label);
223 removeLFibTableEntry(dpnId, label);
224 RemoveVpnLabelInput labelInput = new RemoveVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
225 Future<RpcResult<Void>> labelFuture = vpnService.removeVpnLabel(labelInput);
226 return JdkFutureAdapters.listenInPoolThread(labelFuture);
228 String errMsg = String.format("RPC call to remove custom FIB entries on dpn %s for prefix %s Failed - %s", dpnId, externalIp, result.getErrors());
230 return Futures.immediateFailedFuture(new RuntimeException(errMsg));
235 Futures.addCallback(labelFuture, new FutureCallback<RpcResult<Void>>() {
238 public void onFailure(Throwable error) {
239 LOG.error("Error in removing the label or custom fib entries", error);
243 public void onSuccess(RpcResult<Void> result) {
244 if(result.isSuccessful()) {
245 LOG.debug("Successfully removed the label for the prefix {} from VPN {}", externalIp, vpnName);
247 LOG.error("Error in removing the label for prefix {} from VPN {}, {}", externalIp, vpnName, result.getErrors());
253 private String getFlowRef(BigInteger dpnId, short tableId, long id, String ipAddress) {
254 return new StringBuilder(64).append(FLOWID_PREFIX).append(dpnId).append(NwConstants.FLOWID_SEPARATOR)
255 .append(tableId).append(NwConstants.FLOWID_SEPARATOR)
256 .append(id).append(NwConstants.FLOWID_SEPARATOR).append(ipAddress).toString();
259 private void removeTunnelTableEntry(BigInteger dpnId, long serviceId) {
260 LOG.info("remove terminatingServiceActions called with DpnId = {} and label = {}", dpnId , serviceId);
261 List<MatchInfo> mkMatches = new ArrayList<>();
263 mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)}));
264 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
265 getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""),
266 5, String.format("%s:%d","TST Flow Entry ",serviceId), 0, 0,
267 COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)), mkMatches, null);
268 mdsalManager.removeFlow(dpnId, flowEntity);
269 LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully {}",dpnId, serviceId);
272 private void makeTunnelTableEntry(BigInteger dpnId, long serviceId, List<Instruction> customInstructions) {
273 List<MatchInfo> mkMatches = new ArrayList<>();
275 LOG.info("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}", dpnId , serviceId);
277 mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)}));
279 Flow terminatingServiceTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
280 getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""), 5, String.format("%s:%d","TST Flow Entry ",serviceId),
281 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)),mkMatches, customInstructions);
283 mdsalManager.installFlow(dpnId, terminatingServiceTableFlowEntity);
286 private void makeLFibTableEntry(BigInteger dpId, long serviceId, long tableId) {
287 List<MatchInfo> matches = new ArrayList<>();
288 matches.add(new MatchInfo(MatchFieldType.eth_type,
289 new long[] { 0x8847L }));
290 matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
292 List<Instruction> instructions = new ArrayList<>();
293 List<ActionInfo> actionsInfos = new ArrayList<>();
294 actionsInfos.add(new ActionInfo(ActionType.pop_mpls, new String[]{}));
295 Instruction writeInstruction = new InstructionInfo(InstructionType.apply_actions, actionsInfos).buildInstruction(0);
296 instructions.add(writeInstruction);
297 instructions.add(new InstructionInfo(InstructionType.goto_table, new long[]{tableId}).buildInstruction(1));
299 // Install the flow entry in L3_LFIB_TABLE
300 String flowRef = getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, serviceId, "");
302 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef,
304 NwConstants.COOKIE_VM_LFIB_TABLE, matches, instructions);
306 mdsalManager.installFlow(dpId, flowEntity);
308 LOG.debug("LFIB Entry for dpID {} : label : {} modified successfully {}",dpId, serviceId );
311 private void removeLFibTableEntry(BigInteger dpnId, long serviceId) {
312 List<MatchInfo> matches = new ArrayList<>();
313 matches.add(new MatchInfo(MatchFieldType.eth_type,
314 new long[] { 0x8847L }));
315 matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
317 String flowRef = getFlowRef(dpnId, NwConstants.L3_LFIB_TABLE, serviceId, "");
319 LOG.debug("removing LFib entry with flow ref {}", flowRef);
321 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef,
323 NwConstants.COOKIE_VM_LFIB_TABLE, matches, null);
325 mdsalManager.removeFlow(dpnId, flowEntity);
327 LOG.debug("LFIB Entry for dpID : {} label : {} removed successfully {}",dpnId, serviceId);
330 private String getAssociatedVPN(Uuid networkId, String routerId) {
331 String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG);
332 return vpnName != null ? vpnName : routerId;