UDP communications to multicast groups

314 views
Skip to first unread message

Stuart Longland

unread,
Oct 30, 2017, 3:57:56 AM10/30/17
to openthread-users
Hi all,

I'm in the process of implementing a SNTP client in OpenThread for the
purpose of configuring the real-time clock. Looking around, I found a
fairly straightforward implementation of an NTP client that just polls a
NTP server (unicast) then prints out the time.

https://github.com/lettier/ntpclient

This, I've re-spun as a library built on OpenThread. Perhaps in the
future it might become another add-on module for OpenThread as we can't
be the only people that would find NTP clients useful, the code for this
is in a branch here:

https://github.com/vrtsystems/ntpclient/tree/feature/WSHUB-6-openthread-port

To use this client, I add the following code:
> int main(int argc, char* argv[]) {

> /* NTP client */
> struct ntp_client_t ntp_client;
> uint16_t ntp_interval = 0;

> /* Set up GPT0 as a 100msec (10Hz) one-shot event timer */
> SYS_CTRL_RCGCGPT |= (1 << SYS_CTRL_xCGCGPT_GPT0);
> uint32_t gpt0_period = sysctrl_sys_clock_rate() / 10;
> gptimer_oneshot32(GPT0_BASE, gpt0_period);

> while (1) {
> otTaskletsProcess(instance);
> PlatformProcessDrivers(instance);

> switch (ntp_client.state) {
> case NTP_CLIENT_DONE:
> iprintf("NTP time received: %ld sec %ld usec\r\n",
> ntp_client.tv.tv_sec,
> ntp_client.tv.tv_usec);
> ntp_client.state = NTP_CLIENT_INIT;
> break;
> case NTP_CLIENT_INT_ERR:
> iprintf("NTP time fetch failed: %d\r\n",
> ntp_client.error);
> ntp_client.state = NTP_CLIENT_INIT;
> break;
> case NTP_CLIENT_TIMEOUT:
> iprintf("NTP time fetch timed out\r\n");
> ntp_client.state = NTP_CLIENT_INIT;
> break;
> case NTP_CLIENT_INIT:
> if (!ntp_interval) {
>
> otIp6Address addr;
> otIp6AddressFromString("fdde:ad00:beef:0:d79a:fe40:f760:1de6", &addr);
>
> otError err = ntp_client_begin(instance, &ntp_client,
> &addr, NTP_CLIENT_DEFAULT_PORT, 2);
> if (err != OT_ERROR_NONE) {
> iprintf("Failed to retrieve NTP time: %d\r\n", err);
> } else {
> iprintf("NTP client request sent\r\n");
> }
> ntp_interval = 100;
>
> }
> }
> if (GPT0_MIS & (1 << GPT_INT_TATO)) {

> if ((ntp_client.state == NTP_CLIENT_INIT)
> && ntp_interval) {
> ntp_interval--;
> }
> ntp_client_process(&ntp_client);

> GPT0_ICR = (1 << GPT_INT_TATO);
> gptimer_oneshot32(GPT0_BASE, gpt0_period);
> }
> }
> }

Now, I'm attempting to test this client with `ntpd` running on my host,
which also runs the NCP.

What I have tried so far:

1. trying to get `ntpd` to listen to the IANA-assigned ff05::101
multicast address and having the clients send to that address. -- Device
reports OT_ERROR_INVALID_SOURCE_ADDRESS. This is possibly due to this
bug: https://github.com/openthread/wpantund/issues/207

2. trying to send to the all-nodes link-local multicast address ff02::1,
again device reports OT_ERROR_INVALID_SOURCE_ADDRESS. Doing this works
fine for CoAP, so there's clearly some magic that happens somewhere that
figures out what source address to use, it is not obvious to me what
that magic is.

3. hard-coding a unicast address then calling `ip -6 addr add
${ADDRESS}/64 dev wpan0` on my host to assign the address. -- Device
sends packets allegedly, but I don't receive them on the host (according
to `tcpdump`).

4. hard-coding the MeshLocalPrefix seen in `wpanctl` in the code. --
Re-flashing the device requires me to leave then re-join the WPAN, at
which point I have a *different* address, and I'm back to square one.

Hard coding a unicast address is obviously not a good solution. I'd
like to avoid this as much as possible long-term, although in my case
today I'm just testing a proof-of-concept, it's acceptable there.

I'm also trying to avoid messing around with the CLI, because there's no
way to add new functions to it without modifying OpenThread itself from
what I can see.

Out of all the options, using the ff05::101 address seems the most
technically correct, and I've seen multicast to ff02::1 work with CoAP,
so it's doable in theory.

Has anyone managed to successfully send a raw UDP message to a multicast
group on OpenThread?

Regards,
--
_ ___ Stuart Longland - Systems Engineer
\ /|_) | T: +61 7 3535 9619
\/ | \ | 38b Douglas Street F: +61 7 3535 9699
SYSTEMS Milton QLD 4064 http://www.vrt.com.au

Stuart Longland

unread,
Oct 30, 2017, 8:42:23 PM10/30/17
to openthre...@googlegroups.com
On 30/10/17 17:57, Stuart Longland wrote:
> Hard coding a unicast address is obviously not a good solution. I'd
> like to avoid this as much as possible long-term, although in my case
> today I'm just testing a proof-of-concept, it's acceptable there.
>
> I'm also trying to avoid messing around with the CLI, because there's no
> way to add new functions to it without modifying OpenThread itself from
> what I can see.
>
> Out of all the options, using the ff05::101 address seems the most
> technically correct, and I've seen multicast to ff02::1 work with CoAP,
> so it's doable in theory.
>
> Has anyone managed to successfully send a raw UDP message to a multicast
> group on OpenThread?

Okay, so it appears from the host with `wpantund`, I can reliably *send*
to a multicast group. The problem is in the reception.

There's some magic needed on the client end to be able to send UDP
traffic to multicast groups.

A workaround to my specific need is to use ntpd's broadcast mode. In
ntpd.conf:

> broadcast ff02::101 ttl 1
>
> server 127.127.1.1
> fudge 127.127.1.1 stratum 12

That tells the box to consider itself a stratum 12 NTP server and to
broadcast the time to ff02::101.

On the OpenThread node, you can then run:

> ipmaddr add ff02::101
> udp open
> udp bind ff02::101 123
You'll see a message printed every time a NTP broadcast is sent out. I
should be able to use the existing code to parse that broadcast and
provide a callback for setting the system time.

Not quite what I had in mind, but this might work out better for the
purpose of distributing the time to the nodes.

Stuart Longland

unread,
Oct 31, 2017, 12:00:34 AM10/31/17
to openthre...@googlegroups.com
Hi all,

After my earlier experiments with multicast UDP, it appears I have a
workable NTP client solution that may be of interest to others.

Unicast NTP theoretically works, but isn't tested. It might be worth
looking at using the DNS client to hit pool.ntp.org, but for now, I've
just left it as relying on IP addresses directly.

The code is in a branch right now:
https://github.com/vrtsystems/ntpclient/tree/feature/WSHUB-6-openthread-port/

There's some things to clean up obviously, but using it in its broadcast
listener mode, it works. You'll need an NTP server, ideally running on
the border router / NCP host, set up to send broadcast messages to the
ff02::101 multicast address. Something like this:

> broadcast ff02::101 ttl 1
>
> server 127.127.1.1
> fudge 127.127.1.1 stratum 12

The latter two are optional, but means your host can continue to act as
an NTP server for the mesh if you lose upstream network connectivity.

In your code, you define a handler to execute when a broadcast message
is received. This needs to check what state the client is in, ideally,
it should be in NTP_CLIENT_RECV_BC, meaning it just received a broadcast
message. The `tv` struct member is a `struct timeval`, which you can
use to adjust your system time:

> static void node_ntp_handler(struct ntp_client_t* ntp_client) {
> if (ntp_client->state == NTP_CLIENT_RECV_BC) {
> settimeofday(&(ntp_client->tv), NULL);
> }
> }

In our case, `settimeofday` is implemented using `gmtime_r` to convert
this to `struct tm` and handed to a NXP PCF8523 RTC driver for hardware
storage. It'd be prudent to check the value isn't out-of-bounds.

Prior to entering your main loop, you set up the NTP client to listen
for these broadcasts, passing in this handler function:
> instance = otInstanceInitSingle();
> otCliUartInit(instance);
>
> /* Set up NTP client */
> {
> otIp6Address addr;
> memset(&ntp_client, 0, sizeof(struct ntp_client_t));
> err = otIp6AddressFromString("ff02::101", &addr);
> if (err != OT_ERROR_NONE) {
> iprintf("Failed to parse NTP address: %d\r\n", err);
> } else {
> err = ntp_client_listen(instance, &ntp_client, &addr, NTP_CLIENT_DEFAULT_PORT,
> &node_ntp_handler, NULL);
> if (err)
> iprintf("Failed to start NTP client: %d\r\n", err);
> }
> }

Finally, in your main loop, periodically call `ntp_client_process` to
handle incoming messages:

> while (1) {
> otTaskletsProcess(instance);
> PlatformProcessDrivers(instance);

> if (GPT0_MIS & (1 << GPT_INT_TATO)) {
> ntp_client_process(&ntp_client);
>
> GPT0_ICR = (1 << GPT_INT_TATO);
> gptimer_oneshot32(GPT0_BASE, gpt0_period);
> }
> }

If there's interest, I can look at cleaning this up and contributing
that into OpenThread's core. Thanks go to David Lettier for writing the
original client and releasing it under an open license¹.

Regards,
--
_ ___ Stuart Longland - Systems Engineer
\ /|_) | T: +61 7 3535 9619
\/ | \ | 38b Douglas Street F: +61 7 3535 9699
SYSTEMS Milton QLD 4064 http://www.vrt.com.au

1. https://github.com/lettier/ntpclient/issues/1

Jonathan Hui

unread,
Oct 31, 2017, 12:51:55 PM10/31/17
to Stuart Longland, openthread-users
Hi Stuart,

For sending to a multicast address, can you try setting `msg_info.mInterfaceId` to `OT_NETIF_INTERFACE_ID_THREAD`?

Note that multicast may not be the most efficient solution given that the message is forwarded throughout the entire mesh.

Couple alternative options:
1) Use DNS to determine the IP address of the NTP server.
2) Use Thread's Service advertising functionality in the Network Data to advertise NTP server information.  This feature was recently added with PR#2256.

--
Jonathan Hui

--
You received this message because you are subscribed to the Google Groups "openthread-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to openthread-users+unsubscribe@googlegroups.com.
To post to this group, send email to openthread-users@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/openthread-users/d1b8cf4b-1aef-11b8-ffc7-c7a32a9f642f%40vrt.com.au.
For more options, visit https://groups.google.com/d/optout.

Jonathan Hui

unread,
Oct 31, 2017, 12:58:12 PM10/31/17
to Stuart Longland, openthread-users
I think it would be great to have an example app that demonstrates NTP over OpenThread.

--
Jonathan Hui

--
You received this message because you are subscribed to the Google Groups "openthread-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to openthread-users+unsubscribe@googlegroups.com.
To post to this group, send email to openthread-users@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages