diff --git a/src/Makefile b/src/Makefile
index 656e03d702790df9d52ffcb35c17e39f41d2b418..69e8a0830451a8451fc456d84b698d7dc2cbe36c 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -7,6 +7,7 @@ OBJS = \
 	utils/liblist.o \
 	utils/utils.o \
 	utils/alloctrace.o \
+	utils/opclass.o \
 	allsta.o \
 	cntlr_ubus.o \
 	cntlr.o \
diff --git a/src/cntlr.c b/src/cntlr.c
index df09ce19cf1ce72da0d46d6feab300360c0ddec0..6f1a39ec7f2bb175ed574d76c3f0a549d3c77395 100644
--- a/src/cntlr.c
+++ b/src/cntlr.c
@@ -1134,135 +1134,68 @@ static enum wifi_freqband get_op_class_band(int opclass)
 	}
 }
 
-static struct opclass_entry *cntlr_radio_opclass_find_entry(struct opclass *opclass, uint8_t id)
-{
-	struct opclass_entry *entry;
-	int i;
-
-	for (i = 0; i < opclass->entry_num; i++) {
-		entry = &opclass->entry[i];
-		if (entry->id != id)
-			continue;
-
-		return entry;
-	}
-
-	return NULL;
-}
-
-static struct opclass_entry *cntlr_radio_opclass_new_entry(struct opclass *opclass)
-{
-	struct opclass_entry *entry;
-
-	if (opclass->entry_num >= ARRAY_SIZE(opclass->entry))
-		return NULL;
-
-	entry = &opclass->entry[opclass->entry_num];
-	opclass->entry_num++;
-	opclass->entry_time = time(NULL);
-
-	return entry;
-}
-
-static int cntlr_radio_opclass_add_channel(struct opclass_entry *entry, uint8_t channel, uint8_t preference)
-{
-	int i;
-
-	/* First check if we already have this */
-	for (i = 0; i < entry->channels_num; i++) {
-		if (entry->channels[i].channel == channel) {
-			entry->channels[i].preference = preference;
-			return 0;
-		}
-	}
-
-	/* Add new channel */
-	if (entry->channels_num >= ARRAY_SIZE(entry->channels))
-		return -1;
-
-	entry->channels[entry->channels_num].channel = channel;
-	entry->channels[entry->channels_num].preference = preference;
-	entry->channels_num++;
-
-	return 0;
-}
-
-static void _cntlr_radio_opclass_reset(struct opclass *opclass)
-{
-	opclass->entry_num = 0;
-	memset(opclass->entry, 0, sizeof(opclass->entry));
-}
-
-static void _cntlr_radio_opclass_dump(struct opclass *opclass)
-{
-	struct opclass_entry *entry;
-	int i, j;
-
-	dbg(">>> opclass num: %d\n", opclass->entry_num);
-	for (i = 0; i < opclass->entry_num; i++) {
-		entry = &opclass->entry[i];
-		dbg("opclass: %u\n", entry->id);
-		for (j = 0; j < entry->channels_num; j++) {
-			dbg("\tchan %u pref %u reason %u\n",
-			    entry->channels[j].channel,
-			    (entry->channels[j].preference & CHANNEL_PREF_MASK) >> 4,
-			    entry->channels[j].preference & CHANNEL_PREF_REASON);
-		}
-	}
-	dbg("<<<\n");
-}
 
 void cntlr_radio_opclass_reset(struct netif_radio *radio)
 {
-	_cntlr_radio_opclass_reset(&radio->opclass);
+	opclass_reset(&radio->opclass);
 }
 
 int cntlr_radio_opclass_add(struct netif_radio *radio, uint8_t id,
 			    uint8_t channel, uint8_t preference)
 {
+	struct opclass_channel chan = {};
 	struct opclass_entry *entry;
 
-	entry = cntlr_radio_opclass_find_entry(&radio->opclass, id);
+	entry = opclass_find_entry(&radio->opclass, id);
 	if (!entry)
-		entry = cntlr_radio_opclass_new_entry(&radio->opclass);
+		entry = opclass_new_entry(&radio->opclass);
 	if (!entry)
 		return -1;
 
 	entry->id = id;
 	entry->bandwidth = get_op_class_bw(id);
-	return cntlr_radio_opclass_add_channel(entry, channel, preference);
+
+	chan.channel = channel;
+	chan.preference = preference;
+
+	return opclass_add_channel(entry, &chan);
 }
 
 void cntlr_radio_opclass_dump(struct netif_radio *radio)
 {
-	_cntlr_radio_opclass_dump(&radio->opclass);
+	opclass_dump(&radio->opclass);
 }
 
 void cntlr_radio_cur_opclass_reset(struct netif_radio *radio)
 {
-	_cntlr_radio_opclass_reset(&radio->cur_opclass);
+	opclass_reset(&radio->cur_opclass);
 }
 
 int cntlr_radio_cur_opclass_add(struct netif_radio *radio, uint8_t id,
 				uint8_t channel, uint8_t txpower)
 {
+	struct opclass_channel chan = {};
 	struct opclass_entry *entry;
 
-	entry = cntlr_radio_opclass_find_entry(&radio->cur_opclass, id);
+	entry = opclass_find_entry(&radio->cur_opclass, id);
 	if (!entry)
-		entry = cntlr_radio_opclass_new_entry(&radio->cur_opclass);
+		entry = opclass_new_entry(&radio->cur_opclass);
 	if (!entry)
 		return -1;
 
 	entry->id = id;
 	entry->bandwidth = get_op_class_bw(id);
 	entry->max_txpower = txpower;
-	return cntlr_radio_opclass_add_channel(entry, channel, 15 << 4);
+
+	chan.channel = channel;
+	chan.preference = 15 << 4;
+
+	return opclass_add_channel(entry, &chan);
 }
 
 void cntlr_radio_cur_opclass_dump(struct netif_radio *radio)
 {
-	_cntlr_radio_opclass_dump(&radio->cur_opclass);
+	opclass_dump(&radio->cur_opclass);
 }
 
 static bool cntlr_radio_opclass_expired(struct netif_radio *radio)
diff --git a/src/cntlr.h b/src/cntlr.h
index 6807ecb4f7127684f9606b9cd3ead8f927016a41..8f4146d95a7d9b632b4137274e37671c3fd8ee4e 100644
--- a/src/cntlr.h
+++ b/src/cntlr.h
@@ -17,6 +17,7 @@
 #include <timer_impl.h>
 #include <cmdu_ackq.h>
 #include <map_module.h>
+#include "utils/opclass.h"
 
 extern const char *ubus_socket;
 
@@ -251,36 +252,6 @@ struct netif_link {
 	struct list_head list;
 };
 
-enum opclass_dfs {
-	OPCLASS_CHANNEL_DFS_NONE,
-	OPCLASS_CHANNEL_DFS_USABLE,
-	OPCLASS_CHANNEL_DFS_AVAILABLE,
-	OPCLASS_CHANNEL_DFS_NOP,
-	OPCLASS_CHANNEL_DFS_CAC,
-};
-
-struct opclass_entry {
-	uint8_t id;
-	int bandwidth;
-	int max_txpower;
-
-	int channels_num;
-	struct {
-		uint8_t channel;
-		uint8_t preference;
-
-		enum opclass_dfs dfs;
-		uint32_t cac_time;
-		uint32_t nop_time;
-	} channels[64];
-};
-
-struct opclass {
-	struct opclass_entry entry[64];
-	int entry_num;
-	time_t entry_time;
-};
-
 /* TODO - fill this structure */
 struct netif_radio {
 	char name[16];
diff --git a/src/cntlr_acs.c b/src/cntlr_acs.c
index 03edf335c397cf7361f31f5f54dd861ae3cb8499..8a7b8a55e55900f27aec2265cd85bb89ccf30a06 100644
--- a/src/cntlr_acs.c
+++ b/src/cntlr_acs.c
@@ -64,10 +64,10 @@ int cntlr_acs_radio_channel_recalc(struct netif_radio *radio, struct acs_params
 		if (params->bw && params->bw != entry->bandwidth)
 			continue;
 
-		for (j = 0; j < entry->channels_num; j++) {
-			chan = entry->channels[j].channel;
-			pref = (entry->channels[j].preference & CHANNEL_PREF_MASK) >> 4;
-			reas = entry->channels[j].preference & CHANNEL_PREF_REASON;
+		for (j = 0; j < entry->channel_num; j++) {
+			chan = entry->channel[j].channel;
+			pref = (entry->channel[j].preference & CHANNEL_PREF_MASK) >> 4;
+			reas = entry->channel[j].preference & CHANNEL_PREF_REASON;
 
 			trace("\tacs check/cmp chan %d pref %d reas %d\n", chan, pref, reas);
 
@@ -181,7 +181,7 @@ static int cntlr_get_current_acs_params(struct netif_radio *radio, struct acs_pa
 	params->opclass = radio->cur_opclass.entry[0].id;
 	params->bw = radio->cur_opclass.entry[0].bandwidth;
 
-	params->best_channel = radio->cur_opclass.entry[0].channels[0].channel;
+	params->best_channel = radio->cur_opclass.entry[0].channel[0].channel;
 	params->best_bw = params->bw;
 	params->best_opclass = params->opclass;
 
@@ -245,12 +245,12 @@ static bool cntlr_dfs_get_usable(struct opclass_entry *entry, struct cac_data *c
 	uint8_t reas;
 	int i;
 
-	for (i = 0; i < entry->channels_num; i++) {
-		reas = entry->channels[i].preference & CHANNEL_PREF_REASON;
+	for (i = 0; i < entry->channel_num; i++) {
+		reas = entry->channel[i].preference & CHANNEL_PREF_REASON;
 
 		/* Usable - we can run CAC */
 		if (reas == CHANNEL_PREF_REASON_DFS_USABLE) {
-			cac_data->channel = entry->channels[i].channel;
+			cac_data->channel = entry->channel[i].channel;
 			cac_data->opclass = entry->id;
 			return true;
 		}
@@ -296,7 +296,7 @@ static bool cntlr_dfs_get_cac_data(struct netif_radio *radio, struct cac_data *c
 			continue;
 
 		/* TODO check chan/bw - not only control channel */
-		if (cac_data->channel == cur_opclass->entry[0].channels[0].channel)
+		if (cac_data->channel == cur_opclass->entry[0].channel[0].channel)
 			continue;
 
 		/* TODO define this in ieee1905 */
diff --git a/src/cntlr_map.c b/src/cntlr_map.c
index f9b55a46ae2e193522134a13aac56380b47ee2ee..0f24e09c50ea25893c87952767354fb0d22905fb 100644
--- a/src/cntlr_map.c
+++ b/src/cntlr_map.c
@@ -75,18 +75,18 @@ static void _cntlr_update_steer_params(struct controller *c, struct opclass *op)
 
 	for (i = 0; i < op->entry_num; i++) {
 		oe = &op->entry[i];
-		for (j = 0; j < oe->channels_num; j++) {
+		for (j = 0; j < oe->channel_num; j++) {
 			sp = &c->steer_params;
 			found = false;
 			for (k = 0; k < sp->channels_num; k++) {
-				if(sp->channels[k] == oe->channels[j].channel) {
+				if(sp->channels[k] == oe->channel[j].channel) {
 					found = true;
 					break;
 				}
 			}
 
 			if (!found) {
-				sp->channels[sp->channels_num] = oe->channels[j].channel;
+				sp->channels[sp->channels_num] = oe->channel[j].channel;
 				sp->channels_num++;
 			}
 		}
@@ -1284,8 +1284,8 @@ static uint8_t cntlr_get_opclass_ht20(struct netif_radio *nr, uint8_t channel)
 		if (entry->bandwidth != 20)
 			continue;
 
-		for (j = 0; j < entry->channels_num; j++) {
-			if (entry->channels[j].channel == channel)
+		for (j = 0; j < entry->channel_num; j++) {
+			if (entry->channel[j].channel == channel)
 				return entry->id;
 		}
 	}
diff --git a/src/cntlr_ubus.c b/src/cntlr_ubus.c
index fdf87f01d50632b8da80c420634a1d72b6dc52e0..06fefc977edfb457e898802be2990a13b920d6cf 100644
--- a/src/cntlr_ubus.c
+++ b/src/cntlr_ubus.c
@@ -450,12 +450,12 @@ static void cntlr_status_add_opclass(struct blob_buf *bb, struct opclass *opclas
 		if (strstr(name, "cur"))
 			blobmsg_add_u32(bb, "txpower", opclass->entry[j].max_txpower);
 		aa = blobmsg_open_array(bb, "channels");
-		for (k = 0; k < opclass->entry[j].channels_num; k++) {
+		for (k = 0; k < opclass->entry[j].channel_num; k++) {
 			tt = blobmsg_open_table(bb, "");
-			blobmsg_add_u32(bb, "channel", opclass->entry[j].channels[k].channel);
+			blobmsg_add_u32(bb, "channel", opclass->entry[j].channel[k].channel);
 			if (!strstr(name, "cur")) {
-				pref = (opclass->entry[j].channels[k].preference & CHANNEL_PREF_MASK) >> 4;
-				reas = opclass->entry[j].channels[k].preference & CHANNEL_PREF_REASON;
+				pref = (opclass->entry[j].channel[k].preference & CHANNEL_PREF_MASK) >> 4;
+				reas = opclass->entry[j].channel[k].preference & CHANNEL_PREF_REASON;
 				blobmsg_add_u32(bb, "preference", pref);
 				blobmsg_add_string(bb, "reason", cntrl_status_reason(reas));
 			}
diff --git a/src/utils/opclass.c b/src/utils/opclass.c
new file mode 100644
index 0000000000000000000000000000000000000000..eaf4018be9408bd6c90401fa721da0066b4b7192
--- /dev/null
+++ b/src/utils/opclass.c
@@ -0,0 +1,131 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <easy/easy.h>
+#include <map2.h>
+#include "utils.h"
+#include "debug.h"
+#include "opclass.h"
+
+struct opclass_entry *opclass_find_entry(struct opclass *opclass, uint8_t id)
+{
+	struct opclass_entry *entry;
+	int i;
+
+	for (i = 0; i < opclass->entry_num; i++) {
+		entry = &opclass->entry[i];
+		if (entry->id != id)
+			continue;
+
+		return entry;
+	}
+
+	return NULL;
+}
+
+struct opclass_entry *opclass_new_entry(struct opclass *opclass)
+{
+	struct opclass_entry *entry;
+
+	if (opclass->entry_num >= ARRAY_SIZE(opclass->entry))
+		return NULL;
+
+	entry = &opclass->entry[opclass->entry_num];
+	opclass->entry_num++;
+	opclass->entry_time = time(NULL);
+
+	return entry;
+}
+
+struct opclass_channel *opclass_find_channel(struct opclass_entry *entry, uint8_t chan)
+{
+	struct opclass_channel *channel;
+	int i;
+
+	for (i = 0; i < entry->channel_num; i++) {
+		channel = &entry->channel[i];
+		if (channel->channel != chan)
+			continue;
+
+		return channel;
+	}
+
+	return NULL;
+}
+
+struct opclass_channel *opclass_new_channel(struct opclass_entry *entry)
+{
+	struct opclass_channel *channel;
+
+	if (entry->channel_num >= ARRAY_SIZE(entry->channel))
+		return NULL;
+
+	channel = &entry->channel[entry->channel_num];
+	entry->channel_num++;
+
+	return channel;
+}
+
+int opclass_add_channel(struct opclass_entry *entry, struct opclass_channel *new)
+{
+	struct opclass_channel *channel;
+
+	channel = opclass_find_channel(entry, new->channel);
+	if (!channel)
+		channel = opclass_new_channel(entry);
+	if (!channel)
+		return -1;
+
+	memcpy(channel, new, sizeof(*channel));
+	return 0;
+}
+
+int opclass_add_entry(struct opclass *opclass, struct opclass_entry *new)
+{
+	struct opclass_entry *entry;
+	int ret = -1;
+	int i;
+
+	entry = opclass_find_entry(opclass, new->id);
+	if (!entry)
+		entry = opclass_new_entry(opclass);
+	if (!entry)
+		return ret;
+
+	entry->id = new->id;
+	entry->bandwidth = new->bandwidth;
+	entry->max_txpower = new->max_txpower;
+
+	for (i = 0; i < new->channel_num; i++)
+		ret |= opclass_add_channel(entry, &new->channel[i]);
+
+	return ret;
+}
+
+void opclass_reset(struct opclass *opclass)
+{
+	opclass->entry_num = 0;
+	memset(opclass->entry, 0, sizeof(opclass->entry));
+}
+
+void opclass_dump(struct opclass *opclass)
+{
+	struct opclass_entry *entry;
+	int i, j;
+
+	dbg(">>> opclass num: %d\n", opclass->entry_num);
+	for (i = 0; i < opclass->entry_num; i++) {
+		entry = &opclass->entry[i];
+		dbg("opclass: %u\n", entry->id);
+		for (j = 0; j < entry->channel_num; j++) {
+			dbg("\tchan %u pref %u reason %u\n",
+			    entry->channel[j].channel,
+			    (entry->channel[j].preference & CHANNEL_PREF_MASK) >> 4,
+			    entry->channel[j].preference & CHANNEL_PREF_REASON);
+		}
+	}
+	dbg("<<<\n");
+}
+
diff --git a/src/utils/opclass.h b/src/utils/opclass.h
new file mode 100644
index 0000000000000000000000000000000000000000..88bb0c4360c0083dbbfe167fd1cea38a1f97bfe5
--- /dev/null
+++ b/src/utils/opclass.h
@@ -0,0 +1,49 @@
+#ifndef _OPCLASS_H_
+#define _OPCLASS_H_
+
+enum opclass_dfs {
+	OPCLASS_CHANNEL_DFS_NONE,
+	OPCLASS_CHANNEL_DFS_USABLE,
+	OPCLASS_CHANNEL_DFS_AVAILABLE,
+	OPCLASS_CHANNEL_DFS_NOP,
+	OPCLASS_CHANNEL_DFS_CAC,
+};
+
+struct opclass_channel {
+	uint8_t channel;
+	uint8_t preference;
+
+	enum opclass_dfs dfs;
+	uint32_t cac_time;
+	uint32_t nop_time;
+};
+
+struct opclass_entry {
+	uint8_t id;
+	int bandwidth;
+	int max_txpower;
+
+	int channel_num;
+	struct opclass_channel channel[64];
+};
+
+struct opclass {
+	time_t entry_time;
+
+	int entry_num;
+	struct opclass_entry entry[64];
+};
+
+struct opclass_entry *opclass_find_entry(struct opclass *opclass, uint8_t id);
+struct opclass_entry *opclass_new_entry(struct opclass *opclass);
+
+struct opclass_channel *opclass_find_channel(struct opclass_entry *entry, uint8_t chan);
+struct opclass_channel *opclass_new_channel(struct opclass_entry *entry);
+
+int opclass_add_channel(struct opclass_entry *entry, struct opclass_channel *new);
+int opclass_add_entry(struct opclass *opclass, struct opclass_entry *new);
+
+void opclass_reset(struct opclass *opclass);
+void opclass_dump(struct opclass *opclass);
+
+#endif /* _OPCLASS_H_ */