NETVIRT-1063 SNAT flows fails to install sporadically(conntrack SNAT)
[netvirt.git] / vpnservice / natservice / natservice-impl / src / main / java / org / opendaylight / netvirt / natservice / internal / AbstractSnatService.java
index 3efdd8582c6153455afe1bd235f5aa3afc2fcc2c..0476f789ba5fa14a60eb3a28737e76aa1b782977 100644 (file)
@@ -12,9 +12,7 @@ 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.controller.md.sal.binding.api.WriteTransaction;
 import org.opendaylight.genius.mdsalutil.ActionInfo;
 import org.opendaylight.genius.mdsalutil.BucketInfo;
 import org.opendaylight.genius.mdsalutil.FlowEntity;
@@ -26,6 +24,7 @@ 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;
+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;
@@ -34,8 +33,8 @@ import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
 import org.opendaylight.genius.mdsalutil.matches.MatchEthernetType;
 import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
+import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
 import org.opendaylight.netvirt.natservice.api.SnatServiceListener;
-import org.opendaylight.netvirt.vpnmanager.api.IVpnManager;
 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.genius.idmanager.rev160406.AllocateIdInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdInputBuilder;
@@ -56,32 +55,34 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public abstract class AbstractSnatService implements SnatServiceListener {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractSnatService.class);
+
+    static final int LOAD_START = mostSignificantBit(MetaDataUtil.METADATA_MASK_SH_FLAG.intValue());
+    static final int LOAD_END = mostSignificantBit(MetaDataUtil.METADATA_MASK_VRFID.intValue() | MetaDataUtil
+            .METADATA_MASK_SH_FLAG.intValue());
 
     protected final DataBroker dataBroker;
     protected final IMdsalApiManager mdsalManager;
     protected final IdManagerService idManager;
-    protected final NaptManager naptManager;
     protected final NAPTSwitchSelector naptSwitchSelector;
     protected final ItmRpcService itmManager;
     protected final OdlInterfaceRpcService interfaceManager;
-    private final IVpnManager vpnManager;
-    private static final Logger LOG = LoggerFactory.getLogger(AbstractSnatService.class);
 
-    public AbstractSnatService(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
+    protected AbstractSnatService(final DataBroker dataBroker, final IMdsalApiManager mdsalManager,
             final ItmRpcService itmManager,
             final OdlInterfaceRpcService interfaceManager,
             final IdManagerService idManager,
-            final NaptManager naptManager,
-            final NAPTSwitchSelector naptSwitchSelector,
-            final IVpnManager vpnManager) {
+            final NAPTSwitchSelector naptSwitchSelector) {
         this.dataBroker = dataBroker;
         this.mdsalManager = mdsalManager;
         this.itmManager = itmManager;
         this.interfaceManager = interfaceManager;
         this.idManager = idManager;
-        this.naptManager = naptManager;
         this.naptSwitchSelector = naptSwitchSelector;
-        this.vpnManager = vpnManager;
+    }
+
+    protected DataBroker getDataBroker() {
+        return dataBroker;
     }
 
     public void init() {
@@ -89,28 +90,25 @@ public abstract class AbstractSnatService implements SnatServiceListener {
     }
 
     public void close() {
-
         LOG.debug("AbstractSnatService Closed");
     }
 
     @Override
     public boolean handleSnatAllSwitch(Routers routers, BigInteger primarySwitchId,  int addOrRemove) {
-        LOG.debug("AbstractSnatService : Handle Snat in all switches");
+        LOG.debug("handleSnatAllSwitch : Handle Snat in all switches");
         String routerName = routers.getRouterName();
-        installRouterGwFlows(routers, primarySwitchId, addOrRemove);
         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.
          */
         handleSnat(routers, primarySwitchId, primarySwitchId, addOrRemove);
-        if (switches != null) {
-            for (BigInteger dpnId : switches) {
-                if (primarySwitchId != dpnId) {
-                    handleSnat(routers, primarySwitchId, dpnId, addOrRemove);
-                }
+        for (BigInteger dpnId : switches) {
+            if (primarySwitchId != dpnId) {
+                handleSnat(routers, primarySwitchId, dpnId, addOrRemove);
             }
         }
+
         return true;
     }
 
@@ -119,11 +117,11 @@ public abstract class AbstractSnatService implements SnatServiceListener {
 
         // Handle non NAPT switches and NAPT switches separately
         if (!dpnId.equals(primarySwitchId)) {
-            LOG.debug("AbstractSnatService : Handle non NAPT switch {}", dpnId);
+            LOG.debug("handleSnat : Handle non NAPT switch {}", dpnId);
             installSnatCommonEntriesForNonNaptSwitch(routers, primarySwitchId, dpnId, addOrRemove);
             installSnatSpecificEntriesForNonNaptSwitch(routers, dpnId, addOrRemove);
         } else {
-            LOG.debug("AbstractSnatService : Handle NAPT switch {}", dpnId);
+            LOG.debug("handleSnat : Handle NAPT switch {}", dpnId);
             installSnatCommonEntriesForNaptSwitch(routers, dpnId, addOrRemove);
             installSnatSpecificEntriesForNaptSwitch(routers, dpnId, addOrRemove);
 
@@ -142,16 +140,37 @@ public abstract class AbstractSnatService implements SnatServiceListener {
                     routerId);
             return;
         }
+        //The logic now handle only one external IP per router, others if present will be ignored.
         String externalIp = externalIps.get(0).getIpAddress();
-        installInboundFibEntry(dpnId, externalIp, routerName, routerId, addOrRemove);
+        Uuid externalSubnetId = externalIps.get(0).getSubnetId();
+        long extSubnetId = NatConstants.INVALID_ID;
+        if (addOrRemove == NwConstants.ADD_FLOW) {
+            extSubnetId = NatUtil.getExternalSubnetVpnId(dataBroker,externalSubnetId);
+        }
+        installInboundFibEntry(dpnId, externalIp, routerName, routerId, extSubnetId, addOrRemove);
+        installInboundTerminatingServiceTblEntry(dpnId, routerId, routerName, externalIp, extSubnetId, addOrRemove);
     }
 
     protected void installSnatCommonEntriesForNonNaptSwitch(Routers routers, BigInteger primarySwitchId,
             BigInteger dpnId, int addOrRemove) {
         String routerName = routers.getRouterName();
         Long routerId = NatUtil.getVpnId(dataBroker, routerName);
+        List<ExternalIps> externalIps = routers.getExternalIps();
+        if (externalIps.isEmpty()) {
+            LOG.error("AbstractSnatService: installSnatCommonEntriesForNaptSwitch no externalIP present"
+                    + " for routerId {}",
+                    routerId);
+            return;
+        }
+        String externalIp = externalIps.get(0).getIpAddress();
+        Uuid externalSubnetId = externalIps.get(0).getSubnetId();
+        long extSubnetId = NatConstants.INVALID_ID;
+        if (addOrRemove == NwConstants.ADD_FLOW) {
+            extSubnetId = NatUtil.getExternalSubnetVpnId(dataBroker,externalSubnetId);
+        }
+
         installDefaultFibRouteForSNAT(dpnId, routerId, addOrRemove);
-        installSnatMissEntry(dpnId, routerId, routerName, primarySwitchId, addOrRemove);
+        installSnatMissEntry(dpnId, routerId, routerName, primarySwitchId, externalIp, extSubnetId, addOrRemove);
     }
 
     protected abstract void installSnatSpecificEntriesForNaptSwitch(Routers routers, BigInteger dpnId,
@@ -161,11 +180,10 @@ public abstract class AbstractSnatService implements SnatServiceListener {
             int addOrRemove);
 
     protected void installInboundFibEntry(BigInteger dpnId, String externalIp, String routerName, Long routerId,
-            int addOrRemove) {
-        List<MatchInfo> matches = new ArrayList<MatchInfo>();
+            long extSubnetId, int addOrRemove) {
+        List<MatchInfo> matches = new ArrayList<>();
         matches.add(MatchEthernetType.IPV4);
         if (addOrRemove == NwConstants.ADD_FLOW) {
-            Long extSubnetId = NatUtil.getVpnIdFromExternalSubnet(dataBroker, routerName, externalIp);
             if (extSubnetId == NatConstants.INVALID_ID) {
                 LOG.error("ConntrackBasedSnatService : installInboundFibEntry : external subnet id is invalid.");
                 return;
@@ -186,44 +204,28 @@ public abstract class AbstractSnatService implements SnatServiceListener {
                 NwConstants.COOKIE_SNAT_TABLE, matches, instructionInfo, addOrRemove);
     }
 
-    protected void installRouterGwFlows(Routers router, BigInteger primarySwitchId, int addOrRemove) {
-        WriteTransaction writeTx = dataBroker.newWriteOnlyTransaction();
-        List<ExternalIps> externalIps = router.getExternalIps();
-        List<String> externalIpsSting = new ArrayList<>();
-        for (ExternalIps externalIp : externalIps) {
-            externalIpsSting.add(externalIp.getIpAddress());
-        }
-        Uuid subnetVpnName = externalIps.get(0).getSubnetId();
-        vpnManager.setupRouterGwMacFlow(router.getRouterName(), router.getExtGwMacAddress(), primarySwitchId,
-                router.getNetworkId(), subnetVpnName.getValue(), writeTx, addOrRemove);
-        vpnManager.setupArpResponderFlowsToExternalNetworkIps(router.getRouterName(), externalIpsSting,
-                router.getExtGwMacAddress(), primarySwitchId,
-                router.getNetworkId(), writeTx, addOrRemove);
-        writeTx.submit();
-    }
-
     protected void installSnatMissEntry(BigInteger dpnId, Long routerId, String routerName, BigInteger primarySwitchId,
-            int addOrRemove) {
-        LOG.debug("AbstractSnatService : Installing SNAT miss entry in switch {}", dpnId);
+            String externalIp, long extSubnetId, int addOrRemove) {
+        LOG.debug("installSnatMissEntry : Installing SNAT miss entry in switch {}", dpnId);
         List<ActionInfo> listActionInfoPrimary = new ArrayList<>();
         String ifNamePrimary = getTunnelInterfaceName(dpnId, primarySwitchId);
         List<BucketInfo> listBucketInfo = new ArrayList<>();
         if (ifNamePrimary != null) {
-            LOG.debug("AbstractSnatService : On Non- Napt switch , Primary Tunnel interface is {}", ifNamePrimary);
+            LOG.debug("installSnatMissEntry : On Non- Napt switch , Primary Tunnel interface is {}", ifNamePrimary);
             listActionInfoPrimary = NatUtil.getEgressActionsForInterface(interfaceManager, ifNamePrimary, routerId);
         }
         BucketInfo bucketPrimary = new BucketInfo(listActionInfoPrimary);
         listBucketInfo.add(0, bucketPrimary);
-        LOG.debug("AbstractSnatService : installSnatMissEntry called for dpnId {} with primaryBucket {} ", dpnId,
+        LOG.debug("installSnatMissEntry : installSnatMissEntry called for dpnId {} with primaryBucket {} ", dpnId,
                 listBucketInfo.get(0));
         // Install the select group
         long groupId = createGroupId(getGroupIdKey(routerName));
         GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, routerName, GroupTypes.GroupAll,
                 listBucketInfo);
-        LOG.debug("AbstractSnatService : installing the SNAT to NAPT GroupEntity:{}", groupEntity);
+        LOG.debug("installSnatMissEntry : installing the SNAT to NAPT GroupEntity:{}", groupEntity);
         mdsalManager.installGroup(groupEntity);
         // Install miss entry pointing to group
-        LOG.debug("AbstractSnatService : buildSnatFlowEntity is called for dpId {}, routerName {} and groupId {}",
+        LOG.debug("installSnatMissEntry : buildSnatFlowEntity is called for dpId {}, routerName {} and groupId {}",
                 dpnId, routerName, groupId);
         List<MatchInfo> matches = new ArrayList<>();
         matches.add(new MatchEthernetType(0x0800L));
@@ -232,13 +234,63 @@ public abstract class AbstractSnatService implements SnatServiceListener {
 
         List<ActionInfo> actionsInfo = new ArrayList<>();
         actionsInfo.add(new ActionSetFieldTunnelId(BigInteger.valueOf(routerId)));
-        LOG.debug("AbstractSnatService : Setting the tunnel to the list of action infos {}", actionsInfo);
+        LOG.debug("installSnatMissEntry : Setting the tunnel to the list of action infos {}", actionsInfo);
         actionsInfo.add(new ActionGroup(groupId));
         List<InstructionInfo> instructions = new ArrayList<>();
         instructions.add(new InstructionApplyActions(actionsInfo));
         String flowRef = getFlowRef(dpnId, NwConstants.PSNAT_TABLE, routerId);
         syncFlow(dpnId, NwConstants.PSNAT_TABLE, flowRef,  NatConstants.DEFAULT_PSNAT_FLOW_PRIORITY, flowRef,
                 NwConstants.COOKIE_SNAT_TABLE, matches, instructions, addOrRemove);
+        //Install the FIB entry for traffic destined to SNAT IP in Non-NAPT switch.
+        matches = new ArrayList<>();
+        actionsInfo = new ArrayList<>();
+        matches.add(new MatchEthernetType(0x0800L));
+        if (addOrRemove == NwConstants.ADD_FLOW) {
+            if (extSubnetId == NatConstants.INVALID_ID) {
+                LOG.error("installSnatMissEntry : external subnet id is invalid.");
+                return;
+            }
+            actionsInfo.add(new ActionSetFieldTunnelId(BigInteger.valueOf(extSubnetId)));
+            matches.add(new MatchMetadata(MetaDataUtil.getVpnIdMetadata(extSubnetId),
+                    MetaDataUtil.METADATA_MASK_VRFID));
+        }
+        matches.add(new MatchIpv4Destination(externalIp, "32"));
+        LOG.debug("installSnatMissEntry : Setting the tunnel to the list of action infos {}", actionsInfo);
+        actionsInfo.add(new ActionGroup(groupId));
+        instructions = new ArrayList<>();
+        instructions.add(new InstructionApplyActions(actionsInfo));
+        flowRef = getFlowRef(dpnId, NwConstants.L3_FIB_TABLE, routerId);
+        flowRef = flowRef + "inboundmiss" + externalIp;
+        syncFlow(dpnId, NwConstants.L3_FIB_TABLE, flowRef,  NatConstants.SNAT_FIB_FLOW_PRIORITY, flowRef,
+                NwConstants.COOKIE_SNAT_TABLE, matches, instructions, addOrRemove);
+    }
+
+    protected void installInboundTerminatingServiceTblEntry(BigInteger dpnId, Long  routerId, String routerName,
+            String externalIp,  long extSubnetId, int addOrRemove) {
+        //Install the tunnel table entry in NAPT switch for inbound traffic to SNAP 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<>();
+        matches.add(MatchEthernetType.IPV4);
+        List<ActionInfo> actionsInfos = new ArrayList<>();
+        if (addOrRemove == NwConstants.ADD_FLOW) {
+            if (extSubnetId == NatConstants.INVALID_ID) {
+                LOG.error("installInboundTerminatingServiceTblEntry : external subnet id is invalid.");
+                return;
+            }
+            matches.add(new MatchTunnelId(BigInteger.valueOf(extSubnetId)));
+            ActionNxLoadMetadata actionLoadMeta = new ActionNxLoadMetadata(MetaDataUtil
+                    .getVpnIdMetadata(extSubnetId), 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 = getFlowRef(dpnId, NwConstants.INTERNAL_TUNNEL_TABLE, routerId.longValue());
+        flowRef = flowRef + "INBOUND";
+        syncFlow(dpnId,  NwConstants.INTERNAL_TUNNEL_TABLE, flowRef, NatConstants.SNAT_FIB_FLOW_PRIORITY, flowRef,
+                 NwConstants.COOKIE_SNAT_TABLE, matches, instructions, addOrRemove);
+
     }
 
     protected void installDefaultFibRouteForSNAT(BigInteger dpnId, Long extNetId, int addOrRemove) {
@@ -256,24 +308,6 @@ public abstract class AbstractSnatService implements SnatServiceListener {
                 NwConstants.COOKIE_SNAT_TABLE, matches, instructions, addOrRemove);
     }
 
-    protected BigInteger getPrimaryNaptSwitch(String routerName, long routerId) {
-        // Allocate Primary Napt Switch for this router
-        BigInteger primarySwitchId = NatUtil.getPrimaryNaptfromRouterId(dataBroker, routerId);
-        if (primarySwitchId != null && !primarySwitchId.equals(BigInteger.ZERO)) {
-            LOG.debug("AbstractSnatService : Primary NAPT switch with DPN ID {} is already elected for router",
-                    primarySwitchId, routerName);
-            return primarySwitchId;
-        }
-
-        primarySwitchId = naptSwitchSelector.selectNewNAPTSwitch(routerName);
-        LOG.debug("AbstractSnatService : Primary NAPT switch DPN ID {}", primarySwitchId);
-        if (primarySwitchId == null || primarySwitchId.equals(BigInteger.ZERO)) {
-            LOG.error("AbstractSnatService : Unable to to select the primary NAPT switch");
-        }
-
-        return primarySwitchId;
-    }
-
     protected String getFlowRef(BigInteger dpnId, short tableId, long routerID) {
         return new StringBuilder().append(NatConstants.NAPT_FLOWID_PREFIX).append(dpnId).append(NatConstants
                 .FLOWID_SEPARATOR).append(tableId).append(NatConstants.FLOWID_SEPARATOR).append(routerID).toString();
@@ -285,18 +319,18 @@ public abstract class AbstractSnatService implements SnatServiceListener {
         if (addOrRemove == NwConstants.DEL_FLOW) {
             FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, flowId, priority, flowName,
                     NatConstants.DEFAULT_IDLE_TIMEOUT, NatConstants.DEFAULT_IDLE_TIMEOUT, cookie, matches, null);
-            LOG.trace("Removing Acl Flow DpnId {}, flowId {}", dpId, flowId);
+            LOG.trace("syncFlow : Removing Acl Flow DpnId {}, flowId {}", dpId, flowId);
             mdsalManager.removeFlow(flowEntity);
         } else {
             FlowEntity flowEntity = MDSALUtil.buildFlowEntity(dpId, tableId, flowId, priority, flowName,
                     NatConstants.DEFAULT_IDLE_TIMEOUT, NatConstants.DEFAULT_IDLE_TIMEOUT, cookie, matches,
                     instructions);
-            LOG.trace("Installing DpnId {}, flowId {}", dpId, flowId);
+            LOG.trace("syncFlow : Installing DpnId {}, flowId {}", dpId, flowId);
             mdsalManager.installFlow(flowEntity);
         }
     }
 
-    private long createGroupId(String groupIdKey) {
+    protected long createGroupId(String groupIdKey) {
         AllocateIdInput getIdInput = new AllocateIdInputBuilder()
             .setPoolName(NatConstants.SNAT_IDPOOL_NAME).setIdKey(groupIdKey)
             .build();
@@ -305,17 +339,16 @@ public abstract class AbstractSnatService implements SnatServiceListener {
             RpcResult<AllocateIdOutput> rpcResult = result.get();
             return rpcResult.getResult().getIdValue();
         } catch (NullPointerException | InterruptedException | ExecutionException e) {
-            LOG.trace("",e);
+            LOG.error("createGroupId: Exception while creating group with key : {}",groupIdKey, e);
         }
         return 0;
     }
 
-    private String getGroupIdKey(String routerName) {
-        String groupIdKey = new String("snatmiss." + routerName);
-        return groupIdKey;
+    protected String getGroupIdKey(String routerName) {
+        return "snatmiss." + routerName;
     }
 
-    private String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId) {
+    protected String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId) {
         Class<? extends TunnelTypeBase> tunType = TunnelTypeVxlan.class;
         RpcResult<GetTunnelInterfaceNameOutput> rpcResult;
         try {
@@ -332,18 +365,24 @@ public abstract class AbstractSnatService implements SnatServiceListener {
                         .build());
                 rpcResult = result.get();
                 if (!rpcResult.isSuccessful()) {
-                    LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
+                    LOG.warn("getTunnelInterfaceName : RPC Call to getTunnelInterfaceId returned with Errors {}",
+                            rpcResult.getErrors());
                 } else {
                     return rpcResult.getResult().getInterfaceName();
                 }
-                LOG.warn("RPC Call to getTunnelInterfaceId returned with Errors {}", rpcResult.getErrors());
+                LOG.warn("getTunnelInterfaceName : RPC Call to getTunnelInterfaceId returned with Errors {}",
+                        rpcResult.getErrors());
             } else {
                 return rpcResult.getResult().getInterfaceName();
             }
         } catch (InterruptedException | ExecutionException | NullPointerException e) {
-            LOG.warn("AbstractSnatService : Exception when getting tunnel interface Id for tunnel between {} and  {}",
-                    srcDpId, dstDpId);
+            LOG.error("getTunnelInterfaceName : Exception when getting tunnel interface Id for tunnel "
+                    + "between {} and {}", srcDpId, dstDpId);
         }
         return null;
     }
-}
\ No newline at end of file
+
+    static int mostSignificantBit(int value) {
+        return 31 - Integer.numberOfLeadingZeros(value);
+    }
+}