return 0;
 }
 
+#define ETHER_TYPE_FULL_MASK ((__force __be16)~0)
+#define VLAN_PRIO_FULL_MASK (0x07)
+
+static int igb_parse_cls_flower(struct igb_adapter *adapter,
+                               struct tc_cls_flower_offload *f,
+                               int traffic_class,
+                               struct igb_nfc_filter *input)
+{
+       struct netlink_ext_ack *extack = f->common.extack;
+
+       if (f->dissector->used_keys &
+           ~(BIT(FLOW_DISSECTOR_KEY_BASIC) |
+             BIT(FLOW_DISSECTOR_KEY_CONTROL) |
+             BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
+             BIT(FLOW_DISSECTOR_KEY_VLAN))) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Unsupported key used, only BASIC, CONTROL, ETH_ADDRS and VLAN are supported");
+               return -EOPNOTSUPP;
+       }
+
+       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+               struct flow_dissector_key_eth_addrs *key, *mask;
+
+               key = skb_flow_dissector_target(f->dissector,
+                                               FLOW_DISSECTOR_KEY_ETH_ADDRS,
+                                               f->key);
+               mask = skb_flow_dissector_target(f->dissector,
+                                                FLOW_DISSECTOR_KEY_ETH_ADDRS,
+                                                f->mask);
+
+               if (!is_zero_ether_addr(mask->dst)) {
+                       if (!is_broadcast_ether_addr(mask->dst)) {
+                               NL_SET_ERR_MSG_MOD(extack, "Only full masks are supported for destination MAC address");
+                               return -EINVAL;
+                       }
+
+                       input->filter.match_flags |=
+                               IGB_FILTER_FLAG_DST_MAC_ADDR;
+                       ether_addr_copy(input->filter.dst_addr, key->dst);
+               }
+
+               if (!is_zero_ether_addr(mask->src)) {
+                       if (!is_broadcast_ether_addr(mask->src)) {
+                               NL_SET_ERR_MSG_MOD(extack, "Only full masks are supported for source MAC address");
+                               return -EINVAL;
+                       }
+
+                       input->filter.match_flags |=
+                               IGB_FILTER_FLAG_SRC_MAC_ADDR;
+                       ether_addr_copy(input->filter.src_addr, key->src);
+               }
+       }
+
+       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
+               struct flow_dissector_key_basic *key, *mask;
+
+               key = skb_flow_dissector_target(f->dissector,
+                                               FLOW_DISSECTOR_KEY_BASIC,
+                                               f->key);
+               mask = skb_flow_dissector_target(f->dissector,
+                                                FLOW_DISSECTOR_KEY_BASIC,
+                                                f->mask);
+
+               if (mask->n_proto) {
+                       if (mask->n_proto != ETHER_TYPE_FULL_MASK) {
+                               NL_SET_ERR_MSG_MOD(extack, "Only full mask is supported for EtherType filter");
+                               return -EINVAL;
+                       }
+
+                       input->filter.match_flags |= IGB_FILTER_FLAG_ETHER_TYPE;
+                       input->filter.etype = key->n_proto;
+               }
+       }
+
+       if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_VLAN)) {
+               struct flow_dissector_key_vlan *key, *mask;
+
+               key = skb_flow_dissector_target(f->dissector,
+                                               FLOW_DISSECTOR_KEY_VLAN,
+                                               f->key);
+               mask = skb_flow_dissector_target(f->dissector,
+                                                FLOW_DISSECTOR_KEY_VLAN,
+                                                f->mask);
+
+               if (mask->vlan_priority) {
+                       if (mask->vlan_priority != VLAN_PRIO_FULL_MASK) {
+                               NL_SET_ERR_MSG_MOD(extack, "Only full mask is supported for VLAN priority");
+                               return -EINVAL;
+                       }
+
+                       input->filter.match_flags |= IGB_FILTER_FLAG_VLAN_TCI;
+                       input->filter.vlan_tci = key->vlan_priority;
+               }
+       }
+
+       input->action = traffic_class;
+       input->cookie = f->cookie;
+
+       return 0;
+}
+
 static int igb_configure_clsflower(struct igb_adapter *adapter,
                                   struct tc_cls_flower_offload *cls_flower)
 {
-       return -EOPNOTSUPP;
+       struct netlink_ext_ack *extack = cls_flower->common.extack;
+       struct igb_nfc_filter *filter, *f;
+       int err, tc;
+
+       tc = tc_classid_to_hwtc(adapter->netdev, cls_flower->classid);
+       if (tc < 0) {
+               NL_SET_ERR_MSG_MOD(extack, "Invalid traffic class");
+               return -EINVAL;
+       }
+
+       filter = kzalloc(sizeof(*filter), GFP_KERNEL);
+       if (!filter)
+               return -ENOMEM;
+
+       err = igb_parse_cls_flower(adapter, cls_flower, tc, filter);
+       if (err < 0)
+               goto err_parse;
+
+       spin_lock(&adapter->nfc_lock);
+
+       hlist_for_each_entry(f, &adapter->nfc_filter_list, nfc_node) {
+               if (!memcmp(&f->filter, &filter->filter, sizeof(f->filter))) {
+                       err = -EEXIST;
+                       NL_SET_ERR_MSG_MOD(extack,
+                                          "This filter is already set in ethtool");
+                       goto err_locked;
+               }
+       }
+
+       hlist_for_each_entry(f, &adapter->cls_flower_list, nfc_node) {
+               if (!memcmp(&f->filter, &filter->filter, sizeof(f->filter))) {
+                       err = -EEXIST;
+                       NL_SET_ERR_MSG_MOD(extack,
+                                          "This filter is already set in cls_flower");
+                       goto err_locked;
+               }
+       }
+
+       err = igb_add_filter(adapter, filter);
+       if (err < 0) {
+               NL_SET_ERR_MSG_MOD(extack, "Could not add filter to the adapter");
+               goto err_locked;
+       }
+
+       hlist_add_head(&filter->nfc_node, &adapter->cls_flower_list);
+
+       spin_unlock(&adapter->nfc_lock);
+
+       return 0;
+
+err_locked:
+       spin_unlock(&adapter->nfc_lock);
+
+err_parse:
+       kfree(filter);
+
+       return err;
 }
 
 static int igb_delete_clsflower(struct igb_adapter *adapter,
                                struct tc_cls_flower_offload *cls_flower)
 {
-       return -EOPNOTSUPP;
+       struct igb_nfc_filter *filter;
+       int err;
+
+       spin_lock(&adapter->nfc_lock);
+
+       hlist_for_each_entry(filter, &adapter->cls_flower_list, nfc_node)
+               if (filter->cookie == cls_flower->cookie)
+                       break;
+
+       if (!filter) {
+               err = -ENOENT;
+               goto out;
+       }
+
+       err = igb_erase_filter(adapter, filter);
+       if (err < 0)
+               goto out;
+
+       hlist_del(&filter->nfc_node);
+       kfree(filter);
+
+out:
+       spin_unlock(&adapter->nfc_lock);
+
+       return err;
 }
 
 static int igb_setup_tc_cls_flower(struct igb_adapter *adapter,
        hlist_for_each_entry(rule, &adapter->nfc_filter_list, nfc_node)
                igb_erase_filter(adapter, rule);
 
+       hlist_for_each_entry(rule, &adapter->cls_flower_list, nfc_node)
+               igb_erase_filter(adapter, rule);
+
        spin_unlock(&adapter->nfc_lock);
 }