I am trying to create a bridge to an interface in my host, much like the Virtualbox’s and VMWare’s bridge adapters, in QEMU, using a combination of socket networking and a Python library called Scapy (essentially, relying on WinPcap/Npcap on Windows OSes or libpcap on Unix OSes behind the scenes). Here’s the bridge script I created to connect the VLAN created by the socket network backend of QEMU to the outside interfaces:
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()
With this bridge source code, I was able to ping back and forth between a device outside of my host (e.g. a Raspberry Pi controller) and my QEMU VM, which both of my controller and my host reside on the same LAN. However, I was not able to do the same thing between my QEMU VM and my host on the same network. I would like to know if the issue has to do with having two different MAC addresses (i.e. my QEMU VM’s MAC address and my host’s MAC address) having the same interface (the host’s interface connected to the LAN) and the traffic to the same LAN gets filtered out, or what am I missing here?
Edit #1:
So, I inspected the packets being sent/received on the network by either of the QEMU VM or my host, and they are being received on all devices on the network. I have connected a router to my network, and another laptop device with a Wi-Fi and Wireshark to join the network through the router. I can see the packets being received on all ends of the network (e.g. on the new laptop on the network), but my host doesn’t respond to the ARP packets, for e.g., that were originated by another machine on the same interface such as the QEMU VM.
Here are Wireshark screenshots taken from the host and from the new laptop on the network, respectively:
From the screenshots taken above, any device outside of my host (the router at 192.168.1.1, the Rasberry Pi controller, and the other laptop) can talk to my QEMU VM and replying to ARP requests (and the QEMU VM likewise), but not the host.
Here’s an observation of what I am seeing (apologize for the bad drawing):
However, if I replace the QEMU VM with the Virtualbox VM, there would be a connection between the host and the Virtualbox VM. In addition, the QEMU VM cannot talk to any machine inside the host either, such as between the QEMU VM and the Virtualbox VM, which both are connected to the same LAN through the host’s interface.
Edit #2:
Another thing I observed is that when ARPing my host from my VirtualBox VMs, I would not see the ARP reply from my host to the VMs on the same interface in Wireshark (just like the symptoms above). I think VirtualBox’s NetFilter host driver that is responsible for creating bridges creates another network, which the host’s and the VMs’s requests and replies are exchanged, while the host and VM’s communicate with the outside world on the real interface. Apparently I would need to create my own host driver that would inject the packets into the receive stream that are sent from my QEMU VMs through the bridge.
Okay, this is a complex issue involving network bridging, packet manipulation, and interaction with the host operating system. Let\’s break down the problem and potential solutions. Your analysis is good so far, and the Wireshark captures are helpful.\n\n**The Core Problem:**\n\nYou\’ve correctly identified the problem: While your QEMU VM can communicate with devices *outside* the host, it cannot communicate directly with the host itself, or with other VMs running on the same host. This suggests the issue is related to how the host is processing (or *not* processing) packets originating from the QEMU VM that are being injected back into the host\’s network interface.\n\n**Why VirtualBox Works (And Your Solution Doesn\’t):**\n\nYour \”Edit #2\” gets to the heart of the matter. VirtualBox and VMware use specialized kernel-level drivers (NetFilter in VirtualBox\’s case) to create a virtual bridge. These drivers operate within the OS\’s network stack, allowing them to:\n\n1. **Intercept and Modify Packets:** They can intercept packets from the VM, rewrite MAC addresses (if necessary), and inject them directly into the host\’s network stack as if they came from a different process on the host.\n2. **Handle ARP correctly:** The VirtualBox driver handles ARP requests and replies, making the VM appear as a fully integrated member of the host\’s network from the host\’s perspective.\n3. **Bypass Filtering:** These low-level drivers can often bypass standard network filtering rules that might otherwise block traffic.\n\nYour Scapy-based bridge, on the other hand, is a user-space application. It sends raw packets onto the network interface, but it doesn\’t have the same level of integration with the host\’s network stack. This leads to the host effectively ignoring the packets originating from the QEMU VM that are \”reflected\” back onto the same interface.\n\n**Reasons for the Host Ignoring Packets:**\n\nSeveral factors could be contributing to this:\n\n* **Source MAC Address Filtering (Anti-Spoofing):** Most network stacks have some form of anti-spoofing protection. The host might be dropping packets that have a source MAC address that doesn\’t \”belong\” to the host itself on that interface. The OS detects that the packet\’s source MAC address is not its own, and drops the packet thinking it is spoofed traffic.\n* **ARP Cache Issues:** The host\’s ARP cache might not be correctly populated with the QEMU VM\’s MAC address to IP mapping. While the host might see the ARP requests, it might not associate them with the correct interface or believe they are legitimate.\n* **Network Interface Configuration:** The host\’s network interface might not be configured to allow traffic from the QEMU VM. Some configurations may restrict traffic based on MAC address or VLAN.\n* **Firewall Rules:** While less likely, firewall rules on the host could be blocking traffic originating from the QEMU VM\’s MAC address.\n* **Loopback Prevention:** The OS might be actively preventing what it sees as a loopback situation. It\’s detecting packets originating and arriving on the same interface, and dropping them to prevent network storms.\n* **Multicast TTL:** While you set the TTL for outgoing packets, it might be related to how the host is handling the \”reflected\” multicast traffic. It\’s less likely, but worth considering.\n\n**Solutions and Approaches (Ordered by Difficulty and Effectiveness):**\n\n1. **The Simplest (But Least Likely to Work) – Disable Anti-Spoofing/MAC Address Filtering (Host Configuration):**\n\n * This is the first thing to try, but it has limitations and security implications. Check your OS documentation for how to disable MAC address filtering or anti-spoofing measures on your network interface. For example, on Linux, you might look at `rp_filter` (Reverse Path Filtering) settings. **Be very careful with this, as disabling these protections can make your host vulnerable to attacks.** This likely will not work by itself but may be a pre-requisite for other solutions.\n\n2. **ARP Table Manipulation (Ugly Hack – Not Recommended for Production):**\n\n * *Manually* add an ARP entry for the QEMU VM to the host\’s ARP table. This is a temporary workaround, and the entry will likely expire. Use the `arp` command (Linux) or `arp -s` (Windows) to add the entry. This is **not** a robust solution.\n * On Windows, you might need to run `netsh interface ipv4 set neighbor