Bandbreite im DHCP

DHCP-Pakete, die über Broadband-Infrastruktur wie DSLAMs laufen, werden providerseitig mit Zusatzinformationen angereichert. Beispielsweise mit Angaben zur Leitungsqualität und Bandbreite. Diese Informationen auszulesen und ggfl. auch selbst setzen zu können ist entweder Bitpfriemelei oder eine Konfiguration des ICS DHCP-Servers.

Rohe Bits

Selbstverständlich ist es möglich, die Daten unterwegs mitzuschneiden und aus dem Binärblob zu extrahieren. RFC4243 definiert dazu die Suboption 9 in der DHCP Option (82) die für das DHCP-Relay zuständig ist. In dieser Option sind herstellerspezifische Dinge abgelegt.

Das Extrahieren per Perl ist einfach:

my $d = Net::DHCP::Packet->new($data);
die "Not a DHCP packet.\n" unless $d->isDhcp;

my $bbf;
my $o82 = $d->getOptionRaw(82);
if(defined $o82) {
    my %o = unpack('(CC/a)*', $o82);
    if(%o) {
        my $o9 = rfc4243($o{9});
        if(exists $o9->{3561}) {
            $bbf = BBF($o9->{3561});
        }
    }
}

sub rfc4243($) {
    local $_ = shift;
    my %r = unpack('(NC/a)*',$_);
    return \%r;
}

sub BBF($) {
    local $_ = shift;
    my %c = unpack('(CC/a)*',$_);

    foreach(keys %c) {
        if($_ >= 129 && $_ <= 142) {
            ($c{$_}) = unpack('N', $c{$_});
        } elsif($_ >= 155 && $_ <= 162) {
            ($c{$_}) = unpack('N', $c{$_});
        }
    }
    return \%c;
}

Im Ergebnis bekommt man einen Hash-Referenz in $bbf in die Hand, die die Angaben der eingefügten Optionen nach Broadbandforum (Vendor-ID 3561) enthält.

Im Einzelnen sind das:

my %bbfnames = (
    129 => 'Actual data rate Upstream in kbps',    # from client
    130 => 'Actual data rate Downstream in kbps',  # from client
    131 => 'Minimum Data Rate Upstream in kbps',
    132 => 'Minimum Data Rate Downstream in kbps',
    133 => 'Attainable Data Rate Upstream in kbps',
    134 => 'Attainable Data Rate Downstream in kbps',
    135 => 'Maximum Data Rate Upstream in kbps',   # from server
    136 => 'Maximum Data Rate Downstream in kbps', # from server
    137 => 'Minimum Data Rate Upstream in low power state in kbps',
    138 => 'Minimum Data Rate Downstream in low power state in kbps',
    139 => 'Maximum Interleaving Delay Upstream in milliseconds',
    140 => 'Actual interleaving Delay Upstream in milliseconds',
    141 => 'Maximum Interleaving Delay Downstream in milliseconds',
    142 => 'Actual interleaving Delay Downstream in milliseconds',

    155 => 'Expected throughput (ETR) upstream in kbps',
    156 => 'Expected throughput (ETR) downstream in kbps',
    157 => 'Attainable expected throughput (ATTETR) upstream in kbps',
    158 => 'Attainable expected throughput (ATTETR) downstream in kbps',
    159 => 'Gamma data rate (GDR) upstream in kbps',
    160 => 'Gamma data rate (GDR) downstream in kbps',
    161 => 'Attainable gamma data rate (ATTGDR) upstream in kbps',
    162 => 'Attainable gamma data rate (ATTGDR) downstream in kbps',
);

Man kann dann also über das Feld {130} der Variable $bbf auf die aktuelle Downloadgeschwindigkeit in kbps zugreifen.

ISC DHCP

Natürlich sind solche Programmierungen bei der Benutzung fertiger Software, wie dem DHCPD von ISC, nicht möglich. Hier muss mit Bordmitteln die Option auseinander genommen werden. Es ist nahezu unmöglich, die Substrukturen auf Bitebene selbst zu parsen, dass muss der Daemon schon allein hin bekommen. Aber dafür braucht er Hilfe.

Man muss der Software erklären, wie die Option strukturiert sind, damit sie die parsen kann. Zunächst einmal erkläre ich den RFC4243.

option space rfc4243 code width 4 length width 1 hash size 7;
option agent.vendor code 9 = encapsulate rfc4243;

Es wird eine neuer Optionsbereich definiert, der intern mit 32bit Codenummern arbeitet. Da ich nur eine Handvoll Codes auch real definieren will, lass ich die Hashtable auf einer kleinen Primzahl (hier 7) stehen, um RAM zu sparen.

Dieser Optionsbereich wird dann als Suboption 9 an die vordefinierte "agent" Option 82 eingehängt. Der Server weiß also bereits, wie Option 82 zu parsen ist, kennt nun auch die innere Struktur der Unteroption 9: Eine komplette eigene DHCP-Optionsstruktur mit eigenen Code- und Längenangaben.

Man sieht auch gleich, dass die Struktur (rfc4243) nicht den gleichen Namen haben muss wie die Option (vendor).

option space bbf code width 1 length width 1 hash size 257;
option rfc4243.bbf code 3561 = encapsulate bbf;

In analoger Weise wird die Substruktur des Vendors Broadbandforum definiert. Wenn in der Suboption 9 der (32bit) Code 3561 auftaucht, so ist der gesamte Datenbereich dieses Feldes wieder eine DHCP-Optionsstruktur mit 8bit-Code und 8bit-Länge. Diesmal will ich aber mehr Codes definieren, mache also die Hashtable größer.

option bbf.actual-up-rate code 129 = unsigned integer 32;
option bbf.actual-down-rate code 130 = unsigned integer 32;
option bbf.minimum-up-rate code 131 = unsigned integer 32;
option bbf.minimum-down-rate code 132 = unsigned integer 32;
option bbf.attainable-up-rate code 133 = unsigned integer 32;
option bbf.attainable-down-rate code 134 = unsigned integer 32;
option bbf.maximum-up-rate code 135 = unsigned integer 32;
option bbf.maximum-down-rate code 136 = unsigned integer 32;
option bbf.maximum-up-lowpower-rate code 137 = unsigned integer 32;
option bbf.maximum-down-lowpower-rate code 138 = unsigned integer 32;
option bbf.maximum-delay-up code 139 = unsigned integer 32;
option bbf.actual-delay-up code 140 = unsigned integer 32;
option bbf.maximum-delay-down code 141 = unsigned integer 32;
option bbf.actual-delay-down code 142 = unsigned integer 32;

option bbf.expected-throughput-up code 155 = unsigned integer 32;
option bbf.expected-throughput-down code 156 = unsigned integer 32;
option bbf.attainable-throughput-up code 157 = unsigned integer 32;
option bbf.attainable-throughput-down code 158 = unsigned integer 32;
option bbf.gamma-data-rate-up code 159 = unsigned integer 32;
option bbf.gamma-data-rate-down code 160 = unsigned integer 32;
option bbf.attainable-gamma-data-rate-up code 161 = unsigned integer 32;
option bbf.attainable-gamma-data-rate-down code 162 = unsigned integer 32;

Hier wurden die für mich interessanten Werte definiert. Der Server kann also beim Parsen der Unterstruktur anhand des (8bit) Codes erkennen, um welchen Wert es geht und wie dieser in interpretieren ist (als uint32 in Netzwerkbyteorder).

Und wie greift man auf die Werte zu?

log (info, concat("BBF: ",
            binary-to-ascii(16, 8, ":", hardware),
            " current speed ",
            binary-to-ascii(10, 32, ".", option bbf.actual-down-rate),
            "/",
            binary-to-ascii(10, 32, ".", option bbf.actual-up-rate)
          ));

Die Loganweisung an passender Stelle in der Konfig erzeugt dann eine Menge Einträge.

Oct  9 18:55:26 dhcpd: BBF: 1:e0:28:6d:aa:bb:cc current speed 5999/639
Oct  9 18:55:27 dhcpd: BBF: 1:34:31:c4:dd:ee:ff current speed 50000/9999
Oct  9 18:55:29 dhcpd: BBF: 1:38:10:d5:gg:hh:ii current speed 54999/11000 

Tut.

Post a comment

Related content