1
0
mirror of https://github.com/Llewellynvdm/conky.git synced 2025-01-23 15:18:35 +00:00

IPv6 support for $tcp_portmon

In theory, this may fail to compile on ancient systems that don't have IPv6 types (struct
sockaddr_in6 et al.) available. If it turns out that such systems are still in use, the best way
to solve it would be to provide dummy declarations via configure tests.
This commit is contained in:
Pavel Labath 2009-11-16 18:17:16 +01:00
parent 66d8b2cbe0
commit e97b67aa20
5 changed files with 151 additions and 68 deletions

View File

@ -1,6 +1,7 @@
2009-11-16 2009-11-16
* Added support for $pid_threads, $pid_thread_list, $pid_nice, * Added support for $pid_threads, $pid_thread_list, $pid_nice,
$pid_priority, $pid_time_usermode, $pid_time_kernelmode and $pid_time $pid_priority, $pid_time_usermode, $pid_time_kernelmode and $pid_time
* Added IPv6 support to $tcp_portmon
2009-11-15 2009-11-15
* Added support for $pid_parent, $pid_uid, $pid_euid, $pid_suid, * Added support for $pid_parent, $pid_uid, $pid_euid, $pid_suid,

View File

@ -54,7 +54,7 @@ Unlike other system monitors such as top, Conky can run as a window in an X sess
<body> <body>
<p> <p>
Gentoo provides an ebuild to quickly and easily install Conky. Pay particular attention to the the USE flags. You'll most likely want X11 support (<c>X</c>), and make sure you select the USE flags for any music players (other than MPD) which you want. XMMS (<c>xmms</c>), Audacious (<c>audacious</c>), BMPx (<c>bmpx</c>), or XMMS support via the xmms-infopipe plugin (<c>infopipe</c>). If you want to use the TCP port monitor, be SURE to disable the <c>ipv6</c> use flag, as the port monitor is for ipv4 systems only. Gentoo provides an ebuild to quickly and easily install Conky. Pay particular attention to the the USE flags. You'll most likely want X11 support (<c>X</c>), and make sure you select the USE flags for any music players (other than MPD) which you want. XMMS (<c>xmms</c>), Audacious (<c>audacious</c>), BMPx (<c>bmpx</c>), or XMMS support via the xmms-infopipe plugin (<c>infopipe</c>).
</p> </p>
<pre caption="/etc/portage/package.use"> <pre caption="/etc/portage/package.use">

View File

@ -3237,12 +3237,11 @@
<option>tcp_portmon</option> <option>tcp_portmon</option>
</command> </command>
<option>port_begin port_end item (index)</option> <option>port_begin port_end item (index)</option>
<emphasis>(ip4 only at present)</emphasis>
</term> </term>
<listitem> <listitem>
<para>TCP port monitor for specified local ports. Port <para>TCP port (both IPv6 and IPv4) monitor for
numbers must be in the range 1 to 65535. Valid items specified local ports. Port numbers must be in
are:</para> the range 1 to 65535. Valid items are:</para>
<simplelist> <simplelist>
<member> <member>
<command>count</command> <command>count</command>

View File

@ -49,8 +49,6 @@ int copy_tcp_connection(tcp_connection_t *p_dest_connection,
return -1; return -1;
} }
g_strlcpy(p_dest_connection->key, p_source_connection->key,
sizeof(p_dest_connection->key));
p_dest_connection->local_addr = p_source_connection->local_addr; p_dest_connection->local_addr = p_source_connection->local_addr;
p_dest_connection->local_port = p_source_connection->local_port; p_dest_connection->local_port = p_source_connection->local_port;
p_dest_connection->remote_addr = p_source_connection->remote_addr; p_dest_connection->remote_addr = p_source_connection->remote_addr;
@ -129,7 +127,7 @@ void age_tcp_port_monitor(tcp_port_monitor_t *p_monitor, void *p_void)
fprintf(stderr, " - OK\n"); fprintf(stderr, " - OK\n");
#else #else
if (!g_hash_table_remove(p_monitor->hash, if (!g_hash_table_remove(p_monitor->hash,
(gconstpointer) p_conn->key)) { (gconstpointer) p_conn)) {
return; return;
} }
#endif #endif
@ -215,7 +213,7 @@ void show_connection_to_tcp_port_monitor(tcp_port_monitor_t *p_monitor,
/* first check the hash to see if the connection is already there. */ /* first check the hash to see if the connection is already there. */
if ((p_conn_hash = g_hash_table_lookup(p_monitor->hash, if ((p_conn_hash = g_hash_table_lookup(p_monitor->hash,
(gconstpointer) p_connection->key))) { (gconstpointer) p_connection))) {
/* it's already in the hash. reset the age of the connection. */ /* it's already in the hash. reset the age of the connection. */
p_conn_hash->age = TCP_CONNECTION_STARTING_AGE; p_conn_hash->age = TCP_CONNECTION_STARTING_AGE;
@ -252,7 +250,7 @@ void show_connection_to_tcp_port_monitor(tcp_port_monitor_t *p_monitor,
p_node->connection.key); p_node->connection.key);
#endif #endif
g_hash_table_insert(p_monitor->hash, g_hash_table_insert(p_monitor->hash,
(gpointer) p_node->connection.key, (gpointer) &p_node->connection); (gpointer) &p_node->connection, (gpointer) &p_node->connection);
/* append the node to the monitor's connection list */ /* append the node to the monitor's connection list */
if (p_monitor->connection_list.p_tail == NULL) { if (p_monitor->connection_list.p_tail == NULL) {
@ -294,6 +292,136 @@ void for_each_tcp_port_monitor_in_collection(
} }
} }
static const unsigned char prefix_4on6[] = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff
};
union sockaddr_in46 {
struct sockaddr_in sa4;
struct sockaddr_in6 sa6;
struct sockaddr sa;
};
/* checks whether the address is a IPv4-mapped IPv6 address */
static int is_4on6(const struct in6_addr *addr)
{
return ! memcmp(&addr->s6_addr, prefix_4on6, sizeof(prefix_4on6));
}
/* converts the address to appropriate textual representation (IPv6, IPv4 or fqdn) */
static void print_host(char *p_buffer, size_t buffer_size, const struct in6_addr *addr, int fqdn)
{
union sockaddr_in46 sa;
socklen_t slen;
memset(&sa, 0, sizeof(sa));
if(is_4on6(addr)) {
sa.sa4.sin_family = AF_INET;
memcpy(&sa.sa4.sin_addr.s_addr, &addr->s6_addr[12], 4);
slen = sizeof(sa.sa4);
} else {
sa.sa6.sin6_family = AF_INET6;
memcpy(&sa.sa6.sin6_addr, addr, sizeof(struct in6_addr));
slen = sizeof(sa.sa6);
}
getnameinfo(&sa.sa, slen, p_buffer, buffer_size, NULL, 0, fqdn?0:NI_NUMERICHOST);
}
/* converts the textual representation of an IPv4 or IPv6 address to struct in6_addr */
static void string_to_addr(struct in6_addr *addr, const char *p_buffer)
{
size_t i;
if(strlen(p_buffer) < 32) { //IPv4 address
i = sizeof(prefix_4on6);
memcpy(addr->s6_addr, prefix_4on6, i);
} else {
i = 0;
}
for( ; i < sizeof(addr->s6_addr); i+=4, p_buffer+=8) {
sscanf(p_buffer, "%8x", (unsigned *)&addr->s6_addr[i]);
}
}
/* hash function for tcp_connections */
static guint tcp_connection_hash(gconstpointer A)
{
const tcp_connection_t *a = (const tcp_connection_t *) A;
guint hash = 0;
size_t i;
hash = hash*47 + a->local_port;
hash = hash*47 + a->remote_port;
for(i = 0; i < sizeof(a->local_addr.s6_addr); ++i)
hash = hash*47 + a->local_addr.s6_addr[i];
for(i = 0; i < sizeof(a->remote_addr.s6_addr); ++i)
hash = hash*47 + a->remote_addr.s6_addr[i];
return hash;
}
/* comparison function for tcp_connections */
static gboolean tcp_connection_equal(gconstpointer A, gconstpointer B)
{
const tcp_connection_t *a = (const tcp_connection_t *) A;
const tcp_connection_t *b = (const tcp_connection_t *) B;
return a->local_port == b->local_port && a->remote_port == b->remote_port &&
! memcmp(&a->local_addr, &b->local_addr, sizeof(a->local_addr)) &&
! memcmp(&a->remote_addr.s6_addr, &b->remote_addr, sizeof(a->remote_addr));
}
/* adds connections from file to the collection */
static void process_file(tcp_port_monitor_collection_t *p_collection, const char *file)
{
FILE *fp;
char buf[256];
char local_addr[40];
char remote_addr[40];
tcp_connection_t conn;
unsigned long inode, uid, state;
if ((fp = fopen(file, "r")) == NULL) {
return;
}
/* ignore field name line */
fgets(buf, 255, fp);
/* read all tcp connections */
while (fgets(buf, sizeof(buf), fp) != NULL) {
if (sscanf(buf,
"%*d: %39[0-9a-fA-F]:%hx %39[0-9a-fA-F]:%hx %lx %*x:%*x %*x:%*x %*x %lu %*d %lu",
local_addr, &conn.local_port,
remote_addr, &conn.remote_port,
(unsigned long *) &state, (unsigned long *) &uid,
(unsigned long *) &inode) != 7) {
fprintf(stderr, "/proc/net/tcp: bad file format\n");
}
/** TCP_ESTABLISHED equals 1, but is not (always??) included **/
//if ((inode == 0) || (state != TCP_ESTABLISHED)) {
if((inode == 0) || (state != 1)) {
continue;
}
string_to_addr(&conn.local_addr, local_addr);
string_to_addr(&conn.remote_addr, remote_addr);
/* show the connection to each port monitor. */
for_each_tcp_port_monitor_in_collection(p_collection,
&show_connection_to_tcp_port_monitor, (void *) &conn);
}
fclose(fp);
}
/* ---------------------------------------------------------------------- /* ----------------------------------------------------------------------
* CLIENT INTERFACE * CLIENT INTERFACE
* *
@ -324,7 +452,7 @@ tcp_port_monitor_t *create_tcp_port_monitor(in_port_t port_range_begin,
g_sprintf(p_monitor->key, ":%04X :%04X", port_range_begin, port_range_end); g_sprintf(p_monitor->key, ":%04X :%04X", port_range_begin, port_range_end);
/* create the monitor's connection hash */ /* create the monitor's connection hash */
if ((p_monitor->hash = g_hash_table_new(g_str_hash, g_str_equal)) == NULL) { if ((p_monitor->hash = g_hash_table_new(tcp_connection_hash, tcp_connection_equal)) == NULL) {
/* we failed to create the hash, so destroy the monitor completely /* we failed to create the hash, so destroy the monitor completely
* so we don't leak */ * so we don't leak */
destroy_tcp_port_monitor(p_monitor, NULL); destroy_tcp_port_monitor(p_monitor, NULL);
@ -357,18 +485,17 @@ tcp_port_monitor_t *create_tcp_port_monitor(in_port_t port_range_begin,
int peek_tcp_port_monitor(const tcp_port_monitor_t *p_monitor, int item, int peek_tcp_port_monitor(const tcp_port_monitor_t *p_monitor, int item,
int connection_index, char *p_buffer, size_t buffer_size) int connection_index, char *p_buffer, size_t buffer_size)
{ {
struct in_addr net;
struct sockaddr_in sa; struct sockaddr_in sa;
sa.sin_family = AF_INET;
if (!p_monitor || !p_buffer || connection_index < 0) { if (!p_monitor || !p_buffer || connection_index < 0) {
return -1; return -1;
} }
memset(p_buffer, 0, buffer_size); memset(p_buffer, 0, buffer_size);
memset(&net, 0, sizeof(net)); memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
/* if the connection index is out of range, we simply return with no error, /* if the connection index is out of range, we simply return with no error,
* having first cleared the client-supplied buffer. */ * having first cleared the client-supplied buffer. */
if ((item != COUNT) && (connection_index if ((item != COUNT) && (connection_index
@ -386,14 +513,12 @@ int peek_tcp_port_monitor(const tcp_port_monitor_t *p_monitor, int item,
case REMOTEIP: case REMOTEIP:
net.s_addr = p_monitor->p_peek[connection_index]->remote_addr; print_host(p_buffer, buffer_size, &p_monitor->p_peek[connection_index]->remote_addr, 0);
snprintf(p_buffer, buffer_size, "%s", inet_ntoa(net));
break; break;
case REMOTEHOST: case REMOTEHOST:
memcpy(&sa.sin_addr.s_addr, &p_monitor->p_peek[connection_index]->remote_addr, sizeof(sa.sin_addr.s_addr)); print_host(p_buffer, buffer_size, &p_monitor->p_peek[connection_index]->remote_addr, 1);
getnameinfo((struct sockaddr *) &sa, sizeof(struct sockaddr_in), p_buffer, buffer_size, NULL, 0, 0);
break; break;
case REMOTEPORT: case REMOTEPORT:
@ -410,14 +535,12 @@ int peek_tcp_port_monitor(const tcp_port_monitor_t *p_monitor, int item,
case LOCALIP: case LOCALIP:
net.s_addr = p_monitor->p_peek[connection_index]->local_addr; print_host(p_buffer, buffer_size, &p_monitor->p_peek[connection_index]->local_addr, 0);
snprintf(p_buffer, buffer_size, "%s", inet_ntoa(net));
break; break;
case LOCALHOST: case LOCALHOST:
memcpy(&sa.sin_addr.s_addr, &p_monitor->p_peek[connection_index]->local_addr, sizeof(sa.sin_addr.s_addr)); print_host(p_buffer, buffer_size, &p_monitor->p_peek[connection_index]->local_addr, 1);
getnameinfo((struct sockaddr *) &sa, sizeof(struct sockaddr_in), p_buffer, buffer_size, NULL, 0, 0);
break; break;
case LOCALPORT: case LOCALPORT:
@ -504,55 +627,17 @@ void destroy_tcp_port_monitor_collection(
void update_tcp_port_monitor_collection( void update_tcp_port_monitor_collection(
tcp_port_monitor_collection_t *p_collection) tcp_port_monitor_collection_t *p_collection)
{ {
FILE *fp;
char buf[256];
tcp_connection_t conn;
unsigned long inode, uid, state;
if (!p_collection) { if (!p_collection) {
return; return;
} }
process_file(p_collection, "/proc/net/tcp");
process_file(p_collection, "/proc/net/tcp6");
/* age the connections in all port monitors. */ /* age the connections in all port monitors. */
for_each_tcp_port_monitor_in_collection(p_collection, for_each_tcp_port_monitor_in_collection(p_collection,
&age_tcp_port_monitor, NULL); &age_tcp_port_monitor, NULL);
/* read tcp data from /proc/net/tcp */
if ((fp = fopen("/proc/net/tcp", "r")) == NULL) {
return;
}
/* ignore field name line */
fgets(buf, 255, fp);
/* read all tcp connections */
while (fgets(buf, sizeof(buf), fp) != NULL) {
if (sscanf(buf,
"%*d: %x:%hx %x:%hx %lx %*x:%*x %*x:%*x %*x %lu %*d %lu",
(unsigned int *) &conn.local_addr, &conn.local_port,
(unsigned int *) &conn.remote_addr, &conn.remote_port,
(unsigned long *) &state, (unsigned long *) &uid,
(unsigned long *) &inode) != 7) {
fprintf(stderr, "/proc/net/tcp: bad file format\n");
}
/** TCP_ESTABLISHED equals 1, but is not (always??) included **/
//if ((inode == 0) || (state != TCP_ESTABLISHED)) {
if((inode == 0) || (state != 1)) {
continue;
}
/* build hash key */
g_sprintf(conn.key, "%08X:%04X %08X:%04X", conn.local_addr,
conn.local_port, conn.remote_addr, conn.remote_port);
/* show the connection to each port monitor. */
for_each_tcp_port_monitor_in_collection(p_collection,
&show_connection_to_tcp_port_monitor, (void *) &conn);
}
fclose(fp);
/* rebuild the connection peek tables of all monitors /* rebuild the connection peek tables of all monitors
* so clients can peek in O(1) time */ * so clients can peek in O(1) time */
for_each_tcp_port_monitor_in_collection(p_collection, for_each_tcp_port_monitor_in_collection(p_collection,

View File

@ -39,7 +39,6 @@
/* connection deleted if unseen again after this # of refreshes */ /* connection deleted if unseen again after this # of refreshes */
#define TCP_CONNECTION_STARTING_AGE 1 #define TCP_CONNECTION_STARTING_AGE 1
#define TCP_CONNECTION_HASH_KEY_SIZE 28
#define TCP_PORT_MONITOR_HASH_KEY_SIZE 12 #define TCP_PORT_MONITOR_HASH_KEY_SIZE 12
#define MAX_PORT_MONITOR_CONNECTIONS_DEFAULT 256 #define MAX_PORT_MONITOR_CONNECTIONS_DEFAULT 256
@ -72,10 +71,9 @@ enum tcp_port_monitor_peekables {
* ------------------------------------------------------------------------ */ * ------------------------------------------------------------------------ */
typedef struct _tcp_connection_t { typedef struct _tcp_connection_t {
/* connection's key in monitor hash */ /* connection's key in monitor hash */
gchar key[TCP_CONNECTION_HASH_KEY_SIZE]; struct in6_addr local_addr;
in_addr_t local_addr; struct in6_addr remote_addr;
in_port_t local_port; in_port_t local_port;
in_addr_t remote_addr;
in_port_t remote_port; in_port_t remote_port;
int age; int age;
} tcp_connection_t; } tcp_connection_t;