Bug5427: Added JavaDoc for OfOverlay rendeder (flow description)
[groupbasedpolicy.git] / renderers / ofoverlay / src / main / java / org / opendaylight / groupbasedpolicy / renderer / ofoverlay / flow / PortSecurity.java
old mode 100644 (file)
new mode 100755 (executable)
index 095cbe6..f919f1b
 
 package org.opendaylight.groupbasedpolicy.renderer.ofoverlay.flow;
 
-import java.util.Map;
+import java.util.Collections;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.Set;
 
-import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
-import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.PolicyManager.Dirty;
-import org.opendaylight.groupbasedpolicy.resolver.EgKey;
-import org.opendaylight.groupbasedpolicy.resolver.PolicyInfo;
+import org.opendaylight.groupbasedpolicy.dto.IndexedTenant;
+import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfContext;
+import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.OfWriter;
+import org.opendaylight.groupbasedpolicy.renderer.ofoverlay.endpoint.EndpointManager;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
 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;
 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.InstructionsBuilder;
+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.TenantId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.go.to.table._case.GoToTable;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
 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.ofoverlay.rev140528.EndpointLocation.LocationType;
+
 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.policy.ExternalImplicitGroup;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.ofoverlay.rev140528.Segmentation;
+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.forwarding.context.L2FloodDomain;
 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.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;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._3.match.Ipv6MatchBuilder;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlan;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.overlay.rev150105.TunnelTypeVxlanGpe;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * Manage the table that enforces port security
- * @author readams
+ * <h1>Manage the table that enforces port security. Initial flows in group-based policy pipeline (table=0)</h1>
+ *
+ * Lower-priority flows are leading flows for all traffic incoming from endpoints associated to gbp classifier.<br>
+ * Created when an {@link Endpoint} is internal and contains {@link OfOverlayContext} augmentation. Several flows of
+ * this kind are produced.
+ *<p>
+ * <i>L2 flow:</i><br>
+ * Priority = 100<br>
+ * Matches:<br>
+ *      - in_port, {@link NodeConnectorId}
+ *      - dl_src {@link org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress}<br>
+ * Actions:<br>
+ *      - {@link GoToTable} SOURCE MAPPER table
+ *<p>
+ * <i>L3 flow:</i><br>
+ * Priority = 120<br>
+ * Matches:<br>
+ *      - ip, (ethertype)<br>
+ *      - in_port, {@link NodeConnectorId}<br>
+ *      - dl_src {@link org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress}<br>
+ *      - nw_src (source ip address)<br>
+ * Actions:<br>
+ *      - {@link GoToTable} SOURCE MAPPER table
+ *<p>
+ * <i>L3 Arp flow:</i><br>
+ * Priority = 121<br>
+ * Matches:<br>
+ *      - arp, (ethertype)<br>
+ *      - in_port, {@link NodeConnectorId}<br>
+ *      - dl_src {@link org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress}<br>
+ *      - arp_spa (arp source transport address)<br>
+ * Actions:<br>
+ *      - {@link GoToTable} SOURCE MAPPER table
+ *<p>
+ * <i>L3 Dhcp dora flow:</i><br>
+ * Priority = 115<br>
+ * Matches:<br>
+ *      - ip, (ethertype)<br>
+ *      - in_port, {@link NodeConnectorId}<br>
+ *      - dl_src {@link org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress}<br>
+ *      - nw_dst (destination ip address)<br>
+ * Actions:<br>
+ *      - {@link GoToTable} SOURCE MAPPER table
+ *<p>
+ * Higher-priority flows providing VLAN support for external networks. Created when node contains external ports
+ *<p>
+ * <i>Allow from external:</i><br>
+ * Priority = 200<br>
+ * Matches:<br>
+ *      - in_port, {@link NodeConnectorId}<br>
+ * Actions:<br>
+ *      - {@link GoToTable} INGRESS NAT table
+ *<p>
+ * <i>Flow that pops VLAN tag for inbound traffic:</i><br>
+ * Priority = 210<br>
+ * See {@link #popVlanTagsOnExternalPort}
+ *<p>
+ * Highest priority flows used to direct traffic coming from tunnel (SFC). These flows are created always
+ *<p>
+ * <i>Allow from tunnel:</i><br>
+ * Priority = 300<br>
+ * Matches:<br>
+ *      - in_port (has to be tunnel port), {@link NodeConnectorId}<br>
+ * Actions:<br>
+ *      - {@link GoToTable} SOURCE MAPPER table
+ *
  */
 public class PortSecurity extends FlowTable {
     protected static final Logger LOG =
             LoggerFactory.getLogger(PortSecurity.class);
 
-    public static final short TABLE_ID = 0;
+    public static short TABLE_ID;
 
-    public PortSecurity(OfTable.OfTableCtx ctx) {
+    public PortSecurity(OfContext ctx, short tableId) {
         super(ctx);
+        TABLE_ID = tableId;
     }
 
     @Override
@@ -56,189 +134,231 @@ public class PortSecurity extends FlowTable {
     }
 
     @Override
-    public void sync(ReadWriteTransaction t,
-                     InstanceIdentifier<Table> tiid,
-                     Map<String, FlowCtx> flowMap,
-                     NodeId nodeId, PolicyInfo policyInfo, Dirty dirty) {
-        // Allow traffic from tunnel and external ports
-        NodeConnectorId tunnelIf = ctx.switchManager.getTunnelPort(nodeId);
-        if (tunnelIf != null)
-            allowFromPort(t, tiid, flowMap, tunnelIf);
-        Set<NodeConnectorId> external =
-                ctx.switchManager.getExternalPorts(nodeId);
-        for (NodeConnectorId extIf: external) {
-            allowFromPort(t, tiid, flowMap, extIf);
-        }
+    public void sync(NodeId nodeId, OfWriter ofWriter) {
+
+        // Allow traffic from tunnel ports
+        NodeConnectorId vxLanTunnel = ctx.getSwitchManager().getTunnelPort(nodeId, TunnelTypeVxlan.class);
+        NodeConnectorId vxLanGpeTunnel = ctx.getSwitchManager().getTunnelPort(nodeId, TunnelTypeVxlanGpe.class);
+        if (vxLanTunnel != null)
+            ofWriter.writeFlow(nodeId, TABLE_ID, allowFromPort(vxLanTunnel));
+        if (vxLanGpeTunnel != null)
+            ofWriter.writeFlow(nodeId, TABLE_ID, allowFromPort(vxLanGpeTunnel));
 
         // Default drop all
-        dropFlow(t, tiid, flowMap, 1, null);
+        ofWriter.writeFlow(nodeId, TABLE_ID, dropFlow(1, null, TABLE_ID));
 
         // Drop IP traffic that doesn't match a source IP rule
-        dropFlow(t, tiid, flowMap, 110, FlowUtils.ARP);
-        dropFlow(t, tiid, flowMap, 111, FlowUtils.IPv4);
-        dropFlow(t, tiid, flowMap, 112, FlowUtils.IPv6);
-
-        for (EgKey sepg : ctx.epManager.getGroupsForNode(nodeId)) {
-            for (Endpoint e : ctx.epManager.getEPsForNode(nodeId, sepg)) {
-                OfOverlayContext ofc = e.getAugmentation(OfOverlayContext.class);
-                if (ofc != null && ofc.getNodeConnectorId() != null &&
-                        (ofc.getLocationType() == null ||
-                        LocationType.Internal.equals(ofc.getLocationType()))) {
-                    // Allow layer 3 traffic (ARP and IP) with the correct
-                    // source IP, MAC, and source port
-                    l3flow(t, tiid, flowMap, e, ofc, 120, false);
-                    l3flow(t, tiid, flowMap, e, ofc, 121, true);
-                    l3DhcpDoraFlow(t, tiid, flowMap, e, ofc, 115);
-
-                    // Allow layer 2 traffic with the correct source MAC and
-                    // source port (note lower priority than drop IP rules)
-                    l2flow(t, tiid, flowMap, e, ofc, 100);
+        ofWriter.writeFlow(nodeId, TABLE_ID, dropFlow(110, FlowUtils.ARP, TABLE_ID));
+        ofWriter.writeFlow(nodeId, TABLE_ID, dropFlow(111, FlowUtils.IPv4, TABLE_ID));
+        ofWriter.writeFlow(nodeId, TABLE_ID, dropFlow(112, FlowUtils.IPv6, TABLE_ID));
+
+        Set<TenantId> tenantIds = new HashSet<>();
+        for (Endpoint ep : ctx.getEndpointManager().getEndpointsForNode(nodeId)) {
+            OfOverlayContext ofc = ep.getAugmentation(OfOverlayContext.class);
+            if (ofc == null || ofc.getNodeConnectorId() == null) {
+                LOG.info("Endpoint {} does not contain node-connector-id. OFOverlay ignores the endpoint.",
+                        ep.getKey());
+                continue;
+            }
+
+            tenantIds.add(ep.getTenant());
+            Set<ExternalImplicitGroup> eigs = getExternalImplicitGroupsForTenant(ep.getTenant());
+            if (EndpointManager.isInternal(ep, eigs)) {
+                // Allow layer 3 traffic (ARP and IP) with the correct
+                // source IP, MAC, and source port
+                l3flow(ofWriter, nodeId, ep, ofc, 120, false);
+                l3flow(ofWriter, nodeId, ep, ofc, 121, true);
+                ofWriter.writeFlow(nodeId, TABLE_ID, l3DhcpDoraFlow(ep, ofc, 115));
+
+                // Allow layer 2 traffic with the correct source MAC and
+                // source port (note lower priority than drop IP rules)
+                ofWriter.writeFlow(nodeId, TABLE_ID, l2flow(ep, ofc, 100));
+            } else { // EP is external
+                if (LOG.isTraceEnabled()) {
+                    LOG.trace("External Endpoint is ignored in PortSecurity: {}", ep);
                 }
             }
         }
+
+        for (TenantId tenantId : tenantIds) {
+            for (NodeConnectorId nc : ctx.getSwitchManager().getExternalPorts(nodeId)) {
+                // TODO Bug 3546 - Difficult: External port is unrelated to Tenant, L3C, L2BD..
+                for (Flow flow : popVlanTagsOnExternalPort(nc, tenantId, 210)) {
+                    // Tagged frames have to be untagged when entering policy domain
+                    ofWriter.writeFlow(nodeId, TABLE_ID, flow);
+                }
+                // Allowing untagged frames entering policy domain
+                ofWriter.writeFlow(nodeId, TABLE_ID, allowFromExternalPort(nc, 200));
+            }
+        }
     }
 
-    private void allowFromPort(ReadWriteTransaction t,
-                               InstanceIdentifier<Table> tiid,
-                               Map<String, FlowCtx> flowMap,
-                               NodeConnectorId port) {
-        FlowId flowid = new FlowId(new StringBuilder()
-            .append("allow|")
-            .append(port.getValue())
-            .toString());
-        if (visit(flowMap, flowid.getValue())) {
-            FlowBuilder flowb = base()
-                .setId(flowid)
-                .setPriority(Integer.valueOf(200))
-                .setMatch(new MatchBuilder()
-                    .setInPort(port)
-                    .build())
-                .setInstructions(FlowUtils.gotoTableInstructions((short)(getTableId()+1)));
-            writeFlow(t, tiid, flowb.build());
+    private Set<ExternalImplicitGroup> getExternalImplicitGroupsForTenant(TenantId tenantId) {
+        IndexedTenant tenant = ctx.getTenant(tenantId);
+        if (tenant == null) {
+            return Collections.emptySet();
         }
+        return tenant.getExternalImplicitGroups();
+    }
+
+    private Flow allowFromPort(NodeConnectorId port) {
+        Match match = new MatchBuilder()
+                .setInPort(port)
+                .build();
+        FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "allow", match);
+        FlowBuilder flowb = base()
+                .setId(flowid)
+                .setPriority(300)
+                .setMatch(match)
+                .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER()));
+        return flowb.build();
     }
 
-    private void l2flow(ReadWriteTransaction t,
-                        InstanceIdentifier<Table> tiid,
-                        Map<String, FlowCtx> flowMap,
-                        Endpoint e, OfOverlayContext ofc,
-                        Integer priority) {
-        FlowId flowid = new FlowId(new StringBuilder()
-            .append(ofc.getNodeConnectorId().getValue())
-            .append("|")
-            .append(e.getMacAddress().getValue())
-            .toString());
-        if (visit(flowMap, flowid.getValue())) {
-            FlowBuilder flowb = base()
+    private Flow l2flow(Endpoint ep, OfOverlayContext ofc, Integer priority) {
+        Match match = new MatchBuilder()
+                .setEthernetMatch(
+                        FlowUtils.ethernetMatch(ep.getMacAddress(), null, null))
+                .setInPort(ofc.getNodeConnectorId())
+                .build();
+        FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "L2", match);
+        FlowBuilder flowb = base()
                 .setPriority(priority)
                 .setId(flowid)
-                .setMatch(new MatchBuilder()
-                    .setEthernetMatch(FlowUtils.ethernetMatch(e.getMacAddress(),
-                                                              null, null))
-                    .setInPort(ofc.getNodeConnectorId())
-                    .build())
-                .setInstructions(FlowUtils.gotoTableInstructions((short)(TABLE_ID + 1)));
+                .setMatch(match)
+                .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER()));
 
-            writeFlow(t, tiid, flowb.build());
-        }
+        return flowb.build();
     }
 
-    private void l3DhcpDoraFlow(ReadWriteTransaction t,
-                                InstanceIdentifier<Table> tiid,
-                                Map<String, FlowCtx> flowMap,
-                                Endpoint e, OfOverlayContext ofc,
-                                Integer priority) {
+    private Flow l3DhcpDoraFlow(Endpoint ep, OfOverlayContext ofc, Integer priority) {
 
-        Long etherType= FlowUtils.IPv4;
+        //TODO: Handle IPv6 DORA
+        Long etherType = FlowUtils.IPv4;
         // DHCP DORA destination is broadcast
-        String ikey="255.255.255.255/32";
-        Layer3Match m=new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(ikey)).build();
-
-        FlowId flowid = new FlowId(new StringBuilder()
-        .append(ofc.getNodeConnectorId().getValue())
-        .append("|")
-        .append(e.getMacAddress().getValue())
-        .append("|dhcp|")
-        .append(etherType)
-        .toString());
-        if (visit(flowMap, flowid.getValue())) {
-            Flow flow = base()
-                    .setPriority(priority)
-                    .setId(flowid)
-                    .setMatch(new MatchBuilder()
-                    .setEthernetMatch(FlowUtils.ethernetMatch(e.getMacAddress(),
-                            null,
-                            etherType))
-                            .setLayer3Match(m)
-                            .setInPort(ofc.getNodeConnectorId())
-                            .build())
-                            .setInstructions(FlowUtils.gotoTableInstructions((short)(TABLE_ID + 1)))
-                            .build();
+        String ikey = "255.255.255.255/32";
+        Layer3Match m = new Ipv4MatchBuilder().setIpv4Destination(new Ipv4Prefix(ikey)).build();
 
-            writeFlow(t, tiid, flow);
-        }
+        Match match = new MatchBuilder()
+                .setEthernetMatch(
+                        FlowUtils.ethernetMatch(ep.getMacAddress(),
+                        null,
+                        etherType))
+                .setLayer3Match(m)
+                .setInPort(ofc.getNodeConnectorId())
+                .build();
+        FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "dhcp", match);
+        return base()
+                .setPriority(priority)
+                .setId(flowid)
+                .setMatch(match)
+                .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER()))
+                .build();
     }
 
-    private void l3flow(ReadWriteTransaction t,
-                        InstanceIdentifier<Table> tiid,
-                        Map<String, FlowCtx> flowMap,
-                        Endpoint e, OfOverlayContext ofc,
-                        Integer priority,
-                        boolean arp) {
-        if (e.getL3Address() == null) return;
-        for (L3Address l3 : e.getL3Address()) {
-            if (l3.getIpAddress() == null) continue;
-            Layer3Match m = null;
-            Long etherType = null;
-            String ikey = null;
+    private void l3flow(OfWriter ofWriter, NodeId nodeId,
+                        Endpoint ep, OfOverlayContext ofc,
+                        Integer priority, boolean arp) {
+        if (ep.getL3Address() == null)
+            return;
+        for (L3Address l3 : ep.getL3Address()) {
+            if (l3.getIpAddress() == null)
+                continue;
+            Layer3Match m;
+            Long etherType;
+            String ikey;
             if (l3.getIpAddress().getIpv4Address() != null) {
-                ikey = l3.getIpAddress().getIpv4Address().getValue()+"/32";
+                ikey = l3.getIpAddress().getIpv4Address().getValue() + "/32";
                 if (arp) {
                     m = new ArpMatchBuilder()
-                        .setArpSourceTransportAddress(new Ipv4Prefix(ikey))
-                        .build();
+                            .setArpSourceTransportAddress(new Ipv4Prefix(ikey))
+                            .build();
                     etherType = FlowUtils.ARP;
                 } else {
                     m = new Ipv4MatchBuilder()
-                        .setIpv4Source(new Ipv4Prefix(ikey))
-                        .build();
+                            .setIpv4Source(new Ipv4Prefix(ikey))
+                            .build();
                     etherType = FlowUtils.IPv4;
                 }
             } else if (l3.getIpAddress().getIpv6Address() != null) {
-                if (arp) continue;
-                ikey = l3.getIpAddress().getIpv6Address().getValue()+"/128";
+                if (arp)
+                    continue;
+                ikey = l3.getIpAddress().getIpv6Address().getValue() + "/128";
                 m = new Ipv6MatchBuilder()
-                    .setIpv6Source(new Ipv6Prefix(ikey))
-                    .build();
+                        .setIpv6Source(new Ipv6Prefix(ikey))
+                        .build();
                 etherType = FlowUtils.IPv6;
             } else {
                 continue;
             }
-            FlowId flowid = new FlowId(new StringBuilder()
-                .append(ofc.getNodeConnectorId().getValue())
-                .append("|")
-                .append(e.getMacAddress().getValue())
-                .append("|")
-                .append(ikey)
-                .append("|")
-                .append(etherType)
-                .toString());
-            if (visit(flowMap, flowid.getValue())) {
-                Flow flow = base()
+            Match match = new MatchBuilder()
+                    .setEthernetMatch(
+                            FlowUtils.ethernetMatch(ep.getMacAddress(),
+                            null,
+                            etherType))
+                    .setLayer3Match(m)
+                    .setInPort(ofc.getNodeConnectorId())
+                    .build();
+            FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "L3", match);
+            Flow flow = base()
                     .setPriority(priority)
                     .setId(flowid)
-                    .setMatch(new MatchBuilder()
-                        .setEthernetMatch(FlowUtils.ethernetMatch(e.getMacAddress(),
-                                                                  null,
-                                                                  etherType))
-                        .setLayer3Match(m)
-                        .setInPort(ofc.getNodeConnectorId())
-                        .build())
-                    .setInstructions(FlowUtils.gotoTableInstructions((short)(TABLE_ID + 1)))
+                    .setMatch(match)
+                    .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_SOURCE_MAPPER()))
                     .build();
 
-                writeFlow(t, tiid, flow);
+            ofWriter.writeFlow(nodeId, TABLE_ID,flow);
+        }
+    }
+
+    private Flow allowFromExternalPort(NodeConnectorId nc, Integer priority) {
+        Match match = new MatchBuilder().setInPort(nc).build();
+        FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "allowExternal", match);
+        FlowBuilder flowb = base().setId(flowid)
+            .setPriority(priority)
+            .setMatch(match)
+            .setInstructions(FlowUtils.gotoTableInstructions(ctx.getPolicyManager().getTABLEID_INGRESS_NAT()));
+        return flowb.build();
+    }
+
+    /**
+     * Pops VLAN tag for inbound traffic.
+     *
+     * @param nc should be external for now
+     * @param tenantId of {@link Tenant} from which {@link L2FloodDomain}s are read from which VLAN IDs are resolved.
+     * @param priority of flows in the table
+     * @return {@link Flow}s which match on ingress port, and VLAN ID to pop.
+     *         {@link GoToTable} Instructions are set to INGRESS NAT table.
+     */
+    private List<Flow> popVlanTagsOnExternalPort(NodeConnectorId nc, TenantId tenantId, Integer priority) {
+        List<Flow> flows = new ArrayList<>();
+        if(ctx.getTenant(tenantId) != null) {
+            for (L2FloodDomain l2Fd : ctx.getTenant(tenantId).getTenant().getForwardingContext().getL2FloodDomain()) {
+                Segmentation segmentation = l2Fd.getAugmentation(Segmentation.class);
+                if (segmentation != null) {
+                    Integer vlanId = segmentation.getSegmentationId();
+                    flows.add(buildPopVlanFlow(nc, vlanId, priority));
+                }
             }
         }
+        return flows;
+    }
+
+    private Flow buildPopVlanFlow(NodeConnectorId nc, Integer vlanId, int priority) {
+        Match match = new MatchBuilder()
+            .setVlanMatch(FlowUtils.vlanMatch(vlanId, true))
+            .setInPort(nc)
+            .build();
+        List<Instruction> instructions = new ArrayList<>();
+        instructions.add(FlowUtils.popVlanInstruction(0));
+        instructions.add(new InstructionBuilder().setOrder(1)
+             // TODO for now matches on external flows are passed to ingress nat table
+            .setInstruction(FlowUtils.gotoTableIns(ctx.getPolicyManager().getTABLEID_INGRESS_NAT()))
+            .build());
+        FlowId flowid = FlowIdUtils.newFlowId(TABLE_ID, "allowExternalPopVlan", match);
+        return base().setPriority(priority)
+            .setId(flowid)
+            .setMatch(match)
+            .setInstructions(new InstructionsBuilder().setInstruction(instructions).build())
+            .build();
     }
 }