/*
 * Copyright (C) 2020-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: Amin Ben Romdhane <amin.benromdhane@iopsys.eu>
 *	Author: Mohd Husaam Mehdi <husaam.mehdi@iopsys.eu>
 *
 */

#include <libbbfdm-ubus/bbfdm-ubus.h>
#include "common.h"
#include "ip.h"

#define STATUS_SIZE 16

typedef struct {
	struct list_head list;
	uint16_t local_port;
	uint16_t remote_port;
	unsigned int state;
	char local_ip[INET6_ADDRSTRLEN];
	char remote_ip[INET6_ADDRSTRLEN];
} ActivePort;

static bool g_icwmp_session_running = false;
static struct list_head port_list;
static struct uloop_timeout refresh_port_timer;
static struct ubus_event_handler ev_cwmp_session;
static bool browse_ip_port(bool is_ipv6, const char *proc_path);

static void init_port_list(void)
{
	browse_ip_port(0, "/proc/net/tcp");
	browse_ip_port(1, "/proc/net/tcp6");
}

static void free_port_list(void)
{
	ActivePort *entry = NULL, *tmp = NULL;

	list_for_each_entry_safe(entry, tmp, &port_list, list) {
		list_del(&entry->list);
		FREE(entry);
	}
}

static void refresh_ip_active_port(struct uloop_timeout *timeout)
{
	if (g_icwmp_session_running == false) {
		free_port_list();
		init_port_list();
	}

	uloop_timeout_set(timeout, 30 * 1000);
}

/*************************************************************
* INIT
**************************************************************/
static void format_ipv6_address(const char *hex_str_ip, char *ipv6_addr)
{
	struct in6_addr addr = {};

	sscanf(hex_str_ip, "%08X%08X%08X%08X",
			&addr.s6_addr32[0], &addr.s6_addr32[1],
			&addr.s6_addr32[2], &addr.s6_addr32[3]);

	// Convert the address to the standard IPv6 format
	inet_ntop(AF_INET6, &addr, ipv6_addr, INET6_ADDRSTRLEN);
}

static void parse_tcp_line(const char *line, int is_ipv6, ActivePort *port)
{
	unsigned int local_port, remote_port;
	unsigned int state;
	char local_ip[INET6_ADDRSTRLEN] = {0};
	char remote_ip[INET6_ADDRSTRLEN] = {0};

	if (is_ipv6) {
		char local_ip6[33] = {0}, remote_ip6[33] = {0};
		sscanf(line, "%*d: %32s:%4X %32s:%4X %2X", local_ip6, &local_port, remote_ip6, &remote_port, &state);
		format_ipv6_address(local_ip6, local_ip);
		format_ipv6_address(remote_ip6, remote_ip);
	} else {
		unsigned int local_ip_num, remote_ip_num;
		sscanf(line, "%*d: %8X:%4X %8X:%4X %2X", &local_ip_num, &local_port, &remote_ip_num, &remote_port, &state);

		struct in_addr local_addr = { local_ip_num };
		struct in_addr remote_addr = { remote_ip_num };

		inet_ntop(AF_INET, &local_addr, local_ip, INET_ADDRSTRLEN);
		inet_ntop(AF_INET, &remote_addr, remote_ip, INET_ADDRSTRLEN);
	}

	DM_STRNCPY(port->local_ip, local_ip, INET6_ADDRSTRLEN);
	port->local_port = local_port;
	DM_STRNCPY(port->remote_ip, remote_ip, INET6_ADDRSTRLEN);
	port->remote_port = remote_port;
	port->state = state;
}

static bool browse_ip_port(bool is_ipv6, const char *proc_path)
{
	bool is_instance = false;

	if (proc_path == NULL || DM_STRLEN(proc_path) == 0)
		return is_instance;

	// cppcheck-suppress cert-MSC24-C
	FILE* fp = fopen(proc_path, "r");
	if (fp == NULL) {
		return is_instance;
	}

	char line[256] = {0};
	fgets(line, sizeof(line), fp); // Skip header line

	while (fgets(line, sizeof(line), fp)) {
		ActivePort port;
		memset(&port, 0, sizeof(port));
		parse_tcp_line(line, is_ipv6, &port);

		// only display LISTEN or ESTABLISHED
		if (port.state != 1 && port.state != 10)
			continue;

		ActivePort *entry = (ActivePort *)calloc(1, sizeof(ActivePort));
		if (!entry) {
			BBFDM_INFO("Failed to allocate memory for process entry");
			continue;
		}

		entry->local_port = port.local_port;
		entry->remote_port = port.remote_port;
		entry->state = port.state;
		snprintf(entry->local_ip, sizeof(entry->local_ip), "%s", port.local_ip);
		snprintf(entry->remote_ip, sizeof(entry->remote_ip), "%s", port.remote_ip);

		list_add_tail(&entry->list, &port_list);
	}

	fclose(fp);

	return is_instance;
}

/*************************************************************
* COMMON Functions
**************************************************************/
static int get_sysctl_disable_ipv6_per_device(const char *device, char **value)
{
	char file[256];
	char val[32] = {0};

	*value = dmstrdup("0");

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

	snprintf(file, sizeof(file), "/proc/sys/net/ipv6/conf/%s/disable_ipv6", device);
	dm_read_sysfs_file(file, val, sizeof(val));
	*value = dmstrdup(val);

	return 0;
}

static int set_sysctl_disable_ipv6_per_device(const char *device, bool value)
{
	FILE *fp = NULL;
	char cmd[128] = {0};
	char path[64] = {0};

	// cppcheck-suppress cert-MSC24-C
	fp = fopen("/etc/bbfdm/sysctl.conf", "r+");
	if (!fp)
		return -1;

	int path_len = snprintf(path, sizeof(path), "net.ipv6.conf.%s.disable_ipv6", device);
	int cmd_len = snprintf(cmd, sizeof(cmd), "%s=%d", path, value ? 0 : 1);

	dmcmd("sysctl", 2, "-w", cmd);

	fseek(fp, 0, SEEK_END);
	long length = ftell(fp);

	char *buf = (char *)dmcalloc(1, length + 1);
	if (buf == NULL) {
		fclose(fp);
		return -1;
	}


	fseek(fp, 0, SEEK_SET);
	size_t len = fread(buf, 1, length, fp);
	if (len != length) {
		dmfree(buf);
		fclose(fp);
		return -1;
	}

	char *ptr = DM_STRSTR(buf, path);
	if (ptr) {
		*(ptr + path_len + 1) = (value) ? '0' : '1';
		fseek(fp, 0, SEEK_SET);
		fwrite(buf, sizeof(char), strlen(buf), fp);
	} else {
		cmd[cmd_len] = '\n';
		cmd[cmd_len + 1] = 0;
		fputs(cmd, fp);
	}

	dmfree(buf);
	fclose(fp);

	return 0;
}

static int get_ip_iface_sysfs(void *data, const char *name, char **value)
{
	return get_net_iface_sysfs(section_name(((struct dm_data *)data)->config_section), name, value);
}

static bool is_main_interface_sec(void *data)
{
	char *iface_instance = NULL, *iface_section = NULL;
	char *current_section = NULL;
	char *link_local = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "link_local", &link_local);
	if (DM_LSTRCMP(link_local, "1") == 0)
		return true;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "__section_name__", &current_section);
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "Interface", &iface_instance);
	struct uci_section *iface_dmmap_s = get_dup_section_in_dmmap_opt("IP", "Interface", "__instance__", iface_instance);
	if (iface_dmmap_s)
		dmuci_get_value_by_section_string(iface_dmmap_s, "__section_name__", &iface_section);

	return !DM_STRCMP(iface_section, current_section);
}

static void add_network_to_firewall_zone_network_list(const char *zone_name, const char *interface_name)
{
	struct uci_section *s = NULL;

	uci_foreach_option_eq("firewall", "zone", "name", zone_name, s) {
		dmuci_add_list_value_by_section(s, "network", interface_name);
		break;
	}
}

static struct uci_section *get_ip_addr_section(const char *obj_name, const char *iface_instance, const char *ip_addr_to_match)
{
	struct uci_section *dmmap_section = NULL;
	char *curr_ip_addr = NULL;

	uci_path_foreach_option_eq(bbfdm, "IP", obj_name, "Interface", iface_instance, dmmap_section) {
		dmuci_get_value_by_section_string(dmmap_section, "address", &curr_ip_addr);
		if (DM_STRCMP(curr_ip_addr, ip_addr_to_match) == 0)
			return dmmap_section;
	}

	return NULL;
}

static void dmmap_synchronize_ipv6_address_link_local(struct dmctx *dmctx, const char *device, const char *iface_instance, struct list_head *dup_list)
{
	struct uci_section *s = NULL, *stmp = NULL;
	char buf[512] = {0}, ipstr[64] = {0};
	FILE *fp = NULL;

	uci_path_foreach_option_eq_safe(bbfdm, "IP", "IPv6Address", "Interface", iface_instance, stmp, s) {
		char *link_local = NULL, *address = NULL;
		char *instance = NULL;

		dmuci_get_value_by_section_string(s, "__instance__", &instance);
		if (DM_STRLEN(instance) == 0) {
			BBF_WARNING("Skipping object with no instance number in package: %s, section type: %s, section name: %s",
				section_config(s), section_type(s), section_name(s));
			continue;
		}

		dmuci_get_value_by_section_string(s, "link_local", &link_local);
		if (DM_LSTRCMP(link_local, "1") != 0) {
			continue;
		}

		dmuci_get_value_by_section_string(s, "address", &address);

		// cppcheck-suppress cert-MSC24-C
		fp = fopen(PROC_INTF6, "r");
		if (fp == NULL)
			return;

		bool found = false;
		while (fgets(buf, 512, fp) != NULL) {

			if (parse_proc_intf6_line(buf, device, ipstr, sizeof(ipstr)))
				continue;

			if (address && DM_STRCMP(address, ipstr) == 0) {
				found = true;
				break;
			}
		}
		fclose(fp);

		if (!found) {
			dmuci_delete_by_section(s, NULL, NULL);
		}
	}

	// cppcheck-suppress cert-MSC24-C
	fp = fopen(PROC_INTF6, "r");
	if (fp == NULL)
		return;

	while (fgets(buf , 512 , fp) != NULL) {

		if (parse_proc_intf6_line(buf, device, ipstr, sizeof(ipstr)))
			continue;

		if (get_ip_addr_section("IPv6Address", iface_instance, ipstr) != NULL)
			continue;

		// Create a new IPv6Address instance -> Device.IP.Interface.{i}.IPv6Address.{i}.
		char *ipv6_instance = NULL;
		struct uci_section *ipv6addr_dmmap_s = create_dmmap_obj(dmctx, 1, "IP", "IPv6Address", NULL, &ipv6_instance);
		if (ipv6addr_dmmap_s && DM_STRLEN(ipv6_instance)) {
			dmuci_set_value_by_section(ipv6addr_dmmap_s, "address", ipstr);
			dmuci_set_value_by_section(ipv6addr_dmmap_s, "assign", "0");
			dmuci_set_value_by_section(ipv6addr_dmmap_s, "link_local", "1");
			if (dup_list != NULL) {
				add_dmmap_config_dup_list(dup_list, NULL, ipv6addr_dmmap_s);
			}
		}
	}

	fclose(fp);
}

static void synchronize_IPInterfaceIPv4AddressInst(struct dmctx *dmctx, struct uci_section *iface_config_sec, const char *iface_instance, struct list_head *dup_list)
{
	json_object *res = NULL, *ip_obj = NULL, *arrobj = NULL;
	struct uci_section *s = NULL, *stmp = NULL;
	char *cur_address = NULL, *proto = NULL, *iface_name = NULL, *device = NULL;
	int i = 0;

	// Device.IP.Interface.{i}.IPv4Address.{i}.
	uci_path_foreach_option_eq_safe(bbfdm, "IP", "IPv4Address", "Interface", iface_instance, stmp, s) {
		bool found = false;
		char *instance = NULL;
		char *config_sec_name = NULL;

		dmuci_get_value_by_section_string(s, "__instance__", &instance);
		if (DM_STRLEN(instance) == 0) {
			BBF_WARNING("Skipping object with no instance number in package: %s, section type: %s, section name: %s",
				section_config(s), section_type(s), section_name(s));
			continue;
		}

		dmuci_get_value_by_section_string(s, "proto", &proto);
		if (DM_LSTRCMP(proto, "static") == 0) {
			dmuci_get_value_by_section_string(s, "__section_name__", &config_sec_name);
			struct uci_section *uci_s = get_config_section_from_dmmap_section_name(config_sec_name);
			if (s != NULL && dup_list != NULL) {
				add_dmmap_config_dup_list(dup_list, uci_s, s);
			}
			continue;
		}

		dmuci_get_value_by_section_string(s, "iface_name", &iface_name);
		if (DM_STRLEN(iface_name) == 0)
			continue;

		dmuci_get_value_by_section_string(s, "address", &cur_address);

		dmubus_call_timeout("network.interface", "status", UBUS_ARGS{{"interface", iface_name, String}}, 1, 5000, &res);

		dmjson_foreach_obj_in_array(res, arrobj, ip_obj, i, 1, "ipv4-address") {

			char *address = dmjson_get_value(ip_obj, 1, "address");
			if (DM_STRLEN(address) && DM_STRCMP(address, cur_address) == 0) {
				found = true;
				break;
			}
		}

		if (res != NULL) {
			json_object_put(res);
			res = NULL;
		}

		if (found) {
			dmuci_get_value_by_section_string(s, "__section_name__", &config_sec_name);
			struct uci_section *uci_s = get_config_section_from_dmmap_section_name(config_sec_name);
			if (s != NULL && dup_list != NULL) {
				add_dmmap_config_dup_list(dup_list, uci_s, s);
			}
			continue;
		}

		dmuci_delete_by_section(s, NULL, NULL);
	}

	dmuci_get_value_by_section_string(iface_config_sec, "device", &device);

	uci_foreach_sections("network", "interface", s) {
		char *ip_device = NULL, *ip_disabled = NULL, *ip_mask = NULL, *ip_proto = NULL, *ip_instance = NULL;;
		char *ipv4_addr = NULL, *if_name = section_name(s);
		struct uci_section *dmmap_s = NULL;

		dmuci_get_value_by_section_string(s, "device", &ip_device);

		if (strcmp(section_name(s), section_name(iface_config_sec)) != 0 && DM_STRCMP(device, ip_device) != 0)
			continue;

		dmubus_call_timeout("network.interface", "status", UBUS_ARGS{{"interface", if_name, String}}, 1, 5000, &res);

		dmuci_get_value_by_section_string(s, "disabled", &ip_disabled);
		dmuci_get_value_by_section_string(s, "ipaddr", &ipv4_addr);
		dmuci_get_value_by_section_string(s, "netmask", &ip_mask);
		dmuci_get_value_by_section_string(s, "proto", &ip_proto);

		if (*ipv4_addr == '\0') {
			if (DM_STRCMP(ip_proto, "static") == 0) {
				// A static entry may also have empty ip, so need to be added if not present in dmmap
				char sec_name[64] = {0};

				snprintf(sec_name, sizeof(sec_name), "%s.%s", section_config(s), section_name(s));
				dmmap_s = get_dup_section_in_dmmap_opt("IP", "IPv4Address", "__section_name__", sec_name);
				if (dmmap_s == NULL) {
					// Create a new IPv4Address instance -> Device.IP.Interface.{i}.IPv4Address.{i}.
					dmmap_s = create_dmmap_obj(dmctx, 1, "IP", "IPv4Address", s, &ip_instance);
					if (dmmap_s && DM_STRLEN(ip_instance)) {
						dmuci_set_value_by_section(dmmap_s, "enable", (ip_disabled && *ip_disabled == '1') ? "0" : "1");
						dmuci_set_value_by_section(dmmap_s, "mask", ip_mask);
						dmuci_set_value_by_section(dmmap_s, "proto", ip_proto);
						dmuci_set_value_by_section(dmmap_s, "iface_name", section_name(s));
						if (dup_list != NULL) {
							add_dmmap_config_dup_list(dup_list, s, dmmap_s);
						}
					}
				}
			} else {
				ip_obj = dmjson_select_obj_in_array_idx(res, 0, 1, "ipv4-address");
				ipv4_addr = dmjson_get_value(ip_obj, 1, "address");
				ip_mask = dmjson_get_value(ip_obj, 1, "mask");
				ip_mask = DM_STRLEN(ip_mask) ? cidr2netmask(DM_STRTOL(ip_mask)) : "";
			}
		}

		if (*ipv4_addr != '\0') {
			// Device.IP.Interface.{i}.IPv4Address.{i}.
			dmmap_s = get_ip_addr_section("IPv4Address", iface_instance, ipv4_addr);
			if (dmmap_s == NULL) {
				// Create a new IPv4Address instance -> Device.IP.Interface.{i}.IPv4Address.{i}.
				dmmap_s = create_dmmap_obj(dmctx, 1, "IP", "IPv4Address", s, &ip_instance);
				if (dmmap_s && DM_STRLEN(ip_instance)) {
					dmuci_set_value_by_section(dmmap_s, "enable", (ip_disabled && *ip_disabled == '1') ? "0" : "1");
					dmuci_set_value_by_section(dmmap_s, "address", ipv4_addr);
					dmuci_set_value_by_section(dmmap_s, "mask", ip_mask);
					dmuci_set_value_by_section(dmmap_s, "proto", ip_proto);
					dmuci_set_value_by_section(dmmap_s, "iface_name", section_name(s));
					if (dup_list != NULL) {
						add_dmmap_config_dup_list(dup_list, s, dmmap_s);
					}
				}
			}
		}

		if (res != NULL) {
			json_object_put(res);
			res = NULL;
		}
	}
}

static void synchronize_IPInterfaceIPv6AddressInst(struct dmctx *dmctx, struct uci_section *iface_config_sec, const char *iface_instance, struct list_head *dup_list)
{
	json_object *res = NULL, *ip_obj = NULL, *arrobj = NULL;
	struct uci_section *s = NULL, *stmp = NULL;
	char *cur_address = NULL, *proto = NULL, *iface_name = NULL, *device = NULL;
	int i = 0;

	// Device.IP.Interface.{i}.IPv6Address.{i}.
	uci_path_foreach_option_eq_safe(bbfdm, "IP", "IPv6Address", "Interface", iface_instance, stmp, s) {
		bool found = false;
		char *instance = NULL;

		dmuci_get_value_by_section_string(s, "__instance__", &instance);
		if (DM_STRLEN(instance) == 0) {
			BBF_WARNING("Skipping object with no instance number in package: %s, section type: %s, section name: %s",
				section_config(s), section_type(s), section_name(s));
			continue;
		}

		char *link_local = NULL;
		dmuci_get_value_by_section_string(s, "link_local", &link_local);
		if (DM_LSTRCMP(link_local, "1") == 0) {
			if (dup_list != NULL) {
				add_dmmap_config_dup_list(dup_list, NULL, s);
			}
			continue;
		}

		dmuci_get_value_by_section_string(s, "proto", &proto);
		if (DM_LSTRCMP(proto, "static") == 0) {
			struct uci_section *uci_s = NULL;
			char *config_sec_name = NULL;

			dmuci_get_value_by_section_string(s, "__section_name__", &config_sec_name);
			if (DM_STRLEN(config_sec_name))
				uci_s = get_config_section_from_dmmap_section_name(config_sec_name);

			if (dup_list != NULL) {
				add_dmmap_config_dup_list(dup_list, uci_s, s);
			}
			continue;
		}

		dmuci_get_value_by_section_string(s, "iface_name", &iface_name);
		if (DM_STRLEN(iface_name) == 0)
			continue;

		dmuci_get_value_by_section_string(s, "address", &cur_address);

		dmubus_call_timeout("network.interface", "status", UBUS_ARGS{{"interface", iface_name, String}}, 1, 5000, &res);

		dmjson_foreach_obj_in_array(res, arrobj, ip_obj, i, 1, "ipv6-address") {

			char *address = dmjson_get_value(ip_obj, 1, "address");
			if (address && *address && DM_STRCMP(address, cur_address) == 0) {
				found = true;
				break;
			}

		}

		if (found) {
			if (res != NULL) {
				json_object_put(res);
				res = NULL;
			}

			if (dup_list != NULL) {
				add_dmmap_config_dup_list(dup_list, NULL, s);
			}

			continue;
		}

		dmjson_foreach_obj_in_array(res, arrobj, ip_obj, i, 1, "ipv6-prefix-assignment") {

			char *address = dmjson_get_value(ip_obj, 2, "local-address", "address");
			if (address && *address && DM_STRCMP(address, cur_address) == 0) {
				found = true;
				break;
			}

		}

		if (res != NULL) {
			json_object_put(res);
			res = NULL;
		}

		if (found) {
			if (dup_list != NULL) {
				add_dmmap_config_dup_list(dup_list, NULL, s);
			}

			continue;
		}

		dmuci_delete_by_section(s, NULL, NULL);
	}

	dmuci_get_value_by_section_string(iface_config_sec, "device", &device);

	uci_foreach_sections("network", "interface", s) {
		char *ip_device = NULL, *ip_disabled = NULL, *ip_proto = NULL, *ip_instance = NULL;;
		char *ipv6_addr = NULL, *if_name = section_name(s);
		struct uci_section *dmmap_s = NULL;

		dmuci_get_value_by_section_string(s, "device", &ip_device);

		if (strcmp(section_name(s), section_name(iface_config_sec)) != 0 && DM_STRCMP(device, ip_device) != 0)
			continue;

		dmubus_call_timeout("network.interface", "status", UBUS_ARGS{{"interface", if_name, String}}, 1, 5000, &res);

		dmuci_get_value_by_section_string(s, "disabled", &ip_disabled);
		dmuci_get_value_by_section_string(s, "ip6addr", &ipv6_addr);
		dmuci_get_value_by_section_string(s, "proto", &ip_proto);

		if (*ipv6_addr == '\0') {
			dmjson_foreach_obj_in_array(res, arrobj, ip_obj, i, 1, "ipv6-address") {
				char *address = dmjson_get_value(ip_obj, 1, "address");
				if (*address == '\0')
					continue;

				char *preferred = dmjson_get_value(ip_obj, 1, "preferred");
				char *valid = dmjson_get_value(ip_obj, 1, "valid");

				// Device.IP.Interface.{i}.IPv6Address.{i}.
				dmmap_s = get_ip_addr_section("IPv6Address", iface_instance, address);
				if (dmmap_s == NULL) {
					// Create a new IPv4Address instance -> Device.IP.Interface.{i}.IPv6Address.{i}.
					dmmap_s = create_dmmap_obj(dmctx, 1, "IP", "IPv6Address", NULL, &ip_instance);
					if (dmmap_s && DM_STRLEN(ip_instance)) {
						dmuci_set_value_by_section(dmmap_s, "address", address);
						dmuci_set_value_by_section(dmmap_s, "assign", "0");
						dmuci_set_value_by_section(dmmap_s, "proto", ip_proto);
						dmuci_set_value_by_section(dmmap_s, "iface_name", section_name(s));
						dmuci_set_value_by_section(dmmap_s, "preferred", preferred);
						dmuci_set_value_by_section(dmmap_s, "valid", valid);

						if (dup_list != NULL) {
							add_dmmap_config_dup_list(dup_list, NULL, dmmap_s);
						}
					}
				}
			}
		} else {
			// Device.IP.Interface.{i}.IPv6Address.{i}.
			dmmap_s = get_ip_addr_section("IPv6Address", iface_instance, ipv6_addr);
			if (dmmap_s == NULL) {
				// Create a new IPv6Address instance -> Device.IP.Interface.{i}.IPv6Address.{i}.
				dmmap_s = create_dmmap_obj(dmctx, 1, "IP", "IPv6Address", s, &ip_instance);
				if (dmmap_s && DM_STRLEN(ip_instance)) {
					dmuci_set_value_by_section(dmmap_s, "address", ipv6_addr);
					dmuci_set_value_by_section(dmmap_s, "assign", "0");
					dmuci_set_value_by_section(dmmap_s, "proto", ip_proto);
					dmuci_set_value_by_section(dmmap_s, "iface_name", section_name(s));

					if (dup_list != NULL) {
						add_dmmap_config_dup_list(dup_list, s, dmmap_s);
					}
				}
			}
		}

		dmjson_foreach_obj_in_array(res, arrobj, ip_obj, i, 1, "ipv6-prefix-assignment") {
			char *address = dmjson_get_value(ip_obj, 2, "local-address", "address");
			if (*address == '\0')
				continue;

			char *preferred = dmjson_get_value(ip_obj, 2, "local-address", "preferred");
			char *valid = dmjson_get_value(ip_obj, 2, "local-address", "valid");

			// Device.IP.Interface.{i}.IPv6Address.{i}.
			dmmap_s = get_ip_addr_section("IPv6Address", iface_instance, address);
			if (dmmap_s == NULL) {
				// Create a new IPv4Address instance -> Device.IP.Interface.{i}.IPv6Address.{i}.
				dmmap_s = create_dmmap_obj(dmctx, 1, "IP", "IPv6Address", NULL, &ip_instance);
				if (dmmap_s && DM_STRLEN(ip_instance)) {
					dmuci_set_value_by_section(dmmap_s, "address", address);
					dmuci_set_value_by_section(dmmap_s, "assign", "1");
					dmuci_set_value_by_section(dmmap_s, "proto", "auto-assign");
					dmuci_set_value_by_section(dmmap_s, "iface_name", section_name(s));
					dmuci_set_value_by_section(dmmap_s, "preferred", preferred);
					dmuci_set_value_by_section(dmmap_s, "valid", valid);

					if (dup_list != NULL) {
						add_dmmap_config_dup_list(dup_list, NULL, dmmap_s);
					}
				}
			}
		}

		if (res != NULL) {
			json_object_put(res);
			res = NULL;
		}

		// Get ipv6 LinkLocal address
		if (strcmp(section_name(iface_config_sec), section_name(s)) == 0) {
			dmmap_synchronize_ipv6_address_link_local(dmctx, device, iface_instance, dup_list);
		}
	}
}

static void synchronize_IPInterfaceIPv6PrefixInst(struct dmctx *dmctx, struct uci_section *iface_config_sec, const char *iface_instance, struct list_head *dup_list)
{
	json_object *res = NULL, *ip_obj = NULL, *arrobj = NULL;
	struct uci_section *s = NULL, *stmp = NULL;
	char *cur_address = NULL, *proto = NULL, *iface_name = NULL, *device = NULL;
	int i = 0;

	// Device.IP.Interface.{i}.IPv6Prefix.{i}.
	uci_path_foreach_option_eq_safe(bbfdm, "IP", "IPv6Prefix", "Interface", iface_instance, stmp, s) {
		char ipv6_prefix[256] = {0};
		bool found = false;
		char *instance = NULL;

		dmuci_get_value_by_section_string(s, "__instance__", &instance);
		if (DM_STRLEN(instance) == 0) {
			BBF_WARNING("Skipping object with no instance number in package: %s, section type: %s, section name: %s",
				section_config(s), section_type(s), section_name(s));
			continue;
		}

		dmuci_get_value_by_section_string(s, "proto", &proto);
		if (DM_LSTRCMP(proto, "static") == 0) {
			struct uci_section *uci_s = NULL;
			char *config_sec_name = NULL;

			dmuci_get_value_by_section_string(s, "__section_name__", &config_sec_name);
			if (DM_STRLEN(config_sec_name))
				uci_s = get_config_section_from_dmmap_section_name(config_sec_name);

			if (dup_list != NULL) {
				add_dmmap_config_dup_list(dup_list, uci_s, s);
			}
			continue;
		}

		dmuci_get_value_by_section_string(s, "iface_name", &iface_name);
		if (DM_STRLEN(iface_name) == 0)
			continue;

		dmuci_get_value_by_section_string(s, "address", &cur_address);

		dmubus_call_timeout("network.interface", "status", UBUS_ARGS{{"interface", iface_name, String}}, 1, 5000, &res);

		dmjson_foreach_obj_in_array(res, arrobj, ip_obj, i, 1, "ipv6-prefix") {

			char *address = dmjson_get_value(ip_obj, 1, "address");
			char *mask = dmjson_get_value(ip_obj, 1, "mask");
			if (*address == '\0' || *mask == '\0')
				continue;

			snprintf(ipv6_prefix, sizeof(ipv6_prefix), "%s/%s", address, mask);
			if (DM_STRCMP(ipv6_prefix, cur_address) == 0) {
				found = true;
				break;
			}

		}

		if (found) {
			if (res != NULL) {
				json_object_put(res);
				res = NULL;
			}

			if (dup_list != NULL) {
				add_dmmap_config_dup_list(dup_list, NULL, s);
			}
			continue;
		}

		dmjson_foreach_obj_in_array(res, arrobj, ip_obj, i, 1, "ipv6-prefix-assignment") {

			char *address = dmjson_get_value(ip_obj, 1, "address");
			char *mask = dmjson_get_value(ip_obj, 1, "mask");
			if (*address == '\0' || *mask == '\0')
				continue;

			snprintf(ipv6_prefix, sizeof(ipv6_prefix), "%s/%s", address, mask);
			if (DM_STRCMP(ipv6_prefix, cur_address) == 0) {
				found = true;
				break;
			}

		}

		if (res != NULL) {
			json_object_put(res);
			res = NULL;
		}

		if (found) {
			if (dup_list != NULL) {
				add_dmmap_config_dup_list(dup_list, NULL, s);
			}
			continue;
		}

		dmuci_delete_by_section(s, NULL, NULL);
	}

	dmuci_get_value_by_section_string(iface_config_sec, "device", &device);

	uci_foreach_sections("network", "interface", s) {
		char *ip_device = NULL, *ip_disabled = NULL, *ip_proto = NULL, *ip_instance = NULL;;
		char *ip6prefix_addr = NULL, *if_name = section_name(s);
		struct uci_section *dmmap_s = NULL;

		dmuci_get_value_by_section_string(s, "device", &ip_device);

		if (strcmp(section_name(s), section_name(iface_config_sec)) != 0 && DM_STRCMP(device, ip_device) != 0)
			continue;

		dmubus_call_timeout("network.interface", "status", UBUS_ARGS{{"interface", if_name, String}}, 1, 5000, &res);

		dmuci_get_value_by_section_string(s, "disabled", &ip_disabled);
		dmuci_get_value_by_section_string(s, "ip6prefix", &ip6prefix_addr);
		dmuci_get_value_by_section_string(s, "proto", &ip_proto);

		char ipv6_prefix[256] = {0};
		if (*ip6prefix_addr == '\0') {
			dmjson_foreach_obj_in_array(res, arrobj, ip_obj, i, 1, "ipv6-prefix") {
				char *address = dmjson_get_value(ip_obj, 1, "address");
				char *mask = dmjson_get_value(ip_obj, 1, "mask");
				if (*address == '\0' || *mask == '\0')
					continue;

				snprintf(ipv6_prefix, sizeof(ipv6_prefix), "%s/%s", address, mask);
				char *preferred = dmjson_get_value(ip_obj, 1, "preferred");

				// Device.IP.Interface.{i}.IPv6Prefix.{i}.
				dmmap_s = get_ip_addr_section("IPv6Prefix", iface_instance, ipv6_prefix);
				if (dmmap_s == NULL) {
					// Create a new IPv4Address instance -> Device.IP.Interface.{i}.IPv6Prefix.{i}.
					dmmap_s = create_dmmap_obj(dmctx, 1, "IP", "IPv6Prefix", NULL, &ip_instance);
					if (dmmap_s && DM_STRLEN(ip_instance)) {
						dmuci_set_value_by_section(dmmap_s, "address", ipv6_prefix);
						dmuci_set_value_by_section(dmmap_s, "assign", "0");
						dmuci_set_value_by_section(dmmap_s, "proto", ip_proto);
						dmuci_set_value_by_section(dmmap_s, "iface_name", section_name(s));
						dmuci_set_value_by_section(dmmap_s, "preferred", preferred);

						if (dup_list != NULL) {
							add_dmmap_config_dup_list(dup_list, NULL, dmmap_s);
						}
					}
				}
			}
		} else {
			// Device.IP.Interface.{i}.IPv6Prefix.{i}.
			dmmap_s = get_ip_addr_section("IPv6Prefix", iface_instance, ip6prefix_addr);
			if (dmmap_s == NULL) {
				// Create a new IPv4Address instance -> Device.IP.Interface.{i}.IPv6Prefix.{i}.
				dmmap_s = create_dmmap_obj(dmctx, 1, "IP", "IPv6Prefix", s, &ip_instance);
				if (dmmap_s && DM_STRLEN(ip_instance)) {
					dmuci_set_value_by_section(dmmap_s, "address", ip6prefix_addr);
					dmuci_set_value_by_section(dmmap_s, "assign", "0");
					dmuci_set_value_by_section(dmmap_s, "proto", ip_proto);
					dmuci_set_value_by_section(dmmap_s, "iface_name", section_name(s));

					if (dup_list != NULL) {
						add_dmmap_config_dup_list(dup_list, s, dmmap_s);
					}
				}
			}
		}

		dmjson_foreach_obj_in_array(res, arrobj, ip_obj, i, 1, "ipv6-prefix-assignment") {
			char *address = dmjson_get_value(ip_obj, 1, "address");
			char *mask = dmjson_get_value(ip_obj, 1, "mask");
			if (*address == '\0' || *mask == '\0')
				continue;

			snprintf(ipv6_prefix, sizeof(ipv6_prefix), "%s/%s", address, mask);
			char *preferred = dmjson_get_value(ip_obj, 1, "preferred");

			// Device.IP.Interface.{i}.IPv6Prefix.{i}.
			dmmap_s = get_ip_addr_section("IPv6Prefix", iface_instance, ipv6_prefix);
			if (dmmap_s == NULL) {
				// Create a new IPv4Address instance -> Device.IP.Interface.{i}.IPv6Prefix.{i}.
				dmmap_s = create_dmmap_obj(dmctx, 1, "IP", "IPv6Prefix", NULL, &ip_instance);
				if (dmmap_s && DM_STRLEN(ip_instance)) {
					dmuci_set_value_by_section(dmmap_s, "address", ipv6_prefix);
					dmuci_set_value_by_section(dmmap_s, "assign", "1");
					dmuci_set_value_by_section(dmmap_s, "proto", "auto-assign");
					dmuci_set_value_by_section(dmmap_s, "iface_name", section_name(s));
					dmuci_set_value_by_section(dmmap_s, "preferred", preferred);

					if (dup_list != NULL) {
						add_dmmap_config_dup_list(dup_list, NULL, dmmap_s);
					}
				}
			}
		}

		if (res != NULL) {
			json_object_put(res);
			res = NULL;
		}
	}
}

static void delete_ip_intertace_instance(struct dmctx *ctx, void *data, char *instance)
{
	struct uci_section *int_ss = NULL, *int_stmp = NULL;
	char *iface_dev = NULL;

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

	struct uci_section *ss = NULL, *stmp = NULL;

	/* Remove "IPv4Address" child section related to this "IP.Interface." object */
	uci_path_foreach_option_eq_safe(bbfdm, "IP", "IPv4Address", "Interface", instance, stmp, ss) {
		dmuci_delete_by_section(ss, NULL, NULL);
	}

	/* Remove "IPv6Address" child section related to this "IP.Interface." object */
	uci_path_foreach_option_eq_safe(bbfdm, "IP", "IPv6Address", "Interface", instance, stmp, ss) {
		dmuci_delete_by_section(ss, NULL, NULL);
	}

	/* Remove "IPv6PrefixAddress" child section related to this "IP.Interface." object */
	uci_path_foreach_option_eq_safe(bbfdm, "IP", "IPv6PrefixAddress", "Interface", instance, stmp, ss) {
		dmuci_delete_by_section(ss, NULL, NULL);
	}

	/* Remove dmmap "IP.Interface." object */
	dmuci_delete_by_section(((struct dm_data *)data)->dmmap_section, NULL, NULL);

	uci_foreach_option_eq_safe("network", "interface", "device", iface_dev, int_stmp, int_ss) {
		char *proto = NULL;

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

		if (DM_LSTRCMP(proto, "dhcp") == 0) {
			char sec_name[128] = {0};

			snprintf(sec_name, sizeof(sec_name), "network.%s", section_name(int_ss));

			struct uci_section *dhcpv4_client_s = get_dup_section_in_dmmap_opt("DHCPv4", "Client", "__section_name__", sec_name);

			if (dhcpv4_client_s) {
				dmuci_set_value_by_section_bbfdm(dhcpv4_client_s, "__section_name__", "");
			}
		}

		if (DM_LSTRCMP(proto, "dhcpv6") == 0) {
			char sec_name[128] = {0};

			snprintf(sec_name, sizeof(sec_name), "network.%s", section_name(int_ss));

			struct uci_section *dhcpv6_client_s = get_dup_section_in_dmmap_opt("DHCPv6", "Client", "__section_name__", sec_name);

			if (dhcpv6_client_s) {
				dmuci_set_value_by_section_bbfdm(dhcpv6_client_s, "__section_name__", "");
			}
		}

		if (DM_LSTRNCMP(proto, "ppp", 3) == 0) {
			char sec_name[32] = {0};

			snprintf(sec_name, sizeof(sec_name), "network.%s", section_name(int_ss));

			struct uci_section *ppp_s = get_dup_section_in_dmmap_opt("PPP", "Interface", "__section_name__", sec_name);

			if (ppp_s) {
				dmuci_set_value_by_section_bbfdm(ppp_s, "__section_name__", "");
			}
		}

		/* Remove Firewall zone section related to this "IP.Interface." object */
		uci_foreach_option_eq_safe("firewall", "zone", "name", section_name(int_ss), stmp, ss) {
			dmuci_delete_by_section(ss, NULL, NULL);
		}

		/* remove interface section */
		dmuci_delete_by_section(int_ss, NULL, NULL);
	}
}

static int delObjIPInterfaceIPv6(void *data, unsigned char del_action, const char *option_name)
{
	char *proto = NULL, *assign = NULL, *link_local = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "assign", &assign);
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "link_local", &link_local);
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "proto", &proto);

	if ((DM_LSTRCMP(assign, "1") == 0) || (DM_LSTRCMP(link_local, "1") == 0) || DM_LSTRCMP(proto, "static") != 0)
		return FAULT_9001;

	if (!is_main_interface_sec(data)) {
		dmuci_delete_by_section(((struct dm_data *)data)->config_section, NULL, NULL);
	} 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 void dmmap_synchronizeIPInterface(struct dmctx *dmctx)
{
	struct uci_section *interface_s = NULL;

	bbfdm_create_empty_file("/etc/bbfdm/dmmap/IP");

	// Device.IP.Interface.{i}.
	uci_foreach_sections("network", "interface", interface_s) {
		char *iface_instance = NULL;

		char *proto = NULL, *device = NULL;
		dmuci_get_value_by_section_string(interface_s, "proto", &proto);
		dmuci_get_value_by_section_string(interface_s, "device", &device);

		if (strcmp(section_name(interface_s), "loopback") == 0 ||
				DM_STRLEN(proto) == 0 ||
				ip___is_gre_protocols(proto) ||
				DM_STRCHR(device, '@') ||
				ip___is_ip_interface_instance_exists(section_name(interface_s), device))
			continue;

		struct uci_section *iface_dmmap_s = create_dmmap_obj(dmctx, 0, "IP", "Interface", interface_s, &iface_instance);
		if (iface_dmmap_s && DM_STRLEN(iface_instance)) {
			dmuci_set_value_by_section(iface_dmmap_s, "device", device);
		}

		synchronize_IPInterfaceIPv4AddressInst(dmctx, interface_s, iface_instance, NULL);
		synchronize_IPInterfaceIPv6AddressInst(dmctx, interface_s, iface_instance, NULL);
		synchronize_IPInterfaceIPv6PrefixInst(dmctx, interface_s, iface_instance, NULL);
	}

	dmuci_commit_package_bbfdm("IP");
}

/*************************************************************
* ENTRY METHOD
**************************************************************/
static int browseIPInterfaceInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	return generic_browse(dmctx, parent_node, prev_data, prev_instance);
}

static int browseIPInterfaceIPv4AddressInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	char *instance = NULL;
	struct dm_data *p = NULL;
	LIST_HEAD(dup_list);

	synchronize_IPInterfaceIPv4AddressInst(dmctx, ((struct dm_data *)prev_data)->config_section, prev_instance, &dup_list);
	list_for_each_entry(p, &dup_list, list) {
		instance = uci_handle_instance(dmctx, parent_node, p);
		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)p, instance) == DM_STOP)
			break;
	}

	free_dmmap_config_dup_list(&dup_list);
	return 0;
}

static int browseIPInterfaceIPv6AddressInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	char *instance = NULL;
	struct dm_data *p = NULL;
	LIST_HEAD(dup_list);

	synchronize_IPInterfaceIPv6AddressInst(dmctx, ((struct dm_data *)prev_data)->config_section, prev_instance, &dup_list);
	list_for_each_entry(p, &dup_list, list) {
		instance = uci_handle_instance(dmctx, parent_node, p);
		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)p, instance) == DM_STOP)
			break;
	}

	free_dmmap_config_dup_list(&dup_list);
	return 0;
}

static int browseIPInterfaceIPv6PrefixInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	char *instance = NULL;
	struct dm_data *p = NULL;
	LIST_HEAD(dup_list);

	synchronize_IPInterfaceIPv6PrefixInst(dmctx, ((struct dm_data *)prev_data)->config_section, prev_instance, &dup_list);
	list_for_each_entry(p, &dup_list, list) {
		instance = uci_handle_instance(dmctx, parent_node, p);
		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)p, instance) == DM_STOP)
			break;
	}

	free_dmmap_config_dup_list(&dup_list);
	return 0;
}

static int browseIPActivePortInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	ActivePort *entry = NULL;
	struct dm_data curr_data = {0};
	char *inst = NULL;
	int id = 0;

	list_for_each_entry(entry, &port_list, list) {
		curr_data.additional_data = entry;

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

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

	return 0;
}

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

	snprintf(sec_name, sizeof(sec_name), "iface%s", *instance);

	// Create and Set default config option
	dmuci_add_section("network", "interface", &curr_data->config_section);
	dmuci_rename_section_by_section(curr_data->config_section, sec_name);

	dmuci_set_value_by_section(curr_data->config_section, "proto", "none");
	dmuci_set_value_by_section(curr_data->config_section, "disabled", "1");
	dmuci_set_value_by_section(curr_data->config_section, "device", sec_name);

	dmuci_set_value_by_section(curr_data->dmmap_section, "device", sec_name);

	// Firewall zone section
	firewall__create_zone_section(sec_name);

	return 0;
}

static int delObjIPInterface(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	delete_ip_intertace_instance(ctx, data, instance);
	return 0;
}

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

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

	snprintf(sec_name, sizeof(sec_name), "%s_ipv4_%s", section_name(((struct dm_data *)data)->config_section), *instance);

	// Create and Set default config option
	dmuci_add_section("network", "interface", &curr_data->config_section);
	dmuci_rename_section_by_section(curr_data->config_section, sec_name);

	dmuci_set_value_by_section(curr_data->config_section, "device", device);
	dmuci_set_value_by_section(curr_data->config_section, "proto", "static");
	dmuci_set_value_by_section(curr_data->config_section, "disabled", "1");

	// Firewall : add this new interface to zone->network list
	add_network_to_firewall_zone_network_list(section_name(((struct dm_data *)data)->config_section), sec_name);

	// dmmap is already created by the core, Set default dmmap option if needed
	dmuci_set_value_by_section(curr_data->dmmap_section, "enable", "0");
	dmuci_set_value_by_section(curr_data->dmmap_section, "proto", "static");

	return 0;
}

static int delObjIPInterfaceIPv4Address(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_LSTRCMP(proto, "static") != 0)
		return FAULT_9001;

	if (!is_main_interface_sec(data)) {
		dmuci_delete_by_section(((struct dm_data *)data)->config_section, NULL, NULL);
	} else {
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "ipaddr", "");
		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "netmask", "");
	}

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

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

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

	snprintf(sec_name, sizeof(sec_name), "%s_ipv6_%s", section_name(((struct dm_data *)data)->config_section), *instance);

	// Create and Set default config option
	dmuci_add_section("network", "interface", &curr_data->config_section);
	dmuci_rename_section_by_section(curr_data->config_section, sec_name);

	dmuci_set_value_by_section(curr_data->config_section, "device", device);
	dmuci_set_value_by_section(curr_data->config_section, "proto", "static");
	dmuci_set_value_by_section(curr_data->config_section, "ip6addr", "::");
	dmuci_set_value_by_section(curr_data->config_section, "disabled", "1");

	// Firewall : add this new interface to zone->network list
	add_network_to_firewall_zone_network_list(section_name(((struct dm_data *)data)->config_section), sec_name);

	// dmmap is already created by the core, Set default dmmap option if needed
	dmuci_set_value_by_section(curr_data->dmmap_section, "proto", "static");
	dmuci_set_value_by_section(curr_data->dmmap_section, "address", "::");
	dmuci_set_value_by_section(curr_data->dmmap_section, "assign", "0");

	return 0;
}

static int delObjIPInterfaceIPv6Address(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	return delObjIPInterfaceIPv6(data, del_action, "ip6addr");
}

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

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

	snprintf(sec_name, sizeof(sec_name), "%s_ipv6_prefix_%s", section_name(((struct dm_data *)data)->config_section), *instance);

	// Create and Set default config option
	dmuci_add_section("network", "interface", &curr_data->config_section);
	dmuci_rename_section_by_section(curr_data->config_section, sec_name);

	dmuci_set_value_by_section(curr_data->config_section, "device", device);
	dmuci_set_value_by_section(curr_data->config_section, "proto", "static");
	dmuci_set_value_by_section(curr_data->config_section, "ip6prefix", "::/64");
	dmuci_set_value_by_section(curr_data->config_section, "disabled", "1");

	// Firewall : add this new interface to zone->network list
	add_network_to_firewall_zone_network_list(section_name(((struct dm_data *)data)->config_section), sec_name);

	// dmmap is already created by the core, Set default dmmap option if needed
	dmuci_set_value_by_section(curr_data->dmmap_section, "proto", "static");
	dmuci_set_value_by_section(curr_data->dmmap_section, "address", "::/64");
	dmuci_set_value_by_section(curr_data->dmmap_section, "assign", "0");

	return 0;
}

static int delObjIPInterfaceIPv6Prefix(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	return delObjIPInterfaceIPv6(data, del_action, "ip6prefix");
}

/*************************************************************
* GET & SET PARAM
**************************************************************/
static int get_IP_IPv4Capable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = folder_exists("/proc/sys/net/ipv4") ? "1" : "0";
	return 0;
}

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

static int set_IP_IPv4Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			break;
		case VALUESET:
			break;
	}

	return 0;
}

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

static int get_IP_IPv6Capable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = folder_exists("/proc/sys/net/ipv6") ? "1" : "0";
	return 0;
}

static int get_IP_IPv6Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *ipv6 = NULL;

	get_sysctl_disable_ipv6_per_device("all", &ipv6);
	*value = (DM_LSTRCMP(ipv6, "1") == 0) ? "0" : "1";
	return 0;
}

static int set_IP_IPv6Enable(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);
			set_sysctl_disable_ipv6_per_device("all", b);
			break;
	}
	return 0;
}

static int get_IP_IPv6Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	get_IP_IPv6Enable(refparam, ctx, data, instance, value);
	*value = (DM_LSTRCMP(*value, "1") == 0) ? "Enabled" : "Disabled";
	return 0;
}

/*#Device.IP.ULAPrefix!UCI:network/globals,globals/ula_prefix*/
static int get_IP_ULAPrefix(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_option_value_string("network", "globals", "ula_prefix", value);
	return 0;
}

static int set_IP_ULAPrefix(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 49, NULL, IPv6Prefix))
				return FAULT_9007;
			break;
		case VALUESET:
			dmuci_set_value("network", "globals", "ula_prefix", value);
			break;
	}
	return 0;
}

static int get_IP_InterfaceNumberOfEntries(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_IP_ActivePortNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	int cnt = get_number_of_entries(ctx, data, instance, browseIPActivePortInst);
	dmasprintf(value, "%d", cnt);
	return 0;
}

/*#Device.IP.Interface.{i}.Enable!UCI:network/interface,@i-1/disabled*/
static int get_IPInterface_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *s = NULL;
	char *device = NULL;

	*value = dmstrdup("0");

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

	uci_foreach_option_eq("network", "interface", "device", device, s) {
		char *disabled = NULL;

		dmuci_get_value_by_section_string(s, "disabled", &disabled);

		if (DM_STRCMP(disabled, "1") != 0) {
			*value = dmstrdup("1");
			break;
		}
	}

	return 0;
}

static int set_IPInterface_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *device = 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, "device", &device);
			ip___update_child_interfaces(device, "disabled", b ? "0" : "1");
			break;
	}
	return 0;
}

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

static int set_IPInterface_IPv4Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			break;
		case VALUESET:
			break;
	}
	return 0;
}

static int get_IPInterface_IPv6Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *device = get_device(section_name(((struct dm_data *)data)->config_section));
	struct uci_section *dev_s = get_dup_section_in_config_opt("network", "device", "name", device);
	*value = dmuci_get_value_by_section_fallback_def(dev_s, "ipv6", "1");
	return 0;
}

static int set_IPInterface_IPv6Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct uci_section *dev_s = NULL;
	char *device = NULL;
	bool b;

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			break;
		case VALUESET:
			string_to_bool(value, &b);
			device = get_device(section_name(((struct dm_data *)data)->config_section));
			dev_s = get_dup_section_in_config_opt("network", "device", "name", device);
			dmuci_set_value_by_section(dev_s, "ipv6", b ? "1" : "0");
			break;
	}
	return 0;
}

/*#Device.IP.Interface.{i}.ULAEnable!UCI:network/interface,@i-1/ula*/
static int get_IPInterface_ULAEnable(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, "ula", "1");
	return 0;
}

static int set_IPInterface_ULAEnable(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)->config_section, "ula", b ? "1" : "0");
			break;
	}
	return 0;
}

// ifstatus = Down and devstatus = Down => Status=Down
// ifstatus = Down and devstatus = Up => Status=Dormant
// ifstatus = Up and devstatus = Up => Status=Up
// ifstatus not available => Status=Unknown
// devstatus != Up => Status=LowerLayerDown
static int get_IPInterface_Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	json_object *res = NULL;
	char *if_name = section_name(((struct dm_data *)data)->config_section);
	char *device = get_device(if_name);
	char *devstatus = NULL;
	bool ifstatus = false;

	dmubus_call("network.interface", "status", UBUS_ARGS{{"interface", if_name, String}}, 1, &res);
	DM_ASSERT(res, *value = dmstrdup("Unknown"));
	string_to_bool(dmjson_get_value(res, 1, "up"), &ifstatus);

	get_net_device_status(device, &devstatus);

	if ((ifstatus == false) && (DM_STRNCMP(devstatus, "Up", 2) == 0)) {
		*value = dmstrdup("Dormant");
	} else if (DM_STRNCMP(devstatus, "Up", 2) != 0) {
		*value = dmstrdup("LowerLayerDown");
	} else {
		*value = devstatus;
	}

	return 0;
}

/*#Device.IP.Interface.{i}.Alias!UCI:dmmap_network/interface,@i-1/ip_int_alias*/
static int get_IPInterface_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return bbf_get_alias(ctx, ((struct dm_data *)data)->dmmap_section, "Alias", section_name(((struct dm_data *)data)->config_section), value);
}

static int set_IPInterface_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_IPInterface_Name(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmstrdup(section_name(((struct dm_data *)data)->config_section));
	return 0;
}

/*#Device.IP.Interface.{i}.LastChange!UBUS:network.interface/status/interface,@Name/uptime*/
static int get_IPInterface_LastChange(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	json_object *res = NULL;

	char *if_name = section_name(((struct dm_data *)data)->config_section);
	dmubus_call("network.interface", "status", UBUS_ARGS{{"interface", if_name, String}}, 1, &res);
	DM_ASSERT(res, *value = dmstrdup("0"));
	*value = dmjson_get_value(res, 1, "uptime");
	return 0;
}

static int get_IPInterface_LowerLayers(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char buf[1024] = {0};
	char *proto = NULL;

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

	if (DM_LSTRNCMP(proto, "ppp", 3) == 0) {
		struct uci_section *ppp_s = NULL;
		char sec_name[32] = {0};

		snprintf(sec_name, sizeof(sec_name), "network.%s", section_name(((struct dm_data *)data)->config_section));

		ppp_s = get_dup_section_in_dmmap_opt("PPP", "Interface", "__section_name__", sec_name);
		if (ppp_s) {
			char *ppp_instance = NULL;

			dmuci_get_value_by_section_string(ppp_s, "__instance__", &ppp_instance);

			if (DM_STRLEN(ppp_instance)) {
				snprintf(buf, sizeof(buf), "Device.PPP.Interface.%s", ppp_instance);
			}
		}
	} else {
		char *device = get_device(section_name(((struct dm_data *)data)->config_section));
		if (DM_STRLEN(device) == 0) {
			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "device", &device);
			if (DM_STRLEN(device) == 0)
				return 0;
		}

		ctx->bbfdm_api_version = BBFDM_API_V1; // This set is necessary to use the new design to resolve reference

		bbfdm_get_references(ctx, MATCH_FIRST, "Device.Ethernet."BBF_VENDOR_PREFIX"MACVLAN.", "Name", device, buf, sizeof(buf));
		bbfdm_get_references(ctx, MATCH_FIRST, "Device.Ethernet.VLANTermination.", "Name", device, buf, sizeof(buf));
		bbfdm_get_references(ctx, MATCH_FIRST, "Device.Ethernet.Link.", "Name", device, buf, sizeof(buf));

		if ((DM_STRLEN(device) > 5) && DM_LSTRNCMP(device, "gre", 3) == 0) {
			// gre device name is of the form gre4-<iface> or gre6-<iface>
			bbfdm_get_references(ctx, MATCH_FIRST, "Device.GRE.Tunnel.*.Interface.", "Name", device + 5, buf, sizeof(buf));
		}
	}

	// Store LowerLayers value
	dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "LowerLayers", buf);

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

static int set_IPInterface_LowerLayers(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *allowed_objects[] = {
			"Device.PPP.Interface.",
			"Device.Ethernet."BBF_VENDOR_PREFIX"MACVLAN",
			"Device.Ethernet.VLANTermination.",
			"Device.Ethernet.Link.",
			"Device.GRE.Tunnel.*.Interface.",
			NULL};
	char *curr_device = NULL;
	struct dm_reference reference = {0};

	bbfdm_get_reference_linker(ctx, value, &reference);

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_string_list(ctx, reference.path, -1, -1, 1024, -1, -1, NULL, NULL))
				return FAULT_9007;

			if (dm_validate_allowed_objects(ctx, &reference, allowed_objects))
				return FAULT_9007;

			break;
		case VALUESET:
			// Store LowerLayers value under dmmap_network section
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "LowerLayers", reference.path);

			if (DM_STRLEN(reference.value) == 0) {
				char *curr_proto = NULL;

				// Update device option
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "device", section_name(((struct dm_data *)data)->config_section));
				dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "device", section_name(((struct dm_data *)data)->config_section));

				dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "proto", &curr_proto);
				if (DM_LSTRNCMP(curr_proto, "ppp", 3) == 0) {
					struct uci_section *ppp_s = NULL;
					char sec_name[32] = {0};

					snprintf(sec_name, sizeof(sec_name), "network.%s", section_name(((struct dm_data *)data)->config_section));

					ppp_s = get_dup_section_in_dmmap_opt("PPP", "Interface", "__section_name__", sec_name);
					dmuci_set_value_by_section_bbfdm(ppp_s, "__section_name__", "");

					dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "proto", "none");
					ppp___reset_options(((struct dm_data *)data)->config_section);
				}
				return 0;
			}

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

			if (DM_STRNCMP(reference.path, "Device.PPP.Interface.", strlen("Device.PPP.Interface.")) == 0) {
				struct uci_section *ppp_s = get_dup_section_in_dmmap_opt("PPP", "Interface", "Name", reference.value);
				if (ppp_s) {
					char *new_device = NULL;
					char sec_name[32] = {0};

					snprintf(sec_name, sizeof(sec_name), "network.%s", section_name(((struct dm_data *)data)->config_section));

					dmuci_get_value_by_section_string(ppp_s, "device", &new_device);
					if (DM_STRLEN(new_device)) {
						dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "device", new_device);
						dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "device", new_device);
						ip___update_child_interfaces(curr_device, "device", new_device);
					}

					dmuci_set_value_by_section_bbfdm(ppp_s, "__section_name__", sec_name);
					ppp___update_sections(ppp_s, ((struct dm_data *)data)->config_section);
				}
			} else if (DM_STRNCMP(reference.path, "Device.GRE.Tunnel.", strlen("Device.GRE.Tunnel.")) == 0) {
				// get gre device name for provided interface in gre_device
				struct uci_section *iface_s = get_origin_section_from_config("network", "interface", reference.value);
				char gre_device[32] = {0};

				gre___get_tunnel_system_name(iface_s, &gre_device[0], sizeof(gre_device));

				if (DM_STRLEN(gre_device) > 0) {
					dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "device", gre_device);
					dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "device", gre_device);
					ip___update_child_interfaces(curr_device, "device", gre_device);
				}
			} else {
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "device", reference.value);
				dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "device", reference.value);
				ip___update_child_interfaces(curr_device, "device", reference.value);
			}

			break;
	}
	return 0;
}

static int get_IPInterface_Router(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "Router", value);

	if ((*value)[0] == '\0') {
		char *ip4table = NULL;
		char linker[32] = {0};

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

		snprintf(linker, sizeof(linker), "route_table-%s", DM_STRLEN(ip4table) ? ip4table : "254");
		_bbfdm_get_references(ctx, "Device.Routing.Router.", "Alias", linker, value);

		// Store LowerLayers value
		dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "Router", *value);
	}

	return 0;
}

static int set_IPInterface_Router(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *allowed_objects[] = {"Device.Routing.Router.", NULL};
	struct dm_reference reference = {0};
	struct uci_section *s = NULL;
	char *device = NULL;

	bbfdm_get_reference_linker(ctx, value, &reference);

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

			if (dm_validate_allowed_objects(ctx, &reference, allowed_objects))
				return FAULT_9007;

			break;
		case VALUESET:
			if (DM_STRLEN(reference.value) == 0)
				return FAULT_9007;

			char *rt_table = DM_STRCHR(reference.value, '-'); // Get rt_table 'X' which is linker from Alias prefix 'route_table-X'
			if (!rt_table)
				return FAULT_9007;

			// Store LowerLayers value
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "Router", reference.path);

			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "ip4table", rt_table + 1);
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "ip6table", rt_table + 1);

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

			uci_foreach_option_eq("network", "interface", "device", device, s) {
				dmuci_set_value_by_section(s, "ip4table", rt_table + 1);
				dmuci_set_value_by_section(s, "ip6table", rt_table + 1);
			}
			break;
	}
	return 0;
}

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

static int set_IPInterface_Reset(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			break;
		case VALUESET:
			/* Reset can disrupt on-going cwmp session, so this parameter must be
			 * taken care by cwmp internally.
			 */
			break;
	}
	return 0;
}

/*#Device.IP.Interface.{i}.MaxMTUSize!SYSFS:/sys/class/net/@Name/mtu*/
static int get_IPInterface_MaxMTUSize(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	get_ip_iface_sysfs(data, "mtu", value);
	if (*value && **value != '0')
		return 0;

	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "mtu", "1500");
	return 0;
}

static int set_IPInterface_MaxMTUSize(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *device = NULL;
	struct uci_section *s = NULL;

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{"64","65535"}}, 1))
				return FAULT_9007;
			break;
		case VALUESET:
			/* find the device section for this interface */
			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "device", &device);

			/* check if device is empty */
			if ((device)[0] == '\0') {
				return FAULT_9002;
			}

			/* set option MTU in device section */
			uci_foreach_option_eq("network", "device", "name", device, s) {
				dmuci_set_value_by_section(s, "mtu", value);
				break;
			}
			break;
	}
	return 0;
}

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

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

static int set_IPInterface_Loopback(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			break;
		case VALUESET:
			break;
	}
	return 0;
}

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

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

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

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

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;

			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "proto", &proto);
			if (DM_LSTRCMP(proto, "static") != 0)
				return FAULT_9007;

			break;
		case VALUESET:
			string_to_bool(value, &b);
			if (is_main_interface_sec(data)) {
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "ipaddr", "");
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "netmask", "");

				if (b) {
					char *addr = NULL, *mask = NULL;

					dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "address", &addr);
					dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "mask", &mask);

					dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "ipaddr", addr);
					dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "netmask", mask);
				}
			} else {
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "disabled", b ? "0" : "1");
			}

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

/*#Device.IP.Interface.{i}.IPv4Address.{i}.Status!UCI:network/interface,@i-1/disabled*/
static int get_IPInterfaceIPv4Address_Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	get_IPInterfaceIPv4Address_Enable(refparam, ctx, data, instance, value);
	*value = ((*value)[0] == '1') ? "Enabled" : "Disabled";
	return 0;
}

static int get_IPInterfaceIPv4Address_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_IPInterfaceIPv4Address_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_IPInterfaceIPv4Address_IPAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "address", value);
	return 0;
}

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

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

			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "proto", &proto);
			if (DM_LSTRCMP(proto, "static") != 0)
				return FAULT_9007;

			break;
		case VALUESET:
			if (is_main_interface_sec(data)) {
				char *enable = NULL;

				dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "enable", &enable);
				if (DM_STRCMP(enable, "1") == 0)
					dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "ipaddr", value);
			} else {
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "ipaddr", value);
			}

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

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

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

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

			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "proto", &proto);
			if (DM_LSTRCMP(proto, "static") != 0)
				return FAULT_9007;

			break;
		case VALUESET:
			if (is_main_interface_sec(data)) {
				char *enable = NULL;

				dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "enable", &enable);
				if (DM_STRCMP(enable, "1") == 0)
					dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "netmask", value);
			} else {
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "netmask", value);
			}

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

/*#Device.IP.Interface.{i}.IPv4Address.{i}.AddressingType!UCI:network/interface,@i-1/proto*/
static int get_IPInterfaceIPv4Address_AddressingType(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *proto = NULL;

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

	if (DM_LSTRCMP(proto, "static") == 0)
		*value = dmstrdup("Static");
	else if (DM_LSTRNCMP(proto, "ppp", 3) == 0)
		*value = dmstrdup("IPCP");
	else
		*value = dmstrdup("DHCP");
	return 0;
}

/*#Device.IP.Interface.{i}.IPv6Address.{i}.Enable!UCI:network/interface,@i-1/disabled*/
static int get_IPInterfaceIPv6Address_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *assign = NULL, *link_local = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "assign", &assign);
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "link_local", &link_local);

	if ((DM_LSTRCMP(assign, "1") == 0) || (DM_LSTRCMP(link_local, "1") == 0)) {
		*value = dmstrdup("1");
	} else {
		char *disabled = NULL;

		dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "disabled", &disabled);
		*value = (disabled && *disabled == '1') ? "0" : "1";
	}

	return 0;
}

static int set_IPInterfaceIPv6Address_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *link_local = NULL, *assign = NULL, *proto = NULL;
	bool b;

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;

			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "assign", &assign);
			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "link_local", &link_local);
			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "proto", &proto);

			if ((DM_LSTRCMP(assign, "1") == 0) || (DM_LSTRCMP(link_local, "1") == 0) || DM_LSTRCMP(proto, "static") != 0)
				return FAULT_9007;

			break;
		case VALUESET:
			string_to_bool(value, &b);
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "disabled", b ? "0" : "1");
			break;
	}
	return 0;
}

/*#Device.IP.Interface.{i}.IPv6Address.{i}.Status!UCI:network/interface,@i-1/ipv6*/
static int get_IPInterfaceIPv6Address_Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	get_IPInterfaceIPv6Address_Enable(refparam, ctx, data, instance, value);
	*value = ((*value)[0] == '1') ? "Enabled" : "Disabled";
	return 0;
}

static int get_IPInterfaceIPv6Address_IPAddressStatus(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *preferred = NULL;

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

	*value = (preferred && *preferred != '\0') ? "Preferred" : "Invalid";
	return 0;
}

/*#Device.IP.Interface.{i}.IPv6Address.{i}.Alias!UCI:dmmap_network_ipv6/intf_ipv6,@i-1/ipv6_alias*/
static int get_IPInterfaceIPv6Address_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_IPInterfaceIPv6Address_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.IP.Interface.{i}.IPv6Address.{i}.IPAddress!UCI:network/interface,@i-1/ip6addr*/
static int get_IPInterfaceIPv6Address_IPAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "address", value);
	char *mask = DM_STRCHR(*value, '/');
	if (mask) *mask = '\0';
	return 0;
}

static int set_IPInterfaceIPv6Address_IPAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *link_local = NULL, *assign = NULL, *proto = NULL;

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

			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "assign", &assign);
			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "link_local", &link_local);
			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "proto", &proto);

			if ((DM_LSTRCMP(assign, "1") == 0) || (DM_LSTRCMP(link_local, "1") == 0) || DM_LSTRCMP(proto, "static") != 0)
				return FAULT_9007;

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

static int get_IPInterfaceIPv6Address_Origin(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *assign = NULL, *link_local = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "assign", &assign);
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "link_local", &link_local);

	if ((DM_LSTRCMP(assign, "1") == 0) || (DM_LSTRCMP(link_local, "1") == 0)) {
		*value = dmstrdup("AutoConfigured");
	} else {
		char *proto;
		dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "proto", &proto);
		*value = (DM_LSTRCMP(proto, "dhcpv6") == 0) ? "DHCPv6" : "Static";
	}
	return 0;
}

static int get_IPInterfaceIPv6Address_Prefix(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *assign = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "assign", &assign);
	if (DM_LSTRCMP(assign, "1") == 0) {
		struct uci_section *dmmap_section = NULL;
		char *ip_inst = NULL, *ipv6_prefix_inst = NULL, *section_name;
		char curr_address[64] = {0}, *address = NULL;

		dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "Interface", &ip_inst);

		char *addr = NULL, *mask = NULL;
		dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "address", &addr);
		dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "mask", &mask);
		snprintf(curr_address, sizeof(curr_address), "%s/%s", addr, mask);


		dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "section_name", &section_name);
		uci_path_foreach_option_eq(bbfdm, "IP", "IPv6Prefix", "Interface", ip_inst, dmmap_section) {
			dmuci_get_value_by_section_string(dmmap_section, "address", &address);
			if (address && DM_STRCMP(address, curr_address) == 0) {
				dmuci_get_value_by_section_string(dmmap_section, "__instance__", &ipv6_prefix_inst);
				break;
			}
		}

		if (DM_STRLEN(ip_inst) && DM_STRLEN(ipv6_prefix_inst))
			dmasprintf(value, "Device.IP.Interface.%s.IPv6Prefix.%s", ip_inst, ipv6_prefix_inst);
	}
	return 0;
}

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

static int get_IPInterfaceIPv6Address_PreferredLifetime(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *preferred = NULL, local_time[32] = {0};

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

	if (preferred && *preferred && get_shift_utc_time(DM_STRTOL(preferred), local_time, sizeof(local_time)) == -1)
		return 0;

	*value = (*local_time) ? dmstrdup(local_time) : "9999-12-31T23:59:59Z";
	return 0;
}

static int set_IPInterfaceIPv6Address_PreferredLifetime(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_dateTime(ctx, value))
				return FAULT_9007;
			break;
		case VALUESET:
			break;
	}
	return 0;
}

static int get_IPInterfaceIPv6Address_ValidLifetime(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *valid = NULL, local_time[32] = {0};

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

	if (valid && *valid && get_shift_utc_time(DM_STRTOL(valid), local_time, sizeof(local_time)) == -1)
		return 0;

	*value = (*local_time) ? dmstrdup(local_time) : "9999-12-31T23:59:59Z";
	return 0;
}

static int set_IPInterfaceIPv6Address_ValidLifetime(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_dateTime(ctx, value))
				return FAULT_9007;
			break;
		case VALUESET:
			break;
	}
	return 0;
}

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

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

static int set_IPInterfaceIPv6Prefix_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *proto = NULL, *assign = NULL;
	bool b;

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;

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

			if (DM_LSTRCMP(proto, "static") != 0 || DM_LSTRCMP(assign, "1") == 0)
				return FAULT_9007;

			break;
		case VALUESET:
			string_to_bool(value, &b);
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "disabled", b ? "0" : "1");
			break;
	}
	return 0;
}

/*#Device.IP.Interface.{i}.IPv6Prefix.{i}.Status!UCI:network/interface,@i-1/ipv6*/
static int get_IPInterfaceIPv6Prefix_Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	get_IPInterfaceIPv6Prefix_Enable(refparam, ctx, data, instance, value);
	*value = ((*value)[0] == '1') ? "Enabled" : "Disabled";
	return 0;
}

static int get_IPInterfaceIPv6Prefix_PrefixStatus(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *preferred = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "preferred");
	*value = (preferred && *preferred) ? "Preferred" : "Invalid";
	return 0;
}

/*#Device.IP.Interface.{i}.IPv6Prefix.{i}.Alias!UCI:dmmap_network_ipv6_prefix/intf_ipv6_prefix,@i-1/ipv6_prefix_alias*/
static int get_IPInterfaceIPv6Prefix_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_IPInterfaceIPv6Prefix_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.IP.Interface.{i}.IPv6Prefix.{i}.Prefix!UCI:network/interface,@i-1/ip6prefix*/
static int get_IPInterfaceIPv6Prefix_Prefix(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "address", value);
	return 0;
}

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

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

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

			if (DM_LSTRCMP(proto, "static") != 0 || DM_LSTRCMP(assign, "1") == 0)
				return FAULT_9007;

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

static int get_IPInterfaceIPv6Prefix_Origin(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *assign = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "assign", &assign);
	if (DM_LSTRCMP(assign, "1") == 0) {
		*value = dmstrdup("AutoConfigured");
	} else {
		char *proto = NULL;
		dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "proto", &proto);
		*value = (DM_LSTRCMP(proto, "dhcpv6") == 0) ? "PrefixDelegation" : "Static";
	}
	return 0;
}

static int get_IPInterfaceIPv6Prefix_ParentPrefix(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *assign = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "assign", &assign);
	if (DM_LSTRCMP(assign, "1") == 0) {
		char *linker = NULL;

		dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "address", &linker);
		_bbfdm_get_references(ctx, "Device.IP.Interface.*.IPv6Prefix.", "ChildPrefixBits", linker, value);
	}
	return 0;
}

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

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

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

static int get_IPInterfaceIPv6Prefix_PreferredLifetime(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char local_time[32] = {0};
	char *preferred = NULL;

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

	if (DM_STRLEN(preferred) && get_shift_utc_time(DM_STRTOL(preferred), local_time, sizeof(local_time)) == -1)
		return 0;

	*value = (*local_time) ? dmstrdup(local_time) : "9999-12-31T23:59:59Z";
	return 0;
}

static int set_IPInterfaceIPv6Prefix_PreferredLifetime(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_dateTime(ctx, value))
				return FAULT_9007;
			break;
		case VALUESET:
			break;
	}
	return 0;
}

static int get_IPInterfaceIPv6Prefix_ValidLifetime(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char local_time[32] = {0};
	char *valid = NULL;

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

	if (DM_STRLEN(valid) && get_shift_time_time(DM_STRTOL(valid), local_time, sizeof(local_time)) == -1)
		return 0;

	*value = (*local_time) ? dmstrdup(local_time) : "9999-12-31T23:59:59Z";
	return 0;
}

static int set_IPInterfaceIPv6Prefix_ValidLifetime(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_dateTime(ctx, value))
				return FAULT_9007;
			break;
		case VALUESET:
			break;
	}
	return 0;
}

/*#Device.IP.Interface.{i}.Stats.BytesSent!SYSFS:/sys/class/net/@Name/statistics/tx_bytes*/
static int get_IPInterfaceStats_BytesSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return get_ip_iface_sysfs(data, "statistics/tx_bytes", value);
}

/*#Device.IP.Interface.{i}.Stats.BytesReceived!SYSFS:/sys/class/net/@Name/statistics/rx_bytes*/
static int get_IPInterfaceStats_BytesReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return get_ip_iface_sysfs(data, "statistics/rx_bytes", value);
}

/*#Device.IP.Interface.{i}.Stats.PacketsSent!SYSFS:/sys/class/net/@Name/statistics/tx_packets*/
static int get_IPInterfaceStats_PacketsSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return get_ip_iface_sysfs(data, "statistics/tx_packets", value);
}

/*#Device.IP.Interface.{i}.Stats.PacketsReceived!SYSFS:/sys/class/net/@Name/statistics/rx_packets*/
static int get_IPInterfaceStats_PacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return get_ip_iface_sysfs(data, "statistics/rx_packets", value);
}

/*#Device.IP.Interface.{i}.Stats.ErrorsSent!SYSFS:/sys/class/net/@Name/statistics/tx_errors*/
static int get_IPInterfaceStats_ErrorsSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return get_ip_iface_sysfs(data, "statistics/tx_errors", value);
}

/*#Device.IP.Interface.{i}.Stats.ErrorsReceived!SYSFS:/sys/class/net/@Name/statistics/rx_errors*/
static int get_IPInterfaceStats_ErrorsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return get_ip_iface_sysfs(data, "statistics/rx_errors", value);
}

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

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

/*#Device.IP.Interface.{i}.Stats.DiscardPacketsSent!SYSFS:/sys/class/net/@Name/statistics/tx_dropped*/
static int get_IPInterfaceStats_DiscardPacketsSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return get_ip_iface_sysfs(data, "statistics/tx_dropped", value);
}

/*#Device.IP.Interface.{i}.Stats.DiscardPacketsReceived!SYSFS:/sys/class/net/@Name/statistics/rx_dropped*/
static int get_IPInterfaceStats_DiscardPacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return get_ip_iface_sysfs(data, "statistics/rx_dropped", value);
}

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

/*#Device.IP.Interface.{i}.Stats.MulticastPacketsReceived!SYSFS:/sys/class/net/@Name/statistics/multicast*/
static int get_IPInterfaceStats_MulticastPacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return get_ip_iface_sysfs(data, "statistics/multicast", value);
}

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

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

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

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

static int get_IP_ActivePort_LocalPort(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmasprintf(value, "%u", ((ActivePort *)((struct dm_data *)data)->additional_data)->local_port);
	return 0;
}

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

static int get_IP_ActivePort_RemotePort(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmasprintf(value, "%u", ((ActivePort *)((struct dm_data *)data)->additional_data)->remote_port);
	return 0;
}

static int get_IP_ActivePort_Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	unsigned int state = (((ActivePort *)((struct dm_data *)data)->additional_data)->state);

	switch (state) {
		case 1:
			*value = dmstrdup("ESTABLISHED");
			break;
		case 10:
			*value = dmstrdup("LISTEN");
			break;
		default:
			*value = dmstrdup("");
			break;
	}

	return 0;
}
/*************************************************************
 * OPERATE COMMANDS
 *************************************************************/
static int operate_IPInterface_Reset(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char interface_obj[64] = {0};

	snprintf(interface_obj, sizeof(interface_obj), "network.interface.%s", section_name((((struct dm_data *)data)->config_section)));
	dmubus_call_set(interface_obj, "down", UBUS_ARGS{0}, 0);
	dmubus_call_set(interface_obj, "up", UBUS_ARGS{0}, 0);

	return 0;
}

/*************************************************************
* Init & Clean Module
**************************************************************/
static void cwmp_session_event_handler(struct ubus_context *ctx, struct ubus_event_handler *ev,
				const char *type, struct blob_attr *msg)
{
	if (!msg)
		return;

	const struct blobmsg_policy p[1] = {
		{"status", BLOBMSG_TYPE_STRING}
	};

	struct blob_attr *tb[1] = {NULL};
	blobmsg_parse(p, 1, tb, blob_data(msg), blob_len(msg));

	if (!tb[0])
		return;

	const char *status = blobmsg_get_string(tb[0]);

	if (DM_STRCMP(status, "start") == 0) {
		g_icwmp_session_running = true;
	} else if (DM_STRCMP(status, "end") == 0) {
		g_icwmp_session_running = false;
	}
}

int init_ip_module(void *data)
{
	struct dmctx bbf_ctx = {0};
	struct bbfdm_context *dm_ctx = data;

	bbf_ctx_init(&bbf_ctx, NULL);
	dmmap_synchronizeIPInterface(&bbf_ctx);
	bbf_ctx_clean(&bbf_ctx);

	INIT_LIST_HEAD(&port_list);
	init_port_list();

	if (dm_ctx != NULL && dm_ctx->ubus_ctx != NULL) {
		memset(&ev_cwmp_session, 0, sizeof(ev_cwmp_session));
		ev_cwmp_session.cb = cwmp_session_event_handler,

		ubus_register_event_handler(dm_ctx->ubus_ctx, &ev_cwmp_session, "icwmp.session");

		refresh_port_timer.cb = refresh_ip_active_port;
		uloop_timeout_set(&refresh_port_timer, 30 * 1000);
	}

	return 0;
}

int clean_ip_module(void *data)
{
	struct bbfdm_context *dm_ctx = data;

	if (dm_ctx != NULL && dm_ctx->ubus_ctx != NULL) {
		ubus_unregister_event_handler(dm_ctx->ubus_ctx, &ev_cwmp_session);
		uloop_timeout_cancel(&refresh_port_timer);
	}

	free_port_list();
	return 0;
}

int sync_ip_module(void *data)
{
	struct bbfdm_context *bbfdm_ctx = (struct bbfdm_context *)data;
	if (bbfdm_ctx == NULL)
		return 0;

	if (DM_STRCMP(bbfdm_ctx->uci_change_proto, "external") == 0 && !list_empty(&bbfdm_ctx->changed_uci)) {
		struct apply_handler_node *node = NULL;
		list_for_each_entry(node, &bbfdm_ctx->changed_uci, list) {
			if (DM_STRCMP(node->file_path, "/etc/config/network") == 0) {
				BBF_DEBUG("Syncing ip module config");

				FILE *fp = fopen("/etc/bbfdm/dmmap/IP", "w"); // empty file
				if (fp) {
					fclose(fp);
				}

				struct dmctx bbf_ctx = {0};
				bbf_ctx_init(&bbf_ctx, NULL);
				dmmap_synchronizeIPInterface(&bbf_ctx);
				bbf_ctx_clean(&bbf_ctx);
				break;
			}
		}
	}

	return 0;
}

/**********************************************************************************************************************************
*                                            OBJ & PARAM DEFINITION
***********************************************************************************************************************************/
/* *** Device. *** */
DMOBJ tDeviceIPObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"IP", &DMREAD, NULL, NULL, "file:/etc/config/network", NULL, NULL, NULL, tIPObj, tIPParams, NULL, BBFDM_BOTH, NULL},
{0}
};

/* *** Device.IP. *** */
DMOBJ tIPObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"Interface", &DMWRITE, addObjIPInterface, delObjIPInterface, NULL, browseIPInterfaceInst, NULL, NULL, tIPInterfaceObj, tIPInterfaceParams, NULL, BBFDM_BOTH, NULL},
{"ActivePort", &DMREAD, NULL, NULL, NULL, browseIPActivePortInst, NULL, NULL, NULL, tIPActivePortParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tIPParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"IPv4Capable", &DMREAD, DMT_BOOL, get_IP_IPv4Capable, NULL, BBFDM_BOTH},
{"IPv4Enable", &DMWRITE, DMT_BOOL, get_IP_IPv4Enable, set_IP_IPv4Enable, BBFDM_BOTH},
{"IPv4Status", &DMREAD, DMT_STRING, get_IP_IPv4Status, NULL, BBFDM_BOTH},
{"IPv6Capable", &DMREAD, DMT_BOOL, get_IP_IPv6Capable, NULL, BBFDM_BOTH},
{"IPv6Enable", &DMWRITE, DMT_BOOL, get_IP_IPv6Enable, set_IP_IPv6Enable, BBFDM_BOTH},
{"IPv6Status", &DMREAD, DMT_STRING, get_IP_IPv6Status, NULL, BBFDM_BOTH},
{"ULAPrefix", &DMWRITE, DMT_STRING, get_IP_ULAPrefix, set_IP_ULAPrefix, BBFDM_BOTH},
{"InterfaceNumberOfEntries", &DMREAD, DMT_UNINT, get_IP_InterfaceNumberOfEntries, NULL, BBFDM_BOTH},
{"ActivePortNumberOfEntries", &DMREAD, DMT_UNINT, get_IP_ActivePortNumberOfEntries, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.IP.Interface.{i}. *** */
DMOBJ tIPInterfaceObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"IPv4Address", &DMWRITE, addObjIPInterfaceIPv4Address, delObjIPInterfaceIPv4Address, NULL, browseIPInterfaceIPv4AddressInst, NULL, NULL, NULL, tIPInterfaceIPv4AddressParams, NULL, BBFDM_BOTH, NULL},
{"IPv6Address", &DMWRITE, addObjIPInterfaceIPv6Address, delObjIPInterfaceIPv6Address, NULL, browseIPInterfaceIPv6AddressInst, NULL, NULL, NULL, tIPInterfaceIPv6AddressParams, NULL, BBFDM_BOTH, NULL},
{"IPv6Prefix", &DMWRITE, addObjIPInterfaceIPv6Prefix, delObjIPInterfaceIPv6Prefix, NULL, browseIPInterfaceIPv6PrefixInst, NULL, NULL, NULL, tIPInterfaceIPv6PrefixParams, NULL, BBFDM_BOTH, NULL},
{"Stats", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, NULL, tIPInterfaceStatsParams, NULL, BBFDM_BOTH},
{0}
};

DMLEAF tIPInterfaceParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_IPInterface_Enable, set_IPInterface_Enable, BBFDM_BOTH},
{"IPv4Enable", &DMWRITE, DMT_BOOL, get_IPInterface_IPv4Enable, set_IPInterface_IPv4Enable, BBFDM_BOTH},
{"IPv6Enable", &DMWRITE, DMT_BOOL, get_IPInterface_IPv6Enable, set_IPInterface_IPv6Enable, BBFDM_BOTH},
{"ULAEnable", &DMWRITE, DMT_BOOL, get_IPInterface_ULAEnable, set_IPInterface_ULAEnable, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_IPInterface_Status, NULL, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_IPInterface_Alias, set_IPInterface_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Name", &DMREAD, DMT_STRING, get_IPInterface_Name, NULL, BBFDM_BOTH, DM_FLAG_UNIQUE|DM_FLAG_LINKER},
{"LastChange", &DMREAD, DMT_UNINT, get_IPInterface_LastChange, NULL, BBFDM_BOTH},
{"LowerLayers", &DMWRITE, DMT_STRING, get_IPInterface_LowerLayers, set_IPInterface_LowerLayers, BBFDM_BOTH, DM_FLAG_REFERENCE},
{"Router", &DMWRITE, DMT_STRING, get_IPInterface_Router, set_IPInterface_Router, BBFDM_BOTH, DM_FLAG_REFERENCE},
{"Reset", &DMWRITE, DMT_BOOL, get_IPInterface_Reset, set_IPInterface_Reset, BBFDM_CWMP},
{"MaxMTUSize", &DMWRITE, DMT_UNINT, get_IPInterface_MaxMTUSize, set_IPInterface_MaxMTUSize, BBFDM_BOTH},
{"Type", &DMREAD, DMT_STRING, get_IPInterface_Type, NULL, BBFDM_BOTH},
{"Loopback", &DMWRITE, DMT_BOOL, get_IPInterface_Loopback, set_IPInterface_Loopback, BBFDM_BOTH},
{"IPv4AddressNumberOfEntries", &DMREAD, DMT_UNINT, get_IPInterface_IPv4AddressNumberOfEntries, NULL, BBFDM_BOTH},
{"IPv6AddressNumberOfEntries", &DMREAD, DMT_UNINT, get_IPInterface_IPv6AddressNumberOfEntries, NULL, BBFDM_BOTH},
{"IPv6PrefixNumberOfEntries", &DMREAD, DMT_UNINT, get_IPInterface_IPv6PrefixNumberOfEntries, NULL, BBFDM_BOTH},
//{"AutoIPEnable", &DMWRITE, DMT_BOOL, get_IPInterface_AutoIPEnable, set_IPInterface_AutoIPEnable, BBFDM_BOTH},
{"Reset()", &DMSYNC, DMT_COMMAND, NULL, operate_IPInterface_Reset, BBFDM_USP},
{0}
};

/* *** Device.IP.Interface.{i}.IPv4Address.{i}. *** */
DMLEAF tIPInterfaceIPv4AddressParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_IPInterfaceIPv4Address_Enable, set_IPInterfaceIPv4Address_Enable, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_IPInterfaceIPv4Address_Status, NULL, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_IPInterfaceIPv4Address_Alias, set_IPInterfaceIPv4Address_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"IPAddress", &DMWRITE, DMT_STRING, get_IPInterfaceIPv4Address_IPAddress, set_IPInterfaceIPv4Address_IPAddress, BBFDM_BOTH, DM_FLAG_UNIQUE|DM_FLAG_LINKER},
{"SubnetMask", &DMWRITE, DMT_STRING, get_IPInterfaceIPv4Address_SubnetMask, set_IPInterfaceIPv4Address_SubnetMask, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"AddressingType", &DMREAD, DMT_STRING, get_IPInterfaceIPv4Address_AddressingType, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.IP.Interface.{i}.IPv6Address.{i}. *** */
DMLEAF tIPInterfaceIPv6AddressParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_IPInterfaceIPv6Address_Enable, set_IPInterfaceIPv6Address_Enable, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_IPInterfaceIPv6Address_Status, NULL, BBFDM_BOTH},
{"IPAddressStatus", &DMREAD, DMT_STRING, get_IPInterfaceIPv6Address_IPAddressStatus, NULL, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_IPInterfaceIPv6Address_Alias, set_IPInterfaceIPv6Address_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"IPAddress", &DMWRITE, DMT_STRING, get_IPInterfaceIPv6Address_IPAddress, set_IPInterfaceIPv6Address_IPAddress, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Origin", &DMREAD, DMT_STRING, get_IPInterfaceIPv6Address_Origin, NULL, BBFDM_BOTH},
{"Prefix", &DMWRITE, DMT_STRING, get_IPInterfaceIPv6Address_Prefix, set_IPInterfaceIPv6Address_Prefix, BBFDM_BOTH},
{"PreferredLifetime", &DMWRITE, DMT_TIME, get_IPInterfaceIPv6Address_PreferredLifetime, set_IPInterfaceIPv6Address_PreferredLifetime, BBFDM_BOTH},
{"ValidLifetime", &DMWRITE, DMT_TIME, get_IPInterfaceIPv6Address_ValidLifetime, set_IPInterfaceIPv6Address_ValidLifetime, BBFDM_BOTH},
//{"Anycast", &DMWRITE, DMT_BOOL, get_IPInterfaceIPv6Address_Anycast, set_IPInterfaceIPv6Address_Anycast, BBFDM_BOTH},
{0}
};

/* *** Device.IP.Interface.{i}.IPv6Prefix.{i}. *** */
DMLEAF tIPInterfaceIPv6PrefixParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type*/
{"Enable", &DMWRITE, DMT_BOOL, get_IPInterfaceIPv6Prefix_Enable, set_IPInterfaceIPv6Prefix_Enable, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_IPInterfaceIPv6Prefix_Status, NULL, BBFDM_BOTH},
{"PrefixStatus", &DMREAD, DMT_STRING, get_IPInterfaceIPv6Prefix_PrefixStatus, NULL, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_IPInterfaceIPv6Prefix_Alias, set_IPInterfaceIPv6Prefix_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Prefix", &DMWRITE, DMT_STRING, get_IPInterfaceIPv6Prefix_Prefix, set_IPInterfaceIPv6Prefix_Prefix, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Origin", &DMREAD, DMT_STRING, get_IPInterfaceIPv6Prefix_Origin, NULL, BBFDM_BOTH},
//{"StaticType", &DMWRITE, DMT_STRING, get_IPInterfaceIPv6Prefix_StaticType, set_IPInterfaceIPv6Prefix_StaticType, BBFDM_BOTH},
{"ParentPrefix", &DMWRITE, DMT_STRING, get_IPInterfaceIPv6Prefix_ParentPrefix, set_IPInterfaceIPv6Prefix_ParentPrefix, BBFDM_BOTH, DM_FLAG_REFERENCE},
{"ChildPrefixBits", &DMWRITE, DMT_STRING, get_IPInterfaceIPv6Prefix_ChildPrefixBits, set_IPInterfaceIPv6Prefix_ChildPrefixBits, BBFDM_BOTH, DM_FLAG_LINKER},
//{"OnLink", &DMWRITE, DMT_BOOL, get_IPInterfaceIPv6Prefix_OnLink, set_IPInterfaceIPv6Prefix_OnLink, BBFDM_BOTH},
//{"Autonomous", &DMWRITE, DMT_BOOL, get_IPInterfaceIPv6Prefix_Autonomous, set_IPInterfaceIPv6Prefix_Autonomous, BBFDM_BOTH},
{"PreferredLifetime", &DMWRITE, DMT_TIME, get_IPInterfaceIPv6Prefix_PreferredLifetime, set_IPInterfaceIPv6Prefix_PreferredLifetime, BBFDM_BOTH},
{"ValidLifetime", &DMWRITE, DMT_TIME, get_IPInterfaceIPv6Prefix_ValidLifetime, set_IPInterfaceIPv6Prefix_ValidLifetime, BBFDM_BOTH},
{0}
};

/* *** Device.IP.Interface.{i}.Stats. *** */
DMLEAF tIPInterfaceStatsParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"BytesSent", &DMREAD, DMT_UNLONG, get_IPInterfaceStats_BytesSent, NULL, BBFDM_BOTH},
{"BytesReceived", &DMREAD, DMT_UNLONG, get_IPInterfaceStats_BytesReceived, NULL, BBFDM_BOTH},
{"PacketsSent", &DMREAD, DMT_UNLONG, get_IPInterfaceStats_PacketsSent, NULL, BBFDM_BOTH},
{"PacketsReceived", &DMREAD, DMT_UNLONG, get_IPInterfaceStats_PacketsReceived, NULL, BBFDM_BOTH},
{"ErrorsSent", &DMREAD, DMT_UNINT, get_IPInterfaceStats_ErrorsSent, NULL, BBFDM_BOTH},
{"ErrorsReceived", &DMREAD, DMT_UNINT, get_IPInterfaceStats_ErrorsReceived, NULL, BBFDM_BOTH},
{"UnicastPacketsSent", &DMREAD, DMT_UNLONG, get_IPInterfaceStats_UnicastPacketsSent, NULL, BBFDM_BOTH},
{"UnicastPacketsReceived", &DMREAD, DMT_UNLONG, get_IPInterfaceStats_UnicastPacketsReceived, NULL, BBFDM_BOTH},
{"DiscardPacketsSent", &DMREAD, DMT_UNINT, get_IPInterfaceStats_DiscardPacketsSent, NULL, BBFDM_BOTH},
{"DiscardPacketsReceived", &DMREAD, DMT_UNINT, get_IPInterfaceStats_DiscardPacketsReceived, NULL, BBFDM_BOTH},
{"MulticastPacketsSent", &DMREAD, DMT_UNLONG, get_IPInterfaceStats_MulticastPacketsSent, NULL, BBFDM_BOTH},
{"MulticastPacketsReceived", &DMREAD, DMT_UNLONG, get_IPInterfaceStats_MulticastPacketsReceived, NULL, BBFDM_BOTH},
{"BroadcastPacketsSent", &DMREAD, DMT_UNLONG, get_IPInterfaceStats_BroadcastPacketsSent, NULL, BBFDM_BOTH},
{"BroadcastPacketsReceived", &DMREAD, DMT_UNLONG, get_IPInterfaceStats_BroadcastPacketsReceived, NULL, BBFDM_BOTH},
{"UnknownProtoPacketsReceived", &DMREAD, DMT_UNINT, get_IPInterfaceStats_UnknownProtoPacketsReceived, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.IP.ActivePort.{i}. *** */
DMLEAF tIPActivePortParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type */
{"LocalIPAddress", &DMREAD, DMT_STRING,  get_IP_ActivePort_LocalIPAddress, NULL, BBFDM_BOTH},
{"LocalPort", &DMREAD, DMT_UNINT, get_IP_ActivePort_LocalPort, NULL, BBFDM_BOTH},
{"RemoteIPAddress", &DMREAD, DMT_STRING,  get_IP_ActivePort_RemoteIPAddress, NULL, BBFDM_BOTH},
{"RemotePort", &DMREAD, DMT_UNINT,  get_IP_ActivePort_RemotePort, NULL, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING,  get_IP_ActivePort_Status, NULL, BBFDM_BOTH},
{0}
};
