# CoAP - REST для constrained-устройств поверх UDP _Сеть: L4 и выше · LinuxLab Knowledge Base_ **TL;DR:** CoAP - REST поверх UDP для маломощных IoT-устройств. 4-байтный header, GET/POST/PUT/DELETE, response codes как HTTP. Observe для notifications. DTLS для security. Применяется в LwM2M, Thread. ## Зачем CoAP Классический HTTP не работает на сильно constrained устройствах: - Маленькая микроконтроллерная RAM (десятки KB) - Battery-powered (нельзя держать TCP-сессии) - Lossy radio (LoRa, Zigbee, Thread mesh) - Не во всех есть IPv4-stack полностью HTTP-парсер - сложный, headers большие (даже минимальный GET ~80 байт). **CoAP (Constrained Application Protocol, RFC 7252, 2014)** - REST-like protocol специально для них. Поверх **UDP**, минимальный header, REST-семантика. Сделан так, чтобы device мог быть и client, и server одновременно. Применения: - **LwM2M** (Lightweight M2M от OMA) - device management поверх CoAP - **Thread** (mesh-сеть для smart home) - CoAP в native protocol stack - **ZigBee Pro** - CoAP опция - **OMA Lightweight Object Tree** - сенсоры/actuators в LwM2M - **Energy management** в HEMS ## CoAP vs HTTP - семантика | HTTP | CoAP | |------|------| | TCP | UDP (TCP-вариант RFC 8323) | | Text headers | Bitfields, options | | Headers ~80+ байт | Header ~4 байта | | Methods GET/POST/... | Те же 4 + блок-расширения | | URLs | URIs, Uri-Path/Uri-Query options | | Response codes 200/404/... | Кодировано по 5.x.y (5 классов × 32) | | Stateless request/response | Confirmable / Non-confirmable | | EventSource/SSE/WebSocket для push | **Observe-pattern** native | Идея: один и тот же ментальный модель (URL, метод, status), но ужатый под радио и мелкое железо. ## Wire format Минимальный CoAP-header - **4 байта**: ``` 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |Ver| T | TKL | Code | Message ID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Token (if any, TKL bytes) ... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options (if any) ... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1 1 1 1 1 1 1 1| Payload (if any) ... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ``` - **Ver** - версия (1) - **T** - тип сообщения: CON (confirmable), NON (non-conf), ACK, RST - **TKL** - длина token (0-8 байт) - **Code** - 8 бит, формат `c.dd` (3 бит class + 5 бит detail). Method: 0.01=GET, 0.02=POST, 0.03=PUT, 0.04=DELETE Response: 2.05=Content (≈200), 4.04=Not Found (≈404), 5.00=Internal Error - **Message ID** - 16-битный, для duplicate detection - **Token** - 0-8 байт, для request-response correlation (отличается от Message ID - один request может породить много responses через observe) - **Options** - URI path, content-format, ETag, Observe, ... - **0xFF** - разделитель payload Минимальный GET с пустым payload - 4 байта + URI options. ## Confirmable vs Non-confirmable CoAP - поверх UDP, нет встроенной reliability. Решение - тип сообщения: ### CON (Confirmable) - reliable ``` client ──CON GET /sensor (mid=42)──► server client ◄──ACK 2.05 Content (mid=42)── server ``` Ack обязателен. Без ack - retry (exp backoff): 2s, 4s, 8s, 16s, 32s - максимум 5 попыток (RFC 7252). Если piggyback - сервер шлёт ACK с уже готовым response (одна посылка). ### NON (Non-confirmable) - fire-and-forget ``` client ──NON GET /sensor (mid=42)──► server ``` Никакого ack. Если потерян - потерян. Для high-rate телеметрии где одно потеряное сообщение неважно. Похоже на [mqtt](/kb/mqtt.md) QoS 0 (NON) и QoS 1 (CON). ## Methods и status codes Methods (RFC 7252): - **GET 0.01** - получить ресурс - **POST 0.02** - создать - **PUT 0.03** - обновить - **DELETE 0.04** - удалить + расширения: - **FETCH 0.05** (RFC 8132) - GET с body (для сложных queries) - **PATCH 0.06**, **iPATCH 0.07** (RFC 8132) Response classes: - **2.xx** - Success: 2.01 Created, 2.02 Deleted, 2.03 Valid (ETag), 2.04 Changed, 2.05 Content - **4.xx** - Client error: 4.00 Bad Request, 4.04 Not Found, 4.05 Method Not Allowed, 4.06 Not Acceptable, 4.13 Request Entity Too Large - **5.xx** - Server error: 5.00 Internal, 5.03 Service Unavailable ## URI и options URI типа `coap://gw.example/sensors/temp?units=C`. На проводе: - `Uri-Host` (option 3) = "gw.example" - `Uri-Path` (option 11) = "sensors", "temp" (по сегментам) - `Uri-Query` (option 15) = "units=C" Опции **typed** - 28 предопределённых, кастомные через elective/critical flag в опции number. Каждая option encode'ится с delta-кодированием от предыдущей - компактно. ## Observe pattern - server-pushed updates RFC 7641. Клиент посылает GET с **Observe option = 0**: ``` client ──GET /temperature, Observe=0──► server client ◄──2.05 Content "21.5", Observe=1── server client ◄──2.05 Content "21.6", Observe=2── server (через 30 сек) client ◄──2.05 Content "21.7", Observe=3── server ... ``` Сервер шлёт push'ы каждый раз когда ресурс изменился (или по таймеру). Observe-counter монотонно растёт - помогает обнаружить потерянные/out-of-order responses. Cancel - GET с **Observe = 1** или просто RST в ответ на следующее. Это **CoAP-эквивалент EventSource/WebSocket** для push'а - но без открытой сессии (UDP). ## Block-wise transfer Большие payloads через UDP - проблема (фрагментация, потеря). CoAP делит на блоки через **Block1/Block2 options** (RFC 7959): ``` Block1 = 0/1/512 означает: блок 0, more=1, размер 512 ``` Клиент шлёт PUT блоками 0..N с Block1, последний с more=0. Сервер собирает. Размер блока 16-1024 байта. Default - подбор под underlying MTU (обычно 64-256 для LoRa, 512-1024 для WiFi/Thread). ## DTLS - security CoAP сам не шифрует. **DTLS (Datagram TLS, RFC 6347)** - TLS-over-UDP (как [[tls-handshake|TLS]] но с retransmission и без strict ordering). CoAP+DTLS = **coaps://** на UDP/5684 (vs обычный coap:// на 5683). Modes: - **NoSec** - plain CoAP (для labа) - **PSK** (Pre-Shared Key) - shared secret, легковесно для constrained - **RPK** (Raw Public Key) - cert без X.509 signature chain - **Certificate** - полный X.509 (как HTTPS) PSK самый популярный в IoT - device shipped с factory key, broker знает. Современные расширения: - **OSCORE** (RFC 8613) - object security, защита payload end-to-end даже через proxy - **EDHOC** (RFC 9528) - lightweight key exchange для PSK ## CoAP vs MQTT | Свойство | CoAP | [[mqtt\|MQTT]] | |----------|------|----------------| | Транспорт | UDP (или TCP/TLS) | TCP/TLS | | Модель | request-response (REST) | publish-subscribe | | Broker | не нужен | обязателен | | Push | observe-pattern | subscribe | | Min header | 4 байта | 2 байта | | Reliability | CON ack/retry | QoS 0/1/2 | | Multicast | да (group communication) | нет | | Best for | sensor query, RESTful API | telemetry, fan-out | | Stack | REST-like, знакомый | специфический | | Security | DTLS | TLS (сложнее на mc) | В Thread/Matter - **CoAP** базовый. В classic IoT (industrial, mobile) - чаще **MQTT**. ## Multicast - group communication CoAP поддерживает **IP multicast**: одно request → все devices в multicast-группе отвечают. Для discovery, group control: ``` GET coap://[ff02::fd]:5683/.well-known/core ──► все CoAP-серверы в локальном link ``` HTTP такого нет. Полезно в smart home: "все лампы → off". Только для NON-confirmable (CON через multicast логически невозможен). ## Discovery - /.well-known/core RFC 6690 - стандартный resource directory: ``` GET /.well-known/core ───► ◄─── 2.05 ;rt="temperature";if="sensor", ;rt="humidity";if="sensor", ;rt="light";if="actuator" ``` Application-level discovery - what resources, what types. ## Когда что-то пошло не так - **Запросы пропадают** - UDP. Проверь firewall (5683/5684 UDP). Используй CON вместо NON чтобы видеть. - **`4.13 Request Entity Too Large`** - payload > MTU. Используй Block1. - **DTLS handshake fail** - PSK не совпадает или TLS-версия mismatch (CoAP DTLS обычно DTLS 1.2). `openssl s_client -dtls1_2`. - **Observe не работает после NAT** - NAT-binding истёк, server шлёт в "никуда". Делай keepalive (CON GET изредка) или сильно короче `interval`. - **Sequence number wrap** - Observe counter 24-bit, при wrap логика может ошибаться. Большинство реализаций обработали. - **Multicast не доходит** - kernel не routing'ит multicast по дефолту. `ip route add 224.0.0.0/4 dev eth0`. И уровень link должен поддерживать (WiFi - часто фильтрует). ## Где почитать - https://datatracker.ietf.org/doc/html/rfc7252 - оригинальный CoAP - https://coap.technology/ - инструменты, статьи - https://github.com/eclipse/californium (Java) - reference implementation - https://github.com/obgm/libcoap (C) - для embedded ## Команды ```bash coap-client -m get coap://localhost/.well-known/core ``` Discovery: список ресурсов на CoAP-сервере (libcoap CLI) ```bash coap-client -m get coap://gw.example/sensors/temp ``` Простой GET-запрос на ресурс ```bash coap-client -m put -e '21.5' coap://gw/actuators/setpoint ``` PUT с payload - обновить ресурс ```bash coap-client -s 60 -m get coap://gw/sensors/temp ``` Subscribe (observe) - 60 секунд получать notifications ```bash coap-client -m get coaps://gw/secret -k mySecretKey -u myIdentity ``` DTLS+PSK - identity и pre-shared key ```bash coap-client -m get -B 64 coap://gw/big_resource ``` Block-wise transfer с размером блока 64 байта ```bash tshark -Y coap -i eth0 ``` Wireshark dissector для CoAP - видно типы, options, observe ```bash nc -u localhost 5683 ``` Открыть UDP-сокет к CoAP-порту - можно посмотреть raw bytes ## См. также - [UDP - User Datagram Protocol](/kb/udp-basics.md) - [MQTT - publish/subscribe для IoT и mobile push](/kb/mqtt.md) - [TLS handshake](/kb/tls-handshake.md) - [Порт - как несколько сервисов делят один IP](/kb/port.md) - [HTTP/1.1, HTTP/2, HTTP/3](/kb/http-protocol.md)