Hi there, firstly apologies as I don't know much about networking. I've tried to piece together my problems through reading various articles online but haven't been able to find a solution.
I'm using virt-manager with qemu/kvm for virtual machines. To provide my VMs with internet access, I created a bridge as specified here (https://github.com/tomit4/notes/blob/main/install_qemu.txt). However, my VMs are unable to access the internet.
I think the issue is that there's no virbr0-nic interface that virbr0 is bound to.
Output of `ip link show type bridge`:
3: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc htb state DOWN mode DEFAULT group default qlen 1000
link/ether 52:54:00:0a:cd:21 brd ff:ff:ff:ff:ff:ff
Output of `ip link show master virbr0` is empty.
Output of `ip link` is
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether e0:d5:5e:23:27:82 brd ff:ff:ff:ff:ff:ff
3: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc htb state DOWN mode DEFAULT group default qlen 1000
link/ether 52:54:00:0a:cd:21 brd ff:ff:ff:ff:ff:ff
I've tried binding virbr0 to eth0 but that makes my internet go down outside of VMs. As I understand it binding a bridge to an interface means that the interface can't be used outside of the bridge?
My understanding is that virbr0-nic is supposed to be automatically created, made the master of virbr0, and is meant to allow you to access your physical network card through it. But virbr0-nic seems to not be getting created.
How do I A. create it, and B. make it automatically create itself upon startup? Thanks.
Edit: If relevant, I am using runit
You will need to install and start the services virtlogd and librvirtd; that will do what you need.
This page might get you started: https://wiki.archlinux.org/title/Virt-manager
artist
I have both services installed and running already. Using virt-manager with KVM/QEMU works except for internet access, but I can otherwise use my VMs as normal.
Pls provide all related details so the issue can be analyzed.
artist
I provided the details I thought were relevant in the OP. I think this is a case of me not knowing enough to know what info to share. XML for my default virtual network is
<network connections="1">
<name>default</name>
<uuid>9a05da11-e96b-47f3-8253-a3a482e445f5</uuid>
<forward mode="nat">
<nat>
<port start="1024" end="65535"/>
</nat>
</forward>
<bridge name="virbr0" stp="on" delay="0"/>
<mac address="52:54:00:0a:cd:21"/>
<ip address="192.168.122.1" netmask="255.255.255.0">
<dhcp>
<range start="192.168.122.2" end="192.168.122.254"/>
</dhcp>
</ip>
</network>
as specified in the thing I linked to in the OP which I just copied.
I've found a stack exchange on how to create such an interface (https://serverfault.com/questions/516366/how-virbr0-nic-is-created) but from what I've read, this virbr0-nic interface is supposed to get auto-created, and I don't want to have to recreate the interface manually every time I reboot or add a janky startup script to create it. It sounds like I've either misconfigured libvirt or there's a bug.
What init do you use and what method of managing the network ?
I could be wrong and there's some magic where virt-manager / libvirt does all this for you but the way I do it is:
Create the bridge with the network management tool. Which for me is netifrc and comprises of
config_eth0="null"
config_br0="192.168.1.111/24"
bridge_br0="eth0"
routes_br0="default via 192.168.1.1"
in /etc/conf.d/net
The same setup with 'ip' commands
ip link set eth0 down
ip link add name br0 type bridge
ip link set eth0 master br0
ip link set eth0 up
ip addr add 192.168.1.111/24 dev br0
ip link set br0 up
ip route add default via 192.168.1.1 dev br0
Then I tell virt-manager to use br0
Edit:
192.168.1.1 is my router. Change to your routers ip address if it differs.
runit with NetworkManager.
If I make eth0 the master of virbr0, that prevents me from using eth0 except through virbr0, meaning that e.g. if I launch a browser on the host machine I can't browse the internet.
Having just tried again just now, following the same commands as you (except omitting `ip link add name br0 type bridge` and using `virbr0` instead of `br0` since `virbr0` already exists, and using the IP address of my router and not yours), on the host machine I seem to be able to ping external IP addresses but not domains, ie DNS resolution doesn't work. In VMs I can access the internet just fine, both IP addresses and domains.
That's how it works except vibr0 is the master.
You just needed to temporarily correct the DNS on the host then.
But the ip commands were just a proof of concept.
Use networkmanager to create a bridge (with eth0 bound to it). Use that bridge in virt-manager.
Sorry, but how do I do this? I'm using one of Mullvad's DNS resolvers which I've set on my router, so I'm getting DNS resolution from my router.
I've tried
nmcli con add type bridge-slave ifname eth0 master virbr0
(which I think is the reverse of the master/slave relationship from above? edit: nvm, i think i got things mixed up in the op, it's the same as the above) which seems to not affect my internet connectivity on the host machine, but in VMs connection seems to be unreliable. I did some testing in a Fedora VM with KDE, and browsing websites with Librewolf seems to not work, whilst I am able to ping most IP addresses and domains (so DNS resolution is working to some degree). KDE reports the connection as having "limited connectivity". Am I missing something?
Ok so what I was missing was
nmcli connection modify virbr0 connection.autoconnect-slaves 1
nmcli connection up virbr0
which gets me the same results as before, except on the host I also can't ping IP addresses and have no internet connectivity at all.
$ nmcli dev show
GENERAL.DEVICE: eth0
GENERAL.TYPE: ethernet
GENERAL.MTU: 1500
GENERAL.STATE: 100 (connected)
GENERAL.CONNECTION: bridge-slave-eth0
GENERAL.CON-PATH: /org/freedesktop/NetworkManager/ActiveConnection/3
WIRED-PROPERTIES.CARRIER: on
IP4.GATEWAY: --
IP6.GATEWAY: --
GENERAL.DEVICE: lo
GENERAL.TYPE: loopback
GENERAL.HWADDR: 00:00:00:00:00:00
GENERAL.MTU: 65536
GENERAL.STATE: 100 (connected (externally))
GENERAL.CONNECTION: lo
GENERAL.CON-PATH: /org/freedesktop/NetworkManager/ActiveConnection/1
IP4.ADDRESS[1]: 127.0.0.1/8
IP4.GATEWAY: --
IP6.ADDRESS[1]: ::1/128
IP6.GATEWAY: --
GENERAL.DEVICE: virbr0
GENERAL.TYPE: bridge
GENERAL.MTU: 1500
GENERAL.STATE: 100 (connected (externally))
GENERAL.CONNECTION: virbr0
GENERAL.CON-PATH: /org/freedesktop/NetworkManager/ActiveConnection/2
IP4.ADDRESS[1]: 192.168.122.1/24
IP4.GATEWAY: --
IP4.ROUTE[1]: dst = 192.168.122.0/24, nh = 0.0.0.0, mt = 0
IP6.GATEWAY: --
virbr0.nmconnection:
[connection]
id=virbr0
uuid=9ee909ac-8ec0-4fcc-ab08-a79f21274c8f
type=bridge
autoconnect=false
autoconnect-ports=0
interface-name=virbr0
timestamp=1750955422
[ethernet]
[bridge]
forward-delay=2
[ipv4]
address1=192.168.122.1/24
method=manual
[ipv6]
addr-gen-mode=default
method=disabled
[proxy]
bridge-slave-eth0.nmconnection, generated by the command in my previous reply:
[connection]
id=bridge-slave-eth0
uuid=6b0e9285-6bee-423a-b45c-04b2e8f42f05
type=ethernet
controller=virbr0
interface-name=eth0
port-type=bridge
[ethernet]
[bridge-port]
Would have done. You could have just stuck 'nameserver 1.1.1.1' in /etc/resolv.conf and commented out the other entry.
Or backed up the file etc. But I fear this is adding to the confusion which I'm partly responsible for (more below).
When I read your initial post you were talking about a bridge. I think you did as well?
What I have described configuring is what I consider a software network bridge.
What I've since learnt about virbr0 is it is sort of a bridge, a virtual network bridge, but it provides NAT not a connection to your physical network i.e.
If you use NAT (virb0) , and it's working, your vm guests would be able to connect to the internet, and connect to each other, but not host to guest or guest to host because they'd be on separate networks.
If NAT is what you actually want then disregard anything I've said and figure out why NAT is not working, which I probably can't help with as I have rarely used it.
If a bridge is what you want then I'd suggest deleting virbr0 and going with br0 . While the name may not matter there's the chance libvirt is interfering with it?
imho a standard bridge is better as you set one up and then it's always there to be used by any vm's you spin up.
I'd only use NAT if i specifically want the vm's on a separate network.
edit: I haven't addressed your most recent post in this one.
Without being sat at your computer it's hard to say what's going on.
But if I was you I'd try and start again from as close to a blank slate as possible.
Get all references to virbr0 gone from your system network setup, and the guest vm's network setup then create the br0 bridge and set up the guest to use it.
To me networkmanager just seems to add complexity rather than making it all easier?
But unfortunately netifrc is unique to openrc so you are probably best sticking with networkmanager.
Thanks for your help, and apologies for any frustration arising out of my ignorance.
Yes, I guess the problem I am ultimately trying to solve is that I want QEMU/KVM VMs managed by virt-manager to have internet access through my regular ethernet connection. I would like to be able to ssh into my VMs from the host, and still have internet access as usual from the host. I suppose VMs being able to connect to each other might be useful but I've never needed that functionality.
I was under the impression that virbr0, which is set up by libvirt, is how this is done. If there is a better way to achieve this, or if this is the wrong way entirely to achieve this, I'm of course open to other options. Just from reading things online the only thing that seemed out of place about my setup is that `ip link show master virbr0` didn't output anything, when normally there is a slave (?) interface called virbr0-nic generated by libvirt, based on the virt-manager networking guides I read online. So I assumed that my problem lay in whatever issue was preventing that interface from being created. But if the issue is that I'm barking up the wrong tree entirely, do let me know.
Looking in virt-manager, my "Network source" for all my VMs is set to "Virtual network 'default': NAT". So it seems I am using the NAT created by libvirt. It sounds like based on what you've said, I should try creating a completely different bridge to connect to and switch "Network source" in virt-manager to "Bridge device" and select the bridge I created?
Would I still have the aforementioned issue though, of not having internet connectivity on the host after creating this bridge and getting it working? As it seems when I've been setting eth0 as a slave of a bridge, that makes eth0 unusable.
As for using something other than NM, I'm not wholly opposed to it but I use NM because it "just works" especially with WiFi (I don't use WiFi on this device but I do for laptops) and generally things seem to be very easy with NM. VMs are not crucial to my workflow but I like having them to test things out without affecting my computer. I've not used anything else to manage my networking so I'd have to learn whatever other software I switch to if I were to switch.
No worries.
In the setup link you posted was this:
<network>
<name>default</name>
<uuid>9a05da11-e96b-47f3-8253-a3a482e445f5</uuid>
<forward mode='nat'/>
<bridge name='virbr0' stp='on' delay='0'/>
<mac address='52:54:00:0a:cd:21'/>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254'/>
</dhcp>
</ip>
</network>
That is NAT.
If you want to ssh to and from the hosts and the guest(s) you want a proper bridge as I've described.
Libvirt cannot setup a proper bridge on it own. Onnly the NAT type.
I have nm as my main interface for the host. I have this for my internet aware bridge; my host network is on 192.168.0.1/255 all managed by virt-manager
<network>
<name>vm-internet</name>
<uuid>c65c8a87-ff55-4eff-bb0e-95e250989547</uuid>
<forward mode="nat">
<nat>
<port start="1024" end="65535"/>
</nat>
</forward>
<bridge name="virbr1" stp="on" delay="0"/>
<mac address="52:54:00:6b:38:e0"/>
<domain name="vm-internet"/>
<dns forwardPlainNames="yes"/>
<ip address="192.168.123.1" netmask="255.255.255.0">
<dhcp>
<range start="192.168.123.128" end="192.168.123.254"/>
</dhcp>
</ip>
</network>
That's still NAT though?
Can the host and guest ssh to each other?
If they can I've been talking semi nonsense then, but I'm fairly sure that didn't use to be possible?
Well I can certainly see the host network from a VM, but I don't think I have transparent dns across the bridge, but explicit ip works.
artix-dinit:[robin]:~$ ping google.com
PING google.com (142.250.187.206) 56(84) bytes of data.
64 bytes from lhr25s33-in-f14.1e100.net (142.250.187.206): icmp_seq=1 ttl=116 time=1.86 ms
64 bytes from lhr25s33-in-f14.1e100.net (142.250.187.206): icmp_seq=2 ttl=116 time=4.38 ms
^C
--- google.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 1.857/3.116/4.375/1.259 ms
artix-dinit:[robin]:~$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host proto kernel_lo
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,DYNAMIC,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:54:00:3c:6b:ff brd ff:ff:ff:ff:ff:ff
inet 192.168.123.188/24 brd 192.168.123.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::5054:ff:fe3c:6bff/64 scope link proto kernel_ll
valid_lft forever preferred_lft forever
artix-dinit:[robin]:~$ ssh 192.168.0.9
Welcome to Artix
Last login: Fri Jun 27 08:27:49 2025 from 192.168.0.16
You! What PLANET is this!
-- McCoy, "The City on the Edge of Forever", stardate 3134.0
robin@delilah:~
I can also ssh directly into the VM using an explicit IP eg
[robin@minikat:~
$ ssh 192.168.123.188
artix-dinit:[robin]:~$
Although I have nat in the virtmanager setup I see this in nm
$ nmcli connection
NAME UUID TYPE DEVICE
eno1 9cb5f3b0-eb4b-48ee-b2b0-3943c010e2b7 ethernet eno1
lo 66bee815-e115-4b0b-bbb3-ea3275fd7afd loopback lo
virbr0 f07e7176-31ad-4bf1-aed4-c13debc255d9 bridge virbr0
virbr1 9df63bd0-abbd-4bff-b16e-02757a2b2065 bridge virbr1
vnet1 152c0aac-ffe9-4cdb-be3c-524f943a8925 tun vnet1
NeuHeimat 77841e45-e6d7-4eca-a405-d3291a517946 wifi --
NeuHeimat5 1c435a04-abd4-4efe-b25d-6d1d6814dd39 wifi --
I don't know where vnet1 comes from, but it's probably from virtmanager somehow.
virtmanager/libvirt puts a lot of stuff into my iptables to do all this eg
$ sudo iptables-save | sed -e 's/\[[0-9:]*\]/[0,0]/' -e '/^#/d' | grep LIBVIRT
:LIBVIRT_PRT - [0,0]
-A POSTROUTING -j LIBVIRT_PRT
-A LIBVIRT_PRT -o virbr1 -p udp -m udp --dport 68 -j CHECKSUM --checksum-fill
-A LIBVIRT_PRT -o virbr0 -p udp -m udp --dport 68 -j CHECKSUM --checksum-fill
:LIBVIRT_PRT - [0,0]
-A POSTROUTING -j LIBVIRT_PRT
-A LIBVIRT_PRT -s 192.168.123.0/24 -d 224.0.0.0/24 -j RETURN
-A LIBVIRT_PRT -s 192.168.123.0/24 -d 255.255.255.255/32 -j RETURN
-A LIBVIRT_PRT -s 192.168.123.0/24 ! -d 192.168.123.0/24 -p tcp -j MASQUERADE --to-ports 1024-65535
-A LIBVIRT_PRT -s 192.168.123.0/24 ! -d 192.168.123.0/24 -p udp -j MASQUERADE --to-ports 1024-65535
-A LIBVIRT_PRT -s 192.168.123.0/24 ! -d 192.168.123.0/24 -j MASQUERADE
-A LIBVIRT_PRT -s 192.168.122.0/24 -d 224.0.0.0/24 -j RETURN
-A LIBVIRT_PRT -s 192.168.122.0/24 -d 255.255.255.255/32 -j RETURN
-A LIBVIRT_PRT -s 192.168.122.0/24 ! -d 192.168.122.0/24 -p tcp -j MASQUERADE --to-ports 1024-65535
-A LIBVIRT_PRT -s 192.168.122.0/24 ! -d 192.168.122.0/24 -p udp -j MASQUERADE --to-ports 1024-65535
-A LIBVIRT_PRT -s 192.168.122.0/24 ! -d 192.168.122.0/24 -j MASQUERADE
:LIBVIRT_FWI - [0,0]
:LIBVIRT_FWO - [0,0]
:LIBVIRT_FWX - [0,0]
:LIBVIRT_INP - [0,0]
:LIBVIRT_OUT - [0,0]
-A INPUT -j LIBVIRT_INP
-A FORWARD -j LIBVIRT_FWX
-A FORWARD -j LIBVIRT_FWI
-A FORWARD -j LIBVIRT_FWO
-A OUTPUT -j LIBVIRT_OUT
-A LIBVIRT_FWI -d 192.168.123.0/24 -o virbr1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A LIBVIRT_FWI -o virbr1 -j REJECT --reject-with icmp-port-unreachable
-A LIBVIRT_FWI -d 192.168.122.0/24 -o virbr0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A LIBVIRT_FWI -o virbr0 -j REJECT --reject-with icmp-port-unreachable
-A LIBVIRT_FWO -s 192.168.100.0/24 ! -d 192.168.0.0/24 -i virbr1 -j ACCEPT
-A LIBVIRT_FWO -s 192.168.123.0/24 -i virbr1 -j ACCEPT
-A LIBVIRT_FWO -i virbr1 -j REJECT --reject-with icmp-port-unreachable
-A LIBVIRT_FWO -s 192.168.122.0/24 -i virbr0 -j ACCEPT
-A LIBVIRT_FWO -i virbr0 -j REJECT --reject-with icmp-port-unreachable
-A LIBVIRT_FWX -i virbr1 -o virbr1 -j ACCEPT
-A LIBVIRT_FWX -i virbr0 -o virbr0 -j ACCEPT
-A LIBVIRT_INP -s 192.168.100.0/24 -d 192.168.0.16/32 -i virbr1 -j REJECT --reject-with icmp-port-unreachable
-A LIBVIRT_INP -i virbr1 -p udp -m udp --dport 53 -j ACCEPT
-A LIBVIRT_INP -i virbr1 -p tcp -m tcp --dport 53 -j ACCEPT
-A LIBVIRT_INP -i virbr1 -p udp -m udp --dport 67 -j ACCEPT
-A LIBVIRT_INP -i virbr1 -p tcp -m tcp --dport 67 -j ACCEPT
-A LIBVIRT_INP -i virbr0 -p udp -m udp --dport 53 -j ACCEPT
-A LIBVIRT_INP -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT
-A LIBVIRT_INP -i virbr0 -p udp -m udp --dport 67 -j ACCEPT
-A LIBVIRT_INP -i virbr0 -p tcp -m tcp --dport 67 -j ACCEPT
-A LIBVIRT_OUT -o virbr1 -p udp -m udp --dport 53 -j ACCEPT
-A LIBVIRT_OUT -o virbr1 -p tcp -m tcp --dport 53 -j ACCEPT
-A LIBVIRT_OUT -o virbr1 -p udp -m udp --dport 68 -j ACCEPT
-A LIBVIRT_OUT -o virbr1 -p tcp -m tcp --dport 68 -j ACCEPT
-A LIBVIRT_OUT -o virbr0 -p udp -m udp --dport 53 -j ACCEPT
-A LIBVIRT_OUT -o virbr0 -p tcp -m tcp --dport 53 -j ACCEPT
-A LIBVIRT_OUT -o virbr0 -p udp -m udp --dport 68 -j ACCEPT
-A LIBVIRT_OUT -o virbr0 -p tcp -m tcp --dport 68 -j ACCEPT
I watched connection and machine startup with virt-manager.
My NAT virbr0/1 etc create a bridge device. VM startup creates a tun vnetx which is bridged by the associated virbry.
The virbr startup adds to the iptables, but it doesn't seem to be totally cleaned up when the bridge is stopped.
I find also that I have an excutable network hook /etc/libvirt/hooks/network I forget where that came from; probably a google
maybe https://superuser.com/questions/1842466/how-can-i-create-a-vm-in-virt-manager-that-can-access-the-internet-through-the-h
#!/bin/sh
DATA="$(cat)"
TAG="libvirt/hooks/$(basename $0)"
add_rule(){
local out=$(/usr/bin/iptables $@ 2>&1) || true
if [ -n "$out" ]; then
logger -t "$TAG" "'$out'" || true
fi
}
case "$2" in
(start)
#called before it is started
ls -alrt /var/lib/libvirt/dnsmasq/ > /tmp/libvirt-hooks.log
;;
(started)
case "$1" in
(vm-internet)
add_rule -D LIBVIRT_FWO -s 192.168.100.0/24 -i virbr1 -j ACCEPT
add_rule -I LIBVIRT_FWO -s 192.168.100.0/24 ! -d 192.168.0.0/24 -i virbr1 -j ACCEPT
add_rule -I LIBVIRT_INP -s 192.168.100.0/24 -d 192.168.0.16 -i virbr1 -j REJECT
;;
(*)
;;
esac
;;
(stopped)
#called after it is stopped
case "$1" in
(vm-internet)
add_rule -D LIBVIRT_FWO -s 192.168.100.0/24 ! -d 192.168.0.0/24 -i virbr1 -j ACCEPT
add_rule -D LIBVIRT_INP -s 192.168.100.0/24 -d 192.168.0.16 -i virbr1 -j REJECT
;;
(*)
;;
esac
;;
(*)
logger -t "$TAG" "unknown arg 2 in '$0 $@'" || true
;;
esac
I stand corrected then. Maybe things have changed with how libvirt handles NAT along the way. Or maybe I've always been wrong ?
I'm fairly sure though that NATwith libvirt used to have drawbacks, compared to bridged connections, when you wanted to ssh between host and guest?
I'll be sticking with my system created bridge anyway as it seems simpler to me.
My network hook above is wrong; it should use the 192.168.123.0/24 not 192.168.100.0/24. If I fix it then I finally see what I was trying to do. The virbr1 ie vm-internet connection is supposed to be internet only. The VM cannot connect to the host network, but the host can see the VM.
My normal default virbr0 can see the internet and also the local network it runs on the default 192.168.122.0/24 net.
Hey, just got some time to work on things now. Thanks so much for the help
@gripped, I made a bridge following these instructions for NetworkManager (https://www.cyberciti.biz/faq/how-to-add-network-bridge-with-nmcli-networkmanager-on-linux/) and everything works very well. I can access the internet as normal from my host machine and also without any problems from the guest machines. Not sure why my libvirt-created NAT was behaving so differently to how other people report but no biggie, as long as I have working internet on both my guest and host machines :)