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

Linux - две подсети, один интерфейс, один шлюз - дополнительная подсеть NAT

В настоящее время у нас есть несколько серверов, которые используют одну и ту же локальную сеть:

Host1: eth0 10.0.0.1/24
Host2: eth0 10.0.0.2/24
Host3: eth0 10.0.0.3/24
Gateway: 10.0.0.254 

Мы хотим запустить несколько виртуальных машин (VirtualBox) на этих серверах. Мы можем настроить их для подключения к eth0 хоста, но мы не можем использовать адреса из диапазона 10.0.0.0/24, поскольку они могут быть выделены в будущем.

Поэтому мы решили использовать другую подсеть:

Host1VM: eth0 192.168.0.1/24 (bridge to host eth0)
Host2VM: eth0 192.168.0.2/24 (bridge to host eth0)
Host3VM: eth0 192.168.0.3/24 (bridge to host eth0)

Это нормально, и все виртуальные машины могут общаться друг с другом, поскольку они находятся в одной подсети, которая использует один и тот же физический интерфейс.

Проблема, с которой мы сталкиваемся, заключается в том, что нам нужно предоставить этим виртуальным машинам доступ в Интернет через шлюз 10.0.0.254. Итак, мы подумали, почему бы не выбрать один из хостов и не использовать его в качестве маршрутизатора / NAT?

Host1: eth0 10.0.0.1/24, eth0:0 192.168.0.254/24

Теперь мы можем предоставить виртуальным машинам шлюз 192.168.0.254. Проблема, которую мы затем видим, заключается в том, что Host1 не работает с NAT должным образом.

iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -o eth0 -j SNAT --to 10.0.0.1

Я думал, что это сработает, и мы действительно видим совпадение пакетов. Если виртуальная машина отправляет эхо-запрос в Интернет, мы видим, что пакет ICMP поступает на узел 1 (поскольку это маршрутизатор), а затем узел повторно отправляет ICMP, потому что это NAT, узел Интернета отвечает узлу, но затем он там умирает. Я ожидал, что хост затем перешлет пакет обратно в виртуальную машину, но этого не произошло.

Что мне не хватает, или эта настройка просто невозможна?

редактировать: Чтобы уточнить, у нас нет правил DENY в iptables, все по умолчанию ACCEPT. Мы также включили переадресацию IP.

Обновление1 - iptables

Игнорируйте virbr0 - это не связано с виртуальными машинами VirtualBox

# Completed on Fri Sep 20 16:50:45 2013
# Generated by iptables-save v1.4.12 on Fri Sep 20 16:50:45 2013
*nat
:PREROUTING ACCEPT [171383:10358740]
:INPUT ACCEPT [1923:115365]
:OUTPUT ACCEPT [192:21531]
:POSTROUTING ACCEPT [169544:10254463]
-A POSTROUTING -s 192.168.0.0/24 -o eth0 -j SNAT --to-source 10.0.0.1
COMMIT
# Completed on Fri Sep 20 16:50:45 2013
# Generated by iptables-save v1.4.12 on Fri Sep 20 16:50:45 2013
*filter
:INPUT ACCEPT [96628707:25146145432]
:FORWARD ACCEPT [195035595:22524430122]
:OUTPUT ACCEPT [44035412:304951330498]
-A INPUT -i virbr0 -p udp -m udp --dport 53 -j ACCEPT
-A INPUT -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT
-A INPUT -i virbr0 -p udp -m udp --dport 67 -j ACCEPT
-A INPUT -i virbr0 -p tcp -m tcp --dport 67 -j ACCEPT
-A FORWARD -d 192.168.122.0/24 -o virbr0 -m state --state RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -s 192.168.122.0/24 -i virbr0 -j ACCEPT
-A FORWARD -i virbr0 -o virbr0 -j ACCEPT
-A FORWARD -o virbr0 -j REJECT --reject-with icmp-port-unreachable
-A FORWARD -i virbr0 -j REJECT --reject-with icmp-port-unreachable
COMMIT
# Completed on Fri Sep 20 16:50:45 2013
# Generated by iptables-save v1.4.12 on Fri Sep 20 16:50:45 2013
*mangle
:PREROUTING ACCEPT [291641356:47665886851]
:INPUT ACCEPT [96628707:25146145432]
:FORWARD ACCEPT [195035595:22524430122]
:OUTPUT ACCEPT [44035838:304951365412]
:POSTROUTING ACCEPT [239078922:327477732680]
-A POSTROUTING -o virbr0 -p udp -m udp --dport 68 -j CHECKSUM --checksum-fill
-A POSTROUTING -o virbr0 -p udp -m udp --dport 68 -j CHECKSUM --checksum-fill
COMMIT
# Completed on Fri Sep 20 16:50:45 2013

Обновление 2 - tcpdump

16:58:37.189758 IP 192.168.0.2 > 74.125.128.106: ICMP echo request, id 1, seq 2, length 40
16:58:37.189805 IP 10.0.0.1 > 74.125.128.106: ICMP echo request, id 1, seq 2, length 40
16:58:37.194607 IP 74.125.128.106 > 10.0.0.1: ICMP echo reply, id 1, seq 2, length 40
(no final reply back to the VM)

Я собираюсь ответить на свой вопрос, потому что вот как я его решил. Я почти уверен, что другие ответы более чем точны при нормальных обстоятельствах, но я считаю, что они не сработали из-за ошибки или предостережения с использованием псевдонимов интерфейса.

Итак, думая, что псевдоним интерфейса вызывает проблему, я искал другие способы предоставления виртуального интерфейса, и оказалось, что существует тип виртуального интерфейса, называемый MAC-VLAN, который, по сути, присоединяется к физическому интерфейсу как виртуальный интерфейс со своими собственный MAC-адрес.

При использовании этого NAT с первого раза работал безупречно, поэтому я предполагаю, что это было просто из-за того, что интерфейс MAC-VLAN представлялся ядру как полностью отдельный интерфейс, тогда как псевдонимы интерфейсов вызывают некоторую путаницу.

Для справки команда для создания MAC-VLAN проста:

ip link add dev macvlan0 link eth0 type macvlan

Это действительно хорошая функция, не могу поверить, что не слышал о ней раньше.

Это говорит:

16:58:37.189758 IP 192.168.0.2 > 74.125.128.106: ICMP echo request, id 1, seq 2, length 40
16:58:37.189805 IP 10.0.0.1 > 74.125.128.106: ICMP echo request, id 1, seq 2, length 40
16:58:37.194607 IP 74.125.128.106 > 10.0.0.1: ICMP echo reply, id 1, seq 2, length 40

Восходящий маршрутизатор (10.0.0.254) отправляет ответ обратно на Host1, поэтому ваша маршрутизация и SNAT работают. Проблема в том, что Host1 не передает этот ответ обратно в 192.168.0.0/24 сеть.

Загружены ли у вас соответствующие модули ядра отслеживания соединений?

Убедитесь, что трафик идет в таблицу отслеживания соединений:

grep src=192.168.0.2 /proc/1/net/nf_conntrack

На днях я видел аналогичную проблему с TCP-пакетами, которая, как оказалось, была связана с rp_filter в ядре. Я не понимаю, как это может быть здесь проблема, но это очень похоже. Можете выложить таблицу маршрутов (ip route show) с Host1 и проверьте настройку rp_filter? (cat /proc/sys/net/ipv4/conf/default/rp_filter)

Интересно, зачем вам их NAT? Поскольку префиксы разные, и вы маршрутизируете трафик, почему бы не установить маршрут через виртуальную машину в шлюзе для сети 10.0.0.0/24?

Также при настройке eth0:0, обычно вы настраиваете eth0 и наоборот. Использовать eth0:1, или еще лучше, прекратите использовать старые инструменты net-tools и используйте вместо них iproute2 (ip addr add 192.168.0.254/24 dev eth0, и т.д.). Это может быть проблемой, так как ваше правило SNAT верное. Если ваш маршрутизатор не знает, что это 10.0.0.1, он не будет возвращать обратный трафик через NAT.

Если ip addr show dev eth0 показывает оба адреса, которые вы установили, ваш NAT настроен правильно и должен работать. Возможно, вы столкнулись с ошибкой, поскольку NAT отправляет пакеты через тот же интерфейс с другим адресом источника, что является необычным сценарием.

Однако часть того, что делает этот сценарий беспорядочным, - это наличие перенаправлений ICMP. Обычно считается, что это зло (поскольку они позволяют злоумышленникам изменять ваши таблицы маршрутизации в некоторых обстоятельствах), и поэтому они почти всегда отключены, но Linux обычно отправляет их при пересылке трафика через тот же интерфейс, на котором он пришел. В этом случае это в основном сообщение, в котором говорится, что «этот хост находится в вашем локальном сегменте; вам не нужно пересылать его через меня». Обычно это было бы правильно, за исключением того, что из-за SNAT хост (предыдущий переход) действительно должен это делать. Чтобы отключить их, используйте следующие настройки sysctl:

net.ipv4.conf.eth0.send_redirects = 0
net.ipv4.conf.eth0.accept_redirects = 0

В зависимости от устройства следующего прыжка вам может потребоваться перезапустить это устройство, настроить его каким-либо образом или немного подождать, пока перенаправление прекратится, если это было причиной проблемы.

Учитывая топологию, как я вас понял, лучше всего было бы предоставить сети 192.168.0.0/24 сетевой доступ к Интернету на маршрутизаторе. Чтобы это сработало, вам необходимо указать маршрутизатору адрес в сети 192.168.0.0/24.

Если вы не можете это сделать, вам нужно использовать MASQUERADE, но ограничить его назначениями не в сети виртуальных машин. Примерно так на каждом хосте:

iptables -A POSTROUTING -o eth0 ! -d 192.168.0.0/24 -j MASQUERADE

Таким образом, весь трафик между виртуальными машинами остается неизменным, весь трафик от виртуальных машин к другим сетям появляется с адресом хоста на проводе.