Setting up QEMU with a NAT on Linux

I’ve always had a lot of trouble setting up QEMU so that I’m able to ping from my host to the guest OS, and the documentation out there doesn’t help much. So here are my own instructions.

First. Most instructions out there use bridging at layer 2 in order to join an ethernet and tap (simulated ethernet) device. The biggest problem with this approach is that it doesn’t work the same way with wireless devices. Most likely you would need WDS (only certain hardware supports it), or parprouted to do layer 3 bridging. There are other problems, like the fact that you need to bring down the interface. So no, bridging is not a nice solution. For reference see this thread.

Another approach is to use a proxy arp. This is slightly better because it doesn’t need to bring down the interface, nor needs any special hardware support. However, you need to reserve one IP address for the spoofed ARP address. I chose not to use this approach mostly because I’m not familiar with it. For reference check this howto.

The simplest approach is to use a NAT. Here are the steps.

Solution

As root in the host, create and setup your tap0 device (192.168.100.0 will be our NAT network):

ip tuntap add tap0 mode tap
ip addr add 192.168.100.1/24 dev tap0
sysctl net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE

You’ll need to run your guest using this tap0 interface like this:

qemu-system-x86_64 -net nic -net tap,ifname=tap0,script=no,downscript=no windows.img

A more modern command seems to be:

qemu-system-x86_64 -netdev tap,id=nd0,ifname=tap0,script=no,downscript=no -device e1000,netdev=nd0 windows.img

And in your guest:

  • ip addr: 192.168.100.2
  • gateway: 192.168.100.1
  • dns: 8.8.8.8 (Google’s public DNS, you can check yours in /etc/resolv.conf)

That’s it. Now you should be able to use ping back-and-forth between the guest and the host, and both should be able to access the Internet 😉

Firewall

If your system has a firewall enabled, you are probably going to have to add more rules. As root in the host:

iptables -I FORWARD 1 -i tap0 -j ACCEPT
iptables -I FORWARD 1 -o tap0 -m state --state RELATED,ESTABLISHED -j ACCEPT

Update: I’ve updated the instructions to commands generally available in 2023, and tested them in Arch Linux with Windows 10. If you have a problem with the new commands leave a comment.

13 thoughts on “Setting up QEMU with a NAT on Linux

  1. Pingback: configure nat of qemu - Information technology

  2. one of it is

    brctl addbr br-dev
    brctl addif br-dev em1
    ifconfig br-dev “em1 ip address”
    ifconfig em1 0.0.0.0 up

    route add default gw 192.168.1.1 via br-dev

    and give the br-dev name in the configuration of virt-manager

    my vm system is ubuntu.

    Like

  3. Just in case, if you have multiple static IPs and want to use one specific ips, just modify the rule in POSTROUTING as –
    iptables -t nat -A POSTROUTING -o eth0 -j SNAT –to-source

    And all traffic would appear to arrive from there.

    Like

  4. Kindly share steps to configure the NAT in windows, Enable IP forwarding and Set iptables rules which should work for both windows 7 as well as on XP

    Like

  5. Does anyone knows how ti reproduce this on OS X ? Mac doesn´t support iptables command. I guess that it does have a natd command. Any hint ?

    Like

  6. Great, this really worked (Debian 8)! Your post was very helpful, as I also had a difficult time figuring this out from the documentation. Thanks a lot for sharing it!

    Like

  7. The first problem is the lack of support for sudo tunctl; the second is the reply that recommends using libvirt, which shows a complex set of steps, none of which appears to use any form of the qemu command, making it a disconnected distraction, rather than an example.

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.