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

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.

More Networking with SIM7600G-H 4G HAT (B) for Raspberry Pi

I initially set up my Waveshare SIM7600G-H 4G HAT (B) for Raspberry Pi as described in the post last week.

Partly because the original web article I was following mentioned different modes of connecting to the internet, and partly because of my infatuation with IPv6, I decided to try to see the performance of different modes I might be able to set up with the device I have.

The support wiki for the device has four pages related to networking setup with the Raspberry Pi, RNDIS, NDIS, SIM868 PPP, and “3G Router“. I’d looked up definitions of NDIS and RNDIS on Wikipedia and didn’t really understand the differences, but the SIM868 page is titled “SIM7600X ECM dial-up Internet” and the ECM led me to believe it was the same as the second type of networking defined in the original article I’d been following. (Choosing QMI or ECM)

I’d been able to issue commands to the board by echoing the correct AT command to /dev/sttyUSB2. I was using the command echo AT+CGPS=1 >/dev/ttyUSB2 to enable the GPS on each boot. Following the Waveshare RNDIS instructions I blithely issued the command echo AT+CUSBPIDSWITCH=9011,1,1 >/dev/sttyUSB2 on my raspberry. The wwan0 device that I’d had set up and running went away and was replaced by a usb0 network device that Raspbian automatically enabled and acquired a valid internet address for. It had a routing metric that was lower than my wlan0 interface, but I figured it might be easier to organize this later without adding udhcpc into the mix of networking software on my raspberry. It also acquired an IPv6 address, which I’d not figured out how to do with the QMI setup and qmicli tools in my initial configuration. The original article mentioned possibly a lower networking latency using ECM vs QMI, so because one of the Waveshare pages had ECM in the title, I tried echo AT+CUSBPIDSWITCH=9018,1,1 /dev/sttyUSB2.

The latency seemed to be slightly different, and I wanted to switch back and do some real testing. That’s when I realized that I no longer had any /dev/ttyUSB devices to be able to send commands to. Completely power cycling the device made no difference. This is when I realized there are no reset jumpers on the device at all, and it had been able to remember the carrier APN that I’d initially set up before switching modes. Looking at the support web page, the only way I could figure out to recover the device was to connect it to a windows machine and install their drivers. I was not interested in downloading drivers from an unknown source and installing them on my primary workstation but realized my old Windows7 machine that hadn’t been turned on in close to a year was a good sacrifice if I could get things working.

First, I had to download the SIM7600X Driver, then I had to get their software.

Initial look at software

It took me a while to figure out that I had to hit the “Tools->STC/IAP15 ISP Programmer” menu, followed by the “Send Multi Char” tab on the resulting window pane to get the list of AT commands it could send.

Simcom Software

It also took me a while to recognize that while I could type commands into the primary window on the left, when I pasted commands that I’d typed in notepad, they were displayed but not executed by the card. The program also didn’t let me easily copy the contents of the main window so I could keep track of what commands I’d used and gotten as a result. After a while, I realized that this was essentially just a specialized serial terminal, and I had serial terminals I’m much more familiar with on my machine already. The Simcom HS-USB ports are visible in the windows device manager as COM12, COM13, COM14, and COM16. The purpose of each port is visible in the naming of the port in the current mode, NMEA, AT PORT, Diagnostics, and Audio.

AT+CRESET appeared to be a good command to get me back to a reasonable working state, but it only seemed to reboot the card, without changing any of the settings that had been stored. I was finally able to figure out that the AT&F command would set all the settings back to the factory defaults. After doing that I was no longer automatically connecting to Google FI, which at least meant I was no longer using up my data allowance while I was figuring things out.

I’d originally gotten into trouble by issuing the command AT+CUSBPIDSWITCH=9018,1,1. I came across the list of all available commands, which is much larger than what was listed in the Simcom Software itself. Issuing the command AT+CUSBPIDSWITCH? returns the current mode, and AT+CUSBPIDSWITCH=? lists the possible modes.

After the factory reset and switch back to 9011 mode, I got some of these results:

AT&F
OK
ATI
Manufacturer: SIMCOM INCORPORATED
Model: SIMCOM_SIM7600G-H
Revision: SIM7600M22_V2.0.1
SVN: 01
IMEI: 868822042540193
+GCAP: +CGSM

AT+CGDCONT?
+CGDCONT: 1,"IPV4V6","Telstra.internet","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","","0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0",0,0,0,0

OK
AT+CGDCONT=?
+CGDCONT: (1-24),"IP",,,(0-2),(0-4),(0-1),(0-1)
+CGDCONT: (1-24),"PPP",,,(0-2),(0-4),(0-1),(0-1)
+CGDCONT: (1-24),"IPV6",,,(0-2),(0-4),(0-1),(0-1)
+CGDCONT: (1-24),"IPV4V6",,,(0-2),(0-4),(0-1),(0-1)

OK
AT+CUSBPIDSWITCH?
+CUSBPIDSWITCH: 9011

OK
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)

Following some instructions I found on a post written by Mathieu Leguey, I was able to reconfigure the APN to connect to Google Fi. That page is also where I found the link to the complete list of commands. This other page is what led me to believe I need to set the APN twice to get IPv6 operating properly.

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

I later realized I could configure the GPS to automatically start with the command AT+CGPSAUTO=1. The final configuration commands I decided to use that seem to work for my purposes are as follows:

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

By doing this, I was able to avoid installing any networking drivers or configuration on Raspian that wasn’t automatically installed with the minimal system image. I should have been able to issue all of the commands via the echo command and the /dev/sttyUSB2 port if I’d not initially put the unit into 9018 mode and removed the USB control port.

ip a
ifconfig -a
route -v -n
wim@WimPiZeroW-Hope:~ $ 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: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether b8:27:eb:7c:6a:80 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.53/24 brd 192.168.0.255 scope global dynamic noprefixroute wlan0
       valid_lft 10481sec preferred_lft 9131sec
    inet6 2604:4080:1304:8010:347f:5b74:9cac:5a2/64 scope global dynamic mngtmpaddr noprefixroute
       valid_lft 27sec preferred_lft 17sec
    inet6 fe80::7a98:f2b1:147d:36d0/64 scope link
       valid_lft forever preferred_lft forever
3: usb0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 1000
    link/ether fe:5c:c9:12:f4:3a brd ff:ff:ff:ff:ff:ff
    inet 192.168.225.31/24 brd 192.168.225.255 scope global dynamic noprefixroute usb0
       valid_lft 42884sec preferred_lft 37484sec
    inet6 2607:fb90:8062:ac89:b44d:ec7e:82c0:b337/64 scope global mngtmpaddr noprefixroute
       valid_lft forever preferred_lft forever
    inet6 fe80::5ade:a00c:113f:dfcd/64 scope link
       valid_lft forever preferred_lft forever
wim@WimPiZeroW-Hope:~ $ ifconfig -a
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 14  bytes 1876 (1.8 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 14  bytes 1876 (1.8 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

usb0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.225.31  netmask 255.255.255.0  broadcast 192.168.225.255
        inet6 2607:fb90:8062:ac89:b44d:ec7e:82c0:b337  prefixlen 64  scopeid 0x0<global>
        inet6 fe80::5ade:a00c:113f:dfcd  prefixlen 64  scopeid 0x20<link>
        ether fe:5c:c9:12:f4:3a  txqueuelen 1000  (Ethernet)
        RX packets 74  bytes 6149 (6.0 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 100  bytes 14461 (14.1 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.0.53  netmask 255.255.255.0  broadcast 192.168.0.255
        inet6 fe80::7a98:f2b1:147d:36d0  prefixlen 64  scopeid 0x20<link>
        inet6 2604:4080:1304:8010:347f:5b74:9cac:5a2  prefixlen 64  scopeid 0x0<global>
        ether b8:27:eb:7c:6a:80  txqueuelen 1000  (Ethernet)
        RX packets 3068  bytes 458727 (447.9 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 956  bytes 151187 (147.6 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

wim@WimPiZeroW-Hope:~ $ route -v
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         mobileap.qualco 0.0.0.0         UG    203    0        0 usb0
default         192.168.0.1     0.0.0.0         UG    302    0        0 wlan0
192.168.0.0     0.0.0.0         255.255.255.0   U     302    0        0 wlan0
192.168.225.0   0.0.0.0         255.255.255.0   U     203    0        0 usb0
wim@WimPiZeroW-Hope:~ $ route -v -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.225.1   0.0.0.0         UG    203    0        0 usb0
0.0.0.0         192.168.0.1     0.0.0.0         UG    302    0        0 wlan0
192.168.0.0     0.0.0.0         255.255.255.0   U     302    0        0 wlan0
192.168.225.0   0.0.0.0         255.255.255.0   U     203    0        0 usb0
wim@WimPiZeroW-Hope:~ $ ping -I usb0 www.google.com -c 4
PING www.google.com(nuq04s43-in-x04.1e100.net (2607:f8b0:4005:810::2004)) from 2607:fb90:8062:ac89:b44d:ec7e:82c0:b337 usb0: 56 data bytes
64 bytes from nuq04s43-in-x04.1e100.net (2607:f8b0:4005:810::2004): icmp_seq=1 ttl=116 time=68.0 ms
64 bytes from nuq04s43-in-x04.1e100.net (2607:f8b0:4005:810::2004): icmp_seq=2 ttl=116 time=67.1 ms
64 bytes from nuq04s43-in-x04.1e100.net (2607:f8b0:4005:810::2004): icmp_seq=3 ttl=116 time=65.2 ms
64 bytes from nuq04s43-in-x04.1e100.net (2607:f8b0:4005:810::2004): icmp_seq=4 ttl=116 time=65.5 ms

--- www.google.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 65.217/66.449/68.012/1.159 ms
wim@WimPiZeroW-Hope:~ $ ping -I wlan0 www.google.com -c 4
PING www.google.com(nuq04s43-in-x04.1e100.net (2607:f8b0:4005:810::2004)) from 2604:4080:1304:8010:347f:5b74:9cac:5a2 wlan0: 56 data bytes
64 bytes from nuq04s43-in-x04.1e100.net (2607:f8b0:4005:810::2004): icmp_seq=1 ttl=50 time=68.0 ms
64 bytes from nuq04s43-in-x04.1e100.net (2607:f8b0:4005:810::2004): icmp_seq=2 ttl=50 time=27.3 ms
64 bytes from nuq04s43-in-x04.1e100.net (2607:f8b0:4005:810::2004): icmp_seq=3 ttl=50 time=30.4 ms
64 bytes from nuq04s43-in-x04.1e100.net (2607:f8b0:4005:810::2004): icmp_seq=4 ttl=50 time=44.7 ms

--- www.google.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3008ms
rtt min/avg/max/mdev = 27.330/42.629/68.048/16.072 ms
wim@WimPiZeroW-Hope:~ $ 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-Hope:~ $ 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
ping results

One thing that jumped out when using the USB interface is that the default gateway is an internal Qualcomm name, mobileap.qualco.

lsusb results

I still need to figure out how to change the metric of usb0 to be higher than wlan0 probably installing the ifmetric program, following directions similar to this answer.

Update 7/17/2020

Adding an interface and metric line to the end of /etc/dhcpd.conf got me the routing I wanted without having to add any new programs. It also means the IPv6 stuff is taken care of automatically.

wim@WimPiZeroW-Hope:~ $ tail -5 /etc/dhcpcd.conf
#interface eth0
#fallback static_eth0

interface usb0
metric 400
wim@WimPiZeroW-Hope:~ $ route -n -v
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.0.1     0.0.0.0         UG    302    0        0 wlan0
0.0.0.0         192.168.225.1   0.0.0.0         UG    400    0        0 usb0
192.168.0.0     0.0.0.0         255.255.255.0   U     302    0        0 wlan0
192.168.225.0   0.0.0.0         255.255.255.0   U     400    0        0 usb0
wim@WimPiZeroW-Hope:~ $ route -n -v -6
Kernel IPv6 routing table
Destination                    Next Hop                   Flag Met Ref Use If
::1/128                        ::                         U    256 2     0 lo
2604:4080:1304:8010::/64       ::                         U    302 1     0 wlan0
2607:fb90:80c7:b02e::/64       ::                         U    400 2     0 usb0
fe80::/64                      ::                         U    256 1     0 usb0
fe80::/64                      ::                         U    256 1     0 wlan0
::/0                           fe80::b27f:b9ff:fe83:6591  UG   302 1     0 wlan0
::/0                           fe80::6c86:c4b4:1f1:c09    UG   400 2     0 usb0
::1/128                        ::                         Un   0   4     0 lo
2604:4080:1304:8010:347f:5b74:9cac:5a2/128 ::                         Un   0   2     0 wlan0
2607:fb90:80c7:b02e:b86b:f626:1fa9:3a62/128 ::                         Un   0   4     0 usb0
fe80::7a98:f2b1:147d:36d0/128  ::                         Un   0   3     0 wlan0
fe80::88be:29ff:2c84:e5a0/128  ::                         Un   0   5     0 usb0
ff00::/8                       ::                         U    256 2     0 usb0
ff00::/8                       ::                         U    256 2     0 wlan0
::/0                           ::                         !n   -1  1     0 lo
usb0 routing metric fixed

References:

DD-WRT Upgrade part two

The upgrade of DD-WRT that I performed this last Saturday brought the version from a 2019 release to a 2022 release. DD-WRT always recommends doing a factory reset of settings before and after flashing a new firmware. As far as I’ve been able to find out, DD-WRT doesn’t provide any way to back up the settings in any form other than a binary download that is not compatible between versions. This shortcoming makes upgrading a router with many customized settings a difficult process.

dd-wrt status screen

I performed the flash upgrade without resetting everything to defaults. It wasn’t until I was going to bed on Saturday night that I realized not all things were working properly. All of the ipv4 services appeared to be working properly. The ipv6 services were not working properly on my internal network clients.

I have a Microsoft Windows Server 2016 Essentials machine running several services including file sharing on my internal network. I also have my Windows 10 desktop, and several Raspberry Pi machines. Some of the Pi machines access the file shares on the server for both reading and writing.

I’ve found that when ipv6 is not allocating global addresses for the windows server and clients, file sharing doesn’t work properly. This is an issue I don’t understand, and don’t want to change the default operation of the windows server or windows client machines, which might create more long term maintenance headaches.

Among the customizations I have set in the router:

  • Router Name
  • Domain Name
  • Local IP (v4) address is 192.168.0.1 instead of 192.168.1.1
  • close to 35 DHCP reservations for machines that run on my internal network.
  • IPv6 enabled and configured for DHCPv6 with Prefix Delegation
  • DDNS service configured as in previous post.
  • Wireless SSID
  • Wireless Password
  • SSH access to the router with rsa keys entered for allowed machines.

I figured out that the primary settings for DHCP and DNS resolution are run using dnsmasq, and the configuration file can be viewed by looking at /tmp/dnsmasq.conf in the ssh console. All of the dns reservations are listed in the form of:

dhcp-host=b0:39:56:78:83:b0,GS108Tv2,192.168.0.123,1440m
dhcp-host=28:c6:8e:09:30:cb,GS108Tv2-LR,192.168.0.125,1440m
dhcp-host=04:a1:51:b0:a6:9a,GS108Tv2-OW,192.168.0.124,1440m

Copying all of them out of the console as one entry and adding them to the Additional Dnsmasq Options field was much easier than pasting MAC addresses, Hostnames, and IP addresses into separate field for each entry.

After adding them via the web interface here, they look exactly like the entries created in the static leases section of the interface. I was hoping that the system would parse them and display them in the static leases section, but it doesn’t seem to do that.

My SSH terminal program is configured to send a series of commands to the console each time I connect which reminds me of the current setup as well as how to examine it after a long time when I’ve not worked on the device.

  • date ; uptime
  • route -A inet
  • route -A inet6
  • ip6tables -vnL
  • cat /tmp/dnsmasq.conf
  • cat /tmp/dhcp6c.conf
  • cat /tmp/radvd.conf
  • ifconfig

I’m currently not dumping the iptables (v4) output simply because there are a large number of rules that don’t get used which takes up a lot of extra space scrolling by.

I’ve compared the ipv4 and ipv6 routes from when ipv6 was not working, and they are identical.

root@Netgear-R7000:~# route -A inet
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         br1-mballard-v1 0.0.0.0         UG    0      0        0 vlan2
24.35.91.128    *               255.255.255.192 U     0      0        0 vlan2
127.0.0.0       *               255.0.0.0       U     0      0        0 lo
192.168.0.0     *               255.255.255.0   U     0      0        0 br0
root@Netgear-R7000:~# route -A inet6
Kernel IPv6 routing table
Destination                                 Next Hop                                Flags Metric Ref    Use Iface
2604:4080:1304::/64                         ::                                      UA    256    0        0 vlan2   
2604:4080:1304:8010::/60                    ::                                      U     256    0        0 br0     
fe80::/64                                   ::                                      U     256    0        0 eth0    
fe80::/64                                   ::                                      U     256    0        0 vlan1   
fe80::/64                                   ::                                      U     256    0        0 eth1    
fe80::/64                                   ::                                      U     256    0        0 eth2    
fe80::/64                                   ::                                      U     256    1       23 br0     
fe80::/64                                   ::                                      U     256    0        0 vlan2   
::/0                                        fe80::22c:c8ff:fe42:24bf                UGDA  1024   2      302 vlan2   
::/0                                        ::                                      U     2048   2       38 vlan2   
::/0                                        ::                                      !n    -1     1      372 lo      
::1/128                                     ::                                      Un    0      3       15 lo      
2604:4080:1304::/128                        ::                                      Un    0      1        0 lo      
2604:4080:1304:0:b27f:b9ff:fe83:6590/128    ::                                      Un    0      3       75 lo      
2604:4080:1304:8010::/128                   ::                                      Un    0      1        0 lo      
2604:4080:1304:8010:b27f:b9ff:fe83:6591/128 ::                                      Un    0      3       64 lo      
fe80::/128                                  ::                                      Un    0      1        0 lo      
fe80::/128                                  ::                                      Un    0      1        0 lo      
fe80::/128                                  ::                                      Un    0      1        0 lo      
fe80::/128                                  ::                                      Un    0      1        0 lo      
fe80::/128                                  ::                                      Un    0      1        0 lo      
fe80::/128                                  ::                                      Un    0      1        0 lo      
fe80::b27f:b9ff:fe83:658f/128               ::                                      Un    0      1        0 lo      
fe80::b27f:b9ff:fe83:658f/128               ::                                      Un    0      1        0 lo      
fe80::b27f:b9ff:fe83:6590/128               ::                                      Un    0      3       61 lo      
fe80::b27f:b9ff:fe83:6591/128               ::                                      Un    0      1        0 lo      
fe80::b27f:b9ff:fe83:6591/128               ::                                      Un    0      3       24 lo      
fe80::b27f:b9ff:fe83:659e/128               ::                                      Un    0      1        0 lo      
ff00::/8                                    ::                                      U     256    0        0 eth0    
ff00::/8                                    ::                                      U     256    0        0 vlan1   
ff00::/8                                    ::                                      U     256    0        0 eth1    
ff00::/8                                    ::                                      U     256    0        0 eth2    
ff00::/8                                    ::                                      U     256    2      580 br0     
ff00::/8                                    ::                                      U     256    2       12 vlan2   
::/0                                        ::                                      !n    -1     1      372 lo      

I’ve looked at the ip6tables, and it also appears identical, beyond the counters.

root@Netgear-R7000:~# ip6tables -vnL
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
   12  2289 ACCEPT     all      *      *       ::/0                 ::/0                 state RELATED,ESTABLISHED
    5   376 ACCEPT     icmpv6    *      *       ::/0                 ::/0                
    0     0 ACCEPT     all      *      *       fe80::/64            ::/0                
    0     0 ACCEPT     all      br0    *       ::/0                 ::/0                
    0     0 ACCEPT     all      *      *       ::/0                 ::/0                

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 ACCEPT     all      *      *       ::/0                 ::/0                 state RELATED,ESTABLISHED
    0     0 ACCEPT     all      *      vlan2   ::/0                 ::/0                
    0     0 ACCEPT     icmpv6    *      *       ::/0                 ::/0                 ipv6-icmptype 128 limit: avg 2/sec burst 5
    0     0 ACCEPT     all      *      *       ::/0                 ::/0                

Chain OUTPUT (policy ACCEPT 31 packets, 4287 bytes)
 pkts bytes target     prot opt in     out     source               destination         

I’d tried disabling Radvd on the IPv6 configuration gui and adding “enable-ra” to the Additional Dnsmasq Options, but that didn’t fix my problems. The current configuration has matching radv.conf files to the non working version.

root@Netgear-R7000:~# cat /tmp/radvd.conf
interface br0
{
 IgnoreIfMissing on;
 AdvSendAdvert on;
 MinRtrAdvInterval 3;
 MaxRtrAdvInterval 10;
 AdvHomeAgentFlag off;
 AdvManagedFlag off;
 AdvOtherConfigFlag on;
 AdvLinkMTU 1452;
 prefix 2604:4080:1304:8010::/64 
 {
  AdvOnLink on;
  AdvAutonomous on;
  AdvValidLifetime 30;
  AdvPreferredLifetime 20;
 };
 RDNSS 2607:f060:2::1 2607:f060:2:1::1{};
};

I spent a lot of time reading up on IPv6 and reminding myself of things I’d known in the past and forgotten. https://blog.dorianbolivar.com/2018/09/going-full-ipv6-with-dd-wrt.html?lr=1 is a well written post with links to more sources that I found especially helpful as it was written specifically using DD-WRT and IPv6. My only issue is that it was written nearly four years ago and may not have the same options in the DD-WRT gui as are currently available.

One of the items I added to the Additional Dnsmasq Options was a couple of host entries so that dnsmasq would resolve IPv6 addresses for my windows machines. It seems to speed up the IPv6 name discovery of my windows server while still pointing default DNS resolution at the router.

host-record entries

My conclusion is that I don’t understand what was different in the non-functioning setup I had with holdovers from the older version of DD-WRT, and going through the pain of re-installing from factory fresh configuration after each upgrade is worth the trouble. I’m still not satisfied with the best way of retrieving all of the configuration data into a text file that I can later run a difference test to see what’s changed, or needs to be changed.

IPv6 Testing on Apple Personal Hotspot

I have IPv6 set up and running on my home network, but there was some testing I wanted to run remotely. My local Starbucks WiFi isn’t running IPv6 according to my quick test with https://test-ipv6.com/

2019-10-28 (2)

The same test from my iPhone on TMobile shows it’s running IPv6 by default.

20191028_221547000_iOS

I had read somewhere that Apple supported IPv6 on the personal hotspot through a loophole in the netmask routing algorithms used by most providers..

When I tested the local network connection on my computer while connected to the Apple Personal Hotspot, it appeared to be running IPv6.

Mon 10/28/2019 14:57:08.69 C:\Users\Wim>ipconfig /all

Windows IP Configuration

Host Name . . . . . . . . . . . . : WimSurface
Primary Dns Suffix . . . . . . . : WIMSWORLD.local
Node Type . . . . . . . . . . . . : Hybrid
IP Routing Enabled. . . . . . . . : No
WINS Proxy Enabled. . . . . . . . : No
DNS Suffix Search List. . . . . . : WIMSWORLD.local

Wireless LAN adapter Local Area Connection* 4:

Media State . . . . . . . . . . . : Media disconnected
Connection-specific DNS Suffix . :
Description . . . . . . . . . . . : Microsoft Wi-Fi Direct Virtual Adapter #4
Physical Address. . . . . . . . . : B6-AE-2B-C1-21-16
DHCP Enabled. . . . . . . . . . . : Yes
Autoconfiguration Enabled . . . . : Yes

Wireless LAN adapter Local Area Connection* 6:

Media State . . . . . . . . . . . : Media disconnected
Connection-specific DNS Suffix . :
Description . . . . . . . . . . . : Microsoft Wi-Fi Direct Virtual Adapter #5
Physical Address. . . . . . . . . : B6-AE-2B-C1-24-16
DHCP Enabled. . . . . . . . . . . : Yes
Autoconfiguration Enabled . . . . : Yes

Wireless LAN adapter Wi-Fi:

Media State . . . . . . . . . . . : Media disconnected
Connection-specific DNS Suffix . : home
Description . . . . . . . . . . . : Marvell AVASTAR Wireless-AC Network Controller
Physical Address. . . . . . . . . : B4-AE-2B-C1-20-17
DHCP Enabled. . . . . . . . . . . : Yes
Autoconfiguration Enabled . . . . : Yes

Ethernet adapter Bluetooth Network Connection:

Connection-specific DNS Suffix . :
Description . . . . . . . . . . . : Bluetooth PAN HelpText
Physical Address. . . . . . . . . : B4-AE-2B-C1-20-18
DHCP Enabled. . . . . . . . . . . : Yes
Autoconfiguration Enabled . . . . : Yes
IPv6 Address. . . . . . . . . . . : 2607:fb90:f2a:1b9b:4d30:692:7441:1cf4(Preferred)
Temporary IPv6 Address. . . . . . : 2607:fb90:f2a:1b9b:2495:be8c:b229:b0b6(Preferred)
Link-local IPv6 Address . . . . . : fe80::4d30:692:7441:1cf4%4(Preferred)
IPv4 Address. . . . . . . . . . . : 172.20.10.2(Preferred)
Subnet Mask . . . . . . . . . . . : 255.255.255.240
Lease Obtained. . . . . . . . . . : Monday, October 28, 2019 3:01:16 PM
Lease Expires . . . . . . . . . . : Tuesday, October 29, 2019 2:46:49 PM
Default Gateway . . . . . . . . . : fe80::1089:a438:80a9:f8e%4
                                    172.20.10.1
DHCP Server . . . . . . . . . . . : 172.20.10.1
DHCPv6 IAID . . . . . . . . . . . : 95727147
DHCPv6 Client DUID. . . . . . . . : 00-01-00-01-1D-D8-F3-3B-B4-AE-2B-C1-20-17
DNS Servers . . . . . . . . . . . : fe80::1089:a438:80a9:f8e%4
                                    172.20.10.1
NetBIOS over Tcpip. . . . . . . . : Enabled

Mon 10/28/2019 15:03:26.55 C:\Users\Wim>

Unfortunately when I connected to my phone from my computer via the personal hotspot, I wasn’t able to get positive IPv6 results. Obviously the hotspot was working since I was able to get to the test site via IPv4 without issues.

2019-10-28 (1)

I’d read “RIPE NIC: ‘In Five Weeks We’ll Run Out of IPv4 Internet Addresses’ “ earlier today and have always been interested in understanding more of the nuances of using IPv6 compared to IPv4. Getting Ready for IPv4 Run-out has more information on how they are allocating IPv4 addresses..