Adding SMS sending capability to a rooted Netgear Nighthawk M6 Pro / MR6500

The Problem

Netgear decided to remove the ability to send SMS messages on the Nighthawk M6. Having SMS sending capability is handy, especially for alerting on events such as Internet outages. This workaround implements a simple HTTP server using Perl that can be used to send messages through the device. Perl was chosen begrudgingly because it was the only capable scripting language that comes by default with the device.

Prerequisite

You must have access to the root telnet shell of the device. You can find details at the URL below how to do this. This is outside of the scope of this post and no assistance will be provided.

https://github.com/0xBAADF0OD/netgear_mr6400/tree/main/netgear-AT-commands#enable-root-telnet-port-23
https://sierra-keygen.uu.sg/ (Hint: Device Generation = SDX55)

Disclaimer

This was tested on an AT&T branded Nighthawk M6 Pro / MR6500 running the following software versions:
Firmware Version: NTGX65_10.04.22.01
Modem Version: MPSS.DE.1.0-02593-OLYMPIC_GENALL_PACK-1
GUI Version: MR6500-1A1NAS_05.00.63.00

There is no warranty included with this software so use at your own risk. If you brick your device it's on you.

The code / How to install

  1. Telnet to the root shell of the M6.
1$ telnet 192.168.1.1
2Trying 192.168.1.1...
3Connected to 192.168.1.1.
4Escape character is '^]'.
5
6QTI Linux reference nogplv3 distro targeting performance builds. fatal: not a git repository (or any of the parent directories): .git sdxlemur
7
8/ #
  1. Add the below code to a new file named /mnt/userrw/sms_server. Update the $password and $phone_number variables to reflect your desired password and phone number that messages will be sent to.
  1#!/usr/bin/perl
  2use IO::Socket::INET;
  3
  4# Automatically get the LAN GW and listen only on that IP
  5my $listen_ip = `/usr/bin/dx router.GwIpAddr`;
  6
  7# TCP port to listen to
  8my $listen_port = '767'; #SMS
  9
 10# Password required to send messages
 11my $password = 'password';
 12
 13# Phone number messages should be sent to
 14my $phone_number = '+12223334567';
 15
 16# Debug Mode - Output AT command status to browser
 17my $debug_mode = 1; # 1 = On, 0 = Off
 18
 19# Create listening socket
 20my $server = new IO::Socket::INET (
 21  LocalHost => $listen_ip,
 22  LocalPort => $listen_port,
 23  Proto => 'tcp',
 24  Listen => 5,
 25  Reuse => 1
 26);
 27die "Error: Cannot create socket - $!\n" unless $server;
 28
 29$SIG{INT} = sub { $server->close(); exit 0; };
 30
 31# Main loop
 32while ($client = $server->accept()) {
 33  $client->autoflush;
 34  my $request = <$client>;
 35
 36  # Show form if accessing /
 37  if ($request =~ m|^GET / HTTP/1.[01]|) {
 38    print $client "HTTP/1.0 200 OK\n";
 39    print $client "Content-Type: text/html\n\n";
 40    print $client "
 41    <!DOCTYPE html>
 42    <html>
 43      <body>
 44        <h2>SMS Server</h2>
 45        <form action=\"/action\" method=\"get\">
 46          <p>Password: <input type=\"password\" id=\"password\" name=\"password\" maxlength=\"64\"></p>
 47          Message:<br>
 48          <textarea id=\"message\" name=\"message\" rows=\"4\" cols=\"50\" maxlength=\"160\"></textarea><br>
 49          <input type=\"submit\" value=\"Submit\">
 50        </form>
 51        <p>Note: Message length is limited to 160 characters.</p>
 52      </body>
 53    </html>";
 54  } elsif ($request =~ m|^GET /action(.+) HTTP/1.[01]|) {
 55    # Process input for /action
 56    print $client "HTTP/1.0 200 OK\n";
 57    print $client "Content-Type: text/plain\n\n";
 58
 59    # Get password and message from GET
 60    my ($password_arg) = $request =~ /password=([^&\s]*)/;
 61    my ($message_arg) = $request =~ /message=([^&\s]*)/;
 62
 63    # URLDecode input
 64    $password_arg =~ s/\+/ /g;
 65    $password_arg =~ s/%([A-Fa-f\d]{2})/chr hex $1/eg;
 66    $message_arg =~ s/\+/ /g;
 67    $message_arg =~ s/%([A-Fa-f\d]{2})/chr hex $1/eg;
 68
 69    if ($password_arg eq $password) {
 70      if (length($message_arg) <= 160) {
 71        if (open my $FH, '+<', '/dev/smd8') {
 72          $FH->autoflush;
 73          print $FH "\r";
 74          print $FH "AT+CMGF=1\r";
 75          sleep(0.1);
 76          print $FH "AT+CMGS=\"$phone_number\"\r";
 77          sleep(0.1);
 78          print $FH "$message_arg";
 79          sleep(0.1);
 80          print $FH "\x1a";
 81
 82          if ($debug_mode) {
 83            print $client "Debug log:\n";
 84          }
 85
 86          while(my $response = <$FH>) {
 87            if ($debug_mode) {
 88              print $client $response;
 89            }
 90
 91            if ($response =~ m/\+CMGS: /) {
 92              print $client "Status: Message sent.\n";
 93              last;
 94            }
 95
 96            if ($response =~ m/ERROR/) {
 97              print $client "Status: Error sending message: $response\n";
 98              last;
 99            }
100          }
101          close($FH);
102        } else {
103          print $client "Status: Error - Unable to open /dev/smd8.\n";
104        }
105      } else {
106        print $client "Status: Error - Message is longer than 160 characters.\n";
107      }
108    } else {
109      # Password does not match
110      print $client "Status: Error - Access denied.\n";
111    }
112  } else {
113    # Error if not accessing / or /action
114    print $client "HTTP/1.0 400 BAD REQUEST\n";
115    print $client "Content-Type: text/plain\n\n";
116    print $client "Status: Error - Bad request.\n";
117  }
118  close $client;
119}
  1. Make the sms_server file executable:
1/ # chmod +x /mnt/userrw/sms_server
2/ # ls -l /mnt/userrw/sms_server
3-rwxr-xr-x    1 root     root          3351 Dec 19 17:12 /mnt/userrw/sms_server
  1. Add the following to the bottom of /etc/init/early_init.sh:
1/mnt/userrw/sms_server &

Make sure to add the & so the server will launch and the init script can exit.

  1. Reboot the device.

  2. Once it reboots you can go to the following address:
    http://[ip of your M6 Pro]:767

Enter the password you provided in the sms_server script and a message (limited to 160 characters) and click submit. You should receive a SMS on the phone number you defined in the script. If not, by default the log of the communication with the modem built into the M6 will show and provide insight into why it didn't work.

Example using curl:

1$ curl "http://192.168.1.1:767/action?password=password&message=hello+world"
2Debug log:
3AT+CMGF=1
4OK
5AT+CMGS="+12223334567"
6> hello world
7
8+CMGS: 70
9Status: Message sent.

You may also be interested in the 3D printable wall mount I designed for the M6 Pro. You can download that here:
https://www.printables.com/model/686317-netgear-nighthawk-m6-pro-wall-mount