switches.txt
planning/planning
mbd/nets.pl
+switches.sql
+include/config.local.pm
+mbd/mbd.log
--- /dev/null
+open PATCHLIST, "/root/nms/switches.txt"
+ or die "switches.txt: $!";
+my %vlans = ();
+
+while (<PATCHLIST>) {
+ if (/87\.76\.(\d+)\.(\d+) 26 e(\d+)-(\d+)/) {
+ my $vlannum = $3.$4;
+ my $helper = "87.76.$1." . ($2+1);
+ my $name = "e$3-$4";
+ my $z = sprintf "f%03u", $vlannum;
+ $z =~ /(.)(.)(.)(.)/; $vlans{$helper} = [ $vlannum, $name, "$4.$3.$2.$1" ];
+ }
+}
+
+while (<>) {
+ /DHCPOFFER on 87.76.\d+.\d+ to 00:(.)(.):(.)(.):(.)(.):(.)(.):(.)(.) \((.*?)\) via (\d+\.\d+\.\d+\.\d+)/ or next;
+ my $v = $vlans{$12};
+ open NSUPDATE, "| nsupdate -y DHCP_UPDATER:F388UOhaIIKHRH9TDE5PTA==";
+ print NSUPDATE <<"EOF";
+server 127.0.0.1
+update delete $11.v6.$v->[1].tg07.gathering.org. AAAA
+update add $11.v6.$v->[1].tg07.gathering.org. 3600 AAAA 2001:16d8:ffff:f$v->[0]:02$1$2:$3$4ff:fe$5$6:$7$8$9$10
+send
+update delete $10.$9.$8.$7.$6.$5.e.f.f.f.$4.$3.$2.$1.2.0.$v->[2].f.f.f.f.8.d.6.1.1.0.0.2.ip6.arpa. PTR
+update add $10.$9.$8.$7.$6.$5.e.f.f.f.$4.$3.$2.$1.2.0.$v->[2].f.f.f.f.8.d.6.1.1.0.0.2.ip6.arpa. 3600 PTR $11.v6.$v->[1].tg07.gathering.org.
+send
+EOF
+ close NSUPDATE;
+}
+
file "tg07.gathering.org";
};
+// hijack
+zone "sth.ip-performance.se" {
+ type slave;
+ notify no;
+ masters { 87.76.254.2; };
+ file "sth.ip-performance.se";
+};
+
+// linknett
+zone "0.76.87.in-addr.arpa" {
+ type slave;
+ notify no;
+ masters { 87.76.254.2; };
+ file "0.76.87.in-addr.arpa";
+ allow-transfer { 193.0.0.0/22; };
+};
+
// linknett
zone "174.76.87.in-addr.arpa" {
type slave;
file "dynamic/76.87.in-addr.arpa";
allow-transfer { 194.19.3.20; 193.0.0.0/22; };
};
+zone "f.f.f.f.8.d.6.1.1.0.0.2.ip6.in-addr.arpa" {
+ type slave;
+ notify no;
+ masters { 87.76.254.2; };
+ allow-update { key DHCP_UPDATER; };
+ file "dynamic/f.f.f.f.8.d.6.1.1.0.0.2.ip6.arpa";
+ allow-transfer { 194.19.3.20; };
+};
+
key DHCP_UPDATER {
algorithm HMAC-MD5.SIG-ALG.REG.INT;
allow-transfer { 87.76.255.2; };
};
+// hijack
+zone "sth.ip-performance.se" {
+ type master;
+ file "sth.ip-performance.se";
+ notify yes;
+ allow-transfer { 87.76.255.2; };
+};
+
+// linknett
+zone "0.76.87.in-addr.arpa" {
+ type master;
+ file "0.76.87.in-addr.arpa";
+ notify yes;
+ allow-transfer { 87.76.255.2; 193.0.0.0/22; };
+};
+
// linknett
zone "174.76.87.in-addr.arpa" {
type master;
file "dynamic/76.87.in-addr.arpa";
allow-transfer { 194.19.3.20; 87.76.255.2; 193.0.0.0/22; };
};
+zone "f.f.f.f.8.d.6.1.1.0.0.2.ip6.arpa" {
+ type master;
+ allow-update { key DHCP_UPDATER; };
+ notify yes;
+ file "dynamic/f.f.f.f.8.d.6.1.1.0.0.2.ip6.arpa";
+ allow-transfer { 194.19.3.20; 87.76.255.2; };
+};
EOF
#! /usr/bin/perl
open PATCHLIST, "<patchlist.txt"
or die "patchlist.txt: $!";
-my %distros = ();
+my %vlans = ();
+my %ports = ();
while (<PATCHLIST>) {
chomp;
my $vlan = "$row$switch";
my $ipv6 = sprintf "F%02u%u", $row, $switch;
- $distros{$distro} .= <<"EOF";
+ $vlans{$distro} .= <<"EOF";
vlan $vlan
name $name
!
+EOF
+
+ $ports{$distro} .= <<"EOF";
default interface vlan $vlan
interface vlan $vlan
description $name
ip address $ip 255.255.255.192
- ip directed-broadcast 10
+ ip directed-broadcast 101
ip helper-address 87.76.254.2
no ip proxy-arp
ip access-group great-wall-of-tg in
ipv6 address 2001:16D8:FFFF:${ipv6}::1/64
- ip igmp version 3
+ ip igmp version 2
ip pim sparse-mode
no shutdown
+ exit
!
-
default interface $port
interface $port
description $name
storm-control broadcast level 2
no shutdown
+ exit
!
EOF
}
-for my $distro (keys %distros) {
+for my $distro (keys %vlans) {
open DISTRO, ">$distro-config"
or die "$distro-config: $!";
- print DISTRO $distros{$distro};
+ print DISTRO <<"EOF";
+access-list 101 remark ip directed broadcast
+access-list 101 permit ip any any dscp cs7
+EOF
+ print DISTRO $vlans{$distro};
+ print DISTRO $ports{$distro};
print DISTRO "end\n";
close DISTRO;
}
scp mbd-ports *-config sesse@f1:/tftpboot/portcfg/
# regenerate the mbd netlist
-(echo '# Autogenerated. Do not touch!' ; echo "package Config;"; echo 'our @networks = (' ; cut -d" " -f1-2 switches.txt | perl -e 'while (<>) { chomp; my ($net,$mask) = split / /; print "\t\"$net/$mask\",\n"; }' ; echo ');' ; echo '1;') > mbd/nets.pl
+(echo '# Autogenerated. Do not touch!' ; echo "package Config;"; echo 'our @networks = (' ; cut -d" " -f1-2 switches.txt | grep -vE '87\.76\.(130|131|216)\.0' | perl -e 'while (<>) { chomp; my ($net,$mask) = split / /; print "\t\"$net/$mask\",\n"; }' ; echo ');' ; echo '1;') > mbd/nets.pl
perl web/make-switches.pl > switches.sql
scp switches.sql root@space:
our @access_list = (
# half-life - untested (packet dump only)
{
+ name => 'Half-Life',
ports => [ 27015 ],
sizes => [ 16 ]
},
# cs 1.6 - verified
# (funker muligens for _alle_ source-spill inkl. hl2/cs:s)
{
+ name => 'CS 1.6, other Source games',
ports => [ 4242, "26900..26905", "27015..27020" ],
sizes => [ 25 ]
},
# doom 3 - verified
{
+ name => 'Doom 3',
ports => [ "27666..27673" ],
sizes => [ 14 ]
},
# quake 1 - verified
{
+ name => 'Quake 1',
ports => [ 26000 ],
sizes => [ 12 ]
},
# q3a - tested with demo only
# rtcw: enemy territory - untested (packet dump only)
{
+ name => 'Quake 3 Arena, RTCW: ET',
ports => [ "27960..27969" ],
sizes => [ 15 ]
},
# bf2 - tested with demo only
# bf2142 reportedly uses same engine
{
+ name => 'BF2/BF2142',
ports => [ "29900..29950" ],
sizes => [ 8 ]
},
# bf1942 - unverified (packet dump only)
{
+ name => 'BF1942',
ports => [ "22000..22010" ],
sizes => [ 8 ]
},
# quake 4 - tested with demo only, MUST select "internet"
{
+ name => 'Quake 4',
ports => [ 27950, 28004 ],
sizes => [ 14 ]
},
# quake 2 - untested (packet dump only)
{
+ name => 'Quake 2',
ports => [ 27910 ],
sizes => [ 11 ]
},
# warcraft 3 - untested (packet dump only)
{
+ name => 'Warcraft 3',
ports => [ "6112..6119" ],
sizes => [ 16, 48 ],
filter => sub { return (ord(substr(shift, 1, 1)) == 0x2f); }
# ut2003/ut2004 - untested (packet dump only)
{
+ name => 'UT2003/UT2004',
ports => [ 10777 ],
sizes => [ 5 ]
},
# soldat - untested (packet dump only)
{
+ name => 'Soldat',
ports => [ 23073 ],
sizes => [ 8 ]
},
# starcraft - untested (packet dump only)
{
+ name => 'Starcraft',
ports => [ 6111, 6112 ],
sizes => [ 8, 20 ]
},
# trackmania nations - untested (packet dump only)
{
+ name => 'Trackmania Nations',
ports => [ "2350..2370" ],
sizes => [ 42 ]
},
# company of heroes - untested (packet dump only)
{
+ name => 'Company of Heroes',
ports => [ 9100 ],
sizes => [ 39 ]
},
# command & conquer 3 - untested (packet dump only, reported to have some kind
# of chat functionality)
{
+ name => 'Command & Conquer 3',
ports => [ "8086..8093" ],
- sizes => [ 476 ]
+ sizes => [ 476 ],
+ filter => sub { return 0; }
+ },
+
+ # openttd
+ {
+ name => 'OpenTTD',
+ ports => [ 3979 ],
+ sizes => [ 3 ]
},
# unreal tournament, port 9777?
use Socket;
use Net::CIDR;
use Net::RawIP;
+use Time::HiRes;
require './access_list.pl';
require './nets.pl';
+require './survey.pl';
require './mbd.pm';
+use lib '../include';
+use nms;
+use strict;
+use warnings;
+
+my ($dbh, $q);
sub fhbits {
my $bits = 0;
return $bits;
}
+# used for rate limiting
+my %last_sent = ();
+
+# for own surveying
+my %active_surveys = ();
+my %last_survey = ();
+
+my %cidrcache = ();
+sub cache_cidrlookup {
+ my ($addr, $net) = @_;
+ my $key = $addr . " " . $net;
+
+ if (!exists($cidrcache{$key})) {
+ $cidrcache{$key} = Net::CIDR::cidrlookup($addr, $net);
+ }
+ return $cidrcache{$key};
+}
+
+my %rangecache = ();
+sub cache_cidrrange {
+ my ($net) = @_;
+
+ if (!exists($rangecache{$net})) {
+ ($rangecache{$net}) = Net::CIDR::cidr2range($net);
+ }
+
+ return $rangecache{$net};
+}
+
open LOG, ">>", "mbd.log";
-my @ports = mbd::find_all_ports();
+my @ports = ( mbd::find_all_ports() , $Config::survey_port_low .. $Config::survey_port_high );
# Open a socket for each port
my @socks = ();
my $rout;
my $nfound = select($rout=$rin, undef, undef, undef);
+ my $now = [Time::HiRes::gettimeofday];
+
+ # First of all, close any surveys that are due.
+ for my $sport (keys %active_surveys) {
+ my $age = Time::HiRes::tv_interval($active_surveys{$sport}{start}, $now);
+ if ($age > $Config::survey_time && $active_surveys{$sport}{active}) {
+ print "Survey for '" . $Config::access_list[$active_surveys{$sport}{entry}]->{name} . "'/" .
+ $active_surveys{$sport}{dport} . ": " . $active_surveys{$sport}{num} . " active servers.\n";
+ $active_surveys{$sport}{active} = 0;
+
+ # (re)connect to the database if needed
+ if (!defined($dbh) || !$dbh->ping) {
+ $dbh = nms::db_connect();
+ $q = $dbh->prepare("INSERT INTO mbd_log (ts,game,port,description,active_servers) VALUES (CURRENT_TIMESTAMP,?,?,?,?)")
+ or die "Couldn't prepare query";
+ }
+ $q->execute($active_surveys{$sport}{entry}, $active_surveys{$sport}{dport}, $Config::access_list[$active_surveys{$sport}{entry}]->{name}, $active_surveys{$sport}{num});
+ }
+ if ($age > $Config::survey_time * 3.0) {
+ delete $active_surveys{$sport};
+ }
+ }
+
for my $sock (@socks) {
next unless (vec($rout, fileno($sock), 1) == 1);
my ($sport, $saddr) = sockaddr_in($addr);
my ($dport, $daddr) = sockaddr_in(getsockname($sock));
my $size = length($data);
+
+ # Check if this is a survey reply
+ if ($dport >= $Config::survey_port_low && $dport <= $Config::survey_port_high) {
+ if (!exists($active_surveys{$dport})) {
+ print "WARNING: Unknown survey port $dport, ignoring\n";
+ next;
+ }
+ if (!$active_surveys{$dport}{active}) {
+ # remains
+ next;
+ }
+
+ ++$active_surveys{$dport}{num};
+ next;
+ }
+
+ # Rate limiting
+ if (exists($last_sent{$saddr}{$dport})) {
+ my $elapsed = Time::HiRes::tv_interval($last_sent{$saddr}{$dport}, $now);
+ if ($elapsed < 1.0) {
+ print LOG "$dport $size 2\n";
+ print inet_ntoa($saddr), ", $dport, $size bytes => rate-limited ($elapsed secs since last)\n";
+ next;
+ }
+ }
+
# We don't get the packet's destination address, but I guess this should do...
# Check against the ACL.
my $pass = 0;
+ my $entry = -1;
for my $rule (@Config::access_list) {
+ ++$entry;
+
next unless (mbd::match_ranges($dport, $rule->{'ports'}));
next unless (mbd::match_ranges($size, $rule->{'sizes'}));
print LOG "$dport $size $pass\n";
if (!$pass) {
- print "$dport, $size bytes => filtered\n";
+ print inet_ntoa($saddr), ", $dport, $size bytes => filtered\n";
+ next;
}
- next unless $pass;
+ $last_sent{$saddr}{$dport} = $now;
- for my $net (@Config::networks) {
- next if (Net::CIDR::cidrlookup(inet_ntoa($saddr), $net));
+ # The packet is OK! Do we already have a recent enough survey
+ # for this port, or should we use this packet?
+ my $survey = 1;
+ if (exists($last_survey{$entry . "/" . $dport})) {
+ my $age = Time::HiRes::tv_interval($last_survey{$entry . "/" . $dport}, $now);
+ if ($age < $Config::survey_freq) {
+ $survey = 0;
+ }
+ }
+
+ # New survey; find an unused port
+ my $survey_sport;
+ if ($survey) {
+ for my $port ($Config::survey_port_low..$Config::survey_port_high) {
+ if (!exists($active_surveys{$port})) {
+ $survey_sport = $port;
+
+ $active_surveys{$port} = {
+ start => $now,
+ active => 1,
+ dport => $dport,
+ entry => $entry,
+ num => 0
+ };
+ $last_survey{$entry . "/" . $dport} = $now;
+
+ last;
+ }
+ }
- my ($range) = Net::CIDR::cidr2range($net);
+ if (!defined($survey_sport)) {
+ print "WARNING: no free survey source ports, not surveying.\n";
+ $survey = 0;
+ }
+ }
+
+ my $num_nets = 0;
+ for my $net (@Config::networks) {
+ my ($range) = cache_cidrrange($net);
$range =~ /-(.*?)$/;
my $broadcast = $1;
- print inet_ntoa($saddr), ", $dport, $size bytes => $broadcast\n";
+ if ($survey) {
+ $sendsock->set({
+ ip => {
+ saddr => $Config::survey_ip,
+ daddr => $broadcast
+ },
+ udp => {
+ source => $survey_sport,
+ dest => $dport,
+ data => $data
+ }
+ });
+ $sendsock->send;
+ }
+
+ next if (cache_cidrlookup(inet_ntoa($saddr), $net));
$sendsock->set({
ip => {
}
});
$sendsock->send;
+
+ ++$num_nets;
+ }
+
+ if ($survey) {
+ print inet_ntoa($saddr), ", $dport, $size bytes => ($num_nets networks) [+survey from port $survey_sport]\n";
+ } else {
+ print inet_ntoa($saddr), ", $dport, $size bytes => ($num_nets networks)\n";
}
}
}
--- /dev/null
+package Config;
+
+our $survey_ip = "87.76.254.2";
+our $survey_port_low = 60100;
+our $survey_port_high = 60200;
+our $survey_freq = 60.0;
+our $survey_time = 10.0;
+
+1;
+
87.76.207.0 24 auditorium
87.76.208.0 24 ute
87.76.209.0 24 infodesk
-87.76.210.0 24 game-server
+87.76.211.0 24 qpad-scene
87.76.212.0 24 game-client
87.76.213.0 24 logistics
87.76.215.0 24 security
87.76.220.0 24 fugleberget
87.76.222.0 24 democrew
87.76.223.0 24 cam
+87.76.224.0 24 chillout
87.76.250.0 24 noc
87.76.252.0 24 presse
open PATCHLIST, "patchlist.txt"
or die "patchlist.txt: $!";
-my $cc = 241;
-my $dd = 0;
-
my $i = 1;
while (<PATCHLIST>) {
chomp;
my $xx = $x + 12;
my $yy = $y + (($s > 3) ? 50 : 48);
- $dk = $dd + 7 + ($cc - 241) * 42;
-
-# lasses magic-factor =)
- $dk += 4;
- if ($dk > 13) {
- $dk += 3;
- }
+ my ($ip, $range);
+ if ($s == 1) {
+ $ip = "87.76." . ($e) . ".2";
+ $range = "87.76." . ($e) . ".0";
+ } elsif ($s == 2) {
+ $ip = "87.76." . ($e) . ".66";
+ $range = "87.76." . ($e) . ".64";
+ } elsif ($s == 3) {
+ $ip = "87.76." . ($e) . ".130";
+ $range = "87.76." . ($e) . ".128";
+ } elsif ($s == 4) {
+ $ip = "87.76." . ($e + 1) . ".2";
+ $range = "87.76." . ($e + 1) . ".0";
+ } elsif ($s == 5) {
+ $ip = "87.76." . ($e + 1) . ".66";
+ $range = "87.76." . ($e + 1) . ".64";
+ } elsif ($s == 6) {
+ $ip = "87.76." . ($e + 1) . ".130";
+ $range = "87.76." . ($e + 1) . ".128";
+ }
- print "insert into switches (ip, sysname, switchtype) values ('87.76.$cc.$dk', 'e$e-${s}sw', 'es3024');\n";
+ print "insert into switches (ip, sysname, switchtype) values ('$ip', 'e$e-${s}sw', 'es3024');\n";
print "insert into placements (switch, placement) values ($i, box '(($x,$y),($xx,$yy))');\n";
print "insert into dhcp (switch, network) values ($i, '87.76.$c.$d/26');\n";
$i++;
-
- $dd++;
- if ($dd == 42) {
- $cc++;
- $dd = 0;
- }
}
print "end;\n";