Ich habe heute mal folgendes Python-Script geschrieben, das alle Downstream-Kanäle ermittelt und die Anzahl der verschiedenen MAC-Adressen ausgibt, d.h. das müssten die Anzahl der Kunden im Segment sein.
Benötigt werden dvbtune und dvbsnoop.
Damit die Wahrscheinlichkeit groß ist, dass alle Kunden erkannt werden, muss das Script relativ lange auf jedem Kanal bleiben, sollten vermutlich schon mind. 5 Minuten pro Kanal sein.
Code: Alles auswählen
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals
import subprocess
import time
import fcntl
import os
import select
import signal
ANALYZE_SECONDS = 300
DEMUX = 0
FRONTEND = 0 # frontend cannot be changed currently!
class CableModulation:
QAM16 = 1
QAM32 = 2
QAM64 = 3
QAM128 = 4
QAM256 = 5
class CableModulationConv:
fromString = {"QAM16": CableModulation.QAM16,
"QAM32": CableModulation.QAM32,
"QAM64": CableModulation.QAM64,
"QAM128": CableModulation.QAM128,
"QAM256": CableModulation.QAM256}
toString = {CableModulation.QAM16: "16QAM",
CableModulation.QAM32: "32QAM",
CableModulation.QAM64: "64QAM",
CableModulation.QAM128: "128QAM",
CableModulation.QAM256: "256QAM"}
toNumber = {CableModulation.QAM16: "16",
CableModulation.QAM32: "32",
CableModulation.QAM64: "64",
CableModulation.QAM128: "128",
CableModulation.QAM256: "256"}
class DownstreamAnalysis:
def __init__(self):
self.mac_cmts = {}
self.mac_customers = {}
self.packets_examined = 0
def add_mac_cmts(self, mac, downstream):
if mac not in self.mac_cmts:
self.mac_cmts[mac] = [downstream]
elif downstream not in self.mac_cmts[mac]:
self.mac_cmts[mac].append(downstream)
def add_mac_customer(self, mac):
if mac in self.mac_customers:
self.mac_customers[mac] += 1
else:
self.mac_customers[mac] = 1
self.packets_examined += 1
class DownstreamChannel:
def __init__(self, frequency, modulation, symbolrate):
self.frequency = int(frequency) # kHz
self.modulation = int(modulation)
if self.modulation < CableModulation.QAM16 or self.modulation > CableModulation.QAM256:
self.modulation = CableModulation.QAM256
self.symbolrate = int(symbolrate) # KSym/s
self.tune_process = None
def getCapacity(self):
return (self.symbolrate / 1000.0) * (188.0 / 204.0) * (self.modulation + 3) # in Mbit/s
def tuning_start(self):
print("tuning to " + str(self.frequency) + " kHz, " + CableModulationConv.toString[self.modulation])
cmd = "exec dvbtune -f " + str(self.frequency * 1000) + " -s " + str(self.symbolrate) + " -qam " + \
CableModulationConv.toNumber[self.modulation] + " -m"
self.tune_process = subprocess.Popen(cmd, shell=True, stdout=None, stderr=subprocess.PIPE, bufsize=256 * 1024)
# make pipe non-blocking
fd = self.tune_process.stderr.fileno()
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
out = ""
tries = 20
while True:
(r, w, x) = select.select([fd], [], [], 0.2)
if len(r) == 0 or tries == 0:
print("tuning failed")
self.tuning_stop()
return False
out += self.tune_process.stderr.read()
tries -= 1
if out.find("FE_HAS_SYNC") != -1:
return True
def tuning_stop(self):
if self.tune_process:
self.tune_process.kill()
self.tune_process.wait()
self.tune_process = None
def dump(self, seconds, cb, *args):
# 80 MB buffer size in kernel for dvb and 1 MB buffer in pipe
cmd = "exec dvbsnoop -n 0 8190 -s ts -buffersize 81920 -b -devnr " + str(DEMUX)
b = bytearray(5000 * 188)
proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=None, bufsize=1024 * 1024)
# make pipe non-blocking
fd = proc.stdout.fileno()
(r, w, x) = select.select([fd], [], [], 0.5)
if len(r) == 0:
print("did not found any DOCSIS data")
proc.kill()
return False
time_start = time.time()
while True:
bytes = proc.stdout.readinto(b)
if (bytes % 188) != 0:
print("NYI We have a problem")
cb(b, int(bytes / 188), *args)
if (time.time() - time_start) > seconds:
proc.kill()
proc.communicate()
return True
def analyze(self, b, ts_packets, analysis):
cmts_mac = []
p = 0
while p < ts_packets:
if b[p*188] != 0x47:
print("[DOCSIS] Error Sync-Byte")
p += 1
continue
error = b[p*188 + 1] >> 7
if error:
print("[DOCSIS] Error in packet")
p += 1
continue
packetStarts = (b[p*188 + 1] >> 6) & 0x01
if packetStarts == 0:
p += 1
continue
pointerField = b[p*188 + 4]
# we don't want to consider the case that the DOCSIS header continues in the next ts packet
if pointerField > 160:
p += 1
continue
# MAC Packet starts
cur = p*188 + 5 + pointerField
fc_type = b[cur] >> 6
if b[cur] == 0xff or fc_type == 0x01:
p += 1
continue
fc_parm = (b[cur] >> 1) & 0x1f
#ehdr_on = s[cur] & 0x01
# Packet PDU or Isolation Packet PDU MAC Frame
# or Management MAC Header
if fc_type == 0x00 or fc_type == 0x02 or (fc_type == 0x03 and fc_parm == 0x01):
mac_parm = b[cur + 1]
# see above
if (mac_parm + pointerField) > 160:
p += 1
continue
cur += 6 + mac_parm
# multicast
if (b[cur] == 0x33 and b[cur+1] == 0x33) or (b[cur] == 0x01 and b[cur+1] == 0x00 and b[cur+2] == 0x5e):
p += 1
continue
dst_mac = "%02X:%02X:%02X:%02X:%02X:%02X" % (b[cur], b[cur+1], b[cur+2], b[cur+3], b[cur+4], b[cur+5])
src_mac = "%02X:%02X:%02X:%02X:%02X:%02X" % (b[cur+6], b[cur+7], b[cur+8], b[cur+9], b[cur+10], b[cur+11])
if src_mac not in cmts_mac:
cmts_mac.append(src_mac)
analysis.add_mac_customer(dst_mac)
p += 1
for mac in cmts_mac:
analysis.add_mac_cmts(mac, self)
class DownstreamSearch:
def __init__(self):
self.found_channels = []
def nothing(self, b, ts_packets):
pass
CH_NO_LOCK = 1
CH_NO_DATA = 2
CH_OK = 3
def check_channel(self, ch):
if ch.tuning_start():
if ch.dump(0, self.nothing):
ch.tuning_stop()
return DownstreamSearch.CH_OK
ch.tuning_stop()
return DownstreamSearch.CH_NO_DATA
return DownstreamSearch.CH_NO_LOCK
def start(self):
for frequency in range(522000, 862000, 8000):
downstream = DownstreamChannel(frequency, CableModulation.QAM256, 6952)
status = self.check_channel(downstream)
if status == DownstreamSearch.CH_OK:
self.found_channels.append(downstream)
continue
elif status == DownstreamSearch.CH_NO_LOCK:
downstream = DownstreamChannel(frequency, CableModulation.QAM64, 6952)
status = self.check_channel(downstream)
if status == DownstreamSearch.CH_OK:
self.found_channels.append(downstream)
if __name__ == '__main__':
search = DownstreamSearch()
search.start()
print("\n\nFound channels:")
for ch in search.found_channels:
print(" %d kHz" % ch.frequency)
analysis = DownstreamAnalysis()
print("\n\nAnalyze channels:")
for ch in search.found_channels:
if ch.tuning_start():
ch.dump(ANALYZE_SECONDS, ch.analyze, analysis)
print("found %d customers until now" % len(analysis.mac_customers))
ch.tuning_stop()
print("\n\nCMTS MAC addresses:")
for mac in analysis.mac_cmts:
print(mac)
Ausgabe bei mir:
Found channels:
546000 kHz
554000 kHz
562000 kHz
570000 kHz
578000 kHz
586000 kHz
594000 kHz
602000 kHz
666000 kHz
674000 kHz
682000 kHz
690000 kHz
698000 kHz
706000 kHz
714000 kHz
722000 kHz
Analyze channels:
tuning to 546000 kHz, 256QAM
found 440 customers until now
tuning to 554000 kHz, 256QAM
found 562 customers until now
tuning to 562000 kHz, 256QAM
found 630 customers until now
tuning to 570000 kHz, 256QAM
found 892 customers until now
tuning to 578000 kHz, 256QAM
found 1320 customers until now
tuning to 586000 kHz, 256QAM
found 1490 customers until now
tuning to 594000 kHz, 256QAM
found 1717 customers until now
tuning to 602000 kHz, 256QAM
found 2134 customers until now
tuning to 666000 kHz, 64QAM
found 2197 customers until now
tuning to 674000 kHz, 64QAM
found 2265 customers until now
tuning to 682000 kHz, 64QAM
found 2288 customers until now
tuning to 690000 kHz, 64QAM
found 2313 customers until now
tuning to 698000 kHz, 64QAM
found 2358 customers until now
tuning to 706000 kHz, 64QAM
found 2369 customers until now
tuning to 714000 kHz, 64QAM
found 2377 customers until now
tuning to 722000 kHz, 64QAM
found 2391 customers until now
CMTS MAC addresses:
00:01:5C:3C:0E:40
00:01:5C:3C:0E:45
Bin gespannt, was bei euch so ermittelt wird.
Aktuell (Samstag 17:45 Uhr) sind bei mir alle Downstream-Kanäle zusammen zu rund 60 % ausgelastet.