/*
 * 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: Anis Ellouze <anis.ellouze@pivasoftware.com>
 *		Author: Omar Kallel <omar.kallel@pivasoftware.com>
 *		Author: Amin Ben Romdhane <amin.benromdhane@iopsys.eu>
 *
 */

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

#define DHCP_OPTION_VENDORID 60
#define DHCP_OPTION_CLIENTID 61
#define DHCP_OPTION_HOSTNAME 12
#define DHCP_CLIENT_INFO "/tmp/dhcp_client_info"

struct dhcp_lease {
	struct list_head list;
	uint64_t ts;
	char hwaddr[24];
	char ipaddr[16];
};

struct dhcp_iface_args {
	char *iface_name;
	unsigned net_start;
	unsigned net_end;
	unsigned bits;
};

struct dhcp_args {
	struct dhcp_iface_args iface_args;
	struct list_head leases;
	unsigned n_leases;
};

/*************************************************************
* COMMON FUNCTIONS
**************************************************************/
static void _str_tolower(const char *src, char *target)
{
	int i;
	size_t len;

	if ( !src && !target)
		return;

	len = DM_STRLEN(src);
	for (i = 0; i < len; i++ ) {
		target[i] = tolower(src[i]);
	}
	target[i] = '\0';
}

static struct uci_section *exist_other_section_same_order(struct uci_section *dmmap_sect, const char *package, const char *sect_type, const char *order)
{
	struct uci_section *s = NULL;

	uci_path_foreach_option_eq(bbfdm, package, sect_type, "order", order, s) {
		if (strcmp(section_name(s), section_name(dmmap_sect)) != 0) {
			return s;
		}
	}
	return NULL;
}

int set_section_order(const char *dmpackage, struct uci_section *dmmap_sect, struct uci_section *conf, int set_force, const char *order)
{
	char *v = NULL, *sect_name, *incrorder;
	struct uci_section *s, *dm;

	dmuci_get_value_by_section_string(dmmap_sect, "order", &v);
	if (DM_STRLEN(v) > 0 && DM_STRCMP(v, order) == 0)
		return 0;
	dmuci_set_value_by_section_bbfdm(dmmap_sect, "order", order);
	if (conf == NULL) {
		dmuci_get_value_by_section_string(dmmap_sect, "__section_name__", &sect_name);
		s = get_config_section_from_dmmap_section_name(sect_name);
	} else
		s = conf;

	if (DM_LSTRCMP(order, "1") != 0 && s != NULL) {
		dmuci_set_value_by_section(s, "force", "");
	}

	if (set_force == 1 && DM_LSTRCMP(order, "1") == 0 && s != NULL) {
		dmuci_set_value_by_section(s, "force", "1");
	}

	if ((dm = exist_other_section_same_order(dmmap_sect, dmpackage, section_type(dmmap_sect), order)) != NULL) {
		dmuci_get_value_by_section_string(dm, "__section_name__", &sect_name);
		s = get_config_section_from_dmmap_section_name(sect_name);
		dmasprintf(&incrorder, "%ld", DM_STRTOL(order)+1);
		if (s != NULL && DM_LSTRCMP(order, "1") == 0) {
			dmuci_set_value_by_section(s, "force", "");
		}
		set_section_order(dmpackage, dm, s, set_force, incrorder);
	}
	return 0;
}

int get_value_in_mac_format(struct uci_section *s, const char *option_name, bool type, char **value)
{
	char *option_value = NULL, **macarray, buf[32];
	unsigned pos = 0;
	size_t length;

	dmuci_get_value_by_section_string(s, option_name, &option_value);
	if (option_value == NULL || *option_value == '\0')
		return -1;

	buf[0] = 0;
	macarray = strsplit(option_value, ":", &length);

	for (int i = 0; i < 6; i++)
		pos += snprintf(&buf[pos], sizeof(buf) - pos, "%s:", (macarray[i] && DM_LSTRCMP(macarray[i], "*") == 0) ? "00" : type ? "FF" : macarray[i]);

	if (pos)
		buf[pos - 1] = 0;

	*value = dmstrdup(buf);
	return 0;
}

/* used in dhcpv6.c also */
struct uci_section *get_dhcp_client_relay_interface_section(void *data)
{
	char *iface_sec_name = NULL;
	struct uci_section *iface_sec = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "__section_name__", &iface_sec_name);
	if (DM_STRLEN(iface_sec_name) == 0)
		return NULL;

	iface_sec = get_dup_section_in_dmmap_opt("IP", "Interface", "__section_name__", iface_sec_name);
	if (iface_sec == NULL) {
		char *device = NULL;
		char sec_name[128] = {0};

		dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "device", &device);
		if (DM_STRLEN(device) == 0)
			return NULL;

		uci_foreach_option_eq("network", "interface", "device", device, iface_sec) {
			if (DM_STRCMP(section_name(iface_sec), section_name(((struct dm_data *)data)->config_section)) != 0) {
				snprintf(sec_name, sizeof(sec_name), "%s.%s", section_config(iface_sec), section_name(iface_sec));
				struct uci_section *s = get_dup_section_in_dmmap_opt("IP", "Interface", "__section_name__", sec_name);
				if (s == NULL)
					continue;

				return s;
			}
		}
	} else {
		return iface_sec;
	}

	return NULL;
}

void get_dhcp_client_relay_interface(void *data, char **value)
{
	struct uci_section *iface_sec = NULL;
	char *iface_inst = NULL;

	iface_sec = get_dhcp_client_relay_interface_section(data);
	if (iface_sec == NULL) {
		return;
	}

	dmuci_get_value_by_section_string(iface_sec, "__instance__", &iface_inst);

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

	dmasprintf(value, "Device.IP.Interface.%s", iface_inst);
}

bool get_dhcp_client_relay_interface_enable(void *data)
{
	struct uci_section *iface_sec = NULL;
	char *disable = NULL;
	bool b = false;

	iface_sec = get_dhcp_client_relay_interface_section(data);
	if (iface_sec == NULL) {
		return b;
	}

	dmuci_get_value_by_section_string(iface_sec, "disabled", &disable);
	string_to_bool(disable, &b);

	return !b;
}

static void dhcp_leases_load(struct list_head *head)
{
	// cppcheck-suppress cert-MSC24-C
	FILE *f = fopen(DHCP_LEASES_FILE, "r");
	char line[128];

	if (f == NULL)
		return;

	while (fgets(line, sizeof(line) - 1, f)) {
		struct dhcp_lease *lease;

		if (line[0] == '\n')
			continue;

		lease = dmcalloc(1, sizeof(*lease));
		if (lease == NULL)
			break;

		sscanf(line, "%" PRId64 "%19s %15s",
			&lease->ts, lease->hwaddr, lease->ipaddr);

		list_add_tail(&lease->list, head);
	}
	fclose(f);

}

static char *find_ipv4_interface_up(char *parent_interface)
{
	struct uci_section *s = NULL;

	if (DM_STRLEN(parent_interface) == 0)
		return "";

	char *device = get_device(parent_interface);
	if (DM_STRLEN(device) == 0) {
		dmuci_get_option_value_string("network", parent_interface, "device", &device);
	}

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

	uci_foreach_option_eq("network", "interface", "device", device, s) {
		json_object *res = NULL;
		char *addr_str = NULL;
		int addr_cidr = -1;

		dhcpmngr_get_interface_status(section_name(s), &res);
		// cannot use DM_ASSERT because if res is null
		// we cannot return, we have to try another approach
		// cppcheck-suppress knownConditionTrueFalse
		if (res) {
			json_object *jobj;

			jobj = dmjson_select_obj_in_array_idx(res, 0, 1, "ipv4-address");
			if (jobj) {
				json_object_object_foreach(jobj, key, val) {
					if (!DM_LSTRCMP(key, "address"))
						addr_str = (char *)json_object_get_string(val);
					else if (!DM_LSTRCMP(key, "mask"))
						addr_cidr = json_object_get_int(val);
				}
			}
		}

		if (DM_STRLEN(addr_str) == 0) {
			dmuci_get_option_value_string("network", section_name(s), "ipaddr", &addr_str);
		}

		if (addr_cidr == -1) {
			char *mask_str = NULL;

			dmuci_get_option_value_string("network", section_name(s), "netmask", &mask_str);
			if (DM_STRLEN(mask_str)) addr_cidr = netmask2cidr(mask_str);
		}

		if (DM_STRLEN(addr_str) == 0 || addr_cidr == -1)
			continue;

		return dmstrdup(section_name(s));
	}

	return parent_interface;
}

int interface_get_ipv4(char *interface, uint32_t *addr, unsigned *bits)
{
	json_object *res = NULL;
	char *addr_str = NULL;
	int addr_cidr = -1;

	if (DM_STRLEN(interface) == 0)
		return -1;

	dhcpmngr_get_interface_status(interface, &res);
	// cannot use DM_ASSERT because if res is null
	// we cannot return, we have to try another approach
	// cppcheck-suppress knownConditionTrueFalse
	if (res) {
		json_object *jobj;

		jobj = dmjson_select_obj_in_array_idx(res, 0, 1, "ipv4-address");
		if (jobj) {
			json_object_object_foreach(jobj, key, val) {
				if (!DM_LSTRCMP(key, "address"))
					addr_str = (char *)json_object_get_string(val);
				else if (!DM_LSTRCMP(key, "mask"))
					addr_cidr = json_object_get_int(val);
			}
		}
	}

	if (DM_STRLEN(addr_str) == 0) {
		dmuci_get_option_value_string("network", interface, "ipaddr", &addr_str);
	}

	if (addr_cidr == -1) {
		char *mask_str = NULL;

		dmuci_get_option_value_string("network", interface, "netmask", &mask_str);
		if (DM_STRLEN(mask_str)) addr_cidr = netmask2cidr(mask_str);
	}

	if (DM_STRLEN(addr_str) == 0 || addr_cidr == -1) {
		goto end;
	}

	if (inet_pton(AF_INET, addr_str, addr) != 1) {
		goto end;
	}

	*bits = addr_cidr;
	return 0;
end:
	return -1;
}

static void dhcp_leases_assign_to_interface(struct dhcp_args *dhcp, struct list_head *src)
{
	struct dhcp_lease *lease = NULL, *tmp = NULL;

	list_for_each_entry_safe(lease, tmp, src, list) {
		unsigned addr;

		inet_pton(AF_INET, lease->ipaddr, &addr);

		if (ntohl(addr) >= dhcp->iface_args.net_start && ntohl(addr) <= dhcp->iface_args.net_end) {
			list_move_tail(&lease->list, &dhcp->leases);
			dhcp->n_leases += 1;
		}
	}
}

static bool check_dhcp_host_option_exists(const char *dhcp_pool_name, const char *option, const char *value)
{
	struct uci_section *s = NULL;
	char *opt_value;

	uci_foreach_option_eq("dhcp", "host", "dhcp_pool", dhcp_pool_name, s) {

		dmuci_get_value_by_section_string(s, option, &opt_value);

		if (DM_STRCMP(opt_value, value) == 0)
			return true;
	}

	return false;
}

static int fill_dhcp_iface_args(struct dm_data *curr_data, struct dhcp_args *dhcp_args)
{
	memset(&dhcp_args->iface_args, 0, sizeof(struct dhcp_iface_args));

	dmuci_get_value_by_section_string(curr_data->config_section, "interface", &dhcp_args->iface_args.iface_name);

	unsigned iface_addr, iface_cidr;
	char addr_min[INET_ADDRSTRLEN] = {0};
	char addr_max[INET_ADDRSTRLEN] = {0};

	char *start = dmuci_get_value_by_section_fallback_def(curr_data->config_section, "start", "0");
	char *limit = dmuci_get_value_by_section_fallback_def(curr_data->config_section, "limit", "0");

	if (interface_get_ipv4(dhcp_args->iface_args.iface_name, &iface_addr, &iface_cidr))
		return -1;

	dhcp_args->iface_args.bits = ~((1 << (32 - iface_cidr)) - 1);
	dhcp_args->iface_args.net_start = (ntohl(iface_addr) & dhcp_args->iface_args.bits) + DM_STRTOL(start);
	dhcp_args->iface_args.net_end = (ntohl(iface_addr) & dhcp_args->iface_args.bits) + DM_STRTOL(start) + DM_STRTOL(limit) - 1;

	unsigned iface_addr_min = htonl(dhcp_args->iface_args.net_start);
	inet_ntop(AF_INET, &iface_addr_min, addr_min, INET_ADDRSTRLEN);

	unsigned iface_addr_max = htonl(dhcp_args->iface_args.net_end);
	inet_ntop(AF_INET, &iface_addr_max, addr_max, INET_ADDRSTRLEN);

	dmuci_set_value_by_section(curr_data->dmmap_section, "MinAddress", addr_min);
	dmuci_set_value_by_section(curr_data->dmmap_section, "MaxAddress", addr_max);
	dmuci_set_value_by_section(curr_data->dmmap_section, "SubnetMask", cidr2netmask(iface_cidr));

	return 0;
}

static int check_ipv4_in_dhcp_pool(struct dhcp_iface_args *dhcp_iface, const char *ip)
{
	unsigned addr, net;

	inet_pton(AF_INET, ip, &addr);
	net = ntohl(addr) & dhcp_iface->bits;

	if (net > dhcp_iface->net_end || net < dhcp_iface->net_start)
		return -1;

	return 0;
}

static char *get_dhcp_network_from_relay_list(const char *net_list)
{
	struct uci_section *s = NULL;
	size_t length = 0;

	if (!net_list || *net_list == 0)
		return "";

	char **net_list_arr = strsplit(net_list, " ", &length);
	uci_foreach_sections("network", "interface", s) {
		char *proto = NULL;

		dmuci_get_value_by_section_string(s, "proto", &proto);
		for (int i = 0; i < length; i++) {
			if (strcmp(net_list_arr[i], section_name(s)) == 0 && DM_LSTRCMP(proto, "dhcp") == 0)
				return net_list_arr[i];
		}
	}

	return "";
}

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

	uci_foreach_sections("dhcp", classifier_name, s) {
		dmuci_get_value_by_section_string(s, "networkid", &networkid);
		if (DM_STRCMP(networkid, network) == 0)
			return s;
	}

	return NULL;
}

bool tag_option_exists(const char *dmmap_package, const char *section, const char *opt_check, const char *value_check, const char *tag_name, const char *tag_value)
{
	struct uci_section *s = NULL;

	uci_path_foreach_option_eq(bbfdm, dmmap_package, section, opt_check, value_check, s) {
		char *curr_tag = NULL;

		dmuci_get_value_by_section_string(s, tag_name, &curr_tag);
		if (curr_tag && tag_value && DM_STRCMP(curr_tag, tag_value) == 0)
			return true;
	}

	return false;
}

char *generate_tag_option(const char *dmmap_package, const char *section, const char *opt_check, const char *value_check, const char *tag_name)
{
	for (int i = 1; i <= 254; i++) {

		char tag_value[16] = {0};
		snprintf(tag_value, sizeof(tag_value), "%d", i);

		if (tag_option_exists(dmmap_package, section, opt_check, value_check, tag_name, tag_value))
			continue;

		return dmstrdup(tag_value);
	}

	return "0";
}

static int get_DHCPv4ServerPool_Option_Value(struct uci_section *s, const char *option, char **value)
{
	struct uci_list *dhcp_option = NULL;
	struct uci_element *e = NULL;

	dmuci_get_value_by_section_list(s, "dhcp_option", &dhcp_option);
	if (dhcp_option == NULL)
		return -1;

	uci_foreach_element(dhcp_option, e) {
		char *pch = DM_STRCHR(e->name, ',');
		if (pch) {
			char opt_tag[8] = {0};
			unsigned int len = pch - e->name + 1;
			unsigned int opt_size = (len > sizeof(opt_tag)) ? sizeof(opt_tag) : len;

			DM_STRNCPY(opt_tag, e->name, opt_size);
			if (DM_STRCMP(opt_tag, option) == 0) {
				*value = dmstrdup(pch + 1);
				return 0;
			}
		} else {
			if (DM_STRCMP(e->name, option) == 0)
				return 0;
		}
	}
	return -1;
}

static int set_DHCPv4ServerPool_Option_Value(void *data, const char *option, const char *value)
{
	struct uci_list *dhcp_option = NULL;
	char new_dhcp_option[256] = {0};

	if (option == NULL || value == NULL)
		return 0;

	struct uci_section *s = ((struct dm_data *)data)->config_section;

	dmuci_get_value_by_section_list(s, "dhcp_option", &dhcp_option);
	if (dhcp_option) {
		struct uci_element *e = NULL, *tmp = NULL;

		uci_foreach_element_safe(dhcp_option, tmp, e) {
			char *pch = DM_STRCHR(e->name, ',');
			if (pch) {
				char opt_tag[8] = {0};
				unsigned int len = pch - e->name + 1;
				unsigned int opt_size = (len > sizeof(opt_tag)) ? sizeof(opt_tag) : len;

				DM_STRNCPY(opt_tag, e->name, opt_size);
				if (DM_STRCMP(opt_tag, option) == 0) {
					dmuci_del_list_value_by_section(s, "dhcp_option", e->name);
					break;
				}
			} else {
				if (DM_STRCMP(e->name, option) == 0) {
					dmuci_del_list_value_by_section(s, "dhcp_option", e->name);
					break;
				}
			}
		}
	}

	if (DM_STRLEN(value) != 0) {
		if (DM_STRCMP(option, "6") == 0 || DM_STRCMP(option, "3") == 0) {
			/* If dhcp_option 6 (DNS servers) is set to the same value as the interface address,
			 * then remove option 6 from dhcp_option list so that dnsmasq will continue with
			 * default behavior to send the interface IP as the DNS server address, hence
			 * any change of interface's IP will be automatically taken care by dnsmasq.
			 * The same logic for dhcp option 3 (Router address) */
			char *ip_add = NULL;
			char *ifname = ((struct dhcp_args *)((struct dm_data *)data)->additional_data)->iface_args.iface_name;

			dmuci_get_option_value_string("network", ifname, "ipaddr", &ip_add);

			if (DM_STRCMP(value, ip_add) == 0) {
				// option 6 or 3 already deleted from list so nothing to do
				return 0;
			}
		}

		if (DM_STRCMP(option, "15") == 0) {
			char *sec = get_dnsmasq_section_name();
			char *domain = NULL;

			dmuci_get_option_value_string("dhcp", sec, "domain", &domain);

			if (DM_STRCMP(value, domain) == 0) {
				// option 15 already deleted, nothing to do
				return 0;
			}
		}

		snprintf(new_dhcp_option, sizeof(new_dhcp_option), "%s,%s", option, value);
	} else {
		// Setting the tag only so, dnsmasq will send empty value for this dhcp option
		snprintf(new_dhcp_option, sizeof(new_dhcp_option), "%s", option);
	}

	dmuci_add_list_value_by_section(s, "dhcp_option", new_dhcp_option);
	return 0;
}

char *get_dhcp_option_name(int tag)
{
	switch(tag) {
		case DHCP_OPTION_VENDORID:
			return "vendorid";
		case DHCP_OPTION_CLIENTID:
			return "clientid";
		case DHCP_OPTION_HOSTNAME:
			return "hostname";
		default:
			return "sendopts";
	}
}

void fill_dhcp_option_value(char *option, char *tag, size_t tag_size, char *value, size_t value_size)
{
	long tag_num = 0;

	if (!option || !tag || !tag_size || !value || !value_size)
		return;

	char *p = strchr(option, ':');
	if (!p)
		return;

	*p = 0;

	if (isdigit_str(option) || ishex_str(option)) {
		tag_num = strtol(option, NULL, 0);
		unsigned pos = 0;

		char *val = p + 1;

		for (int i = 0; i < DM_STRLEN(val); i++) {
			if (val[i] == ':')
				continue;

			pos += snprintf(&value[pos], value_size - pos, "%c", val[i]);
		}
	} else {
		tag_num = dhcpmngr_get_dhcp_option_number_by_name(option);
		if (tag_num < 0)
			return;

		dhcpmngr_convert_str_option_to_hex(tag_num, p + 1, value, value_size);
	}

	snprintf(tag, tag_size, "%ld", tag_num);
}

static void create_dhcp_sent_option_list(struct uci_section *iface_s, const char *client)
{
	struct uci_section *dmmap_s = NULL;
	char buf[1024] = {0};
	unsigned pos = 0;

	uci_path_foreach_option_eq(bbfdm, "DHCPv4", "SentOption", "Client", client, dmmap_s) {
		char *enable = NULL;
		char *tag = NULL;
		char *value = NULL;

		dmuci_get_value_by_section_string(dmmap_s, "enable", &enable);
		if (DM_LSTRCMP(enable, "1") != 0)
			continue;

		dmuci_get_value_by_section_string(dmmap_s, "option_tag", &tag);
		dmuci_get_value_by_section_string(dmmap_s, "option_value", &value);

		char *option_name = get_dhcp_option_name(DM_STRTOL(tag));
		if (DM_LSTRCMP(option_name, "sendopts") == 0) {
			pos += snprintf(&buf[pos], sizeof(buf) - pos, "%s:%s ", tag, value);
		} else {
			char str[256] = {0};

			convert_hex_to_string(value, str, sizeof(str));
			dmuci_set_value_by_section(iface_s, option_name, (DM_STRTOL(tag) == DHCP_OPTION_CLIENTID) ? value : str);
		}
	}

	if (pos)
		buf[pos - 1] = 0;

	dmuci_set_value_by_section(iface_s, "sendopts", buf);
}

static void create_dhcp_req_option_list(struct uci_section *iface_s, const char *client)
{
	struct uci_section *dmmap_s = NULL;
	char buf[256] = {0};
	unsigned pos = 0;

	uci_path_foreach_option_eq(bbfdm, "DHCPv4", "ReqOption", "Client", client, dmmap_s) {
		char *enable = NULL;
		char *tag = NULL;

		dmuci_get_value_by_section_string(dmmap_s, "enable", &enable);
		if (DM_LSTRCMP(enable, "1") != 0)
			continue;

		dmuci_get_value_by_section_string(dmmap_s, "option_tag", &tag);

		pos += snprintf(&buf[pos], sizeof(buf) - pos, "%s ", tag);
	}

	if (pos)
		buf[pos - 1] = 0;

	dmuci_set_value_by_section(iface_s, "reqopts", buf);
}

static char *remove_option_from_str_list(const char *str_list, long tag)
{
	char *pch = NULL, *spch = NULL;
	unsigned pos = 0;

	if (!str_list || !tag)
		return "";

	int len = strlen(str_list);

	char *res = (char *)dmcalloc(len + 1, sizeof(char));
	char *list = dmstrdup(str_list);

	for (pch = strtok_r(list, " ", &spch); pch != NULL; pch = strtok_r(NULL, " ", &spch)) {

		char *p = strchr(pch, ':');
		if (!p)
			continue;

		*p = 0;

		long tag_num = (isdigit_str(pch) || ishex_str(pch)) ? strtol(pch, NULL, 0) : dhcpmngr_get_dhcp_option_number_by_name(pch);

		if (tag_num == tag)
			continue;

		pos += snprintf(&res[pos], len + 1 - pos, "%s:%s ", pch, p + 1);
	}

	dmfree(list);

	if (pos)
		res[pos - 1] = 0;

	return res;
}

static bool is_odhcpd_maindhcp(void)
{
	char *odhcpd_enabled = dmuci_get_option_value_fallback_def("dhcp", "odhcpd", "maindhcp", "0");
	return (odhcpd_enabled && odhcpd_enabled[0] == '1');
}

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

	char *relay_sec = get_dnsmasq_section_name();

	dhcp_leases_load(&leases);

	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);

		INIT_LIST_HEAD(&curr_dhcp_args.leases);
		curr_dhcp_args.n_leases = 0;

		fill_dhcp_iface_args(&curr_data, &curr_dhcp_args);

		dhcp_leases_assign_to_interface(&curr_dhcp_args, &leases);

		curr_data.additional_data = (void *)&curr_dhcp_args;

		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("DHCPv4", 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 browseDHCPv4ServerPoolStaticAddressInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	struct dm_data curr_data = {0};
	char *inst = NULL, *pool = 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) {
		char *config_sec_name = NULL;

		dmuci_get_value_by_section_string(curr_data.dmmap_section, "__section_name__", &config_sec_name);
		curr_data.config_section = get_config_section_from_dmmap_section_name(config_sec_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 browseDhcpClientInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	struct dm_data curr_data = {0};
	struct dhcp_lease *lease = NULL;
	char *inst = NULL;
	int id = 0;

	list_for_each_entry(lease, &((struct dhcp_args *)((struct dm_data *)prev_data)->additional_data)->leases, list) {

		curr_data.additional_data = (void *)lease;

		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 browseDhcpClientIPv4Inst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	DM_LINK_INST_OBJ(dmctx, parent_node, prev_data, "1");
	return 0;
}

static int browseDHCPv4ServerPoolClientOptionInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	// cppcheck-suppress cert-MSC24-C
	FILE *f = fopen(DHCP_CLIENT_OPTIONS_FILE, "r");
	if (f == NULL)
		return 0;

	struct option_args curr_client_options_args = {0};
	struct dm_data curr_data = {0};
	char line[2048], macaddr[24] = {0}, vcid[128] = {0}, clid[128] = {0}, ucid[128] = {0}, hostname[128] = {0}, paramlist[256] = {0};
	char *inst = NULL, *p = NULL;
	int id = 0;

	char *hwaddr = (char *)((struct dhcp_lease *)((struct dm_data *)prev_data)->additional_data)->hwaddr;

	while (fgets(line, sizeof(line), f) != NULL) {
		remove_new_line(line);
		sscanf(line, "%23s ", macaddr);

		if (DM_STRNCMP(macaddr, hwaddr, 24) == 0) {
			p = DM_STRSTR(line, "vcid=");
			if (p) {
				sscanf(p, "vcid=%127s ", vcid);
				if (DM_LSTRCMP(vcid, "-") != 0) {

					curr_client_options_args.tag = dmstrdup("60");
					curr_client_options_args.value = dmstrdup(vcid);
					id++;

					curr_data.additional_data = (void *)&curr_client_options_args;

					inst = handle_instance_without_section(dmctx, parent_node, id);

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

			p = DM_STRSTR(line, "clid=");
			if (p) {
				sscanf(p, "clid=%127s ", clid);
				if (DM_LSTRCMP(clid, "-") != 0) {

					curr_client_options_args.tag = dmstrdup("61");
					curr_client_options_args.value = dmstrdup(clid);
					id++;

					curr_data.additional_data = (void *)&curr_client_options_args;

					inst = handle_instance_without_section(dmctx, parent_node, id);

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


			p = DM_STRSTR(line, "ucid=");
			if (p) {
				sscanf(p, "ucid=%127s ", ucid);
				if (DM_LSTRCMP(ucid, "-") != 0) {

					curr_client_options_args.tag = dmstrdup("77");
					curr_client_options_args.value = dmstrdup(ucid);
					id++;

					curr_data.additional_data = (void *)&curr_client_options_args;

					inst = handle_instance_without_section(dmctx, parent_node, id);

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

			p = DM_STRSTR(line, "hostname=");
			if (p) {
				sscanf(p, "hostname=%127s ", hostname);
				if (DM_LSTRCMP(hostname, "-") != 0) {

					curr_client_options_args.tag = dmstrdup("12");
					curr_client_options_args.value = dmstrdup(hostname);
					id++;

					curr_data.additional_data = (void *)&curr_client_options_args;

					inst = handle_instance_without_section(dmctx, parent_node, id);

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

			p = DM_STRSTR(line, "paramlist=");
			if (p) {
				sscanf(p, "paramlist=%255s ", paramlist);
				if (DM_LSTRCMP(paramlist, "-") != 0) {

					curr_client_options_args.tag = dmstrdup("55");
					curr_client_options_args.value = dmstrdup(paramlist);
					id++;

					curr_data.additional_data = (void *)&curr_client_options_args;

					inst = handle_instance_without_section(dmctx, parent_node, id);

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

			break;
		}
	}
	fclose(f);
	return 0;
}

static int browseDHCPv4ClientInst(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_sec_name = NULL;

		dmuci_get_value_by_section_string(curr_data.dmmap_section, "__section_name__", &config_sec_name);

		curr_data.config_section = get_config_section_from_dmmap_section_name(config_sec_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 browseDHCPv4ClientSentOptionInst(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 *client = NULL, *inst = NULL;

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

	uci_path_foreach_option_eq(bbfdm, parent_node->current_object_file, parent_node->obj->obj, "Client", client, 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 browseDHCPv4ClientReqOptionInst(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 *client = NULL, *inst = NULL;

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

	uci_path_foreach_option_eq(bbfdm, parent_node->current_object_file, parent_node->obj->obj, "Client", client, curr_data.dmmap_section) {

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

		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 browseDHCPv4ServerPoolOptionInst(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 browseDHCPv4RelayForwardingInst(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_sec_name = NULL;

		dmuci_get_value_by_section_string(curr_data.dmmap_section, "__section_name__", &config_sec_name);

		curr_data.config_section = get_config_section_from_dmmap_section_name(config_sec_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;
}

/*************************************************************
* ADD & DEL OBJ
**************************************************************/
static int addObjDHCPv4ServerPool(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
	struct dm_data *curr_data = (struct dm_data *)ctx->addobj_instance;
	char dhcp_sname[32] = {0};

	snprintf(dhcp_sname, sizeof(dhcp_sname), "dhcp_%s", *instance);

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

	dmuci_set_value_by_section(curr_data->dmmap_section, "MinAddress", "0.0.0.0");
	dmuci_set_value_by_section(curr_data->dmmap_section, "MaxAddress", "255.255.255.0");
	dmuci_set_value_by_section(curr_data->dmmap_section, "SubnetMask", "255.255.255.255");

	char *inst = NULL;
	create_dmmap_obj(ctx, 0, "DHCPv6", "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", dhcp_sname);
	dmuci_set_value_by_section(new_s, "radv_intf_instance", *instance);
#endif

	return 0;
}

static int delObjDHCPv4ServerPool(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, "DHCPv4", "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, "DHCPv6", "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, "DHCPv6", "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 addObjDHCPv4ServerPoolStaticAddress(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
	struct dm_data *curr_data = (struct dm_data *)ctx->addobj_instance;
	char *dhcp_pool = section_name(((struct dm_data *)data)->config_section);
	char host_name[32];

	snprintf(host_name, sizeof(host_name), "%s_host_%s", dhcp_pool, *instance);

	dmuci_add_section("dhcp", "host", &curr_data->config_section);
	dmuci_rename_section_by_section(curr_data->config_section, host_name);
	dmuci_set_value_by_section(curr_data->config_section, "dhcp_pool", dhcp_pool);
	dmuci_set_value_by_section(curr_data->config_section, "enable", "0");

	return 0;
}

static int delObjDHCPv4ServerPoolStaticAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	dmuci_delete_by_section(((struct dm_data *)data)->config_section, NULL, NULL);
	dmuci_delete_by_section(((struct dm_data *)data)->dmmap_section, NULL, NULL);
	return 0;
}

static int addObjDHCPv4Client(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");
	return 0;
}

static int delObjDHCPv4Client(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	struct uci_section *s = NULL, *stmp = NULL;
	char *client = NULL, *proto = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "proto", &proto);
	if (DM_STRCMP(proto, "dhcp") == 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, "clientid", "");
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "vendorid", "");
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "hostname", "");
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "sendopts", "");
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "reqopts", "");
	}

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

	uci_path_foreach_option_eq_safe(bbfdm, "DHCPv4", "SentOption", "Client", client, stmp, s) {
		dmuci_delete_by_section(s, NULL, NULL);
	}

	uci_path_foreach_option_eq_safe(bbfdm, "DHCPv4", "ReqOption", "Client", client, stmp, s) {
		dmuci_delete_by_section(s, NULL, NULL);
	}

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

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

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

	char *tag = generate_tag_option("DHCPv4", "SentOption", "Client", client, "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);
	return 0;
}

static int delObjDHCPv4ClientSentOption(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 (((struct dm_data *)data)->config_section && DM_STRCMP(proto, "dhcp") == 0) {
		struct option_args *option = (struct option_args *)((struct dm_data *)data)->additional_data;
		char *option_name = get_dhcp_option_name(DM_STRTOL(option->tag));

		if (DM_LSTRCMP(option_name, "sendopts") == 0) {
			char *sendopts = NULL;

			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "sendopts", &sendopts);
			if (DM_STRLEN(sendopts)) {
				sendopts = remove_option_from_str_list(sendopts, DM_STRTOL(option->tag));
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "sendopts", sendopts);
			}
		} else {
			dmuci_set_value_by_section(((struct dm_data *) data)->config_section, option_name, "");
		}
	}

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

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

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

	char *tag = generate_tag_option("DHCPv4", "ReqOption", "Client", client, "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);
	return 0;
}

static int delObjDHCPv4ClientReqOption(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 (((struct dm_data *)data)->config_section && DM_STRCMP(proto, "dhcp") == 0) {
		char *reqopts = NULL;

		dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "reqopts", &reqopts);
		if (DM_STRLEN(reqopts) != 0) {
			struct option_args *option = (struct option_args *)((struct dm_data *)data)->additional_data;
			reqopts = remove_str_from_str_list(reqopts, " ", option->tag);
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "reqopts", reqopts);
		}
	}

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

static int addObjDHCPv4ServerPoolOption(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("DHCPv4", "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, "DHCPv6", "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 delObjDHCPv4ServerPoolOption(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, "DHCPv6", "Option", dmmap_s) {
		char *v6_pool = NULL, *v6_inst = NULL;

		dmuci_get_value_by_section_string(dmmap_s, "Pool", &v6_pool);
		dmuci_get_value_by_section_string(dmmap_s, "__instance__", &v6_inst);

		if (DM_STRCMP(pool, v6_pool) == 0 && DM_STRCMP(inst, v6_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;
}

static int addObjDHCPv4RelayForwarding(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");
	return 0;
}

static int delObjDHCPv4RelayForwarding(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, "relay") == 0) {
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "proto", "none");
	}

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

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

static int set_DHCPv4ServerPool_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, "dhcpv4", b ? "server" : "disabled");
			return 0;
	}
	return 0;
}

static int get_DHCPv4ServerPool_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, "dhcpv4", &status);

	if (DM_LSTRCMP(status, "disabled") == 0) {
		*value = dmstrdup("Disabled");
	} else {
		unsigned iface_addr, iface_cidr, iface_bits, iface_net;

		*value = dmstrdup("Error_Misconfigured");

		if (DM_STRLEN(((struct dhcp_args *)((struct dm_data *)data)->additional_data)->iface_args.iface_name) == 0)
			return 0;

		if (interface_get_ipv4(((struct dhcp_args *)((struct dm_data *)data)->additional_data)->iface_args.iface_name, &iface_addr, &iface_cidr))
			return 0;

		iface_bits = ~((1 << (32 - iface_cidr)) - 1);
		iface_net = ntohl(iface_addr) & iface_bits;

		if (iface_bits != ((struct dhcp_args *)((struct dm_data *)data)->additional_data)->iface_args.bits)
			return 0;

		if (iface_net != (((struct dhcp_args *)((struct dm_data *)data)->additional_data)->iface_args.net_start & iface_bits) || iface_net != (((struct dhcp_args *)((struct dm_data *)data)->additional_data)->iface_args.net_end & iface_bits))
			return 0;

		*value = dmstrdup("Enabled");
	}

	return 0;
}

static int get_DHCPv4ServerPool_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_DHCPv4ServerPool_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_DHCPv4ServerPool_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_DHCPv4ServerPool_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("DHCPv4", ((struct dm_data *)data)->dmmap_section, ((struct dm_data *)data)->config_section, 1, value);
			break;
	}
	return 0;
}

static int get_DHCPv4ServerPool_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", ((struct dhcp_args *)((struct dm_data *)data)->additional_data)->iface_args.iface_name, value);
	return 0;
}

static int set_DHCPv4ServerPool_Interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *iface_name = NULL, *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;

			return 0;
		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);
			iface_name = find_ipv4_interface_up(section_name(iface_sec));
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "interface", iface_name);
	}
	return 0;
}

/*#Device.DHCPv4.Server.Pool.{i}.alloweddevices!UCI:dhcp/interface,@i-1/allowed_devices*/
static int get_DHCPv4ServerPool_AllowedDevices(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *allowed_dev = NULL;

	*value = dmstrdup("All");

	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "allowed_devices", &allowed_dev);
	if (DM_STRLEN(allowed_dev)) {
		if (strcasecmp(allowed_dev, "known") == 0)
			*value = dmstrdup("Known");
		else if(strcasecmp(allowed_dev, "unknown") == 0)
			*value = dmstrdup("UnKnown");
	}

	return 0;
}

static int set_DHCPv4ServerPool_AllowedDevices(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *allowed_devices[] = {"All", "Known", "UnKnown", NULL};
	char allowed_dev[8] = {0};

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, -1, allowed_devices, NULL))
				return FAULT_9007;
			break;
		case VALUESET:
			if (strcasecmp(value, "Known") == 0)
				snprintf(allowed_dev, sizeof(allowed_dev), "%s", "known");
			else if(strcasecmp(value, "UnKnown") == 0)
				snprintf(allowed_dev, sizeof(allowed_dev), "%s", "unknown");
			else
				snprintf(allowed_dev, sizeof(allowed_dev), "%s", "all");

			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "allowed_devices", allowed_dev);
			break;
	}
	return 0;
}

/*#Device.DHCPv4.Server.Pool.{i}.MinAddress!UCI:dhcp/interface,@i-1/start*/
static int get_DHCPv4ServerPool_MinAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "MinAddress", value);
	return 0;
}

static int set_DHCPv4ServerPool_MinAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	unsigned min_addr, min_net, start;
	unsigned max_addr, max_net, limit;
	char buf[32] = {0};
	char *max = NULL;

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 15, NULL, IPv4Address))
				return FAULT_9007;
			break;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "MinAddress", value);

			inet_pton(AF_INET, value, &min_addr);
			min_net = ntohl(min_addr) & ((struct dhcp_args *)((struct dm_data *)data)->additional_data)->iface_args.bits;
			start = ntohl(min_addr) - min_net;

			snprintf(buf, sizeof(buf), "%u", start);
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "start", buf);

			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "MaxAddress", &max);

			inet_pton(AF_INET, max, &max_addr);
			max_net = ntohl(max_addr) & ((struct dhcp_args *)((struct dm_data *)data)->additional_data)->iface_args.bits;
			limit = ntohl(max_addr) - max_net - start + 1;

			snprintf(buf, sizeof(buf), "%u", limit);
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "limit", buf);
			break;
	}
	return 0;
}

/*#Device.DHCPv4.Server.Pool.{i}.MaxAddress!UCI:dhcp/interface,@i-1/limit*/
static int get_DHCPv4ServerPool_MaxAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "MaxAddress", value);
	return 0;
}

static int set_DHCPv4ServerPool_MaxAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	unsigned min_addr, min_net, start;
	unsigned max_addr, max_net, limit;
	char buf[32] = {0};
	char *min = NULL;

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 15, NULL, IPv4Address))
				return FAULT_9007;
			return 0;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "MaxAddress", value);

			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "MinAddress", &min);

			inet_pton(AF_INET, min, &min_addr);
			min_net = ntohl(min_addr) & ((struct dhcp_args *)((struct dm_data *)data)->additional_data)->iface_args.bits;
			start = ntohl(min_addr) - min_net;

			inet_pton(AF_INET, value, &max_addr);
			max_net = ntohl(max_addr) & ((struct dhcp_args *)((struct dm_data *)data)->additional_data)->iface_args.bits;
			limit = ntohl(max_addr) - max_net - start + 1;

			snprintf(buf, sizeof(buf), "%u", limit);
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "limit", buf);
			break;
	}
	return 0;
}

static int get_DHCPv4ServerPool_ReservedAddresses(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct dhcp_iface_args *iface_args = &((struct dhcp_args *)((struct dm_data *)data)->additional_data)->iface_args;
	char *dhcp_pool = section_name(((struct dm_data *)data)->config_section);
	struct uci_section *s = NULL;
	char list_val[512];
	unsigned pos = 0;

	list_val[0] = 0;
	uci_foreach_option_eq("dhcp", "host", "dhcp_pool", dhcp_pool, s) {

		char *host_name = NULL;
		dmuci_get_value_by_section_string(s, "name", &host_name);
		if (DM_LSTRCMP(host_name, "reserved") != 0)
			continue;

		char *ip = NULL;
		dmuci_get_value_by_section_string(s, "ip", &ip);
		if (DM_STRLEN(ip) == 0)
			continue;

		unsigned addr, net;
		inet_pton(AF_INET, ip, &addr);
		net = ntohl(addr) & iface_args->bits;

		if (net >= iface_args->net_start && net <= iface_args->net_end)
			pos += snprintf(&list_val[pos], sizeof(list_val) - pos, "%s,", ip);
	}

	/* cut tailing ',' */
	if (pos)
		list_val[pos - 1] = 0;

	*value = dmstrdup(list_val);
	return 0;
}

static int set_DHCPv4ServerPool_ReservedAddresses(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct uci_section *s = NULL, *stmp = NULL;
	char *local_value, *pch, *spch = NULL;
	char *dhcp_pool = section_name(((struct dm_data *)data)->config_section);

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string_list(ctx, value, -1, 32, -1, -1, 15, NULL, IPv4Address))
				return FAULT_9007;

			local_value = dmstrdup(value);
			for (pch = strtok_r(local_value, ",", &spch); pch != NULL; pch = strtok_r(NULL, ",", &spch)) {

				// Check if ip address is out dhcp pool
				if (check_ipv4_in_dhcp_pool(&((struct dhcp_args *)((struct dm_data *)data)->additional_data)->iface_args, pch)) {
					dmfree(local_value);
					return FAULT_9007;
				}
			}
			dmfree(local_value);

			return 0;
		case VALUESET:
			uci_foreach_option_eq_safe("dhcp", "host", "dhcp_pool", dhcp_pool, stmp, s) {

				char *host_name = NULL;
				dmuci_get_value_by_section_string(s, "name", &host_name);
				if (DM_LSTRCMP(host_name, "reserved") != 0)
					continue;

				char *ip = NULL;
				dmuci_get_value_by_section_string(s, "ip", &ip);
				if (DM_STRLEN(ip) == 0)
					continue;

				// Check if ip exists in the list value : yes -> skip it else delete it
				if (!DM_STRSTR(value, ip))
					dmuci_delete_by_section(s, NULL, NULL);
			}

			local_value = dmstrdup(value);
			for (pch = strtok_r(local_value, ",", &spch); pch != NULL; pch = strtok_r(NULL, ",", &spch)) {

				// Check if host exists
				bool host_exist = check_dhcp_host_option_exists(dhcp_pool, "ip", pch);

				// host exists -> skip it
				if (host_exist)
					continue;

				// host doesn't exist -> create an new one
				struct uci_section *dhcp_host_section = NULL;
				dmuci_add_section("dhcp", "host", &dhcp_host_section);
				dmuci_set_value_by_section(dhcp_host_section, "name", "reserved");
				dmuci_set_value_by_section(dhcp_host_section, "dhcp_pool", dhcp_pool);
				dmuci_set_value_by_section(dhcp_host_section, "ip", pch);
			}
			dmfree(local_value);
			return 0;
	}
	return 0;
}

/*#Device.DHCPv4.Server.Pool.{i}.SubnetMask!UCI:dhcp/interface,@i-1/netmask*/
static int get_DHCPv4ServerPool_SubnetMask(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "SubnetMask", value);
	return 0;
}

static int set_DHCPv4ServerPool_SubnetMask(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	unsigned min_addr, min_net, start;
	unsigned max_addr, max_net, limit;
	char buf[32] = {0};
	char *min = NULL, *max = NULL;

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 15, NULL, IPv4Address))
				return FAULT_9007;
			return 0;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "SubnetMask", value);

			((struct dhcp_args *)((struct dm_data *)data)->additional_data)->iface_args.bits = ~((1 << (32 - netmask2cidr(value))) - 1);

			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "MinAddress", &min);

			inet_pton(AF_INET, min, &min_addr);
			min_net = ntohl(min_addr) & ((struct dhcp_args *)((struct dm_data *)data)->additional_data)->iface_args.bits;
			start = ntohl(min_addr) - min_net;

			snprintf(buf, sizeof(buf), "%u", start);
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "start", buf);

			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "MaxAddress", &max);

			inet_pton(AF_INET, max, &max_addr);
			max_net = ntohl(max_addr) & ((struct dhcp_args *)((struct dm_data *)data)->additional_data)->iface_args.bits;
			limit = ntohl(max_addr) - max_net - start + 1;

			snprintf(buf, sizeof(buf), "%u", limit);
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "limit", buf);
			return 0;
	}
	return 0;
}

/*#Device.DHCPv4.Server.Pool.{i}.DNSServers!UBUS:network.interface/status/interface,@Name/dns-server*/
static int get_DHCPv4ServerPool_DNSServers(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	if (!get_DHCPv4ServerPool_Option_Value(((struct dm_data *)data)->config_section, "6", value))
		return 0;

	dmuci_get_option_value_string("network", ((struct dhcp_args *)((struct dm_data *)data)->additional_data)->iface_args.iface_name, "ipaddr", value);
	return 0;
}

static int set_DHCPv4ServerPool_DNSServers(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string_list(ctx, value, -1, 4, -1, -1, 15, NULL, IPv4Address))
				return FAULT_9007;
			return 0;
		case VALUESET:
			set_DHCPv4ServerPool_Option_Value(data, "6", value);
			return 0;
	}
	return 0;
}

static int get_DHCPv4ServerPool_DomainName(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	if (!get_DHCPv4ServerPool_Option_Value(((struct dm_data *)data)->config_section, "15", value))
		return 0;

	char *sec = get_dnsmasq_section_name();
	dmuci_get_option_value_string("dhcp", sec, "domain", value);
	return 0;
}

static int set_DHCPv4ServerPool_DomainName(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 64, NULL, NULL))
				return FAULT_9007;
			return 0;
		case VALUESET:
			set_DHCPv4ServerPool_Option_Value(data, "15", value);
			return 0;
	}
	return 0;
}

static int get_DHCPv4ServerPool_IPRouters(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	if (!get_DHCPv4ServerPool_Option_Value(((struct dm_data *)data)->config_section, "3", value))
		return 0;

	dmuci_get_option_value_string("network", ((struct dhcp_args *)((struct dm_data *)data)->additional_data)->iface_args.iface_name, "ipaddr", value);
	return 0;
}

static int set_DHCPv4ServerPool_IPRouters(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string_list(ctx, value, -1, 4, -1, -1, 15, NULL, IPv4Address))
				return FAULT_9007;
			return 0;
		case VALUESET:
			set_DHCPv4ServerPool_Option_Value(data, "3", value);
			return 0;
	}
	return 0;
}

/*#Device.DHCPv4.Server.Pool.{i}.LeaseTime!UCI:dhcp/interface,@i-1/leasetime*/
static int get_DHCPv4ServerPool_LeaseTime(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *ltime = NULL, *pch = NULL, *pchr = NULL;
	int leasetime = 0;

	*value = dmstrdup("-1");
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "leasetime", &ltime);
	if (ltime == NULL || *ltime == '\0')
		return 0;

	if (DM_STRCHR(ltime, 'd')) {
		pch = strtok_r(ltime, "d", &pchr);
		leasetime = DM_STRTOL(pch) * 24 * 3600;
	} else if (DM_STRCHR(ltime, 'h')) {
		pch = strtok_r(ltime, "h", &pchr);
		leasetime = DM_STRTOL(pch) * 3600;
	} else if (DM_STRCHR(ltime, 'm')) {
		pch = strtok_r(ltime, "m", &pchr);
		leasetime = DM_STRTOL(pch) * 60;
	} else {
		pch = strtok_r(ltime, "s", &pchr);
		leasetime = DM_STRTOL(pch);
	}

	dmasprintf(value, "%d", leasetime);
	return 0;
}

static int set_DHCPv4ServerPool_LeaseTime(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	int leasetime;
	char buf[32];

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_int(ctx, value, RANGE_ARGS{{"-1",NULL}}, 1))
				return FAULT_9007;
			return 0;
		case VALUESET:
			leasetime = DM_STRTOL(value);
			if (leasetime == -1)
				buf[0] = '\0';
			else
				snprintf(buf, sizeof(buf), "%ds", leasetime);

			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "leasetime", buf);
			return 0;
	}
	return 0;
}

/*#Device.DHCPv4.Server.Pool.{i}.StaticAddressNumberOfEntries!UCI:dhcp/host/*/
static int get_DHCPv4ServerPool_StaticAddressNumberOfEntries(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_DHCPv4ServerPool_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_DHCPv4ServerPool_ClientNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmasprintf(value, "%u", ((struct dhcp_args *)((struct dm_data *)data)->additional_data)->n_leases);
	return 0;
}

/*#Device.DHCPv4.Server.Pool.{i}.StaticAddress.{i}.Enable!UCI:dhcp/host,@i-1/enable*/
static int get_DHCPv4ServerPoolStaticAddress_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "enable", "1");
	return 0;
}

static int set_DHCPv4ServerPoolStaticAddress_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, "enable", b ? "1" : "0");
			return 0;
	}
	return 0;
}

static int get_DHCPv4ServerPoolStaticAddress_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_DHCPv4ServerPoolStaticAddress_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);
}

/*#Device.DHCPv4.Server.Pool.{i}.StaticAddress.{i}.Chaddr!UCI:dhcp/host,@i-1/mac*/
static int get_DHCPv4ServerPoolStaticAddress_Chaddr(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "mac", value);
	return 0;
}

static int set_DHCPv4ServerPoolStaticAddress_Chaddr(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *dhcp_pool = NULL, *orig = NULL;
	char mac[19] = {0};

	_str_tolower(value, mac);
	switch (action) {
		case VALUECHECK:
			// Validate value string -> MAC Address
			if (bbfdm_validate_string(ctx, value, -1, 17, NULL, MACAddress))
				return FAULT_9007;

			// Check if same mac as applied
			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "mac", &orig);
			if (DM_STRCMP(orig, mac) == 0)
				return 0;

			// Check if mac exists
			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "dhcp_pool", &dhcp_pool);
			if (check_dhcp_host_option_exists(dhcp_pool, "mac", mac)) {
				bbfdm_set_fault_message(ctx, "MAC address conflicts with existing entries");
				return FAULT_9007;
			}

			return 0;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "mac", mac);
			return 0;
	}
	return 0;
}

/*#Device.DHCPv4.Server.Pool.{i}.StaticAddress.{i}.Yiaddr!UCI:dhcp/host,@i-1/ip*/
static int get_DHCPv4ServerPoolStaticAddress_Yiaddr(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "ip", value);
	return 0;
}

static int set_DHCPv4ServerPoolStaticAddress_Yiaddr(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *dhcp_pool = NULL;

	switch (action) {
		case VALUECHECK:
			// Validate value string -> IPv4 Address
			if (bbfdm_validate_string(ctx, value, -1, 15, NULL, IPv4Address))
				return FAULT_9007;

			// Check if ip exists
			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "dhcp_pool", &dhcp_pool);
			if (check_dhcp_host_option_exists(dhcp_pool, "ip", value)) {
				bbfdm_set_fault_message(ctx, "IP address conflicts with existing entries");
				return FAULT_9007;
			}

			return 0;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "ip", value);
			return 0;
	}
	return 0;
}

#ifdef DHCPMNGR_ENABLE_VENDOR_EXT
static int get_DHCPv4ServerPoolStaticAddress_name(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "name", value);
	return 0;
}

static int set_DHCPv4ServerPoolStaticAddress_name(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *dhcp_pool = NULL;

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

			// Check if name exists
			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "dhcp_pool", &dhcp_pool);
			if (check_dhcp_host_option_exists(dhcp_pool, "name", value)) {
				bbfdm_set_fault_message(ctx, "Name %s already used", value);
				return FAULT_9007;
			}

			return 0;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "name", value);
			return 0;
	}
	return 0;
}
#endif

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

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

static int get_DHCPv4ServerPoolClient_Chaddr(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = (char *)((struct dhcp_lease *)((struct dm_data *)data)->additional_data)->hwaddr;
	return 0;
}

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

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

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

static int get_DHCPv4ServerPoolClientIPv4Address_LeaseTimeRemaining(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return dm_time_utc_format(((struct dhcp_lease *)((struct dm_data *)data)->additional_data)->ts, value);
}

static int get_DHCPv4ServerPoolClientIPv4Address_IPAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = (char *)((struct dhcp_lease *)((struct dm_data *)data)->additional_data)->ipaddr;
	return 0;
}

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

static int get_DHCPv4ServerPoolClientOption_Value(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	const char *tag_option = ((struct option_args *)((struct dm_data *)data)->additional_data)->tag;
	const char *tag_value = ((struct option_args *)((struct dm_data *)data)->additional_data)->value;
	char hex[256] = {0};

	if (DM_STRLEN(tag_option) && DM_STRLEN(tag_value))
		dhcpmngr_convert_str_option_to_hex(DM_STRTOL(tag_option), tag_value, hex, sizeof(hex));

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

static int get_DHCPv4_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.DHCPv4.Client.{i}.Enable!UCI:network/interface,@i-1/disabled*/
static int get_DHCPv4Client_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, "dhcp") != 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_DHCPv4Client_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;
				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, "clientid", "");
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "vendorid", "");
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "hostname", "");
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "sendopts", "");
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "reqopts", "");

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

					return 0;
				}

				char *client = NULL;

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

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

				// Added the enabled options for sendopts
				create_dhcp_sent_option_list(((struct dm_data *)data)->config_section, client);

				// Added the enabled options for reqopts
				create_dhcp_req_option_list(((struct dm_data *)data)->config_section, client);
			}
			return 0;
	}
	return 0;
}

static int get_DHCPv4Client_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_DHCPv4Client_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_DHCPv4Client_Interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	get_dhcp_client_relay_interface(data, value);
	return 0;
}

static int set_DHCPv4Client_Interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *client = NULL, *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, "DHCPv4", "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, "dhcp") == 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, "clientid", "");
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "vendorid", "");
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "hostname", "");
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "sendopts", "");
				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, "dhcpv6") == 0) {
				// There is a DHCPv6 Client map to this interface section, therefore create a new interface section
				char *curr_device = NULL;
				char buf[64] = {0};

				snprintf(buf, sizeof(buf), "%s4", 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) {
				// Update proto option of config section
				dmuci_set_value_by_section(iface_sec, "proto", "dhcp");

				dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "__instance__", &client);
				if (DM_STRLEN(client) != 0) {
					// Added the enabled options for sendopts
					create_dhcp_sent_option_list(iface_sec, client);

					// Added the enabled options for reqopts
					create_dhcp_req_option_list(iface_sec, client);
				}
			}

			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.DHCPv4.Client.{i}.Status!UCI:network/interface,@i-1/disabled*/
static int get_DHCPv4Client_Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	bool b = false;
	char *enabled = NULL;

	get_DHCPv4Client_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.DHCPv4.Client.{i}.DHCPStatus!UBUS:network.interface/status/interface,@Name/ipv4-address[@i-1].address*/
static int get_DHCPv4Client_DHCPStatus(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *proto = NULL;
	struct uci_section *dhcpv4_s = ((struct dm_data *)data)->config_section;

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

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

		char *if_name = section_name(dhcpv4_s);
		dhcpmngr_get_interface_status(if_name, &res);
		DM_ASSERT(res, *value = dmstrdup("Requesting"));
		json_object *jobj = dmjson_select_obj_in_array_idx(res, 0, 1, "ipv4-address");
		char *ipaddr = dmjson_get_value(jobj, 1, "address");
		*value = (ipaddr[0] == '\0') ? dmstrdup("Requesting") : dmstrdup("Bound");
	} else {
		*value = dmstrdup("Requesting");
	}
	return 0;
}

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

static int set_DHCPv4Client_Renew(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct uci_section *dhcpv4_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(dhcpv4_s, "proto", &proto);
			if (dhcpv4_s && DM_STRCMP(proto, "dhcp") == 0) {
				char *if_name = section_name(dhcpv4_s);
				dmubus_call_set("network.interface", "renew", UBUS_ARGS{{"interface", if_name, String}}, 1);
			}
			break;
	}
	return 0;
}

static int get_DHCPv4Client_IPAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *dhcpv4_s = ((struct dm_data *)data)->config_section;
	char *ipaddr = NULL;
	char *proto = NULL;

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

	if (dhcpv4_s && DM_STRCMP(proto, "dhcp") == 0) {
		dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "ipaddr", &ipaddr);
		if (!ipaddr || *ipaddr == 0) {
			json_object *res = NULL;

			char *if_name = section_name(dhcpv4_s);
			dhcpmngr_get_interface_status(if_name, &res);
			DM_ASSERT(res, *value = dmstrdup(""));

			json_object *jobj = dmjson_select_obj_in_array_idx(res, 0, 1, "ipv4-address");
			ipaddr = dmjson_get_value(jobj, 1, "address");
		}
	}

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

static int get_DHCPv4Client_SubnetMask(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *dhcpv4_s = ((struct dm_data *)data)->config_section;
	char *mask = NULL;
	char *proto = NULL;

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

	if (dhcpv4_s && DM_STRCMP(proto, "dhcp") == 0) {
		dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "netmask", &mask);
		if (!mask || *mask == 0) {
			json_object *res = NULL;

			char *if_name = section_name(dhcpv4_s);
			dhcpmngr_get_interface_status(if_name, &res);
			DM_ASSERT(res, *value = dmstrdup(""));

			json_object *jobj = dmjson_select_obj_in_array_idx(res, 0, 1, "ipv4-address");
			mask = dmjson_get_value(jobj, 1, "mask");
			mask = (mask && *mask) ? cidr2netmask(DM_STRTOL(mask)) : "";
		}
	}

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

/*#Device.DHCPv4.Client.{i}.IPRouters!UBUS:network.interface/status/interface,@Name/route[@i-1].nexthop*/
static int get_DHCPv4Client_IPRouters(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *dhcpv4_s = ((struct dm_data *)data)->config_section;
	char *proto = NULL;

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

	if (dhcpv4_s && DM_STRCMP(proto, "dhcp") == 0) {
		json_object *res = NULL, *route = NULL, *arrobj = NULL;
		unsigned pos = 0, idx = 0;
		char list_ip[256] = {0};

		char *if_name = section_name(dhcpv4_s);
		dhcpmngr_get_interface_status(if_name, &res);
		DM_ASSERT(res, *value = dmstrdup(""));

		list_ip[0] = 0;
		dmjson_foreach_obj_in_array(res, arrobj, route, idx, 1, "route") {
			char *nexthop = dmjson_get_value(route, 1, "nexthop");
			pos += snprintf(&list_ip[pos], sizeof(list_ip) - pos, "%s,", nexthop);
		}

		/* cut tailing ',' */
		if (pos)
			list_ip[pos - 1] = 0;

		*value = dmstrdup(list_ip);
	}
	return 0;
}

/*#Device.DHCPv4.Client.{i}.DNSServers!UBUS:network.interface/status/interface,@Name/dns-server*/
static int get_DHCPv4Client_DNSServers(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *dhcpv4_s = ((struct dm_data *)data)->config_section;
	char *proto = NULL;

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

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

		char *if_name = section_name(dhcpv4_s);
		dhcpmngr_get_interface_status(if_name, &res);
		DM_ASSERT(res, *value = dmstrdup(""));
		*value = dmjson_get_value_array_all(res, ",", 1, "dns-server");
	}
	return 0;
}

/* get remaining lease time for an interface */
static void get_lease_time_remaining(const char *ifname, char **value)
{
	/* assume wrong unless we find appropriate value */
	*value = dmstrdup("-1");

	if (DM_STRLEN(ifname)) {
		// cppcheck-suppress cert-MSC24-C
		FILE *fp = fopen(DHCP_CLIENT_INFO, "r");

		if (fp != NULL) {
			char line[5120] = {0};

			/* loop over lines, till we get our ifname  */
			while (fgets(line, sizeof(line), fp) != NULL) {
				char *line_ifname = NULL;
				char *lease_time = NULL;
				char *lease_start_time = NULL;
				char *spch = NULL;

				/* delimiter is space */
				/* get the first word, which is the ifname */
				line_ifname = strtok_r(line, " ", &spch);

				/* if it matches our interface, then get the leasetime and lease start time */
				if (!DM_STRCMP(line_ifname, ifname)) {
					lease_time = strtok_r(NULL, " ", &spch);
					if (!DM_STRLEN(lease_time) || DM_STRTOL(lease_time) == 0xFFFFFFFF)
						break;

					lease_start_time = strtok_r(NULL, " ", &spch);
					if (!DM_STRLEN(lease_start_time))
						break;

					char *uptime = get_uptime();

					/* remaining = lease_time - (uptime - lease_start_time) */
					dmasprintf(value, "%ld", DM_STRTOL(lease_time) - (DM_STRTOL(uptime) -  DM_STRTOL(lease_start_time)));
					break;
				}
			}
			fclose(fp);
		}
	}
}

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

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

	if (dhcpv4_s && DM_STRCMP(proto, "dhcp") == 0) {
		char *if_name = section_name(dhcpv4_s);
		/* get remaining lease time in value */
		get_lease_time_remaining(if_name, value);
	}
	return 0;
}

/*#Device.DHCPv4.Client.{i}.SentOptionNumberOfEntries!UCI:network/interface,@i-1/sendopts*/
static int get_DHCPv4Client_SentOptionNumberOfEntries(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.DHCPv4.Client.{i}.ReqOptionNumberOfEntries!UCI:network/interface,@i-1/reqopts*/
static int get_DHCPv4Client_ReqOptionNumberOfEntries(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_DHCPv4ClientSentOption_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_DHCPv4ClientSentOption_Enable(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;
	char *option_name = NULL, *proto = NULL;
	bool b;

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

			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "proto", &proto);
			if (DM_STRCMP(proto, "dhcp") == 0 && DM_STRLEN(option->value) != 0) {
				option_name = get_dhcp_option_name(DM_STRTOL(option->tag));

				if (DM_LSTRCMP(option_name, "sendopts") == 0) {
					char *sendopts = NULL;

					dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "sendopts", &sendopts);
					sendopts = remove_option_from_str_list(sendopts, DM_STRTOL(option->tag));

					if (b) {
						char tag_value[128] = {0};

						snprintf(tag_value, sizeof(tag_value), "%s:%s", option->tag, option->value);
						sendopts = add_str_to_str_list(sendopts, " ", tag_value);
					}

					dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "sendopts", sendopts);
				} else {
					char str[256] = {0};

					convert_hex_to_string(option->value, str, sizeof(str));
					dmuci_set_value_by_section(((struct dm_data *)data)->config_section, option_name, b ? (DM_STRTOL(option->tag) == DHCP_OPTION_CLIENTID) ? option->value : str : "");
				}
			}

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

static int get_DHCPv4ClientSentOption_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_DHCPv4ClientSentOption_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_DHCPv4ClientSentOption_Tag(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = ((struct option_args *)((struct dm_data *)data)->additional_data)->tag;
	return 0;
}

static int set_DHCPv4ClientSentOption_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;
	char *client = NULL;
	bool b = false;
	char *proto = NULL;

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

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

			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "Client", &client);

			if (tag_option_exists("DHCPv4", "SentOption", "Client", client, "option_tag", value))
				return FAULT_9007;

			break;
		case VALUESET:
			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "proto", &proto);
			if (DM_STRCMP(proto, "dhcp") == 0 && DM_STRLEN(option->value) != 0) {
				char *enable = NULL;

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

				if (b == true) {
					char *option_name = NULL;
					char *sendopts = NULL;
					char str[256] = {0};

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

					option_name = get_dhcp_option_name(DM_STRTOL(option->tag));
					if (DM_LSTRCMP(option_name, "sendopts") == 0) {
						sendopts = remove_option_from_str_list(sendopts, DM_STRTOL(option->tag));
						dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "sendopts", sendopts);
					} else {
						dmuci_set_value_by_section(((struct dm_data *)data)->config_section, option_name, "");
					}

					// Add the new option
					option_name = get_dhcp_option_name(DM_STRTOL(value));
					if (DM_LSTRCMP(option_name, "sendopts") == 0) {
						snprintf(str, sizeof(str), "%s:%s", value, option->value);
						sendopts = add_str_to_str_list(sendopts, " ", str);
						dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "sendopts", sendopts);
					} else {
						convert_hex_to_string(option->value, str, sizeof(str));
						dmuci_set_value_by_section(((struct dm_data *)data)->config_section, option_name, (DM_STRTOL(value) == DHCP_OPTION_CLIENTID) ? option->value : str);
					}
				}
			}

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

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

static int set_DHCPv4ClientSentOption_Value(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;
	bool b = false;
	char *proto = NULL;

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_hexBinary(ctx, value, RANGE_ARGS{{"0","255"}}, 1))
				return FAULT_9007;
			break;
		case VALUESET:
			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "proto", &proto);

			if (DM_STRCMP(proto, "dhcp") == 0) {
				char *enable = NULL;

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

				if (b == true) {
					char *option_name = NULL;
					char *sendopts = NULL;
					char str[256] = {0};

					dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "sendopts", &sendopts);
					option_name = get_dhcp_option_name(DM_STRTOL(option->tag));

					// Remove the old option
					if (DM_LSTRCMP(option_name, "sendopts") == 0) {
						sendopts = remove_option_from_str_list(sendopts, DM_STRTOL(option->tag));
						dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "sendopts", sendopts);
					} else {
						dmuci_set_value_by_section(((struct dm_data *)data)->config_section, option_name, "");
					}

					if (DM_STRLEN(value) != 0) {
						// Add the new option
						if (DM_LSTRCMP(option_name, "sendopts") == 0) {
							snprintf(str, sizeof(str), "%s:%s", option->tag, value);
							sendopts = add_str_to_str_list(sendopts, " ", str);
							dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "sendopts", sendopts);
						} else {
							convert_hex_to_string(value, str, sizeof(str));
							dmuci_set_value_by_section(((struct dm_data *)data)->config_section, option_name, (DM_STRTOL(option->tag) == DHCP_OPTION_CLIENTID) ? value : str);
						}
					}
				}
			}

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

static int get_DHCPv4ClientReqOption_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_DHCPv4ClientReqOption_Enable(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;
	char *reqopts = NULL, *proto = NULL;
	bool b;

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			break;
		case VALUESET:
			string_to_bool(value, &b);
			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "proto", &proto);

			if (DM_STRCMP(proto, "dhcp") == 0) {
				dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "reqopts", &reqopts);
				if (b) {
					if (!value_exits_in_str_list(reqopts, " ", option->tag)) {
						reqopts = add_str_to_str_list(reqopts, " ", option->tag);
						dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "reqopts", reqopts);
					}
				} else {
					reqopts = remove_str_from_str_list(reqopts, " ", option->tag);
					dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "reqopts", reqopts);
				}
			}

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

static int get_DHCPv4ClientReqOption_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_DHCPv4ClientReqOption_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_DHCPv4ClientReqOption_Tag(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = ((struct option_args *)((struct dm_data *)data)->additional_data)->tag;
	return 0;
}

static int set_DHCPv4ClientReqOption_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;
	char *client = NULL, *proto = NULL;
	bool b = false;

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

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

			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "Client", &client);

			if (tag_option_exists("DHCPv4", "ReqOption", "Client", client, "option_tag", value))
				return FAULT_9007;

			break;
		case VALUESET:
			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "proto", &proto);
			if (DM_STRCMP(proto, "dhcp") == 0) {
				char *reqopts = NULL;
				char *enable = NULL;

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

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

				if (value_exits_in_str_list(reqopts, " ", option->tag)) {
					reqopts = remove_str_from_str_list(reqopts, " ", option->tag);
				}

				if (b == true) {
					reqopts = add_str_to_str_list(reqopts, " ", value);
					dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "reqopts", reqopts);
				}
			}

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

/*#Device.DHCPv4.Server.Enable!UCI:dhcp/dnsmasq,@dnsmasq[0]/dhcpv4server*/
static int get_DHCPv4Server_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmstrdup("0");

	char *sec = get_dnsmasq_section_name();
	// if backend is unbound then dnsmasq will not be present
	// and dnsmasq uci section will also not be present
	// in that case odhcpd is handling dhcpv4 too
	// so fallback to check odhcpd section in that case
	if (DM_STRLEN(sec) == 0) {
		// dnsmasq was not found, see if odhcpd is handling dhcpv4
		if (is_odhcpd_maindhcp()) {
			char *dhcpv4_server = NULL;
			dhcpv4_server = dmuci_get_option_value_fallback_def("dhcp", "lan", "dhcpv4", "");
			// if mode is server
			if (DM_STRNCMP(dhcpv4_server, "server", 6) == 0) {
				*value = dmstrdup("1");
			}
		}
	} else {
		*value = dmuci_get_option_value_fallback_def("dhcp", sec, "dhcpv4server", "1");
	}

	return 0;
}

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

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			return 0;
		case VALUESET:
			string_to_bool(value, &b);
			sec = get_dnsmasq_section_name();
			// if backend is unbound then dnsmasq will not be present
			// and dnsmasq uci section will also not be present
			// in that case odhcpd is handling dhcpv4 too
			// so fallback to check odhcpd section in that case
			if (DM_STRLEN(sec) == 0) {
				// dnsmasq was not found, see if odhcpd is handling dhcpv4
				if (is_odhcpd_maindhcp()) {
					// set the value
					dmuci_set_value("dhcp", "lan", "dhcpv4", b ? "server" : "disabled");
				}
			} else {
				dmuci_set_value("dhcp", sec, "dhcpv4server", b ? "1" : "0");
			}

			return 0;
	}
	return 0;
}

static int get_DHCPv4Server_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;
}

static int get_DHCPv4ServerPoolOption_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_DHCPv4ServerPoolOption_Enable(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;
	char opt_value[128] = {0};
	struct uci_list *option_list = NULL;
	bool b;

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

			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;
						}
					}
				}

				if(b) {
					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, "DHCPv6", "Option", dmmap_s) {
				char *v6_pool = NULL, *v6_inst = NULL;

				dmuci_get_value_by_section_string(dmmap_s, "Pool", &v6_pool);
				dmuci_get_value_by_section_string(dmmap_s, "__instance__", &v6_inst);

				if (DM_STRCMP(pool, v6_pool) == 0 && DM_STRCMP(inst, v6_inst) == 0) {
					dmuci_set_value_by_section_bbfdm(dmmap_s, "enable", b ? "1" : "0");
					break;
				}
			}

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

static int get_DHCPv4ServerPoolOption_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_DHCPv4ServerPoolOption_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_DHCPv4ServerPoolOption_Tag(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = ((struct option_args *)((struct dm_data *)data)->additional_data)->tag;
	return 0;
}

static int set_DHCPv4ServerPoolOption_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 option_enabled = false;
	char *pool = NULL, *conf_enable = NULL;
	char new_tag_value[512] = {0};

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{"1","254"}}, 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("DHCPv4", "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, &option_enabled);

				if (option_enabled) {
					char res[256] = {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, "DHCPv6", "Option", dmmap_s) {
				char *v6_pool = NULL, *v6_inst = NULL;

				dmuci_get_value_by_section_string(dmmap_s, "Pool", &v6_pool);
				dmuci_get_value_by_section_string(dmmap_s, "__instance__", &v6_inst);

				if (DM_STRCMP(pool, v6_pool) == 0 && DM_STRCMP(inst, v6_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_DHCPv4ServerPoolOption_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_DHCPv4ServerPoolOption_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","255"}}, 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, "DHCPv6", "Option", dmmap_s) {
				char *v6_pool = NULL, *v6_inst = NULL;

				dmuci_get_value_by_section_string(dmmap_s, "Pool", &v6_pool);
				dmuci_get_value_by_section_string(dmmap_s, "__instance__", &v6_inst);

				if (DM_STRCMP(pool, v6_pool) == 0 && DM_STRCMP(inst, v6_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;
}

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

static int set_DHCPv4Relay_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/relayd", 1, b ? "enable" : "disable");
			break;
	}
	return 0;
}

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

static int get_DHCPv4Relay_ForwardingNumberOfEntries(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.DHCPv4.Relay.Forwarding.{i}.Enable!UCI:network/interface,@i-1/disabled*/
static int get_DHCPv4RelayForwarding_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, "relay") != 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_DHCPv4RelayForwarding_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;
				dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "proto", &proto);

				if (b == false) {
					if (DM_STRCMP(proto, "relay") == 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", "relay");
			}
			return 0;
	}
	return 0;
}

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

	get_DHCPv4RelayForwarding_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;
}

static int get_DHCPv4RelayForwarding_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_DHCPv4RelayForwarding_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_DHCPv4RelayForwarding_Interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	get_dhcp_client_relay_interface(data, value);
	return 0;
}

static int set_DHCPv4RelayForwarding_Interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *device = NULL, *if_sec_name = NULL, *fwd_dev = NULL, *disable = NULL, *proto = NULL;
	bool enable = false;
	struct uci_section *iface_sec = NULL, *fwd_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, "DHCPv4", "Forwarding", fwd_sec) {
				dmuci_get_value_by_section_string(fwd_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", &fwd_dev);
				if (DM_STRCMP(fwd_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, "relay") == 0) {
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "proto", "none");
			}

			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;
			}

			if (enable == true) {
				// Update proto option of config section
				dmuci_set_value_by_section(iface_sec, "proto", "relay");
			}

			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.DHCPv4.Relay.Forwarding.{i}.VendorClassID!UCI:network/interface,@i-1/vendorclass*/
static int get_DHCPv4RelayForwarding_VendorClassID(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *dhcp_relay_s = ((struct dm_data *)data)->config_section;

	if (dhcp_relay_s) {
		char *relay_network = NULL;

		dmuci_get_value_by_section_string(dhcp_relay_s, "network", &relay_network);
		char *dhcp_network = (DM_STRLEN(relay_network)) ? get_dhcp_network_from_relay_list(relay_network) : NULL;
		struct uci_section *ven_class_s = (DM_STRLEN(dhcp_network)) ? get_dhcp_classifier("vendorclass", dhcp_network) : NULL;

		if (ven_class_s)
			dmuci_get_value_by_section_string(ven_class_s, "vendorclass", value);
	}
	return 0;
}

static int set_DHCPv4RelayForwarding_VendorClassID(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct uci_section *dhcp_relay_s = ((struct dm_data *)data)->config_section;

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 255, NULL, NULL))
				return FAULT_9007;
			break;
		case VALUESET:
			if (dhcp_relay_s) {
				char *relay_network = NULL;

				dmuci_get_value_by_section_string(dhcp_relay_s, "network", &relay_network);
				char *dhcp_network = (DM_STRLEN(relay_network)) ? get_dhcp_network_from_relay_list(relay_network) : NULL;
				struct uci_section *ven_class_s = (DM_STRLEN(dhcp_network)) ? get_dhcp_classifier("vendorclass", dhcp_network) : NULL;

				if (ven_class_s)
					dmuci_set_value_by_section(ven_class_s, "vendorclass", value);
			}
			break;
	}
	return 0;
}

/*#Device.DHCPv4.Relay.Forwarding.{i}.Chaddr!UCI:network/interface,@i-1/mac*/
static int get_DHCPv4RelayForwarding_Chaddr(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *dhcp_relay_s = ((struct dm_data *)data)->config_section;

	if (dhcp_relay_s) {
		char *relay_network = NULL;

		dmuci_get_value_by_section_string(dhcp_relay_s, "network", &relay_network);
		char *dhcp_network = (DM_STRLEN(relay_network)) ? get_dhcp_network_from_relay_list(relay_network) : NULL;
		struct uci_section *mac_s = (DM_STRLEN(dhcp_network)) ? get_dhcp_classifier("mac", dhcp_network) : NULL;

		if (mac_s)
			dmuci_get_value_by_section_string(mac_s, "mac", value);
	}
	return 0;
}

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

/*#Device.DHCPv4.Relay.Forwarding.{i}.UserClassID!UCI:network/interface,@i-1/userclass*/
static int get_DHCPv4RelayForwarding_UserClassID(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *dhcp_relay_s = ((struct dm_data *)data)->config_section;

	if (dhcp_relay_s) {
		char *relay_network = NULL;

		dmuci_get_value_by_section_string(dhcp_relay_s, "network", &relay_network);
		char *dhcp_network = (DM_STRLEN(relay_network)) ? get_dhcp_network_from_relay_list(relay_network) : NULL;
		struct uci_section *user_class_s = (DM_STRLEN(dhcp_network)) ? get_dhcp_classifier("userclass", dhcp_network) : NULL;

		if (user_class_s) {
			char hex[256] = {0}, *ucid = NULL;

			dmuci_get_value_by_section_string(user_class_s, "userclass", &ucid);

			if (DM_STRLEN(ucid))
				dhcpmngr_convert_str_option_to_hex(77, ucid, hex, sizeof(hex));

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

static int set_DHCPv4RelayForwarding_UserClassID(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct uci_section *dhcp_relay_s = ((struct dm_data *)data)->config_section;


	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_hexBinary(ctx, value, RANGE_ARGS{{NULL,"255"}}, 1))
				return FAULT_9007;
			break;
		case VALUESET:
			if (dhcp_relay_s) {
				char *relay_network = NULL;

				dmuci_get_value_by_section_string(dhcp_relay_s, "network", &relay_network);
				char *dhcp_network = (DM_STRLEN(relay_network)) ? get_dhcp_network_from_relay_list(relay_network) : NULL;
				struct uci_section *user_class_s = (DM_STRLEN(dhcp_network)) ? get_dhcp_classifier("userclass", dhcp_network) : NULL;

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

					dhcpmngr_convert_hex_option_to_string(77, value, res, sizeof(res));
					dmuci_set_value_by_section(user_class_s, "userclass", res);
				}
			}
			break;
	}
	return 0;
}
/*************************************************************
 * OPERATE COMMANDS
 *************************************************************/
static int operate_DHCPv4Client_Renew(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct uci_section *dhcpv4_s = ((struct dm_data *)data)->config_section;
	char *proto = NULL;

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

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

	return 0;
}

/**********************************************************************************************************************************
*                                            OBJ & PARAM DEFINITION
***********************************************************************************************************************************/
/* *** Device.DHCPv4. *** */
DMOBJ tDHCPv4Obj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"Client", &DMWRITE, addObjDHCPv4Client, delObjDHCPv4Client, NULL, browseDHCPv4ClientInst, NULL, NULL, tDHCPv4ClientObj, tDHCPv4ClientParams, NULL, BBFDM_BOTH, NULL},
{"Server", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, tDHCPv4ServerObj, tDHCPv4ServerParams, NULL, BBFDM_BOTH, NULL},
{"Relay", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, tDHCPv4RelayObj, tDHCPv4RelayParams, NULL, BBFDM_BOTH, NULL},
{0}
};

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

/* *** Device.DHCPv4.Client.{i}. *** */
DMOBJ tDHCPv4ClientObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"SentOption", &DMWRITE, addObjDHCPv4ClientSentOption, delObjDHCPv4ClientSentOption, NULL, browseDHCPv4ClientSentOptionInst, NULL, NULL, NULL, tDHCPv4ClientSentOptionParams, NULL, BBFDM_BOTH, NULL},
{"ReqOption", &DMWRITE, addObjDHCPv4ClientReqOption, delObjDHCPv4ClientReqOption, NULL, browseDHCPv4ClientReqOptionInst, NULL, NULL, NULL, tDHCPv4ClientReqOptionParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tDHCPv4ClientParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_DHCPv4Client_Enable, set_DHCPv4Client_Enable, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_DHCPv4Client_Alias, set_DHCPv4Client_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Interface", &DMWRITE, DMT_STRING, get_DHCPv4Client_Interface, set_DHCPv4Client_Interface, BBFDM_BOTH, DM_FLAG_UNIQUE|DM_FLAG_REFERENCE},
{"Status", &DMREAD, DMT_STRING, get_DHCPv4Client_Status, NULL, BBFDM_BOTH},
{"DHCPStatus", &DMREAD, DMT_STRING, get_DHCPv4Client_DHCPStatus, NULL, BBFDM_BOTH},
{"Renew", &DMWRITE, DMT_BOOL, get_DHCPv4Client_Renew, set_DHCPv4Client_Renew, BBFDM_CWMP},
{"IPAddress", &DMREAD, DMT_STRING, get_DHCPv4Client_IPAddress, NULL, BBFDM_BOTH},
{"SubnetMask", &DMREAD, DMT_STRING, get_DHCPv4Client_SubnetMask, NULL, BBFDM_BOTH},
{"IPRouters", &DMREAD, DMT_STRING, get_DHCPv4Client_IPRouters, NULL, BBFDM_BOTH},
{"DNSServers", &DMREAD, DMT_STRING, get_DHCPv4Client_DNSServers, NULL, BBFDM_BOTH},
{"LeaseTimeRemaining", &DMREAD, DMT_INT, get_DHCPv4Client_LeaseTimeRemaining, NULL, BBFDM_BOTH},
//{"DHCPServer", &DMREAD, DMT_STRING, get_DHCPv4Client_DHCPServer, NULL, BBFDM_BOTH},
//{"PassthroughEnable", &DMWRITE, DMT_BOOL, get_DHCPv4Client_PassthroughEnable, set_DHCPv4Client_PassthroughEnable, BBFDM_BOTH},
//{"PassthroughDHCPPool", &DMWRITE, DMT_STRING, get_DHCPv4Client_PassthroughDHCPPool, set_DHCPv4Client_PassthroughDHCPPool, BBFDM_BOTH},
{"SentOptionNumberOfEntries", &DMREAD, DMT_UNINT, get_DHCPv4Client_SentOptionNumberOfEntries, NULL, BBFDM_BOTH},
{"ReqOptionNumberOfEntries", &DMREAD, DMT_UNINT, get_DHCPv4Client_ReqOptionNumberOfEntries, NULL, BBFDM_BOTH},
{"Renew()", &DMSYNC, DMT_COMMAND, NULL, operate_DHCPv4Client_Renew, BBFDM_USP},
{0}
};

/* *** Device.DHCPv4.Client.{i}.SentOption.{i}. *** */
DMLEAF tDHCPv4ClientSentOptionParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_DHCPv4ClientSentOption_Enable, set_DHCPv4ClientSentOption_Enable, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_DHCPv4ClientSentOption_Alias, set_DHCPv4ClientSentOption_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Tag", &DMWRITE, DMT_UNINT, get_DHCPv4ClientSentOption_Tag, set_DHCPv4ClientSentOption_Tag, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Value", &DMWRITE, DMT_HEXBIN, get_DHCPv4ClientSentOption_Value, set_DHCPv4ClientSentOption_Value, BBFDM_BOTH},
{0}
};

/* *** Device.DHCPv4.Client.{i}.ReqOption.{i}. *** */
DMLEAF tDHCPv4ClientReqOptionParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_DHCPv4ClientReqOption_Enable, set_DHCPv4ClientReqOption_Enable, BBFDM_BOTH},
//{"Order", &DMWRITE, DMT_UNINT, get_DHCPv4ClientReqOption_Order, set_DHCPv4ClientReqOption_Order, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_DHCPv4ClientReqOption_Alias, set_DHCPv4ClientReqOption_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Tag", &DMWRITE, DMT_UNINT, get_DHCPv4ClientReqOption_Tag, set_DHCPv4ClientReqOption_Tag, BBFDM_BOTH, DM_FLAG_UNIQUE},
//{"Value", &DMREAD, DMT_HEXBIN, get_DHCPv4ClientReqOption_Value, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.DHCPv4.Server. *** */
DMOBJ tDHCPv4ServerObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"Pool", &DMWRITE, addObjDHCPv4ServerPool, delObjDHCPv4ServerPool, NULL, browseDHCPv4ServerPoolInst, NULL, NULL, tDHCPv4ServerPoolObj, tDHCPv4ServerPoolParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tDHCPv4ServerParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_DHCPv4Server_Enable, set_DHCPv4Server_Enable, BBFDM_BOTH},
{"PoolNumberOfEntries", &DMREAD, DMT_UNINT, get_DHCPv4Server_PoolNumberOfEntries, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.DHCPv4.Server.Pool.{i}. *** */
DMOBJ tDHCPv4ServerPoolObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"StaticAddress", &DMWRITE, addObjDHCPv4ServerPoolStaticAddress, delObjDHCPv4ServerPoolStaticAddress, NULL, browseDHCPv4ServerPoolStaticAddressInst, NULL, NULL, NULL, tDHCPv4ServerPoolStaticAddressParams, NULL, BBFDM_BOTH, NULL},
{"Option", &DMWRITE, addObjDHCPv4ServerPoolOption, delObjDHCPv4ServerPoolOption, NULL, browseDHCPv4ServerPoolOptionInst, NULL, NULL, NULL, tDHCPv4ServerPoolOptionParams, NULL, BBFDM_BOTH, NULL},
{"Client", &DMREAD, NULL, NULL, NULL, browseDhcpClientInst, NULL, NULL, tDHCPv4ServerPoolClientObj, tDHCPv4ServerPoolClientParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tDHCPv4ServerPoolParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_DHCPv4ServerPool_Enable, set_DHCPv4ServerPool_Enable, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_DHCPv4ServerPool_Status, NULL, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_DHCPv4ServerPool_Alias, set_DHCPv4ServerPool_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Order", &DMWRITE, DMT_UNINT, get_DHCPv4ServerPool_Order, set_DHCPv4ServerPool_Order, BBFDM_BOTH},
{"Interface", &DMWRITE, DMT_STRING, get_DHCPv4ServerPool_Interface, set_DHCPv4ServerPool_Interface, BBFDM_BOTH, DM_FLAG_REFERENCE},
//{"VendorClassID", &DMWRITE, DMT_STRING, get_DHCPv4ServerPool_VendorClassID, set_DHCPv4ServerPool_VendorClassID, BBFDM_BOTH},
//{"VendorClassIDExclude", &DMWRITE, DMT_BOOL, get_DHCPv4ServerPool_VendorClassIDExclude, set_DHCPv4ServerPool_VendorClassIDExclude, BBFDM_BOTH},
//{"VendorClassIDMode", &DMWRITE, DMT_STRING, get_DHCPv4ServerPool_VendorClassIDMode, set_DHCPv4ServerPool_VendorClassIDMode, BBFDM_BOTH},
//{"ClientID", &DMWRITE, DMT_HEXBIN, get_DHCPv4ServerPool_ClientID, set_DHCPv4ServerPool_ClientID, BBFDM_BOTH},
//{"ClientIDExclude", &DMWRITE, DMT_BOOL, get_DHCPv4ServerPool_ClientIDExclude, set_DHCPv4ServerPool_ClientIDExclude, BBFDM_BOTH},
//{"UserClassID", &DMWRITE, DMT_HEXBIN, get_DHCPv4ServerPool_UserClassID, set_DHCPv4ServerPool_UserClassID, BBFDM_BOTH},
//{"UserClassIDExclude", &DMWRITE, DMT_BOOL, get_DHCPv4ServerPool_UserClassIDExclude, set_DHCPv4ServerPool_UserClassIDExclude, BBFDM_BOTH},
//{"Chaddr", &DMWRITE, DMT_STRING, get_DHCPv4ServerPool_Chaddr, set_DHCPv4ServerPool_Chaddr, BBFDM_BOTH},
//{"ChaddrMask", &DMWRITE, DMT_STRING, get_DHCPv4ServerPool_ChaddrMask, set_DHCPv4ServerPool_ChaddrMask, BBFDM_BOTH},
//{"ChaddrExclude", &DMWRITE, DMT_BOOL, get_DHCPv4ServerPool_ChaddrExclude, set_DHCPv4ServerPool_ChaddrExclude, BBFDM_BOTH},
{"AllowedDevices", &DMWRITE, DMT_STRING, get_DHCPv4ServerPool_AllowedDevices, set_DHCPv4ServerPool_AllowedDevices, BBFDM_BOTH},
{"MinAddress", &DMWRITE, DMT_STRING, get_DHCPv4ServerPool_MinAddress, set_DHCPv4ServerPool_MinAddress, BBFDM_BOTH},
{"MaxAddress", &DMWRITE, DMT_STRING, get_DHCPv4ServerPool_MaxAddress, set_DHCPv4ServerPool_MaxAddress, BBFDM_BOTH},
{"ReservedAddresses", &DMWRITE, DMT_STRING, get_DHCPv4ServerPool_ReservedAddresses, set_DHCPv4ServerPool_ReservedAddresses, BBFDM_BOTH},
{"SubnetMask", &DMWRITE, DMT_STRING, get_DHCPv4ServerPool_SubnetMask, set_DHCPv4ServerPool_SubnetMask, BBFDM_BOTH},
{"DNSServers", &DMWRITE, DMT_STRING, get_DHCPv4ServerPool_DNSServers, set_DHCPv4ServerPool_DNSServers, BBFDM_BOTH},
{"DomainName", &DMWRITE, DMT_STRING, get_DHCPv4ServerPool_DomainName, set_DHCPv4ServerPool_DomainName, BBFDM_BOTH},
{"IPRouters", &DMWRITE, DMT_STRING, get_DHCPv4ServerPool_IPRouters, set_DHCPv4ServerPool_IPRouters, BBFDM_BOTH},
{"LeaseTime", &DMWRITE, DMT_INT, get_DHCPv4ServerPool_LeaseTime, set_DHCPv4ServerPool_LeaseTime, BBFDM_BOTH},
{"StaticAddressNumberOfEntries", &DMREAD, DMT_UNINT, get_DHCPv4ServerPool_StaticAddressNumberOfEntries, NULL, BBFDM_BOTH},
{"OptionNumberOfEntries", &DMREAD, DMT_UNINT, get_DHCPv4ServerPool_OptionNumberOfEntries, NULL, BBFDM_BOTH},
{"ClientNumberOfEntries", &DMREAD, DMT_UNINT, get_DHCPv4ServerPool_ClientNumberOfEntries, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.DHCPv4.Server.Pool.{i}.StaticAddress.{i}. *** */
DMLEAF tDHCPv4ServerPoolStaticAddressParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_DHCPv4ServerPoolStaticAddress_Enable, set_DHCPv4ServerPoolStaticAddress_Enable, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_DHCPv4ServerPoolStaticAddress_Alias, set_DHCPv4ServerPoolStaticAddress_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Chaddr", &DMWRITE, DMT_STRING, get_DHCPv4ServerPoolStaticAddress_Chaddr, set_DHCPv4ServerPoolStaticAddress_Chaddr, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Yiaddr", &DMWRITE, DMT_STRING, get_DHCPv4ServerPoolStaticAddress_Yiaddr, set_DHCPv4ServerPoolStaticAddress_Yiaddr, BBFDM_BOTH},
#ifdef DHCPMNGR_ENABLE_VENDOR_EXT
{BBF_VENDOR_PREFIX"Name", &DMWRITE, DMT_STRING, get_DHCPv4ServerPoolStaticAddress_name, set_DHCPv4ServerPoolStaticAddress_name, BBFDM_BOTH},
#endif
{0}
};

/* *** Device.DHCPv4.Server.Pool.{i}.Option.{i}. *** */
DMLEAF tDHCPv4ServerPoolOptionParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_DHCPv4ServerPoolOption_Enable, set_DHCPv4ServerPoolOption_Enable, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_DHCPv4ServerPoolOption_Alias, set_DHCPv4ServerPoolOption_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Tag", &DMWRITE, DMT_UNINT, get_DHCPv4ServerPoolOption_Tag, set_DHCPv4ServerPoolOption_Tag, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Value", &DMWRITE, DMT_HEXBIN, get_DHCPv4ServerPoolOption_Value, set_DHCPv4ServerPoolOption_Value, BBFDM_BOTH},
{0}
};

/* *** Device.DHCPv4.Server.Pool.{i}.Client.{i}. *** */
DMOBJ tDHCPv4ServerPoolClientObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"IPv4Address", &DMREAD, NULL, NULL, NULL, browseDhcpClientIPv4Inst, NULL, NULL, NULL, tDHCPv4ServerPoolClientIPv4AddressParams, NULL, BBFDM_BOTH, NULL},
{"Option", &DMREAD, NULL, NULL, NULL, browseDHCPv4ServerPoolClientOptionInst, NULL, NULL, NULL, tDHCPv4ServerPoolClientOptionParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tDHCPv4ServerPoolClientParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Alias", &DMWRITE, DMT_STRING,  get_DHCPv4ServerPoolClient_Alias, set_DHCPv4ServerPoolClient_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Chaddr", &DMREAD, DMT_STRING,  get_DHCPv4ServerPoolClient_Chaddr, NULL, BBFDM_BOTH, DM_FLAG_UNIQUE|DM_FLAG_LINKER},
{"Active", &DMREAD, DMT_BOOL,  get_DHCPv4ServerPoolClient_Active, NULL, BBFDM_BOTH},
{"IPv4AddressNumberOfEntries", &DMREAD, DMT_UNINT, get_DHCPv4ServerPoolClient_IPv4AddressNumberOfEntries, NULL, BBFDM_BOTH},
{"OptionNumberOfEntries", &DMREAD, DMT_UNINT, get_DHCPv4ServerPoolClient_OptionNumberOfEntries, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.DHCPv4.Server.Pool.{i}.Client.{i}.IPv4Address.{i}. *** */
DMLEAF tDHCPv4ServerPoolClientIPv4AddressParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"IPAddress", &DMREAD, DMT_STRING, get_DHCPv4ServerPoolClientIPv4Address_IPAddress, NULL, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"LeaseTimeRemaining", &DMREAD, DMT_TIME, get_DHCPv4ServerPoolClientIPv4Address_LeaseTimeRemaining, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.DHCPv4.Server.Pool.{i}.Client.{i}.Option.{i}. *** */
DMLEAF tDHCPv4ServerPoolClientOptionParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Tag", &DMREAD, DMT_UNINT, get_DHCPv4ServerPoolClientOption_Tag, NULL, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Value", &DMREAD, DMT_HEXBIN, get_DHCPv4ServerPoolClientOption_Value, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.DHCPv4.Relay. *** */
DMOBJ tDHCPv4RelayObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"Forwarding", &DMWRITE, addObjDHCPv4RelayForwarding, delObjDHCPv4RelayForwarding, NULL, browseDHCPv4RelayForwardingInst, NULL, NULL, NULL, tDHCPv4RelayForwardingParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tDHCPv4RelayParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_DHCPv4Relay_Enable, set_DHCPv4Relay_Enable, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_DHCPv4Relay_Status, NULL, BBFDM_BOTH},
{"ForwardingNumberOfEntries", &DMREAD, DMT_UNINT, get_DHCPv4Relay_ForwardingNumberOfEntries, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.DHCPv4.Relay.Forwarding.{i}. *** */
DMLEAF tDHCPv4RelayForwardingParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_DHCPv4RelayForwarding_Enable, set_DHCPv4RelayForwarding_Enable, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_DHCPv4RelayForwarding_Status, NULL, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_DHCPv4RelayForwarding_Alias, set_DHCPv4RelayForwarding_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
//{"Order", &DMWRITE, DMT_UNINT, get_DHCPv4RelayForwarding_Order, set_DHCPv4RelayForwarding_Order, BBFDM_BOTH},
{"Interface", &DMWRITE, DMT_STRING, get_DHCPv4RelayForwarding_Interface, set_DHCPv4RelayForwarding_Interface, BBFDM_BOTH, DM_FLAG_REFERENCE},
{"VendorClassID", &DMWRITE, DMT_STRING, get_DHCPv4RelayForwarding_VendorClassID, set_DHCPv4RelayForwarding_VendorClassID, BBFDM_BOTH},
//{"VendorClassIDExclude", &DMWRITE, DMT_BOOL, get_DHCPv4RelayForwarding_VendorClassIDExclude, set_DHCPv4RelayForwarding_VendorClassIDExclude, BBFDM_BOTH},
//{"VendorClassIDMode", &DMWRITE, DMT_STRING, get_DHCPv4RelayForwarding_VendorClassIDMode, set_DHCPv4RelayForwarding_VendorClassIDMode, BBFDM_BOTH},
//{"ClientID", &DMWRITE, DMT_HEXBIN, get_DHCPv4RelayForwarding_ClientID, set_DHCPv4RelayForwarding_ClientID, BBFDM_BOTH},
//{"ClientIDExclude", &DMWRITE, DMT_BOOL, get_DHCPv4RelayForwarding_ClientIDExclude, set_DHCPv4RelayForwarding_ClientIDExclude, BBFDM_BOTH},
{"UserClassID", &DMWRITE, DMT_HEXBIN, get_DHCPv4RelayForwarding_UserClassID, set_DHCPv4RelayForwarding_UserClassID, BBFDM_BOTH},
//{"UserClassIDExclude", &DMWRITE, DMT_BOOL, get_DHCPv4RelayForwarding_UserClassIDExclude, set_DHCPv4RelayForwarding_UserClassIDExclude, BBFDM_BOTH},
{"Chaddr", &DMWRITE, DMT_STRING, get_DHCPv4RelayForwarding_Chaddr, set_DHCPv4RelayForwarding_Chaddr, BBFDM_BOTH},
//{"ChaddrMask", &DMWRITE, DMT_STRING, get_DHCPv4RelayForwarding_ChaddrMask, set_DHCPv4RelayForwarding_ChaddrMask, BBFDM_BOTH},
//{"ChaddrExclude", &DMWRITE, DMT_BOOL, get_DHCPv4RelayForwarding_ChaddrExclude, set_DHCPv4RelayForwarding_ChaddrExclude, BBFDM_BOTH},
//{"LocallyServed", &DMWRITE, DMT_BOOL, get_DHCPv4RelayForwarding_LocallyServed, set_DHCPv4RelayForwarding_LocallyServed, BBFDM_BOTH},
//{"DHCPServerIPAddress", &DMWRITE, DMT_STRING, get_DHCPv4RelayForwarding_DHCPServerIPAddress, set_DHCPv4RelayForwarding_DHCPServerIPAddress, BBFDM_BOTH},
{0}
};
