/*
 * Copyright (C) 2024 iopsys Software Solutions AB
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation
 *
 *		Author: Omar Kallel <omar.kallel@pivasoftware.com>
 *		Author: Amin Ben Romdhane <amin.benromdhane@iopsys.eu>
 */

#include "dhcpv4.h"
#include "dhcpv6.h"
#include "dhcpmngr.h"

/*************************************************************
* COMMON FUNCTIONS
**************************************************************/
static char *get_req_option_string(const char *value)
{
	int len = DM_STRLEN(value);
	char option[len + 1];
	int i = 0;

	memset (option, 0, len+1);

	for (i = 0; i < len; i++) {
		option[i] = (value[i] == ',') ? ' ' : value[i];
	}

	option[i] = '\0';

	return dmstrdup(option);
}

static struct uci_section *get_dhcpv6_classifier(const char *classifier_name, const char *network)
{
	struct uci_section *s = NULL;

	if (DM_STRLEN(network) == 0)
		return NULL;

	uci_foreach_sections("dhcp", classifier_name, s) {
		char *v = NULL;

		dmuci_get_value_by_section_string(s, "networkid", &v);
		if (DM_STRCMP(v, network) == 0)
			return s;
	}
	return NULL;
}

static int get_value_in_date_time_format(json_object *json_obj, const char *option_name, char **value)
{
	const char *option_value = dmjson_get_value(json_obj, 1, option_name);
	if (option_value && *option_value != '\0' && DM_STRTOL(option_value) > 0) {
		time_t time_value = DM_STRTOL(option_value);
		char s_now[sizeof "AAAA-MM-JJTHH:MM:SSZ"];
		if (strftime(s_now, sizeof s_now, "%Y-%m-%dT%H:%M:%SZ", gmtime(&time_value)) == 0)
			return -1;
		*value = dmstrdup(s_now); // MEM WILL BE FREED IN DMMEMCLEAN
	}
	return 0;	
}

/*************************************************************
* ENTRY METHOD
**************************************************************/
static int browseDHCPv6ClientInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	struct dm_data curr_data = {0};
	char *inst = NULL;

	uci_path_foreach_sections(bbfdm, parent_node->current_object_file, parent_node->obj->obj, curr_data.dmmap_section) {
		char *config_section_name = NULL;

		dmuci_get_value_by_section_string(curr_data.dmmap_section, "__section_name__", &config_section_name);
		curr_data.config_section = get_config_section_from_dmmap_section_name(config_section_name);

		inst = uci_handle_instance(dmctx, parent_node, &curr_data);

		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&curr_data, inst) == DM_STOP)
			break;
	}

	return 0;
}

static int browseDHCPv6ServerPoolInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	char *interface = NULL;
	struct dm_data curr_data = {0};
	char *inst = NULL;

	char *relay_sec = get_dnsmasq_section_name();

	uci_path_foreach_sections(bbfdm, parent_node->current_object_file, parent_node->obj->obj, curr_data.dmmap_section) {
		// skip the section if option ignore = '1'
		char *ignore = NULL;
		char *config_sec_name = NULL;

		dmuci_get_value_by_section_string(curr_data.dmmap_section, "__section_name__", &config_sec_name);
		if (DM_STRLEN(config_sec_name) == 0)
			continue;

		curr_data.config_section = get_config_section_from_dmmap_section_name(config_sec_name);
		if (curr_data.config_section == NULL)
			continue;

		dmuci_get_value_by_section_string(curr_data.config_section, "ignore", &ignore);
		if (ignore && DM_LSTRCMP(ignore, "1") == 0)
			continue;

		// if dns_relay instance not present, add in the section
		char *instance = NULL;
		dmuci_get_value_by_section_string(curr_data.config_section, "instance", &instance);
		if (DM_STRLEN(instance) == 0)
			dmuci_set_value_by_section(curr_data.config_section, "instance", relay_sec);

		dmuci_get_value_by_section_string(curr_data.config_section, "interface", &interface);
		curr_data.additional_data = (void *)interface;

		inst = uci_handle_instance(dmctx, parent_node, &curr_data);

		char *order = NULL;
		dmuci_get_value_by_section_string(curr_data.dmmap_section, "order", &order);
		if (DM_STRLEN(order) == 0)
			set_section_order("DHCPv6", curr_data.dmmap_section, curr_data.config_section, 0, inst);

		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&curr_data, inst) == DM_STOP)
			break;
	}

	return 0;
}

static int browseDHCPv6ServerPoolClientInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	json_object *res = NULL, *leases_arr = NULL, *lease_obj = NULL;
	struct dm_data curr_data = {0};
	char *inst = NULL;
	int id = 0, i = 0;

	char *iface_name = (char *)((struct dm_data *)prev_data)->additional_data;
	if (DM_STRLEN(iface_name) == 0)
		return 0;

	char *device = get_device(iface_name);
	if (DM_STRLEN(device) == 0)
		return 0;

	dmubus_call("dhcp", "ipv6leases", UBUS_ARGS{0}, 0, &res);

	dmjson_foreach_obj_in_array(res, leases_arr, lease_obj, i, 3, "device", device, "leases") {

		curr_data.json_object = lease_obj;

		inst = handle_instance_without_section(dmctx, parent_node, ++id);

		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&curr_data, inst) == DM_STOP)
			break;
	}

	return 0;
}

static int browseDHCPv6ServerPoolOptionInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	struct option_args curr_option_args = {0};
	struct dm_data curr_data = {0};
	char *pool = NULL, *inst = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)prev_data)->dmmap_section, "__instance__", &pool);

	uci_path_foreach_option_eq(bbfdm, parent_node->current_object_file, parent_node->obj->obj, "Pool", pool, curr_data.dmmap_section) {

		dmuci_get_value_by_section_string(curr_data.dmmap_section, "option_tag", &curr_option_args.tag);
		dmuci_get_value_by_section_string(curr_data.dmmap_section, "option_value", &curr_option_args.value);

		curr_data.config_section = ((struct dm_data *)prev_data)->config_section;
		curr_data.additional_data = &curr_option_args;

		inst = uci_handle_instance(dmctx, parent_node, &curr_data);

		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&curr_data, inst) == DM_STOP)
			break;
	}

	return 0;
}

static int browseDHCPv6ServerPoolClientIPv6AddressInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	json_object *ipv6_arr = NULL, *ipv6_obj = NULL;
	struct dm_data curr_data = {0};
	char *inst = NULL;
	int id = 0, i = 0;

	dmjson_foreach_obj_in_array(((struct dm_data *)prev_data)->json_object, ipv6_arr, ipv6_obj, i, 1, "ipv6-addr") {

		curr_data.json_object = ipv6_obj;

		inst = handle_instance_without_section(dmctx, parent_node, ++id);

		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&curr_data, inst) == DM_STOP)
			break;
	}
	return 0;
}

static int browseDHCPv6ServerPoolClientIPv6PrefixInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	json_object *ipv6_prefix_arr = NULL, *ipv6_prefix_obj = NULL;
	struct dm_data curr_data = {0};
	char *inst = NULL;
	int id = 0, i = 0;

	dmjson_foreach_obj_in_array(((struct dm_data *)prev_data)->json_object, ipv6_prefix_arr, ipv6_prefix_obj, i, 1, "ipv6-prefix") {

		curr_data.json_object = ipv6_prefix_obj;

		inst = handle_instance_without_section(dmctx, parent_node, ++id);

		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&curr_data, inst) == DM_STOP)
			break;
	}
	return 0;
}

/*************************************************************
* ADD & DEL OBJ
**************************************************************/
static int addObjDHCPv6Client(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
	struct dm_data *curr_data = (struct dm_data *)ctx->addobj_instance;
	dmuci_set_value_by_section(curr_data->dmmap_section, "disabled", "1");
	dmuci_set_value_by_section(curr_data->dmmap_section, "reqaddress", "force");
	dmuci_set_value_by_section(curr_data->dmmap_section, "reqprefix", "no");
	return 0;
}

static int delObjDHCPv6Client(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	char *proto = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "proto", &proto);
	if (DM_STRCMP(proto, "dhcpv6") == 0) {
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "proto", "none");
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "reqaddress", "");
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "reqprefix", "");
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "reqopts", "");
	}

	dmuci_delete_by_section(((struct dm_data *)data)->dmmap_section, NULL, NULL);
	return 0;
}

static int addObjDHCPv6ServerPool(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
	struct dm_data *curr_data = (struct dm_data *)ctx->addobj_instance;
	char dhcpv6_sname[32] = {0};

	snprintf(dhcpv6_sname, sizeof(dhcpv6_sname), "dhcpv6_%s", *instance);

	dmuci_add_section("dhcp", "dhcp", &curr_data->config_section);
	dmuci_rename_section_by_section(curr_data->config_section, dhcpv6_sname);
	dmuci_set_value_by_section(curr_data->config_section, "ignore", "0");
	dmuci_set_value_by_section(curr_data->config_section, "dhcpv6", "disabled");
	dmuci_set_value_by_section(curr_data->config_section, "instance", get_dnsmasq_section_name());

	char *inst = NULL;
	create_dmmap_obj(ctx, 0, "DHCPv4", "Pool", curr_data->config_section, &inst);

#ifdef DHCPMNGR_ROUTER_ADVERTISEMENT_OBJ
	struct uci_section *new_s = NULL;
	dmuci_add_section_bbfdm("dmmap_radv", "dhcp", &new_s);
	dmuci_set_value_by_section(new_s, "section_name", dhcpv6_sname);
	dmuci_set_value_by_section(new_s, "radv_intf_instance", *instance);
#endif

	return 0;
}

static int delObjDHCPv6ServerPool(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	char *pool_sec = NULL, *pool_inst = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "__section_name__", &pool_sec);
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "__instance__", &pool_inst);

	dmuci_delete_by_section(((struct dm_data *)data)->config_section, NULL, NULL);
	dmuci_delete_by_section(((struct dm_data *)data)->dmmap_section, NULL, NULL);

	/* delete all pool options */
	struct uci_section *dmmap_s = NULL, *dmmap_s_tmp = NULL;
	uci_path_foreach_option_eq_safe(bbfdm, "DHCPv6", "Option", "Pool", pool_inst, dmmap_s_tmp, dmmap_s) {
		dmuci_delete_by_section(dmmap_s, NULL, NULL);
	}

	/* Since v4, v6 and ra maps to same section */
	dmmap_s = NULL;
	dmmap_s_tmp = NULL;
	uci_path_foreach_option_eq_safe(bbfdm, "DHCPv4", "Pool", "__section_name__", pool_sec, dmmap_s_tmp, dmmap_s) {
		dmuci_delete_by_section(dmmap_s, NULL, NULL);
	}

	dmmap_s = NULL;
	dmmap_s_tmp = NULL;
	uci_path_foreach_option_eq_safe(bbfdm, "DHCPv4", "Option", "Pool", pool_inst, dmmap_s_tmp, dmmap_s) {
		dmuci_delete_by_section(dmmap_s, NULL, NULL);
	}

#ifdef DHCPMNGR_ROUTER_ADVERTISEMENT_OBJ
	dmmap_s = NULL;
	dmmap_s_tmp = NULL;
	uci_path_foreach_option_eq_safe(bbfdm, "dmmap_radv", "dhcp", "section_name", pool_sec+5, dmmap_s_tmp, dmmap_s) {
		dmuci_delete_by_section(dmmap_s, NULL, NULL);
	}
#endif

	return 0;
}

static int addObjDHCPv6ServerPoolOption(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
	struct dm_data *curr_data = (struct dm_data *)ctx->addobj_instance;
	char *pool = NULL, *inst = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "__instance__", &pool);

	char *tag = generate_tag_option("DHCPv6", "Option", "Pool", pool, "option_tag");

	dmuci_set_value_by_section(curr_data->dmmap_section, "enable", "0");
	dmuci_set_value_by_section(curr_data->dmmap_section, "option_tag", tag);

	struct uci_section *new_s = create_dmmap_obj(ctx, 1, "DHCPv4", "Option", NULL, &inst);
	dmuci_set_value_by_section(new_s, "enable", "0");
	dmuci_set_value_by_section(new_s, "option_tag", tag);

	return 0;
}

static int delObjDHCPv6ServerPoolOption(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	if (((struct dm_data *)data)->config_section) {
		struct option_args *option = (struct option_args *)((struct dm_data *)data)->additional_data;
		char tag_value[128] = {0};

		snprintf(tag_value, sizeof(tag_value), "%s,%s", option->tag, option->value);
		dmuci_del_list_value_by_section(((struct dm_data *)data)->config_section, "dhcp_option", tag_value);
	}

	// since v6 and v4 option maps to same uci section and option
	char *pool = NULL, *inst = NULL;
	struct uci_section *dmmap_s = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "Pool", &pool);
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "__instance__", &inst);

	uci_path_foreach_sections(bbfdm, "DHCPv4", "Option", dmmap_s) {
		char *v4_pool = NULL, *v4_inst = NULL;

		dmuci_get_value_by_section_string(dmmap_s, "Pool", &v4_pool);
		dmuci_get_value_by_section_string(dmmap_s, "__instance__", &v4_inst);

		if (DM_STRCMP(pool, v4_pool) == 0 && DM_STRCMP(inst, v4_inst) == 0) {
			dmuci_delete_by_section(dmmap_s, NULL, NULL);
			break;
		}
	}

	dmuci_delete_by_section(((struct dm_data *)data)->dmmap_section, NULL, NULL);

	return 0;
}

/*************************************************************
* GET & SET PARAM
**************************************************************/
static int get_DHCPv6_ClientNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	int cnt = get_number_of_entries(ctx, data, instance, NULL);
	dmasprintf(value, "%d", cnt);
	return 0;
}

/*#Device.DHCPv6.Client.{i}.Enable!UCI:network/interface,@i-1/disabled*/
static int get_DHCPv6Client_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *disabled = NULL, *proto = NULL;

	if (((struct dm_data *)data)->config_section != NULL) {
		dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "proto", &proto);
		if (DM_STRCMP(proto, "dhcpv6") != 0) {
			*value = dmstrdup("0");
			return 0;
		}
	}

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "disabled", &disabled);
	*value = (disabled[0] == '1') ? dmstrdup("0") : dmstrdup("1");
	return 0;
}

static int set_DHCPv6Client_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	bool b;

	switch (action)	{
	case VALUECHECK:
		if (bbfdm_validate_boolean(ctx, value))
			return FAULT_9007;
		break;
	case VALUESET:
		string_to_bool(value, &b);
		dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "disabled", b ? "0" : "1");

		if (((struct dm_data *)data)->config_section) {
			char *proto = NULL, *req_opts = NULL, *req_add = NULL, *req_pref = NULL;

			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "proto", &proto);

			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "reqaddress", "");
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "reqprefix", "");
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "reqopts", "");

			if (b == false) {
				if (DM_STRCMP(proto, "dhcpv6") == 0) {
					dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "proto", "none");
				}

				return 0;
			}

			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "proto", "dhcpv6");

			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "reqopts", &req_opts);
			if (DM_STRLEN(req_opts) != 0) {
				char *option = get_req_option_string(req_opts);
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "reqopts", option);
			}

			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "reqaddress", &req_add);
			if (DM_STRLEN(req_add) != 0) {
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "reqaddress", req_add);
			}

			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "reqprefix", &req_pref);
			if (DM_STRLEN(req_pref) != 0) {
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "reqprefix", req_pref);
			}
		}
		break;
	}
	return 0;
}

static int get_DHCPv6Client_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return bbf_get_alias(ctx, ((struct dm_data *)data)->dmmap_section, "Alias", instance, value);
}

static int set_DHCPv6Client_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	return bbf_set_alias(ctx, ((struct dm_data *)data)->dmmap_section, "Alias", instance, value);
}

static int get_DHCPv6Client_Interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	get_dhcp_client_relay_interface(data, value);
	return 0;
}

static int set_DHCPv6Client_Interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *device = NULL, *if_sec_name = NULL, *cl_dev = NULL, *disable = NULL, *proto = NULL;
	bool enable = false;
	struct uci_section *iface_sec = NULL, *cl_sec = NULL;
	int idx = 0;
	char inst[64] = {0};

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 256, NULL, NULL))
				return FAULT_9007;

			if (DM_STRLEN(value) == 0)
				return 0;

			if (DM_STRNCMP(value, "Device.IP.Interface.", 20) == 0) {
				sscanf(value, "Device.IP.Interface.%d", &idx);
				snprintf(inst, sizeof(inst), "%d", idx);

				uci_path_foreach_option_eq(bbfdm, "IP", "Interface", "__instance__", inst, iface_sec) {
					dmuci_get_value_by_section_string(iface_sec, "device", &device);
					break;
				}
			}

			if (DM_STRLEN(device) == 0)
				return FAULT_9007;

			/* Check if already an instance exists */
			uci_path_foreach_sections(bbfdm, "DHCPv6", "Client", cl_sec) {
				dmuci_get_value_by_section_string(cl_sec, "__section_name__", &if_sec_name);
				iface_sec = get_config_section_from_dmmap_section_name(if_sec_name);
				if (iface_sec == NULL)
					continue;

				dmuci_get_value_by_section_string(iface_sec, "device", &cl_dev);
				if (DM_STRCMP(cl_dev, device) == 0) {
					return FAULT_9007;
				}
			}

			break;
		case VALUESET:
			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "disabled", &disable);
			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "proto", &proto);

			enable = !dmuci_string_to_boolean(disable);
			if (enable == true && DM_STRCMP(proto, "dhcpv6") == 0) {
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "proto", "none");
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "reqaddress", "");
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "reqprefix", "");
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "reqopts", "");
			}

			if (DM_STRLEN(value) == 0) {
				dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "__section_name__", "");
				return 0;
			}

			sscanf(value, "Device.IP.Interface.%d", &idx);
			snprintf(inst, sizeof(inst), "%d", idx);

			uci_path_foreach_option_eq(bbfdm, "IP", "Interface", "__instance__", inst, iface_sec) {
				dmuci_get_value_by_section_string(iface_sec, "__section_name__", &if_sec_name);

				iface_sec = get_config_section_from_dmmap_section_name(if_sec_name);
				if (iface_sec == NULL)
					return FAULT_9007;

				break;
			}

			// Get the current network interface protocol
			char *curr_proto = NULL;
			dmuci_get_value_by_section_string(iface_sec, "proto", &curr_proto);

			if (DM_STRCMP(curr_proto, "dhcp") == 0) {
				// There is a DHCPv4 Client map to this interface section, therefore create a new interface section
				char *curr_device = NULL;
				char buf[64] = {0};

				snprintf(buf, sizeof(buf), "%s6", section_name(iface_sec));

				// Get the current device
				dmuci_get_value_by_section_string(iface_sec, "device", &curr_device);

				dmuci_add_section("network", "interface", &iface_sec);
				dmuci_rename_section_by_section(iface_sec, buf);

				// Update device option
				dmuci_set_value_by_section(iface_sec, "device", curr_device);
			}

			if (enable == true) {
				char *reqaddress = NULL, *reqprefix = NULL, *reqopts = NULL;

				// Update proto option of config section
				dmuci_set_value_by_section(iface_sec, "proto", "dhcpv6");

				// Get the current value of requested parameters
				dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "reqaddress", &reqaddress);
				dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "reqprefix", &reqprefix);
				dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "reqopts", &reqopts);

				// Set requested parameters
				char *option = get_req_option_string(reqopts);

				dmuci_set_value_by_section(iface_sec, "reqaddress", reqaddress);
				dmuci_set_value_by_section(iface_sec, "reqprefix", reqprefix);
				dmuci_set_value_by_section(iface_sec, "reqopts", option);
			}

			char sec_name[128] = {0};
			// Update dmmap section
			snprintf(sec_name, sizeof(sec_name), "network.%s", section_name(iface_sec));
			dmuci_set_value_by_section_bbfdm(((struct dm_data *)data)->dmmap_section, "__section_name__", sec_name);

			break;
	}
	return 0;
}

/*#Device.DHCPv6.Client.{i}.Status!UCI:network/interface,@i-1/disabled*/
static int get_DHCPv6Client_Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	bool b = false;
	char *enabled = NULL;

	get_DHCPv6Client_Enable(refparam, ctx, data, instance, &enabled);
	string_to_bool(enabled, &b);

	if (b == false) {
		*value = dmstrdup("Disabled");
		return 0;
	}

	if (false == get_dhcp_client_relay_interface_enable(data)) {
		*value = dmstrdup("Error_Misconfigured");
		return 0;
	}

	*value = dmstrdup("Enabled");
	return 0;
}

/*#Device.DHCPv6.Client.{i}.DUID!UBUS:network.interface/status/interface,@Name/data.passthru*/
static int get_DHCPv6Client_DUID(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *proto = NULL;
	struct uci_section *dhcpv6_s = ((struct dm_data *)data)->config_section;

	dmuci_get_value_by_section_string(dhcpv6_s, "proto", &proto);

	if (dhcpv6_s && DM_STRCMP(proto, "dhcpv6") == 0) {
		json_object *res = NULL;

		char *if_name = section_name(dhcpv6_s);
		dhcpmngr_get_interface_status(if_name, &res);
		DM_ASSERT(res, *value = dmstrdup(""));
		*value = dmjson_get_value(res, 2, "data", "passthru");
	}
	return 0;
}

/*#Device.DHCPv6.Client.{i}.RequestAddresses!UCI:network/interface,@i-1/reqaddress*/
static int get_DHCPv6Client_RequestAddresses(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct dm_data *dhcpv6_client = (struct dm_data *)data;
	char *reqaddress = NULL;

	dmuci_get_value_by_section_string(dhcpv6_client->dmmap_section, "reqaddress", &reqaddress);
	*value = (reqaddress && DM_LSTRCMP(reqaddress, "none") == 0) ? "0" : "1";
	return 0;
}

static int set_DHCPv6Client_RequestAddresses(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct dm_data *dhcpv6_client = (struct dm_data *)data;
	bool b;

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			return 0;
		case VALUESET:
			string_to_bool(value, &b);
			dmuci_set_value_by_section(dhcpv6_client->dmmap_section, "reqaddress", b ? "force" : "none");

			if (dhcpv6_client->config_section) {
				char *proto = NULL;

				dmuci_get_value_by_section_string(dhcpv6_client->config_section, "proto", &proto);
				if (DM_STRCMP(proto, "dhcpv6") == 0) {
					dmuci_set_value_by_section(dhcpv6_client->config_section, "reqaddress", b ? "force" : "none");
				}
			}

			break;
	}
	return 0;
}

/*#Device.DHCPv6.Client.{i}.RequestPrefixes!UCI:network/interface,@i-1/reqprefix*/
static int get_DHCPv6Client_RequestPrefixes(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct dm_data *dhcpv6_client = (struct dm_data *)data;
	char *reqprefix = NULL;

	dmuci_get_value_by_section_string(dhcpv6_client->dmmap_section, "reqprefix", &reqprefix);
	*value = (reqprefix && DM_LSTRCMP(reqprefix, "auto") == 0) ? "1" : "0";
	return 0;
}

static int set_DHCPv6Client_RequestPrefixes(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct dm_data *dhcpv6_client = (struct dm_data *)data;
	bool b;

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			return 0;
		case VALUESET:
			string_to_bool(value, &b);
			dmuci_set_value_by_section(dhcpv6_client->dmmap_section, "reqprefix", b ? "auto" : "no");

			if (dhcpv6_client->config_section) {
				char *proto = NULL;

				dmuci_get_value_by_section_string(dhcpv6_client->config_section, "proto", &proto);
				if (DM_STRCMP(proto, "dhcpv6") == 0) {
					dmuci_set_value_by_section(dhcpv6_client->config_section, "reqprefix", b ? "auto" : "no");
				}
			}
			return 0;
	}
	return 0;
}

static int get_DHCPv6Client_Renew(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmstrdup("false");
	return 0;
}

static int set_DHCPv6Client_Renew(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct uci_section *dhcpv6_s = ((struct dm_data *)data)->config_section;
	bool b;
	char *proto = NULL;

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			break;
		case VALUESET:
			string_to_bool(value, &b);
			if (!b) break;

			dmuci_get_value_by_section_string(dhcpv6_s, "proto", &proto);
			if (dhcpv6_s && DM_STRCMP(proto, "dhcpv6") == 0) {
				char *if_name = section_name(dhcpv6_s);
				dmubus_call_set("network.interface", "renew", UBUS_ARGS{{"interface", if_name, String}}, 1);
			}
			break;
	}
	return 0;
}

/*#Device.DHCPv6.Client.{i}.RequestedOptions!UCI:network/interface,@i-1/reqopts*/
static int get_DHCPv6Client_RequestedOptions(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct dm_data *dhcpv6_client = (struct dm_data *)data;

	dmuci_get_value_by_section_string(dhcpv6_client->dmmap_section, "reqopts", value);
	return 0;
}

static int set_DHCPv6Client_RequestedOptions(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct dm_data *dhcpv6_client = (struct dm_data *)data;

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_unsignedInt_list(ctx, value, -1, -1, -1, RANGE_ARGS{{NULL,NULL}}, 1))
				return FAULT_9007;
			break;
		case VALUESET:
			dmuci_set_value_by_section(dhcpv6_client->dmmap_section, "reqopts", value);
			if (dhcpv6_client->config_section) {
				char *proto = NULL;

				dmuci_get_value_by_section_string(dhcpv6_client->config_section, "proto", &proto);
				if (DM_STRCMP(proto, "dhcpv6") == 0) {
					char *option = get_req_option_string(value);
					dmuci_set_value_by_section(dhcpv6_client->config_section, "reqopts", option);
				}
			}
			break;
	}
	return 0;
}

static int get_DHCPv6Server_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	const char *path = "/etc/rc.d/*odhcpd";
	if (check_file(path))
		*value = dmstrdup("1");
	else
		*value = dmstrdup("0");
	return 0;
}

static int set_DHCPv6Server_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	bool b;

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			break;
		case VALUESET:
			string_to_bool(value, &b);
			dmcmd("/etc/init.d/odhcpd", 1, b ? "enable" : "disable");
			break;
	}
    return 0;
}

static int get_DHCPv6Server_PoolNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	int cnt = get_number_of_entries(ctx, data, instance, NULL);
	dmasprintf(value, "%d", cnt);
	return 0;
}

/*#Device.DHCPv6.Server.Pool.{i}.Enable!UCI:dhcp/dhcp,@i-1/dhcpv6*/
static int get_DHCPv6ServerPool_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "dhcpv6", value);
	*value = (*value && DM_LSTRCMP(*value, "disabled") == 0) ? "0" : "1";
	return 0;
}

static int set_DHCPv6ServerPool_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	bool b;

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			return 0;
		case VALUESET:
			string_to_bool(value, &b);
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "dhcpv6", b ? "server" : "disabled");
			return 0;
	}
	return 0;
}

/*#Device.DHCPv6.Server.Pool.{i}.Status!UCI:dhcp/dhcp,@i-1/dhcpv6*/
static int get_DHCPv6ServerPool_Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *status = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "dhcpv6", &status);
	if (DM_LSTRCMP(status, "disabled") != 0) {
		*value = dmstrdup("Enabled");
	} else {
		*value = dmstrdup("Disabled");
	}
	return 0;
}

static int get_DHCPv6ServerPool_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return bbf_get_alias(ctx, ((struct dm_data *)data)->dmmap_section, "Alias", instance, value);
}

static int set_DHCPv6ServerPool_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	return bbf_set_alias(ctx, ((struct dm_data *)data)->dmmap_section, "Alias", instance, value);
}

static int get_DHCPv6ServerPool_Order(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "order", value);
	return 0;
}

static int set_DHCPv6ServerPool_Order(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{"1",NULL}}, 1))
				return FAULT_9007;
			break;
		case VALUESET:
			set_section_order("DHCPv6", ((struct dm_data *)data)->dmmap_section, ((struct dm_data *)data)->config_section, 1, value);
			break;
	}
	return 0;
}

static int get_DHCPv6ServerPool_Interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	ctx->bbfdm_api_version = BBFDM_API_V1;

	_bbfdm_get_references(ctx, "Device.IP.Interface.", "Name", (char *)((struct dm_data *)data)->additional_data, value);
	return 0;
}

static int set_DHCPv6ServerPool_Interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *if_sec_name = NULL, *device = NULL;
	struct uci_section *iface_sec = NULL;
	int idx = 0;
	char inst[64] = {0};

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 256, NULL, NULL))
				return FAULT_9007;

			if (DM_STRLEN(value) == 0)
				return 0;

			if (DM_STRNCMP(value, "Device.IP.Interface.", 20) == 0) {
				sscanf(value, "Device.IP.Interface.%d", &idx);
				snprintf(inst, sizeof(inst), "%d", idx);

				uci_path_foreach_option_eq(bbfdm, "IP", "Interface", "__instance__", inst, iface_sec) {
					dmuci_get_value_by_section_string(iface_sec, "device", &device);
					dmuci_get_value_by_section_string(iface_sec, "__section_name__", &if_sec_name);
					break;
				}
			}

			if (DM_STRLEN(device) == 0)
				return FAULT_9007;

			iface_sec = get_config_section_from_dmmap_section_name(if_sec_name);
			if (iface_sec == NULL)
				return FAULT_9007;

			break;
		case VALUESET:
			if (DM_STRLEN(value) == 0) {
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "interface", "");
				return 0;
			}

			sscanf(value, "Device.IP.Interface.%d", &idx);
			snprintf(inst, sizeof(inst), "%d", idx);

			uci_path_foreach_option_eq(bbfdm, "IP", "Interface", "__instance__", inst, iface_sec) {
				dmuci_get_value_by_section_string(iface_sec, "__section_name__", &if_sec_name);
				break;
			}

			iface_sec = get_config_section_from_dmmap_section_name(if_sec_name);
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "interface", section_name(iface_sec));
			break;
	}
	return 0;
}

/*#Device.DHCPv6.Server.Pool.{i}.VendorClassID!UCI:dhcp/dhcp,@i-1/vendorclass*/
static int get_DHCPv6ServerPool_VendorClassID(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char hex[256] = {0}, *vcid = NULL;

	struct uci_section *vendorclassidclassifier = get_dhcpv6_classifier("vendorclass", (char *)((struct dm_data *)data)->additional_data);
	dmuci_get_value_by_section_string(vendorclassidclassifier, "vendorclass", &vcid);

	if (vcid && *vcid)
		convert_string_to_hex(vcid, hex, sizeof(hex));

	*value = (*hex) ? dmstrdup(hex) : "";
	return 0;
}

static int set_DHCPv6ServerPool_VendorClassID(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct uci_section *vendorclassidclassifier = NULL;
	char res[256] = {0};

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_hexBinary(ctx, value, RANGE_ARGS{{NULL,"65535"}}, 1))
				return FAULT_9007;
			break;
		case VALUESET:
			convert_hex_to_string(value, res, sizeof(res));

			vendorclassidclassifier = get_dhcpv6_classifier("vendorclass", (char *)((struct dm_data *)data)->additional_data);
			if (!vendorclassidclassifier) {
				dmuci_add_section("dhcp", "vendorclass", &vendorclassidclassifier);
				dmuci_set_value_by_section(vendorclassidclassifier, "networkid", (char *)((struct dm_data *)data)->additional_data);
			}
			dmuci_set_value_by_section(vendorclassidclassifier, "vendorclass", res);
			break;
	}
	return 0;
}

/*#Device.DHCPv6.Server.Pool.{i}.UserClassID!UCI:dhcp/dhcp,@i-1/userclass*/
static int get_DHCPv6ServerPool_UserClassID(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char hex[256] = {0}, *ucid = NULL;

	struct uci_section *userclassidclassifier = get_dhcpv6_classifier("userclass", (char *)((struct dm_data *)data)->additional_data);
	dmuci_get_value_by_section_string(userclassidclassifier, "userclass", &ucid);

	if (ucid && *ucid)
		convert_string_to_hex(ucid, hex, sizeof(hex));

	*value = (*hex) ? dmstrdup(hex) : "";
	return 0;
}

static int set_DHCPv6ServerPool_UserClassID(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct uci_section *userclassidclassifier = NULL;
	char res[256] = {0};

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_hexBinary(ctx, value, RANGE_ARGS{{NULL,"65535"}}, 1))
				return FAULT_9007;
			break;
		case VALUESET:
			convert_hex_to_string(value, res, sizeof(res));

			userclassidclassifier = get_dhcpv6_classifier("userclass", (char *)((struct dm_data *)data)->additional_data);
			if (!userclassidclassifier) {
				dmuci_add_section("dhcp", "userclass", &userclassidclassifier);
				dmuci_set_value_by_section(userclassidclassifier, "networkid", (char *)((struct dm_data *)data)->additional_data);
			}
			dmuci_set_value_by_section(userclassidclassifier, "userclass", res);
			break;
	}
	return 0;
}

static int get_DHCPv6ServerPool_SourceAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *classifier_s = get_dhcpv6_classifier("mac", (char *)((struct dm_data *)data)->additional_data);
	if (classifier_s == NULL) {
		*value = dmstrdup("");
		return 0;
	}

	return get_value_in_mac_format(classifier_s, "mac", false, value);
}

static int set_DHCPv6ServerPool_SourceAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 45, NULL, IPv6Address))
				return FAULT_9007;
			break;
		case VALUESET:
			break;
	}
	return 0;
}

static int get_DHCPv6ServerPool_SourceAddressMask(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *classifier_s = get_dhcpv6_classifier("mac", (char *)((struct dm_data *)data)->additional_data);
	if (classifier_s == NULL) {
		*value = dmstrdup("");
		return 0;
	}

	return get_value_in_mac_format(classifier_s, "mac", true, value);
}

static int set_DHCPv6ServerPool_SourceAddressMask(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 45, NULL, IPv6Address))
				return FAULT_9007;
			break;
		case VALUESET:
			break;
	}
	return 0;
}

static int get_DHCPv6ServerPool_ClientNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	int cnt = get_number_of_entries(ctx, data, instance, browseDHCPv6ServerPoolClientInst);
	dmasprintf(value, "%d", cnt);
	return 0;
}

static int get_DHCPv6ServerPool_OptionNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	int cnt = get_number_of_entries(ctx, data, instance, NULL);
	dmasprintf(value, "%d", cnt);
	return 0;
}

static int get_DHCPv6ServerPoolClient_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmasprintf(value, "cpe-%s", instance);
	return 0;
}

static int set_DHCPv6ServerPoolClient_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	return 0;
}

static int get_DHCPv6ServerPoolClient_IPv6AddressNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	int cnt = get_number_of_entries(ctx, data, instance, browseDHCPv6ServerPoolClientIPv6AddressInst);
	dmasprintf(value, "%d", cnt);
	return 0;
}

static int get_DHCPv6ServerPoolClient_IPv6PrefixNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	int cnt = get_number_of_entries(ctx, data, instance, browseDHCPv6ServerPoolClientIPv6PrefixInst);
	dmasprintf(value, "%d", cnt);
	return 0;
}

static int get_DHCPv6ServerPoolClientIPv6Address_IPAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "address");
	return 0;
}

static int get_DHCPv6ServerPoolClientIPv6Address_PreferredLifetime(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return get_value_in_date_time_format(((struct dm_data *)data)->json_object, "preferred-lifetime", value);
}

static int get_DHCPv6ServerPoolClientIPv6Address_ValidLifetime(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return get_value_in_date_time_format(((struct dm_data *)data)->json_object, "valid-lifetime", value);
}

static int get_DHCPv6ServerPoolClientIPv6Prefix_Prefix(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "address");
	return 0;
}

static int get_DHCPv6ServerPoolClientIPv6Prefix_PreferredLifetime(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmstrdup("0001-01-01T00:00:00Z");

	char *preferred = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "preferred-lifetime");
	if (preferred && *preferred != '\0' && DM_STRTOL(preferred) > 0) {
		time_t time_value = DM_STRTOL(preferred);
		char s_now[sizeof "AAAA-MM-JJTHH:MM:SSZ"];
		if (strftime(s_now, sizeof s_now, "%Y-%m-%dT%H:%M:%SZ", gmtime(&time_value)) == 0)
			return -1;
		*value = dmstrdup(s_now); // MEM WILL BE FREED IN DMMEMCLEAN
	}

	return 0;
}

static int get_DHCPv6ServerPoolClientIPv6Prefix_ValidLifetime(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmstrdup("0001-01-01T00:00:00Z");

	char *valid = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "valid-lifetime");
	if (valid && *valid != '\0' && DM_STRTOL(valid) > 0) {
		time_t time_value = DM_STRTOL(valid);
		char s_now[sizeof "AAAA-MM-JJTHH:MM:SSZ"];
		if (strftime(s_now, sizeof s_now, "%Y-%m-%dT%H:%M:%SZ", gmtime(&time_value)) == 0)
			return -1;
		*value = dmstrdup(s_now);
	}

	return 0;
}

static int get_DHCPv6ServerPoolOption_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "enable", value);
	return 0;
}

static int set_DHCPv6ServerPoolOption_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char opt_value[128] = {0};
	struct uci_list *option_list = NULL;
	bool val = false;
	struct option_args *option = (struct option_args *)((struct dm_data *)data)->additional_data;

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			break;
		case VALUESET:
			string_to_bool(value, &val);

			if (((struct dm_data *)data)->config_section && DM_STRLEN(option->value) != 0) {
				dmuci_get_value_by_section_list(((struct dm_data *)data)->config_section, "dhcp_option", &option_list);

				if (option_list != NULL) {
					struct uci_element *e = NULL;
					size_t length;

					uci_foreach_element(option_list, e) {
						char **buf = strsplit(e->name, ",", &length);
						if (buf && *buf && DM_STRCMP(buf[0], option->tag) == 0) {
							dmuci_del_list_value_by_section(((struct dm_data *)data)->config_section, "dhcp_option", opt_value);
							break;
						}
					}
				}

				if(val) {
					char res[256] = {0};

					dhcpmngr_convert_hex_option_to_string(DM_STRTOL(option->tag), option->value, res, sizeof(res));
					snprintf(opt_value, sizeof(opt_value), "%s,%s", option->tag, option->value);
					dmuci_add_list_value_by_section(((struct dm_data *)data)->config_section, "dhcp_option", opt_value);
				}
			}

			// since v6 and v4 option maps to same uci section and option
			char *pool = NULL, *inst = NULL;
			struct uci_section *dmmap_s = NULL;

			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "Pool", &pool);
			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "__instance__", &inst);

			uci_path_foreach_sections(bbfdm, "DHCPv4", "Option", dmmap_s) {
				char *v4_pool = NULL, *v4_inst = NULL;

				dmuci_get_value_by_section_string(dmmap_s, "Pool", &v4_pool);
				dmuci_get_value_by_section_string(dmmap_s, "__instance__", &v4_inst);

				if (DM_STRCMP(pool, v4_pool) == 0 && DM_STRCMP(inst, v4_inst) == 0) {
					dmuci_set_value_by_section_bbfdm(dmmap_s, "enable", val ? "1" : "0");
					break;
				}
			}

			dmuci_set_value_by_section_bbfdm(((struct dm_data *)data)->dmmap_section, "enable", val ? "1" : "0");
	}
	return 0;
}

static int get_DHCPv6ServerPoolOption_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return bbf_get_alias(ctx, ((struct dm_data *)data)->dmmap_section, "Alias", instance, value);
}

static int set_DHCPv6ServerPoolOption_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	return bbf_set_alias(ctx, ((struct dm_data *)data)->dmmap_section, "Alias", instance, value);
}

static int get_DHCPv6ServerPoolOption_Tag(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmstrdup(((struct option_args *)((struct dm_data *)data)->additional_data)->tag);
	return 0;
}

static int set_DHCPv6ServerPoolOption_Tag(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct option_args *option = (struct option_args *)((struct dm_data *)data)->additional_data;
	struct uci_list *option_list = NULL;
	bool opt_enabled = false;
	char *pool = NULL, *conf_enable = NULL;
	char new_tag_value[1024] = {0};

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{"1","65535"}}, 1))
				return FAULT_9007;

			if (option->tag && DM_STRCMP(option->tag, value) == 0)
				break;

			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "Pool", &pool);
			if (tag_option_exists("DHCPv6", "Option", "Pool", pool, "option_tag", value))
				return FAULT_9007;

			break;
		case VALUESET:
			if (((struct dm_data *)data)->config_section && DM_STRLEN(option->value) != 0) {
				dmuci_get_value_by_section_list(((struct dm_data *)data)->config_section, "dhcp_option", &option_list);

				if (option_list != NULL) {
					struct uci_element *e = NULL;
					size_t length;

					uci_foreach_element(option_list, e) {
						char **buf = strsplit(e->name, ",", &length);
						if (buf && *buf && DM_STRCMP(buf[0], option->tag) == 0) {
							dmuci_del_list_value_by_section(((struct dm_data *)data)->config_section, "dhcp_option", e->name);
							break;
						}
					}
				}

				dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "enable", &conf_enable);
				string_to_bool(conf_enable, &opt_enabled);

				if (opt_enabled) {
					char res[512] = {0};
					dhcpmngr_convert_hex_option_to_string(DM_STRTOL(value), option->value, res, sizeof(res));

					snprintf(new_tag_value, sizeof(new_tag_value), "%s,%s", value, res);
					dmuci_add_list_value_by_section(((struct dm_data *)data)->config_section, "dhcp_option", new_tag_value);
				}
			}

			// since v6 and v4 option maps to same uci section and option
			char *pool = NULL, *inst = NULL;
			struct uci_section *dmmap_s = NULL;

			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "Pool", &pool);
			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "__instance__", &inst);

			uci_path_foreach_sections(bbfdm, "DHCPv4", "Option", dmmap_s) {
				char *v4_pool = NULL, *v4_inst = NULL;

				dmuci_get_value_by_section_string(dmmap_s, "Pool", &v4_pool);
				dmuci_get_value_by_section_string(dmmap_s, "__instance__", &v4_inst);

				if (DM_STRCMP(pool, v4_pool) == 0 && DM_STRCMP(inst, v4_inst) == 0) {
					dmuci_set_value_by_section_bbfdm(dmmap_s, "option_tag", value);
					break;
				}
			}

			dmuci_set_value_by_section_bbfdm(((struct dm_data *)data)->dmmap_section, "option_tag", value);
			break;
	}

	return 0;
}

static int get_DHCPv6ServerPoolOption_Value(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct option_args *option = (struct option_args *)((struct dm_data *)data)->additional_data;
	*value = dmstrdup(option->value);
	return 0;
}

static int set_DHCPv6ServerPoolOption_Value(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_hexBinary(ctx, value, RANGE_ARGS{{"0","65535"}}, 1))
				return FAULT_9007;
			break;
		case VALUESET:
			set_dhcp_pool_option_value(data, value);

			// since v6 and v4 option maps to same uci section and option
			char *pool = NULL, *inst = NULL;
			struct uci_section *dmmap_s = NULL;

			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "Pool", &pool);
			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "__instance__", &inst);

			uci_path_foreach_sections(bbfdm, "DHCPv4", "Option", dmmap_s) {
				char *v4_pool = NULL, *v4_inst = NULL;

				dmuci_get_value_by_section_string(dmmap_s, "Pool", &v4_pool);
				dmuci_get_value_by_section_string(dmmap_s, "__instance__", &v4_inst);

				if (DM_STRCMP(pool, v4_pool) == 0 && DM_STRCMP(inst, v4_inst) == 0) {
					dmuci_set_value_by_section_bbfdm(dmmap_s, "option_value", value);
					break;
				}
			}

			dmuci_set_value_by_section_bbfdm(((struct dm_data *)data)->dmmap_section, "option_value", value);
			break;
	}
	return 0;
}

/*************************************************************
 * OPERATE COMMANDS
 *************************************************************/
static int operate_DHCPv6Client_Renew(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct uci_section *dhcpv6_s = ((struct dm_data *)data)->config_section;
	char *proto = NULL;

	dmuci_get_value_by_section_string(dhcpv6_s, "proto", &proto);

	if (dhcpv6_s && DM_STRCMP(proto, "dhcpv6") == 0) {
		char *if_name = section_name(dhcpv6_s);
		dmubus_call_set("network.interface", "renew", UBUS_ARGS{{"interface", if_name, String}}, 1);
	}

	return 0;
}

/**********************************************************************************************************************************
*                                            OBJ & PARAM DEFINITION
***********************************************************************************************************************************/
/* *** Device.DHCPv6. *** */
DMOBJ tDHCPv6Obj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"Client", &DMWRITE, addObjDHCPv6Client, delObjDHCPv6Client, NULL, browseDHCPv6ClientInst, NULL, NULL, NULL, tDHCPv6ClientParams, NULL, BBFDM_BOTH, NULL},
{"Server", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, tDHCPv6ServerObj, tDHCPv6ServerParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tDHCPv6Params[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"ClientNumberOfEntries", &DMREAD, DMT_UNINT, get_DHCPv6_ClientNumberOfEntries, NULL, BBFDM_BOTH},
{0}
};

DMLEAF tDHCPv6ClientParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_DHCPv6Client_Enable, set_DHCPv6Client_Enable, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_DHCPv6Client_Alias, set_DHCPv6Client_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Interface", &DMWRITE, DMT_STRING, get_DHCPv6Client_Interface, set_DHCPv6Client_Interface, BBFDM_BOTH, DM_FLAG_UNIQUE|DM_FLAG_REFERENCE},
{"Status", &DMREAD, DMT_STRING, get_DHCPv6Client_Status, NULL, BBFDM_BOTH},
{"DUID", &DMREAD, DMT_HEXBIN, get_DHCPv6Client_DUID, NULL, BBFDM_BOTH},
{"RequestAddresses", &DMWRITE, DMT_BOOL, get_DHCPv6Client_RequestAddresses, set_DHCPv6Client_RequestAddresses, BBFDM_BOTH},
{"RequestPrefixes", &DMWRITE, DMT_BOOL, get_DHCPv6Client_RequestPrefixes, set_DHCPv6Client_RequestPrefixes, BBFDM_BOTH},
//{"RapidCommit", &DMWRITE, DMT_BOOL, get_DHCPv6Client_RapidCommit, set_DHCPv6Client_RapidCommit, BBFDM_BOTH},
{"Renew", &DMWRITE, DMT_BOOL, get_DHCPv6Client_Renew, set_DHCPv6Client_Renew, BBFDM_CWMP},
//{"SuggestedT1", &DMWRITE, DMT_INT, get_DHCPv6Client_SuggestedT1, set_DHCPv6Client_SuggestedT1, BBFDM_BOTH},
//{"SuggestedT2", &DMWRITE, DMT_INT, get_DHCPv6Client_SuggestedT2, set_DHCPv6Client_SuggestedT2, BBFDM_BOTH},
//{"SupportedOptions", &DMREAD, DMT_STRING, get_DHCPv6Client_SupportedOptions, NULL, BBFDM_BOTH},
{"RequestedOptions", &DMWRITE, DMT_STRING, get_DHCPv6Client_RequestedOptions, set_DHCPv6Client_RequestedOptions, BBFDM_BOTH},
//{"ServerNumberOfEntries", &DMREAD, DMT_UNINT, get_DHCPv6Client_ServerNumberOfEntries, NULL, BBFDM_BOTH},
//{"SentOptionNumberOfEntries", &DMREAD, DMT_UNINT, get_DHCPv6Client_SentOptionNumberOfEntries, NULL, BBFDM_BOTH},
//{"ReceivedOptionNumberOfEntries", &DMREAD, DMT_UNINT, get_DHCPv6Client_ReceivedOptionNumberOfEntries, NULL, BBFDM_BOTH},
{"Renew()", &DMSYNC, DMT_COMMAND, NULL, operate_DHCPv6Client_Renew, BBFDM_USP},
{0}
};

/* *** Device.DHCPv6.Server. *** */
DMOBJ tDHCPv6ServerObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"Pool", &DMWRITE, addObjDHCPv6ServerPool, delObjDHCPv6ServerPool, NULL, browseDHCPv6ServerPoolInst, NULL, NULL, tDHCPv6ServerPoolObj, tDHCPv6ServerPoolParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tDHCPv6ServerParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_DHCPv6Server_Enable, set_DHCPv6Server_Enable, BBFDM_BOTH},
{"PoolNumberOfEntries", &DMREAD, DMT_UNINT, get_DHCPv6Server_PoolNumberOfEntries, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.DHCPv6.Server.Pool.{i}. *** */
DMOBJ tDHCPv6ServerPoolObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"Client", &DMREAD, NULL, NULL, NULL, browseDHCPv6ServerPoolClientInst, NULL, NULL, tDHCPv6ServerPoolClientObj, tDHCPv6ServerPoolClientParams, NULL, BBFDM_BOTH, NULL},
{"Option", &DMWRITE, addObjDHCPv6ServerPoolOption, delObjDHCPv6ServerPoolOption, NULL, browseDHCPv6ServerPoolOptionInst, NULL, NULL, NULL, tDHCPv6ServerPoolOptionParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tDHCPv6ServerPoolParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_DHCPv6ServerPool_Enable, set_DHCPv6ServerPool_Enable, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_DHCPv6ServerPool_Status, NULL, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_DHCPv6ServerPool_Alias, set_DHCPv6ServerPool_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Order", &DMWRITE, DMT_UNINT, get_DHCPv6ServerPool_Order, set_DHCPv6ServerPool_Order, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Interface", &DMWRITE, DMT_STRING, get_DHCPv6ServerPool_Interface, set_DHCPv6ServerPool_Interface, BBFDM_BOTH, DM_FLAG_REFERENCE},
//{"DUID", &DMWRITE, DMT_HEXBIN, get_DHCPv6ServerPool_DUID, set_DHCPv6ServerPool_DUID, BBFDM_BOTH},
//{"DUIDExclude", &DMWRITE, DMT_BOOL, get_DHCPv6ServerPool_DUIDExclude, set_DHCPv6ServerPool_DUIDExclude, BBFDM_BOTH},
{"VendorClassID", &DMWRITE, DMT_HEXBIN, get_DHCPv6ServerPool_VendorClassID, set_DHCPv6ServerPool_VendorClassID, BBFDM_BOTH},
//{"VendorClassIDExclude", &DMWRITE, DMT_BOOL, get_DHCPv6ServerPool_VendorClassIDExclude, set_DHCPv6ServerPool_VendorClassIDExclude, BBFDM_BOTH},
{"UserClassID", &DMWRITE, DMT_HEXBIN, get_DHCPv6ServerPool_UserClassID, set_DHCPv6ServerPool_UserClassID, BBFDM_BOTH},
//{"UserClassIDExclude", &DMWRITE, DMT_BOOL, get_DHCPv6ServerPool_UserClassIDExclude, set_DHCPv6ServerPool_UserClassIDExclude, BBFDM_BOTH},
{"SourceAddress", &DMWRITE, DMT_STRING, get_DHCPv6ServerPool_SourceAddress, set_DHCPv6ServerPool_SourceAddress, BBFDM_BOTH},
{"SourceAddressMask", &DMWRITE, DMT_STRING, get_DHCPv6ServerPool_SourceAddressMask, set_DHCPv6ServerPool_SourceAddressMask, BBFDM_BOTH},
//{"SourceAddressExclude", &DMWRITE, DMT_BOOL, get_DHCPv6ServerPool_SourceAddressExclude, set_DHCPv6ServerPool_SourceAddressExclude, BBFDM_BOTH},
//{"IANAEnable", &DMWRITE, DMT_BOOL, get_DHCPv6ServerPool_IANAEnable, set_DHCPv6ServerPool_IANAEnable, BBFDM_BOTH},
//{"IANAManualPrefixes", &DMWRITE, DMT_STRING, get_DHCPv6ServerPool_IANAManualPrefixes, set_DHCPv6ServerPool_IANAManualPrefixes, BBFDM_BOTH},
//{"IANAPrefixes", &DMREAD, DMT_STRING, get_DHCPv6ServerPool_IANAPrefixes, NULL, BBFDM_BOTH},
//{"IAPDEnable", &DMWRITE, DMT_BOOL, get_DHCPv6ServerPool_IAPDEnable, set_DHCPv6ServerPool_IAPDEnable, BBFDM_BOTH},
//{"IAPDManualPrefixes", &DMWRITE, DMT_STRING, get_DHCPv6ServerPool_IAPDManualPrefixes, set_DHCPv6ServerPool_IAPDManualPrefixes, BBFDM_BOTH},
//{"IAPDPrefixes", &DMREAD, DMT_STRING, get_DHCPv6ServerPool_IAPDPrefixes, NULL, BBFDM_BOTH},
//{"IAPDAddLength", &DMWRITE, DMT_UNINT, get_DHCPv6ServerPool_IAPDAddLength, set_DHCPv6ServerPool_IAPDAddLength, BBFDM_BOTH},
{"ClientNumberOfEntries", &DMREAD, DMT_UNINT, get_DHCPv6ServerPool_ClientNumberOfEntries, NULL, BBFDM_BOTH},
{"OptionNumberOfEntries", &DMREAD, DMT_UNINT, get_DHCPv6ServerPool_OptionNumberOfEntries, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.DHCPv6.Server.Pool.{i}.Client.{i}. *** */
DMOBJ tDHCPv6ServerPoolClientObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"IPv6Address", &DMREAD, NULL, NULL, NULL, browseDHCPv6ServerPoolClientIPv6AddressInst, NULL, NULL, NULL, tDHCPv6ServerPoolClientIPv6AddressParams, NULL, BBFDM_BOTH, NULL},
{"IPv6Prefix", &DMREAD, NULL, NULL, NULL, browseDHCPv6ServerPoolClientIPv6PrefixInst, NULL, NULL, NULL, tDHCPv6ServerPoolClientIPv6PrefixParams, NULL, BBFDM_BOTH, NULL},
//{"Option", &DMREAD, NULL, NULL, NULL, browseDHCPv6ServerPoolClientOptionInst, NULL, NULL, NULL, tDHCPv6ServerPoolClientOptionParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tDHCPv6ServerPoolClientParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Alias", &DMWRITE, DMT_STRING, get_DHCPv6ServerPoolClient_Alias, set_DHCPv6ServerPoolClient_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
//{"SourceAddress", &DMREAD, DMT_STRING, get_DHCPv6ServerPoolClient_SourceAddress, NULL, BBFDM_BOTH},
//{"Active", &DMREAD, DMT_BOOL, get_DHCPv6ServerPoolClient_Active, NULL, BBFDM_BOTH},
{"IPv6AddressNumberOfEntries", &DMREAD, DMT_UNINT, get_DHCPv6ServerPoolClient_IPv6AddressNumberOfEntries, NULL, BBFDM_BOTH},
{"IPv6PrefixNumberOfEntries", &DMREAD, DMT_UNINT, get_DHCPv6ServerPoolClient_IPv6PrefixNumberOfEntries, NULL, BBFDM_BOTH},
//{"OptionNumberOfEntries", &DMREAD, DMT_UNINT, get_DHCPv6ServerPoolClient_OptionNumberOfEntries, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.DHCPv6.Server.Pool.{i}.Client.{i}.IPv6Address.{i}. *** */
DMLEAF tDHCPv6ServerPoolClientIPv6AddressParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"IPAddress", &DMREAD, DMT_STRING, get_DHCPv6ServerPoolClientIPv6Address_IPAddress, NULL, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"PreferredLifetime", &DMREAD, DMT_TIME, get_DHCPv6ServerPoolClientIPv6Address_PreferredLifetime, NULL, BBFDM_BOTH},
{"ValidLifetime", &DMREAD, DMT_TIME, get_DHCPv6ServerPoolClientIPv6Address_ValidLifetime, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.DHCPv6.Server.Pool.{i}.Client.{i}.IPv6Prefix.{i}. *** */
DMLEAF tDHCPv6ServerPoolClientIPv6PrefixParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Prefix", &DMREAD, DMT_STRING, get_DHCPv6ServerPoolClientIPv6Prefix_Prefix, NULL, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"PreferredLifetime", &DMREAD, DMT_TIME, get_DHCPv6ServerPoolClientIPv6Prefix_PreferredLifetime, NULL, BBFDM_BOTH},
{"ValidLifetime", &DMREAD, DMT_TIME, get_DHCPv6ServerPoolClientIPv6Prefix_ValidLifetime, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.DHCPv6.Server.Pool.{i}.Client.{i}.Option.{i}. *** */
DMLEAF tDHCPv6ServerPoolClientOptionParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
//{"Tag", &DMREAD, DMT_UNINT, get_DHCPv6ServerPoolClientOption_Tag, NULL, BBFDM_BOTH},
//{"Value", &DMREAD, DMT_HEXBIN, get_DHCPv6ServerPoolClientOption_Value, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.DHCPv6.Server.Pool.{i}.Option.{i}. *** */
DMLEAF tDHCPv6ServerPoolOptionParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_DHCPv6ServerPoolOption_Enable, set_DHCPv6ServerPoolOption_Enable, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_DHCPv6ServerPoolOption_Alias, set_DHCPv6ServerPoolOption_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Tag", &DMWRITE, DMT_UNINT, get_DHCPv6ServerPoolOption_Tag, set_DHCPv6ServerPoolOption_Tag, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Value", &DMWRITE, DMT_HEXBIN, get_DHCPv6ServerPoolOption_Value, set_DHCPv6ServerPoolOption_Value, BBFDM_BOTH},
//{"PassthroughClient", &DMWRITE, DMT_STRING, get_DHCPv6ServerPoolOption_PassthroughClient, set_DHCPv6ServerPoolOption_PassthroughClient, BBFDM_BOTH},
{0}
};
