diff --git a/src/ucentral-client/include/ucentral-platform.h b/src/ucentral-client/include/ucentral-platform.h index dd1a204..5487a4a 100644 --- a/src/ucentral-client/include/ucentral-platform.h +++ b/src/ucentral-client/include/ucentral-platform.h @@ -41,6 +41,7 @@ extern "C" { */ #define PID_TO_NAME(p, name) sprintf(name, "Ethernet%hu", p) #define NAME_TO_PID(p, name) sscanf((name), "Ethernet%hu", (p)) +#define VLAN_TO_NAME(v, name) sprintf((name), "Vlan%hu", (v)) struct plat_vlan_memberlist; struct plat_port_vlan; @@ -64,6 +65,18 @@ enum plat_ieee8021x_port_host_mode { PLAT_802_1X_PORT_HOST_MODE_SINGLE_HOST, }; +enum plat_ieee8021x_das_auth_type { + PLAT_802_1X_DAS_AUTH_TYPE_ANY, + PLAT_802_1X_DAS_AUTH_TYPE_ALL, + PLAT_802_1X_DAS_AUTH_TYPE_SESSION_KEY, +}; + +enum plat_igmp_version { + PLAT_IGMP_VERSION_1, + PLAT_IGMP_VERSION_2, + PLAT_IGMP_VERSION_3 +}; + #define UCENTRAL_PORT_LLDP_PEER_INFO_MAX_MGMT_IPS (2) /* Interface LLDP peer's data, as defined in interface.lldp.yml*/ struct plat_port_lldp_peer_info { @@ -252,10 +265,27 @@ struct plat_port_l2 { struct plat_ipv4 ipv4; }; +struct plat_igmp { + bool exist; + bool snooping_enabled; + bool querier_enabled; + bool fast_leave_enabled; + uint32_t query_interval; + uint32_t last_member_query_interval; + uint32_t max_response_time; + enum plat_igmp_version version; + size_t num_groups; + struct { + struct in_addr addr; + struct plat_ports_list *egress_ports_list; + } *groups; +}; + struct plat_port_vlan { struct plat_vlan_memberlist *members_list_head; struct plat_ipv4 ipv4; struct plat_dhcp dhcp; + struct plat_igmp igmp; uint16_t id; uint16_t mstp_instance; }; @@ -282,6 +312,18 @@ struct plat_syslog_cfg { char host[SYSLOG_CFG_FIELD_STR_MAX_LEN]; }; +struct plat_enabled_service_cfg { + struct { + bool enabled; + } ssh; + struct telnet { + bool enabled; + } telnet; + struct { + bool enabled; + } http; +}; + struct plat_rtty_cfg { char id[RTTY_CFG_FIELD_STR_MAX_LEN]; char passwd[RTTY_CFG_FIELD_STR_MAX_LEN]; @@ -376,6 +418,31 @@ struct plat_radius_hosts_list { struct plat_radius_host host; }; +struct plat_ieee8021x_dac_host { + char hostname[RADIUS_CFG_HOSTNAME_STR_MAX_LEN]; + char passkey[RADIUS_CFG_PASSKEY_STR_MAX_LEN]; +}; + +struct plat_ieee8021x_dac_list { + struct plat_ieee8021x_dac_list *next; + struct plat_ieee8021x_dac_host host; +}; + +struct plat_port_isolation_session_ports { + struct plat_ports_list *ports_list; +}; + +struct plat_port_isolation_session { + uint64_t id; + struct plat_port_isolation_session_ports uplink; + struct plat_port_isolation_session_ports downlink; +}; + +struct plat_port_isolation_cfg { + struct plat_port_isolation_session *sessions; + size_t sessions_num; +}; + struct plat_cfg { struct plat_unit unit; /* Alloc all ports, but access them only if bit is set. */ @@ -385,6 +452,7 @@ struct plat_cfg { BITMAP_DECLARE(vlans_to_cfg, MAX_VLANS); struct plat_metrics_cfg metrics; struct plat_syslog_cfg *log_cfg; + struct plat_enabled_service_cfg enabled_services_cfg; /* Port's interfaces (provide l2 iface w/o bridge caps) */ struct plat_port_l2 portsl2[MAX_NUM_OF_PORTS]; struct ucentral_router router; @@ -393,7 +461,17 @@ struct plat_cfg { /* Instance zero is for global instance (like common values in rstp) */ struct plat_stp_instance_cfg stp_instances[MAX_VLANS]; struct plat_radius_hosts_list *radius_hosts_list; - bool ieee8021x_is_auth_ctrl_enabled; + struct { + bool is_auth_ctrl_enabled; + bool bounce_port_ignore; + bool disable_port_ignore; + bool ignore_server_key; + bool ignore_session_key; + char server_key[RADIUS_CFG_PASSKEY_STR_MAX_LEN]; + enum plat_ieee8021x_das_auth_type das_auth_type; + struct plat_ieee8021x_dac_list *das_dac_list; + } ieee8021x; + struct plat_port_isolation_cfg port_isolation_cfg; }; struct plat_learned_mac_addr { @@ -503,15 +581,57 @@ enum { PLAT_REBOOT_CAUSE_UNAVAILABLE, }; +enum sfp_form_factor { + UCENTRAL_SFP_FORM_FACTOR_NA = 0, + + UCENTRAL_SFP_FORM_FACTOR_SFP, + UCENTRAL_SFP_FORM_FACTOR_SFP_PLUS, + UCENTRAL_SFP_FORM_FACTOR_SFP_28, + UCENTRAL_SFP_FORM_FACTOR_SFP_DD, + UCENTRAL_SFP_FORM_FACTOR_QSFP, + UCENTRAL_SFP_FORM_FACTOR_QSFP_PLUS, + UCENTRAL_SFP_FORM_FACTOR_QSFP_28, + UCENTRAL_SFP_FORM_FACTOR_QSFP_DD +}; + +enum sfp_link_mode { + UCENTRAL_SFP_LINK_MODE_NA = 0, + + UCENTRAL_SFP_LINK_MODE_1000_X, + UCENTRAL_SFP_LINK_MODE_2500_X, + UCENTRAL_SFP_LINK_MODE_4000_SR, + UCENTRAL_SFP_LINK_MODE_10G_SR, + UCENTRAL_SFP_LINK_MODE_25G_SR, + UCENTRAL_SFP_LINK_MODE_40G_SR, + UCENTRAL_SFP_LINK_MODE_50G_SR, + UCENTRAL_SFP_LINK_MODE_100G_SR, +}; + +struct plat_port_transceiver_info { + char vendor_name[64]; + char part_number[64]; + char serial_number[64]; + char revision[64]; + enum sfp_form_factor form_factor; + enum sfp_link_mode *supported_link_modes; + size_t num_supported_link_modes; + float temperature; + float tx_optical_power; + float rx_optical_power; + float max_module_power; +}; + struct plat_port_info { struct plat_port_counters stats; struct plat_port_lldp_peer_info lldp_peer_info; struct plat_ieee8021x_port_info ieee8021x_info; + struct plat_port_transceiver_info transceiver_info; uint32_t uptime; uint32_t speed; uint8_t carrier_up; uint8_t duplex; uint8_t has_lldp_peer_info; + uint8_t has_transceiver_info; char name[PORT_MAX_NAME_LEN]; }; @@ -525,6 +645,17 @@ struct plat_system_info { double load_average[3]; /* 1, 5, 15 minutes load average */ }; +struct plat_iee8021x_coa_counters { + uint64_t coa_req_received; + uint64_t coa_ack_sent; + uint64_t coa_nak_sent; + uint64_t coa_ignored; + uint64_t coa_wrong_attr; + uint64_t coa_wrong_attr_value; + uint64_t coa_wrong_session_context; + uint64_t coa_administratively_prohibited_req; +}; + struct plat_state_info { struct plat_poe_state poe_state; struct plat_poe_port_state poe_ports_state[MAX_NUM_OF_PORTS]; @@ -532,10 +663,13 @@ struct plat_state_info { struct plat_port_info *port_info; int port_info_count; + struct plat_port_vlan *vlan_info; + size_t vlan_info_count; struct plat_learned_mac_addr *learned_mac_list; size_t learned_mac_list_size; struct plat_system_info system_info; + struct plat_iee8021x_coa_counters ieee8021x_global_coa_counters; }; struct plat_upgrade_info { diff --git a/src/ucentral-client/platform/brcm-sonic/gnma/gnma_common.c b/src/ucentral-client/platform/brcm-sonic/gnma/gnma_common.c index f715ff3..a250091 100644 --- a/src/ucentral-client/platform/brcm-sonic/gnma/gnma_common.c +++ b/src/ucentral-client/platform/brcm-sonic/gnma/gnma_common.c @@ -4090,25 +4090,12 @@ int gnma_stp_mode_set(gnma_stp_mode_t mode, struct gnma_stp_attr *attr) int err = GNMA_ERR_COMMON, ret; char gpath[256]; + /* delete global config since you cannot change the stp mode otherwise */ + gnmi_jsoni_del(main_switch, + "/sonic-spanning-tree:sonic-spanning-tree/STP/STP_LIST[keyleaf=GLOBAL]", + DEFAULT_TIMEOUT_US); if (mode == GNMA_STP_MODE_NONE) { - gnmi_jsoni_del( - main_switch, - "/sonic-spanning-tree:sonic-spanning-tree/STP_VLAN", - DEFAULT_TIMEOUT_US); - gnmi_jsoni_del( - main_switch, - "/sonic-spanning-tree:sonic-spanning-tree/STP_PORT", - DEFAULT_TIMEOUT_US); - gnmi_jsoni_set( - main_switch, - "/sonic-spanning-tree:sonic-spanning-tree/STP/STP_LIST[keyleaf=GLOBAL]/mode", - "{\"sonic-spanning-tree:mode\": \"pvst\"}", - DEFAULT_TIMEOUT_US); - - sprintf(&gpath[0], "/sonic-spanning-tree:sonic-spanning-tree"); - if (!gnmi_jsoni_del(main_switch, gpath, DEFAULT_TIMEOUT_US)) - err = 0; - + err = 0; goto out; } @@ -4134,15 +4121,21 @@ int gnma_stp_mode_set(gnma_stp_mode_t mode, struct gnma_stp_attr *attr) ret = 1; ret &= !!cJSON_AddNumberToObject(obj, "priority", attr->priority); ret &= !!cJSON_AddStringToObject(obj, "keyleaf", "GLOBAL"); + ret &= !!cJSON_AddBoolToObject(obj, "bpdu_filter", false); switch (mode) { case GNMA_STP_MODE_PVST: ret &= !!cJSON_AddStringToObject(obj, "mode", "pvst"); + ret &= !!cJSON_AddBoolToObject(obj, "portfast", false); + ret &= !!cJSON_AddNumberToObject(obj, "rootguard_timeout", 30); break; case GNMA_STP_MODE_RPVST: ret &= !!cJSON_AddStringToObject(obj, "mode", "rpvst"); + ret &= !!cJSON_AddBoolToObject(obj, "loop_guard", false); + ret &= !!cJSON_AddNumberToObject(obj, "rootguard_timeout", 30); break; case GNMA_STP_MODE_MST: ret &= !!cJSON_AddStringToObject(obj, "mode", "mst"); + ret &= !!cJSON_AddBoolToObject(obj, "loop_guard", false); break; default: goto out; @@ -4366,7 +4359,7 @@ out: } -int gnma_stp_vids_enable(uint32_t list_size, uint16_t *vid_list) +int gnma_stp_vids_set(uint32_t list_size, uint16_t *vid_list, bool enable) { cJSON *root = NULL, *obj, *arr; int err = GNMA_ERR_COMMON, ret; @@ -4394,7 +4387,7 @@ int gnma_stp_vids_enable(uint32_t list_size, uint16_t *vid_list) ret = 1; sprintf(&vidstr[0], "Vlan%u", vid_list[i]); ret &= !!cJSON_AddStringToObject(obj, "name", &vidstr[0]); - ret &= !!cJSON_AddBoolToObject(obj, "enabled", true); + ret &= !!cJSON_AddBoolToObject(obj, "enabled", enable); if (!ret) goto out; } @@ -4408,47 +4401,23 @@ out: return err; } -int gnma_stp_vids_enable_all(void) +int gnma_stp_vids_set_all(bool enable) { - cJSON *root = NULL, *obj, *arr; - int err = GNMA_ERR_COMMON, ret; - char gpath[256], vidstr[64]; - uint32_t i, from = 1, to = 4096; + BITMAP_DECLARE(vlans, GNMA_MAX_VLANS); + uint16_t vid_list[GNMA_MAX_VLANS]; + size_t i, num_vlans = 0; + int ret; - sprintf(&gpath[0], "/sonic-spanning-tree:sonic-spanning-tree/STP_VLAN"); + ret = gnma_vlan_list_get(vlans); + if (ret) + return ret; - root = cJSON_CreateObject(); - if (!root) - goto out; - - obj = cJSON_AddObjectToObject(root, "sonic-spanning-tree:STP_VLAN"); - arr = cJSON_AddArrayToObject(obj, "STP_VLAN_LIST"); - if (!arr) - goto out; - - for (i = from; i < to; i++) { - obj = cJSON_CreateObject(); - if (!cJSON_AddItemToArray(arr, obj)) { - cJSON_Delete(obj); /* Bcs root is not referenced */ - goto out; - } - - ret = 1; - sprintf(&vidstr[0], "Vlan%u", i); - ret &= !!cJSON_AddStringToObject(obj, "name", &vidstr[0]); - ret &= !!cJSON_AddBoolToObject(obj, "enabled", true); - if (!ret) - goto out; + BITMAP_FOR_EACH_BIT_SET(i, vlans, GNMA_MAX_VLANS) { + vid_list[num_vlans] = i; + num_vlans++; } - if (gnmi_json_object_set(main_switch, gpath, root, DEFAULT_TIMEOUT_US)) - goto out; - - err = 0; -out: - cJSON_Delete(root); - return err; - + return gnma_stp_vids_set(num_vlans, vid_list, enable); } static int gnma_stp_vid_enable(uint16_t vid, struct gnma_stp_attr *attr) @@ -4496,12 +4465,7 @@ out: static int gnma_stp_vid_disable(uint16_t vid) { - char gpath[256]; - - sprintf(&gpath[0], "/sonic-spanning-tree:sonic-spanning-tree/STP_VLAN/STP_VLAN_LIST[name=Vlan%u]", - vid); - - gnmi_jsoni_del(main_switch, gpath, DEFAULT_TIMEOUT_US); + gnma_stp_vids_set(1, &vid, false); return 0; } @@ -4640,6 +4604,611 @@ int gnma_ieee8021x_system_auth_clients_get(char *buf, size_t buf_size) buf, buf_size, DEFAULT_TIMEOUT_US); } +int gnma_ieee8021x_das_bounce_port_ignore_set(bool bounce_port_ignore) +{ + char *gpath = "/openconfig-das:das/das-global-config-table/config/ignore-bounce-port"; + int ret = GNMA_ERR_COMMON; + cJSON *root; + + if (!(root = cJSON_CreateObject())) + goto err_alloc; + + if (!cJSON_AddBoolToObject(root, "openconfig-das:ignore-bounce-port", + bounce_port_ignore)) + goto err_json; + + if (gnmi_json_object_set(main_switch, gpath, root, + DEFAULT_TIMEOUT_US)) + goto err_json; + + ret = GNMA_OK; +err_json: + cJSON_Delete(root); +err_alloc: + return ret; +} + +int gnma_ieee8021x_das_bounce_port_ignore_get(bool *bounce_port_ignore) +{ + cJSON *parsed_res, *bounce_port; + char *buf = NULL; + char *gpath; + int ret; + + ret = asprintf(&gpath, + "/openconfig-das:das/das-global-config-table/config/ignore-bounce-port"); + if (ret == -1) { + ret = GNMA_ERR_COMMON; + goto err_path_alloc; + } + + ret = gnmi_jsoni_get_alloc(main_switch, &gpath[0], &buf, 0, + DEFAULT_TIMEOUT_US); + if (ret) { + ret = GNMA_ERR_COMMON; + goto err_gnmi_get; + } + + parsed_res = cJSON_Parse(buf); + ZFREE(buf); + if (!parsed_res) { + ret = GNMA_ERR_COMMON; + goto err_gnmi_parse; + } + + bounce_port = cJSON_GetObjectItemCaseSensitive(parsed_res, "openconfig-das:ignore-bounce-port"); + *bounce_port_ignore = cJSON_IsTrue(bounce_port); + + cJSON_Delete(parsed_res); +err_gnmi_parse: +err_gnmi_get: + free(gpath); +err_path_alloc: + return ret; +} + +int gnma_ieee8021x_das_disable_port_ignore_set(bool disable_port_ignore) +{ + char *gpath = "/openconfig-das:das/das-global-config-table/config/ignore-disable-port"; + int ret = GNMA_ERR_COMMON; + cJSON *root; + + if (!(root = cJSON_CreateObject())) + goto err_alloc; + + if (!cJSON_AddBoolToObject(root, "openconfig-das:ignore-disable-port", + disable_port_ignore)) + goto err_json; + + if (gnmi_json_object_set(main_switch, gpath, root, + DEFAULT_TIMEOUT_US)) + goto err_json; + + ret = GNMA_OK; +err_json: + cJSON_Delete(root); +err_alloc: + return ret; +} + +int gnma_ieee8021x_das_disable_port_ignore_get(bool *disable_port_ignore) +{ + cJSON *parsed_res, *disable_port; + char *buf = NULL; + char *gpath; + int ret; + + ret = asprintf(&gpath, + "/openconfig-das:das/das-global-config-table/config/ignore-disable-port"); + if (ret == -1) { + ret = GNMA_ERR_COMMON; + goto err_path_alloc; + } + + ret = gnmi_jsoni_get_alloc(main_switch, &gpath[0], &buf, 0, + DEFAULT_TIMEOUT_US); + if (ret) { + ret = GNMA_ERR_COMMON; + goto err_gnmi_get; + } + + parsed_res = cJSON_Parse(buf); + ZFREE(buf); + if (!parsed_res) { + ret = GNMA_ERR_COMMON; + goto err_gnmi_parse; + } + + disable_port = cJSON_GetObjectItemCaseSensitive(parsed_res, "openconfig-das:ignore-disable-port"); + *disable_port_ignore = cJSON_IsTrue(disable_port); + + cJSON_Delete(parsed_res); +err_gnmi_parse: +err_gnmi_get: + free(gpath); +err_path_alloc: + return ret; +} + +int gnma_ieee8021x_das_ignore_server_key_set(bool ignore_server_key) +{ + char *gpath = "/openconfig-das:das/das-global-config-table/config/ignore-server-key"; + int ret = GNMA_ERR_COMMON; + cJSON *root; + + if (!(root = cJSON_CreateObject())) + goto err_alloc; + + if (!cJSON_AddBoolToObject(root, "openconfig-das:ignore-server-key", + ignore_server_key)) + goto err_json; + + if (gnmi_json_object_set(main_switch, gpath, root, + DEFAULT_TIMEOUT_US)) + goto err_json; + + ret = GNMA_OK; +err_json: + cJSON_Delete(root); +err_alloc: + return ret; +} + +int gnma_ieee8021x_das_ignore_server_key_get(bool *ignore_server_key) +{ + cJSON *parsed_res, *server_key; + char *buf = NULL; + char *gpath; + int ret; + + ret = asprintf(&gpath, + "/openconfig-das:das/das-global-config-table/config/ignore-server-key"); + if (ret == -1) { + ret = GNMA_ERR_COMMON; + goto err_path_alloc; + } + + ret = gnmi_jsoni_get_alloc(main_switch, &gpath[0], &buf, 0, + DEFAULT_TIMEOUT_US); + if (ret) { + ret = GNMA_ERR_COMMON; + goto err_gnmi_get; + } + + parsed_res = cJSON_Parse(buf); + ZFREE(buf); + if (!parsed_res) { + ret = GNMA_ERR_COMMON; + goto err_gnmi_parse; + } + + server_key = cJSON_GetObjectItemCaseSensitive(parsed_res, "openconfig-das:ignore-server-key"); + *ignore_server_key = cJSON_IsTrue(server_key); + + cJSON_Delete(parsed_res); +err_gnmi_parse: +err_gnmi_get: + free(gpath); +err_path_alloc: + return ret; +} + +int gnma_ieee8021x_das_ignore_session_key_set(bool ignore_session_key) +{ + char *gpath = "/openconfig-das:das/das-global-config-table/config/ignore-session-key"; + int ret = GNMA_ERR_COMMON; + cJSON *root; + + if (!(root = cJSON_CreateObject())) + goto err_alloc; + + if (!cJSON_AddBoolToObject(root, "openconfig-das:ignore-session-key", + ignore_session_key)) + goto err_json; + + if (gnmi_json_object_set(main_switch, gpath, root, + DEFAULT_TIMEOUT_US)) + goto err_json; + + ret = GNMA_OK; +err_json: + cJSON_Delete(root); +err_alloc: + return ret; +} + +int gnma_ieee8021x_das_ignore_session_key_get(bool *ignore_session_key) +{ + cJSON *parsed_res, *server_key; + char *buf = NULL; + char *gpath; + int ret; + + ret = asprintf(&gpath, + "/openconfig-das:das/das-global-config-table/config/ignore-session-key"); + if (ret == -1) { + ret = GNMA_ERR_COMMON; + goto err_path_alloc; + } + + ret = gnmi_jsoni_get_alloc(main_switch, &gpath[0], &buf, 0, + DEFAULT_TIMEOUT_US); + if (ret) { + ret = GNMA_ERR_COMMON; + goto err_gnmi_get; + } + + parsed_res = cJSON_Parse(buf); + ZFREE(buf); + if (!parsed_res) { + ret = GNMA_ERR_COMMON; + goto err_gnmi_parse; + } + + server_key = cJSON_GetObjectItemCaseSensitive(parsed_res, "openconfig-das:ignore-session-key"); + *ignore_session_key = cJSON_IsTrue(server_key); + + cJSON_Delete(parsed_res); +err_gnmi_parse: +err_gnmi_get: + free(gpath); +err_path_alloc: + return ret; +} + +int gnma_ieee8021x_das_auth_type_key_set(gnma_das_auth_type_t auth_type) +{ + char *gpath = "/openconfig-das:das/das-global-config-table/state/das-auth-type"; + int ret = GNMA_ERR_COMMON; + cJSON *root; + + if (!(root = cJSON_CreateObject())) + goto err_alloc; + + if (auth_type == GNMA_802_1X_DAS_AUTH_TYPE_ANY) { + if (!cJSON_AddStringToObject(root, "openconfig-das:das-auth-type", "ANY")) + goto err_json; + } else if (auth_type == GNMA_802_1X_DAS_AUTH_TYPE_ALL) { + if (!cJSON_AddStringToObject(root, "openconfig-das:das-auth-type", "ALL")) + goto err_json; + } else if (auth_type == GNMA_802_1X_DAS_AUTH_TYPE_SESSION_KEY) { + if (!cJSON_AddStringToObject(root, "openconfig-das:das-auth-type", "SESSION_KEY")) + goto err_json; + } else { + goto err_json; + } + + if (gnmi_json_object_set(main_switch, gpath, root, + DEFAULT_TIMEOUT_US)) + goto err_json; + + ret = GNMA_OK; +err_json: + cJSON_Delete(root); +err_alloc: + return ret; +} + +int gnma_ieee8021x_das_auth_type_key_get(gnma_das_auth_type_t *auth_type) +{ + cJSON *parsed_res, *auth; + char *buf = NULL; + char *gpath; + int ret; + + ret = asprintf(&gpath, + "/openconfig-das:das/das-global-config-table/config/das-auth-type"); + if (ret == -1) { + ret = GNMA_ERR_COMMON; + goto err_path_alloc; + } + + ret = gnmi_jsoni_get_alloc(main_switch, &gpath[0], &buf, 0, + DEFAULT_TIMEOUT_US); + if (ret) { + ret = GNMA_ERR_COMMON; + goto err_gnmi_get; + } + + parsed_res = cJSON_Parse(buf); + ZFREE(buf); + if (!parsed_res) { + ret = GNMA_ERR_COMMON; + goto err_gnmi_parse; + } + + auth = cJSON_GetObjectItemCaseSensitive(parsed_res, "openconfig-das:das-auth-type"); + if (!auth || !cJSON_GetStringValue(auth)) + goto err_gnmi_parse; + + if (!strcmp("ANY", cJSON_GetStringValue(auth))) + *auth_type = GNMA_802_1X_DAS_AUTH_TYPE_ANY; + else if (!strcmp("ALL", cJSON_GetStringValue(auth))) + *auth_type = GNMA_802_1X_DAS_AUTH_TYPE_ALL; + else if (!strcmp("SESSION_KEY", cJSON_GetStringValue(auth))) + *auth_type = GNMA_802_1X_DAS_AUTH_TYPE_SESSION_KEY; + else + ret = GNMA_ERR_COMMON; + + cJSON_Delete(parsed_res); +err_gnmi_parse: +err_gnmi_get: + free(gpath); +err_path_alloc: + return ret; +} + +int gnma_ieee8021x_das_dac_hosts_list_get(size_t *list_size, + struct gnma_das_dac_host_key *hosts_list) +{ + cJSON *parsed_res, *host, *hosts_arr, *addr; + uint16_t arr_len; + char *buf = 0; + char *gpath; + int ret; + + ret = asprintf(&gpath, + "/openconfig-das:das/das-client-config-table/das-client-config-table-entry"); + if (ret == -1) { + ret = GNMA_ERR_COMMON; + goto err_path_alloc; + } + + ret = gnmi_jsoni_get_alloc(main_switch, &gpath[0], &buf, 0, + DEFAULT_TIMEOUT_US); + if (ret) { + ret = GNMA_ERR_COMMON; + goto err_gnmi_get; + } + + parsed_res = cJSON_Parse(buf); + ZFREE(buf); + if (!parsed_res) { + ret = GNMA_ERR_COMMON; + goto err_gnmi_parse; + } + + hosts_arr = cJSON_GetObjectItemCaseSensitive(parsed_res, + "openconfig-das:das-client-config-table-entry"); + + arr_len = 0; + cJSON_ArrayForEach(host, hosts_arr) + arr_len++; + + if (arr_len > *list_size) { + ret = GNMA_ERR_OVERFLOW; + *list_size = arr_len; + goto err_overflow; + } + + memset(hosts_list, 0, sizeof(*hosts_list) * (*list_size)); + + *list_size = 0; + cJSON_ArrayForEach(host, hosts_arr) { + addr = cJSON_GetObjectItemCaseSensitive(host, "clientaddress"); + if (!addr) { + ret = GNMA_ERR_COMMON; + goto err_gnmi_check_arr; + } + strcpy(hosts_list[*list_size].hostname, + cJSON_GetStringValue(addr)); + (*list_size)++; + } + + ret = 0; + +err_overflow: +err_gnmi_check_arr: + cJSON_Delete(parsed_res); +err_gnmi_parse: +err_gnmi_get: + free(gpath); +err_path_alloc: + return ret; +} + +int gnma_ieee8021x_das_dac_host_add(struct gnma_das_dac_host_key *key, + const char *passkey) +{ + cJSON *server_item; + cJSON *servers_arr; + cJSON *config; + cJSON *root; + char *path; + int ret; + + ret = asprintf(&path, + "/openconfig-das:das/das-client-config-table/das-client-config-table-entry"); + if (ret == -1) { + ret = GNMA_ERR_COMMON; + goto err_path_alloc; + } + + /* + { + "openconfig-das:das-client-config-table-entry": [ + { + "clientaddress": "test.com", + "config": { + "clientaddress": "test.com", + "encrypted": false, + "server-key": "test" + } + } + ] + } + */ + root = cJSON_CreateObject(); + servers_arr = cJSON_AddArrayToObject(root, "openconfig-das:das-client-config-table-entry"); + server_item = cJSON_CreateObject(); + config = cJSON_AddObjectToObject(server_item, "config"); + if (!cJSON_AddStringToObject(server_item, "clientaddress", key->hostname) || + !cJSON_AddBoolToObject(config, "encrypted", false) || + !cJSON_AddStringToObject(config, "clientaddress", key->hostname) || + !cJSON_AddItemToArray(servers_arr, server_item)) { + cJSON_Delete(server_item); + ret = GNMA_ERR_COMMON; + goto err_val_alloc; + } + + if (passkey[0] != '\0' && !cJSON_AddStringToObject(config, "server-key", passkey)) { + ret = GNMA_ERR_COMMON; + goto err_val_alloc; + } + + ret = gnmi_json_object_set(main_switch, path, root, DEFAULT_TIMEOUT_US); + if (ret) { + ret = GNMA_ERR_COMMON; + goto err_req_fail; + } + + ret = 0; +err_req_fail: +err_val_alloc: + cJSON_Delete(root); + free(path); +err_path_alloc: + return ret; +} + +int gnma_ieee8021x_das_dac_host_remove(struct gnma_das_dac_host_key *key) +{ + char *path; + int ret; + + ret = asprintf(&path, + "/openconfig-das:das/das-client-config-table/das-client-config-table-entry[clientaddress=%s]/", + key->hostname); + if (ret == -1) { + ret = GNMA_ERR_COMMON; + goto err_path_alloc; + } + + ret = gnmi_jsoni_del(main_switch, path, DEFAULT_TIMEOUT_US); + if (ret) { + ret = GNMA_ERR_COMMON; + goto err_req_fail; + } + + ret = 0; +err_req_fail: + free(path); +err_path_alloc: + return ret; +} + +static const char * +gnma_ieee8021x_das_dac_stats_type_to_string(gnma_ieee8021x_das_dac_stat_type_t counter_id) +{ + static struct { + gnma_ieee8021x_das_dac_stat_type_t enu_value; + const char *str_value; + } stats_enum_to_str[] = { + { .enu_value = GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_PKTS, .str_value = "num_coa_requests_received"}, + { .enu_value = GNMA_IEEE8021X_DAS_DAC_STAT_OUT_COA_ACK_PKTS, .str_value = "num_coa_ack_responses_sent"}, + { .enu_value = GNMA_IEEE8021X_DAS_DAC_STAT_OUT_COA_NAK_PKTS, .str_value = "num_coa_nak_responses_sent"}, + { .enu_value = GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_IGNORED_PKTS, .str_value = "num_coa_requests_ignored"}, + { .enu_value = GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_WRONG_ATTR_PKTS, .str_value = "num_coa_missing_unsupported_attributes_requests"}, + { .enu_value = GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_WRONG_ATTR_VALUE_PKTS, .str_value = "num_coa_invalid_attribute_value_requests"}, + { .enu_value = GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_WRONG_SESSION_CONTEXT_PKTS, .str_value = "num_coa_session_context_not_found_requests"}, + { .enu_value = GNMA_IEEE8021X_DAS_DAC_STAT_IN_COA_ADMINISTRATIVELY_PROHIBITED_REQ_PKTS, .str_value = "num_coa_administratively_prohibited_requests"}, + }; + size_t i; + + for (i = 0; i < ARRAY_LENGTH(stats_enum_to_str); ++i) + if (counter_id == stats_enum_to_str[i].enu_value) + return stats_enum_to_str[i].str_value; + + return NULL; +} + +int +gnma_iee8021x_das_dac_global_stats_get(uint32_t num_of_counters, + gnma_ieee8021x_das_dac_stat_type_t *counter_ids, + uint64_t *counters) +{ + cJSON *parsed_res, *global_table, *global_counter_table_list, *counter, + *counters_arr; + const char *counter_string; + char *buf = 0; + char *gpath; + size_t i; + int ret; + + ret = asprintf(&gpath, "/sonic-das:sonic-das/DAS_GLOBAL_COUNTER_TABLE"); + if (ret == -1) { + ret = GNMA_ERR_COMMON; + goto err_path_alloc; + } + + ret = gnmi_jsoni_get_alloc(main_switch, &gpath[0], &buf, 0, + DEFAULT_TIMEOUT_US); + if (ret) { + ret = GNMA_ERR_COMMON; + goto err_gnmi_get; + } + + parsed_res = cJSON_Parse(buf); + ZFREE(buf); + if (!parsed_res) { + ret = GNMA_ERR_COMMON; + goto err_gnmi_parse; + } + + memset(counters, 0, sizeof(*counters) * num_of_counters); + /* + * Parse the following table type: + { + "sonic-das:DAS_GLOBAL_COUNTER_TABLE": { + "DAS_GLOBAL_COUNTER_TABLE_LIST": [ + { + "global": "GLOBAL", + "num_coa_ack_responses_sent": 0, + "num_coa_administratively_prohibited_requests": 0, + "num_coa_invalid_attribute_value_requests": 0, + "num_coa_missing_unsupported_attributes_requests": 0, + "num_coa_nak_responses_sent": 1, + "num_coa_requests_ignored": 1, + "num_coa_requests_received": 1, + "num_coa_session_context_not_found_requests": 0 + } + ] + } + } + */ + + global_table = + cJSON_GetObjectItemCaseSensitive(parsed_res, + "sonic-das:DAS_GLOBAL_COUNTER_TABLE"); + global_counter_table_list = + cJSON_GetObjectItemCaseSensitive(global_table, + "DAS_GLOBAL_COUNTER_TABLE_LIST"); + counters_arr = cJSON_GetArrayItem(global_counter_table_list, 0); + if (!cJSON_IsObject(counters_arr)) { + /* It's okay if these tables do not exists: + * no DAC cfg was present, counters - all zero*/ + ret = GNMA_OK; + goto err_gnmi_get_obj; + } + + for (i = 0; i < num_of_counters; ++i) { + counter_string = gnma_ieee8021x_das_dac_stats_type_to_string(counter_ids[i]); + counter = cJSON_GetObjectItemCaseSensitive(counters_arr, + counter_string); + if (counter && cJSON_IsNumber(counter)) { + counters[i] = (typeof(counters[i])) cJSON_GetNumberValue(counter); + } + } + +err_gnmi_get_obj: + cJSON_Delete(parsed_res); +err_gnmi_parse: +err_gnmi_get: + free(gpath); +err_path_alloc: + return ret; +} + int gnma_radius_hosts_list_get(size_t *list_size, struct gnma_radius_host_key *hosts_list) { @@ -4781,7 +5350,7 @@ int gnma_radius_host_remove(struct gnma_radius_host_key *key) int ret; ret = asprintf(&path, - "/openconfig-system:system/aaa/server-groups/server-group[name=RADIUS]/servers/server[address=%s]", + "/openconfig-das:das/das-client-config-table/das-client-config-table-entry[clientaddress=%s]/", key->hostname); if (ret == -1) { ret = GNMA_ERR_COMMON; @@ -4967,3 +5536,298 @@ err_gnmi_get_obj: err_gnmi_get: return ret; } + +static int __gnma_igmp_disable(uint16_t vid) +{ + char *resource[] = { + "/openconfig-interfaces:interfaces/interface[name=Vlan%u]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv4/openconfig-igmp-ext:igmp", + "/openconfig-network-instance:network-instances/network-instance[name=default]/protocols/protocol[identifier=IGMP_SNOOPING][name=IGMP-SNOOPING]/openconfig-network-instance-deviation:igmp-snooping/interfaces/interface[name=Vlan%u]/staticgrps", + "/openconfig-network-instance:network-instances/network-instance[name=default]/protocols/protocol[identifier=IGMP_SNOOPING][name=IGMP-SNOOPING]/openconfig-network-instance-deviation:igmp-snooping/interfaces/interface[name=Vlan%u]" + }; + char gpath[256]; + size_t i; + int ret; + + for (i = 0; i < ARRAY_LENGTH(resource); i++) { + ret = snprintf(gpath, sizeof(gpath), resource[i], vid); + if (ret == -1) + return GNMA_ERR_COMMON; + /* ignore return value */ + gnmi_jsoni_del(main_switch, gpath, DEFAULT_TIMEOUT_US); + } + return 0; +} + +static int __gnma_ip_igmp_set(uint16_t vid, struct gnma_igmp_snoop_attr *attr) +{ + cJSON *root, *config; + char *gpath; + int ret; + + ret = asprintf(&gpath, + "/openconfig-interfaces:interfaces/interface[name=Vlan%u]/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv4/openconfig-igmp-ext:igmp", + vid); + if (ret == -1) + return GNMA_ERR_COMMON; + + ret = GNMA_ERR_COMMON; + root = cJSON_CreateObject(); + if (!root) + goto err; + + config = cJSON_AddObjectToObject(root, "openconfig-igmp-ext:igmp"); + config = cJSON_AddObjectToObject(config, "config"); + if (!config) + goto err; + + if (!cJSON_AddBoolToObject(config, "enabled", attr->querier_enabled)) + goto err; + + if (attr->querier_enabled){ + if (!cJSON_AddNumberToObject(config, "query-interval", attr->query_interval) || + !cJSON_AddNumberToObject(config, "query-max-response-time", attr->max_response_time) || + !cJSON_AddNumberToObject(config, "last-member-query-interval", attr->last_member_query_interval)) + goto err; + if (attr->version != GNMA_IGMP_VERSION_NA) + if (!cJSON_AddNumberToObject(config, "version", attr->version)) + goto err; + } + + ret = gnmi_json_object_set(main_switch, gpath, root, DEFAULT_TIMEOUT_US); + if (ret) { + ret = GNMA_ERR_COMMON; + goto err; + } +err: + free(gpath); + return ret; +} + +static int __gnma_igmp_root_alloc(uint16_t vid, cJSON **root, cJSON **interface) +{ + cJSON *root_node, *config, *iface_node, *interfaces_list; + char iface_name[] = "VlanXXXX"; + + if (vid >= 4096) /* sanity */ + return GNMA_ERR_COMMON; + + snprintf(iface_name, sizeof(iface_name), "Vlan%u", vid); + + root_node = cJSON_CreateObject(); + if (!root_node) + return GNMA_ERR_COMMON; + + interfaces_list = cJSON_AddObjectToObject(root_node, "openconfig-network-instance-deviation:igmp-snooping"); + interfaces_list = cJSON_AddObjectToObject(interfaces_list, "interfaces"); + interfaces_list = cJSON_AddArrayToObject(interfaces_list, "interface"); + if (!interfaces_list) + goto err; + + iface_node = cJSON_CreateObject(); + if (!iface_node) + goto err; + if (!cJSON_AddItemToArray(interfaces_list, iface_node)) { + cJSON_Delete(iface_node); + goto err; + } + + config = cJSON_AddObjectToObject(iface_node, "config"); + if (!config) + goto err; + + if (!cJSON_AddStringToObject(iface_node, "name", iface_name) || + !cJSON_AddStringToObject(config, "name", iface_name)) + goto err; + + *root = root_node; + *interface = iface_node; + return 0; +err: + cJSON_Delete(root_node); + *root = NULL; + *interface = NULL; + return GNMA_ERR_COMMON; +} + +int gnma_igmp_snooping_set(uint16_t vid, struct gnma_igmp_snoop_attr *attr) +{ + char *gpath = "/openconfig-network-instance:network-instances/network-instance[name=default]/protocols/protocol[identifier=IGMP_SNOOPING][name=IGMP-SNOOPING]/openconfig-network-instance-deviation:igmp-snooping"; + bool enabled = attr->enabled || attr->querier_enabled; + cJSON *root, *config, *interface; + int ret; + + if (!enabled) { + /* ignore return value */ + __gnma_igmp_disable(vid); + return 0; + } + + ret = __gnma_ip_igmp_set(vid, attr); + if (ret) + return ret; + + /* allocates root object which needs to be freed */ + ret = __gnma_igmp_root_alloc(vid, &root, &interface); + if (ret) + return ret; + + config = cJSON_GetObjectItemCaseSensitive(interface, "config"); + if (!config) { + ret = GNMA_ERR_COMMON; + goto err; + } + + if (!cJSON_AddBoolToObject(config, "enabled", attr->enabled) || + !cJSON_AddBoolToObject(config, "querier", attr->querier_enabled)) { + ret = GNMA_ERR_COMMON; + goto err; + } + + if (attr->enabled){ + if (!cJSON_AddBoolToObject(config, "fast-leave", attr->fast_leave_enabled)) + goto err; + if (attr->version != GNMA_IGMP_VERSION_NA) + if (!cJSON_AddNumberToObject(config, "version", attr->version)) + goto err; + } + + ret = gnmi_json_object_set(main_switch, gpath, root, DEFAULT_TIMEOUT_US); + if (ret) { + ret = GNMA_ERR_COMMON; + goto err; + } +err: + cJSON_Delete(root); + return ret; +} + +int gnma_igmp_static_groups_set(uint16_t vid, size_t num_groups, + struct gnma_igmp_static_group_attr *groups) +{ + char *gpath = "/openconfig-network-instance:network-instances/network-instance[name=default]/protocols/protocol[identifier=IGMP_SNOOPING][name=IGMP-SNOOPING]/openconfig-network-instance-deviation:igmp-snooping"; + cJSON *root, *mcast_groups, *group, *port_list, *port, *interface; + char ip_addr[] = {"255.255.255.255"}; + size_t grp_idx, port_idx; + int ret; + + /* allocates root object which needs to be freed */ + ret = __gnma_igmp_root_alloc(vid, &root, &interface); + if (ret) + return ret; + + mcast_groups = cJSON_AddObjectToObject(interface, "staticgrps"); + mcast_groups = cJSON_AddArrayToObject(mcast_groups, "static-multicast-group"); + if (!mcast_groups) { + ret = GNMA_ERR_COMMON; + goto err; + } + + for (grp_idx = 0; grp_idx < num_groups; grp_idx++) { + group = cJSON_CreateObject(); + if (!group) { + ret = GNMA_ERR_COMMON; + goto err; + } + if (!cJSON_AddItemToArray(mcast_groups, group)) { + cJSON_Delete(group); + ret = GNMA_ERR_COMMON; + goto err; + } + + if (!inet_ntop(AF_INET, &groups[grp_idx].address.s_addr, ip_addr, sizeof(ip_addr)) || + !cJSON_AddStringToObject(group, "group", ip_addr) || + !cJSON_AddStringToObject(group, "source-addr", "0.0.0.0")) { + ret = GNMA_ERR_COMMON; + goto err; + } + + port_list = cJSON_AddObjectToObject(group, "config"); + port_list = cJSON_AddArrayToObject(port_list, "outgoing-interface"); + if (!port_list) { + ret = GNMA_ERR_COMMON; + goto err; + } + for (port_idx = 0; port_idx < groups[grp_idx].num_ports; port_idx++) { + port = cJSON_CreateString(groups[grp_idx].egress_ports[port_idx].name); + if (!port) { + ret = GNMA_ERR_COMMON; + goto err; + } + if (!cJSON_AddItemToArray(port_list, port)) { + cJSON_Delete(port); + ret = GNMA_ERR_COMMON; + goto err; + } + } + } + + ret = gnmi_json_object_set(main_switch, gpath, root, DEFAULT_TIMEOUT_US); + if (ret) { + ret = GNMA_ERR_COMMON; + goto err; + } +err: + cJSON_Delete(root); + return ret; +} + +int gnma_igmp_iface_groups_get(struct gnma_port_key *iface, + char *out_buf, size_t *out_buf_size) +{ + char *gpath, *buf = NULL; + cJSON *root, *groups; + size_t json_len = 0; + char *json_buf; + int ret; + + ret = asprintf(&gpath, + "/openconfig-network-instance:network-instances/network-instance[name=default]/protocols/protocol[identifier=IGMP_SNOOPING][name=IGMP-SNOOPING]/openconfig-network-instance-deviation:igmp-snooping/interfaces/interface[name=%s]", + iface->name); + + if (ret == -1) + return GNMA_ERR_COMMON; + ret = gnmi_jsoni_get_alloc(main_switch, &gpath[0], &buf, 0, + DEFAULT_TIMEOUT_US); + ZFREE(gpath); + if (ret) + return GNMA_ERR_COMMON; + root = cJSON_Parse(buf); + ZFREE(buf); + if (!root) + return GNMA_ERR_COMMON; + ret = GNMA_ERR_COMMON; + groups = cJSON_GetObjectItemCaseSensitive(root, "openconfig-network-instance-deviation:interface"); + groups = cJSON_GetArrayItem(groups, 0); + groups = cJSON_GetObjectItemCaseSensitive(groups, "staticgrps"); + groups = cJSON_GetObjectItemCaseSensitive(groups, "static-multicast-group"); + if (cJSON_GetArraySize(groups) == 0) { + /* No IGMP groups exists. */ + *out_buf_size = 0; + goto err_gnmi_no_entries; + } + + json_buf = cJSON_PrintUnformatted(groups); + if (!json_buf) { + ret = GNMA_ERR_COMMON; + goto err_buf_print; + } + + json_len = strlen(json_buf); + /* check that provided buffer is large enough */ + if (*out_buf_size < json_len) { + *out_buf_size = json_len + 1; + ret = GNMA_ERR_OVERFLOW; + free(json_buf); + goto err_gnmi_get_obj; + } + + memcpy(out_buf, json_buf, *out_buf_size - 1); + free(json_buf); + +err_gnmi_no_entries: + ret = GNMA_OK; +err_buf_print: +err_gnmi_get_obj: + cJSON_Delete(root); /* only need to free root */ + return ret; +} diff --git a/src/ucentral-client/platform/brcm-sonic/gnma/gnma_common.h b/src/ucentral-client/platform/brcm-sonic/gnma/gnma_common.h index 462afa9..f12d0a5 100644 --- a/src/ucentral-client/platform/brcm-sonic/gnma/gnma_common.h +++ b/src/ucentral-client/platform/brcm-sonic/gnma/gnma_common.h @@ -27,6 +27,16 @@ struct gnma_radius_host_key { char hostname[GNMA_RADIUS_CFG_HOSTNAME_STR_MAX_LEN]; }; +struct gnma_das_dac_host_key { + char hostname[GNMA_RADIUS_CFG_HOSTNAME_STR_MAX_LEN]; +}; + +typedef enum _gnma_das_auth_type_t { + GNMA_802_1X_DAS_AUTH_TYPE_ANY, + GNMA_802_1X_DAS_AUTH_TYPE_ALL, + GNMA_802_1X_DAS_AUTH_TYPE_SESSION_KEY, +} gnma_das_auth_type_t; + struct gnma_metadata { char platform[GNMA_METADATA_STR_MAX_LEN]; char hwsku[GNMA_METADATA_STR_MAX_LEN]; @@ -59,6 +69,17 @@ typedef enum _gnma_port_stat_type_t { } gnma_port_stat_type_t; +typedef enum _gnma_ieee8021x_das_dac_stat_type_t { + 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, +} gnma_ieee8021x_das_dac_stat_type_t; + struct gnma_alarm { const char *id; const char *resource; @@ -269,6 +290,29 @@ struct gnma_fdb_entry { char mac[18]; }; +typedef enum _gnma_igmp_version_t { + GNMA_IGMP_VERSION_NA = 0, + GNMA_IGMP_VERSION_1 = 1, + GNMA_IGMP_VERSION_2 = 2, + GNMA_IGMP_VERSION_3 = 3 +} gnma_igmp_version_t; + +struct gnma_igmp_snoop_attr { + bool enabled; + bool querier_enabled; + bool fast_leave_enabled; + uint32_t query_interval; + uint32_t last_member_query_interval; + uint32_t max_response_time; + gnma_igmp_version_t version; +}; + +struct gnma_igmp_static_group_attr { + struct in_addr address; + size_t num_ports; + struct gnma_port_key *egress_ports; +}; + int gnma_switch_create(/* TODO id */ /* TODO: attr (adr, login, psw) */); int gnma_port_admin_state_set(struct gnma_port_key *port_key, bool up); int gnma_port_speed_set(struct gnma_port_key *port_key, const char *speed); @@ -403,14 +447,33 @@ int gnma_stp_ports_enable(uint32_t list_size, struct gnma_port_key *ports_list); int gnma_stp_instance_set(uint16_t instance, uint16_t prio, uint32_t list_size, uint16_t *vid_list); -int gnma_stp_vids_enable(uint32_t list_size, uint16_t *vid_list); -int gnma_stp_vids_enable_all(void); +int gnma_stp_vids_set(uint32_t list_size, uint16_t *vid_list, bool enable); +int gnma_stp_vids_set_all(bool enable); int gnma_stp_vid_set(uint16_t vid, struct gnma_stp_attr *attr); int gnma_stp_vid_bulk_get(struct gnma_stp_attr *list, ssize_t size); int gnma_ieee8021x_system_auth_control_set(bool is_enabled); int gnma_ieee8021x_system_auth_control_get(bool *is_enabled); int gnma_ieee8021x_system_auth_clients_get(char *buf, size_t buf_size); +int gnma_ieee8021x_das_bounce_port_ignore_set(bool bounce_port_ignore); +int gnma_ieee8021x_das_bounce_port_ignore_get(bool *bounce_port_ignore); +int gnma_ieee8021x_das_disable_port_ignore_set(bool disable_port_ignore); +int gnma_ieee8021x_das_disable_port_ignore_get(bool *disable_port_ignore); +int gnma_ieee8021x_das_ignore_server_key_set(bool ignore_server_key); +int gnma_ieee8021x_das_ignore_server_key_get(bool *ignore_server_key); +int gnma_ieee8021x_das_ignore_session_key_set(bool ignore_session_key); +int gnma_ieee8021x_das_ignore_session_key_get(bool *ignore_session_key); +int gnma_ieee8021x_das_auth_type_key_set(gnma_das_auth_type_t auth_type); +int gnma_ieee8021x_das_auth_type_key_get(gnma_das_auth_type_t *auth_type); +int gnma_ieee8021x_das_dac_hosts_list_get(size_t *list_size, + struct gnma_das_dac_host_key *das_dac_keys_arr); +int gnma_ieee8021x_das_dac_host_add(struct gnma_das_dac_host_key *key, + const char *passkey); +int gnma_ieee8021x_das_dac_host_remove(struct gnma_das_dac_host_key *key); +int +gnma_iee8021x_das_dac_global_stats_get(uint32_t num_of_counters, + gnma_ieee8021x_das_dac_stat_type_t *counter_ids, + uint64_t *counters); int gnma_radius_hosts_list_get(size_t *list_size, struct gnma_radius_host_key *hosts_list); @@ -419,6 +482,12 @@ int gnma_radius_host_add(struct gnma_radius_host_key *key, const char *passkey, int gnma_radius_host_remove(struct gnma_radius_host_key *key); int gnma_mac_address_list_get(size_t *list_size, struct gnma_fdb_entry *list); int gnma_system_password_set(char *password); +int gnma_igmp_snooping_set(uint16_t vid, struct gnma_igmp_snoop_attr *attr); +int gnma_igmp_static_groups_set(uint16_t vid, size_t num_groups, + struct gnma_igmp_static_group_attr *groups); + +int gnma_igmp_iface_groups_get(struct gnma_port_key *iface, + char *buf, size_t *buf_size); struct gnma_change *gnma_change_create(void); void gnma_change_destory(struct gnma_change *); diff --git a/src/ucentral-client/platform/brcm-sonic/plat-gnma.c b/src/ucentral-client/platform/brcm-sonic/plat-gnma.c index db11582..3c7ef1b 100644 --- a/src/ucentral-client/platform/brcm-sonic/plat-gnma.c +++ b/src/ucentral-client/platform/brcm-sonic/plat-gnma.c @@ -44,6 +44,16 @@ #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); @@ -137,6 +147,17 @@ plat_ieee8021x_system_auth_clients_get(uint16_t port_id, } \ (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). @@ -195,15 +216,6 @@ struct poe_port { gnma_poe_port_priority_t priority; }; -/* Password is obfuscated and key changes all the time. - * So cache only actual hosts (ip / hostname), and do a single - * GNMI request to add host (with all parameters - passkey, port etc) upon - * every cfg reqest. - */ -struct radius_host { - struct gnma_radius_host_key key; -}; - struct port { struct gnma_port_key key; struct { @@ -244,6 +256,13 @@ struct plat_state { } 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; @@ -875,6 +894,98 @@ err: 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); @@ -892,9 +1003,15 @@ static int plat_state_init() featsts[FEAT_CORE] = FEATSTS_FAIL; - ret = gnma_ieee8021x_system_auth_control_get(&plat_state.ieee8021x.is_auth_control_enabled); + ret = plat_state_ieee8021x_init(); if (ret) { - UC_LOG_CRIT("gnma_ieee8021x_system_auth_control_get failed"); + 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; } @@ -1012,12 +1129,6 @@ static int plat_state_init() } } - ret = plat_state_radius_init(); - if (ret) { - UC_LOG_CRIT("plat_state_radius_init failed"); - featsts[FEAT_AAA] = FEATSTS_FAIL; - } - if (featsts[FEAT_AAA] == FEATSTS_FAIL) { UC_LOG_CRIT("AAA feature failed to initialize"); } else { @@ -1458,16 +1569,6 @@ int plat_port_stats_get(uint16_t fp_p_id, struct plat_port_counters *stats) uint64_t counters[ARRAY_LENGTH(stat_types)]; struct gnma_port_key gnma_port; -#define ARR_FIND_VALUE_IDX(A, len, value) \ - ({ \ - size_t it = 0; \ - for ((it) = 0; (it) < (len); (++it)) { \ - if ((A)[it] == (value)) \ - break; \ - } \ - (it); \ - }) - PID_TO_NAME(fp_p_id, gnma_port.name); if (gnma_port_stats_get(&gnma_port, ARRAY_LENGTH(stat_types), @@ -1522,7 +1623,6 @@ int plat_port_stats_get(uint16_t fp_p_id, struct plat_port_counters *stats) stats->tx_packets += counters[ARR_FIND_VALUE_IDX(stat_types, ARRAY_LENGTH(stat_types), GNMA_PORT_STAT_OUT_BCAST_PKTS)]; -#undef ARR_FIND_VALUE_IDX return 0; } @@ -1794,6 +1894,123 @@ err: 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) @@ -1920,6 +2137,7 @@ __poe_state_buf_parse(char *buf, size_t buf_size, { cJSON *status; cJSON *state; + cJSON *tmp; state = cJSON_ParseWithLength(buf, buf_size); if (!state) @@ -1927,10 +2145,19 @@ __poe_state_buf_parse(char *buf, size_t buf_size, 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 = - cJSON_GetNumberValue(cJSON_GetObjectItemCaseSensitive(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 = - cJSON_GetNumberValue(cJSON_GetObjectItemCaseSensitive(state, "power-consumption")); + (typeof(poe_state->power_consumed)) strtod(cJSON_GetStringValue(tmp), NULL); status = cJSON_GetObjectItemCaseSensitive(state, "pse-oper-status"); if (!cJSON_GetStringValue(status)) @@ -2792,6 +3019,11 @@ err: /* 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; @@ -2818,6 +3050,11 @@ int plat_vlan_rif_set(uint16_t vid, struct plat_ipv4 *ipv4) 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' @@ -2852,6 +3089,83 @@ int plat_vlan_rif_set(uint16_t vid, struct plat_ipv4 *ipv4) 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) { @@ -2887,8 +3201,31 @@ int plat_portl2_rif_set(uint16_t fp_p_id, struct plat_ipv4 *ipv4) 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 }; } @@ -2954,6 +3291,11 @@ static int plat_port_info_get(struct plat_port_info **port_info, int *count) 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, @@ -2973,6 +3315,54 @@ err: 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; @@ -3074,6 +3464,54 @@ err: 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; @@ -3092,10 +3530,16 @@ static int plat_state_get(struct plat_state_info *state) 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; } @@ -3111,6 +3555,12 @@ static int config_vlan_ipv4_apply(struct plat_cfg *cfg) 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; @@ -3896,20 +4346,90 @@ static int plat_port_config_apply(struct plat_cfg *cfg) 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) { + 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); + 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; + 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); @@ -4003,6 +4523,7 @@ 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) @@ -4019,6 +4540,14 @@ void plat_config_destroy(struct plat_cfg *cfg) 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); } diff --git a/src/ucentral-client/platform/brcm-sonic/plat-revision.h b/src/ucentral-client/platform/brcm-sonic/plat-revision.h index 2a1bca2..5ac4ecd 100644 --- a/src/ucentral-client/platform/brcm-sonic/plat-revision.h +++ b/src/ucentral-client/platform/brcm-sonic/plat-revision.h @@ -5,7 +5,7 @@ #define STR(x) #x #define PLATFORM_REL_NUM 1.6 -#define PLATFORM_BUILD_NUM 4 +#define PLATFORM_BUILD_NUM 5 #ifndef PLATFORM_REVISION #define PLATFORM_REVISION "Rel " XSTR(PLATFORM_REL_NUM) " build " XSTR(PLATFORM_BUILD_NUM) diff --git a/src/ucentral-client/proto.c b/src/ucentral-client/proto.c index a666c89..37a8872 100644 --- a/src/ucentral-client/proto.c +++ b/src/ucentral-client/proto.c @@ -906,6 +906,171 @@ cfg_ethernet_ieee8021x_parse(cJSON *ieee8021x, struct plat_port *port) return 0; } +static int +cfg_ethernet_port_isolation_interface_parse(cJSON *iface, + struct plat_port_isolation_session_ports *ports) { + struct plat_ports_list *port_node = NULL; + cJSON *iface_list; + int i; + + iface_list = cJSON_GetObjectItemCaseSensitive(iface, "interface-list"); + if (!iface_list || !cJSON_IsArray(iface_list) || + cJSON_GetArraySize(iface_list) == 0) { + UC_LOG_ERR("Ethernet obj 'port_isolation:interface-list' is invalid, parse failed\n"); + return -1; + } + + for (i = 0; i < cJSON_GetArraySize(iface_list); ++i) { + if (!cJSON_GetStringValue(cJSON_GetArrayItem(iface_list, i))) { + UC_LOG_ERR("Ethernet obj 'port_isolation:interface-list:%d' has invalid port name, parse failed\n", + i); + return -1; + } + port_node = calloc(1, sizeof(*port_node)); + if (!port_node) { + UC_LOG_ERR("Failed alloc port list list\n"); + return -1; + } + strcpy(port_node->name, + cJSON_GetStringValue(cJSON_GetArrayItem(iface_list, i))); + UCENTRAL_LIST_PUSH_MEMBER(&ports->ports_list, port_node); + } + + return 0; +} + +static int +cfg_ethernet_port_isolation_parse(cJSON *ethernet, struct plat_cfg *cfg) { + cJSON *eth = NULL, *port_isolation, *sessions, *session; + struct plat_port_isolation_session *session_arr; + struct plat_ports_list *port_node = NULL; + int i = 0, j = 0; + + cJSON_ArrayForEach(eth, ethernet) { + port_isolation = cJSON_GetObjectItemCaseSensitive(eth, "port-isolation"); + if (!port_isolation) + continue; + + if (!cJSON_IsObject(port_isolation)) { + UC_LOG_ERR("Ethernet obj holds 'port_isolation' object of wrongful type, parse failed\n"); + return -1; + } + + sessions = cJSON_GetObjectItemCaseSensitive(port_isolation, + "sessions"); + if (!sessions || !cJSON_IsArray(sessions)) { + UC_LOG_ERR("Ethernet obj holds 'port_isolation:sessions' array of wrongful type (or empty), parse failed\n"); + return -1; + } + + cJSON_ArrayForEach(session, sessions) { + cfg->port_isolation_cfg.sessions_num++; + } + } + + if (cfg->port_isolation_cfg.sessions_num == 0) { + return 0; + } + + session_arr = calloc(cfg->port_isolation_cfg.sessions_num, + sizeof(struct plat_port_isolation_session)); + cfg->port_isolation_cfg.sessions = session_arr; + + if (!session_arr) { + UC_LOG_ERR("Failed to alloc memory for port-isolation-cfg, parse failed\n"); + return -1; + } + + cJSON_ArrayForEach(eth, ethernet) { + port_isolation = cJSON_GetObjectItemCaseSensitive(eth, "port-isolation"); + if (!port_isolation) + continue; + + /* + * Highly unlikeable that the object is missing / invalid, + * as it was okay prior (parsing above). + * But this is still a sanity-check, in case if JSON + * got corrupted for some reason. + */ + if (!cJSON_IsObject(port_isolation)) { + UC_LOG_ERR("Ethernet obj holds 'port_isolation' object of wrongful type, parse failed\n"); + return -1; + } + + sessions = cJSON_GetObjectItemCaseSensitive(port_isolation, + "sessions"); + if (!sessions || !cJSON_IsArray(sessions)) { + UC_LOG_ERR("Ethernet obj holds 'port_isolation:sessions' array of wrongful type (or empty), parse failed\n"); + return -1; + } + + cJSON_ArrayForEach(session, sessions) { + cJSON *id, *uplink, *downlink; + double session_arrid; + + id = cJSON_GetObjectItemCaseSensitive(session, "id"); + if (!id || !cJSON_IsNumber(id)) { + UC_LOG_ERR("Ethernet obj 'port_isolation:id' is invalid, parse failed\n"); + goto err; + } + + session_arrid = cJSON_GetNumberValue(id); + + if (i > 0) { + for (int j = i - 1; j >= 0; --j) { + if ((double) session_arr[j].id == session_arrid) { + UC_LOG_ERR("Expected unique 'port_isolation:id', duplicate (%lu) detected, parse failed\n", + (uint64_t) session_arrid); + goto err; + } + } + } + + session_arr[j].id = (uint64_t) session_arrid; + + uplink = cJSON_GetObjectItemCaseSensitive(session, + "uplink"); + if (!uplink || !cJSON_IsObject(uplink)) { + UC_LOG_ERR("Ethernet obj 'port_isolation:uplink' is invalid, parse failed\n"); + goto err; + } + + downlink = cJSON_GetObjectItemCaseSensitive(session, + "downlink"); + if (!downlink || !cJSON_IsObject(downlink)) { + UC_LOG_ERR("Ethernet obj 'port_isolation:downlink' is invalid, parse failed\n"); + goto err; + } + + if (cfg_ethernet_port_isolation_interface_parse(uplink, + &session_arr[j].uplink)) { + UC_LOG_ERR("Ethernet obj 'port_isolation:uplink' parse failed\n"); + goto err; + } + + if (cfg_ethernet_port_isolation_interface_parse(downlink, + &session_arr[j].downlink)) { + UC_LOG_ERR("Ethernet obj 'port_isolation:downlink' parse failed\n"); + goto err; + } + + ++i; + } + } + + return 0; +err: + for (int j = i; j >= 0; --j) { + UCENTRAL_LIST_DESTROY_SAFE(&session_arr[j].uplink.ports_list, + port_node); + UCENTRAL_LIST_DESTROY_SAFE(&session_arr[j].downlink.ports_list, + port_node); + } + cfg->port_isolation_cfg.sessions = 0; + free(cfg->port_isolation_cfg.sessions); + return -1; +} + static int cfg_ethernet_parse(cJSON *ethernet, struct plat_cfg *cfg) { cJSON *eth = NULL; @@ -986,6 +1151,11 @@ static int cfg_ethernet_parse(cJSON *ethernet, struct plat_cfg *cfg) } } + if (cfg_ethernet_port_isolation_parse(ethernet, cfg)) { + UC_LOG_ERR("port-isolation config parse faile\n"); + return -1; + } + return 0; } @@ -1063,6 +1233,121 @@ static int cfg_port_interface_parse(cJSON *interface, struct plat_cfg *cfg) return 0; } +static int __cfg_vlan_interface_parse_multicast(cJSON *multicast, + struct plat_cfg *cfg, + uint16_t vid) +{ + cJSON *igmp, *field, *group, *addr, *ports, *port; + struct plat_ports_list *e_port; + struct plat_igmp info = { /* default values */ + .snooping_enabled = true, + .querier_enabled = false, + .fast_leave_enabled = false, + .query_interval = 60, + .last_member_query_interval = 60, + .max_response_time = 10, + .version = PLAT_IGMP_VERSION_3, + .num_groups = 0, + .groups = NULL + }; + size_t group_idx; + + if (!(igmp = cJSON_GetObjectItemCaseSensitive(multicast, "igmp"))) + return 0; + + /* handle igmp snooping parameters */ + if ((field = cJSON_GetObjectItemCaseSensitive(igmp, "version")) && cJSON_IsNumber(field)) { + switch ((uint32_t)cJSON_GetNumberValue(field)) { + case 1: + info.version = PLAT_IGMP_VERSION_1; + break; + case 2: + info.version = PLAT_IGMP_VERSION_2; + break; + case 3: + info.version = PLAT_IGMP_VERSION_3; + break; + default: + UC_LOG_ERR("Invalid IGMP version %f", cJSON_GetNumberValue(field)); + return -1; + } + } + if ((field = cJSON_GetObjectItemCaseSensitive(igmp, "snooping-enable")) && cJSON_IsBool(field)) + info.snooping_enabled = cJSON_IsTrue(field); + if ((field = cJSON_GetObjectItemCaseSensitive(igmp, "querier-enable")) && cJSON_IsBool(field)) + info.querier_enabled = cJSON_IsTrue(field); + if ((field = cJSON_GetObjectItemCaseSensitive(igmp, "fast-leave-enable")) && cJSON_IsBool(field)) + info.fast_leave_enabled = cJSON_IsTrue(field); + if ((field = cJSON_GetObjectItemCaseSensitive(igmp, "query-interval")) && cJSON_IsNumber(field)) + info.query_interval = cJSON_GetNumberValue(field); + if ((field = cJSON_GetObjectItemCaseSensitive(igmp, "last-member-query-interval")) && cJSON_IsNumber(field)) + info.last_member_query_interval = cJSON_GetNumberValue(field); + if ((field = cJSON_GetObjectItemCaseSensitive(igmp, "max-response-time")) && cJSON_IsNumber(field)) + info.max_response_time = cJSON_GetNumberValue(field); + + field = cJSON_GetObjectItemCaseSensitive(igmp, "static-mcast-groups"); + if (!field || !cJSON_IsArray(field)) + goto skip_groups; + + info.num_groups = cJSON_GetArraySize(field); + info.groups = calloc(info.num_groups, sizeof(*info.groups)); + if (!info.groups) + goto err; + + /* handle static groups */ + group_idx = 0; + cJSON_ArrayForEach(group, field) { + addr = cJSON_GetObjectItemCaseSensitive(group, "address"); + ports = cJSON_GetObjectItemCaseSensitive(group, "egress-ports"); + if (!addr || !cJSON_IsString(addr) || !ports || !cJSON_IsArray(ports)) { + /* FIXME: workaround for parser issue */ + addr = cJSON_GetObjectItemCaseSensitive(group, "static-mcast-groups[].address"); + ports = cJSON_GetObjectItemCaseSensitive(group, "static-mcast-groups[].egress-ports"); + if (!addr || !cJSON_IsString(addr) || !ports || !cJSON_IsArray(ports)) { + UC_LOG_ERR("Missing static group info\n"); + goto err; + } + } + + if (inet_pton(AF_INET, addr->valuestring, &info.groups[group_idx].addr.s_addr) != 1) { + UC_LOG_ERR("Failed to parse ip addr %s\n", addr->valuestring); + goto err; + } + + /* handle egress ports */ + cJSON_ArrayForEach(port, ports) { + e_port = calloc(1, sizeof(*e_port)); + if (!e_port) { + UC_LOG_ERR("Can't alloc port node\n"); + goto err; + } + + if (!cJSON_IsString(port)){ + UC_LOG_ERR("Invalid port name\n"); + goto err; + } + + strncpy(e_port->name, port->valuestring, sizeof(e_port->name)); + UCENTRAL_LIST_PUSH_MEMBER(&info.groups[group_idx].egress_ports_list, e_port); + } + group_idx++; + } +skip_groups: + info.exist = info.snooping_enabled || info.querier_enabled; + cfg->vlans[vid].igmp = info; + return 0; +err: + if (info.groups) { + for (group_idx = 0; group_idx < info.num_groups; group_idx++) { + UCENTRAL_LIST_DESTROY_SAFE( + &info.groups[group_idx].egress_ports_list, + e_port); + } + } + free(info.groups); + return -1; +} + static int cfg_vlan_interface_parse(cJSON *interface, struct plat_cfg *cfg) { size_t i; @@ -1076,6 +1361,7 @@ static int cfg_vlan_interface_parse(cJSON *interface, struct plat_cfg *cfg) char *ipv4_subnet_str; cJSON *select_ports; cJSON *ipv4_subnet; + cJSON *multicast; cJSON *vlan_tag; cJSON *ethernet; uint8_t tagged; @@ -1141,6 +1427,7 @@ static int cfg_vlan_interface_parse(cJSON *interface, struct plat_cfg *cfg) cfg->vlans[vid].dhcp.relay.enabled = false; ipv4 = cJSON_GetObjectItemCaseSensitive(interface, "ipv4"); dhcp = cJSON_GetObjectItemCaseSensitive(ipv4, "dhcp"); + multicast = cJSON_GetObjectItemCaseSensitive(ipv4, "multicast"); if (ipv4) { /* TODO addressing */ ipv4_subnet_str = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(ipv4, "subnet")); @@ -1181,6 +1468,13 @@ skip_subnet_old: } cfg->vlans[vid].ipv4.exist = true; skip_subnet: + if (multicast) { + ret = __cfg_vlan_interface_parse_multicast(multicast, cfg, vid); + if (ret) { + UC_LOG_ERR("Failed parsing multicast config"); + return ret; + } + } if (!dhcp) return 0; @@ -1333,6 +1627,59 @@ static int cfg_services_parse(cJSON *services, struct plat_cfg *cfg) } } + /* Set default values in case if no cfg supplied */ + cfg->enabled_services_cfg.ssh.enabled = false; + cfg->enabled_services_cfg.telnet.enabled = false; + cfg->enabled_services_cfg.http.enabled = false; + + s = cJSON_GetObjectItemCaseSensitive(services, "ssh"); + if (s) { + if (!cJSON_IsObject(s)) { + UC_LOG_ERR("Unexpected type of services:ssh: Object expected"); + return -1; + } + + cJSON *enable = cJSON_GetObjectItemCaseSensitive(s, "enable"); + if (enable && !cJSON_IsBool(enable)) { + UC_LOG_ERR("Unexpected type of services:ssh:enable: Boolean expected"); + return -1; + } + + cfg->enabled_services_cfg.ssh.enabled = cJSON_IsTrue(enable); + } + + s = cJSON_GetObjectItemCaseSensitive(services, "telnet"); + if (s) { + if (!cJSON_IsObject(s)) { + UC_LOG_ERR("Unexpected type of services:telnet: Object expected"); + return -1; + } + + cJSON *enable = cJSON_GetObjectItemCaseSensitive(s, "enable"); + if (enable && !cJSON_IsBool(enable)) { + UC_LOG_ERR("Unexpected type of services:telnet:enable: Boolean expected"); + return -1; + } + + cfg->enabled_services_cfg.telnet.enabled = cJSON_IsTrue(enable); + } + + s = cJSON_GetObjectItemCaseSensitive(services, "http"); + if (s) { + if (!cJSON_IsObject(s)) { + UC_LOG_ERR("Unexpected type of services:http: Object expected"); + return -1; + } + + cJSON *enable = cJSON_GetObjectItemCaseSensitive(s, "enable"); + if (enable && !cJSON_IsBool(enable)) { + UC_LOG_ERR("Unexpected type of services:http:enable: Boolean expected"); + return -1; + } + + cfg->enabled_services_cfg.http.enabled = cJSON_IsTrue(enable); + } + return 0; } @@ -1353,7 +1700,7 @@ static int cfg_switch_ieee8021x_parse(cJSON *sw, struct plat_cfg *cfg) /* It's safe to check against NULL cJSON obj. * In case if option is missing - defaulting to 'false' is OK for us. */ - cfg->ieee8021x_is_auth_ctrl_enabled = cJSON_IsTrue(auth_ctrl_enabled); + cfg->ieee8021x.is_auth_ctrl_enabled = cJSON_IsTrue(auth_ctrl_enabled); radius = cJSON_GetObjectItemCaseSensitive(ieee8021x, "radius"); if (radius && !cJSON_IsArray(radius)) { @@ -2511,6 +2858,10 @@ static void script_handle(cJSON **rpc) UC_LOG_ERR("script message missing 'uri' parameter"); return; } + + script_reply(0, "pending", id); + UC_LOG_DBG("Script requested OK (pending. Waiting for plat to execute)\n"); + memset(&file_path[0], 0, sizeof(file_path)); if (plat_diagnostic(&file_path[0])) { UC_LOG_ERR("Script failed\n"); @@ -2518,9 +2869,6 @@ static void script_handle(cJSON **rpc) return; } - script_reply(0, "pending", id); - UC_LOG_DBG("Script requested OK\n"); - /* Poll upgrade state - start periodical. */ while (access(file_path, F_OK)) sleep(1); @@ -2766,14 +3114,135 @@ err: return -1; } +static int state_fill_transceiver_info(cJSON *root, + struct plat_port_transceiver_info *info) +{ + char *form_factor[] = { + [UCENTRAL_SFP_FORM_FACTOR_NA] = "", + [UCENTRAL_SFP_FORM_FACTOR_SFP] = "SFP", + [UCENTRAL_SFP_FORM_FACTOR_SFP_PLUS] = "SFP+", + [UCENTRAL_SFP_FORM_FACTOR_SFP_28] = "SFP28", + [UCENTRAL_SFP_FORM_FACTOR_SFP_DD] = "SFP-DD", + [UCENTRAL_SFP_FORM_FACTOR_QSFP] = "QSFP", + [UCENTRAL_SFP_FORM_FACTOR_QSFP_PLUS] = "QSFP+", + [UCENTRAL_SFP_FORM_FACTOR_QSFP_28] = "QSFP28", + [UCENTRAL_SFP_FORM_FACTOR_QSFP_DD] = "QSFP-DD" + }; + char *link_mode[] = { + [UCENTRAL_SFP_LINK_MODE_NA] = "", + [UCENTRAL_SFP_LINK_MODE_1000_X] = "1G", + [UCENTRAL_SFP_LINK_MODE_2500_X] = "2.5G", + [UCENTRAL_SFP_LINK_MODE_4000_SR] = "4G SR", + [UCENTRAL_SFP_LINK_MODE_10G_SR] = "10G SR", + [UCENTRAL_SFP_LINK_MODE_25G_SR] = "25G SR", + [UCENTRAL_SFP_LINK_MODE_40G_SR] = "40G SR", + [UCENTRAL_SFP_LINK_MODE_50G_SR] = "50G SR", + [UCENTRAL_SFP_LINK_MODE_100G_SR] = "100G SR" + }; + cJSON *supp_modes, *mode; + size_t i; + + if (!cJSON_AddStringToObject(root, "vendor-name", + info->vendor_name)) + goto err; + if (!cJSON_AddStringToObject(root, "part-number", + info->part_number)) + goto err; + if (!cJSON_AddStringToObject(root, "serial-number", + info->serial_number)) + goto err; + if (!cJSON_AddStringToObject(root, "revision", + info->revision)) + goto err; + if (!cJSON_AddNumberToObject(root, "temperature", + info->temperature)) + goto err; + if (!cJSON_AddNumberToObject(root, "tx-optical-power", + info->tx_optical_power)) + goto err; + if (!cJSON_AddNumberToObject(root, "rx-optical-power", + info->rx_optical_power)) + goto err; + if (!cJSON_AddNumberToObject(root, "max-module-power", + info->max_module_power)) + goto err; + if (!cJSON_AddStringToObject(root, "form-factor", + form_factor[info->form_factor])) + goto err; + if (!(supp_modes = cJSON_AddArrayToObject(root, "supported-link-modes"))) + goto err; + for (i = 0; i < info->num_supported_link_modes; i++) { + if (!(mode = cJSON_CreateString(link_mode[info->supported_link_modes[i]]))) + goto err; + if (!cJSON_AddItemToArray(supp_modes, mode)) { + cJSON_Delete(mode); + goto err; + } + } + return 0; +err: + return -1; +} + +static int state_fill_interface_multicast(cJSON *root, struct plat_port_vlan *vlan) +{ + cJSON *igmp, *enabled_groups, *group, *outgoing_ports; + struct plat_ports_list *port_node = NULL; + char ip_addr[] = {"255.255.255.255"}; + int ret = -1; + size_t idx; + + if (!vlan->igmp.exist) + return 0; + + igmp = cJSON_AddObjectToObject(root, "igmp"); + enabled_groups = cJSON_AddArrayToObject(igmp, "enabled-groups"); + if (!igmp || !enabled_groups) + goto err; + + for (idx = 0; idx < vlan->igmp.num_groups; idx++) { + if (!(group = cJSON_CreateObject())) + goto err; + + if (!inet_ntop(AF_INET, &vlan->igmp.groups[idx].addr, + ip_addr, sizeof(ip_addr))) + goto err; + + if (!(cJSON_AddStringToObject(group, "address", ip_addr))) + goto err; + + if (!(outgoing_ports = cJSON_AddArrayToObject(group, "egress-ports"))) + goto err; + + UCENTRAL_LIST_FOR_EACH_MEMBER( + port_node, + &vlan->igmp.groups[idx].egress_ports_list) { + if (!cJSON_AddItemToArray(outgoing_ports, cJSON_CreateString(port_node->name))) + goto err; + } + + if (!cJSON_AddItemToArray(enabled_groups, group)) + goto err; + } + + ret = 0; +err: + return ret; +} + static int state_fill_interfaces_data(cJSON *interfaces, struct plat_state_info *state) { + char location[] = { "/interfaces/XXXX" }; + char vlan_name[] = { "VlanXXXX" }; cJSON *dns_servers; + cJSON *transceiver; cJSON *interface; + cJSON *multicast; cJSON *counters; cJSON *clients; cJSON *ipv4; + uint16_t id; int ret; int i; @@ -2807,6 +3276,16 @@ static int state_fill_interfaces_data(cJSON *interfaces, if (!ipv4) goto err; + if (state->port_info[i].has_transceiver_info) { + transceiver = cJSON_AddObjectToObject(interface, "transceiver-info"); + if (!transceiver) + goto err; + ret = state_fill_transceiver_info(transceiver, + &state->port_info[i].transceiver_info); + if (ret) + goto err; + } + ret = state_fill_interface_clients(clients, &state->port_info[i]); if (ret) @@ -2832,23 +3311,46 @@ static int state_fill_interfaces_data(cJSON *interfaces, } /* TBD: find out (?) proper */ - { - char location[] = { "/interfaces/XXXX" }; - uint16_t pid; + NAME_TO_PID(&id, state->port_info[i].name); + sprintf(location, "/interfaces/%hu", id); - NAME_TO_PID(&pid, state->port_info[i].name); - sprintf(location, "/interfaces/%hu", pid); - - if (!cJSON_AddStringToObject(interface, "location", - location)) - goto err; - } + if (!cJSON_AddStringToObject(interface, "location", + location)) + goto err; if (!jobj_u64_set(interface, "uptime", state->system_info.uptime)) goto err; } + for (i = 0; i < (int)state->vlan_info_count; i++) { + interface = cJSON_CreateObject(); + if (!interface || !cJSON_AddItemToArray(interfaces, interface)) + goto err; + + clients = cJSON_AddArrayToObject(interface, "clients"); + counters = cJSON_AddObjectToObject(interface, "counters"); + dns_servers = cJSON_AddArrayToObject(interface, "dns_servers"); + ipv4 = cJSON_AddObjectToObject(interface, "ipv4"); + multicast = cJSON_AddObjectToObject(interface, "multicast"); + if (!clients || !counters || !dns_servers || !ipv4 || !multicast) + goto err; + + ret = state_fill_interface_multicast(multicast, &state->vlan_info[i]); + if (ret) + goto err; + + sprintf(location, "/SVI/%u", state->vlan_info[i].id); + sprintf(vlan_name, "Vlan%u", state->vlan_info[i].id); + + if (!cJSON_AddStringToObject(interface, "name", vlan_name)) + goto err; + if (!cJSON_AddStringToObject(interface, "location", location)) + goto err; + if (!jobj_u64_set(interface, "uptime", state->system_info.uptime)) + goto err; + } + return 0; err: return -1; @@ -3157,16 +3659,16 @@ static int state_fill_unit_poe_data(cJSON *poe, struct plat_poe_state *poe_state_info) { - if (!cJSON_AddNumberToObject(poe, "max-power-budget", - poe_state_info->max_power_budget)) + if (!jobj_u64_set(poe, "max-power-budget", + poe_state_info->max_power_budget)) goto err; - if (!cJSON_AddNumberToObject(poe, "power-consumed", - poe_state_info->power_consumed)) + if (!jobj_u64_set(poe, "power-consumed", + poe_state_info->power_consumed)) goto err; - if (!cJSON_AddNumberToObject(poe, "power-threshold", - poe_state_info->power_threshold)) + if (!jobj_u64_set(poe, "power-threshold", + poe_state_info->power_threshold)) goto err; if (!cJSON_AddStringToObject(poe, "power-status", @@ -3178,8 +3680,51 @@ err: return -1; } +static int +state_fill_unit_ieee8021x_global_counters(cJSON *ieee8021x, + struct plat_iee8021x_coa_counters *c) +{ + cJSON *das, *stats; + + das = cJSON_AddObjectToObject(ieee8021x, "dynamic-authorization"); + if (!das) + return -1; + + stats = cJSON_AddObjectToObject(das, "stats"); + if (!stats) + return -1; + + if (!jobj_u64_set(stats, "coa_req_received", + c->coa_req_received)) + return -1; + if (!jobj_u64_set(stats, "coa_ack_sent", + c->coa_ack_sent)) + return -1; + if (!jobj_u64_set(stats, "coa_nak_sent", + c->coa_nak_sent)) + return -1; + if (!jobj_u64_set(stats, "coa_ignored", + c->coa_ignored)) + return -1; + if (!jobj_u64_set(stats, "coa_wrong_attr", + c->coa_wrong_attr)) + return -1; + if (!jobj_u64_set(stats, "coa_wrong_attr_value", + c->coa_wrong_attr_value)) + return -1; + if (!jobj_u64_set(stats, "coa_wrong_session_context", + c->coa_wrong_session_context)) + return -1; + if (!jobj_u64_set(stats, "administratively_prohibited_req", + c->coa_administratively_prohibited_req)) + return -1; + + return 0; +} + static int state_fill_unit_data(cJSON *unit, struct plat_state_info *state) { + cJSON *ieee8021x; cJSON *loadArr; cJSON *memory; cJSON *poe; @@ -3215,6 +3760,12 @@ static int state_fill_unit_data(cJSON *unit, struct plat_state_info *state) if (!poe || state_fill_unit_poe_data(poe, &state->poe_state)) goto err; + ieee8021x = cJSON_AddObjectToObject(unit, "ieee8021x"); + if (!ieee8021x || + state_fill_unit_ieee8021x_global_counters(ieee8021x, + &state->ieee8021x_global_coa_counters)) + goto err; + return 0; err: