| #include <net-snmp/net-snmp-config.h> |
| #include <net-snmp/net-snmp-includes.h> |
| #include <net-snmp/agent/net-snmp-agent-includes.h> |
| |
| #include "sctpAssocTable.h" |
| #include "sctpAssocLocalAddrTable.h" |
| #include "sctpAssocRemAddrTable.h" |
| #include "sctpTables_common.h" |
| |
| #include <util_funcs.h> |
| |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <arpa/inet.h> |
| |
| /* |
| * Linux provides sctp statistics in /proc/net/sctp/assoc and |
| * /proc/net/sctp/remaddr. The 'assoc' file covers sctpAssocTable and |
| * sctpAssocLocalAddrTable, the later one contains sctpAssocRemAddrTable. |
| * |
| * Linux does *not* provide *StartTime timestamps. This implementation tries |
| * to guess these timestamps. |
| */ |
| |
| |
| #define PROC_PREFIX "/proc" |
| #define ASSOC_FILE PROC_PREFIX "/net/sctp/assocs" |
| #define REMADDR_FILE PROC_PREFIX "/net/sctp/remaddr" |
| |
| /* |
| * Convert string with ipv4 or ipv6 address to provided buffer. |
| */ |
| static int |
| convert_address(char *token, char *addr_buffer, u_long * addr_type, |
| u_long * addr_len) |
| { |
| int family; |
| int ret; |
| |
| if (strchr(token, ':') != NULL) { |
| family = AF_INET6; |
| *addr_type = INETADDRESSTYPE_IPV6; |
| *addr_len = 16; |
| } else { |
| family = AF_INET; |
| *addr_type = INETADDRESSTYPE_IPV4; |
| *addr_len = 4; |
| } |
| ret = inet_pton(family, token, addr_buffer); |
| |
| if (ret <= 0) |
| return SNMP_ERR_GENERR; |
| return SNMP_ERR_NOERROR; |
| } |
| |
| |
| /* |
| * Parse local address part from assoc file. It assumes that strtok will return |
| * these addresses. The addresses are separated by space and the list ends |
| * with "<->". |
| */ |
| static int |
| parse_assoc_local_addresses(sctpAssocTable_entry * entry, |
| sctpTables_containers * containers) |
| { |
| char *token; |
| int ret; |
| /* |
| * parse all local addresses |
| */ |
| |
| while ((token = strtok(NULL, " "))) { |
| sctpAssocLocalAddrTable_entry *localAddr; |
| char *ip = token; |
| |
| if (token[0] == '<') |
| break; /* local addresses finished */ |
| |
| if (token[0] == '*') |
| ip = token + 1; |
| |
| localAddr = sctpAssocLocalAddrTable_entry_create(); |
| if (localAddr == NULL) |
| return SNMP_ERR_GENERR; |
| |
| localAddr->sctpAssocId = entry->sctpAssocId; |
| ret = |
| convert_address(ip, localAddr->sctpAssocLocalAddr, |
| &localAddr->sctpAssocLocalAddrType, |
| &localAddr->sctpAssocLocalAddr_len); |
| if (ret != SNMP_ERR_NOERROR) { |
| sctpAssocLocalAddrTable_entry_free(localAddr); |
| return SNMP_ERR_GENERR; |
| } |
| |
| ret = |
| sctpAssocLocalAddrTable_add_or_update(containers-> |
| sctpAssocLocalAddrTable, |
| localAddr); |
| if (ret != SNMP_ERR_NOERROR) |
| return SNMP_ERR_GENERR; |
| } |
| return SNMP_ERR_NOERROR; |
| } |
| |
| /* |
| * Parse primary remote address part from assoc file. It assumes that strtok will return |
| * all remote addresses. The addresses are separated by space and the list ends with \t . |
| */ |
| static int |
| parse_assoc_remote_addresses(sctpAssocTable_entry * entry) |
| { |
| char *token; |
| int ret = SNMP_ERR_GENERR; |
| |
| while ((token = strtok(NULL, " ")) && (token[0] != '\t')) { |
| if (token[0] == '*') { |
| /* |
| * that's the primary address |
| */ |
| ret = |
| convert_address(token + 1, entry->sctpAssocRemPrimAddr, |
| &entry->sctpAssocRemPrimAddrType, |
| &entry->sctpAssocRemPrimAddr_len); |
| } |
| } |
| return ret; |
| } |
| |
| static int |
| parse_assoc_line(char *line, sctpTables_containers * containers) |
| { |
| unsigned long long inode; |
| char *token; |
| int ret; |
| sctpAssocTable_entry *entry; |
| |
| entry = sctpAssocTable_entry_create(); |
| if (entry == NULL) |
| return SNMP_ERR_GENERR; |
| |
| token = strtok(line, " "); /* ASSOC, ignore */ |
| token = strtok(NULL, " "); /* SOCK, ignore */ |
| token = strtok(NULL, " "); /* STY, ignore */ |
| token = strtok(NULL, " "); /* SST, ignore */ |
| token = strtok(NULL, " "); /* ST */ |
| if (token == NULL) { |
| ret = SNMP_ERR_GENERR; |
| goto error; |
| } |
| entry->sctpAssocState = strtol(token, NULL, 10); |
| |
| token = strtok(NULL, " "); /* HBKT */ |
| if (token == NULL) { |
| ret = SNMP_ERR_GENERR; |
| goto error; |
| } |
| entry->sctpAssocHeartBeatInterval = strtol(token, NULL, 10); |
| |
| token = strtok(NULL, " "); /* ASSOC-ID, store */ |
| if (token == NULL) { |
| ret = SNMP_ERR_GENERR; |
| goto error; |
| } |
| entry->sctpAssocId = strtol(token, NULL, 10); |
| |
| token = strtok(NULL, " "); /* TX_QUEUE, ignore */ |
| token = strtok(NULL, " "); /* RX_QUEUE, ignore */ |
| token = strtok(NULL, " "); /* UID, ignore */ |
| |
| token = strtok(NULL, " "); /* INODE */ |
| if (token == NULL) { |
| ret = SNMP_ERR_GENERR; |
| goto error; |
| } |
| inode = strtoull(token, NULL, 10); |
| entry->sctpAssocPrimProcess = get_pid_from_inode(inode); |
| |
| token = strtok(NULL, " "); /* LPORT */ |
| if (token == NULL) { |
| ret = SNMP_ERR_GENERR; |
| goto error; |
| } |
| entry->sctpAssocLocalPort = strtol(token, NULL, 10); |
| |
| token = strtok(NULL, " "); /* RPORT */ |
| if (token == NULL) { |
| ret = SNMP_ERR_GENERR; |
| goto error; |
| } |
| entry->sctpAssocRemPort = strtol(token, NULL, 10); |
| |
| ret = parse_assoc_local_addresses(entry, containers); |
| if (ret != SNMP_ERR_NOERROR) |
| goto error; |
| |
| ret = parse_assoc_remote_addresses(entry); |
| if (ret != SNMP_ERR_NOERROR) |
| goto error; |
| |
| token = strtok(NULL, " "); /* HBINT */ |
| if (token == NULL) { |
| ret = SNMP_ERR_GENERR; |
| goto error; |
| } |
| entry->sctpAssocHeartBeatInterval = strtol(token, NULL, 10); |
| |
| token = strtok(NULL, " "); /* INS */ |
| if (token == NULL) { |
| ret = SNMP_ERR_GENERR; |
| goto error; |
| } |
| entry->sctpAssocInStreams = strtol(token, NULL, 10); |
| |
| token = strtok(NULL, " "); /* OUTS */ |
| if (token == NULL) { |
| ret = SNMP_ERR_GENERR; |
| goto error; |
| } |
| entry->sctpAssocOutStreams = strtol(token, NULL, 10); |
| |
| token = strtok(NULL, " "); /* MAXRT */ |
| if (token == NULL) { |
| ret = SNMP_ERR_GENERR; |
| goto error; |
| } |
| entry->sctpAssocMaxRetr = strtol(token, NULL, 10); |
| |
| token = strtok(NULL, " "); /* T1X */ |
| if (token == NULL) { |
| ret = SNMP_ERR_GENERR; |
| goto error; |
| } |
| entry->sctpAssocT1expireds = strtol(token, NULL, 10); |
| |
| token = strtok(NULL, " "); /* T2X */ |
| if (token == NULL) { |
| ret = SNMP_ERR_GENERR; |
| goto error; |
| } |
| entry->sctpAssocT2expireds = strtol(token, NULL, 10); |
| |
| token = strtok(NULL, " "); /* RXTC */ |
| if (token == NULL) { |
| ret = SNMP_ERR_GENERR; |
| goto error; |
| } |
| entry->sctpAssocRtxChunks = strtol(token, NULL, 10); |
| |
| entry->sctpAssocRemHostName[0] = 0; |
| entry->sctpAssocRemHostName_len = 0; |
| entry->sctpAssocDiscontinuityTime = 0; |
| |
| ret = sctpAssocTable_add_or_update(containers->sctpAssocTable, entry); |
| if (ret != SNMP_ERR_NOERROR) { |
| DEBUGMSGTL(("sctp:tables:load:assoc", |
| "error adding/updating the entry in container\n")); |
| return ret; |
| } |
| |
| return SNMP_ERR_NOERROR; |
| |
| error: |
| if (entry != NULL) |
| sctpAssocTable_entry_free(entry); |
| return ret; |
| } |
| |
| |
| /* |
| * Load assocTable and localAddrTable from /proc/net/sctp/assoc. Mark all added |
| * or updated entries as valid (so the missing, i.e. invalid, can be deleted). |
| */ |
| static int |
| load_assoc(sctpTables_containers * containers) |
| { |
| FILE *f; |
| char line[1024]; |
| int ret = SNMP_ERR_NOERROR; |
| |
| DEBUGMSGTL(("sctp:tables:load:assoc", "arch load(linux)\n")); |
| |
| f = fopen(ASSOC_FILE, "r"); |
| if (f == NULL) { |
| DEBUGMSGTL(("sctp:tables:load:assoc", |
| "arch load failed: can't open" ASSOC_FILE "\n")); |
| return SNMP_ERR_GENERR; |
| } |
| |
| /* |
| * ignore the header. |
| */ |
| fgets(line, sizeof(line), f); |
| |
| while (fgets(line, sizeof(line), f) != NULL) { |
| DEBUGMSGTL(("sctp:tables:load:assoc", "processing line: %s\n", |
| line)); |
| |
| ret = parse_assoc_line(line, containers); |
| if (ret != SNMP_ERR_NOERROR) { |
| DEBUGMSGTL(("sctp:tables:load:assoc", |
| "error parsing the line\n")); |
| } |
| } |
| fclose(f); |
| |
| return SNMP_ERR_NOERROR; |
| } |
| |
| |
| static int |
| parse_remaddr_line(char *line, sctpTables_containers * containers) |
| { |
| char *token; |
| int ret; |
| sctpAssocRemAddrTable_entry *entry; |
| |
| entry = sctpAssocRemAddrTable_entry_create(); |
| if (entry == NULL) |
| return SNMP_ERR_GENERR; |
| |
| token = strtok(line, " "); /* rem. address */ |
| ret = |
| convert_address(token, entry->sctpAssocRemAddr, |
| &entry->sctpAssocRemAddrType, |
| &entry->sctpAssocRemAddr_len); |
| if (ret < 0) { |
| ret = SNMP_ERR_GENERR; |
| goto error; |
| } |
| |
| token = strtok(NULL, " "); /* assoc id */ |
| if (token == NULL) { |
| ret = SNMP_ERR_GENERR; |
| goto error; |
| } |
| entry->sctpAssocId = strtol(token, NULL, 10); |
| |
| token = strtok(NULL, " "); /* hb act */ |
| if (token == NULL) { |
| ret = SNMP_ERR_GENERR; |
| goto error; |
| } |
| if (token[0] == '1') |
| entry->sctpAssocRemAddrHBActive = TRUTHVALUE_TRUE; |
| else |
| entry->sctpAssocRemAddrHBActive = TRUTHVALUE_FALSE; |
| |
| token = strtok(NULL, " "); /* rto */ |
| if (token == NULL) { |
| ret = SNMP_ERR_GENERR; |
| goto error; |
| } |
| entry->sctpAssocRemAddrRTO = strtol(token, NULL, 10); |
| |
| token = strtok(NULL, " "); /* max path rtx */ |
| if (token == NULL) { |
| ret = SNMP_ERR_GENERR; |
| goto error; |
| } |
| entry->sctpAssocRemAddrMaxPathRtx = strtol(token, NULL, 10); |
| |
| token = strtok(NULL, " "); /* rem addr rtx */ |
| if (token == NULL) { |
| ret = SNMP_ERR_GENERR; |
| goto error; |
| } |
| entry->sctpAssocRemAddrRtx = strtol(token, NULL, 10); |
| |
| entry->sctpAssocRemAddrStartTime = 0; |
| entry->sctpAssocRemAddrActive = TRUTHVALUE_TRUE; |
| |
| ret = |
| sctpAssocRemAddrTable_add_or_update(containers-> |
| sctpAssocRemAddrTable, entry); |
| if (ret != SNMP_ERR_NOERROR) { |
| DEBUGMSGTL(("sctp:load:remaddr", |
| "error adding/updating the entry in container\n")); |
| return ret; |
| } |
| |
| return SNMP_ERR_NOERROR; |
| |
| error: |
| if (entry != NULL) |
| sctpAssocRemAddrTable_entry_free(entry); |
| return ret; |
| } |
| |
| /* |
| * Load sctpAssocRemAddrTable from /proc/net/sctp/remaddr. Mark all added |
| * or updated entries as valid (so the missing, i.e. invalid, can be deleted). |
| */ |
| static int |
| load_remaddr(sctpTables_containers * containers) |
| { |
| FILE *f; |
| char line[1024]; |
| int ret = SNMP_ERR_NOERROR; |
| |
| DEBUGMSGTL(("sctp:load:remaddr", "arch load(linux)\n")); |
| |
| f = fopen(REMADDR_FILE, "r"); |
| if (f == NULL) { |
| DEBUGMSGTL(("sctp:load:remaddr", |
| "arch load failed: can't open" REMADDR_FILE "\n")); |
| return SNMP_ERR_GENERR; |
| } |
| |
| /* |
| * ignore the header. |
| */ |
| fgets(line, sizeof(line), f); |
| |
| while (fgets(line, sizeof(line), f) != NULL) { |
| DEBUGMSGTL(("sctp:load:remaddr", "processing line: %s\n", line)); |
| |
| ret = parse_remaddr_line(line, containers); |
| if (ret != SNMP_ERR_NOERROR) { |
| DEBUGMSGTL(("sctp:load:remaddr", "error parsing the line\n")); |
| } |
| } |
| fclose(f); |
| |
| return SNMP_ERR_NOERROR; |
| } |
| |
| |
| int |
| sctpTables_arch_load(sctpTables_containers * containers, u_long * flags) |
| { |
| int ret = SNMP_ERR_NOERROR; |
| |
| *flags |= SCTP_TABLES_LOAD_FLAG_DELETE_INVALID; |
| *flags |= SCTP_TABLES_LOAD_FLAG_AUTO_LOOKUP; |
| |
| ret = load_assoc(containers); |
| if (ret != SNMP_ERR_NOERROR) |
| return ret; |
| |
| ret = load_remaddr(containers); |
| if (ret != SNMP_ERR_NOERROR) |
| return ret; |
| |
| return ret; |
| } |