Updated:

This page contains notes from my various tests and experiments. It is a raw record of what I did, without correction for errors, or later update for things that I learn. Use at your own risk.

More OpenVPN DHCP tweaks

Continuing from yesterday, I want to make a standard install plan for an OpenVPN AWS gateway that pulls its IP address from the local (robot) network.

I successfully rebooted the OpenVPN server, so whatever caused my grief yesterday is gone.

First, let me remove the Cargo Culted stp: false from the netplan file, leaving this for /etc/netplan/90-rosbridge.yaml:

network:
  version: 2
  ethernets:
    tap0:
      match:
        name: "tap0"

  bridges:
    rosbridge:
      mtu: 1500
      interfaces: [tap0]
      dhcp4: true
      dhcp6: false

I’m also going to work on the openvpn config file directly, instead of going through the docker config. Here is the file that was generated by docker config by sudo ovpn_genconfig -u udp://$OPENVPN_URL -t -d -D at /etc/openvpn/openvpn.conf:

server 192.168.255.0 255.255.255.0
verb 3
key /etc/openvpn/pki/private/44.232.53.80.key
ca /etc/openvpn/pki/ca.crt
cert /etc/openvpn/pki/issued/44.232.53.80.crt
dh /etc/openvpn/pki/dh.pem
tls-auth /etc/openvpn/pki/ta.key
key-direction 0
keepalive 10 60
persist-key
persist-tun

proto udp
# Rely on Docker to do port mapping, internally always 1194
port 1194
dev tap0
status /tmp/openvpn-status.log

user nobody
group nogroup
comp-lzo no

### Push Configurations Below
push "comp-lzo no"

Issues to resolve:

  • Stop pushing routes (use route-noexec)

I added that to the conf, then

systemctl restart openvpn@openvpn

On the local system, I connect to openvpn, then add tap0 to the bridge. On the AWS gateway,

root@rosovpn:/etc/openvpn# ip link set down rosbridge
root@rosovpn:/etc/openvpn# ip link set ip rosbridge

resulted in:

root@rosovpn:/etc/openvpn# ip a
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 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP group default qlen 1000
    link/ether 02:fe:c5:16:53:e9 brd ff:ff:ff:ff:ff:ff
    inet 172.31.24.240/20 brd 172.31.31.255 scope global dynamic eth0
       valid_lft 3490sec preferred_lft 3490sec
    inet6 fe80::fe:c5ff:fe16:53e9/64 scope link 
       valid_lft forever preferred_lft forever
3: rosbridge: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 5a:3f:89:62:4d:9d brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.84/24 brd 192.168.0.255 scope global dynamic rosbridge
       valid_lft 7124sec preferred_lft 7124sec
    inet6 fe80::583f:89ff:fe62:4d9d/64 scope link 
       valid_lft forever preferred_lft forever
5: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:88:b2:00:17 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
6: tap0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master rosbridge state UNKNOWN group default qlen 100
    link/ether 5a:3f:89:62:4d:9d brd ff:ff:ff:ff:ff:ff
root@rosovpn:/etc/openvpn# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.31.16.1     0.0.0.0         UG    100    0        0 eth0
0.0.0.0         192.168.0.11    0.0.0.0         UG    100    0        0 rosbridge
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
172.31.16.0     0.0.0.0         255.255.240.0   U     0      0        0 eth0
172.31.16.1     0.0.0.0         255.255.255.255 UH    100    0        0 eth0
192.168.0.0     0.0.0.0         255.255.255.0   U     0      0        0 rosbridge
192.168.0.11    0.0.0.0         255.255.255.255 UH    100    0        0 rosbridge

That is, I have the offending default root. I bet that is coming from netplan and not from openvpn. Can I fix that? I see dhcp4-overrides has use-dns and use-routes options. What is the syntax? I see an example here. Use that:

root@rosovpn:/etc/netplan# cat 90-rosbridge.yaml 
network:
  version: 2
  ethernets:
    tap0:
      match:
        name: "tap0"

  bridges:
    rosbridge:
      mtu: 1500
      interfaces: [tap0]
      parameters:
        stp: false
      dhcp4: true
      dhcp4-overrides:
        use-dns: false
        use-routes: false
      dhcp6: false
root@rosovpn:/etc/netplan# netplan apply
root@rosovpn:/etc/netplan# ip link set down rosbridge
root@rosovpn:/etc/netplan# ip link set up rosbridge
root@rosovpn:/etc/netplan# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.31.16.1     0.0.0.0         UG    100    0        0 eth0
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
172.31.16.0     0.0.0.0         255.255.240.0   U     0      0        0 eth0
172.31.16.1     0.0.0.0         255.255.255.255 UH    100    0        0 eth0
192.168.0.0     0.0.0.0         255.255.255.0   U     0      0        0 rosbridge

So that worked. I can ping the remote rosbridge address from the local.

Next issue: modify the local config to add tap0 to the rosbridge by default. I think I can just do what I did on the remote. First, I confirmed that disconnecting the local from openvpn and reconnecting resulted in tap0 that is not under the rosbridge. Here’s the netplan mod file:

kent@ubutower:/etc/netplan$ cat 90-rosbridge.yaml 
network:
  version: 2
  ethernets:
    bridge-me:
      match:
        name: "*"

  bridges:
    rosbridge:
      mtu: 1500
      interfaces: [bridge-me]
      nameservers:
        addresses: [8.8.8.8, 8.8.4.4]
      parameters:
        stp: false
      dhcp4: true
      dhcp6: false

I would have expected that to work.

Try connecting, then sudo netplan apply. Nope.

Try rosbridge down/up. Nope.

Try tap0 down/up. Nope.

I’m going to try a more specific netplan file:

network:
  version: 2
  ethernets:
    estar:
      match:
        name: "e*"
    tap0:
      match:
        name: "tap0"

  bridges:
    rosbridge:
      mtu: 1500
      interfaces: [estar, tap0]
      nameservers:
        addresses: [8.8.8.8, 8.8.4.4]
      dhcp4: true
      dhcp6: false

I notice after netplan apply there is still an old bridge-me file in /etc/NetworkManager/system-connections. Remove it. I see the expected files under /run/NetworkManager/system-connections

Connect openvpn. Still tap0 not under bridge. Down/up tap0, no change.

OK, let’s use openvpn options to add tap0 to the bridge. Remove the tap0 stuff from the netplan file.

kent@ubutower:/etc/netplan$ cat 90-rosbridge.yaml 
network:
  version: 2
  ethernets:
    estar:
      match:
        name: "e*"

  bridges:
    rosbridge:
      mtu: 1500
      interfaces: [estar]
      nameservers:
        addresses: [8.8.8.8, 8.8.4.4]
      dhcp4: true
      dhcp6: false

Add to bottom of ros_local_gateway.ovpn:

script-security 2
up "/bin/bash -c 'ip link set master rosbridge dev tap0'"

Yep that works, and I can ping the remote bridge ip address.

I am still getting a unused address on tap0 though:

root@ubutower:~# ip a show tap0
```bash
14: tap0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master rosbridge state UNKNOWN group default qlen 100
    link/ether 1a:52:25:3a:d1:e5 brd ff:ff:ff:ff:ff:ff
    inet 192.168.255.2/24 brd 192.168.255.255 scope global tap0
       valid_lft forever preferred_lft forever
    inet6 fe80::1852:25ff:fe3a:d1e5/64 scope link 
       valid_lft forever preferred_lft forever

Try adding ifconfig-noexec to the local .ovpn as well. Ping to remote did not work, tap0 is down. Bring it up, Ping did not work, tried a few seconds later now it works. Need to add that to .ovpn Now lines at end are:

script-security 2
up "/bin/bash -c 'ip link set master rosbridge dev tap0 && ip link set up tap0'"
ifconfig-noexec

Ping the remote at 192.168.0.84. Took a few tries, but eventually it started working after maybe 10 seconds.

I connected tap0 to Wireshark. It took about 18 seconds until an ARP request went out, once that happened Ping started working. I also noticed two things: 1) the MAC address of the local rosbridge and ethernet adapter are the same, and 2) there is unneeded STP traffic every 2 seconds. Perhaps I should turn off STP.

I am going to try to create the local ROS bridge with its own MAC address. I used a website to get a uuid: c5f6a996-eb51-4277-b79f-0e7f2c2b41a5. I want a private MAC address, which means the second digit must be 2,6,a,, or e. So I will use 0e:7f:2c:2b:41:a5 as MAC address.

Yeah that works, but did not change the startup delay. I tried simply waiting 30 seconds after connect before trying the ping, that worked. So it just takes some time for the network to decide to work. I deleted the mac address from the bridge, tried the same thing, same result. So I don’t see any reason to set the mac address of the bridge. Actually Googling I find this where the answer says that the duplicated mac address is by design, so I will leave that.

On the delay, I tried ip neigh flush all no help.

So who should add tap0 to the bridge? On the AWS remote, the netplan config is doing it. On the local, I added an up command in openvpn. I think that the up approach is likely to be more reliable (and may be needed for other reasons). So let me use that in both places.

Back to the AWS remote, I modified netplan file root@rosovpn:/etc/netplan# cat 90-rosbridge.yaml:

network:
  version: 2

  bridges:
    rosbridge:
      mtu: 1500
      parameters:
        stp: false
      dhcp4: true
      dhcp4-overrides:
        use-dns: false
        use-routes: false
      dhcp6: false

I’m going to reconfigure OpenVPN using docker scripts. Took me awhile to get the quotes right, but this works:

OPENVPN_URL=$(curl icanhazip.com)
UP_COMMAND="/bin/bash -c 'ip link set master rosbridge dev tap0 && ip link set up tap0'"
sudo ovpn_genconfig -u udp://$OPENVPN_URL -t -d -D \
  -e "up \"${UP_COMMAND}\"" \
  -e "script-security 2" \
  -e "ifconfig-noexec"

but there is left over stuff from previous attempts. If I delete /etc/openvpn/ovpn_env.sh first, and restart the su session, junk is gone.

I start (or restart) openvpn with sudo systemctl start openvpn@openvpn. tap0 exists and is under the bridge. There are no routes.

Back at the local, we still have:

root@ubutower:~# cat /etc/netplan/90-rosbridge.yaml 
network:
  version: 2
  ethernets:
    estar:
      match:
        name: "e*"

  bridges:
    rosbridge:
      mtu: 1500
      interfaces: [estar]
      parameters:
        stp: false
      dhcp4: true
      dhcp6: false

Connecting. Back on the remote, rosbridge has no IP address. But wait, after some time it got it with no actiion from me! Ping from local to remote bridge address also works. I think this is all working. Let me summarize everybody.

Local:

root@ubutower:~# cat /etc/netplan/90-rosbridge.yaml 
network:
  version: 2
  ethernets:
    estar:
      match:
        name: "e*"

  bridges:
    rosbridge:
      mtu: 1500
      interfaces: [estar]
      parameters:
        stp: false
      dhcp4: true
      dhcp6: false

Added to .ovpn:

script-security 2
up "/bin/bash -c 'ip link set master rosbridge dev tap0 && ip link set up tap0'"
ifconfig-noexec

On AWS remote:

root@rosovpn:~# cat /etc/netplan/90-rosbridge.yaml 
network:
  version: 2

  bridges:
    rosbridge:
      mtu: 1500
      parameters:
        stp: false
      dhcp4: true
      dhcp4-overrides:
        use-dns: false
        use-routes: false
      dhcp6: false

Command to configure openvpn using docker scripts:

OPENVPN_URL=$(curl icanhazip.com)
UP_COMMAND="/bin/bash -c 'ip link set master rosbridge dev tap0 && ip link set up tap0'"
sudo ovpn_genconfig -u udp://$OPENVPN_URL -t -d -D \
  -e "up \"${UP_COMMAND}\"" \
  -e "script-security 2" \
  -e "ifconfig-noexec"

Now I am going to reboot both systems and try this again. Should only have to reconnect openvpn. Yep, that worked.

How about ROS2? On the local:

ros2 run demo_nodes_cpp talker

On the remote, create the cyclonedds config file and run:

ubuntu@rosovpn:~$ cat cyclonedds.xml 
<?xml version="1.0" encoding="UTF-8" ?>
  <CycloneDDS xmlns="https://cdds.io/config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://cdds.io/config https://raw.githubusercontent.com/eclipse-cyclonedds/cyclonedds/master/etc/cyclonedds.xsd">
    <Domain id="any">
        <General>
            <NetworkInterfaceAddress>rosbridge</NetworkInterfaceAddress>
        </General>
    </Domain>
  </CycloneDDS>
ubuntu@rosovpn:~$ CYCLONEDDS_URI=file://$PWD/cyclonedds.xml ros2 run demo_nodes_cpp listener
[INFO] [1635972343.097352004] [listener]: I heard: [Hello World: 1]
[INFO] [1635972344.097346401] [listener]: I heard: [Hello World: 2]

Works!

I want to automate the client extensions. The ovpn_getclient script uses an array variable OVPN_EXTRA_CLIENT_CONFIG got this. This in turn is set with the -E option in config. Try that. That is, run:

sudo rm /etc/openvpn/ovpn_env.sh
OPENVPN_URL=$(curl icanhazip.com)
UP_COMMAND="/bin/bash -c 'ip link set master rosbridge dev tap0 && ip link set up tap0'"
sudo ovpn_genconfig -u udp://$OPENVPN_URL -t -d -D \
  -e "ifconfig-noexec" \
  -e "script-security 2" \
  -e "up \"${UP_COMMAND}\"" \
  -E "ifconfig-noexec" \
  -E "script-security 2" \
  -E "up \"${UP_COMMAND}\""
sudo ovpn_getclient ros_local_gateway > ros_local_gateway.ovpn

Yep that worked. I think that I have all of the pieces for a summary post now.