Skip to content

Commit d5ec692

Browse files
committed
Add DTLS support to MQTT-SN client
1 parent f653193 commit d5ec692

13 files changed

+306
-95
lines changed

README.md

+1-20
Original file line numberDiff line numberDiff line change
@@ -189,26 +189,7 @@ This example enables the wolfMQTT client to connect to the IBM Watson Internet o
189189
### MQTT-SN Example
190190
The Sensor Network client implements the MQTT-SN protocol for low-bandwidth networks. There are several differences from MQTT, including the ability to use a two byte Topic ID instead the full topic during subscribe and publish. The SN client requires an MQTT-SN gateway. The gateway acts as an intermediary between the SN clients and the broker. This client was tested with the Eclipse Paho MQTT-SN Gateway, which connects by default to the public Eclipse broker, much like our wolfMQTT Client example. The address of the gateway must be configured as the host. The example is located in `/examples/sn-client/`.
191191

192-
A special feature of MQTT-SN is the ability to use QoS level -1 (negative one) to publish to a predefined topic without first connecting to the gateway. There is no feedback in the application if there was an error, so confirmation of the test would involve running the `sn-client` first and watching for the publish from the `sn-client_qos-1`. There is an example provided in `/examples/sn-client/sn-client_qos-1`. It requires some configuration changes of the gateway.
193-
194-
* Enable the the QoS-1 feature, predefined topics, and change the gateway name in `gateway.conf`:
195-
196-
```
197-
QoS-1=YES
198-
PredefinedTopic=YES
199-
PredefinedTopicList=./predefinedTopic.conf
200-
.
201-
.
202-
.
203-
#GatewayName=PahoGateway-01
204-
GatewayName=WolfGateway
205-
```
206-
207-
* Comment out all entries and add a new topic in `predefinedTopic.conf`:
208-
209-
```
210-
WolfGatewayQoS-1,wolfMQTT/example/testTopic, 1
211-
```
192+
More about MQTT-SN examples in [examples/sn-client/README.md](examples/sn-client/README.md)
212193

213194
### Multithread Example
214195
This example exercises the multithreading capabilities of the client library. The client implements two tasks: one that publishes to the broker; and another that waits for messages from the broker. The publish thread is created `NUM_PUB_TASKS` times (10 by default) and sends unique messages to the broker. This feature is enabled using the `--enable-mt` configuration option. The example is located in `/examples/multithread/`.

examples/include.am

+1
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,5 @@ EXTRA_DIST+= examples/mqttuart.c \
168168
examples/aws/awsiot.vcxproj \
169169
examples/wiot/wiot.vcxproj \
170170
examples/sn-client/sn-client.vcxproj \
171+
examples/sn-client/README.md \
171172
examples/multithread/multithread.vcxproj

examples/mqttexample.c

+80-17
Original file line numberDiff line numberDiff line change
@@ -198,31 +198,26 @@ void mqtt_show_usage(MQTTCtx* mqttCtx)
198198
PRINTF("-? Help, print this usage");
199199
PRINTF("-h <host> Host to connect to, default: %s",
200200
mqttCtx->host);
201-
/* Remove TLS and SNI args for sn-client */
202-
if(XSTRNCMP(mqttCtx->app_name, "sn-client", 10)){
203201
#ifdef ENABLE_MQTT_TLS
204202
PRINTF("-p <num> Port to connect on, default: Normal %d, TLS %d",
205203
MQTT_DEFAULT_PORT, MQTT_SECURE_PORT);
206204
PRINTF("-t Enable TLS");
207205
PRINTF("-A <file> Load CA (validate peer)");
208206
PRINTF("-K <key> Use private key (for TLS mutual auth)");
209207
PRINTF("-c <cert> Use certificate (for TLS mutual auth)");
210-
#ifdef HAVE_SNI
211-
PRINTF("-S <str> Use Host Name Indication, blank defaults to host");
212-
#endif
213-
#ifdef HAVE_PQC
208+
#ifdef HAVE_SNI
209+
/* Remove SNI args for sn-client */
210+
if(XSTRNCMP(mqttCtx->app_name, "sn-client", 10)){
211+
PRINTF("-S <str> Use Host Name Indication, blank defaults to host");
212+
}
213+
#endif
214+
#ifdef HAVE_PQC
214215
PRINTF("-Q <str> Use Key Share with post-quantum algorithm");
215-
#endif
216+
#endif
216217
#else
217218
PRINTF("-p <num> Port to connect on, default: %d",
218219
MQTT_DEFAULT_PORT);
219220
#endif
220-
}
221-
222-
else{
223-
PRINTF("-p <num> Port to connect on, default: %d",
224-
MQTT_DEFAULT_PORT);
225-
}
226221
PRINTF("-q <num> Qos Level 0-2, default: %d",
227222
mqttCtx->qos);
228223
PRINTF("-s Disable clean session connect flag");
@@ -401,10 +396,9 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv)
401396
mqtt_show_usage(mqttCtx);
402397
return MY_EX_USAGE;
403398
}
404-
405-
/* Remove TLS and SNI functionality for sn-client */
399+
400+
/* Remove SNI functionality for sn-client */
406401
if(!XSTRNCMP(mqttCtx->app_name, "sn-client", 10)){
407-
mqttCtx->use_tls = 0;
408402
#ifdef HAVE_SNI
409403
useSNI=0;
410404
#endif
@@ -646,7 +640,8 @@ int mqtt_tls_cb(MqttClient* client)
646640
return rc;
647641
}
648642
}
649-
#elif defined(WOLFMQTT_ZEPHYR)
643+
#else
644+
/* Note: Zephyr example uses NO_FILESYSTEM */
650645
#ifdef WOLFSSL_ENCRYPTED_KEYS
651646
/* Setup password callback for pkcs8 key */
652647
wolfSSL_CTX_set_default_passwd_cb(client->tls.ctx,
@@ -714,12 +709,80 @@ int mqtt_tls_cb(MqttClient* client)
714709

715710
return rc;
716711
}
712+
713+
#ifdef WOLFMQTT_SN
714+
int mqtt_dtls_cb(MqttClient* client) {
715+
#ifdef WOLFSSL_DTLS
716+
int rc = WOLFSSL_FAILURE;
717+
718+
client->tls.ctx = wolfSSL_CTX_new(wolfDTLSv1_2_client_method());
719+
if (client->tls.ctx) {
720+
wolfSSL_CTX_set_verify(client->tls.ctx, WOLFSSL_VERIFY_PEER,
721+
mqtt_tls_verify_cb);
722+
723+
/* default to success */
724+
rc = WOLFSSL_SUCCESS;
725+
726+
#if !defined(NO_CERT) && !defined(NO_FILESYSTEM)
727+
if (mTlsCaFile) {
728+
/* Load CA certificate file */
729+
rc = wolfSSL_CTX_load_verify_locations(client->tls.ctx,
730+
mTlsCaFile, NULL);
731+
if (rc != WOLFSSL_SUCCESS) {
732+
PRINTF("Error loading CA %s: %d (%s)", mTlsCaFile,
733+
rc, wolfSSL_ERR_reason_error_string(rc));
734+
return rc;
735+
}
736+
}
737+
if (mTlsCertFile && mTlsKeyFile) {
738+
/* Load If using a mutual authentication */
739+
rc = wolfSSL_CTX_use_certificate_file(client->tls.ctx,
740+
mTlsCertFile, WOLFSSL_FILETYPE_PEM);
741+
if (rc != WOLFSSL_SUCCESS) {
742+
PRINTF("Error loading certificate %s: %d (%s)", mTlsCertFile,
743+
rc, wolfSSL_ERR_reason_error_string(rc));
744+
return rc;
745+
}
746+
747+
rc = wolfSSL_CTX_use_PrivateKey_file(client->tls.ctx,
748+
mTlsKeyFile, WOLFSSL_FILETYPE_PEM);
749+
if (rc != WOLFSSL_SUCCESS) {
750+
PRINTF("Error loading key %s: %d (%s)", mTlsKeyFile,
751+
rc, wolfSSL_ERR_reason_error_string(rc));
752+
return rc;
753+
}
754+
}
755+
#endif
756+
757+
client->tls.ssl = wolfSSL_new(client->tls.ctx);
758+
if (client->tls.ssl == NULL) {
759+
rc = WOLFSSL_FAILURE;
760+
return rc;
761+
}
762+
}
763+
764+
PRINTF("MQTT DTLS Setup (%d)", rc);
765+
#else /* WOLFSSL_DTLS */
766+
(void)client;
767+
int rc = 0;
768+
PRINTF("MQTT DTLS Setup - Must enable DTLS in wolfSSL!");
769+
#endif
770+
return rc;
771+
}
772+
#endif /* WOLFMQTT_SN */
717773
#else
718774
int mqtt_tls_cb(MqttClient* client)
719775
{
720776
(void)client;
721777
return 0;
722778
}
779+
#ifdef WOLFMQTT_SN
780+
int mqtt_dtls_cb(MqttClient* client)
781+
{
782+
(void)client;
783+
return 0;
784+
}
785+
#endif
723786
#endif /* ENABLE_MQTT_TLS */
724787

725788
int mqtt_file_load(const char* filePath, byte** fileBuf, int *fileLen)

examples/mqttexample.h

+5
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,11 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv);
188188
int err_sys(const char* msg);
189189

190190
int mqtt_tls_cb(MqttClient* client);
191+
192+
#ifdef WOLFMQTT_SN
193+
int mqtt_dtls_cb(MqttClient* client);
194+
#endif
195+
191196
word16 mqtt_get_packetid(void);
192197

193198
#ifdef WOLFMQTT_NONBLOCK

examples/mqttnet.c

+7-25
Original file line numberDiff line numberDiff line change
@@ -24,38 +24,14 @@
2424
#include <config.h>
2525
#endif
2626

27-
#include "wolfmqtt/mqtt_client.h"
2827
#include "examples/mqttnet.h"
29-
#include "examples/mqttexample.h"
30-
#include "examples/mqttport.h"
31-
32-
/* Local context for Net callbacks */
33-
typedef enum {
34-
SOCK_BEGIN = 0,
35-
SOCK_CONN
36-
} NB_Stat;
3728

3829
#if 0 /* TODO: add multicast support */
3930
typedef struct MulticastCtx {
4031

4132
} MulticastCtx;
4233
#endif
4334

44-
45-
typedef struct _SocketContext {
46-
SOCKET_T fd;
47-
NB_Stat stat;
48-
SOCK_ADDR_IN addr;
49-
#ifdef MICROCHIP_MPLAB_HARMONY
50-
word32 bytes;
51-
#endif
52-
#if defined(WOLFMQTT_MULTITHREAD) && defined(WOLFMQTT_ENABLE_STDIN_CAP)
53-
/* "self pipe" -> signal wake sleep() */
54-
SOCKET_T pfd[2];
55-
#endif
56-
MQTTCtx* mqttCtx;
57-
} SocketContext;
58-
5935
/* Private functions */
6036

6137
/* -------------------------------------------------------------------------- */
@@ -578,7 +554,7 @@ static int SN_NetConnect(void *context, const char* host, word16 port,
578554
struct addrinfo hints;
579555
MQTTCtx* mqttCtx = sock->mqttCtx;
580556

581-
PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, Use TLS %d",
557+
PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, Use DTLS %d",
582558
host, port, timeout_ms, mqttCtx->use_tls);
583559

584560
/* Get address information for host and locate IPv4 */
@@ -830,6 +806,12 @@ static int NetRead_ex(void *context, byte* buf, int buf_len,
830806
}
831807
else {
832808
bytes += rc; /* Data */
809+
#ifdef ENABLE_MQTT_TLS
810+
if (MqttClient_Flags(&mqttCtx->client, 0, 0)
811+
& MQTT_CLIENT_FLAG_IS_TLS) {
812+
break;
813+
}
814+
#endif
833815
}
834816
}
835817

examples/mqttnet.h

+21
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,27 @@
2727
#endif
2828

2929
#include "examples/mqttexample.h"
30+
#include "examples/mqttport.h"
31+
32+
/* Local context for Net callbacks */
33+
typedef enum {
34+
SOCK_BEGIN = 0,
35+
SOCK_CONN
36+
} NB_Stat;
37+
38+
typedef struct _SocketContext {
39+
SOCKET_T fd;
40+
NB_Stat stat;
41+
SOCK_ADDR_IN addr;
42+
#ifdef MICROCHIP_MPLAB_HARMONY
43+
word32 bytes;
44+
#endif
45+
#if defined(WOLFMQTT_MULTITHREAD) && defined(WOLFMQTT_ENABLE_STDIN_CAP)
46+
/* "self pipe" -> signal wake sleep() */
47+
SOCKET_T pfd[2];
48+
#endif
49+
MQTTCtx* mqttCtx;
50+
} SocketContext;
3051

3152
/* Functions used to handle the MqttNet structure creation / destruction */
3253
int MqttClientNet_Init(MqttNet* net, MQTTCtx* mqttCtx);

examples/sn-client/README.md

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# MQTT-SN Example
2+
The Sensor Network client implements the MQTT-SN protocol for low-bandwidth networks. There are several differences from MQTT, including the ability to use a two byte Topic ID instead the full topic during subscribe and publish. The SN client requires an MQTT-SN gateway. The gateway acts as an intermediary between the SN clients and the broker. This client was tested with the Eclipse Paho MQTT-SN Gateway, which connects by default to the public Eclipse broker, much like our wolfMQTT Client example. The address of the gateway must be configured as the host. The example is located in `/examples/sn-client/`.
3+
4+
To enable Sensor Network protocol support, configure wolfMQTT using:
5+
`./configure --enable-sn`
6+
7+
## QoS-1
8+
A special feature of MQTT-SN is the ability to use QoS level -1 (negative one) to publish to a predefined topic without first connecting to the gateway. There is no feedback in the application if there was an error, so confirmation of the test would involve running the `sn-client` first and watching for the publish from the `sn-client_qos-1`. There is an example provided in `/examples/sn-client/sn-client_qos-1`. It requires some configuration changes of the gateway.
9+
10+
* Enable the the QoS-1 feature, predefined topics, and change the gateway name in `gateway.conf`:
11+
12+
```
13+
QoS-1=YES
14+
PredefinedTopic=YES
15+
PredefinedTopicList=./predefinedTopic.conf
16+
.
17+
.
18+
.
19+
#GatewayName=PahoGateway-01
20+
GatewayName=WolfGateway
21+
```
22+
23+
* Comment out all entries and add a new topic in `predefinedTopic.conf`:
24+
25+
```
26+
WolfGatewayQoS-1,wolfMQTT/example/testTopic, 1
27+
```
28+
29+
## MQTT-SN with DTLS
30+
MQTT-SN can be secured using DTLS. This enables encryption of sensor data to the gateway. The Eclipse Paho MQTT-SN Gateway supports DTLS clients.
31+
32+
To build the Eclipse Paho MQTT-SN Gateway with DTLS:
33+
`<gateway-folder>/MQTTSNGateway$ ./build.sh dtls`
34+
35+
To build wolfSSL with DTLS support:
36+
`./configure --enable-dtls && make && sudo make install`
37+
38+
To run the wolfMQTT sn-client example with DTLS:
39+
`./examples/sn-client/sn-client -t`
40+
41+
### Notes for Gateway configuration
42+
* To use with local mosquitto broker, edit MQTTSNGateway/gateway.conf. Also set paths to DTLS cert / key.
43+
44+
```
45+
-BrokerName=mqtt.eclipseprojects.io
46+
+BrokerName=localhost
47+
48+
...
49+
50+
-DtlsCertsKey=/etc/ssl/certs/gateway.pem
51+
-DtlsPrivKey=/etc/ssl/private/privkey.pem
52+
+#DtlsCertsKey=/etc/ssl/certs/gateway.pem
53+
+DtlsCertsKey=/<path_to_repo>/wolfssl/certs/server-cert.pem
54+
+#DtlsPrivKey=/etc/ssl/private/privkey.pem
55+
+DtlsPrivKey=/<path_to_repo>/wolfssl/certs/server-key.pem
56+
```
57+
* I had to fix a bug in the gateway (could be related to the openssl or compiler version):
58+
59+
```
60+
diff --git a/MQTTSNGateway/src/linux/dtls/SensorNetwork.cpp b/MQTTSNGateway/src/linux/dtls/SensorNetwork.cpp
61+
index 3f2dcf3..363d0ba 100644
62+
--- a/MQTTSNGateway/src/linux/dtls/SensorNetwork.cpp
63+
+++ b/MQTTSNGateway/src/linux/dtls/SensorNetwork.cpp
64+
@@ -308,7 +308,7 @@ Connections::~Connections()
65+
{
66+
for (int i = 0; i < _numfds; i++)
67+
{
68+
- if (_ssls[i] > 0)
69+
+ if (_ssls[i] > (SSL *)0)
70+
{
71+
SSL_shutdown(_ssls[i]);
72+
SSL_free(_ssls[i]);
73+
@@ -416,7 +416,7 @@ void Connections::close(int index)
74+
}
75+
}
76+
77+
- if (ssl > 0)
78+
+ if (ssl > (SSL *)0)
79+
{
80+
_numfds--;
81+
SSL_shutdown(ssl);
82+
```

examples/sn-client/sn-client.c

+14-2
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,21 @@ int sn_test(MQTTCtx *mqttCtx)
139139
goto exit;
140140
}
141141

142-
/* Setup socket direct to gateway (UDP network, so no TLS) */
142+
/* The client.ctx will be stored in the cert callback ctx during
143+
MqttSocket_Connect for use by mqtt_tls_verify_cb */
144+
mqttCtx->client.ctx = mqttCtx;
145+
146+
#if defined(ENABLE_MQTT_TLS) && defined(WOLFSSL_DTLS)
147+
if (mqttCtx->use_tls) {
148+
/* Set the DTLS flag in the client structure to indicate DTLS usage */
149+
MqttClient_Flags(&mqttCtx->client, 0, MQTT_CLIENT_FLAG_IS_DTLS);
150+
}
151+
#endif
152+
153+
/* Setup socket direct to gateway */
143154
rc = MqttClient_NetConnect(&mqttCtx->client, mqttCtx->host,
144-
mqttCtx->port, DEFAULT_CON_TIMEOUT_MS, 0, NULL);
155+
mqttCtx->port, DEFAULT_CON_TIMEOUT_MS,
156+
mqttCtx->use_tls, NULL /*mqtt_dtls_cb*/);
145157

146158
PRINTF("MQTT-SN Socket Connect: %s (%d)",
147159
MqttClient_ReturnCodeToString(rc), rc);

0 commit comments

Comments
 (0)