I just found the following unusual message in my Exim logs:

2009-06-27 21:14:58 host name alias list truncated for 69.10.169.230

I guessed that this meant that the host had a long list of reverse name mappings (IP to name). Curious as to why, I did a DNS lookup on that IP:

chris@top ~ $ host 69.10.169.230 | wc -l
86

chris@top ~ $ host 69.10.169.230 | head -5
;; Truncated, retrying in TCP mode.
230.169.10.69.in-addr.arpa domain name pointer heavenlydonut.com.
230.169.10.69.in-addr.arpa domain name pointer pitrivertribe.org.
230.169.10.69.in-addr.arpa domain name pointer shastawebmail.com.
230.169.10.69.in-addr.arpa domain name pointer vidalvineyard.com.

So, the host has 86 names, right? And they all look like spam domains to me.

This looks like someone is trying hard to get around SMTP HELO verification, by providing a valid domain with forward and reverse lookups that map to their own IP. But they tried a bit too hard, because that’s a LONG list of domains. Nobody does that in the real world, I think.

So I decided to block mail from anyone with more than four reverse DNS entries. I have no idea what the collateral damage will be. I’m going to keep an eye on it.

Luckily, Exim makes this very easy:

defer
        set acl_c_ptr_count = ${reduce {${lookup dnsdb{>: \
                ptr=$sender_host_address}}} {0} {${eval:$value+1}}}
        condition = ${if >{$acl_c_ptr_count}{4}}
        message = Too many PTR records ($acl_c_ptr_count)

This counts the number of entries in the PTR list, assigns it to a local variable, and tests whether that number is greater than four. If so, it defers the message (tells the sender to come back later). This gives me a chance to fix it if I discover that it’s rejecting valid email, and still get the message.

The code to count the number of entries in a list is pretty ugly. I don’t suppose anyone wants to implement a “count” operation to count the number of items in a list in Exim?

I usually use Linux firewalls for traffic shaping, because the power of the traffic control (tc) system exceeds FreeBSD’s dummynet in most ways.

Dummynet can be used to create arbitrary delays and packet loss, which is very useful for simulating poor connections, but not for sharing bandwidth and prioritising packets between different traffic classes on a real traffic shaper.

However, I’ve just been testing PF (the new standard packet filter) and ALTQ (the alternative queueing system) on FreeBSD, and I’m impressed by the capabilities. It does annoy me that ALTQ is not enabled in the default kernel, so you have to compile your own kernel. I used the following commands:

cd /boot
cp -p kernel GENERIC # backup the current kernel
cd /usr/src/sys/i386/conf
cp GENERIC ~/ALTQ
ln -s ~/ALTQ .
vi ALTQ

and added the following lines to my new kernel configuration file, which I called ALTQ:

options ALTQ
options ALTQ_RED
options ALTQ_RIO
options ALTQ_HFSC
options ALTQ_PRIQ

and then compiled and installed the new kernel:

cd /usr/src
make buildkernel KERNCONF=ALTQ
make installkernel KERNCONF=ALTQ

and then reboot to load the new kernel. After that, we need to create a pf configuration. I prefer HFSC over CBQ queueing, because:

  • HFSC is guaranteed accurate, whereas CBQ is approximate
  • CBQ requires you to guess the average packet size and its accuracy depends entirely on this
  • HFSC has service curves which allow you to deliver small files quickly and drop the priority of large connections (e.g. file downloads) with great ease.

I prefer PF+ALTQ over linux TC because:

  • PF and ALTQ are fully integrated and configured using the same file, whereas TC has its own (very hard to use) classifier. I normally use the iptables CLASSIFY target to classify traffic instead, but this is not integrated.
  • TC is very hard to use generally. The authors seem more concerned with functionality than usability.
  • ALTQ has named queues which helps usability enormously compared to TC’s hex numbered classes.
  • ALTQ gives very low delay when the interface is not 100% saturated, which seems impossible to achieve with TC.

Here is a sample configuration of PF+ALTQ that I used for testing on a transparent bridging firewall (bridge0 connecting em0 and em1):

altq on em1 hfsc bandwidth 1Mb queue { ftp, ssh, icmp, other }
queue ftp bandwidth 30% priority 0 hfsc (upperlimit 99%)
queue ssh bandwidth 30% priority 2 hfsc (upperlimit 99%)
queue icmp bandwidth 10% priority 2 hfsc (upperlimit 99%)
queue other bandwidth 30% priority 1 hfsc (default upperlimit 99%)
pass out quick on bridge0 inet proto tcp from any port 21 to any queue ftp
pass out quick on bridge0 inet proto tcp from any port 22 to any queue ssh
pass out quick on bridge0 inet proto icmp from any to any queue icmp
pass out quick on bridge0 all

We are only queueing on em1 here, which is the downstream, so we are only limiting downloads. We deliberately limit them to 1 Mbps for testing. The limit should always be lower than your actual download bandwidth, to ensure that the queue is on the FreeBSD firewall and not any other device.

We create four named queues under the root, which is implicitly named root_em1. We reserve 30% of bandwidth each for FTP, SSH and other traffic, and 10% for ICMP. However, any class can exceed its reserved bandwidth, up to the upperlimit, which defaults to 100%, which means that one class can potentially cause delays to traffic in other classes, so we override this to 99%.

Note that even though we create the queues on the em1 device, we must filter packets on bridge0, as otherwise our traffic does not match our pf rules.

Update: I found some more information about traffic shaping and advanced usage of HFSC, including realtime guaranteed classes for VoIP.