IPv6 CVR North-South Support 80/73180/8
authorSridhar Gaddam <sgaddam@redhat.com>
Mon, 18 Jun 2018 15:01:27 +0000 (20:31 +0530)
committerAswin Suryanarayanan <asuryana@redhat.com>
Sat, 5 Jan 2019 15:08:14 +0000 (15:08 +0000)
This patch programs the pipeline flows on the NAPT and non-NAPT
switch to support North-South traffic. Traffic from non-NAPT
Switch would be forwarded to the NAPT Switch from where its
sent to the external network.

In order to support the IPv6 N/S end-to-end use-case, GWMACResolution
has to happen and the necessary support would be added via a
separate patch.

Change-Id: I2fa0f57af9b063d6bd87c385ab7b1b4d1add5186
Signed-off-by: Sridhar Gaddam <sgaddam@redhat.com>
18 files changed:
natservice/api/src/main/java/org/opendaylight/netvirt/natservice/api/SnatServiceListener.java
natservice/api/src/main/java/org/opendaylight/netvirt/natservice/api/SnatServiceManager.java
natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/AbstractSnatService.java
natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/ConntrackBasedSnatService.java
natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/Ipv6ForwardingService.java [new file with mode: 0644]
natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/Ipv6SubnetFlowProgrammer.java [new file with mode: 0644]
natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/NatConstants.java
natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/NatUtil.java
natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/SnatExternalRoutersListener.java
natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/SnatServiceImplFactory.java
natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/SnatServiceManagerImpl.java
natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/VxlanGreConntrackBasedSnatService.java
neutronvpn/impl/pom.xml
neutronvpn/impl/src/main/java/org/opendaylight/netvirt/neutronvpn/NeutronSubnetGwMacResolver.java
neutronvpn/impl/src/main/resources/OSGI-INF/blueprint/neutronvpn.xml
vpnmanager/impl/src/main/java/org/opendaylight/netvirt/vpnmanager/VpnUtil.java
vpnmanager/impl/src/main/java/org/opendaylight/netvirt/vpnmanager/iplearn/AbstractIpLearnNotificationHandler.java
vpnmanager/impl/src/main/java/org/opendaylight/netvirt/vpnmanager/iplearn/LearntVpnVipToPortEventProcessor.java

index 7588c4ffab26f00e0c5a13051e0408726fa419db..87e2c1016f38e9c539dddfcbceb1e26a3a8ecbc2 100644 (file)
@@ -104,4 +104,12 @@ public interface SnatServiceListener {
     boolean removeCentralizedRouter(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
             BigInteger primarySwitchId, BigInteger dpnId) throws ExecutionException, InterruptedException;
 
+    /**
+     * Handles changes to external router.
+     * @param origRouter the Orignal router.
+     * @param updatedRouter the Updated router.
+     * @return returns success/failure.
+     */
+    boolean handleRouterUpdate(TypedReadWriteTransaction<Configuration> confTx, Routers origRouter,
+            Routers updatedRouter) throws ExecutionException, InterruptedException;
 }
index 8df218294c82c38c25acacef1b6ee24402808b26..e49b758b54ecee4f7f9a628cf81259f73121344c 100644 (file)
@@ -22,6 +22,7 @@ public interface SnatServiceManager {
         SNAT_ALL_SWITCH_DISBL,
         SNAT_ROUTER_ENBL,
         SNAT_ROUTER_DISBL,
+        SNAT_ROUTER_UPDATE,
         CNT_ROUTER_ALL_SWITCH_ENBL,
         CNT_ROUTER_ALL_SWITCH_DISBL,
         CNT_ROUTER_ENBL,
index e22adf220e2471c12dbe1fb24087516ff11f12be..a1f840655c302fc163649cb841663f5e762129d1 100644 (file)
@@ -18,7 +18,6 @@ import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
-import javax.annotation.Nullable;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
@@ -33,12 +32,10 @@ import org.opendaylight.genius.infra.TypedWriteTransaction;
 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
 import org.opendaylight.genius.mdsalutil.ActionInfo;
 import org.opendaylight.genius.mdsalutil.BucketInfo;
-import org.opendaylight.genius.mdsalutil.FlowEntity;
 import org.opendaylight.genius.mdsalutil.GroupEntity;
 import org.opendaylight.genius.mdsalutil.InstructionInfo;
 import org.opendaylight.genius.mdsalutil.MDSALUtil;
 import org.opendaylight.genius.mdsalutil.MatchInfo;
-import org.opendaylight.genius.mdsalutil.MatchInfoBase;
 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
 import org.opendaylight.genius.mdsalutil.NWUtil;
 import org.opendaylight.genius.mdsalutil.NwConstants;
@@ -67,12 +64,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeGre;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
@@ -285,6 +277,12 @@ public abstract class AbstractSnatService implements SnatServiceListener {
         return true;
     }
 
+    @Override
+    public boolean handleRouterUpdate(TypedReadWriteTransaction<Configuration> confTx,
+            Routers origRouter, Routers updatedRouter) throws ExecutionException, InterruptedException {
+        return true;
+    }
+
     protected void addCommonEntriesForNaptSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
             BigInteger dpnId) {
         String routerName = routers.getRouterName();
@@ -418,8 +416,9 @@ public abstract class AbstractSnatService implements SnatServiceListener {
 
         String flowRef = getFlowRef(dpnId, NwConstants.L3_FIB_TABLE, routerId);
         flowRef = flowRef + "inbound" + externalIp;
-        addFlow(confTx, dpnId, NwConstants.L3_FIB_TABLE, flowRef, NatConstants.SNAT_FIB_FLOW_PRIORITY, flowRef,
-                NwConstants.COOKIE_SNAT_TABLE, matches, instructionInfo);
+        NatUtil.addFlow(confTx, mdsalManager,dpnId, NwConstants.L3_FIB_TABLE, flowRef,
+                NatConstants.SNAT_FIB_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches,
+                instructionInfo);
         String rd = NatUtil.getVpnRd(dataBroker, subNetId);
         String nextHopIp = NatUtil.getEndpointIpAddressForDPN(dataBroker, dpnId);
         String ipPrefix = externalIp + "/32";
@@ -435,7 +434,7 @@ public abstract class AbstractSnatService implements SnatServiceListener {
             String externalIp, Long routerId, String subNetId) throws ExecutionException, InterruptedException {
         String flowRef = getFlowRef(dpnId, NwConstants.L3_FIB_TABLE, routerId);
         flowRef = flowRef + "inbound" + externalIp;
-        removeFlow(confTx, dpnId, NwConstants.L3_FIB_TABLE, flowRef);
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.L3_FIB_TABLE, flowRef);
         String rd = NatUtil.getVpnRd(dataBroker, subNetId);
         String ipPrefix = externalIp + "/32";
         fibManager.removeFibEntry(rd, ipPrefix, confTx);
@@ -459,8 +458,8 @@ public abstract class AbstractSnatService implements SnatServiceListener {
         List<InstructionInfo> instructions = new ArrayList<>();
         instructions.add(new InstructionApplyActions(actionsInfos));
         String flowRef = getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId);
-        addFlow(confTx, dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, flowRef, NatConstants.DEFAULT_TS_FLOW_PRIORITY,
-            flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, flowRef,
+                NatConstants.DEFAULT_TS_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
     }
 
     protected void removeTerminatingServiceTblEntry(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
@@ -469,14 +468,14 @@ public abstract class AbstractSnatService implements SnatServiceListener {
             + "for switch {}, routerId {}", dpnId, routerId);
 
         String flowRef = getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId);
-        removeFlow(confTx, dpnId,  NwConstants.INTERNAL_TUNNEL_TABLE, flowRef);
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId,  NwConstants.INTERNAL_TUNNEL_TABLE, flowRef);
     }
 
     protected void addSnatMissEntry(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
         Long routerId, String routerName, BigInteger primarySwitchId)  {
         LOG.debug("installSnatMissEntry : Installing SNAT miss entry in switch {}", dpnId);
         List<ActionInfo> listActionInfoPrimary = new ArrayList<>();
-        String ifNamePrimary = getTunnelInterfaceName(dpnId, primarySwitchId);
+        String ifNamePrimary = NatUtil.getTunnelInterfaceName(dpnId, primarySwitchId, itmManager);
         List<BucketInfo> listBucketInfo = new ArrayList<>();
         if (ifNamePrimary != null) {
             LOG.debug("installSnatMissEntry : On Non- Napt switch , Primary Tunnel interface is {}", ifNamePrimary);
@@ -524,8 +523,9 @@ public abstract class AbstractSnatService implements SnatServiceListener {
         List<InstructionInfo> instructions = new ArrayList<>();
         instructions.add(new InstructionApplyActions(actionsInfo));
         String flowRef = getFlowRef(dpnId, NwConstants.PSNAT_TABLE, routerId);
-        addFlow(confTx, dpnId, NwConstants.PSNAT_TABLE, flowRef,  NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef,
-            NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.PSNAT_TABLE, flowRef,
+                NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches,
+                instructions);
     }
 
     protected void removeSnatMissEntry(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
@@ -542,13 +542,13 @@ public abstract class AbstractSnatService implements SnatServiceListener {
             dpnId, routerName, groupId);
 
         String flowRef = getFlowRef(dpnId, NwConstants.PSNAT_TABLE, routerId);
-        removeFlow(confTx, dpnId, NwConstants.PSNAT_TABLE, flowRef);
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.PSNAT_TABLE, flowRef);
     }
 
     protected void addInboundTerminatingServiceTblEntry(TypedReadWriteTransaction<Configuration> confTx,
         BigInteger dpnId, Long routerId, long extSubnetId) {
 
-        //Install the tunnel table entry in NAPT switch for inbound traffic to SNAP IP from a non a NAPT switch.
+        //Install the tunnel table entry in NAPT switch for inbound traffic to SNAT IP from a non a NAPT switch.
         LOG.info("installInboundTerminatingServiceTblEntry : creating entry for Terminating Service Table "
                 + "for switch {}, routerId {}", dpnId, routerId);
         List<MatchInfo> matches = new ArrayList<>();
@@ -566,17 +566,17 @@ public abstract class AbstractSnatService implements SnatServiceListener {
         List<InstructionInfo> instructions = new ArrayList<>();
         instructions.add(new InstructionApplyActions(actionsInfos));
         String flowRef = getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId.longValue()) + "INBOUND";
-        addFlow(confTx, dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, flowRef, NatConstants.SNAT_FIB_FLOW_PRIORITY, flowRef,
-                 NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, flowRef,
+                NatConstants.SNAT_FIB_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
     }
 
     protected void removeInboundTerminatingServiceTblEntry(TypedReadWriteTransaction<Configuration> confTx,
             BigInteger dpnId, Long routerId) throws ExecutionException, InterruptedException {
-        //Install the tunnel table entry in NAPT switch for inbound traffic to SNAP IP from a non a NAPT switch.
+        //Install the tunnel table entry in NAPT switch for inbound traffic to SNAT IP from a non a NAPT switch.
         LOG.info("installInboundTerminatingServiceTblEntry : creating entry for Terminating Service Table "
             + "for switch {}, routerId {}", dpnId, routerId);
         String flowRef = getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId.longValue()) + "INBOUND";
-        removeFlow(confTx, dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, flowRef);
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, flowRef);
     }
 
     protected void addDefaultFibRouteForSNAT(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
@@ -591,14 +591,15 @@ public abstract class AbstractSnatService implements SnatServiceListener {
         instructions.add(new InstructionGotoTable(NwConstants.PSNAT_TABLE));
 
         String flowRef = "DefaultFibRouteForSNAT" + getFlowRef(dpnId, NwConstants.L3_FIB_TABLE, extNetId);
-        addFlow(confTx, dpnId, NwConstants.L3_FIB_TABLE, flowRef, NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef,
-            NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.L3_FIB_TABLE, flowRef,
+                NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches,
+                instructions);
     }
 
     protected void removeDefaultFibRouteForSNAT(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
             Long extNetId) throws ExecutionException, InterruptedException {
         String flowRef = "DefaultFibRouteForSNAT" + getFlowRef(dpnId, NwConstants.L3_FIB_TABLE, extNetId);
-        removeFlow(confTx, dpnId, NwConstants.L3_FIB_TABLE, flowRef);
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.L3_FIB_TABLE, flowRef);
     }
 
     protected String getFlowRef(BigInteger dpnId, short tableId, long routerID) {
@@ -606,22 +607,6 @@ public abstract class AbstractSnatService implements SnatServiceListener {
             + tableId + NatConstants.FLOWID_SEPARATOR + routerID;
     }
 
-    protected void addFlow(TypedWriteTransaction<Configuration> confTx, BigInteger dpId, short tableId,
-        String flowId, int priority, String flowName, BigInteger cookie, List<? extends MatchInfoBase> matches,
-        List<InstructionInfo> instructions) {
-        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, flowId, priority, flowName,
-                NatConstants.DEFAULT_IDLE_TIMEOUT, NatConstants.DEFAULT_IDLE_TIMEOUT, cookie, matches,
-                instructions);
-        LOG.trace("syncFlow : Installing DpnId {}, flowId {}", dpId, flowId);
-        mdsalManager.addFlow(confTx, flowEntity);
-    }
-
-    protected void removeFlow(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpId, short tableId,
-            String flowId) throws ExecutionException, InterruptedException {
-        LOG.trace("syncFlow : Removing Acl Flow DpnId {}, flowId {}", dpId, flowId);
-        mdsalManager.removeFlow(confTx, dpId, flowId, tableId);
-    }
-
     protected long createGroupId(String groupIdKey) {
         AllocateIdInput getIdInput = new AllocateIdInputBuilder()
             .setPoolName(NatConstants.SNAT_IDPOOL_NAME).setIdKey(groupIdKey)
@@ -640,41 +625,6 @@ public abstract class AbstractSnatService implements SnatServiceListener {
         return "snatmiss." + routerName;
     }
 
-    @Nullable
-    protected String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId) {
-        Class<? extends TunnelTypeBase> tunType = TunnelTypeVxlan.class;
-        RpcResult<GetTunnelInterfaceNameOutput> rpcResult;
-        try {
-            Future<RpcResult<GetTunnelInterfaceNameOutput>> result = itmManager
-                    .getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder().setSourceDpid(srcDpId)
-                            .setDestinationDpid(dstDpId).setTunnelType(tunType).build());
-            rpcResult = result.get();
-            if (!rpcResult.isSuccessful()) {
-                tunType = TunnelTypeGre.class ;
-                result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder()
-                        .setSourceDpid(srcDpId)
-                        .setDestinationDpid(dstDpId)
-                        .setTunnelType(tunType)
-                        .build());
-                rpcResult = result.get();
-                if (!rpcResult.isSuccessful()) {
-                    LOG.warn("getTunnelInterfaceName : RPC Call to getTunnelInterfaceId returned with Errors {}",
-                            rpcResult.getErrors());
-                } else {
-                    return rpcResult.getResult().getInterfaceName();
-                }
-                LOG.warn("getTunnelInterfaceName : RPC Call to getTunnelInterfaceId returned with Errors {}",
-                        rpcResult.getErrors());
-            } else {
-                return rpcResult.getResult().getInterfaceName();
-            }
-        } catch (InterruptedException | ExecutionException | NullPointerException e) {
-            LOG.error("getTunnelInterfaceName : Exception when getting tunnel interface Id for tunnel "
-                    + "between {} and {}", srcDpId, dstDpId);
-        }
-        return null;
-    }
-
     protected void removeMipAdjacencies(Routers routers) {
         LOG.info("removeMipAdjacencies for router {}", routers.getRouterName());
         String externalSubNetId  = null;
index 077604eb886c7eec8722f62b0e1cb8deda46a1d7..c1df57718614459efc9a8639d1614ff9df97ac85 100644 (file)
@@ -199,8 +199,9 @@ public abstract class ConntrackBasedSnatService extends AbstractSnatService {
         instructions.add(new InstructionApplyActions(actionsInfos));
 
         String flowRef = getFlowRef(dpnId, NwConstants.PSNAT_TABLE, routerId);
-        addFlow(confTx, dpnId, NwConstants.PSNAT_TABLE, flowRef, NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef,
-                NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.PSNAT_TABLE, flowRef,
+                NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches,
+                instructions);
     }
 
     protected void removeSnatMissEntryForPrimrySwch(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
@@ -208,7 +209,7 @@ public abstract class ConntrackBasedSnatService extends AbstractSnatService {
         LOG.info("installSnatSpecificEntriesForNaptSwitch : called for the primary NAPT switch dpnId {}", dpnId);
 
         String flowRef = getFlowRef(dpnId, NwConstants.PSNAT_TABLE, routerId);
-        removeFlow(confTx, dpnId, NwConstants.PSNAT_TABLE, flowRef);
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.PSNAT_TABLE, flowRef);
     }
 
     protected void addOutboundTblTrackEntry(TypedWriteTransaction<Configuration> confTx, BigInteger dpnId,
@@ -225,8 +226,9 @@ public abstract class ConntrackBasedSnatService extends AbstractSnatService {
         instructionInfo.add(new InstructionApplyActions(listActionInfo));
 
         String flowRef = getFlowRef(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, routerId) + "trkest";
-        addFlow(confTx, dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef, NatConstants.SNAT_TRK_FLOW_PRIORITY, flowRef,
-                NwConstants.COOKIE_SNAT_TABLE, matches, instructionInfo);
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef,
+                NatConstants.SNAT_TRK_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches,
+                instructionInfo);
     }
 
     protected void removeOutboundTblTrackEntry(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
@@ -234,7 +236,7 @@ public abstract class ConntrackBasedSnatService extends AbstractSnatService {
         LOG.info("createOutboundTblTrackEntry : called for switch {}, routerId {}", dpnId, routerId);
 
         String flowRef = getFlowRef(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, routerId) + "trkest";
-        removeFlow(confTx, dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef);
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef);
     }
 
     protected void addOutboundTblEntry(TypedWriteTransaction<Configuration> confTx, BigInteger dpnId, long routerId,
@@ -259,15 +261,15 @@ public abstract class ConntrackBasedSnatService extends AbstractSnatService {
         List<InstructionInfo> instructions = new ArrayList<>();
         instructions.add(new InstructionApplyActions(actionsInfos));
         String flowRef = getFlowRef(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, routerId);
-        addFlow(confTx, dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef,  NatConstants.SNAT_NEW_FLOW_PRIORITY,
-            flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef,
+                NatConstants.SNAT_NEW_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
     }
 
     protected void removeOutboundTblEntry(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
             long routerId) throws ExecutionException, InterruptedException {
         LOG.info("createOutboundTblEntry : dpId {} and routerId {}", dpnId, routerId);
         String flowRef = getFlowRef(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, routerId);
-        removeFlow(confTx, dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef);
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef);
     }
 
     protected void addNaptPfibFlow(TypedReadWriteTransaction<Configuration> confTx, Routers routers, BigInteger dpnId,
@@ -293,8 +295,8 @@ public abstract class ConntrackBasedSnatService extends AbstractSnatService {
         instructions.add(new InstructionApplyActions(listActionInfo));
         String flowRef = getFlowRef(dpnId, NwConstants.NAPT_PFIB_TABLE, routerId);
         flowRef = flowRef + "OUTBOUND";
-        addFlow(confTx, dpnId, NwConstants.NAPT_PFIB_TABLE, flowRef, NatConstants.SNAT_TRK_FLOW_PRIORITY,
-            flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.NAPT_PFIB_TABLE, flowRef,
+                NatConstants.SNAT_TRK_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
     }
 
     protected void removeNaptPfibFlow(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
@@ -302,7 +304,7 @@ public abstract class ConntrackBasedSnatService extends AbstractSnatService {
         Long extNetId = NatUtil.getVpnId(confTx, routers.getNetworkId().getValue());
         LOG.info("installNaptPfibFlow : dpId {}, extNetId {}", dpnId, extNetId);
         String flowRef = getFlowRef(dpnId, NwConstants.NAPT_PFIB_TABLE, routerId) + "OUTBOUND";
-        removeFlow(confTx, dpnId, NwConstants.NAPT_PFIB_TABLE, flowRef);
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.NAPT_PFIB_TABLE, flowRef);
     }
 
     protected void addInboundEntry(TypedWriteTransaction<Configuration> confTx, BigInteger dpnId, long routerId,
@@ -332,8 +334,8 @@ public abstract class ConntrackBasedSnatService extends AbstractSnatService {
         instructions.add(new InstructionApplyActions(actionsInfos));
         String flowRef = getFlowRef(dpnId, NwConstants.INBOUND_NAPT_TABLE, routerId);
         flowRef = flowRef + "OUTBOUND";
-        addFlow(confTx, dpnId, NwConstants.INBOUND_NAPT_TABLE, flowRef, NatConstants.DEFAULT_TS_FLOW_PRIORITY, flowRef,
-            NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.INBOUND_NAPT_TABLE, flowRef,
+                NatConstants.DEFAULT_TS_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
     }
 
     protected void removeInboundEntry(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
@@ -341,7 +343,7 @@ public abstract class ConntrackBasedSnatService extends AbstractSnatService {
         LOG.info("installInboundEntry : dpId {} and routerId {}", dpnId, routerId);
 
         String flowRef = getFlowRef(dpnId, NwConstants.INBOUND_NAPT_TABLE, routerId) + "OUTBOUND";
-        removeFlow(confTx, dpnId, NwConstants.INBOUND_NAPT_TABLE, flowRef);
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.INBOUND_NAPT_TABLE, flowRef);
     }
 
     protected void addNaptPfibEntry(TypedWriteTransaction<Configuration> confTx, BigInteger dpnId, long routerId) {
@@ -358,14 +360,15 @@ public abstract class ConntrackBasedSnatService extends AbstractSnatService {
         instructionInfo.add(new InstructionApplyActions(listActionInfo));
 
         String flowRef = getFlowRef(dpnId, NwConstants.NAPT_PFIB_TABLE, routerId) + "INBOUND";
-        addFlow(confTx, dpnId, NwConstants.NAPT_PFIB_TABLE, flowRef, NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef,
-            NwConstants.COOKIE_SNAT_TABLE, matches, instructionInfo);
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.NAPT_PFIB_TABLE, flowRef,
+                NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches,
+                instructionInfo);
     }
 
     protected void removeNaptPfibEntry(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
             long routerId) throws ExecutionException, InterruptedException {
         LOG.info("installNaptPfibEntry : called for dpnId {} and routerId {} ", dpnId, routerId);
         String flowRef = getFlowRef(dpnId, NwConstants.NAPT_PFIB_TABLE, routerId) + "INBOUND";
-        removeFlow(confTx, dpnId, NwConstants.NAPT_PFIB_TABLE, flowRef);
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.NAPT_PFIB_TABLE, flowRef);
     }
 }
diff --git a/natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/Ipv6ForwardingService.java b/natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/Ipv6ForwardingService.java
new file mode 100644 (file)
index 0000000..f60c274
--- /dev/null
@@ -0,0 +1,483 @@
+/*
+ * Copyright (c) 2018 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.natservice.internal;
+
+import static org.opendaylight.netvirt.natservice.internal.AbstractSnatService.LOAD_END;
+import static org.opendaylight.netvirt.natservice.internal.AbstractSnatService.LOAD_START;
+import static org.opendaylight.netvirt.natservice.internal.NatUtil.getGroupIdKey;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.genius.infra.Datastore.Configuration;
+import org.opendaylight.genius.infra.TypedReadWriteTransaction;
+import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
+import org.opendaylight.genius.mdsalutil.ActionInfo;
+import org.opendaylight.genius.mdsalutil.BucketInfo;
+import org.opendaylight.genius.mdsalutil.GroupEntity;
+import org.opendaylight.genius.mdsalutil.InstructionInfo;
+import org.opendaylight.genius.mdsalutil.MDSALUtil;
+import org.opendaylight.genius.mdsalutil.MatchInfo;
+import org.opendaylight.genius.mdsalutil.MatchInfoBase;
+import org.opendaylight.genius.mdsalutil.MetaDataUtil;
+import org.opendaylight.genius.mdsalutil.NWUtil;
+import org.opendaylight.genius.mdsalutil.NwConstants;
+import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
+import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadInPort;
+import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadMetadata;
+import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
+import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
+import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
+import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
+import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
+import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
+import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
+import org.opendaylight.netvirt.natservice.api.SnatServiceListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.routers.ExternalIps;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Ipv6ForwardingService implements SnatServiceListener {
+    private static final Logger LOG = LoggerFactory.getLogger(Ipv6ForwardingService.class);
+
+    protected final DataBroker dataBroker;
+    protected final IMdsalApiManager mdsalManager;
+    protected final IdManagerService idManager;
+    protected final NAPTSwitchSelector naptSwitchSelector;
+    protected final ItmRpcService itmManager;
+    protected final OdlInterfaceRpcService odlInterfaceRpcService;
+    protected final IInterfaceManager interfaceManager;
+    protected final Ipv6SubnetFlowProgrammer ipv6SubnetFlowProgrammer;
+
+    public Ipv6ForwardingService(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
+                                  final ItmRpcService itmManager,
+                                  final OdlInterfaceRpcService odlInterfaceRpcService,
+                                  final IdManagerService idManager,
+                                  final NAPTSwitchSelector naptSwitchSelector,
+                                  final IInterfaceManager interfaceManager,
+                                  final Ipv6SubnetFlowProgrammer ipv6SubnetFlowProgrammer) {
+        this.dataBroker = dataBroker;
+        this.mdsalManager = mdsalManager;
+        this.itmManager = itmManager;
+        this.odlInterfaceRpcService = odlInterfaceRpcService;
+        this.idManager = idManager;
+        this.naptSwitchSelector = naptSwitchSelector;
+        this.interfaceManager = interfaceManager;
+        this.ipv6SubnetFlowProgrammer = ipv6SubnetFlowProgrammer;
+    }
+
+    public void init() {
+        LOG.info("Ipv6ForwardingService: {} init", getClass().getSimpleName());
+    }
+
+    @Override
+    public boolean addCentralizedRouterAllSwitch(TypedReadWriteTransaction<Configuration> confTx,
+            Routers routers, BigInteger primarySwitchId) {
+        String routerName = routers.getRouterName();
+        LOG.info("handleSnatAllSwitch : invoked for router {} with NAPTSwitch {} for {} flows",
+                routerName, primarySwitchId, "installing");
+        List<BigInteger> switches = naptSwitchSelector.getDpnsForVpn(routerName);
+        /*
+         * Primary switch handled separately since the pseudo port created may
+         * not be present in the switch list on delete.
+         */
+        addCentralizedRouter(confTx, routers, primarySwitchId, primarySwitchId);
+        for (BigInteger dpnId : switches) {
+            if (primarySwitchId != dpnId) {
+                addCentralizedRouter(confTx, routers, primarySwitchId, dpnId);
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean addCentralizedRouter(TypedReadWriteTransaction<Configuration> confTx,
+            Routers routers, BigInteger primarySwitchId, BigInteger dpnId) {
+        Long routerId = NatUtil.getVpnId(dataBroker, routers.getRouterName());
+        BigInteger routerMetadata = MetaDataUtil.getVpnIdMetadata(routerId);
+
+        if (!dpnId.equals(primarySwitchId)) {
+            LOG.info("handleSnat (non-NAPTSwitch) : {} flows on switch {} for router {}",
+                    "Installing", dpnId, routers.getRouterName());
+            // Program default flow from FIB_TABLE(21) to PSNAT_TABLE(26) (egress direction)
+            addIpv6DefaultFibRoute(confTx, dpnId, routerId, routerMetadata);
+
+            // Currently we are only programming flows when ext-net has an IPv6Subnet
+            if (routerHasIpv6ExtSubnet(routers)) {
+                // Program flows on non-NAPTSwitch to send N/S packets to the NAPTSwitch
+                addIpv6PsNatMissEntryNonNaptSwitch(confTx, dpnId, routerId, routers.getRouterName(),
+                        primarySwitchId);
+            }
+        } else {
+            LOG.info("handleSnat (NAPTSwitch) : {} flows on switch {} for router {}",
+                    "Installing", dpnId, routers.getRouterName());
+            // Program default flow from FIB_TABLE(21) to PSNAT_TABLE(26) (egress direction)
+            addIpv6DefaultFibRoute(confTx, dpnId, routerId, routerMetadata);
+
+            // Program flows from PSNAT_TABLE(26) to OUTBOUND_NAPT_TABLE(46) (egress direction)
+            addIpv6SnatMissEntryForNaptSwitch(confTx, dpnId, routerId, routerMetadata);
+
+            // Program flows in INTERNAL_TUNNEL_TABLE(36) for packets coming from non-NAPTSwitch (egress direction)
+            addIpv6TerminatingServiceTblEntry(confTx, dpnId, routerId, routerMetadata);
+
+            // Program flows from NAPT_PFIB_TABLE(47) to FIB_TABLE(21) (ingress direction)
+            addIpv6NaptPfibInboundFlow(confTx, dpnId, routerId, routerMetadata);
+
+            // Now installing flows that use SubnetInfo
+            ipv6SubnetFlowProgrammer. addSubnetSpecificFlows(confTx, dpnId, routerId, routers, routerMetadata);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean removeCentralizedRouterAllSwitch(TypedReadWriteTransaction<Configuration> confTx,
+            Routers routers, BigInteger primarySwitchId) throws ExecutionException, InterruptedException {
+        String routerName = routers.getRouterName();
+        LOG.info("handleSnatAllSwitch : invoked for router {} with NAPTSwitch {} for {} flows",
+                routerName, primarySwitchId, "removing");
+        List<BigInteger> switches = naptSwitchSelector.getDpnsForVpn(routerName);
+        /*
+         * Primary switch handled separately since the pseudo port created may
+         * not be present in the switch list on delete.
+         */
+        removeCentralizedRouter(confTx, routers, primarySwitchId, primarySwitchId);
+        for (BigInteger dpnId : switches) {
+            if (primarySwitchId != dpnId) {
+                removeCentralizedRouter(confTx, routers, primarySwitchId, dpnId);
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean removeCentralizedRouter(TypedReadWriteTransaction<Configuration> confTx,
+            Routers routers, BigInteger primarySwitchId, BigInteger dpnId)
+                    throws ExecutionException, InterruptedException {
+        Long routerId = NatUtil.getVpnId(dataBroker, routers.getRouterName());
+        BigInteger routerMetadata = MetaDataUtil.getVpnIdMetadata(routerId);
+
+        if (!dpnId.equals(primarySwitchId)) {
+            LOG.info("handleSnat (non-NAPTSwitch) : {} flows on switch {} for router {}",
+                    "Removing", dpnId, routers.getRouterName());
+            // Program default flow from FIB_TABLE(21) to PSNAT_TABLE(26) (egress direction)
+            addIpv6DefaultFibRoute(confTx, dpnId, routerId, routerMetadata);
+
+            // Currently we are only programming flows when ext-net has an IPv6Subnet
+            if (routerHasIpv6ExtSubnet(routers)) {
+                // Program flows on non-NAPTSwitch to send N/S packets to the NAPTSwitch
+                addIpv6PsNatMissEntryNonNaptSwitch(confTx, dpnId, routerId, routers.getRouterName(),
+                        primarySwitchId);
+            }
+        } else {
+            LOG.info("handleSnat (NAPTSwitch) : {} flows on switch {} for router {}",
+                    "Removing", dpnId, routers.getRouterName());
+            // Program default flow from FIB_TABLE(21) to PSNAT_TABLE(26) (egress direction)
+            removeIpv6DefaultFibRoute(confTx, dpnId, routerId);
+
+            // Program flows from PSNAT_TABLE(26) to OUTBOUND_NAPT_TABLE(46) (egress direction)
+            removeIpv6SnatMissEntryForNaptSwitch(confTx, dpnId, routerId);
+
+            // Program flows in INTERNAL_TUNNEL_TABLE(36) for packets coming from non-NAPTSwitch (egress direction)
+            removeIpv6TerminatingServiceTblEntry(confTx, dpnId, routerId);
+
+            // Program flows from NAPT_PFIB_TABLE(47) to FIB_TABLE(21) (ingress direction)
+            removeIpv6NaptPfibInboundFlow(confTx, dpnId, routerId);
+
+            // Now installing flows that use SubnetInfo
+            ipv6SubnetFlowProgrammer.removeSubnetSpecificFlows(confTx, dpnId, routerId, routers);
+        }
+        return true;
+    }
+
+    @Override
+    public boolean handleRouterUpdate(TypedReadWriteTransaction<Configuration> confTx,
+            Routers origRouter, Routers updatedRouter) throws ExecutionException, InterruptedException {
+        LOG.info("handleRouterUpdate : originalRouter {}, updatedRouter {}", origRouter, updatedRouter);
+        String routerName = origRouter.getRouterName();
+        BigInteger primarySwitchId = NatUtil.getPrimaryNaptfromRouterName(dataBroker, routerName);
+        Long routerId = NatUtil.getVpnId(dataBroker, routerName);
+        BigInteger routerMetadata = MetaDataUtil.getVpnIdMetadata(routerId);
+
+        // If the external network is updated with an IPv6Subnet, program the necessary flows on non-NAPTSwitch
+        if (!routerHasIpv6ExtSubnet(origRouter) && routerHasIpv6ExtSubnet(updatedRouter)) {
+            List<BigInteger> switches = naptSwitchSelector.getDpnsForVpn(routerName);
+            for (BigInteger dpnId : switches) {
+                if (primarySwitchId != dpnId) {
+                    LOG.info("handleRouterUpdate (non-NAPTSwitch) : Installing flows on switch {} for router {}",
+                            dpnId, routerName);
+                    addIpv6PsNatMissEntryNonNaptSwitch(confTx, dpnId, routerId, routerName,
+                            primarySwitchId);
+                }
+            }
+        }
+
+        ipv6SubnetFlowProgrammer.removeSubnetSpecificFlows(confTx, primarySwitchId, routerId, origRouter);
+        ipv6SubnetFlowProgrammer.addSubnetSpecificFlows(confTx, primarySwitchId, routerId, updatedRouter,
+                routerMetadata);
+        return true;
+    }
+
+    @Override
+    public boolean addSnatAllSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
+            BigInteger primarySwitchId) {
+        return true;
+    }
+
+    @Override
+    public boolean addSnat(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
+            BigInteger primarySwitchId, BigInteger dpnId) {
+        return true;
+    }
+
+    @Override
+    public boolean removeSnatAllSwitch(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
+            BigInteger primarySwitchId)  throws ExecutionException, InterruptedException {
+        return true;
+    }
+
+    @Override
+    public boolean removeSnat(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
+            BigInteger primarySwitchId, BigInteger dpnId) throws ExecutionException, InterruptedException {
+        return true;
+    }
+
+
+    protected void addIpv6DefaultFibRoute(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
+            Long routerId, BigInteger routerMetadata) {
+        LOG.debug("installIpv6DefaultFibRoute : Installing default FIB route to PSNAT_TABLE on {}", dpnId);
+        List<MatchInfo> matches = new ArrayList<>();
+        matches.add(MatchEthernetType.IPV6);
+        matches.add(new MatchMetadata(routerMetadata, MetaDataUtil.METADATA_MASK_VRFID));
+
+        List<InstructionInfo> instructions = new ArrayList<>();
+        instructions.add(new InstructionGotoTable(NwConstants.PSNAT_TABLE));
+
+        String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.L3_FIB_TABLE, routerId);
+        flowRef += ".Outbound";
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.L3_FIB_TABLE, flowRef,
+                NatConstants.DEFAULT_DNAT_FLOW_PRIORITY, flowRef,
+                NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
+    }
+
+    protected void removeIpv6DefaultFibRoute(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
+            Long routerId) throws ExecutionException, InterruptedException {
+        LOG.debug("installIpv6DefaultFibRoute : Installing default FIB route to PSNAT_TABLE on {}", dpnId);
+        String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.L3_FIB_TABLE, routerId);
+        flowRef += ".Outbound";
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.L3_FIB_TABLE, flowRef);
+    }
+
+    protected void addIpv6PsNatMissEntryNonNaptSwitch(TypedReadWriteTransaction<Configuration> confTx,
+            BigInteger dpnId, Long routerId, String routerName, BigInteger primarySwitchId) {
+        LOG.debug("installIpv6PsNatMissEntryNonNaptSwitch : On Non-Napt Switch, installing SNAT miss entry in"
+                + " switch {} for router {}", dpnId, routerName);
+        List<ActionInfo> listActionInfoPrimary = new ArrayList<>();
+        List<BucketInfo> listBucketInfo = new ArrayList<>();
+
+        String ifNamePrimary = NatUtil.getTunnelInterfaceName(dpnId, primarySwitchId, itmManager);
+        if (ifNamePrimary != null) {
+            LOG.debug("installIpv6PsNatMissEntryNonNaptSwitch : On Non-Napt Switch, Primary Tunnel interface is {}",
+                    ifNamePrimary);
+            listActionInfoPrimary = NatUtil.getEgressActionsForInterface(odlInterfaceRpcService, itmManager,
+                    interfaceManager, ifNamePrimary, routerId, true);
+        } else {
+            LOG.warn("installIpv6PsNatMissEntryNonNaptSwitch: could not get tunnelInterface for {} on Switch {}",
+                    primarySwitchId, dpnId);
+        }
+
+        BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
+        listBucketInfo.add(0, bucketPrimary);
+
+        LOG.debug("installIpv6PsNatMissEntryNonNaptSwitch : installSnatMissEntry called for dpnId {} with"
+                + " primaryBucket {} ", dpnId, listBucketInfo.get(0));
+
+        long groupId = createGroupIdForIpv6Router(getGroupIdKey(routerName + "IPv6"));
+        GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName, GroupTypes.GroupAll,
+                listBucketInfo);
+        LOG.debug("installing the PSNAT to NAPTSwitch GroupEntity:{} with GroupId: {}", groupEntity, groupId);
+        mdsalManager.addGroup(confTx, groupEntity);
+        List<MatchInfo> matches = new ArrayList<>();
+        matches.add(MatchEthernetType.IPV6);
+        matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(routerId), MetaDataUtil.METADATA_MASK_VRFID));
+
+        List<ActionInfo> actionsInfo = new ArrayList<>();
+        actionsInfo.add(new ActionSetFieldTunnelId(BigInteger.valueOf(routerId)));
+        actionsInfo.add(new ActionGroup(groupId));
+        List<InstructionInfo> instructions = new ArrayList<>();
+        instructions.add(new InstructionApplyActions(actionsInfo));
+
+        String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.PSNAT_TABLE, routerId);
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.PSNAT_TABLE, flowRef,
+                NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef,
+                NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
+    }
+
+    protected void removeIpv6PsNatMissEntryNonNaptSwitch(TypedReadWriteTransaction<Configuration> confTx,
+            BigInteger dpnId, Long routerId, String routerName, BigInteger primarySwitchId)
+                    throws ExecutionException, InterruptedException {
+        LOG.debug("installIpv6PsNatMissEntryNonNaptSwitch : On Non-Napt Switch, installing SNAT miss entry in"
+                + " switch {} for router {}", dpnId, routerName);
+        List<ActionInfo> listActionInfoPrimary = new ArrayList<>();
+        List<BucketInfo> listBucketInfo = new ArrayList<>();
+
+        String ifNamePrimary = NatUtil.getTunnelInterfaceName(dpnId, primarySwitchId, itmManager);
+        if (ifNamePrimary != null) {
+            LOG.debug("installIpv6PsNatMissEntryNonNaptSwitch : On Non-Napt Switch, Primary Tunnel interface is {}",
+                    ifNamePrimary);
+            listActionInfoPrimary = NatUtil.getEgressActionsForInterface(odlInterfaceRpcService, itmManager,
+                    interfaceManager, ifNamePrimary, routerId, true);
+        } else {
+            LOG.warn("installIpv6PsNatMissEntryNonNaptSwitch: could not get tunnelInterface for {} on Switch {}",
+                    primarySwitchId, dpnId);
+        }
+
+        BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
+        listBucketInfo.add(0, bucketPrimary);
+
+        LOG.debug("installIpv6PsNatMissEntryNonNaptSwitch : installSnatMissEntry called for dpnId {} with"
+                + " primaryBucket {} ", dpnId, listBucketInfo.get(0));
+
+        long groupId = createGroupIdForIpv6Router(getGroupIdKey(routerName + "IPv6"));
+        GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName, GroupTypes.GroupAll,
+                listBucketInfo);
+        LOG.debug("removing the PSNAT to NAPTSwitch GroupEntity:{} with GroupId: {}", groupEntity, groupId);
+        mdsalManager.removeGroup(confTx, groupEntity);
+
+        String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.PSNAT_TABLE, routerId);
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.PSNAT_TABLE, flowRef);
+    }
+
+
+    protected void addIpv6SnatMissEntryForNaptSwitch(TypedReadWriteTransaction<Configuration> confTx,
+            BigInteger dpnId, Long routerId, BigInteger routerMetadata) {
+        LOG.debug("installIpv6SnatMissEntryForNaptSwitch {} called for routerId {}", dpnId, routerId);
+        List<MatchInfo> matches = new ArrayList<>();
+        matches.add(MatchEthernetType.IPV6);
+        matches.add(new MatchMetadata(routerMetadata, MetaDataUtil.METADATA_MASK_VRFID));
+
+        List<InstructionInfo> instructions = new ArrayList<>();
+        instructions.add(new InstructionGotoTable(NwConstants.OUTBOUND_NAPT_TABLE));
+
+        String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.PSNAT_TABLE, routerId);
+        flowRef += ".Outbound";
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.PSNAT_TABLE, flowRef,
+                NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef,
+                NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
+    }
+
+    protected void removeIpv6SnatMissEntryForNaptSwitch(TypedReadWriteTransaction<Configuration> confTx,
+            BigInteger dpnId, Long routerId)
+                    throws ExecutionException, InterruptedException {
+        LOG.debug("installIpv6SnatMissEntryForNaptSwitch {} called for routerId {}", dpnId, routerId);
+        String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.PSNAT_TABLE, routerId);
+        flowRef += ".Outbound";
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.PSNAT_TABLE, flowRef);
+    }
+
+    protected void addIpv6TerminatingServiceTblEntry(TypedReadWriteTransaction<Configuration> confTx,
+            BigInteger dpnId, Long  routerId, BigInteger routerMetadata) {
+        LOG.debug("installIpv6TerminatingServiceTblEntry : creating entry for Terminating Service Table "
+                + "for switch {}, routerId {}", dpnId, routerId);
+        List<MatchInfo> matches = new ArrayList<>();
+        matches.add(MatchEthernetType.IPV6);
+        matches.add(new MatchTunnelId(BigInteger.valueOf(routerId)));
+
+        List<ActionInfo> actionsInfos = new ArrayList<>();
+        ActionNxLoadMetadata actionLoadMeta = new ActionNxLoadMetadata(routerMetadata, LOAD_START, LOAD_END);
+        actionsInfos.add(actionLoadMeta);
+        actionsInfos.add(new ActionNxResubmit(NwConstants.OUTBOUND_NAPT_TABLE));
+        List<InstructionInfo> instructions = new ArrayList<>();
+        instructions.add(new InstructionApplyActions(actionsInfos));
+
+        String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId);
+        flowRef += ".Outbound";
+        NatUtil.addFlow(confTx, mdsalManager, dpnId,  NwConstants.INTERNAL_TUNNEL_TABLE, flowRef,
+                NatConstants.DEFAULT_TS_FLOW_PRIORITY, flowRef,
+                NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
+
+    }
+
+    protected void removeIpv6TerminatingServiceTblEntry(TypedReadWriteTransaction<Configuration> confTx,
+            BigInteger dpnId, Long  routerId) throws ExecutionException, InterruptedException {
+        LOG.debug("installIpv6TerminatingServiceTblEntry : creating entry for Terminating Service Table "
+                + "for switch {}, routerId {}", dpnId, routerId);
+        String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId);
+        flowRef += ".Outbound";
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId,  NwConstants.INTERNAL_TUNNEL_TABLE, flowRef);
+
+    }
+
+    protected void addIpv6NaptPfibInboundFlow(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
+            long routerId, BigInteger routerMetadata) {
+        LOG.debug("installIpv6NaptPfibInboundFlow : called for dpnId {} and routerId {} ", dpnId, routerId);
+        List<MatchInfoBase> matches = new ArrayList<>();
+        matches.add(MatchEthernetType.IPV6);
+        matches.add(new MatchMetadata(routerMetadata, MetaDataUtil.METADATA_MASK_VRFID));
+
+        ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
+        ArrayList<InstructionInfo> instructionInfo = new ArrayList<>();
+        listActionInfo.add(new ActionNxLoadInPort(BigInteger.ZERO));
+        listActionInfo.add(new ActionNxResubmit(NwConstants.L3_FIB_TABLE));
+        instructionInfo.add(new InstructionApplyActions(listActionInfo));
+
+        String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.NAPT_PFIB_TABLE, routerId);
+        flowRef += ".Inbound";
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.NAPT_PFIB_TABLE, flowRef,
+                NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY,
+                flowRef, NwConstants.COOKIE_SNAT_TABLE,
+                matches, instructionInfo);
+    }
+
+    protected void removeIpv6NaptPfibInboundFlow(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
+            long routerId)
+                    throws ExecutionException, InterruptedException {
+        LOG.debug("installIpv6NaptPfibInboundFlow : called for dpnId {} and routerId {} ", dpnId, routerId);
+        String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.NAPT_PFIB_TABLE, routerId);
+        flowRef += ".Inbound";
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.NAPT_PFIB_TABLE, flowRef);
+    }
+
+    protected long createGroupIdForIpv6Router(String groupIdKey) {
+        AllocateIdInput getIdInput = new AllocateIdInputBuilder()
+                .setPoolName(NatConstants.SNAT_IDPOOL_NAME).setIdKey(groupIdKey)
+                .build();
+        try {
+            Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
+            RpcResult<AllocateIdOutput> rpcResult = result.get();
+            return rpcResult.getResult().getIdValue();
+        } catch (NullPointerException | InterruptedException | ExecutionException e) {
+            LOG.error("createGroupIdForIPv6Router: Exception while creating group with key : {}", groupIdKey, e);
+        }
+        return 0;
+    }
+
+    protected boolean routerHasIpv6ExtSubnet(Routers routers) {
+        for (ExternalIps externalIp : routers.getExternalIps()) {
+            if (!NWUtil.isIpv4Address(externalIp.getIpAddress())) {
+                LOG.debug("router {}, has an external IPv6 subnet {}",
+                        routers.getRouterName(), externalIp.getIpAddress());
+                return true;
+            }
+        }
+        LOG.debug("router {}, does not have an external IPv6 subnet", routers.getRouterName());
+        return false;
+    }
+}
diff --git a/natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/Ipv6SubnetFlowProgrammer.java b/natservice/impl/src/main/java/org/opendaylight/netvirt/natservice/internal/Ipv6SubnetFlowProgrammer.java
new file mode 100644 (file)
index 0000000..1409a7f
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2018 Red Hat, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.netvirt.natservice.internal;
+
+import static org.opendaylight.netvirt.natservice.internal.AbstractSnatService.LOAD_END;
+import static org.opendaylight.netvirt.natservice.internal.AbstractSnatService.LOAD_START;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.genius.infra.Datastore.Configuration;
+import org.opendaylight.genius.infra.TypedReadWriteTransaction;
+import org.opendaylight.genius.mdsalutil.ActionInfo;
+import org.opendaylight.genius.mdsalutil.InstructionInfo;
+import org.opendaylight.genius.mdsalutil.MatchInfo;
+import org.opendaylight.genius.mdsalutil.MatchInfoBase;
+import org.opendaylight.genius.mdsalutil.MetaDataUtil;
+import org.opendaylight.genius.mdsalutil.NWUtil;
+import org.opendaylight.genius.mdsalutil.NwConstants;
+import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadInPort;
+import org.opendaylight.genius.mdsalutil.actions.ActionNxLoadMetadata;
+import org.opendaylight.genius.mdsalutil.actions.ActionNxResubmit;
+import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldEthernetSource;
+import org.opendaylight.genius.mdsalutil.instructions.InstructionApplyActions;
+import org.opendaylight.genius.mdsalutil.instructions.InstructionGotoTable;
+import org.opendaylight.genius.mdsalutil.instructions.InstructionWriteMetadata;
+import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
+import org.opendaylight.genius.mdsalutil.matches.MatchIpv6Destination;
+import org.opendaylight.genius.mdsalutil.matches.MatchIpv6Source;
+import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
+import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.Routers;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ext.routers.routers.ExternalIps;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Singleton
+public class Ipv6SubnetFlowProgrammer {
+    private static final Logger LOG = LoggerFactory.getLogger(Ipv6SubnetFlowProgrammer.class);
+    protected final DataBroker dataBroker;
+    protected final IMdsalApiManager mdsalManager;
+
+    @Inject
+    public Ipv6SubnetFlowProgrammer(final DataBroker dataBroker, final IMdsalApiManager mdsalManager) {
+        this.dataBroker = dataBroker;
+        this.mdsalManager = mdsalManager;
+    }
+
+    public void addSubnetSpecificFlows(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
+            long routerId, Routers routers, BigInteger routerMetadata) {
+        String extGwMacAddress = NatUtil.getExtGwMacAddFromRouterName(dataBroker, routers.getRouterName());
+        for (ExternalIps externalIp : routers.getExternalIps()) {
+            if (NWUtil.isIpv4Address(externalIp.getIpAddress())) {
+                // Skip ipv4 subnets in the external network
+                continue;
+            }
+
+            // Currently we only handle one external IPv6 address per router, others if present will be ignored.
+            long extSubnetId  = NatUtil.getExternalSubnetVpnId(dataBroker, externalIp.getSubnetId());
+
+            BigInteger extIpv6SubnetMetadata = MetaDataUtil.getVpnIdMetadata(extSubnetId);
+            LOG.info("addSubnetSpecificFlows : flows on NAPTSwitch {} for routerId {}, routerName {},"
+                    + " extIPv6Address {} Installing", dpnId, routerId, routers.getRouterName(),
+                    externalIp.getIpAddress());
+
+            // Program flows to handle ingress traffic coming over the tunnel port (i.e., from tableId 36 to 44)
+            addIpv6InboundTerminatingServiceTblEntry(confTx, extSubnetId, extIpv6SubnetMetadata, dpnId, routerId);
+
+            // Program flows in OUTBOUND_NAPT_TABLE(46) with action to send packets to NAPT_PFIB_TABLE(47)
+            addIPv6FlowToUpdateSrcMacToRouterGwMac(confTx, extGwMacAddress, extSubnetId, dpnId, routerId,
+                    routerMetadata);
+
+            for (Uuid subnetId : routers.getSubnetIds()) {
+                String tenantSubnetCidr = NatUtil.getSubnetIp(dataBroker, subnetId);
+                if (!NatUtil.isIPv6Subnet(tenantSubnetCidr)) {
+                    // Skip ipv4 subnets in the tenant network
+                    continue;
+                }
+
+                LOG.info("addSubnetSpecificFlows : flows for NAPTSwitch {} for routerName {},"
+                        + " tenantSubnetCidr {}, Installing",
+                        dpnId, routers.getRouterName(), tenantSubnetCidr);
+
+                // Program flows from NAPT_PFIB_TABLE(47) to FIB_TABLE(21) (egress direction)
+                addIpv6NaptPfibOutboundFlow(confTx, tenantSubnetCidr, extIpv6SubnetMetadata, dpnId, routerId);
+                // Program flows from FIB_TABLE(21) to INBOUND_NAPT_TABLE(44) (ingress direction)
+                addIpv6NaptInboundFibEntry(confTx, extSubnetId, tenantSubnetCidr, extIpv6SubnetMetadata,
+                        dpnId, routerId);
+                // Program flows from INBOUND_NAPT_TABLE(44) to NAPT_PFIB_TABLE(47) (ingress direction)
+                addIpv6NaptInboundNaptFlow(confTx, extSubnetId, tenantSubnetCidr, extIpv6SubnetMetadata,
+                        dpnId, routerId, routerMetadata);
+            }
+        }
+    }
+
+    public void removeSubnetSpecificFlows(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
+            long routerId, Routers routers) throws ExecutionException, InterruptedException {
+        for (ExternalIps externalIp : routers.getExternalIps()) {
+            if (NWUtil.isIpv4Address(externalIp.getIpAddress())) {
+                // Skip ipv4 subnets in the external network
+                continue;
+            }
+            // Currently we only handle one external IPv6 address per router, others if present will be ignored.
+            LOG.info("removeSubnetSpecificFlows : flows on NAPTSwitch {} for routerId {}, routerName {},"
+                    + " extIPv6Address {}, Removing",
+                    dpnId, routerId, routers.getRouterName(), externalIp.getIpAddress());
+
+            // Program flows to handle ingress traffic coming over the tunnel port (i.e., from tableId 36 to 44)
+            removeIpv6InboundTerminatingServiceTblEntry(confTx, dpnId, routerId);
+
+            // Program flows in OUTBOUND_NAPT_TABLE(46) with action to send packets to NAPT_PFIB_TABLE(47)
+            removeIPv6FlowToUpdateSrcMacToRouterGwMac(confTx, dpnId, routerId);
+
+            for (Uuid subnetId : routers.getSubnetIds()) {
+                String tenantSubnetCidr = NatUtil.getSubnetIp(dataBroker, subnetId);
+                if (!NatUtil.isIPv6Subnet(tenantSubnetCidr)) {
+                    // Skip ipv4 subnets in the tenant network
+                    continue;
+                }
+
+                LOG.info("removeSubnetSpecificFlows : flows for NAPTSwitch {} for routerName {},"
+                        + " tenantSubnetCidr {}, Removing",
+                        dpnId, routers.getRouterName(), tenantSubnetCidr);
+
+                // Program flows from NAPT_PFIB_TABLE(47) to FIB_TABLE(21) (egress direction)
+                removeIpv6NaptPfibOutboundFlow(confTx, tenantSubnetCidr, dpnId, routerId);
+                // Program flows from FIB_TABLE(21) to INBOUND_NAPT_TABLE(44) (ingress direction)
+                removeIpv6NaptInboundFibEntry(confTx, tenantSubnetCidr, dpnId, routerId);
+                // Program flows from INBOUND_NAPT_TABLE(44) to NAPT_PFIB_TABLE(47) (ingress direction)
+                removeIpv6NaptInboundNaptFlow(confTx, tenantSubnetCidr, dpnId, routerId);
+            }
+        }
+    }
+
+    private void addIpv6InboundTerminatingServiceTblEntry(TypedReadWriteTransaction<Configuration> confTx,
+            long extSubnetId, BigInteger extIpv6SubnetMetadata,BigInteger dpnId, long routerId) {
+        // Install the tunnel table entry in NAPT Switch for inbound traffic from a non NAPT Switch.
+        LOG.debug("addIpv6InboundTerminatingServiceTblEntry : entry for Terminating Service Table for switch {},"
+                + " routerId {}, Installing", dpnId, routerId);
+        List<MatchInfo> matches = new ArrayList<>();
+        matches.add(MatchEthernetType.IPV6);
+
+        List<ActionInfo> actionsInfos = new ArrayList<>();
+        if (extSubnetId == NatConstants.INVALID_ID) {
+            LOG.error("addIpv6InboundTerminatingServiceTblEntry : external subnet id is invalid.");
+            return;
+        }
+        matches.add(new MatchTunnelId(BigInteger.valueOf(extSubnetId)));
+        ActionNxLoadMetadata actionLoadMeta = new ActionNxLoadMetadata(extIpv6SubnetMetadata, LOAD_START, LOAD_END);
+        actionsInfos.add(actionLoadMeta);
+        actionsInfos.add(new ActionNxResubmit(NwConstants.INBOUND_NAPT_TABLE));
+        List<InstructionInfo> instructions = new ArrayList<>();
+        instructions.add(new InstructionApplyActions(actionsInfos));
+
+        String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId);
+        flowRef = flowRef + ".Inbound";
+        NatUtil.addFlow(confTx, mdsalManager, dpnId,  NwConstants.INTERNAL_TUNNEL_TABLE, flowRef,
+                NatConstants.SNAT_FIB_FLOW_PRIORITY, flowRef,
+                NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
+    }
+
+    private void removeIpv6InboundTerminatingServiceTblEntry(TypedReadWriteTransaction<Configuration> confTx,
+            BigInteger dpnId, long routerId) throws ExecutionException, InterruptedException {
+        // Install the tunnel table entry in NAPT Switch for inbound traffic from a non NAPT Switch.
+        LOG.debug("removeIpv6InboundTerminatingServiceTblEntry : entry for Terminating Service Table for switch {},"
+                + " routerId {}, Removing", dpnId, routerId);
+        String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId);
+        flowRef = flowRef + ".Inbound";
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId,  NwConstants.INTERNAL_TUNNEL_TABLE, flowRef);
+    }
+
+    private void addIPv6FlowToUpdateSrcMacToRouterGwMac(TypedReadWriteTransaction<Configuration> confTx,
+            String extGwMacAddress, long extSubnetId, BigInteger dpnId, long routerId, BigInteger routerMetadata) {
+        LOG.debug("addIPv6FlowToUpdateSrcMacToRouterGwMac : called for switch {}, routerId {}", dpnId, routerId);
+        List<MatchInfoBase> matches = new ArrayList<>();
+        matches.add(MatchEthernetType.IPV6);
+        matches.add(new MatchMetadata(routerMetadata, MetaDataUtil.METADATA_MASK_VRFID));
+
+        ArrayList<ActionInfo> listActionInfo = new ArrayList<>();
+        listActionInfo.add(new ActionSetFieldEthernetSource(new MacAddress(extGwMacAddress)));
+        ActionNxLoadMetadata actionLoadMeta = new ActionNxLoadMetadata(MetaDataUtil
+                .getVpnIdMetadata(extSubnetId), LOAD_START, LOAD_END);
+        listActionInfo.add(actionLoadMeta);
+        ArrayList<InstructionInfo> instructionInfo = new ArrayList<>();
+        listActionInfo.add(new ActionNxResubmit(NwConstants.NAPT_PFIB_TABLE));
+        instructionInfo.add(new InstructionApplyActions(listActionInfo));
+
+        String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, routerId);
+        flowRef += ".Outbound";
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef,
+                NatConstants.SNAT_TRK_FLOW_PRIORITY, flowRef,
+                NwConstants.COOKIE_SNAT_TABLE, matches, instructionInfo);
+    }
+
+    private void removeIPv6FlowToUpdateSrcMacToRouterGwMac(TypedReadWriteTransaction<Configuration> confTx,
+             BigInteger dpnId, long routerId)
+                    throws ExecutionException, InterruptedException {
+        LOG.debug("removeIPv6FlowToUpdateSrcMacToRouterGwMac : called for switch {}, routerId {}", dpnId, routerId);
+        String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, routerId);
+        flowRef += ".Outbound";
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef);
+    }
+
+    private void addIpv6NaptPfibOutboundFlow(TypedReadWriteTransaction<Configuration> confTx,
+            String tenantIpv6SubnetCidr, BigInteger extIpv6SubnetMetadata, BigInteger dpnId, long routerId) {
+        LOG.debug("addIpv6NaptPfibOutboundFlow : called for NAPTSwitch {}, routerId {}, tenantIPv6Cidr {}",
+                dpnId, routerId, tenantIpv6SubnetCidr);
+        List<MatchInfoBase> matches = new ArrayList<>();
+        matches.add(MatchEthernetType.IPV6);
+        matches.add(new MatchMetadata(extIpv6SubnetMetadata, MetaDataUtil.METADATA_MASK_VRFID));
+        matches.add(new MatchIpv6Source(tenantIpv6SubnetCidr));
+
+        List<ActionInfo> listActionInfo = new ArrayList<>();
+        ArrayList<InstructionInfo> instructions = new ArrayList<>();
+        listActionInfo.add(new ActionNxLoadInPort(BigInteger.ZERO));
+        listActionInfo.add(new ActionNxResubmit(NwConstants.L3_FIB_TABLE));
+        instructions.add(new InstructionApplyActions(listActionInfo));
+
+        String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.NAPT_PFIB_TABLE, routerId);
+        flowRef += NatConstants.FLOWID_SEPARATOR + tenantIpv6SubnetCidr + ".Outbound";
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.NAPT_PFIB_TABLE, flowRef,
+                NatConstants.SNAT_TRK_FLOW_PRIORITY,
+                flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
+    }
+
+    private void removeIpv6NaptPfibOutboundFlow(TypedReadWriteTransaction<Configuration> confTx,
+            String tenantIpv6SubnetCidr, BigInteger dpnId, long routerId)
+                    throws ExecutionException, InterruptedException {
+        LOG.debug("removeIpv6NaptPfibOutboundFlow : called for NAPTSwitch {}, routerId {}, tenantIPv6Cidr {}",
+                dpnId, routerId, tenantIpv6SubnetCidr);
+        String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.NAPT_PFIB_TABLE, routerId);
+        flowRef += NatConstants.FLOWID_SEPARATOR + tenantIpv6SubnetCidr + ".Outbound";
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.NAPT_PFIB_TABLE, flowRef);
+    }
+
+    private void addIpv6NaptInboundFibEntry(TypedReadWriteTransaction<Configuration> confTx, long extSubnetId,
+            String tenantIpv6SubnetCidr, BigInteger extIpv6SubnetMetadata, BigInteger dpnId, long routerId) {
+        LOG.debug("addIpv6NaptInboundFibEntry : called for NAPTSwitch {}, routerId {}, tenantIPv6Cidr {}",
+                dpnId, routerId, tenantIpv6SubnetCidr);
+        List<MatchInfo> matches = new ArrayList<>();
+        matches.add(MatchEthernetType.IPV6);
+        if (extSubnetId == NatConstants.INVALID_ID) {
+            LOG.error("installIpv6NaptInboundFibEntry: external subnet id is invalid.");
+            return;
+        }
+        matches.add(new MatchMetadata(extIpv6SubnetMetadata, MetaDataUtil.METADATA_MASK_VRFID));
+        matches.add(new MatchIpv6Destination(tenantIpv6SubnetCidr));
+
+        List<InstructionInfo> instructions = new ArrayList<>();
+        instructions.add(new InstructionGotoTable(NwConstants.INBOUND_NAPT_TABLE));
+
+        String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.L3_FIB_TABLE, routerId);
+        flowRef += NatConstants.FLOWID_SEPARATOR + tenantIpv6SubnetCidr + ".Inbound";
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.L3_FIB_TABLE, flowRef,
+                NatConstants.SNAT_FIB_FLOW_PRIORITY, flowRef,
+                NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
+    }
+
+    private void removeIpv6NaptInboundFibEntry(TypedReadWriteTransaction<Configuration> confTx,
+            String tenantIpv6SubnetCidr, BigInteger dpnId, long routerId)
+                    throws ExecutionException, InterruptedException {
+        LOG.debug("removeIpv6NaptInboundFibEntry : called for NAPTSwitch {}, routerId {}, tenantIPv6Cidr {}",
+                dpnId, routerId, tenantIpv6SubnetCidr);
+        String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.L3_FIB_TABLE, routerId);
+        flowRef += NatConstants.FLOWID_SEPARATOR + tenantIpv6SubnetCidr + ".Inbound";
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.L3_FIB_TABLE, flowRef);
+    }
+
+    private void addIpv6NaptInboundNaptFlow(TypedReadWriteTransaction<Configuration> confTx, long extSubnetId,
+            String tenantIpv6SubnetCidr, BigInteger extIpv6SubnetMetadata, BigInteger dpnId, long routerId,
+            BigInteger routerMetadata) {
+        LOG.debug("addIpv6NaptInboundNaptFlow : called for switch {}, routerId {}, tenantIPv6Cidr {}",
+                dpnId, routerId, tenantIpv6SubnetCidr);
+        List<MatchInfoBase> matches = new ArrayList<>();
+        matches.add(MatchEthernetType.IPV6);
+        if (extSubnetId == NatConstants.INVALID_ID) {
+            LOG.error("installIpv6NaptInboundNaptFlow : external subnet id is invalid.");
+            return;
+        }
+        matches.add(new MatchMetadata(extIpv6SubnetMetadata, MetaDataUtil.METADATA_MASK_VRFID));
+
+        matches.add(new MatchIpv6Destination(tenantIpv6SubnetCidr));
+
+        List<InstructionInfo> instructions = new ArrayList<>();
+        instructions.add(new InstructionGotoTable(NwConstants.NAPT_PFIB_TABLE));
+        instructions.add(new InstructionWriteMetadata(routerMetadata, MetaDataUtil.METADATA_MASK_VRFID));
+
+        String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.INBOUND_NAPT_TABLE, routerId);
+        flowRef += NatConstants.FLOWID_SEPARATOR + tenantIpv6SubnetCidr + ".Inbound";
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.INBOUND_NAPT_TABLE, flowRef,
+                NatConstants.DEFAULT_TS_FLOW_PRIORITY,
+                flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
+    }
+
+    private void removeIpv6NaptInboundNaptFlow(TypedReadWriteTransaction<Configuration> confTx,
+            String tenantIpv6SubnetCidr,BigInteger dpnId, long routerId)
+                    throws ExecutionException, InterruptedException {
+        LOG.debug("removeIpv6NaptInboundNaptFlow : called for switch {}, routerId {}, tenantIPv6Cidr {}",
+                dpnId, routerId, tenantIpv6SubnetCidr);
+        String flowRef = NatUtil.getIpv6FlowRef(dpnId, NwConstants.INBOUND_NAPT_TABLE, routerId);
+        flowRef += NatConstants.FLOWID_SEPARATOR + tenantIpv6SubnetCidr + ".Inbound";
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.INBOUND_NAPT_TABLE, flowRef);
+    }
+}
index d69dd575f07a01ffeea35a9a2a4fc69118c5d0ee..a5a56ebef7a4572b621671eb70ae6d1fb661fbd4 100644 (file)
@@ -17,6 +17,7 @@ public interface NatConstants {
     String NAPT_FLOW_NAME = "SNAT";
     BigInteger COOKIE_NAPT_BASE = new BigInteger("8000000", 16);
     String NAPT_FLOWID_PREFIX = "SNAT.";
+    String IPV6_FLOWID_PREFIX = "IPv6.";
     String FLOWID_SEPARATOR = ".";
     String COLON_SEPARATOR = ":";
     int DEFAULT_NAPT_IDLE_TIMEOUT = 300;
index af10252d8e25674cf1b34a63f1e121687822aa31..d9708410cb36eaa96a0588892ca177b2ac5cbed5 100644 (file)
@@ -56,6 +56,7 @@ import org.opendaylight.genius.mdsalutil.GroupEntity;
 import org.opendaylight.genius.mdsalutil.InstructionInfo;
 import org.opendaylight.genius.mdsalutil.MDSALUtil;
 import org.opendaylight.genius.mdsalutil.MatchInfo;
+import org.opendaylight.genius.mdsalutil.MatchInfoBase;
 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
 import org.opendaylight.genius.mdsalutil.NwConstants;
 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
@@ -106,6 +107,9 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.BridgeRefInfo;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.bridge.ref.info.BridgeRefEntry;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.meta.rev160406.bridge.ref.info.BridgeRefEntryKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeGre;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpidFromInterfaceOutput;
@@ -118,6 +122,8 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.dpn.endpoints.dpn.teps.info.TunnelEndPoints;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupTypes;
@@ -2588,4 +2594,64 @@ public final class NatUtil {
         return null;
 
     }
+
+    public static void addFlow(TypedWriteTransaction<Configuration> confTx, IMdsalApiManager mdsalManager,
+            BigInteger dpId, short tableId, String flowId, int priority, String flowName, BigInteger cookie,
+            List<? extends MatchInfoBase> matches, List<InstructionInfo> instructions) {
+        FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, flowId, priority, flowName,
+                NatConstants.DEFAULT_IDLE_TIMEOUT, NatConstants.DEFAULT_IDLE_TIMEOUT, cookie, matches,
+                instructions);
+        LOG.trace("syncFlow : Installing DpnId {}, flowId {}", dpId, flowId);
+        mdsalManager.addFlow(confTx, flowEntity);
+    }
+
+    public static void removeFlow(TypedReadWriteTransaction<Configuration> confTx, IMdsalApiManager mdsalManager,
+            BigInteger dpId, short tableId, String flowId) throws ExecutionException, InterruptedException {
+        LOG.trace("syncFlow : Removing Acl Flow DpnId {}, flowId {}", dpId, flowId);
+        mdsalManager.removeFlow(confTx, dpId, flowId, tableId);
+    }
+
+    public static String getIpv6FlowRef(BigInteger dpnId, short tableId, long routerID) {
+        return new StringBuilder().append(NatConstants.IPV6_FLOWID_PREFIX).append(dpnId).append(NatConstants
+                .FLOWID_SEPARATOR).append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).toString();
+    }
+
+    public static String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId,
+                                                ItmRpcService itmManager) {
+        Class<? extends TunnelTypeBase> tunType = TunnelTypeVxlan.class;
+        RpcResult<GetTunnelInterfaceNameOutput> rpcResult;
+        try {
+            Future<RpcResult<GetTunnelInterfaceNameOutput>> result = itmManager
+                    .getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder().setSourceDpid(srcDpId)
+                            .setDestinationDpid(dstDpId).setTunnelType(tunType).build());
+            rpcResult = result.get();
+            if (!rpcResult.isSuccessful()) {
+                tunType = TunnelTypeGre.class ;
+                result = itmManager.getTunnelInterfaceName(new GetTunnelInterfaceNameInputBuilder()
+                        .setSourceDpid(srcDpId)
+                        .setDestinationDpid(dstDpId)
+                        .setTunnelType(tunType)
+                        .build());
+                rpcResult = result.get();
+                if (!rpcResult.isSuccessful()) {
+                    LOG.warn("getTunnelInterfaceName : RPC Call to getTunnelInterfaceId returned with Errors {}",
+                            rpcResult.getErrors());
+                } else {
+                    return rpcResult.getResult().getInterfaceName();
+                }
+                LOG.warn("getTunnelInterfaceName : RPC Call to getTunnelInterfaceId returned with Errors {}",
+                        rpcResult.getErrors());
+            } else {
+                return rpcResult.getResult().getInterfaceName();
+            }
+        } catch (InterruptedException | ExecutionException | NullPointerException e) {
+            LOG.error("getTunnelInterfaceName : Exception when getting tunnel interface Id for tunnel "
+                    + "between {} and {}", srcDpId, dstDpId);
+        }
+        return null;
+    }
+
+    public static String getIpv6JobKey(String routerName) {
+        return "Ipv6." + routerName;
+    }
 }
index aefb091ccf15ab8cda9248dbd01967f4a9c007e1..2a4bb463fa4b7eca49c0fb3a606c5764c3375dfc 100644 (file)
@@ -7,12 +7,19 @@
  */
 package org.opendaylight.netvirt.natservice.internal;
 
+import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
+
+import java.util.Objects;
+
 import javax.annotation.PostConstruct;
 import javax.inject.Inject;
 import javax.inject.Singleton;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
+import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
+import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
 import org.opendaylight.netvirt.natservice.api.CentralizedSwitchScheduler;
 import org.opendaylight.netvirt.natservice.api.SnatServiceManager;
 import org.opendaylight.serviceutils.upgrade.UpgradeState;
@@ -31,6 +38,7 @@ public class SnatExternalRoutersListener extends AsyncDataTreeChangeListenerBase
     private static final Logger LOG = LoggerFactory.getLogger(SnatExternalRoutersListener.class);
 
     private final DataBroker dataBroker;
+    private final ManagedNewTransactionRunner txRunner;
     private final IdManagerService idManager;
     private final CentralizedSwitchScheduler  centralizedSwitchScheduler;
     private final NatMode natMode;
@@ -46,6 +54,7 @@ public class SnatExternalRoutersListener extends AsyncDataTreeChangeListenerBase
                                        final UpgradeState upgradeState) {
         super(Routers.class, SnatExternalRoutersListener.class);
         this.dataBroker = dataBroker;
+        this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
         this.idManager = idManager;
         this.centralizedSwitchScheduler = centralizedSwitchScheduler;
         this.upgradeState = upgradeState;
@@ -103,14 +112,17 @@ public class SnatExternalRoutersListener extends AsyncDataTreeChangeListenerBase
             LOG.error("update : external router event - Invalid routerId for routerName {}", routerName);
             return;
         }
-
-        // Check if its update on SNAT flag
-        boolean originalSNATEnabled = original.isEnableSnat();
-        boolean updatedSNATEnabled = update.isEnableSnat();
         LOG.info("update :called for router {} with originalSNATStatus {} and updatedSNATStatus {}",
-                routerName, originalSNATEnabled, updatedSNATEnabled);
+                routerName, original.isEnableSnat(), update.isEnableSnat());
         if (!upgradeState.isUpgradeInProgress()) {
-            centralizedSwitchScheduler.scheduleCentralizedSwitch(update);
+            centralizedSwitchScheduler.updateCentralizedSwitch(original, update);
+        }
+        if (!Objects.equals(original.getSubnetIds(), update.getSubnetIds())
+                || !Objects.equals(original.getExternalIps(), update.getExternalIps())) {
+            ListenableFutures.addErrorLogging(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
+                confTx -> natServiceManager.notify(confTx, update, original, null, null,
+                            SnatServiceManager.Action.SNAT_ROUTER_UPDATE)), LOG,
+                    "error handling external router update");
         }
     }
 
index ea536fdafab186840336f2c8502b8040a0ce4722..4c647c2f74fbb017fdb5417fc119bbf66818c3b8 100644 (file)
@@ -49,6 +49,7 @@ public class SnatServiceImplFactory extends AbstractLifecycle {
     private final NatDataUtil natDataUtil;
     private final DataTreeEventCallbackRegistrar eventCallbacks;
     private final NatOverVxlanUtil natOverVxlanUtil;
+    private final Ipv6SubnetFlowProgrammer ipv6SubnetFlowProgrammer;
 
     @Inject
     public SnatServiceImplFactory(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
@@ -65,7 +66,8 @@ public class SnatServiceImplFactory extends AbstractLifecycle {
                                   final IFibManager fibManager,
                                   final NatDataUtil natDataUtil,
                                   final DataTreeEventCallbackRegistrar eventCallbacks,
-                                  final NatOverVxlanUtil natOverVxlanUtil) {
+                                  final NatOverVxlanUtil natOverVxlanUtil,
+                                  final Ipv6SubnetFlowProgrammer ipv6SubnetFlowProgrammer) {
         this.dataBroker = dataBroker;
         this.mdsalManager = mdsalManager;
         this.itmManager = itmManager;
@@ -86,6 +88,7 @@ public class SnatServiceImplFactory extends AbstractLifecycle {
         this.natDataUtil = natDataUtil;
         this.eventCallbacks = eventCallbacks;
         this.natOverVxlanUtil = natOverVxlanUtil;
+        this.ipv6SubnetFlowProgrammer = ipv6SubnetFlowProgrammer;
     }
 
     @Override
@@ -108,6 +111,11 @@ public class SnatServiceImplFactory extends AbstractLifecycle {
         return null;
     }
 
+    public Ipv6ForwardingService createFlatVlanIpv6ServiceImpl() {
+        return new Ipv6ForwardingService(dataBroker, mdsalManager, itmManager, odlInterfaceRpcService,
+                idManager, naptSwitchSelector, interfaceManager, ipv6SubnetFlowProgrammer);
+    }
+
     @Nullable
     public AbstractSnatService createVxlanGreSnatServiceImpl() {
         if (natMode == NatMode.Conntrack) {
index 942b094f780459da0c4fe8577071c59bf8152f21..dcd828d6fc7d9faced8465e8fba15deed43881f5 100644 (file)
@@ -34,6 +34,7 @@ public class SnatServiceManagerImpl implements SnatServiceManager {
         if (flatVlaSnatServiceImpl != null) {
             addNatServiceListener(flatVlaSnatServiceImpl);
         }
+        addNatServiceListener(factory.createFlatVlanIpv6ServiceImpl());
         AbstractSnatService vxlanGreSnatServiceImpl = factory.createVxlanGreSnatServiceImpl();
         if (vxlanGreSnatServiceImpl != null) {
             addNatServiceListener(vxlanGreSnatServiceImpl);
@@ -74,6 +75,10 @@ public class SnatServiceManagerImpl implements SnatServiceManager {
                     result = snatServiceListener.removeSnat(confTx, router, primarySwitchId, dpnId);
                     break;
 
+                case SNAT_ROUTER_UPDATE:
+                    result = snatServiceListener.handleRouterUpdate(confTx, oldRouter, router);
+                    break;
+
                 //Enables or disables flows to send the traffic to the NAT tables in NAPT switch and
                 //the flows to send the traffic to the NAPT switch from a NON-NAPT switch.
                 case CNT_ROUTER_ALL_SWITCH_ENBL:
index c13e2c121d497b205427b39e08e180608936bddc..8af7b6ecfe625464f781e4aae35f0f216a6ab17c 100644 (file)
@@ -276,8 +276,8 @@ public class VxlanGreConntrackBasedSnatService extends ConntrackBasedSnatService
         instructionInfo.add(new InstructionApplyActions(listActionInfo));
 
         String flowRef = getFlowRef(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, routerId) + "trkest";
-        addFlow(confTx, dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef, NatConstants.SNAT_TRK_FLOW_PRIORITY, flowRef,
-                NwConstants.COOKIE_SNAT_TABLE, matches, instructionInfo);
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef,
+                NatConstants.SNAT_TRK_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructionInfo);
 
     }
 
@@ -287,7 +287,7 @@ public class VxlanGreConntrackBasedSnatService extends ConntrackBasedSnatService
             + "routerId {}", dpnId, routerId);
 
         String flowRef = getFlowRef(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, routerId) + "trkest";
-        removeFlow(confTx, dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef);
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef);
 
     }
 
@@ -323,8 +323,8 @@ public class VxlanGreConntrackBasedSnatService extends ConntrackBasedSnatService
         List<InstructionInfo> instructions = new ArrayList<>();
         instructions.add(new InstructionApplyActions(actionsInfos));
         String flowRef = getFlowRef(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, routerId);
-        addFlow(confTx, dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef,  NatConstants.SNAT_NEW_FLOW_PRIORITY,
-                flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef,
+                NatConstants.SNAT_NEW_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
     }
 
     protected void removeOutboundTblEntryForVxlanGre(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
@@ -338,7 +338,7 @@ public class VxlanGreConntrackBasedSnatService extends ConntrackBasedSnatService
         }
         //The logic now handle only one external IP per router, others if present will be ignored.
         String flowRef = getFlowRef(dpnId, NwConstants.OUTBOUND_NAPT_TABLE, routerId);
-        removeFlow(confTx, dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef);
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.OUTBOUND_NAPT_TABLE, flowRef);
     }
 
     protected void addNaptPfibFlowForVxlanGre(TypedWriteTransaction<Configuration> confTx, Routers routers,
@@ -355,8 +355,8 @@ public class VxlanGreConntrackBasedSnatService extends ConntrackBasedSnatService
         listActionInfo.add(new ActionNxResubmit(NwConstants.L3_FIB_TABLE));
         instructions.add(new InstructionApplyActions(listActionInfo));
         String flowRef = getFlowRef(dpnId, NwConstants.NAPT_PFIB_TABLE, extNetVpnId);
-        addFlow(confTx, dpnId, NwConstants.NAPT_PFIB_TABLE, flowRef, NatConstants.SNAT_TRK_FLOW_PRIORITY,
-                flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.NAPT_PFIB_TABLE, flowRef,
+                NatConstants.SNAT_TRK_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
     }
 
     protected void removeNaptPfibFlowForVxlanGre(TypedReadWriteTransaction<Configuration> confTx, Routers routers,
@@ -364,7 +364,7 @@ public class VxlanGreConntrackBasedSnatService extends ConntrackBasedSnatService
         LOG.info("installNaptPfibFlowForVxlanGre: Install Napt preFibFlow on dpId {} with matching extNetVpnId {} "
             + "for router {}", dpnId, extNetVpnId, routers.getRouterName());
         String flowRef = getFlowRef(dpnId, NwConstants.NAPT_PFIB_TABLE, extNetVpnId);
-        removeFlow(confTx, dpnId, NwConstants.NAPT_PFIB_TABLE, flowRef);
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.NAPT_PFIB_TABLE, flowRef);
     }
 
     protected void addInboundEntryForVxlanGre(TypedWriteTransaction<Configuration> confTx, BigInteger dpnId,
@@ -395,8 +395,8 @@ public class VxlanGreConntrackBasedSnatService extends ConntrackBasedSnatService
         List<InstructionInfo> instructions = new ArrayList<>();
         instructions.add(new InstructionApplyActions(actionsInfos));
         String flowRef = getFlowRef(dpnId, NwConstants.INBOUND_NAPT_TABLE, routerId);
-        addFlow(confTx, dpnId, NwConstants.INBOUND_NAPT_TABLE, flowRef, NatConstants.DEFAULT_TS_FLOW_PRIORITY, flowRef,
-                NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.INBOUND_NAPT_TABLE, flowRef,
+                NatConstants.DEFAULT_TS_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
     }
 
     protected void removeInboundEntryForVxlanGre(TypedReadWriteTransaction<Configuration> confTx, BigInteger dpnId,
@@ -410,7 +410,7 @@ public class VxlanGreConntrackBasedSnatService extends ConntrackBasedSnatService
         }
 
         String flowRef = getFlowRef(dpnId, NwConstants.INBOUND_NAPT_TABLE, routerId);
-        removeFlow(confTx, dpnId, NwConstants.INBOUND_NAPT_TABLE, flowRef);
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.INBOUND_NAPT_TABLE, flowRef);
     }
 
     protected void addTerminatingServiceTblEntryForVxlanGre(TypedWriteTransaction<Configuration> confTx,
@@ -439,8 +439,8 @@ public class VxlanGreConntrackBasedSnatService extends ConntrackBasedSnatService
         List<InstructionInfo> instructions = new ArrayList<>();
         instructions.add(new InstructionApplyActions(actionsInfos));
         String flowRef = getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId.longValue());
-        addFlow(confTx, dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, flowRef, NatConstants.DEFAULT_TS_FLOW_PRIORITY,
-            flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, flowRef,
+                NatConstants.DEFAULT_TS_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
 
     }
 
@@ -450,7 +450,7 @@ public class VxlanGreConntrackBasedSnatService extends ConntrackBasedSnatService
             + "Terminating Service Table for switch {}, routerId {}", dpnId, routerId);
 
         String flowRef = getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId.longValue());
-        removeFlow(confTx, dpnId,  NwConstants.INTERNAL_TUNNEL_TABLE, flowRef);
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId,  NwConstants.INTERNAL_TUNNEL_TABLE, flowRef);
 
     }
 
@@ -459,7 +459,7 @@ public class VxlanGreConntrackBasedSnatService extends ConntrackBasedSnatService
         String routerName, BigInteger primarySwitchId) {
         LOG.debug("installSnatMissEntry : Installing SNAT miss entry in switch {}", dpnId);
         List<ActionInfo> listActionInfoPrimary = new ArrayList<>();
-        String ifNamePrimary = getTunnelInterfaceName(dpnId, primarySwitchId);
+        String ifNamePrimary = NatUtil.getTunnelInterfaceName(dpnId, primarySwitchId, itmManager);
         List<BucketInfo> listBucketInfo = new ArrayList<>();
         if (ifNamePrimary != null) {
             LOG.debug("installSnatMissEntry : On Non- Napt switch , Primary Tunnel interface is {}", ifNamePrimary);
@@ -496,8 +496,9 @@ public class VxlanGreConntrackBasedSnatService extends ConntrackBasedSnatService
         List<InstructionInfo> instructions = new ArrayList<>();
         instructions.add(new InstructionApplyActions(actionsInfo));
         String flowRef = getFlowRef(dpnId, NwConstants.PSNAT_TABLE, routerId);
-        addFlow(confTx, dpnId, NwConstants.PSNAT_TABLE, flowRef,  NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef,
-                NwConstants.COOKIE_SNAT_TABLE, matches, instructions);
+        NatUtil.addFlow(confTx, mdsalManager, dpnId, NwConstants.PSNAT_TABLE, flowRef,
+                NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef, NwConstants.COOKIE_SNAT_TABLE, matches,
+                instructions);
     }
 
     @Override
@@ -506,7 +507,7 @@ public class VxlanGreConntrackBasedSnatService extends ConntrackBasedSnatService
         LOG.debug("installSnatMissEntry : Removing SNAT miss entry in switch {}", dpnId);
 
         String flowRef = getFlowRef(dpnId, NwConstants.PSNAT_TABLE, routerId);
-        removeFlow(confTx, dpnId, NwConstants.PSNAT_TABLE, flowRef);
+        NatUtil.removeFlow(confTx, mdsalManager, dpnId, NwConstants.PSNAT_TABLE, flowRef);
     }
 
 }
index 2ff6504c53b0b57a7c89e1a658a0d8a53b971f2b..9c9406b5aa0be6fe8027065d12aa2cafa3052dfe 100644 (file)
@@ -141,6 +141,11 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
             <version>${infrautils.version}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+               <groupId>org.opendaylight.genius</groupId>
+               <artifactId>ipv6util-api</artifactId>
+               <version>${genius.version}</version>
+        </dependency>
     </dependencies>
 
     <build>
index 3f9ee75d05e464e3ea2f3e950856374a9a15bfa5..dab32fe9729fc9212d3dbc34136b2d169812725b 100644 (file)
@@ -11,9 +11,12 @@ package org.opendaylight.netvirt.neutronvpn;
 import com.google.common.util.concurrent.JdkFutureAdapters;
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import java.math.BigInteger;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 import javax.annotation.Nullable;
@@ -23,10 +26,12 @@ import javax.inject.Inject;
 import javax.inject.Singleton;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.genius.arputil.api.ArpConstants;
+import org.opendaylight.genius.mdsalutil.NWUtil;
 import org.opendaylight.infrautils.utils.concurrent.ListenableFutures;
 import org.opendaylight.netvirt.elanmanager.api.IElanService;
 import org.opendaylight.netvirt.vpnmanager.api.ICentralizedSwitchProvider;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
@@ -35,11 +40,16 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.Se
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpRequestInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.interfaces.InterfaceAddress;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.interfaces.InterfaceAddressBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.util.rev170210.Ipv6NdUtilService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.util.rev170210.SendNeighborSolicitationInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.util.rev170210.SendNeighborSolicitationInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.util.rev170210.SendNeighborSolicitationOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.router.ExternalGatewayInfo;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;
+import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -55,16 +65,19 @@ public class NeutronSubnetGwMacResolver {
     private final NeutronvpnUtils neutronvpnUtils;
     private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(
             new ThreadFactoryBuilder().setNameFormat("Gw-Mac-Res").build());
+    private final Ipv6NdUtilService ipv6NdUtilService;
 
     @Inject
     public NeutronSubnetGwMacResolver(final DataBroker broker,
             final OdlArputilService arputilService, final IElanService elanService,
-            final ICentralizedSwitchProvider cswitchProvider, final NeutronvpnUtils neutronvpnUtils) {
+            final ICentralizedSwitchProvider cswitchProvider, final NeutronvpnUtils neutronvpnUtils,
+            final Ipv6NdUtilService ipv6NdUtilService) {
         this.broker = broker;
         this.arpUtilService = arputilService;
         this.elanService = elanService;
         this.cswitchProvider = cswitchProvider;
         this.neutronvpnUtils = neutronvpnUtils;
+        this.ipv6NdUtilService = ipv6NdUtilService;
     }
 
     // TODO Clean up the exception handling
@@ -135,7 +148,14 @@ public class NeutronSubnetGwMacResolver {
             Uuid subnetId = fixIp.getSubnetId();
             IpAddress srcIpAddress = fixIp.getIpAddress();
             IpAddress dstIpAddress = getExternalGwIpAddress(subnetId);
-            sendArpRequest(srcIpAddress, dstIpAddress, macAddress, extInterface);
+            String srcIpAddressString = srcIpAddress.stringValue();
+            String dstIpAddressString = dstIpAddress.stringValue();
+            if (NWUtil.isIpv4Address(srcIpAddressString)) {
+                sendArpRequest(srcIpAddress, dstIpAddress, macAddress, extInterface);
+            } else {
+                sendNeighborSolication(new Ipv6Address(srcIpAddressString),macAddress,
+                        new Ipv6Address(dstIpAddressString), extInterface);
+            }
         }
 
     }
@@ -165,6 +185,31 @@ public class NeutronSubnetGwMacResolver {
         }
     }
 
+    private void sendNeighborSolication(Ipv6Address srcIpv6Address,
+            MacAddress srcMac, Ipv6Address dstIpv6Address, String interfaceName) {
+        List<org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6
+            .nd.util.rev170210.interfaces.InterfaceAddress> interfaceAddresses = new ArrayList<>();
+        interfaceAddresses.add(new org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6
+                .nd.util.rev170210.interfaces.InterfaceAddressBuilder()
+            .setInterface(interfaceName)
+            .setSrcIpAddress(srcIpv6Address)
+            .setSrcMacAddress(new PhysAddress(srcMac.getValue())).build());
+        SendNeighborSolicitationInput input = new SendNeighborSolicitationInputBuilder()
+                .setInterfaceAddress(interfaceAddresses).setTargetIpAddress(dstIpv6Address)
+                .build();
+        try {
+            Future<RpcResult<SendNeighborSolicitationOutput>> result = ipv6NdUtilService
+                    .sendNeighborSolicitation(input);
+            RpcResult<SendNeighborSolicitationOutput> rpcResult = result.get();
+            if (!rpcResult.isSuccessful()) {
+                LOG.error("sendNeighborSolicitationToOfGroup: RPC Call failed for input={} and Errors={}", input,
+                        rpcResult.getErrors());
+            }
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.error("Failed to send NS packet to ELAN group, input={}", input, e);
+        }
+    }
+
     @Nullable
     private Port getRouterExtGatewayPort(Router router) {
         if (router == null) {
index 66a75590f11f24ed81a33d3e3ebfd000c38a4d92..49ce8e1e7d688a648b9c53eabd9bf70eba3a8e2e 100644 (file)
@@ -39,6 +39,8 @@
                    interface="org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.rpc.rev160201.VpnRpcService" />
   <odl:rpc-service id="odlArputilService"
                    interface="org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.OdlArputilService" />
+  <odl:rpc-service id="ipv6NdUtilService"
+                   interface="org.opendaylight.yang.gen.v1.urn.opendaylight.genius.ipv6.nd.util.rev170210.Ipv6NdUtilService"/>
 
   <odl:clustered-app-config id="neutronvpnConfig"
                             binding-class="org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.config.rev160806.NeutronvpnConfig"
index c486d3a0ed6deb4e0be3b6897bf4eaec89e33906..cccf099d085700e51aa04a38d72b379510e8f9ab 100644 (file)
@@ -1177,6 +1177,32 @@ public final class VpnUtil {
         return routerName;
     }
 
+    @Nullable
+    public String getAssociatedExternalSubnet(String extIp) {
+        InstanceIdentifier<ExtRouters> extRouterInstanceIndentifier =
+                InstanceIdentifier.builder(ExtRouters.class).build();
+        Optional<ExtRouters> extRouterData = read(LogicalDatastoreType.CONFIGURATION, extRouterInstanceIndentifier);
+        if (!extRouterData.isPresent()) {
+            return null;
+        }
+        for (Routers routerData : requireNonNullElse(extRouterData.get().getRouters(),
+                Collections.<Routers>emptyList())) {
+            List<ExternalIps> externalIps = requireNonNullElse(routerData.getExternalIps(), emptyList());
+            for (ExternalIps externalIp : externalIps) {
+                Subnet neutronSubnet = neutronVpnService.getNeutronSubnet(externalIp.getSubnetId());
+                if (neutronSubnet == null) {
+                    LOG.warn("Failed to retrieve subnet {} referenced by router {}",
+                            externalIp.getSubnetId(), routerData);
+                    continue;
+                }
+                if (NWUtil.isIpAddressInRange(IpAddressBuilder.getDefaultInstance(extIp), neutronSubnet.getCidr())) {
+                    return neutronSubnet.getUuid().getValue();
+                }
+            }
+        }
+        return null;
+    }
+
     static InstanceIdentifier<Routers> buildRouterIdentifier(String routerId) {
         return InstanceIdentifier.builder(ExtRouters.class).child(Routers.class, new RoutersKey(routerId)).build();
     }
index bd1c96a8dcb56751b51f8df94fb2963fa7c13898..8f24c70145eb544f1b7921883ae4853b7990ed65 100644 (file)
@@ -107,6 +107,14 @@ public abstract class AbstractIpLearnNotificationHandler {
                       srcMac.getValue(), srcIpToQuery, srcInterface);
             VpnPortipToPort vpnPortipToPort =
                     vpnUtil.getNeutronPortFromVpnPortFixedIp(vpnName, srcIpToQuery);
+            // Check if this IP belongs to  external network
+            if (vpnPortipToPort == null) {
+                String extSubnetId = vpnUtil.getAssociatedExternalSubnet(srcIpToQuery);
+                if (extSubnetId != null) {
+                    vpnPortipToPort =
+                            vpnUtil.getNeutronPortFromVpnPortFixedIp(extSubnetId, srcIpToQuery);
+                }
+            }
             if (vpnPortipToPort != null) {
                 /* This is a well known neutron port and so should be ignored
                  * from being discovered...unless it is an Octavia VIP
index 2f85f8d04c56b7341e3e73baba44411df8230845..5093ccd1d8cc64afc174a889a827565690b675df 100644 (file)
@@ -303,6 +303,13 @@ public class LearntVpnVipToPortEventProcessor
                 }
             }
 
+            // Check if this IP belongs to  external network
+            String extSubnetId = vpnUtil.getAssociatedExternalSubnet(ip);
+            if (extSubnetId != null) {
+                LOG.info("The IP belongs to extenal subnet {} ", extSubnetId);
+                return extSubnetId;
+            }
+
             return null;
         }
     }