C++ IPv6 Ping Code Example

My previous code example for IPv4 needed a bunch of modifications to work for an IPv6 address. The thing that took me the longest to figure out was that because IPv6 seems to send a lot more ICMP messages on the local network, I needed to filter the response messages to only the type I was listening for.

bool send_ping6(const std::string& ping_ip, const std::string& HostName4Output, const bool bOutput = false)
{
    bool rval = false;
    if (bOutput)
        std::cout << "[" << getTimeExcelLocal() << "] " << "send_ping6(" << ping_ip << ", " << HostName4Output << ");" << std::endl;
    struct timespec tfs;
    clock_gettime(CLOCK_MONOTONIC, &tfs);
    auto ping_sockfd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
    if (ping_sockfd < 0)
    {
        if (bOutput)
            std::cout << "[" << getTimeExcelLocal() << "] " << "Socket file descriptor not received!!" << std::endl;
    }
    else
    {
        // set socket options at ip to TTL and value to 64,
        // change to what you want by setting ttl_val
        int ttl_val = 64;
        if (setsockopt(ping_sockfd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl_val, sizeof(ttl_val)) != 0)
        {
            if (bOutput)
                std::cerr << "[" << getTimeExcelLocal() << "] " << "Setting socket options to TTL failed!" << std::endl;
        }
        else
        {
            struct icmp6_filter filt;
            ICMP6_FILTER_SETBLOCKALL(&filt);
            ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
            setsockopt(ping_sockfd, IPPROTO_ICMPV6, ICMP6_FILTER, &filt, sizeof(filt));

            // setting timeout of recv setting
            struct timeval tv_out;
            tv_out.tv_sec = RECV_TIMEOUT;
            tv_out.tv_usec = 0;
            setsockopt(ping_sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv_out, sizeof(tv_out));

            int msg_count = 0;
            int flag = 1;
            int msg_received_count = 0;
            // send icmp packet in a loop
            for (auto pingloop = 4; pingloop > 0; pingloop--)
            {
                // flag is whether packet was sent or not
                flag = 1;

                //filling packet
                struct ping_pkt pckt;
                bzero(&pckt, sizeof(pckt));
                for (auto i = 0; i < sizeof(pckt.msg) - 1; i++)
                    pckt.msg[i] = i + '0';
                pckt.msg[sizeof(pckt.msg) - 1] = 0;
                pckt.hdr.type = ICMP6_ECHO_REQUEST;
                pckt.hdr.un.echo.id = getpid();
                pckt.hdr.un.echo.sequence = msg_count++;
                pckt.hdr.checksum = checksum(&pckt, sizeof(pckt));

                usleep(PING_SLEEP_RATE);

                struct timespec time_start;
                clock_gettime(CLOCK_MONOTONIC, &time_start);

                struct sockaddr_in6 ping_addr;
                ping_addr.sin6_family = AF_INET6;
                ping_addr.sin6_port = htons(0);
                inet_pton(AF_INET6, ping_ip.c_str(), &ping_addr.sin6_addr);
                if (sendto(ping_sockfd, &pckt, sizeof(pckt), 0, (struct sockaddr*)&ping_addr, sizeof(ping_addr)) <= 0)
                {
                    if (bOutput)
                        std::cout << "[" << getTimeExcelLocal() << "] " << "Packet Sending Failed!" << std::endl;
                    flag = 0;
                }

                //receive packet
                struct sockaddr_in6 r_addr;
                auto addr_len = sizeof(r_addr);
                if (recvfrom(ping_sockfd, &pckt, sizeof(pckt), 0, (struct sockaddr*)&r_addr, (socklen_t*)&addr_len) <= 0 && msg_count > 1)
                {
                    if (bOutput)
                        std::cout << "[" << getTimeExcelLocal() << "] " << "Packet receive failed!" << std::endl;
                }
                else
                {
                    struct timespec time_end;
                    clock_gettime(CLOCK_MONOTONIC, &time_end);

                    double timeElapsed = ((double)(time_end.tv_nsec - time_start.tv_nsec)) / 1000000.0;
                    long double rtt_msec = (time_end.tv_sec - time_start.tv_sec) * 1000.0 + timeElapsed;

                    // if packet was not sent, don't receive
                    if (flag)
                    {
                        char szAddr[NI_MAXHOST] = { 0 };
                        inet_ntop(AF_INET6, &r_addr.sin6_addr, szAddr, sizeof(szAddr));
                        if (!(pckt.hdr.type == ICMP6_ECHO_REPLY && pckt.hdr.code == 0))
                        {
                            if (bOutput)
                                std::cout << "[" << getTimeExcelLocal() << "] " << "Error..Packet received from (" << szAddr << ") with ICMP type " << int(pckt.hdr.type) << " code " << int(pckt.hdr.code) << std::endl;
                        }
                        else
                        {
                            if (bOutput)
                                std::cout << "[" << getTimeExcelLocal() << "] " << PING_PKT_S << " bytes from (" << szAddr << ") (" << HostName4Output << ") msg_seq=" << msg_count << " ttl=" << "ttl_val" << " rtt= " << rtt_msec << " ms." << std::endl;
                            msg_received_count++;
                        }
                    }
                }
            }
            rval = msg_received_count > 0;
            struct timespec tfe;
            clock_gettime(CLOCK_MONOTONIC, &tfe);
            double timeElapsed = ((double)(tfe.tv_nsec - tfs.tv_nsec)) / 1000000.0;
            long double total_msec = (tfe.tv_sec - tfs.tv_sec) * 1000.0 + timeElapsed;
            if (bOutput)
                std::cout << "[" << getTimeExcelLocal() << "] " << "=== " << ping_ip << " ping statistics === " << msg_count << " packets sent, " << msg_received_count << " packets received, " << ((msg_count - msg_received_count) / msg_count) * 100.0 << " percent packet loss. Total time : " << total_msec << " ms." << std::endl;
        }
        close(ping_sockfd);
    }
    return(rval);
}

Because my calling routine is keeping the addresses for the hosts as strings, I’m calling each of these routines with those strings and converting them to proper addresses inside the function. I’m making a simple choice of whether it’s an IPv4 address or an IPv6 address by the fact that IPv4 addresses have “.” in them and IPv6 addresses have “:”.

bool send_ping(const std::string& ping_ip, const std::string& HostName4Output, const bool bOutput = false)
{
    bool rval = false;
    if (ping_ip.find('.') == std::string::npos)
        rval = send_ping6(ping_ip, HostName4Output, bOutput);
    else 
        rval = send_ping4(ping_ip, HostName4Output, bOutput);
    return(rval);
}

Here’s a bunch of links I found useful while creating this code:

More Cellular Modem Games

Testing possibilities with my cellular modem, I decided to try setting the PDP context to IPV6 instead of the IPV4V6 dual stack mode I had been running.

To do this, I sent these commands to the modem

AT+CGDCONT=1,"IPV6","h2g2"
AT+CGDCONT=6,"IPV6","h2g2"

I also had to configure dhcpc to not configure the usb0 interface with an IPv4 address. Because of the way the hardware works when I didn’t make the changes to dhcpd.conf an address IPv4 was still allocated to usb0 and a route was set up, but if I tried to ping an ipv4 address on the internet I got a Destination Net Unreachable error. I configured /etc/dhcpcd.conf so the last lines tell dhcpc to only configure the usb0 interface for ipv6.

interface usb0
ipv6only

Looking at the syslog details for usb0 after a reboot of the modem and the entire system

Nov 12 11:56:20 WimPiZeroW-Wim kernel: [   35.599281] rndis_host 1-1.1:1.0 usb0: register 'rndis_host' at usb-20980000.usb-1.1, RNDIS device, 0a:93:cc:8e:31:51
Nov 12 11:56:27 WimPiZeroW-Wim dhcpcd[252]: usb0: waiting for carrier
Nov 12 11:56:27 WimPiZeroW-Wim dhcpcd[252]: usb0: carrier acquired
Nov 12 11:56:27 WimPiZeroW-Wim dhcpcd[252]: usb0: IAID cc:8e:31:51
Nov 12 11:56:27 WimPiZeroW-Wim dhcpcd[252]: usb0: adding address fe80::64a:adc8:ebbe:d0ed
Nov 12 11:56:27 WimPiZeroW-Wim avahi-daemon[240]: Joining mDNS multicast group on interface usb0.IPv6 with address fe80::64a:adc8:ebbe:d0ed.
Nov 12 11:56:27 WimPiZeroW-Wim avahi-daemon[240]: New relevant interface usb0.IPv6 for mDNS.
Nov 12 11:56:27 WimPiZeroW-Wim avahi-daemon[240]: Registering new address record for fe80::64a:adc8:ebbe:d0ed on usb0.*.
Nov 12 11:56:27 WimPiZeroW-Wim dhcpcd[252]: usb0: soliciting an IPv6 router
Nov 12 11:56:28 WimPiZeroW-Wim dhcpcd[252]: usb0: Router Advertisement from fe80::7976:1565:680f:9a36
Nov 12 11:56:28 WimPiZeroW-Wim dhcpcd[252]: usb0: adding address 2607:fb90:8060:79e:d1b6:2c95:f26d:2e0c/64
Nov 12 11:56:28 WimPiZeroW-Wim kernel: [   43.860443] ICMPv6: process `dhcpcd' is using deprecated sysctl (syscall) net.ipv6.neigh.usb0.retrans_time - use net.ipv6.neigh.usb0.retrans_time_ms instead
Nov 12 11:56:28 WimPiZeroW-Wim avahi-daemon[240]: Leaving mDNS multicast group on interface usb0.IPv6 with address fe80::64a:adc8:ebbe:d0ed.
Nov 12 11:56:28 WimPiZeroW-Wim avahi-daemon[240]: Joining mDNS multicast group on interface usb0.IPv6 with address 2607:fb90:8060:79e:d1b6:2c95:f26d:2e0c.
Nov 12 11:56:28 WimPiZeroW-Wim avahi-daemon[240]: Registering new address record for 2607:fb90:8060:79e:d1b6:2c95:f26d:2e0c on usb0.*.
Nov 12 11:56:28 WimPiZeroW-Wim avahi-daemon[240]: Withdrawing address record for fe80::64a:adc8:ebbe:d0ed on usb0.
Nov 12 11:56:28 WimPiZeroW-Wim dhcpcd[252]: usb0: adding route to 2607:fb90:8060:79e::/64
Nov 12 11:56:28 WimPiZeroW-Wim dhcpcd[252]: usb0: requesting DHCPv6 information
Nov 12 11:56:28 WimPiZeroW-Wim dhcpcd[252]: usb0: adding default route via fe80::7976:1565:680f:9a36

An interesting side effect of only having IPv6 on the usb0 interface while allowing it elsewhere is that while my machine is also connected to my home network it automatically routes IPv4 traffic over the wlan0 interface.

wim@WimPiZeroW-Wim:~ $ ip -o a
1: lo    inet 127.0.0.1/8 scope host lo\       valid_lft forever preferred_lft forever
1: lo    inet6 ::1/128 scope host \       valid_lft forever preferred_lft forever
2: wlan0    inet 192.168.0.63/24 brd 192.168.0.255 scope global dynamic noprefixroute wlan0\       valid_lft 4230sec preferred_lft 3555sec
2: wlan0    inet6 2604:4080:1304:8010:57c0:7b33:ef3:3f35/64 scope global dynamic mngtmpaddr noprefixroute \       valid_lft 1789sec preferred_lft 1789sec
2: wlan0    inet6 fe80::2f9e:ceef:76a0:1efa/64 scope link \       valid_lft forever preferred_lft forever
3: usb0    inet6 2607:fb90:8060:79e:d1b6:2c95:f26d:2e0c/64 scope global mngtmpaddr noprefixroute \       valid_lft forever preferred_lft forever
3: usb0    inet6 fe80::64a:adc8:ebbe:d0ed/64 scope link \       valid_lft forever preferred_lft forever

IPv6 and Google Fi on SIM7600G-H and Raspberry Pi

I have been playing with a Raspberry Pi in a remote location that should be connected to a cellular network but I’ve had problems keeping it responding. I duplicated the system in my apartment to spend more time learning about what my options in configuring it may be. I’ve talked about the platform previously https://wimsworld.wordpress.com/2022/07/12/lte-wireless-on-sim7600g-h-4g-hat-b-for-raspberry-pi/ and https://wimsworld.wordpress.com/2022/07/16/more-networking-with-sim7600g-h-4g-hat-b-for-raspberry-pi/

The modem can be configured significantly differently via the CUSBPIDSWITCH command. According to the documentation, the available PIDS are:

  • 9000:Diag, NMEA, At, Modem, Audio, Rmnet
  • 9001:Diag, NMEA, At, Modem, Audio, Rmnet
  • 9002:Diag, NMEA, At, Modem, Audio, Rmnet
  • 9003:Diag, NMEA, At, Modem, Audio, MBIM
  • 9004:Diag, NMEA, At, Modem, Audio, GNSS, Rmnet
  • 9005:Diag, NMEA, At, Modem, Audio, GNSS, MBIM
  • 9006:Diag, NMEA, At,Modem
  • 9007:Diag, NMEA, At, Modem, Audio, Rmnet,mass_storage
  • 9011:RNDIS,Diag, NMEA, At, Modem, Audio
  • 9016:Diag, Rmnet
  • 9018:Diag, NMEA, At, Modem, Audio, Ecm
  • 9019:RNDIS
  • 901A: Diag, NMEA, At, Rmnet
  • 901B:NMEA, At, Rmnet
  • 9020: Diag, At, Modem
  • 9021: Diag, Modem
  • 9022: Diag, Modem, Rmnet
  • 9023: Modem
  • 9024: At, Modem
  • 9025: Modem,rmnet
  • 9026: Modem,Audio
  • 9027: Modem,Audio, Rmnet
  • 9028:Diag, Modem,Audio, Rmnet
  • 9029:Diag, Modem,Audio
  • 902A: At
  • 902B: Diag, NMEA, At, Modem, Rmnet,Usb-audio

If I query the modem possible modes, it responds with

AT+CUSBPIDSWITCH=?
+CUSBPIDSWITCH: (9000,9001,9002,9003,9004,9005,9006,9007,9011,9016,9018,9019,901A,901B,9020,9021,9022,9023,9024,9025,9026,9027,9028,9029,902A,902B),(0-1),(0-1)

OK

I’ve figured out I can switch between three different modem configurations on the linux system safely. 9001, 9011, and 9003. 9001 gives me qmi_wwan, 9011 gives me rndis_host, and 9003 give me cdc_mbim. RNDIS is the only one that is really plug and play on linux and produces the usb0 interface instead of wwan0. The method I’ve been configuring the device has been to connect to /dev/ttyUSB2 and issuing AT commands. When I have tried PID modes other than the ones I’ve called safe, Linux doesn’t provide /dev/ttyUSB* ports even though the device interfaces are displayed in the lsusb -t output.

Here’s what I get when I issue the command

AT+CUSBPIDSWITCH=9001,1,1

wim@WimPiZeroW-Wim:~ $ lsusb
Bus 001 Device 003: ID 1e0e:9001 Qualcomm / Option SimTech, Incorporated
Bus 001 Device 002: ID 1a40:0101 Terminus Technology Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
wim@WimPiZeroW-Wim:~ $ lsusb -t
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=dwc_otg/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M
        |__ Port 1: Dev 3, If 0, Class=Vendor Specific Class, Driver=option, 480M
        |__ Port 1: Dev 3, If 1, Class=Vendor Specific Class, Driver=option, 480M
        |__ Port 1: Dev 3, If 2, Class=Vendor Specific Class, Driver=option, 480M
        |__ Port 1: Dev 3, If 3, Class=Vendor Specific Class, Driver=option, 480M
        |__ Port 1: Dev 3, If 4, Class=Vendor Specific Class, Driver=option, 480M
        |__ Port 1: Dev 3, If 5, Class=Vendor Specific Class, Driver=qmi_wwan, 480M
wim@WimPiZeroW-Wim:~ $ ip -o a
1: lo    inet 127.0.0.1/8 scope host lo\       valid_lft forever preferred_lft forever
1: lo    inet6 ::1/128 scope host \       valid_lft forever preferred_lft forever
2: wwan0    inet 169.254.128.15/16 brd 169.254.255.255 scope global noprefixroute wwan0\       valid_lft forever preferred_lft forever
2: wwan0    inet6 fe80::f9:71:4f35:8ef4/64 scope link \       valid_lft forever preferred_lft forever
3: wlan0    inet 192.168.0.63/24 brd 192.168.0.255 scope global dynamic noprefixroute wlan0\       valid_lft 5335sec preferred_lft 4660sec
3: wlan0    inet6 2604:4080:1304:8010:57c0:7b33:ef3:3f35/64 scope global dynamic mngtmpaddr noprefixroute \       valid_lft 1766sec preferred_lft 1766sec
3: wlan0    inet6 fe80::2f9e:ceef:76a0:1efa/64 scope link \       valid_lft forever preferred_lft forever

AT+CUSBPIDSWITCH=9011,1,1

wim@WimPiZeroW-Wim:~ $ lsusb
Bus 001 Device 003: ID 1e0e:9011 Qualcomm / Option SimTech, Incorporated
Bus 001 Device 002: ID 1a40:0101 Terminus Technology Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
wim@WimPiZeroW-Wim:~ $ lsusb -t
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=dwc_otg/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M
        |__ Port 1: Dev 3, If 0, Class=Communications, Driver=rndis_host, 480M
        |__ Port 1: Dev 3, If 1, Class=CDC Data, Driver=rndis_host, 480M
        |__ Port 1: Dev 3, If 2, Class=Vendor Specific Class, Driver=option, 480M
        |__ Port 1: Dev 3, If 3, Class=Vendor Specific Class, Driver=option, 480M
        |__ Port 1: Dev 3, If 4, Class=Vendor Specific Class, Driver=option, 480M
        |__ Port 1: Dev 3, If 5, Class=Vendor Specific Class, Driver=option, 480M
        |__ Port 1: Dev 3, If 6, Class=Vendor Specific Class, Driver=option, 480M
wim@WimPiZeroW-Wim:~ $ ip -o a
1: lo    inet 127.0.0.1/8 scope host lo\       valid_lft forever preferred_lft forever
1: lo    inet6 ::1/128 scope host \       valid_lft forever preferred_lft forever
2: wlan0    inet 192.168.0.63/24 brd 192.168.0.255 scope global dynamic noprefixroute wlan0\       valid_lft 5371sec preferred_lft 4696sec
2: wlan0    inet6 2604:4080:1304:8010:57c0:7b33:ef3:3f35/64 scope global dynamic mngtmpaddr noprefixroute \       valid_lft 1767sec preferred_lft 1767sec
2: wlan0    inet6 fe80::2f9e:ceef:76a0:1efa/64 scope link \       valid_lft forever preferred_lft forever
3: usb0    inet 192.168.225.59/24 brd 192.168.225.255 scope global dynamic noprefixroute usb0\       valid_lft 43172sec preferred_lft 37772sec
3: usb0    inet6 2607:fb90:8069:ec7e:4a34:bb42:93a9:e27d/64 scope global mngtmpaddr noprefixroute \       valid_lft forever preferred_lft forever
3: usb0    inet6 fe80::b8ef:f02b:c580:1ebc/64 scope link \       valid_lft forever preferred_lft forever

AT+CUSBPIDSWITCH=9003,1,1

wim@WimPiZeroW-Wim:~ $ lsusb
Bus 001 Device 003: ID 1e0e:9003 Qualcomm / Option SimTech, Incorporated
Bus 001 Device 002: ID 1a40:0101 Terminus Technology Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
wim@WimPiZeroW-Wim:~ $ lsusb -t
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=dwc_otg/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M
        |__ Port 1: Dev 3, If 0, Class=Vendor Specific Class, Driver=option, 480M
        |__ Port 1: Dev 3, If 1, Class=Vendor Specific Class, Driver=option, 480M
        |__ Port 1: Dev 3, If 2, Class=Vendor Specific Class, Driver=option, 480M
        |__ Port 1: Dev 3, If 3, Class=Vendor Specific Class, Driver=option, 480M
        |__ Port 1: Dev 3, If 4, Class=Vendor Specific Class, Driver=option, 480M
        |__ Port 1: Dev 3, If 5, Class=Communications, Driver=cdc_mbim, 480M
        |__ Port 1: Dev 3, If 6, Class=CDC Data, Driver=cdc_mbim, 480M
wim@WimPiZeroW-Wim:~ $ ip -o a
1: lo    inet 127.0.0.1/8 scope host lo\       valid_lft forever preferred_lft forever
1: lo    inet6 ::1/128 scope host \       valid_lft forever preferred_lft forever
2: wwan0    inet 169.254.175.62/16 brd 169.254.255.255 scope global noprefixroute wwan0\       valid_lft forever preferred_lft forever
2: wwan0    inet6 fe80::b43f:d4a3:f985:4f33/64 scope link \       valid_lft forever preferred_lft forever
3: wlan0    inet 192.168.0.63/24 brd 192.168.0.255 scope global dynamic noprefixroute wlan0\       valid_lft 5341sec preferred_lft 4666sec
3: wlan0    inet6 2604:4080:1304:8010:57c0:7b33:ef3:3f35/64 scope global dynamic mngtmpaddr noprefixroute \       valid_lft 1764sec preferred_lft 1764sec
3: wlan0    inet6 fe80::2f9e:ceef:76a0:1efa/64 scope link \       valid_lft forever preferred_lft forever

I have configured the modem to run the way I want with the following commands

AT&F
AT+CUSBPIDSWITCH=9011,1,1
AT+CGDCONT=1,"IPV4V6","h2g2"
AT+CGDCONT=6,"IPV4V6","h2g2"
AT+CGPSAUTO=1

There are multiple things I’m trying to understand about networking while running in 9011 mode. It gets a global scope ipv6 address that is reachable, but that address changes on a very frequent but random time. It gets a local ipv4 address that works for traffic over the internet. Obviously the ipv4 address is being converted by NAT between the raspberry and the internet. I don’t seem to have any way of configuring how that works. The post https://www.jeffgeerling.com/blog/2022/using-4g-lte-wireless-modems-on-raspberry-pi had a Built-in AP configuration that was accessible via http. I have run nmap against the internal gateway on my platform and the only port open is DNS.

Today I ran radvdump to listen for router advertisements from the internet and then later looked at my syslog results to see what happened on the network at the corresponding times.

# radvd configuration generated by radvdump 2.18
# based on Router Advertisement from fe80::fca7:efa4:eef4:655b
# received by interface usb0
#

interface usb0
{
        AdvSendAdvert on;
        # Note: {Min,Max}RtrAdvInterval cannot be obtained with radvdump
        AdvManagedFlag off;
        AdvOtherConfigFlag off;
        AdvReachableTime 0;
        AdvRetransTimer 0;
        AdvCurHopLimit 255;
        AdvDefaultLifetime 0;
        AdvHomeAgentFlag off;
        AdvDefaultPreference medium;
        AdvSourceLLAddress on;
        AdvLinkMTU 1500;

        prefix 2607:fb90:80ca:7753::ff00:0/0
        {
                AdvValidLifetime 2;
                AdvPreferredLifetime 1;
                AdvOnLink on;
                AdvAutonomous on;
                AdvRouterAddr off;
        }; # End of prefix definition

}; # End of interface definition


Nov 11 16:11:32 WimPiZeroW-Wim dhcpcd[701]: usb0: Router Advertisement from fe80::fca7:efa4:eef4:655b
Nov 11 16:11:32 WimPiZeroW-Wim dhcpcd[701]: usb0: adding address 5c5a:870:6182:d190:617e:1551:4cea:f6b6/0
Nov 11 16:11:32 WimPiZeroW-Wim dhcpcd[701]: ipv6_addaddr1: Invalid argument
Nov 11 16:11:32 WimPiZeroW-Wim dhcpcd[701]: usb0: adding route to 2607:fb90:80ca:7753::ff00:0/0
Nov 11 16:11:32 WimPiZeroW-Wim dhcpcd[701]: if_route (ADD): File exists

Almost immediately afterwards:

#
# radvd configuration generated by radvdump 2.18
# based on Router Advertisement from fe80::fc89:6dff:fe67:6dde
# received by interface usb0
#

interface usb0
{
        AdvSendAdvert on;
        # Note: {Min,Max}RtrAdvInterval cannot be obtained with radvdump
        AdvManagedFlag off;
        AdvOtherConfigFlag off;
        AdvReachableTime 0;
        AdvRetransTimer 0;
        AdvCurHopLimit 64;
        AdvDefaultLifetime 0;
        AdvHomeAgentFlag off;
        AdvDefaultPreference medium;
        AdvLinkMTU 1500;

        prefix 2607:fb90:80ca:7753::/64
        {
                AdvValidLifetime 2;
                AdvPreferredLifetime 1;
                AdvOnLink on;
                AdvAutonomous on;
                AdvRouterAddr off;
        }; # End of prefix definition


        RDNSS fd00:976a::9 fd00:976a::10
        {
                AdvRDNSSLifetime infinity; # (0xffffffff)
        }; # End of RDNSS definition

}; # End of interface definition

Nov 11 16:11:33 WimPiZeroW-Wim dhcpcd[701]: usb0: Router Advertisement from fe80::fc89:6dff:fe67:6dde
Nov 11 16:11:33 WimPiZeroW-Wim dhcpcd[701]: usb0: fe80::fc89:6dff:fe67:6dde: no longer a default router
Nov 11 16:11:33 WimPiZeroW-Wim dhcpcd[701]: usb0: adding route to 2607:fb90:80ca:7753::ff00:0/0
Nov 11 16:11:33 WimPiZeroW-Wim dhcpcd[701]: if_route (ADD): File exists
Nov 11 16:11:33 WimPiZeroW-Wim dhcpcd[701]: usb0: deleting default route via fe80::fc89:6dff:fe67:6dde
Nov 11 16:11:34 WimPiZeroW-Wim dhcpcd[701]: usb0: part of Router Advertisement expired
Nov 11 16:11:34 WimPiZeroW-Wim avahi-daemon[240]: Registering new address record for fe80::a3b3:ec14:47de:4692 on usb0.*.
Nov 11 16:11:35 WimPiZeroW-Wim dhcpcd[701]: usb0: expired address 2607:fb90:80ca:7753:ee71:7d12:766:2e98/64
Nov 11 16:11:35 WimPiZeroW-Wim dhcpcd[701]: usb0: part of Router Advertisement expired
Nov 11 16:11:35 WimPiZeroW-Wim dhcpcd[701]: usb0: deleting route to 2607:fb90:80ca:7753::/64
Nov 11 16:11:35 WimPiZeroW-Wim avahi-daemon[240]: Withdrawing address record for 2607:fb90:80ca:7753:ee71:7d12:766:2e98 on usb0.
Nov 11 16:11:35 WimPiZeroW-Wim avahi-daemon[240]: Leaving mDNS multicast group on interface usb0.IPv6 with address 2607:fb90:80ca:7753:ee71:7d12:766:2e98.
Nov 11 16:11:35 WimPiZeroW-Wim avahi-daemon[240]: Joining mDNS multicast group on interface usb0.IPv6 with address fe80::a3b3:ec14:47de:4692.

And a couple of minutes later:

# radvd configuration generated by radvdump 2.18
# based on Router Advertisement from fe80::fc89:6dff:fe67:6dde
# received by interface usb0
#

interface usb0
{
        AdvSendAdvert on;
        # Note: {Min,Max}RtrAdvInterval cannot be obtained with radvdump
        AdvManagedFlag off;
        AdvOtherConfigFlag on;
        AdvReachableTime 0;
        AdvRetransTimer 0;
        AdvCurHopLimit 64;
        AdvDefaultLifetime 65535;
        AdvHomeAgentFlag off;
        AdvDefaultPreference medium;
        AdvLinkMTU 1500;

        prefix 2607:fb90:80c8:82f8::/64
        {
                AdvValidLifetime infinity; # (0xffffffff)
                AdvPreferredLifetime infinity; # (0xffffffff)
                AdvOnLink on;
                AdvAutonomous on;
                AdvRouterAddr off;
        }; # End of prefix definition


        RDNSS fd00:976a::9 fd00:976a::10
        {
                AdvRDNSSLifetime infinity; # (0xffffffff)
        }; # End of RDNSS definition

}; # End of interface definition

Nov 11 16:13:42 WimPiZeroW-Wim dhcpcd[701]: usb0: Router Advertisement from fe80::fc89:6dff:fe67:6dde
Nov 11 16:13:42 WimPiZeroW-Wim dhcpcd[701]: usb0: adding address 2607:fb90:80c8:82f8:b87a:2c7a:ae2e:7e0e/64
Nov 11 16:13:42 WimPiZeroW-Wim avahi-daemon[240]: Leaving mDNS multicast group on interface usb0.IPv6 with address fe80::a3b3:ec14:47de:4692.
Nov 11 16:13:43 WimPiZeroW-Wim avahi-daemon[240]: Joining mDNS multicast group on interface usb0.IPv6 with address 2607:fb90:80c8:82f8:b87a:2c7a:ae2e:7e0e.
Nov 11 16:13:43 WimPiZeroW-Wim avahi-daemon[240]: Registering new address record for 2607:fb90:80c8:82f8:b87a:2c7a:ae2e:7e0e on usb0.*.
Nov 11 16:13:43 WimPiZeroW-Wim avahi-daemon[240]: Withdrawing address record for fe80::a3b3:ec14:47de:4692 on usb0.
Nov 11 16:13:43 WimPiZeroW-Wim dhcpcd[701]: usb0: adding route to 2607:fb90:80c8:82f8::/64
Nov 11 16:13:43 WimPiZeroW-Wim dhcpcd[701]: usb0: adding default route via fe80::fc89:6dff:fe67:6dde

Then it went almost a couple of hours before more changes to the network

Nov 11 18:01:54 WimPiZeroW-Wim dhcpcd[701]: usb0: Router Advertisement from fe80::c1e7:222d:69eb:c90c
Nov 11 18:01:54 WimPiZeroW-Wim dhcpcd[701]: usb0: adding address 5c5a:870:6182:d190:617e:1551:4cea:f6b6/0
Nov 11 18:01:54 WimPiZeroW-Wim dhcpcd[701]: ipv6_addaddr1: Invalid argument
Nov 11 18:01:54 WimPiZeroW-Wim dhcpcd[701]: usb0: adding route to 2607:fb90:80c8:82f8::ff00:0/0
Nov 11 18:01:54 WimPiZeroW-Wim dhcpcd[701]: if_route (ADD): File exists
Nov 11 18:01:55 WimPiZeroW-Wim dhcpcd[701]: usb0: Router Advertisement from fe80::fc89:6dff:fe67:6dde
Nov 11 18:01:55 WimPiZeroW-Wim dhcpcd[701]: usb0: fe80::fc89:6dff:fe67:6dde: no longer a default router
Nov 11 18:01:55 WimPiZeroW-Wim dhcpcd[701]: usb0: adding route to 2607:fb90:80c8:82f8::ff00:0/0
Nov 11 18:01:55 WimPiZeroW-Wim dhcpcd[701]: if_route (ADD): File exists
Nov 11 18:01:55 WimPiZeroW-Wim dhcpcd[701]: usb0: deleting default route via fe80::fc89:6dff:fe67:6dde
Nov 11 18:01:56 WimPiZeroW-Wim dhcpcd[701]: usb0: part of Router Advertisement expired
Nov 11 18:01:56 WimPiZeroW-Wim avahi-daemon[240]: Registering new address record for fe80::a3b3:ec14:47de:4692 on usb0.*.
Nov 11 18:01:57 WimPiZeroW-Wim dhcpcd[701]: usb0: expired address 2607:fb90:80c8:82f8:b87a:2c7a:ae2e:7e0e/64
Nov 11 18:01:57 WimPiZeroW-Wim dhcpcd[701]: usb0: part of Router Advertisement expired
Nov 11 18:01:57 WimPiZeroW-Wim dhcpcd[701]: usb0: deleting route to 2607:fb90:80c8:82f8::/64
Nov 11 18:01:57 WimPiZeroW-Wim avahi-daemon[240]: Withdrawing address record for 2607:fb90:80c8:82f8:b87a:2c7a:ae2e:7e0e on usb0.
Nov 11 18:01:57 WimPiZeroW-Wim avahi-daemon[240]: Leaving mDNS multicast group on interface usb0.IPv6 with address 2607:fb90:80c8:82f8:b87a:2c7a:ae2e:7e0e.
Nov 11 18:01:57 WimPiZeroW-Wim avahi-daemon[240]: Joining mDNS multicast group on interface usb0.IPv6 with address fe80::a3b3:ec14:47de:4692.
Nov 11 18:04:04 WimPiZeroW-Wim dhcpcd[701]: usb0: Router Advertisement from fe80::fc89:6dff:fe67:6dde
Nov 11 18:04:04 WimPiZeroW-Wim dhcpcd[701]: usb0: adding address 2607:fb90:80c3:42cc:11b:d753:e52a:40bb/64
Nov 11 18:04:04 WimPiZeroW-Wim avahi-daemon[240]: Leaving mDNS multicast group on interface usb0.IPv6 with address fe80::a3b3:ec14:47de:4692.
Nov 11 18:04:04 WimPiZeroW-Wim avahi-daemon[240]: Joining mDNS multicast group on interface usb0.IPv6 with address 2607:fb90:80c3:42cc:11b:d753:e52a:40bb.
Nov 11 18:04:04 WimPiZeroW-Wim avahi-daemon[240]: Registering new address record for 2607:fb90:80c3:42cc:11b:d753:e52a:40bb on usb0.*.
Nov 11 18:04:04 WimPiZeroW-Wim avahi-daemon[240]: Withdrawing address record for fe80::a3b3:ec14:47de:4692 on usb0.
Nov 11 18:04:04 WimPiZeroW-Wim dhcpcd[701]: usb0: adding route to 2607:fb90:80c3:42cc::/64
Nov 11 18:04:04 WimPiZeroW-Wim dhcpcd[701]: usb0: adding default route via fe80::fc89:6dff:fe67:6dde
Nov 11 18:29:11 WimPiZeroW-Wim dhcpcd[701]: usb0: failed to renew DHCP, rebinding
Nov 11 18:29:11 WimPiZeroW-Wim dhcpcd[701]: usb0: adding route to 192.168.225.0/24
Nov 11 18:29:11 WimPiZeroW-Wim dhcpcd[701]: usb0: adding default route via 192.168.225.1
ov 11 19:52:16 WimPiZeroW-Wim dhcpcd[701]: usb0: Router Advertisement from fe80::459a:7427:b0ae:7848
Nov 11 19:52:16 WimPiZeroW-Wim dhcpcd[701]: usb0: adding address 5c5a:870:6182:d190:617e:1551:4cea:f6b6/0
Nov 11 19:52:16 WimPiZeroW-Wim dhcpcd[701]: ipv6_addaddr1: Invalid argument
Nov 11 19:52:16 WimPiZeroW-Wim dhcpcd[701]: usb0: adding route to 2607:fb90:80c3:42cc::ff00:0/0
Nov 11 19:52:16 WimPiZeroW-Wim dhcpcd[701]: if_route (ADD): File exists
Nov 11 19:52:18 WimPiZeroW-Wim dhcpcd[701]: usb0: Router Advertisement from fe80::fc89:6dff:fe67:6dde
Nov 11 19:52:18 WimPiZeroW-Wim dhcpcd[701]: usb0: fe80::fc89:6dff:fe67:6dde: no longer a default router
Nov 11 19:52:18 WimPiZeroW-Wim dhcpcd[701]: usb0: adding route to 2607:fb90:80c3:42cc::ff00:0/0
Nov 11 19:52:18 WimPiZeroW-Wim dhcpcd[701]: if_route (ADD): File exists
Nov 11 19:52:18 WimPiZeroW-Wim dhcpcd[701]: usb0: deleting default route via fe80::fc89:6dff:fe67:6dde
Nov 11 19:52:18 WimPiZeroW-Wim dhcpcd[701]: usb0: part of Router Advertisement expired
Nov 11 19:52:19 WimPiZeroW-Wim avahi-daemon[240]: Registering new address record for fe80::a3b3:ec14:47de:4692 on usb0.*.
Nov 11 19:52:20 WimPiZeroW-Wim dhcpcd[701]: usb0: expired address 2607:fb90:80c3:42cc:11b:d753:e52a:40bb/64
Nov 11 19:52:20 WimPiZeroW-Wim dhcpcd[701]: usb0: part of Router Advertisement expired
Nov 11 19:52:20 WimPiZeroW-Wim dhcpcd[701]: usb0: deleting route to 2607:fb90:80c3:42cc::/64
Nov 11 19:52:20 WimPiZeroW-Wim avahi-daemon[240]: Withdrawing address record for 2607:fb90:80c3:42cc:11b:d753:e52a:40bb on usb0.
Nov 11 19:52:20 WimPiZeroW-Wim avahi-daemon[240]: Leaving mDNS multicast group on interface usb0.IPv6 with address 2607:fb90:80c3:42cc:11b:d753:e52a:40bb.
Nov 11 19:52:20 WimPiZeroW-Wim avahi-daemon[240]: Joining mDNS multicast group on interface usb0.IPv6 with address fe80::a3b3:ec14:47de:4692.
Nov 11 19:52:27 WimPiZeroW-Wim systemd[1]: session-3.scope: Succeeded.
Nov 11 19:52:27 WimPiZeroW-Wim systemd[1]: session-3.scope: Consumed 24.275s CPU time.
Nov 11 19:54:27 WimPiZeroW-Wim dhcpcd[701]: usb0: Router Advertisement from fe80::fc89:6dff:fe67:6dde
Nov 11 19:54:27 WimPiZeroW-Wim dhcpcd[701]: usb0: adding address 2607:fb90:8069:47ff:69ad:1e37:6fb5:7fa0/64
Nov 11 19:54:27 WimPiZeroW-Wim dhcpcd[701]: usb0: adding route to 2607:fb90:8069:47ff::/64
Nov 11 19:54:27 WimPiZeroW-Wim dhcpcd[701]: usb0: adding default route via fe80::fc89:6dff:fe67:6dde
Nov 11 19:54:27 WimPiZeroW-Wim avahi-daemon[240]: Leaving mDNS multicast group on interface usb0.IPv6 with address fe80::a3b3:ec14:47de:4692.
Nov 11 19:54:27 WimPiZeroW-Wim avahi-daemon[240]: Joining mDNS multicast group on interface usb0.IPv6 with address 2607:fb90:8069:47ff:69ad:1e37:6fb5:7fa0.
Nov 11 19:54:27 WimPiZeroW-Wim avahi-daemon[240]: Registering new address record for 2607:fb90:8069:47ff:69ad:1e37:6fb5:7fa0 on usb0.*.
Nov 11 19:54:27 WimPiZeroW-Wim avahi-daemon[240]: Withdrawing address record for fe80::a3b3:ec14:47de:4692 on usb0.

I do not understand why it gets router advertisements that change as frequently as it does, and especially why sometimes it gets a /0 prefix occasionally.

Here is a series of commands I’ve found useful to checking status on the modem

AT&V
AT+COPS?
AT+CPSI?
AT+CFUN?
AT+CGPSAUTO?
AT+CSQ
AT+CUSBPIDSWITCH=?
AT+CUSBPIDSWITCH?
AT+CGDCONT?

The command AT+CSQ returns the signal strength of the device. Possible values are:

  • 0 113 dBm or less
  • 1 111 dBm
  • 2…30 109… 53 dBm
  • 31 51 dBm or greater
  • 99 not known or not detectable

Value RSSI dBm Condition

  • 2 -109 Marginal
  • 3 -107 Marginal
  • 4 -105 Marginal
  • 5 -103 Marginal
  • 6 -101 Marginal
  • 7 -99 Marginal
  • 8 -97 Marginal
  • 9 -95 Marginal
  • 10 -93 OK
  • 11 -91 OK
  • 12 -89 OK
  • 13 -87 OK
  • 14 -85 OK
  • 15 -83 Good
  • 16 -81 Good
  • 17 -79 Good
  • 18 -77 Good
  • 19 -75 Good
  • 20 -73 Excellent
  • 21 -71 Excellent
  • 22 -69 Excellent
  • 23 -67 Excellent
  • 24 -65 Excellent
  • 25 -63 Excellent
  • 26 -61 Excellent
  • 27 -59 Excellent
  • 28 -57 Excellent
  • 29 -55 Excellent
  • 30 -53 Excellent

Some of the values on my system

AT&V
&C: 2; &D: 2; &E: 1; &F: 0; &S: 0; &W: 0; E: 1; L: 0; M: 0; Q: 0; V: 1;
X: 1; Z: 0; \Q: 3; \S: 0; \V: 0; O: 0; S0: 0; S2: 43; S3: 13; S4: 10;
S5: 8; S6: 2; S7: 0; S8: 2; S9: 6; S10: 14; S11: 95; S30: 0; S103: 1;
S104: 1; +FCLASS: 0; +ICF: 3,3; +IFC: 0,0; +IPR: 115200; +DR: 0;
+DS: 0,0,2048,6; +CMEE: 2; +WS46: 25; +CFUN:; +IPREX: 115200;
+CBST: 0,0,1; +CRLP: (61,61,48,6,0),(61,61,48,6,1),(240,240,52,6,2);
+CV120: 1,1,1,0,0,0; +CHSN: 0,0,0,0; +CSSN: 0,0; +CREG: 0; +CGREG: 0;
+CEREG: 0; +CSCS: "IRA"; +CSTA: 129;  +CR: 0; +CRC: 0;
+CGDCONT: (1,"IPV4V6","h2g2","0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",0,0,0,0),(2,"IPV4V6","","0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",0,0,0,0),(3,"IPV4V6","","0.0.0.0.0.0.0;
+CGDSCONT: ; +CGTFT: ; +CGEQREQ: ; +CGEQMIN: ; +CGEQOS: ; +CGQREQ: ;
+CGQMIN: ; +CGEREP: 0,0; +CGDATA: "PPP"; +CGCLASS: "A";  +CGPIAF: 0,0,0,0;
+CGSMS: 1; +CSMS: 0; +CMGF: 0; +CSAS: 0; +CRES: 0; +CSCA: "",;
+CSMP: ,,0,0; +CSDH: 0; +CSCB: 0,"",""; +CMGP: 4098,0,,0,; +ES: ,,;
+ESA: 0,,,,0,0,255,; +CMOD: 0; +CEMODE: 2; +CVHU: 1; ; ; ; ; ; ;
+CSCLK: 0; +CCUART: 0; +CFGRI: 0,60,120; +CUARTSD: 500; +CURCD: 0;
+CUARTLOG: 0; +CUARTRM: 0; +CFGRIDTM: 60,120; +CATR: 0; +CNMP: 2;
+CNRP: 255; +CNAOP: 0; +CNSDP: 2; +CNSMOD: 0; +CNLSA: 0; +CEXTEPLMN: 1;
+CSQFMT: 1; +AUTOCSQ: 0,0; +CSQDELTA: 5; +CPSI: 0; +CMGSI: 2;
+CMGRMI: 4,0; +MONI: 0; +CRUPSI: 0;  +CRUSET: 0; +CGPS: 1,1; +CGPSMSB: 1;
+CGPSINFO: 0; +CGPSINFOCFG: 0,0,0; +CGPSHOR: 50; +CGPSPMD: 65407;
+CGPSXD: 0; +CGNSSINFO: 0; +CSOCKAUTH: 1,0,""; +CGAUTH: 1,0,""; ;
+CIPHEAD: 1; +CIPSRIP: 1; +CIPCCFG: 10,0,0,1,0,0,500; +CIPENSRXGET: 0;
+CIPMODE: 0; +CPIN: ÿÿÿÿÿÿÿÿ,ÿÿÿÿÿÿÿÿ; +CMEC: 0,0,0,0;
+CIND: 0,3,1,1,0,0,1,0; +CMER: 0,0,0,0,0; +CGATT: 1;
+CGACT(1,1),(2,0),(3,0),(4,0),(5,0),(6,0); +CPBS: "SM";
+CPMS: "SM","SM","SM"; +CNMI: 2,1,0,0,0; +CMMS: 0; +CCUG: 0,0,0;
+COPS: 0,0,""; +CUSD: 0; +CAOC: 1; +CCWA: 0;  +CPOL: 0,2,"",0,0,0,0;
+CPLS: 0; +CTZR: 0; +CTZU: 0; +CLIP: 0; +COLP: 0; +CDIP: 0; +CLIR: 0;
+CSDF: 1; +CEN: 0; +CPSMS: 0,"","","",""; +CEDRXS: 0,1,""; +CCARDMA: 0;
+STK: 0; +CSALPHA: 0; +MORING: 0; +CWAKEUPSMSSWITCH: 0;
+CFILTERSMSSWITCH: 0; +EB: 1,0,30; +EFCS: 1; +ER: 0; +ESR: 1;
+ETBM: 1,1,20; +MA: ; +MR: 0; +MS: ; +MV18R: 0; +MV18S: 0,0,0; +CXT: 0;
+CDR: 0; +CDS: 0,1,2048,6; +CFC: 0; +CFG: ""; +CQD: 10; +CCRC: 0;
+QCMUX: C,2; +CTA: 0; +ILRR: 0; +QCPIN: ,; *CNTI: 0; ^PREFMODE: 0;
^DSCI: 0; ^MODE: 0;  ^CPIN: ,

OK
AT+COPS?
+COPS: 0,0,"Google Fi",7

OK
AT+CPSI?
+CPSI: LTE,Online,310-260,0x2C13,45023373,3,EUTRAN-BAND41,39874,5,5,-68,-947,-680,16

OK
AT+CGDCONT?
+CGDCONT: 1,"IPV4V6","h2g2","0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",0,0,0,0
+CGDCONT: 2,"IPV4V6","","0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",0,0,0,0
+CGDCONT: 3,"IPV4V6","","0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",0,0,0,0
+CGDCONT: 4,"IPV4V6","","0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",0,0,0,0
+CGDCONT: 5,"IPV4V6","","0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",0,0,0,0
+CGDCONT: 6,"IPV4V6","h2g2","0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",0,0,0,0

OK
AT+CSQ
+CSQ: 22,99

OK

If you have any suggestions to make my networking more stable, I would appreciate any help. When I tried these two PID modes I lost access to the ttyUSB ports and had to connect the device to a windows machine, force install questionable drivers for the device and then issue commands to reset the modem back to a PID mode I could work with. I would also appreciate any information on how to send AT commands to a USB port/device/interface if the operating system didn’t install tty drivers to make it easily accessible.

AT+CUSBPIDSWITCH=9002,1,1

wim@WimPiZeroW-Wim:~ $ lsusb -t
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=dwc_otg/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M
        |__ Port 1: Dev 4, If 0, Class=Vendor Specific Class, Driver=, 480M
        |__ Port 1: Dev 4, If 1, Class=Vendor Specific Class, Driver=, 480M
        |__ Port 1: Dev 4, If 2, Class=Vendor Specific Class, Driver=, 480M
        |__ Port 1: Dev 4, If 3, Class=Vendor Specific Class, Driver=, 480M
        |__ Port 1: Dev 4, If 4, Class=Vendor Specific Class, Driver=, 480M
        |__ Port 1: Dev 4, If 5, Class=Vendor Specific Class, Driver=, 480M

AT+CUSBPIDSWITCH=9018,1,1

wim@WimPiZeroW-Wim:~ $ lsusb
Bus 001 Device 003: ID 1e0e:9018 Qualcomm / Option SimTech, Incorporated
Bus 001 Device 002: ID 1a40:0101 Terminus Technology Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
wim@WimPiZeroW-Wim:~ $ lsusb -t
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=dwc_otg/1p, 480M
    |__ Port 1: Dev 2, If 0, Class=Hub, Driver=hub/4p, 480M
        |__ Port 1: Dev 3, If 0, Class=Vendor Specific Class, Driver=, 480M
        |__ Port 1: Dev 3, If 1, Class=Vendor Specific Class, Driver=, 480M
        |__ Port 1: Dev 3, If 2, Class=Vendor Specific Class, Driver=, 480M
        |__ Port 1: Dev 3, If 3, Class=Vendor Specific Class, Driver=, 480M
        |__ Port 1: Dev 3, If 4, Class=Vendor Specific Class, Driver=, 480M
        |__ Port 1: Dev 3, If 5, Class=Communications, Driver=cdc_ether, 480M
        |__ Port 1: Dev 3, If 6, Class=CDC Data, Driver=cdc_ether, 480M

IPv6 DDWRT and Astound Broadband

My apartment has three options for high speed internet. Centurylink, Xfinity, and Astound Broadband. When I moved in I chose Wave Broadband because I’d had good experience in the past with Condointernet and it later became Wave Broadband. Now they seem to be going with the moniker Astound Broadband powered by Wave. One of the features that I’ve used in the past and am using now is their support of IPv6.

I’ve had occasional hiccups in my networking, most often recently the IPv4 networking stops working while the IPv6 network is still working. It has some interesting effects, such as my browser connected to gmail.com working fine but hitting a link on a web page in an email that goes to an IPv4 only address not working. Usually the problem can be fixed by connecting to my router and having it release its external IPv4 DHCP address and requesting a new lease. Occasionally there are problems on the ISP side of things that make things more problematic. Last week when I was having problems, I managed to mess up my IPv6 configuration and then spent a long time figuring out what the problem was. I’m writing this up mainly as a suggestion for a reliable working configuration.

I’m running DDWRT on a Netgear R7000 router. It’s somewhat strange, as it seems there’s no real release, only betas. It explicitly says go look at the directory structure and “DO NOT USE THE ROUTER DATABASE.” I’ve run it in various incarnations for well over a decade.

DD-WRT Basic Setup

The Basic IPv4 page is very standard, naming my local network and router and picking the IPv4 address range I want to use locally. I’ve generally been using the same address block since they were defined in RFC1918, though that’s been superseded by newer RFC’s. Wikipedia has a good description of private address space.

DD-WRT IPv6 Setup

I’m enabling IPv6 and seelcting DHCPv6 with Prefix Delegation. I’ve left all of the other items on this page either blank or at their defaults.

The way IPv6 hosts generally allocate global addresses in stateless mode is that they recognize router advertisements arriving on the network port and configure their own address based on information received. They then broadcast locally to make sure there are no address collisions. This may be a significant oversimplification, but it’s a useful one.

Initially I had the Router Advertisement Daemon enabled on this page but have since changed to use DNSMASQ for router advertisements since it’s already doing so much of the work on the router. Part of the reason I stopped using radvd was also based on the fact that I was trying to add two static DNS entries for IPv6 in the fields on this page. I was adding the google IPv6 DNS entries of 2001:4860:4860::8888 and 2001:4860:4860::8844. The problem I was running into was that DDWRT was creating a radvd.conf file that included the two static DNS servers I listed while also including the DNS servers inherited from the advertisement it received from my ISP router. Radvd was seeing more than three DNS servers and exiting with an error. This meant that no router advertisements were being broadcast on my local network. I’d been looking at the configuration of the radvd deamon with the command cat /tmp/radvd.conf in my terminal window. I asked questions about this over on the DDWRT forums as well as opening an issue with the radvd software itself. The slightly unhelpful answer to my question told me to use dnsmasq for router advertisements. I’d tried that by adding the enable-ra option but never succeeded in having my machines get global IPv6 addresses.

DD-WRT Services DNSMasq Additional Options

I finally came across a set of options that I needed to get dnsmasq to broadcast router advertisements that are recognized by hosts on my local network to have them configure IPv6 global addresses.

dhcp-range=::1000,::FFFF,constructor:br0,ra-stateless,5m
ra-param=br0,10,300
enable-ra

In the terminal window of DDWRT I can dump the contents of the dnsmasq configuration file with the command cat /tmp/dnsmasq.conf. In doing so, I see what DDWRT writes to the configuration followed by my own customizations. When I do that I see multiple dhcp-range= lines, which initially confused me, but seems to work properly. The first is the IPv4 range and lease time, while the second is the IPv6 information from my “Additional Options” entry.

The tool I’ve used to diagnose my router advertisment issues has been radvdump. It seems to be available on most linux distributions as well as being on ddwrt. When I was running it on my router and the router was not sending out its own advertisments, I’d get messages from my ISP that looked like this:

# radvd configuration generated by radvdump 2.19
# based on Router Advertisement from fe80::22c:c8ff:fe42:24bf
# received by interface vlan2
#

interface vlan2
{
        AdvSendAdvert on;
        # Note: {Min,Max}RtrAdvInterval cannot be obtained with radvdump
        AdvManagedFlag off;
        AdvOtherConfigFlag on;
        AdvReachableTime 0;
        AdvRetransTimer 0;
        AdvCurHopLimit 64;
        AdvDefaultLifetime 1800;
        AdvHomeAgentFlag off;
        AdvDefaultPreference high;
        AdvSourceLLAddress on;
        AdvLinkMTU 1500;

        prefix 2604:4080:1304::/64
        {
                AdvValidLifetime 2592000;
                AdvPreferredLifetime 604800;
                AdvOnLink on;
                AdvAutonomous on;
                AdvRouterAddr off;
        }; # End of prefix definition

}; # End of interface definition

When I was successfully running radvd on ddwrt, I never saw its own advertisements but could see them on other machines on my local network. Now that I’m using dnsmasq for my advertisements, I see messages on the router itself.

# radvd configuration generated by radvdump 2.19
# based on Router Advertisement from fe80::b27f:b9ff:fe83:6591
# received by interface br0
#

interface br0
{
        AdvSendAdvert on;
        # Note: {Min,Max}RtrAdvInterval cannot be obtained with radvdump
        AdvManagedFlag off;
        AdvOtherConfigFlag on;
        AdvReachableTime 0;
        AdvRetransTimer 0;
        AdvCurHopLimit 64;
        AdvDefaultLifetime 300;
        AdvHomeAgentFlag off;
        AdvDefaultPreference medium;
        AdvLinkMTU 1500;
        AdvSourceLLAddress on;

        prefix 2604:4080:1304:8010::/64
        {
                AdvValidLifetime 300;
                AdvPreferredLifetime 300;
                AdvOnLink on;
                AdvAutonomous on;
                AdvRouterAddr off;
        }; # End of prefix definition

        DNSSL wimsworld.com wimsworld.local local
        {
                AdvDNSSLLifetime 300;
        }; # End of DNSSL definition

        RDNSS fe80::b27f:b9ff:fe83:6591
        {
                AdvRDNSSLifetime 300;
        }; # End of RDNSS definition

}; # End of interface definition

I looked at the radvdump output from a friend’s location where he’s on XFinity and IPv6 just seems to be configured and works and this is what I saw. This is from a client machine since he has no access to the Xfinity supplied router itself.

# radvd configuration generated by radvdump 2.17
# based on Router Advertisement from fe80::16c0:3eff:fe4e:400c
# received by interface wlan0
#

interface wlan0
{
        AdvSendAdvert on;
        # Note: {Min,Max}RtrAdvInterval cannot be obtained with radvdump
        AdvManagedFlag on;
        AdvOtherConfigFlag on;
        AdvReachableTime 0;
        AdvRetransTimer 0;
        AdvCurHopLimit 64;
        AdvDefaultLifetime 180;
        AdvHomeAgentFlag off;
        AdvDefaultPreference medium;
        AdvSourceLLAddress on;

        RDNSS 2001:558:feed::1 2001:558:feed::2
        {
                AdvRDNSSLifetime 180;
        }; # End of RDNSS definition


        prefix 2601:603:4c7f:41e0::/64
        {
                AdvValidLifetime 300;
                AdvPreferredLifetime 300;
                AdvOnLink on;
                AdvAutonomous on;
                AdvRouterAddr off;
        }; # End of prefix definition


        route ::/0
        {
                AdvRoutePreference medium;
                AdvRouteLifetime 180;
        }; # End of route definition

}; # End of interface definition

When IPv6 is working properly on my router I have a globally scoped IPv6 address on my vlan2 interface and one on my br0 interface. I can recognize that my vlan2 interface is the outside world because the IPv4 address associated with it is a routable IPv4 address while the IPv4 address associated with br0 is 192.168.0.1.

I learned a lot about how IPv6 allocates addresses during this process, A bunch of the resources I used came from this set of locations.

Google Domains Dynamic DNS and IPv6

I’ve been wanting to use a dynamic address in my personal domain and IPv6. While I read that it should be possible, finding the exact method of configuring ddclient to do so was not obvious to me. The default installation configures everything to register an IPv4 address and finding specific configuration examples using IPv6 was hard. It turns out that finding examples was hard because the process itself is easy.

Go to domains.google.com, configure your domain to include dynamic DNS hosts, add the hostname you want to register, and retrieve the specific credentials for that hostname. There are help pages describing that process at google. It will create a record with the hostname you specify, and an A record (IPv4) with a 1 minute time to live.

Google Domains

Install ddclient using apt and go through the debconf wizard to enter the credentials. It will set up the default configuration using IPv4.

sudo apt install ddclient -y
domains.google
ddclient –force –verbose to see if errors occur
Google Domains IPv4 address registered

Manually editing /etc/ddclient.conf and changing the use= statement to usev6= and running ddclient a second time will switch to registering the IPv6 address.

wim@WimPiZeroW-Wim:~ $ sudo cat /etc/ddclient.conf
# Configuration file for ddclient generated by debconf
#
# /etc/ddclient.conf

protocol=googledomains \
usev6=if, if=wlan0 \
login=**************** \
password='***************' \
wimpizerow-wim.wimsworld.com
wim@WimPiZeroW-Wim:~ $
another ddclient –force –verbose to confirm changes didn’t create errors

running sudo systemctl confirms that ddclient.service is loaded active and running. If the address were to change, it should automatically be updated in the dynamic domain entry.