diff --git a/src/core/cntlr_tlv_generator.c b/src/core/cntlr_tlv_generator.c
index 72df47432ec3b8a17520079176ef1468a0ac1cd2..59496837c965f7ff5b57dd3268eb1a67c874987c 100644
--- a/src/core/cntlr_tlv_generator.c
+++ b/src/core/cntlr_tlv_generator.c
@@ -480,3 +480,42 @@ struct tlv_backhaul_steer_req *cntlr_gen_backhaul_steer_req(struct controller *c
 	p->op_class = op_class;
 	return p;
 }
+
+struct tlv_client_assoc_control_req *cntlr_gen_assoc_control_req(
+		uint8_t *bss_id, uint32_t assoc_cntl_mode, uint32_t assoc_timeout,
+		uint32_t sta_nr, uint8_t sta_id[][18])
+{
+	struct tlv_client_assoc_control_req *p = NULL;
+	int j = 0;
+
+	p = calloc(1, sizeof(struct tlv_client_assoc_control_req));
+	if (!p) {
+		fprintf(stderr, "failed to malloc cmdu\n");
+		return NULL;
+	}
+
+	p->tlv_type = MAP_TLV_CLIENT_ASSOCIATION_CONTROL_REQUEST;
+	hwaddr_aton(bss_id, p->bssid);
+	p->validity_period = assoc_timeout;
+	p->assoc_control = assoc_cntl_mode;
+	p->sta_list_cnt = sta_nr;
+
+	if (p->sta_list_cnt > 0) {
+		p->client_assoc_ctrl_req_stas = calloc(p->sta_list_cnt,
+						sizeof(*p->client_assoc_ctrl_req_stas));
+		if (!p->client_assoc_ctrl_req_stas) {
+			fprintf(stderr, "|%s:%d| out of memory!\n",
+				__func__, __LINE__);
+			p->sta_list_cnt = 0;
+			goto fail_p;
+		}
+	}
+	for (j = 0; j < p->sta_list_cnt; j++)
+		hwaddr_aton(sta_id[j], p->client_assoc_ctrl_req_stas[j].addr);
+
+	return p;
+fail_p:
+	if (p != NULL)
+		map_free_tlv_cstruct((uint8_t *) p);
+	return NULL;
+}
diff --git a/src/core/cntlr_tlv_generator.h b/src/core/cntlr_tlv_generator.h
index 74adb4f98a713331f0cf2db65dd27d67d43c6dad..4296df10beec01f5a0dc25b2c3c506e83844a09a 100644
--- a/src/core/cntlr_tlv_generator.h
+++ b/src/core/cntlr_tlv_generator.h
@@ -48,4 +48,7 @@ struct tlv_client_info *cntlr_gen_client_info(struct controller *c,
 struct tlv_backhaul_steer_req *cntlr_gen_backhaul_steer_req(struct controller *c,
 		uint8_t *bssid, uint8_t *bkhaul, uint8_t op_class,
 		uint8_t channel);
+struct tlv_client_assoc_control_req *cntlr_gen_assoc_control_req(
+		uint8_t *bss_id, uint32_t assoc_cntl_mode, uint32_t assoc_timeout,
+		uint32_t sta_nr, uint8_t sta_id[][18]);
 #endif
diff --git a/src/core/cntlr_ubus.c b/src/core/cntlr_ubus.c
index bc383006588ca63e19c75edcf08d08fe2b85dc69..5a70e03fee3223628dc15c03b013f199ccaa7c89 100644
--- a/src/core/cntlr_ubus.c
+++ b/src/core/cntlr_ubus.c
@@ -149,6 +149,23 @@ static const struct blobmsg_policy ap_policy_config_params[__AP_POLICY_CONFIG_MA
 	[AP_POLICY_CONFIG_BSS] = { .name = "bsslist", .type = BLOBMSG_TYPE_ARRAY },
 };
 
+enum {
+	CLIENT_POLICY_ASSOC_CONTROL_AGENT,
+	CLIENT_POLICY_ASSOC_CONTROL_BSSID,
+	CLIENT_POLICY_ASSOC_CONTROL_MODE,
+	CLIENT_POLICY_ASSOC_CONTROL_VALID_TIME,
+	CLIENT_POLICY_ASSOC_CONTROL_STALIST,
+	__CLIENT_POLICY_ASSOC_CONTROL_MAX,
+};
+
+static const struct blobmsg_policy client_assoc_cntrl_policy_config_params[__CLIENT_POLICY_ASSOC_CONTROL_MAX] = {
+	[CLIENT_POLICY_ASSOC_CONTROL_AGENT] = { .name = "agent", .type = BLOBMSG_TYPE_STRING },
+	[CLIENT_POLICY_ASSOC_CONTROL_BSSID] = { .name = "bssid", .type = BLOBMSG_TYPE_STRING },
+	[CLIENT_POLICY_ASSOC_CONTROL_MODE] = { .name = "assoc_cntl_mode", .type = BLOBMSG_TYPE_INT32 },
+	[CLIENT_POLICY_ASSOC_CONTROL_VALID_TIME] = { .name = "assoc_valid_timeout", .type = BLOBMSG_TYPE_INT32 },
+	[CLIENT_POLICY_ASSOC_CONTROL_STALIST] = { .name = "stalist", .type = BLOBMSG_TYPE_ARRAY },
+};
+
 void send_cmdu_cb(struct ubus_request *req,
 				int type, struct blob_attr *msg)
 {
@@ -576,6 +593,108 @@ fail_cmdu:
 	return UBUS_STATUS_UNKNOWN_ERROR;
 }
 
+static int client_assoc_cntlr(struct ubus_context *ctx, struct ubus_object *obj,
+		struct ubus_request_data *req, const char *method,
+		struct blob_attr *msg)
+{
+	struct blob_attr *tb[__CLIENT_POLICY_ASSOC_CONTROL_MAX];
+	struct controller *c = container_of(obj, struct controller, obj);
+	char agent[18] = {0};
+	char bss_id[18] = {0};
+	struct cmdu_cstruct *cmdu_data;
+	uint32_t assoc_cntl_mode = 0, assoc_timeout = 0, sta_nr = 0;
+	uint32_t bssid_present = -1, sta_present = -1;
+	uint32_t  assoc_timeout_present = -1, tlv_index = 0;
+	char sta_id[30][18] = {0};
+	struct tlv_client_assoc_control_req *p = NULL;
+	uint32_t count = 0, j = 0, l = 0;
+	struct blob_attr *cur;
+	int rem = 0;
+
+	cmdu_data = (struct cmdu_cstruct *)calloc(1,
+			sizeof(struct cmdu_cstruct));
+	if (!cmdu_data) {
+		fprintf(stderr, "failed to malloc cmdu\n");
+		return UBUS_STATUS_UNKNOWN_ERROR;
+	}
+
+	blobmsg_parse(client_assoc_cntrl_policy_config_params,
+			__CLIENT_POLICY_ASSOC_CONTROL_MAX, tb,
+	blob_data(msg), blob_len(msg));
+
+	if (tb[CLIENT_POLICY_ASSOC_CONTROL_AGENT]) {
+		strncpy(agent, blobmsg_data(tb[CLIENT_POLICY_ASSOC_CONTROL_AGENT]),
+			sizeof(agent) - 1);
+		if (!hwaddr_aton(agent, cmdu_data->origin))
+			return UBUS_STATUS_UNKNOWN_ERROR;
+	}
+
+	// TODO: ff:ff:ff:ff:ff:ff = send to all agents
+
+	if (tb[CLIENT_POLICY_ASSOC_CONTROL_BSSID]) {
+		strncpy(bss_id, blobmsg_data(tb[CLIENT_POLICY_ASSOC_CONTROL_BSSID]),
+			sizeof(bss_id) - 1);
+		bssid_present = 1;
+	}
+
+	if (tb[CLIENT_POLICY_ASSOC_CONTROL_MODE])
+		assoc_cntl_mode = (int) blobmsg_get_u32(
+				tb[CLIENT_POLICY_ASSOC_CONTROL_MODE]);
+
+	if (tb[CLIENT_POLICY_ASSOC_CONTROL_VALID_TIME]) {
+		assoc_timeout_present = 1;
+		assoc_timeout = (int) blobmsg_get_u32(
+				tb[CLIENT_POLICY_ASSOC_CONTROL_VALID_TIME]);
+	}
+
+	if (tb[CLIENT_POLICY_ASSOC_CONTROL_STALIST]) {
+		sta_present = 1;
+		sta_nr = blobmsg_check_array(tb[CLIENT_POLICY_ASSOC_CONTROL_STALIST],
+				BLOBMSG_TYPE_STRING);
+		l = 0;
+		blobmsg_for_each_attr(cur, tb[CLIENT_POLICY_ASSOC_CONTROL_STALIST], rem)
+			strncpy(sta_id[l++], blobmsg_get_string(cur), 18);
+	}
+
+	cmdu_data->message_type = CMDU_CLIENT_ASSOC_CONTROL_REQUEST;
+
+	if (bssid_present == 1 && sta_present == 1 && assoc_timeout_present == 1
+		&& assoc_cntl_mode >= 0) {
+
+		trace("values  are assoc_mode %d assoc_timeout %d sta_cnt %d\n",
+			assoc_cntl_mode, assoc_timeout, sta_nr);
+
+		/* Client ASSOC CONTROL TLV REQUEST 17.2.31 */
+		p = cntlr_gen_assoc_control_req(bss_id, assoc_cntl_mode,
+			assoc_timeout, sta_nr, sta_id);
+		if (!p)
+			goto fail_cmdu;
+		count++;
+	}
+
+	cmdu_data->num_tlvs = count;
+
+	/*This CMDU has one or more TLVs*/
+	if (cmdu_data->num_tlvs == 0) {
+		fprintf(stderr, "|%s:%d| No TLvs\n", __func__, __LINE__);
+		goto fail_cmdu;
+	}
+
+	cmdu_data->tlvs = (uint8_t **)calloc(cmdu_data->num_tlvs,
+				sizeof(uint8_t *));
+	if (cmdu_data->tlvs && p != NULL)
+		cmdu_data->tlvs[tlv_index++] = (uint8_t *)p;
+	send_cmdu(c, cmdu_data);
+	map_free_cmdu(cmdu_data);
+	return 0;
+fail_p:
+	if (p != NULL)
+		map_free_tlv_cstruct((uint8_t *) p);
+fail_cmdu:
+	map_free_cmdu(cmdu_data);
+	return UBUS_STATUS_UNKNOWN_ERROR;
+}
+
 static int cntlr_sta_caps(struct ubus_context *ctx, struct ubus_object *obj,
 		struct ubus_request_data *req, const char *method,
 		struct blob_attr *msg)
@@ -1106,7 +1225,7 @@ int cntlr_publish_object(struct controller *c, const char *objname)
 	struct ubus_object *obj;
 	struct ubus_object_type *obj_type;
 	struct ubus_method *obj_methods;
-	struct ubus_method m[8] = {
+	struct ubus_method m[9] = {
 		UBUS_METHOD_NOARG("status", cntlr_status),
 		UBUS_METHOD("ap_caps", cntlr_ap_caps,
 				ap_caps_policy_params),
@@ -1121,6 +1240,7 @@ int cntlr_publish_object(struct controller *c, const char *objname)
 				channel_select_policy_params),
 		UBUS_METHOD("reconfig_ap", cntlr_reconfig_ap,
 				reconfig_policy_params),
+		UBUS_METHOD("client_assoc_cntlr", client_assoc_cntlr, client_assoc_cntrl_policy_config_params),
 		/*
 		UBUS_METHOD("teardown_ap", cntlr_teardown_ap,
 				config_policy_params),