blob: 9f716307b1cc84cebabf240c9538704550b13e28 [file] [log] [blame]
/*
<:copyright-BRCM:2016:DUAL/GPL:standard
Broadcom Proprietary and Confidential.(c) 2016 Broadcom
All Rights Reserved
Unless you and Broadcom execute a separate written software license
agreement governing use of this software, this software is licensed
to you under the terms of the GNU General Public License version 2
(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
with the following added to such license:
As a special exception, the copyright holders of this software give
you permission to link this software with independent modules, and
to copy and distribute the resulting executable under terms of your
choice, provided that you also meet, for each linked independent
module, the terms and conditions of the license of that module.
An independent module is a module which is not derived from this
software. The special exception does not apply to any modifications
of the software.
Not withstanding the above, under no circumstances may you combine
this software in any way with any other Broadcom software provided
under a license other than the GPL, without Broadcom's express prior
written consent.
:>
*/
#include <bcmos_system.h>
#include <bcmtr_interface.h>
#include <bcmtr_header.h>
#include <bcmtr_transport_cli.h>
#include <bcmolt_api.h>
#include <bcmtr_debug.h>
#ifdef ENABLE_CLI
#include <bcmcli.h>
#endif
#include "bcmolt_api_proxy.h"
#ifdef ENABLE_LOG
#include "bcm_dev_log.h"
#endif
/* Statistics */
typedef struct proxy_stats
{
unsigned long rx_packets;
unsigned long rx_bytes;
unsigned long tx_packets;
unsigned long tx_bytes;
unsigned long rx_errors;
unsigned long tx_errors;
unsigned long unpack_errors;
unsigned long pack_errors;
unsigned long msg_errors;
} proxy_stats;
typedef struct proxy_control_block
{
bcmos_task proxy_rx_task;
struct sockaddr_in client;
uint32_t proxy_port;
int device_id;
int client_socket;
bcmos_bool is_running;
char *on_ready_cmd;
/* the maximum amount of memory that could possibly by used by all variable-sized lists within a GET request */
#define DYNAMIC_LIST_BUFFER_SIZE (32 * 1024)
uint8_t dynamic_list_buffer[DYNAMIC_LIST_BUFFER_SIZE];
dev_log_id proxy_log;
proxy_stats stats;
} proxy_control_block;
/* Per device proxy control block */
static proxy_control_block proxy_data[BCMTR_MAX_OLTS];
static bcmos_bool is_first_proxy = BCMOS_TRUE;
#ifdef ENABLE_CLI
/*/proxy/stats command handler
*/
static bcmos_errno _proxy_stats_cmd(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
{
int device = (int)parm[0].value.number;
if (device >= BCMTR_MAX_OLTS)
return BCM_ERR_RANGE;
bcmcli_print(session, "%-16s: %lu\n", "rx_packets", proxy_data[device].stats.rx_packets);
bcmcli_print(session, "%-16s: %lu\n", "rx_bytes", proxy_data[device].stats.rx_bytes);
bcmcli_print(session, "%-16s: %lu\n", "tx_packets", proxy_data[device].stats.tx_packets);
bcmcli_print(session, "%-16s: %lu\n", "tx_bytes", proxy_data[device].stats.tx_bytes);
bcmcli_print(session, "%-16s: %lu\n", "rx_errors", proxy_data[device].stats.rx_errors);
bcmcli_print(session, "%-16s: %lu\n", "tx_errors", proxy_data[device].stats.tx_errors);
bcmcli_print(session, "%-16s: %lu\n", "unpack_errors", proxy_data[device].stats.unpack_errors);
bcmcli_print(session, "%-16s: %lu\n", "pack_errors", proxy_data[device].stats.pack_errors);
bcmcli_print(session, "%-16s: %lu\n", "msg_errors", proxy_data[device].stats.msg_errors);
memset(&proxy_data[device].stats, 0, sizeof(proxy_stats));
return BCM_ERR_OK;
}
#endif /* #ifdef ENABLE_CLI */
static bcmos_errno _proxy_msg_error(bcmolt_devid device, bcmolt_msg *proxy_msg)
{
proxy_control_block *proxy = &proxy_data[device];
++proxy->stats.msg_errors;
bcmolt_msg_err(proxy_msg, proxy->proxy_log, BCM_ERR_PARM, BCMOLT_ERR_FIELD_NONE, "Message is insane");
proxy_msg->dir = BCMOLT_MSG_DIR_RESPONSE;
return BCM_ERR_PARM;
}
static void _proxy_invoke(bcmolt_devid device, bcmolt_msg *proxy_msg)
{
proxy_control_block *proxy = &proxy_data[device];
bcmos_errno rc;
bcmolt_system_mode system_mode;
/* Check that the message targets an object that is compatible with our system mode */
bcmolt_system_mode_get(device, &system_mode);
if (system_mode != BCMOLT_SYSTEM_MODE__NUM_OF && !bcmolt_object_is_supported(system_mode, proxy_msg->obj_type))
{
++proxy->stats.msg_errors;
proxy_msg->dir = BCMOLT_MSG_DIR_RESPONSE;
bcmolt_msg_err(
proxy_msg,
proxy->proxy_log,
BCM_ERR_NOT_SUPPORTED,
BCMOLT_ERR_FIELD_NONE,
"Object type is not supported in this system mode");
return;
}
/* Invoke API */
switch (proxy_msg->group)
{
case BCMOLT_MGT_GROUP_CFG:
if ((proxy_msg->type & BCMOLT_MSG_TYPE_CLEAR) != 0)
{
rc = bcmolt_cfg_clear(device, (bcmolt_cfg *)proxy_msg);
}
else if ((proxy_msg->type & BCMOLT_MSG_TYPE_GET) != 0)
{
if ((proxy_msg->type & BCMOLT_MSG_TYPE_MULTI) != 0)
{
if (proxy_msg->msg_set == NULL)
{
rc = _proxy_msg_error(device, proxy_msg);
}
else
{
rc = bcmolt_cfg_get_multi(device,
(bcmolt_cfg *)proxy_msg,
proxy_msg->msg_set->filter_flags,
proxy_msg->msg_set);
}
}
else
{
rc = bcmolt_cfg_get(device, (bcmolt_cfg *)proxy_msg);
}
}
else if ((proxy_msg->type & BCMOLT_MSG_TYPE_SET) != 0)
{
rc = bcmolt_cfg_set(device, (bcmolt_cfg *)proxy_msg);
}
else
{
rc = _proxy_msg_error(device, proxy_msg);
}
break;
case BCMOLT_MGT_GROUP_STAT:
{
bcmolt_stat_flags flags;
flags = ((proxy_msg->type & BCMOLT_MSG_TYPE_CLEAR) != 0) ?
BCMOLT_STAT_FLAGS_CLEAR_ON_READ : 0;
rc = bcmolt_stat_get(device, (bcmolt_stat *)proxy_msg, flags);
}
break;
case BCMOLT_MGT_GROUP_OPER:
rc = bcmolt_oper_submit(device, (bcmolt_oper *)proxy_msg);
break;
case BCMOLT_MGT_GROUP_PROXY:
rc = bcmolt_proxy_send(device, (bcmolt_proxy *)proxy_msg);
break;
case BCMOLT_MGT_GROUP_STAT_CFG:
if ((proxy_msg->type & BCMOLT_MSG_TYPE_GET) != 0)
{
rc = bcmolt_stat_cfg_get(device, (bcmolt_stat_cfg *)proxy_msg);
}
else if ((proxy_msg->type & BCMOLT_MSG_TYPE_SET) != 0)
{
rc = bcmolt_stat_cfg_set(device, (bcmolt_stat_cfg *)proxy_msg);
}
else
{
rc = _proxy_msg_error(device, proxy_msg);
}
break;
case BCMOLT_MGT_GROUP_AUTO_CFG:
if ((proxy_msg->type & BCMOLT_MSG_TYPE_GET) != 0)
{
rc = bcmolt_auto_cfg_get(device, (bcmolt_auto_cfg *)proxy_msg);
}
else if ((proxy_msg->type & BCMOLT_MSG_TYPE_SET) != 0)
{
rc = bcmolt_auto_cfg_set(device, (bcmolt_auto_cfg *)proxy_msg);
}
else
{
rc = _proxy_msg_error(device, proxy_msg);
}
break;
default:
rc = _proxy_msg_error(device, proxy_msg);
}
proxy_msg->err = rc;
}
/* Pack and send message */
static void _proxy_send(bcmolt_devid device, bcmolt_msg *msg)
{
bcmtr_hdr hdr = {};
int32_t packed_length;
bcmolt_buf txb = {};
int len;
/* Allocate buffer and pack */
packed_length = bcmolt_msg_get_packed_length(msg);
if (packed_length <= 0)
{
++proxy_data[device].stats.pack_errors;
goto done;
}
packed_length += BCMTR_HDR_SIZE;
if (bcmolt_buf_alloc(&txb, packed_length, BCMOLT_BUF_ENDIAN_FIXED) != BCM_ERR_OK)
{
++proxy_data[device].stats.pack_errors;
goto done;
}
bcmtr_header_fill(msg, &hdr);
bcmtr_header_pack(&hdr, txb.start);
bcmolt_buf_skip(&txb, BCMTR_HDR_SIZE);
if (bcmolt_msg_pack(msg, &txb) != BCM_ERR_OK)
{
++proxy_data[device].stats.pack_errors;
goto done;
}
/* Send to client */
len = sendto(proxy_data[device].client_socket, txb.start, bcmolt_buf_get_used(&txb), 0,
(struct sockaddr *)&proxy_data[device].client, sizeof(proxy_data[device].client));
if (len <= 0)
{
++proxy_data[device].stats.tx_errors;
}
else
{
++proxy_data[device].stats.tx_packets;
proxy_data[device].stats.tx_bytes += len;
}
done:
bcmolt_buf_free(&txb);
return;
}
/* Task that waits for messages from Maple.
* Once message is received, it is forwarded to remote application
* via UDP socket.
*/
static int _proxy_rx_handler(long data)
{
bcmolt_devid device = (bcmolt_devid)data;
bcmos_task *self = bcmos_task_current();
proxy_control_block *proxy = &proxy_data[device];
struct sockaddr_in sender;
socklen_t sendsize = sizeof(sender);
uint8_t client_buffer[BCMTR_MAX_MTU_SIZE + 128];
bcmolt_msg *proxy_msg;
bcmtr_hdr tr_hdr;
bcmolt_buf rxb;
int len;
uint16_t corr_tag;
bcmos_errno rc;
while (!self->destroyed)
{
memset(&sender, 0, sizeof(sender));
len = recvfrom(proxy->client_socket, client_buffer, sizeof(client_buffer), 0,
(struct sockaddr *)&sender, &sendsize);
if (len < BCMTR_HDR_SIZE)
{
++proxy->stats.rx_errors;
bcmos_usleep(1000000);
continue;
}
++proxy->stats.rx_packets;
proxy->stats.rx_bytes += len;
if (proxy->client.sin_addr.s_addr != sender.sin_addr.s_addr ||
proxy->client.sin_port != sender.sin_port)
{
#ifdef ENABLE_LOG
int client_ip = ntohl(sender.sin_addr.s_addr);
int client_port = ntohs(sender.sin_port);
BCM_LOG(INFO, proxy->proxy_log, "bcm_api_proxy: device %i connected to %d.%d.%d.%d:%d\n",
(int)device,
(client_ip >> 24) & 0xff, (client_ip >> 16) & 0xff,
(client_ip >> 8) & 0xff, client_ip & 0xff, client_port);
#endif
proxy->client = sender;
}
/* Unpack received message */
bcmolt_buf_init(&rxb, len, client_buffer, BCMOLT_BUF_ENDIAN_FIXED);
bcmtr_header_unpack(client_buffer, &tr_hdr);
/* Skip registration messages for now. The proxy has already registered for everything */
if (tr_hdr.auto_proxy_reg || tr_hdr.auto_proxy_unreg)
continue;
bcmolt_buf_skip(&rxb, BCMTR_HDR_SIZE);
proxy_msg = NULL;
rc = bcmolt_msg_unpack(&rxb, &proxy_msg);
if (rc)
{
/* Unpack error. Nothing much we can do */
++proxy->stats.unpack_errors;
continue;
}
/* Store correlation tag for later */
proxy_msg->corr_tag = tr_hdr.corr_tag;
corr_tag = proxy_msg->corr_tag;
/* Point the message unpacker to local storage for dynamically-sized lists */
proxy_msg->list_buf = proxy->dynamic_list_buffer;
proxy_msg->list_buf_size = DYNAMIC_LIST_BUFFER_SIZE;
/* Invoke API */
_proxy_invoke(device, proxy_msg);
/* Pack and send back to the client */
proxy_msg->corr_tag = corr_tag;
_proxy_send(device, proxy_msg);
bcmolt_msg_free(proxy_msg);
}
self->destroyed = BCMOS_TRUE;
return 0;
}
/* Auto / proxy message handler */
void bcmolt_api_proxy_auto_rx_cb(bcmolt_devid device, bcmolt_msg *msg)
{
if (device >= BCMTR_MAX_OLTS)
return;
if (proxy_data[device].is_running)
{
if (proxy_data[device].on_ready_cmd &&
msg->obj_type == BCMOLT_OBJ_ID_DEVICE &&
msg->group == BCMOLT_MGT_GROUP_AUTO &&
msg->subgroup == BCMOLT_DEVICE_AUTO_ID_CONNECTION_COMPLETE)
{
char on_ready_cmd[512];
int rc;
snprintf(on_ready_cmd, sizeof(on_ready_cmd) - 1, "%s %d", proxy_data[device].on_ready_cmd, (int)device);
rc = system(on_ready_cmd);
#ifdef ENABLE_LOG
BCM_LOG(INFO, proxy_data[device].proxy_log, "Executed command %s. rc=%d\n", on_ready_cmd, rc);
#else
(void)rc;
#endif
}
_proxy_send(device, msg);
}
}
static bcmos_errno _proxy_start(bcmolt_devid device, uint32_t udp_port, char *on_ready)
{
bcmos_errno rc;
bcmos_task_parm proxy_rx_parm =
{
.name = "proxy_rx",
.handler = _proxy_rx_handler,
.priority = TASK_PRIORITY_TRANSPORT_PROXY,
.data = device
};
struct sockaddr_in sa = {};
proxy_data[device].proxy_port = udp_port;
proxy_data[device].device_id = device;
proxy_data[device].on_ready_cmd = on_ready;
/* Start listening on proxy port */
proxy_data[device].client_socket = socket(AF_INET, SOCK_DGRAM, 0);
if (proxy_data[device].client_socket < 0)
{
#ifdef ENABLE_LOG
BCM_LOG(ERROR, proxy_data[device].proxy_log, "Can't create UDP socket\n");
#endif
return BCM_ERR_INTERNAL;
}
/* Bind local */
sa.sin_family = AF_INET;
sa.sin_port = htons(proxy_data[device].proxy_port);
sa.sin_addr.s_addr = INADDR_ANY;
if (bind(proxy_data[device].client_socket, (struct sockaddr*)&sa, sizeof(sa) ) == -1)
{
perror("bind");
#ifdef ENABLE_LOG
BCM_LOG(ERROR, proxy_data[device].proxy_log, "Can't bind UDP socket to port %u\n", proxy_data[device].proxy_port);
#endif
close(proxy_data[device].client_socket);
return BCM_ERR_INTERNAL;
}
/* Create thread listening for incoming APIs */
rc = bcmos_task_create(&proxy_data[device].proxy_rx_task, &proxy_rx_parm);
BUG_ON(BCM_ERR_OK != rc);
proxy_data[device].is_running = BCMOS_TRUE;
#ifdef ENABLE_LOG
BCM_LOG(INFO, proxy_data[device].proxy_log, "BCM68620 API proxy for device %d is listening for requests on UDP port %u\n",
(int)device, proxy_data[device].proxy_port);
#endif
return BCM_ERR_OK;
}
bcmos_errno bcmolt_api_proxy_init(bcmcli_entry *root, bcmolt_devid device, uint32_t udp_port, char *on_ready)
{
#ifdef ENABLE_CLI
bcmcli_entry *dir;
#endif
if (device >= BCMTR_MAX_OLTS)
return BCM_ERR_PARM;
if (proxy_data[device].is_running)
return BCM_ERR_ALREADY;
#ifdef ENABLE_LOG
{
char log_id[32];
snprintf(log_id, sizeof(log_id) - 1, "proxy_%d", (int)device);
proxy_data[device].proxy_log = bcm_dev_log_id_register(log_id, DEV_LOG_LEVEL_INFO, DEV_LOG_ID_TYPE_BOTH);
}
#else
proxy_data[device].proxy_log = DEV_LOG_INVALID_ID;
#endif
if (is_first_proxy)
{
#ifdef ENABLE_CLI
dir = bcmcli_dir_add(root, "proxy", "API proxy", BCMCLI_ACCESS_GUEST, NULL);
if (!dir)
{
BCM_LOG(ERROR, proxy_data[device].proxy_log, "Can't create proxy directory\n");
BUG();
}
BCMCLI_MAKE_CMD(dir, "stat", "Proxy statistics", _proxy_stats_cmd,
BCMCLI_MAKE_PARM_RANGE("device", "Device index", BCMCLI_PARM_NUMBER, BCMCLI_PARM_FLAG_OPTIONAL,
0, BCMTR_MAX_OLTS-1));
#endif
is_first_proxy = BCMOS_FALSE;
}
return _proxy_start(device, udp_port, on_ready);
}
void bcmolt_api_proxy_stop(void)
{
int i;
for (i = 0; i < BCMTR_MAX_OLTS; i++)
{
if (proxy_data[i].is_running)
{
bcmos_task_destroy(&proxy_data[i].proxy_rx_task);
close(proxy_data[i].client_socket);
}
}
}