Avoid comparing objects using ==
[netvirt.git] / natservice / impl / src / main / java / org / opendaylight / netvirt / natservice / internal / Ipv6ForwardingService.java
1 /*
2  * Copyright (c) 2018 Red Hat, Inc. 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 static org.opendaylight.netvirt.natservice.internal.AbstractSnatService.LOAD_END;
11 import static org.opendaylight.netvirt.natservice.internal.AbstractSnatService.LOAD_START;
12 import static org.opendaylight.netvirt.natservice.internal.NatUtil.getGroupIdKey;
13
14 import java.math.BigInteger;
15 import java.util.ArrayList;
16 import java.util.List;
17 import java.util.Objects;
18 import java.util.concurrent.ExecutionException;
19 import java.util.concurrent.Future;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.genius.infra.Datastore.Configuration;
22 import org.opendaylight.genius.infra.TypedReadWriteTransaction;
23 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
24 import org.opendaylight.genius.mdsalutil.ActionInfo;
25 import org.opendaylight.genius.mdsalutil.BucketInfo;
26 import org.opendaylight.genius.mdsalutil.GroupEntity;
27 import org.opendaylight.genius.mdsalutil.InstructionInfo;
28 import org.opendaylight.genius.mdsalutil.MDSALUtil;
29 import org.opendaylight.genius.mdsalutil.MatchInfo;
30 import org.opendaylight.genius.mdsalutil.MatchInfoBase;
31 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
32 import org.opendaylight.genius.mdsalutil.NWUtil;
33 import org.opendaylight.genius.mdsalutil.NwConstants;
34 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
35 import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadInPort;
36 import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadMetadata;
37 import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
38 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
39 import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
40 import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
41 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
42 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
43 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
44 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
45 import org.opendaylight.netvirt.natservice.api.SnatServiceListener;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.routers.ExternalIps;
55 import org.opendaylight.yangtools.yang.common.RpcResult;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59 public class Ipv6ForwardingService implements SnatServiceListener {
60     private static final Logger LOG = LoggerFactory.getLogger(Ipv6ForwardingService.class);
61
62     protected final DataBroker dataBroker;
63     protected final IMdsalApiManager mdsalManager;
64     protected final IdManagerService idManager;
65     protected final NAPTSwitchSelector naptSwitchSelector;
66     protected final ItmRpcService itmManager;
67     protected final OdlInterfaceRpcService odlInterfaceRpcService;
68     protected final IInterfaceManager interfaceManager;
69     protected final Ipv6SubnetFlowProgrammer ipv6SubnetFlowProgrammer;
70
71     public Ipv6ForwardingService(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
72                                   final ItmRpcService itmManager,
73                                   final OdlInterfaceRpcService odlInterfaceRpcService,
74                                   final IdManagerService idManager,
75                                   final NAPTSwitchSelector naptSwitchSelector,
76                                   final IInterfaceManager interfaceManager,
77                                   final Ipv6SubnetFlowProgrammer ipv6SubnetFlowProgrammer) {
78         this.dataBroker = dataBroker;
79         this.mdsalManager = mdsalManager;
80         this.itmManager = itmManager;
81         this.odlInterfaceRpcService = odlInterfaceRpcService;
82         this.idManager = idManager;
83         this.naptSwitchSelector = naptSwitchSelector;
84         this.interfaceManager = interfaceManager;
85         this.ipv6SubnetFlowProgrammer = ipv6SubnetFlowProgrammer;
86     }
87
88     public void init() {
89         LOG.info("Ipv6ForwardingService: {} init", getClass().getSimpleName());
90     }
91
92     @Override
93     public boolean addCentralizedRouterAllSwitch(TypedReadWriteTransaction<Configuration> confTx,
94             Routers routers, BigInteger primarySwitchId) {
95         String routerName = routers.getRouterName();
96         LOG.info("handleSnatAllSwitch : invoked for router {} with NAPTSwitch {} for {} flows",
97                 routerName, primarySwitchId, "installing");
98         List<BigInteger> switches = naptSwitchSelector.getDpnsForVpn(routerName);
99         /*
100          * Primary switch handled separately since the pseudo port created may
101          * not be present in the switch list on delete.
102          */
103         addCentralizedRouter(confTx, routers, primarySwitchId, primarySwitchId);
104         for (BigInteger dpnId : switches) {
105             if (!Objects.equals(primarySwitchId, dpnId)) {
106                 addCentralizedRouter(confTx, routers, primarySwitchId, dpnId);
107             }
108         }
109         return true;
110     }
111
112     @Override
113     public boolean addCentralizedRouter(TypedReadWriteTransaction<Configuration> confTx,
114             Routers routers, BigInteger primarySwitchId, BigInteger dpnId) {
115         Long routerId = NatUtil.getVpnId(dataBroker, routers.getRouterName());
116         BigInteger routerMetadata = MetaDataUtil.getVpnIdMetadata(routerId);
117
118         if (!dpnId.equals(primarySwitchId)) {
119             LOG.info("handleSnat (non-NAPTSwitch) : {} flows on switch {} for router {}",
120                     "Installing", dpnId, routers.getRouterName());
121             // Program default flow from FIB_TABLE(21) to PSNAT_TABLE(26) (egress direction)
122             addIpv6DefaultFibRoute(confTx, dpnId, routerId, routerMetadata);
123
124             // Currently we are only programming flows when ext-net has an IPv6Subnet
125             if (routerHasIpv6ExtSubnet(routers)) {
126                 // Program flows on non-NAPTSwitch to send N/S packets to the NAPTSwitch
127                 addIpv6PsNatMissEntryNonNaptSwitch(confTx, dpnId, routerId, routers.getRouterName(),
128                         primarySwitchId);
129             }
130         } else {
131             LOG.info("handleSnat (NAPTSwitch) : {} flows on switch {} for router {}",
132                     "Installing", dpnId, routers.getRouterName());
133             // Program default flow from FIB_TABLE(21) to PSNAT_TABLE(26) (egress direction)
134             addIpv6DefaultFibRoute(confTx, dpnId, routerId, routerMetadata);
135
136             // Program flows from PSNAT_TABLE(26) to OUTBOUND_NAPT_TABLE(46) (egress direction)
137             addIpv6SnatMissEntryForNaptSwitch(confTx, dpnId, routerId, routerMetadata);
138
139             // Program flows in INTERNAL_TUNNEL_TABLE(36) for packets coming from non-NAPTSwitch (egress direction)
140             addIpv6TerminatingServiceTblEntry(confTx, dpnId, routerId, routerMetadata);
141
142             // Program flows from NAPT_PFIB_TABLE(47) to FIB_TABLE(21) (ingress direction)
143             addIpv6NaptPfibInboundFlow(confTx, dpnId, routerId, routerMetadata);
144
145             // Now installing flows that use SubnetInfo
146             ipv6SubnetFlowProgrammer. addSubnetSpecificFlows(confTx, dpnId, routerId, routers, routerMetadata);
147         }
148         return true;
149     }
150
151     @Override
152     public boolean removeCentralizedRouterAllSwitch(TypedReadWriteTransaction<Configuration> confTx,
153             Routers routers, BigInteger primarySwitchId) throws ExecutionException, InterruptedException {
154         String routerName = routers.getRouterName();
155         LOG.info("handleSnatAllSwitch : invoked for router {} with NAPTSwitch {} for {} flows",
156                 routerName, primarySwitchId, "removing");
157         List<BigInteger> switches = naptSwitchSelector.getDpnsForVpn(routerName);
158         /*
159          * Primary switch handled separately since the pseudo port created may
160          * not be present in the switch list on delete.
161          */
162         removeCentralizedRouter(confTx, routers, primarySwitchId, primarySwitchId);
163         for (BigInteger dpnId : switches) {
164             if (!Objects.equals(primarySwitchId, dpnId)) {
165                 removeCentralizedRouter(confTx, routers, primarySwitchId, dpnId);
166             }
167         }
168         return true;
169     }
170
171     @Override
172     public boolean removeCentralizedRouter(TypedReadWriteTransaction<Configuration> confTx,
173             Routers routers, BigInteger primarySwitchId, BigInteger dpnId)
174                     throws ExecutionException, InterruptedException {
175         Long routerId = NatUtil.getVpnId(dataBroker, routers.getRouterName());
176         BigInteger routerMetadata = MetaDataUtil.getVpnIdMetadata(routerId);
177
178         if (!dpnId.equals(primarySwitchId)) {
179             LOG.info("handleSnat (non-NAPTSwitch) : {} flows on switch {} for router {}",
180                     "Removing", dpnId, routers.getRouterName());
181             // Program default flow from FIB_TABLE(21) to PSNAT_TABLE(26) (egress direction)
182             addIpv6DefaultFibRoute(confTx, dpnId, routerId, routerMetadata);
183
184             // Currently we are only programming flows when ext-net has an IPv6Subnet
185             if (routerHasIpv6ExtSubnet(routers)) {
186                 // Program flows on non-NAPTSwitch to send N/S packets to the NAPTSwitch
187                 addIpv6PsNatMissEntryNonNaptSwitch(confTx, dpnId, routerId, routers.getRouterName(),
188                         primarySwitchId);
189             }
190         } else {
191             LOG.info("handleSnat (NAPTSwitch) : {} flows on switch {} for router {}",
192                     "Removing", dpnId, routers.getRouterName());
193             // Program default flow from FIB_TABLE(21) to PSNAT_TABLE(26) (egress direction)
194             removeIpv6DefaultFibRoute(confTx, dpnId, routerId);
195
196             // Program flows from PSNAT_TABLE(26) to OUTBOUND_NAPT_TABLE(46) (egress direction)
197             removeIpv6SnatMissEntryForNaptSwitch(confTx, dpnId, routerId);
198
199             // Program flows in INTERNAL_TUNNEL_TABLE(36) for packets coming from non-NAPTSwitch (egress direction)
200             removeIpv6TerminatingServiceTblEntry(confTx, dpnId, routerId);
201
202             // Program flows from NAPT_PFIB_TABLE(47) to FIB_TABLE(21) (ingress direction)
203             removeIpv6NaptPfibInboundFlow(confTx, dpnId, routerId);
204
205             // Now installing flows that use SubnetInfo
206             ipv6SubnetFlowProgrammer.removeSubnetSpecificFlows(confTx, dpnId, routerId, routers);
207         }
208         return true;
209     }
210
211     @Override
212     public boolean handleRouterUpdate(TypedReadWriteTransaction<Configuration> confTx,
213             Routers origRouter, Routers updatedRouter) throws ExecutionException, InterruptedException {
214         LOG.info("handleRouterUpdate : originalRouter {}, updatedRouter {}", origRouter, updatedRouter);
215         String routerName = origRouter.getRouterName();
216         BigInteger primarySwitchId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
217         Long routerId = NatUtil.getVpnId(dataBroker, routerName);
218         BigInteger routerMetadata = MetaDataUtil.getVpnIdMetadata(routerId);
219
220         // If the external network is updated with an IPv6Subnet, program the necessary flows on non-NAPTSwitch
221         if (!routerHasIpv6ExtSubnet(origRouter) && routerHasIpv6ExtSubnet(updatedRouter)) {
222             List<BigInteger> switches = naptSwitchSelector.getDpnsForVpn(routerName);
223             for (BigInteger dpnId : switches) {
224                 if (!Objects.equals(primarySwitchId, dpnId)) {
225                     LOG.info("handleRouterUpdate (non-NAPTSwitch) : Installing flows on switch {} for router {}",
226                             dpnId, routerName);
227                     addIpv6PsNatMissEntryNonNaptSwitch(confTx, dpnId, routerId, routerName,
228                             primarySwitchId);
229                 }
230             }
231         }
232
233         ipv6SubnetFlowProgrammer.removeSubnetSpecificFlows(confTx, primarySwitchId, routerId, origRouter);
234         ipv6SubnetFlowProgrammer.addSubnetSpecificFlows(confTx, primarySwitchId, routerId, updatedRouter,
235                 routerMetadata);
236         return true;
237     }
238
239     @Override
240     public boolean addSnatAllSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
241             BigInteger primarySwitchId) {
242         return true;
243     }
244
245     @Override
246     public boolean addSnat(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
247             BigInteger primarySwitchId, BigInteger dpnId) {
248         return true;
249     }
250
251     @Override
252     public boolean removeSnatAllSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
253             BigInteger primarySwitchId)  throws ExecutionException, InterruptedException {
254         return true;
255     }
256
257     @Override
258     public boolean removeSnat(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
259             BigInteger primarySwitchId, BigInteger dpnId) throws ExecutionException, InterruptedException {
260         return true;
261     }
262
263
264     protected void addIpv6DefaultFibRoute(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
265             Long routerId, BigInteger routerMetadata) {
266         LOG.debug("installIpv6DefaultFibRoute : Installing default FIB route to PSNAT_TABLE on {}", dpnId);
267         List<MatchInfo> matches = new ArrayList<>();
268         matches.add(MatchEthernetType.IPV6);
269         matches.add(new MatchMetadata(routerMetadata, MetaDataUtil.METADATA_MASK_VRFID));
270
271         List<InstructionInfo> instructions = new ArrayList<>();
272         instructions.add(new InstructionGotoTable(NwConstants.PSNAT_TABLE));
273
274         String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.L3_FIB_TABLE, routerId);
275         flowRef += ".Outbound";
276         NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.L3_FIB_TABLE, flowRef,
277                 NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef,
278                 NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
279     }
280
281     protected void removeIpv6DefaultFibRoute(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
282             Long routerId) throws ExecutionException, InterruptedException {
283         LOG.debug("installIpv6DefaultFibRoute : Installing default FIB route to PSNAT_TABLE on {}", dpnId);
284         String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.L3_FIB_TABLE, routerId);
285         flowRef += ".Outbound";
286         NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.L3_FIB_TABLE, flowRef);
287     }
288
289     protected void addIpv6PsNatMissEntryNonNaptSwitch(TypedReadWriteTransaction<Configuration> confTx,
290             BigInteger dpnId, Long routerId, String routerName, BigInteger primarySwitchId) {
291         LOG.debug("installIpv6PsNatMissEntryNonNaptSwitch : On Non-Napt Switch, installing SNAT miss entry in"
292                 + " switch {} for router {}", dpnId, routerName);
293         List<ActionInfo> listActionInfoPrimary = new ArrayList<>();
294         List<BucketInfo> listBucketInfo = new ArrayList<>();
295
296         String ifNamePrimary = NatUtil.getTunnelInterfaceName(dpnId, primarySwitchId, itmManager);
297         if (ifNamePrimary != null) {
298             LOG.debug("installIpv6PsNatMissEntryNonNaptSwitch : On Non-Napt Switch, Primary Tunnel interface is {}",
299                     ifNamePrimary);
300             listActionInfoPrimary = NatUtil.getEgressActionsForInterface(odlInterfaceRpcService, itmManager,
301                     interfaceManager, ifNamePrimary, routerId, true);
302         } else {
303             LOG.warn("installIpv6PsNatMissEntryNonNaptSwitch: could not get tunnelInterface for {} on Switch {}",
304                     primarySwitchId, dpnId);
305         }
306
307         BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
308         listBucketInfo.add(0, bucketPrimary);
309
310         LOG.debug("installIpv6PsNatMissEntryNonNaptSwitch : installSnatMissEntry called for dpnId {} with"
311                 + " primaryBucket {} ", dpnId, listBucketInfo.get(0));
312
313         long groupId = createGroupIdForIpv6Router(getGroupIdKey(routerName + "IPv6"));
314         GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName, GroupTypes.GroupAll,
315                 listBucketInfo);
316         LOG.debug("installing the PSNAT to NAPTSwitch GroupEntity:{} with GroupId: {}", groupEntity, groupId);
317         mdsalManager.addGroup(confTx, groupEntity);
318         List<MatchInfo> matches = new ArrayList<>();
319         matches.add(MatchEthernetType.IPV6);
320         matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(routerId), MetaDataUtil.METADATA_MASK_VRFID));
321
322         List<ActionInfo> actionsInfo = new ArrayList<>();
323         actionsInfo.add(new ActionSetFieldTunnelId(BigInteger.valueOf(routerId)));
324         actionsInfo.add(new ActionGroup(groupId));
325         List<InstructionInfo> instructions = new ArrayList<>();
326         instructions.add(new InstructionApplyActions(actionsInfo));
327
328         String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.PSNAT_TABLE, routerId);
329         NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.PSNAT_TABLE, flowRef,
330                 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef,
331                 NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
332     }
333
334     protected void removeIpv6PsNatMissEntryNonNaptSwitch(TypedReadWriteTransaction<Configuration> confTx,
335             BigInteger dpnId, Long routerId, String routerName, BigInteger primarySwitchId)
336                     throws ExecutionException, InterruptedException {
337         LOG.debug("installIpv6PsNatMissEntryNonNaptSwitch : On Non-Napt Switch, installing SNAT miss entry in"
338                 + " switch {} for router {}", dpnId, routerName);
339         List<ActionInfo> listActionInfoPrimary = new ArrayList<>();
340         List<BucketInfo> listBucketInfo = new ArrayList<>();
341
342         String ifNamePrimary = NatUtil.getTunnelInterfaceName(dpnId, primarySwitchId, itmManager);
343         if (ifNamePrimary != null) {
344             LOG.debug("installIpv6PsNatMissEntryNonNaptSwitch : On Non-Napt Switch, Primary Tunnel interface is {}",
345                     ifNamePrimary);
346             listActionInfoPrimary = NatUtil.getEgressActionsForInterface(odlInterfaceRpcService, itmManager,
347                     interfaceManager, ifNamePrimary, routerId, true);
348         } else {
349             LOG.warn("installIpv6PsNatMissEntryNonNaptSwitch: could not get tunnelInterface for {} on Switch {}",
350                     primarySwitchId, dpnId);
351         }
352
353         BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
354         listBucketInfo.add(0, bucketPrimary);
355
356         LOG.debug("installIpv6PsNatMissEntryNonNaptSwitch : installSnatMissEntry called for dpnId {} with"
357                 + " primaryBucket {} ", dpnId, listBucketInfo.get(0));
358
359         long groupId = createGroupIdForIpv6Router(getGroupIdKey(routerName + "IPv6"));
360         GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName, GroupTypes.GroupAll,
361                 listBucketInfo);
362         LOG.debug("removing the PSNAT to NAPTSwitch GroupEntity:{} with GroupId: {}", groupEntity, groupId);
363         mdsalManager.removeGroup(confTx, groupEntity);
364
365         String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.PSNAT_TABLE, routerId);
366         NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.PSNAT_TABLE, flowRef);
367     }
368
369
370     protected void addIpv6SnatMissEntryForNaptSwitch(TypedReadWriteTransaction<Configuration> confTx,
371             BigInteger dpnId, Long routerId, BigInteger routerMetadata) {
372         LOG.debug("installIpv6SnatMissEntryForNaptSwitch {} called for routerId {}", dpnId, routerId);
373         List<MatchInfo> matches = new ArrayList<>();
374         matches.add(MatchEthernetType.IPV6);
375         matches.add(new MatchMetadata(routerMetadata, MetaDataUtil.METADATA_MASK_VRFID));
376
377         List<InstructionInfo> instructions = new ArrayList<>();
378         instructions.add(new InstructionGotoTable(NwConstants.OUTBOUND_NAPT_TABLE));
379
380         String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.PSNAT_TABLE, routerId);
381         flowRef += ".Outbound";
382         NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.PSNAT_TABLE, flowRef,
383                 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef,
384                 NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
385     }
386
387     protected void removeIpv6SnatMissEntryForNaptSwitch(TypedReadWriteTransaction<Configuration> confTx,
388             BigInteger dpnId, Long routerId)
389                     throws ExecutionException, InterruptedException {
390         LOG.debug("installIpv6SnatMissEntryForNaptSwitch {} called for routerId {}", dpnId, routerId);
391         String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.PSNAT_TABLE, routerId);
392         flowRef += ".Outbound";
393         NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.PSNAT_TABLE, flowRef);
394     }
395
396     protected void addIpv6TerminatingServiceTblEntry(TypedReadWriteTransaction<Configuration> confTx,
397             BigInteger dpnId, Long  routerId, BigInteger routerMetadata) {
398         LOG.debug("installIpv6TerminatingServiceTblEntry : creating entry for Terminating Service Table "
399                 + "for switch {}, routerId {}", dpnId, routerId);
400         List<MatchInfo> matches = new ArrayList<>();
401         matches.add(MatchEthernetType.IPV6);
402         matches.add(new MatchTunnelId(BigInteger.valueOf(routerId)));
403
404         List<ActionInfo> actionsInfos = new ArrayList<>();
405         ActionNxLoadMetadata actionLoadMeta = new ActionNxLoadMetadata(routerMetadata, LOAD_START, LOAD_END);
406         actionsInfos.add(actionLoadMeta);
407         actionsInfos.add(new ActionNxResubmit(NwConstants.OUTBOUND_NAPT_TABLE));
408         List<InstructionInfo> instructions = new ArrayList<>();
409         instructions.add(new InstructionApplyActions(actionsInfos));
410
411         String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId);
412         flowRef += ".Outbound";
413         NatUtil.addFlow(confTx, mdsalManager, dpnId,  NwConstants.INTERNAL_TUNNEL_TABLE, flowRef,
414                 NatConstants.DEFAULT_TS_FLOW_PRIORITY, flowRef,
415                 NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
416
417     }
418
419     protected void removeIpv6TerminatingServiceTblEntry(TypedReadWriteTransaction<Configuration> confTx,
420             BigInteger dpnId, Long  routerId) throws ExecutionException, InterruptedException {
421         LOG.debug("installIpv6TerminatingServiceTblEntry : creating entry for Terminating Service Table "
422                 + "for switch {}, routerId {}", dpnId, routerId);
423         String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId);
424         flowRef += ".Outbound";
425         NatUtil.removeFlow(confTx, mdsalManager, dpnId,  NwConstants.INTERNAL_TUNNEL_TABLE, flowRef);
426
427     }
428
429     protected void addIpv6NaptPfibInboundFlow(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
430             long routerId, BigInteger routerMetadata) {
431         LOG.debug("installIpv6NaptPfibInboundFlow : called for dpnId {} and routerId {} ", dpnId, routerId);
432         List<MatchInfoBase> matches = new ArrayList<>();
433         matches.add(MatchEthernetType.IPV6);
434         matches.add(new MatchMetadata(routerMetadata, MetaDataUtil.METADATA_MASK_VRFID));
435
436         ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
437         ArrayList<InstructionInfo> instructionInfo = new ArrayList<>();
438         listActionInfo.add(new ActionNxLoadInPort(BigInteger.ZERO));
439         listActionInfo.add(new ActionNxResubmit(NwConstants.L3_FIB_TABLE));
440         instructionInfo.add(new InstructionApplyActions(listActionInfo));
441
442         String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.NAPT_PFIB_TABLE, routerId);
443         flowRef += ".Inbound";
444         NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.NAPT_PFIB_TABLE, flowRef,
445                 NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY,
446                 flowRef, NwConstants.COOKIE_SNAT_TABLE,
447                 matches, instructionInfo);
448     }
449
450     protected void removeIpv6NaptPfibInboundFlow(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
451             long routerId)
452                     throws ExecutionException, InterruptedException {
453         LOG.debug("installIpv6NaptPfibInboundFlow : called for dpnId {} and routerId {} ", dpnId, routerId);
454         String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.NAPT_PFIB_TABLE, routerId);
455         flowRef += ".Inbound";
456         NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.NAPT_PFIB_TABLE, flowRef);
457     }
458
459     protected long createGroupIdForIpv6Router(String groupIdKey) {
460         AllocateIdInput getIdInput = new AllocateIdInputBuilder()
461                 .setPoolName(NatConstants.SNAT_IDPOOL_NAME).setIdKey(groupIdKey)
462                 .build();
463         try {
464             Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
465             RpcResult<AllocateIdOutput> rpcResult = result.get();
466             return rpcResult.getResult().getIdValue();
467         } catch (NullPointerException | InterruptedException | ExecutionException e) {
468             LOG.error("createGroupIdForIPv6Router: Exception while creating group with key : {}", groupIdKey, e);
469         }
470         return 0;
471     }
472
473     protected boolean routerHasIpv6ExtSubnet(Routers routers) {
474         for (ExternalIps externalIp : routers.getExternalIps()) {
475             if (!NWUtil.isIpv4Address(externalIp.getIpAddress())) {
476                 LOG.debug("router {}, has an external IPv6 subnet {}",
477                         routers.getRouterName(), externalIp.getIpAddress());
478                 return true;
479             }
480         }
481         LOG.debug("router {}, does not have an external IPv6 subnet", routers.getRouterName());
482         return false;
483     }
484 }