-
Shubham Sharma authoredShubham Sharma authored
qosmngr.c 12.37 KiB
/* SPDX-License-Identifier: GPL-2.0 */
/*
* qosmngr.c - main qosmngr's code
*
* Copyright (C) 2020 iopsys Software Solutions AB. All rights reserved.
*
* Author: Oskar Viljasaar <oskar.viljasaar@iopsys.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <libubox/blobmsg_json.h>
#include <libubus.h>
#include <uci.h>
/* Needed to get the IFNAMSIZ define */
#include <net/if.h>
#include "qosmngr.h"
/* Used as an internal value to query all the queues */
#define QOS_QUEUE_ANY -1
/* Used for getting interface indexes for global q_stat array */
#define MIN_INDEX 48
#define MAX_INDEX 57
/* Used for fetching keys from ubus json reqest */
#define SEPERATOR 44
#define QUOTE 34
/* Used to validate requested parameters */
#define PARAM1 "ifname"
#define PARAM2 "qid"
static int init_flag = 1;
static struct qos_stats **q_stat = {0};
/**
* get_no_queues function for getting number of queues on device by calling uci qos config
* @param ifname input parameter pointer to char for which number of queues needed
* retrun integer value 0 on success and -1 on failure
*/
static int get_no_queues(const char *ifname)
{
int queues = 0;
struct uci_package *uci_pkg = NULL;
struct uci_element *uci_elmnt = NULL;
struct uci_context *uci_ctx = uci_alloc_context();
uci_load(uci_ctx, "qos", &uci_pkg);
if (!uci_pkg) {
syslog(LOG_ERR, "Failed to load configuration\n");
queues = -1;
goto done;
}
uci_foreach_element(&uci_pkg->sections, uci_elmnt) {
struct uci_section *uci_sec = uci_to_section(uci_elmnt);
if (uci_sec && !strcmp(uci_sec->type, "queue")) {
struct uci_element *e = NULL;
uci_foreach_element(&uci_sec->options, e) {
struct uci_option *uci_opn = uci_to_option(e);
if (uci_opn && !strcmp(uci_opn->v.string, ifname))
queues++;
}
}
}
uci_unload(uci_ctx, uci_pkg);
done:
uci_free_context(uci_ctx);
return queues;
}
/**
* get_interface_index function for getting interface index i.e., eth0 having index 0
* @param ifname input parameter pointer to char for which index needs to be fetched
* retrun integer value 0 on success and -1 on failure
*/
static int get_interface_index(const char *ifname)
{
int i;
for (i = 0; i < strlen(ifname); i++) {
if ((ifname[i] >= MIN_INDEX) && (ifname[i] <= MAX_INDEX)) {
return atoi(ifname + i);
}
}
return -1;
}
/**
* get_no_ports function for getting number of ports on device by calling uci port config
* @param none
* retrun integer value 0 on success and -1 on failure
*/
static int get_no_ports()
{
int ports = 0;
struct uci_package *uci_pkg = NULL;
struct uci_element *uci_elmnt = NULL;
struct uci_context *uci_ctx = uci_alloc_context();
uci_load(uci_ctx, "ports", &uci_pkg);
if (!uci_pkg) {
syslog(LOG_ERR, "Failed to load configuration\n");
ports = -1;
goto done;
}
uci_foreach_element(&uci_pkg->sections, uci_elmnt) {
struct uci_section *uci_sec = uci_to_section(uci_elmnt);
if (uci_sec)
ports++;
}
done:
uci_unload(uci_ctx, uci_pkg);
uci_free_context(uci_ctx);
return ports;
}
/**
* init_qstat function for initializing global q_stat structure for sustainable stats
* @param none
* retrun integer value 0 on success and -1 on failure
*/
static int init_qstat()
{
int ret = 0;
int index = 0;
syslog(LOG_ERR, "Initializing global stat structure.\n");
int ports = get_no_ports();
q_stat = (struct qos_stats **)calloc(ports, sizeof(struct qos_stats *));
if (q_stat == NULL) {
syslog(LOG_ERR, "Initialization failed during memory allocation.\n");
ret = -1;
return ret;
}
struct uci_package *uci_pkg = NULL;
struct uci_element *uci_elmnt = NULL;
struct uci_context *uci_ctx = uci_alloc_context();
uci_load(uci_ctx, "ports", &uci_pkg);
if (!uci_pkg) {
syslog(LOG_ERR, "Failed to load configuration\n");
ret = -1;
goto done;
}
uci_foreach_element(&uci_pkg->sections, uci_elmnt) {
struct uci_section *uci_sec = uci_to_section(uci_elmnt);
if (uci_sec) {
struct uci_option *uci_opn = uci_lookup_option(uci_ctx, uci_sec, "ifname");
index = get_interface_index(uci_opn->v.string);
int queues = get_no_queues(uci_opn->v.string);
q_stat[index] = (struct qos_stats *)calloc(queues, sizeof(struct qos_stats));
if (q_stat[index] == NULL) {
syslog(LOG_ERR, "Initialization failed during memory allocation.\n");
ret = -1;
uci_unload(uci_ctx, uci_pkg);
goto done;
}
}
}
uci_unload(uci_ctx, uci_pkg);
done:
uci_free_context(uci_ctx);
return ret;
}
/**
* prapare_stats_blob function for getting stats by calling libqos function prepare_stats_blob
* @param b output parameter pointer to blob_buf
* @param stats output parameter pointer to qos_stats actually queue stats container
* @param dd output parameter pointer to void used for blob buffer array elements
* @param ifname input parameter pointer to char for which to get stats
* @param qid input parameter integer queue identifier for which to get stats
* retrun integer value 0 on success and -1 on failure
*/
static int prepare_stats_blob(struct blob_buf *b, struct qos_stats *stats, void *dd,
char *ifname, int qid)
{
int index;
int ret = 0;
// Initialize global q_stat global struct
if (init_flag) {
ret = init_qstat();
if (ret) {
syslog(LOG_ERR, "Failed to initialize gobal q_stat.\n");
}
syslog(LOG_ERR, "Initialized gobal q_stat successfully.\n");
init_flag = 0;
}
ret = qos_get_stats(ifname, qid, stats);
if (ret != 0) {
syslog(LOG_ERR, "blob_get_status: ret %d\n", ret);
return ret;
}
index = get_interface_index(ifname);
q_stat[index][qid].tx_packets += stats->tx_packets;
q_stat[index][qid].tx_bytes += stats->tx_bytes;
q_stat[index][qid].tx_dropped_packets += stats->tx_dropped_packets;
q_stat[index][qid].tx_dropped_bytes += stats->tx_dropped_bytes;
dd = blobmsg_open_table(b, "");
blobmsg_add_string(b, "iface", ifname);
blobmsg_add_u32(b, "qid", qid);
blobmsg_add_u32(b, "tx_packets", q_stat[index][qid].tx_packets);
blobmsg_add_u32(b, "tx_bytes", q_stat[index][qid].tx_bytes);
blobmsg_add_u32(b, "tx_dropped_packets", q_stat[index][qid].tx_dropped_packets);
blobmsg_add_u32(b, "tx_dropped_bytes", q_stat[index][qid].tx_dropped_bytes);
blobmsg_close_table(b, dd);
return ret;
}
/**
* get_stats_by_ifname function for getting specific interface stats
* @param b output parameter pointer to blob_buf
* @param stats output parameter pointer to qos_stats actually queue stats container
* @param dd output parameter pointer to void used for blob buffer array elements
* @param ifname input parameter pointer to char for which to get stats
* @param qid input parameter integer queue identifier for which to get stats
* retrun integer value 0 on success and -1 on failure
*/
static int get_stats_by_ifname(struct blob_buf *b, struct qos_stats *stats, void *dd,
char *ifname, int qid)
{
int queues = 0;
int i, ret = 0;
if (qid >= 0) {
ret = prepare_stats_blob(b, stats, dd, ifname, qid);
} else {
queues = get_no_queues(ifname);
for (i = 0; i < queues; i++) {
ret = prepare_stats_blob(b, stats, dd, ifname, i);
if (ret != 0)
return ret;
}
}
return ret;
}
/**
* get_stats_for_all_intf function for getting all interface stats
* @param b output parameter pointer to blob_buf
* @param stats output parameter pointer to qos_stats actually queue stats container
* @param dd output parameter pointer to void used for blob buffer array elements
* retrun integer value 0 on success and -1 on failure
*/
static int get_stats_for_all_intf(struct blob_buf *b, struct qos_stats *stats, void *dd)
{
int ret = 0;
char ifname[IFNAMSIZ] = {0};
struct uci_package *uci_pkg = NULL;
struct uci_element *uci_elmnt = NULL;
struct uci_context *uci_ctx = uci_alloc_context();
uci_load(uci_ctx, "ports", &uci_pkg);
if (!uci_pkg) {
syslog(LOG_ERR, "Failed to load configuration\n");
ret = -1;
return ret;
}
uci_foreach_element(&uci_pkg->sections, uci_elmnt) {
struct uci_section *uci_sec = uci_to_section(uci_elmnt);
if (uci_sec) {
struct uci_option *uci_opn = uci_lookup_option(uci_ctx, uci_sec, "ifname");
strncpy(ifname, uci_opn->v.string, sizeof(ifname) - 1);
ret = get_stats_by_ifname(b, stats, dd, ifname, QOS_QUEUE_ANY);
if (ret != 0) {
syslog(LOG_ERR, "get_stats_by_ifname : ret %d\n", ret);
return ret;
}
}
}
return ret;
}
/**
* validate_keys function to validate requested json keys
* @param rea_json parameter pointer to char string containing json request
* retrun integer value 0 on success and -1 on failure
*/
static int validate_keys(char *req_json)
{
int i;
int ret = 0;
int len = strlen(req_json);
for (i = 0; i < len; i++) {
char key[IFNAMSIZ] = {0};
if (req_json[i] == QUOTE) {
int j = 0;
i++;
while (req_json[i] != QUOTE) {
key[j] = req_json[i];
j++;
i++;
}
i++;
while ((req_json[i] != SEPERATOR) && (i < len))
i++;
if (!(!strncmp(key, PARAM1, strlen(PARAM1)) ||
!strncmp(key, PARAM2, strlen(PARAM2)))) {
syslog(LOG_ERR, "ERROR :: unknown parameter : %s\n", key);
return -1;
}
}
}
return ret;
}
/**
* validate_request function to validate requested blob message
* @param msg parameter pointer to blob_attr containing blob request
* retrun integer value 0 on success and -1 on failure
*/
static int validate_request(struct blob_attr *msg)
{
int ret = 0;
char *json_blob = NULL;
if (msg) {
json_blob = blobmsg_format_json(msg, true);
ret = validate_keys(json_blob);
}
return ret;
}
/**
* qosmngr_get_stats function callback on ubus method queue_stats
* @param ctx input parameter pointer to ubus context
* @param obj input parameter pointer to ubus object in out case qos
* @param req input parameter pointer to ubus requested data
* @param method input parameter pointer to char method i.e., queue_stats
* @param msg input parameter pointer containing qid and ifname
* retrun integer value 0 on success and -1 on failure
*/
int qosmngr_get_stats(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
int ret = 0;
int qid = QOS_QUEUE_ANY;
char ifname[IFNAMSIZ] = {0};
struct blob_attr *tb[NUM_QOS_POLICY];
struct blob_buf b = {0};
struct qos_stats stats = {0};
/* Validate requested parameters, i.e., ifname and qid */
ret = validate_request(msg);
if (ret) {
syslog(LOG_ERR, "validate_request failed : ret %d\n", ret);
return UBUS_STATUS_INVALID_ARGUMENT;
}
/* These are for the blobbuf array elements */
void *d = NULL, *dd = NULL;
blobmsg_parse(get_status_policy, QOS_POLICY_MAX, tb, blob_data(msg),
blob_len(msg));
if (tb[QOS_POLICY_IFNAME])
strncpy(ifname, blobmsg_data(tb[QOS_POLICY_IFNAME]), sizeof(ifname)-1);
/* Parse optional arguments */
if (tb[QOS_POLICY_QID])
qid = blobmsg_get_u32(tb[QOS_POLICY_QID]);
/* Can't have a queue id specified without an interface */
if (tb[QOS_POLICY_QID] && !tb[QOS_POLICY_IFNAME])
return UBUS_STATUS_INVALID_ARGUMENT;
blob_buf_init(&b, 0);
d = blobmsg_open_array(&b, "queues");
if (tb[QOS_POLICY_IFNAME]) {
ret = get_stats_by_ifname(&b, &stats, &dd, ifname, qid);
if (ret != 0) {
syslog(LOG_ERR, "get_stats_by_ifname : ret %d\n", ret);
goto fail_get_status;
}
} else {
ret = get_stats_for_all_intf(&b, &stats, &dd);
if (ret != 0) {
syslog(LOG_ERR, "get_stats_for_all_intf : ret %d\n", ret);
goto fail_get_status;
}
}
blobmsg_close_array(&b, d);
ubus_send_reply(ctx, req, b.head);
fail_get_status:
blob_buf_free(&b);
return ret;
}