Monday, 28 September 2020

Testing DHCP server configured for v6 only with another Raspberry Pi

I wanted to test the DHCP server installed on Raspberry Pi OS configured for v6 only as described in my recent The ultimate guide for RPD provisioning blog post. To achieve this, I used another Raspberry Pi OS directly connected back to back with a single ethernet patch cable.

First, we need a DHCPv6 client on the other Raspberry Pi OS.

pi@raspberrypi:~ $ sudo apt-get install wide-dhcpv6-client

During installation, specify the eth0 interface and also edit the following configuration file:

pi@raspberrypi:~ $ sudo nano /etc/wide-dhcpv6/dhcp6c.conf
interface eth0 {
  send ia-na 0;
};
id-assoc na 0 {
};

As IPv6 networks learned from a router advertisement, we need to install another tool besides DHCP on the server-side to enable back to back IPv6 communication over the direct ethernet patch cable:

pi@raspberrypi:~ $ sudo apt install radvd

The following config file advertises the same network configured in the previous blog post on the DHCP server:

pi@raspberrypi:~ $ sudo nano /etc/radvd.conf
interface eth0 {
    AdvSendAdvert on;
    prefix fd51:42f8:caae:d92e::/64;
};

This concludes IPv6 connectivity between the DHCP server and DHCP client via acquired IPv6 address. 

Sunday, 27 September 2020

First steps to build a cluster with ansible

As I have chosen a cluster case for my very first 2GB Raspberry Pi 4b, it was only a matter of time that I find an excuse to buy a second one. The very first challenge that I found when building a cluster is to keep the packages updated. Of course, I can log in to both of them to keep them updated, but this is easier with ansible. 

To start off, I used Raspberry Pi Imager on Debian with Raspberry Pi Desktop running on my Lenovo T480s laptop from persistent live USB to flash Raspberry Pi OS on both 32 GB SD cards. I made the installation headless by merely adding an empty file named ssh to the /boot partition before ejecting the SD cards. I have added static DHCP entries for both of them in pi-hole so that I can use names instead of IP addresses to reach them. I logged in initially to both of them to change the password and then copied my ssh keys on them.

Next, I installed ansible:

jordana@dad-laptop-ac:~ $ sudo apt install ansible

Checked version to see it installed properly:

jordana@dad-laptop-ac:~ $ ansible --version
ansible 2.7.7
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/pi/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.7.3 (default, Jul 25 2020, 13:03:44) [GCC 8.3.0]

Added both names configured for static DHCP and pi as username:  

jordana@dad-laptop-ac:~ $ sudo nano /etc/ansible/hosts
[pi]
pi4b1-eth
pi4b2-eth
[pi:vars]
ansible_user=pi

Then I created a playbook to bring all packages to the latest state:

jordana@dad-laptop-ac:~ $ nano apt.yml
---
  - hosts: pi
    tasks:
      - become: yes
        apt:
          force_apt_get: yes
          update_cache: yes
          name: "*"
          state: latest

Running the playbook I get the following. Notice that the second run did not cause change.

jordana@dad-laptop-ac:~ $ ansible-playbook apt.yml 

PLAY [pi] ******************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************
ok: [pi4b1-eth]
ok: [pi4b2-eth]

TASK [apt] *****************************************************************************************************************************
changed: [pi4b2-eth]
changed: [pi4b1-eth]

PLAY RECAP *****************************************************************************************************************************
pi4b1-eth                  : ok=2    changed=1    unreachable=0    failed=0   
pi4b2-eth                  : ok=2    changed=1    unreachable=0    failed=0   

jordana@dad-laptop-ac:~ $ ansible-playbook apt.yml 

PLAY [pi] ******************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************
ok: [pi4b1-eth]
ok: [pi4b2-eth]

TASK [apt] *****************************************************************************************************************************
ok: [pi4b2-eth]
ok: [pi4b1-eth]

PLAY RECAP *****************************************************************************************************************************
pi4b1-eth                  : ok=2    changed=0    unreachable=0    failed=0   
pi4b2-eth                  : ok=2    changed=0    unreachable=0    failed=0   

I have also installed ansible on the Ubuntu-20.04 I use with Windows Subsystem for Linux.

jordana@LP-AJORDAN:~$ ansible --version
ansible 2.9.6
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/jordana/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.8.2 (default, Jul 16 2020, 14:00:26) [GCC 9.3.0]

For some reason this installation worked fine without force_apt_get parameter in the apt.yml file.

This concludes the first challenge to keep packages updated on my shiny new cluster.

Monday, 21 September 2020

The ultimate guide for RPD provisioning

To boot up the RPD, we will need two services configured on the Raspberry Pi in IPv6: 1) DHCPv6 service 2) time service. To provide SW upgrade capabilities on the Raspberry Pi for the RPD, we will need a third one: TFTP service.

First let us dip our toe to the services running on Linux by listing them. For this we can use the following command:

pi@raspberrypi:~ $ sudo netstat -lp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 pi4b2.lan:47522         0.0.0.0:*               LISTEN      3501/zerotier-one  
tcp        0      0 pi4b2.lan:47523         0.0.0.0:*               LISTEN      3501/zerotier-one  
tcp        0      0 pi4b2.lan:9993          0.0.0.0:*               LISTEN      3501/zerotier-one  
tcp        0      0 localhost:9993          0.0.0.0:*               LISTEN      3501/zerotier-one  
tcp6       0      0 localhost:9993          [::]:*                  LISTEN      3501/zerotier-one  
udp        0      0 pi4b2.lan:47522         0.0.0.0:*                           3501/zerotier-one  
udp        0      0 pi4b2.lan:47523         0.0.0.0:*                           3501/zerotier-one  
udp        0      0 0.0.0.0:46103           0.0.0.0:*                           352/avahi-daemon: r
udp        0      0 0.0.0.0:bootpc          0.0.0.0:*                           692/dhcpcd          
udp        0      0 0.0.0.0:mdns            0.0.0.0:*                           352/avahi-daemon: r
udp        0      0 pi4b2.lan:9993          0.0.0.0:*                           3501/zerotier-one  
udp6       0      0 [::]:dhcpv6-client      [::]:*                              692/dhcpcd          
udp6       0      0 [::]:48271              [::]:*                              352/avahi-daemon: r
udp6       0      0 [::]:mdns               [::]:*                              352/avahi-daemon: r
raw6       0      0 [::]:ipv6-icmp          [::]:*                  7           692/dhcpcd          
Active UNIX domain sockets (only servers)
Proto RefCnt Flags       Type       State         I-Node   PID/Program name     Path
unix  2      [ ACC ]     STREAM     LISTENING     9742     1/init               /run/systemd/journal/stdout
unix  2      [ ACC ]     STREAM     LISTENING     9763     1/init               /run/systemd/fsck.progress
unix  2      [ ACC ]     SEQPACKET  LISTENING     10330    1/init               /run/udev/control
unix  2      [ ACC ]     STREAM     LISTENING     14993    692/dhcpcd           /var/run/dhcpcd.sock
unix  2      [ ACC ]     STREAM     LISTENING     14995    692/dhcpcd           /var/run/dhcpcd.unpriv.sock
unix  2      [ ACC ]     STREAM     LISTENING     18867    852/systemd          /run/user/1000/systemd/private
unix  2      [ ACC ]     STREAM     LISTENING     13495    1/init               /run/thd.socket
unix  2      [ ACC ]     STREAM     LISTENING     18873    852/systemd          /run/user/1000/gnupg/S.dirmngr
unix  2      [ ACC ]     STREAM     LISTENING     13499    1/init               /var/run/dbus/system_bus_socket
unix  2      [ ACC ]     STREAM     LISTENING     18877    852/systemd          /run/user/1000/gnupg/S.gpg-agent.ssh
unix  2      [ ACC ]     STREAM     LISTENING     18880    852/systemd          /run/user/1000/gnupg/S.gpg-agent.extra
unix  2      [ ACC ]     STREAM     LISTENING     13504    1/init               /run/avahi-daemon/socket
unix  2      [ ACC ]     STREAM     LISTENING     18882    852/systemd          /run/user/1000/gnupg/S.gpg-agent.browser
unix  2      [ ACC ]     STREAM     LISTENING     18884    852/systemd          /run/user/1000/gnupg/S.gpg-agent
unix  2      [ ACC ]     STREAM     LISTENING     9724     1/init               /run/systemd/private

For now, please notice dhcpcd is one of the services. This service is responsible for gaining IP addresses for interfaces. It comes handy for us now because we will need to configure a static IPv6 address for the wired Gigabit so-called eth0 interface to serve the RPD with all those DHCPv6, time and TFTP services. Please connect this wired Gigabit port to a switch so that we can see the changes on the interface as we move along.
Once connected, let us first use the following command to check what IP addresses we currently have:

pi@raspberrypi:~ $ 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 1500 qdisc mq state UP group default qlen 1000
    link/ether dc:a6:32:c6:4f:ee brd ff:ff:ff:ff:ff:ff
    inet 169.254.114.74/16 brd 169.254.255.255 scope global noprefixroute eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::4f1b:d468:f188:19ee/64 scope link
       valid_lft forever preferred_lft forever
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether dc:a6:32:c6:4f:ef brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.8/24 brd 192.168.1.255 scope global dynamic noprefixroute wlan0
       valid_lft 86384sec preferred_lft 75584sec
    inet6 fe80::ed25:290d:d706:8ab2/64 scope link
       valid_lft forever preferred_lft forever
4: ztks5s5bvz: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 2800 qdisc pfifo_fast state UNKNOWN group default qlen 1000
    link/ether d6:8a:40:30:e3:08 brd ff:ff:ff:ff:ff:ff
    inet 192.168.192.43/24 brd 192.168.192.255 scope global ztks5s5bvz
       valid_lft forever preferred_lft forever
    inet6 fe80::d48a:40ff:fe30:e308/64 scope link
       valid_lft forever preferred_lft forever

To add a static IPv6 address to eth0 use the following command:
 
pi@raspberrypi:~ $ sudo nano /etc/dhcpcd.conf
 
Remove the starting # from the following two lines and press Ctrl+x then Yes and Enter:
 
interface eth0
static ip6_address=fd51:42f8:caae:d92e::ff/64
 
Now let us reboot our Raspberry Pi for this configuration to take effect with the following command:
 
pi@raspberrypi:~ $ sudo reboot
 
Once the system boots up again, let us check from the terminal window again the IP addresses with the following command:
 
pi@raspberrypi:~ $ 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 1500 qdisc mq state UP group default qlen 1000
    link/ether dc:a6:32:c6:4f:ee brd ff:ff:ff:ff:ff:ff
    inet 169.254.114.74/16 brd 169.254.255.255 scope global noprefixroute eth0
       valid_lft forever preferred_lft forever
    inet6 fd51:42f8:caae:d92e::ff/64 scope global noprefixroute
       valid_lft forever preferred_lft forever
    inet6 fe80::4f1b:d468:f188:19ee/64 scope link
       valid_lft forever preferred_lft forever
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether dc:a6:32:c6:4f:ef brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.8/24 brd 192.168.1.255 scope global dynamic noprefixroute wlan0
       valid_lft 85542sec preferred_lft 74742sec
    inet6 fe80::ed25:290d:d706:8ab2/64 scope link
       valid_lft forever preferred_lft forever
4: ztks5s5bvz: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 2800 qdisc pfifo_fast state UNKNOWN group default qlen 1000
    link/ether d6:8a:40:30:e3:08 brd ff:ff:ff:ff:ff:ff
    inet 192.168.192.43/24 brd 192.168.192.255 scope global ztks5s5bvz
       valid_lft forever preferred_lft forever
    inet6 fe80::d48a:40ff:fe30:e308/64 scope link
       valid_lft forever preferred_lft forever

Please notice that we have our shiny new static IPv6 address showing up under eth0 now.
 
Now we need to install a DHCP server with the following command:
 
pi@raspberrypi:~ $ sudo apt install isc-dhcp-server
 
Do not bother too much that it failed to load upon installation. We just need to configure it first with the following command:
 
pi@raspberrypi:~ $ sudo nano /etc/dhcp/dhcpd6.conf
 
Add these lines to the end of the file and press Ctrl+x then Yes and Enter:
 
option space docsis code width 2 length width 2;
option docsis.cablelabs-syslog-servers code 34 = array of ip6-address;
option docsis.time-servers code 37 = array of ip6-address;
option docsis.time-offset code 38 = signed integer 32;
option docsis.ccap-core code 61 = array of ip6-address;
option vsio.docsis code 4491 = encapsulate docsis;
option docsis.cablelabs-syslog-servers fd51:42f8:caae:d92e::ff;
option docsis.time-servers fd51:42f8:caae:d92e::ff;
option docsis.time-offset -25200;
option docsis.ccap-core fd51:42f8:caae:d92e::ff;
subnet6 fd51:42f8:caae:d92e::0/64 {
  range6 fd51:42f8:caae:d92e::fe fd51:42f8:caae:d92e::fe;
}

Finally, we need to restrict the DHCP server for IPv6 with the following configuration file:
 
pi@raspberrypi:~ $ sudo nano /etc/default/isc-dhcp-server
 
Modify this configuration file so that it looks like this then press Ctrl+x then Yes and Enter:
 
# Defaults for isc-dhcp-server (sourced by /etc/init.d/isc-dhcp-server)
 
# Path to dhcpd's config file (default: /etc/dhcp/dhcpd.conf).
#DHCPDv4_CONF=/etc/dhcp/dhcpd.conf
DHCPDv6_CONF=/etc/dhcp/dhcpd6.conf
 
# Path to dhcpd's PID file (default: /var/run/dhcpd.pid).
#DHCPDv4_PID=/var/run/dhcpd.pid
DHCPDv6_PID=/var/run/dhcpd6.pid
 
# Additional options to start dhcpd with.
#       Don't use options -cf or -pf here; use DHCPD_CONF/ DHCPD_PID instead
OPTIONS="-6"
 
# On what interfaces should the DHCP server (dhcpd) serve DHCP requests?
#       Separate multiple interfaces with spaces, e.g. "eth0 eth1".
#INTERFACESv4=""
INTERFACESv6="eth0"

Now we need to restart the DHCP service with the following commands:
 
pi@raspberrypi:~ $ sudo systemctl stop isc-dhcp-server.service
pi@raspberrypi:~ $ sudo systemctl start isc-dhcp-server.service
pi@raspberrypi:~ $ sudo systemctl status isc-dhcp-server.service
isc-dhcp-server.service - LSB: DHCP server
   Loaded: loaded (/etc/init.d/isc-dhcp-server; generated)
   Active: active (running) since Tue 2020-09-22 00:38:12 BST; 6s ago
     Docs: man:systemd-sysv-generator(8)
  Process: 2004 ExecStart=/etc/init.d/isc-dhcp-server start (code=exited, status=0/SUCCESS)
    Tasks: 1 (limit: 3862)
   CGroup: /system.slice/isc-dhcp-server.service
           └─2013 /usr/sbin/dhcpd -6 -q -cf /etc/dhcp/dhcpd6.conf eth0
 
Sep 22 00:38:10 raspberrypi systemd[1]: Starting LSB: DHCP server...
Sep 22 00:38:10 raspberrypi isc-dhcp-server[2004]: Launching IPv6 server only.
Sep 22 00:38:10 raspberrypi dhcpd[2013]: Wrote 0 NA, 0 TA, 0 PD leases to lease file.
Sep 22 00:38:10 raspberrypi dhcpd[2013]: Bound to *:547
Sep 22 00:38:10 raspberrypi dhcpd[2013]: Server starting service.
Sep 22 00:38:12 raspberrypi isc-dhcp-server[2004]: Starting ISC DHCPv6 server: dhcpd6.
Sep 22 00:38:12 raspberrypi systemd[1]: Started LSB: DHCP server.
 
We can now find dhcpv6-server in the list of services:
 
pi@raspberrypi:~ $ sudo netstat -lp | grep dhcpv6-server
udp6       0      0 [::]:dhcpv6-server      [::]:*                              654/dhcpd    

There are two more services left to configure. For the time service let us install xinetd with the following command:
 
pi@raspberrypi:~ $ sudo apt install xinetd
 
To enable time service on udp, alter disable from yes to no in the following config file and then press Ctrl+x then Yes and Enter:
 
pi@raspberrypi:~ $ sudo nano /etc/xinetd.d/time-udp
 
Bouncing xinetd will bring up time service:
 
pi@raspberrypi:~ $ sudo systemctl stop xinetd.service
pi@raspberrypi:~ $ sudo systemctl start xinetd.service
pi@raspberrypi:~ $ sudo netstat -lp | grep time
udp6       0      0 [::]:time               [::]:*                              1267/xinetd        
 
Finally, let us add TFTP service with the following command:
 
pi@raspberrypi:~ $ sudo apt install tftpd

We need to add the following configuration file:
 
pi@raspberrypi:~ $ sudo nano /etc/xinetd.d/tftp

Add these lines to this configuration file and then press Ctrl+x then Yes and Enter:
 
service tftp
{
protocol        = udp
port            = 69
socket_type     = dgram
wait            = yes
user            = nobody
server          = /usr/sbin/in.tftpd
server_args     = /tftpboot
disable         = no
}

Bouncing xinetd again will bring up tftp service:
 
pi@raspberrypi:~ $ sudo systemctl stop xinetd.service
pi@raspberrypi:~ $ sudo systemctl start xinetd.service
pi@raspberrypi:~ $ sudo netstat -lp | grep tftp
udp6       0      0 [::]:tftp               [::]:*                              1513/xinetd
 
To create default directory for tftp add these commands:
 
pi@raspberrypi:~ $ sudo mkdir /tftpboot
pi@raspberrypi:~ $ sudo chmod -R 777 /tftpboot
pi@raspberrypi:~ $ sudo chown -R nobody /tftpboot

This concludes the installation. However, isc-dhcp-server might not start automatically if wifi acquires carrier faster then eth0. To work around this issue:

pi@raspberrypi:~ $ sudo systemctl edit isc-dhcp-server.service
[Service]
ExecStartPre=/bin/sleep 10

This adds 10 seconds delay before isc-dhcp-server starts after wifi has come up. This is usually enough for eth0 also to acquire carrier.

With the courtesy of my colleague in the US, I can share a picture of this lab:



Sunday, 6 September 2020

Testing Docsis remote PHY device using DHCPv6 from Raspberry Pi (Desktop)

 I work on an RF bracket for a Docsis remote PHY device. This requires collaboration with RF engineers who have limited knowledge about computers. While the latest software version of the Docsis remote PHY device supports RF test mode that is perfect for an RF engineer, it still requires DHCPv6 to boot up properly which is a definite challenge for someone less familiar with computers.

During the lockdown, I had to have the RF bracket tested with an RF engineer, but he only had RF test equipment in his garage with an ancient laptop having a single working USB adapter. I have remotely supported him flashing Debian with Raspberry Pi Desktop on a USB stick and helped him to configure DHCPv6 using Debian with Raspberry Pi Desktop on the USB stick in live persistent mode. Almost everything worked out perfectly at the end apart from the DHCPv6 server did not start automatically at boot but had to be manually restarted. Posted the issue on Raspberry forum but received no real feedback:

https://www.raspberrypi.org/forums/viewtopic.php?f=116&t=274573

Based on this success now, I work with another RF engineer in an RF lab in California where we also need to provide DHCPv6 address for a Docsis remote PHY device. We have agreed to accomplish this with a Raspberry Pi this time so that it can stay there permanently. I will provide remote support via ssh tunnel through my free Google Cloud VM instance based on this article:

https://medium.com/jj-innovative-results/how-to-access-a-raspberry-pi-anywhere-with-reverse-ssh-and-google-cloud-platform-59b6a89501a