Назад | Перейти на главную страницу

iptables target REDIRECT на мосту без IP

У меня есть установка типа "человек посередине":

А <---> В <---> С

где A, B и C - компьютеры, а стрелки обозначают соединения Ethernet.

A имеет IP 10.0.0.10, C имеет IP 10.0.0.9

оба имеют маску 255.255.255.0

Я пытаюсь реализовать брандмауэр на Bump-in-the-wire на B, используя мост linux без настроенного IP-адреса, например:

brctl addbr br
brctl addif chlep1
brctl addif chlep2
ifconfig br 0.0.0.0 up

чтобы захватить соединения от A до C, я настроил Iptables на B вот так

sysctl net.bridge.bridge-nf-call-arptables=1
sysctl net.bridge.bridge-nf-call-ip6tables=1
sysctl net.bridge.bridge-nf-call-iptables=1
iptables -t nat -I PREROUTING -p tcp -s 10.0.0.10 -d 10.0.0.9 -j REDIRECT --to-ports 40000
iptables -t nat -A PREROUTING -p tcp -s 10.0.0.10 -d 10.0.0.9 -j LOG
iptables -t filter -A INPUT -p tcp -s 10.0.0.10 -j LOG
iptables -t filter -A FORWARD -p tcp -s 10.0.0.10 -j LOG

Я открыл гнездо для прослушивания на C и B вот так

python
Python 2.7.5 (default, Aug  4 2017, 00:39:18)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket
>>> sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM,0)
>>> sk.bind(('0.0.0.0',40000))
>>> sk.listen(1)
>>> conn, addr = sk.accept()

затем на А я побежал

python
Python 2.7.5 (default, Nov  6 2016, 00:28:07)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-11)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket
>>> sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM,0)
>>> sk.connect(('10.0.0.9',40000))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python2.7/socket.py", line 224, in meth
    return getattr(self._sock,name)(*args)
socket.error: [Errno 110] Connection timed out
>>>

проверка Iptables для количества пакетов правил на B я получаю

[root@ace ~]# iptables -t nat -vL
Chain PREROUTING (policy ACCEPT 269 packets, 64867 bytes)
 pkts bytes target     prot opt in     out     source               destination
    4   240 REDIRECT   tcp  --  any    any     10.0.0.10            10.0.0.9             
redir ports 40000
    0     0 LOG        tcp  --  any    any     10.0.0.10            10.0.0.9             
LOG level warning

Chain INPUT (policy ACCEPT 22 packets, 3425 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 1 packets, 76 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain POSTROUTING (policy ACCEPT 2 packets, 160 bytes)
 pkts bytes target     prot opt in     out     source               destination
[root@ace ~]#
[root@ace ~]#
[root@ace ~]#
[root@ace ~]#
[root@ace ~]# iptables -t filter -vL
Chain INPUT (policy ACCEPT 456 packets, 28706 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 LOG        tcp  --  any    any     10.0.0.10            anywhere             
LOG level warning

Chain FORWARD (policy ACCEPT 8 packets, 672 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 LOG        tcp  --  any    any     10.0.0.10            anywhere             
LOG level warning

Chain OUTPUT (policy ACCEPT 240 packets, 19382 bytes)
 pkts bytes target     prot opt in     out     source               destination

dmesg пуст.

почему я не перехватываю соединение? ПРИМЕЧАНИЕ: я могу перехватить соединение, если настрою мост с IP-адресом, чего я бы предпочел избежать.

Нашел это после некоторого покопания ядра.

фактический код iptables REDIRECTing реализован в хук netfilter ядра, который называется nf_nat_redirect_ipv4, в нем мы находим этот относительно простой код

...
/* Local packets: make them go to loopback */
if (hooknum == NF_INET_LOCAL_OUT) {
    newdst = htonl(0x7F000001);
} else {
    struct in_device *indev;
    struct in_ifaddr *ifa;

    newdst = 0;

    rcu_read_lock();
    indev = __in_dev_get_rcu(skb->dev);
    if (indev && indev->ifa_list) {
        ifa = indev->ifa_list;
        newdst = ifa->ifa_local;
    }
    rcu_read_unlock();

    if (!newdst)
        return NF_DROP;
}
...

давайте разберем это

...
if (hooknum == NF_INET_LOCAL_OUT) {
    newdst = htonl(0x7F000001);
}
...

если наш REDIRECT поступает из цепочки OUTPUT, адрес назначения пакета меняется на адрес обратной связи ('127.0.0.1')

...
else {
    struct in_device *indev;
    struct in_ifaddr *ifa;

    newdst = 0;

    rcu_read_lock();
    indev = __in_dev_get_rcu(skb->dev);
    if (indev && indev->ifa_list) {
        ifa = indev->ifa_list;
        newdst = ifa->ifa_local;
    }
    rcu_read_unlock();
...

если мы приходим из другой цепочки (только PREROUTING, поскольку мы уже обработали OUTPUT), мы устанавливаем адрес назначения пакета как адрес нашего локального устройства, наш будет равен 0

 ...
    if (!newdst)
        return NF_DROP;
 }
 ...

нулевой адрес считается значением ошибки, и наш пакет, к сожалению, отброшен :(