Я пытаюсь создать мост к интерфейсу на моем хосте, очень похожий на мостовые адаптеры 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, сделанные с хоста и с нового ноутбука в сети, соответственно:
На снимках экрана, сделанных выше, любое устройство за пределами моего хоста (маршрутизатор на 192.168.1.1, контроллер Rasberry Pi и другой ноутбук) может общаться с моим VM QEMU и ответы на запросы ARP (и VM QEMU аналогично), но не хост.
Вот наблюдение того, что я вижу (извиняюсь за плохой рисунок):
Однако, если я заменю виртуальную машину QEMU виртуальной машиной Virtualbox, между хостом и виртуальной машиной Virtualbox будет установлено соединение. Кроме того, виртуальная машина QEMU также не может общаться с любым компьютером внутри хоста, например между виртуальной машиной QEMU и виртуальной машиной Virtualbox, которые оба подключены к одной локальной сети через интерфейс хоста.
Редактировать # 2:
Еще одна вещь, которую я заметил, заключается в том, что при ARPing моего хоста от моих виртуальных машин VirtualBox я не вижу ответа ARP от моего хоста на виртуальные машины на том же интерфейсе в Wireshark (как и в случае с Wireshark). симптомы выше). Я думаю, что драйвер хоста VirtualBox NetFilter, который отвечает за создание мостов, создает другую сеть, в которой обмениваются запросами и ответами хоста и виртуальных машин, в то время как хост и виртуальная машина взаимодействуют с внешним миром через реальный интерфейс. Очевидно, мне нужно было бы создать свой собственный хост-драйвер, который бы вводил пакеты в поток приема, которые отправляются с моих виртуальных машин QEMU через мост.