Update: see comments
Virtual machines are given a virtual network, but have to decide on their own IP address, which they would usually get from DHCP or by static assignment. This is analogous to the physical case: a physical machine is plugged into a network port, but has to choose its own IP address somehow.
The problem with this is how do you know what IP address the virtual machine has picked?
There are several methods, but by far the simplest is to look at the output of the arp command on the same network segment (usually, just running arp -an
on the host will do). As long as the VM has sent anything over the network, it will have sent out its IP address in an ARP response, and the host will have picked that up and added it to the ARP table.
For example, the one virtual machine I am running now has broadcast its IP address to the host:
$ arp -an ? (192.168.0.**) at **:**:**:23:06:bb [ether] on eth0 ? (192.168.0.**) at **:**:**:74:02:28 [ether] on eth0 ? (192.168.122.16) at 52:54:00:18:04:63 [ether] on virbr0 ? (192.168.0.**) at **:**:**:7c:8b:7e [ether] on eth0
I already described this and it works well if you have one or a few virtual machines, but if you have many, how do you know which MAC address corresponds to the one you want?
libvirt knows the MAC address which was assigned to each virtual machine’s network card. It is in the libvirt XML for the domain, and you can get it from libvirt using the command virsh dumpxml
.
If we put all this together, we can write a short script which queries libvirt for the MAC address, then looks that up in the ARP table. All you need to do is to write:
# virt-addr F14x64 192.168.122.16
Note: As it stands this would only work as root, but you can make it also work for non-root by setting this environment variable:
$ export LIBVIRT_DEFAULT_URI=qemu:///system $ virt-addr F14x64 192.168.122.16 $ ssh $(virt-addr F14x64) rjones@192.168.122.16's password: *** Last login: Sat Oct 16 16:56:17 2010 from 192.168.122.1 [rjones@f14x64 ~]$
The script follows below.
#!/usr/bin/perl -w use strict; use XML::XPath; use XML::XPath::XMLParser; use Sys::Virt; # Open libvirt connection and get the domain. my $conn = Sys::Virt->new (readonly => 1); my $dom = $conn->get_domain_by_name ($ARGV[0]); # Get the libvirt XML for the domain. my $xml = $dom->get_xml_description (); # Parse out the MAC addresses using an XPath expression. my $xp = XML::XPath->new (xml => $xml); my $nodes = $xp->find ("//devices/interface[\@type='network']/mac/\@address"); my $node; my @mac_addrs; foreach $node ($nodes->get_nodelist) { push @mac_addrs, lc ($node->getData) } # Look up the MAC addresses in the output of 'arp -an'. my @arp_lines = split /\n/, `arp -an`; foreach (@arp_lines) { if (/\((.*?)\) at (.*?) /) { my $this_addr = lc $2; if (list_member ($this_addr, @mac_addrs)) { print "$1\n"; } } } sub list_member { local $_; my $item = shift; foreach (@_) { return 1 if $item eq $_; } return 0; }
If anybody prefers Python, my old script (now updated … arp cache is much better than parsing /var/log/messages which I did before) is on http://gitorious.org/various-small-stuff/virt-addr/blobs/master/virt-addr.py
… and of course, even better is to use avahi and you can do then
ssh .local
Interesting Matěj, I didn’t notice that our domains advertise themselves over Rendezvous (or whatever the protocol is called today), but it does work:
no, domain doesn’t advertise anything, but a virtual machine is just another computer on network, right? So, if its Avahi (or whatever Mac and Windows can use) advertise anything, than it is visible as from any networked computer. Of course, you have to make sure iptables are not in the way.
Nifty script. Are you putting these scripts into a utils or similar package? They’re really useful for people. 🙂
Not sure how much this particular script is worth it, given Matěj’s comment above that you can just do
ssh foo.local
.That’s a thought, but not sure how many people’s guests would actually be running services like Avahi.
Certainly no-one who cares about security of their guests would let them run things advertising their capabilities or installed software.
+1 I do not run Avahi, I have _clean_ Fedora installation (bash, vim, x window system). I’d appreciate this script to be part of the Rich’s tools 😀
If user uses bridge type network and uses xenbr0 rather than virbr0 bridge in the guest interface, it seems that we willn’t get any ip address return by arp -an.
arp packets don’t pass across routed networks, so you wouldn’t necessarily see them going from one bridge to another.
Link-local IPv6 would still work across a bridge. Why are people still using Legacy IP for things like this?
You can use arpwatch. It’s a daemon running on the hypervisor that’s printing all new ARP-requests on all interfaces (by default) to syslog. You can then parse syslog for that MAC-address to find the corresponding IP.
Pingback: Tip: Code for getting DHCP address from a virtual machine disk image | Richard WM Jones
what if the MAC address in not in arp cache ? in which case virt-addr doesn’t give any result
Looks like I landed in an expert nest, well let me just take the occasion to ask this question.
I was able to setup my virtual machines, they seem to pickup 2 ips, the first is the one from the br0, 122.X the second is from my dns server 1.X, but for some reason I cannot ping their 122.X ids and I can’t refer to them with hostname, what I’m I doing wrong.
can I get the dom name of a Virtual Machine if I know its IP address ?
As I understand it, the arp table is only populated on demand. So if the host hasn’t already communicated with the guest there’s going to be no entry in the arp table. So how can the script work?
you saved my -script- life, thanks!
“arp -a” will not work if you’re using a bridge to your physical LAN so the guests can get non-NAT’d networking.
I installed arpwatch (debian package “arpwatch”) as advised by Niklas Andersson (@niklasa9). This immediately reported ip-to-mac assignments via email. I had postfix running and it sent them to local mailbox of the non-root user. A sample mail is:
hostname:
ip address: 192.168.42.131
interface: eth0
ethernet address: 52:54:00:6c:55:52
ethernet vendor:
timestamp: Tuesday, January 15, 2013 23:57:51 +0000
I can then do this in virsh on each domain to find a match:
virsh # domiflist wheezy001
Interface Type Source Model MAC
——————————————————-
vnet1 bridge br0 virtio 52:54:00:6c:55:52
Is there an easier way to find which dom the mac belongs to rather than use domiflist on every domain? I only half half a dozen but it’s a hassle.
easier way and most relevant if using dhcp. you can grep syslog with that mac:
virsh # domiflist win7
Interface Type Source Model MAC
——————————————————-
vnet0 bridge br0 – 52:54:00:a8:19:16
[ROOT@delphinus log]# grep ‘new station’ /var/log/syslog | grep 52:54:00:a8:19:16
Jan 15 23:57:51 delphinus arpwatch: new station 192.168.42.130 52:54:00:a8:19:16 eth0
I HATE perl, for not other reason that I have a personal preference against it. So if you want to accomplish the same thing. Here is how you can do this with BASH (ip, virsh, awk).
ip neighbour | grep $(sudo virsh dumpxml Domain_Name | \
grep “mac address” | awk -F\’ ‘{print $2}’) | awk ‘{print $1}’
Libvirt’s participating in GSoC this year we’re gonna be working on this feature [1]. So hold your fingers crossed and by the end of summer you’ll be able to ‘virsh getipaddress $dom’ or similar 🙂
1: http://qemu-project.org/Google_Summer_of_Code_2013#Introduce_API_to_query_IP_addresses_for_given_domain
Pingback: Find Guest IP address using QEMU Guest Agent « A Random Walk Down Tech Street
Michal, Can’t wait til this feature comes through. In the meantime you may be able to use qemu guest agent with the “guest-network-get-interfaces” command.. I have written a short post about it here:
http://dustymabe.com/2013/07/14/find-guest-ip-address-using-qemu-guest-agent/
Its fairly new stuff so not everyone may be able to use it but hope someone is able to 🙂
Dusty
domiflist seems reasonable, but it doesn’t work in Ubuntu 13.04 (or at least it doesn’t work on mine. Crossing my fingers doesn’t help with the summer of code command either. Thanks for trying though.
Basically you need to fill the arp cache. I do that by ping the “possible” IP and then grep in the arp out:
++++
[root@localhost ~]# virsh domiflist RHEL7-tests
Interface Type Source Model MAC
——————————————————-
vnet0 bridge br0 virtio 52:54:00:e4:f2:ca
+++
Then you have the MAC.
Run a cript to ping possible IP in your network (192.168.1 is what you may have to change…
+++
i=1;
while true
do
ping -c 1 -W 1 192.168.1.$i
i=`expr $i + 1`
if [ $i = “255” ]; then
break;
fi
done
+++
grep for the arp cache for the MAC
+++
[root@localhost ~]# arp -na | grep 52:54:00:e4:f2:ca
? (192.168.1.111) at 52:54:00:e4:f2:ca [ether] on br0
+++
Done!!!
Pingback: DNS for your virtual machines | Technitribe
This whole script can be compressed to a bash one-liner:
arp -an | grep `virsh dumpxml | grep ‘<mac' | grep -o '\([0-9a-f][0-9a-f]:\)\+[0-9a-f][0-9a-f]'` | grep -o '\([0-9]\{1,3\}\.\)\+[0-9]\{1,3\}'
Thanks for this! BTW, instead of repeatedly checking for list membership, it’s better practice (more idiomatic, better asymptotic complexity) to use hashes:
Add following to the end of your /etc/bash.bashrc and use virtip command.
You will have to log out and back in.
#alias virtip=’sudo virsh net-dhcp-leases default’