Ce document décrit mon installation d'OpenBSD 5.3 et des outils nécéssaires pour disposer d'un routeur domestique complet sur une carte Alix 2D13.

Objet de ce document

Cette page n'est pas à proprement parler un guide d'installation, mais un memo rédigé dans un but didactique et publié dans l'espoir qu'il puisse servir à d'autres.

Des connaissances de base en réseaux TCP/IP (v4 et v6) sont requises, ainsi qu'une certaine expèrience des systèmes Unix, afin que ce document soit utile.

Fonctionnalités attendues

Accès rapide

  1. Installation
  2. Configuration système
    1. Données persistantes
    2. Accès distant
    3. Gestion des paquets
  3. Configuration réseau
    1. Interfaces réseau
    2. Tunnel IPv6
  4. Routage
  5. Pare-feu
    1. Configuration
    2. Qos
    3. AuthPF
  6. DHCP
    1. IPv4
    2. IPv6
  7. DNS
  8. Utilitaires et notes diverses
    1. Statistiques réseau
    2. Améliorations envisagées
    3. Vue d'ensemble
  9. Références

Installation du système

Il est possible d'installer OpenBSD de différentes manières sur une carte Alix :

J'ai choisi la seconde option, à l'aide de flashrd. N'ayant pas besoin d'adaptation particulière, l'image proposée par défaut convient très bien et l'installation se résume donc à ces quelques opérations après avoir branché un adaptateur CF<->USB sur une autre machine OpenBSD[1] :

# wget http://www.nmedia.net/flashrd/flashrd-1.3.tar.gz
# tar xzf flashrd-1.3.tar.gz
# wget http://www.nmedia.net/flashrd/images/20120831/flashimg.i386.com0-20120831.gz
# gzip -d flashimg.i386.com0-20120831.gz
# ./flashrd-1.3/growimg -t sd0 flashimg.i386.com0-20120831

Après avoir inséré la carte CF dans son emplacement, raccordé un cable série et branché l'alimentation, l'Alix démarre immédiatement sur OpenBSD. Pour la configuration initiale j'ai utilisé mon ordinateur portable sous Debian avec un adapteur série USB et minicom, voici le contenu de mon fichier /etc/minicom/minirc.alix :

% cat /etc/minicom/minirc.alix 
pu port             /dev/ttyUSB0
pu baudrate         38400
pu rtscts           No 
pu xonxoff          Yes

Il ne reste qu'à configurer[2] un minimum le système pour pouvoir l'utiliser confortablement.

# rw
# echo "firewall" > /etc/myname
# echo "description \"CONFIGURATION\" \ninet 192.168.1.1 255.255.255.0 NONE" > /etc/hostname.vr0
# ro
# reboot

Repasser en read-only et effectuer un reboot n'est pas nécéssaire, mais cela permet de vérifier que tout se passe correctement. Le reste de la configuration se ferra en SSH et il est possible de débrancher le cable série.

Configuration de base

Données persistantes

À ce point, les modifications faites sur les partitions /usr, /etc, /sbin, /root, /bin et /flash seront conservées au prochain redémarrage (à condition de passer en "rw" bien évidement), mais les partitions /var et /tmp elles, seront complétement réinitialisées. De même, /home ne disposant pas de point de montage spécifique, il réside sur la racine qui est un ramdisk peuplé par les scripts d'init au démarrage.

En ce qui concerne le(s) repertoire(s) personnel(s), une solution consiste à faire un lien symbolique de /home vers un repertoire dédié dans /flash (par exemple) dès le démarrage en ajoutant dans le script /etc/rc.local :

ln -fs /flash/home /home
# Run flashrd final boot routine
/etc/rc.flashrd.local

Le script /etc/rc.flashrd.local repasse le système en lecture seule, il est donc important de faire le lien symbolique avant son appel.

Pour /var, qui va contenir les journaux, la base de données des paquets, certains fichiers de configuration, et d'autres données moins critiques, une archive sera créée lors du reboot ou de l'arret de la machine en ajoutant (ou décommentant) les lignes suivantes du script /etc/rc.flashrd.sub :

tardirs="var"
set -A tarsize  131072                  # mfs sizes for tar dirs
#
# If you want to save all "tardirs" contents on shutdown:
savetardirs="$tardirs"

Lors d'une modification importante de cette arborescence, l'archive peut être recréée immédiatement en lançant :

# tar cf /flash/var.tar -C /var .

Accès distant

Comme il est prévu d'accèder au routeur depuis l'extèrieur, un compte utilisateur est créé, et le login SSH en root désactivé :

# adduser
[...]
# usermod -S wheel someuser 

Avec visudo (8) décommenter la ligne :

%wheel  ALL=(ALL) SETENV: ALL

Et s'assurer de la présence de ces lignes dans /etc/ssh/sshd_config :

PermitRootLogin no
AllowUsers someuser

Il ne reste qu'à recharger la configuration du démon SSH :

# kill -HUP `cat /var/run/sshd.pid`

Gestion des paquets

Pour utiliser pkg_add (1) il faut exporter une variable d'environnement avec l'URL d'un mirroir (il peut être utile de l'ajouter dans le .profile) :

PKG_PATH="ftp://ftp.openbsd.org/pub/OpenBSD/5.3/packages/i386/" 
export PKG_PATH

Configuration réseau

Interfaces physiques

Le modèle 2D13 dispose de trois interfaces réseau 10/100 BaseTX, la première est pour l'instant utilisée pour configurer l'Alix, mais sera libre une fois la configuration terminée et il sera possible de l'utiliser afin d'avoir un second réseau local, une DMZ, ou même pour y brancher une connexion de secours.

Les deux autres interfaces, vr1 et vr2, sont respectivement prévues pour recevoir la connexion au modem cable de mon FAI configuré en bridge pour l'occasion, et la connexion vers le switch de mon LAN.

# echo "description \"CABLE\" \ndhcp" > /etc/hostname.vr1
# echo "description \"LAN\" \ninet 172.16.12.1 255.255.255.0 NONE" > /etc/hostname.vr2

Enfin, il reste à configurer la carte WiFi que j'ai ajouté (une Wistron DNMA92, qui utilise un chipset Atheros). Plutôt que de faire un bridge avec l'interface vr2, j'ai fait le choix d'utiliser un réseau distinct du LAN (plus de détails sont donnés dans la section Pare-feu) :

# echo "description \"WIFI\" \ninet 172.16.13.1 255.255.255.0 NONE media autoselect mode 11g \
mediaopt hostap nwid NETWORKSSID wpa wpaprotos wpa2 wpaakms psk wpakey SUPERSECRETPASSPHRASE \
chan 6 up" > /etc/hostname.athn0

Et les interfaces réseau sont activées :

# sh /etc/netstart

Tunnel IPv6

Mon FAI ne propose déjà pas d'IP fixe, il propose encore moins d'IPv6 que ce soit nativement ou via un autre procédé. Je dispose donc d'un tunnel chez Sixxs pour la connectivité v6. Aiccu (le client permettant de monter le tunnel) est disponible de longue date dans les ports et les paquets d'OpenBSD.

La configuration est triviale, voici le contenu du fichier /etc/aiccu.conf :

username LOGIN-SIXXS
password SIXXSPASSWORD
protocol tic
server tic.sixxs.net
ipv6_interface sixxs
verbose false
daemonize true
automatic true
requiretls false
pidfile /var/run/aiccu.pid
defaultroute true 

Aiccu est très sensible à la synchronisation de l'heure, et il vaut mieux synchroniser l'horloge avec un serveur NTP. Le démon ntpd (8) est configuré par défaut pour utiliser pool.ntp.org ce qui convient parfaitement, il suffit simplement de le lancer :

# ntpd

Si les logs indiquent que l'horloge est bien synchronisée (à vérifier dans /var/log/daemon), aiccu (8) peut être lancé :

# /etc/rc.d/aiccu start

Si le démon a bien démarré, une interface tun0 a été ajoutée, ainsi que les routes par défaut, et l'Alix est déjà accessible en IPv6.

Il ne reste qu'a lancer automatiquement les deux démons au démarrage de la machine en ajoutant dans /etc/rc.conf.local :

ntpd_flags=""
pkg_scripts="aiccu"

Routage

Le routage n'est par défaut pas activé, il est mis en place ainsi :

# sysctl -w net.inet.ip.forwarding=1
# sysctl -w net.inet.ip.redirect=0
# sysctl -w net.inet6.ip6.forwarding=1
# sysctl -w net.inet6.ip6.accept_rtadv=0

La directive net.inet.ip.redirect=0 permet d'ignorer l'ICMP-Redirect qui n'est que très rarement utilisé, et peut être detourné à des fins malicieuses. La directive net.inet6.ip6.accept_rtadv=0 permet d'ignorer les annonces de routeur IPv6 (puisqu'il s'agit déjà d'un routeur).

Les paquets seront donc routés entre les différentes interfaces (LAN, WAN et Wifi) et PF s'occupera du NAT et des éventuelles redirections de ports pour l'IPv4.

Il ne reste qu'à ajouter ces 4 directives dans /etc/sysctl.conf pour qu'elles soient appliquées automatiquement au démarrage :

net.inet.ip.forwarding=1
net.inet.ip.redirect=0
net.inet6.ip6.forwarding=1
net.inet6.ip6.accept_rtadv=0

Pare-feu

Configuration

En plus de router les paquets, la machine va également faire office de pare-feu. Voici le contenu du fichier /etc/pf.conf final, certaines sections seront détaillées plus loin :

#########################
# General configuration #
#########################

# Interfaces

if_lo = "lo0"
if_backup = "vr0"
if_cable = "vr1"
if_lan = "vr2"
if_wlan = "athn0"
if_v6 = "tun0"

# Networks

net_lo = "127.0.0.0/8"
net_backup = "192.168.1.0/24"
net_lan = "172.16.12.0/24"
net_wlan = "172.16.13.0/24"

# Ports

basic_tcp = "{domain,ftp,ftp-data,whois,http,https,pop3,imap,submission,pop3s,imaps}"
basic_udp = "{domain}"

##########
# Tables #
##########

table <martians> const { 127.0.0.0/8 192.168.0.0/16 172.16.0.0/12 \
    10.0.0.0/8 169.254.0.0/16 192.0.2.0/24 0.0.0.0/8 198.51.100.0/24 \
    203.0.113.0/24 }
table <bruteforce> persist

###########
# Options #
###########

# While their is only one connection we can keep it
set state-policy if-bound
# Reject unauthorized connections politely
set block-policy return
# Monitor the internet interface
set loginterface $if_cable
# Allow loopback obviously
set skip on $if_lo

#######
# QoS #
#######

# Out
## Define queues
altq on $if_cable cbq bandwidth 5Mb queue { ack, dns, \
    ssh_login, ssh_bulk, std }
queue ack bandwidth 50% priority 7 cbq(borrow)
queue dns bandwidth 5% priority 6 cbq(borrow)
queue ssh_login bandwidth 5% priority 5 cbq(borrow ecn)
queue ssh_bulk bandwidth 30% cbq(borrow)
queue std bandwidth 10% cbq(borrow red default)

## Assign packets to a queue
match out on $if_cable proto tcp from any to any queue(std, ack)
match out on $if_cable proto tcp from any to any port { ssh 2222 } queue(ssh_bulk, ssh_login)
match out on $if_cable proto { tcp udp } from any to any port domain queue(dns)

#######
# NAT #
#######

match out on $if_cable from $net_lan to !(if_cable) nat-to ($if_cable)
match out on $if_cable from $net_wlan to !(if_cable) nat-to ($if_cable)

pass in quick on $if_cable proto tcp from any to $if_cable port 2222 rdr-to 172.16.12.xx port 22

############
# Firewall #
############

# Everything is blocked by default
block all

# Antispoof
antispoof for if_cable inet 
antispoof for if_lan inet 
antispoof for if_wlan inet 

## Cable filters

# Martians packet shouldn't come from internet
block drop in on $if_cable inet from <martians> to any
block return out log on $if_cable inet from any to <martians>

# Block zombies or skiddies without being polite
block drop in quick on $if_cable inet from <bruteforce>

# We don't want to block ICMP
pass in on $if_cable inet proto icmp from any to ($if_cable) \
    icmp-type { echoreq unreach timex paramprob } keep state

# Allow SSH 
pass in quick on $if_cable inet proto tcp from any to ($if_cable) \
    port 22 flags S/SFRA modulate state \
    (max-src-conn-rate 5/30, overload <bruteforce> flush global)

# Allow HTTP for the office
pass in quick on $if_cable inet proto tcp from xxx.xxx.xxx.xxx \
    to (self) port http

# Outgoing traffic should be clean
pass out on $if_cable inet from ($if_cable) to any modulate state \
    flags S/SA

## LAN filters

# To be sure I'll keep an access
pass in quick on $if_lan inet proto tcp from any to (self) port ssh \
    keep state flags S/SA

# To be sure I'll keep an access
pass in quick on $if_backup inet proto tcp from any to (self) port ssh \
    keep state flags S/SA

# For the moment, I trust my LAN
pass in on $if_lan
pass out on $if_lan

# And I trust my backup interface
pass in on $if_backup
pass out on $if_backup

# Specific rules for WLAN
pass in on $if_wlan inet proto tcp to any port $basic_tcp \
    modulate state flags S/SA
pass in on $if_wlan inet proto udp to any port $basic_udp \
    modulate state
pass in on $if_wlan inet proto icmp from any to any \
    icmp-type { echoreq unreach timex paramprob } modulate state
pass in on $if_wlan proto tcp to (self) port ssh \
    modulate state flags S/SA
anchor "authpf/*"

# IPv6 tests
pass in quick on $if_v6 inet6 from any to !(self) keep state
pass out quick on $if_v6 inet6 proto { tcp udp ipv6-icmp } keep state

pass in quick on $if_v6 inet6 proto tcp from any to (self) \
    port 22 flags S/SFRA modulate state \
    (max-src-conn-rate 5/30, overload <bruteforce> flush global)

La configuration est chargée et le pare-feu est activé avec pfctl (8) :

# pfctl -f /etc/pf.conf
# pfctl -e

Afin que Packet Filter soit activé au démarrage, si ce n'est pas déjà le cas, la ligne suivante doit être présente dans /etc/rc.conf ou /etc/rc.conf.local :

pf=YES

Cette configuration n'est pas restrictive pour le LAN, elle autorise notamment tout traffic sortant. Le réseau WiFi en revanche est beaucoup plus restreint, et ne peut accèder à presque rien d'autre que le web et les mails. Les machines dignes de confiance sur ce dernier pourront cependant s'identifier avec authpf (8) et disposer d'ouvertures de ports supplémentaires.

En IPv6 le routeur ne filtre rien et il est donc indispensable d'avoir un pare-feu sur les machines connectées derrière.

QoS

Afin de conserver un usage fluide de la connexion à tout moment, certains protocoles se voient attribuer des ressources dédiées. La bande passante sortante (5Mb/s dans mon cas) est découpée en plusieurs queues :

#######
# QoS #
#######

# Out
## Define queues
altq on $if_cable cbq bandwidth 5Mb queue { ack, dns, \
    ssh_login, ssh_bulk, std }
queue ack bandwidth 50% priority 7 cbq(borrow)
queue dns bandwidth 5% priority 6 cbq(borrow)
queue ssh_login bandwidth 5% priority 5 cbq(borrow ecn)
queue ssh_bulk bandwidth 30% cbq(borrow)
queue std bandwidth 10% cbq(borrow red default)

Il est possible d'assigner les ACKs à une queue et de différencier les connexions SFTP des connexions SSH grace au flag ToS des paquets TCP. En passant deux queues comme arguments d'un règle PF, les paquets portants le flag "low-delay" iront dans la dernière. C'est d'ailleurs pour cela que la règle attribuant les paquets SSH et SFTP à leurs queues respectives est placée après la règle s'occupant des ACKs et du traffic standard : sauf si la directive quick est utilisée, Packet Filter applique toujours la dernière régle correspondante.

## Assign packets to a queue
match out on $if_cable proto tcp from any to any queue(std, ack)
match out on $if_cable proto tcp from any to any port { ssh 2222 } queue(ssh_bulk, ssh_login)
match out on $if_cable proto { tcp udp } from any to any port domain queue(dns)

L'option borrow présente sur toutes les règles ne pose pas de problème, chaque queue pourra utiliser la totalité de la bande passante si aucun autre traffic n'est présent, mais ne pourra pas dépasser son quota en cas de forte solicitation.

Note :

Il n'est normalement pas possible de faire de QoS en entrée, mais sur un routeur disposant de plusieurs interfaces, une astuce consisterait à ajouter de la QoS en sortie sur le port dédié au LAN, ce qui simulerait une QoS entrante sur le port WAN. Cependant activer Altq, même sans aucune règle, provoque une baisse de performances non négligeable sur la vitesse du port, ce qui est trop perceptible sur ma connexion puisque le cable me permet un débit descendant théorique (et souvent proche dans la pratique) de 100M. Mais cela serait parfaitement utilisable, et probablement bénéfique, sur une ligne ADSL.

AuthPF

Le réseau WiFi est par défaut isolé du LAN, et n'a que très peu d'accès vers l'extèrieur. Pour les invités de confiance, ou même pour mes machines lorsque je souhaite me connecter sans fil, une authentification avec authpf (8) va permettre de charger et décharger dynamiquement des règles spécifiques à la machine authentifiée.

La mise en place n'est pas très compliquée, mais demande quelques étapes :

# echo "/usr/sbin/authpf" >> /etc/shells
# mkdir -p /etc/pfauth
# touch /etc/authpf/authpf.conf
# chown root:authpf /etc/authpf/authpf.conf

Les utilisateurs authentifiés auront accès à tout internet sauf les ports 25 et 5666 :

# echo "if_wlan = \"athn0\"\npass in on \$if_wlan from \$user_ip to any\n\
block return in on \$if_wlan from \$user_ip to 172.16.12.0/24\n\
block return in on \$if_wlan proto tcp from \$user_ip to any port { 25,5666 }" > /etc/authpf/authpf.rules
# chown root:authpf /etc/authpf/authpf.rules
# chmod 640 /etc/authpf/authpf.rules
# useradd -g authpf -c "Authpf guest user" -d /var/empty -s /usr/sbin/authpf guest
# passwd guest

Les règles du fichier /etc/authpf/authpf.rules s'appliquent à tous les utilisateurs ne disposant pas de règles spécifiques. Pour mon ordinateur portable par exemple, je souhaite n'avoir aucune restriction et pouvoir joindre toutes les machines du LAN :

# mkdir -p /etc/authpf/users/laptop
# echo "if_wlan = \"athn0\"\npass in quick on \$if_wlan from \$user_ip \n\
pass out quick on \$if_wlan from any to \$user_ip" > /etc/authpf/users/laptop/authpf.rules
# useradd -g authpf -c "Authpf laptop user" -d /var/empty -s /usr/sbin/authpf laptop
# passwd laptop

Note :

Les exemples précédents ne montrent la création que d'un seul utilisateur invité, mais il n'est possible d'utiliser un compte qu'à partir d'une seule machine à la fois. Pour plusieurs machines connectées simultanément, il faut autant de comptes que de machines.

DHCP

IPv4

Un serveur DHCP attribuera des IPv4 privées automatiquement sur les reseaux LAN et WiFi. Le serveur DHCP proposé de base par OpenBSD est une version modifiée du serveur ISC qui ne semble pas supporter l'annonce de plusieurs domain-name, j'ai donc décidé d'installer le serveur ISC standard (isc-dhcp-server) avec pkg_add, voici le contenu du fichier /etc/dhcpd.conf :

authoritative;

shared-network LAN {

    option  domain-name "home.lan example.net";
    option  domain-name-servers 172.16.12.1;

    subnet 172.16.12.0 netmask 255.255.255.0 {
    
        option routers 172.16.12.1;
        range 172.16.12.10 172.16.12.50;
        
        host fenrir {
                hardware ethernet 6c:f0:xx:xx:xx:xx;
                fixed-address 172.16.12.xx;
        }
        
        #host wii {
        #       hardware ethernet xx:xx:xx:xx:xx:xx;
        #       fixed-address 172.16.12.xx;
        #}
            
    }

}

shared-network WLAN {

    option  domain-name "home.lan example.net";
    option  domain-name-servers 172.16.13.1;

    subnet 172.16.13.0 netmask 255.255.255.0 {
    
        option  domain-name-servers 172.16.13.1;

        option routers 172.16.13.1;
        range 172.16.13.10 172.16.13.50;
        
    }

}

Pour lancer le serveur DHCP non patché par l'equipe d'OpenBSD il faut utiliser le binaire présent dans /usr/local/sbin :

# /usr/local/sbin/dhcpd

IPv6

DHCPv6 ne sera pas utilisé, mais remplacé par le protocole d'annonces de routeur standard en IPv6 qui est fourni sous OpenBSD par le démon rtadvd (8). Voici le fichier /etc/rtadvd.conf :

vr2:\
       :addrs#1::addr="2a01:dead:beef:1::":prefixlen#64:raflags#64:
athn0:\
       :addrs#1::addr="2a01:dead:beef:2::":prefixlen#64:raflags#64:

"2a01:dead:beef::" étant le /48 qui m'est fourni par SixXS. Deux /64 sont découpés dedans, pour l'interface LAN et l'interface WiFI.

Il faut ajouter une IP du préfixe annoncé sur chaque interface :

# ifconfig vr2 inet6 2a01:dead:beef:1::1 prefixlen 64
# ifconfig athn0 inet6 2a01:dead:beef:2::1 prefixlen 64

Et lancer rtadvd en lui spécifiant les interfaces concernées :

# rtadvd vr2 athn0

Puis rendre ces modifications permanentes :

# echo "inet6 2a01:dead:beef:1::1 prefixlen 64" >> /etc/hostname.vr2
# echo "inet6 2a01:dead:beef:2::1 prefixlen 64" >> /etc/hostname.athn0
# echo "rtadvd_flags=\"vr2 athn0\"" >> /etc/rc.conf.local

DNS

Le service de DNS, recursif et autoritaire, sera fourni par unbound (8). Son empreinte mémoire étant très faible[4], il est un candidat parfait pour de l'embarqué tel qu'une carte Alix. Les fonctionnalité de serveur autoritaire sont moins avancées que Bind ou NSD mais elles ne seront utilisées que pour fournir des enregistrements A et PTR pour la zone locale.

Afin de pouvoir contacter les serveurs racines, Unbound va avoir besoin de la liste de ces derniers :

# wget ftp://FTP.INTERNIC.NET/domain/named.cache -O /var/unbound/etc/root.hints

Unbound est également capable de valider les noms avec DNSSEC, à condition de disposer de la signature racine, disponible chez l'IANA, qui permet d'obtenir[5] le fichier /var/unbound/etc/root.key suivant:

. IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5

Ce fichier ainsi que le repertoire parent[6] doivent être inscriptibles en écriture par Unbound, sous OpenBSD ce dernier tourne avec l'utilisateur "_unbound".

Et voici le contennu du fichier /var/unbound/etc/unbound.conf :

server:
    verbosity: 1

    interface: 0.0.0.0
    port: 53
    do-ip4: yes
    do-ip6: yes
    do-udp: yes
    do-tcp: yes
    access-control: 127.0.0.0/8 allow
    access-control: 172.16.12.0/24 allow
    access-control: 172.16.13.0/24 allow
    access-control: 2a01:dead:beef:1::/48 allow

    root-hints: "/var/unbound/etc/root.hints"
    auto-trust-anchor-file: "/var/unbound/etc/root.key"

    hide-identity: yes
    hide-version: yes
    harden-glue: yes
    harden-dnssec-stripped: yes
    use-caps-for-id: yes
    cache-min-ttl: 3600
    cache-max-ttl: 86400
    prefetch: yes

    private-address: 10.0.0.0/8
    private-address: 172.16.0.0/12
    private-address: 192.168.0.0/16

    private-domain: "home.lan."
    local-zone: "home.lan." static

    local-data: "firewall.home.lan.  IN A 172.16.12.1"
    local-data: "fenrir.home.lan.    IN A 172.16.12.xx"

    local-data-ptr: "172.16.12.1  firewall.home.lan"
    local-data-ptr: "172.16.12.xx  fenrir.home.lan"

Pour être lancé au démarrage, le paquet d'Unbound propose des scripts d'init dans /etc/rc.d, mais ils sont exécutés avant l'extraction de l'archive qui permet de peupler /var, il faut donc lancer le démon via /etc/rc.local :

if [ -x /usr/local/sbin/unbound ]; then
    echo -n ' unbound'; /usr/local/sbin/unbound 
fi

Utilitaires et notes diverses

Statistiques réseau

Afin d'avoir une vue d'ensemble de ce qui transite par le routeur, pfstat (8) permet de grapher de nombreuses valeurs issues de Packet Filter. Voici un /etc/pfstat.conf initial :

# collect
#   global
#     states entries|searches|inserts|removals [diff]
#     counters match|bad-offset|fragment|...|synproxy [diff]
#              (see pfctl -si output, same strings)
#   interface name pass|block packets|bytes in|out v4|v6 [diff]
#   queue name passed|dropped|other packets|bytes|number [diff]

collect 1 = interface "vr1" pass bytes in ipv4 diff
collect 2 = interface "vr1" pass bytes out ipv4 diff
collect 3 = interface "vr1" block packets in ipv4 diff
collect 4 = interface "vr2" pass bytes in ipv4 diff
collect 5 = interface "vr2" pass bytes out ipv4 diff
collect 6 = interface "athn0" pass bytes in ipv4 diff
collect 7 = interface "athn0" pass bytes out ipv4 diff
collect 8 = interface "tun0" pass bytes in ipv6 diff
collect 9 = interface "tun0" pass bytes out ipv6 diff
collect 10 = interface "tun0" block packets in ipv6 diff
collect 11 = global states entries


#######
# Cable
#######

image "/var/www/htdocs/cable_day.jpg" {
        from 1 days to now
        width 980 height 350
        left
                graph 1 "In" "bytes/s" color 45 125 179,
                graph 2 "Out" "bytes/s" color 73 168 53
        right
                graph 3 "Blocked" "packets/s" color 203 12 41
        
}

image "/var/www/htdocs/cable_week.jpg" {
        from 1 weeks to now
        width 980 height 350
        left
                graph 1 "In" "bytes/s" color 45 125 179,
                graph 2 "Out" "bytes/s" color 73 168 53
        right
                graph 3 "Blocked" "packets/s" color 203 12 41
        
}

image "/var/www/htdocs/cable_month.jpg" {
        from 1 months to now
        width 980 height 350
        left
                graph 1 "In" "bytes/s" color 45 125 179,
                graph 2 "Out" "bytes/s" color 73 168 53
        right
                graph 3 "Blocked" "packets/s" color 203 12 41
        
}

#####
# LAN
#####

image "/var/www/htdocs/lan_day.jpg" {
        from 1 days to now
        width 980 height 350
        left
                graph 4 "In" "bytes/s" color 45 125 179,
                graph 5 "Out" "bytes/s" color 73 168 53
}

image "/var/www/htdocs/lan_week.jpg" {
        from 1 weeks to now
        width 980 height 350
        left
                graph 4 "In" "bytes/s" color 45 125 179,
                graph 5 "Out" "bytes/s" color 73 168 53
}

image "/var/www/htdocs/lan_month.jpg" {
        from 1 months to now
        width 980 height 350
        left
                graph 4 "In" "bytes/s" color 45 125 179,
                graph 5 "Out" "bytes/s" color 73 168 53
}

######
# WIFI
######

image "/var/www/htdocs/wlan_day.jpg" {
        from 1 days to now
        width 980 height 350
        left
                graph 6 "In" "bytes/s" color 45 125 179,
                graph 7 "Out" "bytes/s" color 73 168 53
}

image "/var/www/htdocs/wlan_week.jpg" {
        from 1 weeks to now
        width 980 height 350
        left
                graph 6 "In" "bytes/s" color 45 125 179,
                graph 7 "Out" "bytes/s" color 73 168 53
}

image "/var/www/htdocs/wlan_month.jpg" {
        from 1 months to now
        width 980 height 350
        left
                graph 6 "In" "bytes/s" color 45 125 179,
                graph 7 "Out" "bytes/s" color 73 168 53
}

######
# IPV6
######

image "/var/www/htdocs/ipv6_day.jpg" {
        from 1 days to now
        width 980 height 350
        left
                graph 8 "IPv6 In" "bytes/s" color 132 71 152,
                graph 9 "IPv6 Out" "bytes/s" color 45 125 179
#       right
#                graph 10 "Blocked" "packets/s" color 203 12 41
}

image "/var/www/htdocs/ipv6_week.jpg" {
        from 1 weeks to now
        width 980 height 350
        left
                graph 8 "IPv6 In" "bytes/s" color 132 71 152,
                graph 9 "IPv6 Out" "bytes/s" color 45 125 179
#       right
#                graph 10 "Blocked" "packets/s" color 203 12 41
}

image "/var/www/htdocs/ipv6_month.jpg" {
        from 1 months to now
        width 980 height 350
        left
                graph 8 "IPv6 In" "bytes/s" color 132 71 152,
                graph 9 "IPv6 Out" "bytes/s" color 45 125 179
#       right
#                graph 10 "Blocked" "packets/s" color 203 12 41
}

image "/var/www/htdocs/states_day.jpg" {
        from 1 days to now 
        width 980 height 350
        left
                graph 11 "States" "entries" color 255 140 0
}

image "/var/www/htdocs/states_week.jpg" {
        from 1 weeks to now 
        width 980 height 350
        left
                graph 11 "States" "entries" color 255 140 0
}

image "/var/www/htdocs/states_month.jpg" {
        from 1 months to now 
        width 980 height 350
        left
                graph 11 "States" "entries" color 255 140 0
}

Le processeur de la 2D13 n'est pas très puissant, mais il permet tout de même de tracer les graphs toutes les 5 minutes sans problème, voici la crontab de root :

*    *    *   *   *   /usr/local/bin/pfstat -q -d /var/db/pfstat.db
*/5  *    *   *   *   /usr/local/bin/pfstat -p -d /var/db/pfstat.db
25   3    *   *   *   /usr/local/bin/pfstat -t 30 -d /var/db/pfstat.db

Ces commandes assurent respectivement : la collecte des données toutes les minutes, le traçage des graphiques toutes les 5 minutes, et le nettoyage chaque nuit de la base de données pour ne conserver qu'un mois d'historique.

Il ne reste qu'à faire une belle page HTML et lancer un serveur Web pour les consulter.

Améliorations envisagées

Compte tenu des possibilités offertes par le matèriel et par OpenBSD, de nombreux autre services peuvent être envisagés :

Vue d'ensemble

Voici l'adressage final du réseau :

Interface Adressage IPv4 Adressage IPv6 Description
vr0 192.168.1.1/24 - Backup
vr1 DHCP - WAN
vr2 172.16.12.1/24 2a01:dead:beef:1::1/64 LAN
athn0 172.16.13.1/24 2a01:dead:beef:2::1/64 WiFi
tun0 - 2a01:cafe::2/128 Tunnel IPv6

Références


[1] Il est possible d'écrire l'image avec dd depuis n'importe quel Unix, mais sans la possibilité de redimensionnement fournie par growimg.

[2] Dans les exemples donnés la routine echo de KSH interprète par défaut les antislash, les commandes sont à adapter si un autre shell est utilisé.

[3] Cette valeur a été déterminée de manière empirique, car elle dépend pour beaucoup de l'usage qui est fait de la connexion.

[4] Après un mois de service sur mon routeur le démon ne consomme que 22M.

[5] Au moment de la rédaction de ce document (octobre 2013).

[6] Il semblerait qu'Unbound y écrive temporairement des clefs lors de la vérification d'un domaine ou de la clef racine.