TransWikia.com

Script to create macvlan bridge on the host doesn't work unless it's run twice

Unix & Linux Asked by Marc.2377 on December 29, 2020

I have a script that should create a macvlan bridge in the host when it starts up. The host is an up-to-date Arch Linux. This is intended to allow host and guest to share the same network *and talk to each other*. I found instructions given at:

https://www.furorteutonicus.eu/2013/08/04/enabling-host-guest-networking-with-kvm-macvlan-and-macvtap

(Regarding the execution at startup, I also consulted How to write startup script for Systemd? and https://stackoverflow.com/questions/21830670/systemd-start-service-after-specific-service).

The problem, however, is that the script is not effective at first try. It creates the macvlan device and the routing table, but doesn’t make it possible for the host to ping the guest and vice-versa.

But when executed a second time, it works – that is, despite an error message which reads "create_macvlan_bridge.sh[4489]: RTNETLINK answers: File exists". Host can now ping guest as expected.

It’s supposed to work at first try, though, and I can’t figure out why it’s not. Can anyone help?

[Update] I noticed the result of ip a shows a second inet entry for macvlan0@enp10s0 after the second execution:

macvlan0@enp10s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc
noqueue state UP group default qlen 1000
    link/ether da:a2:21:d1:95:24 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.3/24 scope global macvlan0
        valid_lft forever preferred_lft forever
    inet 192.168.1.22/24 brd 192.168.1.255 scope global secondary macvlan0
        valid_lft forever preferred_lft forever

Notice how this second ip address was provided by the dhcp from the router, and it has secondary attribute.

The weird thing is that, after the second execution, the guest can ping the host at 192.168.1.3 and at the "secondary" address.


Code is below.

Script: /usr/local/bin/create_macvlan_bridge.sh

#!/bin/sh

# Evert Mouw, 2013
# Modified by Marc Ranolfi, 2017-07-24

# ------------
# wait for network availability
# ------------
TESTHOST=kernel.org
while ! ping -q -c 1 $TESTHOST > /dev/null
do
    echo "$0: Cannot ping $TESTHOST, waiting another 5 secs..."
    sleep 5
done

# ------------
# network config
# ------------
HWLINK=enp10s0
MACVLN=macvlan0

IP=192.168.1.3/24
NETWORK=192.168.1.0/24
GATEWAY=192.168.1.1

# ------------
# setting up $MACVLN interface
# ------------
ip link add link $HWLINK $MACVLN type macvlan mode bridge
ip address add $IP dev $MACVLN
ip link set dev $MACVLN up

# ------------
# routing table
# ------------
# empty routes
ip route flush dev $HWLINK
ip route flush dev $MACVLN

# add routes
ip route add $NETWORK dev $MACVLN metric 0

# add the default gateway
ip route add default via $GATEWAY

Systemd unit file: /etc/systemd/system/create_macvlan_bridge.service

[Unit]
Description=Create_macvlan_bridge
Wants=network-online.target
After=network.target network-online.target dhcpcd.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/create_macvlan_bridge.sh

[Install]
WantedBy=multi-user.target

2 Answers

The information detailed in the question's latest update gave me an insight of where the problem resided.

I had dhcpcd.service enabled for all interfaces. I tried disabling it and enabling only for enp10s0, and it worked.

So:

systemctl disable dhcpcd
systemctl enable dhcpcd@enp10s0

and voila.

Correct answer by Marc.2377 on December 29, 2020

I also had problems with the script. I didn't have to do it twice. Things started working again after a minute or so. But that was still annoying. I decided that it makes unnecessary routing changes. There's no reason normal traffic from the host shouldn't use the physical interface. You only need to use macvlan to get to your VMs. For that reason the only routing changes you really need are

ip route flush dev $MACVLN

ip route add $VMHOST dev $MACVLN metric 0

At that point I no longer had any issues.

It's also worth nothing that at least on Centos 7, /sbin/ifup-local gets executed by systemctl restart network at the end of bringing up an interface. So you can put the script there, though it should start with something like this, since you don't want to run it when other interfaces are brought up.

if ! test "$1" = "em1"; then exit 0 fi

You probably also don't need the ping loop when it's /sbin/ifup-local. Indeed it's dangerous since it's an infinite loop, so if there's some network issue the system might not come up with this as part of startup.

Answered by Charles Hedrick on December 29, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP