So, after cleaning up the interface abstraction code in pf with Ryan before the Hackathon, I worked on interface groups integration to pf.
An interface group, is, well, a group of interfaces (surprised, anyone?). Interfaces can join and leave interface groups any time, and can be member in an arbitary number of groups. The join and leave is done via ifconfig: ifconfig sk1 group dmz makes sk1 join the group dmz, and ifconfig sk1 -group dmz removes sk1 from that group again. A group is removed when it does not have any members any more and pf does not refer to it. So far, so good. Now, pf can use interface groups instead of interfaces basically everywhere now. Sounds simple, but is quite powerful. For example, you can (ab-)use interface groups as a kind of aliasing. Just a group with one member, and refer to that. For example, hang your dmz of an interface group called "dmz" - if you do this in a consistent manner, your ruleset is entirely independent from the underlying hardware, you make interfaces join the groups in their respective hostname.if files which are machine dependent anyway. now, if you add a second dmz interface for whatever reasons with the same policy, you don't even have to modify (usually not even reload) your ruleset - just make the 2nd dmz interface join the group :) that of course makes much more sense for your "external" interface, where you might get a second internet connection, or customer-facing interfaces which have the same policies. pf can refer to interfaces and interface groups which do not exist (yet) - once the interface / the group shows up, it will be atached to the construct pf uses and (without ruleset reloads!) things Just Work. Moreover, you can use the brace notation for a dynamic interface name to ip address translation, as in, pass in on tun0 proto tcp to (tun0) port ssh keep state and the like. Internally, pf uses a table named after the interface inside the _pf anchor, and updates the table whenever there are address changes on that interface. That works for interface groups too, now - including correct handling of interfaces joining and leaving the group in question, of course. so, if you put all your customer-facing interfaces (vlans or physical, or any combination... as long as it is interfaces :) ) in a group "customers", (customers) correctly expands to all ip addresses on your customer-facing interfaces - and (customer:network) to all networks on them. And suddenly nice things like block in on egress from (customer:network) work. For clonable interfaces (almost all virtual ones are, tun, ppp, lo, vlan, etc), the clones are all member of an interface class group, for example, all loopback interfaces are part of the "lo" interface group, all vlan interfaces are part of the "vlan" group, etc. this is especially useful in cases where interfaces get created by a daemon on a "next free" basis, like tun with userland ppp. now, we had a sick idea for a while, and since we finally had all the parts together now I could implement it - there is an "egress" interface group now which follows the default routes. This interface group contains all interfaces which IPv4 and IPv6 default routes point to - usually, that is one. It even understands multipath routes already, despite them not being useful yet. The group is updated (actually, rebuilt) every time there is changes/additions/deletions of an IPv4 or IPv6 default route. So, imagine that on your notebook, where you are sometimes on wireless and sometimes on wired network connections - just write your pf.conf so that it refers to the egress group instead of wi0 and em0, and it will Just Work :)