diff --git a/drivers/net/softnic/rte_eth_softnic_fc.c b/drivers/net/softnic/rte_eth_softnic_fc.c
index 83f7fd3..e41eb57 100644
--- a/drivers/net/softnic/rte_eth_softnic_fc.c
+++ b/drivers/net/softnic/rte_eth_softnic_fc.c
@@ -330,6 +330,9 @@ fc_flow_uninit(struct pmd_internals *p)
 			break;
 
 		TAILQ_REMOVE(&p->soft.fc.flow_list, flow, node);
+
+		free(flow->pattern);
+		free(flow->actions);
 		free(flow);
 	}
 }
@@ -369,9 +372,309 @@ fc_free(struct pmd_internals *p)
 	rte_free(p->soft.fc.pkts);
 }
 
+static struct rte_flow *
+fc_flow_search(struct pmd_internals *p,
+	const struct rte_flow_attr *attr,
+	const struct rte_flow_item pattern[],
+	const struct rte_flow_action actions[])
+{
+	struct fc_flow_list *fl = &p->soft.fc.flow_list;
+	struct rte_flow *f;
+	uint32_t n_item, n_act, n_match_params;
+
+	TAILQ_FOREACH(f, fl, node) {
+		n_item = 0;
+		n_act = 0;
+		n_match_params = 0;
+
+		/* Check: Flow attr */
+		if (memcmp(&f->attr,
+			(const void *)attr, sizeof(*attr)))
+			continue;
+		else
+			n_match_params += 1;
+
+		/* Check: Flow pattern */
+		while (pattern[n_item].type != RTE_FLOW_ITEM_TYPE_END) {
+			if (pattern[n_item].type != f->pattern[n_item].type)
+				n_match_params = 0;
+
+			n_item++;
+		}
+
+		if (n_match_params)
+			n_match_params += 1;
+		else
+			continue;
+
+		/* Check: Flow action */
+		while (actions[n_act].type != RTE_FLOW_ACTION_TYPE_END) {
+			if (actions[n_act].type != f->actions[n_act].type)
+				n_match_params = 0;
+
+			n_act++;
+		}
+
+		if (n_match_params)
+			n_match_params += 1;
+		else
+			continue;
+
+		if (n_match_params == 3)
+			return f;
+	}
+	return NULL;
+}
+
+static int rule_add_check(struct pmd_internals *p,
+	const struct rte_flow_attr *attr,
+	const struct rte_flow_item pattern[],
+	const struct rte_flow_action actions[],
+	struct rte_flow_error *error)
+{
+	struct rte_flow *flow;
+	uint32_t n_flow_rules;
+
+	/** Check: Number of rules */
+	n_flow_rules = p->soft.fc.n_flow_rules;
+	if (n_flow_rules >= p->params.soft.fc.nb_rules) {
+		rte_flow_error_set(error,
+		EINVAL,
+		RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+		NULL,
+		"Number of Flow rule exceeds.");
+		return -EINVAL;
+	}
+
+	flow = fc_flow_search(p, attr, pattern, actions);
+	if (flow != NULL) {
+		rte_flow_error_set(error,
+		EEXIST,
+		RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+		NULL,
+		"Flow rule exists.");
+		return -EEXIST;
+	}
+
+	return 0;
+}
+
+/* Flow rule validate */
+static int
+pmd_flow_validate(__rte_unused struct rte_eth_dev *dev,
+	const struct rte_flow_attr *attr,
+	const struct rte_flow_item pattern[],
+	const struct rte_flow_action actions[],
+	struct rte_flow_error *error)
+{
+	if (!pattern) {
+		rte_flow_error_set(error,
+			EINVAL,
+			RTE_FLOW_ERROR_TYPE_ITEM_NUM,
+			NULL,
+			"NULL pattern.");
+		return -rte_errno;
+	}
+
+	if (!actions) {
+		rte_flow_error_set(error,
+			EINVAL,
+			RTE_FLOW_ERROR_TYPE_ACTION_NUM,
+			NULL,
+			"NULL action.");
+		return -rte_errno;
+	}
+
+	if (!attr) {
+		rte_flow_error_set(error,
+			EINVAL,
+			RTE_FLOW_ERROR_TYPE_ATTR,
+			NULL,
+			"NULL attribute.");
+		return -rte_errno;
+	}
+
+	/* Add validate function */
+
+	return 0;
+}
+
+/* Create flow rule */
+static struct rte_flow *
+pmd_flow_create(struct rte_eth_dev *dev,
+	const struct rte_flow_attr *attr,
+	const struct rte_flow_item pattern[],
+	const struct rte_flow_action actions[],
+	struct rte_flow_error *error)
+{
+	struct pmd_internals *p = dev->data->dev_private;
+	struct fc_flow_list *fl = &p->soft.fc.flow_list;
+	struct rte_flow *f;
+	uint32_t n_item = 0, n_act = 0;
+	int status;
+
+	/** Check: Attributes */
+	if (!attr) {
+		rte_flow_error_set(error,
+			EINVAL,
+			RTE_FLOW_ERROR_TYPE_ATTR,
+			NULL,
+			"NULL attribute.");
+		return NULL;
+	}
+
+	/** Check: Pattern */
+	if (!pattern) {
+		rte_flow_error_set(error,
+			EINVAL,
+			RTE_FLOW_ERROR_TYPE_ITEM_NUM,
+			NULL,
+			"NULL pattern.");
+		return NULL;
+	}
+
+	/** Check: Actions */
+	if (!actions) {
+		rte_flow_error_set(error,
+			EINVAL,
+			RTE_FLOW_ERROR_TYPE_ACTION_NUM,
+			NULL,
+			"NULL action.");
+		return NULL;
+	}
+
+	/** Check: Rule */
+	status = rule_add_check(p, attr, pattern, actions, error);
+	if (status)
+		return NULL;
+
+	/** Add rule to the table */
+
+	/* Memory allocation */
+	f = calloc(1, sizeof(struct rte_flow));
+	if (f == NULL) {
+		rte_flow_error_set(error,
+		ENOMEM,
+		RTE_TM_ERROR_TYPE_UNSPECIFIED,
+		NULL,
+		"Failed to create flow.");
+		return NULL;
+	}
+
+	/** Add attr to list */
+	memcpy((void *)&f->attr, (const void *)attr,  sizeof(*attr));
+
+	/** Allocate for pattern */
+	while ((pattern + n_item)->type != RTE_FLOW_ITEM_TYPE_END)
+		n_item++;
+
+	n_item++;
+
+	f->pattern = calloc(n_item, sizeof(struct rte_flow_item));
+	if (f->pattern) {
+		rte_flow_error_set(error,
+		ENOMEM,
+		RTE_TM_ERROR_TYPE_UNSPECIFIED,
+		NULL,
+		"Failed to create flow.");
+		return NULL;
+	}
+
+	/** Add pattern */
+	memcpy((void *)f->pattern, (const void *)pattern,  sizeof(*f->pattern));
+
+	/** Allocate for action */
+	while ((actions + n_act)->type != RTE_FLOW_ACTION_TYPE_END)
+		n_act++;
+
+	n_act++;
+
+	f->actions = calloc(n_act, sizeof(struct rte_flow_action));
+	if (f->actions) {
+		rte_flow_error_set(error,
+		ENOMEM,
+		RTE_TM_ERROR_TYPE_UNSPECIFIED,
+		NULL,
+		"Failed to create flow.");
+		return NULL;
+	}
+
+	/** Add actions */
+	memcpy((void *)f->actions, (const void *)actions,  sizeof(*f->actions));
+
+	TAILQ_INSERT_TAIL(fl, f, node);
+	p->soft.fc.n_flow_rules++;
+
+	return f;
+}
+
+/* Destroy flow rule */
+static int
+pmd_flow_destroy(struct rte_eth_dev *dev,
+	struct rte_flow *flow,
+	struct rte_flow_error *error)
+{
+	struct pmd_internals *p = dev->data->dev_private;
+	struct fc_flow_list *fl = &p->soft.fc.flow_list;
+	struct rte_flow *f;
+
+	/** Check: flow */
+	if (!flow) {
+		rte_flow_error_set(error,
+			EINVAL,
+			RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+			NULL,
+			"NULL flow.");
+		return -EINVAL;
+	}
+	/* Check existing */
+	f = fc_flow_search(p, &flow->attr, flow->pattern, flow->actions);
+	if (f == NULL) {
+		rte_flow_error_set(error,
+			EINVAL,
+			RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
+			NULL,
+			"Flow not exist");
+			return -EINVAL;
+	}
+
+	/** Remove from Classifier table */
+
+	/** Remove from list */
+	TAILQ_REMOVE(fl, f, node);
+	p->soft.fc.n_flow_rules--;
+	free(f);
+
+	return 0;
+}
+
+/* Destroy flow rules */
+static int
+pmd_flow_flush(struct rte_eth_dev *dev,
+	__rte_unused struct rte_flow_error *error)
+{
+	struct pmd_internals *p = dev->data->dev_private;
+
+	for ( ; ; ) {
+		struct rte_flow *f;
+
+		f = TAILQ_FIRST(&p->soft.fc.flow_list);
+		if (f == NULL)
+			break;
+
+		/** Remove from Classifier table */
+
+		/** Remove from list */
+		TAILQ_REMOVE(&p->soft.fc.flow_list, f, node);
+		free(f);
+	}
+
+	return 0;
+}
+
 const struct rte_flow_ops pmd_flow_ops = {
-	.validate = NULL,
-	.create = NULL,
-	.destroy = NULL,
-	.flush = NULL,
+	.validate = pmd_flow_validate,
+	.create = pmd_flow_create,
+	.destroy = pmd_flow_destroy,
+	.flush = pmd_flow_flush,
 };
