понедельник, 4 мая 2009 г.

qemu - работа с сетью

/*
материалов на данную тему в сети - море. эта заметка не претендует на оригинальность. пригодится - хорошо, а нет - так ещё лучше. по сути рассматривается частный случай создания виртуальных сетей с использованием qemu, где все "клиенты" друг друга видят, умеют общаться, делиться ресурсами и т.п.

терминология:
"гостевая" система - система, загруженная с использованием qemu/(под управлением qemu) (GUEST)
"домашняя" система - localhost (HOST)

проект qemu не стоит на месте, многие вещи меняются от версии к версии. нетерпеливые энтузиасты собирают эмулятор самостоятельно. шаблончик для сборки на Linux приведён ниже (для версии 0.10.3):

./configure --prefix=/usr --enable-system --enable-linux-user \
--audio-drv-list="oss alsa sdl esd" \
--audio-card-list="ac97 sb16 es1370" \
--enable-mixemu
make
sudo checkinstall


checkinstall генерит корректный пакет (спасибо всем, кто написал хороший Makefile).
ссылки на документацию о работе qemu с сетью:
официальное руководство. читать.
подробная инструкция от товарища "радиста" - может рассматриваться как некая общая "вводная".

осилив материал, можно узнать пару интересных вещей: slirp и vde (Virtual Distributed Ethernet). в теории vde должен упростить процедуру, изложенную ниже, путём соединения гостевой системы с сокетом, автосозданием "tap" интерфейса и установкой корректного FORWARD между всеми гостевыми системами. к сожалению готовых "рецептов" с использованием vde выдать пока не могу, ибо... если у кого работает эта схема - отпишитесь в комментариях или киньте ссылку на более подробные инструкции. использование же slirp не представляется целесообразным.

задача:
поднять несколько виртуальных машин с qemu и обеспечить не только их взаимодействие по "виртуальной сети" между собой, но и открыть им доступ к локальным сетевым ресурсам (интернет в том числе).

для её решения подойдёт практически любой дистрибутив Linux с установленными пакетами: iptables, qemu, kqemu, tunctl и bridge-utils. если вы долго и упорно настраивали iptables - то идея построения "моста" вам явно не понравится.

грузим необходимые модули ядра:

sudo modprobe -v tun
sudo modprobe -v kqemu
sudo chmod 666 /dev/net/tun
sudo chown `whoami` /dev/kqemu


последние две строки дают нам возможность не использовать рута для старта самого qemu, что кажется более логичным, чем "Запускать нужно от root'а, потому что иначе могут возникнуть проблемы с доступом к устройству /dev/net/tun ." угу, всё бросили и запустили от рута, хотя кому-то может так и легче.

запуск виртуальных машин довольно прост. стоит только запомнить, что каждой машине нужен отдельный "tap" интерфейс, соответственно имеет смысл помещать их в разные "подсети". например ("интерфейс tap" -> "локальный eth0, что мы потом поднимем в qemu"):
192.168.4.1 -> 192.168.4.100
192.168.5.1 -> 192.168.5.100
192.168.6.1 -> 192.168.6.100

использовать "глобальные" скрипты (/etc/qemu*) - не рекомендую, но это imho. каждый волен сам решать. если у вас не происходит присваивание уникальных MAC адресов "на лету" - то можете задать их вручную или поменять позже. простейший скриптец старта qemu:

#!/bin/sh
iface="tap0"
iface_addr="192.168.4.1"
user="`whoami`"
sudo modprobe tun
sudo modprobe kqemu
sleep 2
sudo chown $user /dev/kqemu
sudo tunctl -u $user -t $iface
sudo ifconfig $iface $iface_addr up
##sudo ifconfig $iface $iface_addr promisc up ## только для использования в "мостике" !!!
qemu -cdrom ./image.iso -boot d -hda ./disk.img -m 256 -kernel-kqemu -net nic,vlan=0 -net tap,vlan=0,ifname=$iface,script=no
sudo ifconfig $iface down
#EOF


приказ отдохнуть пару секунд после загрузки модуля kqemu (если он ещё не загружен) для вас может быть излишним. маска сети 255.255.255.0 пойдёт автоматом. в "гостевой" системе необходимо и достаточно поднять сетевой интерфейс с номером в пределах заданного сегмента и проставить дефолтный шлюз, равный $iface_addr. для примера с "tap0" некие шаблонные действа в "гостевой" системе:

ifconfig eth0 down
ifconfig eth0 192.168.4.100 up
route add default gw 192.168.4.1
echo 'nameserver 192.168.4.1' >> /etc/resolv.conf
echo 'nameserver ВАШ_ЛОКАЛЬНЫЙ_ДНС' >> /etc/resolv.conf
echo 'nameserver BACKUP_ДНС' >> /etc/resolv.conf


для того, чтобы открыть гостевым машинам доступ в локальную сеть можно воспользоваться "маскарадом". допустим, что ваш локальный сетевой интерфейс - eth0. если интернет идёт по интерфейсам типа dsl0, ppp0 - то "маскарадить" надо и их (если хочется выйти в сеть из qemu плюс ко всему придётся подправить правила для FORWARD). имеем три интерфейса для qemu - tap0, tap1 и tap2. шаблон ниже не предполагает использование "brctl" - "мостиков". мы культурно разрешаем обмен пакетами (FORWARD) между всеми участниками праздничного шоу (используем 2 цепочки - forward_int и forward_ext).

su -c "echo 1 > /proc/sys/net/ipv4/ip_forward"
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
sudo iptables -t filter -I INPUT 1 -i tap0 -j input_int
sudo iptables -t filter -I INPUT 1 -i tap1 -j input_int
sudo iptables -t filter -I INPUT 1 -i tap2 -j input_int
sudo iptables -t filter -A input_int -j ACCEPT
sudo iptables -t filter -I FORWARD 2 -i eth0 -j forward_ext
sudo iptables -t filter -I FORWARD 2 -i tap0 -j forward_int
sudo iptables -t filter -I FORWARD 2 -i tap1 -j forward_int
sudo iptables -t filter -I FORWARD 2 -i tap2 -j forward_int
sudo iptables -t filter -A forward_int -i tap0 -o eth0 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
sudo iptables -t filter -A forward_int -i tap1 -o eth0 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
sudo iptables -t filter -A forward_int -i tap2 -o eth0 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
sudo iptables -t filter -A forward_int -i tap0 -o tap1 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
sudo iptables -t filter -A forward_int -i tap0 -o tap2 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
sudo iptables -t filter -A forward_int -i tap1 -o tap0 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
sudo iptables -t filter -A forward_int -i tap1 -o tap2 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
sudo iptables -t filter -A forward_int -i tap2 -o tap1 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
sudo iptables -t filter -A forward_int -i tap2 -o tap0 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
sudo iptables -t filter -A forward_ext -i eth0 -o tap0 -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -t filter -A forward_ext -i eth0 -o tap1 -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -t filter -A forward_ext -i eth0 -o tap2 -m state --state RELATED,ESTABLISHED -j ACCEPT


почему мы создаём FORWARD с номера 2? - потому, что первым правилом абсолютно корректно выставлять автоподстройку величины пакетов:

sudo iptables -t filter -I FORWARD 1 -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

теперь, когда у нас всё работает, все друг друга видят и пользуют ресурсы друг друга, настало время поиграть с "мостиками". идея крайне проста - "собирайтесь девки в кучу, я вам чучу отчебучу...". шаблонные действия примерно таковы: раскомментируем строку "sudo ifconfig $iface $iface_addr promisc up" (в скрипте вызова qemu) и закомментируем то, что было ранее. выгрузим все правила iptables, ибо основным интерфейсом становится бридж, и оне (енти правила) автоматом идуть далёко...

sudo ifconfig eth0 down
sudo ifconfig eth0 0.0.0.0 promisc up
sudo brctl addbr br0
sudo brctl addif br0 eth0 tap0 tap1 tap2
sudo brctl show
sudo dhclient br0 ## (или ручками настраиваем br0 как раньше был настроен eth0)


тихо матерясь переписываем правила всяких забавных софтинок типа "netams" для работы с "br0" и рихтуем iptables.

зачем нам это надо? чтобы где-то в два раза поднять пропускную способность сети для qemu. скорость аплоада в vsftpd доходила до 2.5 Mb/sec - это неплохо (у вас могут быть совершенно другие цифры!). да и возни в итоге поменьше, плюс получаем новую "игрушку" - brctl. а уж если vconfig ещё валяется рядом - то "гуляйте, Маша, воля ваша".

частный случай - объединить "мостиком" только "tap*" интерфейсы - в этом случае присваиваем руками адрес для "br0" и используем его вместо "tap*" для рихтовки таблиц маршрутизации.

P.S. не забываем о "командном" режиме работы (Ctl+Alt+2). могут пригодиться "host_net_add", "host_net_remove", "system_reset", "q"/"system_powerdown" и т.п.
*/

5 комментариев:

Stalwart комментирует...

Или тупо юзать virt-manager

sda комментирует...

virt-manager - как-то... страшно его пользовать. вижу фразу "NAT to any physical device" - и не вижу опции показывать лог. надо наверное с ним поиграть, но... нахера лишний лисапед?

loukash комментирует...

Пытаюсь использовать virt-manager, но вот не могу настроить сеть. Ошибка: "libvirtError: cannot create bridge 'virbr0': Operation not permitted". Даже не знаю на что дать права. Не сталкивались с подобной проблемой?

sda комментирует...

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

Анонимный комментирует...

It was certainly interesting for me to read this blog. Thanks the author for it. I like such topics and anything that is connected to them. I definitely want to read more on that blog soon.

Best wishes
Alice Tudes