The Best of Both Worlds
In order to access the Web server and Linux's secure shell, I added these parameters to the qemu.exe command:
-redir tcp:88:10.0.2.15:80 -redir tcp:22::22
The first -redir parameter makes QEMU answer any requests to port 88. Actually, it is not going to answer it itself but redirects it to server 10.0.2.15, port 80, which is the Linux Web server. I chose port 88 (different from 80) in case I need to run any other Web service (such as EasyPHP) in Windows, so they don't have conflicts with each other. To test that it's working, open http://127.0.0.1:88/ in a browser. Make sure that the Linux network interface has been configured (with dhclient) after the Linux server has been started.
The second -redir parameter makes QEMU redirect any connections to port 22 (secure shell) to Linux's port 22. If the server IP is missing, the default value is 10.0.2.15, which corresponds to the IP given by the emulated DHCPD to the emulated system (Linux). To access the shell of a Linux server from Windows, I use PuTTY by connecting to 127.0.0.1 port 22. Accessing the Linux shell through PuTTY is much more convenient than accessing it through the QEMU console, because I can open several terminals at the same time, and I can copy/paste between Linux and Windows. I also can enlarge PuTTY terminals and adjust fonts and colors. It also is possible to use pscp to copy files between Windows and Linux through SSH.
If you want to make these Linux services (httpd and sshd) accessible to the network as well (so they can be accessed from other computers on the local network), open the Windows firewall for them: Control Panel→Windows Firewall→Exceptions→Add Program. Then browse, select D:\QEMU\qemu.exe, and press OK. Next, open Control Panel→Windows Firewall→Exceptions→Add Port, and add ports 88 and 22. Also check the box Change scope... when you add or edit a program or port.
I also want to access other Linux services, such as Samba and FTP. Adding another -redir parameter for each port that I want to access is not convenient, and it's not an elegant solution anyway. I want to be able to access Linux from Windows without any restrictions. It does not seem to be so easy, because all that Windows can see is the qemu.exe process, and it has no idea what goes on inside it. So, how can Windows communicate directly with the Linux that runs inside QEMU? It is possible by creating a tap virtual Ethernet adapter using OpenVPN.
I downloaded openvpn-2.0.9-install.exe and installed it. During the installation, I checked only the components TAP-Win32 Virtual Ethernet Adapter, Add OpenVPN to Path and Add Shortcuts to Start Menu, because I didn't need the others. I changed the destination folder to D:\QEMU\OpenVPN, because I prefer to group the related tools together. I received some warnings that this software has not passed Windows testing, but I continued anyway, trusting that open-source testing is stronger than Windows testing.
After installation, I selected the menu Start→OpenVPN→Add a new TAP-Win32 virtual Ethernet adapter to create a new tap interface. Again, I received the same warnings, but continued anyway. Now, in Network Connections, I find a new network connection named Local Area Connection 1. I right-click on it and rename it tap1.
Then, I modified start-linux.bat by adding these parameters to QEMU:
-net nic,vlan=0 -net tap,vlan=0,ifname=tap1 -net nic,vlan=1 -net user,vlan=1
The parameter -net nic tells QEMU to create a new network interface for the emulated system. Because this parameter has been used twice, Linux is going to run in a machine that has two network interfaces, eth0 and eth1. The parameter -net user creates a virtual interface on the other side (the Windows side). It is the network interface that was created previously by default (the one that has a built-in DHCP server associated with it), even though we didn't specify any -net parameters. The parameter -net tap tells QEMU to use the virtual Ethernet adapter tap1, which we created previously, and to connect it to the virtual network. The vlan options that are used with the -net parameters tell QEMU how to connect these virtual interfaces to each other. All the interfaces that have the same vlan number are connected to the same virtual hub/switch. So, we have two switches in our virtual network that is emulated by QEMU.
The last two lines of linux-start.bat now look like this:
net start kqemu qemu.exe -L . -m 256 -kernel-kqemu -hda \\.\PhysicalDrive0 ↪-localtime -redir tcp:88:10.0.2.15:80 -redir tcp:22::22 ↪-net nic,vlan=0 -net tap,vlan=0,ifname=tap1 ↪-net nic,vlan=1 -net user,vlan=1
Note that the QEMU parameter -soundhw all is now missing. I removed it because one of the sound devices was creating conflicts with the network devices, so they were not recognized properly as eth0 and eth1. If you can't do without a sound device, at least append it at the very end of the line; the parameters' order does matter.
The order of the -net parameter declarations matters as well. I have noticed that if -net user is declared before -net tap, eth0 and eth1 are switched with each other, and there is also a failure to initialize eth0 during the Fedora initialization scripts. Keep this in mind, in case you have any similar problems.
After starting QEMU, we have a (virtual) physical network (Figure 4).
To check the “physical” connections of the network, press Ctrl-Alt-2 to switch to the QEMU monitor. Then, in the (qemu) prompt, give the command info network. Finally, press Ctrl-Alt-1 to get back to the Linux console. Here's the output from the command:
VLAN 0 devices: tap: ifname=tap1 ne2000 pci macaddr=52:54:00:12:34:56 VLAN 1 devices: user redirector ne2000 pci macaddr=52:54:00:12:34:57
Now, we need just to configure the network settings properly, such as IPs, gateway and DNS.
The user redirector interface on the Windows side is configured automatically by QEMU, with IP 10.0.2.2/24, and we don't have access to it, so we cannot modify it. If you check in Network Connections, you will find that the virtual interface tap1 now appears to be connected. To configure it, right-click on it and select Properties, then select Internet Protocol (TCP/IP) and Properties again. In the configuration window, set a fixed-IP address of 192.168.10.10 and netmask 255.255.255.0. It's just like a usual network interface configuration.
To check that the network configuration is okay, run ipconfig at the command prompt, and you'll see this output for tap1:
Ethernet adapter tap1: Connection-specific DNS Suffix . : IP Address. . . . . . . . . . . . : 192.168.10.2 Subnet Mask . . . . . . . . . . . : 255.255.255.0 Default Gateway . . . . . . . . . :
This output is displayed when QEMU is running; otherwise, the information for tap1 will be something like: Media disconnected.
Now, we're done with network configuration on the Windows side. This has to be done only once. All that is left is configuring the network interfaces on the Linux side.
First, log in as root. To check that you already have two network interfaces, run ip addr, and it should list eth0 and eth1. You can configure eth0 automatically, like this: dhclient eth0, as we did previously, and it will get an IP from QEMU's built-in DHCP server. Then, you can continue with eth1's manual configuration.
However, I prefer to use scripts whenever possible, and I want to make sure that eth0 always gets the IP 10.0.2.15/24, no matter what, because this is important for the -redir parameters shown previously. So, I do the network configuration on the Linux side by running this script (which has to be rerun whenever the system is rebooted):
bash# cat /usr/local/config/net-config-qemu.sh #!/bin/bash ### configure the network when Linux is being ### emulated from Windows by QEMU ### network settings IP0=10.0.2.15/24 ## eth0 IP1=192.168.10.10/24 ## eth1 GW=10.0.2.2 ## gateway DNS=10.0.2.3 ### configure eth0 ip link set dev eth0 up ip address flush dev eth0 ip address add $IP0 dev eth0 ### configure eth1 ip link set dev eth1 up ip address flush dev eth1 ip address add $IP1 dev eth1 ### set the gateway ip route add to default via $GW ### set the DNS server echo "nameserver $DNS" > /etc/resolv.conf
To check that the network configuration is okay, run ip addr, ip route and cat /etc/resolv.conf. Here's output from these commands:
[root@fedora6 ~]# ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000 link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff inet 192.168.10.10/24 scope global eth1 3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000 link/ether 52:54:00:12:34:57 brd ff:ff:ff:ff:ff:ff inet 10.0.2.15/24 scope global eth0 4: sit0: <NOARP> mtu 1480 qdisc noop link/sit 0.0.0.0 brd 0.0.0.0 [root@fedora6 ~]# [root@fedora6 ~]# ip route 10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15 192.168.10.0/24 dev eth1 proto kernel scope link src 192.168.10.10 default via 10.0.2.2 dev eth0 [root@fedora6 ~]# [root@fedora6 ~]# cat /etc/resolv.conf nameserver 10.0.2.3 [root@fedora6 ~]#
Now, all that remains is making sure the network is working as expected. The first check is to ping from Linux 10.0.2.2. If it is not working, it's possible that you need to switch eth0 and eth1. Sometimes, the network interface with MAC 52:54:00:12:34:56 is recognized by Linux as eth0, and the other as eth1, and sometimes it is recognized as eth1 and the other as eth0. This depends on the Linux distribution (Fedora, Slackware or whatever else). So, it is possible that eth0 and eth1 have gotten the wrong IP addresses from the configuration script, and in that case, the ping won't work. To solve this problem, modify the IP addresses that are assigned to eth0 and eth1 in the script /usr/local/config/net-config-qemu.sh, and run it again.
Now that the ping with 10.0.2.2 is working, try to ping 192.168.10.2 (tap1) from Linux. In general, it doesn't work. This is strange, because the ping to 192.168.10.10 from the command prompt in Windows does work. The problem is with the Windows firewall. To fix this, open the Control Panel, double-click Windows Firewall, select Advanced tab, select tap1 and click on Settings, then choose the ICMP tab, and here, check Allow incoming echo request. After this, the ping to 192.168.10.2 should work.
Don't try to ping to 10.0.2.15 from the command prompt in Windows, because it can't possibly work. Do you wonder why? Me too.
The next thing to try is accessing some Linux services from Windows using the IP 192.168.10.10. Try to open http://192.168.10.10 in a browser, and you will see the pages that are served by the Linux Web server. Try also to log in through PuTTY to 192.168.10.10, port 22, and you will access the Linux shell.
Finally, we have a first-class bidirectional network connection between Windows and Linux, which can be used to access any Linux services from Windows.