Fallback to conntrack-utils if ip_conntrack is not available
[bwmon.git] / bwmon / proc.py
blob4f674e371eedf0d2bf7b4314d5119f0fe7b9643b
1 # -*- coding: utf-8 -*-
3 # Copyright 2010 Thomas Perl and Stefan Kögl. All rights reserved.
5 # Developed for a practical course (Large-scaled distributed computing) at the
6 # University of Technology Vienna in the 2010 summer term.
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions are met:
11 # 1. Redistributions of source code must retain the above copyright notice,
12 # this list of conditions and the following disclaimer.
14 # 2. Redistributions in binary form must reproduce the above copyright notice,
15 # this list of conditions and the following disclaimer in the documentation
16 # and/or other materials provided with the distribution.
18 # THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED
19 # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20 # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
21 # EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27 # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 from __future__ import absolute_import
32 import os
33 import re
34 import hashlib
36 def get_connections(conn_type='tcp'):
37 """Get connections from /proc/net/
39 Read the currently-running connections from the
40 /proc/ filesystem.
42 @param conn_type: The connection type ('tcp' or 'udp')
43 """
44 f = open('/proc/net/%s' % conn_type, 'r')
45 connections = {}
47 #documentation for /proc/net/tcp in
48 #http://lkml.indiana.edu/hypermail/linux/kernel/0409.1/2166.html
50 for line in f.readlines():
51 line = line.strip()
53 # header line
54 if line.startswith('s'):
55 continue
57 parts = filter(lambda x: x, line.split(' '))
58 index = parts[0].strip()[:-1]
59 local_addr = ip_repr(parts[1])
60 rem_addr = ip_repr(parts[2])
61 inode = int(parts[9])
63 connections[index] = {
64 'local': local_addr,
65 'remote': rem_addr,
66 'inode': inode
69 return connections
72 def ip_repr(ip):
73 """Get the dotted-decimal representation of an IP
75 @param ip: The IP as formatted in /proc/net/tcp
76 @return: the dotted-decimal representation of an IP
78 >>> ip_repr('C8BCED82:1A0B')
79 '130.237.188.200:6667'
80 """
82 if ':' in ip:
83 ip, port = ip.split(':')
84 else:
85 port = None
87 s = '.'.join([ repr(int(ip[x-2:x], 16)) for x in range(8, 0, -2) ])
88 if port:
89 s += ':%d' % int(port, 16)
91 return s
94 def get_fd_map():
95 """Get a filedescriptor-to-process mapping
97 @return: A dictionary mapping file descriptors to process infos
98 """
100 proc = '/proc/'
101 fd = 'fd'
102 pid_dir = r'\d+'
103 socket_regex = r'socket:\[(\d+)\]'
104 cmdline = 'cmdline'
105 m = {}
107 for pid in os.listdir(proc):
109 # process directories
110 d = os.path.join(proc, pid)
111 if not os.path.isdir(d):
112 continue
114 if not re.match(pid_dir, pid):
115 continue
117 fd_dir = os.path.join(d, fd)
118 for x in os.listdir(fd_dir):
119 path = os.path.join(fd_dir, x)
120 try:
121 f_desc = os.readlink(path)
122 except OSError:
123 continue
125 # search for socket file-descriptors
126 match = re.match(socket_regex, f_desc)
127 if match:
128 inode = int(match.group(1))
129 else:
130 continue
132 cmd_file = open(os.path.join(d, cmdline), 'rb')
133 cmd = cmd_file.read()
135 # Command line arguments are splitted by '\0'
136 cmd = cmd.replace('\0', ' ')
138 m[inode] = {'inode': inode, 'cmd': cmd, 'pid': int(pid)}
140 return m
143 def parse_ip_conntrack():
144 """Get a parsed representation of /proc/net/ip_conntrack
146 @return: A dictionary of connections
148 connections = {}
150 if not os.path.exists('/proc/net/ip_conntrack'):
151 # conntrack-utils (recent kernels, November 2012)
152 conntrack = os.popen('conntrack -L', 'r')
153 else:
154 # Old "ip_conntrack" kernel module (July 2010)
155 conntrack = open('/proc/net/ip_conntrack', 'r')
157 # http://www.faqs.org/docs/iptables/theconntrackentries.html
158 for line in conntrack:
159 parts = line.split()
161 # We only care about TCP and UDP connections
162 if parts[0] not in ('udp', 'tcp'):
163 continue
165 entry = {}
166 for (k, v) in [x.split('=', 1) for x in parts if '=' in x]:
167 # key-value pairs occur twice per line; if first key occurs
168 # again, we finish the first entry and start the next one
169 if k in entry:
170 key = get_key(entry)
171 connections[key] = entry
172 entry = {}
174 entry[k] = v
176 key = get_key(entry)
177 connections[key] = entry
179 conntrack.close()
181 return connections
184 def get_key(values):
185 """Build a hashed key out of a ip_conntrack connection
187 This takes the source IP, source port and destination
188 IP and destination port and returns a unique key.
190 @param values: An entry from the ip_conntrack table
191 @return: The hashed key for this connection pair
193 src = '%s:%s' % (values['src'], values['sport'])
194 dst = '%s:%s' % (values['dst'], values['dport'])
195 return ip_hash(src, dst)
198 def ip_hash(src_ip, dest_ip):
199 """Hash an IP pair
201 @param src_ip: The IP address of the source
202 @param dest_ip: The IP address of the destination
203 @return: A string that combines both parameters
205 return '%s-%s' % (src_ip, dest_ip)