diff --git a/CMakeLists.txt b/CMakeLists.txt index 17ee50e0..86a4ba9b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,22 +6,3 @@ add_subdirectory(libbbfdm-api) add_subdirectory(libbbfdm) add_subdirectory(bbfdmd) -# Capture the environment variables -set(MY_CC "$ENV{CC}") -set(MY_CFLAGS "$ENV{CFLAGS}") -set(MY_LDFLAGS "$ENV{LDFLAGS}") - -# Define a custom target to build the utilities using Makefile -add_custom_target( - build_utilities - COMMAND ${CMAKE_COMMAND} -E env - CC=${MY_CC} - CFLAGS=${MY_CFLAGS} - LDFLAGS=${MY_LDFLAGS} - make - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/utilities -) - -# Make bbfdmd target depend on the build_utilities target -add_dependencies(bbfdmd build_utilities) - diff --git a/libbbfdm/dmtree/tr181/ip.c b/libbbfdm/dmtree/tr181/ip.c index 830b748c..9ce30200 100644 --- a/libbbfdm/dmtree/tr181/ip.c +++ b/libbbfdm/dmtree/tr181/ip.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 iopsys Software Solutions AB + * Copyright (C) 2020-2024 iopsys Software Solutions AB * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 2.1 @@ -7,6 +7,7 @@ * * Author: Anis Ellouze * Author: Amin Ben Ramdhane + * Author: Mohd Husaam Mehdi * */ @@ -23,6 +24,16 @@ struct intf_ip_args bool is_main_sec; }; +#define STATUS_SIZE 16 + +typedef struct { + char local_ip[INET6_ADDRSTRLEN]; + uint16_t local_port; + char remote_ip[INET6_ADDRSTRLEN]; + uint16_t remote_port; + unsigned int state; +} ActivePort; + /************************************************************* * INIT **************************************************************/ @@ -38,6 +49,82 @@ static int init_interface_ip_args(struct intf_ip_args *args, struct uci_section /************************************************************* * COMMON Functions **************************************************************/ +static void format_ipv6_address(const char *hex_str_ip, char *ipv6_addr) +{ + struct in6_addr addr = {}; + + sscanf(hex_str_ip, "%08X%08X%08X%08X", + &addr.s6_addr32[0], &addr.s6_addr32[1], + &addr.s6_addr32[2], &addr.s6_addr32[3]); + + // Convert the address to the standard IPv6 format + inet_ntop(AF_INET6, &addr, ipv6_addr, INET6_ADDRSTRLEN); +} + +static void parse_tcp_line(const char* line, int is_ipv6, ActivePort* port) +{ + unsigned int local_port, remote_port; + unsigned int state; + char local_ip[INET6_ADDRSTRLEN] = {0}; + char remote_ip[INET6_ADDRSTRLEN] = {0}; + + if (is_ipv6) { + char local_ip6[33] = {0}, remote_ip6[33] = {0}; + sscanf(line, "%*d: %32s:%4X %32s:%4X %2X", local_ip6, &local_port, remote_ip6, &remote_port, &state); + format_ipv6_address(local_ip6, local_ip); + format_ipv6_address(remote_ip6, remote_ip); + } else { + unsigned int local_ip_num, remote_ip_num; + sscanf(line, "%*d: %8X:%4X %8X:%4X %2X", &local_ip_num, &local_port, &remote_ip_num, &remote_port, &state); + + struct in_addr local_addr = { local_ip_num }; + struct in_addr remote_addr = { remote_ip_num }; + + inet_ntop(AF_INET, &local_addr, local_ip, INET_ADDRSTRLEN); + inet_ntop(AF_INET, &remote_addr, remote_ip, INET_ADDRSTRLEN); + } + + DM_STRNCPY(port->local_ip, local_ip, INET6_ADDRSTRLEN); + port->local_port = local_port; + DM_STRNCPY(port->remote_ip, remote_ip, INET6_ADDRSTRLEN); + port->remote_port = remote_port; + port->state = state; +} + +static void browse_ip_port(struct dmctx *dmctx, DMNODE *parent_node, bool is_ipv6, const char *proc_path, int *id, char *inst) +{ + if (proc_path == NULL || DM_STRLEN(proc_path) == 0) + return; + + FILE* fp = fopen(proc_path, "r"); + if (fp == NULL) { + return; + } + + char line[256] = {0}; + fgets(line, sizeof(line), fp); // Skip header line + + while (fgets(line, sizeof(line), fp)) { + struct dm_data curr_data = {0}; + + ActivePort port; + memset(&port, 0, sizeof(port)); + parse_tcp_line(line, is_ipv6, &port); + + // only display LISTEN or ESTABLISHED + if (port.state != 1 && port.state != 10) + continue; + + curr_data.additional_data = (void *)(&port); + inst = handle_instance_without_section(dmctx, parent_node, ++(*id)); + + if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&curr_data, inst) == DM_STOP) + break; + } + + fclose(fp); +} + static int get_sysctl_disable_ipv6_per_device(const char *device, char **value) { char file[256]; @@ -808,6 +895,17 @@ end: return 0; } +static int browseIPActivePortInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance) +{ + char *inst = NULL; + int id = 0; + + browse_ip_port(dmctx, parent_node, 0, "/proc/net/tcp", &id, inst); + browse_ip_port(dmctx, parent_node, 1, "/proc/net/tcp6", &id, inst); + + return 0; +} + /************************************************************* * ADD & DEL OBJ **************************************************************/ @@ -1088,6 +1186,13 @@ static int get_IP_InterfaceNumberOfEntries(char *refparam, struct dmctx *ctx, vo return 0; } +static int get_IP_ActivePortNumberOfEntries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + int cnt = get_number_of_entries(ctx, data, instance, browseIPActivePortInst); + dmasprintf(value, "%d", cnt); + return 0; +} + /*#Device.IP.Interface.{i}.Enable!UCI:network/interface,@i-1/disabled*/ static int get_IPInterface_Enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) { @@ -2180,6 +2285,48 @@ static int operate_IPInterface_Reset(char *refparam, struct dmctx *ctx, void *da return 0; } +static int get_IP_ActivePort_LocalIPAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + *value = dmstrdup(((ActivePort *)((struct dm_data *)data)->additional_data)->local_ip); + return 0; +} + +static int get_IP_ActivePort_LocalPort(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + dmasprintf(value, "%u", ((ActivePort *)((struct dm_data *)data)->additional_data)->local_port); + return 0; +} + +static int get_IP_ActivePort_RemoteIPAddress(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + *value = dmstrdup(((ActivePort *)((struct dm_data *)data)->additional_data)->remote_ip); + return 0; +} + +static int get_IP_ActivePort_RemotePort(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + dmasprintf(value, "%u", ((ActivePort *)((struct dm_data *)data)->additional_data)->remote_port); + return 0; +} + +static int get_IP_ActivePort_Status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value) +{ + unsigned int state = (((ActivePort *)((struct dm_data *)data)->additional_data)->state); + + switch (state) { + case 1: + *value = "ESTABLISHED"; + break; + case 10: + *value = "LISTEN"; + break; + default: + *value = ""; + break; + } + + return 0; +} /********************************************************************************************************************************** * OBJ & PARAM DEFINITION ***********************************************************************************************************************************/ @@ -2187,6 +2334,7 @@ static int operate_IPInterface_Reset(char *refparam, struct dmctx *ctx, void *da DMOBJ tIPObj[] = { /* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/ {"Interface", &DMWRITE, addObjIPInterface, delObjIPInterface, NULL, browseIPInterfaceInst, NULL, NULL, tIPInterfaceObj, tIPInterfaceParams, NULL, BBFDM_BOTH, NULL}, +{"ActivePort", &DMREAD, NULL, NULL, NULL, browseIPActivePortInst, NULL, NULL, NULL, tIPActivePortParams, NULL, BBFDM_BOTH, NULL}, #if defined(BBF_TR143) || defined(BBF_TR471) {"Diagnostics", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, tIPDiagnosticsObj, tIPDiagnosticsParams, NULL, BBFDM_BOTH, NULL}, #endif @@ -2203,6 +2351,7 @@ DMLEAF tIPParams[] = { {"IPv6Status", &DMREAD, DMT_STRING, get_IP_IPv6Status, NULL, BBFDM_BOTH}, {"ULAPrefix", &DMWRITE, DMT_STRING, get_IP_ULAPrefix, set_IP_ULAPrefix, BBFDM_BOTH}, {"InterfaceNumberOfEntries", &DMREAD, DMT_UNINT, get_IP_InterfaceNumberOfEntries, NULL, BBFDM_BOTH}, +{"ActivePortNumberOfEntries", &DMREAD, DMT_UNINT, get_IP_ActivePortNumberOfEntries, NULL, BBFDM_BOTH}, {0} }; @@ -2307,3 +2456,14 @@ DMLEAF tIPInterfaceStatsParams[] = { //{"UnknownProtoPacketsReceived", &DMREAD, DMT_UNINT, get_IPInterfaceStats_UnknownProtoPacketsReceived, NULL, BBFDM_BOTH}, {0} }; + +/* *** Device.IP.ActivePort.{i}. *** */ +DMLEAF tIPActivePortParams[] = { +/* PARAM, permission, type, getvalue, setvalue, bbfdm_type */ +{"LocalIPAddress", &DMREAD, DMT_STRING, get_IP_ActivePort_LocalIPAddress, NULL, BBFDM_BOTH}, +{"LocalPort", &DMREAD, DMT_UNINT, get_IP_ActivePort_LocalPort, NULL, BBFDM_BOTH}, +{"RemoteIPAddress", &DMREAD, DMT_STRING, get_IP_ActivePort_RemoteIPAddress, NULL, BBFDM_BOTH}, +{"RemotePort", &DMREAD, DMT_UNINT, get_IP_ActivePort_RemotePort, NULL, BBFDM_BOTH}, +{"Status", &DMREAD, DMT_STRING, get_IP_ActivePort_Status, NULL, BBFDM_BOTH}, +{0} +}; diff --git a/libbbfdm/dmtree/tr181/ip.h b/libbbfdm/dmtree/tr181/ip.h index 826d261d..8aac50d9 100644 --- a/libbbfdm/dmtree/tr181/ip.h +++ b/libbbfdm/dmtree/tr181/ip.h @@ -23,6 +23,7 @@ extern DMLEAF tIPInterfaceIPv4AddressParams[]; extern DMLEAF tIPInterfaceIPv6AddressParams[]; extern DMLEAF tIPInterfaceIPv6PrefixParams[]; extern DMLEAF tIPInterfaceStatsParams[]; +extern DMLEAF tIPActivePortParams[]; #endif //__IP_H diff --git a/utilities/Makefile b/utilities/Makefile deleted file mode 100644 index 2ba6a66d..00000000 --- a/utilities/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -CC ?= gcc -ACTIVE-PORT = active-port -ACTIVE-PORT_OBJS = src/ubus/active-port.o - -PROG_CFLAGS = $(CFLAGS) -Wall -Werror -PROG_LDFLAGS = $(LDFLAGS) -PROG_LIBS += -luci -lubus -lubox -lblobmsg_json - -INSTALL_DIR = /usr/sbin - -%.o: %.c - $(CC) $(PROG_CFLAGS) $(FPIC) -c -o $@ $< - -.PHONY: all clean install - -all: $(ACTIVE-PORT) - -$(ACTIVE-PORT): $(ACTIVE-PORT_OBJS) - $(CC) $(PROG_LDFLAGS) -o $@ $^ $(PROG_LIBS) - -clean: - rm -f $(ACTIVE-PORT) - -install: $(ACTIVE-PORT) - install -m 0755 $(ACTIVE-PORT) $(INSTALL_DIR) diff --git a/utilities/README.md b/utilities/README.md deleted file mode 100644 index 6f629d0b..00000000 --- a/utilities/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# Utilities to support datamodel deployments - -## active-port - -This directory has active-port utility which implements active-port ubus object. This object contains one function: dump, which dumps information of all active tcp ports. - -```bash -root@sh31b-f04dd44304e0:~# ubus call active-port dump -{ - "connections": [ - { - "local_ip": "127.0.0.1", - "local_port": "5038", - "remote_ip": "0.0.0.0", - "remote_port": "*", - "status": "LISTEN" - }, - { - "local_ip": "0.0.0.0", - "local_port": "8080", - "remote_ip": "0.0.0.0", - "remote_port": "*", - "status": "LISTEN" - }, - ... - ] -} -``` - -## Usage - -This ubus call is used by Device.IP.ActivePort datamodel object. diff --git a/utilities/src/ubus/active-port.c b/utilities/src/ubus/active-port.c deleted file mode 100644 index fa3dfef9..00000000 --- a/utilities/src/ubus/active-port.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * active_port.c: active-port daemon that provides active-port ubus object - * and a dump method which read from netstat output and - * returns local and remote IP and port and connections state. - * This is done for Device.IP.ActivePort object, whose json - * maps to this ubus call. - * - * Copyright (C) 2024 IOPSYS Software Solutions AB. All rights reserved. - * - * Author: Mohd Husaam Mehdi - * - * See LICENSE file for license related information. - */ - -#include -#include -#include -#include -#include -#include - -#define MAX_LINE_LEN 256 - -static int parse_ip_port(char *str, char **ip, char **port) -{ - char *colon = NULL; - - // get the last colon in string - colon = strrchr(str, ':'); - if (!colon) { - syslog(LOG_ERR, "active-port(%s,%u): ERROR (ip port not separated by ':')\n", __func__, __LINE__); - return 1; - } - - *colon = '\0'; - *ip = str; - *port = colon + 1; - - return 0; -} - -static const struct blobmsg_policy active_port_policy[] = {}; - -static int parse_line(char *line, struct blob_buf *b) -{ - char *local_ip = NULL; - char *local_port = NULL; - char *remote_ip = NULL; - char *remote_port = NULL; - bool established = false; - - char *token = NULL, *end = NULL; - // split the columns - token = strtok_r(line, " ", &end); - int i = 0; - while (token != NULL) { - switch (i) { - // first column - case 0: - if (parse_ip_port(token, &local_ip, &local_port) != 0) - return 1; - - i++; - break; - // second column - case 1: - - if (parse_ip_port(token, &remote_ip, &remote_port) != 0) - return 1; - - i++; - break; - case 2: - // if established is found strncmp would be 0 and established would be 1 - established = !strncmp(token, "ESTABLISHED", strlen("ESTABLISHED")); - i++; - break; - - default: - syslog(LOG_ERR, "active-port(%s,%u): ERROR (extra column in netstat)\n", __func__, __LINE__); - return 1; - } - - token = strtok_r(NULL, " ", &end); - } - - if (!local_ip || !local_port || !remote_ip || !remote_port) { - syslog(LOG_ERR, "active-port(%s,%u): ERROR (missing data in netstat)\n", __func__, __LINE__); - return 1; - } - - void *dd = NULL; - - dd = blobmsg_open_table(b, ""); - - blobmsg_add_string(b, "local_ip", local_ip); - blobmsg_add_string(b, "local_port", local_port); - blobmsg_add_string(b, "remote_ip", remote_ip); - blobmsg_add_string(b, "remote_port", remote_port); - blobmsg_add_string(b, "status", established ? "ESTABLISHED" : "LISTEN"); - - blobmsg_close_table(b, dd); - - return 0; -} - -static int active_port_dump_handler(struct ubus_context *ctx, struct ubus_object *obj __attribute__((unused)), - struct ubus_request_data *req, const char *method __attribute__((unused)), - struct blob_attr *msg) -{ - struct blob_buf b = {0}; - - memset(&b, 0, sizeof(struct blob_buf)); - blob_buf_init(&b, 0); - - FILE *pp = NULL; - char cmd[64] = {0}; - // get the output of netstat (do not resolve ip addresses, get tcp connections, all kinds of states) - // skip the first two rows(headers) and print the 4th, 5th and 6th column separated by single space - snprintf(cmd, sizeof(cmd), "netstat -nta 2>/dev/null | awk \'NR>2 {print $4, $5, $6}\'"); - - pp = popen(cmd, "r"); - if (pp != NULL) { - void *d = NULL; - d = blobmsg_open_array(&b, "connections"); - - char line[MAX_LINE_LEN] = {0}; - - while (fgets(line, MAX_LINE_LEN, pp) != NULL) { - // remove_new_line - line[strcspn(line, "\n")] = 0; - // stop parsing if there is a problem in one of the lines - if (parse_line(line, &b) != 0) - break; - } - - pclose(pp); - blobmsg_close_array(&b, d); - } else { - blobmsg_add_string(&b, "error", "Could not run netstat"); - goto end; - } - -end: - ubus_send_reply(ctx, req, b.head); - blob_buf_free(&b); - - return 0; -} - - -static const struct ubus_method active_port_methods[] = { - UBUS_METHOD("dump", active_port_dump_handler, active_port_policy), -}; - -static struct ubus_object_type active_port_object_type = UBUS_OBJECT_TYPE("active-port", active_port_methods); - -static struct ubus_object active_port_object = { - .name = "active-port", - .type = &active_port_object_type, - .methods = active_port_methods, - .n_methods = ARRAY_SIZE(active_port_methods), -}; - -int main(int argc, char **argv) -{ - struct ubus_context *uctx; - - openlog("active-port", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); - - uctx = ubus_connect(NULL); - if (uctx == NULL) { - printf("Can't create UBUS context"); - return -1; - } - - uloop_init(); - ubus_add_uloop(uctx); - - if (ubus_add_object(uctx, &active_port_object)) - goto exit; - - uloop_run(); - -exit: - uloop_done(); - ubus_free(uctx); - closelog(); - - return 0; -}