Владельцам ядер ниже 4.4 можно не читать.
Собственно есть cgroup и iptables -m cgroup.
Почему бы не создать отдельный cgroup и фильтровать локальный трафик для этой группы?
1. Не забываем включить классную фичу
sysctl -w net.ipv4.ip_early_demux=1
# на ядрах 4.10+
sysctl -w net.ipv4.tcp_early_demux=1
sysctl -w net.ipv4.udp_early_demux=1
2. Создаем группу
mkdir /sys/fs/cgroup/net_cls/testgroup2
echo 1234 >/sys/fs/cgroup/net_cls/testgroup2/net_cls.classid
3. создаем тестовые правила
iptables -A INPUT -m cgroup --cgroup 1234 -p icmp
iptables -A INPUT -m cgroup --cgroup 1234 -p udp
iptables -A INPUT -m cgroup --cgroup 1234 -p tcp
iptables -A OUTPUT -m cgroup --cgroup 1234 -p icmp
iptables -A OUTPUT -m cgroup --cgroup 1234 -p udp
iptables -A OUTPUT -m cgroup --cgroup 1234 -p tcp
4. Переносим себя в эту группу
echo $$ >/sys/fs/cgroup/net_cls/testgroup2/tasks
5. Тестируем UDP
dig google.com
iptables -nvxL INPUT; iptables -nvxL OUTPUT
Chain INPUT (policy ACCEPT 2 packets, 255 bytes)
pkts bytes target prot opt in out source destination
0 0 tcp -- * * 0.0.0.0/0 0.0.0.0/0 cgroup 1234
0 0 udp -- * * 0.0.0.0/0 0.0.0.0/0 cgroup 1234
0 0 icmp -- * * 0.0.0.0/0 0.0.0.0/0 cgroup 1234
Chain OUTPUT (policy ACCEPT 2 packets, 91 bytes)
pkts bytes target prot opt in out source destination
0 0 icmp -- * * 0.0.0.0/0 0.0.0.0/0 cgroup 1234
2 91 udp -- * * 0.0.0.0/0 0.0.0.0/0 cgroup 1234
0 0 tcp -- * * 0.0.0.0/0 0.0.0.0/0 cgroup 1234
6. Тестируем TCP
iptables -Z
wget -4 -O /dev/zero https://google.com
iptables -nvxL INPUT; iptables -nvxL OUTPUT
Chain INPUT (policy ACCEPT 4 packets, 538 bytes)
pkts bytes target prot opt in out source destination
4 538 tcp -- * * 0.0.0.0/0 0.0.0.0/0 cgroup 1234
0 0 udp -- * * 0.0.0.0/0 0.0.0.0/0 cgroup 1234
0 0 icmp -- * * 0.0.0.0/0 0.0.0.0/0 cgroup 1234
Chain OUTPUT (policy ACCEPT 6 packets, 451 bytes)
Chain OUTPUT (policy ACCEPT 6 packets, 451 bytes)
pkts bytes target prot opt in out source destination
0 0 icmp -- * * 0.0.0.0/0 0.0.0.0/0 cgroup 1234
0 0 udp -- * * 0.0.0.0/0 0.0.0.0/0 cgroup 1234
6 451 tcp -- * * 0.0.0.0/0 0.0.0.0/0 cgroup 1234
7. Тестировать остальные протоколы бесполезно, т.к. они не поддерживаются в early_demux
Что имеем в результате? TCP можно контролировать для группы, а с UDP пролный 3.14ц!
Поковыряв ядро становится полностью понятно почему.
Проблема в том, что в UDP можно сделать bind() только на порт,
что делает поиск сокета на которые пришли данные в два раза сложнее.
__udp4_lib_demux_lookup() делает поиск только по номеру порта и адресу, игнорируя сокеты привязанные только к номеру порта.
Заменив __udp4_lib_demux_lookup() на полноценный поиск udp сокета через __udp4_lib_lookup()
для ядра 4.14.165
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 731ea78..6143ca5 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -2295,8 +2295,8 @@ int udp_v4_early_demux(struct sk_buff *skb)
uh->source, iph->saddr,
dif, sdif);
} else if (skb->pkt_type == PACKET_HOST) {
- sk = __udp4_lib_demux_lookup(net, uh->dest, iph->daddr,
- uh->source, iph->saddr, dif, sdif);
+ sk = __udp4_lib_lookup(net, iph->saddr, uh->source, iph->daddr, uh->dest,
+ dif, 0, &udp_table, NULL);
}
if (!sk || !refcount_inc_not_zero(&sk->sk_refcnt))
Да, это дополнительные накладные расходы, но мы дважды это подтвердили через
net.ipv4.ip_early_demux=1 и net.ipv4.udp_early_demux=1
Но теперь мы получаем в п.5
Chain INPUT (policy ACCEPT 2 packets, 255 bytes)
pkts bytes target prot opt in out source destination
0 0 tcp -- * * 0.0.0.0/0 0.0.0.0/0 cgroup 1234
1 226 udp -- * * 0.0.0.0/0 0.0.0.0/0 cgroup 1234
0 0 icmp -- * * 0.0.0.0/0 0.0.0.0/0 cgroup 1234
Chain OUTPUT (policy ACCEPT 2 packets, 91 bytes)
pkts bytes target prot opt in out source destination
0 0 icmp -- * * 0.0.0.0/0 0.0.0.0/0 cgroup 1234
2 91 udp -- * * 0.0.0.0/0 0.0.0.0/0 cgroup 1234
0 0 tcp -- * * 0.0.0.0/0 0.0.0.0/0 cgroup 1234
Т.е. оно стало работать!
Но это патч ядра, который не все могут осилить.
Есть возможность получить аналогичный результат при помощи модуля ядра.
( https://github.com/vel21ripn/ipt_sk_helper )
Чтоб оно заработало нужно сделать
sysctl -w net.ipv4.ip_early_demux=2
Работа проверялать на 4.4.209 и 4.14.165.
Собирается даже на 5.4.х
Если кто может попробовать собрать его и протестировать - напишите что получилось.
PS Если починить эту фичу в официальном ядре, то станет доступной фильтраци входящего трафика по UID/GID.