/*
 * 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: Anis Ellouze <anis.ellouze@pivasoftware.com>
 *	Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
 *
 */

#include <libbbfdm-ubus/bbfdm-ubus.h>
#include "ethernet.h"
#include "dmlayer.h"
#include "helper.h"

/*************************************************************
* COMMON FUNCTIONS
**************************************************************/
static uint64_t get_ifstat_value(const char *name, struct eth_stats *stats)
{
	if (name == NULL || stats == NULL)
		return 0;

	if (DM_STRCMP(name, "tx_bytes") == 0)
		return stats->tx_bytes;
	else if (DM_STRCMP(name, "tx_packets") == 0)
		return stats->tx_packets;
	else if (DM_STRCMP(name, "tx_errors") == 0)
		return stats->tx_errors;
	else if (DM_STRCMP(name, "tx_unicast_packets") == 0)
		return stats->tx_ucast_packets;
	else if (DM_STRCMP(name, "tx_multicast_packets") == 0)
		return stats->tx_mcast_packets;
	else if (DM_STRCMP(name, "tx_broadcast_packets") == 0)
		return stats->tx_bcast_packets;
	else if (DM_STRCMP(name, "tx_discard_packets") == 0)
		return stats->tx_discard_packets;
	else if (DM_STRCMP(name, "rx_bytes") == 0)
		return stats->rx_bytes;
	else if (DM_STRCMP(name, "rx_packets") == 0)
		return stats->rx_packets;
	else if (DM_STRCMP(name, "rx_errors") == 0)
		return stats->rx_errors;
	else if (DM_STRCMP(name, "rx_unicast_packets") == 0)
		return stats->rx_ucast_packets;
	else if (DM_STRCMP(name, "rx_multicast_packets") == 0)
		return stats->rx_mcast_packets;
	else if (DM_STRCMP(name, "rx_broadcast_packets") == 0)
		return stats->rx_bcast_packets;
	else if (DM_STRCMP(name, "rx_discard_packets") == 0)
		return stats->rx_discard_packets;
	else if (DM_STRCMP(name, "rx_unknown_packets") == 0)
		return stats->rx_unknown_packets;
	else
		return 0;
}

static bool check_vlan_termination_section(const char *name)
{
	struct uci_section *s = NULL;

	uci_foreach_option_eq("network", "device", "type", "bridge", s) {
		struct uci_list *uci_list = NULL;

		dmuci_get_value_by_section_list(s, "ports", &uci_list);

		if (uci_list == NULL)
			continue;

		if (value_exists_in_uci_list(uci_list, name))
			return false;
	}

	return true;
}

static int eth_iface_sysfs(struct uci_section *data, const char *option_name, const char *name, char **value)
{
	char *device;

	dmuci_get_value_by_section_string(data, option_name, &device);
	return get_net_device_sysfs(device, name, value);
}

static int eth_port_sysfs(struct dm_data *args, const char *name, char **value)
{
	char *ifname = NULL;
	dmuci_get_value_by_section_string(args->config_section, "name", &ifname);
	return get_net_device_sysfs(ifname, name, value);
}

static struct uci_section *is_ethernet_link_exist(char *device)
{
	struct uci_section *s = NULL;

	uci_path_foreach_sections(bbfdm, "Ethernet", "Link", s) {
		char *curr_dev = NULL;

		dmuci_get_value_by_section_string(s, "Name", &curr_dev);
		if (DM_STRCMP(curr_dev, device) == 0)
			return s;
	}

	return NULL;
}

static bool is_mac_vlan_interface(char *device_name)
{
	struct uci_section *s = NULL;
	char *type = NULL, *name = NULL;

	uci_foreach_sections("network", "device", s) {

		dmuci_get_value_by_section_string(s, "type", &type);
		dmuci_get_value_by_section_string(s, "name", &name);

		if (DM_STRCMP(type, "macvlan") == 0 && DM_STRCMP(name, device_name) == 0)
			return true;
	}

	return false;
}

static void dmmap_synchronizeEthernet_Link(struct dmctx *dmctx)
{
	struct uci_section *s = NULL, *dmmap_s = NULL;
	char *instance = NULL;

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

		// Skip this interface section if its proto option is empty
		char *proto = NULL;
		dmuci_get_value_by_section_string(s, "proto", &proto);
		if (DM_STRLEN(proto) == 0)
			continue;

		// Skip this interface section if its name is equal to loopback
		if (strcmp(section_name(s), "loopback") == 0)
			continue;

		// Skip this interface section if its device option is empty or has the section_name value
		char *device = NULL;
		dmuci_get_value_by_section_string(s, "device", &device);
		if (DM_STRLEN(device) == 0 || strcmp(section_name(s), device) == 0)
			continue;

		char *macaddr = NULL;
		get_net_device_sysfs(device, "address", &macaddr);

		char *dev_name = ethernet___get_ethernet_interface_name(device);

		if (is_mac_vlan_interface(dev_name)) {
			char *p = DM_STRRCHR(dev_name, '_');
			if (p)
				*p = '\0';
		}

		if (is_ethernet_link_exist(dev_name))
			continue;

		/* Add a new Ethernet Link section */
		dmmap_s = create_dmmap_obj(dmctx, 0, "Ethernet", "Link", NULL, &instance);
		dmuci_set_value_by_section(dmmap_s, "MACAddress", macaddr);
		dmuci_set_value_by_section(dmmap_s, "Name", dev_name);
		dmuci_set_value_by_section(dmmap_s, "device", device);
	}
}

static void dmmap_create_vlanterm_from_bridge_vlan(struct dmctx *dmctx)
{
	struct uci_section *bv_s = NULL;

	uci_foreach_sections("network", "bridge-vlan", bv_s) {

		char *bridge = NULL, *vid = NULL;
		dmuci_get_value_by_section_string(bv_s, "device", &bridge);
		dmuci_get_value_by_section_string(bv_s, "vlan", &vid);

		if (DM_STRLEN(bridge) == 0 || DM_STRLEN(vid) == 0)
			continue;

		/* Construct interface name: br-bridge1.100 */
		char name[128] = {0};
		snprintf(name, sizeof(name), "%s.%s", bridge, vid);

		/* Check if any interface references this device */
		struct uci_section *iface_s = NULL;
		bool used = false;

		uci_foreach_sections("network", "interface", iface_s) {
			char *dev = NULL;
			dmuci_get_value_by_section_string(iface_s, "device", &dev);

			if (DM_STRCMP(dev, name) == 0) {
				used = true;
				break;
			}
		}

		if (!used)
			continue;

		/* Ensure Ethernet.Link exists for bridge */
		struct uci_section *link_s = is_ethernet_link_exist(bridge);

		char *link_inst = NULL;
		if (!link_s) {
			link_s = create_dmmap_obj(dmctx, 0, "Ethernet", "Link", NULL, &link_inst);
			if (link_s)
				dmuci_set_value_by_section(link_s, "Name", bridge);
		}

		/* Avoid duplicate VLANTermination */
		if (get_dup_section_in_dmmap_opt("Ethernet", "VLANTermination", "Name", name) != NULL)
			continue;

		/* Create VLANTermination */
		char *instance = NULL;
		struct uci_section *dmmap_s =
			create_dmmap_obj(dmctx, 0, "Ethernet", "VLANTermination", NULL, &instance);

		if (!dmmap_s)
			continue;

		dmuci_set_value_by_section(dmmap_s, "Name", name);
		dmuci_set_value_by_section(dmmap_s, "VLANID", vid);
		dmuci_set_value_by_section(dmmap_s, "__ifname__", bridge);
		dmuci_set_value_by_section(dmmap_s, "TPID", "0");

		/* Set LowerLayers */
		if ((DM_STRLEN(link_inst) == 0)) {
			if (link_s != NULL) {
				dmuci_get_value_by_section_string(link_s, "__instance__", &link_inst);
			}
		}

		if (link_inst) {
			char lower[128] = {0};
			snprintf(lower, sizeof(lower), "Device.Ethernet.Link.%s", link_inst);
			dmuci_set_value_by_section(dmmap_s, "LowerLayers", lower);
		}
	}
}

static void dmmap_synchronizeEthernet_Interface_VLANTermination_MACVLAN(struct dmctx *dmctx)
{
	struct uci_section *interface_s = NULL;
	char *val = NULL;

	dmuci_get_option_value_string_bbfdm("Ethernet", "global", "add_rmon", &val);
	bool add_rmon = DM_STRLEN(val) ? dmuci_string_to_boolean(val) : true;

	uci_foreach_sections("network", "device", interface_s) {
		char *instance = NULL;

		char *type = NULL;
		dmuci_get_value_by_section_string(interface_s, "type", &type);
		if (DM_STRLEN(type) == 0) {
			// Device.Ethernet.Interface.{i}.
			create_dmmap_obj(dmctx, 0, "Ethernet", "Interface", interface_s, &instance);

			if (add_rmon == true) {
				// Device.Ethernet.RMONStats.{i}. for each ethernet interface
				char *intf = NULL;
				dmuci_get_value_by_section_string(interface_s, "name", &intf);
				if (DM_STRLEN(intf) == 0)
					continue;

				// Check if RMONStats section already present
				if (get_dup_section_in_dmmap_opt("Ethernet", "RMONStats", "Interface", intf) != NULL)
					continue;

				char *rmon_inst = NULL;
				struct uci_section *sec = create_dmmap_obj(dmctx, 0, "Ethernet", "RMONStats", NULL, &rmon_inst);
				if (sec != NULL) {
					dmuci_set_value_by_section(sec, "Enable", "1");
					dmuci_set_value_by_section(sec, "Interface", intf);
				}
			}
		}
#ifdef ETHMNGR_MACVLAN_EXTENSION
		else if (DM_LSTRCMP(type, "macvlan") == 0) {
			// Device.Ethernet.XXX_MACVLAN.{i}.
			char *name = NULL;
			dmuci_get_value_by_section_string(interface_s, "name", &name);

			struct uci_section *dmmap_s = create_dmmap_obj(dmctx, 0, "Ethernet", BBF_VENDOR_PREFIX"MACVLAN", interface_s, &instance);
			if (dmmap_s != NULL) {
				dmuci_set_value_by_section(dmmap_s, "Name", name);
			}
		}
#endif
		else {
			char *name = NULL;

			dmuci_get_value_by_section_string(interface_s, "name", &name);
			if (DM_LSTRCMP(type, "bridge") == 0 || // Device.Bridging.Bridge.{i}.
					DM_LSTRCMP(type, "tunnel") == 0 || // Device.GRE.Tunnel.{i}.
					(*name != 0 && !check_vlan_termination_section(name)) || // Device.Ethernet.VLANTermination.{i}.
					(*name == 0 && strncmp(section_name(interface_s), "br_", 3) == 0)) // Device.Bridging.ProviderBridge.{i}.
				continue;

			// Device.Ethernet.VLANTermination.{i}.
			struct uci_section *dmmap_s = create_dmmap_obj(dmctx, 0, "Ethernet", "VLANTermination", interface_s, &instance);
			if (dmmap_s != NULL) {
				dmuci_set_value_by_section(dmmap_s, "Name", name);

				if (!DM_LSTRCMP(type, "8021q") || !DM_LSTRCMP(type, "8021ad")) {
					char *vid = NULL, *ifname = NULL, *enabled = NULL;

					dmuci_get_value_by_section_string(interface_s, "ifname", &ifname);
					dmuci_get_value_by_section_string(interface_s, "vid", &vid);
					dmuci_get_value_by_section_string(interface_s, "enabled", &enabled);

					dmuci_set_value_by_section(dmmap_s, "VLANID", vid);
					dmuci_set_value_by_section(dmmap_s, "__ifname__", ifname);
					dmuci_set_value_by_section(dmmap_s, "Enable", enabled);
				}
			}
		}
	}

	dmmap_create_vlanterm_from_bridge_vlan(dmctx);

	if (add_rmon == true) {
		dmuci_set_value_bbfdm("Ethernet", "global", "add_rmon", "0");
	}
}

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

	uci_path_foreach_sections(bbfdm, "Ethernet", "RMONStats", curr_data.dmmap_section) {

		char *intf = NULL, *enab = NULL;
		struct eth_rmon_stats rmon = {0};
		bool enable = false;

		dmuci_get_value_by_section_string(curr_data.dmmap_section, "Enable", &enab);
		string_to_bool(enab, &enable);
		if (enable == false) {
			rmon.status = RMON_STATUS_DISABLED;
			goto end;
		}

		dmuci_get_value_by_section_string(curr_data.dmmap_section, "Interface", &intf);
		if (DM_STRLEN(intf) == 0) {
			rmon.status = RMON_STATUS_ERR_MISCONF;
			goto end;
		}

		if (ethmngr_get_rmon_stats(intf, &rmon, ETH_TXQ_ALL, 0) != 0) {
			rmon.status = RMON_STATUS_ERR;
			goto end;
		}

		rmon.status = RMON_STATUS_ENABLED;

		end:
			curr_data.additional_data = (void *)&rmon;
			inst = uci_handle_instance(dmctx, parent_node, &curr_data);
			if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&curr_data, inst) == DM_STOP)
				break;
	}

	return 0;
}

/*************************************************************
* ADD & DEL OBJ
**************************************************************/
static int addObjEthernetLink(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
	return 0;
}

static int delObjEthernetLink(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	// Update Ethernet Link Top Layers
	ethernet___Update_Link_Top_Layers(refparam, "");

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

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

static int delObjEthernetVLANTermination(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	// Update Ethernet VLAN Termination Top Layers
	ethernet___Update_VLAN_Termination_Top_Layers(refparam, "");

	// Remove device section
	if (((struct dm_data *)data)->config_section)
		dmuci_delete_by_section(((struct dm_data *)data)->config_section, NULL, NULL);

	// Remove device section in dmmap_network file
	dmuci_delete_by_section(((struct dm_data *)data)->dmmap_section, NULL, NULL);

	return 0;
}

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

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

/*************************************************************
* GET & SET PARAM
**************************************************************/
static int get_Ethernet_FlowControlSupported(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmstrdup("1");
	return 0;
}

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

static int get_Ethernet_LinkNumberOfEntries(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_Ethernet_VLANTerminationNumberOfEntries(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_Ethernet_RMONStatsNumberOfEntries(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_EthernetInterface_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->config_section, "enabled", "1");
	return 0;
}

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

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

	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "name", &name);
	return get_net_device_status(name, value);
}

static int get_EthernetInterface_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "Alias", value);
	if ((*value)[0] == '\0') {
		*value = dmstrdup(section_name(((struct dm_data *)data)->config_section));
		dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "Alias", *value);
	}
	return 0;
}

static int set_EthernetInterface_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;
			return 0;
		case VALUESET:
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "Alias", value);
			return 0;
	}
	return 0;
}

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

static int get_EthernetInterface_LastChange(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	json_object *res = NULL;
	struct uci_section *s = NULL, *dev_s = NULL;
	struct uci_list *uci_list = NULL;
	char *device;
	int intf_found = 0;
	char *name = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "name", &name);
	uci_foreach_sections("network", "interface", s) {
		dmuci_get_value_by_section_string(s, "device", &device);
		if (0 == DM_LSTRNCMP(device, "br-", 3)) {
			uci_foreach_option_eq("network", "device", "name", device, dev_s) {
				dmuci_get_value_by_section_list(dev_s, "ports", &uci_list);
				if (value_exists_in_uci_list(uci_list, name)) {
					intf_found = 1;
				}
			}
		} else {
			if (DM_STRSTR(device, name)) {
				intf_found = 1;
			}
		}

		if (1 == intf_found) {
			char *if_name = section_name(s);
			dmubus_call("network.interface", "status", UBUS_ARGS{{"interface", if_name, String}}, 1, &res);
			if (res == NULL) {
				*value = dmstrdup("0");
				return 0;
			}

			*value = dmjson_get_value(res, 1, "uptime");
			if (DM_STRLEN(*value) == 0)
				*value = dmstrdup("0");
			return 0;
		}
	}

	if(DM_STRLEN(*value) == 0)
		*value = dmstrdup("0");

	return 0;
}

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

static int set_EthernetInterface_LowerLayers(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_string_list(ctx, value, -1, -1, 1024, -1, -1, NULL, NULL))
				return FAULT_9007;

			if (*value != '\0')
				return FAULT_9007;

			break;
		case VALUESET:
			break;
	}
	return 0;
}

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

	if (!file_exists(BOARD_JSON_FILE)) {
		*value = dmstrdup("0");
		return 0;
	}

	json_object *json_obj = json_object_from_file(BOARD_JSON_FILE);
	if (!json_obj) {
		*value = dmstrdup("0");
		return 0;
	}

	char *device = dmjson_get_value(json_obj, 3, "network", "wan", "device");
	if (DM_STRLEN(device) == 0) {
		*value = dmstrdup("0");
		goto end;
	}

	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "name", &name);
	*value = (DM_STRCMP(device, name) == 0) ? dmstrdup("1") : dmstrdup("0");

end:
	json_object_put(json_obj);
	return 0;
}

static int get_EthernetInterface_MACAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_port_sysfs(data, "address", value);
}

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

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

	if (autoneg && DM_LSTRCMP(autoneg, "0") == 0)
		dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "speed", value);
	else
		*value = dmstrdup("-1");

	return 0;
}

static int set_EthernetInterface_MaxBitRate(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 (DM_LSTRCMP(value, "-1") == 0)
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "autoneg", "1");
			else {
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "autoneg", "0");
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "speed", value);
			}
			return 0;
	}
	return 0;
}

static int get_EthernetInterface_CurrentBitRate(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	json_object *res = NULL;
	int speed = 0;
	char *name = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "name", &name);
	dmubus_call("network.device", "status", UBUS_ARGS{{"name", name, String}}, 1, &res);
	if (res == NULL) {
		*value = dmstrdup("0");
		return 0;
	}

	*value = dmjson_get_value(res, 1, "speed");
	sscanf(*value, "%d%*c", &speed);
	dmasprintf(value, "%d", speed);
	return 0;
}

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

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

	if (autoneg && DM_LSTRCMP(autoneg, "0") == 0) {
		char *duplex = NULL;

		dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "duplex", &duplex);
		*value = (duplex && DM_LSTRCMP(duplex, "full") == 0) ? "Full" : "Half";
	} else {
		*value = dmstrdup("Auto");
	}
	return 0;
}

static int set_EthernetInterface_DuplexMode(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *Duplex_Mode[] = {"Half", "Full", "Auto", NULL};

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, -1, Duplex_Mode, NULL))
				return FAULT_9007;
			return 0;
		case VALUESET:
			if (DM_LSTRCMP(value, "Auto") == 0)
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "autoneg", "1");
			else {
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "autoneg", "0");
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "duplex", (*value == 'F') ? "full" : "half");
			}
			return 0;
	}
	return 0;
}

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

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

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

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			break;
		case VALUESET:
			string_to_bool(value, &b);
			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "eee", b ? "1" : "0");
			break;
	}
	return 0;
}

static int get_EthernetInterfaceStats_BytesSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_port_sysfs(data, "statistics/tx_bytes", value);
}

static int get_EthernetInterfaceStats_BytesReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_port_sysfs(data, "statistics/rx_bytes", value);
}

static int get_EthernetInterfaceStats_PacketsSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_port_sysfs(data, "statistics/tx_packets", value);
}

static int get_EthernetInterfaceStats_PacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_port_sysfs(data, "statistics/rx_packets", value);
}

static int get_EthernetInterfaceStats_ErrorsSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_port_sysfs(data, "statistics/tx_errors", value);
}

static int get_EthernetInterfaceStats_ErrorsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_port_sysfs(data, "statistics/rx_errors", value);
}

static int get_EthernetInterfaceStats_DiscardPacketsSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_port_sysfs(data, "statistics/tx_dropped", value);
}

static int get_EthernetInterfaceStats_DiscardPacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_port_sysfs(data, "statistics/rx_dropped", value);
}

static int get_EthernetInterfaceStats_MulticastPacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_port_sysfs(data, "statistics/multicast", value);
}

static int eth_port_ubus(struct dm_data *args, const char *name, char **value)
{
	if (args == NULL) {
		*value = dmstrdup("0");
		return 0;
	}

	char *if_name = NULL;
	dmuci_get_value_by_section_string(((struct dm_data *)args)->config_section, "name", &if_name);

	struct eth_stats stats = {0};
	if (ethmngr_get_if_stats(if_name, &stats) != 0) {
		*value = dmstrdup("0");
		return 0;
	}

	uint64_t count = get_ifstat_value(name, &stats);
	dmasprintf(value, "%ju", (uintmax_t)count);

	return 0;
}

static int get_EthernetInterfaceStats_MulticastPacketsSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_port_ubus(data, "tx_multicast_packets", value);
}

static int get_EthernetInterfaceStats_UnicastPacketsSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_port_ubus(data, "tx_unicast_packets", value);
}

static int get_EthernetInterfaceStats_UnicastPacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_port_ubus(data, "rx_unicast_packets", value);
}

static int get_EthernetInterfaceStats_BroadcastPacketsSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_port_ubus(data, "tx_broadcast_packets", value);
}

static int get_EthernetInterfaceStats_BroadcastPacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_port_ubus(data, "rx_broadcast_packets", value);
}

static int get_EthernetInterfaceStats_UnknownProtoPacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_port_ubus(data, "rx_unknown_packets", value);
}

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

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

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

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

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "Name", &device);
	return get_net_device_status(device, value);
}

static int get_EthernetLink_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_EthernetLink_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_EthernetLink_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 get_EthernetLink_LastChange(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_section *s = NULL;
	char *device = NULL;

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

	uci_foreach_option_cont("network", "interface", "device", device, s) {
		json_object *res = NULL;

		dmubus_call("network.interface", "status", UBUS_ARGS{{"interface", section_name(s), String}}, 1, &res);
		if (res == NULL) {
			*value = dmstrdup("0");
			break;
		}

		*value = dmjson_get_value(res, 1, "uptime");
		break;
	}
	return 0;
}

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

	if ((*value)[0] == '\0') {
		char buf[4096] = {0};
		char *linker = NULL;
		char *device = NULL;

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

		dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "Name", &linker);
		if (DM_STRLEN(linker) == 0)
			return 0;

		bbfdm_get_references(ctx, MATCH_FIRST, "Device.ATM.Link.", "Name", linker, buf, sizeof(buf));
		bbfdm_get_references(ctx, MATCH_FIRST, "Device.PTM.Link.", "Name", linker, buf, sizeof(buf));
		bbfdm_get_references(ctx, MATCH_FIRST, "Device.Bridging.Bridge.*.Port.", "Name", linker, buf, sizeof(buf));

		*value = dmstrdup(buf);

		if (DM_STRLEN(buf) == 0 || (DM_STRCMP(device, linker) != 0 && DM_STRSTR(buf, "Device.Bridging.Bridge.") != NULL)) {
			memset(buf, 0, sizeof(buf));

			bbfdm_get_references(ctx, MATCH_FIRST, "Device.Ethernet.Interface.", "Name", linker, buf, sizeof(buf));
			if (DM_STRLEN(buf) != 0)
				*value = dmstrdup(buf);
		}

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

	return 0;
}

static int set_EthernetLink_LowerLayers(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *allowed_objects[] = {
			"Device.Ethernet.Interface.",
			"Device.Bridging.Bridge.*.Port.",
			"Device.ATM.Link.",
			"Device.PTM.Link.",
			NULL};
	struct dm_reference reference = {0};

	bbfdm_get_reference_linker(ctx, value, &reference);

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

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

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

			// Update device option
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "Name", reference.value);

			if (match(reference.path, "Device.Bridging.Bridge.*.Port.", 0, NULL)) {
				// Remove unused Interface section created by Bridge Object if it exists
				struct uci_section *s = get_dup_section_in_config_opt("network", "interface", "device", reference.value);
				dmuci_delete_by_section(s, NULL, NULL);
			}

			// Update Ethernet Link Top Layers
			ethernet___Update_Link_Top_Layers(refparam, reference.value);
			break;
	}
	return 0;
}

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

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "Name", &device);
	if (!DM_STRLEN(device))
		return 0;

	if (!DM_LSTRNCMP(device, "atm", 3) || !DM_LSTRNCMP(device, "ptm", 3))
		return 0;

	char *is_bridge = DM_LSTRSTR(device, "br-");
	if (is_bridge) {
		/* Ethernet.Link.{i}. ---> Bridging.Bridge.{i}.Port.{i}. */
		device_s = get_dup_section_in_config_opt("network", "device", "name", device);
	} else {
		/* Ethernet.Link.{i}. ---> Ethernet.Interface.{i}. */
		device_s = ethernet___get_ethernet_interface_section(device);
	}

	if (device_s) {
		*value = dmuci_get_value_by_section_fallback_def(device_s, "mtu", "1500");
	}

	return 0;
}

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

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{"64","65535"}}, 1))
				return FAULT_9007;
			break;
		case VALUESET:
			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "Name", &device);
			if (!DM_STRLEN(device))
				break;

			if (!DM_LSTRNCMP(device, "atm", 3) || !DM_LSTRNCMP(device, "ptm", 3))
				break;

			char *is_bridge = DM_LSTRSTR(device, "br-");
			if (is_bridge) {
				/* Ethernet.Link.{i}. ---> Bridging.Bridge.{i}.Port.{i}. */
				device_s = get_dup_section_in_config_opt("network", "device", "name", device);
			} else {
				/* Ethernet.Link.{i}. ---> Ethernet.Interface.{i}. */
				device_s = ethernet___get_ethernet_interface_section(device);
			}
		
			if (device_s) {
				dmuci_set_value_by_section(device_s, "mtu", value);
			}

			break;
	}
	return 0;
}

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

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

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "Name", &device);
	if (!DM_STRLEN(device))
		goto end;

	if (!DM_LSTRNCMP(device, "atm", 3) || !DM_LSTRNCMP(device, "ptm", 3))
		goto end;

	char *is_bridge = DM_LSTRSTR(device, "br-");
	if (is_bridge) {
		/* Ethernet.Link.{i}. ---> Bridging.Bridge.{i}.Port.{i}. */
		struct uci_list *ports_list = NULL;

		struct uci_section *dev_s = get_dup_section_in_config_opt("network", "device", "name", device);
		if (!dev_s)
			goto end;

		dmuci_get_value_by_section_list(dev_s, "ports", &ports_list);
		if (ports_list != NULL) {
			struct uci_element *e = NULL;

			uci_foreach_element(ports_list, e) {
				char buf[16] = {0};

				DM_STRNCPY(buf, e->name, sizeof(buf));

				if (!ethernet___get_ethernet_interface_section(buf)) {
					char *is_tagged = DM_STRRCHR(buf, '.');
					if (is_tagged)
						*is_tagged = 0;
				}

				port_s = ethernet___get_ethernet_interface_section(buf);
				char *pause = port_s ? dmuci_get_value_by_section_fallback_def(port_s, "pause", "0") : "0";
				char *curr_value = dmuci_string_to_boolean(pause) ? "1" : "0";

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

			*value = dmstrdup("0");
			return 0;
		}
	} else {
		/* Ethernet.Link.{i}. ---> Ethernet.Interface.{i}. */

		port_s = ethernet___get_ethernet_interface_section(device);
		char *pause = port_s ? dmuci_get_value_by_section_fallback_def(port_s, "pause", "0") : "0";
		*value = dmuci_string_to_boolean(pause) ? "1" : "0";
		return 0;
	}

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

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

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

			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "Name", &device);
			if (!DM_STRLEN(device))
				break;

			if (!DM_LSTRNCMP(device, "atm", 3) || !DM_LSTRNCMP(device, "ptm", 3))
				break;

			char *is_bridge = DM_LSTRSTR(device, "br-");
			if (is_bridge) {
				/* Ethernet.Link.{i}. ---> Bridging.Bridge.{i}.Port.{i}. */

				struct uci_section *dev_s = get_dup_section_in_config_opt("network", "device", "name", device);
				if (dev_s) {
					struct uci_list *ports_list = NULL;

					dmuci_get_value_by_section_list(dev_s, "ports", &ports_list);
					if (ports_list != NULL) {
						struct uci_element *e = NULL;

						uci_foreach_element(ports_list, e) {
							char buf[16] = {0};

							DM_STRNCPY(buf, e->name, sizeof(buf));

							if (!ethernet___get_ethernet_interface_section(buf)) {
								char *is_tagged = DM_STRRCHR(buf, '.');
								if (is_tagged)
									*is_tagged = 0;
							}

							port_s = ethernet___get_ethernet_interface_section(buf);
							if (port_s) {
								dmuci_set_value_by_section(port_s, "pause", b ? "1" : "0");
								dmuci_set_value_by_section(port_s, "rxpause", b ? "1" : "0");
								dmuci_set_value_by_section(port_s, "txpause", b ? "1" : "0");
							}
						}
					}
				}
			} else {
				/* Ethernet.Link.{i}. ---> Ethernet.Interface.{i}. */

				port_s = ethernet___get_ethernet_interface_section(device);
				if (port_s) {
					dmuci_set_value_by_section(port_s, "pause", b ? "1" : "0");
					dmuci_set_value_by_section(port_s, "rxpause", b ? "1" : "0");
					dmuci_set_value_by_section(port_s, "txpause", b ? "1" : "0");
				}
			}
			break;
	}
	return 0;
}

static int get_EthernetLinkStats_BytesSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_sysfs(((struct dm_data *)data)->dmmap_section, "Name", "statistics/tx_bytes", value);
}

static int get_EthernetLinkStats_BytesReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_sysfs(((struct dm_data *)data)->dmmap_section, "Name", "statistics/rx_bytes", value);
}

static int get_EthernetLinkStats_PacketsSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_sysfs(((struct dm_data *)data)->dmmap_section, "Name", "statistics/tx_packets", value);
}

static int get_EthernetLinkStats_PacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_sysfs(((struct dm_data *)data)->dmmap_section, "Name", "statistics/rx_packets", value);
}

static int get_EthernetLinkStats_ErrorsSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_sysfs(((struct dm_data *)data)->dmmap_section, "Name", "statistics/tx_errors", value);
}

static int get_EthernetLinkStats_ErrorsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_sysfs(((struct dm_data *)data)->dmmap_section, "Name", "statistics/rx_errors", value);
}

static int get_EthernetLinkStats_DiscardPacketsSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_sysfs(((struct dm_data *)data)->dmmap_section, "Name", "statistics/tx_dropped", value);
}

static int get_EthernetLinkStats_DiscardPacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_sysfs(((struct dm_data *)data)->dmmap_section, "Name", "statistics/rx_dropped", value);
}

static int get_EthernetLinkStats_MulticastPacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_sysfs(((struct dm_data *)data)->dmmap_section, "Name", "statistics/multicast", value);
}

static int eth_iface_ubus(struct uci_section *iface_s, const char *name, char **value)
{
	char *device = NULL;

	dmuci_get_value_by_section_string(iface_s, "Name", &device);

	struct eth_stats stats = {0};
	if (ethmngr_get_if_stats(device, &stats) != 0) {
		*value = dmstrdup("0");
		return 0;
	}

	uint64_t count = get_ifstat_value(name, &stats);
	dmasprintf(value, "%ju", (uintmax_t)count);

	return 0;
}

static int get_EthernetLinkStats_MulticastPacketsSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_ubus(((struct dm_data *)data)->dmmap_section, "tx_multicast_packets", value);
}

static int get_EthernetLinkStats_UnicastPacketsSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_ubus(((struct dm_data *)data)->dmmap_section, "tx_unicast_packets", value);
}

static int get_EthernetLinkStats_UnicastPacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_ubus(((struct dm_data *)data)->dmmap_section, "rx_unicast_packets", value);
}

static int get_EthernetLinkStats_BroadcastPacketsSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_ubus(((struct dm_data *)data)->dmmap_section, "tx_broadcast_packets", value);
}

static int get_EthernetLinkStats_BroadcastPacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_ubus(((struct dm_data *)data)->dmmap_section, "rx_broadcast_packets", value);
}

static int get_EthernetLinkStats_UnknownProtoPacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_ubus(((struct dm_data *)data)->dmmap_section, "rx_unknown_packets", value);
}

static int get_EthernetVLANTermination_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->dmmap_section, "Enable", "1");
	return 0;
}

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

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

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

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

	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "Name", &name);
	return get_net_device_status(name, value);
}

static int get_EthernetVLANTermination_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_EthernetVLANTermination_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_EthernetVLANTermination_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 get_EthernetVLANTermination_LowerLayers(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "LowerLayers", value);

	if (DM_STRLEN(*value) == 0) {
		if (((struct dm_data *)data)->config_section == NULL) {
			*value = dmstrdup("");
		}

		char *tpid = NULL, *ifname = NULL;

		dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "TPID", &tpid);
		dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "__ifname__", &ifname);

		ctx->bbfdm_api_version = BBFDM_API_V1;

		if (DM_LSTRCMP(tpid, "34984") == 0) {
			_bbfdm_get_references(ctx, "Device.Ethernet.VLANTermination.", "Name", ifname, value);
		} else {
			_bbfdm_get_references(ctx, "Device.Ethernet.Link.", "Name", ifname, value);
		}

		// Store LowerLayers value
		dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "LowerLayers", *value);
	} else {
		if (!adm_entry_object_exists(ctx, *value))
			*value = dmstrdup("");
	}

	return 0;
}

static int set_EthernetVLANTermination_LowerLayers(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *allowed_objects[] = {
			"Device.Ethernet.VLANTermination.",
			"Device.Ethernet.Link.",
			NULL};
	struct dm_reference reference = {0};
	char name[32] = {0};

	bbfdm_get_reference_linker(ctx, value, &reference);

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

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

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

			if (!DM_STRLEN(reference.value)) { /*empty */
				if (((struct dm_data *)data)->config_section == NULL) {
					// It was pointing to bridge instance so reset the TPID to default 33024
					dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "TPID", "33024");
				}

				// Update ifname and name options
				dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "__ifname__", "");
				dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "Name", "");

				if (((struct dm_data *)data)->config_section != NULL) {
					dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "ifname", "");
					dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "name", "");
				}

				// Update Ethernet VLAN Termination Top Layers
				ethernet___Update_VLAN_Termination_Top_Layers(refparam, name);
				return 0;
			}

			// if VLANID is set, then combine the two and set it in name
			char *vid = NULL;
			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "VLANID", &vid);
			snprintf(name, sizeof(name), "%s%s%s", reference.value, DM_STRLEN(vid) ? "." : "", DM_STRLEN(vid) ? vid : "");

			// there are two cases, one is a standalone 8021q section
			// and one is a bridge case, where Ethernet.Link points to a bridge
			// the second case means we want something like br-dev1.200
			// handle both cases separately
			// because in the second case we don't want to set anything in the network config
			// so only set in config if it is not a bridge
			if (!DM_STRSTR(reference.value, "br-")) {
				// Create and Set default config option
				if (((struct dm_data *)data)->config_section == NULL) {
					struct uci_section *s = NULL;
					char sec_name[32] = {0};
					char network_sec_name[64] = {0};
					char *tpid = NULL, *enabled = NULL;

					snprintf(sec_name, sizeof(sec_name), "vlan_ter_%s", instance);
					snprintf(network_sec_name, sizeof(network_sec_name), "network.vlan_ter_%s", instance);

					dmuci_add_section("network", "device", &s);
					dmuci_rename_section_by_section(s, sec_name);

					tpid = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->dmmap_section, "TPID", "33024");
					enabled = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->dmmap_section, "Enable", "1");
					dmuci_set_value_by_section(s, "enabled", enabled);

					if (DM_LSTRCMP(tpid, "34984") == 0) {
						dmuci_set_value_by_section(s, "type", "8021ad");
					} else {
						dmuci_set_value_by_section(s, "type", "8021q");
						/* there could be 0 in dmmap if this instance earlier pointing to bridge, so set to default */
						dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "TPID", "33024");
					}

					if (DM_STRLEN(vid)) {
						dmuci_set_value_by_section(s, "vid", vid);
					}

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

					// update dmmap
					dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "__section_name__", network_sec_name);
				}

				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "ifname", reference.value);
				dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "name", name);
			} else {
				/* there could be TPID in dmmap if this instance earlier pointing to non bridge object, so set to 0 for bridge */
				dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "TPID", "0");
				if (((struct dm_data *)data)->config_section != NULL) {
					dmuci_delete_by_section(((struct dm_data *)data)->config_section, NULL, NULL);
					dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "__section_name__", "");
				}
			}

			// Update ifname and name options
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "__ifname__", reference.value);
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "Name", name);

			// Update Ethernet VLAN Termination Top Layers
			ethernet___Update_VLAN_Termination_Top_Layers(refparam, name);
			break;
	}
	return 0;
}

static int get_EthernetVLANTermination_VLANID(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->dmmap_section, "VLANID", "1");
	return 0;
}

static int set_EthernetVLANTermination_VLANID(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *ifname = NULL;
	char name[32] = {0};

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{"1","4094"}}, 1))
				return FAULT_9007;
			return 0;
		case VALUESET:
			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "__ifname__", &ifname);
			snprintf(name, sizeof(name), "%s%s%s", DM_STRLEN(ifname) ? ifname : "", DM_STRLEN(ifname) ? "." : "", DM_STRLEN(ifname) ? value : "");

			if (((struct dm_data *)data)->config_section) {
				if (!DM_STRSTR(ifname, "br-")) {
					dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "vid", value);
					dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "name", name);

				}
			}

			// Update vid option
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "VLANID", value);
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "Name", name);

			// Update Ethernet VLAN Termination Top Layers
			ethernet___Update_VLAN_Termination_Top_Layers(refparam, name);
			return 0;
	}
	return 0;
}

static int get_EthernetVLANTermination_TPID(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	/* Internally TPID is set to 0 in case it's lower layer points to bridge, hence default is kept 33024 for
	 * instances which either points to ethernet interface or other vlan termination */
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->dmmap_section, "TPID", "33024");
	return 0;
}

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

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{NULL,"65535"}}, 1))
				return FAULT_9007;
			return 0;
		case VALUESET:
			dmuci_get_value_by_section_string(((struct dm_data *)data)->dmmap_section, "__ifname__", &ifname);
			if (DM_STRSTR(ifname, "br-")) {
				return 0;
			}

			if (DM_LSTRCMP(value, "33024") == 0 || DM_LSTRCMP(value, "34984") == 0)
				dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "TPID", value);

			if (((struct dm_data *)data)->config_section) {
				if (DM_LSTRCMP(value, "33024") == 0)
					dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "type", "8021q");
				else if (DM_LSTRCMP(value, "34984") == 0)
					dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "type", "8021ad");
			}
			return 0;
	}
	return 0;
}

static int get_EthernetVLANTerminationStats_BytesSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_sysfs(((struct dm_data *)data)->dmmap_section, "Name", "statistics/tx_bytes", value);
}

static int get_EthernetVLANTerminationStats_BytesReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_sysfs(((struct dm_data *)data)->dmmap_section, "Name", "statistics/rx_bytes", value);
}

static int get_EthernetVLANTerminationStats_PacketsSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_sysfs(((struct dm_data *)data)->dmmap_section, "Name", "statistics/tx_packets", value);
}

static int get_EthernetVLANTerminationStats_PacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_sysfs(((struct dm_data *)data)->dmmap_section, "Name", "statistics/rx_packets", value);
}

static int get_EthernetVLANTerminationStats_ErrorsSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_sysfs(((struct dm_data *)data)->dmmap_section, "Name", "statistics/tx_errors", value);
}

static int get_EthernetVLANTerminationStats_ErrorsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_sysfs(((struct dm_data *)data)->dmmap_section, "Name", "statistics/rx_errors", value);
}

static int get_EthernetVLANTerminationStats_DiscardPacketsSent(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_sysfs(((struct dm_data *)data)->dmmap_section, "Name", "statistics/tx_dropped", value);
}

static int get_EthernetVLANTerminationStats_DiscardPacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_sysfs(((struct dm_data *)data)->dmmap_section, "Name", "statistics/rx_dropped", value);
}

static int get_EthernetVLANTerminationStats_MulticastPacketsReceived(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return eth_iface_sysfs(((struct dm_data *)data)->dmmap_section, "Name", "statistics/multicast", value);
}

static int get_EthernetRMONStats_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_value_by_section_fallback_def(((struct dm_data *)data)->dmmap_section, "Enable", "0");
	return 0;
}

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

	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			break;
		case VALUESET:
			string_to_bool(value, &b);
			dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "Enable", b ? "1" : "0");
			break;
	}
	return 0;
}

static int get_EthernetRMONStats_Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct eth_rmon_stats *rmon = ((struct dm_data *)data)->additional_data;

	if (rmon->status == RMON_STATUS_DISABLED) {
		*value = dmstrdup("Disabled");
	} else if (rmon->status == RMON_STATUS_ENABLED) {
		*value = dmstrdup("Enabled");
	} else if (rmon->status == RMON_STATUS_ERR) {
		*value = dmstrdup("Error");
	} else {
		*value = dmstrdup("Error_Misconfigured");
	}
	return 0;
}

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

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

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

static int set_EthernetRMONStats_Interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *allowed_objects[] = {"Device.Ethernet.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;

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

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

static int set_EthernetRMONStats_VLANID(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action)	{
		case VALUECHECK:
			if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{"0","4094"}}, 1))
				return FAULT_9007;
			break;
		case VALUESET:
			break;
	}
	return 0;
}

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

static int set_EthernetRMONStats_AllQueues(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_EthernetRMONStats_Bytes(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct eth_rmon_stats *rmon = ((struct dm_data *)data)->additional_data;

	if (rmon) {
		dmasprintf(value, "%ju", (uintmax_t)rmon->rx.bytes);
	} else {
		*value = dmstrdup("0");
	}

	return 0;
}

static int get_EthernetRMONStats_Packets(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct eth_rmon_stats *rmon = ((struct dm_data *)data)->additional_data;

	if (rmon) {
		dmasprintf(value, "%ju", (uintmax_t)rmon->rx.packets);
	} else {
		*value = dmstrdup("0");
	}

	return 0;
}

static int get_EthernetRMONStats_BroadcastPackets(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct eth_rmon_stats *rmon = ((struct dm_data *)data)->additional_data;

	if (rmon) {
		dmasprintf(value, "%ju", (uintmax_t)rmon->rx.bcast_packets);
	} else {
		*value = dmstrdup("0");
	}

	return 0;
}

static int get_EthernetRMONStats_MulticastPackets(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct eth_rmon_stats *rmon = ((struct dm_data *)data)->additional_data;

	if (rmon) {
		dmasprintf(value, "%ju", (uintmax_t)rmon->rx.mcast_packets);
	} else {
		*value = dmstrdup("0");
	}

	return 0;
}

static int get_EthernetRMONStats_CRCErroredPackets(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct eth_rmon_stats *rmon = ((struct dm_data *)data)->additional_data;

	if (rmon) {
		dmasprintf(value, "%ju", (uintmax_t)rmon->rx.crc_err_packets);
	} else {
		*value = dmstrdup("0");
	}

	return 0;
}

static int get_EthernetRMONStats_UndersizePackets(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct eth_rmon_stats *rmon = ((struct dm_data *)data)->additional_data;

	if (rmon) {
		dmasprintf(value, "%ju", (uintmax_t)rmon->rx.under_sz_packets);
	} else {
		*value = dmstrdup("0");
	}

	return 0;
}

static int get_EthernetRMONStats_OversizePackets(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct eth_rmon_stats *rmon = ((struct dm_data *)data)->additional_data;

	if (rmon) {
		dmasprintf(value, "%ju", (uintmax_t)rmon->rx.over_sz_packets);
	} else {
		*value = dmstrdup("0");
	}

	return 0;
}

static int get_EthernetRMONStats_Packets64Bytes(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct eth_rmon_stats *rmon = ((struct dm_data *)data)->additional_data;

	if (rmon) {
		dmasprintf(value, "%ju", (uintmax_t)rmon->rx.packets_64bytes);
	} else {
		*value = dmstrdup("0");
	}

	return 0;
}

static int get_EthernetRMONStats_Packets65to127Bytes(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct eth_rmon_stats *rmon = ((struct dm_data *)data)->additional_data;

	if (rmon) {
		dmasprintf(value, "%ju", (uintmax_t)rmon->rx.packets_65to127bytes);
	} else {
		*value = dmstrdup("0");
	}

	return 0;
}

static int get_EthernetRMONStats_Packets128to255Bytes(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct eth_rmon_stats *rmon = ((struct dm_data *)data)->additional_data;

	if (rmon) {
		dmasprintf(value, "%ju", (uintmax_t)rmon->rx.packets_128to255bytes);
	} else {
		*value = dmstrdup("0");
	}

	return 0;
}

static int get_EthernetRMONStats_Packets256to511Bytes(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct eth_rmon_stats *rmon = ((struct dm_data *)data)->additional_data;

	if (rmon) {
		dmasprintf(value, "%ju", (uintmax_t)rmon->rx.packets_256to511bytes);
	} else {
		*value = dmstrdup("0");
	}

	return 0;
}

static int get_EthernetRMONStats_Packets512to1023Bytes(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct eth_rmon_stats *rmon = ((struct dm_data *)data)->additional_data;

	if (rmon) {
		dmasprintf(value, "%ju", (uintmax_t)rmon->rx.packets_512to1023bytes);
	} else {
		*value = dmstrdup("0");
	}

	return 0;
}

static int get_EthernetRMONStats_Packets1024to1518Bytes(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct eth_rmon_stats *rmon = ((struct dm_data *)data)->additional_data;

	if (rmon) {
		dmasprintf(value, "%ju", (uintmax_t)rmon->rx.packets_1024to1518bytes);
	} else {
		*value = dmstrdup("0");
	}

	return 0;
}

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

static int set_EthernetInterfaceReset(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	bool b;
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			break;
		case VALUESET:
			string_to_bool(value, &b);
			if (b) {
				char *ifname = NULL;

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

				if (ethmngr_clear_stats(ifname) != 0)
					return FAULT_9002;
			} else {
				return 0;
			}
			break;
	}
	return 0;
}

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

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

	ethmngr_clear_stats(ifname);

	return 0;
}

/*************************************************************
* Init & Clean Module
**************************************************************/

static int init_ethernet_module(void *data)
{
	struct dmctx bbf_ctx = {0};

	bbf_ctx_init(&bbf_ctx, NULL);

	struct uci_section *global = dmuci_get_section_bbfdm("Ethernet", "global");
	if (global == NULL) {
		dmuci_add_named_section_bbfdm("Ethernet", "global", "global", &global);
		if (global) {
			dmuci_set_value_by_section(global, "add_rmon", "1");
		}
	}

	dmmap_synchronizeEthernet_Interface_VLANTermination_MACVLAN(&bbf_ctx);
	dmmap_synchronizeEthernet_Link(&bbf_ctx);

	dmuci_commit_package_bbfdm("Ethernet");

	bbf_ctx_clean(&bbf_ctx);

	return 0;
}

/*
 * Called by the bbfdm framework whenever the "network" UCI package changes
 * (configured via apply_handler in bbfdm_service.json).
 *
 * Only runs when the change originated from an external source (e.g. another
 * package or a hotplug script), not when the data model itself committed the
 * change.  This mirrors the pattern used by netmngr's sync handlers and avoids
 * unnecessary re-syncs triggered by data-model-initiated UCI commits.
 *
 * Re-runs the same add-logic used at init time to pick up any new network.device
 * or network.interface sections (e.g. an 8021q VLAN device added alongside a
 * network.interface device= change from "ae_wan" to "ae_wan.100").  The called
 * functions are idempotent so no duplicate dmmap entries are created.
 */
static int sync_ethernet_module(void *data)
{
	struct bbfdm_context *bbfdm_ctx = (struct bbfdm_context *)data;

	if (bbfdm_ctx == NULL)
		return 0;

	/*
	 * Skip if the commit was initiated by the data model (proto is "cwmp",
	 * "usp", or "both").  We only need to resync when an external agent
	 * changed the network UCI without going through the data model.
	 */
	if (DM_STRCMP(bbfdm_ctx->uci_change_proto, "external") != 0)
		return 0;

	BBF_INFO("Syncing Ethernet dmmap with network UCI changes");

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

	dmmap_synchronizeEthernet_Interface_VLANTermination_MACVLAN(&bbf_ctx);
	dmmap_synchronizeEthernet_Link(&bbf_ctx);

	dmuci_commit_package_bbfdm("Ethernet");
	bbf_ctx_clean(&bbf_ctx);

	return 0;
}

/*
Sample uci config(/etc/config/maclimit) is:
config limit ‘l1’
	option enable '1'
	option ifname ‘eth3’
	option limit ‘12’
*/
#ifdef ETHMNGR_MACLIMIT_EXTN
static int get_EthernetInterface_maclimit(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *name = NULL;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "name", &name);
	if (DM_STRLEN(name)) {
		struct uci_section *s = NULL;

		uci_foreach_option_eq("maclimit", "limit", "ifname", name, s) {
			dmuci_get_value_by_section_string(s, "limit", value);
			break;
		}
	}

	if (DM_STRLEN(*value) == 0) {
		*value = dmstrdup("-1");
	}

	return 0;
}

static int set_EthernetInterface_maclimit(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;

			if (file_exists("/etc/config/maclimit") == false) {
				bbfdm_set_fault_message(ctx, "maclimit not supported on this device");
				return FAULT_9002;
			}
			return 0;
		case VALUESET:
			char *name = NULL;
			struct uci_section *s = NULL;

			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "name", &name);
			if (DM_STRLEN(name) == 0) {
				return FAULT_9002;
			}

			uci_foreach_option_eq("maclimit", "limit", "ifname", name, s) {
				if (DM_STRCMP(value, "-1") == 0) {
					dmuci_delete_by_section(s, NULL, NULL);
				} else {
					dmuci_set_value_by_section(s, "limit", value);
				}
				return 0;
			}

			dmuci_add_section("maclimit", "limit", &s);
			dmuci_set_value_by_section(s, "enable", "1");
			dmuci_set_value_by_section(s, "ifname", name);
			dmuci_set_value_by_section(s, "limit", value);
	}
	return 0;
}
#endif

/**********************************************************************************************************************************
*                                            OBJ & PARAM DEFINITION
***********************************************************************************************************************************/

DMOBJ tDeviceEthernetObj[] = {
{"Ethernet", &DMREAD, NULL, NULL, "file:/etc/config/network", NULL, NULL, NULL, tEthernetObj, tEthernetParams, NULL, BBFDM_BOTH, NULL},
{0}
};

/* *** Device.Ethernet. *** */
DMOBJ tEthernetObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"Interface", &DMREAD, NULL, NULL, NULL, generic_browse, NULL, NULL, tEthernetInterfaceObj, tEthernetInterfaceParams, NULL, BBFDM_BOTH, NULL},
{"Link", &DMWRITE, addObjEthernetLink, delObjEthernetLink, NULL, generic_browse, NULL, NULL, tEthernetLinkObj, tEthernetLinkParams, NULL, BBFDM_BOTH, NULL},
{"VLANTermination", &DMWRITE, addObjEthernetVLANTermination, delObjEthernetVLANTermination, NULL, generic_browse, NULL, NULL, tEthernetVLANTerminationObj, tEthernetVLANTerminationParams, NULL, BBFDM_BOTH, NULL},
{"RMONStats", &DMWRITE, addObjEthernetRmon, delObjEthernetRmon, NULL, browseEthernetRMONStatsInst, NULL, NULL, NULL, tEthernetRMONStatsParams, NULL, BBFDM_BOTH, NULL},
#ifdef ETHMNGR_MACVLAN_EXTENSION
{BBF_VENDOR_PREFIX"MACVLAN", &DMWRITE, addObjEthernetMACVLAN, delObjEthernetMACVLAN, NULL, generic_browse, NULL, NULL, tEthernetMACVLANObj, tEthernetMACVLANParams, NULL, BBFDM_BOTH, NULL},
#endif
{0}
};

DMLEAF tEthernetParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"FlowControlSupported", &DMREAD, DMT_BOOL, get_Ethernet_FlowControlSupported, NULL, BBFDM_BOTH},
{"InterfaceNumberOfEntries", &DMREAD, DMT_UNINT, get_Ethernet_InterfaceNumberOfEntries, NULL, BBFDM_BOTH},
{"LinkNumberOfEntries", &DMREAD, DMT_UNINT, get_Ethernet_LinkNumberOfEntries, NULL, BBFDM_BOTH},
{"VLANTerminationNumberOfEntries", &DMREAD, DMT_UNINT, get_Ethernet_VLANTerminationNumberOfEntries, NULL, BBFDM_BOTH},
{"RMONStatsNumberOfEntries", &DMREAD, DMT_UNINT, get_Ethernet_RMONStatsNumberOfEntries, NULL, BBFDM_BOTH},
#ifdef ETHMNGR_MACVLAN_EXTENSION
{BBF_VENDOR_PREFIX"MACVLANNumberOfEntries", &DMREAD, DMT_UNINT, get_Ethernet_macVlanNumberOfEntries, NULL, BBFDM_BOTH},
#endif
{0}
};

/* *** Device.Ethernet.Interface.{i}. *** */
DMOBJ tEthernetInterfaceObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"Stats", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, NULL, tEthernetInterfaceStatsParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tEthernetInterfaceParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_EthernetInterface_Enable, set_EthernetInterface_Enable, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_EthernetInterface_Status, NULL, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_EthernetInterface_Alias, set_EthernetInterface_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Name", &DMREAD, DMT_STRING, get_EthernetInterface_Name, NULL, BBFDM_BOTH, DM_FLAG_UNIQUE|DM_FLAG_LINKER},
{"LastChange", &DMREAD, DMT_UNINT, get_EthernetInterface_LastChange, NULL, BBFDM_BOTH},
{"LowerLayers", &DMWRITE, DMT_STRING, get_EthernetInterface_LowerLayers, set_EthernetInterface_LowerLayers, BBFDM_BOTH},
{"Upstream", &DMREAD, DMT_BOOL, get_EthernetInterface_Upstream, NULL, BBFDM_BOTH},
{"MACAddress", &DMREAD, DMT_STRING, get_EthernetInterface_MACAddress, NULL, BBFDM_BOTH},
{"MaxBitRate", &DMWRITE, DMT_INT, get_EthernetInterface_MaxBitRate, set_EthernetInterface_MaxBitRate, BBFDM_BOTH},
{"CurrentBitRate", &DMREAD, DMT_UNINT, get_EthernetInterface_CurrentBitRate, NULL, BBFDM_BOTH},
{"DuplexMode", &DMWRITE, DMT_STRING, get_EthernetInterface_DuplexMode, set_EthernetInterface_DuplexMode, BBFDM_BOTH},
{"EEECapability", &DMREAD, DMT_BOOL, get_EthernetInterface_EEECapability, NULL, BBFDM_BOTH},
{"EEEEnable", &DMWRITE, DMT_BOOL, get_EthernetInterface_EEEEnable, set_EthernetInterface_EEEEnable, BBFDM_BOTH},
#ifdef ETHMNGR_MACLIMIT_EXTN
{BBF_VENDOR_PREFIX"MACLimit", &DMWRITE, DMT_INT, get_EthernetInterface_maclimit, set_EthernetInterface_maclimit, BBFDM_BOTH},
#endif
{0}
};

/* *** Device.Ethernet.Interface.{i}.Stats. *** */
DMLEAF tEthernetInterfaceStatsParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Reset", &DMWRITE, DMT_BOOL, get_EthernetInterfaceReset, set_EthernetInterfaceReset, BBFDM_CWMP},
{"Reset()", &DMSYNC, DMT_COMMAND, NULL, operate_ethernet_ifstats_reset, BBFDM_USP},
{"BytesSent", &DMREAD, DMT_UNLONG, get_EthernetInterfaceStats_BytesSent, NULL, BBFDM_BOTH},
{"BytesReceived", &DMREAD, DMT_UNLONG, get_EthernetInterfaceStats_BytesReceived, NULL, BBFDM_BOTH},
{"PacketsSent", &DMREAD, DMT_UNLONG, get_EthernetInterfaceStats_PacketsSent, NULL, BBFDM_BOTH},
{"PacketsReceived", &DMREAD, DMT_UNLONG, get_EthernetInterfaceStats_PacketsReceived, NULL, BBFDM_BOTH},
{"ErrorsSent", &DMREAD, DMT_UNINT, get_EthernetInterfaceStats_ErrorsSent, NULL, BBFDM_BOTH},
{"ErrorsReceived", &DMREAD, DMT_UNINT, get_EthernetInterfaceStats_ErrorsReceived, NULL, BBFDM_BOTH},
{"DiscardPacketsSent", &DMREAD, DMT_UNINT, get_EthernetInterfaceStats_DiscardPacketsSent, NULL, BBFDM_BOTH},
{"DiscardPacketsReceived", &DMREAD, DMT_UNINT, get_EthernetInterfaceStats_DiscardPacketsReceived, NULL, BBFDM_BOTH},
{"MulticastPacketsReceived", &DMREAD, DMT_UNLONG, get_EthernetInterfaceStats_MulticastPacketsReceived, NULL, BBFDM_BOTH},
{"MulticastPacketsSent", &DMREAD, DMT_UNLONG, get_EthernetInterfaceStats_MulticastPacketsSent, NULL, BBFDM_BOTH},
{"UnicastPacketsSent", &DMREAD, DMT_UNLONG, get_EthernetInterfaceStats_UnicastPacketsSent, NULL, BBFDM_BOTH},
{"UnicastPacketsReceived", &DMREAD, DMT_UNLONG, get_EthernetInterfaceStats_UnicastPacketsReceived, NULL, BBFDM_BOTH},
{"BroadcastPacketsSent", &DMREAD, DMT_UNLONG, get_EthernetInterfaceStats_BroadcastPacketsSent, NULL, BBFDM_BOTH},
{"BroadcastPacketsReceived", &DMREAD, DMT_UNLONG, get_EthernetInterfaceStats_BroadcastPacketsReceived, NULL, BBFDM_BOTH},
{"UnknownProtoPacketsReceived", &DMREAD, DMT_UNINT, get_EthernetInterfaceStats_UnknownProtoPacketsReceived, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.Ethernet.Link.{i}. *** */
DMOBJ tEthernetLinkObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"Stats", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, NULL, tEthernetLinkStatsParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tEthernetLinkParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_EthernetLink_Enable, set_EthernetLink_Enable, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_EthernetLink_Status, NULL, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_EthernetLink_Alias, set_EthernetLink_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Name", &DMREAD, DMT_STRING, get_EthernetLink_Name, NULL, BBFDM_BOTH, DM_FLAG_UNIQUE|DM_FLAG_LINKER},
{"LastChange", &DMREAD, DMT_UNINT, get_EthernetLink_LastChange, NULL, BBFDM_BOTH},
{"LowerLayers", &DMWRITE, DMT_STRING, get_EthernetLink_LowerLayers, set_EthernetLink_LowerLayers, BBFDM_BOTH, DM_FLAG_REFERENCE},
{"MTU", &DMWRITE, DMT_UNINT, get_EthernetLink_MTU, set_EthernetLink_MTU, BBFDM_BOTH},
{"MACAddress", &DMREAD, DMT_STRING, get_EthernetLink_MACAddress, NULL, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"FlowControl", &DMWRITE, DMT_BOOL, get_EthernetLink_FlowControl, set_EthernetLink_FlowControl, BBFDM_BOTH},
{0}
};

/* *** Device.Ethernet.Link.{i}.Stats. *** */
DMLEAF tEthernetLinkStatsParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"BytesSent", &DMREAD, DMT_UNLONG, get_EthernetLinkStats_BytesSent, NULL, BBFDM_BOTH},
{"BytesReceived", &DMREAD, DMT_UNLONG, get_EthernetLinkStats_BytesReceived, NULL, BBFDM_BOTH},
{"PacketsSent", &DMREAD, DMT_UNLONG, get_EthernetLinkStats_PacketsSent, NULL, BBFDM_BOTH},
{"PacketsReceived", &DMREAD, DMT_UNLONG, get_EthernetLinkStats_PacketsReceived, NULL, BBFDM_BOTH},
{"ErrorsSent", &DMREAD, DMT_UNINT, get_EthernetLinkStats_ErrorsSent, NULL, BBFDM_BOTH},
{"ErrorsReceived", &DMREAD, DMT_UNINT, get_EthernetLinkStats_ErrorsReceived, NULL, BBFDM_BOTH},
{"DiscardPacketsSent", &DMREAD, DMT_UNINT, get_EthernetLinkStats_DiscardPacketsSent, NULL, BBFDM_BOTH},
{"DiscardPacketsReceived", &DMREAD, DMT_UNINT, get_EthernetLinkStats_DiscardPacketsReceived, NULL, BBFDM_BOTH},
{"MulticastPacketsReceived", &DMREAD, DMT_UNLONG, get_EthernetLinkStats_MulticastPacketsReceived, NULL, BBFDM_BOTH},
{"MulticastPacketsSent", &DMREAD, DMT_UNLONG, get_EthernetLinkStats_MulticastPacketsSent, NULL, BBFDM_BOTH},
{"UnicastPacketsSent", &DMREAD, DMT_UNLONG, get_EthernetLinkStats_UnicastPacketsSent, NULL, BBFDM_BOTH},
{"UnicastPacketsReceived", &DMREAD, DMT_UNLONG, get_EthernetLinkStats_UnicastPacketsReceived, NULL, BBFDM_BOTH},
{"BroadcastPacketsSent", &DMREAD, DMT_UNLONG, get_EthernetLinkStats_BroadcastPacketsSent, NULL, BBFDM_BOTH},
{"BroadcastPacketsReceived", &DMREAD, DMT_UNLONG, get_EthernetLinkStats_BroadcastPacketsReceived, NULL, BBFDM_BOTH},
{"UnknownProtoPacketsReceived", &DMREAD, DMT_UNINT, get_EthernetLinkStats_UnknownProtoPacketsReceived, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.Ethernet.VLANTermination.{i}. *** */
DMOBJ tEthernetVLANTerminationObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"Stats", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, NULL, tEthernetVLANTerminationStatsParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tEthernetVLANTerminationParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_EthernetVLANTermination_Enable, set_EthernetVLANTermination_Enable, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_EthernetVLANTermination_Status, NULL, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_EthernetVLANTermination_Alias, set_EthernetVLANTermination_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Name", &DMREAD, DMT_STRING, get_EthernetVLANTermination_Name, NULL, BBFDM_BOTH, DM_FLAG_UNIQUE|DM_FLAG_LINKER},
//{"LastChange", &DMREAD, DMT_UNINT, get_EthernetVLANTermination_LastChange, NULL, BBFDM_BOTH},
{"LowerLayers", &DMWRITE, DMT_STRING, get_EthernetVLANTermination_LowerLayers, set_EthernetVLANTermination_LowerLayers, BBFDM_BOTH, DM_FLAG_REFERENCE},
{"VLANID", &DMWRITE, DMT_UNINT, get_EthernetVLANTermination_VLANID, set_EthernetVLANTermination_VLANID, BBFDM_BOTH},
{"TPID", &DMWRITE, DMT_UNINT, get_EthernetVLANTermination_TPID, set_EthernetVLANTermination_TPID, BBFDM_BOTH},
{0}
};

/* *** Device.Ethernet.VLANTermination.{i}.Stats. *** */
DMLEAF tEthernetVLANTerminationStatsParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"BytesSent", &DMREAD, DMT_UNLONG, get_EthernetVLANTerminationStats_BytesSent, NULL, BBFDM_BOTH},
{"BytesReceived", &DMREAD, DMT_UNLONG, get_EthernetVLANTerminationStats_BytesReceived, NULL, BBFDM_BOTH},
{"PacketsSent", &DMREAD, DMT_UNLONG, get_EthernetVLANTerminationStats_PacketsSent, NULL, BBFDM_BOTH},
{"PacketsReceived", &DMREAD, DMT_UNLONG, get_EthernetVLANTerminationStats_PacketsReceived, NULL, BBFDM_BOTH},
{"ErrorsSent", &DMREAD, DMT_UNINT, get_EthernetVLANTerminationStats_ErrorsSent, NULL, BBFDM_BOTH},
{"ErrorsReceived", &DMREAD, DMT_UNINT, get_EthernetVLANTerminationStats_ErrorsReceived, NULL, BBFDM_BOTH},
//{"UnicastPacketsSent", &DMREAD, DMT_UNLONG, get_EthernetVLANTerminationStats_UnicastPacketsSent, NULL, BBFDM_BOTH},
//{"UnicastPacketsReceived", &DMREAD, DMT_UNLONG, get_EthernetVLANTerminationStats_UnicastPacketsReceived, NULL, BBFDM_BOTH},
{"DiscardPacketsSent", &DMREAD, DMT_UNINT, get_EthernetVLANTerminationStats_DiscardPacketsSent, NULL, BBFDM_BOTH},
{"DiscardPacketsReceived", &DMREAD, DMT_UNINT, get_EthernetVLANTerminationStats_DiscardPacketsReceived, NULL, BBFDM_BOTH},
//{"MulticastPacketsSent", &DMREAD, DMT_UNLONG, get_EthernetVLANTerminationStats_MulticastPacketsSent, NULL, BBFDM_BOTH},
{"MulticastPacketsReceived", &DMREAD, DMT_UNLONG, get_EthernetVLANTerminationStats_MulticastPacketsReceived, NULL, BBFDM_BOTH},
//{"BroadcastPacketsSent", &DMREAD, DMT_UNLONG, get_EthernetVLANTerminationStats_BroadcastPacketsSent, NULL, BBFDM_BOTH},
//{"BroadcastPacketsReceived", &DMREAD, DMT_UNLONG, get_EthernetVLANTerminationStats_BroadcastPacketsReceived, NULL, BBFDM_BOTH},
//{"UnknownProtoPacketsReceived", &DMREAD, DMT_UNINT, get_EthernetVLANTerminationStats_UnknownProtoPacketsReceived, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.Ethernet.RMONStats.{i}. *** */
DMLEAF tEthernetRMONStatsParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_EthernetRMONStats_Enable, set_EthernetRMONStats_Enable, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_EthernetRMONStats_Status, NULL, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_EthernetRMONStats_Alias, set_EthernetRMONStats_Alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Name", &DMREAD, DMT_STRING, get_EthernetRMONStats_Name, NULL, BBFDM_BOTH},
{"Interface", &DMWRITE, DMT_STRING, get_EthernetRMONStats_Interface, set_EthernetRMONStats_Interface, BBFDM_BOTH, DM_FLAG_UNIQUE|DM_FLAG_REFERENCE},
{"VLANID", &DMWRITE, DMT_UNINT, get_EthernetRMONStats_VLANID, set_EthernetRMONStats_VLANID, BBFDM_BOTH, DM_FLAG_UNIQUE},
//{"Queue", &DMWRITE, DMT_STRING, get_EthernetRMONStats_Queue, set_EthernetRMONStats_Queue, BBFDM_BOTH},
{"AllQueues", &DMWRITE, DMT_BOOL, get_EthernetRMONStats_AllQueues, set_EthernetRMONStats_AllQueues, BBFDM_BOTH},
//{"DropEvents", &DMREAD, DMT_UNINT, get_EthernetRMONStats_DropEvents, NULL, BBFDM_BOTH},
{"Bytes", &DMREAD, DMT_UNLONG, get_EthernetRMONStats_Bytes, NULL, BBFDM_BOTH},
{"Packets", &DMREAD, DMT_UNLONG, get_EthernetRMONStats_Packets, NULL, BBFDM_BOTH},
{"BroadcastPackets", &DMREAD, DMT_UNLONG, get_EthernetRMONStats_BroadcastPackets, NULL, BBFDM_BOTH},
{"MulticastPackets", &DMREAD, DMT_UNLONG, get_EthernetRMONStats_MulticastPackets, NULL, BBFDM_BOTH},
{"CRCErroredPackets", &DMREAD, DMT_UNINT, get_EthernetRMONStats_CRCErroredPackets, NULL, BBFDM_BOTH},
{"UndersizePackets", &DMREAD, DMT_UNINT, get_EthernetRMONStats_UndersizePackets, NULL, BBFDM_BOTH},
{"OversizePackets", &DMREAD, DMT_UNINT, get_EthernetRMONStats_OversizePackets, NULL, BBFDM_BOTH},
{"Packets64Bytes", &DMREAD, DMT_UNLONG, get_EthernetRMONStats_Packets64Bytes, NULL, BBFDM_BOTH},
{"Packets65to127Bytes", &DMREAD, DMT_UNLONG, get_EthernetRMONStats_Packets65to127Bytes, NULL, BBFDM_BOTH},
{"Packets128to255Bytes", &DMREAD, DMT_UNLONG, get_EthernetRMONStats_Packets128to255Bytes, NULL, BBFDM_BOTH},
{"Packets256to511Bytes", &DMREAD, DMT_UNLONG, get_EthernetRMONStats_Packets256to511Bytes, NULL, BBFDM_BOTH},
{"Packets512to1023Bytes", &DMREAD, DMT_UNLONG, get_EthernetRMONStats_Packets512to1023Bytes, NULL, BBFDM_BOTH},
{"Packets1024to1518Bytes", &DMREAD, DMT_UNLONG, get_EthernetRMONStats_Packets1024to1518Bytes, NULL, BBFDM_BOTH},
{0}
};

DM_MAP_OBJ tDynamicObj[] = {
/* parentobj, nextobject, parameter */
{"Device.", tDeviceEthernetObj, NULL, init_ethernet_module, NULL, sync_ethernet_module},
{0}
};
