/*
 * Copyright (C) 2019-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>
 *
 */

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

#define MAX_ROUTE_LEN 512
#define RT_TABLES_FILE "/etc/iproute2/rt_tables"
#define RT_TABLES_TMP  "/etc/iproute2/.rt_tables.tmp"

struct route_args {
	char *iface;
	char *metric;
	char destination[16];
	char gateway[16];
	char mask[16];
};

struct route6_args {
	char *iface;
	char *metric;
	char destination[INET6_ADDRSTRLEN + 8];
	char gateway[INET6_ADDRSTRLEN + 8];
};

static int set_rt_table_name(int id, const char *name)                                                                                                              
{
	FILE *fp = NULL, *tmp_fp = NULL;
	char line[256] = {0};
	bool found = false;
	bool remove_entry = (DM_STRLEN(name) == 0);

	// cppcheck-suppress cert-MSC24-C
	fp = fopen(RT_TABLES_FILE, "r"); // flawfinder: ignore
	if (!fp)
		return -1;

	// cppcheck-suppress cert-MSC24-C
	tmp_fp = fopen(RT_TABLES_TMP, "w"); // flawfinder: ignore
	if (!tmp_fp) {
		fclose(fp);
		return -1;
	}

	while (fgets(line, sizeof(line), fp) != NULL) {
		int line_id = -1;
		char line_name[128] = {0};

		/* Preserve comment and blank lines as-is */
		if (line[0] == '#' || line[0] == '\n' || line[0] == '\r') {
			fputs(line, tmp_fp);
			continue;
		}

		if (sscanf(line, "%d %127s", &line_id, line_name) == 2 && line_id == id) {
			found = true;

			if (!remove_entry)
				fprintf(tmp_fp, "%d\t%s\n", id, name);
			/* else: skip line — effectively deletes the entry */
		} else {
			fputs(line, tmp_fp);
		}
	}

	/* Append only if not found and name is non-empty */
	if (!found && !remove_entry)
		fprintf(tmp_fp, "%d\t%s\n", id, name);

	fclose(fp);
	fclose(tmp_fp);

	if (rename(RT_TABLES_TMP, RT_TABLES_FILE) != 0) {
		remove(RT_TABLES_TMP);
		return -1;
	}

	return 0;
}
/************************************************************************************* 
**** function related to get_object_router_ipv4forwarding ****
**************************************************************************************/
static bool is_route_in_config(struct route_args *route)
{
	struct uci_section *s = NULL;

	uci_foreach_option_eq("network", "route", "target", route->destination, s) {
		char *mask = NULL;

		dmuci_get_value_by_section_string(s, "netmask", &mask);
		if (DM_STRLEN(mask) == 0)
			return true;

		if (DM_STRCMP(route->mask, mask) == 0)
			return true;
	}

	uci_path_foreach_sections(bbfdm, "Routing", "IPv4Forwarding", s) {
		char *target = NULL, *gateway = NULL, *device = NULL;

		dmuci_get_value_by_section_string(s, "target", &target);
		dmuci_get_value_by_section_string(s, "gateway", &gateway);
		dmuci_get_value_by_section_string(s, "device", &device);
		if (DM_STRCMP(target, route->destination) == 0 && DM_STRCMP(gateway, route->gateway) == 0 && DM_STRCMP(device, route->iface) == 0)
			return true;
	}

	return false;
}

static bool is_route6_in_config(struct route6_args *route6)
{
	struct uci_section *s = NULL;

	uci_foreach_sections("network", "route6", s) {
		char *ip_r = NULL, *gw_r = NULL, *intf_r = NULL;

		dmuci_get_value_by_section_string(s, "target", &ip_r);
		dmuci_get_value_by_section_string(s, "gateway", &gw_r);
		dmuci_get_value_by_section_string(s, "interface", &intf_r);
		char *dev_r = get_l3_device(intf_r);
		if (DM_STRCMP(route6->iface, dev_r) == 0 && DM_STRCMP(route6->gateway, gw_r) == 0 && DM_STRCMP(route6->destination, ip_r) == 0)
			return true;
	}

	uci_path_foreach_sections(bbfdm, "Routing", "IPv6Forwarding", s) {
		char *ip_r6d = NULL, *gw_r6d = NULL, *dev_r6d = NULL;

		dmuci_get_value_by_section_string(s, "target", &ip_r6d);
		dmuci_get_value_by_section_string(s, "gateway", &gw_r6d);
		dmuci_get_value_by_section_string(s, "device", &dev_r6d);
		if (DM_STRCMP(route6->iface, dev_r6d) == 0 && DM_STRCMP(route6->gateway, gw_r6d) == 0 && DM_STRCMP(route6->destination, ip_r6d) == 0)
			return true;
	}

	return false;
}

static void parse_route_line(const char *line, struct route_args *route)
{
	size_t length = 0;

	char **arr = strsplit(line, " ", &length);
	if (arr == NULL || length == 0)
		return;

	for (int i = 0; i < length; i++) {
		if (strcmp(arr[i], "default") == 0) {
			DM_STRNCPY(route->gateway, arr[i + 2], sizeof(route->gateway));
			DM_STRNCPY(route->destination, "0.0.0.0", sizeof(route->destination));
			DM_STRNCPY(route->mask, "0.0.0.0", sizeof(route->mask));
			i += 2;
		}

		if (i == 0 && strcmp(arr[i], "default") != 0) {
			char *p = strchr(arr[i], '/');
			if (p) *p = 0;

			DM_STRNCPY(route->destination, arr[i], sizeof(route->destination));
			DM_STRNCPY(route->mask, (p && DM_STRLEN(p + 1)) ? cidr2netmask(DM_STRTOL(p + 1)) : "0.0.0.0", sizeof(route->mask));
			DM_STRNCPY(route->gateway, "0.0.0.0", sizeof(route->gateway));
		}

		if (strcmp(arr[i], "dev") == 0) {
			route->iface = arr[i + 1];
			i += 1;
		}

		if (strcmp(arr[i], "metric") == 0) {
			route->metric = arr[i + 1];
			i += 1;
		}
	}

	if (route->metric == NULL)
		route->metric = dmstrdup("0");

	if (route->iface == NULL)
		route->iface = dmstrdup("");
}

static int parse_route6_line(const char *line, struct route6_args *route6)
{
	size_t length = 0;

	char **arr = strsplit(line, " ", &length);
	if (arr == NULL || length == 0)
		return -1;

	for (int i = 0; i < length; i++) {

		if (strcmp(arr[i], "dev") == 0 && strcmp(arr[i + 1], "lo") == 0)
			return -1;

		if (strcmp(arr[i], "default") == 0) {
			DM_STRNCPY(route6->gateway, arr[i + 2], sizeof(route6->gateway));
			DM_STRNCPY(route6->destination, "::", sizeof(route6->destination));
			i += 2;
		}

		if (i == 0 && strcmp(arr[i], "default") != 0) {
			DM_STRNCPY(route6->destination, arr[i], sizeof(route6->destination));
			DM_STRNCPY(route6->gateway, "::", sizeof(route6->gateway));
		}

		if (strcmp(arr[i], "dev") == 0) {
			route6->iface = arr[i + 1];
			i += 1;
		}

		if (strcmp(arr[i], "metric") == 0) {
			route6->metric = arr[i + 1];
			i += 1;
		}
	}

	if (route6->metric == NULL)
		route6->metric = dmstrdup("0");

	if (route6->iface == NULL)
		route6->iface = dmstrdup("");

	return 0;
}

static void dmmap_synchronizeRoutingRouterIPv4Forwarding(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance, struct list_head *dup_list, int browse_type)
{
	struct uci_section *s = NULL, *stmp = NULL;
	struct route_args route = {0};
	FILE *pp = NULL;
	char *rt_table = NULL;
	char line[MAX_ROUTE_LEN] = {0};
	char cmd[32] = {0};

	dmuci_get_value_by_section_string(((struct dm_data *)prev_data)->dmmap_section, "rt_table", &rt_table);
	snprintf(cmd, sizeof(cmd), "ip route show table %s", rt_table);

	uci_path_foreach_option_eq_safe(bbfdm, "Routing", "IPv4Forwarding", "table", rt_table, stmp, s) {
		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;
		}

		// If it is a static route then check if exists in uci then skip otherwise delete
		char *config_sec_name = NULL;
		dmuci_get_value_by_section_string(s, "__section_name__", &config_sec_name);
		if (DM_STRLEN(config_sec_name)) {
			struct uci_section *uci_s = get_config_section_from_dmmap_section_name(config_sec_name);
			if (uci_s == NULL) {
				if (browse_type != BROWSE_FIND_MAX_INST)
					dmuci_delete_by_section(s, NULL, NULL);
				continue;
			}

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

		char *target = NULL, *iface = NULL, *gw = NULL;
		dmuci_get_value_by_section_string(s, "target", &target);
		dmuci_get_value_by_section_string(s, "device", &iface);
		dmuci_get_value_by_section_string(s, "gateway", &gw);

		pp = popen(cmd, "r"); // flawfinder: ignore
		if (pp != NULL) {
			bool found = false;

			while (fgets(line, MAX_ROUTE_LEN, pp) != NULL) {
				remove_new_line(line);
				parse_route_line(line, &route);
				if ((DM_STRCMP(iface, route.iface) == 0) && DM_STRCMP(target, route.destination) == 0) {
					found = true;

					if (DM_STRLEN(gw) != 0 && DM_STRCMP(gw, route.gateway) != 0) {
						found = false;
					}

					break;
				}
			}

			if (found) {
				if (dup_list != NULL) {
					add_dmmap_config_dup_list(dup_list, NULL, s);
				}
			} else {
				if (browse_type != BROWSE_FIND_MAX_INST)
					dmuci_delete_by_section(s, NULL, NULL);
			}

			pclose(pp);
		}
	}

	pp = popen(cmd, "r"); // flawfinder: ignore
	if (pp != NULL) {
		while (fgets(line, MAX_ROUTE_LEN, pp) != NULL) {
			remove_new_line(line);

			parse_route_line(line, &route);
			if (is_route_in_config(&route))
				continue;

			char *iface = NULL;
			uci_foreach_sections("network", "interface", s) {
				char *str = get_l3_device(section_name(s));
				if (DM_STRCMP(str, route.iface) == 0) {
					iface = section_name(s);
					break;
				}
			}

			// Device.Routing.Router.{i}.IPv4Forwarding.{i}.
			char *instance = NULL;
			struct uci_section *dmmap_obj = create_dmmap_obj(dmctx, 1, "Routing", "IPv4Forwarding", NULL, &instance);
			if (dmmap_obj != NULL && DM_STRLEN(instance)) {
				dmuci_set_value_by_section_bbfdm(dmmap_obj, "target", route.destination);
				dmuci_set_value_by_section_bbfdm(dmmap_obj, "netmask", route.mask);
				dmuci_set_value_by_section_bbfdm(dmmap_obj, "metric", route.metric);
				dmuci_set_value_by_section_bbfdm(dmmap_obj, "gateway", route.gateway);
				dmuci_set_value_by_section_bbfdm(dmmap_obj, "device", route.iface);
				dmuci_set_value_by_section_bbfdm(dmmap_obj, "interface", iface ? iface : "");
				dmuci_set_value_by_section_bbfdm(dmmap_obj, "table", rt_table);
				if (dup_list != NULL) {
					add_dmmap_config_dup_list(dup_list, NULL, dmmap_obj);
				}
			}
		}
		pclose(pp);
	}
}

static void dmmap_synchronizeRoutingRouterIPv6Forwarding(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance, struct list_head *dup_list, int browse_type)
{
	struct uci_section *s = NULL, *stmp = NULL;
	struct route6_args route6 = {0};
	FILE *pp = NULL;
	char *rt_table = NULL;
	char line[MAX_ROUTE_LEN] = {0};
	char cmd[32] = {0};

	dmuci_get_value_by_section_string(((struct dm_data *)prev_data)->dmmap_section, "rt_table", &rt_table);
	snprintf(cmd, sizeof(cmd), "ip -6 route show table %s", rt_table);

	uci_path_foreach_option_eq_safe(bbfdm, "Routing", "IPv6Forwarding", "table", rt_table, stmp, s) {
		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;
		}

		// If it is a static route then check if exists keep it otherwise delete
		char *config_sec_name = NULL;
		dmuci_get_value_by_section_string(s, "__section_name__", &config_sec_name);
		if (DM_STRLEN(config_sec_name)) {
			struct uci_section *uci_s = get_config_section_from_dmmap_section_name(config_sec_name);
			if (uci_s == NULL) {
				if (browse_type != BROWSE_FIND_MAX_INST)
					dmuci_delete_by_section(s, NULL, NULL);
				continue;
			}

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

		char *iface = NULL, *target = NULL;
		dmuci_get_value_by_section_string(s, "target", &target);
		dmuci_get_value_by_section_string(s, "device", &iface);

		pp = popen(cmd, "r"); // flawfinder: ignore
		if (pp != NULL) {
			bool found = false;

			while (fgets(line, MAX_ROUTE_LEN, pp) != NULL) {
				remove_new_line(line);

				if (parse_route6_line(line, &route6))
					continue;

				if (DM_STRCMP(iface, route6.iface) == 0 && DM_STRCMP(route6.destination, target) == 0) {
					found = 1;
					break;
				}
			}

			if (found) {
				if (dup_list != NULL) {
					add_dmmap_config_dup_list(dup_list, NULL, s);
				}
			} else {
				if (browse_type != BROWSE_FIND_MAX_INST)
					dmuci_delete_by_section(s, NULL, NULL);
			}

			pclose(pp);
		}
	}

	pp = popen(cmd, "r"); // flawfinder: ignore
	if (pp != NULL) {
		while (fgets(line, MAX_ROUTE_LEN, pp) != NULL) {
			remove_new_line(line);

			if (parse_route6_line(line, &route6))
				continue;

			if (is_route6_in_config(&route6))
				continue;

			char *iface = NULL;
			uci_foreach_sections("network", "interface", s) {
				char *str = get_l3_device(section_name(s));
				if (DM_STRCMP(str, route6.iface) == 0) {
					iface = section_name(s);
					break;
				}
			}

			// Device.Routing.Router.{i}.IPv4Forwarding.{i}.
			char *instance = NULL;
			struct uci_section *dmmap_obj = create_dmmap_obj(dmctx, 1, "Routing", "IPv6Forwarding", NULL, &instance);
			if (dmmap_obj != NULL && DM_STRLEN(instance)) {
				dmuci_set_value_by_section_bbfdm(dmmap_obj, "target", route6.destination);
				dmuci_set_value_by_section_bbfdm(dmmap_obj, "gateway", route6.gateway);
				dmuci_set_value_by_section_bbfdm(dmmap_obj, "interface", iface ? iface : "");
				dmuci_set_value_by_section_bbfdm(dmmap_obj, "device", route6.iface);
				dmuci_set_value_by_section_bbfdm(dmmap_obj, "metric", route6.metric);
				dmuci_set_value_by_section_bbfdm(dmmap_obj, "table", rt_table);
				if (dup_list != NULL) {
					add_dmmap_config_dup_list(dup_list, NULL, dmmap_obj);
				}
			}
		}
		pclose(pp);
	}
}

static struct uci_section *route_sec(void *data)
{
	return ((struct dm_data *)data)->config_section ? ((struct dm_data *)data)->config_section : ((struct dm_data *)data)->dmmap_section;
}

/*************************************************************
* COMMON FUNCTIONS
**************************************************************/
static void sync_routing_route_forwarding_section(struct dmctx *dmctx, const char *rt_table)
{
	char *instance = NULL;
	struct uci_section *route_s = NULL;

	// Device.Routing.Router.{i}.IPv4Forwarding.{i}.
	uci_foreach_sections("network", "route", route_s) {

		char *table = NULL;
		dmuci_get_value_by_section_string(route_s, "table", &table);
		if ((DM_STRLEN(table) == 0 && DM_STRCMP(rt_table, "254") != 0) || DM_STRCMP(rt_table, table) != 0)
			continue;

		struct uci_section *p = create_dmmap_obj(dmctx, 1, "Routing", "IPv4Forwarding", route_s, &instance);
		if (p != NULL) {
			dmuci_set_value_by_section(p, "table", rt_table);
		}
	}

	route_s = NULL;
	// Device.Routing.Router.{i}.IPv6Forwarding.{i}.
	uci_foreach_sections("network", "route6", route_s) {

		char *table = NULL;
		dmuci_get_value_by_section_string(route_s, "table", &table);
		if (DM_STRCMP(rt_table, table) != 0 || (DM_STRLEN(table) == 0 && DM_STRCMP(rt_table, "254") != 0))
			continue;

		struct uci_section *p = create_dmmap_obj(dmctx, 1, "Routing", "IPv6Forwarding", route_s, &instance);
		if (p != NULL) {
			dmuci_set_value_by_section(p, "table", rt_table);
		}
	}
}

static void sync_routing_route_rule_section(struct dmctx *dmctx, struct uci_section *rule_s)
{
	char *instance = NULL;

	if (rule_s == NULL)
		return;

	create_dmmap_obj(dmctx, 0, "Routing", "Policy", rule_s, &instance);
}

static void remove_unsynced_sections(void)
{
	struct uci_section *s = NULL, *tmp = NULL;

	uci_path_foreach_sections_safe(bbfdm, "Routing", "Policy", tmp, s) {
		char *orig_sec = NULL;

		dmuci_get_value_by_section_string(s, "__section_name__", &orig_sec);

		struct uci_section *uci_s = get_config_section_from_dmmap_section_name(orig_sec);
		if (uci_s == NULL)
			dmuci_delete_by_section(s, NULL, NULL);
	}
}

static void set_route_tables_name_linker(void)
{
	struct uci_section *s = NULL;

	uci_path_foreach_sections(bbfdm, "Routing", "Router", s) {
		char *table = NULL, *name = NULL;
		char link[64] = {0};

		dmuci_get_value_by_section_string(s, "rt_table", &table);
		dmuci_get_value_by_section_string(s, "name", &name);

		snprintf(link, sizeof(link), "route_table-%s", table);
		dmuci_set_value_by_section(s, "Name", link);

		unsigned int id = (unsigned int)DM_STRTOUL(table);
		if (id == 0)
			continue;

		set_rt_table_name(id, name);
	}
}

static void dmmap_synchronizeRouting(struct dmctx *dmctx)
{
	struct uci_section *uci_s = NULL;
	char *instance = NULL;

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

	// Default Router Entry: Device.Routing.Router.{i}.
	struct uci_section *dmmap_obj = is_dmmap_section_exist_eq("Routing", "Router", "rt_table", "254");
	if (dmmap_obj == NULL) {
		// Device.Routing.Router.{i}.
		dmmap_obj = create_dmmap_obj(dmctx, 0, "Routing", "Router", NULL, &instance);
		if (dmmap_obj != NULL && DM_STRLEN(instance)) {
			dmuci_set_value_by_section(dmmap_obj, "rt_table", "254");
		}
	} else {
		dmuci_get_value_by_section_string(dmmap_obj, "__instance__", &instance);
		dmctx->obj_buf[0] = "Router";
		dmctx->inst_buf[0] = instance;
	}

	if (dmmap_obj != NULL) {
		dmuci_set_value_by_section(dmmap_obj, "name", "main");
		sync_routing_route_forwarding_section(dmctx, "254");
	}

	uci_foreach_sections("network", "interface", uci_s) {

		char *idx = NULL, *device = NULL, *proto = NULL;
		dmuci_get_value_by_section_string(uci_s, "proto", &proto);
		dmuci_get_value_by_section_string(uci_s, "device", &device);
		dmuci_get_value_by_section_string(uci_s, "ip4table", &idx);

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

		if (DM_STRLEN(idx) && DM_STRCMP(idx, "254") != 0) {
			// Create a Router entry if it doesn't exist: Device.Routing.Router.{i}.
			struct uci_section *dmmap_obj = is_dmmap_section_exist_eq("Routing", "Router", "rt_table", idx);
			if (dmmap_obj == NULL) {
				// Device.Routing.Router.{i}.
				dmmap_obj = create_dmmap_obj(dmctx, 0, "Routing", "Router", NULL, &instance);
				if (dmmap_obj != NULL && DM_STRLEN(instance)) {
					dmuci_set_value_by_section(dmmap_obj, "rt_table", idx);
				}
			} else {
				dmuci_get_value_by_section_string(dmmap_obj, "__instance__", &instance);
				dmctx->obj_buf[0] = "Router";
				dmctx->inst_buf[0] = instance;
			}
		}
	}

	const char *type[] = { "route", "route6" };
	for (int i = 0; i < ARRAY_SIZE(type); i++) {
		uci_s = NULL;

		uci_foreach_sections("network", type[i], uci_s) {
			char *idx = NULL;

			dmuci_get_value_by_section_string(uci_s, "table", &idx);

			if (DM_STRLEN(idx) && DM_STRCMP(idx, "254") != 0) {
				// Create a Router entry if it doesn't exist: Device.Routing.Router.{i}.
				struct uci_section *dmmap_obj = is_dmmap_section_exist_eq("Routing", "Router", "rt_table", idx);
				if (dmmap_obj == NULL) {
					// Device.Routing.Router.{i}.
					dmmap_obj = create_dmmap_obj(dmctx, 0, "Routing", "Router", NULL, &instance);
					if (dmmap_obj != NULL && DM_STRLEN(instance)) {
						dmuci_set_value_by_section(dmmap_obj, "rt_table", idx);
					}
				} else {
					dmuci_get_value_by_section_string(dmmap_obj, "__instance__", &instance);
					dmctx->obj_buf[0] = "Router";
					dmctx->inst_buf[0] = instance;
				}

				if (dmmap_obj != NULL) {
					sync_routing_route_forwarding_section(dmctx, idx);
				}
			}
		}
	}

	uci_s = NULL;
	uci_foreach_sections("network", "rule", uci_s) {
		char *idx = NULL;

		dmuci_get_value_by_section_string(uci_s, "lookup", &idx);

		if (DM_STRLEN(idx) && DM_STRCMP(idx, "254") != 0) {
			// Create a Router entry if it doesn't exist: Device.Routing.Router.{i}.
			struct uci_section *dmmap_obj = is_dmmap_section_exist_eq("Routing", "Router", "rt_table", idx);
			if (dmmap_obj == NULL) {
				// Device.Routing.Router.{i}.
				dmmap_obj = create_dmmap_obj(dmctx, 0, "Routing", "Router", NULL, &instance);
				if (dmmap_obj != NULL && DM_STRLEN(instance)) {
					dmuci_set_value_by_section(dmmap_obj, "rt_table", idx);
				}
			} else {
				dmuci_get_value_by_section_string(dmmap_obj, "__instance__", &instance);
				dmctx->obj_buf[0] = "Router";
				dmctx->inst_buf[0] = instance;
			}
		}

		sync_routing_route_rule_section(dmctx, uci_s);
	}

	remove_unsynced_sections();

	set_route_tables_name_linker();

	dmuci_commit_package_bbfdm("Routing");
}

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

	// Dynamic Routes
	dmmap_synchronizeRoutingRouterIPv4Forwarding(dmctx, parent_node, prev_data, prev_instance, &dup_list, parent_node->browse_type);
	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 browseIPv6ForwardingInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	char *instance = NULL;
	struct dm_data *p = NULL;
	LIST_HEAD(dup_list);

	// Dynamic Routes
	dmmap_synchronizeRoutingRouterIPv6Forwarding(dmctx, parent_node, prev_data, prev_instance, &dup_list, parent_node->browse_type);
	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 browseRoutingRouteInformationInterfaceSettingInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	struct dm_data curr_data = {0};
	struct uci_section *s = NULL;
	char *inst = NULL;
	int id = 0, i;

	uci_foreach_sections("network", "interface", s) {
		char *proto = NULL, *ip6addr = NULL;

		dmuci_get_value_by_section_string(s, "proto", &proto);
		dmuci_get_value_by_section_string(s, "ip6addr", &ip6addr);
		if ((proto && DM_LSTRCMP(proto, "dhcpv6") == 0) || (ip6addr && ip6addr[0] != '\0')) {
			json_object *res = NULL, *route_obj = NULL, *arrobj = NULL;

			char *if_name = section_name(s);
			dmubus_call("network.interface", "status", UBUS_ARGS{{"interface", if_name, String}}, 1, &res);
			dmjson_foreach_obj_in_array(res, arrobj, route_obj, i, 1, "route") {

				curr_data.json_object = route_obj;

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

/*************************************************************
* GET & SET PARAM
**************************************************************/
static int get_router_nbr_entry(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_RoutingRouter_ID(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "rt_table", value);
	return 0;
}

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

static int set_RoutingRouter_Name(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	unsigned int id = 0;
	char *table = NULL;

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 256, NULL, NULL))
				return FAULT_9007;
			return 0;
		case VALUESET:
			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "rt_table", &table);

			id = (unsigned int)DM_STRTOUL(table);
			if (id == 0)
				return FAULT_9002;

			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "name", value);
			set_rt_table_name(id, value);
			return 0;
	}
	return 0;
}

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

static int set_RoutingRouter_Enable(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_RoutingRouter_Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmstrdup("Enabled");
	return 0;
}

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

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

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

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

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

static int get_RoutingPolicy_Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	get_RoutingPolicy_Enable(refparam, ctx, data, instance, value);
	*value = ((*value)[0] == '1') ? "Enabled" : "Disabled";
	return 0;
}

static int get_RoutingPolicy_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_RoutingPolicy_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_RoutingPolicy_Router(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *lookup = NULL;
	char linker[32] = {0};

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

	snprintf(linker, sizeof(linker), "route_table-%s", lookup);
	_bbfdm_get_references(ctx, "Device.Routing.Router.", "Alias", linker, value);

	return 0;
}

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

	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;

			return 0;
		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;

			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "lookup", rt_table+1);
			return 0;
	}

	return 0;
}

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

static int set_RoutingPolicy_Priority(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_int(ctx, value, RANGE_ARGS{{"0", NULL}}, 1))
				return FAULT_9007;

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

static int get_RoutingPolicy_Proto(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, "ipproto", "-1");
	return 0;
}

static int set_RoutingPolicy_Proto(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_int(ctx, value, RANGE_ARGS{{"-1","255"}}, 1))
				return FAULT_9007;

			break;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "ipproto", (DM_STRCMP(value, "-1") == 0) ? "" : value);
			break;
	}
	return 0;
}

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

	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "in", &iface);
	if (DM_STRCMP(iface, "loopback") == 0)
		return 0;

	return _bbfdm_get_references(ctx, "Device.IP.Interface.", "Name", iface, value);
}

static int set_RoutingPolicy_Source(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *allowed_objects[] = {"Device.IP.Interface.", NULL};
	struct dm_reference reference = {0};
	char *src = NULL;

	bbfdm_get_reference_linker(ctx, value, &reference);

	switch (action) {
		case VALUECHECK:
			if (dm_validate_allowed_objects(ctx, &reference, allowed_objects))
				return FAULT_9007;

			return 0;
		case VALUESET:
			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "src", &src);
			if (DM_STRLEN(src) == 0 && DM_STRLEN(value) == 0) {
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "in", "loopback");
				return 0;
			}

			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "in", reference.value);
			return 0;
	}
	return 0;
}

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

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

	return _bbfdm_get_references(ctx, "Device.IP.Interface.", "Name", iface, value);
}

static int set_RoutingPolicy_Dest(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *allowed_objects[] = {"Device.IP.Interface.", NULL};
	struct dm_reference reference = {0};

	bbfdm_get_reference_linker(ctx, value, &reference);

	switch (action) {
		case VALUECHECK:
			if (dm_validate_allowed_objects(ctx, &reference, allowed_objects))
				return FAULT_9007;

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

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

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

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 49, NULL, IPPrefix))
				return FAULT_9007;
			return 0;
		case VALUESET:
			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "in", &iface);
			if (DM_STRCMP(iface, "loopback") == 0 && DM_STRLEN(value) != 0)
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "in", "");

			if (DM_STRLEN(iface) == 0 && DM_STRLEN(value) == 0)
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "in", "loopback");

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

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

static int set_RoutingPolicy_DestIP(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, IPPrefix))
				return FAULT_9007;
			return 0;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "dest", value);
			return 0;
	}
	return 0;
}

static int get_router_ipv4forwarding_enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	if (((struct dm_data *)data)->config_section == NULL) { // Dynamic route
		*value = dmstrdup("1");
	} else { //Static route
		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_router_ipv4forwarding_enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	bool b;

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			return 0;
		case VALUESET:
			if (((struct dm_data *)data)->config_section == NULL) // Dynamic route
				return 0;

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

static int get_router_ipv4forwarding_status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	get_router_ipv4forwarding_enable(refparam, ctx, data, instance, value);
	*value = ((*value)[0] == '1') ? "Enabled" : "Disabled";
	return 0;
}

static int get_router_ipv4forwarding_destip(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(route_sec(data), "target", value);
	return 0;
}

static int set_router_ipv4forwarding_destip(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 15, NULL, IPv4Address))
				return FAULT_9007;
			return 0;
		case VALUESET:
			if (((struct dm_data *)data)->config_section == NULL) // Dynamic route
				return 0;

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

static int get_router_ipv4forwarding_destmask(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(route_sec(data), "netmask", value);
	return 0;
}

static int set_router_ipv4forwarding_destmask(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 15, NULL, IPv4Address))
				return FAULT_9007;
			return 0;
		case VALUESET:
			if (((struct dm_data *)data)->config_section == NULL) // Dynamic route
				return 0;

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

static int get_router_ipv4forwarding_static_route(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = (((struct dm_data *)data)->config_section) ? "1" : "0";
	return 0;
}

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

	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "mark", &mark);
	if (DM_STRLEN(mark) == 0) {
		*value = dmstrdup("-1");
		return 0;
	}

	dmasprintf(value, "%lu", strtoul(mark, NULL, 16));
	return 0;
}

static int set_router_ipv4forwarding_forwarding_policy(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char mark[16] = {0};

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_int(ctx, value, RANGE_ARGS{{"-1",NULL}}, 1))
				return FAULT_9007;

			break;
		case VALUESET:
			if (((struct dm_data *)data)->config_section == NULL) // Dynamic route
				return 0;

			if (DM_STRCMP(value, "-1") != 0)
				snprintf(mark, sizeof(mark), "0x%X", (unsigned int)DM_STRTOUL(value));

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

static int get_router_ipv4forwarding_origin(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	if (((struct dm_data *)data)->config_section)
		*value = dmstrdup("Static");
	else {
		json_object *res = NULL;
		char *interface;

		dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "interface", &interface);
		dmubus_call("network.interface", "status", UBUS_ARGS{{"interface", interface, String}}, 1, &res);
		DM_ASSERT(res, *value = dmstrdup("DHCPv4"));
		char *proto = dmjson_get_value(res, 1, "proto");
		*value = (proto && DM_LSTRNCMP(proto, "ppp", 3) == 0) ? "IPCP" : "DHCPv4";
	}
	return 0;
}

static int get_router_ipv4forwarding_gatewayip(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(route_sec(data), "gateway", value);
	return 0;
}

static int set_router_ipv4forwarding_gatewayip(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 15, NULL, IPv4Address))
				return FAULT_9007;
			return 0;
		case VALUESET:
			if (((struct dm_data *)data)->config_section == NULL) // Dynamic route
				return 0;

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

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

	dmuci_get_value_by_section_string(route_sec(data), "interface", &linker);
	_bbfdm_get_references(ctx, "Device.IP.Interface.", "Name", linker, value);
	return 0;
}

static int set_RoutingRouterForwarding_Interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *allowed_objects[] = {"Device.IP.Interface.", NULL};
	struct dm_reference reference = {0};

	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;

			return 0;
		case VALUESET:
			if (((struct dm_data *)data)->config_section == NULL) // Dynamic route
				return 0;

			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "interface", reference.value);
			return 0;
	}
	return 0;
}

static int get_router_ipv4forwarding_metric(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(route_sec(data), "metric", "0");
	return 0;
}

static int set_router_ipv4forwarding_metric(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_int(ctx, value, RANGE_ARGS{{"-1",NULL}}, 1))
				return FAULT_9007;
			return 0;
		case VALUESET:
			if (((struct dm_data *)data)->config_section == NULL) // Dynamic route
				return 0;

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

static int get_RoutingRouterIPv6Forwarding_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	if (((struct dm_data *)data)->config_section == NULL) { // Dynamic route
		*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_RoutingRouterIPv6Forwarding_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	bool b;

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			break;
		case VALUESET:
			if (((struct dm_data *)data)->config_section == NULL) // Dynamic route
				return 0;

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

static int get_RoutingRouterIPv6Forwarding_Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	get_RoutingRouterIPv6Forwarding_Enable(refparam, ctx, data, instance, value);
	*value = ((*value)[0] == '1') ? "Enabled" : "Disabled";
	return 0;
}

static int get_RoutingRouterIPv6Forwarding_DestIPPrefix(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(route_sec(data), "target", value);
	return 0;
}

static int set_RoutingRouterIPv6Forwarding_DestIPPrefix(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;
			return 0;
		case VALUESET:
			if (((struct dm_data *)data)->config_section == NULL) // Dynamic route
				return 0;

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

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

	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "mark", &mark);
	if (DM_STRLEN(mark) == 0) {
		*value = dmstrdup("-1");
		return 0;
	}

	dmasprintf(value, "%lu", strtoul(mark, NULL, 16));
	return 0;
}

static int set_RoutingRouterIPv6Forwarding_ForwardingPolicy(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char mark[16] = {0};

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_int(ctx, value, RANGE_ARGS{{"-1",NULL}}, 1))
				return FAULT_9007;

			break;
		case VALUESET:
			if (((struct dm_data *)data)->config_section == NULL) // Dynamic route
				return 0;

			if (DM_STRCMP(value, "-1") != 0)
				snprintf(mark, sizeof(mark), "0x%X", (unsigned int)DM_STRTOUL(value));

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

static int get_RoutingRouterIPv6Forwarding_NextHop(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(route_sec(data), "gateway", value);
	return 0;
}

static int set_RoutingRouterIPv6Forwarding_NextHop(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 45, NULL, IPv6Address))
				return FAULT_9007;
			return 0;
		case VALUESET:
			if (((struct dm_data *)data)->config_section == NULL) // Dynamic route
				return 0;

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

static int get_RoutingRouterIPv6Forwarding_Origin(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = (((struct dm_data *)data)->config_section) ? "Static" : "DHCPv6";
	return 0;
}

static int get_RoutingRouterIPv6Forwarding_ForwardingMetric(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(route_sec(data), "metric", "0");
	return 0;
}

static int set_RoutingRouterIPv6Forwarding_ForwardingMetric(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_int(ctx, value, RANGE_ARGS{{"-1",NULL}}, 1))
				return FAULT_9007;
			return 0;
		case VALUESET:
			if (((struct dm_data *)data)->config_section == NULL) // Dynamic route
				return 0;

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

static int get_RoutingRouterIPv6Forwarding_ExpirationTime(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmstrdup("9999-12-31T23:59:59Z");
	return 0;
}

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

static int set_RoutingRouteInformation_Enable(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_RoutingRouteInformation_InterfaceSettingNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *s = NULL;
	size_t nbre_routes = 0;

	uci_foreach_sections("network", "interface", s) {
		char *proto = NULL, *ip6addr = NULL;

		dmuci_get_value_by_section_string(s, "proto", &proto);
		dmuci_get_value_by_section_string(s, "ip6addr", &ip6addr);
		if ((proto && DM_LSTRCMP(proto, "dhcpv6") == 0) || (ip6addr && ip6addr[0] != '\0')) {
			json_object *res = NULL, *routes = NULL;

			char *if_name = section_name(s);
			dmubus_call("network.interface", "status", UBUS_ARGS{{"interface", if_name, String}}, 1, &res);
			DM_ASSERT(res, *value = dmstrdup("0"));
			json_object_object_get_ex(res, "route", &routes);
			nbre_routes = (routes) ? json_object_array_length(routes) : 0;
		}
	}
	dmasprintf(value, "%d", nbre_routes);
	return 0;
}

static int get_RoutingRouteInformationInterfaceSetting_Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *s = NULL;
	char *target, *mask, *nexthop, *gateway, *ip_target, buf[64];

	*value = dmstrdup("NoForwardingEntry");
	target = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "target");
	mask = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "mask");
	snprintf(buf, sizeof(buf), "%s/%s", target, mask);
	nexthop = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "nexthop");
	uci_foreach_sections("network", "route6", s) {
		dmuci_get_value_by_section_string(s, "target", &ip_target);
		dmuci_get_value_by_section_string(s, "gateway", &gateway);
		if(DM_STRCMP(ip_target, buf) == 0 && DM_STRCMP(nexthop, gateway) == 0) {
			*value = dmstrdup("ForwardingEntryCreated");
			return 0;
		}
	}
	return 0;
}

static int get_RoutingRouteInformationInterfaceSetting_Interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct route6_args route6 = {0};
	struct uci_section *s = NULL;
	char cmd[32] = {0};
	char *iface = NULL;

	char *source = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "source");
	char *nexthop = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "nexthop");

	snprintf(cmd, sizeof(cmd), "ip -6 route show");

	// cppcheck-suppress cert-MSC24-C
	FILE *fp = fopen(PROC_ROUTE6, "r");
	if (fp != NULL) {
		char line[MAX_ROUTE_LEN] = {0};

		while (fgets(line, MAX_ROUTE_LEN, fp) != NULL) {
			remove_new_line(line);

			if (parse_route6_line(line, &route6))
				continue;

			if((DM_STRCMP(source, route6.destination) == 0) && (DM_STRCMP(nexthop, route6.gateway) == 0))
				break;
		}

		fclose(fp);
	}

	if (DM_STRLEN(route6.iface)) {
		uci_foreach_sections("network", "interface", s) {
			char *str = get_l3_device(section_name(s));
			if (DM_STRCMP(str, route6.iface) == 0) {
				iface = section_name(s);
				break;
			}
		}
	}

	_bbfdm_get_references(ctx, "Device.IP.Interface.", "Name", iface, value);
	return 0;
}

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

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

	char *valid = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "valid");
	if (valid && *valid != '\0' && DM_STRTOL(valid) > 0) {
		char local_time[32] = {0};

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

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

/*************************************************************
* SET AND GET ALIAS FOR ROUTER OBJ
**************************************************************/
static int get_RoutingRouter_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *rt_table = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "rt_table", &rt_table);
	dmasprintf(value, "route_table-%s", rt_table);
	return 0;
}

static int set_RoutingRouter_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 64, NULL, NULL))
				return FAULT_9007;
			break;
		case VALUESET:
			bbfdm_set_fault_message(ctx, "Internal designated unique identifier, not allowed to update");
			return FAULT_9007;
	}
	return 0;
}

static int get_router_ipv4forwarding_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_router_ipv4forwarding_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_RoutingRouterIPv6Forwarding_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_RoutingRouterIPv6Forwarding_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);
}

/*************************************************************
* ADD DEL OBJ
**************************************************************/
static int add_router(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
	struct dm_data *curr_data = (struct dm_data *)ctx->addobj_instance;
	struct uci_section *s = NULL;
	bool used[253] = {false};  // index 0..252; only 1..252 are valid

	/* Mark all rt_table IDs already present in dmmap as used */
	uci_path_foreach_sections(bbfdm, "Routing", "Router", s) {
		char *rt_table_str = NULL;

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

		long id = DM_STRTOL(rt_table_str);
		if (id >= 1 && id <= 252)
			used[id] = true;
	}

	/* Find first free ID in [1..252] excluding 128 */
	int free_id = -1;
	for (int id = 1; id <= 252; id++) {
		if (id == 128)
			continue;

		if (!used[id]) {
			free_id = id;
			break;
		}
	}

	if (free_id == -1) {
		bbfdm_set_fault_message(ctx, "No free routing table ID available in range 1-252.");
		return FAULT_9003;
	}

	char rt_table[16] = {0}, link[64] = {0};
	snprintf(rt_table, sizeof(rt_table), "%d", free_id);
	dmuci_set_value_by_section(curr_data->dmmap_section, "rt_table", rt_table);

	snprintf(link, sizeof(link), "route_table-%s", rt_table);
	dmuci_set_value_by_section(curr_data->dmmap_section, "Name", link);

	return 0;
}

static int delete_router(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	struct uci_section *s = NULL;
	char *rt_table = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "rt_table", &rt_table);
	if(DM_LSTRCMP(rt_table, "254") == 0) {
		bbfdm_set_fault_message(ctx, "It's not allowed to delete the main '254' routing table.");
		return FAULT_9003;
	}

	uci_foreach_sections("network", "interface", s) {
		char *curr_rt_table = NULL;

		dmuci_get_value_by_section_string(s, "ip4table", &curr_rt_table);
		if (DM_STRCMP(curr_rt_table, rt_table) == 0)
			dmuci_set_value_by_section(s, "ip4table", "");
	}

	const char *type[] = { "route", "route6" };
	for (int i = 0; i < ARRAY_SIZE(type); i++) {
		struct uci_section *uci_s = NULL, *tmp_s = NULL;

		uci_foreach_sections_safe("network", type[i], tmp_s, uci_s) {
			char *idx = NULL;
			char sec_name[64] = {0};

			dmuci_get_value_by_section_string(uci_s, "table", &idx);
			if (DM_STRCMP(idx, rt_table) == 0) {
				struct uci_section *dmmap_s = NULL;

				snprintf(sec_name, sizeof(sec_name), "network.%s", section_name(uci_s));
				get_dmmap_section_of_config_section_eq("Routing", (i==0) ? "IPv4Forwarding" : "IPv6Forwarding",
									"__section_name__", sec_name, &dmmap_s);

				if (dmmap_s)
					dmuci_delete_by_section(dmmap_s, NULL, NULL);

				dmuci_delete_by_section(uci_s, NULL, NULL);
			}
		}
	}

	s = NULL;
	uci_foreach_sections("network", "rule", s) {
		char *curr_rt_table = NULL;

		dmuci_get_value_by_section_string(s, "lookup", &curr_rt_table);
		if (DM_STRCMP(curr_rt_table, rt_table) == 0)
			dmuci_set_value_by_section(s, "lookup", "");
	}

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

	unsigned int id = (unsigned int)DM_STRTOUL(rt_table);
	if (id != 0)
		set_rt_table_name(id, "");

	return 0;
}

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

	snprintf(rule_name, sizeof(rule_name), "rule_%s", *instance);
	dmuci_add_named_section("network", "rule", rule_name, &curr_data->config_section);
	dmuci_set_value_by_section(curr_data->config_section, "disabled", "1");
	dmuci_set_value_by_section(curr_data->config_section, "in", "loopback");
	dmuci_set_value_by_section(curr_data->config_section, "priority", *instance);

	return 0;
}

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

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

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "rt_table", &rt_table);
	snprintf(route_name, sizeof(route_name), "route_%s_%s", rt_table, *instance);

	dmuci_add_section("network", "route", &curr_data->config_section);
	dmuci_rename_section_by_section(curr_data->config_section, route_name);

	dmuci_set_value_by_section(curr_data->config_section, "disabled", "1");
	dmuci_set_value_by_section(curr_data->config_section, "table", rt_table);
	dmuci_set_value_by_section(curr_data->dmmap_section, "table", rt_table);

	return 0;
}

static int delete_ipv4forwarding(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	// Return 9008 error if the removed route is dynamic
	if (((struct dm_data *)data)->config_section == NULL) { // Dynamic route
		bbfdm_set_fault_message(ctx, "This is a dynamic 'route' instance, therefore it's not permitted to delete it.");
		return FAULT_9008;
	}

	// Remove dmmap section
	dmuci_delete_by_section(((struct dm_data *)data)->dmmap_section, NULL, NULL);

	// Remove config section
	dmuci_delete_by_section(((struct dm_data *)data)->config_section, NULL, NULL);
	return 0;
}

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

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "rt_table", &rt_table);
	snprintf(route6_name, sizeof(route6_name), "route6_%s", *instance);

	dmuci_add_section("network", "route6", &curr_data->config_section);
	dmuci_rename_section_by_section(curr_data->config_section, route6_name);

	dmuci_set_value_by_section(curr_data->config_section, "disabled", "1");
	dmuci_set_value_by_section(curr_data->config_section, "table", rt_table);
	dmuci_set_value_by_section(curr_data->dmmap_section, "table", rt_table);

	return 0;
}

static int delete_ipv6Forwarding(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	// Return 9008 error if the removed route6 is dynamic
	if (((struct dm_data *)data)->config_section == NULL) { // Dynamic route
		bbfdm_set_fault_message(ctx, "This is a dynamic 'route' instance, therefore it's not permitted to delete it.");
		return FAULT_9008;
	}

	// Remove dmmap section
	dmuci_delete_by_section(((struct dm_data *)data)->dmmap_section, NULL, NULL);

	// Remove config section
	dmuci_delete_by_section(((struct dm_data *)data)->config_section, NULL, NULL);
	return 0;
}

/*************************************************************
* Init & Clean Module
**************************************************************/
int init_routing_module(void *data)
{
	struct dmctx bbf_ctx = {0};

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

	return 0;
}

int sync_routing_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 routing module config");
				init_routing_module(NULL);
				break;
			}
		}
	}

	return 0;
}

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

/* *** Device.Routing. *** */
DMOBJ tRoutingObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"Router", &DMWRITE, add_router, delete_router, NULL, generic_browse, NULL, NULL, tRoutingRouterObj, tRoutingRouterParams, NULL, BBFDM_BOTH, NULL},
{"Policy", &DMWRITE, add_policy, delete_policy, NULL, generic_browse, NULL, NULL, NULL, tRoutingPolicyParams, NULL, BBFDM_BOTH, NULL},
{"RouteInformation", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, tRoutingRouteInformationObj, tRoutingRouteInformationParams, NULL, BBFDM_BOTH, NULL},
{0}
};

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

/* *** Device.Routing.Router.{i}. *** */
DMOBJ tRoutingRouterObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"IPv4Forwarding", &DMWRITE, add_ipv4forwarding, delete_ipv4forwarding, NULL, browseIPv4ForwardingInst, NULL, NULL, NULL, tRoutingRouterIPv4ForwardingParams, NULL, BBFDM_BOTH, NULL},
{"IPv6Forwarding", &DMWRITE, add_ipv6Forwarding, delete_ipv6Forwarding, NULL, browseIPv6ForwardingInst, NULL, NULL, NULL, tRoutingRouterIPv6ForwardingParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tRoutingRouterParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"ID", &DMREAD, DMT_UNINT, get_RoutingRouter_ID, NULL, BBFDM_BOTH},
{"Name", &DMWRITE, DMT_STRING, get_RoutingRouter_Name, set_RoutingRouter_Name, BBFDM_BOTH},
{"Enable", &DMWRITE, DMT_BOOL, get_RoutingRouter_Enable, set_RoutingRouter_Enable, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_RoutingRouter_Status, NULL, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_RoutingRouter_Alias, set_RoutingRouter_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE|DM_FLAG_LINKER},
{"IPv4ForwardingNumberOfEntries", &DMREAD, DMT_UNINT, get_RoutingRouter_IPv4ForwardingNumberOfEntries, NULL, BBFDM_BOTH},
{"IPv6ForwardingNumberOfEntries", &DMREAD, DMT_UNINT, get_RoutingRouter_IPv6ForwardingNumberOfEntries, NULL, BBFDM_BOTH},
{0}
};

DMLEAF tRoutingPolicyParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_RoutingPolicy_Enable, set_RoutingPolicy_Enable, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_RoutingPolicy_Status, NULL, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_RoutingPolicy_Alias, set_RoutingPolicy_Alias, BBFDM_BOTH},
{"RouterRef", &DMWRITE, DMT_STRING, get_RoutingPolicy_Router, set_RoutingPolicy_Router, BBFDM_BOTH, DM_FLAG_REFERENCE},
{"Priority", &DMWRITE, DMT_UNINT, get_RoutingPolicy_Priority, set_RoutingPolicy_Priority, BBFDM_BOTH},
{"Protocol", &DMWRITE, DMT_INT, get_RoutingPolicy_Proto, set_RoutingPolicy_Proto, BBFDM_BOTH},
{"SourceInterface", &DMWRITE, DMT_STRING, get_RoutingPolicy_Source, set_RoutingPolicy_Source, BBFDM_BOTH, DM_FLAG_REFERENCE},
{"DestInterface", &DMWRITE, DMT_STRING, get_RoutingPolicy_Dest, set_RoutingPolicy_Dest, BBFDM_BOTH, DM_FLAG_REFERENCE},
{"SourceIPPrefix", &DMWRITE, DMT_STRING, get_RoutingPolicy_SourceIP, set_RoutingPolicy_SourceIP, BBFDM_BOTH},
{"DestIPPrefix", &DMWRITE, DMT_STRING, get_RoutingPolicy_DestIP, set_RoutingPolicy_DestIP, BBFDM_BOTH},
{0}
};

/* *** Device.Routing.Router.{i}.IPv4Forwarding.{i}. *** */
DMLEAF tRoutingRouterIPv4ForwardingParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_router_ipv4forwarding_enable, set_router_ipv4forwarding_enable, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_router_ipv4forwarding_status, NULL, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_router_ipv4forwarding_alias, set_router_ipv4forwarding_alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"StaticRoute", &DMREAD, DMT_BOOL, get_router_ipv4forwarding_static_route, NULL, BBFDM_BOTH},
{"DestIPAddress", &DMWRITE, DMT_STRING, get_router_ipv4forwarding_destip, set_router_ipv4forwarding_destip, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"DestSubnetMask", &DMWRITE, DMT_STRING, get_router_ipv4forwarding_destmask, set_router_ipv4forwarding_destmask, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"ForwardingPolicy", &DMWRITE, DMT_INT, get_router_ipv4forwarding_forwarding_policy, set_router_ipv4forwarding_forwarding_policy, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"GatewayIPAddress", &DMWRITE, DMT_STRING, get_router_ipv4forwarding_gatewayip, set_router_ipv4forwarding_gatewayip, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Interface", &DMWRITE, DMT_STRING, get_RoutingRouterForwarding_Interface, set_RoutingRouterForwarding_Interface, BBFDM_BOTH, DM_FLAG_UNIQUE|DM_FLAG_REFERENCE},
{"Origin", &DMREAD, DMT_STRING, get_router_ipv4forwarding_origin, NULL, BBFDM_BOTH},
{"ForwardingMetric", &DMWRITE, DMT_INT, get_router_ipv4forwarding_metric, set_router_ipv4forwarding_metric, BBFDM_BOTH, DM_FLAG_UNIQUE},
{0}
};

/* *** Device.Routing.Router.{i}.IPv6Forwarding.{i}. *** */
DMLEAF tRoutingRouterIPv6ForwardingParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_RoutingRouterIPv6Forwarding_Enable, set_RoutingRouterIPv6Forwarding_Enable, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_RoutingRouterIPv6Forwarding_Status, NULL, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_RoutingRouterIPv6Forwarding_Alias, set_RoutingRouterIPv6Forwarding_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"DestIPPrefix", &DMWRITE, DMT_STRING, get_RoutingRouterIPv6Forwarding_DestIPPrefix, set_RoutingRouterIPv6Forwarding_DestIPPrefix, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"ForwardingPolicy", &DMWRITE, DMT_INT, get_RoutingRouterIPv6Forwarding_ForwardingPolicy, set_RoutingRouterIPv6Forwarding_ForwardingPolicy, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"NextHop", &DMWRITE, DMT_STRING, get_RoutingRouterIPv6Forwarding_NextHop, set_RoutingRouterIPv6Forwarding_NextHop, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Interface", &DMWRITE, DMT_STRING, get_RoutingRouterForwarding_Interface, set_RoutingRouterForwarding_Interface, BBFDM_BOTH, DM_FLAG_UNIQUE|DM_FLAG_REFERENCE},
{"Origin", &DMREAD, DMT_STRING, get_RoutingRouterIPv6Forwarding_Origin, NULL, BBFDM_BOTH},
{"ForwardingMetric", &DMWRITE, DMT_INT, get_RoutingRouterIPv6Forwarding_ForwardingMetric, set_RoutingRouterIPv6Forwarding_ForwardingMetric, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"ExpirationTime", &DMREAD, DMT_TIME, get_RoutingRouterIPv6Forwarding_ExpirationTime, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.Routing.RouteInformation. *** */
DMOBJ tRoutingRouteInformationObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"InterfaceSetting", &DMREAD, NULL, NULL, NULL, browseRoutingRouteInformationInterfaceSettingInst, NULL, NULL, NULL, tRoutingRouteInformationInterfaceSettingParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tRoutingRouteInformationParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_RoutingRouteInformation_Enable, set_RoutingRouteInformation_Enable, BBFDM_BOTH},
{"InterfaceSettingNumberOfEntries", &DMREAD, DMT_UNINT, get_RoutingRouteInformation_InterfaceSettingNumberOfEntries, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.Routing.RouteInformation.InterfaceSetting.{i}. *** */
DMLEAF tRoutingRouteInformationInterfaceSettingParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Status", &DMREAD, DMT_STRING, get_RoutingRouteInformationInterfaceSetting_Status, NULL, BBFDM_BOTH},
{"Interface", &DMREAD, DMT_STRING, get_RoutingRouteInformationInterfaceSetting_Interface, NULL, BBFDM_BOTH, DM_FLAG_UNIQUE|DM_FLAG_REFERENCE},
{"SourceRouter", &DMREAD, DMT_STRING, get_RoutingRouteInformationInterfaceSetting_SourceRouter, NULL, BBFDM_BOTH},
{"RouteLifetime", &DMREAD, DMT_TIME, get_RoutingRouteInformationInterfaceSetting_RouteLifetime, NULL, BBFDM_BOTH},
{0}
};
