wolfMQTT は、組み込み機器や IoT デバイス向けに設計した、wolfSSL の軽量な MQTT クライアントおよびブローカーライブラリです。小型で移植性が高く、組み込み向け TLS ライブラリである wolfSSL と直接組み合わせて、TLS 1.3 による安全な MQTT 通信を実現できます。wolfMQTT 2.2.0 では、キープアライブのスケジュール管理をアプリケーション側で行う必要をなくし、永続セッションで再接続した後に、確認応答を受け取っていない QoS 1 と QoS 2 のメッセージを再送するようにしました。また、Packet Identifier とハンドシェイクの処理を MQTT 仕様に合わせて厳格化し、ブローカー、WebSocket、ビルド設定に関する多数の不具合を修正しました。
2.2.0 の主な変更点
- クライアントのキープアライブの自動化:クライアントが送信タイミングを管理し、PINGREQ を自動送信
- セッション再開時の再送:CleanSession=0 で再接続した後、確認応答のない QoS 1 / QoS 2 のパブリッシュを DUP 付きで再送
- Packet Identifier とハンドシェイクのチェック:すべてのビルドで Packet Identifier を追跡し、CONNECT 前・DISCONNECT 後の操作や 2 回目の CONNECT もチェック
- バイナリパスワード:
MqttConnectに新しいpassword_lenフィールドを追加 - ブローカーの修正:拒否時の CONNACK、部分書き込み、保持メッセージのベストエフォート化
- ビルドの修正:
WOLFMQTT_NO_STDIO、WOLFMQTT_CUSTOM_MALLOC、WOLFMQTT_CUSTOM_STRINGを使用する移植環境でのビルドエラーを解消
クライアントのキープアライブの自動化
これまでは、各アプリケーションが独自にタイマーを動かし、キープアライブ間隔が経過する前に MqttClient_Ping を呼び出す必要がありました。本リリースでは、CONNECT で 0 以外のキープアライブ値を設定している場合、最後の送信から、ネゴシエートしたキープアライブ間隔のおよそ4分の3が経過すると、クライアントが MqttClient_WaitMessage の中で PINGREQ を送信します。MQTT v5 でサーバーが Server Keep Alive を返した場合は、その値に従います。ブロッキングビルドでは、応答のない ping をタイムアウトではなく MQTT_CODE_ERROR_NETWORK として通知するため、応答しなくなった接続とアイドル状態の接続を区別できます。時刻の取得には WOLFMQTT_GET_TIME_S() マクロを使用します(デフォルトは time(NULL) で、user_settings.h で上書き可能)。時計を持たないターゲットでは、WOLFMQTT_NO_TIME を定義するとスケジューラをコンパイル対象から除外できます。すでに独自に ping を送信しているアプリケーションは、MQTT_CLIENT_FLAG_NO_AUTO_KEEPALIVE により実行時にこの機能を無効にできます。
セッション再開時の QoS 1 / QoS 2 メッセージ再送
送信した QoS 1 / QoS 2 メッセージのうち、確認応答を受け取っていないものをセッション状態として保持し、CleanSession=0 で再接続してサーバーが Session Present = 1 で応答した場合に再送するようにしました。PUBLISH は元の Packet Identifier のまま DUP を設定して再送し、PUBREC 以降まで進んでいた QoS 2 のやり取りでは PUBREL を再送します。トピックとペイロードは、エントリ数を MQTT_MAX_REPLAY_MSGS(デフォルトは4)に制限したプールにコピーします。プールに収まらないメッセージ、ペイロードコールバックでストリーミング送信するメッセージ、v5 プロパティを持つメッセージは、送信はしますが保持はしません。再送用のコピーは解放前にゼロ化し、セッション状態は作成時の ClientId に紐付けているため、クライアントオブジェクトを再利用しても別のセッションのメッセージを再送することはありません。WOLFMQTT_NO_SESSION_REPLAY を定義すると、このメッセージの保存機能をコンパイル対象から除外できます。
Packet Identifier とハンドシェイクの厳格なチェック
クライアントは、PUBACK、PUBCOMP、SUBACK、UNSUBACK の応答待ちになっている Packet Identifier を、WOLFMQTT_MULTITHREAD 有効時に限らずすべてのビルドで記録するようになりました。記録中の Packet Identifier を再利用する新しいパケットは MQTT_CODE_ERROR_PACKET_ID で拒否します。記録できる数の上限は MQTT_MAX_SEND_INFLIGHT(デフォルトは16)です。また、MQTT のハンドシェイク状態をトランスポートの接続状態とは別に管理します。MqttClient_Connect の前、または MqttClient_Disconnect の後に publish、subscribe、unsubscribe、ping、disconnect を呼び出すと MQTT_CODE_ERROR_STAT を返し、同じ接続上での2回目の CONNECT は拒否します。v5 の CONNECT では Receive Maximum に MQTT_MAX_RECV_QOS2 を設定して通知するため、再送された PUBLISH が二重に配信されることはありません。さらに、Packet Identifier が 0 の確認応答を受け取ると接続を閉じ、v3.1.1 の CONNACK デコーダは、Remaining Length が2以外のものを受け付けません。
ブローカーの修正、堅牢化、ビルドエラーの解消
wolfMQTT のブローカーは、接続を拒否する CONNACK を最後まで書き込んでから接続を閉じるようにしました。接続を受け入れる CONNACK の書き込み中はキープアライブの監視を一時停止します。書き込みの途中で失敗した PUBLISH は、QoS 1 以上なら DUP を設定して再送し、QoS 0 なら二重配信を避けるために破棄します。静的メモリ構成で複数のサブスクライバーへ配信する際は、送信中の Packet Identifier をサブスクライバーごとに追跡します。メッセージの保持は、Mosquitto と同様にベストエフォートとしました。RETAIN=1 の PUBLISH を保存できない場合でもメッセージ自体は配信して確認応答を返し、保持用のコピーだけを省略します。WebSocket の受信コールバックは、バイナリ以外のフレームを受け取ると接続を閉じます。
wolfSSL の社内ツール Fenrir と Coverity を活用し、クライアント、ブローカー、MQTT v5、MQTT-SN の各処理経路でさらに堅牢化を進めました。認証情報とペイロードのゼロ化や、SN_Packet_Read での小さすぎる Length のチェックなども実施しました。また、これまでコンパイルに失敗していたいくつかの移植向け構成を、問題なくビルドできるようにしました。対象は、WOLFMQTT_NO_STDIO(MPLAB Harmony のように WOLFMQTT_CUSTOM_PRINTF を使用する移植を含む)、アロケータマクロを定義しない WOLFMQTT_CUSTOM_MALLOC、WOLFMQTT_CUSTOM_STRING の各構成と、-Werror 指定時にコマンドラインから WOLFMQTT_SESSION_ID_TRACK を定義した構成です。
アップグレード時に注意すべき API と動作の変更
- 再リンクではなく再ビルドが必要:
MqttMsgStatとMqttConnectが大きくなったため、すべてのパケットオブジェクトのsizeofが変わります。WOLFMQTT_STATIC_MEMORY有効時は、デフォルト設定でMqttClientが約 1.3 KB 増加します - 再接続の前に
MqttClient_NetDisconnectが必要:同じ接続上でMqttClient_Connectを再度呼び出すとMQTT_CODE_ERROR_STATを返します - バイナリパスワード:
MqttConnectのpassword_lenを設定するとバイト列をそのまま送信します。0 のままにすると、従来どおり NUL 終端の文字列として扱います - 受信方向の Topic Alias を拒否:クライアントは Topic Alias Maximum に 0 を通知し、0 以外の
MQTT_PROP_TOPIC_ALIAS_MAXを指定するとMqttClient_ConnectがMQTT_CODE_ERROR_PROPERTYを返すようになりました MqttClient_Publish_WriteOnlyで QoS 2 の拒否を検出:マルチスレッドビルドではMQTT_CODE_ERROR_PUBLISH_REJECTEDで通知します。PUBACK と PUBCOMP による拒否は、この経路では引き続き検出しません- v3.1.1 では空の ClientId と clean_session 0 の組み合わせを拒否:v5 では引き続き許可します
MqttClient_WaitMessageの所要時間が最大cmd_timeout_ms延びる場合あり:キープアライブの ping を送信した呼び出しで発生します。ポーリングには、キープアライブ間隔より短いtimeout_msを指定してください
入手方法
wolfMQTT 2.2.0 は GitHub の https://github.com/wolfSSL/wolfMQTT で公開しています。リリースをクローンまたはダウンロードし、autotools または CMake でビルドしてください。TLS を有効にするには、wolfSSL と組み合わせてビルドします。すべての変更点については ChangeLog をご覧ください。あわせて、wolfMQTT プロジェクトへのスターもぜひお願いします!
wolfSSL 製品は GPLv3 と商用ライセンスのデュアルライセンスで提供しており、wolfSSL のサポートチームがサポートしています。
もし弊社製品についてご質問があれば、お問い合わせ窓口info@wolfssl.jpまでご連絡をお願い致します。
