Greeting IPv6 users with Lighttpd
Posted by Damien Pollet Wed, 25 Apr 2007 00:55:12 GMT
Last weekend I configured my web server to accept IPv6 connections (and since my provider ignores IPv6, I’m using a 6to4 tunnel from BTexact).
To celebrate my small patch of adressing space, I added the banner you can see at the top-left of the page. It should be red if you come from IPv4 land and green if my tunnel is up, my config actually works, and you are really in IPv6 (not using an ::ffff:a.b.c.d IP).
So I’m coming to my first point, which is how to tell Lighttpd to differenciate clients and keep the config files modular.
Resarch(ish) discussion after the jump.
Configuring Lighttpd
The goal is to have content under http://domain/ipvX/... be served from a directory or the other on disk, depending on the type of connection.
At first, my idea was to use a $SERVER["socket"] conditional:
$SERVER["socket"] == "0.0.0.0:80" {
# on all v4 connections...
alias.url += ("/ipvX/" => "/var/www/ipv4/")
}
else $SERVER["socket"] "2001:...my IPv6..." {
alias.url += ("/ipvX/" => "/var/www/ipv6/")
}
This doesn’t work for several reasons:
- on Linux, when Lighttpd listens on IPv6, it will get v4 addresses as their v6 representation (
::ffff:a.b.c.d). - more importantly, to Lighttpd, this syntax means “please listen on a newly created socket for this spec”. More on this one below.
OK, so I tried several variations and in the end the one that works is:
$HTTP["remoteip"] =~ "^::ffff:" {
alias.url += ( "/ipvX/" => "/var/www/ipv4/" )
}
else $HTTP["remoteip"] != "" {
alias.url += ( "/ipvX/" => "/var/www/ipv6/" )
}
In both cases this appends a rule to the configuration variable for mod_alias. This worked in all but one virtual host, precisely the one that I have AWStats running in, and that adds more alias rules. I had to move AWStats’ aliases outside the vhost configuration to make them work again. Not a real problem since the aliases are to global static image files in /usr/share/awstats.
In the process, it was really helpful to invoke lighttpd -tp -f lighttpd.conf 2>&1 | less and #lighttpd on irc.freenode.org.
The impact of syntax in domain-specific languages
Taking a few steps back…
Intuitive meaning, or the meaning of intuitive? I like to repeat that in the context of user interfaces, “ergonomic” and “intuitive” both actually mean “familiar”. Jef Raskin explained this better than I can here. I see syntaxes just as textual, non interactive user interfaces and, reversely, user interfaces as languages: base elements and rules for how to combine them in order to convey meaning or affordances.
Let’s go back to lighty’s config. It’s some kind of decision tree: it defines scopes in which requests get sorted according to their properties. Lighty then handles each request, depending on the values of configuration variables in the scope the request falls in; lighttpd -tp basically shows these nested scopes. Maybe I’m wrong but that’s how I understand things.
This decision tree and how it is used to handle requests is the semantics of lighty’s config. The config itself is written down using a particular syntax in /etc/lighttpd.conf. My problem here is the impedance mismatch between what the syntax conveys, and the actual semantics that are attached to it. For instance, the $HTTP["remoteip"] == "1.2.3.4" conditional defines a scope where all requests from address 1.2.3.4 will fall. You can use other comparison operators like regular expression matching =~ or negated versions != and !~. The address operand has special meaning in some cases, like "0.0.0.0" that means “any address”. All this makes sense in the context, but $SERVER["socket"] is different: it has the additional meaning that it will create the socket to listen for these connections, so successive uses can fail.
$SERVER["socket"] == "0.0.0.0:80" {
# listen on all interfaces, port 80
}
$SERVER["socket"] == "1.2.3.4" {
# could match requests adressed to this specific IP,
# but fails because it tries to create a socket
# that conflicts with the previous one
}
One comparison syntax, different meaning ?
On a pickier point of view, the syntax of conditionals in lighttpd looks familiar, related to other languages. For instance, $HTTP["remoteip"] would parse in Ruby as an access to a global dictionary (most probably class Hash), where the key is a string. In Ruby, the dollar, square brackets, and double quotes all have meaning: global, hash access, string. In the case of lighttpd however, what use is there for $? What is the point of requiring quotes, since remoteip actually is a keyword, known a priori in the syntax, in contrast to a user-provided value? Many languages even don’t require special markup for user-provided values, like atoms in Erlang, Prolog or Lisp.
As a conclusion, there is a lot of passion these days around model engineering and domain-specific languages, on one side trying to manipulate and formalize concepts from different domains, but often just providing a variant of UML to represent them, and on the other side taking a more concrete approach of defining specialized languages, syntaxes, and tools. I think I’d like to see some reflection on what makes a syntax –textual or graphical, interactive or not– efficient at conveying the intended meaning. What is the importance of having unambiguous token classes? What can composition, adjunction, order be used for, etc. Maybe something like this already exists in non-computer linguistics?
Update, must read: Writing programs for people to read.
PS. On a not so unrelated note, Lucas is having fun with Bash.

Nice! Jused added this to my openwrt router. But I hat trouble with the syntax, for me worked: $HTTP[“remoteip”] =~ “^::ffff:” {
alias.url = ( “/IPvX/” => “/www/ipv4/” ) }
else $HTTP[“remoteip”] != “” {
alias.url = ( “/IPvX/” => “/www/ipv6/” )
}