mirror of
https://github.com/Telecominfraproject/ols-ucentral-client.git
synced 2025-10-29 09:02:28 +00:00
sizeof(*port_node->name) == 1 sizeof(port_node->name) == PORT_MAX_NAME_LEN # 32 Signed-off-by: Serhiy Boiko <serhiy.boiko@plvision.eu> Change-Id: I3bd09eaf00bb55045a935de7795509f5582b0878
4913 lines
122 KiB
C
4913 lines
122 KiB
C
#define _GNU_SOURCE /* asprintf */
|
|
#include <stdio.h>
|
|
#include <pthread.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <stdarg.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <arpa/inet.h>
|
|
#include <inttypes.h>
|
|
#include <sys/sysinfo.h>
|
|
#include <sys/mman.h>
|
|
|
|
#include <sys/wait.h>
|
|
#include <sys/types.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <poll.h>
|
|
#include <signal.h>
|
|
|
|
#include <curl/curl.h>
|
|
|
|
#include <ucentral-platform.h>
|
|
#define UC_LOG_COMPONENT UC_LOG_COMPONENT_PLAT
|
|
#include <ucentral-log.h>
|
|
#include <base64.h>
|
|
|
|
#include "gnma/gnma_common.h"
|
|
#include "plat-revision.h"
|
|
|
|
#include <cjson/cJSON.h>
|
|
|
|
#define SCRIPT_UID (1996)
|
|
#define SCRIPT_OUTLEN (512 * 1024)
|
|
#define SCRIPT_ARGMAX (32)
|
|
#define SCRIPT_TOKLEN (512)
|
|
|
|
#define ZFREE(p) \
|
|
do { \
|
|
free((p)); \
|
|
(p) = 0; \
|
|
} while (0)
|
|
|
|
#define RTTY_SESS_MAX (10)
|
|
|
|
#define ARR_FIND_VALUE_IDX(A, len, value) \
|
|
({ \
|
|
size_t it = 0; \
|
|
for ((it) = 0; (it) < (len); (++it)) { \
|
|
if ((A)[it] == (value)) \
|
|
break; \
|
|
} \
|
|
(it); \
|
|
})
|
|
|
|
static int plat_state_get(struct plat_state_info *state);
|
|
static void plat_state_deinit(struct plat_state_info *state);
|
|
static int plat_port_speed_get(uint16_t fp_p_id, uint32_t *speed);
|
|
static int plat_port_duplex_get(uint16_t fp_p_id, bool *is_full_duplex);
|
|
static int plat_port_oper_status_get(uint16_t fp_p_id, bool *is_up);
|
|
static int plat_port_stats_get(uint16_t fp_p_id,
|
|
struct plat_port_counters *stats);
|
|
static int
|
|
plat_port_lldp_peer_info_get(uint16_t fp_p_id,
|
|
struct plat_port_lldp_peer_info *peer_info);
|
|
static int plat_upgrade_state(int *operation, int *percentage);
|
|
static int
|
|
plat_poe_port_state_get(uint16_t pid,
|
|
struct plat_poe_port_state *state);
|
|
static int
|
|
plat_poe_state_get(struct plat_poe_state *state);
|
|
static int plat_port_speed_set(uint16_t fp_p_id, uint32_t speed);
|
|
static int plat_port_duplex_set(uint16_t fp_p_id, uint32_t duplex);
|
|
static int plat_port_admin_state_set(uint16_t fp_p_id, uint8_t state);
|
|
static int plat_vlan_rif_set(uint16_t vid, struct plat_ipv4 *ipv4);
|
|
int plat_vlan_memberlist_set(struct gnma_change *c,
|
|
struct gnma_vlan_member_bmap *vlan_mbr,
|
|
struct plat_port_vlan *vlan);
|
|
static int plat_vlan_list_set(BITMAP_DECLARE(, GNMA_MAX_VLANS));
|
|
static int plat_syslog_set(struct plat_syslog_cfg *log_cfg, int count);
|
|
static int
|
|
plat_ieee8021x_system_auth_clients_get(uint16_t port_id,
|
|
char **buf, size_t *buf_size,
|
|
struct plat_ieee8021x_port_info *state);
|
|
|
|
#define CFG_LOG_CRIT(...) \
|
|
do { \
|
|
UC_LOG_CRIT(__VA_ARGS__); \
|
|
plat_log_printf(__VA_ARGS__); \
|
|
} while (0)
|
|
|
|
#define ARRAY_LENGTH(array) (sizeof((array))/sizeof((array)[0]))
|
|
|
|
#define SPEED_TO_STR(p, str) \
|
|
({ \
|
|
int rc = 0; \
|
|
switch (p) { \
|
|
case UCENTRAL_PORT_SPEED_10_E: \
|
|
strcpy(str, "10"); \
|
|
break; \
|
|
case UCENTRAL_PORT_SPEED_100_E: \
|
|
strcpy(str, "100"); \
|
|
break; \
|
|
case UCENTRAL_PORT_SPEED_1000_E: \
|
|
strcpy(str, "1000"); \
|
|
break; \
|
|
case UCENTRAL_PORT_SPEED_2500_E: \
|
|
strcpy(str, "2500"); \
|
|
break; \
|
|
case UCENTRAL_PORT_SPEED_5000_E: \
|
|
strcpy(str, "5000"); \
|
|
break; \
|
|
case UCENTRAL_PORT_SPEED_10000_E: \
|
|
strcpy(str, "10000"); \
|
|
break; \
|
|
case UCENTRAL_PORT_SPEED_25000_E: \
|
|
strcpy(str, "25000"); \
|
|
break; \
|
|
case UCENTRAL_PORT_SPEED_40000_E: \
|
|
strcpy(str, "40000"); \
|
|
break; \
|
|
case UCENTRAL_PORT_SPEED_100000_E: \
|
|
strcpy(str, "100000"); \
|
|
break; \
|
|
default: \
|
|
rc = 1; \
|
|
break; \
|
|
} \
|
|
(rc); \
|
|
})
|
|
#define STR_TO_SPEED(p, str) \
|
|
({ \
|
|
int rc = 0; \
|
|
rc = sscanf((str), "%u", (p)); \
|
|
(rc); \
|
|
})
|
|
|
|
#define PLAT_RADIUS_HOST_EXISTS_IN_CFG(_host, head) \
|
|
({bool res = false; \
|
|
struct plat_radius_hosts_list *_pos; \
|
|
UCENTRAL_LIST_FOR_EACH_MEMBER((_pos), (head)) { \
|
|
if (strcmp((_host), ((_pos)->host.hostname)) == 0) { \
|
|
res = true; \
|
|
break; \
|
|
} \
|
|
} \
|
|
(res);})
|
|
|
|
#define PLAT_DAC_HOST_EXISTS_IN_CFG(_host, head) \
|
|
({bool res = false; \
|
|
struct plat_ieee8021x_dac_list *_pos; \
|
|
UCENTRAL_LIST_FOR_EACH_MEMBER((_pos), (head)) { \
|
|
if (strcmp((_host), ((_pos)->host.hostname)) == 0) { \
|
|
res = true; \
|
|
break; \
|
|
} \
|
|
} \
|
|
(res);})
|
|
|
|
/* For now, let's define abs max buf size as:
|
|
* 1024 (bytes) per client, 10 clients total at max for 100 ports;
|
|
* Bare minimum client info has ~600B size (raw json).
|
|
*/
|
|
#define PLAT_IEEE8021X_AUT_CLIENTS_BUF_SIZE (1024 * 10 * 100)
|
|
|
|
enum {
|
|
FEAT_CORE,
|
|
FEAT_POE,
|
|
FEAT_AAA,
|
|
FEAT_MAX
|
|
};
|
|
|
|
enum {
|
|
FEATSTS_FAIL = -1,
|
|
FEATSTS_NONE = 0,
|
|
FEATSTS_OK,
|
|
FEATSTS_MAX
|
|
};
|
|
|
|
static int featsts[FEAT_MAX];
|
|
|
|
struct plat_cb_ctx {
|
|
void (*cb)();
|
|
void *data;
|
|
};
|
|
|
|
struct plat_telemetry_cb_ctx {
|
|
void (*cb)(struct plat_state_info *);
|
|
void *data;
|
|
};
|
|
|
|
struct plat_upgrade_cb_ctx {
|
|
int (*cb)();
|
|
void *data;
|
|
};
|
|
|
|
struct periodic {
|
|
pthread_t t;
|
|
int (*cb)(void *);
|
|
void *data;
|
|
uint64_t delay_usec;
|
|
uint64_t period_usec;
|
|
pthread_mutex_t mtx;
|
|
pthread_cond_t cv;
|
|
int stop;
|
|
};
|
|
|
|
struct poe_port {
|
|
struct plat_poe_port_state state;
|
|
struct gnma_port_key key;
|
|
bool is_admin_mode_up;
|
|
gnma_poe_port_detection_mode_t detection_mode;
|
|
uint32_t power_limit;
|
|
bool is_power_limit_user_defined;
|
|
gnma_poe_port_priority_t priority;
|
|
};
|
|
|
|
struct port {
|
|
struct gnma_port_key key;
|
|
struct {
|
|
gnma_8021x_port_ctrl_mode_t control_mode;
|
|
gnma_8021x_port_host_mode_t host_mode;
|
|
uint16_t auth_fail_vid;
|
|
uint16_t guest_vid;
|
|
bool is_authenticator;
|
|
} ieee8021x;
|
|
};
|
|
|
|
/* Schema doesn't support policy action and maxhop cfg,
|
|
* so whenever define default values and use whenever (if) needed.
|
|
*/
|
|
#define PLAT_DHCP_RELAY_DEFAULT_POLICY_ACT (GNMA_DHCP_RELAY_POLICY_ACTION_REPLACE)
|
|
#define PLAT_DHCP_RELAY_DEFAULT_MAXHOP_CNT (10)
|
|
#define PLAT_DHCP_RELAY_MAX_SERVERS (1)
|
|
struct plat_state {
|
|
struct {
|
|
struct port array[MAX_NUM_OF_PORTS];
|
|
BITMAP_DECLARE(ports_bmap, MAX_NUM_OF_PORTS);
|
|
} ports;
|
|
struct {
|
|
struct {
|
|
gnma_dhcp_relay_policy_action_type_t policy_act;
|
|
gnma_dhcp_relay_circuit_id_t circ_id;
|
|
uint8_t max_hop_cnt;
|
|
struct gnma_ip helper_addresses[PLAT_DHCP_RELAY_MAX_SERVERS];
|
|
bool enabled;
|
|
} dhcp_relay;
|
|
} vlans[GNMA_MAX_VLANS];
|
|
struct {
|
|
gnma_poe_power_mgmt_mode_t power_mgmt;
|
|
uint8_t usage_threshold;
|
|
/* Alloc all ports, but access them only if bit is set. */
|
|
struct poe_port ports[MAX_NUM_OF_PORTS];
|
|
BITMAP_DECLARE(ports_bmap, MAX_NUM_OF_PORTS);
|
|
} poe;
|
|
struct {
|
|
bool is_auth_control_enabled;
|
|
bool bounce_port_ignore;
|
|
bool disable_port_ignore;
|
|
bool ignore_server_key;
|
|
bool ignore_session_key;
|
|
gnma_das_auth_type_t das_auth_type;
|
|
struct gnma_das_dac_host_key *das_dac_keys_arr;
|
|
size_t das_dac_keys_arr_size;
|
|
} ieee8021x;
|
|
struct {
|
|
struct gnma_radius_host_key *hosts_keys_arr;
|
|
size_t hosts_keys_arr_size;
|
|
} radius;
|
|
struct ucentral_router router;
|
|
gnma_stp_mode_t stp_mode;
|
|
struct gnma_stp_attr stp_mode_attr;
|
|
struct gnma_stp_attr stp_vlan_attr[GNMA_MAX_VLANS];
|
|
/* TODO: max num per iface */
|
|
struct plat_ipv4 portsl2_rif_ipv4[MAX_NUM_OF_PORTS];
|
|
} plat_state;
|
|
|
|
#define plat_log_ringbuf_size 64
|
|
static char *plat_log_ringbuf[plat_log_ringbuf_size];
|
|
static size_t plat_log_ringbuf_rp = 0;
|
|
static size_t plat_log_ringbuf_wp = 0;
|
|
|
|
static const char *cfgid_path = "/var/lib/ucentral/saved_config_id";
|
|
static const char *cfgmetrics_path = "/var/lib/ucentral/saved_cfg_metrics";
|
|
|
|
static void *subscribe_hdl;
|
|
static struct plat_event_callbacks events_cbs;
|
|
|
|
static struct periodic *health_periodic;
|
|
static struct periodic *telemetry_periodic;
|
|
static struct periodic *state_periodic;
|
|
static struct periodic *upgrade_periodic;
|
|
|
|
#define IMG_DL_FILE_PATH "/var/lib/ucentral/img"
|
|
#define IMG_DL_PIPE_PATH "/var/lib/ucentral/upgrade_pipe"
|
|
#define IMG_DL_DEB_FILE_SIGNATURE "\x21\x3C\x61\x72\x63\x68\x3E"
|
|
static struct img_dl_task {
|
|
bool active;
|
|
bool downloaded_full_img;
|
|
CURL *curl;
|
|
pthread_t t;
|
|
FILE *fp;
|
|
uint8_t percentage;
|
|
uint8_t upgrade_state;
|
|
} img_dl_task;
|
|
|
|
static struct script_ctx {
|
|
pthread_t tid;
|
|
int is_tid_valid;
|
|
plat_run_script_cb cb;
|
|
void *ctx;
|
|
int t;
|
|
char *script_buf;
|
|
size_t script_bufsz;
|
|
char *outbuf;
|
|
} script_ctx;
|
|
|
|
static pthread_mutex_t script_mtx = PTHREAD_MUTEX_INITIALIZER;
|
|
static int script_count;
|
|
|
|
static int script_lock_aquire(void)
|
|
{
|
|
int rc = -1;
|
|
if (!pthread_mutex_lock(&script_mtx)) {
|
|
if (!script_count) {
|
|
++script_count;
|
|
rc = 0;
|
|
}
|
|
pthread_mutex_unlock(&script_mtx);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static void script_lock_release(void)
|
|
{
|
|
if (!pthread_mutex_lock(&script_mtx)) {
|
|
if (script_count)
|
|
--script_count;
|
|
pthread_mutex_unlock(&script_mtx);
|
|
}
|
|
}
|
|
|
|
/* free() must be called for returned string */
|
|
char *plat_log_pop(void)
|
|
{
|
|
char *msg = plat_log_ringbuf[plat_log_ringbuf_rp];
|
|
|
|
if (msg) {
|
|
plat_log_ringbuf[plat_log_ringbuf_rp] = NULL;
|
|
|
|
plat_log_ringbuf_rp++;
|
|
plat_log_ringbuf_rp %= plat_log_ringbuf_size;
|
|
|
|
return msg;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void plat_log_flush(void)
|
|
{
|
|
char *msg;
|
|
for (msg = plat_log_pop(); msg; msg = plat_log_pop())
|
|
free(msg);
|
|
}
|
|
|
|
/* free() must be called for returned string */
|
|
char *plat_log_pop_concatenate(void)
|
|
{
|
|
char *res_prev, *res, *msg;
|
|
int ret;
|
|
|
|
res = calloc(1, 1);
|
|
if (!res)
|
|
return NULL;
|
|
|
|
for (msg = plat_log_pop(), res_prev = res;
|
|
msg; msg = plat_log_pop(), res_prev = res) {
|
|
ret = asprintf(&res, "%s%s\n", res, msg);
|
|
free(msg);
|
|
free(res_prev);
|
|
|
|
if (ret == -1)
|
|
return NULL;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static void __plat_log_push(char *msg)
|
|
{
|
|
char *old_msg;
|
|
|
|
plat_log_ringbuf_wp++;
|
|
plat_log_ringbuf_wp %= plat_log_ringbuf_size;
|
|
|
|
if (plat_log_ringbuf_wp == plat_log_ringbuf_rp) {
|
|
old_msg = plat_log_pop();
|
|
free(old_msg);
|
|
}
|
|
|
|
plat_log_ringbuf[plat_log_ringbuf_wp] = msg;
|
|
}
|
|
|
|
static void plat_log_printf(const char *fmt, ...)
|
|
{
|
|
char *new_msg;
|
|
int ret;
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
ret = vasprintf(&new_msg, fmt, ap);
|
|
va_end(ap);
|
|
if (ret == -1 || !new_msg)
|
|
return;
|
|
|
|
__plat_log_push(new_msg);
|
|
}
|
|
|
|
static void plat_log_push(const char *msg)
|
|
{
|
|
plat_log_printf("%s", msg);
|
|
}
|
|
|
|
static int fdsetcmd(int fd, int getcmd, int setcmd, int is_set, int fl)
|
|
{
|
|
int flag = fcntl(fd, getcmd);
|
|
|
|
if (flag < 0 ||
|
|
fcntl(fd, setcmd, is_set ? (flag | fl) : (flag & ~(fl))))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static pid_t spawnp(const char *path, char *av[], char *env[],
|
|
const sigset_t *sigset, int uid, int gid, int *ifd,
|
|
int *ofd, int set_flag)
|
|
{
|
|
int e;
|
|
pid_t pid;
|
|
int in, out;
|
|
int pin[2] = { -1, -1 }, pout[2] = { -1, -1 };
|
|
|
|
if (!ifd) {
|
|
in = -1;
|
|
} else if (*ifd < 0) {
|
|
if (pipe(pin))
|
|
goto err;
|
|
in = pin[0];
|
|
if (set_flag && fdsetcmd(pin[1], F_GETFL, F_SETFL, 1, set_flag))
|
|
goto err;
|
|
} else {
|
|
in = *ifd;
|
|
}
|
|
|
|
if (!ofd) {
|
|
out = -1;
|
|
}
|
|
if (*ofd < 0) {
|
|
if (pipe(pout))
|
|
goto err;
|
|
out = pout[1];
|
|
if (set_flag &&
|
|
fdsetcmd(pout[0], F_GETFL, F_SETFL, 1, set_flag))
|
|
goto err;
|
|
} else {
|
|
out = *ofd;
|
|
}
|
|
|
|
pid = fork();
|
|
if (pid < 0)
|
|
goto err;
|
|
|
|
if (!pid) {
|
|
if ((in >= 0 && dup2(in, 0) < 0) ||
|
|
(out >= 0 && dup2(out, 1) < 0)) {
|
|
_exit(1);
|
|
}
|
|
|
|
if (sigset && sigprocmask(SIG_SETMASK, sigset, 0)) {
|
|
_exit(1);
|
|
}
|
|
|
|
if (in > 0)
|
|
close(in);
|
|
if (out >= 0 && out != 1)
|
|
close(out);
|
|
|
|
close(pin[1]);
|
|
close(pout[0]);
|
|
|
|
/*
|
|
* TODO(vb) closefrom(3)
|
|
*/
|
|
if (gid >= 0 && setgid(gid)) {
|
|
_exit(1);
|
|
}
|
|
if (uid >= 0 && setuid(uid)) {
|
|
_exit(1);
|
|
}
|
|
if (env) {
|
|
execvpe(path, av, env);
|
|
} else {
|
|
execvp(path, av);
|
|
}
|
|
_exit(1);
|
|
}
|
|
|
|
e = errno;
|
|
close(pin[0]);
|
|
if (ifd && *ifd < 0)
|
|
*ifd = pin[1];
|
|
|
|
close(pout[1]);
|
|
if (ofd && *ofd < 0)
|
|
*ofd = pout[0];
|
|
errno = e;
|
|
|
|
return pid;
|
|
|
|
err:
|
|
e = errno;
|
|
close(pin[0]);
|
|
close(pout[1]);
|
|
close(pin[0]);
|
|
close(pout[1]);
|
|
errno = e;
|
|
return -1;
|
|
}
|
|
|
|
struct timespec sub_timespec(const struct timespec *a, const struct timespec *b)
|
|
{
|
|
struct timespec d = {
|
|
.tv_sec = a->tv_sec - b->tv_sec,
|
|
.tv_nsec = a->tv_nsec - b->tv_nsec,
|
|
};
|
|
if (d.tv_nsec < 0) {
|
|
--d.tv_sec;
|
|
d.tv_nsec += 1000000000L;
|
|
}
|
|
return d;
|
|
}
|
|
|
|
void (*main_log_cb)(const char *) = plat_log_push;
|
|
|
|
static int plat_poe_port_num_get(uint16_t *num_of_active_poe_ports)
|
|
{
|
|
struct gnma_port_key port_key_list = {0};
|
|
uint16_t list_size = 1;
|
|
int ret;
|
|
|
|
ret = gnma_poe_port_list_get(&list_size, &port_key_list);
|
|
if (ret && ret != GNMA_ERR_OVERFLOW) {
|
|
*num_of_active_poe_ports = 0;
|
|
return ret;
|
|
}
|
|
|
|
*num_of_active_poe_ports = list_size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int plat_state_poe_init()
|
|
{
|
|
struct gnma_port_key *poe_ports_arr = NULL;
|
|
uint16_t poe_port_arr_size = 0;
|
|
struct poe_port *port;
|
|
uint16_t pid;
|
|
int ret;
|
|
int i;
|
|
|
|
ret = plat_poe_port_num_get(&poe_port_arr_size);
|
|
if (ret) {
|
|
UC_LOG_DBG("plat_poe_port_num_get");
|
|
goto err;
|
|
}
|
|
|
|
poe_ports_arr = calloc(poe_port_arr_size, sizeof(*poe_ports_arr));
|
|
if (!poe_ports_arr)
|
|
goto err;
|
|
|
|
ret = gnma_poe_port_list_get(&poe_port_arr_size, poe_ports_arr);
|
|
if (ret) {
|
|
UC_LOG_DBG("gnma_poe_port_list_get");
|
|
goto err;
|
|
}
|
|
|
|
for (i = 0; i < poe_port_arr_size; ++i) {
|
|
NAME_TO_PID(&pid, poe_ports_arr[i].name);
|
|
BITMAP_SET_BIT(plat_state.poe.ports_bmap, pid);
|
|
|
|
port = &plat_state.poe.ports[pid];
|
|
|
|
strcpy(port->key.name, poe_ports_arr[i].name);
|
|
|
|
ret = gnma_poe_port_admin_mode_get(&port->key,
|
|
&port->is_admin_mode_up);
|
|
if (ret) {
|
|
UC_LOG_ERR("gnma_poe_port_admin_mode_get");
|
|
goto err;
|
|
}
|
|
|
|
ret = gnma_poe_port_detection_mode_get(&port->key,
|
|
&port->detection_mode);
|
|
if (ret) {
|
|
UC_LOG_ERR("gnma_poe_port_detection_mode_get");
|
|
goto err;
|
|
}
|
|
|
|
ret = gnma_poe_port_power_limit_get(&port->key,
|
|
&port->is_power_limit_user_defined,
|
|
&port->power_limit);
|
|
if (ret) {
|
|
UC_LOG_ERR("gnma_poe_port_power_limit_get");
|
|
goto err;
|
|
}
|
|
|
|
ret = gnma_poe_port_priority_get(&port->key,
|
|
&port->priority);
|
|
if (ret) {
|
|
UC_LOG_ERR("gnma_poe_port_priority_get");
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
ret = gnma_poe_power_mgmt_get(&plat_state.poe.power_mgmt);
|
|
if (ret) {
|
|
UC_LOG_ERR("gnma_poe_power_mgmt_get");
|
|
goto err;
|
|
}
|
|
ret = gnma_poe_usage_threshold_get(&plat_state.poe.usage_threshold);
|
|
if (ret) {
|
|
UC_LOG_ERR("gnma_poe_usage_threshold_get");
|
|
goto err;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
err:
|
|
free(poe_ports_arr);
|
|
return ret;
|
|
}
|
|
|
|
static int plat_state_radius_init()
|
|
{
|
|
int ret;
|
|
|
|
free(plat_state.radius.hosts_keys_arr);
|
|
plat_state.radius.hosts_keys_arr = NULL;
|
|
plat_state.radius.hosts_keys_arr_size = 0;
|
|
|
|
ret = gnma_radius_hosts_list_get(&plat_state.radius.hosts_keys_arr_size,
|
|
NULL);
|
|
if (ret && ret != GNMA_ERR_OVERFLOW) {
|
|
UC_LOG_CRIT("gnma_radius_hosts_list_get failed");
|
|
plat_state.radius.hosts_keys_arr_size = 0;
|
|
return ret;
|
|
}
|
|
|
|
/* No RADIUS hosts configured, no need to update cache. */
|
|
if (0 == plat_state.radius.hosts_keys_arr_size)
|
|
return 0;
|
|
|
|
plat_state.radius.hosts_keys_arr =
|
|
calloc(plat_state.radius.hosts_keys_arr_size,
|
|
sizeof(*plat_state.radius.hosts_keys_arr));
|
|
if (!plat_state.radius.hosts_keys_arr) {
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
|
|
ret = gnma_radius_hosts_list_get(&plat_state.radius.hosts_keys_arr_size,
|
|
plat_state.radius.hosts_keys_arr);
|
|
if (ret) {
|
|
UC_LOG_CRIT("gnma_radius_hosts_list_get failed");
|
|
goto err;
|
|
}
|
|
return 0;
|
|
|
|
err:
|
|
free(plat_state.radius.hosts_keys_arr);
|
|
plat_state.radius.hosts_keys_arr = NULL;
|
|
plat_state.radius.hosts_keys_arr_size = 0;
|
|
return ret;
|
|
}
|
|
|
|
static void router_fib_key2gnma_prefix(struct ucentral_router_fib_key *uk,
|
|
struct gnma_ip_prefix *gp)
|
|
{
|
|
gp->ip.v = AF_INET;
|
|
gp->ip.u.v4 = uk->prefix;
|
|
gp->prefix_len = uk->prefix_len;
|
|
}
|
|
|
|
static void gnma_prefix2router_fib_key(struct gnma_ip_prefix *gp,
|
|
struct ucentral_router_fib_key *uk)
|
|
{
|
|
/* TODO ipv6 ? */
|
|
uk->prefix = gp->ip.u.v4;
|
|
uk->prefix_len = gp->prefix_len;
|
|
}
|
|
|
|
static int router_fib_info2gnma_attr(struct ucentral_router_fib_info *ui,
|
|
struct gnma_route_attrs *ga)
|
|
{
|
|
switch (ui->type) {
|
|
case UCENTRAL_ROUTE_BLACKHOLE:
|
|
ga->type = GNMA_ROUTE_TYPE_BLACKHOLE;
|
|
break;
|
|
case UCENTRAL_ROUTE_CONNECTED:
|
|
ga->type = GNMA_ROUTE_TYPE_CONNECTED;
|
|
ga->connected.vid = ui->connected.vid;
|
|
break;
|
|
case UCENTRAL_ROUTE_NH:
|
|
ga->type = GNMA_ROUTE_TYPE_NEXTHOP;
|
|
ga->nexthop.vid = ui->nh.vid;
|
|
ga->nexthop.gw = ui->nh.gw;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gnma_attr2router_fib_info(struct gnma_route_attrs *ga,
|
|
struct ucentral_router_fib_info *ui)
|
|
{
|
|
switch (ga->type) {
|
|
case GNMA_ROUTE_TYPE_BLACKHOLE:
|
|
ui->type = UCENTRAL_ROUTE_BLACKHOLE;
|
|
break;
|
|
case GNMA_ROUTE_TYPE_CONNECTED:
|
|
ui->type = UCENTRAL_ROUTE_CONNECTED;
|
|
ui->connected.vid = ga->connected.vid;
|
|
break;
|
|
case GNMA_ROUTE_TYPE_NEXTHOP:
|
|
ui->type = UCENTRAL_ROUTE_NH;
|
|
ui->nh.vid = ga->nexthop.vid;
|
|
ui->nh.gw = ga->nexthop.gw;
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int plat_state_portsl2_init()
|
|
{
|
|
struct gnma_port_key *plist = NULL;
|
|
uint16_t pcount = 0, list_size;
|
|
struct gnma_ip_prefix prefix;
|
|
uint16_t pid;
|
|
int err = -1;
|
|
int i;
|
|
|
|
if (plat_port_num_get(&pcount)) {
|
|
UC_LOG_ERR("plat_port_num_get failed");
|
|
goto err;
|
|
}
|
|
|
|
plist = calloc(pcount, sizeof(*plist));
|
|
if (!plist)
|
|
goto err;
|
|
|
|
if (gnma_port_list_get(&pcount, plist)) {
|
|
UC_LOG_ERR("gnma_port_list_get failed");
|
|
goto err;
|
|
}
|
|
|
|
/* TODO generic ports iterator... Cache ? */
|
|
for (i = 0; i < pcount; i++) {
|
|
list_size = 1; /* TODO */
|
|
/* plat_state initialized with zeroes */
|
|
if (gnma_portl2_erif_attr_pref_list_get(&plist[i], &list_size,
|
|
&prefix)) {
|
|
UC_LOG_ERR("gnma_port_list_get failed");
|
|
goto err;
|
|
}
|
|
|
|
if (list_size && prefix.ip.v == AF_INET) {
|
|
NAME_TO_PID(&pid, plist[i].name);
|
|
plat_state.portsl2_rif_ipv4[pid].subnet_len = prefix.prefix_len;
|
|
memcpy(&plat_state.portsl2_rif_ipv4[pid].subnet,
|
|
&prefix.ip.u.v4, sizeof(prefix.ip.u.v4));
|
|
plat_state.portsl2_rif_ipv4[pid].exist = true;
|
|
}
|
|
}
|
|
|
|
err = 0;
|
|
err:
|
|
free(plist);
|
|
return err;
|
|
}
|
|
|
|
static int plat_state_port_ieee8021x_init(struct port *port)
|
|
{
|
|
int ret;
|
|
|
|
ret = gnma_port_ieee8021x_pae_mode_get(
|
|
&port->key, &port->ieee8021x.is_authenticator);
|
|
if (ret) {
|
|
UC_LOG_ERR("<%s> gnma_port_ieee8021x_pae_mode_get failed",
|
|
port->key.name);
|
|
return -1;
|
|
}
|
|
|
|
ret = gnma_port_ieee8021x_port_ctrl_get(&port->key,
|
|
&port->ieee8021x.control_mode);
|
|
if (ret) {
|
|
UC_LOG_ERR("<%s> gnma_port_ieee8021x_port_ctrl_get failed",
|
|
port->key.name);
|
|
return -1;
|
|
}
|
|
|
|
ret = gnma_port_ieee8021x_port_host_mode_get(
|
|
&port->key, &port->ieee8021x.host_mode);
|
|
if (ret) {
|
|
UC_LOG_ERR("<%s> gnma_port_ieee8021x_port_host_mode_get failed",
|
|
port->key.name);
|
|
return -1;
|
|
}
|
|
|
|
ret = gnma_port_ieee8021x_guest_vlan_get(&port->key,
|
|
&port->ieee8021x.guest_vid);
|
|
if (ret) {
|
|
UC_LOG_ERR("<%s> gnma_port_ieee8021x_guest_vlan_get failed",
|
|
port->key.name);
|
|
return -1;
|
|
}
|
|
|
|
ret = gnma_port_ieee8021x_unauthorized_vlan_get(
|
|
&port->key, &port->ieee8021x.auth_fail_vid);
|
|
if (ret) {
|
|
UC_LOG_ERR(
|
|
"<%s> gnma_port_ieee8021x_unauthorized_vlan_get failed",
|
|
port->key.name);
|
|
return -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int plat_state_ports_init()
|
|
{
|
|
struct gnma_port_key *port_key_list;
|
|
uint16_t port_list_num;
|
|
struct port *port;
|
|
uint16_t port_id;
|
|
int ret;
|
|
int i;
|
|
|
|
ret = plat_port_num_get(&port_list_num);
|
|
if (ret) {
|
|
UC_LOG_ERR("Failed to get num of active ports");
|
|
return ret;
|
|
}
|
|
|
|
port_key_list = calloc(port_list_num, sizeof(*port_key_list));
|
|
if (!port_key_list) {
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
|
|
ret = gnma_port_list_get(&port_list_num, port_key_list);
|
|
if (ret && ret != GNMA_ERR_OVERFLOW) {
|
|
UC_LOG_ERR("Port list get failed");
|
|
goto err;
|
|
}
|
|
|
|
for (i = 0; i < port_list_num; ++i) {
|
|
NAME_TO_PID(&port_id, port_key_list[i].name);
|
|
BITMAP_SET_BIT(plat_state.ports.ports_bmap, port_id);
|
|
|
|
port = &plat_state.ports.array[port_id];
|
|
|
|
memcpy(&port->key, &port_key_list[i],
|
|
sizeof(port_key_list[i]));
|
|
|
|
if (plat_state_port_ieee8021x_init(port)) {
|
|
UC_LOG_ERR("plat_state_port_ieee8021x_init failed");
|
|
featsts[FEAT_AAA] = FEATSTS_FAIL;
|
|
}
|
|
}
|
|
|
|
free(port_key_list);
|
|
return 0;
|
|
err:
|
|
free(port_key_list);
|
|
BITMAP_CLEAR(plat_state.ports.ports_bmap, MAX_NUM_OF_PORTS);
|
|
return ret;
|
|
}
|
|
|
|
static int plat_state_ieee8021x_dac_list_init(void)
|
|
{
|
|
int ret;
|
|
|
|
free(plat_state.ieee8021x.das_dac_keys_arr);
|
|
plat_state.ieee8021x.das_dac_keys_arr = NULL;
|
|
plat_state.ieee8021x.das_dac_keys_arr_size = 0;
|
|
|
|
ret = gnma_ieee8021x_das_dac_hosts_list_get(&plat_state.ieee8021x.das_dac_keys_arr_size,
|
|
NULL);
|
|
if (ret && ret != GNMA_ERR_OVERFLOW) {
|
|
UC_LOG_CRIT("gnma_ieee8021x_das_dac_hosts_list_get failed");
|
|
plat_state.ieee8021x.das_dac_keys_arr_size = 0;
|
|
return ret;
|
|
}
|
|
|
|
/* No DAC hosts configured, no need to update cache. */
|
|
if (0 == plat_state.ieee8021x.das_dac_keys_arr_size)
|
|
return 0;
|
|
|
|
plat_state.ieee8021x.das_dac_keys_arr =
|
|
calloc(plat_state.ieee8021x.das_dac_keys_arr_size,
|
|
sizeof(*plat_state.ieee8021x.das_dac_keys_arr));
|
|
if (!plat_state.ieee8021x.das_dac_keys_arr) {
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
|
|
ret = gnma_ieee8021x_das_dac_hosts_list_get(&plat_state.ieee8021x.das_dac_keys_arr_size,
|
|
plat_state.ieee8021x.das_dac_keys_arr);
|
|
if (ret) {
|
|
UC_LOG_CRIT("gnma_ieee8021x_das_dac_hosts_list_get failed");
|
|
goto err;
|
|
}
|
|
return 0;
|
|
|
|
err:
|
|
free(plat_state.radius.hosts_keys_arr);
|
|
plat_state.radius.hosts_keys_arr = NULL;
|
|
plat_state.radius.hosts_keys_arr_size = 0;
|
|
return ret;
|
|
}
|
|
|
|
static int plat_state_ieee8021x_init(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = gnma_ieee8021x_system_auth_control_get(&plat_state.ieee8021x.is_auth_control_enabled);
|
|
if (ret) {
|
|
UC_LOG_CRIT("gnma_ieee8021x_system_auth_control_get failed");
|
|
return ret;
|
|
}
|
|
|
|
ret = gnma_ieee8021x_das_bounce_port_ignore_get(&plat_state.ieee8021x.bounce_port_ignore);
|
|
if (ret) {
|
|
UC_LOG_CRIT("gnma_ieee8021x_das_bounce_port_ignore_get failed");
|
|
return ret;
|
|
}
|
|
|
|
ret = gnma_ieee8021x_das_disable_port_ignore_get(&plat_state.ieee8021x.disable_port_ignore);
|
|
if (ret) {
|
|
UC_LOG_CRIT("gnma_ieee8021x_das_disable_port_ignore_get failed");
|
|
return ret;
|
|
}
|
|
|
|
ret = gnma_ieee8021x_das_ignore_server_key_get(&plat_state.ieee8021x.ignore_server_key);
|
|
if (ret) {
|
|
UC_LOG_CRIT("gnma_ieee8021x_das_ignore_server_key_get failed");
|
|
return ret;
|
|
}
|
|
|
|
ret = gnma_ieee8021x_das_ignore_session_key_get(&plat_state.ieee8021x.ignore_session_key);
|
|
if (ret) {
|
|
UC_LOG_CRIT("gnma_ieee8021x_das_ignore_session_key_get failed");
|
|
return ret;
|
|
}
|
|
|
|
ret = gnma_ieee8021x_das_auth_type_key_get(&plat_state.ieee8021x.das_auth_type);
|
|
if (ret) {
|
|
UC_LOG_CRIT("gnma_ieee8021x_das_auth_type_key_get failed");
|
|
return ret;
|
|
}
|
|
|
|
ret = plat_state_ieee8021x_dac_list_init();
|
|
if (ret) {
|
|
UC_LOG_CRIT("plat_state_ieee8021x_dac_list_init failed");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int plat_state_init()
|
|
{
|
|
BITMAP_DECLARE(vlans, GNMA_MAX_VLANS);
|
|
size_t arr_size = PLAT_DHCP_RELAY_MAX_SERVERS;
|
|
struct gnma_route_attrs *attrs_list = NULL;
|
|
struct gnma_ip_prefix *prefix_list = NULL;
|
|
uint32_t prefix_list_size = 0, prefix_iter;
|
|
struct ucentral_router_fib_node node;
|
|
uint16_t vid;
|
|
int ret = 0;
|
|
size_t i;
|
|
|
|
memset(&plat_state, 0, sizeof(plat_state));
|
|
memset(&featsts, 0, sizeof featsts);
|
|
|
|
featsts[FEAT_CORE] = FEATSTS_FAIL;
|
|
|
|
ret = plat_state_ieee8021x_init();
|
|
if (ret) {
|
|
UC_LOG_CRIT("plat_state_ieee8021x_init failed");
|
|
featsts[FEAT_AAA] = FEATSTS_FAIL;
|
|
}
|
|
|
|
ret = plat_state_radius_init();
|
|
if (ret) {
|
|
UC_LOG_CRIT("plat_state_radius_init failed");
|
|
featsts[FEAT_AAA] = FEATSTS_FAIL;
|
|
}
|
|
|
|
ret = plat_state_ports_init();
|
|
if (ret) {
|
|
UC_LOG_CRIT("plat_state_ports_init failed");
|
|
goto err;
|
|
}
|
|
|
|
BITMAP_CLEAR(vlans, GNMA_MAX_VLANS);
|
|
ret = gnma_vlan_list_get(vlans);
|
|
if (ret) {
|
|
UC_LOG_CRIT("gnma_vlan_list_get");
|
|
goto err;
|
|
}
|
|
|
|
ret = gnma_stp_mode_get(&plat_state.stp_mode, &plat_state.stp_mode_attr);
|
|
if (ret) {
|
|
UC_LOG_CRIT("gnma_stp_mode_get");
|
|
goto err;
|
|
}
|
|
|
|
ret = gnma_stp_vid_bulk_get(&plat_state.stp_vlan_attr[0],
|
|
GNMA_MAX_VLANS);
|
|
if (ret) {
|
|
UC_LOG_CRIT("gnma_stp_vid_bulk_get");
|
|
goto err;
|
|
}
|
|
|
|
BITMAP_FOR_EACH_BIT_SET(i, vlans, GNMA_MAX_VLANS)
|
|
{
|
|
/* It's possible that relay is not configured on this vlan.
|
|
* Ignore and proceed to next.
|
|
*/
|
|
vid = (uint16_t)i;
|
|
ret = gnma_vlan_dhcp_relay_server_list_get(vid, &arr_size,
|
|
&plat_state.vlans[vid].dhcp_relay.helper_addresses[0]);
|
|
if (ret)
|
|
continue;
|
|
|
|
ret = gnma_vlan_dhcp_relay_ciruit_id_get(vid,
|
|
&plat_state.vlans[vid].dhcp_relay.circ_id);
|
|
if (ret) {
|
|
UC_LOG_CRIT("gnma_vlan_dhcp_relay_ciruit_id_get");
|
|
goto err;
|
|
}
|
|
|
|
ret = gnma_vlan_dhcp_relay_policy_action_get(vid,
|
|
&plat_state.vlans[vid].dhcp_relay.policy_act);
|
|
if (ret) {
|
|
UC_LOG_CRIT("gnma_vlan_dhcp_relay_policy_action_get");
|
|
goto err;
|
|
}
|
|
|
|
ret = gnma_vlan_dhcp_relay_max_hop_cnt_get(vid,
|
|
&plat_state.vlans[vid].dhcp_relay.max_hop_cnt);
|
|
if (ret) {
|
|
UC_LOG_CRIT("gnma_vlan_dhcp_relay_max_hop_cnt_get");
|
|
goto err;
|
|
}
|
|
|
|
plat_state.vlans[vid].dhcp_relay.enabled = true;
|
|
}
|
|
|
|
if (plat_state_poe_init()) {
|
|
UC_LOG_CRIT("plat_state_poe_init");
|
|
featsts[FEAT_POE] = FEATSTS_FAIL;
|
|
}
|
|
|
|
if (plat_state_portsl2_init()) {
|
|
UC_LOG_CRIT("plat_state_portsl2_init");
|
|
goto err;
|
|
}
|
|
|
|
ret = gnma_route_list_get(0, &prefix_list_size,
|
|
prefix_list, attrs_list);
|
|
if (ret && ret != GNMA_ERR_OVERFLOW) {
|
|
UC_LOG_CRIT("gnma_route_list_get");
|
|
goto err;
|
|
}
|
|
|
|
prefix_list = calloc(prefix_list_size, sizeof(prefix_list[0]));
|
|
attrs_list = calloc(prefix_list_size, sizeof(attrs_list[0]));
|
|
if (!prefix_list || !attrs_list) {
|
|
goto err;
|
|
}
|
|
|
|
ret = gnma_route_list_get(0, &prefix_list_size,
|
|
prefix_list, attrs_list);
|
|
if (ret) {
|
|
UC_LOG_CRIT("gnma_route_list_get");
|
|
goto err;
|
|
}
|
|
|
|
/* TODO ECMP */
|
|
ucentral_router_fib_db_free(&plat_state.router);
|
|
ret = ucentral_router_fib_db_alloc(&plat_state.router, prefix_list_size);
|
|
if (ret) {
|
|
UC_LOG_CRIT("ucentral_router_fib_db_alloc");
|
|
goto err;
|
|
}
|
|
|
|
for (prefix_iter = 0; prefix_iter < prefix_list_size; prefix_iter++) {
|
|
gnma_prefix2router_fib_key(&prefix_list[prefix_iter], &node.key);
|
|
ret = gnma_attr2router_fib_info(&attrs_list[prefix_iter], &node.info);
|
|
if (ret) {
|
|
UC_LOG_CRIT("gnma_attr2router_fib_info");
|
|
goto err;
|
|
}
|
|
|
|
ret = ucentral_router_fib_db_append(&plat_state.router, &node);
|
|
if (ret) {
|
|
UC_LOG_CRIT("ucentral_router_fib_db_append");
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
if (featsts[FEAT_AAA] == FEATSTS_FAIL) {
|
|
UC_LOG_CRIT("AAA feature failed to initialize");
|
|
} else {
|
|
featsts[FEAT_AAA] = FEATSTS_OK;
|
|
}
|
|
|
|
if (featsts[FEAT_POE] == FEATSTS_FAIL) {
|
|
UC_LOG_CRIT("POE feature failed to initialize");
|
|
} else {
|
|
featsts[FEAT_POE] = FEATSTS_OK;
|
|
}
|
|
|
|
featsts[FEAT_CORE] = FEATSTS_OK;
|
|
err:
|
|
free (prefix_list);
|
|
free (attrs_list);
|
|
|
|
for (i = 0; i < FEAT_MAX; ++i) {
|
|
if (featsts[i] != FEATSTS_OK) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int plat_init(void)
|
|
{
|
|
if (gnma_switch_create()) {
|
|
UC_LOG_CRIT("gnma_switch_create failed");
|
|
return -1;
|
|
}
|
|
|
|
if (plat_state_init()) {
|
|
UC_LOG_CRIT("plat_state_init failed");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void *periodic_thread(void *args)
|
|
{
|
|
int rc;
|
|
struct periodic *p = args;
|
|
uint64_t timeout = p->delay_usec;
|
|
struct timespec now = { 0 };
|
|
struct timespec deadline = { 0 };
|
|
int stop = 0;
|
|
|
|
UC_LOG_DBG("enter");
|
|
while (1) {
|
|
rc = 0;
|
|
pthread_mutex_lock(&p->mtx);
|
|
/* TODO(vb) monotonic clock wrap-up? check how that is handled in
|
|
* FUTEX_WAIT. */
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
/* TODO(vb) overflows? */
|
|
deadline.tv_sec = now.tv_sec + (timeout / 1000000);
|
|
deadline.tv_nsec = now.tv_nsec + (timeout % 1000000) * 1000;
|
|
if (deadline.tv_nsec >= 1000000000) {
|
|
/* normalize */
|
|
deadline.tv_sec += 1;
|
|
deadline.tv_nsec -= 1000000000;
|
|
}
|
|
while (!p->stop && !rc) {
|
|
rc = pthread_cond_timedwait(&p->cv, &p->mtx, &deadline);
|
|
}
|
|
stop = p->stop;
|
|
pthread_mutex_unlock(&p->mtx);
|
|
|
|
if (stop)
|
|
break;
|
|
|
|
if (rc != ETIMEDOUT) {
|
|
UC_LOG_ERR("pthread_cond_timedwait rc=%d", rc);
|
|
break;
|
|
}
|
|
|
|
/* If cb returned nonzero value - it signalizes the periodic
|
|
* subsystem that this thread should exit.
|
|
*/
|
|
if (p->cb)
|
|
if (p->cb(p->data))
|
|
break;
|
|
|
|
timeout = p->period_usec;
|
|
}
|
|
UC_LOG_DBG("exit");
|
|
return 0;
|
|
}
|
|
|
|
static int periodic_create(struct periodic **periodic, uint64_t delay_usec,
|
|
uint64_t period_usec, int (*cb)(void *), void *data)
|
|
{
|
|
pthread_condattr_t cvattr;
|
|
struct periodic *p = 0;
|
|
int ret = -1;
|
|
|
|
if (!periodic)
|
|
return -1;
|
|
|
|
if (!(p = malloc(sizeof *p))) {
|
|
UC_LOG_DBG("malloc failed: %s", strerror(errno));
|
|
return -1;
|
|
}
|
|
*p = (struct periodic){
|
|
.cb = cb,
|
|
.data = data,
|
|
.period_usec = period_usec,
|
|
.delay_usec = delay_usec,
|
|
};
|
|
pthread_condattr_init(&cvattr);
|
|
if (pthread_condattr_setclock(&cvattr, CLOCK_MONOTONIC)) {
|
|
UC_LOG_DBG("pthread_condattr_setclock: %s", strerror(errno));
|
|
goto err;
|
|
}
|
|
if (pthread_cond_init(&p->cv, &cvattr)) {
|
|
UC_LOG_DBG("pthread_cond_init: %s", strerror(errno));
|
|
goto err;
|
|
}
|
|
pthread_mutex_init(&p->mtx, 0);
|
|
if (pthread_create(&p->t, 0, periodic_thread, p)) {
|
|
UC_LOG_DBG("pthread_create failed: %s", strerror(errno));
|
|
pthread_cond_destroy(&p->cv);
|
|
pthread_mutex_destroy(&p->mtx);
|
|
goto err;
|
|
}
|
|
|
|
*periodic = p;
|
|
p = 0;
|
|
ret = 0;
|
|
err:
|
|
pthread_condattr_destroy(&cvattr);
|
|
free(p);
|
|
return ret;
|
|
}
|
|
|
|
static void periodic_destroy(struct periodic **p)
|
|
{
|
|
if (!p || !*p)
|
|
return;
|
|
pthread_mutex_lock(&(*p)->mtx);
|
|
(*p)->stop = 1;
|
|
pthread_mutex_unlock(&(*p)->mtx);
|
|
pthread_cond_broadcast(&(*p)->cv);
|
|
pthread_join((*p)->t, 0);
|
|
pthread_cond_destroy(&(*p)->cv);
|
|
pthread_mutex_destroy(&(*p)->mtx);
|
|
free(*p);
|
|
*p = 0;
|
|
}
|
|
|
|
static int health_periodic_cb(void *data)
|
|
{
|
|
struct plat_cb_ctx *ctx = data;
|
|
void (*cb)(struct plat_health_info *) = ctx->cb;
|
|
struct plat_health_info health = {
|
|
.sanity = 100,
|
|
};
|
|
|
|
if (featsts[FEAT_CORE] != FEATSTS_OK) {
|
|
health.sanity = 0;
|
|
snprintf(health.msg[2], sizeof health.msg[2],
|
|
"the core features are not initialized");
|
|
|
|
} else {
|
|
if (featsts[FEAT_AAA] != FEATSTS_OK) {
|
|
health.sanity = 50;
|
|
snprintf(health.msg[0], sizeof health.msg[0],
|
|
"the 8021X feature is not initialized");
|
|
}
|
|
|
|
if (featsts[FEAT_POE] != FEATSTS_OK) {
|
|
health.sanity = 50;
|
|
snprintf(health.msg[1], sizeof health.msg[1],
|
|
"the POE feature is not initialized");
|
|
}
|
|
}
|
|
|
|
if (cb)
|
|
cb(&health);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void plat_health_poll(void (*cb)(struct plat_health_info *), int period_sec)
|
|
{
|
|
static struct plat_cb_ctx ctx;
|
|
periodic_destroy(&health_periodic);
|
|
ctx = (struct plat_cb_ctx){
|
|
.cb = (void (*)())cb,
|
|
};
|
|
periodic_create(&health_periodic, 0, period_sec * 1000000,
|
|
health_periodic_cb, &ctx);
|
|
}
|
|
|
|
void plat_health_poll_stop(void)
|
|
{
|
|
periodic_destroy(&health_periodic);
|
|
}
|
|
|
|
static int telemetry_periodic_cb(void *data)
|
|
{
|
|
struct plat_telemetry_cb_ctx *ctx = data;
|
|
void (*cb)(struct plat_state_info *) = ctx->cb;
|
|
struct plat_state_info state = {0};
|
|
|
|
if (plat_state_get(&state)) {
|
|
return 0;
|
|
}
|
|
|
|
if (cb)
|
|
cb(&state);
|
|
|
|
plat_state_deinit(&state);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void plat_telemetry_poll(void (*cb)(struct plat_state_info *), int period_sec)
|
|
{
|
|
static struct plat_telemetry_cb_ctx ctx;
|
|
periodic_destroy(&telemetry_periodic);
|
|
ctx = (struct plat_telemetry_cb_ctx){
|
|
.cb = cb,
|
|
};
|
|
periodic_create(&telemetry_periodic, 0, period_sec * 1000000,
|
|
telemetry_periodic_cb, &ctx);
|
|
}
|
|
|
|
void plat_telemetry_poll_stop(void)
|
|
{
|
|
periodic_destroy(&telemetry_periodic);
|
|
}
|
|
|
|
static int state_periodic_cb(void *data)
|
|
{
|
|
struct plat_telemetry_cb_ctx *ctx = data;
|
|
void (*cb)(struct plat_state_info *) = ctx->cb;
|
|
struct plat_state_info state = {0};
|
|
|
|
if (plat_state_get(&state)) {
|
|
return 0;
|
|
}
|
|
|
|
if (cb)
|
|
cb(&state);
|
|
|
|
plat_state_deinit(&state);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void plat_state_poll(void (*cb)(struct plat_state_info *), int period_sec)
|
|
{
|
|
static struct plat_telemetry_cb_ctx ctx;
|
|
periodic_destroy(&state_periodic);
|
|
ctx = (struct plat_telemetry_cb_ctx){
|
|
.cb = (void (*)(struct plat_state_info *))cb,
|
|
};
|
|
periodic_create(&state_periodic, 0, period_sec * 1000000,
|
|
state_periodic_cb, &ctx);
|
|
}
|
|
|
|
void plat_state_poll_stop(void)
|
|
{
|
|
periodic_destroy(&state_periodic);
|
|
}
|
|
|
|
static int upgrade_periodic_cb(void *data)
|
|
{
|
|
struct plat_upgrade_cb_ctx *ctx = data;
|
|
int (*cb)(struct plat_upgrade_info *) = ctx->cb;
|
|
struct plat_upgrade_info upgrade = { 0 };
|
|
|
|
if (plat_upgrade_state(&upgrade.operation, &upgrade.percentage))
|
|
return 0;
|
|
|
|
if (cb)
|
|
if (cb(&upgrade))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void plat_upgrade_poll(int (*cb)(struct plat_upgrade_info *), int period_sec)
|
|
{
|
|
static struct plat_upgrade_cb_ctx ctx;
|
|
periodic_destroy(&upgrade_periodic);
|
|
ctx = (struct plat_upgrade_cb_ctx){
|
|
.cb = (int (*)())cb,
|
|
};
|
|
periodic_create(&upgrade_periodic, 0, period_sec * 1000000,
|
|
upgrade_periodic_cb, &ctx);
|
|
}
|
|
|
|
void plat_upgrade_poll_stop(void)
|
|
{
|
|
periodic_destroy(&upgrade_periodic);
|
|
}
|
|
|
|
int plat_port_admin_state_set(uint16_t fp_p_id, uint8_t state)
|
|
{
|
|
struct gnma_port_key gnma_port;
|
|
|
|
PID_TO_NAME(fp_p_id, gnma_port.name);
|
|
|
|
gnma_port_admin_state_set(&gnma_port, state == UCENTRAL_PORT_ENABLED_E ? true : false);
|
|
return 0;
|
|
}
|
|
|
|
int plat_port_speed_set(uint16_t fp_p_id, uint32_t speed)
|
|
{
|
|
struct gnma_port_key gnma_port;
|
|
char speed_str[32] = {0};
|
|
|
|
PID_TO_NAME(fp_p_id, gnma_port.name);
|
|
if (SPEED_TO_STR(speed, speed_str))
|
|
return -1;
|
|
|
|
gnma_port_speed_set(&gnma_port, speed_str);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int plat_port_duplex_set(uint16_t fp_p_id, uint32_t duplex)
|
|
{
|
|
struct gnma_port_key gnma_port;
|
|
|
|
PID_TO_NAME(fp_p_id, gnma_port.name);
|
|
|
|
gnma_port_duplex_set(&gnma_port,
|
|
duplex == UCENTRAL_PORT_DUPLEX_FULL_E
|
|
? true
|
|
: false);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int plat_port_speed_get(uint16_t fp_p_id, uint32_t *speed)
|
|
{
|
|
struct gnma_port_key gnma_port;
|
|
char speed_str[8] = {0};
|
|
int ret;
|
|
|
|
PID_TO_NAME(fp_p_id, gnma_port.name);
|
|
|
|
ret = gnma_port_speed_get(&gnma_port, speed_str, sizeof(speed_str));
|
|
if (ret)
|
|
return ret;
|
|
|
|
STR_TO_SPEED(speed, speed_str);
|
|
return 0;
|
|
}
|
|
|
|
int plat_port_duplex_get(uint16_t fp_p_id, bool *is_full_duplex)
|
|
{
|
|
struct gnma_port_key gnma_port;
|
|
|
|
PID_TO_NAME(fp_p_id, gnma_port.name);
|
|
|
|
return gnma_port_duplex_get(&gnma_port, is_full_duplex);
|
|
}
|
|
|
|
int plat_port_oper_status_get(uint16_t fp_p_id, bool *is_up)
|
|
{
|
|
struct gnma_port_key gnma_port;
|
|
|
|
PID_TO_NAME(fp_p_id, gnma_port.name);
|
|
|
|
return gnma_port_oper_status_get(&gnma_port, is_up);
|
|
}
|
|
|
|
int plat_port_num_get(uint16_t *num_of_active_ports)
|
|
{
|
|
struct gnma_port_key port_key_list = {0};
|
|
uint16_t list_size = 1;
|
|
int ret;
|
|
|
|
/* It is NOT safe to not pass prt to list (e.g. NULL), as underlying
|
|
* function might memset this <object> ptr. C standard explicitly states
|
|
* that memsset(0, 0, 0) (where dst is NULL) is UB.
|
|
* Pass at least <one> object (port_key_list), but pass size <zero>
|
|
* (list_size).
|
|
*/
|
|
ret = gnma_port_list_get(&list_size, &port_key_list);
|
|
if (ret && ret != GNMA_ERR_OVERFLOW) {
|
|
*num_of_active_ports = 0;
|
|
return ret;
|
|
}
|
|
|
|
*num_of_active_ports = list_size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int plat_port_list_get(uint16_t list_size,
|
|
struct plat_ports_list *ports)
|
|
{
|
|
struct plat_ports_list *port_node;
|
|
struct gnma_port_key *port_key_list;
|
|
int i = 0;
|
|
int ret;
|
|
|
|
port_key_list = calloc(list_size, sizeof(*port_key_list));
|
|
if (!port_key_list)
|
|
return -ENOMEM;
|
|
|
|
ret = gnma_port_list_get(&list_size, port_key_list);
|
|
if (ret && ret != GNMA_ERR_OVERFLOW)
|
|
return ret;
|
|
|
|
UCENTRAL_LIST_FOR_EACH_MEMBER(port_node, &ports)
|
|
strcpy(port_node->name, port_key_list[i++].name);
|
|
|
|
free(port_key_list);
|
|
return 0;
|
|
}
|
|
|
|
int plat_port_stats_get(uint16_t fp_p_id, struct plat_port_counters *stats)
|
|
{
|
|
gnma_port_stat_type_t stat_types[] = {
|
|
GNMA_PORT_STAT_IN_OCTETS,
|
|
GNMA_PORT_STAT_IN_DISCARDS,
|
|
GNMA_PORT_STAT_IN_ERRORS,
|
|
GNMA_PORT_STAT_IN_BCAST_PKTS,
|
|
GNMA_PORT_STAT_IN_MCAST_PKTS,
|
|
GNMA_PORT_STAT_IN_UCAST_PKTS,
|
|
GNMA_PORT_STAT_OUT_OCTETS,
|
|
GNMA_PORT_STAT_OUT_DISCARDS,
|
|
GNMA_PORT_STAT_OUT_ERRORS,
|
|
GNMA_PORT_STAT_OUT_BCAST_PKTS,
|
|
GNMA_PORT_STAT_OUT_MCAST_PKTS,
|
|
GNMA_PORT_STAT_OUT_UCAST_PKTS
|
|
};
|
|
uint64_t counters[ARRAY_LENGTH(stat_types)];
|
|
struct gnma_port_key gnma_port;
|
|
|
|
PID_TO_NAME(fp_p_id, gnma_port.name);
|
|
|
|
if (gnma_port_stats_get(&gnma_port, ARRAY_LENGTH(stat_types),
|
|
&stat_types[0], &counters[0]))
|
|
return -EINVAL;
|
|
|
|
/* TBD: find out where to get (not present in openconfig yml)
|
|
* <collisions>, and what is <mukticast> exactly
|
|
* (rx? tx? both?? packets or octets? L2? L3?)
|
|
*/
|
|
stats->collisions = 0;
|
|
stats->multicast = 0;
|
|
|
|
stats->rx_bytes =
|
|
counters[ARR_FIND_VALUE_IDX(stat_types, ARRAY_LENGTH(stat_types),
|
|
GNMA_PORT_STAT_IN_OCTETS)];
|
|
stats->rx_dropped =
|
|
counters[ARR_FIND_VALUE_IDX(stat_types, ARRAY_LENGTH(stat_types),
|
|
GNMA_PORT_STAT_IN_DISCARDS)];
|
|
stats->rx_error =
|
|
counters[ARR_FIND_VALUE_IDX(stat_types, ARRAY_LENGTH(stat_types),
|
|
GNMA_PORT_STAT_IN_ERRORS)];
|
|
|
|
/* Packets is sum of UC + MC + BC */
|
|
stats->rx_packets =
|
|
counters[ARR_FIND_VALUE_IDX(stat_types, ARRAY_LENGTH(stat_types),
|
|
GNMA_PORT_STAT_IN_UCAST_PKTS)];
|
|
stats->rx_packets +=
|
|
counters[ARR_FIND_VALUE_IDX(stat_types, ARRAY_LENGTH(stat_types),
|
|
GNMA_PORT_STAT_IN_MCAST_PKTS)];
|
|
stats->rx_packets +=
|
|
counters[ARR_FIND_VALUE_IDX(stat_types, ARRAY_LENGTH(stat_types),
|
|
GNMA_PORT_STAT_IN_BCAST_PKTS)];
|
|
|
|
stats->tx_bytes =
|
|
counters[ARR_FIND_VALUE_IDX(stat_types, ARRAY_LENGTH(stat_types),
|
|
GNMA_PORT_STAT_OUT_OCTETS)];
|
|
stats->tx_dropped =
|
|
counters[ARR_FIND_VALUE_IDX(stat_types, ARRAY_LENGTH(stat_types),
|
|
GNMA_PORT_STAT_OUT_DISCARDS)];
|
|
stats->tx_error =
|
|
counters[ARR_FIND_VALUE_IDX(stat_types, ARRAY_LENGTH(stat_types),
|
|
GNMA_PORT_STAT_OUT_ERRORS)];
|
|
|
|
/* Packets is sum of UC + MC + BC */
|
|
stats->tx_packets =
|
|
counters[ARR_FIND_VALUE_IDX(stat_types, ARRAY_LENGTH(stat_types),
|
|
GNMA_PORT_STAT_OUT_UCAST_PKTS)];
|
|
stats->tx_packets +=
|
|
counters[ARR_FIND_VALUE_IDX(stat_types, ARRAY_LENGTH(stat_types),
|
|
GNMA_PORT_STAT_OUT_MCAST_PKTS)];
|
|
stats->tx_packets +=
|
|
counters[ARR_FIND_VALUE_IDX(stat_types, ARRAY_LENGTH(stat_types),
|
|
GNMA_PORT_STAT_OUT_BCAST_PKTS)];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
__lldp_peer_info_buf_parse(char *buf, size_t buf_size,
|
|
struct plat_port_lldp_peer_info *peer_info)
|
|
{
|
|
cJSON *system_description;
|
|
cJSON *mgmt_address;
|
|
cJSON *capabilities;
|
|
cJSON *system_name;
|
|
cJSON *capability;
|
|
cJSON *chassis_id;
|
|
cJSON *state;
|
|
cJSON *lldp;
|
|
cJSON *it;
|
|
cJSON *id;
|
|
char *p;
|
|
int ret;
|
|
int i;
|
|
|
|
lldp = cJSON_ParseWithLength(buf, buf_size);
|
|
if (!lldp)
|
|
goto err;
|
|
|
|
state = cJSON_GetObjectItemCaseSensitive(lldp, "state");
|
|
capabilities = cJSON_GetObjectItemCaseSensitive(lldp, "capabilities");
|
|
capability = cJSON_GetObjectItemCaseSensitive(capabilities, "capability");
|
|
if (!state || !capabilities || !capability || !cJSON_IsArray(capability))
|
|
goto err;
|
|
|
|
cJSON_ArrayForEach(it, capability) {
|
|
cJSON *name, *enabled;
|
|
|
|
name = cJSON_GetObjectItemCaseSensitive(it, "name");
|
|
enabled =
|
|
cJSON_GetObjectItemCaseSensitive(
|
|
cJSON_GetObjectItemCaseSensitive(it, "state"),
|
|
"enabled");
|
|
|
|
if (!name || !enabled || !cJSON_GetStringValue(name) ||
|
|
!cJSON_IsBool(enabled))
|
|
goto err;
|
|
|
|
if (!strcmp(cJSON_GetStringValue(name), "openconfig-lldp-types:MAC_BRIDGE"))
|
|
peer_info->capabilities.is_bridge = cJSON_IsTrue(enabled);
|
|
else if (!strcmp(cJSON_GetStringValue(name), "openconfig-lldp-types:ROUTER"))
|
|
peer_info->capabilities.is_router = cJSON_IsTrue(enabled);
|
|
else if (!strcmp(cJSON_GetStringValue(name), "openconfig-lldp-types:WLAN_ACCESS_POINT"))
|
|
peer_info->capabilities.is_wlan_ap = cJSON_IsTrue(enabled);
|
|
else if (!strcmp(cJSON_GetStringValue(name), "openconfig-lldp-types:STATION_ONLY"))
|
|
peer_info->capabilities.is_wlan_ap = cJSON_IsTrue(enabled);
|
|
}
|
|
|
|
system_description = cJSON_GetObjectItemCaseSensitive(state, "system-description");
|
|
mgmt_address = cJSON_GetObjectItemCaseSensitive(state, "management-address");
|
|
system_name = cJSON_GetObjectItemCaseSensitive(state, "system-name");
|
|
chassis_id = cJSON_GetObjectItemCaseSensitive(state, "chassis-id");
|
|
if (!system_description || !mgmt_address || !system_name || !chassis_id ||
|
|
!cJSON_GetStringValue(system_description) ||
|
|
!cJSON_GetStringValue(mgmt_address) ||
|
|
!cJSON_GetStringValue(system_name) ||
|
|
!cJSON_GetStringValue(chassis_id))
|
|
goto err;
|
|
|
|
strcpy(peer_info->name, cJSON_GetStringValue(system_name));
|
|
strcpy(peer_info->description,
|
|
cJSON_GetStringValue(system_description));
|
|
strcpy(peer_info->mac, cJSON_GetStringValue(chassis_id));
|
|
|
|
/* management-address meant to be an array of string, however it's
|
|
* actually one long string with ',' delims.
|
|
* In order to process it correctly do the following things:
|
|
* 1. Split string into chunk delimmed by ','/'EOF'
|
|
* 2. Try to <parse> the string into saddr to verify IP is valid.
|
|
* 3. Copy found string to string in peer_info (since step 2 didn't fail,
|
|
* IP addrs is OK and can be explicitly copied).
|
|
*/
|
|
p = strtok(cJSON_GetStringValue(mgmt_address), ",");
|
|
for (i = 0; i < UCENTRAL_PORT_LLDP_PEER_INFO_MAX_MGMT_IPS && p; ++i) {
|
|
unsigned char addr_buf[sizeof(struct in6_addr)] = {0};
|
|
|
|
ret = inet_pton(AF_INET, p, addr_buf);
|
|
if (ret) {
|
|
ret = inet_pton(AF_INET6, p, addr_buf);
|
|
if (ret)
|
|
goto err;
|
|
}
|
|
|
|
strncpy(peer_info->mgmt_ips[i], p, INET6_ADDRSTRLEN);
|
|
|
|
p = strtok(NULL, ",");
|
|
}
|
|
|
|
id = cJSON_GetObjectItemCaseSensitive(lldp, "id");
|
|
if (!id || !cJSON_GetStringValue(id))
|
|
goto err;
|
|
|
|
strcpy(peer_info->port, cJSON_GetStringValue(id));
|
|
|
|
cJSON_Delete(lldp);
|
|
return 0;
|
|
|
|
err:
|
|
cJSON_Delete(lldp);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
__ieee8021x_auth_clients_parse(uint16_t port_id,
|
|
char *buf, size_t buf_size,
|
|
struct plat_ieee8021x_port_info *state)
|
|
{
|
|
cJSON *root, *clients, *it, *cl_state, *name, *auth_clients;
|
|
struct gnma_port_key gnma_port = {0};
|
|
size_t arr_len = 0;
|
|
int i = 0;
|
|
|
|
PID_TO_NAME(port_id, gnma_port.name);
|
|
|
|
root = cJSON_ParseWithLength(buf, buf_size);
|
|
if (!root)
|
|
goto err;
|
|
|
|
auth_clients = cJSON_GetObjectItemCaseSensitive(root,
|
|
"openconfig-authmgr:authmgr-authenticated-clients");
|
|
clients = cJSON_GetObjectItemCaseSensitive(auth_clients, "authenticated-client");
|
|
|
|
cJSON_ArrayForEach(it, clients) {
|
|
cl_state = cJSON_GetObjectItemCaseSensitive(it, "state");
|
|
name = cJSON_GetObjectItemCaseSensitive(cl_state, "name");
|
|
if (!cJSON_GetStringValue(name)) {
|
|
UC_LOG_ERR("cJSON_GetStringValue(%s) fail %s\n", gnma_port.name, cJSON_GetStringValue(name));
|
|
goto err;
|
|
}
|
|
|
|
/* We're iterating over every client;
|
|
* Count only if it's client of current port.
|
|
*/
|
|
if (strcmp(gnma_port.name, cJSON_GetStringValue(name)) == 0)
|
|
arr_len++;
|
|
}
|
|
|
|
if (!arr_len)
|
|
goto err;
|
|
|
|
state->client_arr = calloc(arr_len, sizeof(*state->client_arr) * arr_len);
|
|
if (!state->client_arr) {
|
|
UC_LOG_ERR("calloc is zero\n");
|
|
goto err;
|
|
}
|
|
|
|
state->arr_len = arr_len;
|
|
|
|
cJSON_ArrayForEach(it, clients) {
|
|
cJSON *auth_method, *mac, *session_time, *username, *vlan_type, *vid;
|
|
struct plat_ieee8021x_authenticated_client_info *info;
|
|
|
|
name = cJSON_GetObjectItemCaseSensitive(it, "name");
|
|
if (!cJSON_GetStringValue(name))
|
|
goto err;
|
|
|
|
/* We're iterating over every client;
|
|
* Skip clients of other ports.
|
|
*/
|
|
if (strcmp(gnma_port.name, cJSON_GetStringValue(name)) != 0)
|
|
continue;
|
|
|
|
cl_state = cJSON_GetObjectItemCaseSensitive(it, "state");
|
|
|
|
info = &state->client_arr[i];
|
|
|
|
auth_method = cJSON_GetObjectItemCaseSensitive(cl_state, "authenticated-method");
|
|
mac = cJSON_GetObjectItemCaseSensitive(cl_state, "macaddress");
|
|
session_time = cJSON_GetObjectItemCaseSensitive(cl_state, "session-time");
|
|
username = cJSON_GetObjectItemCaseSensitive(cl_state, "user-name");
|
|
vlan_type = cJSON_GetObjectItemCaseSensitive(cl_state, "vlan-type");
|
|
vid = cJSON_GetObjectItemCaseSensitive(cl_state, "vlan-id");
|
|
|
|
if (!cJSON_GetStringValue(auth_method))
|
|
goto err;
|
|
|
|
strncpy(info->auth_method, cJSON_GetStringValue(auth_method),
|
|
sizeof(info->auth_method) - 1);
|
|
|
|
if (!cJSON_GetStringValue(mac))
|
|
goto err;
|
|
|
|
strncpy(info->mac_addr, cJSON_GetStringValue(mac),
|
|
sizeof(info->mac_addr) - 1);
|
|
|
|
info->session_time = (size_t)cJSON_GetNumberValue(session_time);
|
|
|
|
if (!cJSON_GetStringValue(username))
|
|
goto err;
|
|
|
|
strncpy(info->username, cJSON_GetStringValue(username),
|
|
sizeof(info->username) - 1);
|
|
|
|
if (!cJSON_GetStringValue(vlan_type))
|
|
goto err;
|
|
|
|
strncpy(info->vlan_type, cJSON_GetStringValue(vlan_type),
|
|
sizeof(info->vlan_type) - 1);
|
|
|
|
info->vid = (uint16_t)cJSON_GetNumberValue(vid);
|
|
|
|
++i;
|
|
}
|
|
|
|
cJSON_Delete(root);
|
|
return 0;
|
|
err:
|
|
cJSON_Delete(root);
|
|
free(state->client_arr);
|
|
state->client_arr = NULL;
|
|
state->arr_len = 0;
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
plat_ieee8021x_system_auth_clients_get(uint16_t port_id,
|
|
char **buf, size_t *buf_size,
|
|
struct plat_ieee8021x_port_info *state)
|
|
{
|
|
int ret;
|
|
|
|
if (!*buf) {
|
|
*buf = calloc(1, PLAT_IEEE8021X_AUT_CLIENTS_BUF_SIZE);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
*buf_size = PLAT_IEEE8021X_AUT_CLIENTS_BUF_SIZE;
|
|
|
|
ret = gnma_ieee8021x_system_auth_clients_get(*buf, *buf_size);
|
|
if (ret) {
|
|
free(*buf);
|
|
*buf = NULL;
|
|
*buf_size = 0;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return __ieee8021x_auth_clients_parse(port_id, *buf, *buf_size, state);
|
|
}
|
|
|
|
int plat_port_lldp_peer_info_get(uint16_t fp_p_id,
|
|
struct plat_port_lldp_peer_info *peer_info)
|
|
{
|
|
struct gnma_port_key gnma_port = {0};
|
|
const size_t buf_size = 4096;
|
|
char *buf;
|
|
int ret;
|
|
|
|
PID_TO_NAME(fp_p_id, gnma_port.name);
|
|
|
|
buf = calloc(1, buf_size);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
ret = gnma_port_lldp_peer_info_get(&gnma_port, buf, buf_size);
|
|
if (ret)
|
|
goto err;
|
|
|
|
ret = __lldp_peer_info_buf_parse(buf, buf_size, peer_info);
|
|
|
|
err:
|
|
free(buf);
|
|
return ret;
|
|
}
|
|
|
|
int plat_port_transceiver_info_get(uint16_t port_id,
|
|
struct plat_port_transceiver_info *info)
|
|
{
|
|
/* TODO */
|
|
(void)((port_id));
|
|
(void)((info));
|
|
return -1;
|
|
}
|
|
|
|
static int plat_vlan_igmp_info_get(uint16_t vid, struct plat_igmp *info)
|
|
{
|
|
size_t list_size = 0, group_idx = 0;
|
|
struct plat_ports_list *port_node;
|
|
struct gnma_port_key iface = {0};
|
|
cJSON *groups = NULL, *group;
|
|
size_t buf_size = 0;
|
|
char *buf = NULL;
|
|
int ret = 0;
|
|
|
|
VLAN_TO_NAME(vid, iface.name);
|
|
ret = gnma_igmp_iface_groups_get(&iface, NULL, &buf_size);
|
|
if (ret == GNMA_OK && !buf_size)
|
|
return 0;
|
|
if (ret != GNMA_ERR_OVERFLOW)
|
|
return -1;
|
|
|
|
buf = calloc(buf_size, sizeof(*buf));
|
|
if (!buf)
|
|
return -1;
|
|
|
|
ret = gnma_igmp_iface_groups_get(&iface, buf, &buf_size);
|
|
if (ret != GNMA_OK) {
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
|
|
groups = cJSON_Parse(buf);
|
|
if (!groups) {
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
|
|
list_size = cJSON_GetArraySize(groups);
|
|
info->groups = calloc(list_size, sizeof(*info->groups));
|
|
if (!info->groups) {
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
info->num_groups = list_size;
|
|
|
|
cJSON_ArrayForEach(group, groups) {
|
|
cJSON *state, *gaddr, *e_ifaces, *e_iface;
|
|
|
|
state = cJSON_GetObjectItemCaseSensitive(group, "state");
|
|
if (!state || !cJSON_IsObject(state)) {
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
|
|
gaddr = cJSON_GetObjectItemCaseSensitive(state, "group");
|
|
if (!gaddr || !cJSON_GetStringValue(gaddr)) {
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
|
|
e_ifaces = cJSON_GetObjectItemCaseSensitive(state, "outgoing-interface");
|
|
if (!e_ifaces || !cJSON_IsArray(e_ifaces)) {
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
|
|
if (inet_pton(AF_INET, cJSON_GetStringValue(gaddr),
|
|
&info->groups[group_idx].addr) != 1) {
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
|
|
cJSON_ArrayForEach(e_iface, e_ifaces) {
|
|
if (!cJSON_GetStringValue(e_iface)) {
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
|
|
port_node = calloc(1, sizeof(*port_node));
|
|
if (!port_node) {
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
|
|
strncpy(port_node->name,
|
|
cJSON_GetStringValue(e_iface),
|
|
sizeof(port_node->name));
|
|
UCENTRAL_LIST_PUSH_MEMBER(
|
|
&info->groups[group_idx].egress_ports_list,
|
|
port_node);
|
|
}
|
|
group_idx++;
|
|
}
|
|
|
|
info->exist = true;
|
|
goto exit;
|
|
|
|
err:
|
|
if (info->groups) {
|
|
for (size_t i = 0; i < info->num_groups; ++i) {
|
|
UCENTRAL_LIST_DESTROY_SAFE(
|
|
&info->groups[i].egress_ports_list,
|
|
port_node);
|
|
}
|
|
}
|
|
free(info->groups);
|
|
exit:
|
|
cJSON_Delete(groups);
|
|
free(buf);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
__poe_port_state_buf_parse(char *buf, size_t buf_size,
|
|
struct plat_poe_port_state *port_state)
|
|
{
|
|
cJSON *class_requested;
|
|
cJSON *class_assigned;
|
|
cJSON *output_current;
|
|
cJSON *output_voltage;
|
|
cJSON *output_power;
|
|
cJSON *fault_status;
|
|
cJSON *temperature;
|
|
cJSON *counters;
|
|
cJSON *state;
|
|
cJSON *status;
|
|
|
|
state = cJSON_ParseWithLength(buf, buf_size);
|
|
if (!state)
|
|
goto err;
|
|
|
|
counters = cJSON_GetObjectItemCaseSensitive(state, "openconfig-if-poe-ext:counters");
|
|
if (!counters || !cJSON_IsObject(counters))
|
|
goto err;
|
|
|
|
port_state->counters.absent =
|
|
cJSON_GetNumberValue(cJSON_GetObjectItemCaseSensitive(counters, "absent-counter"));
|
|
port_state->counters.shorted =
|
|
cJSON_GetNumberValue(cJSON_GetObjectItemCaseSensitive(counters, "short-counter"));
|
|
port_state->counters.overload =
|
|
cJSON_GetNumberValue(cJSON_GetObjectItemCaseSensitive(counters, "overload-counter"));
|
|
port_state->counters.power_denied =
|
|
cJSON_GetNumberValue(cJSON_GetObjectItemCaseSensitive(counters, "power-denied-counter"));
|
|
port_state->counters.invalid_signature =
|
|
cJSON_GetNumberValue(cJSON_GetObjectItemCaseSensitive(counters, "invalid-signature-counter"));
|
|
|
|
class_requested =
|
|
cJSON_GetObjectItemCaseSensitive(state, "openconfig-if-poe-ext:power-class-requested");
|
|
class_assigned =
|
|
cJSON_GetObjectItemCaseSensitive(state, "power-class");
|
|
/* It's okay if these are NULL, means PSE has no Powered Device
|
|
* (or link is down).
|
|
*/
|
|
if (!class_requested || !cJSON_IsNumber(class_requested))
|
|
port_state->class_requested = 0;
|
|
else
|
|
port_state->class_requested = cJSON_GetNumberValue(class_requested);
|
|
|
|
if (!class_assigned || !cJSON_IsNumber(class_assigned))
|
|
port_state->class_assigned = 0;
|
|
else
|
|
port_state->class_assigned = cJSON_GetNumberValue(class_assigned);
|
|
|
|
output_power =
|
|
cJSON_GetObjectItemCaseSensitive(state, "power-used");
|
|
output_current =
|
|
cJSON_GetObjectItemCaseSensitive(state, "openconfig-if-poe-ext:output-current");
|
|
output_voltage =
|
|
cJSON_GetObjectItemCaseSensitive(state, "openconfig-if-poe-ext:output-voltage");
|
|
if (!cJSON_GetStringValue(output_power) ||
|
|
!cJSON_IsNumber(output_current) ||
|
|
!cJSON_GetStringValue(output_voltage))
|
|
goto err;
|
|
|
|
if (1 != sscanf(cJSON_GetStringValue(output_power),
|
|
"%" SCNu32,
|
|
&port_state->output_power))
|
|
goto err;
|
|
port_state->output_current = cJSON_GetNumberValue(output_current);
|
|
strcpy(port_state->output_voltage,
|
|
cJSON_GetStringValue(output_voltage));
|
|
|
|
temperature =
|
|
cJSON_GetObjectItemCaseSensitive(cJSON_GetObjectItemCaseSensitive(state, "openconfig-if-poe-ext:diagnostics"),
|
|
"temperature");
|
|
if (!cJSON_GetStringValue(temperature))
|
|
goto err;
|
|
|
|
strcpy(port_state->temperature, cJSON_GetStringValue(temperature));
|
|
|
|
status = cJSON_GetObjectItemCaseSensitive(state, "openconfig-if-poe-ext:status");
|
|
fault_status = cJSON_GetObjectItemCaseSensitive(state, "openconfig-if-poe-ext:fault-code");
|
|
if (!cJSON_GetStringValue(status) || !cJSON_GetStringValue(fault_status))
|
|
goto err;
|
|
|
|
strcpy(port_state->status, cJSON_GetStringValue(status));
|
|
strcpy(port_state->fault_status, cJSON_GetStringValue(fault_status));
|
|
|
|
cJSON_Delete(state);
|
|
return 0;
|
|
|
|
err:
|
|
cJSON_Delete(state);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
plat_poe_port_state_get(uint16_t pid,
|
|
struct plat_poe_port_state *state)
|
|
{
|
|
struct gnma_port_key gnma_port = {0};
|
|
const size_t buf_size = 4096;
|
|
char *buf;
|
|
int ret;
|
|
|
|
PID_TO_NAME(pid, gnma_port.name);
|
|
|
|
buf = calloc(1, buf_size);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
ret = gnma_poe_port_state_get(&gnma_port, buf, buf_size);
|
|
if (ret)
|
|
goto err;
|
|
|
|
ret = __poe_port_state_buf_parse(buf, buf_size, state);
|
|
|
|
err:
|
|
free(buf);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
__poe_state_buf_parse(char *buf, size_t buf_size,
|
|
struct plat_poe_state *poe_state)
|
|
{
|
|
cJSON *status;
|
|
cJSON *state;
|
|
cJSON *tmp;
|
|
|
|
state = cJSON_ParseWithLength(buf, buf_size);
|
|
if (!state)
|
|
goto err;
|
|
|
|
poe_state->max_power_budget =
|
|
cJSON_GetNumberValue(cJSON_GetObjectItemCaseSensitive(state, "max-power-budget"));
|
|
|
|
/* For some reason, new BRCM images report this value as string, not value... */
|
|
tmp = cJSON_GetObjectItemCaseSensitive(state, "power-threshold");
|
|
if (!tmp || !cJSON_GetStringValue(tmp))
|
|
goto err;
|
|
poe_state->power_threshold =
|
|
(typeof(poe_state->power_threshold)) strtod(cJSON_GetStringValue(tmp), NULL);
|
|
|
|
tmp = cJSON_GetObjectItemCaseSensitive(state, "power-consumption");
|
|
if (!tmp || !cJSON_GetStringValue(tmp))
|
|
goto err;
|
|
poe_state->power_consumed =
|
|
(typeof(poe_state->power_consumed)) strtod(cJSON_GetStringValue(tmp), NULL);
|
|
|
|
status = cJSON_GetObjectItemCaseSensitive(state, "pse-oper-status");
|
|
if (!cJSON_GetStringValue(status))
|
|
goto err;
|
|
|
|
strcpy(poe_state->power_status, cJSON_GetStringValue(status));
|
|
|
|
|
|
cJSON_Delete(state);
|
|
return 0;
|
|
|
|
err:
|
|
cJSON_Delete(state);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
plat_poe_state_get(struct plat_poe_state *state)
|
|
{
|
|
const size_t buf_size = 4096;
|
|
char *buf;
|
|
int ret;
|
|
|
|
buf = calloc(1, buf_size);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
ret = gnma_poe_state_get(buf, buf_size);
|
|
if (ret)
|
|
goto err;
|
|
|
|
ret = __poe_state_buf_parse(buf, buf_size, state);
|
|
|
|
err:
|
|
free(buf);
|
|
return ret;
|
|
}
|
|
|
|
/* NOTE: In case of error this function left partial config */
|
|
int plat_vlan_list_set(BITMAP_DECLARE(vlans_to_cfg, GNMA_MAX_VLANS))
|
|
{
|
|
BITMAP_DECLARE(vlans, GNMA_MAX_VLANS);
|
|
uint16_t vid;
|
|
size_t i;
|
|
struct plat_ipv4 ipv4 = {.exist = false};
|
|
int ret = 0;
|
|
|
|
BITMAP_CLEAR(vlans, GNMA_MAX_VLANS);
|
|
ret = gnma_vlan_list_get(vlans);
|
|
if (ret)
|
|
goto err;
|
|
|
|
BITMAP_FOR_EACH_BIT_SET(i, vlans, GNMA_MAX_VLANS)
|
|
{
|
|
vid = (uint16_t)i;
|
|
if (BITMAP_TEST_BIT(vlans_to_cfg, vid))
|
|
continue;
|
|
UC_LOG_DBG("Removing vid <%" PRIu16
|
|
"> (not in cfg, present on system)\n",
|
|
vid);
|
|
|
|
/* TODO should it be part of gnma vlan_del ? */
|
|
/* Let it fail,
|
|
* to prevent additional rif existence check req
|
|
*/
|
|
ret = plat_vlan_rif_set(vid, &ipv4);
|
|
if (ret)
|
|
goto err;
|
|
ret = gnma_vlan_remove(vid);
|
|
if (ret)
|
|
goto err;
|
|
}
|
|
|
|
/* We skip creating (for every all_vlans_arr), because it created
|
|
* imlicity by plat_vlan_memberlist_set
|
|
*/
|
|
err:
|
|
return ret;
|
|
}
|
|
|
|
int plat_vlan_memberlist_set(struct gnma_change *c,
|
|
struct gnma_vlan_member_bmap *vlan_mbr,
|
|
struct plat_port_vlan *vlan)
|
|
{
|
|
BITMAP_DECLARE(vlan_port_member, MAX_NUM_OF_PORTS);
|
|
BITMAP_DECLARE(vlan_port_tagged, MAX_NUM_OF_PORTS);
|
|
uint16_t pid;
|
|
size_t i;
|
|
struct gnma_port_key gnma_port;
|
|
struct plat_vlan_memberlist *pv;
|
|
uint32_t tagged;
|
|
int ret = 0;
|
|
|
|
ret = gnma_vlan_create(c, vlan->id);
|
|
if (ret)
|
|
return -1;
|
|
|
|
BITMAP_CLEAR(vlan_port_member, MAX_NUM_OF_PORTS);
|
|
BITMAP_CLEAR(vlan_port_tagged, MAX_NUM_OF_PORTS);
|
|
for (pv = vlan->members_list_head; pv; pv = pv->next) {
|
|
if (NAME_TO_PID(&pid, pv->port.name) < 1) {
|
|
UC_LOG_ERR("Invalid port name: '%s'", pv->port.name);
|
|
return -1;
|
|
}
|
|
BITMAP_SET_BIT(vlan_port_member, pid);
|
|
if (pv->tagged)
|
|
BITMAP_SET_BIT(vlan_port_tagged, pid);
|
|
}
|
|
|
|
/* Delete already configured members that are not present in the new config */
|
|
BITMAP_FOR_EACH_BIT_SET(i, vlan_mbr->vlan[vlan->id].port_member,
|
|
MAX_NUM_OF_PORTS)
|
|
{
|
|
if (!BITMAP_TEST_BIT(vlan_port_member, i)) {
|
|
PID_TO_NAME((uint16_t)i, gnma_port.name);
|
|
UC_LOG_DBG("Removing vlan <%u>: member <%s>\n",
|
|
vlan->id, gnma_port.name);
|
|
gnma_vlan_member_remove(c, vlan->id, &gnma_port);
|
|
}
|
|
}
|
|
|
|
/* Configure vlan members from the new config */
|
|
BITMAP_FOR_EACH_BIT_SET(i, vlan_port_member, MAX_NUM_OF_PORTS)
|
|
{
|
|
tagged = BITMAP_TEST_BIT(vlan_port_tagged, i);
|
|
|
|
if (BITMAP_TEST_BIT(vlan_mbr->vlan[vlan->id].port_member, i) &&
|
|
tagged ==
|
|
BITMAP_TEST_BIT(
|
|
vlan_mbr->vlan[vlan->id].port_tagged, i)) {
|
|
continue;
|
|
}
|
|
|
|
PID_TO_NAME((uint16_t)i, gnma_port.name);
|
|
UC_LOG_DBG(
|
|
"Configuring vlan <%u>: member <%s>, fp_id <%zu>, tagged <%d>\n",
|
|
vlan->id, gnma_port.name, i, tagged);
|
|
ret = gnma_vlan_member_create(c, vlan->id, &gnma_port, tagged);
|
|
if (ret) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int plat_reboot(void)
|
|
{
|
|
return gnma_reboot();
|
|
}
|
|
|
|
static int __plat_config_id_store(uint64_t id)
|
|
{
|
|
FILE *cfgid_file = NULL;
|
|
int err = 0;
|
|
int ret;
|
|
|
|
cfgid_file = fopen(cfgid_path, "w");
|
|
if (!cfgid_file) {
|
|
err = -1;
|
|
goto out;
|
|
}
|
|
|
|
ret = fprintf(cfgid_file, "%lu", id);
|
|
if (ret < 0) {
|
|
err = -1;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
if (cfgid_file)
|
|
fclose(cfgid_file);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int __plat_config_id_load(uint64_t *id)
|
|
{
|
|
FILE *cfgid_file = NULL;
|
|
int err = 0;
|
|
int ret;
|
|
|
|
*id = 0;
|
|
|
|
cfgid_file = fopen(cfgid_path, "r");
|
|
if (!cfgid_file) {
|
|
err = -1;
|
|
goto out;
|
|
}
|
|
|
|
ret = fscanf(cfgid_file, "%lu", id);
|
|
if (ret == EOF || !ret) {
|
|
err = -1;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
if (cfgid_file)
|
|
fclose(cfgid_file);
|
|
|
|
return err;
|
|
}
|
|
|
|
/* Save applied config to be restored after reboot */
|
|
int plat_config_save(uint64_t id)
|
|
{
|
|
int err;
|
|
|
|
err = gnma_config_save();
|
|
if (err)
|
|
return err;
|
|
|
|
__plat_config_id_store(id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int plat_config_restore(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = gnma_config_restore();
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Restore internal state/cache of plat objects. */
|
|
return plat_state_init();
|
|
}
|
|
|
|
/* Get id of saved config.
|
|
* Describe, what will be restored during plat_config_restore()
|
|
*/
|
|
/* NOTE: gnma is not supported such things as config id. So, we can add logic
|
|
* to read store config id in plat layer, as defined by design.
|
|
* This layer knows about /var/run directory as well as about gnma restrictions
|
|
*/
|
|
int plat_saved_config_id_get(uint64_t *id)
|
|
{
|
|
return __plat_config_id_load(id);
|
|
}
|
|
|
|
int plat_factory_default(void)
|
|
{
|
|
return gnma_factory_default();
|
|
}
|
|
|
|
int plat_rtty(struct plat_rtty_cfg *rtty_cfg)
|
|
{
|
|
static pid_t child[RTTY_SESS_MAX];
|
|
int n, i, e;
|
|
|
|
/* wait the dead children */
|
|
for (i = 0; i < RTTY_SESS_MAX;) {
|
|
n = 0;
|
|
if (child[i] > 0) {
|
|
while ((n = waitpid(child[i], 0, WNOHANG)) < 0 && errno == EINTR);
|
|
}
|
|
if (n <= 0) {
|
|
++i;
|
|
} else {
|
|
if (RTTY_SESS_MAX > 1)
|
|
memmove(&child[i], &child[i+1], (RTTY_SESS_MAX-i-1)*sizeof(pid_t));
|
|
child[RTTY_SESS_MAX - 1] = -1;
|
|
}
|
|
}
|
|
|
|
/* find a place for a new session */
|
|
for (i = 0; i < RTTY_SESS_MAX && child[i] > 0; ++i);
|
|
|
|
/* if there are RTTY_SESS_MAX sessions, kill the oldest */
|
|
if (i == RTTY_SESS_MAX) {
|
|
if (child[0] <= 0) {
|
|
UC_LOG_CRIT("child[0]==%jd", (intmax_t)child[0]);
|
|
} else {
|
|
if (kill(child[0], SIGKILL)) {
|
|
UC_LOG_CRIT("kill failed: %s", strerror(errno));
|
|
} else {
|
|
while ((n = waitpid(child[0], 0, 0)) < 0 && errno == EINTR);
|
|
if (n < 0)
|
|
UC_LOG_CRIT("waitpid failed: %s", strerror(errno));
|
|
}
|
|
if (RTTY_SESS_MAX > 1)
|
|
memmove(&child[0], &child[1], (RTTY_SESS_MAX - 1) * sizeof(pid_t));
|
|
}
|
|
i = RTTY_SESS_MAX - 1;
|
|
}
|
|
child[i] = fork();
|
|
|
|
if (!child[i]) {
|
|
char argv[][128] = {
|
|
"--id=",
|
|
"--host=",
|
|
"--port=",
|
|
"--token="
|
|
};
|
|
|
|
setsid();
|
|
strcat(argv[0], rtty_cfg->id);
|
|
strcat(argv[1], rtty_cfg->server);
|
|
sprintf(argv[2], "--port=%u", rtty_cfg->port);
|
|
strcat(argv[3], rtty_cfg->token);
|
|
execl("/usr/local/bin/rtty", "rtty", argv[0], argv[1], argv[2], argv[3], "-d Edgecore Switch device", "-v", "-s", NULL);
|
|
e = errno;
|
|
UC_LOG_DBG("execv failed %d\n", e);
|
|
|
|
/* If we got to this line, that means execl failed, and
|
|
* currently, due to simple design (fork/exec), it's impossible
|
|
* to notify <main> process, that forked child failed to execl.
|
|
* TBD: notify about execl fail.
|
|
*/
|
|
_exit(e);
|
|
}
|
|
|
|
if (child[i] < (pid_t)0) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int plat_info_get(struct plat_platform_info *info)
|
|
{
|
|
struct gnma_metadata md = {0};
|
|
|
|
if (gnma_metadata_get(&md)) {
|
|
return -1;
|
|
}
|
|
|
|
*info = (struct plat_platform_info){0};
|
|
snprintf(info->platform, sizeof info->platform, "%s", md.platform);
|
|
snprintf(info->hwsku, sizeof info->hwsku, "%s", md.hwsku);
|
|
snprintf(info->mac, sizeof info->mac, "%s", md.mac);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
img_dl_progress_cb(void *ptr, double dltotal, double dlnow, double ultotal,
|
|
double ulnow)
|
|
{
|
|
struct img_dl_task *tsk = (struct img_dl_task *)ptr;
|
|
|
|
(void)(ultotal);
|
|
(void)(ulnow);
|
|
|
|
tsk->percentage = (uint8_t) (dlnow / dltotal * 100);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int img_dl_get_type(struct img_dl_task *tsk)
|
|
{
|
|
const size_t len = sizeof IMG_DL_DEB_FILE_SIGNATURE - 1;
|
|
void *buf;
|
|
int ret;
|
|
int fd;
|
|
|
|
fd = open(IMG_DL_FILE_PATH, O_RDONLY);
|
|
if (fd < 0) {
|
|
UC_LOG_ERR("IMG dl completed, but couldn't open it for type detection");
|
|
return -1;
|
|
}
|
|
|
|
/* All we care is whether it's a deb or not, hence mmap only signature
|
|
* length.
|
|
*/
|
|
buf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
if (buf == MAP_FAILED) {
|
|
UC_LOG_ERR("IMG dl completed, but couldn't open it for type detection");
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
|
|
tsk->downloaded_full_img =
|
|
(bool)memcmp(buf, IMG_DL_DEB_FILE_SIGNATURE, len);
|
|
ret = 0;
|
|
|
|
UC_LOG_DBG("Downloaded img (is_full_img %d)", tsk->downloaded_full_img);
|
|
UC_LOG_DBG("Img hdr (64): %" PRIu64, *(uint64_t *)buf);
|
|
|
|
err:
|
|
if (buf)
|
|
munmap(buf, len);
|
|
close(fd);
|
|
return ret;
|
|
}
|
|
|
|
static int img_dl_start_partial_upgrade(char *uri)
|
|
{
|
|
char buf[256];
|
|
char *bufpt;
|
|
size_t size;
|
|
int ret;
|
|
int fd;
|
|
|
|
strcpy(buf, "upgrade ");
|
|
strcat(buf, uri);
|
|
|
|
bufpt = buf;
|
|
|
|
size = strlen(buf);
|
|
|
|
fd = open(IMG_DL_PIPE_PATH, O_RDWR);
|
|
if (fd < 0) {
|
|
UC_LOG_ERR("partial upgrade pipe open failed");
|
|
return -1;
|
|
}
|
|
|
|
UC_LOG_DBG("Issuing upgrade cmd: \'%s\'", buf);
|
|
|
|
/* Send 'upgrade' cmd to script through pipe;
|
|
* Cover partial-write case.
|
|
* TBD: verify script can actually install this package
|
|
* (e.g. integrity kept intact, checksums are fine etc).
|
|
*/
|
|
while (size != 0) {
|
|
ret = write(fd, bufpt, size);
|
|
if (ret < 0) {
|
|
UC_LOG_ERR("Write to upgrade pipe failed");
|
|
goto err;
|
|
}
|
|
|
|
bufpt += ret;
|
|
size -= ret;
|
|
}
|
|
|
|
close(fd);
|
|
return 0;
|
|
err:
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
static void *img_dl_entrypoint(void *args)
|
|
{
|
|
struct img_dl_task *tsk = (struct img_dl_task *)args;
|
|
CURLcode res;
|
|
|
|
res = curl_easy_perform(tsk->curl);
|
|
|
|
fclose(tsk->fp);
|
|
curl_easy_cleanup(tsk->curl);
|
|
|
|
if (res != CURLE_OK || img_dl_get_type(tsk)) {
|
|
goto err;
|
|
}
|
|
|
|
if (!tsk->downloaded_full_img) {
|
|
if (img_dl_start_partial_upgrade(IMG_DL_FILE_PATH))
|
|
goto err;
|
|
tsk->upgrade_state = UCENTRAL_UPGRADE_STATE_SUCCESS;
|
|
} else {
|
|
char f_uri[256];
|
|
|
|
strcpy(f_uri, "file://");
|
|
strcat(f_uri, IMG_DL_FILE_PATH);
|
|
if (gnma_image_install(f_uri))
|
|
goto err;
|
|
tsk->upgrade_state = UCENTRAL_UPGRADE_STATE_INSTALL;
|
|
tsk->percentage = 0;
|
|
tsk->active = false;
|
|
}
|
|
|
|
return 0;
|
|
err:
|
|
tsk->upgrade_state = UCENTRAL_UPGRADE_STATE_FAIL;
|
|
tsk->percentage = 0;
|
|
UC_LOG_ERR("IMG DL fail or failed to define downloaded img type");
|
|
return 0;
|
|
}
|
|
|
|
static int plat_img_dl_start(char *uri, char *signature)
|
|
{
|
|
/* TODO */
|
|
if (signature)
|
|
UC_LOG_DBG("Signature check is not implemented!\n");
|
|
|
|
memset(&img_dl_task, 0, sizeof(img_dl_task));
|
|
|
|
img_dl_task.upgrade_state = UCENTRAL_UPGRADE_STATE_DOWNLOAD;
|
|
img_dl_task.active = true;
|
|
|
|
img_dl_task.fp = fopen(IMG_DL_FILE_PATH, "w+");
|
|
if (!img_dl_task.fp) {
|
|
UC_LOG_ERR("Failed to open '%s' for img dl!", IMG_DL_FILE_PATH);
|
|
return -1;
|
|
}
|
|
|
|
img_dl_task.curl = curl_easy_init();
|
|
if (!img_dl_task.curl) {
|
|
UC_LOG_ERR("curl_easy_init failed!");
|
|
goto err;
|
|
}
|
|
|
|
curl_easy_setopt(img_dl_task.curl, CURLOPT_URL, uri);
|
|
curl_easy_setopt(img_dl_task.curl, CURLOPT_NOPROGRESS, 0);
|
|
curl_easy_setopt(img_dl_task.curl, CURLOPT_PROGRESSFUNCTION, img_dl_progress_cb);
|
|
curl_easy_setopt(img_dl_task.curl, CURLOPT_PROGRESSDATA, &img_dl_task);
|
|
curl_easy_setopt(img_dl_task.curl, CURLOPT_WRITEDATA, (void *)img_dl_task.fp);
|
|
|
|
if (pthread_create(&img_dl_task.t, 0, img_dl_entrypoint, &img_dl_task)) {
|
|
UC_LOG_DBG("pthread_create failed: %s", strerror(errno));
|
|
goto err;
|
|
}
|
|
|
|
/* thread will cleanup everything by itself. Clean only in case of
|
|
* err here.
|
|
*/
|
|
return 0;
|
|
|
|
err:
|
|
if (img_dl_task.curl)
|
|
curl_easy_cleanup(img_dl_task.curl);
|
|
if (img_dl_task.fp)
|
|
fclose(img_dl_task.fp);
|
|
|
|
return -1;
|
|
}
|
|
|
|
int plat_upgrade(char *uri, char *signature)
|
|
{
|
|
/* TODO */
|
|
if (signature)
|
|
UC_LOG_DBG("Signature check is not implemented!\n");
|
|
|
|
return plat_img_dl_start(uri, signature);
|
|
}
|
|
|
|
static int __plat_upgrade_state_gnmi(int *operation, int *percentage)
|
|
{
|
|
char buf[256];
|
|
uint16_t buf_size = sizeof(buf);
|
|
cJSON *json, *json_item;
|
|
char *state_name;
|
|
int parsed_percentage;
|
|
|
|
if (gnma_image_install_status(&buf_size, &buf[0]))
|
|
return -1;
|
|
|
|
json = cJSON_Parse(buf);
|
|
json_item = cJSON_GetObjectItemCaseSensitive(json, "global_state");
|
|
if (!json_item)
|
|
goto err_parse;
|
|
state_name = cJSON_GetStringValue(json_item);
|
|
|
|
json_item = cJSON_GetObjectItemCaseSensitive(json, "percentage");
|
|
if (!json_item)
|
|
goto err_parse;
|
|
parsed_percentage = cJSON_GetNumberValue(json_item);
|
|
|
|
*operation = UCENTRAL_UPGRADE_STATE_IDLE;
|
|
*percentage = 0;
|
|
if (!strcmp(state_name, "GLOBAL_STATE_FAILED")) {
|
|
*operation = UCENTRAL_UPGRADE_STATE_FAIL;
|
|
} else if (!strcmp(state_name, "GLOBAL_STATE_DOWNLOAD")) {
|
|
*operation = UCENTRAL_UPGRADE_STATE_DOWNLOAD;
|
|
*percentage = parsed_percentage;
|
|
} else if (!strcmp(state_name, "GLOBAL_STATE_INSTALL")) {
|
|
*operation = UCENTRAL_UPGRADE_STATE_INSTALL;
|
|
} else if (!strcmp(state_name, "GLOBAL_STATE_SUCCESS")) {
|
|
*operation = UCENTRAL_UPGRADE_STATE_SUCCESS;
|
|
}
|
|
|
|
cJSON_Delete(json);
|
|
return 0;
|
|
|
|
err_parse:
|
|
/* TODO single retpath */
|
|
cJSON_Delete(json);
|
|
return -1;
|
|
}
|
|
|
|
static int plat_upgrade_state(int *operation, int *percentage)
|
|
{
|
|
/* */
|
|
if (img_dl_task.active) {
|
|
*percentage = img_dl_task.percentage;
|
|
*operation = img_dl_task.upgrade_state;
|
|
return 0;
|
|
} else
|
|
return __plat_upgrade_state_gnmi(operation, percentage);
|
|
}
|
|
|
|
int plat_running_img_name_get(char *str, size_t str_max_len)
|
|
{
|
|
return gnma_image_running_name_get(str, str_max_len);
|
|
}
|
|
|
|
int plat_revision_get(char *str, size_t str_max_len)
|
|
{
|
|
snprintf(str, str_max_len, PLATFORM_REVISION);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
__reboot_cause_buf_parse(char *buf, size_t buf_size,
|
|
struct plat_reboot_cause *cause)
|
|
{
|
|
struct sysinfo sys_info = { 0 };
|
|
time_t localtime = time(0);
|
|
cJSON *json, *json_cause;
|
|
char *cause_str;
|
|
|
|
sysinfo(&sys_info);
|
|
|
|
json = cJSON_ParseWithLength(buf, buf_size);
|
|
json_cause = cJSON_GetObjectItemCaseSensitive(json, "openconfig-system-ext:reboot-cause");
|
|
if (!(cause_str = cJSON_GetStringValue(json_cause)))
|
|
goto err_parse;
|
|
|
|
/* Some of reboot causes might not have TS set, hence calculate it
|
|
* manually: a tricky way to do so is simply substract uptime from
|
|
* localtime.
|
|
* The only downside is that TS is approximated and not exact,
|
|
* however if system gives no other option to get TS from powerloss,
|
|
* it's an OK way to calculate it this way.
|
|
*/
|
|
cause->ts = (uint64_t)localtime - (uint64_t)sys_info.uptime;
|
|
|
|
/* Upon powerloss no cause is saved, hence Unknown == powerloss.
|
|
* In case if reboot is issued, cause will be explicitly set to reboot;
|
|
* And in case of crash any other value or 'kdump issued' will be set.
|
|
*/
|
|
if (strstr(cause_str, "not yet available")) {
|
|
UC_LOG_ERR("uCentral SW failed to fetch reboot cause string");
|
|
cause->cause = PLAT_REBOOT_CAUSE_UNAVAILABLE;
|
|
strncpy(cause->desc,
|
|
"uCentral SW failed to fetch reboot cause string (not available)",
|
|
sizeof cause->desc - 1);
|
|
} else if (strstr(cause_str, "Unknown")) {
|
|
cause->cause = PLAT_REBOOT_CAUSE_POWERLOSS;
|
|
strncpy(cause->desc,
|
|
"Powerloss detected.",
|
|
sizeof cause->desc - 1);
|
|
} else if (strstr(cause_str, "reboot")) {
|
|
cause->cause = PLAT_REBOOT_CAUSE_REBOOT_CMD;
|
|
strncpy(cause->desc,
|
|
"Reboot command's been executed.",
|
|
sizeof cause->desc - 1);
|
|
} else {
|
|
strncpy(cause->desc,
|
|
"Device's (kernel) crashed (kernelpanic caused device to reboot).",
|
|
sizeof cause->desc - 1);
|
|
cause->cause = PLAT_REBOOT_CAUSE_CRASH;
|
|
}
|
|
|
|
cJSON_Delete(json);
|
|
return 0;
|
|
|
|
err_parse:
|
|
cJSON_Delete(json);
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
plat_reboot_cause_get(struct plat_reboot_cause *cause)
|
|
{
|
|
const size_t buf_size = 4096;
|
|
char *buf;
|
|
int ret;
|
|
|
|
buf = calloc(1, buf_size);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
ret = gnma_rebootcause_get(buf, buf_size);
|
|
if (ret)
|
|
goto err;
|
|
|
|
ret = __reboot_cause_buf_parse(buf, buf_size, cause);
|
|
|
|
err:
|
|
free(buf);
|
|
return ret;
|
|
}
|
|
|
|
static void alarm_gnma_cb(struct gnma_alarm *ga, void *data)
|
|
{
|
|
struct plat_event_callbacks *ctx = data;
|
|
struct plat_alarm a = {
|
|
.id = ga->id,
|
|
.resource = ga->resource,
|
|
.text = ga->text,
|
|
.time_created = ga->time_created,
|
|
.type_id = ga->type_id,
|
|
.severity = ga->severity,
|
|
.acknowledged = ga->acknowledged,
|
|
.acknowledge_time = ga->acknowledge_time,
|
|
};
|
|
|
|
if (ctx->alarm_cb) {
|
|
ctx->alarm_cb(&a);
|
|
}
|
|
}
|
|
|
|
static void linkstatus_gnma_cb(struct gnma_linkstatus *s, void *data)
|
|
{
|
|
struct plat_event_callbacks *ctx = data;
|
|
struct plat_linkstatus ucs = {
|
|
.timestamp = s->timestamp / 1000000000,
|
|
.ifname = s->ifname,
|
|
.up = s->up,
|
|
};
|
|
|
|
if (ctx->linkstatus_cb)
|
|
ctx->linkstatus_cb(&ucs);
|
|
}
|
|
|
|
static void poe_linkstatus_gnma_cb(struct gnma_poe_linkstatus *s, void *data)
|
|
{
|
|
struct plat_event_callbacks *ctx = data;
|
|
struct plat_poe_linkstatus ucs = {
|
|
.timestamp = s->timestamp / 1000000000,
|
|
.ifname = s->ifname,
|
|
};
|
|
|
|
if (!strcmp("DISABLED", s->status))
|
|
ucs.status = PLAT_POE_LINKSTATUS_DISABLED;
|
|
else if (!strcmp("SEARCHING", s->status))
|
|
ucs.status = PLAT_POE_LINKSTATUS_SEARCHING;
|
|
else if (!strcmp("DELIVERING_POWER", s->status))
|
|
ucs.status = PLAT_POE_LINKSTATUS_DELIVERING_POWER;
|
|
else if (!strcmp("OVERLOAD", s->status))
|
|
ucs.status = PLAT_POE_LINKSTATUS_OVERLOAD;
|
|
else if (!strcmp("OTHER_FAULT", s->status))
|
|
ucs.status = PLAT_POE_LINKSTATUS_FAULT;
|
|
else
|
|
return;
|
|
|
|
if (ctx->poe_linkstatus_cb)
|
|
ctx->poe_linkstatus_cb(&ucs);
|
|
}
|
|
|
|
static void poe_link_faultcode_gnma_cb(struct gnma_poe_link_faultcode *s, void *data)
|
|
{
|
|
struct plat_event_callbacks *ctx = data;
|
|
struct plat_poe_link_faultcode ucs = {
|
|
.timestamp = s->timestamp / 1000000000,
|
|
.ifname = s->ifname,
|
|
};
|
|
|
|
if (!strcmp("NO_ERROR", s->faultcode))
|
|
/* We're interested only in actual fault codes, but sonic alerts
|
|
* us also whenever fault-code is 'back-to-normal'. So we explicitly
|
|
* ignore the given NO_ERROR code.
|
|
* However, if two sequential OVERLOAD events happen, for example,
|
|
* we still send BOTH events, as these are two discrete unique
|
|
* events that happened to the device.
|
|
*/
|
|
return;
|
|
else if (!strcmp("OVLO", s->faultcode))
|
|
ucs.faultcode = PLAT_POE_LINK_FAULTCODE_OVLO;
|
|
else if (!strcmp("MPS_ABSENT", s->faultcode))
|
|
ucs.faultcode = PLAT_POE_LINK_FAULTCODE_MPS_ABSENT;
|
|
else if (!strcmp("SHORT", s->faultcode))
|
|
ucs.faultcode = PLAT_POE_LINK_FAULTCODE_SHORT;
|
|
else if (!strcmp("OVERLOAD", s->faultcode))
|
|
ucs.faultcode = PLAT_POE_LINK_FAULTCODE_OVERLOAD;
|
|
else if (!strcmp("POWER_DENIED", s->faultcode))
|
|
ucs.faultcode = PLAT_POE_LINK_FAULTCODE_POWER_DENIED;
|
|
else if (!strcmp("THERMAL_SHUTDOWN", s->faultcode))
|
|
ucs.faultcode = PLAT_POE_LINK_FAULTCODE_THERMAL_SHUTDOWN;
|
|
else if (!strcmp("STARTUP_FAILURE", s->faultcode))
|
|
ucs.faultcode = PLAT_POE_LINK_FAULTCODE_STARTUP_FAILURE;
|
|
else if (!strcmp("UVLO", s->faultcode))
|
|
ucs.faultcode = PLAT_POE_LINK_FAULTCODE_UVLO;
|
|
else if (!strcmp("HW_PIN_DISABLE", s->faultcode))
|
|
ucs.faultcode = PLAT_POE_LINK_FAULTCODE_HW_PIN_DISABLE;
|
|
else if (!strcmp("PORT_UNDEFINED", s->faultcode))
|
|
ucs.faultcode = PLAT_POE_LINK_FAULTCODE_PORT_UNDEFINED;
|
|
else if (!strcmp("INTERNAL_HW_FAULT", s->faultcode))
|
|
ucs.faultcode = PLAT_POE_LINK_FAULTCODE_INTERNAL_HW_FAULT;
|
|
else if (!strcmp("USER_SETTING", s->faultcode))
|
|
ucs.faultcode = PLAT_POE_LINK_FAULTCODE_USER_SETTING;
|
|
else if (!strcmp("NON_STANDARD_PD", s->faultcode))
|
|
ucs.faultcode = PLAT_POE_LINK_FAULTCODE_NON_STANDARD_PD;
|
|
else if (!strcmp("UNDERLOAD", s->faultcode))
|
|
ucs.faultcode = PLAT_POE_LINK_FAULTCODE_UNDERLOAD;
|
|
else if (!strcmp("PWR_BUDGET_EXCEEDED", s->faultcode))
|
|
ucs.faultcode = PLAT_POE_LINK_FAULTCODE_PWR_BUDGET_EXCEEDED;
|
|
else if (!strcmp("OOR_CAPACITOR_VALUE", s->faultcode))
|
|
ucs.faultcode = PLAT_POE_LINK_FAULTCODE_OOR_CAPACITOR_VALUE;
|
|
else if (!strcmp("CLASS_ERROR", s->faultcode))
|
|
ucs.faultcode = PLAT_POE_LINK_FAULTCODE_CLASS_ERROR;
|
|
else
|
|
return;
|
|
|
|
if (ctx->poe_link_faultcode_cb)
|
|
ctx->poe_link_faultcode_cb(&ucs);
|
|
}
|
|
|
|
int plat_event_subscribe(const struct plat_event_callbacks *cbs)
|
|
{
|
|
if (subscribe_hdl) {
|
|
UC_LOG_DBG("already subscribed");
|
|
return -1;
|
|
}
|
|
|
|
events_cbs = *cbs;
|
|
|
|
return gnma_subscribe(&subscribe_hdl,
|
|
&(struct gnma_subscribe_callbacks){
|
|
.alarm_cb = alarm_gnma_cb,
|
|
.alarm_data = &events_cbs,
|
|
.linkstatus_cb = linkstatus_gnma_cb,
|
|
.linkstatus_data = &events_cbs,
|
|
.poe_linkstatus_cb = poe_linkstatus_gnma_cb,
|
|
.poe_linkstatus_data = &events_cbs,
|
|
.poe_link_faultcode_cb = poe_link_faultcode_gnma_cb,
|
|
.poe_link_faultcode_data = &events_cbs,
|
|
});
|
|
}
|
|
|
|
void plat_event_unsubscribe(void)
|
|
{
|
|
gnma_unsubscribe(&subscribe_hdl);
|
|
events_cbs = (struct plat_event_callbacks){ 0 };
|
|
}
|
|
|
|
int plat_syslog_set(struct plat_syslog_cfg *cfg, int count)
|
|
{
|
|
static const char *const prio2str[8] = { "emerg", "alert", "crit",
|
|
"error", "warning", "notice",
|
|
"info", "debug" };
|
|
int i;
|
|
struct gnma_syslog_cfg *gnma_cfg = 0;
|
|
int ret = -1;
|
|
|
|
gnma_cfg = malloc(count * sizeof *gnma_cfg);
|
|
if (!gnma_cfg) {
|
|
UC_LOG_ERR("malloc failed");
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < count; ++i) {
|
|
if (cfg[i].priority < 0 || cfg[i].priority > 7) {
|
|
UC_LOG_ERR("severity must be in range 0-7");
|
|
goto err;
|
|
}
|
|
|
|
gnma_cfg[i] = (struct gnma_syslog_cfg){
|
|
.ipaddress = cfg[i].host,
|
|
.remote_port = cfg[i].port,
|
|
.severity = prio2str[cfg[i].priority],
|
|
};
|
|
}
|
|
|
|
if (gnma_syslog_cfg_clear()) {
|
|
UC_LOG_ERR("failed clearing previous syslog configuration");
|
|
goto err;
|
|
}
|
|
ret = gnma_syslog_cfg_set(gnma_cfg, count);
|
|
|
|
err:
|
|
free(gnma_cfg);
|
|
return ret;
|
|
}
|
|
|
|
/* NOTE: In case of error this function left partial config */
|
|
int plat_vlan_rif_set(uint16_t vid, struct plat_ipv4 *ipv4)
|
|
{
|
|
struct gnma_igmp_snoop_attr igmp_snoop_attr = {
|
|
.enabled=false,
|
|
.querier_enabled=false,
|
|
.version=GNMA_IGMP_VERSION_NA
|
|
};
|
|
struct gnma_ip_prefix pref, pref_old;
|
|
uint16_t list_size;
|
|
int i;
|
|
|
|
pref.prefix_len = ipv4->subnet_len;
|
|
pref.ip.v = AF_INET;
|
|
memcpy(&pref.ip.u.v4, &ipv4->subnet, sizeof(pref.ip.u.v4));
|
|
|
|
list_size = 1;
|
|
/* TODO support more than one. So handle overflow */
|
|
if (gnma_vlan_erif_attr_pref_list_get(vid, &list_size, &pref_old))
|
|
return -1;
|
|
|
|
/* Both update / delete require no upper (dhcp) cfg.
|
|
* Remove dhcp-relay cfg, and restore it, only in case
|
|
* if RIF was updated, not deleted (changed pref for example).
|
|
*/
|
|
for (i = 0; i < PLAT_DHCP_RELAY_MAX_SERVERS; ++i) {
|
|
if (!plat_state.vlans[vid].dhcp_relay.enabled)
|
|
continue;
|
|
if (gnma_vlan_dhcp_relay_server_remove(
|
|
vid,
|
|
&plat_state.vlans[vid].dhcp_relay.helper_addresses[i]))
|
|
return -1;
|
|
}
|
|
|
|
if (gnma_igmp_snooping_set(vid, &igmp_snoop_attr)) {
|
|
UC_LOG_DBG("Failed to set VLAN igmp.\n");
|
|
return -1;
|
|
}
|
|
|
|
if (list_size > 0 && !ipv4->exist) {
|
|
/* Force DHCP cache/state flush (delete), as at this point no
|
|
* dhcp cfg for this vlan exists, and it won't be 'restored'
|
|
* in this function.
|
|
*/
|
|
plat_state.vlans[vid].dhcp_relay.enabled = false;
|
|
memset(&plat_state.vlans[vid].dhcp_relay.helper_addresses[0], 0,
|
|
sizeof(plat_state.vlans[vid].dhcp_relay.helper_addresses));
|
|
|
|
if (gnma_vlan_erif_attr_pref_delete(vid, &pref_old))
|
|
return -1;
|
|
}
|
|
|
|
if (ipv4->exist &&
|
|
(list_size == 0 || !GNMA_IP_PREF_IS_EQ(pref, pref_old))) {
|
|
/* Update works as expected for prefixes less than 2 */
|
|
/* TODO handle comparing for mt 1 cases ! */
|
|
if (gnma_vlan_erif_attr_pref_update(vid, 1, &pref))
|
|
return -1;
|
|
|
|
/* Restore DHCP-relay conf */
|
|
for (i = 0; i < PLAT_DHCP_RELAY_MAX_SERVERS; ++i) {
|
|
if (!plat_state.vlans[vid].dhcp_relay.enabled)
|
|
continue;
|
|
if (gnma_vlan_dhcp_relay_server_add(
|
|
vid,
|
|
&plat_state.vlans[vid].dhcp_relay.helper_addresses[i]))
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int plat_vlan_igmp_set(uint16_t vid, struct plat_igmp *igmp)
|
|
{
|
|
struct gnma_igmp_static_group_attr *group_list;
|
|
struct plat_ports_list *e_port;
|
|
size_t group_idx, port_idx;
|
|
int ret;
|
|
struct gnma_igmp_snoop_attr attr = {
|
|
.last_member_query_interval = igmp->last_member_query_interval,
|
|
.fast_leave_enabled = igmp->fast_leave_enabled,
|
|
.max_response_time = igmp->max_response_time,
|
|
.querier_enabled = igmp->querier_enabled,
|
|
.query_interval = igmp->query_interval,
|
|
.enabled = igmp->snooping_enabled,
|
|
};
|
|
|
|
if (!igmp->exist)
|
|
attr.version = GNMA_IGMP_VERSION_NA;
|
|
else if (igmp->version == PLAT_IGMP_VERSION_1)
|
|
attr.version = GNMA_IGMP_VERSION_1;
|
|
else if (igmp->version == PLAT_IGMP_VERSION_2)
|
|
attr.version = GNMA_IGMP_VERSION_2;
|
|
else if (igmp->version == PLAT_IGMP_VERSION_3)
|
|
attr.version = GNMA_IGMP_VERSION_3;
|
|
else
|
|
return -1;
|
|
|
|
group_list = calloc(igmp->num_groups, sizeof(*group_list));
|
|
if (!group_list) {
|
|
UC_LOG_ERR("ENOMEM");
|
|
return -1;
|
|
}
|
|
|
|
for (group_idx = 0; group_idx < igmp->num_groups; group_idx++) {
|
|
group_list[group_idx].address = igmp->groups[group_idx].addr;
|
|
|
|
group_list[group_idx].num_ports = 0;
|
|
UCENTRAL_LIST_FOR_EACH_MEMBER(e_port, &igmp->groups[group_idx].egress_ports_list) {
|
|
group_list[group_idx].num_ports++;
|
|
}
|
|
group_list[group_idx].egress_ports = calloc(group_list[group_idx].num_ports,
|
|
sizeof(*group_list[group_idx].egress_ports));
|
|
if (!group_list[group_idx].egress_ports) {
|
|
UC_LOG_ERR("ENOMEM");
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
|
|
port_idx = 0;
|
|
UCENTRAL_LIST_FOR_EACH_MEMBER(e_port, &igmp->groups[group_idx].egress_ports_list) {
|
|
strncpy(group_list[group_idx].egress_ports[port_idx].name,
|
|
e_port->name,
|
|
sizeof(group_list[group_idx].egress_ports[port_idx].name));
|
|
port_idx++;
|
|
}
|
|
}
|
|
|
|
ret = gnma_igmp_snooping_set(vid, &attr);
|
|
if (ret) {
|
|
UC_LOG_ERR("gnma_igmp_snooping_set");
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
|
|
ret = gnma_igmp_static_groups_set(vid, igmp->num_groups, group_list);
|
|
if (ret) {
|
|
UC_LOG_ERR("gnma_igmp_static_groups_set");
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
err:
|
|
if (group_list)
|
|
for (group_idx = 0; group_idx < igmp->num_groups; group_idx++)
|
|
free(group_list[group_idx].egress_ports);
|
|
free(group_list);
|
|
return ret;
|
|
}
|
|
|
|
/* NOTE: In case of error this function left partial config */
|
|
int plat_portl2_rif_set(uint16_t fp_p_id, struct plat_ipv4 *ipv4)
|
|
{
|
|
struct plat_ipv4 *sipv4 = &plat_state.portsl2_rif_ipv4[fp_p_id];
|
|
struct gnma_port_key gnma_port;
|
|
struct gnma_ip_prefix pref;
|
|
|
|
PID_TO_NAME(fp_p_id, gnma_port.name);
|
|
|
|
if (sipv4->exist && !ipv4->exist) {
|
|
pref.prefix_len = ipv4->subnet_len;
|
|
pref.ip.v = AF_INET;
|
|
memcpy(&pref.ip.u.v4, &sipv4->subnet, sizeof(pref.ip.u.v4));
|
|
if (gnma_portl2_erif_attr_pref_delete(&gnma_port, &pref))
|
|
return -1;
|
|
}
|
|
|
|
if (ipv4->exist &&
|
|
(sipv4->subnet_len != ipv4->subnet_len ||
|
|
sipv4->subnet.s_addr != ipv4->subnet.s_addr)) {
|
|
pref.prefix_len = ipv4->subnet_len;
|
|
pref.ip.v = AF_INET;
|
|
memcpy(&pref.ip.u.v4, &ipv4->subnet, sizeof(pref.ip.u.v4));
|
|
/* Update works as expected for prefixes less than 2 */
|
|
/* TODO handle comparing for mt 1 cases ! */
|
|
if (gnma_portl2_erif_attr_pref_update(&gnma_port, 1, &pref))
|
|
return -1;
|
|
}
|
|
|
|
memcpy(sipv4, ipv4, sizeof(*ipv4));
|
|
return 0;
|
|
}
|
|
|
|
static void plat_state_deinit(struct plat_state_info *state)
|
|
{
|
|
struct plat_ports_list *port_node;
|
|
|
|
for (int i = 0; i < state->port_info_count; i++) {
|
|
if (state->port_info[i].has_transceiver_info &&
|
|
state->port_info[i].transceiver_info.num_supported_link_modes) {
|
|
free(state->port_info[i].transceiver_info.supported_link_modes);
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < state->vlan_info_count; i++) {
|
|
struct plat_igmp *info = &state->vlan_info[i].igmp;
|
|
|
|
if (info->num_groups) {
|
|
for (size_t i = 0; i < info->num_groups; ++i) {
|
|
UCENTRAL_LIST_DESTROY_SAFE(
|
|
&info->groups[i].egress_ports_list,
|
|
port_node);
|
|
}
|
|
free(info->groups);
|
|
}
|
|
}
|
|
|
|
free(state->learned_mac_list);
|
|
free(state->port_info);
|
|
free(state->vlan_info);
|
|
*state = (struct plat_state_info){ 0 };
|
|
}
|
|
|
|
static int plat_port_info_get(struct plat_port_info **port_info, int *count)
|
|
{
|
|
size_t ieee8021x_buf_size = 0;
|
|
char *ieee8021x_buf = NULL;
|
|
int rc;
|
|
size_t i = 0;
|
|
uint16_t pcount = 0;
|
|
struct plat_port_info *pinfo = 0;
|
|
struct gnma_port_key *plist = 0;
|
|
int ret = -1;
|
|
|
|
/* TODO(vb) beautify &c */
|
|
if (plat_port_num_get(&pcount)) {
|
|
UC_LOG_DBG("plat_port_num_get failed");
|
|
goto err;
|
|
}
|
|
|
|
if (!(plist = malloc(sizeof *plist * pcount))) {
|
|
goto err;
|
|
}
|
|
rc = gnma_port_list_get(&pcount, plist);
|
|
if (rc && rc != GNMA_ERR_OVERFLOW) {
|
|
UC_LOG_DBG("gnma_port_list_get failed");
|
|
goto err;
|
|
}
|
|
|
|
if (!(pinfo = malloc(sizeof *pinfo * pcount))) {
|
|
goto err;
|
|
}
|
|
|
|
for (i = 0; i < pcount; ++i) {
|
|
uint16_t pid;
|
|
bool is_up, is_full_duplex;
|
|
|
|
NAME_TO_PID(&pid, plist[i].name);
|
|
|
|
pinfo[i] = (struct plat_port_info){ 0 };
|
|
snprintf(pinfo[i].name, PORT_MAX_NAME_LEN, "%s", plist[i].name);
|
|
if (plat_port_speed_get(pid, &pinfo[i].speed)) {
|
|
UC_LOG_DBG("plat_port_speed_get failed");
|
|
goto err;
|
|
}
|
|
if (plat_port_duplex_get(pid, &is_full_duplex)) {
|
|
UC_LOG_DBG("plat_port_duplex_get failed");
|
|
goto err;
|
|
}
|
|
pinfo[i].duplex = is_full_duplex;
|
|
if (plat_port_oper_status_get(pid, &is_up)) {
|
|
UC_LOG_DBG("plat_port_oper_status_get failed");
|
|
goto err;
|
|
}
|
|
pinfo[i].carrier_up = is_up;
|
|
if (plat_port_stats_get(pid, &pinfo[i].stats)) {
|
|
UC_LOG_DBG("plat_port_stats_get failed");
|
|
goto err;
|
|
}
|
|
|
|
if (!plat_port_lldp_peer_info_get(pid,
|
|
&pinfo[i].lldp_peer_info)) {
|
|
pinfo[i].has_lldp_peer_info = 1;
|
|
}
|
|
|
|
if (!plat_port_transceiver_info_get(pid,
|
|
&pinfo[i].transceiver_info)) {
|
|
pinfo[i].has_transceiver_info = 1;
|
|
}
|
|
|
|
plat_ieee8021x_system_auth_clients_get(pid,
|
|
&ieee8021x_buf,
|
|
&ieee8021x_buf_size,
|
|
&pinfo[i].ieee8021x_info);
|
|
}
|
|
|
|
*port_info = pinfo;
|
|
*count = pcount;
|
|
pinfo = 0;
|
|
ret = 0;
|
|
err:
|
|
free(pinfo);
|
|
free(plist);
|
|
free(ieee8021x_buf);
|
|
if (ret)
|
|
UC_LOG_DBG("failed");
|
|
return ret;
|
|
}
|
|
|
|
static int plat_vlan_info_get(struct plat_port_vlan **vlan_info, size_t *count)
|
|
{
|
|
BITMAP_DECLARE(vlans_bmp, GNMA_MAX_VLANS);
|
|
struct plat_port_vlan *vinfo = 0;
|
|
size_t num_vlans = 0;
|
|
size_t idx = 0;
|
|
size_t vid;
|
|
int ret;
|
|
|
|
BITMAP_CLEAR(vlans_bmp, GNMA_MAX_VLANS);
|
|
ret = gnma_vlan_list_get(vlans_bmp);
|
|
if (ret)
|
|
return -1;
|
|
|
|
BITMAP_FOR_EACH_BIT_SET(vid, vlans_bmp, GNMA_MAX_VLANS) {
|
|
num_vlans++;
|
|
}
|
|
|
|
if (!num_vlans) {
|
|
*count = 0;
|
|
return 0;
|
|
}
|
|
|
|
vinfo = calloc(num_vlans, sizeof(*vinfo));
|
|
if (!vinfo) {
|
|
UC_LOG_ERR("ENOMEM");
|
|
return -1;
|
|
}
|
|
memset(vinfo, 0, num_vlans * sizeof(*vinfo));
|
|
|
|
BITMAP_FOR_EACH_BIT_SET(vid, vlans_bmp, GNMA_MAX_VLANS) {
|
|
vinfo[idx].id = vid;
|
|
|
|
if (plat_vlan_igmp_info_get(vid, &vinfo[idx].igmp)) {
|
|
UC_LOG_DBG("plat_vlan_igmp_info_get failed");
|
|
return -1;
|
|
}
|
|
|
|
idx++;
|
|
if (idx >= num_vlans)
|
|
break;
|
|
}
|
|
|
|
*count = idx;
|
|
*vlan_info = vinfo;
|
|
return 0;
|
|
}
|
|
|
|
static int get_meminfo_cached_kib(uint64_t *cached)
|
|
{
|
|
size_t n;
|
|
char *line = 0;
|
|
int found = 0;
|
|
FILE *f = fopen("/proc/meminfo", "r");
|
|
if (!f)
|
|
return -1;
|
|
|
|
while (getline(&line, &n, f) >= 0) {
|
|
if (sscanf(line, "Cached:%" SCNu64, cached) == 1) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
free(line);
|
|
fclose(f);
|
|
return found ? 0 : 1;
|
|
}
|
|
|
|
static int plat_system_info_get(struct plat_system_info *info)
|
|
{
|
|
uint64_t cached = 0;
|
|
struct sysinfo sys_info = { 0 };
|
|
double loadArray[3] = { 0 };
|
|
time_t localtime = time(0);
|
|
|
|
sysinfo(&sys_info);
|
|
|
|
get_meminfo_cached_kib(&cached);
|
|
|
|
getloadavg(loadArray, 3);
|
|
loadArray[0] /= 100;
|
|
loadArray[1] /= 100;
|
|
loadArray[2] /= 100;
|
|
|
|
*info = (struct plat_system_info){ 0 };
|
|
info->localtime = (uint64_t)localtime;
|
|
info->uptime = (uint64_t)sys_info.uptime;
|
|
info->ram_buffered = sys_info.bufferram * sys_info.mem_unit;
|
|
info->ram_cached = cached * 1024;
|
|
info->ram_free =
|
|
(sys_info.freeram + sys_info.freeswap) * sys_info.mem_unit;
|
|
info->ram_total = sys_info.totalram * sys_info.mem_unit;
|
|
memcpy(info->load_average, loadArray, sizeof info->load_average);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
plat_learned_mac_addrs_get(struct plat_learned_mac_addr **mac_list,
|
|
size_t *mac_list_size)
|
|
{
|
|
struct plat_learned_mac_addr *m_list;
|
|
struct gnma_fdb_entry *list;
|
|
size_t list_size = 0, i, k;
|
|
int ret;
|
|
|
|
ret = gnma_mac_address_list_get(&list_size, NULL);
|
|
if (ret && ret != GNMA_ERR_OVERFLOW)
|
|
return ret;
|
|
|
|
if (list_size == 0) {
|
|
*mac_list = NULL;
|
|
*mac_list_size = 0;
|
|
return 0;
|
|
}
|
|
|
|
if (!(list = calloc(list_size, sizeof(*list)))) {
|
|
UC_LOG_ERR("ENOMEM");
|
|
return -1;
|
|
}
|
|
|
|
/** TODO: number of entries might change between calls and this will fail */
|
|
ret = gnma_mac_address_list_get(&list_size, list);
|
|
if (ret)
|
|
goto err;
|
|
|
|
if (!(m_list = calloc(list_size, sizeof(*m_list)))) {
|
|
UC_LOG_ERR("ENOMEM");
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
|
|
for (i = 0, k = 0; i < list_size; i++) {
|
|
if (list[i].type != GNMA_FDB_ENTRY_TYPE_DYNAMIC)
|
|
continue;
|
|
strncpy(m_list[k].port, list[i].port.name, sizeof(m_list[k].port));
|
|
strncpy(m_list[k].mac, list[i].mac, sizeof(m_list[k].mac));
|
|
m_list[k].vid = list[i].vid;
|
|
k++;
|
|
}
|
|
|
|
*mac_list = m_list;
|
|
*mac_list_size = k;
|
|
err:
|
|
free(list);
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
plat_state_ieee8021x_coa_global_counters_get(struct plat_iee8021x_coa_counters *stats)
|
|
{
|
|
gnma_ieee8021x_das_dac_stat_type_t stat_types[] = {
|
|
GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_PKTS,
|
|
GNMA_IEEE8021X_DAS_DAC_STAT_OUT_COA_ACK_PKTS,
|
|
GNMA_IEEE8021X_DAS_DAC_STAT_OUT_COA_NAK_PKTS,
|
|
GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_IGNORED_PKTS,
|
|
GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_WRONG_ATTR_PKTS,
|
|
GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_WRONG_ATTR_VALUE_PKTS,
|
|
GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_WRONG_SESSION_CONTEXT_PKTS,
|
|
GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_ADMINISTRATIVELY_PROHIBITED_REQ_PKTS,
|
|
};
|
|
uint64_t counters[ARRAY_LENGTH(stat_types)];
|
|
|
|
if (gnma_iee8021x_das_dac_global_stats_get(ARRAY_LENGTH(stat_types),
|
|
&stat_types[0],
|
|
&counters[0]))
|
|
return -EINVAL;
|
|
|
|
stats->coa_req_received =
|
|
counters[ARR_FIND_VALUE_IDX(stat_types, ARRAY_LENGTH(stat_types),
|
|
GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_PKTS)];
|
|
stats->coa_ack_sent =
|
|
counters[ARR_FIND_VALUE_IDX(stat_types, ARRAY_LENGTH(stat_types),
|
|
GNMA_IEEE8021X_DAS_DAC_STAT_OUT_COA_ACK_PKTS)];
|
|
stats->coa_nak_sent =
|
|
counters[ARR_FIND_VALUE_IDX(stat_types, ARRAY_LENGTH(stat_types),
|
|
GNMA_IEEE8021X_DAS_DAC_STAT_OUT_COA_NAK_PKTS)];
|
|
stats->coa_ignored =
|
|
counters[ARR_FIND_VALUE_IDX(stat_types, ARRAY_LENGTH(stat_types),
|
|
GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_IGNORED_PKTS)];
|
|
stats->coa_wrong_attr =
|
|
counters[ARR_FIND_VALUE_IDX(stat_types, ARRAY_LENGTH(stat_types),
|
|
GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_WRONG_ATTR_PKTS)];
|
|
stats->coa_wrong_attr_value =
|
|
counters[ARR_FIND_VALUE_IDX(stat_types, ARRAY_LENGTH(stat_types),
|
|
GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_WRONG_ATTR_VALUE_PKTS)];
|
|
stats->coa_wrong_session_context =
|
|
counters[ARR_FIND_VALUE_IDX(stat_types, ARRAY_LENGTH(stat_types),
|
|
GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_WRONG_SESSION_CONTEXT_PKTS)];
|
|
stats->coa_administratively_prohibited_req =
|
|
counters[ARR_FIND_VALUE_IDX(stat_types, ARRAY_LENGTH(stat_types),
|
|
GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_ADMINISTRATIVELY_PROHIBITED_REQ_PKTS)];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int plat_state_get(struct plat_state_info *state)
|
|
{
|
|
size_t i;
|
|
|
|
plat_poe_state_get(&state->poe_state);
|
|
|
|
BITMAP_FOR_EACH_BIT_SET(i, plat_state.poe.ports_bmap, MAX_NUM_OF_PORTS)
|
|
{
|
|
plat_poe_port_state_get(i, &state->poe_ports_state[i]);
|
|
BITMAP_SET_BIT(state->poe_ports_bmap, i);
|
|
}
|
|
|
|
if (plat_system_info_get(&state->system_info))
|
|
return -1;
|
|
|
|
if (plat_port_info_get(&state->port_info, &state->port_info_count))
|
|
return -1;
|
|
|
|
if (plat_vlan_info_get(&state->vlan_info, &state->vlan_info_count))
|
|
return -1;
|
|
|
|
if (plat_learned_mac_addrs_get(&state->learned_mac_list,
|
|
&state->learned_mac_list_size))
|
|
return -1;
|
|
|
|
if (plat_state_ieee8021x_coa_global_counters_get(&state->ieee8021x_global_coa_counters))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int config_vlan_ipv4_apply(struct plat_cfg *cfg)
|
|
{
|
|
size_t i;
|
|
int ret;
|
|
|
|
BITMAP_FOR_EACH_BIT_SET(i, cfg->vlans_to_cfg, MAX_VLANS) {
|
|
UC_LOG_DBG("Configuring vlan ip <%u>\n", (uint16_t)i);
|
|
ret = plat_vlan_rif_set(cfg->vlans[i].id, &cfg->vlans[i].ipv4);
|
|
if (ret) {
|
|
UC_LOG_DBG("Failed to set VLAN rif.\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = plat_vlan_igmp_set(cfg->vlans[i].id, &cfg->vlans[i].igmp);
|
|
if (ret) {
|
|
UC_LOG_DBG("Failed to set VLAN igmp.\n");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int config_portl2_ipv4_apply(struct plat_cfg *cfg)
|
|
{
|
|
size_t i;
|
|
int ret;
|
|
|
|
BITMAP_FOR_EACH_BIT_SET(i, cfg->ports_to_cfg, MAX_NUM_OF_PORTS) {
|
|
UC_LOG_DBG("Configuring port ip <%u>\n", (uint16_t)i);
|
|
/* If port is not in cfg - ipv4.exist == false */
|
|
ret = plat_portl2_rif_set(i, &cfg->portsl2[i].ipv4);
|
|
if (ret) {
|
|
UC_LOG_DBG("Failed to set portl2 rif.\n");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int config_vlan_dhcp_relay_apply(struct plat_cfg *cfg)
|
|
{
|
|
gnma_dhcp_relay_circuit_id_t circ_id;
|
|
struct gnma_ip ip;
|
|
size_t i;
|
|
int ret;
|
|
|
|
/* Clear previous cfg: delete disabled relay-addresses, or
|
|
* delete relay-addresses if they've been changed.
|
|
*/
|
|
BITMAP_FOR_EACH_BIT_SET(i, cfg->vlans_to_cfg, MAX_VLANS)
|
|
{
|
|
/* Iterate only over configured vlans. */
|
|
if (!plat_state.vlans[i].dhcp_relay.enabled)
|
|
continue;
|
|
|
|
/* If relay was enabled prior, and new cfg wants to disabled it -
|
|
* remove relay address from this iface.
|
|
* Also, remove relay helper address in case if both previous
|
|
* and new CFG enable dhcp-relay, but the helper-address's changed.
|
|
* New address will be added below.
|
|
*
|
|
* Since schema only able to configure one (first) server,
|
|
* remove server at idx 0.
|
|
*/
|
|
if ((!cfg->vlans[i].dhcp.relay.enabled &&
|
|
plat_state.vlans[i].dhcp_relay.enabled) ||
|
|
(plat_state.vlans[i].dhcp_relay.enabled &&
|
|
cfg->vlans[i].dhcp.relay.enabled &&
|
|
memcmp(&plat_state.vlans[i].dhcp_relay.helper_addresses[0].u.v4.s_addr,
|
|
&cfg->vlans[i].dhcp.relay.server_address.s_addr,
|
|
sizeof(cfg->vlans[i].dhcp.relay.server_address.s_addr)))) {
|
|
|
|
UC_LOG_DBG("Vid <%u> removing %s \n",
|
|
(uint16_t)i,
|
|
inet_ntoa(plat_state.vlans[i].dhcp_relay.helper_addresses[0].u.v4));
|
|
if (gnma_vlan_dhcp_relay_server_remove(i,
|
|
&plat_state.vlans[i].dhcp_relay.helper_addresses[0]))
|
|
return -1;
|
|
|
|
plat_state.vlans[i].dhcp_relay.enabled = false;
|
|
memset(&plat_state.vlans[i].dhcp_relay.helper_addresses[0].u.v4.s_addr, 0,
|
|
sizeof(plat_state.vlans[i].dhcp_relay.helper_addresses[0].u.v4.s_addr));
|
|
}
|
|
}
|
|
|
|
BITMAP_FOR_EACH_BIT_SET(i, cfg->vlans_to_cfg, MAX_VLANS)
|
|
{
|
|
/* Iterate only over vlans to-be-configured. */
|
|
if (!cfg->vlans[i].dhcp.relay.enabled)
|
|
continue;
|
|
|
|
UC_LOG_DBG("Configuring vlan dhcp-relay <%u>\n", (uint16_t)i);
|
|
UC_LOG_DBG("adding dhcp-relay helper addr <%s>\n",
|
|
inet_ntoa(cfg->vlans[i].dhcp.relay.server_address));
|
|
|
|
memcpy(&ip.u.v4.s_addr,
|
|
&cfg->vlans[i].dhcp.relay.server_address.s_addr,
|
|
sizeof(ip.u.v4.s_addr));
|
|
|
|
ret = gnma_vlan_dhcp_relay_server_add(i, &ip);
|
|
if (ret) {
|
|
UC_LOG_DBG("Failed to set VLAN dhcp-relay server.\n");
|
|
return ret;
|
|
}
|
|
|
|
/* Schema only able to configure one (first) server,
|
|
* so copy just-configured addr at idx 0.
|
|
*/
|
|
memcpy(&plat_state.vlans[i].dhcp_relay.helper_addresses[0].u.v4.s_addr,
|
|
&ip.u.v4.s_addr, sizeof(ip.u.v4.s_addr));
|
|
|
|
if (plat_state.vlans[i].dhcp_relay.max_hop_cnt != PLAT_DHCP_RELAY_DEFAULT_MAXHOP_CNT) {
|
|
ret = gnma_vlan_dhcp_relay_max_hop_cnt_set(i, PLAT_DHCP_RELAY_DEFAULT_MAXHOP_CNT);
|
|
if (ret) {
|
|
UC_LOG_DBG("Failed to set VLAN dhcp-relay max hop to default value\n");
|
|
return ret;
|
|
}
|
|
plat_state.vlans[i].dhcp_relay.max_hop_cnt =
|
|
PLAT_DHCP_RELAY_DEFAULT_MAXHOP_CNT;
|
|
}
|
|
if (plat_state.vlans[i].dhcp_relay.policy_act != PLAT_DHCP_RELAY_DEFAULT_POLICY_ACT) {
|
|
ret = gnma_vlan_dhcp_relay_policy_action_set(i, PLAT_DHCP_RELAY_DEFAULT_POLICY_ACT);
|
|
if (ret) {
|
|
UC_LOG_DBG("Failed to set VLAN dhcp-relay policy action to default value\n");
|
|
return ret;
|
|
}
|
|
plat_state.vlans[i].dhcp_relay.policy_act =
|
|
PLAT_DHCP_RELAY_DEFAULT_POLICY_ACT;
|
|
}
|
|
|
|
if (!strcmp(cfg->vlans[i].dhcp.relay.circ_id, "%u"))
|
|
circ_id = GNMA_DHCP_RELAY_CIRCUIT_ID_I;
|
|
else if (!strcmp(cfg->vlans[i].dhcp.relay.circ_id, "%p"))
|
|
circ_id = GNMA_DHCP_RELAY_CIRCUIT_ID_P;
|
|
else
|
|
circ_id = GNMA_DHCP_RELAY_CIRCUIT_ID_H_P;
|
|
|
|
if (plat_state.vlans[i].dhcp_relay.circ_id != circ_id) {
|
|
ret = gnma_vlan_dhcp_relay_ciruit_id_set(i, circ_id);
|
|
if (ret) {
|
|
UC_LOG_DBG("Failed to set VLAN dhcp-relay circuit id format\n");
|
|
return ret;
|
|
}
|
|
plat_state.vlans[i].dhcp_relay.circ_id = circ_id;
|
|
}
|
|
|
|
plat_state.vlans[i].dhcp_relay.enabled = true;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int config_vlan_apply(struct plat_cfg *cfg)
|
|
{
|
|
struct gnma_change *c = 0;
|
|
struct gnma_vlan_member_bmap *vlan_mbr = 0;
|
|
int ret = 0;
|
|
size_t i;
|
|
|
|
/* Handle <negative> case: if there are configured VLANs on system that
|
|
* are not present in CFG - remove the missing VLANs firsts
|
|
*/
|
|
ret = plat_vlan_list_set(cfg->vlans_to_cfg);
|
|
if (ret) {
|
|
UC_LOG_DBG("Failed to set VLANs list.\n");
|
|
goto err;
|
|
}
|
|
|
|
if (!(vlan_mbr = calloc(1, sizeof *vlan_mbr))) {
|
|
UC_LOG_ERR("ENOMEM");
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
|
|
if ((ret = gnma_vlan_member_bmap_get(vlan_mbr))) {
|
|
UC_LOG_ERR("gnma_vlan_member_bmap_get");
|
|
goto err;
|
|
}
|
|
|
|
/* Handle <positive> case: setting vlan's member list;
|
|
* If vlan doesn't exist - it's being created upon member set call
|
|
*/
|
|
if (!(c = gnma_change_create())) {
|
|
UC_LOG_ERR("gnma_change_create failed");
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
|
|
BITMAP_FOR_EACH_BIT_SET(i, cfg->vlans_to_cfg, GNMA_MAX_VLANS)
|
|
{
|
|
UC_LOG_DBG("Configuring vlan <%u>\n", (uint16_t)i);
|
|
ret = plat_vlan_memberlist_set(c, vlan_mbr, &cfg->vlans[i]);
|
|
if (ret) {
|
|
UC_LOG_DBG("Failed to set VLAN members list.\n");
|
|
goto err;
|
|
}
|
|
}
|
|
ZFREE(vlan_mbr);
|
|
|
|
if ((ret = gnma_change_exec(c)))
|
|
UC_LOG_ERR("gnma_change_exec failed");
|
|
|
|
err:
|
|
ZFREE(vlan_mbr);
|
|
gnma_change_destory(c);
|
|
|
|
return ret ? -1 : 0;
|
|
}
|
|
|
|
static int config_stp_apply_ports_enable_all(void)
|
|
{
|
|
struct gnma_port_key *plist = NULL;
|
|
uint16_t pcount = 0;
|
|
int err = -1;
|
|
int ret;
|
|
|
|
if (plat_port_num_get(&pcount)) {
|
|
UC_LOG_ERR("plat_port_num_get failed");
|
|
goto err;
|
|
}
|
|
|
|
if (!(plist = malloc(sizeof *plist * pcount))) {
|
|
goto err;
|
|
}
|
|
|
|
ret = gnma_port_list_get(&pcount, plist);
|
|
if (ret && ret != GNMA_ERR_OVERFLOW) {
|
|
UC_LOG_ERR("gnma_port_list_get failed");
|
|
goto err;
|
|
}
|
|
|
|
ret = gnma_stp_ports_enable(pcount, plist);
|
|
if (ret) {
|
|
UC_LOG_ERR("gnma_stp_ports_enable failed");
|
|
goto err;
|
|
}
|
|
|
|
err = 0;
|
|
err:
|
|
free(plist);
|
|
return err;
|
|
}
|
|
|
|
static int config_stp_apply(struct plat_cfg *cfg)
|
|
{
|
|
struct gnma_stp_attr attr;
|
|
int ret, i;
|
|
|
|
switch (cfg->stp_mode) {
|
|
case PLAT_STP_MODE_NONE:
|
|
if (plat_state.stp_mode == GNMA_STP_MODE_NONE)
|
|
break;
|
|
|
|
/* This will clear all per port/vlan stp entries */
|
|
ret = gnma_stp_mode_set(GNMA_STP_MODE_NONE, NULL);
|
|
if (ret) {
|
|
UC_LOG_ERR("Failed to disable STP.\n");
|
|
return ret;
|
|
}
|
|
|
|
plat_state.stp_mode = GNMA_STP_MODE_NONE;
|
|
memset(&plat_state.stp_vlan_attr[0], 0, sizeof(plat_state.stp_vlan_attr));
|
|
break;
|
|
case PLAT_STP_MODE_RPVST:
|
|
/* Config mode */
|
|
memset(&attr, 0, sizeof(attr));
|
|
if (plat_state.stp_mode != GNMA_STP_MODE_RPVST ||
|
|
!gnma_stp_attr_cmp(&attr, &plat_state.stp_mode_attr)) {
|
|
ret = gnma_stp_mode_set(GNMA_STP_MODE_RPVST, &attr);
|
|
if (ret) {
|
|
UC_LOG_ERR("Failed to set STP mode.\n");
|
|
return ret;
|
|
}
|
|
|
|
/* Once mode enabled - create entries for all ports */
|
|
ret = config_stp_apply_ports_enable_all();
|
|
if (ret) {
|
|
UC_LOG_ERR("Failed to set STP on ports.\n");
|
|
return ret;
|
|
}
|
|
|
|
plat_state.stp_mode = GNMA_STP_MODE_RPVST;
|
|
plat_state.stp_mode_attr = attr;
|
|
}
|
|
|
|
/* Config vlans */
|
|
memset(&attr, 0, sizeof(attr));
|
|
for (i = FIRST_VLAN; i < MAX_VLANS; i++) {
|
|
attr.enabled = cfg->stp_instances[i].enabled;
|
|
attr.priority = cfg->stp_instances[i].priority;
|
|
attr.forward_delay = cfg->stp_instances[i].forward_delay;
|
|
attr.hello_time = cfg->stp_instances[i].hello_time;
|
|
attr.max_age = cfg->stp_instances[i].max_age;
|
|
|
|
if (!plat_state.stp_vlan_attr[i].enabled && !attr.enabled) {
|
|
continue;
|
|
}
|
|
|
|
if (gnma_stp_attr_cmp(&attr, &plat_state.stp_vlan_attr[i]))
|
|
continue;
|
|
|
|
UC_LOG_DBG(
|
|
"set vlan=%d attr.enabled=%d attr.priority=%d "
|
|
"attr.forward_delay=%d attr.hello_time=%d "
|
|
"attr.max_age=%d state.enabled=%d state.priority=%d "
|
|
"state.forward_delay=%d state.hello_time=%d "
|
|
"state.max_age=%d ",
|
|
i, attr.enabled, attr.priority, attr.forward_delay,
|
|
attr.hello_time, attr.max_age,
|
|
plat_state.stp_vlan_attr[i].enabled,
|
|
plat_state.stp_vlan_attr[i].priority,
|
|
plat_state.stp_vlan_attr[i].forward_delay,
|
|
plat_state.stp_vlan_attr[i].hello_time,
|
|
plat_state.stp_vlan_attr[i].max_age);
|
|
|
|
ret = gnma_stp_vid_set(i, &attr);
|
|
if (ret) {
|
|
UC_LOG_ERR("Failed to set STP on vlan");
|
|
return ret;
|
|
}
|
|
|
|
plat_state.stp_vlan_attr[i] = attr;
|
|
}
|
|
|
|
break;
|
|
default:
|
|
UC_LOG_ERR("Unsupported STP mode.\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int config_metrics_apply(struct plat_cfg *cfg)
|
|
{
|
|
/* TODO(vb) */
|
|
|
|
UC_LOG_DBG("Metrics cfg:\n");
|
|
UC_LOG_DBG("healthcheck: enabled <%d>, interval <%zu>\n",
|
|
cfg->metrics.healthcheck.enabled,
|
|
cfg->metrics.healthcheck.interval);
|
|
|
|
UC_LOG_DBG(
|
|
"state: enabled <%d>, interval <%zu>, lldp_enabled <%d>, clients_enabled <%d>\n",
|
|
cfg->metrics.state.enabled, cfg->metrics.state.interval,
|
|
cfg->metrics.state.lldp_enabled, cfg->metrics.state.clients_enabled);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __poe_port_detection_mode_str2num(const char *str,
|
|
gnma_poe_port_detection_mode_t *mode)
|
|
{
|
|
if (!strcmp(str, "2pt-dot3af"))
|
|
*mode = GNMA_POE_PORT_DETECTION_MODE_2PT_DOT3AF_E;
|
|
else if (!strcmp(str, "2pt-dot3af+legacy"))
|
|
*mode = GNMA_POE_PORT_DETECTION_MODE_2PT_DOT3AF_LEG_E;
|
|
else if (!strcmp(str, "4pt-dot3af"))
|
|
*mode = GNMA_POE_PORT_DETECTION_MODE_4PT_DOT3AF_E;
|
|
else if (!strcmp(str, "4pt-dot3af+legacy"))
|
|
*mode = GNMA_POE_PORT_DETECTION_MODE_4PT_DOT3AF_LEG_E;
|
|
else if (!strcmp(str, "dot3bt"))
|
|
*mode = GNMA_POE_PORT_DETECTION_MODE_DOT3BT_E;
|
|
else if (!strcmp(str, "dot3bt+legacy"))
|
|
*mode = GNMA_POE_PORT_DETECTION_MODE_DOT3BT_LEG_E;
|
|
else if (!strcmp(str, "legacy"))
|
|
*mode = GNMA_POE_PORT_DETECTION_MODE_LEG_E;
|
|
else
|
|
/* In case if unsupported supplied - use a default one */
|
|
*mode = GNMA_POE_PORT_DETECTION_MODE_4PT_DOT3AF_E;
|
|
}
|
|
|
|
static void __poe_port_priority_mode_str2num(const char *str,
|
|
gnma_poe_port_priority_t *priority)
|
|
{
|
|
if (!strcmp(str, "low"))
|
|
*priority = GNMA_POE_PORT_PRIORITY_LOW_E;
|
|
else if (!strcmp(str, "medium"))
|
|
*priority = GNMA_POE_PORT_PRIORITY_MEDIUM_E;
|
|
else if (!strcmp(str, "high"))
|
|
*priority = GNMA_POE_PORT_PRIORITY_HIGH_E;
|
|
else if (!strcmp(str, "critical"))
|
|
*priority = GNMA_POE_PORT_PRIORITY_CRITICAL_E;
|
|
else
|
|
*priority = GNMA_POE_PORT_PRIORITY_LOW_E;
|
|
}
|
|
|
|
static void __poe_power_mgmt_str2num(const char *str,
|
|
gnma_poe_power_mgmt_mode_t *mgmt_mode)
|
|
{
|
|
if (!strcmp(str, "class"))
|
|
*mgmt_mode = GNMA_POE_POWER_MGMT_CLASS_E;
|
|
else if (!strcmp(str, "dynamic"))
|
|
*mgmt_mode = GNMA_POE_POWER_MGMT_DYNAMIC_E;
|
|
else if (!strcmp(str, "dynamic-priority"))
|
|
*mgmt_mode = GNMA_POE_POWER_MGMT_DYNAMIC_PRIORITY_E;
|
|
else if (!strcmp(str, "static"))
|
|
*mgmt_mode = GNMA_POE_POWER_MGMT_STATIC_E;
|
|
else if (!strcmp(str, "static-priority"))
|
|
*mgmt_mode = GNMA_POE_POWER_MGMT_STATIC_PRIORITY_E;
|
|
else
|
|
*mgmt_mode = GNMA_POE_POWER_MGMT_CLASS_E;
|
|
}
|
|
|
|
static int
|
|
config_port_ieee8021x_apply(uint16_t port_id, struct plat_port *port_cfg)
|
|
{
|
|
struct port *cached_port = &plat_state.ports.array[port_id];
|
|
gnma_8021x_port_ctrl_mode_t ctrl_mode;
|
|
gnma_8021x_port_host_mode_t host_mode;
|
|
struct gnma_port_key gnma_port = {0};
|
|
int ret;
|
|
|
|
PID_TO_NAME(port_id, gnma_port.name);
|
|
|
|
if (cached_port->ieee8021x.is_authenticator != port_cfg->ieee8021x.is_authenticator) {
|
|
UC_LOG_DBG("configuring port .1x pae mode to %s",
|
|
port_cfg->ieee8021x.is_authenticator
|
|
? "authenticator"
|
|
: "none");
|
|
ret = gnma_port_ieee8021x_pae_mode_set(&gnma_port, port_cfg->ieee8021x.is_authenticator);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
switch (port_cfg->ieee8021x.control_mode) {
|
|
case PLAT_802_1X_PORT_CONTROL_FORCE_AUTHORIZED:
|
|
ctrl_mode = GNMA_8021X_PORT_CTRL_MODE_FORCE_AUTHORIZED;
|
|
break;
|
|
case PLAT_802_1X_PORT_CONTROL_FORCE_UNAUTHORIZED:
|
|
ctrl_mode = GNMA_8021X_PORT_CTRL_MODE_FORCE_UNAUTHORIZED;
|
|
break;
|
|
case PLAT_802_1X_PORT_CONTROL_AUTO:
|
|
ctrl_mode = GNMA_8021X_PORT_CTRL_MODE_AUTO;
|
|
break;
|
|
default: break;
|
|
}
|
|
|
|
if (cached_port->ieee8021x.control_mode != ctrl_mode) {
|
|
ret = gnma_port_ieee8021x_port_ctrl_set(&gnma_port, ctrl_mode);
|
|
UC_LOG_DBG("configuring port .1x pae mode from %d to %d",
|
|
cached_port->ieee8021x.control_mode,
|
|
ctrl_mode);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
switch (port_cfg->ieee8021x.host_mode) {
|
|
case PLAT_802_1X_PORT_HOST_MODE_MULTI_AUTH:
|
|
host_mode = GNMA_8021X_PORT_HOST_MODE_MULTI_AUTH;
|
|
break;
|
|
case PLAT_802_1X_PORT_HOST_MODE_MULTI_DOMAIN:
|
|
host_mode = GNMA_8021X_PORT_HOST_MODE_MULTI_DOMAIN;
|
|
break;
|
|
case PLAT_802_1X_PORT_HOST_MODE_MULTI_HOST:
|
|
host_mode = GNMA_8021X_PORT_HOST_MODE_MULTI_HOST;
|
|
break;
|
|
case PLAT_802_1X_PORT_HOST_MODE_SINGLE_HOST:
|
|
host_mode = GNMA_8021X_PORT_HOST_MODE_SINGLE_HOST;
|
|
break;
|
|
default: break;
|
|
}
|
|
|
|
if (cached_port->ieee8021x.host_mode != host_mode) {
|
|
ret = gnma_port_ieee8021x_port_host_mode_set(&gnma_port, host_mode);
|
|
UC_LOG_DBG("configuring port .1x host mode from %d to %d",
|
|
cached_port->ieee8021x.host_mode,
|
|
host_mode);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
if (cached_port->ieee8021x.guest_vid != port_cfg->ieee8021x.guest_vid) {
|
|
UC_LOG_DBG("configuring port .1x guest vid from %d to %d",
|
|
cached_port->ieee8021x.guest_vid, port_cfg->ieee8021x.guest_vid);
|
|
ret = gnma_port_ieee8021x_guest_vlan_set(&gnma_port, port_cfg->ieee8021x.guest_vid);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
if (cached_port->ieee8021x.auth_fail_vid != port_cfg->ieee8021x.auth_fail_vid) {
|
|
UC_LOG_DBG("configuring port .1x fail vid from %d to %d",
|
|
cached_port->ieee8021x.auth_fail_vid, port_cfg->ieee8021x.auth_fail_vid);
|
|
ret = gnma_port_ieee8021x_unauthorized_vlan_set(&gnma_port,
|
|
port_cfg->ieee8021x.auth_fail_vid);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
cached_port->ieee8021x.is_authenticator =
|
|
port_cfg->ieee8021x.is_authenticator;
|
|
cached_port->ieee8021x.control_mode = ctrl_mode;
|
|
cached_port->ieee8021x.guest_vid = port_cfg->ieee8021x.guest_vid;
|
|
cached_port->ieee8021x.auth_fail_vid = port_cfg->ieee8021x.auth_fail_vid;
|
|
cached_port->ieee8021x.host_mode = port_cfg->ieee8021x.host_mode;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int config_poe_port_apply(uint16_t pid,
|
|
struct plat_port *port_cfg)
|
|
{
|
|
struct poe_port *poe_port = &plat_state.poe.ports[pid];
|
|
bool is_power_limit_user_defined = false;
|
|
uint32_t power_limit = 0;
|
|
int ret;
|
|
|
|
if (port_cfg->poe.is_detection_mode_set) {
|
|
gnma_poe_port_detection_mode_t mode;
|
|
|
|
__poe_port_detection_mode_str2num(port_cfg->poe.detection_mode, &mode);
|
|
|
|
if (poe_port->detection_mode != mode) {
|
|
UC_LOG_DBG("configuring poe port detection_mode <%d> to <%d>\n",
|
|
poe_port->detection_mode, mode);
|
|
ret = gnma_poe_port_detection_mode_set(&poe_port->key,
|
|
mode);
|
|
if (ret)
|
|
return -1;
|
|
poe_port->detection_mode = mode;
|
|
}
|
|
}
|
|
|
|
if (port_cfg->poe.is_priority_set) {
|
|
gnma_poe_port_priority_t priority;
|
|
|
|
__poe_port_priority_mode_str2num(port_cfg->poe.priority, &priority);
|
|
|
|
if (poe_port->priority != priority) {
|
|
UC_LOG_DBG("configuring poe port priority <%d> to <%d>\n",
|
|
poe_port->priority, priority);
|
|
ret = gnma_poe_port_priority_set(&poe_port->key,
|
|
priority);
|
|
if (ret) {
|
|
UC_LOG_ERR("gnma_poe_port_priority_set failed");
|
|
return -1;
|
|
}
|
|
poe_port->priority = priority;
|
|
}
|
|
}
|
|
|
|
/* In case if power_limit is omitted, it means that user doesn't
|
|
* care about actual power limit, and relies the selection choice
|
|
* on the power delivering unit - power limit is unlimited,
|
|
* and power limit type is set to 'Class based' (e.g. NOT user defined).
|
|
* This implies an explicit limit_set.
|
|
*
|
|
* Setting limit type to 'User defined' with power limit '0' is also
|
|
* technically a valid configuration, hence all these cases should
|
|
* be explicitly handled as in if-clauses below.
|
|
*/
|
|
if (port_cfg->poe.is_power_limit_set) {
|
|
power_limit = port_cfg->poe.power_limit;
|
|
is_power_limit_user_defined = true;
|
|
}
|
|
if (poe_port->power_limit != power_limit ||
|
|
poe_port->is_power_limit_user_defined != is_power_limit_user_defined) {
|
|
UC_LOG_DBG("configuring poe port power limit <%d,%d> to <%d,%d>\n",
|
|
poe_port->is_power_limit_user_defined, poe_port->power_limit,
|
|
is_power_limit_user_defined, power_limit);
|
|
ret = gnma_poe_port_power_limit_set(&poe_port->key,
|
|
is_power_limit_user_defined,
|
|
power_limit);
|
|
|
|
if (ret) {
|
|
UC_LOG_ERR("gnma_poe_port_power_limit_set failed");
|
|
return -1;
|
|
}
|
|
poe_port->is_power_limit_user_defined = is_power_limit_user_defined;
|
|
poe_port->power_limit = power_limit;
|
|
}
|
|
|
|
if (poe_port->is_admin_mode_up != port_cfg->poe.is_admin_mode_up) {
|
|
UC_LOG_DBG("configuring poe port admin mode <%d> to <%d>\n",
|
|
poe_port->is_admin_mode_up, port_cfg->poe.is_admin_mode_up);
|
|
ret = gnma_poe_port_admin_mode_set(&poe_port->key,
|
|
port_cfg->poe.is_admin_mode_up);
|
|
if (ret) {
|
|
UC_LOG_ERR("gnma_poe_port_admin_mode_set failed");
|
|
return -1;
|
|
}
|
|
poe_port->is_admin_mode_up = port_cfg->poe.is_admin_mode_up;
|
|
}
|
|
|
|
if (port_cfg->poe.do_reset) {
|
|
UC_LOG_DBG("issuing poe port reset\n");
|
|
ret = gnma_poe_port_reset(&poe_port->key);
|
|
if (ret) {
|
|
UC_LOG_ERR("gnma_poe_port_reset failed");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Do diff + copy to cache + free old cache */
|
|
/* CFG and cache is different structs. Not depend on each other. */
|
|
static int config_router_apply(struct plat_cfg *cfg)
|
|
{
|
|
struct ucentral_router newr, oldr;
|
|
struct gnma_route_attrs gattr;
|
|
struct gnma_ip_prefix gpref;
|
|
ssize_t oi, ni;
|
|
int ret, diff;
|
|
|
|
oldr = plat_state.router;
|
|
ret = ucentral_router_fib_db_copy(&cfg->router, &newr);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (!newr.sorted)
|
|
ucentral_router_fib_db_sort(&newr);
|
|
if (!oldr.sorted)
|
|
ucentral_router_fib_db_sort(&oldr);
|
|
|
|
for_router_db_diff(&newr, &oldr, ni, oi, diff) {
|
|
diff = router_db_diff_get(&newr, &oldr, ni, oi);
|
|
|
|
if (diff_case_upd(diff)) {
|
|
if (!ucentral_router_fib_info_cmp(&router_db_get(&newr, ni)->info,
|
|
&router_db_get(&oldr, oi)->info))
|
|
continue;
|
|
|
|
router_fib_key2gnma_prefix(&router_db_get(&newr, ni)->key, &gpref);
|
|
gnma_route_remove(0, &gpref);
|
|
ret = router_fib_info2gnma_attr(&router_db_get(&newr, ni)->info,
|
|
&gattr);
|
|
if (ret)
|
|
return -1;
|
|
|
|
ret = gnma_route_create(0, &gpref, &gattr);
|
|
if (ret)
|
|
return -1;
|
|
}
|
|
|
|
if (diff_case_del(diff)) {
|
|
router_fib_key2gnma_prefix(&router_db_get(&oldr, oi)->key,
|
|
&gpref);
|
|
gnma_route_remove(0, &gpref);
|
|
}
|
|
|
|
if (diff_case_add(diff)) {
|
|
router_fib_key2gnma_prefix(&router_db_get(&newr, ni)->key, &gpref);
|
|
|
|
ret = router_fib_info2gnma_attr(&router_db_get(&newr, ni)->info,
|
|
&gattr);
|
|
if (ret)
|
|
return -1;
|
|
|
|
ret = gnma_route_create(0, &gpref, &gattr);
|
|
if (ret)
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
ucentral_router_fib_db_free(&oldr);
|
|
plat_state.router = newr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int plat_radius_hosts_list_set(struct plat_radius_hosts_list *hosts)
|
|
{
|
|
struct plat_radius_hosts_list *iter;
|
|
bool cache_changed = false;
|
|
int ret = 0;
|
|
size_t i;
|
|
|
|
/* Check cache and remove any host that is not present in
|
|
* requested CFG (same as for VLAN: if not present in cfg = to
|
|
* be removed).
|
|
*/
|
|
for (i = 0; i < plat_state.radius.hosts_keys_arr_size; ++i) {
|
|
if (!PLAT_RADIUS_HOST_EXISTS_IN_CFG(plat_state.radius.hosts_keys_arr[i].hostname, &hosts)) {
|
|
UC_LOG_DBG("Removing RADIUS server <%s> (not in cfg, present on system)\n",
|
|
plat_state.radius.hosts_keys_arr[i].hostname);
|
|
ret = gnma_radius_host_remove(&plat_state.radius.hosts_keys_arr[i]);
|
|
if (ret)
|
|
return ret;
|
|
cache_changed = true;
|
|
} else {
|
|
/* Special case, when host exists in cache and new CFG omitted password:
|
|
* - remove previous entry
|
|
* - recreate it without specifying password.
|
|
* Either way SONIC treats this host as if password is set
|
|
* explicitly, and might result in false-obfuscations of
|
|
* EAP exchange between RADIUS and switch.
|
|
*/
|
|
UCENTRAL_LIST_FOR_EACH_MEMBER(iter, &hosts) {
|
|
if (strcmp(plat_state.radius.hosts_keys_arr[i].hostname,
|
|
iter->host.hostname) == 0 &&
|
|
iter->host.passkey[0] == '\0') {
|
|
ret = gnma_radius_host_remove(&plat_state.radius.hosts_keys_arr[i]);
|
|
if (ret) {
|
|
UC_LOG_DBG("Failed to remove RADIUS host <%s> (new CFG pass is empty, tried to delete))\n",
|
|
plat_state.radius.hosts_keys_arr[i].hostname);
|
|
return ret;
|
|
}
|
|
cache_changed = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Add any new hosts that are present in requested CFG. */
|
|
UCENTRAL_LIST_FOR_EACH_MEMBER(iter, &hosts) {
|
|
struct gnma_radius_host_key key;
|
|
|
|
strcpy(key.hostname, iter->host.hostname);
|
|
|
|
ret = gnma_radius_host_add(&key, iter->host.passkey,
|
|
iter->host.auth_port,
|
|
iter->host.priority);
|
|
if (ret)
|
|
return ret;
|
|
cache_changed = true;
|
|
}
|
|
|
|
/* Reinit RADIUS hosts cache. */
|
|
if (cache_changed)
|
|
plat_state_radius_init();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int config_unit_apply(struct plat_cfg *cfg)
|
|
{
|
|
gnma_poe_power_mgmt_mode_t mgmt_mode;
|
|
int ret;
|
|
|
|
__poe_power_mgmt_str2num(cfg->unit.poe.power_mgmt, &mgmt_mode);
|
|
|
|
if (cfg->unit.poe.is_power_mgmt_set &&
|
|
plat_state.poe.power_mgmt != mgmt_mode) {
|
|
UC_LOG_DBG("Configuring unit.poe power mgmt mode <%d> to <%d>\n",
|
|
plat_state.poe.power_mgmt, mgmt_mode);
|
|
ret = gnma_poe_power_mgmt_set(mgmt_mode);
|
|
if (ret)
|
|
return ret;
|
|
plat_state.poe.power_mgmt = mgmt_mode;
|
|
}
|
|
|
|
if (cfg->unit.poe.is_usage_threshold_set &&
|
|
plat_state.poe.usage_threshold != cfg->unit.poe.usage_threshold) {
|
|
UC_LOG_DBG("Configuring unit.poe usage threshold <%d> to <%d>\n",
|
|
plat_state.poe.usage_threshold, cfg->unit.poe.usage_threshold);
|
|
ret = gnma_poe_usage_threshold_set(cfg->unit.poe.usage_threshold);
|
|
if (ret)
|
|
return ret;
|
|
plat_state.poe.usage_threshold = cfg->unit.poe.usage_threshold;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int plat_port_config_apply(struct plat_cfg *cfg)
|
|
{
|
|
size_t i;
|
|
int ret;
|
|
int is_8021x_reported = 0, is_poe_reported = 0;
|
|
|
|
BITMAP_FOR_EACH_BIT_SET(i, cfg->ports_to_cfg, MAX_NUM_OF_PORTS) {
|
|
UC_LOG_DBG("Configuring port <%s>: speed <%d> duplex <%d> state <%d>\n",
|
|
cfg->ports[i].name, cfg->ports[i].speed,
|
|
cfg->ports[i].duplex, cfg->ports[i].state);
|
|
ret = plat_port_admin_state_set(i, cfg->ports[i].state);
|
|
if (cfg->ports[i].state) {
|
|
ret |= plat_port_speed_set(i, cfg->ports[i].speed);
|
|
ret |= plat_port_duplex_set(i, cfg->ports[i].duplex);
|
|
}
|
|
|
|
if (ret)
|
|
return -1;
|
|
|
|
if (featsts[FEAT_AAA] == FEATSTS_OK) {
|
|
if (config_port_ieee8021x_apply(i, &cfg->ports[i]))
|
|
return -1;
|
|
} else if (!is_8021x_reported) {
|
|
CFG_LOG_CRIT(
|
|
"AAA feature is not initialized, skipping configuration");
|
|
is_8021x_reported = 1;
|
|
}
|
|
|
|
if (!BITMAP_TEST_BIT(plat_state.poe.ports_bmap, i))
|
|
continue;
|
|
|
|
if (featsts[FEAT_POE] == FEATSTS_OK) {
|
|
if (config_poe_port_apply(i, &cfg->ports[i]))
|
|
return -1;
|
|
} else if (!is_poe_reported) {
|
|
CFG_LOG_CRIT(
|
|
"POE feature is not initialized, skipping configuration");
|
|
is_poe_reported = 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int plat_dac_list_set(struct plat_ieee8021x_dac_list *hosts)
|
|
{
|
|
struct plat_ieee8021x_dac_list *iter;
|
|
bool cache_changed = false;
|
|
int ret = 0;
|
|
size_t i;
|
|
|
|
/*
|
|
* Check cache and remove any host that is not present in
|
|
* requested CFG (same as for VLAN: if not present in cfg = to
|
|
* be removed).
|
|
*/
|
|
for (i = 0; i < plat_state.ieee8021x.das_dac_keys_arr_size; ++i) {
|
|
if (!PLAT_DAC_HOST_EXISTS_IN_CFG(plat_state.ieee8021x.das_dac_keys_arr[i].hostname, &hosts)) {
|
|
UC_LOG_DBG("Removing DAC server <%s> (not in cfg, present on system)\n",
|
|
plat_state.ieee8021x.das_dac_keys_arr[i].hostname);
|
|
ret = gnma_ieee8021x_das_dac_host_remove(&plat_state.ieee8021x.das_dac_keys_arr[i]);
|
|
if (ret)
|
|
return ret;
|
|
cache_changed = true;
|
|
} else {
|
|
/* Special case, when host exists in cache and new CFG omitted password:
|
|
* - remove previous entry
|
|
* - recreate it without specifying password.
|
|
* Either way SONIC treats this host as if password is set
|
|
* explicitly, and might result in false-obfuscations of
|
|
* DaS / CoA exchange between DAC and switch.
|
|
*/
|
|
UCENTRAL_LIST_FOR_EACH_MEMBER(iter, &hosts) {
|
|
if (!strcmp(plat_state.ieee8021x.das_dac_keys_arr[i].hostname,
|
|
iter->host.hostname) &&
|
|
iter->host.passkey[0] == '\0') {
|
|
ret = gnma_ieee8021x_das_dac_host_remove(&plat_state.ieee8021x.das_dac_keys_arr[i]);
|
|
if (ret) {
|
|
UC_LOG_DBG("Failed to remove DAC host <%s> (new CFG pass is empty, tried to delete))\n",
|
|
plat_state.ieee8021x.das_dac_keys_arr[i].hostname);
|
|
return ret;
|
|
}
|
|
cache_changed = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Add any new hosts that are present in requested CFG. */
|
|
UCENTRAL_LIST_FOR_EACH_MEMBER(iter, &hosts) {
|
|
struct gnma_das_dac_host_key key;
|
|
|
|
strcpy(key.hostname, iter->host.hostname);
|
|
|
|
ret = gnma_ieee8021x_das_dac_host_add(&key, iter->host.passkey);
|
|
if (ret)
|
|
return ret;
|
|
cache_changed = true;
|
|
}
|
|
|
|
/* Reinit DAC hosts cache. */
|
|
if (cache_changed)
|
|
plat_state_ieee8021x_dac_list_init();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int config_ieee8021x_apply(struct plat_cfg *cfg)
|
|
{
|
|
int ret;
|
|
|
|
if (cfg->ieee8021x.is_auth_ctrl_enabled != plat_state.ieee8021x.is_auth_control_enabled) {
|
|
UC_LOG_DBG("802.1x: changing global auth ctrl state from %d to %d",
|
|
plat_state.ieee8021x.is_auth_control_enabled,
|
|
cfg->ieee8021x.is_auth_ctrl_enabled);
|
|
ret = gnma_ieee8021x_system_auth_control_set(cfg->ieee8021x.is_auth_ctrl_enabled);
|
|
if (ret) {
|
|
UC_LOG_DBG("802.1x: Failed to set global auth ctrl state.");
|
|
return ret;
|
|
}
|
|
plat_state.ieee8021x.is_auth_control_enabled = cfg->ieee8021x.is_auth_ctrl_enabled;
|
|
}
|
|
|
|
ret = plat_dac_list_set(cfg->ieee8021x.das_dac_list);
|
|
if (ret) {
|
|
UC_LOG_DBG("802.1x: DAS: Failed to configure DAC hosts list.");
|
|
return ret;
|
|
}
|
|
|
|
ret = plat_radius_hosts_list_set(cfg->radius_hosts_list);
|
|
if (ret) {
|
|
UC_LOG_DBG("802.1x: Failed to set RADIUS hosts list.");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int config_system_password_apply(struct plat_cfg *cfg)
|
|
{
|
|
int ret;
|
|
if (cfg->unit.system.password_changed) {
|
|
UC_LOG_DBG("Updating system password\n");
|
|
if ((ret = gnma_system_password_set(cfg->unit.system.password))) {
|
|
UC_LOG_ERR("Failed updating system password\n");
|
|
return ret;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int plat_config_apply(struct plat_cfg *cfg, uint32_t id)
|
|
{
|
|
int ret;
|
|
|
|
(void)id;
|
|
|
|
if (featsts[FEAT_CORE] != FEATSTS_OK) {
|
|
CFG_LOG_CRIT(
|
|
"core features are not initialized, the system is not stable");
|
|
}
|
|
|
|
ret = config_vlan_apply(cfg);
|
|
if (ret)
|
|
return -1;
|
|
|
|
ret = plat_port_config_apply(cfg);
|
|
if (ret)
|
|
return -1;
|
|
|
|
ret = config_unit_apply(cfg);
|
|
if (ret)
|
|
return -1;
|
|
|
|
ret = config_stp_apply(cfg);
|
|
if (ret)
|
|
return -1;
|
|
|
|
ret = config_vlan_ipv4_apply(cfg);
|
|
if (ret)
|
|
return -1;
|
|
|
|
ret = config_portl2_ipv4_apply(cfg);
|
|
if (ret)
|
|
return -1;
|
|
|
|
ret = config_vlan_dhcp_relay_apply(cfg);
|
|
if (ret)
|
|
return -1;
|
|
|
|
ret = config_metrics_apply(cfg);
|
|
if (ret)
|
|
return -1;
|
|
|
|
ret = config_router_apply(cfg);
|
|
if (ret)
|
|
return -1;
|
|
|
|
if (featsts[FEAT_AAA] == FEATSTS_OK) {
|
|
if (config_ieee8021x_apply(cfg))
|
|
return -1;
|
|
} else {
|
|
CFG_LOG_CRIT(
|
|
"AAA feature is not initialized, skipping configuration");
|
|
}
|
|
|
|
/* there is no rollback for password, so this should be run last */
|
|
ret = config_system_password_apply(cfg);
|
|
if (ret)
|
|
return -1;
|
|
|
|
plat_syslog_set(cfg->log_cfg, cfg->log_cfg_cnt);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void plat_config_destroy(struct plat_cfg *cfg)
|
|
{
|
|
struct plat_vlan_memberlist *member_node = NULL;
|
|
struct plat_radius_hosts_list *hosts_node = NULL;
|
|
struct plat_ports_list *port_node;
|
|
size_t i;
|
|
|
|
if (!cfg)
|
|
return; /* like free() */
|
|
|
|
ucentral_router_fib_db_free(&cfg->router);
|
|
|
|
/* TODO: do better */
|
|
BITMAP_FOR_EACH_BIT_SET(i, cfg->vlans_to_cfg, GNMA_MAX_VLANS)
|
|
{
|
|
UCENTRAL_LIST_DESTROY_SAFE(&cfg->vlans[i].members_list_head,
|
|
member_node);
|
|
|
|
cfg->vlans[i].members_list_head = NULL;
|
|
}
|
|
|
|
for (int i = 0; i < 0; ++i) {
|
|
UCENTRAL_LIST_DESTROY_SAFE(&cfg->port_isolation_cfg.sessions[i].uplink.ports_list,
|
|
port_node);
|
|
UCENTRAL_LIST_DESTROY_SAFE(&cfg->port_isolation_cfg.sessions[i].uplink.ports_list,
|
|
port_node);
|
|
}
|
|
free(cfg->port_isolation_cfg.sessions);
|
|
|
|
UCENTRAL_LIST_DESTROY_SAFE(&cfg->radius_hosts_list,
|
|
hosts_node);
|
|
}
|
|
|
|
/* TODO(vb) 1. will be hidden in plat 2. this approach does not survive
|
|
* upgrade. also it does not seem telemetry provide storage services, but need
|
|
* to double-check later */
|
|
int plat_metrics_save(const struct plat_metrics_cfg *cfg)
|
|
{
|
|
FILE *cfg_file = 0;
|
|
|
|
if (!cfg)
|
|
return -1;
|
|
|
|
cfg_file = fopen(cfgmetrics_path, "w+");
|
|
if (!cfg_file)
|
|
return -1;
|
|
|
|
fprintf(cfg_file, "%d\n", !!cfg->healthcheck.enabled);
|
|
fprintf(cfg_file, "%zu\n", cfg->healthcheck.interval);
|
|
fprintf(cfg_file, "%d\n", !!cfg->state.enabled);
|
|
fprintf(cfg_file, "%d\n", !!cfg->state.lldp_enabled);
|
|
fprintf(cfg_file, "%d\n", !!cfg->state.clients_enabled);
|
|
fprintf(cfg_file, "%zu\n", cfg->state.interval);
|
|
fprintf(cfg_file, "%s\n", cfg->state.public_ip_lookup);
|
|
|
|
fclose(cfg_file);
|
|
return 0;
|
|
}
|
|
|
|
int plat_metrics_restore(struct plat_metrics_cfg *cfg)
|
|
{
|
|
size_t len;
|
|
FILE *cfg_file = 0;
|
|
|
|
if (!cfg)
|
|
return -1;
|
|
|
|
cfg_file = fopen(cfgmetrics_path, "r");
|
|
if (!cfg_file)
|
|
return -1;
|
|
|
|
if (fscanf(cfg_file, "%d", &cfg->healthcheck.enabled) != 1 ||
|
|
fscanf(cfg_file, "%zu", &cfg->healthcheck.interval) != 1 ||
|
|
fscanf(cfg_file, "%d", &cfg->state.enabled) != 1 ||
|
|
fscanf(cfg_file, "%d", &cfg->state.lldp_enabled) != 1 ||
|
|
fscanf(cfg_file, "%d", &cfg->state.clients_enabled) != 1 ||
|
|
fscanf(cfg_file, "%zu", &cfg->state.interval) != 1 ||
|
|
!fgets(cfg->state.public_ip_lookup,
|
|
sizeof cfg->state.public_ip_lookup, cfg_file)) {
|
|
fclose(cfg_file);
|
|
return -1;
|
|
}
|
|
|
|
len = strlen(cfg->state.public_ip_lookup);
|
|
if (len && cfg->state.public_ip_lookup[len - 1] == '\n')
|
|
cfg->state.public_ip_lookup[len - 1] = 0;
|
|
|
|
/** FIXME: this should be read from cfg_file */
|
|
cfg->state.max_mac_count = METRICS_WIRED_CLIENTS_MAX_NUM;
|
|
|
|
fclose(cfg_file);
|
|
return 0;
|
|
}
|
|
|
|
int plat_diagnostic(char *res_path)
|
|
{
|
|
if (gnma_techsupport_start(res_path))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static size_t simple_wordexp(const char **s, char *out, size_t outsz,
|
|
size_t *toklen, int *delim)
|
|
{
|
|
int ch; /* current char */
|
|
int esc = 0; /* escape */
|
|
int q = 0; /* quote char */
|
|
size_t tl = 0, oi = 0; /* token length, output iterator */
|
|
|
|
while (**s == ' ' || **s == '\n' || **s == '\t' || **s == '\v' ||
|
|
**s == '\r' || **s == '\f') {
|
|
++*s;
|
|
}
|
|
|
|
for (ch = **s; **s; ch = *++*s) {
|
|
if (!esc && ch == '\\') {
|
|
esc = 1;
|
|
} else if (esc) {
|
|
if (q && q != ch) {
|
|
if (oi < outsz)
|
|
out[oi++] = '\\';
|
|
++tl;
|
|
}
|
|
if (oi < outsz)
|
|
out[oi++] = ch;
|
|
++tl;
|
|
esc = 0;
|
|
} else if (q) {
|
|
if (ch == q) {
|
|
q = 0;
|
|
} else {
|
|
if (oi < outsz)
|
|
out[oi++] = ch;
|
|
++tl;
|
|
}
|
|
} else {
|
|
if (ch == '"' || ch == '\'') {
|
|
q = ch;
|
|
} else {
|
|
if (ch == ' ' || ch == '\n' || ch == '\t' ||
|
|
ch == '\v' || ch == '\r' || ch == '\f') {
|
|
++*s;
|
|
break;
|
|
} else {
|
|
if (oi < outsz)
|
|
out[oi++] = ch;
|
|
++tl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (toklen)
|
|
*toklen = tl;
|
|
|
|
if (delim)
|
|
*delim = ch;
|
|
|
|
return oi;
|
|
}
|
|
|
|
static void *script_runner(void *p)
|
|
{
|
|
static __thread char *arg[SCRIPT_ARGMAX];
|
|
static __thread char token[SCRIPT_ARGMAX][SCRIPT_TOKLEN];
|
|
sigset_t sigset;
|
|
const char *script;
|
|
int timeout, i, n, wstatus = 0;
|
|
struct plat_run_script_result res = {0};
|
|
struct timespec run = {0}, now = {0}, start = {0};
|
|
ssize_t ri = 0, bi = 0;
|
|
pid_t pid = -1;
|
|
int fd = -1, rc = 0;
|
|
int exit_status = 0;
|
|
struct script_ctx *ctx = p;
|
|
|
|
memset(&sigset, 0, sizeof sigset);
|
|
if (sigemptyset(&sigset)) {
|
|
UC_LOG_CRIT("sigemptyset failed");
|
|
}
|
|
|
|
if (clock_gettime(CLOCK_MONOTONIC, &start)) {
|
|
UC_LOG_CRIT("clock_gettime(CLOCK_MONOTONIC): %s", strerror(errno));
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
script = script_ctx.script_buf;
|
|
for (i = 0; *script && !exit_status && bi < SCRIPT_OUTLEN - 1 && !rc; ++i) {
|
|
size_t sz;
|
|
int argc;
|
|
int ignore = 0, delim = 0;
|
|
|
|
for (argc = 0; argc < SCRIPT_ARGMAX && *script && delim != '\n';
|
|
++argc) {
|
|
sz = simple_wordexp(&script, token[argc], SCRIPT_TOKLEN,
|
|
0, &delim);
|
|
if (ignore) /* continue to parse */
|
|
continue;
|
|
if (sz == SCRIPT_TOKLEN) {
|
|
UC_LOG_ERR("the token is too long, ignoring the whole line");
|
|
ignore = 1;
|
|
continue;
|
|
}
|
|
token[argc][sz] = 0;
|
|
arg[argc] = token[argc];
|
|
}
|
|
|
|
if (ignore || argc < 1 || argc >= SCRIPT_ARGMAX)
|
|
continue;
|
|
|
|
arg[argc] = 0;
|
|
if (strcmp(arg[0], "ping") &&
|
|
strcmp(arg[0], "nslookup") &&
|
|
strcmp(arg[0], "traceroute")) {
|
|
continue;
|
|
}
|
|
|
|
/* TODO(vb) add cleaned up env */
|
|
pid = spawnp(arg[0], arg, 0, &sigset, -1, -1, 0, &fd, O_NONBLOCK);
|
|
if (pid < 0) {
|
|
UC_LOG_ERR("failed to spawn %s: %s", arg[0], strerror(errno));
|
|
rc = -1;
|
|
break;
|
|
}
|
|
|
|
while (1) {
|
|
struct pollfd pfd = { fd, POLLIN, 0 };
|
|
|
|
if (clock_gettime(CLOCK_MONOTONIC, &now)) {
|
|
UC_LOG_CRIT(
|
|
"clock_gettime(CLOCK_MONOTONIC): %s",
|
|
strerror(errno));
|
|
rc = -1;
|
|
goto child_finish;
|
|
}
|
|
|
|
run = sub_timespec(&now, &start);
|
|
if (run.tv_sec < 0) {
|
|
rc = -1;
|
|
UC_LOG_CRIT("now is behind start");
|
|
goto child_finish;
|
|
}
|
|
timeout = (ctx->t - run.tv_sec);
|
|
timeout = timeout < 0 ? 0 :
|
|
(timeout * 1000 + run.tv_nsec / 1000000L);
|
|
|
|
n = poll(&pfd, 1, timeout);
|
|
if (n < 0) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
UC_LOG_CRIT("poll failed: %s", strerror(errno));
|
|
rc = -1;
|
|
break;
|
|
}
|
|
if (n == 0) {
|
|
res.timeout_exceeded = 1;
|
|
break;
|
|
}
|
|
|
|
if (pfd.revents & POLLIN) {
|
|
while (1) {
|
|
ri = read(pfd.fd, &script_ctx.outbuf[bi],
|
|
SCRIPT_OUTLEN - bi - 1);
|
|
|
|
if (ri < 0) {
|
|
if (errno == EINTR)
|
|
continue;
|
|
if (errno == EWOULDBLOCK || errno == EAGAIN)
|
|
break;
|
|
UC_LOG_CRIT("read failed: %s", strerror(errno));
|
|
rc = -1;
|
|
goto child_finish;
|
|
}
|
|
|
|
if (ri == 0)
|
|
goto child_finish;
|
|
|
|
bi += ri;
|
|
if (bi >= SCRIPT_OUTLEN - 1) /* TODO(vb) chunked upload? move to callback */
|
|
goto child_finish;
|
|
}
|
|
}
|
|
|
|
if (pfd.revents & POLLHUP) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
child_finish:
|
|
close(fd);
|
|
fd = -1;
|
|
|
|
/* TODO(vb) put into pid namesapce if expanding functionality */
|
|
if (kill(pid, SIGKILL)) {
|
|
UC_LOG_CRIT("kill failed: %s", strerror(errno));
|
|
}
|
|
|
|
while (1) {
|
|
while ((n = waitpid(pid, &wstatus, 0)) < 0 && errno == EINTR);
|
|
if (n <= 0)
|
|
break;
|
|
if (WIFEXITED(wstatus)) {
|
|
exit_status = WEXITSTATUS(wstatus);
|
|
break;
|
|
}
|
|
if (WIFSIGNALED(wstatus)) {
|
|
exit_status = 128 + WTERMSIG(wstatus);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
exit:
|
|
free(script_ctx.script_buf);
|
|
|
|
script_ctx.outbuf[bi] = 0;
|
|
res.exit_status = exit_status;
|
|
res.stdout_string = script_ctx.outbuf;
|
|
res.stdout_string_len = bi;
|
|
|
|
if (ctx->cb)
|
|
ctx->cb(rc, &res, ctx->ctx);
|
|
|
|
free(script_ctx.outbuf);
|
|
script_lock_release();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int plat_run_script(struct plat_run_script *p)
|
|
{
|
|
size_t len;
|
|
|
|
if (strcmp(p->type, "shell")) {
|
|
UC_LOG_ERR("only type 'shell' script is supported");
|
|
return -1;
|
|
}
|
|
|
|
if (p->timeout > INT_MAX) {
|
|
UC_LOG_ERR("invalid timeout");
|
|
return -1;
|
|
}
|
|
|
|
if (script_lock_aquire()) {
|
|
UC_LOG_ERR("max 1 script at a time");
|
|
return -1;
|
|
}
|
|
|
|
if (script_ctx.is_tid_valid) {
|
|
if (pthread_join(script_ctx.tid, 0)) {
|
|
UC_LOG_CRIT("pthread_join: %s", strerror(errno));
|
|
}
|
|
}
|
|
script_ctx = (struct script_ctx){
|
|
.cb = p->cb,
|
|
.ctx = p->ctx,
|
|
};
|
|
script_ctx.t = p->timeout;
|
|
|
|
len = strlen(p->script_base64);
|
|
if (!(script_ctx.script_buf =
|
|
calloc(1, BASE64_DECODE_OUT_SIZE(len) + 1))) {
|
|
goto exit;
|
|
}
|
|
script_ctx.script_bufsz = base64_decode(p->script_base64, len,
|
|
(void *)script_ctx.script_buf);
|
|
if (!script_ctx.script_bufsz) {
|
|
UC_LOG_ERR("failed to decode base64 script text");
|
|
goto exit;
|
|
}
|
|
|
|
if (!(script_ctx.outbuf = malloc(SCRIPT_OUTLEN)))
|
|
goto exit;
|
|
|
|
if (pthread_create(&script_ctx.tid, 0, script_runner, &script_ctx)) {
|
|
UC_LOG_ERR("pthread_create: %s", strerror(errno));
|
|
goto exit;
|
|
}
|
|
|
|
script_ctx.is_tid_valid = 1;
|
|
exit:
|
|
if (!script_ctx.is_tid_valid) {
|
|
free(script_ctx.script_buf);
|
|
free(script_ctx.outbuf);
|
|
script_lock_release();
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|