Bug 5326 - endpoint can reach nodes outside internal domain 73/34273/9
authorTomas Cechvala <tcechval@cisco.com>
Thu, 11 Feb 2016 13:12:23 +0000 (14:12 +0100)
committerMartin Sunal <msunal@cisco.com>
Thu, 11 Feb 2016 22:36:10 +0000 (22:36 +0000)
Traffic of endpoints connected to OVS switch can be forwarded
to external domain.

Change-Id: Icdf23726a411ae7d22ed792f2f9cfa08cb95b1de
Signed-off-by: Tomas Cechvala <tcechval@cisco.com>
groupbasedpolicy/src/main/java/org/opendaylight/groupbasedpolicy/dto/IndexedTenant.java
renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/endpoint/EndpointManager.java
renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/flow/DestinationMapper.java
renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/flow/FlowTable.java
renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/flow/IngressNatMapper.java
renderers/ofoverlay/src/main/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/flow/PolicyEnforcer.java
renderers/ofoverlay/src/test/java/org/opendaylight/groupbasedpolicy/renderer/ofoverlay/flow/OfTableTest.java

index 379c013c26599366932d5ae9f44624b2aa27968e..0bb490a5e2d97fbf6476a1c95af58791f2a56e3c 100644 (file)
@@ -230,6 +230,21 @@ public class IndexedTenant {
         return resolveDomain(L2FloodDomain.class, id);
     }
 
+    /**
+     * Get the subnet based on it's ID. Since subnet is on the bottom
+     * of the forwarding hierarchy, there is no other underlying domain
+     * from which {@link Subnet} could be resolved. That is why the
+     * argument refers directly to {@link SubnetId} and not
+     * {@link NetworkDomainId}
+     *
+     * @param id of the {@link Subnet}
+     * @return the {@link Subnet} or <code>null</code> if it does
+     * not exist
+     */
+    public Subnet resolveSubnet(SubnetId id) {
+        return resolveDomain(Subnet.class, id);
+    }
+
     /**
      * Resolve all subnets applicable to the given network domain ID
      * @param id the {@link NetworkDomainId}
index 65ba2401c51b671366be27a267dad4f0dfd0d667..1d025c691ca29b87b9bcb70b5af371d83d76a77b 100755 (executable)
@@ -41,8 +41,10 @@ import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.statistics.OFStatist
 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
 import org.opendaylight.groupbasedpolicy.util.IidFactory;
 import org.opendaylight.groupbasedpolicy.util.SetUtils;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionName;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.L3ContextId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.EndpointFields;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.Endpoints;
@@ -635,6 +637,34 @@ public class EndpointManager implements AutoCloseable {
         return endpoints.getEndpointL3();
     }
 
+    /**
+     * Reads endpointL3 from data store
+     * @param l3c id of {@link L3Context}
+     * @param ipAddress IP address of the endpoint
+     * @param tenantId ID of {@link Tenant} can be optionally specified
+     * @return {@link EndpointL3} if exists, otherwise null.
+     */
+    public EndpointL3 getL3Endpoint(L3ContextId l3c, IpAddress ipAddress, @Nullable TenantId tenantId) {
+        if (l3c == null || ipAddress == null) {
+            LOG.warn("[ContextId: {}, IpAddress: {}] Cannot read endpoint from DS unless both keys are specified!",
+                    l3c, ipAddress);
+            return null;
+        }
+        ReadOnlyTransaction rTx = dataProvider.newReadOnlyTransaction();
+        Optional<EndpointL3> endpointL3 = DataStoreHelper.readFromDs(LogicalDatastoreType.OPERATIONAL,
+                IidFactory.l3EndpointIid(l3c, ipAddress), rTx);
+        rTx.close();
+        if (!endpointL3.isPresent()) {
+            LOG.warn("EndpointL3 [{},{}] not found in data store.", l3c, ipAddress);
+            return null;
+        }
+        if(tenantId != null && !endpointL3.get().getTenant().equals(tenantId)) {
+            LOG.warn("EndpointL3 [{},{}] not found in data store for tenant: {}", l3c, ipAddress, tenantId);
+            return null;
+        }
+        return endpointL3.get();
+    }
+
     /**
      * Return all L3Prefix Endpoints from data store.
      *
index 7250e509dc74a6e7cd9488139523547e91eca7d2..d819c8b3f6432c4ed36c640c0f131143f2cb1102 100755 (executable)
@@ -73,6 +73,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instru
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.EndpointGroupId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.NetworkDomainId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.SubnetId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.Endpoints;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3Address;
@@ -84,6 +85,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.r
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.Tenant;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.ForwardingContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.L2FloodDomain;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.L3Context;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.Subnet;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
@@ -165,7 +167,10 @@ public class DestinationMapper extends FlowTable {
                 EgKey epg = new EgKey(srcEp.getTenant(), epgId);
                 Set<EgKey> peers = Sets.union(Collections.singleton(epg), ctx.getCurrentPolicy().getPeers(epg));
                 for (EgKey peer : peers) {
-                    for (Endpoint peerEp : ctx.getEndpointManager().getEndpointsForGroup(peer)) {
+                    Collection<Endpoint> endpointsForGroup = new HashSet<>();
+                    endpointsForGroup.addAll(ctx.getEndpointManager().getEndpointsForGroup(peer));
+                    endpointsForGroup.addAll(ctx.getEndpointManager().getExtEpsNoLocForGroup(peer));
+                    for (Endpoint peerEp : endpointsForGroup) {
                         currentTenant = peerEp.getTenant();
                         subnetsByTenant.put(currentTenant, getSubnets(currentTenant));
                         EpKey srcEpKey = new EpKey(srcEp.getL2Context(), srcEp.getMacAddress());
@@ -238,6 +243,8 @@ public class DestinationMapper extends FlowTable {
         }
     }
 
+
+
     // set up next-hop destinations for all the endpoints in the endpoint
     // group on the node
 
@@ -597,11 +604,6 @@ public class DestinationMapper extends FlowTable {
             return;
         }
 
-        if (EndpointManager.isExternal(destEp, ctx.getTenant(destEp.getTenant()).getExternalImplicitGroups())) {
-            LOG.error("syncEp(): External endpoints should not be seen here.");
-            return;
-        }
-
         /*
          * Only care about subnets for L3, but fetch them before loop. We need
          * the local subnets for setting SRC MAC for routing. All Routing is now
@@ -615,7 +617,41 @@ public class DestinationMapper extends FlowTable {
         }
 
         OfOverlayContext ofc = destEp.getAugmentation(OfOverlayContext.class);
-        if (Objects.equals(ofc.getNodeId(), nodeId)) {
+
+        // forwarding outside of internal domain should be done when dest EP or GW is external.
+        Subnet srcSubnet = ctx.getTenant(srcEp.getTenant()).resolveSubnet(new SubnetId(srcEp.getNetworkContainment()));
+        Endpoint l2Gw = this.getL2EndpointOfSubnetGateway(srcEp.getTenant(), srcSubnet);
+        boolean destEpIsExternal = destEp.getNetworkContainment() != null
+                && EndpointManager.isExternal(destEp, ctx.getTenant(destEp.getTenant()).getExternalImplicitGroups());
+        boolean subnetGwIsExternal = l2Gw != null
+                && EndpointManager.isExternal(l2Gw, ctx.getTenant(l2Gw.getTenant()).getExternalImplicitGroups());
+        if (destEpIsExternal || subnetGwIsExternal) {
+            if (ofc == null && destEp.getNetworkContainment().equals(srcEp.getNetworkContainment())) {
+                Flow flow = createExternalL2Flow(destEp, destEpFwdCtxOrds, nodeId);
+                if (flow != null) {
+                    ofWriter.writeFlow(nodeId, TABLE_ID, flow);
+                }
+            } else if (l2Gw != null && EndpointManager.isExternal(l2Gw, ctx.getTenant(l2Gw.getTenant()).getExternalImplicitGroups())
+                    && !destEp.getNetworkContainment().equals(srcEp.getNetworkContainment())) {
+                for (L3Address l3a : destEp.getL3Address()) {
+                    if (l3a.getIpAddress() == null || l3a.getL3Context() == null) {
+                        LOG.error("Endpoint with L3Address but either IPAddress or L3Context is null. {}",
+                                destEp.getL3Address());
+                        continue;
+                    }
+                    for (Subnet localSubnet : localSubnets) {
+                        Flow extL3Flow = createExternalL3RoutedFlow(destEp, l3a, destEpFwdCtxOrds, localSubnet, nodeId);
+                        if (extL3Flow != null) {
+                            ofWriter.writeFlow(nodeId, TABLE_ID, extL3Flow);
+                        } else {
+                            LOG.trace("Did not write remote L3 flow for endpoint {} and subnet {}", l3a.getIpAddress(),
+                                    localSubnet.getIpPrefix().getValue());
+                        }
+                    }
+                }
+            }
+        }
+        else if (ofc != null && Objects.equals(ofc.getNodeId(), nodeId)) {
             // this is a local endpoint; send to the approppriate local
             // port
 
@@ -646,7 +682,7 @@ public class DestinationMapper extends FlowTable {
                     }
                 }
             }
-        } else {
+        } else if(ofc!= null) {
             // this endpoint is on a different switch; send to the
             // appropriate tunnel
             if (srcEpFwdCtxOrds.getBdId() == destEpFwdCtxOrds.getBdId()) {
@@ -833,6 +869,162 @@ public class DestinationMapper extends FlowTable {
         return flowb.build();
     }
 
+    private Flow createExternalL3RoutedFlow(Endpoint destEp, L3Address destL3Address, EndpointFwdCtxOrdinals epFwdCtxOrds,
+            Subnet srcSubnet, NodeId nodeId) {
+
+        Subnet destSubnet = null;
+        HashSet<Subnet> subnets = getSubnets(destEp.getTenant());
+        if (subnets == null) {
+            LOG.trace("No subnets in tenant {}", destEp.getTenant());
+            return null;
+        }
+        NetworkDomainId epNetworkContainment = getEPNetworkContainment(destEp);
+        for (Subnet subnet : subnets) {
+            // TODO Li alagalah add IPv6 support
+            if (subnet.getId().getValue().equals(epNetworkContainment.getValue())) {
+                destSubnet = subnet;
+                break;
+            }
+        }
+        if (destSubnet == null) {
+            LOG.trace("Destination IP address does not match any subnet in tenant {}", destL3Address.getIpAddress());
+            return null;
+        }
+
+        if (destSubnet.getVirtualRouterIp() == null) {
+            LOG.trace("Destination subnet {} for Endpoint {}.{} has no gateway IP", destSubnet.getIpPrefix(),
+                    destL3Address.getKey());
+            return null;
+        }
+
+        if (srcSubnet.getVirtualRouterIp() == null) {
+            LOG.trace("Local subnet {} has no gateway IP", srcSubnet.getIpPrefix());
+            return null;
+        }
+        L3Context destL3c = getL3ContextForSubnet(destEp.getTenant(), destSubnet);
+        if (destL3c == null || destL3c.getId() == null) {
+            LOG.error("No L3 Context found associated with subnet {}", destSubnet.getId());
+            return null;
+        }
+        L3Context srcL3c = getL3ContextForSubnet(destEp.getTenant(), srcSubnet);
+        if (srcL3c == null || srcL3c.getId() == null) {
+            LOG.error("No L3 Context found associated with subnet {}", srcSubnet.getId());
+            return null;
+        }
+
+        if (!(srcL3c.getId().getValue().equals(destL3c.getId().getValue()))) {
+            LOG.trace("Trying to route between two L3Contexts {} and {}. Not currently supported.", srcL3c.getId()
+                .getValue(), destL3c.getId().getValue());
+            return null;
+        }
+
+        Endpoint l2Gw = getL2EndpointOfSubnetGateway(destEp.getTenant(), srcSubnet);
+        if(l2Gw == null) {
+            LOG.warn("The endpoint representing external gateway of subnet {} not found", srcSubnet);
+            return null;
+        }
+        MacAddress matcherMac = destEp.getMacAddress();
+        MacAddress destSubnetGatewayMac = l2Gw.getMacAddress();
+
+        ArrayList<Instruction> l3instructions = new ArrayList<>();
+        List<Action> applyActions = new ArrayList<>();
+        List<Action> l3ApplyActions = new ArrayList<>();
+
+        int order = 0;
+
+        Action setdEPG = nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(epFwdCtxOrds.getEpgId()));
+        Action setdCG = nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(epFwdCtxOrds.getCgId()));
+        Action setNextHop;
+
+        Set<NodeConnectorId> extPorts = ctx.getSwitchManager().getExternalPorts(nodeId);
+        if (extPorts == null || !extPorts.iterator().hasNext()) {
+            LOG.warn("No external interface on node: {}. External Gateway {} is not reachable!", nodeId, l2Gw.getKey());
+            return null;
+        }
+        // only one external port is supported for now
+         NodeConnectorId extPort = extPorts.iterator().next();
+
+        long portNum;
+        try {
+            portNum = getOfPortNum(extPort);
+        } catch (NumberFormatException ex) {
+            LOG.warn("Could not parse port number {}", extPort, ex);
+            return null;
+        }
+
+        setNextHop = nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(portNum));
+        // END L3 LOCAL
+
+
+        Action setDlSrc = setDlSrcAction(destSubnetGatewayMac);
+        l3ApplyActions.add(setDlSrc);
+
+        Action setDlDst = setDlDstAction(l2Gw.getMacAddress());
+        l3ApplyActions.add(setDlDst);
+
+        order += 1;
+        applyActions.add(setdEPG);
+        applyActions.add(setdCG);
+        applyActions.add(setNextHop);
+
+        applyActions.addAll(l3ApplyActions);
+        Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
+            .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
+            .build();
+
+        l3instructions.add(applyActionsIns);
+        Instruction gotoTable = new InstructionBuilder().setOrder(order++)
+            .setInstruction(gotoTableIns(ctx.getPolicyManager().getTABLEID_POLICY_ENFORCER()))
+            .build();
+        l3instructions.add(gotoTable);
+        Layer3Match m = null;
+        Long etherType = null;
+        String ikey = null;
+        if (destL3Address.getIpAddress().getIpv4Address() != null) {
+            ikey = destL3Address.getIpAddress().getIpv4Address().getValue() + "/32";
+            etherType = IPv4;
+            m = new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(ikey)).build();
+        } else if (destL3Address.getIpAddress().getIpv6Address() != null) {
+            ikey = destL3Address.getIpAddress().getIpv6Address().getValue() + "/128";
+            etherType = IPv6;
+            m = new Ipv6MatchBuilder().setIpv6Destination(new Ipv6Prefix(ikey)).build();
+        } else {
+            LOG.error("Endpoint has IPAddress that is not recognised as either IPv4 or IPv6.", destL3Address.toString());
+            return null;
+        }
+
+        MatchBuilder mb = new MatchBuilder().setEthernetMatch(ethernetMatch(null, matcherMac, etherType))
+            .setLayer3Match(m);
+        addNxRegMatch(mb, RegMatch.of(NxmNxReg6.class, Long.valueOf(epFwdCtxOrds.getL3Id())));
+        Match match = mb.build();
+        FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "externalL3", match);
+        FlowBuilder flowb = base().setId(flowid)
+            .setPriority(Integer.valueOf(132))
+            .setMatch(match)
+            .setInstructions(new InstructionsBuilder().setInstruction(l3instructions).build());
+        return flowb.build();
+    }
+
+    private Endpoint getL2EndpointOfSubnetGateway(TenantId tenantId, Subnet subnet) {
+        if (subnet != null && subnet.getVirtualRouterIp() != null) {
+            IpAddress gwIpAddress = subnet.getVirtualRouterIp();
+            Collection<EndpointL3Prefix> prefixEps = ctx.getEndpointManager().getEndpointsL3PrefixForTenant(tenantId);
+            if (prefixEps != null) {
+                for (EndpointL3Prefix prefixEp : prefixEps) {
+                    for (EndpointL3Gateways gw : prefixEp.getEndpointL3Gateways()) {
+                        EndpointL3 l3Ep = ctx.getEndpointManager().getL3Endpoint(gw.getL3Context(), gwIpAddress,
+                                prefixEp.getTenant());
+                        if (l3Ep != null && l3Ep.getL2Context() != null && l3Ep.getMacAddress() != null) {
+                            return ctx.getEndpointManager().getEndpoint(
+                                    new EpKey(l3Ep.getL2Context(), l3Ep.getMacAddress()));
+                        }
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
     private Flow createRemoteL2Flow(Endpoint ep, NodeId nodeId, EndpointFwdCtxOrdinals srcEpFwdCtxOrds,
             EndpointFwdCtxOrdinals destEpFwdCtxOrds, OfOverlayContext ofc) {
 
@@ -921,6 +1113,60 @@ public class DestinationMapper extends FlowTable {
         return flowb.build();
     }
 
+    private Flow createExternalL2Flow(Endpoint ep, EndpointFwdCtxOrdinals epFwdCtxOrds,NodeId nodeId) {
+
+        ArrayList<Instruction> instructions = new ArrayList<>();
+        List<Action> applyActions = new ArrayList<>();
+
+        int order = 0;
+
+        Action setdEPG = nxLoadRegAction(NxmNxReg2.class, BigInteger.valueOf(epFwdCtxOrds.getEpgId()));
+        Action setdCG = nxLoadRegAction(NxmNxReg3.class, BigInteger.valueOf(epFwdCtxOrds.getCgId()));
+        Action setNextHop;
+
+        // BEGIN L2 LOCAL
+        Set<NodeConnectorId> extPorts = ctx.getSwitchManager().getExternalPorts(nodeId);
+        if(extPorts == null || !extPorts.iterator().hasNext()) {
+            return null;
+        }
+        // Only one external port is currently supported.
+        NodeConnectorId extPort = extPorts.iterator().next();
+        long portNum;
+        try {
+            portNum = getOfPortNum(extPort);
+        } catch (NumberFormatException ex) {
+            LOG.warn("Could not parse port number {}", extPort, ex);
+            return null;
+        }
+        setNextHop = nxLoadRegAction(NxmNxReg7.class, BigInteger.valueOf(portNum));
+
+        // END L2 LOCAL
+
+        order += 1;
+        applyActions.add(setdEPG);
+        applyActions.add(setdCG);
+        applyActions.add(setNextHop);
+        Instruction applyActionsIns = new InstructionBuilder().setOrder(order++)
+            .setInstruction(applyActionIns(applyActions.toArray(new Action[applyActions.size()])))
+            .build();
+        instructions.add(applyActionsIns);
+
+        Instruction gotoTable = new InstructionBuilder().setOrder(order++)
+            .setInstruction(gotoTableIns(ctx.getPolicyManager().getTABLEID_POLICY_ENFORCER()))
+            .build();
+        instructions.add(gotoTable);
+
+        MatchBuilder mb = new MatchBuilder().setEthernetMatch(ethernetMatch(null, ep.getMacAddress(), null));
+        addNxRegMatch(mb, RegMatch.of(NxmNxReg4.class, Long.valueOf(epFwdCtxOrds.getBdId())));
+        Match match = mb.build();
+        FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "externalL2", match);
+        FlowBuilder flowb = base().setId(flowid)
+            .setPriority(Integer.valueOf(50))
+            .setMatch(match)
+            .setInstructions(new InstructionsBuilder().setInstruction(instructions).build());
+        return flowb.build();
+    }
+
     private Flow createRemoteL3RoutedFlow(Endpoint destEp, L3Address destL3Address, NodeId nodeId,
             EndpointFwdCtxOrdinals srcEpFwdCtxOrds, EndpointFwdCtxOrdinals destEpFwdCtxOrds, OfOverlayContext ofc,
             Subnet srcSubnet) {
index 0ceff85f9337e5d1c519740fe0fe8bdf03bbfc96..fadcc6e325d3a0627d7184997270fc47fcb38acc 100755 (executable)
@@ -8,6 +8,8 @@
 
 package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow;
 
+import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.gotoTableIns;
+
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
index 5f2c11d45fba8cacb76dd424bfe09a39f7bfe46f..29c6dffaec53bd10c14bbbb26aa87f4cf1a7f002 100755 (executable)
@@ -29,8 +29,11 @@ import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtil
 
 import java.math.BigInteger;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
 
 import org.apache.commons.lang3.ArrayUtils;
+import org.opendaylight.groupbasedpolicy.dto.EgKey;
 import org.opendaylight.groupbasedpolicy.dto.EpKey;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext;
 import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfWriter;
@@ -46,6 +49,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.ta
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.Instruction;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoint.fields.L3Address;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.l3endpoint.rev151217.NatAddress;
@@ -54,6 +58,9 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.tenants.tenant.forwarding.context.Subnet;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.EtherType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetTypeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.Layer3Match;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.ArpMatchBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv4MatchBuilder;
@@ -66,6 +73,9 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev14
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
+
 /**
  * Manage the table that assigns source endpoint group, bridge domain, and
  * router domain to registers to be used by other tables.
@@ -90,23 +100,23 @@ public class IngressNatMapper extends FlowTable {
     @Override
     public void sync(NodeId nodeId, OfWriter ofWriter) throws Exception {
 
-        // TODO for consideration: default instruction is goto next table because when matching against eth type 0x8100
-        // in PortSecurity, it's not possible to match against IPv4 addresses (only inf eth type would be 0x800)
-        // We can't determine just from L2 layer if traffic should be passed from PortSecurity here to IngressNat or to
-        // SourceMapper. Various 802.1q encapsulated IPs can pass through external ports - NATed or not NATed, remote
-        // or directly connected.
-        // All external ingress traffic is currently passed here and if no match is foud - no NAT is performed
-        // and processing continues in SourceMapper.
+        /**
+         * To support provider networks, all external ingress traffic is currently passed here and
+         * if no match is foud - no NAT is performed and processing continues in DestinationMapper.
+         */
         Flow flow = base()
                 .setTableId(TABLE_ID)
                 .setPriority(1)
-                .setInstructions(FlowUtils.instructions(FlowUtils.gotoTableIns(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER())))
-                .setId(FlowIdUtils.newFlowId("gotoSourceMapper"))
+            .setInstructions(
+                    FlowUtils.instructions(FlowUtils.gotoTableIns(
+                        ctx.getPolicyManager().getTABLEID_DESTINATION_MAPPER())))
+                .setId(FlowIdUtils.newFlowId("gotoDestinationMapper"))
                 .build();
         ofWriter.writeFlow(nodeId, TABLE_ID, flow);
 
         // TODO Bug 3546 - Difficult: External port is unrelated to Tenant, L3C, L2BD..
 
+        // Flows for ingress NAT translation
         Collection<Endpoint> endpointsForNode = ctx.getEndpointManager().getEndpointsForNode(nodeId);
         Collection<EndpointL3> l3Endpoints = ctx.getEndpointManager().getL3EndpointsWithNat();
         for (EndpointL3 l3Ep : l3Endpoints) {
@@ -117,6 +127,18 @@ public class IngressNatMapper extends FlowTable {
                 }
             }
         }
+        //Flows for ingress traffic that does not have to be translated.
+        // TODO similar loop in DestinationMapper
+        for (Endpoint ep : ctx.getEndpointManager().getEndpointsForNode(nodeId)) {
+            for (EgKey egKey : ctx.getEndpointManager().getEgKeysForEndpoint(ep)) {
+                Set<EgKey> groups = ctx.getCurrentPolicy().getPeers(egKey);
+                for (EgKey peer : Sets.union(Collections.singleton(egKey), ctx.getCurrentPolicy().getPeers(egKey))) {
+                    for (Endpoint extEp : ctx.getEndpointManager().getExtEpsNoLocForGroup(peer)) {
+                        createIngressExternalFlows(extEp, nodeId, ofWriter);
+                    }
+                }
+            }
+        }
     }
 
     private void createNatFlow(EndpointL3 l3Ep, NodeId nodeId, OfWriter ofWriter) throws Exception {
@@ -141,14 +163,29 @@ public class IngressNatMapper extends FlowTable {
         }
     }
 
+    private void createIngressExternalFlows(Endpoint ep, NodeId nodeId, OfWriter ofWriter) throws Exception {
+        EndpointFwdCtxOrdinals epFwdCtxOrds = OrdinalFactory.getEndpointFwdCtxOrdinals(ctx, ep);
+        if (epFwdCtxOrds == null) {
+            LOG.info("getEndpointFwdCtxOrdinals is null for EP {}", ep);
+            return;
+        }
+        if (ep.getL3Address() != null) {
+            for (L3Address l3Addr : ep.getL3Address()) {
+                Flow ipFlow = buildIngressExternalIpFlow(l3Addr.getIpAddress(), epFwdCtxOrds);
+                if (ipFlow != null) {
+                    ofWriter.writeFlow(nodeId, TABLE_ID, ipFlow);
+                }
+            }
+        }
+        Flow arpFlow = buildIngressExternalArpFlow(ep.getMacAddress(), epFwdCtxOrds);
+        if (arpFlow != null) {
+            ofWriter.writeFlow(nodeId, TABLE_ID, arpFlow);
+        }
+    }
+
     private Flow buildNatFlow(IpAddress outsideDestAddress, IpAddress insideDestAddress, MacAddress toMac,
             EndpointFwdCtxOrdinals epFwdCtxOrds) {
-        // TODO Auto-generated method stub
-        MatchBuilder mb = new MatchBuilder();
         Action setDestIp;
-        String outsideIpMatch;
-        Layer3Match m;
-
         Action setDestMac = setDlDstAction(toMac);
         FlowId flowid = new FlowId(new StringBuilder().append("IngressNat")
             .append("|")
@@ -160,38 +197,19 @@ public class IngressNatMapper extends FlowTable {
             .toString());
         if (insideDestAddress.getIpv4Address() != null) {
             setDestIp = setIpv4DstAction(insideDestAddress.getIpv4Address());
-
-            outsideIpMatch = outsideDestAddress.getIpv4Address().getValue() + "/32";
-            m = new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(outsideIpMatch)).build();
-            mb.setEthernetMatch(ethernetMatch(null, null, FlowUtils.IPv4)).setLayer3Match(m);
         } else if (insideDestAddress.getIpv6Address() != null) {
             setDestIp = setIpv6DstAction(insideDestAddress.getIpv6Address());
-            outsideIpMatch = outsideDestAddress.getIpv6Address().getValue() + "/128";
-            m = new Ipv6MatchBuilder().setIpv6Destination(new Ipv6Prefix(outsideIpMatch)).build();
-            mb.setEthernetMatch(ethernetMatch(null, null, FlowUtils.IPv6)).setLayer3Match(m);
         } else {
             return null;
         }
-
-        int egId = epFwdCtxOrds.getEpgId();
-        int bdId = epFwdCtxOrds.getBdId();
-        int fdId = epFwdCtxOrds.getFdId();
-        int l3Id = epFwdCtxOrds.getL3Id();
-        int cgId = epFwdCtxOrds.getCgId();
-        int tunnelId = epFwdCtxOrds.getTunnelId();
-        Action segReg = nxLoadRegAction(NxmNxReg0.class, BigInteger.valueOf(egId));
-        Action scgReg = nxLoadRegAction(NxmNxReg1.class, BigInteger.valueOf(cgId));
-        Action bdReg = nxLoadRegAction(NxmNxReg4.class, BigInteger.valueOf(bdId));
-        Action fdReg = nxLoadRegAction(NxmNxReg5.class, BigInteger.valueOf(fdId));
-        Action vrfReg = nxLoadRegAction(NxmNxReg6.class, BigInteger.valueOf(l3Id));
-        Action tunIdAction = nxLoadTunIdAction(BigInteger.valueOf(tunnelId), false);
-
+        MatchBuilder mb = createMatchOnDstIpAddress(outsideDestAddress);
+        Action[] dstIpMacAction = {setDestIp, setDestMac};
         FlowBuilder flowb = base().setPriority(Integer.valueOf(100))
             .setId(flowid)
             .setMatch(mb.build())
             .setInstructions(
                     instructions(
-                            applyActionIns(setDestIp, setDestMac, segReg, scgReg, bdReg, fdReg, vrfReg, tunIdAction),
+                            applyActionIns(ArrayUtils.addAll(dstIpMacAction, createEpFwdCtxActions(epFwdCtxOrds))),
                             gotoTableIns(ctx.getPolicyManager().getTABLEID_DESTINATION_MAPPER())));
         return flowb.build();
     }
@@ -235,6 +253,100 @@ public class IngressNatMapper extends FlowTable {
         return flowb.build();
     }
 
+    /**
+     * Builds flow for inbound IP traffic of registered external endpoint.
+     * Priority should be lower than in NAT flow.
+     */
+    private Flow buildIngressExternalIpFlow(IpAddress srcIpAddress, EndpointFwdCtxOrdinals epFwdCtxOrds) {
+        MatchBuilder mb = createMatchOnSrcIpAddress(srcIpAddress);
+        if (mb == null) {
+            return null;
+        }
+        FlowBuilder flowb = base().setPriority(Integer.valueOf(90))
+            .setId(FlowIdUtils.newFlowId(TABLE_ID, "inbound-external-ip", mb.build()))
+            .setMatch(mb.build())
+            .setInstructions(
+                    instructions(applyActionIns(createEpFwdCtxActions(epFwdCtxOrds)),
+                            gotoTableIns(ctx.getPolicyManager().getTABLEID_DESTINATION_MAPPER())));
+        return flowb.build();
+    }
+
+    /**
+     * @param srcIpAddress can be IPv4 or IPv6
+     * @return {@link MatchBuilder} with specified L2 ethertype and L3 source address.
+     *  Returns null if srcIpAddress is null.
+     */
+    private MatchBuilder createMatchOnSrcIpAddress(IpAddress srcIpAddress) {
+        return createMatchOnIpAddress(srcIpAddress, true);
+    }
+
+    private MatchBuilder createMatchOnDstIpAddress(IpAddress srcIpAddress) {
+        return createMatchOnIpAddress(srcIpAddress, false);
+    }
+
+    // use createMatchOnSrcIpAddress or createMatchOnDstIpAddress
+    private MatchBuilder createMatchOnIpAddress(IpAddress srcIpAddress, boolean isSourceAddress) {
+        MatchBuilder mb = new MatchBuilder();
+        String ipPrefix;
+        Layer3Match m;
+        if (srcIpAddress.getIpv4Address() != null) {
+            ipPrefix = srcIpAddress.getIpv4Address().getValue() + "/32";
+            m = (isSourceAddress) ? new Ipv4MatchBuilder().setIpv4Source(new Ipv4Prefix(ipPrefix)).build() :
+                new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(ipPrefix)).build();
+            mb.setEthernetMatch(ethernetMatch(null, null, FlowUtils.IPv4))
+              .setLayer3Match(m);
+            return mb;
+        } else if (srcIpAddress.getIpv6Address() != null) {
+            ipPrefix = srcIpAddress.getIpv6Address().getValue() + "/128";
+            m = (isSourceAddress) ? new Ipv6MatchBuilder().setIpv6Source(new Ipv6Prefix(ipPrefix)).build() :
+                new Ipv6MatchBuilder().setIpv6Destination(new Ipv6Prefix(ipPrefix)).build();
+            mb.setEthernetMatch(ethernetMatch(null, null, FlowUtils.IPv6))
+              .setLayer3Match(m);
+            return mb;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Builds flow for inbound ARP traffic of registered external endpoint.
+     * Priority should be lower than in ARP flow for NAT address.
+     */
+    private Flow buildIngressExternalArpFlow(MacAddress srcMac, EndpointFwdCtxOrdinals epFwdCtxOrds) {
+        if (srcMac == null) {
+            return null;
+        }
+        MatchBuilder mb = new MatchBuilder()
+            .setEthernetMatch(ethernetMatch(srcMac, null, ARP));
+            //.setLayer3Match(
+            //        new ArpMatchBuilder()
+           //             .setArpOp(Integer.valueOf(2))
+           //             .build());
+        FlowBuilder flowb = base().setPriority(80);
+        flowb.setInstructions(instructions(applyActionIns(createEpFwdCtxActions(epFwdCtxOrds)),
+                gotoTableIns(ctx.getPolicyManager().getTABLEID_DESTINATION_MAPPER())));
+        flowb.setId(FlowIdUtils.newFlowId(TABLE_ID, "inbound-external-arp", mb.build()));
+        flowb.setMatch(mb.build());
+        return flowb.build();
+    }
+
+    private Action[] createEpFwdCtxActions(EndpointFwdCtxOrdinals epFwdCtxOrds) {
+        int egId = epFwdCtxOrds.getEpgId();
+        int bdId = epFwdCtxOrds.getBdId();
+        int fdId = epFwdCtxOrds.getFdId();
+        int l3Id = epFwdCtxOrds.getL3Id();
+        int cgId = epFwdCtxOrds.getCgId();
+        int tunnelId = epFwdCtxOrds.getTunnelId();
+        Action segReg = nxLoadRegAction(NxmNxReg0.class, BigInteger.valueOf(egId));
+        Action scgReg = nxLoadRegAction(NxmNxReg1.class, BigInteger.valueOf(cgId));
+        Action bdReg = nxLoadRegAction(NxmNxReg4.class, BigInteger.valueOf(bdId));
+        Action fdReg = nxLoadRegAction(NxmNxReg5.class, BigInteger.valueOf(fdId));
+        Action vrfReg = nxLoadRegAction(NxmNxReg6.class, BigInteger.valueOf(l3Id));
+        Action tunIdAction = nxLoadTunIdAction(BigInteger.valueOf(tunnelId), false);
+        Action[] outsideArpActions = {segReg, scgReg, bdReg, fdReg, vrfReg, tunIdAction};
+        return outsideArpActions;
+    }
+
     static byte[] bytesFromHexString(String values) {
         String target = "";
         if (values != null) {
index 9be8350d5e95f908d428733f5c9c04ffa28e5c7e..bc4d66e8d227dd677ceaab22046894d74f45098e 100755 (executable)
@@ -15,6 +15,7 @@ import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtil
 import static org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow.FlowUtils.nxOutputRegAction;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -31,6 +32,7 @@ import org.opendaylight.groupbasedpolicy.api.sf.IpProtoClassifierDefinition;
 import org.opendaylight.groupbasedpolicy.api.sf.L4ClassifierDefinition;
 import org.opendaylight.groupbasedpolicy.dto.EgKey;
 import org.opendaylight.groupbasedpolicy.dto.EndpointConstraint;
+import org.opendaylight.groupbasedpolicy.dto.EpKey;
 import org.opendaylight.groupbasedpolicy.dto.IndexedTenant;
 import org.opendaylight.groupbasedpolicy.dto.Policy;
 import org.opendaylight.groupbasedpolicy.dto.RuleGroup;
@@ -52,12 +54,17 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.acti
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Instructions;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ClassifierDefinitionId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.ConditionName;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.common.rev140421.TenantId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.Endpoint;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.rev140421.endpoints.EndpointL3Key;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.l3endpoint.rev151217.NatAddress;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.OfOverlayContext;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.HasDirection.Direction;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.policy.rev140421.has.action.refs.ActionRef;
@@ -840,16 +847,27 @@ public class PolicyEnforcer extends FlowTable {
                 .getTenant()
                 .getPolicy()
                 .getExternalImplicitGroup();
-            if (EndpointManager.isExternal(netElements.getDstEp(), eigs)) {
-                flow.setInstructions(instructions(gotoEgressNatInstruction));
-            } else if (actionBuilderList == null) {
+            boolean performNat = false;
+            for (EndpointL3 natEp : ctx.getEndpointManager().getL3EndpointsWithNat()) {
+                if (natEp.getMacAddress() != null &&
+                    natEp.getL2Context() != null &&
+                    netElements.getSrcEp().getKey().equals(new EndpointKey(natEp.getL2Context(),
+                        natEp.getMacAddress())) &&
+                    EndpointManager.isExternal(netElements.getDstEp(), eigs)) {
+                    performNat = true;
+                    break;
+                }
+            }
+            if (actionBuilderList == null) {
                 //TODO - analyse, what happen for unknown action, SFC, etc.
                 LOG.warn("Action builder list not found, partially flow which is not created: {}", flow.build());
                 continue;
-            } else if (actionBuilderList.isEmpty()) {
-                flow.setInstructions(instructions(gotoExternalInstruction));
+            }
+            if (actionBuilderList.isEmpty()) {
+                flow.setInstructions((performNat == true) ? instructions(gotoEgressNatInstruction) : instructions(gotoExternalInstruction));
             } else {
-                flow.setInstructions(instructions(applyActionIns(actionBuilderList), gotoExternalInstruction));
+                flow.setInstructions(instructions(applyActionIns(actionBuilderList),
+                        (performNat == true) ? gotoEgressNatInstruction : gotoExternalInstruction));
             }
             ofWriter.writeFlow(netElements.getLocalNodeId(), TABLE_ID, flow.build());
         }
index a4cbd9a303bfe6fb3e5a25ddac78ef764de362a7..182421b0232e96771c49f00ecbb3768f231c19d2 100755 (executable)
@@ -263,7 +263,8 @@ public class OfTableTest {
             .setL2Context(bd)\r
             .setTenant(tid)\r
             .setEndpointGroup(eg)\r
-            .setMacAddress(new MacAddress("00:00:00:00:00:01"));\r
+            .setMacAddress(new MacAddress("00:00:00:00:00:01"))\r
+            .setNetworkContainment(sub2);\r
     }\r
 \r
     protected EndpointBuilder localEP() {\r