Server driver implements FlowRuleProgrammable

This patch extends the server device driver with the
ability to manage FlowRule objects on the network interface
cards (NICs) of commodity servers.
With a minimal modification in an existing ONOS class (i.e.,
DefaultFlowRule.java), the server device driver re-uses the
FlowRule and FlowEntry concepts to implement a sub-class
translator that converts FlowRule objects into NicFlowRule
objects.
Currently this new feature supports a handful of possible
NIC rule actions but it can be easily extended in the future.

Addressed comment made by ONOS reviewers and fixed two bugs.
One bug was related to rule installation (ability to send
an array of rules at the same time), while the other bug was
related to the hash code of a NicFlowRule.

Refactoring of this driver according to ONOS reviewers's
comments.

Change-Id: Ie76947df120d6e0df86acf9e5917e237653a8cea
Signed-off-by: Georgios Katsikas <katsikas.gp@gmail.com>
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/FlowRuleProgrammableServerImpl.java b/drivers/server/src/main/java/org/onosproject/drivers/server/FlowRuleProgrammableServerImpl.java
new file mode 100644
index 0000000..7177dc0
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/FlowRuleProgrammableServerImpl.java
@@ -0,0 +1,382 @@
+/*
+ * Copyright 2018 Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.server;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Sets;
+
+import org.slf4j.Logger;
+
+import org.onosproject.drivers.server.devices.nic.NicFlowRule;
+import org.onosproject.drivers.server.devices.nic.NicRxFilter.RxFilter;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleProgrammable;
+import org.onosproject.net.flow.FlowRuleService;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import javax.ws.rs.ProcessingException;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Manages rules on commodity server devices, by
+ * converting ONOS FlowRule objetcs into
+ * network interface card (NIC) rules and vice versa.
+ */
+public class FlowRuleProgrammableServerImpl extends BasicServerDriver
+        implements FlowRuleProgrammable {
+
+    private final Logger log = getLogger(getClass());
+
+    /**
+     * Resource endpoints of the server agent (REST server-side).
+     */
+    private static final String RULE_MANAGEMENT_URL = BASE_URL + "/rules";
+
+    /**
+     * Parameters to be exchanged with the server's agent.
+     */
+    private static final String PARAM_RULES        = "rules";
+    private static final String PARAM_NIC_ID       = "nicId";
+    private static final String PARAM_CPU_ID       = "cpuId";
+    private static final String PARAM_CPU_RULES    = "cpuRules";
+    private static final String PARAM_RULE_ID      = "ruleId";
+    private static final String PARAM_RULE_CONTENT = "ruleContent";
+
+    @Override
+    public Collection<FlowEntry> getFlowEntries() {
+        DeviceId deviceId = getHandler().data().deviceId();
+        checkNotNull(deviceId, DEVICE_ID_NULL);
+
+        // Expected FlowEntries installed through ONOS
+        FlowRuleService flowService = getHandler().get(FlowRuleService.class);
+        Iterable<FlowEntry> flowEntries = flowService.getFlowEntries(deviceId);
+
+        // Hit the path that provides the server's flow rules
+        InputStream response = null;
+        try {
+            response = getController().get(deviceId, RULE_MANAGEMENT_URL, JSON);
+        } catch (ProcessingException pEx) {
+            log.error("Failed to get flow entries from device: {}", deviceId);
+            return Collections.EMPTY_LIST;
+        }
+
+        // Load the JSON into objects
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectNode objNode = null;
+        try {
+            Map<String, Object> jsonMap  = mapper.readValue(response, Map.class);
+            JsonNode jsonNode = mapper.convertValue(jsonMap, JsonNode.class);
+            objNode = (ObjectNode) jsonNode;
+        } catch (IOException ioEx) {
+            log.error("Failed to get flow entries from device: {}", deviceId);
+            return Collections.EMPTY_LIST;
+        }
+
+        if (objNode == null) {
+            log.error("Failed to get flow entries from device: {}", deviceId);
+            return Collections.EMPTY_LIST;
+        }
+
+        JsonNode scsNode = objNode.path(PARAM_RULES);
+
+        // Here we store the trully installed rules
+        Collection<FlowEntry> actualFlowEntries =
+            Sets.<FlowEntry>newConcurrentHashSet();
+
+        for (JsonNode scNode : scsNode) {
+            String scId = get(scNode, PARAM_ID);
+            String rxFilter = get(
+                scNode.path(NIC_PARAM_RX_FILTER), NIC_PARAM_RX_METHOD);
+
+            // Only Flow-based RxFilter is permitted
+            if (RxFilter.getByName(rxFilter) != RxFilter.FLOW) {
+                log.warn("Device with Rx filter {} is not managed by this driver",
+                    rxFilter.toString().toUpperCase());
+                continue;
+            }
+
+            // Each device might have multiple NICs
+            for (JsonNode nicNode : scNode.path(PARAM_NICS)) {
+                String nicId = get(nicNode, PARAM_NIC_ID);
+                JsonNode cpusNode = nicNode.path(PARAM_CPUS);
+
+                // Each NIC can dispatch to multiple CPU cores
+                for (JsonNode cpuNode : cpusNode) {
+                    String cpuId = get(cpuNode, PARAM_CPU_ID);
+                    JsonNode rulesNode = cpuNode.path(PARAM_CPU_RULES);
+
+                    // Multiple rules might correspond to each CPU core
+                    for (JsonNode ruleNode : rulesNode) {
+                        long ruleId = ruleNode.path(PARAM_RULE_ID).asLong();
+                        String ruleContent = get(ruleNode, PARAM_RULE_CONTENT);
+
+                        // Search for this rule ID in ONOS's store
+                        FlowRule r = findRuleInFlowEntries(flowEntries, ruleId);
+
+                        // Local rule, not present in the controller => Ignore
+                        if (r == null) {
+                            continue;
+                        // Rule trully present in the data plane => Add
+                        } else {
+                            actualFlowEntries.add(
+                                new DefaultFlowEntry(
+                                    r, FlowEntry.FlowEntryState.ADDED, 0, 0, 0));
+                        }
+                    }
+                }
+            }
+        }
+
+        return actualFlowEntries;
+    }
+
+    @Override
+    public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
+        DeviceId deviceId = getHandler().data().deviceId();
+        checkNotNull(deviceId, DEVICE_ID_NULL);
+
+        // Set of truly-installed rules to be reported
+        Set<FlowRule> installedRules = Sets.<FlowRule>newConcurrentHashSet();
+
+        // Splits the rule set into multiple ones, grouped by traffic class ID
+        Map<String, Set<FlowRule>> rulesPerTc = groupRules(rules);
+
+        // Install NIC rules on a per-traffic class basis
+        for (Map.Entry<String, Set<FlowRule>> entry : rulesPerTc.entrySet()) {
+            String tcId = entry.getKey();
+            Set<FlowRule> tcRuleSet = entry.getValue();
+
+            installedRules.addAll(
+                installNicFlowRules(deviceId, tcId, tcRuleSet)
+            );
+        }
+
+        return installedRules;
+    }
+
+    @Override
+    public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
+        DeviceId deviceId = getHandler().data().deviceId();
+        checkNotNull(deviceId, DEVICE_ID_NULL);
+
+        // Set of truly-removed rules to be reported
+        Set<FlowRule> removedRules = Sets.<FlowRule>newConcurrentHashSet();
+
+        // for (FlowRule rule : rules) {
+        rules.forEach(rule -> {
+            if (removeNicFlowRule(deviceId, rule.id().value())) {
+                removedRules.add(rule);
+            }
+        });
+
+        return removedRules;
+    }
+
+    /**
+     * Groups a set of FlowRules by their traffic class ID.
+     *
+     * @param rules set of NIC rules to install
+     * @return a map of traffic class IDs to their set of NIC rules
+     */
+    private Map<String, Set<FlowRule>> groupRules(Collection<FlowRule> rules) {
+        Map<String, Set<FlowRule>> rulesPerTc =
+            new ConcurrentHashMap<String, Set<FlowRule>>();
+
+        rules.forEach(rule -> {
+            if (!(rule instanceof FlowEntry)) {
+                NicFlowRule nicRule = (NicFlowRule) rule;
+                String tcId = nicRule.trafficClassId();
+
+                // Create a bucket of flow rules for this traffic class
+                if (!rulesPerTc.containsKey(tcId)) {
+                    rulesPerTc.put(tcId, Sets.<FlowRule>newConcurrentHashSet());
+                }
+
+                Set<FlowRule> tcRuleSet = rulesPerTc.get(tcId);
+                tcRuleSet.add(nicRule);
+            }
+        });
+
+        return rulesPerTc;
+    }
+
+    /**
+     * Searches for a flow rule with certain ID.
+     *
+     * @param flowEntries a list of FlowEntries
+     * @param ruleId a desired rule ID
+     * @return a FlowRule that corresponds to the desired ID or null
+     */
+    private FlowRule findRuleInFlowEntries(
+            Iterable<FlowEntry> flowEntries, long ruleId) {
+        for (FlowEntry fe : flowEntries) {
+            if (fe.id().value() == ruleId) {
+                return (FlowRule) fe;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Installs a set of FlowRules of the same traffic class ID
+     * on a server device.
+     *
+     * @param deviceId target server device ID
+     * @param trafficClassId traffic class ID of the NIC rules
+     * @param rules set of NIC rules to install
+     * @return a set of successfully installed NIC rules
+     */
+    private Collection<FlowRule> installNicFlowRules(
+            DeviceId deviceId, String trafficClassId,
+            Collection<FlowRule> rules) {
+        if (rules.isEmpty()) {
+            return Collections.EMPTY_LIST;
+        }
+
+        ObjectMapper mapper = new ObjectMapper();
+
+        // Create the object node to host the list of rules
+        ObjectNode scsObjNode = mapper.createObjectNode();
+
+        // Add the service chain's traffic class ID that requested these rules
+        scsObjNode.put(BasicServerDriver.PARAM_ID, trafficClassId);
+
+        // Create the object node to host the Rx filter method
+        ObjectNode methodObjNode = mapper.createObjectNode();
+        methodObjNode.put(BasicServerDriver.NIC_PARAM_RX_METHOD, "flow");
+        scsObjNode.put(BasicServerDriver.NIC_PARAM_RX_FILTER, methodObjNode);
+
+        // Map each core to an array of rule IDs and rules
+        Map<Long, ArrayNode> cpuObjSet =
+            new ConcurrentHashMap<Long, ArrayNode>();
+
+        String nicId = null;
+
+        for (FlowRule rule : rules) {
+            NicFlowRule nicRule = (NicFlowRule) rule;
+            long coreIndex = nicRule.cpuCoreIndex();
+
+            // Keep the ID of the target NIC
+            if (nicId == null) {
+                long nicIfaceNb = nicRule.interfaceNumber();
+                checkArgument(nicIfaceNb > 0,
+                    "Attempted to install NIC rules on an invalid NIC ID: "
+                    + nicIfaceNb);
+                // NIC IDs in the dataplane start from 0
+                nicId = Long.toString(nicIfaceNb - 1);
+            }
+
+            // Create a JSON array for this CPU core
+            if (!cpuObjSet.containsKey(coreIndex)) {
+                cpuObjSet.put(coreIndex, mapper.createArrayNode());
+            }
+
+            // The array of rules that corresponds to this CPU core
+            ArrayNode ruleArrayNode = cpuObjSet.get(coreIndex);
+
+            // Each rule has an ID and a content
+            ObjectNode ruleNode = mapper.createObjectNode();
+            ruleNode.put("ruleId", nicRule.id().value());
+            ruleNode.put("ruleContent", nicRule.ruleBody());
+
+            ruleArrayNode.add(ruleNode);
+        }
+
+        ObjectNode nicObjNode = mapper.createObjectNode();
+        // TODO: Fix this as it might cause issues
+        nicObjNode.put("nicId", "fd" + nicId);
+
+        ArrayNode cpusArrayNode = nicObjNode.putArray(PARAM_CPUS);
+
+        // Convert the map of CPU cores to arrays of rules to JSON
+        for (Map.Entry<Long, ArrayNode> entry : cpuObjSet.entrySet()) {
+            long coreIndex = entry.getKey();
+            ArrayNode ruleArrayNode = entry.getValue();
+
+            ObjectNode cpuObjNode = mapper.createObjectNode();
+            cpuObjNode.put("cpuId", coreIndex);
+            cpuObjNode.putArray(PARAM_CPU_RULES).addAll(ruleArrayNode);
+
+            cpusArrayNode.add(cpuObjNode);
+        }
+
+        scsObjNode.putArray(PARAM_NICS).add(nicObjNode);
+
+        // Create the object node to host all the data
+        ObjectNode sendObjNode = mapper.createObjectNode();
+        sendObjNode.putArray(PARAM_RULES).add(scsObjNode);
+
+        // Post the NIC rules to the server
+        int response = getController().post(
+            deviceId, RULE_MANAGEMENT_URL,
+            new ByteArrayInputStream(sendObjNode.toString().getBytes()), JSON);
+
+        // Upon an error, return an empty set of rules
+        if (!checkStatusCode(response)) {
+            log.error("Failed to install flow rules on device {}", deviceId);
+            return Collections.EMPTY_LIST;
+        }
+
+        log.info("Successfully installed {} flow rules on device {}",
+            rules.size(), deviceId);
+
+        // .. or all of them
+        return rules;
+    }
+
+    /**
+     * Removes a FlowRule from a server device.
+     *
+     * @param deviceId target server device ID
+     * @param ruleId NIC rule ID to be removed
+     * @return boolean removal status
+     */
+    private boolean removeNicFlowRule(DeviceId deviceId, long ruleId) {
+        // Remove rule with ID from this server
+        int response = getController().delete(deviceId,
+            RULE_MANAGEMENT_URL + "/" + Long.toString(ruleId), null, JSON);
+
+        if (!checkStatusCode(response)) {
+            log.error("Failed to remove flow rule {} from device {}",
+                ruleId, deviceId);
+            return false;
+        }
+
+        log.info("Successfully removed flow rule {} from device {}",
+            ruleId, deviceId);
+
+        return true;
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/DefaultDpdkNicFlowRule.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/DefaultDpdkNicFlowRule.java
new file mode 100644
index 0000000..5831b79
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/DefaultDpdkNicFlowRule.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.server.devices.nic;
+
+import org.onosproject.net.flow.FlowRule;
+
+import org.onlab.packet.MacAddress;
+
+/**
+ * Implementation of a DPDK-based network interface card (NIC) flow rule.
+ */
+public class DefaultDpdkNicFlowRule extends DefaultNicFlowRule {
+
+    public DefaultDpdkNicFlowRule(
+            FlowRule         flowRule,
+            String           trafficClassId,
+            String           interfaceName,
+            long             interfaceNumber,
+            long             cpuCoreIndex,
+            NicRuleScope     scope) {
+        super(flowRule, trafficClassId, interfaceName, interfaceNumber,
+            cpuCoreIndex, scope);
+    }
+
+    @Override
+    public String createRule() {
+        String rule = "flow create " + Long.toString(this.interfaceNumber) + " ";
+        return rule + ruleBody();
+    }
+
+    @Override
+    public String removeRule() {
+        String rule = "flow remove " + Long.toString(this.interfaceNumber) + " ";
+        return rule + ruleBody();
+    }
+
+    @Override
+    public String ruleBody() {
+        String rule = "";
+
+        rule += this.scope() + " pattern ";
+
+        if (this.hasEthernet()) {
+            rule += "eth ";
+
+            if (this.ethernetType() != null) {
+                rule += "type is " + Integer.toString(this.ethernetTypeValue()) + " ";
+            }
+
+            if (this.ethernetSrcAddress() != null) {
+                rule += "src spec " + this.ethernetSrcAddress().toString() + " ";
+                rule += "src mask " + MacAddress.BROADCAST.toString() + " ";
+            }
+
+            if (this.ethernetDstAddress() != null) {
+                rule += "dst spec " + this.ethernetDstAddress().toString() + " ";
+                rule += "dst mask " + MacAddress.BROADCAST.toString() + " ";
+            }
+
+            rule += "/ ";
+        }
+
+        if (this.hasIpv4()) {
+            rule += "ipv4 ";
+
+            if (this.ipv4Protocol() > 0) {
+                rule += "proto spec " + Integer.toString(this.ipv4Protocol()) + " ";
+                rule += "proto mask 0x0 ";
+            }
+
+            if (this.ipv4SrcAddress() != null) {
+                rule += "src is " + this.ipv4SrcAddress().toString() + " ";
+            }
+
+            if (this.ipv4SrcMask() != null) {
+                rule += "src mask " + this.ipv4SrcMask().toString() + " ";
+            }
+
+            if (this.ipv4DstAddress() != null) {
+                rule += "dst is " + this.ipv4DstAddress().toString() + " ";
+            }
+
+            if (this.ipv4DstMask() != null) {
+                rule += "dst mask " + this.ipv4DstMask().toString() + " ";
+            }
+
+            rule += "/ ";
+        }
+
+        if (this.hasTransport()) {
+
+            if (this.hasUdp()) {
+                rule += "udp ";
+            } else if (this.hasTcp()) {
+                rule += "tcp ";
+            }
+
+            if (this.sourcePort() > 0) {
+                rule += "src is " + Integer.toString(this.sourcePort()) + " ";
+            }
+
+            if (this.destinationPort() > 0) {
+                rule += "dst is " + Integer.toString(this.destinationPort()) + " ";
+            }
+
+            rule += "/ ";
+        }
+
+        rule += "end ";
+
+        if (this.actions() != null) {
+            rule += "actions ";
+
+            for (NicRuleAction action : this.actions()) {
+                rule += action.actionType().toString() + " ";
+
+                // No subsequent field
+                if (action.actionField().isEmpty()) {
+                    continue;
+                }
+
+                // A subsequent field is associated with a value
+                rule += action.actionField() + " ";
+                rule += Long.toString(action.actionValue()) + " ";
+            }
+
+            rule += "/ end";
+        }
+
+        return rule;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return super.equals(obj);
+    }
+
+    @Override
+    public int hashCode() {
+        return super.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return super.toString();
+    }
+
+    /**
+     * Returns a DPDK NIC rule builder.
+     *
+     * @return builder
+     */
+    public static Builder nicRulebuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Default DPDK NIC rule builder.
+     */
+    public static final class Builder extends DefaultNicFlowRule.Builder {
+
+        public Builder() {
+            super();
+        }
+
+        @Override
+        public NicFlowRule build() {
+            return new DefaultDpdkNicFlowRule(
+                flowRule, trafficClassId, interfaceName, interfaceNumber,
+                cpuCoreIndex, scope);
+        }
+
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/DefaultNicFlowRule.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/DefaultNicFlowRule.java
new file mode 100644
index 0000000..d50a2b0
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/DefaultNicFlowRule.java
@@ -0,0 +1,498 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.server.devices.nic;
+
+import com.google.common.base.Strings;
+
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.criteria.EthTypeCriterion;
+import org.onosproject.net.flow.criteria.EthCriterion;
+import org.onosproject.net.flow.criteria.IPProtocolCriterion;
+import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.criteria.UdpPortCriterion;
+import org.onosproject.net.flow.criteria.TcpPortCriterion;
+
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions.SetQueueInstruction;
+import org.onosproject.net.flow.instructions.Instructions.MeterInstruction;
+
+import org.onlab.packet.EthType;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.Ip4Prefix;
+
+import org.slf4j.Logger;
+
+import java.util.Objects;
+import java.util.HashSet;
+import java.util.Set;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.flow.criteria.Criterion.Type.ETH_TYPE;
+import static org.onosproject.net.flow.criteria.Criterion.Type.ETH_SRC;
+import static org.onosproject.net.flow.criteria.Criterion.Type.ETH_DST;
+import static org.onosproject.net.flow.criteria.Criterion.Type.IP_PROTO;
+import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_SRC;
+import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
+import static org.onosproject.net.flow.criteria.Criterion.Type.UDP_SRC;
+import static org.onosproject.net.flow.criteria.Criterion.Type.UDP_DST;
+import static org.onosproject.net.flow.criteria.Criterion.Type.TCP_SRC;
+import static org.onosproject.net.flow.criteria.Criterion.Type.TCP_DST;
+import static org.onosproject.net.flow.instructions.Instruction.Type.NOACTION;
+import static org.onosproject.net.flow.instructions.Instruction.Type.QUEUE;
+import static org.onosproject.net.flow.instructions.Instruction.Type.METER;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of an abstract NIC rule.
+ */
+public abstract class DefaultNicFlowRule extends DefaultFlowRule implements NicFlowRule {
+
+    protected static final Logger log = getLogger(DefaultNicFlowRule.class);
+
+    // Additional members that a NIC rule requires
+    protected final String trafficClassId;
+    protected final String interfaceName;
+    protected long interfaceNumber;
+    protected long cpuCoreIndex;
+    protected final NicRuleScope scope;
+
+    // Derive from FlowRule's selector
+    protected EthTypeCriterion ethTypeCriterion;
+    protected EthCriterion ethSrcAddrCriterion;
+    protected EthCriterion ethDstAddrCriterion;
+
+    protected IPProtocolCriterion ipv4ProtoCriterion;
+    protected IPCriterion ipv4SrcAddrCriterion;
+    protected IPCriterion ipv4SrcMaskCriterion;
+    protected IPCriterion ipv4DstAddrCriterion;
+    protected IPCriterion ipv4DstMaskCriterion;
+
+    protected UdpPortCriterion udpSrcPortCriterion;
+    protected UdpPortCriterion udpDstPortCriterion;
+    protected TcpPortCriterion tcpSrcPortCriterion;
+    protected TcpPortCriterion tcpDstPortCriterion;
+
+    // Derives from FlowRule's treatment
+    protected Set<NicRuleAction> actions;
+
+    protected DefaultNicFlowRule(
+            FlowRule         flowRule,
+            String           trafficClassId,
+            String           interfaceName,
+            long             interfaceNumber,
+            long             cpuCoreIndex,
+            NicRuleScope     scope) {
+        super(flowRule);
+
+        checkNotNull(!Strings.isNullOrEmpty(trafficClassId),
+            "NIC rule's traffic class ID is NULL or empty");
+        checkNotNull(interfaceName,
+            "NIC rule's interface name is NULL");
+        checkArgument(interfaceNumber >= 0,
+            "NIC rule's interface number must not be negative");
+        checkArgument(cpuCoreIndex >= 0,
+            "NIC rule's CPU core index must not be negative");
+        checkNotNull(scope, "NIC rule's scope is NULL");
+
+        this.trafficClassId  = trafficClassId;
+        this.interfaceName   = interfaceName;
+        this.interfaceNumber = interfaceNumber;
+        this.cpuCoreIndex    = cpuCoreIndex;
+        this.scope           = scope;
+
+        this.populate();
+    }
+
+    protected DefaultNicFlowRule(FlowRule flowRule) {
+        super(flowRule);
+
+        this.trafficClassId  = Builder.DEFAULT_TRAFFIC_CLASS_ID;
+        this.interfaceName   = "";
+        this.interfaceNumber = Builder.DEFAULT_INTERFACE_NB;
+        this.cpuCoreIndex    = Builder.DEFAULT_CPU_CORE_INDEX;
+        this.scope           = Builder.DEFAULT_RULE_SCOPE;
+
+        this.populate();
+    }
+
+    /**
+     * Parses FlowRule's traffic selector and treatment
+     * and keeps relevant information for this NIC rule.
+     */
+    private void populate() {
+        this.ethTypeCriterion = (EthTypeCriterion) this.selector().getCriterion(ETH_TYPE);
+        this.ethSrcAddrCriterion = (EthCriterion) this.selector().getCriterion(ETH_SRC);
+        this.ethDstAddrCriterion = (EthCriterion) this.selector().getCriterion(ETH_DST);
+        this.ipv4ProtoCriterion = (IPProtocolCriterion) this.selector().getCriterion(IP_PROTO);
+        this.ipv4SrcAddrCriterion = (IPCriterion) this.selector().getCriterion(IPV4_SRC);
+        this.ipv4DstAddrCriterion = (IPCriterion) this.selector().getCriterion(IPV4_DST);
+        // Is there a criterion for IP masks?
+        this.ipv4SrcMaskCriterion = (IPCriterion) null;
+        this.ipv4DstMaskCriterion = (IPCriterion) null;
+        this.udpSrcPortCriterion = (UdpPortCriterion) this.selector().getCriterion(UDP_SRC);
+        this.udpDstPortCriterion = (UdpPortCriterion) this.selector().getCriterion(UDP_DST);
+        this.tcpSrcPortCriterion = (TcpPortCriterion) this.selector().getCriterion(TCP_SRC);
+        this.tcpDstPortCriterion = (TcpPortCriterion) this.selector().getCriterion(TCP_DST);
+
+        this.actions = new HashSet<NicRuleAction>();
+        // TODO: Expand this translator with more actions
+        for (Instruction instr : this.treatment().allInstructions()) {
+            if (instr.type() == NOACTION) {
+                this.actions.add(new NicRuleAction(NicRuleAction.Action.DROP));
+            } else if (instr.type() == QUEUE) {
+                SetQueueInstruction queueInstruction = (SetQueueInstruction) instr;
+                this.actions.add(
+                    new NicRuleAction(NicRuleAction.Action.QUEUE, queueInstruction.queueId()));
+                this.interfaceNumber = queueInstruction.port().toLong();
+            } else if (instr.type() == METER) {
+                MeterInstruction meterInstruction = (MeterInstruction) instr;
+                this.actions.add(
+                    new NicRuleAction(NicRuleAction.Action.METER, meterInstruction.meterId().id()));
+            }
+        }
+    }
+
+    @Override
+    public FlowRule flowRule() {
+        return this;
+    }
+
+    @Override
+    public String trafficClassId() {
+        return trafficClassId;
+    }
+
+    @Override
+    public String interfaceName() {
+        return interfaceName;
+    }
+
+    @Override
+    public long interfaceNumber() {
+        return interfaceNumber;
+    }
+
+    @Override
+    public long cpuCoreIndex() {
+        return cpuCoreIndex;
+    }
+
+    @Override
+    public NicRuleScope scope() {
+        return scope;
+    }
+
+    @Override
+    public EthType ethernetType() {
+        return (ethTypeCriterion != null) ?
+            ethTypeCriterion.ethType() : null;
+    }
+
+    @Override
+    public short ethernetTypeValue() {
+        return (ethTypeCriterion != null) ?
+            ethTypeCriterion.ethType().toShort() : -1;
+    }
+
+    @Override
+    public MacAddress ethernetSrcAddress() {
+        return (ethSrcAddrCriterion != null) ?
+            ethSrcAddrCriterion.mac() : null;
+    }
+
+    @Override
+    public MacAddress ethernetDstAddress() {
+        return (ethDstAddrCriterion != null) ?
+            ethDstAddrCriterion.mac() : null;
+    }
+
+    @Override
+    public boolean hasEthernet() {
+        return (ethernetType() != null) ||
+               (ethernetSrcAddress() != null) ||
+               (ethernetDstAddress() != null);
+    }
+
+    @Override
+    public short ipv4Protocol() {
+        return (ipv4ProtoCriterion != null) ?
+            ipv4ProtoCriterion.protocol() : -1;
+    }
+
+    @Override
+    public Ip4Prefix ipv4SrcAddress() {
+        return (ipv4SrcAddrCriterion != null) ?
+            ipv4SrcAddrCriterion.ip().getIp4Prefix() : null;
+    }
+
+    @Override
+    public Ip4Prefix ipv4SrcMask() {
+        return (ipv4SrcMaskCriterion != null) ?
+            ipv4SrcMaskCriterion.ip().getIp4Prefix() : null;
+    }
+
+    @Override
+    public Ip4Prefix ipv4DstAddress() {
+        return (ipv4DstAddrCriterion != null) ?
+            ipv4DstAddrCriterion.ip().getIp4Prefix() : null;
+    }
+
+    @Override
+    public Ip4Prefix ipv4DstMask() {
+        return (ipv4DstMaskCriterion != null) ?
+            ipv4DstMaskCriterion.ip().getIp4Prefix() : null;
+    }
+
+    @Override
+    public boolean hasIpv4() {
+        return (ipv4Protocol() > 0) ||
+               (ipv4SrcAddress() != null) ||
+               (ipv4DstAddress() != null);
+    }
+
+    @Override
+    public boolean hasIpv6() {
+        return false;
+    }
+
+    @Override
+    public int sourcePort() {
+        return ((udpSrcPortCriterion != null) ?
+                 udpSrcPortCriterion.udpPort().toInt() :
+                    ((tcpSrcPortCriterion != null) ?
+                      tcpSrcPortCriterion.tcpPort().toInt() : -1));
+    }
+
+    @Override
+    public int destinationPort() {
+        return ((udpDstPortCriterion != null) ?
+                 udpDstPortCriterion.udpPort().toInt() :
+                    ((tcpDstPortCriterion != null) ?
+                      tcpDstPortCriterion.tcpPort().toInt() : -1));
+    }
+
+    @Override
+    public boolean hasUdp() {
+        return (udpSrcPortCriterion != null) || (udpDstPortCriterion != null);
+    }
+
+    @Override
+    public boolean hasTcp() {
+        return (tcpSrcPortCriterion != null) || (tcpDstPortCriterion != null);
+    }
+
+    @Override
+    public boolean hasTransport() {
+        return (sourcePort() > 0) || (destinationPort() > 0);
+    }
+
+    @Override
+    public Set<NicRuleAction> actions() {
+        return actions;
+    }
+
+    @Override
+    public void addAction(NicRuleAction action) {
+        checkNotNull(action, "Cannot add a NULL NIC rule action");
+        if (actions == null) {
+            actions = new HashSet<NicRuleAction>();
+        }
+        actions.add(action);
+    }
+
+    @Override
+    public long queueNumber() {
+        for (NicRuleAction action : actions) {
+            if (action.actionType() == NicRuleAction.Action.QUEUE) {
+                return action.actionValue();
+            }
+        }
+
+        return (long) -1;
+    }
+
+    @Override
+    public abstract String createRule();
+
+    @Override
+    public abstract String removeRule();
+
+    @Override
+    public abstract String ruleBody();
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof DefaultNicFlowRule) {
+            DefaultNicFlowRule that = (DefaultNicFlowRule) obj;
+            return  Objects.equals(trafficClassId, that.trafficClassId) &&
+                    Objects.equals(interfaceName, that.interfaceName) &&
+                    Objects.equals(interfaceNumber, that.interfaceNumber) &&
+                    Objects.equals(cpuCoreIndex, that.cpuCoreIndex) &&
+                    Objects.equals(scope, that.scope) &&
+                    Objects.equals(actions, that.actions) &&
+                    Objects.equals(deviceId(), that.deviceId()) &&
+                    Objects.equals(id(), that.id()) &&
+                    Objects.equals(selector(), that.selector());
+        }
+
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+            trafficClassId, interfaceName, interfaceNumber,
+            cpuCoreIndex, scope, deviceId(), id(), selector()
+        );
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .omitNullValues()
+                .add("Device ID", deviceId())
+                .add("Traffic class ID", trafficClassId())
+                .add("Flow ID", id())
+                .add("Interface name", interfaceName())
+                .add("Interface number", interfaceNumber())
+                .add("CPU core index", cpuCoreIndex())
+                .add("Scope", scope())
+                .add("Traffic selector", selector())
+                .add("Traffic treatment", treatment())
+                .add("Priority", priority())
+                .add("Ethernet type", ethernetTypeValue())
+                .add("Ethernet address source", ethernetSrcAddress())
+                .add("Ethernet address destination", ethernetDstAddress())
+                .add("IP protocol", ipv4Protocol())
+                .add("IP address source", ipv4SrcAddress())
+                .add("IP mask source", ipv4SrcMask())
+                .add("IP address destination", ipv4DstAddress())
+                .add("IP mask destination", ipv4DstMask())
+                .add("Source port", sourcePort())
+                .add("Destination port", destinationPort())
+                .add("Actions", actions())
+                .toString();
+    }
+
+
+    public abstract static class Builder implements NicFlowRule.Builder {
+
+        protected FlowRule flowRule;
+
+        protected String trafficClassId;
+        protected String interfaceName;
+        protected long interfaceNumber;
+        protected long cpuCoreIndex;
+        protected NicRuleScope scope;
+
+        protected EthTypeCriterion ethTypeCriterion;
+        protected EthCriterion ethSrcAddrCriterion;
+        protected EthCriterion ethDstAddrCriterion;
+
+        protected IPProtocolCriterion ipv4ProtoCriterion;
+        protected IPCriterion ipv4SrcAddrCriterion;
+        protected IPCriterion ipv4SrcMaskCriterion;
+        protected IPCriterion ipv4DstAddrCriterion;
+        protected IPCriterion ipv4DstMaskCriterion;
+
+        protected UdpPortCriterion udpSrcPortCriterion;
+        protected UdpPortCriterion udpDstPortCriterion;
+        protected TcpPortCriterion tcpSrcPortCriterion;
+        protected TcpPortCriterion tcpDstPortCriterion;
+
+        protected Set<NicRuleAction> actions;
+
+        protected static final String DEFAULT_TRAFFIC_CLASS_ID = "tc:00001";
+        protected static final long DEFAULT_INTERFACE_NB = (long) 0;
+        protected static final long DEFAULT_CPU_CORE_INDEX = (long) 0;
+        protected static final NicRuleScope DEFAULT_RULE_SCOPE =
+            NicRuleScope.INGRESS;
+
+        protected Builder() {
+            this.flowRule = null;
+
+            this.trafficClassId = null;
+            this.interfaceName = "";
+            this.interfaceNumber = DEFAULT_INTERFACE_NB;
+            this.cpuCoreIndex = DEFAULT_CPU_CORE_INDEX;
+            this.scope = DEFAULT_RULE_SCOPE;
+
+            this.ethTypeCriterion = null;
+            this.ethSrcAddrCriterion = null;
+            this.ethDstAddrCriterion = null;
+            this.ipv4ProtoCriterion = null;
+            this.ipv4SrcAddrCriterion = null;
+            this.ipv4SrcMaskCriterion = null;
+            this.ipv4DstAddrCriterion = null;
+            this.ipv4DstMaskCriterion = null;
+            this.udpSrcPortCriterion = null;
+            this.udpDstPortCriterion = null;
+            this.tcpSrcPortCriterion = null;
+            this.tcpDstPortCriterion = null;
+
+            this.actions = new HashSet<NicRuleAction>();
+        }
+
+        @Override
+        public Builder fromFlowRule(FlowRule flowRule) {
+            this.flowRule = flowRule;
+            return this;
+        }
+
+        @Override
+        public Builder withTrafficClassId(String trafficClassId) {
+            this.trafficClassId = trafficClassId;
+            return this;
+        }
+
+        @Override
+        public Builder withInterfaceName(String interfaceName) {
+            this.interfaceName = interfaceName;
+            return this;
+        }
+
+        @Override
+        public Builder withInterfaceNumber(long interfaceNumber) {
+            this.interfaceNumber = interfaceNumber;
+            return this;
+        }
+
+        @Override
+        public Builder assignedToCpuCore(long cpuCoreIndex) {
+            this.cpuCoreIndex = cpuCoreIndex;
+            return this;
+        }
+
+        @Override
+        public Builder withScope(NicRuleScope scope) {
+            this.scope = scope;
+            return this;
+        }
+
+        @Override
+        public abstract NicFlowRule build();
+
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/FlowRxFilterValue.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/FlowRxFilterValue.java
new file mode 100644
index 0000000..64c792b
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/FlowRxFilterValue.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.drivers.server.devices.nic;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * A flow rule-based Rx filter value.
+ */
+public final class FlowRxFilterValue extends RxFilterValue
+        implements Comparable {
+
+    private long cpuCoreId;
+    private String flowRule;
+
+    /**
+     * Constructs a flow-based Rx filter.
+     */
+    public FlowRxFilterValue() {
+        super();
+        setValue(0);
+        setRule("");
+    }
+
+    /**
+     * Constructs a flow-based Rx filter with CPU core ID.
+     *
+     * @param cpuCoreId a CPU core ID when the flow ends up
+     */
+    public FlowRxFilterValue(long cpuCoreId) {
+        super();
+        setValue(cpuCoreId);
+        setRule("");
+    }
+
+    /**
+     * Constructs a flow-based Rx filter with CPU core ID
+     * and an associated rule.
+     *
+     * @param cpuCoreId a CPU core ID
+     * @param flowRule a flow rule as a string
+     */
+    public FlowRxFilterValue(long cpuCoreId, String flowRule) {
+        super();
+        setValue(cpuCoreId);
+        setRule(flowRule);
+    }
+
+    /**
+     * Constructs a flow-based Rx filter out of an existing one.
+     *
+     * @param other a source FlowRxFilterValue object
+     */
+    public FlowRxFilterValue(FlowRxFilterValue other) {
+        super();
+        setValue(other.value());
+        setRule(other.rule());
+    }
+
+    /**
+     * Returns the value of this Rx filter.
+     *
+     * @return Flow Rx filter value
+     */
+    public long value() {
+        return this.cpuCoreId;
+    }
+
+    /**
+     * Sets the value of this Rx filter.
+     *
+     * @param cpuCoreId a CPU core ID for this Rx filter
+     */
+    public void setValue(long cpuCoreId) {
+        checkArgument(cpuCoreId >= 0,
+            "NIC flow Rx filter has invalid CPU core ID");
+        this.cpuCoreId = cpuCoreId;
+    }
+
+    /**
+     * Returns the rule of this Rx filter.
+     *
+     * @return Flow Rx filter rule
+     */
+    public String rule() {
+        return this.flowRule;
+    }
+
+    /**
+     * Sets the rule of this Rx filter.
+     *
+     * @param flowRule Flow Rx filter rule as a string
+     */
+    public void setRule(String flowRule) {
+        checkNotNull(flowRule,
+            "NIC flow Rx filter rule is NULL");
+        this.flowRule = flowRule;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(this.cpuCoreId, this.flowRule);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if ((obj == null) || (!(obj instanceof FlowRxFilterValue))) {
+            return false;
+        }
+
+        FlowRxFilterValue other = (FlowRxFilterValue) obj;
+
+        return (this.value() == other.value()) &&
+                this.rule().equals(other.rule());
+    }
+
+    @Override
+    public int compareTo(Object other) {
+        if (this == other) {
+            return 0;
+        }
+
+        if (other == null) {
+            return -1;
+        }
+
+        if (other instanceof FlowRxFilterValue) {
+            FlowRxFilterValue otherRxVal = (FlowRxFilterValue) other;
+
+            long thisCoreId  = this.value();
+            long otherCoreId = otherRxVal.value();
+
+            if (thisCoreId > otherCoreId) {
+                return 1;
+            } else if (thisCoreId < otherCoreId) {
+                return -1;
+            } else {
+                return 0;
+            }
+        }
+
+        return -1;
+    }
+
+    @Override
+    public String toString() {
+        return Long.toString(this.value());
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/MacRxFilterValue.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/MacRxFilterValue.java
index dec0276..2676f65 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/MacRxFilterValue.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/MacRxFilterValue.java
@@ -24,20 +24,33 @@
 /**
  * A MAC Rx filter value.
  */
-public class MacRxFilterValue extends RxFilterValue implements Comparable {
+public final class MacRxFilterValue extends RxFilterValue implements Comparable {
 
     private MacAddress mac;
 
+    /**
+     * Constructs a MAC-based Rx filter.
+     */
     public MacRxFilterValue() {
         super();
         this.mac = null;
     }
 
+    /**
+     * Constructs a MAC-based Rx filter with specific MAC address.
+     *
+     * @param mac a MAC address to use as a filter
+     */
     public MacRxFilterValue(MacAddress mac) {
         super();
         setValue(mac);
     }
 
+    /**
+     * Constructs a MAC-based Rx filter out of an existing one.
+     *
+     * @param other a source MacRxFilterValue object
+     */
     public MacRxFilterValue(MacRxFilterValue other) {
         super();
         setValue(other.value());
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/MplsRxFilterValue.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/MplsRxFilterValue.java
index 1d4c155..2b5112f 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/MplsRxFilterValue.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/MplsRxFilterValue.java
@@ -24,20 +24,33 @@
 /**
  * An MPLS Rx filter value.
  */
-public class MplsRxFilterValue extends RxFilterValue {
+public final class MplsRxFilterValue extends RxFilterValue {
 
     private MplsLabel mplsLabel;
 
+    /**
+     * Constructs an MPLS-based Rx filter.
+     */
     public MplsRxFilterValue() {
         super();
         this.mplsLabel = null;
     }
 
+    /**
+     * Constructs an MPLS-based Rx filter with specific label.
+     *
+     * @param mplsLabel an MPLS label to use as a filter
+     */
     public MplsRxFilterValue(MplsLabel mplsLabel) {
         super();
         setValue(mplsLabel);
     }
 
+    /**
+     * Constructs an MPLS-based Rx filter out of an existing one.
+     *
+     * @param other a source MplsRxFilterValue object
+     */
     public MplsRxFilterValue(MplsRxFilterValue other) {
         super();
         setValue(other.value());
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicFlowRule.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicFlowRule.java
new file mode 100644
index 0000000..57ade4e
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicFlowRule.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.server.devices.nic;
+
+import org.onosproject.net.flow.FlowRule;
+
+import org.onlab.packet.EthType;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.Ip4Prefix;
+
+import java.util.Set;
+
+/**
+ * Definition of a network interface card (NIC) flow rule.
+ */
+public interface NicFlowRule extends FlowRule {
+
+    /**
+     * Returns parent FlowRule object associated with this NIC rule.
+     *
+     * @return FlowRule object
+     */
+    FlowRule flowRule();
+
+    /**
+     * Returns the traffic class ID associated with this NIC rule.
+     * This ID is related to the software module that processes the
+     * traffic matched by this rule.
+     *
+     * @return traffic class ID
+     */
+    String trafficClassId();
+
+    /**
+     * Returns the NIC's interface name to accommodate this rule.
+     *
+     * @return NIC's interface name
+     */
+    String interfaceName();
+
+    /**
+     * Returns the NIC's interface number to accommodate this rule.
+     *
+     * @return NIC's interface number
+     */
+    long interfaceNumber();
+
+    /**
+     * Returns the CPU core index that handles the traffic matched
+     * by this NIC rule.
+     *
+     * @return CPU core index
+     */
+    long cpuCoreIndex();
+
+    /**
+     * Returns the scope of this rule.
+     *
+     * @return rule scope
+     */
+    NicRuleScope scope();
+
+    /**
+     * Returns the Ethernet type field of this rule
+     * or NULL if no Ethernet type exists in the traffic selector.
+     *
+     * @return Ethernet type field
+     */
+    EthType ethernetType();
+
+    /**
+     * Returns the Ethernet type value field of this rule
+     * or negative if no Ethernet type value exists in the traffic selector.
+     *
+     * @return Ethernet type value field
+     */
+    short ethernetTypeValue();
+
+    /**
+     * Returns the source MAC address field of this rule
+     * or NULL if no source MAC address exists in the traffic selector.
+     *
+     * @return source MAC address field
+     */
+    MacAddress ethernetSrcAddress();
+
+    /**
+     * Returns the destination MAC address field of this rule
+     * or NULL if no destination MAC address exists in the traffic selector.
+     *
+     * @return destination MAC address field
+     */
+    MacAddress ethernetDstAddress();
+
+    /**
+     * Returns whether Ethernet fields are present in this rule.
+     *
+     * @return boolean Ethernet fields' presence
+     */
+    boolean hasEthernet();
+
+    /**
+     * Returns the IP protocol field of this rule
+     * or negative if no IP protocol field exists in the traffic selector.
+     *
+     * @return IP protocol field
+     */
+    short ipv4Protocol();
+
+    /**
+     * Returns the source IP address field of this rule
+     * or null if no source IP address field exists in the traffic selector.
+     *
+     * @return source IP address field
+     */
+    Ip4Prefix ipv4SrcAddress();
+
+    /**
+     * Returns the source IP mask field of this rule
+     * or null if no source IP mask exists in the traffic selector.
+     *
+     * @return source IP mask field
+     */
+    Ip4Prefix ipv4SrcMask();
+
+    /**
+     * Returns the destination IP address field of this rule
+     * or null if no destination IP address field exists in the traffic selector.
+     *
+     * @return destination IP address field
+     */
+    Ip4Prefix ipv4DstAddress();
+
+    /**
+     * Returns the destination IP mask field of this rule
+     * or null if no destination IP mask exists in the traffic selector.
+     *
+     * @return destination IP mask field
+     */
+    Ip4Prefix ipv4DstMask();
+
+    /**
+     * Returns whether IPv4 fields are present in this rule.
+     *
+     * @return boolean IPv4 fields' presence
+     */
+    boolean hasIpv4();
+
+    /**
+     * Returns whether IPv6 fields are present in this rule.
+     *
+     * @return boolean IPv6 fields' presence
+     */
+    boolean hasIpv6();
+
+    /**
+     * Returns the source port field of this rule
+     * or negative if no source port field exists in the traffic selector.
+     *
+     * @return source port field
+     */
+    int sourcePort();
+
+    /**
+     * Returns the destination port field of this rule
+     * or negative if no destination port field exists in the traffic selector.
+     *
+     * @return destination port field
+     */
+    int destinationPort();
+
+    /**
+     * Returns whether UDP header fields are present in this rule.
+     *
+     * @return boolean UDP header fields' presence
+     */
+    boolean hasUdp();
+
+    /**
+     * Returns whether TCP header fields are present in this rule.
+     *
+     * @return boolean TCP header fields' presence
+     */
+    boolean hasTcp();
+
+    /**
+     * Returns whether transport header fields are present in this rule.
+     *
+     * @return boolean transport header fields' presence
+     */
+    boolean hasTransport();
+
+    /**
+     * Returns the set of actions of this rule.
+     *
+     * @return rule's set of actions
+     */
+    Set<NicRuleAction> actions();
+
+    /**
+     * Adds a new action to the set of actions of this rule.
+     *
+     * @param action rule action
+     */
+    void addAction(NicRuleAction action);
+
+    /**
+     * Returns the queue number of this rule's action (if any).
+     *
+     * @return rule action's queue number
+     */
+    long queueNumber();
+
+    /**
+     * Creates rule using NIC-specific rule format.
+     *
+     * @return rule creation command as a string
+     */
+    abstract String createRule();
+
+    /**
+     * Removes rule using NIC-specific rule format.
+     *
+     * @return rule removal command as a string
+     */
+    abstract String removeRule();
+
+    /**
+     * Forms a NIC rule body.
+     *
+     * @return rule body as a string
+     */
+    abstract String ruleBody();
+
+    /**
+     * A builder for NIC rules.
+     */
+    interface Builder {
+
+        /**
+         * Creates a NIC rule out of an ONOS FlowRule.
+         *
+         * @param flowRule an ONOS FlowRule object
+         * @return this
+         */
+        Builder fromFlowRule(FlowRule flowRule);
+
+        /**
+         * Creates a NIC rule with a given traffic class ID.
+         *
+         * @param trafficClassId a traffic class ID
+         * @return this
+         */
+        Builder withTrafficClassId(String trafficClassId);
+
+        /**
+         * Sets the interface name for this NIC rule.
+         *
+         * @param interfaceName an interface's name
+         * @return this
+         */
+        Builder withInterfaceName(String interfaceName);
+
+        /**
+         * Sets the interface number for this NIC rule.
+         *
+         * @param interfaceNumber an interface's number
+         * @return this
+         */
+        Builder withInterfaceNumber(long interfaceNumber);
+
+        /**
+         * Sets the CPU core index to accommodate the traffic
+         * matched by this NIC rule.
+         *
+         * @param cpuCoreIndex a CPU core index
+         * @return this
+         */
+        Builder assignedToCpuCore(long cpuCoreIndex);
+
+        /**
+         * Sets the scope for this NIC rule.
+         *
+         * @param scope an NIC rule scope
+         * @return this
+         */
+        Builder withScope(NicRuleScope scope);
+
+        /**
+         * Builds a NIC rule object.
+         *
+         * @return a NIC rule.
+         */
+        abstract NicFlowRule build();
+
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicRuleAction.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicRuleAction.java
new file mode 100644
index 0000000..3ec13c3
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicRuleAction.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.server.devices.nic;
+
+import java.util.Objects;
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+
+/**
+ * Definition of network interface card (NIC) rule action.
+ * This class acts as a translator from FlowRule objects
+ * to NicFlowRule objects and vice versa.
+ */
+public class NicRuleAction {
+
+    /**
+     * Set of possible NIC rule actions.
+     * Source: https://doc.dpdk.org/guides/prog_guide/rte_flow.html
+     */
+    public enum Action {
+        /**
+         * Leaves traffic up for additional processing
+         * by subsequent flow rules.
+         */
+        PASSTHRU("passthru"),
+        /**
+         * The packet must be redirected to a group on this NIC.
+         */
+        JUMP("jump"),
+        /**
+         * The packet must be marked with a specific value.
+         */
+        MARK("mark"),
+        /**
+         * The packet must be marked without a specific value.
+         */
+        FLAG("flag"),
+        /**
+         * The packet must be counted.
+         */
+        COUNT("count"),
+        /**
+         * The packet must be placed into a hardware queue.
+         * This field is then used by a NIC's Flow Director component.
+         */
+        QUEUE("queue"),
+        /**
+         * The packet must be placed into a hardware queue.
+         * This field is then used by a NIC's RSS component.
+         */
+        RSS("rss"),
+        /**
+         * The packet must be sent to the physical function of the NIC.
+         */
+        PF("pf"),
+        /**
+         * The packet must be sent to a virtual function of the NIC.
+         */
+        VF("vf"),
+        /**
+         * The packet must be sent to a physical port index of the NIC.
+         */
+        PHY_PORT("phy-port"),
+        /**
+         * The packet must be sent to a NIC port ID.
+         */
+        PORT_ID("port-id"),
+        /**
+         * The packet must undergo a stage of metering and policing.
+         */
+        METER("meter"),
+        /**
+         * The packet must undergo a security action.
+         */
+        SECURITY("security"),
+        /**
+         * Sets the MPLS TTL as defined by the OpenFlow Switch Specification.
+         */
+        OF_SET_MPLS_TTL("of_set_mpls_ttl"),
+        /**
+         * Decrements the MPLS TTL as defined by the OpenFlow Switch Specification.
+         */
+        OF_DEC_MPLS_TTL("of_dec_mpls_ttl"),
+        /**
+         * Sets the IP TTL as defined by the OpenFlow Switch Specification.
+         */
+        OF_SET_NW_TTL("of_set_nw_ttl"),
+        /**
+         * Decrements the IP TTL as defined by the OpenFlow Switch Specification.
+         */
+        OF_DEC_NW_TTL("of_dec_nw_ttl"),
+        /**
+         * Implements OFPAT_COPY_TTL_IN (“copy TTL “inwards” – from outermost to next-to-outermost”)
+         * as defined by the OpenFlow Switch Specification.
+         */
+        OF_COPY_TTL_IN("of_copy_ttl_in"),
+        /**
+         * Implements OFPAT_COPY_TTL_OUT (“copy TTL “outwards” – from next-to-outermost to outermost”)
+         * as defined by the OpenFlow Switch Specification.
+         */
+        OF_COPY_TTL_OUT("of_copy_ttl_out"),
+        /**
+         * Pops the outer MPLS tag as defined by the OpenFlow Switch Specification.
+         */
+        OF_POP_MPLS("of_pop_mpls"),
+        /**
+         * Pushes a new MPLS tag as defined by the OpenFlow Switch Specification.
+         */
+        OF_PUSH_MPLS("of_push_mpls"),
+        /**
+         * Pops the outer VLAN tag as defined by the OpenFlow Switch Specification.
+         */
+        OF_POP_VLAN("of_pop_vlan"),
+        /**
+         * Pushes a new VLAN tag as defined by the OpenFlow Switch Specification.
+         */
+        OF_PUSH_VLAN("of_push_vlan"),
+        /**
+         * Sets the 802.1q VLAN ID as defined by the OpenFlow Switch Specification.
+         */
+        OF_SET_VLAN_VID("of_set_vlan_vid"),
+        /**
+         * Sets the 802.1q VLAN priority as defined by the OpenFlow Switch Specification.
+         */
+        OF_SET_VLAN_PCP("of_set_vlan_pcp"),
+        /**
+         * Performs a VXLAN encapsulation action by encapsulating the matched flow
+         * in an VXLAN tunnel.
+         */
+        VXLAN_ENCAP("vxlan_encap"),
+        /**
+         * Performs a decapsulation action by stripping all headers of the VXLAN tunnel
+         * network overlay from the matched flow.
+         */
+        VXLAN_DECAP("vxlan_decap"),
+        /**
+         * Performs a NVGRE encapsulation action by encapsulating the matched flow
+         * in an NVGRE tunnel.
+         */
+        NVGRE_ENCAP("nvgre_encap"),
+        /**
+         * Performs a decapsulation action by stripping all headers of the NVGRE tunnel
+         * network overlay from the matched flow.
+         */
+        NVGRE_DECAP("nvgre_decap"),
+        /**
+         * The packet must be dropped.
+         */
+        DROP("drop"),
+        /**
+         * Denotes end of actions.
+         */
+        END("end");
+
+        protected String action;
+
+        private Action(String action) {
+            this.action = action.toLowerCase();
+        }
+
+        @Override
+        public String toString() {
+            return action;
+        }
+
+    }
+
+    private final Action actionType;
+    private final long actionValue;
+
+    // Statically maps NIC rule action types to their fields
+    private static final Map<Action, String> ACTION_FIELD =
+        new HashMap<Action, String>();
+
+    static {
+        ACTION_FIELD.put(Action.PASSTHRU, "");
+        ACTION_FIELD.put(Action.JUMP, "group");
+        ACTION_FIELD.put(Action.MARK, "id");
+        ACTION_FIELD.put(Action.FLAG, "");
+        ACTION_FIELD.put(Action.COUNT, "id");
+        ACTION_FIELD.put(Action.QUEUE, "index");
+        ACTION_FIELD.put(Action.RSS, "queue");
+        ACTION_FIELD.put(Action.PF, "");
+        ACTION_FIELD.put(Action.VF, "id");
+        ACTION_FIELD.put(Action.PHY_PORT, "index");
+        ACTION_FIELD.put(Action.PORT_ID, "id");
+        ACTION_FIELD.put(Action.METER, "mtr_id");
+        ACTION_FIELD.put(Action.SECURITY, "security_session");
+        ACTION_FIELD.put(Action.OF_SET_MPLS_TTL, "mpls_ttl");
+        ACTION_FIELD.put(Action.OF_DEC_MPLS_TTL, "");
+        ACTION_FIELD.put(Action.OF_SET_NW_TTL, "nw_ttl");
+        ACTION_FIELD.put(Action.OF_DEC_NW_TTL, "");
+        ACTION_FIELD.put(Action.OF_COPY_TTL_IN, "");
+        ACTION_FIELD.put(Action.OF_COPY_TTL_OUT, "");
+        ACTION_FIELD.put(Action.OF_POP_MPLS, "ethertype");
+        ACTION_FIELD.put(Action.OF_PUSH_MPLS, "ethertype");
+        ACTION_FIELD.put(Action.OF_POP_VLAN, "");
+        ACTION_FIELD.put(Action.OF_PUSH_VLAN, "ethertype");
+        ACTION_FIELD.put(Action.OF_SET_VLAN_VID, "vlan_vid");
+        ACTION_FIELD.put(Action.OF_SET_VLAN_PCP, "vlan_pcp");
+        ACTION_FIELD.put(Action.VXLAN_ENCAP, "definition");
+        ACTION_FIELD.put(Action.VXLAN_DECAP, "");
+        ACTION_FIELD.put(Action.NVGRE_ENCAP, "definition");
+        ACTION_FIELD.put(Action.NVGRE_DECAP, "");
+        ACTION_FIELD.put(Action.DROP, "");
+        ACTION_FIELD.put(Action.END, "");
+    }
+
+    public static final long DEF_ACTION_VALUE = (long) 0;
+    public static final long NO_ACTION_VALUE  = (long) -1;
+
+    public NicRuleAction(Action actionType, long actionValue) {
+        checkNotNull(actionType, "NIC rule action type is NULL");
+        checkArgument(actionValue >= 0,
+            "NIC rule action " + actionType + " requires an action value");
+
+        this.actionType = actionType;
+        this.actionValue = actionValue;
+    }
+
+    public NicRuleAction(Action actionType) {
+        checkNotNull(actionType, "NIC rule action type is NULL");
+
+        this.actionType = actionType;
+        this.actionValue = NO_ACTION_VALUE;
+    }
+
+    /**
+     * Returns the NIC's action type associated with a rule.
+     *
+     * @return NIC's rule action type
+     */
+    public Action actionType() {
+        return actionType;
+    }
+
+    /**
+     * Returns the NIC's action value associated with a rule.
+     *
+     * @return NIC's rule action value
+     */
+    public long actionValue() {
+        return actionValue;
+    }
+
+    /**
+     * Returns the NIC's action field associated with its action type.
+     *
+     * @return NIC's rule action field
+     */
+    public String actionField() {
+        return ACTION_FIELD.get(actionType);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(actionType, actionValue);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof NicRuleAction) {
+            NicRuleAction that = (NicRuleAction) obj;
+            return  Objects.equals(actionType, that.actionType) &&
+                    Objects.equals(actionValue, that.actionValue);
+        }
+
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("NIC rule action type", actionType())
+                .add("NIC rule action value", actionValue())
+                .toString();
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicRuleScope.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicRuleScope.java
new file mode 100644
index 0000000..e9a89db
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/NicRuleScope.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.server.devices.nic;
+
+import com.google.common.base.Strings;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+
+/**
+* Definition of network interface card's (NIC) rule's scope.
+*/
+public enum NicRuleScope {
+    /**
+     * A NIC rules is applied to the ingress traffic.
+     */
+    INGRESS("ingress"),
+    /**
+     * A NIC rules is applied to the egress traffic.
+     */
+    EGRESS("egress");
+
+    protected String scope;
+
+    private NicRuleScope(String scope) {
+        checkArgument(!Strings.isNullOrEmpty(scope),
+            "NIC rule scope is NULL or empty");
+        this.scope = scope.toLowerCase();
+    }
+
+    @Override
+    public String toString() {
+        return scope;
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/RssRxFilterValue.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/RssRxFilterValue.java
new file mode 100644
index 0000000..08f2a46
--- /dev/null
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/RssRxFilterValue.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.drivers.server.devices.nic;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * A Receice-Side Scaling (RSS)-based Rx filter value.
+ */
+public final class RssRxFilterValue extends RxFilterValue implements Comparable {
+
+    private int rssHash;
+
+    /**
+     * Constructs an RSS-based Rx filter.
+     */
+    public RssRxFilterValue() {
+        super();
+        setValue(0);
+    }
+
+    /**
+     * Constructs an RSS-based Rx filter with specific hash.
+     *
+     * @param rssHash a hash value
+     */
+    public RssRxFilterValue(int rssHash) {
+        super();
+        setValue(rssHash);
+    }
+
+    /**
+     * Constructs an RSS-based Rx filter out of an existing one.
+     *
+     * @param other a source RssRxFilterValue object
+     */
+    public RssRxFilterValue(RssRxFilterValue other) {
+        super();
+        setValue(other.value());
+    }
+
+    /**
+     * Returns the value of this Rx filter.
+     *
+     * @return Flow rule as a string
+     */
+    public int value() {
+        return this.rssHash;
+    }
+
+    /**
+     * Sets the value of this Rx filter.
+     *
+     * @param rssHash the RSS hash
+     */
+    public void setValue(int rssHash) {
+        checkArgument(rssHash >= 0, "Invalid RSS Rx filter " + rssHash);
+        this.rssHash = rssHash;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(this.rssHash);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if ((obj == null) || (!(obj instanceof RssRxFilterValue))) {
+            return false;
+        }
+
+        RssRxFilterValue other = (RssRxFilterValue) obj;
+
+        return this.value() == other.value();
+    }
+
+    @Override
+    public int compareTo(Object other) {
+        if (this == other) {
+            return 0;
+        }
+
+        if (other == null) {
+            return -1;
+        }
+
+        if (other instanceof RssRxFilterValue) {
+            RssRxFilterValue otherRxVal = (RssRxFilterValue) other;
+
+            int thisHash  = this.value();
+            int otherHash = otherRxVal.value();
+
+            if (thisHash > otherHash) {
+                return 1;
+            } else if (thisHash < otherHash) {
+                return -1;
+            } else {
+                return 0;
+            }
+        }
+
+        return -1;
+    }
+
+    @Override
+    public String toString() {
+        return Integer.toString(this.value());
+    }
+
+}
diff --git a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/VlanRxFilterValue.java b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/VlanRxFilterValue.java
index 86cec25..18a99be 100644
--- a/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/VlanRxFilterValue.java
+++ b/drivers/server/src/main/java/org/onosproject/drivers/server/devices/nic/VlanRxFilterValue.java
@@ -28,16 +28,29 @@
 
     private VlanId vlanId;
 
+    /**
+     * Constructs a VLAN-based Rx filter.
+     */
     public VlanRxFilterValue() {
         super();
         this.vlanId = VlanId.NONE;
     }
 
+    /**
+     * Constructs a VLAN-based Rx filter with specific ID.
+     *
+     * @param vlanId a VLAN ID to use as a filter
+     */
     public VlanRxFilterValue(VlanId vlanId) {
         super();
         setValue(vlanId);
     }
 
+    /**
+     * Constructs a VLAN-based Rx filter out of an existing one.
+     *
+     * @param other a source VlanRxFilterValue object
+     */
     public VlanRxFilterValue(VlanRxFilterValue other) {
         super();
         setValue(other.value());
@@ -59,7 +72,6 @@
      */
     public void setValue(VlanId vlanId) {
         checkNotNull(vlanId, "VLAN ID of Rx filter is NULL");
-
         this.vlanId = vlanId;
     }
 
diff --git a/drivers/server/src/main/resources/server-drivers.xml b/drivers/server/src/main/resources/server-drivers.xml
index 3c97a9d..9058fa1 100644
--- a/drivers/server/src/main/resources/server-drivers.xml
+++ b/drivers/server/src/main/resources/server-drivers.xml
@@ -33,6 +33,9 @@
 
         <behaviour api="org.onosproject.drivers.server.behavior.MonitoringStatisticsDiscovery"
                    impl="org.onosproject.drivers.server.ServerDevicesDiscovery"/>
+
+        <behaviour api="org.onosproject.net.flow.FlowRuleProgrammable"
+                   impl="org.onosproject.drivers.server.FlowRuleProgrammableServerImpl"/>
     </driver>
 </drivers>