Создайте мост QEMU, используя серверную часть Socket Networking - PullRequest
8 голосов
/ 19 февраля 2020

Я пытаюсь создать мост к интерфейсу на моем хосте, очень похожий на мостовые адаптеры Virtualbox и VMWare, в QEMU, используя комбинацию сокетов и библиотеку Python под названием Scapy (по сути, опираясь на WinPcap / Npcap на Windows ОС или libpcap на Unix ОС за кулисами). Вот сценарий моста, который я создал для подключения VLAN, созданной сетевым бэкэндом QEMU socket, к внешним интерфейсам:

import argparse
import scapy
import threading
import socket
import struct
import scapy.sendrecv
import scapy.packet
import scapy.config
import scapy.layers.l2

MAX_PACKET_SIZE = 65535

send_lock = threading.Lock()
qemu_senders = set()
iface_senders = set()


def qemu_in_iface_out_traffic_thread_func(iface, mcast_addr, mcast_port, local_addr):
    global MAX_PACKET_SIZE
    global send_lock
    global qemu_senders
    global iface_senders

    # Create the multicast listen socket.
    listener_addr = (local_addr, mcast_port)
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(listener_addr)
    mcast_group = socket.inet_aton(mcast_addr)
    mreq = struct.pack('4sL', mcast_group, socket.INADDR_ANY)
    sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)

    # Get the packets from the QEMU VLAN, and send them over to the host's interface.
    while True:
        data, _ = sock.recvfrom(MAX_PACKET_SIZE)
        send_lock.acquire()
        eth_pkt = scapy.layers.l2.Ether(data)
        if eth_pkt.src not in iface_senders:
            qemu_senders.add(eth_pkt.src)
            scapy.sendrecv.sendp(eth_pkt, iface=iface, verbose=0)
        send_lock.release()


def iface_in_qemu_out_traffic_thread_func(iface, mcast_addr, mcast_port):
    global send_lock
    global qemu_senders
    global iface_senders

    # Create the multicast send socket.
    mcast_group = (mcast_addr, mcast_port)
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    ttl = struct.pack('b', 1)
    sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl)

    # Sniff packets from the host's interface, and send them to the QEMU VLAN.
    def process_packet(eth_pkt):
        send_lock.acquire()
        if eth_pkt.src not in qemu_senders:
            iface_senders.add(eth_pkt.src)
            sock.sendto(scapy.packet.Raw(eth_pkt).load, mcast_group)
        send_lock.release()
    scapy.sendrecv.sniff(iface=iface, prn=process_packet, store=0)


if __name__ == "__main__":
    # Parse the command line arguments.
    parser = argparse.ArgumentParser()
    parser.add_argument('--iface', '-i', required=True)
    parser.add_argument('--mcast-addr', '-a', required=True)
    parser.add_argument('--mcast-port', '-p', required=True, type=int)
    parser.add_argument('--local-addr', '-l', default='127.0.0.1')
    parser.add_argument('--disable-promisc', '-d',
                        default=False, action='store_true')
    args = parser.parse_args()

    # Set promiscuous mode.
    scapy.config.conf.sniff_promisc = 0 if args.disable_promisc else 1

    # Create the traffic threads.
    qemu_in_iface_out_traffic_thread = \
        threading.Thread(target=qemu_in_iface_out_traffic_thread_func, args=(
            args.iface, args.mcast_addr, args.mcast_port, args.local_addr
        ))
    iface_in_qemu_out_traffic_thread = \
        threading.Thread(target=iface_in_qemu_out_traffic_thread_func, args=(
            args.iface, args.mcast_addr, args.mcast_port
        ))

    # Run the traffic threads, and join them to wait for their exit.
    qemu_in_iface_out_traffic_thread.start()
    iface_in_qemu_out_traffic_thread.start()
    qemu_in_iface_out_traffic_thread.join()
    iface_in_qemu_out_traffic_thread.join()

С этим исходным кодом моста я смог ping туда и обратно между устройством за пределами моего хоста (например, контроллер Raspberry Pi) и моей виртуальной машиной QEMU, которые и мой контроллер, и мой хост находятся в одной локальной сети. Однако я не смог сделать то же самое между моей виртуальной машиной QEMU и моим хостом в одной сети. Я хотел бы знать, связана ли проблема с двумя разными адресами MA C (т.е. адрес MA C моей виртуальной машины QEMU и адрес MA C моего хоста), имеющими один и тот же интерфейс (интерфейс хоста, подключенный к LAN) и трафик c к той же локальной сети отфильтровывается, или что мне здесь не хватает?

Edit # 1:

Итак, я проверил пакеты отправляются / принимаются в сети виртуальной машиной QEMU или моим хостом, и они принимаются на всех устройствах в сети. Я подключил маршрутизатор к своей сети и другое портативное устройство с Wi-Fi и Wireshark для подключения к сети через маршрутизатор. Я вижу, что пакеты принимаются на всех концах сети (например, на новом ноутбуке в сети), но мой хост не отвечает на пакеты ARP, например, которые были отправлены другой машиной на том же интерфейсе, например как QEMU VM.

Вот скриншоты Wireshark, сделанные с хоста и с нового ноутбука в сети, соответственно:

enter image description here

enter image description here

На снимках экрана, сделанных выше, любое устройство за пределами моего хоста (маршрутизатор на 192.168.1.1, контроллер Rasberry Pi и другой ноутбук) может общаться с моим VM QEMU и ответы на запросы ARP (и VM QEMU аналогично), но не хост.

Вот наблюдение того, что я вижу (извиняюсь за плохой рисунок):

enter image description here

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

Редактировать # 2:

Еще одна вещь, которую я заметил, заключается в том, что при ARPing моего хоста от моих виртуальных машин VirtualBox я не вижу ответа ARP от моего хоста на виртуальные машины на том же интерфейсе в Wireshark (как и в случае с Wireshark). симптомы выше). Я думаю, что драйвер хоста VirtualBox NetFilter, который отвечает за создание мостов, создает другую сеть, в которой обмениваются запросами и ответами хоста и виртуальных машин, в то время как хост и виртуальная машина взаимодействуют с внешним миром через реальный интерфейс. Очевидно, мне нужно было бы создать свой собственный хост-драйвер, который бы вводил пакеты в поток приема, которые отправляются с моих виртуальных машин QEMU через мост.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...