У меня есть установка типа "человек посередине":
А <---> В <---> С
где 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;
}
...
нулевой адрес считается значением ошибки, и наш пакет, к сожалению, отброшен :(