OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 | 2 |
3 import os, re, subprocess, sys | 3 import os, re, subprocess, sys, socket, struct, fcntl |
| 4 |
| 5 INTERVAL = 5 |
4 | 6 |
5 def format_bandwidth(bits): | 7 def format_bandwidth(bits): |
6 if bits >= 1000000: | 8 if bits >= 1000000: |
7 return "%.2f Mbit/s" % (bits / 1000000) | 9 return "%.2f Mbit/s" % (bits / 1000000) |
8 elif bits >= 1000: | 10 elif bits >= 1000: |
9 return "%.2f kbit/s" % (bits / 1000) | 11 return "%.2f kbit/s" % (bits / 1000) |
10 else: | 12 else: |
11 return "%.2f bit/s" % bits | 13 return "%.2f bit/s" % bits |
12 | 14 |
| 15 def getmacaddress(): |
| 16 # We are calling SIOCGIFHWADDR (0x8927 according to man ioctl_list) here. See |
| 17 # man netdevice for the request structure: it has to start with 16 bytes |
| 18 # containing the interface name, the OS will write 8 bytes after that (2 bytes |
| 19 # family name and 6 bytes actual MAC address). |
| 20 s = socket.socket() |
| 21 return fcntl.ioctl(s.fileno(), 0x8927, struct.pack("24s", "eth0"))[18:24] |
| 22 |
| 23 def splitport(hostport): |
| 24 match = re.search(r"\.(d+)$", hostport) |
| 25 if match: |
| 26 return hostport[:match.start()], match.group(1) |
| 27 else: |
| 28 return hostport, "0" |
| 29 |
| 30 def readtime(time): |
| 31 hour, minute, second = time.split(":") |
| 32 return int(hour) * 3600 + int(minute) * 60 + float(second) |
| 33 |
13 if __name__ == "__main__": | 34 if __name__ == "__main__": |
14 if len(sys.argv) != 3: | 35 if len(sys.argv) != 3: |
15 script_name = os.path.basename(sys.argv[0]) | 36 script_name = os.path.basename(sys.argv[0]) |
16 print "Usage: %s WARN CRIT" % script_name | 37 print "Usage: %s WARN CRIT" % script_name |
17 sys.exit(0) | 38 sys.exit(0) |
18 | 39 |
19 (warn, crit) = sys.argv[1:3] | 40 (warn, crit) = sys.argv[1:3] |
20 warn = int(sys.argv[1]) | 41 warn = int(sys.argv[1]) |
21 crit = int(sys.argv[2]) | 42 crit = int(sys.argv[2]) |
22 | 43 |
23 process_output = subprocess.check_output(["bwm-ng", "-I", "eth0", "-t", "5000"
, "-c", "1", "-o", "csv"]) | 44 process = subprocess.Popen(["tcpdump", "-q", "-n", "-s", "64", "-w", "-"], std
out=subprocess.PIPE, stderr=subprocess.PIPE) |
24 data = process_output.splitlines()[0].split(";") | 45 starttime = None |
25 tx = float(data[2]) * 8 | 46 mac = getmacaddress() |
26 rx = float(data[3]) * 8 | |
27 status = "rx %s tx %s" % (format_bandwidth(rx), format_bandwidth(tx)) | |
28 | 47 |
29 perfdata = "rx=%i;%i;%i tx=%i;%i;%i" % (rx, warn, crit, tx, warn, crit) | 48 total = {"rx": 0, "tx": 0} |
| 49 http = {"rx": 0, "tx": 0} |
| 50 https = {"rx": 0, "tx": 0} |
| 51 ssh = {"rx": 0, "tx": 0} |
| 52 dns = {"rx": 0, "tx": 0} |
| 53 other = {"rx": 0, "tx": 0} |
| 54 other_detailed = {} |
30 | 55 |
31 output = "%s|%s" % (status, perfdata) | 56 # See http://wiki.wireshark.org/Development/LibpcapFileFormat for libpcap form
at description |
| 57 magic_number, _, _, _, _, _, _ = struct.unpack("IHHiIII", process.stdout.read(
24)) |
| 58 if magic_number != 0xa1b2c3d4: |
| 59 raise Exception("Unexpected format") |
| 60 while True: |
| 61 sec, usec, incl_len, orig_len = struct.unpack("IIII", process.stdout.read(16
)) |
| 62 length = orig_len + 24 # 24 bytes Ethernet overhead not captured by tcpdum
p |
32 | 63 |
| 64 time = sec + float(usec) / 1000000 |
| 65 if starttime == None: |
| 66 starttime = time |
| 67 if time - starttime > INTERVAL: |
| 68 break |
| 69 |
| 70 def add_other(description): |
| 71 other[direction] += length |
| 72 other_detailed[description] = other_detailed.get(description, 0) + length |
| 73 |
| 74 payload = process.stdout.read(incl_len) |
| 75 |
| 76 # Unpack Ethernet frame, http://en.wikipedia.org/wiki/Ethernet_frame#Structu
re |
| 77 destination, source, protocol = struct.unpack("!6s6sH", payload[:14]) |
| 78 payload = payload[14:] |
| 79 direction = "rx" if destination == mac else "tx" |
| 80 total[direction] += length |
| 81 |
| 82 # Check Level 3 protocol |
| 83 if protocol == 0x0800: # IPv4, http://en.wikipedia.org/wiki/Internet_Pro
tocol_version_4#Header |
| 84 ihl = ord(payload[0]) & 0xF |
| 85 protocol = ord(payload[9]) |
| 86 payload = payload[ihl * 4:] |
| 87 elif protocol == 0x86DD: # IPv6, http://en.wikipedia.org/wiki/IPv6_packet#
Fixed_header |
| 88 protocol = ord(payload[6]) |
| 89 payload = payload[40:] |
| 90 else: |
| 91 add_other("L3 0x%04X" % protocol) |
| 92 continue |
| 93 |
| 94 # Check Level 4 protocol |
| 95 if protocol in (0x06, 0x11): # TCP, UDP |
| 96 # The lower port number should be the real port, the other one will be |
| 97 # the ephemeral port. |
| 98 source_port, destination_port = struct.unpack('!HH', payload[:4]) |
| 99 protocol = "TCP" if protocol == 0x06 else "UDP" |
| 100 port = min(source_port, destination_port) |
| 101 else: |
| 102 add_other("L4 0x%02X" % protocol) |
| 103 continue |
| 104 |
| 105 if protocol == "TCP" and port == 80: |
| 106 http[direction] += length |
| 107 elif protocol == "TCP" and port == 443: |
| 108 https[direction] += length |
| 109 elif protocol == "TCP" and port == 22: |
| 110 ssh[direction] += length |
| 111 elif port == 53: |
| 112 dns[direction] += length |
| 113 else: |
| 114 add_other("Port %i" % port) |
| 115 continue |
| 116 |
| 117 status = [] |
| 118 perfdata = [] |
| 119 def add_status(id, values): |
| 120 rx = float(values["rx"]) * 8 / INTERVAL |
| 121 tx = float(values["tx"]) * 8 / INTERVAL |
| 122 status.append("%srx %s %stx %s" % (id, format_bandwidth(rx), id, format_band
width(tx))) |
| 123 if id == "": |
| 124 perfdata.append("rx=%i;%i;%i tx=%i;%i;%i" % (rx, warn, crit, tx, warn, cri
t)) |
| 125 else: |
| 126 perfdata.append("%srx=%i %stx=%i" % (id, rx, id, tx)) |
| 127 |
| 128 add_status("", total) |
| 129 add_status("http_", http) |
| 130 add_status("https_", https) |
| 131 add_status("ssh_", ssh) |
| 132 add_status("dns_", dns) |
| 133 add_status("other_", other) |
| 134 for key in sorted(other_detailed.iterkeys(), key=lambda k: other_detailed[k],
reverse=True): |
| 135 status.append("%s %s" % (key, format_bandwidth(float(other_detailed[key]) /
INTERVAL))) |
| 136 |
| 137 output = "%s|%s" % (", ".join(status), " ".join(perfdata)) |
| 138 |
| 139 rx = float(total["rx"]) * 8 / INTERVAL |
| 140 tx = float(total["tx"]) * 8 / INTERVAL |
33 if rx >= crit or tx >= crit: | 141 if rx >= crit or tx >= crit: |
34 print "CRITICAL - " + output | 142 print "CRITICAL - " + output |
35 sys.exit(2) | 143 sys.exit(2) |
36 | 144 |
37 if rx >= warn or tx >= warn: | 145 if rx >= warn or tx >= warn: |
38 print "WARNING - " + output | 146 print "WARNING - " + output |
39 sys.exit(1) | 147 sys.exit(1) |
40 | 148 |
41 print "OK - " + output | 149 print "OK - " + output |
OLD | NEW |