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:

C++ IPv4 Ping Code Example

I’ve written my own monitoring program to keep track of the availability of some of my machines. They register themselves in DNS using dynamic DNS protocols and occasionally change addresses. I realized that while recognizing when the address has changed is useful, I’d also like to know if the machine itself is reachable. Having code that would test the ICMP ping results directly in my code is useful, and this is what I ended up putting together after having found examples in variousl places on the web.

/ Define the Packet Constants
// ping packet size
#define PING_PKT_S 64
#define PING_SLEEP_RATE 1000000

// Gives the timeout delay for receiving packets in seconds
#define RECV_TIMEOUT 1

// ping packet structure
struct ping_pkt
{
    struct icmphdr hdr;
    char msg[PING_PKT_S - sizeof(struct icmphdr)];
};

// Calculating the Check Sum
unsigned short checksum(void* b, int len)
{
    unsigned short* buf = (unsigned short*) b;
    unsigned int sum = 0;

    for (sum = 0; len > 1; len -= 2)
        sum += *buf++;
    if (len == 1)
        sum += *(unsigned char*)buf;
    sum = (sum >> 16) + (sum & 0xFFFF);
    sum += (sum >> 16);
    unsigned short result = ~sum;
    return result;
}

bool send_ping4(const std::string& ping_ip, const std::string& HostName4Output, const bool bOutput = false)
{
    bool rval = false;
    if (bOutput)
        std::cout << "[" << getTimeExcelLocal() << "] " << "send_ping4(" << ping_ip << ", " << HostName4Output << ");" << std::endl;
    struct timespec tfs;
    clock_gettime(CLOCK_MONOTONIC, &tfs);
    auto ping_sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    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, SOL_IP, IP_TTL, &ttl_val, sizeof(ttl_val)) != 0)
        {
            if (bOutput)
                std::cout << "[" << getTimeExcelLocal() << "] " << "Setting socket options to TTL failed!" << std::endl;
        }
        else
        {
            // 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 = ICMP_ECHO;
                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_in ping_addr;
                ping_addr.sin_family = AF_INET;
                ping_addr.sin_port = htons(0);
                inet_pton(AF_INET, ping_ip.c_str(), &ping_addr.sin_addr.s_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_in 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)
                    {
                        if (!(pckt.hdr.type == 69 && pckt.hdr.code == 0))
                        {
                            if (bOutput)
                                std::cerr << "[" << getTimeExcelLocal() << "] " << "Error..Packet received with ICMP type " << int(pckt.hdr.type) << " code " << int(pckt.hdr.code) << std::endl;
                        }
                        else
                        {
                            char szAddr[NI_MAXHOST] = { 0 };
                            inet_ntop(AF_INET, &r_addr.sin_addr, szAddr, sizeof(szAddr));
                            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);
}

That works great if my address is an IPv4 address, but I required doing a lot of investigation to get a successful IPv6 address.

Remote Desktop with Hotmail Account

I have been using a Microsoft Account to log into my personal workstation for the past few years because it makes working with OneDrive fairly seamless. The account I use for logging in has an @hotmail.com address. Because I have been using a Microsoft Surface as my primary machine for the past few years, I have also been taking advantage of the Windows Hello capable camera to log in with my face most of the time. I’m now in the process of migrating from a Surface 7 to a Surface 9.

There are always reasons to go back to the old machine and look at some particular setting or program. A Remote Desktop connection from my new machine to the old one is much easier than getting up and walking across the room to the other machine and allows copy and paste directly between machines. Both machines are running the Professional version of Windows so I can enable remote desktop.

I looked at who had access, it shows that my hotmail.com address already has access, as well as my local network account. When I tried to access it from the new machine, it prompted for my password, but rejected it. I repeated this process several times thinking I must be typing it incorrectly before searching for what might be happening.

I came across a solution which is very simple. I don’t understand what it’s doing, but it works. https://answers.microsoft.com/en-us/windows/forum/all/remote-desktop-not-working-with-microsoft-account/71f0c323-688a-4c97-8740-e80eb31ae11d

On the old machine I ran the command runas /u:MicrosoftAccount\MyAccount@hotmail.com winver it prompted me for my password, and then it displayed the standard windows version dialog box. I’m not sure why it worked, but I’m happy that I now can sit at my desk with the large monitor and keyboard and have access to both machines.

runas /u:MicrosoftAccount\MyAccount@hotmail.com winver

Updated: I also came across the same answer using cmd.exe instead of winver.exe as the target of runas, but for some reason it was more difficult for me to understand what was going on. I think it was simply due to the way the email address was obscured in the second one that made it more difficult for me to understand, even though it may have had more screen captures in the demonstration. https://nready.net/remote-desktop-on-windows-11-with-microsoft-account-mfa/

Thanks for the information:

Raspberry Pi ZeroW WiFi Power Management

Every Raspberry Pi Zero W I’ve had has had intermittent connection problems on Wi-Fi. I’ve been able to fix the problems by disabling power management on the Wi-Fi interface each time. This page gave me my preferred solution for taking care of the problem on each machine. I’m duplicating the information here to contribute/prevent webrot.

See the current state of power management:

sudo iw wlan0 get power_save

set power management off:

sudo iw wlan0 set power_save off

Create a systemd unit file to set Wi-Fi power management:

sudo systemctl --full --force edit wifi_powersave@.service

With this as the contents of the unit file:

[Unit]
Description=Set WiFi power save %i
After=sys-subsystem-net-devices-wlan0.device

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/sbin/iw dev wlan0 set power_save %i

[Install]
WantedBy=sys-subsystem-net-devices-wlan0.device

Then enable the unit file, setting power management to off whenever wlan0 is activated.

sudo systemctl disable wifi_powersave@on.service
sudo systemctl enable wifi_powersave@off.service

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:

LTE wireless on SIM7600G-H 4G HAT (B) for Raspberry Pi

After coming across the recent and well written article Using 4G LTE wireless modems on a Raspberry Pi, I decided to give it a try myself. I looked at his parts list, and then ended up going in a different direction by buying an all-in-one unit designed explicitly for the Raspberry Pi Zero.

What’s on the Board
  1. SIM7600G-H
  2. FE1.1S USB HUB chip
  3. NAU8810 audio decoder
  4. RT9193-33 voltage translator
  5. USB HUB input D+/D- pogo pin: for Raspberry Pi Zero/Zero W MicroUSB connector: for other Raspberry Pi boards or PC
  6. Pogo pin power supply 5V: connects to 5V pin of Zero/Zero W, up to 2A current GND: connects to GND pin of Zero/Zero W
  1. USB extended ports USB1~USB2: USB-A connectors USB3: solder pad
  2. SIM card slot supports 1.8V/3V SIM card
  3. 3.5mm earphone/mic jack
  4. MAIN antenna connector
  5. AUX auxiliary antenna connector
  6. GNSS antenna connector
  7. Power indicator
  8. Network status indicator

I’m using Google Fi Unlimited Plus as my network on my phone right now, and one of its features is the ability to have multiple data only devices using part of your data allocation. That makes experimentation with a device like this fairly easy as I was able to order a data sim from Google and put it in the device and it just worked.

I’d never heard of pogo pins before. These are spring loaded contacts that line up with pads on the Raspberry Pi Zero to make electrical contact. They worked the first time I screwed everything together, but when I took it apart and put it back together a second time the board wasn’t recognized. I removed power, pushed down the pins with some tweezers, restored power, and the board was recognized again.

wim@WimPiZeroWCamera:~ $ lsusb
Bus 001 Device 003: ID 1e0e:9001 Qualcomm / Option
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@WimPiZeroWCamera:~ $ 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@WimPiZeroWCamera:~ $ sudo apt install libqmi-utils udhcpc ifmetric

I went through the test commands from Jeff Geerling’s post related to QMI Mode setup, then created a config file for my wwan0 interface:

auto wwan0
iface wwan0 inet manual
     pre-up ifconfig wwan0 down
     pre-up echo Y > /sys/class/net/wwan0/qmi/raw_ip
     pre-up for _ in $(seq 1 10); do /usr/bin/test -c /dev/cdc-wdm0 && break; /bin/sleep 1; done
     pre-up for _ in $(seq 1 10); do /usr/bin/qmicli -d /dev/cdc-wdm0 --nas-get-signal-strength && break; /bin/sleep 1; done
     pre-up sudo qmicli -p -d /dev/cdc-wdm0 --device-open-net='net-raw-ip|net-no-qos-header' --wds-start-network="apn='h2g2',ip-type=4" --client-no-release-cid
     pre-up udhcpc -i wwan0
     pre-up /usr/sbin/ifmetric wwan0 400
     post-down /usr/bin/qmi-network /dev/cdc-wdm0 stop
You can see where I added the ifmetric command as the last pre-up line
Route and Ping from interfaces.

You can see in the image above that my Wi-Fi network is running IPv6 and the default ping uses that interface, while specifying the wwan0 interface causes the traffic to travel a different path.

ifconfig details.
wim@WimPiZeroWCamera:~ $ 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 10482sec preferred_lft 9132sec
    inet6 2604:4080:1304:8010:8a38:1e12:3b21:5443/64 scope global dynamic mngtmpaddr noprefixroute
       valid_lft 24sec preferred_lft 14sec
    inet6 fe80::ea0d:3fe8:b6c4:da64/64 scope link
       valid_lft forever preferred_lft forever
3: wwan0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 1000
    link/none
    inet 100.198.30.32/26 scope global wwan0
       valid_lft forever preferred_lft forever
wim@WimPiZeroWCamera:~ $ sudo qmicli -d /dev/cdc-wdm0 --nas-get-signal-info
-get-serving-system
sudo qmi-network /dev/cdc-wdm0 status
sudo qmicli -d /dev/cdc-wdm0  --wds-get-packet-service-status[/dev/cdc-wdm0] Successfully got signal info
LTE:
        RSSI: '-74 dBm'
        RSRQ: '-12 dB'
        RSRP: '-102 dBm'
        SNR: '4.4 dB'
wim@WimPiZeroWCamera:~ $ sudo qmicli -d /dev/cdc-wdm0 --nas-get-signal-strength
[/dev/cdc-wdm0] Successfully got signal strength
Current:
        Network 'lte': '-74 dBm'
RSSI:
        Network 'lte': '-74 dBm'
ECIO:
        Network 'lte': '-2.5 dBm'
IO: '-106 dBm'
SINR (8): '9.0 dB'
RSRQ:
        Network 'lte': '-12 dB'
SNR:
        Network 'lte': '4.4 dB'
RSRP:
        Network 'lte': '-102 dBm'
wim@WimPiZeroWCamera:~ $ sudo qmicli -d /dev/cdc-wdm0 --nas-get-home-network
[/dev/cdc-wdm0] Successfully got home network:
        Home network:
                MCC: '310'
                MNC: '260'
                Description: 'T-Mobile'
wim@WimPiZeroWCamera:~ $ sudo qmicli -d /dev/cdc-wdm0 --nas-get-serving-system
[/dev/cdc-wdm0] Successfully got serving system:
        Registration state: 'registered'
        CS: 'attached'
        PS: 'attached'
        Selected network: '3gpp'
        Radio interfaces: '1'
                [0]: 'lte'
        Roaming status: 'off'
        Data service capabilities: '1'
                [0]: 'lte'
        Current PLMN:
                MCC: '310'
                MNC: '260'
                Description: '����.��i'
        Roaming indicators: '1'
                [0]: 'off' (lte)
        3GPP time zone offset: '-420' minutes
        3GPP daylight saving time adjustment: '1' hours
        3GPP location area code: '65534'
        3GPP cell ID: '45023373'
        Detailed status:
                Status: 'available'
                Capability: 'cs-ps'
                HDR Status: 'none'
                HDR Hybrid: 'no'
                Forbidden: 'no'
        LTE tracking area code: '11316'
        Full operator code info:
                MCC: '310'
                MNC: '260'
                MNC with PCS digit: 'yes'
wim@WimPiZeroWCamera:~ $ sudo qmi-network /dev/cdc-wdm0 status
Profile at '/etc/qmi-network.conf' not found...
Getting status with 'qmicli -d /dev/cdc-wdm0 --wds-get-packet-service-status  '...
Status: connected
wim@WimPiZeroWCamera:~ $ sudo qmicli -d /dev/cdc-wdm0  --wds-get-packet-service-status
[/dev/cdc-wdm0] Connection status: 'connected'
extra information

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.

Access Windows Share from Raspberry Pi (revisited)

Last year I described a simple method of automounting a directory from my windows server to my Raspberry Pi. Since then I’ve gone down a couple of paths to simplify rebuilding my Raspberry Pi machines.

The method I used last year required modifying the /etc/hosts file, the /etc/fstab file, pre-creating the mount points, and creating a credentials file to store the windows login credentials.

My new method doesn’t require modification of the /etc/hosts or /etc/fstab files, or pre-creating the mount points. Instead I’m relying on two features, Multicast DNS and systemd.automount unit files.

In the old method, to find the windows server, I added it to the local hosts file on the raspberry pi.

192.168.0.12 Acid

Using Multicast DNS, if I simply recognize that I can reach the server with the name Acid.WimsWorld.local the raspberry pi will find the server on the local network. My first step was to modify my /etc/fstab enty to use the local address and clean up my hosts file.

//acid.wimsworld.local/web /media/acid/web/ cifs credentials=/etc/wimsworld.smb.credentials,noauto,x-systemd.automount,x-systemd.idle-timeout=2min,_netdev 0 0

I’d never been happy with modifying the /etc/fstab file as part of my system configuration because in newer installations it is unique to each machine, specifying the boot partitions by their formatted serial number:

proc            /proc           proc    defaults          0       0
PARTUUID=142ff4e3-01  /boot           vfat    defaults          0       2
PARTUUID=142ff4e3-02  /               ext4    defaults,noatime  0       1
# a swapfile is not a swap partition, no line here
#   use  dphys-swapfile swap[on|off]  for that

In my recent programming projects I’ve been working with systemd unit files to control my service processes and have come to understand how they work for automounting directories as well. I like that each directory has its own unit files meaning that a modification is less likely to cause problems for the system as a whole.

The single line from the /etc/fstab file above is removed and replaced by two unit files, /etc/systemd/system/media-acid-web.mount and /etc/systemd/system/media-acid-web.automount.

[Unit]
Description=Acid Web

[Mount]
What=//acid.wimsworld.local/web
Where=/media/acid/web
Type=cifs
Options=credentials=/etc/wimsworld.smb.credentials,vers=2.1

[Install]
WantedBy=multi-user.target

and

[Unit]
Description=Automount Acid Web

[Automount]
Where=/media/acid/web
TimeoutIdleSec=120

[Install]
WantedBy=multi-user.target

I still had to create the credentials file for this to work, since I wanted the credentials file to be only root readable in a different location. /etc/wimsworld.smb.credentials

username=WindowsUsername
password=WindowsPassword
domain=OptionalDomainName

After the three files are created, systemd needs to reload its database with the systemctl daemon-reload command, the automount needs to be enabled with the systemctl enable media-acid-web.automount command, and then started with the systemctl start media-acid-web.automount command.

The naming of the mount files is important, and described explicitly in the man pages for each of mount and automount. In my case, /media/acid/web gets named media-acid-web.mount and media-acid-web.automount. I didn’t need to create mount points in the /media directory, as systemd automatically takes care of that.

I was able to create all of the above with a simple paste into my terminal with the following string:

sudo bash
cat > /etc/systemd/system/media-acid-web.mount <<EOF
[Unit]
Description=Acid Web

[Mount]
What=//acid.wimsworld.local/web
Where=/media/acid/web
Type=cifs
Options=credentials=/etc/wimsworld.smb.credentials,vers=2.1

[Install]
WantedBy=multi-user.target
EOF
cat > /etc/systemd/system/media-acid-web.automount <<EOF
[Unit]
Description=Automount Acid Web

[Automount]
Where=/media/acid/web
TimeoutIdleSec=120

[Install]
WantedBy=multi-user.target
EOF
cat > /etc/wimsworld.smb.credentials <<EOF
username=WindowsUsername
password=WindowsPassword
domain=OptionalDomainName
EOF
chmod 0600 /etc/wimsworld.smb.credentials
systemctl daemon-reload
systemctl enable media-acid-web.automount
systemctl start media-acid-web.automount
exit

With the standard Raspberry Pi setup, the cat command is not available as a sudo command while the bash shell is. I’m taking advantage of that by running the bash shell as root and then all of the other commands with root privileges.