GPRSD = GPRS keepalive daemon for Windows (XP)

GPRS uplink down? Shut down RAS session and redial. Rinse, repeat...
by: Frank Rysanek [rysanek AT fccps DOT cz]

Download

There's a ZIP file for download. It contains all the source code and a pre-built binary.

Install

Unpack GPRSD.EXE and SIMPLE.INF into some directory. It's better NOT to have a modem "driver" installed. Run GPRSD.EXE - it will install its own modem driver and create an RAS connection profile ("phonebook entry").

What it does

The classic GPRS modems speak PPP (emulated locally by the modem) to their local DTE = host computer. The PPP is handled by the standard Windows NT+ Remote Access Service (RAS). What RAS cannot do is keep an eye on the GPRS uplink and redial if necessary. RAS alone also isn't very good at inserting a SIM PIN, and generally the Windows modem framework doesn't understand GPRS extensions to the common Hayes-compatible AT command set.
Hence the inspiration for an additional GPRS-aware dialer / connection manager / whatever.

GPRSD does the following:

  1. checks for a COM port
  2. installs a GPRS modem (via an INF file) if missing
  3. creates a "phonebook entry" = RAS "connection" if missing
  4. checks for a modem on the COM port and inits the modem (SIM PIN), by direct access to COM port (= before RAS is asked to dial out)
  5. dials the phonebook entry = opens a RAS session
  6. keeps pinging a configured target host. When severeal pings get lost, it hangs up the RAS session and tries to reconnect, starting from the modem init (point 4. above).

See the source code for ugly details.

Configuration - GPRS.INI

On the first run, GPRSD.EXE creates an INI file with some defaults. You can modify the values in this file and copy it to other machines (you need not generate this file on each and every machine.) Configurable options follow:

COM_PORT=string
default: COM1

MODEM_HWID=string
Automatically generated, essentially internal (maps to INF)
default: Maestro

MODEM=string
Modem entry in the Windows device management.
Automatically generated, essentially internal.
default: Maestro

INF_FILE=string
Points to the modem "driver" (INF file).
default: simple.inf

PHONEBOOK_ENTRY=string
default: GPRS

PIN=string
SIM pin, only used if the modem asks for it

APN=string
APN name. Even "public internet access" uses some APN name, such as "internet.t-mobile.cz". Note that this is not (necessarily) a DNS name, it's just an APN name string chosen by the GPRS operator.

GPRS_NUM=string
The "phone number" to dial out, in order to get the GPRS service.
default: *99***1#

GPRS_USERNAME=string
Leave empty if unused

GPRS_PASSWORD=string
Leave empty if unused

PING_HOSTNAME=string
A DNS name or an IP address of the required Ping target machine
default: www.google.com

PING_TIMEOUT=number in seconds
How long to wait for a response to each PING request (ICMP echo request).
default: 5

PING_RETRIES=number
How many failed pings must fail in a row to initiate a redial.
default: 5

PING_OK_PERIOD=number in seconds
When a ping response arrived just fine, how long to sleep before sending another PING request. The default is somewhat long, to avoid incredible volume-based fees to the GPRS operator.
default: 300

PING_RETRY_PERIOD=number in seconds
When a ping timed out, how long to sleep before sending another PING request. It might look like a good idea to check again relatively soon. Beware, you may want to set this longer - to avoid large fees when it's not the local last mile radio segment that went wrong...
default: 5

RAS_REDIAL_PERIOD=number in seconds
When Ping signals connection down, GPRSD tries to redial immediately. If this immediate redial fails, the next attempt (and any further attempts until success) will be preceded by a configurable delay. This delay should give the modem / GSM network / local PPP stack some time to recover from an error - and should also limit the size of an error log file, if you're trying to capture what's going on.
default: 30

SLEEP_ON_MODEM_INIT_FAILURE=number in seconds
When GPRSD fails to init the modem (no response to AT, modem responds ERROR to AT or some such), GPRSD will try again with a configurable delay. This should limit the size of a log file, if you're trying to capture what's going on.
default: 20

Requirements

Windows XP 32bit (not tested on other versions).
Wavecom/Sierra based GPRS modem (or something compatible). The program was tested against Wavecom-based modems sold by Maestro Wireless / Fargo Telecom (of TW).

Disclaimer

This software is provided as is. I disclaim any responsibility. Use at your own risk.

Beware: GPRSD uses Ping (ICMP echo + echo reply messages) to keep an eye on your GPRS uplink connectivity. This implies data being transferred, which in turn implies more money charged by the GPRS operator. There's no way around this - you need to transfer data to check the status of your GPRS session, and you'll pay for that. How much, that depends on your negotiated fees.
The PING repetition rate is configurable - you'll find yourself trading off monitoring precision (in time) vs. volume-based fees.

Further background info

Written in MinGW for Win32. The source code should be reasonably portable to other compilers.

Known downsides: it's a fairly crude console app. It doesn't have proper GUI behavior, cannot hide in systray, doesn't alone run as a service (you may try running it via NSSM). It sends progress messages to its stdout - to capture that to a file, just use the > sign. Or download some "tee" variant for Win32, if you want to have a logfile AND see the output in the console window at the same time.

The reconnect is a mess. The RAS can fail to reconnect for no obvious reason, once or twice (even when the GSM network is there and antenna is attached) - and the spectrum of errors returned by RasDial() et al. is staggering. Even the modem can sometimes respond "ERROR" to plain "AT" for no apparent reason. It's not quite possible to distinguish fatal vs. temporary errors. It's also difficult to tell if the blame is with Windows or with the modem/network or whatever.
Therefore, GPRSD just keeps on trying to reconnect. You can even remove the SIM from your modem or turn off the modem, and GPRSD just keeps trying (and resumes successfully as soon as you power the modem back on or re-insert its SIM card).

GPRSD is fairly crude/clumsy in how it handles the connection errors and just keeps trying to redial from scratch. This is deliberate. It would certainly be possible to check for more detailed status flags in the modem, but that might make GPRSD sensitive to differences in firmware versions and modem models, which is NOT desired. Therefore, GPRSD is generic at the cost of being somewhat clumsy.

The source code contains practical examples of how to use C++/WinAPI to programmatically install and launch GPRS connectivity via an RS232-attached GPRS modem. It uses the RAS API and the Setup API. It also demonstrates how to code a simple PING-style program.

Apologies for poor coding style, mixing basic C types with Win32-style "Hungarian notation" etc. - the program is based on snippets taken from various sources (see the Credits). It would sure use some janitoring to get more unified / compact / elegant / extensible... I wish I had the time.

GPRS peculiarities

Monitoring GPRS connectivity is not necessarily an obvious task.

PPP (IPCP) does support keepalives, but these are not much practical use. There's no way to ask the Windows RAS to use them, and even if there was a way, there's no guarantee that the GPRS modem relays them to the "PDP context" (GTP) to really check the GPRS "session status". There are hints in Cisco documentation that GTP can do keepalives, but it's difficult to say if these can be relied on...

Another option would be, if you're running some application-level TCP session, to monitor connectivity via TCP "keepalives". A TCP keepalive is a packet with zero payload, sent merely to request an ACK from the opposite end. Such a packet gets generated by calling the socket API's send() function, and providing it with a buffer with zero length = by submitting to TCP a data chunk with length = 0.
This way, you might combine connectivity monitoring with your payload traffic. Unfortunately, it seems that the GPRS endpoints (certainly the GGSN) manipulate the traffic up to the TCP layer in such a way that TCP ACK's to keepalives are sent, even though the GPRS session has actually died (the MT is temporarily out of reach or some such). GPRS simply tries to buffer as much data as possible and hide retransmissions on the radio segment from TCP/IP. The buffering happens even with PING requests and responses (fortunately, ICMP echo responses are NOT locally generated).

There are some flags in the GPRS modem that can be used to check the status of a GPRS "network registration" and PDP context. Unfortunately, these are A) unreliable and B) cannot be read by the host computer when PPP is up and running. The modem would be able to temporarily break into command mode (using +++) and then resume, but the Windows RAS is certainly uncapable of that.

In the end, your only chance to monitor the real-world status of a GPRS session is by classic ICMP PING to some outside host that's known to be available. Either to some server of your own, or to some well-known internet site. You could also ping your local GGSN (your default gateway) if your operator allows this - or some IP address further upstream... or ping multiple addresses and try some "quorum consensus", or try a traceroute and infer status from its output. Nevertheless, any more advanced heuristic approach is complex / problematic. Thus, the GPRSD approaches this in a rather crude way: it pings a single outside host and redials if that's not available. This can lead to false detections of "link down", and it cannot distinguish between a local last-mile outage and other problems further upstream (which won't get solved by a redial). Well that's life.

Credits

The whole code is heavily inspired by several articles from the Code Project website. I've noticed bugs in some of their code samples (or sometimes in MS Windows API behavior), but thanks god for that website, and kudos to all the article authors.
The source code contains specific credits where due.