Skip to content
Snippets Groups Projects
qos_bbf.c 85.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * Copyright (C) 2021 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: Omar Kallel <omar.kallel@pivasoftware.com>
     *	Author: Rohit Topno <r.topno@gxgroup.eu>
     */
    
    #include "qos_bbf.h"
    
    #include "libbbfdm_api.h"
    
    #define PROTOCOLS_FILE "/etc/protocols"
    
    
    /*************************************************************
    * COMMON FUNCTIONS
    **************************************************************/
    
    static int is_numeric(const char *str)
    {
    	if (!str || !*str)
    		return 0;
    	for (; *str; str++) {
    		if (!isdigit((unsigned char)*str))
    			return 0;
    	}
    	return 1;
    }
    
    static int lookup_protocol_number(const char *protocol)
    {
    	FILE *file = fopen(PROTOCOLS_FILE, "r");
    	if (!file)
    		return -1;
    
    	char line[256];
    	while (fgets(line, sizeof(line), file)) {
    		if (line[0] == '#' || line[0] == '\n')
    			continue;
    
    		char name[32] = {0};
    		int number = 0;
    		if (sscanf(line, "%31s %d", name, &number) == 2) {
    			if (strcasecmp(name, protocol) == 0) {
    				fclose(file);
    				return number;
    			}
    		}
    	}
    
    	fclose(file);
    	return -1;
    }
    
    static void convert_protocol(char **value)
    {
    	if (!value || !*value || is_numeric(*value))
    		return;
    
    	int protocol_number = lookup_protocol_number(*value);
    	if (protocol_number >= 0) {
    		char new_value[12] = {0}; // Enough for an int as a string
    		snprintf(new_value, 12, "%d", protocol_number);
    		*value = dmstrdup(new_value);
    	}
    }
    
    
    static unsigned long qos_get_new_order(void)
    {
    	struct uci_section *s = NULL;
    	char *order = NULL;
    	unsigned long max = 0;
    
    	uci_foreach_sections("qos", "classify", s) {
    		dmuci_get_value_by_section_string(s, "order", &order);
    		unsigned long order_tol = DM_STRTOL(order);
    		if (order_tol > max)
    			max = order_tol;
    	}
    
    	return max + 1;
    }
    
    static bool qos_is_order_exists(const char *order)
    {
    	struct uci_section *s = NULL;
    	char *curr_order = NULL;
    
    	uci_foreach_sections("qos", "classify", s) {
    		dmuci_get_value_by_section_string(s, "order", &curr_order);
    		if (DM_STRCMP(curr_order, order) == 0)
    			return true;
    	}
    
    	return false;
    }
    
    static void qos_update_order(const char *order, bool is_del)
    {
    	struct uci_section *classify_s = NULL;
    	char *curr_order = NULL;
    	unsigned long order_tol = DM_STRTOL(order);
    
    	uci_foreach_sections("qos", "classify", classify_s) {
    		dmuci_get_value_by_section_string(classify_s, "order", &curr_order);
    		unsigned long curr_order_tol = DM_STRTOL(curr_order);
    		if (curr_order_tol >= order_tol) {
    			char new_order[16] = {0};
    
    			unsigned long order_ul = is_del ? curr_order_tol - 1 : curr_order_tol + 1;
    			DM_ULTOSTR(new_order, order_ul, sizeof(new_order));
    
    			dmuci_set_value_by_section(classify_s, "order", new_order);
    		}
    	}
    }
    
    /*************************************************************
     * ENTRY METHOD
    *************************************************************/
    static int browseQoSClassificationInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
    {
    	struct dm_data *curr_data = NULL;
    	LIST_HEAD(dup_list);
    	char *inst = NULL;
    
    	synchronize_specific_config_sections_with_dmmap("qos", "classify", "dmmap_qos", &dup_list);
    	list_for_each_entry(curr_data, &dup_list, list) {
    
    		inst = handle_instance(dmctx, parent_node, curr_data->dmmap_section, "classify_instance", "classify_alias");
    
    
    		if (DM_LINK_INST_OBJ(dmctx, parent_node, curr_data, inst) == DM_STOP)
    
    			break;
    	}
    	free_dmmap_config_dup_list(&dup_list);
    	return 0;
    }
    
    static int browseQoSPolicerInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
    {
    	struct dm_data *curr_data = NULL;
    	LIST_HEAD(dup_list);
    	char *inst = NULL;
    
    	synchronize_specific_config_sections_with_dmmap("qos", "policer", "dmmap_qos", &dup_list);
    	list_for_each_entry(curr_data, &dup_list, list) {
    
    		inst = handle_instance(dmctx, parent_node, curr_data->dmmap_section, "policer_instance", "policeralias");
    
    
    		if (DM_LINK_INST_OBJ(dmctx, parent_node, curr_data, inst) == DM_STOP)
    
    			break;
    	}
    	free_dmmap_config_dup_list(&dup_list);
    	return 0;
    }
    
    /*#Device.QoS.Queue.{i}.!UCI:qos/queue/dmmap_qos*/
    static int browseQoSQueueInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
    {
    	struct dm_data *curr_data = NULL;
    	LIST_HEAD(dup_list);
    	char *inst = NULL;
    
    	synchronize_specific_config_sections_with_dmmap("qos", "queue", "dmmap_qos", &dup_list);
    	list_for_each_entry(curr_data, &dup_list, list) {
    
    		dmuci_set_value_by_section(curr_data->dmmap_section, "queuealias", section_name(curr_data->config_section));
    
    		inst = handle_instance(dmctx, parent_node, curr_data->dmmap_section, "queueinstance", "queuealias");
    
    
    		if (DM_LINK_INST_OBJ(dmctx, parent_node, curr_data, inst) == DM_STOP)
    
    			break;
    	}
    	free_dmmap_config_dup_list(&dup_list);
    	return 0;
    }
    
    static int browseQoSQueueStatsInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
    {
    
    	struct dm_data curr_data = {0};
    
    	struct uci_section *s = NULL;
    	char *inst = NULL;
    
    	uci_path_foreach_sections(bbfdm, "dmmap_qstats", "queue_stats", s) {
    
    
    		curr_data.config_section = s;
    
    
    		inst = handle_instance(dmctx, parent_node, s, "q_instance", "q_alias");
    
    
    		if (DM_LINK_INST_OBJ(dmctx, parent_node, &curr_data, inst) == DM_STOP)
    
    			break;
    	}
    	return 0;
    }
    
    static int browseQoSShaperInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
    {
    	struct dm_data *curr_data = NULL;
    	LIST_HEAD(dup_list);
    	char *inst = NULL;
    
    	synchronize_specific_config_sections_with_dmmap("qos", "shaper", "dmmap_qos", &dup_list);
    	list_for_each_entry(curr_data, &dup_list, list) {
    
    		inst = handle_instance(dmctx, parent_node, curr_data->dmmap_section, "shaperinstance", "shaperalias");
    
    
    		if (DM_LINK_INST_OBJ(dmctx, parent_node, curr_data, inst) == DM_STOP)
    
    			break;
    	}
    	free_dmmap_config_dup_list(&dup_list);
    	return 0;
    }
    
    /*************************************************************
     * ADD & DEL OBJ
    *************************************************************/
    static int addObjQoSClassification(char *refparam, struct dmctx *ctx, void *data, char **instance)
    {
    	struct uci_section *classify_s = NULL, *dmmap_s = NULL;
    	char order_str[32] = {0};
    	char buf[32] = {0};
    
    	unsigned long order_ul = qos_get_new_order();
    	DM_ULTOSTR(order_str, order_ul, sizeof(order_str));
    
    	snprintf(buf, sizeof(buf), "classify_%s", *instance);
    
    	dmuci_add_section("qos", "classify", &classify_s);
    	dmuci_rename_section_by_section(classify_s, buf);
    	dmuci_set_value_by_section(classify_s, "enable", "0");
    	dmuci_set_value_by_section(classify_s, "order", order_str);
    
    	dmuci_add_section_bbfdm("dmmap_qos", "classify", &dmmap_s);
    	dmuci_set_value_by_section(dmmap_s, "section_name", buf);
    	dmuci_set_value_by_section(dmmap_s, "classify_instance", *instance);
    	return 0;
    }
    
    static int delObjQoSClassification(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
    {
    	char *curr_order = NULL;
    
    
    	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "order", &curr_order);
    	qos_update_order(curr_order, true);
    
    	dmuci_delete_by_section(((struct dm_data *)data)->config_section, NULL, NULL);
    	dmuci_delete_by_section(((struct dm_data *)data)->dmmap_section, NULL, NULL);
    
    
    	return 0;
    }
    
    static int addObjQoSPolicer(char *refparam, struct dmctx *ctx, void *data, char **instance)
    {
    	struct uci_section *dmmap = NULL, *s = NULL;
    
    	char s_name[16] = {0};
    
    	snprintf(s_name, sizeof(s_name), "policer_%s", *instance);
    
    
    	dmuci_add_section("qos", "policer", &s);
    
    	dmuci_rename_section_by_section(s, s_name);
    
    
    	dmuci_set_value_by_section(s, "enable", "0");
    	dmuci_set_value_by_section(s, "committed_rate", "0");
    	dmuci_set_value_by_section(s, "committed_burst_size", "0");
    	dmuci_set_value_by_section(s, "excess_burst_size", "0");
    	dmuci_set_value_by_section(s, "peak_rate", "0");
    	dmuci_set_value_by_section(s, "peak_burst_size", "0");
    	dmuci_set_value_by_section(s, "meter_type", "0");
    
    	dmuci_set_value_by_section(s, "name", s_name);
    
    
    	dmuci_add_section_bbfdm("dmmap_qos", "policer", &dmmap);
    
    	dmuci_set_value_by_section(dmmap, "section_name", s_name);
    
    	dmuci_set_value_by_section(dmmap, "policer_instance", *instance);
    	return 0;
    }
    
    static int delObjQoSPolicer(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
    {
    
    	struct uci_section *sec = NULL;
    
    	// store section name to update corresponding classification
    	// section if any
    	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "name", &p_name);
    
    	// Set the Classification.Policer to blank if corresponding
    	// Policer instance has been deleted
    	uci_foreach_option_eq("qos", "classify", "policer", p_name, sec) {
    		dmuci_set_value_by_section(sec, "policer", "");
    	}
    
    	dmuci_delete_by_section(((struct dm_data *)data)->config_section, NULL, NULL);
    	dmuci_delete_by_section(((struct dm_data *)data)->dmmap_section, NULL, NULL);
    
    
    	return 0;
    }
    
    static int addObjQoSQueue(char *refparam, struct dmctx *ctx, void *data, char **instance)
    {
    	struct uci_section  *dmmap = NULL, *s = NULL;
    
    	char s_name[16] = {0};
    
    	snprintf(s_name, sizeof(s_name), "queue_%s", *instance);
    
    
    	dmuci_add_section("qos", "queue", &s);
    
    	dmuci_rename_section_by_section(s, s_name);
    
    
    	dmuci_set_value_by_section(s, "enable", "false");
    	dmuci_set_value_by_section(s, "weight", "0");
    	dmuci_set_value_by_section(s, "precedence", "1");
    	dmuci_set_value_by_section(s, "burst_size", "0");
    	dmuci_set_value_by_section(s, "scheduling", "SP");
    	dmuci_set_value_by_section(s, "rate", "-1");
    
    	dmuci_add_section_bbfdm("dmmap_qos", "queue", &dmmap);
    
    	dmuci_set_value_by_section(dmmap, "section_name", s_name);
    
    	dmuci_set_value_by_section(dmmap, "queueinstance", *instance);
    	return 0;
    }
    
    static int delObjQoSQueue(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
    {
    
    	dmuci_delete_by_section(((struct dm_data *)data)->config_section, NULL, NULL);
    	dmuci_delete_by_section(((struct dm_data *)data)->dmmap_section, NULL, NULL);
    
    
    	return 0;
    }
    
    static int addObjQoSQueueStats(char *refparam, struct dmctx *ctx, void *data, char **instance)
    {
    	struct uci_section *qstats_sec = NULL;
    
    	dmuci_add_section_bbfdm("dmmap_qstats", "queue_stats", &qstats_sec);
    	dmuci_set_value_by_section(qstats_sec, "q_instance", *instance);
    	return 0;
    }
    
    static int delObjQoSQueueStats(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
    {
    
    	dmuci_delete_by_section(((struct dm_data *)data)->config_section, NULL, NULL);
    
    	return 0;
    }
    
    static int addObjQoSShaper(char *refparam, struct dmctx *ctx, void *data, char **instance)
    {
    	struct uci_section  *dmmap = NULL, *s = NULL;
    
    	char s_name[16] = {0};
    
    	snprintf(s_name, sizeof(s_name), "shaper_%s", *instance);
    
    
    	dmuci_add_section("qos", "shaper", &s);
    
    	dmuci_rename_section_by_section(s, s_name);
    
    
    	dmuci_set_value_by_section(s, "enable", "0");
    	dmuci_set_value_by_section(s, "burst_size", "0");
    	dmuci_set_value_by_section(s, "rate", "0");
    
    	dmuci_add_section_bbfdm("dmmap_qos", "shaper", &dmmap);
    
    	dmuci_set_value_by_section(dmmap, "section_name", s_name);
    
    	dmuci_set_value_by_section(dmmap, "shaperinstance", *instance);
    
    	return 0;
    }
    
    static int delObjQoSShaper(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
    {
    
    	dmuci_delete_by_section(((struct dm_data *)data)->config_section, NULL, NULL);
    	dmuci_delete_by_section(((struct dm_data *)data)->dmmap_section, NULL, NULL);
    
    
    	return 0;
    }
    
    /*************************************************************
    * COMMON Functions
    **************************************************************/
    static int get_QInterface(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	char *linker = NULL;
    
    	char buf[4096] = {0};
    
    
    	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "ifname", &linker);
    
    	if (DM_STRLEN(linker) == 0)
    		return 0;
    
    	bbfdm_get_references(ctx, MATCH_FIRST, "Device.IP.Interface.", "Name", linker, buf, sizeof(buf));
    	bbfdm_get_references(ctx, MATCH_FIRST, "Device.PPP.Interface.", "Name", linker, buf, sizeof(buf));
    	bbfdm_get_references(ctx, MATCH_FIRST, "Device.Ethernet.Interface.", "Name", linker, buf, sizeof(buf));
    	bbfdm_get_references(ctx, MATCH_FIRST, "Device.WiFi.Radio.", "Name", linker, buf, sizeof(buf));
    
    	*value = dmstrdup(buf);
    
    	return 0;
    }
    
    static int set_QInterface(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
    {
    	char *allowed_objects[] = {
    			"Device.IP.Interface.",
    			"Device.PPP.Interface.",
    			"Device.Ethernet.Interface.",
    			"Device.WiFi.Radio.",
    			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)->config_section, "ifname", reference.value);
    		break;
    	}
    	return 0;
    }
    
    /*************************************************************
     * GET & SET PARAM
    *************************************************************/
    /*#Device.QoS.ClassificationNumberOfEntries!UCI:qos/classify/*/
    static int get_QClassificationNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	int cnt = get_number_of_entries(ctx, data, instance, browseQoSClassificationInst);
    	dmasprintf(value, "%d", cnt);
    	return 0;
    }
    
    static int get_QPolicerNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	int cnt = get_number_of_entries(ctx, data, instance, browseQoSPolicerInst);
    	dmasprintf(value, "%d", cnt);
    	return 0;
    }
    
    static int get_QMaxQueueEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	*value = "100";
    	return 0;
    }
    
    /*#Device.QoS.QueueNumberOfEntries!UCI:qos/queue*/
    static int get_QQueueNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	int cnt = get_number_of_entries(ctx, data, instance, browseQoSQueueInst);
    	dmasprintf(value, "%d", cnt);
    	return 0;
    }
    
    static int get_QMaxClassificationEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	*value = "50";
    	return 0;
    }
    
    
    static int get_QMaxAppEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	*value = "0";
    	return 0;
    }
    
    static int get_QAppNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	*value = "0";
    	return 0;
    }
    
    static int get_QMaxFlowEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	*value = "0";
    	return 0;
    }
    
    static int get_QFlowNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	*value = "0";
    	return 0;
    }
    
    
    static int get_QMaxPolicerEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	*value = "50";
    	return 0;
    }
    
    static int get_QQueueStatsNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	int cnt = get_number_of_entries(ctx, data, instance, browseQoSQueueStatsInst);
    	dmasprintf(value, "%d", cnt);
    	return 0;
    }
    
    static int get_QShaperNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	int cnt = get_number_of_entries(ctx, data, instance, browseQoSShaperInst);
    	dmasprintf(value, "%d", cnt);
    	return 0;
    }
    
    
    static int get_QAvailableAppList(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	*value = "";
    	return 0;
    }
    
    
    static int get_QoSClassification_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, "enable", "1");
    	return 0;
    }
    
    static int set_QoSClassification_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)->config_section, "enable", (b) ? "1" : "0");
    		break;
    	}
    	return 0;
    }
    
    static int get_QoSClassification_Order(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "order", value);
    	return 0;
    }
    
    static int set_QoSClassification_Order(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
    {
    	char *curr_order = NULL;
    
    	switch (action)	{
    		case VALUECHECK:
    			if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{NULL,NULL}}, 1))
    				return FAULT_9007;
    			break;
    		case VALUESET:
    			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "order", &curr_order);
    			if (qos_is_order_exists(value))
    				qos_update_order(value, false);
    
    			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "order", value);
    			break;
    	}
    	return 0;
    }
    
    static int get_QoSClassification_DestMask(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	char *dest_ip = NULL;
    	char *mask = NULL;
    
    	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "dest_ip", &dest_ip);
    	// dest_ip can be of type, 'x.x.x.x' or 'x.x.x.x/y'
    	if (DM_STRLEN(dest_ip))
    		mask = strchr(dest_ip, '/');
    
    	*value = mask ? mask : "";
    	return 0;
    }
    
    static int set_QoSClassification_DestMask(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
    {
    	char dest_ip[32] = {0};
    	char *ip = NULL, *ip_addr = NULL, *mask = NULL;
    
    	switch (action)	{
    	case VALUECHECK:
    		if (bbfdm_validate_string(ctx, value, -1, 49 , NULL, IPPrefix))
    			return FAULT_9007;
    		break;
    	case VALUESET:
    		/* Destination IP address mask is represented as an IP routing prefix using CIDR notation. The IP address part MUST be an empty
    		 * string (and, if specified, MUST be ignored). Possible values are 'x.x.x.x/y' or '/y' and in case of 'x.x.x.x/y' ip address
    		 * part is ignored.
    		 */
    
    		ip_addr = strtok(value, "/");
    		if (ip_addr) {
    			mask = strtok(NULL, "/");
    			if (mask == NULL)
    				mask = ip_addr;
    		}
    
    		// get destination ip addr from qos classify uci section
    		dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "dest_ip", &ip);
    
    		ip_addr = strtok(ip, "/");
    
    		snprintf(dest_ip, sizeof(dest_ip), "%s/%s", ip_addr ? ip_addr : "0.0.0.0", mask);
    
    		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "dest_ip", dest_ip);
    		break;
    	}
    	return 0;
    }
    
    static int get_QoSClassification_SourceMask(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	char *src_ip = NULL;
    	char *mask = NULL;
    
    	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "src_ip", &src_ip);
    	// src_ip can be of type, 'x.x.x.x' or 'x.x.x.x/y'
    	if (DM_STRLEN(src_ip))
    		mask = strchr(src_ip, '/');
    
    	*value = mask ? mask : "";
    	return 0;
    }
    
    static int set_QoSClassification_SourceMask(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
    {
    	char dest_ip[32] = {0};
    	char *ip = NULL, *ip_addr = NULL, *mask = NULL;
    
    	switch (action)	{
    	case VALUECHECK:
    		if (bbfdm_validate_string(ctx, value, -1, 49 , NULL, IPPrefix))
    			return FAULT_9007;
    		break;
    	case VALUESET:
    		/* Source IP address mask is represented as an IP routing prefix using CIDR notation. The IP address part MUST be an empty
    		 * string (and, if specified, MUST be ignored). Possible values are 'x.x.x.x/y' or '/y' and in case of 'x.x.x.x/y' ip address
    		 * part is ignored.
    		 */
    
    		ip_addr = strtok(value, "/");
    		if (ip_addr) {
    			mask = strtok(NULL, "/");
    			if (mask == NULL)
    				mask = ip_addr;
    		}
    
    		// get source ip addr from qos classify uci section
    		dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "src_ip", &ip);
    
    		ip_addr = strtok(ip, "/");
    
    		snprintf(dest_ip, sizeof(dest_ip), "%s/%s", ip_addr ? ip_addr : "0.0.0.0", mask);
    
    		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "src_ip", dest_ip);
    		break;
    	}
    	return 0;
    }
    
    static int get_QoSClassification_Alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	return bbf_get_alias(ctx, ((struct dm_data *)data)->dmmap_section, "classify_alias", instance, value);
    }
    
    static int set_QoSClassification_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, "classify_alias", instance, value);
    }
    
    static int get_QoSClassification_Interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	return get_QInterface(refparam, ctx, data, instance, value);
    }
    
    static int set_QoSClassification_Interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
    {
    	return set_QInterface(refparam, ctx, data, instance, value, action);
    }
    
    static int get_QoSClassification_AllInterfaces(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, "all_interfaces", "0");
    	return 0;
    }
    
    static int set_QoSClassification_AllInterfaces(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, "all_interfaces", (b) ? "1" : "0");
    		break;
    	}
    	return 0;
    }
    
    static int get_QoSClassification_DestIP(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	char *dest_ip = NULL;
    
    	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "dest_ip", &dest_ip);
    	if (DM_STRLEN(dest_ip)) {
    		char *mask = strchr(dest_ip, '/');
    		if (mask)
    			*mask = 0;
    	}
    
    	*value = dest_ip;
    	return 0;
    }
    
    static int set_QoSClassification_DestIP(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
    {
    	char *dest_mask = NULL, *ip = NULL;
    
    	switch (action)	{
    	case VALUECHECK:
    		if (bbfdm_validate_string(ctx, value, -1, 45 , NULL, IPAddress))
    			return FAULT_9007;
    		break;
    	case VALUESET:
    		/* First, get the existing destination ip addr from qos classify uci section, if present.
    		 * IP addr can be of type, 'x.x.x.x' or 'x.x.x.x/y'
    		 * If IP addr is of type 'x.x.x.x/y' then get its mask(y) and use with the new dest IP.
    		 */
    		dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "dest_ip", &ip);
    
    		if (ip[0] != '\0') {
    			strtok(ip, "/");
    			dest_mask = strtok(NULL, "/");
    		}
    
    		if (dest_mask != NULL) {
    			char dest_ip[64] = {0};
    
    			snprintf(dest_ip, sizeof(dest_ip), "%s/%s", value, dest_mask);
    			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "dest_ip", dest_ip);
    		} else {
    			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "dest_ip", value);
    		}
    		break;
    	}
    	return 0;
    }
    
    static int get_QoSClassification_SourceIP(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	char *src_ip = NULL;
    
    	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "src_ip", &src_ip);
    	if (DM_STRLEN(src_ip)) {
    		char *mask = strchr(src_ip, '/');
    		if (mask)
    			*mask = 0;
    	}
    
    	*value = src_ip;
    	return 0;
    }
    
    static int set_QoSClassification_SourceIP(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
    {
    	char *src_mask = NULL, *mask = NULL;
    
    	switch (action)	{
    	case VALUECHECK:
    		if (bbfdm_validate_string(ctx, value, -1, 45 , NULL, IPAddress))
    			return FAULT_9007;
    		break;
    	case VALUESET:
    		/* First, get the existing source ip addr from qos classify uci section, if present.
    		 * IP addr can be of type, 'x.x.x.x' or 'x.x.x.x/y'
    		 * If IP addr is of type 'x.x.x.x/y' then get its mask(y) and use with the new source IP.
    		 */
    		dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "src_ip", &src_mask);
    
    		if (src_mask[0] != '\0') {
    			strtok(src_mask, "/");
    			mask = strtok(NULL, "/");
    		}
    
    		if (mask != NULL) {
    			char src_ip[64] = {0};
    
    			snprintf(src_ip, sizeof(src_ip), "%s/%s", value, mask);
    			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "src_ip", src_ip);
    		} else {
    			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "src_ip", value);
    		}
    		break;
    	}
    	return 0;
    }
    
    static int get_QoSClassification_Protocol(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, "proto", "-1");
    
    	return 0;
    }
    
    static int set_QoSClassification_Protocol(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
    {
    	switch (action)	{
    	case VALUECHECK:
    		if (bbfdm_validate_int(ctx, value, RANGE_ARGS{{"-1","255"}}, 1))
    			return FAULT_9007;
    		break;
    	case VALUESET:
    		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "proto", value);
    		break;
    	}
    	return 0;
    }
    
    
    static int get_QoSClassification_DestPort(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, "dest_port", "-1");
    	return 0;
    }
    
    static int set_QoSClassification_DestPort(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","65535"}}, 1))
    			return FAULT_9007;
    		break;
    	case VALUESET:
    		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "dest_port", value);
    		break;
    	}
    	return 0;
    }
    
    static int get_QoSClassification_DestPortRangeMax(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, "dest_port_range", "-1");
    	return 0;
    }
    
    static int set_QoSClassification_DestPortRangeMax(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","65535"}}, 1))
    				return FAULT_9007;
    			break;
    		case VALUESET:
    			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "dest_port_range", value);
    			break;
    	}
    	return 0;
    }
    
    static int get_QoSClassification_SourcePort(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, "src_port", "-1");
    	return 0;
    }
    
    static int set_QoSClassification_SourcePort(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","65535"}}, 1))
    				return FAULT_9007;
    			break;
    		case VALUESET:
    			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "src_port", value);
    			break;
    	}
    	return 0;
    }
    
    static int get_QoSClassification_SourcePortRangeMax(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, "src_port_range", "-1");
    	return 0;
    }
    
    static int set_QoSClassification_SourcePortRangeMax(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","65535"}}, 1))
    			return FAULT_9007;
    		break;
    	case VALUESET:
    		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "src_port_range", value);
    		break;
    	}
    	return 0;
    }
    
    static int get_QoSClassification_SourceMACAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "src_mac", value);
    	return 0;
    }
    
    static int set_QoSClassification_SourceMACAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
    {
    	switch (action)	{
    	case VALUECHECK:
    		if (bbfdm_validate_string(ctx, value, -1, 17, NULL, MACAddress))
    			return FAULT_9007;
    		break;
    	case VALUESET:
    		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "src_mac", value);
    		break;
    	}
    	return 0;
    }
    
    static int get_QoSClassification_DestMACAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "dst_mac", value);
    	return 0;
    }
    
    static int set_QoSClassification_DestMACAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
    {
    	switch (action)	{
    	case VALUECHECK:
    		if (bbfdm_validate_string(ctx, value, -1, 17, NULL, MACAddress))
    			return FAULT_9007;
    		break;
    	case VALUESET:
    		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "dst_mac", value);
    		break;
    	}
    	return 0;
    }
    
    static int get_QoSClassification_Ethertype(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, "ethertype", "-1");
    	return 0;
    }
    
    static int set_QoSClassification_Ethertype(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;
    		break;
    	case VALUESET:
    		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "ethertype", value);
    		break;
    	}
    	return 0;
    }
    
    static int get_QoSClassification_SourceVendorClassID(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "src_vendor_class_id", value);
    	return 0;
    }
    
    static int set_QoSClassification_SourceVendorClassID(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
    {
    	switch (action)	{
    	case VALUECHECK:
    		if (bbfdm_validate_string(ctx, value, -1, 255, NULL, NULL))
    			return FAULT_9007;
    		break;
    	case VALUESET:
    		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "src_vendor_class_id", value);
    		break;
    	}
    	return 0;
    }
    
    static int get_QoSClassification_DestVendorClassID(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
    {
    	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "dst_vendor_class_id", value);
    	return 0;
    }
    
    static int set_QoSClassification_DestVendorClassID(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
    {
    	switch (action)	{
    	case VALUECHECK:
    		if (bbfdm_validate_string(ctx, value, -1, 255, NULL, NULL))
    			return FAULT_9007;
    		break;
    	case VALUESET:
    		dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "dst_vendor_class_id", value);