Merge "L2 gateway functionality related fixes."
[netvirt.git] / vpnservice / natservice / natservice-impl / src / main / java / org / opendaylight / netvirt / natservice / internal / VpnFloatingIpHandler.java
1 /*
2  * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.netvirt.natservice.internal;
9
10 import java.math.BigInteger;
11 import java.util.ArrayList;
12 import java.util.concurrent.Future;
13 import java.util.List;
14
15 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
16 import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
17 import org.opendaylight.genius.mdsalutil.ActionInfo;
18 import org.opendaylight.genius.mdsalutil.ActionType;
19 import org.opendaylight.genius.mdsalutil.InstructionInfo;
20 import org.opendaylight.genius.mdsalutil.InstructionType;
21 import org.opendaylight.genius.mdsalutil.MDSALUtil;
22 import org.opendaylight.genius.mdsalutil.MatchFieldType;
23 import org.opendaylight.genius.mdsalutil.MatchInfo;
24 import org.opendaylight.genius.mdsalutil.NwConstants;
25 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInput;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.CreateFibEntryInputBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInput;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.RemoveFibEntryInputBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.VpnRpcService;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelInput;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelInputBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.GenerateVpnLabelOutput;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveVpnLabelInput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.RemoveVpnLabelInputBuilder;
40 import org.opendaylight.yangtools.yang.common.RpcResult;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 import com.google.common.util.concurrent.AsyncFunction;
45 import com.google.common.util.concurrent.FutureCallback;
46 import com.google.common.util.concurrent.Futures;
47 import com.google.common.util.concurrent.JdkFutureAdapters;
48 import com.google.common.util.concurrent.ListenableFuture;
49
50 public class VpnFloatingIpHandler implements FloatingIPHandler {
51     private static final Logger LOG = LoggerFactory.getLogger(VpnFloatingIpHandler.class);
52     private VpnRpcService vpnService;
53     private FibRpcService fibService;
54     private IBgpManager bgpManager;
55     private DataBroker dataBroker;
56     private IMdsalApiManager mdsalManager;
57     private FloatingIPListener listener;
58
59     static final BigInteger COOKIE_TUNNEL = new BigInteger("9000000", 16);
60     static final String FLOWID_PREFIX = "NAT.";
61     static final BigInteger COOKIE_VM_LFIB_TABLE = new BigInteger("8000002", 16);
62
63     public VpnFloatingIpHandler(VpnRpcService vpnService, IBgpManager bgpManager, FibRpcService fibService) {
64         this.vpnService = vpnService;
65         this.fibService = fibService;
66         this.bgpManager = bgpManager;
67     }
68
69     void setListener(FloatingIPListener listener) {
70         this.listener = listener;
71     }
72
73     void setBroker(DataBroker broker) {
74         dataBroker = broker;
75     }
76
77     void setMdsalManager(IMdsalApiManager mdsalManager) {
78         this.mdsalManager = mdsalManager;
79     }
80
81     @Override
82     public void onAddFloatingIp(final BigInteger dpnId, final String routerId,
83                                 Uuid networkId, final String interfaceName, final String externalIp, final String internalIp) {
84         final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG);
85         if(vpnName == null) {
86             LOG.info("No VPN associated with ext nw {} to handle add floating ip configuration {} in router {}",
87                     networkId, externalIp, routerId);
88             return;
89         }
90
91         GenerateVpnLabelInput labelInput = new GenerateVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
92         Future<RpcResult<GenerateVpnLabelOutput>> labelFuture = vpnService.generateVpnLabel(labelInput);
93
94         ListenableFuture<RpcResult<Void>> future = Futures.transform(JdkFutureAdapters.listenInPoolThread(labelFuture), new AsyncFunction<RpcResult<GenerateVpnLabelOutput>, RpcResult<Void>>() {
95
96             @Override
97             public ListenableFuture<RpcResult<Void>> apply(RpcResult<GenerateVpnLabelOutput> result) throws Exception {
98                 if(result.isSuccessful()) {
99                     GenerateVpnLabelOutput output = result.getResult();
100                     long label = output.getLabel();
101                     LOG.debug("Generated label {} for prefix {}", label, externalIp);
102                     listener.updateOperationalDS(routerId, interfaceName, (int)label, internalIp, externalIp);
103
104                     //Inform BGP
105                     String rd = NatUtil.getVpnRd(dataBroker, vpnName);
106                     String nextHopIp = NatUtil.getEndpointIpAddressForDPN(dataBroker, dpnId);
107                     LOG.debug("Nexthop ip for prefix {} is {}", externalIp, nextHopIp);
108                     NatUtil.addPrefixToBGP(bgpManager, rd, externalIp + "/32", nextHopIp, label, LOG);
109
110                     List<Instruction> instructions = new ArrayList<Instruction>();
111                     List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
112                     actionsInfos.add(new ActionInfo(ActionType.nx_resubmit, new String[] { Integer.toString(NatConstants.PDNAT_TABLE) }));
113                     instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos).buildInstruction(0));
114                     makeTunnelTableEntry(dpnId, label, instructions);
115
116                     //Install custom FIB routes
117                     List<Instruction> customInstructions = new ArrayList<>();
118                     customInstructions.add(new InstructionInfo(InstructionType.goto_table, new long[] { NatConstants.PDNAT_TABLE }).buildInstruction(0));
119                     makeLFibTableEntry(dpnId, label, NatConstants.PDNAT_TABLE);
120                     CreateFibEntryInput input = new CreateFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setInstruction(customInstructions)
121                             .setIpAddress(externalIp + "/32").setServiceId(label).setInstruction(customInstructions).build();
122                     //Future<RpcResult<java.lang.Void>> createFibEntry(CreateFibEntryInput input);
123                     Future<RpcResult<Void>> future = fibService.createFibEntry(input);
124                     return JdkFutureAdapters.listenInPoolThread(future);
125                 } else {
126                     String errMsg = String.format("Could not retrieve the label for prefix %s in VPN %s, %s", externalIp, vpnName, result.getErrors());
127                     LOG.error(errMsg);
128                     return Futures.immediateFailedFuture(new RuntimeException(errMsg));
129                 }
130             }
131         });
132
133         Futures.addCallback(future, new FutureCallback<RpcResult<Void>>() {
134
135             @Override
136             public void onFailure(Throwable error) {
137                 LOG.error("Error in generate label or fib install process", error);
138             }
139
140             @Override
141             public void onSuccess(RpcResult<Void> result) {
142                 if(result.isSuccessful()) {
143                     LOG.info("Successfully installed custom FIB routes for prefix {}", externalIp);
144                 } else {
145                     LOG.error("Error in rpc call to create custom Fib entries for prefix {} in DPN {}, {}", externalIp, dpnId, result.getErrors());
146                 }
147             }
148         });
149     }
150
151     @Override
152     public void onRemoveFloatingIp(final BigInteger dpnId, String routerId, Uuid networkId, final String externalIp,
153                                    String internalIp, final long label) {
154         final String vpnName = NatUtil.getAssociatedVPN(dataBroker, networkId, LOG);
155         if(vpnName == null) {
156             LOG.info("No VPN associated with ext nw {} to handle remove floating ip configuration {} in router {}",
157                     networkId, externalIp, routerId);
158             return;
159         }
160         //Remove Prefix from BGP
161         String rd = NatUtil.getVpnRd(dataBroker, vpnName);
162         removePrefixFromBGP(rd, externalIp + "/32");
163
164         //Remove custom FIB routes
165         //Future<RpcResult<java.lang.Void>> removeFibEntry(RemoveFibEntryInput input);
166         RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalIp + "/32").setServiceId(label).build();
167         Future<RpcResult<Void>> future = fibService.removeFibEntry(input);
168
169         ListenableFuture<RpcResult<Void>> labelFuture = Futures.transform(JdkFutureAdapters.listenInPoolThread(future), new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
170
171             @Override
172             public ListenableFuture<RpcResult<Void>> apply(RpcResult<Void> result) throws Exception {
173                 //Release label
174                 if(result.isSuccessful()) {
175                     removeTunnelTableEntry(dpnId, label);
176                     removeLFibTableEntry(dpnId, label);
177                     RemoveVpnLabelInput labelInput = new RemoveVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
178                     Future<RpcResult<Void>> labelFuture = vpnService.removeVpnLabel(labelInput);
179                     return JdkFutureAdapters.listenInPoolThread(labelFuture);
180                 } else {
181                     String errMsg = String.format("RPC call to remove custom FIB entries on dpn %s for prefix %s Failed - %s", dpnId, externalIp, result.getErrors());
182                     LOG.error(errMsg);
183                     return Futures.immediateFailedFuture(new RuntimeException(errMsg));
184                 }
185             }
186         });
187
188         Futures.addCallback(labelFuture, new FutureCallback<RpcResult<Void>>() {
189
190             @Override
191             public void onFailure(Throwable error) {
192                 LOG.error("Error in removing the label or custom fib entries", error);
193             }
194
195             @Override
196             public void onSuccess(RpcResult<Void> result) {
197                 if(result.isSuccessful()) {
198                     LOG.debug("Successfully removed the label for the prefix {} from VPN {}", externalIp, vpnName);
199                 } else {
200                     LOG.error("Error in removing the label for prefix {} from VPN {}, {}", externalIp, vpnName, result.getErrors());
201                 }
202             }
203         });
204     }
205
206     private void removePrefixFromBGP(String rd, String prefix) {
207         try {
208             LOG.info("VPN REMOVE: Removing Fib Entry rd {} prefix {}", rd, prefix);
209             bgpManager.deletePrefix(rd, prefix);
210             LOG.info("VPN REMOVE: Removed Fib Entry rd {} prefix {}", rd, prefix);
211         } catch(Exception e) {
212             LOG.error("Delete prefix failed", e);
213         }
214     }
215
216     void cleanupFibEntries(final BigInteger dpnId, final String vpnName, final String externalIp, final long label ) {
217         //Remove Prefix from BGP
218         String rd = NatUtil.getVpnRd(dataBroker, vpnName);
219         removePrefixFromBGP(rd, externalIp + "/32");
220
221         //Remove custom FIB routes
222
223         //Future<RpcResult<java.lang.Void>> removeFibEntry(RemoveFibEntryInput input);
224         RemoveFibEntryInput input = new RemoveFibEntryInputBuilder().setVpnName(vpnName).setSourceDpid(dpnId).setIpAddress(externalIp + "/32").setServiceId(label).build();
225         Future<RpcResult<Void>> future = fibService.removeFibEntry(input);
226
227         ListenableFuture<RpcResult<Void>> labelFuture = Futures.transform(JdkFutureAdapters.listenInPoolThread(future), 
228             new AsyncFunction<RpcResult<Void>, RpcResult<Void>>() {
229
230             @Override
231             public ListenableFuture<RpcResult<Void>> apply(RpcResult<Void> result) throws Exception {
232                 //Release label
233                 if(result.isSuccessful()) {
234                     removeTunnelTableEntry(dpnId, label);
235                     removeLFibTableEntry(dpnId, label);
236                     RemoveVpnLabelInput labelInput = new RemoveVpnLabelInputBuilder().setVpnName(vpnName).setIpPrefix(externalIp).build();
237                     Future<RpcResult<Void>> labelFuture = vpnService.removeVpnLabel(labelInput);
238                     return JdkFutureAdapters.listenInPoolThread(labelFuture);
239                 } else {
240                     String errMsg = String.format("RPC call to remove custom FIB entries on dpn %s for prefix %s Failed - %s", dpnId, externalIp, result.getErrors());
241                     LOG.error(errMsg);
242                     return Futures.immediateFailedFuture(new RuntimeException(errMsg));
243                 }
244             }
245         });
246
247         Futures.addCallback(labelFuture, new FutureCallback<RpcResult<Void>>() {
248
249             @Override
250             public void onFailure(Throwable error) {
251                 LOG.error("Error in removing the label or custom fib entries", error);
252             }
253
254             @Override
255             public void onSuccess(RpcResult<Void> result) {
256                 if(result.isSuccessful()) {
257                     LOG.debug("Successfully removed the label for the prefix {} from VPN {}", externalIp, vpnName);
258                 } else {
259                     LOG.error("Error in removing the label for prefix {} from VPN {}, {}", externalIp, vpnName, result.getErrors());
260                 }
261             }
262         });
263     }
264
265     private String getFlowRef(BigInteger dpnId, short tableId, long id, String ipAddress) {
266         return new StringBuilder(64).append(FLOWID_PREFIX).append(dpnId).append(NwConstants.FLOWID_SEPARATOR)
267                 .append(tableId).append(NwConstants.FLOWID_SEPARATOR)
268                 .append(id).append(NwConstants.FLOWID_SEPARATOR).append(ipAddress).toString();
269     }
270
271     private void removeTunnelTableEntry(BigInteger dpnId, long serviceId) {
272         LOG.info("remove terminatingServiceActions called with DpnId = {} and label = {}", dpnId , serviceId);
273         List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
274         // Matching metadata
275         mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)}));
276         Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
277                 getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""),
278                 5, String.format("%s:%d","TST Flow Entry ",serviceId), 0, 0,
279                 COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)), mkMatches, null);
280         mdsalManager.removeFlow(dpnId, flowEntity);
281         LOG.debug("Terminating service Entry for dpID {} : label : {} removed successfully {}",dpnId, serviceId);
282     }
283
284     private void makeTunnelTableEntry(BigInteger dpnId, long serviceId, List<Instruction> customInstructions) {
285         List<MatchInfo> mkMatches = new ArrayList<MatchInfo>();
286
287         LOG.info("create terminatingServiceAction on DpnId = {} and serviceId = {} and actions = {}", dpnId , serviceId);
288
289         mkMatches.add(new MatchInfo(MatchFieldType.tunnel_id, new BigInteger[] {BigInteger.valueOf(serviceId)}));
290
291         Flow terminatingServiceTableFlowEntity = MDSALUtil.buildFlowNew(NwConstants.INTERNAL_TUNNEL_TABLE,
292                 getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, serviceId, ""), 5, String.format("%s:%d","TST Flow Entry ",serviceId),
293                 0, 0, COOKIE_TUNNEL.add(BigInteger.valueOf(serviceId)),mkMatches, customInstructions);
294
295         mdsalManager.installFlow(dpnId, terminatingServiceTableFlowEntity);
296     }
297
298     private void makeLFibTableEntry(BigInteger dpId, long serviceId, long tableId) {
299         List<MatchInfo> matches = new ArrayList<MatchInfo>();
300         matches.add(new MatchInfo(MatchFieldType.eth_type,
301                 new long[] { 0x8847L }));
302         matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
303
304         List<Instruction> instructions = new ArrayList<Instruction>();
305         List<ActionInfo> actionsInfos = new ArrayList<ActionInfo>();
306         actionsInfos.add(new ActionInfo(ActionType.pop_mpls, new String[]{}));
307         Instruction writeInstruction = new InstructionInfo(InstructionType.apply_actions, actionsInfos).buildInstruction(0);
308         instructions.add(writeInstruction);
309         instructions.add(new InstructionInfo(InstructionType.goto_table, new long[]{tableId}).buildInstruction(1));
310
311         // Install the flow entry in L3_LFIB_TABLE
312         String flowRef = getFlowRef(dpId, NwConstants.L3_LFIB_TABLE, serviceId, "");
313
314         Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef,
315                 10, flowRef, 0, 0,
316                 COOKIE_VM_LFIB_TABLE, matches, instructions);
317
318         mdsalManager.installFlow(dpId, flowEntity);
319
320         LOG.debug("LFIB Entry for dpID {} : label : {} modified successfully {}",dpId, serviceId );
321     }
322
323     private void removeLFibTableEntry(BigInteger dpnId, long serviceId) {
324         List<MatchInfo> matches = new ArrayList<MatchInfo>();
325         matches.add(new MatchInfo(MatchFieldType.eth_type,
326                                   new long[] { 0x8847L }));
327         matches.add(new MatchInfo(MatchFieldType.mpls_label, new String[]{Long.toString(serviceId)}));
328
329         String flowRef = getFlowRef(dpnId, NwConstants.L3_LFIB_TABLE, serviceId, "");
330
331         LOG.debug("removing LFib entry with flow ref {}", flowRef);
332
333         Flow flowEntity = MDSALUtil.buildFlowNew(NwConstants.L3_LFIB_TABLE, flowRef,
334                                                10, flowRef, 0, 0,
335                                                COOKIE_VM_LFIB_TABLE, matches, null);
336
337         mdsalManager.removeFlow(dpnId, flowEntity);
338
339         LOG.debug("LFIB Entry for dpID : {} label : {} removed successfully {}",dpnId, serviceId);
340     }
341
342 }