Dead simple subnet and geo blocking in fail2ban

One of my systems came under a lof of heat recently from basically two different subnets in Asia, which comprised of about 1700 attacking IP addresses. That is, some IPs are harvesting usernames and email addresses from publicly available content, while others randomly attempt to login with this data. Due to the large number of used IP addresses, there aren’t any obvious brute force patterns in the logs, created by the individual IP addresses. Instead, their login attempts are 20 min. apart or 2 hours apart. Standard fail2ban recipes clearly aren’t covering such a scenario at all.

What I’ve done is a bit crude, but turned out effective in this case: I’ve devised a custom fail2ban filter, which collects login attempts over a very long period of time, e.g., 2 or 3 hours. If in this long time period, there are multiple login attempts from the same IP, we check its geographic location. If the geographic location is (in my use case) Germany, then we’re letting it happen (and keep relying on the system’s own flood protection, which does exist, but alas! isn’t quite as sensitive or effective in the described scenario), and otherwise we’re banning for a couple of days or a week, or whatever. This allows for some legitimate use of the service from abroad, but quickly blocks repeated logins.

Since my system employs a proxy, I watch the HAProxy log via an entry in fail2ban’s jail.local, e.g.:

enabled = true
bantime = 72h
maxretry = 7
findtime = 150m
logpath = /var/log/haproxy.log
(The custom filter, filter.d/user-login.conf, is not described as there’s nothing special about it.)

The custom action, action.d/geohostsdeny.conf, looks like this:


# Option: actionstart
# Notes.: command executed once at the start of Fail2Ban.
# Values: CMD
actionstart =

# Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
# Values: CMD
actionstop =

# Option: actioncheck
# Notes.: command executed once before each actionban command
# Values: CMD
actioncheck =

# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: See jail.conf(5) man page
# Values: CMD
actionban = IP=<ip> &&
/etc/fail2ban/scripts/ "$IP" ||
(ufw insert 1 deny from "$IP"/24 to any)

# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
# command is executed with Fail2Ban user rights.
# Tags: See jail.conf(5) man page
# Values: CMD
actionunban = IP=<ip> && ufw delete deny from "$IP"/24 to any 2>1 1>/dev/null

It essentially uses a script, scripts/, to decide if the IP should be blocked or not, and then blocks the IP’s entire class C subnet via ufw (but may as well use iptables directly). Fortunately, x.y.z.231/24 is automatically translated into x.y.z.0/24, which then leads to the blocking of the respective subnet. The unblocking works much the same. (Notice the output stream redirections in actionunban, which suppress error messages when the IP to unban wasn’t actually banned in the first place. This happens, if a German IP address triggers the action, but then ends up not being banned. A minor ugliness of this method that I have, so far, found unproblematic in practice. That is, for a while ufw thinks the IP address is banned, while in reality it isn’t.)

On Debian-based systems, the script,, needs the packages geoip-bin and geoip-database installed and may look as follows or contain additional, more elaborate conditions:

IS_GOOD_COUNTRY=`geoiplookup $IP | egrep "$GOOD" | wc -l`
if [ $IS_GOOD_COUNTRY -gt 0 ]
exit 0
exit 1

Especially in scenarios, where entire networks come attacking, this whole method is very efficient, because a single run of the custom fail2ban action will block hundreds of attacking IPs at once, without adding an endless amount of rules to ufw or iptables, thus potentially slowing down the system if the attack is sufficiently elaborate. And it’s easy to make the banning even more aggressive than just class C subnets.

I should add that the above was inspired by a helpful blog post by Roland Michael, which adds offending hosts to /etc/hosts.deny, but which I can’t use all that often, since many services don’t support it. I needed something more ‘low-level’ than that, which works for all my services equally.

A few new releases of my ScalaJS-programs

Made some more or less seemingly lucky attempts this weekend to release new versions of some of my ScalaJS-programs, namely scalajs-notifyjs and OOREP.

Main motivation was to update OOREP to be using ScalaJS 1.x, by which I first had to update a dependency to one of my own programs, scalajs-notifyjs, to also support said ScalaJS version. I have to say, publishing a Scala package, no matter how big or small, all seems a bit like voodoo, where only priests of level 35 get initiated. In particular, I find getting the build.sbt right quite a challenge every single time I’m faced with this, with all of SBT’s cryptic options and symbols, and changes between various versions that bring perl or sed to mind. That’s software that can do everything, but you have to look up every minute detail every time you use it, if you don’t do it on a daily basis.

Anway, it all seems to work now, but I welcome feedback in case there are problems, of course.

hplip upgrade time again...

Don’t get me wrong, I’m a happy Debian testing user, but I started to dread upgrades solely for the existence of hplip, which absolutely always breaks in mysterious ways.

The latest such breakage was an upgrade from hplip 3.20.3 to 3.20.5. You would think, no major deal, right? Wrong! Because afterwards neither scanning nor printing worked. So, I rerun hp-check which shows me all sorts of errors, including that cups and libcups2 aren’t installed although they are right in front of my face. But, it also shows me that I need to upgrade the hplip plugin. I decide that this must be the true culprit.

However, a call to hp-plugin ends with

subprocess.CalledProcessError: Command '('lsb_release', '-a')' returned non-zero exit status 1.

So, just for not having to search all of the internets for a solution to this problem over and over again, I’d like to point out here that on this site you can download the specific versions of the plugin and then install them manually.

After two years, finally a new eureka release...

About two years have passed since I last released a new version of my CRPG, game engine and editor eureka. And this is roughly how long it took me to develop another project I’ve mentioned in my previous blog post, OOREP, a web-based homeopathic repertory written in Scala(Js). And since I can’t do everything at once, I put eureka on the back burner for a while.

Here’s what it currently looks like

for those of you who are too lazy to compile it and try it out yourselves.

Von Globulisierungsgegnern (und -anhängern und warum diese ihrer Disziplin oft einen Bärendienst erweisen)

Nach ca. 1 1/2 Jahren Entwicklungszeit habe ich diese Woche mit einiger Genugtuung die erste Vorversion meiner Homöopathie-Software namens OOREP online gestellt für jedermann benutzbar im sog. World Wide Web. Wer sich dafür interessiert, kann und sollte sich auf der Seite ein wenig umsehen und ggf. im FAQ einlesen, wenn er oder sie Hilfe benötigt. Ich bin darüber hinaus unglaublich gespannt auf Feedback von der Community und besonders von geübten Homöopathen, die das Gebiet besser übersehen, als ich geneigter Laie es tue.

Leider denken die Leute heute ja, dass wenn man Homöopathieanhänger ist, man automatisch auch an Astrologie und Chemtrails glaubt und Angst vor 5G hat und wird mit allerhand wissenschaftlich anmutender Gegenargumente konfrontiert, auch wenn das Gegenüber ansonsten selbst keinerlei wissenschaftlichen Hintergrund vorweisen kann. Am häufigsten zu hören ist der Einwurf, dass ab einer Verdünnungspotenz von ca. C12 ja in den Globuli keinerlei Spuren der Ursubstanz mehr nachweisbar sind (sog. Avogadrogrenze). Und wo nichts ist, kann schließlich auch nichts wirken. Oder, ähnlich gelagert, dass ja im Wasser, welches zur Potenzierung verwendet wird, auch noch andere Stoffe neben der Ursubstanz mitverdünnt werden, die dann genauso wirken müssten, es aber scheinbar nicht tun und so weiter und so fort.

Ärgerlich für mich in diesem Zusammenhang ist, dass viele Homöopathieanhänger über dieses Stöckchen springen, das die Globulisierungsgegner ihnen damit vor die Füße halten. Sprich: sie lassen sich auf deren Gegenargumente ein - selbst wenn sie erfahrene Homöopathen sind - und versuchen die Gegenargumente zu entkräften bzw. physikalische Zusammenhänge zugunsten der Wirksamkeit von Homöopathie zu zitieren. Dabei geht es dann häufig um Quantenphysik, Schwingungen, Energieübertragung und Experimente, die das Gedächtnis von Wasser bestätigt haben wollen.

Ich bin kein Physiker und auch nicht der liebe Gott. Ich weiß nicht, ob Wasser Gedächtnis hat oder ob Globuli schwingen. Es ist mir, zumindest was diese Diskussion angeht, auch ziemlich egal, weil es darauf nicht ankommt. Worauf es ankommt, ist einzig, dass Homöopathie wirkt. Und nur weil man das nach aktuellen naturwissenschaftlichen Maßstäben nicht erklären kann, heißt es eben nicht, dass die Homöopathie nicht trotzdem funktionieren kann.

Ich halte es für geradezu unwissenschaftlich über ca. 200 Jahre gesammelte Indizienbeweise für die Wirksamkeit von Homöopathie, erbracht von hunderten von Ärzten aus aller Welt, die z.T. unabhängig voneinander die selben oder zumindest ähnliche Phänomene und Zusammenhänge ob der Wirksamkeit homöopathischer Mittel zu Papier gebracht haben, mit dem Argument der angeblichen Unwissenschaftlichkeit einfach vom Tisch zu fegen. Zu den frühen Berichten gehören z.B. Samuel Hahnemanns Erfolge angesichts eines Ausbruchs von Typhus 1813 in Leipzig während der Völkerschlacht. Unter seiner Behandlung sind laut Berichten fast alle der 180 verwundeten und von ihm behandelten Soldaten von dieser Krankheit befreit worden, woraufhin er auch noch Jahre später von anderen Heeresoberen weiterhin als Arzt konsultiert wurde. Es ist zugegeben nur eine Anekdote. Sie alleine hätte keinerlei Gewicht, wären da nicht zudem tausende Arzneimittelprüfungen alleine der großen Homöopathen Kent, Hering, Boericke, Boenninghausen, usw., die diese wiederum auf abertausenden von Seiten Materia Medicas und Repertorien festgehalten haben (von den modernen will ich gar nicht erst anfangen). Diesen Fundus einfach mal eben so pauschal als Unsinn zu verwerfen passt vielleicht zu denen, die von sich gerne behaupten “I fucking love science”, scheint mir an sich jedoch ein geradezu unwissenschaftlicher Akt zu sein.

Aber das ist zugegebenermaßen auch nur meine Meinung. Nach objektiven Maßstäben feststellen kann man jedoch, dass beispielsweise auch nie wissenschaftlich bewiesen wurde, dass Aspirin eine bestimmte Wirkung entfaltet oder dass Vitamin C förderlich für das Immunsystem ist. Was vielen der Globulisierungsgegner nicht bewusst ist, ganz einfach weil sie selbst keine Wissenschaftler sind und sich auch nie tiefer mit Wissenschafts- bzw. Erkenntnistheorie auseinandergesetzt haben, ist, dass man in Experimentalwissenschaften zwar Indizien für eine bestimmte Hypothese oder Modellvorstellung erbringen kann, jedoch zu keinem Zeitpunkt wissen kann, ob nicht irgendwann in der Zukunft man es noch genauer wissen wird, noch genauere Modelle der Wirklichkeit ersinnen wird. Nimmt man seinen (Karl) Popper wörtlich, so sollte man in diesen Disziplinen sogar ausschließlich Falsifizieren, um dann eine widerlegte Hypothese eben durch eine bessere ersetzen zu können. (Doch welcher Wissenschaftler will dadurch Karriere machen, dass er versucht, die Hyopthese, durch die er vllt. gerade Bekanntheit erlangt hat, gleich wieder zu zerlegen? Aber das ist ein anderes Thema…) In der Mathematik ist das freilich anders: dort gibt es tatsächlich - und anders als beim Aspirin oder Vitamin C - Beweise jeweils in Bezug auf ein Axiomensystem, und Theoreme sind dann tatsächlich solche für alle Ewigkeit. So funktioniert, grob und kurz gesagt, der Erkenntnisgewinn in den Wissenschaften (Geisteswissenschaften einmal außen vor; deren Stand ist diesbezgl. häufig noch schwächer).

D.h. aber eben auch, dass man auf diese Weise nicht die Nichtwirksamkeit von Homöopathie beweisen könnte.

Und schließlich wenn man die öffentliche Debatte zum Thema verfolgt, so drängt sich einem der Verdacht auf, dass sie aus Sicht der Homöopathieanhänger bereits verloren ist oder zumindest nicht mehr gewonnen werden kann - egal wie hoch die vielzitierten angeblichen Milliardengewinne der Homöopathieindustrie sind, die beim Beobachter letztlich nicht für die Homöopathie sprechen, sondern zeigen sollen, dass das alles nur reine Geschäftemacher und krumme Hunde sind, denn nur solche haben schließlich Gewinnabsichten. Gefordert wird nicht weniger als allgemeine Verbote von Homöopathie, was mich schon alleine deshalb wundert, weil man sich bis gerade eben noch dafür rechtfertigen musste, dass das doch nur Wasser sei. Die Gefahren, die Homöopathie birgt, sind dann angeblich darin begründet, dass Patienten statt Chemotherapie oder Tetanusimpfung lieber Globuli schlucken. Klar, solche mag es geben genauso wie die, die aus Versehen an einem Cocktail aus Schmerztabletten und anderen Medikamenten sterben, oder denen, die glauben, Ihr Jungbrunnen sei das abendliche Glas Whisky vorm Schlafengehen. Doch die Quintessenz kann nicht sein, alles zu verbieten, was eine kleine Minderheit tut oder ihr wiederfährt. Ich erwarte, dass ich in einer Gesellschaft, die ihre scheinbare Aufgeklärtheit so sehr betont, die Freiheit besitze, Homöopathika nehmen zu können, wenn ich es für angemessen und richtig halte und vor allem niemand anderen damit gefährde oder gar schade. In meinem konkreten Fall zahlt es auch nicht die Krankenkasse und müssen auch keine Kinder in Kobaltminen dafür arbeiten. Und im Zweifel habe ich halt mein verdientes und versteuertes Einkommen für Zuckerwaser ausgegeben. Das ist mein gutes Recht und sollte es auch bleiben.

Protest von Globulisierungsgegnern (Quelle: Andreas Schwarzkopf auf Wikipedia, CC-Lizenz)

In so fern - und jetzt schließt sich der Kreis - erweisen Homöopathieanhänger ihrer Disziplin gewissermaßen einen Bärendienst, wenn sie sich auf das pseudowissenschaftliche Glatteis der Gegenseite begeben. Eine unsubstantiierte Replik mit Hilfe von Quantenphysik, Schwingungen und Wassergedächtnis lässt die Homöopathieanhänger lediglich ungebildet erscheinen und bestärkt die Gegner höchstens noch in ihrem Vorhaben. Zielführender erscheint es mir, im Zweifel auf Erklärungsversuche pro Homöopathie zu verzichten und stattdessen einfach zu akzeptieren, dass sie wirkt. (Auch wenn Vergleiche immer etwas hinken: Gefühle wie Angst oder Liebe kann ich auch nicht sichtbar unterm Elektronenmikroskop machen und auch niemandem erklären, der sie nicht selbst schon mal erlebt hat; und die Frage ist auch, was es brächte.) Abstrakte Konzepte wie energetische Schwingungen beispielsweise können u.U. hilfreich für Homöopathen als Arbeitswerkzeug sein, z.B. um beobachtete Mittelwechselwirkungen bei ähnlichen Potenzen zu vermeiden o.ä. Sie mögen dahingehend nützliche, einfache Modelle sein, sind jedoch i.A. nicht mit Naturwissenschaft zu verwechseln. In diesem Sinne: der Homöopath genießt und schweigt. Dieser Artikel anlässlich der Veröffentlichung meiner Software soll diesbezgl. meine Ausnahme gewesen sein.

Mailfilter statistics

As a (currently not so active) researcher, I tend to keep track of statistics such as citation count. My most influential paper now has more than 400 citations. That’s not too bad, especially in the formal methods community, which is way smaller than, say, software engineering or machine learning.

I sometimes wonder though, how 400 citations fare against thousands of downloads of one of my first bigger C++ programs, mailfilter, which I wrote about 20 years ago. Sure, with broadband available everywhere, including wirelessly, it has somewhat lost its relevance in this day and age, but there was a time, when this little utility was quite popular.

I managed to extract the following two charts, which I decided to keep here because they are interesting to me, not necessarily to you.

SourceForge download statistics

Debian popularity statistics

My first impression of and experiences with Wayland

Ever since I watched this talk by Carsten Haitzler, I wanted to try out Xorg’s unofficial (?) successor, Wayland. In a nutshell, it is supposed to be much more secure than Xorg (e.g., against keyboard loggers) and its architecture easier to extend and maintain. Its downsides are supposedly less network transparency (which in over 20 years of using Linux, never played a big role for me anyway) and, depending on who you ask, no closed-source driver support.

My first impression of it: Wayland has become very usable by now. Within 15 minutes or so, I had it up and running on my Debian unstable.


Wayland does not know the concept of a window manager. It uses what it calls compositors, and thankfully there is the Sway compositor, which behaves more or less like the i3 window manager, which I use under Xorg. Wayland also provides a more mainstream compositor, Weston. So if a tiling window manager is alien to you, you can start Weston instead and feel right at home. But in order to be able to do that, you will need a login manager that allows you to start a Wayland session at all, and not every login manager is able to. It is relatively easy though to find one that is. I have chosen tbsm, a login manager for the console, which naturally requires you to disable all other login managers first, for example, by replacing in /etc/default/grub




In terms of stability and performance, Wayland felt on par with Xorg, although I am not a gamer who challenges his PC much in that regard.

tbsm on tty3 (the friendly Linux penguin brought to you by /etc/issue)


If you want to open X-programs that are not natively Wayland-aware (which most programs aren’t), you’ll also need to install xwayland, which is Wayland’s wrapper around the Xorg-server for compatibility reasons. Without it, you may not even be able to open a text window when you start Wayland for the first time!


The biggest issue I faced was the lack of xmodmap and xsession support. Everything from ~/.xsession had to be migrated into ~/.config/sway/config, which is OK, but means it’s not available to other compositors that I may use in the future. ~/.xsession was universal in that regard when you chose the default session in your login manager.

While the xsession-issue was somewhat inconvenient, it wasn’t exactly a deal breaker. Lack of xmodmap support, however, was. Until today, I have not found out how to remap keys in xkb, so that I can type German Umlauts using Ctrl + a for ä or Ctrl + Shift + o for Ö, etc. By using

setxkbmap -model pc101 -layout us -variant intl -option lv3:ralt_switch,terminate:ctrl_alt_bksp -print

I ended up with the right Alt key and q giving me ö, but with Shift it’s not an Ö, but the same keystroke that both i3 and Sway use to close a window - and I do not want to remap this. I’ve outgrown the urge to wildly remap programs’ default behaviours, just because I can.

Also, I’ve asked this question on Stackexchange, but with 0 replies so far. Hence, I’m back on Xorg - for now. But I’m guessing that it’s not long until these types of rough edges are evened out, and also people who don’t want to study xkb’s source code are able to switch to the more secure Wayland for good.

Switched blog to static using Hexo

In a general attempt to somewhat “degoogling” my life, I have moved this blog from Blogspot (which has been Google-owned for a while now) to my own, private web site.

To this end, I am using the Node.js-based static site generator Hexo and feel like I am its only non-Japanese user in existence. Thankfully however, most of the documentation is also available in English and help plentiful on Github forums and the like. So far, so good.

In other news, I have also completely banned all closed-source Google applications from my mobile, which is now running Lineage OS 14.1 and the F-Droid rather than the Google-Play store. Camera uploads end up in NextCloud, operated by a close relative of mine, and navigation needs are satisifed by the open-source program OsmAnd. I also learned that the freely available mail client K9 is nowhere near as immature as it was 10 years ago, when I had tried it last. To cut a long story short: I am not missing a thing, have regained full control over my personal data, and the device’s battery life has just doubled since there is now bugger-all background activity going on when compared to previously. I am surprised, what took me so long…

Lineage OS 14.1 on my LG G2 D802 (intl) - using only open source software

Finally, a technical task that warrants for transcription in my long neglected blog: I’ve updated my LG G2 from CM11 to LOS 14.1 over the last day or so. It was surprisingly painful, given the large number of tutorials online. Hence I want to point out why exactly this was the case. Though, be warned: this is not intended as a step-by-step tutorial or what not! I’m merely pointing out some caveats in the process, which you should be more or less familiar with, when you attempt anything like that. I will take no responsibility for any harm you, dear reader, might cause to your phone.

  1. Rooting: I am on Linux. I can’t start off by downloading the LG Flash Tool or anything like that. Instead, I rooted my device using a shell script wrapper around the adb tool, called VERY IMPORTANT: before you call it, replace SLEEP with sleep and ./adblinux with adb. Especially if you forget the SLEEP part, you will no longer be able to boot, not even into recovery. Your only choice then is to find above mentioned Windows tool and reflash a stock image from the net somewhere using the phone’s download mode. (That happened to me back when I switched from LG’s original image to CM11.)

  2. Bootloader: If you follow the tutorials on rooting, using, you’ll end up either with the CWM or TWRP bootloader. My observation: TWRP seems to be where it’s at today. However, you’ll end up with a version 2.6, while the official project’s sources are at 2.8 and patched versions floating the net are 3.2.1. To cut a long story short: TWRP 2.6 is absolutely useless for getting a recent CM/LOS zip installed.

More so, I was not even able to install 3.2.1 with it, until I patched the existing 2.6 with what is known as the “loki exploit”. While loki is no longer needed (or even supported) for 3.2.1, it seems, that in 2.6 you cannot install anything rather recent without it. This wasn’t obvious to me and installation somewhat painful, because what the Lineage OS install page says seems wrong: you cannot simply dd an image of TWRP 3.2.1 to /dev/block/.../by-name/recovery or anything like that but have to do as I said: patch 2.6, and then do an ordinary install of 3.2.1 using the patched 2.6.

  1. Bootstack: Also, it turns out, the official Lineage OS documentation didn’t mention either that if you’re coming from a very old version of Android/CM/LOS, as I did, you’ll need to install a new bootstack first. This is the scariest part of the process, as failing to apply the right file will definitely brick your device!

The right bootstack for the D802 seems to be the hybrid one that’s widely available on github. The zip can be installed as normal using the now installed TWRP 3.2.1 and the phone rebooted afterwards.

  1. Lineage OS 14.1: If your device isn’t bricked at this point, it is now possible to adb push a Lineage OS 14.1 zip onto /sdcard, wipe everything except internal storage, and then install it. Afterwards, it is necessary to wipe cache/dalvik clean and install the appropriate OpenGapps for the right device and LOS revision, and then reboot again.

Another very important point in this regard: After step 3., the key combination to enter recovery mode will have changed! Before, one had to hold down Vol-Down + Power on a switched-off phone until the LG-logo would pop up, let go of the buttons, and then press them again until the factory reset would appear. With a new bootstack, one has to initially press Vol-Down + Power, let go, and then press Vol-Up + Vol-Down until the factory reset appears! Very important to know that!

As I said, the whole update process isn’t exactly straightforward and bears the potential to brick the device, but the good news is: it helped me get Android 7.1.2 on my trusty old G2, and I expect that future updates to Android 8 will now only require entering the recovery mode, and then installing a new LOS-zip and GApps without further bootloader, bootstack, etc. patching.

Firefox/Iceweasel slow on a D-Link router? Read on...

I previously complained that my Firefox/Iceweasel browser has become unbearably slow, in that it sometimes loads pages, and sometimes stops right in the process. It looks like some kind of DNS issue, except that DNS works fine, and so do other browsers. For months I have been looking for all kinds of fixes to this issue, disabled ipv6 in Firefox, even for my network, but to no avail.

However, I always suspected it had to do with ipv6, or rather how my router handled it, I just couldn’t nail it down. Moreover, Mozilla forums couldn’t provide a solution either. Most people suggested Firefox plugins, such as AdBlockPlus, are to blame for browser sluggishness.

Anyway, while I had already switched to Google Chrome, which didn’t suffer from these issues, I kept my eyes open for a solution as it always bugged me that I couldn’t find out what was going on. And, indeed, not only can explain the problems I had, but also provide a really simple fix. So let me re-distribute this valuable piece of information via this blog here, so that other users, who are in the same boat, will be able to find it more easily:

Several D-Link models from the DSL series (at least DSL-584T, DSL-G604T, DSL-G624T, DSL-G664T, and DSL-G684T), do not correctly forward DNS responses for hostnames with both A and AAAA records published. What it does is to stuff the first 32 bits of the AAAA record into the A record that’s being returned to the end user’s computer. In other words, will incorrectly resolve to (the 2001:0500: part of the IPv6 address). If the operating system or web browser prefers to use IPv4, it will be unable to connect to the destination. Italian ISP Wind/Infostrada is reported to have distributed the DSL-G624T to its customer base over a period of several years.

It doesn’t happen all the time - it appears to be timing-dependent. Older Mozilla Firefox browsers are hit particularly bad, due to the fact that they will request AAAA lookups even if the local host does not have an IPv6 address. Work-around: Disable DNS forwarding support in the router. This will cause the D-Link to advertise the ISP’s upstream DNS resolvers (instead of itself) in DHCPv4, and the hosts on the LAN will query them directly.

For the record: I own a D-Link DIR-632, running on firmware 1.02. The above, indeed, made Firefox work like a charm again. (Having said that, nothing’s going to make me switch from Conkeror back to Firefox again. ;-)