Networking

How to generate random IPv6 addresses in a given network using Python

This code generates random IPv6 addresses in a given network using Python’s ipaddress module:

import ipaddress
import random

def random_ipv6_addr(network):
    """
    Generate a random IPv6 address in the given network
    Example: random_ipv6_addr("fd66:6cbb:8c10::/48")
    Returns an IPv6Address object.
    """
    net = ipaddress.IPv6Network(network)
    # Which of the network.num_addresses we want to select?
    addr_no = random.randint(0, net.num_addresses)
    # Create the random address by converting to a 128-bit integer, adding addr_no and converting back
    network_int = int.from_bytes(net.network_address.packed, byteorder="big")
    addr_int = network_int + addr_no
    addr = ipaddress.IPv6Address(addr_int.to_bytes(16, byteorder="big"))
    return addr

# Usage example
print(random_ipv6_addr("fdce:4879:a1e9::/48"))
# Prints e.g. fdce:4879:a1e9:e351:1a01:be9:4d9a:157d

It works by first converting the IPv6 network address to binary and then adding a random host number. After that, it will be converted back to an IPv6Address object.

Posted by Uli Köhler in Networking, Python

How to get hostmask/netmask for given prefix length in Python

In order to get the host mask for e.g. a /112 IPv6 prefix, use:

import ipaddress
# Get netmask for a /112 prefix
ipaddress.IPv6Network("::/112").netmask

# Get host mask for a /112 prefix
ipaddress.IPv6Network("::/112").hostmask

 

Posted by Uli Köhler in Networking, Python

Bitwise operation with IPv6 addresses and networks in Python

Python3 features the easy-to-use ipaddress library providing many calculations. However, bitwise boolean operators are not available for addresses.

This post shows you how to perform bitwise operations with IPv6Address() objects. We’ll use the following strategy:

  1. Use .packed to get a binary bytes() instance of the IP address
  2. Use int.from_bytes() to acquire an integer representing the binary address
  3. Perform bitwise operations with said integer
  4. Use result.to_bytes(16, ...) to convert back the integer to a bytes() array in the correct byte order
  5. Construct an IPv6Address() object from the resulting byte array.

Python code:

import ipaddress

def bitwise_and_ipv6(addr1, addr2):
    result_int = int.from_bytes(addr1.packed, byteorder="big") & int.from_bytes(addr2.packed, byteorder="big")
    return ipaddress.IPv6Address(result_int.to_bytes(16, byteorder="big"))

def bitwise_or_ipv6(addr1, addr2):
    result_int = int.from_bytes(addr1.packed, byteorder="big") | int.from_bytes(addr2.packed, byteorder="big")
    return ipaddress.IPv6Address(result_int.to_bytes(16, byteorder="big"))

def bitwise_xor_ipv6(addr1, addr2):
    result_int = int.from_bytes(addr1.packed, byteorder="big") ^ int.from_bytes(addr2.packed, byteorder="big")
    return ipaddress.IPv6Address(result_int.to_bytes(16, byteorder="big"))

Example usage:

a = ipaddress.IPv6Address('2001:16b8:2703:8835:9ec7:a6ff:febe:96b1')
b = ipaddress.IPv6Address('2001:16b8:2703:4241:9ec7:a6ff:febe:96b1')

print(bitwise_and_ipv6(a, b)) # IPv6Address('2001:16b8:2703:1:9ec7:a6ff:febe:96b1')
print(bitwise_or_ipv6(a, b)) # IPv6Address('2001:16b8:2703:ca75:9ec7:a6ff:febe:96b1')
print(bitwise_xor_ipv6(a, b)) # IPv6Address('0:0:0:ca74::')

Similarly, you can use the code in order to manipulate IPv6Network() instances:

a = ipaddress.IPv6Network('2001:16b8:2703:8835:9ec7:a6ff:febe::/112')
b = ipaddress.IPv6Network('2001:16b8:2703:4241:9ec7:a6ff:febe::/112')

print(bitwise_and_ipv6(a.network_address, b.network_address)) # IPv6Address('2001:16b8:2703:1:9ec7:a6ff:febe:0')
print(bitwise_or_ipv6(a.network_address, b.network_address)) # IPv6Address('2001:16b8:2703:ca75:9ec7:a6ff:febe:0')
print(bitwise_xor_ipv6(a.network_address, b.network_address)) # IPv6Address('0:0:0:ca74::')

Note that the return type will always be IPv6Address() and never IPv6Network() since the result of the bitwise operation doesn’t have any netmask associated with it.

Besides .network_address you can also use other properties of IPv6Address() instances like .broadcast_address or .hostmask or .netmask.

Posted by Uli Köhler in Networking, Python

How to fix OpenVPN “TLS Error: cannot locate HMAC in incoming packet from …”

Problem:

Your OpenVPN clients can’t connect to your OpenVPN server and the server log shows an error message like

TLS Error: cannot locate HMAC in incoming packet from [AF_INET6]::ffff:187.100.14.13:41874 (via ::ffff:25.16.25.29%xn0)

Solution:

You have enabled a TLS key (tls-auth option) in your OpenVPN configuration, but your client does not know that it should use the additional layer of authentication.

The server is looking for the HMAC in the incoming packets but can’t find it.

Either disable the tls-auth option in your server config. The config line will look like

tls-auth /var/etc/openvpn/server2.tls-auth 0

or

Enable the correct tls-auth configuration in your client. Remember that you also need to share the correct key.

Posted by Uli Köhler in Networking, OpenVPN, VPN

How to stop vpnc IPSec VPNs and close the tunnel interface

Problem:

You have started a VPN connection using vpnc, e.g.

$ sudo vpnc my-vpn.conf
VPNC started in background (pid: 21763)...

but you can’t find any information on how to stop vpnc i.e. terminating the VPN connection.

Solution:

Preferred method: Use vpnc-disconnect

Run

sudo vpnc-disconnect

This will, for example, print

Terminating vpnc daemon (pid: 21763)

vpnc-disconnect is the official method of stopping vpnc and will terminate the vpnc instance whose PID is written in /var/run/vpnc.pid. In other words, it will not work properly if you have multiple vpnc instances running at the same time, or if you have specified an alternate PID file for vpnc (e.g. using vpnc --pid-file /var/run/my-vpnc.pid my-vpn.conf).

Alternate method 1: Stop all vpnc instances on the current machine

You can kill all vpnc instances on the current machine using

sudo killall vpnc

however this will stop all vpnc instances on the current machine. In case you have multiple vpnc VPN instances active concurrently, this means all of them will be terminated.

Alternate method 2: Kill a specific vpnc (if you know it’s PID)

vpnc tells you its process ID when starting it. In our example above:

VPNC started in background (pid: 21763)...

the PID is 21763 so we can kill the process using

sudo kill 21763

This will cleanly stop vpnc and remove the tunnel interface.

Alternate method 3: Kill a specific vpnc (if you don’t know it’s PID)

Show all running vpnc instances using

pgrep -a vpnc

This will show you, for example,

21763 vpnc my-vpn.conf
30792 vpnc other-vpn.conf

In that list, find the line with the vpnc instance you want to kill (you can identify it by the config file name, e.g. my-vpn.conf – in this example, it would be the first line).

The number at the beginning of the line is the PID of that vpnc process. Copy it and run

sudo kill [PID]

e.g.

sudo kill 21763

just like in Alternate method 2. This will only stop that specific vpnc instance and leave all others running.

Posted by Uli Köhler in Networking

How to make your own ‘get my current IP address’ server using only nginx

This nginx config will return the user’s IP address to the user. Note that it won’t work behind a reverse proxy, it actually needs to listen on port 80 and/or 443 of the IP address of the domain it is served as.

location = /api/get-my-ip {
    add_header 'Access-Control-Allow-Origin' '*' always;
    add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
    add_header 'Content-Type' 'text/plain charset=UTF-8';
    return 200 '$remote_addr';
}

 

This is a JSON-returning variant:

location = /api/get-my-ip-json {
    add_header 'Access-Control-Allow-Origin' '*' always;
    add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS';
    add_header 'Content-Type' 'text/plain charset=UTF-8';
    return 200 '{"ip": "$remote_addr"}';
}

This configuration is available online, for free, at techoverflow.net: See How to get your current IPv4 address using wget and How to get your current IPv6 address using wget

Posted by Uli Köhler in Networking, nginx

How to get your current IPv6 address using wget

Also see How to get your current IPv4 address using wget

Use

wget -qO- https://ipv6.techoverflow.net/api/get-my-ip

which uses the TechOverflow get-my-ip API.

Example:

$ wget -qO- https://ipv6.techoverflow.net/api/get-my-ip
2003:ea:70a:d600:ee98:42ca:b0bb:ac75

If your computer does not have IPv6 connectivity enabled, you will not see any output.

Prefer a JSON response?

Use https://ipv6.techoverflow.net/api/get-my-ip-json instead:

wget -qO- https://ipv6.techoverflow.net/api/get-my-ip-json

Example:

$ wget -qO- https://ipv6.techoverflow.net/api/get-my-ip-json
{"ip": "2003:ea:70a:d600:ee98:42ca:b0bb:ac75"}

Want this API on your own server? Check out How to make your own ‘get my current IP address’ server using only nginx

Posted by Uli Köhler in APIs, Networking

How to get your current IPv4 address using wget

Also see How to get your current IPv6 address using wget

Use

wget -qO- https://ipv4.techoverflow.net/api/get-my-ip

which uses the TechOverflow get-my-ip API.

Example:

$ wget -qO- https://ipv4.techoverflow.net/api/get-my-ip
80.138.203.18

Prefer a JSON response?

Use https://ipv4.techoverflow.net/api/get-my-ip-json instead:

wget -qO- https://ipv4.techoverflow.net/api/get-my-ip-json

Example:

$ wget -qO- https://ipv4.techoverflow.net/api/get-my-ip-json
{"ip": "80.138.201.17"}

Want this API on your own server? Check out How to make your own ‘get my current IP address’ server using only nginx

Posted by Uli Köhler in APIs, Networking

How to fix mount: unknown filesystem type ‘smbfs’

Problem:

When you’re trying to mount a Windows network share using a command like

sudo mount -t smbfs //Asus/store_n_go /mnt/

you see this error message:

mount: unknown filesystem type 'smbfs'

Solution:

First ensure samba is installed

sudo apt install samba

then try again using cifs as filesystem type instead of smbfs:

sudo mount -t cifs //Asus/store_n_go /mnt/

 

Posted by Uli Köhler in Linux, Networking

Fixing ‘netplan apply’ Failed to start NetworkManager.service: Unit NetworkManager.service not found.

Problem:

You’ve configured a wifi or similar (non-ethernet) network in netplan. Your netplan configuration (e.g. in /etc/netplan/50-cloud-init.yaml) looks similar to this:

network:
    ethernets:
        enp0s25:
            addresses: []
            dhcp4: true
    wifis:
        wlxc04a0013c4ca:
            renderer: NetworkManager
            match: {}
            dhcp4: true
            access-points:
                MyWifi:
                    password: "mywifipassword"
    version: 2

 

But when you run

sudo netplan apply

you see an error message like this:

Failed to start NetworkManager.service: Unit NetworkManager.service not found.
Traceback (most recent call last):
  File "/usr/sbin/netplan", line 23, in <module>
    netplan.main()
  File "/usr/share/netplan/netplan/cli/core.py", line 50, in main
    self.run_command()
  File "/usr/share/netplan/netplan/cli/utils.py", line 130, in run_command
    self.func()
  File "/usr/share/netplan/netplan/cli/commands/apply.py", line 41, in run
    self.run_command()
  File "/usr/share/netplan/netplan/cli/utils.py", line 130, in run_command
    self.func()
  File "/usr/share/netplan/netplan/cli/commands/apply.py", line 101, in command_apply
    utils.systemctl_network_manager('start', sync=sync)
  File "/usr/share/netplan/netplan/cli/utils.py", line 68, in systemctl_network_manager
    subprocess.check_call(command)
  File "/usr/lib/python3.6/subprocess.py", line 291, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['systemctl', 'start', '--no-block', 'NetworkManager.service']' returned non-zero exit status 5.

Solution:

The renderer: NetworkManager line tells netplan to use NetworkManager to connect to this network.

The error message tells you that NetworkManager is not installed on your system.

On Ubuntu and Debian, use

sudo apt install network-manager

to install it. On other distributions, try to install network-manager or a similarly named package using your distribution’s package manager.

After than, run

sudo netplan apply

again.

Posted by Uli Köhler in Linux, Networking

How to SSH to an IPv6 address

If your IPv6 address begins with fe80::

This type of IPv6 address is called link-local and is therefore specific to a network interface on your computer. You can use ifconfig to show information about the network interfaces. You are looking for an identifer like eth0, wlan0, enp3s0, wlp4s0 or tap1. For this example we’re using eth0.

Now you can connect to the IPv6 using:

ssh <username>@<ipv6 address>%<interface>

for example

ssh user@fe80::21b:21ff:fe22:e865%eth0

Replace <interface> by the correct interface (if you don’t know, try out every interface), replace <ipv6 address> by the correct IP address and replace <user> by the correct username.

If your IPv6 address does NOT begin with fe80::

You can just use

ssh <username>@<ipv6 address>

for example

ssh uli@2a01:4f9:c010:278::1

Replace <ipv6 address> by the correct IP address and replace <user> by the correct username.

Posted by Uli Köhler in Linux, Networking

Routing public IPv6 addresses to your lxc/lxd containers

The enormous amount of IPv6 addresses available to most commercially hosted VPS / root servers with a public IPv6 prefix allows you to route a public IPv6 address to every container that is running on your server. This tutorial shows you how to do that, even if you have no prior experience with routing,

Step 0: Create your LXC container

We assume you have already done this – just for reference, here’s how you can create a container:

lxc launch ubuntu:18.04 my-container

Step 1: Which IP address do you want to assign to your container?

First you need to find out what prefix is routed to your host. Usually you can do that by checking in your provider’s control panel. You’re looking for something like 2a01:4f9:c010:278::1/64. Another option would be to run sudo ifconfig

and look for a inet6 line in the section of your primary network interface (this only works if you have configured your server to have an IPv6 address). Note that addresses that start with fe80:: and addresses starting with fd, among others, are not public IPv6 addresses.

Then you can define a new IPv6 address to your container. Which one you choose – as long as it’s within the prefix – is entirely your decision.

Often, <prefix>::1 is used for the host itself, therefore you could, for example, choose <prefix>::2. Note that some providers use some IP addresses for other purposes. Check your provider’s documentation for details.

If you don’t want to make it easy to find your container’s public IPv6, don’t choose <prefix>::1<prefix>::2<prefix>::3 etc but something more random like <prefix>:af15:99b1:0b05:1, for example2a01:4f9:c010:278:af15:99b1:0b05:0001. Ensure your IPv6 address has 8 groups of 4 hex digits each!

For this example, we choose the IPv6 address 2a01:4f9:c010:278::8.

Step 2: Find out the ULA of your container

We need to find the ULA (unique local address – similar to a private IPv4 address which is not routed on the internet) of the container. Using lxc, this is quite easy:

uli@myserver:~$ lxc list
+--------------+---------+-----------------------+-----------------------------------------------+
|     NAME     |  STATE  |         IPV4          |                     IPV6                      |
+--------------+---------+-----------------------+-----------------------------------------------+
| my-container | RUNNING | 10.144.118.232 (eth0) | fd42:830b:36dc:3691:216:3eff:fed1:9058 (eth0) |
+--------------+---------+-----------------------+-----------------------------------------------+

You need to look in the IPv6 column and copy the address listed there. In this example, the address is fd42:830b:36dc:3691:216:3eff:fed1:9058.

Step 3: Setup IPv6 routing

Now we can tell the host Linux to route your chosen public IPv6 to the container’s private IPv6. This is quite easy:

sudo ip6tables -t nat -A PREROUTING -d <public IPv6> -j DNAT --to-destination <container private IPv6>

In our example, this would be

sudo ip6tables -t nat -A PREROUTING -d 2a01:4f9:c010:278::8 -j DNAT --to-destination fd42:830b:36dc:3691:216:3eff:fed1:9058

First, test the command by running it in a shell. If it works (i.e. if it doesn’t print any error message), you can permanently store it e.g. by adding it to /etc/rc.local (after #!/bin/bash, before exit 0). Advanced users should prefer to add it to /etc/network/interfaces.

Step 4: Connect to your container using SSH on your public IPv6 (optional)

Note: This step requires that you have working IPv6 connectivity at your local computer. If you are unsure, check at ipv6-test.com

First, open a shell on your container:

lxc exec my-container bash

After running this, you should see a root shell prompt inside your container:

root@my-container:~#

The following commands should be entered in the container shell, not the host!

Now we can create a user to login to (in this example, we create the uli user):

root@my-container:~# adduser uli
Adding user `uli' ...
Adding new group `uli' (1001) ...
Adding new user `uli' (1001) with group `uli' ...
Creating home directory `/home/uli' ...
Copying files from `/etc/skel' ...
Enter new UNIX password: 
Retype new UNIX password: 
passwd: password updated successfully
Changing the user information for uli
Enter the new value, or press ENTER for the default
        Full Name []: 
        Room Number []: 
        Work Phone []: 
        Home Phone []: 
        Other []: 
Is the information correct? [Y/n]

You only need to enter a password (you won’t see anything on screen when entering it) twice, for all other lines you can just press enter.

The ubuntu:18.04 lxc image used in this example does not allow SSH password authentication in its default configuration. In order to fix this, change PasswordAuthentication no to PasswordAuthentication yes in /etc/ssh/sshd_config and restart the SSH server by running service sshd restart. Be sure you understand the security implications before you do that!

Now, logout of your container shell by pressing Ctrl+D. The following commands can be entered on your desktop or any other server with IPv6 connectivity.

Now login to your server:

ssh <username>@<public IPv6 address>

in this example:

ssh uli@2a01:4f9:c010:278::8

If you configured everything correctly, you’ll see the shell prompt for your container:

uli@my-container:~$

Note: Don’t forget to configure a firewall for your container, e.g. ufw! Your container’s IPv6 is exposed to the internet and just assuming noone will guess it is not good security practice.

Posted by Uli Köhler in Cloud, Container, Linux, LXC, Networking