/*
 * 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

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

/************************************************************************************* 
**** function related to get_object_router_ipv4forwarding ****
**************************************************************************************/
static void configure_forwarding_policy(const char *type, void *data, const char *value)
{
	struct uci_section *s = NULL;
	char *route_inst = NULL;

	if (type == NULL || value == NULL || data == NULL)
		return;

	uci_path_foreach_sections(bbfdm, "Routing", "Router", s) {
		char *rt_table = NULL;

		dmuci_get_value_by_section_string(s, "rt_table", &rt_table);
		if (DM_STRCMP(value, rt_table) == 0) {
			dmuci_get_value_by_section_string(s, "__instance__", &route_inst);
			break;
		}
	}

	if (route_inst == NULL)
		return;

	s = NULL;
	int max_instance = 0;
	char *curr_instance = NULL, *new_instance = NULL, *cfg_name = NULL;
	char *sec_name = NULL, *dmmap_name = NULL;

	uci_path_foreach_option_eq(bbfdm, "Routing", type, "table", value, s) {
		dmuci_get_value_by_section_string(s, "__instance__", &curr_instance);
		if (DM_STRLEN(curr_instance) == 0) {
			continue;
		}

		int curr_instance_int = DM_STRTOL(curr_instance);
		if (curr_instance_int > max_instance)
			max_instance = curr_instance_int;
	}

	dmasprintf(&new_instance, "%d", max_instance + 1);
	if (new_instance == NULL)
		return;

	dmasprintf(&cfg_name, "route_%s_%s", route_inst, new_instance);
	dmasprintf(&sec_name, "network.route_%s_%s", route_inst, new_instance);
	dmasprintf(&dmmap_name, "Router_%s%s_%s", route_inst, type, new_instance);
	if (cfg_name == NULL || sec_name == NULL || dmmap_name == NULL)
		return;

	dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "table", value);
	dmuci_rename_section_by_section(((struct dm_data*)data)->config_section, cfg_name);
	dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "table", value);
	dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "Router", route_inst);
	dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "Alias", "");
	dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "__instance__", new_instance);
	dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "__section_name__", sec_name);
	dmuci_rename_section_by_section(((struct dm_data*)data)->dmmap_section, dmmap_name);
}

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)
{
	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 statc 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) {
				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 {
				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)
{
	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 statc route -> skip it
		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 (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 {
				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 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) {
		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;
			}

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

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

	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);
	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);
	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_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_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)
{
	*value = dmuci_get_value_by_section_fallback_def(route_sec(data), "table", "-1");
	return 0;
}

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

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

			uci_path_foreach_sections(bbfdm, "Routing", "Router", s) {
				char *rt_table = NULL;

				dmuci_get_value_by_section_string(s, "rt_table", &rt_table);
				if (DM_STRCMP(value, rt_table) == 0)
					return 0;
			}

			bbfdm_set_fault_message(ctx, "Route table '%s' value doesn't exist on the device. It's only allowed to set an available route table.");
			return FAULT_9007;
		case VALUESET:
			if (((struct dm_data *)data)->config_section == NULL) // Dynamic route
				return 0;

			configure_forwarding_policy("IPv4Forwarding", data, value);
			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)
{
	*value = dmuci_get_value_by_section_fallback_def(route_sec(data), "table", "-1");
	return 0;
}

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

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

			uci_path_foreach_sections(bbfdm, "Routing", "Router", s) {
				char *rt_table = NULL;

				dmuci_get_value_by_section_string(s, "rt_table", &rt_table);
				if (DM_STRCMP(value, rt_table) == 0)
					return 0;
			}

			bbfdm_set_fault_message(ctx, "Route table '%s' value doesn't exist on the device. It's only allowed to set an available route table.");
			return FAULT_9007;
		case VALUESET:
			if (((struct dm_data *)data)->config_section == NULL) // Dynamic route
				return 0;

			configure_forwarding_policy("IPv6Forwarding", data, value);
			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 ? rt_table : instance);
	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)
{
#define LOCAL_TABLE_ID 255
	struct dm_data *curr_data = (struct dm_data *)ctx->addobj_instance;
	char rt_table[32] = {0};

	snprintf(rt_table, sizeof(rt_table), "%ld", LOCAL_TABLE_ID + DM_STRTOL(*instance) - 1);

	dmuci_set_value_by_section(curr_data->dmmap_section, "rt_table", rt_table);

	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", "");
	}

	dmuci_delete_by_section(((struct dm_data *)data)->dmmap_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");

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

				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},
{"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*/
{"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}
};

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