This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

OpenThread CoAP Multicast Message ID

Hello Support

I have a question about the function " uint16_t otCoapMessageGetMessageId(const otMessage *aMessage); ". I need to know the message ID for my application.

On client side:

I send some multicast messages from CoAP clients to servers. If I send the multicast to a Link-Local address like "ff02::1" the function getMessageID returns the right ID. But if I send the message to an Mesh-Local address like "ff03::1" the function return always 0. 

On server side:

It works always perfectly the function returns the right message ID.

I tested it with the example "Thread Simple CoAP Client" on a PCA10056:

void thread_coap_utils_multicast_light_request_send(uint8_t                             command,
                                                    thread_coap_utils_multicast_scope_t scope)
{
    otError       error = OT_ERROR_NONE;
    otMessage   * p_request;
    otMessageInfo message_info;
    const char  * p_scope = NULL;
    otInstance  * p_instance = thread_ot_instance_get();

    do
    {
        p_request = otCoapNewMessage(p_instance, NULL);
        if (p_request == NULL)
        {
            NRF_LOG_INFO("Failed to allocate message for CoAP Request\r\n");
            break;
        }

        otCoapMessageInit(p_request, OT_COAP_TYPE_NON_CONFIRMABLE, OT_COAP_CODE_PUT);

        error = otCoapMessageAppendUriPathOptions(p_request, "light");
        ASSERT(error == OT_ERROR_NONE);

        error = otCoapMessageSetPayloadMarker(p_request);
        ASSERT(error == OT_ERROR_NONE);

        error = otMessageAppend(p_request, &command, sizeof(command));
        if (error != OT_ERROR_NONE)
        {
            break;
        }

        switch (scope)
        {
        case THREAD_COAP_UTILS_MULTICAST_LINK_LOCAL:
            p_scope = "ff02::1";
            break;

        case THREAD_COAP_UTILS_MULTICAST_REALM_LOCAL:
            p_scope = "ff03::1";
            break;

        default:
            ASSERT(false);
        }

        memset(&message_info, 0, sizeof(message_info));
        message_info.mPeerPort = OT_DEFAULT_COAP_PORT;

        error = otIp6AddressFromString(p_scope, &message_info.mPeerAddr);
        ASSERT(error == OT_ERROR_NONE);

        error = otCoapSendRequest(p_instance, p_request, &message_info, NULL, NULL);
        
        NRF_LOG_INFO("Message ID: %u", otCoapMessageGetMessageId(p_request)); // Return 0 if address is Mesh-Local (ff03::1)
    } while (false);

    if (error != OT_ERROR_NONE && p_request != NULL)
    {
        NRF_LOG_INFO("Failed to send CoAP Request: %d\r\n", error);
        otMessageFree(p_request);
    }
}

I tried to make and replace the latest openthread libraries but the problem is still there.

Thank you for your support

Best Regards

Roiben

Parents Reply Children
  • Hello,

    Following is the reply from our Thread Team:
    --

    The OpenThread CoAP implementation does not provide a reliable way of fetching the CoAP Message ID after it is sent. 

    With this PR:

    https://github.com/openthread/openthread/pull/3419 

    the way of handling extra CoAP information such as Message ID was changed. Previously such information was kept in the CoAP Header structure, but right now it is kept in unused space in the Message Buffer itself.

    To give you a better understanding, the Message Buffer may contain a few headers i.e:

    | IPv6 Header | MPL Header | UDP Header | CoAP Header | Payload |

    When a CoAP message is created, it allocates only space for CoAP and UDP Headers, and reserves space to prepend lower-layer headers:

    | Reserved                 | UDP Header | CoAP Header | Payload |

    (Reserved takes up the space of IPv6 Header and MPL Header)

    The current CoAP implementation takes advantage of that, and puts temporarily needed information in the reserved space, because those are needed only to construct the CoAP message):

    | CoAP Helper Data         | UCP Header | CoAP Header | Payload |

    After the packet is sent to the IP layer, the Reserved space is filled with the IPv6 (and for FF03::1 also MPL Header).

    This effectively overwrites the CoAP Helper data (which includes the Message ID).

    Luckily (but not on purpose), the Message ID is at the top of the CoAP Helper Data, and if the MPL Header is not used, then the CoAP Message ID information persists.

    In case the MPL Header is inserted it fully overwrites the CoAP Message ID as well.

    Why do you need to know what the Message ID of the CoAP message is? In the CoAP implementations I know this is always handled internally.

    If you need a workaround, you can increase the reserved space allocated by the IP layer to prevent the CoAP Helper Data (Message ID) being overwritten. For example:

    diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp
    index d85dfb13e..0a1a72756 100644
    --- a/src/core/net/ip6.cpp
    +++ b/src/core/net/ip6.cpp
    @@ -70,7 +70,7 @@ Ip6::Ip6(Instance &aInstance)
     Message *Ip6::NewMessage(uint16_t aReserved, const Message::Settings &aSettings)
     {
         return Get<MessagePool>().New(Message::kTypeIp6,
    -                                  sizeof(Header) + sizeof(HopByHopHeader) + sizeof(OptionMpl) + aReserved, aSettings);
    +                                  sizeof(Header) + sizeof(HopByHopHeader) + sizeof(OptionMpl) + aReserved + sizeof(uint32_t), aSettings);
     }

    --

    Best regards,

    Edvin

Related