/*
 * Copyright (C) 2020 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 Rahul Thakur <rahul.thakur@iopsys.eu>
 *
 */

#include "dmentry.h"
#include "x_iopsys_eu_mld.h"
#include "x_iopsys_eu_igmp.h"

static int add_mld_proxy_obj(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
	struct uci_section *dmmap = NULL, *s = NULL;
	struct browse_args browse_args = {0};
	char s_name[32];

	char *inst = get_last_instance_lev2_bbfdm_dmmap_opt("dmmap_mcast", "proxy", "proxy_instance", "proto", "mld");
	snprintf(s_name, sizeof(s_name), "mld_proxy_%d", inst ? atoi(inst)+1 : 1);

	dmuci_add_section("mcast", "proxy", &s);
	dmuci_rename_section_by_section(s, s_name);
	dmuci_set_value_by_section(s, "enable", "0");
	dmuci_set_value_by_section(s, "proto", "mld");
	dmuci_set_value_by_section(s, "version", "2");
	dmuci_set_value_by_section(s, "robustness", "2");
	dmuci_set_value_by_section(s, "aggregation", "0");

	browse_args.option = "proto";
	browse_args.value = "mld";

	dmuci_add_section_bbfdm("dmmap_mcast", "proxy", &dmmap);
	dmuci_set_value_by_section(dmmap, "section_name", s_name);
	dmuci_set_value_by_section(dmmap, "proto", "mld");
	*instance = update_instance(inst, 6, dmmap, "proxy_instance", "dmmap_mcast", "proxy", check_browse_section, (void *)&browse_args);
	return 0;
}

static int del_mld_proxy_obj(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	struct uci_section *s = NULL, *ss = NULL, *dmmap_section = NULL;
	int found = 0;

	switch (del_action) {
	case DEL_INST:
		// first delete all filter child nodes related to this object
		del_dmmap_sec_with_opt_eq("dmmap_mcast", "proxy_filter",
			"section_name", section_name((struct uci_section *)data));
		// Now delete all interface child nodes related to this object
		del_dmmap_sec_with_opt_eq("dmmap_mcast", "proxy_interface",
			"section_name", section_name((struct uci_section *)data));

		// Now delete the proxy node
		get_dmmap_section_of_config_section("dmmap_mcast", "proxy", section_name((struct uci_section *)data), &dmmap_section);

		if (dmmap_section != NULL)
			dmuci_delete_by_section(dmmap_section, NULL, NULL);

		dmuci_delete_by_section((struct uci_section *)data, NULL, NULL);
		break;
	case DEL_ALL:
		uci_foreach_option_eq("mcast", "proxy", "proto", "mld", s) {
			if (found != 0){
				get_dmmap_section_of_config_section("dmmap_mcast", "proxy", section_name(s), &dmmap_section);
				if(dmmap_section != NULL)
					dmuci_delete_by_section(dmmap_section, NULL, NULL);
				dmuci_delete_by_section(ss, NULL, NULL);
			}
			ss = s;
			found++;
		}
		if (ss != NULL) {
			get_dmmap_section_of_config_section("dmmap_mcast", "proxy", section_name(ss), &dmmap_section);
			if(dmmap_section != NULL)
				dmuci_delete_by_section(dmmap_section, NULL, NULL);
			dmuci_delete_by_section(ss, NULL, NULL);
		}

		break;
	}
	return 0;
}

static int browse_mld_proxy_inst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	char *inst = NULL, *max_inst = NULL;
	struct browse_args browse_args = {0};
	struct dmmap_dup *p;
	LIST_HEAD(dup_list);

	synchronize_specific_config_sections_with_dmmap_cont("mcast", "proxy", "dmmap_mcast", "proto", "mld", &dup_list);
	list_for_each_entry(p, &dup_list, list) {

		browse_args.option = "proto";
		browse_args.value = "mld";

		inst = handle_update_instance(1, dmctx, &max_inst, update_instance_alias, 7,
			   p->dmmap_section, "proxy_instance", "proxy_alias", "dmmap_mcast", "proxy",
			   check_browse_section, (void *)&browse_args);

		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)p->config_section, inst) == DM_STOP)
			break;
	}

	free_dmmap_config_dup_list(&dup_list);
	return 0;
}

static int add_mld_snooping_obj(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
	struct uci_section  *dmmap = NULL, *s = NULL;
	struct browse_args browse_args = {0};
	char s_name[32];

	char *inst = get_last_instance_lev2_bbfdm_dmmap_opt("dmmap_mcast", "snooping", "snooping_instance", "proto", "mld");
	snprintf(s_name, sizeof(s_name), "mld_snoop_%d", inst ? atoi(inst)+1 : 1);

	dmuci_add_section("mcast", "snooping", &s);
	dmuci_rename_section_by_section(s, s_name);
	dmuci_set_value_by_section(s, "enable", "0");
	dmuci_set_value_by_section(s, "proto", "mld");
	dmuci_set_value_by_section(s, "version", "2");
	dmuci_set_value_by_section(s, "robustness", "2");
	dmuci_set_value_by_section(s, "aggregation", "0");

	browse_args.option = "proto";
	browse_args.value = "mld";

	dmuci_add_section_bbfdm("dmmap_mcast", "snooping", &dmmap);
	dmuci_set_value_by_section(dmmap, "section_name", s_name);
	dmuci_set_value_by_section(dmmap, "proto", "mld");
	*instance = update_instance(inst, 6, dmmap, "snooping_instance", "dmmap_mcast", "snooping", check_browse_section, (void *)&browse_args);
	return 0;
}

static int del_mld_snooping_obj(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	struct uci_section *s = NULL, *ss = NULL, *dmmap_section = NULL;
	int found = 0;

	switch (del_action) {
	case DEL_INST:
		// first delete all filter child nodes related to this object
		del_dmmap_sec_with_opt_eq("dmmap_mcast", "snooping_filter",
			"section_name", section_name((struct uci_section *)data));
		// Now delete all interface child nodes related to this object
		del_dmmap_sec_with_opt_eq("dmmap_mcast", "snooping_interface",
			"section_name", section_name((struct uci_section *)data));
		get_dmmap_section_of_config_section("dmmap_mcast", "snooping", section_name((struct uci_section *)data), &dmmap_section);

		if (dmmap_section != NULL)
			dmuci_delete_by_section(dmmap_section, NULL, NULL);

		dmuci_delete_by_section((struct uci_section *)data, NULL, NULL);
		break;
	case DEL_ALL:
		uci_foreach_option_eq("mcast", "snooping", "proto", "mld", s) {
			if (found != 0){
				get_dmmap_section_of_config_section("dmmap_mcast", "snooping", section_name(s), &dmmap_section);
				if(dmmap_section != NULL)
					dmuci_delete_by_section(dmmap_section, NULL, NULL);
				dmuci_delete_by_section(ss, NULL, NULL);
			}
			ss = s;
			found++;
		}
		if (ss != NULL) {
			get_dmmap_section_of_config_section("dmmap_mcast", "snooping", section_name(ss), &dmmap_section);
			if(dmmap_section != NULL)
				dmuci_delete_by_section(dmmap_section, NULL, NULL);
			dmuci_delete_by_section(ss, NULL, NULL);
		}

		break;
	}
	return 0;
}

static int browse_mld_snooping_inst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	char *inst = NULL, *max_inst = NULL;
	struct browse_args browse_args = {0};
	struct dmmap_dup *p;
	LIST_HEAD(dup_list);

	synchronize_specific_config_sections_with_dmmap_cont("mcast", "snooping", "dmmap_mcast", "proto", "mld", &dup_list);
	list_for_each_entry(p, &dup_list, list) {

		browse_args.option = "proto";
		browse_args.value = "mld";

		inst = handle_update_instance(1, dmctx, &max_inst, update_instance_alias, 7,
			   p->dmmap_section, "snooping_instance", "snooping_alias", "dmmap_mcast", "snooping",
			   check_browse_section, (void *)&browse_args);

		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)p->config_section, inst) == DM_STOP)
			break;
	}
	free_dmmap_config_dup_list(&dup_list);
	return 0;
}

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

	uci_foreach_option_eq("mcast", "snooping", "proto", "mld", s) {
		cnt++;
	}

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

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

	uci_foreach_option_eq("mcast", "proxy", "proto", "mld", s) {
		cnt++;
	}

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

#if 0
static int browse_mlds_cgrp_inst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	//ToDo
	return 0;
}

static int browse_mldp_cgrp_inst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	//ToDo
	return 0;
}
#endif

static int add_mlds_filter_obj(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
	struct uci_section *dmmap_mlds_filter = NULL;
	struct browse_args browse_args = {0};

	char *last_inst = get_last_instance_lev2_bbfdm_dmmap_opt("dmmap_mcast", "snooping_filter", "filter_instance",
					"section_name", section_name((struct uci_section *)data));

	dmuci_add_section_bbfdm("dmmap_mcast", "snooping_filter", &dmmap_mlds_filter);
	dmuci_set_value_by_section(dmmap_mlds_filter, "section_name", section_name((struct uci_section *)data));
	dmuci_set_value_by_section(dmmap_mlds_filter, "enable", "0");

	browse_args.option = "section_name";
	browse_args.value = section_name((struct uci_section *)data);

	*instance = update_instance(last_inst, 6, dmmap_mlds_filter, "filter_instance", "dmmap_mcast", "snooping_filter", check_browse_section, (void *)&browse_args);

	return 0;
}

static int browse_mlds_filter_inst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	struct dmmap_dup *p = NULL;
	struct browse_args browse_args = {0};
	char *inst = NULL, *max_inst = NULL;
	LIST_HEAD(dup_list);

	synchronize_specific_config_sections_with_dmmap_mcast_filter("mcast", "snooping", prev_data,
			"dmmap_mcast", "snooping_filter", "mld", &dup_list);
	list_for_each_entry(p, &dup_list, list) {
		if (!p->config_section)
			break;

		browse_args.option = "section_name";
		browse_args.value = section_name((struct uci_section *)prev_data);

		inst = handle_update_instance(2, dmctx, &max_inst, update_instance_alias, 7,
			   p->dmmap_section, "filter_instance", "filter_alias", "dmmap_mcast", "snooping_filter",
			   check_browse_section, (void *)&browse_args);

		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)p->config_section, inst) == DM_STOP)
			break;
	}

	free_dmmap_config_dup_list(&dup_list);
	return 0;
}

static int get_mld_snooping_version(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *val;
	dmuci_get_value_by_section_string((struct uci_section *)data, "version", &val);
	*value = (strcmp(val, "2") == 0) ? "V2" : "V1";
	return 0;
}

static int set_mld_snooping_version(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
	case VALUECHECK:
		if ((strcmp("V2", value) != 0) && (strcmp("V1", value) != 0))
			return FAULT_9007;
		break;
	case VALUESET:
		dmuci_set_value_by_section((struct uci_section *)data, "version", (strcmp(value, "V2") == 0) ? "2" : "1");
		break;
	}

	return 0;
}

static int add_mldp_interface_obj(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
	// This section works but commented for now as it is tested not much yet.
	struct uci_section *dmmap_mldp_interface = NULL;
	struct browse_args browse_args = {0};

	char *last_inst = get_last_instance_lev2_bbfdm_dmmap_opt("dmmap_mcast", "proxy_interface", "iface_instance",
					"section_name", section_name((struct uci_section *)data));

	dmuci_add_section_bbfdm("dmmap_mcast", "proxy_interface", &dmmap_mldp_interface);
	dmuci_set_value_by_section(dmmap_mldp_interface, "section_name", section_name((struct uci_section *)data));
	dmuci_set_value_by_section(dmmap_mldp_interface, "upstream", "0");
	dmuci_set_value_by_section(dmmap_mldp_interface, "snooping_mode", "0");

	browse_args.option = "section_name";
	browse_args.value = section_name((struct uci_section *)data);

	*instance = update_instance(last_inst, 6, dmmap_mldp_interface, "iface_instance", "dmmap_mcast", "proxy_interface", check_browse_section, (void *)&browse_args);
	return 0;
}

static int del_mldp_interface_obj(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	struct uci_section *d_sec = NULL;
	char *f_inst, *if_name, *upstream;
	int found = 0;

	switch (del_action) {
	case DEL_INST:
		uci_path_foreach_option_eq(bbfdm, "dmmap_mcast", "proxy_interface",
				"section_name", section_name((struct uci_section *)data), d_sec) {
			dmuci_get_value_by_section_string(d_sec, "iface_instance", &f_inst);

			if (strcmp(instance, f_inst) == 0) {
				dmuci_get_value_by_section_string(d_sec, "ifname", &if_name);
				dmuci_get_value_by_section_string(d_sec, "upstream", &upstream);
				dmuci_delete_by_section(d_sec, NULL, NULL);
				found = 1;
			}

			if (found) {
				if (strcmp(upstream, "1") == 0) {
					dmuci_del_list_value_by_section((struct uci_section *)data, "upstream_interface", if_name);
				} else {
					dmuci_del_list_value_by_section((struct uci_section *)data, "downstream_interface", if_name);
				}
				break;
			}
		}

		break;
	case DEL_ALL:
		uci_path_foreach_option_eq(bbfdm, "dmmap_mcast", "proxy_interface",
				"section_name", section_name((struct uci_section *)data), d_sec) {
			dmuci_get_value_by_section_string(d_sec, "ifname", &if_name);
			dmuci_get_value_by_section_string(d_sec, "upstream", &upstream);
			if (if_name[0] != '\0') {
				if (strcmp(upstream, "1") == 0) {
					dmuci_del_list_value_by_section((struct uci_section *)data, "upstream_interface", if_name);
				} else {
					dmuci_del_list_value_by_section((struct uci_section *)data, "downstream_interface", if_name);
				}
			}
		}

		del_dmmap_sec_with_opt_eq("dmmap_mcast", "proxy_interface",
			"section_name", section_name((struct uci_section *)data));
		break;
	}

	return 0;
}

static int browse_mldp_interface_inst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	struct dmmap_dup *p = NULL;
	struct browse_args browse_args = {0};
	char *inst = NULL, *max_inst = NULL;
	LIST_HEAD(dup_list);

	synchronize_specific_config_sections_with_dmmap_mcast_iface("mcast", "proxy", prev_data,
			"dmmap_mcast", "proxy_interface", "mld", &dup_list);
	list_for_each_entry(p, &dup_list, list) {
		if (!p->config_section)
			break;

		browse_args.option = "section_name";
		browse_args.value = section_name((struct uci_section *)prev_data);

		inst = handle_update_instance(2, dmctx, &max_inst, update_instance_alias, 7,
			   p->dmmap_section, "iface_instance", "iface_alias", "dmmap_mcast", "proxy_interface",
			   check_browse_section, (void *)&browse_args);

		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)p->config_section, inst) == DM_STOP)
			break;
	}

	free_dmmap_config_dup_list(&dup_list);
	return 0;
}

static int add_mldp_filter_obj(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
	struct uci_section *dmmap_mldp_filter = NULL;
	struct browse_args browse_args = {0};

	char *last_inst = get_last_instance_lev2_bbfdm_dmmap_opt("dmmap_mcast", "proxy_filter", "filter_instance",
			"section_name", section_name((struct uci_section *)data));

	dmuci_add_section_bbfdm("dmmap_mcast", "proxy_filter", &dmmap_mldp_filter);
	dmuci_set_value_by_section(dmmap_mldp_filter, "section_name", section_name((struct uci_section *)data));
	dmuci_set_value_by_section(dmmap_mldp_filter, "enable", "0");

	browse_args.option = "section_name";
	browse_args.value = section_name((struct uci_section *)data);

	*instance = update_instance(last_inst, 6, dmmap_mldp_filter, "filter_instance", "dmmap_mcast", "proxy_filter", check_browse_section, (void *)&browse_args);

	return 0;
}

static int browse_mldp_filter_inst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	struct dmmap_dup *p = NULL;
	struct browse_args browse_args = {0};
	char *inst = NULL, *max_inst = NULL;
	LIST_HEAD(dup_list);

	synchronize_specific_config_sections_with_dmmap_mcast_filter("mcast", "proxy", prev_data,
			"dmmap_mcast", "proxy_filter", "mld", &dup_list);
	list_for_each_entry(p, &dup_list, list) {
		if (!p->config_section)
			break;

		browse_args.option = "section_name";
		browse_args.value = section_name((struct uci_section *)prev_data);

		inst = handle_update_instance(2, dmctx, &max_inst, update_instance_alias, 7,
			   p->dmmap_section, "filter_instance", "filter_alias", "dmmap_mcast", "proxy_filter",
			   check_browse_section, (void *)&browse_args);

		if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)p->config_section, inst) == DM_STOP)
			break;
	}

	free_dmmap_config_dup_list(&dup_list);
	return 0;
}

static int set_mldp_filter_address(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct uci_section *s = NULL;
	char *s_inst, *up;
	bool b;

	switch (action) {
	case VALUECHECK:
		if (dm_validate_string(value, -1, 45, NULL, 0, IPv6Address, 1))
			return FAULT_9007;
		break;
	case VALUESET:
		uci_path_foreach_option_eq(bbfdm, "dmmap_mcast", "proxy_filter",
				"section_name", section_name((struct uci_section *)data), s) {
			dmuci_get_value_by_section_string(s, "filter_instance", &s_inst);
			if (strcmp(s_inst, instance) == 0) {
				dmuci_set_value_by_section(s, "ipaddr", value);
				dmuci_get_value_by_section_string(s, "enable", &up);
				string_to_bool(up, &b);
				sync_dmmap_bool_to_uci_list((struct uci_section *)data, "filter", value, b);
				break;
			}
		}
		break;
	}

	return 0;
}

#if 0
static int browse_mlds_cgrp_assoc_dev_inst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	//ToDo
	return 0;
}

static int browse_mldp_cgrp_assoc_dev_inst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	//ToDo
	return 0;
}

static int browse_mlds_cgrp_stats_inst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	//ToDo
	return 0;
}

static int browse_mldp_cgrp_stats_inst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	//ToDo
	return 0;
}
static int get_mlds_cgrp_gaddr(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	//ToDo
	return 0;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

static int get_mldp_cgrp_stats_lrcvd(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	//ToDo
	return 0;
}
#endif

static int get_mld_proxy_version(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *val;
	dmuci_get_value_by_section_string((struct uci_section *)data, "version", &val);
	*value = (strcmp(val, "2") == 0) ? "V2" : "V1";
	return 0;
}

static int set_mld_proxy_version(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
	case VALUECHECK:
		if ((strcmp("V2", value) != 0) && (strcmp("V1", value) != 0))
			return FAULT_9007;
		break;
	case VALUESET:
		dmuci_set_value_by_section((struct uci_section *)data, "version", (strcmp(value, "V2") == 0) ? "2" : "1");
		break;
	}

	return 0;
}

static int set_mldp_interface_iface(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *linker, *interface_linker = NULL;
	char ifname[16];
	char *up, *f_inst, *if_type;
	struct uci_section *d_sec, *s;
	bool b;

	switch (action) {
	case VALUECHECK:
		if (dm_validate_string(value, -1, 256, NULL, 0, NULL, 0))
			return FAULT_9007;
		break;
	case VALUESET:
		// First check if this is a bridge type interface
		if (get_mcast_snooping_interface_val(value, ifname, sizeof(ifname)) == 0) {
			interface_linker = dmstrdup(ifname);
		} else {
			char interface[256] = {0};

			append_dot_to_string(interface, value, sizeof(interface));
			adm_entry_get_linker_value(ctx, interface, &linker);
			uci_foreach_sections("network", "interface", s) {
				if(strcmp(section_name(s), linker) != 0) {
					continue;
				}
				dmuci_get_value_by_section_string(s, "type", &if_type);
				if (strcmp(if_type, "bridge") == 0)
					dmasprintf(&interface_linker, "br-%s", linker);
				else
					dmuci_get_value_by_section_string(s, "ifname", &interface_linker);
				break;
			}
		}
		uci_path_foreach_option_eq(bbfdm, "dmmap_mcast", "proxy_interface",
				"section_name", section_name((struct uci_section *)data), d_sec) {
			dmuci_get_value_by_section_string(d_sec, "iface_instance", &f_inst);
			if (strcmp(instance, f_inst) == 0) {
				dmuci_set_value_by_section(d_sec, "ifname", interface_linker);
				dmuci_get_value_by_section_string(d_sec, "upstream", &up);
				string_to_bool(up, &b);
				sync_dmmap_bool_to_uci_list((struct uci_section *)data, "downstream_interface", interface_linker, !b);

				// Now update the proxy_interface list
				sync_dmmap_bool_to_uci_list((struct uci_section *)data, "upstream_interface", interface_linker, b);
				update_snooping_mode((struct uci_section *)data);
				break;
			}
		}

		break;
	}

	return 0;
}

static int get_mldp_interface_iface(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *d_sec, *s;
	char *ifname, *f_inst;
	char sec_name[16] = {0};
	int found = 0;

	uci_path_foreach_option_eq(bbfdm, "dmmap_mcast", "proxy_interface",
			"section_name", section_name((struct uci_section *)data), d_sec) {
		dmuci_get_value_by_section_string(d_sec, "iface_instance", &f_inst);
		if (strcmp(instance, f_inst) == 0) {
			dmuci_get_value_by_section_string(d_sec, "ifname", &ifname);
			found = 1;
			break;
		}
	}

	if ((found == 0) || (ifname[0] == '\0')) {
		*value = "";
		return 0;
	}

	// Check if this is bridge type interface
	if (strstr(ifname, "br-")) {
		// Interface is bridge type, convert to network uci file section name
		char val[16] = {0};
		strncpy(val, ifname, sizeof(val) - 1);
		char *tok, *end;
		tok = strtok_r(val, "-", &end);
		if (strcmp(tok, "br") == 0) {
			strncpy(sec_name, end, sizeof(sec_name) - 1);
		} else {
			goto end;
		}

		char *proto;
		uci_foreach_sections("network", "interface", s) {
			if(strcmp(section_name(s), sec_name) != 0)
				continue;

			dmuci_get_value_by_section_string(s, "proto", &proto);
			if (proto[0] != '\0') {
				// It is a L3 bridge, get the linker accordingly
				adm_entry_get_linker_param(ctx, dm_print_path("%s%cIP%cInterface%c",
					dmroot, dm_delim, dm_delim, dm_delim), sec_name, value);
			} else {
				// It is a L2 bridge, get the linker accordingly
				struct uci_section *dmmap_section, *port;
				char *br_inst, *mg, linker[64] = "";

				get_dmmap_section_of_config_section("dmmap_network", "interface", sec_name,
						&dmmap_section);
				if (dmmap_section != NULL) {
					dmuci_get_value_by_section_string(dmmap_section, "bridge_instance", &br_inst);
					uci_path_foreach_option_eq(bbfdm, "dmmap_bridge_port",
							"bridge_port", "bridge_key", br_inst, port) {
						dmuci_get_value_by_section_string(port, "mg_port", &mg);
						if (strcmp(mg, "true") == 0)
							snprintf(linker, sizeof(linker), "%s+", section_name(port));
						else
							continue;

						adm_entry_get_linker_param(ctx, dm_print_path("%s%cBridging%cBridge%c", dmroot,
									dm_delim, dm_delim, dm_delim), linker, value);
						break;
					}
				}
			}
			break;
		}
	} else {
		char *device_name, *tmp_linker = NULL;
		// it is a L3 interface, get the section name from device name to construct the linker
		uci_foreach_sections("network", "interface", s) {
			dmuci_get_value_by_section_string(s, "ifname", &device_name);
			if (strcmp(device_name, ifname) == 0) {
				tmp_linker = dmstrdup(section_name(s));
				break;
			}
		}

		if (tmp_linker == NULL)
			goto end;

		adm_entry_get_linker_param(ctx, dm_print_path("%s%cIP%cInterface%c", dmroot, dm_delim,
					dm_delim, dm_delim), tmp_linker, value);
	}

end:
	if (*value == NULL)
		*value = "";

	return 0;
}

static int set_mldp_interface_upstream(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *f_inst, *ifname;
	struct uci_section *d_sec;
	bool b;

	switch (action) {
	case VALUECHECK:
		if (dm_validate_boolean(value))
			return FAULT_9007;
		break;
	case VALUESET:
		string_to_bool(value, &b);
		uci_path_foreach_option_eq(bbfdm, "dmmap_mcast", "proxy_interface",
				"section_name", section_name((struct uci_section *)data), d_sec) {
			dmuci_get_value_by_section_string(d_sec, "iface_instance", &f_inst);
			if (strcmp(instance, f_inst) == 0) {
				dmuci_get_value_by_section_string(d_sec, "ifname", &ifname);
				dmuci_set_value_by_section(d_sec, "upstream", (b) ? "1" : "0");

				sync_dmmap_bool_to_uci_list((struct uci_section *)data, "downstream_interface", ifname, !b);
				sync_dmmap_bool_to_uci_list((struct uci_section *)data, "upstream_interface", ifname, b);
				update_snooping_mode((struct uci_section *)data);

				break;
			}
		}
		break;
	}

	return 0;
}

/* ***Device.X_IOPSYS_EU_MLD. *** */
DMOBJ X_IOPSYS_EU_MLDObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, nextobj, leaf, linker, bbfdm_type, uniqueKeys*/
{"Snooping", &DMWRITE, add_mld_snooping_obj, del_mld_snooping_obj, NULL, browse_mld_snooping_inst, NULL, X_IOPSYS_EU_MLDSnoopingObj, X_IOPSYS_EU_MLDSnoopingParams, NULL, BBFDM_BOTH},
{"Proxy", &DMWRITE, add_mld_proxy_obj, del_mld_proxy_obj, NULL, browse_mld_proxy_inst, NULL, X_IOPSYS_EU_MLDProxyObj, X_IOPSYS_EU_MLDProxyParams, NULL, BBFDM_BOTH},
{0}
};

DMLEAF X_IOPSYS_EU_MLDParams[] = {
{"SnoopingNumberOfEntries", &DMREAD, DMT_UNINT, get_mlds_no_of_entries, NULL, BBFDM_BOTH},
{"ProxyNumberOfEntries", &DMREAD, DMT_UNINT, get_mldp_no_of_entries, NULL, BBFDM_BOTH},
{0}
};

DMOBJ X_IOPSYS_EU_MLDSnoopingObj[] = {
//{"ClientGroup", &DMREAD, NULL, NULL, NULL, browse_mlds_cgrp_inst, NULL, NULL, MLDSnoopingCLientGroupObj, MLDSnoopingClientGroupParams, NULL, BBFDM_BOTH},
{"Filter", &DMWRITE, add_mlds_filter_obj, del_mcasts_filter_obj, NULL, browse_mlds_filter_inst, NULL, NULL, MLDSnoopingFilterParams, NULL, BBFDM_BOTH},
{0}
};

DMOBJ MLDSnoopingCLientGroupObj[] = {
//{"AssociatedDevice", &DMREAD, NULL, NULL, NULL, browse_mlds_cgrp_assoc_dev_inst, NULL, NULL, MLDSnoopingClientGroupAssociatedDeviceParams, NULL, BBFDM_BOTH},
//{"ClientGroupStats", &DMREAD, NULL, NULL, NULL, browse_mlds_cgrp_stats_inst, NULL, NULL, MLDSnoopingClientGroupStatsParams, NULL, BBFDM_BOTH},
{0}
};

DMLEAF MLDSnoopingClientGroupParams[] = {
//{"GroupAddress", &DMREAD, DMT_STRING, get_mlds_cgrp_gaddr, NULL, BBFDM_BOTH},
//{"AssociatedDeviceNumberOfEntries", &DMREAD, DMT_UNINT, get_mlds_cgrp_assoc_dev_no_of_entries, NULL, BBFDM_BOTH},
{0}
};

DMLEAF MLDSnoopingFilterParams[] = {
{"Enable", &DMWRITE, DMT_BOOL, get_mcasts_filter_enable, set_mcasts_filter_enable, BBFDM_BOTH},
{"IPAddress", &DMWRITE, DMT_STRING, get_mcasts_filter_address, set_mcasts_filter_address, BBFDM_BOTH},
{0}
};

DMLEAF MLDSnoopingClientGroupAssociatedDeviceParams[] = {
//{"Interface", &DMREAD, DMT_STRING, get_mlds_cgrp_adev_iface, NULL, BBFDM_BOTH},
{0}
};

DMLEAF MLDSnoopingClientGroupStatsParams[] = {
//{"ReportsSent", &DMREAD, DMT_UNINT, get_mlds_cgrp_stats_rsent, NULL, BBFDM_BOTH},
//{"ReportsReceived", &DMREAD, DMT_UNINT, get_mlds_cgrp_stats_rrcvd, NULL, BBFDM_BOTH},
//{"QueriesSent", &DMREAD, DMT_UNINT, get_mlds_cgrp_stats_qsent, NULL, BBFDM_BOTH},
//{"QueriesReceived", &DMREAD, DMT_UNINT, get_mlds_cgrp_stats_qrcvd, NULL, BBFDM_BOTH},
//{"LeavesSent", &DMREAD, DMT_UNINT, get_mlds_cgrp_stats_lsent, NULL, BBFDM_BOTH},
//{"LeavesReceived", &DMREAD, DMT_UNINT, get_mlds_cgrp_stats_lrcvd, NULL, BBFDM_BOTH},
{0}
};

DMLEAF X_IOPSYS_EU_MLDSnoopingParams[] = {
{"Enable", &DMWRITE, DMT_BOOL, get_mcast_snooping_enable, set_mcast_snooping_enable, BBFDM_BOTH},
{"Version", &DMWRITE, DMT_STRING, get_mld_snooping_version, set_mld_snooping_version, BBFDM_BOTH},
{"Robustness", &DMWRITE, DMT_UNINT, get_mcast_snooping_robustness, set_mcast_snooping_robustness, BBFDM_BOTH},
{"Aggregation", &DMWRITE, DMT_BOOL, get_mcast_snooping_aggregation, set_mcast_snooping_aggregation, BBFDM_BOTH},
{"Interface", &DMWRITE, DMT_STRING, get_mcast_snooping_interface, set_mcast_snooping_interface, BBFDM_BOTH},
{"Mode", &DMWRITE, DMT_STRING, get_mcast_snooping_mode, set_mcast_snooping_mode, BBFDM_BOTH},
{"FilterNumberOfEntries", &DMREAD, DMT_UNINT, get_mcasts_filter_no_of_entries, NULL, BBFDM_BOTH},
//{"ClientGroupNumberOfEntries", &DMREAD, DMT_UNINT, get_mldp_cgrps_no_of_entries, NULL, BBFDM_BOTH},
{0}
};

DMOBJ X_IOPSYS_EU_MLDProxyObj[] = {
{"Interface", &DMWRITE, add_mldp_interface_obj, del_mldp_interface_obj, NULL, browse_mldp_interface_inst, NULL, NULL, MLDProxyInterfaceParams, NULL, BBFDM_BOTH},
//{"ClientGroup", &DMREAD, NULL, NULL, NULL, browse_mldp_cgrp_inst, NULL, MLDProxyCLientGroupObj, MLDProxyClientGroupParams, NULL, BBFDM_BOTH},
{"Filter", &DMWRITE, add_mldp_filter_obj, del_mcastp_filter_obj, NULL, browse_mldp_filter_inst, NULL, NULL, MLDProxyFilterParams, NULL, BBFDM_BOTH},
{0}
};

DMOBJ MLDProxyCLientGroupObj[] = {
//{"AssociatedDevice", &DMREAD, NULL, NULL, NULL, browse_mldp_cgrp_assoc_dev_inst, NULL, NULL, MLDProxyClientGroupAssociatedDeviceParams, NULL, BBFDM_BOTH},
//{"ClientGroupStats", &DMREAD, NULL, NULL, NULL, browse_mldp_cgrp_stats_inst, NULL, NULL, MLDProxyClientGroupStatsParams, NULL, BBFDM_BOTH},
{0}
};


DMLEAF MLDProxyClientGroupParams[] = {
//{"GroupAddress", &DMREAD, DMT_STRING, get_mldp_cgrp_gaddr, NULL, BBFDM_BOTH},
//{"AssociatedDeviceNumberOfEntries", &DMREAD, DMT_UNINT, get_mldp_cgrp_assoc_dev_no_of_entries, NULL, BBFDM_BOTH},
{0}
};

DMLEAF MLDProxyFilterParams[] = {
{"Enable", &DMWRITE, DMT_BOOL, get_mcastp_filter_enable, set_mcastp_filter_enable, BBFDM_BOTH},
{"IPAddress", &DMWRITE, DMT_STRING, get_mcastp_filter_address, set_mldp_filter_address, BBFDM_BOTH},
{0}
};

DMLEAF MLDProxyClientGroupAssociatedDeviceParams[] = {
//{"Interface", &DMREAD, DMT_STRING, get_mldp_cgrp_adev_iface, NULL, BBFDM_BOTH},
{0}
};

DMLEAF MLDProxyClientGroupStatsParams[] = {
//{"ReportsSent", &DMREAD, DMT_UNINT, get_mldp_cgrp_stats_rsent, NULL, BBFDM_BOTH},
//{"ReportsReceived", &DMREAD, DMT_UNINT, get_mldp_cgrp_stats_rrcvd, NULL, BBFDM_BOTH},
//{"QueriesSent", &DMREAD, DMT_UNINT, get_mldp_cgrp_stats_qsent, NULL, BBFDM_BOTH},
//{"QueriesReceived", &DMREAD, DMT_UNINT, get_mldp_cgrp_stats_qrcvd, NULL, BBFDM_BOTH},
//{"LeavesSent", &DMREAD, DMT_UNINT, get_mldp_cgrp_stats_lsent, NULL, BBFDM_BOTH},
//{"LeavesReceived", &DMREAD, DMT_UNINT, get_mldp_cgrp_stats_lrcvd, NULL, BBFDM_BOTH},
{0}
};

DMLEAF MLDProxyInterfaceParams[] = {
{"Interface", &DMWRITE, DMT_STRING, get_mldp_interface_iface, set_mldp_interface_iface, BBFDM_BOTH},
{"Upstream", &DMWRITE, DMT_BOOL, get_mcastp_interface_upstream, set_mldp_interface_upstream, BBFDM_BOTH},
{"SnoopingMode", &DMWRITE, DMT_STRING, get_mcastp_iface_snoop_mode, set_mcastp_iface_snoop_mode, BBFDM_BOTH},
{0}
};

DMLEAF X_IOPSYS_EU_MLDProxyParams[] = {
{"Enable", &DMWRITE, DMT_BOOL, get_mcast_proxy_enable, set_mcast_proxy_enable, BBFDM_BOTH},
{"Version", &DMWRITE, DMT_STRING, get_mld_proxy_version, set_mld_proxy_version, BBFDM_BOTH},
{"QueryInterval", &DMWRITE, DMT_UNINT, get_mcastp_query_interval, set_mcastp_query_interval, BBFDM_BOTH},
{"QueryResponseInterval", &DMWRITE, DMT_UNINT, get_mcastp_q_response_interval, set_mcastp_q_response_interval, BBFDM_BOTH},
{"LastMemberQueryInterval", &DMWRITE, DMT_UNINT, get_mcastp_last_mq_interval, set_mcastp_last_mq_interval, BBFDM_BOTH},
{"ImmediateLeave", &DMWRITE, DMT_BOOL, get_mcast_proxy_fast_leave, set_mcast_proxy_fast_leave, BBFDM_BOTH},
{"Robustness", &DMWRITE, DMT_UNINT, get_mcast_proxy_robustness, set_mcast_proxy_robustness, BBFDM_BOTH},
{"Aggregation", &DMWRITE, DMT_BOOL, get_mcast_proxy_aggregation, set_mcast_proxy_aggregation, BBFDM_BOTH},
{"FilterNumberOfEntries", &DMREAD, DMT_UNINT, get_mcastp_filter_no_of_entries, NULL, BBFDM_BOTH},
{"InterfaceNumberOfEntries", &DMREAD, DMT_UNINT, get_mcastp_interface_no_of_entries, NULL, BBFDM_BOTH},
{0}
};
