Ansible Automation on Ubuntu 20.04 in GNS3

Ansible is a useful tool for automating tasks on various systems, including servers and network devices. It can be a little tricky to set up, but it’s ok once you get the hang of it. Today we’ll just try to get ansible installed on an Ubuntu 20.04 server and able to connect to another Ubuntu server, a Rocky Linux server, and a Cisco IOSv router.


Ansible in GNS3

Our simple subnet of will hold a Ubuntu 20.04 server at the top at acting as the ansible controller. The others will all be managed nodes, they don’t have ansible installed. Ansible uses python3 on Linux servers to execute various commands, and it’s not installed by default on Rocky Linux 8.5, so I installed it. I also added a firewall to the Rocky node to allow SSH in, which is how ansible connects to nodes. The SSH server has been enabled on the Cisco IOSv router as well.


Following ansible’s official installation instructions, this quick script will get it installed. Comments are inline:

#Create apt file for ansible
touch /etc/apt/sources.list.d/ansible.list

#Add ppa to above file
echo '
deb focal main
' >> /etc/apt/sources.list.d/ansible.list

#Add key for ppa
apt-key adv --keyserver --recv-keys 93C4A3FD7BB9C367

#Update and install
apt-get update
apt-get install ansible

Ansible should now be installed!

Create inventory

The next thing to do is create an inventory file. Ansible keeps it’s config files in /etc/ansible, so we’ll start by writing a config to /etc/ansible/hosts. This file supports multiple formats, we’ll use yaml because I like yaml (don’t judge me!). This simple script (it’s just an echo command and redirect) will set up the hosts file:

echo '

    ansible_python_interpreter: /usr/bin/python3  #specify python3 for ubuntu and rocky

      ansible_network_os: cisco.ios.ios                     #OS is Cisco IOS!
      ansible_connection: ansible.netcommon.network_cli     #connect to IOS via CLI, no python
      ssh_args: -oKexAlgorithms=+diffie-hellman-group1-sha1 #key exchange algorithm to support older IOS

' > /etc/ansible/hosts

Verify inventory connections

The below command will check the integrity of your inventory (hosts) file:

ansible-inventory --list -y

Then once you have worked out any inventory file issues, “ping” your hosts. This is not an ICMP echo-request/echo-reply (commonly known as ping), ansible will actually try to log in to each host via SSH. Ansible assumes you have SSH public/private key pair authentication enabled, but since this is in a test environment in GNS3 we’ll just pass the user and password to ansible on the command line:

ansible all -m ping --extra-vars "ansible_user=james ansible_password=james"

ubuntu | SUCCESS => {
    "changed": false,
    "ping": "pong"
rocky | SUCCESS => {
    "changed": false,
    "ping": "pong"
cisco | SUCCESS => {
    "changed": false,
    "ping": "pong"

With this, we’ve verified that we have configured our hosts and can properly connect to them from the controller.

Next we’ll try to make some changes with playbooks! Hope you liked this one.

Nagios Core – NCPA Agent on Ubuntu and Rocky Linux

Last time, we installed Nagios Core on Ubuntu Server 20.04. We saw how to add hosts and do a ping check to monitor network connectivity. Today we’ll learn how to check CPU, memory and processes. Nagios provides a free agent to do this that we can install on most standard Linux distributions (Ubuntu, Redhat, Debian, Amazon, etc) as well as Windows and MacOS.

This agent can monitor various services on its host machine, has a REST API and a nice web interface. It replaces the NRPE agent that came before it.


Topology in GNS3

Same as the previous post, our Ubuntu server at will monitor the other two hosts, Rocky Linux and Cisco IOSv. We’ll be installing the NCPA agent on Rocky Linux 8.5 at


On Rocky Linux 8.5, installation is pretty quick using the official Nagios repository. We’ll add the repository and install the package:

rpm -Uvh
yum install ncpa -y

Then we need to allow incoming connections through the firewall on tcp port 5693, which is the default port that NCPA uses:

firewall-cmd --zone=public --add-port=5693/tcp --permanent
firewall-cmd --reload

If everything is installed correctly, you should be able to reach the NCPA web interface using the Rocky Linux server’s IP address. Make you use https and port 5693 –> https://<ip address or fqdn>:5693. The default password is “mytoken” which you’ll want to change via the configuration file before putting this in production. But for now it should look like this:

NCPA Web Interface

Nagios Core Server Configuration

Heading back to the Ubuntu server where Nagios Core is installed, we’ll configure new services for “rocky” in /usr/local/nagios/etc/objects/hosts.cfg (we configured the host for “rocky” last time). The following script will set up new services to check via the NCPA agent on the Rocky server:

echo "

define service {
    host_name               rocky
    service_description     CPU Usage
    check_command           check_ncpa!-t 'mytoken' -P 5693 -M cpu/percent -w 20 -c 40 -q 'aggregate=avg'
    max_check_attempts      5
    check_interval          5
    retry_interval          1
    check_period            24x7
    notification_interval   60
    notification_period     24x7
    contacts                nagiosadmin
    register                1

define service {
    host_name               rocky
    service_description     Memory Usage
    check_command           check_ncpa!-t 'mytoken' -P 5693 -M memory/virtual -w 50 -c 80 -u G
    max_check_attempts      5
    check_interval          5
    retry_interval          1
    check_period            24x7
    notification_interval   60
    notification_period     24x7
    contacts                nagiosadmin
    register                1

define service {
    host_name               rocky
    service_description     Process Count
    check_command           check_ncpa!-t 'mytoken' -P 5693 -M processes -w 150 -c 200
    max_check_attempts      5
    check_interval          5
    retry_interval          1
    check_period            24x7
    notification_interval   60
    notification_period     24x7
    contacts                nagiosadmin
    register                1

" >> /usr/local/nagios/etc/objects/hosts.cfg

Then we need to download from the Nagios github website into /usr/local/nagios/libexec which is the directory where Nagios check scripts go. is a script that performs checks on the NCPA agent. This will install it:

wget --no-check-certificate -P /usr/local/nagios/libexec/
chmod 755 /usr/local/nagios/libexec/ #Make accessible and executable
sed -i 's/python/python3/g' /usr/local/nagios/libexec/ #change 'python' to 'python3'

Then create a command in /usr/local/nagios/etc/objects/commands.cfg that defines the “check_ncpa” command:

echo "

define command {
    command_name    check_ncpa
    command_line    \$USER1\$/ -H \$HOSTADDRESS\$ \$ARG1\$

" >> /usr/local/nagios/etc/objects/commands.cfg

Now reload nagios:

systemctl restart nagios

Remember, if you have any troubles, this command will probably help you out. It’s Nagios Core’s tool to check your config:

/usr/local/nagios/bin/nagios -v /usr/local/nagios/etc/nagios.cfg

Now that we have the new services loaded, let’s see how they show up in the Nagios web interface.

Nagios Core Web Interafce

We should be able to see that “CPU Usage”, “Memory Usage” and “Process Count” are now showing up as services by going to “Hosts” (left nav pane) –> rocky (click on name) –> “View Status Detail For This Host”.

Hope you liked!

Nagios Core 4.4.6 Monitoring on Ubuntu 20.04

Nagios is a time-tested network monitoring tool that network engineers and systems administrators alike have used since its creation in 1999 to monitor networks and alert engineers if something goes wrong. At some point around 2009, Nagios became Nagios Core so that the company could release some more products. Nagios Core remains free and open source, though.

It has many ways to monitor various network nodes, including ping checks, agents and SNMP. Today, we’ll see if we can just get the server and web interface installed, along with a couple basic ping checks.

Let’s get started!


Topology in GNS3

We’ll monitor across a simple subnet of We’ll install Nagios Core on Ubuntu server at the top, and monitor the Rocky Linux and Cisco IOSv router at the bottom.


While it’s possible to install from the standard Ubuntu repositories, that version is very old and doesn’t seem to work very well out of the box. Building the most recent version from source works the best.

There are a number of steps, so I will include comments inline about what commands we’re entering. To install:

#Install necessary libraries
apt-get update
apt-get install -y autoconf gcc libc6 make wget unzip apache2 php libapache2-mod-php7.4 libgd-dev

#Download Nagios Core
cd /tmp
wget -O nagioscore.tar.gz

#Extract and enter directory
tar xzf nagioscore.tar.gz

cd /tmp/nagioscore-nagios-4.4.6/
./configure --with-httpd-conf=/etc/apache2/sites-enabled
make all

#Set up users
make install-groups-users
usermod -a -G nagios www-data

make install
make install-daemoninit
make install-commandmode

#Install a sample script config
make install-config

#Install Apache config files
sudo make install-webconf
sudo a2enmod rewrite
sudo a2enmod cgi

#Create a user account for nagios
sudo htpasswd -c /usr/local/nagios/etc/htpasswd.users nagiosadmin

#Restart apache
systemctl restart apache

#Prepare for plugins installation. Need plugins to do anything at all with nagios
sudo apt-get install -y autoconf gcc libc6 libmcrypt-dev make libssl-dev wget bc gawk dc build-essential snmp libnet-snmp-perl gettext

#Download and extract plugins
cd /tmp
wget --no-check-certificate -O nagios-plugins.tar.gz
tar zxf nagios-plugins.tar.gz

#Compile plugins and install
cd /tmp/nagios-plugins-release-2.3.3/
make install

That was a lot! Hopefully you got it installed ok. The nagios web interface can be accessed from http://<fqdn or ip>/nagios. It should look something like this:

Nagios Web Interface

Now that we have it installed, it’s time to set up some monitoring. You’d think this could be done via the web interface but it cannot. It needs to be done from nagios config files.

Monitoring Configuration

There are a number of configuration files, they should all be located in /usr/local/nagios/etc/objects. The nagios program itself is located at /usr/local/nagios/bin/nagios, but the installation process registered a service with systemd, so we’ll mostly be using that. We need to create “hosts”, which are other severs, workstations or routers to monitor. We’ll run this script, I have commented in-line:

#Edit the main nagios config file at /usr/local/nagios/etc/nagios.cfg, add line to add a config file called "hosts.cfg"
echo "cfg_file=/usr/local/nagios/etc/objects/hosts.cfg" >> /usr/local/nagios/etc/nagios.cfg

#Write a config to ping hosts
echo "

define host{
    host_name                       rocky
    alias                           rocky
    check_command                   check-host-alive
    max_check_attempts              5
    check_period                    24x7
    notification_interval           30
    notification_period             24x7
define host{
    host_name                       cisco_iosv
    alias                           cisco_iosv
    check_command                   check-host-alive
    max_check_attempts              5
    check_period                    24x7
    notification_interval           30
    notification_period             24x7

" >> /usr/local/nagios/etc/objects/hosts.cfg

Before you restart nagios and apply the configuration, you can check to see if there’s any errors using a built-in tool that nagios has:

/usr/local/nagios/bin/nagios -v /usr/local/nagios/etc/nagios.cfg
<Edited for brevity>

Total Warnings: 2
Total Errors:   0

Things look okay - No serious problems were detected during the pre-flight check

If there’s any errors, it should give you some advice on how to fix them. Otherwise, restart nagios:

systemctl restart nagios

After the checks run, you should be seeing that your hosts are up by click on “Hosts” on the nav bar to the left:

Nagios is a sizeable piece of software and very extensible. There are many things you can do with it far beyond what we’ve done here. However we’ve gotten the core functionality working – pinging stuff.

We’ll take a look at how to do more than that in the next post. Stay tuned!

Syslog Server on Ubuntu 20.04

Running a syslog server that can collect logs from various devices on your network is really simple with Ubuntu Server 20.04. Using built-in software Rsyslog, you can quickly configure it to be either a syslog client or a server. Since most network devices have the capability to send logs to an external server, you can quickly set up your Ubuntu server act as a central log collection point.

What many folks don’t know is that syslog is actually a standard application-layer network protocol, not just software. It is defined in RFC 5424. It’s because of this standard protocol that network devices and servers alike are able to easily send and store logs. Without a standard protocol, it would be much more difficult to pull that off.

Let’s set up syslog on Ubuntu 20.04!


Topology in GNS3

The Ubuntu server at will act as our syslog server while the other Ubuntu server and Cisco router will act as clients, sending their logs to the server.

Server Configuration

Since Rsyslog is already installed on Ubuntu (and others), there’s no installation. First we need to edit /etc/rsyslog.conf and uncomment these lines:

input(type="imudp" port="514")

input(type="imtcp" port="514")

They will activate the server on TCP and UDP port 514 for incoming syslog messages. With just this configuration, the syslog server will work. But we’ll make one more modification – we want each IP address to have it’s own file. Otherwise all messages get dumped in the main file at /var/log/syslog.

We’ll create a file at /etc/rsyslog.d/30-custom.conf and place a couple of simple rules in it:

if $fromhost-ip startswith '' then /var/log/network/
& stop
if $fromhost-ip startswith '' then /var/log/network/
& stop

Create and change the ownership of the /var/log/network directory:

mkdir /var/log/network
chown syslog:adm /var/log/network

And restart Rsyslog:

systemctl restart rsyslog

And we’re done!

Client Configuration

For a Cisco IOSv device, the following command will turn on logging to a remote server:

logging host

For Ubuntu, just add the following line to /etc/rsyslog.conf:

*.* @@

And restart the service:

systemctl restart rsyslog


To verify that syslog messages are in fact going to the server, we need to initiate an event.

For Cisco IOSv, shutting/no shutting any interface will do the trick. In config mode on the interface, just issue these commands:

Router(config-if)# shut
Router(config-if)# no shut

While you might be tempted to go check /var/log/network/ right away for syslog messages, it might be worth it to do a packet capture first to see if logs are indeed leaving the Cisco router and heading for the syslog server.

A capture between the two shows the following lone packet when we issue those shut commands:

Syslog protocol in Wireshark

Then check the /var/log/network/ on the syslog server to see if the message was properly written:

cat /var/log/network/
Jan 27 13:48:17 45: *Jan 27 13:48:16.540: %LINK-3-UPDOWN: Interface GigabitEthernet0/1, changed state to down

Initiating an event on the Ubuntu client is as easy as shutting down a service (I’m sure there’s others too). I happen to have Nginx web server running on this guy so I’ll stop it:

systemctl stop nginx

The in the file on the syslog server:

cat /var/log/network/
Jan 27 23:21:21 u20vm systemd[1]: Stopping A high performance web server and a reverse proxy server...
Jan 27 23:21:21 u20vm systemd[1]: nginx.service: Succeeded.
Jan 27 23:21:21 u20vm systemd[1]: Stopped A high performance web server and a reverse proxy server.

Hope you liked this one.

IPv6: IPsec Not Mandatory to Use or Exist

One thing is clear, there is a serious amount of misinformation about IPv6. There are many blog posts and even official documents from respected sources that have blatantly incorrect information. Many blog posts that start out with an introduction such as this one, but end up spreading misinformation about IPv6 anyway. To avoid spreading misinformation myself, I’m going to cite the only sources of truth in the network world: the IETF RFC’s. If you’re looking for a clear and simple one-liner to sum up this article here it is:

Since the IETF issued RFC 6434 in 2011, the IPv6 standard does not require devices to implement (to be capable of) IPsec, nor does it require the enablement or use of IPsec for IPv6 nodes that do implement (are capable of) IPsec.

Yes, you read that right. You cannot count on IPsec even existing in nodes and devices running IPv6. For many years now a great number of sources have been trumpeting that IPv6 will herald an era of not having to think about network security anymore. Most respectable sources reject this notion, saying that it’s required to be implemented (to exist) in all IPv6 nodes, but not required to be turned on. Unfortunately that is also false.

RFC 1883 – where it all started

In December 1995, the IETF (Internet Engineering Task Force, a standards body accepted as the source of truth in the networking world) released RFC 1883, which defined IPv6 as a successor to IPv4.

Page 36 of that RFC briefly and quietly stated that Authentication Header (AH) and Encapsulating Security Payload (ESP) be required to secure IPv6 traffic:

This document specifies that the IP Authentication Header [RFC-1826] and the IP Encapsulating Security Payload [RFC-1827] be used with IPv6, in conformance with the Security Architecture for the Internet Protocol [RFC-1825].

This lead to a lot of folks being really excited about the ramifications of mandated secure connections in IPv6. While I can’t find any articles from 1995, this blog post from Bitdefender (a respected name in security) from 2012 is a really good example of an article making really misleading statements about how IPsec works with IPv6:

IPv6 comes with built-in IPSec , a technology that ensures secure host-to-host communication. This means that two clients communicating over IPv6 can automatically do authentication, message integrity and encryption or any combination of those.

While the implementation (the capability) of IPsec was indeed mandatory in IPv6 for the first 16 years after RFC 1883 was released, no one ever said that the use or enablement of it would be mandatory.

RFC 6434 – IPsec from MUST to SHOULD

In December 2011, the IETF updated the IPv6 Node Requirements RFC with RFC 6434. In section 11 (Security), they make this unmistakable statement:

Previously, IPv6 mandated implementation of IPsec and recommended the key management approach of IKE. This document updates that recommendation by making support of the IPsec Architecture [RFC4301] a SHOULD for all IPv6 nodes.

While IPv6 IPsec is implemented (the capability exists) in major desktop/laptop OS’s such as Windows and macOS, the Internet is made up of much more than that. Internet of Things comes to mind.

It seems the IETF realized that it doesn’t make sense (or in many cases it’s not possible) to include the complexity of IPsec in an IPv6 implementation. They went on to say in RFC 6434:

This document recognizes that there exists a range of device types and environments where approaches to security other than IPsec can be justified.

Loads of misinformation

So yes, starting in 2011, IPsec even existing in a device running IPv6 is a big maybe. Despite this, some of the most respected companies in the world write documentation that describe the mandatory-ness of IPsec in IPv6. Take this IOS configuration guide from Cisco, written in August 2012:

IPsec is a mandatory component of IPv6 specification.

Or this article from Microsoft written in 2020:

Internet Protocol Security (IPsec) is a set of security protocols used to transfer IP packets confidentially across the Internet. IPsec is mandatory for all IPv6 implementations and optional for IPv4.

Or this article from Redhat written in 2019:

In What you need to know about IPv6, we mentioned that Internet Protocol Security (IPSec) is incorporated into IPv6. This statement simply means that communication between the two endpoints is either authenticated, encrypted, or both, via the extension headers. There is a long-running discussion on the internet regarding whether the interpretation of “IPSec being mandatory” in IPv6 is correct or not. If you need to know more about this topic, see RFC 6434.

This last one from Redhat is particularly confusing. They mention the very RFC where IPsec became optional and even mention the ongoing debate, but continue to say that communications in IPv6 are either authenticated or encrypted, or both.

What’s actually happening? Not IPv6 IPsec.

All of this RFC and protocol stuff is a bunch of theoretical pie in the sky. What’s actually happening out in the wild? Well, I can’t speak to the whole Internet. Maybe folks are using IPsec like they do in IPv4 – for site-to-site tunnels. Or maybe something else. But to test typical web traffic, I have native IPv6 capability in my home. I took some Wireshark captures to various websites.


IPv6 capture of


IPv6 capture of


IPv6 capture of

Where’s the IPsec? I don’t see any IKE, AH or ESP. Looks like typical TLS traffic to me. So… no change in security from IPv4, then.

iperf2 vs iperf3: What’s the difference?

At first glance, you might be tempted to use iperf3 simply because it is one more than iperf2 (don’t worry, I’m guilty of this crime as well). It’s not an unfair assumption to think that iperf3 is the most recent version of the software, because of the name. It’s common to have two different versions of software in parallel existence, so the new one can take hold while the older version slowly dies away. Python2 and Python3 come to mind. This is not the case with iperf, however.

I recently wrote a post on how to use iperf3 to test bandwidth. Shortly after that one of the authors of iperf2, Bob McMahon, reached out to me. He pointed out that iperf2 is very much actively developed with some cool new features having been added recently. Under the surface they are very different projects, maintained by different teams with different goals.

Today we’ll take a look at some of the differences between the two.


Ubuntu 20.04 and Rocky Linux 8.5 VM’s in GNS3

We have a really basic topology here, Ubuntu 20.04 and Rocky Linux 8.5 connected on a single link with IP subnet Both VM’s have iperf2 and iperf3 installed.

Bandwidth Test

For a bandwidth test, the two are almost identical. You can perform a bandwidth test using either with the same commands. For this test, the Ubuntu VM will be the client, and Rocky the server. Start the server on Rocky like this:

iperf -s

Server listening on TCP port 5001
TCP window size: 85.3 KByte (default)

And from Ubuntu perform a test like this:

iperf -c
Client connecting to, TCP port 5001
TCP window size:  238 KByte (default)
[  3] local port 36528 connected with port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec  1.90 GBytes  1.63 Gbits/sec

These commands will work using iperf2 or iperf3, however it should be noted you can’t use an iperf2 client with an iperf3 server, and vice-versa. Also, they use different TCP ports by default. Even if you used an iperf3 client with an iperf2 server and manually set the TCP port to be the same, you will get an error. They are not compatible:

iperf3 -c -p 5001
iperf3: error - received an unknown control message

Supported Operating Systems

iperf2 is the clear winner here, primarily because it has up-to-date Windows packages available for easy download right on the sourceforge page. I avoid Windows when I can, but it has a tendency to be unavoidable due to it’s sheer installation base. iperf3 apparently had some unofficial builds a while back but nothing officially supported. You’ll need to compile it yourself to work on Windows which can be an inconvenience at best.

iperf2 downloads page

For Linux, many operating systems come with iperf2 preinstalled, Ubuntu 20.04 is one such example. iperf3 is just a command away though, with package managers. For macOS, the Homebrew package manager can quickly get you iperf2 or iperf3.

Feature: iperf3 authentication (not encryption)

Description of authentication features in iperf3

iperf3 supports authenticating clients to the server using public key/private key as well as a users file. I decided to try it out. To avoid a hassle I just used the exact commands they provided in the man file. You first generate a public key and private key on the server:

openssl genrsa -des3 -out private.pem 2048
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
openssl rsa -in private.pem -out private_not_protected.pem -outform PEM

Then create a “credentials.csv” file with hashed passwords. The following commands will get a hashed password for you:

S_USER=james S_PASSWD=james
echo -n "{$S_USER}$S_PASSWD" | sha256sum | awk '{ print $1 }'
0b0c98028105e9e4d3f100280eac29bba90af614d1c75612729228e4d160c601 #This is the hash of "james"

Then create a “credentials.csv” file that looks like this:


Now start the server:

iperf3 -s --rsa-private-key-path ./private_not_protected.pem --authorized-users-path ./credentials.csv

Then from the client, copy the public key over:

scp james@ .

Then run the client:

iperf3 -c --rsa-public-key-path ./public.pem --username james

You’ll be asked for the password. If you get it right, the server will display a message that authentication succeeded:

Server listening on 5201
Authentication successed for user 'james' ts 1639396545
Accepted connection from, port 32784
[  5] local port 5201 connected to port 32786
[ ID] Interval           Transfer     Bitrate
[  5]   0.00-1.00   sec   194 MBytes  1.63 Gbits/sec                  
[  5]   1.00-2.00   sec   204 MBytes  1.71 Gbits/sec

Feature: iperf2 isochronous mode

One of the coolest features of iperf2 is its “isochronous” option. This option is designed to simulate video streaming network traffic. You can hear Bob McMahon explain it himself on his youtube video on this feature.

Using the parameters and commands he describes in his video, we’ll run on a test. The Ubuntu server will be the iperf2 server:

iperf -s -e -i 1

Then on Rocky Linux we’ll run the client test:

[james@localhost ~]$ iperf -c -i 1 --isochronous=60:40m,10m
Client connecting to, TCP port 5001 with pid 1640
UDP isochronous: 60 frames/sec mean=40.0 Mbit/s, stddev=10.0 Mbit/s, Period/IPG=16.67/0.005 ms
TCP window size:  340 KByte (default)
[  3] local port 49150 connected with port 5001 (ct=1.44 ms)
[ ID] Interval        Transfer    Bandwidth       Write/Err  Rtry     Cwnd/RTT        NetPwr
[  3] 0.00-1.00 sec   214 MBytes  1.79 Gbits/sec  1708/0          0       67K/562 us  398346.93
[  3] 1.00-2.00 sec   217 MBytes  1.82 Gbits/sec  1738/0        230      145K/608 us  374676.21
[  3] 2.00-3.00 sec   205 MBytes  1.72 Gbits/sec  1640/0        427      142K/583 us  368710.26
[  3] 3.00-4.00 sec   212 MBytes  1.78 Gbits/sec  1697/0        575      118K/920 us  241770.85
[  3] 4.00-5.00 sec   200 MBytes  1.68 Gbits/sec  1599/0        371      134K/853 us  245702.38
[  3] 5.00-6.00 sec   200 MBytes  1.68 Gbits/sec  1598/0        423      117K/529 us  395941.50

On the server we get our output:

james@u20vm:~$ iperf -s -e -i 1
Server listening on TCP port 5001 with pid 3045
Read buffer size:  128 KByte
TCP window size:  128 KByte (default)
[  4] local port 5001 connected with port 49150
[ ID] Interval            Transfer    Bandwidth       Reads   Dist(bin=16.0K)
[  4] 0.0000-1.0000 sec   213 MBytes  1.79 Gbits/sec  4631    503:1500:1008:577:276:191:138:438
[  4] 1.0000-2.0000 sec   217 MBytes  1.82 Gbits/sec  4018    570:838:812:502:255:231:164:646
[  4] 2.0000-3.0000 sec   204 MBytes  1.71 Gbits/sec  5074    590:1537:1637:511:316:152:115:216
[  4] 3.0000-4.0000 sec   212 MBytes  1.78 Gbits/sec  3924    599:805:717:464:266:264:246:563
[  4] 4.0000-5.0000 sec   200 MBytes  1.68 Gbits/sec  3876    575:953:672:462:258:242:188:526
[  4] 5.0000-6.0000 sec   200 MBytes  1.68 Gbits/sec  4046    656:1040:687:476:258:242:238:449

Unfortunately the version of iperf that is available in Ubuntu 20.04 repositories (2.0.13) doesn’t support isochronous TCP mode mentioned in the video. You would need to compile from source or use Windows for that. A newer version will be included (probably already has been by the time you’re reading this) in Ubuntu 22.04 LTS.

Various smaller differences

There are many other spots that iperf2 and iperf3 are different.

  • iperf2 supports an “enhanced output mode” using -e that is totally revamped (used it above in the isochronous section).
  • iperf3 supports json output using the -j option.
  • iperf2 supports a bidirectional test which performs tests from the client and server simultaneously using -d
  • iperf2 uses a multi-threaded architecture, while iperf3 uses single-threaded. To be honest, I haven’t seen any way that this actually affects performance of the application. I’d be really curious if anyone has some input on this.

I hope this was helpful, and I hope I did both of these cool programs a small amount of justice. I’m really curious to see if anyone has any other input or differences they know about. Please fee free to comment or reach out directly.

How To Test Network Bandwidth With iperf3 in Linux

Testing network bandwidth in multiple flavors in Linux is simple with a tool called iperf. There’s two main versions – iperf2 and iperf3. Project maintainers apparently completely rewrote iperf3 from scratch to make the the tool simpler and to support some new features.

Update 12/12/2021: One of the authors of iperf2 reached out to me. Iperf2 is currently very much actively developed. You can find the most recent code on its page. Iperf3 was indeed rewritten from scratch as the wikipedia page says, but mostly to meet the U.S. Department of Energy’s use cases. Iperf3’s github page clearly states the the DoE owns the project.

For testing bandwidth properly, you need to be running in server mode on one endpoint and client mode on the other. For this experiement, we will run the server on Rocky Linux 8.5 and the client on Ubuntu 20.04.


iperf3 test in GNS3

This is about as simple of a topology as I can think of. Two nodes on either end of a single link, Ubuntu at running iperf3 client and Rocky at running iperf3 server.

Iperf3 installation

On Ubuntu, iperf3 can be installed from distribution sources with apt-get:

apt-get install iperf3

Same on Rocky Linux but with yum:

yum install iperf3

Run iperf3 bandwidth test

First we need to start the server process on Rocky Linux with one command:

iperf3 -s

Then you should see the server listening for incoming tests:

iperf3 server listening on Rocky Linux 8.5

Then from the Ubuntu client, one command will run the test:

iperf3 -c

The output will give us our bandwith test results, which can be see on either the client or server:

Connecting to host, port 5201
[  5] local port 59628 connected to port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec   176 MBytes  1.48 Gbits/sec  685    230 KBytes       
[  5]   1.00-2.00   sec   173 MBytes  1.45 Gbits/sec  738    113 KBytes       
[  5]   2.00-3.00   sec   170 MBytes  1.42 Gbits/sec  1004    191 KBytes       
[  5]   3.00-4.00   sec   175 MBytes  1.47 Gbits/sec  714    123 KBytes       
[  5]   4.00-5.00   sec   182 MBytes  1.52 Gbits/sec  458    163 KBytes       
[  5]   5.00-6.00   sec   204 MBytes  1.71 Gbits/sec  443    314 KBytes       
[  5]   6.00-7.00   sec   180 MBytes  1.51 Gbits/sec  910    130 KBytes       
[  5]   7.00-8.00   sec   191 MBytes  1.60 Gbits/sec  849    123 KBytes       
[  5]   8.00-9.00   sec   172 MBytes  1.44 Gbits/sec  564    170 KBytes       
[  5]   9.00-10.00  sec   184 MBytes  1.54 Gbits/sec  412    225 KBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.00  sec  1.76 GBytes  1.52 Gbits/sec  6777             sender
[  5]   0.00-10.04  sec  1.76 GBytes  1.51 Gbits/sec                  receiver

iperf Done.

A wireshark capture in GNS3 between the two hosts (or tcpdump on the links if you’re not in GNS3) will show the packets flying while the test is running:

Wireshark capture from GNS3 of iperf3 test

Hope you liked it!

Wireguard VPN on Ubuntu 20.04

Wireguard is an attempt to improve VPN tunnels in a number of ways – simpler code, less compute, easier configuration, the list goes on. If we’re comparing it to IPsec, I would say that yes, it’s a bit easier to configure. One of the main differences is that it does not rely on the classic two IPsec options for keys – PSK and X.509 certificates. Instead, it relies on public key/private key similar to SSH.

Today, we’re going to configure a very simple policy-based site-to-site Wireguard VPN. By “policy-based” I mean that tunneled traffic is determined by a pre-written configuration in the Wireguard configuration file, not by static or dynamic routes. By site-to-site, I mean this is not a remote-access (road warrior) VPN, it’s designed to connect the subnets that sit behind the two VPN peers.


The two Ubuntu20.04 machines are serving as routers and VPN peers. No routing is in place for PC1 and PC2 to talk to each other. While the two Ubuntu machines can connect to each other, there is no network-level encryption. That’s where Wireguard comes in.

Generate key pairs

If you haven’t installed it yet, wireguard can be installed with apt-get:

apt-get install wireguard

We’ll use the wg command to generate keys on Ubuntu20.04-1. These two commands will generate a private and public key pair:

wg genkey > private1.key
wg pubkey < private1.key > public1.key

We’ll do the same on Ubuntu20.04-2

wg genkey > private2.key
wg pubkey < private2.key > public2.key

Tunnel configurations

Then we’ll write a tunnel config file for Ubuntu20.04-1 in /etc/wireguard/wg0.conf:

PrivateKey = uOnrdfW/ANcU+fh+RUjlb3TQlFWIdwbOpDpAA+NkonY=
Address =
ListenPort = 8888

PublicKey = JjSDqdzjPOX+iIAaWCjxxg1yZ76jAd6jfSfv/1AlojI=
Endpoint =
AllowedIPs =,

You’ll notice I took the keys out of the key files and pasted them into the config file. Under “Interface” we have Ubuntu20.04-1’s own private key, while the public key of Ubuntu20.04-2 is under “Peer”. The “Address” parameter is the “glue network” of the tunnel. This will be the virtual subnet that exists inside the tunnel encryption. The “AllowedIPs” parameter is where tunneled traffic is specified (interesting traffic). You need to put the destination subnet here. Since this is Ubuntu20.04-1 and is the destination on the other side of the tunnel, we put as part of the “AllowedIPs” parameter. Also put the glue network in here as well.

We can write a similar one for Ubuntu20.04-2 in /etc/wireguard/wg0.conf. For Ubuntu20.04-2, is on the other side of the tunnel so will be in “AllowedIPs”. Under “Interface”, we have its own private key, while the public key of Ubuntu20.04-1 is pasted under “Peer”.

PrivateKey = KJgjkPQVhOX5CyYYWr7B6v1AbI7H2kEtBi4wdhAES2g=
Address =
ListenPort = 8888

PublicKey = +GOlIMgLAnLZujraI8m4F6JyWZOpxWGRAPSUqkwrZyg=
Endpoint =
AllowedIPs =,

To bring the tunnels up, on each Ubuntu machine run this command:

wg-quick up wg0

To verify the configuration is loaded, use “wg show”. I ran this one from Ubuntu20.04-2:

wg show
interface: wg0
  public key: JjSDqdzjPOX+iIAaWCjxxg1yZ76jAd6jfSfv/1AlojI=
  private key: (hidden)
  listening port: 8888

peer: +GOlIMgLAnLZujraI8m4F6JyWZOpxWGRAPSUqkwrZyg=
  allowed ips:,
  latest handshake: 28 minutes, 48 seconds ago
  transfer: 1.09 KiB received, 2.63 KiB sent

We should be able to ping the tunnel glue network IPs (here from Ubuntu20.04):

root@u20vm:/home/james# ping
PING ( 56(84) bytes of data.
64 bytes from icmp_seq=1 ttl=64 time=5.22 ms

And PC1 and PC2 can ping each other too:

PC1> ping

84 bytes from icmp_seq=1 ttl=62 time=4.942 ms

You’ll notice we did not have to add any routes for and to be able to reach each other. The Wireguard configuration added routing automatically, which is why I am calling this type of tunnel “policy-based”.

And the most fun part, Wireshark. We can see the traffic going back and forth, and the protocol is labeled “Wireguard”. Pretty cool, right?

Wireshark capture of Wireguard traffic

Hope you liked this one.

Basic Firewall With Iptables on Ubuntu 20.04

Ubuntu comes with iptables, a configuration utility that allows you to manage rules for Netfilter, the Linux Kernel firewall. Using iptables you can manipulate packets as they leave, enter, or are forwarded across network interfaces on a Linux operating system. Today we’ll look at how to block SSH traffic going through an Ubuntu 20.04 system acting as a router.


I have a basic configuration here with three IP subnets – where the SSH client lives, is a transit network between routers, and where the SSH server lives. We will be configuring Ubuntu20.04-Firewall with iptables to block SSH traffic.


Iptables requires no external package installation with apt-get or otherwise, it comes stock-and-standard with a fresh Ubuntu 20.04 Server or Desktop OS. You can verify the status of your iptables rules like so:

iptables -S


By default, iptables is configured to pretty much do nothing. No packets are filtered and no NAT (network address translation) is configured.

Iptables syntax

The syntax for iptables can be quite confusing, and since I myself am not configuring them on a daily basis, I always need to reference documentation (or someone else’s blog) for how to configure something specific. It’s a good idea to take a quick look at the basic syntax though. The command structure looks like this, taken straight from the iptables manual page:

iptables [-t table] [mode] [chain] [rulenum] [rule-specification] [options]
  • table is which type of table you want to use. Usually it’s filter for dropping disallowed packets, or nat for translating packets. Filter is default if none is specified.
  • mode is an action, -A (append), -I (insert), -D (delete), -R (replace), -L (list), -P (set policy, is default action) are all valid modes.
  • chain is the part of the routing process to which your rule applies, there are five chains – PREROUTING, INPUT, FORWARD, OUTPUT, POSTROUTING.
  • rulenum gives the rule a sequence, rules are applied one-by-one and when there is a match, the corresponding action is taken and all subsequent rules are ignored.
  • rule-specification is the actual rule itself. There are many parameters that can be specified here, like protocol, source, destination, etc.
  • options give you some customization – for example, you can add -v for verbose output.

Now that we have a basic idea of what iptables does, let’s drop some SSH packets.

Verifying what we’re blocking

On Ubuntu20.04-Firewall in my topology, I can see that Ubuntu20.04-SSH_Client at has SSH access to Ubuntu20.04-SSH_Server at

james@client$ssh james@
james@'s password:


SSH is an network application protocol that usually uses TCP port 22 to establish an encrypted command-line session between two computers. If we do a quick wireshark betwen the Ubuntu20.04-Firewall and CiscoIOSv15.6(1)T-1 router, we can see this SSH traffic traversing the link:

Wireshark capture of SSH traffic in GNS3

Starting at the top you can see the TCP handshake on port 22 starting with the SYN flag. A few packets later SSHv2 packets begin. All packets use a randomized source TCP port (source is different per session) and a destination TCP port of 22. So we can safely say that in this experiment, if we drop TCP destination port 22, we will effectively block SSH traffic between the client and server.

Configure the iptables rule

A rule to drop SSH packets on TCP 22 can be configured in one line on Ubuntu20.04-Firewall:

iptables -A FORWARD -p tcp --dport 22 -j DROP
  • -A specifies we are appending a rule.
  • FORWARD applies the rule to packets being forwarded from one interface to another
  • -p tcp is a rule-specification to apply the rule to TCP packets
  • --dport 22 applies the rule to destination TCP port 22
  • -j DROP tells iptables to drop the packet if the previous conditions match


Let’s see if the client can connect now:

james@client$ ssh james@
ssh: connect to host port 22: Connection timed out

It worked! Packets on TCP port 22 are being dropped at the Ubuntu firewall.

Please keep in mind – this is a simple block on TCP destination port 22. TCP and UDP ports use the concept of “well-known” which means servers running protocols use a port that everyone “knows”. This is so that someone connecting to a server for the first time with no previous knowledge of its configuration will be able to connect, since they both assume that TCP port 22 is used for SSH. However if someone configures SSH on the client and server in this example to use any other port, it will go right around the filter.

Reach out if you have issues or something to share!

Telnet to Ubuntu Server 20.04 in GNS3 Instead of VNC

If you’re using Ubuntu VM’s inside of GNS3, you’re probably sick of using a VNC client to access its command line.

The first big drawback to using VNC is that you can’t (or at least it’s not immediately obvious how to) paste text or commands you’ve found into the terminal. You have to retype everything, which is a real bummer.

The second big drawback is that a VNC session can’t be automated (or at least I don’t know of a good tool to do that). Since VNC is like RDP in that the session is visual, a human being or really advanced AI is required to interact with the session.

Having access to a VM in GNS3 via telnet to its terminal is a real benefit. You can set it up pretty quickly in Ubuntu 20.04. Full disclosure – this method only gets you access after the device has booted and arrived at the login prompt. There is a way to allow access earlier than that so the boot process can be viewed, I just haven’t gotten to it yet.

Set your VM to not be “linked base”

One mistake I often make in GNS3 is forgetting to make my VM not a “linked base” when I want to make permanent changes. A linked base is basically a clone of your VM. Any changes you make, files you download or programs you install will be blown away when you delete the device from the GNS3 canvas. To disable this functionality temporarily to make permanent changes, go to the device in the left pane and click “configure template”. On the advanced tab, uncheck “Use as a linked base VM”:

When you are done configuring the telnet capability, you can recheck this box. All linked base VM’s you drag out afterwards will have the telnet capability.

Create the ttyS0.service

You first need to create a systemd service for serial access. We need to create a file called ttyS0.service in the /lib/systemd/system/ directory:

vi /lib/systemd/system/ttyS0.service

The file contents should look like this:

Description=Serial Console Service

ExecStart=/sbin/getty -L 115200 ttyS0 vt102


getty is program that manages tty sessions, physical or virtual terminals. It will run the login prompt when a connection is detected. 115200 is the baud rate, ttyS0 is a device file that points to the current terminal, and vt102 is the terminal emulator.

Load the service in systemd

Just a few commands will load the new service in systemd, and the script will run on boot to activate your serial device and allow telnet. Run these commands:

#Make file executable
chmod 755 ttyS0.service

#Reload systemd
systemctl daemon-reload

#Enable the service
systemctl enable ttyS0

#Start the service
systemctl start ttyS0

Your service is good to go!

Change the console type to telnet

You need to first shut down your VM so you can change the console type. Once it’s shutdown, you can configure the device on the canvas, or the template in the pane to the left, or both. The template will make changes for all VM’s dragged onto the canvas in the figure. Either way, configure the node by right clicking on it, and clicking on “configure” or “configure template”. At the very bottom, you should see a dropdown for “console type”. Change it to “telnet”:

Log in via telnet!

Just double-click on your VM. You won’t see any output on the telnet window while the VM is booting up because the service hasn’t fired yet. But when it does, you should see the login prompt:

Bonus tip – turn off dhcp in netplan

I had to turn off dhcp in Ubuntu’s netplan network configuration tool to get it to stop hanging at boot. There should be a yaml file in /etc/netplan/ (the yaml file name might differ per system) where you can turn it off. My netplan config looks like this:

      dhcp4: false
      optional: yes
  version: 2

Hope that helps!