Nftables : remplacant de Iptables

Nftables : remplacant de Iptables jpp

NOTE février 2022 : CE "LIVRE" EST TOUJOURS EN COURS DE REALISATION. 
Le noyau utilise Nftables mais, par manque de temps, j'utilise toujours l'interface iptables + ipset, pour le moment cela fonctionne encore très bien. 
En plus je n'ai pas encore bien compris la philosophie sous tendue par Nftables et j'ai du mal à concevoir un pare-feu à la fois simple, complet et facile à maintenir. 
Nouveauté Mai 2016 : utilisation notation CIDR et intervalles dans un "set nommé", voir l'article spécifique.

"NFTABLES" :

C'est le remplaçant prévu des commande IPTABLES et IPSET utilisées par tous les parefeux sour Linux. 
Le développement ne semble pas hyper actif ni réactif et de nombreuses commandes ne sont pas encore, à mon goût, complètes, notamment au niveau des "sets" (remplaçants de Ipset) ou suffisament souples. 
Tout est prêt au niveau du noyau Linux depuis déjà quelques versions (3.16 en principe) mais je pense que pour des tests (NFTABLES ne semble pas encore prêt pour mise en exploitation) il vaut mieux utiliser une version très récente du noyau. 
Ici j'utiliserai une 3.19-rc6, la dernière disponible au moment de ces tests. 
Il faut utiliser un moyau compilé avec tous les modules spécifiques à NFTABLES, malheureusement dans "make menuconfig" les sifférentes options sont un peu dispersées. Il faut aussi se rappeler que NFTABLES concernera IpV6 aussi bien que IpV4, donc configurer le tout.

Il faut avoir configuré dans le noyau (>= 3.16, ici 3.19) les extensions pour "nftables": 
   <M> Netfilter nf_tables support 
   <M>   Netfilter nf_tables mixed IPv4/IPv6 tables support 
   <M>   Netfilter nf_tables IPv6 exthdr module 
   <M>   Netfilter nf_tables meta module 
   <M>   Netfilter nf_tables conntrack module 
   <M>   Netfilter nf_tables rbtree set module 
   <M>   Netfilter nf_tables hash set module 
   <M>   Netfilter nf_tables counter module 
   <M>   Netfilter nf_tables log module 
   <M>   Netfilter nf_tables limit module 
   <M>   Netfilter nf_tables masquerade support 
   <M>   Netfilter nf_tables redirect support 
   <M>   Netfilter nf_tables nat module 
   <M>   Netfilter nf_tables queue module 
   <M>   Netfilter nf_tables reject support 
Ajouter Ethernet bridge nf_tables support 
--- Ethernet Bridge nf_tables support    
  <M>   Netfilter nf_table bridge meta support 
  <M>   Netfilter nf_tables bridge reject support

Il est conseillé de charger tous les modules nécessaires au démarrage en constituant une liste des modules dans un fichier dédié (ici nftables.conf) dans /etc/modules-load.d pour une Debian "Testing" ou dans le fichier /etc/modules pour une Debian "stable". 
Les modules se situent dans /lib/modules/$(uname -r)/kernel/net/netfilter et commencent presque tous par "nft"  sauf les deux plus importants "nf_tables_inet" et "nf_tables". 
Liste des modules chargés : 
nf_tables 
nf_tables_inet 
nft_chain_nat_ipv6 
nft_chain_nat_ipv4 
nft_chain_route_ipv6 
nft_chain_route_ipv4 
nft_counter 
nft_ct 
nft_exthdr 
nft_hash 
nft_limit 
nft_log 
nft_masq 
nft_meta 
nft_nat 
nft_queue 
nft_rbtree 
nft_redir 
nft_reject_inet 
nft_reject 
nft_reject_ipv4 
nft_reject_ipv6 
On sera ainsi parés pour toutes les éventualités.  
 

Quelques références : 
http://wiki.gentoo.org/wiki/Nftables 
http://wiki.nftables.org/wiki-nftables/index.php/Main_Page 
 

NFTABLES : un peu de vocabulaire

NFTABLES : un peu de vocabulaire jpp

Aucune "table" n'est définie par défaut comme avec "iptables", c'est à vous de les définir ! 
Rappel : 
Plus de différence entre IPV4 et IPV6, tout peut être traité dans le même flot, si on utilise des tables "inet", par défaut c'est "ip" (IPV4) qui est choisi, on peut aussi utiliser "ip6" pour du spécifique IPV6. 

Commandes disponibles : 
list 
  ruleset 
  tables [famille] 
  table [famille] <nom> 
  chain [famille] <table> <nom> 
add 
  table [famille] <nom> 
  chaine [famille] <table> <nom> [paramètres de la chaîne] 
  rule [famille] <table> <chaine> <paramètres de la règle> 
  element [famille] <table> <set> { 1..n elements } 
table [famille] <nom> (raccourci pour "add table`) 
insert 
  rule [famille] <table> <chaine> <paramètres de la règle> 
delete 
  table [famille] <nom> 
  chain [famille] <table> <nom> 
  rule [famille] <table> <numero handle> 
  set [famille] <table> <set>     : il ne doit plus être référencé 
  element [famille] <table> <set> { 1..N elements a enlever } 
flush 
  table [famille] <nom> 
  chain [famille] <table> <nom> 

Remarques : 
La famille "ip" (ipv4) est prise par défaut. 
Attention à la syntaxe de "list" "list tables" et "list table ENTREE". 
Liste des familles de "tables" et l'outil iptables correspondant 
ip            iptables 
ip6          ip6tables 
inet         iptables and ip6tables 
arp          arptables 
bridge     ebtables 
Liste des "hooks" existants, tiens tiens ! Les noms me rappellent quelque chose : 
INPUT                    paquet entrant dans le système local 
OUTPUT                paquet sortant du système local 
FORWARD            paquet traversant 
PREROUTING      paquet entrant 
POSTROUTING    paquet sortant 
Liste des "types" de tables existants, là aussi les noms semblent familiers : 
filter        Valeur par défaut 
nat 
route      (mangle), ne semble utilisable qu'avec le hook "output". 
Ordres supportés par la commande "table" 
add 
delete 
list 
flush 
Ordres supportés par la commande "chain" : 
add 
create 
delete 
list 
flush 
rename 
Ordres supportés par la commande "rule" : 
add     ajoute une règle en fin de chaîne 
insert    ajoute une règle en début de chaîne 
delete    détruit une règle, utilisé avec un "handle" cf "nft -a list ...."

Ordres supportés par la commande "ruleset" : 
flush   efface tout 
list       liste tout

Destination des paquets 
accept 
reject 
drop 
snat 
dnat 
log 
counter 
return 
jump    <chaine> 
goto     <chaine> 

Quelques "mots clefs" pour les règles : 
oif       interface sortie suivi d'un numéro 
iif       interface entree suivi d'un numéro 
oifname    nom de l'interface de sortie 
iifname nom de l'interface d'entree 
type    suivi d'un type ICMP par exemple "echo-request" 
protocol tcp, udp ... 
daddr    adresse destination 
saddr    adresse source 
dport    port de destination 
sport    port source 
ct         connexion tracking + state ( NEW, ESTABLISHED, RELATED, INVALID) 
C'est un bon début.

NFTABLES : premier test Debian testing

NFTABLES : premier test Debian testing jpp

Pour ce premier essai j'ai utilisé les paquets disponibles à ce jour sur "testing" : 
libjansson4_2.7-1_amd64.deb 
libmxml1_2.6-2_amd64.deb 
libnftnl0_1.0.3-4_amd64.deb 
nftables_0.4-2_amd64.deb 
Une fois tout ce petit monde en place on peut commencer à tenter de se servir de ce nouvel outil remplaçant désigné de "iptables". 
Références : 
Accéder à "http://people.netfilter.org/wiki-nftables/index.php/Main_Page" pour tous les détails sur nftables. 
Un autre Wiki bien fait est celui d'Archlinux accessible à "https://wiki.archlinux.org/index.php/Nftables".

Premier essai : 
"nft list tables" ou "nft list tables ip6" ne donnent aucune sortie puisque avec Nftables rien n'est créé par défaut ! 
Par contre dès qu'une chaine existe une sortie est fournie : 
nft add table entree     
nft list tables 
--> 
table entree 
L'ensemble a donc l'air parfaitement fonctionnel. On va pouvoir passer à des choses plus sérieuses.

NFTABLES : quelques tests

NFTABLES : quelques tests jpp

Points importants :

  • Nftables est prévu pour fonctionner avec l'option "-f" avec laquelle les données sont contenues dans un fichier. Ce fichier peut être généré par la commande "list" et utilisé ensuite en entrée de la commande "nftables -f ".
  • A l'origine, rien n'est créé, les différentes tables et chaines existant par défaut avec "iptables" n'existent pas ici par défaut. Il faut donc créer toutes les "tables" et y attacher toutes les "chaines" nécessaires.

Quelques exemples : 
 

  • Création d'une table "ENTREE" (Ipv4 par défaut le parametre "ip" étant omis) :

nft add table ENTREE 

Liste des tables : 
nft list tables 
--> table ENTREE

  • Ajout d'une table "SORTIE"

nft add table SORTIE 
nft list tables 
--> 
table ENTREE 
table SORTIE

  • Destruction de la table "SORTIE" :

nft delete table SORTIE 

nft list tables 
--> 
table ENTREE 

Ajout d'une chaine nommée "INTERNE" dans la table ENTREE (type filter et Ipv4 par défaut) : 
nft add chain ENTREE INTERNE 

Destruction de la chaine "INTERNE" de la table ENTREE 
nft delete chain ENTREE INTERNE 

Addition d'une règle dans notre table ENTREE (à la fin) : 
nft add table ENTREE 
nft add chain ENTREE INTERNE 
nft add rule ENTREE INTERNE ip saddr 127.0.0.1 accept 
nft list table ENTREE 
--> 
table ip ENTREE { 
    chain INTERNE { 
         ip saddr 127.0.0.1 accept 
    } 
}

Remarque : On aurait pu ici utiliser aussi "nft list ruleset" pour lister l'ensemble des tables.

  • On accepte le port 22

nft add rule ENTREE INTERNE tcp dport 22 accept 
nft list table ENTREE 
--> 
table ip ENTREE { 
    chain INTERNE { 
         ip saddr 127.0.0.1 accept 
         tcp dport ssh accept 
    } 
}

  • Insérer une règle en début de chaîne

nft insert rule ENTREE INTERNE ct state established, related accept 
On fait une liste avec les numeros de "handle" (option "-a") : 
nft -a list table ENTREE 
--> 
table ip ENTREE { 
    chain INTERNE { 
         ct state established,related accept # handle 5 
         ip saddr 127.0.0.1 accept # handle 2 
         tcp dport ssh accept # handle 4 
    } 
}

  • On ne peut détruire une règle que par son numéro (handle), Ici on détruit la règle 4

nft delete rule ENTREE INTERNE handle 4 
nft -a list table ENTREE 
--> 
table ip ENTREE { 
    chain INTERNE { 
         ct state established,related accept # handle 5 
         ip saddr 127.0.0.1 accept # handle 2 
    } 
}

  • On sauvegarde notre table :

nft list table ENTREE >SAUVE

  • On efface tout

nft delete table ENTREE 
nft list tables 
--> ne retourne rien

  • On restaure notre jeu de test

nft -f sauve 
nft list table ENTREE 
--> 
table ip ENTREE { 
    chain INTERNE { 
         ct state established,related accept 
         ip saddr 127.0.0.1 accept 
    } 
} 
On retrouve notre table telle qu'avant destruction, la sauvegarde d'un jeu de règles et sa restauration sont d'une belle simplicité.

  • On rajoute une autorisation pour SSH et on logue la connexion :

nft add rule ENTREE INTERNE tcp dport ssh log prefix \"MSG=Connect SSH \" 
nft -a list ruleset 
--> 
table ip ENTREE { 
    chain INTERNE { 
         ct state established,related accept # handle 2 
         ip saddr 127.0.0.1 accept # handle 3 
         tcp dport ssh log prefix "Connect SSH" # handle 4 
    } 
}

NFTABLES : on passe par la case COMPIL

NFTABLES : on passe par la case COMPIL jpp

Les tests de "map" et autres joyeusetés n'ayant pas fonctionné avec les versions "standard" il faut passer par la case "compil" et récupérer les morceaux dans un petit répertoire bien tranquille (pour moi "/usr/src/PGM/BUILD/NFT"): 
git clone git://git.netfilter.org/nftables.git 
git clone git://git.netfilter.org/libmnl.git 
git clone git://git.netfilter.org/libnftnl.git 

Pré-requis : 
bison 
flex 
libgmp-dev 
libreadline-dev 
libmnl0 
libmnl-dev 
Remarque : il peut y avoir d'autres pré-requis, la machine ayant servi à la compil est une Debian "stable" avec "backports" qui a déjà beaucoup vécu et comporte déjà pas mal de packages "-dev".

Compilation de "LIBMNL" le script :

#!/bin/bash

cd NFT/libmnl ...... le répertoire source 
zzz=$(pwd) 
yyy=$(basename $zzz) 
case $yyy in 
    libmnl)        echo 'OK' 
            ;; 
    *)        echo 'pas dans libmnl' 
            exit 
            ;; 
esac

make clean

./autogen.sh

# configure libmnl 
CONFOPT=" --prefix=/usr/local/libmnl --exec-prefix=/usr/local/libmnl "

./configure ${CONFOPT} 2>&1 | tee LOG.CONF

make 2>&1 | tee LOG.MAKE

Ne pas oublier ensuite (en "root") le "make install". 

Compilation de "LIBNFTNL" le script :

#!/bin/bash

cd NFT/libnftnl 
zzz=$(pwd) 
yyy=$(basename $zzz) 
case $yyy in 
    libnftnl)        echo 'OK' 
            ;; 
    *)        echo 'pas dans libnftnl' 
            exit 
            ;; 
esac

make clean

./autogen.sh

# configure libnftnl 
CONFOPT=" --prefix=/usr/local/libnftnl --exec-prefix=/usr/local/libnftnl " 
CONFOPT=${CONFOPT}' LIBMNL_LIBS=/usr/local/libmnl ' 
CONFOPT=${CONFOPT}' LIBMNL_CFLAGS=/usr/local/libmnl'

./configure ${CONFOPT} 2>&1 | tee LOG.CONF

make 2>&1 | tee LOG.MAKE

Ne pas oublier (toujours) le "make install" en "root". 

Une fois tout vérifié j'ai copié : 
/usr/local/libmnl/lib/libmnl.* dans  /lib/x86_64-linux-gnu 
/usr/local/libnftnl/lib/libnftm* dans /lib/x86_64-linux-gnu 

et vérifié que la version Debian de "nft" fonctionnait toujours --> OK 

Compilation de "NFTABLES" le script :

#!/bin/bash

PKG_CONFIG_PATH=${PKG_CONFIG_PATH}':/usr/local/libnftnl/lib/pkgconfig' 
PKG_CONFIG_PATH=${PKG_CONFIG_PATH}':/usr/local/libmnl/lib/pkgconfig' 
export PKG_CONFIG_PATH

cd /usr/src/PGM/BUILD/NFT/nftables 
zzz=$(pwd) 
yyy=$(basename ${zzz}) 
case ${yyy} in 
    nftables)    echo 'OK' 
            ;; 
    *)        echo 'pas dans nftables' 
            exit 
            ;; 
esac

make clean

./autogen.sh

# configure nftables

CONFOPT=' --prefix=/usr/local/nftables --exec-prefix=/usr/local/nftables/bin ' 
CONFOPT=${CONFOPT}' --includedir=/usr/include ' 
CONFOPT=${CONFOPT}' LIBMNL_LIBS=/usr/local/libmnl/lib/libmnl.so  ' 
CONFOPT=${CONFOPT}' LIBNFTNL_LIBS=/usr/local/libnftnl/lib/libnftnl.so  '

./configure ${CONFOPT} 2>&1 | tee LOG.CONFIGURE

make 2>&1 | tee LOG.MAKE

echo 'PKG_CONFIG_PATH=('${PKG_CONFIG_PATH}')'

echo '' >./doc/nft.8

Ne pas oublier (encore) le "make install", puis on expédie "/usr/local/nftables/bin/sbin/nft" dans "/usr/sbin".

On pourra ainsi reprendre nos essais ... avec la dernière version.

NFTABLES : version compilée tests

NFTABLES : version compilée tests jpp

Cette petite série de tests a été conduite avec la version compilée localement de "nft" car les autres versions essayées ne "comprenaient" pas certaines nouvelles fonctionnalités.

Premier test : réaliser du NAT : 
Si la table existe un petit "flush" remet tout dans l'ordre, si la table n'existe pas cela déclenche une erreur fatale et rien n'est fait ! C'est très dommage pour l'atomicité des opérations. 
Pour être tranquilles il faut donc tester l'existence de la table et la créer (vide) si besoin. Cela peut se faire avec le petit bout de script suivant :


# On teste l'existence de la table !  
# 
nft list table ip POSTROUTE 2>/dev/null >/dev/null 
ret=$? 
# 
# On crée la table si elle n'existe pas !!! 
# 
if [ $ret -ne 0 ] 
    then 
       nft add table ip POSTROUTE 
fi 
On peut maintenant lancer la suite du script avec : 
nft -f 
Mon fichier contient dans notre cas : 
flush table ip POSTROUTE;  
table ip POSTROUTE { chain DEFAUT { 
type nat hook postrouting priority 0; 
ip saddr 192.168.2.0/24 masquerade; 
ip saddr 192.168.3.0/24 masquerade; } }


On peut noter la création d'une table avec un nom "en français" pour faire plus beau !

Deuxième test, effectuer une redirection transparente de HTTP/HTTPS : 
Tous les "clients" HTTP à destination de l'extérieur sont "interceptés". Seules les adresses précisées subissent une redirection du HTTPS, le "set" GO_HTTPS contiendra les adresses sélectionnées. 
On ajoute des compteurs pour suivre le travail. 

flush table ip PREROUTE; 
table ip PREROUTE { chain DEFAUT { type nat hook prerouting priority 0; 
     ip daddr != 192.168.2.1/24 tcp dport 80 counter redirect to 3127 
     ip daddr != 192.168.2.1/24 
     ip saddr @GO_HTTPS tcp dport https counter redirect to 3129 
     } 
set GO_HTTPS { type ipv4_addr; }; 
}

Ensuite on ajoute les adresses au set :

nft add element PREROUTE GO_HTTPS { 192.168.2.9 } 
nft add element PREROUTE GO_HTTPS { 192.168.2.10 , 192.168.2.8} 
et on vérifie : 
nft list set PREROUTE GO_HTTPS 
set GO_HTTPS { type ipv4_addr elements = { 192.168.2.9, 192.168.2.8, 192.168.2.10} }

Troisième test : envoyer des paquets vers une "queue" en espace utilisateur par exemple pour un IPS : 
Ici l'IPS est Suricata, on mettra un compteur sur chaque règle. 
On utilise la "famille" inet pour intercepter aussi IPV6 et une priorité de -100 pour passer avant les autres chaines. On ne peut pas utiliser le type "route" qui ne fonctionne pas avec un autre "hook" que "output".

delete table inet IPS; 
table inet IPS       { 
    chain ENT_150    { type filter hook input         priority -100; 
            counter queue num 0 
            } 
     
    chain SOR_150    { type filter hook output        priority -190; 
            counter queue num 0 
            } 

    chain FOR_150   { type filter hook forward    priority -100; 
            counter queue num 1-2; 
            } 
}

Après quelques accès internet la commande " nft list table inet IPS " renvoie :

table inet IPS { 
    chain ENT_150 { 
         type filter hook input priority -100; 
         counter packets 1676 bytes 490186 queue num 0 
    } 

    chain SOR_150 { 
         type filter hook output priority -190; 
         counter packets 1969 bytes 398894 queue num 0 
    } 

    chain FOR_150 { 
         type filter hook forward priority -100; 
         counter packets 0 bytes 0 queue num 1-2 
    } 
}


Des paquets "locaux" sont bien passés dans la queue 0 et l'un d'entre eux a été "repéré" par Suricata car il contenait un mot de passe de connexion sur un site WEB.

A suivre ....

NFTABLES : sets, maps et dictionnaires

NFTABLES : sets, maps et dictionnaires jpp

NFTABLES :  

Nftables dispose d'un arsenal de modèles permettant de simplifier la réalisation des tests en utilisant des notions analogues à celles des "Ipsets" mais présentant quelques perfectionnements intéressants. 
Il existe plusieurs types de tables dans deux saveurs :

  • Gadgets "nommés", décrits "à part" et utilisés par une règle.
  • Gadgets "inline" non nommés qui peuvent faire partie d'une règle.

Les gadgets nommés permettent de réaliser des ajouts/suppressions "en ligne" gràce aux opérations sur les éléments (de manière analogue à "ipset").

Un certain nombre de "types" de données sont admis pour les éléments :

  • ipv4_addr/ipv6_addr      Adresses IPV4 ou IPV6, avec option "flags interval" pour notation CIDR ou intervalles, nécessite noyau >= 4.7-rc2 et dernière version nftables (à charger et compiler).
  • ether_addr                        Adresse MAC
  • inet_service                     Ports, sous forme numérique ou texte pour les plus connus (ssh, http, https ...)
  • inet_proto                         Prototype : ICMP, TCP ....
  • mark                          Marque posée par une autre règle.

Ces différents types répondent à la plupart des besoins, il manquerait toutefois la possibilité de désigner un réseau sous forme CIDR en plus des adresses IP. Cette possibilité existe dans les "vmap" non nommées, mais pas pour cellles nommées ? Un oubli sans doute ? 
Juin 2016 : 
Cet oubli est corrigé dans la dernière version de Nftables et nécessite un kernel >= 4.7-rc2, la syntaxe de la création du set est légèrement différente, il faut ajouter "flags interval" dans la déclaration du set nommé qui devient : 
set MONSET ( type ipv4-addr; flags interval)

Une autre nouveauté intéressante de ces "gadgets" est donnée par le deuxième élément possible : l'action à réaliser si l'élément "matche", drop, jump table, goto table, accept ..... Cela ouvre des horizons assez sympatiques. 
Par exemple on peut en une seule règle refouler toutes les IP d'un pays à l'aide des listes fournies par différents sites par exemple "ipdeny.com". On peut récupérer la liste des réseaux concernés, mettre en forme la règle (un simple script AWK le fait très bien, générer un fichier au "bon" format et utiliser ce fichier dans un script "Nftables" à l'aide de l'instruction "include" (je vous avais bien dit que la syntaxe Nftables se rapprochait d'un langage !). Dommage d'être obligé d'utiliser un bloc de données non nommé car si on en a besoin plusieurs fois .... par exemple en Entrée et en Forward ou en sortie. 
Exemple de syntaxe pour la règle ci dessus :

add rule INPUT ENT_100 ip saddr vmap { 
1.0.1.0/24 : drop, 
1.0.2.0/23 : drop, 
1.0.8.0/21 : drop, 
........ 
254.254.0.0/24 : drop }

Qui ajoute une règle à la chaine "ENT_100" de la table "INPUT".

Ce fichier est appelé par  l'include suivant dans le script Nftables de création de la table "INPUT" : 
#include "le_chemin_de_mon_fichier" 
Autre petit exemple, répartir sur plusieurs "chaînes" les entrées en fonction de l'interface d'origine pour les compter, on utilisera une "vmap" :

flush table ip ENTREE;

table ip ENTREE { 
    chain INP_INT { 
         counter 
    } 
    chain INP_EXT { 
        counter 
    } 
    chain INP_LOC { 
        counter 
    } 
    chain DEFAUT { 
         type filter hook input priority 0; 
         iifname vmap { br0 : jump INP_EXT, br1 : jump INP_INT, 
                        lo  : jump INP_LOC  } ; 
    } 
}

Après quelques instants on liste :

nft list table ENTREE 
table ip ENTREE { 
    chain INP_INT { 
         counter packets 53 bytes 8010 
    } 
    chain INP_EXT { 
         counter packets 2 bytes 64 
    } 
   chain INP_LOC { 
         counter packets 12 bytes 774 
    }    chain DEFAUT { 
         type filter hook input priority 0; 
         iifname vmap { "lo" : jump INP_LOC, "br1" : jump INP_INT, "br0" : jump INP_EXT} 
    } 
}

C'est bien ce qui était demandé...

Attention en manipulant les sets il existe quelques problèmes lors de la mise à jour d'une table contenant des sets, il est impossible de "flusher" une table tant qu'il y a un set, il faut détruire le set d'abord avant de faire le flush de la table sinon on arrive à un crash système. 
Ce problème est résolu pour les noyaux >= 4.0.0-rc4. 
 

NFTABLES : sets nommés en notation CIDR

NFTABLES : sets nommés en notation CIDR jpp

Juin 2016 : depuis la version 0.5 de Nftables, la dernière que j'ai chargée et testée, on peut créer des sets nommés en utilisant les intervalles et/ou une notation CIDR. Il faut, en plus de cette version récente, disposer d'un kernel >= 4.7-rc2 avec toutes les options "NFT_...." activées en modules. 
Exemple de création d'une table avec deux sets nommés, un en IPV4, l'autre en IPV6 :

table ip TEST      { 
   chain ENT_100    { type filter hook input    priority -100 ; 
            counter; 
            }

    set DISPAT     { type ipv4_addr; flags interval; };  # bien noter les deux ";"

    chain BIDON    { type filter hook input    priority 0; 
            counter; 
            }

    set EXAMPLE    { type ipv6_addr; }; 
}

On peut ensuite ajouter des éléments aux sets, par exemple :

add element TEST  DISPAT    { 192.168.2.2,192.168.2.4 }; 
add element TEST  DISPAT    { 192.168.3.0/24 }; 
add element TEST  DISPAT    { 192.168.0.0/24, 192.168.1.0/24 }; 
add element TEST  DISPAT    { 192.168.4.10 - 192.168.4.127 }; 
La commande de liste du set donne le résultat suivant : 
nft list set TEST DISPAT 
table ip TEST { 
    set DISPAT { 
        type ipv4_addr 
        flags interval 
        elements = { 192.168.0.0/23, 192.168.2.1, 192.168.2.2, 192.168.2.4, 192.168.3.0/24, 192.168.4.10-192.168.4.127} 
    } 
}

On peut remarquer la "fusion" réalisée automagiquement de "192.168.0.0/24, 192.168.1.0/24". 
IPV6 n'étant pas activé chez moi (ni chez mon FAI) je n'ai pas fait de tests sur les sets IPV6.