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