Added SpringOpenTTP switch driver (ONOS-822/ONOS-682).
Modified OpenFlpwRuleProvider to support FlowRule with multiple-table feature.

Change-Id: If95284077b96213593c5c1e3ab12900001bf49a2
diff --git a/openflow/drivers/src/main/java/org/onosproject/openflow/drivers/OFSwitchImplSpringOpenTTP.java b/openflow/drivers/src/main/java/org/onosproject/openflow/drivers/OFSwitchImplSpringOpenTTP.java
index 925a744..0fbdfa8 100644
--- a/openflow/drivers/src/main/java/org/onosproject/openflow/drivers/OFSwitchImplSpringOpenTTP.java
+++ b/openflow/drivers/src/main/java/org/onosproject/openflow/drivers/OFSwitchImplSpringOpenTTP.java
@@ -16,28 +16,99 @@
 package org.onosproject.openflow.drivers;
 
 import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.RoleState;
 import org.onosproject.openflow.controller.driver.AbstractOpenFlowSwitch;
+import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeAlreadyStarted;
+import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeCompleted;
+import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeNotStarted;
+import org.projectfloodlight.openflow.protocol.OFAsyncGetReply;
+import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
+import org.projectfloodlight.openflow.protocol.OFErrorMsg;
+import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFGroupFeaturesStatsReply;
+import org.projectfloodlight.openflow.protocol.OFMatchV3;
+import org.projectfloodlight.openflow.protocol.OFOxmList;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFStatsReply;
+import org.projectfloodlight.openflow.protocol.OFFlowMod;
+import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFFactory;
 import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFType;
+import org.projectfloodlight.openflow.protocol.action.OFAction;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmEthType;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmInPort;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmVlanVid;
+import org.projectfloodlight.openflow.types.EthType;
+import org.projectfloodlight.openflow.types.MacAddress;
+import org.projectfloodlight.openflow.types.OFBufferId;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.OFVlanVidMatch;
+import org.projectfloodlight.openflow.types.TableId;
+import org.projectfloodlight.openflow.types.U32;
+import org.projectfloodlight.openflow.util.HexString;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 
-/**
- * Created by sanghoshin on 1/22/15.
- */
 public class OFSwitchImplSpringOpenTTP extends AbstractOpenFlowSwitch {
 
-    protected OFSwitchImplSpringOpenTTP(Dpid dp) {
-        super(dp);
+    private OFFactory factory;
+
+    private final AtomicBoolean driverHandshakeComplete;
+    private AtomicBoolean haltStateMachine;
+
+    private DriverState driverState;
+
+    /* Default table ID - compatible with CpqD switch */
+    private static final int TABLE_VLAN = 0;
+    private static final int TABLE_TMAC = 1;
+    private static final int TABLE_IPV4_UNICAST = 2;
+    private static final int TABLE_MPLS = 3;
+    private static final int TABLE_ACL = 5;
+
+    private static final long TEST_FLOW_REMOVED_MASK = 0xf;
+    private static final long TEST_PACKET_IN_MASK = 0x7;
+    private static final long TEST_PORT_STATUS_MASK = 0x7;
+
+    private static final int OFPCML_NO_BUFFER = 0xffff;
+
+    private long barrierXidToWaitFor = -1;
+
+    /* Set the default values. These variables will get
+     * overwritten based on the switch vendor type
+     */
+    protected int vlanTableId = TABLE_VLAN;
+    protected int tmacTableId = TABLE_TMAC;
+    protected int ipv4UnicastTableId = TABLE_IPV4_UNICAST;
+    protected int mplsTableId = TABLE_MPLS;
+    protected int aclTableId = TABLE_ACL;
+
+    /* priority values for OF message */
+    private static final short MAX_PRIORITY = (short) 0xffff;
+    private static final short PRIORITY_MULTIPLIER = (short) 2046;
+    private static final short MIN_PRIORITY = 0x0;
+
+
+    protected OFSwitchImplSpringOpenTTP(Dpid dpid, OFDescStatsReply desc) {
+        super(dpid);
+        driverHandshakeComplete = new AtomicBoolean(false);
+        haltStateMachine = new AtomicBoolean(false);
+        driverState = DriverState.INIT;
+        setSwitchDescription(desc);
     }
 
-    @Override
-    public void write(OFMessage msg) {
-
-    }
 
     @Override
-    public void write(List<OFMessage> msgs) {
-
+    public String toString() {
+        return "OFSwitchImplSpringOpenTTP [" + ((channel != null)
+                ? channel.getRemoteAddress() : "?")
+                + " DPID[" + ((this.getStringId() != null) ?
+                this.getStringId() : "?") + "]]";
     }
 
     @Override
@@ -47,17 +118,458 @@
 
     @Override
     public void startDriverHandshake() {
+        log.debug("Starting driver handshake for sw {}", getStringId());
+        if (startDriverHandshakeCalled) {
+            throw new SwitchDriverSubHandshakeAlreadyStarted();
+        }
+        startDriverHandshakeCalled = true;
+        factory = this.factory();
 
+        try {
+            nextDriverState();
+        } catch (IOException e) {
+            log.error("Error {} during driver handshake for sw {}", e.getCause(),
+                    getStringId());
+        }
     }
 
     @Override
     public boolean isDriverHandshakeComplete() {
-        return false;
+        if (!startDriverHandshakeCalled) {
+            throw new SwitchDriverSubHandshakeNotStarted();
+        }
+        return driverHandshakeComplete.get();
     }
 
     @Override
     public void processDriverHandshakeMessage(OFMessage m) {
-
+        if (!startDriverHandshakeCalled) {
+            throw new SwitchDriverSubHandshakeNotStarted();
+        }
+        if (driverHandshakeComplete.get()) {
+            throw new SwitchDriverSubHandshakeCompleted(m);
+        }
+        try {
+            processOFMessage(m);
+        } catch (IOException e) {
+            log.error("Error generated when processing OFMessage {}", e.getCause());
+        }
     }
 
-}
+    @Override
+    public void write(OFMessage msg) {
+        this.channel.write(Collections.singletonList(msg));
+    }
+
+    @Override
+    public void write(List<OFMessage> msgs) {
+        this.channel.write(msgs);
+    }
+
+    @Override
+    public void sendMsg(OFMessage m, TableType tableType) {
+
+        if (m.getType() == OFType.FLOW_MOD) {
+            OFFlowMod flowMod = (OFFlowMod) m;
+            OFFlowMod.Builder builder = flowMod.createBuilder();
+            builder.setTableId(getTableId(tableType));
+            OFFlowMod newFlowMod = builder.build();
+            if (role == RoleState.MASTER) {
+                this.write(newFlowMod);
+            }
+        } else {
+            if (role == RoleState.MASTER) {
+                this.write(m);
+            }
+        }
+    }
+
+    /*
+     * Driver handshake state machine
+     */
+
+    enum DriverState {
+        INIT,
+        SET_TABLE_MISS_ENTRIES,
+        SET_TABLE_VLAN_TMAC,
+        AUDIT_GROUPS,
+        SET_GROUPS,
+        VERIFY_GROUPS,
+        SET_ADJACENCY_LABELS,
+        EXIT
+    }
+
+    protected void nextDriverState() throws IOException {
+        DriverState currentState = driverState;
+        if (haltStateMachine.get()) {
+            return;
+        }
+        switch (currentState) {
+            case INIT:
+                driverState = DriverState.SET_TABLE_MISS_ENTRIES;
+                setTableMissEntries();
+                sendHandshakeBarrier();
+                break;
+            case SET_TABLE_MISS_ENTRIES:
+                driverState = DriverState.SET_TABLE_VLAN_TMAC;
+                /* TODO: read network configuration
+                boolean isConfigured = getNetworkConfig();
+                if (!isConfigured) {
+                    return; // this will result in a handshake timeout
+                }
+                */
+                populateTableVlan();
+                populateTableTMac();
+                sendHandshakeBarrier();
+                break;
+            case SET_TABLE_VLAN_TMAC:
+                driverState = DriverState.EXIT;
+                driverHandshakeComplete.set(true);
+                log.debug("Driver handshake is complete");
+                break;
+            case EXIT:
+            default:
+                driverState = DriverState.EXIT;
+                log.error("Driver handshake has exited for sw: {}", getStringId());
+        }
+    }
+
+    private void processStatsReply(OFStatsReply sr) {
+        switch (sr.getStatsType()) {
+            case AGGREGATE:
+                break;
+            case DESC:
+                break;
+            case EXPERIMENTER:
+                break;
+            case FLOW:
+                break;
+            case GROUP_DESC:
+                processGroupDesc((OFGroupDescStatsReply) sr);
+                break;
+            case GROUP_FEATURES:
+                processGroupFeatures((OFGroupFeaturesStatsReply) sr);
+                break;
+            case METER_CONFIG:
+                break;
+            case METER_FEATURES:
+                break;
+            case PORT_DESC:
+                break;
+            case TABLE_FEATURES:
+                break;
+            default:
+                break;
+
+        }
+    }
+
+    private void processOFMessage(OFMessage m) throws IOException {
+        switch (m.getType()) {
+            case BARRIER_REPLY:
+                processBarrierReply(m);
+                break;
+
+            case ERROR:
+                processErrorMessage(m);
+                break;
+
+            case GET_ASYNC_REPLY:
+                OFAsyncGetReply asrep = (OFAsyncGetReply) m;
+                decodeAsyncGetReply(asrep);
+                break;
+
+            case PACKET_IN:
+                // not ready to handle packet-ins
+                break;
+
+            case QUEUE_GET_CONFIG_REPLY:
+                // not doing queue config yet
+                break;
+
+            case STATS_REPLY:
+                processStatsReply((OFStatsReply) m);
+                break;
+
+            case ROLE_REPLY: // channelHandler should handle this
+            case PORT_STATUS: // channelHandler should handle this
+            case FEATURES_REPLY: // don't care
+            case FLOW_REMOVED: // don't care
+            default:
+                log.debug("Received message {} during switch-driver subhandshake "
+                        + "from switch {} ... Ignoring message", m, getStringId());
+        }
+    }
+
+    private void processBarrierReply(OFMessage m) throws IOException {
+        if (m.getXid() == barrierXidToWaitFor) {
+            // Driver state-machine progresses to the next state.
+            // If Barrier messages is not received, then eventually
+            // the ChannelHandler state machine will timeout, and the switch
+            // will be disconnected.
+            nextDriverState();
+        } else {
+            log.error("Received incorrect barrier-message xid {} (expected: {}) in "
+                            + "switch-driver state {} for switch {}", m, barrierXidToWaitFor,
+                    driverState, getStringId());
+        }
+    }
+
+    private void processErrorMessage(OFMessage m) {
+        log.error("Switch {} Error {} in DriverState", getStringId(),
+                (OFErrorMsg) m, driverState);
+    }
+
+    private void processGroupFeatures(OFGroupFeaturesStatsReply gfsr) {
+        log.info("Sw: {} Group Features {}", getStringId(), gfsr);
+    }
+
+    private void processGroupDesc(OFGroupDescStatsReply gdsr) {
+        log.info("Sw: {} Group Desc {}", getStringId(), gdsr);
+    }
+
+    /*
+     * Utility functions
+     */
+
+    private void decodeAsyncGetReply(OFAsyncGetReply rep) {
+        long frm = rep.getFlowRemovedMaskEqualMaster();
+        //long frs = rep.getFlowRemovedMaskSlave();
+        long pim = rep.getPacketInMaskEqualMaster();
+        //long pis = rep.getPacketInMaskSlave();
+        long psm = rep.getPortStatusMaskEqualMaster();
+        //long pss = rep.getPortStatusMaskSlave();
+
+        if (role == RoleState.MASTER || role == RoleState.EQUAL) { // should separate
+            log.info("FRM:{}", HexString.toHexString((frm & TEST_FLOW_REMOVED_MASK)));
+            log.info("PIM:{}", HexString.toHexString((pim & TEST_PACKET_IN_MASK)));
+            log.info("PSM:{}", HexString.toHexString((psm & TEST_PORT_STATUS_MASK)));
+        }
+    }
+
+    protected void setTableMissEntries() throws IOException {
+        // set all table-miss-entries
+        populateTableMissEntry(vlanTableId, true, false, false, -1);
+        populateTableMissEntry(tmacTableId, true, false, false, -1);
+        populateTableMissEntry(ipv4UnicastTableId, false, true, true,
+                aclTableId);
+        populateTableMissEntry(mplsTableId, false, true, true,
+                aclTableId);
+        populateTableMissEntry(aclTableId, false, false, false, -1);
+        log.debug("TableMissEntries are set");
+    }
+
+    /**
+     * Adds a table-miss-entry to a pipeline table.
+     * <p>
+     * The table-miss-entry can be added with 'write-actions' or
+     * 'apply-actions'. It can also add a 'goto-table' instruction. By default
+     * if none of the booleans in the call are set, then the table-miss entry is
+     * added with no instructions, which means that if a packet hits the
+     * table-miss-entry, pipeline execution will stop, and the action set
+     * associated with the packet will be executed.
+     *
+     * @param tableToAdd the table to where the table-miss-entry will be added
+     * @param toControllerNow as an APPLY_ACTION instruction
+     * @param toControllerWrite as a WRITE_ACTION instruction
+     * @param toTable as a GOTO_TABLE instruction
+     * @param tableToSend the table to send as per the GOTO_TABLE instruction it
+     *        needs to be set if 'toTable' is true. Ignored of 'toTable' is
+     *        false.
+     * @throws IOException
+     */
+    protected void populateTableMissEntry(int tableToAdd, boolean toControllerNow,
+                                          boolean toControllerWrite,
+                                          boolean toTable, int tableToSend) throws IOException {
+        OFOxmList oxmList = OFOxmList.EMPTY;
+        OFMatchV3 match = factory.buildMatchV3()
+                .setOxmList(oxmList)
+                .build();
+        OFAction outc = factory.actions()
+                .buildOutput()
+                .setPort(OFPort.CONTROLLER)
+                .setMaxLen(OFPCML_NO_BUFFER)
+                .build();
+        List<OFInstruction> instructions = new ArrayList<OFInstruction>();
+        if (toControllerNow) {
+            // table-miss instruction to send to controller immediately
+            OFInstruction instr = factory.instructions()
+                    .buildApplyActions()
+                    .setActions(Collections.singletonList(outc))
+                    .build();
+            instructions.add(instr);
+        }
+
+        if (toControllerWrite) {
+            // table-miss instruction to write-action to send to controller
+            // this will be executed whenever the action-set gets executed
+            OFInstruction instr = factory.instructions()
+                    .buildWriteActions()
+                    .setActions(Collections.singletonList(outc))
+                    .build();
+            instructions.add(instr);
+        }
+
+        if (toTable) {
+            // table-miss instruction to goto-table x
+            OFInstruction instr = factory.instructions()
+                    .gotoTable(TableId.of(tableToSend));
+            instructions.add(instr);
+        }
+
+        if (!toControllerNow && !toControllerWrite && !toTable) {
+            // table-miss has no instruction - at which point action-set will be
+            // executed - if there is an action to output/group in the action
+            // set
+            // the packet will be sent there, otherwise it will be dropped.
+            instructions = (List<OFInstruction>) Collections.EMPTY_LIST;
+        }
+
+        OFMessage tableMissEntry = factory.buildFlowAdd()
+                .setTableId(TableId.of(tableToAdd))
+                .setMatch(match) // match everything
+                .setInstructions(instructions)
+                .setPriority(MIN_PRIORITY)
+                .setBufferId(OFBufferId.NO_BUFFER)
+                .setIdleTimeout(0)
+                .setHardTimeout(0)
+                .setXid(getNextTransactionId())
+                .build();
+        write(tableMissEntry);
+    }
+
+    private void populateTableVlan() throws IOException {
+        List<OFMessage> msglist = new ArrayList<OFMessage>();
+        for (OFPortDesc p : getPorts()) {
+            int pnum = p.getPortNo().getPortNumber();
+            if (U32.of(pnum).compareTo(U32.of(OFPort.MAX.getPortNumber())) < 1) {
+                OFOxmInPort oxp = factory.oxms().inPort(p.getPortNo());
+                OFOxmVlanVid oxv = factory.oxms()
+                        .vlanVid(OFVlanVidMatch.UNTAGGED);
+                OFOxmList oxmList = OFOxmList.of(oxp, oxv);
+                OFMatchV3 match = factory.buildMatchV3()
+                        .setOxmList(oxmList).build();
+
+                // TODO: match on vlan-tagged packets for vlans configured on
+                // subnet ports and strip-vlan
+
+                OFInstruction gotoTbl = factory.instructions().buildGotoTable()
+                        .setTableId(TableId.of(tmacTableId)).build();
+                List<OFInstruction> instructions = new ArrayList<OFInstruction>();
+                instructions.add(gotoTbl);
+                OFMessage flowEntry = factory.buildFlowAdd()
+                        .setTableId(TableId.of(vlanTableId))
+                        .setMatch(match)
+                        .setInstructions(instructions)
+                        .setPriority(1000) // does not matter - all rules
+                                // exclusive
+                        .setBufferId(OFBufferId.NO_BUFFER)
+                        .setIdleTimeout(0)
+                        .setHardTimeout(0)
+                        .setXid(getNextTransactionId())
+                        .build();
+                msglist.add(flowEntry);
+            }
+        }
+        write(msglist);
+        log.debug("Adding {} port/vlan-rules in sw {}", msglist.size(), getStringId());
+    }
+
+    private void populateTableTMac() throws IOException {
+        // match for router-mac and ip-packets
+        OFOxmEthType oxe = factory.oxms().ethType(EthType.IPv4);
+
+        /* TODO: need to read network config and need to allow only
+         the packets with DMAC as the correspondent router MAC address
+         Until network configuration is implemented, all packets are allowed
+
+        OFOxmEthDst dmac = factory.oxms().ethDst(getRouterMacAddr());
+        OFOxmList oxmListIp = OFOxmList.of(dmac, oxe);
+        OFMatchV3 matchIp = factory.buildMatchV3()
+                .setOxmList(oxmListIp).build();
+        */
+        OFOxmList oxmList = OFOxmList.EMPTY;
+        OFMatchV3 matchIp = factory.buildMatchV3()
+                .setOxmList(oxmList)
+                .build();
+
+        OFInstruction gotoTblIp = factory.instructions().buildGotoTable()
+                .setTableId(TableId.of(ipv4UnicastTableId)).build();
+        List<OFInstruction> instructionsIp = Collections.singletonList(gotoTblIp);
+        OFMessage ipEntry = factory.buildFlowAdd()
+                .setTableId(TableId.of(tmacTableId))
+                .setMatch(matchIp)
+                .setInstructions(instructionsIp)
+                .setPriority(1000) // strict priority required lower than
+                        // multicastMac
+                .setBufferId(OFBufferId.NO_BUFFER)
+                .setIdleTimeout(0)
+                .setHardTimeout(0)
+                .setXid(getNextTransactionId())
+                .build();
+
+        // match for router-mac and mpls packets
+        OFOxmEthType oxmpls = factory.oxms().ethType(EthType.MPLS_UNICAST);
+        /* TODO: need to read network config and need to allow only
+         the packets with DMAC as the correspondent router MAC address
+        OFOxmList oxmListMpls = OFOxmList.of(dmac, oxmpls);
+        OFMatchV3 matchMpls = factory.buildMatchV3()
+                .setOxmList(oxmListMpls).build();
+        */
+        OFOxmList oxmListMpls = OFOxmList.EMPTY;
+        OFMatchV3 matchMpls = factory.buildMatchV3()
+                .setOxmList(oxmList)
+                .build();
+
+        OFInstruction gotoTblMpls = factory.instructions().buildGotoTable()
+                .setTableId(TableId.of(mplsTableId)).build();
+        List<OFInstruction> instructionsMpls = Collections.singletonList(gotoTblMpls);
+        OFMessage mplsEntry = factory.buildFlowAdd()
+                .setTableId(TableId.of(tmacTableId))
+                .setMatch(matchMpls)
+                .setInstructions(instructionsMpls)
+                .setPriority(1001) // strict priority required lower than
+                        // multicastMac
+                .setBufferId(OFBufferId.NO_BUFFER)
+                .setIdleTimeout(0)
+                .setHardTimeout(0)
+                .setXid(getNextTransactionId())
+                .build();
+
+        log.debug("Adding termination-mac-rules in sw {}", getStringId());
+        List<OFMessage> msglist = new ArrayList<OFMessage>(2);
+        msglist.add(ipEntry);
+        msglist.add(mplsEntry);
+        write(msglist);
+    }
+
+    private MacAddress getRouterMacAddr() {
+        // TODO: need to read network config : RouterIp
+        return MacAddress.of("00:00:00:00:00:00");
+    }
+
+    private TableId getTableId(TableType tableType) {
+        switch (tableType) {
+            case IP:
+                return TableId.of(ipv4UnicastTableId);
+            case MPLS:
+                return TableId.of(mplsTableId);
+            case ACL:
+                return TableId.of(aclTableId);
+            default: {
+                log.error("Table type {} is not supported in the driver", tableType);
+                return TableId.NONE;
+            }
+        }
+    }
+
+    private void sendHandshakeBarrier() throws IOException {
+        long xid = getNextTransactionId();
+        barrierXidToWaitFor = xid;
+        OFBarrierRequest br = factory()
+                .buildBarrierRequest()
+                .setXid(xid)
+                .build();
+        write(br);
+    }
+}
\ No newline at end of file
diff --git a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java
index 32dfefd..f80ab06 100644
--- a/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java
+++ b/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java
@@ -166,8 +166,13 @@
 
     private void applyRule(FlowRule flowRule) {
         OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId().uri()));
-        sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
-                                          Optional.empty()).buildFlowAdd());
+        if (flowRule.type() == FlowRule.Type.DEFAULT) {
+            sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
+                    Optional.empty()).buildFlowAdd());
+        } else {
+            sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
+                    Optional.empty()).buildFlowAdd(), getTableType(flowRule.type()));
+        }
     }
 
 
@@ -181,8 +186,13 @@
 
     private void removeRule(FlowRule flowRule) {
         OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId().uri()));
-        sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
-                                          Optional.empty()).buildFlowDel());
+        if (flowRule.type() == FlowRule.Type.DEFAULT) {
+            sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
+                    Optional.empty()).buildFlowDel());
+        } else {
+            sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
+                    Optional.empty()).buildFlowDel(), getTableType(flowRule.type()));
+        }
     }
 
     @Override
@@ -195,11 +205,13 @@
     public Future<CompletedBatchOperation> executeBatch(BatchOperation<FlowRuleBatchEntry> batch) {
         final Set<Dpid> sws = Sets.newConcurrentHashSet();
         final Map<Long, FlowRuleBatchEntry> fmXids = new HashMap<>();
+
         /*
          * Use identity hash map for reference equality as we could have equal
          * flow mods for different switches.
          */
         Map<OFFlowMod, OpenFlowSwitch> mods = Maps.newIdentityHashMap();
+        Map<OFFlowMod, OpenFlowSwitch.TableType> modTypes = Maps.newIdentityHashMap();
         for (FlowRuleBatchEntry fbe : batch.getOperations()) {
             FlowRule flowRule = fbe.target();
             final Dpid dpid = Dpid.dpid(flowRule.deviceId().uri());
@@ -235,6 +247,7 @@
             }
             if (mod != null) {
                 mods.put(mod, sw);
+                modTypes.put(mod, getTableType(flowRule.type()));
                 fmXids.put(flowModXid, fbe);
             } else {
                 log.error("Conversion of flowrule {} failed.", flowRule);
@@ -249,12 +262,29 @@
         for (Map.Entry<OFFlowMod, OpenFlowSwitch> entry : mods.entrySet()) {
             OpenFlowSwitch sw = entry.getValue();
             OFFlowMod mod = entry.getKey();
-            sw.sendMsg(mod);
+            OpenFlowSwitch.TableType tableType = modTypes.get(mod);
+            if (tableType == OpenFlowSwitch.TableType.NONE) {
+                sw.sendMsg(mod);
+            } else {
+                sw.sendMsg(mod, tableType);
+            }
         }
         installation.verify();
         return installation;
     }
 
+    private OpenFlowSwitch.TableType getTableType(FlowRule.Type type) {
+        switch (type) {
+            case IP:
+                return OpenFlowSwitch.TableType.IP;
+            case MPLS:
+                return OpenFlowSwitch.TableType.MPLS;
+            case ACL:
+                return OpenFlowSwitch.TableType.ACL;
+            default:
+                return OpenFlowSwitch.TableType.NONE;
+        }
+    }
 
     private class InternalFlowProvider
             implements OpenFlowSwitchListener, OpenFlowEventListener {