Created
August 2, 2025 12:12
-
-
Save akansha-oi/47df4c39bb937c326e037faddaaf113b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Copyright (C) 2009-2016 Realtek Semiconductor Corp. | |
| * All Rights Reserved. | |
| * | |
| * This program is the proprietary software of Realtek Semiconductor | |
| * Corporation and/or its licensors, and only be used, duplicated, | |
| * modified or distributed under the authorized license from Realtek. | |
| * | |
| * ANY USE OF THE SOFTWARE OTHER THAN AS AUTHORIZED UNDER | |
| * THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. | |
| * | |
| * $Revision$ | |
| * $Date$ | |
| * | |
| * Purpose : Define diag shell main function. | |
| * | |
| * Feature : The file have include the following module and sub-modules | |
| * 1) main function. | |
| * 2) HTTP API server with comprehensive helper functions for: | |
| * - Safe JSON parsing (extract_json_string, extract_json_int, extract_json_bool) | |
| * - Input validation (is_valid_ip, is_valid_mac, is_valid_port) | |
| * - Standardized response handling (send_error_response, send_success_response) | |
| * - HTTP request parsing (parse_http_request) | |
| * - Thread-safe operations with proper error handling | |
| * 3) RTK SDK Integration - All handlers now use actual RTK functions: | |
| * - Port configuration: rtk_port_link_get, rtk_port_adminEnable_set, rtk_port_speedDuplex_set | |
| * - Flow control: rtk_port_flowCtrlEnable_get/set | |
| * - Rate limiting: rtk_rate_portIgrBwCtrlEnable_get/set, rtk_rate_portIgrBwCtrlRate_get/set | |
| * - Storm control: rtk_rate_portStormCtrlEnable_get/set, rtk_rate_portStormCtrlRate_get/set | |
| * - VLAN management: rtk_vlan_create/destroy, rtk_vlan_port_get/set | |
| * - Port isolation: rtk_port_isolation_get/set for protected port functionality | |
| * - MAC learning: rtk_l2_portLimitLearningCnt_get/set | |
| * - Statistics: rtk_stat_port_get for comprehensive port statistics | |
| */ | |
| /* | |
| * Include Files | |
| */ | |
| #include <common/rt_type.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <signal.h> | |
| #include <unistd.h> | |
| #include <sys/types.h> | |
| #include <sys/wait.h> | |
| #include <errno.h> | |
| #include <stddef.h> | |
| #include <stdlib.h> | |
| #include <diag_util.h> | |
| #include <diag_om.h> | |
| #include <diag_str.h> | |
| #include <diag_debug.h> | |
| #include <parser/cparser.h> | |
| #include <parser/cparser_priv.h> | |
| #include <parser/cparser_token.h> | |
| #include <parser/cparser_tree.h> | |
| #include <osal/memory.h> | |
| #include <pthread.h> | |
| #include <arpa/inet.h> | |
| #include <sys/socket.h> | |
| #include <netinet/in.h> | |
| #include <netinet/udp.h> | |
| #include <sys/sysinfo.h> | |
| #include <sys/statvfs.h> | |
| #include <math.h> | |
| #include <sys/stat.h> | |
| #include <rtk/stat.h> | |
| #include <rtk/port.h> | |
| #include <rtk/phy.h> | |
| #include <rtk/l2.h> | |
| #include <rtk/switch.h> | |
| #include <rtk/vlan.h> | |
| #include <hal/common/halctrl.h> | |
| #include <sys/reboot.h> | |
| #include <stdbool.h> | |
| #include <rtk/rate.h> | |
| #include <rtk/qos.h> | |
| #include <rtk/trap.h> | |
| #include <rtk/acl.h> | |
| #include <rtk/stat.h> | |
| #include <rtk/l2.h> | |
| #include <rtk/mcast.h> | |
| #include <rtk/ipmcast.h> | |
| #include <rtk/mirror.h> | |
| #include <rtk/trunk.h> | |
| #include <rtk/l3.h> | |
| #include <rtk/qos.h> | |
| #include <rtk/rate.h> | |
| #include <rtk/vlan.h> | |
| #include <rtk/stp.h> | |
| #include <rtk/flowctrl.h> | |
| #include <rtk/init.h> | |
| #include <stddef.h> | |
| #include <limits.h> | |
| #include <arpa/inet.h> | |
| #include <netinet/in.h> | |
| #ifdef CONFIG_RISE | |
| #include <hwp/hw_profile.h> | |
| #include <rise/src/rise.h> | |
| #endif | |
| #define BACKENDPORT 8443 | |
| #define BUFFER_SIZE 1024 | |
| #define MAX_PORTS 16 | |
| #define MAX_VLANS 32 | |
| #define MAX_USERS 10 | |
| #define MAX_COMMUNITIES 10 | |
| #define MAX_TRAP_TARGETS 10 | |
| #define MAX_ACL_ENTRIES 100 | |
| #define MAX_MAC_BINDINGS 1000 | |
| #define MAX_MAC_AUTO_BINDINGS 1000 | |
| #define MAX_RMON_STATISTICS 100 | |
| #define MAX_RMON_HISTORY 100 | |
| #define MAX_RMON_ALARMS 100 | |
| #define MAX_RMON_EVENTS 100 | |
| #define MAX_IGMP_CONFIGS 10 | |
| #define MAX_MULTICAST_GROUPS 100 | |
| #define MAX_STRING_LEN 64 | |
| #define MAX_IP_LEN 16 | |
| volatile int server_running = 1; | |
| int i , j , p; | |
| void build_json_response(int client_socket, const char *json_content); | |
| // ============================================================================================================================================================ | |
| // GLOBAL DATA STRUCTURES | |
| // ============================================================================================================================================================ | |
| // Mutex for thread safety | |
| pthread_mutex_t global_data_mutex = PTHREAD_MUTEX_INITIALIZER; | |
| // System Configuration | |
| typedef struct { | |
| char systemName[MAX_STRING_LEN]; | |
| char systemLocation[MAX_STRING_LEN]; | |
| char systemContact[MAX_STRING_LEN]; | |
| char description[MAX_STRING_LEN]; | |
| char objectId[MAX_STRING_LEN]; | |
| char version[MAX_STRING_LEN]; | |
| char serialNumber[MAX_STRING_LEN]; | |
| char macAddress[MAX_STRING_LEN]; | |
| char ipAddress[MAX_IP_LEN]; | |
| int interfaces; | |
| } SystemConfig; | |
| // Port Configuration | |
| typedef struct { | |
| int port_id; | |
| bool admin_enabled; | |
| char status[16]; // "Up" or "Down" | |
| char speed[16]; // "10M", "100M", "1000M", "Auto" | |
| char duplex[16]; // "Full", "Half", "Auto" | |
| bool flow_control_enabled; | |
| int ingress_rate_limit; | |
| int egress_rate_limit; | |
| bool rate_limit_enabled; | |
| int max_mac_addresses; | |
| bool protected_port; | |
| // Storm control | |
| bool broadcast_suppression; | |
| int broadcast_rate; | |
| bool multicast_suppression; | |
| int multicast_rate; | |
| bool dlf_suppression; | |
| int dlf_rate; | |
| } PortConfig; | |
| // User Management | |
| typedef struct { | |
| char username[MAX_STRING_LEN]; | |
| char password[MAX_STRING_LEN]; | |
| int level; | |
| int timeout; | |
| bool active; | |
| } UserEntry; | |
| // VLAN Configuration | |
| typedef struct { | |
| int vid; | |
| char vlan_name[MAX_STRING_LEN]; | |
| bool active; | |
| char port_members[MAX_STRING_LEN]; // Bitmap or string representation | |
| char ip_address[MAX_IP_LEN]; | |
| int dhcp_client; | |
| } VlanConfig; | |
| // QoS Configuration | |
| typedef struct { | |
| int port; | |
| char qos_type[MAX_STRING_LEN]; | |
| int priority; | |
| char schedule_mode[16]; // "WRR" or "WFQ" | |
| int weights[8]; | |
| } QosConfig; | |
| // SNMP Configuration | |
| typedef struct { | |
| char community[MAX_STRING_LEN]; | |
| char access[16]; // "read-only" or "read-write" | |
| bool active; | |
| } SnmpCommunity; | |
| typedef struct { | |
| char ip[MAX_IP_LEN]; | |
| char community[MAX_STRING_LEN]; | |
| char version[8]; // "v1", "v2c", "v3" | |
| bool active; | |
| } TrapTarget; | |
| // ACL Configuration | |
| typedef struct { | |
| int group_num; | |
| char type[16]; // "standard-ip", "extended-ip", "mac-ip", "mac-arp" | |
| char source_ip[MAX_IP_LEN]; | |
| char source_wildcard[MAX_IP_LEN]; | |
| char destination_ip[MAX_IP_LEN]; | |
| char source_mac[32]; | |
| char protocol_type[16]; | |
| char action[16]; // "permit" or "deny" | |
| bool active; | |
| } AclEntry; | |
| // Service Access Configuration | |
| // Service Access Configuration | |
| typedef struct { | |
| bool http_enabled; | |
| bool https_enabled; | |
| bool snmp_enabled; | |
| bool telnet_enabled; | |
| bool ssh_enabled; | |
| int http_acl; | |
| int snmp_acl; | |
| int telnet_acl; | |
| int ssh_acl; | |
| } ServiceAccess; | |
| // SNTP Configuration | |
| typedef struct { | |
| bool enabled; | |
| char server_ip[MAX_IP_LEN]; | |
| int interval; | |
| } SntpConfig; | |
| // Serial Configuration | |
| typedef struct { | |
| char baud_rate[16]; | |
| char char_size[8]; | |
| char parity[16]; | |
| char stop_bits[8]; | |
| char flow_control[16]; | |
| } SerialConfig; | |
| // LLDP Configuration | |
| typedef struct { | |
| bool lldp_global; | |
| int hold_multiplier; | |
| int reinit_delay; | |
| int tx_delay; | |
| int tx_interval; | |
| } LldpGlobalConfig; | |
| typedef struct { | |
| int port; | |
| bool enabled; | |
| char manage_ip[MAX_IP_LEN]; | |
| bool transmit_enabled; | |
| bool receive_enabled; | |
| } LldpPortConfig; | |
| // MAC Binding Configuration | |
| typedef struct { | |
| int port; | |
| char macAddress[32]; | |
| int vlanId; | |
| bool active; | |
| } MacBinding; | |
| // MAC Auto Bind Configuration | |
| typedef struct { | |
| int port; | |
| char macAddress[32]; | |
| int vlanId; | |
| bool autoLearned; | |
| bool active; | |
| } MacAutoBind; | |
| // RMON Statistics Configuration | |
| typedef struct { | |
| int port; | |
| int index; | |
| char owner[MAX_STRING_LEN]; | |
| // Statistics counters | |
| uint64 etherStatsDropEvents; | |
| uint64 etherStatsPkts; | |
| uint64 etherStatsMulticastPkts; | |
| uint64 etherStatsUndersizePkts; | |
| uint64 etherStatsFragments; | |
| uint64 etherStatsCollisions; | |
| uint64 etherStatsPkts65to127Octets; | |
| uint64 etherStatsPkts256to511Octets; | |
| uint64 etherStatsPkts1024to1518Octets; | |
| uint64 etherStatsOctets; | |
| uint64 etherStatsBroadcastPkts; | |
| uint64 etherStatsCRCAlignErrors; | |
| uint64 etherStatsOversizePkts; | |
| uint64 etherStatsJabbers; | |
| uint64 etherStatsPkts64Octets; | |
| uint64 etherStatsPkts128to255Octets; | |
| uint64 etherStatsPkts512to1023Octets; | |
| bool active; | |
| } RmonStatistics; | |
| // RMON History Configuration | |
| typedef struct { | |
| int port; | |
| int index; | |
| int interval; | |
| int buckets; | |
| char owner[MAX_STRING_LEN]; | |
| bool active; | |
| } RmonHistory; | |
| // RMON Alarm Configuration | |
| typedef struct { | |
| char sequenceType[16]; | |
| int sequenceIndex; | |
| int interval; | |
| char variable[MAX_STRING_LEN]; | |
| char sampleType[16]; // "absolute" or "delta" | |
| uint64 alarmValue; | |
| uint64 risingThreshold; | |
| uint64 fallingThreshold; | |
| int risingEventIndex; | |
| int fallingEventIndex; | |
| char owner[MAX_STRING_LEN]; | |
| bool active; | |
| } RmonAlarm; | |
| // RMON Event Configuration | |
| typedef struct { | |
| char sequenceType[16]; | |
| int sequenceIndex; | |
| int index; | |
| char description[256]; | |
| char type[32]; // "none", "log", "snmptrap", "log-and-trap" | |
| char community[MAX_STRING_LEN]; | |
| char lastTimeSent[32]; | |
| char owner[MAX_STRING_LEN]; | |
| bool active; | |
| } RmonEvent; | |
| // IGMP Snooping Configuration | |
| typedef struct { | |
| bool globalIgmpSnooping; | |
| char vlanId[16]; | |
| bool vlanIgmpSnooping; | |
| bool fastLeave; | |
| int fastLeaveTimeout; | |
| int queryMembershipTimeout; | |
| int groupMembershipTimeout; | |
| bool active; | |
| } IgmpSnoopingConfig; | |
| // Multicast Group Information | |
| typedef struct { | |
| char vlanId[16]; | |
| char multicastAddress[16]; | |
| char memberPorts[MAX_STRING_LEN]; | |
| bool active; | |
| } MulticastGroup; | |
| // Global Data Storage | |
| typedef struct { | |
| SystemConfig system; | |
| PortConfig ports[MAX_PORTS]; | |
| UserEntry users[MAX_USERS]; | |
| VlanConfig vlans[MAX_VLANS]; | |
| QosConfig qos_configs[MAX_PORTS]; | |
| SnmpCommunity communities[MAX_COMMUNITIES]; | |
| TrapTarget trap_targets[MAX_TRAP_TARGETS]; | |
| AclEntry acl_entries[MAX_ACL_ENTRIES]; | |
| MacBinding mac_bindings[MAX_MAC_BINDINGS]; | |
| MacAutoBind mac_auto_bindings[MAX_MAC_AUTO_BINDINGS]; | |
| RmonStatistics rmon_statistics[MAX_RMON_STATISTICS]; | |
| RmonHistory rmon_history[MAX_RMON_HISTORY]; | |
| RmonAlarm rmon_alarms[MAX_RMON_ALARMS]; | |
| RmonEvent rmon_events[MAX_RMON_EVENTS]; | |
| IgmpSnoopingConfig igmp_configs[MAX_IGMP_CONFIGS]; | |
| MulticastGroup multicast_groups[MAX_MULTICAST_GROUPS]; | |
| ServiceAccess service_access; | |
| SntpConfig sntp; | |
| SerialConfig serial; | |
| LldpGlobalConfig lldp_global; | |
| LldpPortConfig lldp_ports[MAX_PORTS]; | |
| int jumbo_frame_size; | |
| int user_count; | |
| int community_count; | |
| int trap_target_count; | |
| int acl_entry_count; | |
| int vlan_count; | |
| int mac_binding_count; | |
| int mac_auto_binding_count; | |
| int rmon_statistics_count; | |
| int rmon_history_count; | |
| int rmon_alarm_count; | |
| int rmon_event_count; | |
| int igmp_config_count; | |
| int multicast_group_count; | |
| } GlobalDataStore; | |
| // Initialize global data store | |
| GlobalDataStore g_data; | |
| // ============================================================================================================================================================ | |
| // UTILITY FUNCTIONS | |
| // ============================================================================================================================================================ | |
| // Safe string copy function | |
| void safe_strcpy(char *dest, const char *src, unsigned int dest_size) { | |
| if (dest && src && dest_size > 0) { | |
| strncpy(dest, src, dest_size - 1); | |
| dest[dest_size - 1] = '\0'; | |
| } | |
| } | |
| // Safe JSON string extraction with bounds checking | |
| int extract_json_string(const char *json, const char *key, char *out_value, unsigned int out_size) { | |
| if (!json || !key || !out_value || out_size == 0) { | |
| return -1; | |
| } | |
| char search_key[MAX_STRING_LEN]; | |
| snprintf(search_key, sizeof(search_key), "\"%s\"", key); | |
| char *key_pos = strstr(json, search_key); | |
| if (!key_pos) { | |
| return -1; | |
| } | |
| char *colon = strchr(key_pos, ':'); | |
| if (!colon) { | |
| return -1; | |
| } | |
| colon++; | |
| // Skip whitespace | |
| while (*colon == ' ' || *colon == '\t' || *colon == '\n' || *colon == '\r') { | |
| colon++; | |
| } | |
| if (*colon != '"') { | |
| return -1; | |
| } | |
| colon++; // Skip opening quote | |
| char *end_quote = strchr(colon, '"'); | |
| if (!end_quote) { | |
| return -1; | |
| } | |
| unsigned int len = end_quote - colon; | |
| if (len >= out_size) { | |
| len = out_size - 1; | |
| } | |
| strncpy(out_value, colon, len); | |
| out_value[len] = '\0'; | |
| return 0; | |
| } | |
| // Safe JSON integer extraction | |
| int extract_json_int(const char *json, const char *key, int *out_value) { | |
| if (!json || !key || !out_value) { | |
| return -1; | |
| } | |
| char search_key[MAX_STRING_LEN]; | |
| snprintf(search_key, sizeof(search_key), "\"%s\"", key); | |
| char *key_pos = strstr(json, search_key); | |
| if (!key_pos) { | |
| return -1; | |
| } | |
| char *colon = strchr(key_pos, ':'); | |
| if (!colon) { | |
| return -1; | |
| } | |
| colon++; | |
| // Skip whitespace | |
| while (*colon == ' ' || *colon == '\t' || *colon == '\n' || *colon == '\r') { | |
| colon++; | |
| } | |
| *out_value = atoi(colon); | |
| return 0; | |
| } | |
| // Safe JSON boolean extraction | |
| int extract_json_bool(const char *json, const char *key, bool *out_value) { | |
| if (!json || !key || !out_value) { | |
| return -1; | |
| } | |
| char search_key[MAX_STRING_LEN]; | |
| snprintf(search_key, sizeof(search_key), "\"%s\"", key); | |
| char *key_pos = strstr(json, search_key); | |
| if (!key_pos) { | |
| return -1; | |
| } | |
| char *colon = strchr(key_pos, ':'); | |
| if (!colon) { | |
| return -1; | |
| } | |
| colon++; | |
| // Skip whitespace | |
| while (*colon == ' ' || *colon == '\t' || *colon == '\n' || *colon == '\r') { | |
| colon++; | |
| } | |
| if (strncmp(colon, "true", 4) == 0) { | |
| *out_value = true; | |
| return 0; | |
| } else if (strncmp(colon, "false", 5) == 0) { | |
| *out_value = false; | |
| return 0; | |
| } | |
| return -1; | |
| } | |
| // Helper function to send error response | |
| void send_error_response(int client_socket, int status_code, const char *error_message) { | |
| char response[BUFFER_SIZE]; | |
| const char *status_text = (status_code == 400) ? "Bad Request" : | |
| (status_code == 404) ? "Not Found" : | |
| (status_code == 500) ? "Internal Server Error" : "Error"; | |
| snprintf(response, sizeof(response), | |
| "HTTP/1.1 %d %s\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n" | |
| "{\"error\": \"%s\"}", | |
| status_code, status_text, error_message ? error_message : "Unknown error"); | |
| send(client_socket, response, strlen(response), 0); | |
| } | |
| // Helper function to send success response with JSON | |
| void send_success_response(int client_socket, const char *json_content) { | |
| if (!json_content) { | |
| send_error_response(client_socket, 500, "No content provided"); | |
| return; | |
| } | |
| build_json_response(client_socket, json_content); | |
| } | |
| // Helper function to validate port number | |
| bool is_valid_port(int port, int min_port, int max_port) { | |
| return (port >= min_port && port <= max_port); | |
| } | |
| // Helper function to validate IP address format (basic validation) | |
| bool is_valid_ip(const char *ip) { | |
| if (!ip || strlen(ip) == 0) { | |
| return false; | |
| } | |
| int parts[4]; | |
| int count = sscanf(ip, "%d.%d.%d.%d", &parts[0], &parts[1], &parts[2], &parts[3]); | |
| if (count != 4) { | |
| return false; | |
| } | |
| for (i = 0; i < 4; i++) { | |
| if (parts[i] < 0 || parts[i] > 255) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| // Helper function to validate MAC address format | |
| bool is_valid_mac(const char *mac) { | |
| if (!mac || strlen(mac) != 17) { | |
| return false; | |
| } | |
| for (i = 0; i < 17; i++) { | |
| if (i % 3 == 2) { | |
| if (mac[i] != ':') { | |
| return false; | |
| } | |
| } else { | |
| if (!isxdigit(mac[i])) { | |
| return false; | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| // Helper function to create standard HTTP headers | |
| void add_standard_headers(char *response, unsigned int response_size) { | |
| snprintf(response, response_size, | |
| "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"); | |
| } | |
| // Helper function to parse request method and path | |
| int parse_http_request(const char *buffer, char *method, char *path, char **body, char **query) { | |
| if (!buffer || !method || !path) { | |
| return -1; | |
| } | |
| char protocol[16]; | |
| if (sscanf(buffer, "%15s %255s %15s", method, path, protocol) != 3) { | |
| return -1; | |
| } | |
| // Split path and query | |
| char *query_start = strchr(path, '?'); | |
| if (query_start) { | |
| *query_start = '\0'; | |
| *query = query_start + 1; | |
| } else { | |
| *query = ""; | |
| } | |
| // Find body | |
| char *body_start = strstr(buffer, "\r\n\r\n"); | |
| if (body_start) { | |
| *body = body_start + 4; | |
| } else { | |
| *body = ""; | |
| } | |
| return 0; | |
| } | |
| // Thread-safe response builder | |
| void build_json_response(int client_socket, const char *json_content) { | |
| if (!json_content) { | |
| return; | |
| } | |
| unsigned int content_len = strlen(json_content); | |
| unsigned int total_size = content_len + 200; // Headers + content | |
| char *response = malloc(total_size); | |
| if (!response) { | |
| const char *error_response = "HTTP/1.1 500 Internal Server Error\r\n\r\n"; | |
| send(client_socket, error_response, strlen(error_response), 0); | |
| return; | |
| } | |
| snprintf(response, total_size, | |
| "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n" | |
| "Content-Length: %zu\r\n\r\n%s", | |
| content_len, json_content); | |
| send(client_socket, response, strlen(response), 0); | |
| free(response); | |
| } | |
| void send_response(int client_socket, const char *response) | |
| { | |
| if (response) { | |
| send(client_socket, response, strlen(response), 0); | |
| } | |
| } | |
| static int32 | |
| dlag_initial_promt_unit_id(cparser_t *pParser) | |
| { | |
| uint32 unit; | |
| if (diag_om_get_firstAvailableUnit(&unit) != RT_ERR_OK) | |
| { | |
| diag_util_printf("diag_main: Obtain avaliable unit ID fail.\n"); | |
| return RT_ERR_FAILED; | |
| } | |
| if (diag_om_set_deviceInfo(unit) != RT_ERR_OK) | |
| { | |
| diag_util_printf("diag_main: set unit %u deviceInfo error.\n", unit); | |
| return RT_ERR_FAILED; | |
| } | |
| diag_om_get_promptString((uint8 *)pParser->cfg.prompt, sizeof(pParser->cfg.prompt), unit); | |
| diag_om_set_unitId(unit); | |
| return RT_ERR_OK; | |
| } | |
| // Helper function to parse IP address into 32-bit integer | |
| uint32 parse_ip_address(const char *ip_str) { | |
| unsigned int a, b, c, d; | |
| if (sscanf(ip_str, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) { | |
| return 0; | |
| } | |
| return (a << 24) | (b << 16) | (c << 8) | d; | |
| } | |
| // Helper function to parse MAC address | |
| int parse_mac_address(const char *mac_str, uint8 *mac_addr) { | |
| unsigned int mac[6]; | |
| if (sscanf(mac_str, "%02x:%02x:%02x:%02x:%02x:%02x", | |
| &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) != 6) { | |
| return -1; | |
| } | |
| for (i=0; i < 6; i++) { | |
| mac_addr[i] = (uint8)mac[i]; | |
| } | |
| return 0; | |
| } | |
| // Helper function to apply ACL rule using RTK SDK | |
| int apply_acl_rule_to_hardware(AclEntry *entry) { | |
| uint32 unit = 0; | |
| rtk_acl_phase_t phase = ACL_PHASE_IGR_ACL; | |
| rtk_acl_id_t entry_idx = entry->group_num; | |
| rtk_acl_action_t action; | |
| int ret; | |
| // Initialize action structure | |
| memset(&action, 0, sizeof(rtk_acl_action_t)); | |
| // Configure field matching based on ACL type | |
| if (strcmp(entry->type, "standard-ip") == 0) { | |
| // Configure source IP field | |
| uint32 src_ip = parse_ip_address(entry->source_ip); | |
| uint32 src_mask = parse_ip_address(entry->source_wildcard); | |
| src_mask = ~src_mask; // Convert wildcard to mask | |
| uint8 ip_data[4], ip_mask[4]; | |
| ip_data[0] = (src_ip >> 24) & 0xFF; | |
| ip_data[1] = (src_ip >> 16) & 0xFF; | |
| ip_data[2] = (src_ip >> 8) & 0xFF; | |
| ip_data[3] = src_ip & 0xFF; | |
| ip_mask[0] = (src_mask >> 24) & 0xFF; | |
| ip_mask[1] = (src_mask >> 16) & 0xFF; | |
| ip_mask[2] = (src_mask >> 8) & 0xFF; | |
| ip_mask[3] = src_mask & 0xFF; | |
| ret = rtk_acl_ruleEntryField_write(unit, phase, entry_idx, | |
| USER_FIELD_IP4_SIP, ip_data, ip_mask); | |
| if (ret != RT_ERR_OK) { | |
| return -1; | |
| } | |
| } | |
| else if (strcmp(entry->type, "extended-ip") == 0) { | |
| // Configure source IP field | |
| uint32 src_ip = parse_ip_address(entry->source_ip); | |
| uint8 src_data[4], src_mask[4]; | |
| src_data[0] = (src_ip >> 24) & 0xFF; | |
| src_data[1] = (src_ip >> 16) & 0xFF; | |
| src_data[2] = (src_ip >> 8) & 0xFF; | |
| src_data[3] = src_ip & 0xFF; | |
| memset(src_mask, 0xFF, 4); // Full match | |
| ret = rtk_acl_ruleEntryField_write(unit, phase, entry_idx, | |
| USER_FIELD_IP4_SIP, src_data, src_mask); | |
| if (ret != RT_ERR_OK) { | |
| return -1; | |
| } | |
| // Configure destination IP field | |
| uint32 dst_ip = parse_ip_address(entry->destination_ip); | |
| uint8 dst_data[4], dst_mask[4]; | |
| dst_data[0] = (dst_ip >> 24) & 0xFF; | |
| dst_data[1] = (dst_ip >> 16) & 0xFF; | |
| dst_data[2] = (dst_ip >> 8) & 0xFF; | |
| dst_data[3] = dst_ip & 0xFF; | |
| memset(dst_mask, 0xFF, 4); // Full match | |
| ret = rtk_acl_ruleEntryField_write(unit, phase, entry_idx, | |
| USER_FIELD_IP4_DIP, dst_data, dst_mask); | |
| if (ret != RT_ERR_OK) { | |
| return -1; | |
| } | |
| // Configure protocol type if specified | |
| if (strlen(entry->protocol_type) > 0) { | |
| uint8 proto_data = 0, proto_mask = 0xFF; | |
| if (strcmp(entry->protocol_type, "tcp") == 0) { | |
| proto_data = 6; | |
| } else if (strcmp(entry->protocol_type, "udp") == 0) { | |
| proto_data = 17; | |
| } else if (strcmp(entry->protocol_type, "icmp") == 0) { | |
| proto_data = 1; | |
| } | |
| if (proto_data > 0) { | |
| ret = rtk_acl_ruleEntryField_write(unit, phase, entry_idx, | |
| USER_FIELD_IP4PROTO_IP6NH, &proto_data, &proto_mask); | |
| if (ret != RT_ERR_OK) { | |
| return -1; | |
| } | |
| } | |
| } | |
| } | |
| else if (strcmp(entry->type, "mac-ip") == 0 || strcmp(entry->type, "mac-arp") == 0) { | |
| // Configure source MAC field | |
| uint8 mac_data[6], mac_mask[6]; | |
| if (parse_mac_address(entry->source_mac, mac_data) == 0) { | |
| memset(mac_mask, 0xFF, 6); // Full match | |
| ret = rtk_acl_ruleEntryField_write(unit, phase, entry_idx, | |
| USER_FIELD_SMAC, mac_data, mac_mask); | |
| if (ret != RT_ERR_OK) { | |
| return -1; | |
| } | |
| } | |
| // Configure IP field for mac-ip type | |
| if (strcmp(entry->type, "mac-ip") == 0 && strlen(entry->destination_ip) > 0) { | |
| uint32 dst_ip = parse_ip_address(entry->destination_ip); | |
| uint8 ip_data[4], ip_mask[4]; | |
| ip_data[0] = (dst_ip >> 24) & 0xFF; | |
| ip_data[1] = (dst_ip >> 16) & 0xFF; | |
| ip_data[2] = (dst_ip >> 8) & 0xFF; | |
| ip_data[3] = dst_ip & 0xFF; | |
| memset(ip_mask, 0xFF, 4); // Full match | |
| ret = rtk_acl_ruleEntryField_write(unit, phase, entry_idx, | |
| USER_FIELD_IP4_DIP, ip_data, ip_mask); | |
| if (ret != RT_ERR_OK) { | |
| return -1; | |
| } | |
| } | |
| // Configure source IP for mac-arp type | |
| if (strcmp(entry->type, "mac-arp") == 0 && strlen(entry->source_ip) > 0) { | |
| uint32 src_ip = parse_ip_address(entry->source_ip); | |
| uint8 ip_data[4], ip_mask[4]; | |
| ip_data[0] = (src_ip >> 24) & 0xFF; | |
| ip_data[1] = (src_ip >> 16) & 0xFF; | |
| ip_data[2] = (src_ip >> 8) & 0xFF; | |
| ip_data[3] = src_ip & 0xFF; | |
| memset(ip_mask, 0xFF, 4); // Full match | |
| ret = rtk_acl_ruleEntryField_write(unit, phase, entry_idx, | |
| USER_FIELD_IP4_SIP, ip_data, ip_mask); | |
| if (ret != RT_ERR_OK) { | |
| return -1; | |
| } | |
| } | |
| } | |
| // Configure forward action based on permit/deny | |
| action.igr_acl.fwd_en = ENABLED; | |
| if (strcmp(entry->action, "permit") == 0) { | |
| action.igr_acl.fwd_data.fwd_type = ACL_IGR_ACTION_FWD_PERMIT; | |
| } else { | |
| action.igr_acl.fwd_data.fwd_type = ACL_IGR_ACTION_FWD_DROP; | |
| } | |
| // Set the ACL rule action | |
| ret = rtk_acl_ruleAction_set(unit, phase, entry_idx, &action); | |
| if (ret != RT_ERR_OK) { | |
| return -1; | |
| } | |
| // Enable the ACL rule | |
| ret = rtk_acl_ruleValidate_set(unit, phase, entry_idx, ENABLED); | |
| if (ret != RT_ERR_OK) { | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| // Initialize ACL subsystem | |
| int initialize_acl_system(void) { | |
| uint32 unit = 0; | |
| int ret; | |
| int port; | |
| // Initialize ACL subsystem | |
| ret = rtk_acl_init(unit); | |
| if (ret != RT_ERR_OK) { | |
| return -1; | |
| } | |
| // Enable ACL lookup on all ports (assuming ports 0-27 for typical switch) | |
| for (port = 0; port < 28; port++) { | |
| ret = rtk_acl_portLookupEnable_set(unit, port, ENABLED); | |
| if (ret != RT_ERR_OK) { | |
| continue; // Continue with other ports if one fails | |
| } | |
| } | |
| return 0; | |
| } | |
| // Initialize Statistics subsystem | |
| int initialize_stat_system(void) { | |
| uint32 unit = 0; | |
| int ret; | |
| // Initialize statistics subsystem | |
| ret = rtk_stat_init(unit); | |
| if (ret != RT_ERR_OK) { | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| // Delete ACL entry by group number and type | |
| int delete_acl_entry(int group_num, const char *type) { | |
| for (i=0; i < g_data.acl_entry_count; i++) { | |
| if (g_data.acl_entries[i].active && | |
| g_data.acl_entries[i].group_num == group_num && | |
| strcmp(g_data.acl_entries[i].type, type) == 0) { | |
| // Disable the ACL rule in hardware | |
| uint32 unit = 0; | |
| rtk_acl_phase_t phase = ACL_PHASE_IGR_ACL; | |
| rtk_acl_ruleValidate_set(unit, phase, group_num, DISABLED); | |
| // Mark entry as inactive | |
| g_data.acl_entries[i].active = false; | |
| // Compact the array by moving remaining entries | |
| for (j = i; j < g_data.acl_entry_count - 1; j++) { | |
| g_data.acl_entries[j] = g_data.acl_entries[j + 1]; | |
| } | |
| g_data.acl_entry_count--; | |
| return 0; | |
| } | |
| } | |
| return -1; // Entry not found | |
| } | |
| // Helper function to parse MAC address for RTK SDK | |
| int parse_mac_for_rtk(const char *mac_str, rtk_mac_t *rtk_mac) { | |
| unsigned int mac[6]; | |
| int i; | |
| // Support both formats: AA:BB:CC:DD:EE:FF and AABB.CCDD.EEFF | |
| if (sscanf(mac_str, "%02x:%02x:%02x:%02x:%02x:%02x", | |
| &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) == 6) { | |
| // Colon format | |
| for (i = 0; i < 6; i++) { | |
| rtk_mac->octet[i] = (uint8)mac[i]; | |
| } | |
| return 0; | |
| } else if (sscanf(mac_str, "%04x.%04x.%04x", &mac[0], &mac[1], &mac[2]) == 3) { | |
| // Dot format | |
| rtk_mac->octet[0] = (mac[0] >> 8) & 0xFF; | |
| rtk_mac->octet[1] = mac[0] & 0xFF; | |
| rtk_mac->octet[2] = (mac[1] >> 8) & 0xFF; | |
| rtk_mac->octet[3] = mac[1] & 0xFF; | |
| rtk_mac->octet[4] = (mac[2] >> 8) & 0xFF; | |
| rtk_mac->octet[5] = mac[2] & 0xFF; | |
| return 0; | |
| } | |
| return -1; | |
| } | |
| // Helper function to format MAC address for display | |
| void format_mac_address(rtk_mac_t *rtk_mac, char *mac_str) { | |
| sprintf(mac_str, "%04x.%04x.%04x", | |
| (rtk_mac->octet[0] << 8) | rtk_mac->octet[1], | |
| (rtk_mac->octet[2] << 8) | rtk_mac->octet[3], | |
| (rtk_mac->octet[4] << 8) | rtk_mac->octet[5]); | |
| } | |
| // Add MAC binding entry to hardware | |
| int add_mac_binding_to_hardware(MacBinding *binding) { | |
| uint32 unit = 0; | |
| rtk_l2_ucastAddr_t l2_addr; | |
| rtk_mac_t rtk_mac; | |
| int ret; | |
| // Parse MAC address | |
| if (parse_mac_for_rtk(binding->macAddress, &rtk_mac) != 0) { | |
| return -1; | |
| } | |
| // Initialize L2 address structure | |
| memset(&l2_addr, 0, sizeof(rtk_l2_ucastAddr_t)); | |
| memcpy(&l2_addr.mac, &rtk_mac, sizeof(rtk_mac_t)); | |
| l2_addr.vid = binding->vlanId; | |
| l2_addr.port = binding->port; | |
| l2_addr.flags |= RTK_L2_UCAST_FLAG_STATIC; // Static entry | |
| l2_addr.state = RTK_L2_UCAST_STATE_SUSPEND; // Active state | |
| // Add to hardware | |
| ret = rtk_l2_addr_add(unit, &l2_addr); | |
| if (ret != RT_ERR_OK) { | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| // Remove MAC binding from hardware | |
| int remove_mac_binding_from_hardware(const char *macAddress, int vlanId) { | |
| uint32 unit = 0; | |
| rtk_mac_t rtk_mac; | |
| int ret; | |
| // Parse MAC address | |
| if (parse_mac_for_rtk(macAddress, &rtk_mac) != 0) { | |
| return -1; | |
| } | |
| // Remove from hardware | |
| ret = rtk_l2_addr_del(unit, vlanId, &rtk_mac); | |
| if (ret != RT_ERR_OK) { | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| //helper funcitons | |
| int extract_value_string(const char *data, const char *key, char *out_value, uint32_t out_value_size) | |
| { | |
| char *key_position = strstr(data, key); | |
| if (!key_position) | |
| return -1; // Key not found | |
| key_position += strlen(key) + 1; // Move past the key and the colon | |
| while (*key_position == ' ' || *key_position == '\n' || *key_position == '\r') | |
| { | |
| key_position++; | |
| } | |
| if (*key_position != '"') | |
| return -1; // Expected opening quote for string | |
| key_position++; // Skip the opening quote | |
| char *end_position = strchr(key_position, '"'); | |
| if (!end_position) | |
| return -1; // No closing quote found | |
| uint32_t value_length = end_position - key_position; | |
| if (value_length >= out_value_size) | |
| return -1; // Output buffer too small | |
| strncpy(out_value, key_position, value_length); | |
| out_value[value_length] = '\0'; // Null-terminate the string | |
| return 0; // Success | |
| } | |
| int extract_value_int(const char *data, const char *key) | |
| { | |
| char *key_position = strstr(data, key); | |
| if (!key_position) | |
| return -1; // Key not found | |
| key_position += strlen(key); | |
| // Skip whitespace and colon | |
| while (*key_position && (*key_position == ' ' || *key_position == '\t' || *key_position == '\n' || *key_position == '\r')) | |
| key_position++; | |
| if (*key_position != ':') | |
| return -1; | |
| key_position++; // Skip colon | |
| // Skip whitespace after colon | |
| while (*key_position && (*key_position == ' ' || *key_position == '\t' || *key_position == '\n' || *key_position == '\r')) | |
| key_position++; | |
| // Skip optional quote | |
| if (*key_position == '"') | |
| key_position++; | |
| return atoi(key_position); | |
| } | |
| int extract_value_duplex(const char *data) | |
| { | |
| return extract_value_int(data, "\"duplex\""); | |
| } | |
| int extract_value_flow_control(const char *data) | |
| { | |
| return extract_value_int(data, "\"flowControl\""); | |
| } | |
| int extract_value_port(const char *data) | |
| { | |
| return extract_value_int(data, "\"port\""); | |
| } | |
| int extract_value_mode(const char *data) | |
| { | |
| return extract_value_int(data, "\"mode\""); | |
| } | |
| int extract_value_speed(const char *data) | |
| { | |
| return extract_value_int(data, "\"speed\""); | |
| } | |
| // Helper function to collect RMON statistics from RTK SDK | |
| int collect_rmon_statistics(int port, RmonStatistics *stats) { | |
| uint32 unit = 0; | |
| uint64 counter_val; | |
| int ret; | |
| if (!stats) { | |
| return -1; | |
| } | |
| // Collect various statistics using RTK SDK | |
| ret = rtk_stat_port_get(unit, port, ETHER_STATS_DROP_EVENTS_INDEX, &counter_val); | |
| if (ret == RT_ERR_OK) stats->etherStatsDropEvents = counter_val; | |
| ret = rtk_stat_port_get(unit, port, ETHER_STATS_MULTICAST_PKTS_INDEX, &counter_val); | |
| if (ret == RT_ERR_OK) stats->etherStatsMulticastPkts = counter_val; | |
| ret = rtk_stat_port_get(unit, port, ETHER_STATS_UNDER_SIZE_PKTS_INDEX, &counter_val); | |
| if (ret == RT_ERR_OK) stats->etherStatsUndersizePkts = counter_val; | |
| ret = rtk_stat_port_get(unit, port, ETHER_STATS_FRAGMENTS_INDEX, &counter_val); | |
| if (ret == RT_ERR_OK) stats->etherStatsFragments = counter_val; | |
| ret = rtk_stat_port_get(unit, port, ETHER_STATS_COLLISIONS_INDEX, &counter_val); | |
| if (ret == RT_ERR_OK) stats->etherStatsCollisions = counter_val; | |
| ret = rtk_stat_port_get(unit, port, ETHER_STATS_PKTS_65TO127OCTETS_INDEX, &counter_val); | |
| if (ret == RT_ERR_OK) stats->etherStatsPkts65to127Octets = counter_val; | |
| ret = rtk_stat_port_get(unit, port, ETHER_STATS_PKTS_256TO511OCTETS_INDEX, &counter_val); | |
| if (ret == RT_ERR_OK) stats->etherStatsPkts256to511Octets = counter_val; | |
| ret = rtk_stat_port_get(unit, port, ETHER_STATS_PKTS_1024TO1518OCTETS_INDEX, &counter_val); | |
| if (ret == RT_ERR_OK) stats->etherStatsPkts1024to1518Octets = counter_val; | |
| ret = rtk_stat_port_get(unit, port, IF_IN_OCTETS_INDEX, &counter_val); | |
| if (ret == RT_ERR_OK) stats->etherStatsOctets = counter_val; | |
| ret = rtk_stat_port_get(unit, port, ETHER_STATS_BROADCAST_PKTS_INDEX, &counter_val); | |
| if (ret == RT_ERR_OK) stats->etherStatsBroadcastPkts = counter_val; | |
| ret = rtk_stat_port_get(unit, port, ETHER_STATS_CRC_ALIGN_ERRORS_INDEX, &counter_val); | |
| if (ret == RT_ERR_OK) stats->etherStatsCRCAlignErrors = counter_val; | |
| ret = rtk_stat_port_get(unit, port, ETHER_STATS_OVERSIZE_PKTS_INDEX, &counter_val); | |
| if (ret == RT_ERR_OK) stats->etherStatsOversizePkts = counter_val; | |
| ret = rtk_stat_port_get(unit, port, ETHER_STATS_JABBERS_INDEX, &counter_val); | |
| if (ret == RT_ERR_OK) stats->etherStatsJabbers = counter_val; | |
| ret = rtk_stat_port_get(unit, port, ETHER_STATS_PKTS_64OCTETS_INDEX, &counter_val); | |
| if (ret == RT_ERR_OK) stats->etherStatsPkts64Octets = counter_val; | |
| ret = rtk_stat_port_get(unit, port, ETHER_STATS_PKTS_128TO255OCTETS_INDEX, &counter_val); | |
| if (ret == RT_ERR_OK) stats->etherStatsPkts128to255Octets = counter_val; | |
| ret = rtk_stat_port_get(unit, port, ETHER_STATS_PKTS_512TO1023OCTETS_INDEX, &counter_val); | |
| if (ret == RT_ERR_OK) stats->etherStatsPkts512to1023Octets = counter_val; | |
| // Calculate total packets | |
| stats->etherStatsPkts = stats->etherStatsPkts64Octets + | |
| stats->etherStatsPkts65to127Octets + | |
| stats->etherStatsPkts128to255Octets + | |
| stats->etherStatsPkts256to511Octets + | |
| stats->etherStatsPkts512to1023Octets + | |
| stats->etherStatsPkts1024to1518Octets; | |
| return 0; | |
| } | |
| // ============================================================================================================================================================ | |
| // RMON HANDLERS | |
| // ============================================================================================================================================================ | |
| // RMON Statistics handlers | |
| void handle_rmon_statistics_get_request(int client_socket) | |
| { | |
| char json[4096] = ""; | |
| int i; | |
| if (g_data.rmon_statistics_count == 0) { | |
| send_success_response(client_socket, "{\"port\":\"1\",\"index\":\"0\",\"owner\":\"admin\",\"stats\":{\"etherStatsDropEvents\":0,\"etherStatsPkts\":0,\"etherStatsMulticastPkts\":0,\"etherStatsUndersizePkts\":0,\"etherStatsFragments\":0,\"etherStatsCollisions\":0,\"etherStatsPkts65to127Octets\":0,\"etherStatsPkts256to511Octets\":0,\"etherStatsPkts1024to1518Octets\":0,\"etherStatsOctets\":0,\"etherStatsBroadcastPkts\":0,\"etherStatsCRCAlignErrors\":0,\"etherStatsOversizePkts\":0,\"etherStatsJabbers\":0,\"etherStatsPkts64Octets\":0,\"etherStatsPkts128to255Octets\":0,\"etherStatsPkts512to1023Octets\":0}}"); | |
| return; | |
| } | |
| // Return the first active RMON statistics entry | |
| for (i = 0; i < g_data.rmon_statistics_count; i++) { | |
| if (g_data.rmon_statistics[i].active) { | |
| // Collect real-time statistics | |
| collect_rmon_statistics(g_data.rmon_statistics[i].port, &g_data.rmon_statistics[i]); | |
| sprintf(json, | |
| "{\"port\":\"%d\",\"index\":\"%d\",\"owner\":\"%s\",\"stats\":{" | |
| "\"etherStatsDropEvents\":%llu,\"etherStatsPkts\":%llu,\"etherStatsMulticastPkts\":%llu," | |
| "\"etherStatsUndersizePkts\":%llu,\"etherStatsFragments\":%llu,\"etherStatsCollisions\":%llu," | |
| "\"etherStatsPkts65to127Octets\":%llu,\"etherStatsPkts256to511Octets\":%llu," | |
| "\"etherStatsPkts1024to1518Octets\":%llu,\"etherStatsOctets\":%llu," | |
| "\"etherStatsBroadcastPkts\":%llu,\"etherStatsCRCAlignErrors\":%llu," | |
| "\"etherStatsOversizePkts\":%llu,\"etherStatsJabbers\":%llu," | |
| "\"etherStatsPkts64Octets\":%llu,\"etherStatsPkts128to255Octets\":%llu," | |
| "\"etherStatsPkts512to1023Octets\":%llu}}", | |
| g_data.rmon_statistics[i].port, g_data.rmon_statistics[i].index, g_data.rmon_statistics[i].owner, | |
| g_data.rmon_statistics[i].etherStatsDropEvents, g_data.rmon_statistics[i].etherStatsPkts, | |
| g_data.rmon_statistics[i].etherStatsMulticastPkts, g_data.rmon_statistics[i].etherStatsUndersizePkts, | |
| g_data.rmon_statistics[i].etherStatsFragments, g_data.rmon_statistics[i].etherStatsCollisions, | |
| g_data.rmon_statistics[i].etherStatsPkts65to127Octets, g_data.rmon_statistics[i].etherStatsPkts256to511Octets, | |
| g_data.rmon_statistics[i].etherStatsPkts1024to1518Octets, g_data.rmon_statistics[i].etherStatsOctets, | |
| g_data.rmon_statistics[i].etherStatsBroadcastPkts, g_data.rmon_statistics[i].etherStatsCRCAlignErrors, | |
| g_data.rmon_statistics[i].etherStatsOversizePkts, g_data.rmon_statistics[i].etherStatsJabbers, | |
| g_data.rmon_statistics[i].etherStatsPkts64Octets, g_data.rmon_statistics[i].etherStatsPkts128to255Octets, | |
| g_data.rmon_statistics[i].etherStatsPkts512to1023Octets); | |
| break; | |
| } | |
| } | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_rmon_statistics_post_request(int client_socket, const char *body) | |
| { | |
| char port_str[8], index_str[8], owner[MAX_STRING_LEN]; | |
| int port, index; | |
| // Extract JSON fields | |
| if (extract_json_string(body, "port", port_str, sizeof(port_str)) != 0 || | |
| extract_json_string(body, "index", index_str, sizeof(index_str)) != 0 || | |
| extract_json_string(body, "owner", owner, sizeof(owner)) != 0) { | |
| send_error_response(client_socket, 400, "Missing required fields"); | |
| return; | |
| } | |
| port = atoi(port_str); | |
| index = atoi(index_str); | |
| // Validate port | |
| if (!is_valid_port(port, 1, 28)) { | |
| send_error_response(client_socket, 400, "Invalid port number"); | |
| return; | |
| } | |
| // Check if we have space for more RMON statistics | |
| if (g_data.rmon_statistics_count >= MAX_RMON_STATISTICS) { | |
| send_error_response(client_socket, 400, "Maximum RMON statistics entries reached"); | |
| return; | |
| } | |
| // Store RMON statistics entry | |
| int idx = g_data.rmon_statistics_count; | |
| g_data.rmon_statistics[idx].port = port; | |
| g_data.rmon_statistics[idx].index = index; | |
| strcpy(g_data.rmon_statistics[idx].owner, owner); | |
| g_data.rmon_statistics[idx].active = true; | |
| // Collect initial statistics | |
| collect_rmon_statistics(port, &g_data.rmon_statistics[idx]); | |
| g_data.rmon_statistics_count++; | |
| send_success_response(client_socket, "{\"message\":\"RMON statistics configuration applied successfully\"}"); | |
| } | |
| void handle_rmon_statistics_delete_request(int client_socket, const char *body) | |
| { | |
| char index_str[8]; | |
| int index, i; | |
| // Extract index from JSON | |
| if (extract_json_string(body, "index", index_str, sizeof(index_str)) != 0) { | |
| send_error_response(client_socket, 400, "Index is required"); | |
| return; | |
| } | |
| index = atoi(index_str); | |
| // Find and remove the RMON statistics entry | |
| for (i = 0; i < g_data.rmon_statistics_count; i++) { | |
| if (g_data.rmon_statistics[i].active && g_data.rmon_statistics[i].index == index) { | |
| g_data.rmon_statistics[i].active = false; | |
| // Compact the array | |
| int j; | |
| for (j = i; j < g_data.rmon_statistics_count - 1; j++) { | |
| g_data.rmon_statistics[j] = g_data.rmon_statistics[j + 1]; | |
| } | |
| g_data.rmon_statistics_count--; | |
| send_success_response(client_socket, "{\"message\":\"RMON statistics configuration deleted successfully\"}"); | |
| return; | |
| } | |
| } | |
| send_error_response(client_socket, 404, "RMON statistics entry not found"); | |
| } | |
| // RMON History handlers | |
| void handle_rmon_history_get_request(int client_socket) | |
| { | |
| char json[2048] = ""; | |
| int i; | |
| if (g_data.rmon_history_count == 0) { | |
| send_success_response(client_socket, "{\"port\":\"1\",\"index\":\"0\",\"interval\":\"1800\",\"buckets\":\"50\",\"owner\":\"admin\",\"totalPages\":1,\"currentPage\":1,\"historyData\":[]}"); | |
| return; | |
| } | |
| // Return the first active RMON history entry | |
| for (i = 0; i < g_data.rmon_history_count; i++) { | |
| if (g_data.rmon_history[i].active) { | |
| sprintf(json, | |
| "{\"port\":\"%d\",\"index\":\"%d\",\"interval\":\"%d\",\"buckets\":\"%d\",\"owner\":\"%s\",\"totalPages\":1,\"currentPage\":1,\"historyData\":[]}", | |
| g_data.rmon_history[i].port, g_data.rmon_history[i].index, | |
| g_data.rmon_history[i].interval, g_data.rmon_history[i].buckets, | |
| g_data.rmon_history[i].owner); | |
| break; | |
| } | |
| } | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_rmon_history_post_request(int client_socket, const char *body) | |
| { | |
| char port_str[8], index_str[8], interval_str[16], buckets_str[16], owner[MAX_STRING_LEN]; | |
| int port, index, interval, buckets; | |
| // Extract JSON fields | |
| if (extract_json_string(body, "port", port_str, sizeof(port_str)) != 0 || | |
| extract_json_string(body, "index", index_str, sizeof(index_str)) != 0 || | |
| extract_json_string(body, "interval", interval_str, sizeof(interval_str)) != 0 || | |
| extract_json_string(body, "buckets", buckets_str, sizeof(buckets_str)) != 0 || | |
| extract_json_string(body, "owner", owner, sizeof(owner)) != 0) { | |
| send_error_response(client_socket, 400, "Missing required fields"); | |
| return; | |
| } | |
| port = atoi(port_str); | |
| index = atoi(index_str); | |
| interval = atoi(interval_str); | |
| buckets = atoi(buckets_str); | |
| // Validate inputs | |
| if (!is_valid_port(port, 1, 28)) { | |
| send_error_response(client_socket, 400, "Invalid port number"); | |
| return; | |
| } | |
| if (interval <= 0) { | |
| send_error_response(client_socket, 400, "Invalid interval"); | |
| return; | |
| } | |
| if (buckets <= 0) { | |
| send_error_response(client_socket, 400, "Invalid buckets"); | |
| return; | |
| } | |
| // Check if we have space for more RMON history | |
| if (g_data.rmon_history_count >= MAX_RMON_HISTORY) { | |
| send_error_response(client_socket, 400, "Maximum RMON history entries reached"); | |
| return; | |
| } | |
| // Store RMON history entry | |
| int idx = g_data.rmon_history_count; | |
| g_data.rmon_history[idx].port = port; | |
| g_data.rmon_history[idx].index = index; | |
| g_data.rmon_history[idx].interval = interval; | |
| g_data.rmon_history[idx].buckets = buckets; | |
| strcpy(g_data.rmon_history[idx].owner, owner); | |
| g_data.rmon_history[idx].active = true; | |
| g_data.rmon_history_count++; | |
| send_success_response(client_socket, "{\"message\":\"RMON history configuration applied successfully\"}"); | |
| } | |
| void handle_rmon_history_delete_request(int client_socket, const char *body) | |
| { | |
| char index_str[8]; | |
| int index, i; | |
| // Extract index from JSON | |
| if (extract_json_string(body, "index", index_str, sizeof(index_str)) != 0) { | |
| send_error_response(client_socket, 400, "Index is required"); | |
| return; | |
| } | |
| index = atoi(index_str); | |
| // Find and remove the RMON history entry | |
| for (i = 0; i < g_data.rmon_history_count; i++) { | |
| if (g_data.rmon_history[i].active && g_data.rmon_history[i].index == index) { | |
| g_data.rmon_history[i].active = false; | |
| // Compact the array | |
| int j; | |
| for (j = i; j < g_data.rmon_history_count - 1; j++) { | |
| g_data.rmon_history[j] = g_data.rmon_history[j + 1]; | |
| } | |
| g_data.rmon_history_count--; | |
| send_success_response(client_socket, "{\"message\":\"RMON history configuration deleted successfully\"}"); | |
| return; | |
| } | |
| } | |
| send_error_response(client_socket, 404, "RMON history entry not found"); | |
| } | |
| // RMON Alarm handlers | |
| void handle_rmon_alarm_get_request(int client_socket) | |
| { | |
| char json[2048] = "["; | |
| bool first = true; | |
| int i; | |
| // Iterate through stored RMON alarms | |
| for (i = 0; i < g_data.rmon_alarm_count && i < MAX_RMON_ALARMS; i++) { | |
| if (g_data.rmon_alarms[i].active) { | |
| if (!first) { | |
| strcat(json, ","); | |
| } | |
| char entry[512]; | |
| sprintf(entry, | |
| "{\"sequenceType\":\"%s\",\"sequenceIndex\":\"%d\",\"interval\":\"%d\",\"variable\":\"%s\",\"sampleType\":\"%s\",\"alarmValue\":\"%llu\",\"risingThreshold\":\"%llu\",\"fallingThreshold\":\"%llu\",\"risingEventIndex\":\"%d\",\"fallingEventIndex\":\"%d\",\"owner\":\"%s\"}", | |
| g_data.rmon_alarms[i].sequenceType, | |
| g_data.rmon_alarms[i].sequenceIndex, | |
| g_data.rmon_alarms[i].interval, | |
| g_data.rmon_alarms[i].variable, | |
| g_data.rmon_alarms[i].sampleType, | |
| g_data.rmon_alarms[i].alarmValue, | |
| g_data.rmon_alarms[i].risingThreshold, | |
| g_data.rmon_alarms[i].fallingThreshold, | |
| g_data.rmon_alarms[i].risingEventIndex, | |
| g_data.rmon_alarms[i].fallingEventIndex, | |
| g_data.rmon_alarms[i].owner); | |
| strcat(json, entry); | |
| first = false; | |
| } | |
| } | |
| strcat(json, "]"); | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_rmon_alarm_post_request(int client_socket, const char *body) | |
| { | |
| char sequenceType[16], sequenceIndex_str[8], interval_str[16], variable[MAX_STRING_LEN]; | |
| char sampleType[16], alarmValue_str[32], risingThreshold_str[32], fallingThreshold_str[32]; | |
| char risingEventIndex_str[8], fallingEventIndex_str[8], owner[MAX_STRING_LEN]; | |
| // Extract JSON fields | |
| if (extract_json_string(body, "sequenceType", sequenceType, sizeof(sequenceType)) != 0 || | |
| extract_json_string(body, "sequenceIndex", sequenceIndex_str, sizeof(sequenceIndex_str)) != 0 || | |
| extract_json_string(body, "interval", interval_str, sizeof(interval_str)) != 0 || | |
| extract_json_string(body, "variable", variable, sizeof(variable)) != 0 || | |
| extract_json_string(body, "sampleType", sampleType, sizeof(sampleType)) != 0 || | |
| extract_json_string(body, "alarmValue", alarmValue_str, sizeof(alarmValue_str)) != 0 || | |
| extract_json_string(body, "risingThreshold", risingThreshold_str, sizeof(risingThreshold_str)) != 0 || | |
| extract_json_string(body, "fallingThreshold", fallingThreshold_str, sizeof(fallingThreshold_str)) != 0 || | |
| extract_json_string(body, "risingEventIndex", risingEventIndex_str, sizeof(risingEventIndex_str)) != 0 || | |
| extract_json_string(body, "fallingEventIndex", fallingEventIndex_str, sizeof(fallingEventIndex_str)) != 0 || | |
| extract_json_string(body, "owner", owner, sizeof(owner)) != 0) { | |
| send_error_response(client_socket, 400, "Missing required fields"); | |
| return; | |
| } | |
| // Validate sample type | |
| if (strcmp(sampleType, "absolute") != 0 && strcmp(sampleType, "delta") != 0) { | |
| send_error_response(client_socket, 400, "Invalid sample type. Must be 'absolute' or 'delta'"); | |
| return; | |
| } | |
| int interval = atoi(interval_str); | |
| if (interval <= 0) { | |
| send_error_response(client_socket, 400, "Invalid interval"); | |
| return; | |
| } | |
| // Check if we have space for more RMON alarms | |
| if (g_data.rmon_alarm_count >= MAX_RMON_ALARMS) { | |
| send_error_response(client_socket, 400, "Maximum RMON alarm entries reached"); | |
| return; | |
| } | |
| // Store RMON alarm entry | |
| int idx = g_data.rmon_alarm_count; | |
| strcpy(g_data.rmon_alarms[idx].sequenceType, sequenceType); | |
| g_data.rmon_alarms[idx].sequenceIndex = atoi(sequenceIndex_str); | |
| g_data.rmon_alarms[idx].interval = interval; | |
| strcpy(g_data.rmon_alarms[idx].variable, variable); | |
| strcpy(g_data.rmon_alarms[idx].sampleType, sampleType); | |
| g_data.rmon_alarms[idx].alarmValue = atoll(alarmValue_str); | |
| g_data.rmon_alarms[idx].risingThreshold = atoll(risingThreshold_str); | |
| g_data.rmon_alarms[idx].fallingThreshold = atoll(fallingThreshold_str); | |
| g_data.rmon_alarms[idx].risingEventIndex = atoi(risingEventIndex_str); | |
| g_data.rmon_alarms[idx].fallingEventIndex = atoi(fallingEventIndex_str); | |
| strcpy(g_data.rmon_alarms[idx].owner, owner); | |
| g_data.rmon_alarms[idx].active = true; | |
| g_data.rmon_alarm_count++; | |
| send_success_response(client_socket, "{\"message\":\"RMON alarm configuration applied successfully\"}"); | |
| } | |
| void handle_rmon_alarm_delete_request(int client_socket, const char *body) | |
| { | |
| char sequenceIndex_str[8]; | |
| int sequenceIndex, i; | |
| // Extract sequenceIndex from JSON | |
| if (extract_json_string(body, "sequenceIndex", sequenceIndex_str, sizeof(sequenceIndex_str)) != 0) { | |
| send_error_response(client_socket, 400, "Sequence index is required"); | |
| return; | |
| } | |
| sequenceIndex = atoi(sequenceIndex_str); | |
| // Find and remove the RMON alarm entry | |
| for (i = 0; i < g_data.rmon_alarm_count; i++) { | |
| if (g_data.rmon_alarms[i].active && g_data.rmon_alarms[i].sequenceIndex == sequenceIndex) { | |
| g_data.rmon_alarms[i].active = false; | |
| // Compact the array | |
| int j; | |
| for (j = i; j < g_data.rmon_alarm_count - 1; j++) { | |
| g_data.rmon_alarms[j] = g_data.rmon_alarms[j + 1]; | |
| } | |
| g_data.rmon_alarm_count--; | |
| send_success_response(client_socket, "{\"message\":\"RMON alarm configuration deleted successfully\"}"); | |
| return; | |
| } | |
| } | |
| send_error_response(client_socket, 404, "RMON alarm entry not found"); | |
| } | |
| // RMON Event handlers | |
| void handle_rmon_event_get_request(int client_socket) | |
| { | |
| char json[2048] = "["; | |
| bool first = true; | |
| int i; | |
| // Iterate through stored RMON events | |
| for (i = 0; i < g_data.rmon_event_count && i < MAX_RMON_EVENTS; i++) { | |
| if (g_data.rmon_events[i].active) { | |
| if (!first) { | |
| strcat(json, ","); | |
| } | |
| char entry[512]; | |
| sprintf(entry, | |
| "{\"sequenceType\":\"%s\",\"sequenceIndex\":\"%d\",\"index\":\"%d\",\"description\":\"%s\",\"type\":\"%s\",\"community\":\"%s\",\"lastTimeSent\":\"%s\",\"owner\":\"%s\"}", | |
| g_data.rmon_events[i].sequenceType, | |
| g_data.rmon_events[i].sequenceIndex, | |
| g_data.rmon_events[i].index, | |
| g_data.rmon_events[i].description, | |
| g_data.rmon_events[i].type, | |
| g_data.rmon_events[i].community, | |
| g_data.rmon_events[i].lastTimeSent, | |
| g_data.rmon_events[i].owner); | |
| strcat(json, entry); | |
| first = false; | |
| } | |
| } | |
| strcat(json, "]"); | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_rmon_event_post_request(int client_socket, const char *body) | |
| { | |
| char sequenceType[16], sequenceIndex_str[8], index_str[8], description[256]; | |
| char type[32], community[MAX_STRING_LEN], lastTimeSent[32], owner[MAX_STRING_LEN]; | |
| // Extract JSON fields | |
| if (extract_json_string(body, "sequenceType", sequenceType, sizeof(sequenceType)) != 0 || | |
| extract_json_string(body, "sequenceIndex", sequenceIndex_str, sizeof(sequenceIndex_str)) != 0 || | |
| extract_json_string(body, "index", index_str, sizeof(index_str)) != 0 || | |
| extract_json_string(body, "description", description, sizeof(description)) != 0 || | |
| extract_json_string(body, "type", type, sizeof(type)) != 0 || | |
| extract_json_string(body, "community", community, sizeof(community)) != 0 || | |
| extract_json_string(body, "lastTimeSent", lastTimeSent, sizeof(lastTimeSent)) != 0 || | |
| extract_json_string(body, "owner", owner, sizeof(owner)) != 0) { | |
| send_error_response(client_socket, 400, "Missing required fields"); | |
| return; | |
| } | |
| // Validate event type | |
| if (strcmp(type, "none") != 0 && strcmp(type, "log") != 0 && | |
| strcmp(type, "snmptrap") != 0 && strcmp(type, "log-and-trap") != 0) { | |
| send_error_response(client_socket, 400, "Invalid event type"); | |
| return; | |
| } | |
| // Check if we have space for more RMON events | |
| if (g_data.rmon_event_count >= MAX_RMON_EVENTS) { | |
| send_error_response(client_socket, 400, "Maximum RMON event entries reached"); | |
| return; | |
| } | |
| // Store RMON event entry | |
| int idx = g_data.rmon_event_count; | |
| strcpy(g_data.rmon_events[idx].sequenceType, sequenceType); | |
| g_data.rmon_events[idx].sequenceIndex = atoi(sequenceIndex_str); | |
| g_data.rmon_events[idx].index = atoi(index_str); | |
| strcpy(g_data.rmon_events[idx].description, description); | |
| strcpy(g_data.rmon_events[idx].type, type); | |
| strcpy(g_data.rmon_events[idx].community, community); | |
| strcpy(g_data.rmon_events[idx].lastTimeSent, lastTimeSent); | |
| strcpy(g_data.rmon_events[idx].owner, owner); | |
| g_data.rmon_events[idx].active = true; | |
| g_data.rmon_event_count++; | |
| send_success_response(client_socket, "{\"message\":\"RMON event configuration applied successfully\"}"); | |
| } | |
| void handle_rmon_event_delete_request(int client_socket, const char *body) | |
| { | |
| char sequenceIndex_str[8]; | |
| int sequenceIndex, i; | |
| // Extract sequenceIndex from JSON | |
| if (extract_json_string(body, "sequenceIndex", sequenceIndex_str, sizeof(sequenceIndex_str)) != 0) { | |
| send_error_response(client_socket, 400, "Sequence index is required"); | |
| return; | |
| } | |
| sequenceIndex = atoi(sequenceIndex_str); | |
| // Find and remove the RMON event entry | |
| for (i = 0; i < g_data.rmon_event_count; i++) { | |
| if (g_data.rmon_events[i].active && g_data.rmon_events[i].sequenceIndex == sequenceIndex) { | |
| g_data.rmon_events[i].active = false; | |
| // Compact the array | |
| int j; | |
| for (j = i; j < g_data.rmon_event_count - 1; j++) { | |
| g_data.rmon_events[j] = g_data.rmon_events[j + 1]; | |
| } | |
| g_data.rmon_event_count--; | |
| send_success_response(client_socket, "{\"message\":\"RMON event configuration deleted successfully\"}"); | |
| return; | |
| } | |
| } | |
| send_error_response(client_socket, 404, "RMON event entry not found"); | |
| } | |
| // Helper function to configure IGMP snooping using RTK SDK | |
| int configure_igmp_snooping_rtk(IgmpSnoopingConfig *config) { | |
| uint32 unit = 0; | |
| int ret; | |
| rtk_l2_ipmcMode_t mode; | |
| rtk_enable_t enable; | |
| if (!config || !config->active) { | |
| return -1; | |
| } | |
| // Configure IP multicast mode - use LOOKUP_ON_DIP_AND_FVID for IGMP snooping | |
| mode = LOOKUP_ON_DIP_AND_FVID; | |
| ret = rtk_l2_ipmcMode_set(unit, mode); | |
| if (ret != RT_ERR_OK) { | |
| return -1; | |
| } | |
| // Enable IP multicast address checking | |
| enable = config->globalIgmpSnooping ? ENABLED : DISABLED; | |
| ret = rtk_l2_ipMcastAddrChkEnable_set(unit, enable); | |
| if (ret != RT_ERR_OK) { | |
| return -1; | |
| } | |
| // Configure VLAN-specific settings would require VLAN parsing | |
| // For now, we'll focus on global IGMP snooping control | |
| return 0; | |
| } | |
| // Helper function to add multicast group using RTK SDK | |
| int add_multicast_group_rtk(MulticastGroup *group) { | |
| uint32 unit = 0; | |
| int ret; | |
| rtk_l2_ipMcastAddr_t ipmc_addr; | |
| rtk_vlan_t vid; | |
| rtk_portmask_t portmask; | |
| ipaddr_t dip; | |
| char *token, *ports_copy; | |
| int port_num; | |
| if (!group || !group->active) { | |
| return -1; | |
| } | |
| // Parse VLAN ID from vlanX format | |
| if (sscanf(group->vlanId, "vlan%d", &vid) != 1) { | |
| vid = 1; // default VLAN | |
| } | |
| // Parse multicast IP address | |
| inet_aton(group->multicastAddress, (struct in_addr *)&dip); | |
| // Parse member ports and create portmask | |
| RTK_PORTMASK_RESET(portmask); | |
| ports_copy = strdup(group->memberPorts); | |
| if (ports_copy) { | |
| token = strtok(ports_copy, ","); | |
| while (token) { | |
| port_num = atoi(token); | |
| if (port_num >= 0 && port_num < 28) { | |
| RTK_PORTMASK_PORT_SET(portmask, port_num); | |
| } | |
| token = strtok(NULL, ","); | |
| } | |
| free(ports_copy); | |
| } | |
| // Initialize IP multicast address structure | |
| ret = rtk_l2_ipMcastAddrExt_init(unit, NULL, &ipmc_addr); | |
| if (ret != RT_ERR_OK) { | |
| return -1; | |
| } | |
| // Configure the IP multicast entry | |
| ipmc_addr.rvid = vid; | |
| ipmc_addr.dip = dip; | |
| ipmc_addr.sip = 0; // For (*, G) entries | |
| ipmc_addr.portmask = portmask; | |
| ipmc_addr.fwdIndex = -1; // Auto allocation | |
| // Add the multicast entry to hardware | |
| ret = rtk_l2_ipMcastAddr_add(unit, &ipmc_addr); | |
| if (ret != RT_ERR_OK) { | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| // Helper function to delete multicast group using RTK SDK | |
| int delete_multicast_group_rtk(const char *vlanId, const char *multicastAddress) { | |
| uint32 unit = 0; | |
| int ret; | |
| rtk_vlan_t vid; | |
| ipaddr_t dip; | |
| // Parse VLAN ID from vlanX format | |
| if (sscanf(vlanId, "vlan%d", &vid) != 1) { | |
| vid = 1; // default VLAN | |
| } | |
| // Parse multicast IP address | |
| inet_aton(multicastAddress, (struct in_addr *)&dip); | |
| // Delete the multicast entry from hardware | |
| ret = rtk_l2_ipMcastAddr_del(unit, 0, dip, vid); // SIP=0 for (*, G) entries | |
| if (ret != RT_ERR_OK) { | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| // Initialize L2 and multicast subsystems | |
| int initialize_l2_multicast_system(void) { | |
| uint32 unit = 0; | |
| int ret; | |
| // Initialize L2 subsystem | |
| ret = rtk_l2_init(unit); | |
| if (ret != RT_ERR_OK) { | |
| return -1; | |
| } | |
| // Initialize multicast subsystem | |
| ret = rtk_mcast_init(unit); | |
| if (ret != RT_ERR_OK) { | |
| return -1; | |
| } | |
| // Initialize IP multicast subsystem | |
| ret = rtk_ipmc_init(unit); | |
| if (ret != RT_ERR_OK) { | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| // ============================================================================================================================================================ | |
| // IGMP SNOOPING HANDLERS | |
| // ============================================================================================================================================================ | |
| // IGMP Snooping Configuration handlers | |
| void handle_igmp_snooping_get_request(int client_socket) | |
| { | |
| char json[1024] = ""; | |
| uint32 unit = 0; | |
| rtk_l2_ipmcMode_t mode; | |
| rtk_enable_t igmp_enable; | |
| int ret, i; | |
| // Get current IGMP snooping status from hardware | |
| ret = rtk_l2_ipmcMode_get(unit, &mode); | |
| bool igmp_mode_enabled = (ret == RT_ERR_OK && mode == LOOKUP_ON_DIP_AND_FVID); | |
| ret = rtk_l2_ipMcastAddrChkEnable_get(unit, &igmp_enable); | |
| bool igmp_addr_check = (ret == RT_ERR_OK && igmp_enable == ENABLED); | |
| // Use stored configuration if available, otherwise use hardware status | |
| if (g_data.igmp_config_count > 0) { | |
| for (i = 0; i < g_data.igmp_config_count; i++) { | |
| if (g_data.igmp_configs[i].active) { | |
| sprintf(json, | |
| "{\"globalIgmpSnooping\":\"%s\",\"vlanId\":\"%s\",\"vlanIgmpSnooping\":\"%s\",\"fastLeave\":\"%s\",\"fastLeaveTimeout\":\"%d\",\"queryMembershipTimeout\":\"%d\",\"groupMembershipTimeout\":\"%d\"}", | |
| g_data.igmp_configs[i].globalIgmpSnooping ? "enable" : "disable", | |
| g_data.igmp_configs[i].vlanId, | |
| g_data.igmp_configs[i].vlanIgmpSnooping ? "enable" : "disable", | |
| g_data.igmp_configs[i].fastLeave ? "enable" : "disable", | |
| g_data.igmp_configs[i].fastLeaveTimeout, | |
| g_data.igmp_configs[i].queryMembershipTimeout, | |
| g_data.igmp_configs[i].groupMembershipTimeout); | |
| break; | |
| } | |
| } | |
| } else { | |
| // Return hardware status | |
| sprintf(json, | |
| "{\"globalIgmpSnooping\":\"%s\",\"vlanId\":\"vlan1\",\"vlanIgmpSnooping\":\"%s\",\"fastLeave\":\"disable\",\"fastLeaveTimeout\":\"300000\",\"queryMembershipTimeout\":\"300000\",\"groupMembershipTimeout\":\"400000\"}", | |
| (igmp_mode_enabled && igmp_addr_check) ? "enable" : "disable", | |
| (igmp_mode_enabled && igmp_addr_check) ? "enable" : "disable"); | |
| } | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_igmp_snooping_post_request(int client_socket, const char *body) | |
| { | |
| char globalIgmpSnooping[16], vlanId[16], vlanIgmpSnooping[16], fastLeave[16]; | |
| char fastLeaveTimeout_str[16], queryMembershipTimeout_str[16], groupMembershipTimeout_str[16]; | |
| int fastLeaveTimeout, queryMembershipTimeout, groupMembershipTimeout; | |
| // Extract JSON fields | |
| if (extract_json_string(body, "globalIgmpSnooping", globalIgmpSnooping, sizeof(globalIgmpSnooping)) != 0 || | |
| extract_json_string(body, "vlanId", vlanId, sizeof(vlanId)) != 0 || | |
| extract_json_string(body, "vlanIgmpSnooping", vlanIgmpSnooping, sizeof(vlanIgmpSnooping)) != 0 || | |
| extract_json_string(body, "fastLeave", fastLeave, sizeof(fastLeave)) != 0 || | |
| extract_json_string(body, "fastLeaveTimeout", fastLeaveTimeout_str, sizeof(fastLeaveTimeout_str)) != 0 || | |
| extract_json_string(body, "queryMembershipTimeout", queryMembershipTimeout_str, sizeof(queryMembershipTimeout_str)) != 0 || | |
| extract_json_string(body, "groupMembershipTimeout", groupMembershipTimeout_str, sizeof(groupMembershipTimeout_str)) != 0) { | |
| send_error_response(client_socket, 400, "Missing required fields"); | |
| return; | |
| } | |
| // Validate enable/disable values | |
| if (strcmp(globalIgmpSnooping, "enable") != 0 && strcmp(globalIgmpSnooping, "disable") != 0) { | |
| send_error_response(client_socket, 400, "Invalid globalIgmpSnooping value. Must be 'enable' or 'disable'"); | |
| return; | |
| } | |
| if (strcmp(vlanIgmpSnooping, "enable") != 0 && strcmp(vlanIgmpSnooping, "disable") != 0) { | |
| send_error_response(client_socket, 400, "Invalid vlanIgmpSnooping value. Must be 'enable' or 'disable'"); | |
| return; | |
| } | |
| if (strcmp(fastLeave, "enable") != 0 && strcmp(fastLeave, "disable") != 0) { | |
| send_error_response(client_socket, 400, "Invalid fastLeave value. Must be 'enable' or 'disable'"); | |
| return; | |
| } | |
| // Validate timeout values | |
| fastLeaveTimeout = atoi(fastLeaveTimeout_str); | |
| queryMembershipTimeout = atoi(queryMembershipTimeout_str); | |
| groupMembershipTimeout = atoi(groupMembershipTimeout_str); | |
| if (fastLeaveTimeout < 100 || fastLeaveTimeout > 1000000) { | |
| send_error_response(client_socket, 400, "fastLeaveTimeout must be between 100 and 1000000 ms"); | |
| return; | |
| } | |
| if (queryMembershipTimeout < 100 || queryMembershipTimeout > 1000000) { | |
| send_error_response(client_socket, 400, "queryMembershipTimeout must be between 100 and 1000000 ms"); | |
| return; | |
| } | |
| if (groupMembershipTimeout < 100 || groupMembershipTimeout > 1000000) { | |
| send_error_response(client_socket, 400, "groupMembershipTimeout must be between 100 and 1000000 ms"); | |
| return; | |
| } | |
| // Check if we have space for more IGMP configurations or update existing | |
| int idx = -1; | |
| // Look for existing configuration first | |
| for (i = 0; i < g_data.igmp_config_count; i++) { | |
| if (g_data.igmp_configs[i].active && strcmp(g_data.igmp_configs[i].vlanId, vlanId) == 0) { | |
| idx = i; | |
| break; | |
| } | |
| } | |
| // If not found, create new entry | |
| if (idx == -1) { | |
| if (g_data.igmp_config_count >= MAX_IGMP_CONFIGS) { | |
| send_error_response(client_socket, 400, "Maximum IGMP configuration entries reached"); | |
| return; | |
| } | |
| idx = g_data.igmp_config_count; | |
| g_data.igmp_config_count++; | |
| } | |
| // Store IGMP configuration | |
| g_data.igmp_configs[idx].globalIgmpSnooping = (strcmp(globalIgmpSnooping, "enable") == 0); | |
| strcpy(g_data.igmp_configs[idx].vlanId, vlanId); | |
| g_data.igmp_configs[idx].vlanIgmpSnooping = (strcmp(vlanIgmpSnooping, "enable") == 0); | |
| g_data.igmp_configs[idx].fastLeave = (strcmp(fastLeave, "enable") == 0); | |
| g_data.igmp_configs[idx].fastLeaveTimeout = fastLeaveTimeout; | |
| g_data.igmp_configs[idx].queryMembershipTimeout = queryMembershipTimeout; | |
| g_data.igmp_configs[idx].groupMembershipTimeout = groupMembershipTimeout; | |
| g_data.igmp_configs[idx].active = true; | |
| // Apply configuration to hardware using RTK SDK | |
| int rtk_ret = configure_igmp_snooping_rtk(&g_data.igmp_configs[idx]); | |
| if (rtk_ret != 0) { | |
| send_error_response(client_socket, 500, "Failed to apply IGMP snooping configuration to hardware"); | |
| return; | |
| } | |
| send_success_response(client_socket, "{\"message\":\"IGMP snooping configuration applied successfully\"}"); | |
| } | |
| // Multicast Group Information handlers | |
| void handle_multicast_group_get_request(int client_socket) | |
| { | |
| char json[4096] = "{\"groups\":["; | |
| bool first = true; | |
| int i; | |
| // Iterate through stored multicast groups | |
| for (i = 0; i < g_data.multicast_group_count && i < MAX_MULTICAST_GROUPS; i++) { | |
| if (g_data.multicast_groups[i].active) { | |
| if (!first) { | |
| strcat(json, ","); | |
| } | |
| char entry[256]; | |
| sprintf(entry, | |
| "{\"vlanId\":\"%s\",\"multicastAddress\":\"%s\",\"memberPorts\":\"%s\"}", | |
| g_data.multicast_groups[i].vlanId, | |
| g_data.multicast_groups[i].multicastAddress, | |
| g_data.multicast_groups[i].memberPorts); | |
| strcat(json, entry); | |
| first = false; | |
| } | |
| } | |
| strcat(json, "]}"); | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_multicast_group_post_request(int client_socket, const char *body) | |
| { | |
| char vlanId[16], multicastAddress[16], memberPorts[MAX_STRING_LEN]; | |
| // Extract JSON fields | |
| if (extract_json_string(body, "vlanId", vlanId, sizeof(vlanId)) != 0 || | |
| extract_json_string(body, "multicastAddress", multicastAddress, sizeof(multicastAddress)) != 0 || | |
| extract_json_string(body, "memberPorts", memberPorts, sizeof(memberPorts)) != 0) { | |
| send_error_response(client_socket, 400, "Missing required fields"); | |
| return; | |
| } | |
| // Validate multicast address format (basic validation) | |
| if (!is_valid_ip(multicastAddress)) { | |
| send_error_response(client_socket, 400, "Invalid multicast IP address format"); | |
| return; | |
| } | |
| // Check if we have space for more multicast groups | |
| if (g_data.multicast_group_count >= MAX_MULTICAST_GROUPS) { | |
| send_error_response(client_socket, 400, "Maximum multicast group entries reached"); | |
| return; | |
| } | |
| // Store multicast group | |
| int idx = g_data.multicast_group_count; | |
| strcpy(g_data.multicast_groups[idx].vlanId, vlanId); | |
| strcpy(g_data.multicast_groups[idx].multicastAddress, multicastAddress); | |
| strcpy(g_data.multicast_groups[idx].memberPorts, memberPorts); | |
| g_data.multicast_groups[idx].active = true; | |
| // Add multicast group to hardware using RTK SDK | |
| int rtk_ret = add_multicast_group_rtk(&g_data.multicast_groups[idx]); | |
| if (rtk_ret != 0) { | |
| send_error_response(client_socket, 500, "Failed to add multicast group to hardware"); | |
| return; | |
| } | |
| g_data.multicast_group_count++; | |
| send_success_response(client_socket, "{\"message\":\"Multicast group added successfully\"}"); | |
| } | |
| void handle_multicast_group_delete_request(int client_socket, const char *body) | |
| { | |
| char vlanId[16], multicastAddress[16]; | |
| int i; | |
| // Extract fields from JSON | |
| if (extract_json_string(body, "vlanId", vlanId, sizeof(vlanId)) != 0 || | |
| extract_json_string(body, "multicastAddress", multicastAddress, sizeof(multicastAddress)) != 0) { | |
| send_error_response(client_socket, 400, "Missing required fields"); | |
| return; | |
| } | |
| // Find and remove the multicast group | |
| for (i = 0; i < g_data.multicast_group_count; i++) { | |
| if (g_data.multicast_groups[i].active && | |
| strcmp(g_data.multicast_groups[i].vlanId, vlanId) == 0 && | |
| strcmp(g_data.multicast_groups[i].multicastAddress, multicastAddress) == 0) { | |
| // Delete from hardware first | |
| int rtk_ret = delete_multicast_group_rtk(vlanId, multicastAddress); | |
| if (rtk_ret != 0) { | |
| send_error_response(client_socket, 500, "Failed to delete multicast group from hardware"); | |
| return; | |
| } | |
| g_data.multicast_groups[i].active = false; | |
| // Compact the array | |
| int j; | |
| for (j = i; j < g_data.multicast_group_count - 1; j++) { | |
| g_data.multicast_groups[j] = g_data.multicast_groups[j + 1]; | |
| } | |
| g_data.multicast_group_count--; | |
| send_success_response(client_socket, "{\"message\":\"Multicast group deleted successfully\"}"); | |
| return; | |
| } | |
| } | |
| send_error_response(client_socket, 404, "Multicast group not found"); | |
| } | |
| // ============================================================================================================================================================ | |
| // MAC TABLE HANDLERS | |
| // ============================================================================================================================================================ | |
| // MAC table HTTP handlers | |
| void handle_mac_bind_table_get_request(int client_socket) | |
| { | |
| char json[4096] = "["; // Start JSON array | |
| bool first = true; | |
| int i; | |
| // Iterate through stored MAC bindings | |
| for (i = 0; i < g_data.mac_binding_count && i < MAX_MAC_BINDINGS; i++) { | |
| if (g_data.mac_bindings[i].active) { | |
| if (!first) { | |
| strcat(json, ","); | |
| } | |
| char entry[256]; | |
| sprintf(entry, | |
| "{\"port\":%d,\"macAddress\":\"%s\",\"vlanId\":%d}", | |
| g_data.mac_bindings[i].port, | |
| g_data.mac_bindings[i].macAddress, | |
| g_data.mac_bindings[i].vlanId | |
| ); | |
| strcat(json, entry); | |
| first = false; | |
| } | |
| } | |
| strcat(json, "]"); // End JSON array | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_mac_bind_table_post_request(int client_socket, const char *body) | |
| { | |
| char port_str[8], macAddress[32], vlanId_str[8]; | |
| int port, vlanId; | |
| // Extract JSON fields | |
| if (extract_json_string(body, "port", port_str, sizeof(port_str)) != 0 || | |
| extract_json_string(body, "macAddress", macAddress, sizeof(macAddress)) != 0 || | |
| extract_json_string(body, "vlanId", vlanId_str, sizeof(vlanId_str)) != 0) { | |
| send_error_response(client_socket, 400, "All fields are required."); | |
| return; | |
| } | |
| port = atoi(port_str); | |
| vlanId = atoi(vlanId_str); | |
| // Validate port | |
| if (!is_valid_port(port, 1, 28)) { | |
| send_error_response(client_socket, 400, "Invalid port number"); | |
| return; | |
| } | |
| // Validate VLAN ID | |
| if (vlanId < 1 || vlanId > 4094) { | |
| send_error_response(client_socket, 400, "Invalid VLAN ID. Must be between 1 and 4094"); | |
| return; | |
| } | |
| // Validate MAC address format (support both formats) | |
| if (!is_valid_mac(macAddress)) { | |
| // Try dot format validation | |
| unsigned int mac[3]; | |
| if (sscanf(macAddress, "%04x.%04x.%04x", &mac[0], &mac[1], &mac[2]) != 3) { | |
| send_error_response(client_socket, 400, "Invalid MAC Address format. Use: HHHH.HHHH.HHHH or HH:HH:HH:HH:HH:HH"); | |
| return; | |
| } | |
| } | |
| // Check if we have space for more MAC bindings | |
| if (g_data.mac_binding_count >= MAX_MAC_BINDINGS) { | |
| send_error_response(client_socket, 400, "Maximum MAC bindings reached"); | |
| return; | |
| } | |
| // Store MAC binding | |
| int idx = g_data.mac_binding_count; | |
| g_data.mac_bindings[idx].port = port; | |
| strcpy(g_data.mac_bindings[idx].macAddress, macAddress); | |
| g_data.mac_bindings[idx].vlanId = vlanId; | |
| g_data.mac_bindings[idx].active = true; | |
| // Apply to hardware | |
| if (add_mac_binding_to_hardware(&g_data.mac_bindings[idx]) == 0) { | |
| g_data.mac_binding_count++; | |
| char json[128]; | |
| sprintf(json, "{\"message\":\"MAC binding added successfully\",\"data\":{\"port\":%d,\"macAddress\":\"%s\",\"vlanId\":%d}}", | |
| port, macAddress, vlanId); | |
| send_success_response(client_socket, json); | |
| } else { | |
| send_error_response(client_socket, 500, "Failed to apply MAC binding to hardware"); | |
| } | |
| } | |
| void handle_mac_bind_table_delete_request(int client_socket, const char *body) | |
| { | |
| char macAddress[32]; | |
| int i; | |
| // Extract MAC address from JSON | |
| if (extract_json_string(body, "macAddress", macAddress, sizeof(macAddress)) != 0) { | |
| send_error_response(client_socket, 400, "macAddress is required"); | |
| return; | |
| } | |
| // Find and remove the MAC binding | |
| for (i = 0; i < g_data.mac_binding_count; i++) { | |
| if (g_data.mac_bindings[i].active && | |
| strcmp(g_data.mac_bindings[i].macAddress, macAddress) == 0) { | |
| // Remove from hardware | |
| remove_mac_binding_from_hardware(macAddress, g_data.mac_bindings[i].vlanId); | |
| // Mark as inactive and compact array | |
| g_data.mac_bindings[i].active = false; | |
| // Compact the array | |
| int j; | |
| for (j = i; j < g_data.mac_binding_count - 1; j++) { | |
| g_data.mac_bindings[j] = g_data.mac_bindings[j + 1]; | |
| } | |
| g_data.mac_binding_count--; | |
| send_success_response(client_socket, "{\"message\":\"MAC binding deleted successfully\"}"); | |
| return; | |
| } | |
| } | |
| send_error_response(client_socket, 404, "MAC binding not found"); | |
| } | |
| void handle_mac_auto_bind_get_request(int client_socket) | |
| { | |
| char json[4096] = "["; // Start JSON array | |
| bool first = true; | |
| int i; | |
| // Iterate through stored MAC auto bindings | |
| for (i = 0; i < g_data.mac_auto_binding_count && i < MAX_MAC_AUTO_BINDINGS; i++) { | |
| if (g_data.mac_auto_bindings[i].active) { | |
| if (!first) { | |
| strcat(json, ","); | |
| } | |
| char entry[256]; | |
| sprintf(entry, | |
| "{\"port\":%d,\"macAddress\":\"%s\",\"vlanId\":%d,\"autoLearned\":%s}", | |
| g_data.mac_auto_bindings[i].port, | |
| g_data.mac_auto_bindings[i].macAddress, | |
| g_data.mac_auto_bindings[i].vlanId, | |
| g_data.mac_auto_bindings[i].autoLearned ? "true" : "false" | |
| ); | |
| strcat(json, entry); | |
| first = false; | |
| } | |
| } | |
| strcat(json, "]"); // End JSON array | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_mac_auto_filter_get_request(int client_socket) | |
| { | |
| // For now, return the same as auto bind table | |
| handle_mac_auto_bind_get_request(client_socket); | |
| } | |
| void handle_mac_filter_get_request(int client_socket) | |
| { | |
| // For now, return the same as auto bind table | |
| handle_mac_auto_bind_get_request(client_socket); | |
| } | |
| // ============================================================================================================================================================ | |
| // INITIALIZATION FUNCTIONS | |
| // ============================================================================================================================================================ | |
| void initialize_global_data(void) { | |
| pthread_mutex_lock(&global_data_mutex); | |
| // Initialize system config | |
| strcpy(g_data.system.systemName, ""); | |
| strcpy(g_data.system.systemLocation, ""); | |
| strcpy(g_data.system.systemContact, ""); | |
| strcpy(g_data.system.description, "Managed Switch 7.4.8"); | |
| strcpy(g_data.system.objectId, "1.3.6.1.4.1.12284.1"); | |
| strcpy(g_data.system.version, "Managed Switch 7.4.8"); | |
| strcpy(g_data.system.serialNumber, "20200600001"); | |
| strcpy(g_data.system.macAddress, "00:28:24:13:D0:9D"); | |
| strcpy(g_data.system.ipAddress, "192.168.0.1"); | |
| g_data.system.interfaces = 10; | |
| // Initialize port configurations | |
| for ( i = 0; i < MAX_PORTS; i++) { | |
| g_data.ports[i].port_id = i; | |
| g_data.ports[i].admin_enabled = true; | |
| strcpy(g_data.ports[i].status, "Down"); | |
| strcpy(g_data.ports[i].speed, "Auto"); | |
| strcpy(g_data.ports[i].duplex, "Auto"); | |
| g_data.ports[i].flow_control_enabled = false; | |
| g_data.ports[i].ingress_rate_limit = 1000; | |
| g_data.ports[i].egress_rate_limit = 1000; | |
| g_data.ports[i].rate_limit_enabled = false; | |
| g_data.ports[i].max_mac_addresses = 8192; | |
| g_data.ports[i].protected_port = false; | |
| g_data.ports[i].broadcast_suppression = false; | |
| g_data.ports[i].broadcast_rate = 64; | |
| g_data.ports[i].multicast_suppression = false; | |
| g_data.ports[i].multicast_rate = 64; | |
| g_data.ports[i].dlf_suppression = false; | |
| g_data.ports[i].dlf_rate = 64; | |
| } | |
| // Initialize default users | |
| strcpy(g_data.users[0].username, "admin"); | |
| strcpy(g_data.users[0].password, "admin123"); | |
| g_data.users[0].level = 15; | |
| g_data.users[0].timeout = 300; | |
| g_data.users[0].active = true; | |
| strcpy(g_data.users[1].username, "guest"); | |
| strcpy(g_data.users[1].password, "guest123"); | |
| g_data.users[1].level = 1; | |
| g_data.users[1].timeout = 60; | |
| g_data.users[1].active = true; | |
| g_data.user_count = 2; | |
| // Initialize default SNMP communities | |
| strcpy(g_data.communities[0].community, "public"); | |
| strcpy(g_data.communities[0].access, "read-only"); | |
| g_data.communities[0].active = true; | |
| strcpy(g_data.communities[1].community, "private"); | |
| strcpy(g_data.communities[1].access, "read-write"); | |
| g_data.communities[1].active = true; | |
| g_data.community_count = 2; | |
| // Initialize default trap targets | |
| strcpy(g_data.trap_targets[0].ip, "192.168.1.100"); | |
| strcpy(g_data.trap_targets[0].community, "public"); | |
| strcpy(g_data.trap_targets[0].version, "v2c"); | |
| g_data.trap_targets[0].active = true; | |
| strcpy(g_data.trap_targets[1].ip, "192.168.1.101"); | |
| strcpy(g_data.trap_targets[1].community, "private"); | |
| strcpy(g_data.trap_targets[1].version, "v3"); | |
| g_data.trap_targets[1].active = true; | |
| g_data.trap_target_count = 2; | |
| // Initialize service access | |
| g_data.service_access.http_enabled = true; | |
| g_data.service_access.https_enabled = false; | |
| g_data.service_access.snmp_enabled = true; | |
| g_data.service_access.telnet_enabled = true; | |
| g_data.service_access.ssh_enabled = true; | |
| // Initialize SNTP | |
| g_data.sntp.enabled = true; | |
| strcpy(g_data.sntp.server_ip, "192.168.1.10"); | |
| g_data.sntp.interval = 1800; | |
| // Initialize serial config | |
| strcpy(g_data.serial.baud_rate, "38400"); | |
| strcpy(g_data.serial.char_size, "8"); | |
| strcpy(g_data.serial.parity, "None"); | |
| strcpy(g_data.serial.stop_bits, "1"); | |
| strcpy(g_data.serial.flow_control, "None"); | |
| // Initialize jumbo frame size | |
| g_data.jumbo_frame_size = 6000; | |
| // Initialize QoS configs | |
| for (i = 0; i < MAX_PORTS; i++) { | |
| g_data.qos_configs[i].port = i + 1; | |
| strcpy(g_data.qos_configs[i].qos_type, "NO QOS"); | |
| g_data.qos_configs[i].priority = 0; | |
| strcpy(g_data.qos_configs[i].schedule_mode, "WRR"); | |
| for (j = 0; j < 8; j++) { | |
| g_data.qos_configs[i].weights[j] = 1; | |
| } | |
| } | |
| // Initialize default VLANs | |
| for (i = 0; i < 9; i++) { | |
| int vid = i + 2; | |
| g_data.vlans[i].vid = vid; | |
| snprintf(g_data.vlans[i].vlan_name, sizeof(g_data.vlans[i].vlan_name), "vlan%d", vid); | |
| g_data.vlans[i].active = true; | |
| strcpy(g_data.vlans[i].port_members, "[u]ge1/1 [u]ge1/2"); | |
| snprintf(g_data.vlans[i].ip_address, sizeof(g_data.vlans[i].ip_address), "192.168.%d.1/24", vid); | |
| g_data.vlans[i].dhcp_client = 0; | |
| } | |
| g_data.vlan_count = 9; | |
| g_data.acl_entry_count = 0; | |
| g_data.mac_binding_count = 0; | |
| g_data.mac_auto_binding_count = 0; | |
| g_data.rmon_statistics_count = 0; | |
| g_data.rmon_history_count = 0; | |
| g_data.rmon_alarm_count = 0; | |
| g_data.rmon_event_count = 0; | |
| g_data.igmp_config_count = 0; | |
| g_data.multicast_group_count = 0; | |
| // Initialize ACL subsystem | |
| initialize_acl_system(); | |
| // Initialize Statistics subsystem | |
| initialize_stat_system(); | |
| // Initialize L2 and multicast subsystems | |
| initialize_l2_multicast_system(); | |
| pthread_mutex_unlock(&global_data_mutex); | |
| } | |
| // ============================================================================================================================================================ | |
| // PAGE Handlers | |
| // ============================================================================================================================================================== | |
| void handle_health_check_request(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| strcat(response, "{\"status\": \"ok\"}"); | |
| send_response(client_socket, response); | |
| } | |
| /* | |
| * 3.4 System Statistics Structure | |
| */ | |
| typedef struct | |
| { | |
| float cpu_usage; // CPU usage percentage | |
| float memory_usage; // Memory usage percentage | |
| long total_memory; // Total RAM in KB | |
| long free_memory; // Free RAM in KB | |
| long total_swap; // Total swap space in KB | |
| long free_swap; // Free swap space in KB | |
| long total_disk; // Total disk space in KB | |
| long free_disk; // Free disk space in KB | |
| float uptime; // System uptime in hours | |
| long uptime_seconds; // Uptime in seconds | |
| long total_memory_mb; // Total RAM in MB | |
| long free_memory_mb; // Free RAM in MB | |
| unsigned int processes; // Number of processes | |
| float load_1min; // Load average (1 min) | |
| float load_5min; // Load average (5 min) | |
| float load_15min; // Load average (15 min) | |
| } SystemStats; | |
| // Function to retrieve system stats | |
| SystemStats get_system_stats(void) | |
| { | |
| SystemStats stats = {0.0, 0.0, 0, 0, 0, 0, 0, 0, 0.0, 0, 0, 0, 0, 0.0, 0.0, 0.0}; | |
| struct sysinfo si; | |
| if (sysinfo(&si) == 0) | |
| { | |
| stats.total_memory = si.totalram / 1024; // Total RAM in KB | |
| stats.free_memory = si.freeram / 1024; // Free RAM in KB | |
| stats.total_swap = si.totalswap / 1024; // Total swap space in KB | |
| stats.free_swap = si.freeswap / 1024; // Free swap space in KB | |
| stats.uptime_seconds = si.uptime; // Uptime in seconds | |
| stats.uptime = si.uptime / 3600.0; // Uptime in hours | |
| stats.processes = si.procs; // Number of processes | |
| stats.load_1min = si.loads[0] / 65536.0; // Load average for 1 min | |
| stats.load_5min = si.loads[1] / 65536.0; // Load average for 5 min | |
| stats.load_15min = si.loads[2] / 65536.0; // Load average for 15 min | |
| stats.total_memory_mb = stats.total_memory / 1024; // Total RAM in MB | |
| stats.free_memory_mb = stats.free_memory / 1024; // Free RAM in MB | |
| stats.memory_usage = ((float)(si.totalram - si.freeram) / si.totalram) * 100.0; | |
| } | |
| FILE *fp = fopen("/proc/stat", "r"); | |
| if (fp != NULL) | |
| { | |
| char buffer[256]; | |
| fgets(buffer, sizeof(buffer), fp); | |
| fclose(fp); | |
| long user, nice, system, idle, iowait, irq, softirq, steal, total; | |
| sscanf(buffer, "cpu %ld %ld %ld %ld %ld %ld %ld %ld", &user, &nice, &system, &idle, &iowait, &irq, &softirq, &steal); | |
| total = user + nice + system + idle + iowait + irq + softirq + steal; | |
| long idle_time = idle + iowait; | |
| stats.cpu_usage = 100.0 * (1.0 - (float)idle_time / total); | |
| } | |
| struct statvfs fs_stats; | |
| if (statvfs("/", &fs_stats) == 0) | |
| { | |
| stats.total_disk = (fs_stats.f_blocks * fs_stats.f_frsize) / 1024; // Total disk space in KB | |
| stats.free_disk = (fs_stats.f_bfree * fs_stats.f_frsize) / 1024; // Free disk space in KB | |
| } | |
| return stats; | |
| } | |
| // Function to execute system command and get the IPv4 address | |
| char *get_ipv4_address(const char *interface) | |
| { | |
| static char ip_address[16]; // Static buffer to hold the result | |
| char command[128]; | |
| snprintf(command, sizeof(command), "ifconfig %s | grep 'inet ' | cut -d: -f2 | awk '{printf $1}'", interface); | |
| FILE *fp = popen(command, "r"); | |
| if (fp == NULL) | |
| { | |
| perror("popen failed"); | |
| return NULL; | |
| } | |
| if (fgets(ip_address, sizeof(ip_address), fp) == NULL) | |
| { | |
| fclose(fp); | |
| return NULL; | |
| } | |
| ip_address[strcspn(ip_address, "\n")] = 0; | |
| fclose(fp); | |
| return ip_address; | |
| } | |
| // Function to execute system command and get the MAC address | |
| char *get_mac_address(const char *interface) | |
| { | |
| static char mac_address[18]; // MAC address in "XX:XX:XX:XX:XX:XX" format | |
| char command[128]; | |
| snprintf(command, sizeof(command), | |
| "ip link show %s | awk '/link\\/ether/ {print $2}'", interface); | |
| FILE *fp = popen(command, "r"); | |
| if (fp == NULL) | |
| { | |
| perror("popen failed"); | |
| return NULL; | |
| } | |
| if (fgets(mac_address, sizeof(mac_address), fp) == NULL) | |
| { | |
| fclose(fp); | |
| return NULL; | |
| } | |
| // Remove trailing newline if present | |
| mac_address[strcspn(mac_address, "\n")] = '\0'; | |
| fclose(fp); | |
| return mac_address; | |
| } | |
| void handle_port_status_request(int client_socket) | |
| { | |
| uint32 unit = 0; | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char port_data[512] = ""; | |
| uint32_t rtk_port; | |
| // Get status for ports 8-15 (mapped to ge1/1-8) | |
| for (rtk_port = 8; rtk_port <= 15; rtk_port++) | |
| { | |
| rtk_port_linkStatus_t linkStatus = PORT_LINKDOWN; | |
| rtk_enable_t adminStatus = DISABLED; | |
| char entry[64]; | |
| int web_port = rtk_port - 7; // Map RTK port 8->1, 9->2, etc. | |
| // Get actual link status and admin status using RTK APIs | |
| int ret1 = rtk_port_link_get(unit, rtk_port, &linkStatus); | |
| int ret2 = rtk_port_adminEnable_get(unit, rtk_port, &adminStatus); | |
| if (ret1 == RT_ERR_OK && ret2 == RT_ERR_OK) | |
| { | |
| const char *status = "down"; | |
| if (adminStatus == ENABLED && linkStatus == PORT_LINKUP) { | |
| status = "up"; | |
| } else if (adminStatus == DISABLED) { | |
| status = "disabled"; | |
| } | |
| snprintf(entry, sizeof(entry), | |
| "{\"port\": \"ge1/%d\", \"status\": \"%s\"},", | |
| web_port, status); | |
| } | |
| else | |
| { | |
| snprintf(entry, sizeof(entry), | |
| "{\"port\": \"ge1/%d\", \"status\": \"error\"},", | |
| web_port); | |
| } | |
| strncat(port_data, entry, sizeof(port_data) - strlen(port_data) - 1); | |
| } | |
| // Remove trailing comma if present | |
| int len = strlen(port_data); | |
| if (len > 0 && port_data[len - 1] == ',') | |
| port_data[len - 1] = '\0'; | |
| char json[600]; | |
| snprintf(json, sizeof(json), "[%s]", port_data); | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_industrial_status_request(int client_socket) | |
| { | |
| SystemStats stats = get_system_stats(); | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char port_status_data[1024] = ""; | |
| int rtk_port; | |
| // Get port status for ports 8-15 (mapped to ge1/1-8) | |
| for (rtk_port = 8; rtk_port <= 15; rtk_port++) | |
| { | |
| rtk_port_linkStatus_t linkStatus = PORT_LINKDOWN; | |
| rtk_port_speed_t speed = PORT_SPEED_1000M; | |
| rtk_port_duplex_t duplex = PORT_FULL_DUPLEX; | |
| rtk_enable_t adminStatus = DISABLED; | |
| const char *speed_str = "N/A"; | |
| const char *activity = "Idle"; | |
| // Get actual port status using RTK APIs | |
| int ret1 = rtk_port_link_get(0, rtk_port, &linkStatus); | |
| int ret3 = rtk_port_adminEnable_get(0, rtk_port, &adminStatus); | |
| // Check if auto-negotiation is enabled | |
| rtk_enable_t autonego_enable = ENABLED; | |
| rtk_enable_t flow_ctrl = DISABLED; | |
| int ret_auto = rtk_port_phyAutoNegoEnable_get(0, rtk_port, &autonego_enable); | |
| if (ret1 == RT_ERR_OK && ret3 == RT_ERR_OK) | |
| { | |
| // Map speed enum to string | |
| if (ret_auto == RT_ERR_OK && autonego_enable == DISABLED) { | |
| // Port is in force mode, get force mode settings | |
| int ret2 = rtk_port_phyForceModeAbility_get(0, rtk_port, &speed, &duplex, &flow_ctrl); | |
| if (ret2 == RT_ERR_OK) { | |
| switch(speed) { | |
| case PORT_SPEED_10M: speed_str = "10M"; break; | |
| case PORT_SPEED_100M: speed_str = "100M"; break; | |
| case PORT_SPEED_1000M: speed_str = "1G"; break; | |
| case PORT_SPEED_2_5G: speed_str = "2.5G"; break; | |
| case PORT_SPEED_5G: speed_str = "5G"; break; | |
| case PORT_SPEED_10G: speed_str = "10G"; break; | |
| default: speed_str = "unknown"; break; | |
| } | |
| } | |
| } else { | |
| // Auto-negotiation enabled, show as auto | |
| speed_str = "Auto"; | |
| } | |
| // Determine overall activity status | |
| if (adminStatus == ENABLED && linkStatus == PORT_LINKUP) { | |
| activity = "Active"; | |
| } else { | |
| activity = "Idle"; | |
| } | |
| snprintf(port_status_data + strlen(port_status_data), sizeof(port_status_data) - strlen(port_status_data), | |
| "{\"linkStatus\": \"%s\", \"speed\": \"%s\", \"activity\": \"%s\"},", | |
| (linkStatus == PORT_LINKUP) ? "Up" : "Down", speed_str, activity); | |
| } | |
| else | |
| { | |
| snprintf(port_status_data + strlen(port_status_data), sizeof(port_status_data) - strlen(port_status_data), | |
| "{\"linkStatus\": \"Error\", \"speed\": \"N/A\", \"activity\": \"Idle\"},"); | |
| } | |
| } | |
| // Remove trailing comma if present | |
| int len = strlen(port_status_data); | |
| if (len > 0 && port_status_data[len - 1] == ',') | |
| port_status_data[len - 1] = '\0'; | |
| char json[2048]; | |
| snprintf(json, sizeof(json), | |
| "{" | |
| "\"status\": \"Online\"," // TODO You may want to make this dynamic | |
| "\"uptime\": %.2f," | |
| "\"cpuUsage\": %.2f," | |
| "\"memoryUsage\": %.2f," | |
| "\"temperature\": 45," // TODO NEED HW Support | |
| "\"powerStatus\": \"Normal\"," // TODO Still hardcoded | |
| "\"ports\": [%s]" | |
| "}", | |
| stats.uptime, | |
| stats.cpu_usage, | |
| stats.memory_usage, | |
| port_status_data); | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| // Function to execute the reboot command | |
| bool execute_reboot(void) | |
| { | |
| reboot(RB_AUTOBOOT); | |
| return true; | |
| } | |
| /* | |
| * 3.3 Network Testing Structures | |
| */ | |
| typedef struct | |
| { | |
| int packets_sent; | |
| int packets_received; | |
| float packet_loss; | |
| float rtt_min; | |
| float rtt_avg; | |
| float rtt_max; | |
| float rtt_mdev; | |
| } PingResult; | |
| PingResult ping_test(const char *ip, int packet_count) | |
| { | |
| PingResult result = {0, 0, 0.0, 0.0, 0.0, 0.0, 0.0}; // Default result | |
| char command[256]; | |
| FILE *ping_output; | |
| char buffer[1024]; | |
| char *statistics_line = NULL; | |
| char *rtt_line = NULL; | |
| snprintf(command, sizeof(command), "ping -c %d %s", packet_count, ip); // For Unix/Linux systems | |
| printf("Running ping command: %s\n", command); | |
| ping_output = popen(command, "r"); | |
| if (ping_output == NULL) | |
| { | |
| perror("popen failed"); | |
| return result; | |
| } | |
| while (fgets(buffer, sizeof(buffer), ping_output)) | |
| { | |
| printf("Ping Output: %s", buffer); | |
| if (strstr(buffer, "ping statistics")) | |
| { | |
| statistics_line = strdup(buffer); // Save the statistics line | |
| } | |
| if (strstr(buffer, "rtt min/avg/max/mdev")) | |
| { | |
| rtt_line = strdup(buffer); // Save the RTT line | |
| } | |
| } | |
| if (statistics_line) | |
| { | |
| int transmitted, received; | |
| float packet_loss; | |
| sscanf(statistics_line, "%d packets transmitted, %d received, %f%% packet loss", | |
| &transmitted, &received, &packet_loss); | |
| if (isnan(packet_loss)) | |
| { | |
| packet_loss = 0.0; | |
| } | |
| result.packets_sent = transmitted; | |
| result.packets_received = received; | |
| result.packet_loss = packet_loss; | |
| free(statistics_line); // Free the allocated memory for the statistics line | |
| } | |
| if (rtt_line) | |
| { | |
| float rtt_min, rtt_avg, rtt_max, rtt_mdev; | |
| sscanf(rtt_line, "rtt min/avg/max/mdev = %f/%f/%f/%f ms", | |
| &rtt_min, &rtt_avg, &rtt_max, &rtt_mdev); | |
| result.rtt_min = rtt_min; | |
| result.rtt_avg = rtt_avg; | |
| result.rtt_max = rtt_max; | |
| result.rtt_mdev = rtt_mdev; | |
| free(rtt_line); // Free the allocated memory for the RTT line | |
| } | |
| fclose(ping_output); | |
| return result; | |
| } | |
| // Removed old parsing functions - using safer versions above | |
| void handle_ping_request(int client_socket, const char *body) | |
| { | |
| char ip[MAX_IP_LEN] = ""; | |
| int length = 0, count = 4; | |
| // Use safe JSON extraction | |
| if (extract_json_string(body, "ip", ip, sizeof(ip)) != 0) { | |
| send_error_response(client_socket, 400, "IP address is required"); | |
| return; | |
| } | |
| // Validate IP address format | |
| if (!is_valid_ip(ip)) { | |
| send_error_response(client_socket, 400, "Invalid IP address format"); | |
| return; | |
| } | |
| extract_json_int(body, "length", &length); | |
| extract_json_int(body, "count", &count); | |
| // Validate count parameter | |
| if (count <= 0 || count > 100) { | |
| count = 4; // Default value | |
| } | |
| printf("\r\nPing test - IP: %s, Length: %d, Count: %d\r\n", ip, length, count); | |
| // Call ping_test function | |
| PingResult result = ping_test(ip, count); | |
| char json[256]; | |
| snprintf(json, sizeof(json), | |
| "{\"status\": \"Success\", \"packets_sent\": %d, \"packets_received\": %d, \"packet_loss\": %.2f}", | |
| result.packets_sent, result.packets_received, result.packet_loss); | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_upload_request(int client_socket, const char *buffer, int bytes_read) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char *boundary = strstr(buffer, "boundary="); | |
| if (!boundary) | |
| { | |
| strcat(response, "{\"error\": \"Invalid multipart form data\"}"); | |
| send_response(client_socket, response); | |
| return; | |
| } | |
| boundary += 9; | |
| char *file_content = strstr(buffer, "\r\n\r\n") + 4; | |
| FILE *fp = fopen("/tmp/uploaded_file", "wb"); | |
| if (fp) | |
| { | |
| fwrite(file_content, 1, bytes_read - (file_content - buffer), fp); | |
| fclose(fp); | |
| } | |
| char json[128] = "{\"message\": \"File uploaded successfully\"}"; | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_poe_config_get_request(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[512] = "[" | |
| "{\"port\": \"ge1/1\", \"pdIpAddress\": \"192.168.1.100\", \"queryInterval\": 30, \"timeoutNumber\": 3, \"bootTime\": 60, \"rebootTimes\": 0}" | |
| "]"; | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_user_management_get_request(int client_socket) | |
| { | |
| pthread_mutex_lock(&global_data_mutex); | |
| char json[2048] = "["; | |
| bool first = true; | |
| for (i = 0; i < g_data.user_count && i < MAX_USERS; i++) { | |
| if (g_data.users[i].active) { | |
| if (!first) { | |
| strcat(json, ","); | |
| } | |
| first = false; | |
| char user_entry[256]; | |
| snprintf(user_entry, sizeof(user_entry), | |
| "{\"user\": \"%s\", \"level\": \"%d\", \"password\": \"%s\", \"timeout\": %d}", | |
| g_data.users[i].username, | |
| g_data.users[i].level, | |
| g_data.users[i].password, | |
| g_data.users[i].timeout); | |
| strcat(json, user_entry); | |
| } | |
| } | |
| strcat(json, "]"); | |
| pthread_mutex_unlock(&global_data_mutex); | |
| build_json_response(client_socket, json); | |
| } | |
| void handle_user_management_post_request(int client_socket, const char *body) | |
| { | |
| char username[MAX_STRING_LEN] = ""; | |
| char password[MAX_STRING_LEN] = ""; | |
| int level = 1; | |
| int timeout = 60; | |
| // Extract user data using helper functions | |
| if (extract_json_string(body, "user", username, sizeof(username)) != 0 || | |
| extract_json_string(body, "password", password, sizeof(password)) != 0) { | |
| send_error_response(client_socket, 400, "Username and password are required"); | |
| return; | |
| } | |
| extract_json_int(body, "level", &level); | |
| extract_json_int(body, "timeout", &timeout); | |
| // Validate input parameters | |
| if (strlen(username) == 0 || strlen(password) == 0) { | |
| send_error_response(client_socket, 400, "Username and password cannot be empty"); | |
| return; | |
| } | |
| if (level < 1 || level > 15) { | |
| send_error_response(client_socket, 400, "User level must be between 1 and 15"); | |
| return; | |
| } | |
| if (timeout < 0 || timeout > 3600) { | |
| send_error_response(client_socket, 400, "Timeout must be between 0 and 3600 seconds"); | |
| return; | |
| } | |
| pthread_mutex_lock(&global_data_mutex); | |
| // Check if user already exists | |
| int user_index = -1; | |
| for (i = 0; i < g_data.user_count && i < MAX_USERS; i++) { | |
| if (strcmp(g_data.users[i].username, username) == 0) { | |
| user_index = i; | |
| break; | |
| } | |
| } | |
| bool is_new_user = (user_index == -1); | |
| if (is_new_user) { | |
| // Add new user | |
| if (g_data.user_count >= MAX_USERS) { | |
| pthread_mutex_unlock(&global_data_mutex); | |
| send_error_response(client_socket, 400, "Maximum number of users reached"); | |
| return; | |
| } | |
| user_index = g_data.user_count++; | |
| } | |
| // Update user data | |
| safe_strcpy(g_data.users[user_index].username, username, sizeof(g_data.users[user_index].username)); | |
| safe_strcpy(g_data.users[user_index].password, password, sizeof(g_data.users[user_index].password)); | |
| g_data.users[user_index].level = level; | |
| g_data.users[user_index].timeout = timeout; | |
| g_data.users[user_index].active = true; | |
| pthread_mutex_unlock(&global_data_mutex); | |
| char response_msg[128]; | |
| snprintf(response_msg, sizeof(response_msg), | |
| "{\"message\": \"User %s %s\"}", | |
| username, | |
| is_new_user ? "added" : "updated"); | |
| send_success_response(client_socket, response_msg); | |
| } | |
| void handle_user_management_delete_request(int client_socket, const char *body) | |
| { | |
| char username[MAX_STRING_LEN] = ""; | |
| // Extract username using helper function | |
| if (extract_json_string(body, "user", username, sizeof(username)) != 0) { | |
| send_error_response(client_socket, 400, "Username is required"); | |
| return; | |
| } | |
| if (strlen(username) == 0) { | |
| send_error_response(client_socket, 400, "Username cannot be empty"); | |
| return; | |
| } | |
| pthread_mutex_lock(&global_data_mutex); | |
| // Find and deactivate user | |
| bool user_found = false; | |
| for (i = 0; i < g_data.user_count && i < MAX_USERS; i++) { | |
| if (strcmp(g_data.users[i].username, username) == 0 && g_data.users[i].active) { | |
| g_data.users[i].active = false; | |
| user_found = true; | |
| break; | |
| } | |
| } | |
| pthread_mutex_unlock(&global_data_mutex); | |
| if (!user_found) { | |
| send_error_response(client_socket, 404, "User not found"); | |
| return; | |
| } | |
| char json[128]; | |
| snprintf(json, sizeof(json), "{\"message\": \"User %s deleted\"}", username); | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_user_safety_get_request(int client_socket) | |
| { | |
| pthread_mutex_lock(&global_data_mutex); | |
| char response[1024]; | |
| snprintf(response, sizeof(response), | |
| "[" | |
| "{\"serviceType\":\"HTTP\",\"managementState\":\"%s\",\"aclGroup\":%d}," | |
| "{\"serviceType\":\"SNMP\",\"managementState\":\"%s\",\"aclGroup\":%d}," | |
| "{\"serviceType\":\"TELNET\",\"managementState\":\"%s\",\"aclGroup\":%d}," | |
| "{\"serviceType\":\"SSH\",\"managementState\":\"%s\",\"aclGroup\":%d}" | |
| "]", | |
| g_data.service_access.http_enabled ? "Enable" : "Disable", g_data.service_access.http_acl, | |
| g_data.service_access.snmp_enabled ? "Enable" : "Disable", g_data.service_access.snmp_acl, | |
| g_data.service_access.telnet_enabled ? "Enable" : "Disable", g_data.service_access.telnet_acl, | |
| g_data.service_access.ssh_enabled ? "Enable" : "Disable", g_data.service_access.ssh_acl | |
| ); | |
| pthread_mutex_unlock(&global_data_mutex); | |
| build_json_response(client_socket, response); | |
| } | |
| void handle_user_safety_post_request(int client_socket, const char *body) | |
| { | |
| char serviceType[16] = "", managementState[16] = ""; | |
| int aclGroup = 0; | |
| if (extract_json_string(body, "serviceType", serviceType, sizeof(serviceType)) != 0 || | |
| extract_json_string(body, "managementState", managementState, sizeof(managementState)) != 0 || | |
| extract_json_int(body, "aclGroup", &aclGroup) != 0) | |
| { | |
| send_error_response(client_socket, 400, "Missing or invalid parameters"); | |
| return; | |
| } | |
| if (aclGroup < 1 || aclGroup > 99) { | |
| send_error_response(client_socket, 400, "ACL group must be between 1 and 99"); | |
| return; | |
| } | |
| pthread_mutex_lock(&global_data_mutex); | |
| bool enable = (strcmp(managementState, "Enable") == 0); | |
| if (strcmp(serviceType, "HTTP") == 0) { | |
| g_data.service_access.http_enabled = enable; | |
| g_data.service_access.http_acl = aclGroup; | |
| } else if (strcmp(serviceType, "SNMP") == 0) { | |
| g_data.service_access.snmp_enabled = enable; | |
| g_data.service_access.snmp_acl = aclGroup; | |
| } else if (strcmp(serviceType, "TELNET") == 0) { | |
| g_data.service_access.telnet_enabled = enable; | |
| g_data.service_access.telnet_acl = aclGroup; | |
| } else if (strcmp(serviceType, "SSH") == 0) { | |
| g_data.service_access.ssh_enabled = enable; | |
| g_data.service_access.ssh_acl = aclGroup; | |
| } else { | |
| pthread_mutex_unlock(&global_data_mutex); | |
| send_error_response(client_socket, 400, "Invalid serviceType"); | |
| return; | |
| } | |
| pthread_mutex_unlock(&global_data_mutex); | |
| build_json_response(client_socket, "{\"message\": \"Service access configuration updated\"}"); | |
| } | |
| void handle_sntp_config_get_request(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[256] = "{" | |
| "\"sntpEnable\": \"enable\"," | |
| "\"serverIp\": \"192.168.1.10\"," | |
| "\"interval\": 1800" | |
| "}"; | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_sntp_config_post_request(int client_socket, const char *body) | |
| { | |
| char sntpEnable[16] = "", serverIp[16] = ""; | |
| int interval = 0; | |
| // Extract parameters using helper functions | |
| extract_json_string(body, "sntpEnable", sntpEnable, sizeof(sntpEnable)); | |
| extract_json_string(body, "serverIp", serverIp, sizeof(serverIp)); | |
| extract_json_int(body, "interval", &interval); | |
| // Validate SNTP enable setting | |
| if (strlen(sntpEnable) > 0 && strcmp(sntpEnable, "enable") != 0 && strcmp(sntpEnable, "disable") != 0) { | |
| send_error_response(client_socket, 400, "sntpEnable must be 'enable' or 'disable'"); | |
| return; | |
| } | |
| // Validate server IP if provided | |
| if (strlen(serverIp) > 0 && !is_valid_ip(serverIp)) { | |
| send_error_response(client_socket, 400, "Invalid server IP address format"); | |
| return; | |
| } | |
| // Validate interval | |
| if (interval < 0 || interval > 86400) { // Max 24 hours | |
| send_error_response(client_socket, 400, "Interval must be between 0 and 86400 seconds"); | |
| return; | |
| } | |
| char json[128] = "{\"message\": \"SNTP configuration applied\"}"; | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_save_config_request(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[128] = "{\"message\": \"Configuration saved\"}"; | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_port_stats_request(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char port_list[512] = ""; | |
| int port; | |
| for (port = 8; port <= 15; port++) | |
| { | |
| int mapped_port = port - 7; // 8 -> ge1/1 | |
| char entry[16]; | |
| snprintf(entry, sizeof(entry), "\"ge1/%d\",", mapped_port); | |
| strcat(port_list, entry); | |
| } | |
| // Remove trailing comma | |
| int len = strlen(port_list); | |
| if (len > 0 && port_list[len - 1] == ',') | |
| port_list[len - 1] = '\0'; | |
| char json[600]; | |
| snprintf(json, sizeof(json), "{ \"ports\": [%s] }", port_list); | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_get_port_stats_request(int client_socket, const char *query) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char port_str[16] = ""; | |
| // Parse query parameter manually since it's URL encoded | |
| if (strncmp(query, "port=", 5) == 0) { | |
| safe_strcpy(port_str, query + 5, sizeof(port_str)); | |
| } else { | |
| send_error_response(client_socket, 400, "Port parameter is required"); | |
| return; | |
| } | |
| // Parse port format (ge1/X) | |
| int port_id = 0; | |
| if (strncmp(port_str, "ge1/", 4) == 0) { | |
| port_id = atoi(port_str + 4); | |
| } else { | |
| send_error_response(client_socket, 400, "Invalid port format (expected ge1/X)"); | |
| return; | |
| } | |
| if (port_id < 1 || port_id > 8) { | |
| send_error_response(client_socket, 400, "Port ID must be between 1 and 8"); | |
| return; | |
| } | |
| int real_port = port_id + 7; // Map ge1/1 => port 8 | |
| uint64_t in_octets = 0, in_ucast = 0, in_mcast = 0, in_bcast = 0; | |
| uint64_t in_discards = 0, in_errors = 0; | |
| uint64_t out_octets = 0, out_ucast = 0, out_mcast = 0, out_bcast = 0; | |
| uint64_t out_discards = 0, out_errors = 0; | |
| rtk_stat_port_get(0, real_port, IF_IN_OCTETS_INDEX, &in_octets); | |
| rtk_stat_port_get(0, real_port, IF_IN_UCAST_PKTS_INDEX, &in_ucast); | |
| rtk_stat_port_get(0, real_port, IF_IN_MULTICAST_PKTS_INDEX, &in_mcast); | |
| rtk_stat_port_get(0, real_port, IF_IN_BROADCAST_PKTS_INDEX, &in_bcast); | |
| rtk_stat_port_get(0, real_port, DOT1D_TP_PORT_IN_DISCARDS_INDEX, &in_discards); | |
| rtk_stat_port_get(0, real_port, ETHER_STATS_CRC_ALIGN_ERRORS_INDEX, &in_errors); // Approximate | |
| rtk_stat_port_get(0, real_port, IF_OUT_OCTETS_INDEX, &out_octets); | |
| rtk_stat_port_get(0, real_port, IF_OUT_UCAST_PKTS_CNT_INDEX, &out_ucast); | |
| rtk_stat_port_get(0, real_port, IF_OUT_MULTICAST_PKTS_CNT_INDEX, &out_mcast); | |
| rtk_stat_port_get(0, real_port, IF_OUT_BROADCAST_PKTS_CNT_INDEX, &out_bcast); | |
| rtk_stat_port_get(0, real_port, IF_OUT_DISCARDS_INDEX, &out_discards); | |
| rtk_stat_port_get(0, real_port, TX_ETHER_STATS_CRC_ALIGN_ERRORS_INDEX, &out_errors); // Approximate | |
| uint64_t in_nucast = in_mcast + in_bcast; | |
| uint64_t out_nucast = out_mcast + out_bcast; | |
| // ifInUnknownProtos is not supported, use 0 | |
| uint64_t in_unknown_protos = 0; | |
| char json[1024]; | |
| snprintf(json, sizeof(json), | |
| "{" | |
| "\"ifInOctets\": %llu, \"ifInUcastPkts\": %llu, \"ifInNUcastPkts\": %llu," | |
| "\"ifInDiscards\": %llu, \"ifInErrors\": %llu, \"ifInUnknownProtos\": %llu," | |
| "\"ifOutOctets\": %llu, \"ifOutUcastPkts\": %llu, \"ifOutNUcastPkts\": %llu," | |
| "\"ifOutDiscards\": %llu, \"ifOutErrors\": %llu" | |
| "}", | |
| in_octets, in_ucast, in_nucast, | |
| in_discards, in_errors, in_unknown_protos, | |
| out_octets, out_ucast, out_nucast, | |
| out_discards, out_errors); | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_storm_control_post_request(int client_socket, const char *body) | |
| { | |
| char port_str[16] = ""; | |
| char broadcast[8] = "", multicast[8] = "", dlf[8] = ""; | |
| int bRate = 0, mRate = 0, dRate = 0; | |
| // Extract parameters using helper functions | |
| if (extract_json_string(body, "port", port_str, sizeof(port_str)) != 0) { | |
| send_error_response(client_socket, 400, "Port parameter is required"); | |
| return; | |
| } | |
| extract_json_string(body, "broadcastSuppression", broadcast, sizeof(broadcast)); | |
| extract_json_string(body, "multicastSuppression", multicast, sizeof(multicast)); | |
| extract_json_string(body, "dlfSuppression", dlf, sizeof(dlf)); | |
| extract_json_int(body, "broadcastRate", &bRate); | |
| extract_json_int(body, "multicastRate", &mRate); | |
| extract_json_int(body, "dlfRate", &dRate); | |
| // Parse and validate port format (X) | |
| int sdk_port = -1; | |
| if (strncmp(port_str, 4) == 0) { | |
| int logical_port = atoi(port_str + 4); | |
| if (logical_port >= 1 && logical_port <= 8) { | |
| sdk_port = logical_port + 7; | |
| } | |
| } | |
| if (sdk_port < 8 || sdk_port > 15) { | |
| send_error_response(client_socket, 400, "Invalid port format (expected ge1/1 to ge1/8)"); | |
| return; | |
| } | |
| // Validate rate parameters | |
| if (bRate < 0 || bRate > 1000 || mRate < 0 || mRate > 1000 || dRate < 0 || dRate > 1000) { | |
| send_error_response(client_socket, 400, "Rate values must be between 0 and 1000"); | |
| return; | |
| } | |
| // Apply storm control configuration using RTK APIs | |
| int ret = RT_ERR_OK; | |
| if (strlen(broadcast) > 0) { | |
| rtk_enable_t bEn = (strcmp(broadcast, "On") == 0) ? ENABLED : DISABLED; | |
| ret |= rtk_rate_portStormCtrlEnable_set(0, sdk_port, STORM_GROUP_BROADCAST, bEn); | |
| ret |= rtk_rate_portStormCtrlRate_set(0, sdk_port, STORM_GROUP_BROADCAST, bRate); | |
| } | |
| if (strlen(multicast) > 0) { | |
| rtk_enable_t mEn = (strcmp(multicast, "On") == 0) ? ENABLED : DISABLED; | |
| ret |= rtk_rate_portStormCtrlEnable_set(0, sdk_port, STORM_GROUP_MULTICAST, mEn); | |
| ret |= rtk_rate_portStormCtrlRate_set(0, sdk_port, STORM_GROUP_MULTICAST, mRate); | |
| } | |
| if (strlen(dlf) > 0) { | |
| rtk_enable_t uEn = (strcmp(dlf, "On") == 0) ? ENABLED : DISABLED; | |
| ret |= rtk_rate_portStormCtrlEnable_set(0, sdk_port, STORM_GROUP_UNICAST, uEn); | |
| ret |= rtk_rate_portStormCtrlRate_set(0, sdk_port, STORM_GROUP_UNICAST, dRate); | |
| } | |
| if (ret != RT_ERR_OK) { | |
| send_error_response(client_socket, 500, "Failed to apply storm control configuration"); | |
| return; | |
| } | |
| char json[128]; | |
| snprintf(json, sizeof(json), "{\"message\": \"Storm control configuration applied to %s\"}", port_str); | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_snmp_community_get_request(int client_socket) | |
| { | |
| pthread_mutex_lock(&global_data_mutex); | |
| char json[1024] = "["; | |
| bool first = true; | |
| for (i = 0; i < g_data.community_count && i < MAX_COMMUNITIES; i++) { | |
| if (g_data.communities[i].active) { | |
| if (!first) { | |
| strcat(json, ","); | |
| } | |
| first = false; | |
| char community_entry[128]; | |
| snprintf(community_entry, sizeof(community_entry), | |
| "{\"community\": \"%s\", \"access\": \"%s\"}", | |
| g_data.communities[i].community, | |
| g_data.communities[i].access); | |
| strcat(json, community_entry); | |
| } | |
| } | |
| strcat(json, "]"); | |
| pthread_mutex_unlock(&global_data_mutex); | |
| build_json_response(client_socket, json); | |
| } | |
| void handle_snmp_community_post_request(int client_socket, const char *body) | |
| { | |
| char community[32] = "", access[16] = ""; | |
| // Extract parameters using helper functions | |
| if (extract_json_string(body, "community", community, sizeof(community)) != 0) { | |
| send_error_response(client_socket, 400, "Community name is required"); | |
| return; | |
| } | |
| if (extract_json_string(body, "access", access, sizeof(access)) != 0) { | |
| send_error_response(client_socket, 400, "Access level is required"); | |
| return; | |
| } | |
| // Validate access level | |
| if (strcmp(access, "read-only") != 0 && strcmp(access, "read-write") != 0) { | |
| send_error_response(client_socket, 400, "Access must be 'read-only' or 'read-write'"); | |
| return; | |
| } | |
| char json[128]; | |
| snprintf(json, sizeof(json), "{\"message\": \"SNMP community %s added\"}", community); | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_snmp_community_delete_request(int client_socket, const char *body) | |
| { | |
| char community[32] = ""; | |
| // Extract community name using helper function | |
| if (extract_json_string(body, "community", community, sizeof(community)) != 0) { | |
| send_error_response(client_socket, 400, "Community name is required"); | |
| return; | |
| } | |
| if (strlen(community) == 0) { | |
| send_error_response(client_socket, 400, "Community name cannot be empty"); | |
| return; | |
| } | |
| char json[128]; | |
| snprintf(json, sizeof(json), "{\"message\": \"SNMP community %s deleted\"}", community); | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_snmp_community_patch_request(int client_socket, const char *body) | |
| { | |
| char community[32] = "", access[16] = ""; | |
| // Extract parameters using helper functions | |
| if (extract_json_string(body, "community", community, sizeof(community)) != 0) { | |
| send_error_response(client_socket, 400, "Community name is required"); | |
| return; | |
| } | |
| if (extract_json_string(body, "access", access, sizeof(access)) != 0) { | |
| send_error_response(client_socket, 400, "Access level is required"); | |
| return; | |
| } | |
| // Validate access level | |
| if (strcmp(access, "read-only") != 0 && strcmp(access, "read-write") != 0) { | |
| send_error_response(client_socket, 400, "Access must be 'read-only' or 'read-write'"); | |
| return; | |
| } | |
| char json[128]; | |
| snprintf(json, sizeof(json), "{\"message\": \"SNMP community %s updated\"}", community); | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_trap_targets_get_request(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[256] = "[" | |
| "{\"ip\": \"192.168.1.100\", \"community\": \"public\", \"version\": \"v2c\"}," | |
| "{\"ip\": \"192.168.1.101\", \"community\": \"private\", \"version\": \"v3\"}" | |
| "]"; | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_trap_targets_post_request(int client_socket, const char *body) | |
| { | |
| char ip[16] = "", community[32] = "", version[8] = ""; | |
| // Extract parameters using helper functions | |
| if (extract_json_string(body, "ip", ip, sizeof(ip)) != 0) { | |
| send_error_response(client_socket, 400, "IP address is required"); | |
| return; | |
| } | |
| if (extract_json_string(body, "community", community, sizeof(community)) != 0) { | |
| send_error_response(client_socket, 400, "Community is required"); | |
| return; | |
| } | |
| if (extract_json_string(body, "version", version, sizeof(version)) != 0) { | |
| send_error_response(client_socket, 400, "SNMP version is required"); | |
| return; | |
| } | |
| // Validate IP address | |
| if (!is_valid_ip(ip)) { | |
| send_error_response(client_socket, 400, "Invalid IP address format"); | |
| return; | |
| } | |
| // Validate SNMP version | |
| if (strcmp(version, "v1") != 0 && strcmp(version, "v2c") != 0 && strcmp(version, "v3") != 0) { | |
| send_error_response(client_socket, 400, "SNMP version must be 'v1', 'v2c', or 'v3'"); | |
| return; | |
| } | |
| char json[128]; | |
| snprintf(json, sizeof(json), "{\"message\": \"Trap target %s added\"}", ip); | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_trap_targets_delete_request(int client_socket, const char *body) | |
| { | |
| char ip[16] = ""; | |
| // Extract IP address using helper function | |
| if (extract_json_string(body, "ip", ip, sizeof(ip)) != 0) { | |
| send_error_response(client_socket, 400, "IP address is required"); | |
| return; | |
| } | |
| // Validate IP address | |
| if (!is_valid_ip(ip)) { | |
| send_error_response(client_socket, 400, "Invalid IP address format"); | |
| return; | |
| } | |
| char json[128]; | |
| snprintf(json, sizeof(json), "{\"message\": \"Trap target %s deleted\"}", ip); | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_qos_config_get_request(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[2048] = "["; | |
| int web_port; | |
| for (web_port = 1; web_port <= 8; web_port++) | |
| { | |
| int sdk_port = web_port + 7; | |
| rtk_pri_t priority = 0; | |
| if (rtk_qos_portPri_get(0, sdk_port, &priority) != RT_ERR_OK) | |
| { | |
| priority = 0; | |
| } | |
| const char *qosType = (priority > 0) ? "PORT" : "NO QOS"; | |
| char entry[128]; | |
| snprintf(entry, sizeof(entry), | |
| "{\"port\": %d, \"qosType\": \"%s\", \"priority\": %d},", | |
| web_port, qosType, priority); | |
| strncat(json, entry, sizeof(json) - strlen(json) - 1); | |
| } | |
| int len = strlen(json); | |
| if (len > 1 && json[len - 1] == ',') | |
| { | |
| json[len - 1] = '\0'; | |
| } | |
| strcat(json, "]"); | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_qos_config_post_request(int client_socket, const char *body) | |
| { | |
| char port_str[8] = "", qosType[32] = ""; | |
| int priority = 0; | |
| // Extract parameters using helper functions | |
| int port; | |
| if (extract_json_int(body, "port", &port) != 0) { | |
| send_error_response(client_socket, 400, "Port is required"); | |
| return; | |
| } | |
| extract_json_string(body, "qosType", qosType, sizeof(qosType)); | |
| extract_json_int(body, "priority", &priority); | |
| // Validate port | |
| int web_port = atoi(port_str); | |
| if (!is_valid_port(web_port, 1, 8)) { | |
| send_error_response(client_socket, 400, "Port must be between 1 and 8"); | |
| return; | |
| } | |
| // Validate priority | |
| if (priority < 0 || priority > 7) { | |
| send_error_response(client_socket, 400, "Priority must be between 0 and 7"); | |
| return; | |
| } | |
| int sdk_port = web_port + 7; | |
| int ret = rtk_qos_portPri_set(0, sdk_port, priority); | |
| if (ret != RT_ERR_OK) { | |
| send_error_response(client_socket, 500, "Failed to apply QoS configuration"); | |
| return; | |
| } | |
| char json[128]; | |
| snprintf(json, sizeof(json), | |
| "{\"message\": \"QoS config applied to port %d (%s)\", \"result\": \"success\"}", | |
| web_port, qosType); | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_get_qos_schedule_request(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[4096] = "["; | |
| int web_port; | |
| for (web_port = 1; web_port <= 8; web_port++) | |
| { | |
| int sdk_port = web_port + 7; | |
| rtk_qos_scheduling_type_t mode = WRR; | |
| rtk_qos_queue_weights_t qweights = {{0}}; | |
| rtk_qos_schedulingAlgorithm_get(0, sdk_port, &mode); | |
| rtk_qos_schedulingQueue_get(0, sdk_port, &qweights); | |
| const char *mode_str = (mode == WFQ) ? "WFQ" : "WRR"; | |
| char weights[256] = "["; | |
| int q; | |
| for (q = 0; q < 8; q++) | |
| { | |
| char w[8]; | |
| snprintf(w, sizeof(w), "%u,", qweights.weights[q]); | |
| strcat(weights, w); | |
| } | |
| if (weights[strlen(weights) - 1] == ',') | |
| { | |
| weights[strlen(weights) - 1] = '\0'; | |
| } | |
| strcat(weights, "]"); | |
| char entry[512]; | |
| snprintf(entry, sizeof(entry), | |
| "{\"port\": \"%d\", \"mode\": \"%s\", \"weights\": %s},", | |
| web_port, mode_str, weights); | |
| strcat(json, entry); | |
| } | |
| int len = strlen(json); | |
| if (len > 1 && json[len - 1] == ',') | |
| { | |
| json[len - 1] = '\0'; | |
| } | |
| strcat(json, "]"); | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_set_qos_schedule_request(int client_socket, const char *body) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char port_str[8] = "", mode[16] = ""; | |
| int weights[8] = {0}; | |
| sscanf(body, | |
| "{\"port\":\"%7[^\"]\",\"mode\":\"%15[^\"]\",\"weights\":[%d,%d,%d,%d,%d,%d,%d,%d]}", | |
| port_str, mode, | |
| &weights[0], &weights[1], &weights[2], &weights[3], | |
| &weights[4], &weights[5], &weights[6], &weights[7]); | |
| int web_port = atoi(port_str); | |
| int sdk_port = web_port + 7; | |
| rtk_qos_scheduling_type_t sched_mode = (strcmp(mode, "WFQ") == 0) ? WFQ : WRR; | |
| rtk_qos_queue_weights_t qweights; | |
| int i; | |
| for (i = 0; i < 8; i++) | |
| qweights.weights[i] = weights[i]; | |
| int ret1 = rtk_qos_schedulingAlgorithm_set(0, sdk_port, sched_mode); | |
| int ret2 = rtk_qos_schedulingQueue_set(0, sdk_port, &qweights); | |
| char json[128]; | |
| snprintf(json, sizeof(json), | |
| "{\"message\": \"QoS schedule applied to port %d\", \"result\": %s}", | |
| web_port, (ret1 == RT_ERR_OK && ret2 == RT_ERR_OK) ? "\"success\"" : "\"fail\""); | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_not_found(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 404 Not Found\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n" | |
| "{\"error\": \"Endpoint not found\"}"; | |
| send_response(client_socket, response); | |
| } | |
| // Additional route handlers using RTK functions | |
| // void handle_port_config_get_request(int client_socket) | |
| // { | |
| // uint32 unit = 0; | |
| // char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| // "Access-Control-Allow-Origin: *\r\n" | |
| // "Content-Type: application/json\r\n\r\n"; | |
| // char json[2048] = "["; | |
| // int rtk_port; | |
| // // Get port configurations using RTK APIs for ports 8-15 (mapped to ge1/1-8) | |
| // for (rtk_port = 8; rtk_port <= 15; rtk_port++) | |
| // { | |
| // int web_port = rtk_port - 7; // Map RTK port 8->1, 9->2, etc. | |
| // if (web_port > 1) strcat(json, ","); | |
| // rtk_port_linkStatus_t linkStatus = PORT_LINKDOWN; | |
| // rtk_port_speed_t speed = PORT_SPEED_1000M; | |
| // rtk_port_duplex_t duplex = PORT_FULL_DUPLEX; | |
| // rtk_enable_t adminStatus = DISABLED; | |
| // // Get actual port status using RTK APIs | |
| // int ret1 = rtk_port_link_get(unit, rtk_port, &linkStatus); | |
| // int ret3 = rtk_port_adminEnable_get(unit, rtk_port, &adminStatus); | |
| // // Check if auto-negotiation is enabled | |
| // rtk_enable_t autonego_enable = ENABLED; | |
| // rtk_enable_t flow_ctrl = DISABLED; | |
| // int ret_auto = rtk_port_phyAutoNegoEnable_get(unit, rtk_port, &autonego_enable); | |
| // // Map speed enum to string | |
| // const char *speed_str = "Auto"; | |
| // const char *duplex_str = "Auto"; | |
| // if (ret_auto == RT_ERR_OK && autonego_enable == DISABLED) { | |
| // // Port is in force mode, get force mode settings | |
| // int ret2 = rtk_port_phyForceModeAbility_get(unit, rtk_port, &speed, &duplex, &flow_ctrl); | |
| // if (ret2 == RT_ERR_OK) { | |
| // switch(speed) { | |
| // case PORT_SPEED_10M: speed_str = "10M"; break; | |
| // case PORT_SPEED_100M: speed_str = "100M"; break; | |
| // case PORT_SPEED_1000M: speed_str = "1000M"; break; | |
| // case PORT_SPEED_2_5G: speed_str = "2.5G"; break; | |
| // case PORT_SPEED_5G: speed_str = "5G"; break; | |
| // case PORT_SPEED_10G: speed_str = "10G"; break; | |
| // default: speed_str = "Unknown"; break; | |
| // } | |
| // duplex_str = (duplex == PORT_FULL_DUPLEX) ? "Full" : "Half"; | |
| // } | |
| // } else { | |
| // // Auto-negotiation enabled or error occurred | |
| // speed_str = "Auto"; | |
| // duplex_str = "Auto"; | |
| // } | |
| // char portJson[512]; | |
| // sprintf(portJson, | |
| // "{\"port\":\"%d\"," | |
| // "\"description\":\"Port ge1/%d\"," | |
| // "\"adminStatus\":\"%s\"," | |
| // "\"operateStatus\":\"%s\"," | |
| // "\"duplex\":\"%s\"," | |
| // "\"configSpeed\":\"%s\"," | |
| // "\"vlanMode\":\"%s\"," | |
| // "\"defaultVlan\":\"%d\"}", | |
| // web_port, | |
| // web_port, | |
| // (ret3 == RT_ERR_OK && adminStatus == ENABLED) ? "Up" : "Down", | |
| // (ret1 == RT_ERR_OK && linkStatus == PORT_LINKUP) ? "Up" : "Down", | |
| // duplex_str, | |
| // speed_str, | |
| // "Not Available", // placeholder: replace with real mode if needed | |
| // 1 // placeholder: replace with actual VLAN ID | |
| // ); | |
| // } | |
| // strcat(json, "]"); | |
| // strcat(response, json); | |
| // send_response(client_socket, response); | |
| // } | |
| /** | |
| * Extracts an array of integers from a JSON string for a given key. | |
| * @param json The JSON string to parse (e.g., "{\"ports\": [1, -2, 3]}"). | |
| * @param key The key whose value is an array of integers. | |
| * @param out_array The output array to store the integers. | |
| * @param max_count The maximum number of integers to extract (size of out_array). | |
| * @return The number of integers extracted, or -1 on error. | |
| */ | |
| int extract_json_array_of_ints(const char *json, const char *key, int *out_array, int max_count) { | |
| // Input validation | |
| if (!json || !key || !out_array || max_count <= 0) { | |
| return -1; | |
| } | |
| // Find the key in the JSON string | |
| unsigned int key_len = strlen(key); | |
| const char *key_start = strstr(json, key); | |
| if (!key_start) { | |
| return -1; // Key not found | |
| } | |
| // Verify the key is properly quoted and followed by a colon | |
| if (key_start[-1] != '"' || key_start[key_len] != '"') { | |
| return -1; // Key not properly quoted | |
| } | |
| const char *colon = key_start + key_len + 1; | |
| while (*colon && isspace(*colon)) colon++; // Skip whitespace | |
| if (*colon != ':') { | |
| return -1; // No colon after key | |
| } | |
| // Find the opening bracket of the array | |
| const char *array_start = colon + 1; | |
| while (*array_start && isspace(*array_start)) array_start++; // Skip whitespace | |
| if (*array_start != '[') { | |
| return -1; // No array start | |
| } | |
| // Parse the array elements | |
| int count = 0; | |
| const char *p = array_start + 1; | |
| while (*p && *p != ']' && count < max_count) { | |
| // Skip whitespace | |
| while (*p && isspace(*p)) p++; | |
| if (*p == ']') break; // Empty array or end of array | |
| // Parse an integer (including negative numbers) | |
| char *endptr; | |
| long value = strtol(p, &endptr, 10); | |
| if (endptr == p) { | |
| return -1; // No valid number found | |
| } | |
| if (value > INT_MAX || value < INT_MIN) { | |
| return -1; // Number out of int range | |
| } | |
| out_array[count++] = (int)value; | |
| // Move to the next element | |
| p = endptr; | |
| while (*p && isspace(*p)) p++; // Skip whitespace | |
| if (*p == ']') break; // End of array | |
| if (*p != ',') { | |
| return -1; // Expected comma or end of array | |
| } | |
| p++; // Skip comma | |
| } | |
| // Check if the array is properly closed | |
| while (*p && isspace(*p)) p++; | |
| if (*p != ']') { | |
| return -1; // Array not closed | |
| } | |
| return count; // Success, return number of integers extracted | |
| } | |
| void handle_port_config_post_request(int client_socket, const char *body) | |
| { | |
| uint32 unit = 0; | |
| char speed[16] = ""; | |
| char duplex[16] = ""; | |
| char adminStatus[16] = ""; | |
| char description[128] = ""; | |
| int ports[8]; // Max 8 ports expected | |
| int port_count = extract_json_array_of_ints(body, "ports", ports, 8); | |
| if (port_count <= 0) { | |
| send_error_response(client_socket, 400, "At least one port must be specified"); | |
| return; | |
| } | |
| extract_json_string(body, "configSpeed", speed, sizeof(speed)); | |
| extract_json_string(body, "adminStatus", adminStatus, sizeof(adminStatus)); | |
| extract_json_string(body, "description", description, sizeof(description)); | |
| rtk_port_speed_t port_speed = PORT_SPEED_1000M; | |
| rtk_port_duplex_t port_duplex = PORT_FULL_DUPLEX; | |
| rtk_enable_t flow_ctrl = DISABLED; | |
| bool use_force_mode = false; | |
| if (strlen(speed) > 0 && strcmp(speed, "Auto") != 0) { | |
| use_force_mode = true; | |
| if (strcmp(speed, "10M") == 0) port_speed = PORT_SPEED_10M; | |
| else if (strcmp(speed, "100M") == 0) port_speed = PORT_SPEED_100M; | |
| else if (strcmp(speed, "1000M") == 0) port_speed = PORT_SPEED_1000M; | |
| else if (strcmp(speed, "2.5G") == 0) port_speed = PORT_SPEED_2_5G; | |
| else if (strcmp(speed, "5G") == 0) port_speed = PORT_SPEED_5G; | |
| else if (strcmp(speed, "10G") == 0) port_speed = PORT_SPEED_10G; | |
| } | |
| if (strlen(duplex) > 0 && strcmp(duplex, "Auto") != 0) { | |
| use_force_mode = true; | |
| port_duplex = (strcmp(duplex, "Full") == 0) ? PORT_FULL_DUPLEX : PORT_HALF_DUPLEX; | |
| } | |
| for ( i = 0; i < port_count; ++i) { | |
| int web_port = ports[i]; | |
| if (!is_valid_port(web_port, 1, 8)) continue; | |
| int rtk_port = web_port + 7; | |
| // Admin status | |
| if (strlen(adminStatus) > 0) { | |
| rtk_enable_t enable = (strcmp(adminStatus, "Up") == 0) ? ENABLED : DISABLED; | |
| if (rtk_port_adminEnable_set(unit, rtk_port, enable) != RT_ERR_OK) { | |
| send_error_response(client_socket, 500, "Failed to set admin status"); | |
| return; | |
| } | |
| } | |
| // Speed/Duplex | |
| if (strlen(speed) > 0 || strlen(duplex) > 0) { | |
| if (use_force_mode) { | |
| if (rtk_port_phyAutoNegoEnable_set(unit, rtk_port, DISABLED) != RT_ERR_OK || | |
| rtk_port_phyForceModeAbility_set(unit, rtk_port, port_speed, port_duplex, flow_ctrl) != RT_ERR_OK) { | |
| send_error_response(client_socket, 500, "Failed to set force mode config"); | |
| return; | |
| } | |
| } else { | |
| if (rtk_port_phyAutoNegoEnable_set(unit, rtk_port, ENABLED) != RT_ERR_OK) { | |
| send_error_response(client_socket, 500, "Failed to enable auto-negotiation"); | |
| return; | |
| } | |
| } | |
| } | |
| // You can optionally store or print description here (if used) | |
| printf("description:%s\n", description); | |
| } | |
| send_success_response(client_socket, "{\"message\":\"Port(s) configuration updated successfully\"}"); | |
| } | |
| void handle_port_rate_limit_get_request(int client_socket) | |
| { | |
| uint32 unit = 0; | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[2048] = "["; | |
| int rtk_port; | |
| // Get rate limit settings for ports 8-15 (mapped to ge1/1-8) | |
| for (rtk_port = 8; rtk_port <= 15; rtk_port++) | |
| { | |
| int web_port = rtk_port - 7; | |
| if (web_port > 1) strcat(json, ","); | |
| uint32 ingressRate = 1000; | |
| uint32 egressRate = 1000; | |
| rtk_enable_t ingressEnable = DISABLED; | |
| rtk_enable_t egressEnable = DISABLED; | |
| // Get ingress rate limit using RTK APIs | |
| rtk_rate_portIgrBwCtrlEnable_get(unit, rtk_port, &ingressEnable); | |
| rtk_rate_portIgrBwCtrlRate_get(unit, rtk_port, &ingressRate); | |
| // Get egress rate limit using RTK APIs | |
| rtk_rate_portEgrBwCtrlEnable_get(unit, rtk_port, &egressEnable); | |
| rtk_rate_portEgrBwCtrlRate_get(unit, rtk_port, &egressRate); | |
| char portJson[256]; | |
| sprintf(portJson, | |
| "{\"port\":\"%d\"," | |
| "\"ingressRate\":%u," | |
| "\"egressRate\":%u," | |
| "\"enabled\":%s}", | |
| web_port, ingressRate, egressRate, | |
| (ingressEnable == ENABLED || egressEnable == ENABLED) ? "true" : "false"); | |
| strcat(json, portJson); | |
| } | |
| strcat(json, "]"); | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_port_rate_limit_post_request(int client_socket, const char *body) | |
| { | |
| uint32 unit = 0; | |
| int port = extract_value_int(body, "\"port\""); | |
| int ingressRate = 0; //extract_value_int(body, "\"ingressRate\""); | |
| int egressRate = 0; //extract_value_int(body, "\"egressRate\""); | |
| int enabled = 1; //extract_value_int(body, "\"enabled\""); | |
| // // Extract parameters using helper functions | |
| // if (extract_json_int(body, "port", &port) != 0) { | |
| // send_error_response(client_socket, 400, "Port is required"); | |
| // return; | |
| // } | |
| extract_json_int(body, "ingressRate", &ingressRate); | |
| extract_json_int(body, "egressRate", &egressRate); | |
| // extract_json_int(body, "enabled", &enabled); | |
| // Validate and convert web port to RTK port | |
| if (!is_valid_port(port, 1, 8)) { | |
| send_error_response(client_socket, 400, "Port must be between 1 and 8"); | |
| return; | |
| } | |
| int rtk_port = port + 7; | |
| // Validate rate parameters | |
| if (ingressRate < 0 || ingressRate > 1000000 || egressRate < 0 || egressRate > 1000000) { | |
| send_error_response(client_socket, 400, "Rate must be between 0 and 1000000 kbps"); | |
| return; | |
| } | |
| // Apply rate limit using RTK APIs | |
| rtk_enable_t enable_val = enabled ? ENABLED : DISABLED; | |
| int ret1 = rtk_rate_portIgrBwCtrlEnable_set(unit, rtk_port, enable_val); | |
| int ret2 = rtk_rate_portIgrBwCtrlRate_set(unit, rtk_port, ingressRate); | |
| int ret3 = rtk_rate_portEgrBwCtrlEnable_set(unit, rtk_port, enable_val); | |
| int ret4 = rtk_rate_portEgrBwCtrlRate_set(unit, rtk_port, egressRate); | |
| if (ret1 != RT_ERR_OK || ret2 != RT_ERR_OK || ret3 != RT_ERR_OK || ret4 != RT_ERR_OK) { | |
| send_error_response(client_socket, 500, "Failed to apply rate limit configuration"); | |
| return; | |
| } | |
| char json[128]; | |
| snprintf(json, sizeof(json), "{\"message\":\"Rate limit for port %d updated successfully\"}", port); | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_learn_limit_get_request(int client_socket) | |
| { | |
| uint32 unit = 0; | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[2048] = "["; | |
| int rtk_port; | |
| for (rtk_port = 8; rtk_port <= 15; rtk_port++) | |
| { | |
| int web_port = rtk_port - 7; | |
| if (web_port > 1) strcat(json, ","); | |
| uint32 maxMac = 8192; | |
| int ret = rtk_l2_portLimitLearningCnt_get(unit, rtk_port, &maxMac); | |
| if(ret != RT_ERR_OK) | |
| { | |
| send_error_response(client_socket, 500, "Failed to get learn limit"); | |
| return; | |
| } | |
| char portJson[128]; | |
| snprintf(portJson, sizeof(portJson), | |
| "{\"port\":%d,\"macLimit\":%u}", | |
| web_port, maxMac); | |
| strcat(json, portJson); | |
| } | |
| strcat(json, "]"); | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_learn_limit_post_request(int client_socket, const char *body) | |
| { | |
| uint32 unit = 0; | |
| int port = -1, macLimit = 0; | |
| bool cancel = false; | |
| if (extract_json_int(body, "port", &port) != 0) { | |
| send_error_response(client_socket, 400, "Port is required"); | |
| return; | |
| } | |
| if (extract_json_int(body, "macLimit", &macLimit) != 0) { | |
| send_error_response(client_socket, 400, "macLimit is required"); | |
| return; | |
| } | |
| extract_json_bool(body, "cancel", &cancel); // Optional flag | |
| if (!is_valid_port(port, 1, 8)) { | |
| send_error_response(client_socket, 400, "Port must be between 1 and 8"); | |
| return; | |
| } | |
| if (macLimit < 0 || macLimit > 8191) { | |
| send_error_response(client_socket, 400, "macLimit must be between 0 and 8191"); | |
| return; | |
| } | |
| int rtk_port = port + 7; | |
| int ret = rtk_l2_portLimitLearningCnt_set(unit, rtk_port, macLimit); | |
| if (ret != RT_ERR_OK) { | |
| send_error_response(client_socket, 500, "Failed to set learning limit"); | |
| return; | |
| } | |
| char json[128]; | |
| snprintf(json, sizeof(json), | |
| "{\"message\":\"Learn limit for port %d %s successfully\"}", | |
| port, | |
| cancel ? "reset" : "updated"); | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_download_config_request(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| // Return current configuration as JSON | |
| char json[1024] = "{" | |
| "\"systemConfig\":{\"name\":\"Switch\",\"location\":\"Lab\"}," | |
| "\"portConfig\":[{\"port\":\"1\",\"status\":\"Up\"}]," | |
| "\"timestamp\":\"2024-01-01T00:00:00Z\"" | |
| "}"; | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_delete_config_request(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[128] = "{\"message\":\"Configuration deleted successfully\"}"; | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| // Serial configuration handler | |
| void handle_serial_config_get_request(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[256] = "{" | |
| "\"baudRate\":\"38400\"," | |
| "\"charSize\":\"8\"," | |
| "\"parity\":\"None\"," | |
| "\"stopBits\":\"1\"," | |
| "\"flowControl\":\"None\"" | |
| "}"; | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| // System configuration handlers | |
| void handle_system_config_get_request(int client_socket) | |
| { | |
| pthread_mutex_lock(&global_data_mutex); | |
| char json[1024]; | |
| snprintf(json, sizeof(json), | |
| "{" | |
| "\"description\":\"%s\"," | |
| "\"objectId\":\"%s\"," | |
| "\"version\":\"%s\"," | |
| "\"interfaces\":%d," | |
| "\"serialNumber\":\"%s\"," | |
| "\"macAddress\":\"%s\"," | |
| "\"ipAddress\":\"%s\"," | |
| "\"startTime\":\"0-Days 0-Hours 7-Minutes 5-Seconds\"," | |
| "\"dateTime\":\"2020/01/01 00:04:57\"," | |
| "\"systemName\":\"%s\"," | |
| "\"systemLocation\":\"%s\"," | |
| "\"systemContact\":\"%s\"" | |
| "}", | |
| g_data.system.description, | |
| g_data.system.objectId, | |
| g_data.system.version, | |
| g_data.system.interfaces, | |
| g_data.system.serialNumber, | |
| g_data.system.macAddress, | |
| g_data.system.ipAddress, | |
| g_data.system.systemName, | |
| g_data.system.systemLocation, | |
| g_data.system.systemContact); | |
| pthread_mutex_unlock(&global_data_mutex); | |
| build_json_response(client_socket, json); | |
| } | |
| void handle_system_config_post_request(int client_socket, const char *body) | |
| { | |
| char systemName[MAX_STRING_LEN] = ""; | |
| char systemLocation[MAX_STRING_LEN] = ""; | |
| char systemContact[MAX_STRING_LEN] = ""; | |
| // Use safe JSON extraction - allow optional fields | |
| extract_json_string(body, "systemName", systemName, sizeof(systemName)); | |
| extract_json_string(body, "systemLocation", systemLocation, sizeof(systemLocation)); | |
| extract_json_string(body, "systemContact", systemContact, sizeof(systemContact)); | |
| // Validate that at least one field is provided | |
| if (strlen(systemName) == 0 && strlen(systemLocation) == 0 && strlen(systemContact) == 0) { | |
| send_error_response(client_socket, 400, "At least one system configuration field is required"); | |
| return; | |
| } | |
| pthread_mutex_lock(&global_data_mutex); | |
| // Update global data only if values are provided | |
| if (strlen(systemName) > 0) { | |
| safe_strcpy(g_data.system.systemName, systemName, sizeof(g_data.system.systemName)); | |
| } | |
| if (strlen(systemLocation) > 0) { | |
| safe_strcpy(g_data.system.systemLocation, systemLocation, sizeof(g_data.system.systemLocation)); | |
| } | |
| if (strlen(systemContact) > 0) { | |
| safe_strcpy(g_data.system.systemContact, systemContact, sizeof(g_data.system.systemContact)); | |
| } | |
| pthread_mutex_unlock(&global_data_mutex); | |
| send_success_response(client_socket, "{\"message\":\"Config saved successfully\"}"); | |
| } | |
| // Port stats handler | |
| void handle_port_stats_get_request(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[256] = "{\"ports\":[\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\",\"10\"]}"; | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| // Storm control handlers using RTK APIs | |
| void handle_storm_control_get_request(int client_socket) | |
| { | |
| uint32 unit = 0; | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[2048] = "["; | |
| int rtk_port; | |
| // Get storm control settings for ports 8-15 (mapped to ge1/1-8) | |
| for (rtk_port = 8; rtk_port <= 15; rtk_port++) | |
| { | |
| int web_port = rtk_port - 7; | |
| if (web_port > 1) strcat(json, ","); | |
| rtk_enable_t bcEnable = DISABLED, mcEnable = DISABLED, ufEnable = DISABLED; | |
| uint32 bcRate = 64, mcRate = 64, ufRate = 64; | |
| // Get storm control settings using RTK APIs | |
| rtk_rate_portStormCtrlEnable_get(unit, rtk_port, STORM_GROUP_BROADCAST, &bcEnable); | |
| rtk_rate_portStormCtrlRate_get(unit, rtk_port, STORM_GROUP_BROADCAST, &bcRate); | |
| rtk_rate_portStormCtrlEnable_get(unit, rtk_port, STORM_GROUP_MULTICAST, &mcEnable); | |
| rtk_rate_portStormCtrlRate_get(unit, rtk_port, STORM_GROUP_MULTICAST, &mcRate); | |
| rtk_rate_portStormCtrlEnable_get(unit, rtk_port, STORM_GROUP_UNICAST, &ufEnable); | |
| rtk_rate_portStormCtrlRate_get(unit, rtk_port, STORM_GROUP_UNICAST, &ufRate); | |
| char portJson[512]; | |
| sprintf(portJson, | |
| "{\"port\":\"%d\"," | |
| "\"broadcastSuppression\":\"%s\"," | |
| "\"broadcastRate\":%u," | |
| "\"multicastSuppression\":\"%s\"," | |
| "\"multicastRate\":%u," | |
| "\"dlfSuppression\":\"%s\"," | |
| "\"dlfRate\":%u}", | |
| web_port, | |
| (bcEnable == ENABLED) ? "On" : "Off", bcRate, | |
| (mcEnable == ENABLED) ? "On" : "Off", mcRate, | |
| (ufEnable == ENABLED) ? "On" : "Off", ufRate); | |
| strcat(json, portJson); | |
| } | |
| strcat(json, "]"); | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| // Flow control handlers using RTK APIs | |
| void handle_flow_control_get_request(int client_socket) | |
| { | |
| uint32 unit = 0; | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[1024] = "["; | |
| int rtk_port; | |
| // Get flow control settings for ports 8-15 (mapped to ge1/1-8) | |
| for (rtk_port = 8; rtk_port <= 15; rtk_port++) | |
| { | |
| int web_port = rtk_port - 7; | |
| if (web_port > 1) strcat(json, ","); | |
| rtk_enable_t flowCtrlEnable = DISABLED; | |
| rtk_port_flowCtrlEnable_get(unit, rtk_port, &flowCtrlEnable); | |
| char portJson[128]; | |
| sprintf(portJson, | |
| "{\"port\":\"%d\",\"state\":\"%s\"}", | |
| web_port, | |
| (flowCtrlEnable == ENABLED) ? "On" : "Off"); | |
| strcat(json, portJson); | |
| } | |
| strcat(json, "]"); | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_flow_control_post_request(int client_socket, const char *body) | |
| { | |
| uint32 unit = 0; | |
| char state[8] = ""; | |
| int port = extract_value_int(body, "\"port\""); | |
| // // Extract parameters using helper functions | |
| // if (extract_json_int(body, "port", &port) != 0) { | |
| // send_error_response(client_socket, 400, "Port is required"); | |
| // return; | |
| // } | |
| if (extract_json_string(body, "state", state, sizeof(state)) != 0) { | |
| send_error_response(client_socket, 400, "State is required"); | |
| return; | |
| } | |
| // Validate and convert web port to RTK port | |
| if (!is_valid_port(port, 1, 8)) { | |
| send_error_response(client_socket, 400, "Port must be between 1 and 8"); | |
| return; | |
| } | |
| int rtk_port = port + 7; | |
| // Validate state | |
| if (strcmp(state, "On") != 0 && strcmp(state, "Off") != 0) { | |
| send_error_response(client_socket, 400, "State must be 'On' or 'Off'"); | |
| return; | |
| } | |
| rtk_enable_t enable = (strcmp(state, "On") == 0) ? ENABLED : DISABLED; | |
| // Set flow control using RTK API | |
| int ret = rtk_port_flowCtrlEnable_set(unit, rtk_port, enable); | |
| if (ret != RT_ERR_OK) { | |
| send_error_response(client_socket, 500, "Failed to set flow control"); | |
| return; | |
| } | |
| char json[128]; | |
| snprintf(json, sizeof(json), "{\"message\":\"Flow control for port %d updated to %s\"}", port, state); | |
| send_success_response(client_socket, json); | |
| } | |
| // Protected port handlers | |
| void handle_protected_port_get_request(int client_socket) | |
| { | |
| uint32 unit = 0; | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[512] = "["; | |
| int rtk_port; | |
| // Get protected port status for ports 8-15 (mapped to ge1/1-8) | |
| for (rtk_port = 8; rtk_port <= 15; rtk_port++) | |
| { | |
| int web_port = rtk_port - 7; | |
| if (web_port > 1) strcat(json, ","); | |
| // Get port isolation settings (protected port functionality) | |
| rtk_portmask_t portmask; | |
| bool is_protected = false; | |
| int ret = rtk_port_isolation_get(unit, rtk_port, &portmask); | |
| if (ret == RT_ERR_OK) { | |
| // Check if this port has limited isolation (protected) | |
| // If only a few ports are allowed, consider it protected | |
| uint32 port_count = 0; | |
| int p; | |
| for ( p = 8; p <= 15; p++) { | |
| if (RTK_PORTMASK_IS_PORT_SET(portmask, p)) { | |
| port_count++; | |
| } | |
| } | |
| is_protected = (port_count < 4); // Arbitrary threshold for "protected" | |
| } | |
| char portJson[64]; | |
| sprintf(portJson, "{\"port\":%d,\"protected\":%s}", web_port, is_protected ? "true" : "false"); | |
| strcat(json, portJson); | |
| } | |
| strcat(json, "]"); | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_protected_port_post_request(int client_socket, const char *body) | |
| { | |
| uint32 unit = 0; | |
| int protectedPorts[8]; // Max 8 user ports supported | |
| int port = -1; | |
| bool is_protected = false; | |
| // Check if protectedPorts array is provided (batch mode) | |
| int count = extract_json_array_of_ints(body, "protectedPorts", protectedPorts, 8); | |
| if (count > 0) | |
| { | |
| printf("Setting protected mode for ports: "); | |
| for (i = 0; i < count; ++i) { | |
| printf("%d ", protectedPorts[i]); | |
| } | |
| printf("\n"); | |
| for (i = 0; i < count; ++i) | |
| { | |
| int web_port = protectedPorts[i]; | |
| if (!is_valid_port(web_port, 1, 8)) | |
| continue; | |
| int rtk_port = web_port + 7; | |
| // Configure isolation mask (allow only self and CPU) | |
| rtk_portmask_t portmask; | |
| RTK_PORTMASK_RESET(portmask); | |
| RTK_PORTMASK_PORT_SET(portmask, rtk_port); // Allow to/from self | |
| RTK_PORTMASK_PORT_SET(portmask, 28); // Allow to/from CPU port | |
| int ret = rtk_port_isolation_set(unit, rtk_port, &portmask); | |
| if (ret != RT_ERR_OK) { | |
| send_error_response(client_socket, 500, "Failed to set protected port isolation"); | |
| return; | |
| } | |
| } | |
| send_success_response(client_socket, "{\"message\":\"Protected ports configured successfully\"}"); | |
| return; | |
| } | |
| // Fallback: single-port format {"port": X, "protected": true/false} | |
| if (extract_json_int(body, "port", &port) == 0 && | |
| extract_json_bool(body, "protected", &is_protected) == 0) | |
| { | |
| if (!is_valid_port(port, 1, 8)) { | |
| send_error_response(client_socket, 400, "Invalid port number"); | |
| return; | |
| } | |
| int rtk_port = port + 7; | |
| rtk_portmask_t portmask; | |
| RTK_PORTMASK_RESET(portmask); | |
| if (is_protected) { | |
| RTK_PORTMASK_PORT_SET(portmask, rtk_port); | |
| RTK_PORTMASK_PORT_SET(portmask, 28); // CPU port | |
| } else { | |
| // Allow communication with all user ports and CPU | |
| for (p = 8; p <= 15; p++) { | |
| RTK_PORTMASK_PORT_SET(portmask, p); | |
| } | |
| RTK_PORTMASK_PORT_SET(portmask, 28); // CPU port | |
| } | |
| int ret = rtk_port_isolation_set(unit, rtk_port, &portmask); | |
| if (ret != RT_ERR_OK) { | |
| send_error_response(client_socket, 500, "Failed to set port isolation"); | |
| return; | |
| } | |
| char json[128]; | |
| snprintf(json, sizeof(json), | |
| "{\"message\":\"Port %d protection %s successfully\"}", | |
| port, is_protected ? "enabled" : "disabled"); | |
| send_success_response(client_socket, json); | |
| return; | |
| } | |
| send_error_response(client_socket, 400, "Invalid request format"); | |
| } | |
| // Jumbo frame handlers using RTK APIs | |
| void handle_jumbo_frame_get_request(int client_socket) | |
| { | |
| uint32 unit = 0; | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| uint32 frameSize = 6000; | |
| rtk_switch_maxPktLenLinkSpeed_get(unit, MAXPKTLEN_LINK_SPEED_GE, &frameSize); | |
| char json[128]; | |
| sprintf(json, "{\"frameSize\":%d}", frameSize); | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_jumbo_frame_post_request(int client_socket, const char *body) | |
| { | |
| uint32 unit = 0; | |
| int frameSize = 0; | |
| // Extract frame size using helper function | |
| if (extract_json_int(body, "frameSize", &frameSize) != 0) { | |
| send_error_response(client_socket, 400, "Frame size is required"); | |
| return; | |
| } | |
| // Validate frame size | |
| if (frameSize < 1522 || frameSize > 16283) { | |
| send_error_response(client_socket, 400, "Invalid frame size. Must be between 1522 and 16283"); | |
| return; | |
| } | |
| // Set jumbo frame size using RTK API for all speeds | |
| int ret1 = rtk_switch_maxPktLenLinkSpeed_set(unit, MAXPKTLEN_LINK_SPEED_FE, frameSize); | |
| int ret2 = rtk_switch_maxPktLenLinkSpeed_set(unit, MAXPKTLEN_LINK_SPEED_GE, frameSize); | |
| if (ret1 != RT_ERR_OK || ret2 != RT_ERR_OK) { | |
| send_error_response(client_socket, 500, "Failed to set jumbo frame size"); | |
| return; | |
| } | |
| char json[128] = "{\"message\":\"Jumbo Frame size updated successfully.\"}"; | |
| send_success_response(client_socket, json); | |
| } | |
| // Reboot and factory reset handlers | |
| void handle_reboot_request(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[128] = "{\"message\":\"Switch is rebooting...\"}"; | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| // Actually reboot the system | |
| system("reboot"); | |
| } | |
| void handle_factory_reset_request(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[128] = "{\"message\":\"Factory reset in progress...\"}"; | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| // ACL and QoS handlers | |
| void handle_acl_group_get_request(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[256] = "{\"aclGroupNum\":[\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\",\"10\"]}"; | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_extended_port_options_get_request(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[512] = "{" | |
| "\"protocolType\":[\"tcp\",\"ip\",\"udp\"]," | |
| "\"sourcePort\":[\"ftp(tcp)\",\"ftp-data(tcp)\",\"po3(tcp)\",\"smtp(tcp)\",\"telnet(tcp)\",\"www(tcp)\",\"rip(udp)\",\"snmp(udp)\",\"snmp-trap(udp)\",\"tftp(udp)\"]," | |
| "\"destinationPort\":[\"ftp(tcp)\",\"ftp-data(tcp)\",\"po3(tcp)\",\"smtp(tcp)\",\"telnet(tcp)\",\"www(tcp)\",\"rip(udp)\",\"snmp(udp)\",\"snmp-trap(udp)\",\"tftp(udp)\"]}"; | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_get_acl_standard_ip_request(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[2048] = "["; // Start JSON array | |
| bool first = true; | |
| // Iterate through stored ACL entries | |
| for (i=0; i < g_data.acl_entry_count && i < MAX_ACL_ENTRIES; i++) { | |
| if (g_data.acl_entries[i].active && strcmp(g_data.acl_entries[i].type, "standard-ip") == 0) { | |
| if (!first) { | |
| strcat(json, ","); | |
| } | |
| char entry[256]; | |
| sprintf(entry, | |
| "{\"groupNum\":\"%d\",\"sourceIp\":\"%s\",\"sourceWildcard\":\"%s\",\"action\":\"%s\"}", | |
| g_data.acl_entries[i].group_num, | |
| g_data.acl_entries[i].source_ip, | |
| g_data.acl_entries[i].source_wildcard, | |
| g_data.acl_entries[i].action | |
| ); | |
| strcat(json, entry); | |
| first = false; | |
| } | |
| } | |
| strcat(json, "]"); // End JSON array | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_get_extended_ip_request(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[2048] = "["; // Start JSON array | |
| bool first = true; | |
| // Iterate through stored ACL entries | |
| for (i=0; i < g_data.acl_entry_count && i < MAX_ACL_ENTRIES; i++) { | |
| if (g_data.acl_entries[i].active && strcmp(g_data.acl_entries[i].type, "extended-ip") == 0) { | |
| if (!first) { | |
| strcat(json, ","); | |
| } | |
| char entry[300]; | |
| sprintf(entry, | |
| "{\"groupNum\":\"%d\",\"protocolType\":\"%s\",\"sourceIp\":\"%s\",\"destinationIp\":\"%s\",\"action\":\"%s\"}", | |
| g_data.acl_entries[i].group_num, | |
| g_data.acl_entries[i].protocol_type, | |
| g_data.acl_entries[i].source_ip, | |
| g_data.acl_entries[i].destination_ip, | |
| g_data.acl_entries[i].action | |
| ); | |
| strcat(json, entry); | |
| first = false; | |
| } | |
| } | |
| strcat(json, "]"); // End JSON array | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_get_acl_mac_ip_request(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[2048] = "["; // Start JSON array | |
| bool first = true; | |
| // Iterate through stored ACL entries | |
| for (i=0; i < g_data.acl_entry_count && i < MAX_ACL_ENTRIES; i++) { | |
| if (g_data.acl_entries[i].active && strcmp(g_data.acl_entries[i].type, "mac-ip") == 0) { | |
| if (!first) { | |
| strcat(json, ","); | |
| } | |
| char entry[300]; | |
| sprintf(entry, | |
| "{\"groupNumber\":\"%d\",\"sourceMac\":\"%s\",\"destinationIp\":\"%s\",\"rule\":\"%s\"}", | |
| g_data.acl_entries[i].group_num, | |
| g_data.acl_entries[i].source_mac, | |
| g_data.acl_entries[i].destination_ip, | |
| g_data.acl_entries[i].action | |
| ); | |
| strcat(json, entry); | |
| first = false; | |
| } | |
| } | |
| strcat(json, "]"); // End JSON array | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_get_acl_mac_arp_request(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[2048] = "["; // Start JSON array | |
| bool first = true; | |
| // Iterate through stored ACL entries | |
| for (i=0; i < g_data.acl_entry_count && i < MAX_ACL_ENTRIES; i++) { | |
| if (g_data.acl_entries[i].active && strcmp(g_data.acl_entries[i].type, "mac-arp") == 0) { | |
| if (!first) { | |
| strcat(json, ","); | |
| } | |
| char entry[300]; | |
| sprintf(entry, | |
| "{\"groupNum\":\"%d\",\"srcMac\":\"%s\",\"srcIp\":\"%s\",\"rule\":\"%s\"}", | |
| g_data.acl_entries[i].group_num, | |
| g_data.acl_entries[i].source_mac, | |
| g_data.acl_entries[i].source_ip, | |
| g_data.acl_entries[i].action | |
| ); | |
| strcat(json, entry); | |
| first = false; | |
| } | |
| } | |
| strcat(json, "]"); // End JSON array | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| void handle_get_qos_apply_request(int client_socket) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char json[64] = "[]"; // Empty array - no QoS entries by default | |
| strcat(response, json); | |
| send_response(client_socket, response); | |
| } | |
| // POST handlers for adding ACL and QoS entries | |
| void handle_add_acl_standard_ip_request(int client_socket, const char *body) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char groupNum[8], sourceIp[16], sourceWildcard[16], action[16]; | |
| if (sscanf(body, "{\"groupNum\":\"%7[^\"]\",\"sourceIp\":\"%15[^\"]\",\"sourceWildcard\":\"%15[^\"]\",\"action\":\"%15[^\"]\"}", | |
| groupNum, sourceIp, sourceWildcard, action) >= 3) | |
| { | |
| // Validate inputs | |
| int group = atoi(groupNum); | |
| if (group < 1 || group > 99) { | |
| send_error_response(client_socket, 400, "Group number must be between 1 and 99"); | |
| return; | |
| } | |
| if (!is_valid_ip(sourceIp)) { | |
| send_error_response(client_socket, 400, "Invalid source IP address"); | |
| return; | |
| } | |
| if (strcmp(action, "permit") != 0 && strcmp(action, "deny") != 0) { | |
| send_error_response(client_socket, 400, "Action must be 'permit' or 'deny'"); | |
| return; | |
| } | |
| // Check if we have space for more ACL entries | |
| if (g_data.acl_entry_count >= MAX_ACL_ENTRIES) { | |
| send_error_response(client_socket, 400, "Maximum ACL entries reached"); | |
| return; | |
| } | |
| // Store ACL entry in global data structure | |
| int idx = g_data.acl_entry_count; | |
| g_data.acl_entries[idx].group_num = group; | |
| strcpy(g_data.acl_entries[idx].type, "standard-ip"); | |
| strcpy(g_data.acl_entries[idx].source_ip, sourceIp); | |
| strcpy(g_data.acl_entries[idx].source_wildcard, sourceWildcard); | |
| strcpy(g_data.acl_entries[idx].action, action); | |
| g_data.acl_entries[idx].active = true; | |
| // Apply to hardware using RTK SDK | |
| if (apply_acl_rule_to_hardware(&g_data.acl_entries[idx]) == 0) { | |
| g_data.acl_entry_count++; | |
| char json[128]; | |
| sprintf(json, "{\"message\":\"ACL standard IP rule added successfully\",\"data\":{\"groupNum\":\"%s\"}}", groupNum); | |
| strcat(response, json); | |
| } else { | |
| send_error_response(client_socket, 500, "Failed to apply ACL rule to hardware"); | |
| return; | |
| } | |
| } | |
| else | |
| { | |
| send_error_response(client_socket, 400, "Missing required fields"); | |
| return; | |
| } | |
| send_response(client_socket, response); | |
| } | |
| void handle_add_acl_extended_ip_request(int client_socket, const char *body) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char groupNum[8], protocolType[16], sourceIp[16], destinationIp[16], action[16]; | |
| if (sscanf(body, "{\"groupNum\":\"%7[^\"]\",\"protocolType\":\"%15[^\"]\",\"sourceIp\":\"%15[^\"]\",\"destinationIp\":\"%15[^\"]\",\"action\":\"%15[^\"]\"}", | |
| groupNum, protocolType, sourceIp, destinationIp, action) >= 4) | |
| { | |
| // Validate inputs | |
| int group = atoi(groupNum); | |
| if (group < 1 || group > 99) { | |
| send_error_response(client_socket, 400, "Group number must be between 1 and 99"); | |
| return; | |
| } | |
| if (!is_valid_ip(sourceIp) || !is_valid_ip(destinationIp)) { | |
| send_error_response(client_socket, 400, "Invalid IP address"); | |
| return; | |
| } | |
| if (strcmp(action, "permit") != 0 && strcmp(action, "deny") != 0) { | |
| send_error_response(client_socket, 400, "Action must be 'permit' or 'deny'"); | |
| return; | |
| } | |
| // Check if we have space for more ACL entries | |
| if (g_data.acl_entry_count >= MAX_ACL_ENTRIES) { | |
| send_error_response(client_socket, 400, "Maximum ACL entries reached"); | |
| return; | |
| } | |
| // Store ACL entry in global data structure | |
| int idx = g_data.acl_entry_count; | |
| g_data.acl_entries[idx].group_num = group; | |
| strcpy(g_data.acl_entries[idx].type, "extended-ip"); | |
| strcpy(g_data.acl_entries[idx].source_ip, sourceIp); | |
| strcpy(g_data.acl_entries[idx].destination_ip, destinationIp); | |
| strcpy(g_data.acl_entries[idx].protocol_type, protocolType); | |
| strcpy(g_data.acl_entries[idx].action, action); | |
| g_data.acl_entries[idx].active = true; | |
| // Apply to hardware using RTK SDK | |
| if (apply_acl_rule_to_hardware(&g_data.acl_entries[idx]) == 0) { | |
| g_data.acl_entry_count++; | |
| char json[128]; | |
| sprintf(json, "{\"message\":\"Extended ACL rule added successfully\",\"data\":{\"groupNum\":\"%s\"}}", groupNum); | |
| strcat(response, json); | |
| } else { | |
| send_error_response(client_socket, 500, "Failed to apply ACL rule to hardware"); | |
| return; | |
| } | |
| } | |
| else | |
| { | |
| send_error_response(client_socket, 400, "Missing required fields"); | |
| return; | |
| } | |
| send_response(client_socket, response); | |
| } | |
| void handle_add_acl_mac_ip_request(int client_socket, const char *body) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char groupNumber[8], sourceMac[32], destinationIp[16], rule[16]; | |
| if (sscanf(body, "{\"groupNumber\":\"%7[^\"]\",\"sourceMac\":\"%31[^\"]\",\"destinationIp\":\"%15[^\"]\",\"rule\":\"%15[^\"]\"}", | |
| groupNumber, sourceMac, destinationIp, rule) >= 3) | |
| { | |
| // Validate inputs | |
| int group = atoi(groupNumber); | |
| if (group < 1 || group > 99) { | |
| send_error_response(client_socket, 400, "Group number must be between 1 and 99"); | |
| return; | |
| } | |
| if (!is_valid_mac(sourceMac)) { | |
| send_error_response(client_socket, 400, "Invalid MAC address"); | |
| return; | |
| } | |
| if (!is_valid_ip(destinationIp)) { | |
| send_error_response(client_socket, 400, "Invalid destination IP address"); | |
| return; | |
| } | |
| if (strcmp(rule, "permit") != 0 && strcmp(rule, "deny") != 0) { | |
| send_error_response(client_socket, 400, "Rule must be 'permit' or 'deny'"); | |
| return; | |
| } | |
| // Check if we have space for more ACL entries | |
| if (g_data.acl_entry_count >= MAX_ACL_ENTRIES) { | |
| send_error_response(client_socket, 400, "Maximum ACL entries reached"); | |
| return; | |
| } | |
| // Store ACL entry in global data structure | |
| int idx = g_data.acl_entry_count; | |
| g_data.acl_entries[idx].group_num = group; | |
| strcpy(g_data.acl_entries[idx].type, "mac-ip"); | |
| strcpy(g_data.acl_entries[idx].source_mac, sourceMac); | |
| strcpy(g_data.acl_entries[idx].destination_ip, destinationIp); | |
| strcpy(g_data.acl_entries[idx].action, rule); | |
| g_data.acl_entries[idx].active = true; | |
| // Apply to hardware using RTK SDK | |
| if (apply_acl_rule_to_hardware(&g_data.acl_entries[idx]) == 0) { | |
| g_data.acl_entry_count++; | |
| char json[128] = "{\"message\":\"ACL MAC IP rule added successfully.\"}"; | |
| strcat(response, json); | |
| } else { | |
| send_error_response(client_socket, 500, "Failed to apply ACL rule to hardware"); | |
| return; | |
| } | |
| } | |
| else | |
| { | |
| send_error_response(client_socket, 400, "Missing required fields"); | |
| return; | |
| } | |
| send_response(client_socket, response); | |
| } | |
| void handle_add_acl_mac_arp_request(int client_socket, const char *body) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| char groupNum[8], srcMac[32], srcIp[16], rule[16]; | |
| if (sscanf(body, "{\"groupNum\":\"%7[^\"]\",\"srcMac\":\"%31[^\"]\",\"srcIp\":\"%15[^\"]\",\"rule\":\"%15[^\"]\"}", | |
| groupNum, srcMac, srcIp, rule) >= 3) | |
| { | |
| // Validate inputs | |
| int group = atoi(groupNum); | |
| if (group < 1 || group > 99) { | |
| send_error_response(client_socket, 400, "Group number must be between 1 and 99"); | |
| return; | |
| } | |
| if (!is_valid_mac(srcMac)) { | |
| send_error_response(client_socket, 400, "Invalid MAC address"); | |
| return; | |
| } | |
| if (!is_valid_ip(srcIp)) { | |
| send_error_response(client_socket, 400, "Invalid source IP address"); | |
| return; | |
| } | |
| if (strcmp(rule, "permit") != 0 && strcmp(rule, "deny") != 0) { | |
| send_error_response(client_socket, 400, "Rule must be 'permit' or 'deny'"); | |
| return; | |
| } | |
| // Check if we have space for more ACL entries | |
| if (g_data.acl_entry_count >= MAX_ACL_ENTRIES) { | |
| send_error_response(client_socket, 400, "Maximum ACL entries reached"); | |
| return; | |
| } | |
| // Store ACL entry in global data structure | |
| int idx = g_data.acl_entry_count; | |
| g_data.acl_entries[idx].group_num = group; | |
| strcpy(g_data.acl_entries[idx].type, "mac-arp"); | |
| strcpy(g_data.acl_entries[idx].source_mac, srcMac); | |
| strcpy(g_data.acl_entries[idx].source_ip, srcIp); | |
| strcpy(g_data.acl_entries[idx].action, rule); | |
| g_data.acl_entries[idx].active = true; | |
| // Apply to hardware using RTK SDK | |
| if (apply_acl_rule_to_hardware(&g_data.acl_entries[idx]) == 0) { | |
| g_data.acl_entry_count++; | |
| char json[128] = "{\"message\":\"MAC ARP ACL rule added successfully\"}"; | |
| strcat(response, json); | |
| } else { | |
| send_error_response(client_socket, 500, "Failed to apply ACL rule to hardware"); | |
| return; | |
| } | |
| } | |
| else | |
| { | |
| send_error_response(client_socket, 400, "Missing required fields"); | |
| return; | |
| } | |
| send_response(client_socket, response); | |
| } | |
| void handle_qos_apply_request(int client_socket, const char *body) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| int port, userPriority; | |
| char qosType[16]; | |
| if (sscanf(body, "{\"port\":%d,\"qosType\":\"%15[^\"]\",\"userPriority\":%d}", | |
| &port, qosType, &userPriority) == 3) | |
| { | |
| char json[128] = "{\"message\":\"QOS Applied Successfully!\"}"; | |
| strcat(response, json); | |
| } | |
| else | |
| { | |
| strcpy(response, "HTTP/1.1 400 Bad Request\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n" | |
| "{\"message\":\"All fields are required.\"}"); | |
| } | |
| send_response(client_socket, response); | |
| } | |
| void handle_add_qos_schedule_request(int client_socket, const char *body) | |
| { | |
| char response[BUFFER_SIZE] = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n"; | |
| int port; | |
| char qosType[16]; | |
| if (sscanf(body, "{\"port\":%d,\"qosType\":\"%15[^\"]\"}", &port, qosType) >= 2) | |
| { | |
| char json[128] = "{\"message\":\"QoS schedule applied successfully!\"}"; | |
| strcat(response, json); | |
| } | |
| else | |
| { | |
| strcpy(response, "HTTP/1.1 400 Bad Request\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n" | |
| "{\"error\":\"All fields must be filled with valid values\"}"); | |
| } | |
| send_response(client_socket, response); | |
| } | |
| // VLAN Management Functions | |
| void handle_get_vlan_members_request(int client_socket) | |
| { | |
| uint32 unit = 0; | |
| char response[BUFFER_SIZE * 4]; | |
| strcpy(response, | |
| "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n" | |
| "{ \"vlans\": ["); | |
| bool first = true; | |
| int vid; | |
| // Get VLAN information using RTK APIs | |
| for (vid = 2; vid <= 10; ++vid) | |
| { | |
| rtk_portmask_t memberPorts, untagPorts; | |
| // Check if VLAN exists and get member ports | |
| int ret = rtk_vlan_port_get(unit, vid, &memberPorts, &untagPorts); | |
| if (ret == RT_ERR_OK) { | |
| if (!first) strcat(response, ","); | |
| first = false; | |
| // Convert portmask to string representation | |
| char port_members[64] = ""; | |
| int port; | |
| for (port = 8; port <= 15; port++) { | |
| if (RTK_PORTMASK_IS_PORT_SET(memberPorts, port)) { | |
| char port_str[16]; | |
| bool is_untagged = RTK_PORTMASK_IS_PORT_SET(untagPorts, port); | |
| snprintf(port_str, sizeof(port_str), "%s[%s]ge1/%d", | |
| strlen(port_members) > 0 ? " " : "", | |
| is_untagged ? "u" : "t", | |
| port - 7); | |
| strcat(port_members, port_str); | |
| } | |
| } | |
| char vlan_entry[256]; | |
| sprintf(vlan_entry, | |
| "{\"vid\":%d,\"vlan_name\":\"vlan%d\",\"State\":\"active [S]\",\"Port_member\":\"%s\"}", | |
| vid, vid, port_members); | |
| strcat(response, vlan_entry); | |
| } | |
| } | |
| strcat(response, "] }"); | |
| send_response(client_socket, response); | |
| } | |
| void handle_set_vlan_members_request(int client_socket, const char *body) | |
| { | |
| uint32 unit = 0; | |
| int vid = 2; | |
| char port_members[256] = ""; | |
| // Extract VLAN ID and port members | |
| extract_json_int(body, "vid", &vid); | |
| extract_json_string(body, "port_members", port_members, sizeof(port_members)); | |
| if (vid < 1 || vid > 4094) { | |
| send_error_response(client_socket, 400, "Invalid VLAN ID"); | |
| return; | |
| } | |
| printf("Setting VLAN %d members using RTK API\n", vid); | |
| // Parse port members and set using RTK API | |
| rtk_portmask_t memberPorts, untagPorts; | |
| RTK_PORTMASK_RESET(memberPorts); | |
| RTK_PORTMASK_RESET(untagPorts); | |
| // Add default ports (example: add ports 8-9 as members) | |
| RTK_PORTMASK_PORT_SET(memberPorts, 8); | |
| RTK_PORTMASK_PORT_SET(memberPorts, 9); | |
| RTK_PORTMASK_PORT_SET(untagPorts, 8); | |
| RTK_PORTMASK_PORT_SET(untagPorts, 9); | |
| int ret = rtk_vlan_port_set(unit, vid, &memberPorts, &untagPorts); | |
| if (ret != RT_ERR_OK) { | |
| send_error_response(client_socket, 500, "Failed to set VLAN members"); | |
| return; | |
| } | |
| char json[256]; | |
| sprintf(json, "{\"vid\":%d,\"status\":\"success\",\"message\":\"VLAN members updated\"}", vid); | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_vlan_port_tag_action_request(int client_socket, const char *body) | |
| { | |
| char response[BUFFER_SIZE]; | |
| int vid = 2, port = 1; | |
| char action[16] = "tag", mode[16] = "access"; | |
| // Extract parameters from body | |
| if (strstr(body, "\"vid\"")) | |
| sscanf(body, "{\"vid\":%d", &vid); | |
| if (strstr(body, "\"port\"")) | |
| sscanf(body, "{\"port\":%d", &port); | |
| printf("VLAN port action: VID=%d, Port=%d, Action=%s, Mode=%s\n", vid, port, action, mode); | |
| snprintf(response, sizeof(response), | |
| "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n" | |
| "{\"vid\": %d, \"port\": %d, \"action\": \"%s\", \"mode\": \"%s\", \"status\": \"success\"}", | |
| vid, port, action, mode); | |
| send_response(client_socket, response); | |
| } | |
| void handle_create_vlan_request(int client_socket, const char *body) | |
| { | |
| uint32 unit = 0; | |
| int vid = 2; | |
| if (extract_json_int(body, "vid", &vid) != 0) { | |
| send_error_response(client_socket, 400, "VLAN ID is required"); | |
| return; | |
| } | |
| // Validate VLAN ID range | |
| if (vid < 1 || vid > 4094) { | |
| send_error_response(client_socket, 400, "VLAN ID must be between 1 and 4094"); | |
| return; | |
| } | |
| printf("Creating VLAN %d using RTK API\n", vid); | |
| // Create VLAN using RTK API | |
| int ret = rtk_vlan_create(unit, vid); | |
| if (ret != RT_ERR_OK) { | |
| char error_msg[128]; | |
| snprintf(error_msg, sizeof(error_msg), "Failed to create VLAN %d (RTK error: %d)", vid, ret); | |
| send_error_response(client_socket, 500, error_msg); | |
| return; | |
| } | |
| char json[128]; | |
| snprintf(json, sizeof(json), "{\"vid\": %d, \"status\": \"success\", \"message\": \"VLAN created successfully\"}", vid); | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_destroy_vlan_request(int client_socket, const char *body) | |
| { | |
| uint32 unit = 0; | |
| int vid = 2; | |
| if (extract_json_int(body, "vid", &vid) != 0) { | |
| send_error_response(client_socket, 400, "VLAN ID is required"); | |
| return; | |
| } | |
| // Validate VLAN ID range | |
| if (vid < 1 || vid > 4094) { | |
| send_error_response(client_socket, 400, "VLAN ID must be between 1 and 4094"); | |
| return; | |
| } | |
| printf("Destroying VLAN %d using RTK API\n", vid); | |
| // Destroy VLAN using RTK API | |
| int ret = rtk_vlan_destroy(unit, vid); | |
| if (ret != RT_ERR_OK) { | |
| char error_msg[128]; | |
| snprintf(error_msg, sizeof(error_msg), "Failed to destroy VLAN %d (RTK error: %d)", vid, ret); | |
| send_error_response(client_socket, 500, error_msg); | |
| return; | |
| } | |
| char json[128]; | |
| snprintf(json, sizeof(json), "{\"vid\": %d, \"status\": \"success\", \"message\": \"VLAN destroyed successfully\"}", vid); | |
| send_success_response(client_socket, json); | |
| } | |
| void print_all_vlan_interfaces_json(int client_socket) | |
| { | |
| char response[BUFFER_SIZE * 4]; | |
| int offset = 0, vid; | |
| offset += snprintf(response + offset, sizeof(response) - offset, | |
| "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n" | |
| "{ \"vlan_interfaces\": ["); | |
| bool first = true; | |
| for (vid = 2; vid <= 10; ++vid) | |
| { | |
| if (!first) | |
| offset += snprintf(response + offset, sizeof(response) - offset, ","); | |
| first = false; | |
| offset += snprintf(response + offset, sizeof(response) - offset, | |
| "{ \"vlanId\": %d, \"ipAddress\": \"192.168.%d.1/24\", \"dhcpEnabled\": 0, \"macAddress\": \"02:00:00:00:%02x:%02x\" }", | |
| vid, vid, (vid >> 8) & 0xFF, vid & 0xFF); | |
| } | |
| offset += snprintf(response + offset, sizeof(response) - offset, "] }"); | |
| send_response(client_socket, response); | |
| } | |
| void handle_create_vlan_interface_request(int client_socket, const char *body) | |
| { | |
| uint32 unit = 0; | |
| int vid = 2, dhcp_client = 0; | |
| char ipv4[32] = "192.168.2.1/24"; | |
| char parent_interface[16] = "eth0"; | |
| char vlan_interface[32]; | |
| char command[256]; | |
| // Extract parameters using helper functions | |
| if (extract_json_int(body, "vid", &vid) != 0) { | |
| send_error_response(client_socket, 400, "VLAN ID is required"); | |
| return; | |
| } | |
| extract_json_int(body, "dhcp_client", &dhcp_client); | |
| extract_json_string(body, "ipv4", ipv4, sizeof(ipv4)); | |
| extract_json_string(body, "parent_interface", parent_interface, sizeof(parent_interface)); | |
| // Validate VLAN ID | |
| if (vid < 1 || vid > 4094) { | |
| send_error_response(client_socket, 400, "Invalid VLAN ID (1-4094)"); | |
| return; | |
| } | |
| // Validate IP address format if provided | |
| if (strlen(ipv4) > 0) { | |
| char ipv4_copy[32]; | |
| strcpy(ipv4_copy, ipv4); | |
| char *ip_part = strtok(ipv4_copy, "/"); | |
| if (!is_valid_ip(ip_part)) { | |
| send_error_response(client_socket, 400, "Invalid IP address format"); | |
| return; | |
| } | |
| } | |
| printf("Creating VLAN interface %d with IP %s, DHCP: %d\n", vid, ipv4, dhcp_client); | |
| // Create VLAN interface name | |
| snprintf(vlan_interface, sizeof(vlan_interface), "%s.%d", parent_interface, vid); | |
| // Step 1: Create VLAN interface using ip link | |
| snprintf(command, sizeof(command), | |
| "ip link add link %s name %s type vlan id %d", | |
| parent_interface, vlan_interface, vid); | |
| int ret = system(command); | |
| if (ret != 0) { | |
| printf("Failed to create VLAN interface: %s\n", command); | |
| send_error_response(client_socket, 500, "Failed to create VLAN interface"); | |
| return; | |
| } | |
| // Step 2: Bring the VLAN interface up | |
| snprintf(command, sizeof(command), "ip link set dev %s up", vlan_interface); | |
| ret = system(command); | |
| if (ret != 0) { | |
| printf("Warning: Failed to bring VLAN interface up: %s\n", command); | |
| } | |
| // Step 3: Configure IP address if not using DHCP | |
| if (!dhcp_client && strlen(ipv4) > 0) { | |
| snprintf(command, sizeof(command), | |
| "ip addr add %s dev %s", ipv4, vlan_interface); | |
| ret = system(command); | |
| if (ret != 0) { | |
| printf("Warning: Failed to set IP address: %s\n", command); | |
| } | |
| } else if (dhcp_client) { | |
| // Step 4: Start DHCP client for the interface (optional) | |
| snprintf(command, sizeof(command), "dhclient %s &", vlan_interface); | |
| ret = system(command); | |
| if (ret != 0) { | |
| printf("Warning: Failed to start DHCP client: %s\n", command); | |
| } | |
| } | |
| // Also create VLAN in RTK SDK for hardware switching | |
| int rtk_ret = rtk_vlan_create(unit, vid); | |
| if (rtk_ret != RT_ERR_OK && rtk_ret != RT_ERR_VLAN_EXIST) { | |
| printf("Warning: Failed to create VLAN in RTK SDK (ret=%d)\n", rtk_ret); | |
| } else { | |
| // Set default VLAN port membership | |
| rtk_portmask_t memberPorts, untagPorts; | |
| RTK_PORTMASK_RESET(memberPorts); | |
| RTK_PORTMASK_RESET(untagPorts); | |
| // Add CPU port and default ports as members | |
| RTK_PORTMASK_PORT_SET(memberPorts, 8); | |
| RTK_PORTMASK_PORT_SET(untagPorts, 8); | |
| rtk_ret = rtk_vlan_port_set(unit, vid, &memberPorts, &untagPorts); | |
| if (rtk_ret != RT_ERR_OK) { | |
| printf("Warning: Failed to set VLAN port membership (ret=%d)\n", rtk_ret); | |
| } | |
| } | |
| char json[256]; | |
| snprintf(json, sizeof(json), | |
| "{\"status\": \"success\", \"message\": \"VLAN interface %s created with IP %s\", \"interface\": \"%s\", \"vid\": %d}", | |
| vlan_interface, dhcp_client ? "DHCP" : ipv4, vlan_interface, vid); | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_destroy_vlan_interface_request(int client_socket, const char *body) | |
| { | |
| uint32 unit = 0; | |
| int vid = 2; | |
| char parent_interface[16] = "eth0"; | |
| char vlan_interface[32]; | |
| char command[256]; | |
| // Extract parameters using helper functions | |
| if (extract_json_int(body, "vid", &vid) != 0) { | |
| send_error_response(client_socket, 400, "VLAN ID is required"); | |
| return; | |
| } | |
| extract_json_string(body, "parent_interface", parent_interface, sizeof(parent_interface)); | |
| // Validate VLAN ID | |
| if (vid < 1 || vid > 4094) { | |
| send_error_response(client_socket, 400, "Invalid VLAN ID (1-4094)"); | |
| return; | |
| } | |
| printf("Destroying VLAN interface %d\n", vid); | |
| // Create VLAN interface name | |
| snprintf(vlan_interface, sizeof(vlan_interface), "%s.%d", parent_interface, vid); | |
| // Step 1: Stop DHCP client if running | |
| snprintf(command, sizeof(command), "pkill -f \"dhclient %s\"", vlan_interface); | |
| system(command); // Don't check return code as process might not exist | |
| // Step 2: Remove IP addresses from interface | |
| snprintf(command, sizeof(command), "ip addr flush dev %s", vlan_interface); | |
| int ret = system(command); | |
| if (ret != 0) { | |
| printf("Warning: Failed to flush IP addresses: %s\n", command); | |
| } | |
| // Step 3: Bring the interface down | |
| snprintf(command, sizeof(command), "ip link set dev %s down", vlan_interface); | |
| ret = system(command); | |
| if (ret != 0) { | |
| printf("Warning: Failed to bring VLAN interface down: %s\n", command); | |
| } | |
| // Step 4: Delete the VLAN interface | |
| snprintf(command, sizeof(command), "ip link delete %s", vlan_interface); | |
| ret = system(command); | |
| if (ret != 0) { | |
| printf("Failed to delete VLAN interface: %s\n", command); | |
| send_error_response(client_socket, 500, "Failed to delete VLAN interface"); | |
| return; | |
| } | |
| // Also destroy VLAN in RTK SDK | |
| int rtk_ret = rtk_vlan_destroy(unit, vid); | |
| if (rtk_ret != RT_ERR_OK) { | |
| printf("Warning: Failed to destroy VLAN in RTK SDK (ret=%d)\n", rtk_ret); | |
| } | |
| char json[128]; | |
| snprintf(json, sizeof(json), | |
| "{\"status\": \"success\", \"message\": \"VLAN interface %s deleted successfully\"}", | |
| vlan_interface); | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_get_vlan_ip_request(int client_socket, const char *body) | |
| { | |
| int vid = 2; | |
| char parent_interface[16] = "eth0"; | |
| char vlan_interface[32]; | |
| char command[256]; | |
| char ip_address[64] = "Not configured"; | |
| char mac_address[32] = "Unknown"; | |
| // Extract parameters using helper functions | |
| if (extract_json_int(body, "vid", &vid) != 0) { | |
| send_error_response(client_socket, 400, "VLAN ID is required"); | |
| return; | |
| } | |
| extract_json_string(body, "parent_interface", parent_interface, sizeof(parent_interface)); | |
| // Validate VLAN ID | |
| if (vid < 1 || vid > 4094) { | |
| send_error_response(client_socket, 400, "Invalid VLAN ID (1-4094)"); | |
| return; | |
| } | |
| printf("Getting IP information for VLAN interface %d\n", vid); | |
| // Create VLAN interface name | |
| snprintf(vlan_interface, sizeof(vlan_interface), "%s.%d", parent_interface, vid); | |
| // Get IP address using ip addr show command | |
| snprintf(command, sizeof(command), | |
| "ip addr show %s | grep 'inet ' | awk '{print $2}' | head -1", | |
| vlan_interface); | |
| FILE *fp = popen(command, "r"); | |
| if (fp != NULL) { | |
| if (fgets(ip_address, sizeof(ip_address), fp) != NULL) { | |
| // Remove trailing newline | |
| ip_address[strcspn(ip_address, "\n")] = '\0'; | |
| if (strlen(ip_address) == 0) { | |
| strcpy(ip_address, "Not configured"); | |
| } | |
| } | |
| pclose(fp); | |
| } | |
| // Get MAC address using ip link show command | |
| snprintf(command, sizeof(command), | |
| "ip link show %s | grep 'link/ether' | awk '{print $2}'", | |
| vlan_interface); | |
| fp = popen(command, "r"); | |
| if (fp != NULL) { | |
| if (fgets(mac_address, sizeof(mac_address), fp) != NULL) { | |
| // Remove trailing newline | |
| mac_address[strcspn(mac_address, "\n")] = '\0'; | |
| if (strlen(mac_address) == 0) { | |
| strcpy(mac_address, "Unknown"); | |
| } | |
| } | |
| pclose(fp); | |
| } | |
| // Check if interface exists | |
| snprintf(command, sizeof(command), "ip link show %s >/dev/null 2>&1", vlan_interface); | |
| int interface_exists = (system(command) == 0); | |
| char json[256]; | |
| snprintf(json, sizeof(json), | |
| "{" | |
| "\"vlan_id\": %d, " | |
| "\"interface\": \"%s\", " | |
| "\"ipv4_address\": \"%s\", " | |
| "\"mac_address\": \"%s\", " | |
| "\"interface_exists\": %s" | |
| "}", | |
| vid, vlan_interface, ip_address, mac_address, | |
| interface_exists ? "true" : "false"); | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_delete_vlanip_request(int client_socket, const char *body) | |
| { | |
| int vid = 2; | |
| char parent_interface[16] = "eth0"; | |
| char vlan_interface[32]; | |
| char ip_address[32] = ""; | |
| char command[256]; | |
| // Extract parameters using helper functions | |
| if (extract_json_int(body, "vid", &vid) != 0) { | |
| send_error_response(client_socket, 400, "VLAN ID is required"); | |
| return; | |
| } | |
| extract_json_string(body, "parent_interface", parent_interface, sizeof(parent_interface)); | |
| extract_json_string(body, "ip_address", ip_address, sizeof(ip_address)); | |
| // Validate VLAN ID | |
| if (vid < 1 || vid > 4094) { | |
| send_error_response(client_socket, 400, "Invalid VLAN ID (1-4094)"); | |
| return; | |
| } | |
| printf("Deleting IP from VLAN interface %d\n", vid); | |
| // Create VLAN interface name | |
| snprintf(vlan_interface, sizeof(vlan_interface), "%s.%d", parent_interface, vid); | |
| // Check if interface exists | |
| snprintf(command, sizeof(command), "ip link show %s >/dev/null 2>&1", vlan_interface); | |
| if (system(command) != 0) { | |
| send_error_response(client_socket, 404, "VLAN interface does not exist"); | |
| return; | |
| } | |
| // If specific IP address is provided, delete only that IP | |
| if (strlen(ip_address) > 0) { | |
| // Validate IP address format | |
| char ip_copy[32]; | |
| strcpy(ip_copy, ip_address); | |
| char *ip_part = strtok(ip_copy, "/"); | |
| if (!is_valid_ip(ip_part)) { | |
| send_error_response(client_socket, 400, "Invalid IP address format"); | |
| return; | |
| } | |
| snprintf(command, sizeof(command), "ip addr del %s dev %s", ip_address, vlan_interface); | |
| } else { | |
| // Delete all IP addresses from the interface | |
| snprintf(command, sizeof(command), "ip addr flush dev %s", vlan_interface); | |
| } | |
| int ret = system(command); | |
| if (ret != 0) { | |
| printf("Failed to delete IP from VLAN interface: %s\n", command); | |
| send_error_response(client_socket, 500, "Failed to delete IP address from VLAN interface"); | |
| return; | |
| } | |
| // Also stop DHCP client if it was running | |
| snprintf(command, sizeof(command), "pkill -f \"dhclient %s\"", vlan_interface); | |
| system(command); // Don't check return code as process might not exist | |
| char json[128]; | |
| if (strlen(ip_address) > 0) { | |
| snprintf(json, sizeof(json), | |
| "{\"status\": \"success\", \"message\": \"IP address %s deleted from VLAN interface %s\"}", | |
| ip_address, vlan_interface); | |
| } else { | |
| snprintf(json, sizeof(json), | |
| "{\"status\": \"success\", \"message\": \"All IP addresses deleted from VLAN interface %s\"}", | |
| vlan_interface); | |
| } | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_update_vlanip_request(int client_socket, const char *body) | |
| { | |
| int vid = 2, dhcp_client = 0; | |
| char parent_interface[16] = "eth0"; | |
| char vlan_interface[32]; | |
| char new_ipv4[32] = ""; | |
| char old_ipv4[32] = ""; | |
| char command[256]; | |
| // Extract parameters using helper functions | |
| if (extract_json_int(body, "vid", &vid) != 0) { | |
| send_error_response(client_socket, 400, "VLAN ID is required"); | |
| return; | |
| } | |
| extract_json_string(body, "parent_interface", parent_interface, sizeof(parent_interface)); | |
| extract_json_string(body, "new_ipv4", new_ipv4, sizeof(new_ipv4)); | |
| extract_json_string(body, "old_ipv4", old_ipv4, sizeof(old_ipv4)); | |
| extract_json_int(body, "dhcp_client", &dhcp_client); | |
| // Validate VLAN ID | |
| if (vid < 1 || vid > 4094) { | |
| send_error_response(client_socket, 400, "Invalid VLAN ID (1-4094)"); | |
| return; | |
| } | |
| // Validate new IP address format if provided | |
| if (!dhcp_client && strlen(new_ipv4) > 0) { | |
| char ipv4_copy[32]; | |
| strcpy(ipv4_copy, new_ipv4); | |
| char *ip_part = strtok(ipv4_copy, "/"); | |
| if (!is_valid_ip(ip_part)) { | |
| send_error_response(client_socket, 400, "Invalid new IP address format"); | |
| return; | |
| } | |
| } | |
| printf("Updating VLAN interface %d IP to %s (DHCP: %d)\n", vid, | |
| dhcp_client ? "DHCP" : new_ipv4, dhcp_client); | |
| // Create VLAN interface name | |
| snprintf(vlan_interface, sizeof(vlan_interface), "%s.%d", parent_interface, vid); | |
| // Check if interface exists | |
| snprintf(command, sizeof(command), "ip link show %s >/dev/null 2>&1", vlan_interface); | |
| if (system(command) != 0) { | |
| send_error_response(client_socket, 404, "VLAN interface does not exist"); | |
| return; | |
| } | |
| // Step 1: Stop DHCP client if it was running | |
| snprintf(command, sizeof(command), "pkill -f \"dhclient %s\"", vlan_interface); | |
| system(command); // Don't check return code as process might not exist | |
| // Step 2: Remove old IP address if specified | |
| if (strlen(old_ipv4) > 0) { | |
| snprintf(command, sizeof(command), "ip addr del %s dev %s", old_ipv4, vlan_interface); | |
| int ret = system(command); | |
| if (ret != 0) { | |
| printf("Warning: Failed to remove old IP address: %s\n", command); | |
| } | |
| } else { | |
| // Flush all IP addresses if no specific old IP provided | |
| snprintf(command, sizeof(command), "ip addr flush dev %s", vlan_interface); | |
| int ret = system(command); | |
| if (ret != 0) { | |
| printf("Warning: Failed to flush IP addresses: %s\n", command); | |
| } | |
| } | |
| // Step 3: Configure new IP or start DHCP | |
| if (dhcp_client) { | |
| // Start DHCP client | |
| snprintf(command, sizeof(command), "dhclient %s &", vlan_interface); | |
| int ret = system(command); | |
| if (ret != 0) { | |
| send_error_response(client_socket, 500, "Failed to start DHCP client"); | |
| return; | |
| } | |
| } else if (strlen(new_ipv4) > 0) { | |
| // Set static IP address | |
| snprintf(command, sizeof(command), "ip addr add %s dev %s", new_ipv4, vlan_interface); | |
| int ret = system(command); | |
| if (ret != 0) { | |
| printf("Failed to set new IP address: %s\n", command); | |
| send_error_response(client_socket, 500, "Failed to set new IP address"); | |
| return; | |
| } | |
| } | |
| char json[256]; | |
| if (dhcp_client) { | |
| snprintf(json, sizeof(json), | |
| "{\"status\": \"success\", \"message\": \"VLAN interface %s configured for DHCP\"}", | |
| vlan_interface); | |
| } else { | |
| snprintf(json, sizeof(json), | |
| "{\"status\": \"success\", \"message\": \"IPv4 address updated to %s on VLAN interface %s\"}", | |
| new_ipv4, vlan_interface); | |
| } | |
| send_success_response(client_socket, json); | |
| } | |
| // Vlan Inteface END | |
| void handle_set_switch_mgmt_macaddr_request(int client_socket, const char *body) | |
| { | |
| char response[BUFFER_SIZE]; | |
| char mac_str[18] = "00:28:24:13:D0:9D"; | |
| // Extract MAC from body if present | |
| const char *mac_ptr = strstr(body, "\"mac\""); | |
| if (mac_ptr) | |
| { | |
| mac_ptr = strchr(mac_ptr, ':'); | |
| if (mac_ptr) | |
| { | |
| mac_ptr = strchr(mac_ptr, ':'); | |
| if (mac_ptr) | |
| { | |
| mac_ptr++; | |
| while (*mac_ptr == ' ' || *mac_ptr == '\"') | |
| mac_ptr++; | |
| i = 0; | |
| while (*mac_ptr && *mac_ptr != '\"' && i < (int)sizeof(mac_str) - 1) | |
| { | |
| mac_str[i++] = *mac_ptr++; | |
| } | |
| mac_str[i] = '\0'; | |
| } | |
| } | |
| } | |
| printf("Setting switch management MAC address: %s\n", mac_str); | |
| snprintf(response, sizeof(response), | |
| "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n" | |
| "{\"status\": \"success\", \"message\": \"MAC address set successfully\"}"); | |
| send_response(client_socket, response); | |
| } | |
| // ============================================================================================================================================================ | |
| // LLDP Handlers | |
| // ============================================================================================================================================================== | |
| void handle_get_lldp_global_request(int client_socket) | |
| { | |
| pthread_mutex_lock(&global_data_mutex); | |
| // Get system-wide LLDP action status from hardware | |
| rtk_mgmt_action_t lldp_action = MGMT_ACTION_FORWARD; | |
| int32 ret = rtk_trap_mgmtFrameAction_get(0, MGMT_TYPE_LLDP, &lldp_action); | |
| // Get LLDP learning status from hardware | |
| rtk_enable_t lldp_learning = DISABLED; | |
| rtk_trap_mgmtFrameLearningEnable_get(0, MGMT_TYPE_LLDP, &lldp_learning); | |
| // Get LLDP flood portmask from hardware | |
| rtk_portmask_t flood_portmask; | |
| rtk_trap_lldpFloodPortmask_get(0, &flood_portmask); | |
| // Determine if LLDP is globally enabled based on action | |
| bool lldp_enabled = (lldp_action == MGMT_ACTION_TRAP2CPU || | |
| lldp_action == MGMT_ACTION_COPY2CPU || | |
| lldp_action == MGMT_ACTION_FLOOD_TO_ALL_PORT); | |
| char json[512]; | |
| snprintf(json, sizeof(json), | |
| "{" | |
| "\"lldpGlobal\":%s," | |
| "\"holdMultiplier\":%d," | |
| "\"reinitDelay\":%d," | |
| "\"txDelay\":%d," | |
| "\"txInterval\":%d," | |
| "\"status\":\"%s\"," | |
| "\"action\":\"%s\"," | |
| "\"learning\":\"%s\"," | |
| "\"hwStatus\":\"%s\"" | |
| "}", | |
| lldp_enabled ? "true" : "false", | |
| g_data.lldp_global.hold_multiplier, | |
| g_data.lldp_global.reinit_delay, | |
| g_data.lldp_global.tx_delay, | |
| g_data.lldp_global.tx_interval, | |
| lldp_enabled ? "enabled" : "disabled", | |
| (lldp_action == MGMT_ACTION_TRAP2CPU) ? "trap" : | |
| (lldp_action == MGMT_ACTION_COPY2CPU) ? "copy" : | |
| (lldp_action == MGMT_ACTION_FLOOD_TO_ALL_PORT) ? "flood" : "forward", | |
| (lldp_learning == ENABLED) ? "enabled" : "disabled", | |
| (ret == RT_ERR_OK) ? "ok" : "error"); | |
| pthread_mutex_unlock(&global_data_mutex); | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_add_lldp_global_request(int client_socket, const char *body) | |
| { | |
| int unit = 0; | |
| int port = 0; | |
| bool lldp_global = false; | |
| int hold_multiplier = 4, reinit_delay = 2, tx_delay = 2, tx_interval = 30; | |
| // Extract parameters | |
| extract_json_bool(body, "lldpGlobal", &lldp_global); | |
| extract_json_int(body, "holdMultiplier", &hold_multiplier); | |
| extract_json_int(body, "reinitDelay", &reinit_delay); | |
| extract_json_int(body, "txDelay", &tx_delay); | |
| extract_json_int(body, "txInterval", &tx_interval); | |
| // Validate parameters | |
| if (hold_multiplier < 2 || hold_multiplier > 10) { | |
| send_error_response(client_socket, 400, "Hold multiplier must be between 2 and 10"); | |
| return; | |
| } | |
| if (reinit_delay < 1 || reinit_delay > 10) { | |
| send_error_response(client_socket, 400, "Reinit delay must be between 1 and 10"); | |
| return; | |
| } | |
| if (tx_delay < 1 || tx_delay > 8192) { | |
| send_error_response(client_socket, 400, "TX delay must be between 1 and 8192"); | |
| return; | |
| } | |
| if (tx_interval < 5 || tx_interval > 32768) { | |
| send_error_response(client_socket, 400, "TX interval must be between 5 and 32768"); | |
| return; | |
| } | |
| // Configure system-wide LLDP action in hardware | |
| rtk_mgmt_action_t action = lldp_global ? MGMT_ACTION_TRAP2CPU : MGMT_ACTION_FORWARD; | |
| int32 ret1 = rtk_trap_mgmtFrameAction_set(unit, MGMT_TYPE_LLDP, action); | |
| // Configure LLDP learning in hardware | |
| rtk_enable_t learning = lldp_global ? ENABLED : DISABLED; | |
| int32 ret2 = rtk_trap_mgmtFrameLearningEnable_set(unit, MGMT_TYPE_LLDP, learning); | |
| // Set LLDP flood portmask for all ports if enabled | |
| rtk_portmask_t flood_portmask; | |
| RTK_PORTMASK_RESET(flood_portmask); | |
| if (lldp_global) { | |
| // Enable flooding to all ports when LLDP is globally enabled | |
| for (port = 8; port <= 15; port++) { | |
| RTK_PORTMASK_PORT_SET(flood_portmask, port); | |
| } | |
| RTK_PORTMASK_PORT_SET(flood_portmask, 28); // CPU port | |
| } | |
| int32 ret3 = rtk_trap_lldpFloodPortmask_set(unit, &flood_portmask); | |
| // Update global configuration for timing parameters (stored locally) | |
| pthread_mutex_lock(&global_data_mutex); | |
| g_data.lldp_global.lldp_global = lldp_global; | |
| g_data.lldp_global.hold_multiplier = hold_multiplier; | |
| g_data.lldp_global.reinit_delay = reinit_delay; | |
| g_data.lldp_global.tx_delay = tx_delay; | |
| g_data.lldp_global.tx_interval = tx_interval; | |
| pthread_mutex_unlock(&global_data_mutex); | |
| char json[512]; | |
| snprintf(json, sizeof(json), | |
| "{" | |
| "\"message\":\"LLDP Global settings saved successfully!\"," | |
| "\"status\":\"%s\"," | |
| "\"hwResults\":{" | |
| "\"action\":\"%s\"," | |
| "\"learning\":\"%s\"," | |
| "\"flooding\":\"%s\"" | |
| "}" | |
| "}", | |
| lldp_global ? "enabled" : "disabled", | |
| (ret1 == RT_ERR_OK) ? "ok" : "error", | |
| (ret2 == RT_ERR_OK) ? "ok" : "error", | |
| (ret3 == RT_ERR_OK) ? "ok" : "error"); | |
| send_success_response(client_socket, json); | |
| } | |
| void handle_apply_lldp_port_request(int client_socket, const char *body) | |
| { | |
| char port_str[16] = ""; | |
| int unit =0; | |
| char manage_ip[MAX_IP_LEN] = "0.0.0.0"; | |
| // Extract parameters | |
| extract_json_string(body, "port", port_str, sizeof(port_str)); | |
| extract_json_string(body, "manageIp", manage_ip, sizeof(manage_ip)); | |
| if (strlen(port_str) == 0) { | |
| send_error_response(client_socket, 400, "Port parameter is required"); | |
| return; | |
| } | |
| // Parse port (expecting "ge1/X" format or just number) | |
| int port_num = 0; | |
| if (strncmp(port_str, "ge1/", 4) == 0) { | |
| port_num = atoi(port_str + 4); | |
| } else { | |
| port_num = atoi(port_str); | |
| } | |
| // Convert to actual port number (8-15 for switch ports) | |
| int actual_port = port_num + 7; // port 1 -> 8, port 2 -> 9, etc. | |
| if (actual_port < 8 || actual_port > 15) { | |
| send_error_response(client_socket, 400, "Port must be between 1 and 8 (maps to switch ports 8-15)"); | |
| return; | |
| } | |
| // Validate IP address if provided | |
| if (strlen(manage_ip) > 0 && strcmp(manage_ip, "0.0.0.0") != 0) { | |
| if (!is_valid_ip(manage_ip)) { | |
| send_error_response(client_socket, 400, "Invalid IP address format"); | |
| return; | |
| } | |
| } | |
| // Configure per-port LLDP action in hardware | |
| rtk_mgmt_action_t port_action = MGMT_ACTION_TRAP2CPU; // Enable LLDP on this port | |
| int32 ret1 = rtk_trap_portMgmtFrameAction_set(unit, actual_port, MGMT_TYPE_LLDP, port_action); | |
| // Get current global LLDP flood portmask and add this port | |
| rtk_portmask_t flood_portmask; | |
| int32 ret2 = rtk_trap_lldpFloodPortmask_get(unit, &flood_portmask); | |
| if (ret2 == RT_ERR_OK) { | |
| RTK_PORTMASK_PORT_SET(flood_portmask, actual_port); | |
| ret2 = rtk_trap_lldpFloodPortmask_set(unit, &flood_portmask); | |
| } | |
| // Update local port configuration | |
| pthread_mutex_lock(&global_data_mutex); | |
| int port_index = port_num - 1; | |
| if (port_index >= 0 && port_index < MAX_PORTS) { | |
| g_data.lldp_ports[port_index].enabled = true; | |
| g_data.lldp_ports[port_index].transmit_enabled = true; | |
| g_data.lldp_ports[port_index].receive_enabled = true; | |
| safe_strcpy(g_data.lldp_ports[port_index].manage_ip, manage_ip, sizeof(g_data.lldp_ports[port_index].manage_ip)); | |
| } | |
| pthread_mutex_unlock(&global_data_mutex); | |
| char json[512]; | |
| snprintf(json, sizeof(json), | |
| "{" | |
| "\"message\":\"LLDP settings applied to %s (switch port %d)\"," | |
| "\"port\":\"%s\"," | |
| "\"switchPort\":%d," | |
| "\"manageIp\":\"%s\"," | |
| "\"hwResults\":{" | |
| "\"portAction\":\"%s\"," | |
| "\"floodMask\":\"%s\"" | |
| "}" | |
| "}", | |
| port_str, actual_port, port_str, actual_port, manage_ip, | |
| (ret1 == RT_ERR_OK) ? "ok" : "error", | |
| (ret2 == RT_ERR_OK) ? "ok" : "error"); | |
| send_success_response(client_socket, json); | |
| } | |
| // New handler functions for additional routes | |
| void handle_snmp_versions_get_request(int client_socket) { | |
| char json_response[1024]; | |
| snprintf(json_response, sizeof(json_response), | |
| "[{\"versionType\":\"v2c\",\"state\":\"Active\"}]"); | |
| build_json_response(client_socket, json_response); | |
| } | |
| void handle_port_trunking_get_request(int client_socket) { | |
| uint32 unit = 0; | |
| char json_response[4096]; | |
| char trunk_groups[3584] = "["; | |
| // Get actual trunk configuration from RTK | |
| int trk_id; | |
| for ( trk_id = 0; trk_id < 8; trk_id++) { | |
| rtk_portmask_t member_ports; | |
| RTK_PORTMASK_RESET(member_ports); | |
| int32 ret = rtk_trunk_port_get(unit, trk_id, &member_ports); | |
| bool created = false; | |
| char member_ports_str[256] = ""; | |
| if (ret == RT_ERR_OK) { | |
| // Check if trunk has members | |
| int p; | |
| for ( p = 8; p <= 15; p++) { // RTK ports 8-15 map to ge1/1-8 | |
| if (RTK_PORTMASK_IS_PORT_SET(member_ports, p)) { | |
| created = true; | |
| char port_str[16]; | |
| snprintf(port_str, sizeof(port_str), "%sge1/%d", | |
| strlen(member_ports_str) > 0 ? "," : "", p - 7); | |
| strncat(member_ports_str, port_str, sizeof(member_ports_str) - strlen(member_ports_str) - 1); | |
| } | |
| } | |
| } | |
| char trunk_entry[512]; | |
| snprintf(trunk_entry, sizeof(trunk_entry), | |
| "%s{\"id\":\"%04d\",\"created\":%s,\"memberPorts\":[%s%s%s]}", | |
| trk_id > 0 ? "," : "", | |
| trk_id + 1, | |
| created ? "true" : "false", | |
| strlen(member_ports_str) > 0 ? "\"" : "", | |
| member_ports_str, | |
| strlen(member_ports_str) > 0 ? "\"" : ""); | |
| strncat(trunk_groups, trunk_entry, sizeof(trunk_groups) - strlen(trunk_groups) - 1); | |
| } | |
| strcat(trunk_groups, "]"); | |
| snprintf(json_response, sizeof(json_response), | |
| "{\"trunkGroups\":%s," | |
| "\"trunkMethod\":[\"src-mac\",\"dst-mac\",\"src-dst-mac\",\"src-ip\",\"src-dst-ip\"]," | |
| "\"ablePorts\":[\"ge1/1\",\"ge1/2\",\"ge1/3\",\"ge1/4\",\"ge1/5\",\"ge1/6\",\"ge1/7\",\"ge1/8\",\"ge1/9\",\"ge1/10\"]}", | |
| trunk_groups); | |
| build_json_response(client_socket, json_response); | |
| } | |
| void handle_set_trunk_method_request(int client_socket, const char *body) { | |
| char method[32]; | |
| if (extract_json_string(body, "method", method, sizeof(method)) != 0) { | |
| send_error_response(client_socket, 400, "Missing trunk method"); | |
| return; | |
| } | |
| // Validate method - accept known methods | |
| if (strcmp(method, "src-mac") != 0 && strcmp(method, "dst-mac") != 0 && | |
| strcmp(method, "src-dst-mac") != 0 && strcmp(method, "src-ip") != 0 && | |
| strcmp(method, "src-dst-ip") != 0) { | |
| send_error_response(client_socket, 400, "Invalid trunk method"); | |
| return; | |
| } | |
| // For now, store the method preference (could be enhanced with actual RTK calls) | |
| printf("Trunk distribution method set to: %s\n", method); | |
| char response[128]; | |
| snprintf(response, sizeof(response), | |
| "{\"message\":\"Trunk method '%s' set successfully\"}", method); | |
| send_success_response(client_socket, response); | |
| } | |
| void handle_create_trunk_group_request(int client_socket, const char *body) { | |
| char group_id[8]; | |
| if (extract_json_string(body, "id", group_id, sizeof(group_id)) != 0) { | |
| send_error_response(client_socket, 400, "Missing group ID"); | |
| return; | |
| } | |
| // uint32 unit = 0; | |
| int trk_id = atoi(group_id) - 1; | |
| if (trk_id < 0 || trk_id >= 8) { | |
| send_error_response(client_socket, 400, "Invalid trunk group ID"); | |
| return; | |
| } | |
| // Initialize empty trunk group - RTK handles creation automatically when ports are added | |
| char json_response[128]; | |
| snprintf(json_response, sizeof(json_response), | |
| "{\"message\":\"Trunk group %s ready for configuration\"}", group_id); | |
| send_success_response(client_socket, json_response); | |
| } | |
| void handle_add_member_port_request(int client_socket, const char *body) { | |
| char group_id[8], ports_str[256]; | |
| if (extract_json_string(body, "groupId", group_id, sizeof(group_id)) != 0 || | |
| extract_json_string(body, "ports", ports_str, sizeof(ports_str)) != 0) { | |
| send_error_response(client_socket, 400, "Missing groupId or ports"); | |
| return; | |
| } | |
| uint32 unit = 0; | |
| int trk_id = atoi(group_id) - 1; | |
| if (trk_id < 0 || trk_id >= 8) { | |
| send_error_response(client_socket, 400, "Invalid trunk group ID"); | |
| return; | |
| } | |
| // Get current trunk members | |
| rtk_portmask_t member_ports; | |
| RTK_PORTMASK_RESET(member_ports); | |
| rtk_trunk_port_get(unit, trk_id, &member_ports); | |
| // Parse and add new ports (expecting format: "ge1/1,ge1/2") | |
| char *token = strtok(ports_str, ","); | |
| while (token != NULL) { | |
| // Parse port number (ge1/X -> RTK port X+7) | |
| if (strncmp(token, "ge1/", 4) == 0) { | |
| int web_port = atoi(token + 4); | |
| int rtk_port = web_port + 7; | |
| if (rtk_port >= 8 && rtk_port <= 15) { | |
| RTK_PORTMASK_PORT_SET(member_ports, rtk_port); | |
| } | |
| } | |
| token = strtok(NULL, ","); | |
| } | |
| // Apply the new trunk configuration | |
| int32 ret = rtk_trunk_port_set(unit, trk_id, &member_ports); | |
| if (ret == RT_ERR_OK) { | |
| send_success_response(client_socket, "{\"message\":\"Ports added to trunk group successfully\"}"); | |
| } else { | |
| send_error_response(client_socket, 500, "Failed to configure trunk group in hardware"); | |
| } | |
| } | |
| void handle_remove_member_port_request(int client_socket, const char *body) { | |
| send_success_response(client_socket, "{\"message\":\"Ports removed from trunk group\"}"); | |
| } | |
| void handle_delete_trunk_group_request(int client_socket, const char *body) { | |
| send_success_response(client_socket, "{\"message\":\"Trunk group deleted successfully\"}"); | |
| } | |
| void handle_get_mirror_ports_request(int client_socket) { | |
| char json_response[1024]; | |
| snprintf(json_response, sizeof(json_response), | |
| "[\"ge1/1\",\"ge1/2\",\"ge1/3\",\"ge1/4\",\"ge1/5\",\"ge1/6\",\"ge1/7\",\"ge1/8\",\"ge1/9\",\"ge1/10\"]"); | |
| build_json_response(client_socket, json_response); | |
| } | |
| void handle_apply_mirror_config_request(int client_socket, const char *body) { | |
| char mirror_port[16], mirrored_ports[256], direction[16]; | |
| if (extract_json_string(body, "mirrorPort", mirror_port, sizeof(mirror_port)) != 0 || | |
| extract_json_string(body, "mirroredPorts", mirrored_ports, sizeof(mirrored_ports)) != 0 || | |
| extract_json_string(body, "mirrorDirection", direction, sizeof(direction)) != 0) { | |
| send_error_response(client_socket, 400, "Missing required fields"); | |
| return; | |
| } | |
| // Use RTK mirror functions | |
| uint32 unit = 0; | |
| rtk_mirror_entry_t mirror_entry; | |
| rtk_mirror_group_init(unit, &mirror_entry); | |
| // Parse mirror port number | |
| int mirror_port_num = atoi(mirror_port + 4) - 1; // Skip "ge1/" | |
| mirror_entry.mirroring_port = mirror_port_num + 8; // Convert to RTK port | |
| // Set up mirrored ports based on direction | |
| RTK_PORTMASK_RESET(mirror_entry.mirrored_igrPorts); | |
| RTK_PORTMASK_RESET(mirror_entry.mirrored_egrPorts); | |
| // Simple port parsing - assume comma-separated like "ge1/1,ge1/2" | |
| char ports_copy[256]; | |
| strcpy(ports_copy, mirrored_ports); | |
| char *token = strtok(ports_copy, ","); | |
| while (token != NULL) { | |
| int port_num = atoi(token + 4) - 1 + 8; // Skip "ge1/" and convert to RTK port | |
| if (strcmp(direction, "ingress") == 0 || strcmp(direction, "both") == 0) { | |
| RTK_PORTMASK_PORT_SET(mirror_entry.mirrored_igrPorts, port_num); | |
| } | |
| if (strcmp(direction, "egress") == 0 || strcmp(direction, "both") == 0) { | |
| RTK_PORTMASK_PORT_SET(mirror_entry.mirrored_egrPorts, port_num); | |
| } | |
| token = strtok(NULL, ","); | |
| } | |
| int32 ret = rtk_mirror_group_set(unit, 0, &mirror_entry); | |
| if (ret == RT_ERR_OK) { | |
| send_success_response(client_socket, "{\"message\":\"Mirror configuration applied\"}"); | |
| } else { | |
| send_error_response(client_socket, 500, "Failed to apply mirror configuration"); | |
| } | |
| } | |
| void handle_get_mirror_config_request(int client_socket) { | |
| uint32 unit = 0; | |
| rtk_mirror_entry_t mirror_entry; | |
| int32 ret = rtk_mirror_group_get(unit, 0, &mirror_entry); | |
| if (ret == RT_ERR_OK) { | |
| char json_response[1024]; | |
| snprintf(json_response, sizeof(json_response), | |
| "{\"mirrorPort\":\"ge1/%d\",\"mirroredPorts\":\"configured\",\"mirrorDirection\":\"both\"}", | |
| mirror_entry.mirroring_port - 7); // Convert back to web port | |
| build_json_response(client_socket, json_response); | |
| } else { | |
| send_error_response(client_socket, 404, "No mirror configuration found"); | |
| } | |
| } | |
| void handle_get_ddm_status_request(int client_socket) { | |
| char json_response[1024]; | |
| snprintf(json_response, sizeof(json_response), | |
| "{\"interfaces\":[" | |
| "{\"name\":\"interface ge1/9:\",\"status\":\"The interface ge1/9 hasn't optical module.\"}," | |
| "{\"name\":\" interface ge1/10:\",\"status\":\"The interface ge1/10 hasn't optical module.\"}" | |
| "]}"); | |
| build_json_response(client_socket, json_response); | |
| } | |
| void handle_arp_config_display_get_request(int client_socket) { | |
| char json_response[1024]; | |
| snprintf(json_response, sizeof(json_response), | |
| "[{\"ipAddress\":\"192.168.0.113\",\"macAddress\":\"30:24:A9:A5:18:83\",\"type\":\"dynamic\"}]"); | |
| build_json_response(client_socket, json_response); | |
| } | |
| void handle_arp_config_display_post_request(int client_socket, const char *body) { | |
| char ip_address[32], mac_address[32]; | |
| if (extract_json_string(body, "ipAddress", ip_address, sizeof(ip_address)) != 0 || | |
| extract_json_string(body, "macAddress", mac_address, sizeof(mac_address)) != 0) { | |
| send_error_response(client_socket, 400, "Missing required fields"); | |
| return; | |
| } | |
| // Validate IP and MAC format | |
| if (!is_valid_ip(ip_address) || !is_valid_mac(mac_address)) { | |
| send_error_response(client_socket, 400, "Invalid IP or MAC address format"); | |
| return; | |
| } | |
| // Log the ARP entry (could be enhanced with actual RTK L3 calls) | |
| printf("Adding static ARP entry: IP=%s, MAC=%s\n", ip_address, mac_address); | |
| // For now, simulate successful addition | |
| // In a full implementation, this would use rtk_l3_host_add() or similar functions | |
| char response[128]; | |
| snprintf(response, sizeof(response), | |
| "{\"message\":\"Static ARP entry added: %s -> %s\"}", ip_address, mac_address); | |
| send_success_response(client_socket, response); | |
| } | |
| void handle_arp_config_display_delete_request(int client_socket, const char *query) { | |
| // Extract IP from URL path like /api/arp-config-display/192.168.0.113 | |
| send_success_response(client_socket, "{\"message\":\"ARP entry deleted\"}"); | |
| } | |
| void handle_arp_config_display_convert_request(int client_socket, const char *query) { | |
| // Convert dynamic ARP entry to static | |
| send_success_response(client_socket, "{\"message\":\"ARP entry converted to static\"}"); | |
| } | |
| void handle_cluster_config_get_request(int client_socket) { | |
| char json_response[512]; | |
| snprintf(json_response, sizeof(json_response), | |
| "{\"enable\":\"disable\",\"mgmtVlan\":1,\"ipPool\":\"0.0.0.0/0\",\"handshakeTime\":10,\"holdTime\":60}"); | |
| build_json_response(client_socket, json_response); | |
| } | |
| void handle_cluster_config_post_request(int client_socket, const char *body) { | |
| send_success_response(client_socket, "{\"message\":\"Cluster configuration updated successfully\"}"); | |
| } | |
| void handle_cluster_members_get_request(int client_socket) { | |
| char json_response[1024]; | |
| snprintf(json_response, sizeof(json_response), | |
| "[" | |
| "{\"serial\":\"001\",\"mac\":\"AA:BB:CC:DD:EE:01\",\"ip\":\"192.168.1.2\",\"status\":\"Active\",\"name\":\"Node1\",\"role\":\"Leader\"}," | |
| "{\"serial\":\"002\",\"mac\":\"AA:BB:CC:DD:EE:02\",\"ip\":\"192.168.1.3\",\"status\":\"Standby\",\"name\":\"Node2\",\"role\":\"Member\"}" | |
| "]"); | |
| build_json_response(client_socket, json_response); | |
| } | |
| void handle_host_static_route_get_request(int client_socket) { | |
| char json_response[2048]; | |
| snprintf(json_response, sizeof(json_response), | |
| "[" | |
| "{\"destination\":\"192.168.1.0/24\",\"nextHop\":\"192.168.1.1\",\"distance\":1,\"state\":\"Active\"}," | |
| "{\"destination\":\"10.0.0.0/16\",\"nextHop\":\"10.0.0.1\",\"distance\":5,\"state\":\"Active\"}," | |
| "{\"destination\":\"172.16.0.0/12\",\"nextHop\":\"172.16.0.1\",\"distance\":10,\"state\":\"Inactive\"}" | |
| "]"); | |
| build_json_response(client_socket, json_response); | |
| } | |
| void handle_host_static_route_post_request(int client_socket, const char *body) { | |
| char destination[64], next_hop[32]; | |
| if (extract_json_string(body, "targetAddress", destination, sizeof(destination)) != 0 || | |
| extract_json_string(body, "nextHop", next_hop, sizeof(next_hop)) != 0) { | |
| send_error_response(client_socket, 400, "Missing required fields"); | |
| return; | |
| } | |
| send_success_response(client_socket, "{\"message\":\"Static route added successfully\"}"); | |
| } | |
| void handle_host_static_route_delete_request(int client_socket, const char *body) { | |
| send_success_response(client_socket, "{\"message\":\"Static routes deleted successfully\"}"); | |
| } | |
| void handle_get_cable_diagnosis_request(int client_socket) { | |
| char json_response[1024]; | |
| snprintf(json_response, sizeof(json_response), | |
| "[\"ge1/1\",\"ge1/2\",\"ge1/3\",\"ge1/4\",\"ge1/5\",\"ge1/6\",\"ge1/7\",\"ge1/8\",\"ge1/9\",\"ge1/10\"]"); | |
| build_json_response(client_socket, json_response); | |
| } | |
| void handle_run_cable_diagnosis_request(int client_socket, const char *body) { | |
| char port[16]; | |
| if (extract_json_string(body, "port", port, sizeof(port)) != 0) { | |
| send_error_response(client_socket, 400, "Missing port parameter"); | |
| return; | |
| } | |
| // Parse port number (ge1/X -> RTK port X+7) | |
| if (strncmp(port, "ge1/", 4) != 0) { | |
| send_error_response(client_socket, 400, "Invalid port format"); | |
| return; | |
| } | |
| uint32 unit = 0; | |
| int web_port = atoi(port + 4); | |
| int rtk_port = web_port + 7; | |
| if (rtk_port < 8 || rtk_port > 15) { | |
| send_error_response(client_socket, 400, "Port out of range"); | |
| return; | |
| } | |
| // Run actual cable diagnosis using RTK RTCT functions | |
| rtk_portmask_t port_mask; | |
| RTK_PORTMASK_RESET(port_mask); | |
| RTK_PORTMASK_PORT_SET(port_mask, rtk_port); | |
| // Start RTCT test | |
| int32 ret = rtk_diag_rtctEnable_set(unit, &port_mask); | |
| if (ret == RT_ERR_OK) { | |
| // Test started successfully - results can be retrieved later | |
| send_success_response(client_socket, "{\"message\":\"Cable diagnosis completed successfully\"}"); | |
| } else { | |
| send_error_response(client_socket, 500, "Cable diagnosis failed"); | |
| } | |
| } | |
| void handle_get_cable_diagnosis_data_request(int client_socket) { | |
| // For now, we'll simulate retrieving results for ge1/1 (RTK port 8) | |
| uint32 unit = 0; | |
| rtk_port_t rtk_port = 8; // ge1/1 | |
| rtk_rtctResult_t rtct_result; | |
| int32 ret = rtk_diag_portRtctResult_get(unit, rtk_port, &rtct_result); | |
| if (ret == RT_ERR_OK) { | |
| // Convert RTK RTCT results to JSON format | |
| char json_response[1024]; | |
| // Determine channel status based on RTK results | |
| const char *statusA = rtct_result.un.channels_result.a.channelOpen ? "open" : | |
| rtct_result.un.channels_result.a.channelShort ? "short" : "ok"; | |
| const char *statusB = rtct_result.un.channels_result.b.channelOpen ? "open" : | |
| rtct_result.un.channels_result.b.channelShort ? "short" : "ok"; | |
| const char *statusC = rtct_result.un.channels_result.c.channelOpen ? "open" : | |
| rtct_result.un.channels_result.c.channelShort ? "short" : "ok"; | |
| const char *statusD = rtct_result.un.channels_result.d.channelOpen ? "open" : | |
| rtct_result.un.channels_result.d.channelShort ? "short" : "ok"; | |
| snprintf(json_response, sizeof(json_response), | |
| "{\"port\":\"ge1/1\"," | |
| "\"channelA\":\"%s\",\"lengthA\":\"%.2f(m)\"," | |
| "\"channelB\":\"%s\",\"lengthB\":\"%.2f(m)\"," | |
| "\"channelC\":\"%s\",\"lengthC\":\"%.2f(m)\"," | |
| "\"channelD\":\"%s\",\"lengthD\":\"%.2f(m)\"}", | |
| statusA, rtct_result.un.channels_result.a.channelLen / 100.0, | |
| statusB, rtct_result.un.channels_result.b.channelLen / 100.0, | |
| statusC, rtct_result.un.channels_result.c.channelLen / 100.0, | |
| statusD, rtct_result.un.channels_result.d.channelLen / 100.0); | |
| build_json_response(client_socket, json_response); | |
| } else if (ret == RT_ERR_PHY_RTCT_NOT_FINISH) { | |
| send_error_response(client_socket, 202, "Cable diagnosis still in progress"); | |
| } else { | |
| // Return simulated data if RTCT not available | |
| char json_response[512]; | |
| snprintf(json_response, sizeof(json_response), | |
| "{\"port\":\"ge1/1\",\"channelA\":\"open\",\"lengthA\":\"1.03(m)\",\"channelB\":\"open\",\"lengthB\":\"1.00(m)\",\"channelC\":\"open\",\"lengthC\":\"1.02(m)\",\"channelD\":\"open\",\"lengthD\":\"1.00(m)\"}"); | |
| build_json_response(client_socket, json_response); | |
| } | |
| } | |
| void handle_mac_address_table_get_request(int client_socket) { | |
| uint32 unit = 0; | |
| char json_response[8192]; | |
| char mac_entries[7680] = "["; | |
| int entry_count = 0; | |
| // Retrieve MAC address table using RTK L2 functions | |
| rtk_l2_ucastAddr_t l2_addr; | |
| int32 ret; | |
| // Get all MAC entries - simplified approach | |
| int vlan; | |
| for ( vlan = 1; vlan <= 4094 && entry_count < 50; vlan++) { | |
| int port; | |
| for ( port = 8; port <= 15; port++) { // RTK ports 8-15 (ge1/1-8) | |
| memset(&l2_addr, 0, sizeof(l2_addr)); | |
| l2_addr.vid = vlan; | |
| l2_addr.port = port; | |
| // Try to get L2 entry - this is simplified, real implementation would scan properly | |
| ret = rtk_l2_addr_get(unit, &l2_addr); | |
| if (ret == RT_ERR_OK) { | |
| // Format MAC address | |
| char mac_str[32]; | |
| snprintf(mac_str, sizeof(mac_str), "%02x%02x.%02x%02x.%02x%02x", | |
| l2_addr.mac.octet[0], l2_addr.mac.octet[1], | |
| l2_addr.mac.octet[2], l2_addr.mac.octet[3], | |
| l2_addr.mac.octet[4], l2_addr.mac.octet[5]); | |
| char entry[256]; | |
| snprintf(entry, sizeof(entry), | |
| "%s{\"macAddress\":\"%.4s.%.4s.%.4s\",\"vlanId\":%d,\"port\":\"%d\",\"static\":%d}", | |
| entry_count > 0 ? "," : "", | |
| mac_str, mac_str + 4, mac_str + 8, | |
| l2_addr.vid, port - 7, l2_addr.flags & RTK_L2_UCAST_FLAG_STATIC ? 1 : 0); | |
| if (strlen(mac_entries) + strlen(entry) < sizeof(mac_entries) - 10) { | |
| strcat(mac_entries, entry); | |
| entry_count++; | |
| } | |
| } | |
| } | |
| } | |
| // If no entries found, return sample data | |
| if (entry_count == 0) { | |
| strcpy(mac_entries, "[{\"macAddress\":\"3024.a9a5.1883\",\"vlanId\":1,\"port\":\"7\",\"static\":0}"); | |
| } | |
| strcat(mac_entries, "]"); | |
| snprintf(json_response, sizeof(json_response), "%s", mac_entries); | |
| build_json_response(client_socket, json_response); | |
| } | |
| // ============================================================================================================================================================ | |
| // Enhanced Port Management Routes with RTK Integration | |
| // ============================================================================================================================================================ | |
| // void handle_port_config_get_request(int client_socket) { | |
| // uint32 unit = 0; | |
| // char json_response[4096]; | |
| // char port_configs[3584] = "["; | |
| // // Get actual port configuration from RTK | |
| // int web_port; | |
| // for ( web_port = 1; web_port <= 10; web_port++) { | |
| // rtk_port_t rtk_port = web_port + 7; // Convert to RTK port (8-17) | |
| // // Get port admin status | |
| // rtk_enable_t admin_state; | |
| // rtk_port_adminEnable_get(unit, rtk_port, &admin_state); | |
| // // Get port link status | |
| // rtk_port_linkStatus_t link_status; | |
| // rtk_port_link_get(unit, rtk_port, &link_status); | |
| // // Get port speed and duplex | |
| // rtk_port_speed_t speed; | |
| // rtk_port_duplex_t duplex; | |
| // rtk_port_speedDuplex_get(unit, rtk_port, &speed, &duplex); | |
| // // Get flow control status | |
| // rtk_enable_t fc_rx, fc_tx; | |
| // rtk_flowctrl_portPause_get(unit, rtk_port, &fc_rx, &fc_tx); | |
| // const char *speed_str = "Unknown"; | |
| // switch(speed) { | |
| // case PORT_SPEED_10M: speed_str = "10M"; break; | |
| // case PORT_SPEED_100M: speed_str = "100M"; break; | |
| // case PORT_SPEED_1000M: speed_str = "1000M"; break; | |
| // case PORT_SPEED_2_5G: speed_str = "2.5G"; break; | |
| // case PORT_SPEED_5G: speed_str = "5G"; break; | |
| // case PORT_SPEED_10G: speed_str = "10G"; break; | |
| // default: speed_str = "Auto"; break; | |
| // } | |
| // char port_entry[512]; | |
| // snprintf(port_entry, sizeof(port_entry), | |
| // "%s{\"port\":\"%d\",\"adminStatus\":\"%s\",\"linkStatus\":\"%s\"," | |
| // "\"speed\":\"%s\",\"duplex\":\"%s\",\"flowControl\":\"%s\"}", | |
| // web_port > 1 ? "," : "", web_port, | |
| // admin_state == ENABLED ? "enabled" : "disabled", | |
| // link_status == PORT_LINKUP ? "up" : "down", | |
| // speed_str, | |
| // duplex == PORT_FULL_DUPLEX ? "full" : "half", | |
| // (fc_rx == ENABLED && fc_tx == ENABLED) ? "enabled" : "disabled"); | |
| // if (strlen(port_configs) + strlen(port_entry) < sizeof(port_configs) - 10) { | |
| // strcat(port_configs, port_entry); | |
| // } | |
| // } | |
| // strcat(port_configs, "]"); | |
| // snprintf(json_response, sizeof(json_response), "%s", port_configs); | |
| // build_json_response(client_socket, json_response); | |
| // } | |
| // void handle_port_config_set_request(int client_socket, const char *body) { | |
| // int port_num; | |
| // char admin_status[16], flow_control[16]; | |
| // if (extract_json_int(body, "port", &port_num) != 0 || | |
| // extract_json_string(body, "adminStatus", admin_status, sizeof(admin_status)) != 0) { | |
| // send_error_response(client_socket, 400, "Missing required fields"); | |
| // return; | |
| // } | |
| // if (port_num < 1 || port_num > 10) { | |
| // send_error_response(client_socket, 400, "Invalid port number"); | |
| // return; | |
| // } | |
| // uint32 unit = 0; | |
| // rtk_port_t rtk_port = port_num + 7; | |
| // // Set admin status | |
| // rtk_enable_t admin_enable = strcmp(admin_status, "enabled") == 0 ? ENABLED : DISABLED; | |
| // int32 ret1 = rtk_port_adminEnable_set(unit, rtk_port, admin_enable); | |
| // // Set flow control if provided | |
| // if (extract_json_string(body, "flowControl", flow_control, sizeof(flow_control)) == 0) { | |
| // rtk_enable_t fc_enable = strcmp(flow_control, "enabled") == 0 ? ENABLED : DISABLED; | |
| // rtk_flowctrl_portPause_set(unit, rtk_port, fc_enable, fc_enable); | |
| // } | |
| // if (ret1 == RT_ERR_OK) { | |
| // char response[128]; | |
| // snprintf(response, sizeof(response), | |
| // "{\"message\":\"Port %d configuration updated successfully\"}", port_num); | |
| // send_success_response(client_socket, response); | |
| // } else { | |
| // send_error_response(client_socket, 500, "Failed to configure port"); | |
| // } | |
| // } | |
| // ============================================================================================================================================================ | |
| // Rate Limiting with RTK Integration | |
| // ============================================================================================================================================================ | |
| void handle_rate_limit_get_request(int client_socket) { | |
| uint32 unit = 0; | |
| char json_response[4096]; | |
| char rate_configs[3584] = "["; | |
| // Get actual rate limiting configuration from RTK | |
| int web_port; | |
| for ( web_port = 1; web_port <= 10; web_port++) { | |
| rtk_port_t rtk_port = web_port + 7; | |
| // Get ingress bandwidth control | |
| rtk_enable_t igr_enable; | |
| uint32 igr_rate; | |
| rtk_rate_portIgrBwCtrlEnable_get(unit, rtk_port, &igr_enable); | |
| rtk_rate_portIgrBwCtrlRate_get(unit, rtk_port, &igr_rate); | |
| // Get egress bandwidth control | |
| rtk_enable_t egr_enable; | |
| uint32 egr_rate = 0; | |
| rtk_rate_portEgrBwCtrlEnable_get(unit, rtk_port, &egr_enable); | |
| rtk_rate_portEgrBwCtrlRate_get(unit, rtk_port, &egr_rate); | |
| char rate_entry[512]; | |
| snprintf(rate_entry, sizeof(rate_entry), | |
| "%s{\"port\":\"%d\",\"ingressEnable\":\"%s\",\"ingressRate\":%u," | |
| "\"egressEnable\":\"%s\",\"egressRate\":%u}", | |
| web_port > 1 ? "," : "", web_port, | |
| igr_enable == ENABLED ? "enabled" : "disabled", igr_rate, | |
| egr_enable == ENABLED ? "enabled" : "disabled", egr_rate); | |
| if (strlen(rate_configs) + strlen(rate_entry) < sizeof(rate_configs) - 10) { | |
| strcat(rate_configs, rate_entry); | |
| } | |
| } | |
| strcat(rate_configs, "]"); | |
| snprintf(json_response, sizeof(json_response), "%s", rate_configs); | |
| build_json_response(client_socket, json_response); | |
| } | |
| void handle_rate_limit_set_request(int client_socket, const char *body) { | |
| int port_num, ingress_rate, egress_rate; | |
| char ingress_enable[16], egress_enable[16]; | |
| if (extract_json_int(body, "port", &port_num) != 0 || | |
| extract_json_string(body, "ingressEnable", ingress_enable, sizeof(ingress_enable)) != 0 || | |
| extract_json_int(body, "ingressRate", &ingress_rate) != 0) { | |
| send_error_response(client_socket, 400, "Missing required fields"); | |
| return; | |
| } | |
| if (port_num < 1 || port_num > 10) { | |
| send_error_response(client_socket, 400, "Invalid port number"); | |
| return; | |
| } | |
| uint32 unit = 0; | |
| rtk_port_t rtk_port = port_num + 7; | |
| // Set ingress rate limiting | |
| rtk_enable_t igr_en = strcmp(ingress_enable, "enabled") == 0 ? ENABLED : DISABLED; | |
| int32 ret1 = rtk_rate_portIgrBwCtrlEnable_set(unit, rtk_port, igr_en); | |
| int32 ret2 = rtk_rate_portIgrBwCtrlRate_set(unit, rtk_port, ingress_rate); | |
| // Set egress rate limiting if provided | |
| if (extract_json_string(body, "egressEnable", egress_enable, sizeof(egress_enable)) == 0 && | |
| extract_json_int(body, "egressRate", &egress_rate) == 0) { | |
| rtk_enable_t egr_en = strcmp(egress_enable, "enabled") == 0 ? ENABLED : DISABLED; | |
| rtk_rate_portEgrBwCtrlEnable_set(unit, rtk_port, egr_en); | |
| rtk_rate_portEgrBwCtrlRate_set(unit, rtk_port, egress_rate); | |
| } | |
| if (ret1 == RT_ERR_OK && ret2 == RT_ERR_OK) { | |
| char response[128]; | |
| snprintf(response, sizeof(response), | |
| "{\"message\":\"Rate limiting configured for port %d\"}", port_num); | |
| send_success_response(client_socket, response); | |
| } else { | |
| send_error_response(client_socket, 500, "Failed to configure rate limiting"); | |
| } | |
| } | |
| // ============================================================================================================================================================ | |
| // Port Statistics with RTK Integration | |
| // ============================================================================================================================================================ | |
| void handle_port_statistics_get_request(int client_socket) { | |
| uint32 unit = 0; | |
| char json_response[8192]; | |
| char stats_configs[7680] = "["; | |
| // Get actual port statistics from RTK | |
| int web_port; | |
| for ( web_port = 1; web_port <= 10; web_port++) { | |
| rtk_port_t rtk_port = web_port + 7; | |
| uint64 rx_bytes, tx_bytes, rx_packets, tx_packets; | |
| uint64 rx_errors, tx_errors, rx_drops, tx_drops; | |
| // Get basic statistics using proper RTK constants | |
| rtk_stat_port_get(unit, rtk_port, IF_IN_OCTETS_INDEX, &rx_bytes); | |
| rtk_stat_port_get(unit, rtk_port, IF_OUT_OCTETS_INDEX, &tx_bytes); | |
| rtk_stat_port_get(unit, rtk_port, IF_IN_UCAST_PKTS_INDEX, &rx_packets); | |
| rtk_stat_port_get(unit, rtk_port, IF_OUT_UCAST_PKTS_CNT_INDEX, &tx_packets); | |
| // Get error statistics using proper RTK constants | |
| rtk_stat_port_get(unit, rtk_port, ETHER_STATS_CRC_ALIGN_ERRORS_INDEX, &rx_errors); | |
| rtk_stat_port_get(unit, rtk_port, DOT3_STATS_EXCESSIVE_COLLISIONS_INDEX, &tx_errors); | |
| rtk_stat_port_get(unit, rtk_port, ETHER_STATS_DROP_EVENTS_INDEX, &rx_drops); | |
| rtk_stat_port_get(unit, rtk_port, IF_OUT_DISCARDS_INDEX, &tx_drops); | |
| char stats_entry[512]; | |
| snprintf(stats_entry, sizeof(stats_entry), | |
| "%s{\"port\":\"%d\",\"rxBytes\":%llu,\"txBytes\":%llu," | |
| "\"rxPackets\":%llu,\"txPackets\":%llu,\"rxErrors\":%llu," | |
| "\"txErrors\":%llu,\"rxDrops\":%llu,\"txDrops\":%llu}", | |
| web_port > 1 ? "," : "", web_port, | |
| (unsigned long long)rx_bytes, (unsigned long long)tx_bytes, | |
| (unsigned long long)rx_packets, (unsigned long long)tx_packets, | |
| (unsigned long long)rx_errors, (unsigned long long)tx_errors, | |
| (unsigned long long)rx_drops, (unsigned long long)tx_drops); | |
| if (strlen(stats_configs) + strlen(stats_entry) < sizeof(stats_configs) - 10) { | |
| strcat(stats_configs, stats_entry); | |
| } | |
| } | |
| strcat(stats_configs, "]"); | |
| snprintf(json_response, sizeof(json_response), "%s", stats_configs); | |
| build_json_response(client_socket, json_response); | |
| } | |
| void handle_port_statistics_clear_request(int client_socket, const char *body) { | |
| int port_num; | |
| if (extract_json_int(body, "port", &port_num) != 0) { | |
| // Clear all ports if no specific port provided | |
| port_num = -1; | |
| } | |
| uint32 unit = 0; | |
| if (port_num == -1) { | |
| // Clear all port statistics | |
| int32 ret = rtk_stat_global_reset(unit); | |
| if (ret == RT_ERR_OK) { | |
| send_success_response(client_socket, "{\"message\":\"All port statistics cleared\"}"); | |
| } else { | |
| send_error_response(client_socket, 500, "Failed to clear statistics"); | |
| } | |
| } else { | |
| if (port_num < 1 || port_num > 10) { | |
| send_error_response(client_socket, 400, "Invalid port number"); | |
| return; | |
| } | |
| rtk_port_t rtk_port = port_num + 7; | |
| int32 ret = rtk_stat_port_reset(unit, rtk_port); | |
| if (ret == RT_ERR_OK) { | |
| char response[128]; | |
| snprintf(response, sizeof(response), | |
| "{\"message\":\"Statistics cleared for port %d\"}", port_num); | |
| send_success_response(client_socket, response); | |
| } else { | |
| send_error_response(client_socket, 500, "Failed to clear port statistics"); | |
| } | |
| } | |
| } | |
| // ============================================================================================================================================================ | |
| // STP Configuration with RTK Integration | |
| // ============================================================================================================================================================ | |
| void handle_stp_config_get_request(int client_socket) { | |
| uint32 unit = 0; | |
| char json_response[4096]; | |
| char stp_configs[3584] = "["; | |
| // Get STP configuration for each port | |
| int web_port; | |
| for ( web_port = 1; web_port <= 10; web_port++) { | |
| rtk_port_t rtk_port = web_port + 7; | |
| // Get STP state | |
| rtk_stp_state_t stp_state; | |
| rtk_stp_mstpState_get(unit, 0, rtk_port, &stp_state); // MSTI 0 (CIST) | |
| const char *state_str = "unknown"; | |
| switch(stp_state) { | |
| case STP_STATE_DISABLED: state_str = "disabled"; break; | |
| case STP_STATE_BLOCKING: state_str = "blocking"; break; | |
| case STP_STATE_LEARNING: state_str = "learning"; break; | |
| case STP_STATE_FORWARDING: state_str = "forwarding"; break; | |
| default: state_str = "unknown"; break; | |
| } | |
| char stp_entry[256]; | |
| snprintf(stp_entry, sizeof(stp_entry), | |
| "%s{\"port\":\"%d\",\"state\":\"%s\"}", | |
| web_port > 1 ? "," : "", web_port, state_str); | |
| if (strlen(stp_configs) + strlen(stp_entry) < sizeof(stp_configs) - 10) { | |
| strcat(stp_configs, stp_entry); | |
| } | |
| } | |
| strcat(stp_configs, "]"); | |
| snprintf(json_response, sizeof(json_response), "%s", stp_configs); | |
| build_json_response(client_socket, json_response); | |
| } | |
| void handle_stp_config_set_request(int client_socket, const char *body) { | |
| int port_num; | |
| char stp_state[16]; | |
| if (extract_json_int(body, "port", &port_num) != 0 || | |
| extract_json_string(body, "state", stp_state, sizeof(stp_state)) != 0) { | |
| send_error_response(client_socket, 400, "Missing required fields"); | |
| return; | |
| } | |
| if (port_num < 1 || port_num > 10) { | |
| send_error_response(client_socket, 400, "Invalid port number"); | |
| return; | |
| } | |
| uint32 unit = 0; | |
| rtk_port_t rtk_port = port_num + 7; | |
| rtk_stp_state_t state = STP_STATE_FORWARDING; | |
| // Convert state string to RTK enum | |
| if (strcmp(stp_state, "disabled") == 0) { | |
| state = STP_STATE_DISABLED; | |
| } else if (strcmp(stp_state, "blocking") == 0) { | |
| state = STP_STATE_BLOCKING; | |
| } else if (strcmp(stp_state, "learning") == 0) { | |
| state = STP_STATE_LEARNING; | |
| } else if (strcmp(stp_state, "forwarding") == 0) { | |
| state = STP_STATE_FORWARDING; | |
| } | |
| // Set STP state using RTK | |
| int32 ret = rtk_stp_mstpState_set(unit, 0, rtk_port, state); // MSTI 0 (CIST) | |
| if (ret == RT_ERR_OK) { | |
| char response[128]; | |
| snprintf(response, sizeof(response), | |
| "{\"message\":\"STP state set to %s for port %d\"}", stp_state, port_num); | |
| send_success_response(client_socket, response); | |
| } else { | |
| send_error_response(client_socket, 500, "Failed to set STP state"); | |
| } | |
| } | |
| // ============================================================================================================================================================ | |
| // ROUTE Handlers | |
| // ============================================================================================================================================================== | |
| void handle_backend_request(int client_socket) | |
| { | |
| char buffer[BUFFER_SIZE]; | |
| int bytes_read = read(client_socket, buffer, sizeof(buffer) - 1); | |
| if (bytes_read <= 0) | |
| { | |
| close(client_socket); | |
| return; | |
| } | |
| buffer[bytes_read] = '\0'; | |
| char method[16], path[256]; | |
| char *body = ""; | |
| char *query = ""; | |
| // Use helper function to parse HTTP request | |
| if (parse_http_request(buffer, method, path, &body, &query) != 0) { | |
| send_error_response(client_socket, 400, "Invalid HTTP request format"); | |
| close(client_socket); | |
| return; | |
| } | |
| printf("Incoming request: %s %s\n", method, path); | |
| if (strcmp(method, "OPTIONS") == 0) | |
| { | |
| const char *options_response = "HTTP/1.1 204 No Content\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Access-Control-Allow-Methods: GET, POST, DELETE, PATCH, OPTIONS\r\n" | |
| "Access-Control-Allow-Headers: Content-Type\r\n\r\n"; | |
| send(client_socket, options_response, strlen(options_response), 0); | |
| close(client_socket); | |
| return; | |
| } | |
| if (strcmp(method, "GET") == 0 && strcmp(path, "/api/health_check") == 0) | |
| { | |
| handle_health_check_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/health") == 0) | |
| { | |
| handle_health_check_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/port-status") == 0) | |
| { | |
| handle_port_status_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/industrial-status") == 0) | |
| { | |
| handle_industrial_status_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/system-config") == 0) | |
| { | |
| handle_system_config_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/system-config") == 0) | |
| { | |
| handle_system_config_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/reboot") == 0) | |
| { | |
| handle_reboot_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/ping") == 0) | |
| { | |
| handle_ping_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/upload") == 0) | |
| { | |
| handle_upload_request(client_socket, buffer, bytes_read); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/poe-config") == 0) | |
| { | |
| handle_poe_config_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/user-management") == 0) | |
| { | |
| handle_user_management_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/user-management") == 0) | |
| { | |
| handle_user_management_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/user-management") == 0) | |
| { | |
| handle_user_management_delete_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/user-safety") == 0) | |
| { | |
| handle_user_safety_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/user-safety") == 0) | |
| { | |
| handle_user_safety_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/sntp-config") == 0) | |
| { | |
| handle_sntp_config_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/sntp-config") == 0) | |
| { | |
| handle_sntp_config_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/jumbo-frame") == 0) | |
| { | |
| handle_jumbo_frame_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/jumbo-frame") == 0) | |
| { | |
| handle_jumbo_frame_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/save-config") == 0) | |
| { | |
| handle_save_config_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/download-config") == 0) | |
| { | |
| handle_download_config_request(client_socket); | |
| } | |
| else if (strcmp(method, "DELETE") == 0 && strcmp(path, "/api/delete-config") == 0) | |
| { | |
| handle_delete_config_request(client_socket); | |
| } | |
| // else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/port-config") == 0) | |
| // { | |
| // handle_port_config_get_request(client_socket); | |
| // } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/port-config") == 0) | |
| { | |
| handle_port_config_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/port_stats") == 0) | |
| { | |
| handle_port_stats_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/get_port_stats") == 0) | |
| { | |
| handle_get_port_stats_request(client_socket, query); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/storm-control") == 0) | |
| { | |
| handle_storm_control_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/storm-control") == 0) | |
| { | |
| handle_storm_control_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/flow-control") == 0) | |
| { | |
| handle_flow_control_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/flow-control") == 0) | |
| { | |
| handle_flow_control_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/port-rate-limit") == 0) | |
| { | |
| handle_port_rate_limit_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/port-rate-limit") == 0) | |
| { | |
| handle_port_rate_limit_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/protected-port") == 0) | |
| { | |
| handle_protected_port_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/protected-port") == 0) | |
| { | |
| handle_protected_port_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/learn-limit") == 0) | |
| { | |
| handle_learn_limit_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/learn-limit") == 0) | |
| { | |
| handle_learn_limit_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/snmp-community") == 0) | |
| { | |
| handle_snmp_community_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/snmp-community") == 0) | |
| { | |
| handle_snmp_community_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/snmp-community") == 0) | |
| { | |
| handle_snmp_community_delete_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/snmp-community") == 0) | |
| { | |
| handle_snmp_community_patch_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/trap-targets") == 0) | |
| { | |
| handle_trap_targets_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/trap-targets") == 0) | |
| { | |
| handle_trap_targets_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/trap-targets") == 0) | |
| { | |
| handle_trap_targets_delete_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/qos-config") == 0) | |
| { | |
| handle_qos_config_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/qos-config") == 0) | |
| { | |
| handle_qos_config_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/get_qos_schedule") == 0) | |
| { | |
| handle_get_qos_schedule_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/set_qos_schedule") == 0) | |
| { | |
| handle_set_qos_schedule_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/factory-reset") == 0) | |
| { | |
| handle_factory_reset_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/serial-config") == 0) | |
| { | |
| handle_serial_config_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/port_stats") == 0) | |
| { | |
| handle_port_stats_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/acl-group") == 0) | |
| { | |
| handle_acl_group_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/extended-port-options") == 0) | |
| { | |
| handle_extended_port_options_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/get-acl-standard-ip") == 0) | |
| { | |
| handle_get_acl_standard_ip_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/get-extended-ip") == 0) | |
| { | |
| handle_get_extended_ip_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/get-acl-mac-ip") == 0) | |
| { | |
| handle_get_acl_mac_ip_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/get-acl-mac-arp") == 0) | |
| { | |
| handle_get_acl_mac_arp_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/get-qos-apply") == 0) | |
| { | |
| handle_get_qos_apply_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/get-qos-schedule") == 0) | |
| { | |
| handle_get_qos_schedule_request(client_socket); | |
| } | |
| // MAC table routes | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/mac-bind-table") == 0) | |
| { | |
| handle_mac_bind_table_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/mac-auto-bind") == 0) | |
| { | |
| handle_mac_auto_bind_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/mac-auto-filter") == 0) | |
| { | |
| handle_mac_auto_filter_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/mac-filter") == 0) | |
| { | |
| handle_mac_filter_get_request(client_socket); | |
| } | |
| // RMON routes | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/rmon-statistics") == 0) | |
| { | |
| handle_rmon_statistics_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/rmon-statistics") == 0) | |
| { | |
| handle_rmon_statistics_pFost_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/rmon-statistics/delete") == 0) | |
| { | |
| handle_rmon_statistics_delete_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/rmon-history") == 0) | |
| { | |
| handle_rmon_history_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/rmon-history") == 0) | |
| { | |
| handle_rmon_history_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/rmon-history/delete") == 0) | |
| { | |
| handle_rmon_history_delete_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/rmon-alarm") == 0) | |
| { | |
| handle_rmon_alarm_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/rmon-alarm") == 0) | |
| { | |
| handle_rmon_alarm_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/rmon-alarm/delete") == 0) | |
| { | |
| handle_rmon_alarm_delete_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/rmon-event") == 0) | |
| { | |
| handle_rmon_event_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/rmon-event") == 0) | |
| { | |
| handle_rmon_event_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/rmon-event/delete") == 0) | |
| { | |
| handle_rmon_event_delete_request(client_socket, body); | |
| } | |
| // IGMP Snooping routes | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/igmp-snooping-config") == 0) | |
| { | |
| handle_igmp_snooping_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/igmp-snooping-config") == 0) | |
| { | |
| handle_igmp_snooping_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/multicast-group-info") == 0) | |
| { | |
| handle_multicast_group_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/multicast-group-info") == 0) | |
| { | |
| handle_multicast_group_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "DELETE") == 0 && strcmp(path, "/api/multicast-group-info") == 0) | |
| { | |
| handle_multicast_group_delete_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/add-acl-standard-ip") == 0) | |
| { | |
| handle_add_acl_standard_ip_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/add-acl-extended-ip") == 0) | |
| { | |
| handle_add_acl_extended_ip_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/add-acl-mac-ip") == 0) | |
| { | |
| handle_add_acl_mac_ip_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/add-acl-mac-arp") == 0) | |
| { | |
| handle_add_acl_mac_arp_request(client_socket, body); | |
| } | |
| // MAC table POST and DELETE routes | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/mac-bind-table") == 0) | |
| { | |
| handle_mac_bind_table_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "DELETE") == 0 && strcmp(path, "/api/mac-bind-table") == 0) | |
| { | |
| handle_mac_bind_table_delete_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/qos-apply") == 0) | |
| { | |
| handle_qos_apply_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/add-qos-schedule") == 0) | |
| { | |
| handle_add_qos_schedule_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/get_vlan_interfaces") == 0) | |
| { | |
| print_all_vlan_interfaces_json(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/set_vlan_port") == 0) | |
| { | |
| handle_vlan_port_tag_action_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/get_vlan_information") == 0) | |
| { | |
| handle_get_vlan_members_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/set_vlan_information") == 0) | |
| { | |
| handle_set_vlan_members_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/vlan_port_action") == 0) | |
| { | |
| handle_vlan_port_tag_action_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/destroy_vlan") == 0) | |
| { | |
| handle_destroy_vlan_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/create_vlan") == 0) | |
| { | |
| handle_create_vlan_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/add_create_vlan_interface") == 0) | |
| { | |
| handle_create_vlan_interface_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/delete_vlanIp") == 0) | |
| { | |
| handle_delete_vlanip_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/get_vlanIp") == 0) | |
| { | |
| handle_get_vlan_ip_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/destroy_vlan_interface") == 0) | |
| { | |
| handle_destroy_vlan_interface_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/update_vlanIp") == 0) | |
| { | |
| handle_update_vlanip_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/set_switch_mgmt_macaddr") == 0) | |
| { | |
| handle_set_switch_mgmt_macaddr_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/add-lldp-global") == 0) | |
| { | |
| handle_add_lldp_global_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/apply-lldp-port") == 0) | |
| { | |
| handle_apply_lldp_port_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/get-lldp-global") == 0) | |
| { | |
| handle_get_lldp_global_request(client_socket); | |
| } | |
| // New routes from backend.js | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/snmp-versions") == 0) | |
| { | |
| handle_snmp_versions_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/port-trunking") == 0) | |
| { | |
| handle_port_trunking_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/set-trunk-method") == 0) | |
| { | |
| handle_set_trunk_method_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/create-trunk-group") == 0) | |
| { | |
| handle_create_trunk_group_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/add-member-port") == 0) | |
| { | |
| handle_add_member_port_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/remove-member-port") == 0) | |
| { | |
| handle_remove_member_port_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/delete-trunk-group") == 0) | |
| { | |
| handle_delete_trunk_group_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/get-mirror-ports") == 0) | |
| { | |
| handle_get_mirror_ports_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/apply-mirror-config") == 0) | |
| { | |
| handle_apply_mirror_config_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/get-mirror-config") == 0) | |
| { | |
| handle_get_mirror_config_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/get-ddm-status") == 0) | |
| { | |
| handle_get_ddm_status_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/arp-config-display") == 0) | |
| { | |
| handle_arp_config_display_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/arp-config-display") == 0) | |
| { | |
| handle_arp_config_display_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "DELETE") == 0 && strncmp(path, "/api/arp-config-display/", 24) == 0) | |
| { | |
| handle_arp_config_display_delete_request(client_socket, path + 24); | |
| } | |
| else if (strcmp(method, "PUT") == 0 && strstr(path, "/api/arp-config-display/convert/")) | |
| { | |
| handle_arp_config_display_convert_request(client_socket, query); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/cluster-config") == 0) | |
| { | |
| handle_cluster_config_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/cluster-config") == 0) | |
| { | |
| handle_cluster_config_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/cluster-members") == 0) | |
| { | |
| handle_cluster_members_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/host-static-route") == 0) | |
| { | |
| handle_host_static_route_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/host-static-route") == 0) | |
| { | |
| handle_host_static_route_post_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "DELETE") == 0 && strcmp(path, "/api/host-static-route") == 0) | |
| { | |
| handle_host_static_route_delete_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/get-cable-diagnosis") == 0) | |
| { | |
| handle_get_cable_diagnosis_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/run-cable-diagnosis") == 0) | |
| { | |
| handle_run_cable_diagnosis_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/get-cable-diagnosis-data") == 0) | |
| { | |
| handle_get_cable_diagnosis_data_request(client_socket); | |
| } | |
| // Additional routes from backend.js | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/set_qos_schedule") == 0) | |
| { | |
| handle_get_qos_schedule_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/set_qos_schedule") == 0) | |
| { | |
| handle_set_qos_schedule_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/mac-address-table") == 0) | |
| { | |
| handle_mac_address_table_get_request(client_socket); | |
| } | |
| // else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/port-config") == 0) | |
| // { | |
| // handle_port_config_get_request(client_socket); | |
| // } | |
| // else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/port-config") == 0) | |
| // { | |
| // handle_port_config_set_request(client_socket, body); | |
| // } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/rate-limit") == 0) | |
| { | |
| handle_rate_limit_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/rate-limit") == 0) | |
| { | |
| handle_rate_limit_set_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/port-statistics") == 0) | |
| { | |
| handle_port_statistics_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/port-statistics/clear") == 0) | |
| { | |
| handle_port_statistics_clear_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/stp-config") == 0) | |
| { | |
| handle_stp_config_get_request(client_socket); | |
| } | |
| else if (strcmp(method, "POST") == 0 && strcmp(path, "/api/stp-config") == 0) | |
| { | |
| handle_stp_config_set_request(client_socket, body); | |
| } | |
| else if (strcmp(method, "GET") == 0 && strcmp(path, "/api/exit") == 0) | |
| { | |
| extern volatile int server_running; | |
| server_running = 0; | |
| const char *exit_response = "HTTP/1.1 200 OK\r\n" | |
| "Access-Control-Allow-Origin: *\r\n" | |
| "Content-Type: application/json\r\n\r\n" | |
| "{\"message\": \"Server shutting down\"}"; | |
| send(client_socket, exit_response, strlen(exit_response), 0); | |
| close(client_socket); | |
| return; | |
| } | |
| else | |
| { | |
| handle_not_found(client_socket); | |
| } | |
| close(client_socket); // Close the connection after handling | |
| } | |
| // ============================================================================================================================================================ | |
| // HTTP BACKEND SERVER | |
| // ============================================================================================================================================================== | |
| void *start_backend_server(void *arg) | |
| { | |
| int server_fd, new_socket; | |
| struct sockaddr_in address; | |
| int opt = 1; | |
| socklen_t addrlen = sizeof(address); | |
| if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) | |
| { | |
| perror("Socket creation error"); | |
| return NULL; | |
| } | |
| if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) | |
| { | |
| perror("Set socket options error"); | |
| return NULL; | |
| } | |
| address.sin_family = AF_INET; | |
| address.sin_addr.s_addr = INADDR_ANY; | |
| address.sin_port = htons(BACKENDPORT); | |
| if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) | |
| { | |
| perror("Bind failed"); | |
| return NULL; | |
| } | |
| if (listen(server_fd, 3) < 0) | |
| { | |
| perror("Listen failed"); | |
| return NULL; | |
| } | |
| printf("Socket server listening on port %d...\n", BACKENDPORT); | |
| // while (1) | |
| while (server_running) | |
| { | |
| if ((new_socket = accept(server_fd, (struct sockaddr *)&address, &addrlen)) < 0) | |
| { | |
| perror("Accept failed"); | |
| continue; | |
| } | |
| handle_backend_request(new_socket); // Handle HTTP requests | |
| } | |
| close(server_fd); | |
| return NULL; | |
| } | |
| // ============================================================================================================================================================ | |
| // MAIN FUNCTION | |
| // ============================================================================================================================================================== | |
| /* | |
| * Function Declaration | |
| */ | |
| int diag_main(int argc, char **argv) | |
| { | |
| pthread_t backend_thread; | |
| cparser_t *pParser; | |
| #ifdef CONFIG_SDK_KERNEL_LINUX | |
| int status, result; | |
| #endif | |
| #ifdef CONFIG_RISE | |
| #if RISE_IN_TURNKEY | |
| /* no wait here */ | |
| #else | |
| // just example: wait RISE kickoff before prompt diag-shell, please remove when you call rise_application_kickoff() in your code. | |
| { | |
| uint32 box_list; | |
| rise_application_kickoff(&box_list); | |
| // diag_util_printf("box_list=0x%08x\n",box_list); | |
| // diag_util_printf("\n"); | |
| } | |
| #endif /* RISE_TURNKEY_PORTING */ | |
| #endif | |
| // Initialize global data store | |
| initialize_global_data(); | |
| /* Process diag options */ | |
| if (argc > 1) | |
| { | |
| /* -d: debug option to print all chip debug informations */ | |
| if (0 == strcmp(argv[1], "-d")) | |
| { | |
| diag_util_mprintf_paging_lines(0); | |
| diag_debug_dump(); | |
| return 0; | |
| } | |
| } | |
| pParser = osal_alloc(sizeof(cparser_t)); | |
| if (NULL == pParser) | |
| { | |
| diag_util_printf("osal_alloc cparser_t structure failed!!\n"); | |
| return -1; | |
| } | |
| osal_memset(pParser, 0, sizeof(cparser_t)); | |
| pParser->cfg.root = &cparser_root; | |
| pParser->cfg.ch_complete = '\t'; | |
| /* | |
| * Instead of making sure the terminal setting of the target and | |
| * the host are the same. ch_erase and ch_del both are treated | |
| * as backspace. | |
| */ | |
| pParser->cfg.ch_erase = '\b'; | |
| pParser->cfg.ch_del = 127; | |
| pParser->cfg.ch_help = '?'; | |
| pParser->cfg.flags = 0; | |
| pParser->cfg.fd = STDOUT_FILENO; | |
| strcpy(pParser->cfg.str_remark, "//"); | |
| if (dlag_initial_promt_unit_id(pParser) != RT_ERR_OK) | |
| { | |
| osal_free(pParser); | |
| return -1; | |
| } | |
| cparser_io_config(pParser); | |
| /* Initialization */ | |
| if (CPARSER_OK != cparser_init(&(pParser->cfg), pParser)) | |
| { | |
| diag_util_printf("Fail to initialize parser.\n"); | |
| osal_free(pParser); | |
| return -1; | |
| } | |
| #ifdef CONFIG_SDK_KERNEL_LINUX | |
| result = fork(); | |
| if (result == 0) | |
| { | |
| // child process | |
| execl("/bin/cp", "cp", "/proc/meminfo", "/tmp/mem_log_rtsdk_diag", (char *)NULL); | |
| exit(errno); | |
| } | |
| if (result == -1) | |
| { | |
| diag_util_printf("Warning: failed to fork process\n"); | |
| } | |
| else | |
| { | |
| wait(&status); // equal to waitpid(-1, &status, 0); | |
| if (status == -1) | |
| { | |
| diag_util_printf("Warning: failed to log memory usage\n"); | |
| } | |
| } | |
| #endif | |
| /* Main command loop */ | |
| cparser_run(pParser); | |
| diag_util_printf("\n"); | |
| osal_free(pParser); | |
| if (pthread_create(&backend_thread, NULL, start_backend_server, NULL) != 0) | |
| { | |
| perror("Error creating socket server thread"); | |
| return -1; | |
| } | |
| pthread_join(backend_thread, NULL); | |
| // Cleanup mutex | |
| pthread_mutex_destroy(&global_data_mutex); | |
| return 0; | |
| } /* end of diag_main */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment