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 java.math.BigInteger;
11 import java.util.ArrayList;
12 import java.util.Arrays;
13 import java.util.concurrent.Future;
14 import java.util.List;
16 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
17 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
18 import org.opendaylight.genius.mdsalutil.ActionInfo;
19 import org.opendaylight.genius.mdsalutil.ActionType;
20 import org.opendaylight.genius.mdsalutil.InstructionInfo;
21 import org.opendaylight.genius.mdsalutil.InstructionType;
22 import org.opendaylight.genius.mdsalutil.MDSALUtil;
23 import org.opendaylight.genius.mdsalutil.MatchFieldType;
24 import org.opendaylight.genius.mdsalutil.MatchInfo;
25 import org.opendaylight.genius.mdsalutil.NwConstants;
26 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
27 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
28 import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInput;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInputBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInput;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.VpnRpcService;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelInput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelInputBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelOutput;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveVpnLabelInput;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveVpnLabelInputBuilder;
43 import org.opendaylight.yangtools.yang.common.RpcResult;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
47 import com.google.common.util.concurrent.AsyncFunction;
48 import com.google.common.util.concurrent.FutureCallback;
49 import com.google.common.util.concurrent.Futures;
50 import com.google.common.util.concurrent.JdkFutureAdapters;
51 import com.google.common.util.concurrent.ListenableFuture;
53 public class VpnFloatingIpHandler implements FloatingIPHandler {
54 private static final Logger LOG = LoggerFactory.getLogger(VpnFloatingIpHandler.class);
55 private VpnRpcService vpnService;
56 private FibRpcService fibService;
57 private IBgpManager bgpManager;
58 private DataBroker dataBroker;
59 private IMdsalApiManager mdsalManager;
60 private FloatingIPListener listener;
61 private IFibManager fibManager;
63 static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
64 static final String FLOWID_PREFIX = "NAT.";
65 static final BigInteger COOKIE_VM_LFIB_TABLE = new BigInteger("8000002", 16);
67 public VpnFloatingIpHandler(VpnRpcService vpnService, IBgpManager bgpManager, FibRpcService fibService) {
68 this.vpnService = vpnService;
69 this.fibService = fibService;
70 this.bgpManager = bgpManager;
73 void setListener(FloatingIPListener listener) {
74 this.listener = listener;
77 void setBroker(DataBroker broker) {
81 void setMdsalManager(IMdsalApiManager mdsalManager) {
82 this.mdsalManager = mdsalManager;
85 void setFibManager(IFibManager fibManager) {
86 this.fibManager = fibManager;
90 public void onAddFloatingIp(final BigInteger dpnId, final String routerId,
91 Uuid networkId, final String interfaceName, final String externalIp, final String internalIp) {
92 final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG);
94 LOG.info("No VPN associated with ext nw {} to handle add floating ip configuration {} in router {}",
95 networkId, externalIp, routerId);
99 GenerateVpnLabelInput labelInput = new GenerateVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
100 Future<RpcResult<GenerateVpnLabelOutput>> labelFuture = vpnService.generateVpnLabel(labelInput);
102 ListenableFuture<RpcResult<Void>> future = Futures.transform(JdkFutureAdapters.listenInPoolThread(labelFuture), new AsyncFunction<RpcResult<GenerateVpnLabelOutput>, RpcResult<Void>>() {
105 public ListenableFuture<RpcResult<Void>> apply(RpcResult<GenerateVpnLabelOutput> result) throws Exception {
106 if(result.isSuccessful()) {
107 GenerateVpnLabelOutput output = result.getResult();
108 long label = output.getLabel();
109 LOG.debug("Generated label {} for prefix {}", label, externalIp);
110 listener.updateOperationalDS(routerId, interfaceName, label, internalIp, externalIp);
113 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
114 String nextHopIp = NatUtil.getEndpointIpAddressForDPN(dataBroker, dpnId);
115 LOG.debug("Nexthop ip for prefix {} is {}", externalIp, nextHopIp);
116 NatUtil.addPrefixToBGP(dataBroker, bgpManager, fibManager, rd, externalIp + "/32", nextHopIp,
117 label, LOG, RouteOrigin.STATIC);
119 List<Instruction> instructions = new ArrayList<>();
120 List<ActionInfo> actionsInfos = new ArrayList<>();
121 actionsInfos.add(new ActionInfo(ActionType.nx_resubmit, new String[] { Integer.toString(NatConstants.PDNAT_TABLE) }));
122 instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos).buildInstruction(0));
123 makeTunnelTableEntry(dpnId, label, instructions);
125 //Install custom FIB routes
126 List<Instruction> customInstructions = new ArrayList<>();
127 customInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.PDNAT_TABLE }).buildInstruction(0));
128 makeLFibTableEntry(dpnId, label, NatConstants.PDNAT_TABLE);
129 CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setInstruction(customInstructions)
130 .setIpAddress(externalIp + "/32").setServiceId(label).setInstruction(customInstructions).build();
131 //Future<RpcResult<java.lang.Void>> createFibEntry(CreateFibEntryInput input);
132 Future<RpcResult<Void>> future = fibService.createFibEntry(input);
133 return JdkFutureAdapters.listenInPoolThread(future);
135 String errMsg = String.format("Could not retrieve the label for prefix %s in VPN %s, %s", externalIp, vpnName, result.getErrors());
137 return Futures.immediateFailedFuture(new RuntimeException(errMsg));
142 Futures.addCallback(future, new FutureCallback<RpcResult<Void>>() {
145 public void onFailure(Throwable error) {
146 LOG.error("Error in generate label or fib install process", error);
150 public void onSuccess(RpcResult<Void> result) {
151 if(result.isSuccessful()) {
152 LOG.info("Successfully installed custom FIB routes for prefix {}", externalIp);
154 LOG.error("Error in rpc call to create custom Fib entries for prefix {} in DPN {}, {}", externalIp, dpnId, result.getErrors());
161 public void onRemoveFloatingIp(final BigInteger dpnId, String routerId, Uuid networkId, final String externalIp,
162 String internalIp, final long label) {
163 final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG);
164 if(vpnName == null) {
165 LOG.info("No VPN associated with ext nw {} to handle remove floating ip configuration {} in router {}",
166 networkId, externalIp, routerId);
169 //Remove Prefix from BGP
170 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
171 NatUtil.removePrefixFromBGP(dataBroker, bgpManager, fibManager, rd, externalIp + "/32", LOG);
173 //Remove custom FIB routes
174 //Future<RpcResult<java.lang.Void>> removeFibEntry(RemoveFibEntryInput input);
175 RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalIp + "/32").setServiceId(label).build();
176 Future<RpcResult<Void>> future = fibService.removeFibEntry(input);
178 ListenableFuture<RpcResult<Void>> labelFuture = Futures.transform(JdkFutureAdapters.listenInPoolThread(future), new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
181 public ListenableFuture<RpcResult<Void>> apply(RpcResult<Void> result) throws Exception {
183 if(result.isSuccessful()) {
184 removeTunnelTableEntry(dpnId, label);
185 removeLFibTableEntry(dpnId, label);
186 RemoveVpnLabelInput labelInput = new RemoveVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
187 Future<RpcResult<Void>> labelFuture = vpnService.removeVpnLabel(labelInput);
188 return JdkFutureAdapters.listenInPoolThread(labelFuture);
190 String errMsg = String.format("RPC call to remove custom FIB entries on dpn %s for prefix %s Failed - %s", dpnId, externalIp, result.getErrors());
192 return Futures.immediateFailedFuture(new RuntimeException(errMsg));
197 Futures.addCallback(labelFuture, new FutureCallback<RpcResult<Void>>() {
200 public void onFailure(Throwable error) {
201 LOG.error("Error in removing the label or custom fib entries", error);
205 public void onSuccess(RpcResult<Void> result) {
206 if(result.isSuccessful()) {
207 LOG.debug("Successfully removed the label for the prefix {} from VPN {}", externalIp, vpnName);
209 LOG.error("Error in removing the label for prefix {} from VPN {}, {}", externalIp, vpnName, result.getErrors());
215 void cleanupFibEntries(final BigInteger dpnId, final String vpnName, final String externalIp, final long label ) {
216 //Remove Prefix from BGP
217 String rd = NatUtil.getVpnRd(dataBroker, vpnName);
218 NatUtil.removePrefixFromBGP(dataBroker, bgpManager, fibManager, rd, externalIp + "/32", LOG);
220 //Remove custom FIB routes
222 //Future<RpcResult<java.lang.Void>> removeFibEntry(RemoveFibEntryInput input);
223 RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalIp + "/32").setServiceId(label).build();
224 Future<RpcResult<Void>> future = fibService.removeFibEntry(input);
226 ListenableFuture<RpcResult<Void>> labelFuture = Futures.transform(JdkFutureAdapters.listenInPoolThread(future),
227 new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
230 public ListenableFuture<RpcResult<Void>> apply(RpcResult<Void> result) throws Exception {
232 if(result.isSuccessful()) {
233 removeTunnelTableEntry(dpnId, label);
234 removeLFibTableEntry(dpnId, label);
235 RemoveVpnLabelInput labelInput = new RemoveVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
236 Future<RpcResult<Void>> labelFuture = vpnService.removeVpnLabel(labelInput);
237 return JdkFutureAdapters.listenInPoolThread(labelFuture);
239 String errMsg = String.format("RPC call to remove custom FIB entries on dpn %s for prefix %s Failed - %s", dpnId, externalIp, result.getErrors());
241 return Futures.immediateFailedFuture(new RuntimeException(errMsg));
246 Futures.addCallback(labelFuture, new FutureCallback<RpcResult<Void>>() {
249 public void onFailure(Throwable error) {
250 LOG.error("Error in removing the label or custom fib entries", error);
254 public void onSuccess(RpcResult<Void> result) {
255 if(result.isSuccessful()) {
256 LOG.debug("Successfully removed the label for the prefix {} from VPN {}", externalIp, vpnName);
258 LOG.error("Error in removing the label for prefix {} from VPN {}, {}", externalIp, vpnName, result.getErrors());
264 private String getFlowRef(BigInteger dpnId, short tableId, long id, String ipAddress) {
265 return new StringBuilder(64).append(FLOWID_PREFIX).append(dpnId).append(NwConstants.FLOWID_SEPARATOR)
266 .append(tableId).append(NwConstants.FLOWID_SEPARATOR)
267 .append(id).append(NwConstants.FLOWID_SEPARATOR).append(ipAddress).toString();
270 private void removeTunnelTableEntry(BigInteger dpnId, long serviceId) {
271 LOG.info("remove terminatingServiceActions called with DpnId = {} and label = {}", dpnId , serviceId);
272 List<MatchInfo> mkMatches = new ArrayList<>();
274 mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)}));
275 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
276 getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""),
277 5, String.format("%s:%d","TST Flow Entry ",serviceId), 0, 0,
278 COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)), mkMatches, null);
279 mdsalManager.removeFlow(dpnId, flowEntity);
280 LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully {}",dpnId, serviceId);
283 private void makeTunnelTableEntry(BigInteger dpnId, long serviceId, List<Instruction> customInstructions) {
284 List<MatchInfo> mkMatches = new ArrayList<>();
286 LOG.info("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}", dpnId , serviceId);
288 mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)}));
290 Flow terminatingServiceTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
291 getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""), 5, String.format("%s:%d","TST Flow Entry ",serviceId),
292 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)),mkMatches, customInstructions);
294 mdsalManager.installFlow(dpnId, terminatingServiceTableFlowEntity);
297 private void makeLFibTableEntry(BigInteger dpId, long serviceId, long tableId) {
298 List<MatchInfo> matches = new ArrayList<>();
299 matches.add(new MatchInfo(MatchFieldType.eth_type,
300 new long[] { 0x8847L }));
301 matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
303 List<Instruction> instructions = new ArrayList<>();
304 List<ActionInfo> actionsInfos = new ArrayList<>();
305 actionsInfos.add(new ActionInfo(ActionType.pop_mpls, new String[]{}));
306 Instruction writeInstruction = new InstructionInfo(InstructionType.apply_actions, actionsInfos).buildInstruction(0);
307 instructions.add(writeInstruction);
308 instructions.add(new InstructionInfo(InstructionType.goto_table, new long[]{tableId}).buildInstruction(1));
310 // Install the flow entry in L3_LFIB_TABLE
311 String flowRef = getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, serviceId, "");
313 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef,
315 COOKIE_VM_LFIB_TABLE, matches, instructions);
317 mdsalManager.installFlow(dpId, flowEntity);
319 LOG.debug("LFIB Entry for dpID {} : label : {} modified successfully {}",dpId, serviceId );
322 private void removeLFibTableEntry(BigInteger dpnId, long serviceId) {
323 List<MatchInfo> matches = new ArrayList<>();
324 matches.add(new MatchInfo(MatchFieldType.eth_type,
325 new long[] { 0x8847L }));
326 matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
328 String flowRef = getFlowRef(dpnId, NwConstants.L3_LFIB_TABLE, serviceId, "");
330 LOG.debug("removing LFib entry with flow ref {}", flowRef);
332 Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef,
334 COOKIE_VM_LFIB_TABLE, matches, null);
336 mdsalManager.removeFlow(dpnId, flowEntity);
338 LOG.debug("LFIB Entry for dpID : {} label : {} removed successfully {}",dpnId, serviceId);