“Fun and games with SMS on the RUT-240 in bridge mode”
Sending and receiving SMS text messages from LAN workstations via the RUT-240
SMS fun and games
Using the RUT-240 to text the world
Last year we looked at using the RUT-240 4G router in bridge mode to provide a data connection and internet access for an OpenBSD machine.
At the time I mentioned that other functionality is also available:
As a bonus, although operating in bridge mode obviously means that we lose access to a lot of the router's capabilities, it's still possible to use the SMS sending and receiving functions, as well consult the logs of signal strength and configure the cellular data side of things from it's web interface.
Since the ability of the RUT-240 goes way beyond simply gating internet data between otherwise incompatible networks, let's now take a look at how we can use these features from the same locally connected OpenBSD machine.
Test equipment and environment
As before, we'll be using the RUT-240 in bridge mode with a regular SIM card that provides data service with a dynamic IP address rather than a speciality SIM that provides a fixed IP.
The router firmware has been updated to version 7.06.11, which is the latest version available at the time of writing.
WI-FI has been disabled, and the device is connected to a machine running OpenBSD 7.5-release via the wired ethernet LAN port.
Our primary goal is to make use of the SMS sending and receiving functionality from the locally connected OpenBSD machine, and to do so without the need to use third party software from the ports tree.
RUT-240Firmware 7.06.11Regular SIMWired LAN connectionOpenBSD 7.5-release
Overview of the available functionality
Whilst operating in it's default non-bridging mode, the device offers various ways to handle sending and receiving of SMS messages. Apart from manual interaction with individual messages via the RutOS web interface, the main options of interest are those listed and configured in the services / mobile utilities / SMS gateway tab:
Send individual messages, and read messages already received and stored on the device.
Basically the equivalent of what you can do in the RutOS web interface interactively with a web browser, but with a documented API.
Useful for sending, less useful for receiving as you don't receive any indication in real-time when a new message arrives.
Allows a fixed string to be sent in reply to any SMS message received.
Limited in functionality, probably mostly useful for identifying the specific device when several are in use, or advising people that received messages will not be read, and providing alternative contact details.
Allows forwarding of received SMS messages in three ways:
These are the most interesting options for us, as they allow near real-time delivery of received SMS messages.
Allows collection of specially formatted emails from a POP3 server, which are then re-sent as SMS messages.
This is of limited use to us since we can send messages much more easily and directly using http POST requests from the locally connected machine.
Although this functionality might plausibly be useful in other situations that require deployment of an SMS sending solution that mostly uses existing infrastructure and can be accessed via exising email applications, note that it is somewhat limited in flexibility. For example, collection of emails is only possible by intermittent polling of a POP3 server, and not via IMAP or inbound SMTP.
Fun fact!
SMS utilities
The RUT-240 also has a configuration for SMS utilities, found in services / mobile utilities / SMS utilities.
These allow received SMS messages to directly trigger certain internal functions of the device, such as resetting it or toggling mobile data on and off.
Configuration of the SMS utilities is possible completely independently of regular SMS reception and sending.
When both SMS utilities and SMS gateway functionality is enabled at the same time, inbound SMS messages that match a rule configured in the utilities page will trigger the corresponding action. However the same message will then only be forwarded or auto-replied as well by the gateway if the specific option to forward or auto-reply messages that have already been processed by SMS utilities is enabled.
Local vs remote servers
Although the most obvious way to configure the SMS forwarding via http and smtp functions is to point them to a remote machine elsewhere on the internet, they can just as easily be set to pass the data directly to a locally connected device. This is the approach we'll be using.
Limitations whilst working in bridge mode
All of the SMS gateway options described above continue to be available for configuration whilst the RUT-240 is configured as a bridge.
If the cellular ISP provides a globally routed IPv6 block in addition to a dynamic IPv4 address, then remote connectivity direct from the router is still possible in bridge mode, at least to remote servers that also provide IPv6 connectivity, (lack of which in 2024 is increasingly hard to justify).
However the single global IPv4 address, (or more likely single CGNAT'ed IPv4 address), provided by the cellular operator is not available for use by the router itself to make external connections. Therefore, if we configure such a server within the SMS gateway options then our setup won't work.
Of course, the fact that we will be configuring our RUT-240 to connect to a local server on the LAN neatly side-steps that issue anyway.
Fun fact!
Alternative configuration using passthrough mode
If you really do need to do both of:
... then one possibility is to use pass-through mode instead of bridge mode.
Note that the http-based POST/GET interface in the RUT-240 can also be accessed remotely, either when used in a more traditional configuration - NAT mode together with a public, (typically static), IP that permits inbound connections - or via a globally routed IPv6 block. However in our setup we'll also only be using this interface from a locally connected machine.
Sending SMS via http requests
First we need to enable the functionality.
The relevant toggle can be found in services / mobile utilities / sms gateway, on the Post/Get tab, under the Mobile Post/Get Settings section, the toggle being named 'Enable'.
Note that the following commands can be enabled in the access field: sms_delete, sms_list, sms_read, sms_send, sms_total, mobile_data, and mobile_sim_pin. The only one we actually need enabled is sms_send.
Here we also need to define a username and password. These are separate from the main access credentials for the router and are only used for the SMS gateway functionality.
This username and password pair are also separate from any credentials defined in services / input/output / post/get.
With this configuration in place, sending an SMS message is as simple as invoking ftp from the command line of the connected OpenBSD machine:
$ ftp -o - " message"
Message texts longer than 160 characters are accepted by the RUT-240 and automatically sent as multiple SMS messages.
The value of telephone_number should be formatted as a purely numeric string containing:
So for example, the north american number +1 202 555-0155 would be represented as 0012025550155:
$ ftp -o - " message"
Fun fact!
Sending non-ASCII characters
The text field is interpreted as UTF-8, and non-ascii characters can be encoded in this way.
For example, to send ÿ, (y with diaereses):
$ message=`echo "\0303\0277"`
$ ftp -o - "$message"
Or to send the character 'sparkling heart' 💖:
$ message=`echo "\0360\0237\0222\0266"`
$ ftp -o - "$message"
Receiving SMS via http
We can use a locally running instance of httpd on the bridged OpenBSD machine to receive http requests from the RUT-240.
This in turn can run a simple cgi script via slowcgi that does whatever further processing we require.
Since the ISP-assigned IPv4 address is dynamic and can change, it's not very convenient to configure httpd to listen directly on this address. Doing so would require us to restart httpd whenever the address changed.
A better alternative would be to configure a dedicated static IP address from a reserved range specifically for this purpose. Since this address is only going to be used on the LAN, it could just as easily be either an IPv6 or IPv4 address.
In principle we should be able to assign an address from either family without much difference in functionality, but due to a few quirks in both RutOS and OpenBSD, there may be a practical advantage in chosing one over the other.
The main issue with assigning and using an IPv4 address for this purpose is that it becomes more difficult to configure NAT routing out to the internet using the single dynamic IP for other LAN clients behind the OpenBSD machine. The reason why is that such a firewall rule would usually use the interface identifier to specify the re-written IP:
pass out on if0 from nat-to (if0)
Typical rule for performing NAT to a dynamically assigned IPv4 address
With more than one IPv4 address configured on the interface it becomes difficult to reliably avoid the private static address being used as the target for the NAT re-writing.
At first glance it might seem as if the :0 modifier could be used to avoid the staticly assigned IP from being used. However this doesn't work very well when the interface is configured at boot time with both autoconf and a static IP alias, because the autoconf address is only obtained later via dhcpleased. Effectively the dynamically assigned IP becomes the alias, and the static address which was assigned first is what is obtained by using :0.
However, if no NAT routing is being done on the interface then assigning an IPv4 alias is a reasonable solution.
Using IPv6 mostly sidesteps the issue described above. Although the same limitation applies when multiple IPv6 addresses are configured on a single interface, NAT is much less commonly used with IPv6 than with IPv4 and so most installations won't be relying on a firewall rule that uses the interface specifier for this purpose anyway.
In the case of using IPv6 though, we do also need to add a static route for the private, locally scoped IPv6 address.
Until recently, there was also a slight complication using an IPv6 address for the webserver. In some older RUT-240 firmware versions the RutOS web interface didn't accept a literal IPv6 address in the field of the configuration page that defines the URL for the http request. This is fixed in the current version as of the time of writing, so a workaround is no longer necessary.
Previously the issue could be worked around by manually adding an entry to the /etc/hosts file on the RUT-240, and using that hostname in the URL. Details of this workaround have been included below for completeness.
We'll look at both IP family options in turn.
Using an IPv6 address
We'll use the IPv6 address fd00::1 for the webserver in this example.
Actual deployments should use a more randomly chosen address from the fd00::1/8 subnet.
A suitable hostname.if file would look something like this:
inet autoconf
inet6 autoconf
inet6 fd00::1
description "Internet router"
Configuring the network interface at boot time
The required httpd.conf extract is also very simple:
server "sms.lan" {
listen on fd00::1 port 80
location "/sms_in" {
Configuring httpd
Implementation detail
Plain http vs https
Since the RUT-240 is connected directly to a physical interface on the OpenBSD machine with no intermediate network hardware, there doesn't seem to be any particular reason to use https in place of unencrypted http, although doing so is also possible.
If the inbound traffic would otherwise be blocked by firewall rules, we might need to add a line such as the following to pf.conf:
pass in quick on if0 proto tcp to fd00::1 port 80
Where if0 is the network interface in use.
On the RUT-240 we'll add an entry to /etc/hosts, which can easily be done from the shell:
# echo "fd00::1 sms.lan" >> /etc/hosts
Adding an entry to /etc/hosts.
Note that we're doing this from the shell on the RUT-240, and not on the OpenBSD machine!
We also need to add a new entry to the routing table. This can be done in the RutOS web interface within network / routing / static routes, and the only required fields are interface / lan target / fd00::1 and route type/unicast.
Finally the necessary configuration of the actual SMS forwarding component is made in services / mobile utilities / SMS gateway in sub-options SMS forwarding / SMS forwarding to http configuration.
On this page at a minimum we need to set the enabled toggle, select GET or POST as the http method, enter a suitable URL for our local server, and choose a field name for the submitted message text.
The URL for the local server can now be set to http://sms.lan/sms_in, using our newly added hostname, and the message value name can be chosen arbitrarily but we'll use message.
Using an IPv4 address
In this example, we'll use as the statically assigned IPv4 address for the webserver.
A suitable hostname.if file would look something like this:
inet autoconf
inet6 autoconf
description "Internet router"
Configuring the network interface at boot time
Note that the address we assign should be chosen from outside of the range allocated to the internal DHCP server of the RUT-240, but within the same subnet.
This configuration can be found in network / interfaces / general / lan / dhcp server.
Fun fact!
Output of ifconfig
With such an IPv4 alias configured on the interface, the output of ifconfig when invoked to display information about all network interfaces will by default only include the main IPv4 address and not any aliases. Furthermore, because the ISP-assigned address that is configured using autoconf is only assigned later, the static address will be the one listed in the generic ifconfig output.
Invoking ifconfig with the -A flag will display all of the IPv4 aliases, as will invoking it with the specific interface name.
The required httpd.conf extract is very simple:
server "sms.lan" {
listen on port 80
location "/sms_in" {
Just as with the IPv6 example, since the RUT-240 is connected directly to a physical interface on the OpenBSD machine with no intermediate network hardware, there doesn't seem to be any particular reason to use https in place of unencrypted http.
If the inbound traffic would otherwise be blocked by firewall rules, we might need to add a line such as the following to pf.conf:
pass in quick on if0 proto tcp from to port 80
Where if0 is the network interface in use.
The necessary configuration of the RUT-240 is made in services / mobile utilities / SMS gateway in sub-options SMS forwarding / SMS forwarding to http configuration.
At a minimum we need to set the enabled toggle, select GET or POST as the http method, enter a suitable URL for our local server, and choose a field name for the submitted message text.
The URL for the local server can just be, and the message value name can be chosen arbitrarily but we'll use message.
Testing the setup
At this point, with the RUT-240 configuration updated and httpd restarted on the OpenBSD machine, sending SMS messages to the RUT-240 from another device should result in log entries in the httpd logfile:
# tail -f /var/www/logs/access.log
sms_in.lan - - [04/Jun/2024:09:05:02 +0100] "GET /sms_in?&message=Test%20message HTTP/1.1" 500 0
Now we just need a simple CGI script to do something useful with the received messages.
Top tip!
Passing additional data about received SMS
The configuration page for the RUT-240 that we used to configure the server details also includes a few options to pass more information about the received SMS.
If we need to know the telephone number of the sender, the include senders number option includes it as a separate field in the http request.
Note that the number will be formatted in the standard way for an international number, using a leading + symbol, (encoded as %2B).
Non-ASCII characters
An option also exists to encode the SMS message as Base64, apparently to ensure that non-ASCII characters are preserved.
However, during testing we noticed that without the base64 encoding option enabled the SMS message text was always passed to the webserver as a UTF-8 encoded sequence anyway, allowing us to decode non-ASCII characters.
For example, an SMS containing the single character ÿ, (y with diaereses), produced an entry like this:
sms_in.lan - - [04/Jun/2024:11:42:50 +0100] "GET /sms_in?&message=%C3%BF HTTP/1.1" 500 0
Where 0xC3 0xBF is indeed the correct UTF-8 encoding of ÿ.
Receiving SMS via smtp
As an alternative to relaying received SMS messages to a local webserver, we can also configure the RUT-240 to pass them on as emails to a locally running SMTP server.
The possible solutions are much the same as before. We can assign either an IPv4 address or an IPv6 address to the smtp server, and in the case of IPv6 we would also need to configure an appropriate static route on the RUT-240 to reach it.
We can also follow the same steps described above to add a hostname entry to /etc/hosts if we want to avoid using a literal IPv6 address in the configuration, (which was necessary in some older firmware versions where the RutOS web interface didn't allow us to specify a literal IPv6 address in the smtp server field).
Of course, if we want to use both http and smtp for onward delivery of SMS messages, then the hostname, IP, and static route that we previously configured can be shared between the two servers.
Assuming we want to use IPv6 address fd00::1 for smtp, and that the pre-requisites mentioned above have already been configured, the hostname.if file can be the same as we saw previously:
inet autoconf
inet6 autoconf
inet6 fd00::1
description "Internet router"
Configuring the network interface at boot time
The exact configuration required in smtpd.conf will depend on both the existing setup that we need to integrate with and whether we want to deliver the SMS generated emails to a local user or forward them on to another smtp server.
In this example we'll send the emails from address admin@router.lan and forward them elsewhere, to another local smtp server with hostname mail.lan.
listen on fd00::1 tag SMS
action "forward_sms_emails" relay host smtp://mail.lan
match from mail-from "admin@router.lan" for domain "sms_readers.lan" tag SMS action "forward_sms_emails"
Here we are tagging all smtp transactions received via the new IPv6 address with the tag SMS, and allowing any mail that is duly tagged as well being sent from the address admin@router.lan to a recipient in the domain sms_readers.lan, to be relayed via the smtp server at mail.lan.
Obviously the server at mail.lan needs to be suitably configured to deliver or further relay such messages.
If the server has an existing pki entry in it's smtp.conf file then we can reference it and enable tls:
action "forward_sms_emails" relay pki router.lan tls host smtp://mail.lan
If a pki entry is not already defined then one could obviously be added.
Implementation detail
Tagging matches and limiting the receiving domain
Tagging the transactions received on fd00::1 and then checking for this tag in the match rule isn't strictly necessary, but it serves as a way to prevent users who connect to the smtp server via other IP addresses from matching this specific rule, and therefore being able to send arbitrary messages from the admin@router.lan address.
Likewise, we don't actually need to specify the for domain argument and could easily replace it with for any if we had other appropriate measures in place to prevent emails being relayed to arbitrary addresses via this rule.
If the inbound traffic would otherwise be blocked by firewall rules, we might need to add a line such as the following to pf.conf:
pass in quick on if0 proto tcp to fd00::1 port 25
Where if0 is the network interface in use.
To configure the RUT-240 side we first need to look at system / administration / recipients / email users.
Here we add a single account with an arbitrary identifier for the 'name', (such as test). The secure connection and credentials toggles can be left off, the smtp server set to sms.lan, and the sender's email address configured as admin@router.lan.
Fun fact!
Test emails
Hitting the send test email button here will attempt to connect to the configured smtp server sms.lan, and send an email with both from and to address set to admin@router.lan. The specific smtpd.conf configuration shown above doesn't allow admin@router.lan as a destination address, so this email will not actually be delivered, but typically the web interface will report success anyway.
Looking at the smtp dialogue between the RUT-240 and opensmtpd, we can see what is going on:
smtp: 0x9157729a000: >>> 220 hostname ESMTP OpenSMTPD
smtp: 0x9157729a000: <<< EHLO Teltonika-RUT240.com
smtp: 0x9157729a000: STATE_CONNECTED -> STATE_HELO
smtp: 0x9157729a000: >>> 250-hostname Hello Teltonika-RUT240.com [IPv6 ULA address], pleased to meet you
smtp: 0x9157729a000: >>> 250-8BITMIME
smtp: 0x9157729a000: >>> 250-ENHANCEDSTATUSCODES
smtp: 0x9157729a000: >>> 250-SIZE 36700160
smtp: 0x9157729a000: >>> 250-DSN
smtp: 0x9157729a000: >>> 250 HELP
smtp: 0x9157729a000: <<< MAIL FROM:<admin@router.lan>
smtp: 0x9157729a000: >>> 250 2.0.0 Ok
smtp: 0x9157729a000: <<< RCPT TO:<admin@router.lan>
smtp: 0x9157729a000: >>> 550 Invalid recipient: <admin@router.lan>
a84c2a81c3061fe9 smtp failed-command command="RCPT TO:<admin@router.lan>" result="550 Invalid recipient: <admin@router.lan>"
smtp: 0x9157729a000: <<< DATA
smtp: 0x9157729a000: >>> 503 5.5.4 Invalid command arguments: No recipient specified
a84c2a81c3061fe9 smtp failed-command command="DATA" result="503 5.5.4 Invalid command arguments: No recipient specified"
smtp: 0x9157729a000: <<< QUIT
smtp: 0x9157729a000: >>> 221 2.0.0 Bye
The 550 response code after the RCPT TO: command is ignored, and the RUT-240 continues to send a DATA command anyway, further ignoring the 503 response that comes back.
This doesn't actually matter much, as the actual SMS-originated emails that are generated will have a recipient address that is permitted by our smtpd.conf, but it's interesting and potentially useful to know that RutOS can indicate success for test emails that were actually rejected.
With a recipient address configured we can now head to services / mobile utilities / SMS gateway and fill in the details required in SMS forwarding / SMS forwarding to email configuration.
In this section we need to turn on the enable toggle, configure the sender email by selecting the arbitrary name that we assigned in the previous step, and enter one or more recipient addresses directly as email address strings.
A subject line for the emails also needs to be provided, (in some older RUT-240 firmware versions this was optional but in the current version it's required).
Now, sending SMS messages to the RUT-240 should result in emails being sent.
Non-ASCII characters are UTF-8 encoded, although the email headers don't indicate UTF-8 encoding.
If the option Add sender number is enabled, then the telephone number of the sender is added as the last line of the email prefixed with the string from: .
Lower level modem interaction via SSH
Once connected to the RUT-240 via SSH, the modem can be accessed and controlled using the various /dev/ttyUSB? devices.
/dev/ttyUSB2 continually reports various information such as signal strength.
/dev/ttyUSB3 can be used to interact with the modem directly via at commands.
This can be useful for monitoring signal strength, configuring diverse modem parameters, and performing actions that have no higher-level interface, (such as originating a voice call).
Use of ttyUSB2
Note that opening ttyUSB2 and holding it open with a command such as:
# cat /dev/ttyUSB2
... will prevent RutOS from monitoring the signal strength and updating the web interface with this information.
Additionally, after a timeout the modem will be reset.
For this reason, interacting with the modem by issuing commands manually and reading the responses via ttyUSB3 is preferable.
Higher-level interaction with the modem is also possible using various commandline utilities available as part of the router firmware.
For example, we can use /sbin/uqmi to send SMS, (including flash SMS):
uqmi -d /dev/cdc-wdm0 --send-message "This is a test message" --send-message-target TELEPHONE_NUMBER --send-message-flash
When using /sbin/uqmi we need to specify the device /dev/cdc-wdm0.